From 925d8b27cf4938ed90a341f46fee90024787dbbc Mon Sep 17 00:00:00 2001 From: rextimmy Date: Wed, 9 May 2018 20:48:18 +1000 Subject: [PATCH] openal-soft updates --- Engine/lib/openal-soft/.gitignore | 10 +- Engine/lib/openal-soft/.travis.yml | 69 +- Engine/lib/openal-soft/Alc/ALc.c | 2383 ++++++++++------ Engine/lib/openal-soft/Alc/ALu.c | 2424 +++++++++------- Engine/lib/openal-soft/Alc/alcRing.c | 301 -- .../Alc/{alcConfig.c => alconfig.c} | 179 +- Engine/lib/openal-soft/Alc/alconfig.h | 17 + Engine/lib/openal-soft/Alc/alstring.h | 45 +- Engine/lib/openal-soft/Alc/ambdec.c | 33 +- Engine/lib/openal-soft/Alc/ambdec.h | 2 +- Engine/lib/openal-soft/Alc/backends/alsa.c | 178 +- Engine/lib/openal-soft/Alc/backends/base.c | 176 +- Engine/lib/openal-soft/Alc/backends/base.h | 25 +- .../lib/openal-soft/Alc/backends/coreaudio.c | 552 ++-- Engine/lib/openal-soft/Alc/backends/dsound.c | 336 +-- Engine/lib/openal-soft/Alc/backends/jack.c | 146 +- .../lib/openal-soft/Alc/backends/loopback.c | 7 +- Engine/lib/openal-soft/Alc/backends/null.c | 22 +- Engine/lib/openal-soft/Alc/backends/opensl.c | 1002 +++++-- Engine/lib/openal-soft/Alc/backends/oss.c | 329 ++- .../lib/openal-soft/Alc/backends/portaudio.c | 49 +- .../lib/openal-soft/Alc/backends/pulseaudio.c | 332 ++- Engine/lib/openal-soft/Alc/backends/qsa.c | 381 ++- Engine/lib/openal-soft/Alc/backends/sdl2.c | 287 ++ Engine/lib/openal-soft/Alc/backends/sndio.c | 216 +- Engine/lib/openal-soft/Alc/backends/solaris.c | 103 +- .../Alc/backends/{mmdevapi.c => wasapi.c} | 777 ++--- Engine/lib/openal-soft/Alc/backends/wave.c | 71 +- Engine/lib/openal-soft/Alc/backends/winmm.c | 139 +- Engine/lib/openal-soft/Alc/bformatdec.c | 544 ++-- Engine/lib/openal-soft/Alc/bformatdec.h | 60 +- Engine/lib/openal-soft/Alc/bs2b.c | 8 +- Engine/lib/openal-soft/Alc/bsinc.c | 981 ------- Engine/lib/openal-soft/Alc/compat.h | 18 +- Engine/lib/openal-soft/Alc/converter.c | 468 +++ Engine/lib/openal-soft/Alc/converter.h | 55 + Engine/lib/openal-soft/Alc/cpu_caps.h | 15 + Engine/lib/openal-soft/Alc/effects/chorus.c | 461 +-- .../lib/openal-soft/Alc/effects/compressor.c | 72 +- .../lib/openal-soft/Alc/effects/dedicated.c | 109 +- .../lib/openal-soft/Alc/effects/distortion.c | 203 +- Engine/lib/openal-soft/Alc/effects/echo.c | 198 +- .../lib/openal-soft/Alc/effects/equalizer.c | 247 +- Engine/lib/openal-soft/Alc/effects/flanger.c | 411 --- .../lib/openal-soft/Alc/effects/modulator.c | 180 +- Engine/lib/openal-soft/Alc/effects/null.c | 74 +- Engine/lib/openal-soft/Alc/effects/pshifter.c | 526 ++++ Engine/lib/openal-soft/Alc/effects/reverb.c | 2500 +++++++++-------- Engine/lib/openal-soft/Alc/filters/defs.h | 112 + Engine/lib/openal-soft/Alc/filters/filter.c | 129 + Engine/lib/openal-soft/Alc/filters/nfc.c | 426 +++ Engine/lib/openal-soft/Alc/filters/nfc.h | 49 + Engine/lib/openal-soft/Alc/filters/splitter.c | 109 + Engine/lib/openal-soft/Alc/filters/splitter.h | 40 + Engine/lib/openal-soft/Alc/fpu_modes.h | 34 + Engine/lib/openal-soft/Alc/helpers.c | 527 ++-- Engine/lib/openal-soft/Alc/hrtf.c | 1395 +++++---- Engine/lib/openal-soft/Alc/hrtf.h | 95 +- Engine/lib/openal-soft/Alc/hrtf_res.h | 5 - Engine/lib/openal-soft/Alc/hrtf_res.rc | 4 - Engine/lib/openal-soft/Alc/inprogext.h | 79 + Engine/lib/openal-soft/Alc/logging.h | 69 + Engine/lib/openal-soft/Alc/mastering.c | 232 ++ Engine/lib/openal-soft/Alc/mastering.h | 57 + Engine/lib/openal-soft/Alc/mixer.c | 702 ----- Engine/lib/openal-soft/Alc/mixer/defs.h | 119 + Engine/lib/openal-soft/Alc/mixer/hrtf_inc.c | 123 + Engine/lib/openal-soft/Alc/mixer/mixer_c.c | 176 ++ Engine/lib/openal-soft/Alc/mixer/mixer_neon.c | 269 ++ .../openal-soft/Alc/{ => mixer}/mixer_sse.c | 164 +- .../openal-soft/Alc/{ => mixer}/mixer_sse2.c | 18 +- Engine/lib/openal-soft/Alc/mixer/mixer_sse3.c | 0 .../lib/openal-soft/Alc/mixer/mixer_sse41.c | 88 + Engine/lib/openal-soft/Alc/mixer_c.c | 228 -- Engine/lib/openal-soft/Alc/mixer_defs.h | 110 - Engine/lib/openal-soft/Alc/mixer_inc.c | 149 - Engine/lib/openal-soft/Alc/mixer_neon.c | 173 -- Engine/lib/openal-soft/Alc/mixer_sse3.c | 164 -- Engine/lib/openal-soft/Alc/mixer_sse41.c | 227 -- Engine/lib/openal-soft/Alc/mixvoice.c | 761 +++++ Engine/lib/openal-soft/Alc/panning.c | 908 +++--- Engine/lib/openal-soft/Alc/polymorphism.h | 105 + Engine/lib/openal-soft/Alc/ringbuffer.c | 295 ++ Engine/lib/openal-soft/Alc/ringbuffer.h | 77 + Engine/lib/openal-soft/Alc/uhjfilter.c | 90 +- Engine/lib/openal-soft/Alc/uhjfilter.h | 11 +- Engine/lib/openal-soft/Alc/vector.h | 24 +- Engine/lib/openal-soft/CMakeLists.txt | 967 ++++--- Engine/lib/openal-soft/ChangeLog | 165 ++ .../OpenAL32/Include/alAuxEffectSlot.h | 114 +- .../openal-soft/OpenAL32/Include/alBuffer.h | 109 +- .../openal-soft/OpenAL32/Include/alEffect.h | 78 +- .../openal-soft/OpenAL32/Include/alError.h | 22 +- .../openal-soft/OpenAL32/Include/alFilter.h | 146 +- .../openal-soft/OpenAL32/Include/alListener.h | 49 +- .../lib/openal-soft/OpenAL32/Include/alMain.h | 909 +++--- .../openal-soft/OpenAL32/Include/alSource.h | 153 +- .../openal-soft/OpenAL32/Include/alThunk.h | 20 - Engine/lib/openal-soft/OpenAL32/Include/alu.h | 379 ++- .../lib/openal-soft/OpenAL32/Include/bs2b.h | 2 +- .../openal-soft/OpenAL32/Include/sample_cvt.h | 8 +- .../openal-soft/OpenAL32/alAuxEffectSlot.c | 517 ++-- Engine/lib/openal-soft/OpenAL32/alBuffer.c | 1334 ++++----- Engine/lib/openal-soft/OpenAL32/alEffect.c | 312 +- Engine/lib/openal-soft/OpenAL32/alError.c | 54 +- Engine/lib/openal-soft/OpenAL32/alExtension.c | 20 +- Engine/lib/openal-soft/OpenAL32/alFilter.c | 534 ++-- Engine/lib/openal-soft/OpenAL32/alListener.c | 253 +- Engine/lib/openal-soft/OpenAL32/alSource.c | 2322 ++++++++------- Engine/lib/openal-soft/OpenAL32/alState.c | 387 ++- Engine/lib/openal-soft/OpenAL32/alThunk.c | 105 - Engine/lib/openal-soft/OpenAL32/event.c | 140 + Engine/lib/openal-soft/OpenAL32/sample_cvt.c | 968 +------ Engine/lib/openal-soft/README | 55 - Engine/lib/openal-soft/README.md | 61 + Engine/lib/openal-soft/alsoftrc.sample | 110 +- Engine/lib/openal-soft/appveyor.yml | 9 +- .../cmake/CheckSharedFunctionExists.cmake | 4 +- Engine/lib/openal-soft/cmake/FindDSound.cmake | 30 +- Engine/lib/openal-soft/cmake/FindFFmpeg.cmake | 6 + Engine/lib/openal-soft/cmake/FindOSS.cmake | 14 +- Engine/lib/openal-soft/cmake/FindSDL2.cmake | 20 +- .../openal-soft/cmake/FindWindowsSDK.cmake | 626 +++++ .../openal-soft/{include => common}/align.h | 0 Engine/lib/openal-soft/common/almalloc.c | 48 + Engine/lib/openal-soft/common/almalloc.h | 30 + Engine/lib/openal-soft/common/atomic.h | 439 +++ .../openal-soft/{include => common}/bool.h | 0 Engine/lib/openal-soft/common/math_defs.h | 46 + Engine/lib/openal-soft/common/rwlock.c | 20 +- .../openal-soft/{include => common}/rwlock.h | 9 +- .../{include => common}/static_assert.h | 0 Engine/lib/openal-soft/common/threads.c | 245 +- .../openal-soft/{include => common}/threads.h | 29 +- Engine/lib/openal-soft/common/uintmap.c | 138 +- .../openal-soft/{include => common}/uintmap.h | 18 +- Engine/lib/openal-soft/common/win_main_utf8.h | 97 + Engine/lib/openal-soft/config.h.in | 44 +- Engine/lib/openal-soft/include/AL/alext.h | 73 + Engine/lib/openal-soft/include/almalloc.h | 18 - Engine/lib/openal-soft/include/atomic.h | 317 --- Engine/lib/openal-soft/include/math_defs.h | 19 - .../openal-soft/native-tools/CMakeLists.txt | 29 + Engine/lib/openal-soft/native-tools/bin2h.c | 100 + .../lib/openal-soft/native-tools/bsincgen.c | 367 +++ Engine/lib/openal-soft/openal.pc.in | 1 + Engine/lib/openal-soft/version.cmake | 11 + Engine/lib/openal-soft/version.h.in | 8 + Tools/CMake/torque3d.cmake | 8 + 149 files changed, 22293 insertions(+), 16887 deletions(-) delete mode 100644 Engine/lib/openal-soft/Alc/alcRing.c rename Engine/lib/openal-soft/Alc/{alcConfig.c => alconfig.c} (70%) create mode 100644 Engine/lib/openal-soft/Alc/alconfig.h create mode 100644 Engine/lib/openal-soft/Alc/backends/sdl2.c rename Engine/lib/openal-soft/Alc/backends/{mmdevapi.c => wasapi.c} (67%) delete mode 100644 Engine/lib/openal-soft/Alc/bsinc.c create mode 100644 Engine/lib/openal-soft/Alc/converter.c create mode 100644 Engine/lib/openal-soft/Alc/converter.h create mode 100644 Engine/lib/openal-soft/Alc/cpu_caps.h delete mode 100644 Engine/lib/openal-soft/Alc/effects/flanger.c create mode 100644 Engine/lib/openal-soft/Alc/effects/pshifter.c create mode 100644 Engine/lib/openal-soft/Alc/filters/defs.h create mode 100644 Engine/lib/openal-soft/Alc/filters/filter.c create mode 100644 Engine/lib/openal-soft/Alc/filters/nfc.c create mode 100644 Engine/lib/openal-soft/Alc/filters/nfc.h create mode 100644 Engine/lib/openal-soft/Alc/filters/splitter.c create mode 100644 Engine/lib/openal-soft/Alc/filters/splitter.h create mode 100644 Engine/lib/openal-soft/Alc/fpu_modes.h delete mode 100644 Engine/lib/openal-soft/Alc/hrtf_res.h delete mode 100644 Engine/lib/openal-soft/Alc/hrtf_res.rc create mode 100644 Engine/lib/openal-soft/Alc/inprogext.h create mode 100644 Engine/lib/openal-soft/Alc/logging.h create mode 100644 Engine/lib/openal-soft/Alc/mastering.c create mode 100644 Engine/lib/openal-soft/Alc/mastering.h delete mode 100644 Engine/lib/openal-soft/Alc/mixer.c create mode 100644 Engine/lib/openal-soft/Alc/mixer/defs.h create mode 100644 Engine/lib/openal-soft/Alc/mixer/hrtf_inc.c create mode 100644 Engine/lib/openal-soft/Alc/mixer/mixer_c.c create mode 100644 Engine/lib/openal-soft/Alc/mixer/mixer_neon.c rename Engine/lib/openal-soft/Alc/{ => mixer}/mixer_sse.c (55%) rename Engine/lib/openal-soft/Alc/{ => mixer}/mixer_sse2.c (86%) create mode 100644 Engine/lib/openal-soft/Alc/mixer/mixer_sse3.c create mode 100644 Engine/lib/openal-soft/Alc/mixer/mixer_sse41.c delete mode 100644 Engine/lib/openal-soft/Alc/mixer_c.c delete mode 100644 Engine/lib/openal-soft/Alc/mixer_defs.h delete mode 100644 Engine/lib/openal-soft/Alc/mixer_inc.c delete mode 100644 Engine/lib/openal-soft/Alc/mixer_neon.c delete mode 100644 Engine/lib/openal-soft/Alc/mixer_sse3.c delete mode 100644 Engine/lib/openal-soft/Alc/mixer_sse41.c create mode 100644 Engine/lib/openal-soft/Alc/mixvoice.c create mode 100644 Engine/lib/openal-soft/Alc/polymorphism.h create mode 100644 Engine/lib/openal-soft/Alc/ringbuffer.c create mode 100644 Engine/lib/openal-soft/Alc/ringbuffer.h delete mode 100644 Engine/lib/openal-soft/OpenAL32/Include/alThunk.h delete mode 100644 Engine/lib/openal-soft/OpenAL32/alThunk.c create mode 100644 Engine/lib/openal-soft/OpenAL32/event.c delete mode 100644 Engine/lib/openal-soft/README create mode 100644 Engine/lib/openal-soft/README.md create mode 100644 Engine/lib/openal-soft/cmake/FindWindowsSDK.cmake rename Engine/lib/openal-soft/{include => common}/align.h (100%) create mode 100644 Engine/lib/openal-soft/common/almalloc.h create mode 100644 Engine/lib/openal-soft/common/atomic.h rename Engine/lib/openal-soft/{include => common}/bool.h (100%) create mode 100644 Engine/lib/openal-soft/common/math_defs.h rename Engine/lib/openal-soft/{include => common}/rwlock.h (67%) rename Engine/lib/openal-soft/{include => common}/static_assert.h (100%) rename Engine/lib/openal-soft/{include => common}/threads.h (83%) rename Engine/lib/openal-soft/{include => common}/uintmap.h (60%) create mode 100644 Engine/lib/openal-soft/common/win_main_utf8.h delete mode 100644 Engine/lib/openal-soft/include/almalloc.h delete mode 100644 Engine/lib/openal-soft/include/atomic.h delete mode 100644 Engine/lib/openal-soft/include/math_defs.h create mode 100644 Engine/lib/openal-soft/native-tools/CMakeLists.txt create mode 100644 Engine/lib/openal-soft/native-tools/bin2h.c create mode 100644 Engine/lib/openal-soft/native-tools/bsincgen.c create mode 100644 Engine/lib/openal-soft/version.cmake create mode 100644 Engine/lib/openal-soft/version.h.in diff --git a/Engine/lib/openal-soft/.gitignore b/Engine/lib/openal-soft/.gitignore index 9aa081c61..de57ef223 100644 --- a/Engine/lib/openal-soft/.gitignore +++ b/Engine/lib/openal-soft/.gitignore @@ -1,7 +1,5 @@ -build -winbuild -win64build -include/SLES -include/sndio.h -include/sys +build*/ +winbuild/ +win64build/ openal-soft.kdev4 +.kdev4/ diff --git a/Engine/lib/openal-soft/.travis.yml b/Engine/lib/openal-soft/.travis.yml index dfae8e7a5..426eef40c 100644 --- a/Engine/lib/openal-soft/.travis.yml +++ b/Engine/lib/openal-soft/.travis.yml @@ -1,5 +1,66 @@ -os: - - linux - - osx language: c -script: cmake . && make -j2 +matrix: + include: + - os: linux + dist: trusty + - os: linux + dist: trusty + env: + - BUILD_ANDROID=true + - os: osx +sudo: required +install: + - > + if [[ "${TRAVIS_OS_NAME}" == "linux" && -z "${BUILD_ANDROID}" ]]; then + # Install pulseaudio, portaudio, ALSA, JACK dependencies for + # corresponding backends. + # Install Qt5 dependency for alsoft-config. + sudo apt-get install -qq \ + libpulse-dev \ + portaudio19-dev \ + libasound2-dev \ + libjack-dev \ + qtbase5-dev + fi + - > + if [[ "${TRAVIS_OS_NAME}" == "linux" && "${BUILD_ANDROID}" == "true" ]]; then + curl -o ~/android-ndk.zip https://dl.google.com/android/repository/android-ndk-r15-linux-x86_64.zip + unzip -q ~/android-ndk.zip -d ~ \ + 'android-ndk-r15/build/cmake/*' \ + 'android-ndk-r15/build/core/toolchains/arm-linux-androideabi-*/*' \ + 'android-ndk-r15/platforms/android-14/arch-arm/*' \ + 'android-ndk-r15/source.properties' \ + 'android-ndk-r15/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/*' \ + 'android-ndk-r15/sources/cxx-stl/gnu-libstdc++/4.9/include/*' \ + 'android-ndk-r15/sysroot/*' \ + 'android-ndk-r15/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/*' \ + 'android-ndk-r15/toolchains/llvm/prebuilt/linux-x86_64/*' + fi +script: + - > + if [[ "${TRAVIS_OS_NAME}" == "linux" && -z "${BUILD_ANDROID}" ]]; then + cmake \ + -DALSOFT_REQUIRE_ALSA=ON \ + -DALSOFT_REQUIRE_OSS=ON \ + -DALSOFT_REQUIRE_PORTAUDIO=ON \ + -DALSOFT_REQUIRE_PULSEAUDIO=ON \ + -DALSOFT_REQUIRE_JACK=ON \ + -DALSOFT_EMBED_HRTF_DATA=YES \ + . + fi + - > + if [[ "${TRAVIS_OS_NAME}" == "linux" && "${BUILD_ANDROID}" == "true" ]]; then + cmake \ + -DCMAKE_TOOLCHAIN_FILE=~/android-ndk-r15/build/cmake/android.toolchain.cmake \ + -DALSOFT_REQUIRE_OPENSL=ON \ + -DALSOFT_EMBED_HRTF_DATA=YES \ + . + fi + - > + if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then + cmake \ + -DALSOFT_REQUIRE_COREAUDIO=ON \ + -DALSOFT_EMBED_HRTF_DATA=YES \ + . + fi + - make -j2 diff --git a/Engine/lib/openal-soft/Alc/ALc.c b/Engine/lib/openal-soft/Alc/ALc.c index 7e220205c..597cc890c 100644 --- a/Engine/lib/openal-soft/Alc/ALc.c +++ b/Engine/lib/openal-soft/Alc/ALc.c @@ -20,6 +20,8 @@ #include "config.h" +#include "version.h" + #include #include #include @@ -30,14 +32,20 @@ #include "alMain.h" #include "alSource.h" #include "alListener.h" -#include "alThunk.h" #include "alSource.h" #include "alBuffer.h" +#include "alFilter.h" +#include "alEffect.h" #include "alAuxEffectSlot.h" #include "alError.h" +#include "mastering.h" #include "bformatdec.h" #include "alu.h" +#include "alconfig.h" +#include "ringbuffer.h" +#include "fpu_modes.h" +#include "cpu_caps.h" #include "compat.h" #include "threads.h" #include "alstring.h" @@ -52,61 +60,58 @@ struct BackendInfo { const char *name; ALCbackendFactory* (*getFactory)(void); - ALCboolean (*Init)(BackendFuncs*); - void (*Deinit)(void); - void (*Probe)(enum DevProbe); - BackendFuncs Funcs; }; -#define EmptyFuncs { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } static struct BackendInfo BackendList[] = { #ifdef HAVE_JACK - { "jack", ALCjackBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs }, + { "jack", ALCjackBackendFactory_getFactory }, #endif #ifdef HAVE_PULSEAUDIO - { "pulse", ALCpulseBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs }, + { "pulse", ALCpulseBackendFactory_getFactory }, #endif #ifdef HAVE_ALSA - { "alsa", ALCalsaBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs }, + { "alsa", ALCalsaBackendFactory_getFactory }, #endif #ifdef HAVE_COREAUDIO - { "core", NULL, alc_ca_init, alc_ca_deinit, alc_ca_probe, EmptyFuncs }, + { "core", ALCcoreAudioBackendFactory_getFactory }, #endif #ifdef HAVE_OSS - { "oss", ALCossBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs }, + { "oss", ALCossBackendFactory_getFactory }, #endif #ifdef HAVE_SOLARIS - { "solaris", ALCsolarisBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs }, + { "solaris", ALCsolarisBackendFactory_getFactory }, #endif #ifdef HAVE_SNDIO - { "sndio", NULL, alc_sndio_init, alc_sndio_deinit, alc_sndio_probe, EmptyFuncs }, + { "sndio", ALCsndioBackendFactory_getFactory }, #endif #ifdef HAVE_QSA - { "qsa", NULL, alc_qsa_init, alc_qsa_deinit, alc_qsa_probe, EmptyFuncs }, + { "qsa", ALCqsaBackendFactory_getFactory }, #endif -#ifdef HAVE_MMDEVAPI - { "mmdevapi", ALCmmdevBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs }, +#ifdef HAVE_WASAPI + { "wasapi", ALCwasapiBackendFactory_getFactory }, #endif #ifdef HAVE_DSOUND - { "dsound", ALCdsoundBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs }, + { "dsound", ALCdsoundBackendFactory_getFactory }, #endif #ifdef HAVE_WINMM - { "winmm", ALCwinmmBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs }, + { "winmm", ALCwinmmBackendFactory_getFactory }, #endif #ifdef HAVE_PORTAUDIO - { "port", ALCportBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs }, + { "port", ALCportBackendFactory_getFactory }, #endif #ifdef HAVE_OPENSL - { "opensl", NULL, alc_opensl_init, alc_opensl_deinit, alc_opensl_probe, EmptyFuncs }, + { "opensl", ALCopenslBackendFactory_getFactory }, +#endif +#ifdef HAVE_SDL2 + { "sdl2", ALCsdl2BackendFactory_getFactory }, #endif - { "null", ALCnullBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs }, + { "null", ALCnullBackendFactory_getFactory }, #ifdef HAVE_WAVE - { "wave", ALCwaveBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs }, + { "wave", ALCwaveBackendFactory_getFactory }, #endif - - { NULL, NULL, NULL, NULL, NULL, EmptyFuncs } }; +static ALsizei BackendListSize = COUNTOF(BackendList); #undef EmptyFuncs static struct BackendInfo PlaybackBackend; @@ -116,18 +121,11 @@ static struct BackendInfo CaptureBackend; /************************************************ * Functions, enums, and errors ************************************************/ -typedef struct ALCfunction { +#define DECL(x) { #x, (ALCvoid*)(x) } +static const struct { const ALCchar *funcName; ALCvoid *address; -} ALCfunction; - -typedef struct ALCenums { - const ALCchar *enumName; - ALCenum value; -} ALCenums; - -#define DECL(x) { #x, (ALCvoid*)(x) } -static const ALCfunction alcFunctions[] = { +} alcFunctions[] = { DECL(alcCreateContext), DECL(alcMakeContextCurrent), DECL(alcProcessContext), @@ -288,16 +286,25 @@ static const ALCfunction alcFunctions[] = { DECL(alGetSource3i64SOFT), DECL(alGetSourcei64vSOFT), - DECL(alBufferSamplesSOFT), - DECL(alGetBufferSamplesSOFT), - DECL(alIsBufferFormatSupportedSOFT), + DECL(alGetStringiSOFT), - { NULL, NULL } + DECL(alBufferStorageSOFT), + DECL(alMapBufferSOFT), + DECL(alUnmapBufferSOFT), + DECL(alFlushMappedBufferSOFT), + + DECL(alEventControlSOFT), + DECL(alEventCallbackSOFT), + DECL(alGetPointerSOFT), + DECL(alGetPointervSOFT), }; #undef DECL #define DECL(x) { #x, (x) } -static const ALCenums enumeration[] = { +static const struct { + const ALCchar *enumName; + ALCenum value; +} alcEnumerations[] = { DECL(ALC_INVALID), DECL(ALC_FALSE), DECL(ALC_TRUE), @@ -334,6 +341,7 @@ static const ALCenums enumeration[] = { 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), @@ -356,6 +364,16 @@ static const ALCenums enumeration[] = { 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), @@ -465,64 +483,10 @@ static const ALCenums enumeration[] = { DECL(AL_FORMAT_BFORMAT3D_FLOAT32), DECL(AL_FORMAT_BFORMAT3D_MULAW), - DECL(AL_MONO8_SOFT), - DECL(AL_MONO16_SOFT), - DECL(AL_MONO32F_SOFT), - DECL(AL_STEREO8_SOFT), - DECL(AL_STEREO16_SOFT), - DECL(AL_STEREO32F_SOFT), - DECL(AL_QUAD8_SOFT), - DECL(AL_QUAD16_SOFT), - DECL(AL_QUAD32F_SOFT), - DECL(AL_REAR8_SOFT), - DECL(AL_REAR16_SOFT), - DECL(AL_REAR32F_SOFT), - DECL(AL_5POINT1_8_SOFT), - DECL(AL_5POINT1_16_SOFT), - DECL(AL_5POINT1_32F_SOFT), - DECL(AL_6POINT1_8_SOFT), - DECL(AL_6POINT1_16_SOFT), - DECL(AL_6POINT1_32F_SOFT), - DECL(AL_7POINT1_8_SOFT), - DECL(AL_7POINT1_16_SOFT), - DECL(AL_7POINT1_32F_SOFT), - DECL(AL_BFORMAT2D_8_SOFT), - DECL(AL_BFORMAT2D_16_SOFT), - DECL(AL_BFORMAT2D_32F_SOFT), - DECL(AL_BFORMAT3D_8_SOFT), - DECL(AL_BFORMAT3D_16_SOFT), - DECL(AL_BFORMAT3D_32F_SOFT), - - DECL(AL_MONO_SOFT), - DECL(AL_STEREO_SOFT), - DECL(AL_QUAD_SOFT), - DECL(AL_REAR_SOFT), - DECL(AL_5POINT1_SOFT), - DECL(AL_6POINT1_SOFT), - DECL(AL_7POINT1_SOFT), - DECL(AL_BFORMAT2D_SOFT), - DECL(AL_BFORMAT3D_SOFT), - - DECL(AL_BYTE_SOFT), - DECL(AL_UNSIGNED_BYTE_SOFT), - DECL(AL_SHORT_SOFT), - DECL(AL_UNSIGNED_SHORT_SOFT), - DECL(AL_INT_SOFT), - DECL(AL_UNSIGNED_INT_SOFT), - DECL(AL_FLOAT_SOFT), - DECL(AL_DOUBLE_SOFT), - DECL(AL_BYTE3_SOFT), - DECL(AL_UNSIGNED_BYTE3_SOFT), - DECL(AL_MULAW_SOFT), - DECL(AL_FREQUENCY), DECL(AL_BITS), DECL(AL_CHANNELS), DECL(AL_SIZE), - DECL(AL_INTERNAL_FORMAT_SOFT), - DECL(AL_BYTE_LENGTH_SOFT), - DECL(AL_SAMPLE_LENGTH_SOFT), - DECL(AL_SEC_LENGTH_SOFT), DECL(AL_UNPACK_BLOCK_ALIGNMENT_SOFT), DECL(AL_PACK_BLOCK_ALIGNMENT_SOFT), @@ -585,10 +549,10 @@ static const ALCenums enumeration[] = { DECL(AL_EFFECT_DISTORTION), DECL(AL_EFFECT_ECHO), DECL(AL_EFFECT_FLANGER), + DECL(AL_EFFECT_PITCH_SHIFTER), #if 0 DECL(AL_EFFECT_FREQUENCY_SHIFTER), DECL(AL_EFFECT_VOCAL_MORPHER), - DECL(AL_EFFECT_PITCH_SHIFTER), #endif DECL(AL_EFFECT_RING_MODULATOR), #if 0 @@ -599,6 +563,11 @@ static const ALCenums enumeration[] = { 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), @@ -667,6 +636,9 @@ static const ALCenums enumeration[] = { 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), @@ -682,7 +654,26 @@ static const ALCenums enumeration[] = { DECL(AL_DEDICATED_GAIN), - { NULL, (ALCenum)0 } + 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 @@ -710,13 +701,34 @@ static ALCchar *alcCaptureDefaultDeviceSpecifier; /* Default context extensions */ static const ALchar alExtList[] = - "AL_EXT_ALAW AL_EXT_BFORMAT AL_EXT_DOUBLE AL_EXT_EXPONENT_DISTANCE " - "AL_EXT_FLOAT32 AL_EXT_IMA4 AL_EXT_LINEAR_DISTANCE AL_EXT_MCFORMATS " - "AL_EXT_MULAW AL_EXT_MULAW_BFORMAT AL_EXT_MULAW_MCFORMATS AL_EXT_OFFSET " - "AL_EXT_source_distance_model AL_EXT_SOURCE_RADIUS AL_EXT_STEREO_ANGLES " - "AL_LOKI_quadriphonic AL_SOFT_block_alignment AL_SOFT_deferred_updates " - "AL_SOFT_direct_channels AL_SOFT_gain_clamp_ex AL_SOFT_loop_points " - "AL_SOFT_MSADPCM AL_SOFT_source_latency AL_SOFT_source_length"; + "AL_EXT_ALAW " + "AL_EXT_BFORMAT " + "AL_EXT_DOUBLE " + "AL_EXT_EXPONENT_DISTANCE " + "AL_EXT_FLOAT32 " + "AL_EXT_IMA4 " + "AL_EXT_LINEAR_DISTANCE " + "AL_EXT_MCFORMATS " + "AL_EXT_MULAW " + "AL_EXT_MULAW_BFORMAT " + "AL_EXT_MULAW_MCFORMATS " + "AL_EXT_OFFSET " + "AL_EXT_source_distance_model " + "AL_EXT_SOURCE_RADIUS " + "AL_EXT_STEREO_ANGLES " + "AL_LOKI_quadriphonic " + "AL_SOFT_block_alignment " + "AL_SOFT_deferred_updates " + "AL_SOFT_direct_channels " + "AL_SOFTX_events " + "AL_SOFT_gain_clamp_ex " + "AL_SOFT_loop_points " + "AL_SOFTX_map_buffer " + "AL_SOFT_MSADPCM " + "AL_SOFT_source_latency " + "AL_SOFT_source_length " + "AL_SOFT_source_resampler " + "AL_SOFT_source_spatialize"; static ATOMIC(ALCenum) LastNullDeviceError = ATOMIC_INIT_STATIC(ALC_NO_ERROR); @@ -759,8 +771,8 @@ static const ALCchar alcNoDeviceExtList[] = static const ALCchar alcExtensionList[] = "ALC_ENUMERATE_ALL_EXT ALC_ENUMERATION_EXT ALC_EXT_CAPTURE " "ALC_EXT_DEDICATED ALC_EXT_disconnect ALC_EXT_EFX " - "ALC_EXT_thread_local_context ALC_SOFTX_device_clock ALC_SOFT_HRTF " - "ALC_SOFT_loopback ALC_SOFT_pause_device"; + "ALC_EXT_thread_local_context ALC_SOFT_device_clock ALC_SOFT_HRTF " + "ALC_SOFT_loopback ALC_SOFT_output_limiter ALC_SOFT_pause_device"; static const ALCint alcMajorVersion = 1; static const ALCint alcMinorVersion = 1; @@ -806,6 +818,7 @@ BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD reason, LPVOID lpReserved) break; case DLL_THREAD_DETACH: + althrd_thread_detach(); break; case DLL_PROCESS_DETACH: @@ -868,19 +881,21 @@ static void alc_init(void) if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1)) ZScale *= -1.0f; + str = getenv("__ALSOFT_REVERB_IGNORES_SOUND_SPEED"); + if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1)) + OverrideReverbSpeedOfSound = AL_TRUE; + ret = altss_create(&LocalContext, ReleaseThreadCtx); assert(ret == althrd_success); ret = almtx_init(&ListLock, almtx_recursive); assert(ret == althrd_success); - - ThunkInit(); } static void alc_initconfig(void) { const char *devs, *str; - ALuint capfilter; + int capfilter; float valf; int i, n; @@ -900,10 +915,15 @@ static void alc_initconfig(void) else ERR("Failed to open log file '%s'\n", str); } + TRACE("Initializing library v%s-%s %s\n", ALSOFT_VERSION, + ALSOFT_GIT_COMMIT_HASH, ALSOFT_GIT_BRANCH); { char buf[1024] = ""; - int len = snprintf(buf, sizeof(buf), "%s", BackendList[0].name); - for(i = 1;BackendList[i].name;i++) + int len = 0; + + if(BackendListSize > 0) + len += snprintf(buf, sizeof(buf), "%s", BackendList[0].name); + for(i = 1;i < BackendListSize;i++) len += snprintf(buf+len, sizeof(buf)-len, ", %s", BackendList[i].name); TRACE("Supported backends: %s\n", buf); } @@ -979,6 +999,7 @@ static void alc_initconfig(void) #endif ConfigValueInt(NULL, NULL, "rt-prio", &RTPrioLevel); + aluInit(); aluInitMixer(); str = getenv("ALSOFT_TRAP_ERROR"); @@ -1003,8 +1024,6 @@ static void alc_initconfig(void) if(ConfigValueFloat(NULL, "reverb", "boost", &valf)) ReverbBoost *= powf(10.0f, valf / 20.0f); - EmulateEAXReverb = GetConfigValueBool(NULL, "reverb", "emulate-eax", AL_FALSE); - if(((devs=getenv("ALSOFT_DRIVERS")) && devs[0]) || ConfigValueStr(NULL, NULL, "drivers", &devs)) { @@ -1033,26 +1052,32 @@ static void alc_initconfig(void) len = (next ? ((size_t)(next-devs)) : strlen(devs)); while(len > 0 && isspace(devs[len-1])) len--; - for(n = i;BackendList[n].name;n++) +#ifdef HAVE_WASAPI + /* HACK: For backwards compatibility, convert backend references of + * mmdevapi to wasapi. This should eventually be removed. + */ + if(len == 8 && strncmp(devs, "mmdevapi", len) == 0) + { + devs = "wasapi"; + len = 6; + } +#endif + for(n = i;n < BackendListSize;n++) { if(len == strlen(BackendList[n].name) && strncmp(BackendList[n].name, devs, len) == 0) { if(delitem) { - do { + for(;n+1 < BackendListSize;n++) BackendList[n] = BackendList[n+1]; - ++n; - } while(BackendList[n].name); + BackendListSize--; } else { struct BackendInfo Bkp = BackendList[n]; - while(n > i) - { + for(;n > i;n--) BackendList[n] = BackendList[n-1]; - --n; - } BackendList[n] = Bkp; i++; @@ -1063,59 +1088,36 @@ static void alc_initconfig(void) } while(next++); if(endlist) - { - BackendList[i].name = NULL; - BackendList[i].getFactory = NULL; - BackendList[i].Init = NULL; - BackendList[i].Deinit = NULL; - BackendList[i].Probe = NULL; - } + BackendListSize = i; } - for(i = 0;(BackendList[i].Init || BackendList[i].getFactory) && (!PlaybackBackend.name || !CaptureBackend.name);i++) + for(n = i = 0;i < BackendListSize && (!PlaybackBackend.name || !CaptureBackend.name);i++) { - if(BackendList[i].getFactory) + ALCbackendFactory *factory; + BackendList[n] = BackendList[i]; + + factory = BackendList[n].getFactory(); + if(!V0(factory,init)()) { - ALCbackendFactory *factory = BackendList[i].getFactory(); - if(!V0(factory,init)()) - { - WARN("Failed to initialize backend \"%s\"\n", BackendList[i].name); - continue; - } - - TRACE("Initialized backend \"%s\"\n", BackendList[i].name); - if(!PlaybackBackend.name && V(factory,querySupport)(ALCbackend_Playback)) - { - PlaybackBackend = BackendList[i]; - TRACE("Added \"%s\" for playback\n", PlaybackBackend.name); - } - if(!CaptureBackend.name && V(factory,querySupport)(ALCbackend_Capture)) - { - CaptureBackend = BackendList[i]; - TRACE("Added \"%s\" for capture\n", CaptureBackend.name); - } - + WARN("Failed to initialize backend \"%s\"\n", BackendList[n].name); continue; } - if(!BackendList[i].Init(&BackendList[i].Funcs)) + TRACE("Initialized backend \"%s\"\n", BackendList[n].name); + if(!PlaybackBackend.name && V(factory,querySupport)(ALCbackend_Playback)) { - WARN("Failed to initialize backend \"%s\"\n", BackendList[i].name); - continue; - } - - TRACE("Initialized backend \"%s\"\n", BackendList[i].name); - if(BackendList[i].Funcs.OpenPlayback && !PlaybackBackend.name) - { - PlaybackBackend = BackendList[i]; + PlaybackBackend = BackendList[n]; TRACE("Added \"%s\" for playback\n", PlaybackBackend.name); } - if(BackendList[i].Funcs.OpenCapture && !CaptureBackend.name) + if(!CaptureBackend.name && V(factory,querySupport)(ALCbackend_Capture)) { - CaptureBackend = BackendList[i]; + CaptureBackend = BackendList[n]; TRACE("Added \"%s\" for capture\n", CaptureBackend.name); } + n++; } + BackendListSize = n; + { ALCbackendFactory *factory = ALCloopbackFactory_getFactory(); V0(factory,init)(); @@ -1139,7 +1141,7 @@ static void alc_initconfig(void) continue; len = (next ? ((size_t)(next-str)) : strlen(str)); - for(n = 0;EffectList[n].name;n++) + for(n = 0;n < EFFECTLIST_SIZE;n++) { if(len == strlen(EffectList[n].name) && strncmp(EffectList[n].name, str, len) == 0) @@ -1148,8 +1150,6 @@ static void alc_initconfig(void) } while(next++); } - InitEffectFactoryMap(); - InitEffect(&DefaultEffect); str = getenv("ALSOFT_DEFAULT_REVERB"); if((str && str[0]) || ConfigValueStr(NULL, NULL, "default-reverb", &str)) @@ -1157,6 +1157,75 @@ static void alc_initconfig(void) } #define DO_INITCONFIG() alcall_once(&alc_config_once, alc_initconfig) +#ifdef __ANDROID__ +#include + +static JavaVM *gJavaVM; +static pthread_key_t gJVMThreadKey; + +static void CleanupJNIEnv(void* UNUSED(ptr)) +{ + JCALL0(gJavaVM,DetachCurrentThread)(); +} + +void *Android_GetJNIEnv(void) +{ + if(!gJavaVM) + { + WARN("gJavaVM is NULL!\n"); + return NULL; + } + + /* http://developer.android.com/guide/practices/jni.html + * + * All threads are Linux threads, scheduled by the kernel. They're usually + * started from managed code (using Thread.start), but they can also be + * created elsewhere and then attached to the JavaVM. For example, a thread + * started with pthread_create can be attached with the JNI + * AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a + * thread is attached, it has no JNIEnv, and cannot make JNI calls. + * Attaching a natively-created thread causes a java.lang.Thread object to + * be constructed and added to the "main" ThreadGroup, making it visible to + * the debugger. Calling AttachCurrentThread on an already-attached thread + * is a no-op. + */ + JNIEnv *env = pthread_getspecific(gJVMThreadKey); + if(!env) + { + int status = JCALL(gJavaVM,AttachCurrentThread)(&env, NULL); + if(status < 0) + { + ERR("Failed to attach current thread\n"); + return NULL; + } + pthread_setspecific(gJVMThreadKey, env); + } + return env; +} + +/* Automatically called by JNI. */ +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void* UNUSED(reserved)) +{ + void *env; + int err; + + gJavaVM = jvm; + if(JCALL(gJavaVM,GetEnv)(&env, JNI_VERSION_1_4) != JNI_OK) + { + ERR("Failed to get JNIEnv with JNI_VERSION_1_4\n"); + return JNI_ERR; + } + + /* Create gJVMThreadKey so we can keep track of the JNIEnv assigned to each + * thread. The JNIEnv *must* be detached before the thread is destroyed. + */ + if((err=pthread_key_create(&gJVMThreadKey, CleanupJNIEnv)) != 0) + ERR("pthread_key_create failed: %d\n", err); + pthread_setspecific(gJVMThreadKey, env); + return JNI_VERSION_1_4; +} +#endif + /************************************************ * Library deinitialization @@ -1173,16 +1242,15 @@ static void alc_cleanup(void) free(alcCaptureDefaultDeviceSpecifier); alcCaptureDefaultDeviceSpecifier = NULL; - if((dev=ATOMIC_EXCHANGE(ALCdevice*, &DeviceList, NULL)) != NULL) + if((dev=ATOMIC_EXCHANGE_PTR_SEQ(&DeviceList, NULL)) != NULL) { ALCuint num = 0; do { num++; - } while((dev=dev->next) != NULL); + dev = ATOMIC_LOAD(&dev->next, almemory_order_relaxed); + } while(dev != NULL); ERR("%u device%s not closed\n", num, (num>1)?"s":""); } - - DeinitEffectFactoryMap(); } static void alc_deinit_safe(void) @@ -1192,13 +1260,14 @@ static void alc_deinit_safe(void) FreeHrtfs(); FreeALConfig(); - ThunkExit(); almtx_destroy(&ListLock); altss_delete(LocalContext); if(LogFile != stderr) fclose(LogFile); LogFile = NULL; + + althrd_deinit(); } static void alc_deinit(void) @@ -1210,15 +1279,10 @@ static void alc_deinit(void) memset(&PlaybackBackend, 0, sizeof(PlaybackBackend)); memset(&CaptureBackend, 0, sizeof(CaptureBackend)); - for(i = 0;BackendList[i].Deinit || BackendList[i].getFactory;i++) + for(i = 0;i < BackendListSize;i++) { - if(!BackendList[i].getFactory) - BackendList[i].Deinit(); - else - { - ALCbackendFactory *factory = BackendList[i].getFactory(); - V0(factory,deinit)(); - } + ALCbackendFactory *factory = BackendList[i].getFactory(); + V0(factory,deinit)(); } { ALCbackendFactory *factory = ALCloopbackFactory_getFactory(); @@ -1237,11 +1301,9 @@ static void ProbeDevices(al_string *list, struct BackendInfo *backendinfo, enum DO_INITCONFIG(); LockLists(); - al_string_clear(list); + alstr_clear(list); - if(backendinfo->Probe) - backendinfo->Probe(type); - else if(backendinfo->getFactory) + if(backendinfo->getFactory) { ALCbackendFactory *factory = backendinfo->getFactory(); V(factory,probe)(type); @@ -1258,7 +1320,7 @@ static void AppendDevice(const ALCchar *name, al_string *devnames) { size_t len = strlen(name); if(len > 0) - al_string_append_range(devnames, name, name+len+1); + alstr_append_range(devnames, name, name+len+1); } void AppendAllDevicesList(const ALCchar *name) { AppendDevice(name, &alcAllDevicesList); } @@ -1294,15 +1356,13 @@ const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans) case DevFmtX51Rear: return "5.1 Surround (Rear)"; case DevFmtX61: return "6.1 Surround"; case DevFmtX71: return "7.1 Surround"; - case DevFmtAmbi1: return "Ambisonic (1st Order)"; - case DevFmtAmbi2: return "Ambisonic (2nd Order)"; - case DevFmtAmbi3: return "Ambisonic (3rd Order)"; + case DevFmtAmbi3D: return "Ambisonic 3D"; } return "(unknown channels)"; } -extern inline ALuint FrameSizeFromDevFmt(enum DevFmtChannels chans, enum DevFmtType type); -ALuint BytesFromDevFmt(enum DevFmtType type) +extern inline ALsizei FrameSizeFromDevFmt(enum DevFmtChannels chans, enum DevFmtType type, ALsizei ambiorder); +ALsizei BytesFromDevFmt(enum DevFmtType type) { switch(type) { @@ -1316,7 +1376,7 @@ ALuint BytesFromDevFmt(enum DevFmtType type) } return 0; } -ALuint ChannelsFromDevFmt(enum DevFmtChannels chans) +ALsizei ChannelsFromDevFmt(enum DevFmtChannels chans, ALsizei ambiorder) { switch(chans) { @@ -1327,9 +1387,9 @@ ALuint ChannelsFromDevFmt(enum DevFmtChannels chans) case DevFmtX51Rear: return 6; case DevFmtX61: return 7; case DevFmtX71: return 8; - case DevFmtAmbi1: return 4; - case DevFmtAmbi2: return 9; - case DevFmtAmbi3: return 16; + case DevFmtAmbi3D: return (ambiorder >= 3) ? 16 : + (ambiorder == 2) ? 9 : + (ambiorder == 1) ? 4 : 1; } return 0; } @@ -1407,37 +1467,46 @@ static ALCboolean IsValidALCChannels(ALCenum channels) 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 ************************************************/ -extern inline void LockContext(ALCcontext *context); -extern inline void UnlockContext(ALCcontext *context); - -void ALCdevice_Lock(ALCdevice *device) -{ - V0(device->Backend,lock)(); -} - -void ALCdevice_Unlock(ALCdevice *device) -{ - V0(device->Backend,unlock)(); -} - - /* SetDefaultWFXChannelOrder * * Sets the default channel order used by WaveFormatEx. */ void SetDefaultWFXChannelOrder(ALCdevice *device) { - ALuint i; + ALsizei i; for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) device->RealOut.ChannelName[i] = InvalidChannel; @@ -1492,40 +1561,32 @@ void SetDefaultWFXChannelOrder(ALCdevice *device) device->RealOut.ChannelName[6] = SideLeft; device->RealOut.ChannelName[7] = SideRight; break; - case DevFmtAmbi1: + case DevFmtAmbi3D: device->RealOut.ChannelName[0] = Aux0; - device->RealOut.ChannelName[1] = Aux1; - device->RealOut.ChannelName[2] = Aux2; - device->RealOut.ChannelName[3] = Aux3; - break; - case DevFmtAmbi2: - device->RealOut.ChannelName[0] = Aux0; - device->RealOut.ChannelName[1] = Aux1; - device->RealOut.ChannelName[2] = Aux2; - device->RealOut.ChannelName[3] = Aux3; - device->RealOut.ChannelName[4] = Aux4; - device->RealOut.ChannelName[5] = Aux5; - device->RealOut.ChannelName[6] = Aux6; - device->RealOut.ChannelName[7] = Aux7; - device->RealOut.ChannelName[8] = Aux8; - break; - case DevFmtAmbi3: - device->RealOut.ChannelName[0] = Aux0; - device->RealOut.ChannelName[1] = Aux1; - device->RealOut.ChannelName[2] = Aux2; - device->RealOut.ChannelName[3] = Aux3; - device->RealOut.ChannelName[4] = Aux4; - device->RealOut.ChannelName[5] = Aux5; - device->RealOut.ChannelName[6] = Aux6; - device->RealOut.ChannelName[7] = Aux7; - device->RealOut.ChannelName[8] = Aux8; - device->RealOut.ChannelName[9] = Aux9; - device->RealOut.ChannelName[10] = Aux10; - device->RealOut.ChannelName[11] = Aux11; - device->RealOut.ChannelName[12] = Aux12; - device->RealOut.ChannelName[13] = Aux13; - device->RealOut.ChannelName[14] = Aux14; - device->RealOut.ChannelName[15] = Aux15; + if(device->AmbiOrder > 0) + { + device->RealOut.ChannelName[1] = Aux1; + device->RealOut.ChannelName[2] = Aux2; + device->RealOut.ChannelName[3] = Aux3; + } + if(device->AmbiOrder > 1) + { + device->RealOut.ChannelName[4] = Aux4; + device->RealOut.ChannelName[5] = Aux5; + device->RealOut.ChannelName[6] = Aux6; + device->RealOut.ChannelName[7] = Aux7; + device->RealOut.ChannelName[8] = Aux8; + } + if(device->AmbiOrder > 2) + { + device->RealOut.ChannelName[9] = Aux9; + device->RealOut.ChannelName[10] = Aux10; + device->RealOut.ChannelName[11] = Aux11; + device->RealOut.ChannelName[12] = Aux12; + device->RealOut.ChannelName[13] = Aux13; + device->RealOut.ChannelName[14] = Aux14; + device->RealOut.ChannelName[15] = Aux15; + } break; } } @@ -1536,7 +1597,7 @@ void SetDefaultWFXChannelOrder(ALCdevice *device) */ void SetDefaultChannelOrder(ALCdevice *device) { - ALuint i; + ALsizei i; for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) device->RealOut.ChannelName[i] = InvalidChannel; @@ -1568,15 +1629,14 @@ void SetDefaultChannelOrder(ALCdevice *device) case DevFmtQuad: case DevFmtX51: case DevFmtX61: - case DevFmtAmbi1: - case DevFmtAmbi2: - case DevFmtAmbi3: + case DevFmtAmbi3D: SetDefaultWFXChannelOrder(device); break; } } extern inline ALint GetChannelIndex(const enum Channel names[MAX_OUTPUT_CHANNELS], enum Channel chan); +extern inline ALint GetChannelIdxByName(const RealMixParams *real, enum Channel chan); /* ALCcontext_DeferUpdates @@ -1585,9 +1645,9 @@ extern inline ALint GetChannelIndex(const enum Channel names[MAX_OUTPUT_CHANNELS * does *NOT* stop mixing, but rather prevents certain property changes from * taking effect. */ -void ALCcontext_DeferUpdates(ALCcontext *context, ALenum type) +void ALCcontext_DeferUpdates(ALCcontext *context) { - ATOMIC_STORE(&context->DeferUpdates, type); + ATOMIC_STORE_SEQ(&context->DeferUpdates, AL_TRUE); } /* ALCcontext_ProcessUpdates @@ -1596,55 +1656,29 @@ void ALCcontext_DeferUpdates(ALCcontext *context, ALenum type) */ void ALCcontext_ProcessUpdates(ALCcontext *context) { - ALCdevice *device = context->Device; - - ReadLock(&context->PropLock); - if(ATOMIC_EXCHANGE(ALenum, &context->DeferUpdates, AL_FALSE)) + almtx_lock(&context->PropLock); + if(ATOMIC_EXCHANGE_SEQ(&context->DeferUpdates, AL_FALSE)) { - ALsizei pos; - uint updates; - /* Tell the mixer to stop applying updates, then wait for any active * updating to finish, before providing updates. */ - ATOMIC_STORE(&context->HoldUpdates, AL_TRUE); - while(((updates=ReadRef(&context->UpdateCount))&1) != 0) + ATOMIC_STORE_SEQ(&context->HoldUpdates, AL_TRUE); + while((ATOMIC_LOAD(&context->UpdateCount, almemory_order_acquire)&1) != 0) althrd_yield(); - UpdateListenerProps(context); + if(!ATOMIC_FLAG_TEST_AND_SET(&context->PropsClean, almemory_order_acq_rel)) + UpdateContextProps(context); + if(!ATOMIC_FLAG_TEST_AND_SET(&context->Listener->PropsClean, almemory_order_acq_rel)) + UpdateListenerProps(context); UpdateAllEffectSlotProps(context); - - LockUIntMapRead(&context->SourceMap); - V0(device->Backend,lock)(); - for(pos = 0;pos < context->SourceMap.size;pos++) - { - ALsource *Source = context->SourceMap.values[pos]; - ALenum new_state; - - if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) && - Source->OffsetType != AL_NONE) - { - WriteLock(&Source->queue_lock); - ApplyOffset(Source); - WriteUnlock(&Source->queue_lock); - } - - new_state = Source->new_state; - Source->new_state = AL_NONE; - if(new_state) - SetSourceState(Source, context, new_state); - } - V0(device->Backend,unlock)(); - UnlockUIntMapRead(&context->SourceMap); - UpdateAllSourceProps(context); /* Now with all updates declared, let the mixer continue applying them * so they all happen at once. */ - ATOMIC_STORE(&context->HoldUpdates, AL_FALSE); + ATOMIC_STORE_SEQ(&context->HoldUpdates, AL_FALSE); } - ReadUnlock(&context->PropLock); + almtx_unlock(&context->PropLock); } @@ -1654,6 +1688,7 @@ void ALCcontext_ProcessUpdates(ALCcontext *context) */ static void alcSetError(ALCdevice *device, ALCenum errorCode) { + WARN("Error generated on device %p, code 0x%04x\n", device, errorCode); if(TrapALCError) { #ifdef _WIN32 @@ -1666,22 +1701,31 @@ static void alcSetError(ALCdevice *device, ALCenum errorCode) } if(device) - ATOMIC_STORE(&device->LastError, errorCode); + ATOMIC_STORE_SEQ(&device->LastError, errorCode); else - ATOMIC_STORE(&LastNullDeviceError, errorCode); + ATOMIC_STORE_SEQ(&LastNullDeviceError, errorCode); } +struct Compressor *CreateDeviceLimiter(const ALCdevice *device) +{ + return CompressorInit(0.0f, 0.0f, AL_FALSE, AL_TRUE, 0.0f, 0.0f, 0.5f, 2.0f, + 0.0f, -3.0f, 3.0f, device->Frequency); +} + /* 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. + * to jump forward or back. Must not be called while the device is running/ + * mixing. */ static inline void UpdateClockBase(ALCdevice *device) { + IncrementRef(&device->MixCount); device->ClockBase += device->SamplesDone * DEVICE_CLOCK_RES / device->Frequency; device->SamplesDone = 0; + IncrementRef(&device->MixCount); } /* UpdateDeviceParams @@ -1691,30 +1735,32 @@ static inline void UpdateClockBase(ALCdevice *device) */ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) { - ALCcontext *context; - enum HrtfRequestMode hrtf_appreq = Hrtf_Default; enum HrtfRequestMode hrtf_userreq = Hrtf_Default; + enum HrtfRequestMode hrtf_appreq = Hrtf_Default; + ALCenum gainLimiter = device->Limiter ? ALC_TRUE : ALC_FALSE; + const ALsizei old_sends = device->NumAuxSends; + ALsizei new_sends = device->NumAuxSends; enum DevFmtChannels oldChans; enum DevFmtType oldType; - ALCuint oldFreq; - FPUCtl oldMode; + ALboolean update_failed; ALCsizei hrtf_id = -1; + ALCcontext *context; + ALCuint oldFreq; size_t size; + ALCsizei i; + int val; // Check for attributes if(device->Type == Loopback) { - enum { - GotFreq = 1<<0, - GotChans = 1<<1, - GotType = 1<<2, - GotAll = GotFreq|GotChans|GotType - }; - ALCuint freq, numMono, numStereo, numSends; - enum DevFmtChannels schans; - enum DevFmtType stype; - ALCuint attrIdx = 0; - ALCint gotFmt = 0; + ALCsizei numMono, numStereo, numSends; + ALCenum alayout = AL_NONE; + ALCenum ascale = AL_NONE; + ALCenum schans = AL_NONE; + ALCenum stype = AL_NONE; + ALCsizei attrIdx = 0; + ALCsizei aorder = 0; + ALCuint freq = 0; if(!attrList) { @@ -1724,88 +1770,113 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) numMono = device->NumMonoSources; numStereo = device->NumStereoSources; - numSends = device->NumAuxSends; - schans = device->FmtChans; - stype = device->FmtType; - freq = device->Frequency; + numSends = old_sends; #define TRACE_ATTR(a, v) TRACE("Loopback %s = %d\n", #a, v) while(attrList[attrIdx]) { - if(attrList[attrIdx] == ALC_FORMAT_CHANNELS_SOFT) + switch(attrList[attrIdx]) { - ALCint val = attrList[attrIdx + 1]; - TRACE_ATTR(ALC_FORMAT_CHANNELS_SOFT, val); - if(!IsValidALCChannels(val) || !ChannelsFromDevFmt(val)) - return ALC_INVALID_VALUE; - schans = val; - gotFmt |= GotChans; - } + case ALC_FORMAT_CHANNELS_SOFT: + schans = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_FORMAT_CHANNELS_SOFT, schans); + if(!IsValidALCChannels(schans)) + return ALC_INVALID_VALUE; + break; - if(attrList[attrIdx] == ALC_FORMAT_TYPE_SOFT) - { - ALCint val = attrList[attrIdx + 1]; - TRACE_ATTR(ALC_FORMAT_TYPE_SOFT, val); - if(!IsValidALCType(val) || !BytesFromDevFmt(val)) - return ALC_INVALID_VALUE; - stype = val; - gotFmt |= GotType; - } + case ALC_FORMAT_TYPE_SOFT: + stype = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_FORMAT_TYPE_SOFT, stype); + if(!IsValidALCType(stype)) + return ALC_INVALID_VALUE; + break; - if(attrList[attrIdx] == ALC_FREQUENCY) - { - freq = attrList[attrIdx + 1]; - TRACE_ATTR(ALC_FREQUENCY, freq); - if(freq < MIN_OUTPUT_RATE) - return ALC_INVALID_VALUE; - gotFmt |= GotFreq; - } + case ALC_FREQUENCY: + freq = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_FREQUENCY, freq); + if(freq < MIN_OUTPUT_RATE) + return ALC_INVALID_VALUE; + break; - if(attrList[attrIdx] == ALC_STEREO_SOURCES) - { - numStereo = attrList[attrIdx + 1]; - TRACE_ATTR(ALC_STEREO_SOURCES, numStereo); - if(numStereo > device->SourcesMax) - numStereo = device->SourcesMax; + case ALC_AMBISONIC_LAYOUT_SOFT: + alayout = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_AMBISONIC_LAYOUT_SOFT, alayout); + if(!IsValidAmbiLayout(alayout)) + return ALC_INVALID_VALUE; + break; - numMono = device->SourcesMax - numStereo; - } + case ALC_AMBISONIC_SCALING_SOFT: + ascale = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_AMBISONIC_SCALING_SOFT, ascale); + if(!IsValidAmbiScaling(ascale)) + return ALC_INVALID_VALUE; + break; - if(attrList[attrIdx] == ALC_MAX_AUXILIARY_SENDS) - { - numSends = attrList[attrIdx + 1]; - TRACE_ATTR(ALC_MAX_AUXILIARY_SENDS, numSends); - } + case ALC_AMBISONIC_ORDER_SOFT: + aorder = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_AMBISONIC_ORDER_SOFT, aorder); + if(aorder < 1 || aorder > MAX_AMBI_ORDER) + return ALC_INVALID_VALUE; + break; - if(attrList[attrIdx] == 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; - } + case ALC_MONO_SOURCES: + numMono = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_MONO_SOURCES, numMono); + numMono = maxi(numMono, 0); + break; - if(attrList[attrIdx] == ALC_HRTF_ID_SOFT) - { - hrtf_id = attrList[attrIdx + 1]; - TRACE_ATTR(ALC_HRTF_ID_SOFT, hrtf_id); + case ALC_STEREO_SOURCES: + numStereo = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_STEREO_SOURCES, numStereo); + numStereo = maxi(numStereo, 0); + break; + + case ALC_MAX_AUXILIARY_SENDS: + numSends = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_MAX_AUXILIARY_SENDS, numSends); + numSends = clampi(numSends, 0, MAX_SENDS); + break; + + case ALC_HRTF_SOFT: + TRACE_ATTR(ALC_HRTF_SOFT, attrList[attrIdx + 1]); + if(attrList[attrIdx + 1] == ALC_FALSE) + hrtf_appreq = Hrtf_Disable; + else if(attrList[attrIdx + 1] == ALC_TRUE) + hrtf_appreq = Hrtf_Enable; + else + hrtf_appreq = Hrtf_Default; + break; + + case ALC_HRTF_ID_SOFT: + hrtf_id = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_HRTF_ID_SOFT, hrtf_id); + break; + + case ALC_OUTPUT_LIMITER_SOFT: + gainLimiter = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_OUTPUT_LIMITER_SOFT, gainLimiter); + break; + + default: + TRACE("Loopback 0x%04X = %d (0x%x)\n", attrList[attrIdx], + attrList[attrIdx + 1], attrList[attrIdx + 1]); + break; } attrIdx += 2; } #undef TRACE_ATTR - if(gotFmt != GotAll) + if(!schans || !stype || !freq) { WARN("Missing format for loopback device\n"); return ALC_INVALID_VALUE; } - - ConfigValueUInt(NULL, NULL, "sends", &numSends); - numSends = minu(MAX_SENDS, numSends); + if(schans == ALC_BFORMAT3D_SOFT && (!alayout || !ascale || !aorder)) + { + WARN("Missing ambisonic info for loopback device\n"); + return ALC_INVALID_VALUE; + } if((device->Flags&DEVICE_RUNNING)) V0(device->Backend,stop)(); @@ -1816,14 +1887,40 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) device->Frequency = freq; device->FmtChans = schans; device->FmtType = stype; + if(schans == ALC_BFORMAT3D_SOFT) + { + device->AmbiOrder = aorder; + device->AmbiLayout = alayout; + device->AmbiScale = ascale; + } + + if(numMono > INT_MAX-numStereo) + numMono = INT_MAX-numStereo; + numMono += numStereo; + if(ConfigValueInt(NULL, NULL, "sources", &numMono)) + { + if(numMono <= 0) + numMono = 256; + } + else + numMono = maxi(numMono, 256); + numStereo = mini(numStereo, numMono); + numMono -= numStereo; + device->SourcesMax = numMono + numStereo; + device->NumMonoSources = numMono; device->NumStereoSources = numStereo; - device->NumAuxSends = numSends; + + if(ConfigValueInt(NULL, NULL, "sends", &new_sends)) + new_sends = mini(numSends, clampi(new_sends, 0, MAX_SENDS)); + else + new_sends = numSends; } else if(attrList && attrList[0]) { - ALCuint freq, numMono, numStereo, numSends; - ALCuint attrIdx = 0; + ALCsizei numMono, numStereo, numSends; + ALCsizei attrIdx = 0; + ALCuint freq; /* If a context is already running on the device, stop playback so the * device attributes can be updated. */ @@ -1831,66 +1928,75 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) V0(device->Backend,stop)(); device->Flags &= ~DEVICE_RUNNING; + UpdateClockBase(device); + freq = device->Frequency; numMono = device->NumMonoSources; numStereo = device->NumStereoSources; - numSends = device->NumAuxSends; + numSends = old_sends; #define TRACE_ATTR(a, v) TRACE("%s = %d\n", #a, v) while(attrList[attrIdx]) { - if(attrList[attrIdx] == ALC_FREQUENCY) + switch(attrList[attrIdx]) { - freq = attrList[attrIdx + 1]; - device->Flags |= DEVICE_FREQUENCY_REQUEST; - TRACE_ATTR(ALC_FREQUENCY, freq); - } + case ALC_FREQUENCY: + freq = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_FREQUENCY, freq); + device->Flags |= DEVICE_FREQUENCY_REQUEST; + break; - if(attrList[attrIdx] == ALC_STEREO_SOURCES) - { - numStereo = attrList[attrIdx + 1]; - TRACE_ATTR(ALC_STEREO_SOURCES, numStereo); - if(numStereo > device->SourcesMax) - numStereo = device->SourcesMax; + case ALC_MONO_SOURCES: + numMono = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_MONO_SOURCES, numMono); + numMono = maxi(numMono, 0); + break; - numMono = device->SourcesMax - numStereo; - } + case ALC_STEREO_SOURCES: + numStereo = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_STEREO_SOURCES, numStereo); + numStereo = maxi(numStereo, 0); + break; - if(attrList[attrIdx] == ALC_MAX_AUXILIARY_SENDS) - { - numSends = attrList[attrIdx + 1]; - TRACE_ATTR(ALC_MAX_AUXILIARY_SENDS, numSends); - } + case ALC_MAX_AUXILIARY_SENDS: + numSends = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_MAX_AUXILIARY_SENDS, numSends); + numSends = clampi(numSends, 0, MAX_SENDS); + break; - if(attrList[attrIdx] == 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; - } + 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; - if(attrList[attrIdx] == ALC_HRTF_ID_SOFT) - { - hrtf_id = attrList[attrIdx + 1]; - TRACE_ATTR(ALC_HRTF_ID_SOFT, hrtf_id); + case ALC_HRTF_ID_SOFT: + hrtf_id = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_HRTF_ID_SOFT, hrtf_id); + break; + + case ALC_OUTPUT_LIMITER_SOFT: + gainLimiter = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_OUTPUT_LIMITER_SOFT, gainLimiter); + break; + + default: + TRACE("0x%04X = %d (0x%x)\n", attrList[attrIdx], + attrList[attrIdx + 1], attrList[attrIdx + 1]); + break; } attrIdx += 2; } #undef TRACE_ATTR - ConfigValueUInt(al_string_get_cstr(device->DeviceName), NULL, "frequency", &freq); + ConfigValueUInt(alstr_get_cstr(device->DeviceName), NULL, "frequency", &freq); freq = maxu(freq, MIN_OUTPUT_RATE); - ConfigValueUInt(al_string_get_cstr(device->DeviceName), NULL, "sends", &numSends); - numSends = minu(MAX_SENDS, numSends); - - UpdateClockBase(device); - device->UpdateSize = (ALuint64)device->UpdateSize * freq / device->Frequency; /* SSE and Neon do best with the update size being a multiple of 4 */ @@ -1898,9 +2004,28 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) device->UpdateSize = (device->UpdateSize+3)&~3; device->Frequency = freq; + + if(numMono > INT_MAX-numStereo) + numMono = INT_MAX-numStereo; + numMono += numStereo; + if(ConfigValueInt(alstr_get_cstr(device->DeviceName), NULL, "sources", &numMono)) + { + if(numMono <= 0) + numMono = 256; + } + else + numMono = maxi(numMono, 256); + numStereo = mini(numStereo, numMono); + numMono -= numStereo; + device->SourcesMax = numMono + numStereo; + device->NumMonoSources = numMono; device->NumStereoSources = numStereo; - device->NumAuxSends = numSends; + + if(ConfigValueInt(alstr_get_cstr(device->DeviceName), NULL, "sends", &new_sends)) + new_sends = mini(numSends, clampi(new_sends, 0, MAX_SENDS)); + else + new_sends = numSends; } if((device->Flags&DEVICE_RUNNING)) @@ -1912,6 +2037,13 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) al_free(device->Bs2b); device->Bs2b = NULL; + al_free(device->ChannelDelay[0].Buffer); + for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) + { + device->ChannelDelay[i].Length = 0; + device->ChannelDelay[i].Buffer = NULL; + } + al_free(device->Dry.Buffer); device->Dry.Buffer = NULL; device->Dry.NumChannels = 0; @@ -1922,14 +2054,16 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) UpdateClockBase(device); + device->DitherSeed = DITHER_RNG_SEED; + /************************************************************************* * Update device format request if HRTF is requested */ - device->Hrtf.Status = ALC_HRTF_DISABLED_SOFT; + device->HrtfStatus = ALC_HRTF_DISABLED_SOFT; if(device->Type != Loopback) { const char *hrtf; - if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "hrtf", &hrtf)) + if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "hrtf", &hrtf)) { if(strcasecmp(hrtf, "true") == 0) hrtf_userreq = Hrtf_Enable; @@ -1941,58 +2075,37 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) if(hrtf_userreq == Hrtf_Enable || (hrtf_userreq != Hrtf_Disable && hrtf_appreq == Hrtf_Enable)) { - if(VECTOR_SIZE(device->Hrtf.List) == 0) + struct Hrtf *hrtf = NULL; + if(VECTOR_SIZE(device->HrtfList) == 0) { - VECTOR_DEINIT(device->Hrtf.List); - device->Hrtf.List = EnumerateHrtf(device->DeviceName); + VECTOR_DEINIT(device->HrtfList); + device->HrtfList = EnumerateHrtf(device->DeviceName); } - if(VECTOR_SIZE(device->Hrtf.List) > 0) + if(VECTOR_SIZE(device->HrtfList) > 0) + { + if(hrtf_id >= 0 && (size_t)hrtf_id < VECTOR_SIZE(device->HrtfList)) + hrtf = GetLoadedHrtf(VECTOR_ELEM(device->HrtfList, hrtf_id).hrtf); + else + hrtf = GetLoadedHrtf(VECTOR_ELEM(device->HrtfList, 0).hrtf); + } + + if(hrtf) { device->FmtChans = DevFmtStereo; - if(hrtf_id >= 0 && (size_t)hrtf_id < VECTOR_SIZE(device->Hrtf.List)) - device->Frequency = VECTOR_ELEM(device->Hrtf.List, hrtf_id).hrtf->sampleRate; - else - device->Frequency = VECTOR_ELEM(device->Hrtf.List, 0).hrtf->sampleRate; + device->Frequency = hrtf->sampleRate; device->Flags |= DEVICE_CHANNELS_REQUEST | DEVICE_FREQUENCY_REQUEST; + if(device->HrtfHandle) + Hrtf_DecRef(device->HrtfHandle); + device->HrtfHandle = hrtf; } else { hrtf_userreq = Hrtf_Default; hrtf_appreq = Hrtf_Disable; - device->Hrtf.Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT; + device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT; } } } - else if(hrtf_appreq == Hrtf_Enable) - { - size_t i = VECTOR_SIZE(device->Hrtf.List); - /* Loopback device. We don't need to match to a specific HRTF entry - * here. If the requested ID matches, we'll pick that later, if not, - * we'll try to auto-select one anyway. Just make sure one exists - * that'll work. - */ - if(device->FmtChans == DevFmtStereo) - { - if(VECTOR_SIZE(device->Hrtf.List) == 0) - { - VECTOR_DEINIT(device->Hrtf.List); - device->Hrtf.List = EnumerateHrtf(device->DeviceName); - } - for(i = 0;i < VECTOR_SIZE(device->Hrtf.List);i++) - { - const struct Hrtf *hrtf = VECTOR_ELEM(device->Hrtf.List, i).hrtf; - if(hrtf->sampleRate == device->Frequency) - break; - } - } - if(i == VECTOR_SIZE(device->Hrtf.List)) - { - ERR("Requested format not HRTF compatible: %s, %uhz\n", - DevFmtChannelsString(device->FmtChans), device->Frequency); - hrtf_appreq = Hrtf_Disable; - device->Hrtf.Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT; - } - } oldFreq = device->Frequency; oldChans = device->FmtChans; @@ -2040,15 +2153,14 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) ); aluInitRenderer(device, hrtf_id, hrtf_appreq, hrtf_userreq); + TRACE("Channel config, Dry: %d, FOA: %d, Real: %d\n", device->Dry.NumChannels, + device->FOAOut.NumChannels, device->RealOut.NumChannels); /* Allocate extra channels for any post-filter output. */ - size = device->Dry.NumChannels * sizeof(device->Dry.Buffer[0]); - if((device->AmbiDecoder && bformatdec_getOrder(device->AmbiDecoder) >= 2)) - size += (ChannelsFromDevFmt(device->FmtChans)+4) * sizeof(device->Dry.Buffer[0]); - else if(device->Hrtf.Handle || device->Uhj_Encoder || device->AmbiDecoder) - size += ChannelsFromDevFmt(device->FmtChans) * sizeof(device->Dry.Buffer[0]); - else if(device->FmtChans > DevFmtAmbi1 && device->FmtChans <= DevFmtAmbi3) - size += 4 * sizeof(device->Dry.Buffer[0]); + size = (device->Dry.NumChannels + device->FOAOut.NumChannels + + device->RealOut.NumChannels)*sizeof(device->Dry.Buffer[0]); + + TRACE("Allocating "SZFMT" channels, "SZFMT" bytes\n", size/sizeof(device->Dry.Buffer[0]), size); device->Dry.Buffer = al_calloc(16, size); if(!device->Dry.Buffer) { @@ -2056,100 +2168,214 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) return ALC_INVALID_DEVICE; } - if(device->Hrtf.Handle || device->Uhj_Encoder || device->AmbiDecoder) - { - device->RealOut.Buffer = device->Dry.Buffer + device->Dry.NumChannels; - device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans); - } + if(device->RealOut.NumChannels != 0) + device->RealOut.Buffer = device->Dry.Buffer + device->Dry.NumChannels + + device->FOAOut.NumChannels; else { device->RealOut.Buffer = device->Dry.Buffer; device->RealOut.NumChannels = device->Dry.NumChannels; } - if((device->AmbiDecoder && bformatdec_getOrder(device->AmbiDecoder) >= 2) || - (device->FmtChans > DevFmtAmbi1 && device->FmtChans <= DevFmtAmbi3)) - { - /* Higher-order rendering requires upsampling first-order content, so - * make sure to mix it separately. - */ - device->FOAOut.Buffer = device->RealOut.Buffer + device->RealOut.NumChannels; - device->FOAOut.NumChannels = 4; - } + if(device->FOAOut.NumChannels != 0) + device->FOAOut.Buffer = device->Dry.Buffer + device->Dry.NumChannels; else { device->FOAOut.Buffer = device->Dry.Buffer; device->FOAOut.NumChannels = device->Dry.NumChannels; } - SetMixerFPUMode(&oldMode); - if(device->DefaultSlot) + device->NumAuxSends = new_sends; + TRACE("Max sources: %d (%d + %d), effect slots: %d, sends: %d\n", + device->SourcesMax, device->NumMonoSources, device->NumStereoSources, + device->AuxiliaryEffectSlotMax, device->NumAuxSends); + + device->DitherDepth = 0.0f; + if(GetConfigValueBool(alstr_get_cstr(device->DeviceName), NULL, "dither", 1)) { - ALeffectslot *slot = device->DefaultSlot; - ALeffectState *state = slot->Effect.State; - - state->OutBuffer = device->Dry.Buffer; - state->OutChannels = device->Dry.NumChannels; - if(V(state,deviceUpdate)(device) == AL_FALSE) + ALint depth = 0; + ConfigValueInt(alstr_get_cstr(device->DeviceName), NULL, "dither-depth", &depth); + if(depth <= 0) { - RestoreFPUMode(&oldMode); - return ALC_INVALID_DEVICE; + switch(device->FmtType) + { + case DevFmtByte: + case DevFmtUByte: + depth = 8; + break; + case DevFmtShort: + case DevFmtUShort: + depth = 16; + break; + case DevFmtInt: + case DevFmtUInt: + case DevFmtFloat: + break; + } } - UpdateEffectSlotProps(slot); + else if(depth > 24) + depth = 24; + device->DitherDepth = (depth > 0) ? powf(2.0f, (ALfloat)(depth-1)) : 0.0f; } + if(!(device->DitherDepth > 0.0f)) + TRACE("Dithering disabled\n"); + else + TRACE("Dithering enabled (%g-bit, %g)\n", log2f(device->DitherDepth)+1.0f, + device->DitherDepth); - context = ATOMIC_LOAD(&device->ContextList); + if(ConfigValueBool(alstr_get_cstr(device->DeviceName), NULL, "output-limiter", &val)) + gainLimiter = val ? ALC_TRUE : ALC_FALSE; + /* Valid values for gainLimiter are ALC_DONT_CARE_SOFT, ALC_TRUE, and + * ALC_FALSE. We default to on, so ALC_DONT_CARE_SOFT is the same as + * ALC_TRUE. + */ + if(gainLimiter != ALC_FALSE) + { + if(!device->Limiter || device->Frequency != GetCompressorSampleRate(device->Limiter)) + { + al_free(device->Limiter); + device->Limiter = CreateDeviceLimiter(device); + } + } + else + { + al_free(device->Limiter); + device->Limiter = NULL; + } + TRACE("Output limiter %s\n", device->Limiter ? "enabled" : "disabled"); + + aluSelectPostProcess(device); + + /* Need to delay returning failure until replacement Send arrays have been + * allocated with the appropriate size. + */ + update_failed = AL_FALSE; + START_MIXER_MODE(); + context = ATOMIC_LOAD_SEQ(&device->ContextList); while(context) { + SourceSubList *sublist, *subend; + struct ALvoiceProps *vprops; ALsizei pos; - ReadLock(&context->PropLock); - LockUIntMapRead(&context->EffectSlotMap); - for(pos = 0;pos < context->EffectSlotMap.size;pos++) + if(context->DefaultSlot) { - ALeffectslot *slot = context->EffectSlotMap.values[pos]; + ALeffectslot *slot = context->DefaultSlot; ALeffectState *state = slot->Effect.State; state->OutBuffer = device->Dry.Buffer; state->OutChannels = device->Dry.NumChannels; if(V(state,deviceUpdate)(device) == AL_FALSE) - { - UnlockUIntMapRead(&context->EffectSlotMap); - ReadUnlock(&context->PropLock); - RestoreFPUMode(&oldMode); - return ALC_INVALID_DEVICE; - } - - UpdateEffectSlotProps(slot); + update_failed = AL_TRUE; + else + UpdateEffectSlotProps(slot, context); } - UnlockUIntMapRead(&context->EffectSlotMap); - LockUIntMapRead(&context->SourceMap); - for(pos = 0;pos < context->SourceMap.size;pos++) + almtx_lock(&context->PropLock); + almtx_lock(&context->EffectSlotLock); + for(pos = 0;pos < (ALsizei)VECTOR_SIZE(context->EffectSlotList);pos++) { - ALsource *source = context->SourceMap.values[pos]; - ALuint s = device->NumAuxSends; - while(s < MAX_SENDS) + ALeffectslot *slot = VECTOR_ELEM(context->EffectSlotList, pos); + ALeffectState *state = slot->Effect.State; + + state->OutBuffer = device->Dry.Buffer; + state->OutChannels = device->Dry.NumChannels; + if(V(state,deviceUpdate)(device) == AL_FALSE) + update_failed = AL_TRUE; + else + UpdateEffectSlotProps(slot, context); + } + almtx_unlock(&context->EffectSlotLock); + + almtx_lock(&context->SourceLock); + sublist = VECTOR_BEGIN(context->SourceList); + subend = VECTOR_END(context->SourceList); + for(;sublist != subend;++sublist) + { + ALuint64 usemask = ~sublist->FreeMask; + while(usemask) { - if(source->Send[s].Slot) - DecrementRef(&source->Send[s].Slot->ref); - source->Send[s].Slot = NULL; - source->Send[s].Gain = 1.0f; - source->Send[s].GainHF = 1.0f; - source->Send[s].HFReference = LOWPASSFREQREF; - source->Send[s].GainLF = 1.0f; - source->Send[s].LFReference = HIGHPASSFREQREF; - s++; + ALsizei idx = CTZ64(usemask); + ALsource *source = sublist->Sources + idx; + + usemask &= ~(U64(1) << idx); + + if(old_sends != device->NumAuxSends) + { + ALvoid *sends = al_calloc(16, device->NumAuxSends*sizeof(source->Send[0])); + ALsizei s; + + memcpy(sends, source->Send, + mini(device->NumAuxSends, old_sends)*sizeof(source->Send[0]) + ); + for(s = device->NumAuxSends;s < old_sends;s++) + { + if(source->Send[s].Slot) + DecrementRef(&source->Send[s].Slot->ref); + source->Send[s].Slot = NULL; + } + al_free(source->Send); + source->Send = sends; + for(s = old_sends;s < device->NumAuxSends;s++) + { + source->Send[s].Slot = NULL; + source->Send[s].Gain = 1.0f; + source->Send[s].GainHF = 1.0f; + source->Send[s].HFReference = LOWPASSFREQREF; + source->Send[s].GainLF = 1.0f; + source->Send[s].LFReference = HIGHPASSFREQREF; + } + } + + ATOMIC_FLAG_CLEAR(&source->PropsClean, almemory_order_release); } } - UnlockUIntMapRead(&context->SourceMap); + /* Clear any pre-existing voice property structs, in case the number of + * auxiliary sends is changing. Active sources will have updates + * respecified in UpdateAllSourceProps. + */ + vprops = ATOMIC_EXCHANGE_PTR(&context->FreeVoiceProps, NULL, almemory_order_acq_rel); + while(vprops) + { + struct ALvoiceProps *next = ATOMIC_LOAD(&vprops->next, almemory_order_relaxed); + al_free(vprops); + vprops = next; + } + + AllocateVoices(context, context->MaxVoices, old_sends); + for(pos = 0;pos < context->VoiceCount;pos++) + { + ALvoice *voice = context->Voices[pos]; + + al_free(ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel)); + + if(ATOMIC_LOAD(&voice->Source, almemory_order_acquire) == NULL) + continue; + + if(device->AvgSpeakerDist > 0.0f) + { + /* Reinitialize the NFC filters for new parameters. */ + ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC / + (device->AvgSpeakerDist * device->Frequency); + for(i = 0;i < voice->NumChannels;i++) + NfcFilterCreate(&voice->Direct.Params[i].NFCtrlFilter, 0.0f, w1); + } + } + almtx_unlock(&context->SourceLock); + + ATOMIC_FLAG_TEST_AND_SET(&context->PropsClean, almemory_order_release); + UpdateContextProps(context); + ATOMIC_FLAG_TEST_AND_SET(&context->Listener->PropsClean, almemory_order_release); + UpdateListenerProps(context); UpdateAllSourceProps(context); - ReadUnlock(&context->PropLock); + almtx_unlock(&context->PropLock); - context = context->next; + context = ATOMIC_LOAD(&context->next, almemory_order_relaxed); } - RestoreFPUMode(&oldMode); + END_MIXER_MODE(); + if(update_failed) + return ALC_INVALID_DEVICE; if(!(device->Flags&DEVICE_PAUSED)) { @@ -2161,6 +2387,71 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) return ALC_NO_ERROR; } + +static void InitDevice(ALCdevice *device, enum DeviceType type) +{ + ALsizei i; + + InitRef(&device->ref, 1); + ATOMIC_INIT(&device->Connected, ALC_TRUE); + device->Type = type; + ATOMIC_INIT(&device->LastError, ALC_NO_ERROR); + + device->Flags = 0; + device->Render_Mode = NormalRender; + device->AvgSpeakerDist = 0.0f; + + ATOMIC_INIT(&device->ContextList, NULL); + + device->ClockBase = 0; + device->SamplesDone = 0; + + device->SourcesMax = 0; + device->AuxiliaryEffectSlotMax = 0; + device->NumAuxSends = 0; + + device->Dry.Buffer = NULL; + device->Dry.NumChannels = 0; + device->FOAOut.Buffer = NULL; + device->FOAOut.NumChannels = 0; + device->RealOut.Buffer = NULL; + device->RealOut.NumChannels = 0; + + AL_STRING_INIT(device->DeviceName); + + for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) + { + device->ChannelDelay[i].Gain = 1.0f; + device->ChannelDelay[i].Length = 0; + device->ChannelDelay[i].Buffer = NULL; + } + + AL_STRING_INIT(device->HrtfName); + VECTOR_INIT(device->HrtfList); + device->HrtfHandle = NULL; + device->Hrtf = NULL; + device->Bs2b = NULL; + device->Uhj_Encoder = NULL; + device->AmbiDecoder = NULL; + device->AmbiUp = NULL; + device->Stablizer = NULL; + device->Limiter = NULL; + + VECTOR_INIT(device->BufferList); + almtx_init(&device->BufferLock, almtx_plain); + + VECTOR_INIT(device->EffectList); + almtx_init(&device->EffectLock, almtx_plain); + + VECTOR_INIT(device->FilterList); + almtx_init(&device->FilterLock, almtx_plain); + + almtx_init(&device->BackendLock, almtx_plain); + device->Backend = NULL; + + ATOMIC_INIT(&device->next, NULL); +} + /* FreeDevice * * Frees the device structure, and destroys any objects the app failed to @@ -2168,46 +2459,44 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) */ static ALCvoid FreeDevice(ALCdevice *device) { + ALsizei i; + TRACE("%p\n", device); - V0(device->Backend,close)(); - DELETE_OBJ(device->Backend); + if(device->Backend) + DELETE_OBJ(device->Backend); device->Backend = NULL; almtx_destroy(&device->BackendLock); - if(device->DefaultSlot) - { - DeinitEffectSlot(device->DefaultSlot); - device->DefaultSlot = NULL; - } + ReleaseALBuffers(device); +#define FREE_BUFFERSUBLIST(x) al_free((x)->Buffers) + VECTOR_FOR_EACH(BufferSubList, device->BufferList, FREE_BUFFERSUBLIST); +#undef FREE_BUFFERSUBLIST + VECTOR_DEINIT(device->BufferList); + almtx_destroy(&device->BufferLock); - if(device->BufferMap.size > 0) - { - WARN("(%p) Deleting %d Buffer%s\n", device, device->BufferMap.size, - (device->BufferMap.size==1)?"":"s"); - ReleaseALBuffers(device); - } - ResetUIntMap(&device->BufferMap); + ReleaseALEffects(device); +#define FREE_EFFECTSUBLIST(x) al_free((x)->Effects) + VECTOR_FOR_EACH(EffectSubList, device->EffectList, FREE_EFFECTSUBLIST); +#undef FREE_EFFECTSUBLIST + VECTOR_DEINIT(device->EffectList); + almtx_destroy(&device->EffectLock); - if(device->EffectMap.size > 0) - { - WARN("(%p) Deleting %d Effect%s\n", device, device->EffectMap.size, - (device->EffectMap.size==1)?"":"s"); - ReleaseALEffects(device); - } - ResetUIntMap(&device->EffectMap); + ReleaseALFilters(device); +#define FREE_FILTERSUBLIST(x) al_free((x)->Filters) + VECTOR_FOR_EACH(FilterSubList, device->FilterList, FREE_FILTERSUBLIST); +#undef FREE_FILTERSUBLIST + VECTOR_DEINIT(device->FilterList); + almtx_destroy(&device->FilterLock); - if(device->FilterMap.size > 0) - { - WARN("(%p) Deleting %d Filter%s\n", device, device->FilterMap.size, - (device->FilterMap.size==1)?"":"s"); - ReleaseALFilters(device); - } - ResetUIntMap(&device->FilterMap); - - AL_STRING_DEINIT(device->Hrtf.Name); - FreeHrtfList(&device->Hrtf.List); + AL_STRING_DEINIT(device->HrtfName); + FreeHrtfList(&device->HrtfList); + if(device->HrtfHandle) + Hrtf_DecRef(device->HrtfHandle); + device->HrtfHandle = NULL; + al_free(device->Hrtf); + device->Hrtf = NULL; al_free(device->Bs2b); device->Bs2b = NULL; @@ -2215,11 +2504,22 @@ static ALCvoid FreeDevice(ALCdevice *device) al_free(device->Uhj_Encoder); device->Uhj_Encoder = NULL; - bformatdec_free(device->AmbiDecoder); - device->AmbiDecoder = NULL; + bformatdec_free(&device->AmbiDecoder); + ambiup_free(&device->AmbiUp); - ambiup_free(device->AmbiUp); - device->AmbiUp = NULL; + al_free(device->Stablizer); + device->Stablizer = NULL; + + al_free(device->Limiter); + device->Limiter = NULL; + + al_free(device->ChannelDelay[0].Buffer); + for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) + { + device->ChannelDelay[i].Gain = 1.0f; + device->ChannelDelay[i].Length = 0; + device->ChannelDelay[i].Buffer = NULL; + } AL_STRING_DEINIT(device->DeviceName); @@ -2259,7 +2559,7 @@ static ALCboolean VerifyDevice(ALCdevice **device) ALCdevice *tmpDevice; LockLists(); - tmpDevice = ATOMIC_LOAD(&DeviceList); + tmpDevice = ATOMIC_LOAD_SEQ(&DeviceList); while(tmpDevice) { if(tmpDevice == *device) @@ -2268,7 +2568,7 @@ static ALCboolean VerifyDevice(ALCdevice **device) UnlockLists(); return ALC_TRUE; } - tmpDevice = tmpDevice->next; + tmpDevice = ATOMIC_LOAD(&tmpDevice->next, almemory_order_relaxed); } UnlockLists(); @@ -2284,10 +2584,10 @@ static ALCboolean VerifyDevice(ALCdevice **device) static ALvoid InitContext(ALCcontext *Context) { ALlistener *listener = Context->Listener; + struct ALeffectslotArray *auxslots; //Initialise listener listener->Gain = 1.0f; - listener->MetersPerUnit = 1.0f; listener->Position[0] = 0.0f; listener->Position[1] = 0.0f; listener->Position[2] = 0.0f; @@ -2300,30 +2600,34 @@ static ALvoid InitContext(ALCcontext *Context) listener->Up[0] = 0.0f; listener->Up[1] = 1.0f; listener->Up[2] = 0.0f; - - aluMatrixfSet(&listener->Params.Matrix, - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - ); - aluVectorSet(&listener->Params.Velocity, 0.0f, 0.0f, 0.0f, 0.0f); - listener->Params.Gain = 1.0f; - listener->Params.MetersPerUnit = 1.0f; - listener->Params.DopplerFactor = 1.0f; - listener->Params.SpeedOfSound = SPEEDOFSOUNDMETRESPERSEC; + ATOMIC_FLAG_TEST_AND_SET(&listener->PropsClean, almemory_order_relaxed); ATOMIC_INIT(&listener->Update, NULL); - ATOMIC_INIT(&listener->FreeList, NULL); //Validate Context InitRef(&Context->UpdateCount, 0); ATOMIC_INIT(&Context->HoldUpdates, AL_FALSE); Context->GainBoost = 1.0f; - RWLockInit(&Context->PropLock); + almtx_init(&Context->PropLock, almtx_plain); ATOMIC_INIT(&Context->LastError, AL_NO_ERROR); - InitUIntMap(&Context->SourceMap, Context->Device->SourcesMax); - InitUIntMap(&Context->EffectSlotMap, Context->Device->AuxiliaryEffectSlotMax); + VECTOR_INIT(Context->SourceList); + Context->NumSources = 0; + almtx_init(&Context->SourceLock, almtx_plain); + VECTOR_INIT(Context->EffectSlotList); + almtx_init(&Context->EffectSlotLock, almtx_plain); + + if(Context->DefaultSlot) + { + auxslots = al_calloc(DEF_ALIGN, FAM_SIZE(struct ALeffectslotArray, slot, 1)); + auxslots->count = 1; + auxslots->slot[0] = Context->DefaultSlot; + } + else + { + auxslots = al_calloc(DEF_ALIGN, sizeof(struct ALeffectslotArray)); + auxslots->count = 0; + } + ATOMIC_INIT(&Context->ActiveAuxSlots, auxslots); //Set globals Context->DistanceModel = DefaultDistanceModel; @@ -2331,9 +2635,36 @@ static ALvoid InitContext(ALCcontext *Context) Context->DopplerFactor = 1.0f; Context->DopplerVelocity = 1.0f; Context->SpeedOfSound = SPEEDOFSOUNDMETRESPERSEC; + Context->MetersPerUnit = AL_DEFAULT_METERS_PER_UNIT; + ATOMIC_FLAG_TEST_AND_SET(&Context->PropsClean, almemory_order_relaxed); ATOMIC_INIT(&Context->DeferUpdates, AL_FALSE); + almtx_init(&Context->EventThrdLock, almtx_plain); + alsem_init(&Context->EventSem, 0); + Context->AsyncEvents = NULL; + ATOMIC_INIT(&Context->EnabledEvts, 0); + almtx_init(&Context->EventCbLock, almtx_plain); + Context->EventCb = NULL; + Context->EventParam = NULL; + + ATOMIC_INIT(&Context->Update, NULL); + ATOMIC_INIT(&Context->FreeContextProps, NULL); + ATOMIC_INIT(&Context->FreeListenerProps, NULL); + ATOMIC_INIT(&Context->FreeVoiceProps, NULL); + ATOMIC_INIT(&Context->FreeEffectslotProps, NULL); Context->ExtensionList = alExtList; + + + listener->Params.Matrix = IdentityMatrixf; + aluVectorSet(&listener->Params.Velocity, 0.0f, 0.0f, 0.0f, 0.0f); + listener->Params.Gain = listener->Gain; + listener->Params.MetersPerUnit = Context->MetersPerUnit; + listener->Params.DopplerFactor = Context->DopplerFactor; + listener->Params.SpeedOfSound = Context->SpeedOfSound * Context->DopplerVelocity; + listener->Params.ReverbSpeedOfSound = listener->Params.SpeedOfSound * + listener->Params.MetersPerUnit; + listener->Params.SourceDistanceModel = Context->SourceDistanceModel; + listener->Params.DistanceModel = Context->DistanceModel; } @@ -2345,27 +2676,82 @@ static ALvoid InitContext(ALCcontext *Context) static void FreeContext(ALCcontext *context) { ALlistener *listener = context->Listener; + struct ALeffectslotArray *auxslots; + struct ALeffectslotProps *eprops; struct ALlistenerProps *lprops; + struct ALcontextProps *cprops; + struct ALvoiceProps *vprops; size_t count; + ALsizei i; TRACE("%p\n", context); - if(context->SourceMap.size > 0) + if((cprops=ATOMIC_LOAD(&context->Update, almemory_order_acquire)) != NULL) { - WARN("(%p) Deleting %d Source%s\n", context, context->SourceMap.size, - (context->SourceMap.size==1)?"":"s"); - ReleaseALSources(context); + TRACE("Freed unapplied context update %p\n", cprops); + al_free(cprops); } - ResetUIntMap(&context->SourceMap); - if(context->EffectSlotMap.size > 0) + count = 0; + cprops = ATOMIC_LOAD(&context->FreeContextProps, almemory_order_acquire); + while(cprops) { - WARN("(%p) Deleting %d AuxiliaryEffectSlot%s\n", context, context->EffectSlotMap.size, - (context->EffectSlotMap.size==1)?"":"s"); - ReleaseALAuxiliaryEffectSlots(context); + struct ALcontextProps *next = ATOMIC_LOAD(&cprops->next, almemory_order_acquire); + al_free(cprops); + cprops = next; + ++count; } - ResetUIntMap(&context->EffectSlotMap); + TRACE("Freed "SZFMT" context property object%s\n", count, (count==1)?"":"s"); + if(context->DefaultSlot) + { + DeinitEffectSlot(context->DefaultSlot); + context->DefaultSlot = NULL; + } + + auxslots = ATOMIC_EXCHANGE_PTR(&context->ActiveAuxSlots, NULL, almemory_order_relaxed); + al_free(auxslots); + + ReleaseALSources(context); +#define FREE_SOURCESUBLIST(x) al_free((x)->Sources) + VECTOR_FOR_EACH(SourceSubList, context->SourceList, FREE_SOURCESUBLIST); +#undef FREE_SOURCESUBLIST + VECTOR_DEINIT(context->SourceList); + context->NumSources = 0; + almtx_destroy(&context->SourceLock); + + count = 0; + eprops = ATOMIC_LOAD(&context->FreeEffectslotProps, almemory_order_relaxed); + while(eprops) + { + struct ALeffectslotProps *next = ATOMIC_LOAD(&eprops->next, almemory_order_relaxed); + if(eprops->State) ALeffectState_DecRef(eprops->State); + al_free(eprops); + eprops = next; + ++count; + } + TRACE("Freed "SZFMT" AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s"); + + ReleaseALAuxiliaryEffectSlots(context); +#define FREE_EFFECTSLOTPTR(x) al_free(*(x)) + VECTOR_FOR_EACH(ALeffectslotPtr, context->EffectSlotList, FREE_EFFECTSLOTPTR); +#undef FREE_EFFECTSLOTPTR + VECTOR_DEINIT(context->EffectSlotList); + almtx_destroy(&context->EffectSlotLock); + + count = 0; + vprops = ATOMIC_LOAD(&context->FreeVoiceProps, almemory_order_relaxed); + while(vprops) + { + struct ALvoiceProps *next = ATOMIC_LOAD(&vprops->next, almemory_order_relaxed); + al_free(vprops); + vprops = next; + ++count; + } + TRACE("Freed "SZFMT" voice property object%s\n", count, (count==1)?"":"s"); + + for(i = 0;i < context->VoiceCount;i++) + DeinitVoice(context->Voices[i]); al_free(context->Voices); context->Voices = NULL; context->VoiceCount = 0; @@ -2377,16 +2763,34 @@ static void FreeContext(ALCcontext *context) al_free(lprops); } count = 0; - lprops = ATOMIC_LOAD(&listener->FreeList, almemory_order_consume); + lprops = ATOMIC_LOAD(&context->FreeListenerProps, almemory_order_acquire); while(lprops) { - struct ALlistenerProps *next = ATOMIC_LOAD(&lprops->next, almemory_order_consume); + struct ALlistenerProps *next = ATOMIC_LOAD(&lprops->next, almemory_order_acquire); al_free(lprops); lprops = next; ++count; } TRACE("Freed "SZFMT" listener property object%s\n", count, (count==1)?"":"s"); + if(ATOMIC_EXCHANGE(&context->EnabledEvts, 0, almemory_order_acq_rel)) + { + static const AsyncEvent kill_evt = { 0 }; + while(ll_ringbuffer_write(context->AsyncEvents, (const char*)&kill_evt, 1) == 0) + althrd_yield(); + alsem_post(&context->EventSem); + althrd_join(context->EventThread, NULL); + } + + almtx_destroy(&context->EventCbLock); + almtx_destroy(&context->EventThrdLock); + alsem_destroy(&context->EventSem); + + ll_ringbuffer_free(context->AsyncEvents); + context->AsyncEvents = NULL; + + almtx_destroy(&context->PropLock); + ALCdevice_DecRef(context->Device); context->Device = NULL; @@ -2398,12 +2802,13 @@ static void FreeContext(ALCcontext *context) /* ReleaseContext * * Removes the context reference from the given device and removes it from - * being current on the running thread or globally. + * being current on the running thread or globally. Returns true if other + * contexts still exist on the device. */ -static void ReleaseContext(ALCcontext *context, ALCdevice *device) +static bool ReleaseContext(ALCcontext *context, ALCdevice *device) { - ALCcontext *nextctx; - ALCcontext *origctx; + ALCcontext *origctx, *newhead; + bool ret = true; if(altss_get(LocalContext) == context) { @@ -2413,26 +2818,32 @@ static void ReleaseContext(ALCcontext *context, ALCdevice *device) } origctx = context; - if(ATOMIC_COMPARE_EXCHANGE_STRONG(ALCcontext*, &GlobalContext, &origctx, NULL)) + if(ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&GlobalContext, &origctx, NULL)) ALCcontext_DecRef(context); - ALCdevice_Lock(device); + V0(device->Backend,lock)(); origctx = context; - nextctx = context->next; - if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALCcontext*, &device->ContextList, &origctx, nextctx)) + newhead = ATOMIC_LOAD(&context->next, almemory_order_relaxed); + if(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&device->ContextList, &origctx, newhead)) { ALCcontext *list; do { + /* origctx is what the desired context failed to match. Try + * swapping out the next one in the list. + */ list = origctx; origctx = context; - } while(!COMPARE_EXCHANGE(&list->next, &origctx, nextctx)); + } while(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&list->next, &origctx, newhead)); } - ALCdevice_Unlock(device); + else + ret = !!newhead; + V0(device->Backend,unlock)(); ALCcontext_DecRef(context); + return ret; } -void ALCcontext_IncRef(ALCcontext *context) +static void ALCcontext_IncRef(ALCcontext *context) { uint ref = IncrementRef(&context->ref); TRACEREF("%p increasing refcount to %u\n", context, ref); @@ -2462,10 +2873,10 @@ static ALCboolean VerifyContext(ALCcontext **context) ALCdevice *dev; LockLists(); - dev = ATOMIC_LOAD(&DeviceList); + dev = ATOMIC_LOAD_SEQ(&DeviceList); while(dev) { - ALCcontext *ctx = ATOMIC_LOAD(&dev->ContextList); + ALCcontext *ctx = ATOMIC_LOAD(&dev->ContextList, almemory_order_acquire); while(ctx) { if(ctx == *context) @@ -2474,9 +2885,9 @@ static ALCboolean VerifyContext(ALCcontext **context) UnlockLists(); return ALC_TRUE; } - ctx = ctx->next; + ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed); } - dev = dev->next; + dev = ATOMIC_LOAD(&dev->next, almemory_order_relaxed); } UnlockLists(); @@ -2500,7 +2911,7 @@ ALCcontext *GetContextRef(void) else { LockLists(); - context = ATOMIC_LOAD(&GlobalContext); + context = ATOMIC_LOAD_SEQ(&GlobalContext); if(context) ALCcontext_IncRef(context); UnlockLists(); @@ -2510,6 +2921,91 @@ ALCcontext *GetContextRef(void) } +void AllocateVoices(ALCcontext *context, ALsizei num_voices, ALsizei old_sends) +{ + ALCdevice *device = context->Device; + ALsizei num_sends = device->NumAuxSends; + struct ALvoiceProps *props; + size_t sizeof_props; + size_t sizeof_voice; + ALvoice **voices; + ALvoice *voice; + ALsizei v = 0; + size_t size; + + if(num_voices == context->MaxVoices && num_sends == old_sends) + return; + + /* Allocate the voice pointers, voices, and the voices' stored source + * property set (including the dynamically-sized Send[] array) in one + * chunk. + */ + sizeof_voice = RoundUp(FAM_SIZE(ALvoice, Send, num_sends), 16); + sizeof_props = RoundUp(FAM_SIZE(struct ALvoiceProps, Send, num_sends), 16); + size = sizeof(ALvoice*) + sizeof_voice + sizeof_props; + + voices = al_calloc(16, RoundUp(size*num_voices, 16)); + /* The voice and property objects are stored interleaved since they're + * paired together. + */ + voice = (ALvoice*)((char*)voices + RoundUp(num_voices*sizeof(ALvoice*), 16)); + props = (struct ALvoiceProps*)((char*)voice + sizeof_voice); + + if(context->Voices) + { + const ALsizei v_count = mini(context->VoiceCount, num_voices); + const ALsizei s_count = mini(old_sends, num_sends); + + for(;v < v_count;v++) + { + ALvoice *old_voice = context->Voices[v]; + ALsizei i; + + /* Copy the old voice data and source property set to the new + * storage. + */ + *voice = *old_voice; + for(i = 0;i < s_count;i++) + voice->Send[i] = old_voice->Send[i]; + *props = *(old_voice->Props); + for(i = 0;i < s_count;i++) + props->Send[i] = old_voice->Props->Send[i]; + + /* Set this voice's property set pointer and voice reference. */ + voice->Props = props; + voices[v] = voice; + + /* Increment pointers to the next storage space. */ + voice = (ALvoice*)((char*)props + sizeof_props); + props = (struct ALvoiceProps*)((char*)voice + sizeof_voice); + } + /* Deinit any left over voices that weren't copied over to the new + * array. NOTE: If this does anything, v equals num_voices and + * num_voices is less than VoiceCount, so the following loop won't do + * anything. + */ + for(;v < context->VoiceCount;v++) + DeinitVoice(context->Voices[v]); + } + /* Finish setting the voices' property set pointers and references. */ + for(;v < num_voices;v++) + { + ATOMIC_INIT(&voice->Update, NULL); + + voice->Props = props; + voices[v] = voice; + + voice = (ALvoice*)((char*)props + sizeof_props); + props = (struct ALvoiceProps*)((char*)voice + sizeof_voice); + } + + al_free(context->Voices); + context->Voices = voices; + context->MaxVoices = num_voices; + context->VoiceCount = mini(context->VoiceCount, num_voices); +} + + /************************************************ * Standard ALC functions ************************************************/ @@ -2524,11 +3020,11 @@ ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device) if(VerifyDevice(&device)) { - errorCode = ATOMIC_EXCHANGE(ALCenum, &device->LastError, ALC_NO_ERROR); + errorCode = ATOMIC_EXCHANGE_SEQ(&device->LastError, ALC_NO_ERROR); ALCdevice_DecRef(device); } else - errorCode = ATOMIC_EXCHANGE(ALCenum, &LastNullDeviceError, ALC_NO_ERROR); + errorCode = ATOMIC_EXCHANGE_SEQ(&LastNullDeviceError, ALC_NO_ERROR); return errorCode; } @@ -2547,7 +3043,7 @@ ALC_API ALCvoid ALC_APIENTRY alcSuspendContext(ALCcontext *context) alcSetError(NULL, ALC_INVALID_CONTEXT); else { - ALCcontext_DeferUpdates(context, DeferAllowPlay); + ALCcontext_DeferUpdates(context); ALCcontext_DecRef(context); } } @@ -2612,26 +3108,26 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum para case ALC_ALL_DEVICES_SPECIFIER: if(VerifyDevice(&Device)) { - value = al_string_get_cstr(Device->DeviceName); + value = alstr_get_cstr(Device->DeviceName); ALCdevice_DecRef(Device); } else { ProbeAllDevicesList(); - value = al_string_get_cstr(alcAllDevicesList); + value = alstr_get_cstr(alcAllDevicesList); } break; case ALC_CAPTURE_DEVICE_SPECIFIER: if(VerifyDevice(&Device)) { - value = al_string_get_cstr(Device->DeviceName); + value = alstr_get_cstr(Device->DeviceName); ALCdevice_DecRef(Device); } else { ProbeCaptureDeviceList(); - value = al_string_get_cstr(alcCaptureDeviceList); + value = alstr_get_cstr(alcCaptureDeviceList); } break; @@ -2641,13 +3137,13 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum para break; case ALC_DEFAULT_ALL_DEVICES_SPECIFIER: - if(al_string_empty(alcAllDevicesList)) + if(alstr_empty(alcAllDevicesList)) ProbeAllDevicesList(); VerifyDevice(&Device); free(alcDefaultAllDevicesSpecifier); - alcDefaultAllDevicesSpecifier = strdup(al_string_get_cstr(alcAllDevicesList)); + alcDefaultAllDevicesSpecifier = strdup(alstr_get_cstr(alcAllDevicesList)); if(!alcDefaultAllDevicesSpecifier) alcSetError(Device, ALC_OUT_OF_MEMORY); @@ -2656,13 +3152,13 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum para break; case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER: - if(al_string_empty(alcCaptureDeviceList)) + if(alstr_empty(alcCaptureDeviceList)) ProbeCaptureDeviceList(); VerifyDevice(&Device); free(alcCaptureDefaultDeviceSpecifier); - alcCaptureDefaultDeviceSpecifier = strdup(al_string_get_cstr(alcCaptureDeviceList)); + alcCaptureDefaultDeviceSpecifier = strdup(alstr_get_cstr(alcCaptureDeviceList)); if(!alcCaptureDefaultDeviceSpecifier) alcSetError(Device, ALC_OUT_OF_MEMORY); @@ -2686,7 +3182,7 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum para else { almtx_lock(&Device->BackendLock); - value = (Device->Hrtf.Handle ? al_string_get_cstr(Device->Hrtf.Name) : ""); + value = (Device->HrtfHandle ? alstr_get_cstr(Device->HrtfName) : ""); almtx_unlock(&Device->BackendLock); ALCdevice_DecRef(Device); } @@ -2703,6 +3199,15 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum para } +static inline ALCsizei NumAttrsForDevice(ALCdevice *device) +{ + if(device->Type == Capture) return 9; + if(device->Type != Loopback) return 29; + if(device->FmtChans == DevFmtAmbi3D) + return 35; + return 29; +} + static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) { ALCsizei i; @@ -2734,6 +3239,10 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC 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(NULL, ALC_INVALID_DEVICE); return 0; @@ -2748,6 +3257,39 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC { switch(param) { + case ALC_ATTRIBUTES_SIZE: + values[0] = NumAttrsForDevice(device); + return 1; + + case ALC_ALL_ATTRIBUTES: + if(size < NumAttrsForDevice(device)) + { + alcSetError(device, ALC_INVALID_VALUE); + return 0; + } + + i = 0; + almtx_lock(&device->BackendLock); + values[i++] = ALC_MAJOR_VERSION; + values[i++] = alcMajorVersion; + values[i++] = ALC_MINOR_VERSION; + values[i++] = alcMinorVersion; + values[i++] = ALC_CAPTURE_SAMPLES; + values[i++] = V0(device->Backend,availableSamples)(); + values[i++] = ALC_CONNECTED; + values[i++] = ATOMIC_LOAD(&device->Connected, almemory_order_relaxed); + almtx_unlock(&device->BackendLock); + + values[i++] = 0; + return i; + + case ALC_MAJOR_VERSION: + values[0] = alcMajorVersion; + return 1; + case ALC_MINOR_VERSION: + values[0] = alcMinorVersion; + return 1; + case ALC_CAPTURE_SAMPLES: almtx_lock(&device->BackendLock); values[0] = V0(device->Backend,availableSamples)(); @@ -2755,7 +3297,7 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC return 1; case ALC_CONNECTED: - values[0] = device->Connected; + values[0] = ATOMIC_LOAD(&device->Connected, almemory_order_acquire); return 1; default: @@ -2768,28 +3310,12 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC /* render device */ switch(param) { - case ALC_MAJOR_VERSION: - values[0] = alcMajorVersion; - return 1; - - case ALC_MINOR_VERSION: - values[0] = alcMinorVersion; - return 1; - - case ALC_EFX_MAJOR_VERSION: - values[0] = alcEFXMajorVersion; - return 1; - - case ALC_EFX_MINOR_VERSION: - values[0] = alcEFXMinorVersion; - return 1; - case ALC_ATTRIBUTES_SIZE: - values[0] = 17; + values[0] = NumAttrsForDevice(device); return 1; case ALC_ALL_ATTRIBUTES: - if(size < 17) + if(size < NumAttrsForDevice(device)) { alcSetError(device, ALC_INVALID_VALUE); return 0; @@ -2797,9 +3323,17 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC i = 0; almtx_lock(&device->BackendLock); + values[i++] = ALC_MAJOR_VERSION; + values[i++] = alcMajorVersion; + values[i++] = ALC_MINOR_VERSION; + values[i++] = alcMinorVersion; + values[i++] = ALC_EFX_MAJOR_VERSION; + values[i++] = alcEFXMajorVersion; + values[i++] = ALC_EFX_MINOR_VERSION; + values[i++] = alcEFXMinorVersion; + values[i++] = ALC_FREQUENCY; values[i++] = device->Frequency; - if(device->Type != Loopback) { values[i++] = ALC_REFRESH; @@ -2810,6 +3344,18 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC } else { + if(device->FmtChans == DevFmtAmbi3D) + { + values[i++] = ALC_AMBISONIC_LAYOUT_SOFT; + values[i++] = device->AmbiLayout; + + values[i++] = ALC_AMBISONIC_SCALING_SOFT; + values[i++] = device->AmbiScale; + + values[i++] = ALC_AMBISONIC_ORDER_SOFT; + values[i++] = device->AmbiOrder; + } + values[i++] = ALC_FORMAT_CHANNELS_SOFT; values[i++] = device->FmtChans; @@ -2827,15 +3373,37 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC values[i++] = device->NumAuxSends; values[i++] = ALC_HRTF_SOFT; - values[i++] = (device->Hrtf.Handle ? ALC_TRUE : ALC_FALSE); + values[i++] = (device->HrtfHandle ? ALC_TRUE : ALC_FALSE); values[i++] = ALC_HRTF_STATUS_SOFT; - values[i++] = device->Hrtf.Status; + values[i++] = device->HrtfStatus; + + values[i++] = ALC_OUTPUT_LIMITER_SOFT; + values[i++] = device->Limiter ? ALC_TRUE : ALC_FALSE; + + values[i++] = ALC_MAX_AMBISONIC_ORDER_SOFT; + values[i++] = MAX_AMBI_ORDER; almtx_unlock(&device->BackendLock); values[i++] = 0; return i; + case ALC_MAJOR_VERSION: + values[0] = alcMajorVersion; + return 1; + + case ALC_MINOR_VERSION: + values[0] = alcMinorVersion; + return 1; + + case ALC_EFX_MAJOR_VERSION: + values[0] = alcEFXMajorVersion; + return 1; + + case ALC_EFX_MINOR_VERSION: + values[0] = alcEFXMinorVersion; + return 1; + case ALC_FREQUENCY: values[0] = device->Frequency; return 1; @@ -2878,6 +3446,33 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC values[0] = device->FmtType; return 1; + case ALC_AMBISONIC_LAYOUT_SOFT: + if(device->Type != Loopback || device->FmtChans != DevFmtAmbi3D) + { + alcSetError(device, ALC_INVALID_DEVICE); + return 0; + } + values[0] = device->AmbiLayout; + return 1; + + case ALC_AMBISONIC_SCALING_SOFT: + if(device->Type != Loopback || device->FmtChans != DevFmtAmbi3D) + { + alcSetError(device, ALC_INVALID_DEVICE); + return 0; + } + values[0] = device->AmbiScale; + return 1; + + case ALC_AMBISONIC_ORDER_SOFT: + if(device->Type != Loopback || device->FmtChans != DevFmtAmbi3D) + { + alcSetError(device, ALC_INVALID_DEVICE); + return 0; + } + values[0] = device->AmbiOrder; + return 1; + case ALC_MONO_SOURCES: values[0] = device->NumMonoSources; return 1; @@ -2891,25 +3486,33 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC return 1; case ALC_CONNECTED: - values[0] = device->Connected; + values[0] = ATOMIC_LOAD(&device->Connected, almemory_order_acquire); return 1; case ALC_HRTF_SOFT: - values[0] = (device->Hrtf.Handle ? ALC_TRUE : ALC_FALSE); + values[0] = (device->HrtfHandle ? ALC_TRUE : ALC_FALSE); return 1; case ALC_HRTF_STATUS_SOFT: - values[0] = device->Hrtf.Status; + values[0] = device->HrtfStatus; return 1; case ALC_NUM_HRTF_SPECIFIERS_SOFT: almtx_lock(&device->BackendLock); - FreeHrtfList(&device->Hrtf.List); - device->Hrtf.List = EnumerateHrtf(device->DeviceName); - values[0] = (ALCint)VECTOR_SIZE(device->Hrtf.List); + FreeHrtfList(&device->HrtfList); + device->HrtfList = EnumerateHrtf(device->DeviceName); + values[0] = (ALCint)VECTOR_SIZE(device->HrtfList); almtx_unlock(&device->BackendLock); return 1; + case ALC_OUTPUT_LIMITER_SOFT: + values[0] = device->Limiter ? ALC_TRUE : ALC_FALSE; + return 1; + + case ALC_MAX_AMBISONIC_ORDER_SOFT: + values[0] = MAX_AMBI_ORDER; + return 1; + default: alcSetError(device, ALC_INVALID_ENUM); return 0; @@ -2957,11 +3560,11 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, switch(pname) { case ALC_ATTRIBUTES_SIZE: - *values = 21; + *values = NumAttrsForDevice(device)+4; break; case ALC_ALL_ATTRIBUTES: - if(size < 21) + if(size < NumAttrsForDevice(device)+4) alcSetError(device, ALC_INVALID_VALUE); else { @@ -2980,6 +3583,18 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, } else { + if(device->FmtChans == DevFmtAmbi3D) + { + values[i++] = ALC_AMBISONIC_LAYOUT_SOFT; + values[i++] = device->AmbiLayout; + + values[i++] = ALC_AMBISONIC_SCALING_SOFT; + values[i++] = device->AmbiScale; + + values[i++] = ALC_AMBISONIC_ORDER_SOFT; + values[i++] = device->AmbiOrder; + } + values[i++] = ALC_FORMAT_CHANNELS_SOFT; values[i++] = device->FmtChans; @@ -2997,10 +3612,13 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, values[i++] = device->NumAuxSends; values[i++] = ALC_HRTF_SOFT; - values[i++] = (device->Hrtf.Handle ? ALC_TRUE : ALC_FALSE); + values[i++] = (device->HrtfHandle ? ALC_TRUE : ALC_FALSE); values[i++] = ALC_HRTF_STATUS_SOFT; - values[i++] = device->Hrtf.Status; + values[i++] = device->HrtfStatus; + + values[i++] = ALC_OUTPUT_LIMITER_SOFT; + values[i++] = device->Limiter ? ALC_TRUE : ALC_FALSE; clock = V0(device->Backend,getClockLatency)(); values[i++] = ALC_DEVICE_CLOCK_SOFT; @@ -3027,12 +3645,10 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, break; case ALC_DEVICE_LATENCY_SOFT: - { - almtx_lock(&device->BackendLock); - clock = V0(device->Backend,getClockLatency)(); - almtx_unlock(&device->BackendLock); - *values = clock.Latency; - } + almtx_lock(&device->BackendLock); + clock = V0(device->Backend,getClockLatency)(); + almtx_unlock(&device->BackendLock); + *values = clock.Latency; break; case ALC_DEVICE_CLOCK_LATENCY_SOFT: @@ -3040,7 +3656,6 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, alcSetError(device, ALC_INVALID_VALUE); else { - ClockLatency clock; almtx_lock(&device->BackendLock); clock = V0(device->Backend,getClockLatency)(); almtx_unlock(&device->BackendLock); @@ -3117,10 +3732,15 @@ ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar } else { - ALsizei i = 0; - while(alcFunctions[i].funcName && strcmp(alcFunctions[i].funcName, funcName) != 0) - i++; - ptr = alcFunctions[i].address; + size_t i = 0; + for(i = 0;i < COUNTOF(alcFunctions);i++) + { + if(strcmp(alcFunctions[i].funcName, funcName) == 0) + { + ptr = alcFunctions[i].address; + break; + } + } } return ptr; @@ -3143,10 +3763,15 @@ ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *e } else { - ALsizei i = 0; - while(enumeration[i].enumName && strcmp(enumeration[i].enumName, enumName) != 0) - i++; - val = enumeration[i].value; + size_t i = 0; + for(i = 0;i < COUNTOF(alcEnumerations);i++) + { + if(strcmp(alcEnumerations[i].enumName, enumName) == 0) + { + val = alcEnumerations[i].value; + break; + } + } } return val; @@ -3163,8 +3788,13 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin ALfloat valf; ALCenum err; + /* Explicitly hold the list lock while taking the BackendLock in case the + * device is asynchronously destropyed, to ensure this new context is + * properly cleaned up after being made. + */ LockLists(); - if(!VerifyDevice(&device) || device->Type == Capture || !device->Connected) + if(!VerifyDevice(&device) || device->Type == Capture || + !ATOMIC_LOAD(&device->Connected, almemory_order_relaxed)) { UnlockLists(); alcSetError(device, ALC_INVALID_DEVICE); @@ -3174,45 +3804,36 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin almtx_lock(&device->BackendLock); UnlockLists(); - ATOMIC_STORE(&device->LastError, ALC_NO_ERROR); + ATOMIC_STORE_SEQ(&device->LastError, ALC_NO_ERROR); - ALContext = al_calloc(16, sizeof(ALCcontext)+sizeof(ALlistener)); - if(ALContext) - { - InitRef(&ALContext->ref, 1); - ALContext->Listener = (ALlistener*)ALContext->_listener_mem; - - ATOMIC_INIT(&ALContext->ActiveAuxSlotList, NULL); - - ALContext->VoiceCount = 0; - ALContext->MaxVoices = 256; - ALContext->Voices = al_calloc(16, ALContext->MaxVoices * sizeof(ALContext->Voices[0])); - } - if(!ALContext || !ALContext->Voices) + if(device->Type == Playback && DefaultEffect.type != AL_EFFECT_NULL) + ALContext = al_calloc(16, sizeof(ALCcontext)+sizeof(ALlistener)+sizeof(ALeffectslot)); + else + ALContext = al_calloc(16, sizeof(ALCcontext)+sizeof(ALlistener)); + if(!ALContext) { almtx_unlock(&device->BackendLock); - if(ALContext) - { - al_free(ALContext->Voices); - ALContext->Voices = NULL; - - al_free(ALContext); - ALContext = NULL; - } - alcSetError(device, ALC_OUT_OF_MEMORY); ALCdevice_DecRef(device); return NULL; } + InitRef(&ALContext->ref, 1); + ALContext->Listener = (ALlistener*)ALContext->_listener_mem; + ALContext->DefaultSlot = NULL; + + ALContext->Voices = NULL; + ALContext->VoiceCount = 0; + ALContext->MaxVoices = 0; + ATOMIC_INIT(&ALContext->ActiveAuxSlots, NULL); + ALContext->Device = device; + ATOMIC_INIT(&ALContext->next, NULL); + if((err=UpdateDeviceParams(device, attrList)) != ALC_NO_ERROR) { almtx_unlock(&device->BackendLock); - al_free(ALContext->Voices); - ALContext->Voices = NULL; - al_free(ALContext); ALContext = NULL; @@ -3220,18 +3841,30 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin if(err == ALC_INVALID_DEVICE) { V0(device->Backend,lock)(); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Device update failure"); V0(device->Backend,unlock)(); } ALCdevice_DecRef(device); return NULL; } + AllocateVoices(ALContext, 256, device->NumAuxSends); - ALContext->Device = device; - ALCdevice_IncRef(device); + if(DefaultEffect.type != AL_EFFECT_NULL && device->Type == Playback) + { + ALContext->DefaultSlot = (ALeffectslot*)(ALContext->_listener_mem + sizeof(ALlistener)); + if(InitEffectSlot(ALContext->DefaultSlot) == AL_NO_ERROR) + aluInitEffectPanning(ALContext->DefaultSlot); + else + { + ALContext->DefaultSlot = NULL; + ERR("Failed to initialize the default effect slot\n"); + } + } + + ALCdevice_IncRef(ALContext->Device); InitContext(ALContext); - if(ConfigValueFloat(al_string_get_cstr(device->DeviceName), NULL, "volume-adjust", &valf)) + if(ConfigValueFloat(alstr_get_cstr(device->DeviceName), NULL, "volume-adjust", &valf)) { if(!isfinite(valf)) ERR("volume-adjust must be finite: %f\n", valf); @@ -3247,13 +3880,22 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin UpdateListenerProps(ALContext); { - ALCcontext *head = ATOMIC_LOAD(&device->ContextList); + ALCcontext *head = ATOMIC_LOAD_SEQ(&device->ContextList); do { - ALContext->next = head; - } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCcontext*, &device->ContextList, &head, ALContext)); + ATOMIC_STORE(&ALContext->next, head, almemory_order_relaxed); + } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK_SEQ(&device->ContextList, &head, + ALContext) == 0); } almtx_unlock(&device->BackendLock); + if(ALContext->DefaultSlot) + { + if(InitializeEffect(ALContext, ALContext->DefaultSlot, &DefaultEffect) == AL_NO_ERROR) + UpdateEffectSlotProps(ALContext->DefaultSlot, ALContext); + else + ERR("Failed to initialize the default effect\n"); + } + ALCdevice_DecRef(device); TRACE("Created context %p\n", ALContext); @@ -3269,13 +3911,18 @@ ALC_API ALCvoid ALC_APIENTRY alcDestroyContext(ALCcontext *context) ALCdevice *Device; LockLists(); - /* alcGetContextsDevice sets an error for invalid contexts */ - Device = alcGetContextsDevice(context); + if(!VerifyContext(&context)) + { + UnlockLists(); + alcSetError(NULL, ALC_INVALID_CONTEXT); + return; + } + + Device = context->Device; if(Device) { almtx_lock(&Device->BackendLock); - ReleaseContext(context, Device); - if(!ATOMIC_LOAD(&Device->ContextList)) + if(!ReleaseContext(context, Device)) { V0(Device->Backend,stop)(); Device->Flags &= ~DEVICE_RUNNING; @@ -3283,6 +3930,8 @@ ALC_API ALCvoid ALC_APIENTRY alcDestroyContext(ALCcontext *context) almtx_unlock(&Device->BackendLock); } UnlockLists(); + + ALCcontext_DecRef(context); } @@ -3293,7 +3942,7 @@ ALC_API ALCvoid ALC_APIENTRY alcDestroyContext(ALCcontext *context) ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) { ALCcontext *Context = altss_get(LocalContext); - if(!Context) Context = ATOMIC_LOAD(&GlobalContext); + if(!Context) Context = ATOMIC_LOAD_SEQ(&GlobalContext); return Context; } @@ -3321,7 +3970,7 @@ ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) return ALC_FALSE; } /* context's reference count is already incremented */ - context = ATOMIC_EXCHANGE(ALCcontext*, &GlobalContext, context); + context = ATOMIC_EXCHANGE_PTR_SEQ(&GlobalContext, context); if(context) ALCcontext_DecRef(context); if((context=altss_get(LocalContext)) != NULL) @@ -3382,6 +4031,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *Context) */ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) { + ALCbackendFactory *factory; const ALCchar *fmt; ALCdevice *device; ALCenum err; @@ -3406,7 +4056,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) )) deviceName = NULL; - device = al_calloc(16, sizeof(ALCdevice)+sizeof(ALeffectslot)); + device = al_calloc(16, sizeof(ALCdevice)); if(!device) { alcSetError(NULL, ALC_OUT_OF_MEMORY); @@ -3414,79 +4064,39 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) } //Validate device - InitRef(&device->ref, 1); - device->Connected = ALC_TRUE; - device->Type = Playback; - ATOMIC_INIT(&device->LastError, ALC_NO_ERROR); - - device->Flags = 0; - device->Bs2b = NULL; - device->Uhj_Encoder = NULL; - VECTOR_INIT(device->Hrtf.List); - AL_STRING_INIT(device->Hrtf.Name); - device->Render_Mode = NormalRender; - AL_STRING_INIT(device->DeviceName); - device->Dry.Buffer = NULL; - device->Dry.NumChannels = 0; - device->FOAOut.Buffer = NULL; - device->FOAOut.NumChannels = 0; - device->RealOut.Buffer = NULL; - device->RealOut.NumChannels = 0; - - ATOMIC_INIT(&device->ContextList, NULL); - - device->ClockBase = 0; - device->SamplesDone = 0; - - device->SourcesMax = 256; - device->AuxiliaryEffectSlotMax = 4; - device->NumAuxSends = MAX_SENDS; - - InitUIntMap(&device->BufferMap, ~0); - InitUIntMap(&device->EffectMap, ~0); - InitUIntMap(&device->FilterMap, ~0); + InitDevice(device, Playback); //Set output format device->FmtChans = DevFmtChannelsDefault; device->FmtType = DevFmtTypeDefault; device->Frequency = DEFAULT_OUTPUT_RATE; device->IsHeadphones = AL_FALSE; - device->AmbiFmt = AmbiFormat_Default; - device->NumUpdates = 4; + device->AmbiLayout = AmbiLayout_Default; + device->AmbiScale = AmbiNorm_Default; + device->NumUpdates = 3; device->UpdateSize = 1024; - if(!PlaybackBackend.getFactory) - device->Backend = create_backend_wrapper(device, &PlaybackBackend.Funcs, - ALCbackend_Playback); - else - { - ALCbackendFactory *factory = PlaybackBackend.getFactory(); - device->Backend = V(factory,createBackend)(device, ALCbackend_Playback); - } - if(!device->Backend) - { - al_free(device); - alcSetError(NULL, ALC_OUT_OF_MEMORY); - return NULL; - } - + device->SourcesMax = 256; + device->AuxiliaryEffectSlotMax = 64; + device->NumAuxSends = DEFAULT_SENDS; if(ConfigValueStr(deviceName, NULL, "channels", &fmt)) { static const struct { const char name[16]; enum DevFmtChannels chans; + ALsizei order; } chanlist[] = { - { "mono", DevFmtMono }, - { "stereo", DevFmtStereo }, - { "quad", DevFmtQuad }, - { "surround51", DevFmtX51 }, - { "surround61", DevFmtX61 }, - { "surround71", DevFmtX71 }, - { "surround51rear", DevFmtX51Rear }, - { "ambi1", DevFmtAmbi1 }, - { "ambi2", DevFmtAmbi2 }, - { "ambi3", DevFmtAmbi3 }, + { "mono", DevFmtMono, 0 }, + { "stereo", DevFmtStereo, 0 }, + { "quad", DevFmtQuad, 0 }, + { "surround51", DevFmtX51, 0 }, + { "surround61", DevFmtX61, 0 }, + { "surround71", DevFmtX71, 0 }, + { "surround51rear", DevFmtX51Rear, 0 }, + { "ambi1", DevFmtAmbi3D, 1 }, + { "ambi2", DevFmtAmbi3D, 2 }, + { "ambi3", DevFmtAmbi3D, 3 }, }; size_t i; @@ -3495,6 +4105,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) if(strcasecmp(chanlist[i].name, fmt) == 0) { device->FmtChans = chanlist[i].chans; + device->AmbiOrder = chanlist[i].order; device->Flags |= DEVICE_CHANNELS_REQUEST; break; } @@ -3551,64 +4162,65 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) if(device->SourcesMax == 0) device->SourcesMax = 256; ConfigValueUInt(deviceName, NULL, "slots", &device->AuxiliaryEffectSlotMax); - if(device->AuxiliaryEffectSlotMax == 0) device->AuxiliaryEffectSlotMax = 4; + if(device->AuxiliaryEffectSlotMax == 0) device->AuxiliaryEffectSlotMax = 64; + else device->AuxiliaryEffectSlotMax = minu(device->AuxiliaryEffectSlotMax, INT_MAX); - ConfigValueUInt(deviceName, NULL, "sends", &device->NumAuxSends); - if(device->NumAuxSends > MAX_SENDS) device->NumAuxSends = MAX_SENDS; + if(ConfigValueInt(deviceName, NULL, "sends", &device->NumAuxSends)) + device->NumAuxSends = clampi( + DEFAULT_SENDS, 0, clampi(device->NumAuxSends, 0, MAX_SENDS) + ); device->NumStereoSources = 1; device->NumMonoSources = device->SourcesMax - device->NumStereoSources; + factory = PlaybackBackend.getFactory(); + device->Backend = V(factory,createBackend)(device, ALCbackend_Playback); + if(!device->Backend) + { + FreeDevice(device); + alcSetError(NULL, ALC_OUT_OF_MEMORY); + return NULL; + } + // Find a playback device to open if((err=V(device->Backend,open)(deviceName)) != ALC_NO_ERROR) { - DELETE_OBJ(device->Backend); - al_free(device); + FreeDevice(device); alcSetError(NULL, err); return NULL; } - almtx_init(&device->BackendLock, almtx_plain); - if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "ambi-format", &fmt)) + if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "ambi-format", &fmt)) { if(strcasecmp(fmt, "fuma") == 0) - device->AmbiFmt = AmbiFormat_FuMa; + { + device->AmbiLayout = AmbiLayout_FuMa; + device->AmbiScale = AmbiNorm_FuMa; + } else if(strcasecmp(fmt, "acn+sn3d") == 0) - device->AmbiFmt = AmbiFormat_ACN_SN3D; + { + device->AmbiLayout = AmbiLayout_ACN; + device->AmbiScale = AmbiNorm_SN3D; + } else if(strcasecmp(fmt, "acn+n3d") == 0) - device->AmbiFmt = AmbiFormat_ACN_N3D; + { + device->AmbiLayout = AmbiLayout_ACN; + device->AmbiScale = AmbiNorm_N3D; + } else ERR("Unsupported ambi-format: %s\n", fmt); } - if(DefaultEffect.type != AL_EFFECT_NULL) - { - device->DefaultSlot = (ALeffectslot*)device->_slot_mem; - if(InitEffectSlot(device->DefaultSlot) != AL_NO_ERROR) - { - device->DefaultSlot = NULL; - ERR("Failed to initialize the default effect slot\n"); - } - else - { - aluInitEffectPanning(device->DefaultSlot); - if(InitializeEffect(device, device->DefaultSlot, &DefaultEffect) != AL_NO_ERROR) - { - DeinitEffectSlot(device->DefaultSlot); - device->DefaultSlot = NULL; - ERR("Failed to initialize the default effect\n"); - } - } - } + device->Limiter = CreateDeviceLimiter(device); { - ALCdevice *head = ATOMIC_LOAD(&DeviceList); + ALCdevice *head = ATOMIC_LOAD_SEQ(&DeviceList); do { - device->next = head; - } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCdevice*, &DeviceList, &head, device)); + ATOMIC_STORE(&device->next, head, almemory_order_relaxed); + } while(!ATOMIC_COMPARE_EXCHANGE_PTR_WEAK_SEQ(&DeviceList, &head, device)); } - TRACE("Created device %p, \"%s\"\n", device, al_string_get_cstr(device->DeviceName)); + TRACE("Created device %p, \"%s\"\n", device, alstr_get_cstr(device->DeviceName)); return device; } @@ -3618,38 +4230,40 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) */ ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) { - ALCdevice *list, *origdev, *nextdev; + ALCdevice *iter, *origdev, *nextdev; ALCcontext *ctx; LockLists(); - list = ATOMIC_LOAD(&DeviceList); + iter = ATOMIC_LOAD_SEQ(&DeviceList); do { - if(list == device) + if(iter == device) break; - } while((list=list->next) != NULL); - if(!list || list->Type == Capture) + iter = ATOMIC_LOAD(&iter->next, almemory_order_relaxed); + } while(iter != NULL); + if(!iter || iter->Type == Capture) { - alcSetError(list, ALC_INVALID_DEVICE); + alcSetError(iter, ALC_INVALID_DEVICE); UnlockLists(); return ALC_FALSE; } almtx_lock(&device->BackendLock); origdev = device; - nextdev = device->next; - if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALCdevice*, &DeviceList, &origdev, nextdev)) + nextdev = ATOMIC_LOAD(&device->next, almemory_order_relaxed); + if(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&DeviceList, &origdev, nextdev)) { + ALCdevice *list; do { list = origdev; origdev = device; - } while(!COMPARE_EXCHANGE(&list->next, &origdev, nextdev)); + } while(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&list->next, &origdev, nextdev)); } UnlockLists(); - ctx = ATOMIC_LOAD(&device->ContextList); + ctx = ATOMIC_LOAD_SEQ(&device->ContextList); while(ctx != NULL) { - ALCcontext *next = ctx->next; + ALCcontext *next = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed); WARN("Releasing context %p\n", ctx); ReleaseContext(ctx, device); ctx = next; @@ -3670,6 +4284,7 @@ ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) ************************************************/ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei samples) { + ALCbackendFactory *factory; ALCdevice *device = NULL; ALCenum err; @@ -3698,100 +4313,84 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, } //Validate device - InitRef(&device->ref, 1); - device->Connected = ALC_TRUE; - device->Type = Capture; + InitDevice(device, Capture); - VECTOR_INIT(device->Hrtf.List); - AL_STRING_INIT(device->Hrtf.Name); - - AL_STRING_INIT(device->DeviceName); - device->Dry.Buffer = NULL; - device->Dry.NumChannels = 0; - device->FOAOut.Buffer = NULL; - device->FOAOut.NumChannels = 0; - device->RealOut.Buffer = NULL; - device->RealOut.NumChannels = 0; - - InitUIntMap(&device->BufferMap, ~0); - InitUIntMap(&device->EffectMap, ~0); - InitUIntMap(&device->FilterMap, ~0); - - if(!CaptureBackend.getFactory) - device->Backend = create_backend_wrapper(device, &CaptureBackend.Funcs, - ALCbackend_Capture); - else - { - ALCbackendFactory *factory = CaptureBackend.getFactory(); - device->Backend = V(factory,createBackend)(device, ALCbackend_Capture); - } - if(!device->Backend) - { - al_free(device); - alcSetError(NULL, ALC_OUT_OF_MEMORY); - return NULL; - } - - device->Flags |= DEVICE_FREQUENCY_REQUEST; device->Frequency = frequency; + device->Flags |= DEVICE_FREQUENCY_REQUEST; - device->Flags |= DEVICE_CHANNELS_REQUEST | DEVICE_SAMPLE_TYPE_REQUEST; if(DecomposeDevFormat(format, &device->FmtChans, &device->FmtType) == AL_FALSE) { - al_free(device); + FreeDevice(device); alcSetError(NULL, ALC_INVALID_ENUM); return NULL; } + device->Flags |= DEVICE_CHANNELS_REQUEST | DEVICE_SAMPLE_TYPE_REQUEST; device->IsHeadphones = AL_FALSE; - device->AmbiFmt = AmbiFormat_Default; + device->AmbiOrder = 0; + device->AmbiLayout = AmbiLayout_Default; + device->AmbiScale = AmbiNorm_Default; device->UpdateSize = samples; device->NumUpdates = 1; + factory = CaptureBackend.getFactory(); + device->Backend = V(factory,createBackend)(device, ALCbackend_Capture); + if(!device->Backend) + { + FreeDevice(device); + alcSetError(NULL, ALC_OUT_OF_MEMORY); + return NULL; + } + + TRACE("Capture format: %s, %s, %uhz, %u update size x%d\n", + DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType), + device->Frequency, device->UpdateSize, device->NumUpdates + ); if((err=V(device->Backend,open)(deviceName)) != ALC_NO_ERROR) { - al_free(device); + FreeDevice(device); alcSetError(NULL, err); return NULL; } - almtx_init(&device->BackendLock, almtx_plain); { - ALCdevice *head = ATOMIC_LOAD(&DeviceList); + ALCdevice *head = ATOMIC_LOAD_SEQ(&DeviceList); do { - device->next = head; - } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCdevice*, &DeviceList, &head, device)); + ATOMIC_STORE(&device->next, head, almemory_order_relaxed); + } while(!ATOMIC_COMPARE_EXCHANGE_PTR_WEAK_SEQ(&DeviceList, &head, device)); } - TRACE("Created device %p, \"%s\"\n", device, al_string_get_cstr(device->DeviceName)); + TRACE("Created device %p, \"%s\"\n", device, alstr_get_cstr(device->DeviceName)); return device; } ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) { - ALCdevice *list, *next, *nextdev; + ALCdevice *iter, *origdev, *nextdev; LockLists(); - list = ATOMIC_LOAD(&DeviceList); + iter = ATOMIC_LOAD_SEQ(&DeviceList); do { - if(list == device) + if(iter == device) break; - } while((list=list->next) != NULL); - if(!list || list->Type != Capture) + iter = ATOMIC_LOAD(&iter->next, almemory_order_relaxed); + } while(iter != NULL); + if(!iter || iter->Type != Capture) { - alcSetError(list, ALC_INVALID_DEVICE); + alcSetError(iter, ALC_INVALID_DEVICE); UnlockLists(); return ALC_FALSE; } - next = device; - nextdev = device->next; - if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALCdevice*, &DeviceList, &next, nextdev)) + origdev = device; + nextdev = ATOMIC_LOAD(&device->next, almemory_order_relaxed); + if(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&DeviceList, &origdev, nextdev)) { + ALCdevice *list; do { - list = next; - next = device; - } while(!COMPARE_EXCHANGE(&list->next, &next, nextdev)); + list = origdev; + origdev = device; + } while(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&list->next, &origdev, nextdev)); } UnlockLists(); @@ -3807,7 +4406,7 @@ ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) else { almtx_lock(&device->BackendLock); - if(!device->Connected) + if(!ATOMIC_LOAD(&device->Connected, almemory_order_acquire)) alcSetError(device, ALC_INVALID_DEVICE); else if(!(device->Flags&DEVICE_RUNNING)) { @@ -3815,7 +4414,7 @@ ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) device->Flags |= DEVICE_RUNNING; else { - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Device start failure"); alcSetError(device, ALC_INVALID_DEVICE); } } @@ -3891,47 +4490,11 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN } //Validate device - InitRef(&device->ref, 1); - device->Connected = ALC_TRUE; - device->Type = Loopback; - ATOMIC_INIT(&device->LastError, ALC_NO_ERROR); - - device->Flags = 0; - VECTOR_INIT(device->Hrtf.List); - AL_STRING_INIT(device->Hrtf.Name); - device->Bs2b = NULL; - device->Uhj_Encoder = NULL; - device->Render_Mode = NormalRender; - AL_STRING_INIT(device->DeviceName); - device->Dry.Buffer = NULL; - device->Dry.NumChannels = 0; - device->FOAOut.Buffer = NULL; - device->FOAOut.NumChannels = 0; - device->RealOut.Buffer = NULL; - device->RealOut.NumChannels = 0; - - ATOMIC_INIT(&device->ContextList, NULL); - - device->ClockBase = 0; - device->SamplesDone = 0; + InitDevice(device, Loopback); device->SourcesMax = 256; - device->AuxiliaryEffectSlotMax = 4; - device->NumAuxSends = MAX_SENDS; - - InitUIntMap(&device->BufferMap, ~0); - InitUIntMap(&device->EffectMap, ~0); - InitUIntMap(&device->FilterMap, ~0); - - factory = ALCloopbackFactory_getFactory(); - device->Backend = V(factory,createBackend)(device, ALCbackend_Loopback); - if(!device->Backend) - { - al_free(device); - alcSetError(NULL, ALC_OUT_OF_MEMORY); - return NULL; - } - almtx_init(&device->BackendLock, almtx_plain); + device->AuxiliaryEffectSlotMax = 64; + device->NumAuxSends = DEFAULT_SENDS; //Set output format device->NumUpdates = 0; @@ -3941,28 +4504,43 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN device->FmtChans = DevFmtChannelsDefault; device->FmtType = DevFmtTypeDefault; device->IsHeadphones = AL_FALSE; - device->AmbiFmt = AmbiFormat_Default; + device->AmbiLayout = AmbiLayout_Default; + device->AmbiScale = AmbiNorm_Default; ConfigValueUInt(NULL, NULL, "sources", &device->SourcesMax); if(device->SourcesMax == 0) device->SourcesMax = 256; ConfigValueUInt(NULL, NULL, "slots", &device->AuxiliaryEffectSlotMax); - if(device->AuxiliaryEffectSlotMax == 0) device->AuxiliaryEffectSlotMax = 4; + if(device->AuxiliaryEffectSlotMax == 0) device->AuxiliaryEffectSlotMax = 64; + else device->AuxiliaryEffectSlotMax = minu(device->AuxiliaryEffectSlotMax, INT_MAX); - ConfigValueUInt(NULL, NULL, "sends", &device->NumAuxSends); - if(device->NumAuxSends > MAX_SENDS) device->NumAuxSends = MAX_SENDS; + if(ConfigValueInt(NULL, NULL, "sends", &device->NumAuxSends)) + device->NumAuxSends = clampi( + DEFAULT_SENDS, 0, clampi(device->NumAuxSends, 0, MAX_SENDS) + ); device->NumStereoSources = 1; device->NumMonoSources = device->SourcesMax - device->NumStereoSources; + factory = ALCloopbackFactory_getFactory(); + device->Backend = V(factory,createBackend)(device, ALCbackend_Loopback); + if(!device->Backend) + { + al_free(device); + alcSetError(NULL, ALC_OUT_OF_MEMORY); + return NULL; + } + // Open the "backend" V(device->Backend,open)("Loopback"); + device->Limiter = CreateDeviceLimiter(device); + { - ALCdevice *head = ATOMIC_LOAD(&DeviceList); + ALCdevice *head = ATOMIC_LOAD_SEQ(&DeviceList); do { - device->next = head; - } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCdevice*, &DeviceList, &head, device)); + ATOMIC_STORE(&device->next, head, almemory_order_relaxed); + } while(!ATOMIC_COMPARE_EXCHANGE_PTR_WEAK_SEQ(&DeviceList, &head, device)); } TRACE("Created device %p\n", device); @@ -3983,9 +4561,7 @@ ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device alcSetError(device, ALC_INVALID_VALUE); else { - if(IsValidALCType(type) && BytesFromDevFmt(type) > 0 && - IsValidALCChannels(channels) && ChannelsFromDevFmt(channels) > 0 && - freq >= MIN_OUTPUT_RATE) + if(IsValidALCType(type) && IsValidALCChannels(channels) && freq >= MIN_OUTPUT_RATE) ret = ALC_TRUE; } if(device) ALCdevice_DecRef(device); @@ -4005,7 +4581,11 @@ FORCE_ALIGN ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, AL else if(samples < 0 || (samples > 0 && buffer == NULL)) alcSetError(device, ALC_INVALID_VALUE); else + { + V0(device->Backend,lock)(); aluMixData(device, buffer, samples); + V0(device->Backend,unlock)(); + } if(device) ALCdevice_DecRef(device); } @@ -4048,16 +4628,16 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) if((device->Flags&DEVICE_PAUSED)) { device->Flags &= ~DEVICE_PAUSED; - if(ATOMIC_LOAD(&device->ContextList) != NULL) + if(ATOMIC_LOAD_SEQ(&device->ContextList) != NULL) { if(V0(device->Backend,start)() != ALC_FALSE) device->Flags |= DEVICE_RUNNING; else { - alcSetError(device, ALC_INVALID_DEVICE); V0(device->Backend,lock)(); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Device start failure"); V0(device->Backend,unlock)(); + alcSetError(device, ALC_INVALID_DEVICE); } } } @@ -4084,8 +4664,8 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum else switch(paramName) { case ALC_HRTF_SPECIFIER_SOFT: - if(index >= 0 && (size_t)index < VECTOR_SIZE(device->Hrtf.List)) - str = al_string_get_cstr(VECTOR_ELEM(device->Hrtf.List, index).name); + if(index >= 0 && (size_t)index < VECTOR_SIZE(device->HrtfList)) + str = alstr_get_cstr(VECTOR_ELEM(device->HrtfList, index).name); else alcSetError(device, ALC_INVALID_VALUE); break; @@ -4108,7 +4688,8 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi ALCenum err; LockLists(); - if(!VerifyDevice(&device) || device->Type == Capture || !device->Connected) + if(!VerifyDevice(&device) || device->Type == Capture || + !ATOMIC_LOAD(&device->Connected, almemory_order_relaxed)) { UnlockLists(); alcSetError(device, ALC_INVALID_DEVICE); @@ -4127,7 +4708,7 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi if(err == ALC_INVALID_DEVICE) { V0(device->Backend,lock)(); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Device start failure"); V0(device->Backend,unlock)(); } ALCdevice_DecRef(device); diff --git a/Engine/lib/openal-soft/Alc/ALu.c b/Engine/lib/openal-soft/Alc/ALu.c index cc01f3367..81914850c 100644 --- a/Engine/lib/openal-soft/Alc/ALu.c +++ b/Engine/lib/openal-soft/Alc/ALu.c @@ -34,27 +34,21 @@ #include "alu.h" #include "bs2b.h" #include "hrtf.h" +#include "mastering.h" #include "uhjfilter.h" #include "bformatdec.h" #include "static_assert.h" +#include "ringbuffer.h" +#include "filters/splitter.h" -#include "mixer_defs.h" +#include "mixer/defs.h" +#include "fpu_modes.h" +#include "cpu_caps.h" +#include "bsinc_inc.h" #include "backends/base.h" -struct ChanMap { - enum Channel channel; - ALfloat angle; - ALfloat elevation; -}; - -/* Cone scalar */ -ALfloat ConeScale = 1.0f; - -/* Localized Z scalar for mono sources */ -ALfloat ZScale = 1.0f; - extern inline ALfloat minf(ALfloat a, ALfloat b); extern inline ALfloat maxf(ALfloat a, ALfloat b); extern inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max); @@ -79,9 +73,12 @@ extern inline ALuint64 minu64(ALuint64 a, ALuint64 b); extern inline ALuint64 maxu64(ALuint64 a, ALuint64 b); extern inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max); +extern inline size_t minz(size_t a, size_t b); +extern inline size_t maxz(size_t a, size_t b); +extern inline size_t clampz(size_t val, size_t min, size_t max); + extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu); -extern inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALuint frac); -extern inline ALfloat resample_fir8(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat val5, ALfloat val6, ALfloat val7, ALuint frac); +extern inline ALfloat cubic(ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat mu); extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w); @@ -93,6 +90,16 @@ extern inline void aluMatrixfSet(aluMatrixf *matrix, ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23, ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33); + +/* Cone scalar */ +ALfloat ConeScale = 1.0f; + +/* Localized Z scalar for mono sources */ +ALfloat ZScale = 1.0f; + +/* Force default speed of sound for distance-related reverb decay. */ +ALboolean OverrideReverbSpeedOfSound = AL_FALSE; + const aluMatrixf IdentityMatrixf = {{ { 1.0f, 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f, 0.0f }, @@ -101,21 +108,64 @@ const aluMatrixf IdentityMatrixf = {{ }}; +static void ClearArray(ALfloat f[MAX_OUTPUT_CHANNELS]) +{ + size_t i; + for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) + f[i] = 0.0f; +} + +struct ChanMap { + enum Channel channel; + ALfloat angle; + ALfloat elevation; +}; + +static HrtfDirectMixerFunc MixDirectHrtf = MixDirectHrtf_C; + + +void DeinitVoice(ALvoice *voice) +{ + al_free(ATOMIC_EXCHANGE_PTR_SEQ(&voice->Update, NULL)); +} + + static inline HrtfDirectMixerFunc SelectHrtfMixer(void) { -#ifdef HAVE_SSE - if((CPUCapFlags&CPU_CAP_SSE)) - return MixDirectHrtf_SSE; -#endif #ifdef HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return MixDirectHrtf_Neon; #endif +#ifdef HAVE_SSE + if((CPUCapFlags&CPU_CAP_SSE)) + return MixDirectHrtf_SSE; +#endif return MixDirectHrtf_C; } +/* Prior to VS2013, MSVC lacks the round() family of functions. */ +#if defined(_MSC_VER) && _MSC_VER < 1800 +static float roundf(float val) +{ + if(val < 0.0f) + return ceilf(val-0.5f); + return floorf(val+0.5f); +} +#endif + +/* 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. + */ +static inline ALuint dither_rng(ALuint *seed) +{ + *seed = (*seed * 96314165) + 907633515; + return *seed; +} + + static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector) { outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1]; @@ -131,14 +181,16 @@ static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2 static ALfloat aluNormalize(ALfloat *vec) { ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]); - if(length > 0.0f) + if(length > FLT_EPSILON) { ALfloat inv_length = 1.0f/length; vec[0] *= inv_length; vec[1] *= inv_length; vec[2] *= inv_length; + return length; } - return length; + vec[0] = vec[1] = vec[2] = 0.0f; + return 0.0f; } static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx) @@ -161,6 +213,135 @@ static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec) } +void aluInit(void) +{ + MixDirectHrtf = SelectHrtfMixer(); +} + + +static void SendSourceStoppedEvent(ALCcontext *context, ALuint id) +{ + ALbitfieldSOFT enabledevt; + AsyncEvent evt; + size_t strpos; + ALuint scale; + + enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire); + if(!(enabledevt&EventType_SourceStateChange)) return; + + evt.EnumType = EventType_SourceStateChange; + evt.Type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT; + evt.ObjectId = id; + evt.Param = AL_STOPPED; + + /* Normally snprintf would be used, but this is called from the mixer and + * that function's not real-time safe, so we have to construct it manually. + */ + strcpy(evt.Message, "Source ID "); strpos = 10; + scale = 1000000000; + while(scale > 0 && scale > id) + scale /= 10; + while(scale > 0) + { + evt.Message[strpos++] = '0' + ((id/scale)%10); + scale /= 10; + } + strcpy(evt.Message+strpos, " state changed to AL_STOPPED"); + + if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1) + alsem_post(&context->EventSem); +} + + +static void ProcessHrtf(ALCdevice *device, ALsizei SamplesToDo) +{ + DirectHrtfState *state; + int lidx, ridx; + ALsizei c; + + if(device->AmbiUp) + ambiup_process(device->AmbiUp, + device->Dry.Buffer, device->Dry.NumChannels, device->FOAOut.Buffer, + SamplesToDo + ); + + lidx = GetChannelIdxByName(&device->RealOut, FrontLeft); + ridx = GetChannelIdxByName(&device->RealOut, FrontRight); + assert(lidx != -1 && ridx != -1); + + state = device->Hrtf; + for(c = 0;c < device->Dry.NumChannels;c++) + { + MixDirectHrtf(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx], + device->Dry.Buffer[c], state->Offset, state->IrSize, + state->Chan[c].Coeffs, state->Chan[c].Values, SamplesToDo + ); + } + state->Offset += SamplesToDo; +} + +static void ProcessAmbiDec(ALCdevice *device, ALsizei SamplesToDo) +{ + if(device->Dry.Buffer != device->FOAOut.Buffer) + bformatdec_upSample(device->AmbiDecoder, + device->Dry.Buffer, device->FOAOut.Buffer, device->FOAOut.NumChannels, + SamplesToDo + ); + bformatdec_process(device->AmbiDecoder, + device->RealOut.Buffer, device->RealOut.NumChannels, device->Dry.Buffer, + SamplesToDo + ); +} + +static void ProcessAmbiUp(ALCdevice *device, ALsizei SamplesToDo) +{ + ambiup_process(device->AmbiUp, + device->RealOut.Buffer, device->RealOut.NumChannels, device->FOAOut.Buffer, + SamplesToDo + ); +} + +static void ProcessUhj(ALCdevice *device, ALsizei SamplesToDo) +{ + int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft); + int ridx = GetChannelIdxByName(&device->RealOut, FrontRight); + assert(lidx != -1 && ridx != -1); + + /* Encode to stereo-compatible 2-channel UHJ output. */ + EncodeUhj2(device->Uhj_Encoder, + device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx], + device->Dry.Buffer, SamplesToDo + ); +} + +static void ProcessBs2b(ALCdevice *device, ALsizei SamplesToDo) +{ + int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft); + int ridx = GetChannelIdxByName(&device->RealOut, FrontRight); + assert(lidx != -1 && ridx != -1); + + /* Apply binaural/crossfeed filter */ + bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx], + device->RealOut.Buffer[ridx], SamplesToDo); +} + +void aluSelectPostProcess(ALCdevice *device) +{ + if(device->HrtfHandle) + device->PostProcess = ProcessHrtf; + else if(device->AmbiDecoder) + device->PostProcess = ProcessAmbiDec; + else if(device->AmbiUp) + device->PostProcess = ProcessAmbiUp; + else if(device->Uhj_Encoder) + device->PostProcess = ProcessUhj; + else if(device->Bs2b) + device->PostProcess = ProcessBs2b; + else + device->PostProcess = NULL; +} + + /* Prepares the interpolator for a given rate (determined by increment). A * result of AL_FALSE indicates that the filter output will completely cut * the input signal. @@ -169,91 +350,71 @@ static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec) * modified for use with an interpolated increment for buttery-smooth pitch * changes. */ -static ALboolean BsincPrepare(const ALuint increment, BsincState *state) +void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table) { - static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f; - static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 }; - static const ALuint to[4][BSINC_SCALE_COUNT] = - { - { 0, 24, 408, 792, 1176, 1560, 1944, 2328, 2648, 2968, 3288, 3544, 3800, 4056, 4248, 4440 }, - { 4632, 5016, 5400, 5784, 6168, 6552, 6936, 7320, 7640, 7960, 8280, 8536, 8792, 9048, 9240, 0 }, - { 0, 9432, 9816, 10200, 10584, 10968, 11352, 11736, 12056, 12376, 12696, 12952, 13208, 13464, 13656, 13848 }, - { 14040, 14424, 14808, 15192, 15576, 15960, 16344, 16728, 17048, 17368, 17688, 17944, 18200, 18456, 18648, 0 } - }; - static const ALuint tm[2][BSINC_SCALE_COUNT] = - { - { 0, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 }, - { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 } - }; - ALfloat sf; - ALuint si, pi; - ALboolean uncut = AL_TRUE; + ALfloat sf = 0.0f; + ALsizei si = BSINC_SCALE_COUNT-1; if(increment > FRACTIONONE) { sf = (ALfloat)FRACTIONONE / increment; - if(sf < scaleBase) - { - /* Signal has been completely cut. The return result can be used - * to skip the filter (and output zeros) as an optimization. - */ - sf = 0.0f; - si = 0; - uncut = AL_FALSE; - } - else - { - sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange; - si = fastf2u(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 - cosf(asinf(sf - si)); - } - } - else - { - sf = 0.0f; - si = BSINC_SCALE_COUNT - 1; + 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 - cosf(asinf(sf - si)); } state->sf = sf; - state->m = m[si]; - state->l = -(ALint)((m[si] / 2) - 1); - /* The CPU cost of this table re-mapping could be traded for the memory - * cost of a complete table map (1024 elements large). - */ - for(pi = 0;pi < BSINC_PHASE_COUNT;pi++) - { - state->coeffs[pi].filter = &bsincTab[to[0][si] + tm[0][si]*pi]; - state->coeffs[pi].scDelta = &bsincTab[to[1][si] + tm[1][si]*pi]; - state->coeffs[pi].phDelta = &bsincTab[to[2][si] + tm[0][si]*pi]; - state->coeffs[pi].spDelta = &bsincTab[to[3][si] + tm[1][si]*pi]; - } - return uncut; + state->m = table->m[si]; + state->l = -((state->m/2) - 1); + state->filter = table->Tab + table->filterOffset[si]; } -static ALboolean CalcListenerParams(ALCcontext *Context) +static bool CalcContextParams(ALCcontext *Context) +{ + ALlistener *Listener = Context->Listener; + struct ALcontextProps *props; + + props = ATOMIC_EXCHANGE_PTR(&Context->Update, NULL, almemory_order_acq_rel); + if(!props) return false; + + 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.DistanceModel = props->DistanceModel; + + ATOMIC_REPLACE_HEAD(struct ALcontextProps*, &Context->FreeContextProps, props); + return true; +} + +static bool CalcListenerParams(ALCcontext *Context) { ALlistener *Listener = Context->Listener; ALfloat N[3], V[3], U[3], P[3]; - struct ALlistenerProps *first; struct ALlistenerProps *props; aluVector vel; - props = ATOMIC_EXCHANGE(struct ALlistenerProps*, &Listener->Update, NULL, almemory_order_acq_rel); - if(!props) return AL_FALSE; + props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel); + if(!props) return false; /* AT then UP */ - N[0] = ATOMIC_LOAD(&props->Forward[0], almemory_order_relaxed); - N[1] = ATOMIC_LOAD(&props->Forward[1], almemory_order_relaxed); - N[2] = ATOMIC_LOAD(&props->Forward[2], almemory_order_relaxed); + N[0] = props->Forward[0]; + N[1] = props->Forward[1]; + N[2] = props->Forward[2]; aluNormalize(N); - V[0] = ATOMIC_LOAD(&props->Up[0], almemory_order_relaxed); - V[1] = ATOMIC_LOAD(&props->Up[1], almemory_order_relaxed); - V[2] = ATOMIC_LOAD(&props->Up[2], almemory_order_relaxed); + V[0] = props->Up[0]; + V[1] = props->Up[1]; + V[2] = props->Up[2]; aluNormalize(V); /* Build and normalize right-vector */ aluCrossproduct(N, V, U); @@ -266,661 +427,787 @@ static ALboolean CalcListenerParams(ALCcontext *Context) 0.0, 0.0, 0.0, 1.0 ); - P[0] = ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed); - P[1] = ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed); - P[2] = ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed); + P[0] = props->Position[0]; + P[1] = props->Position[1]; + P[2] = props->Position[2]; aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix); aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f); - aluVectorSet(&vel, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed), - ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed), - ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed), - 0.0f); + aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f); Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel); - Listener->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed) * Context->GainBoost; - Listener->Params.MetersPerUnit = ATOMIC_LOAD(&props->MetersPerUnit, almemory_order_relaxed); + Listener->Params.Gain = props->Gain * Context->GainBoost; - Listener->Params.DopplerFactor = ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed); - Listener->Params.SpeedOfSound = ATOMIC_LOAD(&props->SpeedOfSound, almemory_order_relaxed) * - ATOMIC_LOAD(&props->DopplerVelocity, almemory_order_relaxed); - - Listener->Params.SourceDistanceModel = ATOMIC_LOAD(&props->SourceDistanceModel, almemory_order_relaxed); - Listener->Params.DistanceModel = ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed); - - /* WARNING: A livelock is theoretically possible if another thread keeps - * changing the freelist head without giving this a chance to actually swap - * in the old container (practically impossible with this little code, - * but...). - */ - first = ATOMIC_LOAD(&Listener->FreeList); - do { - ATOMIC_STORE(&props->next, first, almemory_order_relaxed); - } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALlistenerProps*, - &Listener->FreeList, &first, props) == 0); - - return AL_TRUE; + ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Context->FreeListenerProps, props); + return true; } -static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device) +static bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool force) { - struct ALeffectslotProps *first; struct ALeffectslotProps *props; ALeffectState *state; - props = ATOMIC_EXCHANGE(struct ALeffectslotProps*, &slot->Update, NULL, almemory_order_acq_rel); - if(!props) return AL_FALSE; + props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel); + if(!props && !force) return false; - slot->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed); - slot->Params.AuxSendAuto = ATOMIC_LOAD(&props->AuxSendAuto, almemory_order_relaxed); - slot->Params.EffectType = ATOMIC_LOAD(&props->Type, almemory_order_relaxed); - if(IsReverbEffect(slot->Params.EffectType)) + if(props) { - slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor; - slot->Params.DecayTime = props->Props.Reverb.DecayTime; - slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF; + slot->Params.Gain = props->Gain; + slot->Params.AuxSendAuto = props->AuxSendAuto; + slot->Params.EffectType = props->Type; + slot->Params.EffectProps = 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; + } + + /* Swap effect states. No need to play with the ref counts since they + * keep the same number of refs. + */ + state = props->State; + props->State = slot->Params.EffectState; + slot->Params.EffectState = state; + + ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props); } else - { - slot->Params.RoomRolloff = 0.0f; - slot->Params.DecayTime = 0.0f; - slot->Params.AirAbsorptionGainHF = 1.0f; - } + state = slot->Params.EffectState; - /* Swap effect states. No need to play with the ref counts since they keep - * the same number of refs. - */ - state = ATOMIC_EXCHANGE(ALeffectState*, &props->State, slot->Params.EffectState, - almemory_order_relaxed); - slot->Params.EffectState = state; - - V(state,update)(device, slot, &props->Props); - - /* WARNING: A livelock is theoretically possible if another thread keeps - * changing the freelist head without giving this a chance to actually swap - * in the old container (practically impossible with this little code, - * but...). - */ - first = ATOMIC_LOAD(&slot->FreeList); - do { - ATOMIC_STORE(&props->next, first, almemory_order_relaxed); - } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps*, - &slot->FreeList, &first, props) == 0); - - return AL_TRUE; + V(state,update)(context, slot, &slot->Params.EffectProps); + return true; } -static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext) -{ - static const struct 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) } - }; +static const struct 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) } +}; - const ALCdevice *Device = ALContext->Device; - const ALlistener *Listener = ALContext->Listener; - ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume; - ALfloat DryGain, DryGainHF, DryGainLF; - ALfloat WetGain[MAX_SENDS]; - ALfloat WetGainHF[MAX_SENDS]; - ALfloat WetGainLF[MAX_SENDS]; - ALeffectslot *SendSlots[MAX_SENDS]; - ALuint NumSends, Frequency; - ALboolean Relative; - const struct ChanMap *chans = NULL; +static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALfloat Elev, + const ALfloat Distance, const ALfloat Spread, + const ALfloat DryGain, const ALfloat DryGainHF, + const ALfloat DryGainLF, const ALfloat *WetGain, + const ALfloat *WetGainLF, const ALfloat *WetGainHF, + ALeffectslot **SendSlots, const ALbuffer *Buffer, + const struct ALvoiceProps *props, const ALlistener *Listener, + const ALCdevice *Device) +{ struct ChanMap StereoMap[2] = { { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) }, { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) } }; - ALuint num_channels = 0; - ALboolean DirectChannels; - ALboolean isbformat = AL_FALSE; - ALfloat Pitch; - ALuint i, j, c; + bool DirectChannels = props->DirectChannels; + const ALsizei NumSends = Device->NumAuxSends; + const ALuint Frequency = Device->Frequency; + const struct ChanMap *chans = NULL; + ALsizei num_channels = 0; + bool isbformat = false; + ALfloat downmix_gain = 1.0f; + ALsizei c, i; - /* Get device properties */ - NumSends = Device->NumAuxSends; - Frequency = Device->Frequency; - - /* Get listener properties */ - ListenerGain = Listener->Params.Gain; - - /* Get source properties */ - SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed); - MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed); - MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed); - Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed); - Relative = ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed); - DirectChannels = ATOMIC_LOAD(&props->DirectChannels, almemory_order_relaxed); - - /* Convert counter-clockwise to clockwise. */ - StereoMap[0].angle = -ATOMIC_LOAD(&props->StereoPan[0], almemory_order_relaxed); - StereoMap[1].angle = -ATOMIC_LOAD(&props->StereoPan[1], almemory_order_relaxed); - - voice->DirectOut.Buffer = Device->Dry.Buffer; - voice->DirectOut.Channels = Device->Dry.NumChannels; - for(i = 0;i < NumSends;i++) - { - SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed); - if(!SendSlots[i] && i == 0) - SendSlots[i] = Device->DefaultSlot; - if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL) - { - SendSlots[i] = NULL; - voice->SendOut[i].Buffer = NULL; - voice->SendOut[i].Channels = 0; - } - else - { - voice->SendOut[i].Buffer = SendSlots[i]->WetBuffer; - voice->SendOut[i].Channels = SendSlots[i]->NumChannels; - } - } - - /* Calculate the stepping value */ - Pitch *= (ALfloat)ALBuffer->Frequency / Frequency; - if(Pitch > (ALfloat)MAX_PITCH) - voice->Step = MAX_PITCH<Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1); - BsincPrepare(voice->Step, &voice->SincState); - - /* Calculate gains */ - DryGain = clampf(SourceVolume, MinVolume, MaxVolume); - DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain; - DryGain = minf(DryGain, GAIN_MIX_MAX); - DryGainHF = ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed); - DryGainLF = ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed); - for(i = 0;i < NumSends;i++) - { - WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume); - WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain; - WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX); - WetGainHF[i] = ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed); - WetGainLF[i] = ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed); - } - - switch(ALBuffer->FmtChannels) + switch(Buffer->FmtChannels) { 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 = AL_TRUE; - DirectChannels = AL_FALSE; + isbformat = true; + DirectChannels = false; break; case FmtBFormat3D: num_channels = 4; - isbformat = AL_TRUE; - DirectChannels = AL_FALSE; + isbformat = true; + DirectChannels = false; break; } + for(c = 0;c < num_channels;c++) + { + memset(&voice->Direct.Params[c].Hrtf.Target, 0, + sizeof(voice->Direct.Params[c].Hrtf.Target)); + ClearArray(voice->Direct.Params[c].Gains.Target); + } + for(i = 0;i < NumSends;i++) + { + for(c = 0;c < num_channels;c++) + ClearArray(voice->Send[i].Params[c].Gains.Target); + } + + voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC); if(isbformat) { - ALfloat N[3], V[3], U[3]; - aluMatrixf matrix; - ALfloat scale; + /* Special handling for B-Format sources. */ - /* AT then UP */ - N[0] = ATOMIC_LOAD(&props->Orientation[0][0], almemory_order_relaxed); - N[1] = ATOMIC_LOAD(&props->Orientation[0][1], almemory_order_relaxed); - N[2] = ATOMIC_LOAD(&props->Orientation[0][2], almemory_order_relaxed); - aluNormalize(N); - V[0] = ATOMIC_LOAD(&props->Orientation[1][0], almemory_order_relaxed); - V[1] = ATOMIC_LOAD(&props->Orientation[1][1], almemory_order_relaxed); - V[2] = ATOMIC_LOAD(&props->Orientation[1][2], almemory_order_relaxed); - aluNormalize(V); - if(!Relative) + if(Distance > FLT_EPSILON) { - const aluMatrixf *lmatrix = &Listener->Params.Matrix; - aluMatrixfFloat3(N, 0.0f, lmatrix); - aluMatrixfFloat3(V, 0.0f, lmatrix); - } - /* Build and normalize right-vector */ - aluCrossproduct(N, V, U); - aluNormalize(U); - - /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */ - scale = 1.732050808f; - aluMatrixfSet(&matrix, - 1.414213562f, 0.0f, 0.0f, 0.0f, - 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale, - 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale, - 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale - ); - - voice->DirectOut.Buffer = Device->FOAOut.Buffer; - voice->DirectOut.Channels = Device->FOAOut.NumChannels; - for(c = 0;c < num_channels;c++) - ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain, - voice->Chan[c].Direct.Gains.Target); - - for(i = 0;i < NumSends;i++) - { - if(!SendSlots[i]) - { - for(c = 0;c < num_channels;c++) - { - for(j = 0;j < MAX_EFFECT_CHANNELS;j++) - voice->Chan[c].Send[i].Gains.Target[j] = 0.0f; - } - } - else - { - for(c = 0;c < num_channels;c++) - { - const ALeffectslot *Slot = SendSlots[i]; - ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels, matrix.m[c], - WetGain[i], voice->Chan[c].Send[i].Gains.Target); - } - } - } - - voice->IsHrtf = AL_FALSE; - } - else - { - ALfloat coeffs[MAX_AMBI_COEFFS]; - - if(DirectChannels) - { - /* Skip the virtual channels and write inputs to the real output. */ - voice->DirectOut.Buffer = Device->RealOut.Buffer; - voice->DirectOut.Channels = Device->RealOut.NumChannels; - for(c = 0;c < num_channels;c++) - { - int idx; - for(j = 0;j < MAX_OUTPUT_CHANNELS;j++) - voice->Chan[c].Direct.Gains.Target[j] = 0.0f; - if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1) - voice->Chan[c].Direct.Gains.Target[idx] = DryGain; - } - - /* Auxiliary sends still use normal panning since they mix to B-Format, which can't - * channel-match. */ - for(c = 0;c < num_channels;c++) - { - CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs); - - for(i = 0;i < NumSends;i++) - { - if(!SendSlots[i]) - { - for(j = 0;j < MAX_EFFECT_CHANNELS;j++) - voice->Chan[c].Send[i].Gains.Target[j] = 0.0f; - } - else - { - const ALeffectslot *Slot = SendSlots[i]; - ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs, - WetGain[i], voice->Chan[c].Send[i].Gains.Target); - } - } - } - - voice->IsHrtf = AL_FALSE; - } - else if(Device->Render_Mode == HrtfRender) - { - /* Full HRTF rendering. Skip the virtual channels and render each - * input channel to the real outputs. + /* 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. */ - voice->DirectOut.Buffer = Device->RealOut.Buffer; - voice->DirectOut.Channels = Device->RealOut.NumChannels; - for(c = 0;c < num_channels;c++) + ALfloat coeffs[MAX_AMBI_COEFFS]; + + if(Device->AvgSpeakerDist > 0.0f) { - if(chans[c].channel == LFE) - { - /* Skip LFE */ - voice->Chan[c].Direct.Hrtf.Target.Delay[0] = 0; - voice->Chan[c].Direct.Hrtf.Target.Delay[1] = 0; - for(i = 0;i < HRIR_LENGTH;i++) - { - voice->Chan[c].Direct.Hrtf.Target.Coeffs[i][0] = 0.0f; - voice->Chan[c].Direct.Hrtf.Target.Coeffs[i][1] = 0.0f; - } + ALfloat mdist = Distance * Listener->Params.MetersPerUnit; + ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC / + (mdist * (ALfloat)Device->Frequency); + ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC / + (Device->AvgSpeakerDist * (ALfloat)Device->Frequency); + /* Clamp w0 for really close distances, to prevent excessive + * bass. + */ + w0 = minf(w0, w1*4.0f); - for(i = 0;i < NumSends;i++) - { - for(j = 0;j < MAX_EFFECT_CHANNELS;j++) - voice->Chan[c].Send[i].Gains.Target[j] = 0.0f; - } + /* Only need to adjust the first channel of a B-Format source. */ + NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, w0); - continue; - } - - /* Get the static HRIR coefficients and delays for this channel. */ - GetHrtfCoeffs(Device->Hrtf.Handle, - chans[c].elevation, chans[c].angle, 0.0f, DryGain, - voice->Chan[c].Direct.Hrtf.Target.Coeffs, - voice->Chan[c].Direct.Hrtf.Target.Delay - ); - - /* Normal panning for auxiliary sends. */ - CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs); - - for(i = 0;i < NumSends;i++) - { - if(!SendSlots[i]) - { - for(j = 0;j < MAX_EFFECT_CHANNELS;j++) - voice->Chan[c].Send[i].Gains.Target[j] = 0.0f; - } - else - { - const ALeffectslot *Slot = SendSlots[i]; - ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs, - WetGain[i], voice->Chan[c].Send[i].Gains.Target); - } - } + for(i = 0;i < MAX_AMBI_ORDER+1;i++) + voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i]; + voice->Flags |= VOICE_HAS_NFC; } - voice->IsHrtf = AL_TRUE; + if(Device->Render_Mode == StereoPair) + CalcAnglePairwiseCoeffs(Azi, Elev, Spread, coeffs); + else + CalcAngleCoeffs(Azi, Elev, Spread, coeffs); + + /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */ + ComputeDryPanGains(&Device->Dry, coeffs, DryGain*1.414213562f, + voice->Direct.Params[0].Gains.Target); + for(i = 0;i < NumSends;i++) + { + const ALeffectslot *Slot = SendSlots[i]; + if(Slot) + ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, + coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target + ); + } } else { - /* Non-HRTF rendering. Use normal panning to the output. */ + /* Local B-Format sources have their XYZ channels rotated according + * to the orientation. + */ + const ALfloat sqrt_2 = sqrtf(2.0f); + const ALfloat sqrt_3 = sqrtf(3.0f); + ALfloat N[3], V[3], U[3]; + aluMatrixf matrix; + + 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. + */ + NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, 0.0f); + + voice->Direct.ChannelsPerOrder[0] = 1; + voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3); + for(i = 2;i < MAX_AMBI_ORDER+1;i++) + voice->Direct.ChannelsPerOrder[i] = 0; + voice->Flags |= VOICE_HAS_NFC; + } + + /* AT then UP */ + N[0] = props->Orientation[0][0]; + N[1] = props->Orientation[0][1]; + N[2] = props->Orientation[0][2]; + aluNormalize(N); + V[0] = props->Orientation[1][0]; + V[1] = props->Orientation[1][1]; + V[2] = props->Orientation[1][2]; + aluNormalize(V); + if(!props->HeadRelative) + { + const aluMatrixf *lmatrix = &Listener->Params.Matrix; + aluMatrixfFloat3(N, 0.0f, lmatrix); + aluMatrixfFloat3(V, 0.0f, lmatrix); + } + /* Build and normalize right-vector */ + aluCrossproduct(N, V, U); + aluNormalize(U); + + /* 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. + */ + aluMatrixfSet(&matrix, + // ACN0 ACN1 ACN2 ACN3 + sqrt_2, 0.0f, 0.0f, 0.0f, // Ambi W + 0.0f, -N[0]*sqrt_3, N[1]*sqrt_3, -N[2]*sqrt_3, // Ambi X + 0.0f, U[0]*sqrt_3, -U[1]*sqrt_3, U[2]*sqrt_3, // Ambi Y + 0.0f, -V[0]*sqrt_3, V[1]*sqrt_3, -V[2]*sqrt_3 // Ambi Z + ); + + voice->Direct.Buffer = Device->FOAOut.Buffer; + voice->Direct.Channels = Device->FOAOut.NumChannels; + for(c = 0;c < num_channels;c++) + ComputeFirstOrderGains(&Device->FOAOut, matrix.m[c], DryGain, + voice->Direct.Params[c].Gains.Target); + for(i = 0;i < NumSends;i++) + { + const ALeffectslot *Slot = SendSlots[i]; + if(Slot) + { + for(c = 0;c < num_channels;c++) + ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels, + matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target + ); + } + } + } + } + else if(DirectChannels) + { + /* Direct source channels always play local. Skip the virtual channels + * and write inputs to the matching real outputs. + */ + voice->Direct.Buffer = Device->RealOut.Buffer; + voice->Direct.Channels = Device->RealOut.NumChannels; + + for(c = 0;c < num_channels;c++) + { + int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel); + if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain; + } + + /* Auxiliary sends still use normal channel panning since they mix to + * B-Format, which can't channel-match. + */ + for(c = 0;c < num_channels;c++) + { + ALfloat coeffs[MAX_AMBI_COEFFS]; + CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs); + + for(i = 0;i < NumSends;i++) + { + const ALeffectslot *Slot = SendSlots[i]; + if(Slot) + ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, + coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target + ); + } + } + } + else if(Device->Render_Mode == HrtfRender) + { + /* Full HRTF rendering. Skip the virtual channels and render to the + * real outputs. + */ + voice->Direct.Buffer = Device->RealOut.Buffer; + voice->Direct.Channels = Device->RealOut.NumChannels; + + if(Distance > FLT_EPSILON) + { + ALfloat coeffs[MAX_AMBI_COEFFS]; + + /* Get the HRIR coefficients and delays just once, for the given + * source direction. + */ + GetHrtfCoeffs(Device->HrtfHandle, Elev, Azi, Spread, + voice->Direct.Params[0].Hrtf.Target.Coeffs, + voice->Direct.Params[0].Hrtf.Target.Delay); + voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain; + + /* Remaining channels use the same results as the first. */ + for(c = 1;c < num_channels;c++) + { + /* Skip LFE */ + if(chans[c].channel != LFE) + voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target; + } + + /* Calculate the directional coefficients once, which apply to all + * input channels of the source sends. + */ + CalcAngleCoeffs(Azi, Elev, Spread, coeffs); + + for(i = 0;i < NumSends;i++) + { + const ALeffectslot *Slot = SendSlots[i]; + if(Slot) + for(c = 0;c < num_channels;c++) + { + /* Skip LFE */ + if(chans[c].channel != LFE) + ComputePanningGainsBF(Slot->ChanMap, + Slot->NumChannels, coeffs, WetGain[i] * downmix_gain, + voice->Send[i].Params[c].Gains.Target + ); + } + } + } + else + { + /* Local sources on HRTF play with each channel panned to its + * relative location around the listener, providing "virtual + * speaker" responses. + */ + for(c = 0;c < num_channels;c++) + { + ALfloat coeffs[MAX_AMBI_COEFFS]; + + if(chans[c].channel == LFE) + { + /* Skip LFE */ + continue; + } + + /* Get the HRIR coefficients and delays for this channel + * position. + */ + GetHrtfCoeffs(Device->HrtfHandle, + chans[c].elevation, chans[c].angle, Spread, + voice->Direct.Params[c].Hrtf.Target.Coeffs, + voice->Direct.Params[c].Hrtf.Target.Delay + ); + voice->Direct.Params[c].Hrtf.Target.Gain = DryGain; + + /* Normal panning for auxiliary sends. */ + CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs); + + for(i = 0;i < NumSends;i++) + { + const ALeffectslot *Slot = SendSlots[i]; + if(Slot) + ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, + coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target + ); + } + } + } + + voice->Flags |= VOICE_HAS_HRTF; + } + else + { + /* Non-HRTF rendering. Use normal panning to the output. */ + + if(Distance > FLT_EPSILON) + { + ALfloat coeffs[MAX_AMBI_COEFFS]; + ALfloat w0 = 0.0f; + + /* Calculate NFC filter coefficient if needed. */ + if(Device->AvgSpeakerDist > 0.0f) + { + ALfloat mdist = Distance * Listener->Params.MetersPerUnit; + ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC / + (Device->AvgSpeakerDist * (ALfloat)Device->Frequency); + w0 = SPEEDOFSOUNDMETRESPERSEC / + (mdist * (ALfloat)Device->Frequency); + /* Clamp w0 for really close distances, to prevent excessive + * bass. + */ + w0 = minf(w0, w1*4.0f); + + /* Adjust NFC filters. */ + for(c = 0;c < num_channels;c++) + NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0); + + for(i = 0;i < MAX_AMBI_ORDER+1;i++) + voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i]; + voice->Flags |= VOICE_HAS_NFC; + } + + /* Calculate the directional coefficients once, which apply to all + * input channels. + */ + if(Device->Render_Mode == StereoPair) + CalcAnglePairwiseCoeffs(Azi, Elev, Spread, coeffs); + else + CalcAngleCoeffs(Azi, Elev, Spread, coeffs); + for(c = 0;c < num_channels;c++) { /* Special-case LFE */ if(chans[c].channel == LFE) { - for(j = 0;j < MAX_OUTPUT_CHANNELS;j++) - voice->Chan[c].Direct.Gains.Target[j] = 0.0f; if(Device->Dry.Buffer == Device->RealOut.Buffer) { - int idx; - if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1) - voice->Chan[c].Direct.Gains.Target[idx] = DryGain; + int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel); + if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain; } + continue; + } - for(i = 0;i < NumSends;i++) + ComputeDryPanGains(&Device->Dry, + coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target + ); + } + + for(i = 0;i < NumSends;i++) + { + const ALeffectslot *Slot = SendSlots[i]; + if(Slot) + for(c = 0;c < num_channels;c++) { - ALuint j; - for(j = 0;j < MAX_EFFECT_CHANNELS;j++) - voice->Chan[c].Send[i].Gains.Target[j] = 0.0f; + /* Skip LFE */ + if(chans[c].channel != LFE) + ComputePanningGainsBF(Slot->ChanMap, + Slot->NumChannels, coeffs, WetGain[i] * downmix_gain, + voice->Send[i].Params[c].Gains.Target + ); + } + } + } + else + { + ALfloat w0 = 0.0f; + + 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. + */ + w0 = SPEEDOFSOUNDMETRESPERSEC / + (Device->AvgSpeakerDist * (ALfloat)Device->Frequency); + + for(c = 0;c < num_channels;c++) + NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0); + + for(i = 0;i < MAX_AMBI_ORDER+1;i++) + voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i]; + voice->Flags |= VOICE_HAS_NFC; + } + + for(c = 0;c < num_channels;c++) + { + ALfloat coeffs[MAX_AMBI_COEFFS]; + + /* Special-case LFE */ + if(chans[c].channel == LFE) + { + if(Device->Dry.Buffer == Device->RealOut.Buffer) + { + int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel); + if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain; } continue; } if(Device->Render_Mode == StereoPair) - { - /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */ - ALfloat x = sinf(chans[c].angle) * cosf(chans[c].elevation); - coeffs[0] = clampf(-x, -0.5f, 0.5f) + 0.5f; - voice->Chan[c].Direct.Gains.Target[0] = coeffs[0] * DryGain; - voice->Chan[c].Direct.Gains.Target[1] = (1.0f-coeffs[0]) * DryGain; - for(j = 2;j < MAX_OUTPUT_CHANNELS;j++) - voice->Chan[c].Direct.Gains.Target[j] = 0.0f; - - CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs); - } + CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs); else - { - CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs); - ComputePanningGains(Device->Dry, coeffs, DryGain, - voice->Chan[c].Direct.Gains.Target); - } + CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs); + ComputeDryPanGains(&Device->Dry, + coeffs, DryGain, voice->Direct.Params[c].Gains.Target + ); for(i = 0;i < NumSends;i++) { - if(!SendSlots[i]) - { - ALuint j; - for(j = 0;j < MAX_EFFECT_CHANNELS;j++) - voice->Chan[c].Send[i].Gains.Target[j] = 0.0f; - } - else - { - const ALeffectslot *Slot = SendSlots[i]; - ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs, - WetGain[i], voice->Chan[c].Send[i].Gains.Target); - } + const ALeffectslot *Slot = SendSlots[i]; + if(Slot) + ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, + coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target + ); } } - - voice->IsHrtf = AL_FALSE; } } { - ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) / - Frequency; - ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) / - Frequency; - DryGainHF = maxf(DryGainHF, 0.0001f); - DryGainLF = maxf(DryGainLF, 0.0001f); - for(c = 0;c < num_channels;c++) + ALfloat hfScale = props->Direct.HFReference / Frequency; + ALfloat lfScale = props->Direct.LFReference / Frequency; + ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */ + ALfloat gainLF = maxf(DryGainLF, 0.001f); + + voice->Direct.FilterType = AF_None; + if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass; + if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass; + BiquadFilter_setParams( + &voice->Direct.Params[0].LowPass, BiquadType_HighShelf, + gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f) + ); + BiquadFilter_setParams( + &voice->Direct.Params[0].HighPass, BiquadType_LowShelf, + gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f) + ); + for(c = 1;c < num_channels;c++) { - voice->Chan[c].Direct.FilterType = AF_None; - if(DryGainHF != 1.0f) voice->Chan[c].Direct.FilterType |= AF_LowPass; - if(DryGainLF != 1.0f) voice->Chan[c].Direct.FilterType |= AF_HighPass; - ALfilterState_setParams( - &voice->Chan[c].Direct.LowPass, ALfilterType_HighShelf, - DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f) - ); - ALfilterState_setParams( - &voice->Chan[c].Direct.HighPass, ALfilterType_LowShelf, - DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f) - ); + BiquadFilter_copyParams(&voice->Direct.Params[c].LowPass, + &voice->Direct.Params[0].LowPass); + BiquadFilter_copyParams(&voice->Direct.Params[c].HighPass, + &voice->Direct.Params[0].HighPass); } } for(i = 0;i < NumSends;i++) { - ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) / - Frequency; - ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) / - Frequency; - WetGainHF[i] = maxf(WetGainHF[i], 0.0001f); - WetGainLF[i] = maxf(WetGainLF[i], 0.0001f); - for(c = 0;c < num_channels;c++) + ALfloat hfScale = props->Send[i].HFReference / Frequency; + ALfloat lfScale = props->Send[i].LFReference / Frequency; + ALfloat gainHF = maxf(WetGainHF[i], 0.001f); + ALfloat gainLF = maxf(WetGainLF[i], 0.001f); + + voice->Send[i].FilterType = AF_None; + if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass; + if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass; + BiquadFilter_setParams( + &voice->Send[i].Params[0].LowPass, BiquadType_HighShelf, + gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f) + ); + BiquadFilter_setParams( + &voice->Send[i].Params[0].HighPass, BiquadType_LowShelf, + gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f) + ); + for(c = 1;c < num_channels;c++) { - voice->Chan[c].Send[i].FilterType = AF_None; - if(WetGainHF[i] != 1.0f) voice->Chan[c].Send[i].FilterType |= AF_LowPass; - if(WetGainLF[i] != 1.0f) voice->Chan[c].Send[i].FilterType |= AF_HighPass; - ALfilterState_setParams( - &voice->Chan[c].Send[i].LowPass, ALfilterType_HighShelf, - WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f) - ); - ALfilterState_setParams( - &voice->Chan[c].Send[i].HighPass, ALfilterType_LowShelf, - WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f) - ); + BiquadFilter_copyParams(&voice->Send[i].Params[c].LowPass, + &voice->Send[i].Params[0].LowPass); + BiquadFilter_copyParams(&voice->Send[i].Params[c].HighPass, + &voice->Send[i].Params[0].HighPass); } } } -static void CalcAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext) +static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext) { const ALCdevice *Device = ALContext->Device; const ALlistener *Listener = ALContext->Listener; - aluVector Position, Velocity, Direction, SourceToListener; - ALfloat InnerAngle,OuterAngle,Distance,ClampedDist; - ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff; - ALfloat SourceVolume,ListenerGain; - ALfloat DopplerFactor, SpeedOfSound; - ALfloat AirAbsorptionFactor; - ALfloat RoomAirAbsorption[MAX_SENDS]; - ALeffectslot *SendSlots[MAX_SENDS]; - ALfloat Attenuation; - ALfloat RoomAttenuation[MAX_SENDS]; - ALfloat MetersPerUnit; - ALfloat RoomRolloffBase; - ALfloat RoomRolloff[MAX_SENDS]; - ALfloat DecayDistance[MAX_SENDS]; - ALfloat DryGain; - ALfloat DryGainHF; - ALfloat DryGainLF; - ALboolean DryGainHFAuto; + ALfloat DryGain, DryGainHF, DryGainLF; ALfloat WetGain[MAX_SENDS]; ALfloat WetGainHF[MAX_SENDS]; ALfloat WetGainLF[MAX_SENDS]; - ALboolean WetGainAuto; - ALboolean WetGainHFAuto; + ALeffectslot *SendSlots[MAX_SENDS]; ALfloat Pitch; - ALuint Frequency; - ALint NumSends; - ALint i; + ALsizei i; - DryGainHF = 1.0f; - DryGainLF = 1.0f; - for(i = 0;i < MAX_SENDS;i++) + voice->Direct.Buffer = Device->Dry.Buffer; + voice->Direct.Channels = Device->Dry.NumChannels; + for(i = 0;i < Device->NumAuxSends;i++) { - WetGainHF[i] = 1.0f; - WetGainLF[i] = 1.0f; + SendSlots[i] = props->Send[i].Slot; + if(!SendSlots[i] && i == 0) + SendSlots[i] = ALContext->DefaultSlot; + if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL) + { + SendSlots[i] = NULL; + voice->Send[i].Buffer = NULL; + voice->Send[i].Channels = 0; + } + else + { + voice->Send[i].Buffer = SendSlots[i]->WetBuffer; + voice->Send[i].Channels = SendSlots[i]->NumChannels; + } } - /* Get context/device properties */ - DopplerFactor = Listener->Params.DopplerFactor; - SpeedOfSound = Listener->Params.SpeedOfSound; - NumSends = Device->NumAuxSends; - Frequency = Device->Frequency; + /* Calculate the stepping value */ + Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch; + if(Pitch > (ALfloat)MAX_PITCH) + voice->Step = MAX_PITCH<Step = maxi(fastf2i(Pitch * FRACTIONONE), 1); + if(props->Resampler == BSinc24Resampler) + BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24); + else if(props->Resampler == BSinc12Resampler) + BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12); + voice->Resampler = SelectResampler(props->Resampler); - /* Get listener properties */ - ListenerGain = Listener->Params.Gain; - MetersPerUnit = Listener->Params.MetersPerUnit; + /* Calculate gains */ + DryGain = clampf(props->Gain, props->MinGain, props->MaxGain); + DryGain *= props->Direct.Gain * Listener->Params.Gain; + DryGain = minf(DryGain, GAIN_MIX_MAX); + DryGainHF = props->Direct.GainHF; + DryGainLF = props->Direct.GainLF; + for(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; + } - /* Get source properties */ - SourceVolume = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed); - MinVolume = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed); - MaxVolume = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed); - Pitch = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed); - aluVectorSet(&Position, ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed), - ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed), - ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed), - 1.0f); - aluVectorSet(&Direction, ATOMIC_LOAD(&props->Direction[0], almemory_order_relaxed), - ATOMIC_LOAD(&props->Direction[1], almemory_order_relaxed), - ATOMIC_LOAD(&props->Direction[2], almemory_order_relaxed), - 0.0f); - aluVectorSet(&Velocity, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed), - ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed), - ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed), - 0.0f); - MinDist = ATOMIC_LOAD(&props->RefDistance, almemory_order_relaxed); - MaxDist = ATOMIC_LOAD(&props->MaxDistance, almemory_order_relaxed); - Rolloff = ATOMIC_LOAD(&props->RollOffFactor, almemory_order_relaxed); - DopplerFactor *= ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed); - InnerAngle = ATOMIC_LOAD(&props->InnerAngle, almemory_order_relaxed); - OuterAngle = ATOMIC_LOAD(&props->OuterAngle, almemory_order_relaxed); - AirAbsorptionFactor = ATOMIC_LOAD(&props->AirAbsorptionFactor, almemory_order_relaxed); - DryGainHFAuto = ATOMIC_LOAD(&props->DryGainHFAuto, almemory_order_relaxed); - WetGainAuto = ATOMIC_LOAD(&props->WetGainAuto, almemory_order_relaxed); - WetGainHFAuto = ATOMIC_LOAD(&props->WetGainHFAuto, almemory_order_relaxed); - RoomRolloffBase = ATOMIC_LOAD(&props->RoomRolloffFactor, almemory_order_relaxed); + CalcPanningAndFilters(voice, 0.0f, 0.0f, 0.0f, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain, + WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device); +} - voice->DirectOut.Buffer = Device->Dry.Buffer; - voice->DirectOut.Channels = Device->Dry.NumChannels; +static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext) +{ + const ALCdevice *Device = ALContext->Device; + const ALlistener *Listener = ALContext->Listener; + const ALsizei NumSends = Device->NumAuxSends; + aluVector Position, Velocity, Direction, SourceToListener; + ALfloat Distance, ClampedDist, DopplerFactor; + ALeffectslot *SendSlots[MAX_SENDS]; + ALfloat RoomRolloff[MAX_SENDS]; + ALfloat DecayDistance[MAX_SENDS]; + ALfloat DecayLFDistance[MAX_SENDS]; + ALfloat DecayHFDistance[MAX_SENDS]; + ALfloat DryGain, DryGainHF, DryGainLF; + ALfloat WetGain[MAX_SENDS]; + ALfloat WetGainHF[MAX_SENDS]; + ALfloat WetGainLF[MAX_SENDS]; + bool directional; + ALfloat ev, az; + ALfloat spread; + ALfloat Pitch; + ALint i; + + /* Set mixing buffers and get send parameters. */ + voice->Direct.Buffer = Device->Dry.Buffer; + voice->Direct.Channels = Device->Dry.NumChannels; for(i = 0;i < NumSends;i++) { - SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed); - + SendSlots[i] = props->Send[i].Slot; if(!SendSlots[i] && i == 0) - SendSlots[i] = Device->DefaultSlot; + SendSlots[i] = ALContext->DefaultSlot; if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL) { SendSlots[i] = NULL; RoomRolloff[i] = 0.0f; DecayDistance[i] = 0.0f; - RoomAirAbsorption[i] = 1.0f; + DecayLFDistance[i] = 0.0f; + DecayHFDistance[i] = 0.0f; } else if(SendSlots[i]->Params.AuxSendAuto) { - RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + RoomRolloffBase; + 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 * - SPEEDOFSOUNDMETRESPERSEC; - RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF; + 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 = log10f(REVERB_DECAY_GAIN) / log10f(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] = Rolloff; + RoomRolloff[i] = props->RolloffFactor; DecayDistance[i] = 0.0f; - RoomAirAbsorption[i] = AIRABSORBGAINHF; + DecayLFDistance[i] = 0.0f; + DecayHFDistance[i] = 0.0f; } if(!SendSlots[i]) { - voice->SendOut[i].Buffer = NULL; - voice->SendOut[i].Channels = 0; + voice->Send[i].Buffer = NULL; + voice->Send[i].Channels = 0; } else { - voice->SendOut[i].Buffer = SendSlots[i]->WetBuffer; - voice->SendOut[i].Channels = SendSlots[i]->NumChannels; + voice->Send[i].Buffer = SendSlots[i]->WetBuffer; + voice->Send[i].Channels = SendSlots[i]->NumChannels; } } /* Transform source to listener space (convert to head relative) */ - if(ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed) == AL_FALSE) + aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f); + aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f); + aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f); + if(props->HeadRelative == AL_FALSE) { const aluMatrixf *Matrix = &Listener->Params.Matrix; /* Transform source vectors */ @@ -937,573 +1224,569 @@ static void CalcAttnSourceParams(ALvoice *voice, const struct ALsourceProps *pro Velocity.v[2] += lvelocity->v[2]; } - aluNormalize(Direction.v); + directional = aluNormalize(Direction.v) > 0.0f; SourceToListener.v[0] = -Position.v[0]; SourceToListener.v[1] = -Position.v[1]; SourceToListener.v[2] = -Position.v[2]; SourceToListener.v[3] = 0.0f; Distance = aluNormalize(SourceToListener.v); + /* Initial source gain */ + DryGain = props->Gain; + DryGainHF = 1.0f; + DryGainLF = 1.0f; + for(i = 0;i < NumSends;i++) + { + WetGain[i] = props->Gain; + WetGainHF[i] = 1.0f; + WetGainLF[i] = 1.0f; + } + /* Calculate distance attenuation */ ClampedDist = Distance; - Attenuation = 1.0f; - for(i = 0;i < NumSends;i++) - RoomAttenuation[i] = 1.0f; switch(Listener->Params.SourceDistanceModel ? - ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed) : - Listener->Params.DistanceModel) + props->DistanceModel : Listener->Params.DistanceModel) { case InverseDistanceClamped: - ClampedDist = clampf(ClampedDist, MinDist, MaxDist); - if(MaxDist < MinDist) + ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance); + if(props->MaxDistance < props->RefDistance) break; /*fall-through*/ case InverseDistance: - if(MinDist > 0.0f) + if(!(props->RefDistance > 0.0f)) + ClampedDist = props->RefDistance; + else { - ALfloat dist = lerp(MinDist, ClampedDist, Rolloff); - if(dist > 0.0f) Attenuation = MinDist / dist; + ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor); + if(dist > 0.0f) DryGain *= props->RefDistance / dist; for(i = 0;i < NumSends;i++) { - dist = lerp(MinDist, ClampedDist, RoomRolloff[i]); - if(dist > 0.0f) RoomAttenuation[i] = MinDist / dist; + dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]); + if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist; } } break; case LinearDistanceClamped: - ClampedDist = clampf(ClampedDist, MinDist, MaxDist); - if(MaxDist < MinDist) + ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance); + if(props->MaxDistance < props->RefDistance) break; /*fall-through*/ case LinearDistance: - if(MaxDist != MinDist) + if(!(props->MaxDistance != props->RefDistance)) + ClampedDist = props->RefDistance; + else { - Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist)); - Attenuation = maxf(Attenuation, 0.0f); + ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) / + (props->MaxDistance-props->RefDistance); + DryGain *= maxf(1.0f - attn, 0.0f); for(i = 0;i < NumSends;i++) { - RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist)); - RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f); + attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) / + (props->MaxDistance-props->RefDistance); + WetGain[i] *= maxf(1.0f - attn, 0.0f); } } break; case ExponentDistanceClamped: - ClampedDist = clampf(ClampedDist, MinDist, MaxDist); - if(MaxDist < MinDist) + ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance); + if(props->MaxDistance < props->RefDistance) break; /*fall-through*/ case ExponentDistance: - if(ClampedDist > 0.0f && MinDist > 0.0f) + if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f)) + ClampedDist = props->RefDistance; + else { - Attenuation = powf(ClampedDist/MinDist, -Rolloff); + DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor); for(i = 0;i < NumSends;i++) - RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]); + WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]); } break; case DisableDistance: - ClampedDist = MinDist; + ClampedDist = props->RefDistance; break; } - /* Source Gain + Attenuation */ - DryGain = SourceVolume * Attenuation; - for(i = 0;i < NumSends;i++) - WetGain[i] = SourceVolume * RoomAttenuation[i]; - - /* Distance-based air absorption */ - if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist) - { - ALfloat meters = (ClampedDist-MinDist) * MetersPerUnit; - DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters); - for(i = 0;i < NumSends;i++) - WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters); - } - - if(WetGainAuto) - { - ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f; - - /* Apply a decay-time transformation to the wet path, based on the - * attenuation of the dry path. - * - * Using the apparent distance, based on the distance attenuation, the - * initial decay of the reverb effect is calculated and applied to the - * wet path. - */ - for(i = 0;i < NumSends;i++) - { - if(DecayDistance[i] > 0.0f) - WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]); - } - } - /* Calculate directional soundcones */ - if(InnerAngle < 360.0f) + if(directional && props->InnerAngle < 360.0f) { ALfloat ConeVolume; ALfloat ConeHF; ALfloat Angle; - ALfloat scale; - Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f; - if(Angle > InnerAngle) + Angle = acosf(aluDotproduct(&Direction, &SourceToListener)); + Angle = RAD2DEG(Angle * ConeScale * 2.0f); + if(!(Angle > props->InnerAngle)) { - if(Angle < OuterAngle) - { - scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle); - ConeVolume = lerp( - 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale - ); - ConeHF = lerp( - 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale - ); - } - else - { - ConeVolume = ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed); - ConeHF = ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed); - } - DryGain *= ConeVolume; - if(DryGainHFAuto) - DryGainHF *= ConeHF; + 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; } - /* Wet path uses the total area of the cone emitter (the room will - * receive the same amount of sound regardless of its direction). - */ - scale = (asinf(maxf((OuterAngle-InnerAngle)/360.0f, 0.0f)) / F_PI) + - (InnerAngle/360.0f); - if(WetGainAuto) + DryGain *= ConeVolume; + if(props->DryGainHFAuto) + DryGainHF *= ConeHF; + if(props->WetGainAuto) { - ConeVolume = lerp( - 1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale - ); for(i = 0;i < NumSends;i++) WetGain[i] *= ConeVolume; } - if(WetGainHFAuto) + if(props->WetGainHFAuto) { - ConeHF = lerp( - 1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale - ); for(i = 0;i < NumSends;i++) WetGainHF[i] *= ConeHF; } } /* Apply gain and frequency filters */ - DryGain = clampf(DryGain, MinVolume, MaxVolume); - DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain; - DryGain = minf(DryGain, GAIN_MIX_MAX); - DryGainHF *= ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed); - DryGainLF *= ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed); + 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(i = 0;i < NumSends;i++) { - WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume); - WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain; - WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX); - WetGainHF[i] *= ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed); - WetGainLF[i] *= ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed); + 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 = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor); + DryGainHF *= hfattn; + for(i = 0;i < NumSends;i++) + WetGainHF[i] *= 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(i = 0;i < NumSends;i++) + { + ALfloat gain, gainhf, gainlf; + + if(!(DecayDistance[i] > 0.0f)) + continue; + + gain = powf(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) + { + gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]); + WetGainHF[i] *= minf(gainhf / gain, 1.0f); + gainlf = powf(REVERB_DECAY_GAIN, meters_base/DecayLFDistance[i]); + WetGainLF[i] *= minf(gainlf / gain, 1.0f); + } + } + } + } + + + /* Initial source pitch */ + Pitch = props->Pitch; + /* Calculate velocity-based doppler effect */ + DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor; if(DopplerFactor > 0.0f) { const aluVector *lvelocity = &Listener->Params.Velocity; - ALfloat VSS, VLS; + const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound; + ALfloat vss, vls; - if(SpeedOfSound < 1.0f) + vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor; + vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor; + + if(!(vls < SpeedOfSound)) { - DopplerFactor *= 1.0f/SpeedOfSound; - SpeedOfSound = 1.0f; + /* Listener moving away from the source at the speed of sound. + * Sound waves can't catch it. + */ + Pitch = 0.0f; } - - VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor; - VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor; - - Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) / - clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f); - } - - /* Calculate fixed-point stepping value, based on the pitch, buffer - * frequency, and output frequency. - */ - Pitch *= (ALfloat)ALBuffer->Frequency / Frequency; - if(Pitch > (ALfloat)MAX_PITCH) - voice->Step = MAX_PITCH<Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1); - BsincPrepare(voice->Step, &voice->SincState); - - if(Device->Render_Mode == HrtfRender) - { - /* Full HRTF rendering. Skip the virtual channels and render to the - * real outputs. - */ - ALfloat dir[3] = { 0.0f, 0.0f, -1.0f }; - ALfloat ev = 0.0f, az = 0.0f; - ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed); - ALfloat coeffs[MAX_AMBI_COEFFS]; - ALfloat spread = 0.0f; - - voice->DirectOut.Buffer = Device->RealOut.Buffer; - voice->DirectOut.Channels = Device->RealOut.NumChannels; - - if(Distance > FLT_EPSILON) + else if(!(vss < SpeedOfSound)) { - dir[0] = -SourceToListener.v[0]; - dir[1] = -SourceToListener.v[1]; - dir[2] = -SourceToListener.v[2] * ZScale; - - /* Calculate elevation and azimuth only when the source is not at - * the listener. This prevents +0 and -0 Z from producing - * inconsistent panning. Also, clamp Y in case FP precision errors - * cause it to land outside of -1..+1. */ - ev = asinf(clampf(dir[1], -1.0f, 1.0f)); - az = atan2f(dir[0], -dir[2]); - } - if(radius > Distance) - spread = F_TAU - Distance/radius*F_PI; - else if(Distance > FLT_EPSILON) - spread = asinf(radius / Distance) * 2.0f; - - /* Get the HRIR coefficients and delays. */ - GetHrtfCoeffs(Device->Hrtf.Handle, ev, az, spread, DryGain, - voice->Chan[0].Direct.Hrtf.Target.Coeffs, - voice->Chan[0].Direct.Hrtf.Target.Delay); - - CalcDirectionCoeffs(dir, spread, coeffs); - - for(i = 0;i < NumSends;i++) - { - if(!SendSlots[i]) - { - ALuint j; - for(j = 0;j < MAX_EFFECT_CHANNELS;j++) - voice->Chan[0].Send[i].Gains.Target[j] = 0.0f; - } - else - { - const ALeffectslot *Slot = SendSlots[i]; - ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs, - WetGain[i], voice->Chan[0].Send[i].Gains.Target); - } - } - - voice->IsHrtf = AL_TRUE; - } - else - { - /* Non-HRTF rendering. */ - ALfloat dir[3] = { 0.0f, 0.0f, -1.0f }; - ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed); - ALfloat coeffs[MAX_AMBI_COEFFS]; - ALfloat spread = 0.0f; - - /* Get the localized direction, and compute panned gains. */ - if(Distance > FLT_EPSILON) - { - dir[0] = -SourceToListener.v[0]; - dir[1] = -SourceToListener.v[1]; - dir[2] = -SourceToListener.v[2] * ZScale; - } - if(radius > Distance) - spread = F_TAU - Distance/radius*F_PI; - else if(Distance > FLT_EPSILON) - spread = asinf(radius / Distance) * 2.0f; - - if(Device->Render_Mode == StereoPair) - { - /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */ - ALfloat x = -dir[0] * (0.5f * (cosf(spread*0.5f) + 1.0f)); - x = clampf(x, -0.5f, 0.5f) + 0.5f; - voice->Chan[0].Direct.Gains.Target[0] = x * DryGain; - voice->Chan[0].Direct.Gains.Target[1] = (1.0f-x) * DryGain; - for(i = 2;i < MAX_OUTPUT_CHANNELS;i++) - voice->Chan[0].Direct.Gains.Target[i] = 0.0f; - - CalcDirectionCoeffs(dir, spread, coeffs); + /* Source moving toward the listener at the speed of sound. Sound + * waves bunch up to extreme frequencies. + */ + Pitch = HUGE_VALF; } else { - CalcDirectionCoeffs(dir, spread, coeffs); - ComputePanningGains(Device->Dry, coeffs, DryGain, - voice->Chan[0].Direct.Gains.Target); + /* Source and listener movement is nominal. Calculate the proper + * doppler shift. + */ + Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss); } - - for(i = 0;i < NumSends;i++) - { - if(!SendSlots[i]) - { - ALuint j; - for(j = 0;j < MAX_EFFECT_CHANNELS;j++) - voice->Chan[0].Send[i].Gains.Target[j] = 0.0f; - } - else - { - const ALeffectslot *Slot = SendSlots[i]; - ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs, - WetGain[i], voice->Chan[0].Send[i].Gains.Target); - } - } - - voice->IsHrtf = AL_FALSE; } + /* Adjust pitch based on the buffer and output frequencies, and calculate + * fixed-point stepping value. + */ + Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency; + if(Pitch > (ALfloat)MAX_PITCH) + voice->Step = MAX_PITCH<Step = maxi(fastf2i(Pitch * FRACTIONONE), 1); + if(props->Resampler == BSinc24Resampler) + BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24); + else if(props->Resampler == BSinc12Resampler) + BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12); + voice->Resampler = SelectResampler(props->Resampler); + + if(Distance > 0.0f) { - ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) / - Frequency; - ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) / - Frequency; - DryGainHF = maxf(DryGainHF, 0.0001f); - DryGainLF = maxf(DryGainLF, 0.0001f); - voice->Chan[0].Direct.FilterType = AF_None; - if(DryGainHF != 1.0f) voice->Chan[0].Direct.FilterType |= AF_LowPass; - if(DryGainLF != 1.0f) voice->Chan[0].Direct.FilterType |= AF_HighPass; - ALfilterState_setParams( - &voice->Chan[0].Direct.LowPass, ALfilterType_HighShelf, - DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f) - ); - ALfilterState_setParams( - &voice->Chan[0].Direct.HighPass, ALfilterType_LowShelf, - DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f) - ); - } - for(i = 0;i < NumSends;i++) - { - ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) / - Frequency; - ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) / - Frequency; - WetGainHF[i] = maxf(WetGainHF[i], 0.0001f); - WetGainLF[i] = maxf(WetGainLF[i], 0.0001f); - voice->Chan[0].Send[i].FilterType = AF_None; - if(WetGainHF[i] != 1.0f) voice->Chan[0].Send[i].FilterType |= AF_LowPass; - if(WetGainLF[i] != 1.0f) voice->Chan[0].Send[i].FilterType |= AF_HighPass; - ALfilterState_setParams( - &voice->Chan[0].Send[i].LowPass, ALfilterType_HighShelf, - WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f) - ); - ALfilterState_setParams( - &voice->Chan[0].Send[i].HighPass, ALfilterType_LowShelf, - WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f) - ); + /* Clamp Y, in case rounding errors caused it to end up outside of + * -1...+1. + */ + ev = asinf(clampf(-SourceToListener.v[1], -1.0f, 1.0f)); + /* Double negation on Z cancels out; negate once for changing source- + * to-listener to listener-to-source, and again for right-handed coords + * with -Z in front. + */ + az = atan2f(-SourceToListener.v[0], SourceToListener.v[2]*ZScale); } + else + ev = az = 0.0f; + + if(props->Radius > Distance) + spread = F_TAU - Distance/props->Radius*F_PI; + else if(Distance > 0.0f) + spread = asinf(props->Radius / Distance) * 2.0f; + else + spread = 0.0f; + + CalcPanningAndFilters(voice, az, ev, Distance, spread, DryGain, DryGainHF, DryGainLF, WetGain, + WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device); } -static void CalcSourceParams(ALvoice *voice, ALCcontext *context, ALboolean force) +static void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force) { - ALsource *source = voice->Source; - const ALbufferlistitem *BufferListItem; - struct ALsourceProps *first; - struct ALsourceProps *props; + ALbufferlistitem *BufferListItem; + struct ALvoiceProps *props; - props = ATOMIC_EXCHANGE(struct ALsourceProps*, &source->Update, NULL, almemory_order_acq_rel); + props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel); if(!props && !force) return; if(props) { - voice->Props = *props; + memcpy(voice->Props, props, + FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends) + ); - /* WARNING: A livelock is theoretically possible if another thread - * keeps changing the freelist head without giving this a chance to - * actually swap in the old container (practically impossible with this - * little code, but...). - */ - first = ATOMIC_LOAD(&source->FreeList); - do { - ATOMIC_STORE(&props->next, first, almemory_order_relaxed); - } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALsourceProps*, - &source->FreeList, &first, props) == 0); + ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &context->FreeVoiceProps, props); } + props = voice->Props; - BufferListItem = ATOMIC_LOAD(&source->queue, almemory_order_relaxed); + BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed); while(BufferListItem != NULL) { - const ALbuffer *buffer; - if((buffer=BufferListItem->buffer) != NULL) + const ALbuffer *buffer = NULL; + ALsizei i = 0; + while(!buffer && i < BufferListItem->num_buffers) + buffer = BufferListItem->buffers[i]; + if(LIKELY(buffer)) { - if(buffer->FmtChannels == FmtMono) - CalcAttnSourceParams(voice, &voice->Props, buffer, context); + if(props->SpatializeMode == SpatializeOn || + (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono)) + CalcAttnSourceParams(voice, props, buffer, context); else - CalcNonAttnSourceParams(voice, &voice->Props, buffer, context); + CalcNonAttnSourceParams(voice, props, buffer, context); break; } - BufferListItem = BufferListItem->next; + BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire); } } -static void UpdateContextSources(ALCcontext *ctx, ALeffectslot *slot) +static void ProcessParamUpdates(ALCcontext *ctx, const struct ALeffectslotArray *slots) { - ALvoice *voice, *voice_end; + ALvoice **voice, **voice_end; ALsource *source; + ALsizei i; IncrementRef(&ctx->UpdateCount); - if(!ATOMIC_LOAD(&ctx->HoldUpdates)) + if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire)) { - ALboolean force = CalcListenerParams(ctx); - while(slot) - { - force |= CalcEffectSlotParams(slot, ctx->Device); - slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed); - } + bool cforce = CalcContextParams(ctx); + bool force = CalcListenerParams(ctx) | cforce; + for(i = 0;i < slots->count;i++) + force |= CalcEffectSlotParams(slots->slot[i], ctx, cforce); voice = ctx->Voices; voice_end = voice + ctx->VoiceCount; for(;voice != voice_end;++voice) { - if(!(source=voice->Source)) continue; - if(source->state != AL_PLAYING && source->state != AL_PAUSED) - voice->Source = NULL; - else - CalcSourceParams(voice, ctx, force); + source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire); + if(source) CalcSourceParams(*voice, ctx, force); } } IncrementRef(&ctx->UpdateCount); } -/* Specialized function to clamp to [-1, +1] with only one branch. This also - * converts NaN to 0. */ -static inline ALfloat aluClampf(ALfloat val) +static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE], + int lidx, int ridx, int cidx, ALsizei SamplesToDo, + ALsizei NumChannels) { - if(fabsf(val) <= 1.0f) return val; - return (ALfloat)((0.0f < val) - (val < 0.0f)); -} + ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16); + ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16); + ALsizei i; -static inline ALfloat aluF2F(ALfloat val) -{ return val; } - -static inline ALint aluF2I(ALfloat val) -{ - /* Floats only have a 24-bit mantissa, so [-16777215, +16777215] is the max - * integer range normalized floats can be safely converted to. + /* Apply an all-pass to all channels, except the front-left and front- + * right, so they maintain the same relative phase. */ - return fastf2i(aluClampf(val)*16777215.0f)<<7; + for(i = 0;i < NumChannels;i++) + { + if(i == lidx || i == ridx) + continue; + splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo); + } + + bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo); + bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo); + + for(i = 0;i < SamplesToDo;i++) + { + ALfloat lfsum, hfsum; + ALfloat m, s, c; + + lfsum = lsplit[0][i] + rsplit[0][i]; + hfsum = lsplit[1][i] + rsplit[1][i]; + 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. + */ + m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2); + c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2); + + /* 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; + } } -static inline ALuint aluF2UI(ALfloat val) -{ return aluF2I(val)+2147483648u; } -static inline ALshort aluF2S(ALfloat val) -{ return fastf2i(aluClampf(val)*32767.0f); } -static inline ALushort aluF2US(ALfloat val) -{ return aluF2S(val)+32768; } +static void ApplyDistanceComp(ALfloat (*restrict Samples)[BUFFERSIZE], DistanceComp *distcomp, + ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans) +{ + ALsizei i, c; -static inline ALbyte aluF2B(ALfloat val) -{ return fastf2i(aluClampf(val)*127.0f); } -static inline ALubyte aluF2UB(ALfloat val) -{ return aluF2B(val)+128; } + Values = ASSUME_ALIGNED(Values, 16); + for(c = 0;c < numchans;c++) + { + ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16); + const ALfloat gain = distcomp[c].Gain; + const ALsizei base = distcomp[c].Length; + ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16); -#define DECL_TEMPLATE(T, func) \ -static void Write_##T(ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \ - ALuint SamplesToDo, ALuint numchans) \ + if(base == 0) + { + if(gain < 1.0f) + { + for(i = 0;i < SamplesToDo;i++) + inout[i] *= gain; + } + continue; + } + + if(SamplesToDo >= base) + { + for(i = 0;i < base;i++) + Values[i] = distbuf[i]; + for(;i < SamplesToDo;i++) + Values[i] = inout[i-base]; + memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat)); + } + else + { + for(i = 0;i < SamplesToDo;i++) + Values[i] = distbuf[i]; + memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat)); + memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat)); + } + for(i = 0;i < SamplesToDo;i++) + inout[i] = Values[i]*gain; + } +} + +static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_seed, + const ALfloat quant_scale, const ALsizei SamplesToDo, + const ALsizei numchans) +{ + const ALfloat invscale = 1.0f / quant_scale; + ALuint seed = *dither_seed; + ALsizei c, i; + + /* Dithering. Step 1, generate whitenoise (uniform distribution of random + * values between -1 and +1). Step 2 is to add the noise to the samples, + * before rounding and after scaling up to the desired quantization depth. + */ + for(c = 0;c < numchans;c++) + { + ALfloat *restrict samples = Samples[c]; + for(i = 0;i < SamplesToDo;i++) + { + ALfloat val = samples[i] * quant_scale; + ALuint rng0 = dither_rng(&seed); + ALuint rng1 = dither_rng(&seed); + val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX)); + samples[i] = fastf2i(val) * invscale; + } + } + *dither_seed = seed; +} + + +static inline ALfloat Conv_ALfloat(ALfloat val) +{ return val; } +static inline ALint Conv_ALint(ALfloat val) +{ + /* Floats have a 23-bit mantissa. A bit of the exponent helps out along + * with the sign bit, giving 25 bits. So [-16777216, +16777216] is the max + * integer range normalized floats can be converted to before losing + * precision. + */ + return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7; +} +static inline ALshort Conv_ALshort(ALfloat val) +{ return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); } +static inline ALbyte Conv_ALbyte(ALfloat val) +{ return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); } + +/* Define unsigned output variations. */ +#define DECL_TEMPLATE(T, func, O) \ +static inline T Conv_##T(ALfloat val) { return func(val)+O; } + +DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128) +DECL_TEMPLATE(ALushort, Conv_ALshort, 32768) +DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u) + +#undef DECL_TEMPLATE + +#define DECL_TEMPLATE(T, A) \ +static void Write##A(const ALfloat (*restrict InBuffer)[BUFFERSIZE], \ + ALvoid *OutBuffer, ALsizei Offset, ALsizei SamplesToDo, \ + ALsizei numchans) \ { \ - ALuint i, j; \ + ALsizei i, j; \ for(j = 0;j < numchans;j++) \ { \ - const ALfloat *in = InBuffer[j]; \ - T *restrict out = (T*)OutBuffer + j; \ + const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \ + T *restrict out = (T*)OutBuffer + Offset*numchans + j; \ + \ for(i = 0;i < SamplesToDo;i++) \ - out[i*numchans] = func(in[i]); \ + out[i*numchans] = Conv_##T(in[i]); \ } \ } -DECL_TEMPLATE(ALfloat, aluF2F) -DECL_TEMPLATE(ALuint, aluF2UI) -DECL_TEMPLATE(ALint, aluF2I) -DECL_TEMPLATE(ALushort, aluF2US) -DECL_TEMPLATE(ALshort, aluF2S) -DECL_TEMPLATE(ALubyte, aluF2UB) -DECL_TEMPLATE(ALbyte, aluF2B) +DECL_TEMPLATE(ALfloat, F32) +DECL_TEMPLATE(ALuint, UI32) +DECL_TEMPLATE(ALint, I32) +DECL_TEMPLATE(ALushort, UI16) +DECL_TEMPLATE(ALshort, I16) +DECL_TEMPLATE(ALubyte, UI8) +DECL_TEMPLATE(ALbyte, I8) #undef DECL_TEMPLATE -ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size) +void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples) { - ALuint SamplesToDo; - ALvoice *voice, *voice_end; - ALeffectslot *slot; - ALsource *source; + ALsizei SamplesToDo; + ALsizei SamplesDone; ALCcontext *ctx; - FPUCtl oldMode; - ALuint i, c; + ALsizei i, c; - SetMixerFPUMode(&oldMode); - - while(size > 0) + START_MIXER_MODE(); + for(SamplesDone = 0;SamplesDone < NumSamples;) { - SamplesToDo = minu(size, BUFFERSIZE); + SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE); for(c = 0;c < device->Dry.NumChannels;c++) memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat)); - if(device->Dry.Buffer != device->RealOut.Buffer) - for(c = 0;c < device->RealOut.NumChannels;c++) - memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat)); if(device->Dry.Buffer != device->FOAOut.Buffer) for(c = 0;c < device->FOAOut.NumChannels;c++) memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat)); + if(device->Dry.Buffer != device->RealOut.Buffer) + for(c = 0;c < device->RealOut.NumChannels;c++) + memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat)); IncrementRef(&device->MixCount); - V0(device->Backend,lock)(); - if((slot=device->DefaultSlot) != NULL) - { - CalcEffectSlotParams(device->DefaultSlot, device); - for(i = 0;i < slot->NumChannels;i++) - memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat)); - } - - ctx = ATOMIC_LOAD(&device->ContextList); + ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire); while(ctx) { - ALeffectslot *slotroot; + const struct ALeffectslotArray *auxslots; - slotroot = ATOMIC_LOAD(&ctx->ActiveAuxSlotList); - UpdateContextSources(ctx, slotroot); + auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire); + ProcessParamUpdates(ctx, auxslots); - slot = slotroot; - while(slot) + for(i = 0;i < auxslots->count;i++) { - for(i = 0;i < slot->NumChannels;i++) - memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat)); - slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed); + ALeffectslot *slot = auxslots->slot[i]; + for(c = 0;c < slot->NumChannels;c++) + memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat)); } /* source processing */ - voice = ctx->Voices; - voice_end = voice + ctx->VoiceCount; - for(;voice != voice_end;++voice) + for(i = 0;i < ctx->VoiceCount;i++) { - ALboolean IsVoiceInit = (voice->Step > 0); - source = voice->Source; - if(source && source->state == AL_PLAYING && IsVoiceInit) - MixSource(voice, source, device, SamplesToDo); + ALvoice *voice = ctx->Voices[i]; + ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire); + if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) && + voice->Step > 0) + { + if(!MixSource(voice, source->id, ctx, SamplesToDo)) + { + ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed); + ATOMIC_STORE(&voice->Playing, false, almemory_order_release); + SendSourceStoppedEvent(ctx, source->id); + } + } } /* effect slot processing */ - slot = slotroot; - while(slot) + for(i = 0;i < auxslots->count;i++) { + const ALeffectslot *slot = auxslots->slot[i]; ALeffectState *state = slot->Params.EffectState; - V(state,process)(SamplesToDo, SAFE_CONST(ALfloatBUFFERSIZE*,slot->WetBuffer), - state->OutBuffer, state->OutChannels); - slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed); + V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer, + state->OutChannels); } - ctx = ctx->next; - } - - if(device->DefaultSlot != NULL) - { - const ALeffectslot *slot = device->DefaultSlot; - ALeffectState *state = slot->Params.EffectState; - V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer, - state->OutChannels); + ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed); } /* Increment the clock time. Every second's worth of samples is @@ -1513,145 +1796,132 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size) device->SamplesDone += SamplesToDo; device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES; device->SamplesDone %= device->Frequency; - V0(device->Backend,unlock)(); IncrementRef(&device->MixCount); - if(device->Hrtf.Handle) + /* Apply post-process for finalizing the Dry mix to the RealOut + * (Ambisonic decode, UHJ encode, etc). + */ + if(LIKELY(device->PostProcess)) + device->PostProcess(device, SamplesToDo); + + if(device->Stablizer) { - int lidx = GetChannelIdxByName(device->RealOut, FrontLeft); - int ridx = GetChannelIdxByName(device->RealOut, FrontRight); - if(lidx != -1 && ridx != -1) - { - HrtfDirectMixerFunc HrtfMix = SelectHrtfMixer(); - ALuint irsize = device->Hrtf.IrSize; - for(c = 0;c < device->Dry.NumChannels;c++) - { - HrtfMix(device->RealOut.Buffer, lidx, ridx, - device->Dry.Buffer[c], device->Hrtf.Offset, irsize, - device->Hrtf.Coeffs[c], device->Hrtf.Values[c], - SamplesToDo - ); - } - device->Hrtf.Offset += SamplesToDo; - } - } - else if(device->AmbiDecoder) - { - if(device->Dry.Buffer != device->FOAOut.Buffer) - bformatdec_upSample(device->AmbiDecoder, - device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), - device->FOAOut.NumChannels, SamplesToDo - ); - bformatdec_process(device->AmbiDecoder, - device->RealOut.Buffer, device->RealOut.NumChannels, - SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo - ); - } - else if(device->AmbiUp) - { - ambiup_process(device->AmbiUp, - device->RealOut.Buffer, device->RealOut.NumChannels, - SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo - ); - } - else if(device->Uhj_Encoder) - { - int lidx = GetChannelIdxByName(device->RealOut, FrontLeft); - int ridx = GetChannelIdxByName(device->RealOut, FrontRight); - if(lidx != -1 && ridx != -1) - { - /* Encode to stereo-compatible 2-channel UHJ output. */ - EncodeUhj2(device->Uhj_Encoder, - device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx], - device->Dry.Buffer, SamplesToDo - ); - } - } - else if(device->Bs2b) - { - int lidx = GetChannelIdxByName(device->RealOut, FrontLeft); - int ridx = GetChannelIdxByName(device->RealOut, FrontRight); - if(lidx != -1 && ridx != -1) - { - /* Apply binaural/crossfeed filter */ - bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx], - device->RealOut.Buffer[ridx], SamplesToDo); - } + int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft); + int ridx = GetChannelIdxByName(&device->RealOut, FrontRight); + int cidx = GetChannelIdxByName(&device->RealOut, FrontCenter); + assert(lidx >= 0 && ridx >= 0 && cidx >= 0); + + ApplyStablizer(device->Stablizer, device->RealOut.Buffer, lidx, ridx, cidx, + SamplesToDo, device->RealOut.NumChannels); } - if(buffer) - { - ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer; - ALuint OutChannels = device->RealOut.NumChannels; + ApplyDistanceComp(device->RealOut.Buffer, device->ChannelDelay, device->TempBuffer[0], + SamplesToDo, device->RealOut.NumChannels); + + if(device->Limiter) + ApplyCompression(device->Limiter, device->RealOut.NumChannels, SamplesToDo, + device->RealOut.Buffer); + + if(device->DitherDepth > 0.0f) + ApplyDither(device->RealOut.Buffer, &device->DitherSeed, device->DitherDepth, + SamplesToDo, device->RealOut.NumChannels); + + if(LIKELY(OutBuffer)) + { + ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer; + ALsizei Channels = device->RealOut.NumChannels; -#define WRITE(T, a, b, c, d) do { \ - Write_##T((a), (b), (c), (d)); \ - buffer = (T*)buffer + (c)*(d); \ -} while(0) switch(device->FmtType) { case DevFmtByte: - WRITE(ALbyte, OutBuffer, buffer, SamplesToDo, OutChannels); + WriteI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels); break; case DevFmtUByte: - WRITE(ALubyte, OutBuffer, buffer, SamplesToDo, OutChannels); + WriteUI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels); break; case DevFmtShort: - WRITE(ALshort, OutBuffer, buffer, SamplesToDo, OutChannels); + WriteI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels); break; case DevFmtUShort: - WRITE(ALushort, OutBuffer, buffer, SamplesToDo, OutChannels); + WriteUI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels); break; case DevFmtInt: - WRITE(ALint, OutBuffer, buffer, SamplesToDo, OutChannels); + WriteI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels); break; case DevFmtUInt: - WRITE(ALuint, OutBuffer, buffer, SamplesToDo, OutChannels); + WriteUI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels); break; case DevFmtFloat: - WRITE(ALfloat, OutBuffer, buffer, SamplesToDo, OutChannels); + WriteF32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels); break; } -#undef WRITE } - size -= SamplesToDo; + SamplesDone += SamplesToDo; } - - RestoreFPUMode(&oldMode); + END_MIXER_MODE(); } -ALvoid aluHandleDisconnect(ALCdevice *device) +void aluHandleDisconnect(ALCdevice *device, const char *msg, ...) { - ALCcontext *Context; + ALCcontext *ctx; + AsyncEvent evt; + va_list args; + int msglen; - device->Connected = ALC_FALSE; + if(!ATOMIC_EXCHANGE(&device->Connected, AL_FALSE, almemory_order_acq_rel)) + return; - Context = ATOMIC_LOAD(&device->ContextList); - while(Context) + evt.EnumType = EventType_Disconnected; + evt.Type = AL_EVENT_TYPE_DISCONNECTED_SOFT; + evt.ObjectId = 0; + evt.Param = 0; + + va_start(args, msg); + msglen = vsnprintf(evt.Message, sizeof(evt.Message), msg, args); + va_end(args); + + if(msglen < 0 || (size_t)msglen >= sizeof(evt.Message)) { - ALvoice *voice, *voice_end; + evt.Message[sizeof(evt.Message)-1] = 0; + msglen = (int)strlen(evt.Message); + } + if(msglen > 0) + msg = evt.Message; + else + { + msg = ""; + msglen = (int)strlen(msg); + } - voice = Context->Voices; - voice_end = voice + Context->VoiceCount; - while(voice != voice_end) + ctx = ATOMIC_LOAD_SEQ(&device->ContextList); + while(ctx) + { + ALbitfieldSOFT enabledevt = ATOMIC_LOAD(&ctx->EnabledEvts, almemory_order_acquire); + ALsizei i; + + if((enabledevt&EventType_Disconnected) && + ll_ringbuffer_write(ctx->AsyncEvents, (const char*)&evt, 1) == 1) + alsem_post(&ctx->EventSem); + + for(i = 0;i < ctx->VoiceCount;i++) { - ALsource *source = voice->Source; - voice->Source = NULL; + ALvoice *voice = ctx->Voices[i]; + ALsource *source; - if(source && source->state == AL_PLAYING) + source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_relaxed); + if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed)) { - source->state = AL_STOPPED; - ATOMIC_STORE(&source->current_buffer, NULL); - ATOMIC_STORE(&source->position, 0); - ATOMIC_STORE(&source->position_fraction, 0); + /* If the source's voice was playing, it's now effectively + * stopped (the source state will be updated the next time it's + * checked). + */ + SendSourceStoppedEvent(ctx, source->id); } - - voice++; + ATOMIC_STORE(&voice->Playing, false, almemory_order_release); } - Context->VoiceCount = 0; - Context = Context->next; + ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed); } } diff --git a/Engine/lib/openal-soft/Alc/alcRing.c b/Engine/lib/openal-soft/Alc/alcRing.c deleted file mode 100644 index 5994f1967..000000000 --- a/Engine/lib/openal-soft/Alc/alcRing.c +++ /dev/null @@ -1,301 +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 -#include - -#include "alMain.h" -#include "threads.h" -#include "almalloc.h" -#include "compat.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 { - volatile size_t write_ptr; - volatile size_t read_ptr; - size_t size; - size_t size_mask; - size_t elem_size; - int mlocked; - - alignas(16) char buf[]; -}; - -/* 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. */ -ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz) -{ - ll_ringbuffer_t *rb; - ALuint power_of_two; - - power_of_two = NextPowerOf2(sz); - if(power_of_two < sz) - return NULL; - - rb = al_malloc(16, sizeof(*rb) + power_of_two*elem_sz); - if(!rb) return NULL; - - rb->size = power_of_two; - rb->size_mask = rb->size - 1; - rb->elem_size = elem_sz; - rb->write_ptr = 0; - rb->read_ptr = 0; - rb->mlocked = 0; - return rb; -} - -/* Free all data associated with the ringbuffer `rb'. */ -void ll_ringbuffer_free(ll_ringbuffer_t *rb) -{ - if(rb) - { -#ifdef USE_MLOCK - if(rb->mlocked) - munlock(rb, sizeof(*rb) + rb->size*rb->elem_size); -#endif /* USE_MLOCK */ - al_free(rb); - } -} - -/* Lock the data block of `rb' using the system call 'mlock'. */ -int ll_ringbuffer_mlock(ll_ringbuffer_t *rb) -{ -#ifdef USE_MLOCK - if(!rb->locked && mlock(rb, sizeof(*rb) + rb->size*rb->elem_size)) - return -1; -#endif /* USE_MLOCK */ - rb->mlocked = 1; - return 0; -} - -/* Reset the read and write pointers to zero. This is not thread safe. */ -void ll_ringbuffer_reset(ll_ringbuffer_t *rb) -{ - rb->read_ptr = 0; - rb->write_ptr = 0; - memset(rb->buf, 0, rb->size*rb->elem_size); -} - -/* 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 ll_ringbuffer_read_space(const ll_ringbuffer_t *rb) -{ - size_t w = rb->write_ptr; - size_t r = rb->read_ptr; - return (rb->size+w-r) & rb->size_mask; -} -/* 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 ll_ringbuffer_write_space(const ll_ringbuffer_t *rb) -{ - size_t w = rb->write_ptr; - size_t r = rb->read_ptr; - return (rb->size+r-w-1) & rb->size_mask; -} - -/* The copying data reader. Copy at most `cnt' elements from `rb' to `dest'. - * Returns the actual number of elements copied. */ -size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt) -{ - size_t free_cnt; - size_t cnt2; - size_t to_read; - size_t n1, n2; - - free_cnt = ll_ringbuffer_read_space(rb); - if(free_cnt == 0) return 0; - - to_read = (cnt > free_cnt) ? free_cnt : cnt; - cnt2 = rb->read_ptr + to_read; - if(cnt2 > rb->size) - { - n1 = rb->size - rb->read_ptr; - n2 = cnt2 & rb->size_mask; - } - else - { - n1 = to_read; - n2 = 0; - } - - memcpy(dest, &(rb->buf[rb->read_ptr*rb->elem_size]), n1*rb->elem_size); - rb->read_ptr = (rb->read_ptr + n1) & rb->size_mask; - if(n2) - { - memcpy(dest + n1*rb->elem_size, &(rb->buf[rb->read_ptr*rb->elem_size]), n2*rb->elem_size); - rb->read_ptr = (rb->read_ptr + n2) & rb->size_mask; - } - return to_read; -} - -/* The copying data reader w/o read pointer advance. Copy at most `cnt' - * elements from `rb' to `dest'. Returns the actual number of elements copied. - */ -size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt) -{ - size_t free_cnt; - size_t cnt2; - size_t to_read; - size_t n1, n2; - size_t tmp_read_ptr; - - tmp_read_ptr = rb->read_ptr; - free_cnt = ll_ringbuffer_read_space(rb); - if(free_cnt == 0) return 0; - - to_read = (cnt > free_cnt) ? free_cnt : cnt; - cnt2 = tmp_read_ptr + to_read; - if(cnt2 > rb->size) - { - n1 = rb->size - tmp_read_ptr; - n2 = cnt2 & rb->size_mask; - } - else - { - n1 = to_read; - n2 = 0; - } - - memcpy(dest, &(rb->buf[tmp_read_ptr*rb->elem_size]), n1*rb->elem_size); - tmp_read_ptr = (tmp_read_ptr + n1) & rb->size_mask; - if(n2) - memcpy(dest + n1*rb->elem_size, &(rb->buf[tmp_read_ptr*rb->elem_size]), n2*rb->elem_size); - return to_read; -} - -/* The copying data writer. Copy at most `cnt' elements to `rb' from `src'. - * Returns the actual number of elements copied. */ -size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt) -{ - size_t free_cnt; - size_t cnt2; - size_t to_write; - size_t n1, n2; - - free_cnt = ll_ringbuffer_write_space(rb); - if(free_cnt == 0) return 0; - - to_write = (cnt > free_cnt) ? free_cnt : cnt; - cnt2 = rb->write_ptr + to_write; - if(cnt2 > rb->size) - { - n1 = rb->size - rb->write_ptr; - n2 = cnt2 & rb->size_mask; - } - else - { - n1 = to_write; - n2 = 0; - } - - memcpy(&(rb->buf[rb->write_ptr*rb->elem_size]), src, n1*rb->elem_size); - rb->write_ptr = (rb->write_ptr + n1) & rb->size_mask; - if(n2) - { - memcpy(&(rb->buf[rb->write_ptr*rb->elem_size]), src + n1*rb->elem_size, n2*rb->elem_size); - rb->write_ptr = (rb->write_ptr + n2) & rb->size_mask; - } - return to_write; -} - -/* Advance the read pointer `cnt' places. */ -void ll_ringbuffer_read_advance(ll_ringbuffer_t *rb, size_t cnt) -{ - size_t tmp = (rb->read_ptr + cnt) & rb->size_mask; - rb->read_ptr = tmp; -} - -/* Advance the write pointer `cnt' places. */ -void ll_ringbuffer_write_advance(ll_ringbuffer_t *rb, size_t cnt) -{ - size_t tmp = (rb->write_ptr + cnt) & rb->size_mask; - rb->write_ptr = tmp; -} - -/* The non-copying data reader. `vec' is an array of two places. Set the values - * at `vec' to hold the current readable data at `rb'. If the readable data is - * in one segment the second segment has zero length. */ -void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t * vec) -{ - size_t free_cnt; - size_t cnt2; - size_t w, r; - - w = rb->write_ptr; - r = rb->read_ptr; - free_cnt = (rb->size+w-r) & rb->size_mask; - - cnt2 = r + free_cnt; - if(cnt2 > rb->size) - { - /* Two part vector: the rest of the buffer after the current write ptr, - * plus some from the start of the buffer. */ - vec[0].buf = (char*)&(rb->buf[r*rb->elem_size]); - vec[0].len = rb->size - r; - vec[1].buf = (char*)rb->buf; - vec[1].len = cnt2 & rb->size_mask; - } - else - { - /* Single part vector: just the rest of the buffer */ - vec[0].buf = (char*)&(rb->buf[r*rb->elem_size]); - vec[0].len = free_cnt; - vec[1].buf = NULL; - vec[1].len = 0; - } -} - -/* The non-copying data writer. `vec' is an array of two places. Set the values - * at `vec' to hold the current writeable data at `rb'. If the writeable data - * is in one segment the second segment has zero length. */ -void ll_ringbuffer_get_write_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t *vec) -{ - size_t free_cnt; - size_t cnt2; - size_t w, r; - - w = rb->write_ptr; - r = rb->read_ptr; - free_cnt = (rb->size+r-w-1) & rb->size_mask; - - cnt2 = w + free_cnt; - if(cnt2 > rb->size) - { - /* Two part vector: the rest of the buffer after the current write ptr, - * plus some from the start of the buffer. */ - vec[0].buf = (char*)&(rb->buf[w*rb->elem_size]); - vec[0].len = rb->size - w; - vec[1].buf = (char*)rb->buf; - vec[1].len = cnt2 & rb->size_mask; - } - else - { - vec[0].buf = (char*)&(rb->buf[w*rb->elem_size]); - vec[0].len = free_cnt; - vec[1].buf = NULL; - vec[1].len = 0; - } -} diff --git a/Engine/lib/openal-soft/Alc/alcConfig.c b/Engine/lib/openal-soft/Alc/alconfig.c similarity index 70% rename from Engine/lib/openal-soft/Alc/alcConfig.c rename to Engine/lib/openal-soft/Alc/alconfig.c index f83ffd941..5dbf59d2b 100644 --- a/Engine/lib/openal-soft/Alc/alcConfig.c +++ b/Engine/lib/openal-soft/Alc/alconfig.c @@ -38,6 +38,7 @@ #endif #include "alMain.h" +#include "alconfig.h" #include "compat.h" #include "bool.h" @@ -233,7 +234,61 @@ static void LoadConfigFromFile(FILE *f) curSection[0] = 0; else { - strncpy(curSection, section, sizeof(curSection)-1); + size_t len, p = 0; + do { + char *nextp = strchr(section, '%'); + if(!nextp) + { + strncpy(curSection+p, section, sizeof(curSection)-1-p); + break; + } + + len = nextp - section; + if(len > sizeof(curSection)-1-p) + len = sizeof(curSection)-1-p; + strncpy(curSection+p, section, len); + p += len; + 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); + if(p < sizeof(curSection)-1) + curSection[p++] = b; + section += 3; + } + else if(section[1] == '%') + { + if(p < sizeof(curSection)-1) + curSection[p++] = '%'; + section += 2; + } + else + { + if(p < sizeof(curSection)-1) + curSection[p++] = '%'; + section += 1; + } + if(p < sizeof(curSection)-1) + curSection[p] = 0; + } while(p < sizeof(curSection)-1 && *section != 0); curSection[sizeof(curSection)-1] = 0; } @@ -311,33 +366,33 @@ static void LoadConfigFromFile(FILE *f) #ifdef _WIN32 void ReadALConfig(void) { - WCHAR buffer[PATH_MAX]; + al_string ppath = AL_STRING_INIT_STATIC(); + WCHAR buffer[MAX_PATH]; const WCHAR *str; - al_string ppath; FILE *f; if(SHGetSpecialFolderPathW(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE) { al_string filepath = AL_STRING_INIT_STATIC(); - al_string_copy_wcstr(&filepath, buffer); - al_string_append_cstr(&filepath, "\\alsoft.ini"); + alstr_copy_wcstr(&filepath, buffer); + alstr_append_cstr(&filepath, "\\alsoft.ini"); - TRACE("Loading config %s...\n", al_string_get_cstr(filepath)); - f = al_fopen(al_string_get_cstr(filepath), "rt"); + TRACE("Loading config %s...\n", alstr_get_cstr(filepath)); + f = al_fopen(alstr_get_cstr(filepath), "rt"); if(f) { LoadConfigFromFile(f); fclose(f); } - al_string_deinit(&filepath); + alstr_reset(&filepath); } - ppath = GetProcPath(); - if(!al_string_empty(ppath)) + GetProcBinary(&ppath, NULL); + if(!alstr_empty(ppath)) { - al_string_append_cstr(&ppath, "\\alsoft.ini"); - TRACE("Loading config %s...\n", al_string_get_cstr(ppath)); - f = al_fopen(al_string_get_cstr(ppath), "r"); + alstr_append_cstr(&ppath, "\\alsoft.ini"); + TRACE("Loading config %s...\n", alstr_get_cstr(ppath)); + f = al_fopen(alstr_get_cstr(ppath), "r"); if(f) { LoadConfigFromFile(f); @@ -348,26 +403,26 @@ void ReadALConfig(void) if((str=_wgetenv(L"ALSOFT_CONF")) != NULL && *str) { al_string filepath = AL_STRING_INIT_STATIC(); - al_string_copy_wcstr(&filepath, str); + alstr_copy_wcstr(&filepath, str); - TRACE("Loading config %s...\n", al_string_get_cstr(filepath)); - f = al_fopen(al_string_get_cstr(filepath), "rt"); + TRACE("Loading config %s...\n", alstr_get_cstr(filepath)); + f = al_fopen(alstr_get_cstr(filepath), "rt"); if(f) { LoadConfigFromFile(f); fclose(f); } - al_string_deinit(&filepath); + alstr_reset(&filepath); } - al_string_deinit(&ppath); + alstr_reset(&ppath); } #else void ReadALConfig(void) { - char buffer[PATH_MAX]; + al_string confpaths = AL_STRING_INIT_STATIC(); + al_string fname = AL_STRING_INIT_STATIC(); const char *str; - al_string ppath; FILE *f; str = "/etc/openal/alsoft.conf"; @@ -382,45 +437,55 @@ void ReadALConfig(void) if(!(str=getenv("XDG_CONFIG_DIRS")) || str[0] == 0) str = "/etc/xdg"; - strncpy(buffer, str, sizeof(buffer)-1); - buffer[sizeof(buffer)-1] = 0; + alstr_copy_cstr(&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. */ - while(1) + while(!alstr_empty(confpaths)) { - char *next = strrchr(buffer, ':'); - if(next) *(next++) = 0; - else next = buffer; - - if(next[0] != '/') - WARN("Ignoring XDG config dir: %s\n", next); + char *next = strrchr(alstr_get_cstr(confpaths), ':'); + if(next) + { + size_t len = next - alstr_get_cstr(confpaths); + alstr_copy_cstr(&fname, next+1); + VECTOR_RESIZE(confpaths, len, len+1); + VECTOR_ELEM(confpaths, len) = 0; + } else { - size_t len = strlen(next); - strncpy(next+len, "/alsoft.conf", buffer+sizeof(buffer)-next-len); - buffer[sizeof(buffer)-1] = 0; + alstr_reset(&fname); + fname = confpaths; + AL_STRING_INIT(confpaths); + } - TRACE("Loading config %s...\n", next); - f = al_fopen(next, "r"); + if(alstr_empty(fname) || VECTOR_FRONT(fname) != '/') + WARN("Ignoring XDG config dir: %s\n", alstr_get_cstr(fname)); + else + { + if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/alsoft.conf"); + else alstr_append_cstr(&fname, "alsoft.conf"); + + TRACE("Loading config %s...\n", alstr_get_cstr(fname)); + f = al_fopen(alstr_get_cstr(fname), "r"); if(f) { LoadConfigFromFile(f); fclose(f); } } - if(next == buffer) - break; + alstr_clear(&fname); } if((str=getenv("HOME")) != NULL && *str) { - snprintf(buffer, sizeof(buffer), "%s/.alsoftrc", str); + alstr_copy_cstr(&fname, str); + if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/.alsoftrc"); + else alstr_append_cstr(&fname, ".alsoftrc"); - TRACE("Loading config %s...\n", buffer); - f = al_fopen(buffer, "r"); + TRACE("Loading config %s...\n", alstr_get_cstr(fname)); + f = al_fopen(alstr_get_cstr(fname), "r"); if(f) { LoadConfigFromFile(f); @@ -429,17 +494,25 @@ void ReadALConfig(void) } if((str=getenv("XDG_CONFIG_HOME")) != NULL && str[0] != 0) - snprintf(buffer, sizeof(buffer), "%s/%s", str, "alsoft.conf"); + { + alstr_copy_cstr(&fname, str); + if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/alsoft.conf"); + else alstr_append_cstr(&fname, "alsoft.conf"); + } else { - buffer[0] = 0; + alstr_clear(&fname); if((str=getenv("HOME")) != NULL && str[0] != 0) - snprintf(buffer, sizeof(buffer), "%s/.config/%s", str, "alsoft.conf"); + { + alstr_copy_cstr(&fname, str); + if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/.config/alsoft.conf"); + else alstr_append_cstr(&fname, ".config/alsoft.conf"); + } } - if(buffer[0] != 0) + if(!alstr_empty(fname)) { - TRACE("Loading config %s...\n", buffer); - f = al_fopen(buffer, "r"); + TRACE("Loading config %s...\n", alstr_get_cstr(fname)); + f = al_fopen(alstr_get_cstr(fname), "r"); if(f) { LoadConfigFromFile(f); @@ -447,12 +520,15 @@ void ReadALConfig(void) } } - ppath = GetProcPath(); - if(!al_string_empty(ppath)) + alstr_clear(&fname); + GetProcBinary(&fname, NULL); + if(!alstr_empty(fname)) { - al_string_append_cstr(&ppath, "/alsoft.conf"); - TRACE("Loading config %s...\n", al_string_get_cstr(ppath)); - f = al_fopen(al_string_get_cstr(ppath), "r"); + if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/alsoft.conf"); + else alstr_append_cstr(&fname, "alsoft.conf"); + + TRACE("Loading config %s...\n", alstr_get_cstr(fname)); + f = al_fopen(alstr_get_cstr(fname), "r"); if(f) { LoadConfigFromFile(f); @@ -471,7 +547,8 @@ void ReadALConfig(void) } } - al_string_deinit(&ppath); + alstr_reset(&fname); + alstr_reset(&confpaths); } #endif diff --git a/Engine/lib/openal-soft/Alc/alconfig.h b/Engine/lib/openal-soft/Alc/alconfig.h new file mode 100644 index 000000000..1e493e2ee --- /dev/null +++ b/Engine/lib/openal-soft/Alc/alconfig.h @@ -0,0 +1,17 @@ +#ifndef ALCONFIG_H +#define ALCONFIG_H + +void ReadALConfig(void); +void FreeALConfig(void); + +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); + +int ConfigValueStr(const char *devName, const char *blockName, const char *keyName, const char **ret); +int ConfigValueInt(const char *devName, const char *blockName, const char *keyName, int *ret); +int ConfigValueUInt(const char *devName, const char *blockName, const char *keyName, unsigned int *ret); +int ConfigValueFloat(const char *devName, const char *blockName, const char *keyName, float *ret); +int ConfigValueBool(const char *devName, const char *blockName, const char *keyName, int *ret); + +#endif /* ALCONFIG_H */ diff --git a/Engine/lib/openal-soft/Alc/alstring.h b/Engine/lib/openal-soft/Alc/alstring.h index 6167f5ce1..923a5ea2d 100644 --- a/Engine/lib/openal-soft/Alc/alstring.h +++ b/Engine/lib/openal-soft/Alc/alstring.h @@ -6,44 +6,53 @@ #include "vector.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef char al_string_char_type; TYPEDEF_VECTOR(al_string_char_type, al_string) TYPEDEF_VECTOR(al_string, vector_al_string) -inline void al_string_deinit(al_string *str) +inline void alstr_reset(al_string *str) { VECTOR_DEINIT(*str); } #define AL_STRING_INIT(_x) do { (_x) = (al_string)NULL; } while(0) #define AL_STRING_INIT_STATIC() ((al_string)NULL) -#define AL_STRING_DEINIT(_x) al_string_deinit(&(_x)) +#define AL_STRING_DEINIT(_x) alstr_reset(&(_x)) -inline size_t al_string_length(const_al_string str) +inline size_t alstr_length(const_al_string str) { return VECTOR_SIZE(str); } -inline ALboolean al_string_empty(const_al_string str) -{ return al_string_length(str) == 0; } +inline ALboolean alstr_empty(const_al_string str) +{ return alstr_length(str) == 0; } -inline const al_string_char_type *al_string_get_cstr(const_al_string str) +inline const al_string_char_type *alstr_get_cstr(const_al_string str) { return str ? &VECTOR_FRONT(str) : ""; } -void al_string_clear(al_string *str); +void alstr_clear(al_string *str); -int al_string_cmp(const_al_string str1, const_al_string str2); -int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2); +int alstr_cmp(const_al_string str1, const_al_string str2); +int alstr_cmp_cstr(const_al_string str1, const al_string_char_type *str2); -void al_string_copy(al_string *str, const_al_string from); -void al_string_copy_cstr(al_string *str, const al_string_char_type *from); -void al_string_copy_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to); +void alstr_copy(al_string *str, const_al_string from); +void alstr_copy_cstr(al_string *str, const al_string_char_type *from); +void alstr_copy_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to); -void al_string_append_char(al_string *str, const al_string_char_type c); -void al_string_append_cstr(al_string *str, const al_string_char_type *from); -void al_string_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to); +void alstr_append_char(al_string *str, const al_string_char_type c); +void alstr_append_cstr(al_string *str, const al_string_char_type *from); +void alstr_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to); #ifdef _WIN32 #include /* Windows-only methods to deal with WideChar strings. */ -void al_string_copy_wcstr(al_string *str, const wchar_t *from); -void al_string_append_wcstr(al_string *str, const wchar_t *from); -void al_string_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to); +void alstr_copy_wcstr(al_string *str, const wchar_t *from); +void alstr_append_wcstr(al_string *str, const wchar_t *from); +void alstr_copy_wrange(al_string *str, const wchar_t *from, const wchar_t *to); +void alstr_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to); +#endif + +#ifdef __cplusplus +} /* extern "C" */ #endif #endif /* ALSTRING_H */ diff --git a/Engine/lib/openal-soft/Alc/ambdec.c b/Engine/lib/openal-soft/Alc/ambdec.c index 255011d5e..da1143354 100644 --- a/Engine/lib/openal-soft/Alc/ambdec.c +++ b/Engine/lib/openal-soft/Alc/ambdec.c @@ -90,6 +90,15 @@ static char *my_strtok_r(char *str, const char *delim, char **saveptr) return str; } +static char *read_int(ALint *num, const char *line, int base) +{ + char *end; + *num = strtol(line, &end, base); + if(end && *end != '\0') + end = lstrip(end); + return end; +} + static char *read_uint(ALuint *num, const char *line, int base) { char *end; @@ -131,7 +140,7 @@ char *read_clipped_line(FILE *f, char **buffer, size_t *maxlen) static int load_ambdec_speakers(AmbDecConf *conf, FILE *f, char **buffer, size_t *maxlen, char **saveptr) { - ALuint cur = 0; + ALsizei cur = 0; while(cur < conf->NumSpeakers) { const char *cmd = my_strtok_r(NULL, " \t", saveptr); @@ -155,7 +164,7 @@ static int load_ambdec_speakers(AmbDecConf *conf, FILE *f, char **buffer, size_t const char *conn = my_strtok_r(NULL, " \t", saveptr); if(!name) WARN("Name not specified for speaker %u\n", cur+1); - else al_string_copy_cstr(&conf->Speakers[cur].Name, name); + else alstr_copy_cstr(&conf->Speakers[cur].Name, name); if(!dist) WARN("Distance not specified for speaker %u\n", cur+1); else read_float(&conf->Speakers[cur].Distance, dist); if(!az) WARN("Azimuth not specified for speaker %u\n", cur+1); @@ -163,7 +172,7 @@ static int load_ambdec_speakers(AmbDecConf *conf, FILE *f, char **buffer, size_t if(!elev) WARN("Elevation not specified for speaker %u\n", cur+1); else read_float(&conf->Speakers[cur].Elevation, elev); if(!conn) TRACE("Connection not specified for speaker %u\n", cur+1); - else al_string_copy_cstr(&conf->Speakers[cur].Connection, conn); + else alstr_copy_cstr(&conf->Speakers[cur].Connection, conn); cur++; } @@ -184,10 +193,10 @@ static int load_ambdec_speakers(AmbDecConf *conf, FILE *f, char **buffer, size_t return 1; } -static int load_ambdec_matrix(ALfloat *gains, ALfloat (*matrix)[MAX_AMBI_COEFFS], ALuint maxrow, FILE *f, char **buffer, size_t *maxlen, char **saveptr) +static int load_ambdec_matrix(ALfloat *gains, ALfloat (*matrix)[MAX_AMBI_COEFFS], ALsizei maxrow, FILE *f, char **buffer, size_t *maxlen, char **saveptr) { int gotgains = 0; - ALuint cur = 0; + ALsizei cur = 0; while(cur < maxrow) { const char *cmd = my_strtok_r(NULL, " \t", saveptr); @@ -269,7 +278,7 @@ static int load_ambdec_matrix(ALfloat *gains, ALfloat (*matrix)[MAX_AMBI_COEFFS] void ambdec_init(AmbDecConf *conf) { - ALuint i; + ALsizei i; memset(conf, 0, sizeof(*conf)); AL_STRING_INIT(conf->Description); @@ -282,13 +291,13 @@ void ambdec_init(AmbDecConf *conf) void ambdec_deinit(AmbDecConf *conf) { - ALuint i; + ALsizei i; - al_string_deinit(&conf->Description); + alstr_reset(&conf->Description); for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) { - al_string_deinit(&conf->Speakers[i].Name); - al_string_deinit(&conf->Speakers[i].Connection); + alstr_reset(&conf->Speakers[i].Name); + alstr_reset(&conf->Speakers[i].Connection); } memset(conf, 0, sizeof(*conf)); } @@ -322,7 +331,7 @@ int ambdec_load(AmbDecConf *conf, const char *fname) if(strcmp(command, "description") == 0) { char *value = my_strtok_r(NULL, "", &saveptr); - al_string_copy_cstr(&conf->Description, lstrip(value)); + alstr_copy_cstr(&conf->Description, lstrip(value)); } else if(strcmp(command, "version") == 0) { @@ -370,7 +379,7 @@ int ambdec_load(AmbDecConf *conf, const char *fname) else if(strcmp(dec, "speakers") == 0) { line = my_strtok_r(NULL, "", &saveptr); - line = read_uint(&conf->NumSpeakers, line, 10); + line = read_int(&conf->NumSpeakers, line, 10); if(line && *line != '\0') { ERR("Extra junk after speakers: %s\n", line); diff --git a/Engine/lib/openal-soft/Alc/ambdec.h b/Engine/lib/openal-soft/Alc/ambdec.h index 8a3befc1a..0bb84072b 100644 --- a/Engine/lib/openal-soft/Alc/ambdec.h +++ b/Engine/lib/openal-soft/Alc/ambdec.h @@ -17,7 +17,7 @@ typedef struct AmbDecConf { ALuint ChanMask; ALuint FreqBands; /* Must be 1 or 2 */ - ALuint NumSpeakers; + ALsizei NumSpeakers; enum AmbDecScaleType CoeffScale; ALfloat XOverFreq; diff --git a/Engine/lib/openal-soft/Alc/backends/alsa.c b/Engine/lib/openal-soft/Alc/backends/alsa.c index 7a9045bb0..e0fdc070b 100644 --- a/Engine/lib/openal-soft/Alc/backends/alsa.c +++ b/Engine/lib/openal-soft/Alc/backends/alsa.c @@ -26,6 +26,8 @@ #include "alMain.h" #include "alu.h" +#include "alconfig.h" +#include "ringbuffer.h" #include "threads.h" #include "compat.h" @@ -199,15 +201,21 @@ static ALCboolean alsa_load(void) #ifdef HAVE_DYNLOAD if(!alsa_handle) { + al_string missing_funcs = AL_STRING_INIT_STATIC(); + 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 = GetSymbol(alsa_handle, #f); \ if(p##f == NULL) { \ error = ALC_TRUE; \ + alstr_append_cstr(&missing_funcs, "\n" #f); \ } \ } while(0) ALSA_FUNCS(LOAD_FUNC); @@ -215,10 +223,11 @@ static ALCboolean alsa_load(void) if(error) { + WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs)); CloseLib(alsa_handle); alsa_handle = NULL; - return ALC_FALSE; } + alstr_reset(&missing_funcs); } #endif @@ -269,11 +278,45 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList) AL_STRING_INIT(entry.name); AL_STRING_INIT(entry.device_name); - al_string_copy_cstr(&entry.name, alsaDevice); - al_string_copy_cstr(&entry.device_name, GetConfigValue(NULL, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ? - "device" : "capture", "default")); + alstr_copy_cstr(&entry.name, alsaDevice); + alstr_copy_cstr(&entry.device_name, GetConfigValue( + NULL, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ? "device" : "capture", "default" + )); VECTOR_PUSH_BACK(*DeviceList, entry); + if(stream == SND_PCM_STREAM_PLAYBACK) + { + const char *customdevs, *sep, *next; + next = GetConfigValue(NULL, "alsa", "custom-devices", ""); + while((customdevs=next) != NULL && customdevs[0]) + { + next = strchr(customdevs, ';'); + sep = strchr(customdevs, '='); + if(!sep) + { + al_string spec = AL_STRING_INIT_STATIC(); + if(next) + alstr_copy_range(&spec, customdevs, next++); + else + alstr_copy_cstr(&spec, customdevs); + ERR("Invalid ALSA device specification \"%s\"\n", alstr_get_cstr(spec)); + alstr_reset(&spec); + continue; + } + + AL_STRING_INIT(entry.name); + AL_STRING_INIT(entry.device_name); + alstr_copy_range(&entry.name, customdevs, sep++); + if(next) + alstr_copy_range(&entry.device_name, sep, next++); + else + alstr_copy_cstr(&entry.device_name, sep); + TRACE("Got device \"%s\", \"%s\"\n", alstr_get_cstr(entry.name), + alstr_get_cstr(entry.device_name)); + VECTOR_PUSH_BACK(*DeviceList, entry); + } + } + card = -1; if((err=snd_card_next(&card)) < 0) ERR("Failed to find a card: %s\n", snd_strerror(err)); @@ -318,7 +361,8 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList) 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 = snd_ctl_pcm_info(handle, pcminfo)) < 0) + { if(err != -ENOENT) ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err)); continue; @@ -330,15 +374,15 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList) ConfigValueStr(NULL, "alsa", name, &device_prefix); snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)", - cardname, devname, cardid, dev); + cardname, devname, cardid, dev); snprintf(device, sizeof(device), "%sCARD=%s,DEV=%d", - device_prefix, cardid, dev); + device_prefix, cardid, dev); TRACE("Got device \"%s\", \"%s\"\n", name, device); AL_STRING_INIT(entry.name); AL_STRING_INIT(entry.device_name); - al_string_copy_cstr(&entry.name, name); - al_string_copy_cstr(&entry.device_name, device); + alstr_copy_cstr(&entry.name, name); + alstr_copy_cstr(&entry.device_name, device); VECTOR_PUSH_BACK(*DeviceList, entry); } snd_ctl_close(handle); @@ -394,7 +438,7 @@ typedef struct ALCplaybackAlsa { ALvoid *buffer; ALsizei size; - volatile int killNow; + ATOMIC(ALenum) killNow; althrd_t thread; } ALCplaybackAlsa; @@ -402,9 +446,8 @@ static int ALCplaybackAlsa_mixerProc(void *ptr); static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr); static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device); -static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, Destruct) +static void ALCplaybackAlsa_Destruct(ALCplaybackAlsa *self); static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name); -static void ALCplaybackAlsa_close(ALCplaybackAlsa *self); static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self); static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self); static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self); @@ -422,6 +465,19 @@ static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device) { ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); SET_VTABLE2(ALCplaybackAlsa, ALCbackend, self); + + self->pcmHandle = NULL; + self->buffer = NULL; + + ATOMIC_INIT(&self->killNow, AL_TRUE); +} + +void ALCplaybackAlsa_Destruct(ALCplaybackAlsa *self) +{ + if(self->pcmHandle) + snd_pcm_close(self->pcmHandle); + self->pcmHandle = NULL; + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); } @@ -441,14 +497,14 @@ static int ALCplaybackAlsa_mixerProc(void *ptr) update_size = device->UpdateSize; num_updates = device->NumUpdates; - while(!self->killNow) + while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire)) { int state = verify_state(self->pcmHandle); if(state < 0) { ERR("Invalid state detected: %s\n", snd_strerror(state)); ALCplaybackAlsa_lock(self); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Bad state: %s", snd_strerror(state)); ALCplaybackAlsa_unlock(self); break; } @@ -531,14 +587,14 @@ static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr) update_size = device->UpdateSize; num_updates = device->NumUpdates; - while(!self->killNow) + while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire)) { int state = verify_state(self->pcmHandle); if(state < 0) { ERR("Invalid state detected: %s\n", snd_strerror(state)); ALCplaybackAlsa_lock(self); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Bad state: %s", snd_strerror(state)); ALCplaybackAlsa_unlock(self); break; } @@ -629,12 +685,12 @@ static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name) if(VECTOR_SIZE(PlaybackDevices) == 0) probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices); -#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, name) == 0) +#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, name) == 0) VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME); #undef MATCH_NAME if(iter == VECTOR_END(PlaybackDevices)) return ALC_INVALID_VALUE; - driver = al_string_get_cstr(iter->device_name); + driver = alstr_get_cstr(iter->device_name); } else { @@ -653,16 +709,11 @@ static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name) /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */ snd_config_update_free_global(); - al_string_copy_cstr(&device->DeviceName, name); + alstr_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; } -static void ALCplaybackAlsa_close(ALCplaybackAlsa *self) -{ - snd_pcm_close(self->pcmHandle); -} - static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; @@ -704,7 +755,7 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self) break; } - allowmmap = GetConfigValueBool(al_string_get_cstr(device->DeviceName), "alsa", "mmap", 1); + allowmmap = GetConfigValueBool(alstr_get_cstr(device->DeviceName), "alsa", "mmap", 1); periods = device->NumUpdates; periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency; bufferLen = periodLen * periods; @@ -748,7 +799,7 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self) } CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format)); /* test and set channels (implicitly sets frame bits) */ - if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)) < 0) + if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder)) < 0) { static const enum DevFmtChannels channellist[] = { DevFmtStereo, @@ -761,16 +812,17 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self) for(k = 0;k < COUNTOF(channellist);k++) { - if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(channellist[k])) >= 0) + if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(channellist[k], 0)) >= 0) { device->FmtChans = channellist[k]; + device->AmbiOrder = 0; break; } } } - CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans))); + CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder))); /* set rate (implicitly constrains period/buffer parameters) */ - if(!GetConfigValueBool(al_string_get_cstr(device->DeviceName), "alsa", "allow-resampler", 0) || + if(!GetConfigValueBool(alstr_get_cstr(device->DeviceName), "alsa", "allow-resampler", 0) || !(device->Flags&DEVICE_FREQUENCY_REQUEST)) { if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 0) < 0) @@ -860,7 +912,7 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self) } thread_func = ALCplaybackAlsa_mixerProc; } - self->killNow = 0; + ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release); if(althrd_create(&self->thread, thread_func, self) != althrd_success) { ERR("Could not create playback thread\n"); @@ -881,10 +933,8 @@ static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self) { int res; - if(self->killNow) + if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel)) return; - - self->killNow = 1; althrd_join(self->thread, &res); al_free(self->buffer); @@ -928,9 +978,8 @@ typedef struct ALCcaptureAlsa { } ALCcaptureAlsa; static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device); -static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, Destruct) +static void ALCcaptureAlsa_Destruct(ALCcaptureAlsa *self); static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name); -static void ALCcaptureAlsa_close(ALCcaptureAlsa *self); static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, ALCboolean, reset) static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self); static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self); @@ -948,6 +997,25 @@ static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device) { ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); SET_VTABLE2(ALCcaptureAlsa, ALCbackend, self); + + self->pcmHandle = NULL; + self->buffer = NULL; + self->ring = NULL; +} + +void ALCcaptureAlsa_Destruct(ALCcaptureAlsa *self) +{ + if(self->pcmHandle) + snd_pcm_close(self->pcmHandle); + self->pcmHandle = NULL; + + al_free(self->buffer); + self->buffer = NULL; + + ll_ringbuffer_free(self->ring); + self->ring = NULL; + + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); } @@ -970,12 +1038,12 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name) if(VECTOR_SIZE(CaptureDevices) == 0) probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices); -#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, name) == 0) +#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, name) == 0) VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME); #undef MATCH_NAME if(iter == VECTOR_END(CaptureDevices)) return ALC_INVALID_VALUE; - driver = al_string_get_cstr(iter->device_name); + driver = alstr_get_cstr(iter->device_name); } else { @@ -1032,7 +1100,7 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name) /* set format (implicitly sets sample bits) */ CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format)); /* set channels (implicitly sets frame bits) */ - CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans))); + CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder))); /* set rate (implicitly constrains period/buffer parameters) */ CHECK(snd_pcm_hw_params_set_rate(self->pcmHandle, hp, device->Frequency, 0)); /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */ @@ -1055,8 +1123,9 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name) if(needring) { self->ring = ll_ringbuffer_create( - device->UpdateSize*device->NumUpdates + 1, - FrameSizeFromDevFmt(device->FmtChans, device->FmtType) + device->UpdateSize*device->NumUpdates, + FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder), + false ); if(!self->ring) { @@ -1065,7 +1134,7 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name) } } - al_string_copy_cstr(&device->DeviceName, name); + alstr_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; @@ -1077,26 +1146,19 @@ error2: ll_ringbuffer_free(self->ring); self->ring = NULL; snd_pcm_close(self->pcmHandle); + self->pcmHandle = NULL; return ALC_INVALID_VALUE; } -static void ALCcaptureAlsa_close(ALCcaptureAlsa *self) -{ - snd_pcm_close(self->pcmHandle); - ll_ringbuffer_free(self->ring); - - al_free(self->buffer); - self->buffer = NULL; -} - static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self) { int err = snd_pcm_start(self->pcmHandle); if(err < 0) { ERR("start failed: %s\n", snd_strerror(err)); - aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice); + aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice, "Capture state failure: %s", + snd_strerror(err)); return ALC_FALSE; } @@ -1147,7 +1209,7 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff } self->last_avail -= samples; - while(device->Connected && samples > 0) + while(ATOMIC_LOAD(&device->Connected, almemory_order_acquire) && samples > 0) { snd_pcm_sframes_t amt = 0; @@ -1190,7 +1252,7 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff if(amt < 0) { ERR("restore error: %s\n", snd_strerror(amt)); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Capture recovery failure: %s", snd_strerror(amt)); break; } /* If the amount available is less than what's asked, we lost it @@ -1215,7 +1277,7 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self) ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; snd_pcm_sframes_t avail = 0; - if(device->Connected && self->doCapture) + if(ATOMIC_LOAD(&device->Connected, almemory_order_acquire) && self->doCapture) avail = snd_pcm_avail_update(self->pcmHandle); if(avail < 0) { @@ -1231,7 +1293,7 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self) if(avail < 0) { ERR("restore error: %s\n", snd_strerror(avail)); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Capture recovery failure: %s", snd_strerror(avail)); } } @@ -1270,7 +1332,7 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self) if(amt < 0) { ERR("restore error: %s\n", snd_strerror(amt)); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Capture recovery failure: %s", snd_strerror(amt)); break; } avail = amt; @@ -1307,9 +1369,9 @@ static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self) static inline void AppendAllDevicesList2(const DevMap *entry) -{ AppendAllDevicesList(al_string_get_cstr(entry->name)); } +{ AppendAllDevicesList(alstr_get_cstr(entry->name)); } static inline void AppendCaptureDeviceList2(const DevMap *entry) -{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); } +{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); } typedef struct ALCalsaBackendFactory { DERIVE_FROM_TYPE(ALCbackendFactory); diff --git a/Engine/lib/openal-soft/Alc/backends/base.c b/Engine/lib/openal-soft/Alc/backends/base.c index ff808f535..a451fee9b 100644 --- a/Engine/lib/openal-soft/Alc/backends/base.c +++ b/Engine/lib/openal-soft/Alc/backends/base.c @@ -4,11 +4,14 @@ #include #include "alMain.h" +#include "alu.h" #include "backends/base.h" extern inline ALuint64 GetDeviceClockTime(ALCdevice *device); +extern inline void ALCdevice_Lock(ALCdevice *device); +extern inline void ALCdevice_Unlock(ALCdevice *device); /* Base ALCbackend method implementations. */ void ALCbackend_Construct(ALCbackend *self, ALCdevice *device) @@ -41,13 +44,22 @@ ALCuint ALCbackend_availableSamples(ALCbackend* UNUSED(self)) ClockLatency ALCbackend_getClockLatency(ALCbackend *self) { ALCdevice *device = self->mDevice; + ALuint refcount; ClockLatency ret; - almtx_lock(&self->mMutex); - ret.ClockTime = GetDeviceClockTime(device); - // TODO: Perhaps should be NumUpdates-1 worth of UpdateSize? - ret.Latency = 0; - almtx_unlock(&self->mMutex); + do { + while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1)) + althrd_yield(); + ret.ClockTime = GetDeviceClockTime(device); + ATOMIC_THREAD_FENCE(almemory_order_acquire); + } while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_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 = device->UpdateSize * DEVICE_CLOCK_RES / device->Frequency * + maxu(device->NumUpdates-1, 1); return ret; } @@ -69,157 +81,3 @@ void ALCbackend_unlock(ALCbackend *self) void ALCbackendFactory_deinit(ALCbackendFactory* UNUSED(self)) { } - - -/* Wrappers to use an old-style backend with the new interface. */ -typedef struct PlaybackWrapper { - DERIVE_FROM_TYPE(ALCbackend); - - const BackendFuncs *Funcs; -} PlaybackWrapper; - -static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs); -static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, Destruct) -static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name); -static void PlaybackWrapper_close(PlaybackWrapper *self); -static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self); -static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self); -static void PlaybackWrapper_stop(PlaybackWrapper *self); -static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint) -static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples) -static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ClockLatency, getClockLatency) -static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock) -static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock) -DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper) -DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper); - -static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs) -{ - ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); - SET_VTABLE2(PlaybackWrapper, ALCbackend, self); - - self->Funcs = funcs; -} - -static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name) -{ - ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - return self->Funcs->OpenPlayback(device, name); -} - -static void PlaybackWrapper_close(PlaybackWrapper *self) -{ - ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - self->Funcs->ClosePlayback(device); -} - -static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self) -{ - ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - return self->Funcs->ResetPlayback(device); -} - -static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self) -{ - ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - return self->Funcs->StartPlayback(device); -} - -static void PlaybackWrapper_stop(PlaybackWrapper *self) -{ - ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - self->Funcs->StopPlayback(device); -} - - -typedef struct CaptureWrapper { - DERIVE_FROM_TYPE(ALCbackend); - - const BackendFuncs *Funcs; -} CaptureWrapper; - -static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs); -static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, Destruct) -static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name); -static void CaptureWrapper_close(CaptureWrapper *self); -static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset) -static ALCboolean CaptureWrapper_start(CaptureWrapper *self); -static void CaptureWrapper_stop(CaptureWrapper *self); -static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples); -static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self); -static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ClockLatency, getClockLatency) -static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock) -static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock) -DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper) -DEFINE_ALCBACKEND_VTABLE(CaptureWrapper); - -static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs) -{ - ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); - SET_VTABLE2(CaptureWrapper, ALCbackend, self); - - self->Funcs = funcs; -} - -static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name) -{ - ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - return self->Funcs->OpenCapture(device, name); -} - -static void CaptureWrapper_close(CaptureWrapper *self) -{ - ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - self->Funcs->CloseCapture(device); -} - -static ALCboolean CaptureWrapper_start(CaptureWrapper *self) -{ - ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - self->Funcs->StartCapture(device); - return ALC_TRUE; -} - -static void CaptureWrapper_stop(CaptureWrapper *self) -{ - ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - self->Funcs->StopCapture(device); -} - -static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples) -{ - ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - return self->Funcs->CaptureSamples(device, buffer, samples); -} - -static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self) -{ - ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - return self->Funcs->AvailableSamples(device); -} - - -ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type) -{ - if(type == ALCbackend_Playback) - { - PlaybackWrapper *backend; - - NEW_OBJ(backend, PlaybackWrapper)(device, funcs); - if(!backend) return NULL; - - return STATIC_CAST(ALCbackend, backend); - } - - if(type == ALCbackend_Capture) - { - CaptureWrapper *backend; - - NEW_OBJ(backend, CaptureWrapper)(device, funcs); - if(!backend) return NULL; - - return STATIC_CAST(ALCbackend, backend); - } - - return NULL; -} diff --git a/Engine/lib/openal-soft/Alc/backends/base.h b/Engine/lib/openal-soft/Alc/backends/base.h index 04795b368..ba92b4ac6 100644 --- a/Engine/lib/openal-soft/Alc/backends/base.h +++ b/Engine/lib/openal-soft/Alc/backends/base.h @@ -5,6 +5,10 @@ #include "threads.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct ClockLatency { ALint64 ClockTime; ALint64 Latency; @@ -43,7 +47,6 @@ struct ALCbackendVtable { void (*const Destruct)(ALCbackend*); ALCenum (*const open)(ALCbackend*, const ALCchar*); - void (*const close)(ALCbackend*); ALCboolean (*const reset)(ALCbackend*); ALCboolean (*const start)(ALCbackend*); @@ -63,7 +66,6 @@ struct ALCbackendVtable { #define DEFINE_ALCBACKEND_VTABLE(T) \ DECLARE_THUNK(T, ALCbackend, void, Destruct) \ DECLARE_THUNK1(T, ALCbackend, ALCenum, open, const ALCchar*) \ -DECLARE_THUNK(T, ALCbackend, void, close) \ DECLARE_THUNK(T, ALCbackend, ALCboolean, reset) \ DECLARE_THUNK(T, ALCbackend, ALCboolean, start) \ DECLARE_THUNK(T, ALCbackend, void, stop) \ @@ -79,7 +81,6 @@ static const struct ALCbackendVtable T##_ALCbackend_vtable = { \ T##_ALCbackend_Destruct, \ \ T##_ALCbackend_open, \ - T##_ALCbackend_close, \ T##_ALCbackend_reset, \ T##_ALCbackend_start, \ T##_ALCbackend_stop, \ @@ -137,17 +138,31 @@ static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \ ALCbackendFactory *ALCpulseBackendFactory_getFactory(void); ALCbackendFactory *ALCalsaBackendFactory_getFactory(void); +ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void); ALCbackendFactory *ALCossBackendFactory_getFactory(void); ALCbackendFactory *ALCjackBackendFactory_getFactory(void); ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void); -ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void); +ALCbackendFactory *ALCsndioBackendFactory_getFactory(void); +ALCbackendFactory *ALCqsaBackendFactory_getFactory(void); +ALCbackendFactory *ALCwasapiBackendFactory_getFactory(void); ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void); ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void); ALCbackendFactory *ALCportBackendFactory_getFactory(void); +ALCbackendFactory *ALCopenslBackendFactory_getFactory(void); ALCbackendFactory *ALCnullBackendFactory_getFactory(void); ALCbackendFactory *ALCwaveBackendFactory_getFactory(void); +ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void); ALCbackendFactory *ALCloopbackFactory_getFactory(void); -ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type); + +inline void ALCdevice_Lock(ALCdevice *device) +{ V0(device->Backend,lock)(); } + +inline void ALCdevice_Unlock(ALCdevice *device) +{ V0(device->Backend,unlock)(); } + +#ifdef __cplusplus +} /* extern "C" */ +#endif #endif /* AL_BACKENDS_BASE_H */ diff --git a/Engine/lib/openal-soft/Alc/backends/coreaudio.c b/Engine/lib/openal-soft/Alc/backends/coreaudio.c index 24aeabd42..a8787f7b0 100644 --- a/Engine/lib/openal-soft/Alc/backends/coreaudio.c +++ b/Engine/lib/openal-soft/Alc/backends/coreaudio.c @@ -23,128 +23,91 @@ #include #include #include -#include #include "alMain.h" #include "alu.h" +#include "ringbuffer.h" #include #include #include #include +#include "backends/base.h" -typedef struct { - AudioUnit audioUnit; - - ALuint frameSize; - ALdouble sampleRateRatio; // Ratio of hardware sample rate / requested sample rate - AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD - - AudioConverterRef audioConverter; // Sample rate converter if needed - AudioBufferList *bufferList; // Buffer for data coming from the input device - ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling - - ll_ringbuffer_t *ring; -} ca_data; static const ALCchar ca_device[] = "CoreAudio Default"; -static void destroy_buffer_list(AudioBufferList* list) +typedef struct ALCcoreAudioPlayback { + DERIVE_FROM_TYPE(ALCbackend); + + AudioUnit audioUnit; + + ALuint frameSize; + AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD +} ALCcoreAudioPlayback; + +static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice *device); +static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self); +static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCchar *name); +static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self); +static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self); +static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self); +static DECLARE_FORWARD2(ALCcoreAudioPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint) +static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, ALCuint, availableSamples) +static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, ClockLatency, getClockLatency) +static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCcoreAudioPlayback) + +DEFINE_ALCBACKEND_VTABLE(ALCcoreAudioPlayback); + + +static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice *device) { - if(list) - { - UInt32 i; - for(i = 0;i < list->mNumberBuffers;i++) - free(list->mBuffers[i].mData); - free(list); - } + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCcoreAudioPlayback, ALCbackend, self); + + self->frameSize = 0; + memset(&self->format, 0, sizeof(self->format)); } -static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSize) +static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self) { - AudioBufferList *list; + AudioUnitUninitialize(self->audioUnit); + AudioComponentInstanceDispose(self->audioUnit); - list = calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer)); - if(list) - { - list->mNumberBuffers = 1; - - list->mBuffers[0].mNumberChannels = channelCount; - list->mBuffers[0].mDataByteSize = byteSize; - list->mBuffers[0].mData = malloc(byteSize); - if(list->mBuffers[0].mData == NULL) - { - free(list); - list = NULL; - } - } - return list; + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); } -static OSStatus ca_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) -{ - ALCdevice *device = (ALCdevice*)inRefCon; - ca_data *data = (ca_data*)device->ExtraData; +static OSStatus ALCcoreAudioPlayback_MixerProc(void *inRefCon, + AudioUnitRenderActionFlags* UNUSED(ioActionFlags), const AudioTimeStamp* UNUSED(inTimeStamp), + UInt32 UNUSED(inBusNumber), UInt32 UNUSED(inNumberFrames), AudioBufferList *ioData) +{ + ALCcoreAudioPlayback *self = inRefCon; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; + + ALCcoreAudioPlayback_lock(self); aluMixData(device, ioData->mBuffers[0].mData, - ioData->mBuffers[0].mDataByteSize / data->frameSize); + ioData->mBuffers[0].mDataByteSize / self->frameSize); + ALCcoreAudioPlayback_unlock(self); return noErr; } -static OSStatus ca_capture_conversion_callback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, - AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void* inUserData) -{ - ALCdevice *device = (ALCdevice*)inUserData; - ca_data *data = (ca_data*)device->ExtraData; - - // Read from the ring buffer and store temporarily in a large buffer - ll_ringbuffer_read(data->ring, data->resampleBuffer, *ioNumberDataPackets); - - // Set the input data - ioData->mNumberBuffers = 1; - ioData->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame; - ioData->mBuffers[0].mData = data->resampleBuffer; - ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * data->format.mBytesPerFrame; - - return noErr; -} - -static OSStatus ca_capture_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, - UInt32 inNumberFrames, AudioBufferList *ioData) -{ - ALCdevice *device = (ALCdevice*)inRefCon; - ca_data *data = (ca_data*)device->ExtraData; - AudioUnitRenderActionFlags flags = 0; - OSStatus err; - - // fill the bufferList with data from the input device - err = AudioUnitRender(data->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, data->bufferList); - if(err != noErr) - { - ERR("AudioUnitRender error: %d\n", err); - return err; - } - - ll_ringbuffer_write(data->ring, data->bufferList->mBuffers[0].mData, inNumberFrames); - - return noErr; -} - -static ALCenum ca_open_playback(ALCdevice *device, const ALCchar *deviceName) + +static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCchar *name) { + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; AudioComponentDescription desc; AudioComponent comp; - ca_data *data; OSStatus err; - if(!deviceName) - deviceName = ca_device; - else if(strcmp(deviceName, ca_device) != 0) + if(!name) + name = ca_device; + else if(strcmp(name, ca_device) != 0) return ALC_INVALID_VALUE; /* open the default output unit */ @@ -161,57 +124,41 @@ static ALCenum ca_open_playback(ALCdevice *device, const ALCchar *deviceName) return ALC_INVALID_VALUE; } - data = calloc(1, sizeof(*data)); - - err = AudioComponentInstanceNew(comp, &data->audioUnit); + err = AudioComponentInstanceNew(comp, &self->audioUnit); if(err != noErr) { ERR("AudioComponentInstanceNew failed\n"); - free(data); return ALC_INVALID_VALUE; } /* init and start the default audio unit... */ - err = AudioUnitInitialize(data->audioUnit); + err = AudioUnitInitialize(self->audioUnit); if(err != noErr) { ERR("AudioUnitInitialize failed\n"); - AudioComponentInstanceDispose(data->audioUnit); - free(data); + AudioComponentInstanceDispose(self->audioUnit); return ALC_INVALID_VALUE; } - al_string_copy_cstr(&device->DeviceName, deviceName); - device->ExtraData = data; + alstr_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; } -static void ca_close_playback(ALCdevice *device) +static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self) { - ca_data *data = (ca_data*)device->ExtraData; - - AudioUnitUninitialize(data->audioUnit); - AudioComponentInstanceDispose(data->audioUnit); - - free(data); - device->ExtraData = NULL; -} - -static ALCboolean ca_reset_playback(ALCdevice *device) -{ - ca_data *data = (ca_data*)device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; AudioStreamBasicDescription streamFormat; AURenderCallbackStruct input; OSStatus err; UInt32 size; - err = AudioUnitUninitialize(data->audioUnit); + err = AudioUnitUninitialize(self->audioUnit); if(err != noErr) ERR("-- AudioUnitUninitialize failed.\n"); /* retrieve default output unit's properties (output side) */ size = sizeof(AudioStreamBasicDescription); - err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size); + err = AudioUnitGetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size); if(err != noErr || size != sizeof(AudioStreamBasicDescription)) { ERR("AudioUnitGetProperty failed\n"); @@ -229,7 +176,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device) #endif /* set default output unit's input side to match output side */ - err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size); + err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size); if(err != noErr) { ERR("AudioUnitSetProperty failed\n"); @@ -313,7 +260,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device) streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian | kLinearPCMFormatFlagIsPacked; - err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription)); + err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription)); if(err != noErr) { ERR("AudioUnitSetProperty failed\n"); @@ -321,11 +268,11 @@ static ALCboolean ca_reset_playback(ALCdevice *device) } /* setup callback */ - data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); - input.inputProc = ca_callback; - input.inputProcRefCon = device; + self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); + input.inputProc = ALCcoreAudioPlayback_MixerProc; + input.inputProcRefCon = self; - err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct)); + err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct)); if(err != noErr) { ERR("AudioUnitSetProperty failed\n"); @@ -333,7 +280,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device) } /* init the default audio unit... */ - err = AudioUnitInitialize(data->audioUnit); + err = AudioUnitInitialize(self->audioUnit); if(err != noErr) { ERR("AudioUnitInitialize failed\n"); @@ -343,12 +290,9 @@ static ALCboolean ca_reset_playback(ALCdevice *device) return ALC_TRUE; } -static ALCboolean ca_start_playback(ALCdevice *device) +static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self) { - ca_data *data = (ca_data*)device->ExtraData; - OSStatus err; - - err = AudioOutputUnitStart(data->audioUnit); + OSStatus err = AudioOutputUnitStart(self->audioUnit); if(err != noErr) { ERR("AudioOutputUnitStart failed\n"); @@ -358,18 +302,150 @@ static ALCboolean ca_start_playback(ALCdevice *device) return ALC_TRUE; } -static void ca_stop_playback(ALCdevice *device) +static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self) { - ca_data *data = (ca_data*)device->ExtraData; - OSStatus err; - - err = AudioOutputUnitStop(data->audioUnit); + OSStatus err = AudioOutputUnitStop(self->audioUnit); if(err != noErr) ERR("AudioOutputUnitStop failed\n"); } -static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName) + + + +typedef struct ALCcoreAudioCapture { + DERIVE_FROM_TYPE(ALCbackend); + + AudioUnit audioUnit; + + ALuint frameSize; + ALdouble sampleRateRatio; // Ratio of hardware sample rate / requested sample rate + AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD + + AudioConverterRef audioConverter; // Sample rate converter if needed + AudioBufferList *bufferList; // Buffer for data coming from the input device + ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling + + ll_ringbuffer_t *ring; +} ALCcoreAudioCapture; + +static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device); +static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self); +static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar *name); +static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, ALCboolean, reset) +static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self); +static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self); +static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples); +static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self); +static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, ClockLatency, getClockLatency) +static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCcoreAudioCapture) + +DEFINE_ALCBACKEND_VTABLE(ALCcoreAudioCapture); + + +static AudioBufferList *allocate_buffer_list(UInt32 channelCount, UInt32 byteSize) { + AudioBufferList *list; + + list = calloc(1, FAM_SIZE(AudioBufferList, mBuffers, 1) + byteSize); + if(list) + { + list->mNumberBuffers = 1; + + list->mBuffers[0].mNumberChannels = channelCount; + list->mBuffers[0].mDataByteSize = byteSize; + list->mBuffers[0].mData = &list->mBuffers[1]; + } + return list; +} + +static void destroy_buffer_list(AudioBufferList *list) +{ + free(list); +} + + +static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCcoreAudioCapture, ALCbackend, self); + + self->audioUnit = 0; + self->audioConverter = NULL; + self->bufferList = NULL; + self->resampleBuffer = NULL; + self->ring = NULL; +} + +static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self) +{ + ll_ringbuffer_free(self->ring); + self->ring = NULL; + + free(self->resampleBuffer); + self->resampleBuffer = NULL; + + destroy_buffer_list(self->bufferList); + self->bufferList = NULL; + + if(self->audioConverter) + AudioConverterDispose(self->audioConverter); + self->audioConverter = NULL; + + if(self->audioUnit) + AudioComponentInstanceDispose(self->audioUnit); + self->audioUnit = 0; + + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); +} + + +static OSStatus ALCcoreAudioCapture_RecordProc(void *inRefCon, + AudioUnitRenderActionFlags* UNUSED(ioActionFlags), + const AudioTimeStamp *inTimeStamp, UInt32 UNUSED(inBusNumber), + UInt32 inNumberFrames, AudioBufferList* UNUSED(ioData)) +{ + ALCcoreAudioCapture *self = inRefCon; + AudioUnitRenderActionFlags flags = 0; + OSStatus err; + + // fill the bufferList with data from the input device + err = AudioUnitRender(self->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, self->bufferList); + if(err != noErr) + { + ERR("AudioUnitRender error: %d\n", err); + return err; + } + + ll_ringbuffer_write(self->ring, self->bufferList->mBuffers[0].mData, inNumberFrames); + + return noErr; +} + +static OSStatus ALCcoreAudioCapture_ConvertCallback(AudioConverterRef UNUSED(inAudioConverter), + UInt32 *ioNumberDataPackets, AudioBufferList *ioData, + AudioStreamPacketDescription** UNUSED(outDataPacketDescription), + void *inUserData) +{ + ALCcoreAudioCapture *self = inUserData; + + // Read from the ring buffer and store temporarily in a large buffer + ll_ringbuffer_read(self->ring, self->resampleBuffer, *ioNumberDataPackets); + + // Set the input data + ioData->mNumberBuffers = 1; + ioData->mBuffers[0].mNumberChannels = self->format.mChannelsPerFrame; + ioData->mBuffers[0].mData = self->resampleBuffer; + ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * self->format.mBytesPerFrame; + + return noErr; +} + + +static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar *name) +{ + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; AudioStreamBasicDescription requestedFormat; // The application requested format AudioStreamBasicDescription hardwareFormat; // The hardware format AudioStreamBasicDescription outputFormat; // The AudioUnit output format @@ -381,12 +457,11 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName) AudioObjectPropertyAddress propertyAddress; UInt32 enableIO; AudioComponent comp; - ca_data *data; OSStatus err; - if(!deviceName) - deviceName = ca_device; - else if(strcmp(deviceName, ca_device) != 0) + if(!name) + name = ca_device; + else if(strcmp(name, ca_device) != 0) return ALC_INVALID_VALUE; desc.componentType = kAudioUnitType_Output; @@ -403,11 +478,8 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName) return ALC_INVALID_VALUE; } - data = calloc(1, sizeof(*data)); - device->ExtraData = data; - // Open the component - err = AudioComponentInstanceNew(comp, &data->audioUnit); + err = AudioComponentInstanceNew(comp, &self->audioUnit); if(err != noErr) { ERR("AudioComponentInstanceNew failed\n"); @@ -416,7 +488,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName) // Turn off AudioUnit output enableIO = 0; - err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint)); + err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint)); if(err != noErr) { ERR("AudioUnitSetProperty failed\n"); @@ -425,7 +497,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName) // Turn on AudioUnit input enableIO = 1; - err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint)); + err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint)); if(err != noErr) { ERR("AudioUnitSetProperty failed\n"); @@ -453,7 +525,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName) } // Track the input device - err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID)); + err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID)); if(err != noErr) { ERR("AudioUnitSetProperty failed\n"); @@ -461,10 +533,10 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName) } // set capture callback - input.inputProc = ca_capture_callback; - input.inputProcRefCon = device; + input.inputProc = ALCcoreAudioCapture_RecordProc; + input.inputProcRefCon = self; - err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct)); + err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct)); if(err != noErr) { ERR("AudioUnitSetProperty failed\n"); @@ -472,7 +544,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName) } // Initialize the device - err = AudioUnitInitialize(data->audioUnit); + err = AudioUnitInitialize(self->audioUnit); if(err != noErr) { ERR("AudioUnitInitialize failed\n"); @@ -481,7 +553,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName) // Get the hardware format propertySize = sizeof(AudioStreamBasicDescription); - err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize); + err = AudioUnitGetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize); if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription)) { ERR("AudioUnitGetProperty failed\n"); @@ -528,9 +600,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName) case DevFmtX51Rear: case DevFmtX61: case DevFmtX71: - case DevFmtAmbi1: - case DevFmtAmbi2: - case DevFmtAmbi3: + case DevFmtAmbi3D: ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans)); goto error; } @@ -543,8 +613,8 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName) requestedFormat.mFramesPerPacket = 1; // save requested format description for later use - data->format = requestedFormat; - data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + self->format = requestedFormat; + self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); // Use intermediate format for sample rate conversion (outputFormat) // Set sample rate to the same as hardware for resampling later @@ -552,11 +622,11 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName) outputFormat.mSampleRate = hardwareFormat.mSampleRate; // Determine sample rate ratio for resampling - data->sampleRateRatio = outputFormat.mSampleRate / device->Frequency; + self->sampleRateRatio = outputFormat.mSampleRate / device->Frequency; // The output format should be the requested format, but using the hardware sample rate // This is because the AudioUnit will automatically scale other properties, except for sample rate - err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat)); + err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat)); if(err != noErr) { ERR("AudioUnitSetProperty failed\n"); @@ -564,8 +634,8 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName) } // Set the AudioUnit output format frame count - outputFrameCount = device->UpdateSize * data->sampleRateRatio; - err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount)); + outputFrameCount = device->UpdateSize * self->sampleRateRatio; + err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount)); if(err != noErr) { ERR("AudioUnitSetProperty failed: %d\n", err); @@ -573,7 +643,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName) } // Set up sample converter - err = AudioConverterNew(&outputFormat, &requestedFormat, &data->audioConverter); + err = AudioConverterNew(&outputFormat, &requestedFormat, &self->audioConverter); if(err != noErr) { ERR("AudioConverterNew failed: %d\n", err); @@ -581,96 +651,83 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName) } // Create a buffer for use in the resample callback - data->resampleBuffer = malloc(device->UpdateSize * data->frameSize * data->sampleRateRatio); + self->resampleBuffer = malloc(device->UpdateSize * self->frameSize * self->sampleRateRatio); // Allocate buffer for the AudioUnit output - data->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * data->frameSize * data->sampleRateRatio); - if(data->bufferList == NULL) + self->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * self->frameSize * self->sampleRateRatio); + if(self->bufferList == NULL) goto error; - data->ring = ll_ringbuffer_create( - device->UpdateSize*data->sampleRateRatio*device->NumUpdates + 1, - data->frameSize + self->ring = ll_ringbuffer_create( + (size_t)ceil(device->UpdateSize*self->sampleRateRatio*device->NumUpdates), + self->frameSize, false ); - if(!data->ring) goto error; + if(!self->ring) goto error; - al_string_copy_cstr(&device->DeviceName, deviceName); + alstr_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; error: - ll_ringbuffer_free(data->ring); - data->ring = NULL; - free(data->resampleBuffer); - destroy_buffer_list(data->bufferList); + ll_ringbuffer_free(self->ring); + self->ring = NULL; + free(self->resampleBuffer); + self->resampleBuffer = NULL; + destroy_buffer_list(self->bufferList); + self->bufferList = NULL; - if(data->audioConverter) - AudioConverterDispose(data->audioConverter); - if(data->audioUnit) - AudioComponentInstanceDispose(data->audioUnit); - - free(data); - device->ExtraData = NULL; + if(self->audioConverter) + AudioConverterDispose(self->audioConverter); + self->audioConverter = NULL; + if(self->audioUnit) + AudioComponentInstanceDispose(self->audioUnit); + self->audioUnit = 0; return ALC_INVALID_VALUE; } -static void ca_close_capture(ALCdevice *device) + +static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self) { - ca_data *data = (ca_data*)device->ExtraData; - - ll_ringbuffer_free(data->ring); - data->ring = NULL; - free(data->resampleBuffer); - destroy_buffer_list(data->bufferList); - - AudioConverterDispose(data->audioConverter); - AudioComponentInstanceDispose(data->audioUnit); - - free(data); - device->ExtraData = NULL; -} - -static void ca_start_capture(ALCdevice *device) -{ - ca_data *data = (ca_data*)device->ExtraData; - OSStatus err = AudioOutputUnitStart(data->audioUnit); + OSStatus err = AudioOutputUnitStart(self->audioUnit); if(err != noErr) + { ERR("AudioOutputUnitStart failed\n"); + return ALC_FALSE; + } + return ALC_TRUE; } -static void ca_stop_capture(ALCdevice *device) +static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self) { - ca_data *data = (ca_data*)device->ExtraData; - OSStatus err = AudioOutputUnitStop(data->audioUnit); + OSStatus err = AudioOutputUnitStop(self->audioUnit); if(err != noErr) ERR("AudioOutputUnitStop failed\n"); } -static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) +static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples) { - ca_data *data = (ca_data*)device->ExtraData; - AudioBufferList *list; + union { + ALbyte _[sizeof(AudioBufferList) + sizeof(AudioBuffer)]; + AudioBufferList list; + } audiobuf = { { 0 } }; UInt32 frameCount; OSStatus err; // If no samples are requested, just return - if(samples == 0) - return ALC_NO_ERROR; - - // Allocate a temporary AudioBufferList to use as the return resamples data - list = alloca(sizeof(AudioBufferList) + sizeof(AudioBuffer)); + if(samples == 0) return ALC_NO_ERROR; // Point the resampling buffer to the capture buffer - list->mNumberBuffers = 1; - list->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame; - list->mBuffers[0].mDataByteSize = samples * data->frameSize; - list->mBuffers[0].mData = buffer; + audiobuf.list.mNumberBuffers = 1; + audiobuf.list.mBuffers[0].mNumberChannels = self->format.mChannelsPerFrame; + audiobuf.list.mBuffers[0].mDataByteSize = samples * self->frameSize; + audiobuf.list.mBuffers[0].mData = buffer; // Resample into another AudioBufferList frameCount = samples; - err = AudioConverterFillComplexBuffer(data->audioConverter, ca_capture_conversion_callback, - device, &frameCount, list, NULL); + err = AudioConverterFillComplexBuffer(self->audioConverter, + ALCcoreAudioCapture_ConvertCallback, self, &frameCount, &audiobuf.list, NULL + ); if(err != noErr) { ERR("AudioConverterFillComplexBuffer error: %d\n", err); @@ -679,38 +736,47 @@ static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint sa return ALC_NO_ERROR; } -static ALCuint ca_available_samples(ALCdevice *device) +static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self) { - ca_data *data = device->ExtraData; - return ll_ringbuffer_read_space(data->ring) / data->sampleRateRatio; + return ll_ringbuffer_read_space(self->ring) / self->sampleRateRatio; } -static const BackendFuncs ca_funcs = { - ca_open_playback, - ca_close_playback, - ca_reset_playback, - ca_start_playback, - ca_stop_playback, - ca_open_capture, - ca_close_capture, - ca_start_capture, - ca_stop_capture, - ca_capture_samples, - ca_available_samples -}; +typedef struct ALCcoreAudioBackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCcoreAudioBackendFactory; +#define ALCCOREAUDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCcoreAudioBackendFactory, ALCbackendFactory) } } -ALCboolean alc_ca_init(BackendFuncs *func_list) +ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void); + +static ALCboolean ALCcoreAudioBackendFactory_init(ALCcoreAudioBackendFactory *self); +static DECLARE_FORWARD(ALCcoreAudioBackendFactory, ALCbackendFactory, void, deinit) +static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFactory *self, ALCbackend_Type type); +static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory *self, enum DevProbe type); +static ALCbackend* ALCcoreAudioBackendFactory_createBackend(ALCcoreAudioBackendFactory *self, ALCdevice *device, ALCbackend_Type type); +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCcoreAudioBackendFactory); + + +ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void) +{ + static ALCcoreAudioBackendFactory factory = ALCCOREAUDIOBACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); +} + + +static ALCboolean ALCcoreAudioBackendFactory_init(ALCcoreAudioBackendFactory* UNUSED(self)) { - *func_list = ca_funcs; return ALC_TRUE; } -void alc_ca_deinit(void) +static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFactory* UNUSED(self), ALCbackend_Type type) { + if(type == ALCbackend_Playback || ALCbackend_Capture) + return ALC_TRUE; + return ALC_FALSE; } -void alc_ca_probe(enum DevProbe type) +static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory* UNUSED(self), enum DevProbe type) { switch(type) { @@ -722,3 +788,23 @@ void alc_ca_probe(enum DevProbe type) break; } } + +static ALCbackend* ALCcoreAudioBackendFactory_createBackend(ALCcoreAudioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + ALCcoreAudioPlayback *backend; + NEW_OBJ(backend, ALCcoreAudioPlayback)(device); + if(!backend) return NULL; + return STATIC_CAST(ALCbackend, backend); + } + if(type == ALCbackend_Capture) + { + ALCcoreAudioCapture *backend; + NEW_OBJ(backend, ALCcoreAudioCapture)(device); + if(!backend) return NULL; + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} diff --git a/Engine/lib/openal-soft/Alc/backends/dsound.c b/Engine/lib/openal-soft/Alc/backends/dsound.c index bb38d516b..6bab641c7 100644 --- a/Engine/lib/openal-soft/Alc/backends/dsound.c +++ b/Engine/lib/openal-soft/Alc/backends/dsound.c @@ -34,6 +34,7 @@ #include "alMain.h" #include "alu.h" +#include "ringbuffer.h" #include "threads.h" #include "compat.h" #include "alstring.h" @@ -145,16 +146,16 @@ static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHA { const DevMap *iter; - al_string_copy_cstr(&entry.name, DEVNAME_HEAD); - al_string_append_wcstr(&entry.name, desc); + alstr_copy_cstr(&entry.name, DEVNAME_HEAD); + alstr_append_wcstr(&entry.name, desc); if(count != 0) { char str[64]; snprintf(str, sizeof(str), " #%d", count+1); - al_string_append_cstr(&entry.name, str); + alstr_append_cstr(&entry.name, str); } -#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0) +#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0) VECTOR_FIND_IF(iter, const DevMap, *devices, MATCH_ENTRY); if(iter == VECTOR_END(*devices)) break; #undef MATCH_ENTRY @@ -165,7 +166,7 @@ static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHA hr = StringFromCLSID(guid, &guidstr); if(SUCCEEDED(hr)) { - TRACE("Got device \"%s\", GUID \"%ls\"\n", al_string_get_cstr(entry.name), guidstr); + TRACE("Got device \"%s\", GUID \"%ls\"\n", alstr_get_cstr(entry.name), guidstr); CoTaskMemFree(guidstr); } @@ -184,16 +185,15 @@ typedef struct ALCdsoundPlayback { IDirectSoundNotify *Notifies; HANDLE NotifyEvent; - volatile int killNow; + ATOMIC(ALenum) killNow; althrd_t thread; } ALCdsoundPlayback; static int ALCdsoundPlayback_mixerProc(void *ptr); static void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *device); -static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, Destruct) +static void ALCdsoundPlayback_Destruct(ALCdsoundPlayback *self); static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *name); -static void ALCdsoundPlayback_close(ALCdsoundPlayback *self); static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self); static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self); static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self); @@ -211,6 +211,35 @@ static void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *devi { ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); SET_VTABLE2(ALCdsoundPlayback, ALCbackend, self); + + self->DS = NULL; + self->PrimaryBuffer = NULL; + self->Buffer = NULL; + self->Notifies = NULL; + self->NotifyEvent = NULL; + ATOMIC_INIT(&self->killNow, AL_TRUE); +} + +static void ALCdsoundPlayback_Destruct(ALCdsoundPlayback *self) +{ + if(self->Notifies) + IDirectSoundNotify_Release(self->Notifies); + self->Notifies = NULL; + if(self->Buffer) + IDirectSoundBuffer_Release(self->Buffer); + self->Buffer = NULL; + if(self->PrimaryBuffer != NULL) + IDirectSoundBuffer_Release(self->PrimaryBuffer); + self->PrimaryBuffer = NULL; + + if(self->DS) + IDirectSound_Release(self->DS); + self->DS = NULL; + if(self->NotifyEvent) + CloseHandle(self->NotifyEvent); + self->NotifyEvent = NULL; + + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); } @@ -239,16 +268,17 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr) { ERR("Failed to get buffer caps: 0x%lx\n", err); ALCdevice_Lock(device); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Failure retrieving playback buffer info: 0x%lx", err); ALCdevice_Unlock(device); return 1; } - FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); FragSize = device->UpdateSize * FrameSize; IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &LastCursor, NULL); - while(!self->killNow) + while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) && + ATOMIC_LOAD(&device->Connected, almemory_order_acquire)) { // Get current play cursor IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &PlayCursor, NULL); @@ -263,7 +293,7 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr) { ERR("Failed to play buffer: 0x%lx\n", err); ALCdevice_Lock(device); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Failure starting playback: 0x%lx", err); ALCdevice_Unlock(device); return 1; } @@ -299,8 +329,10 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr) if(SUCCEEDED(err)) { // If we have an active context, mix data directly into output buffer otherwise fill with silence + ALCdevice_Lock(device); aluMixData(device, WritePtr1, WriteCnt1/FrameSize); aluMixData(device, WritePtr2, WriteCnt2/FrameSize); + ALCdevice_Unlock(device); // Unlock output buffer only when successfully locked IDirectSoundBuffer_Unlock(self->Buffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2); @@ -309,7 +341,7 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr) { ERR("Buffer lock error: %#lx\n", err); ALCdevice_Lock(device); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Failed to lock output buffer: 0x%lx", err); ALCdevice_Unlock(device); return 1; } @@ -341,14 +373,14 @@ static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *de if(!deviceName && VECTOR_SIZE(PlaybackDevices) > 0) { - deviceName = al_string_get_cstr(VECTOR_FRONT(PlaybackDevices).name); + deviceName = alstr_get_cstr(VECTOR_FRONT(PlaybackDevices).name); guid = &VECTOR_FRONT(PlaybackDevices).guid; } else { const DevMap *iter; -#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0) +#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0) VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME); #undef MATCH_NAME if(iter == VECTOR_END(PlaybackDevices)) @@ -379,29 +411,11 @@ static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *de return ALC_INVALID_VALUE; } - al_string_copy_cstr(&device->DeviceName, deviceName); + alstr_copy_cstr(&device->DeviceName, deviceName); return ALC_NO_ERROR; } -static void ALCdsoundPlayback_close(ALCdsoundPlayback *self) -{ - if(self->Notifies) - IDirectSoundNotify_Release(self->Notifies); - self->Notifies = NULL; - if(self->Buffer) - IDirectSoundBuffer_Release(self->Buffer); - self->Buffer = NULL; - if(self->PrimaryBuffer != NULL) - IDirectSoundBuffer_Release(self->PrimaryBuffer); - self->PrimaryBuffer = NULL; - - IDirectSound_Release(self->DS); - self->DS = NULL; - CloseHandle(self->NotifyEvent); - self->NotifyEvent = NULL; -} - static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; @@ -472,9 +486,7 @@ static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self) case DevFmtMono: OutputType.dwChannelMask = SPEAKER_FRONT_CENTER; break; - case DevFmtAmbi1: - case DevFmtAmbi2: - case DevFmtAmbi3: + case DevFmtAmbi3D: device->FmtChans = DevFmtStereo; /*fall-through*/ case DevFmtStereo: @@ -527,7 +539,7 @@ static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self) retry_open: hr = S_OK; OutputType.Format.wFormatTag = WAVE_FORMAT_PCM; - OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans); + OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8; OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8; OutputType.Format.nSamplesPerSec = device->Frequency; @@ -626,7 +638,7 @@ retry_open: static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self) { - self->killNow = 0; + ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release); if(althrd_create(&self->thread, ALCdsoundPlayback_mixerProc, self) != althrd_success) return ALC_FALSE; @@ -637,10 +649,8 @@ static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self) { int res; - if(self->killNow) + if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel)) return; - - self->killNow = 1; althrd_join(self->thread, &res); IDirectSoundBuffer_Stop(self->Buffer); @@ -660,9 +670,8 @@ typedef struct ALCdsoundCapture { } ALCdsoundCapture; static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device); -static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, Destruct) +static void ALCdsoundCapture_Destruct(ALCdsoundCapture *self); static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *name); -static void ALCdsoundCapture_close(ALCdsoundCapture *self); static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ALCboolean, reset) static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self); static void ALCdsoundCapture_stop(ALCdsoundCapture *self); @@ -679,6 +688,29 @@ static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device { ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); SET_VTABLE2(ALCdsoundCapture, ALCbackend, self); + + self->DSC = NULL; + self->DSCbuffer = NULL; + self->Ring = NULL; +} + +static void ALCdsoundCapture_Destruct(ALCdsoundCapture *self) +{ + ll_ringbuffer_free(self->Ring); + self->Ring = NULL; + + if(self->DSCbuffer != NULL) + { + IDirectSoundCaptureBuffer_Stop(self->DSCbuffer); + IDirectSoundCaptureBuffer_Release(self->DSCbuffer); + self->DSCbuffer = NULL; + } + + if(self->DSC) + IDirectSoundCapture_Release(self->DSC); + self->DSC = NULL; + + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); } @@ -704,14 +736,14 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi if(!deviceName && VECTOR_SIZE(CaptureDevices) > 0) { - deviceName = al_string_get_cstr(VECTOR_FRONT(CaptureDevices).name); + deviceName = alstr_get_cstr(VECTOR_FRONT(CaptureDevices).name); guid = &VECTOR_FRONT(CaptureDevices).guid; } else { const DevMap *iter; -#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0) +#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0) VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME); #undef MATCH_NAME if(iter == VECTOR_END(CaptureDevices)) @@ -734,102 +766,98 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi break; } + memset(&InputType, 0, sizeof(InputType)); + switch(device->FmtChans) + { + case DevFmtMono: + InputType.dwChannelMask = SPEAKER_FRONT_CENTER; + break; + case DevFmtStereo: + InputType.dwChannelMask = SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT; + break; + case DevFmtQuad: + InputType.dwChannelMask = SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT; + break; + case DevFmtX51: + InputType.dwChannelMask = SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_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(device->FmtChans)); + return ALC_INVALID_ENUM; + } + + InputType.Format.wFormatTag = WAVE_FORMAT_PCM; + InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); + InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8; + InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8; + InputType.Format.nSamplesPerSec = device->Frequency; + InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign; + InputType.Format.cbSize = 0; + InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample; + if(device->FmtType == DevFmtFloat) + InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + else + InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + + if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat) + { + InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); + } + + samples = device->UpdateSize * device->NumUpdates; + samples = maxu(samples, 100 * device->Frequency / 1000); + + memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC)); + DSCBDescription.dwSize = sizeof(DSCBUFFERDESC); + DSCBDescription.dwFlags = 0; + DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign; + DSCBDescription.lpwfxFormat = &InputType.Format; + //DirectSoundCapture Init code hr = DirectSoundCaptureCreate(guid, &self->DSC, NULL); if(SUCCEEDED(hr)) - { - memset(&InputType, 0, sizeof(InputType)); - - switch(device->FmtChans) - { - case DevFmtMono: - InputType.dwChannelMask = SPEAKER_FRONT_CENTER; - break; - case DevFmtStereo: - InputType.dwChannelMask = SPEAKER_FRONT_LEFT | - SPEAKER_FRONT_RIGHT; - break; - case DevFmtQuad: - InputType.dwChannelMask = SPEAKER_FRONT_LEFT | - SPEAKER_FRONT_RIGHT | - SPEAKER_BACK_LEFT | - SPEAKER_BACK_RIGHT; - break; - case DevFmtX51: - InputType.dwChannelMask = SPEAKER_FRONT_LEFT | - SPEAKER_FRONT_RIGHT | - SPEAKER_FRONT_CENTER | - SPEAKER_LOW_FREQUENCY | - SPEAKER_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 DevFmtAmbi1: - case DevFmtAmbi2: - case DevFmtAmbi3: - break; - } - - InputType.Format.wFormatTag = WAVE_FORMAT_PCM; - InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans); - InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8; - InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8; - InputType.Format.nSamplesPerSec = device->Frequency; - InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign; - InputType.Format.cbSize = 0; - - if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat) - { - InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); - InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample; - if(device->FmtType == DevFmtFloat) - InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - else - InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - } - - samples = device->UpdateSize * device->NumUpdates; - samples = maxu(samples, 100 * device->Frequency / 1000); - - memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC)); - DSCBDescription.dwSize = sizeof(DSCBUFFERDESC); - DSCBDescription.dwFlags = 0; - DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign; - DSCBDescription.lpwfxFormat = &InputType.Format; - hr = IDirectSoundCapture_CreateCaptureBuffer(self->DSC, &DSCBDescription, &self->DSCbuffer, NULL); - } if(SUCCEEDED(hr)) { - self->Ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates + 1, - InputType.Format.nBlockAlign); + self->Ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates, + InputType.Format.nBlockAlign, false); if(self->Ring == NULL) hr = DSERR_OUTOFMEMORY; } @@ -853,27 +881,11 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi self->BufferBytes = DSCBDescription.dwBufferBytes; SetDefaultWFXChannelOrder(device); - al_string_copy_cstr(&device->DeviceName, deviceName); + alstr_copy_cstr(&device->DeviceName, deviceName); return ALC_NO_ERROR; } -static void ALCdsoundCapture_close(ALCdsoundCapture *self) -{ - ll_ringbuffer_free(self->Ring); - self->Ring = NULL; - - if(self->DSCbuffer != NULL) - { - IDirectSoundCaptureBuffer_Stop(self->DSCbuffer); - IDirectSoundCaptureBuffer_Release(self->DSCbuffer); - self->DSCbuffer = NULL; - } - - IDirectSoundCapture_Release(self->DSC); - self->DSC = NULL; -} - static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self) { HRESULT hr; @@ -882,7 +894,8 @@ static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self) if(FAILED(hr)) { ERR("start failed: 0x%08lx\n", hr); - aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice); + aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice, + "Failure starting capture: 0x%lx", hr); return ALC_FALSE; } @@ -897,7 +910,8 @@ static void ALCdsoundCapture_stop(ALCdsoundCapture *self) if(FAILED(hr)) { ERR("stop failed: 0x%08lx\n", hr); - aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice); + aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice, + "Failure stopping capture: 0x%lx", hr); } } @@ -916,10 +930,10 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self) DWORD FrameSize; HRESULT hr; - if(!device->Connected) + if(!ATOMIC_LOAD(&device->Connected, almemory_order_acquire)) goto done; - FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); BufferBytes = self->BufferBytes; LastCursor = self->Cursor; @@ -947,18 +961,18 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self) if(FAILED(hr)) { ERR("update failed: 0x%08lx\n", hr); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Failure retrieving capture data: 0x%lx", hr); } done: - return ll_ringbuffer_read_space(self->Ring); + return (ALCuint)ll_ringbuffer_read_space(self->Ring); } static inline void AppendAllDevicesList2(const DevMap *entry) -{ AppendAllDevicesList(al_string_get_cstr(entry->name)); } +{ AppendAllDevicesList(alstr_get_cstr(entry->name)); } static inline void AppendCaptureDeviceList2(const DevMap *entry) -{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); } +{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); } typedef struct ALCdsoundBackendFactory { DERIVE_FROM_TYPE(ALCbackendFactory); diff --git a/Engine/lib/openal-soft/Alc/backends/jack.c b/Engine/lib/openal-soft/Alc/backends/jack.c index 283df297a..67e3c1068 100644 --- a/Engine/lib/openal-soft/Alc/backends/jack.c +++ b/Engine/lib/openal-soft/Alc/backends/jack.c @@ -26,6 +26,8 @@ #include "alMain.h" #include "alu.h" +#include "alconfig.h" +#include "ringbuffer.h" #include "threads.h" #include "compat.h" @@ -63,6 +65,7 @@ static const ALCchar jackDevice[] = "JACK Default"; static void *jack_handle; #define MAKE_FUNC(f) static __typeof(f) * p##f JACK_FUNCS(MAKE_FUNC); +static __typeof(jack_error_callback) * pjack_error_callback; #undef MAKE_FUNC #define jack_client_open pjack_client_open @@ -84,6 +87,7 @@ JACK_FUNCS(MAKE_FUNC); #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 @@ -96,6 +100,8 @@ static ALCboolean jack_load(void) #ifdef HAVE_DYNLOAD if(!jack_handle) { + al_string missing_funcs = AL_STRING_INIT_STATIC(); + #ifdef _WIN32 #define JACKLIB "libjack.dll" #else @@ -103,24 +109,33 @@ static ALCboolean jack_load(void) #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 = GetSymbol(jack_handle, #f); \ if(p##f == NULL) { \ error = ALC_TRUE; \ + alstr_append_cstr(&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 = GetSymbol(jack_handle, #f) + LOAD_SYM(jack_error_callback); +#undef LOAD_SYM if(error) { + WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs)); CloseLib(jack_handle); jack_handle = NULL; - return ALC_FALSE; } + alstr_reset(&missing_funcs); } #endif @@ -135,9 +150,9 @@ typedef struct ALCjackPlayback { jack_port_t *Port[MAX_OUTPUT_CHANNELS]; ll_ringbuffer_t *Ring; - alcnd_t Cond; + alsem_t Sem; - volatile int killNow; + ATOMIC(ALenum) killNow; althrd_t thread; } ALCjackPlayback; @@ -149,15 +164,14 @@ static int ALCjackPlayback_mixerProc(void *arg); static void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device); static void ALCjackPlayback_Destruct(ALCjackPlayback *self); static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name); -static void ALCjackPlayback_close(ALCjackPlayback *self); static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self); static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self); static void ALCjackPlayback_stop(ALCjackPlayback *self); static DECLARE_FORWARD2(ALCjackPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint) static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, ALCuint, availableSamples) static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self); -static void ALCjackPlayback_lock(ALCjackPlayback *self); -static void ALCjackPlayback_unlock(ALCjackPlayback *self); +static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, unlock) DECLARE_DEFAULT_ALLOCATORS(ALCjackPlayback) DEFINE_ALCBACKEND_VTABLE(ALCjackPlayback); @@ -170,14 +184,14 @@ static void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device) ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); SET_VTABLE2(ALCjackPlayback, ALCbackend, self); - alcnd_init(&self->Cond); + alsem_init(&self->Sem, 0); self->Client = NULL; for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) self->Port[i] = NULL; self->Ring = NULL; - self->killNow = 1; + ATOMIC_INIT(&self->killNow, AL_TRUE); } static void ALCjackPlayback_Destruct(ALCjackPlayback *self) @@ -196,7 +210,7 @@ static void ALCjackPlayback_Destruct(ALCjackPlayback *self) self->Client = NULL; } - alcnd_destroy(&self->Cond); + alsem_destroy(&self->Sem); ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); } @@ -211,19 +225,23 @@ static int ALCjackPlayback_bufferSizeNotify(jack_nframes_t numframes, void *arg) ALCjackPlayback_lock(self); device->UpdateSize = numframes; device->NumUpdates = 2; - TRACE("%u update size x%u\n", device->UpdateSize, device->NumUpdates); bufsize = device->UpdateSize; - if(ConfigValueUInt(al_string_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize)) + if(ConfigValueUInt(alstr_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize)) bufsize = maxu(NextPowerOf2(bufsize), device->UpdateSize); - bufsize += device->UpdateSize; + device->NumUpdates = (bufsize+device->UpdateSize) / device->UpdateSize; + + TRACE("%u update size x%u\n", device->UpdateSize, device->NumUpdates); ll_ringbuffer_free(self->Ring); - self->Ring = ll_ringbuffer_create(bufsize, FrameSizeFromDevFmt(device->FmtChans, device->FmtType)); + self->Ring = ll_ringbuffer_create(bufsize, + FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder), + true + ); if(!self->Ring) { ERR("Failed to reallocate ringbuffer\n"); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Failed to reallocate %u-sample buffer", bufsize); } ALCjackPlayback_unlock(self); return 0; @@ -237,7 +255,7 @@ static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg) ll_ringbuffer_data_t data[2]; jack_nframes_t total = 0; jack_nframes_t todo; - ALuint i, c, numchans; + ALsizei i, c, numchans; ll_ringbuffer_get_read_vector(self->Ring, data); @@ -248,8 +266,9 @@ static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg) todo = minu(numframes, data[0].len); for(c = 0;c < numchans;c++) { - for(i = 0;i < todo;i++) - out[c][i] = ((ALfloat*)data[0].buf)[i*numchans + c]; + const ALfloat *restrict in = ((ALfloat*)data[0].buf) + c; + for(i = 0;(jack_nframes_t)i < todo;i++) + out[c][i] = in[i*numchans]; out[c] += todo; } total += todo; @@ -259,22 +278,23 @@ static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg) { for(c = 0;c < numchans;c++) { - for(i = 0;i < todo;i++) - out[c][i] = ((ALfloat*)data[1].buf)[i*numchans + c]; + const ALfloat *restrict in = ((ALfloat*)data[1].buf) + c; + for(i = 0;(jack_nframes_t)i < todo;i++) + out[c][i] = in[i*numchans]; out[c] += todo; } total += todo; } ll_ringbuffer_read_advance(self->Ring, total); - alcnd_signal(&self->Cond); + alsem_post(&self->Sem); if(numframes > total) { todo = numframes-total; for(c = 0;c < numchans;c++) { - for(i = 0;i < todo;i++) + for(i = 0;(jack_nframes_t)i < todo;i++) out[c][i] = 0.0f; } } @@ -292,27 +312,16 @@ static int ALCjackPlayback_mixerProc(void *arg) althrd_setname(althrd_current(), MIXER_THREAD_NAME); ALCjackPlayback_lock(self); - while(!self->killNow && device->Connected) + while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) && + ATOMIC_LOAD(&device->Connected, almemory_order_acquire)) { ALuint todo, len1, len2; - /* NOTE: Unfortunately, there is an unavoidable race condition here. - * It's possible for the process() method to run, updating the read - * pointer and signaling the condition variable, in between the mixer - * loop checking the write size and waiting for the condition variable. - * This will cause the mixer loop to wait until the *next* process() - * invocation, most likely writing silence for it. - * - * However, this should only happen if the mixer is running behind - * anyway (as ideally we'll be asleep in alcnd_wait by the time the - * process() method is invoked), so this behavior is not unwarranted. - * It's unfortunate since it'll be wasting time sleeping that could be - * used to catch up, but there's no way around it without blocking in - * the process() method. - */ if(ll_ringbuffer_write_space(self->Ring) < device->UpdateSize) { - alcnd_wait(&self->Cond, &STATIC_CAST(ALCbackend,self)->mMutex); + ALCjackPlayback_unlock(self); + alsem_wait(&self->Sem); + ALCjackPlayback_lock(self); continue; } @@ -362,29 +371,15 @@ static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name) jack_set_process_callback(self->Client, ALCjackPlayback_process, self); jack_set_buffer_size_callback(self->Client, ALCjackPlayback_bufferSizeNotify, self); - al_string_copy_cstr(&device->DeviceName, name); + alstr_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; } -static void ALCjackPlayback_close(ALCjackPlayback *self) -{ - ALuint i; - - for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) - { - if(self->Port[i]) - jack_port_unregister(self->Client, self->Port[i]); - self->Port[i] = NULL; - } - jack_client_close(self->Client); - self->Client = NULL; -} - static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - ALuint numchans, i; + ALsizei numchans, i; ALuint bufsize; for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) @@ -395,23 +390,21 @@ static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self) } /* Ignore the requested buffer metrics and just keep one JACK-sized buffer - * ready for when requested. Note that one period's worth of audio in the - * ring buffer will always be left unfilled because one element of the ring - * buffer will not be writeable, and we only write in period-sized chunks. + * ready for when requested. */ device->Frequency = jack_get_sample_rate(self->Client); device->UpdateSize = jack_get_buffer_size(self->Client); device->NumUpdates = 2; bufsize = device->UpdateSize; - if(ConfigValueUInt(al_string_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize)) + if(ConfigValueUInt(alstr_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize)) bufsize = maxu(NextPowerOf2(bufsize), device->UpdateSize); - bufsize += device->UpdateSize; + device->NumUpdates = (bufsize+device->UpdateSize) / device->UpdateSize; /* Force 32-bit float output. */ device->FmtType = DevFmtFloat; - numchans = ChannelsFromDevFmt(device->FmtChans); + numchans = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); for(i = 0;i < numchans;i++) { char name[64]; @@ -440,7 +433,10 @@ static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self) } ll_ringbuffer_free(self->Ring); - self->Ring = ll_ringbuffer_create(bufsize, FrameSizeFromDevFmt(device->FmtChans, device->FmtType)); + self->Ring = ll_ringbuffer_create(bufsize, + FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder), + true + ); if(!self->Ring) { ERR("Failed to allocate ringbuffer\n"); @@ -455,7 +451,7 @@ static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self) static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self) { const char **ports; - ALuint i; + ALsizei i; if(jack_activate(self->Client)) { @@ -482,7 +478,7 @@ static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self) } jack_free(ports); - self->killNow = 0; + ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release); if(althrd_create(&self->thread, ALCjackPlayback_mixerProc, self) != althrd_success) { jack_deactivate(self->Client); @@ -496,17 +492,10 @@ static void ALCjackPlayback_stop(ALCjackPlayback *self) { int res; - if(self->killNow) + if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel)) return; - self->killNow = 1; - /* Lock the backend to ensure we don't flag the mixer to die and signal the - * mixer to wake up in between it checking the flag and going to sleep and - * wait for a wakeup (potentially leading to it never waking back up to see - * the flag). */ - ALCjackPlayback_lock(self); - ALCjackPlayback_unlock(self); - alcnd_signal(&self->Cond); + alsem_post(&self->Sem); althrd_join(self->thread, &res); jack_deactivate(self->Client); @@ -528,17 +517,6 @@ static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self) } -static void ALCjackPlayback_lock(ALCjackPlayback *self) -{ - almtx_lock(&STATIC_CAST(ALCbackend,self)->mMutex); -} - -static void ALCjackPlayback_unlock(ALCjackPlayback *self) -{ - almtx_unlock(&STATIC_CAST(ALCbackend,self)->mMutex); -} - - static void jack_msg_handler(const char *message) { WARN("%s\n", message); @@ -551,6 +529,7 @@ typedef struct ALCjackBackendFactory { static ALCboolean ALCjackBackendFactory_init(ALCjackBackendFactory* UNUSED(self)) { + void (*old_error_cb)(const char*); jack_client_t *client; jack_status_t status; @@ -560,9 +539,10 @@ static ALCboolean ALCjackBackendFactory_init(ALCjackBackendFactory* UNUSED(self) if(!GetConfigValueBool(NULL, "jack", "spawn-server", 0)) ClientOptions |= JackNoStartServer; + old_error_cb = (&jack_error_callback ? jack_error_callback : NULL); jack_set_error_function(jack_msg_handler); client = jack_client_open("alsoft", ClientOptions, &status, NULL); - jack_set_error_function(NULL); + jack_set_error_function(old_error_cb); if(client == NULL) { WARN("jack_client_open() failed, 0x%02x\n", status); diff --git a/Engine/lib/openal-soft/Alc/backends/loopback.c b/Engine/lib/openal-soft/Alc/backends/loopback.c index 0164bc5a7..9186a92f6 100644 --- a/Engine/lib/openal-soft/Alc/backends/loopback.c +++ b/Engine/lib/openal-soft/Alc/backends/loopback.c @@ -35,7 +35,6 @@ typedef struct ALCloopback { static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device); static DECLARE_FORWARD(ALCloopback, ALCbackend, void, Destruct) static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name); -static void ALCloopback_close(ALCloopback *self); static ALCboolean ALCloopback_reset(ALCloopback *self); static ALCboolean ALCloopback_start(ALCloopback *self); static void ALCloopback_stop(ALCloopback *self); @@ -59,14 +58,10 @@ static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - al_string_copy_cstr(&device->DeviceName, name); + alstr_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; } -static void ALCloopback_close(ALCloopback* UNUSED(self)) -{ -} - static ALCboolean ALCloopback_reset(ALCloopback *self) { SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice); diff --git a/Engine/lib/openal-soft/Alc/backends/null.c b/Engine/lib/openal-soft/Alc/backends/null.c index 5b153cd97..2c2db54ef 100644 --- a/Engine/lib/openal-soft/Alc/backends/null.c +++ b/Engine/lib/openal-soft/Alc/backends/null.c @@ -36,7 +36,7 @@ typedef struct ALCnullBackend { DERIVE_FROM_TYPE(ALCbackend); - volatile int killNow; + ATOMIC(int) killNow; althrd_t thread; } ALCnullBackend; @@ -45,7 +45,6 @@ static int ALCnullBackend_mixerProc(void *ptr); static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device); static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, Destruct) static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name); -static void ALCnullBackend_close(ALCnullBackend *self); static ALCboolean ALCnullBackend_reset(ALCnullBackend *self); static ALCboolean ALCnullBackend_start(ALCnullBackend *self); static void ALCnullBackend_stop(ALCnullBackend *self); @@ -66,6 +65,8 @@ static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device) { ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); SET_VTABLE2(ALCnullBackend, ALCbackend, self); + + ATOMIC_INIT(&self->killNow, AL_TRUE); } @@ -87,7 +88,8 @@ static int ALCnullBackend_mixerProc(void *ptr) ERR("Failed to get starting time\n"); return 1; } - while(!self->killNow && device->Connected) + while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) && + ATOMIC_LOAD(&device->Connected, almemory_order_acquire)) { if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC) { @@ -109,7 +111,9 @@ static int ALCnullBackend_mixerProc(void *ptr) al_nssleep(restTime); else while(avail-done >= device->UpdateSize) { + ALCnullBackend_lock(self); aluMixData(device, NULL, device->UpdateSize); + ALCnullBackend_unlock(self); done += device->UpdateSize; } } @@ -128,15 +132,11 @@ static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name) return ALC_INVALID_VALUE; device = STATIC_CAST(ALCbackend, self)->mDevice; - al_string_copy_cstr(&device->DeviceName, name); + alstr_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; } -static void ALCnullBackend_close(ALCnullBackend* UNUSED(self)) -{ -} - static ALCboolean ALCnullBackend_reset(ALCnullBackend *self) { SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice); @@ -145,7 +145,7 @@ static ALCboolean ALCnullBackend_reset(ALCnullBackend *self) static ALCboolean ALCnullBackend_start(ALCnullBackend *self) { - self->killNow = 0; + ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release); if(althrd_create(&self->thread, ALCnullBackend_mixerProc, self) != althrd_success) return ALC_FALSE; return ALC_TRUE; @@ -155,10 +155,8 @@ static void ALCnullBackend_stop(ALCnullBackend *self) { int res; - if(self->killNow) + if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel)) return; - - self->killNow = 1; althrd_join(self->thread, &res); } diff --git a/Engine/lib/openal-soft/Alc/backends/opensl.c b/Engine/lib/openal-soft/Alc/backends/opensl.c index 0796c49ab..a5ad3b098 100644 --- a/Engine/lib/openal-soft/Alc/backends/opensl.c +++ b/Engine/lib/openal-soft/Alc/backends/opensl.c @@ -22,38 +22,25 @@ #include "config.h" #include +#include #include "alMain.h" #include "alu.h" +#include "ringbuffer.h" #include "threads.h" +#include "compat.h" + +#include "backends/base.h" #include #include +#include /* Helper macros */ #define VCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS #define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS -typedef struct { - /* engine interfaces */ - SLObjectItf engineObject; - SLEngineItf engine; - - /* output mix interfaces */ - SLObjectItf outputMix; - - /* buffer queue player interfaces */ - SLObjectItf bufferQueueObject; - - void *buffer; - ALuint bufferSize; - ALuint curBuffer; - - ALuint frameSize; -} osl_data; - - static const ALCchar opensl_device[] = "OpenSL"; @@ -79,14 +66,32 @@ static SLuint32 GetChannelMask(enum DevFmtChannels chans) 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 DevFmtAmbi1: - case DevFmtAmbi2: - case DevFmtAmbi3: + case DevFmtAmbi3D: break; } return 0; } +#ifdef SL_DATAFORMAT_PCM_EX +static SLuint32 GetTypeRepresentation(enum DevFmtType type) +{ + switch(type) + { + case DevFmtUByte: + case DevFmtUShort: + case DevFmtUInt: + return SL_PCM_REPRESENTATION_UNSIGNED_INT; + case DevFmtByte: + case DevFmtShort: + case DevFmtInt: + return SL_PCM_REPRESENTATION_SIGNED_INT; + case DevFmtFloat: + return SL_PCM_REPRESENTATION_FLOAT; + } + return 0; +} +#endif + static const char *res_str(SLresult result) { switch(result) @@ -126,168 +131,427 @@ static const char *res_str(SLresult result) ERR("%s: %s\n", (s), res_str((x))); \ } while(0) -/* this callback handler is called every time a buffer finishes playing */ -static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *context) + +typedef struct ALCopenslPlayback { + DERIVE_FROM_TYPE(ALCbackend); + + /* engine interfaces */ + SLObjectItf mEngineObj; + SLEngineItf mEngine; + + /* output mix interfaces */ + SLObjectItf mOutputMix; + + /* buffer queue player interfaces */ + SLObjectItf mBufferQueueObj; + + ll_ringbuffer_t *mRing; + alsem_t mSem; + + ALsizei mFrameSize; + + ATOMIC(ALenum) mKillNow; + althrd_t mThread; +} ALCopenslPlayback; + +static void ALCopenslPlayback_process(SLAndroidSimpleBufferQueueItf bq, void *context); +static int ALCopenslPlayback_mixerProc(void *arg); + +static void ALCopenslPlayback_Construct(ALCopenslPlayback *self, ALCdevice *device); +static void ALCopenslPlayback_Destruct(ALCopenslPlayback *self); +static ALCenum ALCopenslPlayback_open(ALCopenslPlayback *self, const ALCchar *name); +static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self); +static ALCboolean ALCopenslPlayback_start(ALCopenslPlayback *self); +static void ALCopenslPlayback_stop(ALCopenslPlayback *self); +static DECLARE_FORWARD2(ALCopenslPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint) +static DECLARE_FORWARD(ALCopenslPlayback, ALCbackend, ALCuint, availableSamples) +static ClockLatency ALCopenslPlayback_getClockLatency(ALCopenslPlayback *self); +static DECLARE_FORWARD(ALCopenslPlayback, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCopenslPlayback, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCopenslPlayback) + +DEFINE_ALCBACKEND_VTABLE(ALCopenslPlayback); + + +static void ALCopenslPlayback_Construct(ALCopenslPlayback *self, ALCdevice *device) { - ALCdevice *Device = context; - osl_data *data = Device->ExtraData; - ALvoid *buf; - SLresult result; + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCopenslPlayback, ALCbackend, self); - buf = (ALbyte*)data->buffer + data->curBuffer*data->bufferSize; - aluMixData(Device, buf, data->bufferSize/data->frameSize); + self->mEngineObj = NULL; + self->mEngine = NULL; + self->mOutputMix = NULL; + self->mBufferQueueObj = NULL; - result = VCALL(bq,Enqueue)(buf, data->bufferSize); - PRINTERR(result, "bq->Enqueue"); + self->mRing = NULL; + alsem_init(&self->mSem, 0); - data->curBuffer = (data->curBuffer+1) % Device->NumUpdates; + self->mFrameSize = 0; + + ATOMIC_INIT(&self->mKillNow, AL_FALSE); +} + +static void ALCopenslPlayback_Destruct(ALCopenslPlayback* self) +{ + if(self->mBufferQueueObj != NULL) + VCALL0(self->mBufferQueueObj,Destroy)(); + self->mBufferQueueObj = NULL; + + if(self->mOutputMix) + VCALL0(self->mOutputMix,Destroy)(); + self->mOutputMix = NULL; + + if(self->mEngineObj) + VCALL0(self->mEngineObj,Destroy)(); + self->mEngineObj = NULL; + self->mEngine = NULL; + + alsem_destroy(&self->mSem); + + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); } -static ALCenum opensl_open_playback(ALCdevice *Device, const ALCchar *deviceName) +/* this callback handler is called every time a buffer finishes playing */ +static void ALCopenslPlayback_process(SLAndroidSimpleBufferQueueItf UNUSED(bq), void *context) { - osl_data *data = NULL; + ALCopenslPlayback *self = context; + + /* 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. + */ + ll_ringbuffer_read_advance(self->mRing, 1); + + alsem_post(&self->mSem); +} + + +static int ALCopenslPlayback_mixerProc(void *arg) +{ + ALCopenslPlayback *self = arg; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; + SLAndroidSimpleBufferQueueItf bufferQueue; + ll_ringbuffer_data_t data[2]; + SLPlayItf player; SLresult result; - if(!deviceName) - deviceName = opensl_device; - else if(strcmp(deviceName, opensl_device) != 0) + SetRTPriority(); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); + + result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, + &bufferQueue); + PRINTERR(result, "bufferQueue->GetInterface SL_IID_ANDROIDSIMPLEBUFFERQUEUE"); + if(SL_RESULT_SUCCESS == result) + { + result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player); + PRINTERR(result, "bufferQueue->GetInterface SL_IID_PLAY"); + } + if(SL_RESULT_SUCCESS != result) + { + ALCopenslPlayback_lock(self); + aluHandleDisconnect(device, "Failed to get playback buffer: 0x%08x", result); + ALCopenslPlayback_unlock(self); + return 1; + } + + ALCopenslPlayback_lock(self); + while(!ATOMIC_LOAD(&self->mKillNow, almemory_order_acquire) && + ATOMIC_LOAD(&device->Connected, almemory_order_acquire)) + { + size_t todo, len0, len1; + + if(ll_ringbuffer_write_space(self->mRing) == 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(device, "Failed to start platback: 0x%08x", result); + break; + } + + if(ll_ringbuffer_write_space(self->mRing) == 0) + { + ALCopenslPlayback_unlock(self); + alsem_wait(&self->mSem); + ALCopenslPlayback_lock(self); + continue; + } + } + + ll_ringbuffer_get_write_vector(self->mRing, data); + todo = data[0].len+data[1].len; + + len0 = minu(todo, data[0].len); + len1 = minu(todo-len0, data[1].len); + + aluMixData(device, data[0].buf, len0*device->UpdateSize); + for(size_t i = 0;i < len0;i++) + { + result = VCALL(bufferQueue,Enqueue)(data[0].buf, device->UpdateSize*self->mFrameSize); + PRINTERR(result, "bufferQueue->Enqueue"); + if(SL_RESULT_SUCCESS == result) + ll_ringbuffer_write_advance(self->mRing, 1); + + data[0].buf += device->UpdateSize*self->mFrameSize; + } + + if(len1 > 0) + { + aluMixData(device, data[1].buf, len1*device->UpdateSize); + for(size_t i = 0;i < len1;i++) + { + result = VCALL(bufferQueue,Enqueue)(data[1].buf, device->UpdateSize*self->mFrameSize); + PRINTERR(result, "bufferQueue->Enqueue"); + if(SL_RESULT_SUCCESS == result) + ll_ringbuffer_write_advance(self->mRing, 1); + + data[1].buf += device->UpdateSize*self->mFrameSize; + } + } + } + ALCopenslPlayback_unlock(self); + + return 0; +} + + +static ALCenum ALCopenslPlayback_open(ALCopenslPlayback *self, const ALCchar *name) +{ + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; + SLresult result; + + if(!name) + name = opensl_device; + else if(strcmp(name, opensl_device) != 0) return ALC_INVALID_VALUE; - data = calloc(1, sizeof(*data)); - if(!data) - return ALC_OUT_OF_MEMORY; - // create engine - result = slCreateEngine(&data->engineObject, 0, NULL, 0, NULL, NULL); + result = slCreateEngine(&self->mEngineObj, 0, NULL, 0, NULL, NULL); PRINTERR(result, "slCreateEngine"); if(SL_RESULT_SUCCESS == result) { - result = VCALL(data->engineObject,Realize)(SL_BOOLEAN_FALSE); + result = VCALL(self->mEngineObj,Realize)(SL_BOOLEAN_FALSE); PRINTERR(result, "engine->Realize"); } if(SL_RESULT_SUCCESS == result) { - result = VCALL(data->engineObject,GetInterface)(SL_IID_ENGINE, &data->engine); + result = VCALL(self->mEngineObj,GetInterface)(SL_IID_ENGINE, &self->mEngine); PRINTERR(result, "engine->GetInterface"); } if(SL_RESULT_SUCCESS == result) { - result = VCALL(data->engine,CreateOutputMix)(&data->outputMix, 0, NULL, NULL); + result = VCALL(self->mEngine,CreateOutputMix)(&self->mOutputMix, 0, NULL, NULL); PRINTERR(result, "engine->CreateOutputMix"); } if(SL_RESULT_SUCCESS == result) { - result = VCALL(data->outputMix,Realize)(SL_BOOLEAN_FALSE); + result = VCALL(self->mOutputMix,Realize)(SL_BOOLEAN_FALSE); PRINTERR(result, "outputMix->Realize"); } if(SL_RESULT_SUCCESS != result) { - if(data->outputMix != NULL) - VCALL0(data->outputMix,Destroy)(); - data->outputMix = NULL; + if(self->mOutputMix != NULL) + VCALL0(self->mOutputMix,Destroy)(); + self->mOutputMix = NULL; - if(data->engineObject != NULL) - VCALL0(data->engineObject,Destroy)(); - data->engineObject = NULL; - data->engine = NULL; + if(self->mEngineObj != NULL) + VCALL0(self->mEngineObj,Destroy)(); + self->mEngineObj = NULL; + self->mEngine = NULL; - free(data); return ALC_INVALID_VALUE; } - al_string_copy_cstr(&Device->DeviceName, deviceName); - Device->ExtraData = data; + alstr_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; } - -static void opensl_close_playback(ALCdevice *Device) +static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self) { - osl_data *data = Device->ExtraData; - - if(data->bufferQueueObject != NULL) - VCALL0(data->bufferQueueObject,Destroy)(); - data->bufferQueueObject = NULL; - - VCALL0(data->outputMix,Destroy)(); - data->outputMix = NULL; - - VCALL0(data->engineObject,Destroy)(); - data->engineObject = NULL; - data->engine = NULL; - - free(data); - Device->ExtraData = NULL; -} - -static ALCboolean opensl_reset_playback(ALCdevice *Device) -{ - osl_data *data = Device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; SLDataLocator_AndroidSimpleBufferQueue loc_bufq; SLDataLocator_OutputMix loc_outmix; - SLDataFormat_PCM format_pcm; SLDataSource audioSrc; SLDataSink audioSnk; - SLInterfaceID id; - SLboolean req; + ALuint sampleRate; + SLInterfaceID ids[2]; + SLboolean reqs[2]; SLresult result; + JNIEnv *env; + if(self->mBufferQueueObj != NULL) + VCALL0(self->mBufferQueueObj,Destroy)(); + self->mBufferQueueObj = NULL; - Device->UpdateSize = (ALuint64)Device->UpdateSize * 44100 / Device->Frequency; - Device->UpdateSize = Device->UpdateSize * Device->NumUpdates / 2; - Device->NumUpdates = 2; + sampleRate = device->Frequency; + if(!(device->Flags&DEVICE_FREQUENCY_REQUEST) && (env=Android_GetJNIEnv()) != NULL) + { + /* FIXME: Disabled until I figure out how to get the Context needed for + * the getSystemService call. + */ +#if 0 + /* 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); - Device->Frequency = 44100; - Device->FmtChans = DevFmtStereo; - Device->FmtType = DevFmtShort; + 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); - SetDefaultWFXChannelOrder(Device); + 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)(ctx_cls, ctx_getSysSvc, strobj); + strchars = JCALL(env,GetStringUTFChars)(strobj, NULL); + 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, NULL); + 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, NULL); + 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 + } + + if(sampleRate != device->Frequency) + { + device->NumUpdates = (device->NumUpdates*sampleRate + (device->Frequency>>1)) / + device->Frequency; + device->NumUpdates = maxu(device->NumUpdates, 2); + device->Frequency = sampleRate; + } + + device->FmtChans = DevFmtStereo; + device->FmtType = DevFmtShort; + + SetDefaultWFXChannelOrder(device); + self->mFrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); - id = SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - req = SL_BOOLEAN_TRUE; loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - loc_bufq.numBuffers = Device->NumUpdates; + loc_bufq.numBuffers = device->NumUpdates; - format_pcm.formatType = SL_DATAFORMAT_PCM; - format_pcm.numChannels = ChannelsFromDevFmt(Device->FmtChans); - format_pcm.samplesPerSec = Device->Frequency * 1000; - format_pcm.bitsPerSample = BytesFromDevFmt(Device->FmtType) * 8; +#ifdef SL_DATAFORMAT_PCM_EX + SLDataFormat_PCM_EX format_pcm; + format_pcm.formatType = SL_DATAFORMAT_PCM_EX; + format_pcm.numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); + format_pcm.sampleRate = device->Frequency * 1000; + format_pcm.bitsPerSample = BytesFromDevFmt(device->FmtType) * 8; format_pcm.containerSize = format_pcm.bitsPerSample; - format_pcm.channelMask = GetChannelMask(Device->FmtChans); + format_pcm.channelMask = GetChannelMask(device->FmtChans); format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN; + format_pcm.representation = GetTypeRepresentation(device->FmtType); +#else + SLDataFormat_PCM format_pcm; + format_pcm.formatType = SL_DATAFORMAT_PCM; + format_pcm.numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); + format_pcm.samplesPerSec = device->Frequency * 1000; + format_pcm.bitsPerSample = BytesFromDevFmt(device->FmtType) * 8; + format_pcm.containerSize = format_pcm.bitsPerSample; + format_pcm.channelMask = GetChannelMask(device->FmtChans); + format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN : + SL_BYTEORDER_BIGENDIAN; +#endif audioSrc.pLocator = &loc_bufq; audioSrc.pFormat = &format_pcm; loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; - loc_outmix.outputMix = data->outputMix; + loc_outmix.outputMix = self->mOutputMix; audioSnk.pLocator = &loc_outmix; audioSnk.pFormat = NULL; - if(data->bufferQueueObject != NULL) - VCALL0(data->bufferQueueObject,Destroy)(); - data->bufferQueueObject = NULL; + ids[0] = SL_IID_ANDROIDSIMPLEBUFFERQUEUE; + reqs[0] = SL_BOOLEAN_TRUE; + ids[1] = SL_IID_ANDROIDCONFIGURATION; + reqs[1] = SL_BOOLEAN_FALSE; - result = VCALL(data->engine,CreateAudioPlayer)(&data->bufferQueueObject, &audioSrc, &audioSnk, 1, &id, &req); + result = VCALL(self->mEngine,CreateAudioPlayer)(&self->mBufferQueueObj, + &audioSrc, &audioSnk, COUNTOF(ids), ids, reqs + ); PRINTERR(result, "engine->CreateAudioPlayer"); if(SL_RESULT_SUCCESS == result) { - result = VCALL(data->bufferQueueObject,Realize)(SL_BOOLEAN_FALSE); + /* Set the stream type to "media" (games, music, etc), if possible. */ + SLAndroidConfigurationItf config; + result = VCALL(self->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(self->mBufferQueueObj,Realize)(SL_BOOLEAN_FALSE); PRINTERR(result, "bufferQueue->Realize"); } if(SL_RESULT_SUCCESS != result) { - if(data->bufferQueueObject != NULL) - VCALL0(data->bufferQueueObject,Destroy)(); - data->bufferQueueObject = NULL; + if(self->mBufferQueueObj != NULL) + VCALL0(self->mBufferQueueObj,Destroy)(); + self->mBufferQueueObj = NULL; return ALC_FALSE; } @@ -295,64 +559,31 @@ static ALCboolean opensl_reset_playback(ALCdevice *Device) return ALC_TRUE; } -static ALCboolean opensl_start_playback(ALCdevice *Device) +static ALCboolean ALCopenslPlayback_start(ALCopenslPlayback *self) { - osl_data *data = Device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; SLAndroidSimpleBufferQueueItf bufferQueue; - SLPlayItf player; SLresult result; - ALuint i; - result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue); + ll_ringbuffer_free(self->mRing); + self->mRing = ll_ringbuffer_create(device->NumUpdates, self->mFrameSize*device->UpdateSize, + true); + + result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, + &bufferQueue); PRINTERR(result, "bufferQueue->GetInterface"); - if(SL_RESULT_SUCCESS == result) - { - result = VCALL(bufferQueue,RegisterCallback)(opensl_callback, Device); - PRINTERR(result, "bufferQueue->RegisterCallback"); - } - if(SL_RESULT_SUCCESS == result) - { - data->frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); - data->bufferSize = Device->UpdateSize * data->frameSize; - data->buffer = calloc(Device->NumUpdates, data->bufferSize); - if(!data->buffer) - { - result = SL_RESULT_MEMORY_FAILURE; - PRINTERR(result, "calloc"); - } - } - /* enqueue the first buffer to kick off the callbacks */ - for(i = 0;i < Device->NumUpdates;i++) - { - if(SL_RESULT_SUCCESS == result) - { - ALvoid *buf = (ALbyte*)data->buffer + i*data->bufferSize; - result = VCALL(bufferQueue,Enqueue)(buf, data->bufferSize); - PRINTERR(result, "bufferQueue->Enqueue"); - } - } - data->curBuffer = 0; - if(SL_RESULT_SUCCESS == result) - { - result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player); - PRINTERR(result, "bufferQueue->GetInterface"); - } - if(SL_RESULT_SUCCESS == result) - { - result = VCALL(player,SetPlayState)(SL_PLAYSTATE_PLAYING); - PRINTERR(result, "player->SetPlayState"); - } - if(SL_RESULT_SUCCESS != result) + return ALC_FALSE; + + result = VCALL(bufferQueue,RegisterCallback)(ALCopenslPlayback_process, self); + PRINTERR(result, "bufferQueue->RegisterCallback"); + if(SL_RESULT_SUCCESS != result) + return ALC_FALSE; + + ATOMIC_STORE_SEQ(&self->mKillNow, AL_FALSE); + if(althrd_create(&self->mThread, ALCopenslPlayback_mixerProc, self) != althrd_success) { - if(data->bufferQueueObject != NULL) - VCALL0(data->bufferQueueObject,Destroy)(); - data->bufferQueueObject = NULL; - - free(data->buffer); - data->buffer = NULL; - data->bufferSize = 0; - + ERR("Failed to start mixer thread\n"); return ALC_FALSE; } @@ -360,14 +591,20 @@ static ALCboolean opensl_start_playback(ALCdevice *Device) } -static void opensl_stop_playback(ALCdevice *Device) +static void ALCopenslPlayback_stop(ALCopenslPlayback *self) { - osl_data *data = Device->ExtraData; - SLPlayItf player; SLAndroidSimpleBufferQueueItf bufferQueue; + SLPlayItf player; SLresult result; + int res; - result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player); + if(ATOMIC_EXCHANGE_SEQ(&self->mKillNow, AL_TRUE)) + return; + + alsem_post(&self->mSem); + althrd_join(self->mThread, &res); + + result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player); PRINTERR(result, "bufferQueue->GetInterface"); if(SL_RESULT_SUCCESS == result) { @@ -375,7 +612,8 @@ static void opensl_stop_playback(ALCdevice *Device) PRINTERR(result, "player->SetPlayState"); } - result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue); + result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, + &bufferQueue); PRINTERR(result, "bufferQueue->GetInterface"); if(SL_RESULT_SUCCESS == result) { @@ -383,6 +621,11 @@ static void opensl_stop_playback(ALCdevice *Device) PRINTERR(result, "bufferQueue->Clear"); } if(SL_RESULT_SUCCESS == result) + { + result = VCALL(bufferQueue,RegisterCallback)(NULL, NULL); + PRINTERR(result, "bufferQueue->RegisterCallback"); + } + if(SL_RESULT_SUCCESS == result) { SLAndroidSimpleBufferQueueState state; do { @@ -392,45 +635,440 @@ static void opensl_stop_playback(ALCdevice *Device) PRINTERR(result, "bufferQueue->GetState"); } - free(data->buffer); - data->buffer = NULL; - data->bufferSize = 0; + ll_ringbuffer_free(self->mRing); + self->mRing = NULL; +} + +static ClockLatency ALCopenslPlayback_getClockLatency(ALCopenslPlayback *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + ClockLatency ret; + + ALCopenslPlayback_lock(self); + ret.ClockTime = GetDeviceClockTime(device); + ret.Latency = ll_ringbuffer_read_space(self->mRing)*device->UpdateSize * + DEVICE_CLOCK_RES / device->Frequency; + ALCopenslPlayback_unlock(self); + + return ret; } -static const BackendFuncs opensl_funcs = { - opensl_open_playback, - opensl_close_playback, - opensl_reset_playback, - opensl_start_playback, - opensl_stop_playback, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL -}; +typedef struct ALCopenslCapture { + DERIVE_FROM_TYPE(ALCbackend); + + /* engine interfaces */ + SLObjectItf mEngineObj; + SLEngineItf mEngine; + + /* recording interfaces */ + SLObjectItf mRecordObj; + + ll_ringbuffer_t *mRing; + ALCuint mSplOffset; + + ALsizei mFrameSize; +} ALCopenslCapture; + +static void ALCopenslCapture_process(SLAndroidSimpleBufferQueueItf bq, void *context); + +static void ALCopenslCapture_Construct(ALCopenslCapture *self, ALCdevice *device); +static void ALCopenslCapture_Destruct(ALCopenslCapture *self); +static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name); +static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, ALCboolean, reset) +static ALCboolean ALCopenslCapture_start(ALCopenslCapture *self); +static void ALCopenslCapture_stop(ALCopenslCapture *self); +static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *buffer, ALCuint samples); +static ALCuint ALCopenslCapture_availableSamples(ALCopenslCapture *self); +static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, ClockLatency, getClockLatency) +static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCopenslCapture) +DEFINE_ALCBACKEND_VTABLE(ALCopenslCapture); -ALCboolean alc_opensl_init(BackendFuncs *func_list) +static void ALCopenslCapture_process(SLAndroidSimpleBufferQueueItf UNUSED(bq), void *context) { - *func_list = opensl_funcs; + ALCopenslCapture *self = context; + /* A new chunk has been written into the ring buffer, advance it. */ + ll_ringbuffer_write_advance(self->mRing, 1); +} + + +static void ALCopenslCapture_Construct(ALCopenslCapture *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCopenslCapture, ALCbackend, self); + + self->mEngineObj = NULL; + self->mEngine = NULL; + + self->mRecordObj = NULL; + + self->mRing = NULL; + self->mSplOffset = 0; + + self->mFrameSize = 0; +} + +static void ALCopenslCapture_Destruct(ALCopenslCapture *self) +{ + ll_ringbuffer_free(self->mRing); + self->mRing = NULL; + + if(self->mRecordObj != NULL) + VCALL0(self->mRecordObj,Destroy)(); + self->mRecordObj = NULL; + + if(self->mEngineObj != NULL) + VCALL0(self->mEngineObj,Destroy)(); + self->mEngineObj = NULL; + self->mEngine = NULL; + + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); +} + +static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + SLDataLocator_AndroidSimpleBufferQueue loc_bq; + SLAndroidSimpleBufferQueueItf bufferQueue; + SLDataLocator_IODevice loc_dev; + SLDataSource audioSrc; + SLDataSink audioSnk; + SLresult result; + + if(!name) + name = opensl_device; + else if(strcmp(name, opensl_device) != 0) + return ALC_INVALID_VALUE; + + result = slCreateEngine(&self->mEngineObj, 0, NULL, 0, NULL, NULL); + PRINTERR(result, "slCreateEngine"); + if(SL_RESULT_SUCCESS == result) + { + result = VCALL(self->mEngineObj,Realize)(SL_BOOLEAN_FALSE); + PRINTERR(result, "engine->Realize"); + } + if(SL_RESULT_SUCCESS == result) + { + result = VCALL(self->mEngineObj,GetInterface)(SL_IID_ENGINE, &self->mEngine); + PRINTERR(result, "engine->GetInterface"); + } + if(SL_RESULT_SUCCESS == result) + { + /* Ensure the total length is at least 100ms */ + ALsizei length = maxi(device->NumUpdates * device->UpdateSize, + device->Frequency / 10); + /* Ensure the per-chunk length is at least 10ms, and no more than 50ms. */ + ALsizei update_len = clampi(device->NumUpdates*device->UpdateSize / 3, + device->Frequency / 100, + device->Frequency / 100 * 5); + + device->UpdateSize = update_len; + device->NumUpdates = (length+update_len-1) / update_len; + + self->mFrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); + } + loc_dev.locatorType = SL_DATALOCATOR_IODEVICE; + loc_dev.deviceType = SL_IODEVICE_AUDIOINPUT; + loc_dev.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; + loc_dev.device = NULL; + + audioSrc.pLocator = &loc_dev; + audioSrc.pFormat = NULL; + + loc_bq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; + loc_bq.numBuffers = device->NumUpdates; + +#ifdef SL_DATAFORMAT_PCM_EX + SLDataFormat_PCM_EX format_pcm; + format_pcm.formatType = SL_DATAFORMAT_PCM_EX; + format_pcm.numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); + format_pcm.sampleRate = device->Frequency * 1000; + format_pcm.bitsPerSample = BytesFromDevFmt(device->FmtType) * 8; + format_pcm.containerSize = format_pcm.bitsPerSample; + format_pcm.channelMask = GetChannelMask(device->FmtChans); + format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN : + SL_BYTEORDER_BIGENDIAN; + format_pcm.representation = GetTypeRepresentation(device->FmtType); +#else + SLDataFormat_PCM format_pcm; + format_pcm.formatType = SL_DATAFORMAT_PCM; + format_pcm.numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); + format_pcm.samplesPerSec = device->Frequency * 1000; + format_pcm.bitsPerSample = BytesFromDevFmt(device->FmtType) * 8; + format_pcm.containerSize = format_pcm.bitsPerSample; + format_pcm.channelMask = GetChannelMask(device->FmtChans); + format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN : + SL_BYTEORDER_BIGENDIAN; +#endif + + audioSnk.pLocator = &loc_bq; + audioSnk.pFormat = &format_pcm; + + if(SL_RESULT_SUCCESS == result) + { + const SLInterfaceID ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION }; + const SLboolean reqs[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE }; + + result = VCALL(self->mEngine,CreateAudioRecorder)(&self->mRecordObj, + &audioSrc, &audioSnk, COUNTOF(ids), ids, reqs + ); + PRINTERR(result, "engine->CreateAudioRecorder"); + } + if(SL_RESULT_SUCCESS == result) + { + /* Set the record preset to "generic", if possible. */ + SLAndroidConfigurationItf config; + result = VCALL(self->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(self->mRecordObj,Realize)(SL_BOOLEAN_FALSE); + PRINTERR(result, "recordObj->Realize"); + } + + if(SL_RESULT_SUCCESS == result) + { + self->mRing = ll_ringbuffer_create(device->NumUpdates, device->UpdateSize*self->mFrameSize, + false); + + result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, + &bufferQueue); + PRINTERR(result, "recordObj->GetInterface"); + } + if(SL_RESULT_SUCCESS == result) + { + result = VCALL(bufferQueue,RegisterCallback)(ALCopenslCapture_process, self); + PRINTERR(result, "bufferQueue->RegisterCallback"); + } + if(SL_RESULT_SUCCESS == result) + { + ALsizei chunk_size = device->UpdateSize * self->mFrameSize; + ll_ringbuffer_data_t data[2]; + size_t i; + + ll_ringbuffer_get_write_vector(self->mRing, data); + for(i = 0;i < data[0].len && SL_RESULT_SUCCESS == result;i++) + { + result = VCALL(bufferQueue,Enqueue)(data[0].buf + chunk_size*i, chunk_size); + PRINTERR(result, "bufferQueue->Enqueue"); + } + for(i = 0;i < data[1].len && SL_RESULT_SUCCESS == result;i++) + { + result = VCALL(bufferQueue,Enqueue)(data[1].buf + chunk_size*i, chunk_size); + PRINTERR(result, "bufferQueue->Enqueue"); + } + } + + if(SL_RESULT_SUCCESS != result) + { + if(self->mRecordObj != NULL) + VCALL0(self->mRecordObj,Destroy)(); + self->mRecordObj = NULL; + + if(self->mEngineObj != NULL) + VCALL0(self->mEngineObj,Destroy)(); + self->mEngineObj = NULL; + self->mEngine = NULL; + + return ALC_INVALID_VALUE; + } + + alstr_copy_cstr(&device->DeviceName, name); + + return ALC_NO_ERROR; +} + +static ALCboolean ALCopenslCapture_start(ALCopenslCapture *self) +{ + SLRecordItf record; + SLresult result; + + result = VCALL(self->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) + { + ALCopenslCapture_lock(self); + aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice, + "Failed to start capture: 0x%08x", result); + ALCopenslCapture_unlock(self); + return ALC_FALSE; + } + return ALC_TRUE; } -void alc_opensl_deinit(void) +static void ALCopenslCapture_stop(ALCopenslCapture *self) +{ + SLRecordItf record; + SLresult result; + + result = VCALL(self->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"); + } +} + +static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *buffer, ALCuint samples) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + ALsizei chunk_size = device->UpdateSize * self->mFrameSize; + SLAndroidSimpleBufferQueueItf bufferQueue; + ll_ringbuffer_data_t data[2]; + SLresult result; + size_t advance; + ALCuint i; + + /* Read the desired samples from the ring buffer then advance its read + * pointer. + */ + ll_ringbuffer_get_read_vector(self->mRing, data); + advance = 0; + for(i = 0;i < samples;) + { + ALCuint rem = minu(samples - i, device->UpdateSize - self->mSplOffset); + memcpy((ALCbyte*)buffer + i*self->mFrameSize, + data[0].buf + self->mSplOffset*self->mFrameSize, + rem * self->mFrameSize); + + self->mSplOffset += rem; + if(self->mSplOffset == device->UpdateSize) + { + /* Finished a chunk, reset the offset and advance the read pointer. */ + self->mSplOffset = 0; + advance++; + + data[0].len--; + if(!data[0].len) + data[0] = data[1]; + else + data[0].buf += chunk_size; + } + + i += rem; + } + ll_ringbuffer_read_advance(self->mRing, advance); + + result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, + &bufferQueue); + PRINTERR(result, "recordObj->GetInterface"); + + /* Enqueue any newly-writable chunks in the ring buffer. */ + ll_ringbuffer_get_write_vector(self->mRing, data); + for(i = 0;i < data[0].len && SL_RESULT_SUCCESS == result;i++) + { + result = VCALL(bufferQueue,Enqueue)(data[0].buf + chunk_size*i, chunk_size); + PRINTERR(result, "bufferQueue->Enqueue"); + } + for(i = 0;i < data[1].len && SL_RESULT_SUCCESS == result;i++) + { + result = VCALL(bufferQueue,Enqueue)(data[1].buf + chunk_size*i, chunk_size); + PRINTERR(result, "bufferQueue->Enqueue"); + } + + if(SL_RESULT_SUCCESS != result) + { + ALCopenslCapture_lock(self); + aluHandleDisconnect(device, "Failed to update capture buffer: 0x%08x", result); + ALCopenslCapture_unlock(self); + return ALC_INVALID_DEVICE; + } + + return ALC_NO_ERROR; +} + +static ALCuint ALCopenslCapture_availableSamples(ALCopenslCapture *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + return ll_ringbuffer_read_space(self->mRing) * device->UpdateSize; +} + + +typedef struct ALCopenslBackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCopenslBackendFactory; +#define ALCOPENSLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCopenslBackendFactory, ALCbackendFactory) } } + +static ALCboolean ALCopenslBackendFactory_init(ALCopenslBackendFactory* UNUSED(self)) +{ + return ALC_TRUE; +} + +static void ALCopenslBackendFactory_deinit(ALCopenslBackendFactory* UNUSED(self)) { } -void alc_opensl_probe(enum DevProbe type) +static ALCboolean ALCopenslBackendFactory_querySupport(ALCopenslBackendFactory* UNUSED(self), ALCbackend_Type type) +{ + if(type == ALCbackend_Playback || type == ALCbackend_Capture) + return ALC_TRUE; + return ALC_FALSE; +} + +static void ALCopenslBackendFactory_probe(ALCopenslBackendFactory* UNUSED(self), enum DevProbe type) { switch(type) { case ALL_DEVICE_PROBE: AppendAllDevicesList(opensl_device); break; + case CAPTURE_DEVICE_PROBE: + AppendAllDevicesList(opensl_device); break; } } + +static ALCbackend* ALCopenslBackendFactory_createBackend(ALCopenslBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + ALCopenslPlayback *backend; + NEW_OBJ(backend, ALCopenslPlayback)(device); + if(!backend) return NULL; + return STATIC_CAST(ALCbackend, backend); + } + if(type == ALCbackend_Capture) + { + ALCopenslCapture *backend; + NEW_OBJ(backend, ALCopenslCapture)(device); + if(!backend) return NULL; + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} + +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCopenslBackendFactory); + + +ALCbackendFactory *ALCopenslBackendFactory_getFactory(void) +{ + static ALCopenslBackendFactory factory = ALCOPENSLBACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); +} diff --git a/Engine/lib/openal-soft/Alc/backends/oss.c b/Engine/lib/openal-soft/Alc/backends/oss.c index 432c75f21..c0c98c432 100644 --- a/Engine/lib/openal-soft/Alc/backends/oss.c +++ b/Engine/lib/openal-soft/Alc/backends/oss.c @@ -35,6 +35,8 @@ #include "alMain.h" #include "alu.h" +#include "alconfig.h" +#include "ringbuffer.h" #include "threads.h" #include "compat.h" @@ -88,7 +90,9 @@ static struct oss_device oss_capture = { #ifdef ALC_OSS_COMPAT -static void ALCossListPopulate(struct oss_device *UNUSED(playback), struct oss_device *UNUSED(capture)) +#define DSP_CAP_OUTPUT 0x00020000 +#define DSP_CAP_INPUT 0x00010000 +static void ALCossListPopulate(struct oss_device *UNUSED(devlist), int UNUSED(type_flag)) { } @@ -153,7 +157,7 @@ static void ALCossListAppend(struct oss_device *list, const char *handle, size_t TRACE("Got device \"%s\", \"%s\"\n", next->handle, next->path); } -static void ALCossListPopulate(struct oss_device *playback, struct oss_device *capture) +static void ALCossListPopulate(struct oss_device *devlist, int type_flag) { struct oss_sysinfo si; struct oss_audioinfo ai; @@ -161,12 +165,12 @@ static void ALCossListPopulate(struct oss_device *playback, struct oss_device *c if((fd=open("/dev/mixer", O_RDONLY)) < 0) { - ERR("Could not open /dev/mixer\n"); + TRACE("Could not open /dev/mixer: %s\n", strerror(errno)); return; } if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1) { - ERR("SNDCTL_SYSINFO failed: %s\n", strerror(errno)); + TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno)); goto done; } for(i = 0;i < si.numaudios;i++) @@ -193,10 +197,9 @@ static void ALCossListPopulate(struct oss_device *playback, struct oss_device *c len = strnlen(ai.name, sizeof(ai.name)); handle = ai.name; } - if((ai.caps&DSP_CAP_INPUT) && capture != NULL) - ALCossListAppend(capture, handle, len, ai.devnode, strnlen(ai.devnode, sizeof(ai.devnode))); - if((ai.caps&DSP_CAP_OUTPUT) && playback != NULL) - ALCossListAppend(playback, handle, len, ai.devnode, strnlen(ai.devnode, sizeof(ai.devnode))); + if((ai.caps&type_flag)) + ALCossListAppend(devlist, handle, len, ai.devnode, + strnlen(ai.devnode, sizeof(ai.devnode))); } done: @@ -242,16 +245,15 @@ typedef struct ALCplaybackOSS { ALubyte *mix_data; int data_size; - volatile int killNow; + ATOMIC(ALenum) killNow; althrd_t thread; } ALCplaybackOSS; static int ALCplaybackOSS_mixerProc(void *ptr); static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device); -static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, Destruct) +static void ALCplaybackOSS_Destruct(ALCplaybackOSS *self); static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name); -static void ALCplaybackOSS_close(ALCplaybackOSS *self); static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self); static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self); static void ALCplaybackOSS_stop(ALCplaybackOSS *self); @@ -268,42 +270,66 @@ static int ALCplaybackOSS_mixerProc(void *ptr) { ALCplaybackOSS *self = (ALCplaybackOSS*)ptr; ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - ALint frameSize; + struct timeval timeout; + ALubyte *write_ptr; + ALint frame_size; + ALint to_write; ssize_t wrote; + fd_set wfds; + int sret; SetRTPriority(); althrd_setname(althrd_current(), MIXER_THREAD_NAME); - frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); - while(!self->killNow && device->Connected) + ALCplaybackOSS_lock(self); + while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) && + ATOMIC_LOAD(&device->Connected, almemory_order_acquire)) { - ALint len = self->data_size; - ALubyte *WritePtr = self->mix_data; + FD_ZERO(&wfds); + FD_SET(self->fd, &wfds); + timeout.tv_sec = 1; + timeout.tv_usec = 0; - aluMixData(device, WritePtr, len/frameSize); - while(len > 0 && !self->killNow) + ALCplaybackOSS_unlock(self); + sret = select(self->fd+1, NULL, &wfds, NULL, &timeout); + ALCplaybackOSS_lock(self); + if(sret < 0) { - wrote = write(self->fd, WritePtr, len); + if(errno == EINTR) + continue; + ERR("select failed: %s\n", strerror(errno)); + aluHandleDisconnect(device, "Failed waiting for playback buffer: %s", strerror(errno)); + break; + } + else if(sret == 0) + { + WARN("select timeout\n"); + continue; + } + + write_ptr = self->mix_data; + to_write = self->data_size; + aluMixData(device, write_ptr, to_write/frame_size); + while(to_write > 0 && !ATOMIC_LOAD_SEQ(&self->killNow)) + { + wrote = write(self->fd, write_ptr, to_write); if(wrote < 0) { - if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) - { - ERR("write failed: %s\n", strerror(errno)); - ALCplaybackOSS_lock(self); - aluHandleDisconnect(device); - ALCplaybackOSS_unlock(self); - break; - } - - al_nssleep(1000000); - continue; + if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + continue; + ERR("write failed: %s\n", strerror(errno)); + aluHandleDisconnect(device, "Failed writing playback samples: %s", + strerror(errno)); + break; } - len -= wrote; - WritePtr += wrote; + to_write -= wrote; + write_ptr += wrote; } } + ALCplaybackOSS_unlock(self); return 0; } @@ -313,6 +339,18 @@ static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device) { ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); SET_VTABLE2(ALCplaybackOSS, ALCbackend, self); + + self->fd = -1; + ATOMIC_INIT(&self->killNow, AL_FALSE); +} + +static void ALCplaybackOSS_Destruct(ALCplaybackOSS *self) +{ + if(self->fd != -1) + close(self->fd); + self->fd = -1; + + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); } static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name) @@ -320,22 +358,28 @@ static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name) struct oss_device *dev = &oss_playback; ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - if(!name) + if(!name || strcmp(name, dev->handle) == 0) name = dev->handle; else { - while (dev != NULL) + if(!dev->next) + { + ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT); + dev = &oss_playback; + } + while(dev != NULL) { if (strcmp(dev->handle, name) == 0) break; dev = dev->next; } - if (dev == NULL) + if(dev == NULL) + { + WARN("Could not find \"%s\" in device list\n", name); return ALC_INVALID_VALUE; + } } - self->killNow = 0; - self->fd = open(dev->path, O_WRONLY); if(self->fd == -1) { @@ -343,17 +387,11 @@ static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name) return ALC_INVALID_VALUE; } - al_string_copy_cstr(&device->DeviceName, name); + alstr_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; } -static void ALCplaybackOSS_close(ALCplaybackOSS *self) -{ - close(self->fd); - self->fd = -1; -} - static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; @@ -387,18 +425,11 @@ static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self) } periods = device->NumUpdates; - numChannels = ChannelsFromDevFmt(device->FmtChans); - frameSize = numChannels * BytesFromDevFmt(device->FmtType); - + numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); ossSpeed = device->Frequency; - log2FragmentSize = log2i(device->UpdateSize * frameSize); - - /* according to the OSS spec, 16 bytes are the minimum */ - if (log2FragmentSize < 4) - log2FragmentSize = 4; - /* Subtract one period since the temp mixing buffer counts as one. Still - * need at least two on the card, though. */ - if(periods > 2) periods--; + frameSize = numChannels * BytesFromDevFmt(device->FmtType); + /* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */ + log2FragmentSize = maxi(log2i(device->UpdateSize*frameSize), 4); numFragmentsLogSize = (periods << 16) | log2FragmentSize; #define CHECKERR(func) if((func) < 0) { \ @@ -420,7 +451,7 @@ static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self) } #undef CHECKERR - if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels) + if((int)ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != numChannels) { ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels); return ALC_FALSE; @@ -436,7 +467,7 @@ static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self) device->Frequency = ossSpeed; device->UpdateSize = info.fragsize / frameSize; - device->NumUpdates = info.fragments + 1; + device->NumUpdates = info.fragments; SetDefaultChannelOrder(device); @@ -447,10 +478,12 @@ static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + self->data_size = device->UpdateSize * FrameSizeFromDevFmt( + device->FmtChans, device->FmtType, device->AmbiOrder + ); self->mix_data = calloc(1, self->data_size); - self->killNow = 0; + ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE); if(althrd_create(&self->thread, ALCplaybackOSS_mixerProc, self) != althrd_success) { free(self->mix_data); @@ -465,10 +498,8 @@ static void ALCplaybackOSS_stop(ALCplaybackOSS *self) { int res; - if(self->killNow) + if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE)) return; - - self->killNow = 1; althrd_join(self->thread, &res); if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0) @@ -485,18 +516,16 @@ typedef struct ALCcaptureOSS { int fd; ll_ringbuffer_t *ring; - int doCapture; - volatile int killNow; + ATOMIC(ALenum) killNow; althrd_t thread; } ALCcaptureOSS; static int ALCcaptureOSS_recordProc(void *ptr); static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device); -static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, Destruct) +static void ALCcaptureOSS_Destruct(ALCcaptureOSS *self); static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name); -static void ALCcaptureOSS_close(ALCcaptureOSS *self); static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALCboolean, reset) static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self); static void ALCcaptureOSS_stop(ALCcaptureOSS *self); @@ -513,41 +542,55 @@ static int ALCcaptureOSS_recordProc(void *ptr) { ALCcaptureOSS *self = (ALCcaptureOSS*)ptr; ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - int frameSize; + struct timeval timeout; + int frame_size; + fd_set rfds; ssize_t amt; + int sret; SetRTPriority(); althrd_setname(althrd_current(), RECORD_THREAD_NAME); - frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); - while(!self->killNow) + while(!ATOMIC_LOAD_SEQ(&self->killNow)) { ll_ringbuffer_data_t vec[2]; - amt = 0; - if(self->doCapture) + FD_ZERO(&rfds); + FD_SET(self->fd, &rfds); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + sret = select(self->fd+1, &rfds, NULL, NULL, &timeout); + if(sret < 0) { - ll_ringbuffer_get_write_vector(self->ring, vec); - if(vec[0].len > 0) - { - amt = read(self->fd, vec[0].buf, vec[0].len*frameSize); - if(amt < 0) - { - ERR("read failed: %s\n", strerror(errno)); - ALCcaptureOSS_lock(self); - aluHandleDisconnect(device); - ALCcaptureOSS_unlock(self); - break; - } - ll_ringbuffer_write_advance(self->ring, amt/frameSize); - } + if(errno == EINTR) + continue; + ERR("select failed: %s\n", strerror(errno)); + aluHandleDisconnect(device, "Failed to check capture samples: %s", strerror(errno)); + break; } - if(amt == 0) + else if(sret == 0) { - al_nssleep(1000000); + WARN("select timeout\n"); continue; } + + ll_ringbuffer_get_write_vector(self->ring, vec); + if(vec[0].len > 0) + { + amt = read(self->fd, vec[0].buf, vec[0].len*frame_size); + if(amt < 0) + { + ERR("read failed: %s\n", strerror(errno)); + ALCcaptureOSS_lock(self); + aluHandleDisconnect(device, "Failed reading capture samples: %s", strerror(errno)); + ALCcaptureOSS_unlock(self); + break; + } + ll_ringbuffer_write_advance(self->ring, amt/frame_size); + } } return 0; @@ -558,6 +601,21 @@ static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device) { ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); SET_VTABLE2(ALCcaptureOSS, ALCbackend, self); + + self->fd = -1; + self->ring = NULL; + ATOMIC_INIT(&self->killNow, AL_FALSE); +} + +static void ALCcaptureOSS_Destruct(ALCcaptureOSS *self) +{ + if(self->fd != -1) + close(self->fd); + self->fd = -1; + + ll_ringbuffer_free(self->ring); + self->ring = NULL; + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); } static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name) @@ -574,18 +632,26 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name) int ossSpeed; char *err; - if(!name) + if(!name || strcmp(name, dev->handle) == 0) name = dev->handle; else { - while (dev != NULL) + if(!dev->next) + { + ALCossListPopulate(&oss_capture, DSP_CAP_INPUT); + dev = &oss_capture; + } + while(dev != NULL) { if (strcmp(dev->handle, name) == 0) break; dev = dev->next; } - if (dev == NULL) + if(dev == NULL) + { + WARN("Could not find \"%s\" in device list\n", name); return ALC_INVALID_VALUE; + } } self->fd = open(dev->path, O_RDONLY); @@ -615,7 +681,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name) } periods = 4; - numChannels = ChannelsFromDevFmt(device->FmtChans); + numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); frameSize = numChannels * BytesFromDevFmt(device->FmtType); ossSpeed = device->Frequency; log2FragmentSize = log2i(device->UpdateSize * device->NumUpdates * @@ -645,7 +711,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name) } #undef CHECKERR - if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels) + if((int)ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != numChannels) { ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels); close(self->fd); @@ -663,7 +729,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name) return ALC_INVALID_VALUE; } - self->ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates + 1, frameSize); + self->ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates, frameSize, false); if(!self->ring) { ERR("Ring buffer create failed\n"); @@ -672,44 +738,30 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name) return ALC_OUT_OF_MEMORY; } - self->killNow = 0; - if(althrd_create(&self->thread, ALCcaptureOSS_recordProc, self) != althrd_success) - { - ll_ringbuffer_free(self->ring); - self->ring = NULL; - close(self->fd); - self->fd = -1; - return ALC_OUT_OF_MEMORY; - } - - al_string_copy_cstr(&device->DeviceName, name); + alstr_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; } -static void ALCcaptureOSS_close(ALCcaptureOSS *self) -{ - int res; - - self->killNow = 1; - althrd_join(self->thread, &res); - - close(self->fd); - self->fd = -1; - - ll_ringbuffer_free(self->ring); - self->ring = NULL; -} - static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self) { - self->doCapture = 1; + ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE); + if(althrd_create(&self->thread, ALCcaptureOSS_recordProc, self) != althrd_success) + return ALC_FALSE; return ALC_TRUE; } static void ALCcaptureOSS_stop(ALCcaptureOSS *self) { - self->doCapture = 0; + int res; + + if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE)) + return; + + althrd_join(self->thread, &res); + + if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0) + ERR("Error resetting device: %s\n", strerror(errno)); } static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples) @@ -770,33 +822,38 @@ ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self), void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type) { + struct oss_device *cur; switch(type) { case ALL_DEVICE_PROBE: - { - struct oss_device *cur = &oss_playback; - ALCossListFree(cur); - ALCossListPopulate(cur, NULL); - while (cur != NULL) + ALCossListFree(&oss_playback); + ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT); + cur = &oss_playback; + while(cur != NULL) { - AppendAllDevicesList(cur->handle); +#ifdef HAVE_STAT + struct stat buf; + if(stat(cur->path, &buf) == 0) +#endif + AppendAllDevicesList(cur->handle); cur = cur->next; } - } - break; + break; case CAPTURE_DEVICE_PROBE: - { - struct oss_device *cur = &oss_capture; - ALCossListFree(cur); - ALCossListPopulate(NULL, cur); - while (cur != NULL) + ALCossListFree(&oss_capture); + ALCossListPopulate(&oss_capture, DSP_CAP_INPUT); + cur = &oss_capture; + while(cur != NULL) { - AppendCaptureDeviceList(cur->handle); +#ifdef HAVE_STAT + struct stat buf; + if(stat(cur->path, &buf) == 0) +#endif + AppendCaptureDeviceList(cur->handle); cur = cur->next; } - } - break; + break; } } diff --git a/Engine/lib/openal-soft/Alc/backends/portaudio.c b/Engine/lib/openal-soft/Alc/backends/portaudio.c index 1dbca9416..9b0d34878 100644 --- a/Engine/lib/openal-soft/Alc/backends/portaudio.c +++ b/Engine/lib/openal-soft/Alc/backends/portaudio.c @@ -26,6 +26,8 @@ #include "alMain.h" #include "alu.h" +#include "alconfig.h" +#include "ringbuffer.h" #include "compat.h" #include "backends/base.h" @@ -139,7 +141,6 @@ static int ALCportPlayback_WriteCallback(const void *inputBuffer, void *outputBu static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device); static void ALCportPlayback_Destruct(ALCportPlayback *self); static ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name); -static void ALCportPlayback_close(ALCportPlayback *self); static ALCboolean ALCportPlayback_reset(ALCportPlayback *self); static ALCboolean ALCportPlayback_start(ALCportPlayback *self); static void ALCportPlayback_stop(ALCportPlayback *self); @@ -163,8 +164,9 @@ static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device) static void ALCportPlayback_Destruct(ALCportPlayback *self) { - if(self->stream) - Pa_CloseStream(self->stream); + PaError err = self->stream ? Pa_CloseStream(self->stream) : paNoError; + if(err != paNoError) + ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); self->stream = NULL; ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); @@ -177,7 +179,9 @@ static int ALCportPlayback_WriteCallback(const void *UNUSED(inputBuffer), void * { ALCportPlayback *self = userData; + ALCportPlayback_lock(self); aluMixData(STATIC_CAST(ALCbackend, self)->mDevice, outputBuffer, framesPerBuffer); + ALCportPlayback_unlock(self); return 0; } @@ -243,20 +247,12 @@ retry_open: return ALC_INVALID_VALUE; } - al_string_copy_cstr(&device->DeviceName, name); + alstr_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; } -static void ALCportPlayback_close(ALCportPlayback *self) -{ - PaError err = Pa_CloseStream(self->stream); - if(err != paNoError) - ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); - self->stream = NULL; -} - static ALCboolean ALCportPlayback_reset(ALCportPlayback *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; @@ -334,7 +330,6 @@ static int ALCportCapture_ReadCallback(const void *inputBuffer, void *outputBuff static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device); static void ALCportCapture_Destruct(ALCportCapture *self); static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name); -static void ALCportCapture_close(ALCportCapture *self); static DECLARE_FORWARD(ALCportCapture, ALCbackend, ALCboolean, reset) static ALCboolean ALCportCapture_start(ALCportCapture *self); static void ALCportCapture_stop(ALCportCapture *self); @@ -354,16 +349,17 @@ static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device) SET_VTABLE2(ALCportCapture, ALCbackend, self); self->stream = NULL; + self->ring = NULL; } static void ALCportCapture_Destruct(ALCportCapture *self) { - if(self->stream) - Pa_CloseStream(self->stream); + PaError err = self->stream ? Pa_CloseStream(self->stream) : paNoError; + if(err != paNoError) + ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); self->stream = NULL; - if(self->ring) - ll_ringbuffer_free(self->ring); + ll_ringbuffer_free(self->ring); self->ring = NULL; ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); @@ -397,9 +393,9 @@ static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name) samples = device->UpdateSize * device->NumUpdates; samples = maxu(samples, 100 * device->Frequency / 1000); - frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); - self->ring = ll_ringbuffer_create(samples, frame_size); + self->ring = ll_ringbuffer_create(samples, frame_size, false); if(self->ring == NULL) return ALC_INVALID_VALUE; self->params.device = -1; @@ -431,7 +427,7 @@ static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name) ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType)); return ALC_INVALID_VALUE; } - self->params.channelCount = ChannelsFromDevFmt(device->FmtChans); + self->params.channelCount = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); err = Pa_OpenStream(&self->stream, &self->params, NULL, device->Frequency, paFramesPerBufferUnspecified, paNoFlag, @@ -443,22 +439,11 @@ static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name) return ALC_INVALID_VALUE; } - al_string_copy_cstr(&device->DeviceName, name); + alstr_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; } -static void ALCportCapture_close(ALCportCapture *self) -{ - PaError err = Pa_CloseStream(self->stream); - if(err != paNoError) - ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); - self->stream = NULL; - - ll_ringbuffer_free(self->ring); - self->ring = NULL; -} - static ALCboolean ALCportCapture_start(ALCportCapture *self) { diff --git a/Engine/lib/openal-soft/Alc/backends/pulseaudio.c b/Engine/lib/openal-soft/Alc/backends/pulseaudio.c index f46386e4f..74d1a1492 100644 --- a/Engine/lib/openal-soft/Alc/backends/pulseaudio.c +++ b/Engine/lib/openal-soft/Alc/backends/pulseaudio.c @@ -25,6 +25,7 @@ #include "alMain.h" #include "alu.h" +#include "alconfig.h" #include "threads.h" #include "compat.h" @@ -182,6 +183,8 @@ static ALCboolean pulse_load(void) #ifdef HAVE_DYNLOAD if(!pa_handle) { + al_string missing_funcs = AL_STRING_INIT_STATIC(); + #ifdef _WIN32 #define PALIB "libpulse-0.dll" #elif defined(__APPLE__) && defined(__MACH__) @@ -191,12 +194,16 @@ static ALCboolean pulse_load(void) #endif pa_handle = LoadLib(PALIB); if(!pa_handle) + { + WARN("Failed to load %s\n", PALIB); return ALC_FALSE; + } #define LOAD_FUNC(x) do { \ p##x = GetSymbol(pa_handle, #x); \ if(!(p##x)) { \ ret = ALC_FALSE; \ + alstr_append_cstr(&missing_funcs, "\n" #x); \ } \ } while(0) LOAD_FUNC(pa_context_unref); @@ -270,9 +277,11 @@ static ALCboolean pulse_load(void) if(ret == ALC_FALSE) { + WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs)); CloseLib(pa_handle); pa_handle = NULL; } + alstr_reset(&missing_funcs); } #endif /* HAVE_DYNLOAD */ return ret; @@ -325,18 +334,20 @@ static void wait_for_operation(pa_operation *op, pa_threaded_mainloop *loop) static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent) { const char *name = "OpenAL Soft"; - char path_name[PATH_MAX]; + al_string binname = AL_STRING_INIT_STATIC(); pa_context_state_t state; pa_context *context; int err; - if(pa_get_binary_name(path_name, sizeof(path_name))) - name = pa_path_get_filename(path_name); + GetProcBinary(NULL, &binname); + if(!alstr_empty(binname)) + name = alstr_get_cstr(binname); context = pa_context_new(pa_threaded_mainloop_get_api(loop), name); if(!context) { ERR("pa_context_new() failed\n"); + alstr_reset(&binname); return NULL; } @@ -363,9 +374,10 @@ static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent) if(!silent) ERR("Context did not connect: %s\n", pa_strerror(err)); pa_context_unref(context); - return NULL; + context = NULL; } + alstr_reset(&binname); return context; } @@ -460,7 +472,7 @@ typedef struct ALCpulsePlayback { pa_stream *stream; pa_context *context; - volatile ALboolean killNow; + ATOMIC(ALenum) killNow; althrd_t thread; } ALCpulsePlayback; @@ -483,7 +495,6 @@ static int ALCpulsePlayback_mixerProc(void *ptr); static void ALCpulsePlayback_Construct(ALCpulsePlayback *self, ALCdevice *device); static void ALCpulsePlayback_Destruct(ALCpulsePlayback *self); static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name); -static void ALCpulsePlayback_close(ALCpulsePlayback *self); static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self); static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self); static void ALCpulsePlayback_stop(ALCpulsePlayback *self); @@ -502,11 +513,20 @@ static void ALCpulsePlayback_Construct(ALCpulsePlayback *self, ALCdevice *device ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); SET_VTABLE2(ALCpulsePlayback, ALCbackend, self); + self->loop = NULL; AL_STRING_INIT(self->device_name); + ATOMIC_INIT(&self->killNow, AL_TRUE); } static void ALCpulsePlayback_Destruct(ALCpulsePlayback *self) { + if(self->loop) + { + pulse_close(self->loop, self->context, self->stream); + self->loop = NULL; + self->context = NULL; + self->stream = NULL; + } AL_STRING_DEINIT(self->device_name); ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); } @@ -525,7 +545,7 @@ static void ALCpulsePlayback_deviceCallback(pa_context *UNUSED(context), const p return; } -#define MATCH_INFO_NAME(iter) (al_string_cmp_cstr((iter)->device_name, info->name) == 0) +#define MATCH_INFO_NAME(iter) (alstr_cmp_cstr((iter)->device_name, info->name) == 0) VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_INFO_NAME); if(iter != VECTOR_END(PlaybackDevices)) return; #undef MATCH_INFO_NAME @@ -533,27 +553,27 @@ static void ALCpulsePlayback_deviceCallback(pa_context *UNUSED(context), const p AL_STRING_INIT(entry.name); AL_STRING_INIT(entry.device_name); - al_string_copy_cstr(&entry.device_name, info->name); + alstr_copy_cstr(&entry.device_name, info->name); count = 0; while(1) { - al_string_copy_cstr(&entry.name, info->description); + alstr_copy_cstr(&entry.name, info->description); if(count != 0) { char str[64]; snprintf(str, sizeof(str), " #%d", count+1); - al_string_append_cstr(&entry.name, str); + alstr_append_cstr(&entry.name, str); } -#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0) +#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0) VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_ENTRY); if(iter == VECTOR_END(PlaybackDevices)) break; #undef MATCH_ENTRY count++; } - TRACE("Got device \"%s\", \"%s\"\n", al_string_get_cstr(entry.name), al_string_get_cstr(entry.device_name)); + TRACE("Got device \"%s\", \"%s\"\n", alstr_get_cstr(entry.name), alstr_get_cstr(entry.device_name)); VECTOR_PUSH_BACK(PlaybackDevices, entry); } @@ -618,6 +638,11 @@ static void ALCpulsePlayback_bufferAttrCallback(pa_stream *stream, void *pdata) self->attr = *pa_stream_get_buffer_attr(stream); TRACE("minreq=%d, tlength=%d, prebuf=%d\n", self->attr.minreq, self->attr.tlength, self->attr.prebuf); + /* FIXME: Update the device's UpdateSize (and/or NumUpdates) 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. + */ } static void ALCpulsePlayback_contextStateCallback(pa_context *context, void *pdata) @@ -626,7 +651,7 @@ static void ALCpulsePlayback_contextStateCallback(pa_context *context, void *pda if(pa_context_get_state(context) == PA_CONTEXT_FAILED) { ERR("Received context failure!\n"); - aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice); + aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice, "Playback state failure"); } pa_threaded_mainloop_signal(self->loop, 0); } @@ -637,7 +662,7 @@ static void ALCpulsePlayback_streamStateCallback(pa_stream *stream, void *pdata) if(pa_stream_get_state(stream) == PA_STREAM_FAILED) { ERR("Received stream failure!\n"); - aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice); + aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice, "Playback stream failure"); } pa_threaded_mainloop_signal(self->loop, 0); } @@ -729,7 +754,7 @@ static void ALCpulsePlayback_sinkNameCallback(pa_context *UNUSED(context), const return; } - al_string_copy_cstr(&device->DeviceName, info->description); + alstr_copy_cstr(&device->DeviceName, info->description); } @@ -737,9 +762,9 @@ static void ALCpulsePlayback_streamMovedCallback(pa_stream *stream, void *pdata) { ALCpulsePlayback *self = pdata; - al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(stream)); + alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(stream)); - TRACE("Stream moved to %s\n", al_string_get_cstr(self->device_name)); + TRACE("Stream moved to %s\n", alstr_get_cstr(self->device_name)); } @@ -751,6 +776,13 @@ static pa_stream *ALCpulsePlayback_connectStream(const char *device_name, pa_stream_state_t state; pa_stream *stream; + if(!device_name) + { + device_name = getenv("ALSOFT_PULSE_DEFAULT"); + if(device_name && !device_name[0]) + device_name = NULL; + } + stream = pa_stream_new_with_proplist(context, "Playback Stream", spec, chanmap, prop_filter); if(!stream) { @@ -789,7 +821,6 @@ static int ALCpulsePlayback_mixerProc(void *ptr) ALCpulsePlayback *self = ptr; ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; ALuint buffer_size; - ALint update_size; size_t frame_size; ssize_t len; @@ -798,18 +829,35 @@ static int ALCpulsePlayback_mixerProc(void *ptr) pa_threaded_mainloop_lock(self->loop); frame_size = pa_frame_size(&self->spec); - update_size = device->UpdateSize * frame_size; - /* Sanitize buffer metrics, in case we actually have less than what we - * asked for. */ - buffer_size = minu(update_size*device->NumUpdates, self->attr.tlength); - update_size = minu(update_size, buffer_size/2); - do { - len = pa_stream_writable_size(self->stream) - self->attr.tlength + - buffer_size; - if(len < update_size) + while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) && + ATOMIC_LOAD(&device->Connected, almemory_order_acquire)) + { + void *buf; + int ret; + + len = pa_stream_writable_size(self->stream); + if(len < 0) { - if(pa_stream_is_corked(self->stream) == 1) + ERR("Failed to get writable size: %ld", (long)len); + aluHandleDisconnect(device, "Failed to get writable size: %ld", (long)len); + break; + } + + /* Make sure we're going to write at least 2 'periods' (minreqs), in + * case the server increased it since starting playback. Also round up + * the number of writable periods if it's not an integer count. + */ + buffer_size = maxu((self->attr.tlength + self->attr.minreq/2) / self->attr.minreq, 2) * + self->attr.minreq; + + /* NOTE: This assumes pa_stream_writable_size returns between 0 and + * tlength, else there will be more latency than intended. + */ + len = mini(len - (ssize_t)self->attr.tlength, 0) + buffer_size; + if(len < (int32_t)self->attr.minreq) + { + if(pa_stream_is_corked(self->stream)) { pa_operation *o; o = pa_stream_cork(self->stream, 0, NULL, NULL); @@ -818,26 +866,17 @@ static int ALCpulsePlayback_mixerProc(void *ptr) pa_threaded_mainloop_wait(self->loop); continue; } - len -= len%update_size; - while(len > 0) - { - size_t newlen = len; - void *buf; - pa_free_cb_t free_func = NULL; + len -= len%self->attr.minreq; + len -= len%frame_size; - if(pa_stream_begin_write(self->stream, &buf, &newlen) < 0) - { - buf = pa_xmalloc(newlen); - free_func = pa_xfree; - } + buf = pa_xmalloc(len); - aluMixData(device, buf, newlen/frame_size); + aluMixData(device, buf, len/frame_size); - pa_stream_write(self->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE); - len -= newlen; - } - } while(!self->killNow && device->Connected); + ret = pa_stream_write(self->stream, buf, len, pa_xfree, 0, PA_SEEK_RELATIVE); + if(ret != PA_OK) ERR("Failed to write to stream: %d, %s\n", ret, pa_strerror(ret)); + } pa_threaded_mainloop_unlock(self->loop); return 0; @@ -858,12 +897,12 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name if(VECTOR_SIZE(PlaybackDevices) == 0) ALCpulsePlayback_probeDevices(); -#define MATCH_NAME(iter) (al_string_cmp_cstr((iter)->name, name) == 0) +#define MATCH_NAME(iter) (alstr_cmp_cstr((iter)->name, name) == 0) VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME); #undef MATCH_NAME if(iter == VECTOR_END(PlaybackDevices)) return ALC_INVALID_VALUE; - pulse_name = al_string_get_cstr(iter->device_name); + pulse_name = alstr_get_cstr(iter->device_name); dev_name = iter->name; } @@ -894,11 +933,11 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name } pa_stream_set_moved_callback(self->stream, ALCpulsePlayback_streamMovedCallback, self); - al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream)); - if(al_string_empty(dev_name)) + alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream)); + if(alstr_empty(dev_name)) { pa_operation *o = pa_context_get_sink_info_by_name( - self->context, al_string_get_cstr(self->device_name), + self->context, alstr_get_cstr(self->device_name), ALCpulsePlayback_sinkNameCallback, self ); wait_for_operation(o, self->loop); @@ -906,7 +945,7 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name else { ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; - al_string_copy(&device->DeviceName, dev_name); + alstr_copy(&device->DeviceName, dev_name); } pa_threaded_mainloop_unlock(self->loop); @@ -914,16 +953,6 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name return ALC_NO_ERROR; } -static void ALCpulsePlayback_close(ALCpulsePlayback *self) -{ - pulse_close(self->loop, self->context, self->stream); - self->loop = NULL; - self->context = NULL; - self->stream = NULL; - - al_string_clear(&self->device_name); -} - static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self) { ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; @@ -931,7 +960,6 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self) const char *mapname = NULL; pa_channel_map chanmap; pa_operation *o; - ALuint len; pa_threaded_mainloop_lock(self->loop); @@ -946,11 +974,11 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self) self->stream = NULL; } - o = pa_context_get_sink_info_by_name(self->context, al_string_get_cstr(self->device_name), + o = pa_context_get_sink_info_by_name(self->context, alstr_get_cstr(self->device_name), ALCpulsePlayback_sinkInfoCallback, self); wait_for_operation(o, self->loop); - if(GetConfigValueBool(al_string_get_cstr(device->DeviceName), "pulse", "fix-rate", 0) || + if(GetConfigValueBool(alstr_get_cstr(device->DeviceName), "pulse", "fix-rate", 0) || !(device->Flags&DEVICE_FREQUENCY_REQUEST)) flags |= PA_STREAM_FIX_RATE; flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE; @@ -984,7 +1012,7 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self) break; } self->spec.rate = device->Frequency; - self->spec.channels = ChannelsFromDevFmt(device->FmtChans); + self->spec.channels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); if(pa_sample_spec_valid(&self->spec) == 0) { @@ -998,9 +1026,7 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self) case DevFmtMono: mapname = "mono"; break; - case DevFmtAmbi1: - case DevFmtAmbi2: - case DevFmtAmbi3: + case DevFmtAmbi3D: device->FmtChans = DevFmtStereo; /*fall-through*/ case DevFmtStereo: @@ -1036,9 +1062,9 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self) self->attr.tlength = self->attr.minreq * maxu(device->NumUpdates, 2); self->attr.maxlength = -1; - self->stream = ALCpulsePlayback_connectStream(al_string_get_cstr(self->device_name), - self->loop, self->context, flags, - &self->attr, &self->spec, &chanmap); + self->stream = ALCpulsePlayback_connectStream(alstr_get_cstr(self->device_name), + self->loop, self->context, flags, &self->attr, &self->spec, &chanmap + ); if(!self->stream) { pa_threaded_mainloop_unlock(self->loop); @@ -1072,11 +1098,10 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self) pa_stream_set_buffer_attr_callback(self->stream, ALCpulsePlayback_bufferAttrCallback, self); ALCpulsePlayback_bufferAttrCallback(self->stream, self); - len = self->attr.minreq / pa_frame_size(&self->spec); - device->NumUpdates = (ALuint)clampd( - (ALdouble)device->NumUpdates/len*device->UpdateSize + 0.5, 2.0, 16.0 + device->NumUpdates = (ALuint)clampu64( + (self->attr.tlength + self->attr.minreq/2) / self->attr.minreq, 2, 16 ); - device->UpdateSize = len; + device->UpdateSize = self->attr.minreq / pa_frame_size(&self->spec); /* 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 @@ -1086,7 +1111,7 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self) */ if(self->attr.prebuf != 0) { - len = self->attr.prebuf / pa_frame_size(&self->spec); + ALuint len = self->attr.prebuf / pa_frame_size(&self->spec); if(len <= device->UpdateSize*device->NumUpdates) ERR("Non-0 prebuf, %u samples (%u bytes), device has %u samples\n", len, self->attr.prebuf, device->UpdateSize*device->NumUpdates); @@ -1104,7 +1129,7 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self) static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self) { - self->killNow = AL_FALSE; + ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release); if(althrd_create(&self->thread, ALCpulsePlayback_mixerProc, self) != althrd_success) return ALC_FALSE; return ALC_TRUE; @@ -1115,10 +1140,9 @@ static void ALCpulsePlayback_stop(ALCpulsePlayback *self) pa_operation *o; int res; - if(!self->stream || self->killNow) + if(!self->stream || ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel)) return; - self->killNow = AL_TRUE; /* Signal the main loop in case PulseAudio isn't sending us audio requests * (e.g. if the device is suspended). We need to lock the mainloop in case * the mixer is between checking the killNow flag but before waiting for @@ -1140,13 +1164,16 @@ static void ALCpulsePlayback_stop(ALCpulsePlayback *self) static ClockLatency ALCpulsePlayback_getClockLatency(ALCpulsePlayback *self) { - pa_usec_t latency = 0; ClockLatency ret; + pa_usec_t latency; int neg, err; pa_threaded_mainloop_lock(self->loop); ret.ClockTime = GetDeviceClockTime(STATIC_CAST(ALCbackend,self)->mDevice); - if((err=pa_stream_get_latency(self->stream, &latency, &neg)) != 0) + err = pa_stream_get_latency(self->stream, &latency, &neg); + pa_threaded_mainloop_unlock(self->loop); + + 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 @@ -1157,9 +1184,9 @@ static ClockLatency ALCpulsePlayback_getClockLatency(ALCpulsePlayback *self) latency = 0; neg = 0; } - if(neg) latency = 0; - ret.Latency = minu64(latency, U64(0xffffffffffffffff)/1000) * 1000; - pa_threaded_mainloop_unlock(self->loop); + else if(UNLIKELY(neg)) + latency = 0; + ret.Latency = (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000; return ret; } @@ -1211,7 +1238,6 @@ static pa_stream *ALCpulseCapture_connectStream(const char *device_name, static void ALCpulseCapture_Construct(ALCpulseCapture *self, ALCdevice *device); static void ALCpulseCapture_Destruct(ALCpulseCapture *self); static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name); -static void ALCpulseCapture_close(ALCpulseCapture *self); static DECLARE_FORWARD(ALCpulseCapture, ALCbackend, ALCboolean, reset) static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self); static void ALCpulseCapture_stop(ALCpulseCapture *self); @@ -1230,11 +1256,19 @@ static void ALCpulseCapture_Construct(ALCpulseCapture *self, ALCdevice *device) ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); SET_VTABLE2(ALCpulseCapture, ALCbackend, self); + self->loop = NULL; AL_STRING_INIT(self->device_name); } static void ALCpulseCapture_Destruct(ALCpulseCapture *self) { + if(self->loop) + { + pulse_close(self->loop, self->context, self->stream); + self->loop = NULL; + self->context = NULL; + self->stream = NULL; + } AL_STRING_DEINIT(self->device_name); ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); } @@ -1253,7 +1287,7 @@ static void ALCpulseCapture_deviceCallback(pa_context *UNUSED(context), const pa return; } -#define MATCH_INFO_NAME(iter) (al_string_cmp_cstr((iter)->device_name, info->name) == 0) +#define MATCH_INFO_NAME(iter) (alstr_cmp_cstr((iter)->device_name, info->name) == 0) VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_INFO_NAME); if(iter != VECTOR_END(CaptureDevices)) return; #undef MATCH_INFO_NAME @@ -1261,27 +1295,27 @@ static void ALCpulseCapture_deviceCallback(pa_context *UNUSED(context), const pa AL_STRING_INIT(entry.name); AL_STRING_INIT(entry.device_name); - al_string_copy_cstr(&entry.device_name, info->name); + alstr_copy_cstr(&entry.device_name, info->name); count = 0; while(1) { - al_string_copy_cstr(&entry.name, info->description); + alstr_copy_cstr(&entry.name, info->description); if(count != 0) { char str[64]; snprintf(str, sizeof(str), " #%d", count+1); - al_string_append_cstr(&entry.name, str); + alstr_append_cstr(&entry.name, str); } -#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0) +#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0) VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_ENTRY); if(iter == VECTOR_END(CaptureDevices)) break; #undef MATCH_ENTRY count++; } - TRACE("Got device \"%s\", \"%s\"\n", al_string_get_cstr(entry.name), al_string_get_cstr(entry.device_name)); + TRACE("Got device \"%s\", \"%s\"\n", alstr_get_cstr(entry.name), alstr_get_cstr(entry.device_name)); VECTOR_PUSH_BACK(CaptureDevices, entry); } @@ -1346,7 +1380,7 @@ static void ALCpulseCapture_contextStateCallback(pa_context *context, void *pdat if(pa_context_get_state(context) == PA_CONTEXT_FAILED) { ERR("Received context failure!\n"); - aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice); + aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice, "Capture state failure"); } pa_threaded_mainloop_signal(self->loop, 0); } @@ -1357,7 +1391,7 @@ static void ALCpulseCapture_streamStateCallback(pa_stream *stream, void *pdata) if(pa_stream_get_state(stream) == PA_STREAM_FAILED) { ERR("Received stream failure!\n"); - aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice); + aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice, "Capture stream failure"); } pa_threaded_mainloop_signal(self->loop, 0); } @@ -1374,7 +1408,7 @@ static void ALCpulseCapture_sourceNameCallback(pa_context *UNUSED(context), cons return; } - al_string_copy_cstr(&device->DeviceName, info->description); + alstr_copy_cstr(&device->DeviceName, info->description); } @@ -1382,9 +1416,9 @@ static void ALCpulseCapture_streamMovedCallback(pa_stream *stream, void *pdata) { ALCpulseCapture *self = pdata; - al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(stream)); + alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(stream)); - TRACE("Stream moved to %s\n", al_string_get_cstr(self->device_name)); + TRACE("Stream moved to %s\n", alstr_get_cstr(self->device_name)); } @@ -1434,6 +1468,7 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name) ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; const char *pulse_name = NULL; pa_stream_flags_t flags = 0; + const char *mapname = NULL; pa_channel_map chanmap; ALuint samples; @@ -1444,13 +1479,13 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name) if(VECTOR_SIZE(CaptureDevices) == 0) ALCpulseCapture_probeDevices(); -#define MATCH_NAME(iter) (al_string_cmp_cstr((iter)->name, name) == 0) +#define MATCH_NAME(iter) (alstr_cmp_cstr((iter)->name, name) == 0) VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME); #undef MATCH_NAME if(iter == VECTOR_END(CaptureDevices)) return ALC_INVALID_VALUE; - pulse_name = al_string_get_cstr(iter->device_name); - al_string_copy(&device->DeviceName, iter->name); + pulse_name = alstr_get_cstr(iter->device_name); + alstr_copy(&device->DeviceName, iter->name); } if(!pulse_open(&self->loop, &self->context, ALCpulseCapture_contextStateCallback, self)) @@ -1458,9 +1493,6 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name) pa_threaded_mainloop_lock(self->loop); - self->spec.rate = device->Frequency; - self->spec.channels = ChannelsFromDevFmt(device->FmtChans); - switch(device->FmtType) { case DevFmtUByte: @@ -1483,6 +1515,44 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name) goto fail; } + switch(device->FmtChans) + { + case DevFmtMono: + mapname = "mono"; + break; + case DevFmtStereo: + mapname = "front-left,front-right"; + break; + case DevFmtQuad: + mapname = "front-left,front-right,rear-left,rear-right"; + break; + case DevFmtX51: + mapname = "front-left,front-right,front-center,lfe,side-left,side-right"; + break; + case DevFmtX51Rear: + mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right"; + break; + case DevFmtX61: + mapname = "front-left,front-right,front-center,lfe,rear-center,side-left,side-right"; + break; + case DevFmtX71: + mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right"; + break; + case DevFmtAmbi3D: + ERR("%s capture samples not supported\n", DevFmtChannelsString(device->FmtChans)); + pa_threaded_mainloop_unlock(self->loop); + goto fail; + } + if(!pa_channel_map_parse(&chanmap, mapname)) + { + ERR("Failed to build channel map for %s\n", DevFmtChannelsString(device->FmtChans)); + pa_threaded_mainloop_unlock(self->loop); + return ALC_FALSE; + } + + self->spec.rate = device->Frequency; + self->spec.channels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); + if(pa_sample_spec_valid(&self->spec) == 0) { ERR("Invalid sample format\n"); @@ -1512,9 +1582,9 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name) flags |= PA_STREAM_DONT_MOVE; TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)"); - self->stream = ALCpulseCapture_connectStream(pulse_name, self->loop, self->context, - flags, &self->attr, &self->spec, - &chanmap); + self->stream = ALCpulseCapture_connectStream(pulse_name, + self->loop, self->context, flags, &self->attr, &self->spec, &chanmap + ); if(!self->stream) { pa_threaded_mainloop_unlock(self->loop); @@ -1523,11 +1593,11 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name) pa_stream_set_moved_callback(self->stream, ALCpulseCapture_streamMovedCallback, self); pa_stream_set_state_callback(self->stream, ALCpulseCapture_streamStateCallback, self); - al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream)); - if(al_string_empty(device->DeviceName)) + alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream)); + if(alstr_empty(device->DeviceName)) { pa_operation *o = pa_context_get_source_info_by_name( - self->context, al_string_get_cstr(self->device_name), + self->context, alstr_get_cstr(self->device_name), ALCpulseCapture_sourceNameCallback, self ); wait_for_operation(o, self->loop); @@ -1545,30 +1615,23 @@ fail: return ALC_INVALID_VALUE; } -static void ALCpulseCapture_close(ALCpulseCapture *self) -{ - pulse_close(self->loop, self->context, self->stream); - self->loop = NULL; - self->context = NULL; - self->stream = NULL; - - al_string_clear(&self->device_name); -} - static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self) { pa_operation *o; + pa_threaded_mainloop_lock(self->loop); o = pa_stream_cork(self->stream, 0, stream_success_callback, self->loop); wait_for_operation(o, self->loop); - + pa_threaded_mainloop_unlock(self->loop); return ALC_TRUE; } static void ALCpulseCapture_stop(ALCpulseCapture *self) { pa_operation *o; + pa_threaded_mainloop_lock(self->loop); o = pa_stream_cork(self->stream, 1, stream_success_callback, self->loop); wait_for_operation(o, self->loop); + pa_threaded_mainloop_unlock(self->loop); } static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *buffer, ALCuint samples) @@ -1579,6 +1642,7 @@ static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *bu /* Capture is done in fragment-sized chunks, so we loop until we get all * that's available */ self->last_readable -= todo; + pa_threaded_mainloop_lock(self->loop); while(todo > 0) { size_t rem = todo; @@ -1590,14 +1654,15 @@ static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *bu state = pa_stream_get_state(self->stream); if(!PA_STREAM_IS_GOOD(state)) { - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Bad capture state: %u", state); break; } if(pa_stream_peek(self->stream, &self->cap_store, &self->cap_len) < 0) { ERR("pa_stream_peek() failed: %s\n", pa_strerror(pa_context_errno(self->context))); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Failed retrieving capture samples: %s", + pa_strerror(pa_context_errno(self->context))); break; } self->cap_remain = self->cap_len; @@ -1618,6 +1683,7 @@ static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *bu self->cap_len = 0; } } + pa_threaded_mainloop_unlock(self->loop); if(todo > 0) memset(buffer, ((device->FmtType==DevFmtUByte) ? 0x80 : 0), todo); @@ -1629,16 +1695,19 @@ static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self) ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; size_t readable = self->cap_remain; - if(device->Connected) + if(ATOMIC_LOAD(&device->Connected, almemory_order_acquire)) { - ssize_t got = pa_stream_readable_size(self->stream); + ssize_t got; + pa_threaded_mainloop_lock(self->loop); + got = pa_stream_readable_size(self->stream); if(got < 0) { ERR("pa_stream_readable_size() failed: %s\n", pa_strerror(got)); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Failed getting readable size: %s", pa_strerror(got)); } else if((size_t)got > self->cap_len) readable += got - self->cap_len; + pa_threaded_mainloop_unlock(self->loop); } if(self->last_readable < readable) @@ -1649,21 +1718,24 @@ static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self) static ClockLatency ALCpulseCapture_getClockLatency(ALCpulseCapture *self) { - pa_usec_t latency = 0; ClockLatency ret; + pa_usec_t latency; int neg, err; pa_threaded_mainloop_lock(self->loop); ret.ClockTime = GetDeviceClockTime(STATIC_CAST(ALCbackend,self)->mDevice); - if((err=pa_stream_get_latency(self->stream, &latency, &neg)) != 0) + err = pa_stream_get_latency(self->stream, &latency, &neg); + pa_threaded_mainloop_unlock(self->loop); + + if(UNLIKELY(err != 0)) { ERR("Failed to get stream latency: 0x%x\n", err); latency = 0; neg = 0; } - if(neg) latency = 0; - ret.Latency = minu64(latency, U64(0xffffffffffffffff)/1000) * 1000; - pa_threaded_mainloop_unlock(self->loop); + else if(UNLIKELY(neg)) + latency = 0; + ret.Latency = (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000; return ret; } @@ -1769,14 +1841,14 @@ static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), e { case ALL_DEVICE_PROBE: ALCpulsePlayback_probeDevices(); -#define APPEND_ALL_DEVICES_LIST(e) AppendAllDevicesList(al_string_get_cstr((e)->name)) +#define APPEND_ALL_DEVICES_LIST(e) AppendAllDevicesList(alstr_get_cstr((e)->name)) VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_ALL_DEVICES_LIST); #undef APPEND_ALL_DEVICES_LIST break; case CAPTURE_DEVICE_PROBE: ALCpulseCapture_probeDevices(); -#define APPEND_CAPTURE_DEVICE_LIST(e) AppendCaptureDeviceList(al_string_get_cstr((e)->name)) +#define APPEND_CAPTURE_DEVICE_LIST(e) AppendCaptureDeviceList(alstr_get_cstr((e)->name)) VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_CAPTURE_DEVICE_LIST); #undef APPEND_CAPTURE_DEVICE_LIST break; diff --git a/Engine/lib/openal-soft/Alc/backends/qsa.c b/Engine/lib/openal-soft/Alc/backends/qsa.c index b7923517a..8f87779ba 100644 --- a/Engine/lib/openal-soft/Alc/backends/qsa.c +++ b/Engine/lib/openal-soft/Alc/backends/qsa.c @@ -33,6 +33,8 @@ #include "alu.h" #include "threads.h" +#include "backends/base.h" + typedef struct { snd_pcm_t* pcmHandle; @@ -44,7 +46,7 @@ typedef struct { ALvoid* buffer; ALsizei size; - volatile int killNow; + ATOMIC(ALenum) killNow; althrd_t thread; } qsa_data; @@ -157,17 +159,39 @@ static void deviceList(int type, vector_DevMap *devmap) } -FORCE_ALIGN static int qsa_proc_playback(void* ptr) +/* Wrappers to use an old-style backend with the new interface. */ +typedef struct PlaybackWrapper { + DERIVE_FROM_TYPE(ALCbackend); + qsa_data *ExtraData; +} PlaybackWrapper; + +static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device); +static void PlaybackWrapper_Destruct(PlaybackWrapper *self); +static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name); +static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self); +static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self); +static void PlaybackWrapper_stop(PlaybackWrapper *self); +static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint) +static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples) +static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ClockLatency, getClockLatency) +static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock) +static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper) +DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper); + + +FORCE_ALIGN static int qsa_proc_playback(void *ptr) { - ALCdevice* device=(ALCdevice*)ptr; - qsa_data* data=(qsa_data*)device->ExtraData; - char* write_ptr; - int avail; + PlaybackWrapper *self = ptr; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; + qsa_data *data = self->ExtraData; snd_pcm_channel_status_t status; struct sched_param param; - fd_set wfds; - int selectret; struct timeval timeout; + char* write_ptr; + fd_set wfds; + ALint len; + int sret; SetRTPriority(); althrd_setname(althrd_current(), MIXER_THREAD_NAME); @@ -177,72 +201,69 @@ FORCE_ALIGN static int qsa_proc_playback(void* ptr) param.sched_priority=param.sched_curpriority+1; SchedSet(0, 0, SCHED_NOCHANGE, ¶m); - ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + const ALint frame_size = FrameSizeFromDevFmt( + device->FmtChans, device->FmtType, device->AmbiOrder + ); - while (!data->killNow) + V0(device->Backend,lock)(); + while(!ATOMIC_LOAD(&data->killNow, almemory_order_acquire)) { - ALint len=data->size; - write_ptr=data->buffer; + FD_ZERO(&wfds); + FD_SET(data->audio_fd, &wfds); + timeout.tv_sec=2; + timeout.tv_usec=0; - avail=len/frame_size; - aluMixData(device, write_ptr, avail); - - while (len>0 && !data->killNow) + /* Select also works like time slice to OS */ + V0(device->Backend,unlock)(); + sret = select(data->audio_fd+1, NULL, &wfds, NULL, &timeout); + V0(device->Backend,lock)(); + if(sret == -1) { - FD_ZERO(&wfds); - FD_SET(data->audio_fd, &wfds); - timeout.tv_sec=2; - timeout.tv_usec=0; + ERR("select error: %s\n", strerror(errno)); + aluHandleDisconnect(device, "Failed waiting for playback buffer: %s", strerror(errno)); + break; + } + if(sret == 0) + { + ERR("select timeout\n"); + continue; + } - /* Select also works like time slice to OS */ - selectret=select(data->audio_fd+1, NULL, &wfds, NULL, &timeout); - switch (selectret) + len = data->size; + write_ptr = data->buffer; + aluMixData(device, write_ptr, len/frame_size); + while(len>0 && !ATOMIC_LOAD(&data->killNow, almemory_order_acquire)) + { + int wrote = snd_pcm_plugin_write(data->pcmHandle, write_ptr, len); + if(wrote <= 0) { - case -1: - aluHandleDisconnect(device); - return 1; - case 0: - break; - default: - if (FD_ISSET(data->audio_fd, &wfds)) - { - break; - } - break; - } - - int wrote=snd_pcm_plugin_write(data->pcmHandle, write_ptr, len); - - if (wrote<=0) - { - if ((errno==EAGAIN) || (errno==EWOULDBLOCK)) - { + if(errno==EAGAIN || errno==EWOULDBLOCK) continue; - } - memset(&status, 0, sizeof (status)); - status.channel=SND_PCM_CHANNEL_PLAYBACK; + 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(status.status == SND_PCM_STATUS_UNDERRUN || + status.status == SND_PCM_STATUS_READY) { - if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0) + if(snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK) < 0) { - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Playback recovery failed"); break; } } } else { - write_ptr+=wrote; - len-=wrote; + write_ptr += wrote; + len -= wrote; } } } + V0(device->Backend,unlock)(); return 0; } @@ -251,8 +272,9 @@ FORCE_ALIGN static int qsa_proc_playback(void* ptr) /* Playback */ /************/ -static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName) +static ALCenum qsa_open_playback(PlaybackWrapper *self, const ALCchar* deviceName) { + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; qsa_data *data; int card, dev; int status; @@ -260,6 +282,7 @@ static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName) data = (qsa_data*)calloc(1, sizeof(qsa_data)); if(data == NULL) return ALC_OUT_OF_MEMORY; + ATOMIC_INIT(&data->killNow, AL_TRUE); if(!deviceName) deviceName = qsaDevice; @@ -299,15 +322,15 @@ static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName) return ALC_INVALID_DEVICE; } - al_string_copy_cstr(&device->DeviceName, deviceName); - device->ExtraData = data; + alstr_copy_cstr(&device->DeviceName, deviceName); + self->ExtraData = data; return ALC_NO_ERROR; } -static void qsa_close_playback(ALCdevice* device) +static void qsa_close_playback(PlaybackWrapper *self) { - qsa_data* data=(qsa_data*)device->ExtraData; + qsa_data *data = self->ExtraData; if (data->buffer!=NULL) { @@ -318,12 +341,13 @@ static void qsa_close_playback(ALCdevice* device) snd_pcm_close(data->pcmHandle); free(data); - device->ExtraData=NULL; + self->ExtraData = NULL; } -static ALCboolean qsa_reset_playback(ALCdevice* device) +static ALCboolean qsa_reset_playback(PlaybackWrapper *self) { - qsa_data* data=(qsa_data*)device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; + qsa_data *data = self->ExtraData; int32_t format=-1; switch(device->FmtType) @@ -364,14 +388,14 @@ static ALCboolean qsa_reset_playback(ALCdevice* device) data->cparams.start_mode=SND_PCM_START_FULL; data->cparams.stop_mode=SND_PCM_STOP_STOP; - data->cparams.buf.block.frag_size=device->UpdateSize* - ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType); + data->cparams.buf.block.frag_size=device->UpdateSize * + FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); data->cparams.buf.block.frags_max=device->NumUpdates; data->cparams.buf.block.frags_min=device->NumUpdates; data->cparams.format.interleave=1; data->cparams.format.rate=device->Frequency; - data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans); + data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); data->cparams.format.format=format; if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0) @@ -555,7 +579,7 @@ static ALCboolean qsa_reset_playback(ALCdevice* device) SetDefaultChannelOrder(device); device->UpdateSize=data->csetup.buf.block.frag_size/ - (ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType)); + FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); device->NumUpdates=data->csetup.buf.block.frags; data->size=data->csetup.buf.block.frag_size; @@ -568,35 +592,93 @@ static ALCboolean qsa_reset_playback(ALCdevice* device) return ALC_TRUE; } -static ALCboolean qsa_start_playback(ALCdevice* device) +static ALCboolean qsa_start_playback(PlaybackWrapper *self) { - qsa_data *data = (qsa_data*)device->ExtraData; + qsa_data *data = self->ExtraData; - data->killNow = 0; - if(althrd_create(&data->thread, qsa_proc_playback, device) != althrd_success) + ATOMIC_STORE(&data->killNow, AL_FALSE, almemory_order_release); + if(althrd_create(&data->thread, qsa_proc_playback, self) != althrd_success) return ALC_FALSE; return ALC_TRUE; } -static void qsa_stop_playback(ALCdevice* device) +static void qsa_stop_playback(PlaybackWrapper *self) { - qsa_data *data = (qsa_data*)device->ExtraData; + qsa_data *data = self->ExtraData; int res; - if(data->killNow) + if(ATOMIC_EXCHANGE(&data->killNow, AL_TRUE, almemory_order_acq_rel)) return; - - data->killNow = 1; althrd_join(data->thread, &res); } + +static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(PlaybackWrapper, ALCbackend, self); + + self->ExtraData = NULL; +} + +static void PlaybackWrapper_Destruct(PlaybackWrapper *self) +{ + if(self->ExtraData) + qsa_close_playback(self); + + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); +} + +static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name) +{ + return qsa_open_playback(self, name); +} + +static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self) +{ + return qsa_reset_playback(self); +} + +static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self) +{ + return qsa_start_playback(self); +} + +static void PlaybackWrapper_stop(PlaybackWrapper *self) +{ + qsa_stop_playback(self); +} + + + /***********/ /* Capture */ /***********/ -static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName) +typedef struct CaptureWrapper { + DERIVE_FROM_TYPE(ALCbackend); + qsa_data *ExtraData; +} CaptureWrapper; + +static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device); +static void CaptureWrapper_Destruct(CaptureWrapper *self); +static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name); +static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset) +static ALCboolean CaptureWrapper_start(CaptureWrapper *self); +static void CaptureWrapper_stop(CaptureWrapper *self); +static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples); +static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self); +static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ClockLatency, getClockLatency) +static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock) +static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper) +DEFINE_ALCBACKEND_VTABLE(CaptureWrapper); + + +static ALCenum qsa_open_capture(CaptureWrapper *self, const ALCchar *deviceName) { + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; qsa_data *data; int card, dev; int format=-1; @@ -646,8 +728,8 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName) return ALC_INVALID_DEVICE; } - al_string_copy_cstr(&device->DeviceName, deviceName); - device->ExtraData = data; + alstr_copy_cstr(&device->DeviceName, deviceName); + self->ExtraData = data; switch (device->FmtType) { @@ -687,20 +769,19 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName) data->cparams.stop_mode=SND_PCM_STOP_STOP; data->cparams.buf.block.frag_size=device->UpdateSize* - ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType); + FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); data->cparams.buf.block.frags_max=device->NumUpdates; data->cparams.buf.block.frags_min=device->NumUpdates; data->cparams.format.interleave=1; data->cparams.format.rate=device->Frequency; - data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans); + data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); data->cparams.format.format=format; if(snd_pcm_plugin_params(data->pcmHandle, &data->cparams) < 0) { snd_pcm_close(data->pcmHandle); free(data); - device->ExtraData=NULL; return ALC_INVALID_VALUE; } @@ -708,20 +789,20 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName) return ALC_NO_ERROR; } -static void qsa_close_capture(ALCdevice* device) +static void qsa_close_capture(CaptureWrapper *self) { - qsa_data* data=(qsa_data*)device->ExtraData; + qsa_data *data = self->ExtraData; if (data->pcmHandle!=NULL) snd_pcm_close(data->pcmHandle); free(data); - device->ExtraData=NULL; + self->ExtraData = NULL; } -static void qsa_start_capture(ALCdevice* device) +static void qsa_start_capture(CaptureWrapper *self) { - qsa_data* data=(qsa_data*)device->ExtraData; + qsa_data *data = self->ExtraData; int rstatus; if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0) @@ -741,18 +822,18 @@ static void qsa_start_capture(ALCdevice* device) snd_pcm_capture_go(data->pcmHandle); } -static void qsa_stop_capture(ALCdevice* device) +static void qsa_stop_capture(CaptureWrapper *self) { - qsa_data* data=(qsa_data*)device->ExtraData; - + qsa_data *data = self->ExtraData; snd_pcm_capture_flush(data->pcmHandle); } -static ALCuint qsa_available_samples(ALCdevice* device) +static ALCuint qsa_available_samples(CaptureWrapper *self) { - qsa_data* data=(qsa_data*)device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; + qsa_data *data = self->ExtraData; snd_pcm_channel_status_t status; - ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + ALint frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); ALint free_size; int rstatus; @@ -765,7 +846,7 @@ static ALCuint qsa_available_samples(ALCdevice* device) if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0) { ERR("capture prepare failed: %s\n", snd_strerror(rstatus)); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Failed capture recovery: %s", snd_strerror(rstatus)); return 0; } @@ -779,16 +860,17 @@ static ALCuint qsa_available_samples(ALCdevice* device) return free_size/frame_size; } -static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) +static ALCenum qsa_capture_samples(CaptureWrapper *self, ALCvoid *buffer, ALCuint samples) { - qsa_data* data=(qsa_data*)device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; + qsa_data *data = self->ExtraData; char* read_ptr; snd_pcm_channel_status_t status; fd_set rfds; int selectret; struct timeval timeout; int bytes_read; - ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); ALint len=samples*frame_size; int rstatus; @@ -807,7 +889,7 @@ static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint s switch (selectret) { case -1: - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Failed to check capture samples"); return ALC_INVALID_DEVICE; case 0: break; @@ -838,7 +920,8 @@ static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint s if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0) { ERR("capture prepare failed: %s\n", snd_strerror(rstatus)); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Failed capture recovery: %s", + snd_strerror(rstatus)); return ALC_INVALID_DEVICE; } snd_pcm_capture_go(data->pcmHandle); @@ -854,27 +937,68 @@ static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint s return ALC_NO_ERROR; } -static const BackendFuncs qsa_funcs= { - qsa_open_playback, - qsa_close_playback, - qsa_reset_playback, - qsa_start_playback, - qsa_stop_playback, - qsa_open_capture, - qsa_close_capture, - qsa_start_capture, - qsa_stop_capture, - qsa_capture_samples, - qsa_available_samples -}; -ALCboolean alc_qsa_init(BackendFuncs* func_list) +static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device) { - *func_list = qsa_funcs; + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(CaptureWrapper, ALCbackend, self); + + self->ExtraData = NULL; +} + +static void CaptureWrapper_Destruct(CaptureWrapper *self) +{ + if(self->ExtraData) + qsa_close_capture(self); + + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); +} + +static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name) +{ + return qsa_open_capture(self, name); +} + +static ALCboolean CaptureWrapper_start(CaptureWrapper *self) +{ + qsa_start_capture(self); return ALC_TRUE; } -void alc_qsa_deinit(void) +static void CaptureWrapper_stop(CaptureWrapper *self) +{ + qsa_stop_capture(self); +} + +static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples) +{ + return qsa_capture_samples(self, buffer, samples); +} + +static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self) +{ + return qsa_available_samples(self); +} + + +typedef struct ALCqsaBackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCqsaBackendFactory; +#define ALCQSABACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCqsaBackendFactory, ALCbackendFactory) } } + +static ALCboolean ALCqsaBackendFactory_init(ALCqsaBackendFactory* UNUSED(self)); +static void ALCqsaBackendFactory_deinit(ALCqsaBackendFactory* UNUSED(self)); +static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED(self), ALCbackend_Type type); +static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type); +static ALCbackend* ALCqsaBackendFactory_createBackend(ALCqsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type); +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCqsaBackendFactory); + +static ALCboolean ALCqsaBackendFactory_init(ALCqsaBackendFactory* UNUSED(self)) +{ + return ALC_TRUE; +} + +static void ALCqsaBackendFactory_deinit(ALCqsaBackendFactory* UNUSED(self)) { #define FREE_NAME(iter) free((iter)->name) VECTOR_FOR_EACH(DevMap, DeviceNameMap, FREE_NAME); @@ -885,7 +1009,14 @@ void alc_qsa_deinit(void) #undef FREE_NAME } -void alc_qsa_probe(enum DevProbe type) +static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED(self), ALCbackend_Type type) +{ + if(type == ALCbackend_Playback || type == ALCbackend_Capture) + return ALC_TRUE; + return ALC_FALSE; +} + +static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type) { switch (type) { @@ -914,3 +1045,29 @@ void alc_qsa_probe(enum DevProbe type) break; } } + +static ALCbackend* ALCqsaBackendFactory_createBackend(ALCqsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + PlaybackWrapper *backend; + NEW_OBJ(backend, PlaybackWrapper)(device); + if(!backend) return NULL; + return STATIC_CAST(ALCbackend, backend); + } + if(type == ALCbackend_Capture) + { + CaptureWrapper *backend; + NEW_OBJ(backend, CaptureWrapper)(device); + if(!backend) return NULL; + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} + +ALCbackendFactory *ALCqsaBackendFactory_getFactory(void) +{ + static ALCqsaBackendFactory factory = ALCQSABACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); +} diff --git a/Engine/lib/openal-soft/Alc/backends/sdl2.c b/Engine/lib/openal-soft/Alc/backends/sdl2.c new file mode 100644 index 000000000..cf005024f --- /dev/null +++ b/Engine/lib/openal-soft/Alc/backends/sdl2.c @@ -0,0 +1,287 @@ +/** + * 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 +#include + +#include "alMain.h" +#include "alu.h" +#include "threads.h" +#include "compat.h" + +#include "backends/base.h" + + +#ifdef _WIN32 +#define DEVNAME_PREFIX "OpenAL Soft on " +#else +#define DEVNAME_PREFIX "" +#endif + +typedef struct ALCsdl2Backend { + DERIVE_FROM_TYPE(ALCbackend); + + SDL_AudioDeviceID deviceID; + ALsizei frameSize; + + ALuint Frequency; + enum DevFmtChannels FmtChans; + enum DevFmtType FmtType; + ALuint UpdateSize; +} ALCsdl2Backend; + +static void ALCsdl2Backend_Construct(ALCsdl2Backend *self, ALCdevice *device); +static void ALCsdl2Backend_Destruct(ALCsdl2Backend *self); +static ALCenum ALCsdl2Backend_open(ALCsdl2Backend *self, const ALCchar *name); +static ALCboolean ALCsdl2Backend_reset(ALCsdl2Backend *self); +static ALCboolean ALCsdl2Backend_start(ALCsdl2Backend *self); +static void ALCsdl2Backend_stop(ALCsdl2Backend *self); +static DECLARE_FORWARD2(ALCsdl2Backend, ALCbackend, ALCenum, captureSamples, void*, ALCuint) +static DECLARE_FORWARD(ALCsdl2Backend, ALCbackend, ALCuint, availableSamples) +static DECLARE_FORWARD(ALCsdl2Backend, ALCbackend, ClockLatency, getClockLatency) +static void ALCsdl2Backend_lock(ALCsdl2Backend *self); +static void ALCsdl2Backend_unlock(ALCsdl2Backend *self); +DECLARE_DEFAULT_ALLOCATORS(ALCsdl2Backend) + +DEFINE_ALCBACKEND_VTABLE(ALCsdl2Backend); + +static const ALCchar defaultDeviceName[] = DEVNAME_PREFIX "Default Device"; + +static void ALCsdl2Backend_Construct(ALCsdl2Backend *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCsdl2Backend, ALCbackend, self); + + self->deviceID = 0; + self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); + self->Frequency = device->Frequency; + self->FmtChans = device->FmtChans; + self->FmtType = device->FmtType; + self->UpdateSize = device->UpdateSize; +} + +static void ALCsdl2Backend_Destruct(ALCsdl2Backend *self) +{ + if(self->deviceID) + SDL_CloseAudioDevice(self->deviceID); + self->deviceID = 0; + + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); +} + + +static void ALCsdl2Backend_audioCallback(void *ptr, Uint8 *stream, int len) +{ + ALCsdl2Backend *self = (ALCsdl2Backend*)ptr; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + + assert((len % self->frameSize) == 0); + aluMixData(device, stream, len / self->frameSize); +} + +static ALCenum ALCsdl2Backend_open(ALCsdl2Backend *self, const ALCchar *name) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + SDL_AudioSpec want, have; + + SDL_zero(want); + SDL_zero(have); + + want.freq = device->Frequency; + switch(device->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 = (device->FmtChans == DevFmtMono) ? 1 : 2; + want.samples = device->UpdateSize; + want.callback = ALCsdl2Backend_audioCallback; + want.userdata = self; + + /* Passing NULL to SDL_OpenAudioDevice opens a default, which isn't + * necessarily the first in the list. + */ + if(!name || strcmp(name, defaultDeviceName) == 0) + self->deviceID = SDL_OpenAudioDevice(NULL, 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) + self->deviceID = SDL_OpenAudioDevice(name+prefix_len, SDL_FALSE, &want, &have, + SDL_AUDIO_ALLOW_ANY_CHANGE); + else + self->deviceID = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have, + SDL_AUDIO_ALLOW_ANY_CHANGE); + } + if(self->deviceID == 0) + return ALC_INVALID_VALUE; + + device->Frequency = have.freq; + if(have.channels == 1) + device->FmtChans = DevFmtMono; + else if(have.channels == 2) + device->FmtChans = DevFmtStereo; + else + { + ERR("Got unhandled SDL channel count: %d\n", (int)have.channels); + return ALC_INVALID_VALUE; + } + switch(have.format) + { + case AUDIO_U8: device->FmtType = DevFmtUByte; break; + case AUDIO_S8: device->FmtType = DevFmtByte; break; + case AUDIO_U16SYS: device->FmtType = DevFmtUShort; break; + case AUDIO_S16SYS: device->FmtType = DevFmtShort; break; + case AUDIO_S32SYS: device->FmtType = DevFmtInt; break; + case AUDIO_F32SYS: device->FmtType = DevFmtFloat; break; + default: + ERR("Got unsupported SDL format: 0x%04x\n", have.format); + return ALC_INVALID_VALUE; + } + device->UpdateSize = have.samples; + device->NumUpdates = 2; /* SDL always (tries to) use two periods. */ + + self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); + self->Frequency = device->Frequency; + self->FmtChans = device->FmtChans; + self->FmtType = device->FmtType; + self->UpdateSize = device->UpdateSize; + + alstr_copy_cstr(&device->DeviceName, name ? name : defaultDeviceName); + + return ALC_NO_ERROR; +} + +static ALCboolean ALCsdl2Backend_reset(ALCsdl2Backend *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + device->Frequency = self->Frequency; + device->FmtChans = self->FmtChans; + device->FmtType = self->FmtType; + device->UpdateSize = self->UpdateSize; + device->NumUpdates = 2; + SetDefaultWFXChannelOrder(device); + return ALC_TRUE; +} + +static ALCboolean ALCsdl2Backend_start(ALCsdl2Backend *self) +{ + SDL_PauseAudioDevice(self->deviceID, 0); + return ALC_TRUE; +} + +static void ALCsdl2Backend_stop(ALCsdl2Backend *self) +{ + SDL_PauseAudioDevice(self->deviceID, 1); +} + +static void ALCsdl2Backend_lock(ALCsdl2Backend *self) +{ + SDL_LockAudioDevice(self->deviceID); +} + +static void ALCsdl2Backend_unlock(ALCsdl2Backend *self) +{ + SDL_UnlockAudioDevice(self->deviceID); +} + + +typedef struct ALCsdl2BackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCsdl2BackendFactory; +#define ALCsdl2BACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsdl2BackendFactory, ALCbackendFactory) } } + +ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void); + +static ALCboolean ALCsdl2BackendFactory_init(ALCsdl2BackendFactory *self); +static void ALCsdl2BackendFactory_deinit(ALCsdl2BackendFactory *self); +static ALCboolean ALCsdl2BackendFactory_querySupport(ALCsdl2BackendFactory *self, ALCbackend_Type type); +static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory *self, enum DevProbe type); +static ALCbackend* ALCsdl2BackendFactory_createBackend(ALCsdl2BackendFactory *self, ALCdevice *device, ALCbackend_Type type); +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsdl2BackendFactory); + + +ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void) +{ + static ALCsdl2BackendFactory factory = ALCsdl2BACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); +} + + +static ALCboolean ALCsdl2BackendFactory_init(ALCsdl2BackendFactory* UNUSED(self)) +{ + if(SDL_InitSubSystem(SDL_INIT_AUDIO) == 0) + return AL_TRUE; + return ALC_FALSE; +} + +static void ALCsdl2BackendFactory_deinit(ALCsdl2BackendFactory* UNUSED(self)) +{ + SDL_QuitSubSystem(SDL_INIT_AUDIO); +} + +static ALCboolean ALCsdl2BackendFactory_querySupport(ALCsdl2BackendFactory* UNUSED(self), ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + return ALC_TRUE; + return ALC_FALSE; +} + +static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory* UNUSED(self), enum DevProbe type) +{ + int num_devices, i; + al_string name; + + if(type != ALL_DEVICE_PROBE) + return; + + AL_STRING_INIT(name); + num_devices = SDL_GetNumAudioDevices(SDL_FALSE); + + AppendAllDevicesList(defaultDeviceName); + for(i = 0;i < num_devices;++i) + { + alstr_copy_cstr(&name, DEVNAME_PREFIX); + alstr_append_cstr(&name, SDL_GetAudioDeviceName(i, SDL_FALSE)); + AppendAllDevicesList(alstr_get_cstr(name)); + } + alstr_reset(&name); +} + +static ALCbackend* ALCsdl2BackendFactory_createBackend(ALCsdl2BackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + ALCsdl2Backend *backend; + NEW_OBJ(backend, ALCsdl2Backend)(device); + if(!backend) return NULL; + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} diff --git a/Engine/lib/openal-soft/Alc/backends/sndio.c b/Engine/lib/openal-soft/Alc/backends/sndio.c index 52bff13a5..5aea457b5 100644 --- a/Engine/lib/openal-soft/Alc/backends/sndio.c +++ b/Engine/lib/openal-soft/Alc/backends/sndio.c @@ -28,55 +28,98 @@ #include "alu.h" #include "threads.h" +#include "backends/base.h" + #include -static const ALCchar sndio_device[] = "SndIO Default"; -static ALCboolean sndio_load(void) -{ - return ALC_TRUE; -} +typedef struct ALCsndioBackend { + DERIVE_FROM_TYPE(ALCbackend); - -typedef struct { struct sio_hdl *sndHandle; ALvoid *mix_data; ALsizei data_size; - volatile int killNow; + ATOMIC(int) killNow; althrd_t thread; -} sndio_data; +} ALCsndioBackend; + +static int ALCsndioBackend_mixerProc(void *ptr); + +static void ALCsndioBackend_Construct(ALCsndioBackend *self, ALCdevice *device); +static void ALCsndioBackend_Destruct(ALCsndioBackend *self); +static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name); +static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self); +static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self); +static void ALCsndioBackend_stop(ALCsndioBackend *self); +static DECLARE_FORWARD2(ALCsndioBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint) +static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, ALCuint, availableSamples) +static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, ClockLatency, getClockLatency) +static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCsndioBackend) + +DEFINE_ALCBACKEND_VTABLE(ALCsndioBackend); -static int sndio_proc(void *ptr) +static const ALCchar sndio_device[] = "SndIO Default"; + + +static void ALCsndioBackend_Construct(ALCsndioBackend *self, ALCdevice *device) { - ALCdevice *device = ptr; - sndio_data *data = device->ExtraData; + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCsndioBackend, ALCbackend, self); + + self->sndHandle = NULL; + self->mix_data = NULL; + ATOMIC_INIT(&self->killNow, AL_TRUE); +} + +static void ALCsndioBackend_Destruct(ALCsndioBackend *self) +{ + if(self->sndHandle) + sio_close(self->sndHandle); + self->sndHandle = NULL; + + al_free(self->mix_data); + self->mix_data = NULL; + + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); +} + + +static int ALCsndioBackend_mixerProc(void *ptr) +{ + ALCsndioBackend *self = (ALCsndioBackend*)ptr; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; ALsizei frameSize; size_t wrote; SetRTPriority(); althrd_setname(althrd_current(), MIXER_THREAD_NAME); - frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); - while(!data->killNow && device->Connected) + while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) && + ATOMIC_LOAD(&device->Connected, almemory_order_acquire)) { - ALsizei len = data->data_size; - ALubyte *WritePtr = data->mix_data; + ALsizei len = self->data_size; + ALubyte *WritePtr = self->mix_data; + ALCsndioBackend_lock(self); aluMixData(device, WritePtr, len/frameSize); - while(len > 0 && !data->killNow) + ALCsndioBackend_unlock(self); + while(len > 0 && !ATOMIC_LOAD(&self->killNow, almemory_order_acquire)) { - wrote = sio_write(data->sndHandle, WritePtr, len); + wrote = sio_write(self->sndHandle, WritePtr, len); if(wrote == 0) { ERR("sio_write failed\n"); ALCdevice_Lock(device); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Failed to write playback samples"); ALCdevice_Unlock(device); break; } @@ -90,45 +133,30 @@ static int sndio_proc(void *ptr) } - -static ALCenum sndio_open_playback(ALCdevice *device, const ALCchar *deviceName) +static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name) { - sndio_data *data; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; - if(!deviceName) - deviceName = sndio_device; - else if(strcmp(deviceName, sndio_device) != 0) + if(!name) + name = sndio_device; + else if(strcmp(name, sndio_device) != 0) return ALC_INVALID_VALUE; - data = calloc(1, sizeof(*data)); - data->killNow = 0; - - data->sndHandle = sio_open(NULL, SIO_PLAY, 0); - if(data->sndHandle == NULL) + self->sndHandle = sio_open(NULL, SIO_PLAY, 0); + if(self->sndHandle == NULL) { - free(data); ERR("Could not open device\n"); return ALC_INVALID_VALUE; } - al_string_copy_cstr(&device->DeviceName, deviceName); - device->ExtraData = data; + alstr_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; } -static void sndio_close_playback(ALCdevice *device) +static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self) { - sndio_data *data = device->ExtraData; - - sio_close(data->sndHandle); - free(data); - device->ExtraData = NULL; -} - -static ALCboolean sndio_reset_playback(ALCdevice *device) -{ - sndio_data *data = device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; struct sio_par par; sio_initpar(&par); @@ -170,7 +198,7 @@ static ALCboolean sndio_reset_playback(ALCdevice *device) par.appbufsz = device->UpdateSize * (device->NumUpdates-1); if(!par.appbufsz) par.appbufsz = device->UpdateSize; - if(!sio_setpar(data->sndHandle, &par) || !sio_getpar(data->sndHandle, &par)) + if(!sio_setpar(self->sndHandle, &par) || !sio_getpar(self->sndHandle, &par)) { ERR("Failed to set device parameters\n"); return ALC_FALSE; @@ -211,77 +239,84 @@ static ALCboolean sndio_reset_playback(ALCdevice *device) return ALC_TRUE; } -static ALCboolean sndio_start_playback(ALCdevice *device) +static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self) { - sndio_data *data = device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; - if(!sio_start(data->sndHandle)) + self->data_size = device->UpdateSize * FrameSizeFromDevFmt( + device->FmtChans, device->FmtType, device->AmbiOrder + ); + al_free(self->mix_data); + self->mix_data = al_calloc(16, self->data_size); + + if(!sio_start(self->sndHandle)) { ERR("Error starting playback\n"); return ALC_FALSE; } - data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType); - data->mix_data = calloc(1, data->data_size); - - data->killNow = 0; - if(althrd_create(&data->thread, sndio_proc, device) != althrd_success) + ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release); + if(althrd_create(&self->thread, ALCsndioBackend_mixerProc, self) != althrd_success) { - sio_stop(data->sndHandle); - free(data->mix_data); - data->mix_data = NULL; + sio_stop(self->sndHandle); return ALC_FALSE; } return ALC_TRUE; } -static void sndio_stop_playback(ALCdevice *device) +static void ALCsndioBackend_stop(ALCsndioBackend *self) { - sndio_data *data = device->ExtraData; int res; - if(data->killNow) + if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel)) return; + althrd_join(self->thread, &res); - data->killNow = 1; - althrd_join(data->thread, &res); - - if(!sio_stop(data->sndHandle)) + if(!sio_stop(self->sndHandle)) ERR("Error stopping device\n"); - free(data->mix_data); - data->mix_data = NULL; + al_free(self->mix_data); + self->mix_data = NULL; } -static const BackendFuncs sndio_funcs = { - sndio_open_playback, - sndio_close_playback, - sndio_reset_playback, - sndio_start_playback, - sndio_stop_playback, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL -}; +typedef struct ALCsndioBackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCsndioBackendFactory; +#define ALCSNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsndioBackendFactory, ALCbackendFactory) } } -ALCboolean alc_sndio_init(BackendFuncs *func_list) +ALCbackendFactory *ALCsndioBackendFactory_getFactory(void); + +static ALCboolean ALCsndioBackendFactory_init(ALCsndioBackendFactory *self); +static DECLARE_FORWARD(ALCsndioBackendFactory, ALCbackendFactory, void, deinit) +static ALCboolean ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory *self, ALCbackend_Type type); +static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory *self, enum DevProbe type); +static ALCbackend* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory *self, ALCdevice *device, ALCbackend_Type type); +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsndioBackendFactory); + + +ALCbackendFactory *ALCsndioBackendFactory_getFactory(void) { - if(!sndio_load()) - return ALC_FALSE; - *func_list = sndio_funcs; + static ALCsndioBackendFactory factory = ALCSNDIOBACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); +} + + +static ALCboolean ALCsndioBackendFactory_init(ALCsndioBackendFactory* UNUSED(self)) +{ + /* No dynamic loading */ return ALC_TRUE; } -void alc_sndio_deinit(void) +static ALCboolean ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory* UNUSED(self), ALCbackend_Type type) { + if(type == ALCbackend_Playback) + return ALC_TRUE; + return ALC_FALSE; } -void alc_sndio_probe(enum DevProbe type) +static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory* UNUSED(self), enum DevProbe type) { switch(type) { @@ -292,3 +327,16 @@ void alc_sndio_probe(enum DevProbe type) break; } } + +static ALCbackend* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + ALCsndioBackend *backend; + NEW_OBJ(backend, ALCsndioBackend)(device); + if(!backend) return NULL; + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} diff --git a/Engine/lib/openal-soft/Alc/backends/solaris.c b/Engine/lib/openal-soft/Alc/backends/solaris.c index 01472e6a1..f1c4aeaa2 100644 --- a/Engine/lib/openal-soft/Alc/backends/solaris.c +++ b/Engine/lib/openal-soft/Alc/backends/solaris.c @@ -34,6 +34,7 @@ #include "alMain.h" #include "alu.h" +#include "alconfig.h" #include "threads.h" #include "compat.h" @@ -50,7 +51,7 @@ typedef struct ALCsolarisBackend { ALubyte *mix_data; int data_size; - volatile int killNow; + ATOMIC(ALenum) killNow; althrd_t thread; } ALCsolarisBackend; @@ -59,7 +60,6 @@ static int ALCsolarisBackend_mixerProc(void *ptr); static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *device); static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self); static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *name); -static void ALCsolarisBackend_close(ALCsolarisBackend *self); static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self); static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self); static void ALCsolarisBackend_stop(ALCsolarisBackend *self); @@ -84,6 +84,8 @@ static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *devi SET_VTABLE2(ALCsolarisBackend, ALCbackend, self); self->fd = -1; + self->mix_data = NULL; + ATOMIC_INIT(&self->killNow, AL_FALSE); } static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self) @@ -103,43 +105,67 @@ static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self) static int ALCsolarisBackend_mixerProc(void *ptr) { ALCsolarisBackend *self = ptr; - ALCdevice *Device = STATIC_CAST(ALCbackend,self)->mDevice; - ALint frameSize; - int wrote; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + struct timeval timeout; + ALubyte *write_ptr; + ALint frame_size; + ALint to_write; + ssize_t wrote; + fd_set wfds; + int sret; SetRTPriority(); althrd_setname(althrd_current(), MIXER_THREAD_NAME); - frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); + frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); - while(!self->killNow && Device->Connected) + ALCsolarisBackend_lock(self); + while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) && + ATOMIC_LOAD(&device->Connected, almemory_order_acquire)) { - ALint len = self->data_size; - ALubyte *WritePtr = self->mix_data; + FD_ZERO(&wfds); + FD_SET(self->fd, &wfds); + timeout.tv_sec = 1; + timeout.tv_usec = 0; - aluMixData(Device, WritePtr, len/frameSize); - while(len > 0 && !self->killNow) + ALCsolarisBackend_unlock(self); + sret = select(self->fd+1, NULL, &wfds, NULL, &timeout); + ALCsolarisBackend_lock(self); + if(sret < 0) { - wrote = write(self->fd, WritePtr, len); + if(errno == EINTR) + continue; + ERR("select failed: %s\n", strerror(errno)); + aluHandleDisconnect(device, "Failed to wait for playback buffer: %s", strerror(errno)); + break; + } + else if(sret == 0) + { + WARN("select timeout\n"); + continue; + } + + write_ptr = self->mix_data; + to_write = self->data_size; + aluMixData(device, write_ptr, to_write/frame_size); + while(to_write > 0 && !ATOMIC_LOAD_SEQ(&self->killNow)) + { + wrote = write(self->fd, write_ptr, to_write); if(wrote < 0) { - if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) - { - ERR("write failed: %s\n", strerror(errno)); - ALCsolarisBackend_lock(self); - aluHandleDisconnect(Device); - ALCsolarisBackend_unlock(self); - break; - } - - al_nssleep(1000000); - continue; + if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + continue; + ERR("write failed: %s\n", strerror(errno)); + aluHandleDisconnect(device, "Failed to write playback samples: %s", + strerror(errno)); + break; } - len -= wrote; - WritePtr += wrote; + to_write -= wrote; + write_ptr += wrote; } } + ALCsolarisBackend_unlock(self); return 0; } @@ -162,23 +188,17 @@ static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *na } device = STATIC_CAST(ALCbackend,self)->mDevice; - al_string_copy_cstr(&device->DeviceName, name); + alstr_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; } -static void ALCsolarisBackend_close(ALCsolarisBackend *self) -{ - close(self->fd); - self->fd = -1; -} - static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self) { ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; audio_info_t info; - ALuint frameSize; - int numChannels; + ALsizei frameSize; + ALsizei numChannels; AUDIO_INITINFO(&info); @@ -186,7 +206,7 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self) if(device->FmtChans != DevFmtMono) device->FmtChans = DevFmtStereo; - numChannels = ChannelsFromDevFmt(device->FmtChans); + numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); info.play.channels = numChannels; switch(device->FmtType) @@ -220,9 +240,9 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self) return ALC_FALSE; } - if(ChannelsFromDevFmt(device->FmtChans) != info.play.channels) + if(ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != (ALsizei)info.play.channels) { - ERR("Could not set %d channels, got %d instead\n", ChannelsFromDevFmt(device->FmtChans), info.play.channels); + ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), info.play.channels); return ALC_FALSE; } @@ -242,7 +262,9 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self) SetDefaultChannelOrder(device); free(self->mix_data); - self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + self->data_size = device->UpdateSize * FrameSizeFromDevFmt( + device->FmtChans, device->FmtType, device->AmbiOrder + ); self->mix_data = calloc(1, self->data_size); return ALC_TRUE; @@ -250,7 +272,7 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self) static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self) { - self->killNow = 0; + ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE); if(althrd_create(&self->thread, ALCsolarisBackend_mixerProc, self) != althrd_success) return ALC_FALSE; return ALC_TRUE; @@ -260,10 +282,9 @@ static void ALCsolarisBackend_stop(ALCsolarisBackend *self) { int res; - if(self->killNow) + if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE)) return; - self->killNow = 1; althrd_join(self->thread, &res); if(ioctl(self->fd, AUDIO_DRAIN) < 0) diff --git a/Engine/lib/openal-soft/Alc/backends/mmdevapi.c b/Engine/lib/openal-soft/Alc/backends/wasapi.c similarity index 67% rename from Engine/lib/openal-soft/Alc/backends/mmdevapi.c rename to Engine/lib/openal-soft/Alc/backends/wasapi.c index 3882b08f0..50471f6b6 100644 --- a/Engine/lib/openal-soft/Alc/backends/mmdevapi.c +++ b/Engine/lib/openal-soft/Alc/backends/wasapi.c @@ -41,9 +41,11 @@ #include "alMain.h" #include "alu.h" +#include "ringbuffer.h" #include "threads.h" #include "compat.h" #include "alstring.h" +#include "converter.h" #include "backends/base.h" @@ -64,9 +66,18 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x #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. */ +static inline ALuint64 ScaleCeil(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale) +{ + return (val*new_scale + old_scale-1) / old_scale; +} + + typedef struct { al_string name; al_string endpoint_guid; // obtained from PKEY_AudioEndpoint_GUID , set to "Unknown device GUID" if absent. @@ -108,6 +119,15 @@ typedef struct { #define WM_USER_Enumerate (WM_USER+5) #define WM_USER_Last (WM_USER+5) +static const char MessageStr[WM_USER_Last+1-WM_USER][20] = { + "Open Device", + "Reset Device", + "Start Device", + "Stop Device", + "Close Device", + "Enumerate Devices", +}; + static inline void ReturnMsgResponse(ThreadRequest *req, HRESULT res) { req->result = res; @@ -130,14 +150,14 @@ static void get_device_name_and_guid(IMMDevice *device, al_string *name, al_stri PROPVARIANT pvguid; HRESULT hr; - al_string_copy_cstr(name, DEVNAME_HEAD); + alstr_copy_cstr(name, DEVNAME_HEAD); hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps); if(FAILED(hr)) { WARN("OpenPropertyStore failed: 0x%08lx\n", hr); - al_string_append_cstr(name, "Unknown Device Name"); - if(guid!=NULL)al_string_copy_cstr(guid, "Unknown Device GUID"); + alstr_append_cstr(name, "Unknown Device Name"); + if(guid!=NULL)alstr_copy_cstr(guid, "Unknown Device GUID"); return; } @@ -147,14 +167,14 @@ static void get_device_name_and_guid(IMMDevice *device, al_string *name, al_stri if(FAILED(hr)) { WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr); - al_string_append_cstr(name, "Unknown Device Name"); + alstr_append_cstr(name, "Unknown Device Name"); } else if(pvname.vt == VT_LPWSTR) - al_string_append_wcstr(name, pvname.pwszVal); + alstr_append_wcstr(name, pvname.pwszVal); else { WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvname.vt); - al_string_append_cstr(name, "Unknown Device Name"); + alstr_append_cstr(name, "Unknown Device Name"); } PropVariantClear(&pvname); @@ -165,14 +185,14 @@ static void get_device_name_and_guid(IMMDevice *device, al_string *name, al_stri if(FAILED(hr)) { WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr); - al_string_copy_cstr(guid, "Unknown Device GUID"); + alstr_copy_cstr(guid, "Unknown Device GUID"); } else if(pvguid.vt == VT_LPWSTR) - al_string_copy_wcstr(guid, pvguid.pwszVal); + alstr_copy_wcstr(guid, pvguid.pwszVal); else { WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvguid.vt); - al_string_copy_cstr(guid, "Unknown Device GUID"); + alstr_copy_cstr(guid, "Unknown Device GUID"); } PropVariantClear(&pvguid); @@ -211,7 +231,7 @@ static void get_device_formfactor(IMMDevice *device, EndpointFormFactor *formfac } -static void add_device(IMMDevice *device, LPCWSTR devid, vector_DevMap *list) +static void add_device(IMMDevice *device, const WCHAR *devid, vector_DevMap *list) { int count = 0; al_string tmpname; @@ -228,30 +248,30 @@ static void add_device(IMMDevice *device, LPCWSTR devid, vector_DevMap *list) { const DevMap *iter; - al_string_copy(&entry.name, tmpname); + alstr_copy(&entry.name, tmpname); if(count != 0) { char str[64]; snprintf(str, sizeof(str), " #%d", count+1); - al_string_append_cstr(&entry.name, str); + alstr_append_cstr(&entry.name, str); } -#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0) +#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0) VECTOR_FIND_IF(iter, const DevMap, *list, MATCH_ENTRY); if(iter == VECTOR_END(*list)) break; #undef MATCH_ENTRY count++; } - TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", al_string_get_cstr(entry.name), al_string_get_cstr(entry.endpoint_guid), entry.devid); + TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", alstr_get_cstr(entry.name), alstr_get_cstr(entry.endpoint_guid), entry.devid); VECTOR_PUSH_BACK(*list, entry); AL_STRING_DEINIT(tmpname); } -static LPWSTR get_device_id(IMMDevice *device) +static WCHAR *get_device_id(IMMDevice *device) { - LPWSTR devid; + WCHAR *devid; HRESULT hr; hr = IMMDevice_GetId(device, &devid); @@ -268,7 +288,7 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve { IMMDeviceCollection *coll; IMMDevice *defdev = NULL; - LPWSTR defdevid = NULL; + WCHAR *defdevid = NULL; HRESULT hr; UINT count; UINT i; @@ -300,7 +320,7 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve for(i = 0;i < count;++i) { IMMDevice *device; - LPWSTR devid; + WCHAR *devid; hr = IMMDeviceCollection_Item(coll, i, &device); if(FAILED(hr)) continue; @@ -324,51 +344,51 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve /* Proxy interface used by the message handler. */ -struct ALCmmdevProxyVtable; +struct ALCwasapiProxyVtable; -typedef struct ALCmmdevProxy { - const struct ALCmmdevProxyVtable *vtbl; -} ALCmmdevProxy; +typedef struct ALCwasapiProxy { + const struct ALCwasapiProxyVtable *vtbl; +} ALCwasapiProxy; -struct ALCmmdevProxyVtable { - HRESULT (*const openProxy)(ALCmmdevProxy*); - void (*const closeProxy)(ALCmmdevProxy*); +struct ALCwasapiProxyVtable { + HRESULT (*const openProxy)(ALCwasapiProxy*); + void (*const closeProxy)(ALCwasapiProxy*); - HRESULT (*const resetProxy)(ALCmmdevProxy*); - HRESULT (*const startProxy)(ALCmmdevProxy*); - void (*const stopProxy)(ALCmmdevProxy*); + HRESULT (*const resetProxy)(ALCwasapiProxy*); + HRESULT (*const startProxy)(ALCwasapiProxy*); + void (*const stopProxy)(ALCwasapiProxy*); }; -#define DEFINE_ALCMMDEVPROXY_VTABLE(T) \ -DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, openProxy) \ -DECLARE_THUNK(T, ALCmmdevProxy, void, closeProxy) \ -DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, resetProxy) \ -DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, startProxy) \ -DECLARE_THUNK(T, ALCmmdevProxy, void, stopProxy) \ +#define DEFINE_ALCWASAPIPROXY_VTABLE(T) \ +DECLARE_THUNK(T, ALCwasapiProxy, HRESULT, openProxy) \ +DECLARE_THUNK(T, ALCwasapiProxy, void, closeProxy) \ +DECLARE_THUNK(T, ALCwasapiProxy, HRESULT, resetProxy) \ +DECLARE_THUNK(T, ALCwasapiProxy, HRESULT, startProxy) \ +DECLARE_THUNK(T, ALCwasapiProxy, void, stopProxy) \ \ -static const struct ALCmmdevProxyVtable T##_ALCmmdevProxy_vtable = { \ - T##_ALCmmdevProxy_openProxy, \ - T##_ALCmmdevProxy_closeProxy, \ - T##_ALCmmdevProxy_resetProxy, \ - T##_ALCmmdevProxy_startProxy, \ - T##_ALCmmdevProxy_stopProxy, \ +static const struct ALCwasapiProxyVtable T##_ALCwasapiProxy_vtable = { \ + T##_ALCwasapiProxy_openProxy, \ + T##_ALCwasapiProxy_closeProxy, \ + T##_ALCwasapiProxy_resetProxy, \ + T##_ALCwasapiProxy_startProxy, \ + T##_ALCwasapiProxy_stopProxy, \ } -static void ALCmmdevProxy_Construct(ALCmmdevProxy* UNUSED(self)) { } -static void ALCmmdevProxy_Destruct(ALCmmdevProxy* UNUSED(self)) { } +static void ALCwasapiProxy_Construct(ALCwasapiProxy* UNUSED(self)) { } +static void ALCwasapiProxy_Destruct(ALCwasapiProxy* UNUSED(self)) { } -static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr) +static DWORD CALLBACK ALCwasapiProxy_messageHandler(void *ptr) { ThreadRequest *req = ptr; IMMDeviceEnumerator *Enumerator; ALuint deviceCount = 0; - ALCmmdevProxy *proxy; + ALCwasapiProxy *proxy; HRESULT hr, cohr; MSG msg; TRACE("Starting message thread\n"); - cohr = CoInitialize(NULL); + cohr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if(FAILED(cohr)) { WARN("Failed to initialize COM: 0x%08lx\n", cohr); @@ -402,16 +422,20 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr) TRACE("Starting message loop\n"); while(GetMessage(&msg, NULL, WM_USER_First, WM_USER_Last)) { - TRACE("Got message %u (lparam=%p, wparam=%p)\n", msg.message, (void*)msg.lParam, (void*)msg.wParam); + TRACE("Got message \"%s\" (0x%04x, lparam=%p, wparam=%p)\n", + (msg.message >= WM_USER && msg.message <= WM_USER_Last) ? + MessageStr[msg.message-WM_USER] : "Unknown", + msg.message, (void*)msg.lParam, (void*)msg.wParam + ); switch(msg.message) { case WM_USER_OpenDevice: req = (ThreadRequest*)msg.wParam; - proxy = (ALCmmdevProxy*)msg.lParam; + proxy = (ALCwasapiProxy*)msg.lParam; hr = cohr = S_OK; if(++deviceCount == 1) - hr = cohr = CoInitialize(NULL); + hr = cohr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if(SUCCEEDED(hr)) hr = V0(proxy,openProxy)(); if(FAILED(hr)) @@ -425,7 +449,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr) case WM_USER_ResetDevice: req = (ThreadRequest*)msg.wParam; - proxy = (ALCmmdevProxy*)msg.lParam; + proxy = (ALCwasapiProxy*)msg.lParam; hr = V0(proxy,resetProxy)(); ReturnMsgResponse(req, hr); @@ -433,7 +457,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr) case WM_USER_StartDevice: req = (ThreadRequest*)msg.wParam; - proxy = (ALCmmdevProxy*)msg.lParam; + proxy = (ALCwasapiProxy*)msg.lParam; hr = V0(proxy,startProxy)(); ReturnMsgResponse(req, hr); @@ -441,7 +465,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr) case WM_USER_StopDevice: req = (ThreadRequest*)msg.wParam; - proxy = (ALCmmdevProxy*)msg.lParam; + proxy = (ALCwasapiProxy*)msg.lParam; V0(proxy,stopProxy)(); ReturnMsgResponse(req, S_OK); @@ -449,7 +473,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr) case WM_USER_CloseDevice: req = (ThreadRequest*)msg.wParam; - proxy = (ALCmmdevProxy*)msg.lParam; + proxy = (ALCwasapiProxy*)msg.lParam; V0(proxy,closeProxy)(); if(--deviceCount == 0) @@ -463,7 +487,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr) hr = cohr = S_OK; if(++deviceCount == 1) - hr = cohr = CoInitialize(NULL); + hr = cohr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if(SUCCEEDED(hr)) hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); if(SUCCEEDED(hr)) @@ -496,9 +520,9 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr) } -typedef struct ALCmmdevPlayback { +typedef struct ALCwasapiPlayback { DERIVE_FROM_TYPE(ALCbackend); - DERIVE_FROM_TYPE(ALCmmdevProxy); + DERIVE_FROM_TYPE(ALCwasapiProxy); WCHAR *devid; @@ -509,43 +533,42 @@ typedef struct ALCmmdevPlayback { HANDLE MsgEvent; - volatile UINT32 Padding; + ATOMIC(UINT32) Padding; - volatile int killNow; + ATOMIC(int) killNow; althrd_t thread; -} ALCmmdevPlayback; +} ALCwasapiPlayback; -static int ALCmmdevPlayback_mixerProc(void *arg); +static int ALCwasapiPlayback_mixerProc(void *arg); -static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device); -static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self); -static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *name); -static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self); -static void ALCmmdevPlayback_close(ALCmmdevPlayback *self); -static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self); -static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self); -static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self); -static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self); -static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self); -static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self); -static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self); -static DECLARE_FORWARD2(ALCmmdevPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint) -static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, ALCuint, availableSamples) -static ClockLatency ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback *self); -static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, lock) -static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, unlock) -DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback) +static void ALCwasapiPlayback_Construct(ALCwasapiPlayback *self, ALCdevice *device); +static void ALCwasapiPlayback_Destruct(ALCwasapiPlayback *self); +static ALCenum ALCwasapiPlayback_open(ALCwasapiPlayback *self, const ALCchar *name); +static HRESULT ALCwasapiPlayback_openProxy(ALCwasapiPlayback *self); +static void ALCwasapiPlayback_closeProxy(ALCwasapiPlayback *self); +static ALCboolean ALCwasapiPlayback_reset(ALCwasapiPlayback *self); +static HRESULT ALCwasapiPlayback_resetProxy(ALCwasapiPlayback *self); +static ALCboolean ALCwasapiPlayback_start(ALCwasapiPlayback *self); +static HRESULT ALCwasapiPlayback_startProxy(ALCwasapiPlayback *self); +static void ALCwasapiPlayback_stop(ALCwasapiPlayback *self); +static void ALCwasapiPlayback_stopProxy(ALCwasapiPlayback *self); +static DECLARE_FORWARD2(ALCwasapiPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint) +static DECLARE_FORWARD(ALCwasapiPlayback, ALCbackend, ALCuint, availableSamples) +static ClockLatency ALCwasapiPlayback_getClockLatency(ALCwasapiPlayback *self); +static DECLARE_FORWARD(ALCwasapiPlayback, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCwasapiPlayback, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCwasapiPlayback) -DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevPlayback); -DEFINE_ALCBACKEND_VTABLE(ALCmmdevPlayback); +DEFINE_ALCWASAPIPROXY_VTABLE(ALCwasapiPlayback); +DEFINE_ALCBACKEND_VTABLE(ALCwasapiPlayback); -static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device) +static void ALCwasapiPlayback_Construct(ALCwasapiPlayback *self, ALCdevice *device) { - SET_VTABLE2(ALCmmdevPlayback, ALCbackend, self); - SET_VTABLE2(ALCmmdevPlayback, ALCmmdevProxy, self); + SET_VTABLE2(ALCwasapiPlayback, ALCbackend, self); + SET_VTABLE2(ALCwasapiPlayback, ALCwasapiProxy, self); ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); - ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self)); + ALCwasapiProxy_Construct(STATIC_CAST(ALCwasapiProxy, self)); self->devid = NULL; @@ -556,13 +579,30 @@ static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device self->MsgEvent = NULL; - self->Padding = 0; + ATOMIC_INIT(&self->Padding, 0); - self->killNow = 0; + ATOMIC_INIT(&self->killNow, 0); } -static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self) +static void ALCwasapiPlayback_Destruct(ALCwasapiPlayback *self) { + if(self->MsgEvent) + { + ThreadRequest req = { self->MsgEvent, 0 }; + if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self))) + (void)WaitForResponse(&req); + + CloseHandle(self->MsgEvent); + self->MsgEvent = NULL; + } + + if(self->NotifyEvent) + CloseHandle(self->NotifyEvent); + self->NotifyEvent = NULL; + + free(self->devid); + self->devid = NULL; + if(self->NotifyEvent != NULL) CloseHandle(self->NotifyEvent); self->NotifyEvent = NULL; @@ -573,26 +613,26 @@ static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self) free(self->devid); self->devid = NULL; - ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self)); + ALCwasapiProxy_Destruct(STATIC_CAST(ALCwasapiProxy, self)); ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); } -FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg) +FORCE_ALIGN static int ALCwasapiPlayback_mixerProc(void *arg) { - ALCmmdevPlayback *self = arg; + ALCwasapiPlayback *self = arg; ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; UINT32 buffer_len, written; ALuint update_size, len; BYTE *buffer; HRESULT hr; - hr = CoInitialize(NULL); + hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if(FAILED(hr)) { - ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr); + ERR("CoInitializeEx(NULL, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr); V0(device->Backend,lock)(); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "COM init failed: 0x%08lx", hr); V0(device->Backend,unlock)(); return 1; } @@ -602,18 +642,18 @@ FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg) update_size = device->UpdateSize; buffer_len = update_size * device->NumUpdates; - while(!self->killNow) + while(!ATOMIC_LOAD(&self->killNow, almemory_order_relaxed)) { hr = IAudioClient_GetCurrentPadding(self->client, &written); if(FAILED(hr)) { ERR("Failed to get padding: 0x%08lx\n", hr); V0(device->Backend,lock)(); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Failed to retrieve buffer padding: 0x%08lx", hr); V0(device->Backend,unlock)(); break; } - self->Padding = written; + ATOMIC_STORE(&self->Padding, written, almemory_order_relaxed); len = buffer_len - written; if(len < update_size) @@ -629,22 +669,22 @@ FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg) hr = IAudioRenderClient_GetBuffer(self->render, len, &buffer); if(SUCCEEDED(hr)) { - V0(device->Backend,lock)(); + ALCwasapiPlayback_lock(self); aluMixData(device, buffer, len); - self->Padding = written + len; - V0(device->Backend,unlock)(); + ATOMIC_STORE(&self->Padding, written + len, almemory_order_relaxed); + ALCwasapiPlayback_unlock(self); hr = IAudioRenderClient_ReleaseBuffer(self->render, len, 0); } if(FAILED(hr)) { ERR("Failed to buffer data: 0x%08lx\n", hr); V0(device->Backend,lock)(); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Failed to send playback samples: 0x%08lx", hr); V0(device->Backend,unlock)(); break; } } - self->Padding = 0; + ATOMIC_STORE(&self->Padding, 0, almemory_order_release); CoUninitialize(); return 0; @@ -690,7 +730,7 @@ static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX * return ALC_TRUE; } -static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *deviceName) +static ALCenum ALCwasapiPlayback_open(ALCwasapiPlayback *self, const ALCchar *deviceName) { HRESULT hr = S_OK; @@ -716,8 +756,8 @@ static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *devi } hr = E_FAIL; -#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0 || \ - al_string_cmp_cstr((i)->endpoint_guid, deviceName) == 0) +#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0 || \ + alstr_cmp_cstr((i)->endpoint_guid, deviceName) == 0) VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME); #undef MATCH_NAME if(iter == VECTOR_END(PlaybackDevices)) @@ -739,7 +779,7 @@ static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *devi { ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; self->devid = strdupW(iter->devid); - al_string_copy(&device->DeviceName, iter->name); + alstr_copy(&device->DeviceName, iter->name); hr = S_OK; } } @@ -750,7 +790,7 @@ static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *devi ThreadRequest req = { self->MsgEvent, 0 }; hr = E_FAIL; - if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) + if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self))) hr = WaitForResponse(&req); else ERR("Failed to post thread message: %lu\n", GetLastError()); @@ -775,7 +815,7 @@ static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *devi return ALC_NO_ERROR; } -static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self) +static HRESULT ALCwasapiPlayback_openProxy(ALCwasapiPlayback *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; void *ptr; @@ -797,7 +837,7 @@ static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self) if(SUCCEEDED(hr)) { self->client = ptr; - if(al_string_empty(device->DeviceName)) + if(alstr_empty(device->DeviceName)) get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL); } @@ -812,24 +852,7 @@ static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self) } -static void ALCmmdevPlayback_close(ALCmmdevPlayback *self) -{ - ThreadRequest req = { self->MsgEvent, 0 }; - - if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) - (void)WaitForResponse(&req); - - CloseHandle(self->MsgEvent); - self->MsgEvent = NULL; - - CloseHandle(self->NotifyEvent); - self->NotifyEvent = NULL; - - free(self->devid); - self->devid = NULL; -} - -static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self) +static void ALCwasapiPlayback_closeProxy(ALCwasapiPlayback *self) { if(self->client) IAudioClient_Release(self->client); @@ -841,18 +864,18 @@ static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self) } -static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self) +static ALCboolean ALCwasapiPlayback_reset(ALCwasapiPlayback *self) { ThreadRequest req = { self->MsgEvent, 0 }; HRESULT hr = E_FAIL; - if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) + if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self))) hr = WaitForResponse(&req); return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE; } -static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self) +static HRESULT ALCwasapiPlayback_resetProxy(ALCwasapiPlayback *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; EndpointFormFactor formfactor = UnknownFormFactor; @@ -890,8 +913,8 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self) CoTaskMemFree(wfx); wfx = NULL; - buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 + - device->Frequency-1) / device->Frequency; + buf_time = ScaleCeil(device->UpdateSize*device->NumUpdates, REFTIME_PER_SEC, + device->Frequency); if(!(device->Flags&DEVICE_FREQUENCY_REQUEST)) device->Frequency = OutputType.Format.nSamplesPerSec; @@ -921,9 +944,7 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self) OutputType.Format.nChannels = 1; OutputType.dwChannelMask = MONO; break; - case DevFmtAmbi1: - case DevFmtAmbi2: - case DevFmtAmbi3: + case DevFmtAmbi3D: device->FmtChans = DevFmtStereo; /*fall-through*/ case DevFmtStereo: @@ -1082,7 +1103,7 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self) hr = IAudioClient_GetDevicePeriod(self->client, &min_per, NULL); if(SUCCEEDED(hr)) { - min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000); + min_len = (UINT32)ScaleCeil(min_per, device->Frequency, REFTIME_PER_SEC); /* Find the nearest multiple of the period size to the update size */ if(min_len < device->UpdateSize) min_len *= (device->UpdateSize + min_len/2)/min_len; @@ -1114,18 +1135,18 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self) } -static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self) +static ALCboolean ALCwasapiPlayback_start(ALCwasapiPlayback *self) { ThreadRequest req = { self->MsgEvent, 0 }; HRESULT hr = E_FAIL; - if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) + if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self))) hr = WaitForResponse(&req); return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE; } -static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self) +static HRESULT ALCwasapiPlayback_startProxy(ALCwasapiPlayback *self) { HRESULT hr; void *ptr; @@ -1140,8 +1161,8 @@ static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self) if(SUCCEEDED(hr)) { self->render = ptr; - self->killNow = 0; - if(althrd_create(&self->thread, ALCmmdevPlayback_mixerProc, self) != althrd_success) + ATOMIC_STORE(&self->killNow, 0, almemory_order_release); + if(althrd_create(&self->thread, ALCwasapiPlayback_mixerProc, self) != althrd_success) { if(self->render) IAudioRenderClient_Release(self->render); @@ -1156,21 +1177,21 @@ static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self) } -static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self) +static void ALCwasapiPlayback_stop(ALCwasapiPlayback *self) { ThreadRequest req = { self->MsgEvent, 0 }; - if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) + if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self))) (void)WaitForResponse(&req); } -static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self) +static void ALCwasapiPlayback_stopProxy(ALCwasapiPlayback *self) { int res; if(!self->render) return; - self->killNow = 1; + ATOMIC_STORE_SEQ(&self->killNow, 1); althrd_join(self->thread, &res); IAudioRenderClient_Release(self->render); @@ -1179,23 +1200,24 @@ static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self) } -static ClockLatency ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback *self) +static ClockLatency ALCwasapiPlayback_getClockLatency(ALCwasapiPlayback *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; ClockLatency ret; - ALCmmdevPlayback_lock(self); + ALCwasapiPlayback_lock(self); ret.ClockTime = GetDeviceClockTime(device); - ret.Latency = self->Padding * DEVICE_CLOCK_RES / device->Frequency; - ALCmmdevPlayback_unlock(self); + ret.Latency = ATOMIC_LOAD(&self->Padding, almemory_order_relaxed) * DEVICE_CLOCK_RES / + device->Frequency; + ALCwasapiPlayback_unlock(self); return ret; } -typedef struct ALCmmdevCapture { +typedef struct ALCwasapiCapture { DERIVE_FROM_TYPE(ALCbackend); - DERIVE_FROM_TYPE(ALCmmdevProxy); + DERIVE_FROM_TYPE(ALCwasapiProxy); WCHAR *devid; @@ -1206,43 +1228,44 @@ typedef struct ALCmmdevCapture { HANDLE MsgEvent; + ChannelConverter *ChannelConv; + SampleConverter *SampleConv; ll_ringbuffer_t *Ring; - volatile int killNow; + ATOMIC(int) killNow; althrd_t thread; -} ALCmmdevCapture; +} ALCwasapiCapture; -static int ALCmmdevCapture_recordProc(void *arg); +static int ALCwasapiCapture_recordProc(void *arg); -static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device); -static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self); -static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *name); -static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self); -static void ALCmmdevCapture_close(ALCmmdevCapture *self); -static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self); -static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ALCboolean, reset) -static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self); -static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self); -static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self); -static void ALCmmdevCapture_stop(ALCmmdevCapture *self); -static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self); -static ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples); -static ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self); -static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ClockLatency, getClockLatency) -static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, lock) -static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, unlock) -DECLARE_DEFAULT_ALLOCATORS(ALCmmdevCapture) +static void ALCwasapiCapture_Construct(ALCwasapiCapture *self, ALCdevice *device); +static void ALCwasapiCapture_Destruct(ALCwasapiCapture *self); +static ALCenum ALCwasapiCapture_open(ALCwasapiCapture *self, const ALCchar *name); +static HRESULT ALCwasapiCapture_openProxy(ALCwasapiCapture *self); +static void ALCwasapiCapture_closeProxy(ALCwasapiCapture *self); +static DECLARE_FORWARD(ALCwasapiCapture, ALCbackend, ALCboolean, reset) +static HRESULT ALCwasapiCapture_resetProxy(ALCwasapiCapture *self); +static ALCboolean ALCwasapiCapture_start(ALCwasapiCapture *self); +static HRESULT ALCwasapiCapture_startProxy(ALCwasapiCapture *self); +static void ALCwasapiCapture_stop(ALCwasapiCapture *self); +static void ALCwasapiCapture_stopProxy(ALCwasapiCapture *self); +static ALCenum ALCwasapiCapture_captureSamples(ALCwasapiCapture *self, ALCvoid *buffer, ALCuint samples); +static ALuint ALCwasapiCapture_availableSamples(ALCwasapiCapture *self); +static DECLARE_FORWARD(ALCwasapiCapture, ALCbackend, ClockLatency, getClockLatency) +static DECLARE_FORWARD(ALCwasapiCapture, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCwasapiCapture, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCwasapiCapture) -DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevCapture); -DEFINE_ALCBACKEND_VTABLE(ALCmmdevCapture); +DEFINE_ALCWASAPIPROXY_VTABLE(ALCwasapiCapture); +DEFINE_ALCBACKEND_VTABLE(ALCwasapiCapture); -static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device) +static void ALCwasapiCapture_Construct(ALCwasapiCapture *self, ALCdevice *device) { - SET_VTABLE2(ALCmmdevCapture, ALCbackend, self); - SET_VTABLE2(ALCmmdevCapture, ALCmmdevProxy, self); + SET_VTABLE2(ALCwasapiCapture, ALCbackend, self); + SET_VTABLE2(ALCwasapiCapture, ALCwasapiProxy, self); ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); - ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self)); + ALCwasapiProxy_Construct(STATIC_CAST(ALCwasapiProxy, self)); self->devid = NULL; @@ -1253,50 +1276,64 @@ static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device) self->MsgEvent = NULL; + self->ChannelConv = NULL; + self->SampleConv = NULL; self->Ring = NULL; - self->killNow = 0; + ATOMIC_INIT(&self->killNow, 0); } -static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self) +static void ALCwasapiCapture_Destruct(ALCwasapiCapture *self) { - ll_ringbuffer_free(self->Ring); - self->Ring = NULL; + if(self->MsgEvent) + { + ThreadRequest req = { self->MsgEvent, 0 }; + if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self))) + (void)WaitForResponse(&req); + + CloseHandle(self->MsgEvent); + self->MsgEvent = NULL; + } if(self->NotifyEvent != NULL) CloseHandle(self->NotifyEvent); self->NotifyEvent = NULL; - if(self->MsgEvent != NULL) - CloseHandle(self->MsgEvent); - self->MsgEvent = NULL; + + ll_ringbuffer_free(self->Ring); + self->Ring = NULL; + + DestroySampleConverter(&self->SampleConv); + DestroyChannelConverter(&self->ChannelConv); free(self->devid); self->devid = NULL; - ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self)); + ALCwasapiProxy_Destruct(STATIC_CAST(ALCwasapiProxy, self)); ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); } -FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg) +FORCE_ALIGN int ALCwasapiCapture_recordProc(void *arg) { - ALCmmdevCapture *self = arg; + ALCwasapiCapture *self = arg; ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + ALfloat *samples = NULL; + size_t samplesmax = 0; HRESULT hr; - hr = CoInitialize(NULL); + hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if(FAILED(hr)) { - ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr); + ERR("CoInitializeEx(NULL, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr); V0(device->Backend,lock)(); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "COM init failed: 0x%08lx", hr); V0(device->Backend,unlock)(); return 1; } althrd_setname(althrd_current(), RECORD_THREAD_NAME); - while(!self->killNow) + while(!ATOMIC_LOAD(&self->killNow, almemory_order_relaxed)) { UINT32 avail; DWORD res; @@ -1304,39 +1341,81 @@ FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg) hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail); if(FAILED(hr)) ERR("Failed to get next packet size: 0x%08lx\n", hr); - else while(avail > 0 && SUCCEEDED(hr)) + else if(avail > 0) { UINT32 numsamples; DWORD flags; - BYTE *data; + BYTE *rdata; hr = IAudioCaptureClient_GetBuffer(self->capture, - &data, &numsamples, &flags, NULL, NULL + &rdata, &numsamples, &flags, NULL, NULL ); if(FAILED(hr)) - { ERR("Failed to get capture buffer: 0x%08lx\n", hr); - break; - } - - ll_ringbuffer_write(self->Ring, (char*)data, numsamples); - - hr = IAudioCaptureClient_ReleaseBuffer(self->capture, numsamples); - if(FAILED(hr)) + else { - ERR("Failed to release capture buffer: 0x%08lx\n", hr); - break; - } + ll_ringbuffer_data_t data[2]; + size_t dstframes = 0; - hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail); - if(FAILED(hr)) - ERR("Failed to get next packet size: 0x%08lx\n", hr); + if(self->ChannelConv) + { + if(samplesmax < numsamples) + { + size_t newmax = RoundUp(numsamples, 4096); + ALfloat *tmp = al_calloc(DEF_ALIGN, newmax*2*sizeof(ALfloat)); + al_free(samples); + samples = tmp; + samplesmax = newmax; + } + ChannelConverterInput(self->ChannelConv, rdata, samples, numsamples); + rdata = (BYTE*)samples; + } + + ll_ringbuffer_get_write_vector(self->Ring, data); + + if(self->SampleConv) + { + const ALvoid *srcdata = rdata; + ALsizei srcframes = numsamples; + + dstframes = SampleConverterInput(self->SampleConv, + &srcdata, &srcframes, data[0].buf, (ALsizei)minz(data[0].len, INT_MAX) + ); + if(srcframes > 0 && dstframes == data[0].len && data[1].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 += SampleConverterInput(self->SampleConv, + &srcdata, &srcframes, data[1].buf, (ALsizei)minz(data[1].len, INT_MAX) + ); + } + } + else + { + ALuint framesize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, + device->AmbiOrder); + size_t len1 = minz(data[0].len, numsamples); + size_t len2 = minz(data[1].len, numsamples-len1); + + memcpy(data[0].buf, rdata, len1*framesize); + if(len2 > 0) + memcpy(data[1].buf, rdata+len1*framesize, len2*framesize); + dstframes = len1 + len2; + } + + ll_ringbuffer_write_advance(self->Ring, dstframes); + + hr = IAudioCaptureClient_ReleaseBuffer(self->capture, numsamples); + if(FAILED(hr)) ERR("Failed to release capture buffer: 0x%08lx\n", hr); + } } if(FAILED(hr)) { V0(device->Backend,lock)(); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Failed to capture samples: 0x%08lx", hr); V0(device->Backend,unlock)(); break; } @@ -1346,12 +1425,16 @@ FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg) ERR("WaitForSingleObjectEx error: 0x%lx\n", res); } + al_free(samples); + samples = NULL; + samplesmax = 0; + CoUninitialize(); return 0; } -static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *deviceName) +static ALCenum ALCwasapiCapture_open(ALCwasapiCapture *self, const ALCchar *deviceName) { HRESULT hr = S_OK; @@ -1377,8 +1460,8 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device } hr = E_FAIL; -#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0 || \ - al_string_cmp_cstr((i)->endpoint_guid, deviceName) == 0) +#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0 || \ + alstr_cmp_cstr((i)->endpoint_guid, deviceName) == 0) VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME); #undef MATCH_NAME if(iter == VECTOR_END(CaptureDevices)) @@ -1400,7 +1483,7 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device { ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; self->devid = strdupW(iter->devid); - al_string_copy(&device->DeviceName, iter->name); + alstr_copy(&device->DeviceName, iter->name); hr = S_OK; } } @@ -1411,7 +1494,7 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device ThreadRequest req = { self->MsgEvent, 0 }; hr = E_FAIL; - if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) + if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self))) hr = WaitForResponse(&req); else ERR("Failed to post thread message: %lu\n", GetLastError()); @@ -1437,14 +1520,13 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device ThreadRequest req = { self->MsgEvent, 0 }; hr = E_FAIL; - if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) + if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self))) hr = WaitForResponse(&req); else ERR("Failed to post thread message: %lu\n", GetLastError()); if(FAILED(hr)) { - ALCmmdevCapture_close(self); if(hr == E_OUTOFMEMORY) return ALC_OUT_OF_MEMORY; return ALC_INVALID_VALUE; @@ -1454,7 +1536,7 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device return ALC_NO_ERROR; } -static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self) +static HRESULT ALCwasapiCapture_openProxy(ALCwasapiCapture *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; void *ptr; @@ -1476,7 +1558,7 @@ static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self) if(SUCCEEDED(hr)) { self->client = ptr; - if(al_string_empty(device->DeviceName)) + if(alstr_empty(device->DeviceName)) get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL); } @@ -1491,27 +1573,7 @@ static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self) } -static void ALCmmdevCapture_close(ALCmmdevCapture *self) -{ - ThreadRequest req = { self->MsgEvent, 0 }; - - if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) - (void)WaitForResponse(&req); - - ll_ringbuffer_free(self->Ring); - self->Ring = NULL; - - CloseHandle(self->MsgEvent); - self->MsgEvent = NULL; - - CloseHandle(self->NotifyEvent); - self->NotifyEvent = NULL; - - free(self->devid); - self->devid = NULL; -} - -static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self) +static void ALCwasapiCapture_closeProxy(ALCwasapiCapture *self) { if(self->client) IAudioClient_Release(self->client); @@ -1523,11 +1585,12 @@ static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self) } -static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self) +static HRESULT ALCwasapiCapture_resetProxy(ALCwasapiCapture *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; WAVEFORMATEXTENSIBLE OutputType; WAVEFORMATEX *wfx = NULL; + enum DevFmtType srcType; REFERENCE_TIME buf_time; UINT32 buffer_len; void *ptr = NULL; @@ -1545,8 +1608,12 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self) } self->client = ptr; - buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 + - device->Frequency-1) / device->Frequency; + buf_time = ScaleCeil(device->UpdateSize*device->NumUpdates, REFTIME_PER_SEC, + device->Frequency); + // Make sure buffer is at least 100ms in size + buf_time = maxu64(buf_time, REFTIME_PER_SEC/10); + device->UpdateSize = (ALuint)ScaleCeil(buf_time, device->Frequency, REFTIME_PER_SEC) / + device->NumUpdates; OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; switch(device->FmtChans) @@ -1580,40 +1647,33 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self) OutputType.dwChannelMask = X7DOT1; break; - case DevFmtAmbi1: - case DevFmtAmbi2: - case DevFmtAmbi3: + case DevFmtAmbi3D: return E_FAIL; } switch(device->FmtType) { + /* NOTE: Signedness doesn't matter, the converter will handle it. */ + case DevFmtByte: case DevFmtUByte: OutputType.Format.wBitsPerSample = 8; - OutputType.Samples.wValidBitsPerSample = 8; OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; break; case DevFmtShort: + case DevFmtUShort: OutputType.Format.wBitsPerSample = 16; - OutputType.Samples.wValidBitsPerSample = 16; OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; break; case DevFmtInt: + case DevFmtUInt: 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; - - case DevFmtByte: - case DevFmtUShort: - case DevFmtUInt: - WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType)); - return E_FAIL; } + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; OutputType.Format.nSamplesPerSec = device->Frequency; OutputType.Format.nBlockAlign = OutputType.Format.nChannels * @@ -1631,27 +1691,107 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self) return hr; } - /* FIXME: We should do conversion/resampling if we didn't get a matching format. */ - if(wfx->nSamplesPerSec != OutputType.Format.nSamplesPerSec || - wfx->wBitsPerSample != OutputType.Format.wBitsPerSample || - wfx->nChannels != OutputType.Format.nChannels || - wfx->nBlockAlign != OutputType.Format.nBlockAlign) + DestroySampleConverter(&self->SampleConv); + DestroyChannelConverter(&self->ChannelConv); + + if(wfx != NULL) { - ERR("Failed to get matching format, wanted: %s %s %uhz, got: %d channel%s %d-bit %luhz\n", - DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType), - device->Frequency, wfx->nChannels, (wfx->nChannels==1)?"":"s", wfx->wBitsPerSample, - wfx->nSamplesPerSec); + 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(device->FmtChans), DevFmtTypeString(device->FmtType), + device->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 = NULL; + } + + 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(!MakeExtensible(&OutputType, wfx)) + if(device->FmtChans == DevFmtMono && OutputType.Format.nChannels == 2) { - CoTaskMemFree(wfx); - return E_FAIL; + self->ChannelConv = CreateChannelConverter(srcType, DevFmtStereo, + device->FmtChans); + if(!self->ChannelConv) + { + 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(device->FmtChans == DevFmtStereo && OutputType.Format.nChannels == 1) + { + self->ChannelConv = CreateChannelConverter(srcType, DevFmtMono, + device->FmtChans); + if(!self->ChannelConv) + { + 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(device->Frequency != OutputType.Format.nSamplesPerSec || device->FmtType != srcType) + { + self->SampleConv = CreateSampleConverter( + srcType, device->FmtType, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder), + OutputType.Format.nSamplesPerSec, device->Frequency + ); + if(!self->SampleConv) + { + ERR("Failed to create converter for %s format, dst: %s %uhz, src: %s %luhz\n", + DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType), + device->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec); + return E_FAIL; + } + TRACE("Created converter for %s format, dst: %s %uhz, src: %s %luhz\n", + DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType), + device->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec); } - CoTaskMemFree(wfx); - wfx = NULL; hr = IAudioClient_Initialize(self->client, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, @@ -1670,9 +1810,12 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self) return hr; } - buffer_len = maxu(device->UpdateSize*device->NumUpdates + 1, buffer_len); + buffer_len = maxu(device->UpdateSize*device->NumUpdates, buffer_len); ll_ringbuffer_free(self->Ring); - self->Ring = ll_ringbuffer_create(buffer_len, OutputType.Format.nBlockAlign); + self->Ring = ll_ringbuffer_create(buffer_len, + FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder), + false + ); if(!self->Ring) { ERR("Failed to allocate capture ring buffer\n"); @@ -1690,18 +1833,18 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self) } -static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self) +static ALCboolean ALCwasapiCapture_start(ALCwasapiCapture *self) { ThreadRequest req = { self->MsgEvent, 0 }; HRESULT hr = E_FAIL; - if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) + if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self))) hr = WaitForResponse(&req); return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE; } -static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self) +static HRESULT ALCwasapiCapture_startProxy(ALCwasapiCapture *self) { HRESULT hr; void *ptr; @@ -1718,8 +1861,8 @@ static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self) if(SUCCEEDED(hr)) { self->capture = ptr; - self->killNow = 0; - if(althrd_create(&self->thread, ALCmmdevCapture_recordProc, self) != althrd_success) + ATOMIC_STORE(&self->killNow, 0, almemory_order_release); + if(althrd_create(&self->thread, ALCwasapiCapture_recordProc, self) != althrd_success) { ERR("Failed to start thread\n"); IAudioCaptureClient_Release(self->capture); @@ -1738,21 +1881,21 @@ static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self) } -static void ALCmmdevCapture_stop(ALCmmdevCapture *self) +static void ALCwasapiCapture_stop(ALCwasapiCapture *self) { ThreadRequest req = { self->MsgEvent, 0 }; - if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self))) + if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self))) (void)WaitForResponse(&req); } -static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self) +static void ALCwasapiCapture_stopProxy(ALCwasapiCapture *self) { int res; if(!self->capture) return; - self->killNow = 1; + ATOMIC_STORE_SEQ(&self->killNow, 1); althrd_join(self->thread, &res); IAudioCaptureClient_Release(self->capture); @@ -1762,14 +1905,14 @@ static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self) } -ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self) +ALuint ALCwasapiCapture_availableSamples(ALCwasapiCapture *self) { return (ALuint)ll_ringbuffer_read_space(self->Ring); } -ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples) +ALCenum ALCwasapiCapture_captureSamples(ALCwasapiCapture *self, ALCvoid *buffer, ALCuint samples) { - if(ALCmmdevCapture_availableSamples(self) < samples) + if(ALCwasapiCapture_availableSamples(self) < samples) return ALC_INVALID_VALUE; ll_ringbuffer_read(self->Ring, buffer, samples); return ALC_NO_ERROR; @@ -1777,27 +1920,31 @@ ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, A static inline void AppendAllDevicesList2(const DevMap *entry) -{ AppendAllDevicesList(al_string_get_cstr(entry->name)); } +{ AppendAllDevicesList(alstr_get_cstr(entry->name)); } static inline void AppendCaptureDeviceList2(const DevMap *entry) -{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); } +{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); } -typedef struct ALCmmdevBackendFactory { +typedef struct ALCwasapiBackendFactory { DERIVE_FROM_TYPE(ALCbackendFactory); -} ALCmmdevBackendFactory; -#define ALCMMDEVBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCmmdevBackendFactory, ALCbackendFactory) } } +} ALCwasapiBackendFactory; +#define ALCWASAPIBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCwasapiBackendFactory, ALCbackendFactory) } } -static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory *self); -static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory *self); -static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory *self, ALCbackend_Type type); -static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory *self, enum DevProbe type); -static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory *self, ALCdevice *device, ALCbackend_Type type); +static ALCboolean ALCwasapiBackendFactory_init(ALCwasapiBackendFactory *self); +static void ALCwasapiBackendFactory_deinit(ALCwasapiBackendFactory *self); +static ALCboolean ALCwasapiBackendFactory_querySupport(ALCwasapiBackendFactory *self, ALCbackend_Type type); +static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory *self, enum DevProbe type); +static ALCbackend* ALCwasapiBackendFactory_createBackend(ALCwasapiBackendFactory *self, ALCdevice *device, ALCbackend_Type type); -DEFINE_ALCBACKENDFACTORY_VTABLE(ALCmmdevBackendFactory); +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwasapiBackendFactory); -static BOOL MMDevApiLoad(void) +static ALCboolean ALCwasapiBackendFactory_init(ALCwasapiBackendFactory* UNUSED(self)) { static HRESULT InitResult; + + VECTOR_INIT(PlaybackDevices); + VECTOR_INIT(CaptureDevices); + if(!ThreadHdl) { ThreadRequest req; @@ -1808,26 +1955,17 @@ static BOOL MMDevApiLoad(void) ERR("Failed to create event: %lu\n", GetLastError()); else { - ThreadHdl = CreateThread(NULL, 0, ALCmmdevProxy_messageHandler, &req, 0, &ThreadID); + ThreadHdl = CreateThread(NULL, 0, ALCwasapiProxy_messageHandler, &req, 0, &ThreadID); if(ThreadHdl != NULL) InitResult = WaitForResponse(&req); CloseHandle(req.FinishedEvt); } } - return SUCCEEDED(InitResult); + + return SUCCEEDED(InitResult) ? ALC_TRUE : ALC_FALSE; } -static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory* UNUSED(self)) -{ - VECTOR_INIT(PlaybackDevices); - VECTOR_INIT(CaptureDevices); - - if(!MMDevApiLoad()) - return ALC_FALSE; - return ALC_TRUE; -} - -static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self)) +static void ALCwasapiBackendFactory_deinit(ALCwasapiBackendFactory* UNUSED(self)) { clear_devlist(&PlaybackDevices); VECTOR_DEINIT(PlaybackDevices); @@ -1844,19 +1982,14 @@ static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self)) } } -static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory* UNUSED(self), ALCbackend_Type type) +static ALCboolean ALCwasapiBackendFactory_querySupport(ALCwasapiBackendFactory* UNUSED(self), ALCbackend_Type type) { - /* TODO: Disable capture with mmdevapi for now, since it doesn't do any - * rechanneling or resampling; if the device is configured for 48000hz - * stereo input, for example, and the app asks for 22050hz mono, - * initialization will fail. - */ - if(type == ALCbackend_Playback /*|| type == ALCbackend_Capture*/) + if(type == ALCbackend_Playback || type == ALCbackend_Capture) return ALC_TRUE; return ALC_FALSE; } -static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), enum DevProbe type) +static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory* UNUSED(self), enum DevProbe type) { ThreadRequest req = { NULL, 0 }; @@ -1883,19 +2016,19 @@ static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), e } } -static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +static ALCbackend* ALCwasapiBackendFactory_createBackend(ALCwasapiBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) { if(type == ALCbackend_Playback) { - ALCmmdevPlayback *backend; - NEW_OBJ(backend, ALCmmdevPlayback)(device); + ALCwasapiPlayback *backend; + NEW_OBJ(backend, ALCwasapiPlayback)(device); if(!backend) return NULL; return STATIC_CAST(ALCbackend, backend); } if(type == ALCbackend_Capture) { - ALCmmdevCapture *backend; - NEW_OBJ(backend, ALCmmdevCapture)(device); + ALCwasapiCapture *backend; + NEW_OBJ(backend, ALCwasapiCapture)(device); if(!backend) return NULL; return STATIC_CAST(ALCbackend, backend); } @@ -1904,8 +2037,8 @@ static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory* } -ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void) +ALCbackendFactory *ALCwasapiBackendFactory_getFactory(void) { - static ALCmmdevBackendFactory factory = ALCMMDEVBACKENDFACTORY_INITIALIZER; + static ALCwasapiBackendFactory factory = ALCWASAPIBACKENDFACTORY_INITIALIZER; return STATIC_CAST(ALCbackendFactory, &factory); } diff --git a/Engine/lib/openal-soft/Alc/backends/wave.c b/Engine/lib/openal-soft/Alc/backends/wave.c index 9bf5a7274..557c2bf26 100644 --- a/Engine/lib/openal-soft/Alc/backends/wave.c +++ b/Engine/lib/openal-soft/Alc/backends/wave.c @@ -27,6 +27,7 @@ #include "alMain.h" #include "alu.h" +#include "alconfig.h" #include "threads.h" #include "compat.h" @@ -76,16 +77,15 @@ typedef struct ALCwaveBackend { ALvoid *mBuffer; ALuint mSize; - volatile int killNow; + ATOMIC(ALenum) killNow; althrd_t thread; } ALCwaveBackend; static int ALCwaveBackend_mixerProc(void *ptr); static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device); -static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, Destruct) +static void ALCwaveBackend_Destruct(ALCwaveBackend *self); static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name); -static void ALCwaveBackend_close(ALCwaveBackend *self); static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self); static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self); static void ALCwaveBackend_stop(ALCwaveBackend *self); @@ -110,9 +110,17 @@ static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device) self->mBuffer = NULL; self->mSize = 0; - self->killNow = 1; + ATOMIC_INIT(&self->killNow, AL_TRUE); } +static void ALCwaveBackend_Destruct(ALCwaveBackend *self) +{ + if(self->mFile) + fclose(self->mFile); + self->mFile = NULL; + + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); +} static int ALCwaveBackend_mixerProc(void *ptr) { @@ -127,7 +135,7 @@ static int ALCwaveBackend_mixerProc(void *ptr) althrd_setname(althrd_current(), MIXER_THREAD_NAME); - frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); done = 0; if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC) @@ -135,7 +143,8 @@ static int ALCwaveBackend_mixerProc(void *ptr) ERR("Failed to get starting time\n"); return 1; } - while(!self->killNow && device->Connected) + while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) && + ATOMIC_LOAD(&device->Connected, almemory_order_acquire)) { if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC) { @@ -157,7 +166,9 @@ static int ALCwaveBackend_mixerProc(void *ptr) al_nssleep(restTime); else while(avail-done >= device->UpdateSize) { + ALCwaveBackend_lock(self); aluMixData(device, self->mBuffer, device->UpdateSize); + ALCwaveBackend_unlock(self); done += device->UpdateSize; if(!IS_LITTLE_ENDIAN) @@ -194,7 +205,7 @@ static int ALCwaveBackend_mixerProc(void *ptr) { ERR("Error writing to file\n"); ALCdevice_Lock(device); - aluHandleDisconnect(device); + aluHandleDisconnect(device, "Failed to write playback samples"); ALCdevice_Unlock(device); break; } @@ -226,18 +237,11 @@ static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name) } device = STATIC_CAST(ALCbackend, self)->mDevice; - al_string_copy_cstr(&device->DeviceName, name); + alstr_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; } -static void ALCwaveBackend_close(ALCwaveBackend *self) -{ - if(self->mFile) - fclose(self->mFile); - self->mFile = NULL; -} - static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; @@ -249,7 +253,10 @@ static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self) clearerr(self->mFile); if(GetConfigValueBool(NULL, "wave", "bformat", 0)) - device->FmtChans = DevFmtAmbi1; + { + device->FmtChans = DevFmtAmbi3D; + device->AmbiOrder = 1; + } switch(device->FmtType) { @@ -277,24 +284,23 @@ static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self) 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 DevFmtAmbi1: - case DevFmtAmbi2: - case DevFmtAmbi3: + case DevFmtAmbi3D: /* .amb output requires FuMa */ - device->AmbiFmt = AmbiFormat_FuMa; + device->AmbiLayout = AmbiLayout_FuMa; + device->AmbiScale = AmbiNorm_FuMa; isbformat = 1; chanmask = 0; break; } bits = BytesFromDevFmt(device->FmtType) * 8; - channels = ChannelsFromDevFmt(device->FmtChans); + channels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); - fprintf(self->mFile, "RIFF"); + fputs("RIFF", self->mFile); fwrite32le(0xFFFFFFFF, self->mFile); // 'RIFF' header len; filled in at close - fprintf(self->mFile, "WAVE"); + fputs("WAVE", self->mFile); - fprintf(self->mFile, "fmt "); + fputs("fmt ", self->mFile); fwrite32le(40, self->mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE // 16-bit val, format type id (extensible: 0xFFFE) @@ -316,11 +322,12 @@ static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self) // 32-bit val, channel mask fwrite32le(chanmask, self->mFile); // 16 byte GUID, sub-type format - val = fwrite(((bits==32) ? (isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) : - (isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM)), 1, 16, self->mFile); + val = fwrite((device->FmtType == DevFmtFloat) ? + (isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) : + (isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, self->mFile); (void)val; - fprintf(self->mFile, "data"); + fputs("data", self->mFile); fwrite32le(0xFFFFFFFF, self->mFile); // 'data' header len; filled in at close if(ferror(self->mFile)) @@ -339,7 +346,9 @@ static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - self->mSize = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + self->mSize = device->UpdateSize * FrameSizeFromDevFmt( + device->FmtChans, device->FmtType, device->AmbiOrder + ); self->mBuffer = malloc(self->mSize); if(!self->mBuffer) { @@ -347,7 +356,7 @@ static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self) return ALC_FALSE; } - self->killNow = 0; + ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release); if(althrd_create(&self->thread, ALCwaveBackend_mixerProc, self) != althrd_success) { free(self->mBuffer); @@ -365,10 +374,8 @@ static void ALCwaveBackend_stop(ALCwaveBackend *self) long size; int res; - if(self->killNow) + if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel)) return; - - self->killNow = 1; althrd_join(self->thread, &res); free(self->mBuffer); diff --git a/Engine/lib/openal-soft/Alc/backends/winmm.c b/Engine/lib/openal-soft/Alc/backends/winmm.c index 9d8f8e9d8..2f4c65dff 100644 --- a/Engine/lib/openal-soft/Alc/backends/winmm.c +++ b/Engine/lib/openal-soft/Alc/backends/winmm.c @@ -29,6 +29,7 @@ #include "alMain.h" #include "alu.h" +#include "ringbuffer.h" #include "threads.h" #include "backends/base.h" @@ -45,7 +46,7 @@ static vector_al_string CaptureDevices; static void clear_devlist(vector_al_string *list) { - VECTOR_FOR_EACH(al_string, *list, al_string_deinit); + VECTOR_FOR_EACH(al_string, *list, alstr_reset); VECTOR_RESIZE(*list, 0, 0); } @@ -71,23 +72,23 @@ static void ProbePlaybackDevices(void) ALuint count = 0; while(1) { - al_string_copy_cstr(&dname, DEVNAME_HEAD); - al_string_append_wcstr(&dname, WaveCaps.szPname); + alstr_copy_cstr(&dname, DEVNAME_HEAD); + alstr_append_wcstr(&dname, WaveCaps.szPname); if(count != 0) { char str[64]; snprintf(str, sizeof(str), " #%d", count+1); - al_string_append_cstr(&dname, str); + alstr_append_cstr(&dname, str); } count++; -#define MATCH_ENTRY(i) (al_string_cmp(dname, *(i)) == 0) +#define MATCH_ENTRY(i) (alstr_cmp(dname, *(i)) == 0) VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_ENTRY); if(iter == VECTOR_END(PlaybackDevices)) break; #undef MATCH_ENTRY } - TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i); + TRACE("Got device \"%s\", ID %u\n", alstr_get_cstr(dname), i); } VECTOR_PUSH_BACK(PlaybackDevices, dname); } @@ -114,23 +115,23 @@ static void ProbeCaptureDevices(void) ALuint count = 0; while(1) { - al_string_copy_cstr(&dname, DEVNAME_HEAD); - al_string_append_wcstr(&dname, WaveCaps.szPname); + alstr_copy_cstr(&dname, DEVNAME_HEAD); + alstr_append_wcstr(&dname, WaveCaps.szPname); if(count != 0) { char str[64]; snprintf(str, sizeof(str), " #%d", count+1); - al_string_append_cstr(&dname, str); + alstr_append_cstr(&dname, str); } count++; -#define MATCH_ENTRY(i) (al_string_cmp(dname, *(i)) == 0) +#define MATCH_ENTRY(i) (alstr_cmp(dname, *(i)) == 0) VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_ENTRY); if(iter == VECTOR_END(CaptureDevices)) break; #undef MATCH_ENTRY } - TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i); + TRACE("Got device \"%s\", ID %u\n", alstr_get_cstr(dname), i); } VECTOR_PUSH_BACK(CaptureDevices, dname); } @@ -147,7 +148,7 @@ typedef struct ALCwinmmPlayback { WAVEFORMATEX Format; - volatile ALboolean killNow; + ATOMIC(ALenum) killNow; althrd_t thread; } ALCwinmmPlayback; @@ -158,7 +159,6 @@ static void CALLBACK ALCwinmmPlayback_waveOutProc(HWAVEOUT device, UINT msg, DWO static int ALCwinmmPlayback_mixerProc(void *arg); static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *name); -static void ALCwinmmPlayback_close(ALCwinmmPlayback *self); static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self); static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self); static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self); @@ -180,7 +180,7 @@ static void ALCwinmmPlayback_Construct(ALCwinmmPlayback *self, ALCdevice *device InitRef(&self->WaveBuffersCommitted, 0); self->OutHdl = NULL; - self->killNow = AL_TRUE; + ATOMIC_INIT(&self->killNow, AL_TRUE); } static void ALCwinmmPlayback_Destruct(ALCwinmmPlayback *self) @@ -224,7 +224,7 @@ FORCE_ALIGN static int ALCwinmmPlayback_mixerProc(void *arg) if(msg.message != WOM_DONE) continue; - if(self->killNow) + if(ATOMIC_LOAD(&self->killNow, almemory_order_acquire)) { if(ReadRef(&self->WaveBuffersCommitted) == 0) break; @@ -232,8 +232,10 @@ FORCE_ALIGN static int ALCwinmmPlayback_mixerProc(void *arg) } WaveHdr = ((WAVEHDR*)msg.lParam); + ALCwinmmPlayback_lock(self); aluMixData(device, WaveHdr->lpData, WaveHdr->dwBufferLength / self->Format.nBlockAlign); + ALCwinmmPlayback_unlock(self); // Send buffer back to play more data waveOutWrite(self->OutHdl, WaveHdr, sizeof(WAVEHDR)); @@ -255,8 +257,8 @@ static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *devi ProbePlaybackDevices(); // Find the Device ID matching the deviceName if valid -#define MATCH_DEVNAME(iter) (!al_string_empty(*(iter)) && \ - (!deviceName || al_string_cmp_cstr(*(iter), deviceName) == 0)) +#define MATCH_DEVNAME(iter) (!alstr_empty(*(iter)) && \ + (!deviceName || alstr_cmp_cstr(*(iter), deviceName) == 0)) VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_DEVNAME); if(iter == VECTOR_END(PlaybackDevices)) return ALC_INVALID_VALUE; @@ -298,7 +300,7 @@ retry_open: goto failure; } - al_string_copy(&device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID)); + alstr_copy(&device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID)); return ALC_NO_ERROR; failure: @@ -309,9 +311,6 @@ failure: return ALC_INVALID_VALUE; } -static void ALCwinmmPlayback_close(ALCwinmmPlayback* UNUSED(self)) -{ } - static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; @@ -372,7 +371,7 @@ static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self) ALint BufferSize; ALuint i; - self->killNow = AL_FALSE; + ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release); if(althrd_create(&self->thread, ALCwinmmPlayback_mixerProc, self) != althrd_success) return ALC_FALSE; @@ -380,7 +379,7 @@ static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self) // Create 4 Buffers BufferSize = device->UpdateSize*device->NumUpdates / 4; - BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); BufferData = calloc(4, BufferSize); for(i = 0;i < 4;i++) @@ -403,11 +402,8 @@ static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self) void *buffer = NULL; int i; - if(self->killNow) + if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel)) return; - - // Set flag to stop processing headers - self->killNow = AL_TRUE; althrd_join(self->thread, &i); // Release the wave buffers @@ -434,7 +430,7 @@ typedef struct ALCwinmmCapture { WAVEFORMATEX Format; - volatile ALboolean killNow; + ATOMIC(ALenum) killNow; althrd_t thread; } ALCwinmmCapture; @@ -445,7 +441,6 @@ static void CALLBACK ALCwinmmCapture_waveInProc(HWAVEIN device, UINT msg, DWORD_ static int ALCwinmmCapture_captureProc(void *arg); static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name); -static void ALCwinmmCapture_close(ALCwinmmCapture *self); static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ALCboolean, reset) static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self); static void ALCwinmmCapture_stop(ALCwinmmCapture *self); @@ -467,11 +462,38 @@ static void ALCwinmmCapture_Construct(ALCwinmmCapture *self, ALCdevice *device) InitRef(&self->WaveBuffersCommitted, 0); self->InHdl = NULL; - self->killNow = AL_TRUE; + ATOMIC_INIT(&self->killNow, AL_TRUE); } static void ALCwinmmCapture_Destruct(ALCwinmmCapture *self) { + void *buffer = NULL; + int i; + + /* Tell the processing thread to quit and wait for it to do so. */ + if(!ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel)) + { + PostThreadMessage(self->thread, WM_QUIT, 0, 0); + + althrd_join(self->thread, &i); + + /* Make sure capture is stopped and all pending buffers are flushed. */ + waveInReset(self->InHdl); + + // Release the wave buffers + for(i = 0;i < 4;i++) + { + waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR)); + if(i == 0) buffer = self->WaveBuffer[i].lpData; + self->WaveBuffer[i].lpData = NULL; + } + free(buffer); + } + + ll_ringbuffer_free(self->Ring); + self->Ring = NULL; + + // Close the Wave device if(self->InHdl) waveInClose(self->InHdl); self->InHdl = 0; @@ -510,7 +532,7 @@ static int ALCwinmmCapture_captureProc(void *arg) continue; /* Don't wait for other buffers to finish before quitting. We're * closing so we don't need them. */ - if(self->killNow) + if(ATOMIC_LOAD(&self->killNow, almemory_order_acquire)) break; WaveHdr = ((WAVEHDR*)msg.lParam); @@ -542,7 +564,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name) ProbeCaptureDevices(); // Find the Device ID matching the deviceName if valid -#define MATCH_DEVNAME(iter) (!al_string_empty(*(iter)) && (!name || al_string_cmp_cstr(*iter, name) == 0)) +#define MATCH_DEVNAME(iter) (!alstr_empty(*(iter)) && (!name || alstr_cmp_cstr(*iter, name) == 0)) VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_DEVNAME); if(iter == VECTOR_END(CaptureDevices)) return ALC_INVALID_VALUE; @@ -561,9 +583,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name) case DevFmtX51Rear: case DevFmtX61: case DevFmtX71: - case DevFmtAmbi1: - case DevFmtAmbi2: - case DevFmtAmbi3: + case DevFmtAmbi3D: return ALC_INVALID_ENUM; } @@ -584,7 +604,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name) memset(&self->Format, 0, sizeof(WAVEFORMATEX)); self->Format.wFormatTag = ((device->FmtType == DevFmtFloat) ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM); - self->Format.nChannels = ChannelsFromDevFmt(device->FmtChans); + self->Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); self->Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8; self->Format.nBlockAlign = self->Format.wBitsPerSample * self->Format.nChannels / 8; @@ -606,7 +626,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name) if(CapturedDataSize < (self->Format.nSamplesPerSec / 10)) CapturedDataSize = self->Format.nSamplesPerSec / 10; - self->Ring = ll_ringbuffer_create(CapturedDataSize+1, self->Format.nBlockAlign); + self->Ring = ll_ringbuffer_create(CapturedDataSize, self->Format.nBlockAlign, false); if(!self->Ring) goto failure; InitRef(&self->WaveBuffersCommitted, 0); @@ -632,11 +652,11 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name) IncrementRef(&self->WaveBuffersCommitted); } - self->killNow = AL_FALSE; + ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release); if(althrd_create(&self->thread, ALCwinmmCapture_captureProc, self) != althrd_success) goto failure; - al_string_copy(&device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID)); + alstr_copy(&device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID)); return ALC_NO_ERROR; failure: @@ -657,37 +677,6 @@ failure: return ALC_INVALID_VALUE; } -static void ALCwinmmCapture_close(ALCwinmmCapture *self) -{ - void *buffer = NULL; - int i; - - /* Tell the processing thread to quit and wait for it to do so. */ - self->killNow = AL_TRUE; - PostThreadMessage(self->thread, WM_QUIT, 0, 0); - - althrd_join(self->thread, &i); - - /* Make sure capture is stopped and all pending buffers are flushed. */ - waveInReset(self->InHdl); - - // Release the wave buffers - for(i = 0;i < 4;i++) - { - waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR)); - if(i == 0) buffer = self->WaveBuffer[i].lpData; - self->WaveBuffer[i].lpData = NULL; - } - free(buffer); - - ll_ringbuffer_free(self->Ring); - self->Ring = NULL; - - // Close the Wave device - waveInClose(self->InHdl); - self->InHdl = NULL; -} - static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self) { waveInStart(self->InHdl); @@ -707,19 +696,19 @@ static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *bu static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self) { - return ll_ringbuffer_read_space(self->Ring); + return (ALCuint)ll_ringbuffer_read_space(self->Ring); } static inline void AppendAllDevicesList2(const al_string *name) { - if(!al_string_empty(*name)) - AppendAllDevicesList(al_string_get_cstr(*name)); + if(!alstr_empty(*name)) + AppendAllDevicesList(alstr_get_cstr(*name)); } static inline void AppendCaptureDeviceList2(const al_string *name) { - if(!al_string_empty(*name)) - AppendCaptureDeviceList(al_string_get_cstr(*name)); + if(!alstr_empty(*name)) + AppendCaptureDeviceList(alstr_get_cstr(*name)); } typedef struct ALCwinmmBackendFactory { diff --git a/Engine/lib/openal-soft/Alc/bformatdec.c b/Engine/lib/openal-soft/Alc/bformatdec.c index 2aab4ed8f..dcde7d70a 100644 --- a/Engine/lib/openal-soft/Alc/bformatdec.c +++ b/Engine/lib/openal-soft/Alc/bformatdec.c @@ -3,82 +3,22 @@ #include "bformatdec.h" #include "ambdec.h" -#include "mixer_defs.h" +#include "filters/splitter.h" #include "alu.h" +#include "bool.h" #include "threads.h" #include "almalloc.h" -void bandsplit_init(BandSplitter *splitter, ALfloat freq_mult) -{ - ALfloat w = freq_mult * F_TAU; - ALfloat cw = cosf(w); - if(cw > FLT_EPSILON) - splitter->coeff = (sinf(w) - 1.0f) / cw; - else - splitter->coeff = cw * -0.5f; - - splitter->lp_z1 = 0.0f; - splitter->lp_z2 = 0.0f; - splitter->hp_z1 = 0.0f; -} - -void bandsplit_clear(BandSplitter *splitter) -{ - splitter->lp_z1 = 0.0f; - splitter->lp_z2 = 0.0f; - splitter->hp_z1 = 0.0f; -} - -void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout, - const ALfloat *input, ALuint count) -{ - ALfloat coeff, d, x; - ALfloat z1, z2; - ALuint i; - - coeff = splitter->coeff*0.5f + 0.5f; - z1 = splitter->lp_z1; - z2 = splitter->lp_z2; - for(i = 0;i < count;i++) - { - x = input[i]; - - d = (x - z1) * coeff; - x = z1 + d; - z1 = x + d; - - d = (x - z2) * coeff; - x = z2 + d; - z2 = x + d; - - lpout[i] = x; - } - splitter->lp_z1 = z1; - splitter->lp_z2 = z2; - - coeff = splitter->coeff; - z1 = splitter->hp_z1; - for(i = 0;i < count;i++) - { - x = input[i]; - - d = x - coeff*z1; - x = z1 + coeff*d; - z1 = d; - - hpout[i] = x - lpout[i]; - } - splitter->hp_z1 = z1; -} - - -static const ALfloat UnitScale[MAX_AMBI_COEFFS] = { +/* NOTE: These are scale factors as applied to Ambisonics content. Decoder + * coefficients should be divided by these values to get proper N3D scalings. + */ +const ALfloat N3D2N3DScale[MAX_AMBI_COEFFS] = { 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 const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS] = { +const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS] = { 1.000000000f, /* ACN 0 (W), sqrt(1) */ 1.732050808f, /* ACN 1 (Y), sqrt(3) */ 1.732050808f, /* ACN 2 (Z), sqrt(3) */ @@ -96,7 +36,7 @@ static const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS] = { 2.645751311f, /* ACN 14 (N), sqrt(7) */ 2.645751311f, /* ACN 15 (P), sqrt(7) */ }; -static const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = { +const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = { 1.414213562f, /* ACN 0 (W), sqrt(2) */ 1.732050808f, /* ACN 1 (Y), sqrt(3) */ 1.732050808f, /* ACN 2 (Z), sqrt(3) */ @@ -116,26 +56,9 @@ static const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = { }; -enum FreqBand { - FB_HighFreq, - FB_LowFreq, - FB_Max -}; - -/* These points are in AL coordinates! */ -static const ALfloat Ambi2DPoints[4][3] = { - { -0.707106781f, 0.0f, -0.707106781f }, - { 0.707106781f, 0.0f, -0.707106781f }, - { -0.707106781f, 0.0f, 0.707106781f }, - { 0.707106781f, 0.0f, 0.707106781f }, -}; -static const ALfloat Ambi2DDecoder[4][FB_Max][MAX_AMBI_COEFFS] = { - { { 0.353553f, 0.204094f, 0.0f, 0.204094f }, { 0.25f, 0.204094f, 0.0f, 0.204094f } }, - { { 0.353553f, -0.204094f, 0.0f, 0.204094f }, { 0.25f, -0.204094f, 0.0f, 0.204094f } }, - { { 0.353553f, 0.204094f, 0.0f, -0.204094f }, { 0.25f, 0.204094f, 0.0f, -0.204094f } }, - { { 0.353553f, -0.204094f, 0.0f, -0.204094f }, { 0.25f, -0.204094f, 0.0f, -0.204094f } }, -}; -static ALfloat Ambi2DEncoder[4][MAX_AMBI_COEFFS]; +#define HF_BAND 0 +#define LF_BAND 1 +#define NUM_BANDS 2 /* These points are in AL coordinates! */ static const ALfloat Ambi3DPoints[8][3] = { @@ -148,57 +71,28 @@ static const ALfloat Ambi3DPoints[8][3] = { { -0.577350269f, -0.577350269f, 0.577350269f }, { 0.577350269f, -0.577350269f, 0.577350269f }, }; -static const ALfloat Ambi3DDecoder[8][FB_Max][MAX_AMBI_COEFFS] = { - { { 0.25f, 0.1443375672f, 0.1443375672f, 0.1443375672f }, { 0.125f, 0.125f, 0.125f, 0.125f } }, - { { 0.25f, -0.1443375672f, 0.1443375672f, 0.1443375672f }, { 0.125f, -0.125f, 0.125f, 0.125f } }, - { { 0.25f, 0.1443375672f, 0.1443375672f, -0.1443375672f }, { 0.125f, 0.125f, 0.125f, -0.125f } }, - { { 0.25f, -0.1443375672f, 0.1443375672f, -0.1443375672f }, { 0.125f, -0.125f, 0.125f, -0.125f } }, - { { 0.25f, 0.1443375672f, -0.1443375672f, 0.1443375672f }, { 0.125f, 0.125f, -0.125f, 0.125f } }, - { { 0.25f, -0.1443375672f, -0.1443375672f, 0.1443375672f }, { 0.125f, -0.125f, -0.125f, 0.125f } }, - { { 0.25f, 0.1443375672f, -0.1443375672f, -0.1443375672f }, { 0.125f, 0.125f, -0.125f, -0.125f } }, - { { 0.25f, -0.1443375672f, -0.1443375672f, -0.1443375672f }, { 0.125f, -0.125f, -0.125f, -0.125f } }, +static const ALfloat Ambi3DDecoder[8][MAX_AMBI_COEFFS] = { + { 0.125f, 0.125f, 0.125f, 0.125f }, + { 0.125f, -0.125f, 0.125f, 0.125f }, + { 0.125f, 0.125f, 0.125f, -0.125f }, + { 0.125f, -0.125f, 0.125f, -0.125f }, + { 0.125f, 0.125f, -0.125f, 0.125f }, + { 0.125f, -0.125f, -0.125f, 0.125f }, + { 0.125f, 0.125f, -0.125f, -0.125f }, + { 0.125f, -0.125f, -0.125f, -0.125f }, +}; +static const ALfloat Ambi3DDecoderHFScale[MAX_AMBI_COEFFS] = { + 2.0f, + 1.15470054f, 1.15470054f, 1.15470054f }; -static ALfloat Ambi3DEncoder[8][MAX_AMBI_COEFFS]; -static RowMixerFunc MixMatrixRow = MixRow_C; - - -static alonce_flag bformatdec_inited = AL_ONCE_FLAG_INIT; - -static void init_bformatdec(void) -{ - ALuint i, j; - - MixMatrixRow = SelectRowMixer(); - - for(i = 0;i < COUNTOF(Ambi3DPoints);i++) - CalcDirectionCoeffs(Ambi3DPoints[i], 0.0f, Ambi3DEncoder[i]); - - for(i = 0;i < COUNTOF(Ambi2DPoints);i++) - { - CalcDirectionCoeffs(Ambi2DPoints[i], 0.0f, Ambi2DEncoder[i]); - - /* Remove the skipped height-related coefficients for 2D rendering. */ - Ambi2DEncoder[i][2] = Ambi2DEncoder[i][3]; - Ambi2DEncoder[i][3] = Ambi2DEncoder[i][4]; - Ambi2DEncoder[i][4] = Ambi2DEncoder[i][8]; - Ambi2DEncoder[i][5] = Ambi2DEncoder[i][9]; - Ambi2DEncoder[i][6] = Ambi2DEncoder[i][15]; - for(j = 7;j < MAX_AMBI_COEFFS;j++) - Ambi2DEncoder[i][j] = 0.0f; - } -} - - -#define MAX_DELAY_LENGTH 128 - /* NOTE: BandSplitter filters are unused with single-band decoding */ typedef struct BFormatDec { - ALboolean Enabled[MAX_OUTPUT_CHANNELS]; + ALuint Enabled; /* Bitfield of enabled channels. */ union { - alignas(16) ALfloat Dual[MAX_OUTPUT_CHANNELS][FB_Max][MAX_AMBI_COEFFS]; + alignas(16) ALfloat Dual[MAX_OUTPUT_CHANNELS][NUM_BANDS][MAX_AMBI_COEFFS]; alignas(16) ALfloat Single[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS]; } Matrix; @@ -212,67 +106,42 @@ typedef struct BFormatDec { alignas(16) ALfloat ChannelMix[BUFFERSIZE]; struct { - alignas(16) ALfloat Buffer[MAX_DELAY_LENGTH]; - ALuint Length; /* Valid range is [0...MAX_DELAY_LENGTH). */ - } Delay[MAX_OUTPUT_CHANNELS]; + BandSplitter XOver; + ALfloat Gains[NUM_BANDS]; + } UpSampler[4]; - struct { - BandSplitter XOver[4]; - - ALfloat Gains[4][MAX_OUTPUT_CHANNELS][FB_Max]; - } UpSampler; - - ALuint NumChannels; + ALsizei NumChannels; ALboolean DualBand; - ALboolean Periphonic; } BFormatDec; BFormatDec *bformatdec_alloc() { - alcall_once(&bformatdec_inited, init_bformatdec); return al_calloc(16, sizeof(BFormatDec)); } -void bformatdec_free(BFormatDec *dec) +void bformatdec_free(BFormatDec **dec) { - if(dec) + if(dec && *dec) { - al_free(dec->Samples); - dec->Samples = NULL; - dec->SamplesHF = NULL; - dec->SamplesLF = NULL; + al_free((*dec)->Samples); + (*dec)->Samples = NULL; + (*dec)->SamplesHF = NULL; + (*dec)->SamplesLF = NULL; - memset(dec, 0, sizeof(*dec)); - al_free(dec); + al_free(*dec); + *dec = NULL; } } -int bformatdec_getOrder(const struct BFormatDec *dec) +void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount, ALuint srate, const ALsizei chanmap[MAX_OUTPUT_CHANNELS]) { - if(dec->Periphonic) - { - if(dec->NumChannels > 9) return 3; - if(dec->NumChannels > 4) return 2; - if(dec->NumChannels > 1) return 1; - } - else - { - if(dec->NumChannels > 5) return 3; - if(dec->NumChannels > 3) return 2; - if(dec->NumChannels > 1) return 1; - } - return 0; -} - -void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALuint chancount, ALuint srate, const ALuint chanmap[MAX_OUTPUT_CHANNELS], int flags) -{ - static const ALuint map2DTo3D[MAX_AMBI2D_COEFFS] = { + static const ALsizei map2DTo3D[MAX_AMBI2D_COEFFS] = { 0, 1, 3, 4, 8, 9, 15 }; - const ALfloat *coeff_scale = UnitScale; - ALfloat distgain[MAX_OUTPUT_CHANNELS]; - ALfloat maxdist, ratio; - ALuint i, j, k; + const ALfloat *coeff_scale = N3D2N3DScale; + bool periphonic; + ALfloat ratio; + ALsizei i; al_free(dec->Samples); dec->Samples = NULL; @@ -284,91 +153,48 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALuint chancount, dec->SamplesHF = dec->Samples; dec->SamplesLF = dec->SamplesHF + dec->NumChannels; - for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) - dec->Enabled[i] = AL_FALSE; + dec->Enabled = 0; for(i = 0;i < conf->NumSpeakers;i++) - dec->Enabled[chanmap[i]] = AL_TRUE; + dec->Enabled |= 1 << chanmap[i]; if(conf->CoeffScale == ADS_SN3D) coeff_scale = SN3D2N3DScale; else if(conf->CoeffScale == ADS_FuMa) coeff_scale = FuMa2N3DScale; + memset(dec->UpSampler, 0, sizeof(dec->UpSampler)); ratio = 400.0f / (ALfloat)srate; for(i = 0;i < 4;i++) - bandsplit_init(&dec->UpSampler.XOver[i], ratio); - memset(dec->UpSampler.Gains, 0, sizeof(dec->UpSampler.Gains)); + bandsplit_init(&dec->UpSampler[i].XOver, ratio); if((conf->ChanMask&AMBI_PERIPHONIC_MASK)) { - /* Combine the matrices that do the in->virt and virt->out conversions - * so we get a single in->out conversion. - */ - for(i = 0;i < 4;i++) - { - for(j = 0;j < dec->NumChannels;j++) - { - ALfloat *gains = dec->UpSampler.Gains[i][j]; - for(k = 0;k < COUNTOF(Ambi3DDecoder);k++) - { - gains[FB_HighFreq] += Ambi3DDecoder[k][FB_HighFreq][i]*Ambi3DEncoder[k][j]; - gains[FB_LowFreq] += Ambi3DDecoder[k][FB_LowFreq][i]*Ambi3DEncoder[k][j]; - } - } - } + periphonic = true; - dec->Periphonic = AL_TRUE; + dec->UpSampler[0].Gains[HF_BAND] = (conf->ChanMask > 0x1ff) ? W_SCALE_3H3P : + (conf->ChanMask > 0xf) ? W_SCALE_2H2P : 1.0f; + dec->UpSampler[0].Gains[LF_BAND] = 1.0f; + for(i = 1;i < 4;i++) + { + dec->UpSampler[i].Gains[HF_BAND] = (conf->ChanMask > 0x1ff) ? XYZ_SCALE_3H3P : + (conf->ChanMask > 0xf) ? XYZ_SCALE_2H2P : 1.0f; + dec->UpSampler[i].Gains[LF_BAND] = 1.0f; + } } else { - for(i = 0;i < 4;i++) + periphonic = false; + + dec->UpSampler[0].Gains[HF_BAND] = (conf->ChanMask > 0x1ff) ? W_SCALE_3H0P : + (conf->ChanMask > 0xf) ? W_SCALE_2H0P : 1.0f; + dec->UpSampler[0].Gains[LF_BAND] = 1.0f; + for(i = 1;i < 3;i++) { - for(j = 0;j < dec->NumChannels;j++) - { - ALfloat *gains = dec->UpSampler.Gains[i][j]; - for(k = 0;k < COUNTOF(Ambi2DDecoder);k++) - { - gains[FB_HighFreq] += Ambi2DDecoder[k][FB_HighFreq][i]*Ambi2DEncoder[k][j]; - gains[FB_LowFreq] += Ambi2DDecoder[k][FB_LowFreq][i]*Ambi2DEncoder[k][j]; - } - } - } - - dec->Periphonic = AL_FALSE; - } - - maxdist = 0.0f; - for(i = 0;i < conf->NumSpeakers;i++) - { - maxdist = maxf(maxdist, conf->Speakers[i].Distance); - distgain[i] = 1.0f; - } - - memset(dec->Delay, 0, sizeof(dec->Delay)); - if((flags&BFDF_DistanceComp) && maxdist > 0.0f) - { - for(i = 0;i < conf->NumSpeakers;i++) - { - ALuint chan = chanmap[i]; - ALfloat delay; - - /* 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. - */ - delay = floorf((maxdist-conf->Speakers[i].Distance) / SPEEDOFSOUNDMETRESPERSEC * - (ALfloat)srate + 0.5f); - if(delay >= (ALfloat)MAX_DELAY_LENGTH) - ERR("Delay for speaker \"%s\" exceeds buffer length (%f >= %u)\n", - al_string_get_cstr(conf->Speakers[i].Name), delay, MAX_DELAY_LENGTH); - - dec->Delay[chan].Length = (ALuint)clampf(delay, 0.0f, (ALfloat)(MAX_DELAY_LENGTH-1)); - distgain[i] = conf->Speakers[i].Distance / maxdist; - TRACE("Channel %u \"%s\" distance compensation: %u samples, %f gain\n", chan, - al_string_get_cstr(conf->Speakers[i].Name), dec->Delay[chan].Length, distgain[i] - ); + dec->UpSampler[i].Gains[HF_BAND] = (conf->ChanMask > 0x1ff) ? XYZ_SCALE_3H0P : + (conf->ChanMask > 0xf) ? XYZ_SCALE_2H0P : 1.0f; + dec->UpSampler[i].Gains[LF_BAND] = 1.0f; } + dec->UpSampler[3].Gains[HF_BAND] = 0.0f; + dec->UpSampler[3].Gains[LF_BAND] = 0.0f; } memset(&dec->Matrix, 0, sizeof(dec->Matrix)); @@ -377,22 +203,22 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALuint chancount, dec->DualBand = AL_FALSE; for(i = 0;i < conf->NumSpeakers;i++) { - ALuint chan = chanmap[i]; + ALsizei chan = chanmap[i]; ALfloat gain; - ALuint j, k; + ALsizei j, k; - if(!dec->Periphonic) + if(!periphonic) { for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++) { - ALuint l = map2DTo3D[j]; + ALsizei l = map2DTo3D[j]; if(j == 0) gain = conf->HFOrderGain[0]; else if(j == 1) gain = conf->HFOrderGain[1]; else if(j == 3) gain = conf->HFOrderGain[2]; else if(j == 5) gain = conf->HFOrderGain[3]; if((conf->ChanMask&(1<Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[l] * - gain * distgain[i]; + gain; } } else @@ -405,7 +231,7 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALuint chancount, else if(j == 9) gain = conf->HFOrderGain[3]; if((conf->ChanMask&(1<Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[j] * - gain * distgain[i]; + gain; } } } @@ -421,35 +247,33 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALuint chancount, ratio = powf(10.0f, conf->XOverRatio / 40.0f); for(i = 0;i < conf->NumSpeakers;i++) { - ALuint chan = chanmap[i]; + ALsizei chan = chanmap[i]; ALfloat gain; - ALuint j, k; + ALsizei j, k; - if(!dec->Periphonic) + if(!periphonic) { for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++) { - ALuint l = map2DTo3D[j]; + ALsizei l = map2DTo3D[j]; if(j == 0) gain = conf->HFOrderGain[0] * ratio; else if(j == 1) gain = conf->HFOrderGain[1] * ratio; else if(j == 3) gain = conf->HFOrderGain[2] * ratio; else if(j == 5) gain = conf->HFOrderGain[3] * ratio; if((conf->ChanMask&(1<Matrix.Dual[chan][FB_HighFreq][j] = conf->HFMatrix[i][k++] / - coeff_scale[l] * gain * - distgain[i]; + dec->Matrix.Dual[chan][HF_BAND][j] = conf->HFMatrix[i][k++] / + coeff_scale[l] * gain; } for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++) { - ALuint l = map2DTo3D[j]; + ALsizei l = map2DTo3D[j]; if(j == 0) gain = conf->LFOrderGain[0] / ratio; else if(j == 1) gain = conf->LFOrderGain[1] / ratio; else if(j == 3) gain = conf->LFOrderGain[2] / ratio; else if(j == 5) gain = conf->LFOrderGain[3] / ratio; if((conf->ChanMask&(1<Matrix.Dual[chan][FB_LowFreq][j] = conf->LFMatrix[i][k++] / - coeff_scale[l] * gain * - distgain[i]; + dec->Matrix.Dual[chan][LF_BAND][j] = conf->LFMatrix[i][k++] / + coeff_scale[l] * gain; } } else @@ -461,9 +285,8 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALuint chancount, else if(j == 4) gain = conf->HFOrderGain[2] * ratio; else if(j == 9) gain = conf->HFOrderGain[3] * ratio; if((conf->ChanMask&(1<Matrix.Dual[chan][FB_HighFreq][j] = conf->HFMatrix[i][k++] / - coeff_scale[j] * gain * - distgain[i]; + dec->Matrix.Dual[chan][HF_BAND][j] = conf->HFMatrix[i][k++] / + coeff_scale[j] * gain; } for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++) { @@ -472,9 +295,8 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALuint chancount, else if(j == 4) gain = conf->LFOrderGain[2] / ratio; else if(j == 9) gain = conf->LFOrderGain[3] / ratio; if((conf->ChanMask&(1<Matrix.Dual[chan][FB_LowFreq][j] = conf->LFMatrix[i][k++] / - coeff_scale[j] * gain * - distgain[i]; + dec->Matrix.Dual[chan][LF_BAND][j] = conf->LFMatrix[i][k++] / + coeff_scale[j] * gain; } } } @@ -482,10 +304,11 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALuint chancount, } -void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint SamplesToDo) +void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo) { - ALuint chan, i; + ALsizei chan, i; + OutBuffer = ASSUME_ALIGNED(OutBuffer, 16); if(dec->DualBand) { for(i = 0;i < dec->NumChannels;i++) @@ -494,42 +317,18 @@ void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BU for(chan = 0;chan < OutChannels;chan++) { - if(!dec->Enabled[chan]) + if(!(dec->Enabled&(1<ChannelMix, 0, SamplesToDo*sizeof(ALfloat)); - MixMatrixRow(dec->ChannelMix, dec->Matrix.Dual[chan][FB_HighFreq], - SAFE_CONST(ALfloatBUFFERSIZE*,dec->SamplesHF), dec->NumChannels, 0, - SamplesToDo + MixRowSamples(dec->ChannelMix, dec->Matrix.Dual[chan][HF_BAND], + dec->SamplesHF, dec->NumChannels, 0, SamplesToDo ); - MixMatrixRow(dec->ChannelMix, dec->Matrix.Dual[chan][FB_LowFreq], - SAFE_CONST(ALfloatBUFFERSIZE*,dec->SamplesLF), dec->NumChannels, 0, - SamplesToDo + MixRowSamples(dec->ChannelMix, dec->Matrix.Dual[chan][LF_BAND], + dec->SamplesLF, dec->NumChannels, 0, SamplesToDo ); - if(dec->Delay[chan].Length > 0) - { - const ALuint base = dec->Delay[chan].Length; - if(SamplesToDo >= base) - { - for(i = 0;i < base;i++) - OutBuffer[chan][i] += dec->Delay[chan].Buffer[i]; - for(;i < SamplesToDo;i++) - OutBuffer[chan][i] += dec->ChannelMix[i-base]; - memcpy(dec->Delay[chan].Buffer, &dec->ChannelMix[SamplesToDo-base], - base*sizeof(ALfloat)); - } - else - { - for(i = 0;i < SamplesToDo;i++) - OutBuffer[chan][i] += dec->Delay[chan].Buffer[i]; - memmove(dec->Delay[chan].Buffer, dec->Delay[chan].Buffer+SamplesToDo, - base - SamplesToDo); - memcpy(dec->Delay[chan].Buffer+base-SamplesToDo, dec->ChannelMix, - SamplesToDo*sizeof(ALfloat)); - } - } - else for(i = 0;i < SamplesToDo;i++) + for(i = 0;i < SamplesToDo;i++) OutBuffer[chan][i] += dec->ChannelMix[i]; } } @@ -537,134 +336,157 @@ void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BU { for(chan = 0;chan < OutChannels;chan++) { - if(!dec->Enabled[chan]) + if(!(dec->Enabled&(1<ChannelMix, 0, SamplesToDo*sizeof(ALfloat)); - MixMatrixRow(dec->ChannelMix, dec->Matrix.Single[chan], InSamples, - dec->NumChannels, 0, SamplesToDo); + MixRowSamples(dec->ChannelMix, dec->Matrix.Single[chan], InSamples, + dec->NumChannels, 0, SamplesToDo); - if(dec->Delay[chan].Length > 0) - { - const ALuint base = dec->Delay[chan].Length; - if(SamplesToDo >= base) - { - for(i = 0;i < base;i++) - OutBuffer[chan][i] += dec->Delay[chan].Buffer[i]; - for(;i < SamplesToDo;i++) - OutBuffer[chan][i] += dec->ChannelMix[i-base]; - memcpy(dec->Delay[chan].Buffer, &dec->ChannelMix[SamplesToDo-base], - base*sizeof(ALfloat)); - } - else - { - for(i = 0;i < SamplesToDo;i++) - OutBuffer[chan][i] += dec->Delay[chan].Buffer[i]; - memmove(dec->Delay[chan].Buffer, dec->Delay[chan].Buffer+SamplesToDo, - base - SamplesToDo); - memcpy(dec->Delay[chan].Buffer+base-SamplesToDo, dec->ChannelMix, - SamplesToDo*sizeof(ALfloat)); - } - } - else for(i = 0;i < SamplesToDo;i++) + for(i = 0;i < SamplesToDo;i++) OutBuffer[chan][i] += dec->ChannelMix[i]; } } } -void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint InChannels, ALuint SamplesToDo) +void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei InChannels, ALsizei SamplesToDo) { - ALuint i, j; + ALsizei i; - /* This up-sampler is very simplistic. It essentially decodes the first- - * order content to a square channel array (or cube if height is desired), - * then encodes those points onto the higher order soundfield. The decoder - * and encoder matrices have been combined to directly convert each input - * channel to the output, without the need for storing the virtual channel - * array. + /* This up-sampler leverages the differences observed in dual-band second- + * and third-order decoder matrices compared to first-order. For the same + * output channel configuration, the low-frequency matrix has identical + * coefficients in the shared input channels, while the high-frequency + * matrix has extra scalars applied to the W channel and X/Y/Z channels. + * Mixing the first-order content into the higher-order stream with the + * appropriate counter-scales applied to the HF response results in the + * subsequent higher-order decode generating the same response as a first- + * order decode. */ for(i = 0;i < InChannels;i++) { /* First, split the first-order components into low and high frequency * bands. */ - bandsplit_process(&dec->UpSampler.XOver[i], - dec->Samples[FB_HighFreq], dec->Samples[FB_LowFreq], + bandsplit_process(&dec->UpSampler[i].XOver, + dec->Samples[HF_BAND], dec->Samples[LF_BAND], InSamples[i], SamplesToDo ); /* Now write each band to the output. */ - for(j = 0;j < dec->NumChannels;j++) - MixMatrixRow(OutBuffer[j], dec->UpSampler.Gains[i][j], - SAFE_CONST(ALfloatBUFFERSIZE*,dec->Samples), FB_Max, 0, - SamplesToDo - ); + MixRowSamples(OutBuffer[i], dec->UpSampler[i].Gains, + dec->Samples, NUM_BANDS, 0, SamplesToDo + ); } } +#define INVALID_UPSAMPLE_INDEX INT_MAX + +static ALsizei GetACNIndex(const BFChannelConfig *chans, ALsizei numchans, ALsizei acn) +{ + ALsizei i; + for(i = 0;i < numchans;i++) + { + if(chans[i].Index == acn) + return i; + } + return INVALID_UPSAMPLE_INDEX; +} +#define GetChannelForACN(b, a) GetACNIndex((b).Ambi.Map, (b).NumChannels, (a)) + typedef struct AmbiUpsampler { - alignas(16) ALfloat Samples[FB_Max][BUFFERSIZE]; + alignas(16) ALfloat Samples[NUM_BANDS][BUFFERSIZE]; BandSplitter XOver[4]; - ALfloat Gains[4][MAX_OUTPUT_CHANNELS][FB_Max]; + ALfloat Gains[4][MAX_OUTPUT_CHANNELS][NUM_BANDS]; } AmbiUpsampler; AmbiUpsampler *ambiup_alloc() { - alcall_once(&bformatdec_inited, init_bformatdec); return al_calloc(16, sizeof(AmbiUpsampler)); } -void ambiup_free(struct AmbiUpsampler *ambiup) +void ambiup_free(struct AmbiUpsampler **ambiup) { - al_free(ambiup); + if(ambiup) + { + al_free(*ambiup); + *ambiup = NULL; + } } -void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device) +void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device, ALfloat w_scale, ALfloat xyz_scale) { - ALfloat gains[8][MAX_OUTPUT_CHANNELS]; ALfloat ratio; - ALuint i, j, k; + ALsizei i; ratio = 400.0f / (ALfloat)device->Frequency; for(i = 0;i < 4;i++) bandsplit_init(&ambiup->XOver[i], ratio); - for(i = 0;i < COUNTOF(Ambi3DEncoder);i++) - ComputePanningGains(device->Dry, Ambi3DEncoder[i], 1.0f, gains[i]); - memset(ambiup->Gains, 0, sizeof(ambiup->Gains)); - for(i = 0;i < 4;i++) + if(device->Dry.CoeffCount > 0) { - for(j = 0;j < device->Dry.NumChannels;j++) + ALfloat encgains[8][MAX_OUTPUT_CHANNELS]; + ALsizei j; + size_t k; + + for(k = 0;k < COUNTOF(Ambi3DPoints);k++) { - for(k = 0;k < COUNTOF(Ambi3DDecoder);k++) + ALfloat coeffs[MAX_AMBI_COEFFS] = { 0.0f }; + CalcDirectionCoeffs(Ambi3DPoints[k], 0.0f, coeffs); + ComputeDryPanGains(&device->Dry, coeffs, 1.0f, encgains[k]); + } + + /* Combine the matrices that do the in->virt and virt->out conversions + * so we get a single in->out conversion. NOTE: the Encoder matrix + * (encgains) and output are transposed, so the input channels line up + * with the rows and the output channels line up with the columns. + */ + for(i = 0;i < 4;i++) + { + for(j = 0;j < device->Dry.NumChannels;j++) { - ambiup->Gains[i][j][FB_HighFreq] += Ambi3DDecoder[k][FB_HighFreq][i]*gains[k][j]; - ambiup->Gains[i][j][FB_LowFreq] += Ambi3DDecoder[k][FB_LowFreq][i]*gains[k][j]; + ALfloat gain=0.0f; + for(k = 0;k < COUNTOF(Ambi3DDecoder);k++) + gain += Ambi3DDecoder[k][i] * encgains[k][j]; + ambiup->Gains[i][j][HF_BAND] = gain * Ambi3DDecoderHFScale[i]; + ambiup->Gains[i][j][LF_BAND] = gain; + } + } + } + else + { + for(i = 0;i < 4;i++) + { + ALsizei index = GetChannelForACN(device->Dry, i); + if(index != INVALID_UPSAMPLE_INDEX) + { + ALfloat scale = device->Dry.Ambi.Map[index].Scale; + ambiup->Gains[i][index][HF_BAND] = scale * ((i==0) ? w_scale : xyz_scale); + ambiup->Gains[i][index][LF_BAND] = scale; } } } } -void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint SamplesToDo) +void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo) { - ALuint i, j; + ALsizei i, j; for(i = 0;i < 4;i++) { bandsplit_process(&ambiup->XOver[i], - ambiup->Samples[FB_HighFreq], ambiup->Samples[FB_LowFreq], + ambiup->Samples[HF_BAND], ambiup->Samples[LF_BAND], InSamples[i], SamplesToDo ); for(j = 0;j < OutChannels;j++) - MixMatrixRow(OutBuffer[j], ambiup->Gains[i][j], - SAFE_CONST(ALfloatBUFFERSIZE*,ambiup->Samples), FB_Max, 0, - SamplesToDo + MixRowSamples(OutBuffer[j], ambiup->Gains[i][j], + ambiup->Samples, NUM_BANDS, 0, SamplesToDo ); } } diff --git a/Engine/lib/openal-soft/Alc/bformatdec.h b/Engine/lib/openal-soft/Alc/bformatdec.h index e78d89a7b..2d7d1d624 100644 --- a/Engine/lib/openal-soft/Alc/bformatdec.h +++ b/Engine/lib/openal-soft/Alc/bformatdec.h @@ -3,47 +3,55 @@ #include "alMain.h" + +/* These are the necessary scales for first-order HF responses to play over + * higher-order 2D (non-periphonic) decoders. + */ +#define W_SCALE_2H0P 1.224744871f /* sqrt(1.5) */ +#define XYZ_SCALE_2H0P 1.0f +#define W_SCALE_3H0P 1.414213562f /* sqrt(2) */ +#define XYZ_SCALE_3H0P 1.082392196f + +/* These are the necessary scales for first-order HF responses to play over + * higher-order 3D (periphonic) decoders. + */ +#define W_SCALE_2H2P 1.341640787f /* sqrt(1.8) */ +#define XYZ_SCALE_2H2P 1.0f +#define W_SCALE_3H3P 1.695486018f +#define XYZ_SCALE_3H3P 1.136697713f + + +/* NOTE: These are scale factors as applied to Ambisonics content. Decoder + * coefficients should be divided by these values to get proper N3D scalings. + */ +const ALfloat N3D2N3DScale[MAX_AMBI_COEFFS]; +const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS]; +const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS]; + + struct AmbDecConf; struct BFormatDec; struct AmbiUpsampler; -enum BFormatDecFlags { - BFDF_DistanceComp = 1<<0 -}; struct BFormatDec *bformatdec_alloc(); -void bformatdec_free(struct BFormatDec *dec); -int bformatdec_getOrder(const struct BFormatDec *dec); -void bformatdec_reset(struct BFormatDec *dec, const struct AmbDecConf *conf, ALuint chancount, ALuint srate, const ALuint chanmap[MAX_OUTPUT_CHANNELS], int flags); +void bformatdec_free(struct BFormatDec **dec); +void bformatdec_reset(struct BFormatDec *dec, const struct AmbDecConf *conf, ALsizei chancount, ALuint srate, const ALsizei chanmap[MAX_OUTPUT_CHANNELS]); /* Decodes the ambisonic input to the given output channels. */ -void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint SamplesToDo); +void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo); /* Up-samples a first-order input to the decoder's configuration. */ -void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint InChannels, ALuint SamplesToDo); +void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei InChannels, ALsizei SamplesToDo); /* Stand-alone first-order upsampler. Kept here because it shares some stuff - * with bformatdec. + * with bformatdec. Assumes a periphonic (4-channel) input mix! */ struct AmbiUpsampler *ambiup_alloc(); -void ambiup_free(struct AmbiUpsampler *ambiup); -void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device); +void ambiup_free(struct AmbiUpsampler **ambiup); +void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device, ALfloat w_scale, ALfloat xyz_scale); -void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint SamplesToDo); - - -/* Band splitter. Splits a signal into two phase-matching frequency bands. */ -typedef struct BandSplitter { - ALfloat coeff; - ALfloat lp_z1; - ALfloat lp_z2; - ALfloat hp_z1; -} BandSplitter; - -void bandsplit_init(BandSplitter *splitter, ALfloat freq_mult); -void bandsplit_clear(BandSplitter *splitter); -void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout, - const ALfloat *input, ALuint count); +void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo); #endif /* BFORMATDEC_H */ diff --git a/Engine/lib/openal-soft/Alc/bs2b.c b/Engine/lib/openal-soft/Alc/bs2b.c index ddc2e2f24..e235e5475 100644 --- a/Engine/lib/openal-soft/Alc/bs2b.c +++ b/Engine/lib/openal-soft/Alc/bs2b.c @@ -129,16 +129,16 @@ void bs2b_clear(struct bs2b *bs2b) memset(&bs2b->last_sample, 0, sizeof(bs2b->last_sample)); } /* bs2b_clear */ -void bs2b_cross_feed(struct bs2b *bs2b, float *restrict Left, float *restrict Right, unsigned int SamplesToDo) +void bs2b_cross_feed(struct bs2b *bs2b, float *restrict Left, float *restrict Right, int SamplesToDo) { float lsamples[128][2]; float rsamples[128][2]; - unsigned int base; + int base; for(base = 0;base < SamplesToDo;) { - unsigned int todo = minu(128, SamplesToDo-base); - unsigned int i; + int todo = mini(128, SamplesToDo-base); + int i; /* Process left input */ lsamples[0][0] = bs2b->a0_lo*Left[0] + diff --git a/Engine/lib/openal-soft/Alc/bsinc.c b/Engine/lib/openal-soft/Alc/bsinc.c deleted file mode 100644 index f795120f7..000000000 --- a/Engine/lib/openal-soft/Alc/bsinc.c +++ /dev/null @@ -1,981 +0,0 @@ - -#include "config.h" - -#include "AL/al.h" -#include "align.h" - -/* Table of windowed sinc coefficients and deltas. This 11th order filter - * has a rejection of -60 dB, yielding a transition width of ~0.302 - * (normalized frequency). Order increases when downsampling to a limit of - * one octave, after which the quality of the filter (transition width) - * suffers to reduce the CPU cost. The bandlimiting will cut all sound after - * downsampling by ~2.73 octaves. - */ -alignas(16) const ALfloat bsincTab[18840] = -{ - /* 24, 0 */ +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, - - /* 24, 0 */ +1.501390780e-03f, +3.431804419e-03f, +6.512803185e-03f, +1.091425387e-02f, +1.664594540e-02f, +2.351091132e-02f, +3.109255671e-02f, +3.878419288e-02f, +4.586050701e-02f, +5.158058002e-02f, +5.530384985e-02f, +5.659614054e-02f, +5.530384985e-02f, +5.158058002e-02f, +4.586050701e-02f, +3.878419288e-02f, +3.109255671e-02f, +2.351091132e-02f, +1.664594540e-02f, +1.091425387e-02f, +6.512803185e-03f, +3.431804419e-03f, +1.501390780e-03f, +4.573885647e-04f, - /* 24, 1 */ +1.413186400e-03f, +3.279858311e-03f, +6.282638036e-03f, +1.059932179e-02f, +1.625135142e-02f, +2.305547031e-02f, +3.060840342e-02f, +3.831365198e-02f, +4.545054680e-02f, +5.127577001e-02f, +5.513916011e-02f, +5.659104154e-02f, +5.545895049e-02f, +5.187752167e-02f, +4.626513642e-02f, +3.925233583e-02f, +3.157717954e-02f, +2.396921539e-02f, +1.704503934e-02f, +1.123445076e-02f, +6.748179094e-03f, +3.588275667e-03f, +1.593065611e-03f, +5.022154476e-04f, - /* 24, 2 */ +1.328380648e-03f, +3.132379333e-03f, +6.057656813e-03f, +1.028967374e-02f, +1.586133102e-02f, +2.260301890e-02f, +3.012488684e-02f, +3.784089895e-02f, +4.503543229e-02f, +5.096323022e-02f, +5.496495842e-02f, +5.657574693e-02f, +5.560438923e-02f, +5.216645963e-02f, +4.666426010e-02f, +3.971789474e-02f, +3.206210284e-02f, +2.443025293e-02f, +1.744855617e-02f, +1.155988996e-02f, +6.988790100e-03f, +3.749328623e-03f, +1.688282347e-03f, +5.494305796e-04f, - /* 24, 3 */ +1.246901403e-03f, +2.989308098e-03f, +5.837830254e-03f, +9.985325752e-03f, +1.547595434e-02f, +2.215368059e-02f, +2.964217216e-02f, +3.736611920e-02f, +4.461534144e-02f, +5.064310236e-02f, +5.478132634e-02f, +5.655026396e-02f, +5.574009777e-02f, +5.244726189e-02f, +4.705770477e-02f, +4.018068337e-02f, +3.254715574e-02f, +2.489389144e-02f, +1.785641537e-02f, +1.189054572e-02f, +7.234657995e-03f, +3.915018340e-03f, +1.787112015e-03f, +5.991047395e-04f, - /* 24, 4 */ +1.168676301e-03f, +2.850583915e-03f, +5.623126723e-03f, +9.686290690e-03f, +1.509528803e-02f, +2.170757578e-02f, +2.916042250e-02f, +3.688949768e-02f, +4.419045351e-02f, +5.031553118e-02f, +5.458834968e-02f, +5.651460469e-02f, +5.586601230e-02f, +5.271979985e-02f, +4.744529894e-02f, +4.064051541e-02f, +3.303216567e-02f, +2.535999546e-02f, +1.826853297e-02f, +1.222638897e-02f, +7.485801959e-03f, +4.085398290e-03f, +1.889625146e-03f, +6.513091287e-04f, - /* 24, 5 */ +1.093632798e-03f, +2.716144855e-03f, +5.413512274e-03f, +9.392578266e-03f, +1.471939531e-02f, +2.126482169e-02f, +2.867979883e-02f, +3.641121873e-02f, +4.376094899e-02f, +4.998066438e-02f, +5.438611851e-02f, +5.646878599e-02f, +5.598207354e-02f, +5.298394839e-02f, +4.782687301e-02f, +4.109720465e-02f, +3.351695842e-02f, +2.582842673e-02f, +1.868482156e-02f, +1.256738733e-02f, +7.742238512e-03f, +4.260520294e-03f, +1.995891717e-03f, +7.061153220e-04f, - /* 24, 6 */ +1.021698233e-03f, +2.585927824e-03f, +5.208950715e-03f, +9.104195104e-03f, +1.434833590e-02f, +2.082553239e-02f, +2.820045990e-02f, +3.593146595e-02f, +4.332700946e-02f, +4.963865252e-02f, +5.417472708e-02f, +5.641282954e-02f, +5.608822683e-02f, +5.323958602e-02f, +4.820225940e-02f, +4.155056502e-02f, +3.400135826e-02f, +2.629904416e-02f, +1.910519032e-02f, +1.291350505e-02f, +8.003981455e-03f, +4.440434453e-03f, +2.105981077e-03f, +7.635952183e-04f, - /* 24, 7 */ +9.527998831e-04f, +2.459868628e-03f, +5.009403670e-03f, +8.821144768e-03f, +1.398216608e-02f, +2.038981869e-02f, +2.772256216e-02f, +3.545042216e-02f, +4.288881749e-02f, +4.928964888e-02f, +5.395427373e-02f, +5.634676181e-02f, +5.618442211e-02f, +5.348659488e-02f, +4.857129262e-02f, +4.200041076e-02f, +3.448518802e-02f, +2.677170395e-02f, +1.952954505e-02f, +1.326470299e-02f, +8.271041819e-03f, +4.625189083e-03f, +2.219961884e-03f, +8.238209888e-04f, - /* 24, 8 */ +8.868650246e-04f, +2.337902042e-03f, +4.814830642e-03f, +8.543427812e-03f, +1.362093865e-02f, +1.995778816e-02f, +2.724625964e-02f, +3.496826923e-02f, +4.244655653e-02f, +4.893380942e-02f, +5.372486088e-02f, +5.627061400e-02f, +5.627061400e-02f, +5.372486088e-02f, +4.893380942e-02f, +4.244655653e-02f, +3.496826923e-02f, +2.724625964e-02f, +1.995778816e-02f, +1.362093865e-02f, +8.543427812e-03f, +4.814830642e-03f, +2.337902042e-03f, +8.868650246e-04f, - /* 24, 9 */ +8.238209888e-04f, +2.219961884e-03f, +4.625189083e-03f, +8.271041819e-03f, +1.326470299e-02f, +1.952954505e-02f, +2.677170395e-02f, +3.448518802e-02f, +4.200041076e-02f, +4.857129262e-02f, +5.348659488e-02f, +5.618442211e-02f, +5.634676181e-02f, +5.395427373e-02f, +4.928964888e-02f, +4.288881749e-02f, +3.545042216e-02f, +2.772256216e-02f, +2.038981869e-02f, +1.398216608e-02f, +8.821144768e-03f, +5.009403670e-03f, +2.459868628e-03f, +9.527998831e-04f, - /* 24,10 */ +7.635952183e-04f, +2.105981077e-03f, +4.440434453e-03f, +8.003981455e-03f, +1.291350505e-02f, +1.910519032e-02f, +2.629904416e-02f, +3.400135826e-02f, +4.155056502e-02f, +4.820225940e-02f, +5.323958602e-02f, +5.608822683e-02f, +5.641282954e-02f, +5.417472708e-02f, +4.963865252e-02f, +4.332700946e-02f, +3.593146595e-02f, +2.820045990e-02f, +2.082553239e-02f, +1.434833590e-02f, +9.104195104e-03f, +5.208950715e-03f, +2.585927824e-03f, +1.021698233e-03f, - /* 24,11 */ +7.061153220e-04f, +1.995891717e-03f, +4.260520294e-03f, +7.742238512e-03f, +1.256738733e-02f, +1.868482156e-02f, +2.582842673e-02f, +3.351695842e-02f, +4.109720465e-02f, +4.782687301e-02f, +5.298394839e-02f, +5.598207354e-02f, +5.646878599e-02f, +5.438611851e-02f, +4.998066438e-02f, +4.376094899e-02f, +3.641121873e-02f, +2.867979883e-02f, +2.126482169e-02f, +1.471939531e-02f, +9.392578266e-03f, +5.413512274e-03f, +2.716144855e-03f, +1.093632798e-03f, - /* 24,12 */ +6.513091287e-04f, +1.889625146e-03f, +4.085398290e-03f, +7.485801959e-03f, +1.222638897e-02f, +1.826853297e-02f, +2.535999546e-02f, +3.303216567e-02f, +4.064051541e-02f, +4.744529894e-02f, +5.271979985e-02f, +5.586601230e-02f, +5.651460469e-02f, +5.458834968e-02f, +5.031553118e-02f, +4.419045351e-02f, +3.688949768e-02f, +2.916042250e-02f, +2.170757578e-02f, +1.509528803e-02f, +9.686290690e-03f, +5.623126723e-03f, +2.850583915e-03f, +1.168676301e-03f, - /* 24,13 */ +5.991047395e-04f, +1.787112015e-03f, +3.915018340e-03f, +7.234657995e-03f, +1.189054572e-02f, +1.785641537e-02f, +2.489389144e-02f, +3.254715574e-02f, +4.018068337e-02f, +4.705770477e-02f, +5.244726189e-02f, +5.574009777e-02f, +5.655026396e-02f, +5.478132634e-02f, +5.064310236e-02f, +4.461534144e-02f, +3.736611920e-02f, +2.964217216e-02f, +2.215368059e-02f, +1.547595434e-02f, +9.985325752e-03f, +5.837830254e-03f, +2.989308098e-03f, +1.246901403e-03f, - /* 24,14 */ +5.494305796e-04f, +1.688282347e-03f, +3.749328623e-03f, +6.988790100e-03f, +1.155988996e-02f, +1.744855617e-02f, +2.443025293e-02f, +3.206210284e-02f, +3.971789474e-02f, +4.666426010e-02f, +5.216645963e-02f, +5.560438923e-02f, +5.657574693e-02f, +5.496495842e-02f, +5.096323022e-02f, +4.503543229e-02f, +3.784089895e-02f, +3.012488684e-02f, +2.260301890e-02f, +1.586133102e-02f, +1.028967374e-02f, +6.057656813e-03f, +3.132379333e-03f, +1.328380648e-03f, - /* 24,15 */ +5.022154476e-04f, +1.593065611e-03f, +3.588275667e-03f, +6.748179094e-03f, +1.123445076e-02f, +1.704503934e-02f, +2.396921539e-02f, +3.157717954e-02f, +3.925233583e-02f, +4.626513642e-02f, +5.187752167e-02f, +5.545895049e-02f, +5.659104154e-02f, +5.513916011e-02f, +5.127577001e-02f, +4.545054680e-02f, +3.831365198e-02f, +3.060840342e-02f, +2.305547031e-02f, +1.625135142e-02f, +1.059932179e-02f, +6.282638036e-03f, +3.279858311e-03f, +1.413186400e-03f, - /* 24, 0 */ -1.127794091e-03f, -1.412146034e-03f, -3.831821143e-04f, +3.227045776e-03f, +1.066768284e-02f, +2.270769386e-02f, +3.918787347e-02f, +5.876378120e-02f, +7.897914846e-02f, +9.670702233e-02f, +1.088639494e-01f, +1.131922811e-01f, +1.088639494e-01f, +9.670702233e-02f, +7.897914846e-02f, +5.876378120e-02f, +3.918787347e-02f, +2.270769386e-02f, +1.066768284e-02f, +3.227045776e-03f, -3.831821143e-04f, -1.412146034e-03f, -1.127794091e-03f, -4.881068065e-04f, - /* 24, 1 */ -1.090580766e-03f, -1.420873386e-03f, -5.091873886e-04f, +2.900756187e-03f, +1.007202248e-02f, +2.181774373e-02f, +3.804709217e-02f, +5.749143322e-02f, +7.775467878e-02f, +9.573284944e-02f, +1.083163184e-01f, +1.131750947e-01f, +1.093805191e-01f, +9.765915788e-02f, +8.019389052e-02f, +6.003885715e-02f, +4.034112484e-02f, +2.361538773e-02f, +1.128161899e-02f, +3.568453927e-03f, -2.470940015e-04f, -1.398398357e-03f, -1.163769773e-03f, -5.264712252e-04f, - /* 24, 2 */ -1.052308046e-03f, -1.424863695e-03f, -6.254426725e-04f, +2.589304991e-03f, +9.494532203e-03f, +2.094570441e-02f, +3.691925193e-02f, +5.622252667e-02f, +7.652128881e-02f, +9.473734332e-02f, +1.077380418e-01f, +1.131235487e-01f, +1.098656350e-01f, +9.858856505e-02f, +8.139809812e-02f, +6.131593665e-02f, +4.150635732e-02f, +2.454063933e-02f, +1.191392194e-02f, +3.925253463e-03f, -1.005901154e-04f, -1.379341793e-03f, -1.198322390e-03f, -5.655319713e-04f, - /* 24, 3 */ -1.013146991e-03f, -1.424394748e-03f, -7.322803183e-04f, +2.292405169e-03f, +8.935092066e-03f, +2.009172411e-02f, +3.580480556e-02f, +5.495776346e-02f, +7.527978553e-02f, +9.372122042e-02f, +1.071295572e-01f, +1.130376830e-01f, +1.103189277e-01f, +9.949456662e-02f, +8.259096568e-02f, +6.259428489e-02f, +4.268306423e-02f, +2.548324354e-02f, +1.256466766e-02f, +4.297709369e-03f, +5.666214823e-05f, -1.354682733e-03f, -1.231259367e-03f, -6.052075404e-04f, - /* 24, 4 */ -9.732614753e-04f, -1.419738627e-03f, -8.300317840e-04f, +2.009763334e-03f, +8.393568260e-03f, +1.925593236e-02f, +3.470418745e-02f, +5.369783330e-02f, +7.403097485e-02f, +9.268520876e-02f, +1.064913245e-01f, +1.129175635e-01f, +1.107400515e-01f, +1.003764999e-01f, +8.377168968e-02f, +6.387315732e-02f, +4.387072153e-02f, +2.644297610e-02f, +1.323391671e-02f, +4.686078310e-03f, +2.249946366e-04f, -1.324122762e-03f, -1.262381011e-03f, -6.454100415e-04f, - /* 24, 5 */ -9.328082015e-04f, -1.411161525e-03f, -9.190272443e-04f, +1.741080225e-03f, +7.869813543e-03f, +1.843844016e-02f, +3.361781336e-02f, +5.244341325e-02f, +7.277566076e-02f, +9.163004717e-02f, +1.058238246e-01f, +1.127632829e-01f, +1.111286847e-01f, +1.012337173e-01f, +8.493946948e-02f, +6.515180031e-02f, +4.506878807e-02f, +2.741959349e-02f, +1.392171386e-02f, +5.090608136e-03f, +4.047380047e-04f, -1.287358924e-03f, -1.291480556e-03f, -6.860450823e-04f, - /* 24, 6 */ -8.919367204e-04f, -1.398923562e-03f, -9.995952120e-04f, +1.486051192e-03f, +7.363667669e-03f, +1.763934022e-02f, +3.254608027e-02f, +5.119516710e-02f, +7.151464464e-02f, +9.055648452e-02f, +1.051275597e-01f, +1.125749599e-01f, +1.114845301e-01f, +1.020655875e-01f, +8.609350809e-02f, +6.642945179e-02f, +4.627670593e-02f, +2.841283293e-02f, +1.462808772e-02f, +5.511537402e-03f, +5.962212734e-04f, -1.244083985e-03f, -1.318344226e-03f, -7.270116618e-04f, - /* 24, 7 */ -8.507894667e-04f, -1.383278624e-03f, -1.072062171e-03f, +1.244366682e-03f, +6.874957829e-03f, +1.685870710e-02f, +3.148936626e-02f, +4.995374490e-02f, +7.024872443e-02f, +8.946527894e-02f, +1.044030520e-01f, +1.123527394e-01f, +1.118073151e-01f, +1.028714955e-01f, +8.723301301e-02f, +6.770534195e-02f, +4.749390083e-02f, +2.942241233e-02f, +1.535305047e-02f, +5.949094883e-03f, +7.997713791e-04f, -1.193986722e-03f, -1.342751302e-03f, -7.682020711e-04f, - /* 24, 8 */ -8.095018024e-04f, -1.364474212e-03f, -1.136752219e-03f, +1.015712718e-03f, +6.403499096e-03f, +1.609659749e-02f, +3.044803032e-02f, +4.871978245e-02f, +6.897869391e-02f, +8.835719701e-02f, +1.036508436e-01f, +1.120967923e-01f, +1.120967923e-01f, +1.036508436e-01f, +8.835719701e-02f, +6.897869391e-02f, +4.871978245e-02f, +3.044803032e-02f, +1.609659749e-02f, +6.403499096e-03f, +1.015712718e-03f, -1.136752219e-03f, -1.364474212e-03f, -8.095018024e-04f, - /* 24, 9 */ -7.682020711e-04f, -1.342751302e-03f, -1.193986722e-03f, +7.997713791e-04f, +5.949094883e-03f, +1.535305047e-02f, +2.942241233e-02f, +4.749390083e-02f, +6.770534195e-02f, +8.723301301e-02f, +1.028714955e-01f, +1.118073151e-01f, +1.123527394e-01f, +1.044030520e-01f, +8.946527894e-02f, +7.024872443e-02f, +4.995374490e-02f, +3.148936626e-02f, +1.685870710e-02f, +6.874957829e-03f, +1.244366682e-03f, -1.072062171e-03f, -1.383278624e-03f, -8.507894667e-04f, - /* 24,10 */ -7.270116618e-04f, -1.318344226e-03f, -1.244083985e-03f, +5.962212734e-04f, +5.511537402e-03f, +1.462808772e-02f, +2.841283293e-02f, +4.627670593e-02f, +6.642945179e-02f, +8.609350809e-02f, +1.020655875e-01f, +1.114845301e-01f, +1.125749599e-01f, +1.051275597e-01f, +9.055648452e-02f, +7.151464464e-02f, +5.119516710e-02f, +3.254608027e-02f, +1.763934022e-02f, +7.363667669e-03f, +1.486051192e-03f, -9.995952120e-04f, -1.398923562e-03f, -8.919367204e-04f, - /* 24,11 */ -6.860450823e-04f, -1.291480556e-03f, -1.287358924e-03f, +4.047380047e-04f, +5.090608136e-03f, +1.392171386e-02f, +2.741959349e-02f, +4.506878807e-02f, +6.515180031e-02f, +8.493946948e-02f, +1.012337173e-01f, +1.111286847e-01f, +1.127632829e-01f, +1.058238246e-01f, +9.163004717e-02f, +7.277566076e-02f, +5.244341325e-02f, +3.361781336e-02f, +1.843844016e-02f, +7.869813543e-03f, +1.741080225e-03f, -9.190272443e-04f, -1.411161525e-03f, -9.328082015e-04f, - /* 24,12 */ -6.454100415e-04f, -1.262381011e-03f, -1.324122762e-03f, +2.249946366e-04f, +4.686078310e-03f, +1.323391671e-02f, +2.644297610e-02f, +4.387072153e-02f, +6.387315732e-02f, +8.377168968e-02f, +1.003764999e-01f, +1.107400515e-01f, +1.129175635e-01f, +1.064913245e-01f, +9.268520876e-02f, +7.403097485e-02f, +5.369783330e-02f, +3.470418745e-02f, +1.925593236e-02f, +8.393568260e-03f, +2.009763334e-03f, -8.300317840e-04f, -1.419738627e-03f, -9.732614753e-04f, - /* 24,13 */ -6.052075404e-04f, -1.231259367e-03f, -1.354682733e-03f, +5.666214823e-05f, +4.297709369e-03f, +1.256466766e-02f, +2.548324354e-02f, +4.268306423e-02f, +6.259428489e-02f, +8.259096568e-02f, +9.949456662e-02f, +1.103189277e-01f, +1.130376830e-01f, +1.071295572e-01f, +9.372122042e-02f, +7.527978553e-02f, +5.495776346e-02f, +3.580480556e-02f, +2.009172411e-02f, +8.935092066e-03f, +2.292405169e-03f, -7.322803183e-04f, -1.424394748e-03f, -1.013146991e-03f, - /* 24,14 */ -5.655319713e-04f, -1.198322390e-03f, -1.379341793e-03f, -1.005901154e-04f, +3.925253463e-03f, +1.191392194e-02f, +2.454063933e-02f, +4.150635732e-02f, +6.131593665e-02f, +8.139809812e-02f, +9.858856505e-02f, +1.098656350e-01f, +1.131235487e-01f, +1.077380418e-01f, +9.473734332e-02f, +7.652128881e-02f, +5.622252667e-02f, +3.691925193e-02f, +2.094570441e-02f, +9.494532203e-03f, +2.589304991e-03f, -6.254426725e-04f, -1.424863695e-03f, -1.052308046e-03f, - /* 24,15 */ -5.264712252e-04f, -1.163769773e-03f, -1.398398357e-03f, -2.470940015e-04f, +3.568453927e-03f, +1.128161899e-02f, +2.361538773e-02f, +4.034112484e-02f, +6.003885715e-02f, +8.019389052e-02f, +9.765915788e-02f, +1.093805191e-01f, +1.131750947e-01f, +1.083163184e-01f, +9.573284944e-02f, +7.775467878e-02f, +5.749143322e-02f, +3.804709217e-02f, +2.181774373e-02f, +1.007202248e-02f, +2.900756187e-03f, -5.091873886e-04f, -1.420873386e-03f, -1.090580766e-03f, - /* 24, 0 */ -6.542299160e-04f, -2.850723396e-03f, -6.490258587e-03f, -9.960104872e-03f, -9.809478345e-03f, -1.578994128e-03f, +1.829834548e-02f, +5.025161588e-02f, +9.015425381e-02f, +1.297327779e-01f, +1.589915292e-01f, +1.697884216e-01f, +1.589915292e-01f, +1.297327779e-01f, +9.015425381e-02f, +5.025161588e-02f, +1.829834548e-02f, -1.578994128e-03f, -9.809478345e-03f, -9.960104872e-03f, -6.490258587e-03f, -2.850723396e-03f, -6.542299160e-04f, +6.349952235e-05f, - /* 24, 1 */ -5.715660670e-04f, -2.664319167e-03f, -6.241370053e-03f, -9.805460951e-03f, -1.000906214e-02f, -2.409006146e-03f, +1.668518463e-02f, +4.795494216e-02f, +8.756853655e-02f, +1.274593023e-01f, +1.576392865e-01f, +1.697451719e-01f, +1.602699418e-01f, +1.319653222e-01f, +9.273931864e-02f, +5.258078166e-02f, +1.996024013e-02f, -7.024321916e-04f, -9.578061730e-03f, -1.010098516e-02f, -6.739131402e-03f, -3.043301383e-03f, -7.429059724e-04f, +4.968305000e-05f, - /* 24, 2 */ -4.947700224e-04f, -2.484234158e-03f, -5.993080931e-03f, -9.638098137e-03f, -1.017794029e-02f, -3.193110201e-03f, +1.512113085e-02f, +4.569233080e-02f, +8.498458400e-02f, +1.251473534e-01f, +1.562147818e-01f, +1.696154741e-01f, +1.614730379e-01f, +1.341545065e-01f, +9.532128436e-02f, +5.494080034e-02f, +2.167042077e-02f, +2.212715669e-04f, -9.313697641e-03f, -1.022703863e-02f, -6.987342300e-03f, -3.241882098e-03f, -8.377276082e-04f, +3.267464436e-05f, - /* 24, 3 */ -4.236872966e-04f, -2.310589033e-03f, -5.745975157e-03f, -9.459041324e-03f, -1.031725016e-02f, -3.931996122e-03f, +1.360648366e-02f, +4.346528177e-02f, +8.240478048e-02f, +1.227994149e-01f, +1.547196623e-01f, +1.693994819e-01f, +1.625994153e-01f, +1.362979353e-01f, +9.789767810e-02f, +5.732996534e-02f, +2.342836441e-02f, +1.192656872e-03f, -9.015286272e-03f, -1.033718507e-02f, -7.234214214e-03f, -3.446268223e-03f, -9.388162067e-04f, +1.226776811e-05f, - /* 24, 4 */ -3.581542611e-04f, -2.143480447e-03f, -5.500605429e-03f, -9.269294257e-03f, -1.042813706e-02f, -4.626399385e-03f, +1.214146970e-02f, +4.127522351e-02f, +7.983147433e-02f, +1.204179923e-01f, +1.531556516e-01f, +1.690974512e-01f, +1.636477581e-01f, +1.383932498e-01f, +1.004660042e-01f, +5.974650439e-02f, +2.523347234e-02f, +2.212209196e-03f, -8.681745020e-03f, -1.043032883e-02f, -7.479039479e-03f, -3.656235461e-03f, -1.046280200e-03f, -1.174474466e-05f, - /* 24, 5 */ -2.979990698e-04f, -1.982981877e-03f, -5.257493218e-03f, -9.069838305e-03f, -1.051175200e-02f, -5.277098842e-03f, +1.072624378e-02f, +3.912351177e-02f, +7.726697481e-02f, +1.180056089e-01f, +1.515245471e-01f, +1.687097397e-01f, +1.646168392e-01f, +1.404381320e-01f, +1.030237476e-01f, +6.218858132e-02f, +2.708506973e-02f, +3.280357753e-03f, -8.312010872e-03f, -1.050536039e-02f, -7.721080180e-03f, -3.871531888e-03f, -1.160214103e-03f, -3.957001380e-05f, - /* 24, 6 */ -2.430425717e-04f, -1.829144469e-03f, -5.017128860e-03f, -8.861631306e-03f, -1.056924947e-02f, -5.884914422e-03f, +9.360889972e-03f, +3.701142868e-02f, +7.471354913e-02f, +1.155648023e-01f, +1.498282170e-01f, +1.682368061e-01f, +1.655055219e-01f, +1.424303080e-01f, +1.055683777e-01f, +6.465429798e-02f, +2.898240538e-02f, +4.397473555e-03f, -7.905042791e-03f, -1.056115807e-02f, -7.959568583e-03f, -4.091877352e-03f, -1.280697546e-03f, -7.141440876e-05f, - /* 24, 7 */ -1.930992056e-04f, -1.681997919e-03f, -4.779971710e-03f, -8.645606504e-03f, -1.060178532e-02f, -6.450704795e-03f, +8.045422842e-03f, +3.494018190e-02f, +7.217341954e-02f, +1.130981205e-01f, +1.480685972e-01f, +1.676792097e-01f, +1.663127619e-01f, +1.443675516e-01f, +1.080973515e-01f, +6.714169632e-02f, +3.092465153e-02f, +5.563867546e-03f, -7.459824124e-03f, -1.059658974e-02f, -8.193707637e-03f, -4.316962918e-03f, -1.407794310e-03f, -1.074828157e-04f, - /* 24, 8 */ -1.479778772e-04f, -1.541551365e-03f, -4.546450360e-03f, -8.422671559e-03f, -1.061051465e-02f, -6.975365011e-03f, +6.779788805e-03f, +3.291090392e-02f, +6.964876056e-02f, +1.106081178e-01f, +1.462476880e-01f, +1.670376092e-01f, +1.670376092e-01f, +1.462476880e-01f, +1.106081178e-01f, +6.964876056e-02f, +3.291090392e-02f, +6.779788805e-03f, -6.975365011e-03f, -1.061051465e-02f, -8.422671559e-03f, -4.546450360e-03f, -1.541551365e-03f, -1.479778772e-04f, - /* 24, 9 */ -1.074828157e-04f, -1.407794310e-03f, -4.316962918e-03f, -8.193707637e-03f, -1.059658974e-02f, -7.459824124e-03f, +5.563867546e-03f, +3.092465153e-02f, +6.714169632e-02f, +1.080973515e-01f, +1.443675516e-01f, +1.663127619e-01f, +1.676792097e-01f, +1.480685972e-01f, +1.130981205e-01f, +7.217341954e-02f, +3.494018190e-02f, +8.045422842e-03f, -6.450704795e-03f, -1.060178532e-02f, -8.645606504e-03f, -4.779971710e-03f, -1.681997919e-03f, -1.930992056e-04f, - /* 24,10 */ -7.141440876e-05f, -1.280697546e-03f, -4.091877352e-03f, -7.959568583e-03f, -1.056115807e-02f, -7.905042791e-03f, +4.397473555e-03f, +2.898240538e-02f, +6.465429798e-02f, +1.055683777e-01f, +1.424303080e-01f, +1.655055219e-01f, +1.682368061e-01f, +1.498282170e-01f, +1.155648023e-01f, +7.471354913e-02f, +3.701142868e-02f, +9.360889972e-03f, -5.884914422e-03f, -1.056924947e-02f, -8.861631306e-03f, -5.017128860e-03f, -1.829144469e-03f, -2.430425717e-04f, - /* 24,11 */ -3.957001380e-05f, -1.160214103e-03f, -3.871531888e-03f, -7.721080180e-03f, -1.050536039e-02f, -8.312010872e-03f, +3.280357753e-03f, +2.708506973e-02f, +6.218858132e-02f, +1.030237476e-01f, +1.404381320e-01f, +1.646168392e-01f, +1.687097397e-01f, +1.515245471e-01f, +1.180056089e-01f, +7.726697481e-02f, +3.912351177e-02f, +1.072624378e-02f, -5.277098842e-03f, -1.051175200e-02f, -9.069838305e-03f, -5.257493218e-03f, -1.982981877e-03f, -2.979990698e-04f, - /* 24,12 */ -1.174474466e-05f, -1.046280200e-03f, -3.656235461e-03f, -7.479039479e-03f, -1.043032883e-02f, -8.681745020e-03f, +2.212209196e-03f, +2.523347234e-02f, +5.974650439e-02f, +1.004660042e-01f, +1.383932498e-01f, +1.636477581e-01f, +1.690974512e-01f, +1.531556516e-01f, +1.204179923e-01f, +7.983147433e-02f, +4.127522351e-02f, +1.214146970e-02f, -4.626399385e-03f, -1.042813706e-02f, -9.269294257e-03f, -5.500605429e-03f, -2.143480447e-03f, -3.581542611e-04f, - /* 24,13 */ +1.226776811e-05f, -9.388162067e-04f, -3.446268223e-03f, -7.234214214e-03f, -1.033718507e-02f, -9.015286272e-03f, +1.192656872e-03f, +2.342836441e-02f, +5.732996534e-02f, +9.789767810e-02f, +1.362979353e-01f, +1.625994153e-01f, +1.693994819e-01f, +1.547196623e-01f, +1.227994149e-01f, +8.240478048e-02f, +4.346528177e-02f, +1.360648366e-02f, -3.931996122e-03f, -1.031725016e-02f, -9.459041324e-03f, -5.745975157e-03f, -2.310589033e-03f, -4.236872966e-04f, - /* 24,14 */ +3.267464436e-05f, -8.377276082e-04f, -3.241882098e-03f, -6.987342300e-03f, -1.022703863e-02f, -9.313697641e-03f, +2.212715669e-04f, +2.167042077e-02f, +5.494080034e-02f, +9.532128436e-02f, +1.341545065e-01f, +1.614730379e-01f, +1.696154741e-01f, +1.562147818e-01f, +1.251473534e-01f, +8.498458400e-02f, +4.569233080e-02f, +1.512113085e-02f, -3.193110201e-03f, -1.017794029e-02f, -9.638098137e-03f, -5.993080931e-03f, -2.484234158e-03f, -4.947700224e-04f, - /* 24,15 */ +4.968305000e-05f, -7.429059724e-04f, -3.043301383e-03f, -6.739131402e-03f, -1.010098516e-02f, -9.578061730e-03f, -7.024321916e-04f, +1.996024013e-02f, +5.258078166e-02f, +9.273931864e-02f, +1.319653222e-01f, +1.602699418e-01f, +1.697451719e-01f, +1.576392865e-01f, +1.274593023e-01f, +8.756853655e-02f, +4.795494216e-02f, +1.668518463e-02f, -2.409006146e-03f, -1.000906214e-02f, -9.805460951e-03f, -6.241370053e-03f, -2.664319167e-03f, -5.715660670e-04f, - /* 24, 0 */ +1.619229527e-03f, +2.585184252e-03f, +7.650378125e-04f, -6.171975840e-03f, -1.695416291e-02f, -2.423274385e-02f, -1.612533623e-02f, +1.737483974e-02f, +7.628093610e-02f, +1.465254238e-01f, +2.041060488e-01f, +2.263845622e-01f, +2.041060488e-01f, +1.465254238e-01f, +7.628093610e-02f, +1.737483974e-02f, -1.612533623e-02f, -2.423274385e-02f, -1.695416291e-02f, -6.171975840e-03f, +7.650378125e-04f, +2.585184252e-03f, +1.619229527e-03f, +4.203426526e-04f, - /* 24, 1 */ +1.531668339e-03f, +2.575087939e-03f, +1.015030141e-03f, -5.584253498e-03f, -1.627529113e-02f, -2.409742304e-02f, -1.730694612e-02f, +1.446720847e-02f, +7.205349539e-02f, +1.422361210e-01f, +2.013530187e-01f, +2.262942875e-01f, +2.067165090e-01f, +1.507648574e-01f, +8.055624103e-02f, +2.038667606e-02f, -1.484111026e-02f, -2.430745079e-02f, -1.762106127e-02f, -6.776879595e-03f, +4.938567092e-04f, +2.584413048e-03f, +1.706479068e-03f, +4.743886054e-04f, - /* 24, 2 */ +1.444251783e-03f, +2.554897668e-03f, +1.244217996e-03f, -5.014646771e-03f, -1.558700841e-02f, -2.390468713e-02f, -1.838770218e-02f, +1.166535016e-02f, +6.787901036e-02f, +1.379034790e-01f, +1.984620386e-01f, +2.260236194e-01f, +2.091800031e-01f, +1.549479100e-01f, +8.487414623e-02f, +2.350091114e-02f, -1.345266938e-02f, -2.431836796e-02f, -1.827334019e-02f, -7.397926552e-03f, +2.011593926e-04f, +2.571998910e-03f, +1.792931314e-03f, +5.318997770e-04f, - /* 24, 3 */ +1.357406375e-03f, +2.525382259e-03f, +1.453038602e-03f, -4.463987325e-03f, -1.489178967e-02f, -2.365774922e-02f, -1.936952211e-02f, +8.970595311e-03f, +6.376239146e-02f, +1.335340325e-01f, +1.954379419e-01f, +2.255730254e-01f, +2.114923689e-01f, +1.590681020e-01f, +8.922922426e-02f, +2.671549986e-02f, -1.195858585e-02f, -2.426235104e-02f, -1.890827435e-02f, -8.033973302e-03f, -1.133208208e-04f, +2.547167581e-03f, +1.878071789e-03f, +5.928148062e-04f, - /* 24, 4 */ +1.271528621e-03f, +2.487303056e-03f, +1.641978155e-03f, -3.933006021e-03f, -1.419201875e-02f, -2.335982837e-02f, -2.025447087e-02f, +6.384038515e-03f, +5.970836021e-02f, +1.291343068e-01f, +1.922857641e-01f, +2.249432832e-01f, +2.136496877e-01f, +1.631190001e-01f, +9.361589375e-02f, +3.002815853e-02f, -1.035761042e-02f, -2.413629608e-02f, -1.952306378e-02f, -8.683770335e-03f, -4.497860188e-04f, +2.509149105e-03f, +1.961357874e-03f, +6.570484107e-04f, - /* 24, 5 */ +1.186984902e-03f, +2.441411339e-03f, +1.811567846e-03f, -3.422334900e-03f, -1.348998519e-02f, -2.301414142e-02f, -2.104475231e-02f, +3.906540614e-03f, +5.572144173e-02f, +1.247108046e-01f, +1.890107314e-01f, +2.241354793e-01f, +2.156482934e-01f, +1.670942309e-01f, +9.802842938e-02f, +3.343636564e-02f, -8.648679404e-03f, -2.393714847e-02f, -2.011483893e-02f, -9.345961431e-03f, -8.083699235e-04f, +2.457181100e-03f, +2.042219659e-03f, +7.244903794e-04f, - /* 24, 6 */ +1.104111497e-03f, +2.388445882e-03f, +1.962379900e-03f, -2.932509404e-03f, -1.278788140e-02f, -2.262389503e-02f, -2.174270056e-02f, +1.538731332e-03f, +5.180595798e-02f, +1.202699924e-01f, +1.856182490e-01f, +2.231510062e-01f, +2.174847808e-01f, +1.709874949e-01f, +1.024609723e-01f, +3.693736330e-02f, -6.830921421e-03f, -2.366191192e-02f, -2.068066597e-02f, -1.001908338e-02f, -1.189134206e-03f, +2.390512141e-03f, +2.120060933e-03f, +7.950046334e-04f, - /* 24, 7 */ +1.023214729e-03f, +2.329130668e-03f, +2.095023622e-03f, -2.463970822e-03f, -1.208780014e-02f, -2.219227795e-02f, -2.235077131e-02f, -7.189875350e-04f, +4.796602143e-02f, +1.158182872e-01f, +1.821138888e-01f, +2.219915591e-01f, +2.191560137e-01f, +1.747925803e-01f, +1.069075413e-01f, +4.052815924e-02f, -4.903663772e-03f, -2.330765754e-02f, -2.121755248e-02f, -1.070156599e-02f, -1.592064902e-03f, +2.308405249e-03f, +2.194260355e-03f, +8.684283615e-04f, - /* 24, 8 */ +9.445712380e-04f, +2.264172764e-03f, +2.210141486e-03f, -2.017068943e-03f, -1.139173248e-02f, -2.172245353e-02f, -2.287153293e-02f, -2.866438416e-03f, +4.420552946e-02f, +1.113620436e-01f, +1.785033768e-01f, +2.206591322e-01f, +2.206591322e-01f, +1.785033768e-01f, +1.113620436e-01f, +4.420552946e-02f, -2.866438416e-03f, -2.287153293e-02f, -2.172245353e-02f, -1.139173248e-02f, -2.017068943e-03f, +2.210141486e-03f, +2.264172764e-03f, +9.445712380e-04f, - /* 24, 9 */ +8.684283615e-04f, +2.194260355e-03f, +2.308405249e-03f, -1.592064902e-03f, -1.070156599e-02f, -2.121755248e-02f, -2.330765754e-02f, -4.903663772e-03f, +4.052815924e-02f, +1.069075413e-01f, +1.747925803e-01f, +2.191560137e-01f, +2.219915591e-01f, +1.821138888e-01f, +1.158182872e-01f, +4.796602143e-02f, -7.189875350e-04f, -2.235077131e-02f, -2.219227795e-02f, -1.208780014e-02f, -2.463970822e-03f, +2.095023622e-03f, +2.329130668e-03f, +1.023214729e-03f, - /* 24,10 */ +7.950046334e-04f, +2.120060933e-03f, +2.390512141e-03f, -1.189134206e-03f, -1.001908338e-02f, -2.068066597e-02f, -2.366191192e-02f, -6.830921421e-03f, +3.693736330e-02f, +1.024609723e-01f, +1.709874949e-01f, +2.174847808e-01f, +2.231510062e-01f, +1.856182490e-01f, +1.202699924e-01f, +5.180595798e-02f, +1.538731332e-03f, -2.174270056e-02f, -2.262389503e-02f, -1.278788140e-02f, -2.932509404e-03f, +1.962379900e-03f, +2.388445882e-03f, +1.104111497e-03f, - /* 24,11 */ +7.244903794e-04f, +2.042219659e-03f, +2.457181100e-03f, -8.083699235e-04f, -9.345961431e-03f, -2.011483893e-02f, -2.393714847e-02f, -8.648679404e-03f, +3.343636564e-02f, +9.802842938e-02f, +1.670942309e-01f, +2.156482934e-01f, +2.241354793e-01f, +1.890107314e-01f, +1.247108046e-01f, +5.572144173e-02f, +3.906540614e-03f, -2.104475231e-02f, -2.301414142e-02f, -1.348998519e-02f, -3.422334900e-03f, +1.811567846e-03f, +2.441411339e-03f, +1.186984902e-03f, - /* 24,12 */ +6.570484107e-04f, +1.961357874e-03f, +2.509149105e-03f, -4.497860188e-04f, -8.683770335e-03f, -1.952306378e-02f, -2.413629608e-02f, -1.035761042e-02f, +3.002815853e-02f, +9.361589375e-02f, +1.631190001e-01f, +2.136496877e-01f, +2.249432832e-01f, +1.922857641e-01f, +1.291343068e-01f, +5.970836021e-02f, +6.384038515e-03f, -2.025447087e-02f, -2.335982837e-02f, -1.419201875e-02f, -3.933006021e-03f, +1.641978155e-03f, +2.487303056e-03f, +1.271528621e-03f, - /* 24,13 */ +5.928148062e-04f, +1.878071789e-03f, +2.547167581e-03f, -1.133208208e-04f, -8.033973302e-03f, -1.890827435e-02f, -2.426235104e-02f, -1.195858585e-02f, +2.671549986e-02f, +8.922922426e-02f, +1.590681020e-01f, +2.114923689e-01f, +2.255730254e-01f, +1.954379419e-01f, +1.335340325e-01f, +6.376239146e-02f, +8.970595311e-03f, -1.936952211e-02f, -2.365774922e-02f, -1.489178967e-02f, -4.463987325e-03f, +1.453038602e-03f, +2.525382259e-03f, +1.357406375e-03f, - /* 24,14 */ +5.318997770e-04f, +1.792931314e-03f, +2.571998910e-03f, +2.011593926e-04f, -7.397926552e-03f, -1.827334019e-02f, -2.431836796e-02f, -1.345266938e-02f, +2.350091114e-02f, +8.487414623e-02f, +1.549479100e-01f, +2.091800031e-01f, +2.260236194e-01f, +1.984620386e-01f, +1.379034790e-01f, +6.787901036e-02f, +1.166535016e-02f, -1.838770218e-02f, -2.390468713e-02f, -1.558700841e-02f, -5.014646771e-03f, +1.244217996e-03f, +2.554897668e-03f, +1.444251783e-03f, - /* 24,15 */ +4.743886054e-04f, +1.706479068e-03f, +2.584413048e-03f, +4.938567092e-04f, -6.776879595e-03f, -1.762106127e-02f, -2.430745079e-02f, -1.484111026e-02f, +2.038667606e-02f, +8.055624103e-02f, +1.507648574e-01f, +2.067165090e-01f, +2.262942875e-01f, +2.013530187e-01f, +1.422361210e-01f, +7.205349539e-02f, +1.446720847e-02f, -1.730694612e-02f, -2.409742304e-02f, -1.627529113e-02f, -5.584253498e-03f, +1.015030141e-03f, +2.575087939e-03f, +1.531668339e-03f, - /* 24, 0 */ -5.620806651e-04f, +1.786951327e-03f, +6.445247430e-03f, +8.135220753e-03f, -1.055728075e-03f, -2.182587186e-02f, -3.862210468e-02f, -2.392616717e-02f, +4.121375257e-02f, +1.449837419e-01f, +2.427850309e-01f, +2.829807027e-01f, +2.427850309e-01f, +1.449837419e-01f, +4.121375257e-02f, -2.392616717e-02f, -3.862210468e-02f, -2.182587186e-02f, -1.055728075e-03f, +8.135220753e-03f, +6.445247430e-03f, +1.786951327e-03f, -5.620806651e-04f, -5.120724112e-04f, - /* 24, 1 */ -6.104492932e-04f, +1.548760636e-03f, +6.159105160e-03f, +8.277197332e-03f, -7.779733613e-05f, -2.039475337e-02f, -3.819819741e-02f, -2.624621678e-02f, +3.569722776e-02f, +1.380982730e-01f, +2.379020609e-01f, +2.828154582e-01f, +2.474326717e-01f, +1.518487179e-01f, +4.689321837e-02f, -2.139810919e-02f, -3.892035913e-02f, -2.324619800e-02f, -2.084809535e-03f, +7.948411519e-03f, +6.721048149e-03f, +2.036121529e-03f, -5.037148469e-04f, -5.469834647e-04f, - /* 24, 2 */ -6.493280747e-04f, +1.322056610e-03f, +5.864617557e-03f, +8.376206823e-03f, +8.476165560e-04f, -1.895882060e-02f, -3.765599422e-02f, -2.836041007e-02f, +3.035103267e-02f, +1.312062799e-01f, +2.327950895e-01f, +2.823201223e-01f, +2.518341522e-01f, +1.586790922e-01f, +5.272765217e-02f, -1.866041870e-02f, -3.908572584e-02f, -2.464952038e-02f, -3.163389088e-03f, +7.715013258e-03f, +6.984447000e-03f, +2.295668536e-03f, -4.348733531e-04f, -5.801620624e-04f, - /* 24, 3 */ -6.792484933e-04f, +1.107253309e-03f, +5.563710253e-03f, +8.434210700e-03f, +1.719427448e-03f, -1.752380524e-02f, -3.700294628e-02f, -3.027140813e-02f, +2.518195985e-02f, +1.243215533e-01f, +2.274759065e-01f, +2.814958870e-01f, +2.559791715e-01f, +1.654606562e-01f, +5.870851072e-02f, -1.571201688e-02f, -3.911111998e-02f, -2.602940857e-02f, -4.289522020e-03f, +7.433392157e-03f, +7.233326681e-03f, +2.564892035e-03f, -3.551113499e-04f, -6.111213026e-04f, - /* 24, 4 */ -7.007615573e-04f, +9.046745282e-04f, +5.258232435e-03f, +8.453253159e-03f, +2.536821717e-03f, -1.609518093e-02f, -3.624657153e-02f, -3.198236083e-02f, +2.019619593e-02f, +1.174576676e-01f, +2.219567270e-01f, +2.803447347e-01f, +2.598579915e-01f, +1.721791413e-01f, +6.482669661e-02f, -1.255238605e-02f, -3.898963496e-02f, -2.737922870e-02f, -5.460966964e-03f, +7.102050305e-03f, +7.465520628e-03f, +2.842992490e-03f, -2.640225027e-04f, -6.393525967e-04f, - /* 24, 5 */ -7.144333056e-04f, +7.145569834e-04f, +4.949951622e-03f, +8.435448103e-03f, +3.299249997e-03f, -1.467815287e-02f, -3.539442781e-02f, -3.349688539e-02f, +1.539931407e-02f, +1.106279448e-01f, +2.162501543e-01f, +2.788694321e-01f, +2.634614666e-01f, +1.788202595e-01f, +7.107257652e-02f, -9.181583996e-03f, -3.871457066e-02f, -2.869216031e-02f, -6.675182397e-03f, +6.719638981e-03f, +7.678821339e-03f, +3.129069982e-03f, -1.612438490e-04f, -6.643278432e-04f, - /* 24, 6 */ -7.208404573e-04f, +5.370537968e-04f, +4.640549073e-03f, +8.382966356e-03f, +4.006418111e-03f, -1.327764882e-02f, -3.445408633e-02f, -3.481904366e-02f, +1.079626845e-02f, +1.038454185e-01f, +2.103691410e-01f, +2.770735210e-01f, +2.667810728e-01f, +1.853697450e-01f, +7.743600141e-02f, -5.600256400e-03f, -3.827946167e-02f, -2.996121443e-02f, -7.929324241e-03f, +6.284971816e-03f, +7.870989279e-03f, +3.422123547e-03f, -4.646067064e-05f, -6.855018549e-04f, - /* 24, 7 */ -7.205662235e-04f, +3.722382714e-04f, +4.331615834e-03f, +8.298023138e-03f, +4.658277300e-03f, -1.189831143e-02f, -3.343310598e-02f, -3.595331849e-02f, +6.391391026e-03f, +9.712280024e-02f, +2.043269499e-01f, +2.749613076e-01f, +2.698089343e-01f, +1.918133956e-01f, +8.390632879e-02f, -1.809647645e-03f, -3.767810524e-02f, -3.117925279e-02f, -9.220244622e-03f, +5.797037759e-03f, +8.039762345e-03f, +3.721051017e-03f, +8.058866307e-05f, -7.023150347e-04f, - /* 24, 8 */ -7.141962964e-04f, +2.201079121e-04f, +4.024649403e-03f, +8.182865872e-03f, +5.255013788e-03f, -1.054449181e-02f, -3.233900821e-02f, -3.690458903e-02f, +2.188390323e-03f, +9.047244679e-02f, +1.981371140e-01f, +2.725378483e-01f, +2.725378483e-01f, +1.981371140e-01f, +9.047244679e-02f, +2.188390323e-03f, -3.690458903e-02f, -3.233900821e-02f, -1.054449181e-02f, +5.255013788e-03f, +8.182865872e-03f, +4.024649403e-03f, +2.201079121e-04f, -7.141962964e-04f, - /* 24, 9 */ -7.023150347e-04f, +8.058866307e-05f, +3.721051017e-03f, +8.039762345e-03f, +5.797037759e-03f, -9.220244622e-03f, -3.117925279e-02f, -3.767810524e-02f, -1.809647645e-03f, +8.390632879e-02f, +1.918133956e-01f, +2.698089343e-01f, +2.749613076e-01f, +2.043269499e-01f, +9.712280024e-02f, +6.391391026e-03f, -3.595331849e-02f, -3.343310598e-02f, -1.189831143e-02f, +4.658277300e-03f, +8.298023138e-03f, +4.331615834e-03f, +3.722382714e-04f, -7.205662235e-04f, - /* 24,10 */ -6.855018549e-04f, -4.646067064e-05f, +3.422123547e-03f, +7.870989279e-03f, +6.284971816e-03f, -7.929324241e-03f, -2.996121443e-02f, -3.827946167e-02f, -5.600256400e-03f, +7.743600141e-02f, +1.853697450e-01f, +2.667810728e-01f, +2.770735210e-01f, +2.103691410e-01f, +1.038454185e-01f, +1.079626845e-02f, -3.481904366e-02f, -3.445408633e-02f, -1.327764882e-02f, +4.006418111e-03f, +8.382966356e-03f, +4.640549073e-03f, +5.370537968e-04f, -7.208404573e-04f, - /* 24,11 */ -6.643278432e-04f, -1.612438490e-04f, +3.129069982e-03f, +7.678821339e-03f, +6.719638981e-03f, -6.675182397e-03f, -2.869216031e-02f, -3.871457066e-02f, -9.181583996e-03f, +7.107257652e-02f, +1.788202595e-01f, +2.634614666e-01f, +2.788694321e-01f, +2.162501543e-01f, +1.106279448e-01f, +1.539931407e-02f, -3.349688539e-02f, -3.539442781e-02f, -1.467815287e-02f, +3.299249997e-03f, +8.435448103e-03f, +4.949951622e-03f, +7.145569834e-04f, -7.144333056e-04f, - /* 24,12 */ -6.393525967e-04f, -2.640225027e-04f, +2.842992490e-03f, +7.465520628e-03f, +7.102050305e-03f, -5.460966964e-03f, -2.737922870e-02f, -3.898963496e-02f, -1.255238605e-02f, +6.482669661e-02f, +1.721791413e-01f, +2.598579915e-01f, +2.803447347e-01f, +2.219567270e-01f, +1.174576676e-01f, +2.019619593e-02f, -3.198236083e-02f, -3.624657153e-02f, -1.609518093e-02f, +2.536821717e-03f, +8.453253159e-03f, +5.258232435e-03f, +9.046745282e-04f, -7.007615573e-04f, - /* 24,13 */ -6.111213026e-04f, -3.551113499e-04f, +2.564892035e-03f, +7.233326681e-03f, +7.433392157e-03f, -4.289522020e-03f, -2.602940857e-02f, -3.911111998e-02f, -1.571201688e-02f, +5.870851072e-02f, +1.654606562e-01f, +2.559791715e-01f, +2.814958870e-01f, +2.274759065e-01f, +1.243215533e-01f, +2.518195985e-02f, -3.027140813e-02f, -3.700294628e-02f, -1.752380524e-02f, +1.719427448e-03f, +8.434210700e-03f, +5.563710253e-03f, +1.107253309e-03f, -6.792484933e-04f, - /* 24,14 */ -5.801620624e-04f, -4.348733531e-04f, +2.295668536e-03f, +6.984447000e-03f, +7.715013258e-03f, -3.163389088e-03f, -2.464952038e-02f, -3.908572584e-02f, -1.866041870e-02f, +5.272765217e-02f, +1.586790922e-01f, +2.518341522e-01f, +2.823201223e-01f, +2.327950895e-01f, +1.312062799e-01f, +3.035103267e-02f, -2.836041007e-02f, -3.765599422e-02f, -1.895882060e-02f, +8.476165560e-04f, +8.376206823e-03f, +5.864617557e-03f, +1.322056610e-03f, -6.493280747e-04f, - /* 24,15 */ -5.469834647e-04f, -5.037148469e-04f, +2.036121529e-03f, +6.721048149e-03f, +7.948411519e-03f, -2.084809535e-03f, -2.324619800e-02f, -3.892035913e-02f, -2.139810919e-02f, +4.689321837e-02f, +1.518487179e-01f, +2.474326717e-01f, +2.828154582e-01f, +2.379020609e-01f, +1.380982730e-01f, +3.569722776e-02f, -2.624621678e-02f, -3.819819741e-02f, -2.039475337e-02f, -7.779733613e-05f, +8.277197332e-03f, +6.159105160e-03f, +1.548760636e-03f, -6.104492932e-04f, - /* 24, 0 */ -1.197013499e-03f, -3.320493122e-03f, -1.144245270e-03f, +8.577337679e-03f, +1.627759141e-02f, +3.152522439e-03f, -3.255249254e-02f, -5.362651723e-02f, -5.304244056e-03f, +1.253006387e-01f, +2.738089134e-01f, +3.395768433e-01f, +2.738089134e-01f, +1.253006387e-01f, -5.304244056e-03f, -5.362651723e-02f, -3.255249254e-02f, +3.152522439e-03f, +1.627759141e-02f, +8.577337679e-03f, -1.144245270e-03f, -3.320493122e-03f, -1.197013499e-03f, +1.261205705e-04f, - /* 24, 1 */ -1.060573898e-03f, -3.246029352e-03f, -1.514205592e-03f, +7.849505167e-03f, +1.622707505e-02f, +4.797556391e-03f, -3.017446993e-02f, -5.385088872e-02f, -1.098434059e-02f, +1.155960124e-01f, +2.659858985e-01f, +3.393017042e-01f, +2.812897341e-01f, +1.350895441e-01f, +7.263382766e-04f, -5.311639759e-02f, -3.488122370e-02f, +1.404407443e-03f, +1.624118609e-02f, +9.301572693e-03f, -7.399572733e-04f, -3.377916464e-03f, -1.338504197e-03f, +9.901282259e-05f, - /* 24, 2 */ -9.298712414e-04f, -3.156277727e-03f, -1.849729696e-03f, +7.122444811e-03f, +1.609438844e-02f, +6.335978542e-03f, -2.776122262e-02f, -5.380213751e-02f, -1.630850202e-02f, +1.060004951e-01f, +2.578448120e-01f, +3.384771755e-01f, +2.884051592e-01f, +1.449371908e-01f, +7.100538384e-03f, -5.230860749e-02f, -3.714619841e-02f, -4.425295608e-04f, +1.611336946e-02f, +1.001762126e-02f, -3.016869975e-04f, -3.416553196e-03f, -1.484263468e-03f, +6.526428163e-05f, - /* 24, 3 */ -8.054954021e-04f, -3.052984548e-03f, -2.150934111e-03f, +6.400291527e-03f, +1.588450664e-02f, +7.764974256e-03f, -2.532636892e-02f, -5.349351937e-02f, -2.127269005e-02f, +9.653812261e-02f, +2.494105998e-01f, +3.371059190e-01f, +2.951330020e-01f, +1.548174220e-01f, +1.381006797e-02f, -5.119199897e-02f, -3.933260713e-02f, -2.383292518e-03f, +1.588995194e-02f, +1.072069264e-02f, +1.699725421e-04f, -3.434676821e-03f, -1.633412152e-03f, +2.453170435e-05f, - /* 24, 4 */ -6.879416803e-04f, -2.937877889e-03f, -2.418147761e-03f, +5.686932142e-03f, +1.560259046e-02f, +9.082429619e-03f, -2.288303213e-02f, -5.293884648e-02f, -2.587426360e-02f, +8.723205545e-02f, +2.407089312e-01f, +3.351923590e-01f, +3.014521813e-01f, +1.647035561e-01f, +2.084522321e-02f, -4.975626781e-02f, -4.142535273e-02f, -4.412143180e-03f, +1.556708209e-02f, +1.140581394e-02f, +6.741710759e-04f, -3.430594409e-03f, -1.784975276e-03f, -2.348660763e-05f, - /* 24, 5 */ -5.776128643e-04f, -2.812656385e-03f, -2.651898517e-03f, +4.985994150e-03f, +1.525394912e-02f, +1.028691298e-02f, -2.044379769e-02f, -5.215241275e-02f, -3.011195925e-02f, +7.810450253e-02f, +2.317660961e-01f, +3.327426635e-01f, +3.073428069e-01f, +1.745684830e-01f, +2.819489579e-02f, -4.799202051e-02f, -4.340911052e-02f, -6.522599631e-03f, +1.514128438e-02f, +1.206785167e-02f, +1.209792693e-03f, -3.402660968e-03f, -1.937883690e-03f, -7.904503123e-05f, - /* 24, 6 */ -4.748218959e-04f, -2.678978820e-03f, -2.852899102e-03f, +4.300836533e-03f, +1.484400357e-02f, +1.137765361e-02f, -1.802067434e-02f, -5.114891871e-02f, -3.398586582e-02f, +6.917664952e-02f, +2.226089010e-01f, +3.297647184e-01f, +3.127862620e-01f, +1.843847637e-01f, +3.584659036e-02f, -4.589083871e-02f, -4.526839113e-02f, -8.707438641e-03f, +1.460949637e-02f, +1.270153536e-02f, +1.775448814e-03f, -3.349294247e-03f, -2.090976552e-03f, -1.423449116e-04f, - /* 24, 7 */ -3.797950945e-04f, -2.538454547e-03f, -3.022032460e-03f, +3.634542645e-03f, +1.437825069e-02f, +1.235451773e-02f, -1.562505912e-02f, -4.994339647e-02f, -3.749739364e-02f, +6.046859273e-02f, +2.132645624e-01f, +3.262680950e-01f, +3.177652787e-01f, +1.941247325e-01f, +4.378644843e-02f, -4.344534054e-02f, -4.698760595e-02f, -1.095870195e-02f, +1.396910503e-02f, +1.330148306e-02f, +2.369472628e-03f, -3.268989872e-03f, -2.243004675e-03f, -2.135289700e-04f, - /* 24, 8 */ -2.926758839e-04f, -2.392634763e-03f, -3.160336722e-03f, +2.989915102e-03f, +1.386222860e-02f, +1.321798203e-02f, -1.326770657e-02f, -4.855113496e-02f, -4.064923848e-02f, +5.199927852e-02f, +2.037606007e-01f, +3.222640100e-01f, +3.222640100e-01f, +2.037606007e-01f, +5.199927852e-02f, -4.064923848e-02f, -4.855113496e-02f, -1.326770657e-02f, +1.321798203e-02f, +1.386222860e-02f, +2.989915102e-03f, -3.160336722e-03f, -2.392634763e-03f, -2.926758839e-04f, - /* 24, 9 */ -2.135289700e-04f, -2.243004675e-03f, -3.268989872e-03f, +2.369472628e-03f, +1.330148306e-02f, +1.396910503e-02f, -1.095870195e-02f, -4.698760595e-02f, -4.344534054e-02f, +4.378644843e-02f, +1.941247325e-01f, +3.177652787e-01f, +3.262680950e-01f, +2.132645624e-01f, +6.046859273e-02f, -3.749739364e-02f, -4.994339647e-02f, -1.562505912e-02f, +1.235451773e-02f, +1.437825069e-02f, +3.634542645e-03f, -3.022032460e-03f, -2.538454547e-03f, -3.797950945e-04f, - /* 24,10 */ -1.423449116e-04f, -2.090976552e-03f, -3.349294247e-03f, +1.775448814e-03f, +1.270153536e-02f, +1.460949637e-02f, -8.707438641e-03f, -4.526839113e-02f, -4.589083871e-02f, +3.584659036e-02f, +1.843847637e-01f, +3.127862620e-01f, +3.297647184e-01f, +2.226089010e-01f, +6.917664952e-02f, -3.398586582e-02f, -5.114891871e-02f, -1.802067434e-02f, +1.137765361e-02f, +1.484400357e-02f, +4.300836533e-03f, -2.852899102e-03f, -2.678978820e-03f, -4.748218959e-04f, - /* 24,11 */ -7.904503123e-05f, -1.937883690e-03f, -3.402660968e-03f, +1.209792693e-03f, +1.206785167e-02f, +1.514128438e-02f, -6.522599631e-03f, -4.340911052e-02f, -4.799202051e-02f, +2.819489579e-02f, +1.745684830e-01f, +3.073428069e-01f, +3.327426635e-01f, +2.317660961e-01f, +7.810450253e-02f, -3.011195925e-02f, -5.215241275e-02f, -2.044379769e-02f, +1.028691298e-02f, +1.525394912e-02f, +4.985994150e-03f, -2.651898517e-03f, -2.812656385e-03f, -5.776128643e-04f, - /* 24,12 */ -2.348660763e-05f, -1.784975276e-03f, -3.430594409e-03f, +6.741710759e-04f, +1.140581394e-02f, +1.556708209e-02f, -4.412143180e-03f, -4.142535273e-02f, -4.975626781e-02f, +2.084522321e-02f, +1.647035561e-01f, +3.014521813e-01f, +3.351923590e-01f, +2.407089312e-01f, +8.723205545e-02f, -2.587426360e-02f, -5.293884648e-02f, -2.288303213e-02f, +9.082429619e-03f, +1.560259046e-02f, +5.686932142e-03f, -2.418147761e-03f, -2.937877889e-03f, -6.879416803e-04f, - /* 24,13 */ +2.453170435e-05f, -1.633412152e-03f, -3.434676821e-03f, +1.699725421e-04f, +1.072069264e-02f, +1.588995194e-02f, -2.383292518e-03f, -3.933260713e-02f, -5.119199897e-02f, +1.381006797e-02f, +1.548174220e-01f, +2.951330020e-01f, +3.371059190e-01f, +2.494105998e-01f, +9.653812261e-02f, -2.127269005e-02f, -5.349351937e-02f, -2.532636892e-02f, +7.764974256e-03f, +1.588450664e-02f, +6.400291527e-03f, -2.150934111e-03f, -3.052984548e-03f, -8.054954021e-04f, - /* 24,14 */ +6.526428163e-05f, -1.484263468e-03f, -3.416553196e-03f, -3.016869975e-04f, +1.001762126e-02f, +1.611336946e-02f, -4.425295608e-04f, -3.714619841e-02f, -5.230860749e-02f, +7.100538384e-03f, +1.449371908e-01f, +2.884051592e-01f, +3.384771755e-01f, +2.578448120e-01f, +1.060004951e-01f, -1.630850202e-02f, -5.380213751e-02f, -2.776122262e-02f, +6.335978542e-03f, +1.609438844e-02f, +7.122444811e-03f, -1.849729696e-03f, -3.156277727e-03f, -9.298712414e-04f, - /* 24,15 */ +9.901282259e-05f, -1.338504197e-03f, -3.377916464e-03f, -7.399572733e-04f, +9.301572693e-03f, +1.624118609e-02f, +1.404407443e-03f, -3.488122370e-02f, -5.311639759e-02f, +7.263382766e-04f, +1.350895441e-01f, +2.812897341e-01f, +3.393017042e-01f, +2.659858985e-01f, +1.155960124e-01f, -1.098434059e-02f, -5.385088872e-02f, -3.017446993e-02f, +4.797556391e-03f, +1.622707505e-02f, +7.849505167e-03f, -1.514205592e-03f, -3.246029352e-03f, -1.060573898e-03f, - /* 20, 0 */ -4.161478318e-03f, -1.410215661e-03f, +1.216462436e-02f, +1.839753508e-02f, -1.019572218e-02f, -5.576407638e-02f, -3.857794503e-02f, +9.869941459e-02f, +2.903842315e-01f, +3.819037908e-01f, +2.903842315e-01f, +9.869941459e-02f, -3.857794503e-02f, -5.576407638e-02f, -1.019572218e-02f, +1.839753508e-02f, +1.216462436e-02f, -1.410215661e-03f, -4.161478318e-03f, -1.002136091e-03f, - /* 20, 1 */ -4.024812873e-03f, -1.935046598e-03f, +1.120868183e-02f, +1.884704309e-02f, -7.349314558e-03f, -5.377462232e-02f, -4.306909662e-02f, +8.713841011e-02f, +2.797272456e-01f, +3.815142140e-01f, +3.006231905e-01f, +1.105090175e-01f, -3.357053763e-02f, -5.750258783e-02f, -1.313424017e-02f, +1.779561475e-02f, +1.309754391e-02f, -8.338854378e-04f, -4.274968522e-03f, -1.184613130e-03f, - /* 20, 2 */ -3.867923854e-03f, -2.407896178e-03f, +1.023778220e-02f, +1.914947854e-02f, -4.608279480e-03f, -5.155911253e-02f, -4.704785126e-02f, +7.585987841e-02f, +2.686929243e-01f, +3.803470604e-01f, +3.104047352e-01f, +1.225315522e-01f, -2.804532708e-02f, -5.896552129e-02f, -1.615031726e-02f, +1.703673197e-02f, +1.399907244e-02f, -2.069933478e-04f, -4.362323551e-03f, -1.376799150e-03f, - /* 20, 3 */ -3.693729756e-03f, -2.828715617e-03f, +9.259646102e-03f, +1.931089692e-02f, -1.984565051e-03f, -4.914254142e-02f, -5.052030051e-02f, +6.489576800e-02f, +2.573230589e-01f, +3.784070525e-01f, +3.196909791e-01f, +1.347297046e-01f, -2.200314505e-02f, -6.012863981e-02f, -1.922814732e-02f, +1.611719780e-02f, +1.486058724e-02f, +4.690483654e-04f, -4.420601658e-03f, -1.577606548e-03f, - /* 20, 4 */ -3.505092707e-03f, -3.197865053e-03f, +8.281610454e-03f, +1.933799402e-02f, +5.112106557e-04f, -4.654987033e-02f, -5.349467921e-02f, +5.427598051e-02f, +2.456603330e-01f, +3.757020336e-01f, +3.284457292e-01f, +1.470646693e-01f, -1.544725782e-02f, -6.096825350e-02f, -2.235071271e-02f, +1.503425320e-02f, +1.567326271e-02f, +1.192336051e-03f, -4.446907145e-03f, -1.785766437e-03f, - /* 20, 5 */ -3.304797071e-03f, -3.516087186e-03f, +7.310594668e-03f, +1.923802927e-02f, +2.869766685e-03f, -4.380589142e-02f, -5.598126618e-02f, +4.402826232e-02f, +2.337481127e-01f, +3.722429273e-01f, +3.366346688e-01f, +1.594963158e-01f, -8.383409345e-03f, -6.146136934e-02f, -2.549983702e-02f, +1.378613431e-02f, +1.642812632e-02f, +1.960461229e-03f, -4.438419451e-03f, -1.999828319e-03f, - /* 20, 6 */ -3.095529963e-03f, -3.784479230e-03f, +6.353071566e-03f, +1.901874863e-02f, +5.083157654e-03f, -4.093509653e-02f, -5.799227582e-02f, +3.417810958e-02f, +2.216302330e-01f, +3.680436800e-01f, +3.442255330e-01f, +1.719833640e-01f, -8.198514564e-04f, -6.158584092e-02f, -2.865624686e-02f, +1.237213363e-02f, +1.711611824e-02f, +2.770500592e-03f, -4.392423280e-03f, -2.218161540e-03f, - /* 20, 7 */ -2.879863731e-03f, -4.004463491e-03f, +5.415043050e-03f, +1.868830751e-02f, +7.144763866e-03f, -3.796155187e-02f, -5.954174144e-02f, +2.474868675e-02f, +2.093507858e-01f, +3.631211887e-01f, +3.511882735e-01f, +1.844835679e-01f, +7.232639407e-03f, -6.132051750e-02f, -3.179964276e-02f, +1.079265669e-02f, +1.772815465e-02f, +3.619009684e-03f, -4.306339536e-03f, -2.438958613e-03f, - /* 20, 8 */ -2.660240473e-03f, -4.177756859e-03f, +4.502020476e-03f, +1.825519424e-02f, +9.049273418e-03f, -3.490877898e-02f, -6.064539119e-02f, +1.576075912e-02f, +1.969539074e-01f, +3.574952133e-01f, +3.574952133e-01f, +1.969539074e-01f, +1.576075912e-02f, -6.064539119e-02f, -3.490877898e-02f, +9.049273418e-03f, +1.825519424e-02f, +4.502020476e-03f, -4.177756859e-03f, -2.660240473e-03f, - /* 20, 9 */ -2.438958613e-03f, -4.306339536e-03f, +3.619009684e-03f, +1.772815465e-02f, +1.079265669e-02f, -3.179964276e-02f, -6.132051750e-02f, +7.232639407e-03f, +1.844835679e-01f, +3.511882735e-01f, +3.631211887e-01f, +2.093507858e-01f, +2.474868675e-02f, -5.954174144e-02f, -3.796155187e-02f, +7.144763866e-03f, +1.868830751e-02f, +5.415043050e-03f, -4.004463491e-03f, -2.879863731e-03f, - /* 20,10 */ -2.218161540e-03f, -4.392423280e-03f, +2.770500592e-03f, +1.711611824e-02f, +1.237213363e-02f, -2.865624686e-02f, -6.158584092e-02f, -8.198514564e-04f, +1.719833640e-01f, +3.442255330e-01f, +3.680436800e-01f, +2.216302330e-01f, +3.417810958e-02f, -5.799227582e-02f, -4.093509653e-02f, +5.083157654e-03f, +1.901874863e-02f, +6.353071566e-03f, -3.784479230e-03f, -3.095529963e-03f, - /* 20,11 */ -1.999828319e-03f, -4.438419451e-03f, +1.960461229e-03f, +1.642812632e-02f, +1.378613431e-02f, -2.549983702e-02f, -6.146136934e-02f, -8.383409345e-03f, +1.594963158e-01f, +3.366346688e-01f, +3.722429273e-01f, +2.337481127e-01f, +4.402826232e-02f, -5.598126618e-02f, -4.380589142e-02f, +2.869766685e-03f, +1.923802927e-02f, +7.310594668e-03f, -3.516087186e-03f, -3.304797071e-03f, - /* 20,12 */ -1.785766437e-03f, -4.446907145e-03f, +1.192336051e-03f, +1.567326271e-02f, +1.503425320e-02f, -2.235071271e-02f, -6.096825350e-02f, -1.544725782e-02f, +1.470646693e-01f, +3.284457292e-01f, +3.757020336e-01f, +2.456603330e-01f, +5.427598051e-02f, -5.349467921e-02f, -4.654987033e-02f, +5.112106557e-04f, +1.933799402e-02f, +8.281610454e-03f, -3.197865053e-03f, -3.505092707e-03f, - /* 20,13 */ -1.577606548e-03f, -4.420601658e-03f, +4.690483654e-04f, +1.486058724e-02f, +1.611719780e-02f, -1.922814732e-02f, -6.012863981e-02f, -2.200314505e-02f, +1.347297046e-01f, +3.196909791e-01f, +3.784070525e-01f, +2.573230589e-01f, +6.489576800e-02f, -5.052030051e-02f, -4.914254142e-02f, -1.984565051e-03f, +1.931089692e-02f, +9.259646102e-03f, -2.828715617e-03f, -3.693729756e-03f, - /* 20,14 */ -1.376799150e-03f, -4.362323551e-03f, -2.069933478e-04f, +1.399907244e-02f, +1.703673197e-02f, -1.615031726e-02f, -5.896552129e-02f, -2.804532708e-02f, +1.225315522e-01f, +3.104047352e-01f, +3.803470604e-01f, +2.686929243e-01f, +7.585987841e-02f, -4.704785126e-02f, -5.155911253e-02f, -4.608279480e-03f, +1.914947854e-02f, +1.023778220e-02f, -2.407896178e-03f, -3.867923854e-03f, - /* 20,15 */ -1.184613130e-03f, -4.274968522e-03f, -8.338854378e-04f, +1.309754391e-02f, +1.779561475e-02f, -1.313424017e-02f, -5.750258783e-02f, -3.357053763e-02f, +1.105090175e-01f, +3.006231905e-01f, +3.815142140e-01f, +2.797272456e-01f, +8.713841011e-02f, -4.306909662e-02f, -5.377462232e-02f, -7.349314558e-03f, +1.884704309e-02f, +1.120868183e-02f, -1.935046598e-03f, -4.024812873e-03f, - /* 20, 0 */ -1.329352252e-03f, -4.865562069e-03f, +1.662947600e-03f, +1.893743982e-02f, +1.052975469e-02f, -4.314924294e-02f, -6.168215525e-02f, +6.793829558e-02f, +3.007295231e-01f, +4.214013440e-01f, +3.007295231e-01f, +6.793829558e-02f, -6.168215525e-02f, -4.314924294e-02f, +1.052975469e-02f, +1.893743982e-02f, +1.662947600e-03f, -4.865562069e-03f, -1.329352252e-03f, +0.000000000e+00f, - /* 20, 1 */ -1.106503038e-03f, -4.784011640e-03f, +7.620481209e-04f, +1.810159639e-02f, +1.258770510e-02f, -3.946126222e-02f, -6.412166469e-02f, +5.516844650e-02f, +2.870012762e-01f, +4.208780002e-01f, +3.139874692e-01f, +8.118253044e-02f, -5.860314053e-02f, -4.671882663e-02f, +8.254179692e-03f, +1.967037055e-02f, +2.622520317e-03f, -4.905078775e-03f, -1.565095816e-03f, +0.000000000e+00f, - /* 20, 2 */ -8.979094201e-04f, -4.664743082e-03f, -7.633034189e-05f, +1.717630323e-02f, +1.442449893e-02f, -3.568911340e-02f, -6.594250862e-02f, +4.291292121e-02f, +2.728656387e-01f, +4.193105477e-01f, +3.267137817e-01f, +9.485763802e-02f, -5.486676259e-02f, -5.013477905e-02f, +5.766539049e-03f, +2.028700325e-02f, +3.636071544e-03f, -4.898357639e-03f, -1.812078842e-03f, +3.513221827e-04f, - /* 20, 3 */ -7.046452899e-04f, -4.512131585e-03f, -8.491713087e-04f, +1.617498084e-02f, +1.603855621e-02f, -3.186582692e-02f, -6.716828458e-02f, +3.120792005e-02f, +2.583867198e-01f, +4.167067067e-01f, +3.388490774e-01f, +1.089167196e-01f, -5.045835457e-02f, -5.336106841e-02f, +3.074480429e-03f, +2.077415592e-02f, +4.698051453e-03f, -4.841360048e-03f, -2.068349661e-03f, +3.288882594e-04f, - /* 20, 4 */ -5.275063595e-04f, -4.330562617e-03f, -1.554266965e-03f, +1.511089362e-02f, +1.743016199e-02f, -2.802305698e-02f, -6.782511100e-02f, +2.008577030e-02f, +2.436294448e-01f, +4.130792899e-01f, +3.503362849e-01f, +1.233097059e-01f, -4.536663323e-02f, -5.636108634e-02f, +1.877878266e-04f, +2.111898038e-02f, +5.802054958e-03f, -4.730268616e-03f, -2.331660076e-03f, +2.941732022e-04f, - /* 20, 5 */ -3.670219514e-04f, -4.124385552e-03f, -2.190189120e-03f, +1.399704337e-02f, +1.860136846e-02f, -2.419092016e-02f, -6.794137953e-02f, +9.574818877e-03f, +2.286591711e-01f, +4.084461208e-01f, +3.611209950e-01f, +1.379835966e-01f, -3.958387309e-02f, -5.909788086e-02f, -2.881585959e-03f, +2.130909659e-02f, +6.940829951e-03f, -4.561544087e-03f, -2.599469210e-03f, +2.461206998e-04f, - /* 20, 6 */ -2.234691091e-04f, -3.897870552e-03f, -2.756254356e-03f, +1.284607053e-02f, +1.955588746e-02f, -2.039785121e-02f, -6.754749865e-02f, -3.006469464e-04f, +2.135413047e-01f, +4.028299211e-01f, +3.711517965e-01f, +1.528827232e-01f, -3.310606021e-02f, -6.153439984e-02f, -6.119502817e-03f, +2.133272965e-02f, +8.106294302e-03f, -4.331982887e-03f, -2.868951124e-03f, +1.837671888e-04f, - /* 20, 7 */ -9.688872179e-05f, -3.655168976e-03f, -3.252484018e-03f, +1.167016373e-02f, +2.029897446e-02f, -1.667047641e-02f, -6.667563061e-02f, -9.520450398e-03f, +1.983409219e-01f, +3.962581664e-01f, +3.803805952e-01f, +1.679490334e-01f, -2.593302438e-02f, -6.363374348e-02f, -9.509639499e-03f, +2.117884859e-02f, +9.289561904e-03f, -4.038774728e-03f, -3.137006363e-03f, +1.062635081e-04f, - /* 20, 8 */ +1.289664191e-05f, -3.400277535e-03f, -3.679559671e-03f, +1.048097797e-02f, +2.083730557e-02f, -1.303350512e-02f, -6.535942396e-02f, -1.806854797e-02f, +1.831223958e-01f, +3.887629129e-01f, +3.887629129e-01f, +1.831223958e-01f, -1.806854797e-02f, -6.535942396e-02f, -1.303350512e-02f, +2.083730557e-02f, +1.048097797e-02f, -3.679559671e-03f, -3.400277535e-03f, +1.289664191e-05f, - /* 20, 9 */ +1.062635081e-04f, -3.137006363e-03f, -4.038774728e-03f, +9.289561904e-03f, +2.117884859e-02f, -9.509639499e-03f, -6.363374348e-02f, -2.593302438e-02f, +1.679490334e-01f, +3.803805952e-01f, +3.962581664e-01f, +1.983409219e-01f, -9.520450398e-03f, -6.667563061e-02f, -1.667047641e-02f, +2.029897446e-02f, +1.167016373e-02f, -3.252484018e-03f, -3.655168976e-03f, -9.688872179e-05f, - /* 20,10 */ +1.837671888e-04f, -2.868951124e-03f, -4.331982887e-03f, +8.106294302e-03f, +2.133272965e-02f, -6.119502817e-03f, -6.153439984e-02f, -3.310606021e-02f, +1.528827232e-01f, +3.711517965e-01f, +4.028299211e-01f, +2.135413047e-01f, -3.006469464e-04f, -6.754749865e-02f, -2.039785121e-02f, +1.955588746e-02f, +1.284607053e-02f, -2.756254356e-03f, -3.897870552e-03f, -2.234691091e-04f, - /* 20,11 */ +2.461206998e-04f, -2.599469210e-03f, -4.561544087e-03f, +6.940829951e-03f, +2.130909659e-02f, -2.881585959e-03f, -5.909788086e-02f, -3.958387309e-02f, +1.379835966e-01f, +3.611209950e-01f, +4.084461208e-01f, +2.286591711e-01f, +9.574818877e-03f, -6.794137953e-02f, -2.419092016e-02f, +1.860136846e-02f, +1.399704337e-02f, -2.190189120e-03f, -4.124385552e-03f, -3.670219514e-04f, - /* 20,12 */ +2.941732022e-04f, -2.331660076e-03f, -4.730268616e-03f, +5.802054958e-03f, +2.111898038e-02f, +1.877878266e-04f, -5.636108634e-02f, -4.536663323e-02f, +1.233097059e-01f, +3.503362849e-01f, +4.130792899e-01f, +2.436294448e-01f, +2.008577030e-02f, -6.782511100e-02f, -2.802305698e-02f, +1.743016199e-02f, +1.511089362e-02f, -1.554266965e-03f, -4.330562617e-03f, -5.275063595e-04f, - /* 20,13 */ +3.288882594e-04f, -2.068349661e-03f, -4.841360048e-03f, +4.698051453e-03f, +2.077415592e-02f, +3.074480429e-03f, -5.336106841e-02f, -5.045835457e-02f, +1.089167196e-01f, +3.388490774e-01f, +4.167067067e-01f, +2.583867198e-01f, +3.120792005e-02f, -6.716828458e-02f, -3.186582692e-02f, +1.603855621e-02f, +1.617498084e-02f, -8.491713087e-04f, -4.512131585e-03f, -7.046452899e-04f, - /* 20,14 */ +3.513221827e-04f, -1.812078842e-03f, -4.898357639e-03f, +3.636071544e-03f, +2.028700325e-02f, +5.766539049e-03f, -5.013477905e-02f, -5.486676259e-02f, +9.485763802e-02f, +3.267137817e-01f, +4.193105477e-01f, +2.728656387e-01f, +4.291292121e-02f, -6.594250862e-02f, -3.568911340e-02f, +1.442449893e-02f, +1.717630323e-02f, -7.633034189e-05f, -4.664743082e-03f, -8.979094201e-04f, - /* 20,15 */ +0.000000000e+00f, -1.565095816e-03f, -4.905078775e-03f, +2.622520317e-03f, +1.967037055e-02f, +8.254179692e-03f, -4.671882663e-02f, -5.860314053e-02f, +8.118253044e-02f, +3.139874692e-01f, +4.208780002e-01f, +2.870012762e-01f, +5.516844650e-02f, -6.412166469e-02f, -3.946126222e-02f, +1.258770510e-02f, +1.810159639e-02f, +7.620481209e-04f, -4.784011640e-03f, -1.106503038e-03f, - /* 20, 0 */ +3.735125865e-04f, -2.550984103e-03f, -4.871486096e-03f, +1.016287769e-02f, +2.252246682e-02f, -2.231523982e-02f, -7.431762424e-02f, +3.414137659e-02f, +3.062278786e-01f, +4.608988972e-01f, +3.062278786e-01f, +3.414137659e-02f, -7.431762424e-02f, -2.231523982e-02f, +2.252246682e-02f, +1.016287769e-02f, -4.871486096e-03f, -2.550984103e-03f, +3.735125865e-04f, +0.000000000e+00f, - /* 20, 1 */ +3.929324583e-04f, -2.236335973e-03f, -5.106050653e-03f, +8.748210493e-03f, +2.303691111e-02f, -1.786093260e-02f, -7.411924916e-02f, +2.086992015e-02f, +2.890848421e-01f, +4.602142272e-01f, +3.228796668e-01f, +4.817530421e-02f, -7.382833631e-02f, -2.685541297e-02f, +2.174514756e-02f, +1.158816420e-02f, -4.555417854e-03f, -2.871502680e-03f, +3.387491377e-04f, +0.000000000e+00f, - /* 20, 2 */ +0.000000000e+00f, -1.931175707e-03f, -5.263447672e-03f, +7.357928950e-03f, +2.330080531e-02f, -1.352771902e-02f, -7.327813327e-02f, +8.401230311e-03f, +2.715429904e-01f, +4.581642525e-01f, +3.389493512e-01f, +6.292517925e-02f, -7.260941515e-02f, -3.144322519e-02f, +2.069454672e-02f, +1.300917115e-02f, -4.154170312e-03f, -3.193828376e-03f, +2.870815261e-04f, +0.000000000e+00f, - /* 20, 3 */ +0.000000000e+00f, -1.638662038e-03f, -5.348575554e-03f, +6.004591146e-03f, +2.332816092e-02f, -9.347674729e-03f, -7.184169597e-02f, -3.230759097e-03f, +2.536956771e-01f, +4.547610488e-01f, +3.543483057e-01f, +7.833843581e-02f, -7.062228683e-02f, -3.603763775e-02f, +1.936240016e-02f, +1.440996064e-02f, -3.664825055e-03f, -3.513472060e-03f, +2.170808154e-04f, +0.000000000e+00f, - /* 20, 4 */ +0.000000000e+00f, -1.361489293e-03f, -5.366796329e-03f, +4.699488665e-03f, +2.313444000e-02f, -5.349604768e-03f, -6.985939290e-02f, -1.399855627e-02f, +2.356365195e-01f, +4.500246402e-01f, +3.689907742e-01f, +9.435670405e-02f, -6.783220328e-02f, -4.059502103e-02f, +1.774280068e-02f, +1.577366666e-02f, -3.085314600e-03f, -3.825546457e-03f, +1.274866066e-04f, +0.000000000e+00f, - /* 20, 5 */ +0.000000000e+00f, -1.101888322e-03f, -5.323837897e-03f, +3.452605627e-03f, +2.273631696e-02f, -1.558959414e-03f, -6.738225311e-02f, -2.388113922e-02f, +2.174587480e-01f, +4.439828472e-01f, +3.827944964e-01f, +1.109160995e-01f, -6.420865295e-02f, -4.506940842e-02f, +1.583239979e-02f, +1.708262332e-02f, -2.414511840e-03f, -4.124801502e-03f, +1.724424543e-05f, +0.000000000e+00f, - /* 20, 6 */ +0.000000000e+00f, -8.616336525e-04f, -5.225698259e-03f, +2.272594520e-03f, +2.215144089e-02f, +2.002216238e-03f, -6.446241843e-02f, -3.286393171e-02f, +1.992545658e-01f, +4.366710759e-01f, +3.956813102e-01f, +1.279475618e-01f, -5.972574809e-02f, -4.941278189e-02f, +1.363059437e-02f, +1.831850983e-02f, -1.652313816e-03f, -4.405667401e-03f, -1.144582079e-04f, +0.000000000e+00f, - /* 20, 7 */ +0.000000000e+00f, -6.420563626e-04f, -5.078552799e-03f, +1.166768183e-03f, +2.139820068e-02f, +5.315299677e-03f, -6.115268907e-02f, -4.093872873e-02f, +1.811145256e-01f, +4.281320500e-01f, +4.075777294e-01f, +1.453772407e-01f, -5.436258502e-02f, -5.357538755e-02f, +1.113969540e-02f, +1.946251142e-02f, -7.997184460e-04f, -4.662305323e-03f, -2.681538305e-04f, +0.000000000e+00f, - /* 20, 8 */ +0.000000000e+00f, -4.440621234e-04f, -4.888665552e-03f, +1.411071672e-04f, +2.049549532e-02f, +8.365076494e-03f, -5.750607943e-02f, -4.810357305e-02f, +1.631269271e-01f, +4.184154881e-01f, +4.184154881e-01f, +1.631269271e-01f, -4.810357305e-02f, -5.750607943e-02f, +8.365076494e-03f, +2.049549532e-02f, +1.411071672e-04f, -4.888665552e-03f, -4.440621234e-04f, +0.000000000e+00f, - /* 20, 9 */ +0.000000000e+00f, -2.681538305e-04f, -4.662305323e-03f, -7.997184460e-04f, +1.946251142e-02f, +1.113969540e-02f, -5.357538755e-02f, -5.436258502e-02f, +1.453772407e-01f, +4.075777294e-01f, +4.281320500e-01f, +1.811145256e-01f, -4.093872873e-02f, -6.115268907e-02f, +5.315299677e-03f, +2.139820068e-02f, +1.166768183e-03f, -5.078552799e-03f, -6.420563626e-04f, +0.000000000e+00f, - /* 20,10 */ +0.000000000e+00f, -1.144582079e-04f, -4.405667401e-03f, -1.652313816e-03f, +1.831850983e-02f, +1.363059437e-02f, -4.941278189e-02f, -5.972574809e-02f, +1.279475618e-01f, +3.956813102e-01f, +4.366710759e-01f, +1.992545658e-01f, -3.286393171e-02f, -6.446241843e-02f, +2.002216238e-03f, +2.215144089e-02f, +2.272594520e-03f, -5.225698259e-03f, -8.616336525e-04f, +0.000000000e+00f, - /* 20,11 */ +0.000000000e+00f, +1.724424543e-05f, -4.124801502e-03f, -2.414511840e-03f, +1.708262332e-02f, +1.583239979e-02f, -4.506940842e-02f, -6.420865295e-02f, +1.109160995e-01f, +3.827944964e-01f, +4.439828472e-01f, +2.174587480e-01f, -2.388113922e-02f, -6.738225311e-02f, -1.558959414e-03f, +2.273631696e-02f, +3.452605627e-03f, -5.323837897e-03f, -1.101888322e-03f, +0.000000000e+00f, - /* 20,12 */ +0.000000000e+00f, +1.274866066e-04f, -3.825546457e-03f, -3.085314600e-03f, +1.577366666e-02f, +1.774280068e-02f, -4.059502103e-02f, -6.783220328e-02f, +9.435670405e-02f, +3.689907742e-01f, +4.500246402e-01f, +2.356365195e-01f, -1.399855627e-02f, -6.985939290e-02f, -5.349604768e-03f, +2.313444000e-02f, +4.699488665e-03f, -5.366796329e-03f, -1.361489293e-03f, +0.000000000e+00f, - /* 20,13 */ +0.000000000e+00f, +2.170808154e-04f, -3.513472060e-03f, -3.664825055e-03f, +1.440996064e-02f, +1.936240016e-02f, -3.603763775e-02f, -7.062228683e-02f, +7.833843581e-02f, +3.543483057e-01f, +4.547610488e-01f, +2.536956771e-01f, -3.230759097e-03f, -7.184169597e-02f, -9.347674729e-03f, +2.332816092e-02f, +6.004591146e-03f, -5.348575554e-03f, -1.638662038e-03f, +0.000000000e+00f, - /* 20,14 */ +0.000000000e+00f, +2.870815261e-04f, -3.193828376e-03f, -4.154170312e-03f, +1.300917115e-02f, +2.069454672e-02f, -3.144322519e-02f, -7.260941515e-02f, +6.292517925e-02f, +3.389493512e-01f, +4.581642525e-01f, +2.715429904e-01f, +8.401230311e-03f, -7.327813327e-02f, -1.352771902e-02f, +2.330080531e-02f, +7.357928950e-03f, -5.263447672e-03f, -1.931175707e-03f, +0.000000000e+00f, - /* 20,15 */ +0.000000000e+00f, +3.387491377e-04f, -2.871502680e-03f, -4.555417854e-03f, +1.158816420e-02f, +2.174514756e-02f, -2.685541297e-02f, -7.382833631e-02f, +4.817530421e-02f, +3.228796668e-01f, +4.602142272e-01f, +2.890848421e-01f, +2.086992015e-02f, -7.411924916e-02f, -1.786093260e-02f, +2.303691111e-02f, +8.748210493e-03f, -5.106050653e-03f, -2.236335973e-03f, +3.929324583e-04f, - /* 16, 0 */ -4.898743621e-03f, -8.679086087e-05f, +2.336043359e-02f, +2.135055302e-04f, -7.556698393e-02f, -3.418085064e-04f, +3.068350485e-01f, +5.003964504e-01f, +3.068350485e-01f, -3.418085064e-04f, -7.556698393e-02f, +2.135055302e-04f, +2.336043359e-02f, -8.679086087e-05f, -4.898743621e-03f, +1.466795211e-05f, - /* 16, 1 */ -4.577177643e-03f, -1.168030162e-03f, +2.231338881e-02f, +4.259286102e-03f, -7.256190983e-02f, -1.325523148e-02f, +2.859961851e-01f, +4.995203198e-01f, +3.272077887e-01f, +1.366916740e-02f, -7.794631959e-02f, -4.137969722e-03f, +2.421268789e-02f, +1.104119466e-03f, -5.186216174e-03f, -1.424716020e-04f, - /* 16, 2 */ -4.229744004e-03f, -2.135347302e-03f, +2.109817837e-02f, +7.975999347e-03f, -6.900371451e-02f, -2.503996167e-02f, +2.648211478e-01f, +4.968980143e-01f, +3.469854770e-01f, +2.873680235e-02f, -7.962890015e-02f, -8.766610174e-03f, +2.484400873e-02f, +2.398541233e-03f, -5.431090074e-03f, -3.278051009e-04f, - /* 16, 3 */ -3.864289504e-03f, -2.986316790e-03f, +1.974155805e-02f, +1.134537917e-02f, -6.496587406e-02f, -3.567459207e-02f, +2.434398342e-01f, +4.925477372e-01f, +3.660412816e-01f, +4.481051979e-02f, -8.054606315e-02f, -1.363873477e-02f, +2.522910176e-02f, +3.788344934e-03f, -5.624692566e-03f, -5.415628140e-04f, - /* 16, 4 */ -3.488195595e-03f, -3.720237037e-03f, +1.827008292e-02f, +1.435416313e-02f, -6.052200094e-02f, -4.514733704e-02f, +2.219810322e-01f, +4.864996469e-01f, +3.842515379e-01f, +6.183022559e-02f, -8.063225815e-02f, -1.871557408e-02f, +2.534390000e-02f, +5.263393235e-03f, -5.758306879e-03f, -7.834433658e-04f, - /* 16, 5 */ -3.108305495e-03f, -4.338012209e-03f, +1.670979794e-02f, +1.699394035e-02f, -5.574514781e-02f, -5.345583367e-02f, +2.005713869e-01f, +4.787955863e-01f, +4.014968013e-01f, +7.972656727e-02f, -7.982580067e-02f, -2.395339311e-02f, +2.516595041e-02f, +6.811528665e-03f, -5.823306183e-03f, -1.052565974e-03f, - /* 16, 6 */ -2.730865011e-03f, -4.842020290e-03f, +1.508595356e-02f, +1.926095418e-02f, -5.070714412e-02f, -6.060686010e-02f, +1.793344024e-01f, +4.694887096e-01f, +4.176628724e-01f, +9.842128660e-02f, -7.806961406e-02f, -2.930367505e-02f, +2.467480385e-02f, +8.418588685e-03f, -5.811296621e-03f, -1.347428958e-03f, - /* 16, 7 */ -2.361477017e-03f, -5.235970004e-03f, +1.342274837e-02f, +2.115586371e-02f, -4.547797122e-02f, -6.661597542e-02f, +1.583894867e-01f, +4.586430086e-01f, +4.326417845e-01f, +1.178276637e-01f, -7.531195111e-02f, -3.471336612e-02f, +2.385240383e-02f, +1.006844961e-02f, -5.714267850e-03f, -1.665875716e-03f, - /* 16, 8 */ -2.005069351e-03f, -5.524749278e-03f, +1.174310057e-02f, +2.268346885e-02f, -4.012518151e-02f, -7.150708699e-02f, +1.378510501e-01f, +4.463327434e-01f, +4.463327434e-01f, +1.378510501e-01f, -7.150708699e-02f, -4.012518151e-02f, +2.268346885e-02f, +1.174310057e-02f, -5.524749278e-03f, -2.005069351e-03f, - /* 16, 9 */ -1.665875716e-03f, -5.714267850e-03f, +1.006844961e-02f, +2.385240383e-02f, -3.471336612e-02f, -7.531195111e-02f, +1.178276637e-01f, +4.326417845e-01f, +4.586430086e-01f, +1.583894867e-01f, -6.661597542e-02f, -4.547797122e-02f, +2.115586371e-02f, +1.342274837e-02f, -5.235970004e-03f, -2.361477017e-03f, - /* 16,10 */ -1.347428958e-03f, -5.811296621e-03f, +8.418588685e-03f, +2.467480385e-02f, -2.930367505e-02f, -7.806961406e-02f, +9.842128660e-02f, +4.176628724e-01f, +4.694887096e-01f, +1.793344024e-01f, -6.060686010e-02f, -5.070714412e-02f, +1.926095418e-02f, +1.508595356e-02f, -4.842020290e-03f, -2.730865011e-03f, - /* 16,11 */ -1.052565974e-03f, -5.823306183e-03f, +6.811528665e-03f, +2.516595041e-02f, -2.395339311e-02f, -7.982580067e-02f, +7.972656727e-02f, +4.014968013e-01f, +4.787955863e-01f, +2.005713869e-01f, -5.345583367e-02f, -5.574514781e-02f, +1.699394035e-02f, +1.670979794e-02f, -4.338012209e-03f, -3.108305495e-03f, - /* 16,12 */ -7.834433658e-04f, -5.758306879e-03f, +5.263393235e-03f, +2.534390000e-02f, -1.871557408e-02f, -8.063225815e-02f, +6.183022559e-02f, +3.842515379e-01f, +4.864996469e-01f, +2.219810322e-01f, -4.514733704e-02f, -6.052200094e-02f, +1.435416313e-02f, +1.827008292e-02f, -3.720237037e-03f, -3.488195595e-03f, - /* 16,13 */ -5.415628140e-04f, -5.624692566e-03f, +3.788344934e-03f, +2.522910176e-02f, -1.363873477e-02f, -8.054606315e-02f, +4.481051979e-02f, +3.660412816e-01f, +4.925477372e-01f, +2.434398342e-01f, -3.567459207e-02f, -6.496587406e-02f, +1.134537917e-02f, +1.974155805e-02f, -2.986316790e-03f, -3.864289504e-03f, - /* 16,14 */ -3.278051009e-04f, -5.431090074e-03f, +2.398541233e-03f, +2.484400873e-02f, -8.766610174e-03f, -7.962890015e-02f, +2.873680235e-02f, +3.469854770e-01f, +4.968980143e-01f, +2.648211478e-01f, -2.503996167e-02f, -6.900371451e-02f, +7.975999347e-03f, +2.109817837e-02f, -2.135347302e-03f, -4.229744004e-03f, - /* 16,15 */ -1.424716020e-04f, -5.186216174e-03f, +1.104119466e-03f, +2.421268789e-02f, -4.137969722e-03f, -7.794631959e-02f, +1.366916740e-02f, +3.272077887e-01f, +4.995203198e-01f, +2.859961851e-01f, -1.325523148e-02f, -7.256190983e-02f, +4.259286102e-03f, +2.231338881e-02f, -1.168030162e-03f, -4.577177643e-03f, - /* 16, 0 */ -1.854349243e-03f, -5.842655877e-03f, +1.571555836e-02f, +1.847159410e-02f, -6.634453543e-02f, -3.320569278e-02f, +3.025932104e-01f, +5.398940036e-01f, +3.025932104e-01f, -3.320569278e-02f, -6.634453543e-02f, +1.847159410e-02f, +1.571555836e-02f, -5.842655877e-03f, -1.854349243e-03f, +0.000000000e+00f, - /* 16, 1 */ -1.480579358e-03f, -6.106700866e-03f, +1.376986381e-02f, +2.107103425e-02f, -6.084498265e-02f, -4.482173865e-02f, +2.778559558e-01f, +5.387937054e-01f, +3.269511876e-01f, -2.013603096e-02f, -7.140657205e-02f, +1.540395578e-02f, +1.762103499e-02f, -5.450677830e-03f, -2.253515747e-03f, +0.000000000e+00f, - /* 16, 2 */ -1.136163241e-03f, -6.251562574e-03f, +1.181432209e-02f, +2.320368920e-02f, -5.500558000e-02f, -5.497544958e-02f, +2.529151163e-01f, +5.355017071e-01f, +3.507537104e-01f, -5.635398845e-03f, -7.593183970e-02f, +1.187314151e-02f, +1.945395611e-02f, -4.923375547e-03f, -2.673102395e-03f, +0.000000000e+00f, - /* 16, 3 */ -8.240613879e-04f, -6.287094834e-03f, +9.877041313e-03f, +2.487696888e-02f, -4.892141083e-02f, -6.367164518e-02f, +2.279442418e-01f, +5.300446041e-01f, +3.738258052e-01f, +1.025938806e-02f, -7.982055919e-02f, +7.890985370e-03f, +2.118036474e-02f, -4.254967852e-03f, -3.107109230e-03f, +0.000000000e+00f, - /* 16, 4 */ -5.462770218e-04f, -6.224002243e-03f, +7.983624178e-03f, +2.610378910e-02f, -4.268405269e-02f, -7.092815895e-02f, +2.031131300e-01f, +5.224664143e-01f, +3.959953988e-01f, +2.749725254e-02f, -8.297353764e-02f, +3.476439880e-03f, +2.276508169e-02f, -3.441527233e-03f, -3.548528769e-03f, +4.634120047e-04f, - /* 16, 5 */ -3.039101756e-04f, -6.073592210e-03f, +6.156978545e-03f, +2.690203687e-02f, -3.638073666e-02f, -7.677518685e-02f, +1.785862812e-01f, +5.128281199e-01f, +4.170950072e-01f, +4.601292009e-02f, -8.529332152e-02f, -1.344195268e-03f, +2.417214928e-02f, -2.481210963e-03f, -3.989383394e-03f, +4.400286560e-04f, - /* 16, 6 */ -9.722433303e-05f, -5.847536081e-03f, +4.417180930e-03f, +2.729400173e-02f, -3.009359622e-02f, -8.125451993e-02f, +1.545214364e-01f, +5.012070337e-01f, +4.369633957e-01f, +6.572714234e-02f, -8.668537697e-02f, -6.537129252e-03f, +2.536531778e-02f, -1.374474827e-03f, -4.420785166e-03f, +3.921992729e-04f, - /* 16, 7 */ +7.427658644e-05f, -5.557642708e-03f, +2.781391675e-03f, +2.730578215e-02f, -2.389901141e-02f, -8.441867356e-02f, +1.310682124e-01f, +4.876959980e-01f, +4.554471907e-01f, +8.654708074e-02f, -8.705928349e-02f, -1.206102709e-02f, +2.630856978e-02f, -1.242645535e-04f, -4.833018707e-03f, +3.169896142e-04f, - /* 16, 8 */ +2.117631665e-04f, -5.215647395e-03f, +1.263819875e-03f, +2.696667686e-02f, -1.786705263e-02f, -8.632992647e-02f, +1.083668491e-01f, +4.724024249e-01f, +4.724024249e-01f, +1.083668491e-01f, -8.632992647e-02f, -1.786705263e-02f, +2.696667686e-02f, +1.263819875e-03f, -5.215647395e-03f, +2.117631665e-04f, - /* 16, 9 */ +3.169896142e-04f, -4.833018707e-03f, -1.242645535e-04f, +2.630856978e-02f, -1.206102709e-02f, -8.705928349e-02f, +8.654708074e-02f, +4.554471907e-01f, +4.876959980e-01f, +1.310682124e-01f, -8.441867356e-02f, -2.389901141e-02f, +2.730578215e-02f, +2.781391675e-03f, -5.557642708e-03f, +7.427658644e-05f, - /* 16,10 */ +3.921992729e-04f, -4.420785166e-03f, -1.374474827e-03f, +2.536531778e-02f, -6.537129252e-03f, -8.668537697e-02f, +6.572714234e-02f, +4.369633957e-01f, +5.012070337e-01f, +1.545214364e-01f, -8.125451993e-02f, -3.009359622e-02f, +2.729400173e-02f, +4.417180930e-03f, -5.847536081e-03f, -9.722433303e-05f, - /* 16,11 */ +4.400286560e-04f, -3.989383394e-03f, -2.481210963e-03f, +2.417214928e-02f, -1.344195268e-03f, -8.529332152e-02f, +4.601292009e-02f, +4.170950072e-01f, +5.128281199e-01f, +1.785862812e-01f, -7.677518685e-02f, -3.638073666e-02f, +2.690203687e-02f, +6.156978545e-03f, -6.073592210e-03f, -3.039101756e-04f, - /* 16,12 */ +4.634120047e-04f, -3.548528769e-03f, -3.441527233e-03f, +2.276508169e-02f, +3.476439880e-03f, -8.297353764e-02f, +2.749725254e-02f, +3.959953988e-01f, +5.224664143e-01f, +2.031131300e-01f, -7.092815895e-02f, -4.268405269e-02f, +2.610378910e-02f, +7.983624178e-03f, -6.224002243e-03f, -5.462770218e-04f, - /* 16,13 */ +0.000000000e+00f, -3.107109230e-03f, -4.254967852e-03f, +2.118036474e-02f, +7.890985370e-03f, -7.982055919e-02f, +1.025938806e-02f, +3.738258052e-01f, +5.300446041e-01f, +2.279442418e-01f, -6.367164518e-02f, -4.892141083e-02f, +2.487696888e-02f, +9.877041313e-03f, -6.287094834e-03f, -8.240613879e-04f, - /* 16,14 */ +0.000000000e+00f, -2.673102395e-03f, -4.923375547e-03f, +1.945395611e-02f, +1.187314151e-02f, -7.593183970e-02f, -5.635398845e-03f, +3.507537104e-01f, +5.355017071e-01f, +2.529151163e-01f, -5.497544958e-02f, -5.500558000e-02f, +2.320368920e-02f, +1.181432209e-02f, -6.251562574e-03f, -1.136163241e-03f, - /* 16,15 */ +0.000000000e+00f, -2.253515747e-03f, -5.450677830e-03f, +1.762103499e-02f, +1.540395578e-02f, -7.140657205e-02f, -2.013603096e-02f, +3.269511876e-01f, +5.387937054e-01f, +2.778559558e-01f, -4.482173865e-02f, -6.084498265e-02f, +2.107103425e-02f, +1.376986381e-02f, -6.106700866e-03f, -1.480579358e-03f, - /* 16, 0 */ +2.517634455e-04f, -5.956310854e-03f, +5.008864062e-03f, +2.864631470e-02f, -4.909056125e-02f, -6.235528720e-02f, +2.936293584e-01f, +5.793915568e-01f, +2.936293584e-01f, -6.235528720e-02f, -4.909056125e-02f, +2.864631470e-02f, +5.008864062e-03f, -5.956310854e-03f, +2.517634455e-04f, +0.000000000e+00f, - /* 16, 1 */ +3.647589216e-04f, -5.559366521e-03f, +3.110945653e-03f, +2.922667528e-02f, -4.185408685e-02f, -7.174125192e-02f, +2.648835910e-01f, +5.780318135e-01f, +3.221602930e-01f, -5.117389488e-02f, -5.619351824e-02f, +2.755457966e-02f, +7.032385926e-03f, -6.289189967e-03f, +9.939782505e-05f, +0.000000000e+00f, - /* 16, 2 */ +4.414886472e-04f, -5.113535158e-03f, +1.357910925e-03f, +2.932892142e-02f, -3.459618474e-02f, -7.936172697e-02f, +2.361522790e-01f, +5.739652427e-01f, +3.502436629e-01f, -3.818535953e-02f, -6.304412145e-02f, +2.592390265e-02f, +9.158035052e-03f, -6.542440674e-03f, -9.477253367e-05f, +0.000000000e+00f, - /* 16, 3 */ +4.855802427e-04f, -4.633347213e-03f, -2.351453324e-04f, +2.899108127e-02f, -2.742112952e-02f, -8.526380919e-02f, +2.076588264e-01f, +5.672296697e-01f, +3.776458156e-01f, -2.339704980e-02f, -6.951800781e-02f, +2.373330296e-02f, +1.135820669e-02f, -6.700410184e-03f, -3.323676319e-04f, +0.000000000e+00f, - /* 16, 4 */ +0.000000000e+00f, -4.132457962e-03f, -1.657230762e-03f, +2.825501306e-02f, -2.042447313e-02f, -8.951050933e-02f, +1.796184274e-01f, +5.578876325e-01f, +4.041347532e-01f, -6.836060229e-03f, -7.548664793e-02f, +2.096923455e-02f, +1.360131724e-02f, -6.747680557e-03f, -6.140535745e-04f, +0.000000000e+00f, - /* 16, 5 */ +0.000000000e+00f, -3.623457200e-03f, -2.901306411e-03f, +2.716546883e-02f, -1.369230379e-02f, -9.217934767e-02f, +1.522358833e-01f, +5.460256322e-01f, +4.294827240e-01f, +1.145038182e-02f, -8.081883229e-02f, +1.762637069e-02f, +1.585203938e-02f, -6.669417793e-03f, -9.394126333e-04f, +0.000000000e+00f, - /* 16, 6 */ +0.000000000e+00f, -3.117716971e-03f, -3.964078879e-03f, +2.576917487e-02f, -7.300675124e-03f, -9.336081470e-02f, +1.257035893e-01f, +5.317530991e-01f, +4.534687988e-01f, +3.139475616e-02f, -8.538226676e-02f, +1.370830904e-02f, +1.807162423e-02f, -6.451740247e-03f, -1.306826648e-03f, +0.000000000e+00f, - /* 16, 7 */ +0.000000000e+00f, -2.625277441e-03f, -4.845741482e-03f, +2.411394259e-02f, -1.315205805e-03f, -9.315672206e-02f, +1.001997139e-01f, +5.152010878e-01f, +4.758813956e-01f, +5.290934542e-02f, -8.904525833e-02f, +9.228181275e-03f, +2.021831098e-02f, -6.082100328e-03f, -1.713377737e-03f, +0.000000000e+00f, - /* 16, 8 */ +0.000000000e+00f, -2.154770219e-03f, -5.549672684e-03f, +2.224782226e-02f, +4.209152176e-03f, -9.167847004e-02f, +7.588659143e-02f, +4.965207199e-01f, +4.965207199e-01f, +7.588659143e-02f, -9.167847004e-02f, +4.209152176e-03f, +2.224782226e-02f, -5.549672684e-03f, -2.154770219e-03f, +0.000000000e+00f, - /* 16, 9 */ +0.000000000e+00f, -1.713377737e-03f, -6.082100328e-03f, +2.021831098e-02f, +9.228181275e-03f, -8.904525833e-02f, +5.290934542e-02f, +4.758813956e-01f, +5.152010878e-01f, +1.001997139e-01f, -9.315672206e-02f, -1.315205805e-03f, +2.411394259e-02f, -4.845741482e-03f, -2.625277441e-03f, +0.000000000e+00f, - /* 16,10 */ +0.000000000e+00f, -1.306826648e-03f, -6.451740247e-03f, +1.807162423e-02f, +1.370830904e-02f, -8.538226676e-02f, +3.139475616e-02f, +4.534687988e-01f, +5.317530991e-01f, +1.257035893e-01f, -9.336081470e-02f, -7.300675124e-03f, +2.576917487e-02f, -3.964078879e-03f, -3.117716971e-03f, +0.000000000e+00f, - /* 16,11 */ +0.000000000e+00f, -9.394126333e-04f, -6.669417793e-03f, +1.585203938e-02f, +1.762637069e-02f, -8.081883229e-02f, +1.145038182e-02f, +4.294827240e-01f, +5.460256322e-01f, +1.522358833e-01f, -9.217934767e-02f, -1.369230379e-02f, +2.716546883e-02f, -2.901306411e-03f, -3.623457200e-03f, +0.000000000e+00f, - /* 16,12 */ +0.000000000e+00f, -6.140535745e-04f, -6.747680557e-03f, +1.360131724e-02f, +2.096923455e-02f, -7.548664793e-02f, -6.836060229e-03f, +4.041347532e-01f, +5.578876325e-01f, +1.796184274e-01f, -8.951050933e-02f, -2.042447313e-02f, +2.825501306e-02f, -1.657230762e-03f, -4.132457962e-03f, +0.000000000e+00f, - /* 16,13 */ +0.000000000e+00f, -3.323676319e-04f, -6.700410184e-03f, +1.135820669e-02f, +2.373330296e-02f, -6.951800781e-02f, -2.339704980e-02f, +3.776458156e-01f, +5.672296697e-01f, +2.076588264e-01f, -8.526380919e-02f, -2.742112952e-02f, +2.899108127e-02f, -2.351453324e-04f, -4.633347213e-03f, +4.855802427e-04f, - /* 16,14 */ +0.000000000e+00f, -9.477253367e-05f, -6.542440674e-03f, +9.158035052e-03f, +2.592390265e-02f, -6.304412145e-02f, -3.818535953e-02f, +3.502436629e-01f, +5.739652427e-01f, +2.361522790e-01f, -7.936172697e-02f, -3.459618474e-02f, +2.932892142e-02f, +1.357910925e-03f, -5.113535158e-03f, +4.414886472e-04f, - /* 16,15 */ +0.000000000e+00f, +9.939782505e-05f, -6.289189967e-03f, +7.032385926e-03f, +2.755457966e-02f, -5.619351824e-02f, -5.117389488e-02f, +3.221602930e-01f, +5.780318135e-01f, +2.648835910e-01f, -7.174125192e-02f, -4.185408685e-02f, +2.922667528e-02f, +3.110945653e-03f, -5.559366521e-03f, +3.647589216e-04f, - /* 12, 0 */ -3.638165547e-03f, +2.979985982e-02f, -2.723323293e-02f, -8.605047059e-02f, +2.801520768e-01f, +6.188891100e-01f, +2.801520768e-01f, -8.605047059e-02f, -2.723323293e-02f, +2.979985982e-02f, -3.638165547e-03f, -3.041512814e-03f, - /* 12, 1 */ -4.749738186e-03f, +2.841159300e-02f, -1.933319589e-02f, -9.237133076e-02f, +2.473915856e-01f, +6.172320760e-01f, +3.129551421e-01f, -7.764805088e-02f, -3.538029377e-02f, +3.077779188e-02f, -2.305275216e-03f, -3.612081594e-03f, - /* 12, 2 */ -5.642312008e-03f, +2.667449885e-02f, -1.178831271e-02f, -9.669686191e-02f, +2.149632830e-01f, +6.122785731e-01f, +3.455026876e-01f, -6.709962705e-02f, -4.365285408e-02f, +3.128617370e-02f, -7.534120996e-04f, -4.192641091e-03f, - /* 12, 3 */ -6.322395719e-03f, +2.465107974e-02f, -4.692548813e-03f, -9.913294928e-02f, +1.831448749e-01f, +6.040811565e-01f, +3.774917553e-01f, -5.436442589e-02f, -5.191703591e-02f, +3.126943445e-02f, +1.010002855e-03f, -4.769140004e-03f, - /* 12, 4 */ -6.800166749e-03f, +2.240361196e-02f, +1.874795505e-03f, -9.980279721e-02f, +1.521989634e-01f, +5.927266195e-01f, +4.086182709e-01f, -3.942694703e-02f, -6.002791415e-02f, +3.067703358e-02f, +2.972136287e-03f, -5.325763732e-03f, - /* 12, 5 */ -7.088917510e-03f, +1.999301835e-02f, +7.849320649e-03f, -9.884443272e-02f, +1.223701278e-01f, +5.783348068e-01f, +4.385808709e-01f, -2.229824534e-02f, -6.783107929e-02f, +2.946485483e-02f, +5.114495497e-03f, -5.845139328e-03f, - /* 12, 6 */ -7.204483224e-03f, +1.747784955e-02f, +1.318150805e-02f, -9.640809295e-02f, +9.388232321e-02f, +5.610569814e-01f, +4.670847512e-01f, -3.016864363e-03f, -7.516444191e-02f, +2.759658236e-02f, +7.412783505e-03f, -6.308600966e-03f, - /* 12, 7 */ -7.164664930e-03f, +1.491338589e-02f, +1.783645872e-02f, -9.265354184e-02f, +6.693662660e-02f, +5.410737692e-01f, +4.938454794e-01f, +1.835060492e-02f, -8.186025948e-02f, +2.504503167e-02f, +9.836867291e-03f, -6.696514785e-03f, - /* 12, 8 */ -6.988660420e-03f, +1.235086875e-02f, +2.179340724e-02f, -8.774736114e-02f, +4.170935934e-02f, +5.185927134e-01f, +5.185927134e-01f, +4.170935934e-02f, -8.774736114e-02f, +2.179340724e-02f, +1.235086875e-02f, -6.988660420e-03f, - /* 12, 9 */ -6.696514785e-03f, +9.836867291e-03f, +2.504503167e-02f, -8.186025948e-02f, +1.835060492e-02f, +4.938454794e-01f, +5.410737692e-01f, +6.693662660e-02f, -9.265354184e-02f, +1.783645872e-02f, +1.491338589e-02f, -7.164664930e-03f, - /* 12,10 */ -6.308600966e-03f, +7.412783505e-03f, +2.759658236e-02f, -7.516444191e-02f, -3.016864363e-03f, +4.670847512e-01f, +5.610569814e-01f, +9.388232321e-02f, -9.640809295e-02f, +1.318150805e-02f, +1.747784955e-02f, -7.204483224e-03f, - /* 12,11 */ -5.845139328e-03f, +5.114495497e-03f, +2.946485483e-02f, -6.783107929e-02f, -2.229824534e-02f, +4.385808709e-01f, +5.783348068e-01f, +1.223701278e-01f, -9.884443272e-02f, +7.849320649e-03f, +1.999301835e-02f, -7.088917510e-03f, - /* 12,12 */ -5.325763732e-03f, +2.972136287e-03f, +3.067703358e-02f, -6.002791415e-02f, -3.942694703e-02f, +4.086182709e-01f, +5.927266195e-01f, +1.521989634e-01f, -9.980279721e-02f, +1.874795505e-03f, +2.240361196e-02f, -6.800166749e-03f, - /* 12,13 */ -4.769140004e-03f, +1.010002855e-03f, +3.126943445e-02f, -5.191703591e-02f, -5.436442589e-02f, +3.774917553e-01f, +6.040811565e-01f, +1.831448749e-01f, -9.913294928e-02f, -4.692548813e-03f, +2.465107974e-02f, -6.322395719e-03f, - /* 12,14 */ -4.192641091e-03f, -7.534120996e-04f, +3.128617370e-02f, -4.365285408e-02f, -6.709962705e-02f, +3.455026876e-01f, +6.122785731e-01f, +2.149632830e-01f, -9.669686191e-02f, -1.178831271e-02f, +2.667449885e-02f, -5.642312008e-03f, - /* 12,15 */ -3.612081594e-03f, -2.305275216e-03f, +3.077779188e-02f, -3.538029377e-02f, -7.764805088e-02f, +3.129551421e-01f, +6.172320760e-01f, +2.473915856e-01f, -9.237133076e-02f, -1.933319589e-02f, +2.841159300e-02f, -4.749738186e-03f, - /* 12, 0 */ -7.562702671e-03f, +2.362257603e-02f, -4.531854693e-03f, -1.030173373e-01f, +2.624467795e-01f, +6.583866631e-01f, +2.624467795e-01f, -1.030173373e-01f, -4.531854693e-03f, +2.362257603e-02f, -7.562702671e-03f, -3.516889901e-04f, - /* 12, 1 */ -7.668183010e-03f, +2.087771707e-02f, +2.839059860e-03f, -1.056218320e-01f, +2.257778124e-01f, +6.563919279e-01f, +2.995227467e-01f, -9.814415944e-02f, -1.254420342e-02f, +2.617709089e-02f, -7.250906804e-03f, -7.142430143e-04f, - /* 12, 2 */ -7.590423774e-03f, +1.801705410e-02f, +9.487879886e-03f, -1.061169074e-01f, +1.898705313e-01f, +6.504316937e-01f, +3.366347146e-01f, -9.086648783e-02f, -2.109702270e-02f, +2.846338313e-02f, -6.712315500e-03f, -1.140764971e-03f, - /* 12, 3 */ -7.354275530e-03f, +1.511050856e-02f, +1.535418598e-02f, -1.046816488e-01f, +1.550586973e-01f, +6.405775033e-01f, +3.734003416e-01f, -8.107535131e-02f, -3.006937567e-02f, +3.040170317e-02f, -5.929880498e-03f, -1.628307909e-03f, - /* 12, 4 */ -6.985575046e-03f, +1.222230420e-02f, +2.039736787e-02f, -1.015111601e-01f, +1.216509697e-01f, +6.269473635e-01f, +4.094311989e-01f, -6.869168809e-02f, -3.932109040e-02f, +3.191206547e-02f, -4.890814148e-03f, -2.171433859e-03f, - /* 12, 5 */ -6.510406524e-03f, +9.410098002e-03f, +2.459585213e-02f, -9.681266365e-02f, +8.992722338e-02f, +6.097039216e-01f, +4.443382217e-01f, -5.366903171e-02f, -4.869391429e-02f, +3.291602689e-02f, -3.587389454e-03f, -2.762058981e-03f, - /* 12, 6 */ -5.954426605e-03f, +6.724324555e-03f, +2.794602403e-02f, -9.080157573e-02f, +6.013541337e-02f, +5.890519583e-01f, +4.777372670e-01f, -3.599575069e-02f, -5.801308453e-02f, +3.333857894e-02f, -2.017687876e-03f, -3.389362400e-03f, - /* 12, 7 */ -5.342264546e-03f, +4.207752570e-03f, +3.046088231e-02f, -8.369763050e-02f, +3.248902822e-02f, +5.652352421e-01f, +5.092546840e-01f, -1.569678608e-02f, -6.708930595e-02f, +3.311011989e-02f, -1.862710466e-04f, -4.039767889e-03f, - /* 12, 8 */ -4.697006242e-03f, +1.895247343e-03f, +3.216846911e-02f, -7.572111885e-02f, +7.165160645e-03f, +5.385328020e-01f, +5.385328020e-01f, +7.165160645e-03f, -7.572111885e-02f, +3.216846911e-02f, +1.895247343e-03f, -4.697006242e-03f, - /* 12, 9 */ -4.039767889e-03f, -1.862710466e-04f, +3.311011989e-02f, -6.708930595e-02f, -1.569678608e-02f, +5.092546840e-01f, +5.652352421e-01f, +3.248902822e-02f, -8.369763050e-02f, +3.046088231e-02f, +4.207752570e-03f, -5.342264546e-03f, - /* 12,10 */ -3.389362400e-03f, -2.017687876e-03f, +3.333857894e-02f, -5.801308453e-02f, -3.599575069e-02f, +4.777372670e-01f, +5.890519583e-01f, +6.013541337e-02f, -9.080157573e-02f, +2.794602403e-02f, +6.724324555e-03f, -5.954426605e-03f, - /* 12,11 */ -2.762058981e-03f, -3.587389454e-03f, +3.291602689e-02f, -4.869391429e-02f, -5.366903171e-02f, +4.443382217e-01f, +6.097039216e-01f, +8.992722338e-02f, -9.681266365e-02f, +2.459585213e-02f, +9.410098002e-03f, -6.510406524e-03f, - /* 12,12 */ -2.171433859e-03f, -4.890814148e-03f, +3.191206547e-02f, -3.932109040e-02f, -6.869168809e-02f, +4.094311989e-01f, +6.269473635e-01f, +1.216509697e-01f, -1.015111601e-01f, +2.039736787e-02f, +1.222230420e-02f, -6.985575046e-03f, - /* 12,13 */ -1.628307909e-03f, -5.929880498e-03f, +3.040170317e-02f, -3.006937567e-02f, -8.107535131e-02f, +3.734003416e-01f, +6.405775033e-01f, +1.550586973e-01f, -1.046816488e-01f, +1.535418598e-02f, +1.511050856e-02f, -7.354275530e-03f, - /* 12,14 */ -1.140764971e-03f, -6.712315500e-03f, +2.846338313e-02f, -2.109702270e-02f, -9.086648783e-02f, +3.366347146e-01f, +6.504316937e-01f, +1.898705313e-01f, -1.061169074e-01f, +9.487879886e-03f, +1.801705410e-02f, -7.590423774e-03f, - /* 12,15 */ -7.142430143e-04f, -7.250906804e-03f, +2.617709089e-02f, -1.254420342e-02f, -9.814415944e-02f, +2.995227467e-01f, +6.563919279e-01f, +2.257778124e-01f, -1.056218320e-01f, +2.839059860e-03f, +2.087771707e-02f, -7.668183010e-03f, - /* 12, 0 */ -7.009786996e-03f, +1.344312953e-02f, +1.557210222e-02f, -1.125190619e-01f, +2.408695221e-01f, +6.978842163e-01f, +2.408695221e-01f, -1.125190619e-01f, +1.557210222e-02f, +1.344312953e-02f, -7.009786996e-03f, +6.003640016e-04f, - /* 12, 1 */ -6.398742119e-03f, +1.026913982e-02f, +2.132332546e-02f, -1.110115061e-01f, +2.005160832e-01f, +6.955088069e-01f, +2.821133540e-01f, -1.117099281e-01f, +8.845385329e-03f, +1.669708199e-02f, -7.518519523e-03f, +5.590854556e-04f, - /* 12, 2 */ -5.716737920e-03f, +7.240001966e-03f, +2.606967700e-02f, -1.074325911e-01f, +1.614746033e-01f, +6.884146462e-01f, +3.237982615e-01f, -1.083606220e-01f, +1.198165974e-03f, +1.995657080e-02f, -7.892366537e-03f, +4.637249154e-04f, - /* 12, 3 */ -4.993154633e-03f, +4.410399512e-03f, +2.980590100e-02f, -1.020439692e-01f, +1.241329667e-01f, +6.766973789e-01f, +3.654537522e-01f, -1.022748781e-01f, -7.287927063e-03f, +2.313861998e-02f, -8.098411048e-03f, +3.063703263e-04f, - /* 12, 4 */ -4.254780730e-03f, +1.824453005e-03f, +3.254896791e-02f, -9.511805181e-02f, +8.884019172e-02f, +6.605145641e-01f, +4.065952670e-01f, -9.328874484e-02f, -1.650412301e-02f, +2.615301189e-02f, -8.104377377e-03f, +8.035370630e-05f, - /* 12, 5 */ -3.525333045e-03f, -4.843426812e-04f, +3.433584064e-02f, -8.693252112e-02f, +5.590207404e-02f, +6.400829406e-01f, +4.467316931e-01f, -8.127529114e-02f, -2.631456917e-02f, +2.890390773e-02f, -7.879705669e-03f, -2.193022854e-04f, - /* 12, 6 */ -2.825116890e-03f, -2.492908449e-03f, +3.522097401e-02f, -7.776502604e-02f, +2.557772128e-02f, +6.156746770e-01f, +4.853731430e-01f, -6.614880956e-02f, -3.655698883e-02f, +3.129176395e-02f, -7.396691856e-03f, -5.954441701e-04f, - /* 12, 7 */ -2.170822930e-03f, -4.188126176e-03f, +3.527362061e-02f, -6.788815999e-02f, -1.922977207e-03f, +5.876126808e-01f, +5.220388545e-01f, -4.786841211e-02f, -4.704395826e-02f, +3.321551909e-02f, -6.631665064e-03f, -1.048370326e-03f, - /* 12, 8 */ -1.575453811e-03f, -5.566170902e-03f, +3.457501660e-02f, -5.756481055e-02f, -2.644092188e-02f, +5.562650609e-01f, +5.562650609e-01f, -2.644092188e-02f, -5.756481055e-02f, +3.457501660e-02f, -5.566170902e-03f, -1.575453811e-03f, - /* 12, 9 */ -1.048370326e-03f, -6.631665064e-03f, +3.321551909e-02f, -4.704395826e-02f, -4.786841211e-02f, +5.220388545e-01f, +5.876126808e-01f, -1.922977207e-03f, -6.788815999e-02f, +3.527362061e-02f, -4.188126176e-03f, -2.170822930e-03f, - /* 12,10 */ -5.954441701e-04f, -7.396691856e-03f, +3.129176395e-02f, -3.655698883e-02f, -6.614880956e-02f, +4.853731430e-01f, +6.156746770e-01f, +2.557772128e-02f, -7.776502604e-02f, +3.522097401e-02f, -2.492908449e-03f, -2.825116890e-03f, - /* 12,11 */ -2.193022854e-04f, -7.879705669e-03f, +2.890390773e-02f, -2.631456917e-02f, -8.127529114e-02f, +4.467316931e-01f, +6.400829406e-01f, +5.590207404e-02f, -8.693252112e-02f, +3.433584064e-02f, -4.843426812e-04f, -3.525333045e-03f, - /* 12,12 */ +8.035370630e-05f, -8.104377377e-03f, +2.615301189e-02f, -1.650412301e-02f, -9.328874484e-02f, +4.065952670e-01f, +6.605145641e-01f, +8.884019172e-02f, -9.511805181e-02f, +3.254896791e-02f, +1.824453005e-03f, -4.254780730e-03f, - /* 12,13 */ +3.063703263e-04f, -8.098411048e-03f, +2.313861998e-02f, -7.287927063e-03f, -1.022748781e-01f, +3.654537522e-01f, +6.766973789e-01f, +1.241329667e-01f, -1.020439692e-01f, +2.980590100e-02f, +4.410399512e-03f, -4.993154633e-03f, - /* 12,14 */ +4.637249154e-04f, -7.892366537e-03f, +1.995657080e-02f, +1.198165974e-03f, -1.083606220e-01f, +3.237982615e-01f, +6.884146462e-01f, +1.614746033e-01f, -1.074325911e-01f, +2.606967700e-02f, +7.240001966e-03f, -5.716737920e-03f, - /* 12,15 */ +5.590854556e-04f, -7.518519523e-03f, +1.669708199e-02f, +8.845385329e-03f, -1.117099281e-01f, +2.821133540e-01f, +6.955088069e-01f, +2.005160832e-01f, -1.110115061e-01f, +2.132332546e-02f, +1.026913982e-02f, -6.398742119e-03f, - - /* 24, 0 */ +1.501390780e-03f, +3.431804419e-03f, +6.512803185e-03f, +1.091425387e-02f, +1.664594540e-02f, +2.351091132e-02f, +3.109255671e-02f, +3.878419288e-02f, +4.586050701e-02f, +5.158058002e-02f, +5.530384985e-02f, +5.659614054e-02f, +5.530384985e-02f, +5.158058002e-02f, +4.586050701e-02f, +3.878419288e-02f, +3.109255671e-02f, +2.351091132e-02f, +1.664594540e-02f, +1.091425387e-02f, +6.512803185e-03f, +3.431804419e-03f, +1.501390780e-03f, +4.573885647e-04f, - /* 24, 1 */ +1.413186400e-03f, +3.279858311e-03f, +6.282638036e-03f, +1.059932179e-02f, +1.625135142e-02f, +2.305547031e-02f, +3.060840342e-02f, +3.831365198e-02f, +4.545054680e-02f, +5.127577001e-02f, +5.513916011e-02f, +5.659104154e-02f, +5.545895049e-02f, +5.187752167e-02f, +4.626513642e-02f, +3.925233583e-02f, +3.157717954e-02f, +2.396921539e-02f, +1.704503934e-02f, +1.123445076e-02f, +6.748179094e-03f, +3.588275667e-03f, +1.593065611e-03f, +5.022154476e-04f, - /* 24, 2 */ +1.328380648e-03f, +3.132379333e-03f, +6.057656813e-03f, +1.028967374e-02f, +1.586133102e-02f, +2.260301890e-02f, +3.012488684e-02f, +3.784089895e-02f, +4.503543229e-02f, +5.096323022e-02f, +5.496495842e-02f, +5.657574693e-02f, +5.560438923e-02f, +5.216645963e-02f, +4.666426010e-02f, +3.971789474e-02f, +3.206210284e-02f, +2.443025293e-02f, +1.744855617e-02f, +1.155988996e-02f, +6.988790100e-03f, +3.749328623e-03f, +1.688282347e-03f, +5.494305796e-04f, - /* 24, 3 */ +1.246901403e-03f, +2.989308098e-03f, +5.837830254e-03f, +9.985325752e-03f, +1.547595434e-02f, +2.215368059e-02f, +2.964217216e-02f, +3.736611920e-02f, +4.461534144e-02f, +5.064310236e-02f, +5.478132634e-02f, +5.655026396e-02f, +5.574009777e-02f, +5.244726189e-02f, +4.705770477e-02f, +4.018068337e-02f, +3.254715574e-02f, +2.489389144e-02f, +1.785641537e-02f, +1.189054572e-02f, +7.234657995e-03f, +3.915018340e-03f, +1.787112015e-03f, +5.991047395e-04f, - /* 24, 4 */ +1.168676301e-03f, +2.850583915e-03f, +5.623126723e-03f, +9.686290690e-03f, +1.509528803e-02f, +2.170757578e-02f, +2.916042250e-02f, +3.688949768e-02f, +4.419045351e-02f, +5.031553118e-02f, +5.458834968e-02f, +5.651460469e-02f, +5.586601230e-02f, +5.271979985e-02f, +4.744529894e-02f, +4.064051541e-02f, +3.303216567e-02f, +2.535999546e-02f, +1.826853297e-02f, +1.222638897e-02f, +7.485801959e-03f, +4.085398290e-03f, +1.889625146e-03f, +6.513091287e-04f, - /* 24, 5 */ +1.093632798e-03f, +2.716144855e-03f, +5.413512274e-03f, +9.392578266e-03f, +1.471939531e-02f, +2.126482169e-02f, +2.867979883e-02f, +3.641121873e-02f, +4.376094899e-02f, +4.998066438e-02f, +5.438611851e-02f, +5.646878599e-02f, +5.598207354e-02f, +5.298394839e-02f, +4.782687301e-02f, +4.109720465e-02f, +3.351695842e-02f, +2.582842673e-02f, +1.868482156e-02f, +1.256738733e-02f, +7.742238512e-03f, +4.260520294e-03f, +1.995891717e-03f, +7.061153220e-04f, - /* 24, 6 */ +1.021698233e-03f, +2.585927824e-03f, +5.208950715e-03f, +9.104195104e-03f, +1.434833590e-02f, +2.082553239e-02f, +2.820045990e-02f, +3.593146595e-02f, +4.332700946e-02f, +4.963865252e-02f, +5.417472708e-02f, +5.641282954e-02f, +5.608822683e-02f, +5.323958602e-02f, +4.820225940e-02f, +4.155056502e-02f, +3.400135826e-02f, +2.629904416e-02f, +1.910519032e-02f, +1.291350505e-02f, +8.003981455e-03f, +4.440434453e-03f, +2.105981077e-03f, +7.635952183e-04f, - /* 24, 7 */ +9.527998831e-04f, +2.459868628e-03f, +5.009403670e-03f, +8.821144768e-03f, +1.398216608e-02f, +2.038981869e-02f, +2.772256216e-02f, +3.545042216e-02f, +4.288881749e-02f, +4.928964888e-02f, +5.395427373e-02f, +5.634676181e-02f, +5.618442211e-02f, +5.348659488e-02f, +4.857129262e-02f, +4.200041076e-02f, +3.448518802e-02f, +2.677170395e-02f, +1.952954505e-02f, +1.326470299e-02f, +8.271041819e-03f, +4.625189083e-03f, +2.219961884e-03f, +8.238209888e-04f, - /* 24, 8 */ +8.868650246e-04f, +2.337902042e-03f, +4.814830642e-03f, +8.543427812e-03f, +1.362093865e-02f, +1.995778816e-02f, +2.724625964e-02f, +3.496826923e-02f, +4.244655653e-02f, +4.893380942e-02f, +5.372486088e-02f, +5.627061400e-02f, +5.627061400e-02f, +5.372486088e-02f, +4.893380942e-02f, +4.244655653e-02f, +3.496826923e-02f, +2.724625964e-02f, +1.995778816e-02f, +1.362093865e-02f, +8.543427812e-03f, +4.814830642e-03f, +2.337902042e-03f, +8.868650246e-04f, - /* 24, 9 */ +8.238209888e-04f, +2.219961884e-03f, +4.625189083e-03f, +8.271041819e-03f, +1.326470299e-02f, +1.952954505e-02f, +2.677170395e-02f, +3.448518802e-02f, +4.200041076e-02f, +4.857129262e-02f, +5.348659488e-02f, +5.618442211e-02f, +5.634676181e-02f, +5.395427373e-02f, +4.928964888e-02f, +4.288881749e-02f, +3.545042216e-02f, +2.772256216e-02f, +2.038981869e-02f, +1.398216608e-02f, +8.821144768e-03f, +5.009403670e-03f, +2.459868628e-03f, +9.527998831e-04f, - /* 24,10 */ +7.635952183e-04f, +2.105981077e-03f, +4.440434453e-03f, +8.003981455e-03f, +1.291350505e-02f, +1.910519032e-02f, +2.629904416e-02f, +3.400135826e-02f, +4.155056502e-02f, +4.820225940e-02f, +5.323958602e-02f, +5.608822683e-02f, +5.641282954e-02f, +5.417472708e-02f, +4.963865252e-02f, +4.332700946e-02f, +3.593146595e-02f, +2.820045990e-02f, +2.082553239e-02f, +1.434833590e-02f, +9.104195104e-03f, +5.208950715e-03f, +2.585927824e-03f, +1.021698233e-03f, - /* 24,11 */ +7.061153220e-04f, +1.995891717e-03f, +4.260520294e-03f, +7.742238512e-03f, +1.256738733e-02f, +1.868482156e-02f, +2.582842673e-02f, +3.351695842e-02f, +4.109720465e-02f, +4.782687301e-02f, +5.298394839e-02f, +5.598207354e-02f, +5.646878599e-02f, +5.438611851e-02f, +4.998066438e-02f, +4.376094899e-02f, +3.641121873e-02f, +2.867979883e-02f, +2.126482169e-02f, +1.471939531e-02f, +9.392578266e-03f, +5.413512274e-03f, +2.716144855e-03f, +1.093632798e-03f, - /* 24,12 */ +6.513091287e-04f, +1.889625146e-03f, +4.085398290e-03f, +7.485801959e-03f, +1.222638897e-02f, +1.826853297e-02f, +2.535999546e-02f, +3.303216567e-02f, +4.064051541e-02f, +4.744529894e-02f, +5.271979985e-02f, +5.586601230e-02f, +5.651460469e-02f, +5.458834968e-02f, +5.031553118e-02f, +4.419045351e-02f, +3.688949768e-02f, +2.916042250e-02f, +2.170757578e-02f, +1.509528803e-02f, +9.686290690e-03f, +5.623126723e-03f, +2.850583915e-03f, +1.168676301e-03f, - /* 24,13 */ +5.991047395e-04f, +1.787112015e-03f, +3.915018340e-03f, +7.234657995e-03f, +1.189054572e-02f, +1.785641537e-02f, +2.489389144e-02f, +3.254715574e-02f, +4.018068337e-02f, +4.705770477e-02f, +5.244726189e-02f, +5.574009777e-02f, +5.655026396e-02f, +5.478132634e-02f, +5.064310236e-02f, +4.461534144e-02f, +3.736611920e-02f, +2.964217216e-02f, +2.215368059e-02f, +1.547595434e-02f, +9.985325752e-03f, +5.837830254e-03f, +2.989308098e-03f, +1.246901403e-03f, - /* 24,14 */ +5.494305796e-04f, +1.688282347e-03f, +3.749328623e-03f, +6.988790100e-03f, +1.155988996e-02f, +1.744855617e-02f, +2.443025293e-02f, +3.206210284e-02f, +3.971789474e-02f, +4.666426010e-02f, +5.216645963e-02f, +5.560438923e-02f, +5.657574693e-02f, +5.496495842e-02f, +5.096323022e-02f, +4.503543229e-02f, +3.784089895e-02f, +3.012488684e-02f, +2.260301890e-02f, +1.586133102e-02f, +1.028967374e-02f, +6.057656813e-03f, +3.132379333e-03f, +1.328380648e-03f, - /* 24,15 */ +5.022154476e-04f, +1.593065611e-03f, +3.588275667e-03f, +6.748179094e-03f, +1.123445076e-02f, +1.704503934e-02f, +2.396921539e-02f, +3.157717954e-02f, +3.925233583e-02f, +4.626513642e-02f, +5.187752167e-02f, +5.545895049e-02f, +5.659104154e-02f, +5.513916011e-02f, +5.127577001e-02f, +4.545054680e-02f, +3.831365198e-02f, +3.060840342e-02f, +2.305547031e-02f, +1.625135142e-02f, +1.059932179e-02f, +6.282638036e-03f, +3.279858311e-03f, +1.413186400e-03f, - /* 24, 0 */ -2.629184871e-03f, -4.843950453e-03f, -6.895985300e-03f, -7.687208098e-03f, -5.978262553e-03f, -8.032174656e-04f, +8.095316761e-03f, +1.997958831e-02f, +3.311864145e-02f, +4.512644231e-02f, +5.356009950e-02f, +5.659614054e-02f, +5.356009950e-02f, +4.512644231e-02f, +3.311864145e-02f, +1.997958831e-02f, +8.095316761e-03f, -8.032174656e-04f, -5.978262553e-03f, -7.687208098e-03f, -6.895985300e-03f, -4.843950453e-03f, -2.629184871e-03f, -9.454953712e-04f, - /* 24, 1 */ -2.503767166e-03f, -4.700731697e-03f, -6.791825424e-03f, -7.698565601e-03f, -6.179328945e-03f, -1.237726578e-03f, +7.438688744e-03f, +1.917778123e-02f, +3.230413198e-02f, +4.445707943e-02f, +5.317715832e-02f, +5.658405316e-02f, +5.392156860e-02f, +4.578163621e-02f, +3.392875410e-02f, +2.078652132e-02f, +8.763945305e-03f, -3.538276542e-04f, -5.763420347e-03f, -7.665996832e-03f, -6.995273095e-03f, -4.986674025e-03f, -2.756835384e-03f, -1.028686673e-03f, - /* 24, 2 */ -2.380688695e-03f, -4.557243028e-03f, -6.683099486e-03f, -7.700368745e-03f, -6.366798820e-03f, -1.657314491e-03f, +6.794365087e-03f, +1.838162773e-02f, +3.148585651e-02f, +4.377411309e-02f, +5.277308334e-02f, +5.654780182e-02f, +5.426124576e-02f, +4.642210542e-02f, +3.473383802e-02f, +2.159804191e-02f, +9.444254477e-03f, +1.103863968e-04f, -5.534634231e-03f, -7.634636496e-03f, -7.089380216e-03f, -5.128670417e-03f, -2.886604737e-03f, -1.114962551e-03f, - /* 24, 3 */ -2.260048394e-03f, -4.413702845e-03f, -6.570110572e-03f, -7.692920583e-03f, -6.540862270e-03f, -2.061956485e-03f, +6.162633403e-03f, +1.759164425e-02f, +3.066444409e-02f, +4.307811806e-02f, +5.234823086e-02f, +5.648741902e-02f, +5.457882991e-02f, +4.704730472e-02f, +3.553326091e-02f, +2.241360152e-02f, +1.013590849e-02f, +5.893521078e-04f, -5.291747706e-03f, -7.592836347e-03f, -7.177995846e-03f, -5.269701073e-03f, -3.018371382e-03f, -1.204312280e-03f, - /* 24, 4 */ -2.141937776e-03f, -4.270322542e-03f, -6.453158507e-03f, -7.676527355e-03f, -6.701719772e-03f, -2.451643421e-03f, +5.543764951e-03f, +1.680833562e-02f, +2.984052134e-02f, +4.236967758e-02f, +5.190297478e-02f, +5.640295884e-02f, +5.487403917e-02f, +4.765670002e-02f, +3.632639074e-02f, +2.323264190e-02f, +1.083855586e-02f, +1.082980638e-03f, -5.034616251e-03f, -7.540310660e-03f, -7.260807322e-03f, -5.409521052e-03f, -3.152006158e-03f, -1.296719170e-03f, - /* 24, 5 */ -2.026441000e-03f, -4.127306381e-03f, -6.332539518e-03f, -7.651498041e-03f, -6.849581767e-03f, -2.826381528e-03f, +4.938014526e-03f, +1.603219452e-02f, +2.901471178e-02f, +4.164938279e-02f, +5.143770614e-02f, +5.629449693e-02f, +5.514661113e-02f, +4.824976895e-02f, +3.711259647e-02f, +2.405459566e-02f, +1.155182965e-02f, +1.591166761e-03f, -4.763107701e-03f, -7.476779193e-03f, -7.337500507e-03f, -5.547879217e-03f, -3.287372274e-03f, -1.392160404e-03f, - /* 24, 6 */ -1.913634953e-03f, -3.984851387e-03f, -6.208545927e-03f, -7.618143912e-03f, -6.984668233e-03f, -3.186192169e-03f, +4.345620369e-03f, +1.526370115e-02f, +2.818763519e-02f, +4.091783200e-02f, +5.095283267e-02f, +5.616213037e-02f, +5.539630322e-02f, +4.882600150e-02f, +3.789124869e-02f, +2.487888677e-02f, +1.227534767e-02f, +2.113788767e-03f, -4.477102606e-03f, -7.401967644e-03f, -7.407760182e-03f, -5.684518438e-03f, -3.424325302e-03f, -1.490606880e-03f, - /* 24, 7 */ -1.803589350e-03f, -3.843147252e-03f, -6.081465840e-03f, -7.576778087e-03f, -7.107208249e-03f, -3.531111592e-03f, +3.766804102e-03f, +1.450332275e-02f, +2.735990694e-02f, +4.017563005e-02f, +5.044877831e-02f, +5.600597761e-02f, +5.562289296e-02f, +4.938490059e-02f, +3.866172039e-02f, +2.570493119e-02f, +1.300871280e-02f, +2.650708377e-03f, -4.176494585e-03f, -7.315608112e-03f, -7.471270440e-03f, -5.819175805e-03f, -3.562713186e-03f, -1.592023060e-03f, - /* 24, 8 */ -1.696366827e-03f, -3.702376254e-03f, -5.951582861e-03f, -7.527715094e-03f, -7.217439556e-03f, -3.861190662e-03f, +3.201770681e-03f, +1.375151322e-02f, +2.653213738e-02f, +3.942338759e-02f, +4.992598268e-02f, +5.582617825e-02f, +5.582617825e-02f, +4.992598268e-02f, +3.942338759e-02f, +2.653213738e-02f, +1.375151322e-02f, +3.201770681e-03f, -3.861190662e-03f, -7.217439556e-03f, -7.527715094e-03f, -5.951582861e-03f, -3.702376254e-03f, -1.696366827e-03f, - /* 24, 9 */ -1.592023060e-03f, -3.562713186e-03f, -5.819175805e-03f, -7.471270440e-03f, -7.315608112e-03f, -4.176494585e-03f, +2.650708377e-03f, +1.300871280e-02f, +2.570493119e-02f, +3.866172039e-02f, +4.938490059e-02f, +5.562289296e-02f, +5.600597761e-02f, +5.044877831e-02f, +4.017563005e-02f, +2.735990694e-02f, +1.450332275e-02f, +3.766804102e-03f, -3.531111592e-03f, -7.107208249e-03f, -7.576778087e-03f, -6.081465840e-03f, -3.843147252e-03f, -1.803589350e-03f, - /* 24,10 */ -1.490606880e-03f, -3.424325302e-03f, -5.684518438e-03f, -7.407760182e-03f, -7.401967644e-03f, -4.477102606e-03f, +2.113788767e-03f, +1.227534767e-02f, +2.487888677e-02f, +3.789124869e-02f, +4.882600150e-02f, +5.539630322e-02f, +5.616213037e-02f, +5.095283267e-02f, +4.091783200e-02f, +2.818763519e-02f, +1.526370115e-02f, +4.345620369e-03f, -3.186192169e-03f, -6.984668233e-03f, -7.618143912e-03f, -6.208545927e-03f, -3.984851387e-03f, -1.913634953e-03f, - /* 24,11 */ -1.392160404e-03f, -3.287372274e-03f, -5.547879217e-03f, -7.337500507e-03f, -7.476779193e-03f, -4.763107701e-03f, +1.591166761e-03f, +1.155182965e-02f, +2.405459566e-02f, +3.711259647e-02f, +4.824976895e-02f, +5.514661113e-02f, +5.629449693e-02f, +5.143770614e-02f, +4.164938279e-02f, +2.901471178e-02f, +1.603219452e-02f, +4.938014526e-03f, -2.826381528e-03f, -6.849581767e-03f, -7.651498041e-03f, -6.332539518e-03f, -4.127306381e-03f, -2.026441000e-03f, - /* 24,12 */ -1.296719170e-03f, -3.152006158e-03f, -5.409521052e-03f, -7.260807322e-03f, -7.540310660e-03f, -5.034616251e-03f, +1.082980638e-03f, +1.083855586e-02f, +2.323264190e-02f, +3.632639074e-02f, +4.765670002e-02f, +5.487403917e-02f, +5.640295884e-02f, +5.190297478e-02f, +4.236967758e-02f, +2.984052134e-02f, +1.680833562e-02f, +5.543764951e-03f, -2.451643421e-03f, -6.701719772e-03f, -7.676527355e-03f, -6.453158507e-03f, -4.270322542e-03f, -2.141937776e-03f, - /* 24,13 */ -1.204312280e-03f, -3.018371382e-03f, -5.269701073e-03f, -7.177995846e-03f, -7.592836347e-03f, -5.291747706e-03f, +5.893521078e-04f, +1.013590849e-02f, +2.241360152e-02f, +3.553326091e-02f, +4.704730472e-02f, +5.457882991e-02f, +5.648741902e-02f, +5.234823086e-02f, +4.307811806e-02f, +3.066444409e-02f, +1.759164425e-02f, +6.162633403e-03f, -2.061956485e-03f, -6.540862270e-03f, -7.692920583e-03f, -6.570110572e-03f, -4.413702845e-03f, -2.260048394e-03f, - /* 24,14 */ -1.114962551e-03f, -2.886604737e-03f, -5.128670417e-03f, -7.089380216e-03f, -7.634636496e-03f, -5.534634231e-03f, +1.103863968e-04f, +9.444254477e-03f, +2.159804191e-02f, +3.473383802e-02f, +4.642210542e-02f, +5.426124576e-02f, +5.654780182e-02f, +5.277308334e-02f, +4.377411309e-02f, +3.148585651e-02f, +1.838162773e-02f, +6.794365087e-03f, -1.657314491e-03f, -6.366798820e-03f, -7.700368745e-03f, -6.683099486e-03f, -4.557243028e-03f, -2.380688695e-03f, - /* 24,15 */ -1.028686673e-03f, -2.756835384e-03f, -4.986674025e-03f, -6.995273095e-03f, -7.665996832e-03f, -5.763420347e-03f, -3.538276542e-04f, +8.763945305e-03f, +2.078652132e-02f, +3.392875410e-02f, +4.578163621e-02f, +5.392156860e-02f, +5.658405316e-02f, +5.317715832e-02f, +4.445707943e-02f, +3.230413198e-02f, +1.917778123e-02f, +7.438688744e-03f, -1.237726578e-03f, -6.179328945e-03f, -7.698565601e-03f, -6.791825424e-03f, -4.700731697e-03f, -2.503767166e-03f, - /* 24, 0 */ +4.735641749e-04f, -1.438577362e-03f, -6.107076473e-03f, -1.318715065e-02f, -2.047716119e-02f, -2.428668798e-02f, -2.088952800e-02f, -8.512165320e-03f, +1.117510535e-02f, +3.302575560e-02f, +5.012757987e-02f, +5.659614054e-02f, +5.012757987e-02f, +3.302575560e-02f, +1.117510535e-02f, -8.512165320e-03f, -2.088952800e-02f, -2.428668798e-02f, -2.047716119e-02f, -1.318715065e-02f, -6.107076473e-03f, -1.438577362e-03f, +4.735641749e-04f, +5.516063288e-04f, - /* 24, 1 */ +5.190146993e-04f, -1.243445781e-03f, -5.732182665e-03f, -1.270621714e-02f, -2.008108462e-02f, -2.422674988e-02f, -2.136190754e-02f, -9.536491055e-03f, +9.813857768e-03f, +3.172645287e-02f, +4.932296812e-02f, +5.657007725e-02f, +5.088942272e-02f, +3.430616434e-02f, +1.254542812e-02f, -7.458075491e-03f, -2.038088472e-02f, -2.431781992e-02f, -2.085968072e-02f, -1.366943909e-02f, -6.492037400e-03f, -1.644903025e-03f, +4.208638005e-04f, +5.761542752e-04f, - /* 24, 2 */ +5.575380238e-04f, -1.059370463e-03f, -5.367638258e-03f, -1.222740313e-02f, -1.967247249e-02f, -2.413881461e-02f, -2.179812108e-02f, -1.053019587e-02f, +8.463295193e-03f, +3.041001010e-02f, +4.847674006e-02f, +5.649192540e-02f, +5.160740292e-02f, +3.556594146e-02f, +1.392318625e-02f, -6.375136316e-03f, -1.983593655e-02f, -2.431936776e-02f, -2.122761958e-02f, -1.415229209e-02f, -6.886752185e-03f, -1.862540304e-03f, +3.605947820e-04f, +5.982066157e-04f, - /* 24, 3 */ +5.894596941e-04f, -8.861942853e-04f, -5.013694839e-03f, -1.175144649e-02f, -1.925234223e-02f, -2.402372023e-02f, -2.219832191e-02f, -1.149248169e-02f, +7.124994951e-03f, +2.907819451e-02f, +4.759010507e-02f, +5.636179897e-02f, +5.228048761e-02f, +3.680336864e-02f, +1.530671242e-02f, -5.264319552e-03f, -1.925469982e-02f, -2.429058667e-02f, -2.157995394e-02f, -1.463489443e-02f, -7.290876362e-03f, -2.091585491e-03f, +2.924431607e-04f, +6.174753085e-04f, - /* 24, 4 */ +6.151072142e-04f, -7.237418200e-04f, -4.670573645e-03f, -1.127905759e-02f, -1.882170532e-02f, -2.388233174e-02f, -2.256271775e-02f, -1.242260980e-02f, +5.800499486e-03f, +2.773278351e-02f, +4.666432713e-02f, +5.617988770e-02f, +5.290770668e-02f, +3.801674993e-02f, +1.669431448e-02f, -4.126652928e-03f, -1.863724919e-02f, -2.423076690e-02f, -2.191566174e-02f, -1.511640714e-02f, -7.704034115e-03f, -2.332112699e-03f, +2.161008111e-04f, +6.336652968e-04f, - /* 24, 5 */ +6.348091316e-04f, -5.718203517e-04f, -4.338465973e-03f, -1.081091853e-02f, -1.838156554e-02f, -2.371553901e-02f, -2.289156957e-02f, -1.331990148e-02f, +4.491314051e-03f, +2.637556170e-02f, +4.570072248e-02f, +5.594645674e-02f, +5.348815454e-02f, +3.920441468e-02f, +1.808427811e-02f, -2.963218986e-03f, -1.798371833e-02f, -2.413923574e-02f, -2.223372473e-02f, -1.559596853e-02f, -8.125818185e-03f, -2.584172964e-03f, +1.312664533e-04f, +6.464750685e-04f, - /* 24, 6 */ +6.488941487e-04f, -4.302209069e-04f, -4.017533648e-03f, -1.034768250e-02f, -1.793291714e-02f, -2.352425464e-02f, -2.318519030e-02f, -1.418373842e-02f, +3.198904487e-03f, +2.500831779e-02f, +4.470065727e-02f, +5.566184614e-02f, +5.402099181e-02f, +4.036472049e-02f, +1.947486957e-02f, -1.775153809e-03f, -1.729430055e-02f, -2.401535937e-02f, -2.253313051e-02f, -1.607269547e-02f, -8.555789856e-03f, -2.847793367e-03f, +3.764667972e-05f, +6.555972530e-04f, - /* 24, 7 */ +6.576902611e-04f, -2.987192943e-04f, -3.707909539e-03f, -9.889973186e-03f, -1.747674315e-02f, -2.330941189e-02f, -2.344394342e-02f, -1.501356300e-02f, +1.924695104e-03f, +2.363284155e-02f, +4.366554511e-02f, +5.532647026e-02f, +5.450544680e-02f, +4.149605616e-02f, +2.086433852e-02f, -5.636456344e-04f, -1.656924930e-02f, -2.385854478e-02f, -2.281287459e-02f, -1.654568462e-02f, -8.993479016e-03f, -3.122976195e-03f, -6.504300803e-05f, +6.607192554e-04f, - /* 24, 8 */ +6.615239252e-04f, -1.770771536e-04f, -3.409698141e-03f, -9.438384277e-03f, -1.701401374e-02f, -2.307196251e-02f, -2.366824152e-02f, -1.580887853e-02f, +6.700666468e-04f, +2.225092083e-02f, +4.259684448e-02f, +5.494081698e-02f, +5.494081698e-02f, +4.259684448e-02f, +2.225092083e-02f, +6.700666468e-04f, -1.580887853e-02f, -2.366824152e-02f, -2.307196251e-02f, -1.701401374e-02f, -9.438384277e-03f, -3.409698141e-03f, -1.770771536e-04f, +6.615239252e-04f, - /* 24, 9 */ +6.607192554e-04f, -6.504300803e-05f, -3.122976195e-03f, -8.993479016e-03f, -1.654568462e-02f, -2.281287459e-02f, -2.385854478e-02f, -1.656924930e-02f, -5.636456344e-04f, +2.086433852e-02f, +4.149605616e-02f, +5.450544680e-02f, +5.532647026e-02f, +4.366554511e-02f, +2.363284155e-02f, +1.924695104e-03f, -1.501356300e-02f, -2.344394342e-02f, -2.330941189e-02f, -1.747674315e-02f, -9.889973186e-03f, -3.707909539e-03f, -2.987192943e-04f, +6.576902611e-04f, - /* 24,10 */ +6.555972530e-04f, +3.764667972e-05f, -2.847793367e-03f, -8.555789856e-03f, -1.607269547e-02f, -2.253313051e-02f, -2.401535937e-02f, -1.729430055e-02f, -1.775153809e-03f, +1.947486957e-02f, +4.036472049e-02f, +5.402099181e-02f, +5.566184614e-02f, +4.470065727e-02f, +2.500831779e-02f, +3.198904487e-03f, -1.418373842e-02f, -2.318519030e-02f, -2.352425464e-02f, -1.793291714e-02f, -1.034768250e-02f, -4.017533648e-03f, -4.302209069e-04f, +6.488941487e-04f, - /* 24,11 */ +6.464750685e-04f, +1.312664533e-04f, -2.584172964e-03f, -8.125818185e-03f, -1.559596853e-02f, -2.223372473e-02f, -2.413923574e-02f, -1.798371833e-02f, -2.963218986e-03f, +1.808427811e-02f, +3.920441468e-02f, +5.348815454e-02f, +5.594645674e-02f, +4.570072248e-02f, +2.637556170e-02f, +4.491314051e-03f, -1.331990148e-02f, -2.289156957e-02f, -2.371553901e-02f, -1.838156554e-02f, -1.081091853e-02f, -4.338465973e-03f, -5.718203517e-04f, +6.348091316e-04f, - /* 24,12 */ +6.336652968e-04f, +2.161008111e-04f, -2.332112699e-03f, -7.704034115e-03f, -1.511640714e-02f, -2.191566174e-02f, -2.423076690e-02f, -1.863724919e-02f, -4.126652928e-03f, +1.669431448e-02f, +3.801674993e-02f, +5.290770668e-02f, +5.617988770e-02f, +4.666432713e-02f, +2.773278351e-02f, +5.800499486e-03f, -1.242260980e-02f, -2.256271775e-02f, -2.388233174e-02f, -1.882170532e-02f, -1.127905759e-02f, -4.670573645e-03f, -7.237418200e-04f, +6.151072142e-04f, - /* 24,13 */ +6.174753085e-04f, +2.924431607e-04f, -2.091585491e-03f, -7.290876362e-03f, -1.463489443e-02f, -2.157995394e-02f, -2.429058667e-02f, -1.925469982e-02f, -5.264319552e-03f, +1.530671242e-02f, +3.680336864e-02f, +5.228048761e-02f, +5.636179897e-02f, +4.759010507e-02f, +2.907819451e-02f, +7.124994951e-03f, -1.149248169e-02f, -2.219832191e-02f, -2.402372023e-02f, -1.925234223e-02f, -1.175144649e-02f, -5.013694839e-03f, -8.861942853e-04f, +5.894596941e-04f, - /* 24,14 */ +5.982066157e-04f, +3.605947820e-04f, -1.862540304e-03f, -6.886752185e-03f, -1.415229209e-02f, -2.122761958e-02f, -2.431936776e-02f, -1.983593655e-02f, -6.375136316e-03f, +1.392318625e-02f, +3.556594146e-02f, +5.160740292e-02f, +5.649192540e-02f, +4.847674006e-02f, +3.041001010e-02f, +8.463295193e-03f, -1.053019587e-02f, -2.179812108e-02f, -2.413881461e-02f, -1.967247249e-02f, -1.222740313e-02f, -5.367638258e-03f, -1.059370463e-03f, +5.575380238e-04f, - /* 24,15 */ +5.761542752e-04f, +4.208638005e-04f, -1.644903025e-03f, -6.492037400e-03f, -1.366943909e-02f, -2.085968072e-02f, -2.431781992e-02f, -2.038088472e-02f, -7.458075491e-03f, +1.254542812e-02f, +3.430616434e-02f, +5.088942272e-02f, +5.657007725e-02f, +4.932296812e-02f, +3.172645287e-02f, +9.813857768e-03f, -9.536491055e-03f, -2.136190754e-02f, -2.422674988e-02f, -2.008108462e-02f, -1.270621714e-02f, -5.732182665e-03f, -1.243445781e-03f, +5.190146993e-04f, - /* 24, 0 */ +2.273459443e-03f, +5.435907648e-03f, +7.255296399e-03f, +3.788129032e-03f, -7.144684562e-03f, -2.265374973e-02f, -3.442368170e-02f, -3.287677614e-02f, -1.387331771e-02f, +1.679264590e-02f, +4.511451955e-02f, +5.659614054e-02f, +4.511451955e-02f, +1.679264590e-02f, -1.387331771e-02f, -3.287677614e-02f, -3.442368170e-02f, -2.265374973e-02f, -7.144684562e-03f, +3.788129032e-03f, +7.255296399e-03f, +5.435907648e-03f, +2.273459443e-03f, +3.568431303e-04f, - /* 24, 1 */ +2.103234406e-03f, +5.239407106e-03f, +7.256400195e-03f, +4.221207454e-03f, -6.266228991e-03f, -2.168841690e-02f, -3.399213075e-02f, -3.348773370e-02f, -1.551504116e-02f, +1.477681868e-02f, +4.371373211e-02f, +5.654911556e-02f, +4.644656718e-02f, +1.879953521e-02f, -1.218307761e-02f, -3.219410560e-02f, -3.480135038e-02f, -2.360501860e-02f, -8.042999544e-03f, +3.324105568e-03f, +7.232988111e-03f, +5.627714430e-03f, +2.449385040e-03f, +4.247055554e-04f, - /* 24, 2 */ +1.939021806e-03f, +5.039131826e-03f, +7.237298926e-03f, +4.623451366e-03f, -5.409068119e-03f, -2.071157693e-02f, -3.350883303e-02f, -3.402698064e-02f, -1.710557364e-02f, +1.275612557e-02f, +4.224725679e-02f, +5.640814528e-02f, +4.770696520e-02f, +2.079340350e-02f, -1.044713814e-02f, -3.143988919e-02f, -3.512309015e-02f, -2.453963953e-02f, -8.959642554e-03f, +2.829112073e-03f, +7.188501693e-03f, +5.813881008e-03f, +2.630658922e-03f, +4.992251326e-04f, - /* 24, 3 */ +1.781093672e-03f, +4.835971292e-03f, +7.199013760e-03f, +4.995053998e-03f, -4.574539506e-03f, -1.972575310e-02f, -3.297600576e-02f, -3.449468646e-02f, -1.864238902e-02f, +1.073461753e-02f, +4.071827965e-02f, +5.617354343e-02f, +4.889295364e-02f, +2.277016679e-02f, -8.668453837e-03f, -3.061446547e-02f, -3.538695026e-02f, -2.545500791e-02f, -9.892988076e-03f, +2.303211764e-03f, +7.120893393e-03f, +5.993435804e-03f, +2.816887995e-03f, +5.805470381e-04f, - /* 24, 4 */ +1.629682882e-03f, +4.630783503e-03f, +7.142583584e-03f, +5.336288236e-03f, -3.763881685e-03f, -1.873342898e-02f, -3.239594057e-02f, -3.489118499e-02f, -2.012311412e-02f, +8.716314532e-03f, +3.913011251e-02f, +5.584583195e-02f, +5.000192959e-02f, +2.472575033e-02f, -6.850110412e-03f, -2.971834586e-02f, -3.559108276e-02f, -2.634850527e-02f, -1.084131876e-02f, +1.746558492e-03f, +7.029253460e-03f, +6.165384566e-03f, +3.007638074e-03f, +6.687931554e-04f, - /* 24, 5 */ +1.484983972e-03f, +4.424393216e-03f, +7.069061064e-03f, +5.647503405e-03f, -2.978233194e-03f, -1.773704257e-02f, -3.177099609e-02f, -3.521697115e-02f, -2.154553308e-02f, +6.705195776e-03f, +3.748618427e-02f, +5.542573963e-02f, +5.103145416e-02f, +2.665609886e-02f, -4.995318215e-03f, -2.875221569e-02f, -3.573374914e-02f, -2.721750622e-02f, -1.180282806e-02f, +1.159398961e-03f, +6.912710257e-03f, +6.328712988e-03f, +3.202433762e-03f, +7.640603932e-04f, - /* 24, 6 */ +1.347154069e-03f, +4.217590351e-03f, +6.979508760e-03f, +5.929121902e-03f, -2.218631929e-03f, -1.673898061e-02f, -3.110359053e-02f, -3.547269735e-02f, -2.290759116e-02f, +4.705190128e-03f, +3.579003198e-02f, +5.491420012e-02f, +5.197925890e-02f, +2.855718684e-02f, -3.107405323e-03f, -2.771693469e-02f, -3.581332680e-02f, -2.805938547e-02f, -1.277562317e-02f, +5.420746921e-04f, +6.770434377e-03f, +6.482389493e-03f, +3.400758480e-03f, +8.664190421e-04f, - /* 24, 7 */ +1.216313935e-03f, +4.011128586e-03f, +6.874995333e-03f, +6.181635683e-03f, -1.486014823e-03f, -1.574157316e-02f, -3.039619415e-02f, -3.565916944e-02f, -2.420739811e-02f, +2.720166717e-03f, +3.404529163e-02f, +5.431234943e-02f, +5.284325182e-02f, +3.042502866e-02f, -1.189810237e-03f, -2.661353708e-02f, -3.582831530e-02f, -2.887152508e-02f, -1.375772836e-02f, -1.049762569e-04f, +6.601642735e-03f, +6.625368167e-03f, +3.602054665e-03f, +9.759111772e-04f, - /* 24, 8 */ +1.092549115e-03f, +3.805724130e-03f, +6.756591845e-03f, +6.405602617e-03f, -7.812178358e-04f, -1.474708852e-02f, -2.965132174e-02f, -3.577734234e-02f, -2.544323110e-02f, +7.539257812e-04f, +3.225568873e-02f, +5.362152294e-02f, +5.362152294e-02f, +3.225568873e-02f, +7.539257812e-04f, -2.544323110e-02f, -3.577734234e-02f, -2.965132174e-02f, -1.474708852e-02f, -7.812178358e-04f, +6.405602617e-03f, +6.756591845e-03f, +3.805724130e-03f, +1.092549115e-03f, - /* 24, 9 */ +9.759111772e-04f, +3.602054665e-03f, +6.625368167e-03f, +6.601642735e-03f, -1.049762569e-04f, -1.375772836e-02f, -2.887152508e-02f, -3.582831530e-02f, -2.661353708e-02f, -1.189810237e-03f, +3.042502866e-02f, +5.284325182e-02f, +5.431234943e-02f, +3.404529163e-02f, +2.720166717e-03f, -2.420739811e-02f, -3.565916944e-02f, -3.039619415e-02f, -1.574157316e-02f, -1.486014823e-03f, +6.181635683e-03f, +6.874995333e-03f, +4.011128586e-03f, +1.216313935e-03f, - /* 24,10 */ +8.664190421e-04f, +3.400758480e-03f, +6.482389493e-03f, +6.770434377e-03f, +5.420746921e-04f, -1.277562317e-02f, -2.805938547e-02f, -3.581332680e-02f, -2.771693469e-02f, -3.107405323e-03f, +2.855718684e-02f, +5.197925890e-02f, +5.491420012e-02f, +3.579003198e-02f, +4.705190128e-03f, -2.290759116e-02f, -3.547269735e-02f, -3.110359053e-02f, -1.673898061e-02f, -2.218631929e-03f, +5.929121902e-03f, +6.979508760e-03f, +4.217590351e-03f, +1.347154069e-03f, - /* 24,11 */ +7.640603932e-04f, +3.202433762e-03f, +6.328712988e-03f, +6.912710257e-03f, +1.159398961e-03f, -1.180282806e-02f, -2.721750622e-02f, -3.573374914e-02f, -2.875221569e-02f, -4.995318215e-03f, +2.665609886e-02f, +5.103145416e-02f, +5.542573963e-02f, +3.748618427e-02f, +6.705195776e-03f, -2.154553308e-02f, -3.521697115e-02f, -3.177099609e-02f, -1.773704257e-02f, -2.978233194e-03f, +5.647503405e-03f, +7.069061064e-03f, +4.424393216e-03f, +1.484983972e-03f, - /* 24,12 */ +6.687931554e-04f, +3.007638074e-03f, +6.165384566e-03f, +7.029253460e-03f, +1.746558492e-03f, -1.084131876e-02f, -2.634850527e-02f, -3.559108276e-02f, -2.971834586e-02f, -6.850110412e-03f, +2.472575033e-02f, +5.000192959e-02f, +5.584583195e-02f, +3.913011251e-02f, +8.716314532e-03f, -2.012311412e-02f, -3.489118499e-02f, -3.239594057e-02f, -1.873342898e-02f, -3.763881685e-03f, +5.336288236e-03f, +7.142583584e-03f, +4.630783503e-03f, +1.629682882e-03f, - /* 24,13 */ +5.805470381e-04f, +2.816887995e-03f, +5.993435804e-03f, +7.120893393e-03f, +2.303211764e-03f, -9.892988076e-03f, -2.545500791e-02f, -3.538695026e-02f, -3.061446547e-02f, -8.668453837e-03f, +2.277016679e-02f, +4.889295364e-02f, +5.617354343e-02f, +4.071827965e-02f, +1.073461753e-02f, -1.864238902e-02f, -3.449468646e-02f, -3.297600576e-02f, -1.972575310e-02f, -4.574539506e-03f, +4.995053998e-03f, +7.199013760e-03f, +4.835971292e-03f, +1.781093672e-03f, - /* 24,14 */ +4.992251326e-04f, +2.630658922e-03f, +5.813881008e-03f, +7.188501693e-03f, +2.829112073e-03f, -8.959642554e-03f, -2.453963953e-02f, -3.512309015e-02f, -3.143988919e-02f, -1.044713814e-02f, +2.079340350e-02f, +4.770696520e-02f, +5.640814528e-02f, +4.224725679e-02f, +1.275612557e-02f, -1.710557364e-02f, -3.402698064e-02f, -3.350883303e-02f, -2.071157693e-02f, -5.409068119e-03f, +4.623451366e-03f, +7.237298926e-03f, +5.039131826e-03f, +1.939021806e-03f, - /* 24,15 */ +4.247055554e-04f, +2.449385040e-03f, +5.627714430e-03f, +7.232988111e-03f, +3.324105568e-03f, -8.042999544e-03f, -2.360501860e-02f, -3.480135038e-02f, -3.219410560e-02f, -1.218307761e-02f, +1.879953521e-02f, +4.644656718e-02f, +5.654911556e-02f, +4.371373211e-02f, +1.477681868e-02f, -1.551504116e-02f, -3.348773370e-02f, -3.399213075e-02f, -2.168841690e-02f, -6.266228991e-03f, +4.221207454e-03f, +7.256400195e-03f, +5.239407106e-03f, +2.103234406e-03f, - /* 24, 0 */ -2.181310192e-03f, -7.982329251e-04f, +5.680209618e-03f, +1.430719659e-02f, +1.589843483e-02f, +2.406871994e-03f, -2.249676846e-02f, -4.130100690e-02f, -3.506718353e-02f, -1.541681908e-03f, +3.867898214e-02f, +5.659614054e-02f, +3.867898214e-02f, -1.541681908e-03f, -3.506718353e-02f, -4.130100690e-02f, -2.249676846e-02f, +2.406871994e-03f, +1.589843483e-02f, +1.430719659e-02f, +5.680209618e-03f, -7.982329251e-04f, -2.181310192e-03f, -9.324150638e-04f, - /* 24, 1 */ -2.142117633e-03f, -1.026327303e-03f, +5.144075019e-03f, +1.386145083e-02f, +1.619749380e-02f, +3.702669669e-03f, -2.089125129e-02f, -4.071342524e-02f, -3.635626763e-02f, -4.137848013e-03f, +3.654904222e-02f, +5.652117066e-02f, +4.071616274e-02f, +1.083860427e-03f, -3.366302266e-02f, -4.178478525e-02f, -2.407924887e-02f, +1.061252794e-03f, +1.553625174e-02f, +1.472529111e-02f, +6.227191439e-03f, -5.482915187e-04f, -2.210193915e-03f, -1.021372070e-03f, - /* 24, 2 */ -2.093579858e-03f, -1.232841058e-03f, +4.620399561e-03f, +1.339085359e-02f, +1.643462496e-02f, +4.945866528e-03f, -1.926829204e-02f, -4.002576024e-02f, -3.752797769e-02f, -6.697199080e-03f, +3.433305089e-02f, +5.629650283e-02f, +4.265414906e-02f, +3.731182183e-03f, -3.214649405e-02f, -4.216132985e-02f, -2.563305646e-02f, -3.311524193e-04f, +1.510995111e-02f, +1.511293981e-02f, +6.783287607e-03f, -2.763303743e-04f, -2.227804667e-03f, -1.112061839e-03f, - /* 24, 3 */ -2.036654869e-03f, -1.418128950e-03f, +4.110671651e-03f, +1.289819803e-02f, +1.661121711e-02f, +6.133943976e-03f, -1.763342417e-02f, -3.924200344e-02f, -3.858043162e-02f, -9.212479159e-03f, +3.203796457e-02f, +5.592286159e-02f, +4.448680259e-02f, +6.392554173e-03f, -3.052071354e-02f, -4.242751674e-02f, -2.715253413e-02f, -1.767057534e-03f, +1.461875233e-02f, +1.546736546e-02f, +7.346647501e-03f, +1.772445414e-05f, -2.233183138e-03f, -1.203936109e-03f, - /* 24, 4 */ -1.972290178e-03f, -1.582628528e-03f, +3.616254280e-03f, +1.238625918e-02f, +1.672884047e-02f, +7.264647438e-03f, -1.599210066e-02f, -3.836639935e-02f, -3.951216427e-02f, -1.167663915e-02f, +2.967096294e-02f, +5.540145153e-02f, +4.620830373e-02f, +9.060141131e-03f, -2.878919714e-02f, -4.258054458e-02f, -2.863202454e-02f, -3.242932618e-03f, +1.406209682e-02f, +1.578582064e-02f, +7.915306646e-03f, +3.338433846e-04f, -2.225380377e-03f, -1.296401007e-03f, - /* 24, 5 */ -1.901418208e-03f, -1.726854356e-03f, +3.138383775e-03f, +1.185778300e-02f, +1.678923519e-02f, +8.335988548e-03f, -1.434967550e-02f, -3.740342600e-02f, -4.032212766e-02f, -1.408285985e-02f, +2.723942290e-02f, +5.473395280e-02f, +4.781317317e-02f, +1.172602857e-02f, -2.695585286e-02f, -4.261794963e-02f, -3.006589126e-02f, -4.755011838e-03f, +1.343965653e-02f, +1.606560041e-02f, +8.487191262e-03f, +6.718888821e-04f, -2.203463508e-03f, -1.388818223e-03f, - /* 24, 6 */ -1.824951954e-03f, -1.851392085e-03f, +2.678169173e-03f, +1.131547576e-02f, +1.679429951e-02f, +9.346246206e-03f, -1.271138577e-02f, -3.635777499e-02f, -4.100968953e-02f, -1.642457396e-02f, +2.475089197e-02f, +5.392251484e-02f, +4.929629202e-02f, +1.438225015e-02f, -2.502497093e-02f, -4.253761970e-02f, -3.144854025e-02f, -6.299302508e-03f, +1.275134172e-02f, +1.630405519e-02f, +9.060123484e-03f, +1.031611407e-03f, -2.166521604e-03f, -1.480506488e-03f, - /* 24, 7 */ -1.743780953e-03f, -1.956892396e-03f, +2.236592212e-03f, +1.076199396e-02f, +1.674607744e-02f, +1.029396653e-02f, -1.108233467e-02f, -3.523433096e-02f, -4.157463041e-02f, -1.869548696e-02f, +2.221306113e-02f, +5.296974848e-02f, +5.065292065e-02f, +1.702081530e-02f, -2.300121251e-02f, -4.233780689e-02f, -3.277444146e-02f, -7.871595256e-03f, +1.199730786e-02f, +1.649860375e-02f, +9.631827247e-03f, +1.412645767e-03f, -2.113671692e-03f, -1.570743396e-03f, - /* 24, 8 */ -1.658767534e-03f, -2.044064852e-03f, +1.814507917e-03f, +1.019993481e-02f, +1.664674627e-02f, +1.117796172e-02f, -9.467475281e-03f, -3.403815061e-02f, -4.201713914e-02f, -2.088959684e-02f, +1.963373727e-02f, +5.187871616e-02f, +5.187871616e-02f, +1.963373727e-02f, -2.088959684e-02f, -4.201713914e-02f, -3.403815061e-02f, -9.467475281e-03f, +1.117796172e-02f, +1.664674627e-02f, +1.019993481e-02f, +1.814507917e-03f, -2.044064852e-03f, -1.658767534e-03f, - /* 24, 9 */ -1.570743396e-03f, -2.113671692e-03f, +1.412645767e-03f, +9.631827247e-03f, +1.649860375e-02f, +1.199730786e-02f, -7.871595256e-03f, -3.277444146e-02f, -4.233780689e-02f, -2.300121251e-02f, +1.702081530e-02f, +5.065292065e-02f, +5.296974848e-02f, +2.221306113e-02f, -1.869548696e-02f, -4.157463041e-02f, -3.523433096e-02f, -1.108233467e-02f, +1.029396653e-02f, +1.674607744e-02f, +1.076199396e-02f, +2.236592212e-03f, -1.956892396e-03f, -1.743780953e-03f, - /* 24,10 */ -1.480506488e-03f, -2.166521604e-03f, +1.031611407e-03f, +9.060123484e-03f, +1.630405519e-02f, +1.275134172e-02f, -6.299302508e-03f, -3.144854025e-02f, -4.253761970e-02f, -2.502497093e-02f, +1.438225015e-02f, +4.929629202e-02f, +5.392251484e-02f, +2.475089197e-02f, -1.642457396e-02f, -4.100968953e-02f, -3.635777499e-02f, -1.271138577e-02f, +9.346246206e-03f, +1.679429951e-02f, +1.131547576e-02f, +2.678169173e-03f, -1.851392085e-03f, -1.824951954e-03f, - /* 24,11 */ -1.388818223e-03f, -2.203463508e-03f, +6.718888821e-04f, +8.487191262e-03f, +1.606560041e-02f, +1.343965653e-02f, -4.755011838e-03f, -3.006589126e-02f, -4.261794963e-02f, -2.695585286e-02f, +1.172602857e-02f, +4.781317317e-02f, +5.473395280e-02f, +2.723942290e-02f, -1.408285985e-02f, -4.032212766e-02f, -3.740342600e-02f, -1.434967550e-02f, +8.335988548e-03f, +1.678923519e-02f, +1.185778300e-02f, +3.138383775e-03f, -1.726854356e-03f, -1.901418208e-03f, - /* 24,12 */ -1.296401007e-03f, -2.225380377e-03f, +3.338433846e-04f, +7.915306646e-03f, +1.578582064e-02f, +1.406209682e-02f, -3.242932618e-03f, -2.863202454e-02f, -4.258054458e-02f, -2.878919714e-02f, +9.060141131e-03f, +4.620830373e-02f, +5.540145153e-02f, +2.967096294e-02f, -1.167663915e-02f, -3.951216427e-02f, -3.836639935e-02f, -1.599210066e-02f, +7.264647438e-03f, +1.672884047e-02f, +1.238625918e-02f, +3.616254280e-03f, -1.582628528e-03f, -1.972290178e-03f, - /* 24,13 */ -1.203936109e-03f, -2.233183138e-03f, +1.772445414e-05f, +7.346647501e-03f, +1.546736546e-02f, +1.461875233e-02f, -1.767057534e-03f, -2.715253413e-02f, -4.242751674e-02f, -3.052071354e-02f, +6.392554173e-03f, +4.448680259e-02f, +5.592286159e-02f, +3.203796457e-02f, -9.212479159e-03f, -3.858043162e-02f, -3.924200344e-02f, -1.763342417e-02f, +6.133943976e-03f, +1.661121711e-02f, +1.289819803e-02f, +4.110671651e-03f, -1.418128950e-03f, -2.036654869e-03f, - /* 24,14 */ -1.112061839e-03f, -2.227804667e-03f, -2.763303743e-04f, +6.783287607e-03f, +1.511293981e-02f, +1.510995111e-02f, -3.311524193e-04f, -2.563305646e-02f, -4.216132985e-02f, -3.214649405e-02f, +3.731182183e-03f, +4.265414906e-02f, +5.629650283e-02f, +3.433305089e-02f, -6.697199080e-03f, -3.752797769e-02f, -4.002576024e-02f, -1.926829204e-02f, +4.945866528e-03f, +1.643462496e-02f, +1.339085359e-02f, +4.620399561e-03f, -1.232841058e-03f, -2.093579858e-03f, - /* 24,15 */ -1.021372070e-03f, -2.210193915e-03f, -5.482915187e-04f, +6.227191439e-03f, +1.472529111e-02f, +1.553625174e-02f, +1.061252794e-03f, -2.407924887e-02f, -4.178478525e-02f, -3.366302266e-02f, +1.083860427e-03f, +4.071616274e-02f, +5.652117066e-02f, +3.654904222e-02f, -4.137848013e-03f, -3.635626763e-02f, -4.071342524e-02f, -2.089125129e-02f, +3.702669669e-03f, +1.619749380e-02f, +1.386145083e-02f, +5.144075019e-03f, -1.026327303e-03f, -2.142117633e-03f, - /* 24, 0 */ -6.349328336e-04f, -5.107444449e-03f, -7.589492700e-03f, +4.421169254e-04f, +1.733331948e-02f, +2.497839430e-02f, +6.069612140e-03f, -2.970035006e-02f, -4.651799663e-02f, -1.968310326e-02f, +3.102388246e-02f, +5.659614054e-02f, +3.102388246e-02f, -1.968310326e-02f, -4.651799663e-02f, -2.970035006e-02f, +6.069612140e-03f, +2.497839430e-02f, +1.733331948e-02f, +4.421169254e-04f, -7.589492700e-03f, -5.107444449e-03f, -6.349328336e-04f, +6.381929817e-04f, - /* 24, 1 */ -4.501246045e-04f, -4.794789988e-03f, -7.673310752e-03f, -4.276921651e-04f, +1.630487239e-02f, +2.519230977e-02f, +8.023727481e-03f, -2.760467194e-02f, -4.668156835e-02f, -2.250226055e-02f, +2.808383767e-02f, +5.648624601e-02f, +3.385706239e-02f, -1.675917373e-02f, -4.616688010e-02f, -3.171828840e-02f, +4.039135426e-03f, +2.465060544e-02f, +1.832599562e-02f, +1.353161175e-03f, -7.461005422e-03f, -5.414037993e-03f, -8.347893499e-04f, +6.459962873e-04f, - /* 24, 2 */ -2.805431667e-04f, -4.478334337e-03f, -7.714347254e-03f, -1.253762011e-03f, +1.524677189e-02f, +2.529479915e-02f, +9.894771606e-03f, -2.544172743e-02f, -4.665953469e-02f, -2.520578478e-02f, +2.504972253e-02f, +5.615705320e-02f, +3.657100703e-02f, -1.374190145e-02f, -4.562711379e-02f, -3.364818878e-02f, +1.939527429e-03f, +2.420699082e-02f, +1.927675855e-02f, +2.302607998e-03f, -7.286133997e-03f, -5.712221732e-03f, -1.049390115e-03f, +6.454263440e-04f, - /* 24, 3 */ -1.262469087e-04f, -4.160237857e-03f, -7.714644364e-03f, -2.033919173e-03f, +1.416507919e-02f, +2.528877950e-02f, +1.167657735e-02f, -2.322211124e-02f, -4.645464989e-02f, -2.778343069e-02f, +2.193469332e-02f, +5.561003206e-02f, +3.915383052e-02f, -1.064323425e-02f, -4.489844274e-02f, -3.547998209e-02f, -2.214871506e-04f, +2.364611605e-02f, +2.017947396e-02f, +3.287300483e-03f, -7.063354139e-03f, -5.999568857e-03f, -1.278300802e-03f, +6.356530069e-04f, - /* 24, 4 */ +1.281987696e-05f, -3.842552418e-03f, -7.676380196e-03f, -2.766321017e-03f, +1.306576874e-02f, +2.517761055e-02f, +1.336353941e-02f, -2.095648565e-02f, -4.607045954e-02f, -3.022561220e-02f, +1.875220414e-02f, +5.484762432e-02f, +4.159418981e-02f, -7.475585158e-03f, -4.398147340e-02f, -3.720388175e-02f, -2.435717775e-03f, +2.296708552e-02f, +2.102804905e-02f, +4.303763633e-03f, -6.791349552e-03f, -6.273586899e-03f, -1.520952773e-03f, +6.158659890e-04f, - /* 24, 5 */ +1.368204413e-04f, -3.527213369e-03f, -7.601850138e-03f, -3.449453954e-03f, +1.195469912e-02f, +2.496506585e-02f, +1.495063011e-02f, -1.865552736e-02f, -4.551127331e-02f, -3.252344226e-02f, +1.551594186e-02f, +5.387323140e-02f, +4.388134039e-02f, -4.251776447e-03f, -4.287768073e-02f, -3.881043651e-02f, -4.694539858e-03f, +2.216956067e-02f, +2.181646678e-02f, +5.348212687e-03f, -6.469028645e-03f, -6.531730950e-03f, -1.776639841e-03f, +5.852828120e-04f, - /* 24, 6 */ +2.460185614e-04f, -3.216032617e-03f, -7.493448175e-03f, -4.082129823e-03f, +1.083758546e-02f, +2.465530244e-02f, +1.643341199e-02f, -1.632987505e-02f, -4.478213426e-02f, -3.466876896e-02f, +1.223976005e-02f, +5.269119738e-02f, +4.600518917e-02f, -9.849812576e-04f, -4.158941105e-02f, -4.029058231e-02f, -6.988929457e-03f, +2.125377578e-02f, +2.253882061e-02f, +6.416563547e-03f, -6.095540465e-03f, -6.771417794e-03f, -2.044515881e-03f, +5.431569434e-04f, - /* 24, 7 */ +3.407711290e-04f, -2.910692818e-03f, -7.353648294e-03f, -4.663480492e-03f, +9.719973395e-03f, +2.425282916e-02f, +1.780804686e-02f, -1.399007798e-02f, -4.388878466e-02f, -3.665420751e-02f, +8.937612534e-03f, +5.130678745e-02f, +4.795634432e-02f, +2.311336934e-03f, -4.011988036e-02f, -4.163569289e-02f, -9.309500719e-03f, +2.022055084e-02f, +2.318934965e-02f, +7.504445299e-03f, -5.670289717e-03f, -6.990040888e-03f, -2.323593338e-03f, +4.887860648e-04f, - /* 24, 8 */ +4.215204125e-04f, -2.612742676e-03f, -7.184986124e-03f, -5.192950770e-03f, +8.607214812e-03f, +2.376247385e-02f, +1.907130164e-02f, -1.164654593e-02f, -4.283762880e-02f, -3.847316827e-02f, +5.623486691e-03f, +4.972616165e-02f, +4.972616165e-02f, +5.623486691e-03f, -3.847316827e-02f, -4.283762880e-02f, -1.164654593e-02f, +1.907130164e-02f, +2.376247385e-02f, +8.607214812e-03f, -5.192950770e-03f, -7.184986124e-03f, -2.612742676e-03f, +4.215204125e-04f, - /* 24, 9 */ +4.887860648e-04f, -2.323593338e-03f, -6.990040888e-03f, -5.670289717e-03f, +7.504445299e-03f, +2.318934965e-02f, +2.022055084e-02f, -9.309500719e-03f, -4.163569289e-02f, -4.011988036e-02f, +2.311336934e-03f, +4.795634432e-02f, +5.130678745e-02f, +8.937612534e-03f, -3.665420751e-02f, -4.388878466e-02f, -1.399007798e-02f, +1.780804686e-02f, +2.425282916e-02f, +9.719973395e-03f, -4.663480492e-03f, -7.353648294e-03f, -2.910692818e-03f, +3.407711290e-04f, - /* 24,10 */ +5.431569434e-04f, -2.044515881e-03f, -6.771417794e-03f, -6.095540465e-03f, +6.416563547e-03f, +2.253882061e-02f, +2.125377578e-02f, -6.988929457e-03f, -4.029058231e-02f, -4.158941105e-02f, -9.849812576e-04f, +4.600518917e-02f, +5.269119738e-02f, +1.223976005e-02f, -3.466876896e-02f, -4.478213426e-02f, -1.632987505e-02f, +1.643341199e-02f, +2.465530244e-02f, +1.083758546e-02f, -4.082129823e-03f, -7.493448175e-03f, -3.216032617e-03f, +2.460185614e-04f, - /* 24,11 */ +5.852828120e-04f, -1.776639841e-03f, -6.531730950e-03f, -6.469028645e-03f, +5.348212687e-03f, +2.181646678e-02f, +2.216956067e-02f, -4.694539858e-03f, -3.881043651e-02f, -4.287768073e-02f, -4.251776447e-03f, +4.388134039e-02f, +5.387323140e-02f, +1.551594186e-02f, -3.252344226e-02f, -4.551127331e-02f, -1.865552736e-02f, +1.495063011e-02f, +2.496506585e-02f, +1.195469912e-02f, -3.449453954e-03f, -7.601850138e-03f, -3.527213369e-03f, +1.368204413e-04f, - /* 24,12 */ +6.158659890e-04f, -1.520952773e-03f, -6.273586899e-03f, -6.791349552e-03f, +4.303763633e-03f, +2.102804905e-02f, +2.296708552e-02f, -2.435717775e-03f, -3.720388175e-02f, -4.398147340e-02f, -7.475585158e-03f, +4.159418981e-02f, +5.484762432e-02f, +1.875220414e-02f, -3.022561220e-02f, -4.607045954e-02f, -2.095648565e-02f, +1.336353941e-02f, +2.517761055e-02f, +1.306576874e-02f, -2.766321017e-03f, -7.676380196e-03f, -3.842552418e-03f, +1.281987696e-05f, - /* 24,13 */ +6.356530069e-04f, -1.278300802e-03f, -5.999568857e-03f, -7.063354139e-03f, +3.287300483e-03f, +2.017947396e-02f, +2.364611605e-02f, -2.214871506e-04f, -3.547998209e-02f, -4.489844274e-02f, -1.064323425e-02f, +3.915383052e-02f, +5.561003206e-02f, +2.193469332e-02f, -2.778343069e-02f, -4.645464989e-02f, -2.322211124e-02f, +1.167657735e-02f, +2.528877950e-02f, +1.416507919e-02f, -2.033919173e-03f, -7.714644364e-03f, -4.160237857e-03f, -1.262469087e-04f, - /* 24,14 */ +6.454263440e-04f, -1.049390115e-03f, -5.712221732e-03f, -7.286133997e-03f, +2.302607998e-03f, +1.927675855e-02f, +2.420699082e-02f, +1.939527429e-03f, -3.364818878e-02f, -4.562711379e-02f, -1.374190145e-02f, +3.657100703e-02f, +5.615705320e-02f, +2.504972253e-02f, -2.520578478e-02f, -4.665953469e-02f, -2.544172743e-02f, +9.894771606e-03f, +2.529479915e-02f, +1.524677189e-02f, -1.253762011e-03f, -7.714347254e-03f, -4.478334337e-03f, -2.805431667e-04f, - /* 24,15 */ +6.459962873e-04f, -8.347893499e-04f, -5.414037993e-03f, -7.461005422e-03f, +1.353161175e-03f, +1.832599562e-02f, +2.465060544e-02f, +4.039135426e-03f, -3.171828840e-02f, -4.616688010e-02f, -1.675917373e-02f, +3.385706239e-02f, +5.648624601e-02f, +2.808383767e-02f, -2.250226055e-02f, -4.668156835e-02f, -2.760467194e-02f, +8.023727481e-03f, +2.519230977e-02f, +1.630487239e-02f, -4.276921651e-04f, -7.673310752e-03f, -4.794789988e-03f, -4.501246045e-04f, - /* 24, 0 */ +1.197013499e-03f, +3.320493122e-03f, -3.017233048e-03f, -9.987553340e-03f, -4.112967049e-03f, +1.524501264e-02f, +2.235677036e-02f, -2.137559158e-03f, -3.327370097e-02f, -2.660122408e-02f, +1.657531807e-02f, +4.232694754e-02f, +1.657531807e-02f, -2.660122408e-02f, -3.327370097e-02f, -2.137559158e-03f, +2.235677036e-02f, +1.524501264e-02f, -4.112967049e-03f, -9.987553340e-03f, -3.017233048e-03f, +2.318357031e-03f, +1.197013499e-03f, -1.261205705e-04f, - /* 24, 1 */ +1.060573898e-03f, +3.246029352e-03f, -2.510607281e-03f, -9.784551764e-03f, -5.018393221e-03f, +1.404948670e-02f, +2.282515537e-02f, +7.626640355e-05f, -3.208475603e-02f, -2.845760231e-02f, +1.374134711e-02f, +4.221250979e-02f, +1.933345641e-02f, -2.458052660e-02f, -3.429687591e-02f, -4.386190237e-03f, +2.174698353e-02f, +1.639120730e-02f, -3.143642175e-03f, -1.013545813e-02f, -3.535011248e-03f, +2.193303334e-03f, +1.338504197e-03f, -9.901282259e-05f, - /* 24, 2 */ +9.298712414e-04f, +3.156277727e-03f, -2.018194158e-03f, -9.530340989e-03f, -5.856606243e-03f, +1.281349999e-02f, +2.315294314e-02f, +2.243024978e-03f, -3.073934924e-02f, -3.014061672e-02f, +1.084811230e-02f, +4.186988491e-02f, +2.199957595e-02f, -2.240563854e-02f, -3.514586546e-02f, -6.656913804e-03f, +2.099588115e-02f, +1.747926153e-02f, -2.114297018e-03f, -1.022461460e-02f, -4.060636553e-03f, +2.039754046e-03f, +1.484263468e-03f, -6.526428163e-05f, - /* 24, 3 */ +8.054954021e-04f, +3.052984548e-03f, -1.542795645e-03f, -9.229007144e-03f, -6.624860539e-03f, +1.154592266e-02f, +2.334180387e-02f, +4.350977952e-03f, -2.924761047e-02f, -3.164235460e-02f, +7.912459038e-03f, +4.130113344e-02f, +2.455797710e-02f, -2.008771737e-02f, -3.581321303e-02f, -8.936640836e-03f, +2.010445982e-02f, +1.850049032e-02f, -1.029364704e-03f, -1.025164428e-02f, -4.590574200e-03f, +1.857070273e-03f, +1.633412152e-03f, -2.453170435e-05f, - /* 24, 4 */ +6.879416803e-04f, +2.937877889e-03f, -1.086944946e-03f, -8.884797195e-03f, -7.320980005e-03f, +1.025556440e-02f, +2.339424278e-02f, +6.388976156e-03f, -2.762041561e-02f, -3.295607494e-02f, +4.951401864e-03f, +4.050967459e-02f, +2.699354793e-02f, -1.763888677e-02f, -3.629248103e-02f, -1.121198570e-02f, +1.907464002e-02f, +1.944639638e-02f, +1.061806254e-04f, -1.021347789e-02f, -5.121078221e-03f, +1.644827972e-03f, +1.784975276e-03f, +2.348660763e-05f, - /* 24, 5 */ +5.776128643e-04f, +2.812656385e-03f, -6.528985547e-04f, -8.502081335e-03f, -7.943354450e-03f, +8.951116293e-03f, +2.331356438e-02f, +8.346521334e-03f, -2.586930694e-02f, -3.407624020e-02f, +1.982016505e-03f, +3.950026382e-02f, +2.929186185e-02f, -1.507216716e-02f, -3.657830513e-02f, -1.346934883e-02f, +1.790927350e-02f, +2.030873394e-02f, +1.286841938e-03f, -1.010739044e-02f, -5.648212144e-03f, +1.402832649e-03f, +1.937883690e-03f, +7.904503123e-05f, - /* 24, 6 */ +4.748218959e-04f, +2.678978820e-03f, -2.426308611e-04f, -8.085315763e-03f, -8.490932000e-03f, +7.641095022e-03f, +2.310383199e-02f, +1.021382218e-02f, -2.400641001e-02f, -3.499853994e-02f, -9.786680537e-04f, +3.827896159e-02f, +3.143927100e-02f, -1.240139974e-02f, -3.666644182e-02f, -1.569500220e-02f, +1.661214427e-02f, +2.107957227e-02f, +2.506621864e-03f, -9.931034771e-03f, -6.167872094e-03f, +1.131132707e-03f, +2.090976552e-03f, +1.423449116e-04f, - /* 24, 7 */ +3.797950945e-04f, +2.538454547e-03f, +1.421687298e-04f, -7.639006136e-03f, -8.963207645e-03f, +6.333789781e-03f, +2.276982298e-02f, +1.198184460e-02f, -2.204434780e-02f, -3.571990598e-02f, -3.913776625e-03f, +3.685309369e-02f, +3.342299483e-02f, -9.641164594e-03f, -3.655380902e-02f, -1.787517696e-02f, +1.518796320e-02f, +2.175135864e-02f, +3.759049618e-03f, -9.682473374e-03f, -6.675812165e-03f, +8.300312585e-04f, +2.243004675e-03f, +2.135289700e-04f, - /* 24, 8 */ +2.926758839e-04f, +2.392634763e-03f, +5.000962484e-04f, -7.167671961e-03f, -9.360208124e-03f, +5.037212205e-03f, +2.231697999e-02f, +1.364235598e-02f, -1.999615271e-02f, -3.623851940e-02f, -6.806693291e-03f, +3.523120326e-02f, +3.523120326e-02f, -6.806693291e-03f, -3.623851940e-02f, -1.999615271e-02f, +1.364235598e-02f, +2.231697999e-02f, +5.037212205e-03f, -9.360208124e-03f, -7.167671961e-03f, +5.000962484e-04f, +2.392634763e-03f, +2.926758839e-04f, - /* 24, 9 */ +2.135289700e-04f, +2.243004675e-03f, +8.300312585e-04f, -6.675812165e-03f, -9.682473374e-03f, +3.759049618e-03f, +2.175135864e-02f, +1.518796320e-02f, -1.787517696e-02f, -3.655380902e-02f, -9.641164594e-03f, +3.342299483e-02f, +3.685309369e-02f, -3.913776625e-03f, -3.571990598e-02f, -2.204434780e-02f, +1.198184460e-02f, +2.276982298e-02f, +6.333789781e-03f, -8.963207645e-03f, -7.639006136e-03f, +1.421687298e-04f, +2.538454547e-03f, +3.797950945e-04f, - /* 24,10 */ +1.423449116e-04f, +2.090976552e-03f, +1.131132707e-03f, -6.167872094e-03f, -9.931034771e-03f, +2.506621864e-03f, +2.107957227e-02f, +1.661214427e-02f, -1.569500220e-02f, -3.666644182e-02f, -1.240139974e-02f, +3.143927100e-02f, +3.827896159e-02f, -9.786680537e-04f, -3.499853994e-02f, -2.400641001e-02f, +1.021382218e-02f, +2.310383199e-02f, +7.641095022e-03f, -8.490932000e-03f, -8.085315763e-03f, -2.426308611e-04f, +2.678978820e-03f, +4.748218959e-04f, - /* 24,11 */ +7.904503123e-05f, +1.937883690e-03f, +1.402832649e-03f, -5.648212144e-03f, -1.010739044e-02f, +1.286841938e-03f, +2.030873394e-02f, +1.790927350e-02f, -1.346934883e-02f, -3.657830513e-02f, -1.507216716e-02f, +2.929186185e-02f, +3.950026382e-02f, +1.982016505e-03f, -3.407624020e-02f, -2.586930694e-02f, +8.346521334e-03f, +2.331356438e-02f, +8.951116293e-03f, -7.943354450e-03f, -8.502081335e-03f, -6.528985547e-04f, +2.812656385e-03f, +5.776128643e-04f, - /* 24,12 */ +2.348660763e-05f, +1.784975276e-03f, +1.644827972e-03f, -5.121078221e-03f, -1.021347789e-02f, +1.061806254e-04f, +1.944639638e-02f, +1.907464002e-02f, -1.121198570e-02f, -3.629248103e-02f, -1.763888677e-02f, +2.699354793e-02f, +4.050967459e-02f, +4.951401864e-03f, -3.295607494e-02f, -2.762041561e-02f, +6.388976156e-03f, +2.339424278e-02f, +1.025556440e-02f, -7.320980005e-03f, -8.884797195e-03f, -1.086944946e-03f, +2.937877889e-03f, +6.879416803e-04f, - /* 24,13 */ -2.453170435e-05f, +1.633412152e-03f, +1.857070273e-03f, -4.590574200e-03f, -1.025164428e-02f, -1.029364704e-03f, +1.850049032e-02f, +2.010445982e-02f, -8.936640836e-03f, -3.581321303e-02f, -2.008771737e-02f, +2.455797710e-02f, +4.130113344e-02f, +7.912459038e-03f, -3.164235460e-02f, -2.924761047e-02f, +4.350977952e-03f, +2.334180387e-02f, +1.154592266e-02f, -6.624860539e-03f, -9.229007144e-03f, -1.542795645e-03f, +3.052984548e-03f, +8.054954021e-04f, - /* 24,14 */ -6.526428163e-05f, +1.484263468e-03f, +2.039754046e-03f, -4.060636553e-03f, -1.022461460e-02f, -2.114297018e-03f, +1.747926153e-02f, +2.099588115e-02f, -6.656913804e-03f, -3.514586546e-02f, -2.240563854e-02f, +2.199957595e-02f, +4.186988491e-02f, +1.084811230e-02f, -3.014061672e-02f, -3.073934924e-02f, +2.243024978e-03f, +2.315294314e-02f, +1.281349999e-02f, -5.856606243e-03f, -9.530340989e-03f, -2.018194158e-03f, +3.156277727e-03f, +9.298712414e-04f, - /* 24,15 */ -9.901282259e-05f, +1.338504197e-03f, +2.193303334e-03f, -3.535011248e-03f, -1.013545813e-02f, -3.143642175e-03f, +1.639120730e-02f, +2.174698353e-02f, -4.386190237e-03f, -3.429687591e-02f, -2.458052660e-02f, +1.933345641e-02f, +4.221250979e-02f, +1.374134711e-02f, -2.845760231e-02f, -3.208475603e-02f, +7.626640355e-05f, +2.282515537e-02f, +1.404948670e-02f, -5.018393221e-03f, -9.784551764e-03f, -2.510607281e-03f, +3.246029352e-03f, +1.060573898e-03f, - /* 20, 0 */ +2.832126065e-03f, -3.455346407e-03f, -1.050167676e-02f, +5.399047405e-04f, +2.072547687e-02f, +1.261483344e-02f, -2.310421022e-02f, -3.076111900e-02f, +1.034529168e-02f, +3.949755319e-02f, +1.034529168e-02f, -3.076111900e-02f, -2.310421022e-02f, +1.261483344e-02f, +2.072547687e-02f, +5.399047405e-04f, -1.050167676e-02f, -3.455346407e-03f, +2.832126065e-03f, +1.002136091e-03f, - /* 20, 1 */ +2.918309836e-03f, -2.848965042e-03f, -1.044663371e-02f, -7.454467031e-04f, +1.993701966e-02f, +1.431336010e-02f, -2.105256806e-02f, -3.196996361e-02f, +7.274030562e-03f, +3.936378623e-02f, +1.336427867e-02f, -2.932648708e-02f, -2.503260290e-02f, +1.078376120e-02f, +2.138841986e-02f, +1.874755806e-03f, -1.047502359e-02f, -4.071193337e-03f, +2.709872705e-03f, +1.184613130e-03f, - /* 20, 2 */ +2.970014434e-03f, -2.256846905e-03f, -1.031411254e-02f, -1.973175306e-03f, +1.903277841e-02f, +1.586999913e-02f, -1.889465735e-02f, -3.294695719e-02f, +4.172714360e-03f, +3.896348732e-02f, +1.630904655e-02f, -2.767391419e-02f, -2.682143551e-02f, +8.830742245e-03f, +2.191685631e-02f, +3.250271284e-03f, -1.036300090e-02f, -4.691364292e-03f, +2.550244709e-03f, +1.728121332e-03f, - /* 20, 3 */ +2.989084466e-03f, -1.683415968e-03f, -1.010881741e-02f, -3.135916081e-03f, +1.802312126e-02f, +1.727671449e-02f, -1.664798407e-02f, -3.368784795e-02f, +1.063660935e-03f, +3.829965427e-02f, +1.915809828e-02f, -2.581298498e-02f, -2.845520951e-02f, +6.767571398e-03f, +2.230262774e-02f, +4.656958119e-03f, -1.016253578e-02f, -5.310408414e-03f, +2.352251997e-03f, +1.906494808e-03f, - /* 20, 4 */ +2.977586348e-03f, -1.132697564e-03f, -9.835877420e-03f, -4.227100403e-03f, +1.691895133e-02f, +1.852681335e-02f, -1.433043179e-02f, -3.419021020e-02f, -2.030888217e-03f, +3.737725626e-02f, +2.189055571e-02f, -2.375496340e-02f, -2.991937541e-02f, +4.607167163e-03f, +2.253850054e-02f, +6.084727180e-03f, -9.871207756e-03f, -5.922604667e-03f, +2.115247068e-03f, +2.079939639e-03f, - /* 20, 5 */ +2.937775120e-03f, -6.082983663e-04f, -9.500783787e-03f, -5.240985904e-03f, +1.573160178e-02f, +1.961497125e-02f, -1.196011335e-02f, -3.445344345e-02f, -5.088941581e-03f, +3.620319350e-02f, +2.448632622e-02f, -2.151271924e-02f, -3.120046374e-02f, +2.363488482e-03f, +2.261825107e-02f, +7.522962281e-03f, -9.487296369e-03f, -6.522005316e-03f, +1.838950241e-03f, +2.245949018e-03f, - /* 20, 6 */ +2.872060854e-03f, -1.133913219e-04f, -9.109325922e-03f, -6.172678101e-03f, +1.447272980e-02f, +2.053724533e-02f, -9.555222824e-03f, -3.447875653e-02f, -8.088928223e-03f, +3.478624106e-02f, +2.692626358e-02f, -1.910064083e-02f, -3.228620876e-02f, +5.144107936e-05f, +2.253674404e-02f, +8.960596019e-03f, -9.009823935e-03f, -7.102483479e-03f, +1.523472156e-03f, +2.401928729e-03f, - /* 20, 7 */ +2.782975009e-03f, +3.492945151e-04f, -8.667527067e-03f, -7.018143782e-03f, +1.315421060e-02f, +2.129107545e-02f, -7.133889173e-03f, -3.426913715e-02f, -1.100986395e-02f, +3.313697764e-02f, +2.919232167e-02f, -1.653453452e-02f, -3.316566379e-02f, -2.313225982e-03f, +2.229000326e-02f, +1.038619190e-02f, -8.438592747e-03f, -7.657784411e-03f, +1.169333173e-03f, +2.545222121e-03f, - /* 20, 8 */ +2.673137115e-03f, +7.774793244e-04f, -8.181580147e-03f, -7.774216267e-03f, +1.178803215e-02f, +2.187527386e-02f, -4.714032773e-03f, -3.382930709e-02f, -1.383151166e-02f, +3.126769968e-02f, +3.126769968e-02f, -1.383151166e-02f, -3.382930709e-02f, -4.714032773e-03f, +2.187527386e-02f, +1.178803215e-02f, -7.774216267e-03f, -8.181580147e-03f, +7.774793244e-04f, +2.673137115e-03f, - /* 20, 9 */ +2.545222121e-03f, +1.169333173e-03f, -7.657784411e-03f, -8.438592747e-03f, +1.038619190e-02f, +2.229000326e-02f, -2.313225982e-03f, -3.316566379e-02f, -1.653453452e-02f, +2.919232167e-02f, +3.313697764e-02f, -1.100986395e-02f, -3.426913715e-02f, -7.133889173e-03f, +2.129107545e-02f, +1.315421060e-02f, -7.018143782e-03f, -8.667527067e-03f, +3.492945151e-04f, +2.782975009e-03f, - /* 20,10 */ +2.401928729e-03f, +1.523472156e-03f, -7.102483479e-03f, -9.009823935e-03f, +8.960596019e-03f, +2.253674404e-02f, +5.144107936e-05f, -3.228620876e-02f, -1.910064083e-02f, +2.692626358e-02f, +3.478624106e-02f, -8.088928223e-03f, -3.447875653e-02f, -9.555222824e-03f, +2.053724533e-02f, +1.447272980e-02f, -6.172678101e-03f, -9.109325922e-03f, -1.133913219e-04f, +2.872060854e-03f, - /* 20,11 */ +2.245949018e-03f, +1.838950241e-03f, -6.522005316e-03f, -9.487296369e-03f, +7.522962281e-03f, +2.261825107e-02f, +2.363488482e-03f, -3.120046374e-02f, -2.151271924e-02f, +2.448632622e-02f, +3.620319350e-02f, -5.088941581e-03f, -3.445344345e-02f, -1.196011335e-02f, +1.961497125e-02f, +1.573160178e-02f, -5.240985904e-03f, -9.500783787e-03f, -6.082983663e-04f, +2.937775120e-03f, - /* 20,12 */ +2.079939639e-03f, +2.115247068e-03f, -5.922604667e-03f, -9.871207756e-03f, +6.084727180e-03f, +2.253850054e-02f, +4.607167163e-03f, -2.991937541e-02f, -2.375496340e-02f, +2.189055571e-02f, +3.737725626e-02f, -2.030888217e-03f, -3.419021020e-02f, -1.433043179e-02f, +1.852681335e-02f, +1.691895133e-02f, -4.227100403e-03f, -9.835877420e-03f, -1.132697564e-03f, +2.977586348e-03f, - /* 20,13 */ +1.906494808e-03f, +2.352251997e-03f, -5.310408414e-03f, -1.016253578e-02f, +4.656958119e-03f, +2.230262774e-02f, +6.767571398e-03f, -2.845520951e-02f, -2.581298498e-02f, +1.915809828e-02f, +3.829965427e-02f, +1.063660935e-03f, -3.368784795e-02f, -1.664798407e-02f, +1.727671449e-02f, +1.802312126e-02f, -3.135916081e-03f, -1.010881741e-02f, -1.683415968e-03f, +2.989084466e-03f, - /* 20,14 */ +1.728121332e-03f, +2.550244709e-03f, -4.691364292e-03f, -1.036300090e-02f, +3.250271284e-03f, +2.191685631e-02f, +8.830742245e-03f, -2.682143551e-02f, -2.767391419e-02f, +1.630904655e-02f, +3.896348732e-02f, +4.172714360e-03f, -3.294695719e-02f, -1.889465735e-02f, +1.586999913e-02f, +1.903277841e-02f, -1.973175306e-03f, -1.031411254e-02f, -2.256846905e-03f, +2.970014434e-03f, - /* 20,15 */ +1.184613130e-03f, +2.709872705e-03f, -4.071193337e-03f, -1.047502359e-02f, +1.874755806e-03f, +2.138841986e-02f, +1.078376120e-02f, -2.503260290e-02f, -2.932648708e-02f, +1.336427867e-02f, +3.936378623e-02f, +7.274030562e-03f, -3.196996361e-02f, -2.105256806e-02f, +1.431336010e-02f, +1.993701966e-02f, -7.454467031e-04f, -1.044663371e-02f, -2.848965042e-03f, +2.918309836e-03f, - /* 20, 0 */ +1.702864838e-03f, +2.314577966e-03f, -6.534433696e-03f, -8.774562126e-03f, +1.199271213e-02f, +2.083400312e-02f, -1.263546899e-02f, -3.379691899e-02f, +5.498355442e-03f, +3.949755319e-02f, +5.498355442e-03f, -3.379691899e-02f, -1.263546899e-02f, +2.083400312e-02f, +1.199271213e-02f, -8.774562126e-03f, -6.534433696e-03f, +2.314577966e-03f, +1.702864838e-03f, +0.000000000e+00f, - /* 20, 1 */ +1.499435496e-03f, +2.547675666e-03f, -5.868098774e-03f, -9.353385898e-03f, +1.044920601e-02f, +2.160032962e-02f, -9.997584470e-03f, -3.429852636e-02f, +2.083565903e-03f, +3.933622696e-02f, +8.892197588e-03f, -3.300722623e-02f, -1.522519578e-02f, +1.986341365e-02f, +1.349096787e-02f, -8.082206357e-03f, -7.177938171e-03f, +2.033576095e-03f, +1.903844954e-03f, +0.000000000e+00f, - /* 20, 2 */ +8.979094201e-04f, +2.733567376e-03f, -5.187117330e-03f, -9.818374279e-03f, +8.876306372e-03f, +2.216139438e-02f, -7.335624654e-03f, -3.451169090e-02f, -1.322648265e-03f, +3.885370480e-02f, +1.223556951e-02f, -3.193245877e-02f, -1.774265256e-02f, +1.869155385e-02f, +1.492800767e-02f, -7.277832099e-03f, -7.790241855e-03f, +1.704529263e-03f, +2.099160368e-03f, -3.513221827e-04f, - /* 20, 3 */ +7.046452899e-04f, +2.873469547e-03f, -4.499404245e-03f, -1.017038969e-02f, +7.289604703e-03f, +2.251815219e-02f, -4.673411391e-03f, -3.443867914e-02f, -4.691042688e-03f, +3.805434209e-02f, +1.549922824e-02f, -3.057828381e-02f, -2.016393226e-02f, +1.732343066e-02f, +1.628791973e-02f, -6.364195274e-03f, -8.362876507e-03f, +1.327887989e-03f, +2.285430476e-03f, -3.288882594e-04f, - /* 20, 4 */ +5.275063595e-04f, +2.969073324e-03f, -3.812529364e-03f, -1.041140495e-02f, +5.704278009e-03f, +2.267345221e-02f, -2.034281900e-03f, -3.408432658e-02f, -7.992925296e-03f, +3.694535030e-02f, +1.865448932e-02f, -2.895300188e-02f, -2.246557005e-02f, +1.576606531e-02f, +1.755501285e-02f, -5.345313722e-03f, -8.887369558e-03f, +9.047221591e-04f, +2.459146683e-03f, -2.941732022e-04f, - /* 20, 5 */ +3.670219514e-04f, +3.022497230e-03f, -3.133648777e-03f, -1.054443774e-02f, +4.134948499e-03f, +2.263196075e-02f, +5.591264205e-04f, -3.345595810e-02f, -1.120042307e-02f, +3.553672638e-02f, +2.167350137e-02f, -2.706749712e-02f, -2.462477986e-02f, +1.402847243e-02f, +1.871398575e-02f, -4.226473266e-03f, -9.355341791e-03f, +4.367425848e-04f, +2.616713456e-03f, -2.461206998e-04f, - /* 20, 6 */ +2.234691091e-04f, +3.036236900e-03f, -2.469443903e-03f, -1.057347601e-02f, +2.595553432e-03f, +2.240006745e-02f, +3.085080219e-03f, -3.256328476e-02f, -1.428673893e-02f, +3.384115481e-02f, +2.452951370e-02f, -2.493516141e-02f, -2.661968788e-02f, +1.212161795e-02f, +1.975009719e-02f, -3.014219819e-03f, -9.758608118e-03f, -7.368451392e-05f, +2.754492916e-03f, -1.837671888e-04f, - /* 20, 7 */ +9.688872179e-05f, +3.013112613e-03f, -1.826068782e-03f, -1.050339555e-02f, +1.099226216e-03f, +2.198577609e-02f, +5.522941542e-03f, -3.141827834e-02f, -1.722639627e-02f, +3.187388359e-02f, +2.719713425e-02f, -2.257179274e-02f, -2.842956063e-02f, +1.005835593e-02f, +2.064933490e-02f, -1.716337171e-03f, -1.008928035e-02f, -6.235305950e-04f, +2.868852533e-03f, -1.062635081e-04f, - /* 20, 8 */ -1.289664191e-05f, +2.956215411e-03f, -1.209105881e-03f, -1.033987080e-02f, -3.418102460e-04f, +2.139858161e-02f, +7.853344535e-03f, -3.003502508e-02f, -1.999546871e-02f, +2.965257519e-02f, +2.965257519e-02f, -1.999546871e-02f, -3.003502508e-02f, +7.853344535e-03f, +2.139858161e-02f, -3.418102460e-04f, -1.033987080e-02f, -1.209105881e-03f, +2.956215411e-03f, -1.289664191e-05f, - /* 20, 9 */ -1.062635081e-04f, +2.868852533e-03f, -6.235305950e-04f, -1.008928035e-02f, -1.716337171e-03f, +2.064933490e-02f, +1.005835593e-02f, -2.842956063e-02f, -2.257179274e-02f, +2.719713425e-02f, +3.187388359e-02f, -1.722639627e-02f, -3.141827834e-02f, +5.522941542e-03f, +2.198577609e-02f, +1.099226216e-03f, -1.050339555e-02f, -1.826068782e-03f, +3.013112613e-03f, +9.688872179e-05f, - /* 20,10 */ -1.837671888e-04f, +2.754492916e-03f, -7.368451392e-05f, -9.758608118e-03f, -3.014219819e-03f, +1.975009719e-02f, +1.212161795e-02f, -2.661968788e-02f, -2.493516141e-02f, +2.452951370e-02f, +3.384115481e-02f, -1.428673893e-02f, -3.256328476e-02f, +3.085080219e-03f, +2.240006745e-02f, +2.595553432e-03f, -1.057347601e-02f, -2.469443903e-03f, +3.036236900e-03f, +2.234691091e-04f, - /* 20,11 */ -2.461206998e-04f, +2.616713456e-03f, +4.367425848e-04f, -9.355341791e-03f, -4.226473266e-03f, +1.871398575e-02f, +1.402847243e-02f, -2.462477986e-02f, -2.706749712e-02f, +2.167350137e-02f, +3.553672638e-02f, -1.120042307e-02f, -3.345595810e-02f, +5.591264205e-04f, +2.263196075e-02f, +4.134948499e-03f, -1.054443774e-02f, -3.133648777e-03f, +3.022497230e-03f, +3.670219514e-04f, - /* 20,12 */ -2.941732022e-04f, +2.459146683e-03f, +9.047221591e-04f, -8.887369558e-03f, -5.345313722e-03f, +1.755501285e-02f, +1.576606531e-02f, -2.246557005e-02f, -2.895300188e-02f, +1.865448932e-02f, +3.694535030e-02f, -7.992925296e-03f, -3.408432658e-02f, -2.034281900e-03f, +2.267345221e-02f, +5.704278009e-03f, -1.041140495e-02f, -3.812529364e-03f, +2.969073324e-03f, +5.275063595e-04f, - /* 20,13 */ -3.288882594e-04f, +2.285430476e-03f, +1.327887989e-03f, -8.362876507e-03f, -6.364195274e-03f, +1.628791973e-02f, +1.732343066e-02f, -2.016393226e-02f, -3.057828381e-02f, +1.549922824e-02f, +3.805434209e-02f, -4.691042688e-03f, -3.443867914e-02f, -4.673411391e-03f, +2.251815219e-02f, +7.289604703e-03f, -1.017038969e-02f, -4.499404245e-03f, +2.873469547e-03f, +7.046452899e-04f, - /* 20,14 */ -3.513221827e-04f, +2.099160368e-03f, +1.704529263e-03f, -7.790241855e-03f, -7.277832099e-03f, +1.492800767e-02f, +1.869155385e-02f, -1.774265256e-02f, -3.193245877e-02f, +1.223556951e-02f, +3.885370480e-02f, -1.322648265e-03f, -3.451169090e-02f, -7.335624654e-03f, +2.216139438e-02f, +8.876306372e-03f, -9.818374279e-03f, -5.187117330e-03f, +2.733567376e-03f, +8.979094201e-04f, - /* 20,15 */ +0.000000000e+00f, +1.903844954e-03f, +2.033576095e-03f, -7.177938171e-03f, -8.082206357e-03f, +1.349096787e-02f, +1.986341365e-02f, -1.522519578e-02f, -3.300722623e-02f, +8.892197588e-03f, +3.933622696e-02f, +2.083565903e-03f, -3.429852636e-02f, -9.997584470e-03f, +2.160032962e-02f, +1.044920601e-02f, -9.353385898e-03f, -5.868098774e-03f, +2.547675666e-03f, +1.499435496e-03f, - /* 20, 0 */ -3.735125865e-04f, +2.550984103e-03f, -2.725752467e-05f, -1.024966855e-02f, +8.379667777e-04f, +2.252874535e-02f, -1.249359695e-03f, -3.448318510e-02f, +6.071698875e-04f, +3.949755319e-02f, +6.071698875e-04f, -3.448318510e-02f, -1.249359695e-03f, +2.252874535e-02f, +8.379667777e-04f, -1.024966855e-02f, -2.725752467e-05f, +2.565652055e-03f, -3.735125865e-04f, +0.000000000e+00f, - /* 20, 1 */ -3.929324583e-04f, +2.236335973e-03f, +5.288730096e-04f, -9.916240655e-03f, -7.235223044e-04f, +2.212021870e-02f, +1.557339330e-03f, -3.412515163e-02f, -3.088657053e-03f, +3.930609269e-02f, +4.328121901e-03f, -3.450613681e-02f, -4.117983282e-03f, +2.271744325e-02f, +2.467540325e-03f, -1.048404473e-02f, -6.307983207e-04f, +2.729031078e-03f, -3.387491377e-04f, +0.000000000e+00f, - /* 20, 2 */ +0.000000000e+00f, +1.931175707e-03f, +1.033703668e-03f, -9.493276252e-03f, -2.202626937e-03f, +2.150371837e-02f, +4.274418757e-03f, -3.344119198e-02f, -6.721842627e-03f, +3.873376177e-02f, +8.036125742e-03f, -3.418837690e-02f, -7.019484999e-03f, +2.267661502e-02f, +4.149462009e-03f, -1.061062992e-02f, -1.276919763e-03f, +2.866023275e-03f, -2.870815261e-04f, +0.000000000e+00f, - /* 20, 3 */ +0.000000000e+00f, +1.638662038e-03f, +1.484286050e-03f, -8.990907936e-03f, -3.586602863e-03f, +2.069305390e-02f, +6.875821908e-03f, -3.244383298e-02f, -1.025584288e-02f, +3.778668841e-02f, +1.169297592e-02f, -3.352791602e-02f, -9.923776326e-03f, +2.239890298e-02f, +5.866701604e-03f, -1.062161571e-02f, -1.959867511e-03f, +2.971909246e-03f, -2.170808154e-04f, +0.000000000e+00f, - /* 20, 4 */ +0.000000000e+00f, +1.361489293e-03f, +1.878600735e-03f, -8.419725702e-03f, -4.864357078e-03f, +1.970376789e-02f, +9.337391966e-03f, -3.114878076e-02f, -1.365548733e-02f, +3.647500669e-02f, +1.526076366e-02f, -3.252647846e-02f, -1.280005487e-02f, +2.187944695e-02f, +7.601099328e-03f, -1.051027343e-02f, -2.672992279e-03f, +3.042103091e-03f, -1.274866066e-04f, +0.000000000e+00f, - /* 20, 5 */ +0.000000000e+00f, +1.101888322e-03f, +2.215532402e-03f, -7.790617836e-03f, -6.026519023e-03f, +1.855289977e-02f, +1.163710530e-02f, -2.957469445e-02f, -1.688736106e-02f, +3.481273909e-02f, +1.870230489e-02f, -3.118953221e-02f, -1.561714772e-02f, +2.111601531e-02f, +9.333550613e-03f, -1.027109466e-02f, -3.408794343e-03f, +3.072235528e-03f, -1.724424543e-05f, +0.000000000e+00f, - /* 20, 6 */ +0.000000000e+00f, +8.616336525e-04f, +2.494833248e-03f, -7.114614810e-03f, -7.065487327e-03f, +1.725873795e-02f, +1.375527431e-02f, -2.774292839e-02f, -1.992016339e-02f, +3.281763375e-02f, +2.198156213e-02f, -2.952627516e-02f, -1.834386597e-02f, +2.010910684e-02f, +1.104420948e-02f, -9.899921148e-03f, -4.158982805e-03f, +3.058238443e-03f, +1.144582079e-04f, +0.000000000e+00f, - /* 20, 7 */ +0.000000000e+00f, +6.420563626e-04f, +2.717075783e-03f, -6.402738187e-03f, -7.975452307e-03f, +1.584056403e-02f, +1.567471786e-02f, -2.567724669e-02f, -2.272503888e-02f, +3.051095862e-02f, +2.506405514e-02f, -2.754957697e-02f, -2.094936609e-02f, +1.886202143e-02f, +1.271270843e-02f, -9.394061811e-03f, -4.914549404e-03f, +2.996429606e-03f, +2.681538305e-04f, +0.000000000e+00f, - /* 20, 8 */ +0.000000000e+00f, +4.440621234e-04f, +2.883596201e-03f, -5.665856445e-03f, -8.752394750e-03f, +1.431839236e-02f, +1.738089791e-02f, -2.340351394e-02f, -2.527587699e-02f, +2.791725525e-02f, +2.791725525e-02f, -2.527587699e-02f, -2.340351394e-02f, +1.738089791e-02f, +1.431839236e-02f, -8.752394750e-03f, -5.665856445e-03f, +2.883596201e-03f, +4.440621234e-04f, +0.000000000e+00f, - /* 20, 9 */ +0.000000000e+00f, +2.681538305e-04f, +2.996429606e-03f, -4.914549404e-03f, -9.394061811e-03f, +1.271270843e-02f, +1.886202143e-02f, -2.094936609e-02f, -2.754957697e-02f, +2.506405514e-02f, +3.051095862e-02f, -2.272503888e-02f, -2.567724669e-02f, +1.567471786e-02f, +1.584056403e-02f, -7.975452307e-03f, -6.402738187e-03f, +2.717075783e-03f, +6.420563626e-04f, +0.000000000e+00f, - /* 20,10 */ +0.000000000e+00f, +1.144582079e-04f, +3.058238443e-03f, -4.158982805e-03f, -9.899921148e-03f, +1.104420948e-02f, +2.010910684e-02f, -1.834386597e-02f, -2.952627516e-02f, +2.198156213e-02f, +3.281763375e-02f, -1.992016339e-02f, -2.774292839e-02f, +1.375527431e-02f, +1.725873795e-02f, -7.065487327e-03f, -7.114614810e-03f, +2.494833248e-03f, +8.616336525e-04f, +0.000000000e+00f, - /* 20,11 */ +0.000000000e+00f, -1.724424543e-05f, +3.072235528e-03f, -3.408794343e-03f, -1.027109466e-02f, +9.333550613e-03f, +2.111601531e-02f, -1.561714772e-02f, -3.118953221e-02f, +1.870230489e-02f, +3.481273909e-02f, -1.688736106e-02f, -2.957469445e-02f, +1.163710530e-02f, +1.855289977e-02f, -6.026519023e-03f, -7.790617836e-03f, +2.215532402e-03f, +1.101888322e-03f, +0.000000000e+00f, - /* 20,12 */ +0.000000000e+00f, -1.274866066e-04f, +3.042103091e-03f, -2.672992279e-03f, -1.051027343e-02f, +7.601099328e-03f, +2.187944695e-02f, -1.280005487e-02f, -3.252647846e-02f, +1.526076366e-02f, +3.647500669e-02f, -1.365548733e-02f, -3.114878076e-02f, +9.337391966e-03f, +1.970376789e-02f, -4.864357078e-03f, -8.419725702e-03f, +1.878600735e-03f, +1.361489293e-03f, +0.000000000e+00f, - /* 20,13 */ +0.000000000e+00f, -2.170808154e-04f, +2.971909246e-03f, -1.959867511e-03f, -1.062161571e-02f, +5.866701604e-03f, +2.239890298e-02f, -9.923776326e-03f, -3.352791602e-02f, +1.169297592e-02f, +3.778668841e-02f, -1.025584288e-02f, -3.244383298e-02f, +6.875821908e-03f, +2.069305390e-02f, -3.586602863e-03f, -8.990907936e-03f, +1.484286050e-03f, +1.638662038e-03f, +0.000000000e+00f, - /* 20,14 */ +0.000000000e+00f, -2.870815261e-04f, +2.866023275e-03f, -1.276919763e-03f, -1.061062992e-02f, +4.149462009e-03f, +2.267661502e-02f, -7.019484999e-03f, -3.418837690e-02f, +8.036125742e-03f, +3.873376177e-02f, -6.721842627e-03f, -3.344119198e-02f, +4.274418757e-03f, +2.150371837e-02f, -2.202626937e-03f, -9.493276252e-03f, +1.033703668e-03f, +1.931175707e-03f, +0.000000000e+00f, - /* 20,15 */ +0.000000000e+00f, -3.387491377e-04f, +2.729031078e-03f, -6.307983207e-04f, -1.048404473e-02f, +2.467540325e-03f, +2.271744325e-02f, -4.117983282e-03f, -3.450613681e-02f, +4.328121901e-03f, +3.930609269e-02f, -3.088657053e-03f, -3.412515163e-02f, +1.557339330e-03f, +2.212021870e-02f, -7.235223044e-04f, -9.916240655e-03f, +5.288730096e-04f, +2.236335973e-03f, -3.929324583e-04f, - /* 16, 0 */ +3.044394378e-03f, -5.755865016e-03f, -7.644875237e-03f, +1.825808857e-02f, +9.222448502e-03f, -3.286388427e-02f, -4.241838036e-03f, +3.949755319e-02f, -4.241838036e-03f, -3.286388427e-02f, +9.222448502e-03f, +1.825808857e-02f, -7.644875237e-03f, -5.755865016e-03f, +3.044394378e-03f, -1.466795211e-05f, - /* 16, 1 */ +3.096598285e-03f, -4.938670705e-03f, -8.543525001e-03f, +1.681174815e-02f, +1.171692718e-02f, -3.156650717e-02f, -8.140229219e-03f, +3.927338559e-02f, -2.566011090e-04f, -3.380519836e-02f, +6.539747546e-03f, +1.954192550e-02f, -6.591652894e-03f, -6.554797296e-03f, +2.932700428e-03f, +1.424716020e-04f, - /* 16, 2 */ +3.093580763e-03f, -4.116215273e-03f, -9.283856280e-03f, +1.522768985e-02f, +1.399813452e-02f, -2.993548791e-02f, -1.190603152e-02f, +3.860369285e-02f, +3.768233493e-03f, -3.437220120e-02f, +3.697060454e-03f, +2.063975168e-02f, -5.390052618e-03f, -7.321916780e-03f, +2.757987679e-03f, +3.278051009e-04f, - /* 16, 3 */ +3.040228116e-03f, -3.300778044e-03f, -9.864516741e-03f, +1.353158972e-02f, +1.604446323e-02f, -2.799705310e-02f, -1.549559239e-02f, +3.749686691e-02f, +7.784523662e-03f, -3.455113173e-02f, +7.255039591e-04f, +2.152972014e-02f, -4.048737024e-03f, -8.043312786e-03f, +2.517583336e-03f, +5.415628140e-04f, - /* 16, 4 */ +2.941918573e-03f, -2.503765206e-03f, -1.028645874e-02f, +1.174962597e-02f, +1.783794825e-02f, -2.578082191e-02f, -1.886790220e-02f, +3.596676747e-02f, +1.174386090e-02f, -3.433297304e-02f, -2.341279491e-03f, +2.219201396e-02f, -2.578818314e-03f, -8.704920467e-03f, +2.209778110e-03f, +1.246855370e-03f, - /* 16, 5 */ +2.804395319e-03f, -1.735580001e-03f, -1.055281940e-02f, +9.908096520e-03f, +1.936441115e-02f, -2.331935318e-02f, -2.198510572e-02f, +3.403253365e-02f, +1.559820593e-02f, -3.371364718e-02f, -5.467520855e-03f, +2.260919785e-02f, -9.938011251e-04f, -9.292739628e-03f, +1.833922789e-03f, +1.492594630e-03f, - /* 16, 6 */ +2.633640678e-03f, -1.005515791e-03f, -1.066877263e-02f, +8.033047542e-03f, +2.061354789e-02f, -2.064765983e-02f, -2.481296600e-02f, +3.171832409e-02f, +1.930052332e-02f, -3.269414425e-02f, -8.615762914e-03f, +2.276654580e-02f, +6.905139302e-04f, -9.793063512e-03f, +1.390511455e-03f, +1.739628231e-03f, - /* 16, 7 */ +2.435753603e-03f, -3.216727033e-04f, -1.064135670e-02f, +6.149918447e-03f, +2.157895980e-02f, -1.780269814e-02f, -2.732127429e-02f, +2.905298940e-02f, +2.280540611e-02f, -3.128058296e-02f, -1.174733238e-02f, +2.265233903e-02f, +2.456165958e-03f, -1.019271416e-02f, +8.812491435e-04f, +1.982865330e-03f, - /* 16, 8 */ +2.216832517e-03f, +3.091018830e-04f, -1.047928070e-02f, +4.283208005e-03f, +2.225812888e-02f, -1.482283947e-02f, -2.948420097e-02f, +2.606968157e-02f, +2.606968157e-02f, -2.948420097e-02f, -1.482283947e-02f, +2.225812888e-02f, +4.283208005e-03f, -1.047928070e-02f, +3.091018830e-04f, +2.216832517e-03f, - /* 16, 9 */ +1.982865330e-03f, +8.812491435e-04f, -1.019271416e-02f, +2.456165958e-03f, +2.265233903e-02f, -1.174733238e-02f, -3.128058296e-02f, +2.280540611e-02f, +2.905298940e-02f, -2.732127429e-02f, -1.780269814e-02f, +2.157895980e-02f, +6.149918447e-03f, -1.064135670e-02f, -3.216727033e-04f, +2.435753603e-03f, - /* 16,10 */ +1.739628231e-03f, +1.390511455e-03f, -9.793063512e-03f, +6.905139302e-04f, +2.276654580e-02f, -8.615762914e-03f, -3.269414425e-02f, +1.930052332e-02f, +3.171832409e-02f, -2.481296600e-02f, -2.064765983e-02f, +2.061354789e-02f, +8.033047542e-03f, -1.066877263e-02f, -1.005515791e-03f, +2.633640678e-03f, - /* 16,11 */ +1.492594630e-03f, +1.833922789e-03f, -9.292739628e-03f, -9.938011251e-04f, +2.260919785e-02f, -5.467520855e-03f, -3.371364718e-02f, +1.559820593e-02f, +3.403253365e-02f, -2.198510572e-02f, -2.331935318e-02f, +1.936441115e-02f, +9.908096520e-03f, -1.055281940e-02f, -1.735580001e-03f, +2.804395319e-03f, - /* 16,12 */ +1.246855370e-03f, +2.209778110e-03f, -8.704920467e-03f, -2.578818314e-03f, +2.219201396e-02f, -2.341279491e-03f, -3.433297304e-02f, +1.174386090e-02f, +3.596676747e-02f, -1.886790220e-02f, -2.578082191e-02f, +1.783794825e-02f, +1.174962597e-02f, -1.028645874e-02f, -2.503765206e-03f, +2.941918573e-03f, - /* 16,13 */ +5.415628140e-04f, +2.517583336e-03f, -8.043312786e-03f, -4.048737024e-03f, +2.152972014e-02f, +7.255039591e-04f, -3.455113173e-02f, +7.784523662e-03f, +3.749686691e-02f, -1.549559239e-02f, -2.799705310e-02f, +1.604446323e-02f, +1.353158972e-02f, -9.864516741e-03f, -3.300778044e-03f, +3.040228116e-03f, - /* 16,14 */ +3.278051009e-04f, +2.757987679e-03f, -7.321916780e-03f, -5.390052618e-03f, +2.063975168e-02f, +3.697060454e-03f, -3.437220120e-02f, +3.768233493e-03f, +3.860369285e-02f, -1.190603152e-02f, -2.993548791e-02f, +1.399813452e-02f, +1.522768985e-02f, -9.283856280e-03f, -4.116215273e-03f, +3.093580763e-03f, - /* 16,15 */ +1.424716020e-04f, +2.932700428e-03f, -6.554797296e-03f, -6.591652894e-03f, +1.954192550e-02f, +6.539747546e-03f, -3.380519836e-02f, -2.566011090e-04f, +3.927338559e-02f, -8.140229219e-03f, -3.156650717e-02f, +1.171692718e-02f, +1.681174815e-02f, -8.543525001e-03f, -4.938670705e-03f, +3.096598285e-03f, - /* 16, 0 */ +2.106112688e-03f, -1.136549770e-04f, -1.070669430e-02f, +1.017472059e-02f, +1.725397418e-02f, -2.914959442e-02f, -8.963852042e-03f, +3.949755319e-02f, -8.963852042e-03f, -2.914959442e-02f, +1.725397418e-02f, +1.017472059e-02f, -1.070669430e-02f, -1.136549770e-04f, +2.106112688e-03f, +0.000000000e+00f, - /* 16, 1 */ +1.845338280e-03f, +5.473343456e-04f, -1.065891816e-02f, +8.155641030e-03f, +1.899089579e-02f, -2.691951328e-02f, -1.297236480e-02f, +3.923810803e-02f, -4.790894575e-03f, -3.103786392e-02f, +1.521305380e-02f, +1.215062389e-02f, -1.058864907e-02f, -8.385121370e-04f, +2.352913572e-03f, +0.000000000e+00f, - /* 16, 2 */ +1.577651889e-03f, +1.138027416e-03f, -1.045641117e-02f, +6.125232216e-03f, +2.040939526e-02f, -2.438627738e-02f, -1.676283724e-02f, +3.846353557e-02f, -5.100475811e-04f, -3.254996068e-02f, +1.288771825e-02f, +1.405076114e-02f, -1.029592106e-02f, -1.619065127e-03f, +2.578329862e-03f, +0.000000000e+00f, - /* 16, 3 */ +1.309641631e-03f, +1.653747621e-03f, -1.011218665e-02f, +4.114112388e-03f, +2.150028131e-02f, -2.159216402e-02f, -2.028541539e-02f, +3.718506562e-02f, +3.820010384e-03f, -3.365643786e-02f, +1.030255138e-02f, +1.584231759e-02f, -9.822158049e-03f, -2.445442331e-03f, +2.774741598e-03f, +0.000000000e+00f, - /* 16, 4 */ +5.462770218e-04f, +2.091544281e-03f, -9.640854940e-03f, +2.151223968e-03f, +2.225957955e-02f, -1.858235038e-02f, -2.349470263e-02f, +3.542121816e-02f, +8.139354376e-03f, -3.433331277e-02f, +7.486889709e-03f, +1.749279467e-02f, -9.163764446e-03f, -3.306153325e-03f, +2.934475195e-03f, -4.634120047e-04f, - /* 16, 5 */ +3.039101756e-04f, +2.450135010e-03f, -9.058284956e-03f, +2.634319572e-04f, +2.268843286e-02f, -1.540416082e-02f, -2.635039795e-02f, +3.319751225e-02f, +1.238771678e-02f, -3.456253827e-02f, +4.474489227e-03f, +1.897056596e-02f, -8.320109905e-03f, -4.188206830e-03f, +3.049970761e-03f, -4.400286560e-04f, - /* 16, 6 */ +9.722433303e-05f, +2.729819110e-03f, -8.381259809e-03f, -1.524826854e-03f, +2.279292110e-02f, -1.210629477e-02f, -2.881784707e-02f, +3.054606534e-02f, +1.650540309e-02f, -3.433238619e-02f, +1.303110212e-03f, +2.024543829e-02f, -7.293693549e-03f, -5.077265419e-03f, +3.113958519e-03f, -3.921992729e-04f, - /* 16, 7 */ -7.427658644e-05f, +2.932365266e-03f, -7.627133157e-03f, -3.191839567e-03f, +2.258380561e-02f, -8.738048497e-03f, -3.086849848e-02f, +2.750508980e-02f, +2.043420490e-02f, -3.363773532e-02f, -1.985974837e-03f, +2.128920837e-02f, -6.090258802e-03f, -5.957835775e-03f, +3.119640969e-03f, -3.169896142e-04f, - /* 16, 8 */ -2.117631665e-04f, +3.060877176e-03f, -6.813492559e-03f, -4.718854594e-03f, +2.207620481e-02f, -5.348543574e-03f, -3.248025769e-02f, +2.411829496e-02f, +2.411829496e-02f, -3.248025769e-02f, -5.348543574e-03f, +2.207620481e-02f, -4.718854594e-03f, -6.813492559e-03f, +3.060877176e-03f, -2.117631665e-04f, - /* 16, 9 */ -3.169896142e-04f, +3.119640969e-03f, -5.957835775e-03f, -6.090258802e-03f, +2.128920837e-02f, -1.985974837e-03f, -3.363773532e-02f, +2.043420490e-02f, +2.750508980e-02f, -3.086849848e-02f, -8.738048497e-03f, +2.258380561e-02f, -3.191839567e-03f, -7.627133157e-03f, +2.932365266e-03f, -7.427658644e-05f, - /* 16,10 */ -3.921992729e-04f, +3.113958519e-03f, -5.077265419e-03f, -7.293693549e-03f, +2.024543829e-02f, +1.303110212e-03f, -3.433238619e-02f, +1.650540309e-02f, +3.054606534e-02f, -2.881784707e-02f, -1.210629477e-02f, +2.279292110e-02f, -1.524826854e-03f, -8.381259809e-03f, +2.729819110e-03f, +9.722433303e-05f, - /* 16,11 */ -4.400286560e-04f, +3.049970761e-03f, -4.188206830e-03f, -8.320109905e-03f, +1.897056596e-02f, +4.474489227e-03f, -3.456253827e-02f, +1.238771678e-02f, +3.319751225e-02f, -2.635039795e-02f, -1.540416082e-02f, +2.268843286e-02f, +2.634319572e-04f, -9.058284956e-03f, +2.450135010e-03f, +3.039101756e-04f, - /* 16,12 */ -4.634120047e-04f, +2.934475195e-03f, -3.306153325e-03f, -9.163764446e-03f, +1.749279467e-02f, +7.486889709e-03f, -3.433331277e-02f, +8.139354376e-03f, +3.542121816e-02f, -2.349470263e-02f, -1.858235038e-02f, +2.225957955e-02f, +2.151223968e-03f, -9.640854940e-03f, +2.091544281e-03f, +5.462770218e-04f, - /* 16,13 */ +0.000000000e+00f, +2.774741598e-03f, -2.445442331e-03f, -9.822158049e-03f, +1.584231759e-02f, +1.030255138e-02f, -3.365643786e-02f, +3.820010384e-03f, +3.718506562e-02f, -2.028541539e-02f, -2.159216402e-02f, +2.150028131e-02f, +4.114112388e-03f, -1.011218665e-02f, +1.653747621e-03f, +1.309641631e-03f, - /* 16,14 */ +0.000000000e+00f, +2.578329862e-03f, -1.619065127e-03f, -1.029592106e-02f, +1.405076114e-02f, +1.288771825e-02f, -3.254996068e-02f, -5.100475811e-04f, +3.846353557e-02f, -1.676283724e-02f, -2.438627738e-02f, +2.040939526e-02f, +6.125232216e-03f, -1.045641117e-02f, +1.138027416e-03f, +1.577651889e-03f, - /* 16,15 */ +0.000000000e+00f, +2.352913572e-03f, -8.385121370e-04f, -1.058864907e-02f, +1.215062389e-02f, +1.521305380e-02f, -3.103786392e-02f, -4.790894575e-03f, +3.923810803e-02f, -1.297236480e-02f, -2.691951328e-02f, +1.899089579e-02f, +8.155641030e-03f, -1.065891816e-02f, +5.473343456e-04f, +1.845338280e-03f, - /* 16, 0 */ -2.517634455e-04f, +5.956310854e-03f, -8.647029609e-03f, +1.153545125e-03f, +2.185732832e-02f, -2.369518339e-02f, -1.347728153e-02f, +3.949755319e-02f, -1.347728153e-02f, -2.369518339e-02f, +2.185732832e-02f, +1.153545125e-03f, -8.647029609e-03f, +2.914798040e-03f, -2.517634455e-04f, +0.000000000e+00f, - /* 16, 1 */ -3.647589216e-04f, +5.559366521e-03f, -7.860683839e-03f, -8.150822761e-04f, +2.252089097e-02f, -2.063007883e-02f, -1.749200540e-02f, +3.920026256e-02f, -9.205150933e-03f, -2.647415600e-02f, +2.081322447e-02f, +3.223212212e-03f, -9.337661142e-03f, +2.677108373e-03f, -9.939782505e-05f, +0.000000000e+00f, - /* 16, 2 */ -4.414886472e-04f, +5.113535158e-03f, -7.000222932e-03f, -2.654422569e-03f, +2.280787202e-02f, -1.733513495e-02f, -2.118899605e-02f, +3.831333038e-02f, -4.740975303e-03f, -2.891426752e-02f, +1.939126736e-02f, +5.362271050e-03f, -9.911447152e-03f, +2.349799583e-03f, +9.477253367e-05f, +0.000000000e+00f, - /* 16, 3 */ -4.855802427e-04f, +4.633347213e-03f, -6.087250386e-03f, -4.340001532e-03f, +2.272858071e-02f, -1.386914009e-02f, -2.451395150e-02f, +3.685148678e-02f, -1.540603716e-04f, -3.096737610e-02f, +1.760097190e-02f, +7.536131493e-03f, -1.034820384e-02f, +1.931270179e-03f, +3.323676319e-04f, +0.000000000e+00f, - /* 16, 4 */ +0.000000000e+00f, +4.132457962e-03f, -5.142935987e-03f, -5.851401101e-03f, +2.229926864e-02f, -1.029228788e-02f, -2.741946400e-02f, +3.483898696e-02f, +4.483517704e-03f, -3.259088680e-02f, +1.545873378e-02f, +9.707799030e-03f, -1.062918096e-02f, +1.421916825e-03f, +6.140535745e-04f, +0.000000000e+00f, - /* 16, 5 */ +0.000000000e+00f, +3.623457200e-03f, -4.187611099e-03f, -7.172450485e-03f, +2.154162444e-02f, -6.665085054e-03f, -2.986575546e-02f, +3.230917458e-02f, +9.098146940e-03f, -3.374862716e-02f, +1.298775300e-02f, +1.183848413e-02f, -1.073754388e-02f, +8.242784646e-04f, +9.394126333e-04f, +0.000000000e+00f, - /* 16, 6 */ +0.000000000e+00f, +3.117716971e-03f, -3.240404345e-03f, -8.291325326e-03f, +2.048218318e-02f, -3.047278250e-03f, -3.182126614e-02f, +2.930388237e-02f, +1.361595241e-02f, -3.441162052e-02f, +1.021782484e-02f, +1.388827332e-02f, -1.065884073e-02f, +1.431392807e-04f, +1.306826648e-03f, +0.000000000e+00f, - /* 16, 7 */ +0.000000000e+00f, +2.625277441e-03f, -2.318923448e-03f, -9.200556695e-03f, +1.915166452e-02f, +5.031802154e-04f, -3.326308735e-02f, +2.587268140e-02f, +1.796408380e-02f, -3.455874049e-02f, +7.184998851e-03f, +1.581685040e-02f, -1.038144369e-02f, -6.144144569e-04f, +1.713377737e-03f, +0.000000000e+00f, - /* 16, 8 */ +0.000000000e+00f, +2.154770219e-03f, -1.438987736e-03f, -9.896953513e-03f, +1.758425506e-02f, +3.931108902e-03f, -3.417723209e-02f, +2.207199352e-02f, +2.207199352e-02f, -3.417723209e-02f, +3.931108902e-03f, +1.758425506e-02f, -9.896953513e-03f, -1.438987736e-03f, +2.154770219e-03f, +0.000000000e+00f, - /* 16, 9 */ +0.000000000e+00f, +1.713377737e-03f, -6.144144569e-04f, -1.038144369e-02f, +1.581685040e-02f, +7.184998851e-03f, -3.455874049e-02f, +1.796408380e-02f, +2.587268140e-02f, -3.326308735e-02f, +5.031802154e-04f, +1.915166452e-02f, -9.200556695e-03f, -2.318923448e-03f, +2.625277441e-03f, +0.000000000e+00f, - /* 16,10 */ +0.000000000e+00f, +1.306826648e-03f, +1.431392807e-04f, -1.065884073e-02f, +1.388827332e-02f, +1.021782484e-02f, -3.441162052e-02f, +1.361595241e-02f, +2.930388237e-02f, -3.182126614e-02f, -3.047278250e-03f, +2.048218318e-02f, -8.291325326e-03f, -3.240404345e-03f, +3.117716971e-03f, +0.000000000e+00f, - /* 16,11 */ +0.000000000e+00f, +9.394126333e-04f, +8.242784646e-04f, -1.073754388e-02f, +1.183848413e-02f, +1.298775300e-02f, -3.374862716e-02f, +9.098146940e-03f, +3.230917458e-02f, -2.986575546e-02f, -6.665085054e-03f, +2.154162444e-02f, -7.172450485e-03f, -4.187611099e-03f, +3.623457200e-03f, +0.000000000e+00f, - /* 16,12 */ +0.000000000e+00f, +6.140535745e-04f, +1.421916825e-03f, -1.062918096e-02f, +9.707799030e-03f, +1.545873378e-02f, -3.259088680e-02f, +4.483517704e-03f, +3.483898696e-02f, -2.741946400e-02f, -1.029228788e-02f, +2.229926864e-02f, -5.851401101e-03f, -5.142935987e-03f, +4.132457962e-03f, +0.000000000e+00f, - /* 16,13 */ +0.000000000e+00f, +3.323676319e-04f, +1.931270179e-03f, -1.034820384e-02f, +7.536131493e-03f, +1.760097190e-02f, -3.096737610e-02f, -1.540603716e-04f, +3.685148678e-02f, -2.451395150e-02f, -1.386914009e-02f, +2.272858071e-02f, -4.340001532e-03f, -6.087250386e-03f, +4.633347213e-03f, -4.855802427e-04f, - /* 16,14 */ +0.000000000e+00f, +9.477253367e-05f, +2.349799583e-03f, -9.911447152e-03f, +5.362271050e-03f, +1.939126736e-02f, -2.891426752e-02f, -4.740975303e-03f, +3.831333038e-02f, -2.118899605e-02f, -1.733513495e-02f, +2.280787202e-02f, -2.654422569e-03f, -7.000222932e-03f, +5.113535158e-03f, -4.414886472e-04f, - /* 16,15 */ +0.000000000e+00f, -9.939782505e-05f, +2.677108373e-03f, -9.337661142e-03f, +3.223212212e-03f, +2.081322447e-02f, -2.647415600e-02f, -9.205150933e-03f, +3.920026256e-02f, -1.749200540e-02f, -2.063007883e-02f, +2.252089097e-02f, -8.150822761e-04f, -7.860683839e-03f, +5.559366521e-03f, -3.647589216e-04f, - /* 12, 0 */ -3.924537125e-03f, -6.177283790e-03f, +2.270137823e-02f, -1.696686670e-02f, -1.770529738e-02f, +3.949755319e-02f, -1.770529738e-02f, -1.696686670e-02f, +2.270137823e-02f, -6.177283790e-03f, -3.924537125e-03f, +2.689823824e-03f, - /* 12, 1 */ -2.918444824e-03f, -7.533875928e-03f, +2.217225575e-02f, -1.325050127e-02f, -2.161377319e-02f, +3.915985190e-02f, -1.343239533e-02f, -2.049610855e-02f, +2.283609035e-02f, -4.600700986e-03f, -4.945631587e-03f, +2.897838580e-03f, - /* 12, 2 */ -1.948111766e-03f, -8.657444743e-03f, +2.127619260e-02f, -9.420045488e-03f, -2.509275167e-02f, +3.815312062e-02f, -8.867972963e-03f, -2.376686078e-02f, +2.255583139e-02f, -2.822790564e-03f, -5.958903400e-03f, +3.051876120e-03f, - /* 12, 3 */ -1.031879811e-03f, -9.540571177e-03f, +2.004673480e-02f, -5.548699503e-03f, -2.808617760e-02f, +3.649634673e-02f, -4.091413649e-03f, -2.671092541e-02f, +2.184766025e-02f, -8.677312765e-04f, -6.939883353e-03f, +3.140832095e-03f, - /* 12, 4 */ -1.854082964e-04f, -1.018130776e-02f, +1.852257236e-02f, -1.708362919e-03f, -3.054799363e-02f, +3.422074403e-02f, +8.129280323e-04f, -2.926474106e-02f, +2.070682375e-02f, +1.235031889e-03f, -7.862950435e-03f, +3.154329873e-03f, - /* 12, 5 */ +5.785109863e-04f, -1.058292035e-02f, +1.674653148e-02f, +2.031769072e-03f, -3.244290444e-02f, +3.136911490e-02f, +5.757350815e-03f, -3.137078637e-02f, +1.913716500e-02f, +3.451172065e-03f, -8.701884951e-03f, +3.083080347e-03f, - /* 12, 6 */ +1.250056620e-03f, -1.075352499e-02f, +1.476451598e-02f, +5.606517218e-03f, -3.374690984e-02f, +2.799497691e-02f, +1.065251585e-02f, -3.297888633e-02f, +1.715135739e-02f, +5.741996578e-03f, -9.430471381e-03f, +2.919238566e-03f, - /* 12, 7 */ +1.822400383e-03f, -1.070563332e-02f, +1.262442359e-02f, +8.955911342e-03f, -3.444759838e-02f, +2.416147295e-02f, +1.540920462e-02f, -3.404739100e-02f, +1.477095353e-02f, +8.065088216e-03f, -1.002313834e-02f, +2.656746896e-03f, - /* 12, 8 */ +2.291654178e-03f, -1.045562141e-02f, +1.037506188e-02f, +1.202624229e-02f, -3.454419870e-02f, +1.994008861e-02f, +1.994008861e-02f, -3.454419870e-02f, +1.202624229e-02f, +1.037506188e-02f, -1.045562141e-02f, +2.291654178e-03f, - /* 12, 9 */ +2.656746896e-03f, -1.002313834e-02f, +8.065088216e-03f, +1.477095353e-02f, -3.404739100e-02f, +1.540920462e-02f, +2.416147295e-02f, -3.444759838e-02f, +8.955911342e-03f, +1.262442359e-02f, -1.070563332e-02f, +1.822400383e-03f, - /* 12,10 */ +2.919238566e-03f, -9.430471381e-03f, +5.741996578e-03f, +1.715135739e-02f, -3.297888633e-02f, +1.065251585e-02f, +2.799497691e-02f, -3.374690984e-02f, +5.606517218e-03f, +1.476451598e-02f, -1.075352499e-02f, +1.250056620e-03f, - /* 12,11 */ +3.083080347e-03f, -8.701884951e-03f, +3.451172065e-03f, +1.913716500e-02f, -3.137078637e-02f, +5.757350815e-03f, +3.136911490e-02f, -3.244290444e-02f, +2.031769072e-03f, +1.674653148e-02f, -1.058292035e-02f, +5.785109863e-04f, - /* 12,12 */ +3.154329873e-03f, -7.862950435e-03f, +1.235031889e-03f, +2.070682375e-02f, -2.926474106e-02f, +8.129280323e-04f, +3.422074403e-02f, -3.054799363e-02f, -1.708362919e-03f, +1.852257236e-02f, -1.018130776e-02f, -1.854082964e-04f, - /* 12,13 */ +3.140832095e-03f, -6.939883353e-03f, -8.677312765e-04f, +2.184766025e-02f, -2.671092541e-02f, -4.091413649e-03f, +3.649634673e-02f, -2.808617760e-02f, -5.548699503e-03f, +2.004673480e-02f, -9.540571177e-03f, -1.031879811e-03f, - /* 12,14 */ +3.051876120e-03f, -5.958903400e-03f, -2.822790564e-03f, +2.255583139e-02f, -2.376686078e-02f, -8.867972963e-03f, +3.815312062e-02f, -2.509275167e-02f, -9.420045488e-03f, +2.127619260e-02f, -8.657444743e-03f, -1.948111766e-03f, - /* 12,15 */ +2.897838580e-03f, -4.945631587e-03f, -4.600700986e-03f, +2.283609035e-02f, -2.049610855e-02f, -1.343239533e-02f, +3.915985190e-02f, -2.161377319e-02f, -1.325050127e-02f, +2.217225575e-02f, -7.533875928e-03f, -2.918444824e-03f, - /* 12, 0 */ +5.529156756e-04f, -1.017944650e-02f, +2.010395691e-02f, -9.501724583e-03f, -2.157725737e-02f, +3.949755319e-02f, -2.157725737e-02f, -9.501724583e-03f, +2.010395691e-02f, -1.017944650e-02f, +5.529156756e-04f, +9.520529918e-04f, - /* 12, 1 */ +1.269440891e-03f, -1.060857725e-02f, +1.848426560e-02f, -5.389674050e-03f, -2.526172923e-02f, +3.911687897e-02f, -1.740939271e-02f, -1.356576871e-02f, +2.138958875e-02f, -9.480008902e-03f, -2.676127190e-04f, +1.273328470e-03f, - /* 12, 2 */ +1.873685854e-03f, -1.077705214e-02f, +1.658179711e-02f, -1.315683663e-03f, -2.839592801e-02f, +3.798295250e-02f, -1.283645305e-02f, -1.749413418e-02f, +2.229518867e-02f, -8.506812328e-03f, -1.180051037e-03f, +1.604489886e-03f, - /* 12, 3 */ +2.361120897e-03f, -1.070010905e-02f, +1.445171501e-02f, +2.637679549e-03f, -3.092573063e-02f, +3.611987567e-02f, -7.946589383e-03f, -2.119952675e-02f, +2.278144860e-02f, -7.263083188e-03f, -2.168530550e-03f, +1.934678236e-03f, - /* 12, 4 */ +2.730794315e-03f, -1.039785120e-02f, +1.215160004e-02f, +6.393108320e-03f, -3.281077803e-02f, +3.356720057e-02f, -2.835931904e-03f, -2.459705675e-02f, +2.281696739e-02f, -5.759053577e-03f, -3.213563229e-03f, +2.251787566e-03f, - /* 12, 5 */ +2.985073479e-03f, -9.894440683e-03f, +9.739988515e-03f, +9.880142532e-03f, -3.402514934e-02f, +3.037901891e-02f, +2.393471366e-03f, -2.760625942e-02f, +2.237934513e-02f, -4.012119167e-03f, -4.292316215e-03f, +2.542756696e-03f, - /* 12, 6 */ +3.129309714e-03f, -9.217233004e-03f, +7.274949975e-03f, +1.303654969e-02f, -3.455769209e-02f, +2.662271867e-02f, +7.635875993e-03f, -3.015305887e-02f, +2.145609570e-02f, -2.046814992e-03f, -5.379003980e-03f, +2.793918230e-03f, - /* 12, 7 */ +3.171441616e-03f, -8.395878746e-03f, +4.812738303e-03f, +1.580947051e-02f, -3.441200543e-02f, +2.237743869e-02f, +1.278417054e-02f, -3.217162603e-02f, +2.004534769e-02f, +1.053992035e-04f, -6.445394017e-03f, +2.991397563e-03f, - /* 12, 8 */ +3.121552430e-03f, -7.461418245e-03f, +2.406547485e-03f, +1.815630830e-02f, -3.360608252e-02f, +1.773225889e-02f, +1.773225889e-02f, -3.360608252e-02f, +1.815630830e-02f, +2.406547485e-03f, -7.461418245e-03f, +3.121552430e-03f, - /* 12, 9 */ +2.991397563e-03f, -6.445394017e-03f, +1.053992035e-04f, +2.004534769e-02f, -3.217162603e-02f, +1.278417054e-02f, +2.237743869e-02f, -3.441200543e-02f, +1.580947051e-02f, +4.812738303e-03f, -8.395878746e-03f, +3.171441616e-03f, - /* 12,10 */ +2.793918230e-03f, -5.379003980e-03f, -2.046814992e-03f, +2.145609570e-02f, -3.015305887e-02f, +7.635875993e-03f, +2.662271867e-02f, -3.455769209e-02f, +1.303654969e-02f, +7.274949975e-03f, -9.217233004e-03f, +3.129309714e-03f, - /* 12,11 */ +2.542756696e-03f, -4.292316215e-03f, -4.012119167e-03f, +2.237934513e-02f, -2.760625942e-02f, +2.393471366e-03f, +3.037901891e-02f, -3.402514934e-02f, +9.880142532e-03f, +9.739988515e-03f, -9.894440683e-03f, +2.985073479e-03f, - /* 12,12 */ +2.251787566e-03f, -3.213563229e-03f, -5.759053577e-03f, +2.281696739e-02f, -2.459705675e-02f, -2.835931904e-03f, +3.356720057e-02f, -3.281077803e-02f, +6.393108320e-03f, +1.215160004e-02f, -1.039785120e-02f, +2.730794315e-03f, - /* 12,13 */ +1.934678236e-03f, -2.168530550e-03f, -7.263083188e-03f, +2.278144860e-02f, -2.119952675e-02f, -7.946589383e-03f, +3.611987567e-02f, -3.092573063e-02f, +2.637679549e-03f, +1.445171501e-02f, -1.070010905e-02f, +2.361120897e-03f, - /* 12,14 */ +1.604489886e-03f, -1.180051037e-03f, -8.506812328e-03f, +2.229518867e-02f, -1.749413418e-02f, -1.283645305e-02f, +3.798295250e-02f, -2.839592801e-02f, -1.315683663e-03f, +1.658179711e-02f, -1.077705214e-02f, +1.873685854e-03f, - /* 12,15 */ +1.273328470e-03f, -2.676127190e-04f, -9.480008902e-03f, +2.138958875e-02f, -1.356576871e-02f, -1.740939271e-02f, +3.911687897e-02f, -2.526172923e-02f, -5.389674050e-03f, +1.848426560e-02f, -1.060857725e-02f, +1.269440891e-03f, - - /* 24, 0 */ -8.820438069e-05f, -1.519461079e-04f, -2.301651496e-04f, -3.149320871e-04f, -3.945939739e-04f, -4.554410135e-04f, -4.841532882e-04f, -4.705408991e-04f, -4.099602091e-04f, -3.048100066e-04f, -1.646897470e-04f, -5.099007530e-06f, +1.551006323e-04f, +2.969416536e-04f, +4.046294158e-04f, +4.681429482e-04f, +4.846228261e-04f, +4.583040637e-04f, +3.990939388e-04f, +3.201968846e-04f, +2.353759082e-04f, +1.564712483e-04f, +9.167483068e-05f, +4.482688286e-05f, - /* 24, 1 */ -8.480575132e-05f, -1.474789784e-04f, -2.249812225e-04f, -3.096480504e-04f, -3.900204007e-04f, -4.524514078e-04f, -4.835165803e-04f, -4.727530367e-04f, -4.151145025e-04f, -3.125397891e-04f, -1.742016828e-04f, -1.529460870e-05f, +1.454387449e-04f, +2.889379628e-04f, +3.991236794e-04f, +4.655589110e-04f, +4.849233000e-04f, +4.610375470e-04f, +4.035168325e-04f, +3.254391996e-04f, +2.406110065e-04f, +1.610529558e-04f, +9.521673594e-05f, +4.721513201e-05f, - /* 24, 2 */ -8.147924507e-05f, -1.430712350e-04f, -2.198265592e-04f, -3.043479843e-04f, -3.853766873e-04f, -4.493383067e-04f, -4.827146831e-04f, -4.747797448e-04f, -4.200908527e-04f, -3.201278616e-04f, -1.836320864e-04f, -2.548296987e-05f, +1.357085413e-04f, +2.808022583e-04f, +3.934446700e-04f, +4.627886263e-04f, +4.850529052e-04f, +4.636385032e-04f, +4.078592000e-04f, +3.306557574e-04f, +2.458678944e-04f, +1.656897170e-04f, +9.882966748e-05f, +4.967415993e-05f, - /* 24, 3 */ -7.822510242e-05f, -1.387241832e-04f, -2.147035314e-04f, -2.990350629e-04f, -3.806663042e-04f, -4.461048161e-04f, -4.817496625e-04f, -4.766215175e-04f, -4.248879309e-04f, -3.275711800e-04f, -1.929766610e-04f, -3.565926997e-05f, +1.259145254e-04f, +2.725379532e-04f, +3.875941701e-04f, +4.598320457e-04f, +4.850099279e-04f, +4.661040260e-04f, +4.121175966e-04f, +3.358432542e-04f, +2.511439643e-04f, +1.703799499e-04f, +1.025131319e-04f, +5.220438912e-05f, - /* 24, 4 */ -7.504350274e-05f, -1.344390595e-04f, -2.096144489e-04f, -2.937124231e-04f, -3.758927218e-04f, -4.427540852e-04f, -4.806236671e-04f, -4.782789577e-04f, -4.295045226e-04f, -3.348667971e-04f, -2.022311686e-04f, -4.581869630e-05f, +1.160612449e-04f, +2.641485480e-04f, +3.815740737e-04f, +4.566892339e-04f, +4.847927474e-04f, +4.684312656e-04f, +4.162885910e-04f, +3.409983591e-04f, +2.564365532e-04f, +1.751220037e-04f, +1.062665706e-04f, +5.480619333e-05f, - /* 24, 5 */ -7.193456522e-05f, -1.302170312e-04f, -2.045615590e-04f, -2.883831622e-04f, -3.710594077e-04f, -4.392893036e-04f, -4.793389263e-04f, -4.797527765e-04f, -4.339395286e-04f, -3.420118645e-04f, -2.113914331e-04f, -5.595644787e-05f, +1.061532886e-04f, +2.556376279e-04f, +3.753863858e-04f, +4.533603695e-04f, +4.843998374e-04f, +4.706174312e-04f, +4.203687678e-04f, +3.461177167e-04f, +2.617429433e-04f, +1.799141593e-04f, +1.100893595e-04f, +5.747989630e-05f, - /* 24, 6 */ -6.889834987e-05f, -1.260591965e-04f, -1.995470454e-04f, -2.830503358e-04f, -3.661698243e-04f, -4.357136989e-04f, -4.778977479e-04f, -4.810437916e-04f, -4.381919648e-04f, -3.490036345e-04f, -2.204533432e-04f, -6.606773875e-05f, +9.619528314e-05f, +2.470088608e-04f, +3.690332209e-04f, +4.498457452e-04f, +4.838297683e-04f, +4.726597937e-04f, +4.243547301e-04f, +3.511979487e-04f, +2.670603639e-04f, +1.847546294e-04f, +1.139808078e-04f, +6.022577049e-05f, - /* 24, 7 */ -6.593485851e-05f, -1.219665852e-04f, -1.945730275e-04f, -2.777169567e-04f, -3.612274261e-04f, -4.320305335e-04f, -4.763025159e-04f, -4.821529264e-04f, -4.422609626e-04f, -3.558394612e-04f, -2.294128549e-04f, -7.614780136e-05f, +8.619188981e-05f, +2.382659945e-04f, +3.625168024e-04f, +4.461457687e-04f, +4.830812085e-04f, +4.745556880e-04f, +4.282431024e-04f, +3.562356572e-04f, +2.723859925e-04f, +1.896415594e-04f, +1.179401580e-04f, +6.304403582e-05f, - /* 24, 8 */ -6.304403582e-05f, -1.179401580e-04f, -1.896415594e-04f, -2.723859925e-04f, -3.562356572e-04f, -4.282431024e-04f, -4.745556880e-04f, -4.830812085e-04f, -4.461457687e-04f, -3.625168024e-04f, -2.382659945e-04f, -8.619188981e-05f, +7.614780136e-05f, +2.294128549e-04f, +3.558394612e-04f, +4.422609626e-04f, +4.821529264e-04f, +4.763025159e-04f, +4.320305335e-04f, +3.612274261e-04f, +2.777169567e-04f, +1.945730275e-04f, +1.219665852e-04f, +6.593485851e-05f, - /* 24, 9 */ -6.022577049e-05f, -1.139808078e-04f, -1.847546294e-04f, -2.670603639e-04f, -3.511979487e-04f, -4.243547301e-04f, -4.726597937e-04f, -4.838297683e-04f, -4.498457452e-04f, -3.690332209e-04f, -2.470088608e-04f, -9.619528314e-05f, +6.606773875e-05f, +2.204533432e-04f, +3.490036345e-04f, +4.381919648e-04f, +4.810437916e-04f, +4.778977479e-04f, +4.357136989e-04f, +3.661698243e-04f, +2.830503358e-04f, +1.995470454e-04f, +1.260591965e-04f, +6.889834987e-05f, - /* 24,10 */ -5.747989630e-05f, -1.100893595e-04f, -1.799141593e-04f, -2.617429433e-04f, -3.461177167e-04f, -4.203687678e-04f, -4.706174312e-04f, -4.843998374e-04f, -4.533603695e-04f, -3.753863858e-04f, -2.556376279e-04f, -1.061532886e-04f, +5.595644787e-05f, +2.113914331e-04f, +3.420118645e-04f, +4.339395286e-04f, +4.797527765e-04f, +4.793389263e-04f, +4.392893036e-04f, +3.710594077e-04f, +2.883831622e-04f, +2.045615590e-04f, +1.302170312e-04f, +7.193456522e-05f, - /* 24,11 */ -5.480619333e-05f, -1.062665706e-04f, -1.751220037e-04f, -2.564365532e-04f, -3.409983591e-04f, -4.162885910e-04f, -4.684312656e-04f, -4.847927474e-04f, -4.566892339e-04f, -3.815740737e-04f, -2.641485480e-04f, -1.160612449e-04f, +4.581869630e-05f, +2.022311686e-04f, +3.348667971e-04f, +4.295045226e-04f, +4.782789577e-04f, +4.806236671e-04f, +4.427540852e-04f, +3.758927218e-04f, +2.937124231e-04f, +2.096144489e-04f, +1.344390595e-04f, +7.504350274e-05f, - /* 24,12 */ -5.220438912e-05f, -1.025131319e-04f, -1.703799499e-04f, -2.511439643e-04f, -3.358432542e-04f, -4.121175966e-04f, -4.661040260e-04f, -4.850099279e-04f, -4.598320457e-04f, -3.875941701e-04f, -2.725379532e-04f, -1.259145254e-04f, +3.565926997e-05f, +1.929766610e-04f, +3.275711800e-04f, +4.248879309e-04f, +4.766215175e-04f, +4.817496625e-04f, +4.461048161e-04f, +3.806663042e-04f, +2.990350629e-04f, +2.147035314e-04f, +1.387241832e-04f, +7.822510242e-05f, - /* 24,13 */ -4.967415993e-05f, -9.882966748e-05f, -1.656897170e-04f, -2.458678944e-04f, -3.306557574e-04f, -4.078592000e-04f, -4.636385032e-04f, -4.850529052e-04f, -4.627886263e-04f, -3.934446700e-04f, -2.808022583e-04f, -1.357085413e-04f, +2.548296987e-05f, +1.836320864e-04f, +3.201278616e-04f, +4.200908527e-04f, +4.747797448e-04f, +4.827146831e-04f, +4.493383067e-04f, +3.853766873e-04f, +3.043479843e-04f, +2.198265592e-04f, +1.430712350e-04f, +8.147924507e-05f, - /* 24,14 */ -4.721513201e-05f, -9.521673594e-05f, -1.610529558e-04f, -2.406110065e-04f, -3.254391996e-04f, -4.035168325e-04f, -4.610375470e-04f, -4.849233000e-04f, -4.655589110e-04f, -3.991236794e-04f, -2.889379628e-04f, -1.454387449e-04f, +1.529460870e-05f, +1.742016828e-04f, +3.125397891e-04f, +4.151145025e-04f, +4.727530367e-04f, +4.835165803e-04f, +4.524514078e-04f, +3.900204007e-04f, +3.096480504e-04f, +2.249812225e-04f, +1.474789784e-04f, +8.480575132e-05f, - /* 24,15 */ -4.482688286e-05f, -9.167483068e-05f, -1.564712483e-04f, -2.353759082e-04f, -3.201968846e-04f, -3.990939388e-04f, -4.583040637e-04f, -4.846228261e-04f, -4.681429482e-04f, -4.046294158e-04f, -2.969416536e-04f, -1.551006323e-04f, +5.099007530e-06f, +1.646897470e-04f, +3.048100066e-04f, +4.099602091e-04f, +4.705408991e-04f, +4.841532882e-04f, +4.554410135e-04f, +3.945939739e-04f, +3.149320871e-04f, +2.301651496e-04f, +1.519461079e-04f, +8.820438069e-05f, - /* 24, 0 */ +3.721332452e-05f, -8.727351622e-06f, -1.260052743e-04f, -3.262895896e-04f, -5.956603662e-04f, -8.899501259e-04f, -1.140781305e-03f, -1.272347980e-03f, -1.224469676e-03f, -9.741728935e-04f, -5.476309302e-04f, -1.718639697e-05f, +5.165697336e-04f, +9.521355524e-04f, +1.214742061e-03f, +1.275075958e-03f, +1.153251370e-03f, +9.076938752e-04f, +6.139361451e-04f, +3.414081512e-04f, +1.360881127e-04f, +1.374767685e-05f, -3.597568203e-05f, -3.836441874e-05f, - /* 24, 1 */ +3.827272022e-05f, -3.990309212e-06f, -1.162552839e-04f, -3.114511951e-04f, -5.774902753e-04f, -8.720393210e-04f, -1.127840237e-03f, -1.268906542e-03f, -1.233389975e-03f, -9.955061195e-04f, -5.782766642e-04f, -5.154594549e-05f, +4.851159022e-04f, +9.294071718e-04f, +1.204207601e-03f, +1.277079499e-03f, +1.165232472e-03f, +9.252515980e-04f, +6.323029482e-04f, +3.567995352e-04f, +1.465038861e-04f, +1.905656402e-05f, -3.455261727e-05f, -3.906074609e-05f, - /* 24, 2 */ +3.916105537e-05f, +4.689475139e-07f, -1.068376458e-04f, -2.968998221e-04f, -5.594401371e-04f, -8.539803004e-04f, -1.114446367e-03f, -1.264763218e-03f, -1.241503276e-03f, -1.016122900e-03f, -6.084845647e-04f, -8.586577249e-05f, +4.532926958e-04f, +9.060015608e-04f, +1.192867560e-03f, +1.278348235e-03f, +1.176706916e-03f, +9.426042142e-04f, +6.507457252e-04f, +3.724559064e-04f, +1.572522637e-04f, +2.465906089e-05f, -3.293697730e-05f, -3.967556907e-05f, - /* 24, 3 */ +3.988551544e-05f, +4.656120273e-06f, -9.775146571e-05f, -2.826418349e-04f, -5.415238064e-04f, -8.357917522e-04f, -1.100618114e-03f, -1.259930153e-03f, -1.248810685e-03f, -1.036011659e-03f, -6.382327409e-04f, -1.201194461e-04f, +4.211237814e-04f, +8.819332498e-04f, +1.180724004e-03f, +1.278872430e-03f, +1.187657300e-03f, +9.597325558e-04f, +6.692490513e-04f, +3.883689407e-04f, +1.683324883e-04f, +3.055997058e-05f, -3.112164378e-05f, -4.020250110e-05f, - /* 24, 4 */ +4.045327387e-05f, +8.577101915e-06f, -8.899546026e-05f, -2.686831091e-04f, -5.237547173e-04f, -8.174921923e-04f, -1.086374092e-03f, -1.254420050e-03f, -1.255314082e-03f, -1.055161588e-03f, -6.674998060e-04f, -1.542806079e-04f, +3.886332072e-04f, +8.572174771e-04f, +1.167779798e-03f, +1.278642989e-03f, +1.198066533e-03f, +9.766173891e-04f, +6.877971412e-04f, +4.045298263e-04f, +1.797433681e-04f, +3.676383839e-05f, -2.909954521e-05f, -4.063504078e-05f, - /* 24, 5 */ +4.087148106e-05f, +1.223796294e-05f, -8.056796775e-05f, -2.550290329e-04f, -5.061458738e-04f, -7.990999447e-04f, -1.071733083e-03f, -1.248246148e-03f, -1.261016119e-03f, -1.073562648e-03f, -6.962648992e-04f, -1.883230024e-04f, +3.558453770e-04f, +8.318701747e-04f, +1.154038613e-03f, +1.277651484e-03f, +1.207917863e-03f, +9.932394374e-04f, +7.063738625e-04f, +4.209292660e-04f, +1.914832687e-04f, +4.327493877e-05f, -2.686366939e-05f, -4.096657950e-05f, - /* 24, 6 */ +4.114725367e-05f, +1.564493806e-05f, -7.246695886e-05f, -2.416845105e-04f, -4.887098400e-04f, -7.806331214e-04f, -1.056714015e-03f, -1.241422199e-03f, -1.265920211e-03f, -1.091205584e-03f, -7.245077077e-04f, -2.222205061e-04f, +3.227850234e-04f, +8.059079530e-04f, +1.139504920e-03f, +1.275890161e-03f, +1.217194897e-03f, +1.009579403e-03f, +7.249627509e-04f, +4.375574807e-04f, +2.035501057e-04f, +5.009726244e-05f, -2.440707607e-05f, -4.119040933e-05f, - /* 24, 7 */ +4.128766430e-05f, +1.880441272e-05f, -6.469004778e-05f, -2.286539641e-04f, -4.714587328e-04f, -7.621096041e-04f, -1.041335936e-03f, -1.233962452e-03f, -1.270030522e-03f, -1.108081926e-03f, -7.522084876e-04f, -2.559471563e-04f, +2.894771803e-04f, +7.793480846e-04f, +1.124183998e-03f, +1.273351959e-03f, +1.225881627e-03f, +1.025617992e-03f, +7.435470253e-04f, +4.544042133e-04f, +2.159413387e-04f, +5.723450367e-05f, -2.172290976e-05f, -4.129973134e-05f, - /* 24, 8 */ +4.129973134e-05f, +2.172290976e-05f, -5.723450367e-05f, -2.159413387e-04f, -4.544042133e-04f, -7.435470253e-04f, -1.025617992e-03f, -1.225881627e-03f, -1.273351959e-03f, -1.124183998e-03f, -7.793480846e-04f, -2.894771803e-04f, +2.559471563e-04f, +7.522084876e-04f, +1.108081926e-03f, +1.270030522e-03f, +1.233962452e-03f, +1.041335936e-03f, +7.621096041e-04f, +4.714587328e-04f, +2.286539641e-04f, +6.469004778e-05f, -1.880441272e-05f, -4.128766430e-05f, - /* 24, 9 */ +4.119040933e-05f, +2.440707607e-05f, -5.009726244e-05f, -2.035501057e-04f, -4.375574807e-04f, -7.249627509e-04f, -1.009579403e-03f, -1.217194897e-03f, -1.275890161e-03f, -1.139504920e-03f, -8.059079530e-04f, -3.227850234e-04f, +2.222205061e-04f, +7.245077077e-04f, +1.091205584e-03f, +1.265920211e-03f, +1.241422199e-03f, +1.056714015e-03f, +7.806331214e-04f, +4.887098400e-04f, +2.416845105e-04f, +7.246695886e-05f, -1.564493806e-05f, -4.114725367e-05f, - /* 24,10 */ +4.096657950e-05f, +2.686366939e-05f, -4.327493877e-05f, -1.914832687e-04f, -4.209292660e-04f, -7.063738625e-04f, -9.932394374e-04f, -1.207917863e-03f, -1.277651484e-03f, -1.154038613e-03f, -8.318701747e-04f, -3.558453770e-04f, +1.883230024e-04f, +6.962648992e-04f, +1.073562648e-03f, +1.261016119e-03f, +1.248246148e-03f, +1.071733083e-03f, +7.990999447e-04f, +5.061458738e-04f, +2.550290329e-04f, +8.056796775e-05f, -1.223796294e-05f, -4.087148106e-05f, - /* 24,11 */ +4.063504078e-05f, +2.909954521e-05f, -3.676383839e-05f, -1.797433681e-04f, -4.045298263e-04f, -6.877971412e-04f, -9.766173891e-04f, -1.198066533e-03f, -1.278642989e-03f, -1.167779798e-03f, -8.572174771e-04f, -3.886332072e-04f, +1.542806079e-04f, +6.674998060e-04f, +1.055161588e-03f, +1.255314082e-03f, +1.254420050e-03f, +1.086374092e-03f, +8.174921923e-04f, +5.237547173e-04f, +2.686831091e-04f, +8.899546026e-05f, -8.577101915e-06f, -4.045327387e-05f, - /* 24,12 */ +4.020250110e-05f, +3.112164378e-05f, -3.055997058e-05f, -1.683324883e-04f, -3.883689407e-04f, -6.692490513e-04f, -9.597325558e-04f, -1.187657300e-03f, -1.278872430e-03f, -1.180724004e-03f, -8.819332498e-04f, -4.211237814e-04f, +1.201194461e-04f, +6.382327409e-04f, +1.036011659e-03f, +1.248810685e-03f, +1.259930153e-03f, +1.100618114e-03f, +8.357917522e-04f, +5.415238064e-04f, +2.826418349e-04f, +9.775146571e-05f, -4.656120273e-06f, -3.988551544e-05f, - /* 24,13 */ +3.967556907e-05f, +3.293697730e-05f, -2.465906089e-05f, -1.572522637e-04f, -3.724559064e-04f, -6.507457252e-04f, -9.426042142e-04f, -1.176706916e-03f, -1.278348235e-03f, -1.192867560e-03f, -9.060015608e-04f, -4.532926958e-04f, +8.586577249e-05f, +6.084845647e-04f, +1.016122900e-03f, +1.241503276e-03f, +1.264763218e-03f, +1.114446367e-03f, +8.539803004e-04f, +5.594401371e-04f, +2.968998221e-04f, +1.068376458e-04f, -4.689475139e-07f, -3.916105537e-05f, - /* 24,14 */ +3.906074609e-05f, +3.455261727e-05f, -1.905656402e-05f, -1.465038861e-04f, -3.567995352e-04f, -6.323029482e-04f, -9.252515980e-04f, -1.165232472e-03f, -1.277079499e-03f, -1.204207601e-03f, -9.294071718e-04f, -4.851159022e-04f, +5.154594549e-05f, +5.782766642e-04f, +9.955061195e-04f, +1.233389975e-03f, +1.268906542e-03f, +1.127840237e-03f, +8.720393210e-04f, +5.774902753e-04f, +3.114511951e-04f, +1.162552839e-04f, +3.990309212e-06f, -3.827272022e-05f, - /* 24,15 */ +3.836441874e-05f, +3.597568203e-05f, -1.374767685e-05f, -1.360881127e-04f, -3.414081512e-04f, -6.139361451e-04f, -9.076938752e-04f, -1.153251370e-03f, -1.275075958e-03f, -1.214742061e-03f, -9.521355524e-04f, -5.165697336e-04f, +1.718639697e-05f, +5.476309302e-04f, +9.741728935e-04f, +1.224469676e-03f, +1.272347980e-03f, +1.140781305e-03f, +8.899501259e-04f, +5.956603662e-04f, +3.262895896e-04f, +1.260052743e-04f, +8.727351622e-06f, -3.721332452e-05f, - /* 24, 0 */ +8.266384897e-05f, +1.864042294e-04f, +2.488885336e-04f, +1.546439211e-04f, -1.995837972e-04f, -8.300120177e-04f, -1.613160849e-03f, -2.296673715e-03f, -2.585717258e-03f, -2.273475621e-03f, -1.352242686e-03f, -4.324968723e-05f, +1.278412578e-03f, +2.232544293e-03f, +2.585064833e-03f, +2.329165788e-03f, +1.661894649e-03f, +8.765619362e-04f, +2.314166150e-04f, -1.408802900e-04f, -2.488728147e-04f, -1.925779863e-04f, -8.867605644e-05f, -1.381647235e-05f, - /* 24, 1 */ +7.679604466e-05f, +1.800850086e-04f, +2.482891228e-04f, +1.673628145e-04f, -1.688781476e-04f, -7.841040553e-04f, -1.564053778e-03f, -2.262611362e-03f, -2.583952550e-03f, -2.311948888e-03f, -1.424504725e-03f, -1.296977982e-04f, +1.203096102e-03f, +2.189184288e-03f, +2.581965725e-03f, +2.360018674e-03f, +1.710180642e-03f, +9.237037585e-04f, +2.643640884e-04f, -1.260534627e-04f, -2.482108983e-04f, -1.985807152e-04f, -9.482163577e-05f, -1.700840565e-05f, - /* 24, 2 */ +7.108272573e-05f, +1.736451253e-04f, +2.471057735e-04f, +1.790568131e-04f, -1.393098721e-04f, -7.388859215e-04f, -1.514647192e-03f, -2.227049028e-03f, -2.579803518e-03f, -2.347938498e-03f, -1.495119547e-03f, -2.159922036e-04f, +1.126377386e-03f, +2.143428746e-03f, +2.576393731e-03f, +2.389164999e-03f, +1.757943642e-03f, +9.713853048e-04f, +2.984113695e-04f, -1.101464409e-04f, -2.468719141e-04f, -2.043861254e-04f, -1.010885985e-04f, -2.040687624e-05f, - /* 24, 3 */ +6.553303557e-05f, +1.671085856e-04f, +2.453697283e-04f, +1.897470664e-04f, -1.108869031e-04f, -6.944032625e-04f, -1.465013955e-03f, -2.190058265e-03f, -2.573306150e-03f, -2.381422658e-03f, -1.564010684e-03f, -3.020307159e-04f, +1.048342851e-03f, +2.095314540e-03f, +2.568326065e-03f, +2.416539054e-03f, +1.805107932e-03f, +1.019552324e-03f, +3.335412514e-04f, -9.314376077e-05f, -2.448252646e-04f, -2.099672379e-04f, -1.074639934e-04f, -2.401251277e-05f, - /* 24, 4 */ +6.015519126e-05f, +1.604985702e-04f, +2.431122111e-04f, +1.994559526e-04f, -8.361493575e-05f, -6.506994570e-04f, -1.415225919e-03f, -2.151711738e-03f, -2.564499518e-03f, -2.412383397e-03f, -1.631104459e-03f, -3.877115714e-04f, +9.690810727e-04f, +2.044882222e-03f, +2.557743429e-03f, +2.442076932e-03f, +1.851597394e-03f, +1.068148557e-03f, +3.697341485e-04f, -7.503156587e-05f, -2.420407013e-04f, -2.152964269e-04f, -1.139339030e-04f, -2.782526914e-05f, - /* 24, 5 */ +5.495649810e-05f, +1.538374078e-04f, +2.403643576e-04f, +2.082069988e-04f, -5.749746926e-05f, -6.078155802e-04f, -1.365353811e-03f, -2.112083086e-03f, -2.553425683e-03f, -2.440806561e-03f, -1.696330106e-03f, -4.729335981e-04f, +8.886826408e-04f, +1.992175989e-03f, +2.544630075e-03f, +2.465716661e-03f, +1.897335642e-03f, +1.117115802e-03f, +4.069680813e-04f, -5.579767803e-05f, -2.384884029e-04f, -2.203454641e-04f, -1.204834430e-04f, -3.184439496e-05f, - /* 24, 6 */ +4.994336610e-05f, +1.471465506e-04f, +2.371571498e-04f, +2.160248014e-04f, -3.253585139e-05f, -5.657903730e-04f, -1.315467130e-03f, -2.071246779e-03f, -2.540129593e-03f, -2.466681818e-03f, -1.759619871e-03f, -5.575963834e-04f, +8.072400133e-04f, +1.937243618e-03f, +2.528973864e-03f, +2.487398336e-03f, +1.942246152e-03f, +1.166393990e-03f, +4.452186667e-04f, -3.543166589e-05f, -2.341390536e-04f, -2.250855657e-04f, -1.270967638e-04f, -3.606840695e-05f, - /* 24, 7 */ +4.512132841e-05f, +1.404465535e-04f, +2.335213506e-04f, +2.229349451e-04f, -8.729326764e-06f, -5.246602166e-04f, -1.265634037e-03f, -2.029277978e-03f, -2.524658979e-03f, -2.490002646e-03f, -1.820909115e-03f, -6.416004392e-04f, +7.248473657e-04f, +1.880136408e-03f, +2.510766314e-03f, +2.507064240e-03f, +1.986252395e-03f, +1.215921259e-03f, +4.844591124e-04f, -1.392491133e-05f, -2.289639228e-04f, -2.294874421e-04f, -1.337570553e-04f, -4.049506149e-05f, - /* 24, 8 */ +4.049506149e-05f, +1.337570553e-04f, +2.294874421e-04f, +2.289639228e-04f, +1.392491133e-05f, -4.844591124e-04f, -1.215921259e-03f, -1.986252395e-03f, -2.507064240e-03f, -2.510766314e-03f, -1.880136408e-03f, -7.248473657e-04f, +6.416004392e-04f, +1.820909115e-03f, +2.490002646e-03f, +2.524658979e-03f, +2.029277978e-03f, +1.265634037e-03f, +5.246602166e-04f, +8.729326764e-06f, -2.229349451e-04f, -2.335213506e-04f, -1.404465535e-04f, -4.512132841e-05f, - /* 24, 9 */ +3.606840695e-05f, +1.270967638e-04f, +2.250855657e-04f, +2.341390536e-04f, +3.543166589e-05f, -4.452186667e-04f, -1.166393990e-03f, -1.942246152e-03f, -2.487398336e-03f, -2.528973864e-03f, -1.937243618e-03f, -8.072400133e-04f, +5.575963834e-04f, +1.759619871e-03f, +2.466681818e-03f, +2.540129593e-03f, +2.071246779e-03f, +1.315467130e-03f, +5.657903730e-04f, +3.253585139e-05f, -2.160248014e-04f, -2.371571498e-04f, -1.471465506e-04f, -4.994336610e-05f, - /* 24,10 */ +3.184439496e-05f, +1.204834430e-04f, +2.203454641e-04f, +2.384884029e-04f, +5.579767803e-05f, -4.069680813e-04f, -1.117115802e-03f, -1.897335642e-03f, -2.465716661e-03f, -2.544630075e-03f, -1.992175989e-03f, -8.886826408e-04f, +4.729335981e-04f, +1.696330106e-03f, +2.440806561e-03f, +2.553425683e-03f, +2.112083086e-03f, +1.365353811e-03f, +6.078155802e-04f, +5.749746926e-05f, -2.082069988e-04f, -2.403643576e-04f, -1.538374078e-04f, -5.495649810e-05f, - /* 24,11 */ +2.782526914e-05f, +1.139339030e-04f, +2.152964269e-04f, +2.420407013e-04f, +7.503156587e-05f, -3.697341485e-04f, -1.068148557e-03f, -1.851597394e-03f, -2.442076932e-03f, -2.557743429e-03f, -2.044882222e-03f, -9.690810727e-04f, +3.877115714e-04f, +1.631104459e-03f, +2.412383397e-03f, +2.564499518e-03f, +2.151711738e-03f, +1.415225919e-03f, +6.506994570e-04f, +8.361493575e-05f, -1.994559526e-04f, -2.431122111e-04f, -1.604985702e-04f, -6.015519126e-05f, - /* 24,12 */ +2.401251277e-05f, +1.074639934e-04f, +2.099672379e-04f, +2.448252646e-04f, +9.314376077e-05f, -3.335412514e-04f, -1.019552324e-03f, -1.805107932e-03f, -2.416539054e-03f, -2.568326065e-03f, -2.095314540e-03f, -1.048342851e-03f, +3.020307159e-04f, +1.564010684e-03f, +2.381422658e-03f, +2.573306150e-03f, +2.190058265e-03f, +1.465013955e-03f, +6.944032625e-04f, +1.108869031e-04f, -1.897470664e-04f, -2.453697283e-04f, -1.671085856e-04f, -6.553303557e-05f, - /* 24,13 */ +2.040687624e-05f, +1.010885985e-04f, +2.043861254e-04f, +2.468719141e-04f, +1.101464409e-04f, -2.984113695e-04f, -9.713853048e-04f, -1.757943642e-03f, -2.389164999e-03f, -2.576393731e-03f, -2.143428746e-03f, -1.126377386e-03f, +2.159922036e-04f, +1.495119547e-03f, +2.347938498e-03f, +2.579803518e-03f, +2.227049028e-03f, +1.514647192e-03f, +7.388859215e-04f, +1.393098721e-04f, -1.790568131e-04f, -2.471057735e-04f, -1.736451253e-04f, -7.108272573e-05f, - /* 24,14 */ +1.700840565e-05f, +9.482163577e-05f, +1.985807152e-04f, +2.482108983e-04f, +1.260534627e-04f, -2.643640884e-04f, -9.237037585e-04f, -1.710180642e-03f, -2.360018674e-03f, -2.581965725e-03f, -2.189184288e-03f, -1.203096102e-03f, +1.296977982e-04f, +1.424504725e-03f, +2.311948888e-03f, +2.583952550e-03f, +2.262611362e-03f, +1.564053778e-03f, +7.841040553e-04f, +1.688781476e-04f, -1.673628145e-04f, -2.482891228e-04f, -1.800850086e-04f, -7.679604466e-05f, - /* 24,15 */ +1.381647235e-05f, +8.867605644e-05f, +1.925779863e-04f, +2.488728147e-04f, +1.408802900e-04f, -2.314166150e-04f, -8.765619362e-04f, -1.661894649e-03f, -2.329165788e-03f, -2.585064833e-03f, -2.232544293e-03f, -1.278412578e-03f, +4.324968723e-05f, +1.352242686e-03f, +2.273475621e-03f, +2.585717258e-03f, +2.296673715e-03f, +1.613160849e-03f, +8.300120177e-04f, +1.995837972e-04f, -1.546439211e-04f, -2.488885336e-04f, -1.864042294e-04f, -8.266384897e-05f, - /* 24, 0 */ -8.756118778e-05f, -1.009631262e-05f, +2.499923290e-04f, +5.877223422e-04f, +6.788717735e-04f, +1.353208099e-04f, -1.181609893e-03f, -2.907631270e-03f, -4.227440709e-03f, -4.289302846e-03f, -2.753030129e-03f, -9.027467135e-05f, +2.610460208e-03f, +4.239433597e-03f, +4.275304929e-03f, +3.011836329e-03f, +1.284225967e-03f, -7.470693818e-05f, -6.668983668e-04f, -6.049037547e-04f, -2.711811033e-04f, -7.712041122e-07f, +8.724954076e-05f, +5.404595280e-05f, - /* 24, 1 */ -8.741655630e-05f, -2.019027119e-05f, +2.291878545e-04f, +5.696067266e-04f, +6.882827247e-04f, +1.927359117e-04f, -1.080756061e-03f, -2.801858302e-03f, -4.174485032e-03f, -4.332641998e-03f, -2.890980043e-03f, -2.706680808e-04f, +2.463494124e-03f, +4.183052585e-03f, +4.317905196e-03f, +3.114235078e-03f, +1.388440877e-03f, -1.091717027e-05f, -6.522789212e-04f, -6.210469572e-04f, -2.926973166e-04f, -1.241413728e-05f, +8.645224618e-05f, +5.751117153e-05f, - /* 24, 2 */ -8.684540791e-05f, -2.951540942e-05f, +2.088206066e-04f, +5.506594457e-04f, +6.952187414e-04f, +2.469379129e-04f, -9.818199274e-04f, -2.694754852e-03f, -4.116618896e-03f, -4.369446535e-03f, -3.024096686e-03f, -4.505940556e-04f, +2.312365817e-03f, +4.120192033e-03f, +4.355078033e-03f, +3.214588722e-03f, +1.494083528e-03f, +5.601692583e-05f, -6.349341530e-04f, -6.360467505e-04f, -3.144802134e-04f, -2.483132916e-05f, +8.514047439e-05f, +6.091502927e-05f, - /* 24, 3 */ -8.587775422e-05f, -3.807920270e-05f, +1.889395528e-04f, +5.309813040e-04f, +6.997709178e-04f, +2.979208532e-04f, -8.849487633e-04f, -2.586556795e-03f, -4.054031257e-03f, -4.399725659e-03f, -3.152177828e-03f, -6.297421881e-04f, +2.157318805e-03f, +4.050898074e-03f, +4.386669491e-03f, +3.312658663e-03f, +1.600975431e-03f, +1.260549593e-04f, -6.147894349e-04f, -6.497970322e-04f, -3.364651980e-04f, -3.801847602e-05f, +8.328608571e-05f, +6.423360450e-05f, - /* 24, 4 */ -8.454371894e-05f, -4.589171696e-05f, +1.695896910e-04f, +5.106711213e-04f, +7.020335552e-04f, +3.456869501e-04f, -7.902814402e-04f, -2.477497901e-03f, -3.986918477e-03f, -4.423502152e-03f, -3.275032702e-03f, -8.078038906e-04f, +1.998605647e-03f, +3.975230752e-03f, +4.412535626e-03f, +3.408207107e-03f, +1.708931018e-03f, +1.991476106e-04f, -5.917751493e-04f, -6.621910968e-04f, -3.585839047e-04f, -5.196800512e-05f, +8.086178430e-05f, +6.744196867e-05f, - /* 24, 5 */ -8.287340509e-05f, -5.296545744e-05f, +1.508120534e-04f, +4.898254962e-04f, +7.021037953e-04f, +3.902463858e-04f, -6.979482496e-04f, -2.367809282e-03f, -3.915483754e-03f, -4.440812209e-03f, -3.392482395e-03f, -9.844731162e-04f, +1.836487379e-03f, +3.893263974e-03f, +4.432542967e-03f, +3.500997660e-03f, +1.817757983e-03f, +2.752365501e-04f, -5.658270331e-04f, -6.731219472e-04f, -3.807642824e-04f, -6.666895953e-05f, +7.784127492e-05f, +7.051425394e-05f, - /* 24, 6 */ -8.089676755e-05f, -5.931521393e-05f, +1.326437227e-04f, +4.685385820e-04f, +7.000812546e-04f, +4.316170765e-04f, -6.080707472e-04f, -2.257718867e-03f, -3.839936544e-03f, -4.451705229e-03f, -3.504360214e-03f, -1.159447072e-03f, +1.671232927e-03f, +3.805085432e-03f, +4.446568950e-03f, +3.590795947e-03f, +1.927257648e-03f, +3.542543807e-04f, -5.368865161e-04f, -6.824826149e-04f, -4.029306956e-04f, -8.210689127e-05f, +7.419942176e-05f, +7.342372810e-05f, - /* 24, 7 */ -7.864349144e-05f, -6.495790319e-05f, +1.151178633e-04f, +4.469018792e-04f, +6.960676605e-04f, +4.698244236e-04f, -5.207616239e-04f, -2.147450881e-03f, -3.760491970e-03f, -4.456243581e-03f, -3.610512013e-03f, -1.332426925e-03f, +1.503118492e-03f, +3.710796487e-03f, +4.454502333e-03f, +3.677370219e-03f, +2.037225356e-03f, +4.361246060e-04f, -5.049010493e-04f, -6.901664903e-04f, -4.250040411e-04f, -9.826376366e-05f, +6.991240915e-05f, +7.614287656e-05f, - /* 24, 8 */ -7.614287656e-05f, -6.991240915e-05f, +9.826376366e-05f, +4.250040411e-04f, +6.901664903e-04f, +5.049010493e-04f, -4.361246060e-04f, -2.037225356e-03f, -3.677370219e-03f, -4.454502333e-03f, -3.710796487e-03f, -1.503118492e-03f, +1.332426925e-03f, +3.610512013e-03f, +4.456243581e-03f, +3.760491970e-03f, +2.147450881e-03f, +5.207616239e-04f, -4.698244236e-04f, -6.960676605e-04f, -4.469018792e-04f, -1.151178633e-04f, +6.495790319e-05f, +7.864349144e-05f, - /* 24, 9 */ -7.342372810e-05f, -7.419942176e-05f, +8.210689127e-05f, +4.029306956e-04f, +6.824826149e-04f, +5.368865161e-04f, -3.542543807e-04f, -1.927257648e-03f, -3.590795947e-03f, -4.446568950e-03f, -3.805085432e-03f, -1.671232927e-03f, +1.159447072e-03f, +3.504360214e-03f, +4.451705229e-03f, +3.839936544e-03f, +2.257718867e-03f, +6.080707472e-04f, -4.316170765e-04f, -7.000812546e-04f, -4.685385820e-04f, -1.326437227e-04f, +5.931521393e-05f, +8.089676755e-05f, - /* 24,10 */ -7.051425394e-05f, -7.784127492e-05f, +6.666895953e-05f, +3.807642824e-04f, +6.731219472e-04f, +5.658270331e-04f, -2.752365501e-04f, -1.817757983e-03f, -3.500997660e-03f, -4.432542967e-03f, -3.893263974e-03f, -1.836487379e-03f, +9.844731162e-04f, +3.392482395e-03f, +4.440812209e-03f, +3.915483754e-03f, +2.367809282e-03f, +6.979482496e-04f, -3.902463858e-04f, -7.021037953e-04f, -4.898254962e-04f, -1.508120534e-04f, +5.296545744e-05f, +8.287340509e-05f, - /* 24,11 */ -6.744196867e-05f, -8.086178430e-05f, +5.196800512e-05f, +3.585839047e-04f, +6.621910968e-04f, +5.917751493e-04f, -1.991476106e-04f, -1.708931018e-03f, -3.408207107e-03f, -4.412535626e-03f, -3.975230752e-03f, -1.998605647e-03f, +8.078038906e-04f, +3.275032702e-03f, +4.423502152e-03f, +3.986918477e-03f, +2.477497901e-03f, +7.902814402e-04f, -3.456869501e-04f, -7.020335552e-04f, -5.106711213e-04f, -1.695896910e-04f, +4.589171696e-05f, +8.454371894e-05f, - /* 24,12 */ -6.423360450e-05f, -8.328608571e-05f, +3.801847602e-05f, +3.364651980e-04f, +6.497970322e-04f, +6.147894349e-04f, -1.260549593e-04f, -1.600975431e-03f, -3.312658663e-03f, -4.386669491e-03f, -4.050898074e-03f, -2.157318805e-03f, +6.297421881e-04f, +3.152177828e-03f, +4.399725659e-03f, +4.054031257e-03f, +2.586556795e-03f, +8.849487633e-04f, -2.979208532e-04f, -6.997709178e-04f, -5.309813040e-04f, -1.889395528e-04f, +3.807920270e-05f, +8.587775422e-05f, - /* 24,13 */ -6.091502927e-05f, -8.514047439e-05f, +2.483132916e-05f, +3.144802134e-04f, +6.360467505e-04f, +6.349341530e-04f, -5.601692583e-05f, -1.494083528e-03f, -3.214588722e-03f, -4.355078033e-03f, -4.120192033e-03f, -2.312365817e-03f, +4.505940556e-04f, +3.024096686e-03f, +4.369446535e-03f, +4.116618896e-03f, +2.694754852e-03f, +9.818199274e-04f, -2.469379129e-04f, -6.952187414e-04f, -5.506594457e-04f, -2.088206066e-04f, +2.951540942e-05f, +8.684540791e-05f, - /* 24,14 */ -5.751117153e-05f, -8.645224618e-05f, +1.241413728e-05f, +2.926973166e-04f, +6.210469572e-04f, +6.522789212e-04f, +1.091717027e-05f, -1.388440877e-03f, -3.114235078e-03f, -4.317905196e-03f, -4.183052585e-03f, -2.463494124e-03f, +2.706680808e-04f, +2.890980043e-03f, +4.332641998e-03f, +4.174485032e-03f, +2.801858302e-03f, +1.080756061e-03f, -1.927359117e-04f, -6.882827247e-04f, -5.696067266e-04f, -2.291878545e-04f, +2.019027119e-05f, +8.741655630e-05f, - /* 24,15 */ -5.404595280e-05f, -8.724954076e-05f, +7.712041122e-07f, +2.711811033e-04f, +6.049037547e-04f, +6.668983668e-04f, +7.470693818e-05f, -1.284225967e-03f, -3.011836329e-03f, -4.275304929e-03f, -4.239433597e-03f, -2.610460208e-03f, +9.027467135e-05f, +2.753030129e-03f, +4.289302846e-03f, +4.227440709e-03f, +2.907631270e-03f, +1.181609893e-03f, -1.353208099e-04f, -6.788717735e-04f, -5.877223422e-04f, -2.499923290e-04f, +1.009631262e-05f, +8.756118778e-05f, - /* 24, 0 */ -4.836862817e-05f, -2.381906908e-04f, -2.861422699e-04f, +1.419765781e-04f, +9.779307384e-04f, +1.431118485e-03f, +4.239072727e-04f, -2.320049614e-03f, -5.516524807e-03f, -6.885468951e-03f, -4.882970050e-03f, -1.652445539e-04f, +4.647640808e-03f, +6.864975932e-03f, +5.679465803e-03f, +2.528057977e-03f, -2.982544427e-04f, -1.420326139e-03f, -1.029081461e-03f, -1.868092348e-04f, +2.758007186e-04f, +2.491702023e-04f, +5.836581816e-05f, -3.491105347e-05f, - /* 24, 1 */ -3.887878147e-05f, -2.267040256e-04f, -2.944876029e-04f, +9.900949096e-05f, +9.254138922e-04f, +1.435932770e-03f, +5.422031866e-04f, -2.114193296e-03f, -5.346195092e-03f, -6.891993065e-03f, -5.106971374e-03f, -4.953359135e-04f, +4.401480442e-03f, +6.830374341e-03f, +5.834433801e-03f, +2.737690485e-03f, -1.653667139e-04f, -1.403322383e-03f, -1.078579553e-03f, -2.333982604e-04f, +2.633988510e-04f, +2.595470071e-04f, +6.884149383e-05f, -3.317859772e-05f, - /* 24, 2 */ -2.992041863e-05f, -2.148033017e-04f, -3.009073041e-04f, +5.800387728e-05f, +8.718108917e-04f, +1.435015360e-03f, +6.530479478e-04f, -1.910998057e-03f, -5.169072824e-03f, -6.884726614e-03f, -5.319183002e-03f, -8.242352929e-04f, +4.145019341e-03f, +6.781564023e-03f, +5.980858542e-03f, +2.948401826e-03f, -2.539414214e-05f, -1.379888189e-03f, -1.126132932e-03f, -2.816211011e-04f, +2.488796810e-04f, +2.692234993e-04f, +7.976200316e-05f, -3.095924021e-05f, - /* 24, 3 */ -2.151306397e-05f, -2.025787804e-04f, -3.054778181e-04f, +1.904245883e-05f, +8.173942695e-04f, +1.428624316e-03f, +7.563747429e-04f, -1.710952703e-03f, -4.985763915e-03f, -6.863885651e-03f, -5.519179462e-03f, -1.151152247e-03f, +3.878819947e-03f, +6.718485032e-03f, +6.118185890e-03f, +3.159630822e-03f, +1.214850236e-04f, -1.349820125e-03f, -1.171444944e-03f, -3.313418525e-04f, +2.321939470e-04f, +2.781004545e-04f, +9.108884721e-05f, -2.823129406e-05f, - /* 24, 4 */ -1.367174826e-05f, -1.901175448e-04f, -3.082808136e-04f, -1.780505549e-05f, +7.624282793e-04f, +1.417028061e-03f, +8.521437270e-04f, -1.514524553e-03f, -4.796881865e-03f, -6.829722854e-03f, -5.706572744e-03f, -1.475302628e-03f, +3.603475092e-03f, +6.641118197e-03f, +6.245879909e-03f, +3.370802059e-03f, +2.750642929e-04f, -1.312931609e-03f, -1.214215433e-03f, -3.824113238e-04f, +2.133007109e-04f, +2.860774924e-04f, +1.027786538e-04f, -2.497524656e-05f, - /* 24, 5 */ -6.407151783e-06f, -1.775031866e-04f, -3.094025487e-04f, -5.248174754e-05f, +7.071681142e-04f, +1.400504044e-03f, +9.403414758e-04f, -1.322158275e-03f, -4.603045619e-03f, -6.782526316e-03f, -5.881013326e-03f, -1.795911068e-03f, +3.319606230e-03f, +6.549485546e-03f, +6.363424898e-03f, +3.581327596e-03f, +4.351089927e-04f, -1.269054120e-03f, -1.254141844e-03f, -4.346671648e-04f, +1.921679399e-04f, +2.930535649e-04f, +1.147831783e-04f, -2.117401174e-05f, - /* 24, 6 */ +2.742338831e-07f, -1.648155254e-04f, -3.089332390e-04f, -8.494321776e-05f, +6.518591895e-04f, +1.379337399e-03f, +1.020980349e-03f, -1.134274832e-03f, -4.404877423e-03f, -6.722618231e-03f, -6.042191052e-03f, -2.112213435e-03f, +3.027861551e-03f, +6.443650588e-03f, +6.470327373e-03f, +3.790608755e-03f, +6.013564365e-04f, -1.218038367e-03f, -1.290920380e-03f, -4.879340571e-04f, +1.687730668e-04f, +2.989274696e-04f, +1.270493337e-04f, -1.681317980e-05f, - /* 24, 7 */ +6.369927035e-06f, -1.521303593e-04f, -3.069664313e-04f, -1.151572658e-04f, +5.967364879e-04f, +1.353819611e-03f, +1.094097769e-03f, -9.512705360e-04f, -4.203000702e-03f, -6.650353458e-03f, -6.189835876e-03f, -2.423459243e-03f, +2.728914008e-03f, +6.323718452e-03f, +6.566118000e-03f, +3.998037969e-03f, +7.735162047e-04f, -1.159755419e-03f, -1.324247193e-03f, -5.420239710e-04f, +1.431035268e-04f, +3.035983857e-04f, +1.395192491e-04f, -1.188126168e-05f, - /* 24, 8 */ +1.188126168e-05f, -1.395192491e-04f, -3.035983857e-04f, -1.431035268e-04f, +5.420239710e-04f, +1.324247193e-03f, +1.159755419e-03f, -7.735162047e-04f, -3.998037969e-03f, -6.566118000e-03f, -6.323718452e-03f, -2.728914008e-03f, +2.423459243e-03f, +6.189835876e-03f, +6.650353458e-03f, +4.203000702e-03f, +9.512705360e-04f, -1.094097769e-03f, -1.353819611e-03f, -5.967364879e-04f, +1.151572658e-04f, +3.069664313e-04f, +1.521303593e-04f, -6.369927035e-06f, - /* 24, 9 */ +1.681317980e-05f, -1.270493337e-04f, -2.989274696e-04f, -1.687730668e-04f, +4.879340571e-04f, +1.290920380e-03f, +1.218038367e-03f, -6.013564365e-04f, -3.790608755e-03f, -6.470327373e-03f, -6.443650588e-03f, -3.027861551e-03f, +2.112213435e-03f, +6.042191052e-03f, +6.722618231e-03f, +4.404877423e-03f, +1.134274832e-03f, -1.020980349e-03f, -1.379337399e-03f, -6.518591895e-04f, +8.494321776e-05f, +3.089332390e-04f, +1.648155254e-04f, -2.742338831e-07f, - /* 24,10 */ +2.117401174e-05f, -1.147831783e-04f, -2.930535649e-04f, -1.921679399e-04f, +4.346671648e-04f, +1.254141844e-03f, +1.269054120e-03f, -4.351089927e-04f, -3.581327596e-03f, -6.363424898e-03f, -6.549485546e-03f, -3.319606230e-03f, +1.795911068e-03f, +5.881013326e-03f, +6.782526316e-03f, +4.603045619e-03f, +1.322158275e-03f, -9.403414758e-04f, -1.400504044e-03f, -7.071681142e-04f, +5.248174754e-05f, +3.094025487e-04f, +1.775031866e-04f, +6.407151783e-06f, - /* 24,11 */ +2.497524656e-05f, -1.027786538e-04f, -2.860774924e-04f, -2.133007109e-04f, +3.824113238e-04f, +1.214215433e-03f, +1.312931609e-03f, -2.750642929e-04f, -3.370802059e-03f, -6.245879909e-03f, -6.641118197e-03f, -3.603475092e-03f, +1.475302628e-03f, +5.706572744e-03f, +6.829722854e-03f, +4.796881865e-03f, +1.514524553e-03f, -8.521437270e-04f, -1.417028061e-03f, -7.624282793e-04f, +1.780505549e-05f, +3.082808136e-04f, +1.901175448e-04f, +1.367174826e-05f, - /* 24,12 */ +2.823129406e-05f, -9.108884721e-05f, -2.781004545e-04f, -2.321939470e-04f, +3.313418525e-04f, +1.171444944e-03f, +1.349820125e-03f, -1.214850236e-04f, -3.159630822e-03f, -6.118185890e-03f, -6.718485032e-03f, -3.878819947e-03f, +1.151152247e-03f, +5.519179462e-03f, +6.863885651e-03f, +4.985763915e-03f, +1.710952703e-03f, -7.563747429e-04f, -1.428624316e-03f, -8.173942695e-04f, -1.904245883e-05f, +3.054778181e-04f, +2.025787804e-04f, +2.151306397e-05f, - /* 24,13 */ +3.095924021e-05f, -7.976200316e-05f, -2.692234993e-04f, -2.488796810e-04f, +2.816211011e-04f, +1.126132932e-03f, +1.379888189e-03f, +2.539414214e-05f, -2.948401826e-03f, -5.980858542e-03f, -6.781564023e-03f, -4.145019341e-03f, +8.242352929e-04f, +5.319183002e-03f, +6.884726614e-03f, +5.169072824e-03f, +1.910998057e-03f, -6.530479478e-04f, -1.435015360e-03f, -8.718108917e-04f, -5.800387728e-05f, +3.009073041e-04f, +2.148033017e-04f, +2.992041863e-05f, - /* 24,14 */ +3.317859772e-05f, -6.884149383e-05f, -2.595470071e-04f, -2.633988510e-04f, +2.333982604e-04f, +1.078579553e-03f, +1.403322383e-03f, +1.653667139e-04f, -2.737690485e-03f, -5.834433801e-03f, -6.830374341e-03f, -4.401480442e-03f, +4.953359135e-04f, +5.106971374e-03f, +6.891993065e-03f, +5.346195092e-03f, +2.114193296e-03f, -5.422031866e-04f, -1.435932770e-03f, -9.254138922e-04f, -9.900949096e-05f, +2.944876029e-04f, +2.267040256e-04f, +3.887878147e-05f, - /* 24,15 */ +3.491105347e-05f, -5.836581816e-05f, -2.491702023e-04f, -2.758007186e-04f, +1.868092348e-04f, +1.029081461e-03f, +1.420326139e-03f, +2.982544427e-04f, -2.528057977e-03f, -5.679465803e-03f, -6.864975932e-03f, -4.647640808e-03f, +1.652445539e-04f, +4.882970050e-03f, +6.885468951e-03f, +5.516524807e-03f, +2.320049614e-03f, -4.239072727e-04f, -1.431118485e-03f, -9.779307384e-04f, -1.419765781e-04f, +2.861422699e-04f, +2.381906908e-04f, +4.836862817e-05f, - /* 24, 0 */ +1.364396009e-04f, +7.446376994e-05f, -3.699603221e-04f, -7.278325124e-04f, -5.051635567e-05f, +1.645033952e-03f, +2.378022613e-03f, -2.243714932e-04f, -5.680096534e-03f, -9.704626250e-03f, -7.823014841e-03f, -2.751390883e-04f, +7.480820734e-03f, +9.788905453e-03f, +6.030582333e-03f, +5.101196376e-04f, -2.328731157e-03f, -1.748114996e-03f, -3.640531891e-05f, +7.242350145e-04f, +4.042879967e-04f, -5.742334247e-05f, -1.414906982e-04f, -2.710774794e-05f, - /* 24, 1 */ +1.307026563e-04f, +8.975162518e-05f, -3.355241044e-04f, -7.270603554e-04f, -1.326866063e-04f, +1.538422151e-03f, +2.413247311e-03f, +4.875121157e-05f, -5.324161431e-03f, -9.595517291e-03f, -8.141086512e-03f, -8.245287223e-04f, +7.115425083e-03f, +9.847646625e-03f, +6.374200107e-03f, +8.077901021e-04f, -2.264974711e-03f, -1.846937004e-03f, -1.278166248e-04f, +7.160485626e-04f, +4.382702758e-04f, -3.863673145e-05f, -1.457592711e-04f, -3.374854096e-05f, - /* 24, 2 */ +1.243758393e-04f, +1.032931784e-04f, -3.012044148e-04f, -7.221532843e-04f, -2.098818030e-04f, +1.428995714e-03f, +2.434853697e-03f, +3.086181402e-04f, -4.964188024e-03f, -9.462372525e-03f, -8.434212217e-03f, -1.371256439e-03f, +6.727842835e-03f, +9.880231223e-03f, +6.709529590e-03f, +1.116608515e-03f, -2.186408722e-03f, -1.940762958e-03f, -2.234175210e-04f, +7.030713845e-04f, +4.716595396e-04f, -1.812362539e-05f, -1.491486840e-04f, -4.073257728e-05f, - /* 24, 3 */ +1.175537217e-04f, +1.151066589e-04f, -2.672136493e-04f, -7.133593846e-04f, -2.819161814e-04f, +1.317455363e-03f, +2.443336794e-03f, +5.546728855e-04f, -4.601573558e-03f, -9.306067159e-03f, -8.701668636e-03f, -1.913559980e-03f, +6.319179241e-03f, +9.886134123e-03f, +7.035155238e-03f, +1.435731166e-03f, -2.092745601e-03f, -2.028850662e-03f, -3.228698520e-04f, +6.851212972e-04f, +5.041985339e-04f, +4.082412249e-06f, -1.515631236e-04f, -4.801831197e-05f, - /* 24, 4 */ +1.103288160e-04f, +1.252215041e-04f, -2.337507561e-04f, -7.009379926e-04f, -3.486413414e-04f, +1.204483360e-03f, +2.439234434e-03f, +7.864337317e-04f, -4.237695644e-03f, -9.127552920e-03f, -8.942835031e-03f, -2.449695551e-03f, +5.890625670e-03f, +9.864926907e-03f, +7.349672574e-03f, +1.764247300e-03f, -1.983757790e-03f, -2.110456450e-03f, -4.257977068e-04f, +6.620377298e-04f, +5.356216172e-04f, +2.793344097e-05f, -1.529084143e-04f, -5.555842361e-05f, - /* 24, 5 */ +1.027909684e-04f, +1.336775649e-04f, -2.010005851e-04f, -6.851576168e-04f, -4.099455518e-04f, +1.090740633e-03f, +2.423123355e-03f, +1.003494037e-03f, -3.873906570e-03f, -8.927853008e-03f, -9.157195135e-03f, -2.977945083e-03f, +5.443455012e-03f, +9.816280735e-03f, +7.651694576e-03f, +2.101181791e-03f, -1.859280606e-03f, -2.184839011e-03f, -5.317880090e-04f, +6.336836952e-04f, +5.656561208e-04f, +5.336672075e-05f, -1.530928622e-04f, -6.329988035e-05f, - /* 24, 6 */ +9.502680145e-05f, +1.405242734e-04f, -1.691333585e-04f, -6.662938873e-04f, -4.657528710e-04f, +9.768641187e-04f, +2.395615219e-03f, +1.205522243e-03f, -3.511527821e-03f, -8.708056786e-03f, -9.344338564e-03f, -3.496623369e-03f, +4.979016698e-03f, +9.739968779e-03f, +7.939858067e-03f, +2.445498177e-03f, -1.719214825e-03f, -2.251263312e-03f, -6.403913399e-04f, +5.999476948e-04f, +5.940238143e-04f, +8.030437567e-05f, -1.520281231e-04f, -7.118405837e-05f, - /* 24, 7 */ +8.711921055e-05f, +1.458197836e-04f, -1.383042613e-04f, -6.446275435e-04f, -5.160220948e-04f, +8.634643034e-04f, +2.357352548e-03f, +1.392261511e-03f, -3.151844842e-03f, -8.469314215e-03f, -9.503961719e-03f, -4.004085039e-03f, +4.498731341e-03f, +9.635868210e-03f, +8.212830089e-03f, +2.796102059e-03f, -1.563529005e-03f, -2.309004616e-03f, -7.511229995e-04f, +5.607455425e-04f, +6.204424737e-04f, +1.086531499e-04f, -1.496300883e-04f, -7.914691395e-05f, - /* 24, 8 */ +7.914691395e-05f, +1.496300883e-04f, -1.086531499e-04f, -6.204424737e-04f, -5.607455425e-04f, +7.511229995e-04f, +2.309004616e-03f, +1.563529005e-03f, -2.796102059e-03f, -8.212830089e-03f, -9.635868210e-03f, -4.498731341e-03f, +4.004085039e-03f, +9.503961719e-03f, +8.469314215e-03f, +3.151844842e-03f, -1.392261511e-03f, -2.357352548e-03f, -8.634643034e-04f, +5.160220948e-04f, +6.446275435e-04f, +1.383042613e-04f, -1.458197836e-04f, -8.711921055e-05f, - /* 24, 9 */ +7.118405837e-05f, +1.520281231e-04f, -8.030437567e-05f, -5.940238143e-04f, -5.999476948e-04f, +6.403913399e-04f, +2.251263312e-03f, +1.719214825e-03f, -2.445498177e-03f, -7.939858067e-03f, -9.739968779e-03f, -4.979016698e-03f, +3.496623369e-03f, +9.344338564e-03f, +8.708056786e-03f, +3.511527821e-03f, -1.205522243e-03f, -2.395615219e-03f, -9.768641187e-04f, +4.657528710e-04f, +6.662938873e-04f, +1.691333585e-04f, -1.405242734e-04f, -9.502680145e-05f, - /* 24,10 */ +6.329988035e-05f, +1.530928622e-04f, -5.336672075e-05f, -5.656561208e-04f, -6.336836952e-04f, +5.317880090e-04f, +2.184839011e-03f, +1.859280606e-03f, -2.101181791e-03f, -7.651694576e-03f, -9.816280735e-03f, -5.443455012e-03f, +2.977945083e-03f, +9.157195135e-03f, +8.927853008e-03f, +3.873906570e-03f, -1.003494037e-03f, -2.423123355e-03f, -1.090740633e-03f, +4.099455518e-04f, +6.851576168e-04f, +2.010005851e-04f, -1.336775649e-04f, -1.027909684e-04f, - /* 24,11 */ +5.555842361e-05f, +1.529084143e-04f, -2.793344097e-05f, -5.356216172e-04f, -6.620377298e-04f, +4.257977068e-04f, +2.110456450e-03f, +1.983757790e-03f, -1.764247300e-03f, -7.349672574e-03f, -9.864926907e-03f, -5.890625670e-03f, +2.449695551e-03f, +8.942835031e-03f, +9.127552920e-03f, +4.237695644e-03f, -7.864337317e-04f, -2.439234434e-03f, -1.204483360e-03f, +3.486413414e-04f, +7.009379926e-04f, +2.337507561e-04f, -1.252215041e-04f, -1.103288160e-04f, - /* 24,12 */ +4.801831197e-05f, +1.515631236e-04f, -4.082412249e-06f, -5.041985339e-04f, -6.851212972e-04f, +3.228698520e-04f, +2.028850662e-03f, +2.092745601e-03f, -1.435731166e-03f, -7.035155238e-03f, -9.886134123e-03f, -6.319179241e-03f, +1.913559980e-03f, +8.701668636e-03f, +9.306067159e-03f, +4.601573558e-03f, -5.546728855e-04f, -2.443336794e-03f, -1.317455363e-03f, +2.819161814e-04f, +7.133593846e-04f, +2.672136493e-04f, -1.151066589e-04f, -1.175537217e-04f, - /* 24,13 */ +4.073257728e-05f, +1.491486840e-04f, +1.812362539e-05f, -4.716595396e-04f, -7.030713845e-04f, +2.234175210e-04f, +1.940762958e-03f, +2.186408722e-03f, -1.116608515e-03f, -6.709529590e-03f, -9.880231223e-03f, -6.727842835e-03f, +1.371256439e-03f, +8.434212217e-03f, +9.462372525e-03f, +4.964188024e-03f, -3.086181402e-04f, -2.434853697e-03f, -1.428995714e-03f, +2.098818030e-04f, +7.221532843e-04f, +3.012044148e-04f, -1.032931784e-04f, -1.243758393e-04f, - /* 24,14 */ +3.374854096e-05f, +1.457592711e-04f, +3.863673145e-05f, -4.382702758e-04f, -7.160485626e-04f, +1.278166248e-04f, +1.846937004e-03f, +2.264974711e-03f, -8.077901021e-04f, -6.374200107e-03f, -9.847646625e-03f, -7.115425083e-03f, +8.245287223e-04f, +8.141086512e-03f, +9.595517291e-03f, +5.324161431e-03f, -4.875121157e-05f, -2.413247311e-03f, -1.538422151e-03f, +1.326866063e-04f, +7.270603554e-04f, +3.355241044e-04f, -8.975162518e-05f, -1.307026563e-04f, - /* 24,15 */ +2.710774794e-05f, +1.414906982e-04f, +5.742334247e-05f, -4.042879967e-04f, -7.242350145e-04f, +3.640531891e-05f, +1.748114996e-03f, +2.328731157e-03f, -5.101196376e-04f, -6.030582333e-03f, -9.788905453e-03f, -7.480820734e-03f, +2.751390883e-04f, +7.823014841e-03f, +9.704626250e-03f, +5.680096534e-03f, +2.243714932e-04f, -2.378022613e-03f, -1.645033952e-03f, +5.051635567e-05f, +7.278325124e-04f, +3.699603221e-04f, -7.446376994e-05f, -1.364396009e-04f, - /* 20, 0 */ +1.366654441e-04f, -5.248309364e-04f, -9.559425272e-04f, +4.495080153e-04f, +2.846407623e-03f, +1.989454068e-03f, -4.491151594e-03f, -1.156100448e-02f, -1.065698581e-02f, -3.895768346e-04f, +1.023895907e-02f, +1.180960294e-02f, +5.007407400e-03f, -1.738511442e-03f, -2.938517986e-03f, -6.019203323e-04f, +9.329195550e-04f, +5.763302237e-04f, -1.134902041e-04f, -1.824770389e-04f, - /* 20, 1 */ +1.568890194e-04f, -4.728495798e-04f, -9.708996289e-04f, +3.024354413e-04f, +2.741035078e-03f, +2.215509786e-03f, -3.978754642e-03f, -1.127853170e-02f, -1.103432131e-02f, -1.167153605e-03f, +9.781544627e-03f, +1.202253469e-02f, +5.525210549e-03f, -1.462933465e-03f, -3.016077094e-03f, -7.588827792e-04f, +9.015285321e-04f, +6.268920900e-04f, -8.735502929e-05f, -1.921860197e-04f, - /* 20, 2 */ +1.741940978e-04f, -4.208194393e-04f, -9.781360984e-04f, +1.614183836e-04f, +2.623714429e-03f, +2.416571115e-03f, -3.472449249e-03f, -1.096411041e-02f, -1.136986548e-02f, -1.940007910e-03f, +9.286243980e-03f, +1.219815240e-02f, +6.042182027e-03f, -1.163118517e-03f, -3.077830056e-03f, -9.195341683e-04f, +8.615147935e-04f, +6.760417132e-04f, -5.827810709e-05f, -2.008073985e-04f, - /* 20, 3 */ +1.886370492e-04f, -3.691494362e-04f, -9.780356474e-04f, +2.709710175e-05f, +2.495775707e-03f, +2.592671089e-03f, -2.974378699e-03f, -1.061978749e-02f, -1.166272581e-02f, -2.705018824e-03f, +8.754750074e-03f, +1.233496472e-02f, +6.555887236e-03f, -8.396136973e-04f, -3.122565398e-03f, -1.082944596e-03f, +8.126754773e-04f, +7.232876858e-04f, -2.630548656e-05f, -2.081598890e-04f, - /* 20, 4 */ +2.002956356e-04f, -3.182221327e-04f, -9.710157868e-04f, -9.996474913e-05f, +2.358556029e-03f, +2.743978910e-03f, -2.486586970e-03f, -1.024771819e-02f, -1.191222039e-02f, -3.459106327e-03f, +8.188939591e-03f, +1.243164652e-02f, +7.063848473e-03f, -4.931158341e-04f, -3.149124311e-03f, -1.248118895e-03f, +7.548636055e-04f, +7.681251779e-04f, +8.487693398e-06f, -2.140618814e-04f, - /* 20, 5 */ +2.092671085e-04f, -2.683920446e-04f, -9.575231021e-04f, -2.192806382e-04f, +2.213390969e-03f, +2.870794883e-03f, -2.011009640e-03f, -9.850152742e-03f, -1.211787969e-02f, -4.199247312e-03f, +7.590864160e-03f, +1.248704815e-02f, +7.563557889e-03f, -1.244715801e-04f, -3.156409832e-03f, -1.414000676e-03f, +6.879919170e-04f, +8.100393630e-04f, +4.599617124e-05f, -2.183332212e-04f, - /* 20, 6 */ +2.156662323e-04f, -2.199842607e-04f, -9.380285157e-04f, -3.304411223e-04f, +2.061606212e-03f, +2.973544666e-03f, -1.549465619e-03f, -9.429422833e-03f, -1.227944713e-02f, -4.922491262e-03f, +6.962740532e-03f, +1.250020393e-02f, +8.052490864e-03f, +2.653234199e-04f, -3.143395899e-03f, -1.579476941e-03f, +6.120364145e-04f, +8.485090918e-04f, +8.608374363e-05f, -2.207970734e-04f, - /* 20, 7 */ +2.196232573e-04f, -1.732933680e-04f, -9.130225739e-04f, -4.331132727e-04f, +1.904509553e-03f, +3.052772891e-03f, -1.103649750e-03f, -8.987927630e-03f, -1.239687839e-02f, -5.625975475e-03f, +6.306939763e-03f, +1.247033951e-02f, +8.528119711e-03f, +6.751263085e-04f, -3.109136222e-03f, -1.743383273e-03f, +5.270395872e-04f, +8.830107920e-04f, +1.285826773e-04f, -2.212818602e-04f, - /* 20, 8 */ +2.212818602e-04f, -1.285826773e-04f, -8.830107920e-04f, -5.270395872e-04f, +1.743383273e-03f, +3.109136222e-03f, -6.751263085e-04f, -8.528119711e-03f, -1.247033951e-02f, -6.306939763e-03f, +5.625975475e-03f, +1.239687839e-02f, +8.987927630e-03f, +1.103649750e-03f, -3.052772891e-03f, -1.904509553e-03f, +4.331132727e-04f, +9.130225739e-04f, +1.732933680e-04f, -2.196232573e-04f, - /* 20, 9 */ +2.207970734e-04f, -8.608374363e-05f, -8.485090918e-04f, -6.120364145e-04f, +1.579476941e-03f, +3.143395899e-03f, -2.653234199e-04f, -8.052490864e-03f, -1.250020393e-02f, -6.962740532e-03f, +4.922491262e-03f, +1.227944713e-02f, +9.429422833e-03f, +1.549465619e-03f, -2.973544666e-03f, -2.061606212e-03f, +3.304411223e-04f, +9.380285157e-04f, +2.199842607e-04f, -2.156662323e-04f, - /* 20,10 */ +2.183332212e-04f, -4.599617124e-05f, -8.100393630e-04f, -6.879919170e-04f, +1.414000676e-03f, +3.156409832e-03f, +1.244715801e-04f, -7.563557889e-03f, -1.248704815e-02f, -7.590864160e-03f, +4.199247312e-03f, +1.211787969e-02f, +9.850152742e-03f, +2.011009640e-03f, -2.870794883e-03f, -2.213390969e-03f, +2.192806382e-04f, +9.575231021e-04f, +2.683920446e-04f, -2.092671085e-04f, - /* 20,11 */ +2.140618814e-04f, -8.487693398e-06f, -7.681251779e-04f, -7.548636055e-04f, +1.248118895e-03f, +3.149124311e-03f, +4.931158341e-04f, -7.063848473e-03f, -1.243164652e-02f, -8.188939591e-03f, +3.459106327e-03f, +1.191222039e-02f, +1.024771819e-02f, +2.486586970e-03f, -2.743978910e-03f, -2.358556029e-03f, +9.996474913e-05f, +9.710157868e-04f, +3.182221327e-04f, -2.002956356e-04f, - /* 20,12 */ +2.081598890e-04f, +2.630548656e-05f, -7.232876858e-04f, -8.126754773e-04f, +1.082944596e-03f, +3.122565398e-03f, +8.396136973e-04f, -6.555887236e-03f, -1.233496472e-02f, -8.754750074e-03f, +2.705018824e-03f, +1.166272581e-02f, +1.061978749e-02f, +2.974378699e-03f, -2.592671089e-03f, -2.495775707e-03f, -2.709710175e-05f, +9.780356474e-04f, +3.691494362e-04f, -1.886370492e-04f, - /* 20,13 */ +2.008073985e-04f, +5.827810709e-05f, -6.760417132e-04f, -8.615147935e-04f, +9.195341683e-04f, +3.077830056e-03f, +1.163118517e-03f, -6.042182027e-03f, -1.219815240e-02f, -9.286243980e-03f, +1.940007910e-03f, +1.136986548e-02f, +1.096411041e-02f, +3.472449249e-03f, -2.416571115e-03f, -2.623714429e-03f, -1.614183836e-04f, +9.781360984e-04f, +4.208194393e-04f, -1.741940978e-04f, - /* 20,14 */ +1.921860197e-04f, +8.735502929e-05f, -6.268920900e-04f, -9.015285321e-04f, +7.588827792e-04f, +3.016077094e-03f, +1.462933465e-03f, -5.525210549e-03f, -1.202253469e-02f, -9.781544627e-03f, +1.167153605e-03f, +1.103432131e-02f, +1.127853170e-02f, +3.978754642e-03f, -2.215509786e-03f, -2.741035078e-03f, -3.024354413e-04f, +9.708996289e-04f, +4.728495798e-04f, -1.568890194e-04f, - /* 20,15 */ +1.824770389e-04f, +1.134902041e-04f, -5.763302237e-04f, -9.329195550e-04f, +6.019203323e-04f, +2.938517986e-03f, +1.738511442e-03f, -5.007407400e-03f, -1.180960294e-02f, -1.023895907e-02f, +3.895768346e-04f, +1.065698581e-02f, +1.156100448e-02f, +4.491151594e-03f, -1.989454068e-03f, -2.846407623e-03f, -4.495080153e-04f, +9.559425272e-04f, +5.248309364e-04f, -1.366654441e-04f, - /* 20, 0 */ +2.228492143e-04f, +8.155042897e-05f, -9.008994790e-04f, -8.358434283e-04f, +2.057950411e-03f, +3.687980724e-03f, -2.439509438e-03f, -1.276984908e-02f, -1.372824692e-02f, -5.233437973e-04f, +1.325794606e-02f, +1.324423486e-02f, +3.079014715e-03f, -3.569583683e-03f, -2.275574997e-03f, +7.329307333e-04f, +9.595727172e-04f, -3.951670647e-05f, -2.357435643e-04f, +0.000000000e+00f, - /* 20, 1 */ +2.085936177e-04f, +1.192685572e-04f, -8.383784628e-04f, -9.252931617e-04f, +1.836793834e-03f, +3.772148819e-03f, -1.820843931e-03f, -1.225552529e-02f, -1.413563751e-02f, -1.567452511e-03f, +1.272631251e-02f, +1.367510758e-02f, +3.736377939e-03f, -3.415952420e-03f, -2.487640644e-03f, +6.166326985e-04f, +1.013551226e-03f, +6.721135679e-06f, -2.469830254e-04f, +3.513221827e-04f, - /* 20, 2 */ +1.932641301e-04f, +1.526114972e-04f, -7.728409668e-04f, -1.001322392e-03f, +1.614057279e-03f, +3.823286477e-03f, -1.225775962e-03f, -1.170500117e-02f, -1.447891891e-02f, -2.603840968e-03f, +1.213529571e-02f, +1.405908160e-02f, +4.408408028e-03f, -3.226289363e-03f, -2.692058620e-03f, +4.871526668e-04f, +1.061979909e-03f, +5.699759108e-05f, -2.562708188e-04f, -2.243392330e-05f, - /* 20, 3 */ +1.771389305e-04f, +1.815689683e-04f, -7.050956564e-04f, -1.064087220e-03f, +1.391605775e-03f, +3.842769943e-03f, -6.568264230e-04f, -1.112214974e-02f, -1.475727496e-02f, -3.627416831e-03f, +1.148720750e-02f, +1.439298630e-02f, +5.091721334e-03f, -3.000017933e-03f, -2.886692602e-03f, +3.448244648e-04f, +1.104003505e-03f, +1.110914327e-04f, -2.633104157e-04f, -3.471505729e-05f, - /* 20, 4 */ +1.604844080e-04f, +2.061770649e-04f, -6.359221544e-04f, -1.113850250e-03f, +1.171206475e-03f, +3.832136817e-03f, -1.162685315e-04f, -1.051095143e-02f, -1.497027375e-02f, -4.633169088e-03f, +1.078471010e-02f, +1.467389068e-02f, +5.782760145e-03f, -2.736794515e-03f, -3.069373786e-03f, +1.901162062e-04f, +1.138774993e-03f, +1.687245284e-04f, -2.678091338e-04f, -4.805250238e-05f, - /* 20, 5 */ +1.435528424e-04f, +2.265149998e-04f, -5.660652366e-04f, -1.150972836e-03f, +9.545189950e-04f, +3.793068954e-03f, +3.938808876e-04f, -9.875465824e-03f, -1.511786634e-02f, -5.616199746e-03f, +1.003080152e-02f, +1.489912656e-02f, +6.477812874e-03f, -2.436518983e-03f, -3.237916857e-03f, +2.363306300e-05f, +1.165464351e-03f, +2.295611999e-04f, -2.694819135e-04f, -6.235351094e-05f, - /* 20, 6 */ +1.265803873e-04f, +2.427015763e-04f, -4.962296614e-04f, -1.175906803e-03f, +7.430870045e-04f, +3.727374795e-03f, +8.718680321e-04f, -9.219803451e-03f, -1.520038286e-02f, -6.571754682e-03f, +9.228798617e-03f, +1.506631023e-02f, +7.173035830e-03f, -2.099343642e-03f, -3.390136682e-03f, -1.538810619e-04f, +1.183267602e-03f, +2.932081598e-04f, -2.680552395e-04f, -7.750368078e-05f, - /* 20, 7 */ +1.097853637e-04f, +2.548914413e-04f, -4.270756535e-04f, -1.189185758e-03f, +5.383311068e-04f, +3.636971298e-03f, +1.316206650e-03f, -8.548097577e-03f, -1.521852609e-02f, -7.495253444e-03f, +8.382317770e-03f, +1.517336238e-02f, +7.864476409e-03f, -1.725680482e-03f, -3.523865616e-03f, -3.415430197e-04f, +1.191416067e-03f, +3.592150564e-04f, -2.632711715e-04f, -9.336686615e-05f, - /* 20, 8 */ +9.336686615e-05f, +2.632711715e-04f, -3.592150564e-04f, -1.191416067e-03f, +3.415430197e-04f, +3.523865616e-03f, +1.725680482e-03f, -7.864476409e-03f, -1.517336238e-02f, -8.382317770e-03f, +7.495253444e-03f, +1.521852609e-02f, +8.548097577e-03f, -1.316206650e-03f, -3.636971298e-03f, -5.383311068e-04f, +1.189185758e-03f, +4.270756535e-04f, -2.548914413e-04f, -1.097853637e-04f, - /* 20, 9 */ +7.750368078e-05f, +2.680552395e-04f, -2.932081598e-04f, -1.183267602e-03f, +1.538810619e-04f, +3.390136682e-03f, +2.099343642e-03f, -7.173035830e-03f, -1.506631023e-02f, -9.228798617e-03f, +6.571754682e-03f, +1.520038286e-02f, +9.219803451e-03f, -8.718680321e-04f, -3.727374795e-03f, -7.430870045e-04f, +1.175906803e-03f, +4.962296614e-04f, -2.427015763e-04f, -1.265803873e-04f, - /* 20,10 */ +6.235351094e-05f, +2.694819135e-04f, -2.295611999e-04f, -1.165464351e-03f, -2.363306300e-05f, +3.237916857e-03f, +2.436518983e-03f, -6.477812874e-03f, -1.489912656e-02f, -1.003080152e-02f, +5.616199746e-03f, +1.511786634e-02f, +9.875465824e-03f, -3.938808876e-04f, -3.793068954e-03f, -9.545189950e-04f, +1.150972836e-03f, +5.660652366e-04f, -2.265149998e-04f, -1.435528424e-04f, - /* 20,11 */ +4.805250238e-05f, +2.678091338e-04f, -1.687245284e-04f, -1.138774993e-03f, -1.901162062e-04f, +3.069373786e-03f, +2.736794515e-03f, -5.782760145e-03f, -1.467389068e-02f, -1.078471010e-02f, +4.633169088e-03f, +1.497027375e-02f, +1.051095143e-02f, +1.162685315e-04f, -3.832136817e-03f, -1.171206475e-03f, +1.113850250e-03f, +6.359221544e-04f, -2.061770649e-04f, -1.604844080e-04f, - /* 20,12 */ +3.471505729e-05f, +2.633104157e-04f, -1.110914327e-04f, -1.104003505e-03f, -3.448244648e-04f, +2.886692602e-03f, +3.000017933e-03f, -5.091721334e-03f, -1.439298630e-02f, -1.148720750e-02f, +3.627416831e-03f, +1.475727496e-02f, +1.112214974e-02f, +6.568264230e-04f, -3.842769943e-03f, -1.391605775e-03f, +1.064087220e-03f, +7.050956564e-04f, -1.815689683e-04f, -1.771389305e-04f, - /* 20,13 */ +2.243392330e-05f, +2.562708188e-04f, -5.699759108e-05f, -1.061979909e-03f, -4.871526668e-04f, +2.692058620e-03f, +3.226289363e-03f, -4.408408028e-03f, -1.405908160e-02f, -1.213529571e-02f, +2.603840968e-03f, +1.447891891e-02f, +1.170500117e-02f, +1.225775962e-03f, -3.823286477e-03f, -1.614057279e-03f, +1.001322392e-03f, +7.728409668e-04f, -1.526114972e-04f, -1.932641301e-04f, - /* 20,14 */ -3.513221827e-04f, +2.469830254e-04f, -6.721135679e-06f, -1.013551226e-03f, -6.166326985e-04f, +2.487640644e-03f, +3.415952420e-03f, -3.736377939e-03f, -1.367510758e-02f, -1.272631251e-02f, +1.567452511e-03f, +1.413563751e-02f, +1.225552529e-02f, +1.820843931e-03f, -3.772148819e-03f, -1.836793834e-03f, +9.252931617e-04f, +8.383784628e-04f, -1.192685572e-04f, -2.085936177e-04f, - /* 20,15 */ +0.000000000e+00f, +2.357435643e-04f, +3.951670647e-05f, -9.595727172e-04f, -7.329307333e-04f, +2.275574997e-03f, +3.569583683e-03f, -3.079014715e-03f, -1.324423486e-02f, -1.325794606e-02f, +5.233437973e-04f, +1.372824692e-02f, +1.276984908e-02f, +2.439509438e-03f, -3.687980724e-03f, -2.057950411e-03f, +8.358434283e-04f, +9.008994790e-04f, -8.155042897e-05f, -2.228492143e-04f, - /* 20, 0 */ +1.941987182e-05f, +3.146481294e-04f, -2.345645569e-04f, -1.414667200e-03f, +5.144442975e-04f, +4.454307224e-03f, +1.983750799e-04f, -1.327145644e-02f, -1.714303646e-02f, -6.846700315e-04f, +1.665178821e-02f, +1.403392762e-02f, +4.892879248e-04f, -4.540173148e-03f, -7.773192529e-04f, +1.425286503e-03f, +3.160682424e-04f, -3.205185770e-04f, -3.476344875e-05f, +0.000000000e+00f, - /* 20, 1 */ -3.929324583e-04f, +3.051602666e-04f, -1.573970191e-04f, -1.390281543e-03f, +2.638941923e-04f, +4.333213577e-03f, +8.411158857e-04f, -1.246868983e-02f, -1.754185168e-02f, -2.049974665e-03f, +1.606968443e-02f, +1.474987505e-02f, +1.218921159e-03f, -4.587812221e-03f, -1.050600845e-03f, +1.421006956e-03f, +4.012475422e-04f, -3.223256966e-04f, -5.166761157e-05f, +0.000000000e+00f, - /* 20, 2 */ +0.000000000e+00f, +2.925136688e-04f, -8.512788201e-05f, -1.353337804e-03f, +2.735561011e-05f, +4.180044295e-03f, +1.436437300e-03f, -1.163198941e-02f, -1.784731333e-02f, -3.403203680e-03f, +1.539895444e-02f, +1.541325656e-02f, +1.987128326e-03f, -4.594412557e-03f, -1.332146559e-03f, +1.400789491e-03f, +4.893452569e-04f, -3.196436833e-04f, -7.000071077e-05f, +0.000000000e+00f, - /* 20, 3 */ +0.000000000e+00f, +2.771727451e-04f, -1.822077520e-05f, -1.305102482e-03f, -1.937209193e-04f, +3.998069961e-03f, +1.982303068e-03f, -1.076779718e-02f, -1.805915757e-02f, -4.736408619e-03f, +1.464246859e-02f, +1.601826823e-02f, +2.790083541e-03f, -4.557383277e-03f, -1.619599483e-03f, +1.363706016e-03f, +5.795104543e-04f, -3.120743969e-04f, -8.959420873e-05f, +0.000000000e+00f, - /* 20, 4 */ +0.000000000e+00f, +2.596009711e-04f, +4.295843246e-05f, -1.246883037e-03f, -3.981230344e-04f, +3.790645354e-03f, +2.477139789e-03f, -9.882582946e-03f, -1.817777152e-02f, -6.041793017e-03f, +1.380372215e-02f, +1.655939544e-02f, +3.623550339e-03f, -4.474387395e-03f, -1.910400883e-03f, +1.308956662e-03f, +6.708027600e-04f, -2.992550459e-04f, -1.102423612e-04f, +0.000000000e+00f, - /* 20, 5 */ +0.000000000e+00f, +2.402546692e-04f, +9.813963752e-05f, -1.180011107e-03f, -5.848760724e-04f, +3.561175652e-03f, +2.919834686e-03f, -8.982792490e-03f, -1.820418220e-02f, -7.311771311e-03f, +1.288681385e-02f, +1.703146227e-02f, +4.482904855e-03f, -4.343373467e-03f, -2.201805423e-03f, +1.235886510e-03f, +7.621980241e-04f, -2.808658988e-04f, -1.317024534e-04f, +0.000000000e+00f, - /* 20, 6 */ +0.000000000e+00f, +2.195772899e-04f, +1.471454599e-04f, -1.105826337e-03f, -7.532402115e-04f, +3.313083439e-03f, +3.309729355e-03f, -8.074797022e-03f, -1.814004021e-02f, -8.539025902e-03f, +1.189641917e-02f, +1.742967891e-02f, +5.363163073e-03f, -4.162605659e-03f, -2.490898970e-03f, +1.144001586e-03f, +8.525953702e-04f, -2.566379213e-04f, -1.536956226e-04f, +0.000000000e+00f, - /* 20, 7 */ +0.000000000e+00f, +1.979942392e-04f, +1.898872476e-04f, -1.025661016e-03f, -9.027053553e-04f, +3.049776817e-03f, +3.646609643e-03f, -7.164844319e-03f, -1.798759853e-02f, -9.716561844e-03f, +1.083775871e-02f, +1.774968640e-02f, +6.259011965e-03f, -3.930691879e-03f, -2.774618906e-03f, +1.032983905e-03f, +9.408256132e-04f, -2.263602294e-04f, -1.759082929e-04f, +0.000000000e+00f, - /* 20, 8 */ +0.000000000e+00f, +1.759082929e-04f, +2.263602294e-04f, -9.408256132e-04f, -1.032983905e-03f, +2.774618906e-03f, +3.930691879e-03f, -6.259011965e-03f, -1.774968640e-02f, -1.083775871e-02f, +9.716561844e-03f, +1.798759853e-02f, +7.164844319e-03f, -3.646609643e-03f, -3.049776817e-03f, +9.027053553e-04f, +1.025661016e-03f, -1.898872476e-04f, -1.979942392e-04f, +0.000000000e+00f, - /* 20, 9 */ +0.000000000e+00f, +1.536956226e-04f, +2.566379213e-04f, -8.525953702e-04f, -1.144001586e-03f, +2.490898970e-03f, +4.162605659e-03f, -5.363163073e-03f, -1.742967891e-02f, -1.189641917e-02f, +8.539025902e-03f, +1.814004021e-02f, +8.074797022e-03f, -3.309729355e-03f, -3.313083439e-03f, +7.532402115e-04f, +1.105826337e-03f, -1.471454599e-04f, -2.195772899e-04f, +0.000000000e+00f, - /* 20,10 */ +0.000000000e+00f, +1.317024534e-04f, +2.808658988e-04f, -7.621980241e-04f, -1.235886510e-03f, +2.201805423e-03f, +4.343373467e-03f, -4.482904855e-03f, -1.703146227e-02f, -1.288681385e-02f, +7.311771311e-03f, +1.820418220e-02f, +8.982792490e-03f, -2.919834686e-03f, -3.561175652e-03f, +5.848760724e-04f, +1.180011107e-03f, -9.813963752e-05f, -2.402546692e-04f, +0.000000000e+00f, - /* 20,11 */ +0.000000000e+00f, +1.102423612e-04f, +2.992550459e-04f, -6.708027600e-04f, -1.308956662e-03f, +1.910400883e-03f, +4.474387395e-03f, -3.623550339e-03f, -1.655939544e-02f, -1.380372215e-02f, +6.041793017e-03f, +1.817777152e-02f, +9.882582946e-03f, -2.477139789e-03f, -3.790645354e-03f, +3.981230344e-04f, +1.246883037e-03f, -4.295843246e-05f, -2.596009711e-04f, +0.000000000e+00f, - /* 20,12 */ +0.000000000e+00f, +8.959420873e-05f, +3.120743969e-04f, -5.795104543e-04f, -1.363706016e-03f, +1.619599483e-03f, +4.557383277e-03f, -2.790083541e-03f, -1.601826823e-02f, -1.464246859e-02f, +4.736408619e-03f, +1.805915757e-02f, +1.076779718e-02f, -1.982303068e-03f, -3.998069961e-03f, +1.937209193e-04f, +1.305102482e-03f, +1.822077520e-05f, -2.771727451e-04f, +0.000000000e+00f, - /* 20,13 */ +0.000000000e+00f, +7.000071077e-05f, +3.196436833e-04f, -4.893452569e-04f, -1.400789491e-03f, +1.332146559e-03f, +4.594412557e-03f, -1.987128326e-03f, -1.541325656e-02f, -1.539895444e-02f, +3.403203680e-03f, +1.784731333e-02f, +1.163198941e-02f, -1.436437300e-03f, -4.180044295e-03f, -2.735561011e-05f, +1.353337804e-03f, +8.512788201e-05f, -2.925136688e-04f, +0.000000000e+00f, - /* 20,14 */ +0.000000000e+00f, +5.166761157e-05f, +3.223256966e-04f, -4.012475422e-04f, -1.421006956e-03f, +1.050600845e-03f, +4.587812221e-03f, -1.218921159e-03f, -1.474987505e-02f, -1.606968443e-02f, +2.049974665e-03f, +1.754185168e-02f, +1.246868983e-02f, -8.411158857e-04f, -4.333213577e-03f, -2.638941923e-04f, +1.390281543e-03f, +1.573970191e-04f, -3.051602666e-04f, +3.929324583e-04f, - /* 20,15 */ +0.000000000e+00f, +3.476344875e-05f, +3.205185770e-04f, -3.160682424e-04f, -1.425286503e-03f, +7.773192529e-04f, +4.540173148e-03f, -4.892879248e-04f, -1.403392762e-02f, -1.665178821e-02f, +6.846700315e-04f, +1.714303646e-02f, +1.327145644e-02f, -1.983750799e-04f, -4.454307224e-03f, -5.144442975e-04f, +1.414667200e-03f, +2.345645569e-04f, -3.146481294e-04f, -1.941987182e-05f, - /* 16, 0 */ +3.215659774e-04f, -1.081239301e-03f, -1.047044785e-03f, +4.045780572e-03f, +3.005074105e-03f, -1.291342297e-02f, -2.083886340e-02f, -8.761305366e-04f, +2.037274022e-02f, +1.401097590e-02f, -2.379335663e-03f, -4.351475252e-03f, +8.522542940e-04f, +1.190910327e-03f, -2.874725537e-04f, -1.571395541e-04f, - /* 16, 1 */ +3.474336395e-04f, -9.673171402e-04f, -1.215210440e-03f, +3.716713245e-03f, +3.558195313e-03f, -1.178473019e-02f, -2.117503726e-02f, -2.622305580e-03f, +1.977768827e-02f, +1.506763496e-02f, -1.682580557e-03f, -4.628640452e-03f, +6.313208395e-04f, +1.294421768e-03f, -2.448738999e-04f, -1.853334990e-04f, - /* 16, 2 */ +3.654544998e-04f, -8.509694882e-04f, -1.356620316e-03f, +3.369379821e-03f, +4.037840451e-03f, -1.063463040e-02f, -2.138131359e-02f, -4.350277043e-03f, +1.905580462e-02f, +1.607371744e-02f, -9.171630004e-04f, -4.872124601e-03f, +3.850930357e-04f, +1.389803701e-03f, -1.936024914e-04f, -2.137577131e-04f, - /* 16, 3 */ +3.760939096e-04f, -7.339202470e-04f, -1.471475134e-03f, +3.008783957e-03f, +4.443873126e-03f, -9.472744965e-03f, -2.145880202e-02f, -6.048090342e-03f, +1.821025632e-02f, +1.701970579e-02f, -8.619500088e-05f, -5.076839304e-03f, +1.147982409e-04f, +1.475048300e-03f, -1.336143134e-04f, -2.418805517e-04f, - /* 16, 4 */ +3.798900997e-04f, -6.177751723e-04f, -1.560284979e-03f, +2.639777229e-03f, +4.776853126e-03f, -8.308496634e-03f, -2.140964525e-02f, -7.704060609e-03f, +1.724526338e-02f, +1.789634168e-02f, +8.064574864e-04f, -5.237819035e-03f, -1.779495980e-04f, +1.548135431e-03f, -6.499930382e-05f, -2.691226085e-04f, - /* 16, 5 */ +3.774404835e-04f, -5.040080805e-04f, -1.623844377e-03f, +2.267013831e-03f, +5.038003695e-03f, -7.151026424e-03f, -2.123698452e-02f, -9.306876654e-03f, +1.616607109e-02f, +1.869471933e-02f, +1.756186609e-03f, -5.350281937e-03f, -4.911465534e-04f, +1.607060019e-03f, +1.200956190e-05f, -2.948629835e-04f, - /* 16, 6 */ +3.693879948e-04f, -3.939497146e-04f, -1.663205192e-03f, +1.894909522e-03f, +5.229172900e-03f, -6.009115326e-03f, -2.094491570e-02f, -1.084570103e-02f, +1.497891218e-02f, +1.940637710e-02f, +2.757662946e-03f, -5.409691072e-03f, -8.224000281e-04f, +1.649860923e-03f, +9.702877105e-05f, -3.184467584e-04f, - /* 16, 7 */ +3.564076658e-04f, -2.887792732e-04f, -1.679647798e-03f, +1.527605146e-03f, +5.352789702e-03f, -4.891111570e-03f, -2.053843663e-02f, -1.231026521e-02f, +1.369095882e-02f, +2.002338639e-02f, +3.804864118e-03f, -5.411815390e-03f, -1.168934972e-03f, +1.674650966e-03f, +1.895185724e-04f, -3.391936346e-04f, - /* 16, 8 */ +3.391936346e-04f, -1.895185724e-04f, -1.674650966e-03f, +1.168934972e-03f, +5.411815390e-03f, -3.804864118e-03f, -2.002338639e-02f, -1.369095882e-02f, +1.231026521e-02f, +2.053843663e-02f, +4.891111570e-03f, -5.352789702e-03f, -1.527605146e-03f, +1.679647798e-03f, +2.887792732e-04f, -3.564076658e-04f, - /* 16, 9 */ +3.184467584e-04f, -9.702877105e-05f, -1.649860923e-03f, +8.224000281e-04f, +5.409691072e-03f, -2.757662946e-03f, -1.940637710e-02f, -1.497891218e-02f, +1.084570103e-02f, +2.094491570e-02f, +6.009115326e-03f, -5.229172900e-03f, -1.894909522e-03f, +1.663205192e-03f, +3.939497146e-04f, -3.693879948e-04f, - /* 16,10 */ +2.948629835e-04f, -1.200956190e-05f, -1.607060019e-03f, +4.911465534e-04f, +5.350281937e-03f, -1.756186609e-03f, -1.869471933e-02f, -1.616607109e-02f, +9.306876654e-03f, +2.123698452e-02f, +7.151026424e-03f, -5.038003695e-03f, -2.267013831e-03f, +1.623844377e-03f, +5.040080805e-04f, -3.774404835e-04f, - /* 16,11 */ +2.691226085e-04f, +6.499930382e-05f, -1.548135431e-03f, +1.779495980e-04f, +5.237819035e-03f, -8.064574864e-04f, -1.789634168e-02f, -1.724526338e-02f, +7.704060609e-03f, +2.140964525e-02f, +8.308496634e-03f, -4.776853126e-03f, -2.639777229e-03f, +1.560284979e-03f, +6.177751723e-04f, -3.798900997e-04f, - /* 16,12 */ +2.418805517e-04f, +1.336143134e-04f, -1.475048300e-03f, -1.147982409e-04f, +5.076839304e-03f, +8.619500088e-05f, -1.701970579e-02f, -1.821025632e-02f, +6.048090342e-03f, +2.145880202e-02f, +9.472744965e-03f, -4.443873126e-03f, -3.008783957e-03f, +1.471475134e-03f, +7.339202470e-04f, -3.760939096e-04f, - /* 16,13 */ +2.137577131e-04f, +1.936024914e-04f, -1.389803701e-03f, -3.850930357e-04f, +4.872124601e-03f, +9.171630004e-04f, -1.607371744e-02f, -1.905580462e-02f, +4.350277043e-03f, +2.138131359e-02f, +1.063463040e-02f, -4.037840451e-03f, -3.369379821e-03f, +1.356620316e-03f, +8.509694882e-04f, -3.654544998e-04f, - /* 16,14 */ +1.853334990e-04f, +2.448738999e-04f, -1.294421768e-03f, -6.313208395e-04f, +4.628640452e-03f, +1.682580557e-03f, -1.506763496e-02f, -1.977768827e-02f, +2.622305580e-03f, +2.117503726e-02f, +1.178473019e-02f, -3.558195313e-03f, -3.716713245e-03f, +1.215210440e-03f, +9.673171402e-04f, -3.474336395e-04f, - /* 16,15 */ +1.571395541e-04f, +2.874725537e-04f, -1.190910327e-03f, -8.522542940e-04f, +4.351475252e-03f, +2.379335663e-03f, -1.401097590e-02f, -2.037274022e-02f, +8.761305366e-04f, +2.083886340e-02f, +1.291342297e-02f, -3.005074105e-03f, -4.045780572e-03f, +1.047044785e-03f, +1.081239301e-03f, -3.215659774e-04f, - /* 16, 0 */ +3.737698842e-04f, -2.640449894e-04f, -1.945694549e-03f, +2.599440145e-03f, +5.499552783e-03f, -1.161604587e-02f, -2.473725459e-02f, -1.100298137e-03f, +2.435797715e-02f, +1.306966182e-02f, -5.062036618e-03f, -3.067638325e-03f, +1.905476637e-03f, +3.919780470e-04f, -3.991665042e-04f, +0.000000000e+00f, - /* 16, 1 */ +3.444161169e-04f, -1.448617079e-04f, -1.955541719e-03f, +2.132654952e-03f, +5.839402648e-03f, -1.015371093e-02f, -2.494083956e-02f, -3.291998323e-03f, +2.380252288e-02f, +1.450063212e-02f, -4.525267650e-03f, -3.530814272e-03f, +1.832921116e-03f, +5.273022833e-04f, -4.195866486e-04f, +0.000000000e+00f, - /* 16, 2 */ +3.121018536e-04f, -3.553225965e-05f, -1.937280777e-03f, +1.673279684e-03f, +6.084169165e-03f, -8.696195593e-03f, -2.497087446e-02f, -5.457102987e-03f, +2.307209479e-02f, +1.589478690e-02f, -3.888719495e-03f, -3.982156136e-03f, +1.726408630e-03f, +6.684076945e-04f, -4.340068349e-04f, +0.000000000e+00f, - /* 16, 3 */ +2.777843660e-04f, +6.309259068e-05f, -1.893417136e-03f, +1.226820211e-03f, +6.237358144e-03f, -7.256513778e-03f, -2.483111182e-02f, -7.578189778e-03f, +2.216959356e-02f, +1.723786448e-02f, -3.152978451e-03f, -4.414545490e-03f, +1.584716951e-03f, +8.134406195e-04f, -4.414195390e-04f, +4.634120047e-04f, - /* 16, 4 */ +2.423668462e-04f, +1.504100330e-04f, -1.826645633e-03f, +7.982477790e-04f, +6.303316031e-03f, -5.847027899e-03f, -2.452684878e-02f, -9.638294429e-03f, +2.109960841e-02f, +1.851566754e-02f, -2.319783877e-03f, -4.820635148e-03f, +1.407067591e-03f, +9.603162700e-04f, -4.408546251e-04f, -2.338334874e-05f, - /* 16, 5 */ +2.066858426e-04f, +2.260561294e-04f, -1.739797615e-03f, +3.919648528e-04f, +6.287140435e-03f, -4.479333074e-03f, -2.406484480e-02f, -1.162108621e-02f, +1.986838848e-02f, +1.971422226e-02f, -1.392055451e-03f, -5.192933984e-03f, +1.193168502e-03f, +1.106736135e-03f, -4.314017719e-04f, -4.782938304e-05f, - /* 16, 6 */ +1.715009195e-04f, +2.898933732e-04f, -1.635789255e-03f, +1.178042715e-05f, +6.194584811e-03f, -3.164153635e-03f, -2.345322399e-02f, -1.351103572e-02f, +1.848379497e-02f, +2.081993840e-02f, -3.739065234e-04f, -5.523897843e-03f, +9.432519997e-04f, +1.250210274e-03f, -4.122335402e-04f, -7.520965874e-05f, - /* 16, 7 */ +1.374865801e-04f, +3.419953130e-04f, -1.517571800e-03f, -3.391052954e-04f, +6.031958779e-03f, -1.911252903e-03f, -2.270136331e-02f, -1.529357304e-02f, +1.695523429e-02f, +2.181976838e-02f, +7.293570292e-04f, -5.806025538e-03f, +6.581070752e-04f, +1.388084428e-03f, -3.826286881e-04f, -1.052264476e-04f, - /* 16, 8 */ +1.052264476e-04f, +3.826286881e-04f, -1.388084428e-03f, -6.581070752e-04f, +5.806025538e-03f, -7.293570292e-04f, -2.181976838e-02f, -1.695523429e-02f, +1.529357304e-02f, +2.270136331e-02f, +1.911252903e-03f, -6.031958779e-03f, +3.391052954e-04f, +1.517571800e-03f, -3.419953130e-04f, -1.374865801e-04f, - /* 16, 9 */ +7.520965874e-05f, +4.122335402e-04f, -1.250210274e-03f, -9.432519997e-04f, +5.523897843e-03f, +3.739065234e-04f, -2.081993840e-02f, -1.848379497e-02f, +1.351103572e-02f, +2.345322399e-02f, +3.164153635e-03f, -6.194584811e-03f, -1.178042715e-05f, +1.635789255e-03f, -2.898933732e-04f, -1.715009195e-04f, - /* 16,10 */ +4.782938304e-05f, +4.314017719e-04f, -1.106736135e-03f, -1.193168502e-03f, +5.192933984e-03f, +1.392055451e-03f, -1.971422226e-02f, -1.986838848e-02f, +1.162108621e-02f, +2.406484480e-02f, +4.479333074e-03f, -6.287140435e-03f, -3.919648528e-04f, +1.739797615e-03f, -2.260561294e-04f, -2.066858426e-04f, - /* 16,11 */ +2.338334874e-05f, +4.408546251e-04f, -9.603162700e-04f, -1.407067591e-03f, +4.820635148e-03f, +2.319783877e-03f, -1.851566754e-02f, -2.109960841e-02f, +9.638294429e-03f, +2.452684878e-02f, +5.847027899e-03f, -6.303316031e-03f, -7.982477790e-04f, +1.826645633e-03f, -1.504100330e-04f, -2.423668462e-04f, - /* 16,12 */ -4.634120047e-04f, +4.414195390e-04f, -8.134406195e-04f, -1.584716951e-03f, +4.414545490e-03f, +3.152978451e-03f, -1.723786448e-02f, -2.216959356e-02f, +7.578189778e-03f, +2.483111182e-02f, +7.256513778e-03f, -6.237358144e-03f, -1.226820211e-03f, +1.893417136e-03f, -6.309259068e-05f, -2.777843660e-04f, - /* 16,13 */ +0.000000000e+00f, +4.340068349e-04f, -6.684076945e-04f, -1.726408630e-03f, +3.982156136e-03f, +3.888719495e-03f, -1.589478690e-02f, -2.307209479e-02f, +5.457102987e-03f, +2.497087446e-02f, +8.696195593e-03f, -6.084169165e-03f, -1.673279684e-03f, +1.937280777e-03f, +3.553225965e-05f, -3.121018536e-04f, - /* 16,14 */ +0.000000000e+00f, +4.195866486e-04f, -5.273022833e-04f, -1.832921116e-03f, +3.530814272e-03f, +4.525267650e-03f, -1.450063212e-02f, -2.380252288e-02f, +3.291998323e-03f, +2.494083956e-02f, +1.015371093e-02f, -5.839402648e-03f, -2.132654952e-03f, +1.955541719e-03f, +1.448617079e-04f, -3.444161169e-04f, - /* 16,15 */ +0.000000000e+00f, +3.991665042e-04f, -3.919780470e-04f, -1.905476637e-03f, +3.067638325e-03f, +5.062036618e-03f, -1.306966182e-02f, -2.435797715e-02f, +1.100298137e-03f, +2.473725459e-02f, +1.161604587e-02f, -5.499552783e-03f, -2.599440145e-03f, +1.945694549e-03f, +2.640449894e-04f, -3.737698842e-04f, - /* 16, 0 */ +1.129954761e-04f, +3.969443331e-04f, -1.897918409e-03f, +5.803605804e-04f, +7.236474393e-03f, -9.385964725e-03f, -2.874576735e-02f, -1.359743295e-03f, +2.853093461e-02f, +1.118139232e-02f, -7.102956997e-03f, -1.091735034e-03f, +2.023521864e-03f, -3.328791130e-04f, -1.523656204e-04f, +0.000000000e+00f, - /* 16, 1 */ +7.672972562e-05f, +4.458313625e-04f, -1.753034729e-03f, +1.022461377e-04f, +7.257902118e-03f, -7.620475041e-03f, -2.873131200e-02f, -4.066570788e-03f, +2.808336987e-02f, +1.298853535e-02f, -6.850603202e-03f, -1.630677017e-03f, +2.125649126e-03f, -2.532507071e-04f, -1.941703587e-04f, +0.000000000e+00f, - /* 16, 2 */ +4.409159553e-05f, +4.801879450e-04f, -1.593056257e-03f, -3.378401438e-04f, +7.175055211e-03f, -5.902082228e-03f, -2.849345261e-02f, -6.735572940e-03f, +2.740215275e-02f, +1.478830973e-02f, -6.473886365e-03f, -2.190599691e-03f, +2.200171639e-03f, -1.579695096e-04f, -2.375950982e-04f, +0.000000000e+00f, - /* 16, 3 */ -4.855802427e-04f, +5.008892512e-04f, -1.422085430e-03f, -7.360682089e-04f, +6.996656391e-03f, -4.246700138e-03f, -2.804039906e-02f, -9.342037235e-03f, +2.648893755e-02f, +1.656098957e-02f, -5.968640123e-03f, -2.764068406e-03f, +2.243110553e-03f, -4.727037370e-05f, -2.816859426e-04f, +0.000000000e+00f, - /* 16, 4 */ +0.000000000e+00f, +5.090007623e-04f, -1.244075649e-03f, -1.089544232e-03f, +6.732169339e-03f, -2.668838337e-03f, -2.738254409e-02f, -1.186200034e-02f, +2.534797081e-02f, +1.828644204e-02f, -5.332184360e-03f, -3.342863857e-03f, +2.250722132e-03f, +7.826276448e-05f, -3.253590588e-04f, +0.000000000e+00f, - /* 16, 5 */ +0.000000000e+00f, +5.057402285e-04f, -1.062772468e-03f, -1.396293958e-03f, +6.391628670e-03f, -1.181467029e-03f, -2.653229393e-02f, -1.427253312e-02f, +2.398607480e-02f, +1.994437434e-02f, -4.563434465e-03f, -3.918061651e-03f, +2.219584858e-03f, +2.176775461e-04f, -3.674140144e-04f, +0.000000000e+00f, - /* 16, 6 */ +0.000000000e+00f, +4.924395299e-04f, -8.816626027e-04f, -1.655232286e-03f, +5.985469320e-03f, +2.040926394e-04f, -2.550387540e-02f, -1.655201127e-02f, +2.241259678e-02f, +2.151458926e-02f, -3.662991573e-03f, -4.480127768e-03f, +2.146686747e-03f, +3.696399185e-04f, -4.065510896e-04f, +0.000000000e+00f, - /* 16, 7 */ +0.000000000e+00f, +4.705072223e-04f, -7.039312026e-04f, -1.866120323e-03f, +5.524357980e-03f, +1.478252020e-03f, -2.431312252e-02f, -1.868036788e-02f, +2.063932435e-02f, +2.297724601e-02f, -2.633211707e-03f, -5.019029099e-03f, +2.029511283e-03f, +5.324276437e-04f, -4.413924818e-04f, +0.000000000e+00f, - /* 16, 8 */ +0.000000000e+00f, +4.413924818e-04f, -5.324276437e-04f, -2.029511283e-03f, +5.019029099e-03f, +2.633211707e-03f, -2.297724601e-02f, -2.063932435e-02f, +1.868036788e-02f, +2.431312252e-02f, -1.478252020e-03f, -5.524357980e-03f, +1.866120323e-03f, +7.039312026e-04f, -4.705072223e-04f, +0.000000000e+00f, - /* 16, 9 */ +0.000000000e+00f, +4.065510896e-04f, -3.696399185e-04f, -2.146686747e-03f, +4.480127768e-03f, +3.662991573e-03f, -2.151458926e-02f, -2.241259678e-02f, +1.655201127e-02f, +2.550387540e-02f, -2.040926394e-04f, -5.985469320e-03f, +1.655232286e-03f, +8.816626027e-04f, -4.924395299e-04f, +0.000000000e+00f, - /* 16,10 */ +0.000000000e+00f, +3.674140144e-04f, -2.176775461e-04f, -2.219584858e-03f, +3.918061651e-03f, +4.563434465e-03f, -1.994437434e-02f, -2.398607480e-02f, +1.427253312e-02f, +2.653229393e-02f, +1.181467029e-03f, -6.391628670e-03f, +1.396293958e-03f, +1.062772468e-03f, -5.057402285e-04f, +0.000000000e+00f, - /* 16,11 */ +0.000000000e+00f, +3.253590588e-04f, -7.826276448e-05f, -2.250722132e-03f, +3.342863857e-03f, +5.332184360e-03f, -1.828644204e-02f, -2.534797081e-02f, +1.186200034e-02f, +2.738254409e-02f, +2.668838337e-03f, -6.732169339e-03f, +1.089544232e-03f, +1.244075649e-03f, -5.090007623e-04f, +0.000000000e+00f, - /* 16,12 */ +0.000000000e+00f, +2.816859426e-04f, +4.727037370e-05f, -2.243110553e-03f, +2.764068406e-03f, +5.968640123e-03f, -1.656098957e-02f, -2.648893755e-02f, +9.342037235e-03f, +2.804039906e-02f, +4.246700138e-03f, -6.996656391e-03f, +7.360682089e-04f, +1.422085430e-03f, -5.008892512e-04f, +4.855802427e-04f, - /* 16,13 */ +0.000000000e+00f, +2.375950982e-04f, +1.579695096e-04f, -2.200171639e-03f, +2.190599691e-03f, +6.473886365e-03f, -1.478830973e-02f, -2.740215275e-02f, +6.735572940e-03f, +2.849345261e-02f, +5.902082228e-03f, -7.175055211e-03f, +3.378401438e-04f, +1.593056257e-03f, -4.801879450e-04f, -4.409159553e-05f, - /* 16,14 */ +0.000000000e+00f, +1.941703587e-04f, +2.532507071e-04f, -2.125649126e-03f, +1.630677017e-03f, +6.850603202e-03f, -1.298853535e-02f, -2.808336987e-02f, +4.066570788e-03f, +2.873131200e-02f, +7.620475041e-03f, -7.257902118e-03f, -1.022461377e-04f, +1.753034729e-03f, -4.458313625e-04f, -7.672972562e-05f, - /* 16,15 */ +0.000000000e+00f, +1.523656204e-04f, +3.328791130e-04f, -2.023521864e-03f, +1.091735034e-03f, +7.102956997e-03f, -1.118139232e-02f, -2.853093461e-02f, +1.359743295e-03f, +2.874576735e-02f, +9.385964725e-03f, -7.236474393e-03f, -5.803605804e-04f, +1.897918409e-03f, -3.969443331e-04f, -1.129954761e-04f, - /* 12, 0 */ -1.111572639e-03f, -1.388266820e-03f, +7.900037037e-03f, -6.320860170e-03f, -3.276049121e-02f, -1.657033928e-03f, +3.280306521e-02f, +8.402419704e-03f, -8.147060845e-03f, +9.779320530e-04f, +1.332890330e-03f, -5.705687800e-04f, - /* 12, 1 */ -8.925738220e-04f, -1.737094155e-03f, +7.544883174e-03f, -4.325531156e-03f, -3.242830266e-02f, -4.953502968e-03f, +3.254754550e-02f, +1.054842384e-02f, -8.272560314e-03f, +5.083818205e-04f, +1.551863117e-03f, -5.805594968e-04f, - /* 12, 2 */ -6.800837112e-04f, -2.023419107e-03f, +7.095763901e-03f, -2.436087367e-03f, -3.181840805e-02f, -8.197416535e-03f, +3.198906769e-02f, +1.273520115e-02f, -8.264181830e-03f, -1.673924732e-05f, +1.763414955e-03f, -5.764989135e-04f, - /* 12, 3 */ -4.777710304e-04f, -2.247467778e-03f, +6.567344318e-03f, -6.698479312e-04f, -3.094591156e-02f, -1.135453705e-02f, +3.112651563e-02f, +1.493747887e-02f, -8.110878239e-03f, -5.924008684e-04f, +1.962133432e-03f, -5.566237283e-04f, - /* 12, 4 */ -2.887507612e-04f, -2.410593616e-03f, +5.974525144e-03f, +9.583644900e-04f, -2.982883555e-02f, -1.439181272e-02f, +2.996260004e-02f, +1.712870168e-02f, -7.803165138e-03f, -1.212178752e-03f, +2.142359210e-03f, -5.193755958e-04f, - /* 12, 5 */ -1.155657138e-04f, -2.515168800e-03f, +5.332187403e-03f, +2.436339775e-03f, -2.848780461e-02f, -1.727782533e-02f, +2.850388027e-02f, +1.928138098e-02f, -7.333362623e-03f, -1.868272468e-03f, +2.298288008e-03f, -4.634616378e-04f, - /* 12, 6 */ +3.981829456e-05f, -2.564463655e-03f, +4.654950666e-03f, +3.754551105e-03f, -2.694569661e-02f, -1.998321224e-02f, +2.676072816e-02f, +2.136746929e-02f, -6.695817566e-03f, -2.551550686e-03f, +2.424083786e-03f, -3.879138191e-04f, - /* 12, 7 */ +1.760045094e-04f, -2.562517141e-03f, +3.956948521e-03f, +4.906180706e-03f, -2.522726725e-02f, -2.248105576e-02f, +2.474723407e-02f, +2.335875442e-02f, -5.887101657e-03f, -3.251624436e-03f, +2.514001460e-03f, -2.921456350e-04f, - /* 12, 8 */ +2.921456350e-04f, -2.514001460e-03f, +3.251624436e-03f, +5.887101657e-03f, -2.335875442e-02f, -2.474723407e-02f, +2.248105576e-02f, +2.522726725e-02f, -4.906180706e-03f, -3.956948521e-03f, +2.562517141e-03f, -1.760045094e-04f, - /* 12, 9 */ +3.879138191e-04f, -2.424083786e-03f, +2.551550686e-03f, +6.695817566e-03f, -2.136746929e-02f, -2.676072816e-02f, +1.998321224e-02f, +2.694569661e-02f, -3.754551105e-03f, -4.654950666e-03f, +2.564463655e-03f, -3.981829456e-05f, - /* 12,10 */ +4.634616378e-04f, -2.298288008e-03f, +1.868272468e-03f, +7.333362623e-03f, -1.928138098e-02f, -2.850388027e-02f, +1.727782533e-02f, +2.848780461e-02f, -2.436339775e-03f, -5.332187403e-03f, +2.515168800e-03f, +1.155657138e-04f, - /* 12,11 */ +5.193755958e-04f, -2.142359210e-03f, +1.212178752e-03f, +7.803165138e-03f, -1.712870168e-02f, -2.996260004e-02f, +1.439181272e-02f, +2.982883555e-02f, -9.583644900e-04f, -5.974525144e-03f, +2.410593616e-03f, +2.887507612e-04f, - /* 12,12 */ +5.566237283e-04f, -1.962133432e-03f, +5.924008684e-04f, +8.110878239e-03f, -1.493747887e-02f, -3.112651563e-02f, +1.135453705e-02f, +3.094591156e-02f, +6.698479312e-04f, -6.567344318e-03f, +2.247467778e-03f, +4.777710304e-04f, - /* 12,13 */ +5.764989135e-04f, -1.763414955e-03f, +1.673924732e-05f, +8.264181830e-03f, -1.273520115e-02f, -3.198906769e-02f, +8.197416535e-03f, +3.181840805e-02f, +2.436087367e-03f, -7.095763901e-03f, +2.023419107e-03f, +6.800837112e-04f, - /* 12,14 */ +5.805594968e-04f, -1.551863117e-03f, -5.083818205e-04f, +8.272560314e-03f, -1.054842384e-02f, -3.254754550e-02f, +4.953502968e-03f, +3.242830266e-02f, +4.325531156e-03f, -7.544883174e-03f, +1.737094155e-03f, +8.925738220e-04f, - /* 12,15 */ +5.705687800e-04f, -1.332890330e-03f, -9.779320530e-04f, +8.147060845e-03f, -8.402419704e-03f, -3.280306521e-02f, +1.657033928e-03f, +3.276049121e-02f, +6.320860170e-03f, -7.900037037e-03f, +1.388266820e-03f, +1.111572639e-03f, - /* 12, 0 */ -1.054803383e-04f, -2.744858958e-03f, +7.370914553e-03f, -2.604494739e-03f, -3.666896703e-02f, -1.994735221e-03f, +3.707596726e-02f, +4.873177855e-03f, -8.012348726e-03f, +2.554514858e-03f, +3.117958677e-04f, -3.625540242e-04f, - /* 12, 1 */ +7.775923585e-05f, -2.860662969e-03f, +6.648820026e-03f, -4.950753709e-04f, -3.590728113e-02f, -5.960234251e-03f, +3.711196787e-02f, +7.277671607e-03f, -8.552819277e-03f, +2.286292242e-03f, +5.385913036e-04f, -4.265219564e-04f, - /* 12, 2 */ +2.361482435e-04f, -2.906545541e-03f, +5.866306097e-03f, +1.435258618e-03f, -3.481183398e-02f, -9.854190425e-03f, +3.676562700e-02f, +9.791136525e-03f, -8.972352970e-03f, +1.938320040e-03f, +7.824350015e-04f, -4.875429386e-04f, - /* 12, 3 */ +3.687004846e-04f, -2.888204359e-03f, +5.043181884e-03f, +3.170488652e-03f, -3.340772759e-02f, -1.363013975e-02f, +3.603085731e-02f, +1.238366322e-02f, -9.251714734e-03f, +1.510362297e-03f, +1.039066351e-03f, -5.431259501e-04f, - /* 12, 4 */ +4.751685216e-04f, -2.812206203e-03f, +4.198484259e-03f, +4.698496481e-03f, -3.172374637e-02f, -1.724344185e-02f, +3.490702283e-02f, +1.502265637e-02f, -9.372823891e-03f, +1.003961424e-03f, +1.303424694e-03f, -5.906251216e-04f, - /* 12, 5 */ +5.559799195e-04f, -2.685773447e-03f, +3.350171906e-03f, +6.011087921e-03f, -2.979181001e-02f, -2.065196332e-02f, +3.339904530e-02f, +1.767328103e-02f, -9.319170237e-03f, +4.225520445e-04f, +1.569701578e-03f, -6.273034189e-04f, - /* 12, 6 */ +6.121620582e-04f, -2.516571985e-03f, +2.514858275e-03f, +7.103945228e-03f, -2.764638515e-02f, -2.381671619e-02f, +3.151741694e-02f, +2.029896461e-02f, -9.076221424e-03f, -2.284590483e-04f, +1.831416829e-03f, -6.504054889e-04f, - /* 12, 7 */ +6.452583046e-04f, -2.312505227e-03f, +1.707586806e-03f, +7.976511649e-03f, -2.532386758e-02f, -2.670244010e-02f, +2.927811806e-02f, +2.286194672e-02f, -8.631812899e-03f, -9.416507761e-04f, +2.081518390e-03f, -6.572383529e-04f, - /* 12, 8 */ +6.572383529e-04f, -2.081518390e-03f, +9.416507761e-04f, +8.631812899e-03f, -2.286194672e-02f, -2.927811806e-02f, +2.670244010e-02f, +2.532386758e-02f, -7.976511649e-03f, -1.707586806e-03f, +2.312505227e-03f, -6.452583046e-04f, - /* 12, 9 */ +6.504054889e-04f, -1.831416829e-03f, +2.284590483e-04f, +9.076221424e-03f, -2.029896461e-02f, -3.151741694e-02f, +2.381671619e-02f, +2.764638515e-02f, -7.103945228e-03f, -2.514858275e-03f, +2.516571985e-03f, -6.121620582e-04f, - /* 12,10 */ +6.273034189e-04f, -1.569701578e-03f, -4.225520445e-04f, +9.319170237e-03f, -1.767328103e-02f, -3.339904530e-02f, +2.065196332e-02f, +2.979181001e-02f, -6.011087921e-03f, -3.350171906e-03f, +2.685773447e-03f, -5.559799195e-04f, - /* 12,11 */ +5.906251216e-04f, -1.303424694e-03f, -1.003961424e-03f, +9.372823891e-03f, -1.502265637e-02f, -3.490702283e-02f, +1.724344185e-02f, +3.172374637e-02f, -4.698496481e-03f, -4.198484259e-03f, +2.812206203e-03f, -4.751685216e-04f, - /* 12,12 */ +5.431259501e-04f, -1.039066351e-03f, -1.510362297e-03f, +9.251714734e-03f, -1.238366322e-02f, -3.603085731e-02f, +1.363013975e-02f, +3.340772759e-02f, -3.170488652e-03f, -5.043181884e-03f, +2.888204359e-03f, -3.687004846e-04f, - /* 12,13 */ +4.875429386e-04f, -7.824350015e-04f, -1.938320040e-03f, +8.972352970e-03f, -9.791136525e-03f, -3.676562700e-02f, +9.854190425e-03f, +3.481183398e-02f, -1.435258618e-03f, -5.866306097e-03f, +2.906545541e-03f, -2.361482435e-04f, - /* 12,14 */ +4.265219564e-04f, -5.385913036e-04f, -2.286292242e-03f, +8.552819277e-03f, -7.277671607e-03f, -3.711196787e-02f, +5.960234251e-03f, +3.590728113e-02f, +4.950753709e-04f, -6.648820026e-03f, +2.860662969e-03f, -7.775923585e-05f, - /* 12,15 */ +3.625540242e-04f, -3.117958677e-04f, -2.554514858e-03f, +8.012348726e-03f, -4.873177855e-03f, -3.707596726e-02f, +1.994735221e-03f, +3.666896703e-02f, +2.604494739e-03f, -7.370914553e-03f, +2.744858958e-03f, +1.054803383e-04f, - /* 12, 0 */ +6.110448771e-04f, -3.173989705e-03f, +5.751223243e-03f, +1.507555794e-03f, -4.035343888e-02f, -2.375409442e-03f, +4.124383193e-02f, +8.091337269e-04f, -6.726716888e-03f, +3.253952459e-03f, -5.087325269e-04f, -4.127854608e-05f, - /* 12, 1 */ +6.820041984e-04f, -3.029137857e-03f, +4.746351538e-03f, +3.578915017e-03f, -3.904147991e-02f, -7.094160720e-03f, +4.168490752e-02f, +3.349306133e-03f, -7.647219355e-03f, +3.259488816e-03f, -3.738470149e-04f, -9.536054020e-05f, - /* 12, 2 */ +7.235832870e-04f, -2.829602454e-03f, +3.736224000e-03f, +5.388621830e-03f, -3.734163661e-02f, -1.171726725e-02f, +4.165549067e-02f, +6.085743956e-03f, -8.486093037e-03f, +3.182049180e-03f, -2.060445111e-04f, -1.573545891e-04f, - /* 12, 3 */ +7.383739029e-04f, -2.585946508e-03f, +2.743066911e-03f, +6.925917423e-03f, -3.529277498e-02f, -1.618281485e-02f, +4.114151479e-02f, +8.986133221e-03f, -9.216195947e-03f, +3.014391908e-03f, -5.966328360e-06f, -2.260166200e-04f, - /* 12, 4 */ +7.294476853e-04f, -2.308795686e-03f, +1.786872733e-03f, +8.185530694e-03f, -3.293811768e-02f, -2.043162352e-02f, +4.013642610e-02f, +1.201345370e-02f, -9.810446156e-03f, +2.750895834e-03f, +2.246717082e-04f, -2.996559917e-04f, - /* 12, 5 */ +7.002161546e-04f, -2.008565767e-03f, +8.851333656e-04f, +9.167495079e-03f, -3.032435275e-02f, -2.440826356e-02f, +3.864144993e-02f, +1.512648157e-02f, -1.024241966e-02f, +2.387856219e-03f, +4.830138128e-04f, -3.761418846e-04f, - /* 12, 6 */ +6.542939604e-04f, -1.695217727e-03f, +5.264660367e-05f, +9.876866054e-03f, -2.750069849e-02f, -2.806199617e-02f, +3.666571148e-02f, +1.828039745e-02f, -1.048696943e-02f, +1.923755148e-03f, +7.650267923e-04f, -4.529261561e-04f, - /* 12, 7 */ +5.953691186e-04f, -1.378044726e-03f, -6.986040124e-04f, +1.032334944e-02f, -2.451794467e-02f, -3.134761990e-02f, +3.422620641e-02f, +2.142749023e-02f, -1.052085229e-02f, +1.359497506e-03f, +1.065494162e-03f, -5.270834853e-04f, - /* 12, 8 */ +5.270834853e-04f, -1.065494162e-03f, -1.359497506e-03f, +1.052085229e-02f, -2.142749023e-02f, -3.422620641e-02f, +3.134761990e-02f, +2.451794467e-02f, -1.032334944e-02f, +6.986040124e-04f, +1.378044726e-03f, -5.953691186e-04f, - /* 12, 9 */ +4.529261561e-04f, -7.650267923e-04f, -1.923755148e-03f, +1.048696943e-02f, -1.828039745e-02f, -3.666571148e-02f, +2.806199617e-02f, +2.750069849e-02f, -9.876866054e-03f, -5.264660367e-05f, +1.695217727e-03f, -6.542939604e-04f, - /* 12,10 */ +3.761418846e-04f, -4.830138128e-04f, -2.387856219e-03f, +1.024241966e-02f, -1.512648157e-02f, -3.864144993e-02f, +2.440826356e-02f, +3.032435275e-02f, -9.167495079e-03f, -8.851333656e-04f, +2.008565767e-03f, -7.002161546e-04f, - /* 12,11 */ +2.996559917e-04f, -2.246717082e-04f, -2.750895834e-03f, +9.810446156e-03f, -1.201345370e-02f, -4.013642610e-02f, +2.043162352e-02f, +3.293811768e-02f, -8.185530694e-03f, -1.786872733e-03f, +2.308795686e-03f, -7.294476853e-04f, - /* 12,12 */ +2.260166200e-04f, +5.966328360e-06f, -3.014391908e-03f, +9.216195947e-03f, -8.986133221e-03f, -4.114151479e-02f, +1.618281485e-02f, +3.529277498e-02f, -6.925917423e-03f, -2.743066911e-03f, +2.585946508e-03f, -7.383739029e-04f, - /* 12,13 */ +1.573545891e-04f, +2.060445111e-04f, -3.182049180e-03f, +8.486093037e-03f, -6.085743956e-03f, -4.165549067e-02f, +1.171726725e-02f, +3.734163661e-02f, -5.388621830e-03f, -3.736224000e-03f, +2.829602454e-03f, -7.235832870e-04f, - /* 12,14 */ +9.536054020e-05f, +3.738470149e-04f, -3.259488816e-03f, +7.647219355e-03f, -3.349306133e-03f, -4.168490752e-02f, +7.094160720e-03f, +3.904147991e-02f, -3.578915017e-03f, -4.746351538e-03f, +3.029137857e-03f, -6.820041984e-04f, - /* 12,15 */ +4.127854608e-05f, +5.087325269e-04f, -3.253952459e-03f, +6.726716888e-03f, -8.091337269e-04f, -4.124383193e-02f, +2.375409442e-03f, +4.035343888e-02f, -1.507555794e-03f, -5.751223243e-03f, +3.173989705e-03f, -6.110448771e-04f, - - /* 24, 0 */ -8.820438069e-05f, -1.519461079e-04f, -2.301651496e-04f, -3.149320871e-04f, -3.945939739e-04f, -4.554410135e-04f, -4.841532882e-04f, -4.705408991e-04f, -4.099602091e-04f, -3.048100066e-04f, -1.646897470e-04f, -5.099007530e-06f, +1.551006323e-04f, +2.969416536e-04f, +4.046294158e-04f, +4.681429482e-04f, +4.846228261e-04f, +4.583040637e-04f, +3.990939388e-04f, +3.201968846e-04f, +2.353759082e-04f, +1.564712483e-04f, +9.167483068e-05f, +4.482688286e-05f, - /* 24, 1 */ -8.480575132e-05f, -1.474789784e-04f, -2.249812225e-04f, -3.096480504e-04f, -3.900204007e-04f, -4.524514078e-04f, -4.835165803e-04f, -4.727530367e-04f, -4.151145025e-04f, -3.125397891e-04f, -1.742016828e-04f, -1.529460870e-05f, +1.454387449e-04f, +2.889379628e-04f, +3.991236794e-04f, +4.655589110e-04f, +4.849233000e-04f, +4.610375470e-04f, +4.035168325e-04f, +3.254391996e-04f, +2.406110065e-04f, +1.610529558e-04f, +9.521673594e-05f, +4.721513201e-05f, - /* 24, 2 */ -8.147924507e-05f, -1.430712350e-04f, -2.198265592e-04f, -3.043479843e-04f, -3.853766873e-04f, -4.493383067e-04f, -4.827146831e-04f, -4.747797448e-04f, -4.200908527e-04f, -3.201278616e-04f, -1.836320864e-04f, -2.548296987e-05f, +1.357085413e-04f, +2.808022583e-04f, +3.934446700e-04f, +4.627886263e-04f, +4.850529052e-04f, +4.636385032e-04f, +4.078592000e-04f, +3.306557574e-04f, +2.458678944e-04f, +1.656897170e-04f, +9.882966748e-05f, +4.967415993e-05f, - /* 24, 3 */ -7.822510242e-05f, -1.387241832e-04f, -2.147035314e-04f, -2.990350629e-04f, -3.806663042e-04f, -4.461048161e-04f, -4.817496625e-04f, -4.766215175e-04f, -4.248879309e-04f, -3.275711800e-04f, -1.929766610e-04f, -3.565926997e-05f, +1.259145254e-04f, +2.725379532e-04f, +3.875941701e-04f, +4.598320457e-04f, +4.850099279e-04f, +4.661040260e-04f, +4.121175966e-04f, +3.358432542e-04f, +2.511439643e-04f, +1.703799499e-04f, +1.025131319e-04f, +5.220438912e-05f, - /* 24, 4 */ -7.504350274e-05f, -1.344390595e-04f, -2.096144489e-04f, -2.937124231e-04f, -3.758927218e-04f, -4.427540852e-04f, -4.806236671e-04f, -4.782789577e-04f, -4.295045226e-04f, -3.348667971e-04f, -2.022311686e-04f, -4.581869630e-05f, +1.160612449e-04f, +2.641485480e-04f, +3.815740737e-04f, +4.566892339e-04f, +4.847927474e-04f, +4.684312656e-04f, +4.162885910e-04f, +3.409983591e-04f, +2.564365532e-04f, +1.751220037e-04f, +1.062665706e-04f, +5.480619333e-05f, - /* 24, 5 */ -7.193456522e-05f, -1.302170312e-04f, -2.045615590e-04f, -2.883831622e-04f, -3.710594077e-04f, -4.392893036e-04f, -4.793389263e-04f, -4.797527765e-04f, -4.339395286e-04f, -3.420118645e-04f, -2.113914331e-04f, -5.595644787e-05f, +1.061532886e-04f, +2.556376279e-04f, +3.753863858e-04f, +4.533603695e-04f, +4.843998374e-04f, +4.706174312e-04f, +4.203687678e-04f, +3.461177167e-04f, +2.617429433e-04f, +1.799141593e-04f, +1.100893595e-04f, +5.747989630e-05f, - /* 24, 6 */ -6.889834987e-05f, -1.260591965e-04f, -1.995470454e-04f, -2.830503358e-04f, -3.661698243e-04f, -4.357136989e-04f, -4.778977479e-04f, -4.810437916e-04f, -4.381919648e-04f, -3.490036345e-04f, -2.204533432e-04f, -6.606773875e-05f, +9.619528314e-05f, +2.470088608e-04f, +3.690332209e-04f, +4.498457452e-04f, +4.838297683e-04f, +4.726597937e-04f, +4.243547301e-04f, +3.511979487e-04f, +2.670603639e-04f, +1.847546294e-04f, +1.139808078e-04f, +6.022577049e-05f, - /* 24, 7 */ -6.593485851e-05f, -1.219665852e-04f, -1.945730275e-04f, -2.777169567e-04f, -3.612274261e-04f, -4.320305335e-04f, -4.763025159e-04f, -4.821529264e-04f, -4.422609626e-04f, -3.558394612e-04f, -2.294128549e-04f, -7.614780136e-05f, +8.619188981e-05f, +2.382659945e-04f, +3.625168024e-04f, +4.461457687e-04f, +4.830812085e-04f, +4.745556880e-04f, +4.282431024e-04f, +3.562356572e-04f, +2.723859925e-04f, +1.896415594e-04f, +1.179401580e-04f, +6.304403582e-05f, - /* 24, 8 */ -6.304403582e-05f, -1.179401580e-04f, -1.896415594e-04f, -2.723859925e-04f, -3.562356572e-04f, -4.282431024e-04f, -4.745556880e-04f, -4.830812085e-04f, -4.461457687e-04f, -3.625168024e-04f, -2.382659945e-04f, -8.619188981e-05f, +7.614780136e-05f, +2.294128549e-04f, +3.558394612e-04f, +4.422609626e-04f, +4.821529264e-04f, +4.763025159e-04f, +4.320305335e-04f, +3.612274261e-04f, +2.777169567e-04f, +1.945730275e-04f, +1.219665852e-04f, +6.593485851e-05f, - /* 24, 9 */ -6.022577049e-05f, -1.139808078e-04f, -1.847546294e-04f, -2.670603639e-04f, -3.511979487e-04f, -4.243547301e-04f, -4.726597937e-04f, -4.838297683e-04f, -4.498457452e-04f, -3.690332209e-04f, -2.470088608e-04f, -9.619528314e-05f, +6.606773875e-05f, +2.204533432e-04f, +3.490036345e-04f, +4.381919648e-04f, +4.810437916e-04f, +4.778977479e-04f, +4.357136989e-04f, +3.661698243e-04f, +2.830503358e-04f, +1.995470454e-04f, +1.260591965e-04f, +6.889834987e-05f, - /* 24,10 */ -5.747989630e-05f, -1.100893595e-04f, -1.799141593e-04f, -2.617429433e-04f, -3.461177167e-04f, -4.203687678e-04f, -4.706174312e-04f, -4.843998374e-04f, -4.533603695e-04f, -3.753863858e-04f, -2.556376279e-04f, -1.061532886e-04f, +5.595644787e-05f, +2.113914331e-04f, +3.420118645e-04f, +4.339395286e-04f, +4.797527765e-04f, +4.793389263e-04f, +4.392893036e-04f, +3.710594077e-04f, +2.883831622e-04f, +2.045615590e-04f, +1.302170312e-04f, +7.193456522e-05f, - /* 24,11 */ -5.480619333e-05f, -1.062665706e-04f, -1.751220037e-04f, -2.564365532e-04f, -3.409983591e-04f, -4.162885910e-04f, -4.684312656e-04f, -4.847927474e-04f, -4.566892339e-04f, -3.815740737e-04f, -2.641485480e-04f, -1.160612449e-04f, +4.581869630e-05f, +2.022311686e-04f, +3.348667971e-04f, +4.295045226e-04f, +4.782789577e-04f, +4.806236671e-04f, +4.427540852e-04f, +3.758927218e-04f, +2.937124231e-04f, +2.096144489e-04f, +1.344390595e-04f, +7.504350274e-05f, - /* 24,12 */ -5.220438912e-05f, -1.025131319e-04f, -1.703799499e-04f, -2.511439643e-04f, -3.358432542e-04f, -4.121175966e-04f, -4.661040260e-04f, -4.850099279e-04f, -4.598320457e-04f, -3.875941701e-04f, -2.725379532e-04f, -1.259145254e-04f, +3.565926997e-05f, +1.929766610e-04f, +3.275711800e-04f, +4.248879309e-04f, +4.766215175e-04f, +4.817496625e-04f, +4.461048161e-04f, +3.806663042e-04f, +2.990350629e-04f, +2.147035314e-04f, +1.387241832e-04f, +7.822510242e-05f, - /* 24,13 */ -4.967415993e-05f, -9.882966748e-05f, -1.656897170e-04f, -2.458678944e-04f, -3.306557574e-04f, -4.078592000e-04f, -4.636385032e-04f, -4.850529052e-04f, -4.627886263e-04f, -3.934446700e-04f, -2.808022583e-04f, -1.357085413e-04f, +2.548296987e-05f, +1.836320864e-04f, +3.201278616e-04f, +4.200908527e-04f, +4.747797448e-04f, +4.827146831e-04f, +4.493383067e-04f, +3.853766873e-04f, +3.043479843e-04f, +2.198265592e-04f, +1.430712350e-04f, +8.147924507e-05f, - /* 24,14 */ -4.721513201e-05f, -9.521673594e-05f, -1.610529558e-04f, -2.406110065e-04f, -3.254391996e-04f, -4.035168325e-04f, -4.610375470e-04f, -4.849233000e-04f, -4.655589110e-04f, -3.991236794e-04f, -2.889379628e-04f, -1.454387449e-04f, +1.529460870e-05f, +1.742016828e-04f, +3.125397891e-04f, +4.151145025e-04f, +4.727530367e-04f, +4.835165803e-04f, +4.524514078e-04f, +3.900204007e-04f, +3.096480504e-04f, +2.249812225e-04f, +1.474789784e-04f, +8.480575132e-05f, - /* 24,15 */ -4.482688286e-05f, -9.167483068e-05f, -1.564712483e-04f, -2.353759082e-04f, -3.201968846e-04f, -3.990939388e-04f, -4.583040637e-04f, -4.846228261e-04f, -4.681429482e-04f, -4.046294158e-04f, -2.969416536e-04f, -1.551006323e-04f, +5.099007530e-06f, +1.646897470e-04f, +3.048100066e-04f, +4.099602091e-04f, +4.705408991e-04f, +4.841532882e-04f, +4.554410135e-04f, +3.945939739e-04f, +3.149320871e-04f, +2.301651496e-04f, +1.519461079e-04f, +8.820438069e-05f, - /* 24, 0 */ +1.254177052e-04f, +1.432187562e-04f, +1.041598752e-04f, -1.135750248e-05f, -2.010663923e-04f, -4.345091125e-04f, -6.566280172e-04f, -8.018070806e-04f, -8.145094672e-04f, -6.693628869e-04f, -3.829411831e-04f, -1.208738944e-05f, +3.614691013e-04f, +6.551938988e-04f, +8.101126455e-04f, +8.069330100e-04f, +6.686285441e-04f, +4.493898115e-04f, +2.148422063e-04f, +2.121126661e-05f, -9.928779545e-05f, -1.427235715e-04f, -1.276505127e-04f, -8.319130160e-05f, - /* 24, 1 */ +1.230784715e-04f, +1.434886692e-04f, +1.087259386e-04f, -1.803144714e-06f, -1.874698746e-04f, -4.195879132e-04f, -6.443236569e-04f, -7.961535056e-04f, -8.182754723e-04f, -6.829663304e-04f, -4.040749814e-04f, -3.625133679e-05f, +3.396771574e-04f, +6.404692089e-04f, +8.050839212e-04f, +8.115205881e-04f, +6.803091718e-04f, +4.642140510e-04f, +2.287861157e-04f, +3.136033560e-05f, -9.410712043e-05f, -1.419963918e-04f, -1.297693532e-04f, -8.627587811e-05f, - /* 24, 2 */ +1.206403004e-04f, +1.435401825e-04f, +1.129889134e-04f, +7.448162127e-06f, -1.740634498e-04f, -4.046419937e-04f, -6.317316839e-04f, -7.899834729e-04f, -8.214124236e-04f, -6.959950382e-04f, -4.248524782e-04f, -6.038280262e-05f, +3.175841545e-04f, +6.251993025e-04f, +7.994228899e-04f, +8.155596091e-04f, +6.916540113e-04f, +4.789657110e-04f, +2.428865252e-04f, +4.180014906e-05f, -8.861563067e-05f, -1.410306561e-04f, -1.317666448e-04f, -8.934972901e-05f, - /* 24, 3 */ +1.181106179e-04f, +1.433803035e-04f, +1.169520657e-04f, +1.639322797e-05f, -1.608575022e-04f, -3.896869361e-04f, -6.188684520e-04f, -7.833086355e-04f, -8.239227539e-04f, -7.084404793e-04f, -4.452560799e-04f, -8.446017611e-05f, +2.952092559e-04f, +6.093952965e-04f, +7.931298338e-04f, +8.190403838e-04f, +7.026473724e-04f, +4.936285297e-04f, +2.571314547e-04f, +5.252568657e-05f, -8.281147599e-05f, -1.398199793e-04f, -1.336347757e-04f, -9.240689021e-05f, - /* 24, 4 */ +1.154967766e-04f, +1.430161614e-04f, +1.206189886e-04f, +2.502931407e-05f, -1.478619956e-04f, -3.747381071e-04f, -6.057504254e-04f, -7.761410928e-04f, -8.258095592e-04f, -7.202947906e-04f, -4.652686374e-04f, -1.084619116e-04f, +2.725719623e-04f, +5.930689291e-04f, +7.862057246e-04f, +8.219537553e-04f, +7.132737861e-04f, +5.081861235e-04f, +2.715085502e-04f, +6.353146713e-05f, -7.669318508e-05f, -1.383581653e-04f, -1.353661159e-04f, -9.544123411e-05f, - /* 24, 5 */ +1.128060463e-04f, +1.424549941e-04f, +1.239935912e-04f, +3.335412922e-05f, -1.350864661e-04f, -3.598106411e-04f, -5.923941571e-04f, -7.684933716e-04f, -8.270765907e-04f, -7.315507834e-04f, -4.848734661e-04f, -1.323665545e-04f, +2.496920884e-04f, +5.762325468e-04f, +7.786522269e-04f, +8.242911148e-04f, +7.235180256e-04f, +5.226220062e-04f, +2.860050947e-04f, +7.481154929e-05f, -7.025967465e-05f, -1.366392205e-04f, -1.369530289e-04f, -9.844647580e-05f, - /* 24, 6 */ +1.100456035e-04f, +1.417041346e-04f, +1.270800866e-04f, +4.136582536e-05f, -1.225400157e-04f, -3.449194225e-04f, -5.788162671e-04f, -7.603784078e-04f, -8.277282458e-04f, -7.422019493e-04f, -5.040543645e-04f, -1.561527673e-04f, +2.265897402e-04f, +5.588990922e-04f, +7.704716994e-04f, +8.260444163e-04f, +7.333651287e-04f, +5.369196097e-04f, +3.006080208e-04f, +8.635953200e-05f, -6.351025813e-05f, -1.346573670e-04f, -1.383878839e-04f, -1.014161798e-04f, - /* 24, 7 */ +1.072225228e-04f, +1.407709979e-04f, +1.298829797e-04f, +4.906299265e-05f, -1.102313067e-04f, -3.300790706e-04f, -5.650334202e-04f, -7.518095256e-04f, -8.277695590e-04f, -7.522424649e-04f, -5.227956327e-04f, -1.797993550e-04f, +2.032852905e-04f, +5.410820901e-04f, +7.616671958e-04f, +8.272061905e-04f, +7.428004186e-04f, +5.510623045e-04f, +3.153039229e-04f, +9.816855611e-05f, -5.644465382e-05f, -1.324070557e-04f, -1.396630677e-04f, -1.043437672e-04f, - /* 24, 8 */ +1.043437672e-04f, +1.396630677e-04f, +1.324070557e-04f, +5.644465382e-05f, -9.816855611e-05f, -3.153039229e-04f, -5.510623045e-04f, -7.428004186e-04f, -8.272061905e-04f, -7.616671958e-04f, -5.410820901e-04f, -2.032852905e-04f, +1.797993550e-04f, +5.227956327e-04f, +7.522424649e-04f, +8.277695590e-04f, +7.518095256e-04f, +5.650334202e-04f, +3.300790706e-04f, +1.102313067e-04f, -4.906299265e-05f, -1.298829797e-04f, -1.407709979e-04f, -1.072225228e-04f, - /* 24, 9 */ +1.014161798e-04f, +1.383878839e-04f, +1.346573670e-04f, +6.351025813e-05f, -8.635953200e-05f, -3.006080208e-04f, -5.369196097e-04f, -7.333651287e-04f, -8.260444163e-04f, -7.704716994e-04f, -5.588990922e-04f, -2.265897402e-04f, +1.561527673e-04f, +5.040543645e-04f, +7.422019493e-04f, +8.277282458e-04f, +7.603784078e-04f, +5.788162671e-04f, +3.449194225e-04f, +1.225400157e-04f, -4.136582536e-05f, -1.270800866e-04f, -1.417041346e-04f, -1.100456035e-04f, - /* 24,10 */ +9.844647580e-05f, +1.369530289e-04f, +1.366392205e-04f, +7.025967465e-05f, -7.481154929e-05f, -2.860050947e-04f, -5.226220062e-04f, -7.235180256e-04f, -8.242911148e-04f, -7.786522269e-04f, -5.762325468e-04f, -2.496920884e-04f, +1.323665545e-04f, +4.848734661e-04f, +7.315507834e-04f, +8.270765907e-04f, +7.684933716e-04f, +5.923941571e-04f, +3.598106411e-04f, +1.350864661e-04f, -3.335412922e-05f, -1.239935912e-04f, -1.424549941e-04f, -1.128060463e-04f, - /* 24,11 */ +9.544123411e-05f, +1.353661159e-04f, +1.383581653e-04f, +7.669318508e-05f, -6.353146713e-05f, -2.715085502e-04f, -5.081861235e-04f, -7.132737861e-04f, -8.219537553e-04f, -7.862057246e-04f, -5.930689291e-04f, -2.725719623e-04f, +1.084619116e-04f, +4.652686374e-04f, +7.202947906e-04f, +8.258095592e-04f, +7.761410928e-04f, +6.057504254e-04f, +3.747381071e-04f, +1.478619956e-04f, -2.502931407e-05f, -1.206189886e-04f, -1.430161614e-04f, -1.154967766e-04f, - /* 24,12 */ +9.240689021e-05f, +1.336347757e-04f, +1.398199793e-04f, +8.281147599e-05f, -5.252568657e-05f, -2.571314547e-04f, -4.936285297e-04f, -7.026473724e-04f, -8.190403838e-04f, -7.931298338e-04f, -6.093952965e-04f, -2.952092559e-04f, +8.446017611e-05f, +4.452560799e-04f, +7.084404793e-04f, +8.239227539e-04f, +7.833086355e-04f, +6.188684520e-04f, +3.896869361e-04f, +1.608575022e-04f, -1.639322797e-05f, -1.169520657e-04f, -1.433803035e-04f, -1.181106179e-04f, - /* 24,13 */ +8.934972901e-05f, +1.317666448e-04f, +1.410306561e-04f, +8.861563067e-05f, -4.180014906e-05f, -2.428865252e-04f, -4.789657110e-04f, -6.916540113e-04f, -8.155596091e-04f, -7.994228899e-04f, -6.251993025e-04f, -3.175841545e-04f, +6.038280262e-05f, +4.248524782e-04f, +6.959950382e-04f, +8.214124236e-04f, +7.899834729e-04f, +6.317316839e-04f, +4.046419937e-04f, +1.740634498e-04f, -7.448162127e-06f, -1.129889134e-04f, -1.435401825e-04f, -1.206403004e-04f, - /* 24,14 */ +8.627587811e-05f, +1.297693532e-04f, +1.419963918e-04f, +9.410712043e-05f, -3.136033560e-05f, -2.287861157e-04f, -4.642140510e-04f, -6.803091718e-04f, -8.115205881e-04f, -8.050839212e-04f, -6.404692089e-04f, -3.396771574e-04f, +3.625133679e-05f, +4.040749814e-04f, +6.829663304e-04f, +8.182754723e-04f, +7.961535056e-04f, +6.443236569e-04f, +4.195879132e-04f, +1.874698746e-04f, +1.803144714e-06f, -1.087259386e-04f, -1.434886692e-04f, -1.230784715e-04f, - /* 24,15 */ +8.319130160e-05f, +1.276505127e-04f, +1.427235715e-04f, +9.928779545e-05f, -2.121126661e-05f, -2.148422063e-04f, -4.493898115e-04f, -6.686285441e-04f, -8.069330100e-04f, -8.101126455e-04f, -6.551938988e-04f, -3.614691013e-04f, +1.208738944e-05f, +3.829411831e-04f, +6.693628869e-04f, +8.145094672e-04f, +8.018070806e-04f, +6.566280172e-04f, +4.345091125e-04f, +2.010663923e-04f, +1.135750248e-05f, -1.041598752e-04f, -1.432187562e-04f, -1.254177052e-04f, - /* 24, 0 */ +4.545052445e-05f, +1.951315810e-04f, +3.748938080e-04f, +4.809335107e-04f, +3.960765690e-04f, +5.993810822e-05f, -4.723795438e-04f, -1.024325735e-03f, -1.361247582e-03f, -1.299302728e-03f, -8.046117557e-04f, -2.606329026e-05f, +7.618428442e-04f, +1.280408741e-03f, +1.370322771e-03f, +1.054089829e-03f, +5.086432784e-04f, -3.113193898e-05f, -3.825195300e-04f, -4.822884412e-04f, -3.849609275e-04f, -2.063256631e-04f, -5.270037440e-05f, +2.454794639e-05f, - /* 24, 1 */ +3.852332445e-05f, +1.840753178e-04f, +3.645444067e-04f, +4.788140096e-04f, +4.086121277e-04f, +8.793526572e-05f, -4.362135407e-04f, -9.937048198e-04f, -1.350562575e-03f, -1.316442769e-03f, -8.462280606e-04f, -7.815185270e-05f, +7.179801999e-04f, +1.259777116e-03f, +1.377758125e-03f, +1.082939175e-03f, +5.449481703e-04f, -1.547839432e-06f, -3.679388598e-04f, -4.828529979e-04f, -3.947147844e-04f, -2.176372792e-04f, -6.026901850e-05f, +2.205234045e-05f, - /* 24, 2 */ +3.192167036e-05f, +1.731761778e-04f, +3.539434193e-04f, +4.759566352e-04f, +4.201302650e-04f, +1.150943789e-04f, -4.002008253e-04f, -9.622858103e-04f, -1.338300241e-03f, -1.331815598e-03f, -8.866349827e-04f, -1.301264311e-04f, +6.730846905e-04f, +1.237427186e-03f, +1.383526171e-03f, +1.110816763e-03f, +5.812367254e-04f, +2.878109064e-05f, -3.523343557e-04f, -4.826023474e-04f, -4.041241778e-04f, -2.290451863e-04f, -6.815162122e-05f, +1.926869283e-05f, - /* 24, 3 */ +2.564752013e-05f, +1.624524653e-04f, +3.431211940e-04f, +4.723889014e-04f, +4.306369033e-04f, +1.413884897e-04f, -3.643958409e-04f, -9.301281119e-04f, -1.324495465e-03f, -1.345410999e-03f, -9.257779427e-04f, -1.819112698e-04f, +6.272190697e-04f, +1.213381290e-03f, +1.387602061e-03f, +1.137666624e-03f, +6.174506312e-04f, +5.981976836e-05f, -3.357077999e-04f, -4.815127015e-04f, -4.131577529e-04f, -2.405272085e-04f, -7.634234963e-05f, +1.618998833e-05f, - /* 24, 4 */ +1.970191739e-05f, +1.519214683e-04f, +3.321076713e-04f, +4.681390617e-04f, +4.401397816e-04f, +1.667927354e-04f, -3.288518263e-04f, -8.972916878e-04f, -1.309185436e-03f, -1.357221809e-03f, -9.636046528e-04f, -2.334309635e-04f, +5.804478655e-04f, +1.187664745e-03f, +1.389963631e-03f, +1.163433942e-03f, +6.535308606e-04f, +9.153116784e-05f, -3.180629927e-04f, -4.795613921e-04f, -4.217840695e-04f, -2.520602653e-04f, -8.483435780e-05f, +1.280977164e-05f, - /* 24, 5 */ +1.408501704e-05f, +1.415994448e-04f, +3.209323254e-04f, +4.632360317e-04f, +4.486484045e-04f, +1.912843645e-04f, -2.936207276e-04f, -8.638369381e-04f, -1.292409564e-03f, -1.367243913e-03f, -1.000065207e-03f, -2.846105957e-04f, +5.328372638e-04f, +1.160305814e-03f, +1.390591463e-03f, +1.188065177e-03f, +6.894177793e-04f, +1.238763650e-04f, -2.994057812e-04f, -4.767269440e-04f, -4.299716716e-04f, -2.636204028e-04f, -9.361977359e-05f, +9.122184539e-06f, - /* 24, 6 */ +8.796112429e-06f, +1.315016126e-04f, +3.096241086e-04f, +4.577093118e-04f, +4.561739887e-04f, +2.148427485e-04f, -2.587531149e-04f, -8.298245798e-04f, -1.274209383e-03f, -1.375476234e-03f, -1.035112163e-03f, -3.353758773e-04f, +4.844549899e-04f, +1.131335665e-03f, +1.389468944e-03f, +1.211508174e-03f, +7.250512548e-04f, +1.568145870e-04f, -2.797440842e-04f, -4.729891466e-04f, -4.376891593e-04f, -2.751828281e-04f, -1.026896878e-04f, +5.122002376e-06f, - /* 24, 7 */ +3.833664119e-06f, +1.216421408e-04f, +2.982113984e-04f, +4.515889092e-04f, +4.627294060e-04f, +2.374493875e-04f, -2.242981012e-04f, -7.953155261e-04f, -1.254628457e-03f, -1.381920720e-03f, -1.068700627e-03f, -3.856532828e-04f, +4.353701854e-04f, +1.100788324e-03f, +1.386582316e-03f, +1.233712281e-03f, +7.603707678e-04f, +1.903032668e-04f, -2.590879129e-04f, -4.683291247e-04f, -4.449052614e-04f, -2.867219458e-04f, -1.120341455e-04f, +8.046698450e-07f, - /* 24, 8 */ -8.046698450e-07f, +1.120341455e-04f, +2.867219458e-04f, +4.449052614e-04f, +4.683291247e-04f, +2.590879129e-04f, -1.903032668e-04f, -7.603707678e-04f, -1.233712281e-03f, -1.386582316e-03f, -1.100788324e-03f, -4.353701854e-04f, +3.856532828e-04f, +1.068700627e-03f, +1.381920720e-03f, +1.254628457e-03f, +7.953155261e-04f, +2.242981012e-04f, -2.374493875e-04f, -4.627294060e-04f, -4.515889092e-04f, -2.982113984e-04f, -1.216421408e-04f, -3.833664119e-06f, - /* 24, 9 */ -5.122002376e-06f, +1.026896878e-04f, +2.751828281e-04f, +4.376891593e-04f, +4.729891466e-04f, +2.797440842e-04f, -1.568145870e-04f, -7.250512548e-04f, -1.211508174e-03f, -1.389468944e-03f, -1.131335665e-03f, -4.844549899e-04f, +3.353758773e-04f, +1.035112163e-03f, +1.375476234e-03f, +1.274209383e-03f, +8.298245798e-04f, +2.587531149e-04f, -2.148427485e-04f, -4.561739887e-04f, -4.577093118e-04f, -3.096241086e-04f, -1.315016126e-04f, -8.796112429e-06f, - /* 24,10 */ -9.122184539e-06f, +9.361977359e-05f, +2.636204028e-04f, +4.299716716e-04f, +4.767269440e-04f, +2.994057812e-04f, -1.238763650e-04f, -6.894177793e-04f, -1.188065177e-03f, -1.390591463e-03f, -1.160305814e-03f, -5.328372638e-04f, +2.846105957e-04f, +1.000065207e-03f, +1.367243913e-03f, +1.292409564e-03f, +8.638369381e-04f, +2.936207276e-04f, -1.912843645e-04f, -4.486484045e-04f, -4.632360317e-04f, -3.209323254e-04f, -1.415994448e-04f, -1.408501704e-05f, - /* 24,11 */ -1.280977164e-05f, +8.483435780e-05f, +2.520602653e-04f, +4.217840695e-04f, +4.795613921e-04f, +3.180629927e-04f, -9.153116784e-05f, -6.535308606e-04f, -1.163433942e-03f, -1.389963631e-03f, -1.187664745e-03f, -5.804478655e-04f, +2.334309635e-04f, +9.636046528e-04f, +1.357221809e-03f, +1.309185436e-03f, +8.972916878e-04f, +3.288518263e-04f, -1.667927354e-04f, -4.401397816e-04f, -4.681390617e-04f, -3.321076713e-04f, -1.519214683e-04f, -1.970191739e-05f, - /* 24,12 */ -1.618998833e-05f, +7.634234963e-05f, +2.405272085e-04f, +4.131577529e-04f, +4.815127015e-04f, +3.357077999e-04f, -5.981976836e-05f, -6.174506312e-04f, -1.137666624e-03f, -1.387602061e-03f, -1.213381290e-03f, -6.272190697e-04f, +1.819112698e-04f, +9.257779427e-04f, +1.345410999e-03f, +1.324495465e-03f, +9.301281119e-04f, +3.643958409e-04f, -1.413884897e-04f, -4.306369033e-04f, -4.723889014e-04f, -3.431211940e-04f, -1.624524653e-04f, -2.564752013e-05f, - /* 24,13 */ -1.926869283e-05f, +6.815162122e-05f, +2.290451863e-04f, +4.041241778e-04f, +4.826023474e-04f, +3.523343557e-04f, -2.878109064e-05f, -5.812367254e-04f, -1.110816763e-03f, -1.383526171e-03f, -1.237427186e-03f, -6.730846905e-04f, +1.301264311e-04f, +8.866349827e-04f, +1.331815598e-03f, +1.338300241e-03f, +9.622858103e-04f, +4.002008253e-04f, -1.150943789e-04f, -4.201302650e-04f, -4.759566352e-04f, -3.539434193e-04f, -1.731761778e-04f, -3.192167036e-05f, - /* 24,14 */ -2.205234045e-05f, +6.026901850e-05f, +2.176372792e-04f, +3.947147844e-04f, +4.828529979e-04f, +3.679388598e-04f, +1.547839432e-06f, -5.449481703e-04f, -1.082939175e-03f, -1.377758125e-03f, -1.259777116e-03f, -7.179801999e-04f, +7.815185270e-05f, +8.462280606e-04f, +1.316442769e-03f, +1.350562575e-03f, +9.937048198e-04f, +4.362135407e-04f, -8.793526572e-05f, -4.086121277e-04f, -4.788140096e-04f, -3.645444067e-04f, -1.840753178e-04f, -3.852332445e-05f, - /* 24,15 */ -2.454794639e-05f, +5.270037440e-05f, +2.063256631e-04f, +3.849609275e-04f, +4.822884412e-04f, +3.825195300e-04f, +3.113193898e-05f, -5.086432784e-04f, -1.054089829e-03f, -1.370322771e-03f, -1.280408741e-03f, -7.618428442e-04f, +2.606329026e-05f, +8.046117557e-04f, +1.299302728e-03f, +1.361247582e-03f, +1.024325735e-03f, +4.723795438e-04f, -5.993810822e-05f, -3.960765690e-04f, -4.809335107e-04f, -3.748938080e-04f, -1.951315810e-04f, -4.545052445e-05f, - /* 24, 0 */ -1.702250368e-04f, -1.965005420e-04f, +1.103795304e-06f, +4.330784212e-04f, +8.784555707e-04f, +9.653328276e-04f, +4.315509563e-04f, -6.109575553e-04f, -1.641723450e-03f, -2.015827225e-03f, -1.400787443e-03f, -4.702498413e-05f, +1.332047630e-03f, +2.006889303e-03f, +1.690240096e-03f, +6.826705419e-04f, -3.776686811e-04f, -9.512688743e-04f, -8.983149818e-04f, -4.640234647e-04f, -2.230828855e-05f, +1.918067822e-04f, +1.759255972e-04f, +6.786242515e-05f, - /* 24, 1 */ -1.642126010e-04f, -2.002752798e-04f, -1.910126829e-05f, +4.022439121e-04f, +8.571608722e-04f, +9.768399670e-04f, +4.832977173e-04f, -5.392469404e-04f, -1.590532482e-03f, -2.020693110e-03f, -1.466475318e-03f, -1.409702826e-04f, +1.260398022e-03f, +1.993868298e-03f, +1.735939471e-03f, +7.542164043e-04f, -3.217397652e-04f, -9.346209288e-04f, -9.166430096e-04f, -4.949934945e-04f, -4.448641826e-05f, +1.861665779e-04f, +1.812738819e-04f, +7.451957718e-05f, - /* 24, 2 */ -1.579281336e-04f, -2.031605347e-04f, -3.828516688e-05f, +3.716026326e-04f, +8.345286135e-04f, +9.858238344e-04f, +5.328272649e-04f, -4.677058239e-04f, -1.536815378e-03f, -2.021508037e-03f, -1.528977139e-03f, -2.346018520e-04f, +1.185988431e-03f, +1.976763286e-03f, +1.778684302e-03f, +8.254237228e-04f, -2.638601140e-04f, -9.153683790e-04f, -9.333455225e-04f, -5.259003096e-04f, -6.760829922e-05f, +1.795547962e-04f, +1.862290729e-04f, +8.132190552e-05f, - /* 24, 3 */ -1.514107898e-04f, -2.051877883e-04f, -5.643017558e-05f, +3.412342375e-04f, +8.106578209e-04f, +9.923241157e-04f, +5.800651921e-04f, -3.964985305e-04f, -1.480725107e-03f, -2.018303000e-03f, -1.588167145e-03f, -3.277114722e-04f, +1.108975953e-03f, +1.955583535e-03f, +1.818343426e-03f, +8.961196099e-04f, -2.041325004e-04f, -8.934973649e-04f, -9.483306864e-04f, -5.566532714e-04f, -9.163993343e-05f, +1.719487619e-04f, +1.907500791e-04f, +8.824611727e-05f, - /* 24, 4 */ -1.446989102e-04f, -2.063902871e-04f, -7.352252002e-05f, +3.112151687e-04f, +7.856484910e-04f, +9.963864071e-04f, +6.249444785e-04f, -3.257861630e-04f, -1.422418959e-03f, -2.011118755e-03f, -1.643928243e-03f, -4.200923193e-04f, +1.029524574e-03f, +1.930348530e-03f, +1.854792197e-03f, +9.661301752e-04f, -1.426663759e-04f, -8.690009463e-04f, -9.615092978e-04f, -5.871595310e-04f, -1.165432034e-04f, +1.633284218e-04f, +1.947956873e-04f, +9.526723781e-05f, - /* 24, 5 */ -1.378299032e-04f, -2.068028652e-04f, -8.955230425e-05f, +2.816184974e-04f, +7.596012646e-04f, +9.980619660e-04f, +6.674055614e-04f, -2.557261963e-04f, -1.362058071e-03f, -2.000005648e-03f, -1.696152289e-03f, -5.115395181e-04f, +9.478047386e-04f, +1.901087985e-03f, +1.887912892e-03f, +1.035280999e-03f, -7.957765930e-05f, -8.418792522e-04f, -9.727951144e-04f, -6.173242692e-04f, -1.422758795e-04f, +1.536765045e-04f, +1.983247179e-04f, +1.023586489e-04f, - /* 24, 6 */ -1.308401336e-04f, -2.064617646e-04f, -1.045134271e-04f, +2.525137807e-04f, +7.326171060e-04f, +9.974074494e-04f, +7.073963828e-04f, -1.864720875e-04f, -1.299806951e-03f, -1.985023411e-03f, -1.744740344e-03f, -6.018506887e-04f, +8.639929140e-04f, +1.867841815e-03f, +1.917595086e-03f, +1.103397612e-03f, -1.498850339e-05f, -8.121396097e-04f, -9.821051828e-04f, -6.470509490e-04f, -1.687916421e-04f, +1.429786744e-04f, +2.012961856e-04f, +1.094921351e-04f, - /* 24, 7 */ -1.237648199e-04f, -2.054044567e-04f, -1.184034872e-04f, +2.239669341e-04f, +7.047969872e-04f, +9.944846402e-04f, +7.448724134e-04f, -1.181729029e-04f, -1.235832990e-03f, -1.966240936e-03f, -1.789602898e-03f, -6.908264855e-04f, +7.782711267e-04f, +1.830660078e-03f, +1.943736019e-03f, +1.170305979e-03f, +5.097296116e-05f, -7.797966533e-04f, -9.893601617e-04f, -6.762415789e-04f, -1.960401183e-04f, +1.312236785e-04f, +2.036694644e-04f, +1.166379381e-04f, - /* 24, 8 */ -1.166379381e-04f, -2.036694644e-04f, -1.312236785e-04f, +1.960401183e-04f, +6.762415789e-04f, +9.893601617e-04f, +7.797966533e-04f, -5.097296116e-05f, -1.170305979e-03f, -1.943736019e-03f, -1.830660078e-03f, -7.782711267e-04f, +6.908264855e-04f, +1.789602898e-03f, +1.966240936e-03f, +1.235832990e-03f, +1.181729029e-04f, -7.448724134e-04f, -9.944846402e-04f, -7.047969872e-04f, -2.239669341e-04f, +1.184034872e-04f, +2.054044567e-04f, +1.237648199e-04f, - /* 24, 9 */ -1.094921351e-04f, -2.012961856e-04f, -1.429786744e-04f, +1.687916421e-04f, +6.470509490e-04f, +9.821051828e-04f, +8.121396097e-04f, +1.498850339e-05f, -1.103397612e-03f, -1.917595086e-03f, -1.867841815e-03f, -8.639929140e-04f, +6.018506887e-04f, +1.744740344e-03f, +1.985023411e-03f, +1.299806951e-03f, +1.864720875e-04f, -7.073963828e-04f, -9.974074494e-04f, -7.326171060e-04f, -2.525137807e-04f, +1.045134271e-04f, +2.064617646e-04f, +1.308401336e-04f, - /* 24,10 */ -1.023586489e-04f, -1.983247179e-04f, -1.536765045e-04f, +1.422758795e-04f, +6.173242692e-04f, +9.727951144e-04f, +8.418792522e-04f, +7.957765930e-05f, -1.035280999e-03f, -1.887912892e-03f, -1.901087985e-03f, -9.478047386e-04f, +5.115395181e-04f, +1.696152289e-03f, +2.000005648e-03f, +1.362058071e-03f, +2.557261963e-04f, -6.674055614e-04f, -9.980619660e-04f, -7.596012646e-04f, -2.816184974e-04f, +8.955230425e-05f, +2.068028652e-04f, +1.378299032e-04f, - /* 24,11 */ -9.526723781e-05f, -1.947956873e-04f, -1.633284218e-04f, +1.165432034e-04f, +5.871595310e-04f, +9.615092978e-04f, +8.690009463e-04f, +1.426663759e-04f, -9.661301752e-04f, -1.854792197e-03f, -1.930348530e-03f, -1.029524574e-03f, +4.200923193e-04f, +1.643928243e-03f, +2.011118755e-03f, +1.422418959e-03f, +3.257861630e-04f, -6.249444785e-04f, -9.963864071e-04f, -7.856484910e-04f, -3.112151687e-04f, +7.352252002e-05f, +2.063902871e-04f, +1.446989102e-04f, - /* 24,12 */ -8.824611727e-05f, -1.907500791e-04f, -1.719487619e-04f, +9.163993343e-05f, +5.566532714e-04f, +9.483306864e-04f, +8.934973649e-04f, +2.041325004e-04f, -8.961196099e-04f, -1.818343426e-03f, -1.955583535e-03f, -1.108975953e-03f, +3.277114722e-04f, +1.588167145e-03f, +2.018303000e-03f, +1.480725107e-03f, +3.964985305e-04f, -5.800651921e-04f, -9.923241157e-04f, -8.106578209e-04f, -3.412342375e-04f, +5.643017558e-05f, +2.051877883e-04f, +1.514107898e-04f, - /* 24,13 */ -8.132190552e-05f, -1.862290729e-04f, -1.795547962e-04f, +6.760829922e-05f, +5.259003096e-04f, +9.333455225e-04f, +9.153683790e-04f, +2.638601140e-04f, -8.254237228e-04f, -1.778684302e-03f, -1.976763286e-03f, -1.185988431e-03f, +2.346018520e-04f, +1.528977139e-03f, +2.021508037e-03f, +1.536815378e-03f, +4.677058239e-04f, -5.328272649e-04f, -9.858238344e-04f, -8.345286135e-04f, -3.716026326e-04f, +3.828516688e-05f, +2.031605347e-04f, +1.579281336e-04f, - /* 24,14 */ -7.451957718e-05f, -1.812738819e-04f, -1.861665779e-04f, +4.448641826e-05f, +4.949934945e-04f, +9.166430096e-04f, +9.346209288e-04f, +3.217397652e-04f, -7.542164043e-04f, -1.735939471e-03f, -1.993868298e-03f, -1.260398022e-03f, +1.409702826e-04f, +1.466475318e-03f, +2.020693110e-03f, +1.590532482e-03f, +5.392469404e-04f, -4.832977173e-04f, -9.768399670e-04f, -8.571608722e-04f, -4.022439121e-04f, +1.910126829e-05f, +2.002752798e-04f, +1.642126010e-04f, - /* 24,15 */ -6.786242515e-05f, -1.759255972e-04f, -1.918067822e-04f, +2.230828855e-05f, +4.640234647e-04f, +8.983149818e-04f, +9.512688743e-04f, +3.776686811e-04f, -6.826705419e-04f, -1.690240096e-03f, -2.006889303e-03f, -1.332047630e-03f, +4.702498413e-05f, +1.400787443e-03f, +2.015827225e-03f, +1.641723450e-03f, +6.109575553e-04f, -4.315509563e-04f, -9.653328276e-04f, -8.784555707e-04f, -4.330784212e-04f, -1.103795304e-06f, +1.965005420e-04f, +1.702250368e-04f, - /* 24, 0 */ +3.919255962e-05f, -2.280943782e-04f, -5.361345988e-04f, -4.457457641e-04f, +2.990589649e-04f, +1.295797675e-03f, +1.605517166e-03f, +5.875816565e-04f, -1.289084098e-03f, -2.596166105e-03f, -2.129939921e-03f, -7.496988250e-05f, +2.037180601e-03f, +2.625542335e-03f, +1.404160874e-03f, -4.837783526e-04f, -1.582480410e-03f, -1.345619201e-03f, -3.621830939e-04f, +4.180945199e-04f, +5.469818219e-04f, +2.499414065e-04f, -2.888372260e-05f, -8.895700627e-05f, - /* 24, 1 */ +4.853777483e-05f, -2.065137544e-04f, -5.236754574e-04f, -4.705972357e-04f, +2.371311675e-04f, +1.243196859e-03f, +1.622959247e-03f, +6.876650067e-04f, -1.171710060e-03f, -2.559351067e-03f, -2.215991331e-03f, -2.246678327e-04f, +1.937986318e-03f, +2.647321756e-03f, +1.516528605e-03f, -3.765445934e-04f, -1.553807591e-03f, -1.392405213e-03f, -4.263006320e-04f, +3.876486968e-04f, +5.560961676e-04f, +2.719611444e-04f, -1.761075235e-05f, -9.068976925e-05f, - /* 24, 2 */ +5.692498928e-05f, -1.852878923e-04f, -5.097279107e-04f, -4.926555685e-04f, +1.765921503e-04f, +1.188077447e-03f, +1.634867875e-03f, +7.837567953e-04f, -1.052453928e-03f, -2.515280079e-03f, -2.295086316e-03f, -3.736412373e-04f, +1.832653524e-03f, +2.661371990e-03f, +1.625780509e-03f, -2.661868955e-04f, -1.519477670e-03f, -1.435905115e-03f, -4.911987788e-04f, +3.544256494e-04f, +5.633598944e-04f, +2.940548284e-04f, -5.378471232e-06f, -9.187426948e-05f, - /* 24, 3 */ +6.436469025e-05f, -1.644995777e-04f, -4.944173708e-04f, -5.119388451e-04f, +1.176233517e-04f, +1.130703462e-03f, +1.641323506e-03f, +8.756040923e-04f, -9.317326579e-04f, -2.464159993e-03f, -2.367001633e-03f, -5.214100591e-04f, +1.721501142e-03f, +2.667586958e-03f, +1.731516399e-03f, -1.530278412e-04f, -1.479490407e-03f, -1.475875084e-03f, -5.566555092e-04f, +3.184551797e-04f, +5.686591450e-04f, +3.161189305e-04f, +7.802761507e-06f, -9.246489856e-05f, - /* 24, 4 */ +7.087197068e-05f, -1.442258278e-04f, -4.778705046e-04f, -5.284761768e-04f, +6.039472401e-05f, +1.071341110e-03f, +1.642425167e-03f, +9.629733481e-04f, -8.099633882e-04f, -2.406220702e-03f, -2.431540042e-03f, -6.674987371e-04f, +1.604869446e-03f, +2.665887444e-03f, +1.833344283e-03f, -3.740504807e-05f, -1.433866725e-03f, -1.512079220e-03f, -6.224402836e-04f, +2.797797731e-04f, +5.718846156e-04f, +3.380454975e-04f, +2.191686946e-05f, -9.241721523e-05f, - /* 24, 5 */ +7.646625331e-05f, -1.245377292e-04f, -4.602146020e-04f, -5.423072437e-04f, +5.064318866e-06f, +1.010257658e-03f, +1.638289725e-03f, +1.045651008e-03f, -6.875618648e-04f, -2.341714107e-03f, -2.488530931e-03f, -8.114379517e-04f, +1.483118851e-03f, +2.656221571e-03f, +1.930881931e-03f, +8.032993590e-05f, -1.382648990e-03f, -1.544290670e-03f, -6.883148110e-04f, +2.384547825e-04f, +5.729322223e-04f, +3.597225244e-04f, +3.694190340e-05f, -9.168826568e-05f, - /* 24, 6 */ +8.117100143e-05f, -1.055003114e-04f, -4.415769617e-04f, -5.534817998e-04f, -4.822206513e-05f, +9.477203224e-04f, +1.629051096e-03f, +1.123444034e-03f, -5.649408783e-04f, -2.270913001e-03f, -2.537830838e-03f, -9.527663633e-04f, +1.356628624e-03f, +2.638565156e-03f, +2.023758423e-03f, +1.998128074e-04f, -1.325901212e-03f, -1.572292748e-03f, -7.540338642e-04f, +1.945485578e-04f, +5.717037624e-04f, +3.810343609e-04f, +5.284991195e-05f, -9.023690790e-05f, - /* 24, 7 */ +8.501341847e-05f, -8.717245610e-05f, -4.220842946e-04f, -5.620591450e-04f, -9.933117260e-05f, +8.839951872e-04f, +1.614859393e-03f, +1.196180345e-03f, -4.425087329e-04f, -2.194109877e-03f, -2.579323863e-03f, -1.091032318e-03f, +1.225795515e-03f, +2.612921966e-03f, +2.111615667e-03f, +3.206677492e-04f, -1.263709151e-03f, -1.595880025e-03f, -8.193461436e-04f, +1.481425192e-04f, +5.681075679e-04f, +4.018621494e-04f, +6.960683992e-05f, -8.802413824e-05f, - /* 24, 8 */ +8.802413824e-05f, -6.960683992e-05f, -4.018621494e-04f, -5.681075679e-04f, -1.481425192e-04f, +8.193461436e-04f, +1.595880025e-03f, +1.263709151e-03f, -3.206677492e-04f, -2.111615667e-03f, -2.612921966e-03f, -1.225795515e-03f, +1.091032318e-03f, +2.579323863e-03f, +2.194109877e-03f, +4.425087329e-04f, -1.196180345e-03f, -1.614859393e-03f, -8.839951872e-04f, +9.933117260e-05f, +5.620591450e-04f, +4.220842946e-04f, +8.717245610e-05f, -8.501341847e-05f, - /* 24, 9 */ +9.023690790e-05f, -5.284991195e-05f, -3.810343609e-04f, -5.717037624e-04f, -1.945485578e-04f, +7.540338642e-04f, +1.572292748e-03f, +1.325901212e-03f, -1.998128074e-04f, -2.023758423e-03f, -2.638565156e-03f, -1.356628624e-03f, +9.527663633e-04f, +2.537830838e-03f, +2.270913001e-03f, +5.649408783e-04f, -1.123444034e-03f, -1.629051096e-03f, -9.477203224e-04f, +4.822206513e-05f, +5.534817998e-04f, +4.415769617e-04f, +1.055003114e-04f, -8.117100143e-05f, - /* 24,10 */ +9.168826568e-05f, -3.694190340e-05f, -3.597225244e-04f, -5.729322223e-04f, -2.384547825e-04f, +6.883148110e-04f, +1.544290670e-03f, +1.382648990e-03f, -8.032993590e-05f, -1.930881931e-03f, -2.656221571e-03f, -1.483118851e-03f, +8.114379517e-04f, +2.488530931e-03f, +2.341714107e-03f, +6.875618648e-04f, -1.045651008e-03f, -1.638289725e-03f, -1.010257658e-03f, -5.064318866e-06f, +5.423072437e-04f, +4.602146020e-04f, +1.245377292e-04f, -7.646625331e-05f, - /* 24,11 */ +9.241721523e-05f, -2.191686946e-05f, -3.380454975e-04f, -5.718846156e-04f, -2.797797731e-04f, +6.224402836e-04f, +1.512079220e-03f, +1.433866725e-03f, +3.740504807e-05f, -1.833344283e-03f, -2.665887444e-03f, -1.604869446e-03f, +6.674987371e-04f, +2.431540042e-03f, +2.406220702e-03f, +8.099633882e-04f, -9.629733481e-04f, -1.642425167e-03f, -1.071341110e-03f, -6.039472401e-05f, +5.284761768e-04f, +4.778705046e-04f, +1.442258278e-04f, -7.087197068e-05f, - /* 24,12 */ +9.246489856e-05f, -7.802761507e-06f, -3.161189305e-04f, -5.686591450e-04f, -3.184551797e-04f, +5.566555092e-04f, +1.475875084e-03f, +1.479490407e-03f, +1.530278412e-04f, -1.731516399e-03f, -2.667586958e-03f, -1.721501142e-03f, +5.214100591e-04f, +2.367001633e-03f, +2.464159993e-03f, +9.317326579e-04f, -8.756040923e-04f, -1.641323506e-03f, -1.130703462e-03f, -1.176233517e-04f, +5.119388451e-04f, +4.944173708e-04f, +1.644995777e-04f, -6.436469025e-05f, - /* 24,13 */ +9.187426948e-05f, +5.378471232e-06f, -2.940548284e-04f, -5.633598944e-04f, -3.544256494e-04f, +4.911987788e-04f, +1.435905115e-03f, +1.519477670e-03f, +2.661868955e-04f, -1.625780509e-03f, -2.661371990e-03f, -1.832653524e-03f, +3.736412373e-04f, +2.295086316e-03f, +2.515280079e-03f, +1.052453928e-03f, -7.837567953e-04f, -1.634867875e-03f, -1.188077447e-03f, -1.765921503e-04f, +4.926555685e-04f, +5.097279107e-04f, +1.852878923e-04f, -5.692498928e-05f, - /* 24,14 */ +9.068976925e-05f, +1.761075235e-05f, -2.719611444e-04f, -5.560961676e-04f, -3.876486968e-04f, +4.263006320e-04f, +1.392405213e-03f, +1.553807591e-03f, +3.765445934e-04f, -1.516528605e-03f, -2.647321756e-03f, -1.937986318e-03f, +2.246678327e-04f, +2.215991331e-03f, +2.559351067e-03f, +1.171710060e-03f, -6.876650067e-04f, -1.622959247e-03f, -1.243196859e-03f, -2.371311675e-04f, +4.705972357e-04f, +5.236754574e-04f, +2.065137544e-04f, -4.853777483e-05f, - /* 24,15 */ +8.895700627e-05f, +2.888372260e-05f, -2.499414065e-04f, -5.469818219e-04f, -4.180945199e-04f, +3.621830939e-04f, +1.345619201e-03f, +1.582480410e-03f, +4.837783526e-04f, -1.404160874e-03f, -2.625542335e-03f, -2.037180601e-03f, +7.496988250e-05f, +2.129939921e-03f, +2.596166105e-03f, +1.289084098e-03f, -5.875816565e-04f, -1.605517166e-03f, -1.295797675e-03f, -2.990589649e-04f, +4.457457641e-04f, +5.361345988e-04f, +2.280943782e-04f, -3.919255962e-05f, - /* 24, 0 */ +1.848082291e-04f, +3.126544607e-04f, -8.381805218e-05f, -8.698090905e-04f, -1.028447094e-03f, +2.139154673e-04f, +1.954115341e-03f, +2.095678120e-03f, -1.635717275e-04f, -2.819157299e-03f, -2.940044791e-03f, -1.098945345e-04f, +2.833179926e-03f, +2.923929522e-03f, +3.511165299e-04f, -2.017938339e-03f, -2.030476715e-03f, -3.277888569e-04f, +9.926761418e-04f, +9.110442493e-04f, +1.284872781e-04f, -3.065935448e-04f, -1.998565163e-04f, +7.803305534e-06f, - /* 24, 1 */ +1.695814378e-04f, +3.164556508e-04f, -4.103650150e-05f, -8.260698464e-04f, -1.058100498e-03f, +1.024893805e-04f, +1.871044125e-03f, +2.162944507e-03f, +2.203366063e-05f, -2.703524226e-03f, -3.034115138e-03f, -3.291928088e-04f, +2.713944640e-03f, +3.017272284e-03f, +5.397663057e-04f, -1.929900383e-03f, -2.099607997e-03f, -4.436146208e-04f, +9.507629285e-04f, +9.494468230e-04f, +1.748714247e-04f, -2.981837386e-04f, -2.146007649e-04f, -5.699432396e-07f, - /* 24, 2 */ +1.542962580e-04f, +3.180964801e-04f, -2.971107404e-07f, -7.801571616e-04f, -1.081692695e-03f, -6.019646704e-06f, +1.781805749e-03f, +2.219616197e-03f, +2.048848004e-04f, -2.577645910e-03f, -3.115029215e-03f, -5.470211463e-04f, +2.582823494e-03f, +3.098667200e-03f, +7.286710477e-04f, -1.831793311e-03f, -2.161014579e-03f, -5.608747689e-04f, +9.027154107e-04f, +9.846924856e-04f, +2.227798586e-04f, -2.873471247e-04f, -2.289106872e-04f, -9.773337074e-06f, - /* 24, 3 */ +1.390667857e-04f, +3.176854393e-04f, +3.826416878e-05f, -7.324018434e-04f, -1.099310451e-03f, -1.111689527e-04f, +1.686962051e-03f, +2.265625589e-03f, +3.841903571e-04f, -2.442181508e-03f, -3.182489175e-03f, -7.624077328e-04f, +2.440359294e-03f, +3.167649091e-03f, +9.169693475e-04f, -1.723899657e-03f, -2.214230624e-03f, -6.790305367e-04f, +8.485750922e-04f, +1.016463150e-03f, +2.720045869e-04f, -2.740180422e-04f, -2.426519708e-04f, -1.978701792e-05f, - /* 24, 4 */ +1.240005643e-04f, +3.153390489e-04f, +7.453005747e-05f, -6.831329371e-04f, -1.111069621e-03f, -2.125447010e-04f, +1.587090707e-03f, +2.300958285e-03f, +5.591862212e-04f, -2.297830066e-03f, -3.236262287e-03f, -9.743929228e-04f, +2.287150577e-03f, +3.223808711e-03f, +1.103792665e-03f, -1.606554759e-03f, -2.258822083e-03f, -7.975248409e-04f, +7.884177261e-04f, +1.044449054e-03f, +3.223209063e-04f, -2.581440514e-04f, -2.556870681e-04f, -3.058317705e-05f, - /* 24, 5 */ +1.091981202e-04f, +3.111807515e-04f, +1.084019636e-04f, -6.326758693e-04f, -1.117113666e-03f, -3.097634105e-04f, +1.482781879e-03f, +2.325652312e-03f, +7.291390495e-04f, -2.145326692e-03f, -3.276181809e-03f, -1.182034015e-03f, +2.123848782e-03f, +3.266795190e-03f, +1.288269679e-03f, -1.480145805e-03f, -2.294389599e-03f, -9.157848908e-04f, +7.223538352e-04f, +1.068350860e-03f, +3.734881809e-04f, -2.396868442e-04f, -2.678760406e-04f, -4.212586861e-05f, - /* 24, 6 */ +9.475256757e-05f, +3.053397988e-04f, +1.397998805e-04f, -5.813506695e-04f, -1.117612060e-03f, -4.024732802e-04f, +1.374634870e-03f, +2.339797075e-03f, +8.933496022e-04f, -1.985438555e-03f, -3.302147511e-03f, -1.384409934e-03f, +1.951155148e-03f, +3.296318191e-03f, +1.469530695e-03f, -1.345110577e-03f, -2.320571262e-03f, -1.033224945e-03f, +6.505290403e-04f, +1.087881752e-03f, +4.252507475e-04f, -2.186230940e-04f, -2.790774568e-04f, -5.437087857e-05f, - /* 24, 7 */ +8.074928352e-05f, +2.979501429e-04f, +1.686621699e-04f, -5.294702777e-04f, -1.112758583e-03f, -4.903553074e-04f, +1.263254779e-03f, +2.343532047e-03f, +1.051155860e-03f, -1.818960757e-03f, -3.314125843e-03f, -1.580625796e-03f, +1.769817333e-03f, +3.312149758e-03f, +1.646712089e-03f, -1.201935910e-03f, -2.337045209e-03f, -1.149249197e-03f, +5.731241934e-04f, +1.102769514e-03f, +4.773389469e-04f, -1.949452358e-04f, -2.891493374e-04f, -6.726565227e-05f, - /* 24, 8 */ +6.726565227e-05f, +2.891493374e-04f, +1.949452358e-04f, -4.773389469e-04f, -1.102769514e-03f, -5.731241934e-04f, +1.149249197e-03f, +2.337045209e-03f, +1.201935910e-03f, -1.646712089e-03f, -3.312149758e-03f, -1.769817333e-03f, +1.580625796e-03f, +3.314125843e-03f, +1.818960757e-03f, -1.051155860e-03f, -2.343532047e-03f, -1.263254779e-03f, +4.903553074e-04f, +1.112758583e-03f, +5.294702777e-04f, -1.686621699e-04f, -2.979501429e-04f, -8.074928352e-05f, - /* 24, 9 */ +5.437087857e-05f, +2.790774568e-04f, +2.186230940e-04f, -4.252507475e-04f, -1.087881752e-03f, -6.505290403e-04f, +1.033224945e-03f, +2.320571262e-03f, +1.345110577e-03f, -1.469530695e-03f, -3.296318191e-03f, -1.951155148e-03f, +1.384409934e-03f, +3.302147511e-03f, +1.985438555e-03f, -8.933496022e-04f, -2.339797075e-03f, -1.374634870e-03f, +4.024732802e-04f, +1.117612060e-03f, +5.813506695e-04f, -1.397998805e-04f, -3.053397988e-04f, -9.475256757e-05f, - /* 24,10 */ +4.212586861e-05f, +2.678760406e-04f, +2.396868442e-04f, -3.734881809e-04f, -1.068350860e-03f, -7.223538352e-04f, +9.157848908e-04f, +2.294389599e-03f, +1.480145805e-03f, -1.288269679e-03f, -3.266795190e-03f, -2.123848782e-03f, +1.182034015e-03f, +3.276181809e-03f, +2.145326692e-03f, -7.291390495e-04f, -2.325652312e-03f, -1.482781879e-03f, +3.097634105e-04f, +1.117113666e-03f, +6.326758693e-04f, -1.084019636e-04f, -3.111807515e-04f, -1.091981202e-04f, - /* 24,11 */ +3.058317705e-05f, +2.556870681e-04f, +2.581440514e-04f, -3.223209063e-04f, -1.044449054e-03f, -7.884177261e-04f, +7.975248409e-04f, +2.258822083e-03f, +1.606554759e-03f, -1.103792665e-03f, -3.223808711e-03f, -2.287150577e-03f, +9.743929228e-04f, +3.236262287e-03f, +2.297830066e-03f, -5.591862212e-04f, -2.300958285e-03f, -1.587090707e-03f, +2.125447010e-04f, +1.111069621e-03f, +6.831329371e-04f, -7.453005747e-05f, -3.153390489e-04f, -1.240005643e-04f, - /* 24,12 */ +1.978701792e-05f, +2.426519708e-04f, +2.740180422e-04f, -2.720045869e-04f, -1.016463150e-03f, -8.485750922e-04f, +6.790305367e-04f, +2.214230624e-03f, +1.723899657e-03f, -9.169693475e-04f, -3.167649091e-03f, -2.440359294e-03f, +7.624077328e-04f, +3.182489175e-03f, +2.442181508e-03f, -3.841903571e-04f, -2.265625589e-03f, -1.686962051e-03f, +1.111689527e-04f, +1.099310451e-03f, +7.324018434e-04f, -3.826416878e-05f, -3.176854393e-04f, -1.390667857e-04f, - /* 24,13 */ +9.773337074e-06f, +2.289106872e-04f, +2.873471247e-04f, -2.227798586e-04f, -9.846924856e-04f, -9.027154107e-04f, +5.608747689e-04f, +2.161014579e-03f, +1.831793311e-03f, -7.286710477e-04f, -3.098667200e-03f, -2.582823494e-03f, +5.470211463e-04f, +3.115029215e-03f, +2.577645910e-03f, -2.048848004e-04f, -2.219616197e-03f, -1.781805749e-03f, +6.019646704e-06f, +1.081692695e-03f, +7.801571616e-04f, +2.971107404e-07f, -3.180964801e-04f, -1.542962580e-04f, - /* 24,14 */ +5.699432396e-07f, +2.146007649e-04f, +2.981837386e-04f, -1.748714247e-04f, -9.494468230e-04f, -9.507629285e-04f, +4.436146208e-04f, +2.099607997e-03f, +1.929900383e-03f, -5.397663057e-04f, -3.017272284e-03f, -2.713944640e-03f, +3.291928088e-04f, +3.034115138e-03f, +2.703524226e-03f, -2.203366063e-05f, -2.162944507e-03f, -1.871044125e-03f, -1.024893805e-04f, +1.058100498e-03f, +8.260698464e-04f, +4.103650150e-05f, -3.164556508e-04f, -1.695814378e-04f, - /* 24,15 */ -7.803305534e-06f, +1.998565163e-04f, +3.065935448e-04f, -1.284872781e-04f, -9.110442493e-04f, -9.926761418e-04f, +3.277888569e-04f, +2.030476715e-03f, +2.017938339e-03f, -3.511165299e-04f, -2.923929522e-03f, -2.833179926e-03f, +1.098945345e-04f, +2.940044791e-03f, +2.819157299e-03f, +1.635717275e-04f, -2.095678120e-03f, -1.954115341e-03f, -2.139154673e-04f, +1.028447094e-03f, +8.698090905e-04f, +8.381805218e-05f, -3.126544607e-04f, -1.848082291e-04f, - /* 24, 0 */ -1.364396009e-04f, -7.446376994e-05f, +5.066257662e-04f, +2.030015760e-04f, -9.054261715e-04f, -1.195525937e-03f, +4.683850093e-04f, +2.213825561e-03f, +1.188944940e-03f, -1.856378227e-03f, -2.833970964e-03f, -1.144377463e-04f, +2.758138339e-03f, +2.020697482e-03f, -1.023174933e-03f, -2.248631080e-03f, -6.097868283e-04f, +1.146194663e-03f, +9.693248739e-04f, -1.479047908e-04f, -5.177782008e-04f, -1.250536964e-04f, +1.414906982e-04f, +2.710774794e-05f, - /* 24, 1 */ -1.307026563e-04f, -8.975162518e-05f, +4.924131238e-04f, +2.542107757e-04f, -8.382130226e-04f, -1.235986710e-03f, +3.277877666e-04f, +2.166758574e-03f, +1.345406789e-03f, -1.683014413e-03f, -2.893234801e-03f, -3.426248829e-04f, +2.666119545e-03f, +2.174888063e-03f, -8.489895579e-04f, -2.270723567e-03f, -7.511023835e-04f, +1.088054225e-03f, +1.029345157e-03f, -8.915647260e-05f, -5.256253050e-04f, -1.535492882e-04f, +1.457592711e-04f, +3.374854096e-05f, - /* 24, 2 */ -1.243758393e-04f, -1.032931784e-04f, +4.753985127e-04f, +3.013338450e-04f, -7.682542954e-04f, -1.267577330e-03f, +1.888607322e-04f, +2.107952975e-03f, +1.491738775e-03f, -1.501737881e-03f, -2.935653267e-03f, -5.687514710e-04f, +2.558401145e-03f, +2.317921172e-03f, -6.673475630e-04f, -2.279727032e-03f, -8.914213340e-04f, +1.021228789e-03f, +1.084932315e-03f, -2.702967135e-05f, -5.299376467e-04f, -1.826837732e-04f, +1.491486840e-04f, +4.073257728e-05f, - /* 24, 3 */ -1.175537217e-04f, -1.151066589e-04f, +4.558506985e-04f, +3.442099484e-04f, -6.961194660e-04f, -1.290358261e-03f, +5.243891240e-05f, +2.037998203e-03f, +1.627194859e-03f, -1.313720334e-03f, -2.961057174e-03f, -7.914588446e-04f, +2.435570833e-03f, +2.448830599e-03f, -4.792680017e-04f, -2.275344863e-03f, -1.029819797e-03f, +9.459060659e-04f, +1.135545329e-03f, +3.816638860e-05f, -5.305040204e-04f, -2.122423012e-04f, +1.515631236e-04f, +4.801831197e-05f, - /* 24, 4 */ -1.103288160e-04f, -1.252215041e-04f, +4.340463917e-04f, +3.827158599e-04f, -6.223744454e-04f, -1.304448109e-03f, -8.067840470e-05f, +1.957545178e-03f, +1.751108674e-03f, -1.120165265e-03f, -2.969385358e-03f, -1.009410776e-03f, +2.298313921e-03f, +2.566719612e-03f, -2.858241016e-04f, -2.257363134e-03f, -1.165366520e-03f, +8.623375551e-04f, +1.180661312e-03f, +1.060874480e-04f, -5.271339238e-04f, -2.419953224e-04f, +1.529084143e-04f, +5.555842361e-05f, - /* 24, 5 */ -1.027909684e-04f, -1.336775649e-04f, +4.102676936e-04f, +4.167655723e-04f, -5.475775503e-04f, -1.310021272e-03f, -2.097323863e-04f, +1.867300846e-03f, +1.862896930e-03f, -9.222997333e-04f, -2.960684559e-03f, -1.221302229e-03f, +2.147409148e-03f, +2.670767418e-03f, -8.813668768e-05f, -2.225653372e-03f, -1.297129225e-03f, +7.708383352e-04f, +1.219779926e-03f, +1.763556677e-04f, -5.196599496e-04f, -2.716999419e-04f, +1.530928622e-04f, +6.329988035e-05f, - /* 24, 6 */ -9.502680145e-05f, -1.405242734e-04f, +3.847995909e-04f, +4.463096266e-04f, -4.722756447e-04f, -1.307305241e-03f, -3.340090076e-04f, +1.768022423e-03f, +1.962062202e-03f, -7.213660470e-04f, -2.935108571e-03f, -1.425867893e-03f, +1.983723834e-03f, +2.760235146e-03f, +1.126327965e-04f, -2.180174758e-03f, -1.424181074e-03f, +6.717863708e-04f, +1.252427754e-03f, +2.485613970e-04f, -5.079400707e-04f, -3.011014490e-04f, +1.520281231e-04f, +7.118405837e-05f, - /* 24, 7 */ -8.711921055e-05f, -1.458197836e-04f, +3.579275186e-04f, +4.713341756e-04f, -3.970004792e-04f, -1.296577576e-03f, -4.528429958e-04f, +1.660511380e-03f, +2.048195092e-03f, -5.186134147e-04f, -2.892916666e-03f, -1.621890436e-03f, +1.808208423e-03f, +2.834471303e-03f, +3.152896222e-04f, -2.120975750e-03f, -1.545607217e-03f, +5.656213428e-04f, +1.278162587e-03f, +3.222652495e-04f, -4.918597964e-04f, -3.299350101e-04f, +1.496300883e-04f, +7.914691395e-05f, - /* 24, 8 */ -7.914691395e-05f, -1.496300883e-04f, +3.299350101e-04f, +4.918597964e-04f, -3.222652495e-04f, -1.278162587e-03f, -5.656213428e-04f, +1.545607217e-03f, +2.120975750e-03f, -3.152896222e-04f, -2.834471303e-03f, -1.808208423e-03f, +1.621890436e-03f, +2.892916666e-03f, +5.186134147e-04f, -2.048195092e-03f, -1.660511380e-03f, +4.528429958e-04f, +1.296577576e-03f, +3.970004792e-04f, -4.713341756e-04f, -3.579275186e-04f, +1.458197836e-04f, +8.711921055e-05f, - /* 24, 9 */ -7.118405837e-05f, -1.520281231e-04f, +3.011014490e-04f, +5.079400707e-04f, -2.485613970e-04f, -1.252427754e-03f, -6.717863708e-04f, +1.424181074e-03f, +2.180174758e-03f, -1.126327965e-04f, -2.760235146e-03f, -1.983723834e-03f, +1.425867893e-03f, +2.935108571e-03f, +7.213660470e-04f, -1.962062202e-03f, -1.768022423e-03f, +3.340090076e-04f, +1.307305241e-03f, +4.722756447e-04f, -4.463096266e-04f, -3.847995909e-04f, +1.405242734e-04f, +9.502680145e-05f, - /* 24,10 */ -6.329988035e-05f, -1.530928622e-04f, +2.716999419e-04f, +5.196599496e-04f, -1.763556677e-04f, -1.219779926e-03f, -7.708383352e-04f, +1.297129225e-03f, +2.225653372e-03f, +8.813668768e-05f, -2.670767418e-03f, -2.147409148e-03f, +1.221302229e-03f, +2.960684559e-03f, +9.222997333e-04f, -1.862896930e-03f, -1.867300846e-03f, +2.097323863e-04f, +1.310021272e-03f, +5.475775503e-04f, -4.167655723e-04f, -4.102676936e-04f, +1.336775649e-04f, +1.027909684e-04f, - /* 24,11 */ -5.555842361e-05f, -1.529084143e-04f, +2.419953224e-04f, +5.271339238e-04f, -1.060874480e-04f, -1.180661312e-03f, -8.623375551e-04f, +1.165366520e-03f, +2.257363134e-03f, +2.858241016e-04f, -2.566719612e-03f, -2.298313921e-03f, +1.009410776e-03f, +2.969385358e-03f, +1.120165265e-03f, -1.751108674e-03f, -1.957545178e-03f, +8.067840470e-05f, +1.304448109e-03f, +6.223744454e-04f, -3.827158599e-04f, -4.340463917e-04f, +1.252215041e-04f, +1.103288160e-04f, - /* 24,12 */ -4.801831197e-05f, -1.515631236e-04f, +2.122423012e-04f, +5.305040204e-04f, -3.816638860e-05f, -1.135545329e-03f, -9.459060659e-04f, +1.029819797e-03f, +2.275344863e-03f, +4.792680017e-04f, -2.448830599e-03f, -2.435570833e-03f, +7.914588446e-04f, +2.961057174e-03f, +1.313720334e-03f, -1.627194859e-03f, -2.037998203e-03f, -5.243891240e-05f, +1.290358261e-03f, +6.961194660e-04f, -3.442099484e-04f, -4.558506985e-04f, +1.151066589e-04f, +1.175537217e-04f, - /* 24,13 */ -4.073257728e-05f, -1.491486840e-04f, +1.826837732e-04f, +5.299376467e-04f, +2.702967135e-05f, -1.084932315e-03f, -1.021228789e-03f, +8.914213340e-04f, +2.279727032e-03f, +6.673475630e-04f, -2.317921172e-03f, -2.558401145e-03f, +5.687514710e-04f, +2.935653267e-03f, +1.501737881e-03f, -1.491738775e-03f, -2.107952975e-03f, -1.888607322e-04f, +1.267577330e-03f, +7.682542954e-04f, -3.013338450e-04f, -4.753985127e-04f, +1.032931784e-04f, +1.243758393e-04f, - /* 24,14 */ -3.374854096e-05f, -1.457592711e-04f, +1.535492882e-04f, +5.256253050e-04f, +8.915647260e-05f, -1.029345157e-03f, -1.088054225e-03f, +7.511023835e-04f, +2.270723567e-03f, +8.489895579e-04f, -2.174888063e-03f, -2.666119545e-03f, +3.426248829e-04f, +2.893234801e-03f, +1.683014413e-03f, -1.345406789e-03f, -2.166758574e-03f, -3.277877666e-04f, +1.235986710e-03f, +8.382130226e-04f, -2.542107757e-04f, -4.924131238e-04f, +8.975162518e-05f, +1.307026563e-04f, - /* 24,15 */ -2.710774794e-05f, -1.414906982e-04f, +1.250536964e-04f, +5.177782008e-04f, +1.479047908e-04f, -9.693248739e-04f, -1.146194663e-03f, +6.097868283e-04f, +2.248631080e-03f, +1.023174933e-03f, -2.020697482e-03f, -2.758138339e-03f, +1.144377463e-04f, +2.833970964e-03f, +1.856378227e-03f, -1.188944940e-03f, -2.213825561e-03f, -4.683850093e-04f, +1.195525937e-03f, +9.054261715e-04f, -2.030015760e-04f, -5.066257662e-04f, +7.446376994e-05f, +1.364396009e-04f, - /* 20, 0 */ +8.618377023e-05f, +6.063813654e-04f, +5.504304823e-05f, -1.285351444e-03f, -7.884572117e-04f, +1.698526656e-03f, +2.051642156e-03f, -1.208844608e-03f, -3.071261118e-03f, -1.337669627e-04f, +3.018986987e-03f, +1.434631920e-03f, -1.928392685e-03f, -1.831072241e-03f, +6.629429882e-04f, +1.334851066e-03f, +2.665316224e-05f, -6.158469302e-04f, -1.222533602e-04f, +1.824770389e-04f, - /* 20, 1 */ +5.170459821e-05f, +5.921181369e-04f, +1.325211661e-04f, -1.227728603e-03f, -9.042412445e-04f, +1.556639033e-03f, +2.157910711e-03f, -9.769935819e-04f, -3.101316201e-03f, -4.002989059e-04f, +2.944767882e-03f, +1.652572896e-03f, -1.788832610e-03f, -1.953018956e-03f, +5.284364506e-04f, +1.375515478e-03f, +1.120226944e-04f, -6.201709543e-04f, -1.596279961e-04f, +5.435082024e-04f, - /* 20, 2 */ +1.907003227e-05f, +5.734309365e-04f, +2.052951315e-04f, -1.162740775e-03f, -1.009657150e-03f, +1.406715362e-03f, +2.246673287e-03f, -7.408907618e-04f, -3.109053425e-03f, -6.638330580e-04f, +2.849051730e-03f, +1.860929207e-03f, -1.633773999e-03f, -2.063170847e-03f, +3.857714352e-04f, +1.406686835e-03f, +2.004651158e-04f, -6.190441221e-04f, -1.979927117e-04f, +1.783734753e-04f, - /* 20, 3 */ -1.149811868e-05f, +5.507184044e-04f, +2.729399910e-04f, -1.091184321e-03f, -1.104169932e-03f, +1.250098855e-03f, +2.317552276e-03f, -5.023622502e-04f, -3.094549152e-03f, -9.223980063e-04f, +2.732457430e-03f, +2.058021578e-03f, -1.464165902e-03f, -2.160404235e-03f, +2.358727957e-04f, +1.427769061e-03f, +2.913280278e-04f, -6.121962531e-04f, -2.370049292e-04f, +1.734448317e-04f, - /* 20, 4 */ -3.981122763e-05f, +5.243991976e-04f, +3.350936324e-04f, -1.013885501e-03f, -1.187349554e-03f, +1.088157908e-03f, +2.370318438e-03f, -2.632332411e-04f, -3.058053364e-03f, -1.174062761e-03f, +2.595770512e-03f, +2.242244162e-03f, -1.281088328e-03f, -2.243678681e-03f, +7.975052491e-05f, +1.438235101e-03f, +3.839113875e-04f, -5.994006494e-04f, -2.762968272e-04f, +1.660093790e-04f, - /* 20, 5 */ -6.571426612e-05f, +4.949070444e-04f, +3.914578654e-04f, -9.316921975e-04f, -1.258871974e-03f, +9.222740706e-04f, +2.404890527e-03f, -2.531308203e-05f, -2.999986642e-03f, -1.416952434e-03f, +2.439937364e-03f, +2.412078410e-03f, -1.085745014e-03f, -2.312047403e-03f, -8.150702581e-05f, +1.437633739e-03f, +4.774724341e-04f, -5.804781630e-04f, -3.154780847e-04f, +1.559797103e-04f, - /* 20, 6 */ -8.908584503e-05f, +4.626858369e-04f, +4.417988543e-04f, -8.454656803e-04f, -1.318519207e-03f, +7.538301290e-04f, +2.421333651e-03f, +2.096193816e-04f, -2.920935727e-03f, -1.649263420e-03f, +2.266058084e-03f, +2.566106310e-03f, -8.794550343e-04f, -2.364667062e-03f, -2.467407834e-04f, +1.425595879e-03f, +5.712311871e-04f, -5.553009320e-04f, -3.541389831e-04f, +1.432933926e-04f, - /* 20, 7 */ -1.098378936e-04f, +4.281848093e-04f, +4.859469205e-04f, -7.560724855e-04f, -1.366178446e-03f, +5.841984075e-04f, +2.419856400e-03f, +4.398300532e-04f, -2.821647705e-03f, -1.869277969e-03f, +2.075378006e-03f, +2.703022864e-03f, -6.636433018e-04f, -2.400806791e-03f, -4.147293943e-04f, +1.401840253e-03f, +6.643764801e-04f, -5.237957356e-04f, -3.918538488e-04f, +1.279149940e-04f, - /* 20, 8 */ -1.279149940e-04f, +3.918538488e-04f, +5.237957356e-04f, -6.643764801e-04f, -1.401840253e-03f, +4.147293943e-04f, +2.400806791e-03f, +6.636433018e-04f, -2.703022864e-03f, -2.075378006e-03f, +1.869277969e-03f, +2.821647705e-03f, -4.398300532e-04f, -2.419856400e-03f, -5.841984075e-04f, +1.366178446e-03f, +7.560724855e-04f, -4.859469205e-04f, -4.281848093e-04f, +1.098378936e-04f, - /* 20, 9 */ -1.432933926e-04f, +3.541389831e-04f, +5.553009320e-04f, -5.712311871e-04f, -1.425595879e-03f, +2.467407834e-04f, +2.364667062e-03f, +8.794550343e-04f, -2.566106310e-03f, -2.266058084e-03f, +1.649263420e-03f, +2.920935727e-03f, -2.096193816e-04f, -2.421333651e-03f, -7.538301290e-04f, +1.318519207e-03f, +8.454656803e-04f, -4.417988543e-04f, -4.626858369e-04f, +8.908584503e-05f, - /* 20,10 */ -1.559797103e-04f, +3.154780847e-04f, +5.804781630e-04f, -4.774724341e-04f, -1.437633739e-03f, +8.150702581e-05f, +2.312047403e-03f, +1.085745014e-03f, -2.412078410e-03f, -2.439937364e-03f, +1.416952434e-03f, +2.999986642e-03f, +2.531308203e-05f, -2.404890527e-03f, -9.222740706e-04f, +1.258871974e-03f, +9.316921975e-04f, -3.914578654e-04f, -4.949070444e-04f, +6.571426612e-05f, - /* 20,11 */ -1.660093790e-04f, +2.762968272e-04f, +5.994006494e-04f, -3.839113875e-04f, -1.438235101e-03f, -7.975052491e-05f, +2.243678681e-03f, +1.281088328e-03f, -2.242244162e-03f, -2.595770512e-03f, +1.174062761e-03f, +3.058053364e-03f, +2.632332411e-04f, -2.370318438e-03f, -1.088157908e-03f, +1.187349554e-03f, +1.013885501e-03f, -3.350936324e-04f, -5.243991976e-04f, +3.981122763e-05f, - /* 20,12 */ -1.734448317e-04f, +2.370049292e-04f, +6.121962531e-04f, -2.913280278e-04f, -1.427769061e-03f, -2.358727957e-04f, +2.160404235e-03f, +1.464165902e-03f, -2.058021578e-03f, -2.732457430e-03f, +9.223980063e-04f, +3.094549152e-03f, +5.023622502e-04f, -2.317552276e-03f, -1.250098855e-03f, +1.104169932e-03f, +1.091184321e-03f, -2.729399910e-04f, -5.507184044e-04f, +1.149811868e-05f, - /* 20,13 */ -1.783734753e-04f, +1.979927117e-04f, +6.190441221e-04f, -2.004651158e-04f, -1.406686835e-03f, -3.857714352e-04f, +2.063170847e-03f, +1.633773999e-03f, -1.860929207e-03f, -2.849051730e-03f, +6.638330580e-04f, +3.109053425e-03f, +7.408907618e-04f, -2.246673287e-03f, -1.406715362e-03f, +1.009657150e-03f, +1.162740775e-03f, -2.052951315e-04f, -5.734309365e-04f, -1.907003227e-05f, - /* 20,14 */ -5.435082024e-04f, +1.596279961e-04f, +6.201709543e-04f, -1.120226944e-04f, -1.375515478e-03f, -5.284364506e-04f, +1.953018956e-03f, +1.788832610e-03f, -1.652572896e-03f, -2.944767882e-03f, +4.002989059e-04f, +3.101316201e-03f, +9.769935819e-04f, -2.157910711e-03f, -1.556639033e-03f, +9.042412445e-04f, +1.227728603e-03f, -1.325211661e-04f, -5.921181369e-04f, -5.170459821e-05f, - /* 20,15 */ -1.824770389e-04f, +1.222533602e-04f, +6.158469302e-04f, -2.665316224e-05f, -1.334851066e-03f, -6.629429882e-04f, +1.831072241e-03f, +1.928392685e-03f, -1.434631920e-03f, -3.018986987e-03f, +1.337669627e-04f, +3.071261118e-03f, +1.208844608e-03f, -2.051642156e-03f, -1.698526656e-03f, +7.884572117e-04f, +1.285351444e-03f, -5.504304823e-05f, -6.063813654e-04f, -8.618377023e-05f, - /* 20, 0 */ -2.034293425e-04f, +2.330977005e-04f, +6.663349221e-04f, -5.788237715e-04f, -1.543506114e-03f, +7.663264997e-04f, +2.637884518e-03f, -5.016073607e-04f, -3.414789539e-03f, -1.613262342e-04f, +3.393842146e-03f, +7.896927597e-04f, -2.589726790e-03f, -9.705894653e-04f, +1.498255744e-03f, +6.923557695e-04f, -6.435044748e-04f, -2.810018705e-04f, +2.009801156e-04f, +0.000000000e+00f, - /* 20, 1 */ -6.015260759e-04f, +1.858917095e-04f, +6.809814437e-04f, -4.649883812e-04f, -1.572899641e-03f, +5.610647582e-04f, +2.661959816e-03f, -2.131645492e-04f, -3.406214168e-03f, -4.825221542e-04f, +3.343371924e-03f, +1.074767465e-03f, -2.517456780e-03f, -1.171859801e-03f, +1.437039798e-03f, +8.043742580e-04f, -6.123036842e-04f, -3.290468323e-04f, +1.953154138e-04f, -3.513221827e-04f, - /* 20, 2 */ -1.932641301e-04f, +1.399021716e-04f, +6.877130848e-04f, -3.520154127e-04f, -1.586701669e-03f, +3.567578182e-04f, +2.662213263e-03f, +7.301175991e-05f, -3.368394423e-03f, -7.993627115e-04f, +3.263658730e-03f, +1.354174959e-03f, -2.421279702e-03f, -1.368123194e-03f, +1.359912061e-03f, +9.136368247e-04f, -5.726346524e-04f, -3.766412744e-04f, +1.862701081e-04f, +2.243392330e-05f, - /* 20, 3 */ -1.771389305e-04f, +9.560377684e-05f, +6.868748812e-04f, -2.410152618e-04f, -1.585326694e-03f, +1.553000173e-04f, +2.639129491e-03f, +3.543525656e-04f, -3.301882608e-03f, -1.108991788e-03f, +3.155261081e-03f, +1.625281932e-03f, -2.301637793e-03f, -1.557365345e-03f, +1.267093119e-03f, +1.018881552e-03f, -5.244930508e-04f, -4.231658296e-04f, +1.737162070e-04f, +3.471505729e-05f, - /* 20, 4 */ -1.604844080e-04f, +5.342390613e-05f, +6.788805869e-04f, -1.330327870e-04f, -1.569329509e-03f, -4.149146373e-05f, +2.593408320e-03f, +6.283684809e-04f, -3.207497769e-03f, -1.408623929e-03f, +3.019012048e-03f, +1.885504757e-03f, -2.159209806e-03f, -1.737592880e-03f, +1.158972903e-03f, +1.118840456e-03f, -4.679722330e-04f, -4.679795743e-04f, +1.575667726e-04f, +4.805250238e-05f, - /* 20, 5 */ -1.435528424e-04f, +1.373966937e-05f, +6.642048742e-04f, -2.903827106e-05f, -1.539395067e-03f, -2.318933016e-04f, +2.525953799e-03f, +8.926733333e-04f, -3.086315860e-03f, -1.695571566e-03f, +2.856012327e-03f, +2.132335710e-03f, -1.994908019e-03f, -1.906854484e-03f, +1.036111434e-03f, +1.212253447e-03f, -4.032663270e-04f, -5.104270987e-04f, +1.377794601e-04f, +6.235351094e-05f, - /* 20, 6 */ -1.265803873e-04f, -2.312428639e-05f, +6.433751213e-04f, +7.008046528e-05f, -1.496327216e-03f, -4.142913555e-04f, +2.437861323e-03f, +1.145006429e-03f, -2.939657349e-03f, -1.967271220e-03f, +2.667620552e-03f, +2.363368676e-03f, -1.809872756e-03f, -2.063262017e-03f, +8.992377119e-04f, +1.297882648e-03f, -3.306722314e-04f, -5.498460811e-04f, +1.143596169e-04f, +7.750368078e-05f, - /* 20, 7 */ -1.097853637e-04f, -5.689720211e-05f, +6.169629011e-04f, +1.635247423e-04f, -1.441036462e-03f, -5.871944806e-04f, +2.330402993e-03f, +1.383253258e-03f, -2.769072436e-03f, -2.221308400e-03f, +2.455440941e-03f, +2.576324026e-03f, -1.605464444e-03f, -2.205011397e-03f, +7.492467105e-04f, +1.374526925e-03f, -2.505904541e-04f, -5.855752858e-04f, +8.736287854e-05f, +9.336686615e-05f, - /* 20, 8 */ -9.336686615e-05f, -8.736287854e-05f, +5.855752858e-04f, +2.505904541e-04f, -1.374526925e-03f, -7.492467105e-04f, +2.205011397e-03f, +1.605464444e-03f, -2.576324026e-03f, -2.455440941e-03f, +2.221308400e-03f, +2.769072436e-03f, -1.383253258e-03f, -2.330402993e-03f, +5.871944806e-04f, +1.441036462e-03f, -1.635247423e-04f, -6.169629011e-04f, +5.689720211e-05f, +1.097853637e-04f, - /* 20, 9 */ -7.750368078e-05f, -1.143596169e-04f, +5.498460811e-04f, +3.306722314e-04f, -1.297882648e-03f, -8.992377119e-04f, +2.063262017e-03f, +1.809872756e-03f, -2.363368676e-03f, -2.667620552e-03f, +1.967271220e-03f, +2.939657349e-03f, -1.145006429e-03f, -2.437861323e-03f, +4.142913555e-04f, +1.496327216e-03f, -7.008046528e-05f, -6.433751213e-04f, +2.312428639e-05f, +1.265803873e-04f, - /* 20,10 */ -6.235351094e-05f, -1.377794601e-04f, +5.104270987e-04f, +4.032663270e-04f, -1.212253447e-03f, -1.036111434e-03f, +1.906854484e-03f, +1.994908019e-03f, -2.132335710e-03f, -2.856012327e-03f, +1.695571566e-03f, +3.086315860e-03f, -8.926733333e-04f, -2.525953799e-03f, +2.318933016e-04f, +1.539395067e-03f, +2.903827106e-05f, -6.642048742e-04f, -1.373966937e-05f, +1.435528424e-04f, - /* 20,11 */ -4.805250238e-05f, -1.575667726e-04f, +4.679795743e-04f, +4.679722330e-04f, -1.118840456e-03f, -1.158972903e-03f, +1.737592880e-03f, +2.159209806e-03f, -1.885504757e-03f, -3.019012048e-03f, +1.408623929e-03f, +3.207497769e-03f, -6.283684809e-04f, -2.593408320e-03f, +4.149146373e-05f, +1.569329509e-03f, +1.330327870e-04f, -6.788805869e-04f, -5.342390613e-05f, +1.604844080e-04f, - /* 20,12 */ -3.471505729e-05f, -1.737162070e-04f, +4.231658296e-04f, +5.244930508e-04f, -1.018881552e-03f, -1.267093119e-03f, +1.557365345e-03f, +2.301637793e-03f, -1.625281932e-03f, -3.155261081e-03f, +1.108991788e-03f, +3.301882608e-03f, -3.543525656e-04f, -2.639129491e-03f, -1.553000173e-04f, +1.585326694e-03f, +2.410152618e-04f, -6.868748812e-04f, -9.560377684e-05f, +1.771389305e-04f, - /* 20,13 */ -2.243392330e-05f, -1.862701081e-04f, +3.766412744e-04f, +5.726346524e-04f, -9.136368247e-04f, -1.359912061e-03f, +1.368123194e-03f, +2.421279702e-03f, -1.354174959e-03f, -3.263658730e-03f, +7.993627115e-04f, +3.368394423e-03f, -7.301175991e-05f, -2.662213263e-03f, -3.567578182e-04f, +1.586701669e-03f, +3.520154127e-04f, -6.877130848e-04f, -1.399021716e-04f, +1.932641301e-04f, - /* 20,14 */ +3.513221827e-04f, -1.953154138e-04f, +3.290468323e-04f, +6.123036842e-04f, -8.043742580e-04f, -1.437039798e-03f, +1.171859801e-03f, +2.517456780e-03f, -1.074767465e-03f, -3.343371924e-03f, +4.825221542e-04f, +3.406214168e-03f, +2.131645492e-04f, -2.661959816e-03f, -5.610647582e-04f, +1.572899641e-03f, +4.649883812e-04f, -6.809814437e-04f, -1.858917095e-04f, +6.015260759e-04f, - /* 20,15 */ +0.000000000e+00f, -2.009801156e-04f, +2.810018705e-04f, +6.435044748e-04f, -6.923557695e-04f, -1.498255744e-03f, +9.705894653e-04f, +2.589726790e-03f, -7.896927597e-04f, -3.393842146e-03f, +1.613262342e-04f, +3.414789539e-03f, +5.016073607e-04f, -2.637884518e-03f, -7.663264997e-04f, +1.543506114e-03f, +5.788237715e-04f, -6.663349221e-04f, -2.330977005e-04f, +2.034293425e-04f, - /* 20, 0 */ -1.941987182e-05f, -3.146481294e-04f, +5.561305343e-04f, +3.334278991e-04f, -1.561489082e-03f, -4.085266513e-04f, +2.806699025e-03f, +3.580334706e-04f, -3.695826941e-03f, -1.914605051e-04f, +3.720952014e-03f, -2.295171057e-05f, -2.868623587e-03f, +1.886978960e-04f, +1.629573547e-03f, -2.343761763e-04f, -6.035407960e-04f, +1.633790229e-04f, +3.476344875e-05f, +0.000000000e+00f, - /* 20, 1 */ +3.929324583e-04f, -3.051602666e-04f, +5.048306585e-04f, +4.229644027e-04f, -1.479104632e-03f, -6.165003319e-04f, +2.717079427e-03f, +6.839596419e-04f, -3.633185574e-03f, -5.723309145e-04f, +3.708003841e-03f, +3.177599071e-04f, -2.901501717e-03f, -4.082823094e-05f, +1.681921685e-03f, -1.265851889e-04f, -6.461214422e-04f, +1.369921977e-04f, +5.166761157e-05f, +0.000000000e+00f, - /* 20, 2 */ +0.000000000e+00f, -2.925136688e-04f, +4.505823818e-04f, +5.023683161e-04f, -1.383975926e-03f, -8.106644739e-04f, +2.601403151e-03f, +9.973590062e-04f, -3.534000256e-03f, -9.470733637e-04f, +3.656850180e-03f, +6.604608766e-04f, -2.904291326e-03f, -2.777120434e-04f, +1.717239595e-03f, -1.098579054e-05f, -6.829477483e-04f, +1.058859702e-04f, +7.000071077e-05f, +0.000000000e+00f, - /* 20, 3 */ +0.000000000e+00f, -2.771727451e-04f, +3.943146848e-04f, +5.711822345e-04f, -1.277754215e-03f, -9.892860040e-04f, +2.461570058e-03f, +1.295052213e-03f, -3.399644449e-03f, -1.311681723e-03f, +3.567787737e-03f, +1.001437562e-03f, -2.876278542e-03f, -5.194560271e-04f, +1.734397724e-03f, +1.113422841e-04f, -7.131247677e-04f, +7.019384523e-05f, +8.959420873e-05f, +0.000000000e+00f, - /* 20, 4 */ +0.000000000e+00f, -2.596009711e-04f, +3.369316672e-04f, +6.291078651e-04f, -1.162161945e-03f, -1.150868125e-03f, +2.299713337e-03f, +1.574086312e-03f, -3.231873732e-03f, -1.662267592e-03f, +3.441541225e-03f, +1.336946247e-03f, -2.817092852e-03f, -7.634316403e-04f, +1.732451285e-03f, +2.391787684e-04f, -7.358020638e-04f, +3.013243736e-05f, +1.102423612e-04f, +0.000000000e+00f, - /* 20, 5 */ +0.000000000e+00f, -2.402546692e-04f, +2.793008459e-04f, +6.760030262e-04f, -1.038968304e-03f, -1.294161821e-03f, +2.118169008e-03f, +1.831766066e-03f, -3.032802328e-03f, -1.995105343e-03f, +3.279257242e-03f, +1.663257052e-03f, -2.726718246e-03f, -1.006908470e-03f, +1.710658870e-03f, +3.711735089e-04f, -7.501884622e-04f, -1.399708473e-05f, +1.317024534e-04f, +0.000000000e+00f, - /* 20, 6 */ +0.000000000e+00f, -2.195772899e-04f, +2.222425350e-04f, +7.118766228e-04f, -9.099649801e-04f, -1.418173917e-03f, +1.919443545e-03f, +2.065681697e-03f, -2.804875489e-03f, -2.306675131e-03f, +3.082493013e-03f, +1.976698190e-03f, -2.605500127e-03f, -1.247085413e-03f, +1.668498942e-03f, +5.058593370e-04f, -7.555665992e-04f, -6.180883712e-05f, +1.536956226e-04f, +0.000000000e+00f, - /* 20, 7 */ +0.000000000e+00f, -1.979942392e-04f, +1.665204182e-04f, +7.368817426e-04f, -7.769424426e-04f, -1.522171671e-03f, +1.706180058e-03f, +2.273732749e-03f, -2.550838108e-03f, -2.593703365e-03f, +2.853200114e-03f, +2.273699984e-03f, -2.454147846e-03f, -1.481123511e-03f, +1.605683934e-03f, +6.416670612e-04f, -7.513070408e-04f, -1.128334053e-04f, +1.759082929e-04f, +0.000000000e+00f, - /* 20, 8 */ +0.000000000e+00f, -1.759082929e-04f, +1.128334053e-04f, +7.513070408e-04f, -6.416670612e-04f, -1.605683934e-03f, +1.481123511e-03f, +2.454147846e-03f, -2.273699984e-03f, -2.853200114e-03f, +2.593703365e-03f, +2.550838108e-03f, -2.273732749e-03f, -1.706180058e-03f, +1.522171671e-03f, +7.769424426e-04f, -7.368817426e-04f, -1.665204182e-04f, +1.979942392e-04f, +0.000000000e+00f, - /* 20, 9 */ +0.000000000e+00f, -1.536956226e-04f, +6.180883712e-05f, +7.555665992e-04f, -5.058593370e-04f, -1.668498942e-03f, +1.247085413e-03f, +2.605500127e-03f, -1.976698190e-03f, -3.082493013e-03f, +2.306675131e-03f, +2.804875489e-03f, -2.065681697e-03f, -1.919443545e-03f, +1.418173917e-03f, +9.099649801e-04f, -7.118766228e-04f, -2.222425350e-04f, +2.195772899e-04f, +0.000000000e+00f, - /* 20,10 */ +0.000000000e+00f, -1.317024534e-04f, +1.399708473e-05f, +7.501884622e-04f, -3.711735089e-04f, -1.710658870e-03f, +1.006908470e-03f, +2.726718246e-03f, -1.663257052e-03f, -3.279257242e-03f, +1.995105343e-03f, +3.032802328e-03f, -1.831766066e-03f, -2.118169008e-03f, +1.294161821e-03f, +1.038968304e-03f, -6.760030262e-04f, -2.793008459e-04f, +2.402546692e-04f, +0.000000000e+00f, - /* 20,11 */ +0.000000000e+00f, -1.102423612e-04f, -3.013243736e-05f, +7.358020638e-04f, -2.391787684e-04f, -1.732451285e-03f, +7.634316403e-04f, +2.817092852e-03f, -1.336946247e-03f, -3.441541225e-03f, +1.662267592e-03f, +3.231873732e-03f, -1.574086312e-03f, -2.299713337e-03f, +1.150868125e-03f, +1.162161945e-03f, -6.291078651e-04f, -3.369316672e-04f, +2.596009711e-04f, +0.000000000e+00f, - /* 20,12 */ +0.000000000e+00f, -8.959420873e-05f, -7.019384523e-05f, +7.131247677e-04f, -1.113422841e-04f, -1.734397724e-03f, +5.194560271e-04f, +2.876278542e-03f, -1.001437562e-03f, -3.567787737e-03f, +1.311681723e-03f, +3.399644449e-03f, -1.295052213e-03f, -2.461570058e-03f, +9.892860040e-04f, +1.277754215e-03f, -5.711822345e-04f, -3.943146848e-04f, +2.771727451e-04f, +0.000000000e+00f, - /* 20,13 */ +0.000000000e+00f, -7.000071077e-05f, -1.058859702e-04f, +6.829477483e-04f, +1.098579054e-05f, -1.717239595e-03f, +2.777120434e-04f, +2.904291326e-03f, -6.604608766e-04f, -3.656850180e-03f, +9.470733637e-04f, +3.534000256e-03f, -9.973590062e-04f, -2.601403151e-03f, +8.106644739e-04f, +1.383975926e-03f, -5.023683161e-04f, -4.505823818e-04f, +2.925136688e-04f, +0.000000000e+00f, - /* 20,14 */ +0.000000000e+00f, -5.166761157e-05f, -1.369921977e-04f, +6.461214422e-04f, +1.265851889e-04f, -1.681921685e-03f, +4.082823094e-05f, +2.901501717e-03f, -3.177599071e-04f, -3.708003841e-03f, +5.723309145e-04f, +3.633185574e-03f, -6.839596419e-04f, -2.717079427e-03f, +6.165003319e-04f, +1.479104632e-03f, -4.229644027e-04f, -5.048306585e-04f, +3.051602666e-04f, -3.929324583e-04f, - /* 20,15 */ +0.000000000e+00f, -3.476344875e-05f, -1.633790229e-04f, +6.035407960e-04f, +2.343761763e-04f, -1.629573547e-03f, -1.886978960e-04f, +2.868623587e-03f, +2.295171057e-05f, -3.720952014e-03f, +1.914605051e-04f, +3.695826941e-03f, -3.580334706e-04f, -2.806699025e-03f, +4.085266513e-04f, +1.561489082e-03f, -3.334278991e-04f, -5.561305343e-04f, +3.146481294e-04f, +1.941987182e-05f, - /* 16, 0 */ +5.220390682e-05f, +8.171943113e-04f, -8.986497643e-04f, -1.446340428e-03f, +2.494478678e-03f, +1.297377101e-03f, -3.898391184e-03f, -2.241676001e-04f, +3.985236927e-03f, -9.413140896e-04f, -2.682700956e-03f, +1.283836927e-03f, +1.053222343e-03f, -7.989322796e-04f, -1.116939505e-04f, +1.571395541e-04f, - /* 16, 1 */ -3.017522584e-06f, +8.224554322e-04f, -7.403312787e-04f, -1.584058293e-03f, +2.281207335e-03f, +1.631019258e-03f, -3.765802305e-03f, -6.696927435e-04f, +4.024834602e-03f, -5.670028406e-04f, -2.842687093e-03f, +1.097826180e-03f, +1.201600277e-03f, -7.671194843e-04f, -1.747127487e-04f, +1.853334990e-04f, - /* 16, 2 */ -5.335264618e-05f, +8.154372286e-04f, -5.806604605e-04f, -1.696100138e-03f, +2.046328714e-03f, +1.938434809e-03f, -3.589560871e-03f, -1.106825943e-03f, +4.016290169e-03f, -1.789305351e-04f, -2.971556495e-03f, +8.899684644e-04f, +1.341315594e-03f, -7.213960065e-04f, -2.404043435e-04f, +2.137577131e-04f, - /* 16, 3 */ -9.830954357e-05f, +7.970128377e-04f, -4.219420013e-04f, -1.781963746e-03f, +1.793485018e-03f, +2.216231187e-03f, -3.372309802e-03f, -1.530099436e-03f, +3.959337237e-03f, +2.181586899e-04f, -3.066783450e-03f, +6.622938144e-04f, +1.469918710e-03f, -6.616076810e-04f, -3.078052257e-04f, +7.052925564e-04f, - /* 16, 4 */ -1.375232535e-04f, +7.681852053e-04f, -2.663606532e-04f, -1.841529450e-03f, +1.526462906e-03f, +2.461468734e-03f, -3.117203525e-03f, -1.934233820e-03f, +3.854345030e-03f, +6.193258599e-04f, -3.126241364e-03f, +4.171838871e-04f, +1.585017189e-03f, -5.878191605e-04f, -3.758553213e-04f, +2.457392598e-04f, - /* 16, 5 */ -1.707546409e-04f, +7.300642099e-04f, -1.159532388e-04f, -1.875048978e-03f, +1.249136740e-03f, +2.671693350e-03f, -2.827860277e-03f, -2.314209556e-03f, +3.702317390e-03f, +1.019502933e-03f, -3.148242059e-03f, +1.573479538e-04f, +1.684315055e-03f, -5.003238841e-04f, -4.434113338e-04f, +2.470336005e-04f, - /* 16, 6 */ -1.978870754e-04f, +6.838430878e-04f, +2.741593655e-05f, -1.883129095e-03f, +9.654119112e-04f, +2.844961691e-03f, -2.508308289e-03f, -2.665334690e-03f, +3.504882792e-03f, +1.413561295e-03f, -3.131569469e-03f, -1.142067707e-04f, +1.765652028e-03f, -3.996506493e-04f, -5.092623113e-04f, +2.432370997e-04f, - /* 16, 7 */ -2.189210857e-04f, +6.307745862e-04f, +1.620759979e-04f, -1.866710442e-03f, +6.791690772e-04f, +2.979858667e-03f, -2.162926680e-03f, -2.983307830e-03f, +3.264275462e-03f, +1.796381989e-03f, -3.075507089e-03f, -3.942101476e-04f, +1.827042047e-03f, -2.865665382e-04f, -5.721472605e-04f, +2.339671870e-04f, - /* 16, 8 */ -2.339671870e-04f, +5.721472605e-04f, +2.865665382e-04f, -1.827042047e-03f, +3.942101476e-04f, +3.075507089e-03f, -1.796381989e-03f, -3.264275462e-03f, +2.983307830e-03f, +2.162926680e-03f, -2.979858667e-03f, -6.791690772e-04f, +1.866710442e-03f, -1.620759979e-04f, -6.307745862e-04f, +2.189210857e-04f, - /* 16, 9 */ -2.432370997e-04f, +5.092623113e-04f, +3.996506493e-04f, -1.765652028e-03f, +1.142067707e-04f, +3.131569469e-03f, -1.413561295e-03f, -3.504882792e-03f, +2.665334690e-03f, +2.508308289e-03f, -2.844961691e-03f, -9.654119112e-04f, +1.883129095e-03f, -2.741593655e-05f, -6.838430878e-04f, +1.978870754e-04f, - /* 16,10 */ -2.470336005e-04f, +4.434113338e-04f, +5.003238841e-04f, -1.684315055e-03f, -1.573479538e-04f, +3.148242059e-03f, -1.019502933e-03f, -3.702317390e-03f, +2.314209556e-03f, +2.827860277e-03f, -2.671693350e-03f, -1.249136740e-03f, +1.875048978e-03f, +1.159532388e-04f, -7.300642099e-04f, +1.707546409e-04f, - /* 16,11 */ -2.457392598e-04f, +3.758553213e-04f, +5.878191605e-04f, -1.585017189e-03f, -4.171838871e-04f, +3.126241364e-03f, -6.193258599e-04f, -3.854345030e-03f, +1.934233820e-03f, +3.117203525e-03f, -2.461468734e-03f, -1.526462906e-03f, +1.841529450e-03f, +2.663606532e-04f, -7.681852053e-04f, +1.375232535e-04f, - /* 16,12 */ -7.052925564e-04f, +3.078052257e-04f, +6.616076810e-04f, -1.469918710e-03f, -6.622938144e-04f, +3.066783450e-03f, -2.181586899e-04f, -3.959337237e-03f, +1.530099436e-03f, +3.372309802e-03f, -2.216231187e-03f, -1.793485018e-03f, +1.781963746e-03f, +4.219420013e-04f, -7.970128377e-04f, +9.830954357e-05f, - /* 16,13 */ -2.137577131e-04f, +2.404043435e-04f, +7.213960065e-04f, -1.341315594e-03f, -8.899684644e-04f, +2.971556495e-03f, +1.789305351e-04f, -4.016290169e-03f, +1.106825943e-03f, +3.589560871e-03f, -1.938434809e-03f, -2.046328714e-03f, +1.696100138e-03f, +5.806604605e-04f, -8.154372286e-04f, +5.335264618e-05f, - /* 16,14 */ -1.853334990e-04f, +1.747127487e-04f, +7.671194843e-04f, -1.201600277e-03f, -1.097826180e-03f, +2.842687093e-03f, +5.670028406e-04f, -4.024834602e-03f, +6.696927435e-04f, +3.765802305e-03f, -1.631019258e-03f, -2.281207335e-03f, +1.584058293e-03f, +7.403312787e-04f, -8.224554322e-04f, +3.017522584e-06f, - /* 16,15 */ -1.571395541e-04f, +1.116939505e-04f, +7.989322796e-04f, -1.053222343e-03f, -1.283836927e-03f, +2.682700956e-03f, +9.413140896e-04f, -3.985236927e-03f, +2.241676001e-04f, +3.898391184e-03f, -1.297377101e-03f, -2.494478678e-03f, +1.446340428e-03f, +8.986497643e-04f, -8.171943113e-04f, -5.220390682e-05f, - /* 16, 0 */ -2.607744081e-04f, +6.609893225e-04f, +4.777614003e-05f, -2.019079564e-03f, +1.736921610e-03f, +2.230081148e-03f, -4.008512761e-03f, -2.594451583e-04f, +4.172957467e-03f, -1.888269495e-03f, -2.040920379e-03f, +1.975903291e-03f, +1.180452271e-04f, -7.248571600e-04f, +2.468008838e-04f, +0.000000000e+00f, - /* 16, 1 */ -2.676863913e-04f, +5.906930705e-04f, +2.025069901e-04f, -2.030408814e-03f, +1.418499470e-03f, +2.533235894e-03f, -3.790472440e-03f, -7.745724648e-04f, +4.280846994e-03f, -1.512096765e-03f, -2.325335551e-03f, +1.900137256e-03f, +2.927280105e-04f, -7.805529904e-04f, +2.254162899e-04f, +0.000000000e+00f, - /* 16, 2 */ -2.680102581e-04f, +5.157202047e-04f, +3.442245196e-04f, -2.011119828e-03f, +1.090886046e-03f, +2.794113365e-03f, -3.522578151e-03f, -1.278469954e-03f, +4.330057965e-03f, -1.106477171e-03f, -2.585166870e-03f, +1.791556445e-03f, +4.737630093e-04f, -8.263772041e-04f, +1.964117366e-04f, +0.000000000e+00f, - /* 16, 3 */ -7.633646088e-04f, +4.377966605e-04f, +4.713317060e-04f, -1.962888420e-03f, +7.592982470e-04f, +3.009813640e-03f, -3.209287239e-03f, -1.763847458e-03f, +4.319343992e-03f, -6.768749158e-04f, -2.815661672e-03f, +1.650477084e-03f, +6.583936022e-04f, -8.607109932e-04f, +1.597335965e-04f, -4.634120047e-04f, - /* 16, 4 */ -2.423668462e-04f, +3.585907293e-04f, +5.825699836e-04f, -1.887792011e-03f, +4.288533077e-04f, +3.178189562e-03f, -2.855695312e-03f, -2.223705909e-03f, +4.248362401e-03f, -2.292254995e-04f, -3.012400483e-03f, +1.477771292e-03f, +8.436545409e-04f, -8.820535056e-04f, +1.154955663e-04f, +2.338334874e-05f, - /* 16, 5 */ -2.066858426e-04f, +2.796840991e-04f, +6.770251471e-04f, -1.788258811e-03f, +1.044882353e-04f, +3.297866045e-03f, -2.467449129e-03f, -2.651446905e-03f, +4.117686315e-03f, +2.301520823e-04f, -3.171379014e-03f, +1.274872332e-03f, +1.026416356e-03f, -8.890585891e-04f, +6.398775754e-05f, +4.782938304e-05f, - /* 16, 6 */ -1.715009195e-04f, +2.025461567e-04f, +7.541266524e-04f, -1.667012713e-03f, -2.091154912e-04f, +3.368246274e-03f, -2.050651409e-03f, -3.040975547e-03f, +3.928801804e-03f, +6.946508648e-04f, -3.289085050e-03f, +1.043770075e-03f, +1.203434747e-03f, -8.805703554e-04f, +5.682450650e-06f, +7.520965874e-05f, - /* 16, 7 */ -1.374865801e-04f, +1.285119093e-04f, +8.136405975e-04f, -1.527015027e-03f, -5.076007987e-04f, +3.389504923e-03f, -1.611759203e-03f, -3.386794836e-03f, +3.684090065e-03f, +1.157477637e-03f, -3.362568736e-03f, +7.869964389e-04f, +1.371404208e-03f, -8.556567843e-04f, -5.876379361e-05f, +1.052264476e-04f, - /* 16, 8 */ -1.052264476e-04f, +5.876379361e-05f, +8.556567843e-04f, -1.371404208e-03f, -7.869964389e-04f, +3.362568736e-03f, -1.157477637e-03f, -3.684090065e-03f, +3.386794836e-03f, +1.611759203e-03f, -3.389504923e-03f, +5.076007987e-04f, +1.527015027e-03f, -8.136405975e-04f, -1.285119093e-04f, +1.374865801e-04f, - /* 16, 9 */ -7.520965874e-05f, -5.682450650e-06f, +8.805703554e-04f, -1.203434747e-03f, -1.043770075e-03f, +3.289085050e-03f, -6.946508648e-04f, -3.928801804e-03f, +3.040975547e-03f, +2.050651409e-03f, -3.368246274e-03f, +2.091154912e-04f, +1.667012713e-03f, -7.541266524e-04f, -2.025461567e-04f, +1.715009195e-04f, - /* 16,10 */ -4.782938304e-05f, -6.398775754e-05f, +8.890585891e-04f, -1.026416356e-03f, -1.274872332e-03f, +3.171379014e-03f, -2.301520823e-04f, -4.117686315e-03f, +2.651446905e-03f, +2.467449129e-03f, -3.297866045e-03f, -1.044882353e-04f, +1.788258811e-03f, -6.770251471e-04f, -2.796840991e-04f, +2.066858426e-04f, - /* 16,11 */ -2.338334874e-05f, -1.154955663e-04f, +8.820535056e-04f, -8.436545409e-04f, -1.477771292e-03f, +3.012400483e-03f, +2.292254995e-04f, -4.248362401e-03f, +2.223705909e-03f, +2.855695312e-03f, -3.178189562e-03f, -4.288533077e-04f, +1.887792011e-03f, -5.825699836e-04f, -3.585907293e-04f, +2.423668462e-04f, - /* 16,12 */ +4.634120047e-04f, -1.597335965e-04f, +8.607109932e-04f, -6.583936022e-04f, -1.650477084e-03f, +2.815661672e-03f, +6.768749158e-04f, -4.319343992e-03f, +1.763847458e-03f, +3.209287239e-03f, -3.009813640e-03f, -7.592982470e-04f, +1.962888420e-03f, -4.713317060e-04f, -4.377966605e-04f, +7.633646088e-04f, - /* 16,13 */ +0.000000000e+00f, -1.964117366e-04f, +8.263772041e-04f, -4.737630093e-04f, -1.791556445e-03f, +2.585166870e-03f, +1.106477171e-03f, -4.330057965e-03f, +1.278469954e-03f, +3.522578151e-03f, -2.794113365e-03f, -1.090886046e-03f, +2.011119828e-03f, -3.442245196e-04f, -5.157202047e-04f, +2.680102581e-04f, - /* 16,14 */ +0.000000000e+00f, -2.254162899e-04f, +7.805529904e-04f, -2.927280105e-04f, -1.900137256e-03f, +2.325335551e-03f, +1.512096765e-03f, -4.280846994e-03f, +7.745724648e-04f, +3.790472440e-03f, -2.533235894e-03f, -1.418499470e-03f, +2.030408814e-03f, -2.025069901e-04f, -5.906930705e-04f, +2.676863913e-04f, - /* 16,15 */ +0.000000000e+00f, -2.468008838e-04f, +7.248571600e-04f, -1.180452271e-04f, -1.975903291e-03f, +2.040920379e-03f, +1.888269495e-03f, -4.172957467e-03f, +2.594451583e-04f, +4.008512761e-03f, -2.230081148e-03f, -1.736921610e-03f, +2.019079564e-03f, -4.777614003e-05f, -6.609893225e-04f, +2.607744081e-04f, - /* 16, 0 */ -1.129954761e-04f, -3.969443331e-04f, +7.863457700e-04f, -1.968627401e-03f, +6.635626436e-04f, +3.065104554e-03f, -4.014723865e-03f, -2.972906332e-04f, +4.272130602e-03f, -2.778972616e-03f, -1.044103847e-03f, +2.069667087e-03f, -6.906315332e-04f, -2.376896670e-04f, +1.523656204e-04f, +0.000000000e+00f, - /* 16, 1 */ -7.672972562e-05f, -4.458313625e-04f, +8.604609066e-04f, -1.839340292e-03f, +2.869810557e-04f, +3.294943885e-03f, -3.696990655e-03f, -8.869321804e-04f, +4.464175630e-03f, -2.440111513e-03f, -1.421957113e-03f, +2.139058837e-03f, -5.737860098e-04f, -3.273087897e-04f, +1.941703587e-04f, +0.000000000e+00f, - /* 16, 2 */ -4.409159553e-05f, -4.801879450e-04f, +9.129725459e-04f, -1.685578963e-03f, -7.929130936e-05f, +3.465994861e-03f, -3.324955442e-03f, -1.461843594e-03f, +4.586914931e-03f, -2.053108579e-03f, -1.790295465e-03f, +2.173860444e-03f, -4.367566844e-04f, -4.185294039e-04f, +2.375950982e-04f, +0.000000000e+00f, - /* 16, 3 */ +4.855802427e-04f, -5.008892512e-04f, +9.443143993e-04f, -1.511399569e-03f, -4.293120730e-04f, +3.576852207e-03f, -2.905512498e-03f, -2.012499819e-03f, +4.637578076e-03f, -1.623510702e-03f, -2.142238116e-03f, +2.171667537e-03f, -2.809771210e-04f, -5.093533546e-04f, +2.816859426e-04f, +0.000000000e+00f, - /* 16, 4 */ +0.000000000e+00f, -5.090007623e-04f, +9.553248878e-04f, -1.321049384e-03f, -7.576441950e-04f, +3.627202827e-03f, -2.446291464e-03f, -2.529812382e-03f, +4.614629236e-03f, -1.157740361e-03f, -2.470980778e-03f, +2.130685105e-03f, -1.083629217e-04f, -5.976383603e-04f, +3.253590588e-04f, +0.000000000e+00f, - /* 16, 5 */ +0.000000000e+00f, -5.057402285e-04f, +9.472067544e-04f, -1.118874842e-03f, -1.059441268e-03f, +3.617806804e-03f, -1.955510679e-03f, -3.005292216e-03f, +4.517805471e-03f, -6.629933593e-04f, -2.769928158e-03f, +2.049789183e-03f, +7.870314989e-05f, -6.811391839e-04f, +3.674140144e-04f, +0.000000000e+00f, - /* 16, 6 */ +0.000000000e+00f, -4.924395299e-04f, +9.214808972e-04f, -9.092313688e-04f, -1.330518654e-03f, +3.550458465e-03f, -1.441821213e-03f, -3.431200966e-03f, +4.348131386e-03f, -1.471199733e-04f, -3.032825993e-03f, +1.928577081e-03f, +2.773970394e-04f, -7.575537377e-04f, +4.065510896e-04f, +0.000000000e+00f, - /* 16, 7 */ +0.000000000e+00f, -4.705072223e-04f, +8.799357120e-04f, -6.963968181e-04f, -1.567409460e-03f, +3.427928686e-03f, -9.141447359e-04f, -3.800687882e-03f, +4.107909720e-03f, +3.815084058e-04f, -3.253889950e-03f, +1.767404663e-03f, +4.844901765e-04f, -8.245732787e-04f, +4.413924818e-04f, +0.000000000e+00f, - /* 16, 8 */ +0.000000000e+00f, -4.413924818e-04f, +8.245732787e-04f, -4.844901765e-04f, -1.767404663e-03f, +3.253889950e-03f, -3.815084058e-04f, -4.107909720e-03f, +3.800687882e-03f, +9.141447359e-04f, -3.427928686e-03f, +1.567409460e-03f, +6.963968181e-04f, -8.799357120e-04f, +4.705072223e-04f, +0.000000000e+00f, - /* 16, 9 */ +0.000000000e+00f, -4.065510896e-04f, +7.575537377e-04f, -2.773970394e-04f, -1.928577081e-03f, +3.032825993e-03f, +1.471199733e-04f, -4.348131386e-03f, +3.431200966e-03f, +1.441821213e-03f, -3.550458465e-03f, +1.330518654e-03f, +9.092313688e-04f, -9.214808972e-04f, +4.924395299e-04f, +0.000000000e+00f, - /* 16,10 */ +0.000000000e+00f, -3.674140144e-04f, +6.811391839e-04f, -7.870314989e-05f, -2.049789183e-03f, +2.769928158e-03f, +6.629933593e-04f, -4.517805471e-03f, +3.005292216e-03f, +1.955510679e-03f, -3.617806804e-03f, +1.059441268e-03f, +1.118874842e-03f, -9.472067544e-04f, +5.057402285e-04f, +0.000000000e+00f, - /* 16,11 */ +0.000000000e+00f, -3.253590588e-04f, +5.976383603e-04f, +1.083629217e-04f, -2.130685105e-03f, +2.470980778e-03f, +1.157740361e-03f, -4.614629236e-03f, +2.529812382e-03f, +2.446291464e-03f, -3.627202827e-03f, +7.576441950e-04f, +1.321049384e-03f, -9.553248878e-04f, +5.090007623e-04f, +0.000000000e+00f, - /* 16,12 */ +0.000000000e+00f, -2.816859426e-04f, +5.093533546e-04f, +2.809771210e-04f, -2.171667537e-03f, +2.142238116e-03f, +1.623510702e-03f, -4.637578076e-03f, +2.012499819e-03f, +2.905512498e-03f, -3.576852207e-03f, +4.293120730e-04f, +1.511399569e-03f, -9.443143993e-04f, +5.008892512e-04f, -4.855802427e-04f, - /* 16,13 */ +0.000000000e+00f, -2.375950982e-04f, +4.185294039e-04f, +4.367566844e-04f, -2.173860444e-03f, +1.790295465e-03f, +2.053108579e-03f, -4.586914931e-03f, +1.461843594e-03f, +3.324955442e-03f, -3.465994861e-03f, +7.929130936e-05f, +1.685578963e-03f, -9.129725459e-04f, +4.801879450e-04f, +4.409159553e-05f, - /* 16,14 */ +0.000000000e+00f, -1.941703587e-04f, +3.273087897e-04f, +5.737860098e-04f, -2.139058837e-03f, +1.421957113e-03f, +2.440111513e-03f, -4.464175630e-03f, +8.869321804e-04f, +3.696990655e-03f, -3.294943885e-03f, -2.869810557e-04f, +1.839340292e-03f, -8.604609066e-04f, +4.458313625e-04f, +7.672972562e-05f, - /* 16,15 */ +0.000000000e+00f, -1.523656204e-04f, +2.376896670e-04f, +6.906315332e-04f, -2.069667087e-03f, +1.044103847e-03f, +2.778972616e-03f, -4.272130602e-03f, +2.972906332e-04f, +4.014723865e-03f, -3.065104554e-03f, -6.635626436e-04f, +1.968627401e-03f, -7.863457700e-04f, +3.969443331e-04f, +1.129954761e-04f, - /* 12, 0 */ +1.006092301e-03f, -1.356592138e-03f, -5.291224839e-04f, +3.716365432e-03f, -3.908475818e-03f, -3.377012930e-04f, +4.272902045e-03f, -3.529241849e-03f, +1.347121185e-04f, +1.576582805e-03f, -1.021094463e-03f, +2.080147558e-04f, - /* 12, 1 */ +9.703330579e-04f, -1.123568815e-03f, -8.960631472e-04f, +3.830455785e-03f, -3.478978472e-03f, -1.006731283e-03f, +4.564422369e-03f, -3.270752231e-03f, -2.802589631e-04f, +1.777910422e-03f, -1.013271813e-03f, +1.540375404e-04f, - /* 12, 2 */ +9.162319547e-04f, -8.831264337e-04f, -1.229457804e-03f, +3.871345986e-03f, -2.993425932e-03f, -1.656773890e-03f, +4.776559314e-03f, -2.944064628e-03f, -7.081711396e-04f, +1.955059288e-03f, -9.809799530e-04f, +8.895597490e-05f, - /* 12, 3 */ +8.464715149e-04f, -6.407365814e-04f, -1.524162434e-03f, +3.840336583e-03f, -2.461816027e-03f, -2.275602698e-03f, +4.904341682e-03f, -2.553815646e-03f, -1.140836495e-03f, +2.102763165e-03f, -9.230670815e-04f, +1.349777819e-05f, - /* 12, 4 */ +7.639192828e-04f, -4.016125871e-04f, -1.776040886e-03f, +3.740131991e-03f, -1.894910812e-03f, -2.851629129e-03f, +4.944422783e-03f, -2.106045312e-03f, -1.569658753e-03f, +2.216140176e-03f, -8.389345160e-04f, -7.124952580e-05f, - /* 12, 5 */ +6.715456333e-04f, -1.706046467e-04f, -1.982015497e-03f, +3.574748146e-03f, -1.304005398e-03f, -3.374137989e-03f, +4.895165032e-03f, -1.608099956e-03f, -1.985807614e-03f, +2.290824513e-03f, -7.285864297e-04f, -1.638417811e-04f, - /* 12, 6 */ +5.723437636e-04f, +4.789167018e-05f, -2.140092391e-03f, +3.349394124e-03f, -7.006885404e-04f, -3.833503957e-03f, +4.756688774e-03f, -1.068504673e-03f, -2.380403858e-03f, +2.323091638e-03f, -5.926669574e-04f, -2.624916697e-04f, - /* 12, 7 */ +4.692537952e-04f, +2.500119139e-04f, -2.249361715e-03f, +3.070330944e-03f, -9.660032268e-05f, -4.221384345e-03f, +4.530883988e-03f, -4.968076992e-04f, -2.744711242e-03f, +2.309973659e-03f, -4.324830696e-04f, -3.650927179e-04f, - /* 12, 8 */ +3.650927179e-04f, +4.324830696e-04f, -2.309973659e-03f, +2.744711242e-03f, +4.968076992e-04f, -4.530883988e-03f, +4.221384345e-03f, +9.660032268e-05f, -3.070330944e-03f, +2.249361715e-03f, -2.500119139e-04f, -4.692537952e-04f, - /* 12, 9 */ +2.624916697e-04f, +5.926669574e-04f, -2.323091638e-03f, +2.380403858e-03f, +1.068504673e-03f, -4.756688774e-03f, +3.833503957e-03f, +7.006885404e-04f, -3.349394124e-03f, +2.140092391e-03f, -4.789167018e-05f, -5.723437636e-04f, - /* 12,10 */ +1.638417811e-04f, +7.285864297e-04f, -2.290824513e-03f, +1.985807614e-03f, +1.608099956e-03f, -4.895165032e-03f, +3.374137989e-03f, +1.304005398e-03f, -3.574748146e-03f, +1.982015497e-03f, +1.706046467e-04f, -6.715456333e-04f, - /* 12,11 */ +7.124952580e-05f, +8.389345160e-04f, -2.216140176e-03f, +1.569658753e-03f, +2.106045312e-03f, -4.944422783e-03f, +2.851629129e-03f, +1.894910812e-03f, -3.740131991e-03f, +1.776040886e-03f, +4.016125871e-04f, -7.639192828e-04f, - /* 12,12 */ -1.349777819e-05f, +9.230670815e-04f, -2.102763165e-03f, +1.140836495e-03f, +2.553815646e-03f, -4.904341682e-03f, +2.275602698e-03f, +2.461816027e-03f, -3.840336583e-03f, +1.524162434e-03f, +6.407365814e-04f, -8.464715149e-04f, - /* 12,13 */ -8.895597490e-05f, +9.809799530e-04f, -1.955059288e-03f, +7.081711396e-04f, +2.944064628e-03f, -4.776559314e-03f, +1.656773890e-03f, +2.993425932e-03f, -3.871345986e-03f, +1.229457804e-03f, +8.831264337e-04f, -9.162319547e-04f, - /* 12,14 */ -1.540375404e-04f, +1.013271813e-03f, -1.777910422e-03f, +2.802589631e-04f, +3.270752231e-03f, -4.564422369e-03f, +1.006731283e-03f, +3.478978472e-03f, -3.830455785e-03f, +8.960631472e-04f, +1.123568815e-03f, -9.703330579e-04f, - /* 12,15 */ -2.080147558e-04f, +1.021094463e-03f, -1.576582805e-03f, -1.347121185e-04f, +3.529241849e-03f, -4.272902045e-03f, +3.377012930e-04f, +3.908475818e-03f, -3.716365432e-03f, +5.291224839e-04f, +1.356592138e-03f, -1.006092301e-03f, - /* 12, 0 */ +7.165252154e-04f, -4.291307465e-04f, -1.619691310e-03f, +4.112050532e-03f, -3.684471854e-03f, -3.806742210e-04f, +4.167864669e-03f, -4.064044128e-03f, +1.285631838e-03f, +6.994376016e-04f, -8.205283947e-04f, +3.212754781e-04f, - /* 12, 1 */ +6.042449626e-04f, -1.684748882e-04f, -1.902468489e-03f, +4.073990388e-03f, -3.134198780e-03f, -1.133926469e-03f, +4.572939653e-03f, -3.928365474e-03f, +9.055999228e-04f, +9.731965741e-04f, -9.124383184e-04f, +3.311614162e-04f, - /* 12, 2 */ +4.874350434e-04f, +7.694308689e-05f, -2.130082097e-03f, +3.953363211e-03f, -2.529802622e-03f, -1.863076830e-03f, +4.889863669e-03f, -3.705392569e-03f, +4.862599326e-04f, +1.243729140e-03f, -9.884795125e-04f, +3.301883495e-04f, - /* 12, 3 */ +3.696734183e-04f, +3.022578517e-04f, -2.300114973e-03f, +3.755428771e-03f, -1.885047396e-03f, -2.552675098e-03f, +5.110657479e-03f, -3.397530000e-03f, +3.551878686e-05f, +1.504029611e-03f, -1.045032679e-03f, +3.171093301e-04f, - /* 12, 4 */ +2.542791637e-04f, +5.034105172e-04f, -2.411611526e-03f, +3.487034212e-03f, -1.214371318e-03f, -3.188181667e-03f, +5.229403271e-03f, -3.009202669e-03f, -4.376222644e-04f, +1.746934410e-03f, -1.078752986e-03f, +2.909691299e-04f, - /* 12, 5 */ +1.442362351e-04f, +6.772076791e-04f, -2.465038541e-03f, +3.156407157e-03f, -5.325427440e-04f, -3.756300237e-03f, +5.242404627e-03f, -2.546799451e-03f, -9.232494237e-04f, +1.965304174e-03f, -1.086687765e-03f, +2.511615343e-04f, - /* 12, 6 */ +4.213190220e-05f, +8.213542577e-04f, -2.462211671e-03f, +2.772920825e-03f, +1.456866600e-04f, -4.245279979e-03f, +5.148294544e-03f, -2.018567160e-03f, -1.410748009e-03f, +2.152214196e-03f, -1.066390037e-03f, +1.974793328e-04f, - /* 12, 7 */ -4.988918599e-05f, +9.344605010e-04f, -2.406190818e-03f, +2.346837790e-03f, +8.059229054e-04f, -4.645179801e-03f, +4.948088353e-03f, -1.434456490e-03f, -1.889039390e-03f, +2.301148282e-03f, -1.016024228e-03f, +1.301548676e-04f, - /* 12, 8 */ -1.301548676e-04f, +1.016024228e-03f, -2.301148282e-03f, +1.889039390e-03f, +1.434456490e-03f, -4.948088353e-03f, +4.645179801e-03f, -8.059229054e-04f, -2.346837790e-03f, +2.406190818e-03f, -9.344605010e-04f, +4.988918599e-05f, - /* 12, 9 */ -1.974793328e-04f, +1.066390037e-03f, -2.152214196e-03f, +1.410748009e-03f, +2.018567160e-03f, -5.148294544e-03f, +4.245279979e-03f, -1.456866600e-04f, -2.772920825e-03f, +2.462211671e-03f, -8.213542577e-04f, -4.213190220e-05f, - /* 12,10 */ -2.511615343e-04f, +1.086687765e-03f, -1.965304174e-03f, +9.232494237e-04f, +2.546799451e-03f, -5.242404627e-03f, +3.756300237e-03f, +5.325427440e-04f, -3.156407157e-03f, +2.465038541e-03f, -6.772076791e-04f, -1.442362351e-04f, - /* 12,11 */ -2.909691299e-04f, +1.078752986e-03f, -1.746934410e-03f, +4.376222644e-04f, +3.009202669e-03f, -5.229403271e-03f, +3.188181667e-03f, +1.214371318e-03f, -3.487034212e-03f, +2.411611526e-03f, -5.034105172e-04f, -2.542791637e-04f, - /* 12,12 */ -3.171093301e-04f, +1.045032679e-03f, -1.504029611e-03f, -3.551878686e-05f, +3.397530000e-03f, -5.110657479e-03f, +2.552675098e-03f, +1.885047396e-03f, -3.755428771e-03f, +2.300114973e-03f, -3.022578517e-04f, -3.696734183e-04f, - /* 12,13 */ -3.301883495e-04f, +9.884795125e-04f, -1.243729140e-03f, -4.862599326e-04f, +3.705392569e-03f, -4.889863669e-03f, +1.863076830e-03f, +2.529802622e-03f, -3.953363211e-03f, +2.130082097e-03f, -7.694308689e-05f, -4.874350434e-04f, - /* 12,14 */ -3.311614162e-04f, +9.124383184e-04f, -9.731965741e-04f, -9.055999228e-04f, +3.928365474e-03f, -4.572939653e-03f, +1.133926469e-03f, +3.134198780e-03f, -4.073990388e-03f, +1.902468489e-03f, +1.684748882e-04f, -6.042449626e-04f, - /* 12,15 */ -3.212754781e-04f, +8.205283947e-04f, -6.994376016e-04f, -1.285631838e-03f, +4.064044128e-03f, -4.167864669e-03f, +3.806742210e-04f, +3.684471854e-03f, -4.112050532e-03f, +1.619691310e-03f, +4.291307465e-04f, -7.165252154e-04f -}; diff --git a/Engine/lib/openal-soft/Alc/compat.h b/Engine/lib/openal-soft/Alc/compat.h index 114fc655d..093184c81 100644 --- a/Engine/lib/openal-soft/Alc/compat.h +++ b/Engine/lib/openal-soft/Alc/compat.h @@ -3,6 +3,10 @@ #include "alstring.h" +#ifdef __cplusplus +extern "C" { +#endif + #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN @@ -38,7 +42,7 @@ struct FileMapping { struct FileMapping MapFileToMem(const char *fname); void UnmapFileMem(const struct FileMapping *mapping); -al_string GetProcPath(void); +void GetProcBinary(al_string *path, al_string *fname); #ifdef HAVE_DYNLOAD void *LoadLib(const char *name); @@ -46,4 +50,16 @@ void CloseLib(void *handle); void *GetSymbol(void *handle, const char *name); #endif +#ifdef __ANDROID__ +#define JCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS +#define JCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS + +/** Returns a JNIEnv*. */ +void *Android_GetJNIEnv(void); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + #endif /* AL_COMPAT_H */ diff --git a/Engine/lib/openal-soft/Alc/converter.c b/Engine/lib/openal-soft/Alc/converter.c new file mode 100644 index 000000000..ef2eb9af2 --- /dev/null +++ b/Engine/lib/openal-soft/Alc/converter.c @@ -0,0 +1,468 @@ + +#include "config.h" + +#include "converter.h" + +#include "fpu_modes.h" +#include "mixer/defs.h" + + +SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType dstType, ALsizei numchans, ALsizei srcRate, ALsizei dstRate) +{ + SampleConverter *converter; + ALsizei step; + + if(numchans <= 0 || srcRate <= 0 || dstRate <= 0) + return NULL; + + converter = al_calloc(16, FAM_SIZE(SampleConverter, Chan, numchans)); + converter->mSrcType = srcType; + converter->mDstType = dstType; + converter->mNumChannels = numchans; + 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. */ + START_MIXER_MODE(); + step = (ALsizei)mind(((ALdouble)srcRate/dstRate*FRACTIONONE) + 0.5, + MAX_PITCH * FRACTIONONE); + converter->mIncrement = maxi(step, 1); + if(converter->mIncrement == FRACTIONONE) + converter->mResample = Resample_copy_C; + else + { + /* TODO: Allow other resamplers. */ + BsincPrepare(converter->mIncrement, &converter->mState.bsinc, &bsinc12); + converter->mResample = SelectResampler(BSinc12Resampler); + } + END_MIXER_MODE(); + + return converter; +} + +void DestroySampleConverter(SampleConverter **converter) +{ + if(converter) + { + al_free(*converter); + *converter = NULL; + } +} + + +static inline ALfloat Sample_ALbyte(ALbyte val) +{ return val * (1.0f/128.0f); } +static inline ALfloat Sample_ALubyte(ALubyte val) +{ return Sample_ALbyte((ALint)val - 128); } + +static inline ALfloat Sample_ALshort(ALshort val) +{ return val * (1.0f/32768.0f); } +static inline ALfloat Sample_ALushort(ALushort val) +{ return Sample_ALshort((ALint)val - 32768); } + +static inline ALfloat Sample_ALint(ALint val) +{ return (val>>7) * (1.0f/16777216.0f); } +static inline ALfloat Sample_ALuint(ALuint val) +{ return Sample_ALint(val - INT_MAX - 1); } + +static inline ALfloat Sample_ALfloat(ALfloat val) +{ return val; } + +#define DECL_TEMPLATE(T) \ +static inline void Load_##T(ALfloat *restrict dst, const T *restrict src, \ + ALint srcstep, ALsizei samples) \ +{ \ + ALsizei i; \ + for(i = 0;i < samples;i++) \ + dst[i] = Sample_##T(src[i*srcstep]); \ +} + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +DECL_TEMPLATE(ALshort) +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) + +#undef DECL_TEMPLATE + +static void LoadSamples(ALfloat *dst, const ALvoid *src, ALint srcstep, enum DevFmtType srctype, ALsizei samples) +{ + switch(srctype) + { + case DevFmtByte: + Load_ALbyte(dst, src, srcstep, samples); + break; + case DevFmtUByte: + Load_ALubyte(dst, src, srcstep, samples); + break; + case DevFmtShort: + Load_ALshort(dst, src, srcstep, samples); + break; + case DevFmtUShort: + Load_ALushort(dst, src, srcstep, samples); + break; + case DevFmtInt: + Load_ALint(dst, src, srcstep, samples); + break; + case DevFmtUInt: + Load_ALuint(dst, src, srcstep, samples); + break; + case DevFmtFloat: + Load_ALfloat(dst, src, srcstep, samples); + break; + } +} + + +static inline ALbyte ALbyte_Sample(ALfloat val) +{ return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); } +static inline ALubyte ALubyte_Sample(ALfloat val) +{ return ALbyte_Sample(val)+128; } + +static inline ALshort ALshort_Sample(ALfloat val) +{ return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); } +static inline ALushort ALushort_Sample(ALfloat val) +{ return ALshort_Sample(val)+32768; } + +static inline ALint ALint_Sample(ALfloat val) +{ return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f)) << 7; } +static inline ALuint ALuint_Sample(ALfloat val) +{ return ALint_Sample(val)+INT_MAX+1; } + +static inline ALfloat ALfloat_Sample(ALfloat val) +{ return val; } + +#define DECL_TEMPLATE(T) \ +static inline void Store_##T(T *restrict dst, const ALfloat *restrict src, \ + ALint dststep, ALsizei samples) \ +{ \ + ALsizei i; \ + for(i = 0;i < samples;i++) \ + dst[i*dststep] = T##_Sample(src[i]); \ +} + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +DECL_TEMPLATE(ALshort) +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) + +#undef DECL_TEMPLATE + +static void StoreSamples(ALvoid *dst, const ALfloat *src, ALint dststep, enum DevFmtType dsttype, ALsizei samples) +{ + switch(dsttype) + { + case DevFmtByte: + Store_ALbyte(dst, src, dststep, samples); + break; + case DevFmtUByte: + Store_ALubyte(dst, src, dststep, samples); + break; + case DevFmtShort: + Store_ALshort(dst, src, dststep, samples); + break; + case DevFmtUShort: + Store_ALushort(dst, src, dststep, samples); + break; + case DevFmtInt: + Store_ALint(dst, src, dststep, samples); + break; + case DevFmtUInt: + Store_ALuint(dst, src, dststep, samples); + break; + case DevFmtFloat: + Store_ALfloat(dst, src, dststep, samples); + break; + } +} + + +ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframes) +{ + ALint prepcount = converter->mSrcPrepCount; + ALsizei increment = converter->mIncrement; + ALsizei DataPosFrac = converter->mFracOffset; + ALuint64 DataSize64; + + 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; + } + + DataSize64 = prepcount; + DataSize64 += srcframes; + DataSize64 -= MAX_RESAMPLE_PADDING*2; + DataSize64 <<= FRACTIONBITS; + DataSize64 -= DataPosFrac; + + /* If we have a full prep, we can generate at least one sample. */ + return (ALsizei)clampu64((DataSize64 + increment-1)/increment, 1, BUFFERSIZE); +} + + +ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALsizei *srcframes, ALvoid *dst, ALsizei dstframes) +{ + const ALsizei SrcFrameSize = converter->mNumChannels * converter->mSrcTypeSize; + const ALsizei DstFrameSize = converter->mNumChannels * converter->mDstTypeSize; + const ALsizei increment = converter->mIncrement; + ALsizei pos = 0; + + START_MIXER_MODE(); + while(pos < dstframes && *srcframes > 0) + { + ALfloat *restrict SrcData = ASSUME_ALIGNED(converter->mSrcSamples, 16); + ALfloat *restrict DstData = ASSUME_ALIGNED(converter->mDstSamples, 16); + ALint prepcount = converter->mSrcPrepCount; + ALsizei DataPosFrac = converter->mFracOffset; + ALuint64 DataSize64; + ALsizei DstSize; + ALint toread; + ALsizei chan; + + if(prepcount < 0) + { + /* Negative prepcount means we need to skip that many input samples. */ + if(-prepcount >= *srcframes) + { + converter->mSrcPrepCount = prepcount + *srcframes; + *srcframes = 0; + break; + } + *src = (const ALbyte*)*src + SrcFrameSize*-prepcount; + *srcframes += prepcount; + converter->mSrcPrepCount = 0; + continue; + } + toread = mini(*srcframes, 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(chan = 0;chan < converter->mNumChannels;chan++) + LoadSamples(&converter->Chan[chan].mPrevSamples[prepcount], + (const ALbyte*)*src + converter->mSrcTypeSize*chan, + converter->mNumChannels, converter->mSrcType, toread + ); + + converter->mSrcPrepCount = prepcount + toread; + *srcframes = 0; + break; + } + + DataSize64 = 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. */ + DstSize = (ALsizei)clampu64((DataSize64 + increment-1)/increment, 1, BUFFERSIZE); + DstSize = mini(DstSize, dstframes-pos); + + for(chan = 0;chan < converter->mNumChannels;chan++) + { + const ALbyte *SrcSamples = (const ALbyte*)*src + converter->mSrcTypeSize*chan; + ALbyte *DstSamples = (ALbyte*)dst + converter->mDstTypeSize*chan; + const ALfloat *ResampledData; + ALsizei SrcDataEnd; + + /* Load the previous samples into the source data first, then the + * new samples from the input buffer. + */ + memcpy(SrcData, converter->Chan[chan].mPrevSamples, + prepcount*sizeof(ALfloat)); + LoadSamples(SrcData + prepcount, SrcSamples, + converter->mNumChannels, converter->mSrcType, toread + ); + + /* Store as many prep samples for next time as possible, given the + * number of output samples being generated. + */ + SrcDataEnd = (DataPosFrac + increment*DstSize)>>FRACTIONBITS; + if(SrcDataEnd >= prepcount+toread) + memset(converter->Chan[chan].mPrevSamples, 0, + sizeof(converter->Chan[chan].mPrevSamples)); + else + { + size_t len = mini(MAX_RESAMPLE_PADDING*2, prepcount+toread-SrcDataEnd); + memcpy(converter->Chan[chan].mPrevSamples, &SrcData[SrcDataEnd], + len*sizeof(ALfloat)); + memset(converter->Chan[chan].mPrevSamples+len, 0, + sizeof(converter->Chan[chan].mPrevSamples) - len*sizeof(ALfloat)); + } + + /* Now resample, and store the result in the output buffer. */ + ResampledData = converter->mResample(&converter->mState, + SrcData+MAX_RESAMPLE_PADDING, DataPosFrac, increment, + DstData, DstSize + ); + + StoreSamples(DstSamples, ResampledData, converter->mNumChannels, + converter->mDstType, DstSize); + } + + /* Update the number of prep samples still available, as well as the + * fractional offset. + */ + DataPosFrac += increment*DstSize; + converter->mSrcPrepCount = mini(prepcount + toread - (DataPosFrac>>FRACTIONBITS), + MAX_RESAMPLE_PADDING*2); + converter->mFracOffset = DataPosFrac & FRACTIONMASK; + + /* Update the src and dst pointers in case there's still more to do. */ + *src = (const ALbyte*)*src + SrcFrameSize*(DataPosFrac>>FRACTIONBITS); + *srcframes -= mini(*srcframes, (DataPosFrac>>FRACTIONBITS)); + + dst = (ALbyte*)dst + DstFrameSize*DstSize; + pos += DstSize; + } + END_MIXER_MODE(); + + return pos; +} + + +ChannelConverter *CreateChannelConverter(enum DevFmtType srcType, enum DevFmtChannels srcChans, enum DevFmtChannels dstChans) +{ + ChannelConverter *converter; + + if(srcChans != dstChans && !((srcChans == DevFmtMono && dstChans == DevFmtStereo) || + (srcChans == DevFmtStereo && dstChans == DevFmtMono))) + return NULL; + + converter = al_calloc(DEF_ALIGN, sizeof(*converter)); + converter->mSrcType = srcType; + converter->mSrcChans = srcChans; + converter->mDstChans = dstChans; + + return converter; +} + +void DestroyChannelConverter(ChannelConverter **converter) +{ + if(converter) + { + al_free(*converter); + *converter = NULL; + } +} + + +#define DECL_TEMPLATE(T) \ +static void Mono2Stereo##T(ALfloat *restrict dst, const T *src, ALsizei frames)\ +{ \ + ALsizei i; \ + for(i = 0;i < frames;i++) \ + dst[i*2 + 1] = dst[i*2 + 0] = Sample_##T(src[i]) * 0.707106781187f; \ +} \ + \ +static void Stereo2Mono##T(ALfloat *restrict dst, const T *src, ALsizei frames)\ +{ \ + ALsizei i; \ + for(i = 0;i < frames;i++) \ + dst[i] = (Sample_##T(src[i*2 + 0])+Sample_##T(src[i*2 + 1])) * \ + 0.707106781187f; \ +} + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +DECL_TEMPLATE(ALshort) +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) + +#undef DECL_TEMPLATE + +void ChannelConverterInput(ChannelConverter *converter, const ALvoid *src, ALfloat *dst, ALsizei frames) +{ + if(converter->mSrcChans == converter->mDstChans) + { + LoadSamples(dst, src, 1, converter->mSrcType, + frames*ChannelsFromDevFmt(converter->mSrcChans, 0)); + return; + } + + if(converter->mSrcChans == DevFmtStereo && converter->mDstChans == DevFmtMono) + { + switch(converter->mSrcType) + { + case DevFmtByte: + Stereo2MonoALbyte(dst, src, frames); + break; + case DevFmtUByte: + Stereo2MonoALubyte(dst, src, frames); + break; + case DevFmtShort: + Stereo2MonoALshort(dst, src, frames); + break; + case DevFmtUShort: + Stereo2MonoALushort(dst, src, frames); + break; + case DevFmtInt: + Stereo2MonoALint(dst, src, frames); + break; + case DevFmtUInt: + Stereo2MonoALuint(dst, src, frames); + break; + case DevFmtFloat: + Stereo2MonoALfloat(dst, src, frames); + break; + } + } + else /*if(converter->mSrcChans == DevFmtMono && converter->mDstChans == DevFmtStereo)*/ + { + switch(converter->mSrcType) + { + case DevFmtByte: + Mono2StereoALbyte(dst, src, frames); + break; + case DevFmtUByte: + Mono2StereoALubyte(dst, src, frames); + break; + case DevFmtShort: + Mono2StereoALshort(dst, src, frames); + break; + case DevFmtUShort: + Mono2StereoALushort(dst, src, frames); + break; + case DevFmtInt: + Mono2StereoALint(dst, src, frames); + break; + case DevFmtUInt: + Mono2StereoALuint(dst, src, frames); + break; + case DevFmtFloat: + Mono2StereoALfloat(dst, src, frames); + break; + } + } +} diff --git a/Engine/lib/openal-soft/Alc/converter.h b/Engine/lib/openal-soft/Alc/converter.h new file mode 100644 index 000000000..b58fd831d --- /dev/null +++ b/Engine/lib/openal-soft/Alc/converter.h @@ -0,0 +1,55 @@ +#ifndef CONVERTER_H +#define CONVERTER_H + +#include "alMain.h" +#include "alu.h" + +#ifdef __cpluspluc +extern "C" { +#endif + +typedef struct SampleConverter { + enum DevFmtType mSrcType; + enum DevFmtType mDstType; + ALsizei mNumChannels; + 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 { + alignas(16) ALfloat mPrevSamples[MAX_RESAMPLE_PADDING*2]; + } Chan[]; +} SampleConverter; + +SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType dstType, ALsizei numchans, ALsizei srcRate, ALsizei dstRate); +void DestroySampleConverter(SampleConverter **converter); + +ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALsizei *srcframes, ALvoid *dst, ALsizei dstframes); +ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframes); + + +typedef struct ChannelConverter { + enum DevFmtType mSrcType; + enum DevFmtChannels mSrcChans; + enum DevFmtChannels mDstChans; +} ChannelConverter; + +ChannelConverter *CreateChannelConverter(enum DevFmtType srcType, enum DevFmtChannels srcChans, enum DevFmtChannels dstChans); +void DestroyChannelConverter(ChannelConverter **converter); + +void ChannelConverterInput(ChannelConverter *converter, const ALvoid *src, ALfloat *dst, ALsizei frames); + +#ifdef __cpluspluc +} +#endif + +#endif /* CONVERTER_H */ diff --git a/Engine/lib/openal-soft/Alc/cpu_caps.h b/Engine/lib/openal-soft/Alc/cpu_caps.h new file mode 100644 index 000000000..328d470e4 --- /dev/null +++ b/Engine/lib/openal-soft/Alc/cpu_caps.h @@ -0,0 +1,15 @@ +#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/Engine/lib/openal-soft/Alc/effects/chorus.c b/Engine/lib/openal-soft/Alc/effects/chorus.c index 63d0ca018..ffb2b572e 100644 --- a/Engine/lib/openal-soft/Alc/effects/chorus.c +++ b/Engine/lib/openal-soft/Alc/effects/chorus.c @@ -24,32 +24,40 @@ #include #include "alMain.h" -#include "alFilter.h" #include "alAuxEffectSlot.h" #include "alError.h" #include "alu.h" +#include "filters/defs.h" -enum ChorusWaveForm { - CWF_Triangle = AL_CHORUS_WAVEFORM_TRIANGLE, - CWF_Sinusoid = AL_CHORUS_WAVEFORM_SINUSOID +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 WaveForm { + WF_Sinusoid, + WF_Triangle }; typedef struct ALchorusState { DERIVE_FROM_TYPE(ALeffectState); - ALfloat *SampleBuffer[2]; - ALuint BufferLength; - ALuint offset; - ALuint lfo_range; + ALfloat *SampleBuffer; + ALsizei BufferLength; + ALsizei offset; + + ALsizei lfo_offset; + ALsizei lfo_range; ALfloat lfo_scale; ALint lfo_disp; /* Gains for left and right sides */ - ALfloat Gain[2][MAX_OUTPUT_CHANNELS]; + struct { + ALfloat Current[MAX_OUTPUT_CHANNELS]; + ALfloat Target[MAX_OUTPUT_CHANNELS]; + } Gains[2]; /* effect parameters */ - enum ChorusWaveForm waveform; + enum WaveForm waveform; ALint delay; ALfloat depth; ALfloat feedback; @@ -57,8 +65,8 @@ typedef struct ALchorusState { static ALvoid ALchorusState_Destruct(ALchorusState *state); static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device); -static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props); -static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels); +static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props); +static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels); DECLARE_DEFAULT_ALLOCATORS(ALchorusState) DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState); @@ -70,54 +78,51 @@ static void ALchorusState_Construct(ALchorusState *state) SET_VTABLE2(ALchorusState, ALeffectState, state); state->BufferLength = 0; - state->SampleBuffer[0] = NULL; - state->SampleBuffer[1] = NULL; + state->SampleBuffer = NULL; state->offset = 0; + state->lfo_offset = 0; state->lfo_range = 1; - state->waveform = CWF_Triangle; + state->waveform = WF_Triangle; } static ALvoid ALchorusState_Destruct(ALchorusState *state) { - al_free(state->SampleBuffer[0]); - state->SampleBuffer[0] = NULL; - state->SampleBuffer[1] = NULL; + al_free(state->SampleBuffer); + state->SampleBuffer = NULL; ALeffectState_Destruct(STATIC_CAST(ALeffectState,state)); } static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device) { - ALuint maxlen; - ALuint it; + const ALfloat max_delay = maxf(AL_CHORUS_MAX_DELAY, AL_FLANGER_MAX_DELAY); + ALsizei maxlen; - maxlen = fastf2u(AL_CHORUS_MAX_DELAY * 3.0f * Device->Frequency) + 1; - maxlen = NextPowerOf2(maxlen); + maxlen = NextPowerOf2(float2int(max_delay*2.0f*Device->Frequency) + 1u); + if(maxlen <= 0) return AL_FALSE; if(maxlen != state->BufferLength) { - void *temp = al_calloc(16, maxlen * sizeof(ALfloat) * 2); + void *temp = al_calloc(16, maxlen * sizeof(ALfloat)); if(!temp) return AL_FALSE; - al_free(state->SampleBuffer[0]); - state->SampleBuffer[0] = temp; - state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen; + al_free(state->SampleBuffer); + state->SampleBuffer = temp; state->BufferLength = maxlen; } - for(it = 0;it < state->BufferLength;it++) - { - state->SampleBuffer[0][it] = 0.0f; - state->SampleBuffer[1][it] = 0.0f; - } + memset(state->SampleBuffer, 0, state->BufferLength*sizeof(ALfloat)); + memset(state->Gains, 0, sizeof(state->Gains)); return AL_TRUE; } -static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props) +static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props) { - ALfloat frequency = (ALfloat)Device->Frequency; + const ALsizei mindelay = MAX_RESAMPLE_PADDING << FRACTIONBITS; + const ALCdevice *device = Context->Device; + ALfloat frequency = (ALfloat)device->Frequency; ALfloat coeffs[MAX_AMBI_COEFFS]; ALfloat rate; ALint phase; @@ -125,156 +130,166 @@ static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device switch(props->Chorus.Waveform) { case AL_CHORUS_WAVEFORM_TRIANGLE: - state->waveform = CWF_Triangle; + state->waveform = WF_Triangle; break; case AL_CHORUS_WAVEFORM_SINUSOID: - state->waveform = CWF_Sinusoid; + state->waveform = WF_Sinusoid; break; } - state->depth = props->Chorus.Depth; + + /* The LFO depth is scaled to be relative to the sample delay. Clamp the + * delay and depth to allow enough padding for resampling. + */ + state->delay = maxi(float2int(props->Chorus.Delay*frequency*FRACTIONONE + 0.5f), + mindelay); + state->depth = minf(props->Chorus.Depth * state->delay, + (ALfloat)(state->delay - mindelay)); + state->feedback = props->Chorus.Feedback; - state->delay = fastf2i(props->Chorus.Delay * frequency); /* Gains for left and right sides */ - CalcXYZCoeffs(-1.0f, 0.0f, 0.0f, 0.0f, coeffs); - ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[0]); - CalcXYZCoeffs( 1.0f, 0.0f, 0.0f, 0.0f, coeffs); - ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[1]); + CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs); + ComputeDryPanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[0].Target); + CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs); + ComputeDryPanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[1].Target); phase = props->Chorus.Phase; rate = props->Chorus.Rate; if(!(rate > 0.0f)) { - state->lfo_scale = 0.0f; + state->lfo_offset = 0; state->lfo_range = 1; + state->lfo_scale = 0.0f; state->lfo_disp = 0; } else { - /* Calculate LFO coefficient */ - state->lfo_range = fastf2u(frequency/rate + 0.5f); + /* 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, (ALfloat)(INT_MAX/360 - 180))); + + state->lfo_offset = float2int((ALfloat)state->lfo_offset/state->lfo_range* + lfo_range + 0.5f) % lfo_range; + state->lfo_range = lfo_range; switch(state->waveform) { - case CWF_Triangle: + case WF_Triangle: state->lfo_scale = 4.0f / state->lfo_range; break; - case CWF_Sinusoid: + case WF_Sinusoid: state->lfo_scale = F_TAU / state->lfo_range; break; } /* Calculate lfo phase displacement */ - state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f)); + if(phase < 0) phase = 360 + phase; + state->lfo_disp = (state->lfo_range*phase + 180) / 360; } } -static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state) +static void GetTriangleDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range, + const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay, + const ALsizei todo) { - ALfloat lfo_value; - - lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range)); - lfo_value *= state->depth * state->delay; - *delay_left = fastf2i(lfo_value) + state->delay; - - offset += state->lfo_disp; - lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range)); - lfo_value *= state->depth * state->delay; - *delay_right = fastf2i(lfo_value) + state->delay; + ALsizei i; + for(i = 0;i < todo;i++) + { + delays[i] = fastf2i((1.0f - fabsf(2.0f - lfo_scale*offset)) * depth) + delay; + offset = (offset+1)%lfo_range; + } } -static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state) +static void GetSinusoidDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range, + const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay, + const ALsizei todo) { - ALfloat lfo_value; - - lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range)); - lfo_value *= state->depth * state->delay; - *delay_left = fastf2i(lfo_value) + state->delay; - - offset += state->lfo_disp; - lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range)); - lfo_value *= state->depth * state->delay; - *delay_right = fastf2i(lfo_value) + state->delay; + ALsizei i; + for(i = 0;i < todo;i++) + { + delays[i] = fastf2i(sinf(lfo_scale*offset) * depth) + delay; + offset = (offset+1)%lfo_range; + } } -#define DECL_TEMPLATE(Func) \ -static void Process##Func(ALchorusState *state, const ALuint SamplesToDo, \ - const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \ -{ \ - const ALuint bufmask = state->BufferLength-1; \ - ALfloat *restrict leftbuf = state->SampleBuffer[0]; \ - ALfloat *restrict rightbuf = state->SampleBuffer[1]; \ - ALuint offset = state->offset; \ - const ALfloat feedback = state->feedback; \ - ALuint it; \ - \ - for(it = 0;it < SamplesToDo;it++) \ - { \ - ALint delay_left, delay_right; \ - Func(&delay_left, &delay_right, offset, state); \ - \ - out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \ - leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback; \ - \ - out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \ - rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback; \ - \ - offset++; \ - } \ - state->offset = offset; \ -} -DECL_TEMPLATE(Triangle) -DECL_TEMPLATE(Sinusoid) - -#undef DECL_TEMPLATE - -static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) +static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels) { - ALuint it, kt; - ALuint base; + const ALsizei bufmask = state->BufferLength-1; + const ALfloat feedback = state->feedback; + const ALsizei avgdelay = (state->delay + (FRACTIONONE>>1)) >> FRACTIONBITS; + ALfloat *restrict delaybuf = state->SampleBuffer; + ALsizei offset = state->offset; + ALsizei i, c; + ALsizei base; for(base = 0;base < SamplesToDo;) { - ALfloat temps[128][2]; - ALuint td = minu(128, SamplesToDo-base); + const ALsizei todo = mini(256, SamplesToDo-base); + ALint moddelays[2][256]; + alignas(16) ALfloat temps[2][256]; - switch(state->waveform) + if(state->waveform == WF_Sinusoid) { - case CWF_Triangle: - ProcessTriangle(state, td, SamplesIn[0]+base, temps); - break; - case CWF_Sinusoid: - ProcessSinusoid(state, td, SamplesIn[0]+base, temps); - break; + GetSinusoidDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale, + state->depth, state->delay, todo); + GetSinusoidDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range, + state->lfo_range, state->lfo_scale, state->depth, state->delay, + todo); + } + else /*if(state->waveform == WF_Triangle)*/ + { + GetTriangleDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale, + state->depth, state->delay, todo); + GetTriangleDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range, + state->lfo_range, state->lfo_scale, state->depth, state->delay, + todo); + } + state->lfo_offset = (state->lfo_offset+todo) % state->lfo_range; + + for(i = 0;i < todo;i++) + { + ALint delay; + ALfloat mu; + + // Feed the buffer's input first (necessary for delays < 1). + delaybuf[offset&bufmask] = SamplesIn[0][base+i]; + + // Tap for the left output. + delay = offset - (moddelays[0][i]>>FRACTIONBITS); + 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(kt = 0;kt < NumChannels;kt++) - { - ALfloat gain = state->Gain[0][kt]; - if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) - { - for(it = 0;it < td;it++) - SamplesOut[kt][it+base] += temps[it][0] * gain; - } + for(c = 0;c < 2;c++) + MixSamples(temps[c], NumChannels, SamplesOut, state->Gains[c].Current, + state->Gains[c].Target, SamplesToDo-base, base, todo); - gain = state->Gain[1][kt]; - if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) - { - for(it = 0;it < td;it++) - SamplesOut[kt][it+base] += temps[it][1] * gain; - } - } - - base += td; + base += todo; } + + state->offset = offset; } -typedef struct ALchorusStateFactory { - DERIVE_FROM_TYPE(ALeffectStateFactory); -} ALchorusStateFactory; +typedef struct ChorusStateFactory { + DERIVE_FROM_TYPE(EffectStateFactory); +} ChorusStateFactory; -static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(factory)) +static ALeffectState *ChorusStateFactory_create(ChorusStateFactory *UNUSED(factory)) { ALchorusState *state; @@ -284,14 +299,14 @@ static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(f return STATIC_CAST(ALeffectState, state); } -DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALchorusStateFactory); +DEFINE_EFFECTSTATEFACTORY_VTABLE(ChorusStateFactory); -ALeffectStateFactory *ALchorusStateFactory_getFactory(void) +EffectStateFactory *ChorusStateFactory_getFactory(void) { - static ALchorusStateFactory ChorusFactory = { { GET_VTABLE2(ALchorusStateFactory, ALeffectStateFactory) } }; + static ChorusStateFactory ChorusFactory = { { GET_VTABLE2(ChorusStateFactory, EffectStateFactory) } }; - return STATIC_CAST(ALeffectStateFactory, &ChorusFactory); + return STATIC_CAST(EffectStateFactory, &ChorusFactory); } @@ -302,24 +317,22 @@ void ALchorus_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALi { case AL_CHORUS_WAVEFORM: if(!(val >= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus phase out of range"); props->Chorus.Phase = val; break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param); } } void ALchorus_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) -{ - ALchorus_setParami(effect, context, param, vals[0]); -} +{ ALchorus_setParami(effect, context, param, vals[0]); } void ALchorus_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) { ALeffectProps *props = &effect->Props; @@ -327,36 +340,34 @@ void ALchorus_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALf { case AL_CHORUS_RATE: if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus delay out of range"); props->Chorus.Delay = val; break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param); } } void ALchorus_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) -{ - ALchorus_setParamf(effect, context, param, vals[0]); -} +{ ALchorus_setParamf(effect, context, param, vals[0]); } void ALchorus_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val) { @@ -372,13 +383,11 @@ void ALchorus_getParami(const ALeffect *effect, ALCcontext *context, ALenum para break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param); } } void ALchorus_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) -{ - ALchorus_getParami(effect, context, param, vals); -} +{ ALchorus_getParami(effect, context, param, vals); } void ALchorus_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) { const ALeffectProps *props = &effect->Props; @@ -401,12 +410,146 @@ void ALchorus_getParamf(const ALeffect *effect, ALCcontext *context, ALenum para break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param); } } void ALchorus_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) -{ - ALchorus_getParamf(effect, context, param, vals); -} +{ ALchorus_getParamf(effect, context, param, vals); } DEFINE_ALEFFECT_VTABLE(ALchorus); + + +/* 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. + */ +typedef struct FlangerStateFactory { + DERIVE_FROM_TYPE(EffectStateFactory); +} FlangerStateFactory; + +ALeffectState *FlangerStateFactory_create(FlangerStateFactory *UNUSED(factory)) +{ + ALchorusState *state; + + NEW_OBJ0(state, ALchorusState)(); + if(!state) return NULL; + + return STATIC_CAST(ALeffectState, state); +} + +DEFINE_EFFECTSTATEFACTORY_VTABLE(FlangerStateFactory); + +EffectStateFactory *FlangerStateFactory_getFactory(void) +{ + static FlangerStateFactory FlangerFactory = { { GET_VTABLE2(FlangerStateFactory, EffectStateFactory) } }; + + return STATIC_CAST(EffectStateFactory, &FlangerFactory); +} + + +void ALflanger_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_FLANGER_WAVEFORM: + if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM)) + 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 ALflanger_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) +{ ALflanger_setParami(effect, context, param, vals[0]); } +void ALflanger_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) +{ + ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_FLANGER_RATE: + if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE)) + 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 ALflanger_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) +{ ALflanger_setParamf(effect, context, param, vals[0]); } + +void ALflanger_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_FLANGER_WAVEFORM: + *val = props->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 ALflanger_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) +{ ALflanger_getParami(effect, context, param, vals); } +void ALflanger_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_FLANGER_RATE: + *val = props->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 ALflanger_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) +{ ALflanger_getParamf(effect, context, param, vals); } + +DEFINE_ALEFFECT_VTABLE(ALflanger); diff --git a/Engine/lib/openal-soft/Alc/effects/compressor.c b/Engine/lib/openal-soft/Alc/effects/compressor.c index ab0a42167..4ec28f9aa 100644 --- a/Engine/lib/openal-soft/Alc/effects/compressor.c +++ b/Engine/lib/openal-soft/Alc/effects/compressor.c @@ -42,8 +42,8 @@ typedef struct ALcompressorState { static ALvoid ALcompressorState_Destruct(ALcompressorState *state); static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device); -static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props); -static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels); +static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props); +static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels); DECLARE_DEFAULT_ALLOCATORS(ALcompressorState) DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState); @@ -76,8 +76,9 @@ static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdev return AL_TRUE; } -static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props) +static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props) { + const ALCdevice *device = context->Device; ALuint i; state->Enabled = props->Compressor.OnOff; @@ -85,19 +86,19 @@ static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCdevice STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer; STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels; for(i = 0;i < 4;i++) - ComputeFirstOrderGains(device->FOAOut, IdentityMatrixf.m[i], + ComputeFirstOrderGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain, state->Gain[i]); } -static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) +static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels) { - ALuint i, j, k; - ALuint base; + ALsizei i, j, k; + ALsizei base; for(base = 0;base < SamplesToDo;) { ALfloat temps[64][4]; - ALuint td = minu(64, SamplesToDo-base); + ALsizei td = mini(64, SamplesToDo-base); /* Load samples into the temp buffer first. */ for(j = 0;j < 4;j++) @@ -178,11 +179,11 @@ static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint Samples } -typedef struct ALcompressorStateFactory { - DERIVE_FROM_TYPE(ALeffectStateFactory); -} ALcompressorStateFactory; +typedef struct CompressorStateFactory { + DERIVE_FROM_TYPE(EffectStateFactory); +} CompressorStateFactory; -static ALeffectState *ALcompressorStateFactory_create(ALcompressorStateFactory *UNUSED(factory)) +static ALeffectState *CompressorStateFactory_create(CompressorStateFactory *UNUSED(factory)) { ALcompressorState *state; @@ -192,13 +193,13 @@ static ALeffectState *ALcompressorStateFactory_create(ALcompressorStateFactory * return STATIC_CAST(ALeffectState, state); } -DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALcompressorStateFactory); +DEFINE_EFFECTSTATEFACTORY_VTABLE(CompressorStateFactory); -ALeffectStateFactory *ALcompressorStateFactory_getFactory(void) +EffectStateFactory *CompressorStateFactory_getFactory(void) { - static ALcompressorStateFactory CompressorFactory = { { GET_VTABLE2(ALcompressorStateFactory, ALeffectStateFactory) } }; + static CompressorStateFactory CompressorFactory = { { GET_VTABLE2(CompressorStateFactory, EffectStateFactory) } }; - return STATIC_CAST(ALeffectStateFactory, &CompressorFactory); + return STATIC_CAST(EffectStateFactory, &CompressorFactory); } @@ -209,24 +210,21 @@ void ALcompressor_setParami(ALeffect *effect, ALCcontext *context, ALenum param, { case AL_COMPRESSOR_ONOFF: if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "Compressor state out of range"); props->Compressor.OnOff = val; break; - default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + default: + alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x", + param); } } void ALcompressor_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) -{ - ALcompressor_setParami(effect, context, param, vals[0]); -} -void ALcompressor_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -void ALcompressor_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) -{ - ALcompressor_setParamf(effect, context, param, vals[0]); -} +{ ALcompressor_setParami(effect, context, param, vals[0]); } +void ALcompressor_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); } +void ALcompressor_setParamfv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); } void ALcompressor_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val) { @@ -236,19 +234,17 @@ void ALcompressor_getParami(const ALeffect *effect, ALCcontext *context, ALenum case AL_COMPRESSOR_ONOFF: *val = props->Compressor.OnOff; break; + default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x", + param); } } void ALcompressor_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) -{ - ALcompressor_getParami(effect, context, param, vals); -} -void ALcompressor_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -void ALcompressor_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) -{ - ALcompressor_getParamf(effect, context, param, vals); -} +{ ALcompressor_getParami(effect, context, param, vals); } +void ALcompressor_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); } +void ALcompressor_getParamfv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); } DEFINE_ALEFFECT_VTABLE(ALcompressor); diff --git a/Engine/lib/openal-soft/Alc/effects/dedicated.c b/Engine/lib/openal-soft/Alc/effects/dedicated.c index 818cbb6bb..62a3894fe 100644 --- a/Engine/lib/openal-soft/Alc/effects/dedicated.c +++ b/Engine/lib/openal-soft/Alc/effects/dedicated.c @@ -23,22 +23,23 @@ #include #include "alMain.h" -#include "alFilter.h" #include "alAuxEffectSlot.h" #include "alError.h" #include "alu.h" +#include "filters/defs.h" typedef struct ALdedicatedState { DERIVE_FROM_TYPE(ALeffectState); - ALfloat gains[MAX_OUTPUT_CHANNELS]; + ALfloat CurrentGains[MAX_OUTPUT_CHANNELS]; + ALfloat TargetGains[MAX_OUTPUT_CHANNELS]; } ALdedicatedState; static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state); static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *device); -static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCdevice *device, const ALeffectslot *Slot, const ALeffectProps *props); -static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels); +static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props); +static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels); DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState) DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState); @@ -46,13 +47,8 @@ DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState); static void ALdedicatedState_Construct(ALdedicatedState *state) { - ALsizei s; - ALeffectState_Construct(STATIC_CAST(ALeffectState, state)); SET_VTABLE2(ALdedicatedState, ALeffectState, state); - - for(s = 0;s < MAX_OUTPUT_CHANNELS;s++) - state->gains[s] = 0.0f; } static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state) @@ -60,74 +56,69 @@ static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state) ALeffectState_Destruct(STATIC_CAST(ALeffectState,state)); } -static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state), ALCdevice *UNUSED(device)) +static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *UNUSED(device)) { + ALsizei i; + for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) + state->CurrentGains[i] = 0.0f; return AL_TRUE; } -static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCdevice *device, const ALeffectslot *Slot, const ALeffectProps *props) +static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props) { + const ALCdevice *device = context->Device; ALfloat Gain; - ALuint i; + ALsizei i; for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) - state->gains[i] = 0.0f; + state->TargetGains[i] = 0.0f; - Gain = Slot->Params.Gain * props->Dedicated.Gain; - if(Slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT) + Gain = slot->Params.Gain * props->Dedicated.Gain; + if(slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT) { int idx; - if((idx=GetChannelIdxByName(device->RealOut, LFE)) != -1) + if((idx=GetChannelIdxByName(&device->RealOut, LFE)) != -1) { STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer; STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels; - state->gains[idx] = Gain; + state->TargetGains[idx] = Gain; } } - else if(Slot->Params.EffectType == AL_EFFECT_DEDICATED_DIALOGUE) + else if(slot->Params.EffectType == AL_EFFECT_DEDICATED_DIALOGUE) { int idx; /* Dialog goes to the front-center speaker if it exists, otherwise it * plays from the front-center location. */ - if((idx=GetChannelIdxByName(device->RealOut, FrontCenter)) != -1) + if((idx=GetChannelIdxByName(&device->RealOut, FrontCenter)) != -1) { STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer; STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels; - state->gains[idx] = Gain; + state->TargetGains[idx] = Gain; } else { ALfloat coeffs[MAX_AMBI_COEFFS]; - CalcXYZCoeffs(0.0f, 0.0f, -1.0f, 0.0f, coeffs); + CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs); STATIC_CAST(ALeffectState,state)->OutBuffer = device->Dry.Buffer; STATIC_CAST(ALeffectState,state)->OutChannels = device->Dry.NumChannels; - ComputePanningGains(device->Dry, coeffs, Gain, state->gains); + ComputeDryPanGains(&device->Dry, coeffs, Gain, state->TargetGains); } } } -static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) +static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels) { - const ALfloat *gains = state->gains; - ALuint i, c; - - for(c = 0;c < NumChannels;c++) - { - if(!(fabsf(gains[c]) > GAIN_SILENCE_THRESHOLD)) - continue; - - for(i = 0;i < SamplesToDo;i++) - SamplesOut[c][i] += SamplesIn[0][i] * gains[c]; - } + MixSamples(SamplesIn[0], NumChannels, SamplesOut, state->CurrentGains, + state->TargetGains, SamplesToDo, 0, SamplesToDo); } -typedef struct ALdedicatedStateFactory { - DERIVE_FROM_TYPE(ALeffectStateFactory); -} ALdedicatedStateFactory; +typedef struct DedicatedStateFactory { + DERIVE_FROM_TYPE(EffectStateFactory); +} DedicatedStateFactory; -ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(factory)) +ALeffectState *DedicatedStateFactory_create(DedicatedStateFactory *UNUSED(factory)) { ALdedicatedState *state; @@ -137,23 +128,21 @@ ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(fa return STATIC_CAST(ALeffectState, state); } -DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdedicatedStateFactory); +DEFINE_EFFECTSTATEFACTORY_VTABLE(DedicatedStateFactory); -ALeffectStateFactory *ALdedicatedStateFactory_getFactory(void) +EffectStateFactory *DedicatedStateFactory_getFactory(void) { - static ALdedicatedStateFactory DedicatedFactory = { { GET_VTABLE2(ALdedicatedStateFactory, ALeffectStateFactory) } }; + static DedicatedStateFactory DedicatedFactory = { { GET_VTABLE2(DedicatedStateFactory, EffectStateFactory) } }; - return STATIC_CAST(ALeffectStateFactory, &DedicatedFactory); + return STATIC_CAST(EffectStateFactory, &DedicatedFactory); } -void ALdedicated_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -void ALdedicated_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) -{ - ALdedicated_setParami(effect, context, param, vals[0]); -} +void ALdedicated_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param); } +void ALdedicated_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param); } void ALdedicated_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) { ALeffectProps *props = &effect->Props; @@ -161,25 +150,21 @@ void ALdedicated_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, { case AL_DEDICATED_GAIN: if(!(val >= 0.0f && isfinite(val))) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "Dedicated gain out of range"); props->Dedicated.Gain = val; break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param); } } void ALdedicated_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) -{ - ALdedicated_setParamf(effect, context, param, vals[0]); -} +{ ALdedicated_setParamf(effect, context, param, vals[0]); } -void ALdedicated_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -void ALdedicated_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) -{ - ALdedicated_getParami(effect, context, param, vals); -} +void ALdedicated_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param); } +void ALdedicated_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param); } void ALdedicated_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) { const ALeffectProps *props = &effect->Props; @@ -190,12 +175,10 @@ void ALdedicated_getParamf(const ALeffect *effect, ALCcontext *context, ALenum p break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param); } } void ALdedicated_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) -{ - ALdedicated_getParamf(effect, context, param, vals); -} +{ ALdedicated_getParamf(effect, context, param, vals); } DEFINE_ALEFFECT_VTABLE(ALdedicated); diff --git a/Engine/lib/openal-soft/Alc/effects/distortion.c b/Engine/lib/openal-soft/Alc/effects/distortion.c index bc1e7d84f..f4e9969c0 100644 --- a/Engine/lib/openal-soft/Alc/effects/distortion.c +++ b/Engine/lib/openal-soft/Alc/effects/distortion.c @@ -24,10 +24,10 @@ #include #include "alMain.h" -#include "alFilter.h" #include "alAuxEffectSlot.h" #include "alError.h" #include "alu.h" +#include "filters/defs.h" typedef struct ALdistortionState { @@ -37,16 +37,18 @@ typedef struct ALdistortionState { ALfloat Gain[MAX_OUTPUT_CHANNELS]; /* Effect parameters */ - ALfilterState lowpass; - ALfilterState bandpass; + BiquadFilter lowpass; + BiquadFilter bandpass; ALfloat attenuation; ALfloat edge_coeff; + + ALfloat Buffer[2][BUFFERSIZE]; } ALdistortionState; static ALvoid ALdistortionState_Destruct(ALdistortionState *state); static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *device); -static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props); -static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels); +static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props); +static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels); DECLARE_DEFAULT_ALLOCATORS(ALdistortionState) DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState); @@ -56,9 +58,6 @@ static void ALdistortionState_Construct(ALdistortionState *state) { ALeffectState_Construct(STATIC_CAST(ALeffectState, state)); SET_VTABLE2(ALdistortionState, ALeffectState, state); - - ALfilterState_clear(&state->lowpass); - ALfilterState_clear(&state->bandpass); } static ALvoid ALdistortionState_Destruct(ALdistortionState *state) @@ -66,125 +65,121 @@ static ALvoid ALdistortionState_Destruct(ALdistortionState *state) ALeffectState_Destruct(STATIC_CAST(ALeffectState,state)); } -static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state), ALCdevice *UNUSED(device)) +static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *UNUSED(device)) { + BiquadFilter_clear(&state->lowpass); + BiquadFilter_clear(&state->bandpass); return AL_TRUE; } -static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props) +static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props) { - ALfloat frequency = (ALfloat)Device->Frequency; + const ALCdevice *device = context->Device; + ALfloat frequency = (ALfloat)device->Frequency; + ALfloat coeffs[MAX_AMBI_COEFFS]; ALfloat bandwidth; ALfloat cutoff; ALfloat edge; - /* Store distorted signal attenuation settings */ - state->attenuation = props->Distortion.Gain; - - /* Store waveshaper edge settings */ + /* Store waveshaper edge settings. */ edge = sinf(props->Distortion.Edge * (F_PI_2)); edge = minf(edge, 0.99f); state->edge_coeff = 2.0f * edge / (1.0f-edge); - /* Lowpass filter */ cutoff = props->Distortion.LowpassCutoff; - /* Bandwidth value is constant in octaves */ + /* Bandwidth value is constant in octaves. */ bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f); - ALfilterState_setParams(&state->lowpass, ALfilterType_LowPass, 1.0f, + /* Multiply sampling frequency by the amount of oversampling done during + * processing. + */ + BiquadFilter_setParams(&state->lowpass, BiquadType_LowPass, 1.0f, cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth) ); - /* Bandpass filter */ cutoff = props->Distortion.EQCenter; - /* Convert bandwidth in Hz to octaves */ + /* Convert bandwidth in Hz to octaves. */ bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f); - ALfilterState_setParams(&state->bandpass, ALfilterType_BandPass, 1.0f, + BiquadFilter_setParams(&state->bandpass, BiquadType_BandPass, 1.0f, cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth) ); - ComputeAmbientGains(Device->Dry, Slot->Params.Gain, state->Gain); + CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs); + ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain * props->Distortion.Gain, + state->Gain); } -static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) +static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels) { + ALfloat (*restrict buffer)[BUFFERSIZE] = state->Buffer; const ALfloat fc = state->edge_coeff; - ALuint base; - ALuint it; - ALuint ot; - ALuint kt; + ALsizei base; + ALsizei i, k; for(base = 0;base < SamplesToDo;) { - float buffer[2][64 * 4]; - ALuint td = minu(64, SamplesToDo-base); + /* 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); - /* Perform 4x oversampling to avoid aliasing. */ - /* Oversampling greatly improves distortion */ - /* quality and allows to implement lowpass and */ - /* bandpass filters using high frequencies, at */ - /* which classic IIR filters became unstable. */ + /* Fill oversample buffer using zero stuffing. Multiply the sample by + * the amount of oversampling to maintain the signal's power. + */ + for(i = 0;i < todo;i++) + buffer[0][i] = !(i&3) ? SamplesIn[0][(i>>2)+base] * 4.0f : 0.0f; - /* Fill oversample buffer using zero stuffing */ - for(it = 0;it < td;it++) + /* 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. + */ + BiquadFilter_process(&state->lowpass, buffer[1], buffer[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(i = 0;i < todo;i++) { - buffer[0][it*4 + 0] = SamplesIn[0][it+base]; - buffer[0][it*4 + 1] = 0.0f; - buffer[0][it*4 + 2] = 0.0f; - buffer[0][it*4 + 3] = 0.0f; + ALfloat smp = buffer[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)); + + buffer[0][i] = smp; } - /* 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. */ - ALfilterState_process(&state->lowpass, buffer[1], buffer[0], td*4); + /* Third step, do bandpass filtering of distorted signal. */ + BiquadFilter_process(&state->bandpass, buffer[1], buffer[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(it = 0;it < td;it++) - { - for(ot = 0;ot < 4;ot++) - { - /* Restore signal power by multiplying sample by amount of oversampling */ - ALfloat smp = buffer[1][it*4 + ot] * 4.0f; - - 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)); - - buffer[1][it*4 + ot] = smp; - } - } - - /* Third step, do bandpass filtering of distorted signal */ - ALfilterState_process(&state->bandpass, buffer[0], buffer[1], td*4); - - for(kt = 0;kt < NumChannels;kt++) + todo >>= 2; + for(k = 0;k < NumChannels;k++) { /* Fourth step, final, do attenuation and perform decimation, - * store only one sample out of 4. + * storing only one sample out of four. */ - ALfloat gain = state->Gain[kt] * state->attenuation; + ALfloat gain = state->Gain[k]; if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) continue; - for(it = 0;it < td;it++) - SamplesOut[kt][base+it] += gain * buffer[0][it*4]; + for(i = 0;i < todo;i++) + SamplesOut[k][base+i] += gain * buffer[1][i*4]; } - base += td; + base += todo; } } -typedef struct ALdistortionStateFactory { - DERIVE_FROM_TYPE(ALeffectStateFactory); -} ALdistortionStateFactory; +typedef struct DistortionStateFactory { + DERIVE_FROM_TYPE(EffectStateFactory); +} DistortionStateFactory; -static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory *UNUSED(factory)) +static ALeffectState *DistortionStateFactory_create(DistortionStateFactory *UNUSED(factory)) { ALdistortionState *state; @@ -194,23 +189,21 @@ static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory * return STATIC_CAST(ALeffectState, state); } -DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdistortionStateFactory); +DEFINE_EFFECTSTATEFACTORY_VTABLE(DistortionStateFactory); -ALeffectStateFactory *ALdistortionStateFactory_getFactory(void) +EffectStateFactory *DistortionStateFactory_getFactory(void) { - static ALdistortionStateFactory DistortionFactory = { { GET_VTABLE2(ALdistortionStateFactory, ALeffectStateFactory) } }; + static DistortionStateFactory DistortionFactory = { { GET_VTABLE2(DistortionStateFactory, EffectStateFactory) } }; - return STATIC_CAST(ALeffectStateFactory, &DistortionFactory); + return STATIC_CAST(EffectStateFactory, &DistortionFactory); } -void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -void ALdistortion_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) -{ - ALdistortion_setParami(effect, context, param, vals[0]); -} +void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); } +void ALdistortion_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); } void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) { ALeffectProps *props = &effect->Props; @@ -218,49 +211,46 @@ void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, { case AL_DISTORTION_EDGE: if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ bandwidth out of range"); props->Distortion.EQBandwidth = val; break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", + param); } } void ALdistortion_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) -{ - ALdistortion_setParamf(effect, context, param, vals[0]); -} +{ ALdistortion_setParamf(effect, context, param, vals[0]); } -void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -void ALdistortion_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) -{ - ALdistortion_getParami(effect, context, param, vals); -} +void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); } +void ALdistortion_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); } void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) { const ALeffectProps *props = &effect->Props; @@ -287,12 +277,11 @@ void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", + param); } } void ALdistortion_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) -{ - ALdistortion_getParamf(effect, context, param, vals); -} +{ ALdistortion_getParamf(effect, context, param, vals); } DEFINE_ALEFFECT_VTABLE(ALdistortion); diff --git a/Engine/lib/openal-soft/Alc/effects/echo.c b/Engine/lib/openal-soft/Alc/effects/echo.c index 3ad1e9752..676b17e8e 100644 --- a/Engine/lib/openal-soft/Alc/effects/echo.c +++ b/Engine/lib/openal-soft/Alc/effects/echo.c @@ -28,32 +28,37 @@ #include "alAuxEffectSlot.h" #include "alError.h" #include "alu.h" +#include "filters/defs.h" typedef struct ALechoState { DERIVE_FROM_TYPE(ALeffectState); ALfloat *SampleBuffer; - ALuint BufferLength; + ALsizei BufferLength; // The echo is two tap. The delay is the number of samples from before the // current offset struct { - ALuint delay; + ALsizei delay; } Tap[2]; - ALuint Offset; + ALsizei Offset; + /* The panning gains for the two taps */ - ALfloat Gain[2][MAX_OUTPUT_CHANNELS]; + struct { + ALfloat Current[MAX_OUTPUT_CHANNELS]; + ALfloat Target[MAX_OUTPUT_CHANNELS]; + } Gains[2]; ALfloat FeedGain; - ALfilterState Filter; + BiquadFilter Filter; } ALechoState; static ALvoid ALechoState_Destruct(ALechoState *state); static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device); -static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props); -static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels); +static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props); +static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels); DECLARE_DEFAULT_ALLOCATORS(ALechoState) DEFINE_ALEFFECTSTATE_VTABLE(ALechoState); @@ -71,7 +76,7 @@ static void ALechoState_Construct(ALechoState *state) state->Tap[1].delay = 0; state->Offset = 0; - ALfilterState_clear(&state->Filter); + BiquadFilter_clear(&state->Filter); } static ALvoid ALechoState_Destruct(ALechoState *state) @@ -83,13 +88,14 @@ static ALvoid ALechoState_Destruct(ALechoState *state) static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device) { - ALuint maxlen, i; + ALsizei 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 = fastf2u(AL_ECHO_MAX_DELAY * Device->Frequency) + 1; - maxlen += fastf2u(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1; - maxlen = NextPowerOf2(maxlen); + 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 != state->BufferLength) { @@ -100,20 +106,22 @@ static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device) state->SampleBuffer = temp; state->BufferLength = maxlen; } - for(i = 0;i < state->BufferLength;i++) - state->SampleBuffer[i] = 0.0f; + + memset(state->SampleBuffer, 0, state->BufferLength*sizeof(ALfloat)); + memset(state->Gains, 0, sizeof(state->Gains)); return AL_TRUE; } -static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props) +static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props) { - ALuint frequency = Device->Frequency; + const ALCdevice *device = context->Device; + ALuint frequency = device->Frequency; ALfloat coeffs[MAX_AMBI_COEFFS]; - ALfloat gain, lrpan, spread; + ALfloat gainhf, lrpan, spread; - state->Tap[0].delay = fastf2u(props->Echo.Delay * frequency) + 1; - state->Tap[1].delay = fastf2u(props->Echo.LRDelay * frequency); + state->Tap[0].delay = maxi(float2int(props->Echo.Delay*frequency + 0.5f), 1); + state->Tap[1].delay = float2int(props->Echo.LRDelay*frequency + 0.5f); state->Tap[1].delay += state->Tap[0].delay; spread = props->Echo.Spread; @@ -126,94 +134,78 @@ static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, co state->FeedGain = props->Echo.Feedback; - gain = minf(1.0f - props->Echo.Damping, 0.01f); - ALfilterState_setParams(&state->Filter, ALfilterType_HighShelf, - gain, LOWPASSFREQREF/frequency, - calc_rcpQ_from_slope(gain, 0.75f)); - - gain = Slot->Params.Gain; + gainhf = maxf(1.0f - props->Echo.Damping, 0.0625f); /* Limit -24dB */ + BiquadFilter_setParams(&state->Filter, BiquadType_HighShelf, + gainhf, LOWPASSFREQREF/frequency, calc_rcpQ_from_slope(gainhf, 1.0f) + ); /* First tap panning */ - CalcXYZCoeffs(-lrpan, 0.0f, 0.0f, spread, coeffs); - ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[0]); + CalcAngleCoeffs(-F_PI_2*lrpan, 0.0f, spread, coeffs); + ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[0].Target); /* Second tap panning */ - CalcXYZCoeffs( lrpan, 0.0f, 0.0f, spread, coeffs); - ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[1]); + CalcAngleCoeffs( F_PI_2*lrpan, 0.0f, spread, coeffs); + ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[1].Target); } -static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) +static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels) { - const ALuint mask = state->BufferLength-1; - const ALuint tap1 = state->Tap[0].delay; - const ALuint tap2 = state->Tap[1].delay; - ALuint offset = state->Offset; - ALfloat x[2], y[2], in, out; - ALuint base; - ALuint i, k; + const ALsizei mask = state->BufferLength-1; + const ALsizei tap1 = state->Tap[0].delay; + const ALsizei tap2 = state->Tap[1].delay; + ALfloat *restrict delaybuf = state->SampleBuffer; + ALsizei offset = state->Offset; + ALfloat z1, z2, in, out; + ALsizei base; + ALsizei c, i; - x[0] = state->Filter.x[0]; - x[1] = state->Filter.x[1]; - y[0] = state->Filter.y[0]; - y[1] = state->Filter.y[1]; + z1 = state->Filter.z1; + z2 = state->Filter.z2; for(base = 0;base < SamplesToDo;) { - ALfloat temps[128][2]; - ALuint td = minu(128, SamplesToDo-base); + alignas(16) ALfloat temps[2][128]; + ALsizei td = mini(128, SamplesToDo-base); for(i = 0;i < td;i++) { + /* Feed the delay buffer's input first. */ + delaybuf[offset&mask] = SamplesIn[0][i+base]; + /* First tap */ - temps[i][0] = state->SampleBuffer[(offset-tap1) & mask]; + temps[0][i] = delaybuf[(offset-tap1) & mask]; /* Second tap */ - temps[i][1] = state->SampleBuffer[(offset-tap2) & mask]; + temps[1][i] = delaybuf[(offset-tap2) & mask]; - // Apply damping and feedback gain to the second tap, and mix in the - // new sample - in = temps[i][1] + SamplesIn[0][i+base]; - out = in*state->Filter.b0 + - x[0]*state->Filter.b1 + x[1]*state->Filter.b2 - - y[0]*state->Filter.a1 - y[1]*state->Filter.a2; - x[1] = x[0]; x[0] = in; - y[1] = y[0]; y[0] = out; + /* Apply damping to the second tap, then add it to the buffer with + * feedback attenuation. + */ + in = temps[1][i]; + out = in*state->Filter.b0 + z1; + z1 = in*state->Filter.b1 - out*state->Filter.a1 + z2; + z2 = in*state->Filter.b2 - out*state->Filter.a2; - state->SampleBuffer[offset&mask] = out * state->FeedGain; + delaybuf[offset&mask] += out * state->FeedGain; offset++; } - for(k = 0;k < NumChannels;k++) - { - ALfloat gain = state->Gain[0][k]; - if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) - { - for(i = 0;i < td;i++) - SamplesOut[k][i+base] += temps[i][0] * gain; - } - - gain = state->Gain[1][k]; - if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) - { - for(i = 0;i < td;i++) - SamplesOut[k][i+base] += temps[i][1] * gain; - } - } + for(c = 0;c < 2;c++) + MixSamples(temps[c], NumChannels, SamplesOut, state->Gains[c].Current, + state->Gains[c].Target, SamplesToDo-base, base, td); base += td; } - state->Filter.x[0] = x[0]; - state->Filter.x[1] = x[1]; - state->Filter.y[0] = y[0]; - state->Filter.y[1] = y[1]; + state->Filter.z1 = z1; + state->Filter.z2 = z2; state->Offset = offset; } -typedef struct ALechoStateFactory { - DERIVE_FROM_TYPE(ALeffectStateFactory); -} ALechoStateFactory; +typedef struct EchoStateFactory { + DERIVE_FROM_TYPE(EffectStateFactory); +} EchoStateFactory; -ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory)) +ALeffectState *EchoStateFactory_create(EchoStateFactory *UNUSED(factory)) { ALechoState *state; @@ -223,22 +215,20 @@ ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory)) return STATIC_CAST(ALeffectState, state); } -DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALechoStateFactory); +DEFINE_EFFECTSTATEFACTORY_VTABLE(EchoStateFactory); -ALeffectStateFactory *ALechoStateFactory_getFactory(void) +EffectStateFactory *EchoStateFactory_getFactory(void) { - static ALechoStateFactory EchoFactory = { { GET_VTABLE2(ALechoStateFactory, ALeffectStateFactory) } }; + static EchoStateFactory EchoFactory = { { GET_VTABLE2(EchoStateFactory, EffectStateFactory) } }; - return STATIC_CAST(ALeffectStateFactory, &EchoFactory); + return STATIC_CAST(EffectStateFactory, &EchoFactory); } -void ALecho_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -void ALecho_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) -{ - ALecho_setParami(effect, context, param, vals[0]); -} +void ALecho_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param); } +void ALecho_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param); } void ALecho_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) { ALeffectProps *props = &effect->Props; @@ -246,49 +236,45 @@ void ALecho_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALflo { case AL_ECHO_DELAY: if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo spread out of range"); props->Echo.Spread = val; break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param); } } void ALecho_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) -{ - ALecho_setParamf(effect, context, param, vals[0]); -} +{ ALecho_setParamf(effect, context, param, vals[0]); } -void ALecho_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -void ALecho_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) -{ - ALecho_getParami(effect, context, param, vals); -} +void ALecho_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param); } +void ALecho_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param); } void ALecho_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) { const ALeffectProps *props = &effect->Props; @@ -315,12 +301,10 @@ void ALecho_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param); } } void ALecho_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) -{ - ALecho_getParamf(effect, context, param, vals); -} +{ ALecho_getParamf(effect, context, param, vals); } DEFINE_ALEFFECT_VTABLE(ALecho); diff --git a/Engine/lib/openal-soft/Alc/effects/equalizer.c b/Engine/lib/openal-soft/Alc/effects/equalizer.c index 1a63b4181..8ff56fb5d 100644 --- a/Engine/lib/openal-soft/Alc/effects/equalizer.c +++ b/Engine/lib/openal-soft/Alc/effects/equalizer.c @@ -24,10 +24,10 @@ #include #include "alMain.h" -#include "alFilter.h" #include "alAuxEffectSlot.h" #include "alError.h" #include "alu.h" +#include "filters/defs.h" /* The document "Effects Extension Guide.pdf" says that low and high * @@ -72,25 +72,25 @@ * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */ -/* The maximum number of sample frames per update. */ -#define MAX_UPDATE_SAMPLES 256 - typedef struct ALequalizerState { DERIVE_FROM_TYPE(ALeffectState); - /* Effect gains for each channel */ - ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS]; + struct { + /* Effect gains for each channel */ + ALfloat CurrentGains[MAX_OUTPUT_CHANNELS]; + ALfloat TargetGains[MAX_OUTPUT_CHANNELS]; - /* Effect parameters */ - ALfilterState filter[4][MAX_EFFECT_CHANNELS]; + /* Effect parameters */ + BiquadFilter filter[4]; + } Chans[MAX_EFFECT_CHANNELS]; - ALfloat SampleBuffer[4][MAX_EFFECT_CHANNELS][MAX_UPDATE_SAMPLES]; + ALfloat SampleBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE]; } ALequalizerState; static ALvoid ALequalizerState_Destruct(ALequalizerState *state); static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *device); -static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props); -static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels); +static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props); +static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels); DECLARE_DEFAULT_ALLOCATORS(ALequalizerState) DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState); @@ -98,18 +98,8 @@ DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState); static void ALequalizerState_Construct(ALequalizerState *state) { - int it, ft; - ALeffectState_Construct(STATIC_CAST(ALeffectState, state)); SET_VTABLE2(ALequalizerState, ALeffectState, state); - - /* Initialize sample history only on filter creation to avoid */ - /* sound clicks if filter settings were changed in runtime. */ - for(it = 0; it < 4; it++) - { - for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++) - ALfilterState_clear(&state->filter[it][ft]); - } } static ALvoid ALequalizerState_Destruct(ALequalizerState *state) @@ -117,131 +107,100 @@ static ALvoid ALequalizerState_Destruct(ALequalizerState *state) ALeffectState_Destruct(STATIC_CAST(ALeffectState,state)); } -static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state), ALCdevice *UNUSED(device)) +static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *UNUSED(device)) { + ALsizei i, j; + + for(i = 0; i < MAX_EFFECT_CHANNELS;i++) + { + for(j = 0;j < 4;j++) + BiquadFilter_clear(&state->Chans[i].filter[j]); + for(j = 0;j < MAX_OUTPUT_CHANNELS;j++) + state->Chans[i].CurrentGains[j] = 0.0f; + } return AL_TRUE; } -static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props) +static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props) { + const ALCdevice *device = context->Device; ALfloat frequency = (ALfloat)device->Frequency; - ALfloat gain, freq_mult; + ALfloat gain, f0norm; ALuint i; STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer; STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels; for(i = 0;i < MAX_EFFECT_CHANNELS;i++) - ComputeFirstOrderGains(device->FOAOut, IdentityMatrixf.m[i], - slot->Params.Gain, state->Gain[i]); + ComputeFirstOrderGains(&device->FOAOut, IdentityMatrixf.m[i], + slot->Params.Gain, state->Chans[i].TargetGains); /* 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 = sqrtf(props->Equalizer.LowGain); - freq_mult = props->Equalizer.LowCutoff/frequency; - ALfilterState_setParams(&state->filter[0][0], ALfilterType_LowShelf, - gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f) + gain = maxf(sqrtf(props->Equalizer.LowGain), 0.0625f); /* Limit -24dB */ + f0norm = props->Equalizer.LowCutoff/frequency; + BiquadFilter_setParams(&state->Chans[0].filter[0], BiquadType_LowShelf, + gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f) ); + + gain = maxf(props->Equalizer.Mid1Gain, 0.0625f); + f0norm = props->Equalizer.Mid1Center/frequency; + BiquadFilter_setParams(&state->Chans[0].filter[1], BiquadType_Peaking, + gain, f0norm, calc_rcpQ_from_bandwidth( + f0norm, props->Equalizer.Mid1Width + ) + ); + + gain = maxf(props->Equalizer.Mid2Gain, 0.0625f); + f0norm = props->Equalizer.Mid2Center/frequency; + BiquadFilter_setParams(&state->Chans[0].filter[2], BiquadType_Peaking, + gain, f0norm, calc_rcpQ_from_bandwidth( + f0norm, props->Equalizer.Mid2Width + ) + ); + + gain = maxf(sqrtf(props->Equalizer.HighGain), 0.0625f); + f0norm = props->Equalizer.HighCutoff/frequency; + BiquadFilter_setParams(&state->Chans[0].filter[3], BiquadType_HighShelf, + gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f) + ); + /* Copy the filter coefficients for the other input channels. */ for(i = 1;i < MAX_EFFECT_CHANNELS;i++) { - state->filter[0][i].a1 = state->filter[0][0].a1; - state->filter[0][i].a2 = state->filter[0][0].a2; - state->filter[0][i].b0 = state->filter[0][0].b0; - state->filter[0][i].b1 = state->filter[0][0].b1; - state->filter[0][i].b2 = state->filter[0][0].b2; - } - - gain = props->Equalizer.Mid1Gain; - freq_mult = props->Equalizer.Mid1Center/frequency; - ALfilterState_setParams(&state->filter[1][0], ALfilterType_Peaking, - gain, freq_mult, calc_rcpQ_from_bandwidth( - freq_mult, props->Equalizer.Mid1Width - ) - ); - for(i = 1;i < MAX_EFFECT_CHANNELS;i++) - { - state->filter[1][i].a1 = state->filter[1][0].a1; - state->filter[1][i].a2 = state->filter[1][0].a2; - state->filter[1][i].b0 = state->filter[1][0].b0; - state->filter[1][i].b1 = state->filter[1][0].b1; - state->filter[1][i].b2 = state->filter[1][0].b2; - } - - gain = props->Equalizer.Mid2Gain; - freq_mult = props->Equalizer.Mid2Center/frequency; - ALfilterState_setParams(&state->filter[2][0], ALfilterType_Peaking, - gain, freq_mult, calc_rcpQ_from_bandwidth( - freq_mult, props->Equalizer.Mid2Width - ) - ); - for(i = 1;i < MAX_EFFECT_CHANNELS;i++) - { - state->filter[2][i].a1 = state->filter[2][0].a1; - state->filter[2][i].a2 = state->filter[2][0].a2; - state->filter[2][i].b0 = state->filter[2][0].b0; - state->filter[2][i].b1 = state->filter[2][0].b1; - state->filter[2][i].b2 = state->filter[2][0].b2; - } - - gain = sqrtf(props->Equalizer.HighGain); - freq_mult = props->Equalizer.HighCutoff/frequency; - ALfilterState_setParams(&state->filter[3][0], ALfilterType_HighShelf, - gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f) - ); - for(i = 1;i < MAX_EFFECT_CHANNELS;i++) - { - state->filter[3][i].a1 = state->filter[3][0].a1; - state->filter[3][i].a2 = state->filter[3][0].a2; - state->filter[3][i].b0 = state->filter[3][0].b0; - state->filter[3][i].b1 = state->filter[3][0].b1; - state->filter[3][i].b2 = state->filter[3][0].b2; + BiquadFilter_copyParams(&state->Chans[i].filter[0], &state->Chans[0].filter[0]); + BiquadFilter_copyParams(&state->Chans[i].filter[1], &state->Chans[0].filter[1]); + BiquadFilter_copyParams(&state->Chans[i].filter[2], &state->Chans[0].filter[2]); + BiquadFilter_copyParams(&state->Chans[i].filter[3], &state->Chans[0].filter[3]); } } -static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) +static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels) { - ALfloat (*Samples)[MAX_EFFECT_CHANNELS][MAX_UPDATE_SAMPLES] = state->SampleBuffer; - ALuint it, kt, ft; - ALuint base; + ALfloat (*restrict temps)[BUFFERSIZE] = state->SampleBuffer; + ALsizei c; - for(base = 0;base < SamplesToDo;) + for(c = 0;c < MAX_EFFECT_CHANNELS;c++) { - ALuint td = minu(MAX_UPDATE_SAMPLES, SamplesToDo-base); + BiquadFilter_process(&state->Chans[c].filter[0], temps[0], SamplesIn[c], SamplesToDo); + BiquadFilter_process(&state->Chans[c].filter[1], temps[1], temps[0], SamplesToDo); + BiquadFilter_process(&state->Chans[c].filter[2], temps[2], temps[1], SamplesToDo); + BiquadFilter_process(&state->Chans[c].filter[3], temps[3], temps[2], SamplesToDo); - for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++) - ALfilterState_process(&state->filter[0][ft], Samples[0][ft], &SamplesIn[ft][base], td); - for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++) - ALfilterState_process(&state->filter[1][ft], Samples[1][ft], Samples[0][ft], td); - for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++) - ALfilterState_process(&state->filter[2][ft], Samples[2][ft], Samples[1][ft], td); - for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++) - ALfilterState_process(&state->filter[3][ft], Samples[3][ft], Samples[2][ft], td); - - for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++) - { - for(kt = 0;kt < NumChannels;kt++) - { - ALfloat gain = state->Gain[ft][kt]; - if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) - continue; - - for(it = 0;it < td;it++) - SamplesOut[kt][base+it] += gain * Samples[3][ft][it]; - } - } - - base += td; + MixSamples(temps[3], NumChannels, SamplesOut, + state->Chans[c].CurrentGains, state->Chans[c].TargetGains, + SamplesToDo, 0, SamplesToDo + ); } } -typedef struct ALequalizerStateFactory { - DERIVE_FROM_TYPE(ALeffectStateFactory); -} ALequalizerStateFactory; +typedef struct EqualizerStateFactory { + DERIVE_FROM_TYPE(EffectStateFactory); +} EqualizerStateFactory; -ALeffectState *ALequalizerStateFactory_create(ALequalizerStateFactory *UNUSED(factory)) +ALeffectState *EqualizerStateFactory_create(EqualizerStateFactory *UNUSED(factory)) { ALequalizerState *state; @@ -251,22 +210,20 @@ ALeffectState *ALequalizerStateFactory_create(ALequalizerStateFactory *UNUSED(fa return STATIC_CAST(ALeffectState, state); } -DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALequalizerStateFactory); +DEFINE_EFFECTSTATEFACTORY_VTABLE(EqualizerStateFactory); -ALeffectStateFactory *ALequalizerStateFactory_getFactory(void) +EffectStateFactory *EqualizerStateFactory_getFactory(void) { - static ALequalizerStateFactory EqualizerFactory = { { GET_VTABLE2(ALequalizerStateFactory, ALeffectStateFactory) } }; + static EqualizerStateFactory EqualizerFactory = { { GET_VTABLE2(EqualizerStateFactory, EffectStateFactory) } }; - return STATIC_CAST(ALeffectStateFactory, &EqualizerFactory); + return STATIC_CAST(EffectStateFactory, &EqualizerFactory); } -void ALequalizer_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -void ALequalizer_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) -{ - ALequalizer_setParami(effect, context, param, vals[0]); -} +void ALequalizer_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); } +void ALequalizer_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); } void ALequalizer_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) { ALeffectProps *props = &effect->Props; @@ -274,79 +231,75 @@ void ALequalizer_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, { case AL_EQUALIZER_LOW_GAIN: if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer high-band cutoff out of range"); props->Equalizer.HighCutoff = val; break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param); } } void ALequalizer_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) -{ - ALequalizer_setParamf(effect, context, param, vals[0]); -} +{ ALequalizer_setParamf(effect, context, param, vals[0]); } -void ALequalizer_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -void ALequalizer_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) -{ - ALequalizer_getParami(effect, context, param, vals); -} +void ALequalizer_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); } +void ALequalizer_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); } void ALequalizer_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) { const ALeffectProps *props = &effect->Props; @@ -393,12 +346,10 @@ void ALequalizer_getParamf(const ALeffect *effect, ALCcontext *context, ALenum p break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param); } } void ALequalizer_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) -{ - ALequalizer_getParamf(effect, context, param, vals); -} +{ ALequalizer_getParamf(effect, context, param, vals); } DEFINE_ALEFFECT_VTABLE(ALequalizer); diff --git a/Engine/lib/openal-soft/Alc/effects/flanger.c b/Engine/lib/openal-soft/Alc/effects/flanger.c deleted file mode 100644 index 1575212bc..000000000 --- a/Engine/lib/openal-soft/Alc/effects/flanger.c +++ /dev/null @@ -1,411 +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 -#include - -#include "alMain.h" -#include "alFilter.h" -#include "alAuxEffectSlot.h" -#include "alError.h" -#include "alu.h" - - -enum FlangerWaveForm { - FWF_Triangle = AL_FLANGER_WAVEFORM_TRIANGLE, - FWF_Sinusoid = AL_FLANGER_WAVEFORM_SINUSOID -}; - -typedef struct ALflangerState { - DERIVE_FROM_TYPE(ALeffectState); - - ALfloat *SampleBuffer[2]; - ALuint BufferLength; - ALuint offset; - ALuint lfo_range; - ALfloat lfo_scale; - ALint lfo_disp; - - /* Gains for left and right sides */ - ALfloat Gain[2][MAX_OUTPUT_CHANNELS]; - - /* effect parameters */ - enum FlangerWaveForm waveform; - ALint delay; - ALfloat depth; - ALfloat feedback; -} ALflangerState; - -static ALvoid ALflangerState_Destruct(ALflangerState *state); -static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device); -static ALvoid ALflangerState_update(ALflangerState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props); -static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels); -DECLARE_DEFAULT_ALLOCATORS(ALflangerState) - -DEFINE_ALEFFECTSTATE_VTABLE(ALflangerState); - - -static void ALflangerState_Construct(ALflangerState *state) -{ - ALeffectState_Construct(STATIC_CAST(ALeffectState, state)); - SET_VTABLE2(ALflangerState, ALeffectState, state); - - state->BufferLength = 0; - state->SampleBuffer[0] = NULL; - state->SampleBuffer[1] = NULL; - state->offset = 0; - state->lfo_range = 1; - state->waveform = FWF_Triangle; -} - -static ALvoid ALflangerState_Destruct(ALflangerState *state) -{ - al_free(state->SampleBuffer[0]); - state->SampleBuffer[0] = NULL; - state->SampleBuffer[1] = NULL; - - ALeffectState_Destruct(STATIC_CAST(ALeffectState,state)); -} - -static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device) -{ - ALuint maxlen; - ALuint it; - - maxlen = fastf2u(AL_FLANGER_MAX_DELAY * 3.0f * Device->Frequency) + 1; - maxlen = NextPowerOf2(maxlen); - - if(maxlen != state->BufferLength) - { - void *temp = al_calloc(16, maxlen * sizeof(ALfloat) * 2); - if(!temp) return AL_FALSE; - - al_free(state->SampleBuffer[0]); - state->SampleBuffer[0] = temp; - state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen; - - state->BufferLength = maxlen; - } - - for(it = 0;it < state->BufferLength;it++) - { - state->SampleBuffer[0][it] = 0.0f; - state->SampleBuffer[1][it] = 0.0f; - } - - return AL_TRUE; -} - -static ALvoid ALflangerState_update(ALflangerState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props) -{ - ALfloat frequency = (ALfloat)Device->Frequency; - ALfloat coeffs[MAX_AMBI_COEFFS]; - ALfloat rate; - ALint phase; - - switch(props->Flanger.Waveform) - { - case AL_FLANGER_WAVEFORM_TRIANGLE: - state->waveform = FWF_Triangle; - break; - case AL_FLANGER_WAVEFORM_SINUSOID: - state->waveform = FWF_Sinusoid; - break; - } - state->depth = props->Flanger.Depth; - state->feedback = props->Flanger.Feedback; - state->delay = fastf2i(props->Flanger.Delay * frequency); - - /* Gains for left and right sides */ - CalcXYZCoeffs(-1.0f, 0.0f, 0.0f, 0.0f, coeffs); - ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[0]); - CalcXYZCoeffs( 1.0f, 0.0f, 0.0f, 0.0f, coeffs); - ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[1]); - - phase = props->Flanger.Phase; - rate = props->Flanger.Rate; - if(!(rate > 0.0f)) - { - state->lfo_scale = 0.0f; - state->lfo_range = 1; - state->lfo_disp = 0; - } - else - { - /* Calculate LFO coefficient */ - state->lfo_range = fastf2u(frequency/rate + 0.5f); - switch(state->waveform) - { - case FWF_Triangle: - state->lfo_scale = 4.0f / state->lfo_range; - break; - case FWF_Sinusoid: - state->lfo_scale = F_TAU / state->lfo_range; - break; - } - - /* Calculate lfo phase displacement */ - state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f)); - } -} - -static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state) -{ - ALfloat lfo_value; - - lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range)); - lfo_value *= state->depth * state->delay; - *delay_left = fastf2i(lfo_value) + state->delay; - - offset += state->lfo_disp; - lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range)); - lfo_value *= state->depth * state->delay; - *delay_right = fastf2i(lfo_value) + state->delay; -} - -static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state) -{ - ALfloat lfo_value; - - lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range)); - lfo_value *= state->depth * state->delay; - *delay_left = fastf2i(lfo_value) + state->delay; - - offset += state->lfo_disp; - lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range)); - lfo_value *= state->depth * state->delay; - *delay_right = fastf2i(lfo_value) + state->delay; -} - -#define DECL_TEMPLATE(Func) \ -static void Process##Func(ALflangerState *state, const ALuint SamplesToDo, \ - const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \ -{ \ - const ALuint bufmask = state->BufferLength-1; \ - ALfloat *restrict leftbuf = state->SampleBuffer[0]; \ - ALfloat *restrict rightbuf = state->SampleBuffer[1]; \ - ALuint offset = state->offset; \ - const ALfloat feedback = state->feedback; \ - ALuint it; \ - \ - for(it = 0;it < SamplesToDo;it++) \ - { \ - ALint delay_left, delay_right; \ - Func(&delay_left, &delay_right, offset, state); \ - \ - out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \ - leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback; \ - \ - out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \ - rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback; \ - \ - offset++; \ - } \ - state->offset = offset; \ -} - -DECL_TEMPLATE(Triangle) -DECL_TEMPLATE(Sinusoid) - -#undef DECL_TEMPLATE - -static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) -{ - ALuint it, kt; - ALuint base; - - for(base = 0;base < SamplesToDo;) - { - ALfloat temps[128][2]; - ALuint td = minu(128, SamplesToDo-base); - - switch(state->waveform) - { - case FWF_Triangle: - ProcessTriangle(state, td, SamplesIn[0]+base, temps); - break; - case FWF_Sinusoid: - ProcessSinusoid(state, td, SamplesIn[0]+base, temps); - break; - } - - for(kt = 0;kt < NumChannels;kt++) - { - ALfloat gain = state->Gain[0][kt]; - if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) - { - for(it = 0;it < td;it++) - SamplesOut[kt][it+base] += temps[it][0] * gain; - } - - gain = state->Gain[1][kt]; - if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) - { - for(it = 0;it < td;it++) - SamplesOut[kt][it+base] += temps[it][1] * gain; - } - } - - base += td; - } -} - - -typedef struct ALflangerStateFactory { - DERIVE_FROM_TYPE(ALeffectStateFactory); -} ALflangerStateFactory; - -ALeffectState *ALflangerStateFactory_create(ALflangerStateFactory *UNUSED(factory)) -{ - ALflangerState *state; - - NEW_OBJ0(state, ALflangerState)(); - if(!state) return NULL; - - return STATIC_CAST(ALeffectState, state); -} - -DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALflangerStateFactory); - -ALeffectStateFactory *ALflangerStateFactory_getFactory(void) -{ - static ALflangerStateFactory FlangerFactory = { { GET_VTABLE2(ALflangerStateFactory, ALeffectStateFactory) } }; - - return STATIC_CAST(ALeffectStateFactory, &FlangerFactory); -} - - -void ALflanger_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val) -{ - ALeffectProps *props = &effect->Props; - switch(param) - { - case AL_FLANGER_WAVEFORM: - if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); - props->Flanger.Waveform = val; - break; - - case AL_FLANGER_PHASE: - if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); - props->Flanger.Phase = val; - break; - - default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); - } -} -void ALflanger_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) -{ - ALflanger_setParami(effect, context, param, vals[0]); -} -void ALflanger_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) -{ - ALeffectProps *props = &effect->Props; - switch(param) - { - case AL_FLANGER_RATE: - if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); - props->Flanger.Rate = val; - break; - - case AL_FLANGER_DEPTH: - if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); - props->Flanger.Depth = val; - break; - - case AL_FLANGER_FEEDBACK: - if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); - props->Flanger.Feedback = val; - break; - - case AL_FLANGER_DELAY: - if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); - props->Flanger.Delay = val; - break; - - default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); - } -} -void ALflanger_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) -{ - ALflanger_setParamf(effect, context, param, vals[0]); -} - -void ALflanger_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val) -{ - const ALeffectProps *props = &effect->Props; - switch(param) - { - case AL_FLANGER_WAVEFORM: - *val = props->Flanger.Waveform; - break; - - case AL_FLANGER_PHASE: - *val = props->Flanger.Phase; - break; - - default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); - } -} -void ALflanger_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) -{ - ALflanger_getParami(effect, context, param, vals); -} -void ALflanger_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) -{ - const ALeffectProps *props = &effect->Props; - switch(param) - { - case AL_FLANGER_RATE: - *val = props->Flanger.Rate; - break; - - case AL_FLANGER_DEPTH: - *val = props->Flanger.Depth; - break; - - case AL_FLANGER_FEEDBACK: - *val = props->Flanger.Feedback; - break; - - case AL_FLANGER_DELAY: - *val = props->Flanger.Delay; - break; - - default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); - } -} -void ALflanger_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) -{ - ALflanger_getParamf(effect, context, param, vals); -} - -DEFINE_ALEFFECT_VTABLE(ALflanger); diff --git a/Engine/lib/openal-soft/Alc/effects/modulator.c b/Engine/lib/openal-soft/Alc/effects/modulator.c index 247cdf61a..7f1a2cad0 100644 --- a/Engine/lib/openal-soft/Alc/effects/modulator.c +++ b/Engine/lib/openal-soft/Alc/effects/modulator.c @@ -24,29 +24,36 @@ #include #include "alMain.h" -#include "alFilter.h" #include "alAuxEffectSlot.h" #include "alError.h" #include "alu.h" +#include "filters/defs.h" +#define MAX_UPDATE_SAMPLES 128 + typedef struct ALmodulatorState { DERIVE_FROM_TYPE(ALeffectState); - void (*Process)(ALfloat*, const ALfloat*, ALuint, const ALuint, ALuint); + void (*GetSamples)(ALfloat*, ALsizei, const ALsizei, ALsizei); - ALuint index; - ALuint step; + ALsizei index; + ALsizei step; - ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS]; + alignas(16) ALfloat ModSamples[MAX_UPDATE_SAMPLES]; - ALfilterState Filter[MAX_EFFECT_CHANNELS]; + struct { + BiquadFilter Filter; + + ALfloat CurrentGains[MAX_OUTPUT_CHANNELS]; + ALfloat TargetGains[MAX_OUTPUT_CHANNELS]; + } Chans[MAX_EFFECT_CHANNELS]; } ALmodulatorState; static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state); static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *state, ALCdevice *device); -static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props); -static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels); +static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props); +static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels); DECLARE_DEFAULT_ALLOCATORS(ALmodulatorState) DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState); @@ -56,31 +63,31 @@ DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState); #define WAVEFORM_FRACONE (1<> (WAVEFORM_FRACBITS - 1)) & 1); } #define DECL_TEMPLATE(func) \ -static void Modulate##func(ALfloat *restrict dst, const ALfloat *restrict src,\ - ALuint index, const ALuint step, ALuint todo) \ +static void Modulate##func(ALfloat *restrict dst, ALsizei index, \ + const ALsizei step, ALsizei todo) \ { \ - ALuint i; \ + ALsizei i; \ for(i = 0;i < todo;i++) \ { \ index += step; \ index &= WAVEFORM_FRACMASK; \ - dst[i] = src[i] * func(index); \ + dst[i] = func(index); \ } \ } @@ -93,16 +100,11 @@ DECL_TEMPLATE(Square) static void ALmodulatorState_Construct(ALmodulatorState *state) { - ALuint i; - ALeffectState_Construct(STATIC_CAST(ALeffectState, state)); SET_VTABLE2(ALmodulatorState, ALeffectState, state); state->index = 0; state->step = 1; - - for(i = 0;i < MAX_EFFECT_CHANNELS;i++) - ALfilterState_clear(&state->Filter[i]); } static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state) @@ -110,91 +112,89 @@ static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state) ALeffectState_Destruct(STATIC_CAST(ALeffectState,state)); } -static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *UNUSED(state), ALCdevice *UNUSED(device)) +static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *state, ALCdevice *UNUSED(device)) { + ALsizei i, j; + for(i = 0;i < MAX_EFFECT_CHANNELS;i++) + { + BiquadFilter_clear(&state->Chans[i].Filter); + for(j = 0;j < MAX_OUTPUT_CHANNELS;j++) + state->Chans[i].CurrentGains[j] = 0.0f; + } return AL_TRUE; } -static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props) +static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props) { + const ALCdevice *device = context->Device; ALfloat cw, a; - ALuint i; + ALsizei i; if(props->Modulator.Waveform == AL_RING_MODULATOR_SINUSOID) - state->Process = ModulateSin; + state->GetSamples = ModulateSin; else if(props->Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH) - state->Process = ModulateSaw; + state->GetSamples = ModulateSaw; else /*if(Slot->Params.EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)*/ - state->Process = ModulateSquare; + state->GetSamples = ModulateSquare; - state->step = fastf2u(props->Modulator.Frequency*WAVEFORM_FRACONE / - Device->Frequency); - if(state->step == 0) state->step = 1; + state->step = float2int(props->Modulator.Frequency*WAVEFORM_FRACONE/device->Frequency + 0.5f); + state->step = clampi(state->step, 1, WAVEFORM_FRACONE-1); /* Custom filter coeffs, which match the old version instead of a low-shelf. */ - cw = cosf(F_TAU * props->Modulator.HighPassCutoff / Device->Frequency); + cw = cosf(F_TAU * props->Modulator.HighPassCutoff / device->Frequency); a = (2.0f-cw) - sqrtf(powf(2.0f-cw, 2.0f) - 1.0f); - for(i = 0;i < MAX_EFFECT_CHANNELS;i++) - { - state->Filter[i].a1 = -a; - state->Filter[i].a2 = 0.0f; - state->Filter[i].b0 = a; - state->Filter[i].b1 = -a; - state->Filter[i].b2 = 0.0f; - } + state->Chans[0].Filter.b0 = a; + state->Chans[0].Filter.b1 = -a; + state->Chans[0].Filter.b2 = 0.0f; + state->Chans[0].Filter.a1 = -a; + state->Chans[0].Filter.a2 = 0.0f; + for(i = 1;i < MAX_EFFECT_CHANNELS;i++) + BiquadFilter_copyParams(&state->Chans[i].Filter, &state->Chans[0].Filter); - STATIC_CAST(ALeffectState,state)->OutBuffer = Device->FOAOut.Buffer; - STATIC_CAST(ALeffectState,state)->OutChannels = Device->FOAOut.NumChannels; + STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer; + STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels; for(i = 0;i < MAX_EFFECT_CHANNELS;i++) - ComputeFirstOrderGains(Device->FOAOut, IdentityMatrixf.m[i], - Slot->Params.Gain, state->Gain[i]); + ComputeFirstOrderGains(&device->FOAOut, IdentityMatrixf.m[i], + slot->Params.Gain, state->Chans[i].TargetGains); } -static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) +static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels) { - const ALuint step = state->step; - ALuint index = state->index; - ALuint base; + ALfloat *restrict modsamples = ASSUME_ALIGNED(state->ModSamples, 16); + const ALsizei step = state->step; + ALsizei base; for(base = 0;base < SamplesToDo;) { - ALfloat temps[2][128]; - ALuint td = minu(128, SamplesToDo-base); - ALuint i, j, k; + alignas(16) ALfloat temps[2][MAX_UPDATE_SAMPLES]; + ALsizei td = mini(MAX_UPDATE_SAMPLES, SamplesToDo-base); + ALsizei c, i; - for(j = 0;j < MAX_EFFECT_CHANNELS;j++) + state->GetSamples(modsamples, state->index, step, td); + state->index += (step*td) & WAVEFORM_FRACMASK; + state->index &= WAVEFORM_FRACMASK; + + for(c = 0;c < MAX_EFFECT_CHANNELS;c++) { - ALfilterState_process(&state->Filter[j], temps[0], &SamplesIn[j][base], td); - state->Process(temps[1], temps[0], index, step, td); + BiquadFilter_process(&state->Chans[c].Filter, temps[0], &SamplesIn[c][base], td); + for(i = 0;i < td;i++) + temps[1][i] = temps[0][i] * modsamples[i]; - for(k = 0;k < NumChannels;k++) - { - ALfloat gain = state->Gain[j][k]; - if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) - continue; - - for(i = 0;i < td;i++) - SamplesOut[k][base+i] += gain * temps[1][i]; - } + MixSamples(temps[1], NumChannels, SamplesOut, state->Chans[c].CurrentGains, + state->Chans[c].TargetGains, SamplesToDo-base, base, td); } - for(i = 0;i < td;i++) - { - index += step; - index &= WAVEFORM_FRACMASK; - } base += td; } - state->index = index; } -typedef struct ALmodulatorStateFactory { - DERIVE_FROM_TYPE(ALeffectStateFactory); -} ALmodulatorStateFactory; +typedef struct ModulatorStateFactory { + DERIVE_FROM_TYPE(EffectStateFactory); +} ModulatorStateFactory; -static ALeffectState *ALmodulatorStateFactory_create(ALmodulatorStateFactory *UNUSED(factory)) +static ALeffectState *ModulatorStateFactory_create(ModulatorStateFactory *UNUSED(factory)) { ALmodulatorState *state; @@ -204,13 +204,13 @@ static ALeffectState *ALmodulatorStateFactory_create(ALmodulatorStateFactory *UN return STATIC_CAST(ALeffectState, state); } -DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALmodulatorStateFactory); +DEFINE_EFFECTSTATEFACTORY_VTABLE(ModulatorStateFactory); -ALeffectStateFactory *ALmodulatorStateFactory_getFactory(void) +EffectStateFactory *ModulatorStateFactory_getFactory(void) { - static ALmodulatorStateFactory ModulatorFactory = { { GET_VTABLE2(ALmodulatorStateFactory, ALeffectStateFactory) } }; + static ModulatorStateFactory ModulatorFactory = { { GET_VTABLE2(ModulatorStateFactory, EffectStateFactory) } }; - return STATIC_CAST(ALeffectStateFactory, &ModulatorFactory); + return STATIC_CAST(EffectStateFactory, &ModulatorFactory); } @@ -221,24 +221,22 @@ void ALmodulator_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, { case AL_RING_MODULATOR_FREQUENCY: if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "Modulator high-pass cutoff out of range"); props->Modulator.HighPassCutoff = val; break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param); } } void ALmodulator_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) -{ - ALmodulator_setParamf(effect, context, param, vals[0]); -} +{ ALmodulator_setParamf(effect, context, param, vals[0]); } void ALmodulator_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val) { ALeffectProps *props = &effect->Props; @@ -251,18 +249,16 @@ void ALmodulator_setParami(ALeffect *effect, ALCcontext *context, ALenum param, case AL_RING_MODULATOR_WAVEFORM: if(!(val >= AL_RING_MODULATOR_MIN_WAVEFORM && val <= AL_RING_MODULATOR_MAX_WAVEFORM)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid modulator waveform"); props->Modulator.Waveform = val; break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", param); } } void ALmodulator_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) -{ - ALmodulator_setParami(effect, context, param, vals[0]); -} +{ ALmodulator_setParami(effect, context, param, vals[0]); } void ALmodulator_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val) { @@ -280,13 +276,11 @@ void ALmodulator_getParami(const ALeffect *effect, ALCcontext *context, ALenum p break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", param); } } void ALmodulator_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) -{ - ALmodulator_getParami(effect, context, param, vals); -} +{ ALmodulator_getParami(effect, context, param, vals); } void ALmodulator_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) { const ALeffectProps *props = &effect->Props; @@ -300,12 +294,10 @@ void ALmodulator_getParamf(const ALeffect *effect, ALCcontext *context, ALenum p break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param); } } void ALmodulator_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) -{ - ALmodulator_getParamf(effect, context, param, vals); -} +{ ALmodulator_getParamf(effect, context, param, vals); } DEFINE_ALEFFECT_VTABLE(ALmodulator); diff --git a/Engine/lib/openal-soft/Alc/effects/null.c b/Engine/lib/openal-soft/Alc/effects/null.c index bff00b567..e57359e39 100644 --- a/Engine/lib/openal-soft/Alc/effects/null.c +++ b/Engine/lib/openal-soft/Alc/effects/null.c @@ -16,8 +16,8 @@ typedef struct ALnullState { /* Forward-declare "virtual" functions to define the vtable with. */ static ALvoid ALnullState_Destruct(ALnullState *state); static ALboolean ALnullState_deviceUpdate(ALnullState *state, ALCdevice *device); -static ALvoid ALnullState_update(ALnullState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props); -static ALvoid ALnullState_process(ALnullState *state, ALuint samplesToDo, const ALfloatBUFFERSIZE*restrict samplesIn, ALfloatBUFFERSIZE*restrict samplesOut, ALuint NumChannels); +static ALvoid ALnullState_update(ALnullState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props); +static ALvoid ALnullState_process(ALnullState *state, ALsizei samplesToDo, const ALfloat (*restrict samplesIn)[BUFFERSIZE], ALfloat (*restrict samplesOut)[BUFFERSIZE], ALsizei mumChannels); static void *ALnullState_New(size_t size); static void ALnullState_Delete(void *ptr); @@ -56,7 +56,7 @@ static ALboolean ALnullState_deviceUpdate(ALnullState* UNUSED(state), ALCdevice* /* This updates the effect state. This is called any time the effect is * (re)loaded into a slot. */ -static ALvoid ALnullState_update(ALnullState* UNUSED(state), const ALCdevice* UNUSED(device), const ALeffectslot* UNUSED(slot), const ALeffectProps* UNUSED(props)) +static ALvoid ALnullState_update(ALnullState* UNUSED(state), const ALCcontext* UNUSED(context), const ALeffectslot* UNUSED(slot), const ALeffectProps* UNUSED(props)) { } @@ -64,7 +64,7 @@ static ALvoid ALnullState_update(ALnullState* UNUSED(state), const ALCdevice* UN * input to the output buffer. The result should be added to the output buffer, * not replace it. */ -static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALuint UNUSED(samplesToDo), const ALfloatBUFFERSIZE*restrict UNUSED(samplesIn), ALfloatBUFFERSIZE*restrict UNUSED(samplesOut), ALuint UNUSED(NumChannels)) +static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALsizei UNUSED(samplesToDo), const ALfloatBUFFERSIZE*restrict UNUSED(samplesIn), ALfloatBUFFERSIZE*restrict UNUSED(samplesOut), ALsizei UNUSED(numChannels)) { } @@ -85,12 +85,12 @@ static void ALnullState_Delete(void *ptr) } -typedef struct ALnullStateFactory { - DERIVE_FROM_TYPE(ALeffectStateFactory); -} ALnullStateFactory; +typedef struct NullStateFactory { + DERIVE_FROM_TYPE(EffectStateFactory); +} NullStateFactory; /* Creates ALeffectState objects of the appropriate type. */ -ALeffectState *ALnullStateFactory_create(ALnullStateFactory *UNUSED(factory)) +ALeffectState *NullStateFactory_create(NullStateFactory *UNUSED(factory)) { ALnullState *state; @@ -100,79 +100,79 @@ ALeffectState *ALnullStateFactory_create(ALnullStateFactory *UNUSED(factory)) return STATIC_CAST(ALeffectState, state); } -/* Define the ALeffectStateFactory vtable for this type. */ -DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALnullStateFactory); +/* Define the EffectStateFactory vtable for this type. */ +DEFINE_EFFECTSTATEFACTORY_VTABLE(NullStateFactory); -ALeffectStateFactory *ALnullStateFactory_getFactory(void) +EffectStateFactory *NullStateFactory_getFactory(void) { - static ALnullStateFactory NullFactory = { { GET_VTABLE2(ALnullStateFactory, ALeffectStateFactory) } }; - return STATIC_CAST(ALeffectStateFactory, &NullFactory); + static NullStateFactory NullFactory = { { GET_VTABLE2(NullStateFactory, EffectStateFactory) } }; + return STATIC_CAST(EffectStateFactory, &NullFactory); } -void ALnull_setParami(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val)) +void ALnull_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val)) { switch(param) { - default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + default: + alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", param); } } -void ALnull_setParamiv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALint* UNUSED(vals)) +void ALnull_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint* UNUSED(vals)) { switch(param) { - default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + default: + alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer-vector property 0x%04x", param); } } -void ALnull_setParamf(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val)) +void ALnull_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val)) { switch(param) { - default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + default: + alSetError(context, AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", param); } } -void ALnull_setParamfv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat* UNUSED(vals)) +void ALnull_setParamfv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat* UNUSED(vals)) { switch(param) { - default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + default: + alSetError(context, AL_INVALID_ENUM, "Invalid null effect float-vector property 0x%04x", param); } } -void ALnull_getParami(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(val)) +void ALnull_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(val)) { switch(param) { - default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + default: + alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", param); } } -void ALnull_getParamiv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(vals)) +void ALnull_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(vals)) { switch(param) { - default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + default: + alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer-vector property 0x%04x", param); } } -void ALnull_getParamf(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(val)) +void ALnull_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(val)) { switch(param) { - default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + default: + alSetError(context, AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", param); } } -void ALnull_getParamfv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(vals)) +void ALnull_getParamfv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(vals)) { switch(param) { - default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + default: + alSetError(context, AL_INVALID_ENUM, "Invalid null effect float-vector property 0x%04x", param); } } diff --git a/Engine/lib/openal-soft/Alc/effects/pshifter.c b/Engine/lib/openal-soft/Alc/effects/pshifter.c new file mode 100644 index 000000000..618573431 --- /dev/null +++ b/Engine/lib/openal-soft/Alc/effects/pshifter.c @@ -0,0 +1,526 @@ +/** + * 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 +#include + +#include "alMain.h" +#include "alAuxEffectSlot.h" +#include "alError.h" +#include "alu.h" +#include "filters/defs.h" + + +#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)) + +typedef struct ALcomplex { + ALdouble Real; + ALdouble Imag; +} ALcomplex; + +typedef struct ALphasor { + ALdouble Amplitude; + ALdouble Phase; +} ALphasor; + +typedef struct ALFrequencyDomain { + ALdouble Amplitude; + ALdouble Frequency; +} ALfrequencyDomain; + +typedef struct ALpshifterState { + DERIVE_FROM_TYPE(ALeffectState); + + /* Effect parameters */ + ALsizei count; + ALsizei PitchShiftI; + ALfloat PitchShift; + ALfloat FreqPerBin; + + /*Effects buffers*/ + ALfloat InFIFO[STFT_SIZE]; + ALfloat OutFIFO[STFT_STEP]; + ALdouble LastPhase[STFT_HALF_SIZE+1]; + ALdouble SumPhase[STFT_HALF_SIZE+1]; + ALdouble OutputAccum[STFT_SIZE]; + + ALcomplex FFTbuffer[STFT_SIZE]; + + ALfrequencyDomain Analysis_buffer[STFT_HALF_SIZE+1]; + ALfrequencyDomain Syntesis_buffer[STFT_HALF_SIZE+1]; + + alignas(16) ALfloat BufferOut[BUFFERSIZE]; + + /* Effect gains for each output channel */ + ALfloat CurrentGains[MAX_OUTPUT_CHANNELS]; + ALfloat TargetGains[MAX_OUTPUT_CHANNELS]; +} ALpshifterState; + +static ALvoid ALpshifterState_Destruct(ALpshifterState *state); +static ALboolean ALpshifterState_deviceUpdate(ALpshifterState *state, ALCdevice *device); +static ALvoid ALpshifterState_update(ALpshifterState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props); +static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels); +DECLARE_DEFAULT_ALLOCATORS(ALpshifterState) + +DEFINE_ALEFFECTSTATE_VTABLE(ALpshifterState); + + +/* Define a Hann window, used to filter the STFT input and output. */ +alignas(16) static ALdouble HannWindow[STFT_SIZE]; + +static void InitHannWindow(void) +{ + ALsizei i; + + /* Create lookup table of the Hann window for the desired size, i.e. STFT_SIZE */ + for(i = 0;i < STFT_SIZE>>1;i++) + { + ALdouble val = sin(M_PI * (ALdouble)i / (ALdouble)(STFT_SIZE-1)); + HannWindow[i] = HannWindow[STFT_SIZE-1-i] = val * val; + } +} +static alonce_flag HannInitOnce = AL_ONCE_FLAG_INIT; + + +/* Fast double-to-int conversion. Assumes the FPU is already in round-to-zero + * mode. */ +static inline ALint fastd2i(ALdouble d) +{ + /* NOTE: SSE2 is required for the efficient double-to-int opcodes on x86. + * Otherwise, we need to rely on x87's fistp opcode with it already in + * round-to-zero mode. x86-64 guarantees SSE2 support. + */ +#if (defined(__i386__) && !defined(__SSE2_MATH__)) || (defined(_M_IX86_FP) && (_M_IX86_FP < 2)) +#ifdef HAVE_LRINTF + return lrint(d); +#elif defined(_MSC_VER) && defined(_M_IX86) + ALint i; + __asm fld d + __asm fistp i + return i; +#else + return (ALint)d; +#endif +#else + return (ALint)d; +#endif +} + + +/* Converts ALcomplex to ALphasor */ +static inline ALphasor rect2polar(ALcomplex number) +{ + ALphasor polar; + + polar.Amplitude = sqrt(number.Real*number.Real + number.Imag*number.Imag); + polar.Phase = atan2(number.Imag, number.Real); + + return polar; +} + +/* Converts ALphasor to ALcomplex */ +static inline ALcomplex polar2rect(ALphasor number) +{ + ALcomplex cartesian; + + cartesian.Real = number.Amplitude * cos(number.Phase); + cartesian.Imag = number.Amplitude * sin(number.Phase); + + return cartesian; +} + +/* Addition of two complex numbers (ALcomplex format) */ +static inline ALcomplex complex_add(ALcomplex a, ALcomplex b) +{ + ALcomplex result; + + result.Real = a.Real + b.Real; + result.Imag = a.Imag + b.Imag; + + return result; +} + +/* Subtraction of two complex numbers (ALcomplex format) */ +static inline ALcomplex complex_sub(ALcomplex a, ALcomplex b) +{ + ALcomplex result; + + result.Real = a.Real - b.Real; + result.Imag = a.Imag - b.Imag; + + return result; +} + +/* Multiplication of two complex numbers (ALcomplex format) */ +static inline ALcomplex complex_mult(ALcomplex a, ALcomplex b) +{ + ALcomplex result; + + result.Real = a.Real*b.Real - a.Imag*b.Imag; + result.Imag = a.Imag*b.Real + a.Real*b.Imag; + + return result; +} + +/* Iterative implementation of 2-radix FFT (In-place algorithm). Sign = -1 is + * FFT and 1 is iFFT (inverse). Fills FFTBuffer[0...FFTSize-1] with the + * Discrete Fourier Transform (DFT) of the time domain data stored in + * FFTBuffer[0...FFTSize-1]. FFTBuffer is an array of complex numbers + * (ALcomplex), FFTSize MUST BE power of two. + */ +static inline ALvoid FFT(ALcomplex *FFTBuffer, ALsizei FFTSize, ALdouble Sign) +{ + ALsizei i, j, k, mask, step, step2; + ALcomplex temp, u, w; + ALdouble arg; + + /* Bit-reversal permutation applied to a sequence of FFTSize items */ + for(i = 1;i < FFTSize-1;i++) + { + for(mask = 0x1, j = 0;mask < FFTSize;mask <<= 1) + { + if((i&mask) != 0) + j++; + j <<= 1; + } + j >>= 1; + + if(i < j) + { + temp = FFTBuffer[i]; + FFTBuffer[i] = FFTBuffer[j]; + FFTBuffer[j] = temp; + } + } + + /* Iterative form of Danielson–Lanczos lemma */ + for(i = 1, step = 2;i < FFTSize;i<<=1, step<<=1) + { + step2 = step >> 1; + arg = M_PI / step2; + + w.Real = cos(arg); + w.Imag = sin(arg) * Sign; + + u.Real = 1.0; + u.Imag = 0.0; + + for(j = 0;j < step2;j++) + { + for(k = j;k < FFTSize;k+=step) + { + temp = complex_mult(FFTBuffer[k+step2], u); + FFTBuffer[k+step2] = complex_sub(FFTBuffer[k], temp); + FFTBuffer[k] = complex_add(FFTBuffer[k], temp); + } + + u = complex_mult(u, w); + } + } +} + + +static void ALpshifterState_Construct(ALpshifterState *state) +{ + ALeffectState_Construct(STATIC_CAST(ALeffectState, state)); + SET_VTABLE2(ALpshifterState, ALeffectState, state); + + alcall_once(&HannInitOnce, InitHannWindow); +} + +static ALvoid ALpshifterState_Destruct(ALpshifterState *state) +{ + ALeffectState_Destruct(STATIC_CAST(ALeffectState,state)); +} + +static ALboolean ALpshifterState_deviceUpdate(ALpshifterState *state, ALCdevice *device) +{ + /* (Re-)initializing parameters and clear the buffers. */ + state->count = FIFO_LATENCY; + state->PitchShiftI = FRACTIONONE; + state->PitchShift = 1.0f; + state->FreqPerBin = device->Frequency / (ALfloat)STFT_SIZE; + + memset(state->InFIFO, 0, sizeof(state->InFIFO)); + memset(state->OutFIFO, 0, sizeof(state->OutFIFO)); + memset(state->FFTbuffer, 0, sizeof(state->FFTbuffer)); + memset(state->LastPhase, 0, sizeof(state->LastPhase)); + memset(state->SumPhase, 0, sizeof(state->SumPhase)); + memset(state->OutputAccum, 0, sizeof(state->OutputAccum)); + memset(state->Analysis_buffer, 0, sizeof(state->Analysis_buffer)); + memset(state->Syntesis_buffer, 0, sizeof(state->Syntesis_buffer)); + + memset(state->CurrentGains, 0, sizeof(state->CurrentGains)); + memset(state->TargetGains, 0, sizeof(state->TargetGains)); + + return AL_TRUE; +} + +static ALvoid ALpshifterState_update(ALpshifterState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props) +{ + const ALCdevice *device = context->Device; + ALfloat coeffs[MAX_AMBI_COEFFS]; + float pitch; + + pitch = powf(2.0f, + (ALfloat)(props->Pshifter.CoarseTune*100 + props->Pshifter.FineTune) / 1200.0f + ); + state->PitchShiftI = (ALsizei)(pitch*FRACTIONONE + 0.5f); + state->PitchShift = state->PitchShiftI * (1.0f/FRACTIONONE); + + CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs); + ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain, state->TargetGains); +} + +static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels) +{ + /* Pitch shifter engine based on the work of Stephan Bernsee. + * http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/ + */ + + static const ALdouble expected = M_PI*2.0 / OVERSAMP; + const ALdouble freq_per_bin = state->FreqPerBin; + ALfloat *restrict bufferOut = state->BufferOut; + ALsizei count = state->count; + ALsizei i, j, k; + + for(i = 0;i < SamplesToDo;) + { + do { + /* Fill FIFO buffer with samples data */ + state->InFIFO[count] = SamplesIn[0][i]; + bufferOut[i] = state->OutFIFO[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(k = 0;k < STFT_SIZE;k++) + { + state->FFTbuffer[k].Real = state->InFIFO[k] * HannWindow[k]; + state->FFTbuffer[k].Imag = 0.0; + } + + /* ANALYSIS */ + /* Apply FFT to FFTbuffer data */ + FFT(state->FFTbuffer, STFT_SIZE, -1.0); + + /* Analyze the obtained data. Since the real FFT is symmetric, only + * STFT_HALF_SIZE+1 samples are needed. + */ + for(k = 0;k < STFT_HALF_SIZE+1;k++) + { + ALphasor component; + ALdouble tmp; + ALint qpd; + + /* Compute amplitude and phase */ + component = rect2polar(state->FFTbuffer[k]); + + /* Compute phase difference and subtract expected phase difference */ + tmp = (component.Phase - state->LastPhase[k]) - k*expected; + + /* Map delta phase into +/- Pi interval */ + qpd = fastd2i(tmp / M_PI); + tmp -= M_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. + */ + state->Analysis_buffer[k].Amplitude = 2.0 * component.Amplitude; + state->Analysis_buffer[k].Frequency = (k + tmp) * freq_per_bin; + + /* Store actual phase[k] for the calculations in the next frame*/ + state->LastPhase[k] = component.Phase; + } + + /* PROCESSING */ + /* pitch shifting */ + for(k = 0;k < STFT_HALF_SIZE+1;k++) + { + state->Syntesis_buffer[k].Amplitude = 0.0; + state->Syntesis_buffer[k].Frequency = 0.0; + } + + for(k = 0;k < STFT_HALF_SIZE+1;k++) + { + j = (k*state->PitchShiftI) >> FRACTIONBITS; + if(j >= STFT_HALF_SIZE+1) break; + + state->Syntesis_buffer[j].Amplitude += state->Analysis_buffer[k].Amplitude; + state->Syntesis_buffer[j].Frequency = state->Analysis_buffer[k].Frequency * + state->PitchShift; + } + + /* SYNTHESIS */ + /* Synthesis the processing data */ + for(k = 0;k < STFT_HALF_SIZE+1;k++) + { + ALphasor component; + ALdouble tmp; + + /* Compute bin deviation from scaled freq */ + tmp = state->Syntesis_buffer[k].Frequency/freq_per_bin - k; + + /* Calculate actual delta phase and accumulate it to get bin phase */ + state->SumPhase[k] += (k + tmp) * expected; + + component.Amplitude = state->Syntesis_buffer[k].Amplitude; + component.Phase = state->SumPhase[k]; + + /* Compute phasor component to cartesian complex number and storage it into FFTbuffer*/ + state->FFTbuffer[k] = polar2rect(component); + } + /* zero negative frequencies for recontruct a real signal */ + for(k = STFT_HALF_SIZE+1;k < STFT_SIZE;k++) + { + state->FFTbuffer[k].Real = 0.0; + state->FFTbuffer[k].Imag = 0.0; + } + + /* Apply iFFT to buffer data */ + FFT(state->FFTbuffer, STFT_SIZE, 1.0); + + /* Windowing and add to output */ + for(k = 0;k < STFT_SIZE;k++) + state->OutputAccum[k] += HannWindow[k] * state->FFTbuffer[k].Real / + (0.5 * STFT_HALF_SIZE * OVERSAMP); + + /* Shift accumulator, input & output FIFO */ + for(k = 0;k < STFT_STEP;k++) state->OutFIFO[k] = (ALfloat)state->OutputAccum[k]; + for(j = 0;k < STFT_SIZE;k++,j++) state->OutputAccum[j] = state->OutputAccum[k]; + for(;j < STFT_SIZE;j++) state->OutputAccum[j] = 0.0; + for(k = 0;k < FIFO_LATENCY;k++) + state->InFIFO[k] = state->InFIFO[k+STFT_STEP]; + } + state->count = count; + + /* Now, mix the processed sound data to the output. */ + MixSamples(bufferOut, NumChannels, SamplesOut, state->CurrentGains, state->TargetGains, + maxi(SamplesToDo, 512), 0, SamplesToDo); +} + +typedef struct PshifterStateFactory { + DERIVE_FROM_TYPE(EffectStateFactory); +} PshifterStateFactory; + +static ALeffectState *PshifterStateFactory_create(PshifterStateFactory *UNUSED(factory)) +{ + ALpshifterState *state; + + NEW_OBJ0(state, ALpshifterState)(); + if(!state) return NULL; + + return STATIC_CAST(ALeffectState, state); +} + +DEFINE_EFFECTSTATEFACTORY_VTABLE(PshifterStateFactory); + +EffectStateFactory *PshifterStateFactory_getFactory(void) +{ + static PshifterStateFactory PshifterFactory = { { GET_VTABLE2(PshifterStateFactory, EffectStateFactory) } }; + + return STATIC_CAST(EffectStateFactory, &PshifterFactory); +} + + +void ALpshifter_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val)) +{ + alSetError( context, AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param ); +} + +void ALpshifter_setParamfv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat *UNUSED(vals)) +{ + alSetError( context, AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x", param ); +} + +void ALpshifter_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val) +{ + ALeffectProps *props = &effect->Props; + 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 ALpshifter_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) +{ + ALpshifter_setParami(effect, context, param, vals[0]); +} + +void ALpshifter_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val) +{ + const ALeffectProps *props = &effect->Props; + switch(param) + { + case AL_PITCH_SHIFTER_COARSE_TUNE: + *val = (ALint)props->Pshifter.CoarseTune; + break; + case AL_PITCH_SHIFTER_FINE_TUNE: + *val = (ALint)props->Pshifter.FineTune; + break; + + default: + alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", param); + } +} +void ALpshifter_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) +{ + ALpshifter_getParami(effect, context, param, vals); +} + +void ALpshifter_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(val)) +{ + alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param); +} + +void ALpshifter_getParamfv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(vals)) +{ + alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x", param); +} + +DEFINE_ALEFFECT_VTABLE(ALpshifter); diff --git a/Engine/lib/openal-soft/Alc/effects/reverb.c b/Engine/lib/openal-soft/Alc/effects/reverb.c index f2d5b7189..12e78bdfc 100644 --- a/Engine/lib/openal-soft/Alc/effects/reverb.c +++ b/Engine/lib/openal-soft/Alc/effects/reverb.c @@ -1,6 +1,6 @@ /** - * Reverb for the OpenAL cross platform audio library - * Copyright (C) 2008-2009 by Christopher Fitzgerald. + * 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 @@ -27,258 +27,400 @@ #include "alMain.h" #include "alu.h" #include "alAuxEffectSlot.h" -#include "alEffect.h" -#include "alFilter.h" +#include "alListener.h" #include "alError.h" -#include "mixer_defs.h" +#include "filters/defs.h" +/* This is a user config option for modifying the overall output of the reverb + * effect. + */ +ALfloat ReverbBoost = 1.0f; /* This is the maximum number of samples processed for each inner loop * iteration. */ #define MAX_UPDATE_SAMPLES 256 +/* 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. + */ +#define FADE_SAMPLES 128 -static MixerFunc MixSamples = Mix_C; -static RowMixerFunc MixRowSamples = MixRow_C; +/* 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. + */ +#define NUM_LINES 4 -static alonce_flag mixfunc_inited = AL_ONCE_FLAG_INIT; -static void init_mixfunc(void) + +/* 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. + */ +static const aluMatrixf B2A = {{ + { 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. */ +static const aluMatrixf A2B = {{ + { 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 } +}}; + +static const 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, cbrtf(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. + */ +static const 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: + */ +static const ALfloat EARLY_TAP_LENGTHS[NUM_LINES] = { - MixSamples = SelectMixer(); - MixRowSamples = SelectRowMixer(); -} + 0.0000000e+0f, 2.0213520e-4f, 4.2531060e-4f, 6.7171600e-4f +}; - -typedef struct DelayLine +/* 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). + */ +static const ALfloat EARLY_ALLPASS_LENGTHS[NUM_LINES] = { - // The delay lines use sample lengths that are powers of 2 to allow the - // use of bit-masking instead of a modulus for wrapping. - ALuint Mask; - ALfloat *Line; -} DelayLine; + 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: + */ +static const ALfloat EARLY_LINE_LENGTHS[NUM_LINES] = +{ + 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 + */ +static const ALfloat LATE_ALLPASS_LENGTHS[NUM_LINES] = +{ + 1.6182800e-4f, 2.0389060e-4f, 2.8159360e-4f, 3.2365600e-4f +}; + +/* 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: + */ +static const ALfloat LATE_LINE_LENGTHS[NUM_LINES] = +{ + 1.9419362e-3f, 2.4466860e-3f, 3.3791220e-3f, 3.8838720e-3f +}; + + +typedef 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; + ALfloat (*Line)[NUM_LINES]; +} DelayLineI; + +typedef struct VecAllpass { + DelayLineI Delay; + ALsizei Offset[NUM_LINES][2]; +} VecAllpass; + +typedef struct T60Filter { + /* Two filters are used to adjust the signal. One to control the low + * frequencies, and one to control the high frequencies. The HF filter also + * adjusts the overall output gain, affecting the remaining mid-band. + */ + ALfloat HFCoeffs[3]; + ALfloat LFCoeffs[3]; + + /* The HF and LF filters each keep a delay component. */ + ALfloat HFState; + ALfloat LFState; +} T60Filter; + +typedef 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]; + + /* The gain for each output channel based on 3D panning. */ + ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS]; + ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS]; +} EarlyReflections; + +typedef struct LateReverb { + /* Attenuation to compensate for the modal density and decay rate of the + * late lines. + */ + ALfloat DensityGain; + + /* A recursive delay line is used fill in the reverb tail. */ + DelayLineI Delay; + ALsizei Offset[NUM_LINES][2]; + + /* 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]; +} LateReverb; typedef struct ALreverbState { DERIVE_FROM_TYPE(ALeffectState); - ALboolean IsEax; + /* All delay lines are allocated as a single buffer to reduce memory + * fragmentation and management code. + */ + ALfloat *SampleBuffer; + ALuint TotalSamples; - // All delay lines are allocated as a single buffer to reduce memory - // fragmentation and management code. - ALfloat *SampleBuffer; - ALuint TotalSamples; - - // Master effect filters + /* Master effect filters */ struct { - ALfilterState Lp; - ALfilterState Hp; // EAX only - } Filter[4]; - - struct { - // Modulator delay line. - DelayLine Delay[4]; - - // The vibrato time is tracked with an index over a modulus-wrapped - // range (in samples). - ALuint Index; - ALuint Range; - - // The depth of frequency change (also in samples) and its filter. - ALfloat Depth; - ALfloat Coeff; - ALfloat Filter; - } Mod; // EAX only + BiquadFilter Lp; + BiquadFilter Hp; + } Filter[NUM_LINES]; /* Core delay line (early reflections and late reverb tap from this). */ - DelayLine Delay; - /* The tap points for the initial delay. First set go to early - * reflections, second to late reverb. - */ - ALuint EarlyDelayTap[4]; - ALuint LateDelayTap[4]; + DelayLineI Delay; - struct { - // Early reflections are done with 4 delay lines. - ALfloat Coeff[4]; - DelayLine Delay[4]; - ALuint Offset[4]; + /* Tap points for early reflection delay. */ + ALsizei EarlyDelayTap[NUM_LINES][2]; + ALfloat EarlyDelayCoeff[NUM_LINES]; - // The gain for each output channel based on 3D panning. - ALfloat CurrentGain[4][MAX_OUTPUT_CHANNELS]; - ALfloat PanGain[4][MAX_OUTPUT_CHANNELS]; - } Early; + /* Tap points for late reverb feed and delay. */ + ALsizei LateFeedTap; + ALsizei LateDelayTap[NUM_LINES][2]; - struct { - // Output gain for late reverb. - ALfloat Gain; + /* The feed-back and feed-forward all-pass coefficient. */ + ALfloat ApFeedCoeff; - // Attenuation to compensate for the modal density and decay rate of - // the late lines. - ALfloat DensityGain; + /* Coefficients for the all-pass and line scattering matrices. */ + ALfloat MixX; + ALfloat MixY; - // The feed-back and feed-forward all-pass coefficient. - ALfloat ApFeedCoeff; + EarlyReflections Early; - // Mixing matrix coefficient. - ALfloat MixCoeff; + LateReverb Late; - // Late reverb has 4 parallel all-pass filters. - struct { - ALfloat Coeff; - DelayLine Delay; - ALuint Offset; - } Ap[4]; + /* Indicates the cross-fade point for delay line reads [0,FADE_SAMPLES]. */ + ALsizei FadeCount; - // In addition to 4 cyclical delay lines. - ALfloat Coeff[4]; - DelayLine Delay[4]; - ALuint Offset[4]; - - // The cyclical delay lines are 1-pole low-pass filtered. - struct { - ALfloat Sample; - ALfloat Coeff; - } Lp[4]; - - // The gain for each output channel based on 3D panning. - ALfloat CurrentGain[4][MAX_OUTPUT_CHANNELS]; - ALfloat PanGain[4][MAX_OUTPUT_CHANNELS]; - } Late; - - struct { - // Attenuation to compensate for the modal density and decay rate of - // the echo line. - ALfloat DensityGain; - - // Echo delay and all-pass lines. - struct { - DelayLine Feedback; - DelayLine Ap; - } Delay[4]; - - ALfloat Coeff; - ALfloat ApFeedCoeff; - ALfloat ApCoeff; - - ALuint Offset; - ALuint ApOffset; - - // The echo line is 1-pole low-pass filtered. - ALfloat LpCoeff; - ALfloat LpSample[4]; - - // Echo mixing coefficient. - ALfloat MixCoeff; - } Echo; // EAX only - - // The current read offset for all delay lines. - ALuint Offset; + /* The current write offset for all delay lines. */ + ALsizei Offset; /* Temporary storage used when processing. */ - alignas(16) ALfloat AFormatSamples[4][MAX_UPDATE_SAMPLES]; - alignas(16) ALfloat ReverbSamples[4][MAX_UPDATE_SAMPLES]; - alignas(16) ALfloat EarlySamples[4][MAX_UPDATE_SAMPLES]; + alignas(16) ALfloat AFormatSamples[NUM_LINES][MAX_UPDATE_SAMPLES]; + alignas(16) ALfloat ReverbSamples[NUM_LINES][MAX_UPDATE_SAMPLES]; + alignas(16) ALfloat EarlySamples[NUM_LINES][MAX_UPDATE_SAMPLES]; } ALreverbState; static ALvoid ALreverbState_Destruct(ALreverbState *State); static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device); -static ALvoid ALreverbState_update(ALreverbState *State, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props); -static ALvoid ALreverbState_process(ALreverbState *State, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels); +static ALvoid ALreverbState_update(ALreverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props); +static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels); DECLARE_DEFAULT_ALLOCATORS(ALreverbState) DEFINE_ALEFFECTSTATE_VTABLE(ALreverbState); - static void ALreverbState_Construct(ALreverbState *state) { - ALuint index, l; + ALsizei i, j; ALeffectState_Construct(STATIC_CAST(ALeffectState, state)); SET_VTABLE2(ALreverbState, ALeffectState, state); - state->IsEax = AL_FALSE; - state->TotalSamples = 0; state->SampleBuffer = NULL; - for(index = 0;index < 4;index++) + for(i = 0;i < NUM_LINES;i++) { - ALfilterState_clear(&state->Filter[index].Lp); - ALfilterState_clear(&state->Filter[index].Hp); - - state->Mod.Delay[index].Mask = 0; - state->Mod.Delay[index].Line = NULL; + BiquadFilter_clear(&state->Filter[i].Lp); + BiquadFilter_clear(&state->Filter[i].Hp); } - state->Mod.Index = 0; - state->Mod.Range = 1; - state->Mod.Depth = 0.0f; - state->Mod.Coeff = 0.0f; - state->Mod.Filter = 0.0f; - state->Delay.Mask = 0; state->Delay.Line = NULL; - for(index = 0;index < 4;index++) - state->EarlyDelayTap[index] = 0; - for(index = 0;index < 4;index++) - state->LateDelayTap[index] = 0; - for(index = 0;index < 4;index++) + for(i = 0;i < NUM_LINES;i++) { - state->Early.Coeff[index] = 0.0f; - state->Early.Delay[index].Mask = 0; - state->Early.Delay[index].Line = NULL; - state->Early.Offset[index] = 0; + state->EarlyDelayTap[i][0] = 0; + state->EarlyDelayTap[i][1] = 0; + state->EarlyDelayCoeff[i] = 0.0f; + } + + state->LateFeedTap = 0; + + for(i = 0;i < NUM_LINES;i++) + { + state->LateDelayTap[i][0] = 0; + state->LateDelayTap[i][1] = 0; + } + + state->ApFeedCoeff = 0.0f; + state->MixX = 0.0f; + state->MixY = 0.0f; + + state->Early.VecAp.Delay.Mask = 0; + state->Early.VecAp.Delay.Line = NULL; + state->Early.Delay.Mask = 0; + state->Early.Delay.Line = NULL; + for(i = 0;i < NUM_LINES;i++) + { + state->Early.VecAp.Offset[i][0] = 0; + state->Early.VecAp.Offset[i][1] = 0; + state->Early.Offset[i][0] = 0; + state->Early.Offset[i][1] = 0; + state->Early.Coeff[i] = 0.0f; } - state->Late.Gain = 0.0f; state->Late.DensityGain = 0.0f; - state->Late.ApFeedCoeff = 0.0f; - state->Late.MixCoeff = 0.0f; - for(index = 0;index < 4;index++) + + state->Late.Delay.Mask = 0; + state->Late.Delay.Line = NULL; + state->Late.VecAp.Delay.Mask = 0; + state->Late.VecAp.Delay.Line = NULL; + for(i = 0;i < NUM_LINES;i++) { - state->Late.Ap[index].Coeff = 0.0f; - state->Late.Ap[index].Delay.Mask = 0; - state->Late.Ap[index].Delay.Line = NULL; - state->Late.Ap[index].Offset = 0; + state->Late.Offset[i][0] = 0; + state->Late.Offset[i][1] = 0; - state->Late.Coeff[index] = 0.0f; - state->Late.Delay[index].Mask = 0; - state->Late.Delay[index].Line = NULL; - state->Late.Offset[index] = 0; + state->Late.VecAp.Offset[i][0] = 0; + state->Late.VecAp.Offset[i][1] = 0; - state->Late.Lp[index].Sample = 0.0f; - state->Late.Lp[index].Coeff = 0.0f; + for(j = 0;j < 3;j++) + { + state->Late.T60[i].HFCoeffs[j] = 0.0f; + state->Late.T60[i].LFCoeffs[j] = 0.0f; + } + state->Late.T60[i].HFState = 0.0f; + state->Late.T60[i].LFState = 0.0f; } - for(l = 0;l < 4;l++) + for(i = 0;i < NUM_LINES;i++) { - for(index = 0;index < MAX_OUTPUT_CHANNELS;index++) + for(j = 0;j < MAX_OUTPUT_CHANNELS;j++) { - state->Early.CurrentGain[l][index] = 0.0f; - state->Early.PanGain[l][index] = 0.0f; - state->Late.CurrentGain[l][index] = 0.0f; - state->Late.PanGain[l][index] = 0.0f; + state->Early.CurrentGain[i][j] = 0.0f; + state->Early.PanGain[i][j] = 0.0f; + state->Late.CurrentGain[i][j] = 0.0f; + state->Late.PanGain[i][j] = 0.0f; } } - state->Echo.DensityGain = 0.0f; - for(l = 0;l < 4;l++) - { - state->Echo.Delay[l].Feedback.Mask = 0; - state->Echo.Delay[l].Feedback.Line = NULL; - state->Echo.Delay[l].Ap.Mask = 0; - state->Echo.Delay[l].Ap.Line = NULL; - } - state->Echo.Coeff = 0.0f; - state->Echo.ApFeedCoeff = 0.0f; - state->Echo.ApCoeff = 0.0f; - state->Echo.Offset = 0; - state->Echo.ApOffset = 0; - state->Echo.LpCoeff = 0.0f; - for(l = 0;l < 4;l++) - state->Echo.LpSample[l] = 0.0f; - state->Echo.MixCoeff = 0.0f; - + state->FadeCount = 0; state->Offset = 0; } @@ -290,107 +432,45 @@ static ALvoid ALreverbState_Destruct(ALreverbState *State) ALeffectState_Destruct(STATIC_CAST(ALeffectState,State)); } -/* This is a user config option for modifying the overall output of the reverb - * effect. - */ -ALfloat ReverbBoost = 1.0f; - -/* Specifies whether to use a standard reverb effect in place of EAX reverb (no - * high-pass, modulation, or echo). - */ -ALboolean EmulateEAXReverb = AL_FALSE; - -/* This coefficient is used to define the maximum frequency range controlled - * by the modulation depth. The current value of 0.1 will allow it to swing - * from 0.9x to 1.1x. This value must be below 1. At 1 it will cause the - * sampler to stall on the downswing, and above 1 it will cause it to sample - * backwards. - */ -static const ALfloat MODULATION_DEPTH_COEFF = 0.1f; - -/* A filter is used to avoid the terrible distortion caused by changing - * modulation time and/or depth. To be consistent across different sample - * rates, the coefficient must be raised to a constant divided by the sample - * rate: coeff^(constant / rate). - */ -static const ALfloat MODULATION_FILTER_COEFF = 0.048f; -static const ALfloat MODULATION_FILTER_CONST = 100000.0f; - -// When diffusion is above 0, an all-pass filter is used to take the edge off -// the echo effect. It uses the following line length (in seconds). -static const ALfloat ECHO_ALLPASS_LENGTH = 0.0133f; - -/* Input into the early reflections and late reverb are decorrelated between - * four channels. Their timings are dependent on a fraction and multiplier. See - * the UpdateDelayLine() routine for the calculations involved. - */ -static const ALfloat DECO_FRACTION = 0.15f; -static const ALfloat DECO_MULTIPLIER = 2.0f; - -// All delay line lengths are specified in seconds. - -// The lengths of the early delay lines. -static const ALfloat EARLY_LINE_LENGTH[4] = -{ - 0.0015f, 0.0045f, 0.0135f, 0.0405f -}; - -// The lengths of the late cyclical delay lines. -static const ALfloat LATE_LINE_LENGTH[4] = -{ - 0.0211f, 0.0311f, 0.0461f, 0.0680f -}; - -// The lengths of the late all-pass delay lines. -static const ALfloat ALLPASS_LINE_LENGTH[4] = -{ - 0.0151f, 0.0167f, 0.0183f, 0.0200f, -}; - -// The late cyclical delay lines have a variable length dependent on the -// effect's density parameter (inverted for some reason) and this multiplier. -static const ALfloat LATE_LINE_MULTIPLIER = 4.0f; - - -#if defined(_WIN32) && !defined (_M_X64) && !defined(_M_ARM) -/* HACK: Workaround for a modff bug in 32-bit Windows, which attempts to write - * a 64-bit double to the 32-bit float parameter. - */ -static inline float hack_modff(float x, float *y) -{ - double di; - double df = modf((double)x, &di); - *y = (float)di; - return (float)df; -} -#define modff hack_modff -#endif - - /************************************** * Device Update * **************************************/ -// Given the allocated sample buffer, this function updates each delay line -// offset. -static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLine *Delay) +static inline ALfloat CalcDelayLengthMult(ALfloat density) { - Delay->Line = &sampleBuffer[(ptrdiff_t)Delay->Line]; + return maxf(5.0f, cbrtf(density*DENSITY_SCALE)); } -// Calculate the length of a delay line and store its mask and offset. -static ALuint CalcLineLength(ALfloat length, ptrdiff_t offset, ALuint frequency, ALuint extra, DelayLine *Delay) +/* Given the allocated sample buffer, this function updates each delay line + * offset. + */ +static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLineI *Delay) +{ + union { + ALfloat *f; + ALfloat (*f4)[NUM_LINES]; + } u; + u.f = &sampleBuffer[(ptrdiff_t)Delay->Line * NUM_LINES]; + Delay->Line = u.f4; +} + +/* Calculate the length of a delay line and store its mask and offset. */ +static ALuint CalcLineLength(const ALfloat length, const ptrdiff_t offset, const ALuint frequency, + const ALuint extra, DelayLineI *Delay) { ALuint samples; - // All line lengths are powers of 2, calculated from their lengths, with - // an additional sample in case of rounding errors. - samples = fastf2u(length*frequency) + extra; - samples = NextPowerOf2(samples + 1); - // All lines share a single sample buffer. + /* All line lengths are powers of 2, calculated from their lengths in + * seconds, rounded up. + */ + samples = float2int(ceilf(length*frequency)); + samples = NextPowerOf2(samples + extra); + + /* All lines share a single sample buffer. */ Delay->Mask = samples - 1; - Delay->Line = (ALfloat*)offset; - // Return the sample count for accumulation. + Delay->Line = (ALfloat(*)[NUM_LINES])offset; + + /* Return the sample count for accumulation. */ return samples; } @@ -398,73 +478,60 @@ static ALuint CalcLineLength(ALfloat length, ptrdiff_t offset, ALuint frequency, * for all lines given the sample rate (frequency). If an allocation failure * occurs, it returns AL_FALSE. */ -static ALboolean AllocLines(ALuint frequency, ALreverbState *State) +static ALboolean AllocLines(const ALuint frequency, ALreverbState *State) { - ALuint totalSamples, index; - ALfloat length; + ALuint totalSamples, i; + ALfloat multiplier, length; - // All delay line lengths are calculated to accomodate the full range of - // lengths given their respective paramters. + /* All delay line lengths are calculated to accomodate the full range of + * lengths given their respective paramters. + */ totalSamples = 0; - /* The modulator's line length is calculated from the maximum modulation - * time and depth coefficient, and halfed for the low-to-high frequency - * swing. An additional sample is added to keep it stable when there is no - * modulation. + /* Multiplier for the maximum density value, i.e. density=1, which is + * actually the least density... */ - length = (AL_EAXREVERB_MAX_MODULATION_TIME*MODULATION_DEPTH_COEFF/2.0f); - for(index = 0;index < 4;index++) - totalSamples += CalcLineLength(length, totalSamples, frequency, 1, - &State->Mod.Delay[index]); + multiplier = CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY); - /* The initial delay is the sum of the reflections and late reverb delays. - * The decorrelator length is calculated from the lowest reverb density (a - * parameter value of 1). This must include space for storing a loop - * update. + /* 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 (MAX_UPDATE_SAMPLES) for block processing. */ - length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY + - AL_EAXREVERB_MAX_LATE_REVERB_DELAY; - length += (DECO_FRACTION * DECO_MULTIPLIER * DECO_MULTIPLIER) * - LATE_LINE_LENGTH[0] * (1.0f + LATE_LINE_MULTIPLIER); - /* Multiply length by 4, since we're storing 4 interleaved channels in the - * main delay line. + length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY + EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier + + AL_EAXREVERB_MAX_LATE_REVERB_DELAY + + (LATE_LINE_LENGTHS[NUM_LINES-1] - LATE_LINE_LENGTHS[0])*0.25f*multiplier; + totalSamples += CalcLineLength(length, totalSamples, frequency, MAX_UPDATE_SAMPLES, + &State->Delay); + + /* The early vector all-pass line. */ + length = EARLY_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier; + totalSamples += CalcLineLength(length, totalSamples, frequency, 0, + &State->Early.VecAp.Delay); + + /* The early reflection line. */ + length = EARLY_LINE_LENGTHS[NUM_LINES-1] * multiplier; + totalSamples += CalcLineLength(length, totalSamples, frequency, 0, + &State->Early.Delay); + + /* The late vector all-pass line. */ + length = LATE_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier; + totalSamples += CalcLineLength(length, totalSamples, frequency, 0, + &State->Late.VecAp.Delay); + + /* The late delay lines are calculated from the larger of the maximum + * density line length or the maximum echo time. */ - totalSamples += CalcLineLength(length*4, totalSamples, frequency, - MAX_UPDATE_SAMPLES*4, &State->Delay); - - // The early reflection lines. - for(index = 0;index < 4;index++) - totalSamples += CalcLineLength(EARLY_LINE_LENGTH[index], totalSamples, - frequency, 0, &State->Early.Delay[index]); - - // The late delay lines are calculated from the lowest reverb density. - for(index = 0;index < 4;index++) - { - length = LATE_LINE_LENGTH[index] * (1.0f + LATE_LINE_MULTIPLIER); - totalSamples += CalcLineLength(length, totalSamples, frequency, 0, - &State->Late.Delay[index]); - } - - // The late all-pass lines. - for(index = 0;index < 4;index++) - totalSamples += CalcLineLength(ALLPASS_LINE_LENGTH[index], totalSamples, - frequency, 0, &State->Late.Ap[index].Delay); - - // The echo all-pass and delay lines. - for(index = 0;index < 4;index++) - { - totalSamples += CalcLineLength(ECHO_ALLPASS_LENGTH, totalSamples, - frequency, 0, &State->Echo.Delay[index].Ap); - totalSamples += CalcLineLength(AL_EAXREVERB_MAX_ECHO_TIME, totalSamples, - frequency, 0, &State->Echo.Delay[index].Feedback); - } + length = maxf(AL_EAXREVERB_MAX_ECHO_TIME, LATE_LINE_LENGTHS[NUM_LINES-1]*multiplier); + totalSamples += CalcLineLength(length, totalSamples, frequency, 0, + &State->Late.Delay); if(totalSamples != State->TotalSamples) { ALfloat *newBuffer; - TRACE("New reverb buffer length: %u samples\n", totalSamples); - newBuffer = al_calloc(16, sizeof(ALfloat) * totalSamples); + TRACE("New reverb buffer length: %ux4 samples\n", totalSamples); + newBuffer = al_calloc(16, sizeof(ALfloat[NUM_LINES]) * totalSamples); if(!newBuffer) return AL_FALSE; al_free(State->SampleBuffer); @@ -472,54 +539,35 @@ static ALboolean AllocLines(ALuint frequency, ALreverbState *State) State->TotalSamples = totalSamples; } - // Update all delays to reflect the new sample buffer. + /* Update all delays to reflect the new sample buffer. */ RealizeLineOffset(State->SampleBuffer, &State->Delay); - for(index = 0;index < 4;index++) - { - RealizeLineOffset(State->SampleBuffer, &State->Mod.Delay[index]); + RealizeLineOffset(State->SampleBuffer, &State->Early.VecAp.Delay); + RealizeLineOffset(State->SampleBuffer, &State->Early.Delay); + RealizeLineOffset(State->SampleBuffer, &State->Late.VecAp.Delay); + RealizeLineOffset(State->SampleBuffer, &State->Late.Delay); - RealizeLineOffset(State->SampleBuffer, &State->Early.Delay[index]); - - RealizeLineOffset(State->SampleBuffer, &State->Late.Ap[index].Delay); - RealizeLineOffset(State->SampleBuffer, &State->Late.Delay[index]); - - RealizeLineOffset(State->SampleBuffer, &State->Echo.Delay[index].Ap); - RealizeLineOffset(State->SampleBuffer, &State->Echo.Delay[index].Feedback); - } - - // Clear the sample buffer. - for(index = 0;index < State->TotalSamples;index++) - State->SampleBuffer[index] = 0.0f; + /* Clear the sample buffer. */ + for(i = 0;i < State->TotalSamples;i++) + State->SampleBuffer[i] = 0.0f; return AL_TRUE; } static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device) { - ALuint frequency = Device->Frequency, index; + ALuint frequency = Device->Frequency; + ALfloat multiplier; - // Allocate the delay lines. + /* Allocate the delay lines. */ if(!AllocLines(frequency, State)) return AL_FALSE; - // Calculate the modulation filter coefficient. Notice that the exponent - // is calculated given the current sample rate. This ensures that the - // resulting filter response over time is consistent across all sample - // rates. - State->Mod.Coeff = powf(MODULATION_FILTER_COEFF, - MODULATION_FILTER_CONST / frequency); + multiplier = CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY); - // The early reflection and late all-pass filter line lengths are static, - // so their offsets only need to be calculated once. - for(index = 0;index < 4;index++) - { - State->Early.Offset[index] = fastf2u(EARLY_LINE_LENGTH[index] * frequency); - State->Late.Ap[index].Offset = fastf2u(ALLPASS_LINE_LENGTH[index] * frequency); - } - - // The echo all-pass filter line length is static, so its offset only - // needs to be calculated once. - State->Echo.ApOffset = fastf2u(ECHO_ALLPASS_LENGTH * frequency); + /* The late feed taps are set a fixed position past the latest delay tap. */ + State->LateFeedTap = float2int((AL_EAXREVERB_MAX_REFLECTIONS_DELAY + + EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier) * + frequency); return AL_TRUE; } @@ -528,23 +576,26 @@ static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Dev * Effect Update * **************************************/ -// Calculate a decay coefficient given the length of each cycle and the time -// until the decay reaches -60 dB. -static inline ALfloat CalcDecayCoeff(ALfloat length, ALfloat decayTime) +/* Calculate a decay coefficient given the length of each cycle and the time + * until the decay reaches -60 dB. + */ +static inline ALfloat CalcDecayCoeff(const ALfloat length, const ALfloat decayTime) { - return powf(0.001f/*-60 dB*/, length/decayTime); + return powf(REVERB_DECAY_GAIN, length/decayTime); } -// Calculate a decay length from a coefficient and the time until the decay -// reaches -60 dB. -static inline ALfloat CalcDecayLength(ALfloat coeff, ALfloat decayTime) +/* Calculate a decay length from a coefficient and the time until the decay + * reaches -60 dB. + */ +static inline ALfloat CalcDecayLength(const ALfloat coeff, const ALfloat decayTime) { - return log10f(coeff) * decayTime / log10f(0.001f)/*-60 dB*/; + return log10f(coeff) * decayTime / log10f(REVERB_DECAY_GAIN); } -// Calculate an attenuation to be applied to the input of any echo models to -// compensate for modal density and decay time. -static inline ALfloat CalcDensityGain(ALfloat a) +/* Calculate an attenuation to be applied to the input of any echo models to + * compensate for modal density and decay time. + */ +static inline ALfloat CalcDensityGain(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 @@ -554,32 +605,34 @@ static inline ALfloat CalcDensityGain(ALfloat a) * where a is the attenuation coefficient, and n is the sample. The area * under this decay curve can be calculated as: 1 / (1 - a). * - * Modifying the above equation to find the squared area under the curve + * 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 sqrtf(1.0f - (a * a)); + return sqrtf(1.0f - a*a); } -// Calculate the mixing matrix coefficients given a diffusion factor. -static inline ALvoid CalcMatrixCoeffs(ALfloat diffusion, ALfloat *x, ALfloat *y) +/* Calculate the scattering matrix coefficients given a diffusion factor. */ +static inline ALvoid CalcMatrixCoeffs(const ALfloat diffusion, ALfloat *x, ALfloat *y) { ALfloat n, t; - // The matrix is of order 4, so n is sqrt (4 - 1). + /* The matrix is of order 4, so n is sqrt(4 - 1). */ n = sqrtf(3.0f); t = diffusion * atanf(n); - // Calculate the first mixing matrix coefficient. + /* Calculate the first mixing matrix coefficient. */ *x = cosf(t); - // Calculate the second mixing matrix coefficient. + /* Calculate the second mixing matrix coefficient. */ *y = sinf(t) / n; } -// Calculate the limited HF ratio for use with the late reverb low-pass -// filters. -static ALfloat CalcLimitedHfRatio(ALfloat hfRatio, ALfloat airAbsorptionGainHF, ALfloat decayTime) +/* Calculate the limited HF ratio for use with the late reverb low-pass + * filters. + */ +static ALfloat CalcLimitedHfRatio(const ALfloat hfRatio, const ALfloat airAbsorptionGainHF, + const ALfloat decayTime, const ALfloat SpeedOfSound) { ALfloat limitRatio; @@ -588,420 +641,591 @@ static ALfloat CalcLimitedHfRatio(ALfloat hfRatio, ALfloat airAbsorptionGainHF, * equation, solve for HF ratio. The delay length is cancelled out of * the equation, so it can be calculated once for all lines. */ - limitRatio = 1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) * - SPEEDOFSOUNDMETRESPERSEC); - /* Using the limit calculated above, apply the upper bound to the HF - * ratio. Also need to limit the result to a minimum of 0.1, just like the - * HF ratio parameter. */ - return clampf(limitRatio, 0.1f, hfRatio); + limitRatio = 1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) * SpeedOfSound); + + /* Using the limit calculated above, apply the upper bound to the HF ratio. + */ + return minf(limitRatio, hfRatio); } -// Calculate the coefficient for a HF (and eventually LF) decay damping -// filter. -static inline ALfloat CalcDampingCoeff(ALfloat hfRatio, ALfloat length, ALfloat decayTime, ALfloat decayCoeff, ALfloat cw) +/* Calculates the first-order high-pass coefficients following the I3DL2 + * reference model. This is the transfer function: + * + * 1 - z^-1 + * H(z) = p ------------ + * 1 - p z^-1 + * + * And this is the I3DL2 coefficient calculation given gain (g) and reference + * angular frequency (w): + * + * g + * p = ------------------------------------------------------ + * g cos(w) + sqrt((cos(w) - 1) (g^2 cos(w) + g^2 - 2)) + * + * The coefficient is applied to the partial differential filter equation as: + * + * c_0 = p + * c_1 = -p + * c_2 = p + * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1) + * + */ +static inline void CalcHighpassCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3]) { - ALfloat coeff, g; + ALfloat g, g2, cw, p; - // Eventually this should boost the high frequencies when the ratio - // exceeds 1. - coeff = 0.0f; - if (hfRatio < 1.0f) + if(gain >= 1.0f) { - // Calculate the low-pass coefficient by dividing the HF decay - // coefficient by the full decay coefficient. - g = CalcDecayCoeff(length, decayTime * hfRatio) / decayCoeff; - - // Damping is done with a 1-pole filter, so g needs to be squared. - g *= g; - if(g < 0.9999f) /* 1-epsilon */ - { - /* Be careful with gains < 0.001, as that causes the coefficient - * head towards 1, which will flatten the signal. */ - g = maxf(g, 0.001f); - coeff = (1 - g*cw - sqrtf(2*g*(1-cw) - g*g*(1 - cw*cw))) / - (1 - g); - } - - // Very low decay times will produce minimal output, so apply an - // upper bound to the coefficient. - coeff = minf(coeff, 0.98f); + coeffs[0] = 1.0f; + coeffs[1] = 0.0f; + coeffs[2] = 0.0f; + return; } - return coeff; + + g = maxf(0.001f, gain); + g2 = g * g; + cw = cosf(w); + p = g / (g*cw + sqrtf((cw - 1.0f) * (g2*cw + g2 - 2.0f))); + + coeffs[0] = p; + coeffs[1] = -p; + coeffs[2] = p; } -// Update the EAX modulation index, range, and depth. Keep in mind that this -// kind of vibrato is additive and not multiplicative as one may expect. The -// downswing will sound stronger than the upswing. -static ALvoid UpdateModulator(ALfloat modTime, ALfloat modDepth, ALuint frequency, ALreverbState *State) +/* Calculates the first-order low-pass coefficients following the I3DL2 + * reference model. This is the transfer function: + * + * (1 - a) z^0 + * H(z) = ---------------- + * 1 z^0 - a z^-1 + * + * And this is the I3DL2 coefficient calculation given gain (g) and reference + * angular frequency (w): + * + * 1 - g^2 cos(w) - sqrt(2 g^2 (1 - cos(w)) - g^4 (1 - cos(w)^2)) + * a = ---------------------------------------------------------------- + * 1 - g^2 + * + * The coefficient is applied to the partial differential filter equation as: + * + * c_0 = 1 - a + * c_1 = 0 + * c_2 = a + * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1) + * + */ +static inline void CalcLowpassCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3]) { - ALuint range; + ALfloat g, g2, cw, a; - /* Modulation is calculated in two parts. - * - * The modulation time effects the sinus applied to the change in - * frequency. An index out of the current time range (both in samples) - * is incremented each sample. The range is bound to a reasonable - * minimum (1 sample) and when the timing changes, the index is rescaled - * to the new range (to keep the sinus consistent). - */ - range = maxu(fastf2u(modTime*frequency), 1); - State->Mod.Index = (ALuint)(State->Mod.Index * (ALuint64)range / - State->Mod.Range); - State->Mod.Range = range; + if(gain >= 1.0f) + { + coeffs[0] = 1.0f; + coeffs[1] = 0.0f; + coeffs[2] = 0.0f; + return; + } - /* The modulation depth effects the amount of frequency change over the - * range of the sinus. It needs to be scaled by the modulation time so - * that a given depth produces a consistent change in frequency over all - * ranges of time. Since the depth is applied to a sinus value, it needs - * to be halfed once for the sinus range and again for the sinus swing - * in time (half of it is spent decreasing the frequency, half is spent - * increasing it). - */ - State->Mod.Depth = modDepth * MODULATION_DEPTH_COEFF * modTime / 2.0f / - 2.0f * frequency; + /* Be careful with gains < 0.001, as that causes the coefficient + * to head towards 1, which will flatten the signal. */ + g = maxf(0.001f, gain); + g2 = g * g; + cw = cosf(w); + a = (1.0f - g2*cw - sqrtf((2.0f*g2*(1.0f - cw)) - g2*g2*(1.0f - cw*cw))) / + (1.0f - g2); + + coeffs[0] = 1.0f - a; + coeffs[1] = 0.0f; + coeffs[2] = a; } -// Update the offsets for the main effect delay line. -static ALvoid UpdateDelayLine(ALfloat earlyDelay, ALfloat lateDelay, ALfloat density, ALuint frequency, ALreverbState *State) +/* Calculates the first-order low-shelf coefficients. The shelf filters are + * used in place of low/high-pass filters to preserve the mid-band. This is + * the transfer function: + * + * a_0 + a_1 z^-1 + * H(z) = ---------------- + * 1 + b_1 z^-1 + * + * And these are the coefficient calculations given cut gain (g) and a center + * angular frequency (w): + * + * sin(0.5 (pi - w) - 0.25 pi) + * p = ----------------------------- + * sin(0.5 (pi - w) + 0.25 pi) + * + * g + 1 g + 1 + * a = ------- + sqrt((-------)^2 - 1) + * g - 1 g - 1 + * + * 1 + g + (1 - g) a + * b_0 = ------------------- + * 2 + * + * 1 - g + (1 + g) a + * b_1 = ------------------- + * 2 + * + * The coefficients are applied to the partial differential filter equation + * as: + * + * b_0 + p b_1 + * c_0 = ------------- + * 1 + p a + * + * -(b_1 + p b_0) + * c_1 = ---------------- + * 1 + p a + * + * p + a + * c_2 = --------- + * 1 + p a + * + * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1) + * + */ +static inline void CalcLowShelfCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3]) { - ALfloat length; + ALfloat g, rw, p, n; + ALfloat alpha, beta0, beta1; + + if(gain >= 1.0f) + { + coeffs[0] = 1.0f; + coeffs[1] = 0.0f; + coeffs[2] = 0.0f; + return; + } + + g = maxf(0.001f, gain); + rw = F_PI - w; + p = sinf(0.5f*rw - 0.25f*F_PI) / sinf(0.5f*rw + 0.25f*F_PI); + n = (g + 1.0f) / (g - 1.0f); + alpha = n + sqrtf(n*n - 1.0f); + beta0 = (1.0f + g + (1.0f - g)*alpha) / 2.0f; + beta1 = (1.0f - g + (1.0f + g)*alpha) / 2.0f; + + coeffs[0] = (beta0 + p*beta1) / (1.0f + p*alpha); + coeffs[1] = -(beta1 + p*beta0) / (1.0f + p*alpha); + coeffs[2] = (p + alpha) / (1.0f + p*alpha); +} + +/* Calculates the first-order high-shelf coefficients. The shelf filters are + * used in place of low/high-pass filters to preserve the mid-band. This is + * the transfer function: + * + * a_0 + a_1 z^-1 + * H(z) = ---------------- + * 1 + b_1 z^-1 + * + * And these are the coefficient calculations given cut gain (g) and a center + * angular frequency (w): + * + * sin(0.5 w - 0.25 pi) + * p = ---------------------- + * sin(0.5 w + 0.25 pi) + * + * g + 1 g + 1 + * a = ------- + sqrt((-------)^2 - 1) + * g - 1 g - 1 + * + * 1 + g + (1 - g) a + * b_0 = ------------------- + * 2 + * + * 1 - g + (1 + g) a + * b_1 = ------------------- + * 2 + * + * The coefficients are applied to the partial differential filter equation + * as: + * + * b_0 + p b_1 + * c_0 = ------------- + * 1 + p a + * + * b_1 + p b_0 + * c_1 = ------------- + * 1 + p a + * + * -(p + a) + * c_2 = ---------- + * 1 + p a + * + * y_i = c_0 x_i + c_1 x_(i-1) + c_2 y_(i-1) + * + */ +static inline void CalcHighShelfCoeffs(const ALfloat gain, const ALfloat w, ALfloat coeffs[3]) +{ + ALfloat g, p, n; + ALfloat alpha, beta0, beta1; + + if(gain >= 1.0f) + { + coeffs[0] = 1.0f; + coeffs[1] = 0.0f; + coeffs[2] = 0.0f; + return; + } + + g = maxf(0.001f, gain); + p = sinf(0.5f*w - 0.25f*F_PI) / sinf(0.5f*w + 0.25f*F_PI); + n = (g + 1.0f) / (g - 1.0f); + alpha = n + sqrtf(n*n - 1.0f); + beta0 = (1.0f + g + (1.0f - g)*alpha) / 2.0f; + beta1 = (1.0f - g + (1.0f + g)*alpha) / 2.0f; + + coeffs[0] = (beta0 + p*beta1) / (1.0f + p*alpha); + coeffs[1] = (beta1 + p*beta0) / (1.0f + p*alpha); + coeffs[2] = -(p + alpha) / (1.0f + p*alpha); +} + +/* Calculates the 3-band T60 damping coefficients for a particular delay line + * of specified length using a combination of two low/high-pass/shelf or + * pass-through filter sections (producing 3 coefficients each) given decay + * times for each band split at two (LF/HF) reference frequencies (w). + */ +static void CalcT60DampingCoeffs(const ALfloat length, const ALfloat lfDecayTime, + const ALfloat mfDecayTime, const ALfloat hfDecayTime, + const ALfloat lfW, const ALfloat hfW, ALfloat lfcoeffs[3], + ALfloat hfcoeffs[3]) +{ + ALfloat lfGain = CalcDecayCoeff(length, lfDecayTime); + ALfloat mfGain = CalcDecayCoeff(length, mfDecayTime); + ALfloat hfGain = CalcDecayCoeff(length, hfDecayTime); + + if(lfGain <= mfGain) + { + CalcHighpassCoeffs(lfGain / mfGain, lfW, lfcoeffs); + if(mfGain >= hfGain) + { + CalcLowpassCoeffs(hfGain / mfGain, hfW, hfcoeffs); + hfcoeffs[0] *= mfGain; hfcoeffs[1] *= mfGain; + } + else + { + CalcLowShelfCoeffs(mfGain / hfGain, hfW, hfcoeffs); + hfcoeffs[0] *= hfGain; hfcoeffs[1] *= hfGain; + } + } + else + { + CalcHighShelfCoeffs(mfGain / lfGain, lfW, lfcoeffs); + if(mfGain >= hfGain) + { + CalcLowpassCoeffs(hfGain / mfGain, hfW, hfcoeffs); + hfcoeffs[0] *= lfGain; hfcoeffs[1] *= lfGain; + } + else + { + ALfloat hg = mfGain / lfGain; + ALfloat lg = mfGain / hfGain; + ALfloat mg = maxf(lfGain, hfGain) / maxf(hg, lg); + + CalcLowShelfCoeffs(lg, hfW, hfcoeffs); + hfcoeffs[0] *= mg; hfcoeffs[1] *= mg; + } + } +} + +/* Update the offsets for the main effect delay line. */ +static ALvoid UpdateDelayLine(const ALfloat earlyDelay, const ALfloat lateDelay, const ALfloat density, const ALfloat decayTime, const ALuint frequency, ALreverbState *State) +{ + ALfloat multiplier, length; ALuint i; - /* The early reflections and late reverb inputs are decorrelated to provide - * time-varying reflections, smooth out the reverb tail, and reduce harsh - * echoes. The first tap occurs immediately, while the remaining taps are - * delayed by multiples of a fraction of the smallest cyclical delay time. - * - * offset[index] = (FRACTION (MULTIPLIER^(index-1))) smallest_delay - * - * for index = 1...max_lines - */ - State->EarlyDelayTap[0] = fastf2u(earlyDelay * frequency); - for(i = 1;i < 4;i++) - { - length = (DECO_FRACTION * powf(DECO_MULTIPLIER, (ALfloat)i-1.0f)) * - EARLY_LINE_LENGTH[0]; - State->EarlyDelayTap[i] = fastf2u(length * frequency) + State->EarlyDelayTap[0]; - } + multiplier = CalcDelayLengthMult(density); - State->LateDelayTap[0] = fastf2u((earlyDelay + lateDelay) * frequency); - for(i = 1;i < 4;i++) + /* 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(i = 0;i < NUM_LINES;i++) { - length = (DECO_FRACTION * powf(DECO_MULTIPLIER, (ALfloat)i-1.0f)) * - LATE_LINE_LENGTH[0] * (1.0f + (density * LATE_LINE_MULTIPLIER)); - State->LateDelayTap[i] = fastf2u(length * frequency) + State->LateDelayTap[0]; + length = earlyDelay + EARLY_TAP_LENGTHS[i]*multiplier; + State->EarlyDelayTap[i][1] = float2int(length * frequency); + + length = EARLY_TAP_LENGTHS[i]*multiplier; + State->EarlyDelayCoeff[i] = CalcDecayCoeff(length, decayTime); + + length = lateDelay + (LATE_LINE_LENGTHS[i] - LATE_LINE_LENGTHS[0])*0.25f*multiplier; + State->LateDelayTap[i][1] = State->LateFeedTap + float2int(length * frequency); } } -// Update the early reflections mix and line coefficients. -static ALvoid UpdateEarlyLines(ALfloat lateDelay, ALreverbState *State) +/* Update the early reflection line lengths and gain coefficients. */ +static ALvoid UpdateEarlyLines(const ALfloat density, const ALfloat decayTime, const ALuint frequency, EarlyReflections *Early) { - ALuint index; + ALfloat multiplier, length; + ALsizei i; - // Calculate the gain (coefficient) for each early delay line using the - // late delay time. This expands the early reflections to the start of - // the late reverb. - for(index = 0;index < 4;index++) - State->Early.Coeff[index] = CalcDecayCoeff(EARLY_LINE_LENGTH[index], - lateDelay); + multiplier = CalcDelayLengthMult(density); + + for(i = 0;i < NUM_LINES;i++) + { + /* Calculate the length (in seconds) of each all-pass line. */ + length = EARLY_ALLPASS_LENGTHS[i] * multiplier; + + /* Calculate the delay offset for each all-pass line. */ + Early->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. */ + Early->Offset[i][1] = float2int(length * frequency); + + /* Calculate the gain (coefficient) for each line. */ + Early->Coeff[i] = CalcDecayCoeff(length, decayTime); + } } -// Update the late reverb mix, line lengths, and line coefficients. -static ALvoid UpdateLateLines(ALfloat xMix, ALfloat density, ALfloat decayTime, ALfloat diffusion, ALfloat echoDepth, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALreverbState *State) +/* Update the late reverb line lengths and T60 coefficients. */ +static ALvoid UpdateLateLines(const ALfloat density, const ALfloat diffusion, const ALfloat lfDecayTime, const ALfloat mfDecayTime, const ALfloat hfDecayTime, const ALfloat lfW, const ALfloat hfW, const ALfloat echoTime, const ALfloat echoDepth, const ALuint frequency, LateReverb *Late) { - ALfloat length; - ALuint index; - - /* Calculate the late reverb gain. Since the output is tapped prior to the - * application of the next delay line coefficients, this gain needs to be - * attenuated by the 'x' mixing matrix coefficient as well. Also attenuate - * the late reverb when echo depth is high and diffusion is low, so the - * echo is slightly stronger than the decorrelated echos in the reverb - * tail. - */ - State->Late.Gain = xMix * (1.0f - (echoDepth*0.5f*(1.0f - diffusion))); + ALfloat multiplier, length, bandWeights[3]; + ALsizei i; /* To compensate for changes in modal density and decay time of the late * reverb signal, the input is attenuated based on the maximal energy of * the outgoing signal. This approximation is used to keep the apparent * energy of the signal equal for all ranges of density and decay time. * - * The average length of the cyclcical delay lines is used to calculate - * the attenuation coefficient. + * The average length of the delay lines is used to calculate the + * attenuation coefficient. */ - length = (LATE_LINE_LENGTH[0] + LATE_LINE_LENGTH[1] + - LATE_LINE_LENGTH[2] + LATE_LINE_LENGTH[3]) / 4.0f; - length *= 1.0f + (density * LATE_LINE_MULTIPLIER); - /* To account for each channel being a discrete input, also multiply by - * sqrt(num_channels). + multiplier = CalcDelayLengthMult(density); + length = (LATE_LINE_LENGTHS[0] + LATE_LINE_LENGTHS[1] + + LATE_LINE_LENGTHS[2] + LATE_LINE_LENGTHS[3]) / 4.0f * multiplier; + /* Include the echo transformation (see below). */ + length = lerp(length, echoTime, echoDepth); + length += (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] + + LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f * 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. */ - State->Late.DensityGain = 2.0f * CalcDensityGain( - CalcDecayCoeff(length, decayTime) + bandWeights[0] = lfW; + bandWeights[1] = hfW - lfW; + bandWeights[2] = F_TAU - hfW; + Late->DensityGain = CalcDensityGain( + CalcDecayCoeff(length, (bandWeights[0]*lfDecayTime + bandWeights[1]*mfDecayTime + + bandWeights[2]*hfDecayTime) / F_TAU) ); - // Calculate the all-pass feed-back and feed-forward coefficient. - State->Late.ApFeedCoeff = 0.5f * powf(diffusion, 2.0f); - - for(index = 0;index < 4;index++) + for(i = 0;i < NUM_LINES;i++) { - // Calculate the gain (coefficient) for each all-pass line. - State->Late.Ap[index].Coeff = CalcDecayCoeff( - ALLPASS_LINE_LENGTH[index], decayTime - ); + /* Calculate the length (in seconds) of each all-pass line. */ + length = LATE_ALLPASS_LENGTHS[i] * multiplier; - // Calculate the length (in seconds) of each cyclical delay line. - length = LATE_LINE_LENGTH[index] * - (1.0f + (density * LATE_LINE_MULTIPLIER)); + /* Calculate the delay offset for each all-pass line. */ + Late->VecAp.Offset[i][1] = float2int(length * frequency); - // Calculate the delay offset for each cyclical delay line. - State->Late.Offset[index] = fastf2u(length * frequency); + /* Calculate the length (in seconds) of each delay line. This also + * applies the echo transformation. As the EAX echo depth approaches + * 1, the line lengths approach a length equal to the echoTime. This + * helps to produce distinct echoes along the tail. + */ + length = lerp(LATE_LINE_LENGTHS[i] * multiplier, echoTime, echoDepth); - // Calculate the gain (coefficient) for each cyclical line. - State->Late.Coeff[index] = CalcDecayCoeff(length, decayTime); + /* Calculate the delay offset for each delay line. */ + Late->Offset[i][1] = float2int(length*frequency + 0.5f); - // Calculate the damping coefficient for each low-pass filter. - State->Late.Lp[index].Coeff = CalcDampingCoeff( - hfRatio, length, decayTime, State->Late.Coeff[index], cw - ); + /* 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_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] + + LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f, + diffusion) * multiplier; - // Attenuate the cyclical line coefficients by the mixing coefficient - // (x). - State->Late.Coeff[index] *= xMix; + /* Calculate the T60 damping coefficients for each line. */ + CalcT60DampingCoeffs(length, lfDecayTime, mfDecayTime, hfDecayTime, + lfW, hfW, Late->T60[i].LFCoeffs, + Late->T60[i].HFCoeffs); } } -// Update the echo gain, line offset, line coefficients, and mixing -// coefficients. -static ALvoid UpdateEchoLine(ALfloat echoTime, ALfloat decayTime, ALfloat diffusion, ALfloat echoDepth, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALreverbState *State) -{ - // Update the offset and coefficient for the echo delay line. - State->Echo.Offset = fastf2u(echoTime * frequency); - - // Calculate the decay coefficient for the echo line. - State->Echo.Coeff = CalcDecayCoeff(echoTime, decayTime); - - // Calculate the energy-based attenuation coefficient for the echo delay - // line. - State->Echo.DensityGain = CalcDensityGain(State->Echo.Coeff); - - // Calculate the echo all-pass feed coefficient. - State->Echo.ApFeedCoeff = 0.5f * powf(diffusion, 2.0f); - - // Calculate the echo all-pass attenuation coefficient. - State->Echo.ApCoeff = CalcDecayCoeff(ECHO_ALLPASS_LENGTH, decayTime); - - // Calculate the damping coefficient for each low-pass filter. - State->Echo.LpCoeff = CalcDampingCoeff(hfRatio, echoTime, decayTime, - State->Echo.Coeff, cw); - - /* Calculate the echo mixing coefficient. This is applied to the output mix - * only, not the feedback. - */ - State->Echo.MixCoeff = echoDepth; -} - -/* Creates a transform matrix given a reverb vector. This works by creating a - * Z-focus transform, then a rotate transform around X, then Y, to place the - * focal point in the direction of the vector, using the vector length as a - * focus strength. - * - * This isn't technically correct since the vector is supposed to define the - * aperture and not rotate the perceived soundfield, but in practice it's - * probably good enough. +/* 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. */ static aluMatrixf GetTransformFromVector(const ALfloat *vec) { - aluMatrixf zfocus, xrot, yrot; - aluMatrixf tmp1, tmp2; - ALfloat length; - ALfloat sa, a; + const ALfloat sqrt_3 = 1.732050808f; + aluMatrixf focus; + ALfloat norm[3]; + ALfloat mag; - length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]); - - /* Define a Z-focus (X in Ambisonics) transform, given the panning vector - * length. + /* 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. */ - sa = sinf(minf(length, 1.0f) * (F_PI/4.0f)); - aluMatrixfSet(&zfocus, - 1.0f/(1.0f+sa), 0.0f, 0.0f, (sa/(1.0f+sa))/1.732050808f, - 0.0f, sqrtf((1.0f-sa)/(1.0f+sa)), 0.0f, 0.0f, - 0.0f, 0.0f, sqrtf((1.0f-sa)/(1.0f+sa)), 0.0f, - (sa/(1.0f+sa))*1.732050808f, 0.0f, 0.0f, 1.0f/(1.0f+sa) + mag = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]); + if(mag > 1.0f) + { + norm[0] = vec[0] / mag * -sqrt_3; + norm[1] = vec[1] / mag * sqrt_3; + norm[2] = vec[2] / mag * sqrt_3; + 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] * -sqrt_3; + norm[1] = vec[1] * sqrt_3; + norm[2] = vec[2] * sqrt_3; + } + + aluMatrixfSet(&focus, + 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 ); - /* Define rotation around X (Y in Ambisonics) */ - a = atan2f(vec[1], sqrtf(vec[0]*vec[0] + vec[2]*vec[2])); - aluMatrixfSet(&xrot, - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, cosf(a), sinf(a), - 0.0f, 0.0f, -sinf(a), cosf(a) - ); - - /* Define rotation around Y (Z in Ambisonics). NOTE: EFX's reverb vectors - * use a right-handled coordinate system, compared to the rest of OpenAL - * which uses left-handed. This is fixed by negating Z, however it would - * need to also be negated to get a proper Ambisonics angle, thus - * cancelling it out. - */ - a = atan2f(-vec[0], vec[2]); - aluMatrixfSet(&yrot, - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, cosf(a), 0.0f, sinf(a), - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, -sinf(a), 0.0f, cosf(a) - ); - -#define MATRIX_MULT(_res, _m1, _m2) do { \ - int row, col; \ - for(col = 0;col < 4;col++) \ - { \ - for(row = 0;row < 4;row++) \ - _res.m[row][col] = _m1.m[row][0]*_m2.m[0][col] + _m1.m[row][1]*_m2.m[1][col] + \ - _m1.m[row][2]*_m2.m[2][col] + _m1.m[row][3]*_m2.m[3][col]; \ - } \ -} while(0) - /* Define a matrix that first focuses on Z, then rotates around X then Y to - * focus the output in the direction of the vector. - */ - MATRIX_MULT(tmp1, xrot, zfocus); - MATRIX_MULT(tmp2, yrot, tmp1); -#undef MATRIX_MULT - - return tmp2; + return focus; } -// Update the early and late 3D panning gains. -static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, ALfloat Gain, ALfloat EarlyGain, ALfloat LateGain, ALreverbState *State) +/* Update the early and late 3D panning gains. */ +static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, const ALfloat gain, const ALfloat earlyGain, const ALfloat lateGain, ALreverbState *State) { - /* Converts early reflections A-Format to B-Format (transposed). */ - static const aluMatrixf EarlyA2B = {{ - { 0.8660254038f, 0.8660254038f, 0.8660254038f, 0.8660254038f }, - { 0.8660254038f, 0.8660254038f, -0.8660254038f, -0.8660254038f }, - { 0.8660254038f, -0.8660254038f, 0.8660254038f, -0.8660254038f }, - { 0.8660254038f, -0.8660254038f, -0.8660254038f, 0.8660254038f } - }}; - /* Converts late reverb A-Format to B-Format (transposed). */ - static const aluMatrixf LateA2B = {{ - { 0.8660254038f, -0.8660254038f, 0.8660254038f, 0.8660254038f }, - { 0.8660254038f, -0.8660254038f, -0.8660254038f, -0.8660254038f }, - { 0.8660254038f, 0.8660254038f, 0.8660254038f, -0.8660254038f }, - { 0.8660254038f, 0.8660254038f, -0.8660254038f, 0.8660254038f } -/* { 0.8660254038f, 1.2247448714f, 0.0f, 0.8660254038f }, - { 0.8660254038f, 0.0f, -1.2247448714f, -0.8660254038f }, - { 0.8660254038f, 0.0f, 1.2247448714f, -0.8660254038f }, - { 0.8660254038f, -1.2247448714f, 0.0f, 0.8660254038f }*/ - }}; aluMatrixf transform, rot; - ALuint i; + ALsizei i; STATIC_CAST(ALeffectState,State)->OutBuffer = Device->FOAOut.Buffer; STATIC_CAST(ALeffectState,State)->OutChannels = Device->FOAOut.NumChannels; - /* Note: Both _m2 and _res are transposed. */ -#define MATRIX_MULT(_res, _m1, _m2) do { \ - int row, col; \ - for(col = 0;col < 4;col++) \ - { \ - for(row = 0;row < 4;row++) \ - _res.m[col][row] = _m1.m[row][0]*_m2.m[col][0] + _m1.m[row][1]*_m2.m[col][1] + \ - _m1.m[row][2]*_m2.m[col][2] + _m1.m[row][3]*_m2.m[col][3]; \ - } \ + /* Note: _res is transposed. */ +#define MATRIX_MULT(_res, _m1, _m2) do { \ + int row, col; \ + for(col = 0;col < 4;col++) \ + { \ + for(row = 0;row < 4;row++) \ + _res.m[col][row] = _m1.m[row][0]*_m2.m[0][col] + _m1.m[row][1]*_m2.m[1][col] + \ + _m1.m[row][2]*_m2.m[2][col] + _m1.m[row][3]*_m2.m[3][col]; \ + } \ } while(0) - /* Create a matrix that first converts A-Format to B-Format, then rotates - * the B-Format soundfield according to the panning vector. + /* Create a matrix that first converts A-Format to B-Format, then + * transforms the B-Format signal according to the panning vector. */ rot = GetTransformFromVector(ReflectionsPan); - MATRIX_MULT(transform, rot, EarlyA2B); + MATRIX_MULT(transform, rot, A2B); memset(&State->Early.PanGain, 0, sizeof(State->Early.PanGain)); for(i = 0;i < MAX_EFFECT_CHANNELS;i++) - ComputeFirstOrderGains(Device->FOAOut, transform.m[i], Gain*EarlyGain, State->Early.PanGain[i]); + ComputeFirstOrderGains(&Device->FOAOut, transform.m[i], gain*earlyGain, + State->Early.PanGain[i]); rot = GetTransformFromVector(LateReverbPan); - MATRIX_MULT(transform, rot, LateA2B); + MATRIX_MULT(transform, rot, A2B); memset(&State->Late.PanGain, 0, sizeof(State->Late.PanGain)); for(i = 0;i < MAX_EFFECT_CHANNELS;i++) - ComputeFirstOrderGains(Device->FOAOut, transform.m[i], Gain*LateGain, State->Late.PanGain[i]); + ComputeFirstOrderGains(&Device->FOAOut, transform.m[i], gain*lateGain, + State->Late.PanGain[i]); #undef MATRIX_MULT } -static ALvoid ALreverbState_update(ALreverbState *State, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props) +static ALvoid ALreverbState_update(ALreverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props) { + const ALCdevice *Device = Context->Device; + const ALlistener *Listener = Context->Listener; ALuint frequency = Device->Frequency; - ALfloat lfscale, hfscale, hfRatio; + ALfloat lf0norm, hf0norm, hfRatio; + ALfloat lfDecayTime, hfDecayTime; ALfloat gain, gainlf, gainhf; - ALfloat cw, x, y; - ALuint i; + ALsizei i; - if(Slot->Params.EffectType == AL_EFFECT_EAXREVERB && !EmulateEAXReverb) - State->IsEax = AL_TRUE; - else if(Slot->Params.EffectType == AL_EFFECT_REVERB || EmulateEAXReverb) - State->IsEax = AL_FALSE; - - // Calculate the master filters - hfscale = props->Reverb.HFReference / frequency; - gainhf = maxf(props->Reverb.GainHF, 0.0001f); - ALfilterState_setParams(&State->Filter[0].Lp, ALfilterType_HighShelf, - gainhf, hfscale, calc_rcpQ_from_slope(gainhf, 0.75f)); - lfscale = props->Reverb.LFReference / frequency; - gainlf = maxf(props->Reverb.GainLF, 0.0001f); - ALfilterState_setParams(&State->Filter[0].Hp, ALfilterType_LowShelf, - gainlf, lfscale, calc_rcpQ_from_slope(gainlf, 0.75f)); - for(i = 1;i < 4;i++) + /* Calculate the master filters */ + hf0norm = props->Reverb.HFReference / frequency; + /* Restrict the filter gains from going below -60dB to keep the filter from + * killing most of the signal. + */ + gainhf = maxf(props->Reverb.GainHF, 0.001f); + BiquadFilter_setParams(&State->Filter[0].Lp, BiquadType_HighShelf, gainhf, hf0norm, + calc_rcpQ_from_slope(gainhf, 1.0f)); + lf0norm = props->Reverb.LFReference / frequency; + gainlf = maxf(props->Reverb.GainLF, 0.001f); + BiquadFilter_setParams(&State->Filter[0].Hp, BiquadType_LowShelf, gainlf, lf0norm, + calc_rcpQ_from_slope(gainlf, 1.0f)); + for(i = 1;i < NUM_LINES;i++) { - State->Filter[i].Lp.a1 = State->Filter[0].Lp.a1; - State->Filter[i].Lp.a2 = State->Filter[0].Lp.a2; - State->Filter[i].Lp.b0 = State->Filter[0].Lp.b0; - State->Filter[i].Lp.b1 = State->Filter[0].Lp.b1; - State->Filter[i].Lp.b2 = State->Filter[0].Lp.b2; - - State->Filter[i].Hp.a1 = State->Filter[0].Hp.a1; - State->Filter[i].Hp.a2 = State->Filter[0].Hp.a2; - State->Filter[i].Hp.b0 = State->Filter[0].Hp.b0; - State->Filter[i].Hp.b1 = State->Filter[0].Hp.b1; - State->Filter[i].Hp.b2 = State->Filter[0].Hp.b2; + BiquadFilter_copyParams(&State->Filter[i].Lp, &State->Filter[0].Lp); + BiquadFilter_copyParams(&State->Filter[i].Hp, &State->Filter[0].Hp); } - // Update the modulator line. - UpdateModulator(props->Reverb.ModulationTime, props->Reverb.ModulationDepth, - frequency, State); - - // Update the main effect delay. + /* Update the main effect delay and associated taps. */ UpdateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay, - props->Reverb.Density, frequency, State); + props->Reverb.Density, props->Reverb.DecayTime, frequency, + State); - // Update the early lines. - UpdateEarlyLines(props->Reverb.LateReverbDelay, State); + /* Calculate the all-pass feed-back/forward coefficient. */ + State->ApFeedCoeff = sqrtf(0.5f) * powf(props->Reverb.Diffusion, 2.0f); - // Get the mixing matrix coefficients (x and y). - CalcMatrixCoeffs(props->Reverb.Diffusion, &x, &y); - // Then divide x into y to simplify the matrix calculation. - State->Late.MixCoeff = y / x; + /* Update the early lines. */ + UpdateEarlyLines(props->Reverb.Density, props->Reverb.DecayTime, + frequency, &State->Early); - // If the HF limit parameter is flagged, calculate an appropriate limit - // based on the air absorption parameter. + /* Get the mixing matrix coefficients. */ + CalcMatrixCoeffs(props->Reverb.Diffusion, &State->MixX, &State->MixY); + + /* If the HF limit parameter is flagged, calculate an appropriate limit + * based on the air absorption parameter. + */ hfRatio = props->Reverb.DecayHFRatio; if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f) hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF, - props->Reverb.DecayTime); + props->Reverb.DecayTime, Listener->Params.ReverbSpeedOfSound + ); - cw = cosf(F_TAU * hfscale); - // Update the late lines. - UpdateLateLines(x, props->Reverb.Density, props->Reverb.DecayTime, - props->Reverb.Diffusion, props->Reverb.EchoDepth, - hfRatio, cw, frequency, State); + /* Calculate the LF/HF decay times. */ + lfDecayTime = clampf(props->Reverb.DecayTime * props->Reverb.DecayLFRatio, + AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME); + hfDecayTime = clampf(props->Reverb.DecayTime * hfRatio, + AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME); - // Update the echo line. - UpdateEchoLine(props->Reverb.EchoTime, props->Reverb.DecayTime, - props->Reverb.Diffusion, props->Reverb.EchoDepth, - hfRatio, cw, frequency, State); + /* Update the late lines. */ + UpdateLateLines(props->Reverb.Density, props->Reverb.Diffusion, + lfDecayTime, props->Reverb.DecayTime, hfDecayTime, + F_TAU * lf0norm, F_TAU * hf0norm, + props->Reverb.EchoTime, props->Reverb.EchoDepth, + frequency, &State->Late); + /* Update early and late 3D panning. */ gain = props->Reverb.Gain * Slot->Params.Gain * ReverbBoost; - // Update early and late 3D panning. Update3DPanning(Device, props->Reverb.ReflectionsPan, props->Reverb.LateReverbPan, gain, props->Reverb.ReflectionsGain, props->Reverb.LateReverbGain, State); + + /* Determine if delay-line cross-fading is required. */ + for(i = 0;i < NUM_LINES;i++) + { + if(State->EarlyDelayTap[i][1] != State->EarlyDelayTap[i][0] || + State->Early.VecAp.Offset[i][1] != State->Early.VecAp.Offset[i][0] || + State->Early.Offset[i][1] != State->Early.Offset[i][0] || + State->LateDelayTap[i][1] != State->LateDelayTap[i][0] || + State->Late.VecAp.Offset[i][1] != State->Late.VecAp.Offset[i][0] || + State->Late.Offset[i][1] != State->Late.Offset[i][0]) + { + State->FadeCount = 0; + break; + } + } } @@ -1009,415 +1233,373 @@ static ALvoid ALreverbState_update(ALreverbState *State, const ALCdevice *Device * Effect Processing * **************************************/ -// Basic delay line input/output routines. -static inline ALfloat DelayLineOut(DelayLine *Delay, ALuint offset) +/* Basic delay line input/output routines. */ +static inline ALfloat DelayLineOut(const DelayLineI *Delay, const ALsizei offset, const ALsizei c) { - return Delay->Line[offset&Delay->Mask]; + return Delay->Line[offset&Delay->Mask][c]; } -static inline ALvoid DelayLineIn(DelayLine *Delay, ALuint offset, ALfloat in) -{ - Delay->Line[offset&Delay->Mask] = in; -} - -static inline ALfloat DelayLineInOut(DelayLine *Delay, ALuint offset, ALuint outoffset, ALfloat in) -{ - Delay->Line[offset&Delay->Mask] = in; - return Delay->Line[(offset-outoffset)&Delay->Mask]; -} - -static void CalcModulationDelays(ALreverbState *State, ALfloat *restrict delays, ALuint todo) -{ - ALfloat sinus, range; - ALuint index, i; - - index = State->Mod.Index; - range = State->Mod.Filter; - for(i = 0;i < todo;i++) - { - /* Calculate the sinus rythm (dependent on modulation time and the - * sampling rate). The center of the sinus is moved to reduce the - * delay of the effect when the time or depth are low. - */ - sinus = 1.0f - cosf(F_TAU * index / State->Mod.Range); - - /* Step the modulation index forward, keeping it bound to its range. */ - index = (index+1) % State->Mod.Range; - - /* The depth determines the range over which to read the input samples - * from, so it must be filtered to reduce the distortion caused by even - * small parameter changes. - */ - range = lerp(range, State->Mod.Depth, State->Mod.Coeff); - - /* Calculate the read offset with fraction. */ - delays[i] = range*sinus; - } - State->Mod.Index = index; - State->Mod.Filter = range; -} - -// Given some input samples, this function produces modulation for the late -// reverb. -static void EAXModulation(DelayLine *ModDelay, ALuint offset, const ALfloat *restrict delays, ALfloat*restrict dst, const ALfloat*restrict src, ALuint todo) -{ - ALfloat frac, fdelay; - ALfloat out0, out1; - ALuint delay, i; - - for(i = 0;i < todo;i++) - { - /* Separate the integer offset and fraction between it and the next - * sample. - */ - frac = modff(delays[i], &fdelay); - delay = fastf2u(fdelay); - - /* Add the incoming sample to the delay line, and get the two samples - * crossed by the offset delay. - */ - out0 = DelayLineInOut(ModDelay, offset, delay, src[i]); - out1 = DelayLineOut(ModDelay, offset - delay - 1); - offset++; - - /* The output is obtained by linearly interpolating the two samples - * that were acquired above. - */ - dst[i] = lerp(out0, out1, frac); - } -} - -/* Given some input samples from the main delay line, this function produces - * four-channel outputs for the early reflections. +/* Cross-faded delay line output routine. Instead of interpolating the + * offsets, this interpolates (cross-fades) the outputs at each offset. */ -static ALvoid EarlyReflection(ALreverbState *State, ALuint todo, ALfloat (*restrict out)[MAX_UPDATE_SAMPLES]) +static inline ALfloat FadedDelayLineOut(const DelayLineI *Delay, const ALsizei off0, + const ALsizei off1, const ALsizei c, const ALfloat mu) { - ALfloat d[4], v, f[4]; - ALuint i; + return Delay->Line[off0&Delay->Mask][c]*(1.0f-mu) + + Delay->Line[off1&Delay->Mask][c]*( mu); +} +#define UnfadedDelayLineOut(d, o0, o1, c, mu) DelayLineOut(d, o0, c) - for(i = 0;i < todo;i++) - { - ALuint offset = State->Offset+i; - - /* Obtain the first reflection samples from the main delay line. */ - f[0] = DelayLineOut(&State->Delay, (offset-State->EarlyDelayTap[0])*4 + 0); - f[1] = DelayLineOut(&State->Delay, (offset-State->EarlyDelayTap[1])*4 + 1); - f[2] = DelayLineOut(&State->Delay, (offset-State->EarlyDelayTap[2])*4 + 2); - f[3] = DelayLineOut(&State->Delay, (offset-State->EarlyDelayTap[3])*4 + 3); - - /* The following uses a lossless scattering junction from waveguide - * theory. It actually amounts to a householder mixing matrix, which - * will produce a maximally diffuse response, and means this can - * probably be considered a simple feed-back delay network (FDN). - * N - * --- - * \ - * v = 2/N / d_i - * --- - * i=1 - */ - v = (f[0] + f[1] + f[2] + f[3]) * 0.5f; - - /* Calculate the feed values for the early delay lines. */ - d[0] = v - f[0]; - d[1] = v - f[1]; - d[2] = v - f[2]; - d[3] = v - f[3]; - - /* Feed the early delay lines, and load the delayed results. */ - d[0] = DelayLineInOut(&State->Early.Delay[0], offset, State->Early.Offset[0], d[0]); - d[1] = DelayLineInOut(&State->Early.Delay[1], offset, State->Early.Offset[1], d[1]); - d[2] = DelayLineInOut(&State->Early.Delay[2], offset, State->Early.Offset[2], d[2]); - d[3] = DelayLineInOut(&State->Early.Delay[3], offset, State->Early.Offset[3], d[3]); - - /* Output the initial reflection taps and the results of the delayed - * and decayed junction for all four channels. - */ - out[0][i] = f[0] + d[0]*State->Early.Coeff[0]; - out[1][i] = f[1] + d[1]*State->Early.Coeff[1]; - out[2][i] = f[2] + d[2]*State->Early.Coeff[2]; - out[3][i] = f[3] + d[3]*State->Early.Coeff[3]; - } +static inline ALvoid DelayLineIn(DelayLineI *Delay, ALsizei offset, const ALsizei c, + const ALfloat *restrict in, ALsizei count) +{ + ALsizei i; + for(i = 0;i < count;i++) + Delay->Line[(offset++)&Delay->Mask][c] = *(in++); } -// Basic attenuated all-pass input/output routine. -static inline ALfloat AllpassInOut(DelayLine *Delay, ALuint outOffset, ALuint inOffset, ALfloat in, ALfloat feedCoeff, ALfloat coeff) +static inline ALvoid DelayLineIn4(DelayLineI *Delay, ALsizei offset, const ALfloat in[NUM_LINES]) { - ALfloat out, feed; - - out = DelayLineOut(Delay, outOffset); - feed = feedCoeff * in; - DelayLineIn(Delay, inOffset, (feedCoeff * (out - feed)) + in); - - // The time-based attenuation is only applied to the delay output to - // keep it from affecting the feed-back path (which is already controlled - // by the all-pass feed coefficient). - return (coeff * out) - feed; + ALsizei i; + offset &= Delay->Mask; + for(i = 0;i < NUM_LINES;i++) + Delay->Line[offset][i] = in[i]; } -// All-pass input/output routine for late reverb. -static inline ALfloat LateAllPassInOut(ALreverbState *State, ALuint offset, ALuint index, ALfloat in) +static inline ALvoid DelayLineIn4Rev(DelayLineI *Delay, ALsizei offset, const ALfloat in[NUM_LINES]) { - return AllpassInOut(&State->Late.Ap[index].Delay, - offset - State->Late.Ap[index].Offset, - offset, in, State->Late.ApFeedCoeff, - State->Late.Ap[index].Coeff); + ALsizei i; + offset &= Delay->Mask; + for(i = 0;i < NUM_LINES;i++) + Delay->Line[offset][i] = in[NUM_LINES-1-i]; } -// Low-pass filter input/output routine for late reverb. -static inline ALfloat LateLowPassInOut(ALreverbState *State, ALuint index, ALfloat in) +/* 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. + */ +static inline void VectorPartialScatter(ALfloat *restrict out, const ALfloat *restrict in, + const ALfloat xCoeff, const ALfloat yCoeff) { - in = lerp(in, State->Late.Lp[index].Sample, State->Late.Lp[index].Coeff); - State->Late.Lp[index].Sample = in; - return in; + 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] ); } -// Given four decorrelated input samples, this function produces four-channel -// output for the late reverb. -static ALvoid LateReverb(ALreverbState *State, ALuint todo, ALfloat (*restrict out)[MAX_UPDATE_SAMPLES]) +/* Same as above, but reverses the input. */ +static inline void VectorPartialScatterRev(ALfloat *restrict out, const ALfloat *restrict in, + const ALfloat xCoeff, const ALfloat yCoeff) { - ALfloat d[4], f[4]; - ALuint offset; - ALuint base, i; - - offset = State->Offset; - for(base = 0;base < todo;) - { - ALfloat tmp[MAX_UPDATE_SAMPLES/4][4]; - ALuint tmp_todo = minu(todo, MAX_UPDATE_SAMPLES/4); - - for(i = 0;i < tmp_todo;i++) - { - /* Obtain four decorrelated input samples. */ - f[0] = DelayLineOut(&State->Delay, (offset-State->LateDelayTap[0])*4 + 0) * State->Late.DensityGain; - f[1] = DelayLineOut(&State->Delay, (offset-State->LateDelayTap[1])*4 + 1) * State->Late.DensityGain; - f[2] = DelayLineOut(&State->Delay, (offset-State->LateDelayTap[2])*4 + 2) * State->Late.DensityGain; - f[3] = DelayLineOut(&State->Delay, (offset-State->LateDelayTap[3])*4 + 3) * State->Late.DensityGain; - - /* Add the decayed results of the cyclical delay lines, then pass - * the results through the low-pass filters. - */ - f[0] += DelayLineOut(&State->Late.Delay[0], offset-State->Late.Offset[0]) * State->Late.Coeff[0]; - f[1] += DelayLineOut(&State->Late.Delay[1], offset-State->Late.Offset[1]) * State->Late.Coeff[1]; - f[2] += DelayLineOut(&State->Late.Delay[2], offset-State->Late.Offset[2]) * State->Late.Coeff[2]; - f[3] += DelayLineOut(&State->Late.Delay[3], offset-State->Late.Offset[3]) * State->Late.Coeff[3]; - - /* This is where the feed-back cycles from line 0 to 3 to 1 to 2 - * and back to 0. - */ - d[0] = LateLowPassInOut(State, 2, f[2]); - d[1] = LateLowPassInOut(State, 3, f[3]); - d[2] = LateLowPassInOut(State, 1, f[1]); - d[3] = LateLowPassInOut(State, 0, f[0]); - - /* To help increase diffusion, run each line through an all-pass - * filter. When there is no diffusion, the shortest all-pass filter - * will feed the shortest delay line. - */ - d[0] = LateAllPassInOut(State, offset, 0, d[0]); - d[1] = LateAllPassInOut(State, offset, 1, d[1]); - d[2] = LateAllPassInOut(State, offset, 2, d[2]); - d[3] = LateAllPassInOut(State, offset, 3, d[3]); - - /* Late reverb is done with a modified feed-back delay network (FDN) - * topology. Four input lines are each fed through their own all-pass - * filter and then into the mixing matrix. The four outputs of the - * mixing matrix are then cycled back to the inputs. Each output feeds - * a different input to form a circlular feed cycle. - * - * The mixing matrix used is a 4D skew-symmetric rotation matrix - * derived using a single unitary rotational parameter: - * - * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2 - * [ -a, d, c, -b ] - * [ -b, -c, d, a ] - * [ -c, b, -a, d ] - * - * The rotation is constructed from the effect's diffusion parameter, - * yielding: 1 = x^2 + 3 y^2; where a, b, and c are the coefficient y - * with differing signs, and d is the coefficient x. The matrix is - * thus: - * - * [ x, y, -y, y ] n = sqrt(matrix_order - 1) - * [ -y, x, y, y ] t = diffusion_parameter * atan(n) - * [ y, -y, x, y ] x = cos(t) - * [ -y, -y, -y, x ] y = sin(t) / n - * - * To reduce the number of multiplies, the x coefficient is applied - * with the cyclical delay line coefficients. Thus only the y - * coefficient is applied when mixing, and is modified to be: y / x. - */ - f[0] = d[0] + (State->Late.MixCoeff * ( d[1] + -d[2] + d[3])); - f[1] = d[1] + (State->Late.MixCoeff * (-d[0] + d[2] + d[3])); - f[2] = d[2] + (State->Late.MixCoeff * ( d[0] + -d[1] + d[3])); - f[3] = d[3] + (State->Late.MixCoeff * (-d[0] + -d[1] + -d[2] )); - - /* Re-feed the cyclical delay lines. */ - DelayLineIn(&State->Late.Delay[0], offset, f[0]); - DelayLineIn(&State->Late.Delay[1], offset, f[1]); - DelayLineIn(&State->Late.Delay[2], offset, f[2]); - DelayLineIn(&State->Late.Delay[3], offset, f[3]); - offset++; - - /* Output the results of the matrix for all four channels, - * attenuated by the late reverb gain (which is attenuated by the - * 'x' mix coefficient). - */ - tmp[i][0] = State->Late.Gain * f[0]; - tmp[i][1] = State->Late.Gain * f[1]; - tmp[i][2] = State->Late.Gain * f[2]; - tmp[i][3] = State->Late.Gain * f[3]; - } - - /* Deinterlace to output */ - for(i = 0;i < tmp_todo;i++) out[0][base+i] = tmp[i][0]; - for(i = 0;i < tmp_todo;i++) out[1][base+i] = tmp[i][1]; - for(i = 0;i < tmp_todo;i++) out[2][base+i] = tmp[i][2]; - for(i = 0;i < tmp_todo;i++) out[3][base+i] = tmp[i][3]; - - base += tmp_todo; - } + out[0] = xCoeff*in[3] + yCoeff*(in[0] + -in[1] + in[2] ); + out[1] = xCoeff*in[2] + yCoeff*(in[0] + in[1] + -in[3]); + out[2] = xCoeff*in[1] + yCoeff*(in[0] + -in[2] + in[3]); + out[3] = xCoeff*in[0] + yCoeff*( -in[1] + -in[2] + -in[3]); } -// Given an input sample, this function mixes echo into the four-channel late -// reverb. -static ALvoid EAXEcho(ALreverbState *State, ALuint todo, ALfloat (*restrict late)[MAX_UPDATE_SAMPLES]) +/* 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. + */ +#define DECL_TEMPLATE(T) \ +static void VectorAllpass_##T(ALfloat *restrict out, \ + const ALfloat *restrict in, \ + const ALsizei offset, const ALfloat feedCoeff, \ + const ALfloat xCoeff, const ALfloat yCoeff, \ + const ALfloat mu, VecAllpass *Vap) \ +{ \ + ALfloat f[NUM_LINES], fs[NUM_LINES]; \ + ALfloat input; \ + ALsizei i; \ + \ + (void)mu; /* Ignore for Unfaded. */ \ + \ + for(i = 0;i < NUM_LINES;i++) \ + { \ + input = in[i]; \ + out[i] = T##DelayLineOut(&Vap->Delay, offset-Vap->Offset[i][0], \ + offset-Vap->Offset[i][1], i, mu) - \ + feedCoeff*input; \ + f[i] = input + feedCoeff*out[i]; \ + } \ + VectorPartialScatter(fs, f, xCoeff, yCoeff); \ + \ + DelayLineIn4(&Vap->Delay, offset, fs); \ +} +DECL_TEMPLATE(Unfaded) +DECL_TEMPLATE(Faded) +#undef DECL_TEMPLATE + +/* 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. + */ +#define DECL_TEMPLATE(T) \ +static void EarlyReflection_##T(ALreverbState *State, const ALsizei todo, \ + ALfloat fade, \ + ALfloat (*restrict out)[MAX_UPDATE_SAMPLES]) \ +{ \ + ALsizei offset = State->Offset; \ + const ALfloat apFeedCoeff = State->ApFeedCoeff; \ + const ALfloat mixX = State->MixX; \ + const ALfloat mixY = State->MixY; \ + ALfloat f[NUM_LINES], fr[NUM_LINES]; \ + ALsizei i, j; \ + \ + for(i = 0;i < todo;i++) \ + { \ + for(j = 0;j < NUM_LINES;j++) \ + fr[j] = T##DelayLineOut(&State->Delay, \ + offset-State->EarlyDelayTap[j][0], \ + offset-State->EarlyDelayTap[j][1], j, fade \ + ) * State->EarlyDelayCoeff[j]; \ + \ + VectorAllpass_##T(f, fr, offset, apFeedCoeff, mixX, mixY, fade, \ + &State->Early.VecAp); \ + \ + DelayLineIn4Rev(&State->Early.Delay, offset, f); \ + \ + for(j = 0;j < NUM_LINES;j++) \ + f[j] += T##DelayLineOut(&State->Early.Delay, \ + offset-State->Early.Offset[j][0], \ + offset-State->Early.Offset[j][1], j, fade \ + ) * State->Early.Coeff[j]; \ + \ + for(j = 0;j < NUM_LINES;j++) \ + out[j][i] = f[j]; \ + \ + VectorPartialScatterRev(fr, f, mixX, mixY); \ + \ + DelayLineIn4(&State->Delay, offset-State->LateFeedTap, fr); \ + \ + offset++; \ + fade += FadeStep; \ + } \ +} +DECL_TEMPLATE(Unfaded) +DECL_TEMPLATE(Faded) +#undef DECL_TEMPLATE + +/* Applies a first order filter section. */ +static inline ALfloat FirstOrderFilter(const ALfloat in, const ALfloat *restrict coeffs, + ALfloat *restrict state) { - ALfloat feed; - ALuint offset; - ALuint c, i; - - for(c = 0;c < 4;c++) - { - offset = State->Offset; - for(i = 0;i < todo;i++) - { - // Get the latest attenuated echo sample for output. - feed = DelayLineOut(&State->Echo.Delay[c].Feedback, offset-State->Echo.Offset) * - State->Echo.Coeff; - - // Write the output. - late[c][i] += State->Echo.MixCoeff * feed; - - // Mix the energy-attenuated input with the output and pass it through - // the echo low-pass filter. - feed += DelayLineOut(&State->Delay, (offset-State->LateDelayTap[0])*4 + c) * - State->Echo.DensityGain; - feed = lerp(feed, State->Echo.LpSample[c], State->Echo.LpCoeff); - State->Echo.LpSample[c] = feed; - - // Then the echo all-pass filter. - feed = AllpassInOut(&State->Echo.Delay[c].Ap, offset-State->Echo.ApOffset, - offset, feed, State->Echo.ApFeedCoeff, - State->Echo.ApCoeff); - - // Feed the delay with the mixed and filtered sample. - DelayLineIn(&State->Echo.Delay[c].Feedback, offset, feed); - offset++; - } - } + ALfloat out = coeffs[0]*in + *state; + *state = coeffs[1]*in + coeffs[2]*out; + return out; } -// Perform the non-EAX reverb pass on a given input sample, resulting in -// four-channel output. -static ALvoid VerbPass(ALreverbState *State, ALuint todo, ALfloat (*restrict input)[MAX_UPDATE_SAMPLES], ALfloat (*restrict early)[MAX_UPDATE_SAMPLES], ALfloat (*restrict late)[MAX_UPDATE_SAMPLES]) +/* Applies the two T60 damping filter sections. */ +static inline void LateT60Filter(ALfloat *restrict out, const ALfloat *restrict in, + T60Filter *filter) { - ALuint i, c; - - for(c = 0;c < 4;c++) - { - /* Low-pass filter the incoming samples (use the early buffer as temp - * storage). - */ - ALfilterState_process(&State->Filter[c].Lp, &early[0][0], input[c], todo); - for(i = 0;i < todo;i++) - DelayLineIn(&State->Delay, (State->Offset+i)*4 + c, early[0][i]); - } - - // Calculate the early reflection from the first delay tap. - EarlyReflection(State, todo, early); - - // Calculate the late reverb from the decorrelator taps. - LateReverb(State, todo, late); - - // Step all delays forward one sample. - State->Offset += todo; + ALsizei i; + for(i = 0;i < NUM_LINES;i++) + out[i] = FirstOrderFilter( + FirstOrderFilter(in[i], filter[i].HFCoeffs, &filter[i].HFState), + filter[i].LFCoeffs, &filter[i].LFState + ); } -// Perform the EAX reverb pass on a given input sample, resulting in four- -// channel output. -static ALvoid EAXVerbPass(ALreverbState *State, ALuint todo, ALfloat (*restrict input)[MAX_UPDATE_SAMPLES], ALfloat (*restrict early)[MAX_UPDATE_SAMPLES], ALfloat (*restrict late)[MAX_UPDATE_SAMPLES]) -{ - ALuint i, c; - - /* Perform any modulation on the input (use the early and late buffers as - * temp storage). - */ - CalcModulationDelays(State, &late[0][0], todo); - for(c = 0;c < 4;c++) - { - EAXModulation(&State->Mod.Delay[c], State->Offset, &late[0][0], - &early[0][0], input[c], todo); - - /* Band-pass the incoming samples */ - ALfilterState_process(&State->Filter[c].Lp, &early[1][0], &early[0][0], todo); - ALfilterState_process(&State->Filter[c].Hp, &early[2][0], &early[1][0], todo); - - /* Feed the initial delay line. */ - for(i = 0;i < todo;i++) - DelayLineIn(&State->Delay, (State->Offset+i)*4 + c, early[2][i]); - } - - // Calculate the early reflection from the first delay tap. - EarlyReflection(State, todo, early); - - // Calculate the late reverb from the decorrelator taps. - LateReverb(State, todo, late); - - // Calculate and mix in any echo. - EAXEcho(State, todo, late); - - // Step all delays forward. - State->Offset += todo; +/* This generates the reverb tail using a modified feed-back delay network + * (FDN). + * + * Results from the early reflections are attenuated by the density gain and + * 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. + */ +#define DECL_TEMPLATE(T) \ +static void LateReverb_##T(ALreverbState *State, const ALsizei todo, \ + ALfloat fade, \ + ALfloat (*restrict out)[MAX_UPDATE_SAMPLES]) \ +{ \ + const ALfloat apFeedCoeff = State->ApFeedCoeff; \ + const ALfloat mixX = State->MixX; \ + const ALfloat mixY = State->MixY; \ + ALsizei offset; \ + ALsizei i, j; \ + \ + offset = State->Offset; \ + for(i = 0;i < todo;i++) \ + { \ + ALfloat f[NUM_LINES], fr[NUM_LINES]; \ + \ + for(j = 0;j < NUM_LINES;j++) \ + f[j] = T##DelayLineOut(&State->Delay, \ + offset - State->LateDelayTap[j][0], \ + offset - State->LateDelayTap[j][1], j, fade \ + ) * State->Late.DensityGain; \ + \ + for(j = 0;j < NUM_LINES;j++) \ + f[j] += T##DelayLineOut(&State->Late.Delay, \ + offset - State->Late.Offset[j][0], \ + offset - State->Late.Offset[j][1], j, fade \ + ); \ + \ + LateT60Filter(fr, f, State->Late.T60); \ + VectorAllpass_##T(f, fr, offset, apFeedCoeff, mixX, mixY, fade, \ + &State->Late.VecAp); \ + \ + for(j = 0;j < NUM_LINES;j++) \ + out[j][i] = f[j]; \ + \ + VectorPartialScatterRev(fr, f, mixX, mixY); \ + \ + DelayLineIn4(&State->Late.Delay, offset, fr); \ + \ + offset++; \ + fade += FadeStep; \ + } \ } +DECL_TEMPLATE(Unfaded) +DECL_TEMPLATE(Faded) +#undef DECL_TEMPLATE - -static ALvoid ALreverbState_processStandard(ALreverbState *State, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) +static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels) { - static const aluMatrixf B2A = {{ - { 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 } - }}; ALfloat (*restrict afmt)[MAX_UPDATE_SAMPLES] = State->AFormatSamples; ALfloat (*restrict early)[MAX_UPDATE_SAMPLES] = State->EarlySamples; ALfloat (*restrict late)[MAX_UPDATE_SAMPLES] = State->ReverbSamples; - ALuint base, c; + ALsizei fadeCount = State->FadeCount; + ALfloat fade = (ALfloat)fadeCount / FADE_SAMPLES; + ALsizei base, c; /* Process reverb for these samples. */ for(base = 0;base < SamplesToDo;) { - ALuint todo = minu(SamplesToDo-base, MAX_UPDATE_SAMPLES); + ALsizei todo = mini(SamplesToDo-base, MAX_UPDATE_SAMPLES); + /* If cross-fading, don't do more samples than there are to fade. */ + if(FADE_SAMPLES-fadeCount > 0) + todo = mini(todo, FADE_SAMPLES-fadeCount); - /* Convert B-Foramt to A-Format for processing. */ - memset(afmt, 0, sizeof(*afmt)*4); - for(c = 0;c < 4;c++) + /* Convert B-Format to A-Format for processing. */ + memset(afmt, 0, sizeof(*afmt)*NUM_LINES); + for(c = 0;c < NUM_LINES;c++) MixRowSamples(afmt[c], B2A.m[c], SamplesIn, MAX_EFFECT_CHANNELS, base, todo ); - VerbPass(State, todo, afmt, early, late); + /* Process the samples for reverb. */ + for(c = 0;c < NUM_LINES;c++) + { + /* Band-pass the incoming samples. Use the early output lines for + * temp storage. + */ + BiquadFilter_process(&State->Filter[c].Lp, early[0], afmt[c], todo); + BiquadFilter_process(&State->Filter[c].Hp, early[1], early[0], todo); + + /* Feed the initial delay line. */ + DelayLineIn(&State->Delay, State->Offset, c, early[1], todo); + } + + if(UNLIKELY(fadeCount < FADE_SAMPLES)) + { + /* Generate early reflections. */ + EarlyReflection_Faded(State, todo, fade, early); + + /* Generate late reverb. */ + LateReverb_Faded(State, todo, fade, late); + fade = minf(1.0f, fade + todo*FadeStep); + } + else + { + /* Generate early reflections. */ + EarlyReflection_Unfaded(State, todo, fade, early); + + /* Generate late reverb. */ + LateReverb_Unfaded(State, todo, fade, late); + } + + /* Step all delays forward. */ + State->Offset += todo; + + if(UNLIKELY(fadeCount < FADE_SAMPLES) && (fadeCount += todo) >= FADE_SAMPLES) + { + /* Update the cross-fading delay line taps. */ + fadeCount = FADE_SAMPLES; + fade = 1.0f; + for(c = 0;c < NUM_LINES;c++) + { + State->EarlyDelayTap[c][0] = State->EarlyDelayTap[c][1]; + State->Early.VecAp.Offset[c][0] = State->Early.VecAp.Offset[c][1]; + State->Early.Offset[c][0] = State->Early.Offset[c][1]; + State->LateDelayTap[c][0] = State->LateDelayTap[c][1]; + State->Late.VecAp.Offset[c][0] = State->Late.VecAp.Offset[c][1]; + State->Late.Offset[c][0] = State->Late.Offset[c][1]; + } + } /* Mix the A-Format results to output, implicitly converting back to * B-Format. */ - for(c = 0;c < 4;c++) + for(c = 0;c < NUM_LINES;c++) MixSamples(early[c], NumChannels, SamplesOut, State->Early.CurrentGain[c], State->Early.PanGain[c], SamplesToDo-base, base, todo ); - for(c = 0;c < 4;c++) + for(c = 0;c < NUM_LINES;c++) MixSamples(late[c], NumChannels, SamplesOut, State->Late.CurrentGain[c], State->Late.PanGain[c], SamplesToDo-base, base, todo @@ -1425,81 +1607,31 @@ static ALvoid ALreverbState_processStandard(ALreverbState *State, ALuint Samples base += todo; } -} - -static ALvoid ALreverbState_processEax(ALreverbState *State, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) -{ - static const aluMatrixf B2A = {{ - { 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 } - }}; - ALfloat (*restrict afmt)[MAX_UPDATE_SAMPLES] = State->AFormatSamples; - ALfloat (*restrict early)[MAX_UPDATE_SAMPLES] = State->EarlySamples; - ALfloat (*restrict late)[MAX_UPDATE_SAMPLES] = State->ReverbSamples; - ALuint base, c; - - /* Process reverb for these samples. */ - for(base = 0;base < SamplesToDo;) - { - ALuint todo = minu(SamplesToDo-base, MAX_UPDATE_SAMPLES); - - memset(afmt, 0, 4*MAX_UPDATE_SAMPLES*sizeof(float)); - for(c = 0;c < 4;c++) - MixRowSamples(afmt[c], B2A.m[c], - SamplesIn, MAX_EFFECT_CHANNELS, base, todo - ); - - EAXVerbPass(State, todo, afmt, early, late); - - for(c = 0;c < 4;c++) - MixSamples(early[c], NumChannels, SamplesOut, - State->Early.CurrentGain[c], State->Early.PanGain[c], - SamplesToDo-base, base, todo - ); - for(c = 0;c < 4;c++) - MixSamples(late[c], NumChannels, SamplesOut, - State->Late.CurrentGain[c], State->Late.PanGain[c], - SamplesToDo-base, base, todo - ); - - base += todo; - } -} - -static ALvoid ALreverbState_process(ALreverbState *State, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) -{ - if(State->IsEax) - ALreverbState_processEax(State, SamplesToDo, SamplesIn, SamplesOut, NumChannels); - else - ALreverbState_processStandard(State, SamplesToDo, SamplesIn, SamplesOut, NumChannels); + State->FadeCount = fadeCount; } -typedef struct ALreverbStateFactory { - DERIVE_FROM_TYPE(ALeffectStateFactory); -} ALreverbStateFactory; +typedef struct ReverbStateFactory { + DERIVE_FROM_TYPE(EffectStateFactory); +} ReverbStateFactory; -static ALeffectState *ALreverbStateFactory_create(ALreverbStateFactory* UNUSED(factory)) +static ALeffectState *ReverbStateFactory_create(ReverbStateFactory* UNUSED(factory)) { ALreverbState *state; - alcall_once(&mixfunc_inited, init_mixfunc); - NEW_OBJ0(state, ALreverbState)(); if(!state) return NULL; return STATIC_CAST(ALeffectState, state); } -DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALreverbStateFactory); +DEFINE_EFFECTSTATEFACTORY_VTABLE(ReverbStateFactory); -ALeffectStateFactory *ALreverbStateFactory_getFactory(void) +EffectStateFactory *ReverbStateFactory_getFactory(void) { - static ALreverbStateFactory ReverbFactory = { { GET_VTABLE2(ALreverbStateFactory, ALeffectStateFactory) } }; + static ReverbStateFactory ReverbFactory = { { GET_VTABLE2(ReverbStateFactory, EffectStateFactory) } }; - return STATIC_CAST(ALeffectStateFactory, &ReverbFactory); + return STATIC_CAST(EffectStateFactory, &ReverbFactory); } @@ -1510,18 +1642,17 @@ void ALeaxreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, { case AL_EAXREVERB_DECAY_HFLIMIT: if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hflimit out of range"); props->Reverb.DecayHFLimit = val; break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x", + param); } } void ALeaxreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) -{ - ALeaxreverb_setParami(effect, context, param, vals[0]); -} +{ ALeaxreverb_setParami(effect, context, param, vals[0]); } void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) { ALeffectProps *props = &effect->Props; @@ -1529,126 +1660,127 @@ void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, { case AL_EAXREVERB_DENSITY: if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb room rolloff factor out of range"); props->Reverb.RoomRolloffFactor = val; break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", + param); } } void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) @@ -1658,14 +1790,14 @@ void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, { case AL_EAXREVERB_REFLECTIONS_PAN: if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2]))) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2]))) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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]; @@ -1687,13 +1819,12 @@ void ALeaxreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum p break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x", + param); } } void ALeaxreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) -{ - ALeaxreverb_getParami(effect, context, param, vals); -} +{ ALeaxreverb_getParami(effect, context, param, vals); } void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) { const ALeffectProps *props = &effect->Props; @@ -1780,7 +1911,8 @@ void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum p break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", + param); } } void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) @@ -1814,18 +1946,16 @@ void ALreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALi { case AL_REVERB_DECAY_HFLIMIT: if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hflimit out of range"); props->Reverb.DecayHFLimit = val; break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param); } } void ALreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) -{ - ALreverb_setParami(effect, context, param, vals[0]); -} +{ ALreverb_setParami(effect, context, param, vals[0]); } void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) { ALeffectProps *props = &effect->Props; @@ -1833,84 +1963,82 @@ void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALf { case AL_REVERB_DENSITY: if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + 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)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb room rolloff factor out of range"); props->Reverb.RoomRolloffFactor = val; break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param); } } void ALreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) -{ - ALreverb_setParamf(effect, context, param, vals[0]); -} +{ ALreverb_setParamf(effect, context, param, vals[0]); } void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val) { @@ -1922,13 +2050,11 @@ void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum para break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param); } } void ALreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) -{ - ALreverb_getParami(effect, context, param, vals); -} +{ ALreverb_getParami(effect, context, param, vals); } void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) { const ALeffectProps *props = &effect->Props; @@ -1983,12 +2109,10 @@ void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum para break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param); } } void ALreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) -{ - ALreverb_getParamf(effect, context, param, vals); -} +{ ALreverb_getParamf(effect, context, param, vals); } DEFINE_ALEFFECT_VTABLE(ALreverb); diff --git a/Engine/lib/openal-soft/Alc/filters/defs.h b/Engine/lib/openal-soft/Alc/filters/defs.h new file mode 100644 index 000000000..133a85ebd --- /dev/null +++ b/Engine/lib/openal-soft/Alc/filters/defs.h @@ -0,0 +1,112 @@ +#ifndef ALC_FILTER_H +#define ALC_FILTER_H + +#include "AL/al.h" +#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). + */ + +typedef enum BiquadType { + /** EFX-style low-pass filter, specifying a gain and reference frequency. */ + BiquadType_HighShelf, + /** EFX-style high-pass filter, specifying a gain and reference frequency. */ + BiquadType_LowShelf, + /** Peaking filter, specifying a gain and reference frequency. */ + BiquadType_Peaking, + + /** Low-pass cut-off filter, specifying a cut-off frequency. */ + BiquadType_LowPass, + /** High-pass cut-off filter, specifying a cut-off frequency. */ + BiquadType_HighPass, + /** Band-pass filter, specifying a center frequency. */ + BiquadType_BandPass, +} BiquadType; + +typedef struct BiquadFilter { + ALfloat z1, z2; /* Last two delayed components for direct form II. */ + ALfloat b0, b1, b2; /* Transfer function coefficients "b" (numerator) */ + ALfloat a1, a2; /* Transfer function coefficients "a" (denominator; a0 is + * pre-applied). */ +} BiquadFilter; +/* Currently only a C-based filter process method is implemented. */ +#define BiquadFilter_process BiquadFilter_processC + +/** + * 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 + */ +inline ALfloat calc_rcpQ_from_slope(ALfloat gain, ALfloat slope) +{ + return sqrtf((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 + */ +inline ALfloat calc_rcpQ_from_bandwidth(ALfloat f0norm, ALfloat bandwidth) +{ + ALfloat w0 = F_TAU * f0norm; + return 2.0f*sinhf(logf(2.0f)/2.0f*bandwidth*w0/sinf(w0)); +} + +inline void BiquadFilter_clear(BiquadFilter *filter) +{ + filter->z1 = 0.0f; + filter->z2 = 0.0f; +} + +/** + * Sets up the filter state for the specified filter type and its parameters. + * + * \param filter The filter object to prepare. + * \param type The type of filter for the object to apply. + * \param gain The gain for the reference frequency response. Only used by the + * Shelf and Peaking filter types. + * \param f0norm The normalized reference frequency (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 calc_rcpQ_from_slope or + * calc_rcpQ_from_bandwidth depending on the available data. + */ +void BiquadFilter_setParams(BiquadFilter *filter, BiquadType type, ALfloat gain, ALfloat f0norm, ALfloat rcpQ); + +inline void BiquadFilter_copyParams(BiquadFilter *restrict dst, const BiquadFilter *restrict src) +{ + dst->b0 = src->b0; + dst->b1 = src->b1; + dst->b2 = src->b2; + dst->a1 = src->a1; + dst->a2 = src->a2; +} + +void BiquadFilter_processC(BiquadFilter *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples); + +inline void BiquadFilter_passthru(BiquadFilter *filter, ALsizei numsamples) +{ + if(LIKELY(numsamples >= 2)) + { + filter->z1 = 0.0f; + filter->z2 = 0.0f; + } + else if(numsamples == 1) + { + filter->z1 = filter->z2; + filter->z2 = 0.0f; + } +} + +#endif /* ALC_FILTER_H */ diff --git a/Engine/lib/openal-soft/Alc/filters/filter.c b/Engine/lib/openal-soft/Alc/filters/filter.c new file mode 100644 index 000000000..2b370f89d --- /dev/null +++ b/Engine/lib/openal-soft/Alc/filters/filter.c @@ -0,0 +1,129 @@ + +#include "config.h" + +#include "AL/alc.h" +#include "AL/al.h" + +#include "alMain.h" +#include "defs.h" + +extern inline void BiquadFilter_clear(BiquadFilter *filter); +extern inline void BiquadFilter_copyParams(BiquadFilter *restrict dst, const BiquadFilter *restrict src); +extern inline void BiquadFilter_passthru(BiquadFilter *filter, ALsizei numsamples); +extern inline ALfloat calc_rcpQ_from_slope(ALfloat gain, ALfloat slope); +extern inline ALfloat calc_rcpQ_from_bandwidth(ALfloat f0norm, ALfloat bandwidth); + + +void BiquadFilter_setParams(BiquadFilter *filter, BiquadType type, ALfloat gain, ALfloat f0norm, ALfloat rcpQ) +{ + ALfloat alpha, sqrtgain_alpha_2; + ALfloat w0, sin_w0, cos_w0; + ALfloat a[3] = { 1.0f, 0.0f, 0.0f }; + ALfloat b[3] = { 1.0f, 0.0f, 0.0f }; + + // Limit gain to -100dB + assert(gain > 0.00001f); + + w0 = F_TAU * f0norm; + sin_w0 = sinf(w0); + cos_w0 = cosf(w0); + alpha = sin_w0/2.0f * rcpQ; + + /* Calculate filter coefficients depending on filter type */ + switch(type) + { + case BiquadType_HighShelf: + sqrtgain_alpha_2 = 2.0f * sqrtf(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 * sqrtf(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 = sqrtf(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; + b[2] = -alpha; + a[0] = 1.0f + alpha; + a[1] = -2.0f * cos_w0; + a[2] = 1.0f - alpha; + break; + } + + filter->a1 = a[1] / a[0]; + filter->a2 = a[2] / a[0]; + filter->b0 = b[0] / a[0]; + filter->b1 = b[1] / a[0]; + filter->b2 = b[2] / a[0]; +} + + +void BiquadFilter_processC(BiquadFilter *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples) +{ + const ALfloat a1 = filter->a1; + const ALfloat a2 = filter->a2; + const ALfloat b0 = filter->b0; + const ALfloat b1 = filter->b1; + const ALfloat b2 = filter->b2; + ALfloat z1 = filter->z1; + ALfloat z2 = filter->z2; + ALsizei i; + + ASSUME(numsamples > 0); + + /* 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/ + */ + for(i = 0;i < numsamples;i++) + { + ALfloat input = src[i]; + ALfloat output = input*b0 + z1; + z1 = input*b1 - output*a1 + z2; + z2 = input*b2 - output*a2; + dst[i] = output; + } + + filter->z1 = z1; + filter->z2 = z2; +} diff --git a/Engine/lib/openal-soft/Alc/filters/nfc.c b/Engine/lib/openal-soft/Alc/filters/nfc.c new file mode 100644 index 000000000..8869d1d00 --- /dev/null +++ b/Engine/lib/openal-soft/Alc/filters/nfc.c @@ -0,0 +1,426 @@ + +#include "config.h" + +#include "nfc.h" +#include "alMain.h" + +#include + + +/* 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. + */ + +static const float B[4][3] = { + { 0.0f }, + { 1.0f }, + { 3.0f, 3.0f }, + { 3.6778f, 6.4595f, 2.3222f }, + /*{ 4.2076f, 11.4877f, 5.7924f, 9.1401f }*/ +}; + +static void NfcFilterCreate1(struct NfcFilter1 *nfc, const float w0, const float w1) +{ + 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; +} + +static void NfcFilterAdjust1(struct NfcFilter1 *nfc, const float w0) +{ + float b_00, g_0; + float r; + + r = 0.5f * w0; + b_00 = B[1][0] * r; + g_0 = 1.0f + b_00; + + nfc->gain = nfc->base_gain * g_0; + nfc->b1 = 2.0f * b_00 / g_0; +} + + +static void NfcFilterCreate2(struct NfcFilter2 *nfc, const float w0, const float w1) +{ + 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; +} + +static void NfcFilterAdjust2(struct NfcFilter2 *nfc, const float w0) +{ + float b_10, b_11, g_1; + float r; + + 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 = 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; +} + + +static void NfcFilterCreate3(struct NfcFilter3 *nfc, const float w0, const float w1) +{ + 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; + 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; + + b_00 = B[3][2] * r; + g_0 = 1.0f + b_00; + + nfc->gain *= g_0; + 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; + 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; + + b_00 = B[3][2] * r; + g_0 = 1.0f + b_00; + + nfc->base_gain /= g_0; + nfc->gain /= g_0; + nfc->a3 = 2.0f * b_00 / g_0; +} + +static void NfcFilterAdjust3(struct NfcFilter3 *nfc, const float w0) +{ + float b_10, b_11, g_1; + float b_00, g_0; + float r; + + r = 0.5f * w0; + b_10 = B[3][0] * r; + b_11 = B[3][1] * r * r; + 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; + + b_00 = B[3][2] * r; + g_0 = 1.0f + b_00; + + nfc->gain *= g_0; + nfc->b3 = 2.0f * b_00 / g_0; +} + + +void NfcFilterCreate(NfcFilter *nfc, const float w0, const float w1) +{ + memset(nfc, 0, sizeof(*nfc)); + NfcFilterCreate1(&nfc->first, w0, w1); + NfcFilterCreate2(&nfc->second, w0, w1); + NfcFilterCreate3(&nfc->third, w0, w1); +} + +void NfcFilterAdjust(NfcFilter *nfc, const float w0) +{ + NfcFilterAdjust1(&nfc->first, w0); + NfcFilterAdjust2(&nfc->second, w0); + NfcFilterAdjust3(&nfc->third, w0); +} + + +void NfcFilterProcess1(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count) +{ + const float gain = nfc->first.gain; + const float b1 = nfc->first.b1; + const float a1 = nfc->first.a1; + float z1 = nfc->first.z[0]; + int i; + + ASSUME(count > 0); + + for(i = 0;i < count;i++) + { + float y = src[i]*gain - a1*z1; + float out = y + b1*z1; + z1 += y; + + dst[i] = out; + } + nfc->first.z[0] = z1; +} + +void NfcFilterProcess2(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count) +{ + const float gain = nfc->second.gain; + const float b1 = nfc->second.b1; + const float b2 = nfc->second.b2; + const float a1 = nfc->second.a1; + const float a2 = nfc->second.a2; + float z1 = nfc->second.z[0]; + float z2 = nfc->second.z[1]; + int i; + + ASSUME(count > 0); + + for(i = 0;i < count;i++) + { + float y = src[i]*gain - a1*z1 - a2*z2; + float out = y + b1*z1 + b2*z2; + z2 += z1; + z1 += y; + + dst[i] = out; + } + nfc->second.z[0] = z1; + nfc->second.z[1] = z2; +} + +void NfcFilterProcess3(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count) +{ + const float gain = nfc->third.gain; + const float b1 = nfc->third.b1; + const float b2 = nfc->third.b2; + const float b3 = nfc->third.b3; + const float a1 = nfc->third.a1; + const float a2 = nfc->third.a2; + const float a3 = nfc->third.a3; + float z1 = nfc->third.z[0]; + float z2 = nfc->third.z[1]; + float z3 = nfc->third.z[2]; + int i; + + ASSUME(count > 0); + + for(i = 0;i < count;i++) + { + float y = src[i]*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; + + dst[i] = out; + } + nfc->third.z[0] = z1; + nfc->third.z[1] = z2; + nfc->third.z[2] = z3; +} + +#if 0 /* Original methods the above are derived from. */ +static void NfcFilterCreate(NfcFilter *nfc, const ALsizei order, const float src_dist, const float ctl_dist, const float rate) +{ + static const float B[4][5] = { + { }, + { 1.0f }, + { 3.0f, 3.0f }, + { 3.6778f, 6.4595f, 2.3222f }, + { 4.2076f, 11.4877f, 5.7924f, 9.1401f } + }; + float w0 = SPEEDOFSOUNDMETRESPERSEC / (src_dist * rate); + float w1 = SPEEDOFSOUNDMETRESPERSEC / (ctl_dist * rate); + ALsizei i; + float r; + + nfc->g = 1.0f; + nfc->coeffs[0] = 1.0f; + + /* NOTE: Slight adjustment from the literature to raise the center + * frequency a bit (0.5 -> 1.0). + */ + r = 1.0f * w0; + for(i = 0; i < (order-1);i += 2) + { + float b_10 = B[order][i ] * r; + float b_11 = B[order][i+1] * r * r; + float g_1 = 1.0f + b_10 + b_11; + + nfc->b[i] = b_10; + nfc->b[i + 1] = b_11; + nfc->coeffs[0] *= g_1; + nfc->coeffs[i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1; + nfc->coeffs[i+2] = (4.0f * b_11) / g_1; + } + if(i < order) + { + float b_00 = B[order][i] * r; + float g_0 = 1.0f + b_00; + + nfc->b[i] = b_00; + nfc->coeffs[0] *= g_0; + nfc->coeffs[i+1] = (2.0f * b_00) / g_0; + } + + r = 1.0f * w1; + for(i = 0;i < (order-1);i += 2) + { + float b_10 = B[order][i ] * r; + float b_11 = B[order][i+1] * r * r; + float g_1 = 1.0f + b_10 + b_11; + + nfc->g /= g_1; + nfc->coeffs[0] /= g_1; + nfc->coeffs[order+i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1; + nfc->coeffs[order+i+2] = (4.0f * b_11) / g_1; + } + if(i < order) + { + float b_00 = B[order][i] * r; + float g_0 = 1.0f + b_00; + + nfc->g /= g_0; + nfc->coeffs[0] /= g_0; + nfc->coeffs[order+i+1] = (2.0f * b_00) / g_0; + } + + for(i = 0; i < MAX_AMBI_ORDER; i++) + nfc->history[i] = 0.0f; +} + +static void NfcFilterAdjust(NfcFilter *nfc, const float distance) +{ + int i; + + nfc->coeffs[0] = nfc->g; + + for(i = 0;i < (nfc->order-1);i += 2) + { + float b_10 = nfc->b[i] / distance; + float b_11 = nfc->b[i+1] / (distance * distance); + float g_1 = 1.0f + b_10 + b_11; + + nfc->coeffs[0] *= g_1; + nfc->coeffs[i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1; + nfc->coeffs[i+2] = (4.0f * b_11) / g_1; + } + if(i < nfc->order) + { + float b_00 = nfc->b[i] / distance; + float g_0 = 1.0f + b_00; + + nfc->coeffs[0] *= g_0; + nfc->coeffs[i+1] = (2.0f * b_00) / g_0; + } +} + +static float NfcFilterProcess(const float in, NfcFilter *nfc) +{ + int i; + float out = in * nfc->coeffs[0]; + + for(i = 0;i < (nfc->order-1);i += 2) + { + float y = out - (nfc->coeffs[nfc->order+i+1] * nfc->history[i]) - + (nfc->coeffs[nfc->order+i+2] * nfc->history[i+1]) + 1.0e-30f; + out = y + (nfc->coeffs[i+1]*nfc->history[i]) + (nfc->coeffs[i+2]*nfc->history[i+1]); + + nfc->history[i+1] += nfc->history[i]; + nfc->history[i] += y; + } + if(i < nfc->order) + { + float y = out - (nfc->coeffs[nfc->order+i+1] * nfc->history[i]) + 1.0e-30f; + + out = y + (nfc->coeffs[i+1] * nfc->history[i]); + nfc->history[i] += y; + } + + return out; +} +#endif diff --git a/Engine/lib/openal-soft/Alc/filters/nfc.h b/Engine/lib/openal-soft/Alc/filters/nfc.h new file mode 100644 index 000000000..12a5a18f3 --- /dev/null +++ b/Engine/lib/openal-soft/Alc/filters/nfc.h @@ -0,0 +1,49 @@ +#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]; +}; + +typedef struct NfcFilter { + struct NfcFilter1 first; + struct NfcFilter2 second; + struct NfcFilter3 third; +} NfcFilter; + + +/* 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 NfcFilterCreate(NfcFilter *nfc, const float w0, const float w1); +void NfcFilterAdjust(NfcFilter *nfc, const float w0); + +/* Near-field control filter for first-order ambisonic channels (1-3). */ +void NfcFilterProcess1(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count); + +/* Near-field control filter for second-order ambisonic channels (4-8). */ +void NfcFilterProcess2(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count); + +/* Near-field control filter for third-order ambisonic channels (9-15). */ +void NfcFilterProcess3(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count); + +#endif /* FILTER_NFC_H */ diff --git a/Engine/lib/openal-soft/Alc/filters/splitter.c b/Engine/lib/openal-soft/Alc/filters/splitter.c new file mode 100644 index 000000000..e99f4b953 --- /dev/null +++ b/Engine/lib/openal-soft/Alc/filters/splitter.c @@ -0,0 +1,109 @@ + +#include "config.h" + +#include "splitter.h" + +#include "math_defs.h" + + +void bandsplit_init(BandSplitter *splitter, ALfloat f0norm) +{ + ALfloat w = f0norm * F_TAU; + ALfloat cw = cosf(w); + if(cw > FLT_EPSILON) + splitter->coeff = (sinf(w) - 1.0f) / cw; + else + splitter->coeff = cw * -0.5f; + + splitter->lp_z1 = 0.0f; + splitter->lp_z2 = 0.0f; + splitter->hp_z1 = 0.0f; +} + +void bandsplit_clear(BandSplitter *splitter) +{ + splitter->lp_z1 = 0.0f; + splitter->lp_z2 = 0.0f; + splitter->hp_z1 = 0.0f; +} + +void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout, + const ALfloat *input, ALsizei count) +{ + ALfloat lp_coeff, hp_coeff, lp_y, hp_y, d; + ALfloat lp_z1, lp_z2, hp_z1; + ALsizei i; + + ASSUME(count > 0); + + hp_coeff = splitter->coeff; + lp_coeff = splitter->coeff*0.5f + 0.5f; + lp_z1 = splitter->lp_z1; + lp_z2 = splitter->lp_z2; + hp_z1 = splitter->hp_z1; + for(i = 0;i < count;i++) + { + ALfloat in = input[i]; + + /* Low-pass sample processing. */ + d = (in - lp_z1) * lp_coeff; + 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[i] = lp_y; + + /* All-pass sample processing. */ + hp_y = in*hp_coeff + hp_z1; + hp_z1 = in - hp_y*hp_coeff; + + /* High-pass generated from removing low-passed output. */ + hpout[i] = hp_y - lp_y; + } + splitter->lp_z1 = lp_z1; + splitter->lp_z2 = lp_z2; + splitter->hp_z1 = hp_z1; +} + + +void splitterap_init(SplitterAllpass *splitter, ALfloat f0norm) +{ + ALfloat w = f0norm * F_TAU; + ALfloat cw = cosf(w); + if(cw > FLT_EPSILON) + splitter->coeff = (sinf(w) - 1.0f) / cw; + else + splitter->coeff = cw * -0.5f; + + splitter->z1 = 0.0f; +} + +void splitterap_clear(SplitterAllpass *splitter) +{ + splitter->z1 = 0.0f; +} + +void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count) +{ + ALfloat coeff, in, out; + ALfloat z1; + ALsizei i; + + ASSUME(count > 0); + + coeff = splitter->coeff; + z1 = splitter->z1; + for(i = 0;i < count;i++) + { + in = samples[i]; + + out = in*coeff + z1; + z1 = in - out*coeff; + + samples[i] = out; + } + splitter->z1 = z1; +} diff --git a/Engine/lib/openal-soft/Alc/filters/splitter.h b/Engine/lib/openal-soft/Alc/filters/splitter.h new file mode 100644 index 000000000..a788bc3e9 --- /dev/null +++ b/Engine/lib/openal-soft/Alc/filters/splitter.h @@ -0,0 +1,40 @@ +#ifndef FILTER_SPLITTER_H +#define FILTER_SPLITTER_H + +#include "alMain.h" + + +/* Band splitter. Splits a signal into two phase-matching frequency bands. */ +typedef struct BandSplitter { + ALfloat coeff; + ALfloat lp_z1; + ALfloat lp_z2; + ALfloat hp_z1; +} BandSplitter; + +void bandsplit_init(BandSplitter *splitter, ALfloat f0norm); +void bandsplit_clear(BandSplitter *splitter); +void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout, + const ALfloat *input, ALsizei count); + +/* The all-pass portion of the band splitter. Applies the same phase shift + * without splitting the signal. + */ +typedef struct SplitterAllpass { + ALfloat coeff; + ALfloat z1; +} SplitterAllpass; + +void splitterap_init(SplitterAllpass *splitter, ALfloat f0norm); +void splitterap_clear(SplitterAllpass *splitter); +void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count); + + +typedef struct FrontStablizer { + SplitterAllpass APFilter[MAX_OUTPUT_CHANNELS]; + BandSplitter LFilter, RFilter; + alignas(16) ALfloat LSplit[2][BUFFERSIZE]; + alignas(16) ALfloat RSplit[2][BUFFERSIZE]; +} FrontStablizer; + +#endif /* FILTER_SPLITTER_H */ diff --git a/Engine/lib/openal-soft/Alc/fpu_modes.h b/Engine/lib/openal-soft/Alc/fpu_modes.h new file mode 100644 index 000000000..eb3059679 --- /dev/null +++ b/Engine/lib/openal-soft/Alc/fpu_modes.h @@ -0,0 +1,34 @@ +#ifndef FPU_MODES_H +#define FPU_MODES_H + +#ifdef HAVE_FENV_H +#include +#endif + + +typedef struct FPUCtl { +#if defined(__GNUC__) && defined(HAVE_SSE) + unsigned int sse_state; +#elif defined(HAVE___CONTROL87_2) + unsigned int state; + unsigned int sse_state; +#elif defined(HAVE__CONTROLFP) + unsigned int state; +#endif +} FPUCtl; +void SetMixerFPUMode(FPUCtl *ctl); +void RestoreFPUMode(const FPUCtl *ctl); + +#ifdef __GNUC__ +/* Use an alternate macro set with GCC to avoid accidental continue or break + * statements within the mixer mode. + */ +#define START_MIXER_MODE() __extension__({ FPUCtl _oldMode; SetMixerFPUMode(&_oldMode) +#define END_MIXER_MODE() RestoreFPUMode(&_oldMode); }) +#else +#define START_MIXER_MODE() do { FPUCtl _oldMode; SetMixerFPUMode(&_oldMode) +#define END_MIXER_MODE() RestoreFPUMode(&_oldMode); } while(0) +#endif +#define LEAVE_MIXER_MODE() RestoreFPUMode(&_oldMode) + +#endif /* FPU_MODES_H */ diff --git a/Engine/lib/openal-soft/Alc/helpers.c b/Engine/lib/openal-soft/Alc/helpers.c index 0a6982e90..7bcb3f4ab 100644 --- a/Engine/lib/openal-soft/Alc/helpers.c +++ b/Engine/lib/openal-soft/Alc/helpers.c @@ -39,6 +39,14 @@ #ifdef HAVE_DIRENT_H #include #endif +#ifdef HAVE_PROC_PIDPATH +#include +#endif + +#ifdef __FreeBSD__ +#include +#include +#endif #ifndef AL_NO_UID_DEFS #if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H) @@ -61,7 +69,7 @@ DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc 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_MMDEVAPI +#ifdef HAVE_WASAPI #include #include #include @@ -103,6 +111,8 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x #include "alMain.h" #include "alu.h" +#include "cpu_caps.h" +#include "fpu_modes.h" #include "atomic.h" #include "uintmap.h" #include "vector.h" @@ -112,71 +122,50 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x extern inline ALuint NextPowerOf2(ALuint value); +extern inline size_t RoundUp(size_t value, size_t r); extern inline ALint fastf2i(ALfloat f); -extern inline ALuint fastf2u(ALfloat f); +extern inline int float2int(float f); +#ifndef __GNUC__ +#if defined(HAVE_BITSCANFORWARD64_INTRINSIC) +extern inline int msvc64_ctz64(ALuint64 v); +#elif defined(HAVE_BITSCANFORWARD_INTRINSIC) +extern inline int msvc_ctz64(ALuint64 v); +#else +extern inline int fallback_popcnt64(ALuint64 v); +extern inline int fallback_ctz64(ALuint64 value); +#endif +#endif -ALuint CPUCapFlags = 0; +#if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \ + defined(_M_IX86) || defined(_M_X64)) +typedef unsigned int reg_type; +static inline void get_cpuid(int f, reg_type *regs) +{ __get_cpuid(f, ®s[0], ®s[1], ®s[2], ®s[3]); } +#define CAN_GET_CPUID +#elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \ + defined(_M_IX86) || defined(_M_X64)) +typedef int reg_type; +static inline void get_cpuid(int f, reg_type *regs) +{ (__cpuid)(regs, f); } +#define CAN_GET_CPUID +#endif +int CPUCapFlags = 0; -void FillCPUCaps(ALuint capfilter) +void FillCPUCaps(int capfilter) { - ALuint caps = 0; + 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?). */ -#if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \ - defined(_M_IX86) || defined(_M_X64)) +#ifdef CAN_GET_CPUID union { - unsigned int regs[4]; - char str[sizeof(unsigned int[4])]; - } cpuinf[3]; + reg_type regs[4]; + char str[sizeof(reg_type[4])]; + } cpuinf[3] = {{ { 0, 0, 0, 0 } }}; - if(!__get_cpuid(0, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3])) - ERR("Failed to get CPUID\n"); - else - { - unsigned int maxfunc = cpuinf[0].regs[0]; - unsigned int maxextfunc = 0; - - if(__get_cpuid(0x80000000, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3])) - maxextfunc = cpuinf[0].regs[0]; - TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc); - - TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8); - if(maxextfunc >= 0x80000004 && - __get_cpuid(0x80000002, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]) && - __get_cpuid(0x80000003, &cpuinf[1].regs[0], &cpuinf[1].regs[1], &cpuinf[1].regs[2], &cpuinf[1].regs[3]) && - __get_cpuid(0x80000004, &cpuinf[2].regs[0], &cpuinf[2].regs[1], &cpuinf[2].regs[2], &cpuinf[2].regs[3])) - TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str); - - if(maxfunc >= 1 && - __get_cpuid(1, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3])) - { - if((cpuinf[0].regs[3]&(1<<25))) - { - caps |= CPU_CAP_SSE; - if((cpuinf[0].regs[3]&(1<<26))) - { - caps |= CPU_CAP_SSE2; - if((cpuinf[0].regs[2]&(1<<0))) - { - caps |= CPU_CAP_SSE3; - if((cpuinf[0].regs[2]&(1<<19))) - caps |= CPU_CAP_SSE4_1; - } - } - } - } - } -#elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \ - defined(_M_IX86) || defined(_M_X64)) - union { - int regs[4]; - char str[sizeof(int[4])]; - } cpuinf[3]; - - (__cpuid)(cpuinf[0].regs, 0); + get_cpuid(0, cpuinf[0].regs); if(cpuinf[0].regs[0] == 0) ERR("Failed to get CPUID\n"); else @@ -184,7 +173,7 @@ void FillCPUCaps(ALuint capfilter) unsigned int maxfunc = cpuinf[0].regs[0]; unsigned int maxextfunc; - (__cpuid)(cpuinf[0].regs, 0x80000000); + get_cpuid(0x80000000, cpuinf[0].regs); maxextfunc = cpuinf[0].regs[0]; TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc); @@ -192,29 +181,23 @@ void FillCPUCaps(ALuint capfilter) TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8); if(maxextfunc >= 0x80000004) { - (__cpuid)(cpuinf[0].regs, 0x80000002); - (__cpuid)(cpuinf[1].regs, 0x80000003); - (__cpuid)(cpuinf[2].regs, 0x80000004); + 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) { - (__cpuid)(cpuinf[0].regs, 1); + get_cpuid(1, cpuinf[0].regs); if((cpuinf[0].regs[3]&(1<<25))) - { caps |= CPU_CAP_SSE; - if((cpuinf[0].regs[3]&(1<<26))) - { - caps |= CPU_CAP_SSE2; - if((cpuinf[0].regs[2]&(1<<0))) - { - caps |= CPU_CAP_SSE3; - if((cpuinf[0].regs[2]&(1<<19))) - caps |= CPU_CAP_SSE4_1; - } - } - } + 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 @@ -242,11 +225,16 @@ void FillCPUCaps(ALuint capfilter) char buf[256]; while(fgets(buf, sizeof(buf), file) != NULL) { + size_t len; char *str; if(strncmp(buf, "Features\t:", 10) != 0) continue; + len = strlen(buf); + while(len > 0 && isspace(buf[len-1])) + buf[--len] = 0; + TRACE("Got features string:%s\n", buf+10); str = buf; @@ -272,7 +260,7 @@ void FillCPUCaps(ALuint capfilter) ((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&CPU_CAP_NEON) ? ((caps&CPU_CAP_NEON) ? " +NEON" : " -NEON") : ""), ((!capfilter) ? " -none-" : "") ); CPUCapFlags = caps & capfilter; @@ -281,78 +269,51 @@ void FillCPUCaps(ALuint capfilter) void SetMixerFPUMode(FPUCtl *ctl) { -#ifdef HAVE_FENV_H - fegetenv(STATIC_CAST(fenv_t, ctl)); -#if defined(__GNUC__) && defined(HAVE_SSE) - /* FIXME: Some fegetenv implementations can get the SSE environment too? - * How to tell when it does? */ - if((CPUCapFlags&CPU_CAP_SSE)) - __asm__ __volatile__("stmxcsr %0" : "=m" (*&ctl->sse_state)); -#endif - -#ifdef FE_TOWARDZERO - fesetround(FE_TOWARDZERO); -#endif #if defined(__GNUC__) && defined(HAVE_SSE) if((CPUCapFlags&CPU_CAP_SSE)) { - int sseState = ctl->sse_state; - sseState |= 0x6000; /* set round-to-zero */ + __asm__ __volatile__("stmxcsr %0" : "=m" (*&ctl->sse_state)); + unsigned int sseState = ctl->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 #elif defined(HAVE___CONTROL87_2) - int mode; - __control87_2(0, 0, &ctl->state, NULL); - __control87_2(_RC_CHOP, _MCW_RC, &mode, NULL); -#ifdef HAVE_SSE - if((CPUCapFlags&CPU_CAP_SSE)) - { - __control87_2(0, 0, NULL, &ctl->sse_state); - __control87_2(_RC_CHOP|_DN_FLUSH, _MCW_RC|_MCW_DN, NULL, &mode); - } -#endif + __control87_2(0, 0, &ctl->state, &ctl->sse_state); + _control87(_DN_FLUSH, _MCW_DN); #elif defined(HAVE__CONTROLFP) ctl->state = _controlfp(0, 0); - (void)_controlfp(_RC_CHOP, _MCW_RC); + _controlfp(_DN_FLUSH, _MCW_DN); #endif } void RestoreFPUMode(const FPUCtl *ctl) { -#ifdef HAVE_FENV_H - fesetenv(STATIC_CAST(fenv_t, ctl)); #if defined(__GNUC__) && defined(HAVE_SSE) if((CPUCapFlags&CPU_CAP_SSE)) __asm__ __volatile__("ldmxcsr %0" : : "m" (*&ctl->sse_state)); -#endif #elif defined(HAVE___CONTROL87_2) int mode; - __control87_2(ctl->state, _MCW_RC, &mode, NULL); -#ifdef HAVE_SSE - if((CPUCapFlags&CPU_CAP_SSE)) - __control87_2(ctl->sse_state, _MCW_RC|_MCW_DN, NULL, &mode); -#endif + __control87_2(ctl->state, _MCW_DN, &mode, NULL); + __control87_2(ctl->sse_state, _MCW_DN, NULL, &mode); #elif defined(HAVE__CONTROLFP) - _controlfp(ctl->state, _MCW_RC); + _controlfp(ctl->state, _MCW_DN); #endif } static int StringSortCompare(const void *str1, const void *str2) { - return al_string_cmp(*(const_al_string*)str1, *(const_al_string*)str2); + return alstr_cmp(*(const_al_string*)str1, *(const_al_string*)str2); } #ifdef _WIN32 @@ -369,9 +330,8 @@ static WCHAR *strrchrW(WCHAR *str, WCHAR ch) return ret; } -al_string GetProcPath(void) +void GetProcBinary(al_string *path, al_string *fname) { - al_string ret = AL_STRING_INIT_STATIC(); WCHAR *pathname, *sep; DWORD pathlen; DWORD len; @@ -388,23 +348,34 @@ al_string GetProcPath(void) { free(pathname); ERR("Failed to get process name: error %lu\n", GetLastError()); - return ret; + return; } pathname[len] = 0; - if((sep = strrchrW(pathname, '\\'))) + if((sep=strrchrW(pathname, '\\')) != NULL) { - WCHAR *sep2 = strrchrW(pathname, '/'); - if(sep2) *sep2 = 0; - else *sep = 0; + WCHAR *sep2 = strrchrW(sep+1, '/'); + if(sep2) sep = sep2; + } + else + sep = strrchrW(pathname, '/'); + + if(sep) + { + if(path) alstr_copy_wrange(path, pathname, sep); + if(fname) alstr_copy_wcstr(fname, sep+1); + } + else + { + if(path) alstr_clear(path); + if(fname) alstr_copy_wcstr(fname, pathname); } - else if((sep = strrchrW(pathname, '/'))) - *sep = 0; - al_string_copy_wcstr(&ret, pathname); free(pathname); - TRACE("Got: %s\n", al_string_get_cstr(ret)); - return ret; + if(path && fname) + TRACE("Got: %s, %s\n", alstr_get_cstr(*path), alstr_get_cstr(*fname)); + else if(path) TRACE("Got path: %s\n", alstr_get_cstr(*path)); + else if(fname) TRACE("Got filename: %s\n", alstr_get_cstr(*fname)); } @@ -520,13 +491,13 @@ static void DirectorySearch(const char *path, const char *ext, vector_al_string WCHAR *wpath; HANDLE hdl; - al_string_copy_cstr(&pathstr, path); - al_string_append_cstr(&pathstr, "\\*"); - al_string_append_cstr(&pathstr, ext); + alstr_copy_cstr(&pathstr, path); + alstr_append_cstr(&pathstr, "\\*"); + alstr_append_cstr(&pathstr, ext); - TRACE("Searching %s\n", al_string_get_cstr(pathstr)); + TRACE("Searching %s\n", alstr_get_cstr(pathstr)); - wpath = FromUTF8(al_string_get_cstr(pathstr)); + wpath = FromUTF8(alstr_get_cstr(pathstr)); hdl = FindFirstFileW(wpath, &fdata); if(hdl != INVALID_HANDLE_VALUE) @@ -534,10 +505,10 @@ static void DirectorySearch(const char *path, const char *ext, vector_al_string size_t base = VECTOR_SIZE(*results); do { al_string str = AL_STRING_INIT_STATIC(); - al_string_copy_cstr(&str, path); - al_string_append_char(&str, '\\'); - al_string_append_wcstr(&str, fdata.cFileName); - TRACE("Got result %s\n", al_string_get_cstr(str)); + alstr_copy_cstr(&str, path); + alstr_append_char(&str, '\\'); + alstr_append_wcstr(&str, fdata.cFileName); + TRACE("Got result %s\n", alstr_get_cstr(str)); VECTOR_PUSH_BACK(*results, str); } while(FindNextFileW(hdl, &fdata)); FindClose(hdl); @@ -548,7 +519,7 @@ static void DirectorySearch(const char *path, const char *ext, vector_al_string } free(wpath); - al_string_deinit(&pathstr); + alstr_reset(&pathstr); } vector_al_string SearchDataFiles(const char *ext, const char *subdir) @@ -558,21 +529,21 @@ vector_al_string SearchDataFiles(const char *ext, const char *subdir) vector_al_string results = VECTOR_INIT_STATIC(); size_t i; - while(ATOMIC_EXCHANGE(uint, &search_lock, 1) == 1) + while(ATOMIC_EXCHANGE_SEQ(&search_lock, 1) == 1) althrd_yield(); /* If the path is absolute, use it directly. */ if(isalpha(subdir[0]) && subdir[1] == ':' && is_slash(subdir[2])) { al_string path = AL_STRING_INIT_STATIC(); - al_string_copy_cstr(&path, subdir); + alstr_copy_cstr(&path, subdir); #define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0) VECTOR_FOR_EACH(char, path, FIX_SLASH); #undef FIX_SLASH - DirectorySearch(al_string_get_cstr(path), ext, &results); + DirectorySearch(alstr_get_cstr(path), ext, &results); - al_string_deinit(&path); + alstr_reset(&path); } else if(subdir[0] == '\\' && subdir[1] == '\\' && subdir[2] == '?' && subdir[3] == '\\') DirectorySearch(subdir, ext, &results); @@ -584,7 +555,7 @@ vector_al_string SearchDataFiles(const char *ext, const char *subdir) /* Search the app-local directory. */ if((cwdbuf=_wgetenv(L"ALSOFT_LOCAL_PATH")) && *cwdbuf != '\0') { - al_string_copy_wcstr(&path, cwdbuf); + alstr_copy_wcstr(&path, cwdbuf); if(is_slash(VECTOR_BACK(path))) { VECTOR_POP_BACK(path); @@ -592,10 +563,10 @@ vector_al_string SearchDataFiles(const char *ext, const char *subdir) } } else if(!(cwdbuf=_wgetcwd(NULL, 0))) - al_string_copy_cstr(&path, "."); + alstr_copy_cstr(&path, "."); else { - al_string_copy_wcstr(&path, cwdbuf); + alstr_copy_wcstr(&path, cwdbuf); if(is_slash(VECTOR_BACK(path))) { VECTOR_POP_BACK(path); @@ -606,30 +577,30 @@ vector_al_string SearchDataFiles(const char *ext, const char *subdir) #define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0) VECTOR_FOR_EACH(char, path, FIX_SLASH); #undef FIX_SLASH - DirectorySearch(al_string_get_cstr(path), ext, &results); + DirectorySearch(alstr_get_cstr(path), ext, &results); /* Search the local and global data dirs. */ for(i = 0;i < COUNTOF(ids);i++) { - WCHAR buffer[PATH_MAX]; + WCHAR buffer[MAX_PATH]; if(SHGetSpecialFolderPathW(NULL, buffer, ids[i], FALSE) != FALSE) { - al_string_copy_wcstr(&path, buffer); + alstr_copy_wcstr(&path, buffer); if(!is_slash(VECTOR_BACK(path))) - al_string_append_char(&path, '\\'); - al_string_append_cstr(&path, subdir); + alstr_append_char(&path, '\\'); + alstr_append_cstr(&path, subdir); #define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0) VECTOR_FOR_EACH(char, path, FIX_SLASH); #undef FIX_SLASH - DirectorySearch(al_string_get_cstr(path), ext, &results); + DirectorySearch(alstr_get_cstr(path), ext, &results); } } - al_string_deinit(&path); + alstr_reset(&path); } - ATOMIC_STORE(&search_lock, 0); + ATOMIC_STORE_SEQ(&search_lock, 0); return results; } @@ -698,49 +669,103 @@ void UnmapFileMem(const struct FileMapping *mapping) #else -al_string GetProcPath(void) +void GetProcBinary(al_string *path, al_string *fname) { - al_string ret = AL_STRING_INIT_STATIC(); - const char *fname; - char *pathname, *sep; + char *pathname = NULL; size_t pathlen; - ssize_t len; - pathlen = 256; - pathname = malloc(pathlen); - - fname = "/proc/self/exe"; - len = readlink(fname, pathname, pathlen); - if(len == -1 && errno == ENOENT) - { - fname = "/proc/self/file"; - len = readlink(fname, pathname, pathlen); - } - - while(len > 0 && (size_t)len == pathlen) - { - free(pathname); - pathlen <<= 1; - pathname = malloc(pathlen); - len = readlink(fname, pathname, pathlen); - } - if(len <= 0) - { - free(pathname); - WARN("Failed to readlink %s: %s\n", fname, strerror(errno)); - return ret; - } - - pathname[len] = 0; - sep = strrchr(pathname, '/'); - if(sep) - al_string_copy_range(&ret, pathname, sep); +#ifdef __FreeBSD__ + int mib[4] = { CTL_KERN, KERN_PROC_ARGS, getpid() }; + if(sysctl(mib, 3, NULL, &pathlen, NULL, 0) == -1) + WARN("Failed to sysctl kern.procargs.%d: %s\n", mib[2], strerror(errno)); else - al_string_copy_cstr(&ret, pathname); + { + pathname = malloc(pathlen + 1); + sysctl(mib, 3, (void*)pathname, &pathlen, NULL, 0); + pathname[pathlen] = 0; + } +#endif +#ifdef HAVE_PROC_PIDPATH + if(!pathname) + { + const pid_t pid = getpid(); + char procpath[PROC_PIDPATHINFO_MAXSIZE]; + int ret; + + ret = proc_pidpath(pid, procpath, sizeof(procpath)); + if(ret < 1) + { + WARN("proc_pidpath(%d, ...) failed: %s\n", pid, strerror(errno)); + free(pathname); + pathname = NULL; + } + else + { + pathlen = strlen(procpath); + pathname = strdup(procpath); + } + } +#endif + if(!pathname) + { + const char *selfname; + ssize_t len; + + pathlen = 256; + pathname = malloc(pathlen); + + selfname = "/proc/self/exe"; + len = readlink(selfname, pathname, pathlen); + if(len == -1 && errno == ENOENT) + { + selfname = "/proc/self/file"; + len = readlink(selfname, pathname, pathlen); + } + if(len == -1 && errno == ENOENT) + { + selfname = "/proc/curproc/exe"; + len = readlink(selfname, pathname, pathlen); + } + if(len == -1 && errno == ENOENT) + { + selfname = "/proc/curproc/file"; + len = readlink(selfname, pathname, pathlen); + } + + while(len > 0 && (size_t)len == pathlen) + { + free(pathname); + pathlen <<= 1; + pathname = malloc(pathlen); + len = readlink(selfname, pathname, pathlen); + } + if(len <= 0) + { + free(pathname); + WARN("Failed to readlink %s: %s\n", selfname, strerror(errno)); + return; + } + + pathname[len] = 0; + } + + char *sep = strrchr(pathname, '/'); + if(sep) + { + if(path) alstr_copy_range(path, pathname, sep); + if(fname) alstr_copy_cstr(fname, sep+1); + } + else + { + if(path) alstr_clear(path); + if(fname) alstr_copy_cstr(fname, pathname); + } free(pathname); - TRACE("Got: %s\n", al_string_get_cstr(ret)); - return ret; + if(path && fname) + TRACE("Got: %s, %s\n", alstr_get_cstr(*path), alstr_get_cstr(*fname)); + else if(path) TRACE("Got path: %s\n", alstr_get_cstr(*path)); + else if(fname) TRACE("Got filename: %s\n", alstr_get_cstr(*fname)); } @@ -814,11 +839,11 @@ static void DirectorySearch(const char *path, const char *ext, vector_al_string continue; AL_STRING_INIT(str); - al_string_copy_cstr(&str, path); + alstr_copy_cstr(&str, path); if(VECTOR_BACK(str) != '/') - al_string_append_char(&str, '/'); - al_string_append_cstr(&str, dirent->d_name); - TRACE("Got result %s\n", al_string_get_cstr(str)); + alstr_append_char(&str, '/'); + alstr_append_cstr(&str, dirent->d_name); + TRACE("Got result %s\n", alstr_get_cstr(str)); VECTOR_PUSH_BACK(*results, str); } closedir(dir); @@ -834,7 +859,7 @@ vector_al_string SearchDataFiles(const char *ext, const char *subdir) static RefCount search_lock; vector_al_string results = VECTOR_INIT_STATIC(); - while(ATOMIC_EXCHANGE(uint, &search_lock, 1) == 1) + while(ATOMIC_EXCHANGE_SEQ(&search_lock, 1) == 1) althrd_yield(); if(subdir[0] == '/') @@ -843,36 +868,53 @@ vector_al_string SearchDataFiles(const char *ext, const char *subdir) { al_string path = AL_STRING_INIT_STATIC(); const char *str, *next; - char cwdbuf[PATH_MAX]; /* Search the app-local directory. */ if((str=getenv("ALSOFT_LOCAL_PATH")) && *str != '\0') DirectorySearch(str, ext, &results); - else if(getcwd(cwdbuf, sizeof(cwdbuf))) - DirectorySearch(cwdbuf, ext, &results); else - DirectorySearch(".", ext, &results); + { + size_t cwdlen = 256; + char *cwdbuf = malloc(cwdlen); + while(!getcwd(cwdbuf, cwdlen)) + { + free(cwdbuf); + cwdbuf = NULL; + if(errno != ERANGE) + break; + cwdlen <<= 1; + cwdbuf = malloc(cwdlen); + } + if(!cwdbuf) + DirectorySearch(".", ext, &results); + else + { + DirectorySearch(cwdbuf, ext, &results); + free(cwdbuf); + cwdbuf = NULL; + } + } // Search local data dir if((str=getenv("XDG_DATA_HOME")) != NULL && str[0] != '\0') { - al_string_copy_cstr(&path, str); + alstr_copy_cstr(&path, str); if(VECTOR_BACK(path) != '/') - al_string_append_char(&path, '/'); - al_string_append_cstr(&path, subdir); - DirectorySearch(al_string_get_cstr(path), ext, &results); + alstr_append_char(&path, '/'); + alstr_append_cstr(&path, subdir); + DirectorySearch(alstr_get_cstr(path), ext, &results); } else if((str=getenv("HOME")) != NULL && str[0] != '\0') { - al_string_copy_cstr(&path, str); + alstr_copy_cstr(&path, str); if(VECTOR_BACK(path) == '/') { VECTOR_POP_BACK(path); *VECTOR_END(path) = 0; } - al_string_append_cstr(&path, "/.local/share/"); - al_string_append_cstr(&path, subdir); - DirectorySearch(al_string_get_cstr(path), ext, &results); + alstr_append_cstr(&path, "/.local/share/"); + alstr_append_cstr(&path, subdir); + DirectorySearch(alstr_get_cstr(path), ext, &results); } // Search global data dirs @@ -884,26 +926,26 @@ vector_al_string SearchDataFiles(const char *ext, const char *subdir) { next = strchr(str, ':'); if(!next) - al_string_copy_cstr(&path, str); + alstr_copy_cstr(&path, str); else { - al_string_copy_range(&path, str, next); + alstr_copy_range(&path, str, next); ++next; } - if(!al_string_empty(path)) + if(!alstr_empty(path)) { if(VECTOR_BACK(path) != '/') - al_string_append_char(&path, '/'); - al_string_append_cstr(&path, subdir); + alstr_append_char(&path, '/'); + alstr_append_cstr(&path, subdir); - DirectorySearch(al_string_get_cstr(path), ext, &results); + DirectorySearch(alstr_get_cstr(path), ext, &results); } } - al_string_deinit(&path); + alstr_reset(&path); } - ATOMIC_STORE(&search_lock, 0); + ATOMIC_STORE_SEQ(&search_lock, 0); return results; } @@ -977,14 +1019,14 @@ void SetRTPriority(void) } -extern inline void al_string_deinit(al_string *str); -extern inline size_t al_string_length(const_al_string str); -extern inline ALboolean al_string_empty(const_al_string str); -extern inline const al_string_char_type *al_string_get_cstr(const_al_string str); +extern inline void alstr_reset(al_string *str); +extern inline size_t alstr_length(const_al_string str); +extern inline ALboolean alstr_empty(const_al_string str); +extern inline const al_string_char_type *alstr_get_cstr(const_al_string str); -void al_string_clear(al_string *str) +void alstr_clear(al_string *str) { - if(!al_string_empty(*str)) + if(!alstr_empty(*str)) { /* Reserve one more character than the total size of the string. This * is to ensure we have space to add a null terminator in the string @@ -995,8 +1037,8 @@ void al_string_clear(al_string *str) } } -static inline int al_string_compare(const al_string_char_type *str1, size_t str1len, - const al_string_char_type *str2, size_t str2len) +static inline int alstr_compare(const al_string_char_type *str1, size_t str1len, + const al_string_char_type *str2, size_t str2len) { size_t complen = (str1len < str2len) ? str1len : str2len; int ret = memcmp(str1, str2, complen); @@ -1007,20 +1049,20 @@ static inline int al_string_compare(const al_string_char_type *str1, size_t str1 } return ret; } -int al_string_cmp(const_al_string str1, const_al_string str2) +int alstr_cmp(const_al_string str1, const_al_string str2) { - return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1), - &VECTOR_FRONT(str2), al_string_length(str2)); + return alstr_compare(&VECTOR_FRONT(str1), alstr_length(str1), + &VECTOR_FRONT(str2), alstr_length(str2)); } -int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2) +int alstr_cmp_cstr(const_al_string str1, const al_string_char_type *str2) { - return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1), - str2, strlen(str2)); + return alstr_compare(&VECTOR_FRONT(str1), alstr_length(str1), + str2, strlen(str2)); } -void al_string_copy(al_string *str, const_al_string from) +void alstr_copy(al_string *str, const_al_string from) { - size_t len = al_string_length(from); + size_t len = alstr_length(from); size_t i; VECTOR_RESIZE(*str, len, len+1); @@ -1029,7 +1071,7 @@ void al_string_copy(al_string *str, const_al_string from) VECTOR_ELEM(*str, i) = 0; } -void al_string_copy_cstr(al_string *str, const al_string_char_type *from) +void alstr_copy_cstr(al_string *str, const al_string_char_type *from) { size_t len = strlen(from); size_t i; @@ -1040,7 +1082,7 @@ void al_string_copy_cstr(al_string *str, const al_string_char_type *from) VECTOR_ELEM(*str, i) = 0; } -void al_string_copy_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to) +void alstr_copy_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to) { size_t len = to - from; size_t i; @@ -1051,20 +1093,20 @@ void al_string_copy_range(al_string *str, const al_string_char_type *from, const VECTOR_ELEM(*str, i) = 0; } -void al_string_append_char(al_string *str, const al_string_char_type c) +void alstr_append_char(al_string *str, const al_string_char_type c) { - size_t len = al_string_length(*str); + size_t len = alstr_length(*str); VECTOR_RESIZE(*str, len, len+2); VECTOR_PUSH_BACK(*str, c); VECTOR_ELEM(*str, len+1) = 0; } -void al_string_append_cstr(al_string *str, const al_string_char_type *from) +void alstr_append_cstr(al_string *str, const al_string_char_type *from) { size_t len = strlen(from); if(len != 0) { - size_t base = al_string_length(*str); + size_t base = alstr_length(*str); size_t i; VECTOR_RESIZE(*str, base+len, base+len+1); @@ -1074,12 +1116,12 @@ void al_string_append_cstr(al_string *str, const al_string_char_type *from) } } -void al_string_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to) +void alstr_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to) { size_t len = to - from; if(len != 0) { - size_t base = al_string_length(*str); + size_t base = alstr_length(*str); size_t i; VECTOR_RESIZE(*str, base+len, base+len+1); @@ -1090,7 +1132,7 @@ void al_string_append_range(al_string *str, const al_string_char_type *from, con } #ifdef _WIN32 -void al_string_copy_wcstr(al_string *str, const wchar_t *from) +void alstr_copy_wcstr(al_string *str, const wchar_t *from) { int len; if((len=WideCharToMultiByte(CP_UTF8, 0, from, -1, NULL, 0, NULL, NULL)) > 0) @@ -1101,24 +1143,35 @@ void al_string_copy_wcstr(al_string *str, const wchar_t *from) } } -void al_string_append_wcstr(al_string *str, const wchar_t *from) +void alstr_append_wcstr(al_string *str, const wchar_t *from) { int len; if((len=WideCharToMultiByte(CP_UTF8, 0, from, -1, NULL, 0, NULL, NULL)) > 0) { - size_t base = al_string_length(*str); + size_t base = alstr_length(*str); VECTOR_RESIZE(*str, base+len-1, base+len); WideCharToMultiByte(CP_UTF8, 0, from, -1, &VECTOR_ELEM(*str, base), len, NULL, NULL); VECTOR_ELEM(*str, base+len-1) = 0; } } -void al_string_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to) +void alstr_copy_wrange(al_string *str, const wchar_t *from, const wchar_t *to) { int len; if((len=WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), NULL, 0, NULL, NULL)) > 0) { - size_t base = al_string_length(*str); + VECTOR_RESIZE(*str, len, len+1); + WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), &VECTOR_FRONT(*str), len+1, NULL, NULL); + VECTOR_ELEM(*str, len) = 0; + } +} + +void alstr_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to) +{ + int len; + if((len=WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), NULL, 0, NULL, NULL)) > 0) + { + size_t base = alstr_length(*str); VECTOR_RESIZE(*str, base+len, base+len+1); WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), &VECTOR_ELEM(*str, base), len+1, NULL, NULL); VECTOR_ELEM(*str, base+len) = 0; diff --git a/Engine/lib/openal-soft/Alc/hrtf.c b/Engine/lib/openal-soft/Alc/hrtf.c index 02889736c..810530e5c 100644 --- a/Engine/lib/openal-soft/Alc/hrtf.c +++ b/Engine/lib/openal-soft/Alc/hrtf.c @@ -28,8 +28,9 @@ #include "alMain.h" #include "alSource.h" #include "alu.h" -#include "bformatdec.h" #include "hrtf.h" +#include "alconfig.h" +#include "filters/splitter.h" #include "compat.h" #include "almalloc.h" @@ -37,290 +38,447 @@ /* Current data set limits defined by the makehrtf utility. */ #define MIN_IR_SIZE (8) -#define MAX_IR_SIZE (128) +#define MAX_IR_SIZE (512) #define MOD_IR_SIZE (8) +#define MIN_FD_COUNT (1) +#define MAX_FD_COUNT (16) + +#define MIN_FD_DISTANCE (50) +#define MAX_FD_DISTANCE (2500) + #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) + +struct HrtfEntry { + struct HrtfEntry *next; + struct Hrtf *handle; + char filename[]; +}; + static const ALchar magicMarker00[8] = "MinPHR00"; static const ALchar magicMarker01[8] = "MinPHR01"; +static const ALchar magicMarker02[8] = "MinPHR02"; /* First value for pass-through coefficients (remaining are 0), used for omni- * directional sounds. */ -static const ALfloat PassthruCoeff = 32767.0f * 0.707106781187f/*sqrt(0.5)*/; +static const ALfloat PassthruCoeff = 0.707106781187f/*sqrt(0.5)*/; -static struct Hrtf *LoadedHrtfs = NULL; +static ATOMIC_FLAG LoadedHrtfLock = ATOMIC_FLAG_INIT; +static struct HrtfEntry *LoadedHrtfs = NULL; /* Calculate the elevation index given the polar elevation in radians. This - * will return an index between 0 and (evcount - 1). Assumes the FPU is in - * round-to-zero mode. + * will return an index between 0 and (evcount - 1). */ -static ALuint CalcEvIndex(ALuint evcount, ALfloat ev) +static ALsizei CalcEvIndex(ALsizei evcount, ALfloat ev, ALfloat *mu) { - ev = (F_PI_2 + ev) * (evcount-1) / F_PI; - return minu(fastf2u(ev + 0.5f), evcount-1); + ALsizei idx; + ev = (F_PI_2+ev) * (evcount-1) / F_PI; + idx = float2int(ev); + + *mu = ev - idx; + return mini(idx, evcount-1); } /* Calculate the azimuth index given the polar azimuth in radians. This will - * return an index between 0 and (azcount - 1). Assumes the FPU is in round-to- - * zero mode. + * return an index between 0 and (azcount - 1). */ -static ALuint CalcAzIndex(ALuint azcount, ALfloat az) +static ALsizei CalcAzIndex(ALsizei azcount, ALfloat az, ALfloat *mu) { - az = (F_TAU + az) * azcount / F_TAU; - return fastf2u(az + 0.5f) % azcount; + ALsizei idx; + az = (F_TAU+az) * azcount / F_TAU; + + idx = float2int(az); + *mu = az - idx; + return idx % azcount; } /* Calculates static HRIR coefficients and delays for the given polar elevation - * and azimuth in radians. The coefficients are normalized and attenuated by - * the specified gain. + * and azimuth in radians. The coefficients are normalized. */ -void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat spread, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays) +void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat spread, + ALfloat (*restrict coeffs)[2], ALsizei *delays) { - ALuint evidx, azidx, lidx, ridx; - ALuint azcount, evoffset; + ALsizei evidx, azidx, idx[4]; + ALsizei evoffset; + ALfloat emu, amu[2]; + ALfloat blend[4]; ALfloat dirfact; - ALuint i; + ALsizei i, c; dirfact = 1.0f - (spread / F_TAU); - /* Claculate elevation index. */ - evidx = CalcEvIndex(Hrtf->evCount, elevation); - azcount = Hrtf->azCount[evidx]; + /* Claculate the lower elevation index. */ + evidx = CalcEvIndex(Hrtf->evCount, elevation, &emu); evoffset = Hrtf->evOffset[evidx]; - /* Calculate azimuth index. */ - azidx = CalcAzIndex(Hrtf->azCount[evidx], azimuth); + /* Calculate lower azimuth index. */ + azidx= CalcAzIndex(Hrtf->azCount[evidx], azimuth, &amu[0]); - /* Calculate the HRIR indices for left and right channels. */ - lidx = evoffset + azidx; - ridx = evoffset + ((azcount-azidx) % azcount); - - /* Calculate the HRIR delays. */ - delays[0] = fastf2u(Hrtf->delays[lidx]*dirfact + 0.5f) << HRTFDELAY_BITS; - delays[1] = fastf2u(Hrtf->delays[ridx]*dirfact + 0.5f) << HRTFDELAY_BITS; - - /* Calculate the sample offsets for the HRIR indices. */ - lidx *= Hrtf->irSize; - ridx *= Hrtf->irSize; - - /* Calculate the normalized and attenuated HRIR coefficients. Zero the - * coefficients if gain is too low. - */ - if(gain > 0.0001f) + /* Calculate the lower HRIR indices. */ + idx[0] = evoffset + azidx; + idx[1] = evoffset + ((azidx+1) % Hrtf->azCount[evidx]); + if(evidx < Hrtf->evCount-1) { - gain /= 32767.0f; + /* Increment elevation to the next (upper) index. */ + evidx++; + evoffset = Hrtf->evOffset[evidx]; - i = 0; - coeffs[i][0] = lerp(PassthruCoeff, Hrtf->coeffs[lidx+i], dirfact)*gain; - coeffs[i][1] = lerp(PassthruCoeff, Hrtf->coeffs[ridx+i], dirfact)*gain; - for(i = 1;i < Hrtf->irSize;i++) - { - coeffs[i][0] = Hrtf->coeffs[lidx+i]*gain * dirfact; - coeffs[i][1] = Hrtf->coeffs[ridx+i]*gain * dirfact; - } + /* Calculate upper azimuth index. */ + azidx = CalcAzIndex(Hrtf->azCount[evidx], azimuth, &amu[1]); + + /* Calculate the upper HRIR indices. */ + idx[2] = evoffset + azidx; + idx[3] = evoffset + ((azidx+1) % Hrtf->azCount[evidx]); } else { + /* If the lower elevation is the top index, the upper elevation is the + * same as the lower. + */ + amu[1] = amu[0]; + idx[2] = idx[0]; + idx[3] = idx[1]; + } + + /* Calculate bilinear blending weights, attenuated according to the + * directional panning factor. + */ + blend[0] = (1.0f-emu) * (1.0f-amu[0]) * dirfact; + blend[1] = (1.0f-emu) * ( amu[0]) * dirfact; + blend[2] = ( emu) * (1.0f-amu[1]) * dirfact; + blend[3] = ( emu) * ( amu[1]) * dirfact; + + /* Calculate the blended HRIR delays. */ + delays[0] = float2int( + 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] + 0.5f + ); + delays[1] = float2int( + 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] + 0.5f + ); + + /* Calculate the sample offsets for the HRIR indices. */ + idx[0] *= Hrtf->irSize; + idx[1] *= Hrtf->irSize; + idx[2] *= Hrtf->irSize; + idx[3] *= Hrtf->irSize; + + ASSUME(Hrtf->irSize >= MIN_IR_SIZE && (Hrtf->irSize%MOD_IR_SIZE) == 0); + coeffs = ASSUME_ALIGNED(coeffs, 16); + /* Calculate the blended HRIR coefficients. */ + coeffs[0][0] = PassthruCoeff * (1.0f-dirfact); + coeffs[0][1] = PassthruCoeff * (1.0f-dirfact); + for(i = 1;i < Hrtf->irSize;i++) + { + coeffs[i][0] = 0.0f; + coeffs[i][1] = 0.0f; + } + for(c = 0;c < 4;c++) + { + const ALfloat (*restrict srccoeffs)[2] = ASSUME_ALIGNED(Hrtf->coeffs+idx[c], 16); for(i = 0;i < Hrtf->irSize;i++) { - coeffs[i][0] = 0.0f; - coeffs[i][1] = 0.0f; + coeffs[i][0] += srccoeffs[i][0] * blend[c]; + coeffs[i][1] += srccoeffs[i][1] * blend[c]; } } } -ALuint BuildBFormatHrtf(const struct Hrtf *Hrtf, ALfloat (*coeffs)[HRIR_LENGTH][2], ALuint NumChannels) +void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei NumChannels, const struct AngularPoint *AmbiPoints, const ALfloat (*restrict AmbiMatrix)[MAX_AMBI_COEFFS], ALsizei AmbiCount, const ALfloat *restrict AmbiOrderHFGain) { - static const struct { - ALfloat elevation; - ALfloat azimuth; - } Ambi3DPoints[14] = { - { DEG2RAD( 90.0f), DEG2RAD( 0.0f) }, - { DEG2RAD( 35.0f), DEG2RAD( -45.0f) }, - { DEG2RAD( 35.0f), DEG2RAD( 45.0f) }, - { DEG2RAD( 35.0f), DEG2RAD( 135.0f) }, - { DEG2RAD( 35.0f), DEG2RAD(-135.0f) }, - { DEG2RAD( 0.0f), DEG2RAD( 0.0f) }, - { DEG2RAD( 0.0f), DEG2RAD( 90.0f) }, - { DEG2RAD( 0.0f), DEG2RAD( 180.0f) }, - { DEG2RAD( 0.0f), DEG2RAD( -90.0f) }, - { DEG2RAD(-35.0f), DEG2RAD( -45.0f) }, - { DEG2RAD(-35.0f), DEG2RAD( 45.0f) }, - { DEG2RAD(-35.0f), DEG2RAD( 135.0f) }, - { DEG2RAD(-35.0f), DEG2RAD(-135.0f) }, - { DEG2RAD(-90.0f), DEG2RAD( 0.0f) }, - }; - static const ALfloat Ambi3DMatrix[14][2][MAX_AMBI_COEFFS] = { - { { 0.078851598f, 0.000000000f, 0.070561967f, 0.000000000f }, { 0.0269973975f, 0.0000000000f, 0.0467610443f, 0.0000000000f } }, - { { 0.124051278f, 0.059847972f, 0.059847972f, 0.059847972f }, { 0.0269973975f, 0.0269973975f, 0.0269973975f, 0.0269973975f } }, - { { 0.124051278f, -0.059847972f, 0.059847972f, 0.059847972f }, { 0.0269973975f, -0.0269973975f, 0.0269973975f, 0.0269973975f } }, - { { 0.124051278f, -0.059847972f, 0.059847972f, -0.059847972f }, { 0.0269973975f, -0.0269973975f, 0.0269973975f, -0.0269973975f } }, - { { 0.124051278f, 0.059847972f, 0.059847972f, -0.059847972f }, { 0.0269973975f, 0.0269973975f, 0.0269973975f, -0.0269973975f } }, - { { 0.078851598f, 0.000000000f, 0.000000000f, 0.070561967f }, { 0.0269973975f, 0.0000000000f, 0.0000000000f, 0.0467610443f } }, - { { 0.078851598f, -0.070561967f, 0.000000000f, 0.000000000f }, { 0.0269973975f, -0.0467610443f, 0.0000000000f, 0.0000000000f } }, - { { 0.078851598f, 0.000000000f, 0.000000000f, -0.070561967f }, { 0.0269973975f, 0.0000000000f, 0.0000000000f, -0.0467610443f } }, - { { 0.078851598f, 0.070561967f, 0.000000000f, 0.000000000f }, { 0.0269973975f, 0.0467610443f, 0.0000000000f, 0.0000000000f } }, - { { 0.124051278f, 0.059847972f, -0.059847972f, 0.059847972f }, { 0.0269973975f, 0.0269973975f, -0.0269973975f, 0.0269973975f } }, - { { 0.124051278f, -0.059847972f, -0.059847972f, 0.059847972f }, { 0.0269973975f, -0.0269973975f, -0.0269973975f, 0.0269973975f } }, - { { 0.124051278f, -0.059847972f, -0.059847972f, -0.059847972f }, { 0.0269973975f, -0.0269973975f, -0.0269973975f, -0.0269973975f } }, - { { 0.124051278f, 0.059847972f, -0.059847972f, -0.059847972f }, { 0.0269973975f, 0.0269973975f, -0.0269973975f, -0.0269973975f } }, - { { 0.078851598f, 0.000000000f, -0.070561967f, 0.000000000f }, { 0.0269973975f, 0.0000000000f, -0.0467610443f, 0.0000000000f } }, - }; -/* Change this to 2 for dual-band HRTF processing. May require a higher quality +/* Set this to 2 for dual-band HRTF processing. May require a higher quality * band-splitter, or better calculation of the new IR length to deal with the * tail generated by the filter. */ #define NUM_BANDS 2 BandSplitter splitter; + ALdouble (*tmpres)[HRIR_LENGTH][2]; + ALsizei idx[HRTF_AMBI_MAX_CHANNELS]; + ALsizei min_delay = HRTF_HISTORY_LENGTH; ALfloat temps[3][HRIR_LENGTH]; - ALuint lidx[14], ridx[14]; - ALuint min_delay = HRTF_HISTORY_LENGTH; - ALuint max_length = 0; - ALuint i, j, c, b; + ALsizei max_length = 0; + ALsizei i, c, b; - assert(NumChannels == 4); - - for(c = 0;c < COUNTOF(Ambi3DPoints);c++) + for(c = 0;c < AmbiCount;c++) { ALuint evidx, azidx; ALuint evoffset; ALuint azcount; /* Calculate elevation index. */ - evidx = (ALuint)floorf((F_PI_2 + Ambi3DPoints[c].elevation) * - (Hrtf->evCount-1)/F_PI + 0.5f); - evidx = minu(evidx, Hrtf->evCount-1); + evidx = (ALsizei)((F_PI_2+AmbiPoints[c].Elev) * (Hrtf->evCount-1) / F_PI + 0.5f); + evidx = clampi(evidx, 0, Hrtf->evCount-1); azcount = Hrtf->azCount[evidx]; evoffset = Hrtf->evOffset[evidx]; /* Calculate azimuth index for this elevation. */ - azidx = (ALuint)floorf((F_TAU+Ambi3DPoints[c].azimuth) * - azcount/F_TAU + 0.5f) % azcount; + azidx = (ALsizei)((F_TAU+AmbiPoints[c].Azim) * azcount / F_TAU + 0.5f) % azcount; /* Calculate indices for left and right channels. */ - lidx[c] = evoffset + azidx; - ridx[c] = evoffset + ((azcount-azidx) % azcount); + idx[c] = evoffset + azidx; - min_delay = minu(min_delay, minu(Hrtf->delays[lidx[c]], Hrtf->delays[ridx[c]])); + min_delay = mini(min_delay, mini(Hrtf->delays[idx[c]][0], Hrtf->delays[idx[c]][1])); } + tmpres = al_calloc(16, NumChannels * sizeof(*tmpres)); + memset(temps, 0, sizeof(temps)); bandsplit_init(&splitter, 400.0f / (ALfloat)Hrtf->sampleRate); - for(c = 0;c < COUNTOF(Ambi3DMatrix);c++) + for(c = 0;c < AmbiCount;c++) { - const ALshort *fir; - ALuint delay; + const ALfloat (*fir)[2] = &Hrtf->coeffs[idx[c] * Hrtf->irSize]; + ALsizei ldelay = Hrtf->delays[idx[c]][0] - min_delay; + ALsizei rdelay = Hrtf->delays[idx[c]][1] - min_delay; - /* Convert the left FIR from shorts to float */ - fir = &Hrtf->coeffs[lidx[c] * Hrtf->irSize]; if(NUM_BANDS == 1) { - for(i = 0;i < Hrtf->irSize;i++) - temps[0][i] = fir[i] / 32767.0f; + max_length = maxi(max_length, + mini(maxi(ldelay, rdelay) + Hrtf->irSize, HRIR_LENGTH) + ); + + for(i = 0;i < NumChannels;++i) + { + ALdouble mult = (ALdouble)AmbiOrderHFGain[(ALsizei)sqrt(i)] * AmbiMatrix[c][i]; + ALsizei lidx = ldelay, ridx = rdelay; + ALsizei j = 0; + while(lidx < HRIR_LENGTH && ridx < HRIR_LENGTH && j < Hrtf->irSize) + { + tmpres[i][lidx++][0] += fir[j][0] * mult; + tmpres[i][ridx++][1] += fir[j][1] * mult; + j++; + } + } } else { + /* Increase the IR size by 2/3rds to account for the tail generated + * by the band-split filter. + */ + const ALsizei irsize = mini(Hrtf->irSize*5/3, HRIR_LENGTH); + + max_length = maxi(max_length, + mini(maxi(ldelay, rdelay) + irsize, HRIR_LENGTH) + ); + /* Band-split left HRIR into low and high frequency responses. */ bandsplit_clear(&splitter); for(i = 0;i < Hrtf->irSize;i++) - temps[2][i] = fir[i] / 32767.0f; + temps[2][i] = fir[i][0]; bandsplit_process(&splitter, temps[0], temps[1], temps[2], HRIR_LENGTH); - } - /* Add to the left output coefficients with the specified delay. */ - delay = Hrtf->delays[lidx[c]] - min_delay; - for(i = 0;i < NumChannels;++i) - { - for(b = 0;b < NUM_BANDS;b++) + /* Apply left ear response with delay. */ + for(i = 0;i < NumChannels;++i) { - ALuint k = 0; - for(j = delay;j < HRIR_LENGTH;++j) - coeffs[i][j][0] += temps[b][k++] * Ambi3DMatrix[c][b][i]; + ALfloat hfgain = AmbiOrderHFGain[(ALsizei)sqrt(i)]; + for(b = 0;b < NUM_BANDS;b++) + { + ALdouble mult = AmbiMatrix[c][i] * (ALdouble)((b==0) ? hfgain : 1.0); + ALsizei lidx = ldelay; + ALsizei j = 0; + while(lidx < HRIR_LENGTH) + tmpres[i][lidx++][0] += temps[b][j++] * mult; + } } - } - max_length = maxu(max_length, minu(delay + Hrtf->irSize, HRIR_LENGTH)); - /* Convert the right FIR from shorts to float */ - fir = &Hrtf->coeffs[ridx[c] * Hrtf->irSize]; - if(NUM_BANDS == 1) - { - for(i = 0;i < Hrtf->irSize;i++) - temps[0][i] = fir[i] / 32767.0f; - } - else - { /* Band-split right HRIR into low and high frequency responses. */ bandsplit_clear(&splitter); for(i = 0;i < Hrtf->irSize;i++) - temps[2][i] = fir[i] / 32767.0f; + temps[2][i] = fir[i][1]; bandsplit_process(&splitter, temps[0], temps[1], temps[2], HRIR_LENGTH); - } - /* Add to the right output coefficients with the specified delay. */ - delay = Hrtf->delays[ridx[c]] - min_delay; - for(i = 0;i < NumChannels;++i) - { - for(b = 0;b < NUM_BANDS;b++) + /* Apply right ear response with delay. */ + for(i = 0;i < NumChannels;++i) { - ALuint k = 0; - for(j = delay;j < HRIR_LENGTH;++j) - coeffs[i][j][1] += temps[b][k++] * Ambi3DMatrix[c][b][i]; + ALfloat hfgain = AmbiOrderHFGain[(ALsizei)sqrt(i)]; + for(b = 0;b < NUM_BANDS;b++) + { + ALdouble mult = AmbiMatrix[c][i] * (ALdouble)((b==0) ? hfgain : 1.0); + ALsizei ridx = rdelay; + ALsizei j = 0; + while(ridx < HRIR_LENGTH) + tmpres[i][ridx++][1] += temps[b][j++] * mult; + } } } - max_length = maxu(max_length, minu(delay + Hrtf->irSize, HRIR_LENGTH)); } - TRACE("Skipped min delay: %u, new combined length: %u\n", min_delay, max_length); -#undef NUM_BANDS + /* Round up to the next IR size multiple. */ + max_length += MOD_IR_SIZE-1; + max_length -= max_length%MOD_IR_SIZE; - return max_length; + for(i = 0;i < NumChannels;++i) + { + int idx; + for(idx = 0;idx < HRIR_LENGTH;idx++) + { + state->Chan[i].Coeffs[idx][0] = (ALfloat)tmpres[i][idx][0]; + state->Chan[i].Coeffs[idx][1] = (ALfloat)tmpres[i][idx][1]; + } + } + + al_free(tmpres); + tmpres = NULL; + + TRACE("Skipped delay: %d, new FIR length: %d\n", min_delay, max_length); + state->IrSize = max_length; +#undef NUM_BANDS } -static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const_al_string filename) +static struct Hrtf *CreateHrtfStore(ALuint rate, ALsizei irSize, + ALfloat distance, ALsizei evCount, ALsizei irCount, const ALubyte *azCount, + const ALushort *evOffset, const ALfloat (*coeffs)[2], const ALubyte (*delays)[2], + const char *filename) +{ + struct Hrtf *Hrtf; + size_t total; + + total = sizeof(struct Hrtf); + total += sizeof(Hrtf->azCount[0])*evCount; + total = RoundUp(total, sizeof(ALushort)); /* Align for ushort fields */ + total += sizeof(Hrtf->evOffset[0])*evCount; + total = RoundUp(total, 16); /* Align for coefficients using SIMD */ + total += sizeof(Hrtf->coeffs[0])*irSize*irCount; + total += sizeof(Hrtf->delays[0])*irCount; + + Hrtf = al_calloc(16, total); + if(Hrtf == NULL) + ERR("Out of memory allocating storage for %s.\n", filename); + else + { + uintptr_t offset = sizeof(struct Hrtf); + char *base = (char*)Hrtf; + ALushort *_evOffset; + ALubyte *_azCount; + ALubyte (*_delays)[2]; + ALfloat (*_coeffs)[2]; + ALsizei i; + + InitRef(&Hrtf->ref, 0); + Hrtf->sampleRate = rate; + Hrtf->irSize = irSize; + Hrtf->distance = distance; + Hrtf->evCount = evCount; + + /* Set up pointers to storage following the main HRTF struct. */ + _azCount = (ALubyte*)(base + offset); + offset += sizeof(_azCount[0])*evCount; + + offset = RoundUp(offset, sizeof(ALushort)); /* Align for ushort fields */ + _evOffset = (ALushort*)(base + offset); + offset += sizeof(_evOffset[0])*evCount; + + offset = RoundUp(offset, 16); /* Align for coefficients using SIMD */ + _coeffs = (ALfloat(*)[2])(base + offset); + offset += sizeof(_coeffs[0])*irSize*irCount; + + _delays = (ALubyte(*)[2])(base + offset); + offset += sizeof(_delays[0])*irCount; + + assert(offset == total); + + /* Copy input data to storage. */ + for(i = 0;i < evCount;i++) _azCount[i] = azCount[i]; + for(i = 0;i < evCount;i++) _evOffset[i] = evOffset[i]; + for(i = 0;i < irSize*irCount;i++) + { + _coeffs[i][0] = coeffs[i][0]; + _coeffs[i][1] = coeffs[i][1]; + } + for(i = 0;i < irCount;i++) + { + _delays[i][0] = delays[i][0]; + _delays[i][1] = delays[i][1]; + } + + /* Finally, assign the storage pointers. */ + Hrtf->azCount = _azCount; + Hrtf->evOffset = _evOffset; + Hrtf->coeffs = _coeffs; + Hrtf->delays = _delays; + } + + return Hrtf; +} + +static ALubyte GetLE_ALubyte(const ALubyte **data, size_t *len) +{ + ALubyte ret = (*data)[0]; + *data += 1; *len -= 1; + return ret; +} + +static ALshort GetLE_ALshort(const ALubyte **data, size_t *len) +{ + ALshort ret = (*data)[0] | ((*data)[1]<<8); + *data += 2; *len -= 2; + return ret; +} + +static ALushort GetLE_ALushort(const ALubyte **data, size_t *len) +{ + ALushort ret = (*data)[0] | ((*data)[1]<<8); + *data += 2; *len -= 2; + return ret; +} + +static ALint GetLE_ALint24(const ALubyte **data, size_t *len) +{ + ALint ret = (*data)[0] | ((*data)[1]<<8) | ((*data)[2]<<16); + *data += 3; *len -= 3; + return (ret^0x800000) - 0x800000; +} + +static ALuint GetLE_ALuint(const ALubyte **data, size_t *len) +{ + ALuint ret = (*data)[0] | ((*data)[1]<<8) | ((*data)[2]<<16) | ((*data)[3]<<24); + *data += 4; *len -= 4; + return ret; +} + +static const ALubyte *Get_ALubytePtr(const ALubyte **data, size_t *len, size_t size) +{ + const ALubyte *ret = *data; + *data += size; *len -= size; + return ret; +} + +static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const char *filename) { - const ALubyte maxDelay = HRTF_HISTORY_LENGTH-1; struct Hrtf *Hrtf = NULL; ALboolean failed = AL_FALSE; - ALuint rate = 0, irCount = 0; + ALuint rate = 0; + ALushort irCount = 0; ALushort irSize = 0; ALubyte evCount = 0; ALubyte *azCount = NULL; ALushort *evOffset = NULL; - ALshort *coeffs = NULL; - const ALubyte *delays = NULL; - ALuint i, j; + ALfloat (*coeffs)[2] = NULL; + ALubyte (*delays)[2] = NULL; + ALsizei i, j; if(datalen < 9) { - ERR("Unexpected end of %s data (req %d, rem "SZFMT")\n", - al_string_get_cstr(filename), 9, datalen); + ERR("Unexpected end of %s data (req %d, rem "SZFMT")\n", filename, 9, datalen); return NULL; } - rate = *(data++); - rate |= *(data++)<<8; - rate |= *(data++)<<16; - rate |= *(data++)<<24; - datalen -= 4; + rate = GetLE_ALuint(&data, &datalen); - irCount = *(data++); - irCount |= *(data++)<<8; - datalen -= 2; + irCount = GetLE_ALushort(&data, &datalen); - irSize = *(data++); - irSize |= *(data++)<<8; - datalen -= 2; + irSize = GetLE_ALushort(&data, &datalen); - evCount = *(data++); - datalen -= 1; + evCount = GetLE_ALubyte(&data, &datalen); if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE)) { @@ -337,10 +495,9 @@ static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const_al_str if(failed) return NULL; - if(datalen < evCount*2) + if(datalen < evCount*2u) { - ERR("Unexpected end of %s data (req %d, rem "SZFMT")\n", - al_string_get_cstr(filename), evCount*2, datalen); + ERR("Unexpected end of %s data (req %d, rem "SZFMT")\n", filename, evCount*2, datalen); return NULL; } @@ -354,14 +511,10 @@ static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const_al_str if(!failed) { - evOffset[0] = *(data++); - evOffset[0] |= *(data++)<<8; - datalen -= 2; + evOffset[0] = GetLE_ALushort(&data, &datalen); for(i = 1;i < evCount;i++) { - evOffset[i] = *(data++); - evOffset[i] |= *(data++)<<8; - datalen -= 2; + evOffset[i] = GetLE_ALushort(&data, &datalen); if(evOffset[i] <= evOffset[i-1]) { ERR("Invalid evOffset: evOffset[%d]=%d (last=%d)\n", @@ -396,7 +549,8 @@ static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const_al_str if(!failed) { coeffs = malloc(sizeof(coeffs[0])*irSize*irCount); - if(coeffs == NULL) + delays = malloc(sizeof(delays[0])*irCount); + if(coeffs == NULL || delays == NULL) { ERR("Out of memory.\n"); failed = AL_TRUE; @@ -409,31 +563,25 @@ static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const_al_str if(datalen < reqsize) { ERR("Unexpected end of %s data (req "SZFMT", rem "SZFMT")\n", - al_string_get_cstr(filename), reqsize, datalen); + filename, reqsize, datalen); failed = AL_TRUE; } } if(!failed) { - for(i = 0;i < irCount*irSize;i+=irSize) - { - for(j = 0;j < irSize;j++) - { - coeffs[i+j] = *(data++); - coeffs[i+j] |= *(data++)<<8; - datalen -= 2; - } - } - - delays = data; - data += irCount; - datalen -= irCount; for(i = 0;i < irCount;i++) { - if(delays[i] > maxDelay) + for(j = 0;j < irSize;j++) + coeffs[i*irSize + j][0] = GetLE_ALshort(&data, &datalen) / 32768.0f; + } + + for(i = 0;i < irCount;i++) + { + delays[i][0] = GetLE_ALubyte(&data, &datalen); + if(delays[i][0] > MAX_HRIR_DELAY) { - ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i], maxDelay); + ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i][0], MAX_HRIR_DELAY); failed = AL_TRUE; } } @@ -441,82 +589,59 @@ static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const_al_str if(!failed) { - size_t total = sizeof(struct Hrtf); - total += sizeof(azCount[0])*evCount; - total = (total+1)&~1; /* Align for (u)short fields */ - total += sizeof(evOffset[0])*evCount; - total += sizeof(coeffs[0])*irSize*irCount; - total += sizeof(delays[0])*irCount; - total += al_string_length(filename)+1; - - Hrtf = al_calloc(16, total); - if(Hrtf == NULL) + /* Mirror the left ear responses to the right ear. */ + for(i = 0;i < evCount;i++) { - ERR("Out of memory.\n"); - failed = AL_TRUE; + ALushort evoffset = evOffset[i]; + ALubyte azcount = azCount[i]; + for(j = 0;j < azcount;j++) + { + ALsizei lidx = evoffset + j; + ALsizei ridx = evoffset + ((azcount-j) % azcount); + ALsizei k; + + for(k = 0;k < irSize;k++) + coeffs[ridx*irSize + k][1] = coeffs[lidx*irSize + k][0]; + delays[ridx][1] = delays[lidx][0]; + } } - } - if(!failed) - { - char *base = (char*)Hrtf; - uintptr_t offset = sizeof(*Hrtf); - - Hrtf->sampleRate = rate; - Hrtf->irSize = irSize; - Hrtf->evCount = evCount; - Hrtf->azCount = ((ALubyte*)(base + offset)); offset += evCount*sizeof(Hrtf->azCount[0]); - offset = (offset+1)&~1; /* Align for (u)short fields */ - Hrtf->evOffset = ((ALushort*)(base + offset)); offset += evCount*sizeof(Hrtf->evOffset[0]); - Hrtf->coeffs = ((ALshort*)(base + offset)); offset += irSize*irCount*sizeof(Hrtf->coeffs[0]); - Hrtf->delays = ((ALubyte*)(base + offset)); offset += irCount*sizeof(Hrtf->delays[0]); - Hrtf->filename = ((char*)(base + offset)); - Hrtf->next = NULL; - - memcpy((void*)Hrtf->azCount, azCount, sizeof(azCount[0])*evCount); - memcpy((void*)Hrtf->evOffset, evOffset, sizeof(evOffset[0])*evCount); - memcpy((void*)Hrtf->coeffs, coeffs, sizeof(coeffs[0])*irSize*irCount); - memcpy((void*)Hrtf->delays, delays, sizeof(delays[0])*irCount); - memcpy((void*)Hrtf->filename, al_string_get_cstr(filename), al_string_length(filename)+1); + Hrtf = CreateHrtfStore(rate, irSize, 0.0f, evCount, irCount, azCount, + evOffset, coeffs, delays, filename); } free(azCount); free(evOffset); free(coeffs); + free(delays); return Hrtf; } -static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const_al_string filename) +static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const char *filename) { - const ALubyte maxDelay = HRTF_HISTORY_LENGTH-1; struct Hrtf *Hrtf = NULL; ALboolean failed = AL_FALSE; - ALuint rate = 0, irCount = 0; - ALubyte irSize = 0, evCount = 0; + ALuint rate = 0; + ALushort irCount = 0; + ALushort irSize = 0; + ALubyte evCount = 0; const ALubyte *azCount = NULL; ALushort *evOffset = NULL; - ALshort *coeffs = NULL; - const ALubyte *delays = NULL; - ALuint i, j; + ALfloat (*coeffs)[2] = NULL; + ALubyte (*delays)[2] = NULL; + ALsizei i, j; if(datalen < 6) { - ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", - al_string_get_cstr(filename), 6, datalen); + ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", filename, 6, datalen); return NULL; } - rate = *(data++); - rate |= *(data++)<<8; - rate |= *(data++)<<16; - rate |= *(data++)<<24; - datalen -= 4; + rate = GetLE_ALuint(&data, &datalen); - irSize = *(data++); - datalen -= 1; + irSize = GetLE_ALubyte(&data, &datalen); - evCount = *(data++); - datalen -= 1; + evCount = GetLE_ALubyte(&data, &datalen); if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE)) { @@ -535,14 +660,11 @@ static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const_al_str if(datalen < evCount) { - ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", - al_string_get_cstr(filename), evCount, datalen); + ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", filename, evCount, datalen); return NULL; } - azCount = data; - data += evCount; - datalen -= evCount; + azCount = Get_ALubytePtr(&data, &datalen, evCount); evOffset = malloc(sizeof(evOffset[0])*evCount); if(azCount == NULL || evOffset == NULL) @@ -575,7 +697,8 @@ static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const_al_str } coeffs = malloc(sizeof(coeffs[0])*irSize*irCount); - if(coeffs == NULL) + delays = malloc(sizeof(delays[0])*irCount); + if(coeffs == NULL || delays == NULL) { ERR("Out of memory.\n"); failed = AL_TRUE; @@ -588,33 +711,25 @@ static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const_al_str if(datalen < reqsize) { ERR("Unexpected end of %s data (req "SZFMT", rem "SZFMT"\n", - al_string_get_cstr(filename), reqsize, datalen); + filename, reqsize, datalen); failed = AL_TRUE; } } if(!failed) { - for(i = 0;i < irCount*irSize;i+=irSize) - { - for(j = 0;j < irSize;j++) - { - ALshort coeff; - coeff = *(data++); - coeff |= *(data++)<<8; - datalen -= 2; - coeffs[i+j] = coeff; - } - } - - delays = data; - data += irCount; - datalen -= irCount; for(i = 0;i < irCount;i++) { - if(delays[i] > maxDelay) + for(j = 0;j < irSize;j++) + coeffs[i*irSize + j][0] = GetLE_ALshort(&data, &datalen) / 32768.0f; + } + + for(i = 0;i < irCount;i++) + { + delays[i][0] = GetLE_ALubyte(&data, &datalen); + if(delays[i][0] > MAX_HRIR_DELAY) { - ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i], maxDelay); + ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i][0], MAX_HRIR_DELAY); failed = AL_TRUE; } } @@ -622,16 +737,163 @@ static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const_al_str if(!failed) { - size_t total = sizeof(struct Hrtf); - total += sizeof(azCount[0])*evCount; - total = (total+1)&~1; /* Align for (u)short fields */ - total += sizeof(evOffset[0])*evCount; - total += sizeof(coeffs[0])*irSize*irCount; - total += sizeof(delays[0])*irCount; - total += al_string_length(filename)+1; + /* Mirror the left ear responses to the right ear. */ + for(i = 0;i < evCount;i++) + { + ALushort evoffset = evOffset[i]; + ALubyte azcount = azCount[i]; + for(j = 0;j < azcount;j++) + { + ALsizei lidx = evoffset + j; + ALsizei ridx = evoffset + ((azcount-j) % azcount); + ALsizei k; - Hrtf = al_calloc(16, total); - if(Hrtf == NULL) + for(k = 0;k < irSize;k++) + coeffs[ridx*irSize + k][1] = coeffs[lidx*irSize + k][0]; + delays[ridx][1] = delays[lidx][0]; + } + } + + Hrtf = CreateHrtfStore(rate, irSize, 0.0f, evCount, irCount, azCount, + evOffset, coeffs, delays, filename); + } + + free(evOffset); + free(coeffs); + free(delays); + return Hrtf; +} + +#define SAMPLETYPE_S16 0 +#define SAMPLETYPE_S24 1 + +#define CHANTYPE_LEFTONLY 0 +#define CHANTYPE_LEFTRIGHT 1 + +static struct Hrtf *LoadHrtf02(const ALubyte *data, size_t datalen, const char *filename) +{ + struct Hrtf *Hrtf = NULL; + ALboolean failed = AL_FALSE; + ALuint rate = 0; + ALubyte sampleType; + ALubyte channelType; + ALushort irCount = 0; + ALushort irSize = 0; + ALubyte fdCount = 0; + ALushort distance = 0; + ALubyte evCount = 0; + const ALubyte *azCount = NULL; + ALushort *evOffset = NULL; + ALfloat (*coeffs)[2] = NULL; + ALubyte (*delays)[2] = NULL; + ALsizei i, j; + + if(datalen < 8) + { + ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", filename, 8, datalen); + return NULL; + } + + rate = GetLE_ALuint(&data, &datalen); + sampleType = GetLE_ALubyte(&data, &datalen); + channelType = GetLE_ALubyte(&data, &datalen); + + irSize = GetLE_ALubyte(&data, &datalen); + + fdCount = GetLE_ALubyte(&data, &datalen); + + 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) + { + ERR("Multiple field-depths not supported: fdCount=%d (%d to %d)\n", + evCount, MIN_FD_COUNT, MAX_FD_COUNT); + failed = AL_TRUE; + } + if(failed) + return NULL; + + for(i = 0;i < fdCount;i++) + { + if(datalen < 3) + { + ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", filename, 3, datalen); + return NULL; + } + + distance = GetLE_ALushort(&data, &datalen); + if(distance < MIN_FD_DISTANCE || distance > MAX_FD_DISTANCE) + { + ERR("Unsupported field distance: distance=%d (%dmm to %dmm)\n", + distance, MIN_FD_DISTANCE, MAX_FD_DISTANCE); + failed = AL_TRUE; + } + + evCount = GetLE_ALubyte(&data, &datalen); + if(evCount < MIN_EV_COUNT || evCount > MAX_EV_COUNT) + { + ERR("Unsupported elevation count: evCount=%d (%d to %d)\n", + evCount, MIN_EV_COUNT, MAX_EV_COUNT); + failed = AL_TRUE; + } + if(failed) + return NULL; + + if(datalen < evCount) + { + ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", filename, evCount, datalen); + return NULL; + } + + azCount = Get_ALubytePtr(&data, &datalen, evCount); + for(j = 0;j < evCount;j++) + { + if(azCount[j] < MIN_AZ_COUNT || azCount[j] > MAX_AZ_COUNT) + { + ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n", + j, azCount[j], MIN_AZ_COUNT, MAX_AZ_COUNT); + failed = AL_TRUE; + } + } + } + if(failed) + return NULL; + + evOffset = malloc(sizeof(evOffset[0])*evCount); + if(azCount == NULL || evOffset == NULL) + { + ERR("Out of memory.\n"); + failed = AL_TRUE; + } + + if(!failed) + { + evOffset[0] = 0; + irCount = azCount[0]; + for(i = 1;i < evCount;i++) + { + evOffset[i] = evOffset[i-1] + azCount[i-1]; + irCount += azCount[i]; + } + + coeffs = malloc(sizeof(coeffs[0])*irSize*irCount); + delays = malloc(sizeof(delays[0])*irCount); + if(coeffs == NULL || delays == NULL) { ERR("Out of memory.\n"); failed = AL_TRUE; @@ -640,108 +902,164 @@ static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const_al_str if(!failed) { - char *base = (char*)Hrtf; - uintptr_t offset = sizeof(*Hrtf); + size_t reqsize = 2*irSize*irCount + irCount; + if(datalen < reqsize) + { + ERR("Unexpected end of %s data (req "SZFMT", rem "SZFMT"\n", + filename, reqsize, datalen); + failed = AL_TRUE; + } + } - Hrtf->sampleRate = rate; - Hrtf->irSize = irSize; - Hrtf->evCount = evCount; - Hrtf->azCount = ((ALubyte*)(base + offset)); offset += evCount*sizeof(Hrtf->azCount[0]); - offset = (offset+1)&~1; /* Align for (u)short fields */ - Hrtf->evOffset = ((ALushort*)(base + offset)); offset += evCount*sizeof(Hrtf->evOffset[0]); - Hrtf->coeffs = ((ALshort*)(base + offset)); offset += irSize*irCount*sizeof(Hrtf->coeffs[0]); - Hrtf->delays = ((ALubyte*)(base + offset)); offset += irCount*sizeof(Hrtf->delays[0]); - Hrtf->filename = ((char*)(base + offset)); - Hrtf->next = NULL; + if(!failed) + { + if(channelType == CHANTYPE_LEFTONLY) + { + if(sampleType == SAMPLETYPE_S16) + for(i = 0;i < irCount;i++) + { + for(j = 0;j < irSize;j++) + coeffs[i*irSize + j][0] = GetLE_ALshort(&data, &datalen) / 32768.0f; + } + else if(sampleType == SAMPLETYPE_S24) + for(i = 0;i < irCount;i++) + { + for(j = 0;j < irSize;j++) + coeffs[i*irSize + j][0] = GetLE_ALint24(&data, &datalen) / 8388608.0f; + } - memcpy((void*)Hrtf->azCount, azCount, sizeof(azCount[0])*evCount); - memcpy((void*)Hrtf->evOffset, evOffset, sizeof(evOffset[0])*evCount); - memcpy((void*)Hrtf->coeffs, coeffs, sizeof(coeffs[0])*irSize*irCount); - memcpy((void*)Hrtf->delays, delays, sizeof(delays[0])*irCount); - memcpy((void*)Hrtf->filename, al_string_get_cstr(filename), al_string_length(filename)+1); + for(i = 0;i < irCount;i++) + { + delays[i][0] = GetLE_ALubyte(&data, &datalen); + 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(i = 0;i < irCount;i++) + { + for(j = 0;j < irSize;j++) + { + coeffs[i*irSize + j][0] = GetLE_ALshort(&data, &datalen) / 32768.0f; + coeffs[i*irSize + j][1] = GetLE_ALshort(&data, &datalen) / 32768.0f; + } + } + else if(sampleType == SAMPLETYPE_S24) + for(i = 0;i < irCount;i++) + { + for(j = 0;j < irSize;j++) + { + coeffs[i*irSize + j][0] = GetLE_ALint24(&data, &datalen) / 8388608.0f; + coeffs[i*irSize + j][1] = GetLE_ALint24(&data, &datalen) / 8388608.0f; + } + } + + for(i = 0;i < irCount;i++) + { + delays[i][0] = GetLE_ALubyte(&data, &datalen); + 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; + } + delays[i][1] = GetLE_ALubyte(&data, &datalen); + 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) + { + if(channelType == CHANTYPE_LEFTONLY) + { + /* Mirror the left ear responses to the right ear. */ + for(i = 0;i < evCount;i++) + { + ALushort evoffset = evOffset[i]; + ALubyte azcount = azCount[i]; + for(j = 0;j < azcount;j++) + { + ALsizei lidx = evoffset + j; + ALsizei ridx = evoffset + ((azcount-j) % azcount); + ALsizei k; + + for(k = 0;k < irSize;k++) + coeffs[ridx*irSize + k][1] = coeffs[lidx*irSize + k][0]; + delays[ridx][1] = delays[lidx][0]; + } + } + } + + Hrtf = CreateHrtfStore(rate, irSize, + (ALfloat)distance / 1000.0f, evCount, irCount, azCount, evOffset, + coeffs, delays, filename + ); } free(evOffset); free(coeffs); + free(delays); return Hrtf; } -static void AddFileEntry(vector_HrtfEntry *list, al_string *filename) + +static void AddFileEntry(vector_EnumeratedHrtf *list, const_al_string filename) { - HrtfEntry entry = { AL_STRING_INIT_STATIC(), NULL }; - struct Hrtf *hrtf = NULL; - const HrtfEntry *iter; - struct FileMapping fmap; + EnumeratedHrtf entry = { AL_STRING_INIT_STATIC(), NULL }; + struct HrtfEntry *loaded_entry; + const EnumeratedHrtf *iter; const char *name; const char *ext; int i; -#define MATCH_FNAME(i) (al_string_cmp_cstr(*filename, (i)->hrtf->filename) == 0) - VECTOR_FIND_IF(iter, const HrtfEntry, *list, MATCH_FNAME); - if(iter != VECTOR_END(*list)) + /* Check if this file has already been loaded globally. */ + loaded_entry = LoadedHrtfs; + while(loaded_entry) { - TRACE("Skipping duplicate file entry %s\n", al_string_get_cstr(*filename)); - goto done; - } + if(alstr_cmp_cstr(filename, loaded_entry->filename) == 0) + { + /* Check if this entry has already been added to the list. */ +#define MATCH_ENTRY(i) (loaded_entry == (i)->hrtf) + VECTOR_FIND_IF(iter, const EnumeratedHrtf, *list, MATCH_ENTRY); + if(iter != VECTOR_END(*list)) + { + TRACE("Skipping duplicate file entry %s\n", alstr_get_cstr(filename)); + return; + } #undef MATCH_FNAME - entry.hrtf = LoadedHrtfs; - while(entry.hrtf) - { - if(al_string_cmp_cstr(*filename, entry.hrtf->filename) == 0) - { - TRACE("Skipping load of already-loaded file %s\n", al_string_get_cstr(*filename)); - goto skip_load; + break; } - entry.hrtf = entry.hrtf->next; + loaded_entry = loaded_entry->next; } - TRACE("Loading %s...\n", al_string_get_cstr(*filename)); - fmap = MapFileToMem(al_string_get_cstr(*filename)); - if(fmap.ptr == NULL) + if(!loaded_entry) { - ERR("Could not open %s\n", al_string_get_cstr(*filename)); - goto done; - } + TRACE("Got new file \"%s\"\n", alstr_get_cstr(filename)); - if(fmap.len < sizeof(magicMarker01)) - ERR("%s data is too short ("SZFMT" bytes)\n", al_string_get_cstr(*filename), fmap.len); - else if(memcmp(fmap.ptr, magicMarker01, sizeof(magicMarker01)) == 0) - { - TRACE("Detected data set format v1\n"); - hrtf = LoadHrtf01((const ALubyte*)fmap.ptr+sizeof(magicMarker01), - fmap.len-sizeof(magicMarker01), *filename + loaded_entry = al_calloc(DEF_ALIGN, + FAM_SIZE(struct HrtfEntry, filename, alstr_length(filename)+1) ); - } - else if(memcmp(fmap.ptr, magicMarker00, sizeof(magicMarker00)) == 0) - { - TRACE("Detected data set format v0\n"); - hrtf = LoadHrtf00((const ALubyte*)fmap.ptr+sizeof(magicMarker00), - fmap.len-sizeof(magicMarker00), *filename - ); - } - else - ERR("Invalid header in %s: \"%.8s\"\n", al_string_get_cstr(*filename), (const char*)fmap.ptr); - UnmapFileMem(&fmap); - - if(!hrtf) - { - ERR("Failed to load %s\n", al_string_get_cstr(*filename)); - goto done; + loaded_entry->next = LoadedHrtfs; + loaded_entry->handle = NULL; + strcpy(loaded_entry->filename, alstr_get_cstr(filename)); + LoadedHrtfs = loaded_entry; } - hrtf->next = LoadedHrtfs; - LoadedHrtfs = hrtf; - TRACE("Loaded HRTF support for format: %s %uhz\n", - DevFmtChannelsString(DevFmtStereo), hrtf->sampleRate); - entry.hrtf = hrtf; - -skip_load: /* TODO: Get a human-readable name from the HRTF data (possibly coming in a * format update). */ - name = strrchr(al_string_get_cstr(*filename), '/'); - if(!name) name = strrchr(al_string_get_cstr(*filename), '\\'); - if(!name) name = al_string_get_cstr(*filename); + name = strrchr(alstr_get_cstr(filename), '/'); + if(!name) name = strrchr(alstr_get_cstr(filename), '\\'); + if(!name) name = alstr_get_cstr(filename); else ++name; ext = strrchr(name, '.'); @@ -749,124 +1067,114 @@ skip_load: i = 0; do { if(!ext) - al_string_copy_cstr(&entry.name, name); + alstr_copy_cstr(&entry.name, name); else - al_string_copy_range(&entry.name, name, ext); + alstr_copy_range(&entry.name, name, ext); if(i != 0) { char str[64]; snprintf(str, sizeof(str), " #%d", i+1); - al_string_append_cstr(&entry.name, str); + alstr_append_cstr(&entry.name, str); } ++i; -#define MATCH_NAME(i) (al_string_cmp(entry.name, (i)->name) == 0) - VECTOR_FIND_IF(iter, const HrtfEntry, *list, MATCH_NAME); +#define MATCH_NAME(i) (alstr_cmp(entry.name, (i)->name) == 0) + VECTOR_FIND_IF(iter, const EnumeratedHrtf, *list, MATCH_NAME); #undef MATCH_NAME } while(iter != VECTOR_END(*list)); + entry.hrtf = loaded_entry; - TRACE("Adding entry \"%s\" from file \"%s\"\n", al_string_get_cstr(entry.name), - al_string_get_cstr(*filename)); + TRACE("Adding entry \"%s\" from file \"%s\"\n", alstr_get_cstr(entry.name), + alstr_get_cstr(filename)); VECTOR_PUSH_BACK(*list, entry); - -done: - al_string_deinit(filename); } /* Unfortunate that we have to duplicate AddFileEntry to take a memory buffer * for input instead of opening the given filename. */ -static void AddBuiltInEntry(vector_HrtfEntry *list, const ALubyte *data, size_t datalen, al_string *filename) +static void AddBuiltInEntry(vector_EnumeratedHrtf *list, const_al_string filename, ALuint residx) { - HrtfEntry entry = { AL_STRING_INIT_STATIC(), NULL }; + EnumeratedHrtf entry = { AL_STRING_INIT_STATIC(), NULL }; + struct HrtfEntry *loaded_entry; struct Hrtf *hrtf = NULL; - const HrtfEntry *iter; + const EnumeratedHrtf *iter; + const char *name; + const char *ext; int i; -#define MATCH_FNAME(i) (al_string_cmp_cstr(*filename, (i)->hrtf->filename) == 0) - VECTOR_FIND_IF(iter, const HrtfEntry, *list, MATCH_FNAME); - if(iter != VECTOR_END(*list)) + loaded_entry = LoadedHrtfs; + while(loaded_entry) { - TRACE("Skipping duplicate file entry %s\n", al_string_get_cstr(*filename)); - goto done; - } + if(alstr_cmp_cstr(filename, loaded_entry->filename) == 0) + { +#define MATCH_ENTRY(i) (loaded_entry == (i)->hrtf) + VECTOR_FIND_IF(iter, const EnumeratedHrtf, *list, MATCH_ENTRY); + if(iter != VECTOR_END(*list)) + { + TRACE("Skipping duplicate file entry %s\n", alstr_get_cstr(filename)); + return; + } #undef MATCH_FNAME - entry.hrtf = LoadedHrtfs; - while(entry.hrtf) - { - if(al_string_cmp_cstr(*filename, entry.hrtf->filename) == 0) - { - TRACE("Skipping load of already-loaded file %s\n", al_string_get_cstr(*filename)); - goto skip_load; + break; } - entry.hrtf = entry.hrtf->next; + loaded_entry = loaded_entry->next; } - TRACE("Loading %s...\n", al_string_get_cstr(*filename)); - if(datalen < sizeof(magicMarker01)) + if(!loaded_entry) { - ERR("%s data is too short ("SZFMT" bytes)\n", al_string_get_cstr(*filename), datalen); - goto done; - } + size_t namelen = alstr_length(filename)+32; - if(memcmp(data, magicMarker01, sizeof(magicMarker01)) == 0) - { - TRACE("Detected data set format v1\n"); - hrtf = LoadHrtf01(data+sizeof(magicMarker01), - datalen-sizeof(magicMarker01), *filename + TRACE("Got new file \"%s\"\n", alstr_get_cstr(filename)); + + loaded_entry = al_calloc(DEF_ALIGN, + FAM_SIZE(struct HrtfEntry, filename, namelen) ); - } - else if(memcmp(data, magicMarker00, sizeof(magicMarker00)) == 0) - { - TRACE("Detected data set format v0\n"); - hrtf = LoadHrtf00(data+sizeof(magicMarker00), - datalen-sizeof(magicMarker00), *filename - ); - } - else - ERR("Invalid header in %s: \"%.8s\"\n", al_string_get_cstr(*filename), data); - - if(!hrtf) - { - ERR("Failed to load %s\n", al_string_get_cstr(*filename)); - goto done; + loaded_entry->next = LoadedHrtfs; + loaded_entry->handle = hrtf; + snprintf(loaded_entry->filename, namelen, "!%u_%s", + residx, alstr_get_cstr(filename)); + LoadedHrtfs = loaded_entry; } - hrtf->next = LoadedHrtfs; - LoadedHrtfs = hrtf; - TRACE("Loaded HRTF support for format: %s %uhz\n", - DevFmtChannelsString(DevFmtStereo), hrtf->sampleRate); - entry.hrtf = hrtf; + /* TODO: Get a human-readable name from the HRTF data (possibly coming in a + * format update). */ + name = strrchr(alstr_get_cstr(filename), '/'); + if(!name) name = strrchr(alstr_get_cstr(filename), '\\'); + if(!name) name = alstr_get_cstr(filename); + else ++name; + + ext = strrchr(name, '.'); -skip_load: i = 0; do { - al_string_copy(&entry.name, *filename); + if(!ext) + alstr_copy_cstr(&entry.name, name); + else + alstr_copy_range(&entry.name, name, ext); if(i != 0) { char str[64]; snprintf(str, sizeof(str), " #%d", i+1); - al_string_append_cstr(&entry.name, str); + alstr_append_cstr(&entry.name, str); } ++i; -#define MATCH_NAME(i) (al_string_cmp(entry.name, (i)->name) == 0) - VECTOR_FIND_IF(iter, const HrtfEntry, *list, MATCH_NAME); +#define MATCH_NAME(i) (alstr_cmp(entry.name, (i)->name) == 0) + VECTOR_FIND_IF(iter, const EnumeratedHrtf, *list, MATCH_NAME); #undef MATCH_NAME } while(iter != VECTOR_END(*list)); + entry.hrtf = loaded_entry; - TRACE("Adding built-in entry \"%s\"\n", al_string_get_cstr(entry.name)); + TRACE("Adding built-in entry \"%s\"\n", alstr_get_cstr(entry.name)); VECTOR_PUSH_BACK(*list, entry); - -done: - al_string_deinit(filename); } +#define IDR_DEFAULT_44100_MHR 1 +#define IDR_DEFAULT_48000_MHR 2 + #ifndef ALSOFT_EMBED_HRTF_DATA -#define IDR_DEFAULT_44100_MHR 0 -#define IDR_DEFAULT_48000_MHR 1 static const ALubyte *GetResource(int UNUSED(name), size_t *size) { @@ -875,72 +1183,37 @@ static const ALubyte *GetResource(int UNUSED(name), size_t *size) } #else -#include "hrtf_res.h" -#ifdef _WIN32 -static const ALubyte *GetResource(int name, size_t *size) -{ - HMODULE handle; - HGLOBAL res; - HRSRC rc; - - GetModuleHandleExW( - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - (LPCWSTR)GetResource, &handle - ); - rc = FindResourceW(handle, MAKEINTRESOURCEW(name), MAKEINTRESOURCEW(MHRTYPE)); - res = LoadResource(handle, rc); - - *size = SizeofResource(handle, rc); - return LockResource(res); -} - -#else - -extern const ALubyte _binary_default_44100_mhr_start[] HIDDEN_DECL; -extern const ALubyte _binary_default_44100_mhr_end[] HIDDEN_DECL; -extern const ALubyte _binary_default_44100_mhr_size[] HIDDEN_DECL; - -extern const ALubyte _binary_default_48000_mhr_start[] HIDDEN_DECL; -extern const ALubyte _binary_default_48000_mhr_end[] HIDDEN_DECL; -extern const ALubyte _binary_default_48000_mhr_size[] HIDDEN_DECL; +#include "default-44100.mhr.h" +#include "default-48000.mhr.h" static const ALubyte *GetResource(int name, size_t *size) { if(name == IDR_DEFAULT_44100_MHR) { - /* Make sure all symbols are referenced, to ensure the compiler won't - * ignore the declarations and lose the visibility attribute used to - * hide them (would be nice if ld or objcopy could automatically mark - * them as hidden when generating them, but apparently they can't). - */ - const void *volatile ptr =_binary_default_44100_mhr_size; - (void)ptr; - *size = _binary_default_44100_mhr_end - _binary_default_44100_mhr_start; - return _binary_default_44100_mhr_start; + *size = sizeof(hrtf_default_44100); + return hrtf_default_44100; } if(name == IDR_DEFAULT_48000_MHR) { - const void *volatile ptr =_binary_default_48000_mhr_size; - (void)ptr; - *size = _binary_default_48000_mhr_end - _binary_default_48000_mhr_start; - return _binary_default_48000_mhr_start; + *size = sizeof(hrtf_default_48000); + return hrtf_default_48000; } *size = 0; return NULL; } #endif -#endif -vector_HrtfEntry EnumerateHrtf(const_al_string devname) +vector_EnumeratedHrtf EnumerateHrtf(const_al_string devname) { - vector_HrtfEntry list = VECTOR_INIT_STATIC(); + vector_EnumeratedHrtf list = VECTOR_INIT_STATIC(); const char *defaulthrtf = ""; const char *pathlist = ""; bool usedefaults = true; - if(ConfigValueStr(al_string_get_cstr(devname), NULL, "hrtf-paths", &pathlist)) + if(ConfigValueStr(alstr_get_cstr(devname), NULL, "hrtf-paths", &pathlist)) { + al_string pname = AL_STRING_INIT_STATIC(); while(pathlist && *pathlist) { const char *next, *end; @@ -963,65 +1236,69 @@ vector_HrtfEntry EnumerateHrtf(const_al_string devname) --end; if(end != pathlist) { - al_string pname = AL_STRING_INIT_STATIC(); vector_al_string flist; + size_t i; - al_string_append_range(&pname, pathlist, end); + alstr_copy_range(&pname, pathlist, end); - flist = SearchDataFiles(".mhr", al_string_get_cstr(pname)); - VECTOR_FOR_EACH_PARAMS(al_string, flist, AddFileEntry, &list); + flist = SearchDataFiles(".mhr", alstr_get_cstr(pname)); + for(i = 0;i < VECTOR_SIZE(flist);i++) + AddFileEntry(&list, VECTOR_ELEM(flist, i)); + VECTOR_FOR_EACH(al_string, flist, alstr_reset); VECTOR_DEINIT(flist); - - al_string_deinit(&pname); } pathlist = next; } + + alstr_reset(&pname); } - else if(ConfigValueExists(al_string_get_cstr(devname), NULL, "hrtf_tables")) + else if(ConfigValueExists(alstr_get_cstr(devname), NULL, "hrtf_tables")) ERR("The hrtf_tables option is deprecated, please use hrtf-paths instead.\n"); if(usedefaults) { + al_string ename = AL_STRING_INIT_STATIC(); vector_al_string flist; const ALubyte *rdata; - size_t rsize; + size_t rsize, i; flist = SearchDataFiles(".mhr", "openal/hrtf"); - VECTOR_FOR_EACH_PARAMS(al_string, flist, AddFileEntry, &list); + for(i = 0;i < VECTOR_SIZE(flist);i++) + AddFileEntry(&list, VECTOR_ELEM(flist, i)); + VECTOR_FOR_EACH(al_string, flist, alstr_reset); VECTOR_DEINIT(flist); rdata = GetResource(IDR_DEFAULT_44100_MHR, &rsize); if(rdata != NULL && rsize > 0) { - al_string ename = AL_STRING_INIT_STATIC(); - al_string_copy_cstr(&ename, "Built-In 44100hz"); - AddBuiltInEntry(&list, rdata, rsize, &ename); + alstr_copy_cstr(&ename, "Built-In 44100hz"); + AddBuiltInEntry(&list, ename, IDR_DEFAULT_44100_MHR); } rdata = GetResource(IDR_DEFAULT_48000_MHR, &rsize); if(rdata != NULL && rsize > 0) { - al_string ename = AL_STRING_INIT_STATIC(); - al_string_copy_cstr(&ename, "Built-In 48000hz"); - AddBuiltInEntry(&list, rdata, rsize, &ename); + alstr_copy_cstr(&ename, "Built-In 48000hz"); + AddBuiltInEntry(&list, ename, IDR_DEFAULT_48000_MHR); } + alstr_reset(&ename); } - if(VECTOR_SIZE(list) > 1 && ConfigValueStr(al_string_get_cstr(devname), NULL, "default-hrtf", &defaulthrtf)) + if(VECTOR_SIZE(list) > 1 && ConfigValueStr(alstr_get_cstr(devname), NULL, "default-hrtf", &defaulthrtf)) { - const HrtfEntry *iter; + const EnumeratedHrtf *iter; /* Find the preferred HRTF and move it to the front of the list. */ -#define FIND_ENTRY(i) (al_string_cmp_cstr((i)->name, defaulthrtf) == 0) - VECTOR_FIND_IF(iter, const HrtfEntry, list, FIND_ENTRY); +#define FIND_ENTRY(i) (alstr_cmp_cstr((i)->name, defaulthrtf) == 0) + VECTOR_FIND_IF(iter, const EnumeratedHrtf, list, FIND_ENTRY); #undef FIND_ENTRY if(iter == VECTOR_END(list)) WARN("Failed to find default HRTF \"%s\"\n", defaulthrtf); else if(iter != VECTOR_BEGIN(list)) { - HrtfEntry entry = *iter; + EnumeratedHrtf entry = *iter; memmove(&VECTOR_ELEM(list,1), &VECTOR_ELEM(list,0), - (iter-VECTOR_BEGIN(list))*sizeof(HrtfEntry)); + (iter-VECTOR_BEGIN(list))*sizeof(EnumeratedHrtf)); VECTOR_ELEM(list,0) = entry; } } @@ -1029,25 +1306,155 @@ vector_HrtfEntry EnumerateHrtf(const_al_string devname) return list; } -void FreeHrtfList(vector_HrtfEntry *list) +void FreeHrtfList(vector_EnumeratedHrtf *list) { -#define CLEAR_ENTRY(i) do { \ - al_string_deinit(&(i)->name); \ -} while(0) - VECTOR_FOR_EACH(HrtfEntry, *list, CLEAR_ENTRY); +#define CLEAR_ENTRY(i) alstr_reset(&(i)->name) + VECTOR_FOR_EACH(EnumeratedHrtf, *list, CLEAR_ENTRY); VECTOR_DEINIT(*list); #undef CLEAR_ENTRY } +struct Hrtf *GetLoadedHrtf(struct HrtfEntry *entry) +{ + struct Hrtf *hrtf = NULL; + struct FileMapping fmap; + const ALubyte *rdata; + const char *name; + ALuint residx; + size_t rsize; + char ch; + + while(ATOMIC_FLAG_TEST_AND_SET(&LoadedHrtfLock, almemory_order_seq_cst)) + althrd_yield(); + + if(entry->handle) + { + hrtf = entry->handle; + Hrtf_IncRef(hrtf); + goto done; + } + + fmap.ptr = NULL; + fmap.len = 0; + if(sscanf(entry->filename, "!%u%c", &residx, &ch) == 2 && ch == '_') + { + name = strchr(entry->filename, ch)+1; + + TRACE("Loading %s...\n", name); + rdata = GetResource(residx, &rsize); + if(rdata == NULL || rsize == 0) + { + ERR("Could not get resource %u, %s\n", residx, name); + goto done; + } + } + else + { + name = entry->filename; + + TRACE("Loading %s...\n", entry->filename); + fmap = MapFileToMem(entry->filename); + if(fmap.ptr == NULL) + { + ERR("Could not open %s\n", entry->filename); + goto done; + } + + rdata = fmap.ptr; + rsize = fmap.len; + } + + if(rsize < sizeof(magicMarker02)) + ERR("%s data is too short ("SZFMT" bytes)\n", name, rsize); + else if(memcmp(rdata, magicMarker02, sizeof(magicMarker02)) == 0) + { + TRACE("Detected data set format v2\n"); + hrtf = LoadHrtf02(rdata+sizeof(magicMarker02), + rsize-sizeof(magicMarker02), name + ); + } + else if(memcmp(rdata, magicMarker01, sizeof(magicMarker01)) == 0) + { + TRACE("Detected data set format v1\n"); + hrtf = LoadHrtf01(rdata+sizeof(magicMarker01), + rsize-sizeof(magicMarker01), name + ); + } + else if(memcmp(rdata, magicMarker00, sizeof(magicMarker00)) == 0) + { + TRACE("Detected data set format v0\n"); + hrtf = LoadHrtf00(rdata+sizeof(magicMarker00), + rsize-sizeof(magicMarker00), name + ); + } + else + ERR("Invalid header in %s: \"%.8s\"\n", name, (const char*)rdata); + if(fmap.ptr) + UnmapFileMem(&fmap); + + if(!hrtf) + { + ERR("Failed to load %s\n", name); + goto done; + } + entry->handle = hrtf; + Hrtf_IncRef(hrtf); + + TRACE("Loaded HRTF support for format: %s %uhz\n", + DevFmtChannelsString(DevFmtStereo), hrtf->sampleRate); + +done: + ATOMIC_FLAG_CLEAR(&LoadedHrtfLock, almemory_order_seq_cst); + return hrtf; +} + + +void Hrtf_IncRef(struct Hrtf *hrtf) +{ + uint ref = IncrementRef(&hrtf->ref); + TRACEREF("%p increasing refcount to %u\n", hrtf, ref); +} + +void Hrtf_DecRef(struct Hrtf *hrtf) +{ + struct HrtfEntry *Hrtf; + uint ref = DecrementRef(&hrtf->ref); + TRACEREF("%p decreasing refcount to %u\n", hrtf, ref); + if(ref == 0) + { + while(ATOMIC_FLAG_TEST_AND_SET(&LoadedHrtfLock, almemory_order_seq_cst)) + althrd_yield(); + + Hrtf = LoadedHrtfs; + while(Hrtf != NULL) + { + /* Need to double-check that it's still unused, as another device + * could've reacquired this HRTF after its reference went to 0 and + * before the lock was taken. + */ + if(hrtf == Hrtf->handle && ReadRef(&hrtf->ref) == 0) + { + al_free(Hrtf->handle); + Hrtf->handle = NULL; + TRACE("Unloaded unused HRTF %s\n", Hrtf->filename); + } + Hrtf = Hrtf->next; + } + + ATOMIC_FLAG_CLEAR(&LoadedHrtfLock, almemory_order_seq_cst); + } +} + void FreeHrtfs(void) { - struct Hrtf *Hrtf = LoadedHrtfs; + struct HrtfEntry *Hrtf = LoadedHrtfs; LoadedHrtfs = NULL; while(Hrtf != NULL) { - struct Hrtf *next = Hrtf->next; + struct HrtfEntry *next = Hrtf->next; + al_free(Hrtf->handle); al_free(Hrtf); Hrtf = next; } diff --git a/Engine/lib/openal-soft/Alc/hrtf.h b/Engine/lib/openal-soft/Alc/hrtf.h index ebba0d50c..cb6dfddc8 100644 --- a/Engine/lib/openal-soft/Alc/hrtf.h +++ b/Engine/lib/openal-soft/Alc/hrtf.h @@ -4,49 +4,86 @@ #include "AL/al.h" #include "AL/alc.h" +#include "alMain.h" #include "alstring.h" +#include "atomic.h" -struct Hrtf { - ALuint sampleRate; - ALuint irSize; - ALubyte evCount; +/* The maximum number of virtual speakers used to generate HRTF coefficients + * for decoding B-Format. + */ +#define HRTF_AMBI_MAX_CHANNELS 18 - const ALubyte *azCount; - const ALushort *evOffset; - const ALshort *coeffs; - const ALubyte *delays; - const char *filename; - struct Hrtf *next; -}; - -typedef struct HrtfEntry { - al_string name; - - const struct Hrtf *hrtf; -} HrtfEntry; -TYPEDEF_VECTOR(HrtfEntry, vector_HrtfEntry) +#define HRTF_HISTORY_BITS (6) +#define HRTF_HISTORY_LENGTH (1< + + +#ifdef __GNUC__ +#define DECL_FORMAT(x, y, z) __attribute__((format(x, (y), (z)))) +#else +#define DECL_FORMAT(x, y, z) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern FILE *LogFile; + +#if defined(__GNUC__) && !defined(_WIN32) +#define AL_PRINT(T, MSG, ...) fprintf(LogFile, "AL lib: %s %s: "MSG, T, __FUNCTION__ , ## __VA_ARGS__) +#else +void al_print(const char *type, const char *func, const char *fmt, ...) DECL_FORMAT(printf, 3,4); +#define AL_PRINT(T, ...) al_print((T), __FUNCTION__, __VA_ARGS__) +#endif + +#ifdef __ANDROID__ +#include +#define LOG_ANDROID(T, MSG, ...) __android_log_print(T, "openal", "AL lib: %s: "MSG, __FUNCTION__ , ## __VA_ARGS__) +#else +#define LOG_ANDROID(T, MSG, ...) ((void)0) +#endif + +enum LogLevel { + NoLog, + LogError, + LogWarning, + LogTrace, + LogRef +}; +extern enum LogLevel LogLevel; + +#define TRACEREF(...) do { \ + if(LogLevel >= LogRef) \ + AL_PRINT("(--)", __VA_ARGS__); \ +} while(0) + +#define TRACE(...) do { \ + if(LogLevel >= LogTrace) \ + AL_PRINT("(II)", __VA_ARGS__); \ + LOG_ANDROID(ANDROID_LOG_DEBUG, __VA_ARGS__); \ +} while(0) + +#define WARN(...) do { \ + if(LogLevel >= LogWarning) \ + AL_PRINT("(WW)", __VA_ARGS__); \ + LOG_ANDROID(ANDROID_LOG_WARN, __VA_ARGS__); \ +} while(0) + +#define ERR(...) do { \ + if(LogLevel >= LogError) \ + AL_PRINT("(EE)", __VA_ARGS__); \ + LOG_ANDROID(ANDROID_LOG_ERROR, __VA_ARGS__); \ +} while(0) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LOGGING_H */ diff --git a/Engine/lib/openal-soft/Alc/mastering.c b/Engine/lib/openal-soft/Alc/mastering.c new file mode 100644 index 000000000..1636c8d93 --- /dev/null +++ b/Engine/lib/openal-soft/Alc/mastering.c @@ -0,0 +1,232 @@ +#include "config.h" + +#include + +#include "mastering.h" +#include "alu.h" +#include "almalloc.h" + + +extern inline ALuint GetCompressorSampleRate(const Compressor *Comp); + +#define RMS_WINDOW_SIZE (1<<7) +#define RMS_WINDOW_MASK (RMS_WINDOW_SIZE-1) +#define RMS_VALUE_MAX (1<<24) + +static_assert(RMS_VALUE_MAX < (UINT_MAX / RMS_WINDOW_SIZE), "RMS_VALUE_MAX is too big"); + + +/* Multichannel compression is linked via one of two modes: + * + * Summed - Absolute sum of all channels. + * Maxed - Absolute maximum of any channel. + */ +static void SumChannels(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo, + ALfloat (*restrict OutBuffer)[BUFFERSIZE]) +{ + ALsizei c, i; + + for(i = 0;i < SamplesToDo;i++) + Comp->Envelope[i] = 0.0f; + + for(c = 0;c < NumChans;c++) + { + for(i = 0;i < SamplesToDo;i++) + Comp->Envelope[i] += OutBuffer[c][i]; + } + + for(i = 0;i < SamplesToDo;i++) + Comp->Envelope[i] = fabsf(Comp->Envelope[i]); +} + +static void MaxChannels(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo, + ALfloat (*restrict OutBuffer)[BUFFERSIZE]) +{ + ALsizei c, i; + + for(i = 0;i < SamplesToDo;i++) + Comp->Envelope[i] = 0.0f; + + for(c = 0;c < NumChans;c++) + { + for(i = 0;i < SamplesToDo;i++) + Comp->Envelope[i] = maxf(Comp->Envelope[i], fabsf(OutBuffer[c][i])); + } +} + +/* Envelope detection/sensing can be done via: + * + * RMS - Rectangular windowed root mean square of linking stage. + * Peak - Implicit output from linking stage. + */ +static void RmsDetection(Compressor *Comp, const ALsizei SamplesToDo) +{ + ALuint sum = Comp->RmsSum; + ALuint *window = Comp->RmsWindow; + ALsizei index = Comp->RmsIndex; + ALsizei i; + + for(i = 0;i < SamplesToDo;i++) + { + ALfloat sig = Comp->Envelope[i]; + + sum -= window[index]; + window[index] = fastf2i(minf(sig * sig * 65536.0f, RMS_VALUE_MAX)); + sum += window[index]; + index = (index + 1) & RMS_WINDOW_MASK; + + Comp->Envelope[i] = sqrtf(sum / 65536.0f / RMS_WINDOW_SIZE); + } + + Comp->RmsSum = sum; + Comp->RmsIndex = index; +} + +/* This isn't a very sophisticated envelope follower, but it gets the job + * done. First, it operates at logarithmic scales to keep transitions + * appropriate for human hearing. Second, it can apply adaptive (automated) + * attack/release adjustments based on the signal. + */ +static void FollowEnvelope(Compressor *Comp, const ALsizei SamplesToDo) +{ + ALfloat attackMin = Comp->AttackMin; + ALfloat attackMax = Comp->AttackMax; + ALfloat releaseMin = Comp->ReleaseMin; + ALfloat releaseMax = Comp->ReleaseMax; + ALfloat last = Comp->EnvLast; + ALsizei i; + + for(i = 0;i < SamplesToDo;i++) + { + ALfloat env = log10f(maxf(Comp->Envelope[i], 0.000001f)); + ALfloat slope = minf(1.0f, fabsf(env - last) / 4.5f); + + if(env > last) + last = minf(env, last + lerp(attackMin, attackMax, 1.0f - (slope * slope))); + else + last = maxf(env, last + lerp(releaseMin, releaseMax, 1.0f - (slope * slope))); + + Comp->Envelope[i] = last; + } + + Comp->EnvLast = last; +} + +/* The envelope is converted to control gain with an optional soft knee. */ +static void EnvelopeGain(Compressor *Comp, const ALsizei SamplesToDo, const ALfloat Slope) +{ + const ALfloat threshold = Comp->Threshold; + const ALfloat knee = Comp->Knee; + ALsizei i; + + if(!(knee > 0.0f)) + { + for(i = 0;i < SamplesToDo;i++) + { + ALfloat gain = Slope * (threshold - Comp->Envelope[i]); + Comp->Envelope[i] = powf(10.0f, minf(0.0f, gain)); + } + } + else + { + const ALfloat lower = threshold - (0.5f * knee); + const ALfloat upper = threshold + (0.5f * knee); + const ALfloat m = 0.5f * Slope / knee; + + for(i = 0;i < SamplesToDo;i++) + { + ALfloat env = Comp->Envelope[i]; + ALfloat gain; + + if(env > lower && env < upper) + gain = m * (env - lower) * (lower - env); + else + gain = Slope * (threshold - env); + + Comp->Envelope[i] = powf(10.0f, minf(0.0f, gain)); + } + } +} + + +Compressor *CompressorInit(const ALfloat PreGainDb, const ALfloat PostGainDb, + const ALboolean SummedLink, const ALboolean RmsSensing, + const ALfloat AttackTimeMin, const ALfloat AttackTimeMax, + const ALfloat ReleaseTimeMin, const ALfloat ReleaseTimeMax, + const ALfloat Ratio, const ALfloat ThresholdDb, + const ALfloat KneeDb, const ALuint SampleRate) +{ + Compressor *Comp; + size_t size; + ALsizei i; + + size = sizeof(*Comp); + if(RmsSensing) + size += sizeof(Comp->RmsWindow[0]) * RMS_WINDOW_SIZE; + Comp = al_calloc(16, size); + + Comp->PreGain = powf(10.0f, PreGainDb / 20.0f); + Comp->PostGain = powf(10.0f, PostGainDb / 20.0f); + Comp->SummedLink = SummedLink; + Comp->AttackMin = 1.0f / maxf(0.000001f, AttackTimeMin * SampleRate * logf(10.0f)); + Comp->AttackMax = 1.0f / maxf(0.000001f, AttackTimeMax * SampleRate * logf(10.0f)); + Comp->ReleaseMin = -1.0f / maxf(0.000001f, ReleaseTimeMin * SampleRate * logf(10.0f)); + Comp->ReleaseMax = -1.0f / maxf(0.000001f, ReleaseTimeMax * SampleRate * logf(10.0f)); + Comp->Ratio = Ratio; + Comp->Threshold = ThresholdDb / 20.0f; + Comp->Knee = maxf(0.0f, KneeDb / 20.0f); + Comp->SampleRate = SampleRate; + + Comp->RmsSum = 0; + if(RmsSensing) + Comp->RmsWindow = (ALuint*)(Comp+1); + else + Comp->RmsWindow = NULL; + Comp->RmsIndex = 0; + + for(i = 0;i < BUFFERSIZE;i++) + Comp->Envelope[i] = 0.0f; + Comp->EnvLast = -6.0f; + + return Comp; +} + +void ApplyCompression(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo, + ALfloat (*restrict OutBuffer)[BUFFERSIZE]) +{ + ALsizei c, i; + + if(Comp->PreGain != 1.0f) + { + for(c = 0;c < NumChans;c++) + { + for(i = 0;i < SamplesToDo;i++) + OutBuffer[c][i] *= Comp->PreGain; + } + } + + if(Comp->SummedLink) + SumChannels(Comp, NumChans, SamplesToDo, OutBuffer); + else + MaxChannels(Comp, NumChans, SamplesToDo, OutBuffer); + + if(Comp->RmsWindow) + RmsDetection(Comp, SamplesToDo); + FollowEnvelope(Comp, SamplesToDo); + + if(Comp->Ratio > 0.0f) + EnvelopeGain(Comp, SamplesToDo, 1.0f - (1.0f / Comp->Ratio)); + else + EnvelopeGain(Comp, SamplesToDo, 1.0f); + + if(Comp->PostGain != 1.0f) + { + for(i = 0;i < SamplesToDo;i++) + Comp->Envelope[i] *= Comp->PostGain; + } + for(c = 0;c < NumChans;c++) + { + for(i = 0;i < SamplesToDo;i++) + OutBuffer[c][i] *= Comp->Envelope[i]; + } +} diff --git a/Engine/lib/openal-soft/Alc/mastering.h b/Engine/lib/openal-soft/Alc/mastering.h new file mode 100644 index 000000000..0a7b49017 --- /dev/null +++ b/Engine/lib/openal-soft/Alc/mastering.h @@ -0,0 +1,57 @@ +#ifndef MASTERING_H +#define MASTERING_H + +#include "AL/al.h" + +/* For BUFFERSIZE. */ +#include "alMain.h" + +typedef struct Compressor { + ALfloat PreGain; + ALfloat PostGain; + ALboolean SummedLink; + ALfloat AttackMin; + ALfloat AttackMax; + ALfloat ReleaseMin; + ALfloat ReleaseMax; + ALfloat Ratio; + ALfloat Threshold; + ALfloat Knee; + ALuint SampleRate; + + ALuint RmsSum; + ALuint *RmsWindow; + ALsizei RmsIndex; + ALfloat Envelope[BUFFERSIZE]; + ALfloat EnvLast; +} Compressor; + +/* The compressor requires the following information for proper + * initialization: + * + * PreGainDb - Gain applied before detection (in dB). + * PostGainDb - Gain applied after compression (in dB). + * SummedLink - Whether to use summed (true) or maxed (false) linking. + * RmsSensing - Whether to use RMS (true) or Peak (false) sensing. + * AttackTimeMin - Minimum attack time (in seconds). + * AttackTimeMax - Maximum attack time. Automates when min != max. + * ReleaseTimeMin - Minimum release time (in seconds). + * ReleaseTimeMax - Maximum release time. Automates when min != max. + * Ratio - Compression ratio (x:1). Set to 0 for true limiter. + * ThresholdDb - Triggering threshold (in dB). + * KneeDb - Knee width (below threshold; in dB). + * SampleRate - Sample rate to process. + */ +Compressor *CompressorInit(const ALfloat PreGainDb, const ALfloat PostGainDb, + const ALboolean SummedLink, const ALboolean RmsSensing, const ALfloat AttackTimeMin, + const ALfloat AttackTimeMax, const ALfloat ReleaseTimeMin, const ALfloat ReleaseTimeMax, + const ALfloat Ratio, const ALfloat ThresholdDb, const ALfloat KneeDb, + const ALuint SampleRate); + +void ApplyCompression(struct Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo, + ALfloat (*restrict OutBuffer)[BUFFERSIZE]); + +inline ALuint GetCompressorSampleRate(const Compressor *Comp) +{ return Comp->SampleRate; } + +#endif /* MASTERING_H */ diff --git a/Engine/lib/openal-soft/Alc/mixer.c b/Engine/lib/openal-soft/Alc/mixer.c deleted file mode 100644 index b1f79d055..000000000 --- a/Engine/lib/openal-soft/Alc/mixer.c +++ /dev/null @@ -1,702 +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 -#include -#include -#include -#include - -#include "alMain.h" -#include "AL/al.h" -#include "AL/alc.h" -#include "alSource.h" -#include "alBuffer.h" -#include "alListener.h" -#include "alAuxEffectSlot.h" -#include "alu.h" - -#include "mixer_defs.h" - - -static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE, - "MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!"); - -extern inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *restrict frac_arr, ALuint *restrict pos_arr, ALuint size); - -alignas(16) union ResamplerCoeffs ResampleCoeffs; - - -enum Resampler { - PointResampler, - LinearResampler, - FIR4Resampler, - FIR8Resampler, - BSincResampler, - - ResamplerDefault = LinearResampler -}; - -/* FIR8 requires 3 extra samples before the current position, and 4 after. */ -static_assert(MAX_PRE_SAMPLES >= 3, "MAX_PRE_SAMPLES must be at least 3!"); -static_assert(MAX_POST_SAMPLES >= 4, "MAX_POST_SAMPLES must be at least 4!"); - - -static MixerFunc MixSamples = Mix_C; -static HrtfMixerFunc MixHrtfSamples = MixHrtf_C; -static ResamplerFunc ResampleSamples = Resample_point32_C; - -MixerFunc SelectMixer(void) -{ -#ifdef HAVE_SSE - if((CPUCapFlags&CPU_CAP_SSE)) - return Mix_SSE; -#endif -#ifdef HAVE_NEON - if((CPUCapFlags&CPU_CAP_NEON)) - return Mix_Neon; -#endif - - return Mix_C; -} - -RowMixerFunc SelectRowMixer(void) -{ -#ifdef HAVE_SSE - if((CPUCapFlags&CPU_CAP_SSE)) - return MixRow_SSE; -#endif -#ifdef HAVE_NEON - if((CPUCapFlags&CPU_CAP_NEON)) - return MixRow_Neon; -#endif - return MixRow_C; -} - -static inline HrtfMixerFunc SelectHrtfMixer(void) -{ -#ifdef HAVE_SSE - if((CPUCapFlags&CPU_CAP_SSE)) - return MixHrtf_SSE; -#endif -#ifdef HAVE_NEON - if((CPUCapFlags&CPU_CAP_NEON)) - return MixHrtf_Neon; -#endif - - return MixHrtf_C; -} - -static inline ResamplerFunc SelectResampler(enum Resampler resampler) -{ - switch(resampler) - { - case PointResampler: - return Resample_point32_C; - case LinearResampler: -#ifdef HAVE_SSE4_1 - if((CPUCapFlags&CPU_CAP_SSE4_1)) - return Resample_lerp32_SSE41; -#endif -#ifdef HAVE_SSE2 - if((CPUCapFlags&CPU_CAP_SSE2)) - return Resample_lerp32_SSE2; -#endif - return Resample_lerp32_C; - case FIR4Resampler: -#ifdef HAVE_SSE4_1 - if((CPUCapFlags&CPU_CAP_SSE4_1)) - return Resample_fir4_32_SSE41; -#endif -#ifdef HAVE_SSE3 - if((CPUCapFlags&CPU_CAP_SSE3)) - return Resample_fir4_32_SSE3; -#endif - return Resample_fir4_32_C; - case FIR8Resampler: -#ifdef HAVE_SSE4_1 - if((CPUCapFlags&CPU_CAP_SSE4_1)) - return Resample_fir8_32_SSE41; -#endif -#ifdef HAVE_SSE3 - if((CPUCapFlags&CPU_CAP_SSE3)) - return Resample_fir8_32_SSE3; -#endif - return Resample_fir8_32_C; - case BSincResampler: -#ifdef HAVE_SSE - if((CPUCapFlags&CPU_CAP_SSE)) - return Resample_bsinc32_SSE; -#endif - return Resample_bsinc32_C; - } - - return Resample_point32_C; -} - - -/* The sinc resampler makes use of a Kaiser window to limit the needed sample - * points to 4 and 8, respectively. - */ - -#ifndef M_PI -#define M_PI (3.14159265358979323846) -#endif -static inline double Sinc(double x) -{ - if(x == 0.0) return 1.0; - return sin(x*M_PI) / (x*M_PI); -} - -/* The zero-order modified Bessel function of the first kind, used for the - * Kaiser window. - * - * I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k) - * = sum_{k=0}^inf ((x / 2)^k / k!)^2 - */ -static double BesselI_0(double x) -{ - double term, sum, x2, y, last_sum; - int k; - - /* Start at k=1 since k=0 is trivial. */ - term = 1.0; - sum = 1.0; - x2 = x / 2.0; - k = 1; - - /* Let the integration converge until the term of the sum is no longer - * significant. - */ - do { - y = x2 / k; - k ++; - last_sum = sum; - term *= y * y; - sum += term; - } while(sum != last_sum); - return sum; -} - -/* Calculate a Kaiser window from the given beta value and a normalized k - * [-1, 1]. - * - * w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B), -1 <= k <= 1 - * { 0, elsewhere. - * - * Where k can be calculated as: - * - * k = i / l, where -l <= i <= l. - * - * or: - * - * k = 2 i / M - 1, where 0 <= i <= M. - */ -static inline double Kaiser(double b, double k) -{ - if(k <= -1.0 || k >= 1.0) return 0.0; - return BesselI_0(b * sqrt(1.0 - (k*k))) / BesselI_0(b); -} - -static inline double CalcKaiserBeta(double rejection) -{ - if(rejection > 50.0) - return 0.1102 * (rejection - 8.7); - if(rejection >= 21.0) - return (0.5842 * pow(rejection - 21.0, 0.4)) + - (0.07886 * (rejection - 21.0)); - return 0.0; -} - -static float SincKaiser(double r, double x) -{ - /* Limit rippling to -60dB. */ - return (float)(Kaiser(CalcKaiserBeta(60.0), x / r) * Sinc(x)); -} - - -void aluInitMixer(void) -{ - enum Resampler resampler = ResamplerDefault; - const char *str; - ALuint i; - - if(ConfigValueStr(NULL, NULL, "resampler", &str)) - { - if(strcasecmp(str, "point") == 0 || strcasecmp(str, "none") == 0) - resampler = PointResampler; - else if(strcasecmp(str, "linear") == 0) - resampler = LinearResampler; - else if(strcasecmp(str, "sinc4") == 0) - resampler = FIR4Resampler; - else if(strcasecmp(str, "sinc8") == 0) - resampler = FIR8Resampler; - else if(strcasecmp(str, "bsinc") == 0) - resampler = BSincResampler; - else if(strcasecmp(str, "cubic") == 0) - { - WARN("Resampler option \"cubic\" is deprecated, using sinc4\n"); - resampler = FIR4Resampler; - } - else - { - char *end; - long n = strtol(str, &end, 0); - if(*end == '\0' && (n == PointResampler || n == LinearResampler || n == FIR4Resampler)) - resampler = n; - else - WARN("Invalid resampler: %s\n", str); - } - } - - if(resampler == FIR8Resampler) - for(i = 0;i < FRACTIONONE;i++) - { - ALdouble mu = (ALdouble)i / FRACTIONONE; - ResampleCoeffs.FIR8[i][0] = SincKaiser(4.0, mu - -3.0); - ResampleCoeffs.FIR8[i][1] = SincKaiser(4.0, mu - -2.0); - ResampleCoeffs.FIR8[i][2] = SincKaiser(4.0, mu - -1.0); - ResampleCoeffs.FIR8[i][3] = SincKaiser(4.0, mu - 0.0); - ResampleCoeffs.FIR8[i][4] = SincKaiser(4.0, mu - 1.0); - ResampleCoeffs.FIR8[i][5] = SincKaiser(4.0, mu - 2.0); - ResampleCoeffs.FIR8[i][6] = SincKaiser(4.0, mu - 3.0); - ResampleCoeffs.FIR8[i][7] = SincKaiser(4.0, mu - 4.0); - } - else if(resampler == FIR4Resampler) - for(i = 0;i < FRACTIONONE;i++) - { - ALdouble mu = (ALdouble)i / FRACTIONONE; - ResampleCoeffs.FIR4[i][0] = SincKaiser(2.0, mu - -1.0); - ResampleCoeffs.FIR4[i][1] = SincKaiser(2.0, mu - 0.0); - ResampleCoeffs.FIR4[i][2] = SincKaiser(2.0, mu - 1.0); - ResampleCoeffs.FIR4[i][3] = SincKaiser(2.0, mu - 2.0); - } - - MixHrtfSamples = SelectHrtfMixer(); - MixSamples = SelectMixer(); - ResampleSamples = SelectResampler(resampler); -} - - -static inline ALfloat Sample_ALbyte(ALbyte val) -{ return val * (1.0f/127.0f); } - -static inline ALfloat Sample_ALshort(ALshort val) -{ return val * (1.0f/32767.0f); } - -static inline ALfloat Sample_ALfloat(ALfloat val) -{ return val; } - -#define DECL_TEMPLATE(T) \ -static inline void Load_##T(ALfloat *dst, const T *src, ALuint srcstep, ALuint samples)\ -{ \ - ALuint i; \ - for(i = 0;i < samples;i++) \ - dst[i] = Sample_##T(src[i*srcstep]); \ -} - -DECL_TEMPLATE(ALbyte) -DECL_TEMPLATE(ALshort) -DECL_TEMPLATE(ALfloat) - -#undef DECL_TEMPLATE - -static void LoadSamples(ALfloat *dst, const ALvoid *src, ALuint srcstep, enum FmtType srctype, ALuint samples) -{ - switch(srctype) - { - case FmtByte: - Load_ALbyte(dst, src, srcstep, samples); - break; - case FmtShort: - Load_ALshort(dst, src, srcstep, samples); - break; - case FmtFloat: - Load_ALfloat(dst, src, srcstep, samples); - break; - } -} - -static inline void SilenceSamples(ALfloat *dst, ALuint samples) -{ - ALuint i; - for(i = 0;i < samples;i++) - dst[i] = 0.0f; -} - - -static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter, - ALfloat *restrict dst, const ALfloat *restrict src, - ALuint numsamples, enum ActiveFilters type) -{ - ALuint i; - switch(type) - { - case AF_None: - ALfilterState_processPassthru(lpfilter, src, numsamples); - ALfilterState_processPassthru(hpfilter, src, numsamples); - break; - - case AF_LowPass: - ALfilterState_process(lpfilter, dst, src, numsamples); - ALfilterState_processPassthru(hpfilter, dst, numsamples); - return dst; - case AF_HighPass: - ALfilterState_processPassthru(lpfilter, src, numsamples); - ALfilterState_process(hpfilter, dst, src, numsamples); - return dst; - - case AF_BandPass: - for(i = 0;i < numsamples;) - { - ALfloat temp[256]; - ALuint todo = minu(256, numsamples-i); - - ALfilterState_process(lpfilter, temp, src+i, todo); - ALfilterState_process(hpfilter, dst+i, temp, todo); - i += todo; - } - return dst; - } - return src; -} - - -ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint SamplesToDo) -{ - ResamplerFunc Resample; - ALbufferlistitem *BufferListItem; - ALuint DataPosInt, DataPosFrac; - ALboolean Looping; - ALuint increment; - ALenum State; - ALuint OutPos; - ALuint NumChannels; - ALuint SampleSize; - ALint64 DataSize64; - ALuint Counter; - ALuint IrSize; - ALuint chan, send, j; - - /* Get source info */ - State = AL_PLAYING; /* Only called while playing. */ - BufferListItem = ATOMIC_LOAD(&Source->current_buffer); - DataPosInt = ATOMIC_LOAD(&Source->position, almemory_order_relaxed); - DataPosFrac = ATOMIC_LOAD(&Source->position_fraction, almemory_order_relaxed); - Looping = ATOMIC_LOAD(&Source->looping, almemory_order_relaxed); - NumChannels = Source->NumChannels; - SampleSize = Source->SampleSize; - increment = voice->Step; - - IrSize = (Device->Hrtf.Handle ? Device->Hrtf.Handle->irSize : 0); - - Resample = ((increment == FRACTIONONE && DataPosFrac == 0) ? - Resample_copy32_C : ResampleSamples); - - Counter = voice->Moving ? SamplesToDo : 0; - OutPos = 0; - do { - ALuint SrcBufferSize, DstBufferSize; - - /* Figure out how many buffer samples will be needed */ - DataSize64 = SamplesToDo-OutPos; - DataSize64 *= increment; - DataSize64 += DataPosFrac+FRACTIONMASK; - DataSize64 >>= FRACTIONBITS; - DataSize64 += MAX_POST_SAMPLES+MAX_PRE_SAMPLES; - - SrcBufferSize = (ALuint)mini64(DataSize64, BUFFERSIZE); - - /* Figure out how many samples we can actually mix from this. */ - DataSize64 = SrcBufferSize; - DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES; - DataSize64 <<= FRACTIONBITS; - DataSize64 -= DataPosFrac; - - DstBufferSize = (ALuint)((DataSize64+(increment-1)) / increment); - DstBufferSize = minu(DstBufferSize, (SamplesToDo-OutPos)); - - /* Some mixers like having a multiple of 4, so try to give that unless - * this is the last update. */ - if(OutPos+DstBufferSize < SamplesToDo) - DstBufferSize &= ~3; - - for(chan = 0;chan < NumChannels;chan++) - { - const ALfloat *ResampledData; - ALfloat *SrcData = Device->SourceData; - ALuint SrcDataSize; - - /* Load the previous samples into the source data first. */ - memcpy(SrcData, voice->PrevSamples[chan], MAX_PRE_SAMPLES*sizeof(ALfloat)); - SrcDataSize = MAX_PRE_SAMPLES; - - if(Source->SourceType == AL_STATIC) - { - const ALbuffer *ALBuffer = BufferListItem->buffer; - const ALubyte *Data = ALBuffer->data; - ALuint DataSize; - ALuint pos; - - /* Offset buffer data to current channel */ - Data += chan*SampleSize; - - /* If current pos is beyond the loop range, do not loop */ - if(Looping == AL_FALSE || DataPosInt >= (ALuint)ALBuffer->LoopEnd) - { - Looping = AL_FALSE; - - /* Load what's left to play from the source buffer, and - * clear the rest of the temp buffer */ - pos = DataPosInt; - DataSize = minu(SrcBufferSize - SrcDataSize, ALBuffer->SampleLen - pos); - - LoadSamples(&SrcData[SrcDataSize], &Data[pos * NumChannels*SampleSize], - NumChannels, ALBuffer->FmtType, DataSize); - SrcDataSize += DataSize; - - SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize); - SrcDataSize += SrcBufferSize - SrcDataSize; - } - else - { - ALuint LoopStart = ALBuffer->LoopStart; - ALuint LoopEnd = ALBuffer->LoopEnd; - - /* Load what's left of this loop iteration, then load - * repeats of the loop section */ - pos = DataPosInt; - DataSize = LoopEnd - pos; - DataSize = minu(SrcBufferSize - SrcDataSize, DataSize); - - LoadSamples(&SrcData[SrcDataSize], &Data[pos * NumChannels*SampleSize], - NumChannels, ALBuffer->FmtType, DataSize); - SrcDataSize += DataSize; - - DataSize = LoopEnd-LoopStart; - while(SrcBufferSize > SrcDataSize) - { - DataSize = minu(SrcBufferSize - SrcDataSize, DataSize); - - LoadSamples(&SrcData[SrcDataSize], &Data[LoopStart * NumChannels*SampleSize], - NumChannels, ALBuffer->FmtType, DataSize); - SrcDataSize += DataSize; - } - } - } - else - { - /* Crawl the buffer queue to fill in the temp buffer */ - ALbufferlistitem *tmpiter = BufferListItem; - ALuint pos = DataPosInt; - - while(tmpiter && SrcBufferSize > SrcDataSize) - { - const ALbuffer *ALBuffer; - if((ALBuffer=tmpiter->buffer) != NULL) - { - const ALubyte *Data = ALBuffer->data; - ALuint DataSize = ALBuffer->SampleLen; - - /* Skip the data already played */ - if(DataSize <= pos) - pos -= DataSize; - else - { - Data += (pos*NumChannels + chan)*SampleSize; - DataSize -= pos; - pos -= pos; - - DataSize = minu(SrcBufferSize - SrcDataSize, DataSize); - LoadSamples(&SrcData[SrcDataSize], Data, NumChannels, - ALBuffer->FmtType, DataSize); - SrcDataSize += DataSize; - } - } - tmpiter = tmpiter->next; - if(!tmpiter && Looping) - tmpiter = ATOMIC_LOAD(&Source->queue); - else if(!tmpiter) - { - SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize); - SrcDataSize += SrcBufferSize - SrcDataSize; - } - } - } - - /* Store the last source samples used for next time. */ - memcpy(voice->PrevSamples[chan], - &SrcData[(increment*DstBufferSize + DataPosFrac)>>FRACTIONBITS], - MAX_PRE_SAMPLES*sizeof(ALfloat) - ); - - /* Now resample, then filter and mix to the appropriate outputs. */ - ResampledData = Resample(&voice->SincState, - &SrcData[MAX_PRE_SAMPLES], DataPosFrac, increment, - Device->ResampledData, DstBufferSize - ); - { - DirectParams *parms = &voice->Chan[chan].Direct; - const ALfloat *samples; - - samples = DoFilters( - &parms->LowPass, &parms->HighPass, Device->FilteredData, - ResampledData, DstBufferSize, parms->FilterType - ); - if(!voice->IsHrtf) - { - if(!Counter) - memcpy(parms->Gains.Current, parms->Gains.Target, - sizeof(parms->Gains.Current)); - MixSamples(samples, voice->DirectOut.Channels, voice->DirectOut.Buffer, - parms->Gains.Current, parms->Gains.Target, Counter, OutPos, DstBufferSize - ); - } - else - { - MixHrtfParams hrtfparams; - int lidx, ridx; - - if(!Counter) - { - parms->Hrtf.Current = parms->Hrtf.Target; - for(j = 0;j < HRIR_LENGTH;j++) - { - hrtfparams.Steps.Coeffs[j][0] = 0.0f; - hrtfparams.Steps.Coeffs[j][1] = 0.0f; - } - hrtfparams.Steps.Delay[0] = 0; - hrtfparams.Steps.Delay[1] = 0; - } - else - { - ALfloat delta = 1.0f / (ALfloat)Counter; - ALfloat coeffdiff; - ALint delaydiff; - for(j = 0;j < IrSize;j++) - { - coeffdiff = parms->Hrtf.Target.Coeffs[j][0] - parms->Hrtf.Current.Coeffs[j][0]; - hrtfparams.Steps.Coeffs[j][0] = coeffdiff * delta; - coeffdiff = parms->Hrtf.Target.Coeffs[j][1] - parms->Hrtf.Current.Coeffs[j][1]; - hrtfparams.Steps.Coeffs[j][1] = coeffdiff * delta; - } - delaydiff = (ALint)(parms->Hrtf.Target.Delay[0] - parms->Hrtf.Current.Delay[0]); - hrtfparams.Steps.Delay[0] = fastf2i((ALfloat)delaydiff * delta); - delaydiff = (ALint)(parms->Hrtf.Target.Delay[1] - parms->Hrtf.Current.Delay[1]); - hrtfparams.Steps.Delay[1] = fastf2i((ALfloat)delaydiff * delta); - } - hrtfparams.Target = &parms->Hrtf.Target; - hrtfparams.Current = &parms->Hrtf.Current; - - lidx = GetChannelIdxByName(Device->RealOut, FrontLeft); - ridx = GetChannelIdxByName(Device->RealOut, FrontRight); - assert(lidx != -1 && ridx != -1); - - MixHrtfSamples(voice->DirectOut.Buffer, lidx, ridx, samples, Counter, - voice->Offset, OutPos, IrSize, &hrtfparams, - &parms->Hrtf.State, DstBufferSize); - } - } - - for(send = 0;send < Device->NumAuxSends;send++) - { - SendParams *parms = &voice->Chan[chan].Send[send]; - const ALfloat *samples; - - if(!voice->SendOut[send].Buffer) - continue; - - samples = DoFilters( - &parms->LowPass, &parms->HighPass, Device->FilteredData, - ResampledData, DstBufferSize, parms->FilterType - ); - - if(!Counter) - memcpy(parms->Gains.Current, parms->Gains.Target, - sizeof(parms->Gains.Current)); - MixSamples(samples, voice->SendOut[send].Channels, voice->SendOut[send].Buffer, - parms->Gains.Current, parms->Gains.Target, Counter, OutPos, DstBufferSize - ); - } - } - /* Update positions */ - DataPosFrac += increment*DstBufferSize; - DataPosInt += DataPosFrac>>FRACTIONBITS; - DataPosFrac &= FRACTIONMASK; - - OutPos += DstBufferSize; - voice->Offset += DstBufferSize; - Counter = maxu(DstBufferSize, Counter) - DstBufferSize; - - /* Handle looping sources */ - while(1) - { - const ALbuffer *ALBuffer; - ALuint DataSize = 0; - ALuint LoopStart = 0; - ALuint LoopEnd = 0; - - if((ALBuffer=BufferListItem->buffer) != NULL) - { - DataSize = ALBuffer->SampleLen; - LoopStart = ALBuffer->LoopStart; - LoopEnd = ALBuffer->LoopEnd; - if(LoopEnd > DataPosInt) - break; - } - - if(Looping && Source->SourceType == AL_STATIC) - { - assert(LoopEnd > LoopStart); - DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart; - break; - } - - if(DataSize > DataPosInt) - break; - - if(!(BufferListItem=BufferListItem->next)) - { - if(Looping) - BufferListItem = ATOMIC_LOAD(&Source->queue); - else - { - State = AL_STOPPED; - BufferListItem = NULL; - DataPosInt = 0; - DataPosFrac = 0; - break; - } - } - - DataPosInt -= DataSize; - } - } while(State == AL_PLAYING && OutPos < SamplesToDo); - - voice->Moving = AL_TRUE; - - /* Update source info */ - Source->state = State; - ATOMIC_STORE(&Source->current_buffer, BufferListItem, almemory_order_relaxed); - ATOMIC_STORE(&Source->position, DataPosInt, almemory_order_relaxed); - ATOMIC_STORE(&Source->position_fraction, DataPosFrac); -} diff --git a/Engine/lib/openal-soft/Alc/mixer/defs.h b/Engine/lib/openal-soft/Alc/mixer/defs.h new file mode 100644 index 000000000..fe19cef4f --- /dev/null +++ b/Engine/lib/openal-soft/Alc/mixer/defs.h @@ -0,0 +1,119 @@ +#ifndef MIXER_DEFS_H +#define MIXER_DEFS_H + +#include "AL/alc.h" +#include "AL/al.h" +#include "alMain.h" +#include "alu.h" + +struct MixGains; + +struct MixHrtfParams; +struct HrtfState; + +/* C resamplers */ +const ALfloat *Resample_copy_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen); +const ALfloat *Resample_point_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen); +const ALfloat *Resample_lerp_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen); +const ALfloat *Resample_cubic_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen); +const ALfloat *Resample_bsinc_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen); + + +/* C mixers */ +void MixHrtf_C(ALfloat *restrict LeftOut, ALfloat *restrict RightOut, + const ALfloat *data, ALsizei Offset, ALsizei OutPos, + const ALsizei IrSize, struct MixHrtfParams *hrtfparams, + struct HrtfState *hrtfstate, ALsizei BufferSize); +void MixHrtfBlend_C(ALfloat *restrict LeftOut, ALfloat *restrict RightOut, + const ALfloat *data, ALsizei Offset, ALsizei OutPos, + const ALsizei IrSize, const HrtfParams *oldparams, + MixHrtfParams *newparams, HrtfState *hrtfstate, + ALsizei BufferSize); +void MixDirectHrtf_C(ALfloat *restrict LeftOut, ALfloat *restrict RightOut, + const ALfloat *data, ALsizei Offset, const ALsizei IrSize, + const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2], + ALsizei BufferSize); +void Mix_C(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], + ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos, + ALsizei BufferSize); +void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains, + const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, + ALsizei InPos, ALsizei BufferSize); + +/* SSE mixers */ +void MixHrtf_SSE(ALfloat *restrict LeftOut, ALfloat *restrict RightOut, + const ALfloat *data, ALsizei Offset, ALsizei OutPos, + const ALsizei IrSize, struct MixHrtfParams *hrtfparams, + struct HrtfState *hrtfstate, ALsizei BufferSize); +void MixHrtfBlend_SSE(ALfloat *restrict LeftOut, ALfloat *restrict RightOut, + const ALfloat *data, ALsizei Offset, ALsizei OutPos, + const ALsizei IrSize, const HrtfParams *oldparams, + MixHrtfParams *newparams, HrtfState *hrtfstate, + ALsizei BufferSize); +void MixDirectHrtf_SSE(ALfloat *restrict LeftOut, ALfloat *restrict RightOut, + const ALfloat *data, ALsizei Offset, const ALsizei IrSize, + const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2], + ALsizei BufferSize); +void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], + ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos, + ALsizei BufferSize); +void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains, + const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, + ALsizei InPos, ALsizei BufferSize); + +/* SSE resamplers */ +inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size) +{ + ALsizei i; + + pos_arr[0] = 0; + frac_arr[0] = frac; + for(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; + } +} + +const ALfloat *Resample_lerp_SSE2(const InterpState *state, const ALfloat *restrict src, + ALsizei frac, ALint increment, ALfloat *restrict dst, + ALsizei numsamples); +const ALfloat *Resample_lerp_SSE41(const InterpState *state, const ALfloat *restrict src, + ALsizei frac, ALint increment, ALfloat *restrict dst, + ALsizei numsamples); + +const ALfloat *Resample_bsinc_SSE(const InterpState *state, const ALfloat *restrict src, + ALsizei frac, ALint increment, ALfloat *restrict dst, + ALsizei dstlen); + +/* Neon mixers */ +void MixHrtf_Neon(ALfloat *restrict LeftOut, ALfloat *restrict RightOut, + const ALfloat *data, ALsizei Offset, ALsizei OutPos, + const ALsizei IrSize, struct MixHrtfParams *hrtfparams, + struct HrtfState *hrtfstate, ALsizei BufferSize); +void MixHrtfBlend_Neon(ALfloat *restrict LeftOut, ALfloat *restrict RightOut, + const ALfloat *data, ALsizei Offset, ALsizei OutPos, + const ALsizei IrSize, const HrtfParams *oldparams, + MixHrtfParams *newparams, HrtfState *hrtfstate, + ALsizei BufferSize); +void MixDirectHrtf_Neon(ALfloat *restrict LeftOut, ALfloat *restrict RightOut, + const ALfloat *data, ALsizei Offset, const ALsizei IrSize, + const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2], + ALsizei BufferSize); +void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], + ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos, + ALsizei BufferSize); +void MixRow_Neon(ALfloat *OutBuffer, const ALfloat *Gains, + const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, + ALsizei InPos, ALsizei BufferSize); + +/* Neon resamplers */ +const ALfloat *Resample_lerp_Neon(const InterpState *state, const ALfloat *restrict src, + ALsizei frac, ALint increment, ALfloat *restrict dst, + ALsizei numsamples); +const ALfloat *Resample_bsinc_Neon(const InterpState *state, const ALfloat *restrict src, + ALsizei frac, ALint increment, ALfloat *restrict dst, + ALsizei dstlen); + +#endif /* MIXER_DEFS_H */ diff --git a/Engine/lib/openal-soft/Alc/mixer/hrtf_inc.c b/Engine/lib/openal-soft/Alc/mixer/hrtf_inc.c new file mode 100644 index 000000000..d6bd8042c --- /dev/null +++ b/Engine/lib/openal-soft/Alc/mixer/hrtf_inc.c @@ -0,0 +1,123 @@ +#include "config.h" + +#include "alMain.h" +#include "alSource.h" + +#include "hrtf.h" +#include "align.h" +#include "alu.h" +#include "defs.h" + + +static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2], + const ALsizei irSize, + const ALfloat (*restrict Coeffs)[2], + ALfloat left, ALfloat right); + + +void MixHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut, + const ALfloat *data, ALsizei Offset, ALsizei OutPos, + const ALsizei IrSize, MixHrtfParams *hrtfparams, HrtfState *hrtfstate, + ALsizei BufferSize) +{ + const ALfloat (*Coeffs)[2] = ASSUME_ALIGNED(hrtfparams->Coeffs, 16); + const ALsizei Delay[2] = { hrtfparams->Delay[0], hrtfparams->Delay[1] }; + ALfloat gainstep = hrtfparams->GainStep; + ALfloat gain = hrtfparams->Gain; + ALfloat left, right; + ALsizei i; + + ASSUME(IrSize >= 4); + ASSUME(BufferSize > 0); + + LeftOut += OutPos; + RightOut += OutPos; + for(i = 0;i < BufferSize;i++) + { + hrtfstate->History[Offset&HRTF_HISTORY_MASK] = *(data++); + left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK]*gain; + right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK]*gain; + + hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][0] = 0.0f; + hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][1] = 0.0f; + + ApplyCoeffs(Offset, hrtfstate->Values, IrSize, Coeffs, left, right); + *(LeftOut++) += hrtfstate->Values[Offset&HRIR_MASK][0]; + *(RightOut++) += hrtfstate->Values[Offset&HRIR_MASK][1]; + + gain += gainstep; + Offset++; + } + hrtfparams->Gain = gain; +} + +void MixHrtfBlend(ALfloat *restrict LeftOut, ALfloat *restrict RightOut, + const ALfloat *data, ALsizei Offset, ALsizei OutPos, + const ALsizei IrSize, const HrtfParams *oldparams, + MixHrtfParams *newparams, HrtfState *hrtfstate, + ALsizei BufferSize) +{ + const ALfloat (*OldCoeffs)[2] = ASSUME_ALIGNED(oldparams->Coeffs, 16); + const ALsizei OldDelay[2] = { oldparams->Delay[0], oldparams->Delay[1] }; + ALfloat oldGain = oldparams->Gain; + ALfloat oldGainStep = -oldGain / (ALfloat)BufferSize; + const ALfloat (*NewCoeffs)[2] = ASSUME_ALIGNED(newparams->Coeffs, 16); + const ALsizei NewDelay[2] = { newparams->Delay[0], newparams->Delay[1] }; + ALfloat newGain = newparams->Gain; + ALfloat newGainStep = newparams->GainStep; + ALfloat left, right; + ALsizei i; + + ASSUME(IrSize >= 4); + ASSUME(BufferSize > 0); + + LeftOut += OutPos; + RightOut += OutPos; + for(i = 0;i < BufferSize;i++) + { + hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][0] = 0.0f; + hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][1] = 0.0f; + + hrtfstate->History[Offset&HRTF_HISTORY_MASK] = *(data++); + + left = hrtfstate->History[(Offset-OldDelay[0])&HRTF_HISTORY_MASK]*oldGain; + right = hrtfstate->History[(Offset-OldDelay[1])&HRTF_HISTORY_MASK]*oldGain; + ApplyCoeffs(Offset, hrtfstate->Values, IrSize, OldCoeffs, left, right); + + left = hrtfstate->History[(Offset-NewDelay[0])&HRTF_HISTORY_MASK]*newGain; + right = hrtfstate->History[(Offset-NewDelay[1])&HRTF_HISTORY_MASK]*newGain; + ApplyCoeffs(Offset, hrtfstate->Values, IrSize, NewCoeffs, left, right); + + *(LeftOut++) += hrtfstate->Values[Offset&HRIR_MASK][0]; + *(RightOut++) += hrtfstate->Values[Offset&HRIR_MASK][1]; + + oldGain += oldGainStep; + newGain += newGainStep; + Offset++; + } + newparams->Gain = newGain; +} + +void MixDirectHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut, + const ALfloat *data, ALsizei Offset, const ALsizei IrSize, + const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2], + ALsizei BufferSize) +{ + ALfloat insample; + ALsizei i; + + ASSUME(IrSize >= 4); + ASSUME(BufferSize > 0); + + for(i = 0;i < BufferSize;i++) + { + Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f; + Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f; + Offset++; + + insample = *(data++); + ApplyCoeffs(Offset, Values, IrSize, Coeffs, insample, insample); + *(LeftOut++) += Values[Offset&HRIR_MASK][0]; + *(RightOut++) += Values[Offset&HRIR_MASK][1]; + } +} diff --git a/Engine/lib/openal-soft/Alc/mixer/mixer_c.c b/Engine/lib/openal-soft/Alc/mixer/mixer_c.c new file mode 100644 index 000000000..844852060 --- /dev/null +++ b/Engine/lib/openal-soft/Alc/mixer/mixer_c.c @@ -0,0 +1,176 @@ +#include "config.h" + +#include + +#include "alMain.h" +#include "alu.h" +#include "alSource.h" +#include "alAuxEffectSlot.h" +#include "defs.h" + + +static inline ALfloat do_point(const ALfloat *restrict vals, ALsizei UNUSED(frac)) +{ return vals[0]; } +static inline ALfloat do_lerp(const ALfloat *restrict vals, ALsizei frac) +{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); } +static inline ALfloat do_cubic(const ALfloat *restrict vals, ALsizei frac) +{ return cubic(vals[0], vals[1], vals[2], vals[3], frac * (1.0f/FRACTIONONE)); } + +const ALfloat *Resample_copy_C(const InterpState* UNUSED(state), + const ALfloat *restrict src, ALsizei UNUSED(frac), ALint UNUSED(increment), + ALfloat *restrict dst, ALsizei numsamples) +{ +#if defined(HAVE_SSE) || defined(HAVE_NEON) + /* Avoid copying the source data if it's aligned like the destination. */ + if((((intptr_t)src)&15) == (((intptr_t)dst)&15)) + return src; +#endif + memcpy(dst, src, numsamples*sizeof(ALfloat)); + return dst; +} + +#define DECL_TEMPLATE(Tag, Sampler, O) \ +const ALfloat *Resample_##Tag##_C(const InterpState* UNUSED(state), \ + const ALfloat *restrict src, ALsizei frac, ALint increment, \ + ALfloat *restrict dst, ALsizei numsamples) \ +{ \ + ALsizei i; \ + \ + src -= O; \ + for(i = 0;i < numsamples;i++) \ + { \ + dst[i] = Sampler(src, frac); \ + \ + frac += increment; \ + src += frac>>FRACTIONBITS; \ + frac &= FRACTIONMASK; \ + } \ + return dst; \ +} + +DECL_TEMPLATE(point, do_point, 0) +DECL_TEMPLATE(lerp, do_lerp, 0) +DECL_TEMPLATE(cubic, do_cubic, 1) + +#undef DECL_TEMPLATE + +const ALfloat *Resample_bsinc_C(const InterpState *state, const ALfloat *restrict src, + ALsizei frac, ALint increment, ALfloat *restrict dst, + ALsizei dstlen) +{ + const ALfloat *fil, *scd, *phd, *spd; + const ALfloat *const filter = state->bsinc.filter; + const ALfloat sf = state->bsinc.sf; + const ALsizei m = state->bsinc.m; + ALsizei j_f, pi, i; + ALfloat pf, r; + + ASSUME(m > 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<>FRACTIONBITS; + frac &= FRACTIONMASK; + } + return dst; +} + + +static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2], + const ALsizei IrSize, + const ALfloat (*restrict Coeffs)[2], + ALfloat left, ALfloat right) +{ + ALsizei c; + for(c = 0;c < IrSize;c++) + { + const ALsizei off = (Offset+c)&HRIR_MASK; + Values[off][0] += Coeffs[c][0] * left; + Values[off][1] += Coeffs[c][1] * right; + } +} + +#define MixHrtf MixHrtf_C +#define MixHrtfBlend MixHrtfBlend_C +#define MixDirectHrtf MixDirectHrtf_C +#include "hrtf_inc.c" + + +void Mix_C(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], + ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos, + ALsizei BufferSize) +{ + ALfloat gain, delta, step; + ALsizei c; + + ASSUME(OutChans > 0); + ASSUME(BufferSize > 0); + delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f; + + for(c = 0;c < OutChans;c++) + { + ALsizei pos = 0; + gain = CurrentGains[c]; + step = (TargetGains[c] - gain) * delta; + if(fabsf(step) > FLT_EPSILON) + { + ALsizei minsize = mini(BufferSize, Counter); + for(;pos < minsize;pos++) + { + OutBuffer[c][OutPos+pos] += data[pos]*gain; + gain += step; + } + if(pos == Counter) + gain = TargetGains[c]; + CurrentGains[c] = gain; + } + + if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) + continue; + for(;pos < BufferSize;pos++) + OutBuffer[c][OutPos+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. + */ +void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize) +{ + ALsizei c, i; + + ASSUME(InChans > 0); + ASSUME(BufferSize > 0); + + for(c = 0;c < InChans;c++) + { + ALfloat gain = Gains[c]; + if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) + continue; + + for(i = 0;i < BufferSize;i++) + OutBuffer[i] += data[c][InPos+i] * gain; + } +} diff --git a/Engine/lib/openal-soft/Alc/mixer/mixer_neon.c b/Engine/lib/openal-soft/Alc/mixer/mixer_neon.c new file mode 100644 index 000000000..1a5e8ee77 --- /dev/null +++ b/Engine/lib/openal-soft/Alc/mixer/mixer_neon.c @@ -0,0 +1,269 @@ +#include "config.h" + +#include + +#include "AL/al.h" +#include "AL/alc.h" +#include "alMain.h" +#include "alu.h" +#include "hrtf.h" +#include "defs.h" + + +const ALfloat *Resample_lerp_Neon(const InterpState* UNUSED(state), + const ALfloat *restrict src, ALsizei frac, ALint increment, + ALfloat *restrict dst, ALsizei numsamples) +{ + 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) ALint pos_[4]; + alignas(16) ALsizei frac_[4]; + int32x4_t pos4; + int32x4_t frac4; + ALsizei i; + + ASSUME(numsamples > 0); + + InitiatePositionArrays(frac, increment, frac_, pos_, 4); + + frac4 = vld1q_s32(frac_); + pos4 = vld1q_s32(pos_); + + for(i = 0;numsamples-i > 3;i += 4) + { + const float32x4_t val1 = (float32x4_t){src[pos_[0]], src[pos_[1]], src[pos_[2]], src[pos_[3]]}; + const float32x4_t val2 = (float32x4_t){src[pos_[0]+1], src[pos_[1]+1], src[pos_[2]+1], src[pos_[3]+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); + + vst1q_s32(pos_, pos4); + } + + if(i < numsamples) + { + /* NOTE: These four elements represent the position *after* the last + * four samples, so the lowest element is the next position to + * resample. + */ + ALint pos = pos_[0]; + frac = vgetq_lane_s32(frac4, 0); + do { + dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE)); + + frac += increment; + pos += frac>>FRACTIONBITS; + frac &= FRACTIONMASK; + } while(++i < numsamples); + } + return dst; +} + +const ALfloat *Resample_bsinc_Neon(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); + + 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<>FRACTIONBITS; + frac &= FRACTIONMASK; + } + return dst; +} + + +static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2], + const ALsizei IrSize, + const ALfloat (*restrict Coeffs)[2], + ALfloat left, ALfloat right) +{ + ALsizei c; + float32x4_t leftright4; + { + float32x2_t leftright2 = vdup_n_f32(0.0); + leftright2 = vset_lane_f32(left, leftright2, 0); + leftright2 = vset_lane_f32(right, leftright2, 1); + leftright4 = vcombine_f32(leftright2, leftright2); + } + Values = ASSUME_ALIGNED(Values, 16); + Coeffs = ASSUME_ALIGNED(Coeffs, 16); + for(c = 0;c < IrSize;c += 2) + { + const ALsizei o0 = (Offset+c)&HRIR_MASK; + const ALsizei o1 = (o0+1)&HRIR_MASK; + float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]), + vld1_f32((float32_t*)&Values[o1][0])); + float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]); + + vals = vmlaq_f32(vals, coefs, leftright4); + + vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals)); + vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals)); + } +} + +#define MixHrtf MixHrtf_Neon +#define MixHrtfBlend MixHrtfBlend_Neon +#define MixDirectHrtf MixDirectHrtf_Neon +#include "hrtf_inc.c" + + +void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], + ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos, + ALsizei BufferSize) +{ + ALfloat gain, delta, step; + float32x4_t gain4; + ALsizei c; + + ASSUME(OutChans > 0); + ASSUME(BufferSize > 0); + data = ASSUME_ALIGNED(data, 16); + OutBuffer = ASSUME_ALIGNED(OutBuffer, 16); + + delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f; + + for(c = 0;c < OutChans;c++) + { + ALsizei pos = 0; + gain = CurrentGains[c]; + step = (TargetGains[c] - gain) * delta; + if(fabsf(step) > FLT_EPSILON) + { + ALsizei minsize = mini(BufferSize, Counter); + /* Mix with applying gain steps in aligned multiples of 4. */ + if(minsize-pos > 3) + { + float32x4_t step4; + gain4 = vsetq_lane_f32(gain, gain4, 0); + gain4 = vsetq_lane_f32(gain + step, gain4, 1); + gain4 = vsetq_lane_f32(gain + step + step, gain4, 2); + gain4 = vsetq_lane_f32(gain + step + step + step, gain4, 3); + step4 = vdupq_n_f32(step + step + step + step); + do { + const float32x4_t val4 = vld1q_f32(&data[pos]); + float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]); + dry4 = vmlaq_f32(dry4, val4, gain4); + gain4 = vaddq_f32(gain4, step4); + vst1q_f32(&OutBuffer[c][OutPos+pos], dry4); + pos += 4; + } while(minsize-pos > 3); + /* NOTE: gain4 now represents the next four gains after the + * last four mixed samples, so the lowest element represents + * the next gain to apply. + */ + gain = vgetq_lane_f32(gain4, 0); + } + /* Mix with applying left over gain steps that aren't aligned multiples of 4. */ + for(;pos < minsize;pos++) + { + OutBuffer[c][OutPos+pos] += data[pos]*gain; + gain += step; + } + if(pos == Counter) + gain = TargetGains[c]; + CurrentGains[c] = gain; + + /* Mix until pos is aligned with 4 or the mix is done. */ + minsize = mini(BufferSize, (pos+3)&~3); + for(;pos < minsize;pos++) + OutBuffer[c][OutPos+pos] += data[pos]*gain; + } + + if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) + continue; + gain4 = vdupq_n_f32(gain); + for(;BufferSize-pos > 3;pos += 4) + { + const float32x4_t val4 = vld1q_f32(&data[pos]); + float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]); + dry4 = vmlaq_f32(dry4, val4, gain4); + vst1q_f32(&OutBuffer[c][OutPos+pos], dry4); + } + for(;pos < BufferSize;pos++) + OutBuffer[c][OutPos+pos] += data[pos]*gain; + } +} + +void MixRow_Neon(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize) +{ + float32x4_t gain4; + ALsizei c; + + ASSUME(InChans > 0); + ASSUME(BufferSize > 0); + data = ASSUME_ALIGNED(data, 16); + OutBuffer = ASSUME_ALIGNED(OutBuffer, 16); + + for(c = 0;c < InChans;c++) + { + ALsizei pos = 0; + ALfloat gain = Gains[c]; + if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) + continue; + + gain4 = vdupq_n_f32(gain); + for(;BufferSize-pos > 3;pos += 4) + { + const float32x4_t val4 = vld1q_f32(&data[c][InPos+pos]); + float32x4_t dry4 = vld1q_f32(&OutBuffer[pos]); + dry4 = vmlaq_f32(dry4, val4, gain4); + vst1q_f32(&OutBuffer[pos], dry4); + } + for(;pos < BufferSize;pos++) + OutBuffer[pos] += data[c][InPos+pos]*gain; + } +} diff --git a/Engine/lib/openal-soft/Alc/mixer_sse.c b/Engine/lib/openal-soft/Alc/mixer/mixer_sse.c similarity index 55% rename from Engine/lib/openal-soft/Alc/mixer_sse.c rename to Engine/lib/openal-soft/Alc/mixer/mixer_sse.c index f5e21e23d..a178477f8 100644 --- a/Engine/lib/openal-soft/Alc/mixer_sse.c +++ b/Engine/lib/openal-soft/Alc/mixer/mixer_sse.c @@ -9,22 +9,25 @@ #include "alSource.h" #include "alAuxEffectSlot.h" -#include "mixer_defs.h" +#include "defs.h" -const ALfloat *Resample_bsinc32_SSE(const BsincState *state, const ALfloat *restrict src, - ALuint frac, ALuint increment, ALfloat *restrict dst, - ALuint dstlen) +const ALfloat *Resample_bsinc_SSE(const InterpState *state, const ALfloat *restrict src, + ALsizei frac, ALint increment, ALfloat *restrict dst, + ALsizei dstlen) { - const __m128 sf4 = _mm_set1_ps(state->sf); - const ALuint m = state->m; - const ALint l = state->l; - const ALfloat *fil, *scd, *phd, *spd; - ALuint pi, j_f, i; + const ALfloat *const filter = state->bsinc.filter; + const __m128 sf4 = _mm_set1_ps(state->bsinc.sf); + const ALsizei m = state->bsinc.m; + const __m128 *fil, *scd, *phd, *spd; + ALsizei pi, i, j, offset; ALfloat pf; - ALint j_s; __m128 r4; + ASSUME(m > 0); + ASSUME(dstlen > 0); + + src += state->bsinc.l; for(i = 0;i < dstlen;i++) { // Calculate the phase index and factor. @@ -33,32 +36,28 @@ const ALfloat *Resample_bsinc32_SSE(const BsincState *state, const ALfloat *rest pf = (frac & ((1<coeffs[pi].filter; - scd = state->coeffs[pi].scDelta; - phd = state->coeffs[pi].phDelta; - spd = state->coeffs[pi].spDelta; + offset = m*pi*4; + fil = (const __m128*)ASSUME_ALIGNED(filter + offset, 16); offset += m; + scd = (const __m128*)ASSUME_ALIGNED(filter + offset, 16); offset += m; + phd = (const __m128*)ASSUME_ALIGNED(filter + offset, 16); offset += m; + spd = (const __m128*)ASSUME_ALIGNED(filter + offset, 16); // Apply the scale and phase interpolated filter. r4 = _mm_setzero_ps(); { const __m128 pf4 = _mm_set1_ps(pf); - for(j_f = 0,j_s = l;j_f < m;j_f+=4,j_s+=4) +#define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z)) + for(j = 0;j < m;j+=4,fil++,scd++,phd++,spd++) { - const __m128 f4 = _mm_add_ps( - _mm_add_ps( - _mm_load_ps(&fil[j_f]), - _mm_mul_ps(sf4, _mm_load_ps(&scd[j_f])) - ), - _mm_mul_ps( - pf4, - _mm_add_ps( - _mm_load_ps(&phd[j_f]), - _mm_mul_ps(sf4, _mm_load_ps(&spd[j_f])) - ) - ) + /* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */ + const __m128 f4 = MLA4( + MLA4(*fil, sf4, *scd), + pf4, MLA4(*phd, sf4, *spd) ); - r4 = _mm_add_ps(r4, _mm_mul_ps(f4, _mm_loadu_ps(&src[j_s]))); + /* r += f*src */ + r4 = MLA4(r4, f4, _mm_loadu_ps(&src[j])); } +#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)); @@ -72,82 +71,22 @@ const ALfloat *Resample_bsinc32_SSE(const BsincState *state, const ALfloat *rest } -static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2], - const ALuint IrSize, - ALfloat (*restrict Coeffs)[2], - const ALfloat (*restrict CoeffStep)[2], - ALfloat left, ALfloat right) -{ - const __m128 lrlr = _mm_setr_ps(left, right, left, right); - __m128 coeffs, deltas, imp0, imp1; - __m128 vals = _mm_setzero_ps(); - ALuint i; - - if((Offset&1)) - { - const ALuint o0 = Offset&HRIR_MASK; - const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK; - - coeffs = _mm_load_ps(&Coeffs[0][0]); - deltas = _mm_load_ps(&CoeffStep[0][0]); - vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]); - imp0 = _mm_mul_ps(lrlr, coeffs); - coeffs = _mm_add_ps(coeffs, deltas); - vals = _mm_add_ps(imp0, vals); - _mm_store_ps(&Coeffs[0][0], coeffs); - _mm_storel_pi((__m64*)&Values[o0][0], vals); - for(i = 1;i < IrSize-1;i += 2) - { - const ALuint o2 = (Offset+i)&HRIR_MASK; - - coeffs = _mm_load_ps(&Coeffs[i+1][0]); - deltas = _mm_load_ps(&CoeffStep[i+1][0]); - vals = _mm_load_ps(&Values[o2][0]); - imp1 = _mm_mul_ps(lrlr, coeffs); - coeffs = _mm_add_ps(coeffs, deltas); - imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2)); - vals = _mm_add_ps(imp0, vals); - _mm_store_ps(&Coeffs[i+1][0], coeffs); - _mm_store_ps(&Values[o2][0], vals); - imp0 = imp1; - } - vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]); - imp0 = _mm_movehl_ps(imp0, imp0); - vals = _mm_add_ps(imp0, vals); - _mm_storel_pi((__m64*)&Values[o1][0], vals); - } - else - { - for(i = 0;i < IrSize;i += 2) - { - const ALuint o = (Offset + i)&HRIR_MASK; - - coeffs = _mm_load_ps(&Coeffs[i][0]); - deltas = _mm_load_ps(&CoeffStep[i][0]); - vals = _mm_load_ps(&Values[o][0]); - imp0 = _mm_mul_ps(lrlr, coeffs); - coeffs = _mm_add_ps(coeffs, deltas); - vals = _mm_add_ps(imp0, vals); - _mm_store_ps(&Coeffs[i][0], coeffs); - _mm_store_ps(&Values[o][0], vals); - } - } -} - -static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], - const ALuint IrSize, - ALfloat (*restrict Coeffs)[2], +static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2], + const ALsizei IrSize, + const ALfloat (*restrict Coeffs)[2], ALfloat left, ALfloat right) { const __m128 lrlr = _mm_setr_ps(left, right, left, right); __m128 vals = _mm_setzero_ps(); __m128 coeffs; - ALuint i; + ALsizei i; + Values = ASSUME_ALIGNED(Values, 16); + Coeffs = ASSUME_ALIGNED(Coeffs, 16); if((Offset&1)) { - const ALuint o0 = Offset&HRIR_MASK; - const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK; + const ALsizei o0 = Offset&HRIR_MASK; + const ALsizei o1 = (Offset+IrSize-1)&HRIR_MASK; __m128 imp0, imp1; coeffs = _mm_load_ps(&Coeffs[0][0]); @@ -157,7 +96,7 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], _mm_storel_pi((__m64*)&Values[o0][0], vals); for(i = 1;i < IrSize-1;i += 2) { - const ALuint o2 = (Offset+i)&HRIR_MASK; + const ALsizei o2 = (Offset+i)&HRIR_MASK; coeffs = _mm_load_ps(&Coeffs[i+1][0]); vals = _mm_load_ps(&Values[o2][0]); @@ -176,7 +115,7 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], { for(i = 0;i < IrSize;i += 2) { - const ALuint o = (Offset + i)&HRIR_MASK; + const ALsizei o = (Offset + i)&HRIR_MASK; coeffs = _mm_load_ps(&Coeffs[i][0]); vals = _mm_load_ps(&Values[o][0]); @@ -187,29 +126,31 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], } #define MixHrtf MixHrtf_SSE +#define MixHrtfBlend MixHrtfBlend_SSE #define MixDirectHrtf MixDirectHrtf_SSE -#include "mixer_inc.c" -#undef MixHrtf +#include "hrtf_inc.c" -void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], - ALfloat *CurrentGains, const ALfloat *TargetGains, ALuint Counter, ALuint OutPos, - ALuint BufferSize) +void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], + ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos, + ALsizei BufferSize) { ALfloat gain, delta, step; __m128 gain4; - ALuint c; + ALsizei c; + ASSUME(OutChans > 0); + ASSUME(BufferSize > 0); delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f; for(c = 0;c < OutChans;c++) { - ALuint pos = 0; + ALsizei pos = 0; gain = CurrentGains[c]; step = (TargetGains[c] - gain) * delta; if(fabsf(step) > FLT_EPSILON) { - ALuint minsize = minu(BufferSize, Counter); + ALsizei minsize = mini(BufferSize, Counter); /* Mix with applying gain steps in aligned multiples of 4. */ if(minsize-pos > 3) { @@ -246,7 +187,7 @@ void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer) CurrentGains[c] = gain; /* Mix until pos is aligned with 4 or the mix is done. */ - minsize = minu(BufferSize, (pos+3)&~3); + minsize = mini(BufferSize, (pos+3)&~3); for(;pos < minsize;pos++) OutBuffer[c][OutPos+pos] += data[pos]*gain; } @@ -266,14 +207,17 @@ void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer) } } -void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALuint InChans, ALuint InPos, ALuint BufferSize) +void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize) { __m128 gain4; - ALuint c; + ALsizei c; + + ASSUME(InChans > 0); + ASSUME(BufferSize > 0); for(c = 0;c < InChans;c++) { - ALuint pos = 0; + ALsizei pos = 0; ALfloat gain = Gains[c]; if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) continue; diff --git a/Engine/lib/openal-soft/Alc/mixer_sse2.c b/Engine/lib/openal-soft/Alc/mixer/mixer_sse2.c similarity index 86% rename from Engine/lib/openal-soft/Alc/mixer_sse2.c rename to Engine/lib/openal-soft/Alc/mixer/mixer_sse2.c index 22a18ef3d..4aeb6fc4c 100644 --- a/Engine/lib/openal-soft/Alc/mixer_sse2.c +++ b/Engine/lib/openal-soft/Alc/mixer/mixer_sse2.c @@ -24,21 +24,23 @@ #include #include "alu.h" -#include "mixer_defs.h" +#include "defs.h" -const ALfloat *Resample_lerp32_SSE2(const BsincState* UNUSED(state), const ALfloat *restrict src, - ALuint frac, ALuint increment, ALfloat *restrict dst, - ALuint numsamples) +const ALfloat *Resample_lerp_SSE2(const InterpState* UNUSED(state), + const ALfloat *restrict src, ALsizei frac, ALint increment, + ALfloat *restrict dst, ALsizei numsamples) { const __m128i increment4 = _mm_set1_epi32(increment*4); const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE); const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK); - union { alignas(16) ALuint i[4]; float f[4]; } pos_; - union { alignas(16) ALuint i[4]; float f[4]; } frac_; + union { alignas(16) ALint i[4]; float f[4]; } pos_; + union { alignas(16) ALsizei i[4]; float f[4]; } frac_; __m128i frac4, pos4; - ALuint pos; - ALuint i; + ALint pos; + ALsizei i; + + ASSUME(numsamples > 0); InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4); diff --git a/Engine/lib/openal-soft/Alc/mixer/mixer_sse3.c b/Engine/lib/openal-soft/Alc/mixer/mixer_sse3.c new file mode 100644 index 000000000..e69de29bb diff --git a/Engine/lib/openal-soft/Alc/mixer/mixer_sse41.c b/Engine/lib/openal-soft/Alc/mixer/mixer_sse41.c new file mode 100644 index 000000000..98a3ef74b --- /dev/null +++ b/Engine/lib/openal-soft/Alc/mixer/mixer_sse41.c @@ -0,0 +1,88 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2014 by Timothy Arceri . + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include +#include +#include + +#include "alu.h" +#include "defs.h" + + +const ALfloat *Resample_lerp_SSE41(const InterpState* UNUSED(state), + const ALfloat *restrict src, ALsizei frac, ALint increment, + ALfloat *restrict dst, ALsizei numsamples) +{ + const __m128i increment4 = _mm_set1_epi32(increment*4); + const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE); + const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK); + union { alignas(16) ALint i[4]; float f[4]; } pos_; + union { alignas(16) ALsizei i[4]; float f[4]; } frac_; + __m128i frac4, pos4; + ALint pos; + ALsizei i; + + ASSUME(numsamples > 0); + + InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4); + + frac4 = _mm_castps_si128(_mm_load_ps(frac_.f)); + pos4 = _mm_castps_si128(_mm_load_ps(pos_.f)); + + for(i = 0;numsamples-i > 3;i += 4) + { + const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]); + const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]); + + /* val1 + (val2-val1)*mu */ + const __m128 r0 = _mm_sub_ps(val2, val1); + const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4); + const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0)); + + _mm_store_ps(&dst[i], out); + + frac4 = _mm_add_epi32(frac4, increment4); + pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS)); + frac4 = _mm_and_si128(frac4, fracMask4); + + pos_.i[0] = _mm_extract_epi32(pos4, 0); + pos_.i[1] = _mm_extract_epi32(pos4, 1); + pos_.i[2] = _mm_extract_epi32(pos4, 2); + pos_.i[3] = _mm_extract_epi32(pos4, 3); + } + + /* NOTE: These four elements represent the position *after* the last four + * samples, so the lowest element is the next position to resample. + */ + pos = pos_.i[0]; + frac = _mm_cvtsi128_si32(frac4); + + for(;i < numsamples;i++) + { + dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE)); + + frac += increment; + pos += frac>>FRACTIONBITS; + frac &= FRACTIONMASK; + } + return dst; +} diff --git a/Engine/lib/openal-soft/Alc/mixer_c.c b/Engine/lib/openal-soft/Alc/mixer_c.c deleted file mode 100644 index 6ef818c78..000000000 --- a/Engine/lib/openal-soft/Alc/mixer_c.c +++ /dev/null @@ -1,228 +0,0 @@ -#include "config.h" - -#include - -#include "alMain.h" -#include "alu.h" -#include "alSource.h" -#include "alAuxEffectSlot.h" - - -static inline ALfloat point32(const ALfloat *restrict vals, ALuint UNUSED(frac)) -{ return vals[0]; } -static inline ALfloat lerp32(const ALfloat *restrict vals, ALuint frac) -{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); } -static inline ALfloat fir4_32(const ALfloat *restrict vals, ALuint frac) -{ return resample_fir4(vals[-1], vals[0], vals[1], vals[2], frac); } -static inline ALfloat fir8_32(const ALfloat *restrict vals, ALuint frac) -{ return resample_fir8(vals[-3], vals[-2], vals[-1], vals[0], vals[1], vals[2], vals[3], vals[4], frac); } - - -const ALfloat *Resample_copy32_C(const BsincState* UNUSED(state), const ALfloat *restrict src, ALuint UNUSED(frac), - ALuint UNUSED(increment), ALfloat *restrict dst, ALuint numsamples) -{ -#if defined(HAVE_SSE) || defined(HAVE_NEON) - /* Avoid copying the source data if it's aligned like the destination. */ - if((((intptr_t)src)&15) == (((intptr_t)dst)&15)) - return src; -#endif - memcpy(dst, src, numsamples*sizeof(ALfloat)); - return dst; -} - -#define DECL_TEMPLATE(Sampler) \ -const ALfloat *Resample_##Sampler##_C(const BsincState* UNUSED(state), \ - const ALfloat *restrict src, ALuint frac, ALuint increment, \ - ALfloat *restrict dst, ALuint numsamples) \ -{ \ - ALuint i; \ - for(i = 0;i < numsamples;i++) \ - { \ - dst[i] = Sampler(src, frac); \ - \ - frac += increment; \ - src += frac>>FRACTIONBITS; \ - frac &= FRACTIONMASK; \ - } \ - return dst; \ -} - -DECL_TEMPLATE(point32) -DECL_TEMPLATE(lerp32) -DECL_TEMPLATE(fir4_32) -DECL_TEMPLATE(fir8_32) - -#undef DECL_TEMPLATE - -const ALfloat *Resample_bsinc32_C(const BsincState *state, const ALfloat *restrict src, - ALuint frac, ALuint increment, ALfloat *restrict dst, - ALuint dstlen) -{ - const ALfloat *fil, *scd, *phd, *spd; - const ALfloat sf = state->sf; - const ALuint m = state->m; - const ALint l = state->l; - ALuint j_f, pi, i; - ALfloat pf, r; - ALint j_s; - - 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<coeffs[pi].filter; - scd = state->coeffs[pi].scDelta; - phd = state->coeffs[pi].phDelta; - spd = state->coeffs[pi].spDelta; - - // Apply the scale and phase interpolated filter. - r = 0.0f; - for(j_f = 0,j_s = l;j_f < m;j_f++,j_s++) - r += (fil[j_f] + sf*scd[j_f] + pf*(phd[j_f] + sf*spd[j_f])) * - src[j_s]; - dst[i] = r; - - frac += increment; - src += frac>>FRACTIONBITS; - frac &= FRACTIONMASK; - } - return dst; -} - - -void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALuint numsamples) -{ - ALuint i; - if(numsamples > 1) - { - dst[0] = filter->b0 * src[0] + - filter->b1 * filter->x[0] + - filter->b2 * filter->x[1] - - filter->a1 * filter->y[0] - - filter->a2 * filter->y[1]; - dst[1] = filter->b0 * src[1] + - filter->b1 * src[0] + - filter->b2 * filter->x[0] - - filter->a1 * dst[0] - - filter->a2 * filter->y[0]; - for(i = 2;i < numsamples;i++) - dst[i] = filter->b0 * src[i] + - filter->b1 * src[i-1] + - filter->b2 * src[i-2] - - filter->a1 * dst[i-1] - - filter->a2 * dst[i-2]; - filter->x[0] = src[i-1]; - filter->x[1] = src[i-2]; - filter->y[0] = dst[i-1]; - filter->y[1] = dst[i-2]; - } - else if(numsamples == 1) - { - dst[0] = filter->b0 * src[0] + - filter->b1 * filter->x[0] + - filter->b2 * filter->x[1] - - filter->a1 * filter->y[0] - - filter->a2 * filter->y[1]; - filter->x[1] = filter->x[0]; - filter->x[0] = src[0]; - filter->y[1] = filter->y[0]; - filter->y[0] = dst[0]; - } -} - - -static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2], - const ALuint IrSize, - ALfloat (*restrict Coeffs)[2], - const ALfloat (*restrict CoeffStep)[2], - ALfloat left, ALfloat right) -{ - ALuint c; - for(c = 0;c < IrSize;c++) - { - const ALuint off = (Offset+c)&HRIR_MASK; - Values[off][0] += Coeffs[c][0] * left; - Values[off][1] += Coeffs[c][1] * right; - Coeffs[c][0] += CoeffStep[c][0]; - Coeffs[c][1] += CoeffStep[c][1]; - } -} - -static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], - const ALuint IrSize, - ALfloat (*restrict Coeffs)[2], - ALfloat left, ALfloat right) -{ - ALuint c; - for(c = 0;c < IrSize;c++) - { - const ALuint off = (Offset+c)&HRIR_MASK; - Values[off][0] += Coeffs[c][0] * left; - Values[off][1] += Coeffs[c][1] * right; - } -} - -#define MixHrtf MixHrtf_C -#define MixDirectHrtf MixDirectHrtf_C -#include "mixer_inc.c" -#undef MixHrtf - - -void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], - ALfloat *CurrentGains, const ALfloat *TargetGains, ALuint Counter, ALuint OutPos, - ALuint BufferSize) -{ - ALfloat gain, delta, step; - ALuint c; - - delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f; - - for(c = 0;c < OutChans;c++) - { - ALuint pos = 0; - gain = CurrentGains[c]; - step = (TargetGains[c] - gain) * delta; - if(fabsf(step) > FLT_EPSILON) - { - ALuint minsize = minu(BufferSize, Counter); - for(;pos < minsize;pos++) - { - OutBuffer[c][OutPos+pos] += data[pos]*gain; - gain += step; - } - if(pos == Counter) - gain = TargetGains[c]; - CurrentGains[c] = gain; - } - - if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) - continue; - for(;pos < BufferSize;pos++) - OutBuffer[c][OutPos+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. - */ -void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALuint InChans, ALuint InPos, ALuint BufferSize) -{ - ALuint c, i; - - for(c = 0;c < InChans;c++) - { - ALfloat gain = Gains[c]; - if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) - continue; - - for(i = 0;i < BufferSize;i++) - OutBuffer[i] += data[c][InPos+i] * gain; - } -} diff --git a/Engine/lib/openal-soft/Alc/mixer_defs.h b/Engine/lib/openal-soft/Alc/mixer_defs.h deleted file mode 100644 index 249160025..000000000 --- a/Engine/lib/openal-soft/Alc/mixer_defs.h +++ /dev/null @@ -1,110 +0,0 @@ -#ifndef MIXER_DEFS_H -#define MIXER_DEFS_H - -#include "AL/alc.h" -#include "AL/al.h" -#include "alMain.h" -#include "alu.h" - -struct MixGains; - -struct MixHrtfParams; -struct HrtfState; - -/* C resamplers */ -const ALfloat *Resample_copy32_C(const BsincState *state, const ALfloat *restrict src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); -const ALfloat *Resample_point32_C(const BsincState *state, const ALfloat *restrict src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); -const ALfloat *Resample_lerp32_C(const BsincState *state, const ALfloat *restrict src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); -const ALfloat *Resample_fir4_32_C(const BsincState *state, const ALfloat *restrict src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); -const ALfloat *Resample_fir8_32_C(const BsincState *state, const ALfloat *restrict src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); -const ALfloat *Resample_bsinc32_C(const BsincState *state, const ALfloat *restrict src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); - - -/* C mixers */ -void MixHrtf_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint lidx, ALuint ridx, - const ALfloat *data, ALuint Counter, ALuint Offset, ALuint OutPos, - const ALuint IrSize, const struct MixHrtfParams *hrtfparams, - struct HrtfState *hrtfstate, ALuint BufferSize); -void MixDirectHrtf_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint lidx, ALuint ridx, - const ALfloat *data, ALuint Offset, const ALuint IrSize, - ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2], - ALuint BufferSize); -void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], - ALfloat *CurrentGains, const ALfloat *TargetGains, ALuint Counter, ALuint OutPos, - ALuint BufferSize); -void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains, - const ALfloat (*restrict data)[BUFFERSIZE], ALuint InChans, - ALuint InPos, ALuint BufferSize); - -/* SSE mixers */ -void MixHrtf_SSE(ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint lidx, ALuint ridx, - const ALfloat *data, ALuint Counter, ALuint Offset, ALuint OutPos, - const ALuint IrSize, const struct MixHrtfParams *hrtfparams, - struct HrtfState *hrtfstate, ALuint BufferSize); -void MixDirectHrtf_SSE(ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint lidx, ALuint ridx, - const ALfloat *data, ALuint Offset, const ALuint IrSize, - ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2], - ALuint BufferSize); -void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], - ALfloat *CurrentGains, const ALfloat *TargetGains, ALuint Counter, ALuint OutPos, - ALuint BufferSize); -void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains, - const ALfloat (*restrict data)[BUFFERSIZE], ALuint InChans, - ALuint InPos, ALuint BufferSize); - -/* SSE resamplers */ -inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *restrict frac_arr, ALuint *restrict pos_arr, ALuint size) -{ - ALuint i; - - pos_arr[0] = 0; - frac_arr[0] = frac; - for(i = 1;i < size;i++) - { - ALuint frac_tmp = frac_arr[i-1] + increment; - pos_arr[i] = pos_arr[i-1] + (frac_tmp>>FRACTIONBITS); - frac_arr[i] = frac_tmp&FRACTIONMASK; - } -} - -const ALfloat *Resample_bsinc32_SSE(const BsincState *state, const ALfloat *restrict src, ALuint frac, - ALuint increment, ALfloat *restrict dst, ALuint dstlen); - -const ALfloat *Resample_lerp32_SSE2(const BsincState *state, const ALfloat *restrict src, - ALuint frac, ALuint increment, ALfloat *restrict dst, - ALuint numsamples); -const ALfloat *Resample_lerp32_SSE41(const BsincState *state, const ALfloat *restrict src, - ALuint frac, ALuint increment, ALfloat *restrict dst, - ALuint numsamples); - -const ALfloat *Resample_fir4_32_SSE3(const BsincState *state, const ALfloat *restrict src, - ALuint frac, ALuint increment, ALfloat *restrict dst, - ALuint numsamples); -const ALfloat *Resample_fir4_32_SSE41(const BsincState *state, const ALfloat *restrict src, - ALuint frac, ALuint increment, ALfloat *restrict dst, - ALuint numsamples); - -const ALfloat *Resample_fir8_32_SSE3(const BsincState *state, const ALfloat *restrict src, - ALuint frac, ALuint increment, ALfloat *restrict dst, - ALuint numsamples); -const ALfloat *Resample_fir8_32_SSE41(const BsincState *state, const ALfloat *restrict src, - ALuint frac, ALuint increment, ALfloat *restrict dst, - ALuint numsamples); - -/* Neon mixers */ -void MixHrtf_Neon(ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint lidx, ALuint ridx, - const ALfloat *data, ALuint Counter, ALuint Offset, ALuint OutPos, - const ALuint IrSize, const struct MixHrtfParams *hrtfparams, - struct HrtfState *hrtfstate, ALuint BufferSize); -void MixDirectHrtf_Neon(ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint lidx, ALuint ridx, - const ALfloat *data, ALuint Offset, const ALuint IrSize, - ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2], - ALuint BufferSize); -void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], - ALfloat *CurrentGains, const ALfloat *TargetGains, ALuint Counter, ALuint OutPos, - ALuint BufferSize); -void MixRow_Neon(ALfloat *OutBuffer, const ALfloat *Gains, - const ALfloat (*restrict data)[BUFFERSIZE], ALuint InChans, - ALuint InPos, ALuint BufferSize); - -#endif /* MIXER_DEFS_H */ diff --git a/Engine/lib/openal-soft/Alc/mixer_inc.c b/Engine/lib/openal-soft/Alc/mixer_inc.c deleted file mode 100644 index 25dc2b588..000000000 --- a/Engine/lib/openal-soft/Alc/mixer_inc.c +++ /dev/null @@ -1,149 +0,0 @@ -#include "config.h" - -#include "alMain.h" -#include "alSource.h" - -#include "hrtf.h" -#include "mixer_defs.h" -#include "align.h" -#include "alu.h" - - -#define MAX_UPDATE_SAMPLES 128 - - -static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2], - const ALuint irSize, - ALfloat (*restrict Coeffs)[2], - const ALfloat (*restrict CoeffStep)[2], - ALfloat left, ALfloat right); -static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], - const ALuint irSize, - ALfloat (*restrict Coeffs)[2], - ALfloat left, ALfloat right); - - -void MixHrtf(ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint lidx, ALuint ridx, - const ALfloat *data, ALuint Counter, ALuint Offset, ALuint OutPos, - const ALuint IrSize, const MixHrtfParams *hrtfparams, HrtfState *hrtfstate, - ALuint BufferSize) -{ - ALfloat (*Coeffs)[2] = hrtfparams->Current->Coeffs; - ALuint Delay[2] = { hrtfparams->Current->Delay[0], hrtfparams->Current->Delay[1] }; - ALfloat out[MAX_UPDATE_SAMPLES][2]; - ALfloat left, right; - ALuint minsize; - ALuint pos, i; - - pos = 0; - if(Counter == 0) - goto skip_stepping; - - minsize = minu(BufferSize, Counter); - while(pos < minsize) - { - ALuint todo = minu(minsize-pos, MAX_UPDATE_SAMPLES); - - for(i = 0;i < todo;i++) - { - hrtfstate->History[Offset&HRTF_HISTORY_MASK] = data[pos++]; - left = lerp(hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS))&HRTF_HISTORY_MASK], - hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS)-1)&HRTF_HISTORY_MASK], - (Delay[0]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE)); - right = lerp(hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS))&HRTF_HISTORY_MASK], - hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS)-1)&HRTF_HISTORY_MASK], - (Delay[1]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE)); - - Delay[0] += hrtfparams->Steps.Delay[0]; - Delay[1] += hrtfparams->Steps.Delay[1]; - - hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f; - hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f; - Offset++; - - ApplyCoeffsStep(Offset, hrtfstate->Values, IrSize, Coeffs, hrtfparams->Steps.Coeffs, left, right); - out[i][0] = hrtfstate->Values[Offset&HRIR_MASK][0]; - out[i][1] = hrtfstate->Values[Offset&HRIR_MASK][1]; - } - - for(i = 0;i < todo;i++) - OutBuffer[lidx][OutPos+i] += out[i][0]; - for(i = 0;i < todo;i++) - OutBuffer[ridx][OutPos+i] += out[i][1]; - OutPos += todo; - } - - if(pos == Counter) - { - *hrtfparams->Current = *hrtfparams->Target; - Delay[0] = hrtfparams->Target->Delay[0]; - Delay[1] = hrtfparams->Target->Delay[1]; - } - else - { - hrtfparams->Current->Delay[0] = Delay[0]; - hrtfparams->Current->Delay[1] = Delay[1]; - } - -skip_stepping: - Delay[0] >>= HRTFDELAY_BITS; - Delay[1] >>= HRTFDELAY_BITS; - while(pos < BufferSize) - { - ALuint todo = minu(BufferSize-pos, MAX_UPDATE_SAMPLES); - - for(i = 0;i < todo;i++) - { - hrtfstate->History[Offset&HRTF_HISTORY_MASK] = data[pos++]; - left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK]; - right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK]; - - hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f; - hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f; - Offset++; - - ApplyCoeffs(Offset, hrtfstate->Values, IrSize, Coeffs, left, right); - out[i][0] = hrtfstate->Values[Offset&HRIR_MASK][0]; - out[i][1] = hrtfstate->Values[Offset&HRIR_MASK][1]; - } - - for(i = 0;i < todo;i++) - OutBuffer[lidx][OutPos+i] += out[i][0]; - for(i = 0;i < todo;i++) - OutBuffer[ridx][OutPos+i] += out[i][1]; - OutPos += todo; - } -} - -void MixDirectHrtf(ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint lidx, ALuint ridx, - const ALfloat *data, ALuint Offset, const ALuint IrSize, - ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2], - ALuint BufferSize) -{ - ALfloat out[MAX_UPDATE_SAMPLES][2]; - ALfloat insample; - ALuint pos, i; - - for(pos = 0;pos < BufferSize;) - { - ALuint todo = minu(BufferSize-pos, MAX_UPDATE_SAMPLES); - - for(i = 0;i < todo;i++) - { - Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f; - Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f; - Offset++; - - insample = *(data++); - ApplyCoeffs(Offset, Values, IrSize, Coeffs, insample, insample); - out[i][0] = Values[Offset&HRIR_MASK][0]; - out[i][1] = Values[Offset&HRIR_MASK][1]; - } - - for(i = 0;i < todo;i++) - OutBuffer[lidx][pos+i] += out[i][0]; - for(i = 0;i < todo;i++) - OutBuffer[ridx][pos+i] += out[i][1]; - pos += todo; - } -} diff --git a/Engine/lib/openal-soft/Alc/mixer_neon.c b/Engine/lib/openal-soft/Alc/mixer_neon.c deleted file mode 100644 index 6b506357b..000000000 --- a/Engine/lib/openal-soft/Alc/mixer_neon.c +++ /dev/null @@ -1,173 +0,0 @@ -#include "config.h" - -#include - -#include "AL/al.h" -#include "AL/alc.h" -#include "alMain.h" -#include "alu.h" -#include "hrtf.h" - - -static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2], - const ALuint IrSize, - ALfloat (*restrict Coeffs)[2], - const ALfloat (*restrict CoeffStep)[2], - ALfloat left, ALfloat right) -{ - ALuint c; - float32x4_t leftright4; - { - float32x2_t leftright2 = vdup_n_f32(0.0); - leftright2 = vset_lane_f32(left, leftright2, 0); - leftright2 = vset_lane_f32(right, leftright2, 1); - leftright4 = vcombine_f32(leftright2, leftright2); - } - for(c = 0;c < IrSize;c += 2) - { - const ALuint o0 = (Offset+c)&HRIR_MASK; - const ALuint o1 = (o0+1)&HRIR_MASK; - float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]), - vld1_f32((float32_t*)&Values[o1][0])); - float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]); - float32x4_t deltas = vld1q_f32(&CoeffStep[c][0]); - - vals = vmlaq_f32(vals, coefs, leftright4); - coefs = vaddq_f32(coefs, deltas); - - vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals)); - vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals)); - vst1q_f32(&Coeffs[c][0], coefs); - } -} - -static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], - const ALuint IrSize, - ALfloat (*restrict Coeffs)[2], - ALfloat left, ALfloat right) -{ - ALuint c; - float32x4_t leftright4; - { - float32x2_t leftright2 = vdup_n_f32(0.0); - leftright2 = vset_lane_f32(left, leftright2, 0); - leftright2 = vset_lane_f32(right, leftright2, 1); - leftright4 = vcombine_f32(leftright2, leftright2); - } - for(c = 0;c < IrSize;c += 2) - { - const ALuint o0 = (Offset+c)&HRIR_MASK; - const ALuint o1 = (o0+1)&HRIR_MASK; - float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]), - vld1_f32((float32_t*)&Values[o1][0])); - float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]); - - vals = vmlaq_f32(vals, coefs, leftright4); - - vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals)); - vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals)); - } -} - -#define MixHrtf MixHrtf_Neon -#define MixDirectHrtf MixDirectHrtf_Neon -#include "mixer_inc.c" -#undef MixHrtf - - -void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], - ALfloat *CurrentGains, const ALfloat *TargetGains, ALuint Counter, ALuint OutPos, - ALuint BufferSize) -{ - ALfloat gain, delta, step; - float32x4_t gain4; - ALuint c; - - delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f; - - for(c = 0;c < OutChans;c++) - { - ALuint pos = 0; - gain = CurrentGains[c]; - step = (TargetGains[c] - gain) * delta; - if(fabsf(step) > FLT_EPSILON) - { - ALuint minsize = minu(BufferSize, Counter); - /* Mix with applying gain steps in aligned multiples of 4. */ - if(minsize-pos > 3) - { - float32x4_t step4; - gain4 = vsetq_lane_f32(gain, gain4, 0); - gain4 = vsetq_lane_f32(gain + step, gain4, 1); - gain4 = vsetq_lane_f32(gain + step + step, gain4, 2); - gain4 = vsetq_lane_f32(gain + step + step + step, gain4, 3); - step4 = vdupq_n_f32(step + step + step + step); - do { - const float32x4_t val4 = vld1q_f32(&data[pos]); - float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]); - dry4 = vmlaq_f32(dry4, val4, gain4); - gain4 = vaddq_f32(gain4, step4); - vst1q_f32(&OutBuffer[c][OutPos+pos], dry4); - pos += 4; - } while(minsize-pos > 3); - /* NOTE: gain4 now represents the next four gains after the - * last four mixed samples, so the lowest element represents - * the next gain to apply. - */ - gain = vgetq_lane_f32(gain4, 0); - } - /* Mix with applying left over gain steps that aren't aligned multiples of 4. */ - for(;pos < minsize;pos++) - { - OutBuffer[c][OutPos+pos] += data[pos]*gain; - gain += step; - } - if(pos == Counter) - gain = TargetGains[c]; - CurrentGains[c] = gain; - - /* Mix until pos is aligned with 4 or the mix is done. */ - minsize = minu(BufferSize, (pos+3)&~3); - for(;pos < minsize;pos++) - OutBuffer[c][OutPos+pos] += data[pos]*gain; - } - - if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) - continue; - gain4 = vdupq_n_f32(gain); - for(;BufferSize-pos > 3;pos += 4) - { - const float32x4_t val4 = vld1q_f32(&data[pos]); - float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]); - dry4 = vmlaq_f32(dry4, val4, gain4); - vst1q_f32(&OutBuffer[c][OutPos+pos], dry4); - } - for(;pos < BufferSize;pos++) - OutBuffer[c][OutPos+pos] += data[pos]*gain; - } -} - -void MixRow_Neon(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALuint InChans, ALuint InPos, ALuint BufferSize) -{ - float32x4_t gain4; - ALuint c; - - for(c = 0;c < InChans;c++) - { - ALuint pos = 0; - ALfloat gain = Gains[c]; - if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) - continue; - - gain4 = vdupq_n_f32(gain); - for(;BufferSize-pos > 3;pos += 4) - { - const float32x4_t val4 = vld1q_f32(&data[c][InPos+pos]); - float32x4_t dry4 = vld1q_f32(&OutBuffer[pos]); - dry4 = vmlaq_f32(dry4, val4, gain4); - vst1q_f32(&OutBuffer[pos], dry4); - } - for(;pos < BufferSize;pos++) - OutBuffer[pos] += data[c][InPos+pos]*gain; - } -} diff --git a/Engine/lib/openal-soft/Alc/mixer_sse3.c b/Engine/lib/openal-soft/Alc/mixer_sse3.c deleted file mode 100644 index 66005e534..000000000 --- a/Engine/lib/openal-soft/Alc/mixer_sse3.c +++ /dev/null @@ -1,164 +0,0 @@ -/** - * OpenAL cross platform audio library, SSE3 mixer functions - * - * Copyright (C) 2014 by Timothy Arceri . - * Copyright (C) 2015 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 -#include -#include - -#include "alu.h" -#include "mixer_defs.h" - - -const ALfloat *Resample_fir4_32_SSE3(const BsincState* UNUSED(state), const ALfloat *restrict src, - ALuint frac, ALuint increment, ALfloat *restrict dst, - ALuint numsamples) -{ - const __m128i increment4 = _mm_set1_epi32(increment*4); - const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK); - union { alignas(16) ALuint i[4]; float f[4]; } pos_; - union { alignas(16) ALuint i[4]; float f[4]; } frac_; - __m128i frac4, pos4; - ALuint pos; - ALuint i; - - InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4); - - frac4 = _mm_castps_si128(_mm_load_ps(frac_.f)); - pos4 = _mm_castps_si128(_mm_load_ps(pos_.f)); - - --src; - for(i = 0;numsamples-i > 3;i += 4) - { - const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]]); - const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]]); - const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]]); - const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]]); - __m128 k0 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[0]]); - __m128 k1 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[1]]); - __m128 k2 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[2]]); - __m128 k3 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[3]]); - __m128 out; - - k0 = _mm_mul_ps(k0, val0); - k1 = _mm_mul_ps(k1, val1); - k2 = _mm_mul_ps(k2, val2); - k3 = _mm_mul_ps(k3, val3); - k0 = _mm_hadd_ps(k0, k1); - k2 = _mm_hadd_ps(k2, k3); - out = _mm_hadd_ps(k0, k2); - - _mm_store_ps(&dst[i], out); - - frac4 = _mm_add_epi32(frac4, increment4); - pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS)); - frac4 = _mm_and_si128(frac4, fracMask4); - - _mm_store_ps(pos_.f, _mm_castsi128_ps(pos4)); - _mm_store_ps(frac_.f, _mm_castsi128_ps(frac4)); - } - - /* NOTE: These four elements represent the position *after* the last four - * samples, so the lowest element is the next position to resample. - */ - pos = pos_.i[0]; - frac = frac_.i[0]; - - for(;i < numsamples;i++) - { - dst[i] = resample_fir4(src[pos], src[pos+1], src[pos+2], src[pos+3], frac); - - frac += increment; - pos += frac>>FRACTIONBITS; - frac &= FRACTIONMASK; - } - return dst; -} - -const ALfloat *Resample_fir8_32_SSE3(const BsincState* UNUSED(state), const ALfloat *restrict src, - ALuint frac, ALuint increment, ALfloat *restrict dst, - ALuint numsamples) -{ - const __m128i increment4 = _mm_set1_epi32(increment*4); - const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK); - union { alignas(16) ALuint i[4]; float f[4]; } pos_; - union { alignas(16) ALuint i[4]; float f[4]; } frac_; - __m128i frac4, pos4; - ALuint pos; - ALuint i, j; - - InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4); - - frac4 = _mm_castps_si128(_mm_load_ps(frac_.f)); - pos4 = _mm_castps_si128(_mm_load_ps(pos_.f)); - - src -= 3; - for(i = 0;numsamples-i > 3;i += 4) - { - __m128 out[2]; - for(j = 0;j < 8;j+=4) - { - const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]+j]); - const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]+j]); - const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]+j]); - const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]+j]); - __m128 k0 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[0]][j]); - __m128 k1 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[1]][j]); - __m128 k2 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[2]][j]); - __m128 k3 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[3]][j]); - - k0 = _mm_mul_ps(k0, val0); - k1 = _mm_mul_ps(k1, val1); - k2 = _mm_mul_ps(k2, val2); - k3 = _mm_mul_ps(k3, val3); - k0 = _mm_hadd_ps(k0, k1); - k2 = _mm_hadd_ps(k2, k3); - out[j>>2] = _mm_hadd_ps(k0, k2); - } - - out[0] = _mm_add_ps(out[0], out[1]); - _mm_store_ps(&dst[i], out[0]); - - frac4 = _mm_add_epi32(frac4, increment4); - pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS)); - frac4 = _mm_and_si128(frac4, fracMask4); - - _mm_store_ps(pos_.f, _mm_castsi128_ps(pos4)); - _mm_store_ps(frac_.f, _mm_castsi128_ps(frac4)); - } - - pos = pos_.i[0]; - frac = frac_.i[0]; - - for(;i < numsamples;i++) - { - dst[i] = resample_fir8(src[pos ], src[pos+1], src[pos+2], src[pos+3], - src[pos+4], src[pos+5], src[pos+6], src[pos+7], frac); - - frac += increment; - pos += frac>>FRACTIONBITS; - frac &= FRACTIONMASK; - } - return dst; -} diff --git a/Engine/lib/openal-soft/Alc/mixer_sse41.c b/Engine/lib/openal-soft/Alc/mixer_sse41.c deleted file mode 100644 index 7a4db6cf7..000000000 --- a/Engine/lib/openal-soft/Alc/mixer_sse41.c +++ /dev/null @@ -1,227 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 2014 by Timothy Arceri . - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * Or go to http://www.gnu.org/copyleft/lgpl.html - */ - -#include "config.h" - -#include -#include -#include - -#include "alu.h" -#include "mixer_defs.h" - - -const ALfloat *Resample_lerp32_SSE41(const BsincState* UNUSED(state), const ALfloat *restrict src, - ALuint frac, ALuint increment, ALfloat *restrict dst, - ALuint numsamples) -{ - const __m128i increment4 = _mm_set1_epi32(increment*4); - const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE); - const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK); - union { alignas(16) ALuint i[4]; float f[4]; } pos_; - union { alignas(16) ALuint i[4]; float f[4]; } frac_; - __m128i frac4, pos4; - ALuint pos; - ALuint i; - - InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4); - - frac4 = _mm_castps_si128(_mm_load_ps(frac_.f)); - pos4 = _mm_castps_si128(_mm_load_ps(pos_.f)); - - for(i = 0;numsamples-i > 3;i += 4) - { - const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]); - const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]); - - /* val1 + (val2-val1)*mu */ - const __m128 r0 = _mm_sub_ps(val2, val1); - const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4); - const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0)); - - _mm_store_ps(&dst[i], out); - - frac4 = _mm_add_epi32(frac4, increment4); - pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS)); - frac4 = _mm_and_si128(frac4, fracMask4); - - pos_.i[0] = _mm_extract_epi32(pos4, 0); - pos_.i[1] = _mm_extract_epi32(pos4, 1); - pos_.i[2] = _mm_extract_epi32(pos4, 2); - pos_.i[3] = _mm_extract_epi32(pos4, 3); - } - - /* NOTE: These four elements represent the position *after* the last four - * samples, so the lowest element is the next position to resample. - */ - pos = pos_.i[0]; - frac = _mm_cvtsi128_si32(frac4); - - for(;i < numsamples;i++) - { - dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE)); - - frac += increment; - pos += frac>>FRACTIONBITS; - frac &= FRACTIONMASK; - } - return dst; -} - -const ALfloat *Resample_fir4_32_SSE41(const BsincState* UNUSED(state), const ALfloat *restrict src, - ALuint frac, ALuint increment, ALfloat *restrict dst, - ALuint numsamples) -{ - const __m128i increment4 = _mm_set1_epi32(increment*4); - const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK); - union { alignas(16) ALuint i[4]; float f[4]; } pos_; - union { alignas(16) ALuint i[4]; float f[4]; } frac_; - __m128i frac4, pos4; - ALuint pos; - ALuint i; - - InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4); - - frac4 = _mm_castps_si128(_mm_load_ps(frac_.f)); - pos4 = _mm_castps_si128(_mm_load_ps(pos_.f)); - - --src; - for(i = 0;numsamples-i > 3;i += 4) - { - const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]]); - const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]]); - const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]]); - const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]]); - __m128 k0 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[0]]); - __m128 k1 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[1]]); - __m128 k2 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[2]]); - __m128 k3 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[3]]); - __m128 out; - - k0 = _mm_mul_ps(k0, val0); - k1 = _mm_mul_ps(k1, val1); - k2 = _mm_mul_ps(k2, val2); - k3 = _mm_mul_ps(k3, val3); - k0 = _mm_hadd_ps(k0, k1); - k2 = _mm_hadd_ps(k2, k3); - out = _mm_hadd_ps(k0, k2); - - _mm_store_ps(&dst[i], out); - - frac4 = _mm_add_epi32(frac4, increment4); - pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS)); - frac4 = _mm_and_si128(frac4, fracMask4); - - pos_.i[0] = _mm_extract_epi32(pos4, 0); - pos_.i[1] = _mm_extract_epi32(pos4, 1); - pos_.i[2] = _mm_extract_epi32(pos4, 2); - pos_.i[3] = _mm_extract_epi32(pos4, 3); - frac_.i[0] = _mm_extract_epi32(frac4, 0); - frac_.i[1] = _mm_extract_epi32(frac4, 1); - frac_.i[2] = _mm_extract_epi32(frac4, 2); - frac_.i[3] = _mm_extract_epi32(frac4, 3); - } - - pos = pos_.i[0]; - frac = frac_.i[0]; - - for(;i < numsamples;i++) - { - dst[i] = resample_fir4(src[pos], src[pos+1], src[pos+2], src[pos+3], frac); - - frac += increment; - pos += frac>>FRACTIONBITS; - frac &= FRACTIONMASK; - } - return dst; -} - -const ALfloat *Resample_fir8_32_SSE41(const BsincState* UNUSED(state), const ALfloat *restrict src, - ALuint frac, ALuint increment, ALfloat *restrict dst, - ALuint numsamples) -{ - const __m128i increment4 = _mm_set1_epi32(increment*4); - const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK); - union { alignas(16) ALuint i[4]; float f[4]; } pos_; - union { alignas(16) ALuint i[4]; float f[4]; } frac_; - __m128i frac4, pos4; - ALuint pos; - ALuint i, j; - - InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4); - - frac4 = _mm_castps_si128(_mm_load_ps(frac_.f)); - pos4 = _mm_castps_si128(_mm_load_ps(pos_.f)); - - src -= 3; - for(i = 0;numsamples-i > 3;i += 4) - { - __m128 out[2]; - for(j = 0;j < 8;j+=4) - { - const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]+j]); - const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]+j]); - const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]+j]); - const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]+j]); - __m128 k0 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[0]][j]); - __m128 k1 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[1]][j]); - __m128 k2 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[2]][j]); - __m128 k3 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[3]][j]); - - k0 = _mm_mul_ps(k0, val0); - k1 = _mm_mul_ps(k1, val1); - k2 = _mm_mul_ps(k2, val2); - k3 = _mm_mul_ps(k3, val3); - k0 = _mm_hadd_ps(k0, k1); - k2 = _mm_hadd_ps(k2, k3); - out[j>>2] = _mm_hadd_ps(k0, k2); - } - - out[0] = _mm_add_ps(out[0], out[1]); - _mm_store_ps(&dst[i], out[0]); - - frac4 = _mm_add_epi32(frac4, increment4); - pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS)); - frac4 = _mm_and_si128(frac4, fracMask4); - - pos_.i[0] = _mm_extract_epi32(pos4, 0); - pos_.i[1] = _mm_extract_epi32(pos4, 1); - pos_.i[2] = _mm_extract_epi32(pos4, 2); - pos_.i[3] = _mm_extract_epi32(pos4, 3); - frac_.i[0] = _mm_extract_epi32(frac4, 0); - frac_.i[1] = _mm_extract_epi32(frac4, 1); - frac_.i[2] = _mm_extract_epi32(frac4, 2); - frac_.i[3] = _mm_extract_epi32(frac4, 3); - } - - pos = pos_.i[0]; - frac = frac_.i[0]; - - for(;i < numsamples;i++) - { - dst[i] = resample_fir8(src[pos ], src[pos+1], src[pos+2], src[pos+3], - src[pos+4], src[pos+5], src[pos+6], src[pos+7], frac); - - frac += increment; - pos += frac>>FRACTIONBITS; - frac &= FRACTIONMASK; - } - return dst; -} diff --git a/Engine/lib/openal-soft/Alc/mixvoice.c b/Engine/lib/openal-soft/Alc/mixvoice.c new file mode 100644 index 000000000..2d935ce5e --- /dev/null +++ b/Engine/lib/openal-soft/Alc/mixvoice.c @@ -0,0 +1,761 @@ +/** + * 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 +#include +#include +#include +#include + +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" +#include "alSource.h" +#include "alBuffer.h" +#include "alListener.h" +#include "alAuxEffectSlot.h" +#include "sample_cvt.h" +#include "alu.h" +#include "alconfig.h" +#include "ringbuffer.h" + +#include "cpu_caps.h" +#include "mixer/defs.h" + + +static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE, + "MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!"); + +extern inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size); + + +/* 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!"); + + +enum Resampler ResamplerDefault = LinearResampler; + +MixerFunc MixSamples = Mix_C; +RowMixerFunc MixRowSamples = MixRow_C; +static HrtfMixerFunc MixHrtfSamples = MixHrtf_C; +static HrtfMixerBlendFunc MixHrtfBlendSamples = MixHrtfBlend_C; + +static MixerFunc SelectMixer(void) +{ +#ifdef HAVE_NEON + if((CPUCapFlags&CPU_CAP_NEON)) + return Mix_Neon; +#endif +#ifdef HAVE_SSE + if((CPUCapFlags&CPU_CAP_SSE)) + return Mix_SSE; +#endif + return Mix_C; +} + +static RowMixerFunc SelectRowMixer(void) +{ +#ifdef HAVE_NEON + if((CPUCapFlags&CPU_CAP_NEON)) + return MixRow_Neon; +#endif +#ifdef HAVE_SSE + if((CPUCapFlags&CPU_CAP_SSE)) + return MixRow_SSE; +#endif + return MixRow_C; +} + +static inline HrtfMixerFunc SelectHrtfMixer(void) +{ +#ifdef HAVE_NEON + if((CPUCapFlags&CPU_CAP_NEON)) + return MixHrtf_Neon; +#endif +#ifdef HAVE_SSE + if((CPUCapFlags&CPU_CAP_SSE)) + return MixHrtf_SSE; +#endif + return MixHrtf_C; +} + +static inline HrtfMixerBlendFunc SelectHrtfBlendMixer(void) +{ +#ifdef HAVE_NEON + if((CPUCapFlags&CPU_CAP_NEON)) + return MixHrtfBlend_Neon; +#endif +#ifdef HAVE_SSE + if((CPUCapFlags&CPU_CAP_SSE)) + return MixHrtfBlend_SSE; +#endif + return MixHrtfBlend_C; +} + +ResamplerFunc SelectResampler(enum Resampler resampler) +{ + switch(resampler) + { + case PointResampler: + return Resample_point_C; + case LinearResampler: +#ifdef HAVE_NEON + if((CPUCapFlags&CPU_CAP_NEON)) + return Resample_lerp_Neon; +#endif +#ifdef HAVE_SSE4_1 + if((CPUCapFlags&CPU_CAP_SSE4_1)) + return Resample_lerp_SSE41; +#endif +#ifdef HAVE_SSE2 + if((CPUCapFlags&CPU_CAP_SSE2)) + return Resample_lerp_SSE2; +#endif + return Resample_lerp_C; + case FIR4Resampler: + return Resample_cubic_C; + case BSinc12Resampler: + case BSinc24Resampler: +#ifdef HAVE_NEON + if((CPUCapFlags&CPU_CAP_NEON)) + return Resample_bsinc_Neon; +#endif +#ifdef HAVE_SSE + if((CPUCapFlags&CPU_CAP_SSE)) + return Resample_bsinc_SSE; +#endif + return Resample_bsinc_C; + } + + return Resample_point_C; +} + + +void aluInitMixer(void) +{ + const char *str; + + if(ConfigValueStr(NULL, NULL, "resampler", &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 = n; + else + WARN("Invalid resampler: %s\n", str); + } + } + + MixHrtfBlendSamples = SelectHrtfBlendMixer(); + MixHrtfSamples = SelectHrtfMixer(); + MixSamples = SelectMixer(); + MixRowSamples = SelectRowMixer(); +} + + +static void SendAsyncEvent(ALCcontext *context, ALuint enumtype, ALenum type, + ALuint objid, ALuint param, const char *msg) +{ + AsyncEvent evt; + evt.EnumType = enumtype; + evt.Type = type; + evt.ObjectId = objid; + evt.Param = param; + strcpy(evt.Message, msg); + if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1) + alsem_post(&context->EventSem); +} + + +static inline ALfloat Sample_ALubyte(ALubyte val) +{ return (val-128) * (1.0f/128.0f); } + +static inline ALfloat Sample_ALshort(ALshort val) +{ return val * (1.0f/32768.0f); } + +static inline ALfloat Sample_ALfloat(ALfloat val) +{ return val; } + +static inline ALfloat Sample_ALdouble(ALdouble val) +{ return (ALfloat)val; } + +typedef ALubyte ALmulaw; +static inline ALfloat Sample_ALmulaw(ALmulaw val) +{ return muLawDecompressionTable[val] * (1.0f/32768.0f); } + +typedef ALubyte ALalaw; +static inline ALfloat Sample_ALalaw(ALalaw val) +{ return aLawDecompressionTable[val] * (1.0f/32768.0f); } + +#define DECL_TEMPLATE(T) \ +static inline void Load_##T(ALfloat *restrict dst, const T *restrict src, \ + ALint srcstep, ALsizei samples) \ +{ \ + ALsizei i; \ + for(i = 0;i < samples;i++) \ + dst[i] += Sample_##T(src[i*srcstep]); \ +} + +DECL_TEMPLATE(ALubyte) +DECL_TEMPLATE(ALshort) +DECL_TEMPLATE(ALfloat) +DECL_TEMPLATE(ALdouble) +DECL_TEMPLATE(ALmulaw) +DECL_TEMPLATE(ALalaw) + +#undef DECL_TEMPLATE + +static void LoadSamples(ALfloat *restrict dst, const ALvoid *restrict src, ALint srcstep, + enum FmtType srctype, ALsizei samples) +{ +#define HANDLE_FMT(ET, ST) case ET: Load_##ST(dst, src, srcstep, samples); break + switch(srctype) + { + HANDLE_FMT(FmtUByte, ALubyte); + HANDLE_FMT(FmtShort, ALshort); + HANDLE_FMT(FmtFloat, ALfloat); + HANDLE_FMT(FmtDouble, ALdouble); + HANDLE_FMT(FmtMulaw, ALmulaw); + HANDLE_FMT(FmtAlaw, ALalaw); + } +#undef HANDLE_FMT +} + + +static const ALfloat *DoFilters(BiquadFilter *lpfilter, BiquadFilter *hpfilter, + ALfloat *restrict dst, const ALfloat *restrict src, + ALsizei numsamples, enum ActiveFilters type) +{ + ALsizei i; + switch(type) + { + case AF_None: + BiquadFilter_passthru(lpfilter, numsamples); + BiquadFilter_passthru(hpfilter, numsamples); + break; + + case AF_LowPass: + BiquadFilter_process(lpfilter, dst, src, numsamples); + BiquadFilter_passthru(hpfilter, numsamples); + return dst; + case AF_HighPass: + BiquadFilter_passthru(lpfilter, numsamples); + BiquadFilter_process(hpfilter, dst, src, numsamples); + return dst; + + case AF_BandPass: + for(i = 0;i < numsamples;) + { + ALfloat temp[256]; + ALsizei todo = mini(256, numsamples-i); + + BiquadFilter_process(lpfilter, temp, src+i, todo); + BiquadFilter_process(hpfilter, dst+i, temp, todo); + i += todo; + } + return dst; + } + return src; +} + + +/* This function uses these device temp buffers. */ +#define SOURCE_DATA_BUF 0 +#define RESAMPLED_BUF 1 +#define FILTERED_BUF 2 +#define NFC_DATA_BUF 3 +ALboolean MixSource(ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsizei SamplesToDo) +{ + ALCdevice *Device = Context->Device; + ALbufferlistitem *BufferListItem; + ALbufferlistitem *BufferLoopItem; + ALsizei NumChannels, SampleSize; + ALbitfieldSOFT enabledevt; + ALsizei buffers_done = 0; + ResamplerFunc Resample; + ALsizei DataPosInt; + ALsizei DataPosFrac; + ALint64 DataSize64; + ALint increment; + ALsizei Counter; + ALsizei OutPos; + ALsizei IrSize; + bool isplaying; + bool firstpass; + bool isstatic; + ALsizei chan; + ALsizei send; + + /* Get source info */ + isplaying = true; /* Will only be called while playing. */ + isstatic = !!(voice->Flags&VOICE_IS_STATIC); + DataPosInt = ATOMIC_LOAD(&voice->position, almemory_order_acquire); + DataPosFrac = ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed); + BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed); + BufferLoopItem = ATOMIC_LOAD(&voice->loop_buffer, almemory_order_relaxed); + NumChannels = voice->NumChannels; + SampleSize = voice->SampleSize; + increment = voice->Step; + + IrSize = (Device->HrtfHandle ? Device->HrtfHandle->irSize : 0); + + Resample = ((increment == FRACTIONONE && DataPosFrac == 0) ? + Resample_copy_C : voice->Resampler); + + Counter = (voice->Flags&VOICE_IS_FADING) ? SamplesToDo : 0; + firstpass = true; + OutPos = 0; + + do { + ALsizei SrcBufferSize, DstBufferSize; + + /* Figure out how many buffer samples will be needed */ + DataSize64 = SamplesToDo-OutPos; + DataSize64 *= increment; + DataSize64 += DataPosFrac+FRACTIONMASK; + DataSize64 >>= FRACTIONBITS; + DataSize64 += MAX_RESAMPLE_PADDING*2; + SrcBufferSize = (ALsizei)mini64(DataSize64, BUFFERSIZE); + + /* Figure out how many samples we can actually mix from this. */ + DataSize64 = SrcBufferSize; + DataSize64 -= MAX_RESAMPLE_PADDING*2; + DataSize64 <<= FRACTIONBITS; + DataSize64 -= DataPosFrac; + DstBufferSize = (ALsizei)mini64((DataSize64+(increment-1)) / increment, + SamplesToDo - OutPos); + + /* 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; + + /* It's impossible to have a buffer list item with no entries. */ + assert(BufferListItem->num_buffers > 0); + + for(chan = 0;chan < NumChannels;chan++) + { + const ALfloat *ResampledData; + ALfloat *SrcData = Device->TempBuffer[SOURCE_DATA_BUF]; + ALsizei FilledAmt; + + /* Load the previous samples into the source data first, and clear the rest. */ + memcpy(SrcData, voice->PrevSamples[chan], MAX_RESAMPLE_PADDING*sizeof(ALfloat)); + memset(SrcData+MAX_RESAMPLE_PADDING, 0, (BUFFERSIZE-MAX_RESAMPLE_PADDING)* + sizeof(ALfloat)); + FilledAmt = MAX_RESAMPLE_PADDING; + + if(isstatic) + { + /* 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; + const ALsizei LoopSize = LoopEnd - LoopStart; + + /* If current pos is beyond the loop range, do not loop */ + if(!BufferLoopItem || DataPosInt >= LoopEnd) + { + ALsizei SizeToDo = SrcBufferSize - FilledAmt; + ALsizei CompLen = 0; + ALsizei i; + + BufferLoopItem = NULL; + + for(i = 0;i < BufferListItem->num_buffers;i++) + { + const ALbuffer *buffer = BufferListItem->buffers[i]; + const ALubyte *Data = buffer->data; + ALsizei DataSize; + + if(DataPosInt >= buffer->SampleLen) + continue; + + /* Load what's left to play from the buffer */ + DataSize = mini(SizeToDo, buffer->SampleLen - DataPosInt); + CompLen = maxi(CompLen, DataSize); + + LoadSamples(&SrcData[FilledAmt], + &Data[(DataPosInt*NumChannels + chan)*SampleSize], + NumChannels, buffer->FmtType, DataSize + ); + } + FilledAmt += CompLen; + } + else + { + ALsizei SizeToDo = mini(SrcBufferSize - FilledAmt, LoopEnd - DataPosInt); + ALsizei CompLen = 0; + ALsizei i; + + for(i = 0;i < BufferListItem->num_buffers;i++) + { + const ALbuffer *buffer = BufferListItem->buffers[i]; + const ALubyte *Data = buffer->data; + ALsizei DataSize; + + if(DataPosInt >= buffer->SampleLen) + continue; + + /* Load what's left of this loop iteration */ + DataSize = mini(SizeToDo, buffer->SampleLen - DataPosInt); + CompLen = maxi(CompLen, DataSize); + + LoadSamples(&SrcData[FilledAmt], + &Data[(DataPosInt*NumChannels + chan)*SampleSize], + NumChannels, buffer->FmtType, DataSize + ); + } + FilledAmt += CompLen; + + while(SrcBufferSize > FilledAmt) + { + const ALsizei SizeToDo = mini(SrcBufferSize - FilledAmt, LoopSize); + + CompLen = 0; + for(i = 0;i < BufferListItem->num_buffers;i++) + { + const ALbuffer *buffer = BufferListItem->buffers[i]; + const ALubyte *Data = buffer->data; + ALsizei DataSize; + + if(LoopStart >= buffer->SampleLen) + continue; + + DataSize = mini(SizeToDo, buffer->SampleLen - LoopStart); + CompLen = maxi(CompLen, DataSize); + + LoadSamples(&SrcData[FilledAmt], + &Data[(LoopStart*NumChannels + chan)*SampleSize], + NumChannels, buffer->FmtType, DataSize + ); + } + FilledAmt += CompLen; + } + } + } + else + { + /* Crawl the buffer queue to fill in the temp buffer */ + ALbufferlistitem *tmpiter = BufferListItem; + ALsizei pos = DataPosInt; + + while(tmpiter && SrcBufferSize > FilledAmt) + { + ALsizei SizeToDo = SrcBufferSize - FilledAmt; + ALsizei i; + + for(i = 0;i < tmpiter->num_buffers;i++) + { + const ALbuffer *ALBuffer = tmpiter->buffers[i]; + ALsizei DataSize = ALBuffer ? ALBuffer->SampleLen : 0; + + if(DataSize > pos) + { + const ALubyte *Data = ALBuffer->data; + Data += (pos*NumChannels + chan)*SampleSize; + + DataSize = minu(SizeToDo, DataSize - pos); + LoadSamples(&SrcData[FilledAmt], Data, NumChannels, + ALBuffer->FmtType, DataSize); + } + } + if(pos > tmpiter->max_samples) + pos -= tmpiter->max_samples; + else + { + FilledAmt += tmpiter->max_samples - pos; + pos = 0; + } + if(SrcBufferSize > FilledAmt) + { + tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire); + if(!tmpiter) tmpiter = BufferLoopItem; + } + } + } + + /* Store the last source samples used for next time. */ + memcpy(voice->PrevSamples[chan], + &SrcData[(increment*DstBufferSize + DataPosFrac)>>FRACTIONBITS], + MAX_RESAMPLE_PADDING*sizeof(ALfloat) + ); + + /* Now resample, then filter and mix to the appropriate outputs. */ + ResampledData = Resample(&voice->ResampleState, + &SrcData[MAX_RESAMPLE_PADDING], DataPosFrac, increment, + Device->TempBuffer[RESAMPLED_BUF], DstBufferSize + ); + { + DirectParams *parms = &voice->Direct.Params[chan]; + const ALfloat *samples; + + samples = DoFilters( + &parms->LowPass, &parms->HighPass, Device->TempBuffer[FILTERED_BUF], + ResampledData, DstBufferSize, voice->Direct.FilterType + ); + if(!(voice->Flags&VOICE_HAS_HRTF)) + { + if(!Counter) + memcpy(parms->Gains.Current, parms->Gains.Target, + sizeof(parms->Gains.Current)); + if(!(voice->Flags&VOICE_HAS_NFC)) + MixSamples(samples, voice->Direct.Channels, voice->Direct.Buffer, + parms->Gains.Current, parms->Gains.Target, Counter, OutPos, + DstBufferSize + ); + else + { + ALfloat *nfcsamples = Device->TempBuffer[NFC_DATA_BUF]; + ALsizei chanoffset = 0; + + MixSamples(samples, + voice->Direct.ChannelsPerOrder[0], voice->Direct.Buffer, + parms->Gains.Current, parms->Gains.Target, Counter, OutPos, + DstBufferSize + ); + chanoffset += voice->Direct.ChannelsPerOrder[0]; +#define APPLY_NFC_MIX(order) \ + if(voice->Direct.ChannelsPerOrder[order] > 0) \ + { \ + NfcFilterProcess##order(&parms->NFCtrlFilter, nfcsamples, samples, \ + DstBufferSize); \ + MixSamples(nfcsamples, voice->Direct.ChannelsPerOrder[order], \ + voice->Direct.Buffer+chanoffset, parms->Gains.Current+chanoffset, \ + parms->Gains.Target+chanoffset, Counter, OutPos, DstBufferSize \ + ); \ + chanoffset += voice->Direct.ChannelsPerOrder[order]; \ + } + APPLY_NFC_MIX(1) + APPLY_NFC_MIX(2) + APPLY_NFC_MIX(3) +#undef APPLY_NFC_MIX + } + } + else + { + MixHrtfParams hrtfparams; + ALsizei fademix = 0; + int lidx, ridx; + + lidx = GetChannelIdxByName(&Device->RealOut, FrontLeft); + ridx = GetChannelIdxByName(&Device->RealOut, FrontRight); + assert(lidx != -1 && ridx != -1); + + if(!Counter) + { + /* No fading, just overwrite the old HRTF params. */ + parms->Hrtf.Old = parms->Hrtf.Target; + } + else 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; + } + else if(firstpass) + { + ALfloat gain; + + /* Fade between the coefficients over 128 samples. */ + fademix = mini(DstBufferSize, 128); + + /* 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. + */ + gain = lerp(parms->Hrtf.Old.Gain, parms->Hrtf.Target.Gain, + minf(1.0f, (ALfloat)fademix/Counter)); + 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 / (ALfloat)fademix; + + MixHrtfBlendSamples( + voice->Direct.Buffer[lidx], voice->Direct.Buffer[ridx], + samples, voice->Offset, OutPos, IrSize, &parms->Hrtf.Old, + &hrtfparams, &parms->Hrtf.State, fademix + ); + /* Update the old parameters with the result. */ + parms->Hrtf.Old = parms->Hrtf.Target; + if(fademix < Counter) + parms->Hrtf.Old.Gain = hrtfparams.Gain; + } + + if(fademix < DstBufferSize) + { + ALsizei todo = DstBufferSize - fademix; + ALfloat gain = parms->Hrtf.Target.Gain; + + /* Interpolate the target gain if the gain fading lasts + * longer than this mix. + */ + if(Counter > DstBufferSize) + gain = lerp(parms->Hrtf.Old.Gain, gain, + (ALfloat)todo/(Counter-fademix)); + + 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) / (ALfloat)todo; + MixHrtfSamples( + voice->Direct.Buffer[lidx], voice->Direct.Buffer[ridx], + samples+fademix, voice->Offset+fademix, OutPos+fademix, IrSize, + &hrtfparams, &parms->Hrtf.State, 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 = parms->Hrtf.Target.Gain; + } + } + } + + for(send = 0;send < Device->NumAuxSends;send++) + { + SendParams *parms = &voice->Send[send].Params[chan]; + const ALfloat *samples; + + if(!voice->Send[send].Buffer) + continue; + + samples = DoFilters( + &parms->LowPass, &parms->HighPass, Device->TempBuffer[FILTERED_BUF], + ResampledData, DstBufferSize, voice->Send[send].FilterType + ); + + if(!Counter) + memcpy(parms->Gains.Current, parms->Gains.Target, + sizeof(parms->Gains.Current)); + MixSamples(samples, voice->Send[send].Channels, voice->Send[send].Buffer, + parms->Gains.Current, parms->Gains.Target, Counter, OutPos, DstBufferSize + ); + } + } + /* Update positions */ + DataPosFrac += increment*DstBufferSize; + DataPosInt += DataPosFrac>>FRACTIONBITS; + DataPosFrac &= FRACTIONMASK; + + OutPos += DstBufferSize; + voice->Offset += DstBufferSize; + Counter = maxi(DstBufferSize, Counter) - DstBufferSize; + firstpass = false; + + if(isstatic) + { + if(BufferLoopItem) + { + /* Handle looping static source */ + const ALbuffer *Buffer = BufferListItem->buffers[0]; + ALsizei LoopStart = Buffer->LoopStart; + 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) + { + isplaying = false; + BufferListItem = NULL; + DataPosInt = 0; + DataPosFrac = 0; + break; + } + } + } + else while(1) + { + /* Handle streaming source */ + if(BufferListItem->max_samples > DataPosInt) + break; + + buffers_done += BufferListItem->num_buffers; + BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire); + if(!BufferListItem && !(BufferListItem=BufferLoopItem)) + { + isplaying = false; + DataPosInt = 0; + DataPosFrac = 0; + break; + } + + DataPosInt -= BufferListItem->max_samples; + } + } while(isplaying && OutPos < SamplesToDo); + + voice->Flags |= VOICE_IS_FADING; + + /* Update source info */ + ATOMIC_STORE(&voice->position, DataPosInt, almemory_order_relaxed); + ATOMIC_STORE(&voice->position_fraction, DataPosFrac, almemory_order_relaxed); + ATOMIC_STORE(&voice->current_buffer, BufferListItem, almemory_order_release); + + /* Send any events now, after the position/buffer info was updated. */ + enabledevt = ATOMIC_LOAD(&Context->EnabledEvts, almemory_order_acquire); + if(buffers_done > 0 && (enabledevt&EventType_BufferCompleted)) + SendAsyncEvent(Context, EventType_BufferCompleted, + AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, SourceID, buffers_done, "Buffer completed" + ); + + return isplaying; +} diff --git a/Engine/lib/openal-soft/Alc/panning.c b/Engine/lib/openal-soft/Alc/panning.c index 1162bbbcf..7f9e74e25 100644 --- a/Engine/lib/openal-soft/Alc/panning.c +++ b/Engine/lib/openal-soft/Alc/panning.c @@ -29,23 +29,21 @@ #include "alMain.h" #include "alAuxEffectSlot.h" #include "alu.h" +#include "alconfig.h" #include "bool.h" #include "ambdec.h" #include "bformatdec.h" +#include "filters/splitter.h" #include "uhjfilter.h" #include "bs2b.h" -extern inline void CalcXYZCoeffs(ALfloat x, ALfloat y, ALfloat z, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]); +extern inline void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]); +extern inline void ComputeDryPanGains(const DryMixParams *dry, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]); +extern inline void ComputeFirstOrderGains(const BFMixParams *foa, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]); -#define ZERO_ORDER_SCALE 0.0f -#define FIRST_ORDER_SCALE 1.0f -#define SECOND_ORDER_SCALE (1.0f / 1.22474f) -#define THIRD_ORDER_SCALE (1.0f / 1.30657f) - - -static const ALuint FuMa2ACN[MAX_AMBI_COEFFS] = { +static const ALsizei FuMa2ACN[MAX_AMBI_COEFFS] = { 0, /* W */ 3, /* X */ 1, /* Y */ @@ -63,55 +61,11 @@ static const ALuint FuMa2ACN[MAX_AMBI_COEFFS] = { 15, /* P */ 9, /* Q */ }; -static const ALuint ACN2ACN[MAX_AMBI_COEFFS] = { +static const ALsizei ACN2ACN[MAX_AMBI_COEFFS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; -/* NOTE: These are scale factors as applied to Ambisonics content. Decoder - * coefficients should be divided by these values to get proper N3D scalings. - */ -static const ALfloat UnitScale[MAX_AMBI_COEFFS] = { - 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 const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS] = { - 1.000000000f, /* ACN 0 (W), sqrt(1) */ - 1.732050808f, /* ACN 1 (Y), sqrt(3) */ - 1.732050808f, /* ACN 2 (Z), sqrt(3) */ - 1.732050808f, /* ACN 3 (X), sqrt(3) */ - 2.236067978f, /* ACN 4 (V), sqrt(5) */ - 2.236067978f, /* ACN 5 (T), sqrt(5) */ - 2.236067978f, /* ACN 6 (R), sqrt(5) */ - 2.236067978f, /* ACN 7 (S), sqrt(5) */ - 2.236067978f, /* ACN 8 (U), sqrt(5) */ - 2.645751311f, /* ACN 9 (Q), sqrt(7) */ - 2.645751311f, /* ACN 10 (O), sqrt(7) */ - 2.645751311f, /* ACN 11 (M), sqrt(7) */ - 2.645751311f, /* ACN 12 (K), sqrt(7) */ - 2.645751311f, /* ACN 13 (L), sqrt(7) */ - 2.645751311f, /* ACN 14 (N), sqrt(7) */ - 2.645751311f, /* ACN 15 (P), sqrt(7) */ -}; -static const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = { - 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) */ -}; - void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]) { @@ -158,8 +112,7 @@ void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MA * 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 - * loundness doesn't depend on the spread. That is, the factors are - * scaled so that ZH0 remains 1 regardless of the spread. Thus: + * loundness doesn't depend on the spread. Thus: * * ZH0 = 1.0f; * ZH1 = 0.5f * (ca+1.0f); @@ -169,11 +122,13 @@ void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MA * ZH5 = 0.0625f * (ca+1.0f)*(21.0f*ca*ca*ca*ca - 14.0f*ca*ca + 1.0f); */ ALfloat ca = cosf(spread * 0.5f); + /* Increase the source volume by up to +3dB for a full spread. */ + ALfloat scale = sqrtf(1.0f + spread/F_TAU); - ALfloat ZH0_norm = 1.0f; - ALfloat ZH1_norm = 0.5f * (ca+1.f); - ALfloat ZH2_norm = 0.5f * (ca+1.f)*ca; - ALfloat ZH3_norm = 0.125f * (ca+1.f)*(5.f*ca*ca-1.f); + 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; @@ -198,65 +153,33 @@ void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MA } } -void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]) +void CalcAnglePairwiseCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]) { - ALfloat dir[3] = { - sinf(azimuth) * cosf(elevation), - sinf(elevation), - -cosf(azimuth) * cosf(elevation) - }; - CalcDirectionCoeffs(dir, spread, coeffs); + ALfloat sign = (azimuth < 0.0f) ? -1.0f : 1.0f; + if(!(fabsf(azimuth) > F_PI_2)) + azimuth = minf(fabsf(azimuth) * F_PI_2 / (F_PI/6.0f), F_PI_2) * sign; + CalcAngleCoeffs(azimuth, elevation, spread, coeffs); } -void ComputeAmbientGainsMC(const ChannelConfig *chancoeffs, ALuint numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) +void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) { - ALuint i; - - for(i = 0;i < numchans;i++) - { - // The W coefficients are based on a mathematical average of the - // output. The square root of the base average provides for a more - // perceptual average volume, better suited to non-directional gains. - gains[i] = sqrtf(chancoeffs[i][0]) * ingain; - } - for(;i < MAX_OUTPUT_CHANNELS;i++) - gains[i] = 0.0f; -} - -void ComputeAmbientGainsBF(const BFChannelConfig *chanmap, ALuint numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) -{ - ALfloat gain = 0.0f; - ALuint i; - - for(i = 0;i < numchans;i++) - { - if(chanmap[i].Index == 0) - gain += chanmap[i].Scale; - } - gains[0] = gain * 1.414213562f * ingain; - for(i = 1;i < MAX_OUTPUT_CHANNELS;i++) - gains[i] = 0.0f; -} - -void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALuint numchans, ALuint numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) -{ - ALuint i, j; + ALsizei i, j; for(i = 0;i < numchans;i++) { float gain = 0.0f; for(j = 0;j < numcoeffs;j++) gain += chancoeffs[i][j]*coeffs[j]; - gains[i] = gain * ingain; + gains[i] = clampf(gain, 0.0f, 1.0f) * ingain; } for(;i < MAX_OUTPUT_CHANNELS;i++) gains[i] = 0.0f; } -void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALuint numchans, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) +void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) { - ALuint i; + ALsizei i; for(i = 0;i < numchans;i++) gains[i] = chanmap[i].Scale * coeffs[chanmap[i].Index] * ingain; @@ -264,24 +187,24 @@ void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALuint numchans, cons gains[i] = 0.0f; } -void ComputeFirstOrderGainsMC(const ChannelConfig *chancoeffs, ALuint numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) +void ComputeFirstOrderGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) { - ALuint i, j; + ALsizei i, j; for(i = 0;i < numchans;i++) { float gain = 0.0f; for(j = 0;j < 4;j++) gain += chancoeffs[i][j] * mtx[j]; - gains[i] = gain * ingain; + gains[i] = clampf(gain, 0.0f, 1.0f) * ingain; } for(;i < MAX_OUTPUT_CHANNELS;i++) gains[i] = 0.0f; } -void ComputeFirstOrderGainsBF(const BFChannelConfig *chanmap, ALuint numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) +void ComputeFirstOrderGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) { - ALuint i; + ALsizei i; for(i = 0;i < numchans;i++) gains[i] = chanmap[i].Scale * mtx[chanmap[i].Index] * ingain; @@ -341,49 +264,38 @@ typedef struct ChannelMap { ChannelConfig Config; } ChannelMap; -static void SetChannelMap(const enum Channel *devchans, ChannelConfig *ambicoeffs, - const ChannelMap *chanmap, size_t count, ALuint *outcount, - ALboolean isfuma) +static void SetChannelMap(const enum Channel devchans[MAX_OUTPUT_CHANNELS], + ChannelConfig *ambicoeffs, const ChannelMap *chanmap, + ALsizei count, ALsizei *outcount) { - const ALuint *acnmap = isfuma ? FuMa2ACN : ACN2ACN; - const ALfloat *n3dscale = isfuma ? FuMa2N3DScale : UnitScale; - size_t j, k; - ALuint i; + ALsizei maxchans = 0; + ALsizei i, j; - for(i = 0;i < MAX_OUTPUT_CHANNELS && devchans[i] != InvalidChannel;i++) + for(i = 0;i < count;i++) { - if(devchans[i] == LFE) + ALint idx = GetChannelIndex(devchans, chanmap[i].ChanName); + if(idx < 0) { - for(j = 0;j < MAX_AMBI_COEFFS;j++) - ambicoeffs[i][j] = 0.0f; + ERR("Failed to find %s channel in device\n", + GetLabelFromChannel(chanmap[i].ChanName)); continue; } - for(j = 0;j < count;j++) - { - if(devchans[i] != chanmap[j].ChanName) - continue; - - for(k = 0;k < MAX_AMBI_COEFFS;++k) - { - ALuint acn = acnmap[k]; - ambicoeffs[i][acn] = chanmap[j].Config[k] / n3dscale[acn]; - } - break; - } - if(j == count) - ERR("Failed to match %s channel (%u) in channel map\n", GetLabelFromChannel(devchans[i]), i); + maxchans = maxi(maxchans, idx+1); + for(j = 0;j < MAX_AMBI_COEFFS;j++) + ambicoeffs[idx][j] = chanmap[i].Config[j]; } - *outcount = i; + *outcount = mini(maxchans, MAX_OUTPUT_CHANNELS); } -static bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALuint speakermap[MAX_OUTPUT_CHANNELS]) +static bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALsizei speakermap[MAX_OUTPUT_CHANNELS]) { - ALuint i; + ALsizei i; for(i = 0;i < conf->NumSpeakers;i++) { - int c = -1; + enum Channel ch; + int chidx = -1; /* NOTE: AmbDec does not define any standard speaker names, however * for this to work we have to by able to find the output channel @@ -405,204 +317,268 @@ static bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALuint spe * use the side channels when the device is configured for back, * and vice-versa. */ - if(al_string_cmp_cstr(conf->Speakers[i].Name, "LF") == 0) - c = GetChannelIdxByName(device->RealOut, FrontLeft); - else if(al_string_cmp_cstr(conf->Speakers[i].Name, "RF") == 0) - c = GetChannelIdxByName(device->RealOut, FrontRight); - else if(al_string_cmp_cstr(conf->Speakers[i].Name, "CE") == 0) - c = GetChannelIdxByName(device->RealOut, FrontCenter); - else if(al_string_cmp_cstr(conf->Speakers[i].Name, "LS") == 0) + if(alstr_cmp_cstr(conf->Speakers[i].Name, "LF") == 0) + ch = FrontLeft; + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "RF") == 0) + ch = FrontRight; + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "CE") == 0) + ch = FrontCenter; + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "LS") == 0) { if(device->FmtChans == DevFmtX51Rear) - c = GetChannelIdxByName(device->RealOut, BackLeft); + ch = BackLeft; else - c = GetChannelIdxByName(device->RealOut, SideLeft); + ch = SideLeft; } - else if(al_string_cmp_cstr(conf->Speakers[i].Name, "RS") == 0) + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "RS") == 0) { if(device->FmtChans == DevFmtX51Rear) - c = GetChannelIdxByName(device->RealOut, BackRight); + ch = BackRight; else - c = GetChannelIdxByName(device->RealOut, SideRight); + ch = SideRight; } - else if(al_string_cmp_cstr(conf->Speakers[i].Name, "LB") == 0) + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "LB") == 0) { if(device->FmtChans == DevFmtX51) - c = GetChannelIdxByName(device->RealOut, SideLeft); + ch = SideLeft; else - c = GetChannelIdxByName(device->RealOut, BackLeft); + ch = BackLeft; } - else if(al_string_cmp_cstr(conf->Speakers[i].Name, "RB") == 0) + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "RB") == 0) { if(device->FmtChans == DevFmtX51) - c = GetChannelIdxByName(device->RealOut, SideRight); + ch = SideRight; else - c = GetChannelIdxByName(device->RealOut, BackRight); + ch = BackRight; } - else if(al_string_cmp_cstr(conf->Speakers[i].Name, "CB") == 0) - c = GetChannelIdxByName(device->RealOut, BackCenter); + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "CB") == 0) + ch = BackCenter; else { - const char *name = al_string_get_cstr(conf->Speakers[i].Name); + const char *name = alstr_get_cstr(conf->Speakers[i].Name); unsigned int n; - char ch; + char c; - if(sscanf(name, "AUX%u%c", &n, &ch) == 1 && n < 16) - c = GetChannelIdxByName(device->RealOut, Aux0+n); + if(sscanf(name, "AUX%u%c", &n, &c) == 1 && n < 16) + ch = Aux0+n; else { ERR("AmbDec speaker label \"%s\" not recognized\n", name); return false; } } - if(c == -1) + chidx = GetChannelIdxByName(&device->RealOut, ch); + if(chidx == -1) { ERR("Failed to lookup AmbDec speaker label %s\n", - al_string_get_cstr(conf->Speakers[i].Name)); + alstr_get_cstr(conf->Speakers[i].Name)); return false; } - speakermap[i] = c; + speakermap[i] = chidx; } return true; } -/* NOTE: These decoder coefficients are using FuMa channel ordering and - * normalization, since that's what was produced by the Ambisonic Decoder - * Toolbox. SetChannelMap will convert them to N3D. - */ static const ChannelMap MonoCfg[1] = { - { FrontCenter, { 1.414213562f } }, + { FrontCenter, { 1.0f } }, }, StereoCfg[2] = { - { FrontLeft, { 0.707106781f, 0.0f, 0.5f, 0.0f } }, - { FrontRight, { 0.707106781f, 0.0f, -0.5f, 0.0f } }, + { FrontLeft, { 5.00000000e-1f, 2.88675135e-1f, 0.0f, 5.52305643e-2f } }, + { FrontRight, { 5.00000000e-1f, -2.88675135e-1f, 0.0f, 5.52305643e-2f } }, }, QuadCfg[4] = { - { FrontLeft, { 0.353553f, 0.306186f, 0.306186f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.125000f } }, - { FrontRight, { 0.353553f, 0.306186f, -0.306186f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.125000f } }, - { BackLeft, { 0.353553f, -0.306186f, 0.306186f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.125000f } }, - { BackRight, { 0.353553f, -0.306186f, -0.306186f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.125000f } }, -}, X51SideCfg[5] = { - { FrontLeft, { 0.208954f, 0.199518f, 0.223424f, 0.0f, 0.0f, 0.0f, 0.0f, -0.012543f, 0.144260f } }, - { FrontRight, { 0.208950f, 0.199514f, -0.223425f, 0.0f, 0.0f, 0.0f, 0.0f, -0.012544f, -0.144258f } }, - { FrontCenter, { 0.109403f, 0.168250f, -0.000002f, 0.0f, 0.0f, 0.0f, 0.0f, 0.100431f, -0.000001f } }, - { SideLeft, { 0.470934f, -0.346484f, 0.327504f, 0.0f, 0.0f, 0.0f, 0.0f, -0.022188f, -0.041113f } }, - { SideRight, { 0.470936f, -0.346480f, -0.327507f, 0.0f, 0.0f, 0.0f, 0.0f, -0.022186f, 0.041114f } }, -}, X51RearCfg[5] = { - { FrontLeft, { 0.208954f, 0.199518f, 0.223424f, 0.0f, 0.0f, 0.0f, 0.0f, -0.012543f, 0.144260f } }, - { FrontRight, { 0.208950f, 0.199514f, -0.223425f, 0.0f, 0.0f, 0.0f, 0.0f, -0.012544f, -0.144258f } }, - { FrontCenter, { 0.109403f, 0.168250f, -0.000002f, 0.0f, 0.0f, 0.0f, 0.0f, 0.100431f, -0.000001f } }, - { BackLeft, { 0.470934f, -0.346484f, 0.327504f, 0.0f, 0.0f, 0.0f, 0.0f, -0.022188f, -0.041113f } }, - { BackRight, { 0.470936f, -0.346480f, -0.327507f, 0.0f, 0.0f, 0.0f, 0.0f, -0.022186f, 0.041114f } }, + { BackLeft, { 3.53553391e-1f, 2.04124145e-1f, 0.0f, -2.04124145e-1f } }, + { FrontLeft, { 3.53553391e-1f, 2.04124145e-1f, 0.0f, 2.04124145e-1f } }, + { FrontRight, { 3.53553391e-1f, -2.04124145e-1f, 0.0f, 2.04124145e-1f } }, + { BackRight, { 3.53553391e-1f, -2.04124145e-1f, 0.0f, -2.04124145e-1f } }, +}, X51SideCfg[4] = { + { SideLeft, { 3.33000782e-1f, 1.89084803e-1f, 0.0f, -2.00042375e-1f, -2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } }, + { FrontLeft, { 1.88542860e-1f, 1.27709292e-1f, 0.0f, 1.66295695e-1f, 7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } }, + { FrontRight, { 1.88542860e-1f, -1.27709292e-1f, 0.0f, 1.66295695e-1f, -7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } }, + { SideRight, { 3.33000782e-1f, -1.89084803e-1f, 0.0f, -2.00042375e-1f, 2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } }, +}, X51RearCfg[4] = { + { BackLeft, { 3.33000782e-1f, 1.89084803e-1f, 0.0f, -2.00042375e-1f, -2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } }, + { FrontLeft, { 1.88542860e-1f, 1.27709292e-1f, 0.0f, 1.66295695e-1f, 7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } }, + { FrontRight, { 1.88542860e-1f, -1.27709292e-1f, 0.0f, 1.66295695e-1f, -7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } }, + { BackRight, { 3.33000782e-1f, -1.89084803e-1f, 0.0f, -2.00042375e-1f, 2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } }, }, X61Cfg[6] = { - { FrontLeft, { 0.167065f, 0.200583f, 0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, 0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, 0.068910f } }, - { FrontRight, { 0.167065f, 0.200583f, -0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, -0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, -0.068910f } }, - { FrontCenter, { 0.109403f, 0.179490f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.142031f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.072024f, 0.000000f } }, - { BackCenter, { 0.353556f, -0.461940f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.165723f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.000000f } }, - { SideLeft, { 0.289151f, -0.081301f, 0.401292f, 0.0f, 0.0f, 0.0f, 0.0f, -0.188208f, -0.071420f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.010099f, -0.032897f } }, - { SideRight, { 0.289151f, -0.081301f, -0.401292f, 0.0f, 0.0f, 0.0f, 0.0f, -0.188208f, 0.071420f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.010099f, 0.032897f } }, -}, X71Cfg[7] = { - { FrontLeft, { 0.167065f, 0.200583f, 0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, 0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, 0.068910f } }, - { FrontRight, { 0.167065f, 0.200583f, -0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, -0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, -0.068910f } }, - { FrontCenter, { 0.109403f, 0.179490f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.142031f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.072024f, 0.000000f } }, - { BackLeft, { 0.224752f, -0.295009f, 0.170325f, 0.0f, 0.0f, 0.0f, 0.0f, 0.105349f, -0.182473f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.065799f } }, - { BackRight, { 0.224752f, -0.295009f, -0.170325f, 0.0f, 0.0f, 0.0f, 0.0f, 0.105349f, 0.182473f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.065799f } }, - { SideLeft, { 0.224739f, 0.000000f, 0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.065795f } }, - { SideRight, { 0.224739f, 0.000000f, -0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.065795f } }, + { SideLeft, { 2.04460341e-1f, 2.17177926e-1f, 0.0f, -4.39996780e-2f, -2.60790269e-2f, 0.0f, 0.0f, 0.0f, -6.87239792e-2f } }, + { FrontLeft, { 1.58923161e-1f, 9.21772680e-2f, 0.0f, 1.59658796e-1f, 6.66278083e-2f, 0.0f, 0.0f, 0.0f, 3.84686854e-2f } }, + { FrontRight, { 1.58923161e-1f, -9.21772680e-2f, 0.0f, 1.59658796e-1f, -6.66278083e-2f, 0.0f, 0.0f, 0.0f, 3.84686854e-2f } }, + { SideRight, { 2.04460341e-1f, -2.17177926e-1f, 0.0f, -4.39996780e-2f, 2.60790269e-2f, 0.0f, 0.0f, 0.0f, -6.87239792e-2f } }, + { BackCenter, { 2.50001688e-1f, 0.00000000e+0f, 0.0f, -2.50000094e-1f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, 6.05133395e-2f } }, +}, X71Cfg[6] = { + { BackLeft, { 2.04124145e-1f, 1.08880247e-1f, 0.0f, -1.88586120e-1f, -1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, 3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } }, + { SideLeft, { 2.04124145e-1f, 2.17760495e-1f, 0.0f, 0.00000000e+0f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, -1.49071198e-1f, -3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } }, + { FrontLeft, { 2.04124145e-1f, 1.08880247e-1f, 0.0f, 1.88586120e-1f, 1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, 3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } }, + { FrontRight, { 2.04124145e-1f, -1.08880247e-1f, 0.0f, 1.88586120e-1f, -1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, -3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } }, + { SideRight, { 2.04124145e-1f, -2.17760495e-1f, 0.0f, 0.00000000e+0f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, -1.49071198e-1f, 3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } }, + { BackRight, { 2.04124145e-1f, -1.08880247e-1f, 0.0f, -1.88586120e-1f, 1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, -3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } }, }; +static void InitNearFieldCtrl(ALCdevice *device, ALfloat ctrl_dist, ALsizei order, + const ALsizei *restrict chans_per_order) +{ + const char *devname = alstr_get_cstr(device->DeviceName); + ALsizei i; + + if(GetConfigValueBool(devname, "decoder", "nfc", 1) && ctrl_dist > 0.0f) + { + /* NFC is only used when AvgSpeakerDist is greater than 0, and can only + * be used when rendering to an ambisonic buffer. + */ + device->AvgSpeakerDist = minf(ctrl_dist, 10.0f); + + for(i = 0;i < order+1;i++) + device->Dry.NumChannelsPerOrder[i] = chans_per_order[i]; + for(;i < MAX_AMBI_ORDER+1;i++) + device->Dry.NumChannelsPerOrder[i] = 0; + } +} + +static void InitDistanceComp(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS]) +{ + const char *devname = alstr_get_cstr(device->DeviceName); + ALfloat maxdist = 0.0f; + size_t total = 0; + ALsizei i; + + for(i = 0;i < conf->NumSpeakers;i++) + maxdist = maxf(maxdist, conf->Speakers[i].Distance); + + if(GetConfigValueBool(devname, "decoder", "distance-comp", 1) && maxdist > 0.0f) + { + ALfloat srate = (ALfloat)device->Frequency; + for(i = 0;i < conf->NumSpeakers;i++) + { + ALsizei chan = speakermap[i]; + ALfloat delay; + + /* 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. + */ + delay = floorf((maxdist-conf->Speakers[i].Distance) / SPEEDOFSOUNDMETRESPERSEC * + srate + 0.5f); + if(delay >= (ALfloat)MAX_DELAY_LENGTH) + ERR("Delay for speaker \"%s\" exceeds buffer length (%f >= %u)\n", + alstr_get_cstr(conf->Speakers[i].Name), delay, MAX_DELAY_LENGTH); + + device->ChannelDelay[chan].Length = (ALsizei)clampf( + delay, 0.0f, (ALfloat)(MAX_DELAY_LENGTH-1) + ); + device->ChannelDelay[chan].Gain = conf->Speakers[i].Distance / maxdist; + TRACE("Channel %u \"%s\" distance compensation: %d samples, %f gain\n", chan, + alstr_get_cstr(conf->Speakers[i].Name), device->ChannelDelay[chan].Length, + device->ChannelDelay[chan].Gain + ); + + /* Round up to the next 4th sample, so each channel buffer starts + * 16-byte aligned. + */ + total += RoundUp(device->ChannelDelay[chan].Length, 4); + } + } + + if(total > 0) + { + device->ChannelDelay[0].Buffer = al_calloc(16, total * sizeof(ALfloat)); + for(i = 1;i < MAX_OUTPUT_CHANNELS;i++) + { + size_t len = RoundUp(device->ChannelDelay[i-1].Length, 4); + device->ChannelDelay[i].Buffer = device->ChannelDelay[i-1].Buffer + len; + } + } +} + static void InitPanning(ALCdevice *device) { const ChannelMap *chanmap = NULL; - ALuint coeffcount = 0; - ALfloat ambiscale; - size_t count = 0; - ALuint i, j; + ALsizei coeffcount = 0; + ALsizei count = 0; + ALsizei i, j; - ambiscale = 1.0f; switch(device->FmtChans) { case DevFmtMono: count = COUNTOF(MonoCfg); chanmap = MonoCfg; - ambiscale = ZERO_ORDER_SCALE; coeffcount = 1; break; case DevFmtStereo: count = COUNTOF(StereoCfg); chanmap = StereoCfg; - ambiscale = FIRST_ORDER_SCALE; coeffcount = 4; break; case DevFmtQuad: count = COUNTOF(QuadCfg); chanmap = QuadCfg; - ambiscale = SECOND_ORDER_SCALE; - coeffcount = 9; + coeffcount = 4; break; case DevFmtX51: count = COUNTOF(X51SideCfg); chanmap = X51SideCfg; - ambiscale = SECOND_ORDER_SCALE; coeffcount = 9; break; case DevFmtX51Rear: count = COUNTOF(X51RearCfg); chanmap = X51RearCfg; - ambiscale = SECOND_ORDER_SCALE; coeffcount = 9; break; case DevFmtX61: count = COUNTOF(X61Cfg); chanmap = X61Cfg; - ambiscale = THIRD_ORDER_SCALE; - coeffcount = 16; + coeffcount = 9; break; case DevFmtX71: count = COUNTOF(X71Cfg); chanmap = X71Cfg; - ambiscale = THIRD_ORDER_SCALE; coeffcount = 16; break; - case DevFmtAmbi1: - case DevFmtAmbi2: - case DevFmtAmbi3: + case DevFmtAmbi3D: break; } - if(device->FmtChans >= DevFmtAmbi1 && device->FmtChans <= DevFmtAmbi3) + if(device->FmtChans == DevFmtAmbi3D) { - const ALuint *acnmap = (device->AmbiFmt == AmbiFormat_FuMa) ? FuMa2ACN : ACN2ACN; - const ALfloat *n3dscale = (device->AmbiFmt == AmbiFormat_FuMa) ? FuMa2N3DScale : - (device->AmbiFmt == AmbiFormat_ACN_SN3D) ? SN3D2N3DScale : - /*(device->AmbiFmt == AmbiFormat_ACN_N3D) ?*/ UnitScale; + const char *devname = alstr_get_cstr(device->DeviceName); + const ALsizei *acnmap = (device->AmbiLayout == AmbiLayout_FuMa) ? FuMa2ACN : ACN2ACN; + const ALfloat *n3dscale = (device->AmbiScale == AmbiNorm_FuMa) ? FuMa2N3DScale : + (device->AmbiScale == AmbiNorm_SN3D) ? SN3D2N3DScale : + /*(device->AmbiScale == AmbiNorm_N3D) ?*/ N3D2N3DScale; + ALfloat nfc_delay = 0.0f; - count = (device->FmtChans == DevFmtAmbi3) ? 16 : - (device->FmtChans == DevFmtAmbi2) ? 9 : - (device->FmtChans == DevFmtAmbi1) ? 4 : 1; + count = (device->AmbiOrder == 3) ? 16 : + (device->AmbiOrder == 2) ? 9 : + (device->AmbiOrder == 1) ? 4 : 1; for(i = 0;i < count;i++) { - ALuint acn = acnmap[i]; + ALsizei acn = acnmap[i]; device->Dry.Ambi.Map[i].Scale = 1.0f/n3dscale[acn]; device->Dry.Ambi.Map[i].Index = acn; } device->Dry.CoeffCount = 0; device->Dry.NumChannels = count; - if(device->FmtChans == DevFmtAmbi1) + if(device->AmbiOrder < 2) { device->FOAOut.Ambi = device->Dry.Ambi; device->FOAOut.CoeffCount = device->Dry.CoeffCount; + device->FOAOut.NumChannels = 0; } else { + ALfloat w_scale=1.0f, xyz_scale=1.0f; + /* FOA output is always ACN+N3D for higher-order ambisonic output. * The upsampler expects this and will convert it for output. */ @@ -613,46 +589,95 @@ static void InitPanning(ALCdevice *device) device->FOAOut.Ambi.Map[i].Index = i; } device->FOAOut.CoeffCount = 0; + device->FOAOut.NumChannels = 4; - ambiup_reset(device->AmbiUp, device); + if(device->AmbiOrder >= 3) + { + w_scale = W_SCALE_3H3P; + xyz_scale = XYZ_SCALE_3H3P; + } + else + { + w_scale = W_SCALE_2H2P; + xyz_scale = XYZ_SCALE_2H2P; + } + ambiup_reset(device->AmbiUp, device, w_scale, xyz_scale); + } + + if(ConfigValueFloat(devname, "decoder", "nfc-ref-delay", &nfc_delay) && nfc_delay > 0.0f) + { + static const ALsizei chans_per_order[MAX_AMBI_ORDER+1] = { + 1, 3, 5, 7 + }; + nfc_delay = clampf(nfc_delay, 0.001f, 1000.0f); + InitNearFieldCtrl(device, nfc_delay * SPEEDOFSOUNDMETRESPERSEC, + device->AmbiOrder, chans_per_order); } } else { + ALfloat w_scale, xyz_scale; + SetChannelMap(device->RealOut.ChannelName, device->Dry.Ambi.Coeffs, - chanmap, count, &device->Dry.NumChannels, AL_TRUE); + chanmap, count, &device->Dry.NumChannels); device->Dry.CoeffCount = coeffcount; + w_scale = (device->Dry.CoeffCount > 9) ? W_SCALE_3H0P : + (device->Dry.CoeffCount > 4) ? W_SCALE_2H0P : 1.0f; + xyz_scale = (device->Dry.CoeffCount > 9) ? XYZ_SCALE_3H0P : + (device->Dry.CoeffCount > 4) ? XYZ_SCALE_2H0P : 1.0f; + memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi)); for(i = 0;i < device->Dry.NumChannels;i++) { - device->FOAOut.Ambi.Coeffs[i][0] = device->Dry.Ambi.Coeffs[i][0]; + device->FOAOut.Ambi.Coeffs[i][0] = device->Dry.Ambi.Coeffs[i][0] * w_scale; for(j = 1;j < 4;j++) - device->FOAOut.Ambi.Coeffs[i][j] = device->Dry.Ambi.Coeffs[i][j] * ambiscale; + device->FOAOut.Ambi.Coeffs[i][j] = device->Dry.Ambi.Coeffs[i][j] * xyz_scale; } device->FOAOut.CoeffCount = 4; + device->FOAOut.NumChannels = 0; } + device->RealOut.NumChannels = 0; } -static void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const ALuint speakermap[MAX_OUTPUT_CHANNELS]) +static void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS]) { ChannelMap chanmap[MAX_OUTPUT_CHANNELS]; - const ALfloat *coeff_scale = UnitScale; - ALfloat ambiscale = 1.0f; - ALuint i, j; + const ALfloat *coeff_scale = N3D2N3DScale; + ALfloat w_scale = 1.0f; + ALfloat xyz_scale = 1.0f; + ALsizei i, j; if(conf->FreqBands != 1) ERR("Basic renderer uses the high-frequency matrix as single-band (xover_freq = %.0fhz)\n", conf->XOverFreq); - if(conf->ChanMask > 0x1ff) - ambiscale = THIRD_ORDER_SCALE; - else if(conf->ChanMask > 0xf) - ambiscale = SECOND_ORDER_SCALE; - else if(conf->ChanMask > 0x1) - ambiscale = FIRST_ORDER_SCALE; + if((conf->ChanMask&AMBI_PERIPHONIC_MASK)) + { + if(conf->ChanMask > 0x1ff) + { + w_scale = W_SCALE_3H3P; + xyz_scale = XYZ_SCALE_3H3P; + } + else if(conf->ChanMask > 0xf) + { + w_scale = W_SCALE_2H2P; + xyz_scale = XYZ_SCALE_2H2P; + } + } else - ambiscale = 0.0f; + { + if(conf->ChanMask > 0x1ff) + { + w_scale = W_SCALE_3H0P; + xyz_scale = XYZ_SCALE_3H0P; + } + else if(conf->ChanMask > 0xf) + { + w_scale = W_SCALE_2H0P; + xyz_scale = XYZ_SCALE_2H0P; + } + } if(conf->CoeffScale == ADS_SN3D) coeff_scale = SN3D2N3DScale; @@ -661,9 +686,9 @@ static void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const A for(i = 0;i < conf->NumSpeakers;i++) { - ALuint chan = speakermap[i]; + ALsizei chan = speakermap[i]; ALfloat gain; - ALuint k = 0; + ALsizei k = 0; for(j = 0;j < MAX_AMBI_COEFFS;j++) chanmap[i].Config[j] = 0.0f; @@ -681,30 +706,32 @@ static void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const A } SetChannelMap(device->RealOut.ChannelName, device->Dry.Ambi.Coeffs, chanmap, - conf->NumSpeakers, &device->Dry.NumChannels, AL_FALSE); + conf->NumSpeakers, &device->Dry.NumChannels); device->Dry.CoeffCount = (conf->ChanMask > 0x1ff) ? 16 : (conf->ChanMask > 0xf) ? 9 : 4; memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi)); for(i = 0;i < device->Dry.NumChannels;i++) { - device->FOAOut.Ambi.Coeffs[i][0] = device->Dry.Ambi.Coeffs[i][0]; + device->FOAOut.Ambi.Coeffs[i][0] = device->Dry.Ambi.Coeffs[i][0] * w_scale; for(j = 1;j < 4;j++) - device->FOAOut.Ambi.Coeffs[i][j] = device->Dry.Ambi.Coeffs[i][j] * ambiscale; + device->FOAOut.Ambi.Coeffs[i][j] = device->Dry.Ambi.Coeffs[i][j] * xyz_scale; } device->FOAOut.CoeffCount = 4; + device->FOAOut.NumChannels = 0; + + device->RealOut.NumChannels = 0; + + InitDistanceComp(device, conf, speakermap); } -static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALuint speakermap[MAX_OUTPUT_CHANNELS]) +static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS]) { - const char *devname; - int decflags = 0; - size_t count; - ALuint i; - - devname = al_string_get_cstr(device->DeviceName); - if(GetConfigValueBool(devname, "decoder", "distance-comp", 1)) - decflags |= BFDF_DistanceComp; + static const ALsizei chans_per_order2d[MAX_AMBI_ORDER+1] = { 1, 2, 2, 2 }; + static const ALsizei chans_per_order3d[MAX_AMBI_ORDER+1] = { 1, 3, 5, 7 }; + ALfloat avg_dist; + ALsizei count; + ALsizei i; if((conf->ChanMask&AMBI_PERIPHONIC_MASK)) { @@ -736,15 +763,150 @@ static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALuin (conf->ChanMask > 0xf) ? (conf->ChanMask > 0x1ff) ? "third" : "second" : "first", (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? " periphonic" : "" ); - bformatdec_reset(device->AmbiDecoder, conf, count, device->Frequency, - speakermap, decflags); + bformatdec_reset(device->AmbiDecoder, conf, count, device->Frequency, speakermap); - if(bformatdec_getOrder(device->AmbiDecoder) < 2) + if(!(conf->ChanMask > 0xf)) { device->FOAOut.Ambi = device->Dry.Ambi; device->FOAOut.CoeffCount = device->Dry.CoeffCount; + device->FOAOut.NumChannels = 0; } else + { + memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi)); + if((conf->ChanMask&AMBI_PERIPHONIC_MASK)) + { + count = 4; + for(i = 0;i < count;i++) + { + device->FOAOut.Ambi.Map[i].Scale = 1.0f; + device->FOAOut.Ambi.Map[i].Index = i; + } + } + else + { + static const int map[3] = { 0, 1, 3 }; + count = 3; + for(i = 0;i < count;i++) + { + device->FOAOut.Ambi.Map[i].Scale = 1.0f; + device->FOAOut.Ambi.Map[i].Index = map[i]; + } + } + device->FOAOut.CoeffCount = 0; + device->FOAOut.NumChannels = count; + } + + device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); + + avg_dist = 0.0f; + for(i = 0;i < conf->NumSpeakers;i++) + avg_dist += conf->Speakers[i].Distance; + avg_dist /= (ALfloat)conf->NumSpeakers; + InitNearFieldCtrl(device, avg_dist, + (conf->ChanMask > 0x1ff) ? 3 : (conf->ChanMask > 0xf) ? 2 : 1, + (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? chans_per_order3d : chans_per_order2d + ); + + InitDistanceComp(device, conf, speakermap); +} + +static void InitHrtfPanning(ALCdevice *device) +{ + /* NOTE: azimuth goes clockwise. */ + static const struct AngularPoint AmbiPoints[] = { + { DEG2RAD( 90.0f), DEG2RAD( 0.0f) }, + { DEG2RAD( 35.0f), DEG2RAD( 45.0f) }, + { DEG2RAD( 35.0f), DEG2RAD( 135.0f) }, + { DEG2RAD( 35.0f), DEG2RAD(-135.0f) }, + { DEG2RAD( 35.0f), DEG2RAD( -45.0f) }, + { DEG2RAD( 0.0f), DEG2RAD( 0.0f) }, + { DEG2RAD( 0.0f), DEG2RAD( 45.0f) }, + { DEG2RAD( 0.0f), DEG2RAD( 90.0f) }, + { DEG2RAD( 0.0f), DEG2RAD( 135.0f) }, + { DEG2RAD( 0.0f), DEG2RAD( 180.0f) }, + { DEG2RAD( 0.0f), DEG2RAD(-135.0f) }, + { DEG2RAD( 0.0f), DEG2RAD( -90.0f) }, + { DEG2RAD( 0.0f), DEG2RAD( -45.0f) }, + { DEG2RAD(-35.0f), DEG2RAD( 45.0f) }, + { DEG2RAD(-35.0f), DEG2RAD( 135.0f) }, + { DEG2RAD(-35.0f), DEG2RAD(-135.0f) }, + { DEG2RAD(-35.0f), DEG2RAD( -45.0f) }, + { DEG2RAD(-90.0f), DEG2RAD( 0.0f) }, + }; + static const ALfloat AmbiMatrixFOA[][MAX_AMBI_COEFFS] = { + { 5.55555556e-02f, 0.00000000e+00f, 1.23717915e-01f, 0.00000000e+00f }, + { 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f }, + { 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f }, + { 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f }, + { 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f }, + { 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, 8.66025404e-02f }, + { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f }, + { 5.55555556e-02f, -8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f }, + { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f }, + { 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, -8.66025404e-02f }, + { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f }, + { 5.55555556e-02f, 8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f }, + { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f }, + { 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f }, + { 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f }, + { 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f }, + { 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f }, + { 5.55555556e-02f, 0.00000000e+00f, -1.23717915e-01f, 0.00000000e+00f }, + }, AmbiMatrixHOA[][MAX_AMBI_COEFFS] = { + { 5.55555556e-02f, 0.00000000e+00f, 1.23717915e-01f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f }, + { 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, 8.66025404e-02f, 0.00000000e+00f, 1.29099445e-01f }, + { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f, -6.83467648e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, -8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, -1.29099445e-01f }, + { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f, 6.83467648e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, -8.66025404e-02f, 0.00000000e+00f, 1.29099445e-01f }, + { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f, -6.83467648e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, 8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, -1.29099445e-01f }, + { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f, 6.83467648e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f }, + { 5.55555556e-02f, 0.00000000e+00f, -1.23717915e-01f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f }, + }; + static const ALfloat AmbiOrderHFGainFOA[MAX_AMBI_ORDER+1] = { + 3.00000000e+00f, 1.73205081e+00f + }, AmbiOrderHFGainHOA[MAX_AMBI_ORDER+1] = { + 2.40192231e+00f, 1.86052102e+00f, 9.60768923e-01f + }; + static const ALsizei IndexMap[6] = { 0, 1, 2, 3, 4, 8 }; + static const ALsizei ChansPerOrder[MAX_AMBI_ORDER+1] = { 1, 3, 2, 0 }; + const ALfloat (*restrict AmbiMatrix)[MAX_AMBI_COEFFS] = AmbiMatrixFOA; + const ALfloat *restrict AmbiOrderHFGain = AmbiOrderHFGainFOA; + ALsizei count = 4; + ALsizei i; + + static_assert(COUNTOF(AmbiPoints) == COUNTOF(AmbiMatrixFOA), "FOA Ambisonic HRTF mismatch"); + static_assert(COUNTOF(AmbiPoints) == COUNTOF(AmbiMatrixHOA), "HOA Ambisonic HRTF mismatch"); + static_assert(COUNTOF(AmbiPoints) <= HRTF_AMBI_MAX_CHANNELS, "HRTF_AMBI_MAX_CHANNELS is too small"); + + if(device->AmbiUp) + { + AmbiMatrix = AmbiMatrixHOA; + AmbiOrderHFGain = AmbiOrderHFGainHOA; + count = COUNTOF(IndexMap); + } + + device->Hrtf = al_calloc(16, FAM_SIZE(DirectHrtfState, Chan, count)); + + for(i = 0;i < count;i++) + { + device->Dry.Ambi.Map[i].Scale = 1.0f; + device->Dry.Ambi.Map[i].Index = IndexMap[i]; + } + device->Dry.CoeffCount = 0; + device->Dry.NumChannels = count; + + if(device->AmbiUp) { memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi)); for(i = 0;i < 4;i++) @@ -753,42 +915,37 @@ static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALuin device->FOAOut.Ambi.Map[i].Index = i; } device->FOAOut.CoeffCount = 0; + device->FOAOut.NumChannels = 4; + + ambiup_reset(device->AmbiUp, device, AmbiOrderHFGainFOA[0] / AmbiOrderHFGain[0], + AmbiOrderHFGainFOA[1] / AmbiOrderHFGain[1]); } -} - -static void InitHrtfPanning(ALCdevice *device) -{ - size_t count = 4; - ALuint i; - - for(i = 0;i < count;i++) + else { - device->Dry.Ambi.Map[i].Scale = 1.0f; - device->Dry.Ambi.Map[i].Index = i; + device->FOAOut.Ambi = device->Dry.Ambi; + device->FOAOut.CoeffCount = device->Dry.CoeffCount; + device->FOAOut.NumChannels = 0; } - device->Dry.CoeffCount = 0; - device->Dry.NumChannels = count; - device->FOAOut.Ambi = device->Dry.Ambi; - device->FOAOut.CoeffCount = device->Dry.CoeffCount; + device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); - memset(device->Hrtf.Coeffs, 0, sizeof(device->Hrtf.Coeffs)); - device->Hrtf.IrSize = BuildBFormatHrtf(device->Hrtf.Handle, - device->Hrtf.Coeffs, device->Dry.NumChannels + BuildBFormatHrtf(device->HrtfHandle, + device->Hrtf, device->Dry.NumChannels, AmbiPoints, AmbiMatrix, COUNTOF(AmbiPoints), + AmbiOrderHFGain ); - /* Round up to the nearest multiple of 8 */ - device->Hrtf.IrSize = (device->Hrtf.IrSize+7)&~7; + InitNearFieldCtrl(device, device->HrtfHandle->distance, device->AmbiUp ? 2 : 1, + ChansPerOrder); } static void InitUhjPanning(ALCdevice *device) { - size_t count = 3; - ALuint i; + ALsizei count = 3; + ALsizei i; for(i = 0;i < count;i++) { - ALuint acn = FuMa2ACN[i]; + ALsizei acn = FuMa2ACN[i]; device->Dry.Ambi.Map[i].Scale = 1.0f/FuMa2N3DScale[acn]; device->Dry.Ambi.Map[i].Index = acn; } @@ -797,48 +954,69 @@ static void InitUhjPanning(ALCdevice *device) device->FOAOut.Ambi = device->Dry.Ambi; device->FOAOut.CoeffCount = device->Dry.CoeffCount; + device->FOAOut.NumChannels = 0; + + device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); } void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf_appreq, enum HrtfRequestMode hrtf_userreq) { + /* Hold the HRTF the device last used, in case it's used again. */ + struct Hrtf *old_hrtf = device->HrtfHandle; const char *mode; bool headphones; int bs2blevel; size_t i; - device->Hrtf.Handle = NULL; - al_string_clear(&device->Hrtf.Name); + al_free(device->Hrtf); + device->Hrtf = NULL; + device->HrtfHandle = NULL; + alstr_clear(&device->HrtfName); device->Render_Mode = NormalRender; memset(&device->Dry.Ambi, 0, sizeof(device->Dry.Ambi)); device->Dry.CoeffCount = 0; device->Dry.NumChannels = 0; + for(i = 0;i < MAX_AMBI_ORDER+1;i++) + device->Dry.NumChannelsPerOrder[i] = 0; + + device->AvgSpeakerDist = 0.0f; + memset(device->ChannelDelay, 0, sizeof(device->ChannelDelay)); + for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) + { + device->ChannelDelay[i].Gain = 1.0f; + device->ChannelDelay[i].Length = 0; + } + + al_free(device->Stablizer); + device->Stablizer = NULL; if(device->FmtChans != DevFmtStereo) { - ALuint speakermap[MAX_OUTPUT_CHANNELS]; + ALsizei speakermap[MAX_OUTPUT_CHANNELS]; const char *devname, *layout = NULL; AmbDecConf conf, *pconf = NULL; + if(old_hrtf) + Hrtf_DecRef(old_hrtf); + old_hrtf = NULL; if(hrtf_appreq == Hrtf_Enable) - device->Hrtf.Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT; + device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT; ambdec_init(&conf); - devname = al_string_get_cstr(device->DeviceName); + devname = alstr_get_cstr(device->DeviceName); switch(device->FmtChans) { case DevFmtQuad: layout = "quad"; break; - case DevFmtX51: layout = "surround51"; break; - case DevFmtX51Rear: layout = "surround51rear"; 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 DevFmtAmbi1: - case DevFmtAmbi2: - case DevFmtAmbi3: + case DevFmtAmbi3D: break; } if(layout) @@ -863,25 +1041,20 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf if(pconf && GetConfigValueBool(devname, "decoder", "hq-mode", 0)) { - ambiup_free(device->AmbiUp); - device->AmbiUp = NULL; + ambiup_free(&device->AmbiUp); if(!device->AmbiDecoder) device->AmbiDecoder = bformatdec_alloc(); } else { - bformatdec_free(device->AmbiDecoder); - device->AmbiDecoder = NULL; - if(device->FmtChans > DevFmtAmbi1 && device->FmtChans <= DevFmtAmbi3) + bformatdec_free(&device->AmbiDecoder); + if(device->FmtChans != DevFmtAmbi3D || device->AmbiOrder < 2) + ambiup_free(&device->AmbiUp); + else { if(!device->AmbiUp) device->AmbiUp = ambiup_alloc(); } - else - { - ambiup_free(device->AmbiUp); - device->AmbiUp = NULL; - } } if(!pconf) @@ -891,20 +1064,54 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf else InitCustomPanning(device, pconf, speakermap); + /* 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(devname, NULL, "front-stablizer", 0)) + { + /* Initialize band-splitting filters for the front-left and + * front-right channels, with a crossover at 5khz (could be + * higher). + */ + ALfloat scale = (ALfloat)(5000.0 / device->Frequency); + FrontStablizer *stablizer = al_calloc(16, sizeof(*stablizer)); + + bandsplit_init(&stablizer->LFilter, scale); + stablizer->RFilter = stablizer->LFilter; + + /* Initialize all-pass filters for all other channels. */ + splitterap_init(&stablizer->APFilter[0], scale); + for(i = 1;i < (size_t)device->RealOut.NumChannels;i++) + stablizer->APFilter[i] = stablizer->APFilter[0]; + + device->Stablizer = stablizer; + } + break; + case DevFmtMono: + case DevFmtStereo: + case DevFmtQuad: + case DevFmtAmbi3D: + break; + } + TRACE("Front stablizer %s\n", device->Stablizer ? "enabled" : "disabled"); + ambdec_deinit(&conf); return; } - ambiup_free(device->AmbiUp); - device->AmbiUp = NULL; - bformatdec_free(device->AmbiDecoder); - device->AmbiDecoder = NULL; + bformatdec_free(&device->AmbiDecoder); headphones = device->IsHeadphones; if(device->Type != Loopback) { const char *mode; - if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "stereo-mode", &mode)) + if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "stereo-mode", &mode)) { if(strcasecmp(mode, "headphones") == 0) headphones = true; @@ -921,51 +1128,61 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf (hrtf_appreq == Hrtf_Enable); if(!usehrtf) goto no_hrtf; - device->Hrtf.Status = ALC_HRTF_ENABLED_SOFT; + device->HrtfStatus = ALC_HRTF_ENABLED_SOFT; if(headphones && hrtf_appreq != Hrtf_Disable) - device->Hrtf.Status = ALC_HRTF_HEADPHONES_DETECTED_SOFT; + device->HrtfStatus = ALC_HRTF_HEADPHONES_DETECTED_SOFT; } else { if(hrtf_userreq != Hrtf_Enable) { if(hrtf_appreq == Hrtf_Enable) - device->Hrtf.Status = ALC_HRTF_DENIED_SOFT; + device->HrtfStatus = ALC_HRTF_DENIED_SOFT; goto no_hrtf; } - device->Hrtf.Status = ALC_HRTF_REQUIRED_SOFT; + device->HrtfStatus = ALC_HRTF_REQUIRED_SOFT; } - if(VECTOR_SIZE(device->Hrtf.List) == 0) + if(VECTOR_SIZE(device->HrtfList) == 0) { - VECTOR_DEINIT(device->Hrtf.List); - device->Hrtf.List = EnumerateHrtf(device->DeviceName); + VECTOR_DEINIT(device->HrtfList); + device->HrtfList = EnumerateHrtf(device->DeviceName); } - if(hrtf_id >= 0 && (size_t)hrtf_id < VECTOR_SIZE(device->Hrtf.List)) + if(hrtf_id >= 0 && (size_t)hrtf_id < VECTOR_SIZE(device->HrtfList)) { - const HrtfEntry *entry = &VECTOR_ELEM(device->Hrtf.List, hrtf_id); - if(entry->hrtf->sampleRate == device->Frequency) + const EnumeratedHrtf *entry = &VECTOR_ELEM(device->HrtfList, hrtf_id); + struct Hrtf *hrtf = GetLoadedHrtf(entry->hrtf); + if(hrtf && hrtf->sampleRate == device->Frequency) { - device->Hrtf.Handle = entry->hrtf; - al_string_copy(&device->Hrtf.Name, entry->name); + device->HrtfHandle = hrtf; + alstr_copy(&device->HrtfName, entry->name); } + else if(hrtf) + Hrtf_DecRef(hrtf); } - for(i = 0;!device->Hrtf.Handle && i < VECTOR_SIZE(device->Hrtf.List);i++) + for(i = 0;!device->HrtfHandle && i < VECTOR_SIZE(device->HrtfList);i++) { - const HrtfEntry *entry = &VECTOR_ELEM(device->Hrtf.List, i); - if(entry->hrtf->sampleRate == device->Frequency) + const EnumeratedHrtf *entry = &VECTOR_ELEM(device->HrtfList, i); + struct Hrtf *hrtf = GetLoadedHrtf(entry->hrtf); + if(hrtf && hrtf->sampleRate == device->Frequency) { - device->Hrtf.Handle = entry->hrtf; - al_string_copy(&device->Hrtf.Name, entry->name); + device->HrtfHandle = hrtf; + alstr_copy(&device->HrtfName, entry->name); } + else if(hrtf) + Hrtf_DecRef(hrtf); } - if(device->Hrtf.Handle) + if(device->HrtfHandle) { + if(old_hrtf) + Hrtf_DecRef(old_hrtf); + old_hrtf = NULL; + device->Render_Mode = HrtfRender; - if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "hrtf-mode", &mode)) + if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "hrtf-mode", &mode)) { if(strcasecmp(mode, "full") == 0) device->Render_Mode = HrtfRender; @@ -975,24 +1192,46 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf ERR("Unexpected hrtf-mode: %s\n", mode); } - TRACE("HRTF enabled, \"%s\"\n", al_string_get_cstr(device->Hrtf.Name)); + if(device->Render_Mode == HrtfRender) + { + /* Don't bother with HOA when using full HRTF rendering. Nothing + * needs it, and it eases the CPU/memory load. + */ + ambiup_free(&device->AmbiUp); + } + else + { + if(!device->AmbiUp) + device->AmbiUp = ambiup_alloc(); + } + + TRACE("%s HRTF rendering enabled, using \"%s\"\n", + ((device->Render_Mode == HrtfRender) ? "Full" : "Basic"), + alstr_get_cstr(device->HrtfName) + ); InitHrtfPanning(device); return; } - device->Hrtf.Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT; + device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT; no_hrtf: + if(old_hrtf) + Hrtf_DecRef(old_hrtf); + old_hrtf = NULL; TRACE("HRTF disabled\n"); + device->Render_Mode = StereoPair; + + ambiup_free(&device->AmbiUp); + bs2blevel = ((headphones && hrtf_appreq != Hrtf_Disable) || (hrtf_appreq == Hrtf_Enable)) ? 5 : 0; if(device->Type != Loopback) - ConfigValueInt(al_string_get_cstr(device->DeviceName), NULL, "cf_level", &bs2blevel); + ConfigValueInt(alstr_get_cstr(device->DeviceName), NULL, "cf_level", &bs2blevel); if(bs2blevel > 0 && bs2blevel <= 6) { device->Bs2b = al_calloc(16, sizeof(*device->Bs2b)); bs2b_set_params(device->Bs2b, bs2blevel, device->Frequency); - device->Render_Mode = StereoPair; TRACE("BS2B enabled\n"); InitPanning(device); return; @@ -1000,13 +1239,12 @@ no_hrtf: TRACE("BS2B disabled\n"); - device->Render_Mode = NormalRender; - if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "stereo-panning", &mode)) + if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "stereo-encoding", &mode)) { - if(strcasecmp(mode, "paired") == 0) - device->Render_Mode = StereoPair; - else if(strcasecmp(mode, "uhj") != 0) - ERR("Unexpected stereo-panning: %s\n", mode); + if(strcasecmp(mode, "uhj") == 0) + device->Render_Mode = NormalRender; + else if(strcasecmp(mode, "panpot") != 0) + ERR("Unexpected stereo-encoding: %s\n", mode); } if(device->Render_Mode == NormalRender) { @@ -1023,7 +1261,7 @@ no_hrtf: void aluInitEffectPanning(ALeffectslot *slot) { - ALuint i; + ALsizei i; memset(slot->ChanMap, 0, sizeof(slot->ChanMap)); slot->NumChannels = 0; diff --git a/Engine/lib/openal-soft/Alc/polymorphism.h b/Engine/lib/openal-soft/Alc/polymorphism.h new file mode 100644 index 000000000..fa31fad25 --- /dev/null +++ b/Engine/lib/openal-soft/Alc/polymorphism.h @@ -0,0 +1,105 @@ +#ifndef POLYMORPHISM_H +#define POLYMORPHISM_H + +/* Macros to declare inheriting types, and to (down-)cast and up-cast. */ +#define DERIVE_FROM_TYPE(t) t t##_parent +#define STATIC_CAST(to, obj) (&(obj)->to##_parent) +#ifdef __GNUC__ +#define STATIC_UPCAST(to, from, obj) __extension__({ \ + static_assert(__builtin_types_compatible_p(from, __typeof(*(obj))), \ + "Invalid upcast object from type"); \ + (to*)((char*)(obj) - offsetof(to, from##_parent)); \ +}) +#else +#define STATIC_UPCAST(to, from, obj) ((to*)((char*)(obj) - offsetof(to, from##_parent))) +#endif + +/* Defines method forwards, which call the given parent's (T2's) implementation. */ +#define DECLARE_FORWARD(T1, T2, rettype, func) \ +rettype T1##_##func(T1 *obj) \ +{ return T2##_##func(STATIC_CAST(T2, obj)); } + +#define DECLARE_FORWARD1(T1, T2, rettype, func, argtype1) \ +rettype T1##_##func(T1 *obj, argtype1 a) \ +{ return T2##_##func(STATIC_CAST(T2, obj), a); } + +#define DECLARE_FORWARD2(T1, T2, rettype, func, argtype1, argtype2) \ +rettype T1##_##func(T1 *obj, argtype1 a, argtype2 b) \ +{ return T2##_##func(STATIC_CAST(T2, obj), a, b); } + +#define DECLARE_FORWARD3(T1, T2, rettype, func, argtype1, argtype2, argtype3) \ +rettype T1##_##func(T1 *obj, argtype1 a, argtype2 b, argtype3 c) \ +{ return T2##_##func(STATIC_CAST(T2, obj), a, b, c); } + +/* Defines method thunks, functions that call to the child's method. */ +#define DECLARE_THUNK(T1, T2, rettype, func) \ +static rettype T1##_##T2##_##func(T2 *obj) \ +{ return T1##_##func(STATIC_UPCAST(T1, T2, obj)); } + +#define DECLARE_THUNK1(T1, T2, rettype, func, argtype1) \ +static rettype T1##_##T2##_##func(T2 *obj, argtype1 a) \ +{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a); } + +#define DECLARE_THUNK2(T1, T2, rettype, func, argtype1, argtype2) \ +static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b) \ +{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b); } + +#define DECLARE_THUNK3(T1, T2, rettype, func, argtype1, argtype2, argtype3) \ +static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b, argtype3 c) \ +{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b, c); } + +#define DECLARE_THUNK4(T1, T2, rettype, func, argtype1, argtype2, argtype3, argtype4) \ +static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b, argtype3 c, argtype4 d) \ +{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b, c, d); } + +/* Defines the default functions used to (de)allocate a polymorphic object. */ +#define DECLARE_DEFAULT_ALLOCATORS(T) \ +static void* T##_New(size_t size) { return al_malloc(16, size); } \ +static void T##_Delete(void *ptr) { al_free(ptr); } + + +/* Helper to extract an argument list for virtual method calls. */ +#define EXTRACT_VCALL_ARGS(...) __VA_ARGS__)) + +/* Call a "virtual" method on an object, with arguments. */ +#define V(obj, func) ((obj)->vtbl->func((obj), EXTRACT_VCALL_ARGS +/* Call a "virtual" method on an object, with no arguments. */ +#define V0(obj, func) ((obj)->vtbl->func((obj) EXTRACT_VCALL_ARGS + + +/* Helper to extract an argument list for NEW_OBJ calls. */ +#define EXTRACT_NEW_ARGS(...) __VA_ARGS__); \ + } \ +} while(0) + +/* Allocate and construct an object, with arguments. */ +#define NEW_OBJ(_res, T) do { \ + _res = T##_New(sizeof(T)); \ + if(_res) \ + { \ + memset(_res, 0, sizeof(T)); \ + T##_Construct(_res, EXTRACT_NEW_ARGS +/* Allocate and construct an object, with no arguments. */ +#define NEW_OBJ0(_res, T) do { \ + _res = T##_New(sizeof(T)); \ + if(_res) \ + { \ + memset(_res, 0, sizeof(T)); \ + T##_Construct(_res EXTRACT_NEW_ARGS + +/* Destructs and deallocate an object. */ +#define DELETE_OBJ(obj) do { \ + if((obj) != NULL) \ + { \ + V0((obj),Destruct)(); \ + V0((obj),Delete)(); \ + } \ +} while(0) + + +/* Helper to get a type's vtable thunk for a child type. */ +#define GET_VTABLE2(T1, T2) (&(T1##_##T2##_vtable)) +/* Helper to set an object's vtable thunk for a child type. Used when constructing an object. */ +#define SET_VTABLE2(T1, T2, obj) (STATIC_CAST(T2, obj)->vtbl = GET_VTABLE2(T1, T2)) + +#endif /* POLYMORPHISM_H */ diff --git a/Engine/lib/openal-soft/Alc/ringbuffer.c b/Engine/lib/openal-soft/Alc/ringbuffer.c new file mode 100644 index 000000000..6c419cf8e --- /dev/null +++ b/Engine/lib/openal-soft/Alc/ringbuffer.c @@ -0,0 +1,295 @@ +/** + * 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 +#include +#include + +#include "ringbuffer.h" +#include "align.h" +#include "atomic.h" +#include "threads.h" +#include "almalloc.h" +#include "compat.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 { + ATOMIC(size_t) write_ptr; + ATOMIC(size_t) read_ptr; + size_t size; + size_t size_mask; + size_t elem_size; + + alignas(16) char buf[]; +}; + +ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz, int limit_writes) +{ + ll_ringbuffer_t *rb; + size_t power_of_two = 0; + + 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 NULL; + + rb = al_malloc(16, sizeof(*rb) + power_of_two*elem_sz); + if(!rb) return NULL; + + ATOMIC_INIT(&rb->write_ptr, 0); + ATOMIC_INIT(&rb->read_ptr, 0); + rb->size = limit_writes ? sz : power_of_two; + rb->size_mask = power_of_two - 1; + rb->elem_size = elem_sz; + return rb; +} + +void ll_ringbuffer_free(ll_ringbuffer_t *rb) +{ + al_free(rb); +} + +void ll_ringbuffer_reset(ll_ringbuffer_t *rb) +{ + ATOMIC_STORE(&rb->write_ptr, 0, almemory_order_release); + ATOMIC_STORE(&rb->read_ptr, 0, almemory_order_release); + memset(rb->buf, 0, (rb->size_mask+1)*rb->elem_size); +} + + +size_t ll_ringbuffer_read_space(const ll_ringbuffer_t *rb) +{ + size_t w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire); + size_t r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire); + return (w-r) & rb->size_mask; +} + +size_t ll_ringbuffer_write_space(const ll_ringbuffer_t *rb) +{ + size_t w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire); + size_t r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire); + w = (r-w-1) & rb->size_mask; + return (w > rb->size) ? rb->size : w; +} + + +size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt) +{ + size_t read_ptr; + size_t free_cnt; + size_t cnt2; + size_t to_read; + size_t n1, n2; + + free_cnt = ll_ringbuffer_read_space(rb); + if(free_cnt == 0) return 0; + + to_read = (cnt > free_cnt) ? free_cnt : cnt; + read_ptr = ATOMIC_LOAD(&rb->read_ptr, almemory_order_relaxed) & rb->size_mask; + + cnt2 = read_ptr + to_read; + if(cnt2 > rb->size_mask+1) + { + n1 = rb->size_mask+1 - read_ptr; + n2 = cnt2 & rb->size_mask; + } + else + { + n1 = to_read; + n2 = 0; + } + + memcpy(dest, &rb->buf[read_ptr*rb->elem_size], n1*rb->elem_size); + read_ptr += n1; + if(n2) + { + memcpy(dest + n1*rb->elem_size, &rb->buf[(read_ptr&rb->size_mask)*rb->elem_size], + n2*rb->elem_size); + read_ptr += n2; + } + ATOMIC_STORE(&rb->read_ptr, read_ptr, almemory_order_release); + return to_read; +} + +size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt) +{ + size_t free_cnt; + size_t cnt2; + size_t to_read; + size_t n1, n2; + size_t read_ptr; + + free_cnt = ll_ringbuffer_read_space(rb); + if(free_cnt == 0) return 0; + + to_read = (cnt > free_cnt) ? free_cnt : cnt; + read_ptr = ATOMIC_LOAD(&rb->read_ptr, almemory_order_relaxed) & rb->size_mask; + + cnt2 = read_ptr + to_read; + if(cnt2 > rb->size_mask+1) + { + n1 = rb->size_mask+1 - read_ptr; + n2 = cnt2 & rb->size_mask; + } + else + { + n1 = to_read; + n2 = 0; + } + + memcpy(dest, &rb->buf[read_ptr*rb->elem_size], n1*rb->elem_size); + if(n2) + { + read_ptr += n1; + memcpy(dest + n1*rb->elem_size, &rb->buf[(read_ptr&rb->size_mask)*rb->elem_size], + n2*rb->elem_size); + } + return to_read; +} + +size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt) +{ + size_t write_ptr; + size_t free_cnt; + size_t cnt2; + size_t to_write; + size_t n1, n2; + + free_cnt = ll_ringbuffer_write_space(rb); + if(free_cnt == 0) return 0; + + to_write = (cnt > free_cnt) ? free_cnt : cnt; + write_ptr = ATOMIC_LOAD(&rb->write_ptr, almemory_order_relaxed) & rb->size_mask; + + cnt2 = write_ptr + to_write; + if(cnt2 > rb->size_mask+1) + { + n1 = rb->size_mask+1 - write_ptr; + n2 = cnt2 & rb->size_mask; + } + else + { + n1 = to_write; + n2 = 0; + } + + memcpy(&rb->buf[write_ptr*rb->elem_size], src, n1*rb->elem_size); + write_ptr += n1; + if(n2) + { + memcpy(&rb->buf[(write_ptr&rb->size_mask)*rb->elem_size], src + n1*rb->elem_size, + n2*rb->elem_size); + write_ptr += n2; + } + ATOMIC_STORE(&rb->write_ptr, write_ptr, almemory_order_release); + return to_write; +} + + +void ll_ringbuffer_read_advance(ll_ringbuffer_t *rb, size_t cnt) +{ + ATOMIC_ADD(&rb->read_ptr, cnt, almemory_order_acq_rel); +} + +void ll_ringbuffer_write_advance(ll_ringbuffer_t *rb, size_t cnt) +{ + ATOMIC_ADD(&rb->write_ptr, cnt, almemory_order_acq_rel); +} + + +void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t vec[2]) +{ + size_t free_cnt; + size_t cnt2; + size_t w, r; + + w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire); + r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire); + w &= rb->size_mask; + r &= rb->size_mask; + free_cnt = (w-r) & rb->size_mask; + + cnt2 = r + free_cnt; + if(cnt2 > rb->size_mask+1) + { + /* Two part vector: the rest of the buffer after the current write ptr, + * plus some from the start of the buffer. */ + vec[0].buf = (char*)&rb->buf[r*rb->elem_size]; + vec[0].len = rb->size_mask+1 - r; + vec[1].buf = (char*)rb->buf; + vec[1].len = cnt2 & rb->size_mask; + } + else + { + /* Single part vector: just the rest of the buffer */ + vec[0].buf = (char*)&rb->buf[r*rb->elem_size]; + vec[0].len = free_cnt; + vec[1].buf = NULL; + vec[1].len = 0; + } +} + +void ll_ringbuffer_get_write_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t vec[2]) +{ + size_t free_cnt; + size_t cnt2; + size_t w, r; + + w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire); + r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire); + w &= rb->size_mask; + r &= rb->size_mask; + free_cnt = (r-w-1) & rb->size_mask; + if(free_cnt > rb->size) free_cnt = rb->size; + + cnt2 = w + free_cnt; + if(cnt2 > rb->size_mask+1) + { + /* Two part vector: the rest of the buffer after the current write ptr, + * plus some from the start of the buffer. */ + vec[0].buf = (char*)&rb->buf[w*rb->elem_size]; + vec[0].len = rb->size_mask+1 - w; + vec[1].buf = (char*)rb->buf; + vec[1].len = cnt2 & rb->size_mask; + } + else + { + vec[0].buf = (char*)&rb->buf[w*rb->elem_size]; + vec[0].len = free_cnt; + vec[1].buf = NULL; + vec[1].len = 0; + } +} diff --git a/Engine/lib/openal-soft/Alc/ringbuffer.h b/Engine/lib/openal-soft/Alc/ringbuffer.h new file mode 100644 index 000000000..0d05ec840 --- /dev/null +++ b/Engine/lib/openal-soft/Alc/ringbuffer.h @@ -0,0 +1,77 @@ +#ifndef RINGBUFFER_H +#define RINGBUFFER_H + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ll_ringbuffer ll_ringbuffer_t; +typedef struct ll_ringbuffer_data { + char *buf; + size_t len; +} ll_ringbuffer_data_t; + + +/** + * 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). + */ +ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz, int limit_writes); +/** Free all data associated with the ringbuffer `rb'. */ +void ll_ringbuffer_free(ll_ringbuffer_t *rb); +/** Reset the read and write pointers to zero. This is not thread safe. */ +void ll_ringbuffer_reset(ll_ringbuffer_t *rb); + +/** + * The non-copying data reader. `vec' is an array of two places. Set the values + * at `vec' to hold the current readable data at `rb'. If the readable data is + * in one segment the second segment has zero length. + */ +void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t vec[2]); +/** + * The non-copying data writer. `vec' is an array of two places. Set the values + * at `vec' to hold the current writeable data at `rb'. If the writeable data + * is in one segment the second segment has zero length. + */ +void ll_ringbuffer_get_write_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t vec[2]); + +/** + * 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 ll_ringbuffer_read_space(const ll_ringbuffer_t *rb); +/** + * The copying data reader. Copy at most `cnt' elements from `rb' to `dest'. + * Returns the actual number of elements copied. + */ +size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt); +/** + * The copying data reader w/o read pointer advance. Copy at most `cnt' + * elements from `rb' to `dest'. Returns the actual number of elements copied. + */ +size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt); +/** Advance the read pointer `cnt' places. */ +void ll_ringbuffer_read_advance(ll_ringbuffer_t *rb, size_t cnt); + +/** + * 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 ll_ringbuffer_write_space(const ll_ringbuffer_t *rb); +/** + * The copying data writer. Copy at most `cnt' elements to `rb' from `src'. + * Returns the actual number of elements copied. + */ +size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt); +/** Advance the write pointer `cnt' places. */ +void ll_ringbuffer_write_advance(ll_ringbuffer_t *rb, size_t cnt); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* RINGBUFFER_H */ diff --git a/Engine/lib/openal-soft/Alc/uhjfilter.c b/Engine/lib/openal-soft/Alc/uhjfilter.c index 0a702873c..42b0bc40f 100644 --- a/Engine/lib/openal-soft/Alc/uhjfilter.c +++ b/Engine/lib/openal-soft/Alc/uhjfilter.c @@ -9,36 +9,29 @@ #define MAX_UPDATE_SAMPLES 128 -static const ALfloat Filter1Coeff[4] = { - 0.6923878f, 0.9360654322959f, 0.9882295226860f, 0.9987488452737f +static const ALfloat Filter1CoeffSqr[4] = { + 0.479400865589f, 0.876218493539f, 0.976597589508f, 0.997499255936f }; -static const ALfloat Filter2Coeff[4] = { - 0.4021921162426f, 0.8561710882420f, 0.9722909545651f, 0.9952884791278f +static const ALfloat Filter2CoeffSqr[4] = { + 0.161758498368f, 0.733028932341f, 0.945349700329f, 0.990599156685f }; -static void allpass_process(AllPassState *state, ALfloat *restrict dst, const ALfloat *restrict src, const ALfloat aa, ALuint todo) +static void allpass_process(AllPassState *state, ALfloat *restrict dst, const ALfloat *restrict src, const ALfloat aa, ALsizei todo) { - ALuint i; + ALfloat z1 = state->z[0]; + ALfloat z2 = state->z[1]; + ALsizei i; - if(todo > 1) + for(i = 0;i < todo;i++) { - dst[0] = aa*(src[0] + state->y[1]) - state->x[1]; - dst[1] = aa*(src[1] + state->y[0]) - state->x[0]; - for(i = 2;i < todo;i++) - dst[i] = aa*(src[i] + dst[i-2]) - src[i-2]; - state->x[1] = src[i-2]; - state->x[0] = src[i-1]; - state->y[1] = dst[i-2]; - state->y[0] = dst[i-1]; - } - else if(todo == 1) - { - dst[0] = aa*(src[0] + state->y[1]) - state->x[1]; - state->x[1] = state->x[0]; - state->x[0] = src[0]; - state->y[1] = state->y[0]; - state->y[0] = dst[0]; + ALfloat input = src[i]; + ALfloat output = input*aa + z1; + z1 = z2; z2 = output*aa - input; + dst[i] = output; } + + state->z[0] = z1; + state->z[1] = z2; } @@ -62,47 +55,43 @@ static void allpass_process(AllPassState *state, ALfloat *restrict dst, const AL * know which is the intended result. */ -void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict RightOut, ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint SamplesToDo) +void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict RightOut, ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo) { ALfloat D[MAX_UPDATE_SAMPLES], S[MAX_UPDATE_SAMPLES]; ALfloat temp[2][MAX_UPDATE_SAMPLES]; - ALuint base, i; + ALsizei base, i; + + ASSUME(SamplesToDo > 0); for(base = 0;base < SamplesToDo;) { - ALuint todo = minu(SamplesToDo - base, MAX_UPDATE_SAMPLES); + ALsizei todo = mini(SamplesToDo - base, MAX_UPDATE_SAMPLES); + ASSUME(todo > 0); /* D = 0.6554516*Y */ for(i = 0;i < todo;i++) temp[0][i] = 0.6554516f*InSamples[2][base+i]; - allpass_process(&enc->Filter1_Y[0], temp[1], temp[0], - Filter1Coeff[0]*Filter1Coeff[0], todo); - allpass_process(&enc->Filter1_Y[1], temp[0], temp[1], - Filter1Coeff[1]*Filter1Coeff[1], todo); - allpass_process(&enc->Filter1_Y[2], temp[1], temp[0], - Filter1Coeff[2]*Filter1Coeff[2], todo); + allpass_process(&enc->Filter1_Y[0], temp[1], temp[0], Filter1CoeffSqr[0], todo); + allpass_process(&enc->Filter1_Y[1], temp[0], temp[1], Filter1CoeffSqr[1], todo); + allpass_process(&enc->Filter1_Y[2], temp[1], temp[0], Filter1CoeffSqr[2], todo); + allpass_process(&enc->Filter1_Y[3], temp[0], temp[1], 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] = enc->Filter1_Y[3].y[0]; - allpass_process(&enc->Filter1_Y[3], temp[0], temp[1], - Filter1Coeff[3]*Filter1Coeff[3], todo); + D[0] = enc->LastY; for(i = 1;i < todo;i++) D[i] = temp[0][i-1]; + enc->LastY = temp[0][i-1]; /* D += j(-0.3420201*W + 0.5098604*X) */ for(i = 0;i < todo;i++) temp[0][i] = -0.3420201f*InSamples[0][base+i] + 0.5098604f*InSamples[1][base+i]; - allpass_process(&enc->Filter2_WX[0], temp[1], temp[0], - Filter2Coeff[0]*Filter2Coeff[0], todo); - allpass_process(&enc->Filter2_WX[1], temp[0], temp[1], - Filter2Coeff[1]*Filter2Coeff[1], todo); - allpass_process(&enc->Filter2_WX[2], temp[1], temp[0], - Filter2Coeff[2]*Filter2Coeff[2], todo); - allpass_process(&enc->Filter2_WX[3], temp[0], temp[1], - Filter2Coeff[3]*Filter2Coeff[3], todo); + allpass_process(&enc->Filter2_WX[0], temp[1], temp[0], Filter2CoeffSqr[0], todo); + allpass_process(&enc->Filter2_WX[1], temp[0], temp[1], Filter2CoeffSqr[1], todo); + allpass_process(&enc->Filter2_WX[2], temp[1], temp[0], Filter2CoeffSqr[2], todo); + allpass_process(&enc->Filter2_WX[3], temp[0], temp[1], Filter2CoeffSqr[3], todo); for(i = 0;i < todo;i++) D[i] += temp[0][i]; @@ -110,17 +99,14 @@ void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict R for(i = 0;i < todo;i++) temp[0][i] = 0.9396926f*InSamples[0][base+i] + 0.1855740f*InSamples[1][base+i]; - allpass_process(&enc->Filter1_WX[0], temp[1], temp[0], - Filter1Coeff[0]*Filter1Coeff[0], todo); - allpass_process(&enc->Filter1_WX[1], temp[0], temp[1], - Filter1Coeff[1]*Filter1Coeff[1], todo); - allpass_process(&enc->Filter1_WX[2], temp[1], temp[0], - Filter1Coeff[2]*Filter1Coeff[2], todo); - S[0] = enc->Filter1_WX[3].y[0]; - allpass_process(&enc->Filter1_WX[3], temp[0], temp[1], - Filter1Coeff[3]*Filter1Coeff[3], todo); + allpass_process(&enc->Filter1_WX[0], temp[1], temp[0], Filter1CoeffSqr[0], todo); + allpass_process(&enc->Filter1_WX[1], temp[0], temp[1], Filter1CoeffSqr[1], todo); + allpass_process(&enc->Filter1_WX[2], temp[1], temp[0], Filter1CoeffSqr[2], todo); + allpass_process(&enc->Filter1_WX[3], temp[0], temp[1], Filter1CoeffSqr[3], todo); + S[0] = enc->LastWX; for(i = 1;i < todo;i++) S[i] = temp[0][i-1]; + enc->LastWX = temp[0][i-1]; /* Left = (S + D)/2.0 */ for(i = 0;i < todo;i++) diff --git a/Engine/lib/openal-soft/Alc/uhjfilter.h b/Engine/lib/openal-soft/Alc/uhjfilter.h index 14572bc31..e773e0a72 100644 --- a/Engine/lib/openal-soft/Alc/uhjfilter.h +++ b/Engine/lib/openal-soft/Alc/uhjfilter.h @@ -6,8 +6,7 @@ #include "alMain.h" typedef struct AllPassState { - ALfloat x[2]; /* Last two input samples */ - ALfloat y[2]; /* Last two output samples */ + ALfloat z[2]; } AllPassState; /* Encoding 2-channel UHJ from B-Format is done as: @@ -36,13 +35,15 @@ typedef struct AllPassState { */ typedef struct Uhj2Encoder { - AllPassState Filter1_WX[4]; AllPassState Filter1_Y[4]; AllPassState Filter2_WX[4]; + AllPassState Filter1_WX[4]; + ALfloat LastY, LastWX; } Uhj2Encoder; /* Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input - * signal. */ -void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict RightOut, ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint SamplesToDo); + * signal. The input must use FuMa channel ordering and scaling. + */ +void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict RightOut, ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo); #endif /* UHJFILTER_H */ diff --git a/Engine/lib/openal-soft/Alc/vector.h b/Engine/lib/openal-soft/Alc/vector.h index 4bb92458f..ed9acfb02 100644 --- a/Engine/lib/openal-soft/Alc/vector.h +++ b/Engine/lib/openal-soft/Alc/vector.h @@ -37,13 +37,15 @@ typedef const _##N* const_##N; \ if(((_x) ? (_x)->Capacity : 0) < _cap) \ { \ + ptrdiff_t data_offset = (_x) ? (char*)((_x)->Data) - (char*)(_x) : \ + sizeof(*(_x)); \ size_t old_size = ((_x) ? (_x)->Size : 0); \ void *temp; \ \ - temp = al_calloc(16, sizeof(*(_x)) + sizeof((_x)->Data[0])*_cap); \ + temp = al_calloc(16, data_offset + sizeof((_x)->Data[0])*_cap); \ assert(temp != NULL); \ if((_x)) \ - memcpy(((ALubyte*)temp)+sizeof(*(_x)), (_x)->Data, \ + memcpy(((char*)temp)+data_offset, (_x)->Data, \ sizeof((_x)->Data[0])*old_size); \ \ al_free((_x)); \ @@ -78,13 +80,6 @@ typedef const _##N* const_##N; _f(_iter); \ } while(0) -#define VECTOR_FOR_EACH_PARAMS(_t, _x, _f, ...) do { \ - _t *_iter = VECTOR_BEGIN((_x)); \ - _t *_end = VECTOR_END((_x)); \ - for(;_iter != _end;++_iter) \ - _f(__VA_ARGS__, _iter); \ -} while(0) - #define VECTOR_FIND_IF(_i, _t, _x, _f) do { \ _t *_iter = VECTOR_BEGIN((_x)); \ _t *_end = VECTOR_END((_x)); \ @@ -96,15 +91,4 @@ typedef const _##N* const_##N; (_i) = _iter; \ } while(0) -#define VECTOR_FIND_IF_PARMS(_i, _t, _x, _f, ...) do { \ - _t *_iter = VECTOR_BEGIN((_x)); \ - _t *_end = VECTOR_END((_x)); \ - for(;_iter != _end;++_iter) \ - { \ - if(_f(__VA_ARGS__, _iter)) \ - break; \ - } \ - (_i) = _iter; \ -} while(0) - #endif /* AL_VECTOR_H */ diff --git a/Engine/lib/openal-soft/CMakeLists.txt b/Engine/lib/openal-soft/CMakeLists.txt index cf6d7ca6f..6a4f8ff4a 100644 --- a/Engine/lib/openal-soft/CMakeLists.txt +++ b/Engine/lib/openal-soft/CMakeLists.txt @@ -1,15 +1,21 @@ # CMake build file list for OpenAL -CMAKE_MINIMUM_REQUIRED(VERSION 2.8.5) +CMAKE_MINIMUM_REQUIRED(VERSION 3.0.2) PROJECT(OpenAL) IF(COMMAND CMAKE_POLICY) CMAKE_POLICY(SET CMP0003 NEW) CMAKE_POLICY(SET CMP0005 NEW) + IF(POLICY CMP0020) + CMAKE_POLICY(SET CMP0020 NEW) + ENDIF(POLICY CMP0020) IF(POLICY CMP0042) CMAKE_POLICY(SET CMP0042 NEW) ENDIF(POLICY CMP0042) + IF(POLICY CMP0054) + CMAKE_POLICY(SET CMP0054 NEW) + ENDIF(POLICY CMP0054) ENDIF(COMMAND CMAKE_POLICY) SET(CMAKE_MODULE_PATH "${OpenAL_SOURCE_DIR}/cmake") @@ -21,13 +27,13 @@ INCLUDE(CheckIncludeFile) INCLUDE(CheckIncludeFiles) INCLUDE(CheckSymbolExists) INCLUDE(CheckCCompilerFlag) +INCLUDE(CheckCXXCompilerFlag) INCLUDE(CheckCSourceCompiles) INCLUDE(CheckTypeSize) include(CheckStructHasMember) include(CheckFileOffsetBits) include(GNUInstallDirs) - SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS TRUE) @@ -56,11 +62,16 @@ if(DEFINED LIB_SUFFIX) endif() -IF(NOT WIN32) - SET(LIBNAME openal) -ELSE() - SET(LIBNAME OpenAL32) - ADD_DEFINITIONS("-D_WIN32 -D_WIN32_WINNT=0x0502") +SET(CPP_DEFS ) # C pre-process, not C++ +SET(INC_PATHS ) +SET(C_FLAGS ) +SET(LINKER_FLAGS ) +SET(EXTRA_LIBS ) + +IF(WIN32) + SET(CPP_DEFS ${CPP_DEFS} _WIN32 _WIN32_WINNT=0x0502) + + OPTION(ALSOFT_BUILD_ROUTER "Build the router (EXPERIMENTAL; creates OpenAL32.dll and soft_oal.dll)" OFF) # This option is mainly for static linking OpenAL Soft into another project # that already defines the IDs. It is up to that project to ensure all @@ -82,8 +93,8 @@ ENDIF() # QNX's gcc do not uses /usr/include and /usr/lib pathes by default IF ("${CMAKE_C_PLATFORM_ID}" STREQUAL "QNX") - ADD_DEFINITIONS("-I/usr/include") - SET(EXTRA_LIBS ${EXTRA_LIBS} -L/usr/lib) + SET(INC_PATHS ${INC_PATHS} /usr/include) + SET(LINKER_FLAGS ${LINKER_FLAGS} -L/usr/lib) ENDIF() IF(NOT LIBTYPE) @@ -91,7 +102,7 @@ IF(NOT LIBTYPE) ENDIF() SET(LIB_MAJOR_VERSION "1") -SET(LIB_MINOR_VERSION "17") +SET(LIB_MINOR_VERSION "18") SET(LIB_REVISION "2") SET(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}") @@ -113,17 +124,22 @@ ELSE() ENDIF() ENDIF() +CHECK_CXX_COMPILER_FLAG(-std=c++11 HAVE_STD_CXX11) +IF(HAVE_STD_CXX11) + SET(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") +ENDIF() + if(NOT WIN32) # Check if _POSIX_C_SOURCE and _XOPEN_SOURCE needs to be set for POSIX functions CHECK_SYMBOL_EXISTS(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_DEFAULT) IF(NOT HAVE_POSIX_MEMALIGN_DEFAULT) SET(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) - SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=500") + SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600") CHECK_SYMBOL_EXISTS(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_POSIX) IF(NOT HAVE_POSIX_MEMALIGN_POSIX) SET(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS}) ELSE() - ADD_DEFINITIONS(-D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=500) + SET(CPP_DEFS ${CPP_DEFS} _POSIX_C_SOURCE=200112L _XOPEN_SOURCE=600) ENDIF() ENDIF() UNSET(OLD_REQUIRED_FLAGS) @@ -132,10 +148,10 @@ ENDIF() # Set defines for large file support CHECK_FILE_OFFSET_BITS() IF(_FILE_OFFSET_BITS) - ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=${_FILE_OFFSET_BITS}) + SET(CPP_DEFS ${CPP_DEFS} "_FILE_OFFSET_BITS=${_FILE_OFFSET_BITS}") SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_FILE_OFFSET_BITS=${_FILE_OFFSET_BITS}") ENDIF() -ADD_DEFINITIONS(-D_LARGEFILE_SOURCE -D_LARGE_FILES) +SET(CPP_DEFS ${CPP_DEFS} _LARGEFILE_SOURCE _LARGE_FILES) SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_LARGEFILE_SOURCE -D_LARGE_FILES") # MSVC may need workarounds for C99 restrict and inline @@ -145,7 +161,7 @@ IF(MSVC) CHECK_C_SOURCE_COMPILES("int *restrict foo; int main() {return 0;}" HAVE_RESTRICT) IF(NOT HAVE_RESTRICT) - ADD_DEFINITIONS("-Drestrict=") + SET(CPP_DEFS ${CPP_DEFS} "restrict=") SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Drestrict=") ENDIF() @@ -158,13 +174,15 @@ IF(MSVC) MESSAGE(FATAL_ERROR "No inline keyword found, please report!") ENDIF() - ADD_DEFINITIONS(-Dinline=__inline) + SET(CPP_DEFS ${CPP_DEFS} inline=__inline) SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Dinline=__inline") ENDIF() ENDIF() # Make sure we have C99-style inline semantics with GCC (4.3 or newer). IF(CMAKE_COMPILER_IS_GNUCC) + SET(CMAKE_C_FLAGS "-fno-gnu89-inline ${CMAKE_C_FLAGS}") + SET(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") # Force no inlining for the next test. SET(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -fno-inline") @@ -183,7 +201,7 @@ ENDIF() CHECK_STRUCT_HAS_MEMBER("struct timespec" tv_sec time.h HAVE_STRUCT_TIMESPEC) IF(HAVE_STRUCT_TIMESPEC) # Define it here so we don't have to include config.h for it - ADD_DEFINITIONS("-DHAVE_STRUCT_TIMESPEC") + SET(CPP_DEFS ${CPP_DEFS} HAVE_STRUCT_TIMESPEC) ENDIF() # Some systems may need libatomic for C11 atomic functions to work @@ -203,15 +221,12 @@ ELSE() ENDIF() UNSET(OLD_REQUIRED_LIBRARIES) -# Check if we have C99 variable length arrays -CHECK_C_SOURCE_COMPILES( -"int main(int argc, char *argv[]) - { - volatile int tmp[argc]; - tmp[0] = argv[0][0]; - return tmp[0]; - }" -HAVE_C99_VLA) +# Include liblog for Android logging +CHECK_LIBRARY_EXISTS(log __android_log_print "" HAVE_LIBLOG) +IF(HAVE_LIBLOG) + SET(EXTRA_LIBS log ${EXTRA_LIBS}) + SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} log) +ENDIF() # Check if we have C99 bool CHECK_C_SOURCE_COMPILES( @@ -244,23 +259,16 @@ HAVE_C11_ALIGNAS) # Check if we have C11 _Atomic CHECK_C_SOURCE_COMPILES( "#include - const int _Atomic foo = ATOMIC_VAR_INIT(~0); - int _Atomic bar = ATOMIC_VAR_INIT(0); + int _Atomic foo = ATOMIC_VAR_INIT(0); int main() { - atomic_fetch_add(&bar, 2); - return atomic_load(&foo); + atomic_fetch_add(&foo, 2); + return 0; }" HAVE_C11_ATOMIC) # Add definitions, compiler switches, etc. -INCLUDE_DIRECTORIES("${OpenAL_SOURCE_DIR}/include" "${OpenAL_BINARY_DIR}") -IF(CMAKE_VERSION VERSION_LESS "2.8.8") - INCLUDE_DIRECTORIES("${OpenAL_SOURCE_DIR}/OpenAL32/Include" "${OpenAL_SOURCE_DIR}/Alc") - IF(WIN32 AND ALSOFT_NO_UID_DEFS) - ADD_DEFINITIONS("-DAL_NO_UID_DEFS") - ENDIF() -ENDIF() +INCLUDE_DIRECTORIES("${OpenAL_SOURCE_DIR}/include" "${OpenAL_SOURCE_DIR}/common" "${OpenAL_BINARY_DIR}") IF(NOT CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING @@ -273,11 +281,9 @@ IF(NOT CMAKE_DEBUG_POSTFIX) FORCE) ENDIF() -SET(EXTRA_CFLAGS "") IF(MSVC) - ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS) - ADD_DEFINITIONS(-D_CRT_NONSTDC_NO_DEPRECATE) - SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} /wd4098") + SET(CPP_DEFS ${CPP_DEFS} _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE) + SET(C_FLAGS ${C_FLAGS} /wd4098) IF(NOT DXSDK_DIR) STRING(REGEX REPLACE "\\\\" "/" DXSDK_DIR "$ENV{DXSDK_DIR}") @@ -299,25 +305,14 @@ IF(MSVC) ENDFOREACH(flag_var) ENDIF() ELSE() - SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Winline -Wall") + SET(C_FLAGS ${C_FLAGS} -Winline -Wall) CHECK_C_COMPILER_FLAG(-Wextra HAVE_W_EXTRA) IF(HAVE_W_EXTRA) - SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Wextra") + SET(C_FLAGS ${C_FLAGS} -Wextra) ENDIF() IF(ALSOFT_WERROR) - SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Werror") - ENDIF() - - # Force enable -fPIC for CMake versions before 2.8.9 (later versions have - # the POSITION_INDEPENDENT_CODE target property). The static common library - # will be linked into the dynamic openal library, which requires all its - # code to be position-independent. - IF(CMAKE_VERSION VERSION_LESS "2.8.9" AND NOT WIN32) - CHECK_C_COMPILER_FLAG(-fPIC HAVE_FPIC_SWITCH) - IF(HAVE_FPIC_SWITCH) - SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fPIC") - ENDIF() + SET(C_FLAGS ${C_FLAGS} -Werror) ENDIF() # We want RelWithDebInfo to actually include debug stuff (define _DEBUG @@ -328,6 +323,11 @@ ELSE() ENDIF() ENDFOREACH() + CHECK_C_COMPILER_FLAG(-fno-math-errno HAVE_FNO_MATH_ERRNO) + IF(HAVE_FNO_MATH_ERRNO) + SET(C_FLAGS ${C_FLAGS} -fno-math-errno) + ENDIF() + CHECK_C_SOURCE_COMPILES("int foo() __attribute__((destructor)); int main() {return 0;}" HAVE_GCC_DESTRUCTOR) @@ -344,7 +344,7 @@ int main() HAVE_STATIC_LIBGCC_SWITCH ) if(HAVE_STATIC_LIBGCC_SWITCH) - set(EXTRA_LIBS ${EXTRA_LIBS} -static-libgcc) + SET(LINKER_FLAGS ${LINKER_FLAGS} -static-libgcc) endif() set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES}) unset(OLD_REQUIRED_LIBRARIES) @@ -352,7 +352,6 @@ int main() ENDIF() # Set visibility/export options if available -SET(HIDDEN_DECL "") IF(WIN32) SET(EXPORT_DECL "__declspec(dllexport)") IF(NOT MINGW) @@ -380,8 +379,7 @@ ELSE() IF(HAVE_GCC_PROTECTED_VISIBILITY OR HAVE_GCC_DEFAULT_VISIBILITY) CHECK_C_COMPILER_FLAG(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN_SWITCH) IF(HAVE_VISIBILITY_HIDDEN_SWITCH) - SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden") - SET(HIDDEN_DECL "__attribute__((visibility(\"hidden\")))") + SET(C_FLAGS ${C_FLAGS} -fvisibility=hidden) ENDIF() ENDIF() @@ -394,32 +392,44 @@ ELSE() SET(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}") ENDIF() +CHECK_C_SOURCE_COMPILES(" +int main() +{ + float *ptr; + ptr = __builtin_assume_aligned(ptr, 16); + return 0; +}" HAVE___BUILTIN_ASSUME_ALIGNED) +IF(HAVE___BUILTIN_ASSUME_ALIGNED) + SET(ASSUME_ALIGNED_DECL "__builtin_assume_aligned(x, y)") +ELSE() + SET(ASSUME_ALIGNED_DECL "x") +ENDIF() + SET(SSE_SWITCH "") SET(SSE2_SWITCH "") SET(SSE3_SWITCH "") SET(SSE4_1_SWITCH "") SET(FPU_NEON_SWITCH "") -IF(NOT MSVC) - CHECK_C_COMPILER_FLAG(-msse HAVE_MSSE_SWITCH) - IF(HAVE_MSSE_SWITCH) - SET(SSE_SWITCH "-msse") - ENDIF() - CHECK_C_COMPILER_FLAG(-msse2 HAVE_MSSE2_SWITCH) - IF(HAVE_MSSE2_SWITCH) - SET(SSE2_SWITCH "-msse2") - ENDIF() - CHECK_C_COMPILER_FLAG(-msse3 HAVE_MSSE3_SWITCH) - IF(HAVE_MSSE3_SWITCH) - SET(SSE3_SWITCH "-msse3") - ENDIF() - CHECK_C_COMPILER_FLAG(-msse4.1 HAVE_MSSE4_1_SWITCH) - IF(HAVE_MSSE4_1_SWITCH) - SET(SSE4_1_SWITCH "-msse4.1") - ENDIF() - CHECK_C_COMPILER_FLAG(-mfpu=neon HAVE_MFPU_NEON_SWITCH) - IF(HAVE_MFPU_NEON_SWITCH) - SET(FPU_NEON_SWITCH "-mfpu=neon") - ENDIF() + +CHECK_C_COMPILER_FLAG(-msse HAVE_MSSE_SWITCH) +IF(HAVE_MSSE_SWITCH) + SET(SSE_SWITCH "-msse") +ENDIF() +CHECK_C_COMPILER_FLAG(-msse2 HAVE_MSSE2_SWITCH) +IF(HAVE_MSSE2_SWITCH) + SET(SSE2_SWITCH "-msse2") +ENDIF() +CHECK_C_COMPILER_FLAG(-msse3 HAVE_MSSE3_SWITCH) +IF(HAVE_MSSE3_SWITCH) + SET(SSE3_SWITCH "-msse3") +ENDIF() +CHECK_C_COMPILER_FLAG(-msse4.1 HAVE_MSSE4_1_SWITCH) +IF(HAVE_MSSE4_1_SWITCH) + SET(SSE4_1_SWITCH "-msse4.1") +ENDIF() +CHECK_C_COMPILER_FLAG(-mfpu=neon HAVE_MFPU_NEON_SWITCH) +IF(HAVE_MFPU_NEON_SWITCH) + SET(FPU_NEON_SWITCH "-mfpu=neon") ENDIF() CHECK_C_SOURCE_COMPILES("int foo(const char *str, ...) __attribute__((format(printf, 1, 2))); @@ -442,9 +452,10 @@ IF(NOT HAVE_GUIDDEF_H) ENDIF() # Some systems need libm for some of the following math functions to work +SET(MATH_LIB ) CHECK_LIBRARY_EXISTS(m pow "" HAVE_LIBM) IF(HAVE_LIBM) - SET(EXTRA_LIBS m ${EXTRA_LIBS}) + SET(MATH_LIB ${MATH_LIB} m) SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} m) ENDIF() @@ -476,19 +487,31 @@ IF(HAVE_INTRIN_H) __cpuid(regs, 0); return regs[0]; }" HAVE_CPUID_INTRINSIC) + CHECK_C_SOURCE_COMPILES("#include + int main() + { + unsigned long idx = 0; + _BitScanForward64(&idx, 1); + return idx; + }" HAVE_BITSCANFORWARD64_INTRINSIC) + CHECK_C_SOURCE_COMPILES("#include + int main() + { + unsigned long idx = 0; + _BitScanForward(&idx, 1); + return idx; + }" HAVE_BITSCANFORWARD_INTRINSIC) ENDIF() +CHECK_SYMBOL_EXISTS(sysconf unistd.h HAVE_SYSCONF) CHECK_SYMBOL_EXISTS(aligned_alloc stdlib.h HAVE_ALIGNED_ALLOC) CHECK_SYMBOL_EXISTS(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN) CHECK_SYMBOL_EXISTS(_aligned_malloc malloc.h HAVE__ALIGNED_MALLOC) +CHECK_SYMBOL_EXISTS(proc_pidpath libproc.h HAVE_PROC_PIDPATH) CHECK_SYMBOL_EXISTS(lrintf math.h HAVE_LRINTF) CHECK_SYMBOL_EXISTS(modff math.h HAVE_MODFF) -IF(NOT HAVE_C99_VLA) - CHECK_SYMBOL_EXISTS(alloca malloc.h HAVE_ALLOCA) - IF(NOT HAVE_ALLOCA) - MESSAGE(FATAL_ERROR "No alloca function found, please report!") - ENDIF() -ENDIF() +CHECK_SYMBOL_EXISTS(log2f math.h HAVE_LOG2F) +CHECK_SYMBOL_EXISTS(cbrtf math.h HAVE_CBRTF) IF(HAVE_FLOAT_H) CHECK_SYMBOL_EXISTS(_controlfp float.h HAVE__CONTROLFP) @@ -504,7 +527,7 @@ IF(NOT HAVE_STRCASECMP) MESSAGE(FATAL_ERROR "No case-insensitive compare function found, please report!") ENDIF() - ADD_DEFINITIONS(-Dstrcasecmp=_stricmp) + SET(CPP_DEFS ${CPP_DEFS} strcasecmp=_stricmp) ENDIF() CHECK_FUNCTION_EXISTS(strncasecmp HAVE_STRNCASECMP) @@ -514,7 +537,7 @@ IF(NOT HAVE_STRNCASECMP) MESSAGE(FATAL_ERROR "No case-insensitive size-limitted compare function found, please report!") ENDIF() - ADD_DEFINITIONS(-Dstrncasecmp=_strnicmp) + SET(CPP_DEFS ${CPP_DEFS} strncasecmp=_strnicmp) ENDIF() CHECK_SYMBOL_EXISTS(strnlen string.h HAVE_STRNLEN) @@ -525,7 +548,7 @@ IF(NOT HAVE_SNPRINTF) MESSAGE(FATAL_ERROR "No snprintf function found, please report!") ENDIF() - ADD_DEFINITIONS(-Dsnprintf=_snprintf) + SET(CPP_DEFS ${CPP_DEFS} snprintf=_snprintf) ENDIF() CHECK_SYMBOL_EXISTS(isfinite math.h HAVE_ISFINITE) @@ -536,9 +559,9 @@ IF(NOT HAVE_ISFINITE) IF(NOT HAVE__FINITE) MESSAGE(FATAL_ERROR "No isfinite function found, please report!") ENDIF() - ADD_DEFINITIONS(-Disfinite=_finite) + SET(CPP_DEFS ${CPP_DEFS} isfinite=_finite) ELSE() - ADD_DEFINITIONS(-Disfinite=finite) + SET(CPP_DEFS ${CPP_DEFS} isfinite=finite) ENDIF() ENDIF() @@ -549,12 +572,17 @@ IF(NOT HAVE_ISNAN) MESSAGE(FATAL_ERROR "No isnan function found, please report!") ENDIF() - ADD_DEFINITIONS(-Disnan=_isnan) + SET(CPP_DEFS ${CPP_DEFS} isnan=_isnan) ENDIF() # Check if we have Windows headers -CHECK_INCLUDE_FILE(windows.h HAVE_WINDOWS_H -D_WIN32_WINNT=0x0502) +SET(OLD_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}) +SET(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -D_WIN32_WINNT=0x0502) +CHECK_INCLUDE_FILE(windows.h HAVE_WINDOWS_H) +SET(CMAKE_REQUIRED_DEFINITIONS ${OLD_REQUIRED_DEFINITIONS}) +UNSET(OLD_REQUIRED_DEFINITIONS) + IF(NOT HAVE_WINDOWS_H) CHECK_SYMBOL_EXISTS(gettimeofday sys/time.h HAVE_GETTIMEOFDAY) IF(NOT HAVE_GETTIMEOFDAY) @@ -576,9 +604,9 @@ IF(NOT HAVE_WINDOWS_H) CHECK_C_COMPILER_FLAG(-pthread HAVE_PTHREAD) IF(HAVE_PTHREAD) - SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -pthread") SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -pthread") - SET(EXTRA_LIBS ${EXTRA_LIBS} -pthread) + SET(C_FLAGS ${C_FLAGS} -pthread) + SET(LINKER_FLAGS ${LINKER_FLAGS} -pthread) ENDIF() CHECK_LIBRARY_EXISTS(pthread pthread_create "" HAVE_LIBPTHREAD) @@ -603,6 +631,16 @@ int main() }" PTHREAD_SETNAME_NP_ONE_PARAM ) + CHECK_C_SOURCE_COMPILES(" +#include +#include +int main() +{ + pthread_setname_np(pthread_self(), \"%s\", \"testname\"); + return 0; +}" + PTHREAD_SETNAME_NP_THREE_PARAMS + ) ENDIF() CHECK_SYMBOL_EXISTS(pthread_mutexattr_setkind_np "pthread.h;pthread_np.h" HAVE_PTHREAD_MUTEXATTR_SETKIND_NP) ELSE() @@ -619,6 +657,15 @@ int main() }" PTHREAD_SETNAME_NP_ONE_PARAM ) + CHECK_C_SOURCE_COMPILES(" +#include +int main() +{ + pthread_setname_np(pthread_self(), \"%s\", \"testname\"); + return 0; +}" + PTHREAD_SETNAME_NP_THREE_PARAMS + ) ENDIF() CHECK_SYMBOL_EXISTS(pthread_mutexattr_setkind_np pthread.h HAVE_PTHREAD_MUTEXATTR_SETKIND_NP) ENDIF() @@ -631,6 +678,8 @@ int main() ENDIF() ENDIF() +CHECK_SYMBOL_EXISTS(getopt unistd.h HAVE_GETOPT) + # Check for a 64-bit type CHECK_INCLUDE_FILE(stdint.h HAVE_STDINT_H) IF(NOT HAVE_STDINT_H) @@ -650,48 +699,96 @@ IF(NOT HAVE_STDINT_H) ENDIF() -SET(COMMON_OBJS common/almalloc.c - common/atomic.c - common/rwlock.c - common/threads.c - common/uintmap.c +SET(COMMON_OBJS + common/align.h + common/almalloc.c + common/almalloc.h + common/atomic.c + common/atomic.h + common/bool.h + common/math_defs.h + common/rwlock.c + common/rwlock.h + common/static_assert.h + common/threads.c + common/threads.h + common/uintmap.c + common/uintmap.h ) -SET(OPENAL_OBJS OpenAL32/alAuxEffectSlot.c - OpenAL32/alBuffer.c - OpenAL32/alEffect.c - OpenAL32/alError.c - OpenAL32/alExtension.c - OpenAL32/alFilter.c - OpenAL32/alListener.c - OpenAL32/alSource.c - OpenAL32/alState.c - OpenAL32/alThunk.c - OpenAL32/sample_cvt.c +SET(OPENAL_OBJS + OpenAL32/Include/bs2b.h + OpenAL32/Include/alMain.h + OpenAL32/Include/alu.h + + OpenAL32/Include/alAuxEffectSlot.h + OpenAL32/alAuxEffectSlot.c + OpenAL32/Include/alBuffer.h + OpenAL32/alBuffer.c + OpenAL32/Include/alEffect.h + OpenAL32/alEffect.c + OpenAL32/Include/alError.h + OpenAL32/alError.c + OpenAL32/alExtension.c + OpenAL32/Include/alFilter.h + OpenAL32/alFilter.c + OpenAL32/Include/alListener.h + OpenAL32/alListener.c + OpenAL32/Include/alSource.h + OpenAL32/alSource.c + OpenAL32/alState.c + OpenAL32/event.c + OpenAL32/Include/sample_cvt.h + OpenAL32/sample_cvt.c ) -SET(ALC_OBJS Alc/ALc.c - Alc/ALu.c - Alc/alcConfig.c - Alc/alcRing.c - Alc/bs2b.c - Alc/effects/chorus.c - Alc/effects/compressor.c - Alc/effects/dedicated.c - Alc/effects/distortion.c - Alc/effects/echo.c - Alc/effects/equalizer.c - Alc/effects/flanger.c - Alc/effects/modulator.c - Alc/effects/null.c - Alc/effects/reverb.c - Alc/helpers.c - Alc/bsinc.c - Alc/hrtf.c - Alc/uhjfilter.c - Alc/ambdec.c - Alc/bformatdec.c - Alc/panning.c - Alc/mixer.c - Alc/mixer_c.c +SET(ALC_OBJS + Alc/ALc.c + Alc/ALu.c + Alc/alconfig.c + Alc/alconfig.h + Alc/bs2b.c + Alc/converter.c + Alc/converter.h + Alc/inprogext.h + Alc/mastering.c + Alc/mastering.h + Alc/ringbuffer.c + Alc/ringbuffer.h + Alc/effects/chorus.c + Alc/effects/compressor.c + Alc/effects/dedicated.c + Alc/effects/distortion.c + Alc/effects/echo.c + Alc/effects/equalizer.c + Alc/effects/modulator.c + Alc/effects/null.c + Alc/effects/pshifter.c + Alc/effects/reverb.c + Alc/filters/defs.h + Alc/filters/filter.c + Alc/filters/nfc.c + Alc/filters/nfc.h + Alc/filters/splitter.c + Alc/filters/splitter.h + Alc/helpers.c + Alc/alstring.h + Alc/compat.h + Alc/cpu_caps.h + Alc/fpu_modes.h + Alc/logging.h + Alc/vector.h + Alc/hrtf.c + Alc/hrtf.h + Alc/uhjfilter.c + Alc/uhjfilter.h + Alc/ambdec.c + Alc/ambdec.h + Alc/bformatdec.c + Alc/bformatdec.h + Alc/panning.c + Alc/polymorphism.h + Alc/mixvoice.c + Alc/mixer/defs.h + Alc/mixer/mixer_c.c ) @@ -708,13 +805,14 @@ SET(HAVE_SOLARIS 0) SET(HAVE_SNDIO 0) SET(HAVE_QSA 0) SET(HAVE_DSOUND 0) -SET(HAVE_MMDEVAPI 0) +SET(HAVE_WASAPI 0) SET(HAVE_WINMM 0) SET(HAVE_PORTAUDIO 0) SET(HAVE_PULSEAUDIO 0) SET(HAVE_COREAUDIO 0) SET(HAVE_OPENSL 0) SET(HAVE_WAVE 0) +SET(HAVE_SDL2 0) # Check for SSE support OPTION(ALSOFT_REQUIRE_SSE "Require SSE support" OFF) @@ -724,9 +822,9 @@ IF(HAVE_XMMINTRIN_H) IF(ALSOFT_CPUEXT_SSE) IF(ALIGN_DECL OR HAVE_C11_ALIGNAS) SET(HAVE_SSE 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/mixer_sse.c) + SET(ALC_OBJS ${ALC_OBJS} Alc/mixer/mixer_sse.c) IF(SSE_SWITCH) - SET_SOURCE_FILES_PROPERTIES(Alc/mixer_sse.c PROPERTIES + SET_SOURCE_FILES_PROPERTIES(Alc/mixer/mixer_sse.c PROPERTIES COMPILE_FLAGS "${SSE_SWITCH}") ENDIF() SET(CPU_EXTS "${CPU_EXTS}, SSE") @@ -744,9 +842,9 @@ IF(HAVE_EMMINTRIN_H) IF(HAVE_SSE AND ALSOFT_CPUEXT_SSE2) IF(ALIGN_DECL OR HAVE_C11_ALIGNAS) SET(HAVE_SSE2 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/mixer_sse2.c) + SET(ALC_OBJS ${ALC_OBJS} Alc/mixer/mixer_sse2.c) IF(SSE2_SWITCH) - SET_SOURCE_FILES_PROPERTIES(Alc/mixer_sse2.c PROPERTIES + SET_SOURCE_FILES_PROPERTIES(Alc/mixer/mixer_sse2.c PROPERTIES COMPILE_FLAGS "${SSE2_SWITCH}") ENDIF() SET(CPU_EXTS "${CPU_EXTS}, SSE2") @@ -764,9 +862,9 @@ IF(HAVE_EMMINTRIN_H) IF(HAVE_SSE2 AND ALSOFT_CPUEXT_SSE3) IF(ALIGN_DECL OR HAVE_C11_ALIGNAS) SET(HAVE_SSE3 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/mixer_sse3.c) + SET(ALC_OBJS ${ALC_OBJS} Alc/mixer/mixer_sse3.c) IF(SSE2_SWITCH) - SET_SOURCE_FILES_PROPERTIES(Alc/mixer_sse3.c PROPERTIES + SET_SOURCE_FILES_PROPERTIES(Alc/mixer/mixer_sse3.c PROPERTIES COMPILE_FLAGS "${SSE3_SWITCH}") ENDIF() SET(CPU_EXTS "${CPU_EXTS}, SSE3") @@ -784,9 +882,9 @@ IF(HAVE_SMMINTRIN_H) IF(HAVE_SSE2 AND ALSOFT_CPUEXT_SSE4_1) IF(ALIGN_DECL OR HAVE_C11_ALIGNAS) SET(HAVE_SSE4_1 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/mixer_sse41.c) + SET(ALC_OBJS ${ALC_OBJS} Alc/mixer/mixer_sse41.c) IF(SSE4_1_SWITCH) - SET_SOURCE_FILES_PROPERTIES(Alc/mixer_sse41.c PROPERTIES + SET_SOURCE_FILES_PROPERTIES(Alc/mixer/mixer_sse41.c PROPERTIES COMPILE_FLAGS "${SSE4_1_SWITCH}") ENDIF() SET(CPU_EXTS "${CPU_EXTS}, SSE4.1") @@ -799,14 +897,14 @@ ENDIF() # Check for ARM Neon support OPTION(ALSOFT_REQUIRE_NEON "Require ARM Neon support" OFF) -CHECK_INCLUDE_FILE(arm_neon.h HAVE_ARM_NEON_H) +CHECK_INCLUDE_FILE(arm_neon.h HAVE_ARM_NEON_H ${FPU_NEON_SWITCH}) IF(HAVE_ARM_NEON_H) OPTION(ALSOFT_CPUEXT_NEON "Enable ARM Neon support" ON) IF(ALSOFT_CPUEXT_NEON) SET(HAVE_NEON 1) - SET(ALC_OBJS ${ALC_OBJS} Alc/mixer_neon.c) + SET(ALC_OBJS ${ALC_OBJS} Alc/mixer/mixer_neon.c) IF(FPU_NEON_SWITCH) - SET_SOURCE_FILES_PROPERTIES(Alc/mixer_neon.c PROPERTIES + SET_SOURCE_FILES_PROPERTIES(Alc/mixer/mixer_neon.c PROPERTIES COMPILE_FLAGS "${FPU_NEON_SWITCH}") ENDIF() SET(CPU_EXTS "${CPU_EXTS}, Neon") @@ -830,10 +928,11 @@ ENDIF() SET(BACKENDS "") SET(ALC_OBJS ${ALC_OBJS} - Alc/backends/base.c - # Default backends, always available - Alc/backends/loopback.c - Alc/backends/null.c + Alc/backends/base.c + Alc/backends/base.h + # Default backends, always available + Alc/backends/loopback.c + Alc/backends/null.c ) # Check ALSA backend @@ -846,9 +945,7 @@ IF(ALSA_FOUND) SET(BACKENDS "${BACKENDS} ALSA${IS_LINKED},") SET(ALC_OBJS ${ALC_OBJS} Alc/backends/alsa.c) ADD_BACKEND_LIBS(${ALSA_LIBRARIES}) - IF(CMAKE_VERSION VERSION_LESS "2.8.8") - INCLUDE_DIRECTORIES(${ALSA_INCLUDE_DIRS}) - ENDIF() + SET(INC_PATHS ${INC_PATHS} ${ALSA_INCLUDE_DIRS}) ENDIF() ENDIF() IF(ALSOFT_REQUIRE_ALSA AND NOT HAVE_ALSA) @@ -864,9 +961,10 @@ IF(OSS_FOUND) SET(HAVE_OSS 1) SET(BACKENDS "${BACKENDS} OSS,") SET(ALC_OBJS ${ALC_OBJS} Alc/backends/oss.c) - IF(CMAKE_VERSION VERSION_LESS "2.8.8") - INCLUDE_DIRECTORIES(${OSS_INCLUDE_DIRS}) + IF(OSS_LIBRARIES) + SET(EXTRA_LIBS ${OSS_LIBRARIES} ${EXTRA_LIBS}) ENDIF() + SET(INC_PATHS ${INC_PATHS} ${OSS_INCLUDE_DIRS}) ENDIF() ENDIF() IF(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS) @@ -882,9 +980,7 @@ IF(AUDIOIO_FOUND) SET(HAVE_SOLARIS 1) SET(BACKENDS "${BACKENDS} Solaris,") SET(ALC_OBJS ${ALC_OBJS} Alc/backends/solaris.c) - IF(CMAKE_VERSION VERSION_LESS "2.8.8") - INCLUDE_DIRECTORIES(${AUDIOIO_INCLUDE_DIRS}) - ENDIF() + SET(INC_PATHS ${INC_PATHS} ${AUDIOIO_INCLUDE_DIRS}) ENDIF() ENDIF() IF(ALSOFT_REQUIRE_SOLARIS AND NOT HAVE_SOLARIS) @@ -901,9 +997,7 @@ IF(SOUNDIO_FOUND) SET(BACKENDS "${BACKENDS} SndIO (linked),") SET(ALC_OBJS ${ALC_OBJS} Alc/backends/sndio.c) SET(EXTRA_LIBS ${SOUNDIO_LIBRARIES} ${EXTRA_LIBS}) - IF(CMAKE_VERSION VERSION_LESS "2.8.8") - INCLUDE_DIRECTORIES(${SOUNDIO_INCLUDE_DIRS}) - ENDIF() + SET(INC_PATHS ${INC_PATHS} ${SOUNDIO_INCLUDE_DIRS}) ENDIF() ENDIF() IF(ALSOFT_REQUIRE_SNDIO AND NOT HAVE_SNDIO) @@ -920,9 +1014,7 @@ IF(QSA_FOUND) SET(BACKENDS "${BACKENDS} QSA (linked),") SET(ALC_OBJS ${ALC_OBJS} Alc/backends/qsa.c) SET(EXTRA_LIBS ${QSA_LIBRARIES} ${EXTRA_LIBS}) - IF(CMAKE_VERSION VERSION_LESS "2.8.8") - INCLUDE_DIRECTORIES(${QSA_INCLUDE_DIRS}) - ENDIF() + SET(INC_PATHS ${INC_PATHS} ${QSA_INCLUDE_DIRS}) ENDIF() ENDIF() IF(ALSOFT_REQUIRE_QSA AND NOT HAVE_QSA) @@ -932,10 +1024,13 @@ ENDIF() # Check Windows-only backends OPTION(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF) OPTION(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF) -OPTION(ALSOFT_REQUIRE_MMDEVAPI "Require MMDevApi backend" OFF) +OPTION(ALSOFT_REQUIRE_WASAPI "Require WASAPI backend" OFF) IF(HAVE_WINDOWS_H) + SET(OLD_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}) + SET(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -D_WIN32_WINNT=0x0502) + # Check MMSystem backend - CHECK_INCLUDE_FILES("windows.h;mmsystem.h" HAVE_MMSYSTEM_H -D_WIN32_WINNT=0x0502) + CHECK_INCLUDE_FILES("windows.h;mmsystem.h" HAVE_MMSYSTEM_H) IF(HAVE_MMSYSTEM_H) CHECK_SHARED_FUNCTION_EXISTS(waveOutOpen "windows.h;mmsystem.h" winmm "" HAVE_LIBWINMM) IF(HAVE_LIBWINMM) @@ -958,22 +1053,23 @@ IF(HAVE_WINDOWS_H) SET(BACKENDS "${BACKENDS} DirectSound${IS_LINKED},") SET(ALC_OBJS ${ALC_OBJS} Alc/backends/dsound.c) ADD_BACKEND_LIBS(${DSOUND_LIBRARIES}) - IF(CMAKE_VERSION VERSION_LESS "2.8.8") - INCLUDE_DIRECTORIES(${DSOUND_INCLUDE_DIRS}) - ENDIF() + SET(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIRS}) ENDIF() ENDIF() - # Check for MMDevApi backend + # Check for WASAPI backend CHECK_INCLUDE_FILE(mmdeviceapi.h HAVE_MMDEVICEAPI_H) IF(HAVE_MMDEVICEAPI_H) - OPTION(ALSOFT_BACKEND_MMDEVAPI "Enable MMDevApi backend" ON) - IF(ALSOFT_BACKEND_MMDEVAPI) - SET(HAVE_MMDEVAPI 1) - SET(BACKENDS "${BACKENDS} MMDevApi,") - SET(ALC_OBJS ${ALC_OBJS} Alc/backends/mmdevapi.c) + OPTION(ALSOFT_BACKEND_WASAPI "Enable WASAPI backend" ON) + IF(ALSOFT_BACKEND_WASAPI) + SET(HAVE_WASAPI 1) + SET(BACKENDS "${BACKENDS} WASAPI,") + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/wasapi.c) ENDIF() ENDIF() + + SET(CMAKE_REQUIRED_DEFINITIONS ${OLD_REQUIRED_DEFINITIONS}) + UNSET(OLD_REQUIRED_DEFINITIONS) ENDIF() IF(ALSOFT_REQUIRE_WINMM AND NOT HAVE_WINMM) MESSAGE(FATAL_ERROR "Failed to enabled required WinMM backend") @@ -981,8 +1077,8 @@ ENDIF() IF(ALSOFT_REQUIRE_DSOUND AND NOT HAVE_DSOUND) MESSAGE(FATAL_ERROR "Failed to enabled required DSound backend") ENDIF() -IF(ALSOFT_REQUIRE_MMDEVAPI AND NOT HAVE_MMDEVAPI) - MESSAGE(FATAL_ERROR "Failed to enabled required MMDevApi backend") +IF(ALSOFT_REQUIRE_WASAPI AND NOT HAVE_WASAPI) + MESSAGE(FATAL_ERROR "Failed to enabled required WASAPI backend") ENDIF() # Check PortAudio backend @@ -995,9 +1091,7 @@ IF(PORTAUDIO_FOUND) SET(BACKENDS "${BACKENDS} PortAudio${IS_LINKED},") SET(ALC_OBJS ${ALC_OBJS} Alc/backends/portaudio.c) ADD_BACKEND_LIBS(${PORTAUDIO_LIBRARIES}) - IF(CMAKE_VERSION VERSION_LESS "2.8.8") - INCLUDE_DIRECTORIES(${PORTAUDIO_INCLUDE_DIRS}) - ENDIF() + SET(INC_PATHS ${INC_PATHS} ${PORTAUDIO_INCLUDE_DIRS}) ENDIF() ENDIF() IF(ALSOFT_REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO) @@ -1014,9 +1108,7 @@ IF(PULSEAUDIO_FOUND) SET(BACKENDS "${BACKENDS} PulseAudio${IS_LINKED},") SET(ALC_OBJS ${ALC_OBJS} Alc/backends/pulseaudio.c) ADD_BACKEND_LIBS(${PULSEAUDIO_LIBRARIES}) - IF(CMAKE_VERSION VERSION_LESS "2.8.8") - INCLUDE_DIRECTORIES(${PULSEAUDIO_INCLUDE_DIRS}) - ENDIF() + SET(INC_PATHS ${INC_PATHS} ${PULSEAUDIO_INCLUDE_DIRS}) ENDIF() ENDIF() IF(ALSOFT_REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO) @@ -1033,9 +1125,7 @@ IF(JACK_FOUND) SET(BACKENDS "${BACKENDS} JACK${IS_LINKED},") SET(ALC_OBJS ${ALC_OBJS} Alc/backends/jack.c) ADD_BACKEND_LIBS(${JACK_LIBRARIES}) - IF(CMAKE_VERSION VERSION_LESS "2.8.8") - INCLUDE_DIRECTORIES(${JACK_INCLUDE_DIRS}) - ENDIF() + SET(INC_PATHS ${INC_PATHS} ${JACK_INCLUDE_DIRS}) ENDIF() ENDIF() IF(ALSOFT_REQUIRE_JACK AND NOT HAVE_JACK) @@ -1094,6 +1184,24 @@ IF(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL) MESSAGE(FATAL_ERROR "Failed to enabled required OpenSL backend") ENDIF() +# Check for SDL2 backend +OPTION(ALSOFT_REQUIRE_SDL2 "Require SDL2 backend" OFF) +FIND_PACKAGE(SDL2) +IF(SDL2_FOUND) + # Off by default, since it adds a runtime dependency + OPTION(ALSOFT_BACKEND_SDL2 "Enable SDL2 backend" OFF) + IF(ALSOFT_BACKEND_SDL2) + SET(HAVE_SDL2 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/sdl2.c) + SET(BACKENDS "${BACKENDS} SDL2,") + SET(EXTRA_LIBS ${SDL2_LIBRARY} ${EXTRA_LIBS}) + SET(INC_PATHS ${INC_PATHS} ${SDL2_INCLUDE_DIR}) + ENDIF() +ENDIF() +IF(ALSOFT_REQUIRE_SDL2 AND NOT SDL2_FOUND) + MESSAGE(FATAL_ERROR "Failed to enabled required SDL2 backend") +ENDIF() + # Optionally enable the Wave Writer backend OPTION(ALSOFT_BACKEND_WAVE "Enable Wave Writer backend" ON) IF(ALSOFT_BACKEND_WAVE) @@ -1105,50 +1213,91 @@ ENDIF() # This is always available SET(BACKENDS "${BACKENDS} Null") + +FIND_PACKAGE(Git) +IF(GIT_FOUND AND EXISTS "${OpenAL_SOURCE_DIR}/.git") + # Get the current working branch and its latest abbreviated commit hash + ADD_CUSTOM_TARGET(build_version + ${CMAKE_COMMAND} -D GIT_EXECUTABLE=${GIT_EXECUTABLE} + -D LIB_VERSION=${LIB_VERSION} + -D SRC=${OpenAL_SOURCE_DIR}/version.h.in + -D DST=${OpenAL_BINARY_DIR}/version.h + -P ${OpenAL_SOURCE_DIR}/version.cmake + WORKING_DIRECTORY "${OpenAL_SOURCE_DIR}" + VERBATIM + ) +ELSE() + SET(GIT_BRANCH "UNKNOWN") + SET(GIT_COMMIT_HASH "unknown") + CONFIGURE_FILE( + "${OpenAL_SOURCE_DIR}/version.h.in" + "${OpenAL_BINARY_DIR}/version.h") +ENDIF() + +SET(NATIVE_SRC_DIR "${OpenAL_SOURCE_DIR}/native-tools/") +SET(NATIVE_BIN_DIR "${OpenAL_BINARY_DIR}/native-tools/") +FILE(MAKE_DIRECTORY "${NATIVE_BIN_DIR}") + +SET(BIN2H_COMMAND "${NATIVE_BIN_DIR}bin2h") +SET(BSINCGEN_COMMAND "${NATIVE_BIN_DIR}bsincgen") +ADD_CUSTOM_COMMAND(OUTPUT "${BIN2H_COMMAND}" "${BSINCGEN_COMMAND}" + COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" "${NATIVE_SRC_DIR}" + COMMAND ${CMAKE_COMMAND} -E remove "${BIN2H_COMMAND}" "${BSINCGEN_COMMAND}" + COMMAND ${CMAKE_COMMAND} --build . --config "Release" + WORKING_DIRECTORY "${NATIVE_BIN_DIR}" + DEPENDS "${NATIVE_SRC_DIR}CMakeLists.txt" + IMPLICIT_DEPENDS C "${NATIVE_SRC_DIR}bin2h.c" + C "${NATIVE_SRC_DIR}bsincgen.c" + VERBATIM +) +ADD_CUSTOM_TARGET(native-tools + DEPENDS "${BIN2H_COMMAND}" "${BSINCGEN_COMMAND}" + VERBATIM +) + option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library footprint)" OFF) if(ALSOFT_EMBED_HRTF_DATA) - if(WIN32) - set(ALC_OBJS ${ALC_OBJS} Alc/hrtf_res.rc) - else() - set(FILENAMES default-44100.mhr default-48000.mhr) - foreach(FILENAME ${FILENAMES}) - set(outfile ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${FILENAME}${CMAKE_C_OUTPUT_EXTENSION}) - add_custom_command(OUTPUT ${outfile} - DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/hrtf/${FILENAME}" - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/hrtf" - COMMAND "${CMAKE_LINKER}" -r -b binary -o "${outfile}" ${FILENAME} - COMMAND "${CMAKE_OBJCOPY}" --rename-section .data=.rodata,alloc,load,readonly,data,contents "${outfile}" "${outfile}" - COMMENT "Generating ${FILENAME}${CMAKE_C_OUTPUT_EXTENSION}" - VERBATIM - ) - set(ALC_OBJS ${ALC_OBJS} ${outfile}) - endforeach() - unset(outfile) - unset(FILENAMES) - endif() + MACRO(make_hrtf_header FILENAME VARNAME) + SET(infile "${OpenAL_SOURCE_DIR}/hrtf/${FILENAME}") + SET(outfile "${OpenAL_BINARY_DIR}/${FILENAME}.h") + + ADD_CUSTOM_COMMAND(OUTPUT "${outfile}" + COMMAND "${BIN2H_COMMAND}" "${infile}" "${outfile}" ${VARNAME} + DEPENDS native-tools "${infile}" + VERBATIM + ) + SET(ALC_OBJS ${ALC_OBJS} "${outfile}") + ENDMACRO() + + make_hrtf_header(default-44100.mhr "hrtf_default_44100") + make_hrtf_header(default-48000.mhr "hrtf_default_48000") endif() +ADD_CUSTOM_COMMAND(OUTPUT "${OpenAL_BINARY_DIR}/bsinc_inc.h" + COMMAND "${BSINCGEN_COMMAND}" "${OpenAL_BINARY_DIR}/bsinc_inc.h" + DEPENDS native-tools "${NATIVE_SRC_DIR}bsincgen.c" + VERBATIM +) +SET(ALC_OBJS ${ALC_OBJS} "${OpenAL_BINARY_DIR}/bsinc_inc.h") + IF(ALSOFT_UTILS AND NOT ALSOFT_NO_CONFIG_UTIL) add_subdirectory(utils/alsoft-config) ENDIF() IF(ALSOFT_EXAMPLES) - FIND_PACKAGE(SDL2) + IF(NOT SDL2_FOUND) + FIND_PACKAGE(SDL2) + ENDIF() IF(SDL2_FOUND) FIND_PACKAGE(SDL_sound) - IF(SDL_SOUND_FOUND AND CMAKE_VERSION VERSION_LESS "2.8.8") - INCLUDE_DIRECTORIES(${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) - ENDIF() FIND_PACKAGE(FFmpeg COMPONENTS AVFORMAT AVCODEC AVUTIL SWSCALE SWRESAMPLE) - IF(FFMPEG_FOUND AND CMAKE_VERSION VERSION_LESS "2.8.8") - INCLUDE_DIRECTORIES(${FFMPEG_INCLUDE_DIRS}) - ENDIF() ENDIF() ENDIF() -IF(LIBTYPE STREQUAL "STATIC") - ADD_DEFINITIONS(-DAL_LIBTYPE_STATIC) - SET(PKG_CONFIG_CFLAGS -DAL_LIBTYPE_STATIC ${PKG_CONFIG_CFLAGS}) +IF(NOT WIN32) + SET(LIBNAME "openal") +ELSE() + SET(LIBNAME "OpenAL32") ENDIF() # Needed for openal.pc.in @@ -1158,6 +1307,20 @@ SET(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") SET(bindir "\${exec_prefix}/${CMAKE_INSTALL_BINDIR}") SET(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") SET(PACKAGE_VERSION "${LIB_VERSION}") +SET(PKG_CONFIG_CFLAGS ) +SET(PKG_CONFIG_PRIVATE_LIBS ) +IF(LIBTYPE STREQUAL "STATIC") + SET(PKG_CONFIG_CFLAGS -DAL_LIBTYPE_STATIC) + FOREACH(FLAG ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) + # If this is already a linker flag, or is a full path+file, add it + # as-is. Otherwise, it's a name intended to be dressed as -lname. + IF(FLAG MATCHES "^-.*" OR EXISTS "${FLAG}") + SET(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} ${FLAG}") + ELSE() + SET(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} -l${FLAG}") + ENDIF() + ENDFOREACH() +ENDIF() # End configuration CONFIGURE_FILE( @@ -1168,89 +1331,113 @@ CONFIGURE_FILE( "${OpenAL_BINARY_DIR}/openal.pc" @ONLY) -# Build a common library with reusable helpers + +# Add a static library with common functions used by multiple targets ADD_LIBRARY(common STATIC ${COMMON_OBJS}) -SET_PROPERTY(TARGET common APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) -IF(NOT LIBTYPE STREQUAL "STATIC") - SET_PROPERTY(TARGET common PROPERTY POSITION_INDEPENDENT_CODE TRUE) -ENDIF() +TARGET_COMPILE_DEFINITIONS(common PRIVATE ${CPP_DEFS}) +TARGET_COMPILE_OPTIONS(common PRIVATE ${C_FLAGS}) + + +UNSET(HAS_ROUTER) +SET(IMPL_TARGET OpenAL) +SET(COMMON_LIB ) +SET(SUBSYS_FLAG ) # Build main library IF(LIBTYPE STREQUAL "STATIC") - ADD_LIBRARY(${LIBNAME} STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS}) + SET(CPP_DEFS ${CPP_DEFS} AL_LIBTYPE_STATIC) + IF(WIN32 AND ALSOFT_NO_UID_DEFS) + SET(CPP_DEFS ${CPP_DEFS} AL_NO_UID_DEFS) + ENDIF() + ADD_LIBRARY(OpenAL STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS}) ELSE() - ADD_LIBRARY(${LIBNAME} SHARED ${OPENAL_OBJS} ${ALC_OBJS}) -ENDIF() -SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) -SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY COMPILE_DEFINITIONS AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES) -IF(WIN32 AND ALSOFT_NO_UID_DEFS) - SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY COMPILE_DEFINITIONS AL_NO_UID_DEFS) -ENDIF() -SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES "${OpenAL_SOURCE_DIR}/OpenAL32/Include" "${OpenAL_SOURCE_DIR}/Alc") -IF(HAVE_ALSA) - SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${ALSA_INCLUDE_DIRS}) -ENDIF() -IF(HAVE_OSS) - SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${OSS_INCLUDE_DIRS}) -ENDIF() -IF(HAVE_SOLARIS) - SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${AUDIOIO_INCLUDE_DIRS}) -ENDIF() -IF(HAVE_SNDIO) - SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${SOUNDIO_INCLUDE_DIRS}) -ENDIF() -IF(HAVE_QSA) - SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${QSA_INCLUDE_DIRS}) -ENDIF() -IF(HAVE_DSOUND) - SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${DSOUND_INCLUDE_DIRS}) -ENDIF() -IF(HAVE_PORTAUDIO) - SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${PORTAUDIO_INCLUDE_DIRS}) -ENDIF() -IF(HAVE_PULSEAUDIO) - SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${PULSEAUDIO_INCLUDE_DIRS}) -ENDIF() -IF(HAVE_JACK) - SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${JACK_INCLUDE_DIRS}) -ENDIF() -SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES VERSION ${LIB_VERSION} - SOVERSION ${LIB_MAJOR_VERSION}) -IF(WIN32 AND NOT LIBTYPE STREQUAL "STATIC") - SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES PREFIX "") + # Make sure to compile the common code with PIC, since it'll be linked into + # shared libs that needs it. + SET_PROPERTY(TARGET common PROPERTY POSITION_INDEPENDENT_CODE TRUE) + SET(COMMON_LIB common) - IF(MINGW AND ALSOFT_BUILD_IMPORT_LIB) - FIND_PROGRAM(SED_EXECUTABLE NAMES sed DOC "sed executable") - FIND_PROGRAM(DLLTOOL_EXECUTABLE NAMES "${DLLTOOL}" DOC "dlltool executable") - IF(NOT SED_EXECUTABLE OR NOT DLLTOOL_EXECUTABLE) - MESSAGE(STATUS "") - IF(NOT SED_EXECUTABLE) - MESSAGE(STATUS "WARNING: Cannot find sed, disabling .def/.lib generation") - ENDIF() - IF(NOT DLLTOOL_EXECUTABLE) - MESSAGE(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation") - ENDIF() - ELSE() - SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES LINK_FLAGS "-Wl,--output-def,${LIBNAME}.def") - ADD_CUSTOM_COMMAND(TARGET ${LIBNAME} POST_BUILD - COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" ${LIBNAME}.def - COMMAND "${DLLTOOL_EXECUTABLE}" -d ${LIBNAME}.def -l ${LIBNAME}.lib -D ${LIBNAME}.dll - COMMENT "Stripping ordinals from ${LIBNAME}.def and generating ${LIBNAME}.lib..." - VERBATIM - ) + IF(WIN32) + IF(MSVC) + SET(SUBSYS_FLAG ${SUBSYS_FLAG} "/SUBSYSTEM:WINDOWS") + ELSEIF(CMAKE_COMPILER_IS_GNUCC) + SET(SUBSYS_FLAG ${SUBSYS_FLAG} "-mwindows") ENDIF() ENDIF() + + IF(WIN32 AND ALSOFT_BUILD_ROUTER) + ADD_LIBRARY(OpenAL SHARED router/router.c router/router.h router/alc.c router/al.c) + TARGET_COMPILE_DEFINITIONS(OpenAL + PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES ${CPP_DEFS}) + TARGET_COMPILE_OPTIONS(OpenAL PRIVATE ${C_FLAGS}) + TARGET_LINK_LIBRARIES(OpenAL PRIVATE ${LINKER_FLAGS} ${COMMON_LIB}) + SET_TARGET_PROPERTIES(OpenAL PROPERTIES PREFIX "") + SET_TARGET_PROPERTIES(OpenAL PROPERTIES OUTPUT_NAME ${LIBNAME}) + IF(TARGET build_version) + ADD_DEPENDENCIES(OpenAL build_version) + ENDIF() + SET(HAS_ROUTER 1) + + SET(LIBNAME "soft_oal") + SET(IMPL_TARGET soft_oal) + ENDIF() + + ADD_LIBRARY(${IMPL_TARGET} SHARED ${OPENAL_OBJS} ${ALC_OBJS}) + IF(WIN32) + SET_TARGET_PROPERTIES(${IMPL_TARGET} PROPERTIES PREFIX "") + ENDIF() +ENDIF() +SET_TARGET_PROPERTIES(${IMPL_TARGET} PROPERTIES OUTPUT_NAME ${LIBNAME} + VERSION ${LIB_VERSION} + SOVERSION ${LIB_MAJOR_VERSION} +) +TARGET_COMPILE_DEFINITIONS(${IMPL_TARGET} + PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES ${CPP_DEFS}) +TARGET_INCLUDE_DIRECTORIES(${IMPL_TARGET} + PRIVATE "${OpenAL_SOURCE_DIR}/OpenAL32/Include" "${OpenAL_SOURCE_DIR}/Alc" ${INC_PATHS}) +TARGET_COMPILE_OPTIONS(${IMPL_TARGET} PRIVATE ${C_FLAGS}) +TARGET_LINK_LIBRARIES(${IMPL_TARGET} + PRIVATE ${LINKER_FLAGS} ${COMMON_LIB} ${EXTRA_LIBS} ${MATH_LIB}) +IF(TARGET build_version) + ADD_DEPENDENCIES(${IMPL_TARGET} build_version) +ENDIF() + +IF(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC") + FIND_PROGRAM(SED_EXECUTABLE NAMES sed DOC "sed executable") + FIND_PROGRAM(DLLTOOL_EXECUTABLE NAMES "${DLLTOOL}" DOC "dlltool executable") + IF(NOT SED_EXECUTABLE OR NOT DLLTOOL_EXECUTABLE) + MESSAGE(STATUS "") + IF(NOT SED_EXECUTABLE) + MESSAGE(STATUS "WARNING: Cannot find sed, disabling .def/.lib generation") + ENDIF() + IF(NOT DLLTOOL_EXECUTABLE) + MESSAGE(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation") + ENDIF() + ELSE() + SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS + " -Wl,--output-def,OpenAL32.def") + ADD_CUSTOM_COMMAND(TARGET OpenAL POST_BUILD + COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def + COMMAND "${DLLTOOL_EXECUTABLE}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll + COMMENT "Stripping ordinals from OpenAL32.def and generating OpenAL32.lib..." + VERBATIM + ) + ENDIF() ENDIF() -TARGET_LINK_LIBRARIES(${LIBNAME} common ${EXTRA_LIBS}) - IF(ALSOFT_INSTALL) - # Add an install target here - INSTALL(TARGETS ${LIBNAME} + INSTALL(TARGETS OpenAL EXPORT OpenAL RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_INCLUDEDIR}/AL ) + EXPORT(TARGETS OpenAL + NAMESPACE OpenAL:: + FILE OpenALConfig.cmake) + INSTALL(EXPORT OpenAL + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/OpenAL + NAMESPACE OpenAL:: + FILE OpenALConfig.cmake) INSTALL(FILES include/AL/al.h include/AL/alc.h include/AL/alext.h @@ -1261,9 +1448,19 @@ IF(ALSOFT_INSTALL) ) INSTALL(FILES "${OpenAL_BINARY_DIR}/openal.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + IF(TARGET soft_oal) + INSTALL(TARGETS soft_oal + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) + ENDIF() ENDIF() +if(HAS_ROUTER) + message(STATUS "") + message(STATUS "Building DLL router") +endif() + MESSAGE(STATUS "") MESSAGE(STATUS "Building OpenAL with support for the following backends:") MESSAGE(STATUS " ${BACKENDS}") @@ -1309,6 +1506,7 @@ IF(ALSOFT_AMBDEC_PRESETS) INSTALL(FILES presets/3D7.1.ambdec presets/hexagon.ambdec presets/itu5.1.ambdec + presets/itu5.1-nocenter.ambdec presets/rectangle.ambdec presets/square.ambdec presets/presets.txt @@ -1320,23 +1518,22 @@ ENDIF() IF(ALSOFT_UTILS) ADD_EXECUTABLE(openal-info utils/openal-info.c) - SET_PROPERTY(TARGET openal-info APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) - TARGET_LINK_LIBRARIES(openal-info ${LIBNAME}) + TARGET_COMPILE_OPTIONS(openal-info PRIVATE ${C_FLAGS}) + TARGET_LINK_LIBRARIES(openal-info PRIVATE ${LINKER_FLAGS} OpenAL) - ADD_EXECUTABLE(makehrtf utils/makehrtf.c) - SET_PROPERTY(TARGET makehrtf APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) - IF(HAVE_LIBM) - TARGET_LINK_LIBRARIES(makehrtf m) + SET(MAKEHRTF_SRCS utils/makehrtf.c) + IF(NOT HAVE_GETOPT) + SET(MAKEHRTF_SRCS ${MAKEHRTF_SRCS} utils/getopt.c utils/getopt.h) ENDIF() - - ADD_EXECUTABLE(bsincgen utils/bsincgen.c) - SET_PROPERTY(TARGET bsincgen APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) + ADD_EXECUTABLE(makehrtf ${MAKEHRTF_SRCS}) + TARGET_COMPILE_DEFINITIONS(makehrtf PRIVATE ${CPP_DEFS}) + TARGET_COMPILE_OPTIONS(makehrtf PRIVATE ${C_FLAGS}) IF(HAVE_LIBM) - TARGET_LINK_LIBRARIES(bsincgen m) + TARGET_LINK_LIBRARIES(makehrtf PRIVATE ${LINKER_FLAGS} m) ENDIF() IF(ALSOFT_INSTALL) - INSTALL(TARGETS openal-info makehrtf bsincgen + INSTALL(TARGETS openal-info makehrtf RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -1351,12 +1548,12 @@ IF(ALSOFT_UTILS) ENDIF() IF(ALSOFT_TESTS) - ADD_LIBRARY(test-common STATIC examples/common/alhelpers.c) - SET_PROPERTY(TARGET test-common APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) + SET(TEST_COMMON_OBJS examples/common/alhelpers.c) - ADD_EXECUTABLE(altonegen examples/altonegen.c) - TARGET_LINK_LIBRARIES(altonegen test-common ${LIBNAME}) - SET_PROPERTY(TARGET altonegen APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) + ADD_EXECUTABLE(altonegen examples/altonegen.c ${TEST_COMMON_OBJS}) + TARGET_COMPILE_DEFINITIONS(altonegen PRIVATE ${CPP_DEFS}) + TARGET_COMPILE_OPTIONS(altonegen PRIVATE ${C_FLAGS}) + TARGET_LINK_LIBRARIES(altonegen PRIVATE ${LINKER_FLAGS} common OpenAL ${MATH_LIB}) IF(ALSOFT_INSTALL) INSTALL(TARGETS altonegen @@ -1371,86 +1568,134 @@ IF(ALSOFT_TESTS) ENDIF() IF(ALSOFT_EXAMPLES) - IF(SDL2_FOUND AND SDL_SOUND_FOUND) - ADD_LIBRARY(ex-common STATIC examples/common/alhelpers.c - examples/common/sdl_sound.c) - SET_PROPERTY(TARGET ex-common APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) - SET_PROPERTY(TARGET ex-common APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} - ${SDL_SOUND_INCLUDE_DIR}) + ADD_EXECUTABLE(alrecord examples/alrecord.c) + TARGET_COMPILE_DEFINITIONS(alrecord PRIVATE ${CPP_DEFS}) + TARGET_COMPILE_OPTIONS(alrecord PRIVATE ${C_FLAGS}) + TARGET_LINK_LIBRARIES(alrecord PRIVATE ${LINKER_FLAGS} common OpenAL) - ADD_EXECUTABLE(alstream examples/alstream.c) - TARGET_LINK_LIBRARIES(alstream ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} - common ${LIBNAME}) - SET_PROPERTY(TARGET alstream APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) - SET_PROPERTY(TARGET alstream APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} - ${SDL_SOUND_INCLUDE_DIR}) + IF(ALSOFT_INSTALL) + INSTALL(TARGETS alrecord + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + ENDIF() - ADD_EXECUTABLE(alreverb examples/alreverb.c) - TARGET_LINK_LIBRARIES(alreverb ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} - common ${LIBNAME}) - SET_PROPERTY(TARGET alreverb APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) - SET_PROPERTY(TARGET alreverb APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} - ${SDL_SOUND_INCLUDE_DIR}) + MESSAGE(STATUS "Building example programs") - ADD_EXECUTABLE(allatency examples/allatency.c) - TARGET_LINK_LIBRARIES(allatency ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} - common ${LIBNAME}) - SET_PROPERTY(TARGET allatency APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) - SET_PROPERTY(TARGET allatency APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} - ${SDL_SOUND_INCLUDE_DIR}) + IF(SDL2_FOUND) + IF(SDL_SOUND_FOUND) + # Add a static library with common functions used by multiple targets + ADD_LIBRARY(ex-common STATIC examples/common/alhelpers.c) + TARGET_COMPILE_DEFINITIONS(ex-common PRIVATE ${CPP_DEFS}) + TARGET_COMPILE_OPTIONS(ex-common PRIVATE ${C_FLAGS}) - ADD_EXECUTABLE(alloopback examples/alloopback.c) - TARGET_LINK_LIBRARIES(alloopback ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} - common ${LIBNAME}) - SET_PROPERTY(TARGET alloopback APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) - SET_PROPERTY(TARGET alloopback APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} - ${SDL_SOUND_INCLUDE_DIR}) + ADD_EXECUTABLE(alplay examples/alplay.c) + TARGET_COMPILE_DEFINITIONS(alplay PRIVATE ${CPP_DEFS}) + TARGET_INCLUDE_DIRECTORIES(alplay + PRIVATE ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) + TARGET_COMPILE_OPTIONS(alplay PRIVATE ${C_FLAGS}) + TARGET_LINK_LIBRARIES(alplay + PRIVATE ${LINKER_FLAGS} ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ex-common common + OpenAL) - ADD_EXECUTABLE(alhrtf examples/alhrtf.c) - TARGET_LINK_LIBRARIES(alhrtf ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} - common ${LIBNAME}) - SET_PROPERTY(TARGET alhrtf APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) - SET_PROPERTY(TARGET alhrtf APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} - ${SDL_SOUND_INCLUDE_DIR}) + ADD_EXECUTABLE(alstream examples/alstream.c) + TARGET_COMPILE_DEFINITIONS(alstream PRIVATE ${CPP_DEFS}) + TARGET_INCLUDE_DIRECTORIES(alstream + PRIVATE ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) + TARGET_COMPILE_OPTIONS(alstream PRIVATE ${C_FLAGS}) + TARGET_LINK_LIBRARIES(alstream + PRIVATE ${LINKER_FLAGS} ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ex-common common + OpenAL) - IF(ALSOFT_INSTALL) - INSTALL(TARGETS alstream alreverb allatency alloopback alhrtf - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - ) + ADD_EXECUTABLE(alreverb examples/alreverb.c) + TARGET_COMPILE_DEFINITIONS(alreverb PRIVATE ${CPP_DEFS}) + TARGET_INCLUDE_DIRECTORIES(alreverb + PRIVATE ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) + TARGET_COMPILE_OPTIONS(alreverb PRIVATE ${C_FLAGS}) + TARGET_LINK_LIBRARIES(alreverb + PRIVATE ${LINKER_FLAGS} ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ex-common common + OpenAL) + + ADD_EXECUTABLE(almultireverb examples/almultireverb.c) + TARGET_COMPILE_DEFINITIONS(almultireverb PRIVATE ${CPP_DEFS}) + TARGET_INCLUDE_DIRECTORIES(almultireverb + PRIVATE ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) + TARGET_COMPILE_OPTIONS(almultireverb PRIVATE ${C_FLAGS}) + TARGET_LINK_LIBRARIES(almultireverb + PRIVATE ${LINKER_FLAGS} ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ex-common common + OpenAL ${MATH_LIB}) + + ADD_EXECUTABLE(allatency examples/allatency.c) + TARGET_COMPILE_DEFINITIONS(allatency PRIVATE ${CPP_DEFS}) + TARGET_INCLUDE_DIRECTORIES(allatency + PRIVATE ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) + TARGET_COMPILE_OPTIONS(allatency PRIVATE ${C_FLAGS}) + TARGET_LINK_LIBRARIES(allatency + PRIVATE ${LINKER_FLAGS} ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ex-common common + OpenAL) + + ADD_EXECUTABLE(alloopback examples/alloopback.c) + TARGET_COMPILE_DEFINITIONS(alloopback PRIVATE ${CPP_DEFS}) + TARGET_INCLUDE_DIRECTORIES(alloopback + PRIVATE ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) + TARGET_COMPILE_OPTIONS(alloopback PRIVATE ${C_FLAGS}) + TARGET_LINK_LIBRARIES(alloopback + PRIVATE ${LINKER_FLAGS} ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ex-common common + OpenAL ${MATH_LIB}) + + ADD_EXECUTABLE(alhrtf examples/alhrtf.c) + TARGET_COMPILE_DEFINITIONS(alhrtf PRIVATE ${CPP_DEFS}) + TARGET_INCLUDE_DIRECTORIES(alhrtf + PRIVATE ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) + TARGET_COMPILE_OPTIONS(alhrtf PRIVATE ${C_FLAGS}) + TARGET_LINK_LIBRARIES(alhrtf + PRIVATE ${LINKER_FLAGS} ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} ex-common common + OpenAL ${MATH_LIB}) + + IF(ALSOFT_INSTALL) + INSTALL(TARGETS alplay alstream alreverb almultireverb allatency alloopback alhrtf + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + ENDIF() + + MESSAGE(STATUS "Building SDL_sound example programs") ENDIF() SET(FFVER_OK FALSE) IF(FFMPEG_FOUND) SET(FFVER_OK TRUE) - IF(AVFORMAT_VERSION VERSION_LESS "55.33.100") - MESSAGE(STATUS "libavformat is too old! (${AVFORMAT_VERSION}, wanted 55.33.100)") + IF(AVFORMAT_VERSION VERSION_LESS "57.56.101") + MESSAGE(STATUS "libavformat is too old! (${AVFORMAT_VERSION}, wanted 57.56.101)") SET(FFVER_OK FALSE) ENDIF() - IF(AVCODEC_VERSION VERSION_LESS "55.52.102") - MESSAGE(STATUS "libavcodec is too old! (${AVCODEC_VERSION}, wanted 55.52.102)") + IF(AVCODEC_VERSION VERSION_LESS "57.64.101") + MESSAGE(STATUS "libavcodec is too old! (${AVCODEC_VERSION}, wanted 57.64.101)") SET(FFVER_OK FALSE) ENDIF() - IF(AVUTIL_VERSION VERSION_LESS "52.66.100") - MESSAGE(STATUS "libavutil is too old! (${AVUTIL_VERSION}, wanted 52.66.100)") + IF(AVUTIL_VERSION VERSION_LESS "55.34.101") + MESSAGE(STATUS "libavutil is too old! (${AVUTIL_VERSION}, wanted 55.34.101)") SET(FFVER_OK FALSE) ENDIF() - IF(SWSCALE_VERSION VERSION_LESS "2.5.102") - MESSAGE(STATUS "libswscale is too old! (${SWSCALE_VERSION}, wanted 2.5.102)") + IF(SWSCALE_VERSION VERSION_LESS "4.2.100") + MESSAGE(STATUS "libswscale is too old! (${SWSCALE_VERSION}, wanted 4.2.100)") SET(FFVER_OK FALSE) ENDIF() - IF(SWRESAMPLE_VERSION VERSION_LESS "0.18.100") - MESSAGE(STATUS "libswresample is too old! (${SWRESAMPLE_VERSION}, wanted 0.18.100)") + IF(SWRESAMPLE_VERSION VERSION_LESS "2.3.100") + MESSAGE(STATUS "libswresample is too old! (${SWRESAMPLE_VERSION}, wanted 2.3.100)") SET(FFVER_OK FALSE) ENDIF() ENDIF() - IF(FFVER_OK AND NOT MSVC) - ADD_EXECUTABLE(alffplay examples/alffplay.c) - TARGET_LINK_LIBRARIES(alffplay common ex-common ${SDL2_LIBRARY} ${LIBNAME} ${FFMPEG_LIBRARIES}) - SET_PROPERTY(TARGET alffplay APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) - SET_PROPERTY(TARGET alffplay APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} - ${FFMPEG_INCLUDE_DIRS}) + IF(FFVER_OK) + ADD_EXECUTABLE(alffplay examples/alffplay.cpp) + TARGET_COMPILE_DEFINITIONS(alffplay PRIVATE ${CPP_DEFS}) + TARGET_INCLUDE_DIRECTORIES(alffplay + PRIVATE ${SDL2_INCLUDE_DIR} ${FFMPEG_INCLUDE_DIRS}) + TARGET_COMPILE_OPTIONS(alffplay PRIVATE ${C_FLAGS}) + TARGET_LINK_LIBRARIES(alffplay + PRIVATE ${LINKER_FLAGS} ${SDL2_LIBRARY} ${FFMPEG_LIBRARIES} common OpenAL) IF(ALSOFT_INSTALL) INSTALL(TARGETS alffplay @@ -1459,9 +1704,7 @@ IF(ALSOFT_EXAMPLES) ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) ENDIF() - MESSAGE(STATUS "Building SDL and FFmpeg example programs") - ELSE() - MESSAGE(STATUS "Building SDL example programs") + MESSAGE(STATUS "Building SDL+FFmpeg example programs") ENDIF() MESSAGE(STATUS "") ENDIF() diff --git a/Engine/lib/openal-soft/ChangeLog b/Engine/lib/openal-soft/ChangeLog index 189872cf7..a7aec6388 100644 --- a/Engine/lib/openal-soft/ChangeLog +++ b/Engine/lib/openal-soft/ChangeLog @@ -1,3 +1,168 @@ +openal-soft-1.19.0: + + Implemented the ALC_SOFT_device_clock extension. + + Implemented the Pitch Shifter effect. + + Fixed compiling on FreeBSD systems that use freebsd-lib 9.1. + + Fixed compiling on NetBSD. + + Fixed the reverb effect's density scale and panning parameters. + + Fixed use of the WASAPI backend with certain games, which caused odd COM + initialization errors. + + Increased the number of virtual channels for decoding Ambisonics to HRTF + output. + + Replaced the 4-point Sinc resampler with a more efficient cubic resampler. + + Renamed the MMDevAPI backend to WASAPI. + + Added support fot 24-bit, dual-ear HRTF data sets. The built-in data set + has been updated to 24-bit. + + Added a 24- to 48-point band-limited Sinc resampler. + + Added an SDL2 playback backend. Disabled by default to avoid a dependency + on SDL2. + + Improved the performance and quality of the Chorus and Flanger effects. + + Improved the efficiency of the band-limited Sinc resampler. + + Improved the Sinc resampler's transition band to avoid over-attenuating + higher frequencies. + + Improved the performance of some filter operations. + + Improved the efficiency of object ID lookups. + + Improved the efficienty of internal voice/source synchronization. + + Improved AL call error logging with contextualized messages. + + Removed the reverb effect's modulation stage. Due to the lack of reference + for its intended behavior and strength. + +openal-soft-1.18.2: + + Fixed resetting the FPU rounding mode after certain function calls on + Windows. + + Fixed use of SSE intrinsics when building with Clang on Windows. + + Fixed a crash with the JACK backend when using JACK1. + + Fixed use of pthread_setnane_np on NetBSD. + + Fixed building on FreeBSD with an older freebsd-lib. + + OSS now links with libossaudio if found at build time (for NetBSD). + +openal-soft-1.18.1: + + Fixed an issue where resuming a source might not restart playing it. + + Fixed PulseAudio playback when the configured stream length is much less + than the requested length. + + Fixed MMDevAPI capture with sample rates not matching the backing device. + + Fixed int32 output for the Wave Writer. + + Fixed enumeration of OSS devices that are missing device files. + + Added correct retrieval of the executable's path on FreeBSD. + + Added a config option to specify the dithering depth. + + Added a 5.1 decoder preset that excludes front-center output. + +openal-soft-1.18.0: + + Implemented the AL_EXT_STEREO_ANGLES and AL_EXT_SOURCE_RADIUS extensions. + + Implemented the AL_SOFT_gain_clamp_ex, AL_SOFT_source_resampler, + AL_SOFT_source_spatialize, and ALC_SOFT_output_limiter extensions. + + Implemented 3D processing for some effects. Currently implemented for + Reverb, Compressor, Equalizer, and Ring Modulator. + + Implemented 2-channel UHJ output encoding. This needs to be enabled with a + config option to be used. + + Implemented dual-band processing for high-quality ambisonic decoding. + + Implemented distance-compensation for surround sound output. + + Implemented near-field emulation and compensation with ambisonic rendering. + Currently only applies when using the high-quality ambisonic decoder or + ambisonic output, with appropriate config options. + + Implemented an output limiter to reduce the amount of distortion from + clipping. + + Implemented dithering for 8-bit and 16-bit output. + + Implemented a config option to select a preferred HRTF. + + Implemented a run-time check for NEON extensions using /proc/cpuinfo. + + Implemented experimental capture support for the OpenSL backend. + + Fixed building on compilers with NEON support but don't default to having + NEON enabled. + + Fixed support for JACK on Windows. + + Fixed starting a source while alcSuspendContext is in effect. + + Fixed detection of headsets as headphones, with MMDevAPI. + + Added support for AmbDec config files, for custom ambisonic decoder + configurations. Version 3 files only. + + Added backend-specific options to alsoft-config. + + Added first-, second-, and third-order ambisonic output formats. Currently + only works with backends that don't rely on channel labels, like JACK, + ALSA, and OSS. + + Added a build option to embed the default HRTFs into the lib. + + Added AmbDec presets to enable high-quality ambisonic decoding. + + Added an AmbDec preset for 3D7.1 speaker setups. + + Added documentation regarding Ambisonics, 3D7.1, AmbDec config files, and + the provided ambdec presets. + + Added the ability for MMDevAPI to open devices given a Device ID or GUID + string. + + Added an option to the example apps to open a specific device. + + Increased the maximum auxiliary send limit to 16 (up from 4). Requires + requesting them with the ALC_MAX_AUXILIARY_SENDS context creation + attribute. + + Increased the default auxiliary effect slot count to 64 (up from 4). + + Reduced the default period count to 3 (down from 4). + + Slightly improved automatic naming for enumerated HRTFs. + + Improved B-Format decoding with HRTF output. + + Improved internal property handling for better batching behavior. + + Improved performance of certain filter uses. + + Removed support for the AL_SOFT_buffer_samples and AL_SOFT_buffer_sub_data + extensions. Due to conflicts with AL_EXT_SOURCE_RADIUS. + openal-soft-1.17.2: Implemented device enumeration for OSSv4. diff --git a/Engine/lib/openal-soft/OpenAL32/Include/alAuxEffectSlot.h b/Engine/lib/openal-soft/OpenAL32/Include/alAuxEffectSlot.h index 70fcac5c3..bb9aef590 100644 --- a/Engine/lib/openal-soft/OpenAL32/Include/alAuxEffectSlot.h +++ b/Engine/lib/openal-soft/OpenAL32/Include/alAuxEffectSlot.h @@ -4,6 +4,7 @@ #include "alMain.h" #include "alEffect.h" +#include "atomic.h" #include "align.h" #ifdef __cplusplus @@ -18,7 +19,7 @@ typedef struct ALeffectState { const struct ALeffectStateVtable *vtbl; ALfloat (*OutBuffer)[BUFFERSIZE]; - ALuint OutChannels; + ALsizei OutChannels; } ALeffectState; void ALeffectState_Construct(ALeffectState *state); @@ -28,17 +29,22 @@ struct ALeffectStateVtable { void (*const Destruct)(ALeffectState *state); ALboolean (*const deviceUpdate)(ALeffectState *state, ALCdevice *device); - void (*const update)(ALeffectState *state, const ALCdevice *device, const struct ALeffectslot *slot, const union ALeffectProps *props); - void (*const process)(ALeffectState *state, ALuint samplesToDo, const ALfloat (*restrict samplesIn)[BUFFERSIZE], ALfloat (*restrict samplesOut)[BUFFERSIZE], ALuint numChannels); + void (*const update)(ALeffectState *state, const ALCcontext *context, const struct ALeffectslot *slot, const union ALeffectProps *props); + void (*const process)(ALeffectState *state, ALsizei samplesToDo, const ALfloat (*restrict samplesIn)[BUFFERSIZE], ALfloat (*restrict samplesOut)[BUFFERSIZE], ALsizei numChannels); void (*const Delete)(void *ptr); }; +/* Small hack to use a pointer-to-array types as a normal argument type. + * Shouldn't be used directly. + */ +typedef ALfloat ALfloatBUFFERSIZE[BUFFERSIZE]; + #define DEFINE_ALEFFECTSTATE_VTABLE(T) \ DECLARE_THUNK(T, ALeffectState, void, Destruct) \ DECLARE_THUNK1(T, ALeffectState, ALboolean, deviceUpdate, ALCdevice*) \ -DECLARE_THUNK3(T, ALeffectState, void, update, const ALCdevice*, const ALeffectslot*, const ALeffectProps*) \ -DECLARE_THUNK4(T, ALeffectState, void, process, ALuint, const ALfloatBUFFERSIZE*restrict, ALfloatBUFFERSIZE*restrict, ALuint) \ +DECLARE_THUNK3(T, ALeffectState, void, update, const ALCcontext*, const ALeffectslot*, const ALeffectProps*) \ +DECLARE_THUNK4(T, ALeffectState, void, process, ALsizei, const ALfloatBUFFERSIZE*restrict, ALfloatBUFFERSIZE*restrict, ALsizei) \ static void T##_ALeffectState_Delete(void *ptr) \ { return T##_Delete(STATIC_UPCAST(T, ALeffectState, (ALeffectState*)ptr)); } \ \ @@ -53,43 +59,48 @@ static const struct ALeffectStateVtable T##_ALeffectState_vtable = { \ } -struct ALeffectStateFactoryVtable; +struct EffectStateFactoryVtable; -typedef struct ALeffectStateFactory { - const struct ALeffectStateFactoryVtable *vtbl; -} ALeffectStateFactory; +typedef struct EffectStateFactory { + const struct EffectStateFactoryVtable *vtab; +} EffectStateFactory; -struct ALeffectStateFactoryVtable { - ALeffectState *(*const create)(ALeffectStateFactory *factory); +struct EffectStateFactoryVtable { + ALeffectState *(*const create)(EffectStateFactory *factory); }; +#define EffectStateFactory_create(x) ((x)->vtab->create((x))) -#define DEFINE_ALEFFECTSTATEFACTORY_VTABLE(T) \ -DECLARE_THUNK(T, ALeffectStateFactory, ALeffectState*, create) \ +#define DEFINE_EFFECTSTATEFACTORY_VTABLE(T) \ +DECLARE_THUNK(T, EffectStateFactory, ALeffectState*, create) \ \ -static const struct ALeffectStateFactoryVtable T##_ALeffectStateFactory_vtable = { \ - T##_ALeffectStateFactory_create, \ +static const struct EffectStateFactoryVtable T##_EffectStateFactory_vtable = { \ + T##_EffectStateFactory_create, \ } #define MAX_EFFECT_CHANNELS (4) -struct ALeffectslotProps { - ATOMIC(ALfloat) Gain; - ATOMIC(ALboolean) AuxSendAuto; +struct ALeffectslotArray { + ALsizei count; + struct ALeffectslot *slot[]; +}; - ATOMIC(ALenum) Type; + +struct ALeffectslotProps { + ALfloat Gain; + ALboolean AuxSendAuto; + + ALenum Type; ALeffectProps Props; - ATOMIC(ALeffectState*) State; + ALeffectState *State; ATOMIC(struct ALeffectslotProps*) next; }; typedef struct ALeffectslot { - ALboolean NeedsUpdate; - ALfloat Gain; ALboolean AuxSendAuto; @@ -100,81 +111,70 @@ typedef struct ALeffectslot { ALeffectState *State; } Effect; + ATOMIC_FLAG PropsClean; + RefCount ref; ATOMIC(struct ALeffectslotProps*) Update; - ATOMIC(struct ALeffectslotProps*) FreeList; struct { ALfloat Gain; ALboolean AuxSendAuto; ALenum EffectType; + ALeffectProps EffectProps; ALeffectState *EffectState; ALfloat RoomRolloff; /* Added to the source's room rolloff, not multiplied. */ ALfloat DecayTime; + ALfloat DecayLFRatio; + ALfloat DecayHFRatio; + ALboolean DecayHFLimit; ALfloat AirAbsorptionGainHF; } Params; /* Self ID */ ALuint id; - ALuint NumChannels; + ALsizei NumChannels; BFChannelConfig ChanMap[MAX_EFFECT_CHANNELS]; /* Wet buffer configuration is ACN channel order with N3D scaling: * * Channel 0 is the unattenuated mono signal. - * * Channel 1 is OpenAL -X - * * Channel 2 is OpenAL Y - * * Channel 3 is OpenAL -Z + * * Channel 1 is OpenAL -X * sqrt(3) + * * Channel 2 is OpenAL Y * sqrt(3) + * * Channel 3 is OpenAL -Z * sqrt(3) * Consequently, effects that only want to work with mono input can use * channel 0 by itself. Effects that want multichannel can process the * ambisonics signal and make a B-Format pan (ComputeFirstOrderGains) for * first-order device output (FOAOut). */ alignas(16) ALfloat WetBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE]; - - ATOMIC(struct ALeffectslot*) next; } ALeffectslot; -inline void LockEffectSlotsRead(ALCcontext *context) -{ LockUIntMapRead(&context->EffectSlotMap); } -inline void UnlockEffectSlotsRead(ALCcontext *context) -{ UnlockUIntMapRead(&context->EffectSlotMap); } -inline void LockEffectSlotsWrite(ALCcontext *context) -{ LockUIntMapWrite(&context->EffectSlotMap); } -inline void UnlockEffectSlotsWrite(ALCcontext *context) -{ UnlockUIntMapWrite(&context->EffectSlotMap); } - -inline struct ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) -{ return (struct ALeffectslot*)LookupUIntMapKeyNoLock(&context->EffectSlotMap, id); } -inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id) -{ return (struct ALeffectslot*)RemoveUIntMapKeyNoLock(&context->EffectSlotMap, id); } - ALenum InitEffectSlot(ALeffectslot *slot); void DeinitEffectSlot(ALeffectslot *slot); -void UpdateEffectSlotProps(ALeffectslot *slot); +void UpdateEffectSlotProps(ALeffectslot *slot, ALCcontext *context); void UpdateAllEffectSlotProps(ALCcontext *context); ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context); -ALeffectStateFactory *ALnullStateFactory_getFactory(void); -ALeffectStateFactory *ALreverbStateFactory_getFactory(void); -ALeffectStateFactory *ALchorusStateFactory_getFactory(void); -ALeffectStateFactory *ALcompressorStateFactory_getFactory(void); -ALeffectStateFactory *ALdistortionStateFactory_getFactory(void); -ALeffectStateFactory *ALechoStateFactory_getFactory(void); -ALeffectStateFactory *ALequalizerStateFactory_getFactory(void); -ALeffectStateFactory *ALflangerStateFactory_getFactory(void); -ALeffectStateFactory *ALmodulatorStateFactory_getFactory(void); +EffectStateFactory *NullStateFactory_getFactory(void); +EffectStateFactory *ReverbStateFactory_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 *ModulatorStateFactory_getFactory(void); +EffectStateFactory *PshifterStateFactory_getFactory(void); -ALeffectStateFactory *ALdedicatedStateFactory_getFactory(void); +EffectStateFactory *DedicatedStateFactory_getFactory(void); -ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect); +ALenum InitializeEffect(ALCcontext *Context, ALeffectslot *EffectSlot, ALeffect *effect); -void InitEffectFactoryMap(void); -void DeinitEffectFactoryMap(void); +void ALeffectState_DecRef(ALeffectState *state); #ifdef __cplusplus } diff --git a/Engine/lib/openal-soft/OpenAL32/Include/alBuffer.h b/Engine/lib/openal-soft/OpenAL32/Include/alBuffer.h index e99af050a..fbe3e6e5e 100644 --- a/Engine/lib/openal-soft/OpenAL32/Include/alBuffer.h +++ b/Engine/lib/openal-soft/OpenAL32/Include/alBuffer.h @@ -1,7 +1,13 @@ #ifndef _AL_BUFFER_H_ #define _AL_BUFFER_H_ -#include "alMain.h" +#include "AL/alc.h" +#include "AL/al.h" +#include "AL/alext.h" + +#include "inprogext.h" +#include "atomic.h" +#include "rwlock.h" #ifdef __cplusplus extern "C" { @@ -9,36 +15,30 @@ extern "C" { /* User formats */ enum UserFmtType { - UserFmtByte = AL_BYTE_SOFT, - UserFmtUByte = AL_UNSIGNED_BYTE_SOFT, - UserFmtShort = AL_SHORT_SOFT, - UserFmtUShort = AL_UNSIGNED_SHORT_SOFT, - UserFmtInt = AL_INT_SOFT, - UserFmtUInt = AL_UNSIGNED_INT_SOFT, - UserFmtFloat = AL_FLOAT_SOFT, - UserFmtDouble = AL_DOUBLE_SOFT, - UserFmtByte3 = AL_BYTE3_SOFT, - UserFmtUByte3 = AL_UNSIGNED_BYTE3_SOFT, - UserFmtMulaw = AL_MULAW_SOFT, - UserFmtAlaw = 0x10000000, + UserFmtUByte, + UserFmtShort, + UserFmtFloat, + UserFmtDouble, + UserFmtMulaw, + UserFmtAlaw, UserFmtIMA4, UserFmtMSADPCM, }; enum UserFmtChannels { - UserFmtMono = AL_MONO_SOFT, - UserFmtStereo = AL_STEREO_SOFT, - UserFmtRear = AL_REAR_SOFT, - UserFmtQuad = AL_QUAD_SOFT, - UserFmtX51 = AL_5POINT1_SOFT, /* (WFX order) */ - UserFmtX61 = AL_6POINT1_SOFT, /* (WFX order) */ - UserFmtX71 = AL_7POINT1_SOFT, /* (WFX order) */ - UserFmtBFormat2D = AL_BFORMAT2D_SOFT, /* WXY */ - UserFmtBFormat3D = AL_BFORMAT3D_SOFT, /* WXYZ */ + UserFmtMono, + UserFmtStereo, + UserFmtRear, + UserFmtQuad, + UserFmtX51, /* (WFX order) */ + UserFmtX61, /* (WFX order) */ + UserFmtX71, /* (WFX order) */ + UserFmtBFormat2D, /* WXY */ + UserFmtBFormat3D, /* WXYZ */ }; -ALuint BytesFromUserFmt(enum UserFmtType type); -ALuint ChannelsFromUserFmt(enum UserFmtChannels chans); -inline ALuint FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type) +ALsizei BytesFromUserFmt(enum UserFmtType type); +ALsizei ChannelsFromUserFmt(enum UserFmtChannels chans); +inline ALsizei FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type) { return ChannelsFromUserFmt(chans) * BytesFromUserFmt(type); } @@ -46,9 +46,12 @@ inline ALuint FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType /* Storable formats */ enum FmtType { - FmtByte = UserFmtByte, - FmtShort = UserFmtShort, - FmtFloat = UserFmtFloat, + FmtUByte = UserFmtUByte, + FmtShort = UserFmtShort, + FmtFloat = UserFmtFloat, + FmtDouble = UserFmtDouble, + FmtMulaw = UserFmtMulaw, + FmtAlaw = UserFmtAlaw, }; enum FmtChannels { FmtMono = UserFmtMono, @@ -63,9 +66,9 @@ enum FmtChannels { }; #define MAX_INPUT_CHANNELS (8) -ALuint BytesFromFmt(enum FmtType type); -ALuint ChannelsFromFmt(enum FmtChannels chans); -inline ALuint FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type) +ALsizei BytesFromFmt(enum FmtType type); +ALsizei ChannelsFromFmt(enum FmtChannels chans); +inline ALsizei FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type) { return ChannelsFromFmt(chans) * BytesFromFmt(type); } @@ -74,53 +77,35 @@ inline ALuint FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type) typedef struct ALbuffer { ALvoid *data; - ALsizei Frequency; - ALenum Format; - ALsizei SampleLen; + ALsizei Frequency; + ALbitfieldSOFT Access; + ALsizei SampleLen; enum FmtChannels FmtChannels; enum FmtType FmtType; - ALuint BytesAlloc; + ALsizei BytesAlloc; - enum UserFmtChannels OriginalChannels; - enum UserFmtType OriginalType; - ALsizei OriginalSize; - ALsizei OriginalAlign; + enum UserFmtType OriginalType; + ALsizei OriginalSize; + ALsizei OriginalAlign; - ALsizei LoopStart; - ALsizei LoopEnd; + ALsizei LoopStart; + ALsizei LoopEnd; ATOMIC(ALsizei) UnpackAlign; ATOMIC(ALsizei) PackAlign; + ALbitfieldSOFT MappedAccess; + ALsizei MappedOffset; + ALsizei MappedSize; + /* Number of times buffer was attached to a source (deletion can only occur when 0) */ RefCount ref; - RWLock lock; - /* Self ID */ ALuint id; } ALbuffer; -ALbuffer *NewBuffer(ALCcontext *context); -void DeleteBuffer(ALCdevice *device, ALbuffer *buffer); - -ALenum LoadData(ALbuffer *buffer, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, ALboolean storesrc); - -inline void LockBuffersRead(ALCdevice *device) -{ LockUIntMapRead(&device->BufferMap); } -inline void UnlockBuffersRead(ALCdevice *device) -{ UnlockUIntMapRead(&device->BufferMap); } -inline void LockBuffersWrite(ALCdevice *device) -{ LockUIntMapWrite(&device->BufferMap); } -inline void UnlockBuffersWrite(ALCdevice *device) -{ UnlockUIntMapWrite(&device->BufferMap); } - -inline struct ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) -{ return (struct ALbuffer*)LookupUIntMapKeyNoLock(&device->BufferMap, id); } -inline struct ALbuffer *RemoveBuffer(ALCdevice *device, ALuint id) -{ return (struct ALbuffer*)RemoveUIntMapKeyNoLock(&device->BufferMap, id); } - ALvoid ReleaseALBuffers(ALCdevice *device); #ifdef __cplusplus diff --git a/Engine/lib/openal-soft/OpenAL32/Include/alEffect.h b/Engine/lib/openal-soft/OpenAL32/Include/alEffect.h index b97b01479..50b64ee1d 100644 --- a/Engine/lib/openal-soft/OpenAL32/Include/alEffect.h +++ b/Engine/lib/openal-soft/OpenAL32/Include/alEffect.h @@ -10,23 +10,32 @@ extern "C" { struct ALeffect; enum { - EAXREVERB = 0, - REVERB, - CHORUS, - COMPRESSOR, - DISTORTION, - ECHO, - EQUALIZER, - FLANGER, - MODULATOR, - DEDICATED, + EAXREVERB_EFFECT = 0, + REVERB_EFFECT, + CHORUS_EFFECT, + COMPRESSOR_EFFECT, + DISTORTION_EFFECT, + ECHO_EFFECT, + EQUALIZER_EFFECT, + FLANGER_EFFECT, + MODULATOR_EFFECT, + PSHIFTER_EFFECT, + DEDICATED_EFFECT, MAX_EFFECTS }; extern ALboolean DisabledEffects[MAX_EFFECTS]; extern ALfloat ReverbBoost; -extern ALboolean EmulateEAXReverb; + +struct EffectList { + const char name[16]; + int type; + ALenum val; +}; +#define EFFECTLIST_SIZE 12 +extern const struct EffectList EffectList[EFFECTLIST_SIZE]; + struct ALeffectVtable { void (*const setParami)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALint val); @@ -58,6 +67,7 @@ extern const struct ALeffectVtable ALequalizer_vtable; extern const struct ALeffectVtable ALflanger_vtable; extern const struct ALeffectVtable ALmodulator_vtable; extern const struct ALeffectVtable ALnull_vtable; +extern const struct ALeffectVtable ALpshifter_vtable; extern const struct ALeffectVtable ALdedicated_vtable; @@ -98,7 +108,7 @@ typedef union ALeffectProps { ALfloat Depth; ALfloat Feedback; ALfloat Delay; - } Chorus; + } Chorus; /* Also Flanger */ struct { ALboolean OnOff; @@ -135,21 +145,17 @@ typedef union ALeffectProps { ALfloat HighGain; } Equalizer; - struct { - ALint Waveform; - ALint Phase; - ALfloat Rate; - ALfloat Depth; - ALfloat Feedback; - ALfloat Delay; - } Flanger; - struct { ALfloat Frequency; ALfloat HighPassCutoff; ALint Waveform; } Modulator; + struct { + ALint CoarseTune; + ALint FineTune; + } Pshifter; + struct { ALfloat Gain; } Dedicated; @@ -161,33 +167,27 @@ typedef struct ALeffect { ALeffectProps Props; - const struct ALeffectVtable *vtbl; + const struct ALeffectVtable *vtab; /* Self ID */ ALuint id; } ALeffect; - -inline void LockEffectsRead(ALCdevice *device) -{ LockUIntMapRead(&device->EffectMap); } -inline void UnlockEffectsRead(ALCdevice *device) -{ UnlockUIntMapRead(&device->EffectMap); } -inline void LockEffectsWrite(ALCdevice *device) -{ LockUIntMapWrite(&device->EffectMap); } -inline void UnlockEffectsWrite(ALCdevice *device) -{ UnlockUIntMapWrite(&device->EffectMap); } - -inline struct ALeffect *LookupEffect(ALCdevice *device, ALuint id) -{ return (struct ALeffect*)LookupUIntMapKeyNoLock(&device->EffectMap, id); } -inline struct ALeffect *RemoveEffect(ALCdevice *device, ALuint id) -{ return (struct ALeffect*)RemoveUIntMapKeyNoLock(&device->EffectMap, id); } +#define ALeffect_setParami(o, c, p, v) ((o)->vtab->setParami(o, c, p, v)) +#define ALeffect_setParamf(o, c, p, v) ((o)->vtab->setParamf(o, c, p, v)) +#define ALeffect_setParamiv(o, c, p, v) ((o)->vtab->setParamiv(o, c, p, v)) +#define ALeffect_setParamfv(o, c, p, v) ((o)->vtab->setParamfv(o, c, p, v)) +#define ALeffect_getParami(o, c, p, v) ((o)->vtab->getParami(o, c, p, v)) +#define ALeffect_getParamf(o, c, p, v) ((o)->vtab->getParamf(o, c, p, v)) +#define ALeffect_getParamiv(o, c, p, v) ((o)->vtab->getParamiv(o, c, p, v)) +#define ALeffect_getParamfv(o, c, p, v) ((o)->vtab->getParamfv(o, c, p, v)) inline ALboolean IsReverbEffect(ALenum type) { return type == AL_EFFECT_REVERB || type == AL_EFFECT_EAXREVERB; } -ALenum InitEffect(ALeffect *effect); -ALvoid ReleaseALEffects(ALCdevice *device); +void InitEffect(ALeffect *effect); +void ReleaseALEffects(ALCdevice *device); -ALvoid LoadReverbPreset(const char *name, ALeffect *effect); +void LoadReverbPreset(const char *name, ALeffect *effect); #ifdef __cplusplus } diff --git a/Engine/lib/openal-soft/OpenAL32/Include/alError.h b/Engine/lib/openal-soft/OpenAL32/Include/alError.h index ab91d27b2..858f81de5 100644 --- a/Engine/lib/openal-soft/OpenAL32/Include/alError.h +++ b/Engine/lib/openal-soft/OpenAL32/Include/alError.h @@ -2,6 +2,7 @@ #define _AL_ERROR_H_ #include "alMain.h" +#include "logging.h" #ifdef __cplusplus extern "C" { @@ -9,23 +10,18 @@ extern "C" { extern ALboolean TrapALError; -ALvoid alSetError(ALCcontext *Context, ALenum errorCode); +void alSetError(ALCcontext *context, ALenum errorCode, const char *msg, ...) DECL_FORMAT(printf, 3, 4); -#define SET_ERROR_AND_RETURN(ctx, err) do { \ - alSetError((ctx), (err)); \ - return; \ -} while(0) - -#define SET_ERROR_AND_RETURN_VALUE(ctx, err, val) do { \ - alSetError((ctx), (err)); \ - return (val); \ -} while(0) - -#define SET_ERROR_AND_GOTO(ctx, err, lbl) do { \ - alSetError((ctx), (err)); \ +#define SETERR_GOTO(ctx, err, lbl, ...) do { \ + alSetError((ctx), (err), __VA_ARGS__); \ goto lbl; \ } while(0) +#define SETERR_RETURN(ctx, err, retval, ...) do { \ + alSetError((ctx), (err), __VA_ARGS__); \ + return retval; \ +} while(0) + #ifdef __cplusplus } #endif diff --git a/Engine/lib/openal-soft/OpenAL32/Include/alFilter.h b/Engine/lib/openal-soft/OpenAL32/Include/alFilter.h index 1f7095bc6..2634d5e80 100644 --- a/Engine/lib/openal-soft/OpenAL32/Include/alFilter.h +++ b/Engine/lib/openal-soft/OpenAL32/Include/alFilter.h @@ -1,9 +1,8 @@ #ifndef _AL_FILTER_H_ #define _AL_FILTER_H_ -#include "alMain.h" - -#include "math_defs.h" +#include "AL/alc.h" +#include "AL/al.h" #ifdef __cplusplus extern "C" { @@ -13,90 +12,27 @@ extern "C" { #define HIGHPASSFREQREF (250.0f) -/* Filters implementation is based on the "Cookbook formulae for audio - * EQ biquad filter coefficients" by Robert Bristow-Johnson - * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt - */ -/* 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). - */ +struct ALfilter; -typedef enum ALfilterType { - /** EFX-style low-pass filter, specifying a gain and reference frequency. */ - ALfilterType_HighShelf, - /** EFX-style high-pass filter, specifying a gain and reference frequency. */ - ALfilterType_LowShelf, - /** Peaking filter, specifying a gain and reference frequency. */ - ALfilterType_Peaking, +typedef struct ALfilterVtable { + void (*const setParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint val); + void (*const setParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALint *vals); + void (*const setParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val); + void (*const setParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals); - /** Low-pass cut-off filter, specifying a cut-off frequency. */ - ALfilterType_LowPass, - /** High-pass cut-off filter, specifying a cut-off frequency. */ - ALfilterType_HighPass, - /** Band-pass filter, specifying a center frequency. */ - ALfilterType_BandPass, -} ALfilterType; + void (*const getParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *val); + void (*const getParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *vals); + void (*const getParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val); + void (*const getParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals); +} ALfilterVtable; -typedef struct ALfilterState { - ALfloat x[2]; /* History of two last input samples */ - ALfloat y[2]; /* History of two last output samples */ - ALfloat a1, a2; /* Transfer function coefficients "a" (a0 is pre-applied) */ - ALfloat b0, b1, b2; /* Transfer function coefficients "b" */ -} ALfilterState; -/* Currently only a C-based filter process method is implemented. */ -#define ALfilterState_process ALfilterState_processC - -/* Calculates the rcpQ (i.e. 1/Q) coefficient for shelving filters, using the - * reference gain and shelf slope parameter. - * 0 < gain - * 0 < slope <= 1 - */ -inline ALfloat calc_rcpQ_from_slope(ALfloat gain, ALfloat slope) -{ - return sqrtf((gain + 1.0f/gain)*(1.0f/slope - 1.0f) + 2.0f); +#define DEFINE_ALFILTER_VTABLE(T) \ +const struct ALfilterVtable T##_vtable = { \ + T##_setParami, T##_setParamiv, \ + T##_setParamf, T##_setParamfv, \ + T##_getParami, T##_getParamiv, \ + T##_getParamf, T##_getParamfv, \ } -/* Calculates the rcpQ (i.e. 1/Q) coefficient for filters, using the frequency - * multiple (i.e. ref_freq / sampling_freq) and bandwidth. - * 0 < freq_mult < 0.5. - */ -inline ALfloat calc_rcpQ_from_bandwidth(ALfloat freq_mult, ALfloat bandwidth) -{ - ALfloat w0 = F_TAU * freq_mult; - return 2.0f*sinhf(logf(2.0f)/2.0f*bandwidth*w0/sinf(w0)); -} - -inline void ALfilterState_clear(ALfilterState *filter) -{ - filter->x[0] = 0.0f; - filter->x[1] = 0.0f; - filter->y[0] = 0.0f; - filter->y[1] = 0.0f; -} - -void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat rcpQ); - -void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALuint numsamples); - -inline void ALfilterState_processPassthru(ALfilterState *filter, const ALfloat *restrict src, ALuint numsamples) -{ - if(numsamples >= 2) - { - filter->x[1] = src[numsamples-2]; - filter->x[0] = src[numsamples-1]; - filter->y[1] = src[numsamples-2]; - filter->y[0] = src[numsamples-1]; - } - else if(numsamples == 1) - { - filter->x[1] = filter->x[0]; - filter->x[0] = src[0]; - filter->y[1] = filter->y[0]; - filter->y[0] = src[0]; - } -} - typedef struct ALfilter { // Filter type (AL_FILTER_NULL, ...) @@ -108,45 +44,21 @@ typedef struct ALfilter { ALfloat GainLF; ALfloat LFReference; - void (*SetParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint val); - void (*SetParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALint *vals); - void (*SetParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val); - void (*SetParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals); - - void (*GetParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *val); - void (*GetParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *vals); - void (*GetParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val); - void (*GetParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals); + const struct ALfilterVtable *vtab; /* Self ID */ ALuint id; } ALfilter; +#define ALfilter_setParami(o, c, p, v) ((o)->vtab->setParami(o, c, p, v)) +#define ALfilter_setParamf(o, c, p, v) ((o)->vtab->setParamf(o, c, p, v)) +#define ALfilter_setParamiv(o, c, p, v) ((o)->vtab->setParamiv(o, c, p, v)) +#define ALfilter_setParamfv(o, c, p, v) ((o)->vtab->setParamfv(o, c, p, v)) +#define ALfilter_getParami(o, c, p, v) ((o)->vtab->getParami(o, c, p, v)) +#define ALfilter_getParamf(o, c, p, v) ((o)->vtab->getParamf(o, c, p, v)) +#define ALfilter_getParamiv(o, c, p, v) ((o)->vtab->getParamiv(o, c, p, v)) +#define ALfilter_getParamfv(o, c, p, v) ((o)->vtab->getParamfv(o, c, p, v)) -#define ALfilter_SetParami(x, c, p, v) ((x)->SetParami((x),(c),(p),(v))) -#define ALfilter_SetParamiv(x, c, p, v) ((x)->SetParamiv((x),(c),(p),(v))) -#define ALfilter_SetParamf(x, c, p, v) ((x)->SetParamf((x),(c),(p),(v))) -#define ALfilter_SetParamfv(x, c, p, v) ((x)->SetParamfv((x),(c),(p),(v))) - -#define ALfilter_GetParami(x, c, p, v) ((x)->GetParami((x),(c),(p),(v))) -#define ALfilter_GetParamiv(x, c, p, v) ((x)->GetParamiv((x),(c),(p),(v))) -#define ALfilter_GetParamf(x, c, p, v) ((x)->GetParamf((x),(c),(p),(v))) -#define ALfilter_GetParamfv(x, c, p, v) ((x)->GetParamfv((x),(c),(p),(v))) - -inline void LockFiltersRead(ALCdevice *device) -{ LockUIntMapRead(&device->FilterMap); } -inline void UnlockFiltersRead(ALCdevice *device) -{ UnlockUIntMapRead(&device->FilterMap); } -inline void LockFiltersWrite(ALCdevice *device) -{ LockUIntMapWrite(&device->FilterMap); } -inline void UnlockFiltersWrite(ALCdevice *device) -{ UnlockUIntMapWrite(&device->FilterMap); } - -inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id) -{ return (struct ALfilter*)LookupUIntMapKeyNoLock(&device->FilterMap, id); } -inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id) -{ return (struct ALfilter*)RemoveUIntMapKeyNoLock(&device->FilterMap, id); } - -ALvoid ReleaseALFilters(ALCdevice *device); +void ReleaseALFilters(ALCdevice *device); #ifdef __cplusplus } diff --git a/Engine/lib/openal-soft/OpenAL32/Include/alListener.h b/Engine/lib/openal-soft/OpenAL32/Include/alListener.h index b89a00e7a..0d80a8d72 100644 --- a/Engine/lib/openal-soft/OpenAL32/Include/alListener.h +++ b/Engine/lib/openal-soft/OpenAL32/Include/alListener.h @@ -8,40 +8,40 @@ extern "C" { #endif -struct ALlistenerProps { - ATOMIC(ALfloat) Position[3]; - ATOMIC(ALfloat) Velocity[3]; - ATOMIC(ALfloat) Forward[3]; - ATOMIC(ALfloat) Up[3]; - ATOMIC(ALfloat) Gain; - ATOMIC(ALfloat) MetersPerUnit; +struct ALcontextProps { + ALfloat DopplerFactor; + ALfloat DopplerVelocity; + ALfloat SpeedOfSound; + ALboolean SourceDistanceModel; + enum DistanceModel DistanceModel; + ALfloat MetersPerUnit; - ATOMIC(ALfloat) DopplerFactor; - ATOMIC(ALfloat) DopplerVelocity; - ATOMIC(ALfloat) SpeedOfSound; - ATOMIC(ALboolean) SourceDistanceModel; - ATOMIC(enum DistanceModel) DistanceModel; + ATOMIC(struct ALcontextProps*) next; +}; + +struct ALlistenerProps { + ALfloat Position[3]; + ALfloat Velocity[3]; + ALfloat Forward[3]; + ALfloat Up[3]; + ALfloat Gain; ATOMIC(struct ALlistenerProps*) next; }; typedef struct ALlistener { - volatile ALfloat Position[3]; - volatile ALfloat Velocity[3]; - volatile ALfloat Forward[3]; - volatile ALfloat Up[3]; - volatile ALfloat Gain; - volatile ALfloat MetersPerUnit; + alignas(16) ALfloat Position[3]; + ALfloat Velocity[3]; + ALfloat Forward[3]; + ALfloat Up[3]; + ALfloat Gain; + + ATOMIC_FLAG PropsClean; /* Pointer to the most recent property values that are awaiting an update. */ ATOMIC(struct ALlistenerProps*) Update; - /* A linked list of unused property containers, free to use for future - * updates. - */ - ATOMIC(struct ALlistenerProps*) FreeList; - struct { aluMatrixf Matrix; aluVector Velocity; @@ -50,7 +50,8 @@ typedef struct ALlistener { ALfloat MetersPerUnit; ALfloat DopplerFactor; - ALfloat SpeedOfSound; + ALfloat SpeedOfSound; /* in units per sec! */ + ALfloat ReverbSpeedOfSound; /* in meters per sec! */ ALboolean SourceDistanceModel; enum DistanceModel DistanceModel; diff --git a/Engine/lib/openal-soft/OpenAL32/Include/alMain.h b/Engine/lib/openal-soft/OpenAL32/Include/alMain.h index 837e1082b..e29d9c270 100644 --- a/Engine/lib/openal-soft/OpenAL32/Include/alMain.h +++ b/Engine/lib/openal-soft/OpenAL32/Include/alMain.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -11,15 +12,25 @@ #ifdef HAVE_STRINGS_H #include #endif - -#ifdef HAVE_FENV_H -#include +#ifdef HAVE_INTRIN_H +#include #endif #include "AL/al.h" #include "AL/alc.h" #include "AL/alext.h" +#include "inprogext.h" +#include "logging.h" +#include "polymorphism.h" +#include "static_assert.h" +#include "align.h" +#include "atomic.h" +#include "vector.h" +#include "alstring.h" +#include "almalloc.h" +#include "threads.h" + #if defined(_WIN64) #define SZFMT "%I64u" @@ -29,157 +40,38 @@ #define SZFMT "%zu" #endif - -#include "static_assert.h" -#include "align.h" -#include "atomic.h" -#include "uintmap.h" -#include "vector.h" -#include "alstring.h" -#include "almalloc.h" -#include "threads.h" - -#include "hrtf.h" - -#ifndef ALC_SOFT_device_clock -#define ALC_SOFT_device_clock 1 -typedef int64_t ALCint64SOFT; -typedef uint64_t ALCuint64SOFT; -#define ALC_DEVICE_CLOCK_SOFT 0x1600 -#define ALC_DEVICE_LATENCY_SOFT 0x1601 -#define ALC_DEVICE_CLOCK_LATENCY_SOFT 0x1602 -typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values); -#ifdef AL_ALEXT_PROTOTYPES -ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values); +#ifdef __has_builtin +#define HAS_BUILTIN __has_builtin +#else +#define HAS_BUILTIN(x) (0) #endif -#endif - -#ifndef AL_SOFT_buffer_samples2 -#define AL_SOFT_buffer_samples2 1 -/* Channel configurations */ -#define AL_MONO_SOFT 0x1500 -#define AL_STEREO_SOFT 0x1501 -#define AL_REAR_SOFT 0x1502 -#define AL_QUAD_SOFT 0x1503 -#define AL_5POINT1_SOFT 0x1504 -#define AL_6POINT1_SOFT 0x1505 -#define AL_7POINT1_SOFT 0x1506 -#define AL_BFORMAT2D_SOFT 0x1507 -#define AL_BFORMAT3D_SOFT 0x1508 - -/* Sample types */ -#define AL_BYTE_SOFT 0x1400 -#define AL_UNSIGNED_BYTE_SOFT 0x1401 -#define AL_SHORT_SOFT 0x1402 -#define AL_UNSIGNED_SHORT_SOFT 0x1403 -#define AL_INT_SOFT 0x1404 -#define AL_UNSIGNED_INT_SOFT 0x1405 -#define AL_FLOAT_SOFT 0x1406 -#define AL_DOUBLE_SOFT 0x1407 -#define AL_BYTE3_SOFT 0x1408 -#define AL_UNSIGNED_BYTE3_SOFT 0x1409 -#define AL_MULAW_SOFT 0x140A - -/* Storage formats */ -#define AL_MONO8_SOFT 0x1100 -#define AL_MONO16_SOFT 0x1101 -#define AL_MONO32F_SOFT 0x10010 -#define AL_STEREO8_SOFT 0x1102 -#define AL_STEREO16_SOFT 0x1103 -#define AL_STEREO32F_SOFT 0x10011 -#define AL_QUAD8_SOFT 0x1204 -#define AL_QUAD16_SOFT 0x1205 -#define AL_QUAD32F_SOFT 0x1206 -#define AL_REAR8_SOFT 0x1207 -#define AL_REAR16_SOFT 0x1208 -#define AL_REAR32F_SOFT 0x1209 -#define AL_5POINT1_8_SOFT 0x120A -#define AL_5POINT1_16_SOFT 0x120B -#define AL_5POINT1_32F_SOFT 0x120C -#define AL_6POINT1_8_SOFT 0x120D -#define AL_6POINT1_16_SOFT 0x120E -#define AL_6POINT1_32F_SOFT 0x120F -#define AL_7POINT1_8_SOFT 0x1210 -#define AL_7POINT1_16_SOFT 0x1211 -#define AL_7POINT1_32F_SOFT 0x1212 -#define AL_BFORMAT2D_8_SOFT 0x20021 -#define AL_BFORMAT2D_16_SOFT 0x20022 -#define AL_BFORMAT2D_32F_SOFT 0x20023 -#define AL_BFORMAT3D_8_SOFT 0x20031 -#define AL_BFORMAT3D_16_SOFT 0x20032 -#define AL_BFORMAT3D_32F_SOFT 0x20033 - -/* Buffer attributes */ -#define AL_INTERNAL_FORMAT_SOFT 0x2008 -#define AL_BYTE_LENGTH_SOFT 0x2009 -#define AL_SAMPLE_LENGTH_SOFT 0x200A -#define AL_SEC_LENGTH_SOFT 0x200B - -#if 0 -typedef void (AL_APIENTRY*LPALBUFFERSAMPLESSOFT)(ALuint,ALuint,ALenum,ALsizei,ALenum,ALenum,const ALvoid*); -typedef void (AL_APIENTRY*LPALGETBUFFERSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,ALvoid*); -typedef ALboolean (AL_APIENTRY*LPALISBUFFERFORMATSUPPORTEDSOFT)(ALenum); -#ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, ALuint samplerate, ALenum internalformat, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data); -AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, ALvoid *data); -AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format); -#endif -#endif -#endif - #ifdef __GNUC__ -/* Because of a long-standing deficiency in C, you're not allowed to implicitly - * cast a pointer-to-type-array to a pointer-to-const-type-array. For example, - * - * int (*ptr)[10]; - * const int (*cptr)[10] = ptr; - * - * is not allowed and most compilers will generate noisy warnings about - * incompatible types, even though it just makes the array elements const. - * Clang will allow it if you make the array type a typedef, like this: - * - * typedef int int10[10]; - * int10 *ptr; - * const int10 *cptr = ptr; - * - * however GCC does not and still issues the incompatible type warning. The - * "proper" way to fix it is to add an explicit cast for the constified type, - * but that removes the vast majority of otherwise useful type-checking you'd - * get, and runs the risk of improper casts if types are later changed. Leaving - * it non-const can also be an issue if you use it as a function parameter, and - * happen to have a const type as input (and also reduce the capabilities of - * the compiler to better optimize the function). - * - * So to work around the problem, we use a macro. The macro first assigns the - * incoming variable to the specified non-const type to ensure it's the correct - * type, then casts the variable as the desired constified type. Very ugly, but - * I'd rather not have hundreds of lines of warnings because I want to tell the - * compiler that some array(s) can't be changed by the code, or have lots of - * error-prone casts. +/* LIKELY optimizes the case where the condition is true. The condition is not + * required to be true, but it can result in more optimal code for the true + * path at the expense of a less optimal false path. */ -#define SAFE_CONST(T, var) __extension__({ \ - T _tmp = (var); \ - (const T)_tmp; \ -}) +#define LIKELY(x) __builtin_expect(!!(x), !0) +/* The opposite of LIKELY, optimizing the case where the condition is false. */ +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +/* Unlike LIKELY, ASSUME requires the condition to be true or else it invokes + * undefined behavior. It's essentially an assert without actually checking the + * condition at run-time, allowing for stronger optimizations than LIKELY. + */ +#if HAS_BUILTIN(__builtin_assume) +#define ASSUME __builtin_assume #else -/* Non-GNU-compatible compilers have to use a straight cast with no extra - * checks, due to the lack of multi-statement expressions. - */ -#define SAFE_CONST(T, var) ((const T)(var)) +#define ASSUME(x) do { if(!(x)) __builtin_unreachable(); } while(0) #endif +#else -typedef ALint64SOFT ALint64; -typedef ALuint64SOFT ALuint64; - -#ifndef U64 -#if defined(_MSC_VER) -#define U64(x) ((ALuint64)(x##ui64)) -#elif SIZEOF_LONG == 8 -#define U64(x) ((ALuint64)(x##ul)) -#elif SIZEOF_LONG_LONG == 8 -#define U64(x) ((ALuint64)(x##ull)) +#define LIKELY(x) (!!(x)) +#define UNLIKELY(x) (!!(x)) +#ifdef _MSC_VER +#define ASSUME __assume +#else +#define ASSUME(x) ((void)0) #endif #endif @@ -199,36 +91,88 @@ typedef ALuint64SOFT ALuint64; #endif #endif +/* Calculates the size of a struct with N elements of a flexible array member. + * GCC and Clang allow offsetof(Type, fam[N]) for this, but MSVC seems to have + * trouble, so a bit more verbose workaround is needed. + */ +#define FAM_SIZE(T, M, N) (offsetof(T, M) + sizeof(((T*)NULL)->M[0])*(N)) + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef ALint64SOFT ALint64; +typedef ALuint64SOFT ALuint64; + +#ifndef U64 +#if defined(_MSC_VER) +#define U64(x) ((ALuint64)(x##ui64)) +#elif SIZEOF_LONG == 8 +#define U64(x) ((ALuint64)(x##ul)) +#elif SIZEOF_LONG_LONG == 8 +#define U64(x) ((ALuint64)(x##ull)) +#endif +#endif + +/* Define a CTZ64 macro (count trailing zeros, for 64-bit integers). The result + * is *UNDEFINED* if the value is 0. + */ #ifdef __GNUC__ -#define DECL_FORMAT(x, y, z) __attribute__((format(x, (y), (z)))) + +#if SIZEOF_LONG == 8 +#define CTZ64(x) __builtin_ctzl(x) #else -#define DECL_FORMAT(x, y, z) +#define CTZ64(x) __builtin_ctzll(x) #endif -#if defined(__GNUC__) && defined(__i386__) -/* force_align_arg_pointer is required for proper function arguments aligning - * when SSE code is used. Some systems (Windows, QNX) do not guarantee our - * thread functions will be properly aligned on the stack, even though GCC may - * generate code with the assumption that it is. */ -#define FORCE_ALIGN __attribute__((force_align_arg_pointer)) -#else -#define FORCE_ALIGN -#endif +#elif defined(HAVE_BITSCANFORWARD64_INTRINSIC) -#ifdef HAVE_C99_VLA -#define DECL_VLA(T, _name, _size) T _name[(_size)] -#else -#define DECL_VLA(T, _name, _size) T *_name = alloca((_size) * sizeof(T)) -#endif +inline int msvc64_ctz64(ALuint64 v) +{ + unsigned long idx = 64; + _BitScanForward64(&idx, v); + return (int)idx; +} +#define CTZ64(x) msvc64_ctz64(x) -#ifndef PATH_MAX -#ifdef MAX_PATH -#define PATH_MAX MAX_PATH -#else -#define PATH_MAX 4096 -#endif -#endif +#elif defined(HAVE_BITSCANFORWARD_INTRINSIC) +inline int msvc_ctz64(ALuint64 v) +{ + unsigned long idx = 64; + if(!_BitScanForward(&idx, v&0xffffffff)) + { + if(_BitScanForward(&idx, v>>32)) + idx += 32; + } + return (int)idx; +} +#define CTZ64(x) msvc_ctz64(x) + +#else + +/* There be black magics here. The popcnt64 method is derived from + * https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + * while the ctz-utilizing-popcnt algorithm is shown here + * http://www.hackersdelight.org/hdcodetxt/ntz.c.txt + * as the ntz2 variant. These likely aren't the most efficient methods, but + * they're good enough if the GCC or MSVC intrinsics aren't available. + */ +inline int fallback_popcnt64(ALuint64 v) +{ + v = v - ((v >> 1) & U64(0x5555555555555555)); + v = (v & U64(0x3333333333333333)) + ((v >> 2) & U64(0x3333333333333333)); + v = (v + (v >> 4)) & U64(0x0f0f0f0f0f0f0f0f); + return (int)((v * U64(0x0101010101010101)) >> 56); +} + +inline int fallback_ctz64(ALuint64 value) +{ + return fallback_popcnt64(~value & (value - 1)); +} +#define CTZ64(x) fallback_ctz64(x) +#endif static const union { ALuint u; @@ -236,108 +180,24 @@ static const union { } EndianTest = { 1 }; #define IS_LITTLE_ENDIAN (EndianTest.b[0] == 1) -#define COUNTOF(x) (sizeof((x))/sizeof((x)[0])) +#define COUNTOF(x) (sizeof(x) / sizeof(0[x])) -#define DERIVE_FROM_TYPE(t) t t##_parent -#define STATIC_CAST(to, obj) (&(obj)->to##_parent) -#ifdef __GNUC__ -#define STATIC_UPCAST(to, from, obj) __extension__({ \ - static_assert(__builtin_types_compatible_p(from, __typeof(*(obj))), \ - "Invalid upcast object from type"); \ - (to*)((char*)(obj) - offsetof(to, from##_parent)); \ -}) -#else -#define STATIC_UPCAST(to, from, obj) ((to*)((char*)(obj) - offsetof(to, from##_parent))) -#endif - -#define DECLARE_FORWARD(T1, T2, rettype, func) \ -rettype T1##_##func(T1 *obj) \ -{ return T2##_##func(STATIC_CAST(T2, obj)); } - -#define DECLARE_FORWARD1(T1, T2, rettype, func, argtype1) \ -rettype T1##_##func(T1 *obj, argtype1 a) \ -{ return T2##_##func(STATIC_CAST(T2, obj), a); } - -#define DECLARE_FORWARD2(T1, T2, rettype, func, argtype1, argtype2) \ -rettype T1##_##func(T1 *obj, argtype1 a, argtype2 b) \ -{ return T2##_##func(STATIC_CAST(T2, obj), a, b); } - -#define DECLARE_FORWARD3(T1, T2, rettype, func, argtype1, argtype2, argtype3) \ -rettype T1##_##func(T1 *obj, argtype1 a, argtype2 b, argtype3 c) \ -{ return T2##_##func(STATIC_CAST(T2, obj), a, b, c); } - - -#define GET_VTABLE1(T1) (&(T1##_vtable)) -#define GET_VTABLE2(T1, T2) (&(T1##_##T2##_vtable)) - -#define SET_VTABLE1(T1, obj) ((obj)->vtbl = GET_VTABLE1(T1)) -#define SET_VTABLE2(T1, T2, obj) (STATIC_CAST(T2, obj)->vtbl = GET_VTABLE2(T1, T2)) - -#define DECLARE_THUNK(T1, T2, rettype, func) \ -static rettype T1##_##T2##_##func(T2 *obj) \ -{ return T1##_##func(STATIC_UPCAST(T1, T2, obj)); } - -#define DECLARE_THUNK1(T1, T2, rettype, func, argtype1) \ -static rettype T1##_##T2##_##func(T2 *obj, argtype1 a) \ -{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a); } - -#define DECLARE_THUNK2(T1, T2, rettype, func, argtype1, argtype2) \ -static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b) \ -{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b); } - -#define DECLARE_THUNK3(T1, T2, rettype, func, argtype1, argtype2, argtype3) \ -static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b, argtype3 c) \ -{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b, c); } - -#define DECLARE_THUNK4(T1, T2, rettype, func, argtype1, argtype2, argtype3, argtype4) \ -static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b, argtype3 c, argtype4 d) \ -{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b, c, d); } - -#define DECLARE_DEFAULT_ALLOCATORS(T) \ -static void* T##_New(size_t size) { return al_malloc(16, size); } \ -static void T##_Delete(void *ptr) { al_free(ptr); } - -/* Helper to extract an argument list for VCALL. Not used directly. */ -#define EXTRACT_VCALL_ARGS(...) __VA_ARGS__)) - -/* Call a "virtual" method on an object, with arguments. */ -#define V(obj, func) ((obj)->vtbl->func((obj), EXTRACT_VCALL_ARGS -/* Call a "virtual" method on an object, with no arguments. */ -#define V0(obj, func) ((obj)->vtbl->func((obj) EXTRACT_VCALL_ARGS - -#define DELETE_OBJ(obj) do { \ - if((obj) != NULL) \ - { \ - V0((obj),Destruct)(); \ - V0((obj),Delete)(); \ - } \ -} while(0) - - -#define EXTRACT_NEW_ARGS(...) __VA_ARGS__); \ - } \ -} while(0) - -#define NEW_OBJ(_res, T) do { \ - _res = T##_New(sizeof(T)); \ - if(_res) \ - { \ - memset(_res, 0, sizeof(T)); \ - T##_Construct(_res, EXTRACT_NEW_ARGS -#define NEW_OBJ0(_res, T) do { \ - _res = T##_New(sizeof(T)); \ - if(_res) \ - { \ - memset(_res, 0, sizeof(T)); \ - T##_Construct(_res EXTRACT_NEW_ARGS - - -#ifdef __cplusplus -extern "C" { -#endif - +struct ll_ringbuffer; struct Hrtf; +struct HrtfEntry; +struct DirectHrtfState; +struct FrontStablizer; +struct Compressor; +struct ALCbackend; +struct ALbuffer; +struct ALeffect; +struct ALfilter; +struct ALsource; +struct ALcontextProps; +struct ALlistenerProps; +struct ALvoiceProps; +struct ALeffectslotProps; #define DEFAULT_OUTPUT_RATE (44100) @@ -359,26 +219,61 @@ inline ALuint NextPowerOf2(ALuint value) return value+1; } -/* Fast float-to-int conversion. Assumes the FPU is already in round-to-zero - * mode. */ +/** Round up a value to the next multiple. */ +inline size_t RoundUp(size_t value, size_t r) +{ + value += r-1; + return value - (value%r); +} + +/* Fast float-to-int conversion. No particular rounding mode is assumed; the + * IEEE-754 default is round-to-nearest with ties-to-even, though an app could + * change it on its own threads. On some systems, a truncating conversion may + * always be the fastest method. + */ inline ALint fastf2i(ALfloat f) { -#ifdef HAVE_LRINTF - return lrintf(f); -#elif defined(_MSC_VER) && defined(_M_IX86) +#if defined(HAVE_INTRIN_H) && ((defined(_M_IX86_FP) && (_M_IX86_FP > 0)) || defined(_M_X64)) + return _mm_cvt_ss2si(_mm_set1_ps(f)); + +#elif defined(_MSC_VER) && defined(_M_IX86_FP) + ALint i; __asm fld f __asm fistp i return i; + +#elif (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) + + ALint i; +#ifdef __SSE_MATH__ + __asm__("cvtss2si %1, %0" : "=r"(i) : "x"(f)); #else + __asm__("flds %1\n fistps %0" : "=m"(i) : "m"(f)); +#endif + return i; + + /* On GCC when compiling with -fno-math-errno, lrintf can be inlined to + * some simple instructions. Clang does not inline it, always generating a + * libc call, while MSVC's implementation is horribly slow, so always fall + * back to a normal integer conversion for them. + */ +#elif defined(HAVE_LRINTF) && !defined(_MSC_VER) && !defined(__clang__) + + return lrintf(f); + +#else + return (ALint)f; #endif } -/* Fast float-to-uint conversion. Assumes the FPU is already in round-to-zero - * mode. */ -inline ALuint fastf2u(ALfloat f) -{ return fastf2i(f); } +/* Converts float-to-int using standard behavior (truncation). */ +inline int float2int(float f) +{ + /* TODO: Make a more efficient method for x87. */ + return (ALint)f; +} enum DevProbe { @@ -386,36 +281,6 @@ enum DevProbe { CAPTURE_DEVICE_PROBE }; -typedef struct { - ALCenum (*OpenPlayback)(ALCdevice*, const ALCchar*); - void (*ClosePlayback)(ALCdevice*); - ALCboolean (*ResetPlayback)(ALCdevice*); - ALCboolean (*StartPlayback)(ALCdevice*); - void (*StopPlayback)(ALCdevice*); - - ALCenum (*OpenCapture)(ALCdevice*, const ALCchar*); - void (*CloseCapture)(ALCdevice*); - void (*StartCapture)(ALCdevice*); - void (*StopCapture)(ALCdevice*); - ALCenum (*CaptureSamples)(ALCdevice*, void*, ALCuint); - ALCuint (*AvailableSamples)(ALCdevice*); -} BackendFuncs; - -ALCboolean alc_sndio_init(BackendFuncs *func_list); -void alc_sndio_deinit(void); -void alc_sndio_probe(enum DevProbe type); -ALCboolean alc_ca_init(BackendFuncs *func_list); -void alc_ca_deinit(void); -void alc_ca_probe(enum DevProbe type); -ALCboolean alc_opensl_init(BackendFuncs *func_list); -void alc_opensl_deinit(void); -void alc_opensl_probe(enum DevProbe type); -ALCboolean alc_qsa_init(BackendFuncs *func_list); -void alc_qsa_deinit(void); -void alc_qsa_probe(enum DevProbe type); - -struct ALCbackend; - enum DistanceModel { InverseDistanceClamped = AL_INVERSE_DISTANCE_CLAMPED, @@ -489,41 +354,36 @@ enum DevFmtChannels { 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 = 0x80000000, - /* Ambisonic formats should be kept together */ - DevFmtAmbi1, - DevFmtAmbi2, - DevFmtAmbi3, - DevFmtChannelsDefault = DevFmtStereo }; #define MAX_OUTPUT_CHANNELS (16) -ALuint BytesFromDevFmt(enum DevFmtType type); -ALuint ChannelsFromDevFmt(enum DevFmtChannels chans); -inline ALuint FrameSizeFromDevFmt(enum DevFmtChannels chans, enum DevFmtType type) +ALsizei BytesFromDevFmt(enum DevFmtType type); +ALsizei ChannelsFromDevFmt(enum DevFmtChannels chans, ALsizei ambiorder); +inline ALsizei FrameSizeFromDevFmt(enum DevFmtChannels chans, enum DevFmtType type, ALsizei ambiorder) { - return ChannelsFromDevFmt(chans) * BytesFromDevFmt(type); + return ChannelsFromDevFmt(chans, ambiorder) * BytesFromDevFmt(type); } -enum AmbiFormat { - AmbiFormat_FuMa, /* FuMa channel order and normalization */ - AmbiFormat_ACN_SN3D, /* ACN channel order and SN3D normalization */ - AmbiFormat_ACN_N3D, /* ACN channel order and N3D normalization */ +enum AmbiLayout { + AmbiLayout_FuMa = ALC_FUMA_SOFT, /* FuMa channel order */ + AmbiLayout_ACN = ALC_ACN_SOFT, /* ACN channel order */ - AmbiFormat_Default = AmbiFormat_ACN_SN3D + AmbiLayout_Default = AmbiLayout_ACN }; +enum AmbiNorm { + AmbiNorm_FuMa = ALC_FUMA_SOFT, /* FuMa normalization */ + AmbiNorm_SN3D = ALC_SN3D_SOFT, /* SN3D normalization */ + AmbiNorm_N3D = ALC_N3D_SOFT, /* N3D normalization */ -extern const struct EffectList { - const char *name; - int type; - const char *ename; - ALenum val; -} EffectList[]; + AmbiNorm_Default = AmbiNorm_SN3D +}; enum DeviceType { @@ -565,7 +425,7 @@ enum RenderMode { typedef ALfloat ChannelConfig[MAX_AMBI_COEFFS]; typedef struct BFChannelConfig { ALfloat Scale; - ALuint Index; + ALsizei Index; } BFChannelConfig; typedef union AmbiConfig { @@ -576,33 +436,96 @@ typedef union AmbiConfig { } AmbiConfig; -#define HRTF_HISTORY_BITS (6) -#define HRTF_HISTORY_LENGTH (1<Device); } - -inline void UnlockContext(ALCcontext *context) -{ ALCdevice_Unlock(context->Device); } - -enum { - DeferOff = AL_FALSE, - DeferAll, - DeferAllowPlay -}; - - -typedef struct { -#ifdef HAVE_FENV_H - DERIVE_FROM_TYPE(fenv_t); -#else - int state; -#endif -#ifdef HAVE_SSE - int sse_state; -#endif -} FPUCtl; -void SetMixerFPUMode(FPUCtl *ctl); -void RestoreFPUMode(const FPUCtl *ctl); - - -typedef struct ll_ringbuffer ll_ringbuffer_t; -typedef struct ll_ringbuffer_data { - char *buf; - size_t len; -} ll_ringbuffer_data_t; -ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz); -void ll_ringbuffer_free(ll_ringbuffer_t *rb); -void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t *vec); -void ll_ringbuffer_get_write_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t *vec); -size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt); -size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt); -void ll_ringbuffer_read_advance(ll_ringbuffer_t *rb, size_t cnt); -size_t ll_ringbuffer_read_space(const ll_ringbuffer_t *rb); -int ll_ringbuffer_mlock(ll_ringbuffer_t *rb); -void ll_ringbuffer_reset(ll_ringbuffer_t *rb); -size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt); -void ll_ringbuffer_write_advance(ll_ringbuffer_t *rb, size_t cnt); -size_t ll_ringbuffer_write_space(const ll_ringbuffer_t *rb); - -void ReadALConfig(void); -void FreeALConfig(void); -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); -int ConfigValueStr(const char *devName, const char *blockName, const char *keyName, const char **ret); -int ConfigValueInt(const char *devName, const char *blockName, const char *keyName, int *ret); -int ConfigValueUInt(const char *devName, const char *blockName, const char *keyName, unsigned int *ret); -int ConfigValueFloat(const char *devName, const char *blockName, const char *keyName, float *ret); -int ConfigValueBool(const char *devName, const char *blockName, const char *keyName, int *ret); +extern ALint RTPrioLevel; void SetRTPriority(void); void SetDefaultChannelOrder(ALCdevice *device); @@ -882,12 +778,6 @@ void SetDefaultWFXChannelOrder(ALCdevice *device); const ALCchar *DevFmtTypeString(enum DevFmtType type); const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans); -/** - * GetChannelIdxByName - * - * Returns the index for the given channel name (e.g. FrontCenter), or -1 if it - * doesn't exist. - */ inline ALint GetChannelIndex(const enum Channel names[MAX_OUTPUT_CHANNELS], enum Channel chan) { ALint i; @@ -898,68 +788,33 @@ inline ALint GetChannelIndex(const enum Channel names[MAX_OUTPUT_CHANNELS], enum } return -1; } -#define GetChannelIdxByName(x, c) GetChannelIndex((x).ChannelName, (c)) - -extern FILE *LogFile; - -#if defined(__GNUC__) && !defined(_WIN32) && !defined(IN_IDE_PARSER) -#define AL_PRINT(T, MSG, ...) fprintf(LogFile, "AL lib: %s %s: "MSG, T, __FUNCTION__ , ## __VA_ARGS__) -#else -void al_print(const char *type, const char *func, const char *fmt, ...) DECL_FORMAT(printf, 3,4); -#define AL_PRINT(T, ...) al_print((T), __FUNCTION__, __VA_ARGS__) -#endif - -enum LogLevel { - NoLog, - LogError, - LogWarning, - LogTrace, - LogRef -}; -extern enum LogLevel LogLevel; - -#define TRACEREF(...) do { \ - if(LogLevel >= LogRef) \ - AL_PRINT("(--)", __VA_ARGS__); \ -} while(0) - -#define TRACE(...) do { \ - if(LogLevel >= LogTrace) \ - AL_PRINT("(II)", __VA_ARGS__); \ -} while(0) - -#define WARN(...) do { \ - if(LogLevel >= LogWarning) \ - AL_PRINT("(WW)", __VA_ARGS__); \ -} while(0) - -#define ERR(...) do { \ - if(LogLevel >= LogError) \ - AL_PRINT("(EE)", __VA_ARGS__); \ -} while(0) +/** + * 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, enum Channel chan) +{ return GetChannelIndex(real->ChannelName, chan); } -extern ALint RTPrioLevel; +inline void LockBufferList(ALCdevice *device) { almtx_lock(&device->BufferLock); } +inline void UnlockBufferList(ALCdevice *device) { almtx_unlock(&device->BufferLock); } +inline void LockEffectList(ALCdevice *device) { almtx_lock(&device->EffectLock); } +inline void UnlockEffectList(ALCdevice *device) { almtx_unlock(&device->EffectLock); } -extern ALuint 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, -}; +inline void LockFilterList(ALCdevice *device) { almtx_lock(&device->FilterLock); } +inline void UnlockFilterList(ALCdevice *device) { almtx_unlock(&device->FilterLock); } + +inline void LockEffectSlotList(ALCcontext *context) +{ almtx_lock(&context->EffectSlotLock); } +inline void UnlockEffectSlotList(ALCcontext *context) +{ almtx_unlock(&context->EffectSlotLock); } -void FillCPUCaps(ALuint capfilter); vector_al_string SearchDataFiles(const char *match, const char *subdir); -/* Small hack to use a pointer-to-array type as a normal argument type. - * Shouldn't be used directly. */ -typedef ALfloat ALfloatBUFFERSIZE[BUFFERSIZE]; - - #ifdef __cplusplus } #endif diff --git a/Engine/lib/openal-soft/OpenAL32/Include/alSource.h b/Engine/lib/openal-soft/OpenAL32/Include/alSource.h index b288937a3..5f07c09d7 100644 --- a/Engine/lib/openal-soft/OpenAL32/Include/alSource.h +++ b/Engine/lib/openal-soft/OpenAL32/Include/alSource.h @@ -1,11 +1,14 @@ #ifndef _AL_SOURCE_H_ #define _AL_SOURCE_H_ -#define MAX_SENDS 4 - +#include "bool.h" #include "alMain.h" #include "alu.h" #include "hrtf.h" +#include "atomic.h" + +#define MAX_SENDS 16 +#define DEFAULT_SENDS 2 #ifdef __cplusplus extern "C" { @@ -13,104 +16,16 @@ extern "C" { struct ALbuffer; struct ALsource; -struct ALsourceProps; typedef struct ALbufferlistitem { - struct ALbuffer *buffer; - struct ALbufferlistitem *volatile next; + ATOMIC(struct ALbufferlistitem*) next; + ALsizei max_samples; + ALsizei num_buffers; + struct ALbuffer *buffers[]; } ALbufferlistitem; -struct ALsourceProps { - ATOMIC(ALfloat) Pitch; - ATOMIC(ALfloat) Gain; - ATOMIC(ALfloat) OuterGain; - ATOMIC(ALfloat) MinGain; - ATOMIC(ALfloat) MaxGain; - ATOMIC(ALfloat) InnerAngle; - ATOMIC(ALfloat) OuterAngle; - ATOMIC(ALfloat) RefDistance; - ATOMIC(ALfloat) MaxDistance; - ATOMIC(ALfloat) RollOffFactor; - ATOMIC(ALfloat) Position[3]; - ATOMIC(ALfloat) Velocity[3]; - ATOMIC(ALfloat) Direction[3]; - ATOMIC(ALfloat) Orientation[2][3]; - ATOMIC(ALboolean) HeadRelative; - ATOMIC(enum DistanceModel) DistanceModel; - ATOMIC(ALboolean) DirectChannels; - - ATOMIC(ALboolean) DryGainHFAuto; - ATOMIC(ALboolean) WetGainAuto; - ATOMIC(ALboolean) WetGainHFAuto; - ATOMIC(ALfloat) OuterGainHF; - - ATOMIC(ALfloat) AirAbsorptionFactor; - ATOMIC(ALfloat) RoomRolloffFactor; - ATOMIC(ALfloat) DopplerFactor; - - ATOMIC(ALfloat) StereoPan[2]; - - ATOMIC(ALfloat) Radius; - - /** Direct filter and auxiliary send info. */ - struct { - ATOMIC(ALfloat) Gain; - ATOMIC(ALfloat) GainHF; - ATOMIC(ALfloat) HFReference; - ATOMIC(ALfloat) GainLF; - ATOMIC(ALfloat) LFReference; - } Direct; - struct { - ATOMIC(struct ALeffectslot*) Slot; - ATOMIC(ALfloat) Gain; - ATOMIC(ALfloat) GainHF; - ATOMIC(ALfloat) HFReference; - ATOMIC(ALfloat) GainLF; - ATOMIC(ALfloat) LFReference; - } Send[MAX_SENDS]; - - ATOMIC(struct ALsourceProps*) next; -}; - - -typedef struct ALvoice { - struct ALsourceProps Props; - - struct ALsource *volatile Source; - - /** Current target parameters used for mixing. */ - ALint Step; - - /* If not 'moving', gain/coefficients are set directly without fading. */ - ALboolean Moving; - - ALboolean IsHrtf; - - ALuint Offset; /* Number of output samples mixed since starting. */ - - alignas(16) ALfloat PrevSamples[MAX_INPUT_CHANNELS][MAX_PRE_SAMPLES]; - - BsincState SincState; - - struct { - ALfloat (*Buffer)[BUFFERSIZE]; - ALuint Channels; - } DirectOut; - - struct { - ALfloat (*Buffer)[BUFFERSIZE]; - ALuint Channels; - } SendOut[MAX_SENDS]; - - struct { - DirectParams Direct; - SendParams Send[MAX_SENDS]; - } Chan[MAX_INPUT_CHANNELS]; -} ALvoice; - - typedef struct ALsource { /** Source properties. */ ALfloat Pitch; @@ -122,14 +37,17 @@ typedef struct ALsource { ALfloat OuterAngle; ALfloat RefDistance; ALfloat MaxDistance; - ALfloat RollOffFactor; + ALfloat RolloffFactor; ALfloat Position[3]; ALfloat Velocity[3]; ALfloat Direction[3]; ALfloat Orientation[2][3]; ALboolean HeadRelative; + ALboolean Looping; enum DistanceModel DistanceModel; + enum Resampler Resampler; ALboolean DirectChannels; + enum SpatializeMode Spatialize; ALboolean DryGainHFAuto; ALboolean WetGainAuto; @@ -162,7 +80,7 @@ typedef struct ALsource { ALfloat HFReference; ALfloat GainLF; ALfloat LFReference; - } Send[MAX_SENDS]; + } *Send; /** * Last user-specified offset, and the offset type (bytes, samples, or @@ -176,51 +94,22 @@ typedef struct ALsource { /** Source state (initial, playing, paused, or stopped) */ ALenum state; - ALenum new_state; - /** Source Buffer Queue info. */ - RWLock queue_lock; - ATOMIC(ALbufferlistitem*) queue; - ATOMIC(ALbufferlistitem*) current_buffer; + /** Source Buffer Queue head. */ + ALbufferlistitem *queue; - /** - * Source offset in samples, relative to the currently playing buffer, NOT - * the whole queue, and the fractional (fixed-point) offset to the next - * sample. + ATOMIC_FLAG PropsClean; + + /* Index into the context's Voices array. Lazily updated, only checked and + * reset when looking up the voice. */ - ATOMIC(ALuint) position; - ATOMIC(ALuint) position_fraction; - - ATOMIC(ALboolean) looping; - - /** Current buffer sample info. */ - ALuint NumChannels; - ALuint SampleSize; - - ATOMIC(struct ALsourceProps*) Update; - ATOMIC(struct ALsourceProps*) FreeList; + ALint VoiceIdx; /** Self ID */ ALuint id; } ALsource; -inline void LockSourcesRead(ALCcontext *context) -{ LockUIntMapRead(&context->SourceMap); } -inline void UnlockSourcesRead(ALCcontext *context) -{ UnlockUIntMapRead(&context->SourceMap); } -inline void LockSourcesWrite(ALCcontext *context) -{ LockUIntMapWrite(&context->SourceMap); } -inline void UnlockSourcesWrite(ALCcontext *context) -{ UnlockUIntMapWrite(&context->SourceMap); } - -inline struct ALsource *LookupSource(ALCcontext *context, ALuint id) -{ return (struct ALsource*)LookupUIntMapKeyNoLock(&context->SourceMap, id); } -inline struct ALsource *RemoveSource(ALCcontext *context, ALuint id) -{ return (struct ALsource*)RemoveUIntMapKeyNoLock(&context->SourceMap, id); } - void UpdateAllSourceProps(ALCcontext *context); -ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state); -ALboolean ApplyOffset(ALsource *Source); ALvoid ReleaseALSources(ALCcontext *Context); diff --git a/Engine/lib/openal-soft/OpenAL32/Include/alThunk.h b/Engine/lib/openal-soft/OpenAL32/Include/alThunk.h deleted file mode 100644 index adc77dec9..000000000 --- a/Engine/lib/openal-soft/OpenAL32/Include/alThunk.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef ALTHUNK_H -#define ALTHUNK_H - -#include "alMain.h" - -#ifdef __cplusplus -extern "C" { -#endif - -void ThunkInit(void); -void ThunkExit(void); -ALenum NewThunkEntry(ALuint *index); -void FreeThunkEntry(ALuint index); - -#ifdef __cplusplus -} -#endif - -#endif //ALTHUNK_H - diff --git a/Engine/lib/openal-soft/OpenAL32/Include/alu.h b/Engine/lib/openal-soft/OpenAL32/Include/alu.h index ae8645fa7..b977613c0 100644 --- a/Engine/lib/openal-soft/OpenAL32/Include/alu.h +++ b/Engine/lib/openal-soft/OpenAL32/Include/alu.h @@ -12,35 +12,56 @@ #include "alMain.h" #include "alBuffer.h" -#include "alFilter.h" -#include "alAuxEffectSlot.h" #include "hrtf.h" #include "align.h" #include "math_defs.h" +#include "filters/defs.h" +#include "filters/nfc.h" #define MAX_PITCH (255) -/* Maximum number of buffer samples before the current pos needed for resampling. */ -#define MAX_PRE_SAMPLES 12 - -/* Maximum number of buffer samples after the current pos needed for resampling. */ -#define MAX_POST_SAMPLES 12 +/* 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 #ifdef __cplusplus extern "C" { #endif +struct BSincTable; struct ALsource; -struct ALsourceProps; +struct ALbufferlistitem; struct ALvoice; struct ALeffectslot; -struct ALbuffer; -/* The number of distinct scale and phase intervals within the filter table. */ +#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 enum 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< b) ? b : a); } +inline size_t maxz(size_t a, size_t b) +{ return ((a > b) ? a : b); } +inline size_t clampz(size_t val, size_t min, size_t max) +{ return minz(max, maxz(min, val)); } inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu) { return val1 + (val2-val1)*mu; } -inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALuint frac) +inline ALfloat cubic(ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat mu) { - const ALfloat *k = ResampleCoeffs.FIR4[frac]; - return k[0]*val0 + k[1]*val1 + k[2]*val2 + k[3]*val3; -} -inline ALfloat resample_fir8(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat val5, ALfloat val6, ALfloat val7, ALuint frac) -{ - const ALfloat *k = ResampleCoeffs.FIR8[frac]; - return k[0]*val0 + k[1]*val1 + k[2]*val2 + k[3]*val3 + - k[4]*val4 + k[5]*val5 + k[6]*val6 + k[7]*val7; + 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; } @@ -256,11 +413,11 @@ enum HrtfRequestMode { Hrtf_Disable = 2, }; +void aluInit(void); void aluInitMixer(void); -MixerFunc SelectMixer(void); -RowMixerFunc SelectRowMixer(void); +ResamplerFunc SelectResampler(enum Resampler resampler); /* aluInitRenderer * @@ -271,6 +428,8 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf void aluInitEffectPanning(struct ALeffectslot *slot); +void aluSelectPostProcess(ALCdevice *device); + /** * CalcDirectionCoeffs * @@ -280,18 +439,6 @@ void aluInitEffectPanning(struct ALeffectslot *slot); */ void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]); -/** - * CalcXYZCoeffs - * - * Same as CalcDirectionCoeffs except the direction is specified as separate x, - * y, and z parameters instead of an array. - */ -inline void CalcXYZCoeffs(ALfloat x, ALfloat y, ALfloat z, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]) -{ - ALfloat dir[3] = { x, y, z }; - CalcDirectionCoeffs(dir, spread, coeffs); -} - /** * CalcAngleCoeffs * @@ -299,37 +446,46 @@ inline void CalcXYZCoeffs(ALfloat x, ALfloat y, ALfloat z, ALfloat spread, ALflo * azimuth and elevation parameters are in radians, going right and up * respectively. */ -void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]); +inline void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]) +{ + ALfloat dir[3] = { + sinf(azimuth) * cosf(elevation), + sinf(elevation), + -cosf(azimuth) * cosf(elevation) + }; + CalcDirectionCoeffs(dir, spread, coeffs); +} /** - * ComputeAmbientGains + * CalcAnglePairwiseCoeffs * - * Computes channel gains for ambient, omni-directional sounds. + * Calculates ambisonic coefficients based on azimuth and elevation. The + * azimuth and elevation parameters are in radians, going right and up + * respectively. This pairwise variant warps the result such that +30 azimuth + * is full right, and -30 azimuth is full left. */ -#define ComputeAmbientGains(b, g, o) do { \ - if((b).CoeffCount > 0) \ - ComputeAmbientGainsMC((b).Ambi.Coeffs, (b).NumChannels, g, o); \ - else \ - ComputeAmbientGainsBF((b).Ambi.Map, (b).NumChannels, g, o); \ -} while (0) -void ComputeAmbientGainsMC(const ChannelConfig *chancoeffs, ALuint numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]); -void ComputeAmbientGainsBF(const BFChannelConfig *chanmap, ALuint numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]); +void CalcAnglePairwiseCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]); + +void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]); +void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]); /** - * ComputePanningGains + * ComputeDryPanGains * * Computes panning gains using the given channel decoder coefficients and the * pre-calculated direction or angle coefficients. */ -#define ComputePanningGains(b, c, g, o) do { \ - if((b).CoeffCount > 0) \ - ComputePanningGainsMC((b).Ambi.Coeffs, (b).NumChannels, (b).CoeffCount, c, g, o);\ - else \ - ComputePanningGainsBF((b).Ambi.Map, (b).NumChannels, c, g, o); \ -} while (0) -void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALuint numchans, ALuint numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]); -void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALuint numchans, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]); +inline void ComputeDryPanGains(const DryMixParams *dry, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) +{ + if(dry->CoeffCount > 0) + ComputePanningGainsMC(dry->Ambi.Coeffs, dry->NumChannels, dry->CoeffCount, + coeffs, ingain, gains); + else + ComputePanningGainsBF(dry->Ambi.Map, dry->NumChannels, coeffs, ingain, gains); +} +void ComputeFirstOrderGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]); +void ComputeFirstOrderGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]); /** * ComputeFirstOrderGains * @@ -337,24 +493,29 @@ void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALuint numchans, cons * a 1x4 'slice' of a transform matrix for the input channel, used to scale and * orient the sound samples. */ -#define ComputeFirstOrderGains(b, m, g, o) do { \ - if((b).CoeffCount > 0) \ - ComputeFirstOrderGainsMC((b).Ambi.Coeffs, (b).NumChannels, m, g, o); \ - else \ - ComputeFirstOrderGainsBF((b).Ambi.Map, (b).NumChannels, m, g, o); \ -} while (0) -void ComputeFirstOrderGainsMC(const ChannelConfig *chancoeffs, ALuint numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]); -void ComputeFirstOrderGainsBF(const BFChannelConfig *chanmap, ALuint numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]); +inline void ComputeFirstOrderGains(const BFMixParams *foa, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) +{ + if(foa->CoeffCount > 0) + ComputeFirstOrderGainsMC(foa->Ambi.Coeffs, foa->NumChannels, mtx, ingain, gains); + else + ComputeFirstOrderGainsBF(foa->Ambi.Map, foa->NumChannels, mtx, ingain, gains); +} -ALvoid MixSource(struct ALvoice *voice, struct ALsource *source, ALCdevice *Device, ALuint SamplesToDo); +ALboolean MixSource(struct ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsizei SamplesToDo); -ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size); -/* Caller must lock the device. */ -ALvoid aluHandleDisconnect(ALCdevice *device); +void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples); +/* Caller must lock the device, and the mixer must not be running. */ +void aluHandleDisconnect(ALCdevice *device, const char *msg, ...) DECL_FORMAT(printf, 2, 3); + +void UpdateContextProps(ALCcontext *context); + +extern MixerFunc MixSamples; +extern RowMixerFunc MixRowSamples; extern ALfloat ConeScale; extern ALfloat ZScale; +extern ALboolean OverrideReverbSpeedOfSound; #ifdef __cplusplus } diff --git a/Engine/lib/openal-soft/OpenAL32/Include/bs2b.h b/Engine/lib/openal-soft/OpenAL32/Include/bs2b.h index bfe5c274c..e845d906d 100644 --- a/Engine/lib/openal-soft/OpenAL32/Include/bs2b.h +++ b/Engine/lib/openal-soft/OpenAL32/Include/bs2b.h @@ -85,7 +85,7 @@ int bs2b_get_srate(struct bs2b *bs2b); /* Clear buffer */ void bs2b_clear(struct bs2b *bs2b); -void bs2b_cross_feed(struct bs2b *bs2b, float *restrict Left, float *restrict Right, unsigned int SamplesToDo); +void bs2b_cross_feed(struct bs2b *bs2b, float *restrict Left, float *restrict Right, int SamplesToDo); #ifdef __cplusplus } /* extern "C" */ diff --git a/Engine/lib/openal-soft/OpenAL32/Include/sample_cvt.h b/Engine/lib/openal-soft/OpenAL32/Include/sample_cvt.h index 12bb1fa6c..c041760e6 100644 --- a/Engine/lib/openal-soft/OpenAL32/Include/sample_cvt.h +++ b/Engine/lib/openal-soft/OpenAL32/Include/sample_cvt.h @@ -4,6 +4,12 @@ #include "AL/al.h" #include "alBuffer.h" -void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src, enum UserFmtType srcType, ALsizei numchans, ALsizei len, ALsizei align); +extern const ALshort muLawDecompressionTable[256]; +extern const ALshort aLawDecompressionTable[256]; + +void Convert_ALshort_ALima4(ALshort *dst, const ALubyte *src, ALsizei numchans, ALsizei len, + ALsizei align); +void Convert_ALshort_ALmsadpcm(ALshort *dst, const ALubyte *src, ALsizei numchans, ALsizei len, + ALsizei align); #endif /* SAMPLE_CVT_H */ diff --git a/Engine/lib/openal-soft/OpenAL32/alAuxEffectSlot.c b/Engine/lib/openal-soft/OpenAL32/alAuxEffectSlot.c index b860b2b0e..d04fc4a77 100644 --- a/Engine/lib/openal-soft/OpenAL32/alAuxEffectSlot.c +++ b/Engine/lib/openal-soft/OpenAL32/alAuxEffectSlot.c @@ -27,99 +27,140 @@ #include "AL/alc.h" #include "alMain.h" #include "alAuxEffectSlot.h" -#include "alThunk.h" #include "alError.h" #include "alListener.h" #include "alSource.h" +#include "fpu_modes.h" #include "almalloc.h" -extern inline void LockEffectSlotsRead(ALCcontext *context); -extern inline void UnlockEffectSlotsRead(ALCcontext *context); -extern inline void LockEffectSlotsWrite(ALCcontext *context); -extern inline void UnlockEffectSlotsWrite(ALCcontext *context); -extern inline struct ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id); -extern inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id); +extern inline void LockEffectSlotList(ALCcontext *context); +extern inline void UnlockEffectSlotList(ALCcontext *context); -static void RemoveEffectSlotList(ALCcontext *Context, ALeffectslot *slot); +static void AddActiveEffectSlots(const ALuint *slotids, ALsizei count, ALCcontext *context); +static void RemoveActiveEffectSlots(const ALuint *slotids, ALsizei count, ALCcontext *context); -static UIntMap EffectStateFactoryMap; -static inline ALeffectStateFactory *getFactoryByType(ALenum type) +static const struct { + ALenum Type; + EffectStateFactory* (*GetFactory)(void); +} FactoryList[] = { + { AL_EFFECT_NULL, NullStateFactory_getFactory }, + { AL_EFFECT_EAXREVERB, ReverbStateFactory_getFactory }, + { AL_EFFECT_REVERB, ReverbStateFactory_getFactory }, + { AL_EFFECT_CHORUS, ChorusStateFactory_getFactory }, + { AL_EFFECT_COMPRESSOR, CompressorStateFactory_getFactory }, + { AL_EFFECT_DISTORTION, DistortionStateFactory_getFactory }, + { AL_EFFECT_ECHO, EchoStateFactory_getFactory }, + { AL_EFFECT_EQUALIZER, EqualizerStateFactory_getFactory }, + { AL_EFFECT_FLANGER, FlangerStateFactory_getFactory }, + { AL_EFFECT_RING_MODULATOR, ModulatorStateFactory_getFactory }, + { AL_EFFECT_PITCH_SHIFTER, PshifterStateFactory_getFactory}, + { AL_EFFECT_DEDICATED_DIALOGUE, DedicatedStateFactory_getFactory }, + { AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, DedicatedStateFactory_getFactory } +}; + +static inline EffectStateFactory *getFactoryByType(ALenum type) { - ALeffectStateFactory* (*getFactory)(void) = LookupUIntMapKey(&EffectStateFactoryMap, type); - if(getFactory != NULL) - return getFactory(); + size_t i; + for(i = 0;i < COUNTOF(FactoryList);i++) + { + if(FactoryList[i].Type == type) + return FactoryList[i].GetFactory(); + } return NULL; } static void ALeffectState_IncRef(ALeffectState *state); -static void ALeffectState_DecRef(ALeffectState *state); + + +static inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) +{ + id--; + if(UNLIKELY(id >= VECTOR_SIZE(context->EffectSlotList))) + return NULL; + return VECTOR_ELEM(context->EffectSlotList, id); +} + +static inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) +{ + EffectSubList *sublist; + ALuint lidx = (id-1) >> 6; + ALsizei slidx = (id-1) & 0x3f; + + if(UNLIKELY(lidx >= VECTOR_SIZE(device->EffectList))) + return NULL; + sublist = &VECTOR_ELEM(device->EffectList, lidx); + if(UNLIKELY(sublist->FreeMask & (U64(1)<Effects + slidx; +} + #define DO_UPDATEPROPS() do { \ if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) \ - UpdateEffectSlotProps(slot); \ + UpdateEffectSlotProps(slot, context); \ else \ - slot->NeedsUpdate = AL_TRUE; \ + ATOMIC_FLAG_CLEAR(&slot->PropsClean, almemory_order_release); \ } while(0) AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots) { + ALCdevice *device; ALCcontext *context; - ALeffectslot *first, *last; ALsizei cur; - ALenum err; context = GetContextRef(); if(!context) return; if(!(n >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Generating %d effect slots", n); + if(n == 0) goto done; - first = last = NULL; + LockEffectSlotList(context); + device = context->Device; + if(device->AuxiliaryEffectSlotMax - VECTOR_SIZE(context->EffectSlotList) < (ALuint)n) + { + UnlockEffectSlotList(context); + SETERR_GOTO(context, AL_OUT_OF_MEMORY, done, "Exceeding %u auxiliary effect slot limit", + device->AuxiliaryEffectSlotMax); + } for(cur = 0;cur < n;cur++) { - ALeffectslot *slot = al_calloc(16, sizeof(ALeffectslot)); - err = AL_OUT_OF_MEMORY; + ALeffectslotPtr *iter = VECTOR_BEGIN(context->EffectSlotList); + ALeffectslotPtr *end = VECTOR_END(context->EffectSlotList); + ALeffectslot *slot = NULL; + ALenum err = AL_OUT_OF_MEMORY; + + for(;iter != end;iter++) + { + if(!*iter) + break; + } + if(iter == end) + { + VECTOR_PUSH_BACK(context->EffectSlotList, NULL); + iter = &VECTOR_BACK(context->EffectSlotList); + } + slot = al_calloc(16, sizeof(ALeffectslot)); if(!slot || (err=InitEffectSlot(slot)) != AL_NO_ERROR) { al_free(slot); - alDeleteAuxiliaryEffectSlots(cur, effectslots); - SET_ERROR_AND_GOTO(context, err, done); - } - - err = NewThunkEntry(&slot->id); - if(err == AL_NO_ERROR) - err = InsertUIntMapEntry(&context->EffectSlotMap, slot->id, slot); - if(err != AL_NO_ERROR) - { - FreeThunkEntry(slot->id); - ALeffectState_DecRef(slot->Effect.State); - if(slot->Params.EffectState) - ALeffectState_DecRef(slot->Params.EffectState); - al_free(slot); + UnlockEffectSlotList(context); alDeleteAuxiliaryEffectSlots(cur, effectslots); - SET_ERROR_AND_GOTO(context, err, done); + SETERR_GOTO(context, err, done, "Effect slot object allocation failed"); } - aluInitEffectPanning(slot); - if(!first) first = slot; - if(last) ATOMIC_STORE(&last->next, slot, almemory_order_relaxed); - last = slot; + slot->id = (iter - VECTOR_BEGIN(context->EffectSlotList)) + 1; + *iter = slot; effectslots[cur] = slot->id; } - if(last != NULL) - { - ALeffectslot *root = ATOMIC_LOAD(&context->ActiveAuxSlotList); - do { - ATOMIC_STORE(&last->next, root, almemory_order_relaxed); - } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALeffectslot*, &context->ActiveAuxSlotList, - &root, first)); - } + AddActiveEffectSlots(effectslots, n, context); + UnlockEffectSlotList(context); done: ALCcontext_DecRef(context); @@ -134,25 +175,29 @@ AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint * context = GetContextRef(); if(!context) return; - LockEffectSlotsWrite(context); + LockEffectSlotList(context); if(!(n >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Deleting %d effect slots", n); + if(n == 0) goto done; + for(i = 0;i < n;i++) { if((slot=LookupEffectSlot(context, effectslots[i])) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect slot ID %u", + effectslots[i]); if(ReadRef(&slot->ref) != 0) - SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + SETERR_GOTO(context, AL_INVALID_NAME, done, "Deleting in-use effect slot %u", + effectslots[i]); } // All effectslots are valid + RemoveActiveEffectSlots(effectslots, n, context); for(i = 0;i < n;i++) { - if((slot=RemoveEffectSlot(context, effectslots[i])) == NULL) + if((slot=LookupEffectSlot(context, effectslots[i])) == NULL) continue; - FreeThunkEntry(slot->id); + VECTOR_ELEM(context->EffectSlotList, effectslots[i]-1) = NULL; - RemoveEffectSlotList(context, slot); DeinitEffectSlot(slot); memset(slot, 0, sizeof(*slot)); @@ -160,7 +205,7 @@ AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint * } done: - UnlockEffectSlotsWrite(context); + UnlockEffectSlotList(context); ALCcontext_DecRef(context); } @@ -172,9 +217,9 @@ AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot) context = GetContextRef(); if(!context) return AL_FALSE; - LockEffectSlotsRead(context); + LockEffectSlotList(context); ret = (LookupEffectSlot(context, effectslot) ? AL_TRUE : AL_FALSE); - UnlockEffectSlotsRead(context); + UnlockEffectSlotList(context); ALCcontext_DecRef(context); @@ -192,43 +237,45 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param context = GetContextRef(); if(!context) return; - WriteLock(&context->PropLock); - LockEffectSlotsRead(context); + almtx_lock(&context->PropLock); + LockEffectSlotList(context); if((slot=LookupEffectSlot(context, effectslot)) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect slot ID %u", effectslot); switch(param) { case AL_EFFECTSLOT_EFFECT: device = context->Device; - LockEffectsRead(device); + LockEffectList(device); effect = (value ? LookupEffect(device, value) : NULL); if(!(value == 0 || effect != NULL)) { - UnlockEffectsRead(device); - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + UnlockEffectList(device); + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Invalid effect ID %u", value); } - err = InitializeEffect(device, slot, effect); - UnlockEffectsRead(device); + err = InitializeEffect(context, slot, effect); + UnlockEffectList(device); if(err != AL_NO_ERROR) - SET_ERROR_AND_GOTO(context, err, done); + SETERR_GOTO(context, err, done, "Effect initialization failed"); break; case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO: if(!(value == AL_TRUE || value == AL_FALSE)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + SETERR_GOTO(context, AL_INVALID_VALUE, done, + "Effect slot auxiliary send auto out of range"); slot->AuxSendAuto = value; break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + SETERR_GOTO(context, AL_INVALID_ENUM, done, "Invalid effect slot integer property 0x%04x", + param); } DO_UPDATEPROPS(); done: - UnlockEffectSlotsRead(context); - WriteUnlock(&context->PropLock); + UnlockEffectSlotList(context); + almtx_unlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -247,17 +294,18 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum para context = GetContextRef(); if(!context) return; - LockEffectSlotsRead(context); + LockEffectSlotList(context); if(LookupEffectSlot(context, effectslot) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect slot ID %u", effectslot); switch(param) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid effect slot integer-vector property 0x%04x", + param); } done: - UnlockEffectSlotsRead(context); + UnlockEffectSlotList(context); ALCcontext_DecRef(context); } @@ -269,26 +317,27 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param context = GetContextRef(); if(!context) return; - WriteLock(&context->PropLock); - LockEffectSlotsRead(context); + almtx_lock(&context->PropLock); + LockEffectSlotList(context); if((slot=LookupEffectSlot(context, effectslot)) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect slot ID %u", effectslot); switch(param) { case AL_EFFECTSLOT_GAIN: if(!(value >= 0.0f && value <= 1.0f)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Effect slot gain out of range"); slot->Gain = value; break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + SETERR_GOTO(context, AL_INVALID_ENUM, done, "Invalid effect slot float property 0x%04x", + param); } DO_UPDATEPROPS(); done: - UnlockEffectSlotsRead(context); - WriteUnlock(&context->PropLock); + UnlockEffectSlotList(context); + almtx_unlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -306,17 +355,18 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum para context = GetContextRef(); if(!context) return; - LockEffectSlotsRead(context); + LockEffectSlotList(context); if(LookupEffectSlot(context, effectslot) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect slot ID %u", effectslot); switch(param) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid effect slot float-vector property 0x%04x", + param); } done: - UnlockEffectSlotsRead(context); + UnlockEffectSlotList(context); ALCcontext_DecRef(context); } @@ -328,9 +378,9 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum pa context = GetContextRef(); if(!context) return; - LockEffectSlotsRead(context); + LockEffectSlotList(context); if((slot=LookupEffectSlot(context, effectslot)) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect slot ID %u", effectslot); switch(param) { case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO: @@ -338,11 +388,11 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum pa break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param); } done: - UnlockEffectSlotsRead(context); + UnlockEffectSlotList(context); ALCcontext_DecRef(context); } @@ -361,17 +411,18 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum p context = GetContextRef(); if(!context) return; - LockEffectSlotsRead(context); + LockEffectSlotList(context); if(LookupEffectSlot(context, effectslot) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect slot ID %u", effectslot); switch(param) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid effect slot integer-vector property 0x%04x", + param); } done: - UnlockEffectSlotsRead(context); + UnlockEffectSlotList(context); ALCcontext_DecRef(context); } @@ -383,9 +434,9 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum pa context = GetContextRef(); if(!context) return; - LockEffectSlotsRead(context); + LockEffectSlotList(context); if((slot=LookupEffectSlot(context, effectslot)) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect slot ID %u", effectslot); switch(param) { case AL_EFFECTSLOT_GAIN: @@ -393,11 +444,11 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum pa break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param); } done: - UnlockEffectSlotsRead(context); + UnlockEffectSlotList(context); ALCcontext_DecRef(context); } @@ -415,76 +466,32 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum p context = GetContextRef(); if(!context) return; - LockEffectSlotsRead(context); + LockEffectSlotList(context); if(LookupEffectSlot(context, effectslot) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect slot ID %u", effectslot); switch(param) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid effect slot float-vector property 0x%04x", + param); } done: - UnlockEffectSlotsRead(context); + UnlockEffectSlotList(context); ALCcontext_DecRef(context); } -static void RemoveEffectSlotList(ALCcontext *context, ALeffectslot *slot) -{ - ALCdevice *device = context->Device; - ALeffectslot *root, *next; - - root = slot; - next = ATOMIC_LOAD(&slot->next); - if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALeffectslot*, &context->ActiveAuxSlotList, &root, next)) - { - ALeffectslot *cur; - do { - cur = root; - root = slot; - } while(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALeffectslot*, &cur->next, &root, next)); - } - /* Wait for any mix that may be using these effect slots to finish. */ - while((ReadRef(&device->MixCount)&1) != 0) - althrd_yield(); -} - - -void InitEffectFactoryMap(void) -{ - InitUIntMap(&EffectStateFactoryMap, ~0); - - InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_NULL, ALnullStateFactory_getFactory); - InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EAXREVERB, ALreverbStateFactory_getFactory); - InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_REVERB, ALreverbStateFactory_getFactory); - InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_CHORUS, ALchorusStateFactory_getFactory); - InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_COMPRESSOR, ALcompressorStateFactory_getFactory); - InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DISTORTION, ALdistortionStateFactory_getFactory); - InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_ECHO, ALechoStateFactory_getFactory); - InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EQUALIZER, ALequalizerStateFactory_getFactory); - InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_FLANGER, ALflangerStateFactory_getFactory); - InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_RING_MODULATOR, ALmodulatorStateFactory_getFactory); - InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DEDICATED_DIALOGUE, ALdedicatedStateFactory_getFactory); - InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, ALdedicatedStateFactory_getFactory); -} - -void DeinitEffectFactoryMap(void) -{ - ResetUIntMap(&EffectStateFactoryMap); -} - - -ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect) +ALenum InitializeEffect(ALCcontext *Context, ALeffectslot *EffectSlot, ALeffect *effect) { + ALCdevice *Device = Context->Device; ALenum newtype = (effect ? effect->type : AL_EFFECT_NULL); struct ALeffectslotProps *props; ALeffectState *State; if(newtype != EffectSlot->Effect.Type) { - ALeffectStateFactory *factory; - FPUCtl oldMode; + EffectStateFactory *factory; factory = getFactoryByType(newtype); if(!factory) @@ -492,22 +499,22 @@ ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *e ERR("Failed to find factory for effect type 0x%04x\n", newtype); return AL_INVALID_ENUM; } - State = V0(factory,create)(); + State = EffectStateFactory_create(factory); if(!State) return AL_OUT_OF_MEMORY; - SetMixerFPUMode(&oldMode); + START_MIXER_MODE(); almtx_lock(&Device->BackendLock); State->OutBuffer = Device->Dry.Buffer; State->OutChannels = Device->Dry.NumChannels; if(V(State,deviceUpdate)(Device) == AL_FALSE) { almtx_unlock(&Device->BackendLock); - RestoreFPUMode(&oldMode); + LEAVE_MIXER_MODE(); ALeffectState_DecRef(State); return AL_OUT_OF_MEMORY; } almtx_unlock(&Device->BackendLock); - RestoreFPUMode(&oldMode); + END_MIXER_MODE(); if(!effect) { @@ -527,11 +534,12 @@ ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *e EffectSlot->Effect.Props = effect->Props; /* Remove state references from old effect slot property updates. */ - props = ATOMIC_LOAD(&EffectSlot->FreeList); + props = ATOMIC_LOAD_SEQ(&Context->FreeEffectslotProps); while(props) { - State = ATOMIC_EXCHANGE(ALeffectState*, &props->State, NULL, almemory_order_relaxed); - if(State) ALeffectState_DecRef(State); + if(props->State) + ALeffectState_DecRef(props->State); + props->State = NULL; props = ATOMIC_LOAD(&props->next, almemory_order_relaxed); } @@ -546,7 +554,7 @@ static void ALeffectState_IncRef(ALeffectState *state) TRACEREF("%p increasing refcount to %u\n", state, ref); } -static void ALeffectState_DecRef(ALeffectState *state) +void ALeffectState_DecRef(ALeffectState *state) { uint ref; ref = DecrementRef(&state->Ref); @@ -568,23 +576,110 @@ void ALeffectState_Destruct(ALeffectState *UNUSED(state)) } +static void AddActiveEffectSlots(const ALuint *slotids, ALsizei count, ALCcontext *context) +{ + struct ALeffectslotArray *curarray = ATOMIC_LOAD(&context->ActiveAuxSlots, + almemory_order_acquire); + struct ALeffectslotArray *newarray = NULL; + ALsizei newcount = curarray->count + count; + ALCdevice *device = context->Device; + ALsizei i, j; + + /* Insert the new effect slots into the head of the array, followed by the + * existing ones. + */ + newarray = al_calloc(DEF_ALIGN, FAM_SIZE(struct ALeffectslotArray, slot, newcount)); + newarray->count = newcount; + for(i = 0;i < count;i++) + newarray->slot[i] = LookupEffectSlot(context, slotids[i]); + for(j = 0;i < newcount;) + newarray->slot[i++] = curarray->slot[j++]; + /* Remove any duplicates (first instance of each will be kept). */ + for(i = 1;i < newcount;i++) + { + for(j = i;j != 0;) + { + if(UNLIKELY(newarray->slot[i] == newarray->slot[--j])) + { + newcount--; + for(j = i;j < newcount;j++) + newarray->slot[j] = newarray->slot[j+1]; + i--; + break; + } + } + } + + /* Reallocate newarray if the new size ended up smaller from duplicate + * removal. + */ + if(UNLIKELY(newcount < newarray->count)) + { + struct ALeffectslotArray *tmpnewarray = al_calloc(DEF_ALIGN, + FAM_SIZE(struct ALeffectslotArray, slot, newcount)); + memcpy(tmpnewarray, newarray, FAM_SIZE(struct ALeffectslotArray, slot, newcount)); + al_free(newarray); + newarray = tmpnewarray; + newarray->count = newcount; + } + + curarray = ATOMIC_EXCHANGE_PTR(&context->ActiveAuxSlots, newarray, almemory_order_acq_rel); + while((ATOMIC_LOAD(&device->MixCount, almemory_order_acquire)&1)) + althrd_yield(); + al_free(curarray); +} + +static void RemoveActiveEffectSlots(const ALuint *slotids, ALsizei count, ALCcontext *context) +{ + struct ALeffectslotArray *curarray = ATOMIC_LOAD(&context->ActiveAuxSlots, + almemory_order_acquire); + struct ALeffectslotArray *newarray = NULL; + ALCdevice *device = context->Device; + ALsizei i, j; + + /* Don't shrink the allocated array size since we don't know how many (if + * any) of the effect slots to remove are in the array. + */ + newarray = al_calloc(DEF_ALIGN, FAM_SIZE(struct ALeffectslotArray, slot, curarray->count)); + newarray->count = 0; + for(i = 0;i < curarray->count;i++) + { + /* Insert this slot into the new array only if it's not one to remove. */ + ALeffectslot *slot = curarray->slot[i]; + for(j = count;j != 0;) + { + if(slot->id == slotids[--j]) + goto skip_ins; + } + newarray->slot[newarray->count++] = slot; + skip_ins: ; + } + + /* TODO: Could reallocate newarray now that we know it's needed size. */ + + curarray = ATOMIC_EXCHANGE_PTR(&context->ActiveAuxSlots, newarray, almemory_order_acq_rel); + while((ATOMIC_LOAD(&device->MixCount, almemory_order_acquire)&1)) + althrd_yield(); + al_free(curarray); +} + + ALenum InitEffectSlot(ALeffectslot *slot) { - ALeffectStateFactory *factory; + EffectStateFactory *factory; slot->Effect.Type = AL_EFFECT_NULL; factory = getFactoryByType(AL_EFFECT_NULL); - if(!(slot->Effect.State=V0(factory,create)())) - return AL_OUT_OF_MEMORY; + slot->Effect.State = EffectStateFactory_create(factory); + if(!slot->Effect.State) return AL_OUT_OF_MEMORY; - slot->NeedsUpdate = AL_FALSE; slot->Gain = 1.0; slot->AuxSendAuto = AL_TRUE; + ATOMIC_FLAG_TEST_AND_SET(&slot->PropsClean, almemory_order_relaxed); InitRef(&slot->ref, 0); ATOMIC_INIT(&slot->Update, NULL); - ATOMIC_INIT(&slot->FreeList, NULL); slot->Params.Gain = 1.0f; slot->Params.AuxSendAuto = AL_TRUE; @@ -592,52 +687,38 @@ ALenum InitEffectSlot(ALeffectslot *slot) slot->Params.EffectState = slot->Effect.State; 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; - ATOMIC_INIT(&slot->next, NULL); - return AL_NO_ERROR; } void DeinitEffectSlot(ALeffectslot *slot) { struct ALeffectslotProps *props; - ALeffectState *state; - size_t count = 0; - props = ATOMIC_LOAD(&slot->Update); + props = ATOMIC_LOAD_SEQ(&slot->Update); if(props) { - state = ATOMIC_LOAD(&props->State, almemory_order_relaxed); - if(state) ALeffectState_DecRef(state); + if(props->State) ALeffectState_DecRef(props->State); TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n", props); al_free(props); } - props = ATOMIC_LOAD(&slot->FreeList, almemory_order_relaxed); - while(props) - { - struct ALeffectslotProps *next; - state = ATOMIC_LOAD(&props->State, almemory_order_relaxed); - next = ATOMIC_LOAD(&props->next, almemory_order_relaxed); - if(state) ALeffectState_DecRef(state); - al_free(props); - props = next; - ++count; - } - TRACE("Freed "SZFMT" AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s"); ALeffectState_DecRef(slot->Effect.State); if(slot->Params.EffectState) ALeffectState_DecRef(slot->Params.EffectState); } -void UpdateEffectSlotProps(ALeffectslot *slot) +void UpdateEffectSlotProps(ALeffectslot *slot, ALCcontext *context) { struct ALeffectslotProps *props; ALeffectState *oldstate; /* Get an unused property container, or allocate a new one as needed. */ - props = ATOMIC_LOAD(&slot->FreeList, almemory_order_relaxed); + props = ATOMIC_LOAD(&context->FreeEffectslotProps, almemory_order_relaxed); if(!props) props = al_calloc(16, sizeof(*props)); else @@ -645,37 +726,31 @@ void UpdateEffectSlotProps(ALeffectslot *slot) struct ALeffectslotProps *next; do { next = ATOMIC_LOAD(&props->next, almemory_order_relaxed); - } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps*, - &slot->FreeList, &props, next, almemory_order_seq_cst, - almemory_order_consume) == 0); + } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&context->FreeEffectslotProps, &props, next, + almemory_order_seq_cst, almemory_order_acquire) == 0); } /* Copy in current property values. */ - ATOMIC_STORE(&props->Gain, slot->Gain, almemory_order_relaxed); - ATOMIC_STORE(&props->AuxSendAuto, slot->AuxSendAuto, almemory_order_relaxed); + props->Gain = slot->Gain; + props->AuxSendAuto = slot->AuxSendAuto; - ATOMIC_STORE(&props->Type, slot->Effect.Type, almemory_order_relaxed); + props->Type = slot->Effect.Type; props->Props = slot->Effect.Props; /* Swap out any stale effect state object there may be in the container, to * delete it. */ ALeffectState_IncRef(slot->Effect.State); - oldstate = ATOMIC_EXCHANGE(ALeffectState*, &props->State, slot->Effect.State, - almemory_order_relaxed); + oldstate = props->State; + props->State = slot->Effect.State; /* Set the new container for updating internal parameters. */ - props = ATOMIC_EXCHANGE(struct ALeffectslotProps*, &slot->Update, props, - almemory_order_acq_rel); + props = ATOMIC_EXCHANGE_PTR(&slot->Update, props, almemory_order_acq_rel); if(props) { /* If there was an unused update container, put it back in the * freelist. */ - struct ALeffectslotProps *first = ATOMIC_LOAD(&slot->FreeList); - do { - ATOMIC_STORE(&props->next, first, almemory_order_relaxed); - } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps*, - &slot->FreeList, &first, props) == 0); + ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props); } if(oldstate) @@ -684,32 +759,38 @@ void UpdateEffectSlotProps(ALeffectslot *slot) void UpdateAllEffectSlotProps(ALCcontext *context) { - ALeffectslot *slot; + struct ALeffectslotArray *auxslots; + ALsizei i; - LockEffectSlotsRead(context); - slot = ATOMIC_LOAD(&context->ActiveAuxSlotList); - while(slot) + LockEffectSlotList(context); + auxslots = ATOMIC_LOAD(&context->ActiveAuxSlots, almemory_order_acquire); + for(i = 0;i < auxslots->count;i++) { - if(slot->NeedsUpdate) - UpdateEffectSlotProps(slot); - slot->NeedsUpdate = AL_FALSE; - slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed); + ALeffectslot *slot = auxslots->slot[i]; + if(!ATOMIC_FLAG_TEST_AND_SET(&slot->PropsClean, almemory_order_acq_rel)) + UpdateEffectSlotProps(slot, context); } - UnlockEffectSlotsRead(context); + UnlockEffectSlotList(context); } -ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context) +ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *context) { - ALsizei pos; - for(pos = 0;pos < Context->EffectSlotMap.size;pos++) + ALeffectslotPtr *iter = VECTOR_BEGIN(context->EffectSlotList); + ALeffectslotPtr *end = VECTOR_END(context->EffectSlotList); + size_t leftover = 0; + + for(;iter != end;iter++) { - ALeffectslot *temp = Context->EffectSlotMap.values[pos]; - Context->EffectSlotMap.values[pos] = NULL; + ALeffectslot *slot = *iter; + if(!slot) continue; + *iter = NULL; - DeinitEffectSlot(temp); + DeinitEffectSlot(slot); - FreeThunkEntry(temp->id); - memset(temp, 0, sizeof(ALeffectslot)); - al_free(temp); + memset(slot, 0, sizeof(*slot)); + al_free(slot); + ++leftover; } + if(leftover > 0) + WARN("(%p) Deleted "SZFMT" AuxiliaryEffectSlot%s\n", context, leftover, (leftover==1)?"":"s"); } diff --git a/Engine/lib/openal-soft/OpenAL32/alBuffer.c b/Engine/lib/openal-soft/OpenAL32/alBuffer.c index 193cfeaa9..ed7124348 100644 --- a/Engine/lib/openal-soft/OpenAL32/alBuffer.c +++ b/Engine/lib/openal-soft/OpenAL32/alBuffer.c @@ -32,24 +32,41 @@ #include "alu.h" #include "alError.h" #include "alBuffer.h" -#include "alThunk.h" #include "sample_cvt.h" -extern inline void LockBuffersRead(ALCdevice *device); -extern inline void UnlockBuffersRead(ALCdevice *device); -extern inline void LockBuffersWrite(ALCdevice *device); -extern inline void UnlockBuffersWrite(ALCdevice *device); -extern inline struct ALbuffer *LookupBuffer(ALCdevice *device, ALuint id); -extern inline struct ALbuffer *RemoveBuffer(ALCdevice *device, ALuint id); -extern inline ALuint FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type); -extern inline ALuint FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type); +extern inline void LockBufferList(ALCdevice *device); +extern inline void UnlockBufferList(ALCdevice *device); +extern inline ALsizei FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type); +extern inline ALsizei FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type); -static ALboolean IsValidType(ALenum type); -static ALboolean IsValidChannels(ALenum channels); +static ALbuffer *AllocBuffer(ALCcontext *context); +static void FreeBuffer(ALCdevice *device, ALbuffer *buffer); +static const ALchar *NameFromUserFmtType(enum UserFmtType type); +static void LoadData(ALCcontext *context, ALbuffer *buffer, ALuint freq, ALsizei size, + enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, + const ALvoid *data, ALbitfieldSOFT access); static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans, enum UserFmtType *type); -static ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum FmtType *type); -static ALboolean SanitizeAlignment(enum UserFmtType type, ALsizei *align); +static ALsizei SanitizeAlignment(enum UserFmtType type, ALsizei align); + +static inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) +{ + BufferSubList *sublist; + ALuint lidx = (id-1) >> 6; + ALsizei slidx = (id-1) & 0x3f; + + if(UNLIKELY(lidx >= VECTOR_SIZE(device->BufferList))) + return NULL; + sublist = &VECTOR_ELEM(device->BufferList, lidx); + if(UNLIKELY(sublist->FreeMask & (U64(1)<Buffers + slidx; +} + + +#define INVALID_STORAGE_MASK ~(AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT | AL_PRESERVE_DATA_BIT_SOFT | AL_MAP_PERSISTENT_BIT_SOFT) +#define MAP_READ_WRITE_FLAGS (AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT) +#define INVALID_MAP_FLAGS ~(AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT | AL_MAP_PERSISTENT_BIT_SOFT) AL_API ALvoid AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers) @@ -61,11 +78,10 @@ AL_API ALvoid AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers) if(!context) return; if(!(n >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - - for(cur = 0;cur < n;cur++) + alSetError(context, AL_INVALID_VALUE, "Generating %d buffers", n); + else for(cur = 0;cur < n;cur++) { - ALbuffer *buffer = NewBuffer(context); + ALbuffer *buffer = AllocBuffer(context); if(!buffer) { alDeleteBuffers(cur, buffers); @@ -75,7 +91,6 @@ AL_API ALvoid AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers) buffers[cur] = buffer->id; } -done: ALCcontext_DecRef(context); } @@ -91,30 +106,38 @@ AL_API ALvoid AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers) device = context->Device; - LockBuffersWrite(device); - if(!(n >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + LockBufferList(device); + if(UNLIKELY(n < 0)) + { + alSetError(context, AL_INVALID_VALUE, "Deleting %d buffers", n); + goto done; + } for(i = 0;i < n;i++) { if(!buffers[i]) continue; - /* Check for valid Buffer ID */ + /* Check for valid Buffer ID, and make sure it's not in use. */ if((ALBuf=LookupBuffer(device, buffers[i])) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + { + alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffers[i]); + goto done; + } if(ReadRef(&ALBuf->ref) != 0) - SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + { + alSetError(context, AL_INVALID_OPERATION, "Deleting in-use buffer %u", buffers[i]); + goto done; + } } - for(i = 0;i < n;i++) { if((ALBuf=LookupBuffer(device, buffers[i])) != NULL) - DeleteBuffer(device, ALBuf); + FreeBuffer(device, ALBuf); } done: - UnlockBuffersWrite(device); + UnlockBufferList(device); ALCcontext_DecRef(context); } @@ -126,10 +149,10 @@ AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer) context = GetContextRef(); if(!context) return AL_FALSE; - LockBuffersRead(context->Device); + LockBufferList(context->Device); ret = ((!buffer || LookupBuffer(context->Device, buffer)) ? AL_TRUE : AL_FALSE); - UnlockBuffersRead(context->Device); + UnlockBufferList(context->Device); ALCcontext_DecRef(context); @@ -138,399 +161,297 @@ AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer) AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq) +{ alBufferStorageSOFT(buffer, format, data, size, freq, 0); } + +AL_API void AL_APIENTRY alBufferStorageSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags) { enum UserFmtChannels srcchannels = UserFmtMono; - enum UserFmtType srctype = UserFmtByte; + enum UserFmtType srctype = UserFmtUByte; ALCdevice *device; ALCcontext *context; ALbuffer *albuf; - ALenum newformat = AL_NONE; - ALuint framesize; - ALsizei align; - ALenum err; context = GetContextRef(); if(!context) return; device = context->Device; - LockBuffersRead(device); - if((albuf=LookupBuffer(device, buffer)) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - if(!(size >= 0 && freq > 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - if(DecomposeUserFormat(format, &srcchannels, &srctype) == AL_FALSE) - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + LockBufferList(device); + if(UNLIKELY((albuf=LookupBuffer(device, buffer)) == NULL)) + alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer); + else if(UNLIKELY(size < 0)) + alSetError(context, AL_INVALID_VALUE, "Negative storage size %d", size); + else if(UNLIKELY(freq < 1)) + alSetError(context, AL_INVALID_VALUE, "Invalid sample rate %d", freq); + else if(UNLIKELY((flags&INVALID_STORAGE_MASK) != 0)) + alSetError(context, AL_INVALID_VALUE, "Invalid storage flags 0x%x", + flags&INVALID_STORAGE_MASK); + else if(UNLIKELY((flags&AL_MAP_PERSISTENT_BIT_SOFT) && !(flags&MAP_READ_WRITE_FLAGS))) + alSetError(context, AL_INVALID_VALUE, + "Declaring persistently mapped storage without read or write access"); + else if(UNLIKELY(DecomposeUserFormat(format, &srcchannels, &srctype) == AL_FALSE)) + alSetError(context, AL_INVALID_ENUM, "Invalid format 0x%04x", format); + else + LoadData(context, albuf, freq, size, srcchannels, srctype, data, flags); - align = ATOMIC_LOAD(&albuf->UnpackAlign); - if(SanitizeAlignment(srctype, &align) == AL_FALSE) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - switch(srctype) + UnlockBufferList(device); + ALCcontext_DecRef(context); +} + +AL_API void* AL_APIENTRY alMapBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access) +{ + void *retval = NULL; + ALCdevice *device; + ALCcontext *context; + ALbuffer *albuf; + + context = GetContextRef(); + if(!context) return retval; + + device = context->Device; + LockBufferList(device); + if(UNLIKELY((albuf=LookupBuffer(device, buffer)) == NULL)) + alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer); + else if(UNLIKELY((access&INVALID_MAP_FLAGS) != 0)) + alSetError(context, AL_INVALID_VALUE, "Invalid map flags 0x%x", access&INVALID_MAP_FLAGS); + else if(UNLIKELY(!(access&MAP_READ_WRITE_FLAGS))) + alSetError(context, AL_INVALID_VALUE, "Mapping buffer %u without read or write access", + buffer); + else { - case UserFmtByte: - case UserFmtUByte: - case UserFmtShort: - case UserFmtUShort: - case UserFmtFloat: - framesize = FrameSizeFromUserFmt(srcchannels, srctype) * align; - if((size%framesize) != 0) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - - err = LoadData(albuf, freq, format, size/framesize*align, - srcchannels, srctype, data, align, AL_TRUE); - if(err != AL_NO_ERROR) - SET_ERROR_AND_GOTO(context, err, done); - break; - - case UserFmtInt: - case UserFmtUInt: - case UserFmtByte3: - case UserFmtUByte3: - case UserFmtDouble: - framesize = FrameSizeFromUserFmt(srcchannels, srctype) * align; - if((size%framesize) != 0) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - - switch(srcchannels) - { - case UserFmtMono: newformat = AL_FORMAT_MONO_FLOAT32; break; - case UserFmtStereo: newformat = AL_FORMAT_STEREO_FLOAT32; break; - case UserFmtRear: newformat = AL_FORMAT_REAR32; break; - case UserFmtQuad: newformat = AL_FORMAT_QUAD32; break; - case UserFmtX51: newformat = AL_FORMAT_51CHN32; break; - case UserFmtX61: newformat = AL_FORMAT_61CHN32; break; - case UserFmtX71: newformat = AL_FORMAT_71CHN32; break; - case UserFmtBFormat2D: newformat = AL_FORMAT_BFORMAT2D_FLOAT32; break; - case UserFmtBFormat3D: newformat = AL_FORMAT_BFORMAT3D_FLOAT32; break; - } - err = LoadData(albuf, freq, newformat, size/framesize*align, - srcchannels, srctype, data, align, AL_TRUE); - if(err != AL_NO_ERROR) - SET_ERROR_AND_GOTO(context, err, done); - break; - - case UserFmtMulaw: - case UserFmtAlaw: - framesize = FrameSizeFromUserFmt(srcchannels, srctype) * align; - if((size%framesize) != 0) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - - switch(srcchannels) - { - case UserFmtMono: newformat = AL_FORMAT_MONO16; break; - case UserFmtStereo: newformat = AL_FORMAT_STEREO16; break; - case UserFmtRear: newformat = AL_FORMAT_REAR16; break; - case UserFmtQuad: newformat = AL_FORMAT_QUAD16; break; - case UserFmtX51: newformat = AL_FORMAT_51CHN16; break; - case UserFmtX61: newformat = AL_FORMAT_61CHN16; break; - case UserFmtX71: newformat = AL_FORMAT_71CHN16; break; - case UserFmtBFormat2D: newformat = AL_FORMAT_BFORMAT2D_16; break; - case UserFmtBFormat3D: newformat = AL_FORMAT_BFORMAT3D_16; break; - } - err = LoadData(albuf, freq, newformat, size/framesize*align, - srcchannels, srctype, data, align, AL_TRUE); - if(err != AL_NO_ERROR) - SET_ERROR_AND_GOTO(context, err, done); - break; - - case UserFmtIMA4: - framesize = (align-1)/2 + 4; - framesize *= ChannelsFromUserFmt(srcchannels); - if((size%framesize) != 0) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - - switch(srcchannels) - { - case UserFmtMono: newformat = AL_FORMAT_MONO16; break; - case UserFmtStereo: newformat = AL_FORMAT_STEREO16; break; - case UserFmtRear: newformat = AL_FORMAT_REAR16; break; - case UserFmtQuad: newformat = AL_FORMAT_QUAD16; break; - case UserFmtX51: newformat = AL_FORMAT_51CHN16; break; - case UserFmtX61: newformat = AL_FORMAT_61CHN16; break; - case UserFmtX71: newformat = AL_FORMAT_71CHN16; break; - case UserFmtBFormat2D: newformat = AL_FORMAT_BFORMAT2D_16; break; - case UserFmtBFormat3D: newformat = AL_FORMAT_BFORMAT3D_16; break; - } - err = LoadData(albuf, freq, newformat, size/framesize*align, - srcchannels, srctype, data, align, AL_TRUE); - if(err != AL_NO_ERROR) - SET_ERROR_AND_GOTO(context, err, done); - break; - - case UserFmtMSADPCM: - framesize = (align-2)/2 + 7; - framesize *= ChannelsFromUserFmt(srcchannels); - if((size%framesize) != 0) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - - switch(srcchannels) - { - case UserFmtMono: newformat = AL_FORMAT_MONO16; break; - case UserFmtStereo: newformat = AL_FORMAT_STEREO16; break; - case UserFmtRear: newformat = AL_FORMAT_REAR16; break; - case UserFmtQuad: newformat = AL_FORMAT_QUAD16; break; - case UserFmtX51: newformat = AL_FORMAT_51CHN16; break; - case UserFmtX61: newformat = AL_FORMAT_61CHN16; break; - case UserFmtX71: newformat = AL_FORMAT_71CHN16; break; - case UserFmtBFormat2D: newformat = AL_FORMAT_BFORMAT2D_16; break; - case UserFmtBFormat3D: newformat = AL_FORMAT_BFORMAT3D_16; break; - } - err = LoadData(albuf, freq, newformat, size/framesize*align, - srcchannels, srctype, data, align, AL_TRUE); - if(err != AL_NO_ERROR) - SET_ERROR_AND_GOTO(context, err, done); - break; + ALbitfieldSOFT unavailable = (albuf->Access^access) & access; + if(UNLIKELY(ReadRef(&albuf->ref) != 0 && !(access&AL_MAP_PERSISTENT_BIT_SOFT))) + alSetError(context, AL_INVALID_OPERATION, + "Mapping in-use buffer %u without persistent mapping", buffer); + else if(UNLIKELY(albuf->MappedAccess != 0)) + alSetError(context, AL_INVALID_OPERATION, "Mapping already-mapped buffer %u", buffer); + else if(UNLIKELY((unavailable&AL_MAP_READ_BIT_SOFT))) + alSetError(context, AL_INVALID_VALUE, + "Mapping buffer %u for reading without read access", buffer); + else if(UNLIKELY((unavailable&AL_MAP_WRITE_BIT_SOFT))) + alSetError(context, AL_INVALID_VALUE, + "Mapping buffer %u for writing without write access", buffer); + else if(UNLIKELY((unavailable&AL_MAP_PERSISTENT_BIT_SOFT))) + alSetError(context, AL_INVALID_VALUE, + "Mapping buffer %u persistently without persistent access", buffer); + else if(UNLIKELY(offset < 0 || offset >= albuf->OriginalSize || + length <= 0 || length > albuf->OriginalSize - offset)) + alSetError(context, AL_INVALID_VALUE, "Mapping invalid range %d+%d for buffer %u", + offset, length, buffer); + else + { + retval = (ALbyte*)albuf->data + offset; + albuf->MappedAccess = access; + albuf->MappedOffset = offset; + albuf->MappedSize = length; + } } + UnlockBufferList(device); + + ALCcontext_DecRef(context); + return retval; +} + +AL_API void AL_APIENTRY alUnmapBufferSOFT(ALuint buffer) +{ + ALCdevice *device; + ALCcontext *context; + ALbuffer *albuf; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + LockBufferList(device); + if((albuf=LookupBuffer(device, buffer)) == NULL) + alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer); + else if(albuf->MappedAccess == 0) + alSetError(context, AL_INVALID_OPERATION, "Unmapping unmapped buffer %u", buffer); + else + { + albuf->MappedAccess = 0; + albuf->MappedOffset = 0; + albuf->MappedSize = 0; + } + UnlockBufferList(device); + + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alFlushMappedBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length) +{ + ALCdevice *device; + ALCcontext *context; + ALbuffer *albuf; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + LockBufferList(device); + if(UNLIKELY((albuf=LookupBuffer(device, buffer)) == NULL)) + alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer); + else if(UNLIKELY(!(albuf->MappedAccess&AL_MAP_WRITE_BIT_SOFT))) + alSetError(context, AL_INVALID_OPERATION, + "Flushing buffer %u while not mapped for writing", buffer); + else if(UNLIKELY(offset < albuf->MappedOffset || + offset >= albuf->MappedOffset+albuf->MappedSize || + length <= 0 || length > albuf->MappedOffset+albuf->MappedSize-offset)) + alSetError(context, AL_INVALID_VALUE, "Flushing invalid range %d+%d on buffer %u", + offset, length, buffer); + else + { + /* FIXME: Need to use some method of double-buffering for the mixer and + * app to hold separate memory, which can be safely transfered + * asynchronously. Currently we just say the app shouldn't write where + * OpenAL's reading, and hope for the best... + */ + ATOMIC_THREAD_FENCE(almemory_order_seq_cst); + } + UnlockBufferList(device); -done: - UnlockBuffersRead(device); ALCcontext_DecRef(context); } AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) { enum UserFmtChannels srcchannels = UserFmtMono; - enum UserFmtType srctype = UserFmtByte; + enum UserFmtType srctype = UserFmtUByte; ALCdevice *device; ALCcontext *context; ALbuffer *albuf; - ALuint byte_align; - ALuint channels; - ALuint bytes; - ALsizei align; context = GetContextRef(); if(!context) return; device = context->Device; - LockBuffersRead(device); - if((albuf=LookupBuffer(device, buffer)) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - if(!(length >= 0 && offset >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - if(DecomposeUserFormat(format, &srcchannels, &srctype) == AL_FALSE) - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); - - WriteLock(&albuf->lock); - align = ATOMIC_LOAD(&albuf->UnpackAlign); - if(SanitizeAlignment(srctype, &align) == AL_FALSE) - { - WriteUnlock(&albuf->lock); - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - } - if(srcchannels != albuf->OriginalChannels || srctype != albuf->OriginalType) - { - WriteUnlock(&albuf->lock); - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); - } - if(align != albuf->OriginalAlign) - { - WriteUnlock(&albuf->lock); - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); - } - - if(albuf->OriginalType == UserFmtIMA4) - { - byte_align = (albuf->OriginalAlign-1)/2 + 4; - byte_align *= ChannelsFromUserFmt(albuf->OriginalChannels); - } - else if(albuf->OriginalType == UserFmtMSADPCM) - { - byte_align = (albuf->OriginalAlign-2)/2 + 7; - byte_align *= ChannelsFromUserFmt(albuf->OriginalChannels); - } + LockBufferList(device); + if(UNLIKELY((albuf=LookupBuffer(device, buffer)) == NULL)) + alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer); + else if(UNLIKELY(DecomposeUserFormat(format, &srcchannels, &srctype) == AL_FALSE)) + alSetError(context, AL_INVALID_ENUM, "Invalid format 0x%04x", format); else { - byte_align = albuf->OriginalAlign; - byte_align *= FrameSizeFromUserFmt(albuf->OriginalChannels, - albuf->OriginalType); + ALsizei unpack_align, align; + ALsizei byte_align; + ALsizei frame_size; + ALsizei num_chans; + void *dst; + + unpack_align = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign); + align = SanitizeAlignment(srctype, unpack_align); + if(UNLIKELY(align < 1)) + alSetError(context, AL_INVALID_VALUE, "Invalid unpack alignment %d", unpack_align); + else if(UNLIKELY((long)srcchannels != (long)albuf->FmtChannels || + srctype != albuf->OriginalType)) + alSetError(context, AL_INVALID_ENUM, "Unpacking data with mismatched format"); + else if(UNLIKELY(align != albuf->OriginalAlign)) + alSetError(context, AL_INVALID_VALUE, + "Unpacking data with alignment %u does not match original alignment %u", + align, albuf->OriginalAlign); + else if(UNLIKELY(albuf->MappedAccess != 0)) + alSetError(context, AL_INVALID_OPERATION, "Unpacking data into mapped buffer %u", + buffer); + else + { + num_chans = ChannelsFromFmt(albuf->FmtChannels); + frame_size = num_chans * BytesFromFmt(albuf->FmtType); + if(albuf->OriginalType == UserFmtIMA4) + byte_align = ((align-1)/2 + 4) * num_chans; + else if(albuf->OriginalType == UserFmtMSADPCM) + byte_align = ((align-2)/2 + 7) * num_chans; + else + byte_align = align * frame_size; + + if(UNLIKELY(offset < 0 || length < 0 || offset > albuf->OriginalSize || + length > albuf->OriginalSize-offset)) + alSetError(context, AL_INVALID_VALUE, "Invalid data sub-range %d+%d on buffer %u", + offset, length, buffer); + else if(UNLIKELY((offset%byte_align) != 0)) + alSetError(context, AL_INVALID_VALUE, + "Sub-range offset %d is not a multiple of frame size %d (%d unpack alignment)", + offset, byte_align, align); + else if(UNLIKELY((length%byte_align) != 0)) + alSetError(context, AL_INVALID_VALUE, + "Sub-range length %d is not a multiple of frame size %d (%d unpack alignment)", + length, byte_align, align); + else + { + /* offset -> byte offset, length -> sample count */ + offset = offset/byte_align * align * frame_size; + length = length/byte_align * align; + + dst = (ALbyte*)albuf->data + offset; + if(srctype == UserFmtIMA4 && albuf->FmtType == FmtShort) + Convert_ALshort_ALima4(dst, data, num_chans, length, align); + else if(srctype == UserFmtMSADPCM && albuf->FmtType == FmtShort) + Convert_ALshort_ALmsadpcm(dst, data, num_chans, length, align); + else + { + assert((long)srctype == (long)albuf->FmtType); + memcpy(dst, data, length*frame_size); + } + } + } } + UnlockBufferList(device); - if(offset > albuf->OriginalSize || length > albuf->OriginalSize-offset || - (offset%byte_align) != 0 || (length%byte_align) != 0) - { - WriteUnlock(&albuf->lock); - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - } - - channels = ChannelsFromFmt(albuf->FmtChannels); - bytes = BytesFromFmt(albuf->FmtType); - /* offset -> byte offset, length -> sample count */ - offset = offset/byte_align * channels*bytes; - length = length/byte_align * albuf->OriginalAlign; - - ConvertData((char*)albuf->data+offset, (enum UserFmtType)albuf->FmtType, - data, srctype, channels, length, align); - WriteUnlock(&albuf->lock); - -done: - UnlockBuffersRead(device); ALCcontext_DecRef(context); } -AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, - ALuint samplerate, ALenum internalformat, ALsizei samples, - ALenum channels, ALenum type, const ALvoid *data) +AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint UNUSED(buffer), + ALuint UNUSED(samplerate), ALenum UNUSED(internalformat), ALsizei UNUSED(samples), + ALenum UNUSED(channels), ALenum UNUSED(type), const ALvoid *UNUSED(data)) { - ALCdevice *device; ALCcontext *context; - ALbuffer *albuf; - ALsizei align; - ALenum err; context = GetContextRef(); if(!context) return; - device = context->Device; - LockBuffersRead(device); - if((albuf=LookupBuffer(device, buffer)) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - if(!(samples >= 0 && samplerate != 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - if(IsValidType(type) == AL_FALSE || IsValidChannels(channels) == AL_FALSE) - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_OPERATION, "alBufferSamplesSOFT not supported"); - align = ATOMIC_LOAD(&albuf->UnpackAlign); - if(SanitizeAlignment(type, &align) == AL_FALSE) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - if((samples%align) != 0) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - - err = LoadData(albuf, samplerate, internalformat, samples, - channels, type, data, align, AL_FALSE); - if(err != AL_NO_ERROR) - SET_ERROR_AND_GOTO(context, err, done); - -done: - UnlockBuffersRead(device); ALCcontext_DecRef(context); } -AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, - ALsizei offset, ALsizei samples, - ALenum channels, ALenum type, const ALvoid *data) +AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint UNUSED(buffer), + ALsizei UNUSED(offset), ALsizei UNUSED(samples), + ALenum UNUSED(channels), ALenum UNUSED(type), const ALvoid *UNUSED(data)) { - ALCdevice *device; ALCcontext *context; - ALbuffer *albuf; - ALsizei align; context = GetContextRef(); if(!context) return; - device = context->Device; - LockBuffersRead(device); - if((albuf=LookupBuffer(device, buffer)) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - if(!(samples >= 0 && offset >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - if(IsValidType(type) == AL_FALSE) - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_OPERATION, "alBufferSubSamplesSOFT not supported"); - WriteLock(&albuf->lock); - align = ATOMIC_LOAD(&albuf->UnpackAlign); - if(SanitizeAlignment(type, &align) == AL_FALSE) - { - WriteUnlock(&albuf->lock); - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - } - if(channels != (ALenum)albuf->FmtChannels) - { - WriteUnlock(&albuf->lock); - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); - } - if(offset > albuf->SampleLen || samples > albuf->SampleLen-offset) - { - WriteUnlock(&albuf->lock); - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - } - if((samples%align) != 0) - { - WriteUnlock(&albuf->lock); - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - } - - /* offset -> byte offset */ - offset *= FrameSizeFromFmt(albuf->FmtChannels, albuf->FmtType); - ConvertData((char*)albuf->data+offset, (enum UserFmtType)albuf->FmtType, - data, type, ChannelsFromFmt(albuf->FmtChannels), samples, align); - WriteUnlock(&albuf->lock); - -done: - UnlockBuffersRead(device); ALCcontext_DecRef(context); } -AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, - ALsizei offset, ALsizei samples, - ALenum channels, ALenum type, ALvoid *data) +AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint UNUSED(buffer), + ALsizei UNUSED(offset), ALsizei UNUSED(samples), + ALenum UNUSED(channels), ALenum UNUSED(type), ALvoid *UNUSED(data)) { - ALCdevice *device; ALCcontext *context; - ALbuffer *albuf; - ALsizei align; context = GetContextRef(); if(!context) return; - device = context->Device; - LockBuffersRead(device); - if((albuf=LookupBuffer(device, buffer)) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - if(!(samples >= 0 && offset >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - if(IsValidType(type) == AL_FALSE) - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_OPERATION, "alGetBufferSamplesSOFT not supported"); - ReadLock(&albuf->lock); - align = ATOMIC_LOAD(&albuf->PackAlign); - if(SanitizeAlignment(type, &align) == AL_FALSE) - { - ReadUnlock(&albuf->lock); - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - } - if(channels != (ALenum)albuf->FmtChannels) - { - ReadUnlock(&albuf->lock); - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); - } - if(offset > albuf->SampleLen || samples > albuf->SampleLen-offset) - { - ReadUnlock(&albuf->lock); - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - } - if((samples%align) != 0) - { - ReadUnlock(&albuf->lock); - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - } - - /* offset -> byte offset */ - offset *= FrameSizeFromFmt(albuf->FmtChannels, albuf->FmtType); - ConvertData(data, type, (char*)albuf->data+offset, (enum UserFmtType)albuf->FmtType, - ChannelsFromFmt(albuf->FmtChannels), samples, align); - ReadUnlock(&albuf->lock); - -done: - UnlockBuffersRead(device); ALCcontext_DecRef(context); } -AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format) +AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum UNUSED(format)) { - enum FmtChannels dstchannels; - enum FmtType dsttype; ALCcontext *context; - ALboolean ret; context = GetContextRef(); if(!context) return AL_FALSE; - ret = DecomposeFormat(format, &dstchannels, &dsttype); + alSetError(context, AL_INVALID_OPERATION, "alIsBufferFormatSupportedSOFT not supported"); ALCcontext_DecRef(context); - - return ret; + return AL_FALSE; } @@ -543,18 +464,16 @@ AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat UNUSED(va if(!context) return; device = context->Device; - LockBuffersRead(device); - if(LookupBuffer(device, buffer) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - - switch(param) + LockBufferList(device); + if(UNLIKELY(LookupBuffer(device, buffer) == NULL)) + alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer); + else switch(param) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param); } + UnlockBufferList(device); -done: - UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -568,18 +487,16 @@ AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat UNUSED(v if(!context) return; device = context->Device; - LockBuffersRead(device); - if(LookupBuffer(device, buffer) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - - switch(param) + LockBufferList(device); + if(UNLIKELY(LookupBuffer(device, buffer) == NULL)) + alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer); + else switch(param) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param); } + UnlockBufferList(device); -done: - UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -593,20 +510,18 @@ AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *v if(!context) return; device = context->Device; - LockBuffersRead(device); - if(LookupBuffer(device, buffer) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - - if(!(values)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - switch(param) + LockBufferList(device); + if(UNLIKELY(LookupBuffer(device, buffer) == NULL)) + alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer); + else if(UNLIKELY(!values)) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); + else switch(param) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param); } + UnlockBufferList(device); -done: - UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -621,30 +536,30 @@ AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value) if(!context) return; device = context->Device; - LockBuffersRead(device); - if((albuf=LookupBuffer(device, buffer)) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - - switch(param) + LockBufferList(device); + if(UNLIKELY((albuf=LookupBuffer(device, buffer)) == NULL)) + alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer); + else switch(param) { case AL_UNPACK_BLOCK_ALIGNMENT_SOFT: - if(!(value >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - ATOMIC_STORE(&albuf->UnpackAlign, value); + if(UNLIKELY(value < 0)) + alSetError(context, AL_INVALID_VALUE, "Invalid unpack block alignment %d", value); + else + ATOMIC_STORE_SEQ(&albuf->UnpackAlign, value); break; case AL_PACK_BLOCK_ALIGNMENT_SOFT: - if(!(value >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - ATOMIC_STORE(&albuf->PackAlign, value); + if(UNLIKELY(value < 0)) + alSetError(context, AL_INVALID_VALUE, "Invalid pack block alignment %d", value); + else + ATOMIC_STORE_SEQ(&albuf->PackAlign, value); break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param); } + UnlockBufferList(device); -done: - UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -658,16 +573,16 @@ AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint UNUSED(val if(!context) return; device = context->Device; - if(LookupBuffer(device, buffer) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - - switch(param) + LockBufferList(device); + if(UNLIKELY(LookupBuffer(device, buffer) == NULL)) + alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer); + else switch(param) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param); } + UnlockBufferList(device); -done: ALCcontext_DecRef(context); } @@ -693,39 +608,33 @@ AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *val if(!context) return; device = context->Device; - LockBuffersRead(device); - if((albuf=LookupBuffer(device, buffer)) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - - if(!(values)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - switch(param) + LockBufferList(device); + if(UNLIKELY((albuf=LookupBuffer(device, buffer)) == NULL)) + alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer); + else if(UNLIKELY(!values)) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); + else switch(param) { case AL_LOOP_POINTS_SOFT: - WriteLock(&albuf->lock); - if(ReadRef(&albuf->ref) != 0) + if(UNLIKELY(ReadRef(&albuf->ref) != 0)) + alSetError(context, AL_INVALID_OPERATION, "Modifying in-use buffer %u's loop points", + buffer); + else if(UNLIKELY(values[0] >= values[1] || values[0] < 0 || values[1] > albuf->SampleLen)) + alSetError(context, AL_INVALID_VALUE, "Invalid loop point range %d -> %d o buffer %u", + values[0], values[1], buffer); + else { - WriteUnlock(&albuf->lock); - SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + albuf->LoopStart = values[0]; + albuf->LoopEnd = values[1]; } - if(values[0] >= values[1] || values[0] < 0 || - values[1] > albuf->SampleLen) - { - WriteUnlock(&albuf->lock); - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - } - - albuf->LoopStart = values[0]; - albuf->LoopEnd = values[1]; - WriteUnlock(&albuf->lock); break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x", + param); } + UnlockBufferList(device); -done: - UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -740,29 +649,18 @@ AL_API ALvoid AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *val if(!context) return; device = context->Device; - LockBuffersRead(device); - if((albuf=LookupBuffer(device, buffer)) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - - if(!(value)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - switch(param) + LockBufferList(device); + if(UNLIKELY((albuf=LookupBuffer(device, buffer)) == NULL)) + alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer); + else if(UNLIKELY(!value)) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); + else switch(param) { - case AL_SEC_LENGTH_SOFT: - ReadLock(&albuf->lock); - if(albuf->SampleLen != 0) - *value = albuf->SampleLen / (ALfloat)albuf->Frequency; - else - *value = 0.0f; - ReadUnlock(&albuf->lock); - break; - default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param); } + UnlockBufferList(device); -done: - UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -776,20 +674,18 @@ AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *valu if(!context) return; device = context->Device; - LockBuffersRead(device); - if(LookupBuffer(device, buffer) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - - if(!(value1 && value2 && value3)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - switch(param) + LockBufferList(device); + if(UNLIKELY(LookupBuffer(device, buffer) == NULL)) + alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer); + else if(UNLIKELY(!value1 || !value2 || !value3)) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); + else switch(param) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param); } + UnlockBufferList(device); -done: - UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -810,20 +706,18 @@ AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *valu if(!context) return; device = context->Device; - LockBuffersRead(device); - if(LookupBuffer(device, buffer) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - - if(!(values)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - switch(param) + LockBufferList(device); + if(UNLIKELY(LookupBuffer(device, buffer) == NULL)) + alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer); + else if(UNLIKELY(!values)) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); + else switch(param) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param); } + UnlockBufferList(device); -done: - UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -838,13 +732,12 @@ AL_API ALvoid AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value if(!context) return; device = context->Device; - LockBuffersRead(device); - if((albuf=LookupBuffer(device, buffer)) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - - if(!(value)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - switch(param) + LockBufferList(device); + if(UNLIKELY((albuf=LookupBuffer(device, buffer)) == NULL)) + alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer); + else if(UNLIKELY(!value)) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); + else switch(param) { case AL_FREQUENCY: *value = albuf->Frequency; @@ -859,38 +752,23 @@ AL_API ALvoid AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value break; case AL_SIZE: - ReadLock(&albuf->lock); *value = albuf->SampleLen * FrameSizeFromFmt(albuf->FmtChannels, albuf->FmtType); - ReadUnlock(&albuf->lock); - break; - - case AL_INTERNAL_FORMAT_SOFT: - *value = albuf->Format; - break; - - case AL_BYTE_LENGTH_SOFT: - *value = albuf->OriginalSize; - break; - - case AL_SAMPLE_LENGTH_SOFT: - *value = albuf->SampleLen; break; case AL_UNPACK_BLOCK_ALIGNMENT_SOFT: - *value = ATOMIC_LOAD(&albuf->UnpackAlign); + *value = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign); break; case AL_PACK_BLOCK_ALIGNMENT_SOFT: - *value = ATOMIC_LOAD(&albuf->PackAlign); + *value = ATOMIC_LOAD_SEQ(&albuf->PackAlign); break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param); } + UnlockBufferList(device); -done: - UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -904,20 +782,18 @@ AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1 if(!context) return; device = context->Device; - LockBuffersRead(device); - if(LookupBuffer(device, buffer) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - - if(!(value1 && value2 && value3)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - switch(param) + LockBufferList(device); + if(UNLIKELY(LookupBuffer(device, buffer) == NULL)) + alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer); + else if(UNLIKELY(!value1 || !value2 || !value3)) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); + else switch(param) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param); } + UnlockBufferList(device); -done: - UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -947,65 +823,146 @@ AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values if(!context) return; device = context->Device; - LockBuffersRead(device); - if((albuf=LookupBuffer(device, buffer)) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - - if(!(values)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - switch(param) + LockBufferList(device); + if(UNLIKELY((albuf=LookupBuffer(device, buffer)) == NULL)) + alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer); + else if(UNLIKELY(!values)) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); + else switch(param) { case AL_LOOP_POINTS_SOFT: - ReadLock(&albuf->lock); values[0] = albuf->LoopStart; values[1] = albuf->LoopEnd; - ReadUnlock(&albuf->lock); break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x", + param); } + UnlockBufferList(device); -done: - UnlockBuffersRead(device); ALCcontext_DecRef(context); } +static const ALchar *NameFromUserFmtType(enum UserFmtType type) +{ + switch(type) + { + case UserFmtUByte: return "Unsigned Byte"; + case UserFmtShort: return "Signed Short"; + case UserFmtFloat: return "Float32"; + case UserFmtDouble: return "Float64"; + case UserFmtMulaw: return "muLaw"; + case UserFmtAlaw: return "aLaw"; + case UserFmtIMA4: return "IMA4 ADPCM"; + case UserFmtMSADPCM: return "MSADPCM"; + } + return ""; +} + /* * LoadData * - * Loads the specified data into the buffer, using the specified formats. - * Currently, the new format must have the same channel configuration as the - * original format. + * Loads the specified data into the buffer, using the specified format. */ -ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, ALboolean storesrc) +static void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALuint freq, ALsizei size, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALbitfieldSOFT access) { enum FmtChannels DstChannels = FmtMono; - enum FmtType DstType = FmtByte; - ALuint NewChannels, NewBytes; - ALuint64 newsize; + enum FmtType DstType = FmtUByte; + ALsizei NumChannels, FrameSize; + ALsizei SrcByteAlign; + ALsizei unpackalign; + ALsizei newsize; + ALsizei frames; + ALsizei align; - if(DecomposeFormat(NewFormat, &DstChannels, &DstType) == AL_FALSE) - return AL_INVALID_ENUM; - if((long)SrcChannels != (long)DstChannels) - return AL_INVALID_ENUM; + if(UNLIKELY(ReadRef(&ALBuf->ref) != 0 || ALBuf->MappedAccess != 0)) + SETERR_RETURN(context, AL_INVALID_OPERATION,, "Modifying storage for in-use buffer %u", + ALBuf->id); - NewChannels = ChannelsFromFmt(DstChannels); - NewBytes = BytesFromFmt(DstType); - - newsize = frames; - newsize *= NewBytes; - newsize *= NewChannels; - if(newsize > INT_MAX) - return AL_OUT_OF_MEMORY; - - WriteLock(&ALBuf->lock); - if(ReadRef(&ALBuf->ref) != 0) + /* Currently no channel configurations need to be converted. */ + switch(SrcChannels) { - WriteUnlock(&ALBuf->lock); - return AL_INVALID_OPERATION; + case UserFmtMono: DstChannels = FmtMono; break; + case UserFmtStereo: DstChannels = FmtStereo; break; + case UserFmtRear: DstChannels = FmtRear; break; + case UserFmtQuad: DstChannels = FmtQuad; break; + case UserFmtX51: DstChannels = FmtX51; break; + case UserFmtX61: DstChannels = FmtX61; break; + case UserFmtX71: DstChannels = FmtX71; break; + case UserFmtBFormat2D: DstChannels = FmtBFormat2D; break; + case UserFmtBFormat3D: DstChannels = FmtBFormat3D; break; } + if(UNLIKELY((long)SrcChannels != (long)DstChannels)) + SETERR_RETURN(context, AL_INVALID_ENUM,, "Invalid format"); + + /* IMA4 and MSADPCM convert to 16-bit short. */ + switch(SrcType) + { + case UserFmtUByte: DstType = FmtUByte; break; + case UserFmtShort: DstType = FmtShort; break; + case UserFmtFloat: DstType = FmtFloat; break; + case UserFmtDouble: DstType = FmtDouble; break; + case UserFmtAlaw: DstType = FmtAlaw; break; + case UserFmtMulaw: DstType = FmtMulaw; break; + case UserFmtIMA4: DstType = FmtShort; break; + case UserFmtMSADPCM: DstType = FmtShort; break; + } + + /* TODO: Currently we can only map samples when they're not converted. To + * allow it would need some kind of double-buffering to hold onto a copy of + * the original data. + */ + if((access&MAP_READ_WRITE_FLAGS)) + { + if(UNLIKELY((long)SrcType != (long)DstType)) + SETERR_RETURN(context, AL_INVALID_VALUE,, "%s samples cannot be mapped", + NameFromUserFmtType(SrcType)); + } + + unpackalign = ATOMIC_LOAD_SEQ(&ALBuf->UnpackAlign); + if(UNLIKELY((align=SanitizeAlignment(SrcType, unpackalign)) < 1)) + SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid unpack alignment %d for %s samples", + unpackalign, NameFromUserFmtType(SrcType)); + + if((access&AL_PRESERVE_DATA_BIT_SOFT)) + { + /* Can only preserve data with the same format and alignment. */ + if(UNLIKELY(ALBuf->FmtChannels != DstChannels || ALBuf->OriginalType != SrcType)) + SETERR_RETURN(context, AL_INVALID_VALUE,, "Preserving data of mismatched format"); + if(UNLIKELY(ALBuf->OriginalAlign != align)) + SETERR_RETURN(context, AL_INVALID_VALUE,, "Preserving data of mismatched alignment"); + } + + /* Convert the input/source size in bytes to sample frames using the unpack + * block alignment. + */ + if(SrcType == UserFmtIMA4) + SrcByteAlign = ((align-1)/2 + 4) * ChannelsFromUserFmt(SrcChannels); + else if(SrcType == UserFmtMSADPCM) + SrcByteAlign = ((align-2)/2 + 7) * ChannelsFromUserFmt(SrcChannels); + else + SrcByteAlign = align * FrameSizeFromUserFmt(SrcChannels, SrcType); + if(UNLIKELY((size%SrcByteAlign) != 0)) + SETERR_RETURN(context, AL_INVALID_VALUE,, + "Data size %d is not a multiple of frame size %d (%d unpack alignment)", + size, SrcByteAlign, align); + + if(UNLIKELY(size / SrcByteAlign > INT_MAX / align)) + SETERR_RETURN(context, AL_OUT_OF_MEMORY,, + "Buffer size overflow, %d blocks x %d samples per block", size/SrcByteAlign, align); + frames = size / SrcByteAlign * align; + + /* Convert the sample frames to the number of bytes needed for internal + * storage. + */ + NumChannels = ChannelsFromFmt(DstChannels); + FrameSize = NumChannels * BytesFromFmt(DstType); + if(UNLIKELY(frames > INT_MAX/FrameSize)) + SETERR_RETURN(context, AL_OUT_OF_MEMORY,, + "Buffer size overflow, %d frames x %d bytes per frame", frames, FrameSize); + newsize = frames*FrameSize; /* Round up to the next 16-byte multiple. This could reallocate only when * increasing or the new size is less than half the current, but then the @@ -1013,81 +970,67 @@ ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames, * usage, and reporting the real size could cause problems for apps that * use AL_SIZE to try to get the buffer's play length. */ - newsize = (newsize+15) & ~0xf; + if(LIKELY(newsize <= INT_MAX-15)) + newsize = (newsize+15) & ~0xf; if(newsize != ALBuf->BytesAlloc) { - void *temp = al_calloc(16, (size_t)newsize); - if(!temp && newsize) + void *temp = al_malloc(16, (size_t)newsize); + if(UNLIKELY(!temp && newsize)) + SETERR_RETURN(context, AL_OUT_OF_MEMORY,, "Failed to allocate %d bytes of storage", + newsize); + if((access&AL_PRESERVE_DATA_BIT_SOFT)) { - WriteUnlock(&ALBuf->lock); - return AL_OUT_OF_MEMORY; + ALsizei tocopy = mini(newsize, ALBuf->BytesAlloc); + if(tocopy > 0) memcpy(temp, ALBuf->data, tocopy); } al_free(ALBuf->data); ALBuf->data = temp; - ALBuf->BytesAlloc = (ALuint)newsize; + ALBuf->BytesAlloc = newsize; } - if(data != NULL) - ConvertData(ALBuf->data, (enum UserFmtType)DstType, data, SrcType, NewChannels, frames, align); - - if(storesrc) + if(SrcType == UserFmtIMA4) { - ALBuf->OriginalChannels = SrcChannels; - ALBuf->OriginalType = SrcType; - if(SrcType == UserFmtIMA4) - { - ALsizei byte_align = ((align-1)/2 + 4) * ChannelsFromUserFmt(SrcChannels); - ALBuf->OriginalSize = frames / align * byte_align; - ALBuf->OriginalAlign = align; - } - else if(SrcType == UserFmtMSADPCM) - { - ALsizei byte_align = ((align-2)/2 + 7) * ChannelsFromUserFmt(SrcChannels); - ALBuf->OriginalSize = frames / align * byte_align; - ALBuf->OriginalAlign = align; - } - else - { - ALBuf->OriginalSize = frames * FrameSizeFromUserFmt(SrcChannels, SrcType); - ALBuf->OriginalAlign = 1; - } + assert(DstType == FmtShort); + if(data != NULL && ALBuf->data != NULL) + Convert_ALshort_ALima4(ALBuf->data, data, NumChannels, frames, align); + ALBuf->OriginalAlign = align; + } + else if(SrcType == UserFmtMSADPCM) + { + assert(DstType == FmtShort); + if(data != NULL && ALBuf->data != NULL) + Convert_ALshort_ALmsadpcm(ALBuf->data, data, NumChannels, frames, align); + ALBuf->OriginalAlign = align; } else { - ALBuf->OriginalChannels = (enum UserFmtChannels)DstChannels; - ALBuf->OriginalType = (enum UserFmtType)DstType; - ALBuf->OriginalSize = frames * NewBytes * NewChannels; - ALBuf->OriginalAlign = 1; + assert((long)SrcType == (long)DstType); + if(data != NULL && ALBuf->data != NULL) + memcpy(ALBuf->data, data, frames*FrameSize); + ALBuf->OriginalAlign = 1; } + ALBuf->OriginalSize = size; + ALBuf->OriginalType = SrcType; ALBuf->Frequency = freq; ALBuf->FmtChannels = DstChannels; ALBuf->FmtType = DstType; - ALBuf->Format = NewFormat; + ALBuf->Access = access; ALBuf->SampleLen = frames; ALBuf->LoopStart = 0; ALBuf->LoopEnd = ALBuf->SampleLen; - - WriteUnlock(&ALBuf->lock); - return AL_NO_ERROR; } -ALuint BytesFromUserFmt(enum UserFmtType type) +ALsizei BytesFromUserFmt(enum UserFmtType type) { switch(type) { - case UserFmtByte: return sizeof(ALbyte); case UserFmtUByte: return sizeof(ALubyte); case UserFmtShort: return sizeof(ALshort); - case UserFmtUShort: return sizeof(ALushort); - case UserFmtInt: return sizeof(ALint); - case UserFmtUInt: return sizeof(ALuint); case UserFmtFloat: return sizeof(ALfloat); case UserFmtDouble: return sizeof(ALdouble); - case UserFmtByte3: return sizeof(ALbyte[3]); - case UserFmtUByte3: return sizeof(ALubyte[3]); case UserFmtMulaw: return sizeof(ALubyte); case UserFmtAlaw: return sizeof(ALubyte); case UserFmtIMA4: break; /* not handled here */ @@ -1095,7 +1038,7 @@ ALuint BytesFromUserFmt(enum UserFmtType type) } return 0; } -ALuint ChannelsFromUserFmt(enum UserFmtChannels chans) +ALsizei ChannelsFromUserFmt(enum UserFmtChannels chans) { switch(chans) { @@ -1190,17 +1133,20 @@ static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans, return AL_FALSE; } -ALuint BytesFromFmt(enum FmtType type) +ALsizei BytesFromFmt(enum FmtType type) { switch(type) { - case FmtByte: return sizeof(ALbyte); + case FmtUByte: return sizeof(ALubyte); case FmtShort: return sizeof(ALshort); case FmtFloat: return sizeof(ALfloat); + case FmtDouble: return sizeof(ALdouble); + case FmtMulaw: return sizeof(ALubyte); + case FmtAlaw: return sizeof(ALubyte); } return 0; } -ALuint ChannelsFromFmt(enum FmtChannels chans) +ALsizei ChannelsFromFmt(enum FmtChannels chans) { switch(chans) { @@ -1216,73 +1162,13 @@ ALuint ChannelsFromFmt(enum FmtChannels chans) } return 0; } -static ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum FmtType *type) + +static ALsizei SanitizeAlignment(enum UserFmtType type, ALsizei align) { - static const struct { - ALenum format; - enum FmtChannels channels; - enum FmtType type; - } list[] = { - { AL_MONO8_SOFT, FmtMono, FmtByte }, - { AL_MONO16_SOFT, FmtMono, FmtShort }, - { AL_MONO32F_SOFT, FmtMono, FmtFloat }, + if(align < 0) + return 0; - { AL_STEREO8_SOFT, FmtStereo, FmtByte }, - { AL_STEREO16_SOFT, FmtStereo, FmtShort }, - { AL_STEREO32F_SOFT, FmtStereo, FmtFloat }, - - { AL_REAR8_SOFT, FmtRear, FmtByte }, - { AL_REAR16_SOFT, FmtRear, FmtShort }, - { AL_REAR32F_SOFT, FmtRear, FmtFloat }, - - { AL_FORMAT_QUAD8_LOKI, FmtQuad, FmtByte }, - { AL_FORMAT_QUAD16_LOKI, FmtQuad, FmtShort }, - - { AL_QUAD8_SOFT, FmtQuad, FmtByte }, - { AL_QUAD16_SOFT, FmtQuad, FmtShort }, - { AL_QUAD32F_SOFT, FmtQuad, FmtFloat }, - - { AL_5POINT1_8_SOFT, FmtX51, FmtByte }, - { AL_5POINT1_16_SOFT, FmtX51, FmtShort }, - { AL_5POINT1_32F_SOFT, FmtX51, FmtFloat }, - - { AL_6POINT1_8_SOFT, FmtX61, FmtByte }, - { AL_6POINT1_16_SOFT, FmtX61, FmtShort }, - { AL_6POINT1_32F_SOFT, FmtX61, FmtFloat }, - - { AL_7POINT1_8_SOFT, FmtX71, FmtByte }, - { AL_7POINT1_16_SOFT, FmtX71, FmtShort }, - { AL_7POINT1_32F_SOFT, FmtX71, FmtFloat }, - - { AL_BFORMAT2D_8_SOFT, FmtBFormat2D, FmtByte }, - { AL_BFORMAT2D_16_SOFT, FmtBFormat2D, FmtShort }, - { AL_BFORMAT2D_32F_SOFT, FmtBFormat2D, FmtFloat }, - - { AL_BFORMAT3D_8_SOFT, FmtBFormat3D, FmtByte }, - { AL_BFORMAT3D_16_SOFT, FmtBFormat3D, FmtShort }, - { AL_BFORMAT3D_32F_SOFT, FmtBFormat3D, FmtFloat }, - }; - ALuint i; - - for(i = 0;i < COUNTOF(list);i++) - { - if(list[i].format == format) - { - *chans = list[i].channels; - *type = list[i].type; - return AL_TRUE; - } - } - - return AL_FALSE; -} - -static ALboolean SanitizeAlignment(enum UserFmtType type, ALsizei *align) -{ - if(*align < 0) - return AL_FALSE; - - if(*align == 0) + if(align == 0) { if(type == UserFmtIMA4) { @@ -1290,106 +1176,101 @@ static ALboolean SanitizeAlignment(enum UserFmtType type, ALsizei *align) * nVidia and Apple use 64+1 sample frames per block -> block_size=36 bytes per channel * Most PC sound software uses 2040+1 sample frames per block -> block_size=1024 bytes per channel */ - *align = 65; + return 65; } - else if(type == UserFmtMSADPCM) - *align = 64; - else - *align = 1; - return AL_TRUE; + if(type == UserFmtMSADPCM) + return 64; + return 1; } if(type == UserFmtIMA4) { /* IMA4 block alignment must be a multiple of 8, plus 1. */ - return ((*align)&7) == 1; + if((align&7) == 1) return align; + return 0; } if(type == UserFmtMSADPCM) { /* MSADPCM block alignment must be a multiple of 2. */ - /* FIXME: Too strict? Might only require align*channels to be a - * multiple of 2. */ - return ((*align)&1) == 0; + if((align&1) == 0) return align; + return 0; } - return AL_TRUE; + return align; } -static ALboolean IsValidType(ALenum type) -{ - switch(type) - { - case AL_BYTE_SOFT: - case AL_UNSIGNED_BYTE_SOFT: - case AL_SHORT_SOFT: - case AL_UNSIGNED_SHORT_SOFT: - case AL_INT_SOFT: - case AL_UNSIGNED_INT_SOFT: - case AL_FLOAT_SOFT: - case AL_DOUBLE_SOFT: - case AL_BYTE3_SOFT: - case AL_UNSIGNED_BYTE3_SOFT: - case AL_MULAW_SOFT: - return AL_TRUE; - } - return AL_FALSE; -} - -static ALboolean IsValidChannels(ALenum channels) -{ - switch(channels) - { - case AL_MONO_SOFT: - case AL_STEREO_SOFT: - case AL_REAR_SOFT: - case AL_QUAD_SOFT: - case AL_5POINT1_SOFT: - case AL_6POINT1_SOFT: - case AL_7POINT1_SOFT: - case AL_BFORMAT2D_SOFT: - case AL_BFORMAT3D_SOFT: - return AL_TRUE; - } - return AL_FALSE; -} - - -ALbuffer *NewBuffer(ALCcontext *context) +static ALbuffer *AllocBuffer(ALCcontext *context) { ALCdevice *device = context->Device; - ALbuffer *buffer; - ALenum err; + BufferSubList *sublist, *subend; + ALbuffer *buffer = NULL; + ALsizei lidx = 0; + ALsizei slidx; - buffer = al_calloc(16, sizeof(ALbuffer)); - if(!buffer) - SET_ERROR_AND_RETURN_VALUE(context, AL_OUT_OF_MEMORY, NULL); - RWLockInit(&buffer->lock); - - err = NewThunkEntry(&buffer->id); - if(err == AL_NO_ERROR) - err = InsertUIntMapEntry(&device->BufferMap, buffer->id, buffer); - if(err != AL_NO_ERROR) + almtx_lock(&device->BufferLock); + sublist = VECTOR_BEGIN(device->BufferList); + subend = VECTOR_END(device->BufferList); + for(;sublist != subend;++sublist) { - FreeThunkEntry(buffer->id); - memset(buffer, 0, sizeof(ALbuffer)); - al_free(buffer); - - SET_ERROR_AND_RETURN_VALUE(context, err, NULL); + if(sublist->FreeMask) + { + slidx = CTZ64(sublist->FreeMask); + buffer = sublist->Buffers + slidx; + break; + } + ++lidx; } + if(UNLIKELY(!buffer)) + { + const BufferSubList empty_sublist = { 0, NULL }; + /* Don't allocate so many list entries that the 32-bit ID could + * overflow... + */ + if(UNLIKELY(VECTOR_SIZE(device->BufferList) >= 1<<25)) + { + almtx_unlock(&device->BufferLock); + alSetError(context, AL_OUT_OF_MEMORY, "Too many buffers allocated"); + return NULL; + } + lidx = (ALsizei)VECTOR_SIZE(device->BufferList); + VECTOR_PUSH_BACK(device->BufferList, empty_sublist); + sublist = &VECTOR_BACK(device->BufferList); + sublist->FreeMask = ~U64(0); + sublist->Buffers = al_calloc(16, sizeof(ALbuffer)*64); + if(UNLIKELY(!sublist->Buffers)) + { + VECTOR_POP_BACK(device->BufferList); + almtx_unlock(&device->BufferLock); + alSetError(context, AL_OUT_OF_MEMORY, "Failed to allocate buffer batch"); + return NULL; + } + + slidx = 0; + buffer = sublist->Buffers + slidx; + } + + memset(buffer, 0, sizeof(*buffer)); + + /* Add 1 to avoid buffer ID 0. */ + buffer->id = ((lidx<<6) | slidx) + 1; + + sublist->FreeMask &= ~(U64(1)<BufferLock); return buffer; } -void DeleteBuffer(ALCdevice *device, ALbuffer *buffer) +static void FreeBuffer(ALCdevice *device, ALbuffer *buffer) { - RemoveBuffer(device, buffer->id); - FreeThunkEntry(buffer->id); + ALuint id = buffer->id - 1; + ALsizei lidx = id >> 6; + ALsizei slidx = id & 0x3f; al_free(buffer->data); - memset(buffer, 0, sizeof(*buffer)); - al_free(buffer); + + VECTOR_ELEM(device->BufferList, lidx).FreeMask |= U64(1) << slidx; } @@ -1400,16 +1281,25 @@ void DeleteBuffer(ALCdevice *device, ALbuffer *buffer) */ ALvoid ReleaseALBuffers(ALCdevice *device) { - ALsizei i; - for(i = 0;i < device->BufferMap.size;i++) + BufferSubList *sublist = VECTOR_BEGIN(device->BufferList); + BufferSubList *subend = VECTOR_END(device->BufferList); + size_t leftover = 0; + for(;sublist != subend;++sublist) { - ALbuffer *temp = device->BufferMap.values[i]; - device->BufferMap.values[i] = NULL; + ALuint64 usemask = ~sublist->FreeMask; + while(usemask) + { + ALsizei idx = CTZ64(usemask); + ALbuffer *buffer = sublist->Buffers + idx; - al_free(temp->data); + al_free(buffer->data); + memset(buffer, 0, sizeof(*buffer)); + ++leftover; - FreeThunkEntry(temp->id); - memset(temp, 0, sizeof(ALbuffer)); - al_free(temp); + usemask &= ~(U64(1) << idx); + } + sublist->FreeMask = ~usemask; } + if(leftover > 0) + WARN("(%p) Deleted "SZFMT" Buffer%s\n", device, leftover, (leftover==1)?"":"s"); } diff --git a/Engine/lib/openal-soft/OpenAL32/alEffect.c b/Engine/lib/openal-soft/OpenAL32/alEffect.c index 65a4dcb68..e7dc6aced 100644 --- a/Engine/lib/openal-soft/OpenAL32/alEffect.c +++ b/Engine/lib/openal-soft/OpenAL32/alEffect.c @@ -28,26 +28,51 @@ #include "AL/alc.h" #include "alMain.h" #include "alEffect.h" -#include "alThunk.h" #include "alError.h" -ALboolean DisabledEffects[MAX_EFFECTS]; - -extern inline void LockEffectsRead(ALCdevice *device); -extern inline void UnlockEffectsRead(ALCdevice *device); -extern inline void LockEffectsWrite(ALCdevice *device); -extern inline void UnlockEffectsWrite(ALCdevice *device); -extern inline struct ALeffect *LookupEffect(ALCdevice *device, ALuint id); -extern inline struct ALeffect *RemoveEffect(ALCdevice *device, ALuint id); +extern inline void LockEffectList(ALCdevice *device); +extern inline void UnlockEffectList(ALCdevice *device); extern inline ALboolean IsReverbEffect(ALenum type); +const struct EffectList EffectList[EFFECTLIST_SIZE] = { + { "eaxreverb", EAXREVERB_EFFECT, AL_EFFECT_EAXREVERB }, + { "reverb", REVERB_EFFECT, AL_EFFECT_REVERB }, + { "chorus", CHORUS_EFFECT, AL_EFFECT_CHORUS }, + { "compressor", COMPRESSOR_EFFECT, AL_EFFECT_COMPRESSOR }, + { "distortion", DISTORTION_EFFECT, AL_EFFECT_DISTORTION }, + { "echo", ECHO_EFFECT, AL_EFFECT_ECHO }, + { "equalizer", EQUALIZER_EFFECT, AL_EFFECT_EQUALIZER }, + { "flanger", FLANGER_EFFECT, AL_EFFECT_FLANGER }, + { "modulator", MODULATOR_EFFECT, AL_EFFECT_RING_MODULATOR }, + { "pshifter", PSHIFTER_EFFECT, AL_EFFECT_PITCH_SHIFTER }, + { "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT }, + { "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_DIALOGUE }, +}; + +ALboolean DisabledEffects[MAX_EFFECTS]; + +static ALeffect *AllocEffect(ALCcontext *context); +static void FreeEffect(ALCdevice *device, ALeffect *effect); static void InitEffectParams(ALeffect *effect, ALenum type); +static inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) +{ + EffectSubList *sublist; + ALuint lidx = (id-1) >> 6; + ALsizei slidx = (id-1) & 0x3f; + + if(UNLIKELY(lidx >= VECTOR_SIZE(device->EffectList))) + return NULL; + sublist = &VECTOR_ELEM(device->EffectList, lidx); + if(UNLIKELY(sublist->FreeMask & (U64(1)<Effects + slidx; +} + AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects) { - ALCdevice *device; ALCcontext *context; ALsizei cur; @@ -55,37 +80,18 @@ AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects) if(!context) return; if(!(n >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - - device = context->Device; - for(cur = 0;cur < n;cur++) + alSetError(context, AL_INVALID_VALUE, "Generating %d effects", n); + else for(cur = 0;cur < n;cur++) { - ALeffect *effect = al_calloc(16, sizeof(ALeffect)); - ALenum err = AL_OUT_OF_MEMORY; - if(!effect || (err=InitEffect(effect)) != AL_NO_ERROR) + ALeffect *effect = AllocEffect(context); + if(!effect) { - al_free(effect); alDeleteEffects(cur, effects); - SET_ERROR_AND_GOTO(context, err, done); + break; } - - err = NewThunkEntry(&effect->id); - if(err == AL_NO_ERROR) - err = InsertUIntMapEntry(&device->EffectMap, effect->id, effect); - if(err != AL_NO_ERROR) - { - FreeThunkEntry(effect->id); - memset(effect, 0, sizeof(ALeffect)); - al_free(effect); - - alDeleteEffects(cur, effects); - SET_ERROR_AND_GOTO(context, err, done); - } - effects[cur] = effect->id; } -done: ALCcontext_DecRef(context); } @@ -100,26 +106,22 @@ AL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects) if(!context) return; device = context->Device; - LockEffectsWrite(device); + LockEffectList(device); if(!(n >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Deleting %d effects", n); for(i = 0;i < n;i++) { if(effects[i] && LookupEffect(device, effects[i]) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect ID %u", effects[i]); } for(i = 0;i < n;i++) { - if((effect=RemoveEffect(device, effects[i])) == NULL) - continue; - FreeThunkEntry(effect->id); - - memset(effect, 0, sizeof(*effect)); - al_free(effect); + if((effect=LookupEffect(device, effects[i])) != NULL) + FreeEffect(device, effect); } done: - UnlockEffectsWrite(device); + UnlockEffectList(device); ALCcontext_DecRef(context); } @@ -131,10 +133,10 @@ AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect) Context = GetContextRef(); if(!Context) return AL_FALSE; - LockEffectsRead(Context->Device); + LockEffectList(Context->Device); result = ((!effect || LookupEffect(Context->Device, effect)) ? AL_TRUE : AL_FALSE); - UnlockEffectsRead(Context->Device); + UnlockEffectList(Context->Device); ALCcontext_DecRef(Context); @@ -151,16 +153,16 @@ AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value) if(!Context) return; Device = Context->Device; - LockEffectsWrite(Device); + LockEffectList(Device); if((ALEffect=LookupEffect(Device, effect)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid effect ID %u", effect); else { if(param == AL_EFFECT_TYPE) { ALboolean isOk = (value == AL_EFFECT_NULL); ALint i; - for(i = 0;!isOk && EffectList[i].val;i++) + for(i = 0;!isOk && i < EFFECTLIST_SIZE;i++) { if(value == EffectList[i].val && !DisabledEffects[EffectList[i].type]) @@ -170,15 +172,15 @@ AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value) if(isOk) InitEffectParams(ALEffect, value); else - alSetError(Context, AL_INVALID_VALUE); + alSetError(Context, AL_INVALID_VALUE, "Effect type 0x%04x not supported", value); } else { /* Call the appropriate handler */ - V(ALEffect,setParami)(Context, param, value); + ALeffect_setParami(ALEffect, Context, param, value); } } - UnlockEffectsWrite(Device); + UnlockEffectList(Device); ALCcontext_DecRef(Context); } @@ -200,15 +202,15 @@ AL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *v if(!Context) return; Device = Context->Device; - LockEffectsWrite(Device); + LockEffectList(Device); if((ALEffect=LookupEffect(Device, effect)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid effect ID %u", effect); else { /* Call the appropriate handler */ - V(ALEffect,setParamiv)(Context, param, values); + ALeffect_setParamiv(ALEffect, Context, param, values); } - UnlockEffectsWrite(Device); + UnlockEffectList(Device); ALCcontext_DecRef(Context); } @@ -223,15 +225,15 @@ AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat value) if(!Context) return; Device = Context->Device; - LockEffectsWrite(Device); + LockEffectList(Device); if((ALEffect=LookupEffect(Device, effect)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid effect ID %u", effect); else { /* Call the appropriate handler */ - V(ALEffect,setParamf)(Context, param, value); + ALeffect_setParamf(ALEffect, Context, param, value); } - UnlockEffectsWrite(Device); + UnlockEffectList(Device); ALCcontext_DecRef(Context); } @@ -246,15 +248,15 @@ AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat if(!Context) return; Device = Context->Device; - LockEffectsWrite(Device); + LockEffectList(Device); if((ALEffect=LookupEffect(Device, effect)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid effect ID %u", effect); else { /* Call the appropriate handler */ - V(ALEffect,setParamfv)(Context, param, values); + ALeffect_setParamfv(ALEffect, Context, param, values); } - UnlockEffectsWrite(Device); + UnlockEffectList(Device); ALCcontext_DecRef(Context); } @@ -269,9 +271,9 @@ AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value if(!Context) return; Device = Context->Device; - LockEffectsRead(Device); + LockEffectList(Device); if((ALEffect=LookupEffect(Device, effect)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid effect ID %u", effect); else { if(param == AL_EFFECT_TYPE) @@ -279,10 +281,10 @@ AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value else { /* Call the appropriate handler */ - V(ALEffect,getParami)(Context, param, value); + ALeffect_getParami(ALEffect, Context, param, value); } } - UnlockEffectsRead(Device); + UnlockEffectList(Device); ALCcontext_DecRef(Context); } @@ -304,15 +306,15 @@ AL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *valu if(!Context) return; Device = Context->Device; - LockEffectsRead(Device); + LockEffectList(Device); if((ALEffect=LookupEffect(Device, effect)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid effect ID %u", effect); else { /* Call the appropriate handler */ - V(ALEffect,getParamiv)(Context, param, values); + ALeffect_getParamiv(ALEffect, Context, param, values); } - UnlockEffectsRead(Device); + UnlockEffectList(Device); ALCcontext_DecRef(Context); } @@ -327,15 +329,15 @@ AL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *val if(!Context) return; Device = Context->Device; - LockEffectsRead(Device); + LockEffectList(Device); if((ALEffect=LookupEffect(Device, effect)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid effect ID %u", effect); else { /* Call the appropriate handler */ - V(ALEffect,getParamf)(Context, param, value); + ALeffect_getParamf(ALEffect, Context, param, value); } - UnlockEffectsRead(Device); + UnlockEffectList(Device); ALCcontext_DecRef(Context); } @@ -350,39 +352,120 @@ AL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *va if(!Context) return; Device = Context->Device; - LockEffectsRead(Device); + LockEffectList(Device); if((ALEffect=LookupEffect(Device, effect)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid effect ID %u", effect); else { /* Call the appropriate handler */ - V(ALEffect,getParamfv)(Context, param, values); + ALeffect_getParamfv(ALEffect, Context, param, values); } - UnlockEffectsRead(Device); + UnlockEffectList(Device); ALCcontext_DecRef(Context); } -ALenum InitEffect(ALeffect *effect) +void InitEffect(ALeffect *effect) { InitEffectParams(effect, AL_EFFECT_NULL); - return AL_NO_ERROR; } -ALvoid ReleaseALEffects(ALCdevice *device) +static ALeffect *AllocEffect(ALCcontext *context) { - ALsizei i; - for(i = 0;i < device->EffectMap.size;i++) - { - ALeffect *temp = device->EffectMap.values[i]; - device->EffectMap.values[i] = NULL; + ALCdevice *device = context->Device; + EffectSubList *sublist, *subend; + ALeffect *effect = NULL; + ALsizei lidx = 0; + ALsizei slidx; - // Release effect structure - FreeThunkEntry(temp->id); - memset(temp, 0, sizeof(ALeffect)); - al_free(temp); + almtx_lock(&device->EffectLock); + sublist = VECTOR_BEGIN(device->EffectList); + subend = VECTOR_END(device->EffectList); + for(;sublist != subend;++sublist) + { + if(sublist->FreeMask) + { + slidx = CTZ64(sublist->FreeMask); + effect = sublist->Effects + slidx; + break; + } + ++lidx; } + if(UNLIKELY(!effect)) + { + const EffectSubList empty_sublist = { 0, NULL }; + /* Don't allocate so many list entries that the 32-bit ID could + * overflow... + */ + if(UNLIKELY(VECTOR_SIZE(device->EffectList) >= 1<<25)) + { + almtx_unlock(&device->EffectLock); + alSetError(context, AL_OUT_OF_MEMORY, "Too many effects allocated"); + return NULL; + } + lidx = (ALsizei)VECTOR_SIZE(device->EffectList); + VECTOR_PUSH_BACK(device->EffectList, empty_sublist); + sublist = &VECTOR_BACK(device->EffectList); + sublist->FreeMask = ~U64(0); + sublist->Effects = al_calloc(16, sizeof(ALeffect)*64); + if(UNLIKELY(!sublist->Effects)) + { + VECTOR_POP_BACK(device->EffectList); + almtx_unlock(&device->EffectLock); + alSetError(context, AL_OUT_OF_MEMORY, "Failed to allocate effect batch"); + return NULL; + } + + slidx = 0; + effect = sublist->Effects + slidx; + } + + memset(effect, 0, sizeof(*effect)); + InitEffectParams(effect, AL_EFFECT_NULL); + + /* Add 1 to avoid effect ID 0. */ + effect->id = ((lidx<<6) | slidx) + 1; + + sublist->FreeMask &= ~(U64(1)<EffectLock); + + return effect; +} + +static void FreeEffect(ALCdevice *device, ALeffect *effect) +{ + ALuint id = effect->id - 1; + ALsizei lidx = id >> 6; + ALsizei slidx = id & 0x3f; + + memset(effect, 0, sizeof(*effect)); + + VECTOR_ELEM(device->EffectList, lidx).FreeMask |= U64(1) << slidx; +} + +void ReleaseALEffects(ALCdevice *device) +{ + EffectSubList *sublist = VECTOR_BEGIN(device->EffectList); + EffectSubList *subend = VECTOR_END(device->EffectList); + size_t leftover = 0; + for(;sublist != subend;++sublist) + { + ALuint64 usemask = ~sublist->FreeMask; + while(usemask) + { + ALsizei idx = CTZ64(usemask); + ALeffect *effect = sublist->Effects + idx; + + memset(effect, 0, sizeof(*effect)); + ++leftover; + + usemask &= ~(U64(1) << idx); + } + sublist->FreeMask = ~usemask; + } + if(leftover > 0) + WARN("(%p) Deleted "SZFMT" Effect%s\n", device, leftover, (leftover==1)?"":"s"); } @@ -418,7 +501,7 @@ static void InitEffectParams(ALeffect *effect, ALenum type) effect->Props.Reverb.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE; effect->Props.Reverb.RoomRolloffFactor = AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; effect->Props.Reverb.DecayHFLimit = AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT; - SET_VTABLE1(ALeaxreverb, effect); + effect->vtab = &ALeaxreverb_vtable; break; case AL_EFFECT_REVERB: effect->Props.Reverb.Density = AL_REVERB_DEFAULT_DENSITY; @@ -448,7 +531,7 @@ static void InitEffectParams(ALeffect *effect, ALenum type) effect->Props.Reverb.LFReference = 250.0f; effect->Props.Reverb.RoomRolloffFactor = AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; effect->Props.Reverb.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT; - SET_VTABLE1(ALreverb, effect); + effect->vtab = &ALreverb_vtable; break; case AL_EFFECT_CHORUS: effect->Props.Chorus.Waveform = AL_CHORUS_DEFAULT_WAVEFORM; @@ -457,11 +540,11 @@ static void InitEffectParams(ALeffect *effect, ALenum type) effect->Props.Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH; effect->Props.Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK; effect->Props.Chorus.Delay = AL_CHORUS_DEFAULT_DELAY; - SET_VTABLE1(ALchorus, effect); + effect->vtab = &ALchorus_vtable; break; case AL_EFFECT_COMPRESSOR: effect->Props.Compressor.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF; - SET_VTABLE1(ALcompressor, effect); + effect->vtab = &ALcompressor_vtable; break; case AL_EFFECT_DISTORTION: effect->Props.Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE; @@ -469,7 +552,7 @@ static void InitEffectParams(ALeffect *effect, ALenum type) effect->Props.Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF; effect->Props.Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER; effect->Props.Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH; - SET_VTABLE1(ALdistortion, effect); + effect->vtab = &ALdistortion_vtable; break; case AL_EFFECT_ECHO: effect->Props.Echo.Delay = AL_ECHO_DEFAULT_DELAY; @@ -477,7 +560,7 @@ static void InitEffectParams(ALeffect *effect, ALenum type) effect->Props.Echo.Damping = AL_ECHO_DEFAULT_DAMPING; effect->Props.Echo.Feedback = AL_ECHO_DEFAULT_FEEDBACK; effect->Props.Echo.Spread = AL_ECHO_DEFAULT_SPREAD; - SET_VTABLE1(ALecho, effect); + effect->vtab = &ALecho_vtable; break; case AL_EFFECT_EQUALIZER: effect->Props.Equalizer.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF; @@ -490,30 +573,35 @@ static void InitEffectParams(ALeffect *effect, ALenum type) effect->Props.Equalizer.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH; effect->Props.Equalizer.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF; effect->Props.Equalizer.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN; - SET_VTABLE1(ALequalizer, effect); + effect->vtab = &ALequalizer_vtable; break; case AL_EFFECT_FLANGER: - effect->Props.Flanger.Waveform = AL_FLANGER_DEFAULT_WAVEFORM; - effect->Props.Flanger.Phase = AL_FLANGER_DEFAULT_PHASE; - effect->Props.Flanger.Rate = AL_FLANGER_DEFAULT_RATE; - effect->Props.Flanger.Depth = AL_FLANGER_DEFAULT_DEPTH; - effect->Props.Flanger.Feedback = AL_FLANGER_DEFAULT_FEEDBACK; - effect->Props.Flanger.Delay = AL_FLANGER_DEFAULT_DELAY; - SET_VTABLE1(ALflanger, effect); + effect->Props.Chorus.Waveform = AL_FLANGER_DEFAULT_WAVEFORM; + effect->Props.Chorus.Phase = AL_FLANGER_DEFAULT_PHASE; + effect->Props.Chorus.Rate = AL_FLANGER_DEFAULT_RATE; + effect->Props.Chorus.Depth = AL_FLANGER_DEFAULT_DEPTH; + effect->Props.Chorus.Feedback = AL_FLANGER_DEFAULT_FEEDBACK; + effect->Props.Chorus.Delay = AL_FLANGER_DEFAULT_DELAY; + effect->vtab = &ALflanger_vtable; break; case AL_EFFECT_RING_MODULATOR: effect->Props.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY; effect->Props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF; effect->Props.Modulator.Waveform = AL_RING_MODULATOR_DEFAULT_WAVEFORM; - SET_VTABLE1(ALmodulator, effect); + effect->vtab = &ALmodulator_vtable; + break; + case AL_EFFECT_PITCH_SHIFTER: + effect->Props.Pshifter.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE; + effect->Props.Pshifter.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE; + effect->vtab = &ALpshifter_vtable; break; case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: case AL_EFFECT_DEDICATED_DIALOGUE: effect->Props.Dedicated.Gain = 1.0f; - SET_VTABLE1(ALdedicated, effect); + effect->vtab = &ALdedicated_vtable; break; default: - SET_VTABLE1(ALnull, effect); + effect->vtab = &ALnull_vtable; break; } effect->type = type; @@ -656,7 +744,7 @@ static const struct { }; #undef DECL -ALvoid LoadReverbPreset(const char *name, ALeffect *effect) +void LoadReverbPreset(const char *name, ALeffect *effect) { size_t i; @@ -667,9 +755,9 @@ ALvoid LoadReverbPreset(const char *name, ALeffect *effect) return; } - if(!DisabledEffects[EAXREVERB]) + if(!DisabledEffects[EAXREVERB_EFFECT]) InitEffectParams(effect, AL_EFFECT_EAXREVERB); - else if(!DisabledEffects[REVERB]) + else if(!DisabledEffects[REVERB_EFFECT]) InitEffectParams(effect, AL_EFFECT_REVERB); else InitEffectParams(effect, AL_EFFECT_NULL); diff --git a/Engine/lib/openal-soft/OpenAL32/alError.c b/Engine/lib/openal-soft/OpenAL32/alError.c index b38d8dfed..b6208f770 100644 --- a/Engine/lib/openal-soft/OpenAL32/alError.c +++ b/Engine/lib/openal-soft/OpenAL32/alError.c @@ -21,6 +21,7 @@ #include "config.h" #include +#include #ifdef HAVE_WINDOWS_H #define WIN32_LEAN_AND_MEAN @@ -33,9 +34,32 @@ ALboolean TrapALError = AL_FALSE; -ALvoid alSetError(ALCcontext *Context, ALenum errorCode) +void alSetError(ALCcontext *context, ALenum errorCode, const char *msg, ...) { ALenum curerr = AL_NO_ERROR; + char message[1024] = { 0 }; + va_list args; + int msglen; + + va_start(args, msg); + msglen = vsnprintf(message, sizeof(message), msg, args); + va_end(args); + + if(msglen < 0 || (size_t)msglen >= sizeof(message)) + { + message[sizeof(message)-1] = 0; + msglen = (int)strlen(message); + } + if(msglen > 0) + msg = message; + else + { + msg = ""; + msglen = (int)strlen(msg); + } + + WARN("Error generated on context %p, code 0x%04x, \"%s\"\n", + context, errorCode, message); if(TrapALError) { #ifdef _WIN32 @@ -46,17 +70,30 @@ ALvoid alSetError(ALCcontext *Context, ALenum errorCode) raise(SIGTRAP); #endif } - ATOMIC_COMPARE_EXCHANGE_STRONG(ALenum, &Context->LastError, &curerr, errorCode); + + ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&context->LastError, &curerr, errorCode); + if((ATOMIC_LOAD(&context->EnabledEvts, almemory_order_relaxed)&EventType_Error)) + { + ALbitfieldSOFT enabledevts; + almtx_lock(&context->EventCbLock); + enabledevts = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_relaxed); + if((enabledevts&EventType_Error) && context->EventCb) + (*context->EventCb)(AL_EVENT_TYPE_ERROR_SOFT, 0, errorCode, msglen, msg, + context->EventParam); + almtx_unlock(&context->EventCbLock); + } } AL_API ALenum AL_APIENTRY alGetError(void) { - ALCcontext *Context; + ALCcontext *context; ALenum errorCode; - Context = GetContextRef(); - if(!Context) + context = GetContextRef(); + if(!context) { + const ALenum deferror = AL_INVALID_OPERATION; + WARN("Querying error state on null context (implicitly 0x%04x)\n", deferror); if(TrapALError) { #ifdef _WIN32 @@ -66,12 +103,11 @@ AL_API ALenum AL_APIENTRY alGetError(void) raise(SIGTRAP); #endif } - return AL_INVALID_OPERATION; + return deferror; } - errorCode = ATOMIC_EXCHANGE(ALenum, &Context->LastError, AL_NO_ERROR); - - ALCcontext_DecRef(Context); + errorCode = ATOMIC_EXCHANGE_SEQ(&context->LastError, AL_NO_ERROR); + ALCcontext_DecRef(context); return errorCode; } diff --git a/Engine/lib/openal-soft/OpenAL32/alExtension.c b/Engine/lib/openal-soft/OpenAL32/alExtension.c index 1a559d9cb..f6378c706 100644 --- a/Engine/lib/openal-soft/OpenAL32/alExtension.c +++ b/Engine/lib/openal-soft/OpenAL32/alExtension.c @@ -35,22 +35,6 @@ #include "AL/alc.h" -const struct EffectList EffectList[] = { - { "eaxreverb", EAXREVERB, "AL_EFFECT_EAXREVERB", AL_EFFECT_EAXREVERB }, - { "reverb", REVERB, "AL_EFFECT_REVERB", AL_EFFECT_REVERB }, - { "chorus", CHORUS, "AL_EFFECT_CHORUS", AL_EFFECT_CHORUS }, - { "compressor", COMPRESSOR, "AL_EFFECT_COMPRESSOR", AL_EFFECT_COMPRESSOR }, - { "distortion", DISTORTION, "AL_EFFECT_DISTORTION", AL_EFFECT_DISTORTION }, - { "echo", ECHO, "AL_EFFECT_ECHO", AL_EFFECT_ECHO }, - { "equalizer", EQUALIZER, "AL_EFFECT_EQUALIZER", AL_EFFECT_EQUALIZER }, - { "flanger", FLANGER, "AL_EFFECT_FLANGER", AL_EFFECT_FLANGER }, - { "modulator", MODULATOR, "AL_EFFECT_RING_MODULATOR", AL_EFFECT_RING_MODULATOR }, - { "dedicated", DEDICATED, "AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT", AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT }, - { "dedicated", DEDICATED, "AL_EFFECT_DEDICATED_DIALOGUE", AL_EFFECT_DEDICATED_DIALOGUE }, - { NULL, 0, NULL, (ALenum)0 } -}; - - AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extName) { ALboolean ret = AL_FALSE; @@ -61,8 +45,8 @@ AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extName) context = GetContextRef(); if(!context) return AL_FALSE; - if(!(extName)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + if(!extName) + SETERR_GOTO(context, AL_INVALID_VALUE, done, "NULL pointer"); len = strlen(extName); ptr = context->ExtensionList; diff --git a/Engine/lib/openal-soft/OpenAL32/alFilter.c b/Engine/lib/openal-soft/OpenAL32/alFilter.c index c675d3448..7d8a886c5 100644 --- a/Engine/lib/openal-soft/OpenAL32/alFilter.c +++ b/Engine/lib/openal-soft/OpenAL32/alFilter.c @@ -25,65 +25,53 @@ #include "alMain.h" #include "alu.h" #include "alFilter.h" -#include "alThunk.h" #include "alError.h" -extern inline void LockFiltersRead(ALCdevice *device); -extern inline void UnlockFiltersRead(ALCdevice *device); -extern inline void LockFiltersWrite(ALCdevice *device); -extern inline void UnlockFiltersWrite(ALCdevice *device); -extern inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id); -extern inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id); -extern inline void ALfilterState_clear(ALfilterState *filter); -extern inline void ALfilterState_processPassthru(ALfilterState *filter, const ALfloat *restrict src, ALuint numsamples); -extern inline ALfloat calc_rcpQ_from_slope(ALfloat gain, ALfloat slope); -extern inline ALfloat calc_rcpQ_from_bandwidth(ALfloat freq_mult, ALfloat bandwidth); +extern inline void LockFilterList(ALCdevice *device); +extern inline void UnlockFilterList(ALCdevice *device); +static ALfilter *AllocFilter(ALCcontext *context); +static void FreeFilter(ALCdevice *device, ALfilter *filter); static void InitFilterParams(ALfilter *filter, ALenum type); +static inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) +{ + FilterSubList *sublist; + ALuint lidx = (id-1) >> 6; + ALsizei slidx = (id-1) & 0x3f; + + if(UNLIKELY(lidx >= VECTOR_SIZE(device->FilterList))) + return NULL; + sublist = &VECTOR_ELEM(device->FilterList, lidx); + if(UNLIKELY(sublist->FreeMask & (U64(1)<Filters + slidx; +} + AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters) { - ALCdevice *device; ALCcontext *context; ALsizei cur = 0; - ALenum err; context = GetContextRef(); if(!context) return; if(!(n >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - - device = context->Device; - for(cur = 0;cur < n;cur++) + alSetError(context, AL_INVALID_VALUE, "Generating %d filters", n); + else for(cur = 0;cur < n;cur++) { - ALfilter *filter = al_calloc(16, sizeof(ALfilter)); + ALfilter *filter = AllocFilter(context); if(!filter) { alDeleteFilters(cur, filters); - SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done); - } - InitFilterParams(filter, AL_FILTER_NULL); - - err = NewThunkEntry(&filter->id); - if(err == AL_NO_ERROR) - err = InsertUIntMapEntry(&device->FilterMap, filter->id, filter); - if(err != AL_NO_ERROR) - { - FreeThunkEntry(filter->id); - memset(filter, 0, sizeof(ALfilter)); - al_free(filter); - - alDeleteFilters(cur, filters); - SET_ERROR_AND_GOTO(context, err, done); + break; } filters[cur] = filter->id; } -done: ALCcontext_DecRef(context); } @@ -98,26 +86,22 @@ AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters) if(!context) return; device = context->Device; - LockFiltersWrite(device); + LockFilterList(device); if(!(n >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Deleting %d filters", n); for(i = 0;i < n;i++) { if(filters[i] && LookupFilter(device, filters[i]) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid filter ID %u", filters[i]); } for(i = 0;i < n;i++) { - if((filter=RemoveFilter(device, filters[i])) == NULL) - continue; - FreeThunkEntry(filter->id); - - memset(filter, 0, sizeof(*filter)); - al_free(filter); + if((filter=LookupFilter(device, filters[i])) != NULL) + FreeFilter(device, filter); } done: - UnlockFiltersWrite(device); + UnlockFilterList(device); ALCcontext_DecRef(context); } @@ -129,10 +113,10 @@ AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter) Context = GetContextRef(); if(!Context) return AL_FALSE; - LockFiltersRead(Context->Device); + LockFilterList(Context->Device); result = ((!filter || LookupFilter(Context->Device, filter)) ? AL_TRUE : AL_FALSE); - UnlockFiltersRead(Context->Device); + UnlockFilterList(Context->Device); ALCcontext_DecRef(Context); @@ -149,9 +133,9 @@ AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value) if(!Context) return; Device = Context->Device; - LockFiltersWrite(Device); + LockFilterList(Device); if((ALFilter=LookupFilter(Device, filter)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid filter ID %u", filter); else { if(param == AL_FILTER_TYPE) @@ -160,15 +144,15 @@ AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value) value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS) InitFilterParams(ALFilter, value); else - alSetError(Context, AL_INVALID_VALUE); + alSetError(Context, AL_INVALID_VALUE, "Invalid filter type 0x%04x", value); } else { /* Call the appropriate handler */ - ALfilter_SetParami(ALFilter, Context, param, value); + ALfilter_setParami(ALFilter, Context, param, value); } } - UnlockFiltersWrite(Device); + UnlockFilterList(Device); ALCcontext_DecRef(Context); } @@ -190,15 +174,15 @@ AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *v if(!Context) return; Device = Context->Device; - LockFiltersWrite(Device); + LockFilterList(Device); if((ALFilter=LookupFilter(Device, filter)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid filter ID %u", filter); else { /* Call the appropriate handler */ - ALfilter_SetParamiv(ALFilter, Context, param, values); + ALfilter_setParamiv(ALFilter, Context, param, values); } - UnlockFiltersWrite(Device); + UnlockFilterList(Device); ALCcontext_DecRef(Context); } @@ -213,15 +197,15 @@ AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat value) if(!Context) return; Device = Context->Device; - LockFiltersWrite(Device); + LockFilterList(Device); if((ALFilter=LookupFilter(Device, filter)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid filter ID %u", filter); else { /* Call the appropriate handler */ - ALfilter_SetParamf(ALFilter, Context, param, value); + ALfilter_setParamf(ALFilter, Context, param, value); } - UnlockFiltersWrite(Device); + UnlockFilterList(Device); ALCcontext_DecRef(Context); } @@ -236,15 +220,15 @@ AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat if(!Context) return; Device = Context->Device; - LockFiltersWrite(Device); + LockFilterList(Device); if((ALFilter=LookupFilter(Device, filter)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid filter ID %u", filter); else { /* Call the appropriate handler */ - ALfilter_SetParamfv(ALFilter, Context, param, values); + ALfilter_setParamfv(ALFilter, Context, param, values); } - UnlockFiltersWrite(Device); + UnlockFilterList(Device); ALCcontext_DecRef(Context); } @@ -259,9 +243,9 @@ AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value if(!Context) return; Device = Context->Device; - LockFiltersRead(Device); + LockFilterList(Device); if((ALFilter=LookupFilter(Device, filter)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid filter ID %u", filter); else { if(param == AL_FILTER_TYPE) @@ -269,10 +253,10 @@ AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value else { /* Call the appropriate handler */ - ALfilter_GetParami(ALFilter, Context, param, value); + ALfilter_getParami(ALFilter, Context, param, value); } } - UnlockFiltersRead(Device); + UnlockFilterList(Device); ALCcontext_DecRef(Context); } @@ -294,15 +278,15 @@ AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *valu if(!Context) return; Device = Context->Device; - LockFiltersRead(Device); + LockFilterList(Device); if((ALFilter=LookupFilter(Device, filter)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid filter ID %u", filter); else { /* Call the appropriate handler */ - ALfilter_GetParamiv(ALFilter, Context, param, values); + ALfilter_getParamiv(ALFilter, Context, param, values); } - UnlockFiltersRead(Device); + UnlockFilterList(Device); ALCcontext_DecRef(Context); } @@ -317,15 +301,15 @@ AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *val if(!Context) return; Device = Context->Device; - LockFiltersRead(Device); + LockFilterList(Device); if((ALFilter=LookupFilter(Device, filter)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid filter ID %u", filter); else { /* Call the appropriate handler */ - ALfilter_GetParamf(ALFilter, Context, param, value); + ALfilter_getParamf(ALFilter, Context, param, value); } - UnlockFiltersRead(Device); + UnlockFilterList(Device); ALCcontext_DecRef(Context); } @@ -340,134 +324,52 @@ AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *va if(!Context) return; Device = Context->Device; - LockFiltersRead(Device); + LockFilterList(Device); if((ALFilter=LookupFilter(Device, filter)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid filter ID %u", filter); else { /* Call the appropriate handler */ - ALfilter_GetParamfv(ALFilter, Context, param, values); + ALfilter_getParamfv(ALFilter, Context, param, values); } - UnlockFiltersRead(Device); + UnlockFilterList(Device); ALCcontext_DecRef(Context); } -void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat rcpQ) -{ - ALfloat alpha, sqrtgain_alpha_2; - ALfloat w0, sin_w0, cos_w0; - ALfloat a[3] = { 1.0f, 0.0f, 0.0f }; - ALfloat b[3] = { 1.0f, 0.0f, 0.0f }; - - // Limit gain to -100dB - gain = maxf(gain, 0.00001f); - - w0 = F_TAU * freq_mult; - sin_w0 = sinf(w0); - cos_w0 = cosf(w0); - alpha = sin_w0/2.0f * rcpQ; - - /* Calculate filter coefficients depending on filter type */ - switch(type) - { - case ALfilterType_HighShelf: - sqrtgain_alpha_2 = 2.0f * sqrtf(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 ALfilterType_LowShelf: - sqrtgain_alpha_2 = 2.0f * sqrtf(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 ALfilterType_Peaking: - gain = sqrtf(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 ALfilterType_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 ALfilterType_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 ALfilterType_BandPass: - b[0] = alpha; - b[1] = 0; - b[2] = -alpha; - a[0] = 1.0f + alpha; - a[1] = -2.0f * cos_w0; - a[2] = 1.0f - alpha; - break; - } - - filter->a1 = a[1] / a[0]; - filter->a2 = a[2] / a[0]; - filter->b0 = b[0] / a[0]; - filter->b1 = b[1] / a[0]; - filter->b2 = b[2] / a[0]; -} - - -static void lp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -static void lp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -static void lp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val) +static void ALlowpass_setParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param); } +static void ALlowpass_setParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, const ALint *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid low-pass integer-vector property 0x%04x", param); } +static void ALlowpass_setParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val) { switch(param) { case AL_LOWPASS_GAIN: if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "Low-pass gain %f out of range", val); filter->Gain = val; break; case AL_LOWPASS_GAINHF: if(!(val >= AL_LOWPASS_MIN_GAINHF && val <= AL_LOWPASS_MAX_GAINHF)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "Low-pass gainhf %f out of range", val); filter->GainHF = val; break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param); } } -static void lp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals) -{ - lp_SetParamf(filter, context, param, vals[0]); -} +static void ALlowpass_setParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals) +{ ALlowpass_setParamf(filter, context, param, vals[0]); } -static void lp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -static void lp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -static void lp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val) +static void ALlowpass_getParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint *UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param); } +static void ALlowpass_getParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid low-pass integer-vector property 0x%04x", param); } +static void ALlowpass_getParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val) { switch(param) { @@ -480,49 +382,47 @@ static void lp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, AL break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param); } } -static void lp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals) -{ - lp_GetParamf(filter, context, param, vals); -} +static void ALlowpass_getParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals) +{ ALlowpass_getParamf(filter, context, param, vals); } + +DEFINE_ALFILTER_VTABLE(ALlowpass); -static void hp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -static void hp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -static void hp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val) +static void ALhighpass_setParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param); } +static void ALhighpass_setParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, const ALint *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid high-pass integer-vector property 0x%04x", param); } +static void ALhighpass_setParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val) { switch(param) { case AL_HIGHPASS_GAIN: if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "High-pass gain out of range"); filter->Gain = val; break; case AL_HIGHPASS_GAINLF: if(!(val >= AL_HIGHPASS_MIN_GAINLF && val <= AL_HIGHPASS_MAX_GAINLF)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "High-pass gainlf out of range"); filter->GainLF = val; break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param); } } -static void hp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals) -{ - hp_SetParamf(filter, context, param, vals[0]); -} +static void ALhighpass_setParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals) +{ ALhighpass_setParamf(filter, context, param, vals[0]); } -static void hp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -static void hp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -static void hp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val) +static void ALhighpass_getParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint *UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param); } +static void ALhighpass_getParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid high-pass integer-vector property 0x%04x", param); } +static void ALhighpass_getParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val) { switch(param) { @@ -535,55 +435,53 @@ static void hp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, AL break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param); } } -static void hp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals) -{ - hp_GetParamf(filter, context, param, vals); -} +static void ALhighpass_getParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals) +{ ALhighpass_getParamf(filter, context, param, vals); } + +DEFINE_ALFILTER_VTABLE(ALhighpass); -static void bp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -static void bp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -static void bp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val) +static void ALbandpass_setParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param); } +static void ALbandpass_setParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, const ALint *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid band-pass integer-vector property 0x%04x", param); } +static void ALbandpass_setParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val) { switch(param) { case AL_BANDPASS_GAIN: if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "Band-pass gain out of range"); filter->Gain = val; break; case AL_BANDPASS_GAINHF: if(!(val >= AL_BANDPASS_MIN_GAINHF && val <= AL_BANDPASS_MAX_GAINHF)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "Band-pass gainhf out of range"); filter->GainHF = val; break; case AL_BANDPASS_GAINLF: if(!(val >= AL_BANDPASS_MIN_GAINLF && val <= AL_BANDPASS_MAX_GAINLF)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); + SETERR_RETURN(context, AL_INVALID_VALUE,, "Band-pass gainlf out of range"); filter->GainLF = val; break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param); } } -static void bp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals) -{ - bp_SetParamf(filter, context, param, vals[0]); -} +static void ALbandpass_setParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals) +{ ALbandpass_setParamf(filter, context, param, vals[0]); } -static void bp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -static void bp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -static void bp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val) +static void ALbandpass_getParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint *UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param); } +static void ALbandpass_getParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid band-pass integer-vector property 0x%04x", param); } +static void ALbandpass_getParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val) { switch(param) { @@ -600,47 +498,131 @@ static void bp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, AL break; default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); + alSetError(context, AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param); } } -static void bp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals) +static void ALbandpass_getParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals) +{ ALbandpass_getParamf(filter, context, param, vals); } + +DEFINE_ALFILTER_VTABLE(ALbandpass); + + +static void ALnullfilter_setParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param); } +static void ALnullfilter_setParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, const ALint *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param); } +static void ALnullfilter_setParamf(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALfloat UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param); } +static void ALnullfilter_setParamfv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, const ALfloat *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param); } + +static void ALnullfilter_getParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint *UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param); } +static void ALnullfilter_getParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param); } +static void ALnullfilter_getParamf(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALfloat *UNUSED(val)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param); } +static void ALnullfilter_getParamfv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALfloat *UNUSED(vals)) +{ alSetError(context, AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param); } + +DEFINE_ALFILTER_VTABLE(ALnullfilter); + + +static ALfilter *AllocFilter(ALCcontext *context) { - bp_GetParamf(filter, context, param, vals); -} + ALCdevice *device = context->Device; + FilterSubList *sublist, *subend; + ALfilter *filter = NULL; + ALsizei lidx = 0; + ALsizei slidx; - -static void null_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -static void null_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -static void null_SetParamf(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -static void null_SetParamfv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALfloat *UNUSED(vals)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } - -static void null_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -static void null_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -static void null_GetParamf(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -static void null_GetParamfv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(vals)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } - - -ALvoid ReleaseALFilters(ALCdevice *device) -{ - ALsizei i; - for(i = 0;i < device->FilterMap.size;i++) + almtx_lock(&device->FilterLock); + sublist = VECTOR_BEGIN(device->FilterList); + subend = VECTOR_END(device->FilterList); + for(;sublist != subend;++sublist) { - ALfilter *temp = device->FilterMap.values[i]; - device->FilterMap.values[i] = NULL; - - // Release filter structure - FreeThunkEntry(temp->id); - memset(temp, 0, sizeof(ALfilter)); - al_free(temp); + if(sublist->FreeMask) + { + slidx = CTZ64(sublist->FreeMask); + filter = sublist->Filters + slidx; + break; + } + ++lidx; } + if(UNLIKELY(!filter)) + { + const FilterSubList empty_sublist = { 0, NULL }; + /* Don't allocate so many list entries that the 32-bit ID could + * overflow... + */ + if(UNLIKELY(VECTOR_SIZE(device->FilterList) >= 1<<25)) + { + almtx_unlock(&device->FilterLock); + alSetError(context, AL_OUT_OF_MEMORY, "Too many filters allocated"); + return NULL; + } + lidx = (ALsizei)VECTOR_SIZE(device->FilterList); + VECTOR_PUSH_BACK(device->FilterList, empty_sublist); + sublist = &VECTOR_BACK(device->FilterList); + sublist->FreeMask = ~U64(0); + sublist->Filters = al_calloc(16, sizeof(ALfilter)*64); + if(UNLIKELY(!sublist->Filters)) + { + VECTOR_POP_BACK(device->FilterList); + almtx_unlock(&device->FilterLock); + alSetError(context, AL_OUT_OF_MEMORY, "Failed to allocate filter batch"); + return NULL; + } + + slidx = 0; + filter = sublist->Filters + slidx; + } + + memset(filter, 0, sizeof(*filter)); + InitFilterParams(filter, AL_FILTER_NULL); + + /* Add 1 to avoid filter ID 0. */ + filter->id = ((lidx<<6) | slidx) + 1; + + sublist->FreeMask &= ~(U64(1)<FilterLock); + + return filter; +} + +static void FreeFilter(ALCdevice *device, ALfilter *filter) +{ + ALuint id = filter->id - 1; + ALsizei lidx = id >> 6; + ALsizei slidx = id & 0x3f; + + memset(filter, 0, sizeof(*filter)); + + VECTOR_ELEM(device->FilterList, lidx).FreeMask |= U64(1) << slidx; +} + +void ReleaseALFilters(ALCdevice *device) +{ + FilterSubList *sublist = VECTOR_BEGIN(device->FilterList); + FilterSubList *subend = VECTOR_END(device->FilterList); + size_t leftover = 0; + for(;sublist != subend;++sublist) + { + ALuint64 usemask = ~sublist->FreeMask; + while(usemask) + { + ALsizei idx = CTZ64(usemask); + ALfilter *filter = sublist->Filters + idx; + + memset(filter, 0, sizeof(*filter)); + ++leftover; + + usemask &= ~(U64(1) << idx); + } + sublist->FreeMask = ~usemask; + } + if(leftover > 0) + WARN("(%p) Deleted "SZFMT" Filter%s\n", device, leftover, (leftover==1)?"":"s"); } @@ -653,15 +635,7 @@ static void InitFilterParams(ALfilter *filter, ALenum type) filter->HFReference = LOWPASSFREQREF; filter->GainLF = 1.0f; filter->LFReference = HIGHPASSFREQREF; - - filter->SetParami = lp_SetParami; - filter->SetParamiv = lp_SetParamiv; - filter->SetParamf = lp_SetParamf; - filter->SetParamfv = lp_SetParamfv; - filter->GetParami = lp_GetParami; - filter->GetParamiv = lp_GetParamiv; - filter->GetParamf = lp_GetParamf; - filter->GetParamfv = lp_GetParamfv; + filter->vtab = &ALlowpass_vtable; } else if(type == AL_FILTER_HIGHPASS) { @@ -670,15 +644,7 @@ static void InitFilterParams(ALfilter *filter, ALenum type) filter->HFReference = LOWPASSFREQREF; filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF; filter->LFReference = HIGHPASSFREQREF; - - filter->SetParami = hp_SetParami; - filter->SetParamiv = hp_SetParamiv; - filter->SetParamf = hp_SetParamf; - filter->SetParamfv = hp_SetParamfv; - filter->GetParami = hp_GetParami; - filter->GetParamiv = hp_GetParamiv; - filter->GetParamf = hp_GetParamf; - filter->GetParamfv = hp_GetParamfv; + filter->vtab = &ALhighpass_vtable; } else if(type == AL_FILTER_BANDPASS) { @@ -687,15 +653,7 @@ static void InitFilterParams(ALfilter *filter, ALenum type) filter->HFReference = LOWPASSFREQREF; filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF; filter->LFReference = HIGHPASSFREQREF; - - filter->SetParami = bp_SetParami; - filter->SetParamiv = bp_SetParamiv; - filter->SetParamf = bp_SetParamf; - filter->SetParamfv = bp_SetParamfv; - filter->GetParami = bp_GetParami; - filter->GetParamiv = bp_GetParamiv; - filter->GetParamf = bp_GetParamf; - filter->GetParamfv = bp_GetParamfv; + filter->vtab = &ALbandpass_vtable; } else { @@ -704,15 +662,7 @@ static void InitFilterParams(ALfilter *filter, ALenum type) filter->HFReference = LOWPASSFREQREF; filter->GainLF = 1.0f; filter->LFReference = HIGHPASSFREQREF; - - filter->SetParami = null_SetParami; - filter->SetParamiv = null_SetParamiv; - filter->SetParamf = null_SetParamf; - filter->SetParamfv = null_SetParamfv; - filter->GetParami = null_GetParami; - filter->GetParamiv = null_GetParamiv; - filter->GetParamf = null_GetParamf; - filter->GetParamfv = null_GetParamfv; + filter->vtab = &ALnullfilter_vtable; } filter->type = type; } diff --git a/Engine/lib/openal-soft/OpenAL32/alListener.c b/Engine/lib/openal-soft/OpenAL32/alListener.c index 08ece19db..f1ac3ff4a 100644 --- a/Engine/lib/openal-soft/OpenAL32/alListener.c +++ b/Engine/lib/openal-soft/OpenAL32/alListener.c @@ -21,85 +21,101 @@ #include "config.h" #include "alMain.h" -#include "AL/alc.h" +#include "alu.h" #include "alError.h" #include "alListener.h" #include "alSource.h" +#define DO_UPDATEPROPS() do { \ + if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) \ + UpdateListenerProps(context); \ + else \ + ATOMIC_FLAG_CLEAR(&listener->PropsClean, almemory_order_release); \ +} while(0) + + AL_API ALvoid AL_APIENTRY alListenerf(ALenum param, ALfloat value) { + ALlistener *listener; ALCcontext *context; context = GetContextRef(); if(!context) return; - WriteLock(&context->PropLock); + listener = context->Listener; + almtx_lock(&context->PropLock); switch(param) { case AL_GAIN: if(!(value >= 0.0f && isfinite(value))) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - context->Listener->Gain = value; + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Listener gain out of range"); + listener->Gain = value; + DO_UPDATEPROPS(); break; case AL_METERS_PER_UNIT: - if(!(value >= 0.0f && isfinite(value))) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - context->Listener->MetersPerUnit = value; + if(!(value >= AL_MIN_METERS_PER_UNIT && value <= AL_MAX_METERS_PER_UNIT)) + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Listener meters per unit out of range"); + context->MetersPerUnit = value; + if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) + UpdateContextProps(context); + else + ATOMIC_FLAG_CLEAR(&context->PropsClean, almemory_order_release); break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid listener float property"); } - if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) - UpdateListenerProps(context); done: - WriteUnlock(&context->PropLock); + almtx_unlock(&context->PropLock); ALCcontext_DecRef(context); } AL_API ALvoid AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) { + ALlistener *listener; ALCcontext *context; context = GetContextRef(); if(!context) return; - WriteLock(&context->PropLock); + listener = context->Listener; + almtx_lock(&context->PropLock); switch(param) { case AL_POSITION: if(!(isfinite(value1) && isfinite(value2) && isfinite(value3))) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - context->Listener->Position[0] = value1; - context->Listener->Position[1] = value2; - context->Listener->Position[2] = value3; + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Listener position out of range"); + listener->Position[0] = value1; + listener->Position[1] = value2; + listener->Position[2] = value3; + DO_UPDATEPROPS(); break; case AL_VELOCITY: if(!(isfinite(value1) && isfinite(value2) && isfinite(value3))) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - context->Listener->Velocity[0] = value1; - context->Listener->Velocity[1] = value2; - context->Listener->Velocity[2] = value3; + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Listener velocity out of range"); + listener->Velocity[0] = value1; + listener->Velocity[1] = value2; + listener->Velocity[2] = value3; + DO_UPDATEPROPS(); break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid listener 3-float property"); } - if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) - UpdateListenerProps(context); done: - WriteUnlock(&context->PropLock); + almtx_unlock(&context->PropLock); ALCcontext_DecRef(context); } AL_API ALvoid AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values) { + ALlistener *listener; ALCcontext *context; if(values) @@ -121,32 +137,31 @@ AL_API ALvoid AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values) context = GetContextRef(); if(!context) return; - WriteLock(&context->PropLock); - if(!(values)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + listener = context->Listener; + almtx_lock(&context->PropLock); + if(!values) SETERR_GOTO(context, AL_INVALID_VALUE, done, "NULL pointer"); switch(param) { case AL_ORIENTATION: if(!(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]) && isfinite(values[3]) && isfinite(values[4]) && isfinite(values[5]))) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Listener orientation out of range"); /* AT then UP */ - context->Listener->Forward[0] = values[0]; - context->Listener->Forward[1] = values[1]; - context->Listener->Forward[2] = values[2]; - context->Listener->Up[0] = values[3]; - context->Listener->Up[1] = values[4]; - context->Listener->Up[2] = values[5]; + listener->Forward[0] = values[0]; + listener->Forward[1] = values[1]; + listener->Forward[2] = values[2]; + listener->Up[0] = values[3]; + listener->Up[1] = values[4]; + listener->Up[2] = values[5]; + DO_UPDATEPROPS(); break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid listener float-vector property"); } - if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) - UpdateListenerProps(context); done: - WriteUnlock(&context->PropLock); + almtx_unlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -158,17 +173,14 @@ AL_API ALvoid AL_APIENTRY alListeneri(ALenum param, ALint UNUSED(value)) context = GetContextRef(); if(!context) return; - WriteLock(&context->PropLock); + almtx_lock(&context->PropLock); switch(param) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid listener integer property"); } - if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) - UpdateListenerProps(context); + almtx_unlock(&context->PropLock); -done: - WriteUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -188,17 +200,14 @@ AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, A context = GetContextRef(); if(!context) return; - WriteLock(&context->PropLock); + almtx_lock(&context->PropLock); switch(param) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid listener 3-integer property"); } - if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) - UpdateListenerProps(context); + almtx_unlock(&context->PropLock); -done: - WriteUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -232,19 +241,16 @@ AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values) context = GetContextRef(); if(!context) return; - WriteLock(&context->PropLock); - if(!(values)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - switch(param) + almtx_lock(&context->PropLock); + if(!values) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); + else switch(param) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid listener integer-vector property"); } - if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) - UpdateListenerProps(context); + almtx_unlock(&context->PropLock); -done: - WriteUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -256,25 +262,24 @@ AL_API ALvoid AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value) context = GetContextRef(); if(!context) return; - ReadLock(&context->PropLock); - if(!(value)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - switch(param) + almtx_lock(&context->PropLock); + if(!value) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); + else switch(param) { case AL_GAIN: *value = context->Listener->Gain; break; case AL_METERS_PER_UNIT: - *value = context->Listener->MetersPerUnit; + *value = context->MetersPerUnit; break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid listener float property"); } + almtx_unlock(&context->PropLock); -done: - ReadUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -286,10 +291,10 @@ AL_API ALvoid AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat context = GetContextRef(); if(!context) return; - ReadLock(&context->PropLock); - if(!(value1 && value2 && value3)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - switch(param) + almtx_lock(&context->PropLock); + if(!value1 || !value2 || !value3) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); + else switch(param) { case AL_POSITION: *value1 = context->Listener->Position[0]; @@ -304,11 +309,10 @@ AL_API ALvoid AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid listener 3-float property"); } + almtx_unlock(&context->PropLock); -done: - ReadUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -333,10 +337,10 @@ AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values) context = GetContextRef(); if(!context) return; - ReadLock(&context->PropLock); - if(!(values)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - switch(param) + almtx_lock(&context->PropLock); + if(!values) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); + else switch(param) { case AL_ORIENTATION: // AT then UP @@ -349,11 +353,10 @@ AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values) break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid listener float-vector property"); } + almtx_unlock(&context->PropLock); -done: - ReadUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -365,17 +368,16 @@ AL_API ALvoid AL_APIENTRY alGetListeneri(ALenum param, ALint *value) context = GetContextRef(); if(!context) return; - ReadLock(&context->PropLock); - if(!(value)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - switch(param) + almtx_lock(&context->PropLock); + if(!value) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); + else switch(param) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid listener integer property"); } + almtx_unlock(&context->PropLock); -done: - ReadUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -387,10 +389,10 @@ AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *valu context = GetContextRef(); if(!context) return; - ReadLock(&context->PropLock); - if(!(value1 && value2 && value3)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - switch (param) + almtx_lock(&context->PropLock); + if(!value1 || !value2 || !value3) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); + else switch(param) { case AL_POSITION: *value1 = (ALint)context->Listener->Position[0]; @@ -405,11 +407,10 @@ AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *valu break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid listener 3-integer property"); } + almtx_unlock(&context->PropLock); -done: - ReadUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -429,10 +430,10 @@ AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values) context = GetContextRef(); if(!context) return; - ReadLock(&context->PropLock); - if(!(values)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - switch(param) + almtx_lock(&context->PropLock); + if(!values) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); + else switch(param) { case AL_ORIENTATION: // AT then UP @@ -445,11 +446,10 @@ AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values) break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_ENUM, "Invalid listener integer-vector property"); } + almtx_unlock(&context->PropLock); -done: - ReadUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -460,7 +460,7 @@ void UpdateListenerProps(ALCcontext *context) struct ALlistenerProps *props; /* Get an unused proprty container, or allocate a new one as needed. */ - props = ATOMIC_LOAD(&listener->FreeList, almemory_order_acquire); + props = ATOMIC_LOAD(&context->FreeListenerProps, almemory_order_acquire); if(!props) props = al_calloc(16, sizeof(*props)); else @@ -468,48 +468,35 @@ void UpdateListenerProps(ALCcontext *context) struct ALlistenerProps *next; do { next = ATOMIC_LOAD(&props->next, almemory_order_relaxed); - } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALlistenerProps*, - &listener->FreeList, &props, next, almemory_order_seq_cst, - almemory_order_consume) == 0); + } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&context->FreeListenerProps, &props, next, + almemory_order_seq_cst, almemory_order_acquire) == 0); } /* Copy in current property values. */ - ATOMIC_STORE(&props->Position[0], listener->Position[0], almemory_order_relaxed); - ATOMIC_STORE(&props->Position[1], listener->Position[1], almemory_order_relaxed); - ATOMIC_STORE(&props->Position[2], listener->Position[2], almemory_order_relaxed); + props->Position[0] = listener->Position[0]; + props->Position[1] = listener->Position[1]; + props->Position[2] = listener->Position[2]; - ATOMIC_STORE(&props->Velocity[0], listener->Velocity[0], almemory_order_relaxed); - ATOMIC_STORE(&props->Velocity[1], listener->Velocity[1], almemory_order_relaxed); - ATOMIC_STORE(&props->Velocity[2], listener->Velocity[2], almemory_order_relaxed); + props->Velocity[0] = listener->Velocity[0]; + props->Velocity[1] = listener->Velocity[1]; + props->Velocity[2] = listener->Velocity[2]; - ATOMIC_STORE(&props->Forward[0], listener->Forward[0], almemory_order_relaxed); - ATOMIC_STORE(&props->Forward[1], listener->Forward[1], almemory_order_relaxed); - ATOMIC_STORE(&props->Forward[2], listener->Forward[2], almemory_order_relaxed); - ATOMIC_STORE(&props->Up[0], listener->Up[0], almemory_order_relaxed); - ATOMIC_STORE(&props->Up[1], listener->Up[1], almemory_order_relaxed); - ATOMIC_STORE(&props->Up[2], listener->Up[2], almemory_order_relaxed); + props->Forward[0] = listener->Forward[0]; + props->Forward[1] = listener->Forward[1]; + props->Forward[2] = listener->Forward[2]; + props->Up[0] = listener->Up[0]; + props->Up[1] = listener->Up[1]; + props->Up[2] = listener->Up[2]; - ATOMIC_STORE(&props->Gain, listener->Gain, almemory_order_relaxed); - ATOMIC_STORE(&props->MetersPerUnit, listener->MetersPerUnit, almemory_order_relaxed); - - ATOMIC_STORE(&props->DopplerFactor, context->DopplerFactor, almemory_order_relaxed); - ATOMIC_STORE(&props->DopplerVelocity, context->DopplerVelocity, almemory_order_relaxed); - ATOMIC_STORE(&props->SpeedOfSound, context->SpeedOfSound, almemory_order_relaxed); - - ATOMIC_STORE(&props->SourceDistanceModel, context->SourceDistanceModel, almemory_order_relaxed); - ATOMIC_STORE(&props->DistanceModel, context->DistanceModel, almemory_order_relaxed); + props->Gain = listener->Gain; /* Set the new container for updating internal parameters. */ - props = ATOMIC_EXCHANGE(struct ALlistenerProps*, &listener->Update, props, almemory_order_acq_rel); + props = ATOMIC_EXCHANGE_PTR(&listener->Update, props, almemory_order_acq_rel); if(props) { /* If there was an unused update container, put it back in the * freelist. */ - struct ALlistenerProps *first = ATOMIC_LOAD(&listener->FreeList); - do { - ATOMIC_STORE(&props->next, first, almemory_order_relaxed); - } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALlistenerProps*, - &listener->FreeList, &first, props) == 0); + ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &context->FreeListenerProps, props); } } diff --git a/Engine/lib/openal-soft/OpenAL32/alSource.c b/Engine/lib/openal-soft/OpenAL32/alSource.c index f20498f48..ed6bd8ee3 100644 --- a/Engine/lib/openal-soft/OpenAL32/alSource.c +++ b/Engine/lib/openal-soft/OpenAL32/alSource.c @@ -31,8 +31,9 @@ #include "alError.h" #include "alSource.h" #include "alBuffer.h" -#include "alThunk.h" +#include "alFilter.h" #include "alAuxEffectSlot.h" +#include "ringbuffer.h" #include "backends/base.h" @@ -40,20 +41,72 @@ #include "almalloc.h" -extern inline void LockSourcesRead(ALCcontext *context); -extern inline void UnlockSourcesRead(ALCcontext *context); -extern inline void LockSourcesWrite(ALCcontext *context); -extern inline void UnlockSourcesWrite(ALCcontext *context); -extern inline struct ALsource *LookupSource(ALCcontext *context, ALuint id); -extern inline struct ALsource *RemoveSource(ALCcontext *context, ALuint id); +static ALsource *AllocSource(ALCcontext *context); +static void FreeSource(ALCcontext *context, ALsource *source); +static void InitSourceParams(ALsource *Source, ALsizei num_sends); +static void DeinitSource(ALsource *source, ALsizei num_sends); +static void UpdateSourceProps(ALsource *source, ALvoice *voice, ALsizei num_sends, ALCcontext *context); +static ALint64 GetSourceSampleOffset(ALsource *Source, ALCcontext *context, ALuint64 *clocktime); +static ALdouble GetSourceSecOffset(ALsource *Source, ALCcontext *context, ALuint64 *clocktime); +static ALdouble GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context); +static ALboolean GetSampleOffset(ALsource *Source, ALuint *offset, ALsizei *frac); +static ALboolean ApplyOffset(ALsource *Source, ALvoice *voice); + +static inline void LockSourceList(ALCcontext *context) +{ almtx_lock(&context->SourceLock); } +static inline void UnlockSourceList(ALCcontext *context) +{ almtx_unlock(&context->SourceLock); } + +static inline ALsource *LookupSource(ALCcontext *context, ALuint id) +{ + SourceSubList *sublist; + ALuint lidx = (id-1) >> 6; + ALsizei slidx = (id-1) & 0x3f; + + if(UNLIKELY(lidx >= VECTOR_SIZE(context->SourceList))) + return NULL; + sublist = &VECTOR_ELEM(context->SourceList, lidx); + if(UNLIKELY(sublist->FreeMask & (U64(1)<Sources + slidx; +} + +static inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) +{ + BufferSubList *sublist; + ALuint lidx = (id-1) >> 6; + ALsizei slidx = (id-1) & 0x3f; + + if(UNLIKELY(lidx >= VECTOR_SIZE(device->BufferList))) + return NULL; + sublist = &VECTOR_ELEM(device->BufferList, lidx); + if(UNLIKELY(sublist->FreeMask & (U64(1)<Buffers + slidx; +} + +static inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) +{ + FilterSubList *sublist; + ALuint lidx = (id-1) >> 6; + ALsizei slidx = (id-1) & 0x3f; + + if(UNLIKELY(lidx >= VECTOR_SIZE(device->FilterList))) + return NULL; + sublist = &VECTOR_ELEM(device->FilterList, lidx); + if(UNLIKELY(sublist->FreeMask & (U64(1)<Filters + slidx; +} + +static inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) +{ + id--; + if(UNLIKELY(id >= VECTOR_SIZE(context->EffectSlotList))) + return NULL; + return VECTOR_ELEM(context->EffectSlotList, id); +} -static void InitSourceParams(ALsource *Source); -static void DeinitSource(ALsource *source); -static void UpdateSourceProps(ALsource *source, ALuint num_sends); -static ALint64 GetSourceSampleOffset(ALsource *Source, ALCdevice *device, ALuint64 *clocktime); -static ALdouble GetSourceSecOffset(ALsource *Source, ALCdevice *device, ALuint64 *clocktime); -static ALdouble GetSourceOffset(ALsource *Source, ALenum name, ALCdevice *device); -static ALboolean GetSampleOffset(ALsource *Source, ALuint *offset, ALuint *frac); typedef enum SourceProp { srcPitch = AL_PITCH, @@ -99,10 +152,6 @@ typedef enum SourceProp { /* AL_EXT_source_distance_model */ srcDistanceModel = AL_DISTANCE_MODEL, - srcByteLengthSOFT = AL_BYTE_LENGTH_SOFT, - srcSampleLengthSOFT = AL_SAMPLE_LENGTH_SOFT, - srcSecLengthSOFT = AL_SEC_LENGTH_SOFT, - /* AL_SOFT_source_latency */ srcSampleOffsetLatencySOFT = AL_SAMPLE_OFFSET_LATENCY_SOFT, srcSecOffsetLatencySOFT = AL_SEC_OFFSET_LATENCY_SOFT, @@ -115,6 +164,16 @@ typedef enum SourceProp { /* AL_EXT_BFORMAT */ srcOrientation = AL_ORIENTATION, + + /* AL_SOFT_source_resampler */ + srcResampler = AL_SOURCE_RESAMPLER_SOFT, + + /* AL_SOFT_source_spatialize */ + srcSpatialize = AL_SOURCE_SPATIALIZE_SOFT, + + /* ALC_SOFT_device_clock */ + srcSampleOffsetClockSOFT = AL_SAMPLE_OFFSET_CLOCK_SOFT, + srcSecOffsetClockSOFT = AL_SEC_OFFSET_CLOCK_SOFT, } SourceProp; static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALfloat *values); @@ -125,12 +184,76 @@ static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp p static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint *values); static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint64 *values); -static inline bool SourceShouldUpdate(const ALsource *source, const ALCcontext *context) +static inline ALvoice *GetSourceVoice(ALsource *source, ALCcontext *context) { - return (source->state == AL_PLAYING || source->state == AL_PAUSED) && - !ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire); + ALint idx = source->VoiceIdx; + if(idx >= 0 && idx < context->VoiceCount) + { + ALvoice *voice = context->Voices[idx]; + if(ATOMIC_LOAD(&voice->Source, almemory_order_acquire) == source) + return voice; + } + source->VoiceIdx = -1; + return NULL; } +/** + * Returns if the last known state for the source was playing or paused. Does + * not sync with the mixer voice. + */ +static inline bool IsPlayingOrPaused(ALsource *source) +{ return source->state == AL_PLAYING || source->state == AL_PAUSED; } + +/** + * Returns an updated source state using the matching voice's status (or lack + * thereof). + */ +static inline ALenum GetSourceState(ALsource *source, ALvoice *voice) +{ + if(!voice && source->state == AL_PLAYING) + source->state = AL_STOPPED; + return source->state; +} + +/** + * Returns if the source should specify an update, given the context's + * deferring state and the source's last known state. + */ +static inline bool SourceShouldUpdate(ALsource *source, ALCcontext *context) +{ + return !ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire) && + IsPlayingOrPaused(source); +} + + +/** Can only be called while the mixer is locked! */ +static void SendStateChangeEvent(ALCcontext *context, ALuint id, ALenum state) +{ + ALbitfieldSOFT enabledevt; + AsyncEvent evt; + + enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire); + if(!(enabledevt&EventType_SourceStateChange)) return; + + evt.EnumType = EventType_SourceStateChange; + evt.Type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT; + evt.ObjectId = id; + evt.Param = state; + snprintf(evt.Message, sizeof(evt.Message), "Source ID %u state changed to %s", id, + (state==AL_INITIAL) ? "AL_INITIAL" : + (state==AL_PLAYING) ? "AL_PLAYING" : + (state==AL_PAUSED) ? "AL_PAUSED" : + (state==AL_STOPPED) ? "AL_STOPPED" : "" + ); + /* The mixer may have queued a state change that's not yet been processed, + * and we don't want state change messages to occur out of order, so send + * it through the async queue to ensure proper ordering. + */ + if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1) + alsem_post(&context->EventSem); +} + + static ALint FloatValsByProp(ALenum prop) { if(prop != (ALenum)((SourceProp)prop)) @@ -165,10 +288,9 @@ static ALint FloatValsByProp(ALenum prop) case AL_BUFFERS_QUEUED: case AL_BUFFERS_PROCESSED: case AL_SOURCE_TYPE: - case AL_BYTE_LENGTH_SOFT: - case AL_SAMPLE_LENGTH_SOFT: - case AL_SEC_LENGTH_SOFT: case AL_SOURCE_RADIUS: + case AL_SOURCE_RESAMPLER_SOFT: + case AL_SOURCE_SPATIALIZE_SOFT: return 1; case AL_STEREO_ANGLES: @@ -183,6 +305,7 @@ static ALint FloatValsByProp(ALenum prop) return 6; case AL_SEC_OFFSET_LATENCY_SOFT: + case AL_SEC_OFFSET_CLOCK_SOFT: break; /* Double only */ case AL_BUFFER: @@ -190,6 +313,7 @@ static ALint FloatValsByProp(ALenum prop) case AL_AUXILIARY_SEND_FILTER: break; /* i/i64 only */ case AL_SAMPLE_OFFSET_LATENCY_SOFT: + case AL_SAMPLE_OFFSET_CLOCK_SOFT: break; /* i64 only */ } return 0; @@ -228,13 +352,13 @@ static ALint DoubleValsByProp(ALenum prop) case AL_BUFFERS_QUEUED: case AL_BUFFERS_PROCESSED: case AL_SOURCE_TYPE: - case AL_BYTE_LENGTH_SOFT: - case AL_SAMPLE_LENGTH_SOFT: - case AL_SEC_LENGTH_SOFT: case AL_SOURCE_RADIUS: + case AL_SOURCE_RESAMPLER_SOFT: + case AL_SOURCE_SPATIALIZE_SOFT: return 1; case AL_SEC_OFFSET_LATENCY_SOFT: + case AL_SEC_OFFSET_CLOCK_SOFT: case AL_STEREO_ANGLES: return 2; @@ -251,6 +375,7 @@ static ALint DoubleValsByProp(ALenum prop) case AL_AUXILIARY_SEND_FILTER: break; /* i/i64 only */ case AL_SAMPLE_OFFSET_LATENCY_SOFT: + case AL_SAMPLE_OFFSET_CLOCK_SOFT: break; /* i64 only */ } return 0; @@ -292,10 +417,9 @@ static ALint IntValsByProp(ALenum prop) case AL_BUFFERS_PROCESSED: case AL_SOURCE_TYPE: case AL_DIRECT_FILTER: - case AL_BYTE_LENGTH_SOFT: - case AL_SAMPLE_LENGTH_SOFT: - case AL_SEC_LENGTH_SOFT: case AL_SOURCE_RADIUS: + case AL_SOURCE_RESAMPLER_SOFT: + case AL_SOURCE_SPATIALIZE_SOFT: return 1; case AL_POSITION: @@ -308,8 +432,10 @@ static ALint IntValsByProp(ALenum prop) return 6; case AL_SAMPLE_OFFSET_LATENCY_SOFT: + case AL_SAMPLE_OFFSET_CLOCK_SOFT: break; /* i64 only */ case AL_SEC_OFFSET_LATENCY_SOFT: + case AL_SEC_OFFSET_CLOCK_SOFT: break; /* Double only */ case AL_STEREO_ANGLES: break; /* Float/double only */ @@ -352,13 +478,13 @@ static ALint Int64ValsByProp(ALenum prop) case AL_BUFFERS_PROCESSED: case AL_SOURCE_TYPE: case AL_DIRECT_FILTER: - case AL_BYTE_LENGTH_SOFT: - case AL_SAMPLE_LENGTH_SOFT: - case AL_SEC_LENGTH_SOFT: case AL_SOURCE_RADIUS: + case AL_SOURCE_RESAMPLER_SOFT: + case AL_SOURCE_SPATIALIZE_SOFT: return 1; case AL_SAMPLE_OFFSET_LATENCY_SOFT: + case AL_SAMPLE_OFFSET_CLOCK_SOFT: return 2; case AL_POSITION: @@ -371,6 +497,7 @@ static ALint Int64ValsByProp(ALenum prop) return 6; case AL_SEC_OFFSET_LATENCY_SOFT: + case AL_SEC_OFFSET_CLOCK_SOFT: break; /* Double only */ case AL_STEREO_ANGLES: break; /* Float/double only */ @@ -381,12 +508,19 @@ static ALint Int64ValsByProp(ALenum prop) #define CHECKVAL(x) do { \ if(!(x)) \ - SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE); \ + { \ + alSetError(Context, AL_INVALID_VALUE, "Value out of range"); \ + return AL_FALSE; \ + } \ } while(0) #define DO_UPDATEPROPS() do { \ - if(SourceShouldUpdate(Source, Context)) \ - UpdateSourceProps(Source, device->NumAuxSends); \ + ALvoice *voice; \ + if(SourceShouldUpdate(Source, Context) && \ + (voice=GetSourceVoice(Source, Context)) != NULL) \ + UpdateSourceProps(Source, voice, device->NumAuxSends, Context); \ + else \ + ATOMIC_FLAG_CLEAR(&Source->PropsClean, almemory_order_release); \ } while(0) static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALfloat *values) @@ -396,12 +530,11 @@ static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp p switch(prop) { - case AL_BYTE_LENGTH_SOFT: - case AL_SAMPLE_LENGTH_SOFT: - case AL_SEC_LENGTH_SOFT: case AL_SEC_OFFSET_LATENCY_SOFT: + case AL_SEC_OFFSET_CLOCK_SOFT: /* Query only */ - SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_OPERATION, AL_FALSE); + SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE, + "Setting read-only source property 0x%04x", prop); case AL_PITCH: CHECKVAL(*values >= 0.0f); @@ -441,7 +574,7 @@ static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_ROLLOFF_FACTOR: CHECKVAL(*values >= 0.0f); - Source->RollOffFactor = *values; + Source->RolloffFactor = *values; DO_UPDATEPROPS(); return AL_TRUE; @@ -509,19 +642,24 @@ static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp p Source->OffsetType = prop; Source->Offset = *values; - if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) && - !ATOMIC_LOAD(&Context->DeferUpdates, almemory_order_acquire)) + if(IsPlayingOrPaused(Source)) { - LockContext(Context); - WriteLock(&Source->queue_lock); - if(ApplyOffset(Source) == AL_FALSE) + ALvoice *voice; + + ALCdevice_Lock(Context->Device); + /* Double-check that the source is still playing while we have + * the lock. + */ + voice = GetSourceVoice(Source, Context); + if(voice) { - WriteUnlock(&Source->queue_lock); - UnlockContext(Context); - SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE); + if(ApplyOffset(Source, voice) == AL_FALSE) + { + ALCdevice_Unlock(Context->Device); + SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid offset"); + } } - WriteUnlock(&Source->queue_lock); - UnlockContext(Context); + ALCdevice_Unlock(Context->Device); } return AL_TRUE; @@ -591,6 +729,8 @@ static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: case AL_DIRECT_CHANNELS_SOFT: + case AL_SOURCE_RESAMPLER_SOFT: + case AL_SOURCE_SPATIALIZE_SOFT: ival = (ALint)values[0]; return SetSourceiv(Source, Context, prop, &ival); @@ -603,11 +743,12 @@ static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_DIRECT_FILTER: case AL_AUXILIARY_SEND_FILTER: case AL_SAMPLE_OFFSET_LATENCY_SOFT: + case AL_SAMPLE_OFFSET_CLOCK_SOFT: break; } ERR("Unexpected property: 0x%04x\n", prop); - SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE); + SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source float property 0x%04x", prop); } static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint *values) @@ -617,7 +758,6 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p ALfilter *filter = NULL; ALeffectslot *slot = NULL; ALbufferlistitem *oldlist; - ALbufferlistitem *newlist; ALfloat fvals[6]; switch(prop) @@ -626,11 +766,9 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_SOURCE_TYPE: case AL_BUFFERS_QUEUED: case AL_BUFFERS_PROCESSED: - case AL_BYTE_LENGTH_SOFT: - case AL_SAMPLE_LENGTH_SOFT: - case AL_SEC_LENGTH_SOFT: /* Query only */ - SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_OPERATION, AL_FALSE); + SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE, + "Setting read-only source property 0x%04x", prop); case AL_SOURCE_RELATIVE: CHECKVAL(*values == AL_FALSE || *values == AL_TRUE); @@ -642,63 +780,91 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_LOOPING: CHECKVAL(*values == AL_FALSE || *values == AL_TRUE); - WriteLock(&Source->queue_lock); - ATOMIC_STORE(&Source->looping, *values); - WriteUnlock(&Source->queue_lock); + Source->Looping = (ALboolean)*values; + if(IsPlayingOrPaused(Source)) + { + ALvoice *voice = GetSourceVoice(Source, Context); + if(voice) + { + if(Source->Looping) + ATOMIC_STORE(&voice->loop_buffer, Source->queue, almemory_order_release); + else + ATOMIC_STORE(&voice->loop_buffer, NULL, almemory_order_release); + + /* If the source is playing, wait for the current mix to finish + * to ensure it isn't currently looping back or reaching the + * end. + */ + while((ATOMIC_LOAD(&device->MixCount, almemory_order_acquire)&1)) + althrd_yield(); + } + } return AL_TRUE; case AL_BUFFER: - LockBuffersRead(device); + LockBufferList(device); if(!(*values == 0 || (buffer=LookupBuffer(device, *values)) != NULL)) { - UnlockBuffersRead(device); - SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE); + UnlockBufferList(device); + SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid buffer ID %u", + *values); } - WriteLock(&Source->queue_lock); - if(!(Source->state == AL_STOPPED || Source->state == AL_INITIAL)) + if(buffer && buffer->MappedAccess != 0 && + !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) { - WriteUnlock(&Source->queue_lock); - UnlockBuffersRead(device); - SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_OPERATION, AL_FALSE); + UnlockBufferList(device); + SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE, + "Setting non-persistently mapped buffer %u", buffer->id); + } + else + { + ALenum state = GetSourceState(Source, GetSourceVoice(Source, Context)); + if(state == AL_PLAYING || state == AL_PAUSED) + { + UnlockBufferList(device); + SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE, + "Setting buffer on playing or paused source %u", Source->id); + } } + oldlist = Source->queue; if(buffer != NULL) { /* Add the selected buffer to a one-item queue */ - newlist = malloc(sizeof(ALbufferlistitem)); - newlist->buffer = buffer; - newlist->next = NULL; + ALbufferlistitem *newlist = al_calloc(DEF_ALIGN, + FAM_SIZE(ALbufferlistitem, buffers, 1)); + ATOMIC_INIT(&newlist->next, NULL); + newlist->max_samples = buffer->SampleLen; + newlist->num_buffers = 1; + newlist->buffers[0] = buffer; IncrementRef(&buffer->ref); /* Source is now Static */ Source->SourceType = AL_STATIC; - - ReadLock(&buffer->lock); - Source->NumChannels = ChannelsFromFmt(buffer->FmtChannels); - Source->SampleSize = BytesFromFmt(buffer->FmtType); - ReadUnlock(&buffer->lock); + Source->queue = newlist; } else { /* Source is now Undetermined */ Source->SourceType = AL_UNDETERMINED; - newlist = NULL; + Source->queue = NULL; } - oldlist = ATOMIC_EXCHANGE(ALbufferlistitem*, &Source->queue, newlist); - ATOMIC_STORE(&Source->current_buffer, newlist); - WriteUnlock(&Source->queue_lock); - UnlockBuffersRead(device); + UnlockBufferList(device); /* Delete all elements in the previous queue */ while(oldlist != NULL) { + ALsizei i; ALbufferlistitem *temp = oldlist; - oldlist = temp->next; + oldlist = ATOMIC_LOAD(&temp->next, almemory_order_relaxed); - if(temp->buffer) - DecrementRef(&temp->buffer->ref); - free(temp); + for(i = 0;i < temp->num_buffers;i++) + { + if(temp->buffers[i]) + DecrementRef(&temp->buffers[i]->ref); + } + al_free(temp); } return AL_TRUE; @@ -710,28 +876,32 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p Source->OffsetType = prop; Source->Offset = *values; - if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) && - !ATOMIC_LOAD(&Context->DeferUpdates, almemory_order_acquire)) + if(IsPlayingOrPaused(Source)) { - LockContext(Context); - WriteLock(&Source->queue_lock); - if(ApplyOffset(Source) == AL_FALSE) + ALvoice *voice; + + ALCdevice_Lock(Context->Device); + voice = GetSourceVoice(Source, Context); + if(voice) { - WriteUnlock(&Source->queue_lock); - UnlockContext(Context); - SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE); + if(ApplyOffset(Source, voice) == AL_FALSE) + { + ALCdevice_Unlock(Context->Device); + SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, + "Invalid source offset"); + } } - WriteUnlock(&Source->queue_lock); - UnlockContext(Context); + ALCdevice_Unlock(Context->Device); } return AL_TRUE; case AL_DIRECT_FILTER: - LockFiltersRead(device); + LockFilterList(device); if(!(*values == 0 || (filter=LookupFilter(device, *values)) != NULL)) { - UnlockFiltersRead(device); - SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE); + UnlockFilterList(device); + SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid filter ID %u", + *values); } if(!filter) @@ -750,7 +920,7 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p Source->Direct.GainLF = filter->GainLF; Source->Direct.LFReference = filter->LFReference; } - UnlockFiltersRead(device); + UnlockFilterList(device); DO_UPDATEPROPS(); return AL_TRUE; @@ -796,17 +966,41 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p DO_UPDATEPROPS(); return AL_TRUE; + case AL_SOURCE_RESAMPLER_SOFT: + CHECKVAL(*values >= 0 && *values <= ResamplerMax); + + Source->Resampler = *values; + DO_UPDATEPROPS(); + return AL_TRUE; + + case AL_SOURCE_SPATIALIZE_SOFT: + CHECKVAL(*values >= AL_FALSE && *values <= AL_AUTO_SOFT); + + Source->Spatialize = *values; + DO_UPDATEPROPS(); + return AL_TRUE; + case AL_AUXILIARY_SEND_FILTER: - LockEffectSlotsRead(Context); - LockFiltersRead(device); - if(!((ALuint)values[1] < device->NumAuxSends && - (values[0] == 0 || (slot=LookupEffectSlot(Context, values[0])) != NULL) && - (values[2] == 0 || (filter=LookupFilter(device, values[2])) != NULL))) + LockEffectSlotList(Context); + if(!(values[0] == 0 || (slot=LookupEffectSlot(Context, values[0])) != NULL)) { - UnlockFiltersRead(device); - UnlockEffectSlotsRead(Context); - SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE); + UnlockEffectSlotList(Context); + SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid effect ID %u", + values[0]); + } + if(!((ALuint)values[1] < (ALuint)device->NumAuxSends)) + { + UnlockEffectSlotList(Context); + SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid send %u", values[1]); + } + LockFilterList(device); + if(!(values[2] == 0 || (filter=LookupFilter(device, values[2])) != NULL)) + { + UnlockFilterList(device); + UnlockEffectSlotList(Context); + SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid filter ID %u", + values[2]); } if(!filter) @@ -826,21 +1020,24 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p Source->Send[values[1]].GainLF = filter->GainLF; Source->Send[values[1]].LFReference = filter->LFReference; } - UnlockFiltersRead(device); + UnlockFilterList(device); - if(slot != Source->Send[values[1]].Slot && - (Source->state == AL_PLAYING || Source->state == AL_PAUSED)) + if(slot != Source->Send[values[1]].Slot && IsPlayingOrPaused(Source)) { + ALvoice *voice; /* Add refcount on the new slot, and release the previous slot */ if(slot) IncrementRef(&slot->ref); if(Source->Send[values[1]].Slot) DecrementRef(&Source->Send[values[1]].Slot->ref); Source->Send[values[1]].Slot = slot; - /* We must force an update if the auxiliary slot changed on a - * playing source, in case the slot is about to be deleted. + /* We must force an update if the auxiliary slot changed on an + * active source, in case the slot is about to be deleted. */ - UpdateSourceProps(Source, device->NumAuxSends); + if((voice=GetSourceVoice(Source, Context)) != NULL) + UpdateSourceProps(Source, voice, device->NumAuxSends, Context); + else + ATOMIC_FLAG_CLEAR(&Source->PropsClean, almemory_order_release); } else { @@ -850,7 +1047,7 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p Source->Send[values[1]].Slot = slot; DO_UPDATEPROPS(); } - UnlockEffectSlotsRead(Context); + UnlockEffectSlotList(Context); return AL_TRUE; @@ -895,12 +1092,15 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_SAMPLE_OFFSET_LATENCY_SOFT: case AL_SEC_OFFSET_LATENCY_SOFT: + case AL_SEC_OFFSET_CLOCK_SOFT: + case AL_SAMPLE_OFFSET_CLOCK_SOFT: case AL_STEREO_ANGLES: break; } ERR("Unexpected property: 0x%04x\n", prop); - SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE); + SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer property 0x%04x", + prop); } static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint64SOFT *values) @@ -915,12 +1115,10 @@ static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp case AL_BUFFERS_PROCESSED: case AL_SOURCE_STATE: case AL_SAMPLE_OFFSET_LATENCY_SOFT: - case AL_BYTE_LENGTH_SOFT: - case AL_SAMPLE_LENGTH_SOFT: - case AL_SEC_LENGTH_SOFT: + case AL_SAMPLE_OFFSET_CLOCK_SOFT: /* Query only */ - SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_OPERATION, AL_FALSE); - + SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE, + "Setting read-only source property 0x%04x", prop); /* 1x int */ case AL_SOURCE_RELATIVE: @@ -933,6 +1131,8 @@ static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: case AL_DIRECT_CHANNELS_SOFT: case AL_DISTANCE_MODEL: + case AL_SOURCE_RESAMPLER_SOFT: + case AL_SOURCE_SPATIALIZE_SOFT: CHECKVAL(*values <= INT_MAX && *values >= INT_MIN); ivals[0] = (ALint)*values; @@ -996,12 +1196,14 @@ static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp return SetSourcefv(Source, Context, (int)prop, fvals); case AL_SEC_OFFSET_LATENCY_SOFT: + case AL_SEC_OFFSET_CLOCK_SOFT: case AL_STEREO_ANGLES: break; } ERR("Unexpected property: 0x%04x\n", prop); - SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE); + SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer64 property 0x%04x", + prop); } #undef CHECKVAL @@ -1010,7 +1212,6 @@ static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALdouble *values) { ALCdevice *device = Context->Device; - ALbufferlistitem *BufferList; ClockLatency clocktime; ALuint64 srcclock; ALint ivals[3]; @@ -1031,7 +1232,7 @@ static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp p return AL_TRUE; case AL_ROLLOFF_FACTOR: - *values = Source->RollOffFactor; + *values = Source->RolloffFactor; return AL_TRUE; case AL_REFERENCE_DISTANCE: @@ -1061,7 +1262,7 @@ static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_SEC_OFFSET: case AL_SAMPLE_OFFSET: case AL_BYTE_OFFSET: - *values = GetSourceOffset(Source, prop, device); + *values = GetSourceOffset(Source, prop, Context); return AL_TRUE; case AL_CONE_OUTER_GAINHF: @@ -1080,27 +1281,6 @@ static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp p *values = Source->DopplerFactor; return AL_TRUE; - case AL_SEC_LENGTH_SOFT: - ReadLock(&Source->queue_lock); - if(!(BufferList=ATOMIC_LOAD(&Source->queue))) - *values = 0; - else - { - ALint length = 0; - ALsizei freq = 1; - do { - ALbuffer *buffer = BufferList->buffer; - if(buffer && buffer->SampleLen > 0) - { - freq = buffer->Frequency; - length += buffer->SampleLen; - } - } while((BufferList=BufferList->next) != NULL); - *values = (ALdouble)length / (ALdouble)freq; - } - ReadUnlock(&Source->queue_lock); - return AL_TRUE; - case AL_SOURCE_RADIUS: *values = Source->Radius; return AL_TRUE; @@ -1114,8 +1294,10 @@ static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp p /* Get the source offset with the clock time first. Then get the * clock time with the device latency. Order is important. */ - values[0] = GetSourceSecOffset(Source, device, &srcclock); + values[0] = GetSourceSecOffset(Source, Context, &srcclock); + almtx_lock(&device->BackendLock); clocktime = V0(device->Backend,getClockLatency)(); + almtx_unlock(&device->BackendLock); if(srcclock == (ALuint64)clocktime.ClockTime) values[1] = (ALdouble)clocktime.Latency / 1000000000.0; else @@ -1130,6 +1312,11 @@ static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp p } return AL_TRUE; + case AL_SEC_OFFSET_CLOCK_SOFT: + values[0] = GetSourceSecOffset(Source, Context, &srcclock); + values[1] = srcclock / 1000000000.0; + return AL_TRUE; + case AL_POSITION: values[0] = Source->Position[0]; values[1] = Source->Position[1]; @@ -1168,9 +1355,9 @@ static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: case AL_DIRECT_CHANNELS_SOFT: - case AL_BYTE_LENGTH_SOFT: - case AL_SAMPLE_LENGTH_SOFT: case AL_DISTANCE_MODEL: + case AL_SOURCE_RESAMPLER_SOFT: + case AL_SOURCE_SPATIALIZE_SOFT: if((err=GetSourceiv(Source, Context, (int)prop, ivals)) != AL_FALSE) *values = (ALdouble)ivals[0]; return err; @@ -1179,11 +1366,13 @@ static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_DIRECT_FILTER: case AL_AUXILIARY_SEND_FILTER: case AL_SAMPLE_OFFSET_LATENCY_SOFT: + case AL_SAMPLE_OFFSET_CLOCK_SOFT: break; } ERR("Unexpected property: 0x%04x\n", prop); - SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE); + SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source double property 0x%04x", + prop); } static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint *values) @@ -1199,94 +1388,35 @@ static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p return AL_TRUE; case AL_LOOPING: - *values = ATOMIC_LOAD(&Source->looping); + *values = Source->Looping; return AL_TRUE; case AL_BUFFER: - ReadLock(&Source->queue_lock); - BufferList = (Source->SourceType == AL_STATIC) ? ATOMIC_LOAD(&Source->queue) : - ATOMIC_LOAD(&Source->current_buffer); - *values = (BufferList && BufferList->buffer) ? BufferList->buffer->id : 0; - ReadUnlock(&Source->queue_lock); + BufferList = (Source->SourceType == AL_STATIC) ? Source->queue : NULL; + *values = (BufferList && BufferList->num_buffers >= 1 && BufferList->buffers[0]) ? + BufferList->buffers[0]->id : 0; return AL_TRUE; case AL_SOURCE_STATE: - *values = Source->state; - return AL_TRUE; - - case AL_BYTE_LENGTH_SOFT: - ReadLock(&Source->queue_lock); - if(!(BufferList=ATOMIC_LOAD(&Source->queue))) - *values = 0; - else - { - ALint length = 0; - do { - ALbuffer *buffer = BufferList->buffer; - if(buffer && buffer->SampleLen > 0) - { - ALuint byte_align, sample_align; - if(buffer->OriginalType == UserFmtIMA4) - { - ALsizei align = (buffer->OriginalAlign-1)/2 + 4; - byte_align = align * ChannelsFromFmt(buffer->FmtChannels); - sample_align = buffer->OriginalAlign; - } - else if(buffer->OriginalType == UserFmtMSADPCM) - { - ALsizei align = (buffer->OriginalAlign-2)/2 + 7; - byte_align = align * ChannelsFromFmt(buffer->FmtChannels); - sample_align = buffer->OriginalAlign; - } - else - { - ALsizei align = buffer->OriginalAlign; - byte_align = align * ChannelsFromFmt(buffer->FmtChannels); - sample_align = buffer->OriginalAlign; - } - - length += buffer->SampleLen / sample_align * byte_align; - } - } while((BufferList=BufferList->next) != NULL); - *values = length; - } - ReadUnlock(&Source->queue_lock); - return AL_TRUE; - - case AL_SAMPLE_LENGTH_SOFT: - ReadLock(&Source->queue_lock); - if(!(BufferList=ATOMIC_LOAD(&Source->queue))) - *values = 0; - else - { - ALint length = 0; - do { - ALbuffer *buffer = BufferList->buffer; - if(buffer) length += buffer->SampleLen; - } while((BufferList=BufferList->next) != NULL); - *values = length; - } - ReadUnlock(&Source->queue_lock); + *values = GetSourceState(Source, GetSourceVoice(Source, Context)); return AL_TRUE; case AL_BUFFERS_QUEUED: - ReadLock(&Source->queue_lock); - if(!(BufferList=ATOMIC_LOAD(&Source->queue))) + if(!(BufferList=Source->queue)) *values = 0; else { ALsizei count = 0; do { - ++count; - } while((BufferList=BufferList->next) != NULL); + count += BufferList->num_buffers; + BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed); + } while(BufferList != NULL); *values = count; } - ReadUnlock(&Source->queue_lock); return AL_TRUE; case AL_BUFFERS_PROCESSED: - ReadLock(&Source->queue_lock); - if(ATOMIC_LOAD(&Source->looping) || Source->SourceType != AL_STREAMING) + if(Source->Looping || Source->SourceType != AL_STREAMING) { /* Buffers on a looping source are in a perpetual state of * PENDING, so don't report any as PROCESSED */ @@ -1294,17 +1424,24 @@ static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p } else { - const ALbufferlistitem *BufferList = ATOMIC_LOAD(&Source->queue); - const ALbufferlistitem *Current = ATOMIC_LOAD(&Source->current_buffer); + const ALbufferlistitem *BufferList = Source->queue; + const ALbufferlistitem *Current = NULL; ALsizei played = 0; + ALvoice *voice; + + if((voice=GetSourceVoice(Source, Context)) != NULL) + Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed); + else if(Source->state == AL_INITIAL) + Current = BufferList; + while(BufferList && BufferList != Current) { - played++; - BufferList = BufferList->next; + played += BufferList->num_buffers; + BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next, + almemory_order_relaxed); } *values = played; } - ReadUnlock(&Source->queue_lock); return AL_TRUE; case AL_SOURCE_TYPE: @@ -1331,6 +1468,14 @@ static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p *values = Source->DistanceModel; return AL_TRUE; + case AL_SOURCE_RESAMPLER_SOFT: + *values = Source->Resampler; + return AL_TRUE; + + case AL_SOURCE_SPATIALIZE_SOFT: + *values = Source->Spatialize; + return AL_TRUE; + /* 1x float/double */ case AL_CONE_INNER_ANGLE: case AL_CONE_OUTER_ANGLE: @@ -1349,7 +1494,6 @@ static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_AIR_ABSORPTION_FACTOR: case AL_ROOM_ROLLOFF_FACTOR: case AL_CONE_OUTER_GAINHF: - case AL_SEC_LENGTH_SOFT: case AL_SOURCE_RADIUS: if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE) *values = (ALint)dvals[0]; @@ -1381,8 +1525,10 @@ static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p return err; case AL_SAMPLE_OFFSET_LATENCY_SOFT: + case AL_SAMPLE_OFFSET_CLOCK_SOFT: break; /* i64 only */ case AL_SEC_OFFSET_LATENCY_SOFT: + case AL_SEC_OFFSET_CLOCK_SOFT: break; /* Double only */ case AL_STEREO_ANGLES: break; /* Float/double only */ @@ -1393,7 +1539,8 @@ static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p } ERR("Unexpected property: 0x%04x\n", prop); - SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE); + SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer property 0x%04x", + prop); } static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint64 *values) @@ -1411,8 +1558,10 @@ static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp /* Get the source offset with the clock time first. Then get the * clock time with the device latency. Order is important. */ - values[0] = GetSourceSampleOffset(Source, device, &srcclock); + values[0] = GetSourceSampleOffset(Source, Context, &srcclock); + almtx_lock(&device->BackendLock); clocktime = V0(device->Backend,getClockLatency)(); + almtx_unlock(&device->BackendLock); if(srcclock == (ALuint64)clocktime.ClockTime) values[1] = clocktime.Latency; else @@ -1426,6 +1575,11 @@ static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp } return AL_TRUE; + case AL_SAMPLE_OFFSET_CLOCK_SOFT: + values[0] = GetSourceSampleOffset(Source, Context, &srcclock); + values[1] = srcclock; + return AL_TRUE; + /* 1x float/double */ case AL_CONE_INNER_ANGLE: case AL_CONE_OUTER_ANGLE: @@ -1444,7 +1598,6 @@ static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp case AL_AIR_ABSORPTION_FACTOR: case AL_ROOM_ROLLOFF_FACTOR: case AL_CONE_OUTER_GAINHF: - case AL_SEC_LENGTH_SOFT: case AL_SOURCE_RADIUS: if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE) *values = (ALint64)dvals[0]; @@ -1481,14 +1634,14 @@ static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp case AL_SOURCE_STATE: case AL_BUFFERS_QUEUED: case AL_BUFFERS_PROCESSED: - case AL_BYTE_LENGTH_SOFT: - case AL_SAMPLE_LENGTH_SOFT: case AL_SOURCE_TYPE: case AL_DIRECT_FILTER_GAINHF_AUTO: case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: case AL_DIRECT_CHANNELS_SOFT: case AL_DISTANCE_MODEL: + case AL_SOURCE_RESAMPLER_SOFT: + case AL_SOURCE_SPATIALIZE_SOFT: if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE) *values = ivals[0]; return err; @@ -1511,13 +1664,15 @@ static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp return err; case AL_SEC_OFFSET_LATENCY_SOFT: + case AL_SEC_OFFSET_CLOCK_SOFT: break; /* Double only */ case AL_STEREO_ANGLES: break; /* Float/double only */ } ERR("Unexpected property: 0x%04x\n", prop); - SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE); + SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer64 property 0x%04x", + prop); } @@ -1525,40 +1680,23 @@ AL_API ALvoid AL_APIENTRY alGenSources(ALsizei n, ALuint *sources) { ALCcontext *context; ALsizei cur = 0; - ALenum err; context = GetContextRef(); if(!context) return; if(!(n >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - for(cur = 0;cur < n;cur++) + alSetError(context, AL_INVALID_VALUE, "Generating %d sources", n); + else for(cur = 0;cur < n;cur++) { - ALsource *source = al_calloc(16, sizeof(ALsource)); + ALsource *source = AllocSource(context); if(!source) { alDeleteSources(cur, sources); - SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done); + break; } - InitSourceParams(source); - - err = NewThunkEntry(&source->id); - if(err == AL_NO_ERROR) - err = InsertUIntMapEntry(&context->SourceMap, source->id, source); - if(err != AL_NO_ERROR) - { - FreeThunkEntry(source->id); - memset(source, 0, sizeof(ALsource)); - al_free(source); - - alDeleteSources(cur, sources); - SET_ERROR_AND_GOTO(context, err, done); - } - sources[cur] = source->id; } -done: ALCcontext_DecRef(context); } @@ -1572,44 +1710,24 @@ AL_API ALvoid AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources) context = GetContextRef(); if(!context) return; - LockSourcesWrite(context); + LockSourceList(context); if(!(n >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Deleting %d sources", n); /* Check that all Sources are valid */ for(i = 0;i < n;i++) { if(LookupSource(context, sources[i]) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", sources[i]); } for(i = 0;i < n;i++) { - ALvoice *voice, *voice_end; - - if((Source=RemoveSource(context, sources[i])) == NULL) - continue; - FreeThunkEntry(Source->id); - - LockContext(context); - voice = context->Voices; - voice_end = voice + context->VoiceCount; - while(voice != voice_end) - { - ALsource *old = Source; - if(COMPARE_EXCHANGE(&voice->Source, &old, NULL)) - break; - voice++; - } - UnlockContext(context); - - DeinitSource(Source); - - memset(Source, 0, sizeof(*Source)); - al_free(Source); + if((Source=LookupSource(context, sources[i])) != NULL) + FreeSource(context, Source); } done: - UnlockSourcesWrite(context); + UnlockSourceList(context); ALCcontext_DecRef(context); } @@ -1622,9 +1740,9 @@ AL_API ALboolean AL_APIENTRY alIsSource(ALuint source) context = GetContextRef(); if(!context) return AL_FALSE; - LockSourcesRead(context); + LockSourceList(context); ret = (LookupSource(context, source) ? AL_TRUE : AL_FALSE); - UnlockSourcesRead(context); + UnlockSourceList(context); ALCcontext_DecRef(context); @@ -1640,16 +1758,16 @@ AL_API ALvoid AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value) Context = GetContextRef(); if(!Context) return; - WriteLock(&Context->PropLock); - LockSourcesRead(Context); + almtx_lock(&Context->PropLock); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!(FloatValsByProp(param) == 1)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid float property 0x%04x", param); else SetSourcefv(Source, Context, param, &value); - UnlockSourcesRead(Context); - WriteUnlock(&Context->PropLock); + UnlockSourceList(Context); + almtx_unlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1662,19 +1780,19 @@ AL_API ALvoid AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1 Context = GetContextRef(); if(!Context) return; - WriteLock(&Context->PropLock); - LockSourcesRead(Context); + almtx_lock(&Context->PropLock); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!(FloatValsByProp(param) == 3)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid 3-float property 0x%04x", param); else { ALfloat fvals[3] = { value1, value2, value3 }; SetSourcefv(Source, Context, param, fvals); } - UnlockSourcesRead(Context); - WriteUnlock(&Context->PropLock); + UnlockSourceList(Context); + almtx_unlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1687,18 +1805,18 @@ AL_API ALvoid AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat Context = GetContextRef(); if(!Context) return; - WriteLock(&Context->PropLock); - LockSourcesRead(Context); + almtx_lock(&Context->PropLock); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!values) - alSetError(Context, AL_INVALID_VALUE); + alSetError(Context, AL_INVALID_VALUE, "NULL pointer"); else if(!(FloatValsByProp(param) > 0)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid float-vector property 0x%04x", param); else SetSourcefv(Source, Context, param, values); - UnlockSourcesRead(Context); - WriteUnlock(&Context->PropLock); + UnlockSourceList(Context); + almtx_unlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1712,19 +1830,19 @@ AL_API ALvoid AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble va Context = GetContextRef(); if(!Context) return; - WriteLock(&Context->PropLock); - LockSourcesRead(Context); + almtx_lock(&Context->PropLock); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!(DoubleValsByProp(param) == 1)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid double property 0x%04x", param); else { ALfloat fval = (ALfloat)value; SetSourcefv(Source, Context, param, &fval); } - UnlockSourcesRead(Context); - WriteUnlock(&Context->PropLock); + UnlockSourceList(Context); + almtx_unlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1737,19 +1855,19 @@ AL_API ALvoid AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble v Context = GetContextRef(); if(!Context) return; - WriteLock(&Context->PropLock); - LockSourcesRead(Context); + almtx_lock(&Context->PropLock); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!(DoubleValsByProp(param) == 3)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid 3-double property 0x%04x", param); else { ALfloat fvals[3] = { (ALfloat)value1, (ALfloat)value2, (ALfloat)value3 }; SetSourcefv(Source, Context, param, fvals); } - UnlockSourcesRead(Context); - WriteUnlock(&Context->PropLock); + UnlockSourceList(Context); + almtx_unlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1763,14 +1881,14 @@ AL_API ALvoid AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdo Context = GetContextRef(); if(!Context) return; - WriteLock(&Context->PropLock); - LockSourcesRead(Context); + almtx_lock(&Context->PropLock); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!values) - alSetError(Context, AL_INVALID_VALUE); + alSetError(Context, AL_INVALID_VALUE, "NULL pointer"); else if(!((count=DoubleValsByProp(param)) > 0 && count <= 6)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid double-vector property 0x%04x", param); else { ALfloat fvals[6]; @@ -1780,8 +1898,8 @@ AL_API ALvoid AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdo fvals[i] = (ALfloat)values[i]; SetSourcefv(Source, Context, param, fvals); } - UnlockSourcesRead(Context); - WriteUnlock(&Context->PropLock); + UnlockSourceList(Context); + almtx_unlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1795,16 +1913,16 @@ AL_API ALvoid AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value) Context = GetContextRef(); if(!Context) return; - WriteLock(&Context->PropLock); - LockSourcesRead(Context); + almtx_lock(&Context->PropLock); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!(IntValsByProp(param) == 1)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid integer property 0x%04x", param); else SetSourceiv(Source, Context, param, &value); - UnlockSourcesRead(Context); - WriteUnlock(&Context->PropLock); + UnlockSourceList(Context); + almtx_unlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1817,19 +1935,19 @@ AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, AL Context = GetContextRef(); if(!Context) return; - WriteLock(&Context->PropLock); - LockSourcesRead(Context); + almtx_lock(&Context->PropLock); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!(IntValsByProp(param) == 3)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid 3-integer property 0x%04x", param); else { ALint ivals[3] = { value1, value2, value3 }; SetSourceiv(Source, Context, param, ivals); } - UnlockSourcesRead(Context); - WriteUnlock(&Context->PropLock); + UnlockSourceList(Context); + almtx_unlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1842,18 +1960,18 @@ AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *val Context = GetContextRef(); if(!Context) return; - WriteLock(&Context->PropLock); - LockSourcesRead(Context); + almtx_lock(&Context->PropLock); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!values) - alSetError(Context, AL_INVALID_VALUE); + alSetError(Context, AL_INVALID_VALUE, "NULL pointer"); else if(!(IntValsByProp(param) > 0)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid integer-vector property 0x%04x", param); else SetSourceiv(Source, Context, param, values); - UnlockSourcesRead(Context); - WriteUnlock(&Context->PropLock); + UnlockSourceList(Context); + almtx_unlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1867,16 +1985,16 @@ AL_API ALvoid AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SO Context = GetContextRef(); if(!Context) return; - WriteLock(&Context->PropLock); - LockSourcesRead(Context); + almtx_lock(&Context->PropLock); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!(Int64ValsByProp(param) == 1)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid integer64 property 0x%04x", param); else SetSourcei64v(Source, Context, param, &value); - UnlockSourcesRead(Context); - WriteUnlock(&Context->PropLock); + UnlockSourceList(Context); + almtx_unlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1889,19 +2007,19 @@ AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOF Context = GetContextRef(); if(!Context) return; - WriteLock(&Context->PropLock); - LockSourcesRead(Context); + almtx_lock(&Context->PropLock); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!(Int64ValsByProp(param) == 3)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid 3-integer64 property 0x%04x", param); else { ALint64SOFT i64vals[3] = { value1, value2, value3 }; SetSourcei64v(Source, Context, param, i64vals); } - UnlockSourcesRead(Context); - WriteUnlock(&Context->PropLock); + UnlockSourceList(Context); + almtx_unlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1914,18 +2032,18 @@ AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALin Context = GetContextRef(); if(!Context) return; - WriteLock(&Context->PropLock); - LockSourcesRead(Context); + almtx_lock(&Context->PropLock); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!values) - alSetError(Context, AL_INVALID_VALUE); + alSetError(Context, AL_INVALID_VALUE, "NULL pointer"); else if(!(Int64ValsByProp(param) > 0)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid integer64-vector property 0x%04x", param); else SetSourcei64v(Source, Context, param, values); - UnlockSourcesRead(Context); - WriteUnlock(&Context->PropLock); + UnlockSourceList(Context); + almtx_unlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1939,22 +2057,20 @@ AL_API ALvoid AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *val Context = GetContextRef(); if(!Context) return; - ReadLock(&Context->PropLock); - LockSourcesRead(Context); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!value) - alSetError(Context, AL_INVALID_VALUE); + alSetError(Context, AL_INVALID_VALUE, "NULL pointer"); else if(!(FloatValsByProp(param) == 1)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid float property 0x%04x", param); else { ALdouble dval; if(GetSourcedv(Source, Context, param, &dval)) *value = (ALfloat)dval; } - UnlockSourcesRead(Context); - ReadUnlock(&Context->PropLock); + UnlockSourceList(Context); ALCcontext_DecRef(Context); } @@ -1968,14 +2084,13 @@ AL_API ALvoid AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *va Context = GetContextRef(); if(!Context) return; - ReadLock(&Context->PropLock); - LockSourcesRead(Context); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!(value1 && value2 && value3)) - alSetError(Context, AL_INVALID_VALUE); + alSetError(Context, AL_INVALID_VALUE, "NULL pointer"); else if(!(FloatValsByProp(param) == 3)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid 3-float property 0x%04x", param); else { ALdouble dvals[3]; @@ -1986,8 +2101,7 @@ AL_API ALvoid AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *va *value3 = (ALfloat)dvals[2]; } } - UnlockSourcesRead(Context); - ReadUnlock(&Context->PropLock); + UnlockSourceList(Context); ALCcontext_DecRef(Context); } @@ -2002,14 +2116,13 @@ AL_API ALvoid AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *va Context = GetContextRef(); if(!Context) return; - ReadLock(&Context->PropLock); - LockSourcesRead(Context); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!values) - alSetError(Context, AL_INVALID_VALUE); + alSetError(Context, AL_INVALID_VALUE, "NULL pointer"); else if(!((count=FloatValsByProp(param)) > 0 && count <= 6)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid float-vector property 0x%04x", param); else { ALdouble dvals[6]; @@ -2020,8 +2133,7 @@ AL_API ALvoid AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *va values[i] = (ALfloat)dvals[i]; } } - UnlockSourcesRead(Context); - ReadUnlock(&Context->PropLock); + UnlockSourceList(Context); ALCcontext_DecRef(Context); } @@ -2035,18 +2147,16 @@ AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble * Context = GetContextRef(); if(!Context) return; - ReadLock(&Context->PropLock); - LockSourcesRead(Context); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!value) - alSetError(Context, AL_INVALID_VALUE); + alSetError(Context, AL_INVALID_VALUE, "NULL pointer"); else if(!(DoubleValsByProp(param) == 1)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid double property 0x%04x", param); else GetSourcedv(Source, Context, param, value); - UnlockSourcesRead(Context); - ReadUnlock(&Context->PropLock); + UnlockSourceList(Context); ALCcontext_DecRef(Context); } @@ -2059,14 +2169,13 @@ AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble Context = GetContextRef(); if(!Context) return; - ReadLock(&Context->PropLock); - LockSourcesRead(Context); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!(value1 && value2 && value3)) - alSetError(Context, AL_INVALID_VALUE); + alSetError(Context, AL_INVALID_VALUE, "NULL pointer"); else if(!(DoubleValsByProp(param) == 3)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid 3-double property 0x%04x", param); else { ALdouble dvals[3]; @@ -2077,8 +2186,7 @@ AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value3 = dvals[2]; } } - UnlockSourcesRead(Context); - ReadUnlock(&Context->PropLock); + UnlockSourceList(Context); ALCcontext_DecRef(Context); } @@ -2091,18 +2199,16 @@ AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble Context = GetContextRef(); if(!Context) return; - ReadLock(&Context->PropLock); - LockSourcesRead(Context); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!values) - alSetError(Context, AL_INVALID_VALUE); + alSetError(Context, AL_INVALID_VALUE, "NULL pointer"); else if(!(DoubleValsByProp(param) > 0)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid double-vector property 0x%04x", param); else GetSourcedv(Source, Context, param, values); - UnlockSourcesRead(Context); - ReadUnlock(&Context->PropLock); + UnlockSourceList(Context); ALCcontext_DecRef(Context); } @@ -2116,18 +2222,16 @@ AL_API ALvoid AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value Context = GetContextRef(); if(!Context) return; - ReadLock(&Context->PropLock); - LockSourcesRead(Context); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!value) - alSetError(Context, AL_INVALID_VALUE); + alSetError(Context, AL_INVALID_VALUE, "NULL pointer"); else if(!(IntValsByProp(param) == 1)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid integer property 0x%04x", param); else GetSourceiv(Source, Context, param, value); - UnlockSourcesRead(Context); - ReadUnlock(&Context->PropLock); + UnlockSourceList(Context); ALCcontext_DecRef(Context); } @@ -2141,14 +2245,13 @@ AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1 Context = GetContextRef(); if(!Context) return; - ReadLock(&Context->PropLock); - LockSourcesRead(Context); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!(value1 && value2 && value3)) - alSetError(Context, AL_INVALID_VALUE); + alSetError(Context, AL_INVALID_VALUE, "NULL pointer"); else if(!(IntValsByProp(param) == 3)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid 3-integer property 0x%04x", param); else { ALint ivals[3]; @@ -2159,8 +2262,7 @@ AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1 *value3 = ivals[2]; } } - UnlockSourcesRead(Context); - ReadUnlock(&Context->PropLock); + UnlockSourceList(Context); ALCcontext_DecRef(Context); } @@ -2174,18 +2276,16 @@ AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values Context = GetContextRef(); if(!Context) return; - ReadLock(&Context->PropLock); - LockSourcesRead(Context); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!values) - alSetError(Context, AL_INVALID_VALUE); + alSetError(Context, AL_INVALID_VALUE, "NULL pointer"); else if(!(IntValsByProp(param) > 0)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid integer-vector property 0x%04x", param); else GetSourceiv(Source, Context, param, values); - UnlockSourcesRead(Context); - ReadUnlock(&Context->PropLock); + UnlockSourceList(Context); ALCcontext_DecRef(Context); } @@ -2199,18 +2299,16 @@ AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64S Context = GetContextRef(); if(!Context) return; - ReadLock(&Context->PropLock); - LockSourcesRead(Context); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!value) - alSetError(Context, AL_INVALID_VALUE); + alSetError(Context, AL_INVALID_VALUE, "NULL pointer"); else if(!(Int64ValsByProp(param) == 1)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid integer64 property 0x%04x", param); else GetSourcei64v(Source, Context, param, value); - UnlockSourcesRead(Context); - ReadUnlock(&Context->PropLock); + UnlockSourceList(Context); ALCcontext_DecRef(Context); } @@ -2223,14 +2321,13 @@ AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64 Context = GetContextRef(); if(!Context) return; - ReadLock(&Context->PropLock); - LockSourcesRead(Context); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!(value1 && value2 && value3)) - alSetError(Context, AL_INVALID_VALUE); + alSetError(Context, AL_INVALID_VALUE, "NULL pointer"); else if(!(Int64ValsByProp(param) == 3)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid 3-integer64 property 0x%04x", param); else { ALint64 i64vals[3]; @@ -2241,8 +2338,7 @@ AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64 *value3 = i64vals[2]; } } - UnlockSourcesRead(Context); - ReadUnlock(&Context->PropLock); + UnlockSourceList(Context); ALCcontext_DecRef(Context); } @@ -2255,18 +2351,16 @@ AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64 Context = GetContextRef(); if(!Context) return; - ReadLock(&Context->PropLock); - LockSourcesRead(Context); + LockSourceList(Context); if((Source=LookupSource(Context, source)) == NULL) - alSetError(Context, AL_INVALID_NAME); + alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source); else if(!values) - alSetError(Context, AL_INVALID_VALUE); + alSetError(Context, AL_INVALID_VALUE, "NULL pointer"); else if(!(Int64ValsByProp(param) > 0)) - alSetError(Context, AL_INVALID_ENUM); + alSetError(Context, AL_INVALID_ENUM, "Invalid integer64-vector property 0x%04x", param); else GetSourcei64v(Source, Context, param, values); - UnlockSourcesRead(Context); - ReadUnlock(&Context->PropLock); + UnlockSourceList(Context); ALCcontext_DecRef(Context); } @@ -2279,63 +2373,183 @@ AL_API ALvoid AL_APIENTRY alSourcePlay(ALuint source) AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources) { ALCcontext *context; + ALCdevice *device; ALsource *source; - ALsizei i; + ALvoice *voice; + ALsizei i, j; context = GetContextRef(); if(!context) return; - LockSourcesRead(context); + LockSourceList(context); if(!(n >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Playing %d sources", n); for(i = 0;i < n;i++) { if(!LookupSource(context, sources[i])) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", sources[i]); + } + + device = context->Device; + ALCdevice_Lock(device); + /* If the device is disconnected, go right to stopped. */ + if(!ATOMIC_LOAD(&device->Connected, almemory_order_acquire)) + { + /* TODO: Send state change event? */ + for(i = 0;i < n;i++) + { + source = LookupSource(context, sources[i]); + source->OffsetType = AL_NONE; + source->Offset = 0.0; + source->state = AL_STOPPED; + } + ALCdevice_Unlock(device); + goto done; } - LockContext(context); while(n > context->MaxVoices-context->VoiceCount) { - ALvoice *temp = NULL; - ALsizei newcount; - - newcount = context->MaxVoices << 1; - if(newcount > 0) - temp = al_malloc(16, newcount * sizeof(context->Voices[0])); - if(!temp) + ALsizei newcount = context->MaxVoices << 1; + if(context->MaxVoices >= newcount) { - UnlockContext(context); - SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done); + ALCdevice_Unlock(device); + SETERR_GOTO(context, AL_OUT_OF_MEMORY, done, + "Overflow increasing voice count %d -> %d", context->MaxVoices, newcount); } - memcpy(temp, context->Voices, context->MaxVoices * sizeof(temp[0])); - memset(&temp[context->MaxVoices], 0, (newcount-context->MaxVoices) * sizeof(temp[0])); - - al_free(context->Voices); - context->Voices = temp; - context->MaxVoices = newcount; + AllocateVoices(context, newcount, device->NumAuxSends); } - if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire) == DeferAll) + for(i = 0;i < n;i++) { - for(i = 0;i < n;i++) + ALbufferlistitem *BufferList; + bool start_fading = false; + ALint vidx = -1; + + source = LookupSource(context, sources[i]); + /* Check that there is a queue containing at least one valid, non zero + * length buffer. + */ + BufferList = source->queue; + while(BufferList && BufferList->max_samples == 0) + BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed); + + /* If there's nothing to play, go right to stopped. */ + if(UNLIKELY(!BufferList)) { - source = LookupSource(context, sources[i]); - source->new_state = AL_PLAYING; + /* NOTE: A source without any playable buffers should not have an + * ALvoice since it shouldn't be in a playing or paused state. So + * there's no need to look up its voice and clear the source. + */ + ALenum oldstate = GetSourceState(source, NULL); + source->OffsetType = AL_NONE; + source->Offset = 0.0; + if(oldstate != AL_STOPPED) + { + source->state = AL_STOPPED; + SendStateChangeEvent(context, source->id, AL_STOPPED); + } + continue; } - } - else - { - for(i = 0;i < n;i++) + + voice = GetSourceVoice(source, context); + switch(GetSourceState(source, voice)) { - source = LookupSource(context, sources[i]); - SetSourceState(source, context, AL_PLAYING); + case AL_PLAYING: + assert(voice != NULL); + /* A source that's already playing is restarted from the beginning. */ + ATOMIC_STORE(&voice->current_buffer, BufferList, almemory_order_relaxed); + ATOMIC_STORE(&voice->position, 0, almemory_order_relaxed); + ATOMIC_STORE(&voice->position_fraction, 0, almemory_order_release); + continue; + + case AL_PAUSED: + assert(voice != NULL); + /* A source that's paused simply resumes. */ + ATOMIC_STORE(&voice->Playing, true, almemory_order_release); + source->state = AL_PLAYING; + SendStateChangeEvent(context, source->id, AL_PLAYING); + continue; + + default: + break; } + + /* Look for an unused voice to play this source with. */ + assert(voice == NULL); + for(j = 0;j < context->VoiceCount;j++) + { + if(ATOMIC_LOAD(&context->Voices[j]->Source, almemory_order_acquire) == NULL) + { + vidx = j; + break; + } + } + if(vidx == -1) + vidx = context->VoiceCount++; + voice = context->Voices[vidx]; + ATOMIC_STORE(&voice->Playing, false, almemory_order_release); + + ATOMIC_FLAG_TEST_AND_SET(&source->PropsClean, almemory_order_acquire); + UpdateSourceProps(source, voice, device->NumAuxSends, context); + + /* A source that's not playing or paused has any offset applied when it + * starts playing. + */ + if(source->Looping) + ATOMIC_STORE(&voice->loop_buffer, source->queue, almemory_order_relaxed); + else + ATOMIC_STORE(&voice->loop_buffer, NULL, almemory_order_relaxed); + ATOMIC_STORE(&voice->current_buffer, BufferList, almemory_order_relaxed); + ATOMIC_STORE(&voice->position, 0, almemory_order_relaxed); + ATOMIC_STORE(&voice->position_fraction, 0, almemory_order_relaxed); + if(ApplyOffset(source, voice) != AL_FALSE) + start_fading = ATOMIC_LOAD(&voice->position, almemory_order_relaxed) != 0 || + ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed) != 0 || + ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed) != BufferList; + + for(j = 0;j < BufferList->num_buffers;j++) + { + ALbuffer *buffer = BufferList->buffers[j]; + if(buffer) + { + voice->NumChannels = ChannelsFromFmt(buffer->FmtChannels); + voice->SampleSize = BytesFromFmt(buffer->FmtType); + break; + } + } + + /* Clear previous samples. */ + memset(voice->PrevSamples, 0, sizeof(voice->PrevSamples)); + + /* Clear the stepping value so the mixer knows not to mix this until + * the update gets applied. + */ + voice->Step = 0; + + voice->Flags = start_fading ? VOICE_IS_FADING : 0; + if(source->SourceType == AL_STATIC) voice->Flags |= VOICE_IS_STATIC; + memset(voice->Direct.Params, 0, sizeof(voice->Direct.Params[0])*voice->NumChannels); + for(j = 0;j < device->NumAuxSends;j++) + memset(voice->Send[j].Params, 0, sizeof(voice->Send[j].Params[0])*voice->NumChannels); + if(device->AvgSpeakerDist > 0.0f) + { + ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC / + (device->AvgSpeakerDist * device->Frequency); + for(j = 0;j < voice->NumChannels;j++) + NfcFilterCreate(&voice->Direct.Params[j].NFCtrlFilter, 0.0f, w1); + } + + ATOMIC_STORE(&voice->Source, source, almemory_order_relaxed); + ATOMIC_STORE(&voice->Playing, true, almemory_order_release); + source->state = AL_PLAYING; + source->VoiceIdx = vidx; + + SendStateChangeEvent(context, source->id, AL_PLAYING); } - UnlockContext(context); + ALCdevice_Unlock(device); done: - UnlockSourcesRead(context); + UnlockSourceList(context); ALCcontext_DecRef(context); } @@ -2346,42 +2560,40 @@ AL_API ALvoid AL_APIENTRY alSourcePause(ALuint source) AL_API ALvoid AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources) { ALCcontext *context; + ALCdevice *device; ALsource *source; + ALvoice *voice; ALsizei i; context = GetContextRef(); if(!context) return; - LockSourcesRead(context); + LockSourceList(context); if(!(n >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Pausing %d sources", n); for(i = 0;i < n;i++) { if(!LookupSource(context, sources[i])) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", sources[i]); } - LockContext(context); - if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) + device = context->Device; + ALCdevice_Lock(device); + for(i = 0;i < n;i++) { - for(i = 0;i < n;i++) + source = LookupSource(context, sources[i]); + if((voice=GetSourceVoice(source, context)) != NULL) + ATOMIC_STORE(&voice->Playing, false, almemory_order_release); + if(GetSourceState(source, voice) == AL_PLAYING) { - source = LookupSource(context, sources[i]); - source->new_state = AL_PAUSED; + source->state = AL_PAUSED; + SendStateChangeEvent(context, source->id, AL_PAUSED); } } - else - { - for(i = 0;i < n;i++) - { - source = LookupSource(context, sources[i]); - SetSourceState(source, context, AL_PAUSED); - } - } - UnlockContext(context); + ALCdevice_Unlock(device); done: - UnlockSourcesRead(context); + UnlockSourceList(context); ALCcontext_DecRef(context); } @@ -2392,32 +2604,48 @@ AL_API ALvoid AL_APIENTRY alSourceStop(ALuint source) AL_API ALvoid AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources) { ALCcontext *context; + ALCdevice *device; ALsource *source; + ALvoice *voice; ALsizei i; context = GetContextRef(); if(!context) return; - LockSourcesRead(context); + LockSourceList(context); if(!(n >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Stopping %d sources", n); for(i = 0;i < n;i++) { if(!LookupSource(context, sources[i])) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", sources[i]); } - LockContext(context); + device = context->Device; + ALCdevice_Lock(device); for(i = 0;i < n;i++) { + ALenum oldstate; source = LookupSource(context, sources[i]); - source->new_state = AL_NONE; - SetSourceState(source, context, AL_STOPPED); + if((voice=GetSourceVoice(source, context)) != NULL) + { + ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed); + ATOMIC_STORE(&voice->Playing, false, almemory_order_release); + voice = NULL; + } + oldstate = GetSourceState(source, voice); + if(oldstate != AL_INITIAL && oldstate != AL_STOPPED) + { + source->state = AL_STOPPED; + SendStateChangeEvent(context, source->id, AL_STOPPED); + } + source->OffsetType = AL_NONE; + source->Offset = 0.0; } - UnlockContext(context); + ALCdevice_Unlock(device); done: - UnlockSourcesRead(context); + UnlockSourceList(context); ALCcontext_DecRef(context); } @@ -2428,32 +2656,46 @@ AL_API ALvoid AL_APIENTRY alSourceRewind(ALuint source) AL_API ALvoid AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources) { ALCcontext *context; + ALCdevice *device; ALsource *source; + ALvoice *voice; ALsizei i; context = GetContextRef(); if(!context) return; - LockSourcesRead(context); + LockSourceList(context); if(!(n >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Rewinding %d sources", n); for(i = 0;i < n;i++) { if(!LookupSource(context, sources[i])) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", sources[i]); } - LockContext(context); + device = context->Device; + ALCdevice_Lock(device); for(i = 0;i < n;i++) { source = LookupSource(context, sources[i]); - source->new_state = AL_NONE; - SetSourceState(source, context, AL_INITIAL); + if((voice=GetSourceVoice(source, context)) != NULL) + { + ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed); + ATOMIC_STORE(&voice->Playing, false, almemory_order_release); + voice = NULL; + } + if(GetSourceState(source, voice) != AL_INITIAL) + { + source->state = AL_INITIAL; + SendStateChangeEvent(context, source->id, AL_INITIAL); + } + source->OffsetType = AL_NONE; + source->Offset = 0.0; } - UnlockContext(context); + ALCdevice_Unlock(device); done: - UnlockSourcesRead(context); + UnlockSourceList(context); ALCcontext_DecRef(context); } @@ -2476,126 +2718,111 @@ AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALu device = context->Device; - LockSourcesRead(context); + LockSourceList(context); if(!(nb >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Queueing %d buffers", nb); if((source=LookupSource(context, src)) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", src); - WriteLock(&source->queue_lock); if(source->SourceType == AL_STATIC) { - WriteUnlock(&source->queue_lock); /* Can't queue on a Static Source */ - SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + SETERR_GOTO(context, AL_INVALID_OPERATION, done, "Queueing onto static source %u", src); } /* Check for a valid Buffer, for its frequency and format */ - BufferList = ATOMIC_LOAD(&source->queue); + BufferList = source->queue; while(BufferList) { - if(BufferList->buffer) + for(i = 0;i < BufferList->num_buffers;i++) { - BufferFmt = BufferList->buffer; - break; + if((BufferFmt=BufferList->buffers[i]) != NULL) + break; } - BufferList = BufferList->next; + if(BufferFmt) break; + BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed); } - LockBuffersRead(device); + LockBufferList(device); BufferListStart = NULL; BufferList = NULL; for(i = 0;i < nb;i++) { ALbuffer *buffer = NULL; if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == NULL) - { - WriteUnlock(&source->queue_lock); - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, buffer_error); - } + SETERR_GOTO(context, AL_INVALID_NAME, buffer_error, "Queueing invalid buffer ID %u", + buffers[i]); if(!BufferListStart) { - BufferListStart = malloc(sizeof(ALbufferlistitem)); + BufferListStart = al_calloc(DEF_ALIGN, + FAM_SIZE(ALbufferlistitem, buffers, 1)); BufferList = BufferListStart; } else { - BufferList->next = malloc(sizeof(ALbufferlistitem)); - BufferList = BufferList->next; + ALbufferlistitem *item = al_calloc(DEF_ALIGN, + FAM_SIZE(ALbufferlistitem, buffers, 1)); + ATOMIC_STORE(&BufferList->next, item, almemory_order_relaxed); + BufferList = item; } - BufferList->buffer = buffer; - BufferList->next = NULL; + ATOMIC_INIT(&BufferList->next, NULL); + BufferList->max_samples = buffer ? buffer->SampleLen : 0; + BufferList->num_buffers = 1; + BufferList->buffers[0] = buffer; if(!buffer) continue; - /* Hold a read lock on each buffer being queued while checking all - * provided buffers. This is done so other threads don't see an extra - * reference on some buffers if this operation ends up failing. */ - ReadLock(&buffer->lock); IncrementRef(&buffer->ref); - if(BufferFmt == NULL) - { - BufferFmt = buffer; + if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) + SETERR_GOTO(context, AL_INVALID_OPERATION, buffer_error, + "Queueing non-persistently mapped buffer %u", buffer->id); - source->NumChannels = ChannelsFromFmt(buffer->FmtChannels); - source->SampleSize = BytesFromFmt(buffer->FmtType); - } + if(BufferFmt == NULL) + BufferFmt = buffer; else if(BufferFmt->Frequency != buffer->Frequency || - BufferFmt->OriginalChannels != buffer->OriginalChannels || + BufferFmt->FmtChannels != buffer->FmtChannels || BufferFmt->OriginalType != buffer->OriginalType) { - WriteUnlock(&source->queue_lock); - SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, buffer_error); + alSetError(context, AL_INVALID_OPERATION, "Queueing buffer with mismatched format"); buffer_error: /* A buffer failed (invalid ID or format), so unlock and release * each buffer we had. */ while(BufferListStart) { - ALbufferlistitem *next = BufferListStart->next; - if((buffer=BufferListStart->buffer) != NULL) + ALbufferlistitem *next = ATOMIC_LOAD(&BufferListStart->next, + almemory_order_relaxed); + for(i = 0;i < BufferListStart->num_buffers;i++) { - DecrementRef(&buffer->ref); - ReadUnlock(&buffer->lock); + if((buffer=BufferListStart->buffers[i]) != NULL) + DecrementRef(&buffer->ref); } - free(BufferListStart); + al_free(BufferListStart); BufferListStart = next; } - UnlockBuffersRead(device); + UnlockBufferList(device); goto done; } } - /* All buffers good, unlock them now. */ - BufferList = BufferListStart; - while(BufferList != NULL) - { - ALbuffer *buffer = BufferList->buffer; - if(buffer) ReadUnlock(&buffer->lock); - BufferList = BufferList->next; - } - UnlockBuffersRead(device); + /* All buffers good. */ + UnlockBufferList(device); /* Source is now streaming */ source->SourceType = AL_STREAMING; - BufferList = NULL; - if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALbufferlistitem*, &source->queue, &BufferList, BufferListStart)) + if(!(BufferList=source->queue)) + source->queue = BufferListStart; + else { - /* Queue head is not NULL, append to the end of the queue */ - while(BufferList->next != NULL) - BufferList = BufferList->next; - BufferList->next = BufferListStart; + ALbufferlistitem *next; + while((next=ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed)) != NULL) + BufferList = next; + ATOMIC_STORE(&BufferList->next, BufferListStart, almemory_order_release); } - /* If the current buffer was at the end (NULL), put it at the start of the newly queued - * buffers. - */ - BufferList = NULL; - ATOMIC_COMPARE_EXCHANGE_STRONG(ALbufferlistitem*, &source->current_buffer, &BufferList, BufferListStart); - WriteUnlock(&source->queue_lock); done: - UnlockSourcesRead(context); + UnlockSourceList(context); ALCcontext_DecRef(context); } @@ -2603,98 +2830,102 @@ AL_API ALvoid AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint { ALCcontext *context; ALsource *source; - ALbufferlistitem *OldHead; - ALbufferlistitem *OldTail; + ALbufferlistitem *BufferList; ALbufferlistitem *Current; - ALsizei i = 0; - - if(nb == 0) - return; + ALvoice *voice; + ALsizei i; context = GetContextRef(); if(!context) return; - LockSourcesRead(context); + LockSourceList(context); if(!(nb >= 0)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Unqueueing %d buffers", nb); if((source=LookupSource(context, src)) == NULL) - SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", src); - WriteLock(&source->queue_lock); - if(ATOMIC_LOAD(&source->looping) || source->SourceType != AL_STREAMING) - { - WriteUnlock(&source->queue_lock); - /* Trying to unqueue buffers on a looping or non-streaming source. */ - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - } + /* Nothing to unqueue. */ + if(nb == 0) goto done; - /* Find the new buffer queue head */ - OldTail = ATOMIC_LOAD(&source->queue); - Current = ATOMIC_LOAD(&source->current_buffer); - if(OldTail != Current) - { - for(i = 1;i < nb;i++) - { - ALbufferlistitem *next = OldTail->next; - if(!next || next == Current) break; - OldTail = next; - } - } - if(i != nb) - { - WriteUnlock(&source->queue_lock); - /* Trying to unqueue pending buffers. */ - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - } + if(source->Looping) + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Unqueueing from looping source %u", src); + if(source->SourceType != AL_STREAMING) + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Unqueueing from a non-streaming source %u", + src); - /* Swap it, and cut the new head from the old. */ - OldHead = ATOMIC_EXCHANGE(ALbufferlistitem*, &source->queue, OldTail->next); - if(OldTail->next) - { - ALCdevice *device = context->Device; - uint count; + /* Make sure enough buffers have been processed to unqueue. */ + BufferList = source->queue; + Current = NULL; + if((voice=GetSourceVoice(source, context)) != NULL) + Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed); + else if(source->state == AL_INITIAL) + Current = BufferList; + if(BufferList == Current) + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Unqueueing pending buffers"); - /* Once the active mix (if any) is done, it's safe to cut the old tail - * from the new head. + i = BufferList->num_buffers; + while(i < nb) + { + /* If the next bufferlist to check is NULL or is the current one, it's + * trying to unqueue pending buffers. */ - if(((count=ReadRef(&device->MixCount))&1) != 0) - { - while(count == ReadRef(&device->MixCount)) - althrd_yield(); - } - OldTail->next = NULL; + ALbufferlistitem *next = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed); + if(!next || next == Current) + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Unqueueing pending buffers"); + BufferList = next; + + i += BufferList->num_buffers; } - WriteUnlock(&source->queue_lock); - while(OldHead != NULL) + while(nb > 0) { - ALbufferlistitem *next = OldHead->next; - ALbuffer *buffer = OldHead->buffer; - - if(!buffer) - *(buffers++) = 0; - else + ALbufferlistitem *head = source->queue; + ALbufferlistitem *next = ATOMIC_LOAD(&head->next, almemory_order_relaxed); + for(i = 0;i < head->num_buffers && nb > 0;i++,nb--) { - *(buffers++) = buffer->id; - DecrementRef(&buffer->ref); + ALbuffer *buffer = head->buffers[i]; + if(!buffer) + *(buffers++) = 0; + else + { + *(buffers++) = buffer->id; + DecrementRef(&buffer->ref); + } + } + if(i < head->num_buffers) + { + /* This head has some buffers left over, so move them to the front + * and update the sample and buffer count. + */ + ALsizei max_length = 0; + ALsizei j = 0; + while(i < head->num_buffers) + { + ALbuffer *buffer = head->buffers[i++]; + if(buffer) max_length = maxi(max_length, buffer->SampleLen); + head->buffers[j++] = buffer; + } + head->max_samples = max_length; + head->num_buffers = j; + break; } - free(OldHead); - OldHead = next; + /* Otherwise, free this item and set the source queue head to the next + * one. + */ + al_free(head); + source->queue = next; } done: - UnlockSourcesRead(context); + UnlockSourceList(context); ALCcontext_DecRef(context); } -static void InitSourceParams(ALsource *Source) +static void InitSourceParams(ALsource *Source, ALsizei num_sends) { - ALuint i; - - RWLockInit(&Source->queue_lock); + ALsizei i; Source->InnerAngle = 360.0f; Source->OuterAngle = 360.0f; @@ -2716,7 +2947,7 @@ static void InitSourceParams(ALsource *Source) Source->Orientation[1][2] = 0.0f; Source->RefDistance = 1.0f; Source->MaxDistance = FLT_MAX; - Source->RollOffFactor = 1.0f; + Source->RolloffFactor = 1.0f; Source->Gain = 1.0f; Source->MinGain = 0.0f; Source->MaxGain = 1.0f; @@ -2729,22 +2960,27 @@ static void InitSourceParams(ALsource *Source) Source->AirAbsorptionFactor = 0.0f; Source->RoomRolloffFactor = 0.0f; Source->DopplerFactor = 1.0f; + Source->HeadRelative = AL_FALSE; + Source->Looping = AL_FALSE; + Source->DistanceModel = DefaultDistanceModel; + Source->Resampler = ResamplerDefault; Source->DirectChannels = AL_FALSE; + Source->Spatialize = SpatializeAuto; Source->StereoPan[0] = DEG2RAD( 30.0f); Source->StereoPan[1] = DEG2RAD(-30.0f); Source->Radius = 0.0f; - Source->DistanceModel = DefaultDistanceModel; - Source->Direct.Gain = 1.0f; Source->Direct.GainHF = 1.0f; Source->Direct.HFReference = LOWPASSFREQREF; Source->Direct.GainLF = 1.0f; Source->Direct.LFReference = HIGHPASSFREQREF; - for(i = 0;i < MAX_SENDS;i++) + Source->Send = al_calloc(16, num_sends*sizeof(Source->Send[0])); + for(i = 0;i < num_sends;i++) { + Source->Send[i].Slot = NULL; Source->Send[i].Gain = 1.0f; Source->Send[i].GainHF = 1.0f; Source->Send[i].HFReference = LOWPASSFREQREF; @@ -2756,353 +2992,198 @@ static void InitSourceParams(ALsource *Source) Source->OffsetType = AL_NONE; Source->SourceType = AL_UNDETERMINED; Source->state = AL_INITIAL; - Source->new_state = AL_NONE; - ATOMIC_INIT(&Source->queue, NULL); - ATOMIC_INIT(&Source->current_buffer, NULL); + Source->queue = NULL; - ATOMIC_INIT(&Source->position, 0); - ATOMIC_INIT(&Source->position_fraction, 0); + /* No way to do an 'init' here, so just test+set with relaxed ordering and + * ignore the test. + */ + ATOMIC_FLAG_TEST_AND_SET(&Source->PropsClean, almemory_order_relaxed); - ATOMIC_INIT(&Source->looping, AL_FALSE); - - ATOMIC_INIT(&Source->Update, NULL); - ATOMIC_INIT(&Source->FreeList, NULL); + Source->VoiceIdx = -1; } -static void DeinitSource(ALsource *source) +static void DeinitSource(ALsource *source, ALsizei num_sends) { ALbufferlistitem *BufferList; - struct ALsourceProps *props; - size_t count = 0; - size_t i; + ALsizei i; - props = ATOMIC_LOAD(&source->Update); - if(props) al_free(props); - - props = ATOMIC_LOAD(&source->FreeList, almemory_order_relaxed); - while(props) - { - struct ALsourceProps *next; - next = ATOMIC_LOAD(&props->next, almemory_order_relaxed); - al_free(props); - props = next; - ++count; - } - /* This is excessively spammy if it traces every source destruction, so - * just warn if it was unexpectedly large. - */ - if(count > 3) - WARN("Freed "SZFMT" Source property objects\n", count); - - BufferList = ATOMIC_EXCHANGE(ALbufferlistitem*, &source->queue, NULL); + BufferList = source->queue; while(BufferList != NULL) { - ALbufferlistitem *next = BufferList->next; - if(BufferList->buffer != NULL) - DecrementRef(&BufferList->buffer->ref); - free(BufferList); + ALbufferlistitem *next = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed); + for(i = 0;i < BufferList->num_buffers;i++) + { + if(BufferList->buffers[i] != NULL) + DecrementRef(&BufferList->buffers[i]->ref); + } + al_free(BufferList); BufferList = next; } + source->queue = NULL; - for(i = 0;i < MAX_SENDS;++i) + if(source->Send) { - if(source->Send[i].Slot) - DecrementRef(&source->Send[i].Slot->ref); - source->Send[i].Slot = NULL; + for(i = 0;i < num_sends;i++) + { + if(source->Send[i].Slot) + DecrementRef(&source->Send[i].Slot->ref); + source->Send[i].Slot = NULL; + } + al_free(source->Send); + source->Send = NULL; } } -static void UpdateSourceProps(ALsource *source, ALuint num_sends) +static void UpdateSourceProps(ALsource *source, ALvoice *voice, ALsizei num_sends, ALCcontext *context) { - struct ALsourceProps *props; - size_t i; + struct ALvoiceProps *props; + ALsizei i; /* Get an unused property container, or allocate a new one as needed. */ - props = ATOMIC_LOAD(&source->FreeList, almemory_order_acquire); + props = ATOMIC_LOAD(&context->FreeVoiceProps, almemory_order_acquire); if(!props) - props = al_calloc(16, sizeof(*props)); + props = al_calloc(16, FAM_SIZE(struct ALvoiceProps, Send, num_sends)); else { - struct ALsourceProps *next; + struct ALvoiceProps *next; do { next = ATOMIC_LOAD(&props->next, almemory_order_relaxed); - } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALsourceProps*, - &source->FreeList, &props, next, almemory_order_seq_cst, - almemory_order_consume) == 0); + } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&context->FreeVoiceProps, &props, next, + almemory_order_acq_rel, almemory_order_acquire) == 0); } /* Copy in current property values. */ - ATOMIC_STORE(&props->Pitch, source->Pitch, almemory_order_relaxed); - ATOMIC_STORE(&props->Gain, source->Gain, almemory_order_relaxed); - ATOMIC_STORE(&props->OuterGain, source->OuterGain, almemory_order_relaxed); - ATOMIC_STORE(&props->MinGain, source->MinGain, almemory_order_relaxed); - ATOMIC_STORE(&props->MaxGain, source->MaxGain, almemory_order_relaxed); - ATOMIC_STORE(&props->InnerAngle, source->InnerAngle, almemory_order_relaxed); - ATOMIC_STORE(&props->OuterAngle, source->OuterAngle, almemory_order_relaxed); - ATOMIC_STORE(&props->RefDistance, source->RefDistance, almemory_order_relaxed); - ATOMIC_STORE(&props->MaxDistance, source->MaxDistance, almemory_order_relaxed); - ATOMIC_STORE(&props->RollOffFactor, source->RollOffFactor, almemory_order_relaxed); + props->Pitch = source->Pitch; + props->Gain = source->Gain; + props->OuterGain = source->OuterGain; + props->MinGain = source->MinGain; + props->MaxGain = source->MaxGain; + props->InnerAngle = source->InnerAngle; + props->OuterAngle = source->OuterAngle; + props->RefDistance = source->RefDistance; + props->MaxDistance = source->MaxDistance; + props->RolloffFactor = source->RolloffFactor; for(i = 0;i < 3;i++) - ATOMIC_STORE(&props->Position[i], source->Position[i], almemory_order_relaxed); + props->Position[i] = source->Position[i]; for(i = 0;i < 3;i++) - ATOMIC_STORE(&props->Velocity[i], source->Velocity[i], almemory_order_relaxed); + props->Velocity[i] = source->Velocity[i]; for(i = 0;i < 3;i++) - ATOMIC_STORE(&props->Direction[i], source->Direction[i], almemory_order_relaxed); + props->Direction[i] = source->Direction[i]; for(i = 0;i < 2;i++) { - size_t j; + ALsizei j; for(j = 0;j < 3;j++) - ATOMIC_STORE(&props->Orientation[i][j], source->Orientation[i][j], - almemory_order_relaxed); + props->Orientation[i][j] = source->Orientation[i][j]; } - ATOMIC_STORE(&props->HeadRelative, source->HeadRelative, almemory_order_relaxed); - ATOMIC_STORE(&props->DistanceModel, source->DistanceModel, almemory_order_relaxed); - ATOMIC_STORE(&props->DirectChannels, source->DirectChannels, almemory_order_relaxed); + props->HeadRelative = source->HeadRelative; + props->DistanceModel = source->DistanceModel; + props->Resampler = source->Resampler; + props->DirectChannels = source->DirectChannels; + props->SpatializeMode = source->Spatialize; - ATOMIC_STORE(&props->DryGainHFAuto, source->DryGainHFAuto, almemory_order_relaxed); - ATOMIC_STORE(&props->WetGainAuto, source->WetGainAuto, almemory_order_relaxed); - ATOMIC_STORE(&props->WetGainHFAuto, source->WetGainHFAuto, almemory_order_relaxed); - ATOMIC_STORE(&props->OuterGainHF, source->OuterGainHF, almemory_order_relaxed); + props->DryGainHFAuto = source->DryGainHFAuto; + props->WetGainAuto = source->WetGainAuto; + props->WetGainHFAuto = source->WetGainHFAuto; + props->OuterGainHF = source->OuterGainHF; - ATOMIC_STORE(&props->AirAbsorptionFactor, source->AirAbsorptionFactor, almemory_order_relaxed); - ATOMIC_STORE(&props->RoomRolloffFactor, source->RoomRolloffFactor, almemory_order_relaxed); - ATOMIC_STORE(&props->DopplerFactor, source->DopplerFactor, almemory_order_relaxed); + props->AirAbsorptionFactor = source->AirAbsorptionFactor; + props->RoomRolloffFactor = source->RoomRolloffFactor; + props->DopplerFactor = source->DopplerFactor; - ATOMIC_STORE(&props->StereoPan[0], source->StereoPan[0], almemory_order_relaxed); - ATOMIC_STORE(&props->StereoPan[1], source->StereoPan[1], almemory_order_relaxed); + props->StereoPan[0] = source->StereoPan[0]; + props->StereoPan[1] = source->StereoPan[1]; - ATOMIC_STORE(&props->Radius, source->Radius, almemory_order_relaxed); + props->Radius = source->Radius; - ATOMIC_STORE(&props->Direct.Gain, source->Direct.Gain, almemory_order_relaxed); - ATOMIC_STORE(&props->Direct.GainHF, source->Direct.GainHF, almemory_order_relaxed); - ATOMIC_STORE(&props->Direct.HFReference, source->Direct.HFReference, almemory_order_relaxed); - ATOMIC_STORE(&props->Direct.GainLF, source->Direct.GainLF, almemory_order_relaxed); - ATOMIC_STORE(&props->Direct.LFReference, source->Direct.LFReference, almemory_order_relaxed); + props->Direct.Gain = source->Direct.Gain; + props->Direct.GainHF = source->Direct.GainHF; + props->Direct.HFReference = source->Direct.HFReference; + props->Direct.GainLF = source->Direct.GainLF; + props->Direct.LFReference = source->Direct.LFReference; for(i = 0;i < num_sends;i++) { - ATOMIC_STORE(&props->Send[i].Slot, source->Send[i].Slot, almemory_order_relaxed); - ATOMIC_STORE(&props->Send[i].Gain, source->Send[i].Gain, almemory_order_relaxed); - ATOMIC_STORE(&props->Send[i].GainHF, source->Send[i].GainHF, almemory_order_relaxed); - ATOMIC_STORE(&props->Send[i].HFReference, source->Send[i].HFReference, - almemory_order_relaxed); - ATOMIC_STORE(&props->Send[i].GainLF, source->Send[i].GainLF, almemory_order_relaxed); - ATOMIC_STORE(&props->Send[i].LFReference, source->Send[i].LFReference, - almemory_order_relaxed); + props->Send[i].Slot = source->Send[i].Slot; + props->Send[i].Gain = source->Send[i].Gain; + props->Send[i].GainHF = source->Send[i].GainHF; + props->Send[i].HFReference = source->Send[i].HFReference; + props->Send[i].GainLF = source->Send[i].GainLF; + props->Send[i].LFReference = source->Send[i].LFReference; } /* Set the new container for updating internal parameters. */ - props = ATOMIC_EXCHANGE(struct ALsourceProps*, &source->Update, props, almemory_order_acq_rel); + props = ATOMIC_EXCHANGE_PTR(&voice->Update, props, almemory_order_acq_rel); if(props) { /* If there was an unused update container, put it back in the * freelist. */ - struct ALsourceProps *first = ATOMIC_LOAD(&source->FreeList); - do { - ATOMIC_STORE(&props->next, first, almemory_order_relaxed); - } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALsourceProps*, - &source->FreeList, &first, props) == 0); + ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &context->FreeVoiceProps, props); } } void UpdateAllSourceProps(ALCcontext *context) { - ALuint num_sends = context->Device->NumAuxSends; + ALsizei num_sends = context->Device->NumAuxSends; ALsizei pos; for(pos = 0;pos < context->VoiceCount;pos++) { - ALvoice *voice = &context->Voices[pos]; - ALsource *source = voice->Source; - if(source != NULL && (source->state == AL_PLAYING || - source->state == AL_PAUSED)) - UpdateSourceProps(source, num_sends); + ALvoice *voice = context->Voices[pos]; + ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire); + if(source && !ATOMIC_FLAG_TEST_AND_SET(&source->PropsClean, almemory_order_acq_rel)) + UpdateSourceProps(source, voice, num_sends, context); } } -/* SetSourceState - * - * Sets the source's new play state given its current state. - */ -ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state) -{ - WriteLock(&Source->queue_lock); - if(state == AL_PLAYING) - { - ALCdevice *device = Context->Device; - ALbufferlistitem *BufferList; - ALboolean discontinuity; - ALvoice *voice = NULL; - ALsizei i; - - /* Check that there is a queue containing at least one valid, non zero - * length Buffer. */ - BufferList = ATOMIC_LOAD(&Source->queue); - while(BufferList) - { - ALbuffer *buffer; - if((buffer=BufferList->buffer) != NULL && buffer->SampleLen > 0) - break; - BufferList = BufferList->next; - } - - if(Source->state != AL_PAUSED) - { - Source->state = AL_PLAYING; - ATOMIC_STORE(&Source->current_buffer, BufferList, almemory_order_relaxed); - ATOMIC_STORE(&Source->position, 0, almemory_order_relaxed); - ATOMIC_STORE(&Source->position_fraction, 0); - discontinuity = AL_TRUE; - } - else - { - Source->state = AL_PLAYING; - discontinuity = AL_FALSE; - } - - // Check if an Offset has been set - if(Source->OffsetType != AL_NONE) - { - ApplyOffset(Source); - /* discontinuity = AL_TRUE;??? */ - } - - /* If there's nothing to play, or device is disconnected, go right to - * stopped */ - if(!BufferList || !device->Connected) - goto do_stop; - - /* Make sure this source isn't already active, while looking for an - * unused active source slot to put it in. */ - for(i = 0;i < Context->VoiceCount;i++) - { - ALsource *old = Source; - if(COMPARE_EXCHANGE(&Context->Voices[i].Source, &old, NULL)) - { - if(voice == NULL) - { - voice = &Context->Voices[i]; - voice->Source = Source; - } - break; - } - old = NULL; - if(voice == NULL && COMPARE_EXCHANGE(&Context->Voices[i].Source, &old, Source)) - voice = &Context->Voices[i]; - } - if(voice == NULL) - { - voice = &Context->Voices[Context->VoiceCount++]; - voice->Source = Source; - } - - if(discontinuity) - { - /* Clear previous samples if playback is discontinuous. */ - memset(voice->PrevSamples, 0, sizeof(voice->PrevSamples)); - - /* Clear the stepping value so the mixer knows not to mix this - * until the update gets applied. - */ - voice->Step = 0; - } - - voice->Moving = AL_FALSE; - for(i = 0;i < MAX_INPUT_CHANNELS;i++) - { - ALsizei j; - for(j = 0;j < HRTF_HISTORY_LENGTH;j++) - voice->Chan[i].Direct.Hrtf.State.History[j] = 0.0f; - for(j = 0;j < HRIR_LENGTH;j++) - { - voice->Chan[i].Direct.Hrtf.State.Values[j][0] = 0.0f; - voice->Chan[i].Direct.Hrtf.State.Values[j][1] = 0.0f; - } - } - - UpdateSourceProps(Source, device->NumAuxSends); - } - else if(state == AL_PAUSED) - { - if(Source->state == AL_PLAYING) - Source->state = AL_PAUSED; - } - else if(state == AL_STOPPED) - { - do_stop: - if(Source->state != AL_INITIAL) - { - Source->state = AL_STOPPED; - ATOMIC_STORE(&Source->current_buffer, NULL); - } - Source->OffsetType = AL_NONE; - Source->Offset = 0.0; - } - else if(state == AL_INITIAL) - { - if(Source->state != AL_INITIAL) - { - Source->state = AL_INITIAL; - ATOMIC_STORE(&Source->current_buffer, ATOMIC_LOAD(&Source->queue), - almemory_order_relaxed); - ATOMIC_STORE(&Source->position, 0, almemory_order_relaxed); - ATOMIC_STORE(&Source->position_fraction, 0); - } - Source->OffsetType = AL_NONE; - Source->Offset = 0.0; - } - WriteUnlock(&Source->queue_lock); -} - /* GetSourceSampleOffset * * Gets the current read offset for the given Source, in 32.32 fixed-point * samples. The offset is relative to the start of the queue (not the start of * the current buffer). */ -static ALint64 GetSourceSampleOffset(ALsource *Source, ALCdevice *device, ALuint64 *clocktime) +static ALint64 GetSourceSampleOffset(ALsource *Source, ALCcontext *context, ALuint64 *clocktime) { - const ALbufferlistitem *BufferList; + ALCdevice *device = context->Device; const ALbufferlistitem *Current; ALuint64 readPos; ALuint refcount; - - ReadLock(&Source->queue_lock); - if(Source->state != AL_PLAYING && Source->state != AL_PAUSED) - { - ReadUnlock(&Source->queue_lock); - do { - while(((refcount=ReadRef(&device->MixCount))&1)) - althrd_yield(); - *clocktime = GetDeviceClockTime(device); - } while(refcount != ReadRef(&device->MixCount)); - return 0; - } + ALvoice *voice; do { - while(((refcount=ReadRef(&device->MixCount))&1)) + Current = NULL; + readPos = 0; + while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1)) althrd_yield(); *clocktime = GetDeviceClockTime(device); - BufferList = ATOMIC_LOAD(&Source->queue, almemory_order_relaxed); - Current = ATOMIC_LOAD(&Source->current_buffer, almemory_order_relaxed); + voice = GetSourceVoice(Source, context); + if(voice) + { + Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed); - readPos = (ALuint64)ATOMIC_LOAD(&Source->position, almemory_order_relaxed) << 32; - readPos |= (ALuint64)ATOMIC_LOAD(&Source->position_fraction, almemory_order_relaxed) << - (32-FRACTIONBITS); - } while(refcount != ReadRef(&device->MixCount)); - while(BufferList && BufferList != Current) + readPos = (ALuint64)ATOMIC_LOAD(&voice->position, almemory_order_relaxed) << 32; + readPos |= (ALuint64)ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed) << + (32-FRACTIONBITS); + } + ATOMIC_THREAD_FENCE(almemory_order_acquire); + } while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_order_relaxed)); + + if(voice) { - if(BufferList->buffer) - readPos += (ALuint64)BufferList->buffer->SampleLen << 32; - BufferList = BufferList->next; + const ALbufferlistitem *BufferList = Source->queue; + while(BufferList && BufferList != Current) + { + readPos += (ALuint64)BufferList->max_samples << 32; + BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next, + almemory_order_relaxed); + } + readPos = minu64(readPos, U64(0x7fffffffffffffff)); } - ReadUnlock(&Source->queue_lock); - return (ALint64)minu64(readPos, U64(0x7fffffffffffffff)); + return (ALint64)readPos; } /* GetSourceSecOffset @@ -3110,57 +3191,64 @@ static ALint64 GetSourceSampleOffset(ALsource *Source, ALCdevice *device, ALuint * Gets the current read offset for the given Source, in seconds. The offset is * relative to the start of the queue (not the start of the current buffer). */ -static ALdouble GetSourceSecOffset(ALsource *Source, ALCdevice *device, ALuint64 *clocktime) +static ALdouble GetSourceSecOffset(ALsource *Source, ALCcontext *context, ALuint64 *clocktime) { - const ALbufferlistitem *BufferList; + ALCdevice *device = context->Device; const ALbufferlistitem *Current; - const ALbuffer *Buffer = NULL; ALuint64 readPos; ALuint refcount; - - ReadLock(&Source->queue_lock); - if(Source->state != AL_PLAYING && Source->state != AL_PAUSED) - { - ReadUnlock(&Source->queue_lock); - do { - while(((refcount=ReadRef(&device->MixCount))&1)) - althrd_yield(); - *clocktime = GetDeviceClockTime(device); - } while(refcount != ReadRef(&device->MixCount)); - return 0.0; - } + ALdouble offset; + ALvoice *voice; do { - while(((refcount=ReadRef(&device->MixCount))&1)) + Current = NULL; + readPos = 0; + while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1)) althrd_yield(); *clocktime = GetDeviceClockTime(device); - BufferList = ATOMIC_LOAD(&Source->queue, almemory_order_relaxed); - Current = ATOMIC_LOAD(&Source->current_buffer, almemory_order_relaxed); - - readPos = (ALuint64)ATOMIC_LOAD(&Source->position, almemory_order_relaxed)<position_fraction, almemory_order_relaxed); - } while(refcount != ReadRef(&device->MixCount)); - while(BufferList && BufferList != Current) - { - const ALbuffer *buffer = BufferList->buffer; - if(buffer != NULL) + voice = GetSourceVoice(Source, context); + if(voice) { - if(!Buffer) Buffer = buffer; - readPos += (ALuint64)buffer->SampleLen << FRACTIONBITS; + Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed); + + readPos = (ALuint64)ATOMIC_LOAD(&voice->position, almemory_order_relaxed) << + FRACTIONBITS; + readPos |= ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed); } - BufferList = BufferList->next; - } + ATOMIC_THREAD_FENCE(almemory_order_acquire); + } while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_order_relaxed)); - while(BufferList && !Buffer) + offset = 0.0; + if(voice) { - Buffer = BufferList->buffer; - BufferList = BufferList->next; - } - assert(Buffer != NULL); + const ALbufferlistitem *BufferList = Source->queue; + const ALbuffer *BufferFmt = NULL; + while(BufferList && BufferList != Current) + { + ALsizei i = 0; + while(!BufferFmt && i < BufferList->num_buffers) + BufferFmt = BufferList->buffers[i++]; + readPos += (ALuint64)BufferList->max_samples << FRACTIONBITS; + BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next, + almemory_order_relaxed); + } - ReadUnlock(&Source->queue_lock); - return (ALdouble)readPos / (ALdouble)FRACTIONONE / (ALdouble)Buffer->Frequency; + while(BufferList && !BufferFmt) + { + ALsizei i = 0; + while(!BufferFmt && i < BufferList->num_buffers) + BufferFmt = BufferList->buffers[i++]; + BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next, + almemory_order_relaxed); + } + assert(BufferFmt != NULL); + + offset = (ALdouble)readPos / (ALdouble)FRACTIONONE / + (ALdouble)BufferFmt->Frequency; + } + + return offset; } /* GetSourceOffset @@ -3169,99 +3257,104 @@ static ALdouble GetSourceSecOffset(ALsource *Source, ALCdevice *device, ALuint64 * (Bytes, Samples or Seconds). The offset is relative to the start of the * queue (not the start of the current buffer). */ -static ALdouble GetSourceOffset(ALsource *Source, ALenum name, ALCdevice *device) +static ALdouble GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) { - const ALbufferlistitem *BufferList; + ALCdevice *device = context->Device; const ALbufferlistitem *Current; - const ALbuffer *Buffer = NULL; - ALboolean readFin = AL_FALSE; - ALuint readPos, readPosFrac; - ALuint totalBufferLen; - ALdouble offset = 0.0; - ALboolean looping; + ALuint readPos; + ALsizei readPosFrac; ALuint refcount; + ALdouble offset; + ALvoice *voice; - ReadLock(&Source->queue_lock); - if(Source->state != AL_PLAYING && Source->state != AL_PAUSED) - { - ReadUnlock(&Source->queue_lock); - return 0.0; - } - - totalBufferLen = 0; do { - while(((refcount=ReadRef(&device->MixCount))&1)) + Current = NULL; + readPos = readPosFrac = 0; + while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1)) althrd_yield(); - BufferList = ATOMIC_LOAD(&Source->queue, almemory_order_relaxed); - Current = ATOMIC_LOAD(&Source->current_buffer, almemory_order_relaxed); - - readPos = ATOMIC_LOAD(&Source->position, almemory_order_relaxed); - readPosFrac = ATOMIC_LOAD(&Source->position_fraction, almemory_order_relaxed); - - looping = ATOMIC_LOAD(&Source->looping, almemory_order_relaxed); - } while(refcount != ReadRef(&device->MixCount)); - - while(BufferList != NULL) - { - const ALbuffer *buffer; - readFin = readFin || (BufferList == Current); - if((buffer=BufferList->buffer) != NULL) + voice = GetSourceVoice(Source, context); + if(voice) { - if(!Buffer) Buffer = buffer; - totalBufferLen += buffer->SampleLen; - if(!readFin) readPos += buffer->SampleLen; + Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed); + + readPos = ATOMIC_LOAD(&voice->position, almemory_order_relaxed); + readPosFrac = ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed); } - BufferList = BufferList->next; - } - assert(Buffer != NULL); + ATOMIC_THREAD_FENCE(almemory_order_acquire); + } while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_order_relaxed)); - if(looping) - readPos %= totalBufferLen; - else + offset = 0.0; + if(voice) { - /* Wrap back to 0 */ - if(readPos >= totalBufferLen) - readPos = readPosFrac = 0; + const ALbufferlistitem *BufferList = Source->queue; + const ALbuffer *BufferFmt = NULL; + ALboolean readFin = AL_FALSE; + ALuint totalBufferLen = 0; + + while(BufferList != NULL) + { + ALsizei i = 0; + while(!BufferFmt && i < BufferList->num_buffers) + BufferFmt = BufferList->buffers[i++]; + + readFin |= (BufferList == Current); + totalBufferLen += BufferList->max_samples; + if(!readFin) readPos += BufferList->max_samples; + + BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next, + almemory_order_relaxed); + } + assert(BufferFmt != NULL); + + if(Source->Looping) + readPos %= totalBufferLen; + else + { + /* Wrap back to 0 */ + if(readPos >= totalBufferLen) + readPos = readPosFrac = 0; + } + + offset = 0.0; + switch(name) + { + case AL_SEC_OFFSET: + offset = (readPos + (ALdouble)readPosFrac/FRACTIONONE) / BufferFmt->Frequency; + break; + + case AL_SAMPLE_OFFSET: + offset = readPos + (ALdouble)readPosFrac/FRACTIONONE; + break; + + case AL_BYTE_OFFSET: + if(BufferFmt->OriginalType == UserFmtIMA4) + { + ALsizei align = (BufferFmt->OriginalAlign-1)/2 + 4; + ALuint BlockSize = align * ChannelsFromFmt(BufferFmt->FmtChannels); + ALuint FrameBlockSize = BufferFmt->OriginalAlign; + + /* Round down to nearest ADPCM block */ + offset = (ALdouble)(readPos / FrameBlockSize * BlockSize); + } + else if(BufferFmt->OriginalType == UserFmtMSADPCM) + { + ALsizei align = (BufferFmt->OriginalAlign-2)/2 + 7; + ALuint BlockSize = align * ChannelsFromFmt(BufferFmt->FmtChannels); + ALuint FrameBlockSize = BufferFmt->OriginalAlign; + + /* Round down to nearest ADPCM block */ + offset = (ALdouble)(readPos / FrameBlockSize * BlockSize); + } + else + { + ALuint FrameSize = FrameSizeFromFmt(BufferFmt->FmtChannels, + BufferFmt->FmtType); + offset = (ALdouble)(readPos * FrameSize); + } + break; + } } - switch(name) - { - case AL_SEC_OFFSET: - offset = (readPos + (ALdouble)readPosFrac/FRACTIONONE)/Buffer->Frequency; - break; - - case AL_SAMPLE_OFFSET: - offset = readPos + (ALdouble)readPosFrac/FRACTIONONE; - break; - - case AL_BYTE_OFFSET: - if(Buffer->OriginalType == UserFmtIMA4) - { - ALsizei align = (Buffer->OriginalAlign-1)/2 + 4; - ALuint BlockSize = align * ChannelsFromFmt(Buffer->FmtChannels); - ALuint FrameBlockSize = Buffer->OriginalAlign; - - /* Round down to nearest ADPCM block */ - offset = (ALdouble)(readPos / FrameBlockSize * BlockSize); - } - else if(Buffer->OriginalType == UserFmtMSADPCM) - { - ALsizei align = (Buffer->OriginalAlign-2)/2 + 7; - ALuint BlockSize = align * ChannelsFromFmt(Buffer->FmtChannels); - ALuint FrameBlockSize = Buffer->OriginalAlign; - - /* Round down to nearest ADPCM block */ - offset = (ALdouble)(readPos / FrameBlockSize * BlockSize); - } - else - { - ALuint FrameSize = FrameSizeFromUserFmt(Buffer->OriginalChannels, Buffer->OriginalType); - offset = (ALdouble)(readPos * FrameSize); - } - break; - } - - ReadUnlock(&Source->queue_lock); return offset; } @@ -3271,37 +3364,32 @@ static ALdouble GetSourceOffset(ALsource *Source, ALenum name, ALCdevice *device * Apply the stored playback offset to the Source. This function will update * the number of buffers "played" given the stored offset. */ -ALboolean ApplyOffset(ALsource *Source) +static ALboolean ApplyOffset(ALsource *Source, ALvoice *voice) { ALbufferlistitem *BufferList; - const ALbuffer *Buffer; - ALuint bufferLen, totalBufferLen; - ALuint offset=0, frac=0; + ALuint totalBufferLen; + ALuint offset = 0; + ALsizei frac = 0; /* Get sample frame offset */ if(!GetSampleOffset(Source, &offset, &frac)) return AL_FALSE; totalBufferLen = 0; - BufferList = ATOMIC_LOAD(&Source->queue); + BufferList = Source->queue; while(BufferList && totalBufferLen <= offset) { - Buffer = BufferList->buffer; - bufferLen = Buffer ? Buffer->SampleLen : 0; - - if(bufferLen > offset-totalBufferLen) + if((ALuint)BufferList->max_samples > offset-totalBufferLen) { /* Offset is in this buffer */ - ATOMIC_STORE(&Source->current_buffer, BufferList, almemory_order_relaxed); - - ATOMIC_STORE(&Source->position, offset - totalBufferLen, almemory_order_relaxed); - ATOMIC_STORE(&Source->position_fraction, frac); + ATOMIC_STORE(&voice->position, offset - totalBufferLen, almemory_order_relaxed); + ATOMIC_STORE(&voice->position_fraction, frac, almemory_order_relaxed); + ATOMIC_STORE(&voice->current_buffer, BufferList, almemory_order_release); return AL_TRUE; } + totalBufferLen += BufferList->max_samples; - totalBufferLen += bufferLen; - - BufferList = BufferList->next; + BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed); } /* Offset is out of range of the queue */ @@ -3315,24 +3403,24 @@ ALboolean ApplyOffset(ALsource *Source) * or Second offset supplied by the application). This takes into account the * fact that the buffer format may have been modifed since. */ -static ALboolean GetSampleOffset(ALsource *Source, ALuint *offset, ALuint *frac) +static ALboolean GetSampleOffset(ALsource *Source, ALuint *offset, ALsizei *frac) { - const ALbuffer *Buffer = NULL; + const ALbuffer *BufferFmt = NULL; const ALbufferlistitem *BufferList; ALdouble dbloff, dblfrac; /* Find the first valid Buffer in the Queue */ - BufferList = ATOMIC_LOAD(&Source->queue); + BufferList = Source->queue; while(BufferList) { - if(BufferList->buffer) - { - Buffer = BufferList->buffer; - break; - } - BufferList = BufferList->next; + ALsizei i; + for(i = 0;i < BufferList->num_buffers && !BufferFmt;i++) + BufferFmt = BufferList->buffers[i]; + if(BufferFmt) break; + BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next, + almemory_order_relaxed); } - if(!Buffer) + if(!BufferFmt) { Source->OffsetType = AL_NONE; Source->Offset = 0.0; @@ -3344,33 +3432,33 @@ static ALboolean GetSampleOffset(ALsource *Source, ALuint *offset, ALuint *frac) case AL_BYTE_OFFSET: /* Determine the ByteOffset (and ensure it is block aligned) */ *offset = (ALuint)Source->Offset; - if(Buffer->OriginalType == UserFmtIMA4) + if(BufferFmt->OriginalType == UserFmtIMA4) { - ALsizei align = (Buffer->OriginalAlign-1)/2 + 4; - *offset /= align * ChannelsFromUserFmt(Buffer->OriginalChannels); - *offset *= Buffer->OriginalAlign; + ALsizei align = (BufferFmt->OriginalAlign-1)/2 + 4; + *offset /= align * ChannelsFromFmt(BufferFmt->FmtChannels); + *offset *= BufferFmt->OriginalAlign; } - else if(Buffer->OriginalType == UserFmtMSADPCM) + else if(BufferFmt->OriginalType == UserFmtMSADPCM) { - ALsizei align = (Buffer->OriginalAlign-2)/2 + 7; - *offset /= align * ChannelsFromUserFmt(Buffer->OriginalChannels); - *offset *= Buffer->OriginalAlign; + ALsizei align = (BufferFmt->OriginalAlign-2)/2 + 7; + *offset /= align * ChannelsFromFmt(BufferFmt->FmtChannels); + *offset *= BufferFmt->OriginalAlign; } else - *offset /= FrameSizeFromUserFmt(Buffer->OriginalChannels, Buffer->OriginalType); + *offset /= FrameSizeFromFmt(BufferFmt->FmtChannels, BufferFmt->FmtType); *frac = 0; break; case AL_SAMPLE_OFFSET: dblfrac = modf(Source->Offset, &dbloff); *offset = (ALuint)mind(dbloff, UINT_MAX); - *frac = (ALuint)mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0); + *frac = (ALsizei)mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0); break; case AL_SEC_OFFSET: - dblfrac = modf(Source->Offset*Buffer->Frequency, &dbloff); + dblfrac = modf(Source->Offset*BufferFmt->Frequency, &dbloff); *offset = (ALuint)mind(dbloff, UINT_MAX); - *frac = (ALuint)mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0); + *frac = (ALsizei)mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0); break; } Source->OffsetType = AL_NONE; @@ -3380,22 +3468,124 @@ static ALboolean GetSampleOffset(ALsource *Source, ALuint *offset, ALuint *frac) } +static ALsource *AllocSource(ALCcontext *context) +{ + ALCdevice *device = context->Device; + SourceSubList *sublist, *subend; + ALsource *source = NULL; + ALsizei lidx = 0; + ALsizei slidx; + + almtx_lock(&context->SourceLock); + if(context->NumSources >= device->SourcesMax) + { + almtx_unlock(&context->SourceLock); + alSetError(context, AL_OUT_OF_MEMORY, "Exceeding %u source limit", device->SourcesMax); + return NULL; + } + sublist = VECTOR_BEGIN(context->SourceList); + subend = VECTOR_END(context->SourceList); + for(;sublist != subend;++sublist) + { + if(sublist->FreeMask) + { + slidx = CTZ64(sublist->FreeMask); + source = sublist->Sources + slidx; + break; + } + ++lidx; + } + if(UNLIKELY(!source)) + { + const SourceSubList empty_sublist = { 0, NULL }; + /* Don't allocate so many list entries that the 32-bit ID could + * overflow... + */ + if(UNLIKELY(VECTOR_SIZE(context->SourceList) >= 1<<25)) + { + almtx_unlock(&device->BufferLock); + alSetError(context, AL_OUT_OF_MEMORY, "Too many sources allocated"); + return NULL; + } + lidx = (ALsizei)VECTOR_SIZE(context->SourceList); + VECTOR_PUSH_BACK(context->SourceList, empty_sublist); + sublist = &VECTOR_BACK(context->SourceList); + sublist->FreeMask = ~U64(0); + sublist->Sources = al_calloc(16, sizeof(ALsource)*64); + if(UNLIKELY(!sublist->Sources)) + { + VECTOR_POP_BACK(context->SourceList); + almtx_unlock(&context->SourceLock); + alSetError(context, AL_OUT_OF_MEMORY, "Failed to allocate source batch"); + return NULL; + } + + slidx = 0; + source = sublist->Sources + slidx; + } + + memset(source, 0, sizeof(*source)); + InitSourceParams(source, device->NumAuxSends); + + /* Add 1 to avoid source ID 0. */ + source->id = ((lidx<<6) | slidx) + 1; + + context->NumSources++; + sublist->FreeMask &= ~(U64(1)<SourceLock); + + return source; +} + +static void FreeSource(ALCcontext *context, ALsource *source) +{ + ALCdevice *device = context->Device; + ALuint id = source->id - 1; + ALsizei lidx = id >> 6; + ALsizei slidx = id & 0x3f; + ALvoice *voice; + + ALCdevice_Lock(device); + if((voice=GetSourceVoice(source, context)) != NULL) + { + ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed); + ATOMIC_STORE(&voice->Playing, false, almemory_order_release); + } + ALCdevice_Unlock(device); + + DeinitSource(source, device->NumAuxSends); + memset(source, 0, sizeof(*source)); + + VECTOR_ELEM(context->SourceList, lidx).FreeMask |= U64(1) << slidx; + context->NumSources--; +} + /* ReleaseALSources * * Destroys all sources in the source map. */ -ALvoid ReleaseALSources(ALCcontext *Context) +ALvoid ReleaseALSources(ALCcontext *context) { - ALsizei pos; - for(pos = 0;pos < Context->SourceMap.size;pos++) + ALCdevice *device = context->Device; + SourceSubList *sublist = VECTOR_BEGIN(context->SourceList); + SourceSubList *subend = VECTOR_END(context->SourceList); + size_t leftover = 0; + for(;sublist != subend;++sublist) { - ALsource *temp = Context->SourceMap.values[pos]; - Context->SourceMap.values[pos] = NULL; + ALuint64 usemask = ~sublist->FreeMask; + while(usemask) + { + ALsizei idx = CTZ64(usemask); + ALsource *source = sublist->Sources + idx; - DeinitSource(temp); + DeinitSource(source, device->NumAuxSends); + memset(source, 0, sizeof(*source)); + ++leftover; - FreeThunkEntry(temp->id); - memset(temp, 0, sizeof(*temp)); - al_free(temp); + usemask &= ~(U64(1) << idx); + } + sublist->FreeMask = ~usemask; } + if(leftover > 0) + WARN("(%p) Deleted "SZFMT" Source%s\n", device, leftover, (leftover==1)?"":"s"); } diff --git a/Engine/lib/openal-soft/OpenAL32/alState.c b/Engine/lib/openal-soft/OpenAL32/alState.c index 3d8e6c404..ce93e1438 100644 --- a/Engine/lib/openal-soft/OpenAL32/alState.c +++ b/Engine/lib/openal-soft/OpenAL32/alState.c @@ -20,6 +20,8 @@ #include "config.h" +#include "version.h" + #include #include "alMain.h" #include "AL/alc.h" @@ -45,6 +47,31 @@ static const ALchar alErrInvalidValue[] = "Invalid Value"; static const ALchar alErrInvalidOp[] = "Invalid Operation"; static const ALchar alErrOutOfMemory[] = "Out of Memory"; +/* Resampler strings */ +static const ALchar alPointResampler[] = "Nearest"; +static const ALchar alLinearResampler[] = "Linear"; +static const ALchar alCubicResampler[] = "Cubic"; +static const ALchar alBSinc12Resampler[] = "11th order Sinc"; +static const ALchar alBSinc24Resampler[] = "23rd order Sinc"; + +/* WARNING: Non-standard export! Not part of any extension, or exposed in the + * alcFunctions list. + */ +AL_API const ALchar* AL_APIENTRY alsoft_get_version(void) +{ + const char *spoof = getenv("ALSOFT_SPOOF_VERSION"); + if(spoof && spoof[0] != '\0') return spoof; + return ALSOFT_VERSION; +} + +#define DO_UPDATEPROPS() do { \ + if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) \ + UpdateContextProps(context); \ + else \ + ATOMIC_FLAG_CLEAR(&context->PropsClean, almemory_order_release); \ +} while(0) + + AL_API ALvoid AL_APIENTRY alEnable(ALenum capability) { ALCcontext *context; @@ -52,21 +79,19 @@ AL_API ALvoid AL_APIENTRY alEnable(ALenum capability) context = GetContextRef(); if(!context) return; - WriteLock(&context->PropLock); + almtx_lock(&context->PropLock); switch(capability) { case AL_SOURCE_DISTANCE_MODEL: context->SourceDistanceModel = AL_TRUE; + DO_UPDATEPROPS(); break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_VALUE, "Invalid enable property 0x%04x", capability); } - if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) - UpdateListenerProps(context); + almtx_unlock(&context->PropLock); -done: - WriteUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -77,21 +102,19 @@ AL_API ALvoid AL_APIENTRY alDisable(ALenum capability) context = GetContextRef(); if(!context) return; - WriteLock(&context->PropLock); + almtx_lock(&context->PropLock); switch(capability) { case AL_SOURCE_DISTANCE_MODEL: context->SourceDistanceModel = AL_FALSE; + DO_UPDATEPROPS(); break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_VALUE, "Invalid disable property 0x%04x", capability); } - if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) - UpdateListenerProps(context); + almtx_unlock(&context->PropLock); -done: - WriteUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -103,6 +126,7 @@ AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability) context = GetContextRef(); if(!context) return AL_FALSE; + almtx_lock(&context->PropLock); switch(capability) { case AL_SOURCE_DISTANCE_MODEL: @@ -110,12 +134,11 @@ AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability) break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_VALUE, "Invalid is enabled property 0x%04x", capability); } + almtx_unlock(&context->PropLock); -done: ALCcontext_DecRef(context); - return value; } @@ -127,6 +150,7 @@ AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum pname) context = GetContextRef(); if(!context) return AL_FALSE; + almtx_lock(&context->PropLock); switch(pname) { case AL_DOPPLER_FACTOR: @@ -150,7 +174,7 @@ AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum pname) break; case AL_DEFERRED_UPDATES_SOFT: - if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire) == DeferAll) + if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) value = AL_TRUE; break; @@ -159,13 +183,21 @@ AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum pname) value = AL_TRUE; break; + case AL_NUM_RESAMPLERS_SOFT: + /* Always non-0. */ + value = AL_TRUE; + break; + + case AL_DEFAULT_RESAMPLER_SOFT: + value = ResamplerDefault ? AL_TRUE : AL_FALSE; + break; + default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_VALUE, "Invalid boolean property 0x%04x", pname); } + almtx_unlock(&context->PropLock); -done: ALCcontext_DecRef(context); - return value; } @@ -177,6 +209,7 @@ AL_API ALdouble AL_APIENTRY alGetDouble(ALenum pname) context = GetContextRef(); if(!context) return 0.0; + almtx_lock(&context->PropLock); switch(pname) { case AL_DOPPLER_FACTOR: @@ -196,7 +229,7 @@ AL_API ALdouble AL_APIENTRY alGetDouble(ALenum pname) break; case AL_DEFERRED_UPDATES_SOFT: - if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire) == DeferAll) + if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) value = (ALdouble)AL_TRUE; break; @@ -204,13 +237,20 @@ AL_API ALdouble AL_APIENTRY alGetDouble(ALenum pname) value = (ALdouble)GAIN_MIX_MAX/context->GainBoost; break; + case AL_NUM_RESAMPLERS_SOFT: + value = (ALdouble)(ResamplerMax + 1); + break; + + case AL_DEFAULT_RESAMPLER_SOFT: + value = (ALdouble)ResamplerDefault; + break; + default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_VALUE, "Invalid double property 0x%04x", pname); } + almtx_unlock(&context->PropLock); -done: ALCcontext_DecRef(context); - return value; } @@ -222,6 +262,7 @@ AL_API ALfloat AL_APIENTRY alGetFloat(ALenum pname) context = GetContextRef(); if(!context) return 0.0f; + almtx_lock(&context->PropLock); switch(pname) { case AL_DOPPLER_FACTOR: @@ -241,7 +282,7 @@ AL_API ALfloat AL_APIENTRY alGetFloat(ALenum pname) break; case AL_DEFERRED_UPDATES_SOFT: - if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire) == DeferAll) + if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) value = (ALfloat)AL_TRUE; break; @@ -249,13 +290,20 @@ AL_API ALfloat AL_APIENTRY alGetFloat(ALenum pname) value = GAIN_MIX_MAX/context->GainBoost; break; + case AL_NUM_RESAMPLERS_SOFT: + value = (ALfloat)(ResamplerMax + 1); + break; + + case AL_DEFAULT_RESAMPLER_SOFT: + value = (ALfloat)ResamplerDefault; + break; + default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_VALUE, "Invalid float property 0x%04x", pname); } + almtx_unlock(&context->PropLock); -done: ALCcontext_DecRef(context); - return value; } @@ -267,6 +315,7 @@ AL_API ALint AL_APIENTRY alGetInteger(ALenum pname) context = GetContextRef(); if(!context) return 0; + almtx_lock(&context->PropLock); switch(pname) { case AL_DOPPLER_FACTOR: @@ -286,7 +335,7 @@ AL_API ALint AL_APIENTRY alGetInteger(ALenum pname) break; case AL_DEFERRED_UPDATES_SOFT: - if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire) == DeferAll) + if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) value = (ALint)AL_TRUE; break; @@ -294,13 +343,20 @@ AL_API ALint AL_APIENTRY alGetInteger(ALenum pname) value = (ALint)(GAIN_MIX_MAX/context->GainBoost); break; + case AL_NUM_RESAMPLERS_SOFT: + value = ResamplerMax + 1; + break; + + case AL_DEFAULT_RESAMPLER_SOFT: + value = ResamplerDefault; + break; + default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_VALUE, "Invalid integer property 0x%04x", pname); } + almtx_unlock(&context->PropLock); -done: ALCcontext_DecRef(context); - return value; } @@ -312,6 +368,7 @@ AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname) context = GetContextRef(); if(!context) return 0; + almtx_lock(&context->PropLock); switch(pname) { case AL_DOPPLER_FACTOR: @@ -331,7 +388,7 @@ AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname) break; case AL_DEFERRED_UPDATES_SOFT: - if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire) == DeferAll) + if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) value = (ALint64SOFT)AL_TRUE; break; @@ -339,13 +396,48 @@ AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname) value = (ALint64SOFT)(GAIN_MIX_MAX/context->GainBoost); break; + case AL_NUM_RESAMPLERS_SOFT: + value = (ALint64SOFT)(ResamplerMax + 1); + break; + + case AL_DEFAULT_RESAMPLER_SOFT: + value = (ALint64SOFT)ResamplerDefault; + break; + default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_VALUE, "Invalid integer64 property 0x%04x", pname); } + almtx_unlock(&context->PropLock); -done: ALCcontext_DecRef(context); + return value; +} +AL_API void* AL_APIENTRY alGetPointerSOFT(ALenum pname) +{ + ALCcontext *context; + void *value = NULL; + + context = GetContextRef(); + if(!context) return NULL; + + almtx_lock(&context->PropLock); + switch(pname) + { + case AL_EVENT_CALLBACK_FUNCTION_SOFT: + value = context->EventCb; + break; + + case AL_EVENT_CALLBACK_USER_PARAM_SOFT: + value = context->EventParam; + break; + + default: + alSetError(context, AL_INVALID_VALUE, "Invalid pointer property 0x%04x", pname); + } + almtx_unlock(&context->PropLock); + + ALCcontext_DecRef(context); return value; } @@ -363,6 +455,8 @@ AL_API ALvoid AL_APIENTRY alGetBooleanv(ALenum pname, ALboolean *values) case AL_SPEED_OF_SOUND: case AL_DEFERRED_UPDATES_SOFT: case AL_GAIN_LIMIT_SOFT: + case AL_NUM_RESAMPLERS_SOFT: + case AL_DEFAULT_RESAMPLER_SOFT: values[0] = alGetBoolean(pname); return; } @@ -371,15 +465,14 @@ AL_API ALvoid AL_APIENTRY alGetBooleanv(ALenum pname, ALboolean *values) context = GetContextRef(); if(!context) return; - if(!(values)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + if(!values) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); switch(pname) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_VALUE, "Invalid boolean-vector property 0x%04x", pname); } -done: ALCcontext_DecRef(context); } @@ -397,6 +490,8 @@ AL_API ALvoid AL_APIENTRY alGetDoublev(ALenum pname, ALdouble *values) case AL_SPEED_OF_SOUND: case AL_DEFERRED_UPDATES_SOFT: case AL_GAIN_LIMIT_SOFT: + case AL_NUM_RESAMPLERS_SOFT: + case AL_DEFAULT_RESAMPLER_SOFT: values[0] = alGetDouble(pname); return; } @@ -405,15 +500,14 @@ AL_API ALvoid AL_APIENTRY alGetDoublev(ALenum pname, ALdouble *values) context = GetContextRef(); if(!context) return; - if(!(values)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + if(!values) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); switch(pname) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_VALUE, "Invalid double-vector property 0x%04x", pname); } -done: ALCcontext_DecRef(context); } @@ -431,6 +525,8 @@ AL_API ALvoid AL_APIENTRY alGetFloatv(ALenum pname, ALfloat *values) case AL_SPEED_OF_SOUND: case AL_DEFERRED_UPDATES_SOFT: case AL_GAIN_LIMIT_SOFT: + case AL_NUM_RESAMPLERS_SOFT: + case AL_DEFAULT_RESAMPLER_SOFT: values[0] = alGetFloat(pname); return; } @@ -439,15 +535,14 @@ AL_API ALvoid AL_APIENTRY alGetFloatv(ALenum pname, ALfloat *values) context = GetContextRef(); if(!context) return; - if(!(values)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + if(!values) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); switch(pname) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_VALUE, "Invalid float-vector property 0x%04x", pname); } -done: ALCcontext_DecRef(context); } @@ -465,6 +560,8 @@ AL_API ALvoid AL_APIENTRY alGetIntegerv(ALenum pname, ALint *values) case AL_SPEED_OF_SOUND: case AL_DEFERRED_UPDATES_SOFT: case AL_GAIN_LIMIT_SOFT: + case AL_NUM_RESAMPLERS_SOFT: + case AL_DEFAULT_RESAMPLER_SOFT: values[0] = alGetInteger(pname); return; } @@ -473,13 +570,14 @@ AL_API ALvoid AL_APIENTRY alGetIntegerv(ALenum pname, ALint *values) context = GetContextRef(); if(!context) return; + if(!values) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); switch(pname) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_VALUE, "Invalid integer-vector property 0x%04x", pname); } -done: ALCcontext_DecRef(context); } @@ -497,6 +595,8 @@ AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values) case AL_SPEED_OF_SOUND: case AL_DEFERRED_UPDATES_SOFT: case AL_GAIN_LIMIT_SOFT: + case AL_NUM_RESAMPLERS_SOFT: + case AL_DEFAULT_RESAMPLER_SOFT: values[0] = alGetInteger64SOFT(pname); return; } @@ -505,13 +605,43 @@ AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values) context = GetContextRef(); if(!context) return; + if(!values) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); switch(pname) { default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_VALUE, "Invalid integer64-vector property 0x%04x", pname); + } + + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, void **values) +{ + ALCcontext *context; + + if(values) + { + switch(pname) + { + case AL_EVENT_CALLBACK_FUNCTION_SOFT: + case AL_EVENT_CALLBACK_USER_PARAM_SOFT: + values[0] = alGetPointerSOFT(pname); + return; + } + } + + context = GetContextRef(); + if(!context) return; + + if(!values) + alSetError(context, AL_INVALID_VALUE, "NULL pointer"); + switch(pname) + { + default: + alSetError(context, AL_INVALID_VALUE, "Invalid pointer-vector property 0x%04x", pname); } -done: ALCcontext_DecRef(context); } @@ -566,12 +696,10 @@ AL_API const ALchar* AL_APIENTRY alGetString(ALenum pname) break; default: - SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + alSetError(context, AL_INVALID_VALUE, "Invalid string property 0x%04x", pname); } -done: ALCcontext_DecRef(context); - return value; } @@ -583,15 +711,15 @@ AL_API ALvoid AL_APIENTRY alDopplerFactor(ALfloat value) if(!context) return; if(!(value >= 0.0f && isfinite(value))) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + alSetError(context, AL_INVALID_VALUE, "Doppler factor %f out of range", value); + else + { + almtx_lock(&context->PropLock); + context->DopplerFactor = value; + DO_UPDATEPROPS(); + almtx_unlock(&context->PropLock); + } - WriteLock(&context->PropLock); - context->DopplerFactor = value; - if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) - UpdateListenerProps(context); - WriteUnlock(&context->PropLock); - -done: ALCcontext_DecRef(context); } @@ -602,16 +730,30 @@ AL_API ALvoid AL_APIENTRY alDopplerVelocity(ALfloat value) context = GetContextRef(); if(!context) return; + if((ATOMIC_LOAD(&context->EnabledEvts, almemory_order_relaxed)&EventType_Deprecated)) + { + static const ALCchar msg[] = + "alDopplerVelocity is deprecated in AL1.1, use alSpeedOfSound"; + const ALsizei msglen = (ALsizei)strlen(msg); + ALbitfieldSOFT enabledevts; + almtx_lock(&context->EventCbLock); + enabledevts = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_relaxed); + if((enabledevts&EventType_Deprecated) && context->EventCb) + (*context->EventCb)(AL_EVENT_TYPE_DEPRECATED_SOFT, 0, 0, msglen, msg, + context->EventParam); + almtx_unlock(&context->EventCbLock); + } + if(!(value >= 0.0f && isfinite(value))) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + alSetError(context, AL_INVALID_VALUE, "Doppler velocity %f out of range", value); + else + { + almtx_lock(&context->PropLock); + context->DopplerVelocity = value; + DO_UPDATEPROPS(); + almtx_unlock(&context->PropLock); + } - WriteLock(&context->PropLock); - context->DopplerVelocity = value; - if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) - UpdateListenerProps(context); - WriteUnlock(&context->PropLock); - -done: ALCcontext_DecRef(context); } @@ -623,15 +765,15 @@ AL_API ALvoid AL_APIENTRY alSpeedOfSound(ALfloat value) if(!context) return; if(!(value > 0.0f && isfinite(value))) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + alSetError(context, AL_INVALID_VALUE, "Speed of sound %f out of range", value); + else + { + almtx_lock(&context->PropLock); + context->SpeedOfSound = value; + DO_UPDATEPROPS(); + almtx_unlock(&context->PropLock); + } - WriteLock(&context->PropLock); - context->SpeedOfSound = value; - if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) - UpdateListenerProps(context); - WriteUnlock(&context->PropLock); - -done: ALCcontext_DecRef(context); } @@ -646,18 +788,16 @@ AL_API ALvoid AL_APIENTRY alDistanceModel(ALenum value) value == AL_LINEAR_DISTANCE || value == AL_LINEAR_DISTANCE_CLAMPED || value == AL_EXPONENT_DISTANCE || value == AL_EXPONENT_DISTANCE_CLAMPED || value == AL_NONE)) - SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - - WriteLock(&context->PropLock); - context->DistanceModel = value; - if(!context->SourceDistanceModel) + alSetError(context, AL_INVALID_VALUE, "Distance model 0x%04x out of range", value); + else { - if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) - UpdateListenerProps(context); + almtx_lock(&context->PropLock); + context->DistanceModel = value; + if(!context->SourceDistanceModel) + DO_UPDATEPROPS(); + almtx_unlock(&context->PropLock); } - WriteUnlock(&context->PropLock); -done: ALCcontext_DecRef(context); } @@ -669,7 +809,7 @@ AL_API ALvoid AL_APIENTRY alDeferUpdatesSOFT(void) context = GetContextRef(); if(!context) return; - ALCcontext_DeferUpdates(context, DeferAll); + ALCcontext_DeferUpdates(context); ALCcontext_DecRef(context); } @@ -685,3 +825,76 @@ AL_API ALvoid AL_APIENTRY alProcessUpdatesSOFT(void) ALCcontext_DecRef(context); } + + +AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index) +{ + const char *ResamplerNames[] = { + alPointResampler, alLinearResampler, + alCubicResampler, alBSinc12Resampler, + alBSinc24Resampler, + }; + const ALchar *value = NULL; + ALCcontext *context; + + static_assert(COUNTOF(ResamplerNames) == ResamplerMax+1, "Incorrect ResamplerNames list"); + + context = GetContextRef(); + if(!context) return NULL; + + switch(pname) + { + case AL_RESAMPLER_NAME_SOFT: + if(index < 0 || (size_t)index >= COUNTOF(ResamplerNames)) + SETERR_GOTO(context, AL_INVALID_VALUE, done, "Resampler name index %d out of range", + index); + value = ResamplerNames[index]; + break; + + default: + alSetError(context, AL_INVALID_VALUE, "Invalid string indexed property"); + } + +done: + ALCcontext_DecRef(context); + return value; +} + + +void UpdateContextProps(ALCcontext *context) +{ + struct ALcontextProps *props; + + /* Get an unused proprty container, or allocate a new one as needed. */ + props = ATOMIC_LOAD(&context->FreeContextProps, almemory_order_acquire); + if(!props) + props = al_calloc(16, sizeof(*props)); + else + { + struct ALcontextProps *next; + do { + next = ATOMIC_LOAD(&props->next, almemory_order_relaxed); + } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&context->FreeContextProps, &props, next, + almemory_order_seq_cst, almemory_order_acquire) == 0); + } + + /* Copy in current property values. */ + props->MetersPerUnit = context->MetersPerUnit; + + props->DopplerFactor = context->DopplerFactor; + props->DopplerVelocity = context->DopplerVelocity; + props->SpeedOfSound = context->SpeedOfSound; + + props->SourceDistanceModel = context->SourceDistanceModel; + props->DistanceModel = context->DistanceModel; + + /* Set the new container for updating internal parameters. */ + props = ATOMIC_EXCHANGE_PTR(&context->Update, props, almemory_order_acq_rel); + if(props) + { + /* If there was an unused update container, put it back in the + * freelist. + */ + ATOMIC_REPLACE_HEAD(struct ALcontextProps*, &context->FreeContextProps, props); + } +} diff --git a/Engine/lib/openal-soft/OpenAL32/alThunk.c b/Engine/lib/openal-soft/OpenAL32/alThunk.c deleted file mode 100644 index 72fc0dcbe..000000000 --- a/Engine/lib/openal-soft/OpenAL32/alThunk.c +++ /dev/null @@ -1,105 +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 - -#include "alMain.h" -#include "alThunk.h" - -#include "almalloc.h" - - -static ATOMIC(ALenum) *ThunkArray; -static ALuint ThunkArraySize; -static RWLock ThunkLock; - -void ThunkInit(void) -{ - RWLockInit(&ThunkLock); - ThunkArraySize = 1024; - ThunkArray = al_calloc(16, ThunkArraySize * sizeof(*ThunkArray)); -} - -void ThunkExit(void) -{ - al_free(ThunkArray); - ThunkArray = NULL; - ThunkArraySize = 0; -} - -ALenum NewThunkEntry(ALuint *index) -{ - void *NewList; - ALuint i; - - ReadLock(&ThunkLock); - for(i = 0;i < ThunkArraySize;i++) - { - if(ATOMIC_EXCHANGE(ALenum, &ThunkArray[i], AL_TRUE) == AL_FALSE) - { - ReadUnlock(&ThunkLock); - *index = i+1; - return AL_NO_ERROR; - } - } - ReadUnlock(&ThunkLock); - - WriteLock(&ThunkLock); - /* Double-check that there's still no free entries, in case another - * invocation just came through and increased the size of the array. - */ - for(;i < ThunkArraySize;i++) - { - if(ATOMIC_EXCHANGE(ALenum, &ThunkArray[i], AL_TRUE) == AL_FALSE) - { - WriteUnlock(&ThunkLock); - *index = i+1; - return AL_NO_ERROR; - } - } - - NewList = al_calloc(16, ThunkArraySize*2 * sizeof(*ThunkArray)); - if(!NewList) - { - WriteUnlock(&ThunkLock); - ERR("Realloc failed to increase to %u entries!\n", ThunkArraySize*2); - return AL_OUT_OF_MEMORY; - } - memcpy(NewList, ThunkArray, ThunkArraySize*sizeof(*ThunkArray)); - al_free(ThunkArray); - ThunkArray = NewList; - ThunkArraySize *= 2; - - ATOMIC_STORE(&ThunkArray[i], AL_TRUE); - WriteUnlock(&ThunkLock); - - *index = i+1; - return AL_NO_ERROR; -} - -void FreeThunkEntry(ALuint index) -{ - ReadLock(&ThunkLock); - if(index > 0 && index <= ThunkArraySize) - ATOMIC_STORE(&ThunkArray[index-1], AL_FALSE); - ReadUnlock(&ThunkLock); -} diff --git a/Engine/lib/openal-soft/OpenAL32/event.c b/Engine/lib/openal-soft/OpenAL32/event.c new file mode 100644 index 000000000..12636489b --- /dev/null +++ b/Engine/lib/openal-soft/OpenAL32/event.c @@ -0,0 +1,140 @@ + +#include "config.h" + +#include "AL/alc.h" +#include "AL/al.h" +#include "AL/alext.h" +#include "alMain.h" +#include "alError.h" +#include "ringbuffer.h" + + +static int EventThread(void *arg) +{ + ALCcontext *context = arg; + + /* Clear all pending posts on the semaphore. */ + while(alsem_trywait(&context->EventSem) == althrd_success) + { + } + + while(1) + { + ALbitfieldSOFT enabledevts; + AsyncEvent evt; + + if(ll_ringbuffer_read(context->AsyncEvents, (char*)&evt, 1) == 0) + { + alsem_wait(&context->EventSem); + continue; + } + if(!evt.EnumType) + break; + + almtx_lock(&context->EventCbLock); + enabledevts = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire); + if(context->EventCb && (enabledevts&evt.EnumType) == evt.EnumType) + context->EventCb(evt.Type, evt.ObjectId, evt.Param, (ALsizei)strlen(evt.Message), + evt.Message, context->EventParam); + almtx_unlock(&context->EventCbLock); + } + return 0; +} + +AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable) +{ + ALCcontext *context; + ALbitfieldSOFT enabledevts; + ALbitfieldSOFT flags = 0; + bool isrunning; + ALsizei i; + + context = GetContextRef(); + if(!context) return; + + if(count < 0) SETERR_GOTO(context, AL_INVALID_VALUE, done, "Controlling %d events", count); + if(count == 0) goto done; + if(!types) SETERR_GOTO(context, AL_INVALID_VALUE, done, "NULL pointer"); + + for(i = 0;i < count;i++) + { + if(types[i] == AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT) + flags |= EventType_BufferCompleted; + else if(types[i] == AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT) + flags |= EventType_SourceStateChange; + else if(types[i] == AL_EVENT_TYPE_ERROR_SOFT) + flags |= EventType_Error; + else if(types[i] == AL_EVENT_TYPE_PERFORMANCE_SOFT) + flags |= EventType_Performance; + else if(types[i] == AL_EVENT_TYPE_DEPRECATED_SOFT) + flags |= EventType_Deprecated; + else if(types[i] == AL_EVENT_TYPE_DISCONNECTED_SOFT) + flags |= EventType_Disconnected; + else + SETERR_GOTO(context, AL_INVALID_ENUM, done, "Invalid event type 0x%04x", types[i]); + } + + almtx_lock(&context->EventThrdLock); + if(enable) + { + if(!context->AsyncEvents) + context->AsyncEvents = ll_ringbuffer_create(63, sizeof(AsyncEvent), false); + enabledevts = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_relaxed); + isrunning = !!enabledevts; + while(ATOMIC_COMPARE_EXCHANGE_WEAK(&context->EnabledEvts, &enabledevts, enabledevts|flags, + almemory_order_acq_rel, almemory_order_acquire) == 0) + { + /* enabledevts is (re-)filled with the current value on failure, so + * just try again. + */ + } + if(!isrunning && flags) + althrd_create(&context->EventThread, EventThread, context); + } + else + { + enabledevts = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_relaxed); + isrunning = !!enabledevts; + while(ATOMIC_COMPARE_EXCHANGE_WEAK(&context->EnabledEvts, &enabledevts, enabledevts&~flags, + almemory_order_acq_rel, almemory_order_acquire) == 0) + { + } + if(isrunning && !(enabledevts&~flags)) + { + static const AsyncEvent kill_evt = { 0 }; + while(ll_ringbuffer_write(context->AsyncEvents, (const char*)&kill_evt, 1) == 0) + althrd_yield(); + alsem_post(&context->EventSem); + althrd_join(context->EventThread, NULL); + } + else + { + /* Wait to ensure the event handler sees the changed flags before + * returning. + */ + almtx_lock(&context->EventCbLock); + almtx_unlock(&context->EventCbLock); + } + } + almtx_unlock(&context->EventThrdLock); + +done: + ALCcontext_DecRef(context); +} + +AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam) +{ + ALCcontext *context; + + context = GetContextRef(); + if(!context) return; + + almtx_lock(&context->PropLock); + almtx_lock(&context->EventCbLock); + context->EventCb = callback; + context->EventParam = userParam; + almtx_unlock(&context->EventCbLock); + almtx_unlock(&context->PropLock); + + ALCcontext_DecRef(context); +} diff --git a/Engine/lib/openal-soft/OpenAL32/sample_cvt.c b/Engine/lib/openal-soft/OpenAL32/sample_cvt.c index aff3de834..4a85f74af 100644 --- a/Engine/lib/openal-soft/OpenAL32/sample_cvt.c +++ b/Engine/lib/openal-soft/OpenAL32/sample_cvt.c @@ -3,13 +3,6 @@ #include "sample_cvt.h" -#ifdef HAVE_ALLOCA_H -#include -#endif -#ifdef HAVE_MALLOC_H -#include -#endif - #include "AL/al.h" #include "alu.h" #include "alBuffer.h" @@ -61,7 +54,7 @@ static const int MSADPCMAdaptionCoeff[7][2] = { /* A quick'n'dirty lookup table to decode a muLaw-encoded byte sample into a * signed 16-bit sample */ -static const ALshort muLawDecompressionTable[256] = { +const ALshort muLawDecompressionTable[256] = { -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, @@ -96,32 +89,10 @@ static const ALshort muLawDecompressionTable[256] = { 56, 48, 40, 32, 24, 16, 8, 0 }; -/* Values used when encoding a muLaw sample */ -static const int muLawBias = 0x84; -static const int muLawClip = 32635; -static const char muLawCompressTable[256] = { - 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 -}; - /* A quick'n'dirty lookup table to decode an aLaw-encoded byte sample into a * signed 16-bit sample */ -static const ALshort aLawDecompressionTable[256] = { +const ALshort aLawDecompressionTable[256] = { -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, @@ -156,92 +127,13 @@ static const ALshort aLawDecompressionTable[256] = { 944, 912, 1008, 976, 816, 784, 880, 848 }; -/* Values used when encoding an aLaw sample */ -static const int aLawClip = 32635; -static const char aLawCompressTable[128] = { - 1,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 -}; - -typedef ALubyte ALmulaw; -typedef ALubyte ALalaw; -typedef ALubyte ALima4; -typedef ALubyte ALmsadpcm; -typedef struct { - ALbyte b[3]; -} ALbyte3; -static_assert(sizeof(ALbyte3)==sizeof(ALbyte[3]), "ALbyte3 size is not 3"); -typedef struct { - ALubyte b[3]; -} ALubyte3; -static_assert(sizeof(ALubyte3)==sizeof(ALubyte[3]), "ALubyte3 size is not 3"); - -static inline ALshort DecodeMuLaw(ALmulaw val) -{ return muLawDecompressionTable[val]; } - -static ALmulaw EncodeMuLaw(ALshort val) +static void DecodeIMA4Block(ALshort *dst, const ALubyte *src, ALint numchans, ALsizei align) { - ALint mant, exp, sign; - - sign = (val>>8) & 0x80; - if(sign) - { - /* -32768 doesn't properly negate on a short; it results in itself. - * So clamp to -32767 */ - val = maxi(val, -32767); - val = -val; - } - - val = mini(val, muLawClip); - val += muLawBias; - - exp = muLawCompressTable[(val>>7) & 0xff]; - mant = (val >> (exp+3)) & 0x0f; - - return ~(sign | (exp<<4) | mant); -} - -static inline ALshort DecodeALaw(ALalaw val) -{ return aLawDecompressionTable[val]; } - -static ALalaw EncodeALaw(ALshort val) -{ - ALint mant, exp, sign; - - sign = ((~val) >> 8) & 0x80; - if(!sign) - { - val = maxi(val, -32767); - val = -val; - } - val = mini(val, aLawClip); - - if(val >= 256) - { - exp = aLawCompressTable[(val>>8) & 0x7f]; - mant = (val >> (exp+3)) & 0x0f; - } - else - { - exp = 0; - mant = val >> 4; - } - - return ((exp<<4) | mant) ^ (sign^0x55); -} - -static void DecodeIMA4Block(ALshort *dst, const ALima4 *src, ALint numchans, ALsizei align) -{ - ALint sample[MAX_INPUT_CHANNELS], index[MAX_INPUT_CHANNELS]; - ALuint code[MAX_INPUT_CHANNELS]; - ALsizei j,k,c; + ALint sample[MAX_INPUT_CHANNELS] = { 0 }; + ALint index[MAX_INPUT_CHANNELS] = { 0 }; + ALuint code[MAX_INPUT_CHANNELS] = { 0 }; + ALsizei c, i; for(c = 0;c < numchans;c++) { @@ -257,143 +149,77 @@ static void DecodeIMA4Block(ALshort *dst, const ALima4 *src, ALint numchans, ALs dst[c] = sample[c]; } - for(j = 1;j < align;j += 8) + for(i = 1;i < align;i++) { - for(c = 0;c < numchans;c++) - { - code[c] = *(src++); - code[c] |= *(src++) << 8; - code[c] |= *(src++) << 16; - code[c] |= *(src++) << 24; - } - - for(k = 0;k < 8;k++) + if((i&7) == 1) { for(c = 0;c < numchans;c++) { - int nibble = code[c]&0xf; - code[c] >>= 4; - - sample[c] += IMA4Codeword[nibble] * IMAStep_size[index[c]] / 8; - sample[c] = clampi(sample[c], -32768, 32767); - - index[c] += IMA4Index_adjust[nibble]; - index[c] = clampi(index[c], 0, 88); - - dst[(j+k)*numchans + c] = sample[c]; + code[c] = *(src++); + code[c] |= *(src++) << 8; + code[c] |= *(src++) << 16; + code[c] |= *(src++) << 24; } } + + for(c = 0;c < numchans;c++) + { + int nibble = code[c]&0xf; + code[c] >>= 4; + + sample[c] += IMA4Codeword[nibble] * IMAStep_size[index[c]] / 8; + sample[c] = clampi(sample[c], -32768, 32767); + + index[c] += IMA4Index_adjust[nibble]; + index[c] = clampi(index[c], 0, 88); + + *(dst++) = sample[c]; + } } } -static void EncodeIMA4Block(ALima4 *dst, const ALshort *src, ALint *sample, ALint *index, ALint numchans, ALsizei align) +static void DecodeMSADPCMBlock(ALshort *dst, const ALubyte *src, ALint numchans, ALsizei align) { - ALsizei j,k,c; + ALubyte blockpred[MAX_INPUT_CHANNELS] = { 0 }; + ALint delta[MAX_INPUT_CHANNELS] = { 0 }; + ALshort samples[MAX_INPUT_CHANNELS][2] = { { 0, 0 } }; + ALint c, i; for(c = 0;c < numchans;c++) { - int diff = src[c] - sample[c]; - int step = IMAStep_size[index[c]]; - int nibble; - - nibble = 0; - if(diff < 0) - { - nibble = 0x8; - diff = -diff; - } - - diff = mini(step*2, diff); - nibble |= (diff*8/step - 1) / 2; - - sample[c] += IMA4Codeword[nibble] * step / 8; - sample[c] = clampi(sample[c], -32768, 32767); - - index[c] += IMA4Index_adjust[nibble]; - index[c] = clampi(index[c], 0, 88); - - *(dst++) = sample[c] & 0xff; - *(dst++) = (sample[c]>>8) & 0xff; - *(dst++) = index[c] & 0xff; - *(dst++) = (index[c]>>8) & 0xff; + blockpred[c] = *(src++); + blockpred[c] = minu(blockpred[c], 6); } - - for(j = 1;j < align;j += 8) + for(c = 0;c < numchans;c++) { - for(c = 0;c < numchans;c++) - { - for(k = 0;k < 8;k++) - { - int diff = src[(j+k)*numchans + c] - sample[c]; - int step = IMAStep_size[index[c]]; - int nibble; - - nibble = 0; - if(diff < 0) - { - nibble = 0x8; - diff = -diff; - } - - diff = mini(step*2, diff); - nibble |= (diff*8/step - 1) / 2; - - sample[c] += IMA4Codeword[nibble] * step / 8; - sample[c] = clampi(sample[c], -32768, 32767); - - index[c] += IMA4Index_adjust[nibble]; - index[c] = clampi(index[c], 0, 88); - - if(!(k&1)) *dst = nibble; - else *(dst++) |= nibble<<4; - } - } + delta[c] = *(src++); + delta[c] |= *(src++) << 8; + delta[c] = (delta[c]^0x8000) - 32768; } -} - - -static void DecodeMSADPCMBlock(ALshort *dst, const ALmsadpcm *src, ALint numchans, ALsizei align) -{ - ALubyte blockpred[MAX_INPUT_CHANNELS]; - ALint delta[MAX_INPUT_CHANNELS]; - ALshort samples[MAX_INPUT_CHANNELS][2]; - ALint i, j; - - for(i = 0;i < numchans;i++) + for(c = 0;c < numchans;c++) { - blockpred[i] = *(src++); - blockpred[i] = minu(blockpred[i], 6); + samples[c][0] = *(src++); + samples[c][0] |= *(src++) << 8; + samples[c][0] = (samples[c][0]^0x8000) - 32768; } - for(i = 0;i < numchans;i++) + for(c = 0;c < numchans;c++) { - delta[i] = *(src++); - delta[i] |= *(src++) << 8; - delta[i] = (delta[i]^0x8000) - 0x8000; - } - for(i = 0;i < numchans;i++) - { - samples[i][0] = *(src++); - samples[i][0] |= *(src++) << 8; - samples[i][0] = (samples[i][0]^0x8000) - 0x8000; - } - for(i = 0;i < numchans;i++) - { - samples[i][1] = *(src++); - samples[i][1] |= *(src++) << 8; - samples[i][1] = (samples[i][1]^0x8000) - 0x8000; + samples[c][1] = *(src++); + samples[c][1] |= *(src++) << 8; + samples[c][1] = (samples[c][1]^0x8000) - 0x8000; } /* Second sample is written first. */ - for(i = 0;i < numchans;i++) - *(dst++) = samples[i][1]; - for(i = 0;i < numchans;i++) - *(dst++) = samples[i][0]; + for(c = 0;c < numchans;c++) + *(dst++) = samples[c][1]; + for(c = 0;c < numchans;c++) + *(dst++) = samples[c][0]; - for(j = 2;j < align;j++) + for(i = 2;i < align;i++) { - for(i = 0;i < numchans;i++) + for(c = 0;c < numchans;c++) { - const ALint num = (j*numchans) + i; + const ALint num = (i*numchans) + c; ALint nibble, pred; /* Read the nibble (first is in the upper bits). */ @@ -402,388 +228,28 @@ static void DecodeMSADPCMBlock(ALshort *dst, const ALmsadpcm *src, ALint numchan else nibble = (*(src++))&0x0f; - pred = (samples[i][0]*MSADPCMAdaptionCoeff[blockpred[i]][0] + - samples[i][1]*MSADPCMAdaptionCoeff[blockpred[i]][1]) / 256; - pred += ((nibble^0x08) - 0x08) * delta[i]; + pred = (samples[c][0]*MSADPCMAdaptionCoeff[blockpred[c]][0] + + samples[c][1]*MSADPCMAdaptionCoeff[blockpred[c]][1]) / 256; + pred += ((nibble^0x08) - 0x08) * delta[c]; pred = clampi(pred, -32768, 32767); - samples[i][1] = samples[i][0]; - samples[i][0] = pred; + samples[c][1] = samples[c][0]; + samples[c][0] = pred; - delta[i] = (MSADPCMAdaption[nibble] * delta[i]) / 256; - delta[i] = maxi(16, delta[i]); + delta[c] = (MSADPCMAdaption[nibble] * delta[c]) / 256; + delta[c] = maxi(16, delta[c]); *(dst++) = pred; } } } -/* NOTE: This encoder is pretty dumb/simplistic. Some kind of pre-processing - * that tries to find the optimal block predictors would be nice, at least. A - * multi-pass method that can generate better deltas would be good, too. */ -static void EncodeMSADPCMBlock(ALmsadpcm *dst, const ALshort *src, ALint *sample, ALint numchans, ALsizei align) -{ - ALubyte blockpred[MAX_INPUT_CHANNELS]; - ALint delta[MAX_INPUT_CHANNELS]; - ALshort samples[MAX_INPUT_CHANNELS][2]; - ALint i, j; - /* Block predictor */ - for(i = 0;i < numchans;i++) - { - /* FIXME: Calculate something better. */ - blockpred[i] = 0; - *(dst++) = blockpred[i]; - } - /* Initial delta */ - for(i = 0;i < numchans;i++) - { - delta[i] = 16; - *(dst++) = (delta[i] ) & 0xff; - *(dst++) = (delta[i]>>8) & 0xff; - } - /* Initial sample 1 */ - for(i = 0;i < numchans;i++) - { - samples[i][0] = src[1*numchans + i]; - *(dst++) = (samples[i][0] ) & 0xff; - *(dst++) = (samples[i][0]>>8) & 0xff; - } - /* Initial sample 2 */ - for(i = 0;i < numchans;i++) - { - samples[i][1] = src[i]; - *(dst++) = (samples[i][1] ) & 0xff; - *(dst++) = (samples[i][1]>>8) & 0xff; - } - - for(j = 2;j < align;j++) - { - for(i = 0;i < numchans;i++) - { - const ALint num = (j*numchans) + i; - ALint nibble = 0; - ALint bias; - - sample[i] = (samples[i][0]*MSADPCMAdaptionCoeff[blockpred[i]][0] + - samples[i][1]*MSADPCMAdaptionCoeff[blockpred[i]][1]) / 256; - - nibble = src[num] - sample[i]; - if(nibble >= 0) - bias = delta[i] / 2; - else - bias = -delta[i] / 2; - - nibble = (nibble + bias) / delta[i]; - nibble = clampi(nibble, -8, 7)&0x0f; - - sample[i] += ((nibble^0x08)-0x08) * delta[i]; - sample[i] = clampi(sample[i], -32768, 32767); - - samples[i][1] = samples[i][0]; - samples[i][0] = sample[i]; - - delta[i] = (MSADPCMAdaption[nibble] * delta[i]) / 256; - delta[i] = maxi(16, delta[i]); - - if(!(num&1)) - *dst = nibble << 4; - else - { - *dst |= nibble; - dst++; - } - } - } -} - - -static inline ALint DecodeByte3(ALbyte3 val) -{ - if(IS_LITTLE_ENDIAN) - return (val.b[2]<<16) | (((ALubyte)val.b[1])<<8) | ((ALubyte)val.b[0]); - return (val.b[0]<<16) | (((ALubyte)val.b[1])<<8) | ((ALubyte)val.b[2]); -} - -static inline ALbyte3 EncodeByte3(ALint val) -{ - if(IS_LITTLE_ENDIAN) - { - ALbyte3 ret = {{ val, val>>8, val>>16 }}; - return ret; - } - else - { - ALbyte3 ret = {{ val>>16, val>>8, val }}; - return ret; - } -} - -static inline ALint DecodeUByte3(ALubyte3 val) -{ - if(IS_LITTLE_ENDIAN) - return (val.b[2]<<16) | (val.b[1]<<8) | (val.b[0]); - return (val.b[0]<<16) | (val.b[1]<<8) | val.b[2]; -} - -static inline ALubyte3 EncodeUByte3(ALint val) -{ - if(IS_LITTLE_ENDIAN) - { - ALubyte3 ret = {{ val, val>>8, val>>16 }}; - return ret; - } - else - { - ALubyte3 ret = {{ val>>16, val>>8, val }}; - return ret; - } -} - - -/* Define same-type pass-through sample conversion functions (excludes ADPCM, - * which are block-based). */ -#define DECL_TEMPLATE(T) \ -static inline T Conv_##T##_##T(T val) { return val; } - -DECL_TEMPLATE(ALbyte); -DECL_TEMPLATE(ALubyte); -DECL_TEMPLATE(ALshort); -DECL_TEMPLATE(ALushort); -DECL_TEMPLATE(ALint); -DECL_TEMPLATE(ALuint); -DECL_TEMPLATE(ALbyte3); -DECL_TEMPLATE(ALubyte3); -DECL_TEMPLATE(ALalaw); -DECL_TEMPLATE(ALmulaw); - -/* Slightly special handling for floats and doubles (converts NaN to 0, and - * allows float<->double pass-through). - */ -static inline ALfloat Conv_ALfloat_ALfloat(ALfloat val) -{ return (val==val) ? val : 0.0f; } -static inline ALfloat Conv_ALfloat_ALdouble(ALdouble val) -{ return (val==val) ? (ALfloat)val : 0.0f; } -static inline ALdouble Conv_ALdouble_ALfloat(ALfloat val) -{ return (val==val) ? (ALdouble)val : 0.0; } -static inline ALdouble Conv_ALdouble_ALdouble(ALdouble val) -{ return (val==val) ? val : 0.0; } - -#undef DECL_TEMPLATE - -/* Define alternate-sign functions. */ -#define DECL_TEMPLATE(T1, T2, O) \ -static inline T1 Conv_##T1##_##T2(T2 val) { return (T1)val - O; } \ -static inline T2 Conv_##T2##_##T1(T1 val) { return (T2)val + O; } - -DECL_TEMPLATE(ALbyte, ALubyte, 128); -DECL_TEMPLATE(ALshort, ALushort, 32768); -DECL_TEMPLATE(ALint, ALuint, 2147483648u); - -#undef DECL_TEMPLATE - -/* Define int-type to int-type functions */ -#define DECL_TEMPLATE(T, ST, UT, SH) \ -static inline T Conv_##T##_##ST(ST val){ return val >> SH; } \ -static inline T Conv_##T##_##UT(UT val){ return Conv_##ST##_##UT(val) >> SH; }\ -static inline ST Conv_##ST##_##T(T val){ return val << SH; } \ -static inline UT Conv_##UT##_##T(T val){ return Conv_##UT##_##ST(val << SH); } - -#define DECL_TEMPLATE2(T1, T2, SH) \ -DECL_TEMPLATE(AL##T1, AL##T2, ALu##T2, SH) \ -DECL_TEMPLATE(ALu##T1, ALu##T2, AL##T2, SH) - -DECL_TEMPLATE2(byte, short, 8) -DECL_TEMPLATE2(short, int, 16) -DECL_TEMPLATE2(byte, int, 24) - -#undef DECL_TEMPLATE2 -#undef DECL_TEMPLATE - -/* Define int-type to fp functions */ -#define DECL_TEMPLATE(T, ST, UT, OP) \ -static inline T Conv_##T##_##ST(ST val) { return (T)val * OP; } \ -static inline T Conv_##T##_##UT(UT val) { return (T)Conv_##ST##_##UT(val) * OP; } - -#define DECL_TEMPLATE2(T1, T2, OP) \ -DECL_TEMPLATE(T1, AL##T2, ALu##T2, OP) - -DECL_TEMPLATE2(ALfloat, byte, (1.0f/127.0f)) -DECL_TEMPLATE2(ALdouble, byte, (1.0/127.0)) -DECL_TEMPLATE2(ALfloat, short, (1.0f/32767.0f)) -DECL_TEMPLATE2(ALdouble, short, (1.0/32767.0)) -DECL_TEMPLATE2(ALdouble, int, (1.0/2147483647.0)) - -/* Special handling for int32 to float32, since it would overflow. */ -static inline ALfloat Conv_ALfloat_ALint(ALint val) -{ return (ALfloat)(val>>7) * (1.0f/16777215.0f); } -static inline ALfloat Conv_ALfloat_ALuint(ALuint val) -{ return (ALfloat)(Conv_ALint_ALuint(val)>>7) * (1.0f/16777215.0f); } - -#undef DECL_TEMPLATE2 -#undef DECL_TEMPLATE - -/* Define fp to int-type functions */ -#define DECL_TEMPLATE(FT, T, smin, smax) \ -static inline AL##T Conv_AL##T##_##FT(FT val) \ -{ \ - if(val > 1.0f) return smax; \ - if(val < -1.0f) return smin; \ - return (AL##T)(val * (FT)smax); \ -} \ -static inline ALu##T Conv_ALu##T##_##FT(FT val) \ -{ return Conv_ALu##T##_AL##T(Conv_AL##T##_##FT(val)); } - -DECL_TEMPLATE(ALfloat, byte, -128, 127) -DECL_TEMPLATE(ALdouble, byte, -128, 127) -DECL_TEMPLATE(ALfloat, short, -32768, 32767) -DECL_TEMPLATE(ALdouble, short, -32768, 32767) -DECL_TEMPLATE(ALdouble, int, -2147483647-1, 2147483647) - -/* Special handling for float32 to int32, since it would overflow. */ -static inline ALint Conv_ALint_ALfloat(ALfloat val) -{ - if(val > 1.0f) return 2147483647; - if(val < -1.0f) return -2147483647-1; - return (ALint)(val * 16777215.0f) << 7; -} -static inline ALuint Conv_ALuint_ALfloat(ALfloat val) -{ return Conv_ALuint_ALint(Conv_ALint_ALfloat(val)); } - -#undef DECL_TEMPLATE - -/* Define byte3 and ubyte3 functions (goes through int and uint functions). */ -#define DECL_TEMPLATE(T) \ -static inline ALbyte3 Conv_ALbyte3_##T(T val) \ -{ return EncodeByte3(Conv_ALint_##T(val)>>8); } \ -static inline T Conv_##T##_ALbyte3(ALbyte3 val) \ -{ return Conv_##T##_ALint(DecodeByte3(val)<<8); } \ - \ -static inline ALubyte3 Conv_ALubyte3_##T(T val) \ -{ return EncodeUByte3(Conv_ALuint_##T(val)>>8); } \ -static inline T Conv_##T##_ALubyte3(ALubyte3 val) \ -{ return Conv_##T##_ALuint(DecodeUByte3(val)<<8); } - -DECL_TEMPLATE(ALbyte) -DECL_TEMPLATE(ALubyte) -DECL_TEMPLATE(ALshort) -DECL_TEMPLATE(ALushort) -DECL_TEMPLATE(ALint) -DECL_TEMPLATE(ALuint) -DECL_TEMPLATE(ALfloat) -DECL_TEMPLATE(ALdouble) - -#undef DECL_TEMPLATE - -/* Define byte3 <-> ubyte3 functions. */ -static inline ALbyte3 Conv_ALbyte3_ALubyte3(ALubyte3 val) -{ return EncodeByte3(DecodeUByte3(val)-8388608); } -static inline ALubyte3 Conv_ALubyte3_ALbyte3(ALbyte3 val) -{ return EncodeUByte3(DecodeByte3(val)+8388608); } - -/* Define muLaw and aLaw functions (goes through short functions). */ -#define DECL_TEMPLATE(T) \ -static inline ALmulaw Conv_ALmulaw_##T(T val) \ -{ return EncodeMuLaw(Conv_ALshort_##T(val)); } \ -static inline T Conv_##T##_ALmulaw(ALmulaw val) \ -{ return Conv_##T##_ALshort(DecodeMuLaw(val)); } \ - \ -static inline ALalaw Conv_ALalaw_##T(T val) \ -{ return EncodeALaw(Conv_ALshort_##T(val)); } \ -static inline T Conv_##T##_ALalaw(ALalaw val) \ -{ return Conv_##T##_ALshort(DecodeALaw(val)); } - -DECL_TEMPLATE(ALbyte) -DECL_TEMPLATE(ALubyte) -DECL_TEMPLATE(ALshort) -DECL_TEMPLATE(ALushort) -DECL_TEMPLATE(ALint) -DECL_TEMPLATE(ALuint) -DECL_TEMPLATE(ALfloat) -DECL_TEMPLATE(ALdouble) -DECL_TEMPLATE(ALbyte3) -DECL_TEMPLATE(ALubyte3) - -#undef DECL_TEMPLATE - -/* Define muLaw <-> aLaw functions. */ -static inline ALalaw Conv_ALalaw_ALmulaw(ALmulaw val) -{ return EncodeALaw(DecodeMuLaw(val)); } -static inline ALmulaw Conv_ALmulaw_ALalaw(ALalaw val) -{ return EncodeMuLaw(DecodeALaw(val)); } - - -#define DECL_TEMPLATE(T1, T2) \ -static void Convert_##T1##_##T2(T1 *dst, const T2 *src, ALuint numchans, \ - ALuint len, ALsizei UNUSED(align)) \ -{ \ - ALuint i, j; \ - for(i = 0;i < len;i++) \ - { \ - for(j = 0;j < numchans;j++) \ - *(dst++) = Conv_##T1##_##T2(*(src++)); \ - } \ -} - -#define DECL_TEMPLATE2(T) \ -DECL_TEMPLATE(T, ALbyte) \ -DECL_TEMPLATE(T, ALubyte) \ -DECL_TEMPLATE(T, ALshort) \ -DECL_TEMPLATE(T, ALushort) \ -DECL_TEMPLATE(T, ALint) \ -DECL_TEMPLATE(T, ALuint) \ -DECL_TEMPLATE(T, ALfloat) \ -DECL_TEMPLATE(T, ALdouble) \ -DECL_TEMPLATE(T, ALmulaw) \ -DECL_TEMPLATE(T, ALalaw) \ -DECL_TEMPLATE(T, ALbyte3) \ -DECL_TEMPLATE(T, ALubyte3) - -DECL_TEMPLATE2(ALbyte) -DECL_TEMPLATE2(ALubyte) -DECL_TEMPLATE2(ALshort) -DECL_TEMPLATE2(ALushort) -DECL_TEMPLATE2(ALint) -DECL_TEMPLATE2(ALuint) -DECL_TEMPLATE2(ALfloat) -DECL_TEMPLATE2(ALdouble) -DECL_TEMPLATE2(ALmulaw) -DECL_TEMPLATE2(ALalaw) -DECL_TEMPLATE2(ALbyte3) -DECL_TEMPLATE2(ALubyte3) - -#undef DECL_TEMPLATE2 -#undef DECL_TEMPLATE - -#define DECL_TEMPLATE(T) \ -static void Convert_##T##_ALima4(T *dst, const ALima4 *src, ALuint numchans, \ - ALuint len, ALuint align) \ -{ \ - ALsizei byte_align = ((align-1)/2 + 4) * numchans; \ - DECL_VLA(ALshort, tmp, align*numchans); \ - ALuint i, j, k; \ - \ - assert(align > 0 && (len%align) == 0); \ - for(i = 0;i < len;i += align) \ - { \ - DecodeIMA4Block(tmp, src, numchans, align); \ - src += byte_align; \ - \ - for(j = 0;j < align;j++) \ - { \ - for(k = 0;k < numchans;k++) \ - *(dst++) = Conv_##T##_ALshort(tmp[j*numchans + k]); \ - } \ - } \ -} - -DECL_TEMPLATE(ALbyte) -DECL_TEMPLATE(ALubyte) -static void Convert_ALshort_ALima4(ALshort *dst, const ALima4 *src, ALuint numchans, - ALuint len, ALuint align) +void Convert_ALshort_ALima4(ALshort *dst, const ALubyte *src, ALsizei numchans, ALsizei len, + ALsizei align) { ALsizei byte_align = ((align-1)/2 + 4) * numchans; - ALuint i; + ALsizei i; assert(align > 0 && (len%align) == 0); for(i = 0;i < len;i += align) @@ -793,103 +259,12 @@ static void Convert_ALshort_ALima4(ALshort *dst, const ALima4 *src, ALuint numch dst += align*numchans; } } -DECL_TEMPLATE(ALushort) -DECL_TEMPLATE(ALint) -DECL_TEMPLATE(ALuint) -DECL_TEMPLATE(ALfloat) -DECL_TEMPLATE(ALdouble) -DECL_TEMPLATE(ALmulaw) -DECL_TEMPLATE(ALalaw) -DECL_TEMPLATE(ALbyte3) -DECL_TEMPLATE(ALubyte3) -#undef DECL_TEMPLATE - -#define DECL_TEMPLATE(T) \ -static void Convert_ALima4_##T(ALima4 *dst, const T *src, ALuint numchans, \ - ALuint len, ALuint align) \ -{ \ - ALint sample[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0}; \ - ALint index[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0}; \ - ALsizei byte_align = ((align-1)/2 + 4) * numchans; \ - DECL_VLA(ALshort, tmp, align*numchans); \ - ALuint i, j, k; \ - \ - assert(align > 0 && (len%align) == 0); \ - for(i = 0;i < len;i += align) \ - { \ - for(j = 0;j < align;j++) \ - { \ - for(k = 0;k < numchans;k++) \ - tmp[j*numchans + k] = Conv_ALshort_##T(*(src++)); \ - } \ - EncodeIMA4Block(dst, tmp, sample, index, numchans, align); \ - dst += byte_align; \ - } \ -} - -DECL_TEMPLATE(ALbyte) -DECL_TEMPLATE(ALubyte) -static void Convert_ALima4_ALshort(ALima4 *dst, const ALshort *src, - ALuint numchans, ALuint len, ALuint align) -{ - ALint sample[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0}; - ALint index[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0}; - ALsizei byte_align = ((align-1)/2 + 4) * numchans; - ALuint i; - - assert(align > 0 && (len%align) == 0); - for(i = 0;i < len;i += align) - { - EncodeIMA4Block(dst, src, sample, index, numchans, align); - src += align*numchans; - dst += byte_align; - } -} -DECL_TEMPLATE(ALushort) -DECL_TEMPLATE(ALint) -DECL_TEMPLATE(ALuint) -DECL_TEMPLATE(ALfloat) -DECL_TEMPLATE(ALdouble) -DECL_TEMPLATE(ALmulaw) -DECL_TEMPLATE(ALalaw) -DECL_TEMPLATE(ALbyte3) -DECL_TEMPLATE(ALubyte3) - -#undef DECL_TEMPLATE - - -#define DECL_TEMPLATE(T) \ -static void Convert_##T##_ALmsadpcm(T *dst, const ALmsadpcm *src, \ - ALuint numchans, ALuint len, \ - ALuint align) \ -{ \ - ALsizei byte_align = ((align-2)/2 + 7) * numchans; \ - DECL_VLA(ALshort, tmp, align*numchans); \ - ALuint i, j, k; \ - \ - assert(align > 1 && (len%align) == 0); \ - for(i = 0;i < len;i += align) \ - { \ - DecodeMSADPCMBlock(tmp, src, numchans, align); \ - src += byte_align; \ - \ - for(j = 0;j < align;j++) \ - { \ - for(k = 0;k < numchans;k++) \ - *(dst++) = Conv_##T##_ALshort(tmp[j*numchans + k]); \ - } \ - } \ -} - -DECL_TEMPLATE(ALbyte) -DECL_TEMPLATE(ALubyte) -static void Convert_ALshort_ALmsadpcm(ALshort *dst, const ALmsadpcm *src, - ALuint numchans, ALuint len, - ALuint align) +void Convert_ALshort_ALmsadpcm(ALshort *dst, const ALubyte *src, ALsizei numchans, ALsizei len, + ALsizei align) { ALsizei byte_align = ((align-2)/2 + 7) * numchans; - ALuint i; + ALsizei i; assert(align > 1 && (len%align) == 0); for(i = 0;i < len;i += align) @@ -899,214 +274,3 @@ static void Convert_ALshort_ALmsadpcm(ALshort *dst, const ALmsadpcm *src, dst += align*numchans; } } -DECL_TEMPLATE(ALushort) -DECL_TEMPLATE(ALint) -DECL_TEMPLATE(ALuint) -DECL_TEMPLATE(ALfloat) -DECL_TEMPLATE(ALdouble) -DECL_TEMPLATE(ALmulaw) -DECL_TEMPLATE(ALalaw) -DECL_TEMPLATE(ALbyte3) -DECL_TEMPLATE(ALubyte3) - -#undef DECL_TEMPLATE - -#define DECL_TEMPLATE(T) \ -static void Convert_ALmsadpcm_##T(ALmsadpcm *dst, const T *src, \ - ALuint numchans, ALuint len, ALuint align) \ -{ \ - ALint sample[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0}; \ - ALsizei byte_align = ((align-2)/2 + 7) * numchans; \ - DECL_VLA(ALshort, tmp, align*numchans); \ - ALuint i, j, k; \ - \ - assert(align > 1 && (len%align) == 0); \ - for(i = 0;i < len;i += align) \ - { \ - for(j = 0;j < align;j++) \ - { \ - for(k = 0;k < numchans;k++) \ - tmp[j*numchans + k] = Conv_ALshort_##T(*(src++)); \ - } \ - EncodeMSADPCMBlock(dst, tmp, sample, numchans, align); \ - dst += byte_align; \ - } \ -} - -DECL_TEMPLATE(ALbyte) -DECL_TEMPLATE(ALubyte) -static void Convert_ALmsadpcm_ALshort(ALmsadpcm *dst, const ALshort *src, - ALuint numchans, ALuint len, ALuint align) -{ - ALint sample[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0}; - ALsizei byte_align = ((align-2)/2 + 7) * numchans; - ALuint i; - - assert(align > 1 && (len%align) == 0); - for(i = 0;i < len;i += align) - { - EncodeMSADPCMBlock(dst, src, sample, numchans, align); - src += align*numchans; - dst += byte_align; - } -} -DECL_TEMPLATE(ALushort) -DECL_TEMPLATE(ALint) -DECL_TEMPLATE(ALuint) -DECL_TEMPLATE(ALfloat) -DECL_TEMPLATE(ALdouble) -DECL_TEMPLATE(ALmulaw) -DECL_TEMPLATE(ALalaw) -DECL_TEMPLATE(ALbyte3) -DECL_TEMPLATE(ALubyte3) - -#undef DECL_TEMPLATE - -/* NOTE: We don't store compressed samples internally, so these conversions - * should never happen. */ -static void Convert_ALima4_ALima4(ALima4* UNUSED(dst), const ALima4* UNUSED(src), - ALuint UNUSED(numchans), ALuint UNUSED(len), - ALuint UNUSED(align)) -{ - ERR("Unexpected IMA4-to-IMA4 conversion!\n"); -} - -static void Convert_ALmsadpcm_ALmsadpcm(ALmsadpcm* UNUSED(dst), const ALmsadpcm* UNUSED(src), - ALuint UNUSED(numchans), ALuint UNUSED(len), - ALuint UNUSED(align)) -{ - ERR("Unexpected MSADPCM-to-MSADPCM conversion!\n"); -} - -static void Convert_ALmsadpcm_ALima4(ALmsadpcm* UNUSED(dst), const ALima4* UNUSED(src), - ALuint UNUSED(numchans), ALuint UNUSED(len), - ALuint UNUSED(align)) -{ - ERR("Unexpected IMA4-to-MSADPCM conversion!\n"); -} - -static void Convert_ALima4_ALmsadpcm(ALima4* UNUSED(dst), const ALmsadpcm* UNUSED(src), - ALuint UNUSED(numchans), ALuint UNUSED(len), - ALuint UNUSED(align)) -{ - ERR("Unexpected MSADPCM-to-IMA4 conversion!\n"); -} - - -#define DECL_TEMPLATE(T) \ -static void Convert_##T(T *dst, const ALvoid *src, enum UserFmtType srcType, \ - ALsizei numchans, ALsizei len, ALsizei align) \ -{ \ - switch(srcType) \ - { \ - case UserFmtByte: \ - Convert_##T##_ALbyte(dst, src, numchans, len, align); \ - break; \ - case UserFmtUByte: \ - Convert_##T##_ALubyte(dst, src, numchans, len, align); \ - break; \ - case UserFmtShort: \ - Convert_##T##_ALshort(dst, src, numchans, len, align); \ - break; \ - case UserFmtUShort: \ - Convert_##T##_ALushort(dst, src, numchans, len, align); \ - break; \ - case UserFmtInt: \ - Convert_##T##_ALint(dst, src, numchans, len, align); \ - break; \ - case UserFmtUInt: \ - Convert_##T##_ALuint(dst, src, numchans, len, align); \ - break; \ - case UserFmtFloat: \ - Convert_##T##_ALfloat(dst, src, numchans, len, align); \ - break; \ - case UserFmtDouble: \ - Convert_##T##_ALdouble(dst, src, numchans, len, align); \ - break; \ - case UserFmtMulaw: \ - Convert_##T##_ALmulaw(dst, src, numchans, len, align); \ - break; \ - case UserFmtAlaw: \ - Convert_##T##_ALalaw(dst, src, numchans, len, align); \ - break; \ - case UserFmtIMA4: \ - Convert_##T##_ALima4(dst, src, numchans, len, align); \ - break; \ - case UserFmtMSADPCM: \ - Convert_##T##_ALmsadpcm(dst, src, numchans, len, align); \ - break; \ - case UserFmtByte3: \ - Convert_##T##_ALbyte3(dst, src, numchans, len, align); \ - break; \ - case UserFmtUByte3: \ - Convert_##T##_ALubyte3(dst, src, numchans, len, align); \ - break; \ - } \ -} - -DECL_TEMPLATE(ALbyte) -DECL_TEMPLATE(ALubyte) -DECL_TEMPLATE(ALshort) -DECL_TEMPLATE(ALushort) -DECL_TEMPLATE(ALint) -DECL_TEMPLATE(ALuint) -DECL_TEMPLATE(ALfloat) -DECL_TEMPLATE(ALdouble) -DECL_TEMPLATE(ALmulaw) -DECL_TEMPLATE(ALalaw) -DECL_TEMPLATE(ALima4) -DECL_TEMPLATE(ALmsadpcm) -DECL_TEMPLATE(ALbyte3) -DECL_TEMPLATE(ALubyte3) - -#undef DECL_TEMPLATE - - -void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src, enum UserFmtType srcType, ALsizei numchans, ALsizei len, ALsizei align) -{ - switch(dstType) - { - case UserFmtByte: - Convert_ALbyte(dst, src, srcType, numchans, len, align); - break; - case UserFmtUByte: - Convert_ALubyte(dst, src, srcType, numchans, len, align); - break; - case UserFmtShort: - Convert_ALshort(dst, src, srcType, numchans, len, align); - break; - case UserFmtUShort: - Convert_ALushort(dst, src, srcType, numchans, len, align); - break; - case UserFmtInt: - Convert_ALint(dst, src, srcType, numchans, len, align); - break; - case UserFmtUInt: - Convert_ALuint(dst, src, srcType, numchans, len, align); - break; - case UserFmtFloat: - Convert_ALfloat(dst, src, srcType, numchans, len, align); - break; - case UserFmtDouble: - Convert_ALdouble(dst, src, srcType, numchans, len, align); - break; - case UserFmtMulaw: - Convert_ALmulaw(dst, src, srcType, numchans, len, align); - break; - case UserFmtAlaw: - Convert_ALalaw(dst, src, srcType, numchans, len, align); - break; - case UserFmtIMA4: - Convert_ALima4(dst, src, srcType, numchans, len, align); - break; - case UserFmtMSADPCM: - Convert_ALmsadpcm(dst, src, srcType, numchans, len, align); - break; - case UserFmtByte3: - Convert_ALbyte3(dst, src, srcType, numchans, len, align); - break; - case UserFmtUByte3: - Convert_ALubyte3(dst, src, srcType, numchans, len, align); - break; - } -} diff --git a/Engine/lib/openal-soft/README b/Engine/lib/openal-soft/README deleted file mode 100644 index a0178ae5e..000000000 --- a/Engine/lib/openal-soft/README +++ /dev/null @@ -1,55 +0,0 @@ -Source Install -============== - -To install OpenAL Soft, use your favorite shell to go into the build/ -directory, and run: - -cmake .. - -Assuming configuration went well, you can then build it, typically using GNU -Make (KDevelop, MSVC, and others are possible depending on your system setup -and CMake configuration). - -Please Note: Double check that the appropriate backends were detected. Often, -complaints of no sound, crashing, and missing devices can be solved by making -sure the correct backends are being used. CMake's output will identify which -backends were enabled. - -For most systems, you will likely want to make sure ALSA, OSS, and PulseAudio -were detected (if your target system uses them). For Windows, make sure -DirectSound was detected. - - -Utilities -========= - -The source package comes with an informational utility, openal-info, and is -built by default. It prints out information provided by the ALC and AL sub- -systems, including discovered devices, version information, and extensions. - - -Configuration -============= - -OpenAL Soft can be configured on a per-user and per-system basis. This allows -users and sysadmins to control information provided to applications, as well -as application-agnostic behavior of the library. See alsoftrc.sample for -available settings. - - -Acknowledgements -================ - -Special thanks go to: - -Creative Labs for the original source code this is based off of. - -Christopher Fitzgerald for the current reverb effect implementation, and -helping with the low-pass and HRTF filters. - -Christian Borss for the 3D panning code previous versions used as a base. - -Ben Davis for the idea behind a previous version of the click-removal code. - -Richard Furse for helping with my understanding of Ambisonics that is used by -the various parts of the library. diff --git a/Engine/lib/openal-soft/README.md b/Engine/lib/openal-soft/README.md new file mode 100644 index 000000000..0c9d30276 --- /dev/null +++ b/Engine/lib/openal-soft/README.md @@ -0,0 +1,61 @@ +OpenAL soft +=========== + +`master` branch CI status : [![Build Status](https://travis-ci.org/kcat/openal-soft.svg?branch=master)](https://travis-ci.org/kcat/openal-soft) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/kcat/openal-soft?branch=master&svg=true)](https://ci.appveyor.com/api/projects/status/github/kcat/openal-soft?branch=master&svg=true) + +OpenAL Soft is an LGPL-licensed, cross-platform, software implementation of the OpenAL 3D audio API. It's forked from the open-sourced Windows version available originally from openal.org's SVN repository (now defunct). +OpenAL provides capabilities for playing audio in a virtual 3D environment. Distance attenuation, doppler shift, and directional sound emitters are among the features handled by the API. More advanced effects, including air absorption, occlusion, and environmental reverb, are available through the EFX extension. It also facilitates streaming audio, multi-channel buffers, and audio capture. + +More information is available on the [official website](http://openal-soft.org/) + +Source Install +------------- +To install OpenAL Soft, use your favorite shell to go into the build/ +directory, and run: + +```bash +cmake .. +``` + +Assuming configuration went well, you can then build it, typically using GNU +Make (KDevelop, MSVC, and others are possible depending on your system setup +and CMake configuration). + +Please Note: Double check that the appropriate backends were detected. Often, +complaints of no sound, crashing, and missing devices can be solved by making +sure the correct backends are being used. CMake's output will identify which +backends were enabled. + +For most systems, you will likely want to make sure ALSA, OSS, and PulseAudio +were detected (if your target system uses them). For Windows, make sure +DirectSound was detected. + + +Utilities +--------- +The source package comes with an informational utility, openal-info, and is +built by default. It prints out information provided by the ALC and AL sub- +systems, including discovered devices, version information, and extensions. + + +Configuration +------------- + +OpenAL Soft can be configured on a per-user and per-system basis. This allows +users and sysadmins to control information provided to applications, as well +as application-agnostic behavior of the library. See alsoftrc.sample for +available settings. + + +Acknowledgements +---------------- + +Special thanks go to: + + - Creative Labs for the original source code this is based off of. + - Christopher Fitzgerald for the current reverb effect implementation, and +helping with the low-pass and HRTF filters. + - Christian Borss for the 3D panning code previous versions used as a base. + - Ben Davis for the idea behind a previous version of the click-removal code. + - Richard Furse for helping with my understanding of Ambisonics that is used by +the various parts of the library. diff --git a/Engine/lib/openal-soft/alsoftrc.sample b/Engine/lib/openal-soft/alsoftrc.sample index d5913ff0e..2b7093a95 100644 --- a/Engine/lib/openal-soft/alsoftrc.sample +++ b/Engine/lib/openal-soft/alsoftrc.sample @@ -81,7 +81,7 @@ # which helps protect against skips when the CPU is under load, but increases # the delay between a sound getting mixed and being heard. Acceptable values # range between 2 and 16. -#periods = 4 +#periods = 3 ## stereo-mode: # Specifies if stereo output is treated as being headphones or speakers. With @@ -89,13 +89,14 @@ # Valid settings are auto, speakers, and headphones. #stereo-mode = auto -## stereo-panning: -# Specifies the panning method for non-HRTF stereo output. uhj (default) -# creates stereo-compatible two-channel UHJ output, which encodes some -# surround sound information, while paired uses standard pair-wise panning -# between -30 and +30 degrees. If crossfeed filters are used, uhj panning is -# disabled. -#stereo-panning = uhj +## stereo-encoding: +# Specifies the encoding method for non-HRTF stereo output. 'panpot' (default) +# uses standard amplitude panning (aka pair-wise, stereo pair, etc) between +# -30 and +30 degrees, while 'uhj' creates stereo-compatible two-channel UHJ +# output, which encodes some surround sound information into stereo output +# that can be decoded with a surround sound receiver. If crossfeed filters are +# used, UHJ is disabled. +#stereo-encoding = panpot ## ambi-format: # Specifies the channel order and normalization for the "ambi*" set of channel @@ -148,11 +149,11 @@ # Selects the resampler used when mixing sources. Valid values are: # point - nearest sample, no interpolation # linear - extrapolates samples using a linear slope between samples -# sinc4 - extrapolates samples using a 4-point Sinc filter -# sinc8 - extrapolates samples using an 8-point Sinc filter -# bsinc - extrapolates samples using a band-limited Sinc filter (varying -# between 12 and 24 points, with anti-aliasing) -# Specifying other values will result in using the default (linear). +# cubic - extrapolates samples using a Catmull-Rom spline +# bsinc12 - extrapolates samples using a band-limited Sinc filter (varying +# between 12 and 24 points, with anti-aliasing) +# bsinc24 - extrapolates samples using a band-limited Sinc filter (varying +# between 24 and 48 points, with anti-aliasing) #resampler = linear ## rt-prio: (global) @@ -174,13 +175,39 @@ # can use a non-negligible amount of CPU time if an effect is set on it even # if no sources are feeding it, so this may help when apps use more than the # system can handle. -#slots = 4 +#slots = 64 ## sends: -# Sets the number of auxiliary sends per source. When not specified (default), -# it allows the app to request how many it wants. The maximum value currently -# possible is 4. -#sends = +# Limits the number of auxiliary sends allowed per source. Setting this higher +# than the default has no effect. +#sends = 16 + +## front-stablizer: +# Applies filters to "stablize" front sound imaging. A psychoacoustic method +# is used to generate a front-center channel signal from the front-left and +# front-right channels, improving the front response by reducing the combing +# artifacts and phase errors. Consequently, it will only work with channel +# configurations that include front-left, front-right, and front-center. +#front-stablizer = false + +## output-limiter: +# Applies a gain limiter on the final mixed output. This reduces the volume +# when the output samples would otherwise clamp, avoiding excessive clipping +# noise. +#output-limiter = true + +## dither: +# Applies dithering on the final mix, for 8- and 16-bit output by default. +# This replaces the distortion created by nearest-value quantization with low- +# level whitenoise. +#dither = true + +## dither-depth: +# Quantization bit-depth for dithered output. A value of 0 (or less) will +# match the output sample depth. For int32, uint32, and float32 output, 0 will +# disable dithering because they're at or beyond the rendered precision. The +# maximum dither depth is 24. +#dither-depth = 0 ## volume-adjust: # A global volume adjustment for source output, expressed in decibels. The @@ -193,7 +220,7 @@ # Sets which effects to exclude, preventing apps from using them. This can # help for apps that try to use effects which are too CPU intensive for the # system to handle. Available effects are: eaxreverb,reverb,chorus,compressor, -# distortion,echo,equalizer,flanger,modulator,dedicated +# distortion,echo,equalizer,flanger,modulator,dedicated,pshifter #excludefx = ## default-reverb: (global) @@ -235,26 +262,39 @@ hq-mode = false # Enables compensation for the speakers' relative distances to the listener. # This applies the necessary delays and attenuation to make the speakers # behave as though they are all equidistant, which is important for proper -# playback of 3D sound rendering. Requires the high-quality ambisonic decoder, -# as well as the proper distances to be specified in the decoder configuration -# file. +# playback of 3D sound rendering. Requires the proper distances to be +# specified in the decoder configuration file. distance-comp = true +## nfc: +# Enables near-field control filters. This simulates and compensates for low- +# frequency effects caused by the curvature of nearby sound-waves, which +# creates a more realistic perception of sound distance. Note that the effect +# may be stronger or weaker than intended if the application doesn't use or +# specify an appropriate unit scale, or if incorrect speaker distances are set +# in the decoder configuration file. Requires hq-mode to be enabled. +nfc = true + +## nfc-ref-delay +# Specifies the reference delay value for ambisonic output. When channels is +# set to one of the ambi* formats, this option enables NFC-HOA output with the +# specified Reference Delay parameter. The specified value can then be shared +# with an appropriate NFC-HOA decoder to reproduce correct near-field effects. +# Keep in mind that despite being designed for higher-order ambisonics, this +# applies to first-order output all the same. When left unset, normal output +# is created with no near-field simulation. +nfc-ref-delay = + ## quad: -# Decoder configuration file for Quadrophonic channel output. See +# Decoder configuration file for Quadraphonic channel output. See # docs/ambdec.txt for a description of the file format. quad = ## surround51: -# Decoder configuration file for 5.1 Surround (Side) channel output. See -# docs/ambdec.txt for a description of the file format. +# Decoder configuration file for 5.1 Surround (Side and Rear) channel output. +# See docs/ambdec.txt for a description of the file format. surround51 = -## surround51rear: -# Decoder configuration file for 5.1 Surround (Rear) channel output. See -# docs/ambdec.txt for a description of the file format. -surround51rear = - ## surround61: # Decoder configuration file for 6.1 Surround channel output. See # docs/ambdec.txt for a description of the file format. @@ -279,12 +319,6 @@ surround71 = # value of 0 means no change. #boost = 0 -## emulate-eax: (global) -# Allows the standard reverb effect to be used in place of EAX reverb. EAX -# reverb processing is a bit more CPU intensive than standard, so this option -# allows a simpler effect to be used at the loss of some quality. -#emulate-eax = false - ## ## PulseAudio backend stuff ## @@ -410,9 +444,9 @@ surround71 = #buffer-size = 0 ## -## MMDevApi backend stuff +## WASAPI backend stuff ## -[mmdevapi] +[wasapi] ## ## DirectSound backend stuff diff --git a/Engine/lib/openal-soft/appveyor.yml b/Engine/lib/openal-soft/appveyor.yml index 4010f2ead..0e5b7ce40 100644 --- a/Engine/lib/openal-soft/appveyor.yml +++ b/Engine/lib/openal-soft/appveyor.yml @@ -1,4 +1,4 @@ -version: 1.17.2.{build} +version: 1.18.2.{build} environment: matrix: @@ -7,8 +7,13 @@ environment: - GEN: "Visual Studio 14 2015 Win64" CFG: Release +install: + # Remove the VS Xamarin targets to reduce AppVeyor specific noise in build + # logs. See also http://help.appveyor.com/discussions/problems/4569 + - del "C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.targets\ImportAfter\Xamarin.Common.targets" + build_script: - cd build - - cmake .. -G"%GEN%" + - cmake -G"%GEN%" -DALSOFT_REQUIRE_WINMM=ON -DALSOFT_REQUIRE_DSOUND=ON -DALSOFT_REQUIRE_WASAPI=ON -DALSOFT_EMBED_HRTF_DATA=YES .. - cmake --build . --config %CFG% --clean-first diff --git a/Engine/lib/openal-soft/cmake/CheckSharedFunctionExists.cmake b/Engine/lib/openal-soft/cmake/CheckSharedFunctionExists.cmake index 7975f2334..c691fa9c9 100644 --- a/Engine/lib/openal-soft/cmake/CheckSharedFunctionExists.cmake +++ b/Engine/lib/openal-soft/cmake/CheckSharedFunctionExists.cmake @@ -34,7 +34,7 @@ # License text for the above reference.) MACRO(CHECK_SHARED_FUNCTION_EXISTS SYMBOL FILES LIBRARY LOCATION VARIABLE) - IF("${VARIABLE}" MATCHES "^${VARIABLE}$") + IF(NOT DEFINED "${VARIABLE}" OR "x${${VARIABLE}}" STREQUAL "x${VARIABLE}") SET(CMAKE_CONFIGURABLE_FILE_CONTENT "/* */\n") SET(MACRO_CHECK_SYMBOL_EXISTS_FLAGS ${CMAKE_REQUIRED_FLAGS}) IF(CMAKE_REQUIRED_LIBRARIES) @@ -88,5 +88,5 @@ MACRO(CHECK_SHARED_FUNCTION_EXISTS SYMBOL FILES LIBRARY LOCATION VARIABLE) "${OUTPUT}\nFile ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.c:\n" "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n") ENDIF(${VARIABLE}) - ENDIF("${VARIABLE}" MATCHES "^${VARIABLE}$") + ENDIF(NOT DEFINED "${VARIABLE}" OR "x${${VARIABLE}}" STREQUAL "x${VARIABLE}") ENDMACRO(CHECK_SHARED_FUNCTION_EXISTS) diff --git a/Engine/lib/openal-soft/cmake/FindDSound.cmake b/Engine/lib/openal-soft/cmake/FindDSound.cmake index 0ddf98aad..4078deb5e 100644 --- a/Engine/lib/openal-soft/cmake/FindDSound.cmake +++ b/Engine/lib/openal-soft/cmake/FindDSound.cmake @@ -8,24 +8,30 @@ # DSOUND_LIBRARY - the dsound library # -find_path(DSOUND_INCLUDE_DIR - NAMES dsound.h - PATHS "${DXSDK_DIR}" - PATH_SUFFIXES include - DOC "The DirectSound include directory" -) +if (WIN32) + include(FindWindowsSDK) + if (WINDOWSSDK_FOUND) + get_windowssdk_library_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_LIB_DIRS) + get_windowssdk_include_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_INCLUDE_DIRS) + endif() +endif() +# DSOUND_INCLUDE_DIR +find_path(DSOUND_INCLUDE_DIR + NAMES "dsound.h" + PATHS "${DXSDK_DIR}" ${WINSDK_INCLUDE_DIRS} + PATH_SUFFIXES include + DOC "The DirectSound include directory") + +# DSOUND_LIBRARY find_library(DSOUND_LIBRARY NAMES dsound - PATHS "${DXSDK_DIR}" + PATHS "${DXSDK_DIR}" ${WINSDK_LIB_DIRS} PATH_SUFFIXES lib lib/x86 lib/x64 - DOC "The DirectSound library" -) + DOC "The DirectSound library") include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(DSound - REQUIRED_VARS DSOUND_LIBRARY DSOUND_INCLUDE_DIR -) +find_package_handle_standard_args(DSound REQUIRED_VARS DSOUND_LIBRARY DSOUND_INCLUDE_DIR) if(DSOUND_FOUND) set(DSOUND_LIBRARIES ${DSOUND_LIBRARY}) diff --git a/Engine/lib/openal-soft/cmake/FindFFmpeg.cmake b/Engine/lib/openal-soft/cmake/FindFFmpeg.cmake index 96cbb6ed0..c489c2c3c 100644 --- a/Engine/lib/openal-soft/cmake/FindFFmpeg.cmake +++ b/Engine/lib/openal-soft/cmake/FindFFmpeg.cmake @@ -142,6 +142,12 @@ foreach(_component ${FFmpeg_FIND_COMPONENTS}) endif() endforeach() +# Add libz if it exists (needed for static ffmpeg builds) +find_library(_FFmpeg_HAVE_LIBZ NAMES z) +if(_FFmpeg_HAVE_LIBZ) + set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${_FFmpeg_HAVE_LIBZ}) +endif() + # Build the include path and library list with duplicates removed. if(FFMPEG_INCLUDE_DIRS) list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS) diff --git a/Engine/lib/openal-soft/cmake/FindOSS.cmake b/Engine/lib/openal-soft/cmake/FindOSS.cmake index 88ee66ad2..feffb4516 100644 --- a/Engine/lib/openal-soft/cmake/FindOSS.cmake +++ b/Engine/lib/openal-soft/cmake/FindOSS.cmake @@ -2,8 +2,10 @@ # # OSS_FOUND - True if OSS_INCLUDE_DIR is found # OSS_INCLUDE_DIRS - Set when OSS_INCLUDE_DIR is found +# OSS_LIBRARIES - Set when OSS_LIBRARY is found # # OSS_INCLUDE_DIR - where to find sys/soundcard.h, etc. +# OSS_LIBRARY - where to find libossaudio (optional). # find_path(OSS_INCLUDE_DIR @@ -11,11 +13,21 @@ find_path(OSS_INCLUDE_DIR DOC "The OSS include directory" ) +find_library(OSS_LIBRARY + NAMES ossaudio + DOC "Optional OSS library" +) + include(FindPackageHandleStandardArgs) find_package_handle_standard_args(OSS REQUIRED_VARS OSS_INCLUDE_DIR) if(OSS_FOUND) set(OSS_INCLUDE_DIRS ${OSS_INCLUDE_DIR}) + if(OSS_LIBRARY) + set(OSS_LIBRARIES ${OSS_LIBRARY}) + else() + unset(OSS_LIBRARIES) + endif() endif() -mark_as_advanced(OSS_INCLUDE_DIR) +mark_as_advanced(OSS_INCLUDE_DIR OSS_LIBRARY) diff --git a/Engine/lib/openal-soft/cmake/FindSDL2.cmake b/Engine/lib/openal-soft/cmake/FindSDL2.cmake index 70e607a89..e808d0061 100644 --- a/Engine/lib/openal-soft/cmake/FindSDL2.cmake +++ b/Engine/lib/openal-soft/cmake/FindSDL2.cmake @@ -18,10 +18,10 @@ # module will automatically add the -framework Cocoa on your behalf. # # -# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration +# Additional Note: If you see an empty SDL2_CORE_LIBRARY in your configuration # and no SDL2_LIBRARY, it means CMake did not find your SDL2 library # (SDL2.dll, libsdl2.so, SDL2.framework, etc). -# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again. +# Set SDL2_CORE_LIBRARY to point to your SDL2 library, and configure again. # Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value # as appropriate. These values are used to generate the final SDL2_LIBRARY # variable, but when these values are unset, SDL2_LIBRARY does not get created. @@ -87,7 +87,7 @@ FIND_PATH(SDL2_INCLUDE_DIR SDL.h ) #MESSAGE("SDL2_INCLUDE_DIR is ${SDL2_INCLUDE_DIR}") -FIND_LIBRARY(SDL2_LIBRARY_TEMP +FIND_LIBRARY(SDL2_CORE_LIBRARY NAMES SDL2 HINTS $ENV{SDL2DIR} @@ -98,8 +98,7 @@ FIND_LIBRARY(SDL2_LIBRARY_TEMP /opt/csw /opt ) - -#MESSAGE("SDL2_LIBRARY_TEMP is ${SDL2_LIBRARY_TEMP}") +#MESSAGE("SDL2_CORE_LIBRARY is ${SDL2_CORE_LIBRARY}") IF(NOT SDL2_BUILDING_LIBRARY) IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") @@ -137,7 +136,9 @@ IF(MINGW) ENDIF(MINGW) SET(SDL2_FOUND "NO") -IF(SDL2_LIBRARY_TEMP) +IF(SDL2_CORE_LIBRARY) + SET(SDL2_LIBRARY_TEMP ${SDL2_CORE_LIBRARY}) + # For SDL2main IF(NOT SDL2_BUILDING_LIBRARY) IF(SDL2MAIN_LIBRARY) @@ -172,15 +173,12 @@ IF(SDL2_LIBRARY_TEMP) ENDIF(WIN32) # Set the final string here so the GUI reflects the final state. - SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found") - # Set the temp variable to INTERNAL so it is not seen in the CMake GUI - SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "") + SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP}) SET(SDL2_FOUND "YES") -ENDIF(SDL2_LIBRARY_TEMP) +ENDIF(SDL2_CORE_LIBRARY) INCLUDE(FindPackageHandleStandardArgs) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) diff --git a/Engine/lib/openal-soft/cmake/FindWindowsSDK.cmake b/Engine/lib/openal-soft/cmake/FindWindowsSDK.cmake new file mode 100644 index 000000000..e136b8976 --- /dev/null +++ b/Engine/lib/openal-soft/cmake/FindWindowsSDK.cmake @@ -0,0 +1,626 @@ +# - Find the Windows SDK aka Platform SDK +# +# Relevant Wikipedia article: http://en.wikipedia.org/wiki/Microsoft_Windows_SDK +# +# Pass "COMPONENTS tools" to ignore Visual Studio version checks: in case +# you just want the tool binaries to run, rather than the libraries and headers +# for compiling. +# +# Variables: +# WINDOWSSDK_FOUND - if any version of the windows or platform SDK was found that is usable with the current version of visual studio +# WINDOWSSDK_LATEST_DIR +# WINDOWSSDK_LATEST_NAME +# WINDOWSSDK_FOUND_PREFERENCE - if we found an entry indicating a "preferred" SDK listed for this visual studio version +# WINDOWSSDK_PREFERRED_DIR +# WINDOWSSDK_PREFERRED_NAME +# +# WINDOWSSDK_DIRS - contains no duplicates, ordered most recent first. +# WINDOWSSDK_PREFERRED_FIRST_DIRS - contains no duplicates, ordered with preferred first, followed by the rest in descending recency +# +# Functions: +# windowssdk_name_lookup( ) - Find the name corresponding with the SDK directory you pass in, or +# NOTFOUND if not recognized. Your directory must be one of WINDOWSSDK_DIRS for this to work. +# +# windowssdk_build_lookup( ) - Find the build version number corresponding with the SDK directory you pass in, or +# NOTFOUND if not recognized. Your directory must be one of WINDOWSSDK_DIRS for this to work. +# +# get_windowssdk_from_component( ) - Given a library or include dir, +# find the Windows SDK root dir corresponding to it, or NOTFOUND if unrecognized. +# +# get_windowssdk_library_dirs( ) - Find the architecture-appropriate +# library directories corresponding to the SDK directory you pass in (or NOTFOUND if none) +# +# get_windowssdk_library_dirs_multiple( ...) - Find the architecture-appropriate +# library directories corresponding to the SDK directories you pass in, in order, skipping those not found. NOTFOUND if none at all. +# Good for passing WINDOWSSDK_DIRS or WINDOWSSDK_DIRS to if you really just want a file and don't care where from. +# +# get_windowssdk_include_dirs( ) - Find the +# include directories corresponding to the SDK directory you pass in (or NOTFOUND if none) +# +# get_windowssdk_include_dirs_multiple( ...) - Find the +# include directories corresponding to the SDK directories you pass in, in order, skipping those not found. NOTFOUND if none at all. +# Good for passing WINDOWSSDK_DIRS or WINDOWSSDK_DIRS to if you really just want a file and don't care where from. +# +# Requires these CMake modules: +# FindPackageHandleStandardArgs (known included with CMake >=2.6.2) +# +# Original Author: +# 2012 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2012. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +set(_preferred_sdk_dirs) # pre-output +set(_win_sdk_dirs) # pre-output +set(_win_sdk_versanddirs) # pre-output +set(_win_sdk_buildsanddirs) # pre-output +set(_winsdk_vistaonly) # search parameters +set(_winsdk_kits) # search parameters + + +set(_WINDOWSSDK_ANNOUNCE OFF) +if(NOT WINDOWSSDK_FOUND AND (NOT WindowsSDK_FIND_QUIETLY)) + set(_WINDOWSSDK_ANNOUNCE ON) +endif() +macro(_winsdk_announce) + if(_WINSDK_ANNOUNCE) + message(STATUS ${ARGN}) + endif() +endmacro() + +set(_winsdk_win10vers + 10.0.14393.0 # Redstone aka Win10 1607 "Anniversary Update" + 10.0.10586.0 # TH2 aka Win10 1511 + 10.0.10240.0 # Win10 RTM + 10.0.10150.0 # just ucrt + 10.0.10056.0 +) + +if(WindowsSDK_FIND_COMPONENTS MATCHES "tools") + set(_WINDOWSSDK_IGNOREMSVC ON) + _winsdk_announce("Checking for tools from Windows/Platform SDKs...") +else() + set(_WINDOWSSDK_IGNOREMSVC OFF) + _winsdk_announce("Checking for Windows/Platform SDKs...") +endif() + +# Appends to the three main pre-output lists used only if the path exists +# and is not already in the list. +function(_winsdk_conditional_append _vername _build _path) + if(("${_path}" MATCHES "registry") OR (NOT EXISTS "${_path}")) + # Path invalid - do not add + return() + endif() + list(FIND _win_sdk_dirs "${_path}" _win_sdk_idx) + if(_win_sdk_idx GREATER -1) + # Path already in list - do not add + return() + endif() + _winsdk_announce( " - ${_vername}, Build ${_build} @ ${_path}") + # Not yet in the list, so we'll add it + list(APPEND _win_sdk_dirs "${_path}") + set(_win_sdk_dirs "${_win_sdk_dirs}" CACHE INTERNAL "" FORCE) + list(APPEND + _win_sdk_versanddirs + "${_vername}" + "${_path}") + set(_win_sdk_versanddirs "${_win_sdk_versanddirs}" CACHE INTERNAL "" FORCE) + list(APPEND + _win_sdk_buildsanddirs + "${_build}" + "${_path}") + set(_win_sdk_buildsanddirs "${_win_sdk_buildsanddirs}" CACHE INTERNAL "" FORCE) +endfunction() + +# Appends to the "preferred SDK" lists only if the path exists +function(_winsdk_conditional_append_preferred _info _path) + if(("${_path}" MATCHES "registry") OR (NOT EXISTS "${_path}")) + # Path invalid - do not add + return() + endif() + + get_filename_component(_path "${_path}" ABSOLUTE) + + list(FIND _win_sdk_preferred_sdk_dirs "${_path}" _win_sdk_idx) + if(_win_sdk_idx GREATER -1) + # Path already in list - do not add + return() + endif() + _winsdk_announce( " - Found \"preferred\" SDK ${_info} @ ${_path}") + # Not yet in the list, so we'll add it + list(APPEND _win_sdk_preferred_sdk_dirs "${_path}") + set(_win_sdk_preferred_sdk_dirs "${_win_sdk_dirs}" CACHE INTERNAL "" FORCE) + + # Just in case we somehow missed it: + _winsdk_conditional_append("${_info}" "" "${_path}") +endfunction() + +# Given a version like v7.0A, looks for an SDK in the registry under "Microsoft SDKs". +# If the given version might be in both HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows +# and HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots aka "Windows Kits", +# use this macro first, since these registry keys usually have more information. +# +# Pass a "default" build number as an extra argument in case we can't find it. +function(_winsdk_check_microsoft_sdks_registry _winsdkver) + set(SDKKEY "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\${_winsdkver}") + get_filename_component(_sdkdir + "[${SDKKEY};InstallationFolder]" + ABSOLUTE) + + set(_sdkname "Windows SDK ${_winsdkver}") + + # Default build number passed as extra argument + set(_build ${ARGN}) + # See if the registry holds a Microsoft-mutilated, err, designated, product name + # (just using get_filename_component to execute the registry lookup) + get_filename_component(_sdkproductname + "[${SDKKEY};ProductName]" + NAME) + if(NOT "${_sdkproductname}" MATCHES "registry") + # Got a product name + set(_sdkname "${_sdkname} (${_sdkproductname})") + endif() + + # try for a version to augment our name + # (just using get_filename_component to execute the registry lookup) + get_filename_component(_sdkver + "[${SDKKEY};ProductVersion]" + NAME) + if(NOT "${_sdkver}" MATCHES "registry" AND NOT MATCHES) + # Got a version + if(NOT "${_sdkver}" MATCHES "\\.\\.") + # and it's not an invalid one with two dots in it: + # use to override the default build + set(_build ${_sdkver}) + if(NOT "${_sdkname}" MATCHES "${_sdkver}") + # Got a version that's not already in the name, let's use it to improve our name. + set(_sdkname "${_sdkname} (${_sdkver})") + endif() + endif() + endif() + _winsdk_conditional_append("${_sdkname}" "${_build}" "${_sdkdir}") +endfunction() + +# Given a name for identification purposes, the build number, and a key (technically a "value name") +# corresponding to a Windows SDK packaged as a "Windows Kit", look for it +# in HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots +# Note that the key or "value name" tends to be something weird like KitsRoot81 - +# no easy way to predict, just have to observe them in the wild. +# Doesn't hurt to also try _winsdk_check_microsoft_sdks_registry for these: +# sometimes you get keys in both parts of the registry (in the wow64 portion especially), +# and the non-"Windows Kits" location is often more descriptive. +function(_winsdk_check_windows_kits_registry _winkit_name _winkit_build _winkit_key) + get_filename_component(_sdkdir + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots;${_winkit_key}]" + ABSOLUTE) + _winsdk_conditional_append("${_winkit_name}" "${_winkit_build}" "${_sdkdir}") +endfunction() + +# Given a name for identification purposes and the build number +# corresponding to a Windows 10 SDK packaged as a "Windows Kit", look for it +# in HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots +# Doesn't hurt to also try _winsdk_check_microsoft_sdks_registry for these: +# sometimes you get keys in both parts of the registry (in the wow64 portion especially), +# and the non-"Windows Kits" location is often more descriptive. +function(_winsdk_check_win10_kits _winkit_build) + get_filename_component(_sdkdir + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots;KitsRoot10]" + ABSOLUTE) + if(("${_sdkdir}" MATCHES "registry") OR (NOT EXISTS "${_sdkdir}")) + return() # not found + endif() + if(EXISTS "${_sdkdir}/Include/${_winkit_build}/um") + _winsdk_conditional_append("Windows Kits 10 (Build ${_winkit_build})" "${_winkit_build}" "${_sdkdir}") + endif() +endfunction() + +# Given a name for indentification purposes, the build number, and the associated package GUID, +# look in the registry under both HKLM and HKCU in \\SOFTWARE\\Microsoft\\MicrosoftSDK\\InstalledSDKs\\ +# for that guid and the SDK it points to. +function(_winsdk_check_platformsdk_registry _platformsdkname _build _platformsdkguid) + foreach(_winsdk_hive HKEY_LOCAL_MACHINE HKEY_CURRENT_USER) + get_filename_component(_sdkdir + "[${_winsdk_hive}\\SOFTWARE\\Microsoft\\MicrosoftSDK\\InstalledSDKs\\${_platformsdkguid};Install Dir]" + ABSOLUTE) + _winsdk_conditional_append("${_platformsdkname} (${_build})" "${_build}" "${_sdkdir}") + endforeach() +endfunction() + +### +# Detect toolchain information: to know whether it's OK to use Vista+ only SDKs +### +set(_winsdk_vistaonly_ok OFF) +if(MSVC AND NOT _WINDOWSSDK_IGNOREMSVC) + # VC 10 and older has broad target support + if(MSVC_VERSION LESS 1700) + # VC 11 by default targets Vista and later only, so we can add a few more SDKs that (might?) only work on vista+ + elseif("${CMAKE_VS_PLATFORM_TOOLSET}" MATCHES "_xp") + # This is the XP-compatible v110+ toolset + elseif("${CMAKE_VS_PLATFORM_TOOLSET}" STREQUAL "v100" OR "${CMAKE_VS_PLATFORM_TOOLSET}" STREQUAL "v90") + # This is the VS2010/VS2008 toolset + else() + # OK, we're VC11 or newer and not using a backlevel or XP-compatible toolset. + # These versions have no XP (and possibly Vista pre-SP1) support + set(_winsdk_vistaonly_ok ON) + if(_WINDOWSSDK_ANNOUNCE AND NOT _WINDOWSSDK_VISTAONLY_PESTERED) + set(_WINDOWSSDK_VISTAONLY_PESTERED ON CACHE INTERNAL "" FORCE) + message(STATUS "FindWindowsSDK: Detected Visual Studio 2012 or newer, not using the _xp toolset variant: including SDK versions that drop XP support in search!") + endif() + endif() +endif() +if(_WINDOWSSDK_IGNOREMSVC) + set(_winsdk_vistaonly_ok ON) +endif() + +### +# MSVC version checks - keeps messy conditionals in one place +# (messy because of _WINDOWSSDK_IGNOREMSVC) +### +set(_winsdk_msvc_greater_1200 OFF) +if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (MSVC_VERSION GREATER 1200))) + set(_winsdk_msvc_greater_1200 ON) +endif() +# Newer than VS .NET/VS Toolkit 2003 +set(_winsdk_msvc_greater_1310 OFF) +if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (MSVC_VERSION GREATER 1310))) + set(_winsdk_msvc_greater_1310 ON) +endif() + +# VS2005/2008 +set(_winsdk_msvc_less_1600 OFF) +if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (MSVC_VERSION LESS 1600))) + set(_winsdk_msvc_less_1600 ON) +endif() + +# VS2013+ +set(_winsdk_msvc_not_less_1800 OFF) +if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (NOT MSVC_VERSION LESS 1800))) + set(_winsdk_msvc_not_less_1800 ON) +endif() + +### +# START body of find module +### +if(_winsdk_msvc_greater_1310) # Newer than VS .NET/VS Toolkit 2003 + ### + # Look for "preferred" SDKs + ### + + # Environment variable for SDK dir + if(EXISTS "$ENV{WindowsSDKDir}" AND (NOT "$ENV{WindowsSDKDir}" STREQUAL "")) + _winsdk_conditional_append_preferred("WindowsSDKDir environment variable" "$ENV{WindowsSDKDir}") + endif() + + if(_winsdk_msvc_less_1600) + # Per-user current Windows SDK for VS2005/2008 + get_filename_component(_sdkdir + "[HKEY_CURRENT_USER\\Software\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]" + ABSOLUTE) + _winsdk_conditional_append_preferred("Per-user current Windows SDK" "${_sdkdir}") + + # System-wide current Windows SDK for VS2005/2008 + get_filename_component(_sdkdir + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]" + ABSOLUTE) + _winsdk_conditional_append_preferred("System-wide current Windows SDK" "${_sdkdir}") + endif() + + ### + # Begin the massive list of SDK searching! + ### + if(_winsdk_vistaonly_ok AND _winsdk_msvc_not_less_1800) + # These require at least Visual Studio 2013 (VC12) + + _winsdk_check_microsoft_sdks_registry(v10.0A) + + # Windows Software Development Kit (SDK) for Windows 10 + # Several different versions living in the same directory - if nothing else we can assume RTM (10240) + _winsdk_check_microsoft_sdks_registry(v10.0 10.0.10240.0) + foreach(_win10build ${_winsdk_win10vers}) + _winsdk_check_win10_kits(${_win10build}) + endforeach() + endif() # vista-only and 2013+ + + # Included in Visual Studio 2013 + # Includes the v120_xp toolset + _winsdk_check_microsoft_sdks_registry(v8.1A 8.1.51636) + + if(_winsdk_vistaonly_ok AND _winsdk_msvc_not_less_1800) + # Windows Software Development Kit (SDK) for Windows 8.1 + # http://msdn.microsoft.com/en-gb/windows/desktop/bg162891 + _winsdk_check_microsoft_sdks_registry(v8.1 8.1.25984.0) + _winsdk_check_windows_kits_registry("Windows Kits 8.1" 8.1.25984.0 KitsRoot81) + endif() # vista-only and 2013+ + + if(_winsdk_vistaonly_ok) + # Included in Visual Studio 2012 + _winsdk_check_microsoft_sdks_registry(v8.0A 8.0.50727) + + # Microsoft Windows SDK for Windows 8 and .NET Framework 4.5 + # This is the first version to also include the DirectX SDK + # http://msdn.microsoft.com/en-US/windows/desktop/hh852363.aspx + _winsdk_check_microsoft_sdks_registry(v8.0 6.2.9200.16384) + _winsdk_check_windows_kits_registry("Windows Kits 8.0" 6.2.9200.16384 KitsRoot) + endif() # vista-only + + # Included with VS 2012 Update 1 or later + # Introduces v110_xp toolset + _winsdk_check_microsoft_sdks_registry(v7.1A 7.1.51106) + if(_winsdk_vistaonly_ok) + # Microsoft Windows SDK for Windows 7 and .NET Framework 4 + # http://www.microsoft.com/downloads/en/details.aspx?FamilyID=6b6c21d2-2006-4afa-9702-529fa782d63b + _winsdk_check_microsoft_sdks_registry(v7.1 7.1.7600.0.30514) + endif() # vista-only + + # Included with VS 2010 + _winsdk_check_microsoft_sdks_registry(v7.0A 6.1.7600.16385) + + # Windows SDK for Windows 7 and .NET Framework 3.5 SP1 + # Works with VC9 + # http://www.microsoft.com/en-us/download/details.aspx?id=18950 + _winsdk_check_microsoft_sdks_registry(v7.0 6.1.7600.16385) + + # Two versions call themselves "v6.1": + # Older: + # Windows Vista Update & .NET 3.0 SDK + # http://www.microsoft.com/en-us/download/details.aspx?id=14477 + + # Newer: + # Windows Server 2008 & .NET 3.5 SDK + # may have broken VS9SP1? they recommend v7.0 instead, or a KB... + # http://www.microsoft.com/en-us/download/details.aspx?id=24826 + _winsdk_check_microsoft_sdks_registry(v6.1 6.1.6000.16384.10) + + # Included in VS 2008 + _winsdk_check_microsoft_sdks_registry(v6.0A 6.1.6723.1) + + # Microsoft Windows Software Development Kit for Windows Vista and .NET Framework 3.0 Runtime Components + # http://blogs.msdn.com/b/stanley/archive/2006/11/08/microsoft-windows-software-development-kit-for-windows-vista-and-net-framework-3-0-runtime-components.aspx + _winsdk_check_microsoft_sdks_registry(v6.0 6.0.6000.16384) +endif() + +# Let's not forget the Platform SDKs, which sometimes are useful! +if(_winsdk_msvc_greater_1200) + _winsdk_check_platformsdk_registry("Microsoft Platform SDK for Windows Server 2003 R2" "5.2.3790.2075.51" "D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1") + _winsdk_check_platformsdk_registry("Microsoft Platform SDK for Windows Server 2003 SP1" "5.2.3790.1830.15" "8F9E5EF3-A9A5-491B-A889-C58EFFECE8B3") +endif() +### +# Finally, look for "preferred" SDKs +### +if(_winsdk_msvc_greater_1310) # Newer than VS .NET/VS Toolkit 2003 + + + # Environment variable for SDK dir + if(EXISTS "$ENV{WindowsSDKDir}" AND (NOT "$ENV{WindowsSDKDir}" STREQUAL "")) + _winsdk_conditional_append_preferred("WindowsSDKDir environment variable" "$ENV{WindowsSDKDir}") + endif() + + if(_winsdk_msvc_less_1600) + # Per-user current Windows SDK for VS2005/2008 + get_filename_component(_sdkdir + "[HKEY_CURRENT_USER\\Software\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]" + ABSOLUTE) + _winsdk_conditional_append_preferred("Per-user current Windows SDK" "${_sdkdir}") + + # System-wide current Windows SDK for VS2005/2008 + get_filename_component(_sdkdir + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]" + ABSOLUTE) + _winsdk_conditional_append_preferred("System-wide current Windows SDK" "${_sdkdir}") + endif() +endif() + + +function(windowssdk_name_lookup _dir _outvar) + list(FIND _win_sdk_versanddirs "${_dir}" _diridx) + math(EXPR _idx "${_diridx} - 1") + if(${_idx} GREATER -1) + list(GET _win_sdk_versanddirs ${_idx} _ret) + else() + set(_ret "NOTFOUND") + endif() + set(${_outvar} "${_ret}" PARENT_SCOPE) +endfunction() + +function(windowssdk_build_lookup _dir _outvar) + list(FIND _win_sdk_buildsanddirs "${_dir}" _diridx) + math(EXPR _idx "${_diridx} - 1") + if(${_idx} GREATER -1) + list(GET _win_sdk_buildsanddirs ${_idx} _ret) + else() + set(_ret "NOTFOUND") + endif() + set(${_outvar} "${_ret}" PARENT_SCOPE) +endfunction() + +# If we found something... +if(_win_sdk_dirs) + list(GET _win_sdk_dirs 0 WINDOWSSDK_LATEST_DIR) + windowssdk_name_lookup("${WINDOWSSDK_LATEST_DIR}" + WINDOWSSDK_LATEST_NAME) + set(WINDOWSSDK_DIRS ${_win_sdk_dirs}) + + # Fallback, in case no preference found. + set(WINDOWSSDK_PREFERRED_DIR "${WINDOWSSDK_LATEST_DIR}") + set(WINDOWSSDK_PREFERRED_NAME "${WINDOWSSDK_LATEST_NAME}") + set(WINDOWSSDK_PREFERRED_FIRST_DIRS ${WINDOWSSDK_DIRS}) + set(WINDOWSSDK_FOUND_PREFERENCE OFF) +endif() + +# If we found indications of a user preference... +if(_win_sdk_preferred_sdk_dirs) + list(GET _win_sdk_preferred_sdk_dirs 0 WINDOWSSDK_PREFERRED_DIR) + windowssdk_name_lookup("${WINDOWSSDK_PREFERRED_DIR}" + WINDOWSSDK_PREFERRED_NAME) + set(WINDOWSSDK_PREFERRED_FIRST_DIRS + ${_win_sdk_preferred_sdk_dirs} + ${_win_sdk_dirs}) + list(REMOVE_DUPLICATES WINDOWSSDK_PREFERRED_FIRST_DIRS) + set(WINDOWSSDK_FOUND_PREFERENCE ON) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(WindowsSDK + "No compatible version of the Windows SDK or Platform SDK found." + WINDOWSSDK_DIRS) + +if(WINDOWSSDK_FOUND) + # Internal: Architecture-appropriate library directory names. + if("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM") + if(CMAKE_SIZEOF_VOID_P MATCHES "8") + # Only supported in Win10 SDK and up. + set(_winsdk_arch8 arm64) # what the WDK for Win8+ calls this architecture + else() + set(_winsdk_archbare /arm) # what the architecture used to be called in oldest SDKs + set(_winsdk_arch arm) # what the architecture used to be called + set(_winsdk_arch8 arm) # what the WDK for Win8+ calls this architecture + endif() + else() + if(CMAKE_SIZEOF_VOID_P MATCHES "8") + set(_winsdk_archbare /x64) # what the architecture used to be called in oldest SDKs + set(_winsdk_arch amd64) # what the architecture used to be called + set(_winsdk_arch8 x64) # what the WDK for Win8+ calls this architecture + else() + set(_winsdk_archbare ) # what the architecture used to be called in oldest SDKs + set(_winsdk_arch i386) # what the architecture used to be called + set(_winsdk_arch8 x86) # what the WDK for Win8+ calls this architecture + endif() + endif() + + function(get_windowssdk_from_component _component _var) + get_filename_component(_component "${_component}" ABSOLUTE) + file(TO_CMAKE_PATH "${_component}" _component) + foreach(_sdkdir ${WINDOWSSDK_DIRS}) + get_filename_component(_sdkdir "${_sdkdir}" ABSOLUTE) + string(LENGTH "${_sdkdir}" _sdklen) + file(RELATIVE_PATH _rel "${_sdkdir}" "${_component}") + # If we don't have any "parent directory" items... + if(NOT "${_rel}" MATCHES "[.][.]") + set(${_var} "${_sdkdir}" PARENT_SCOPE) + return() + endif() + endforeach() + # Fail. + set(${_var} "NOTFOUND" PARENT_SCOPE) + endfunction() + function(get_windowssdk_library_dirs _winsdk_dir _var) + set(_dirs) + set(_suffixes + "lib${_winsdk_archbare}" # SDKs like 7.1A + "lib/${_winsdk_arch}" # just because some SDKs have x86 dir and root dir + "lib/w2k/${_winsdk_arch}" # Win2k min requirement + "lib/wxp/${_winsdk_arch}" # WinXP min requirement + "lib/wnet/${_winsdk_arch}" # Win Server 2003 min requirement + "lib/wlh/${_winsdk_arch}" + "lib/wlh/um/${_winsdk_arch8}" # Win Vista ("Long Horn") min requirement + "lib/win7/${_winsdk_arch}" + "lib/win7/um/${_winsdk_arch8}" # Win 7 min requirement + ) + foreach(_ver + wlh # Win Vista ("Long Horn") min requirement + win7 # Win 7 min requirement + win8 # Win 8 min requirement + winv6.3 # Win 8.1 min requirement + ) + + list(APPEND _suffixes + "lib/${_ver}/${_winsdk_arch}" + "lib/${_ver}/um/${_winsdk_arch8}" + "lib/${_ver}/km/${_winsdk_arch8}" + ) + endforeach() + + # Look for WDF libraries in Win10+ SDK + foreach(_mode umdf kmdf) + file(GLOB _wdfdirs RELATIVE "${_winsdk_dir}" "${_winsdk_dir}/lib/wdf/${_mode}/${_winsdk_arch8}/*") + if(_wdfdirs) + list(APPEND _suffixes ${_wdfdirs}) + endif() + endforeach() + + # Look in each Win10+ SDK version for the components + foreach(_win10ver ${_winsdk_win10vers}) + foreach(_component um km ucrt mmos) + list(APPEND _suffixes "lib/${_win10ver}/${_component}/${_winsdk_arch8}") + endforeach() + endforeach() + + foreach(_suffix ${_suffixes}) + # Check to see if a library actually exists here. + file(GLOB _libs "${_winsdk_dir}/${_suffix}/*.lib") + if(_libs) + list(APPEND _dirs "${_winsdk_dir}/${_suffix}") + endif() + endforeach() + if("${_dirs}" STREQUAL "") + set(_dirs NOTFOUND) + else() + list(REMOVE_DUPLICATES _dirs) + endif() + set(${_var} ${_dirs} PARENT_SCOPE) + endfunction() + function(get_windowssdk_include_dirs _winsdk_dir _var) + set(_dirs) + + set(_subdirs shared um winrt km wdf mmos ucrt) + set(_suffixes Include) + + foreach(_dir ${_subdirs}) + list(APPEND _suffixes "Include/${_dir}") + endforeach() + + foreach(_ver ${_winsdk_win10vers}) + foreach(_dir ${_subdirs}) + list(APPEND _suffixes "Include/${_ver}/${_dir}") + endforeach() + endforeach() + + foreach(_suffix ${_suffixes}) + # Check to see if a header file actually exists here. + file(GLOB _headers "${_winsdk_dir}/${_suffix}/*.h") + if(_headers) + list(APPEND _dirs "${_winsdk_dir}/${_suffix}") + endif() + endforeach() + if("${_dirs}" STREQUAL "") + set(_dirs NOTFOUND) + else() + list(REMOVE_DUPLICATES _dirs) + endif() + set(${_var} ${_dirs} PARENT_SCOPE) + endfunction() + function(get_windowssdk_library_dirs_multiple _var) + set(_dirs) + foreach(_sdkdir ${ARGN}) + get_windowssdk_library_dirs("${_sdkdir}" _current_sdk_libdirs) + if(_current_sdk_libdirs) + list(APPEND _dirs ${_current_sdk_libdirs}) + endif() + endforeach() + if("${_dirs}" STREQUAL "") + set(_dirs NOTFOUND) + else() + list(REMOVE_DUPLICATES _dirs) + endif() + set(${_var} ${_dirs} PARENT_SCOPE) + endfunction() + function(get_windowssdk_include_dirs_multiple _var) + set(_dirs) + foreach(_sdkdir ${ARGN}) + get_windowssdk_include_dirs("${_sdkdir}" _current_sdk_incdirs) + if(_current_sdk_libdirs) + list(APPEND _dirs ${_current_sdk_incdirs}) + endif() + endforeach() + if("${_dirs}" STREQUAL "") + set(_dirs NOTFOUND) + else() + list(REMOVE_DUPLICATES _dirs) + endif() + set(${_var} ${_dirs} PARENT_SCOPE) + endfunction() +endif() diff --git a/Engine/lib/openal-soft/include/align.h b/Engine/lib/openal-soft/common/align.h similarity index 100% rename from Engine/lib/openal-soft/include/align.h rename to Engine/lib/openal-soft/common/align.h diff --git a/Engine/lib/openal-soft/common/almalloc.c b/Engine/lib/openal-soft/common/almalloc.c index 8c1c5794e..0d982ca19 100644 --- a/Engine/lib/openal-soft/common/almalloc.c +++ b/Engine/lib/openal-soft/common/almalloc.c @@ -10,8 +10,20 @@ #endif #ifdef HAVE_WINDOWS_H #include +#else +#include #endif + +#ifdef __GNUC__ +#define LIKELY(x) __builtin_expect(!!(x), !0) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define LIKELY(x) (!!(x)) +#define UNLIKELY(x) (!!(x)) +#endif + + void *al_malloc(size_t alignment, size_t size) { #if defined(HAVE_ALIGNED_ALLOC) @@ -60,3 +72,39 @@ void al_free(void *ptr) } #endif } + +size_t al_get_page_size(void) +{ + static size_t psize = 0; + if(UNLIKELY(!psize)) + { +#ifdef HAVE_SYSCONF +#if defined(_SC_PAGESIZE) + if(!psize) psize = sysconf(_SC_PAGESIZE); +#elif defined(_SC_PAGE_SIZE) + if(!psize) psize = sysconf(_SC_PAGE_SIZE); +#endif +#endif /* HAVE_SYSCONF */ +#ifdef _WIN32 + if(!psize) + { + SYSTEM_INFO sysinfo; + memset(&sysinfo, 0, sizeof(sysinfo)); + + GetSystemInfo(&sysinfo); + psize = sysinfo.dwPageSize; + } +#endif + if(!psize) psize = DEF_ALIGN; + } + return psize; +} + +int al_is_sane_alignment_allocator(void) +{ +#if defined(HAVE_ALIGNED_ALLOC) || defined(HAVE_POSIX_MEMALIGN) || defined(HAVE__ALIGNED_MALLOC) + return 1; +#else + return 0; +#endif +} diff --git a/Engine/lib/openal-soft/common/almalloc.h b/Engine/lib/openal-soft/common/almalloc.h new file mode 100644 index 000000000..a4297cf50 --- /dev/null +++ b/Engine/lib/openal-soft/common/almalloc.h @@ -0,0 +1,30 @@ +#ifndef AL_MALLOC_H +#define AL_MALLOC_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Minimum alignment required by posix_memalign. */ +#define DEF_ALIGN sizeof(void*) + +void *al_malloc(size_t alignment, size_t size); +void *al_calloc(size_t alignment, size_t size); +void al_free(void *ptr); + +size_t al_get_page_size(void); + +/** + * Returns non-0 if the allocation function has direct alignment handling. + * Otherwise, the standard malloc is used with an over-allocation and pointer + * offset strategy. + */ +int al_is_sane_alignment_allocator(void); + +#ifdef __cplusplus +} +#endif + +#endif /* AL_MALLOC_H */ diff --git a/Engine/lib/openal-soft/common/atomic.h b/Engine/lib/openal-soft/common/atomic.h new file mode 100644 index 000000000..39daa1dc4 --- /dev/null +++ b/Engine/lib/openal-soft/common/atomic.h @@ -0,0 +1,439 @@ +#ifndef AL_ATOMIC_H +#define AL_ATOMIC_H + +#include "static_assert.h" +#include "bool.h" + +#ifdef __GNUC__ +/* This helps cast away the const-ness of a pointer without accidentally + * changing the pointer type. This is necessary due to Clang's inability to use + * atomic_load on a const _Atomic variable. + */ +#define CONST_CAST(T, V) __extension__({ \ + const T _tmp = (V); \ + (T)_tmp; \ +}) +#else +#define CONST_CAST(T, V) ((T)(V)) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Atomics using C11 */ +#ifdef HAVE_C11_ATOMIC + +#include + +#define almemory_order memory_order +#define almemory_order_relaxed memory_order_relaxed +#define almemory_order_consume memory_order_consume +#define almemory_order_acquire memory_order_acquire +#define almemory_order_release memory_order_release +#define almemory_order_acq_rel memory_order_acq_rel +#define almemory_order_seq_cst memory_order_seq_cst + +#define ATOMIC(T) T _Atomic +#define ATOMIC_FLAG atomic_flag + +#define ATOMIC_INIT atomic_init +#define ATOMIC_INIT_STATIC ATOMIC_VAR_INIT +/*#define ATOMIC_FLAG_INIT ATOMIC_FLAG_INIT*/ + +#define ATOMIC_LOAD atomic_load_explicit +#define ATOMIC_STORE atomic_store_explicit + +#define ATOMIC_ADD atomic_fetch_add_explicit +#define ATOMIC_SUB atomic_fetch_sub_explicit + +#define ATOMIC_EXCHANGE atomic_exchange_explicit +#define ATOMIC_COMPARE_EXCHANGE_STRONG atomic_compare_exchange_strong_explicit +#define ATOMIC_COMPARE_EXCHANGE_WEAK atomic_compare_exchange_weak_explicit + +#define ATOMIC_FLAG_TEST_AND_SET atomic_flag_test_and_set_explicit +#define ATOMIC_FLAG_CLEAR atomic_flag_clear_explicit + +#define ATOMIC_THREAD_FENCE atomic_thread_fence + +/* Atomics using GCC intrinsics */ +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && !defined(__QNXNTO__) + +enum almemory_order { + almemory_order_relaxed, + almemory_order_consume, + almemory_order_acquire, + almemory_order_release, + almemory_order_acq_rel, + almemory_order_seq_cst +}; + +#define ATOMIC(T) struct { T volatile value; } +#define ATOMIC_FLAG ATOMIC(int) + +#define ATOMIC_INIT(_val, _newval) do { (_val)->value = (_newval); } while(0) +#define ATOMIC_INIT_STATIC(_newval) {(_newval)} +#define ATOMIC_FLAG_INIT ATOMIC_INIT_STATIC(0) + +#define ATOMIC_LOAD(_val, _MO) __extension__({ \ + __typeof((_val)->value) _r = (_val)->value; \ + __asm__ __volatile__("" ::: "memory"); \ + _r; \ +}) +#define ATOMIC_STORE(_val, _newval, _MO) do { \ + __asm__ __volatile__("" ::: "memory"); \ + (_val)->value = (_newval); \ +} while(0) + +#define ATOMIC_ADD(_val, _incr, _MO) __sync_fetch_and_add(&(_val)->value, (_incr)) +#define ATOMIC_SUB(_val, _decr, _MO) __sync_fetch_and_sub(&(_val)->value, (_decr)) + +#define ATOMIC_EXCHANGE(_val, _newval, _MO) __extension__({ \ + __asm__ __volatile__("" ::: "memory"); \ + __sync_lock_test_and_set(&(_val)->value, (_newval)); \ +}) +#define ATOMIC_COMPARE_EXCHANGE_STRONG(_val, _oldval, _newval, _MO1, _MO2) __extension__({ \ + __typeof(*(_oldval)) _o = *(_oldval); \ + *(_oldval) = __sync_val_compare_and_swap(&(_val)->value, _o, (_newval)); \ + *(_oldval) == _o; \ +}) + +#define ATOMIC_FLAG_TEST_AND_SET(_val, _MO) __extension__({ \ + __asm__ __volatile__("" ::: "memory"); \ + __sync_lock_test_and_set(&(_val)->value, 1); \ +}) +#define ATOMIC_FLAG_CLEAR(_val, _MO) __extension__({ \ + __sync_lock_release(&(_val)->value); \ + __asm__ __volatile__("" ::: "memory"); \ +}) + + +#define ATOMIC_THREAD_FENCE(order) do { \ + enum { must_be_constant = (order) }; \ + const int _o = must_be_constant; \ + if(_o > almemory_order_relaxed) \ + __asm__ __volatile__("" ::: "memory"); \ +} while(0) + +/* Atomics using x86/x86-64 GCC inline assembly */ +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + +#define WRAP_ADD(S, ret, dest, incr) __asm__ __volatile__( \ + "lock; xadd"S" %0,(%1)" \ + : "=r" (ret) \ + : "r" (dest), "0" (incr) \ + : "memory" \ +) +#define WRAP_SUB(S, ret, dest, decr) __asm__ __volatile__( \ + "lock; xadd"S" %0,(%1)" \ + : "=r" (ret) \ + : "r" (dest), "0" (-(decr)) \ + : "memory" \ +) + +#define WRAP_XCHG(S, ret, dest, newval) __asm__ __volatile__( \ + "lock; xchg"S" %0,(%1)" \ + : "=r" (ret) \ + : "r" (dest), "0" (newval) \ + : "memory" \ +) +#define WRAP_CMPXCHG(S, ret, dest, oldval, newval) __asm__ __volatile__( \ + "lock; cmpxchg"S" %2,(%1)" \ + : "=a" (ret) \ + : "r" (dest), "r" (newval), "0" (oldval) \ + : "memory" \ +) + + +enum almemory_order { + almemory_order_relaxed, + almemory_order_consume, + almemory_order_acquire, + almemory_order_release, + almemory_order_acq_rel, + almemory_order_seq_cst +}; + +#define ATOMIC(T) struct { T volatile value; } + +#define ATOMIC_INIT(_val, _newval) do { (_val)->value = (_newval); } while(0) +#define ATOMIC_INIT_STATIC(_newval) {(_newval)} + +#define ATOMIC_LOAD(_val, _MO) __extension__({ \ + __typeof((_val)->value) _r = (_val)->value; \ + __asm__ __volatile__("" ::: "memory"); \ + _r; \ +}) +#define ATOMIC_STORE(_val, _newval, _MO) do { \ + __asm__ __volatile__("" ::: "memory"); \ + (_val)->value = (_newval); \ +} while(0) + +#define ATOMIC_ADD(_val, _incr, _MO) __extension__({ \ + static_assert(sizeof((_val)->value)==4 || sizeof((_val)->value)==8, "Unsupported size!"); \ + __typeof((_val)->value) _r; \ + if(sizeof((_val)->value) == 4) WRAP_ADD("l", _r, &(_val)->value, _incr); \ + else if(sizeof((_val)->value) == 8) WRAP_ADD("q", _r, &(_val)->value, _incr); \ + _r; \ +}) +#define ATOMIC_SUB(_val, _decr, _MO) __extension__({ \ + static_assert(sizeof((_val)->value)==4 || sizeof((_val)->value)==8, "Unsupported size!"); \ + __typeof((_val)->value) _r; \ + if(sizeof((_val)->value) == 4) WRAP_SUB("l", _r, &(_val)->value, _decr); \ + else if(sizeof((_val)->value) == 8) WRAP_SUB("q", _r, &(_val)->value, _decr); \ + _r; \ +}) + +#define ATOMIC_EXCHANGE(_val, _newval, _MO) __extension__({ \ + __typeof((_val)->value) _r; \ + if(sizeof((_val)->value) == 4) WRAP_XCHG("l", _r, &(_val)->value, (_newval)); \ + else if(sizeof((_val)->value) == 8) WRAP_XCHG("q", _r, &(_val)->value, (_newval)); \ + _r; \ +}) +#define ATOMIC_COMPARE_EXCHANGE_STRONG(_val, _oldval, _newval, _MO1, _MO2) __extension__({ \ + __typeof(*(_oldval)) _old = *(_oldval); \ + if(sizeof((_val)->value) == 4) WRAP_CMPXCHG("l", *(_oldval), &(_val)->value, _old, (_newval)); \ + else if(sizeof((_val)->value) == 8) WRAP_CMPXCHG("q", *(_oldval), &(_val)->value, _old, (_newval)); \ + *(_oldval) == _old; \ +}) + +#define ATOMIC_EXCHANGE_PTR(_val, _newval, _MO) __extension__({ \ + void *_r; \ + if(sizeof(void*) == 4) WRAP_XCHG("l", _r, &(_val)->value, (_newval)); \ + else if(sizeof(void*) == 8) WRAP_XCHG("q", _r, &(_val)->value, (_newval));\ + _r; \ +}) +#define ATOMIC_COMPARE_EXCHANGE_PTR_STRONG(_val, _oldval, _newval, _MO1, _MO2) __extension__({ \ + void *_old = *(_oldval); \ + if(sizeof(void*) == 4) WRAP_CMPXCHG("l", *(_oldval), &(_val)->value, _old, (_newval)); \ + else if(sizeof(void*) == 8) WRAP_CMPXCHG("q", *(_oldval), &(_val)->value, _old, (_newval)); \ + *(_oldval) == _old; \ +}) + +#define ATOMIC_THREAD_FENCE(order) do { \ + enum { must_be_constant = (order) }; \ + const int _o = must_be_constant; \ + if(_o > almemory_order_relaxed) \ + __asm__ __volatile__("" ::: "memory"); \ +} while(0) + +/* Atomics using Windows methods */ +#elif defined(_WIN32) + +#define WIN32_LEAN_AND_MEAN +#include + +/* NOTE: This mess is *extremely* touchy. It lacks quite a bit of safety + * checking due to the lack of multi-statement expressions, typeof(), and C99 + * compound literals. It is incapable of properly exchanging floats, which get + * casted to LONG/int, and could cast away potential warnings. + * + * Unfortunately, it's the only semi-safe way that doesn't rely on C99 (because + * MSVC). + */ + +inline LONG AtomicAdd32(volatile LONG *dest, LONG incr) +{ + return InterlockedExchangeAdd(dest, incr); +} +inline LONGLONG AtomicAdd64(volatile LONGLONG *dest, LONGLONG incr) +{ + return InterlockedExchangeAdd64(dest, incr); +} +inline LONG AtomicSub32(volatile LONG *dest, LONG decr) +{ + return InterlockedExchangeAdd(dest, -decr); +} +inline LONGLONG AtomicSub64(volatile LONGLONG *dest, LONGLONG decr) +{ + return InterlockedExchangeAdd64(dest, -decr); +} + +inline LONG AtomicSwap32(volatile LONG *dest, LONG newval) +{ + return InterlockedExchange(dest, newval); +} +inline LONGLONG AtomicSwap64(volatile LONGLONG *dest, LONGLONG newval) +{ + return InterlockedExchange64(dest, newval); +} +inline void *AtomicSwapPtr(void *volatile *dest, void *newval) +{ + return InterlockedExchangePointer(dest, newval); +} + +inline bool CompareAndSwap32(volatile LONG *dest, LONG newval, LONG *oldval) +{ + LONG old = *oldval; + *oldval = InterlockedCompareExchange(dest, newval, *oldval); + return old == *oldval; +} +inline bool CompareAndSwap64(volatile LONGLONG *dest, LONGLONG newval, LONGLONG *oldval) +{ + LONGLONG old = *oldval; + *oldval = InterlockedCompareExchange64(dest, newval, *oldval); + return old == *oldval; +} +inline bool CompareAndSwapPtr(void *volatile *dest, void *newval, void **oldval) +{ + void *old = *oldval; + *oldval = InterlockedCompareExchangePointer(dest, newval, *oldval); + return old == *oldval; +} + +#define WRAP_ADDSUB(T, _func, _ptr, _amnt) _func((T volatile*)(_ptr), (_amnt)) +#define WRAP_XCHG(T, _func, _ptr, _newval) _func((T volatile*)(_ptr), (_newval)) +#define WRAP_CMPXCHG(T, _func, _ptr, _newval, _oldval) _func((T volatile*)(_ptr), (_newval), (T*)(_oldval)) + + +enum almemory_order { + almemory_order_relaxed, + almemory_order_consume, + almemory_order_acquire, + almemory_order_release, + almemory_order_acq_rel, + almemory_order_seq_cst +}; + +#define ATOMIC(T) struct { T volatile value; } + +#define ATOMIC_INIT(_val, _newval) do { (_val)->value = (_newval); } while(0) +#define ATOMIC_INIT_STATIC(_newval) {(_newval)} + +#define ATOMIC_LOAD(_val, _MO) ((_val)->value) +#define ATOMIC_STORE(_val, _newval, _MO) do { \ + (_val)->value = (_newval); \ +} while(0) + +int _al_invalid_atomic_size(); /* not defined */ +void *_al_invalid_atomic_ptr_size(); /* not defined */ + +#define ATOMIC_ADD(_val, _incr, _MO) \ + ((sizeof((_val)->value)==4) ? WRAP_ADDSUB(LONG, AtomicAdd32, &(_val)->value, (_incr)) : \ + (sizeof((_val)->value)==8) ? WRAP_ADDSUB(LONGLONG, AtomicAdd64, &(_val)->value, (_incr)) : \ + _al_invalid_atomic_size()) +#define ATOMIC_SUB(_val, _decr, _MO) \ + ((sizeof((_val)->value)==4) ? WRAP_ADDSUB(LONG, AtomicSub32, &(_val)->value, (_decr)) : \ + (sizeof((_val)->value)==8) ? WRAP_ADDSUB(LONGLONG, AtomicSub64, &(_val)->value, (_decr)) : \ + _al_invalid_atomic_size()) + +#define ATOMIC_EXCHANGE(_val, _newval, _MO) \ + ((sizeof((_val)->value)==4) ? WRAP_XCHG(LONG, AtomicSwap32, &(_val)->value, (_newval)) : \ + (sizeof((_val)->value)==8) ? WRAP_XCHG(LONGLONG, AtomicSwap64, &(_val)->value, (_newval)) : \ + (LONG)_al_invalid_atomic_size()) +#define ATOMIC_COMPARE_EXCHANGE_STRONG(_val, _oldval, _newval, _MO1, _MO2) \ + ((sizeof((_val)->value)==4) ? WRAP_CMPXCHG(LONG, CompareAndSwap32, &(_val)->value, (_newval), (_oldval)) : \ + (sizeof((_val)->value)==8) ? WRAP_CMPXCHG(LONGLONG, CompareAndSwap64, &(_val)->value, (_newval), (_oldval)) : \ + (bool)_al_invalid_atomic_size()) + +#define ATOMIC_EXCHANGE_PTR(_val, _newval, _MO) \ + ((sizeof((_val)->value)==sizeof(void*)) ? AtomicSwapPtr((void*volatile*)&(_val)->value, (_newval)) : \ + _al_invalid_atomic_ptr_size()) +#define ATOMIC_COMPARE_EXCHANGE_PTR_STRONG(_val, _oldval, _newval, _MO1, _MO2)\ + ((sizeof((_val)->value)==sizeof(void*)) ? CompareAndSwapPtr((void*volatile*)&(_val)->value, (_newval), (void**)(_oldval)) : \ + (bool)_al_invalid_atomic_size()) + +#define ATOMIC_THREAD_FENCE(order) do { \ + enum { must_be_constant = (order) }; \ + const int _o = must_be_constant; \ + if(_o > almemory_order_relaxed) \ + _ReadWriteBarrier(); \ +} while(0) + +#else + +#error "No atomic functions available on this platform!" + +#define ATOMIC(T) T + +#define ATOMIC_INIT(_val, _newval) ((void)0) +#define ATOMIC_INIT_STATIC(_newval) (0) + +#define ATOMIC_LOAD(...) (0) +#define ATOMIC_STORE(...) ((void)0) + +#define ATOMIC_ADD(...) (0) +#define ATOMIC_SUB(...) (0) + +#define ATOMIC_EXCHANGE(...) (0) +#define ATOMIC_COMPARE_EXCHANGE_STRONG(...) (0) + +#define ATOMIC_THREAD_FENCE(...) ((void)0) +#endif + +/* If no PTR xchg variants are provided, the normal ones can handle it. */ +#ifndef ATOMIC_EXCHANGE_PTR +#define ATOMIC_EXCHANGE_PTR ATOMIC_EXCHANGE +#define ATOMIC_COMPARE_EXCHANGE_PTR_STRONG ATOMIC_COMPARE_EXCHANGE_STRONG +#define ATOMIC_COMPARE_EXCHANGE_PTR_WEAK ATOMIC_COMPARE_EXCHANGE_WEAK +#endif + +/* If no weak cmpxchg is provided (not all systems will have one), substitute a + * strong cmpxchg. */ +#ifndef ATOMIC_COMPARE_EXCHANGE_WEAK +#define ATOMIC_COMPARE_EXCHANGE_WEAK ATOMIC_COMPARE_EXCHANGE_STRONG +#endif +#ifndef ATOMIC_COMPARE_EXCHANGE_PTR_WEAK +#define ATOMIC_COMPARE_EXCHANGE_PTR_WEAK ATOMIC_COMPARE_EXCHANGE_PTR_STRONG +#endif + +/* If no ATOMIC_FLAG is defined, simulate one with an atomic int using exchange + * and store ops. + */ +#ifndef ATOMIC_FLAG +#define ATOMIC_FLAG ATOMIC(int) +#define ATOMIC_FLAG_INIT ATOMIC_INIT_STATIC(0) +#define ATOMIC_FLAG_TEST_AND_SET(_val, _MO) ATOMIC_EXCHANGE(_val, 1, _MO) +#define ATOMIC_FLAG_CLEAR(_val, _MO) ATOMIC_STORE(_val, 0, _MO) +#endif + + +#define ATOMIC_LOAD_SEQ(_val) ATOMIC_LOAD(_val, almemory_order_seq_cst) +#define ATOMIC_STORE_SEQ(_val, _newval) ATOMIC_STORE(_val, _newval, almemory_order_seq_cst) + +#define ATOMIC_ADD_SEQ(_val, _incr) ATOMIC_ADD(_val, _incr, almemory_order_seq_cst) +#define ATOMIC_SUB_SEQ(_val, _decr) ATOMIC_SUB(_val, _decr, almemory_order_seq_cst) + +#define ATOMIC_EXCHANGE_SEQ(_val, _newval) ATOMIC_EXCHANGE(_val, _newval, almemory_order_seq_cst) +#define ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(_val, _oldval, _newval) \ + ATOMIC_COMPARE_EXCHANGE_STRONG(_val, _oldval, _newval, almemory_order_seq_cst, almemory_order_seq_cst) +#define ATOMIC_COMPARE_EXCHANGE_WEAK_SEQ(_val, _oldval, _newval) \ + ATOMIC_COMPARE_EXCHANGE_WEAK(_val, _oldval, _newval, almemory_order_seq_cst, almemory_order_seq_cst) + +#define ATOMIC_EXCHANGE_PTR_SEQ(_val, _newval) ATOMIC_EXCHANGE_PTR(_val, _newval, almemory_order_seq_cst) +#define ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(_val, _oldval, _newval) \ + ATOMIC_COMPARE_EXCHANGE_PTR_STRONG(_val, _oldval, _newval, almemory_order_seq_cst, almemory_order_seq_cst) +#define ATOMIC_COMPARE_EXCHANGE_PTR_WEAK_SEQ(_val, _oldval, _newval) \ + ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(_val, _oldval, _newval, almemory_order_seq_cst, almemory_order_seq_cst) + + +typedef unsigned int uint; +typedef ATOMIC(uint) RefCount; + +inline void InitRef(RefCount *ptr, uint value) +{ ATOMIC_INIT(ptr, value); } +inline uint ReadRef(RefCount *ptr) +{ return ATOMIC_LOAD(ptr, almemory_order_acquire); } +inline uint IncrementRef(RefCount *ptr) +{ return ATOMIC_ADD(ptr, 1, almemory_order_acq_rel)+1; } +inline uint DecrementRef(RefCount *ptr) +{ return ATOMIC_SUB(ptr, 1, almemory_order_acq_rel)-1; } + + +/* WARNING: A livelock is theoretically possible if another thread keeps + * changing the head without giving this a chance to actually swap in the new + * one (practically impossible with this little code, but...). + */ +#define ATOMIC_REPLACE_HEAD(T, _head, _entry) do { \ + T _first = ATOMIC_LOAD(_head, almemory_order_acquire); \ + do { \ + ATOMIC_STORE(&(_entry)->next, _first, almemory_order_relaxed); \ + } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(_head, &_first, _entry, \ + almemory_order_acq_rel, almemory_order_acquire) == 0); \ +} while(0) + +#ifdef __cplusplus +} +#endif + +#endif /* AL_ATOMIC_H */ diff --git a/Engine/lib/openal-soft/include/bool.h b/Engine/lib/openal-soft/common/bool.h similarity index 100% rename from Engine/lib/openal-soft/include/bool.h rename to Engine/lib/openal-soft/common/bool.h diff --git a/Engine/lib/openal-soft/common/math_defs.h b/Engine/lib/openal-soft/common/math_defs.h new file mode 100644 index 000000000..8ce93d0a9 --- /dev/null +++ b/Engine/lib/openal-soft/common/math_defs.h @@ -0,0 +1,46 @@ +#ifndef AL_MATH_DEFS_H +#define AL_MATH_DEFS_H + +#include +#ifdef HAVE_FLOAT_H +#include +#endif + +#ifndef M_PI +#define M_PI (3.14159265358979323846) +#endif + +#define F_PI (3.14159265358979323846f) +#define F_PI_2 (1.57079632679489661923f) +#define F_TAU (6.28318530717958647692f) + +#ifndef FLT_EPSILON +#define FLT_EPSILON (1.19209290e-07f) +#endif + +#ifndef HUGE_VALF +static const union msvc_inf_hack { + unsigned char b[4]; + float f; +} msvc_inf_union = {{ 0x00, 0x00, 0x80, 0x7F }}; +#define HUGE_VALF (msvc_inf_union.f) +#endif + +#ifndef HAVE_LOG2F +static inline float log2f(float f) +{ + return logf(f) / logf(2.0f); +} +#endif + +#ifndef HAVE_CBRTF +static inline float cbrtf(float f) +{ + return powf(f, 1.0f/3.0f); +} +#endif + +#define DEG2RAD(x) ((float)(x) * (F_PI/180.0f)) +#define RAD2DEG(x) ((float)(x) * (180.0f/F_PI)) + +#endif /* AL_MATH_DEFS_H */ diff --git a/Engine/lib/openal-soft/common/rwlock.c b/Engine/lib/openal-soft/common/rwlock.c index cfa3aee4e..67cf3acf0 100644 --- a/Engine/lib/openal-soft/common/rwlock.c +++ b/Engine/lib/openal-soft/common/rwlock.c @@ -11,26 +11,27 @@ /* A simple spinlock. Yield the thread while the given integer is set by * another. Could probably be improved... */ #define LOCK(l) do { \ - while(ATOMIC_EXCHANGE(int, &(l), true) == true) \ + while(ATOMIC_FLAG_TEST_AND_SET(&(l), almemory_order_acq_rel) == true) \ althrd_yield(); \ } while(0) -#define UNLOCK(l) ATOMIC_STORE(&(l), false) +#define UNLOCK(l) ATOMIC_FLAG_CLEAR(&(l), almemory_order_release) void RWLockInit(RWLock *lock) { InitRef(&lock->read_count, 0); InitRef(&lock->write_count, 0); - ATOMIC_INIT(&lock->read_lock, false); - ATOMIC_INIT(&lock->read_entry_lock, false); - ATOMIC_INIT(&lock->write_lock, false); + ATOMIC_FLAG_CLEAR(&lock->read_lock, almemory_order_relaxed); + ATOMIC_FLAG_CLEAR(&lock->read_entry_lock, almemory_order_relaxed); + ATOMIC_FLAG_CLEAR(&lock->write_lock, almemory_order_relaxed); } void ReadLock(RWLock *lock) { LOCK(lock->read_entry_lock); LOCK(lock->read_lock); - if(IncrementRef(&lock->read_count) == 1) + /* NOTE: ATOMIC_ADD returns the *old* value! */ + if(ATOMIC_ADD(&lock->read_count, 1, almemory_order_acq_rel) == 0) LOCK(lock->write_lock); UNLOCK(lock->read_lock); UNLOCK(lock->read_entry_lock); @@ -38,13 +39,14 @@ void ReadLock(RWLock *lock) void ReadUnlock(RWLock *lock) { - if(DecrementRef(&lock->read_count) == 0) + /* NOTE: ATOMIC_SUB returns the *old* value! */ + if(ATOMIC_SUB(&lock->read_count, 1, almemory_order_acq_rel) == 1) UNLOCK(lock->write_lock); } void WriteLock(RWLock *lock) { - if(IncrementRef(&lock->write_count) == 1) + if(ATOMIC_ADD(&lock->write_count, 1, almemory_order_acq_rel) == 0) LOCK(lock->read_lock); LOCK(lock->write_lock); } @@ -52,6 +54,6 @@ void WriteLock(RWLock *lock) void WriteUnlock(RWLock *lock) { UNLOCK(lock->write_lock); - if(DecrementRef(&lock->write_count) == 0) + if(ATOMIC_SUB(&lock->write_count, 1, almemory_order_acq_rel) == 1) UNLOCK(lock->read_lock); } diff --git a/Engine/lib/openal-soft/include/rwlock.h b/Engine/lib/openal-soft/common/rwlock.h similarity index 67% rename from Engine/lib/openal-soft/include/rwlock.h rename to Engine/lib/openal-soft/common/rwlock.h index 158a06708..8e36fa1a7 100644 --- a/Engine/lib/openal-soft/include/rwlock.h +++ b/Engine/lib/openal-soft/common/rwlock.h @@ -11,13 +11,12 @@ extern "C" { typedef struct { RefCount read_count; RefCount write_count; - ATOMIC(int) read_lock; - ATOMIC(int) read_entry_lock; - ATOMIC(int) write_lock; + ATOMIC_FLAG read_lock; + ATOMIC_FLAG read_entry_lock; + ATOMIC_FLAG write_lock; } RWLock; #define RWLOCK_STATIC_INITIALIZE { ATOMIC_INIT_STATIC(0), ATOMIC_INIT_STATIC(0), \ - ATOMIC_INIT_STATIC(false), ATOMIC_INIT_STATIC(false), \ - ATOMIC_INIT_STATIC(false) } + ATOMIC_FLAG_INIT, ATOMIC_FLAG_INIT, ATOMIC_FLAG_INIT } void RWLockInit(RWLock *lock); void ReadLock(RWLock *lock); diff --git a/Engine/lib/openal-soft/include/static_assert.h b/Engine/lib/openal-soft/common/static_assert.h similarity index 100% rename from Engine/lib/openal-soft/include/static_assert.h rename to Engine/lib/openal-soft/common/static_assert.h diff --git a/Engine/lib/openal-soft/common/threads.c b/Engine/lib/openal-soft/common/threads.c index 0a019d036..2655a2445 100644 --- a/Engine/lib/openal-soft/common/threads.c +++ b/Engine/lib/openal-soft/common/threads.c @@ -64,6 +64,22 @@ extern inline int altss_set(altss_t tss_id, void *val); #include +/* An associative map of uint:void* pairs. The key is the unique Thread ID and + * the value is the thread HANDLE. The thread ID is passed around as the + * althrd_t since there is only one ID per thread, whereas a thread may be + * referenced by multiple different HANDLEs. This map allows retrieving the + * original handle which is needed to join the thread and get its return value. + */ +static UIntMap ThrdIdHandle = UINTMAP_STATIC_INITIALIZE; + +/* An associative map of uint:void* pairs. The key is the TLS index (given by + * TlsAlloc), and the value is the altss_dtor_t callback. When a thread exits, + * we iterate over the TLS indices for their thread-local value and call the + * destructor function with it if they're both not NULL. + */ +static UIntMap TlsDestructors = UINTMAP_STATIC_INITIALIZE; + + void althrd_setname(althrd_t thr, const char *name) { #if defined(_MSC_VER) @@ -94,23 +110,6 @@ void althrd_setname(althrd_t thr, const char *name) } -static UIntMap ThrdIdHandle = UINTMAP_STATIC_INITIALIZE; - -static void NTAPI althrd_callback(void* UNUSED(handle), DWORD reason, void* UNUSED(reserved)) -{ - if(reason == DLL_PROCESS_DETACH) - ResetUIntMap(&ThrdIdHandle); -} -#ifdef _MSC_VER -#pragma section(".CRT$XLC",read) -__declspec(allocate(".CRT$XLC")) PIMAGE_TLS_CALLBACK althrd_callback_ = althrd_callback; -#elif defined(__GNUC__) -PIMAGE_TLS_CALLBACK althrd_callback_ __attribute__((section(".CRT$XLC"))) = althrd_callback; -#else -PIMAGE_TLS_CALLBACK althrd_callback_ = althrd_callback; -#endif - - typedef struct thread_cntr { althrd_start_t func; void *arg; @@ -208,12 +207,6 @@ void almtx_destroy(almtx_t *mtx) DeleteCriticalSection(mtx); } -int almtx_timedlock(almtx_t* UNUSED(mtx), const struct timespec* UNUSED(ts)) -{ - /* Windows CRITICAL_SECTIONs don't seem to have a timedlock method. */ - return althrd_error; -} - #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 int alcnd_init(alcnd_t *cond) { @@ -240,30 +233,6 @@ int alcnd_wait(alcnd_t *cond, almtx_t *mtx) return althrd_error; } -int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_point) -{ - struct timespec curtime; - DWORD sleeptime; - - if(altimespec_get(&curtime, AL_TIME_UTC) != AL_TIME_UTC) - return althrd_error; - - if(curtime.tv_sec > time_point->tv_sec || (curtime.tv_sec == time_point->tv_sec && - curtime.tv_nsec >= time_point->tv_nsec)) - { - if(SleepConditionVariableCS(cond, mtx, 0) != 0) - return althrd_success; - } - else - { - sleeptime = (time_point->tv_nsec - curtime.tv_nsec + 999999)/1000000; - sleeptime += (time_point->tv_sec - curtime.tv_sec)*1000; - if(SleepConditionVariableCS(cond, mtx, sleeptime) != 0) - return althrd_success; - } - return (GetLastError()==ERROR_TIMEOUT) ? althrd_timedout : althrd_error; -} - void alcnd_destroy(alcnd_t* UNUSED(cond)) { /* Nothing to delete? */ @@ -348,37 +317,6 @@ int alcnd_wait(alcnd_t *cond, almtx_t *mtx) return althrd_success; } -int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_point) -{ - _int_alcnd_t *icond = cond->Ptr; - struct timespec curtime; - DWORD sleeptime; - int res; - - if(altimespec_get(&curtime, AL_TIME_UTC) != AL_TIME_UTC) - return althrd_error; - - if(curtime.tv_sec > time_point->tv_sec || (curtime.tv_sec == time_point->tv_sec && - curtime.tv_nsec >= time_point->tv_nsec)) - sleeptime = 0; - else - { - sleeptime = (time_point->tv_nsec - curtime.tv_nsec + 999999)/1000000; - sleeptime += (time_point->tv_sec - curtime.tv_sec)*1000; - } - - IncrementRef(&icond->wait_count); - LeaveCriticalSection(mtx); - - res = WaitForMultipleObjects(2, icond->events, FALSE, sleeptime); - - if(DecrementRef(&icond->wait_count) == 0 && res == WAIT_OBJECT_0+BROADCAST) - ResetEvent(icond->events[BROADCAST]); - EnterCriticalSection(mtx); - - return (res == WAIT_TIMEOUT) ? althrd_timedout : althrd_success; -} - void alcnd_destroy(alcnd_t *cond) { _int_alcnd_t *icond = cond->Ptr; @@ -389,46 +327,40 @@ void alcnd_destroy(alcnd_t *cond) #endif /* defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 */ -/* An associative map of uint:void* pairs. The key is the TLS index (given by - * TlsAlloc), and the value is the altss_dtor_t callback. When a thread exits, - * we iterate over the TLS indices for their thread-local value and call the - * destructor function with it if they're both not NULL. To avoid using - * DllMain, a PIMAGE_TLS_CALLBACK function pointer is placed in a ".CRT$XLx" - * section (where x is a character A to Z) which will be called by the CRT. - */ -static UIntMap TlsDestructors = UINTMAP_STATIC_INITIALIZE; - -static void NTAPI altss_callback(void* UNUSED(handle), DWORD reason, void* UNUSED(reserved)) +int alsem_init(alsem_t *sem, unsigned int initial) { - ALsizei i; - - if(reason == DLL_PROCESS_DETACH) - { - ResetUIntMap(&TlsDestructors); - return; - } - if(reason != DLL_THREAD_DETACH) - return; - - LockUIntMapRead(&TlsDestructors); - for(i = 0;i < TlsDestructors.size;i++) - { - void *ptr = altss_get(TlsDestructors.keys[i]); - altss_dtor_t callback = (altss_dtor_t)TlsDestructors.values[i]; - if(ptr && callback) - callback(ptr); - } - UnlockUIntMapRead(&TlsDestructors); + *sem = CreateSemaphore(NULL, initial, INT_MAX, NULL); + if(*sem != NULL) return althrd_success; + return althrd_error; } -#ifdef _MSC_VER -#pragma section(".CRT$XLB",read) -__declspec(allocate(".CRT$XLB")) PIMAGE_TLS_CALLBACK altss_callback_ = altss_callback; -#elif defined(__GNUC__) -PIMAGE_TLS_CALLBACK altss_callback_ __attribute__((section(".CRT$XLB"))) = altss_callback; -#else -#warning "No TLS callback support, thread-local contexts may leak references on poorly written applications." -PIMAGE_TLS_CALLBACK altss_callback_ = altss_callback; -#endif + +void alsem_destroy(alsem_t *sem) +{ + CloseHandle(*sem); +} + +int alsem_post(alsem_t *sem) +{ + DWORD ret = ReleaseSemaphore(*sem, 1, NULL); + if(ret) return althrd_success; + return althrd_error; +} + +int alsem_wait(alsem_t *sem) +{ + DWORD ret = WaitForSingleObject(*sem, INFINITE); + if(ret == WAIT_OBJECT_0) return althrd_success; + return althrd_error; +} + +int alsem_trywait(alsem_t *sem) +{ + DWORD ret = WaitForSingleObject(*sem, 0); + if(ret == WAIT_OBJECT_0) return althrd_success; + if(ret == WAIT_TIMEOUT) return althrd_busy; + return althrd_error; +} + int altss_create(altss_t *tss_id, altss_dtor_t callback) { @@ -480,6 +412,27 @@ void alcall_once(alonce_flag *once, void (*callback)(void)) InterlockedExchange(once, 2); } + +void althrd_deinit(void) +{ + ResetUIntMap(&ThrdIdHandle); + ResetUIntMap(&TlsDestructors); +} + +void althrd_thread_detach(void) +{ + ALsizei i; + + LockUIntMapRead(&TlsDestructors); + for(i = 0;i < TlsDestructors.size;i++) + { + void *ptr = altss_get(TlsDestructors.keys[i]); + altss_dtor_t callback = (altss_dtor_t)TlsDestructors.values[i]; + if(ptr && callback) callback(ptr); + } + UnlockUIntMapRead(&TlsDestructors); +} + #else #include @@ -493,6 +446,8 @@ void alcall_once(alonce_flag *once, void (*callback)(void)) extern inline int althrd_sleep(const struct timespec *ts, struct timespec *rem); extern inline void alcall_once(alonce_flag *once, void (*callback)(void)); +extern inline void althrd_deinit(void); +extern inline void althrd_thread_detach(void); void althrd_setname(althrd_t thr, const char *name) { @@ -500,6 +455,8 @@ void althrd_setname(althrd_t thr, const char *name) #if defined(PTHREAD_SETNAME_NP_ONE_PARAM) if(althrd_equal(thr, althrd_current())) pthread_setname_np(name); +#elif defined(PTHREAD_SETNAME_NP_THREE_PARAMS) + pthread_setname_np(thr, "%s", (void*)name); #else pthread_setname_np(thr, name); #endif @@ -602,15 +559,9 @@ int almtx_init(almtx_t *mtx, int type) int ret; if(!mtx) return althrd_error; -#ifdef HAVE_PTHREAD_MUTEX_TIMEDLOCK - if((type&~(almtx_recursive|almtx_timed)) != 0) - return althrd_error; -#else if((type&~almtx_recursive) != 0) return althrd_error; -#endif - type &= ~almtx_timed; if(type == almtx_plain) ret = pthread_mutex_init(mtx, NULL); else @@ -642,20 +593,6 @@ void almtx_destroy(almtx_t *mtx) pthread_mutex_destroy(mtx); } -int almtx_timedlock(almtx_t *mtx, const struct timespec *ts) -{ -#ifdef HAVE_PTHREAD_MUTEX_TIMEDLOCK - int ret = pthread_mutex_timedlock(mtx, ts); - switch(ret) - { - case 0: return althrd_success; - case ETIMEDOUT: return althrd_timedout; - case EBUSY: return althrd_busy; - } -#endif - return althrd_error; -} - int alcnd_init(alcnd_t *cond) { if(pthread_cond_init(cond, NULL) == 0) @@ -684,16 +621,44 @@ int alcnd_wait(alcnd_t *cond, almtx_t *mtx) return althrd_error; } -int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_point) +void alcnd_destroy(alcnd_t *cond) { - if(pthread_cond_timedwait(cond, mtx, time_point) == 0) + pthread_cond_destroy(cond); +} + + +int alsem_init(alsem_t *sem, unsigned int initial) +{ + if(sem_init(sem, 0, initial) == 0) return althrd_success; return althrd_error; } -void alcnd_destroy(alcnd_t *cond) +void alsem_destroy(alsem_t *sem) { - pthread_cond_destroy(cond); + sem_destroy(sem); +} + +int alsem_post(alsem_t *sem) +{ + if(sem_post(sem) == 0) + return althrd_success; + return althrd_error; +} + +int alsem_wait(alsem_t *sem) +{ + if(sem_wait(sem) == 0) return althrd_success; + if(errno == EINTR) return -2; + return althrd_error; +} + +int alsem_trywait(alsem_t *sem) +{ + if(sem_trywait(sem) == 0) return althrd_success; + if(errno == EWOULDBLOCK) return althrd_busy; + if(errno == EINTR) return -2; + return althrd_error; } diff --git a/Engine/lib/openal-soft/include/threads.h b/Engine/lib/openal-soft/common/threads.h similarity index 83% rename from Engine/lib/openal-soft/include/threads.h rename to Engine/lib/openal-soft/common/threads.h index c2848ee77..b0bebd8de 100644 --- a/Engine/lib/openal-soft/include/threads.h +++ b/Engine/lib/openal-soft/common/threads.h @@ -3,6 +3,16 @@ #include +#if defined(__GNUC__) && defined(__i386__) +/* force_align_arg_pointer is required for proper function arguments aligning + * when SSE code is used. Some systems (Windows, QNX) do not guarantee our + * thread functions will be properly aligned on the stack, even though GCC may + * generate code with the assumption that it is. */ +#define FORCE_ALIGN __attribute__((force_align_arg_pointer)) +#else +#define FORCE_ALIGN +#endif + #ifdef __cplusplus extern "C" { #endif @@ -18,7 +28,6 @@ enum { enum { almtx_plain = 0, almtx_recursive = 1, - almtx_timed = 2 }; typedef int (*althrd_start_t)(void*); @@ -47,6 +56,7 @@ typedef CONDITION_VARIABLE alcnd_t; #else typedef struct { void *Ptr; } alcnd_t; #endif +typedef HANDLE alsem_t; typedef DWORD altss_t; typedef LONG alonce_flag; @@ -55,6 +65,9 @@ typedef LONG alonce_flag; int althrd_sleep(const struct timespec *ts, struct timespec *rem); void alcall_once(alonce_flag *once, void (*callback)(void)); +void althrd_deinit(void); +void althrd_thread_detach(void); + inline althrd_t althrd_current(void) { @@ -117,11 +130,13 @@ inline int altss_set(altss_t tss_id, void *val) #include #include #include +#include typedef pthread_t althrd_t; typedef pthread_mutex_t almtx_t; typedef pthread_cond_t alcnd_t; +typedef sem_t alsem_t; typedef pthread_key_t altss_t; typedef pthread_once_t alonce_flag; @@ -204,6 +219,10 @@ inline void alcall_once(alonce_flag *once, void (*callback)(void)) pthread_once(once, callback); } + +inline void althrd_deinit(void) { } +inline void althrd_thread_detach(void) { } + #endif @@ -214,15 +233,19 @@ void althrd_setname(althrd_t thr, const char *name); int almtx_init(almtx_t *mtx, int type); void almtx_destroy(almtx_t *mtx); -int almtx_timedlock(almtx_t *mtx, const struct timespec *ts); int alcnd_init(alcnd_t *cond); int alcnd_signal(alcnd_t *cond); int alcnd_broadcast(alcnd_t *cond); int alcnd_wait(alcnd_t *cond, almtx_t *mtx); -int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_point); void alcnd_destroy(alcnd_t *cond); +int alsem_init(alsem_t *sem, unsigned int initial); +void alsem_destroy(alsem_t *sem); +int alsem_post(alsem_t *sem); +int alsem_wait(alsem_t *sem); +int alsem_trywait(alsem_t *sem); + int altss_create(altss_t *tss_id, altss_dtor_t callback); void altss_delete(altss_t tss_id); diff --git a/Engine/lib/openal-soft/common/uintmap.c b/Engine/lib/openal-soft/common/uintmap.c index d3b519236..18d52d64b 100644 --- a/Engine/lib/openal-soft/common/uintmap.c +++ b/Engine/lib/openal-soft/common/uintmap.c @@ -43,24 +43,23 @@ ALenum InsertUIntMapEntry(UIntMap *map, ALuint key, ALvoid *value) WriteLock(&map->lock); if(map->size > 0) { - ALsizei low = 0; - ALsizei high = map->size - 1; - while(low < high) - { - ALsizei mid = low + (high-low)/2; - if(map->keys[mid] < key) - low = mid + 1; + ALsizei count = map->size; + do { + ALsizei step = count>>1; + ALsizei i = pos+step; + if(!(map->keys[i] < key)) + count = step; else - high = mid; - } - if(map->keys[low] < key) - low++; - pos = low; + { + pos = i+1; + count -= step+1; + } + } while(count > 0); } if(pos == map->size || map->keys[pos] != key) { - if(map->size == map->limit) + if(map->size >= map->limit) { WriteUnlock(&map->lock); return AL_OUT_OF_MEMORY; @@ -126,25 +125,28 @@ ALvoid *RemoveUIntMapKey(UIntMap *map, ALuint key) WriteLock(&map->lock); if(map->size > 0) { - ALsizei low = 0; - ALsizei high = map->size - 1; - while(low < high) - { - ALsizei mid = low + (high-low)/2; - if(map->keys[mid] < key) - low = mid + 1; + ALsizei pos = 0; + ALsizei count = map->size; + do { + ALsizei step = count>>1; + ALsizei i = pos+step; + if(!(map->keys[i] < key)) + count = step; else - high = mid; - } - if(map->keys[low] == key) - { - ptr = map->values[low]; - if(low < map->size-1) { - memmove(&map->keys[low], &map->keys[low+1], - (map->size-1-low)*sizeof(map->keys[0])); - memmove(&map->values[low], &map->values[low+1], - (map->size-1-low)*sizeof(map->values[0])); + pos = i+1; + count -= step+1; + } + } while(count > 0); + if(pos < map->size && map->keys[pos] == key) + { + ptr = map->values[pos]; + if(pos < map->size-1) + { + memmove(&map->keys[pos], &map->keys[pos+1], + (map->size-1-pos)*sizeof(map->keys[0])); + memmove(&map->values[pos], &map->values[pos+1], + (map->size-1-pos)*sizeof(map->values[0])); } map->size--; } @@ -153,76 +155,28 @@ ALvoid *RemoveUIntMapKey(UIntMap *map, ALuint key) return ptr; } -ALvoid *RemoveUIntMapKeyNoLock(UIntMap *map, ALuint key) -{ - if(map->size > 0) - { - ALsizei low = 0; - ALsizei high = map->size - 1; - while(low < high) - { - ALsizei mid = low + (high-low)/2; - if(map->keys[mid] < key) - low = mid + 1; - else - high = mid; - } - if(map->keys[low] == key) - { - ALvoid *ptr = map->values[low]; - if(low < map->size-1) - { - memmove(&map->keys[low], &map->keys[low+1], - (map->size-1-low)*sizeof(map->keys[0])); - memmove(&map->values[low], &map->values[low+1], - (map->size-1-low)*sizeof(map->values[0])); - } - map->size--; - return ptr; - } - } - return NULL; -} - ALvoid *LookupUIntMapKey(UIntMap *map, ALuint key) { ALvoid *ptr = NULL; ReadLock(&map->lock); if(map->size > 0) { - ALsizei low = 0; - ALsizei high = map->size - 1; - while(low < high) - { - ALsizei mid = low + (high-low)/2; - if(map->keys[mid] < key) - low = mid + 1; + ALsizei pos = 0; + ALsizei count = map->size; + do { + ALsizei step = count>>1; + ALsizei i = pos+step; + if(!(map->keys[i] < key)) + count = step; else - high = mid; - } - if(map->keys[low] == key) - ptr = map->values[low]; + { + pos = i+1; + count -= step+1; + } + } while(count > 0); + if(pos < map->size && map->keys[pos] == key) + ptr = map->values[pos]; } ReadUnlock(&map->lock); return ptr; } - -ALvoid *LookupUIntMapKeyNoLock(UIntMap *map, ALuint key) -{ - if(map->size > 0) - { - ALsizei low = 0; - ALsizei high = map->size - 1; - while(low < high) - { - ALsizei mid = low + (high-low)/2; - if(map->keys[mid] < key) - low = mid + 1; - else - high = mid; - } - if(map->keys[low] == key) - return map->values[low]; - } - return NULL; -} diff --git a/Engine/lib/openal-soft/include/uintmap.h b/Engine/lib/openal-soft/common/uintmap.h similarity index 60% rename from Engine/lib/openal-soft/include/uintmap.h rename to Engine/lib/openal-soft/common/uintmap.h index acb2749af..32868653d 100644 --- a/Engine/lib/openal-soft/include/uintmap.h +++ b/Engine/lib/openal-soft/common/uintmap.h @@ -1,6 +1,8 @@ #ifndef AL_UINTMAP_H #define AL_UINTMAP_H +#include + #include "AL/al.h" #include "rwlock.h" @@ -19,24 +21,18 @@ typedef struct UIntMap { RWLock lock; } UIntMap; #define UINTMAP_STATIC_INITIALIZE_N(_n) { NULL, NULL, 0, 0, (_n), RWLOCK_STATIC_INITIALIZE } -#define UINTMAP_STATIC_INITIALIZE UINTMAP_STATIC_INITIALIZE_N(~0) +#define UINTMAP_STATIC_INITIALIZE UINTMAP_STATIC_INITIALIZE_N(INT_MAX) void InitUIntMap(UIntMap *map, ALsizei limit); void ResetUIntMap(UIntMap *map); ALenum InsertUIntMapEntry(UIntMap *map, ALuint key, ALvoid *value); ALvoid *RemoveUIntMapKey(UIntMap *map, ALuint key); -ALvoid *RemoveUIntMapKeyNoLock(UIntMap *map, ALuint key); ALvoid *LookupUIntMapKey(UIntMap *map, ALuint key); -ALvoid *LookupUIntMapKeyNoLock(UIntMap *map, ALuint key); -inline void LockUIntMapRead(UIntMap *map) -{ ReadLock(&map->lock); } -inline void UnlockUIntMapRead(UIntMap *map) -{ ReadUnlock(&map->lock); } -inline void LockUIntMapWrite(UIntMap *map) -{ WriteLock(&map->lock); } -inline void UnlockUIntMapWrite(UIntMap *map) -{ WriteUnlock(&map->lock); } +inline void LockUIntMapRead(UIntMap *map) { ReadLock(&map->lock); } +inline void UnlockUIntMapRead(UIntMap *map) { ReadUnlock(&map->lock); } +inline void LockUIntMapWrite(UIntMap *map) { WriteLock(&map->lock); } +inline void UnlockUIntMapWrite(UIntMap *map) { WriteUnlock(&map->lock); } #ifdef __cplusplus } diff --git a/Engine/lib/openal-soft/common/win_main_utf8.h b/Engine/lib/openal-soft/common/win_main_utf8.h new file mode 100644 index 000000000..faddc2579 --- /dev/null +++ b/Engine/lib/openal-soft/common/win_main_utf8.h @@ -0,0 +1,97 @@ +#ifndef WIN_MAIN_UTF8_H +#define WIN_MAIN_UTF8_H + +/* For Windows systems this provides a way to get UTF-8 encoded argv strings, + * and also overrides fopen to accept UTF-8 filenames. Working with wmain + * directly complicates cross-platform compatibility, while normal main() in + * Windows uses the current codepage (which has limited availability of + * characters). + * + * For MinGW, you must link with -municode + */ +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include + +static FILE *my_fopen(const char *fname, const char *mode) +{ + WCHAR *wname=NULL, *wmode=NULL; + int namelen, modelen; + FILE *file = NULL; + errno_t err; + + namelen = MultiByteToWideChar(CP_UTF8, 0, fname, -1, NULL, 0); + modelen = MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0); + + if(namelen <= 0 || modelen <= 0) + { + fprintf(stderr, "Failed to convert UTF-8 fname \"%s\", mode \"%s\"\n", fname, mode); + return NULL; + } + + wname = calloc(sizeof(WCHAR), namelen+modelen); + wmode = wname + namelen; + MultiByteToWideChar(CP_UTF8, 0, fname, -1, wname, namelen); + MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, modelen); + + err = _wfopen_s(&file, wname, wmode); + if(err) + { + errno = err; + file = NULL; + } + + free(wname); + + return file; +} +#define fopen my_fopen + + +static char **arglist; +static void cleanup_arglist(void) +{ + free(arglist); +} + +static void GetUnicodeArgs(int *argc, char ***argv) +{ + size_t total; + wchar_t **args; + int nargs, i; + + args = CommandLineToArgvW(GetCommandLineW(), &nargs); + if(!args) + { + fprintf(stderr, "Failed to get command line args: %ld\n", GetLastError()); + exit(EXIT_FAILURE); + } + + total = sizeof(**argv) * nargs; + for(i = 0;i < nargs;i++) + total += WideCharToMultiByte(CP_UTF8, 0, args[i], -1, NULL, 0, NULL, NULL); + + atexit(cleanup_arglist); + arglist = *argv = calloc(1, total); + (*argv)[0] = (char*)(*argv + nargs); + for(i = 0;i < nargs-1;i++) + { + int len = WideCharToMultiByte(CP_UTF8, 0, args[i], -1, (*argv)[i], 65535, NULL, NULL); + (*argv)[i+1] = (*argv)[i] + len; + } + WideCharToMultiByte(CP_UTF8, 0, args[i], -1, (*argv)[i], 65535, NULL, NULL); + *argc = nargs; + + LocalFree(args); +} +#define GET_UNICODE_ARGS(argc, argv) GetUnicodeArgs(argc, argv) + +#else + +/* Do nothing. */ +#define GET_UNICODE_ARGS(argc, argv) + +#endif + +#endif /* WIN_MAIN_UTF8_H */ diff --git a/Engine/lib/openal-soft/config.h.in b/Engine/lib/openal-soft/config.h.in index a06c2039a..1ff64fe47 100644 --- a/Engine/lib/openal-soft/config.h.in +++ b/Engine/lib/openal-soft/config.h.in @@ -2,18 +2,18 @@ #define AL_API ${EXPORT_DECL} #define ALC_API ${EXPORT_DECL} -/* Define to the library version */ -#define ALSOFT_VERSION "${LIB_VERSION}" - /* Define any available alignment declaration */ #define ALIGN(x) ${ALIGN_DECL} -/* Explicit hidden visibility attribute */ -#define HIDDEN_DECL ${HIDDEN_DECL} +/* Define a built-in call indicating an aligned data pointer */ +#define ASSUME_ALIGNED(x, y) ${ASSUME_ALIGNED_DECL} /* Define if HRTF data is embedded in the library */ #cmakedefine ALSOFT_EMBED_HRTF_DATA +/* Define if we have the sysconf function */ +#cmakedefine HAVE_SYSCONF + /* Define if we have the C11 aligned_alloc function */ #cmakedefine HAVE_ALIGNED_ALLOC @@ -23,6 +23,12 @@ /* Define if we have the _aligned_malloc function */ #cmakedefine HAVE__ALIGNED_MALLOC +/* Define if we have the proc_pidpath function */ +#cmakedefine HAVE_PROC_PIDPATH + +/* Define if we have the getopt function */ +#cmakedefine HAVE_GETOPT + /* Define if we have SSE CPU extensions */ #cmakedefine HAVE_SSE #cmakedefine HAVE_SSE2 @@ -47,8 +53,8 @@ /* Define if we have the QSA backend */ #cmakedefine HAVE_QSA -/* Define if we have the MMDevApi backend */ -#cmakedefine HAVE_MMDEVAPI +/* Define if we have the WASAPI backend */ +#cmakedefine HAVE_WASAPI /* Define if we have the DSound backend */ #cmakedefine HAVE_DSOUND @@ -74,6 +80,9 @@ /* Define if we have the Wave Writer backend */ #cmakedefine HAVE_WAVE +/* Define if we have the SDL2 backend */ +#cmakedefine HAVE_SDL2 + /* Define if we have the stat function */ #cmakedefine HAVE_STAT @@ -83,6 +92,12 @@ /* Define if we have the modff function */ #cmakedefine HAVE_MODFF +/* Define if we have the log2f function */ +#cmakedefine HAVE_LOG2F + +/* Define if we have the cbrtf function */ +#cmakedefine HAVE_CBRTF + /* Define if we have the strtof function */ #cmakedefine HAVE_STRTOF @@ -98,9 +113,6 @@ /* Define to the size of a long long int type */ #cmakedefine SIZEOF_LONG_LONG ${SIZEOF_LONG_LONG} -/* Define if we have C99 variable-length array support */ -#cmakedefine HAVE_C99_VLA - /* Define if we have C99 _Bool support */ #cmakedefine HAVE_C99_BOOL @@ -137,9 +149,6 @@ /* Define if we have pthread_np.h */ #cmakedefine HAVE_PTHREAD_NP_H -/* Define if we have alloca.h */ -#cmakedefine HAVE_ALLOCA_H - /* Define if we have malloc.h */ #cmakedefine HAVE_MALLOC_H @@ -179,6 +188,12 @@ /* Define if we have the __cpuid() intrinsic */ #cmakedefine HAVE_CPUID_INTRINSIC +/* Define if we have the _BitScanForward64() intrinsic */ +#cmakedefine HAVE_BITSCANFORWARD64_INTRINSIC + +/* Define if we have the _BitScanForward() intrinsic */ +#cmakedefine HAVE_BITSCANFORWARD_INTRINSIC + /* Define if we have _controlfp() */ #cmakedefine HAVE__CONTROLFP @@ -194,6 +209,9 @@ /* Define if pthread_setname_np() only accepts one parameter */ #cmakedefine PTHREAD_SETNAME_NP_ONE_PARAM +/* Define if pthread_setname_np() accepts three parameters */ +#cmakedefine PTHREAD_SETNAME_NP_THREE_PARAMS + /* Define if we have pthread_set_name_np() */ #cmakedefine HAVE_PTHREAD_SET_NAME_NP diff --git a/Engine/lib/openal-soft/include/AL/alext.h b/Engine/lib/openal-soft/include/AL/alext.h index 0090c8041..cd7f2750d 100644 --- a/Engine/lib/openal-soft/include/AL/alext.h +++ b/Engine/lib/openal-soft/include/AL/alext.h @@ -97,6 +97,31 @@ extern "C" { #ifndef AL_EXT_MCFORMATS #define AL_EXT_MCFORMATS 1 +/* Provides support for surround sound buffer formats with 8, 16, and 32-bit + * samples. + * + * QUAD8: Unsigned 8-bit, Quadraphonic (Front Left, Front Right, Rear Left, + * Rear Right). + * QUAD16: Signed 16-bit, Quadraphonic. + * QUAD32: 32-bit float, Quadraphonic. + * REAR8: Unsigned 8-bit, Rear Stereo (Rear Left, Rear Right). + * REAR16: Signed 16-bit, Rear Stereo. + * REAR32: 32-bit float, Rear Stereo. + * 51CHN8: Unsigned 8-bit, 5.1 Surround (Front Left, Front Right, Front Center, + * LFE, Side Left, Side Right). Note that some audio systems may label + * 5.1's Side channels as Rear or Surround; they are equivalent for the + * purposes of this extension. + * 51CHN16: Signed 16-bit, 5.1 Surround. + * 51CHN32: 32-bit float, 5.1 Surround. + * 61CHN8: Unsigned 8-bit, 6.1 Surround (Front Left, Front Right, Front Center, + * LFE, Rear Center, Side Left, Side Right). + * 61CHN16: Signed 16-bit, 6.1 Surround. + * 61CHN32: 32-bit float, 6.1 Surround. + * 71CHN8: Unsigned 8-bit, 7.1 Surround (Front Left, Front Right, Front Center, + * LFE, Rear Left, Rear Right, Side Left, Side Right). + * 71CHN16: Signed 16-bit, 7.1 Surround. + * 71CHN32: 32-bit float, 7.1 Surround. + */ #define AL_FORMAT_QUAD8 0x1204 #define AL_FORMAT_QUAD16 0x1205 #define AL_FORMAT_QUAD32 0x1206 @@ -395,6 +420,16 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device); #ifndef AL_EXT_BFORMAT #define AL_EXT_BFORMAT 1 +/* Provides support for B-Format ambisonic buffers (first-order, FuMa scaling + * and layout). + * + * BFORMAT2D_8: Unsigned 8-bit, 3-channel non-periphonic (WXY). + * BFORMAT2D_16: Signed 16-bit, 3-channel non-periphonic (WXY). + * BFORMAT2D_FLOAT32: 32-bit float, 3-channel non-periphonic (WXY). + * BFORMAT3D_8: Unsigned 8-bit, 4-channel periphonic (WXYZ). + * BFORMAT3D_16: Signed 16-bit, 4-channel periphonic (WXYZ). + * BFORMAT3D_FLOAT32: 32-bit float, 4-channel periphonic (WXYZ). + */ #define AL_FORMAT_BFORMAT2D_8 0x20021 #define AL_FORMAT_BFORMAT2D_16 0x20022 #define AL_FORMAT_BFORMAT2D_FLOAT32 0x20023 @@ -436,6 +471,44 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi #define AL_GAIN_LIMIT_SOFT 0x200E #endif +#ifndef AL_SOFT_source_resampler +#define AL_SOFT_source_resampler +#define AL_NUM_RESAMPLERS_SOFT 0x1210 +#define AL_DEFAULT_RESAMPLER_SOFT 0x1211 +#define AL_SOURCE_RESAMPLER_SOFT 0x1212 +#define AL_RESAMPLER_NAME_SOFT 0x1213 +typedef const ALchar* (AL_APIENTRY*LPALGETSTRINGISOFT)(ALenum pname, ALsizei index); +#ifdef AL_ALEXT_PROTOTYPES +AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index); +#endif +#endif + +#ifndef AL_SOFT_source_spatialize +#define AL_SOFT_source_spatialize +#define AL_SOURCE_SPATIALIZE_SOFT 0x1214 +#define AL_AUTO_SOFT 0x0002 +#endif + +#ifndef ALC_SOFT_output_limiter +#define ALC_SOFT_output_limiter +#define ALC_OUTPUT_LIMITER_SOFT 0x199A +#endif + +#ifndef ALC_SOFT_device_clock +#define ALC_SOFT_device_clock 1 +typedef int64_t ALCint64SOFT; +typedef uint64_t ALCuint64SOFT; +#define ALC_DEVICE_CLOCK_SOFT 0x1600 +#define ALC_DEVICE_LATENCY_SOFT 0x1601 +#define ALC_DEVICE_CLOCK_LATENCY_SOFT 0x1602 +#define AL_SAMPLE_OFFSET_CLOCK_SOFT 0x1202 +#define AL_SEC_OFFSET_CLOCK_SOFT 0x1203 +typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values); +#ifdef AL_ALEXT_PROTOTYPES +ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values); +#endif +#endif + #ifdef __cplusplus } #endif diff --git a/Engine/lib/openal-soft/include/almalloc.h b/Engine/lib/openal-soft/include/almalloc.h deleted file mode 100644 index 355db7954..000000000 --- a/Engine/lib/openal-soft/include/almalloc.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef AL_MALLOC_H -#define AL_MALLOC_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -void *al_malloc(size_t alignment, size_t size); -void *al_calloc(size_t alignment, size_t size); -void al_free(void *ptr); - -#ifdef __cplusplus -} -#endif - -#endif /* AL_MALLOC_H */ diff --git a/Engine/lib/openal-soft/include/atomic.h b/Engine/lib/openal-soft/include/atomic.h deleted file mode 100644 index 2a996625a..000000000 --- a/Engine/lib/openal-soft/include/atomic.h +++ /dev/null @@ -1,317 +0,0 @@ -#ifndef AL_ATOMIC_H -#define AL_ATOMIC_H - -#include "static_assert.h" -#include "bool.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* Atomics using C11 */ -#ifdef HAVE_C11_ATOMIC - -#include - -#define almemory_order memory_order -#define almemory_order_relaxed memory_order_relaxed -#define almemory_order_consume memory_order_consume -#define almemory_order_acquire memory_order_acquire -#define almemory_order_release memory_order_release -#define almemory_order_acq_rel memory_order_acq_rel -#define almemory_order_seq_cst memory_order_seq_cst - -#define ATOMIC(T) T _Atomic - -#define ATOMIC_INIT(_val, _newval) atomic_init((_val), (_newval)) -#define ATOMIC_INIT_STATIC(_newval) ATOMIC_VAR_INIT(_newval) - -#define PARAM2(f, a, b, ...) (f((a), (b))) -#define PARAM3(f, a, b, c, ...) (f((a), (b), (c))) -#define PARAM5(f, a, b, c, d, e, ...) (f((a), (b), (c), (d), (e))) - -#define ATOMIC_LOAD(...) PARAM2(atomic_load_explicit, __VA_ARGS__, memory_order_seq_cst) -#define ATOMIC_STORE(...) PARAM3(atomic_store_explicit, __VA_ARGS__, memory_order_seq_cst) - -#define ATOMIC_ADD(T, ...) PARAM3(atomic_fetch_add_explicit, __VA_ARGS__, memory_order_seq_cst) -#define ATOMIC_SUB(T, ...) PARAM3(atomic_fetch_sub_explicit, __VA_ARGS__, memory_order_seq_cst) - -#define ATOMIC_EXCHANGE(T, ...) PARAM3(atomic_exchange_explicit, __VA_ARGS__, memory_order_seq_cst) -#define ATOMIC_COMPARE_EXCHANGE_STRONG(T, ...) \ - PARAM5(atomic_compare_exchange_strong_explicit, __VA_ARGS__, memory_order_seq_cst, memory_order_seq_cst) -#define ATOMIC_COMPARE_EXCHANGE_WEAK(T, ...) \ - PARAM5(atomic_compare_exchange_weak_explicit, __VA_ARGS__, memory_order_seq_cst, memory_order_seq_cst) - -/* Atomics using GCC intrinsics */ -#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && !defined(__QNXNTO__) - -enum almemory_order { - almemory_order_relaxed, - almemory_order_consume, - almemory_order_acquire, - almemory_order_release, - almemory_order_acq_rel, - almemory_order_seq_cst -}; - -#define ATOMIC(T) struct { T volatile value; } - -#define ATOMIC_INIT(_val, _newval) do { (_val)->value = (_newval); } while(0) -#define ATOMIC_INIT_STATIC(_newval) {(_newval)} - -#define ATOMIC_LOAD(_val, ...) __extension__({ \ - __typeof((_val)->value) _r = (_val)->value; \ - __asm__ __volatile__("" ::: "memory"); \ - _r; \ -}) -#define ATOMIC_STORE(_val, _newval, ...) do { \ - __asm__ __volatile__("" ::: "memory"); \ - (_val)->value = (_newval); \ -} while(0) - -#define ATOMIC_ADD(T, _val, _incr, ...) __extension__({ \ - static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \ - __sync_fetch_and_add(&(_val)->value, (_incr)); \ -}) -#define ATOMIC_SUB(T, _val, _decr, ...) __extension__({ \ - static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \ - __sync_fetch_and_sub(&(_val)->value, (_decr)); \ -}) - -#define ATOMIC_EXCHANGE(T, _val, _newval, ...) __extension__({ \ - static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \ - __sync_lock_test_and_set(&(_val)->value, (_newval)); \ -}) -#define ATOMIC_COMPARE_EXCHANGE_STRONG(T, _val, _oldval, _newval, ...) __extension__({ \ - static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \ - T _o = *(_oldval); \ - *(_oldval) = __sync_val_compare_and_swap(&(_val)->value, _o, (_newval)); \ - *(_oldval) == _o; \ -}) - -/* Atomics using x86/x86-64 GCC inline assembly */ -#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) - -#define WRAP_ADD(ret, dest, incr) __asm__ __volatile__( \ - "lock; xaddl %0,(%1)" \ - : "=r" (ret) \ - : "r" (dest), "0" (incr) \ - : "memory" \ -) -#define WRAP_SUB(ret, dest, decr) __asm__ __volatile__( \ - "lock; xaddl %0,(%1)" \ - : "=r" (ret) \ - : "r" (dest), "0" (-(decr)) \ - : "memory" \ -) - -#define WRAP_XCHG(S, ret, dest, newval) __asm__ __volatile__( \ - "lock; xchg"S" %0,(%1)" \ - : "=r" (ret) \ - : "r" (dest), "0" (newval) \ - : "memory" \ -) -#define WRAP_CMPXCHG(S, ret, dest, oldval, newval) __asm__ __volatile__( \ - "lock; cmpxchg"S" %2,(%1)" \ - : "=a" (ret) \ - : "r" (dest), "r" (newval), "0" (oldval) \ - : "memory" \ -) - - -enum almemory_order { - almemory_order_relaxed, - almemory_order_consume, - almemory_order_acquire, - almemory_order_release, - almemory_order_acq_rel, - almemory_order_seq_cst -}; - -#define ATOMIC(T) struct { T volatile value; } - -#define ATOMIC_INIT(_val, _newval) do { (_val)->value = (_newval); } while(0) -#define ATOMIC_INIT_STATIC(_newval) {(_newval)} - -#define ATOMIC_LOAD(_val, ...) __extension__({ \ - __typeof((_val)->value) _r = (_val)->value; \ - __asm__ __volatile__("" ::: "memory"); \ - _r; \ -}) -#define ATOMIC_STORE(_val, _newval, ...) do { \ - __asm__ __volatile__("" ::: "memory"); \ - (_val)->value = (_newval); \ -} while(0) - -#define ATOMIC_ADD(T, _val, _incr, ...) __extension__({ \ - static_assert(sizeof(T)==4, "Type "#T" has incorrect size!"); \ - static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \ - T _r; \ - WRAP_ADD(_r, &(_val)->value, (T)(_incr)); \ - _r; \ -}) -#define ATOMIC_SUB(T, _val, _decr, ...) __extension__({ \ - static_assert(sizeof(T)==4, "Type "#T" has incorrect size!"); \ - static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \ - T _r; \ - WRAP_SUB(_r, &(_val)->value, (T)(_decr)); \ - _r; \ -}) - -#define ATOMIC_EXCHANGE(T, _val, _newval, ...) __extension__({ \ - static_assert(sizeof(T)==4 || sizeof(T)==8, "Type "#T" has incorrect size!"); \ - static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \ - T _r; \ - if(sizeof(T) == 4) WRAP_XCHG("l", _r, &(_val)->value, (T)(_newval)); \ - else if(sizeof(T) == 8) WRAP_XCHG("q", _r, &(_val)->value, (T)(_newval)); \ - _r; \ -}) -#define ATOMIC_COMPARE_EXCHANGE_STRONG(T, _val, _oldval, _newval, ...) __extension__({ \ - static_assert(sizeof(T)==4 || sizeof(T)==8, "Type "#T" has incorrect size!"); \ - static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \ - T _old = *(_oldval); \ - if(sizeof(T) == 4) WRAP_CMPXCHG("l", *(_oldval), &(_val)->value, _old, (T)(_newval)); \ - else if(sizeof(T) == 8) WRAP_CMPXCHG("q", *(_oldval), &(_val)->value, _old, (T)(_newval)); \ - *(_oldval) == _old; \ -}) - -/* Atomics using Windows methods */ -#elif defined(_WIN32) - -#define WIN32_LEAN_AND_MEAN -#include - -/* NOTE: This mess is *extremely* noisy, at least on GCC. It works by wrapping - * Windows' 32-bit and 64-bit atomic methods, which are then casted to use the - * given type based on its size (e.g. int and float use 32-bit atomics). This - * is fine for the swap and compare-and-swap methods, although the add and - * subtract methods only work properly for integer types. - * - * Despite how noisy it is, it's unfortunately the only way that doesn't rely - * on C99 (damn MSVC). - */ - -inline LONG AtomicAdd32(volatile LONG *dest, LONG incr) -{ - return InterlockedExchangeAdd(dest, incr); -} -inline LONG AtomicSub32(volatile LONG *dest, LONG decr) -{ - return InterlockedExchangeAdd(dest, -decr); -} - -inline LONG AtomicSwap32(volatile LONG *dest, LONG newval) -{ - return InterlockedExchange(dest, newval); -} -inline LONGLONG AtomicSwap64(volatile LONGLONG *dest, LONGLONG newval) -{ - return InterlockedExchange64(dest, newval); -} - -inline bool CompareAndSwap32(volatile LONG *dest, LONG newval, LONG *oldval) -{ - LONG old = *oldval; - *oldval = InterlockedCompareExchange(dest, newval, *oldval); - return old == *oldval; -} -inline bool CompareAndSwap64(volatile LONGLONG *dest, LONGLONG newval, LONGLONG *oldval) -{ - LONGLONG old = *oldval; - *oldval = InterlockedCompareExchange64(dest, newval, *oldval); - return old == *oldval; -} - -#define WRAP_ADDSUB(T, _func, _ptr, _amnt) ((T(*)(T volatile*,T))_func)((_ptr), (_amnt)) -#define WRAP_XCHG(T, _func, _ptr, _newval) ((T(*)(T volatile*,T))_func)((_ptr), (_newval)) -#define WRAP_CMPXCHG(T, _func, _ptr, _newval, _oldval) ((bool(*)(T volatile*,T,T*))_func)((_ptr), (_newval), (_oldval)) - - -enum almemory_order { - almemory_order_relaxed, - almemory_order_consume, - almemory_order_acquire, - almemory_order_release, - almemory_order_acq_rel, - almemory_order_seq_cst -}; - -#define ATOMIC(T) struct { T volatile value; } - -#define ATOMIC_INIT(_val, _newval) do { (_val)->value = (_newval); } while(0) -#define ATOMIC_INIT_STATIC(_newval) {(_newval)} - -#define ATOMIC_LOAD(_val, ...) ((_val)->value) -#define ATOMIC_STORE(_val, _newval, ...) do { \ - (_val)->value = (_newval); \ -} while(0) - -int _al_invalid_atomic_size(); /* not defined */ - -#define ATOMIC_ADD(T, _val, _incr, ...) \ - ((sizeof(T)==4) ? WRAP_ADDSUB(T, AtomicAdd32, &(_val)->value, (_incr)) : \ - (T)_al_invalid_atomic_size()) -#define ATOMIC_SUB(T, _val, _decr, ...) \ - ((sizeof(T)==4) ? WRAP_ADDSUB(T, AtomicSub32, &(_val)->value, (_decr)) : \ - (T)_al_invalid_atomic_size()) - -#define ATOMIC_EXCHANGE(T, _val, _newval, ...) \ - ((sizeof(T)==4) ? WRAP_XCHG(T, AtomicSwap32, &(_val)->value, (_newval)) : \ - (sizeof(T)==8) ? WRAP_XCHG(T, AtomicSwap64, &(_val)->value, (_newval)) : \ - (T)_al_invalid_atomic_size()) -#define ATOMIC_COMPARE_EXCHANGE_STRONG(T, _val, _oldval, _newval, ...) \ - ((sizeof(T)==4) ? WRAP_CMPXCHG(T, CompareAndSwap32, &(_val)->value, (_newval), (_oldval)) : \ - (sizeof(T)==8) ? WRAP_CMPXCHG(T, CompareAndSwap64, &(_val)->value, (_newval), (_oldval)) : \ - (bool)_al_invalid_atomic_size()) - -#else - -#error "No atomic functions available on this platform!" - -#define ATOMIC(T) T - -#define ATOMIC_INIT_STATIC(_newval) (0) - -#define ATOMIC_LOAD_UNSAFE(_val) (0) -#define ATOMIC_STORE_UNSAFE(_val, _newval) ((void)0) - -#define ATOMIC_LOAD(_val, ...) (0) -#define ATOMIC_STORE(_val, _newval, ...) ((void)0) - -#define ATOMIC_ADD(T, _val, _incr, ...) (0) -#define ATOMIC_SUB(T, _val, _decr, ...) (0) - -#define ATOMIC_EXCHANGE(T, _val, _newval, ...) (0) -#define ATOMIC_COMPARE_EXCHANGE_STRONG(T, _val, _oldval, _newval, ...) (0) -#endif - -/* If no weak cmpxchg is provided (not all systems will have one), substitute a - * strong cmpxchg. */ -#ifndef ATOMIC_COMPARE_EXCHANGE_WEAK -#define ATOMIC_COMPARE_EXCHANGE_WEAK ATOMIC_COMPARE_EXCHANGE_STRONG -#endif - - -typedef unsigned int uint; -typedef ATOMIC(uint) RefCount; - -inline void InitRef(RefCount *ptr, uint value) -{ ATOMIC_INIT(ptr, value); } -inline uint ReadRef(RefCount *ptr) -{ return ATOMIC_LOAD(ptr); } -inline uint IncrementRef(RefCount *ptr) -{ return ATOMIC_ADD(uint, ptr, 1)+1; } -inline uint DecrementRef(RefCount *ptr) -{ return ATOMIC_SUB(uint, ptr, 1)-1; } - - -/* This is *NOT* atomic, but is a handy utility macro to compare-and-swap non- - * atomic variables. */ -#define COMPARE_EXCHANGE(_val, _oldval, _newval) ((*(_val) == *(_oldval)) ? ((*(_val)=(_newval)),true) : ((*(_oldval)=*(_val)),false)) - - -#ifdef __cplusplus -} -#endif - -#endif /* AL_ATOMIC_H */ diff --git a/Engine/lib/openal-soft/include/math_defs.h b/Engine/lib/openal-soft/include/math_defs.h deleted file mode 100644 index 149cf80ba..000000000 --- a/Engine/lib/openal-soft/include/math_defs.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef AL_MATH_DEFS_H -#define AL_MATH_DEFS_H - -#ifdef HAVE_FLOAT_H -#include -#endif - -#define F_PI (3.14159265358979323846f) -#define F_PI_2 (1.57079632679489661923f) -#define F_TAU (6.28318530717958647692f) - -#ifndef FLT_EPSILON -#define FLT_EPSILON (1.19209290e-07f) -#endif - -#define DEG2RAD(x) ((ALfloat)(x) * (F_PI/180.0f)) -#define RAD2DEG(x) ((ALfloat)(x) * (180.0f/F_PI)) - -#endif /* AL_MATH_DEFS_H */ diff --git a/Engine/lib/openal-soft/native-tools/CMakeLists.txt b/Engine/lib/openal-soft/native-tools/CMakeLists.txt new file mode 100644 index 000000000..5e816bba7 --- /dev/null +++ b/Engine/lib/openal-soft/native-tools/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.0.2) + +project(native-tools) + +include(CheckLibraryExists) + +set(CPP_DEFS ) +if(WIN32) + set(CPP_DEFS ${CPP_DEFS} _WIN32) +endif(WIN32) + +check_library_exists(m pow "" HAVE_LIBM) + +add_executable(bin2h bin2h.c) +# Enforce no dressing for executable names, so the main script can find it +set_target_properties(bin2h PROPERTIES OUTPUT_NAME bin2h) +# Avoid configuration-dependent subdirectories while building with Visual Studio +set_target_properties(bin2h PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}") +set_target_properties(bin2h PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}") +target_compile_definitions(bin2h PRIVATE ${CPP_DEFS}) + +add_executable(bsincgen bsincgen.c) +set_target_properties(bsincgen PROPERTIES OUTPUT_NAME bsincgen) +set_target_properties(bsincgen PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}") +set_target_properties(bsincgen PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}") +target_compile_definitions(bsincgen PRIVATE ${CPP_DEFS}) +if(HAVE_LIBM) + target_link_libraries(bsincgen m) +endif(HAVE_LIBM) diff --git a/Engine/lib/openal-soft/native-tools/bin2h.c b/Engine/lib/openal-soft/native-tools/bin2h.c new file mode 100644 index 000000000..92f2b8a51 --- /dev/null +++ b/Engine/lib/openal-soft/native-tools/bin2h.c @@ -0,0 +1,100 @@ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include + +int main (int argc, char *argv[]) +{ + char* input_name; + FILE* input_file; + + char* output_name; + FILE* output_file; + + char* variable_name; + + if (4 != argc) + { + puts("Usage: bin2h [input] [output] [variable]"); + return EXIT_FAILURE; + } + + input_name = argv[1]; + output_name = argv[2]; + variable_name = argv[3]; + + input_file = fopen(input_name, "rb"); + + if (NULL == input_file) + { + printf("Could not open input file '%s': %s\n", input_name, strerror(errno)); + return EXIT_FAILURE; + } + + output_file = fopen(output_name, "w"); + + if (NULL == output_file) + { + printf("Could not open output file '%s': %s\n", output_name, strerror(errno)); + return EXIT_FAILURE; + } + + if (fprintf(output_file, "static const unsigned char %s[] = {", variable_name) < 0) + { + printf("Could not write to output file '%s': %s\n", output_name, strerror(ferror(output_file))); + return EXIT_FAILURE; + } + + while (0 == feof(input_file)) + { + unsigned char buffer[4096]; + size_t i, count = fread(buffer, 1, sizeof(buffer), input_file); + + if (sizeof(buffer) != count) + { + if (0 == feof(input_file) || 0 != ferror(input_file)) + { + printf("Could not read from input file '%s': %s\n", input_name, strerror(ferror(input_file))); + return EXIT_FAILURE; + } + } + + for (i = 0; i < count; ++i) + { + if ((i & 15) == 0) + { + if (fprintf(output_file, "\n ") < 0) + { + printf("Could not write to output file '%s': %s\n", output_name, strerror(ferror(output_file))); + return EXIT_FAILURE; + } + } + + if (fprintf(output_file, "0x%2.2x, ", buffer[i]) < 0) + { + printf("Could not write to output file '%s': %s\n", output_name, strerror(ferror(output_file))); + return EXIT_FAILURE; + } + + } + } + + if (fprintf(output_file, "\n};\n") < 0) + { + printf("Could not write to output file '%s': %s\n", output_name, strerror(ferror(output_file))); + return EXIT_FAILURE; + } + + if (fclose(output_file) < 0) + { + printf("Could not close output file '%s': %s\n", output_name, strerror(ferror(output_file))); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/Engine/lib/openal-soft/native-tools/bsincgen.c b/Engine/lib/openal-soft/native-tools/bsincgen.c new file mode 100644 index 000000000..7edf37f14 --- /dev/null +++ b/Engine/lib/openal-soft/native-tools/bsincgen.c @@ -0,0 +1,367 @@ +/* + * Sinc interpolator coefficient and delta generator for the OpenAL Soft + * cross platform audio library. + * + * Copyright (C) 2015 by Christopher Fitzgerald. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Or visit: http://www.gnu.org/licenses/old-licenses/lgpl-2.0.html + * + * -------------------------------------------------------------------------- + * + * This is a modified version of the bandlimited windowed sinc interpolator + * algorithm presented here: + * + * Smith, J.O. "Windowed Sinc Interpolation", in + * Physical Audio Signal Processing, + * https://ccrma.stanford.edu/~jos/pasp/Windowed_Sinc_Interpolation.html, + * online book, + * accessed October 2012. + */ + +#define _UNICODE +#include +#include +#include +#include + +#include "../common/win_main_utf8.h" + + +#ifndef M_PI +#define M_PI (3.14159265358979323846) +#endif + +#if !(defined(_ISOC99_SOURCE) || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L)) +#define log2(x) (log(x) / log(2.0)) +#endif + +/* Same as in alu.h! */ +#define FRACTIONBITS (12) +#define FRACTIONONE (1<= b) ? a : b; } + +/* NOTE: This is the normalized (instead of just sin(x)/x) cardinal sine + * function. + * 2 f_t sinc(2 f_t x) + * f_t -- normalized transition frequency (0.5 is nyquist) + * x -- sample index (-N to N) + */ +static double Sinc(const double x) +{ + if(fabs(x) < 1e-15) + return 1.0; + return sin(M_PI * x) / (M_PI * x); +} + +static double BesselI_0(const double x) +{ + double term, sum, last_sum, x2, y; + int i; + + term = 1.0; + sum = 1.0; + x2 = x / 2.0; + i = 1; + + do { + y = x2 / i; + i++; + last_sum = sum; + term *= y * y; + sum += term; + } while(sum != last_sum); + + return sum; +} + +/* NOTE: k is assumed normalized (-1 to 1) + * beta is equivalent to 2 alpha + */ +static double Kaiser(const double b, const double k) +{ + if(!(k >= -1.0 && k <= 1.0)) + return 0.0; + return BesselI_0(b * sqrt(1.0 - k*k)) / BesselI_0(b); +} + +/* Calculates the (normalized frequency) transition width of the Kaiser window. + * Rejection is in dB. + */ +static double CalcKaiserWidth(const double rejection, const int order) +{ + double w_t = 2.0 * M_PI; + + if(rejection > 21.0) + return (rejection - 7.95) / (order * 2.285 * w_t); + /* This enforces a minimum rejection of just above 21.18dB */ + return 5.79 / (order * w_t); +} + +static double CalcKaiserBeta(const double rejection) +{ + if(rejection > 50.0) + return 0.1102 * (rejection - 8.7); + else if(rejection >= 21.0) + return (0.5842 * pow(rejection - 21.0, 0.4)) + + (0.07886 * (rejection - 21.0)); + return 0.0; +} + +/* Generates the coefficient, delta, and index tables required by the bsinc resampler */ +static void BsiGenerateTables(FILE *output, const char *tabname, const double rejection, const int order) +{ + static double filter[BSINC_SCALE_COUNT][BSINC_PHASE_COUNT + 1][BSINC_POINTS_MAX]; + static double scDeltas[BSINC_SCALE_COUNT][BSINC_PHASE_COUNT ][BSINC_POINTS_MAX]; + static double phDeltas[BSINC_SCALE_COUNT][BSINC_PHASE_COUNT + 1][BSINC_POINTS_MAX]; + static double spDeltas[BSINC_SCALE_COUNT][BSINC_PHASE_COUNT ][BSINC_POINTS_MAX]; + static int mt[BSINC_SCALE_COUNT]; + static double at[BSINC_SCALE_COUNT]; + const int num_points_min = order + 1; + double width, beta, scaleBase, scaleRange; + int si, pi, i; + + memset(filter, 0, sizeof(filter)); + memset(scDeltas, 0, sizeof(scDeltas)); + memset(phDeltas, 0, sizeof(phDeltas)); + memset(spDeltas, 0, sizeof(spDeltas)); + + /* Calculate windowing parameters. The width describes the transition + band, but it may vary due to the linear interpolation between scales + of the filter. + */ + width = CalcKaiserWidth(rejection, order); + beta = CalcKaiserBeta(rejection); + scaleBase = width / 2.0; + scaleRange = 1.0 - scaleBase; + + // Determine filter scaling. + for(si = 0; si < BSINC_SCALE_COUNT; si++) + { + const double scale = scaleBase + (scaleRange * si / (BSINC_SCALE_COUNT - 1)); + const double a = MinDouble(floor(num_points_min / (2.0 * scale)), num_points_min); + const int m = 2 * (int)a; + + mt[si] = m; + at[si] = a; + } + + /* Calculate the Kaiser-windowed Sinc filter coefficients for each scale + and phase. + */ + for(si = 0; si < BSINC_SCALE_COUNT; si++) + { + const int m = mt[si]; + const int o = num_points_min - (m / 2); + const int l = (m / 2) - 1; + const double a = at[si]; + const double scale = scaleBase + (scaleRange * si / (BSINC_SCALE_COUNT - 1)); + const double cutoff = (0.5 * scale) - (scaleBase * MaxDouble(0.5, scale)); + + for(pi = 0; pi <= BSINC_PHASE_COUNT; pi++) + { + const double phase = l + ((double)pi / BSINC_PHASE_COUNT); + + for(i = 0; i < m; i++) + { + const double x = i - phase; + filter[si][pi][o + i] = Kaiser(beta, x / a) * 2.0 * cutoff * Sinc(2.0 * cutoff * x); + } + } + } + + /* Linear interpolation between scales is simplified by pre-calculating + the delta (b - a) in: x = a + f (b - a) + + Given a difference in points between scales, the destination points + will be 0, thus: x = a + f (-a) + */ + for(si = 0; si < (BSINC_SCALE_COUNT - 1); si++) + { + const int m = mt[si]; + const int o = num_points_min - (m / 2); + + for(pi = 0; pi < BSINC_PHASE_COUNT; pi++) + { + for(i = 0; i < m; i++) + scDeltas[si][pi][o + i] = filter[si + 1][pi][o + i] - filter[si][pi][o + i]; + } + } + + // Linear interpolation between phases is also simplified. + for(si = 0; si < BSINC_SCALE_COUNT; si++) + { + const int m = mt[si]; + const int o = num_points_min - (m / 2); + + for(pi = 0; pi < BSINC_PHASE_COUNT; pi++) + { + for(i = 0; i < m; i++) + phDeltas[si][pi][o + i] = filter[si][pi + 1][o + i] - filter[si][pi][o + i]; + } + } + + /* This last simplification is done to complete the bilinear equation for + the combination of scale and phase. + */ + for(si = 0; si < (BSINC_SCALE_COUNT - 1); si++) + { + const int m = mt[si]; + const int o = num_points_min - (m / 2); + + for(pi = 0; pi < BSINC_PHASE_COUNT; pi++) + { + for(i = 0; i < m; i++) + spDeltas[si][pi][o + i] = phDeltas[si + 1][pi][o + i] - phDeltas[si][pi][o + i]; + } + } + + // Make sure the number of points is a multiple of 4 (for SIMD). + for(si = 0; si < BSINC_SCALE_COUNT; si++) + mt[si] = (mt[si]+3) & ~3; + + fprintf(output, +"/* This %d%s order filter has a rejection of -%.0fdB, yielding a transition width\n" +" * of ~%.3f (normalized frequency). Order increases when downsampling to a\n" +" * limit of one octave, after which the quality of the filter (transition\n" +" * width) suffers to reduce the CPU cost. The bandlimiting will cut all sound\n" +" * after downsampling by ~%.2f octaves.\n" +" */\n" +"const BSincTable %s = {\n", + order, (((order%100)/10) == 1) ? "th" : + ((order%10) == 1) ? "st" : + ((order%10) == 2) ? "nd" : + ((order%10) == 3) ? "rd" : "th", + rejection, width, log2(1.0/scaleBase), tabname); + + /* The scaleBase is calculated from the Kaiser window transition width. + It represents the absolute limit to the filter before it fully cuts + the signal. The limit in octaves can be calculated by taking the + base-2 logarithm of its inverse: log_2(1 / scaleBase) + */ + fprintf(output, " /* scaleBase */ %.9ef, /* scaleRange */ %.9ef,\n", scaleBase, 1.0 / scaleRange); + + fprintf(output, " /* m */ {"); + fprintf(output, " %d", mt[0]); + for(si = 1; si < BSINC_SCALE_COUNT; si++) + fprintf(output, ", %d", mt[si]); + fprintf(output, " },\n"); + + fprintf(output, " /* filterOffset */ {"); + fprintf(output, " %d", 0); + i = mt[0]*4*BSINC_PHASE_COUNT; + for(si = 1; si < BSINC_SCALE_COUNT; si++) + { + fprintf(output, ", %d", i); + i += mt[si]*4*BSINC_PHASE_COUNT; + } + + fprintf(output, " },\n"); + + // Calculate the table size. + i = 0; + for(si = 0; si < BSINC_SCALE_COUNT; si++) + i += 4 * BSINC_PHASE_COUNT * mt[si]; + + fprintf(output, "\n /* Tab (%d entries) */ {\n", i); + for(si = 0; si < BSINC_SCALE_COUNT; si++) + { + const int m = mt[si]; + const int o = num_points_min - (m / 2); + + for(pi = 0; pi < BSINC_PHASE_COUNT; pi++) + { + fprintf(output, " /* %2d,%2d (%d) */", si, pi, m); + fprintf(output, "\n "); + for(i = 0; i < m; i++) + fprintf(output, " %+14.9ef,", filter[si][pi][o + i]); + fprintf(output, "\n "); + for(i = 0; i < m; i++) + fprintf(output, " %+14.9ef,", scDeltas[si][pi][o + i]); + fprintf(output, "\n "); + for(i = 0; i < m; i++) + fprintf(output, " %+14.9ef,", phDeltas[si][pi][o + i]); + fprintf(output, "\n "); + for(i = 0; i < m; i++) + fprintf(output, " %+14.9ef,", spDeltas[si][pi][o + i]); + fprintf(output, "\n"); + } + } + fprintf(output, " }\n};\n\n"); +} + + +int main(int argc, char *argv[]) +{ + FILE *output; + + GET_UNICODE_ARGS(&argc, &argv); + + if(argc > 2) + { + fprintf(stderr, "Usage: %s [output file]\n", argv[0]); + return 1; + } + + if(argc == 2) + { + output = fopen(argv[1], "wb"); + if(!output) + { + fprintf(stderr, "Failed to open %s for writing\n", argv[1]); + return 1; + } + } + else + output = stdout; + + fprintf(output, "/* Generated by bsincgen, do not edit! */\n\n" +"static_assert(BSINC_SCALE_COUNT == %d, \"Unexpected BSINC_SCALE_COUNT value!\");\n" +"static_assert(BSINC_PHASE_COUNT == %d, \"Unexpected BSINC_PHASE_COUNT value!\");\n" +"static_assert(FRACTIONONE == %d, \"Unexpected FRACTIONONE value!\");\n\n" +"typedef struct BSincTable {\n" +" const float scaleBase, scaleRange;\n" +" const int m[BSINC_SCALE_COUNT];\n" +" const int filterOffset[BSINC_SCALE_COUNT];\n" +" alignas(16) const float Tab[];\n" +"} BSincTable;\n\n", BSINC_SCALE_COUNT, BSINC_PHASE_COUNT, FRACTIONONE); + /* A 23rd order filter with a -60dB drop at nyquist. */ + BsiGenerateTables(output, "bsinc24", 60.0, 23); + /* An 11th order filter with a -40dB drop at nyquist. */ + BsiGenerateTables(output, "bsinc12", 40.0, 11); + + if(output != stdout) + fclose(output); + output = NULL; + + return 0; +} diff --git a/Engine/lib/openal-soft/openal.pc.in b/Engine/lib/openal-soft/openal.pc.in index 8bdd4f3b1..dfa6f5738 100644 --- a/Engine/lib/openal-soft/openal.pc.in +++ b/Engine/lib/openal-soft/openal.pc.in @@ -8,4 +8,5 @@ Description: OpenAL is a cross-platform 3D audio API Requires: @PKG_CONFIG_REQUIRES@ Version: @PACKAGE_VERSION@ Libs: -L${libdir} -l@LIBNAME@ @PKG_CONFIG_LIBS@ +Libs.private:@PKG_CONFIG_PRIVATE_LIBS@ Cflags: -I${includedir} -I${includedir}/AL @PKG_CONFIG_CFLAGS@ diff --git a/Engine/lib/openal-soft/version.cmake b/Engine/lib/openal-soft/version.cmake new file mode 100644 index 000000000..af7ff0a67 --- /dev/null +++ b/Engine/lib/openal-soft/version.cmake @@ -0,0 +1,11 @@ +EXECUTE_PROCESS( + COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD + OUTPUT_VARIABLE GIT_BRANCH + OUTPUT_STRIP_TRAILING_WHITESPACE +) +EXECUTE_PROCESS( + COMMAND ${GIT_EXECUTABLE} log -1 --format=%h + OUTPUT_VARIABLE GIT_COMMIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE +) +CONFIGURE_FILE(${SRC} ${DST}) diff --git a/Engine/lib/openal-soft/version.h.in b/Engine/lib/openal-soft/version.h.in new file mode 100644 index 000000000..56f738a39 --- /dev/null +++ b/Engine/lib/openal-soft/version.h.in @@ -0,0 +1,8 @@ +/* Define to the library version */ +#define ALSOFT_VERSION "${LIB_VERSION}" + +/* Define the branch being built */ +#define ALSOFT_GIT_BRANCH "${GIT_BRANCH}" + +/* Define the hash of the head commit */ +#define ALSOFT_GIT_COMMIT_HASH "${GIT_COMMIT_HASH}" diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index d746913fb..07d56a9b9 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -118,6 +118,14 @@ if(TORQUE_SFX_OPENAL) mark_as_advanced(COREAUDIO_FRAMEWORK) mark_as_advanced(CMAKE_DEBUG_POSTFIX) mark_as_advanced(FORCE_STATIC_VCRT) + mark_as_advanced(ALSOFT_BACKEND_WASAPI) + mark_as_advanced(ALSOFT_BUILD_ROUTER) + mark_as_advanced(ALSOFT_REQUIRE_SDL2) + mark_as_advanced(ALSOFT_REQUIRE_WASAPI) + #the following is from openal-soft + mark_as_advanced(SDL2MAIN_LIBRARY) + mark_as_advanced(SDL2_CORE_LIBRARY) + mark_as_advanced(SDL2_INCLUDE_DIR) endif() mark_as_advanced(TORQUE_SFX_OPENAL)