From b160006eb177f69c5b879c877fef44b5a950baef Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sun, 10 Dec 2017 22:34:10 -0400 Subject: [PATCH] Update OpenAL Soft to 1.18.2 --- libs/openal-soft/.travis.yml | 73 +- libs/openal-soft/Alc/ALc.c | 2387 +++++---- libs/openal-soft/Alc/ALu.c | 2349 +++++---- libs/openal-soft/Alc/alcConfig.c | 106 +- libs/openal-soft/Alc/alcRing.c | 210 +- libs/openal-soft/Alc/alstring.h | 35 +- libs/openal-soft/Alc/ambdec.c | 566 +++ libs/openal-soft/Alc/ambdec.h | 46 + libs/openal-soft/Alc/backends/alsa.c | 196 +- libs/openal-soft/Alc/backends/base.c | 184 +- libs/openal-soft/Alc/backends/base.h | 29 +- libs/openal-soft/Alc/backends/coreaudio.c | 476 +- libs/openal-soft/Alc/backends/dsound.c | 238 +- libs/openal-soft/Alc/backends/jack.c | 94 +- libs/openal-soft/Alc/backends/loopback.c | 4 +- libs/openal-soft/Alc/backends/mmdevapi.c | 417 +- libs/openal-soft/Alc/backends/null.c | 6 +- libs/openal-soft/Alc/backends/opensl.c | 1052 +++- libs/openal-soft/Alc/backends/oss.c | 294 +- libs/openal-soft/Alc/backends/portaudio.c | 14 +- libs/openal-soft/Alc/backends/pulseaudio.c | 297 +- libs/openal-soft/Alc/backends/qsa.c | 370 +- libs/openal-soft/Alc/backends/sndio.c | 208 +- libs/openal-soft/Alc/backends/solaris.c | 94 +- libs/openal-soft/Alc/backends/wave.c | 73 +- libs/openal-soft/Alc/backends/winmm.c | 88 +- libs/openal-soft/Alc/bformatdec.c | 612 +++ libs/openal-soft/Alc/bformatdec.h | 75 + libs/openal-soft/Alc/bs2b.c | 57 +- libs/openal-soft/Alc/bsinc.c | 4100 ++++++++++++++++ libs/openal-soft/Alc/compat.h | 25 + libs/openal-soft/Alc/converter.c | 466 ++ libs/openal-soft/Alc/converter.h | 55 + libs/openal-soft/Alc/effects/autowah.c | 270 -- libs/openal-soft/Alc/effects/chorus.c | 219 +- libs/openal-soft/Alc/effects/compressor.c | 119 +- libs/openal-soft/Alc/effects/dedicated.c | 73 +- libs/openal-soft/Alc/effects/distortion.c | 148 +- libs/openal-soft/Alc/effects/echo.c | 131 +- libs/openal-soft/Alc/effects/equalizer.c | 147 +- libs/openal-soft/Alc/effects/flanger.c | 220 +- libs/openal-soft/Alc/effects/modulator.c | 188 +- libs/openal-soft/Alc/effects/null.c | 47 +- libs/openal-soft/Alc/effects/reverb.c | 2636 ++++++---- libs/openal-soft/Alc/helpers.c | 1298 ++--- libs/openal-soft/Alc/hrtf.c | 1324 +++-- libs/openal-soft/Alc/hrtf.h | 54 +- libs/openal-soft/Alc/mastering.c | 255 + libs/openal-soft/Alc/mixer.c | 545 ++- libs/openal-soft/Alc/mixer_c.c | 173 +- libs/openal-soft/Alc/mixer_defs.h | 135 +- libs/openal-soft/Alc/mixer_inc.c | 149 +- libs/openal-soft/Alc/mixer_neon.c | 304 +- libs/openal-soft/Alc/mixer_sse.c | 199 +- libs/openal-soft/Alc/mixer_sse2.c | 13 +- libs/openal-soft/Alc/mixer_sse3.c | 87 +- libs/openal-soft/Alc/mixer_sse41.c | 106 +- libs/openal-soft/Alc/nfcfilter.c | 418 ++ libs/openal-soft/Alc/nfcfilter.h | 37 + libs/openal-soft/Alc/panning.c | 1370 ++++-- libs/openal-soft/Alc/uhjfilter.c | 134 + libs/openal-soft/Alc/uhjfilter.h | 49 + libs/openal-soft/Alc/vector.h | 94 +- libs/openal-soft/CMakeLists.txt | 715 ++- libs/openal-soft/ChangeLog | 117 + .../OpenAL32/Include/alAuxEffectSlot.h | 101 +- libs/openal-soft/OpenAL32/Include/alBuffer.h | 54 +- libs/openal-soft/OpenAL32/Include/alEffect.h | 42 +- libs/openal-soft/OpenAL32/Include/alFilter.h | 45 +- .../openal-soft/OpenAL32/Include/alListener.h | 51 +- libs/openal-soft/OpenAL32/Include/alMain.h | 654 ++- libs/openal-soft/OpenAL32/Include/alSource.h | 160 +- libs/openal-soft/OpenAL32/Include/alu.h | 404 +- libs/openal-soft/OpenAL32/Include/bs2b.h | 41 +- libs/openal-soft/OpenAL32/alAuxEffectSlot.c | 389 +- libs/openal-soft/OpenAL32/alBuffer.c | 166 +- libs/openal-soft/OpenAL32/alEffect.c | 52 +- libs/openal-soft/OpenAL32/alError.c | 9 +- libs/openal-soft/OpenAL32/alExtension.c | 25 +- libs/openal-soft/OpenAL32/alFilter.c | 49 +- libs/openal-soft/OpenAL32/alListener.c | 152 +- libs/openal-soft/OpenAL32/alSource.c | 1797 ++++--- libs/openal-soft/OpenAL32/alState.c | 166 +- libs/openal-soft/OpenAL32/alThunk.c | 25 +- libs/openal-soft/OpenAL32/sample_cvt.c | 495 +- libs/openal-soft/alsoftrc.sample | 144 +- libs/openal-soft/appveyor.yml | 19 + .../cmake/CheckSharedFunctionExists.cmake | 4 +- libs/openal-soft/cmake/FindDSound.cmake | 30 +- libs/openal-soft/cmake/FindFFmpeg.cmake | 6 + libs/openal-soft/cmake/FindOSS.cmake | 14 +- libs/openal-soft/cmake/FindWindowsSDK.cmake | 626 +++ libs/openal-soft/{include => common}/align.h | 0 libs/openal-soft/common/almalloc.c | 62 + libs/openal-soft/common/almalloc.h | 21 + libs/openal-soft/common/atomic.c | 3 - libs/openal-soft/common/atomic.h | 425 ++ libs/openal-soft/{include => common}/bool.h | 0 libs/openal-soft/common/math_defs.h | 35 + libs/openal-soft/common/rwlock.c | 20 +- libs/openal-soft/{include => common}/rwlock.h | 9 +- .../{include => common}/static_assert.h | 0 libs/openal-soft/common/threads.c | 125 +- .../openal-soft/{include => common}/threads.h | 8 +- libs/openal-soft/common/uintmap.c | 289 +- .../openal-soft/{include => common}/uintmap.h | 18 +- libs/openal-soft/config.h.in | 21 +- libs/openal-soft/docs/3D7.1.txt | 82 + libs/openal-soft/docs/ambdec.txt | 189 + libs/openal-soft/docs/ambisonics.txt | 126 + libs/openal-soft/{ => docs}/env-vars.txt | 0 libs/openal-soft/{ => docs}/hrtf.txt | 0 libs/openal-soft/examples/alffplay.c | 1533 ------ libs/openal-soft/examples/alffplay.cpp | 1570 ++++++ libs/openal-soft/examples/alhrtf.c | 152 +- libs/openal-soft/examples/allatency.c | 98 +- libs/openal-soft/examples/alloopback.c | 38 +- libs/openal-soft/examples/alrecord.c | 386 ++ libs/openal-soft/examples/alreverb.c | 100 +- libs/openal-soft/examples/alstream.c | 134 +- libs/openal-soft/examples/altonegen.c | 97 +- libs/openal-soft/examples/common/alhelpers.c | 273 +- libs/openal-soft/examples/common/alhelpers.h | 26 +- libs/openal-soft/examples/common/sdl_sound.c | 164 - libs/openal-soft/examples/common/sdl_sound.h | 43 - libs/openal-soft/hrtf/default-44100.mhr | Bin 53853 -> 53853 bytes libs/openal-soft/hrtf/default-48000.mhr | Bin 53853 -> 53853 bytes libs/openal-soft/include/AL/alext.h | 28 + libs/openal-soft/include/atomic.h | 334 -- libs/openal-soft/include/math_defs.h | 19 - libs/openal-soft/native-tools/CMakeLists.txt | 8 + libs/openal-soft/native-tools/bin2h.c | 100 + libs/openal-soft/presets/3D7.1.ambdec | 43 + libs/openal-soft/presets/hexagon.ambdec | 51 + .../presets/itu5.1-nocenter.ambdec | 46 + libs/openal-soft/presets/itu5.1.ambdec | 48 + libs/openal-soft/presets/presets.txt | 42 + libs/openal-soft/presets/rectangle.ambdec | 45 + libs/openal-soft/presets/square.ambdec | 45 + libs/openal-soft/router/al.c | 132 + libs/openal-soft/router/alc.c | 956 ++++ libs/openal-soft/router/router.c | 512 ++ libs/openal-soft/router/router.h | 196 + .../utils/alsoft-config/CMakeLists.txt | 59 +- .../utils/alsoft-config/mainwindow.cpp | 953 +++- .../utils/alsoft-config/mainwindow.h | 32 + .../utils/alsoft-config/mainwindow.ui | 2003 ++++++-- libs/openal-soft/utils/bsincgen.c | 40 +- libs/openal-soft/utils/makehrtf.c | 4289 +++++++++-------- libs/openal-soft/utils/openal-info.c | 98 + libs/openal-soft/version.cmake | 11 + libs/openal-soft/version.h.in | 8 + 152 files changed, 33572 insertions(+), 15363 deletions(-) create mode 100644 libs/openal-soft/Alc/ambdec.c create mode 100644 libs/openal-soft/Alc/ambdec.h create mode 100644 libs/openal-soft/Alc/bformatdec.c create mode 100644 libs/openal-soft/Alc/bformatdec.h create mode 100644 libs/openal-soft/Alc/converter.c create mode 100644 libs/openal-soft/Alc/converter.h delete mode 100644 libs/openal-soft/Alc/effects/autowah.c create mode 100644 libs/openal-soft/Alc/mastering.c create mode 100644 libs/openal-soft/Alc/nfcfilter.c create mode 100644 libs/openal-soft/Alc/nfcfilter.h create mode 100644 libs/openal-soft/Alc/uhjfilter.c create mode 100644 libs/openal-soft/Alc/uhjfilter.h create mode 100644 libs/openal-soft/appveyor.yml create mode 100644 libs/openal-soft/cmake/FindWindowsSDK.cmake rename libs/openal-soft/{include => common}/align.h (100%) create mode 100644 libs/openal-soft/common/almalloc.c create mode 100644 libs/openal-soft/common/almalloc.h create mode 100644 libs/openal-soft/common/atomic.h rename libs/openal-soft/{include => common}/bool.h (100%) create mode 100644 libs/openal-soft/common/math_defs.h rename libs/openal-soft/{include => common}/rwlock.h (67%) rename libs/openal-soft/{include => common}/static_assert.h (100%) rename libs/openal-soft/{include => common}/threads.h (95%) rename libs/openal-soft/{include => common}/uintmap.h (61%) create mode 100644 libs/openal-soft/docs/3D7.1.txt create mode 100644 libs/openal-soft/docs/ambdec.txt create mode 100644 libs/openal-soft/docs/ambisonics.txt rename libs/openal-soft/{ => docs}/env-vars.txt (100%) rename libs/openal-soft/{ => docs}/hrtf.txt (100%) delete mode 100644 libs/openal-soft/examples/alffplay.c create mode 100644 libs/openal-soft/examples/alffplay.cpp create mode 100644 libs/openal-soft/examples/alrecord.c delete mode 100644 libs/openal-soft/examples/common/sdl_sound.c delete mode 100644 libs/openal-soft/examples/common/sdl_sound.h delete mode 100644 libs/openal-soft/include/atomic.h delete mode 100644 libs/openal-soft/include/math_defs.h create mode 100644 libs/openal-soft/native-tools/CMakeLists.txt create mode 100644 libs/openal-soft/native-tools/bin2h.c create mode 100644 libs/openal-soft/presets/3D7.1.ambdec create mode 100644 libs/openal-soft/presets/hexagon.ambdec create mode 100644 libs/openal-soft/presets/itu5.1-nocenter.ambdec create mode 100644 libs/openal-soft/presets/itu5.1.ambdec create mode 100644 libs/openal-soft/presets/presets.txt create mode 100644 libs/openal-soft/presets/rectangle.ambdec create mode 100644 libs/openal-soft/presets/square.ambdec create mode 100644 libs/openal-soft/router/al.c create mode 100644 libs/openal-soft/router/alc.c create mode 100644 libs/openal-soft/router/router.c create mode 100644 libs/openal-soft/router/router.h create mode 100644 libs/openal-soft/version.cmake create mode 100644 libs/openal-soft/version.h.in diff --git a/libs/openal-soft/.travis.yml b/libs/openal-soft/.travis.yml index dfae8e7a..e0a64c84 100644 --- a/libs/openal-soft/.travis.yml +++ b/libs/openal-soft/.travis.yml @@ -1,5 +1,70 @@ -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 +cache: + directories: + - $HOME/android-ndk-r14 +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 + if [[ ! -d ~/android-ndk-r14 || -z "$(ls -A ~/android-ndk-r14)" ]]; then + curl -o ~/android-ndk.zip https://dl.google.com/android/repository/android-ndk-r14-linux-x86_64.zip + unzip -q ~/android-ndk.zip -d ~ \ + 'android-ndk-r14/build/cmake/*' \ + 'android-ndk-r14/platforms/android-9/arch-arm/*' \ + 'android-ndk-r14/source.properties' \ + 'android-ndk-r14/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/*' \ + 'android-ndk-r14/sysroot/*' \ + 'android-ndk-r14/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/*' \ + 'android-ndk-r14/toolchains/llvm/prebuilt/linux-x86_64/*' + sed -i -e 's/VERSION 3.6.0/VERSION 3.2/' ~/android-ndk-r14/build/cmake/android.toolchain.cmake + fi + 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-r14/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/libs/openal-soft/Alc/ALc.c b/libs/openal-soft/Alc/ALc.c index d6d23eba..8c032ddf 100644 --- a/libs/openal-soft/Alc/ALc.c +++ b/libs/openal-soft/Alc/ALc.c @@ -20,6 +20,8 @@ #include "config.h" +#include "version.h" + #include #include #include @@ -35,12 +37,13 @@ #include "alBuffer.h" #include "alAuxEffectSlot.h" #include "alError.h" -#include "bs2b.h" +#include "bformatdec.h" #include "alu.h" #include "compat.h" #include "threads.h" #include "alstring.h" +#include "almalloc.h" #include "backends/base.h" @@ -51,61 +54,55 @@ 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 }, + { "mmdevapi", ALCmmdevBackendFactory_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 - { "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; @@ -115,18 +112,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), @@ -155,6 +145,8 @@ static const ALCfunction alcFunctions[] = { DECL(alcIsRenderFormatSupportedSOFT), DECL(alcRenderSamplesSOFT), + DECL(alcIsAmbisonicFormatSupportedSOFT), + DECL(alcDevicePauseSOFT), DECL(alcDeviceResumeSOFT), @@ -271,13 +263,6 @@ static const ALCfunction alcFunctions[] = { DECL(alGetAuxiliaryEffectSlotf), DECL(alGetAuxiliaryEffectSlotfv), - DECL(alBufferSubDataSOFT), - - DECL(alBufferSamplesSOFT), - DECL(alBufferSubSamplesSOFT), - DECL(alGetBufferSamplesSOFT), - DECL(alIsBufferFormatSupportedSOFT), - DECL(alDeferUpdatesSOFT), DECL(alProcessUpdatesSOFT), @@ -294,12 +279,19 @@ static const ALCfunction alcFunctions[] = { DECL(alGetSource3i64SOFT), DECL(alGetSourcei64vSOFT), - { NULL, NULL } + DECL(alBufferSamplesSOFT), + DECL(alGetBufferSamplesSOFT), + DECL(alIsBufferFormatSupportedSOFT), + + DECL(alGetStringiSOFT), }; #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), @@ -336,6 +328,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), @@ -358,6 +351,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), @@ -390,9 +393,7 @@ static const ALCenums enumeration[] = { DECL(AL_MAX_DISTANCE), DECL(AL_SEC_OFFSET), DECL(AL_SAMPLE_OFFSET), - DECL(AL_SAMPLE_RW_OFFSETS_SOFT), DECL(AL_BYTE_OFFSET), - DECL(AL_BYTE_RW_OFFSETS_SOFT), DECL(AL_SOURCE_TYPE), DECL(AL_STATIC), DECL(AL_STREAMING), @@ -460,6 +461,15 @@ static const ALCenums enumeration[] = { DECL(AL_FORMAT_MONO_ALAW_EXT), DECL(AL_FORMAT_STEREO_ALAW_EXT), + DECL(AL_FORMAT_BFORMAT2D_8), + DECL(AL_FORMAT_BFORMAT2D_16), + DECL(AL_FORMAT_BFORMAT2D_FLOAT32), + DECL(AL_FORMAT_BFORMAT2D_MULAW), + DECL(AL_FORMAT_BFORMAT3D_8), + DECL(AL_FORMAT_BFORMAT3D_16), + DECL(AL_FORMAT_BFORMAT3D_FLOAT32), + DECL(AL_FORMAT_BFORMAT3D_MULAW), + DECL(AL_MONO8_SOFT), DECL(AL_MONO16_SOFT), DECL(AL_MONO32F_SOFT), @@ -481,14 +491,12 @@ static const ALCenums enumeration[] = { DECL(AL_7POINT1_8_SOFT), DECL(AL_7POINT1_16_SOFT), DECL(AL_7POINT1_32F_SOFT), - DECL(AL_FORMAT_BFORMAT2D_8), - DECL(AL_FORMAT_BFORMAT2D_16), - DECL(AL_FORMAT_BFORMAT2D_FLOAT32), - DECL(AL_FORMAT_BFORMAT2D_MULAW), - DECL(AL_FORMAT_BFORMAT3D_8), - DECL(AL_FORMAT_BFORMAT3D_16), - DECL(AL_FORMAT_BFORMAT3D_FLOAT32), - DECL(AL_FORMAT_BFORMAT3D_MULAW), + DECL(AL_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), @@ -497,6 +505,8 @@ static const ALCenums enumeration[] = { 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), @@ -508,6 +518,7 @@ static const ALCenums enumeration[] = { DECL(AL_DOUBLE_SOFT), DECL(AL_BYTE3_SOFT), DECL(AL_UNSIGNED_BYTE3_SOFT), + DECL(AL_MULAW_SOFT), DECL(AL_FREQUENCY), DECL(AL_BITS), @@ -520,6 +531,10 @@ static const ALCenums enumeration[] = { DECL(AL_UNPACK_BLOCK_ALIGNMENT_SOFT), DECL(AL_PACK_BLOCK_ALIGNMENT_SOFT), + DECL(AL_SOURCE_RADIUS), + + DECL(AL_STEREO_ANGLES), + DECL(AL_UNUSED), DECL(AL_PENDING), DECL(AL_PROCESSED), @@ -542,6 +557,7 @@ static const ALCenums enumeration[] = { DECL(AL_SPEED_OF_SOUND), DECL(AL_SOURCE_DISTANCE_MODEL), DECL(AL_DEFERRED_UPDATES_SOFT), + DECL(AL_GAIN_LIMIT_SOFT), DECL(AL_INVERSE_DISTANCE), DECL(AL_INVERSE_DISTANCE_CLAMPED), @@ -588,6 +604,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), @@ -656,13 +677,6 @@ static const ALCenums enumeration[] = { DECL(AL_RING_MODULATOR_HIGHPASS_CUTOFF), DECL(AL_RING_MODULATOR_WAVEFORM), -#if 0 - DECL(AL_AUTOWAH_ATTACK_TIME), - DECL(AL_AUTOWAH_PEAK_GAIN), - DECL(AL_AUTOWAH_RELEASE_TIME), - DECL(AL_AUTOWAH_RESONANCE), -#endif - DECL(AL_COMPRESSOR_ONOFF), DECL(AL_EQUALIZER_LOW_GAIN), @@ -678,7 +692,13 @@ 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), }; #undef DECL @@ -709,10 +729,11 @@ 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_LOKI_quadriphonic AL_SOFT_block_alignment " - "AL_SOFT_buffer_samples AL_SOFT_buffer_sub_data AL_SOFT_deferred_updates " - "AL_SOFT_direct_channels AL_SOFT_loop_points AL_SOFT_MSADPCM " - "AL_SOFT_source_latency AL_SOFT_source_length"; + "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_SOFT_source_resampler AL_SOFT_source_spatialize"; static ATOMIC(ALCenum) LastNullDeviceError = ATOMIC_INIT_STATIC(ALC_NO_ERROR); @@ -756,7 +777,7 @@ 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_SOFT_loopback ALC_SOFT_output_limiter ALC_SOFT_pause_device"; static const ALCint alcMajorVersion = 1; static const ALCint alcMinorVersion = 1; @@ -772,13 +793,13 @@ static ATOMIC(ALCdevice*) DeviceList = ATOMIC_INIT_STATIC(NULL); static almtx_t ListLock; static inline void LockLists(void) { - int lockret = almtx_lock(&ListLock); - assert(lockret == althrd_success); + int ret = almtx_lock(&ListLock); + assert(ret == althrd_success); } static inline void UnlockLists(void) { - int unlockret = almtx_unlock(&ListLock); - assert(unlockret == althrd_success); + int ret = almtx_unlock(&ListLock); + assert(ret == althrd_success); } /************************************************ @@ -896,10 +917,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); } @@ -1029,26 +1055,22 @@ 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++) + 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++; @@ -1059,54 +1081,25 @@ 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(i = 0;i < BackendListSize && (!PlaybackBackend.name || !CaptureBackend.name);i++) { - if(BackendList[i].getFactory) - { - ALCbackendFactory *factory = BackendList[i].getFactory(); - if(!V0(factory,init)()) - { - WARN("Failed to initialize backend \"%s\"\n", BackendList[i].name); - continue; - } - - TRACE("Initialized backend \"%s\"\n", BackendList[i].name); - if(!PlaybackBackend.name && V(factory,querySupport)(ALCbackend_Playback)) - { - PlaybackBackend = BackendList[i]; - TRACE("Added \"%s\" for playback\n", PlaybackBackend.name); - } - if(!CaptureBackend.name && V(factory,querySupport)(ALCbackend_Capture)) - { - CaptureBackend = BackendList[i]; - TRACE("Added \"%s\" for capture\n", CaptureBackend.name); - } - - continue; - } - - if(!BackendList[i].Init(&BackendList[i].Funcs)) + 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(BackendList[i].Funcs.OpenPlayback && !PlaybackBackend.name) + if(!PlaybackBackend.name && V(factory,querySupport)(ALCbackend_Playback)) { PlaybackBackend = BackendList[i]; 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]; TRACE("Added \"%s\" for capture\n", CaptureBackend.name); @@ -1117,6 +1110,11 @@ static void alc_initconfig(void) V0(factory,init)(); } + if(!PlaybackBackend.name) + WARN("No playback backend available!\n"); + if(!CaptureBackend.name) + WARN("No capture backend available!\n"); + if(ConfigValueStr(NULL, NULL, "excludefx", &str)) { size_t len; @@ -1148,6 +1146,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 @@ -1164,7 +1231,7 @@ 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 { @@ -1201,15 +1268,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(); @@ -1225,18 +1287,15 @@ static void alc_deinit(void) ************************************************/ static void ProbeDevices(al_string *list, struct BackendInfo *backendinfo, enum DevProbe type) { + ALCbackendFactory *factory; + DO_INITCONFIG(); LockLists(); - al_string_clear(list); + alstr_clear(list); - if(!backendinfo->getFactory) - backendinfo->Probe(type); - else - { - ALCbackendFactory *factory = backendinfo->getFactory(); - V(factory,probe)(type); - } + factory = backendinfo->getFactory(); + V(factory,probe)(type); UnlockLists(); } @@ -1249,7 +1308,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); } @@ -1285,13 +1344,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 DevFmtBFormat3D: return "B-Format 3D"; + 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) { @@ -1305,7 +1364,7 @@ ALuint BytesFromDevFmt(enum DevFmtType type) } return 0; } -ALuint ChannelsFromDevFmt(enum DevFmtChannels chans) +ALsizei ChannelsFromDevFmt(enum DevFmtChannels chans, ALsizei ambiorder) { switch(chans) { @@ -1316,13 +1375,15 @@ ALuint ChannelsFromDevFmt(enum DevFmtChannels chans) case DevFmtX51Rear: return 6; case DevFmtX61: return 7; case DevFmtX71: return 8; - case DevFmtBFormat3D: return 4; + case DevFmtAmbi3D: return (ambiorder >= 3) ? 16 : + (ambiorder == 2) ? 9 : + (ambiorder == 1) ? 4 : 1; } return 0; } -DECL_CONST static ALboolean DecomposeDevFormat(ALenum format, - enum DevFmtChannels *chans, enum DevFmtType *type) +static ALboolean DecomposeDevFormat(ALenum format, enum DevFmtChannels *chans, + enum DevFmtType *type) { static const struct { ALenum format; @@ -1368,7 +1429,7 @@ DECL_CONST static ALboolean DecomposeDevFormat(ALenum format, return AL_FALSE; } -DECL_CONST static ALCboolean IsValidALCType(ALCenum type) +static ALCboolean IsValidALCType(ALCenum type) { switch(type) { @@ -1384,7 +1445,7 @@ DECL_CONST static ALCboolean IsValidALCType(ALCenum type) return ALC_FALSE; } -DECL_CONST static ALCboolean IsValidALCChannels(ALCenum channels) +static ALCboolean IsValidALCChannels(ALCenum channels) { switch(channels) { @@ -1394,23 +1455,38 @@ DECL_CONST 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 ************************************************/ -enum HrtfRequestMode { - Hrtf_Default = 0, - Hrtf_Enable = 1, - Hrtf_Disable = 2, -}; - -extern inline void LockContext(ALCcontext *context); -extern inline void UnlockContext(ALCcontext *context); void ALCdevice_Lock(ALCdevice *device) { @@ -1429,66 +1505,87 @@ void ALCdevice_Unlock(ALCdevice *device) */ void SetDefaultWFXChannelOrder(ALCdevice *device) { - ALuint i; + ALsizei i; for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) - device->ChannelName[i] = InvalidChannel; + device->RealOut.ChannelName[i] = InvalidChannel; switch(device->FmtChans) { case DevFmtMono: - device->ChannelName[0] = FrontCenter; + device->RealOut.ChannelName[0] = FrontCenter; break; case DevFmtStereo: - device->ChannelName[0] = FrontLeft; - device->ChannelName[1] = FrontRight; + device->RealOut.ChannelName[0] = FrontLeft; + device->RealOut.ChannelName[1] = FrontRight; break; case DevFmtQuad: - device->ChannelName[0] = FrontLeft; - device->ChannelName[1] = FrontRight; - device->ChannelName[2] = BackLeft; - device->ChannelName[3] = BackRight; + device->RealOut.ChannelName[0] = FrontLeft; + device->RealOut.ChannelName[1] = FrontRight; + device->RealOut.ChannelName[2] = BackLeft; + device->RealOut.ChannelName[3] = BackRight; break; case DevFmtX51: - device->ChannelName[0] = FrontLeft; - device->ChannelName[1] = FrontRight; - device->ChannelName[2] = FrontCenter; - device->ChannelName[3] = LFE; - device->ChannelName[4] = SideLeft; - device->ChannelName[5] = SideRight; + device->RealOut.ChannelName[0] = FrontLeft; + device->RealOut.ChannelName[1] = FrontRight; + device->RealOut.ChannelName[2] = FrontCenter; + device->RealOut.ChannelName[3] = LFE; + device->RealOut.ChannelName[4] = SideLeft; + device->RealOut.ChannelName[5] = SideRight; break; case DevFmtX51Rear: - device->ChannelName[0] = FrontLeft; - device->ChannelName[1] = FrontRight; - device->ChannelName[2] = FrontCenter; - device->ChannelName[3] = LFE; - device->ChannelName[4] = BackLeft; - device->ChannelName[5] = BackRight; + device->RealOut.ChannelName[0] = FrontLeft; + device->RealOut.ChannelName[1] = FrontRight; + device->RealOut.ChannelName[2] = FrontCenter; + device->RealOut.ChannelName[3] = LFE; + device->RealOut.ChannelName[4] = BackLeft; + device->RealOut.ChannelName[5] = BackRight; break; case DevFmtX61: - device->ChannelName[0] = FrontLeft; - device->ChannelName[1] = FrontRight; - device->ChannelName[2] = FrontCenter; - device->ChannelName[3] = LFE; - device->ChannelName[4] = BackCenter; - device->ChannelName[5] = SideLeft; - device->ChannelName[6] = SideRight; + device->RealOut.ChannelName[0] = FrontLeft; + device->RealOut.ChannelName[1] = FrontRight; + device->RealOut.ChannelName[2] = FrontCenter; + device->RealOut.ChannelName[3] = LFE; + device->RealOut.ChannelName[4] = BackCenter; + device->RealOut.ChannelName[5] = SideLeft; + device->RealOut.ChannelName[6] = SideRight; break; case DevFmtX71: - device->ChannelName[0] = FrontLeft; - device->ChannelName[1] = FrontRight; - device->ChannelName[2] = FrontCenter; - device->ChannelName[3] = LFE; - device->ChannelName[4] = BackLeft; - device->ChannelName[5] = BackRight; - device->ChannelName[6] = SideLeft; - device->ChannelName[7] = SideRight; + device->RealOut.ChannelName[0] = FrontLeft; + device->RealOut.ChannelName[1] = FrontRight; + device->RealOut.ChannelName[2] = FrontCenter; + device->RealOut.ChannelName[3] = LFE; + device->RealOut.ChannelName[4] = BackLeft; + device->RealOut.ChannelName[5] = BackRight; + device->RealOut.ChannelName[6] = SideLeft; + device->RealOut.ChannelName[7] = SideRight; break; - case DevFmtBFormat3D: - device->ChannelName[0] = BFormatW; - device->ChannelName[1] = BFormatX; - device->ChannelName[2] = BFormatY; - device->ChannelName[3] = BFormatZ; + case DevFmtAmbi3D: + device->RealOut.ChannelName[0] = Aux0; + if(device->AmbiOrder > 0) + { + device->RealOut.ChannelName[1] = Aux1; + device->RealOut.ChannelName[2] = Aux2; + device->RealOut.ChannelName[3] = Aux3; + } + if(device->AmbiOrder > 1) + { + device->RealOut.ChannelName[4] = Aux4; + device->RealOut.ChannelName[5] = Aux5; + device->RealOut.ChannelName[6] = Aux6; + device->RealOut.ChannelName[7] = Aux7; + device->RealOut.ChannelName[8] = Aux8; + } + if(device->AmbiOrder > 2) + { + device->RealOut.ChannelName[9] = Aux9; + device->RealOut.ChannelName[10] = Aux10; + device->RealOut.ChannelName[11] = Aux11; + device->RealOut.ChannelName[12] = Aux12; + device->RealOut.ChannelName[13] = Aux13; + device->RealOut.ChannelName[14] = Aux14; + device->RealOut.ChannelName[15] = Aux15; + } break; } } @@ -1499,30 +1596,30 @@ void SetDefaultWFXChannelOrder(ALCdevice *device) */ void SetDefaultChannelOrder(ALCdevice *device) { - ALuint i; + ALsizei i; for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) - device->ChannelName[i] = InvalidChannel; + device->RealOut.ChannelName[i] = InvalidChannel; switch(device->FmtChans) { case DevFmtX51Rear: - device->ChannelName[0] = FrontLeft; - device->ChannelName[1] = FrontRight; - device->ChannelName[2] = BackLeft; - device->ChannelName[3] = BackRight; - device->ChannelName[4] = FrontCenter; - device->ChannelName[5] = LFE; + device->RealOut.ChannelName[0] = FrontLeft; + device->RealOut.ChannelName[1] = FrontRight; + device->RealOut.ChannelName[2] = BackLeft; + device->RealOut.ChannelName[3] = BackRight; + device->RealOut.ChannelName[4] = FrontCenter; + device->RealOut.ChannelName[5] = LFE; return; case DevFmtX71: - device->ChannelName[0] = FrontLeft; - device->ChannelName[1] = FrontRight; - device->ChannelName[2] = BackLeft; - device->ChannelName[3] = BackRight; - device->ChannelName[4] = FrontCenter; - device->ChannelName[5] = LFE; - device->ChannelName[6] = SideLeft; - device->ChannelName[7] = SideRight; + device->RealOut.ChannelName[0] = FrontLeft; + device->RealOut.ChannelName[1] = FrontRight; + device->RealOut.ChannelName[2] = BackLeft; + device->RealOut.ChannelName[3] = BackRight; + device->RealOut.ChannelName[4] = FrontCenter; + device->RealOut.ChannelName[5] = LFE; + device->RealOut.ChannelName[6] = SideLeft; + device->RealOut.ChannelName[7] = SideRight; return; /* Same as WFX order */ @@ -1531,13 +1628,13 @@ void SetDefaultChannelOrder(ALCdevice *device) case DevFmtQuad: case DevFmtX51: case DevFmtX61: - case DevFmtBFormat3D: + case DevFmtAmbi3D: SetDefaultWFXChannelOrder(device); break; } } -extern inline ALint GetChannelIdxByName(const ALCdevice *device, enum Channel chan); +extern inline ALint GetChannelIndex(const enum Channel names[MAX_OUTPUT_CHANNELS], enum Channel chan); /* ALCcontext_DeferUpdates @@ -1548,28 +1645,7 @@ extern inline ALint GetChannelIdxByName(const ALCdevice *device, enum Channel ch */ void ALCcontext_DeferUpdates(ALCcontext *context) { - ALCdevice *device = context->Device; - FPUCtl oldMode; - - SetMixerFPUMode(&oldMode); - - V0(device->Backend,lock)(); - if(!context->DeferUpdates) - { - context->DeferUpdates = AL_TRUE; - - /* Make sure all pending updates are performed */ - UpdateContextSources(context); -#define UPDATE_SLOT(iter) do { \ - if(ATOMIC_EXCHANGE(ALenum, &(*iter)->NeedsUpdate, AL_FALSE)) \ - V((*iter)->EffectState,update)(device, *iter); \ -} while(0) - VECTOR_FOR_EACH(ALeffectslot*, context->ActiveAuxSlots, UPDATE_SLOT); -#undef UPDATE_SLOT - } - V0(device->Backend,unlock)(); - - RestoreFPUMode(&oldMode); + ATOMIC_STORE_SEQ(&context->DeferUpdates, AL_TRUE); } /* ALCcontext_ProcessUpdates @@ -1578,37 +1654,26 @@ void ALCcontext_DeferUpdates(ALCcontext *context) */ void ALCcontext_ProcessUpdates(ALCcontext *context) { - ALCdevice *device = context->Device; - - V0(device->Backend,lock)(); - if(context->DeferUpdates) + ReadLock(&context->PropLock); + if(ATOMIC_EXCHANGE_SEQ(&context->DeferUpdates, AL_FALSE)) { - ALsizei pos; + /* Tell the mixer to stop applying updates, then wait for any active + * updating to finish, before providing updates. + */ + ATOMIC_STORE_SEQ(&context->HoldUpdates, AL_TRUE); + while((ATOMIC_LOAD(&context->UpdateCount, almemory_order_acquire)&1) != 0) + althrd_yield(); - context->DeferUpdates = AL_FALSE; + UpdateListenerProps(context); + UpdateAllEffectSlotProps(context); + UpdateAllSourceProps(context); - LockUIntMapRead(&context->SourceMap); - for(pos = 0;pos < context->SourceMap.size;pos++) - { - ALsource *Source = context->SourceMap.array[pos].value; - ALenum new_state; - - if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) && - Source->Offset >= 0.0) - { - 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); - } - UnlockUIntMapRead(&context->SourceMap); + /* Now with all updates declared, let the mixer continue applying them + * so they all happen at once. + */ + ATOMIC_STORE_SEQ(&context->HoldUpdates, AL_FALSE); } - V0(device->Backend,unlock)(); + ReadUnlock(&context->PropLock); } @@ -1618,6 +1683,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 @@ -1630,22 +1696,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 @@ -1655,30 +1730,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) { @@ -1688,75 +1765,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]; - 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; + + case ALC_FORMAT_TYPE_SOFT: + stype = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_FORMAT_TYPE_SOFT, stype); + if(!IsValidALCType(stype)) + return ALC_INVALID_VALUE; + break; + + case ALC_FREQUENCY: + freq = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_FREQUENCY, freq); + if(freq < MIN_OUTPUT_RATE) + return ALC_INVALID_VALUE; + break; + + case ALC_AMBISONIC_LAYOUT_SOFT: + alayout = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_AMBISONIC_LAYOUT_SOFT, alayout); + if(!IsValidAmbiLayout(alayout)) + return ALC_INVALID_VALUE; + break; + + case ALC_AMBISONIC_SCALING_SOFT: + ascale = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_AMBISONIC_SCALING_SOFT, ascale); + if(!IsValidAmbiScaling(ascale)) + return ALC_INVALID_VALUE; + break; + + case ALC_AMBISONIC_ORDER_SOFT: + aorder = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_AMBISONIC_ORDER_SOFT, aorder); + if(aorder < 1 || aorder > MAX_AMBI_ORDER) + return ALC_INVALID_VALUE; + break; + + case ALC_MONO_SOURCES: + numMono = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_MONO_SOURCES, numMono); + numMono = maxi(numMono, 0); + break; + + case ALC_STEREO_SOURCES: + numStereo = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_STEREO_SOURCES, numStereo); + numStereo = maxi(numStereo, 0); + break; + + case ALC_MAX_AUXILIARY_SENDS: + numSends = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_MAX_AUXILIARY_SENDS, numSends); + numSends = clampi(numSends, 0, MAX_SENDS); + break; + + case ALC_HRTF_SOFT: + TRACE_ATTR(ALC_HRTF_SOFT, attrList[attrIdx + 1]); + if(attrList[attrIdx + 1] == ALC_FALSE) + hrtf_appreq = Hrtf_Disable; + else if(attrList[attrIdx + 1] == ALC_TRUE) + hrtf_appreq = Hrtf_Enable; + else + hrtf_appreq = Hrtf_Default; + break; + + case ALC_HRTF_ID_SOFT: + hrtf_id = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_HRTF_ID_SOFT, hrtf_id); + break; + + case ALC_OUTPUT_LIMITER_SOFT: + gainLimiter = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_OUTPUT_LIMITER_SOFT, gainLimiter); + break; + + default: + TRACE("Loopback 0x%04X = %d (0x%x)\n", attrList[attrIdx], + attrList[attrIdx + 1], attrList[attrIdx + 1]); + break; } - if(attrList[attrIdx] == ALC_FORMAT_TYPE_SOFT) - { - ALCint val = attrList[attrIdx + 1]; - if(!IsValidALCType(val) || !BytesFromDevFmt(val)) - return ALC_INVALID_VALUE; - stype = val; - gotFmt |= GotType; - } - - if(attrList[attrIdx] == ALC_FREQUENCY) - { - freq = attrList[attrIdx + 1]; - if(freq < MIN_OUTPUT_RATE) - return ALC_INVALID_VALUE; - gotFmt |= GotFreq; - } - - if(attrList[attrIdx] == ALC_STEREO_SOURCES) - { - numStereo = attrList[attrIdx + 1]; - if(numStereo > device->MaxNoOfSources) - numStereo = device->MaxNoOfSources; - - numMono = device->MaxNoOfSources - numStereo; - } - - if(attrList[attrIdx] == ALC_MAX_AUXILIARY_SENDS) - numSends = attrList[attrIdx + 1]; - - if(attrList[attrIdx] == ALC_HRTF_SOFT) - { - if(attrList[attrIdx + 1] == ALC_FALSE) - hrtf_appreq = Hrtf_Disable; - else if(attrList[attrIdx + 1] == ALC_TRUE) - hrtf_appreq = Hrtf_Enable; - else - hrtf_appreq = Hrtf_Default; - } - - if(attrList[attrIdx] == ALC_HRTF_ID_SOFT) - hrtf_id = attrList[attrIdx + 1]; - 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)(); @@ -1767,14 +1882,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. */ @@ -1782,55 +1923,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; + case ALC_FREQUENCY: + freq = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_FREQUENCY, freq); + device->Flags |= DEVICE_FREQUENCY_REQUEST; + break; + + case ALC_MONO_SOURCES: + numMono = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_MONO_SOURCES, numMono); + numMono = maxi(numMono, 0); + break; + + case ALC_STEREO_SOURCES: + numStereo = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_STEREO_SOURCES, numStereo); + numStereo = maxi(numStereo, 0); + break; + + case ALC_MAX_AUXILIARY_SENDS: + numSends = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_MAX_AUXILIARY_SENDS, numSends); + numSends = clampi(numSends, 0, MAX_SENDS); + break; + + case ALC_HRTF_SOFT: + TRACE_ATTR(ALC_HRTF_SOFT, attrList[attrIdx + 1]); + if(attrList[attrIdx + 1] == ALC_FALSE) + hrtf_appreq = Hrtf_Disable; + else if(attrList[attrIdx + 1] == ALC_TRUE) + hrtf_appreq = Hrtf_Enable; + else + hrtf_appreq = Hrtf_Default; + break; + + case ALC_HRTF_ID_SOFT: + hrtf_id = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_HRTF_ID_SOFT, hrtf_id); + break; + + case ALC_OUTPUT_LIMITER_SOFT: + gainLimiter = attrList[attrIdx + 1]; + TRACE_ATTR(ALC_OUTPUT_LIMITER_SOFT, gainLimiter); + break; + + default: + TRACE("0x%04X = %d (0x%x)\n", attrList[attrIdx], + attrList[attrIdx + 1], attrList[attrIdx + 1]); + break; } - if(attrList[attrIdx] == ALC_STEREO_SOURCES) - { - numStereo = attrList[attrIdx + 1]; - if(numStereo > device->MaxNoOfSources) - numStereo = device->MaxNoOfSources; - - numMono = device->MaxNoOfSources - numStereo; - } - - if(attrList[attrIdx] == ALC_MAX_AUXILIARY_SENDS) - numSends = attrList[attrIdx + 1]; - - if(attrList[attrIdx] == ALC_HRTF_SOFT) - { - if(attrList[attrIdx + 1] == ALC_FALSE) - hrtf_appreq = Hrtf_Disable; - else if(attrList[attrIdx + 1] == ALC_TRUE) - hrtf_appreq = Hrtf_Enable; - else - hrtf_appreq = Hrtf_Default; - } - - if(attrList[attrIdx] == ALC_HRTF_ID_SOFT) - hrtf_id = attrList[attrIdx + 1]; - 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 */ @@ -1838,24 +1999,66 @@ 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)) return ALC_NO_ERROR; - al_free(device->DryBuffer); - device->DryBuffer = NULL; + al_free(device->Uhj_Encoder); + device->Uhj_Encoder = NULL; + + 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; + device->FOAOut.Buffer = NULL; + device->FOAOut.NumChannels = 0; + device->RealOut.Buffer = NULL; + device->RealOut.NumChannels = 0; UpdateClockBase(device); - device->Hrtf_Status = ALC_HRTF_DISABLED_SOFT; + device->DitherSeed = DITHER_RNG_SEED; + + /************************************************************************* + * Update device format request if HRTF is requested + */ + device->HrtfStatus = ALC_HRTF_DISABLED_SOFT; if(device->Type != Loopback) { const char *hrtf; - if(ConfigValueStr(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; @@ -1867,57 +2070,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 = GetHrtfSampleRate(VECTOR_ELEM(device->Hrtf_List, hrtf_id).hrtf); - else - device->Frequency = GetHrtfSampleRate(VECTOR_ELEM(device->Hrtf_List, 0).hrtf); + 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_appreq = Hrtf_Default; - device->Hrtf_Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT; + hrtf_userreq = Hrtf_Default; + hrtf_appreq = Hrtf_Disable; + device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT; } } } - else if(hrtf_appreq == Hrtf_Enable) - { - size_t i; - /* 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. */ - if(device->FmtChans != DevFmtStereo) - i = VECTOR_SIZE(device->Hrtf_List); - else - { - 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(GetHrtfSampleRate(hrtf) == 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_Default; - device->Hrtf_Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT; - } - } oldFreq = device->Frequency; oldChans = device->FmtChans; @@ -1951,11 +2134,6 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) device->Flags &= ~DEVICE_FREQUENCY_REQUEST; } - TRACE("Post-reset: %s, %s, %uhz, %u update size x%d\n", - DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType), - device->Frequency, device->UpdateSize, device->NumUpdates - ); - if((device->UpdateSize&3) != 0) { if((CPUCapFlags&CPU_CAP_SSE)) @@ -1964,230 +2142,225 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) WARN("NEON performs best with multiple of 4 update sizes (%u)\n", device->UpdateSize); } - device->Hrtf = NULL; - device->Hrtf_Mode = DisabledHrtf; - al_string_clear(&device->Hrtf_Name); - if(device->FmtChans != DevFmtStereo) - { - if(hrtf_appreq == Hrtf_Enable) - device->Hrtf_Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT; + TRACE("Post-reset: %s, %s, %uhz, %u update size x%d\n", + DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType), + device->Frequency, device->UpdateSize, device->NumUpdates + ); - free(device->Bs2b); - device->Bs2b = NULL; - } - else - { - bool headphones = device->IsHeadphones; - enum HrtfMode hrtf_mode = FullHrtf; - ALCenum hrtf_status = device->Hrtf_Status; - const char *mode; - int bs2blevel; - int usehrtf; + 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); - if(device->Type != Loopback) - { - if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "stereo-mode", &mode)) - { - if(strcasecmp(mode, "headphones") == 0) - headphones = true; - else if(strcasecmp(mode, "speakers") == 0) - headphones = false; - else if(strcasecmp(mode, "auto") != 0) - ERR("Unexpected stereo-mode: %s\n", mode); - } + /* Allocate extra channels for any post-filter output. */ + size = (device->Dry.NumChannels + device->FOAOut.NumChannels + + device->RealOut.NumChannels)*sizeof(device->Dry.Buffer[0]); - if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "hrtf-mode", &mode)) - { - if(strcasecmp(mode, "full") == 0) - hrtf_mode = FullHrtf; - else if(strcasecmp(mode, "basic") == 0) - hrtf_mode = BasicHrtf; - else - ERR("Unexpected hrtf-mode: %s\n", mode); - } - } - - - if(hrtf_userreq == Hrtf_Default) - { - usehrtf = (headphones && hrtf_appreq != Hrtf_Disable) || - (hrtf_appreq == Hrtf_Enable); - if(headphones && hrtf_appreq != Hrtf_Disable) - hrtf_status = ALC_HRTF_HEADPHONES_DETECTED_SOFT; - else if(usehrtf) - hrtf_status = ALC_HRTF_ENABLED_SOFT; - } - else - { - usehrtf = (hrtf_userreq == Hrtf_Enable); - if(!usehrtf) - hrtf_status = ALC_HRTF_DENIED_SOFT; - else - hrtf_status = ALC_HRTF_REQUIRED_SOFT; - } - - if(!usehrtf) - device->Hrtf_Status = hrtf_status; - else - { - size_t i; - - device->Hrtf_Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT; - if(VECTOR_SIZE(device->Hrtf_List) == 0) - { - VECTOR_DEINIT(device->Hrtf_List); - device->Hrtf_List = EnumerateHrtf(device->DeviceName); - } - - if(hrtf_id >= 0 && (size_t)hrtf_id < VECTOR_SIZE(device->Hrtf_List)) - { - const HrtfEntry *entry = &VECTOR_ELEM(device->Hrtf_List, hrtf_id); - if(GetHrtfSampleRate(entry->hrtf) == device->Frequency) - { - device->Hrtf = entry->hrtf; - al_string_copy(&device->Hrtf_Name, entry->name); - } - } - if(!device->Hrtf) - { - for(i = 0;i < VECTOR_SIZE(device->Hrtf_List);i++) - { - const HrtfEntry *entry = &VECTOR_ELEM(device->Hrtf_List, i); - if(GetHrtfSampleRate(entry->hrtf) == device->Frequency) - { - device->Hrtf = entry->hrtf; - al_string_copy(&device->Hrtf_Name, entry->name); - break; - } - } - } - } - if(device->Hrtf) - { - device->Hrtf_Mode = hrtf_mode; - device->Hrtf_Status = hrtf_status; - TRACE("HRTF enabled, \"%s\"\n", al_string_get_cstr(device->Hrtf_Name)); - free(device->Bs2b); - device->Bs2b = NULL; - } - else - { - TRACE("HRTF disabled\n"); - - 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); - if(bs2blevel > 0 && bs2blevel <= 6) - { - if(!device->Bs2b) - { - device->Bs2b = calloc(1, sizeof(*device->Bs2b)); - bs2b_clear(device->Bs2b); - } - bs2b_set_params(device->Bs2b, bs2blevel, device->Frequency); - TRACE("BS2B enabled\n"); - } - else - { - free(device->Bs2b); - device->Bs2b = NULL; - TRACE("BS2B disabled\n"); - } - } - } - - aluInitPanning(device); - - /* With HRTF, allocate two extra channels for the post-filter output. */ - size = sizeof(device->DryBuffer[0]) * (device->NumChannels + (device->Hrtf ? 2 : 0)); - device->DryBuffer = al_calloc(16, size); - if(!device->DryBuffer) + 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) { ERR("Failed to allocate "SZFMT" bytes for mix buffer\n", size); return ALC_INVALID_DEVICE; } - SetMixerFPUMode(&oldMode); - V0(device->Backend,lock)(); - context = ATOMIC_LOAD(&device->ContextList); + if(device->RealOut.NumChannels != 0) + device->RealOut.Buffer = device->Dry.Buffer + device->Dry.NumChannels + + device->FOAOut.NumChannels; + else + { + device->RealOut.Buffer = device->Dry.Buffer; + device->RealOut.NumChannels = device->Dry.NumChannels; + } + + if(device->FOAOut.NumChannels != 0) + device->FOAOut.Buffer = device->Dry.Buffer + device->Dry.NumChannels; + else + { + device->FOAOut.Buffer = device->Dry.Buffer; + device->FOAOut.NumChannels = device->Dry.NumChannels; + } + + device->NumAuxSends = new_sends; + TRACE("Max sources: %d (%d + %d), effect slots: %d, sends: %d\n", + device->SourcesMax, device->NumMonoSources, device->NumStereoSources, + device->AuxiliaryEffectSlotMax, device->NumAuxSends); + + device->DitherDepth = 0.0f; + if(GetConfigValueBool(alstr_get_cstr(device->DeviceName), NULL, "dither", 1)) + { + ALint depth = 0; + ConfigValueInt(alstr_get_cstr(device->DeviceName), NULL, "dither-depth", &depth); + if(depth <= 0) + { + switch(device->FmtType) + { + case DevFmtByte: + case DevFmtUByte: + depth = 8; + break; + case DevFmtShort: + case DevFmtUShort: + depth = 16; + break; + case DevFmtInt: + case DevFmtUInt: + case DevFmtFloat: + break; + } + } + 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); + + 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"); + + /* 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) { ALsizei pos; - ATOMIC_STORE(&context->UpdateSources, AL_FALSE); + if(context->DefaultSlot) + { + ALeffectslot *slot = context->DefaultSlot; + ALeffectState *state = slot->Effect.State; + + state->OutBuffer = device->Dry.Buffer; + state->OutChannels = device->Dry.NumChannels; + if(V(state,deviceUpdate)(device) == AL_FALSE) + update_failed = AL_TRUE; + else + UpdateEffectSlotProps(slot); + } + + WriteLock(&context->PropLock); LockUIntMapRead(&context->EffectSlotMap); for(pos = 0;pos < context->EffectSlotMap.size;pos++) { - ALeffectslot *slot = context->EffectSlotMap.array[pos].value; + ALeffectslot *slot = context->EffectSlotMap.values[pos]; + ALeffectState *state = slot->Effect.State; - if(V(slot->EffectState,deviceUpdate)(device) == AL_FALSE) - { - UnlockUIntMapRead(&context->EffectSlotMap); - V0(device->Backend,unlock)(); - RestoreFPUMode(&oldMode); - return ALC_INVALID_DEVICE; - } - ATOMIC_STORE(&slot->NeedsUpdate, AL_FALSE); - V(slot->EffectState,update)(device, slot); + state->OutBuffer = device->Dry.Buffer; + state->OutChannels = device->Dry.NumChannels; + if(V(state,deviceUpdate)(device) == AL_FALSE) + update_failed = AL_TRUE; + else + UpdateEffectSlotProps(slot); } UnlockUIntMapRead(&context->EffectSlotMap); LockUIntMapRead(&context->SourceMap); + RelimitUIntMapNoLock(&context->SourceMap, device->SourcesMax); for(pos = 0;pos < context->SourceMap.size;pos++) { - ALsource *source = context->SourceMap.array[pos].value; - ALuint s = device->NumAuxSends; - while(s < MAX_SENDS) + ALsource *source = context->SourceMap.values[pos]; + + if(old_sends != device->NumAuxSends) { - if(source->Send[s].Slot) - DecrementRef(&source->Send[s].Slot->ref); - source->Send[s].Slot = NULL; - source->Send[s].Gain = 1.0f; - source->Send[s].GainHF = 1.0f; - s++; + 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); + } + AllocateVoices(context, context->MaxVoices, old_sends); + for(pos = 0;pos < context->VoiceCount;pos++) + { + ALvoice *voice = context->Voices[pos]; + struct ALvoiceProps *props; + + /* Clear any pre-existing voice property structs, in case the + * number of auxiliary sends changed. Active sources will have + * updates respecified in UpdateAllSourceProps. + */ + props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_relaxed); + al_free(props); + + props = ATOMIC_EXCHANGE_PTR(&voice->FreeList, NULL, almemory_order_relaxed); + while(props) + { + struct ALvoiceProps *next = ATOMIC_LOAD(&props->next, almemory_order_relaxed); + al_free(props); + props = next; + } + + 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++) + { + NfcFilterCreate1(&voice->Direct.Params[i].NFCtrlFilter[0], 0.0f, w1); + NfcFilterCreate2(&voice->Direct.Params[i].NFCtrlFilter[1], 0.0f, w1); + NfcFilterCreate3(&voice->Direct.Params[i].NFCtrlFilter[2], 0.0f, w1); + } } - ATOMIC_STORE(&source->NeedsUpdate, AL_TRUE); } UnlockUIntMapRead(&context->SourceMap); - for(pos = 0;pos < context->VoiceCount;pos++) - { - ALvoice *voice = &context->Voices[pos]; - ALsource *source = voice->Source; - ALuint s = device->NumAuxSends; - - while(s < MAX_SENDS) - { - voice->Send[s].Moving = AL_FALSE; - voice->Send[s].Counter = 0; - s++; - } - - if(source) - { - ATOMIC_STORE(&source->NeedsUpdate, AL_FALSE); - voice->Update(voice, source, context); - } - } + UpdateListenerProps(context); + UpdateAllSourceProps(context); + WriteUnlock(&context->PropLock); context = context->next; } - if(device->DefaultSlot) - { - ALeffectslot *slot = device->DefaultSlot; - - if(V(slot->EffectState,deviceUpdate)(device) == AL_FALSE) - { - V0(device->Backend,unlock)(); - RestoreFPUMode(&oldMode); - return ALC_INVALID_DEVICE; - } - ATOMIC_STORE(&slot->NeedsUpdate, AL_FALSE); - V(slot->EffectState,update)(device, slot); - } - V0(device->Backend,unlock)(); - RestoreFPUMode(&oldMode); + END_MIXER_MODE(); + if(update_failed) + return ALC_INVALID_DEVICE; if(!(device->Flags&DEVICE_PAUSED)) { @@ -2206,50 +2379,80 @@ 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); device->Backend = NULL; - if(device->DefaultSlot) - { - ALeffectState *state = device->DefaultSlot->EffectState; - device->DefaultSlot = NULL; - DELETE_OBJ(state); - } + almtx_destroy(&device->BackendLock); if(device->BufferMap.size > 0) { - WARN("(%p) Deleting %d Buffer(s)\n", device, device->BufferMap.size); + WARN("(%p) Deleting %d Buffer%s\n", device, device->BufferMap.size, + (device->BufferMap.size==1)?"":"s"); ReleaseALBuffers(device); } ResetUIntMap(&device->BufferMap); if(device->EffectMap.size > 0) { - WARN("(%p) Deleting %d Effect(s)\n", device, device->EffectMap.size); + WARN("(%p) Deleting %d Effect%s\n", device, device->EffectMap.size, + (device->EffectMap.size==1)?"":"s"); ReleaseALEffects(device); } ResetUIntMap(&device->EffectMap); if(device->FilterMap.size > 0) { - WARN("(%p) Deleting %d Filter(s)\n", device, device->FilterMap.size); + 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; - free(device->Bs2b); + al_free(device->Bs2b); device->Bs2b = NULL; + al_free(device->Uhj_Encoder); + device->Uhj_Encoder = NULL; + + bformatdec_free(device->AmbiDecoder); + device->AmbiDecoder = NULL; + + ambiup_free(device->AmbiUp); + device->AmbiUp = 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); - al_free(device->DryBuffer); - device->DryBuffer = NULL; + al_free(device->Dry.Buffer); + 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_free(device); } @@ -2279,7 +2482,7 @@ static ALCboolean VerifyDevice(ALCdevice **device) ALCdevice *tmpDevice; LockLists(); - tmpDevice = ATOMIC_LOAD(&DeviceList); + tmpDevice = ATOMIC_LOAD_SEQ(&DeviceList); while(tmpDevice) { if(tmpDevice == *device) @@ -2304,38 +2507,68 @@ 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; - aluVectorSet(&listener->Position, 0.0f, 0.0f, 0.0f, 1.0f); - aluVectorSet(&listener->Velocity, 0.0f, 0.0f, 0.0f, 0.0f); + listener->Position[0] = 0.0f; + listener->Position[1] = 0.0f; + listener->Position[2] = 0.0f; + listener->Velocity[0] = 0.0f; + listener->Velocity[1] = 0.0f; + listener->Velocity[2] = 0.0f; listener->Forward[0] = 0.0f; listener->Forward[1] = 0.0f; listener->Forward[2] = -1.0f; listener->Up[0] = 0.0f; listener->Up[1] = 1.0f; listener->Up[2] = 0.0f; - aluMatrixdSet(&listener->Params.Matrix, - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0 + + 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_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); ATOMIC_INIT(&Context->LastError, AL_NO_ERROR); - ATOMIC_INIT(&Context->UpdateSources, AL_FALSE); - InitUIntMap(&Context->SourceMap, Context->Device->MaxNoOfSources); + InitUIntMap(&Context->SourceMap, Context->Device->SourcesMax); InitUIntMap(&Context->EffectSlotMap, Context->Device->AuxiliaryEffectSlotMax); + 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; Context->SourceDistanceModel = AL_FALSE; Context->DopplerFactor = 1.0f; Context->DopplerVelocity = 1.0f; Context->SpeedOfSound = SPEEDOFSOUNDMETRESPERSEC; - Context->DeferUpdates = AL_FALSE; + ATOMIC_INIT(&Context->DeferUpdates, AL_FALSE); Context->ExtensionList = alExtList; } @@ -2348,28 +2581,61 @@ static ALvoid InitContext(ALCcontext *Context) */ static void FreeContext(ALCcontext *context) { + ALlistener *listener = context->Listener; + struct ALeffectslotArray *auxslots; + struct ALlistenerProps *lprops; + size_t count; + ALsizei i; + TRACE("%p\n", context); + if(context->DefaultSlot) + { + DeinitEffectSlot(context->DefaultSlot); + context->DefaultSlot = NULL; + } + + auxslots = ATOMIC_EXCHANGE_PTR(&context->ActiveAuxSlots, NULL, almemory_order_relaxed); + al_free(auxslots); + if(context->SourceMap.size > 0) { - WARN("(%p) Deleting %d Source(s)\n", context, context->SourceMap.size); + WARN("(%p) Deleting %d Source%s\n", context, context->SourceMap.size, + (context->SourceMap.size==1)?"":"s"); ReleaseALSources(context); } ResetUIntMap(&context->SourceMap); if(context->EffectSlotMap.size > 0) { - WARN("(%p) Deleting %d AuxiliaryEffectSlot(s)\n", context, context->EffectSlotMap.size); + WARN("(%p) Deleting %d AuxiliaryEffectSlot%s\n", context, context->EffectSlotMap.size, + (context->EffectSlotMap.size==1)?"":"s"); ReleaseALAuxiliaryEffectSlots(context); } ResetUIntMap(&context->EffectSlotMap); + for(i = 0;i < context->VoiceCount;i++) + DeinitVoice(context->Voices[i]); al_free(context->Voices); context->Voices = NULL; context->VoiceCount = 0; context->MaxVoices = 0; - VECTOR_DEINIT(context->ActiveAuxSlots); + if((lprops=ATOMIC_LOAD(&listener->Update, almemory_order_acquire)) != NULL) + { + TRACE("Freed unapplied listener update %p\n", lprops); + al_free(lprops); + } + count = 0; + lprops = ATOMIC_LOAD(&listener->FreeList, almemory_order_acquire); + while(lprops) + { + struct ALlistenerProps *next = ATOMIC_LOAD(&lprops->next, almemory_order_acquire); + al_free(lprops); + lprops = next; + ++count; + } + TRACE("Freed "SZFMT" listener property object%s\n", count, (count==1)?"":"s"); ALCdevice_DecRef(context->Device); context->Device = NULL; @@ -2382,12 +2648,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) { @@ -2397,44 +2664,52 @@ 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); origctx = context; - nextctx = context->next; - if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALCcontext*, &device->ContextList, &origctx, nextctx)) + newhead = context->next; + if(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&device->ContextList, &origctx, newhead)) { - ALCcontext *list; - do { - list = origctx; - origctx = context; - } while(!COMPARE_EXCHANGE(&list->next, &origctx, nextctx)); + ALCcontext *volatile*list = &origctx->next; + while(*list) + { + if(*list == context) + { + *list = (*list)->next; + break; + } + list = &(*list)->next; + } } + else + ret = !!newhead; ALCdevice_Unlock(device); ALCcontext_DecRef(context); + return ret; } void ALCcontext_IncRef(ALCcontext *context) { - uint ref; - ref = IncrementRef(&context->ref); + uint ref = IncrementRef(&context->ref); TRACEREF("%p increasing refcount to %u\n", context, ref); } void ALCcontext_DecRef(ALCcontext *context) { - uint ref; - ref = DecrementRef(&context->ref); + uint ref = DecrementRef(&context->ref); TRACEREF("%p decreasing refcount to %u\n", context, ref); if(ref == 0) FreeContext(context); } static void ReleaseThreadCtx(void *ptr) { - WARN("%p current for thread being destroyed\n", ptr); - ALCcontext_DecRef(ptr); + ALCcontext *context = ptr; + uint ref = DecrementRef(&context->ref); + TRACEREF("%p decreasing refcount to %u\n", context, ref); + ERR("Context %p current for thread being destroyed, possible leak!\n", context); } /* VerifyContext @@ -2446,10 +2721,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) @@ -2484,7 +2759,7 @@ ALCcontext *GetContextRef(void) else { LockLists(); - context = ATOMIC_LOAD(&GlobalContext); + context = ATOMIC_LOAD_SEQ(&GlobalContext); if(context) ALCcontext_IncRef(context); UnlockLists(); @@ -2494,6 +2769,92 @@ 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); + ATOMIC_INIT(&voice->FreeList, 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 ************************************************/ @@ -2508,11 +2869,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; } @@ -2596,26 +2957,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; @@ -2625,13 +2986,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); @@ -2640,13 +3001,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); @@ -2669,9 +3030,9 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum para alcSetError(NULL, ALC_INVALID_DEVICE); else { - LockLists(); - value = (Device->Hrtf ? al_string_get_cstr(Device->Hrtf_Name) : ""); - UnlockLists(); + almtx_lock(&Device->BackendLock); + value = (Device->HrtfHandle ? alstr_get_cstr(Device->HrtfName) : ""); + almtx_unlock(&Device->BackendLock); ALCdevice_DecRef(Device); } break; @@ -2687,6 +3048,13 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum para } +static inline ALCsizei NumAttrsForDevice(ALCdevice *device) +{ + if(device->Type == Loopback && device->FmtChans == DevFmtAmbi3D) + return 25; + return 19; +} + static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) { ALCsizei i; @@ -2718,6 +3086,9 @@ 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: alcSetError(NULL, ALC_INVALID_DEVICE); return 0; @@ -2733,9 +3104,9 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC switch(param) { case ALC_CAPTURE_SAMPLES: - V0(device->Backend,lock)(); + almtx_lock(&device->BackendLock); values[0] = V0(device->Backend,availableSamples)(); - V0(device->Backend,unlock)(); + almtx_unlock(&device->BackendLock); return 1; case ALC_CONNECTED: @@ -2769,17 +3140,18 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC 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; } i = 0; + almtx_lock(&device->BackendLock); values[i++] = ALC_FREQUENCY; values[i++] = device->Frequency; @@ -2793,6 +3165,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; @@ -2810,10 +3194,14 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC values[i++] = device->NumAuxSends; values[i++] = ALC_HRTF_SOFT; - values[i++] = (device->Hrtf ? 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; + almtx_unlock(&device->BackendLock); values[i++] = 0; return i; @@ -2828,7 +3216,9 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC alcSetError(device, ALC_INVALID_DEVICE); return 0; } + almtx_lock(&device->BackendLock); values[0] = device->Frequency / device->UpdateSize; + almtx_unlock(&device->BackendLock); return 1; case ALC_SYNC: @@ -2858,6 +3248,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; @@ -2875,17 +3292,23 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC return 1; case ALC_HRTF_SOFT: - values[0] = (device->Hrtf ? 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: - FreeHrtfList(&device->Hrtf_List); - device->Hrtf_List = EnumerateHrtf(device->DeviceName); - values[0] = (ALCint)VECTOR_SIZE(device->Hrtf_List); + almtx_lock(&device->BackendLock); + FreeHrtfList(&device->HrtfList); + device->HrtfList = EnumerateHrtf(device->DeviceName); + values[0] = (ALCint)VECTOR_SIZE(device->HrtfList); + almtx_unlock(&device->BackendLock); + return 1; + + case ALC_OUTPUT_LIMITER_SOFT: + values[0] = device->Limiter ? ALC_TRUE : ALC_FALSE; return 1; default: @@ -2927,20 +3350,24 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, } else /* render device */ { + ClockLatency clock; + ALuint64 basecount; + ALuint samplecount; + ALuint refcount; + switch(pname) { case ALC_ATTRIBUTES_SIZE: - *values = 19; + *values = NumAttrsForDevice(device)+4; break; case ALC_ALL_ATTRIBUTES: - if(size < 19) + if(size < NumAttrsForDevice(device)+4) alcSetError(device, ALC_INVALID_VALUE); else { - int i = 0; - - V0(device->Backend,lock)(); + i = 0; + almtx_lock(&device->BackendLock); values[i++] = ALC_FREQUENCY; values[i++] = device->Frequency; @@ -2954,6 +3381,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; @@ -2971,25 +3410,56 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, values[i++] = device->NumAuxSends; values[i++] = ALC_HRTF_SOFT; - values[i++] = (device->Hrtf ? 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; - values[i++] = device->ClockBase + - (device->SamplesDone * DEVICE_CLOCK_RES / device->Frequency); + values[i++] = clock.ClockTime; + + values[i++] = ALC_DEVICE_LATENCY_SOFT; + values[i++] = clock.Latency; + almtx_unlock(&device->BackendLock); values[i++] = 0; - V0(device->Backend,unlock)(); } break; case ALC_DEVICE_CLOCK_SOFT: - V0(device->Backend,lock)(); - *values = device->ClockBase + - (device->SamplesDone * DEVICE_CLOCK_RES / device->Frequency); - V0(device->Backend,unlock)(); + almtx_lock(&device->BackendLock); + do { + while(((refcount=ReadRef(&device->MixCount))&1) != 0) + althrd_yield(); + basecount = device->ClockBase; + samplecount = device->SamplesDone; + } while(refcount != ReadRef(&device->MixCount)); + *values = basecount + (samplecount*DEVICE_CLOCK_RES/device->Frequency); + almtx_unlock(&device->BackendLock); + break; + + case ALC_DEVICE_LATENCY_SOFT: + almtx_lock(&device->BackendLock); + clock = V0(device->Backend,getClockLatency)(); + almtx_unlock(&device->BackendLock); + *values = clock.Latency; + break; + + case ALC_DEVICE_CLOCK_LATENCY_SOFT: + if(size < 2) + alcSetError(device, ALC_INVALID_VALUE); + else + { + almtx_lock(&device->BackendLock); + clock = V0(device->Backend,getClockLatency)(); + almtx_unlock(&device->BackendLock); + values[0] = clock.ClockTime; + values[1] = clock.Latency; + } break; default: @@ -3060,10 +3530,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; @@ -3086,10 +3561,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; @@ -3103,8 +3583,13 @@ ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *e ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrList) { ALCcontext *ALContext; + ALfloat valf; ALCenum err; + /* Explicitly hold the list lock while taking the BackendLock in case the + * device is asynchronously destropyed, to ensure this new context is + * properly cleaned up after being made. + */ LockLists(); if(!VerifyDevice(&device) || device->Type == Capture || !device->Connected) { @@ -3113,12 +3598,41 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin if(device) ALCdevice_DecRef(device); return NULL; } + almtx_lock(&device->BackendLock); + UnlockLists(); - ATOMIC_STORE(&device->LastError, ALC_NO_ERROR); + ATOMIC_STORE_SEQ(&device->LastError, ALC_NO_ERROR); + + 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); + + 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; if((err=UpdateDeviceParams(device, attrList)) != ALC_NO_ERROR) { - UnlockLists(); + almtx_unlock(&device->BackendLock); + + al_free(ALContext); + ALContext = NULL; + alcSetError(device, err); if(err == ALC_INVALID_DEVICE) { @@ -3129,55 +3643,54 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin ALCdevice_DecRef(device); return NULL; } + AllocateVoices(ALContext, 256, device->NumAuxSends); - ALContext = al_calloc(16, sizeof(ALCcontext)+sizeof(ALlistener)); - if(ALContext) + if(DefaultEffect.type != AL_EFFECT_NULL && device->Type == Playback) { - InitRef(&ALContext->ref, 1); - ALContext->Listener = (ALlistener*)ALContext->_listener_mem; - - VECTOR_INIT(ALContext->ActiveAuxSlots); - - ALContext->VoiceCount = 0; - ALContext->MaxVoices = 256; - ALContext->Voices = al_calloc(16, ALContext->MaxVoices * sizeof(ALContext->Voices[0])); - } - if(!ALContext || !ALContext->Voices) - { - if(!ATOMIC_LOAD(&device->ContextList)) + ALContext->DefaultSlot = (ALeffectslot*)(ALContext->_listener_mem + sizeof(ALlistener)); + if(InitEffectSlot(ALContext->DefaultSlot) == AL_NO_ERROR) + aluInitEffectPanning(ALContext->DefaultSlot); + else { - V0(device->Backend,stop)(); - device->Flags &= ~DEVICE_RUNNING; + ALContext->DefaultSlot = NULL; + ERR("Failed to initialize the default effect slot\n"); } - UnlockLists(); - - if(ALContext) - { - al_free(ALContext->Voices); - ALContext->Voices = NULL; - - VECTOR_DEINIT(ALContext->ActiveAuxSlots); - - al_free(ALContext); - ALContext = NULL; - } - - alcSetError(device, ALC_OUT_OF_MEMORY); - ALCdevice_DecRef(device); - return NULL; } - ALContext->Device = device; - ALCdevice_IncRef(device); + ALCdevice_IncRef(ALContext->Device); InitContext(ALContext); + if(ConfigValueFloat(alstr_get_cstr(device->DeviceName), NULL, "volume-adjust", &valf)) { - ALCcontext *head = ATOMIC_LOAD(&device->ContextList); + if(!isfinite(valf)) + ERR("volume-adjust must be finite: %f\n", valf); + else + { + ALfloat db = clampf(valf, -24.0f, 24.0f); + if(db != valf) + WARN("volume-adjust clamped: %f, range: +/-%f\n", valf, 24.0f); + ALContext->GainBoost = powf(10.0f, db/20.0f); + TRACE("volume-adjust gain: %f\n", ALContext->GainBoost); + } + } + UpdateListenerProps(ALContext); + + { + ALCcontext *head = ATOMIC_LOAD_SEQ(&device->ContextList); do { ALContext->next = head; - } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCcontext*, &device->ContextList, &head, ALContext)); + } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK_SEQ(&device->ContextList, &head, + ALContext) == 0); + } + almtx_unlock(&device->BackendLock); + + if(ALContext->DefaultSlot) + { + if(InitializeEffect(device, ALContext->DefaultSlot, &DefaultEffect) == AL_NO_ERROR) + UpdateEffectSlotProps(ALContext->DefaultSlot); + else + ERR("Failed to initialize the default effect\n"); } - UnlockLists(); ALCdevice_DecRef(device); @@ -3194,18 +3707,27 @@ 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) { - ReleaseContext(context, Device); - if(!ATOMIC_LOAD(&Device->ContextList)) + almtx_lock(&Device->BackendLock); + if(!ReleaseContext(context, Device)) { V0(Device->Backend,stop)(); Device->Flags &= ~DEVICE_RUNNING; } + almtx_unlock(&Device->BackendLock); } UnlockLists(); + + ALCcontext_DecRef(context); } @@ -3216,7 +3738,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; } @@ -3244,7 +3766,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) @@ -3305,9 +3827,11 @@ 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; + ALCsizei i; DO_INITCONFIG(); @@ -3329,7 +3853,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); @@ -3344,41 +3868,54 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) device->Flags = 0; device->Bs2b = NULL; - VECTOR_INIT(device->Hrtf_List); - AL_STRING_INIT(device->Hrtf_Name); - device->Hrtf_Mode = DisabledHrtf; + device->Uhj_Encoder = NULL; + device->Hrtf = NULL; + device->HrtfHandle = NULL; + VECTOR_INIT(device->HrtfList); + AL_STRING_INIT(device->HrtfName); + device->Render_Mode = NormalRender; AL_STRING_INIT(device->DeviceName); - device->DryBuffer = NULL; + device->Dry.Buffer = NULL; + device->Dry.NumChannels = 0; + device->FOAOut.Buffer = NULL; + device->FOAOut.NumChannels = 0; + device->RealOut.Buffer = NULL; + device->RealOut.NumChannels = 0; + device->Limiter = NULL; + device->AvgSpeakerDist = 0.0f; ATOMIC_INIT(&device->ContextList, NULL); device->ClockBase = 0; device->SamplesDone = 0; - device->MaxNoOfSources = 256; - device->AuxiliaryEffectSlotMax = 4; - device->NumAuxSends = MAX_SENDS; + device->SourcesMax = 256; + device->AuxiliaryEffectSlotMax = 64; + device->NumAuxSends = DEFAULT_SENDS; - InitUIntMap(&device->BufferMap, ~0); - InitUIntMap(&device->EffectMap, ~0); - InitUIntMap(&device->FilterMap, ~0); + InitUIntMap(&device->BufferMap, INT_MAX); + InitUIntMap(&device->EffectMap, INT_MAX); + InitUIntMap(&device->FilterMap, INT_MAX); + + for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) + { + device->ChannelDelay[i].Gain = 1.0f; + device->ChannelDelay[i].Length = 0; + device->ChannelDelay[i].Buffer = NULL; + } //Set output format device->FmtChans = DevFmtChannelsDefault; device->FmtType = DevFmtTypeDefault; device->Frequency = DEFAULT_OUTPUT_RATE; device->IsHeadphones = AL_FALSE; - 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); - } + factory = PlaybackBackend.getFactory(); + device->Backend = V(factory,createBackend)(device, ALCbackend_Playback); if(!device->Backend) { al_free(device); @@ -3392,14 +3929,18 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) 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 }, + { "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; @@ -3408,6 +3949,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; } @@ -3460,17 +4002,19 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) if((CPUCapFlags&(CPU_CAP_SSE|CPU_CAP_NEON)) != 0) device->UpdateSize = (device->UpdateSize+3)&~3; - ConfigValueUInt(deviceName, NULL, "sources", &device->MaxNoOfSources); - if(device->MaxNoOfSources == 0) device->MaxNoOfSources = 256; + ConfigValueUInt(deviceName, NULL, "sources", &device->SourcesMax); + 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; - 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->MaxNoOfSources - device->NumStereoSources; + device->NumMonoSources = device->SourcesMax - device->NumStereoSources; // Find a playback device to open if((err=V(device->Backend,open)(deviceName)) != ALC_NO_ERROR) @@ -3480,32 +4024,39 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) alcSetError(NULL, err); return NULL; } + almtx_init(&device->BackendLock, almtx_plain); - if(DefaultEffect.type != AL_EFFECT_NULL) + if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "ambi-format", &fmt)) { - device->DefaultSlot = (ALeffectslot*)device->_slot_mem; - if(InitEffectSlot(device->DefaultSlot) != AL_NO_ERROR) + if(strcasecmp(fmt, "fuma") == 0) { - device->DefaultSlot = NULL; - ERR("Failed to initialize the default effect slot\n"); + device->AmbiLayout = AmbiLayout_FuMa; + device->AmbiScale = AmbiNorm_FuMa; } - else if(InitializeEffect(device, device->DefaultSlot, &DefaultEffect) != AL_NO_ERROR) + else if(strcasecmp(fmt, "acn+sn3d") == 0) { - ALeffectState *state = device->DefaultSlot->EffectState; - device->DefaultSlot = NULL; - DELETE_OBJ(state); - ERR("Failed to initialize the default effect\n"); + device->AmbiLayout = AmbiLayout_ACN; + device->AmbiScale = AmbiNorm_SN3D; } + else if(strcasecmp(fmt, "acn+n3d") == 0) + { + device->AmbiLayout = AmbiLayout_ACN; + device->AmbiScale = AmbiNorm_N3D; + } + else + ERR("Unsupported ambi-format: %s\n", fmt); } + 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)); + } 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; } @@ -3515,34 +4066,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; 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) + } while((iter=iter->next) != 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)) + if(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&DeviceList, &origdev, device->next)) { - do { - list = origdev; - origdev = device; - } while(!COMPARE_EXCHANGE(&list->next, &origdev, nextdev)); + ALCdevice *volatile*list = &origdev->next; + while(*list) + { + if(*list == device) + { + *list = (*list)->next; + break; + } + list = &(*list)->next; + } } UnlockLists(); - ctx = ATOMIC_LOAD(&device->ContextList); + ctx = ATOMIC_LOAD_SEQ(&device->ContextList); while(ctx != NULL) { ALCcontext *next = ctx->next; @@ -3553,6 +4110,7 @@ ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) if((device->Flags&DEVICE_RUNNING)) V0(device->Backend,stop)(); device->Flags &= ~DEVICE_RUNNING; + almtx_unlock(&device->BackendLock); ALCdevice_DecRef(device); @@ -3565,8 +4123,10 @@ 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; + ALCsizei i; DO_INITCONFIG(); @@ -3597,24 +4157,32 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, device->Connected = ALC_TRUE; device->Type = Capture; - VECTOR_INIT(device->Hrtf_List); - AL_STRING_INIT(device->Hrtf_Name); + device->Hrtf = NULL; + device->HrtfHandle = NULL; + VECTOR_INIT(device->HrtfList); + AL_STRING_INIT(device->HrtfName); AL_STRING_INIT(device->DeviceName); - device->DryBuffer = NULL; + 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); + InitUIntMap(&device->BufferMap, INT_MAX); + InitUIntMap(&device->EffectMap, INT_MAX); + InitUIntMap(&device->FilterMap, INT_MAX); - if(!CaptureBackend.getFactory) - device->Backend = create_backend_wrapper(device, &CaptureBackend.Funcs, - ALCbackend_Capture); - else + for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) { - ALCbackendFactory *factory = CaptureBackend.getFactory(); - device->Backend = V(factory,createBackend)(device, ALCbackend_Capture); + device->ChannelDelay[i].Gain = 1.0f; + device->ChannelDelay[i].Length = 0; + device->ChannelDelay[i].Buffer = NULL; } + + factory = CaptureBackend.getFactory(); + device->Backend = V(factory,createBackend)(device, ALCbackend_Capture); if(!device->Backend) { al_free(device); @@ -3633,53 +4201,66 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, return NULL; } device->IsHeadphones = AL_FALSE; + device->AmbiOrder = 0; + device->AmbiLayout = AmbiLayout_Default; + device->AmbiScale = AmbiNorm_Default; device->UpdateSize = samples; device->NumUpdates = 1; + 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); 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)); + } 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; 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) + } while((iter=iter->next) != 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; + if(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&DeviceList, &origdev, device->next)) { - do { - list = next; - next = device; - } while(!COMPARE_EXCHANGE(&list->next, &next, nextdev)); + ALCdevice *volatile*list = &origdev->next; + while(*list) + { + if(*list == device) + { + *list = (*list)->next; + break; + } + list = &(*list)->next; + } } UnlockLists(); @@ -3694,7 +4275,7 @@ ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) alcSetError(device, ALC_INVALID_DEVICE); else { - V0(device->Backend,lock)(); + almtx_lock(&device->BackendLock); if(!device->Connected) alcSetError(device, ALC_INVALID_DEVICE); else if(!(device->Flags&DEVICE_RUNNING)) @@ -3707,7 +4288,7 @@ ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) alcSetError(device, ALC_INVALID_DEVICE); } } - V0(device->Backend,unlock)(); + almtx_unlock(&device->BackendLock); } if(device) ALCdevice_DecRef(device); @@ -3719,11 +4300,11 @@ ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) alcSetError(device, ALC_INVALID_DEVICE); else { - V0(device->Backend,lock)(); + almtx_lock(&device->BackendLock); if((device->Flags&DEVICE_RUNNING)) V0(device->Backend,stop)(); device->Flags &= ~DEVICE_RUNNING; - V0(device->Backend,unlock)(); + almtx_unlock(&device->BackendLock); } if(device) ALCdevice_DecRef(device); @@ -3737,10 +4318,10 @@ ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, { ALCenum err = ALC_INVALID_VALUE; - V0(device->Backend,lock)(); + almtx_lock(&device->BackendLock); if(samples >= 0 && V0(device->Backend,availableSamples)() >= (ALCuint)samples) err = V(device->Backend,captureSamples)(buffer, samples); - V0(device->Backend,unlock)(); + almtx_unlock(&device->BackendLock); if(err != ALC_NO_ERROR) alcSetError(device, err); @@ -3761,6 +4342,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN { ALCbackendFactory *factory; ALCdevice *device; + ALCsizei i; DO_INITCONFIG(); @@ -3785,25 +4367,42 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN ATOMIC_INIT(&device->LastError, ALC_NO_ERROR); device->Flags = 0; - VECTOR_INIT(device->Hrtf_List); - AL_STRING_INIT(device->Hrtf_Name); + device->Hrtf = NULL; + device->HrtfHandle = NULL; + VECTOR_INIT(device->HrtfList); + AL_STRING_INIT(device->HrtfName); device->Bs2b = NULL; - device->Hrtf_Mode = DisabledHrtf; + device->Uhj_Encoder = NULL; + device->Render_Mode = NormalRender; AL_STRING_INIT(device->DeviceName); - device->DryBuffer = NULL; + device->Dry.Buffer = NULL; + device->Dry.NumChannels = 0; + device->FOAOut.Buffer = NULL; + device->FOAOut.NumChannels = 0; + device->RealOut.Buffer = NULL; + device->RealOut.NumChannels = 0; + device->Limiter = NULL; + device->AvgSpeakerDist = 0.0f; ATOMIC_INIT(&device->ContextList, NULL); device->ClockBase = 0; device->SamplesDone = 0; - device->MaxNoOfSources = 256; - device->AuxiliaryEffectSlotMax = 4; - device->NumAuxSends = MAX_SENDS; + device->SourcesMax = 256; + device->AuxiliaryEffectSlotMax = 64; + device->NumAuxSends = DEFAULT_SENDS; - InitUIntMap(&device->BufferMap, ~0); - InitUIntMap(&device->EffectMap, ~0); - InitUIntMap(&device->FilterMap, ~0); + InitUIntMap(&device->BufferMap, INT_MAX); + InitUIntMap(&device->EffectMap, INT_MAX); + InitUIntMap(&device->FilterMap, INT_MAX); + + for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) + { + device->ChannelDelay[i].Gain = 1.0f; + device->ChannelDelay[i].Length = 0; + device->ChannelDelay[i].Buffer = NULL; + } factory = ALCloopbackFactory_getFactory(); device->Backend = V(factory,createBackend)(device, ALCbackend_Loopback); @@ -3813,6 +4412,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN alcSetError(NULL, ALC_OUT_OF_MEMORY); return NULL; } + almtx_init(&device->BackendLock, almtx_plain); //Set output format device->NumUpdates = 0; @@ -3822,27 +4422,33 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN device->FmtChans = DevFmtChannelsDefault; device->FmtType = DevFmtTypeDefault; device->IsHeadphones = AL_FALSE; + device->AmbiLayout = AmbiLayout_Default; + device->AmbiScale = AmbiNorm_Default; - ConfigValueUInt(NULL, NULL, "sources", &device->MaxNoOfSources); - if(device->MaxNoOfSources == 0) device->MaxNoOfSources = 256; + 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; - 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->MaxNoOfSources - device->NumStereoSources; + device->NumMonoSources = device->SourcesMax - device->NumStereoSources; // 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)); + } while(!ATOMIC_COMPARE_EXCHANGE_PTR_WEAK_SEQ(&DeviceList, &head, device)); } TRACE("Created device %p\n", device); @@ -3863,9 +4469,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); @@ -3885,11 +4489,37 @@ 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); } +/************************************************ + * ALC loopback2 functions + ************************************************/ + +ALC_API ALCboolean ALC_APIENTRY alcIsAmbisonicFormatSupportedSOFT(ALCdevice *device, ALCenum layout, ALCenum scaling, ALsizei order) +{ + ALCboolean ret = ALC_FALSE; + + if(!VerifyDevice(&device) || device->Type != Loopback) + alcSetError(device, ALC_INVALID_DEVICE); + else if(order <= 0) + alcSetError(device, ALC_INVALID_VALUE); + else + { + if(IsValidAmbiLayout(layout) && IsValidAmbiScaling(scaling) && order <= MAX_AMBI_ORDER) + ret = ALC_TRUE; + } + if(device) ALCdevice_DecRef(device); + + return ret; +} + /************************************************ * ALC DSP pause/resume functions ************************************************/ @@ -3904,12 +4534,12 @@ ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device) alcSetError(device, ALC_INVALID_DEVICE); else { - LockLists(); + almtx_lock(&device->BackendLock); if((device->Flags&DEVICE_RUNNING)) V0(device->Backend,stop)(); device->Flags &= ~DEVICE_RUNNING; device->Flags |= DEVICE_PAUSED; - UnlockLists(); + almtx_unlock(&device->BackendLock); } if(device) ALCdevice_DecRef(device); } @@ -3924,11 +4554,11 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) alcSetError(device, ALC_INVALID_DEVICE); else { - LockLists(); + almtx_lock(&device->BackendLock); 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; @@ -3941,7 +4571,7 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) } } } - UnlockLists(); + almtx_unlock(&device->BackendLock); } if(device) ALCdevice_DecRef(device); } @@ -3964,8 +4594,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; @@ -3995,10 +4625,14 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi if(device) ALCdevice_DecRef(device); return ALC_FALSE; } + almtx_lock(&device->BackendLock); + UnlockLists(); - if((err=UpdateDeviceParams(device, attribs)) != ALC_NO_ERROR) + err = UpdateDeviceParams(device, attribs); + almtx_unlock(&device->BackendLock); + + if(err != ALC_NO_ERROR) { - UnlockLists(); alcSetError(device, err); if(err == ALC_INVALID_DEVICE) { @@ -4009,7 +4643,6 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi ALCdevice_DecRef(device); return ALC_FALSE; } - UnlockLists(); ALCdevice_DecRef(device); return ALC_TRUE; diff --git a/libs/openal-soft/Alc/ALu.c b/libs/openal-soft/Alc/ALu.c index 91c2aa7f..fa9634b8 100644 --- a/libs/openal-soft/Alc/ALu.c +++ b/libs/openal-soft/Alc/ALu.c @@ -34,6 +34,8 @@ #include "alu.h" #include "bs2b.h" #include "hrtf.h" +#include "uhjfilter.h" +#include "bformatdec.h" #include "static_assert.h" #include "mixer_defs.h" @@ -78,8 +80,7 @@ extern inline ALuint64 maxu64(ALuint64 a, ALuint64 b); extern inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max); extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu); -extern inline ALfloat 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 resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALsizei frac); extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w); @@ -91,50 +92,72 @@ extern inline void aluMatrixfSet(aluMatrixf *matrix, ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23, ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33); -extern inline void aluMatrixdSetRow(aluMatrixd *matrix, ALuint row, - ALdouble m0, ALdouble m1, ALdouble m2, ALdouble m3); -extern inline void aluMatrixdSet(aluMatrixd *matrix, - ALdouble m00, ALdouble m01, ALdouble m02, ALdouble m03, - ALdouble m10, ALdouble m11, ALdouble m12, ALdouble m13, - ALdouble m20, ALdouble m21, ALdouble m22, ALdouble m23, - ALdouble m30, ALdouble m31, ALdouble m32, ALdouble m33); +const aluMatrixf IdentityMatrixf = {{ + { 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 }, +}}; -/* NOTE: HRTF is set up a bit special in the device. By default, the device's - * DryBuffer, NumChannels, ChannelName, and Channel fields correspond to the - * output mixing format, and the DryBuffer is then converted and written to the - * backend's audio buffer. - * - * With HRTF, these fields correspond to a virtual format (typically B-Format), - * and the actual output is stored in DryBuffer[NumChannels] for the left - * channel and DryBuffer[NumChannels+1] for the right. As a final output step, - * the virtual channels will have HRTF applied and written to the actual - * output. Things like effects and B-Format decoding will want to write to the - * virtual channels so that they can be mixed with HRTF in full 3D. - * - * Sources that get mixed using HRTF directly (or that want to skip HRTF - * completely) will need to offset the output buffer so that they skip the - * virtual output and write to the actual output channels. This is the reason - * you'll see - * - * voice->Direct.OutBuffer += voice->Direct.OutChannels; - * voice->Direct.OutChannels = 2; - * - * at various points in the code where HRTF is explicitly used or bypassed. - */ - -static inline HrtfMixerFunc SelectHrtfMixer(void) +void DeinitVoice(ALvoice *voice) +{ + struct ALvoiceProps *props; + size_t count = 0; + + props = ATOMIC_EXCHANGE_PTR_SEQ(&voice->Update, NULL); + if(props) al_free(props); + + props = ATOMIC_EXCHANGE_PTR(&voice->FreeList, NULL, almemory_order_relaxed); + while(props) + { + struct ALvoiceProps *next; + next = ATOMIC_LOAD(&props->next, almemory_order_relaxed); + al_free(props); + props = next; + ++count; + } + /* This is excessively spammy if it traces every voice destruction, so just + * warn if it was unexpectedly large. + */ + if(count > 3) + WARN("Freed "SZFMT" voice property objects\n", count); +} + + +static inline HrtfDirectMixerFunc 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; + return MixDirectHrtf_Neon; +#endif +#ifdef HAVE_SSE + if((CPUCapFlags&CPU_CAP_SSE)) + return MixDirectHrtf_SSE; #endif - return MixHrtf_C; + 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; } @@ -150,7 +173,7 @@ static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2 return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2]; } -static inline ALfloat aluNormalize(ALfloat *vec) +static ALfloat aluNormalize(ALfloat *vec) { ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]); if(length > 0.0f) @@ -163,52 +186,22 @@ static inline ALfloat aluNormalize(ALfloat *vec) return length; } - -static inline void aluCrossproductd(const ALdouble *inVector1, const ALdouble *inVector2, ALdouble *outVector) +static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx) { - outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1]; - outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2]; - outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0]; -} - -static inline ALdouble aluNormalized(ALdouble *vec) -{ - ALdouble length = sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]); - if(length > 0.0) - { - ALdouble inv_length = 1.0/length; - vec[0] *= inv_length; - vec[1] *= inv_length; - vec[2] *= inv_length; - } - return length; -} - -static inline ALvoid aluMatrixdFloat3(ALfloat *vec, ALfloat w, const aluMatrixd *mtx) -{ - ALdouble v[4] = { vec[0], vec[1], vec[2], w }; - - vec[0] = (ALfloat)(v[0]*mtx->m[0][0] + v[1]*mtx->m[1][0] + v[2]*mtx->m[2][0] + v[3]*mtx->m[3][0]); - vec[1] = (ALfloat)(v[0]*mtx->m[0][1] + v[1]*mtx->m[1][1] + v[2]*mtx->m[2][1] + v[3]*mtx->m[3][1]); - vec[2] = (ALfloat)(v[0]*mtx->m[0][2] + v[1]*mtx->m[1][2] + v[2]*mtx->m[2][2] + v[3]*mtx->m[3][2]); -} - -static inline ALvoid aluMatrixdDouble3(ALdouble *vec, ALdouble w, const aluMatrixd *mtx) -{ - ALdouble v[4] = { vec[0], vec[1], vec[2], w }; + ALfloat v[4] = { vec[0], vec[1], vec[2], w }; vec[0] = v[0]*mtx->m[0][0] + v[1]*mtx->m[1][0] + v[2]*mtx->m[2][0] + v[3]*mtx->m[3][0]; vec[1] = v[0]*mtx->m[0][1] + v[1]*mtx->m[1][1] + v[2]*mtx->m[2][1] + v[3]*mtx->m[3][1]; vec[2] = v[0]*mtx->m[0][2] + v[1]*mtx->m[1][2] + v[2]*mtx->m[2][2] + v[3]*mtx->m[3][2]; } -static inline aluVector aluMatrixdVector(const aluMatrixd *mtx, const aluVector *vec) +static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec) { aluVector v; - v.v[0] = (ALfloat)(vec->v[0]*mtx->m[0][0] + vec->v[1]*mtx->m[1][0] + vec->v[2]*mtx->m[2][0] + vec->v[3]*mtx->m[3][0]); - v.v[1] = (ALfloat)(vec->v[0]*mtx->m[0][1] + vec->v[1]*mtx->m[1][1] + vec->v[2]*mtx->m[2][1] + vec->v[3]*mtx->m[3][1]); - v.v[2] = (ALfloat)(vec->v[0]*mtx->m[0][2] + vec->v[1]*mtx->m[1][2] + vec->v[2]*mtx->m[2][2] + vec->v[3]*mtx->m[3][2]); - v.v[3] = (ALfloat)(vec->v[0]*mtx->m[0][3] + vec->v[1]*mtx->m[1][3] + vec->v[2]*mtx->m[2][3] + vec->v[3]*mtx->m[3][3]); + v.v[0] = vec->v[0]*mtx->m[0][0] + vec->v[1]*mtx->m[1][0] + vec->v[2]*mtx->m[2][0] + vec->v[3]*mtx->m[3][0]; + v.v[1] = vec->v[0]*mtx->m[0][1] + vec->v[1]*mtx->m[1][1] + vec->v[2]*mtx->m[2][1] + vec->v[3]*mtx->m[3][1]; + v.v[2] = vec->v[0]*mtx->m[0][2] + vec->v[1]*mtx->m[1][2] + vec->v[2]*mtx->m[2][2] + vec->v[3]*mtx->m[3][2]; + v.v[3] = vec->v[0]*mtx->m[0][3] + vec->v[1]*mtx->m[1][3] + vec->v[2]*mtx->m[2][3] + vec->v[3]*mtx->m[3][3]; return v; } @@ -221,7 +214,7 @@ static inline aluVector aluMatrixdVector(const aluMatrixd *mtx, const aluVector * modified for use with an interpolated increment for buttery-smooth pitch * changes. */ -static ALboolean BsincPrepare(const ALuint increment, BsincState *state) +ALboolean BsincPrepare(const ALuint increment, BsincState *state) { 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 }; @@ -238,7 +231,7 @@ static ALboolean BsincPrepare(const ALuint increment, BsincState *state) { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 } }; ALfloat sf; - ALuint si, pi; + ALsizei si, pi; ALboolean uncut = AL_TRUE; if(increment > FRACTIONONE) @@ -256,7 +249,7 @@ static ALboolean BsincPrepare(const ALuint increment, BsincState *state) else { sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange; - si = fastf2u(sf); + si = fastf2i(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. @@ -287,1200 +280,1396 @@ static ALboolean BsincPrepare(const ALuint increment, BsincState *state) } -/* Calculates the fade time from the changes in gain and listener to source - * angle between updates. The result is a the time, in seconds, for the - * transition to complete. - */ -static ALfloat CalcFadeTime(ALfloat oldGain, ALfloat newGain, const aluVector *olddir, const aluVector *newdir) +static ALboolean CalcListenerParams(ALCcontext *Context) { - ALfloat gainChange, angleChange, change; + ALlistener *Listener = Context->Listener; + ALfloat N[3], V[3], U[3], P[3]; + struct ALlistenerProps *props; + aluVector vel; - /* Calculate the normalized dB gain change. */ - newGain = maxf(newGain, 0.0001f); - oldGain = maxf(oldGain, 0.0001f); - gainChange = fabsf(log10f(newGain / oldGain) / log10f(0.0001f)); - - /* Calculate the angle change only when there is enough gain to notice it. */ - angleChange = 0.0f; - if(gainChange > 0.0001f || newGain > 0.0001f) - { - /* No angle change when the directions are equal or degenerate (when - * both have zero length). - */ - if(newdir->v[0] != olddir->v[0] || newdir->v[1] != olddir->v[1] || newdir->v[2] != olddir->v[2]) - { - ALfloat dotp = aluDotproduct(olddir, newdir); - angleChange = acosf(clampf(dotp, -1.0f, 1.0f)) / F_PI; - } - } - - /* Use the largest of the two changes, and apply a significance shaping - * function to it. The result is then scaled to cover a 15ms transition - * range. - */ - change = maxf(angleChange * 25.0f, gainChange) * 2.0f; - return minf(change, 1.0f) * 0.015f; -} - - -static void UpdateDryStepping(DirectParams *params, ALuint num_chans, ALuint steps) -{ - ALfloat delta; - ALuint i, j; - - if(steps < 2) - { - for(i = 0;i < num_chans;i++) - { - MixGains *gains = params->Gains[i]; - for(j = 0;j < params->OutChannels;j++) - { - gains[j].Current = gains[j].Target; - gains[j].Step = 0.0f; - } - } - params->Counter = 0; - return; - } - - delta = 1.0f / (ALfloat)steps; - for(i = 0;i < num_chans;i++) - { - MixGains *gains = params->Gains[i]; - for(j = 0;j < params->OutChannels;j++) - { - ALfloat diff = gains[j].Target - gains[j].Current; - if(fabsf(diff) >= GAIN_SILENCE_THRESHOLD) - gains[j].Step = diff * delta; - else - { - gains[j].Current = gains[j].Target; - gains[j].Step = 0.0f; - } - } - } - params->Counter = steps; -} - -static void UpdateWetStepping(SendParams *params, ALuint num_chans, ALuint steps) -{ - ALfloat delta; - ALuint i; - - if(steps < 2) - { - for(i = 0;i < num_chans;i++) - { - params->Gains[i].Current = params->Gains[i].Target; - params->Gains[i].Step = 0.0f; - } - params->Counter = 0; - return; - } - - delta = 1.0f / (ALfloat)steps; - for(i = 0;i < num_chans;i++) - { - ALfloat diff = params->Gains[i].Target - params->Gains[i].Current; - if(fabsf(diff) >= GAIN_SILENCE_THRESHOLD) - params->Gains[i].Step = diff * delta; - else - { - params->Gains[i].Current = params->Gains[i].Target; - params->Gains[i].Step = 0.0f; - } - } - params->Counter = steps; -} - - -static ALvoid CalcListenerParams(ALlistener *Listener) -{ - ALdouble N[3], V[3], U[3], P[3]; + props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel); + if(!props) return AL_FALSE; /* AT then UP */ - N[0] = Listener->Forward[0]; - N[1] = Listener->Forward[1]; - N[2] = Listener->Forward[2]; - aluNormalized(N); - V[0] = Listener->Up[0]; - V[1] = Listener->Up[1]; - V[2] = Listener->Up[2]; - aluNormalized(V); + N[0] = props->Forward[0]; + N[1] = props->Forward[1]; + N[2] = props->Forward[2]; + aluNormalize(N); + V[0] = props->Up[0]; + V[1] = props->Up[1]; + V[2] = props->Up[2]; + aluNormalize(V); /* Build and normalize right-vector */ - aluCrossproductd(N, V, U); - aluNormalized(U); + aluCrossproduct(N, V, U); + aluNormalize(U); - aluMatrixdSet(&Listener->Params.Matrix, + aluMatrixfSet(&Listener->Params.Matrix, U[0], V[0], -N[0], 0.0, U[1], V[1], -N[1], 0.0, U[2], V[2], -N[2], 0.0, 0.0, 0.0, 0.0, 1.0 ); - P[0] = Listener->Position.v[0]; - P[1] = Listener->Position.v[1]; - P[2] = Listener->Position.v[2]; - aluMatrixdDouble3(P, 1.0, &Listener->Params.Matrix); - aluMatrixdSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f); + 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); - Listener->Params.Velocity = aluMatrixdVector(&Listener->Params.Matrix, &Listener->Velocity); + aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f); + Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel); + + Listener->Params.Gain = props->Gain * Context->GainBoost; + Listener->Params.MetersPerUnit = props->MetersPerUnit; + + Listener->Params.DopplerFactor = props->DopplerFactor; + Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity; + + Listener->Params.SourceDistanceModel = props->SourceDistanceModel; + Listener->Params.DistanceModel = props->DistanceModel; + + ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Listener->FreeList, props); + return AL_TRUE; } -ALvoid CalcNonAttnSourceParams(ALvoice *voice, const ALsource *ALSource, const ALCcontext *ALContext) +static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device) { - static const struct ChanMap MonoMap[1] = { - { FrontCenter, 0.0f, 0.0f } - }, StereoMap[2] = { + struct ALeffectslotProps *props; + ALeffectState *state; + + props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel); + if(!props) return AL_FALSE; + + slot->Params.Gain = props->Gain; + slot->Params.AuxSendAuto = props->AuxSendAuto; + slot->Params.EffectType = props->Type; + if(IsReverbEffect(slot->Params.EffectType)) + { + slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor; + slot->Params.DecayTime = props->Props.Reverb.DecayTime; + 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.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; + + V(state,update)(device, slot, &props->Props); + + ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &slot->FreeList, props); + return AL_TRUE; +} + + +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 void CalcPanningAndFilters(ALvoice *voice, const ALfloat Distance, const ALfloat *Dir, + 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) } - }, StereoWideMap[2] = { - { FrontLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) }, - { FrontRight, DEG2RAD( 90.0f), DEG2RAD(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) } }; - - ALCdevice *Device = ALContext->Device; - ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume; - ALbufferlistitem *BufferListItem; - enum FmtChannels Channels; - ALfloat DryGain, DryGainHF, DryGainLF; - ALfloat WetGain[MAX_SENDS]; - ALfloat WetGainHF[MAX_SENDS]; - ALfloat WetGainLF[MAX_SENDS]; - ALuint NumSends, Frequency; - ALboolean Relative; + bool DirectChannels = props->DirectChannels; + const ALsizei NumSends = Device->NumAuxSends; + const ALuint Frequency = Device->Frequency; const struct ChanMap *chans = NULL; - ALuint num_channels = 0; - ALboolean DirectChannels; - ALboolean isbformat = AL_FALSE; - ALfloat Pitch; - ALuint i, j, c; + ALsizei num_channels = 0; + bool isbformat = false; + ALfloat downmix_gain = 1.0f; + ALsizei c, i, j; - /* Get device properties */ - NumSends = Device->NumAuxSends; - Frequency = Device->Frequency; - - /* Get listener properties */ - ListenerGain = ALContext->Listener->Gain; - - /* Get source properties */ - SourceVolume = ALSource->Gain; - MinVolume = ALSource->MinGain; - MaxVolume = ALSource->MaxGain; - Pitch = ALSource->Pitch; - Relative = ALSource->HeadRelative; - DirectChannels = ALSource->DirectChannels; - - voice->Direct.OutBuffer = Device->DryBuffer; - voice->Direct.OutChannels = Device->NumChannels; - for(i = 0;i < NumSends;i++) - { - ALeffectslot *Slot = ALSource->Send[i].Slot; - if(!Slot && i == 0) - Slot = Device->DefaultSlot; - if(!Slot || Slot->EffectType == AL_EFFECT_NULL) - voice->Send[i].OutBuffer = NULL; - else - voice->Send[i].OutBuffer = Slot->WetBuffer; - } - - /* Calculate the stepping value */ - Channels = FmtMono; - BufferListItem = ATOMIC_LOAD(&ALSource->queue); - while(BufferListItem != NULL) - { - ALbuffer *ALBuffer; - if((ALBuffer=BufferListItem->buffer) != NULL) - { - Pitch = Pitch * ALBuffer->Frequency / Frequency; - if(Pitch > (ALfloat)MAX_PITCH) - voice->Step = MAX_PITCH<Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1); - BsincPrepare(voice->Step, &voice->SincState); - - Channels = ALBuffer->FmtChannels; - break; - } - BufferListItem = BufferListItem->next; - } - - /* Calculate gains */ - DryGain = clampf(SourceVolume, MinVolume, MaxVolume); - DryGain *= ALSource->Direct.Gain * ListenerGain; - DryGainHF = ALSource->Direct.GainHF; - DryGainLF = ALSource->Direct.GainLF; - for(i = 0;i < NumSends;i++) - { - WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume); - WetGain[i] *= ALSource->Send[i].Gain * ListenerGain; - WetGainHF[i] = ALSource->Send[i].GainHF; - WetGainLF[i] = ALSource->Send[i].GainLF; - } - - switch(Channels) + switch(Buffer->FmtChannels) { case FmtMono: chans = MonoMap; num_channels = 1; + /* Mono buffers are never played direct. */ + DirectChannels = false; break; case FmtStereo: - /* HACK: Place the stereo channels at +/-90 degrees when using non- - * HRTF stereo output. This helps reduce the "monoization" caused - * by them panning towards the center. */ - if(Device->FmtChans == DevFmtStereo && !Device->Hrtf) - chans = StereoWideMap; - else - chans = StereoMap; + /* 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; } + 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] = ALSource->Orientation[0][0]; - N[1] = ALSource->Orientation[0][1]; - N[2] = ALSource->Orientation[0][2]; - aluNormalize(N); - V[0] = ALSource->Orientation[1][0]; - V[1] = ALSource->Orientation[1][1]; - V[2] = ALSource->Orientation[1][2]; - aluNormalize(V); - if(!Relative) + if(Distance > FLT_EPSILON) { - const aluMatrixd *lmatrix = &ALContext->Listener->Params.Matrix; - aluMatrixdFloat3(N, 0.0f, lmatrix); - aluMatrixdFloat3(V, 0.0f, lmatrix); - } - /* Build and normalize right-vector */ - aluCrossproduct(N, V, U); - aluNormalize(U); - - /* Build a rotate + conversion matrix (B-Format -> N3D), and include - * scaling for first-order content. */ - scale = Device->AmbiScale * 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 - ); - - for(c = 0;c < num_channels;c++) - { - MixGains *gains = voice->Direct.Gains[c]; - ALfloat Target[MAX_OUTPUT_CHANNELS]; - - ComputeBFormatGains(Device, matrix.m[c], DryGain, Target); - for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) - gains[i].Target = Target[i]; - } - UpdateDryStepping(&voice->Direct, num_channels, (voice->Direct.Moving ? 64 : 0)); - voice->Direct.Moving = AL_TRUE; - - voice->IsHrtf = AL_FALSE; - - for(i = 0;i < NumSends;i++) - { - /* Only the first channel of B-Format buffers (W) goes to auxiliary - * sends. It also needs to be scaled by sqrt(2) to account for the - * signal being scaled by sqrt(1/2). + /* 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->Send[i].Gains[0].Target = WetGain[i] * 1.414213562f; - for(c = 1;c < num_channels;c++) - voice->Send[i].Gains[c].Target = 0.0f; - UpdateWetStepping(&voice->Send[i], num_channels, (voice->Send[i].Moving ? 64 : 0)); - voice->Send[i].Moving = AL_TRUE; - } - } - else - { - if(DirectChannels) - { - if(Device->Hrtf) + ALfloat coeffs[MAX_AMBI_COEFFS]; + + if(Device->AvgSpeakerDist > 0.0f && Listener->Params.MetersPerUnit > 0.0f) { - /* DirectChannels with HRTF enabled. Skip the virtual channels - * and write FrontLeft and FrontRight inputs to the first and - * second outputs. + 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. */ - voice->Direct.OutBuffer += voice->Direct.OutChannels; - voice->Direct.OutChannels = 2; - for(c = 0;c < num_channels;c++) - { - MixGains *gains = voice->Direct.Gains[c]; + w0 = minf(w0, w1*4.0f); - for(j = 0;j < MAX_OUTPUT_CHANNELS;j++) - gains[j].Target = 0.0f; + /* Only need to adjust the first channel of a B-Format source. */ + NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], w0); + NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], w0); + NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], w0); - if(chans[c].channel == FrontLeft) - gains[0].Target = DryGain; - else if(chans[c].channel == FrontRight) - gains[1].Target = DryGain; - } + for(i = 0;i < MAX_AMBI_ORDER+1;i++) + voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i]; + voice->Flags |= VOICE_HAS_NFC; } - else for(c = 0;c < num_channels;c++) - { - MixGains *gains = voice->Direct.Gains[c]; - int idx; + if(Device->Render_Mode == StereoPair) + { + ALfloat ev = asinf(Dir[1]); + ALfloat az = atan2f(Dir[0], -Dir[2]); + CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs); + } + else + CalcDirectionCoeffs(Dir, Spread, coeffs); + + /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */ + ComputePanningGains(Device->Dry, coeffs, DryGain*1.414213562f, + voice->Direct.Params[0].Gains.Target); + for(c = 1;c < num_channels;c++) + { for(j = 0;j < MAX_OUTPUT_CHANNELS;j++) - gains[j].Target = 0.0f; - if((idx=GetChannelIdxByName(Device, chans[c].channel)) != -1) - gains[idx].Target = DryGain; + voice->Direct.Params[c].Gains.Target[j] = 0.0f; } - UpdateDryStepping(&voice->Direct, num_channels, (voice->Direct.Moving ? 64 : 0)); - voice->Direct.Moving = AL_TRUE; - voice->IsHrtf = AL_FALSE; - } - else if(Device->Hrtf_Mode == FullHrtf) - { - /* Full HRTF rendering. Skip the virtual channels and render each - * input channel to the real outputs. - */ - voice->Direct.OutBuffer += voice->Direct.OutChannels; - voice->Direct.OutChannels = 2; - for(c = 0;c < num_channels;c++) + for(i = 0;i < NumSends;i++) { - if(chans[c].channel == LFE) - { - /* Skip LFE */ - voice->Direct.Hrtf[c].Params.Delay[0] = 0; - voice->Direct.Hrtf[c].Params.Delay[1] = 0; - for(i = 0;i < HRIR_LENGTH;i++) - { - voice->Direct.Hrtf[c].Params.Coeffs[i][0] = 0.0f; - voice->Direct.Hrtf[c].Params.Coeffs[i][1] = 0.0f; - } - } - else - { - /* Get the static HRIR coefficients and delays for this - * channel. */ - GetLerpedHrtfCoeffs(Device->Hrtf, - chans[c].elevation, chans[c].angle, 1.0f, DryGain, - voice->Direct.Hrtf[c].Params.Coeffs, - voice->Direct.Hrtf[c].Params.Delay + 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 + for(j = 0;j < MAX_EFFECT_CHANNELS;j++) + voice->Send[i].Params[0].Gains.Target[j] = 0.0f; + for(c = 1;c < num_channels;c++) + { + for(j = 0;j < MAX_EFFECT_CHANNELS;j++) + voice->Send[i].Params[c].Gains.Target[j] = 0.0f; } } - voice->Direct.Counter = 0; - voice->Direct.Moving = AL_TRUE; - - voice->IsHrtf = AL_TRUE; } else { - /* Basic or no HRTF rendering. Use normal panning to the output. */ + /* Local B-Format sources have their XYZ channels rotated according + * to the orientation. + */ + ALfloat N[3], V[3], U[3]; + aluMatrixf matrix; + ALfloat scale; + + 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. + */ + NfcFilterAdjust1(&voice->Direct.Params[0].NFCtrlFilter[0], 0.0f); + NfcFilterAdjust2(&voice->Direct.Params[0].NFCtrlFilter[1], 0.0f); + NfcFilterAdjust3(&voice->Direct.Params[0].NFCtrlFilter[2], 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[2] = 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). */ + 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->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 + { + for(c = 0;c < num_channels;c++) + for(j = 0;j < MAX_EFFECT_CHANNELS;j++) + voice->Send[i].Params[c].Gains.Target[j] = 0.0f; + } + } + } + } + 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; + for(j = 0;j < MAX_OUTPUT_CHANNELS;j++) + voice->Direct.Params[c].Gains.Target[j] = 0.0f; + if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -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 + for(j = 0;j < MAX_EFFECT_CHANNELS;j++) + voice->Send[i].Params[c].Gains.Target[j] = 0.0f; + } + } + } + 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]; + ALfloat ev, az; + + ev = asinf(Dir[1]); + az = atan2f(Dir[0], -Dir[2]); + + /* Get the HRIR coefficients and delays just once, for the given + * source direction. + */ + GetHrtfCoeffs(Device->HrtfHandle, ev, az, 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) + memset(&voice->Direct.Params[c].Hrtf.Target, 0, + sizeof(voice->Direct.Params[c].Hrtf.Target)); + else + 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. + */ + CalcDirectionCoeffs(Dir, 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) + for(j = 0;j < MAX_EFFECT_CHANNELS;j++) + voice->Send[i].Params[c].Gains.Target[j] = 0.0f; + else + ComputePanningGainsBF(Slot->ChanMap, + Slot->NumChannels, coeffs, WetGain[i] * downmix_gain, + voice->Send[i].Params[c].Gains.Target + ); + } + else + for(j = 0;j < MAX_EFFECT_CHANNELS;j++) + voice->Send[i].Params[c].Gains.Target[j] = 0.0f; + } + } + 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++) { - MixGains *gains = voice->Direct.Gains[c]; - ALfloat Target[MAX_OUTPUT_CHANNELS]; + ALfloat coeffs[MAX_AMBI_COEFFS]; + + if(chans[c].channel == LFE) + { + /* Skip LFE */ + memset(&voice->Direct.Params[c].Hrtf.Target, 0, + sizeof(voice->Direct.Params[c].Hrtf.Target)); + for(i = 0;i < NumSends;i++) + { + for(j = 0;j < MAX_EFFECT_CHANNELS;j++) + voice->Send[i].Params[c].Gains.Target[j] = 0.0f; + } + 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 + ); + else + for(j = 0;j < MAX_EFFECT_CHANNELS;j++) + voice->Send[i].Params[c].Gains.Target[j] = 0.0f; + } + } + } + + 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 && Listener->Params.MetersPerUnit > 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); + + 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) + { + ALfloat ev = asinf(Dir[1]); + ALfloat az = atan2f(Dir[0], -Dir[2]); + CalcAnglePairwiseCoeffs(az, ev, Spread, coeffs); + } + else + CalcDirectionCoeffs(Dir, Spread, coeffs); + + for(c = 0;c < num_channels;c++) + { + /* Adjust NFC filters if needed. */ + if((voice->Flags&VOICE_HAS_NFC)) + { + NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0); + NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0); + NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0); + } /* Special-case LFE */ if(chans[c].channel == LFE) { - int idx; - for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) - gains[i].Target = 0.0f; - if((idx=GetChannelIdxByName(Device, chans[c].channel)) != -1) - gains[idx].Target = DryGain; + for(j = 0;j < MAX_OUTPUT_CHANNELS;j++) + voice->Direct.Params[c].Gains.Target[j] = 0.0f; + 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; } - ComputeAngleGains(Device, chans[c].angle, chans[c].elevation, DryGain, Target); - for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) - gains[i].Target = Target[i]; + ComputePanningGains(Device->Dry, + coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target + ); } - UpdateDryStepping(&voice->Direct, num_channels, (voice->Direct.Moving ? 64 : 0)); - voice->Direct.Moving = AL_TRUE; - voice->IsHrtf = AL_FALSE; + 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) + for(j = 0;j < MAX_EFFECT_CHANNELS;j++) + voice->Send[i].Params[c].Gains.Target[j] = 0.0f; + else + ComputePanningGainsBF(Slot->ChanMap, + Slot->NumChannels, coeffs, WetGain[i] * downmix_gain, + voice->Send[i].Params[c].Gains.Target + ); + } + else + for(c = 0;c < num_channels;c++) + { + for(j = 0;j < MAX_EFFECT_CHANNELS;j++) + voice->Send[i].Params[c].Gains.Target[j] = 0.0f; + } + } } - for(i = 0;i < NumSends;i++) + 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(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++) - voice->Send[i].Gains[c].Target = WetGain[i]; - UpdateWetStepping(&voice->Send[i], num_channels, (voice->Send[i].Moving ? 64 : 0)); - voice->Send[i].Moving = AL_TRUE; + { + ALfloat coeffs[MAX_AMBI_COEFFS]; + + if((voice->Flags&VOICE_HAS_NFC)) + { + NfcFilterAdjust1(&voice->Direct.Params[c].NFCtrlFilter[0], w0); + NfcFilterAdjust2(&voice->Direct.Params[c].NFCtrlFilter[1], w0); + NfcFilterAdjust3(&voice->Direct.Params[c].NFCtrlFilter[2], w0); + } + + /* Special-case LFE */ + if(chans[c].channel == LFE) + { + for(j = 0;j < MAX_OUTPUT_CHANNELS;j++) + voice->Direct.Params[c].Gains.Target[j] = 0.0f; + 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; + } + + for(i = 0;i < NumSends;i++) + { + for(j = 0;j < MAX_EFFECT_CHANNELS;j++) + voice->Send[i].Params[c].Gains.Target[j] = 0.0f; + } + continue; + } + + if(Device->Render_Mode == StereoPair) + CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs); + else + CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs); + ComputePanningGains(Device->Dry, + coeffs, DryGain, voice->Direct.Params[c].Gains.Target + ); + + 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 + for(j = 0;j < MAX_EFFECT_CHANNELS;j++) + voice->Send[i].Params[c].Gains.Target[j] = 0.0f; + } + } } } { - ALfloat hfscale = ALSource->Direct.HFReference / Frequency; - ALfloat lfscale = ALSource->Direct.LFReference / 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; + ALfilterState_setParams( + &voice->Direct.Params[0].LowPass, ALfilterType_HighShelf, + gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f) + ); + ALfilterState_setParams( + &voice->Direct.Params[0].HighPass, ALfilterType_LowShelf, + gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f) + ); + for(c = 1;c < num_channels;c++) { - voice->Direct.Filters[c].ActiveType = AF_None; - if(DryGainHF != 1.0f) voice->Direct.Filters[c].ActiveType |= AF_LowPass; - if(DryGainLF != 1.0f) voice->Direct.Filters[c].ActiveType |= AF_HighPass; - ALfilterState_setParams( - &voice->Direct.Filters[c].LowPass, ALfilterType_HighShelf, - DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f) - ); - ALfilterState_setParams( - &voice->Direct.Filters[c].HighPass, ALfilterType_LowShelf, - DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f) - ); + ALfilterState_copyParams(&voice->Direct.Params[c].LowPass, + &voice->Direct.Params[0].LowPass); + ALfilterState_copyParams(&voice->Direct.Params[c].HighPass, + &voice->Direct.Params[0].HighPass); } } for(i = 0;i < NumSends;i++) { - ALfloat hfscale = ALSource->Send[i].HFReference / Frequency; - ALfloat lfscale = ALSource->Send[i].LFReference / 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; + ALfilterState_setParams( + &voice->Send[i].Params[0].LowPass, ALfilterType_HighShelf, + gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f) + ); + ALfilterState_setParams( + &voice->Send[i].Params[0].HighPass, ALfilterType_LowShelf, + gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f) + ); + for(c = 1;c < num_channels;c++) { - voice->Send[i].Filters[c].ActiveType = AF_None; - if(WetGainHF[i] != 1.0f) voice->Send[i].Filters[c].ActiveType |= AF_LowPass; - if(WetGainLF[i] != 1.0f) voice->Send[i].Filters[c].ActiveType |= AF_HighPass; - ALfilterState_setParams( - &voice->Send[i].Filters[c].LowPass, ALfilterType_HighShelf, - WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f) - ); - ALfilterState_setParams( - &voice->Send[i].Filters[c].HighPass, ALfilterType_LowShelf, - WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f) - ); + ALfilterState_copyParams(&voice->Send[i].Params[c].LowPass, + &voice->Send[i].Params[0].LowPass); + ALfilterState_copyParams(&voice->Send[i].Params[c].HighPass, + &voice->Send[i].Params[0].HighPass); } } } -ALvoid CalcSourceParams(ALvoice *voice, const ALsource *ALSource, const ALCcontext *ALContext) +static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext) { - ALCdevice *Device = ALContext->Device; - aluVector Position, Velocity, Direction, SourceToListener; - ALfloat InnerAngle,OuterAngle,Angle,Distance,ClampedDist; - ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff; - ALfloat ConeVolume,ConeHF,SourceVolume,ListenerGain; - ALfloat DopplerFactor, SpeedOfSound; - ALfloat AirAbsorptionFactor; - ALfloat RoomAirAbsorption[MAX_SENDS]; - ALbufferlistitem *BufferListItem; - ALfloat Attenuation; - ALfloat RoomAttenuation[MAX_SENDS]; - ALfloat MetersPerUnit; - ALfloat RoomRolloffBase; - ALfloat RoomRolloff[MAX_SENDS]; - ALfloat DecayDistance[MAX_SENDS]; - ALfloat DryGain; - ALfloat DryGainHF; - ALfloat DryGainLF; - ALboolean DryGainHFAuto; + static const ALfloat dir[3] = { 0.0f, 0.0f, -1.0f }; + const ALCdevice *Device = ALContext->Device; + const ALlistener *Listener = ALContext->Listener; + 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, j; + 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 = ALContext->DopplerFactor * ALSource->DopplerFactor; - SpeedOfSound = ALContext->SpeedOfSound * ALContext->DopplerVelocity; - 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 + 0.5f), 1); + BsincPrepare(voice->Step, &voice->ResampleState.bsinc); + voice->Resampler = SelectResampler(props->Resampler); - /* Get listener properties */ - ListenerGain = ALContext->Listener->Gain; - MetersPerUnit = ALContext->Listener->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 = ALSource->Gain; - MinVolume = ALSource->MinGain; - MaxVolume = ALSource->MaxGain; - Pitch = ALSource->Pitch; - Position = ALSource->Position; - Direction = ALSource->Direction; - Velocity = ALSource->Velocity; - MinDist = ALSource->RefDistance; - MaxDist = ALSource->MaxDistance; - Rolloff = ALSource->RollOffFactor; - InnerAngle = ALSource->InnerAngle; - OuterAngle = ALSource->OuterAngle; - AirAbsorptionFactor = ALSource->AirAbsorptionFactor; - DryGainHFAuto = ALSource->DryGainHFAuto; - WetGainAuto = ALSource->WetGainAuto; - WetGainHFAuto = ALSource->WetGainHFAuto; - RoomRolloffBase = ALSource->RoomRolloffFactor; + CalcPanningAndFilters(voice, 0.0f, dir, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain, + WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device); +} - voice->Direct.OutBuffer = Device->DryBuffer; - voice->Direct.OutChannels = Device->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 DecayHFDistance[MAX_SENDS]; + ALfloat DryGain, DryGainHF, DryGainLF; + ALfloat WetGain[MAX_SENDS]; + ALfloat WetGainHF[MAX_SENDS]; + ALfloat WetGainLF[MAX_SENDS]; + bool directional; + ALfloat dir[3]; + 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++) { - ALeffectslot *Slot = ALSource->Send[i].Slot; - - if(!Slot && i == 0) - Slot = Device->DefaultSlot; - if(!Slot || Slot->EffectType == AL_EFFECT_NULL) + 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) { - Slot = NULL; + SendSlots[i] = NULL; RoomRolloff[i] = 0.0f; DecayDistance[i] = 0.0f; - RoomAirAbsorption[i] = 1.0f; + DecayHFDistance[i] = 0.0f; } - else if(Slot->AuxSendAuto) + else if(SendSlots[i]->Params.AuxSendAuto) { - RoomRolloff[i] = RoomRolloffBase; - if(IsReverbEffect(Slot->EffectType)) + RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor; + DecayDistance[i] = SendSlots[i]->Params.DecayTime * SPEEDOFSOUNDMETRESPERSEC; + DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio; + if(SendSlots[i]->Params.DecayHFLimit) { - RoomRolloff[i] += Slot->EffectProps.Reverb.RoomRolloffFactor; - DecayDistance[i] = Slot->EffectProps.Reverb.DecayTime * - SPEEDOFSOUNDMETRESPERSEC; - RoomAirAbsorption[i] = Slot->EffectProps.Reverb.AirAbsorptionGainHF; - } - else - { - DecayDistance[i] = 0.0f; - RoomAirAbsorption[i] = 1.0f; + ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF; + if(airAbsorption < 1.0f) + { + ALfloat limitRatio = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption); + DecayHFDistance[i] = minf(limitRatio, 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; + DecayHFDistance[i] = 0.0f; } - if(!Slot || Slot->EffectType == AL_EFFECT_NULL) - voice->Send[i].OutBuffer = NULL; + if(!SendSlots[i]) + { + voice->Send[i].Buffer = NULL; + voice->Send[i].Channels = 0; + } else - voice->Send[i].OutBuffer = Slot->WetBuffer; + { + voice->Send[i].Buffer = SendSlots[i]->WetBuffer; + voice->Send[i].Channels = SendSlots[i]->NumChannels; + } } /* Transform source to listener space (convert to head relative) */ - if(ALSource->HeadRelative == 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 aluMatrixd *Matrix = &ALContext->Listener->Params.Matrix; + const aluMatrixf *Matrix = &Listener->Params.Matrix; /* Transform source vectors */ - Position = aluMatrixdVector(Matrix, &Position); - Velocity = aluMatrixdVector(Matrix, &Velocity); - Direction = aluMatrixdVector(Matrix, &Direction); + Position = aluMatrixfVector(Matrix, &Position); + Velocity = aluMatrixfVector(Matrix, &Velocity); + Direction = aluMatrixfVector(Matrix, &Direction); } else { - const aluVector *lvelocity = &ALContext->Listener->Params.Velocity; + const aluVector *lvelocity = &Listener->Params.Velocity; /* Offset the source velocity to be relative of the listener velocity */ Velocity.v[0] += lvelocity->v[0]; Velocity.v[1] += lvelocity->v[1]; Velocity.v[2] += lvelocity->v[2]; } - aluNormalize(Direction.v); + directional = aluNormalize(Direction.v) > FLT_EPSILON; 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(ALContext->SourceDistanceModel ? ALSource->DistanceModel : - ALContext->DistanceModel) + switch(Listener->Params.SourceDistanceModel ? + 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) + if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f) { - 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++) + ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor * + Listener->Params.MetersPerUnit; + if(props->AirAbsorptionFactor > 0.0f) { - if(DecayDistance[i] > 0.0f) - WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]); + 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; + + 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) + { + ALfloat gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]); + WetGainHF[i] *= minf(gainhf / gain, 1.0f); + } + } } } /* Calculate directional soundcones */ - Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f; - if(Angle > InnerAngle && Angle <= OuterAngle) + if(directional && props->InnerAngle < 360.0f) { - ALfloat scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle); - ConeVolume = lerp(1.0f, ALSource->OuterGain, scale); - ConeHF = lerp(1.0f, ALSource->OuterGainHF, scale); - } - else if(Angle > OuterAngle) - { - ConeVolume = ALSource->OuterGain; - ConeHF = ALSource->OuterGainHF; - } - else - { - ConeVolume = 1.0f; - ConeHF = 1.0f; - } + ALfloat ConeVolume; + ALfloat ConeHF; + ALfloat Angle; - DryGain *= ConeVolume; - if(WetGainAuto) - { - for(i = 0;i < NumSends;i++) - WetGain[i] *= ConeVolume; - } - if(DryGainHFAuto) - DryGainHF *= ConeHF; - if(WetGainHFAuto) - { - for(i = 0;i < NumSends;i++) - WetGainHF[i] *= ConeHF; - } - - /* Clamp to Min/Max Gain */ - DryGain = clampf(DryGain, MinVolume, MaxVolume); - for(i = 0;i < NumSends;i++) - WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume); - - /* Apply gain and frequency filters */ - DryGain *= ALSource->Direct.Gain * ListenerGain; - DryGainHF *= ALSource->Direct.GainHF; - DryGainLF *= ALSource->Direct.GainLF; - for(i = 0;i < NumSends;i++) - { - WetGain[i] *= ALSource->Send[i].Gain * ListenerGain; - WetGainHF[i] *= ALSource->Send[i].GainHF; - WetGainLF[i] *= ALSource->Send[i].GainLF; - } - - /* Calculate velocity-based doppler effect */ - if(DopplerFactor > 0.0f) - { - const aluVector *lvelocity = &ALContext->Listener->Params.Velocity; - ALfloat VSS, VLS; - - if(SpeedOfSound < 1.0f) + Angle = acosf(aluDotproduct(&Direction, &SourceToListener)); + Angle = RAD2DEG(Angle * ConeScale * 2.0f); + if(!(Angle > props->InnerAngle)) { - DopplerFactor *= 1.0f/SpeedOfSound; - SpeedOfSound = 1.0f; + ConeVolume = 1.0f; + ConeHF = 1.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); - } - - BufferListItem = ATOMIC_LOAD(&ALSource->queue); - while(BufferListItem != NULL) - { - ALbuffer *ALBuffer; - if((ALBuffer=BufferListItem->buffer) != NULL) + else if(Angle < props->OuterAngle) { - /* Calculate fixed-point stepping value, based on the pitch, buffer - * frequency, and output frequency. */ - Pitch = Pitch * 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); - - break; - } - BufferListItem = BufferListItem->next; - } - - if(Device->Hrtf_Mode == FullHrtf) - { - /* Full HRTF rendering. Skip the virtual channels and render to the - * real outputs. - */ - aluVector dir = {{ 0.0f, 0.0f, -1.0f, 0.0f }}; - ALfloat ev = 0.0f, az = 0.0f; - ALfloat radius = ALSource->Radius; - ALfloat dirfact = 1.0f; - - voice->Direct.OutBuffer += voice->Direct.OutChannels; - voice->Direct.OutChannels = 2; - - if(Distance > FLT_EPSILON) - { - dir.v[0] = -SourceToListener.v[0]; - dir.v[1] = -SourceToListener.v[1]; - dir.v[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.v[1], -1.0f, 1.0f)); - az = atan2f(dir.v[0], -dir.v[2]); - } - if(radius > 0.0f) - { - if(radius >= Distance) - dirfact *= Distance / radius * 0.5f; - else - dirfact *= 1.0f - (asinf(radius / Distance) / F_PI); - } - - /* Check to see if the HRIR is already moving. */ - if(voice->Direct.Moving) - { - ALfloat delta; - delta = CalcFadeTime(voice->Direct.LastGain, DryGain, - &voice->Direct.LastDir, &dir); - /* If the delta is large enough, get the moving HRIR target - * coefficients, target delays, steppping values, and counter. - */ - if(delta > 0.000015f) - { - ALuint counter = GetMovingHrtfCoeffs(Device->Hrtf, - ev, az, dirfact, DryGain, delta, voice->Direct.Counter, - voice->Direct.Hrtf[0].Params.Coeffs, voice->Direct.Hrtf[0].Params.Delay, - voice->Direct.Hrtf[0].Params.CoeffStep, voice->Direct.Hrtf[0].Params.DelayStep - ); - voice->Direct.Counter = counter; - voice->Direct.LastGain = DryGain; - voice->Direct.LastDir = dir; - } + ALfloat scale = ( Angle-props->InnerAngle) / + (props->OuterAngle-props->InnerAngle); + ConeVolume = lerp(1.0f, props->OuterGain, scale); + ConeHF = lerp(1.0f, props->OuterGainHF, scale); } else { - /* Get the initial (static) HRIR coefficients and delays. */ - GetLerpedHrtfCoeffs(Device->Hrtf, ev, az, dirfact, DryGain, - voice->Direct.Hrtf[0].Params.Coeffs, - voice->Direct.Hrtf[0].Params.Delay); - voice->Direct.Counter = 0; - voice->Direct.Moving = AL_TRUE; - voice->Direct.LastGain = DryGain; - voice->Direct.LastDir = dir; + ConeVolume = props->OuterGain; + ConeHF = props->OuterGainHF; } - voice->IsHrtf = AL_TRUE; - } - else - { - /* Basic or no HRTF rendering. Use normal panning to the output. */ - MixGains *gains = voice->Direct.Gains[0]; - ALfloat dir[3] = { 0.0f, 0.0f, -1.0f }; - ALfloat radius = ALSource->Radius; - ALfloat Target[MAX_OUTPUT_CHANNELS]; - - /* Get the localized direction, and compute panned gains. */ - if(Distance > FLT_EPSILON) + DryGain *= ConeVolume; + if(props->DryGainHFAuto) + DryGainHF *= ConeHF; + if(props->WetGainAuto) { - dir[0] = -SourceToListener.v[0]; - dir[1] = -SourceToListener.v[1]; - dir[2] = -SourceToListener.v[2] * ZScale; + for(i = 0;i < NumSends;i++) + WetGain[i] *= ConeVolume; } - if(radius > 0.0f) + if(props->WetGainHFAuto) { - ALfloat dirfact; - if(radius >= Distance) - dirfact = Distance / radius * 0.5f; - else - dirfact = 1.0f - (asinf(radius / Distance) / F_PI); - dir[0] *= dirfact; - dir[1] *= dirfact; - dir[2] *= dirfact; + for(i = 0;i < NumSends;i++) + WetGainHF[i] *= ConeHF; } - ComputeDirectionalGains(Device, dir, DryGain, Target); - - for(j = 0;j < MAX_OUTPUT_CHANNELS;j++) - gains[j].Target = Target[j]; - UpdateDryStepping(&voice->Direct, 1, (voice->Direct.Moving ? 64 : 0)); - voice->Direct.Moving = AL_TRUE; - - voice->IsHrtf = AL_FALSE; } + + /* Apply gain and frequency filters */ + DryGain = clampf(DryGain, props->MinGain, props->MaxGain); + DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX); + DryGainHF *= props->Direct.GainHF; + DryGainLF *= props->Direct.GainLF; for(i = 0;i < NumSends;i++) { - voice->Send[i].Gains[0].Target = WetGain[i]; - UpdateWetStepping(&voice->Send[i], 1, (voice->Send[i].Moving ? 64 : 0)); - voice->Send[i].Moving = AL_TRUE; + 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; } + + /* Initial source pitch */ + Pitch = props->Pitch; + + /* Calculate velocity-based doppler effect */ + DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor; + if(DopplerFactor > 0.0f) { - ALfloat hfscale = ALSource->Direct.HFReference / Frequency; - ALfloat lfscale = ALSource->Direct.LFReference / Frequency; - DryGainHF = maxf(DryGainHF, 0.0001f); - DryGainLF = maxf(DryGainLF, 0.0001f); - voice->Direct.Filters[0].ActiveType = AF_None; - if(DryGainHF != 1.0f) voice->Direct.Filters[0].ActiveType |= AF_LowPass; - if(DryGainLF != 1.0f) voice->Direct.Filters[0].ActiveType |= AF_HighPass; - ALfilterState_setParams( - &voice->Direct.Filters[0].LowPass, ALfilterType_HighShelf, - DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f) - ); - ALfilterState_setParams( - &voice->Direct.Filters[0].HighPass, ALfilterType_LowShelf, - DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f) - ); - } - for(i = 0;i < NumSends;i++) - { - ALfloat hfscale = ALSource->Send[i].HFReference / Frequency; - ALfloat lfscale = ALSource->Send[i].LFReference / Frequency; - WetGainHF[i] = maxf(WetGainHF[i], 0.0001f); - WetGainLF[i] = maxf(WetGainLF[i], 0.0001f); - voice->Send[i].Filters[0].ActiveType = AF_None; - if(WetGainHF[i] != 1.0f) voice->Send[i].Filters[0].ActiveType |= AF_LowPass; - if(WetGainLF[i] != 1.0f) voice->Send[i].Filters[0].ActiveType |= AF_HighPass; - ALfilterState_setParams( - &voice->Send[i].Filters[0].LowPass, ALfilterType_HighShelf, - WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f) - ); - ALfilterState_setParams( - &voice->Send[i].Filters[0].HighPass, ALfilterType_LowShelf, - WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f) - ); - } -} + const aluVector *lvelocity = &Listener->Params.Velocity; + const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound; + ALfloat vss, vls; + vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor; + vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor; -void UpdateContextSources(ALCcontext *ctx) -{ - ALvoice *voice, *voice_end; - ALsource *source; - - if(ATOMIC_EXCHANGE(ALenum, &ctx->UpdateSources, AL_FALSE)) - { - CalcListenerParams(ctx->Listener); - - voice = ctx->Voices; - voice_end = voice + ctx->VoiceCount; - for(;voice != voice_end;++voice) + if(!(vls < SpeedOfSound)) { - if(!(source=voice->Source)) continue; - if(source->state != AL_PLAYING && source->state != AL_PAUSED) - voice->Source = NULL; - else - { - ATOMIC_STORE(&source->NeedsUpdate, AL_FALSE); - voice->Update(voice, source, ctx); - } + /* Listener moving away from the source at the speed of sound. + * Sound waves can't catch it. + */ + Pitch = 0.0f; + } + else if(!(vss < SpeedOfSound)) + { + /* Source moving toward the listener at the speed of sound. Sound + * waves bunch up to extreme frequencies. + */ + Pitch = HUGE_VALF; + } + else + { + /* Source and listener movement is nominal. Calculate the proper + * doppler shift. + */ + Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss); } } - else - { - 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 if(ATOMIC_EXCHANGE(ALenum, &source->NeedsUpdate, AL_FALSE)) - voice->Update(voice, source, ctx); - } - } -} - -/* Specialized function to clamp to [-1, +1] with only one branch. This also - * converts NaN to 0. */ -static inline ALfloat aluClampf(ALfloat val) -{ - if(fabsf(val) <= 1.0f) return val; - return (ALfloat)((0.0f < val) - (val < 0.0f)); -} - -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. + /* Adjust pitch based on the buffer and output frequencies, and calculate + * fixed-point stepping value. */ - return fastf2i(aluClampf(val)*16777215.0f)<<7; + Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency; + if(Pitch > (ALfloat)MAX_PITCH) + voice->Step = MAX_PITCH<Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1); + BsincPrepare(voice->Step, &voice->ResampleState.bsinc); + voice->Resampler = SelectResampler(props->Resampler); + + if(Distance > FLT_EPSILON) + { + dir[0] = -SourceToListener.v[0]; + /* Clamp Y, in case rounding errors caused it to end up outside of + * -1...+1. + */ + dir[1] = clampf(-SourceToListener.v[1], -1.0f, 1.0f); + dir[2] = -SourceToListener.v[2] * ZScale; + } + else + { + dir[0] = 0.0f; + dir[1] = 0.0f; + dir[2] = -1.0f; + } + if(props->Radius > Distance) + spread = F_TAU - Distance/props->Radius*F_PI; + else if(Distance > FLT_EPSILON) + spread = asinf(props->Radius / Distance) * 2.0f; + else + spread = 0.0f; + + CalcPanningAndFilters(voice, Distance, dir, spread, DryGain, DryGainHF, DryGainLF, WetGain, + WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device); } -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 CalcSourceParams(ALvoice *voice, ALCcontext *context, ALboolean force) +{ + ALbufferlistitem *BufferListItem; + struct ALvoiceProps *props; -static inline ALbyte aluF2B(ALfloat val) -{ return fastf2i(aluClampf(val)*127.0f); } -static inline ALubyte aluF2UB(ALfloat val) -{ return aluF2B(val)+128; } + props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel); + if(!props && !force) return; -#define DECL_TEMPLATE(T, func) \ -static void Write_##T(ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \ - ALuint SamplesToDo, ALuint numchans) \ + if(props) + { + memcpy(voice->Props, props, + FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends) + ); + + ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &voice->FreeList, props); + } + props = voice->Props; + + BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed); + while(BufferListItem != NULL) + { + const ALbuffer *buffer; + if((buffer=BufferListItem->buffer) != NULL) + { + if(props->SpatializeMode == SpatializeOn || + (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono)) + CalcAttnSourceParams(voice, props, buffer, context); + else + CalcNonAttnSourceParams(voice, props, buffer, context); + break; + } + BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire); + } +} + + +static void UpdateContextSources(ALCcontext *ctx, const struct ALeffectslotArray *slots) +{ + ALvoice **voice, **voice_end; + ALsource *source; + ALsizei i; + + IncrementRef(&ctx->UpdateCount); + if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire)) + { + ALboolean force = CalcListenerParams(ctx); + for(i = 0;i < slots->count;i++) + force |= CalcEffectSlotParams(slots->slot[i], ctx->Device); + + voice = ctx->Voices; + voice_end = voice + ctx->VoiceCount; + for(;voice != voice_end;++voice) + { + source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire); + if(source) CalcSourceParams(*voice, ctx, force); + } + } + IncrementRef(&ctx->UpdateCount); +} + + +static void ApplyDistanceComp(ALfloatBUFFERSIZE *restrict Samples, DistanceComp *distcomp, + ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans) +{ + ALsizei i, c; + + 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); + + 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(ALfloatBUFFERSIZE *restrict Samples, 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] = roundf(val) * invscale; + } + } + *dither_seed = seed; +} + + +static inline ALfloat Conv_ALfloat(ALfloat val) +{ return val; } +static inline ALint Conv_ALint(ALfloat val) +{ + /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max + * integer range normalized floats can be safely converted to (a bit of the + * exponent helps out, effectively giving 25 bits). + */ + 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 ALfloatBUFFERSIZE *InBuffer, 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;) { - ALfloat (*OutBuffer)[BUFFERSIZE]; - ALuint OutChannels; + 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->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); - OutBuffer = device->DryBuffer; - OutChannels = device->NumChannels; - - SamplesToDo = minu(size, BUFFERSIZE); - for(c = 0;c < OutChannels;c++) - memset(OutBuffer[c], 0, SamplesToDo*sizeof(ALfloat)); - if(device->Hrtf) - { - /* Set OutBuffer/OutChannels to correspond to the actual output - * with HRTF. Make sure to clear them too. */ - OutBuffer += OutChannels; - OutChannels = 2; - for(c = 0;c < OutChannels;c++) - memset(OutBuffer[c], 0, SamplesToDo*sizeof(ALfloat)); - } - - V0(device->Backend,lock)(); - - if((slot=device->DefaultSlot) != NULL) - { - if(ATOMIC_EXCHANGE(ALenum, &slot->NeedsUpdate, AL_FALSE)) - V(slot->EffectState,update)(device, slot); - memset(slot->WetBuffer[0], 0, SamplesToDo*sizeof(ALfloat)); - } - - ctx = ATOMIC_LOAD(&device->ContextList); + ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire); while(ctx) { - if(!ctx->DeferUpdates) + const struct ALeffectslotArray *auxslots; + + auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire); + UpdateContextSources(ctx, auxslots); + + for(i = 0;i < auxslots->count;i++) { - UpdateContextSources(ctx); -#define UPDATE_SLOT(iter) do { \ - if(ATOMIC_EXCHANGE(ALenum, &(*iter)->NeedsUpdate, AL_FALSE)) \ - V((*iter)->EffectState,update)(device, *iter); \ - memset((*iter)->WetBuffer[0], 0, SamplesToDo*sizeof(ALfloat)); \ -} while(0) - VECTOR_FOR_EACH(ALeffectslot*, ctx->ActiveAuxSlots, UPDATE_SLOT); -#undef UPDATE_SLOT - } - else - { -#define CLEAR_WET_BUFFER(iter) memset((*iter)->WetBuffer[0], 0, SamplesToDo*sizeof(ALfloat)) - VECTOR_FOR_EACH(ALeffectslot*, ctx->ActiveAuxSlots, CLEAR_WET_BUFFER); -#undef CLEAR_WET_BUFFER + 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++) { - source = voice->Source; - if(source && source->state == AL_PLAYING) - 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, device, SamplesToDo)) + { + ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed); + ATOMIC_STORE(&voice->Playing, false, almemory_order_release); + } + } } /* effect slot processing */ -#define PROCESS_SLOT(iter) V((*iter)->EffectState,process)( \ - SamplesToDo, (*iter)->WetBuffer[0], device->DryBuffer, device->NumChannels \ -); - VECTOR_FOR_EACH(ALeffectslot*, ctx->ActiveAuxSlots, PROCESS_SLOT); -#undef PROCESS_SLOT + for(i = 0;i < auxslots->count;i++) + { + const ALeffectslot *slot = auxslots->slot[i]; + ALeffectState *state = slot->Params.EffectState; + V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer, + state->OutChannels); + } ctx = ctx->next; } - if((slot=device->DefaultSlot) != NULL) - V(slot->EffectState,process)( - SamplesToDo, slot->WetBuffer[0], device->DryBuffer, device->NumChannels - ); - /* Increment the clock time. Every second's worth of samples is * converted and added to clock base so that large sample counts don't * overflow during conversion. This also guarantees an exact, stable @@ -1488,104 +1677,154 @@ 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) + if(device->HrtfHandle) { - HrtfMixerFunc HrtfMix = SelectHrtfMixer(); - ALuint irsize = GetHrtfIrSize(device->Hrtf); - for(c = 0;c < device->NumChannels;c++) - HrtfMix(OutBuffer, device->DryBuffer[c], 0, device->Hrtf_Offset, - 0, irsize, &device->Hrtf_Params[c], &device->Hrtf_State[c], - SamplesToDo + HrtfDirectMixerFunc HrtfMix; + DirectHrtfState *state; + int lidx, ridx; + + if(device->AmbiUp) + ambiup_process(device->AmbiUp, + device->Dry.Buffer, device->Dry.NumChannels, + SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo ); - device->Hrtf_Offset += SamplesToDo; + + lidx = GetChannelIdxByName(device->RealOut, FrontLeft); + ridx = GetChannelIdxByName(device->RealOut, FrontRight); + assert(lidx != -1 && ridx != -1); + + HrtfMix = SelectHrtfMixer(); + state = device->Hrtf; + for(c = 0;c < device->Dry.NumChannels;c++) + { + HrtfMix(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx], + device->Dry.Buffer[c], state->Offset, state->IrSize, + SAFE_CONST(ALfloat2*,state->Chan[c].Coeffs), + state->Chan[c].Values, SamplesToDo + ); + } + state->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) { - /* Apply binaural/crossfeed filter */ - for(i = 0;i < SamplesToDo;i++) + int lidx = GetChannelIdxByName(device->RealOut, FrontLeft); + int ridx = GetChannelIdxByName(device->RealOut, FrontRight); + if(lidx != -1 && ridx != -1) { - float samples[2]; - samples[0] = device->DryBuffer[0][i]; - samples[1] = device->DryBuffer[1][i]; - bs2b_cross_feed(device->Bs2b, samples); - device->DryBuffer[0][i] = samples[0]; - device->DryBuffer[1][i] = samples[1]; + /* Apply binaural/crossfeed filter */ + bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx], + device->RealOut.Buffer[ridx], SamplesToDo); } } - if(buffer) + if(OutBuffer) { -#define WRITE(T, a, b, c, d) do { \ - Write_##T((a), (b), (c), (d)); \ - buffer = (T*)buffer + (c)*(d); \ -} while(0) + ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer; + ALsizei Channels = device->RealOut.NumChannels; + + /* Use NFCtrlData for temp value storage. */ + ApplyDistanceComp(Buffer, device->ChannelDelay, device->NFCtrlData, + SamplesToDo, Channels); + + if(device->Limiter) + ApplyCompression(device->Limiter, Channels, SamplesToDo, Buffer); + + if(device->DitherDepth > 0.0f) + ApplyDither(Buffer, &device->DitherSeed, device->DitherDepth, SamplesToDo, + Channels); + 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; - IncrementRef(&device->MixCount); + SamplesDone += SamplesToDo; } - - RestoreFPUMode(&oldMode); + END_MIXER_MODE(); } -ALvoid aluHandleDisconnect(ALCdevice *device) +void aluHandleDisconnect(ALCdevice *device) { - ALCcontext *Context; + ALCcontext *ctx; device->Connected = ALC_FALSE; - Context = ATOMIC_LOAD(&device->ContextList); - while(Context) + ctx = ATOMIC_LOAD_SEQ(&device->ContextList); + while(ctx) { - ALvoice *voice, *voice_end; - - voice = Context->Voices; - voice_end = voice + Context->VoiceCount; - while(voice != voice_end) + ALsizei i; + 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_acq_rel); + ATOMIC_STORE(&voice->Playing, false, almemory_order_release); + + if(source) { - source->state = AL_STOPPED; - ATOMIC_STORE(&source->current_buffer, NULL); - source->position = 0; - source->position_fraction = 0; + ALenum playing = AL_PLAYING; + (void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&source->state, &playing, AL_STOPPED)); } - - voice++; } - Context->VoiceCount = 0; + ctx->VoiceCount = 0; - Context = Context->next; + ctx = ctx->next; } } diff --git a/libs/openal-soft/Alc/alcConfig.c b/libs/openal-soft/Alc/alcConfig.c index 6fc9db33..6fd01746 100644 --- a/libs/openal-soft/Alc/alcConfig.c +++ b/libs/openal-soft/Alc/alcConfig.c @@ -233,7 +233,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; } @@ -313,44 +367,61 @@ void ReadALConfig(void) { WCHAR buffer[PATH_MAX]; 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); + } + alstr_reset(&filepath); + } + + ppath = GetProcPath(); + if(!alstr_empty(ppath)) + { + 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); fclose(f); } - al_string_deinit(&filepath); } 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); } + + alstr_reset(&ppath); } #else void ReadALConfig(void) { char buffer[PATH_MAX]; const char *str; + al_string ppath; FILE *f; str = "/etc/openal/alsoft.conf"; @@ -430,6 +501,19 @@ void ReadALConfig(void) } } + ppath = GetProcPath(); + if(!alstr_empty(ppath)) + { + alstr_append_cstr(&ppath, "/alsoft.conf"); + TRACE("Loading config %s...\n", alstr_get_cstr(ppath)); + f = al_fopen(alstr_get_cstr(ppath), "r"); + if(f) + { + LoadConfigFromFile(f); + fclose(f); + } + } + if((str=getenv("ALSOFT_CONF")) != NULL && *str) { TRACE("Loading config %s...\n", str); @@ -440,6 +524,8 @@ void ReadALConfig(void) fclose(f); } } + + alstr_reset(&ppath); } #endif diff --git a/libs/openal-soft/Alc/alcRing.c b/libs/openal-soft/Alc/alcRing.c index e9a40a12..d72b34f1 100644 --- a/libs/openal-soft/Alc/alcRing.c +++ b/libs/openal-soft/Alc/alcRing.c @@ -25,117 +25,17 @@ #include "alMain.h" #include "threads.h" +#include "almalloc.h" #include "compat.h" -struct RingBuffer { - ALubyte *mem; - - ALsizei frame_size; - ALsizei length; - ALint read_pos; - ALint write_pos; - - almtx_t mtx; -}; - - -RingBuffer *CreateRingBuffer(ALsizei frame_size, ALsizei length) -{ - RingBuffer *ring = calloc(1, sizeof(*ring) + ((length+1) * frame_size)); - if(ring) - { - ring->mem = (ALubyte*)(ring+1); - - ring->frame_size = frame_size; - ring->length = length+1; - ring->read_pos = 0; - ring->write_pos = 0; - - almtx_init(&ring->mtx, almtx_plain); - } - return ring; -} - -void DestroyRingBuffer(RingBuffer *ring) -{ - if(ring) - { - almtx_destroy(&ring->mtx); - free(ring); - } -} - -ALsizei RingBufferSize(RingBuffer *ring) -{ - ALsizei s; - - almtx_lock(&ring->mtx); - s = (ring->write_pos-ring->read_pos+ring->length) % ring->length; - almtx_unlock(&ring->mtx); - - return s; -} - -void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len) -{ - int remain; - - almtx_lock(&ring->mtx); - - remain = (ring->read_pos-ring->write_pos-1+ring->length) % ring->length; - if(remain < len) len = remain; - - if(len > 0) - { - remain = ring->length - ring->write_pos; - if(remain < len) - { - memcpy(ring->mem+(ring->write_pos*ring->frame_size), data, - remain*ring->frame_size); - memcpy(ring->mem, data+(remain*ring->frame_size), - (len-remain)*ring->frame_size); - } - else - memcpy(ring->mem+(ring->write_pos*ring->frame_size), data, - len*ring->frame_size); - - ring->write_pos += len; - ring->write_pos %= ring->length; - } - - almtx_unlock(&ring->mtx); -} - -void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len) -{ - int remain; - - almtx_lock(&ring->mtx); - - remain = ring->length - ring->read_pos; - if(remain < len) - { - memcpy(data, ring->mem+(ring->read_pos*ring->frame_size), remain*ring->frame_size); - memcpy(data+(remain*ring->frame_size), ring->mem, (len-remain)*ring->frame_size); - } - else - memcpy(data, ring->mem+(ring->read_pos*ring->frame_size), len*ring->frame_size); - - ring->read_pos += len; - ring->read_pos %= ring->length; - - almtx_unlock(&ring->mtx); -} - - /* 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; + ATOMIC(size_t) write_ptr; + ATOMIC(size_t) read_ptr; size_t size; size_t size_mask; size_t elem_size; @@ -158,11 +58,11 @@ ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz) 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 = 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; } @@ -184,7 +84,7 @@ void ll_ringbuffer_free(ll_ringbuffer_t *rb) int ll_ringbuffer_mlock(ll_ringbuffer_t *rb) { #ifdef USE_MLOCK - if(!rb->locked && mlock(rb, sizeof(*rb) + rb->size*rb->elem_size)) + if(!rb->mlocked && mlock(rb, sizeof(*rb) + rb->size*rb->elem_size)) return -1; #endif /* USE_MLOCK */ rb->mlocked = 1; @@ -194,8 +94,8 @@ int ll_ringbuffer_mlock(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) { - rb->read_ptr = 0; - rb->write_ptr = 0; + ATOMIC_STORE(&rb->write_ptr, 0, almemory_order_release); + ATOMIC_STORE(&rb->read_ptr, 0, almemory_order_release); memset(rb->buf, 0, rb->size*rb->elem_size); } @@ -203,23 +103,24 @@ void ll_ringbuffer_reset(ll_ringbuffer_t *rb) * 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; + 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; } /* 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; + 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 (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 read_ptr; size_t free_cnt; size_t cnt2; size_t to_read; @@ -229,10 +130,12 @@ size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt) if(free_cnt == 0) return 0; to_read = (cnt > free_cnt) ? free_cnt : cnt; - cnt2 = rb->read_ptr + to_read; + read_ptr = ATOMIC_LOAD(&rb->read_ptr, almemory_order_relaxed) & rb->size_mask; + + cnt2 = read_ptr + to_read; if(cnt2 > rb->size) { - n1 = rb->size - rb->read_ptr; + n1 = rb->size - read_ptr; n2 = cnt2 & rb->size_mask; } else @@ -241,13 +144,15 @@ size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt) 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; + 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[rb->read_ptr*rb->elem_size]), n2*rb->elem_size); - rb->read_ptr = (rb->read_ptr + n2) & rb->size_mask; + 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; } @@ -260,17 +165,18 @@ size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt) size_t cnt2; size_t to_read; size_t n1, n2; - size_t tmp_read_ptr; + size_t 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; + read_ptr = ATOMIC_LOAD(&rb->read_ptr, almemory_order_relaxed) & rb->size_mask; + + cnt2 = read_ptr + to_read; if(cnt2 > rb->size) { - n1 = rb->size - tmp_read_ptr; + n1 = rb->size - read_ptr; n2 = cnt2 & rb->size_mask; } else @@ -279,10 +185,13 @@ size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt) 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; + memcpy(dest, &rb->buf[read_ptr*rb->elem_size], n1*rb->elem_size); if(n2) - memcpy(dest + n1*rb->elem_size, &(rb->buf[tmp_read_ptr*rb->elem_size]), n2*rb->elem_size); + { + 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; } @@ -290,6 +199,7 @@ size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt) * Returns the actual number of elements copied. */ 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; @@ -299,10 +209,12 @@ size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt) if(free_cnt == 0) return 0; to_write = (cnt > free_cnt) ? free_cnt : cnt; - cnt2 = rb->write_ptr + to_write; + write_ptr = ATOMIC_LOAD(&rb->write_ptr, almemory_order_relaxed) & rb->size_mask; + + cnt2 = write_ptr + to_write; if(cnt2 > rb->size) { - n1 = rb->size - rb->write_ptr; + n1 = rb->size - write_ptr; n2 = cnt2 & rb->size_mask; } else @@ -311,28 +223,28 @@ size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt) 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; + memcpy(&rb->buf[write_ptr*rb->elem_size], src, n1*rb->elem_size); + write_ptr += n1; 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; + 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; } /* 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; + ATOMIC_ADD(&rb->read_ptr, cnt, almemory_order_acq_rel); } /* 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; + ATOMIC_ADD(&rb->write_ptr, cnt, almemory_order_acq_rel); } /* The non-copying data reader. `vec' is an array of two places. Set the values @@ -344,16 +256,18 @@ void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data size_t cnt2; size_t w, r; - w = rb->write_ptr; - r = rb->read_ptr; - free_cnt = (rb->size+w-r) & rb->size_mask; + 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) { /* 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].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; @@ -361,7 +275,7 @@ void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data else { /* Single part vector: just the rest of the buffer */ - vec[0].buf = (char*)&(rb->buf[r*rb->elem_size]); + vec[0].buf = (char*)&rb->buf[r*rb->elem_size]; vec[0].len = free_cnt; vec[1].buf = NULL; vec[1].len = 0; @@ -377,23 +291,25 @@ void ll_ringbuffer_get_write_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_dat 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; + 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; 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].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].buf = (char*)&rb->buf[w*rb->elem_size]; vec[0].len = free_cnt; vec[1].buf = NULL; vec[1].len = 0; diff --git a/libs/openal-soft/Alc/alstring.h b/libs/openal-soft/Alc/alstring.h index f53d2c57..a7513516 100644 --- a/libs/openal-soft/Alc/alstring.h +++ b/libs/openal-soft/Alc/alstring.h @@ -10,39 +10,40 @@ 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 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_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to); #endif #endif /* ALSTRING_H */ diff --git a/libs/openal-soft/Alc/ambdec.c b/libs/openal-soft/Alc/ambdec.c new file mode 100644 index 00000000..da114335 --- /dev/null +++ b/libs/openal-soft/Alc/ambdec.c @@ -0,0 +1,566 @@ + +#include "config.h" + +#include "ambdec.h" + +#include +#include +#include + +#include "compat.h" + + +static char *lstrip(char *line) +{ + while(isspace(line[0])) + line++; + return line; +} + +static char *rstrip(char *line) +{ + size_t len = strlen(line); + while(len > 0 && isspace(line[len-1])) + len--; + line[len] = 0; + return line; +} + +static int readline(FILE *f, char **output, size_t *maxlen) +{ + size_t len = 0; + int c; + + while((c=fgetc(f)) != EOF && (c == '\r' || c == '\n')) + ; + if(c == EOF) + return 0; + + do { + if(len+1 >= *maxlen) + { + void *temp = NULL; + size_t newmax; + + newmax = (*maxlen ? (*maxlen)<<1 : 32); + if(newmax > *maxlen) + temp = realloc(*output, newmax); + if(!temp) + { + ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, *maxlen); + return 0; + } + + *output = temp; + *maxlen = newmax; + } + (*output)[len++] = c; + (*output)[len] = '\0'; + } while((c=fgetc(f)) != EOF && c != '\r' && c != '\n'); + + return 1; +} + + +/* Custom strtok_r, since we can't rely on it existing. */ +static char *my_strtok_r(char *str, const char *delim, char **saveptr) +{ + /* Sanity check and update internal pointer. */ + if(!saveptr || !delim) return NULL; + if(str) *saveptr = str; + str = *saveptr; + + /* Nothing more to do with this string. */ + if(!str) return NULL; + + /* Find the first non-delimiter character. */ + while(*str != '\0' && strchr(delim, *str) != NULL) + str++; + if(*str == '\0') + { + /* End of string. */ + *saveptr = NULL; + return NULL; + } + + /* Find the next delimiter character. */ + *saveptr = strpbrk(str, delim); + if(*saveptr) *((*saveptr)++) = '\0'; + + 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; + *num = strtoul(line, &end, base); + if(end && *end != '\0') + end = lstrip(end); + return end; +} + +static char *read_float(ALfloat *num, const char *line) +{ + char *end; +#ifdef HAVE_STRTOF + *num = strtof(line, &end); +#else + *num = (ALfloat)strtod(line, &end); +#endif + if(end && *end != '\0') + end = lstrip(end); + return end; +} + + +char *read_clipped_line(FILE *f, char **buffer, size_t *maxlen) +{ + while(readline(f, buffer, maxlen)) + { + char *line, *comment; + + line = lstrip(*buffer); + comment = strchr(line, '#'); + if(comment) *(comment++) = 0; + + line = rstrip(line); + if(line[0]) return line; + } + return NULL; +} + +static int load_ambdec_speakers(AmbDecConf *conf, FILE *f, char **buffer, size_t *maxlen, char **saveptr) +{ + ALsizei cur = 0; + while(cur < conf->NumSpeakers) + { + const char *cmd = my_strtok_r(NULL, " \t", saveptr); + if(!cmd) + { + char *line = read_clipped_line(f, buffer, maxlen); + if(!line) + { + ERR("Unexpected end of file\n"); + return 0; + } + cmd = my_strtok_r(line, " \t", saveptr); + } + + if(strcmp(cmd, "add_spkr") == 0) + { + const char *name = my_strtok_r(NULL, " \t", saveptr); + const char *dist = my_strtok_r(NULL, " \t", saveptr); + const char *az = my_strtok_r(NULL, " \t", saveptr); + const char *elev = my_strtok_r(NULL, " \t", saveptr); + const char *conn = my_strtok_r(NULL, " \t", saveptr); + + if(!name) WARN("Name not specified for speaker %u\n", cur+1); + 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); + else read_float(&conf->Speakers[cur].Azimuth, az); + 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 alstr_copy_cstr(&conf->Speakers[cur].Connection, conn); + + cur++; + } + else + { + ERR("Unexpected speakers command: %s\n", cmd); + return 0; + } + + cmd = my_strtok_r(NULL, " \t", saveptr); + if(cmd) + { + ERR("Unexpected junk on line: %s\n", cmd); + return 0; + } + } + + return 1; +} + +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; + ALsizei cur = 0; + while(cur < maxrow) + { + const char *cmd = my_strtok_r(NULL, " \t", saveptr); + if(!cmd) + { + char *line = read_clipped_line(f, buffer, maxlen); + if(!line) + { + ERR("Unexpected end of file\n"); + return 0; + } + cmd = my_strtok_r(line, " \t", saveptr); + } + + if(strcmp(cmd, "order_gain") == 0) + { + ALuint curgain = 0; + char *line; + while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL) + { + ALfloat value; + line = read_float(&value, line); + if(line && *line != '\0') + { + ERR("Extra junk on gain %u: %s\n", curgain+1, line); + return 0; + } + if(curgain < MAX_AMBI_ORDER+1) + gains[curgain] = value; + curgain++; + } + while(curgain < MAX_AMBI_ORDER+1) + gains[curgain++] = 0.0f; + gotgains = 1; + } + else if(strcmp(cmd, "add_row") == 0) + { + ALuint curidx = 0; + char *line; + while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL) + { + ALfloat value; + line = read_float(&value, line); + if(line && *line != '\0') + { + ERR("Extra junk on matrix element %ux%u: %s\n", cur, curidx, line); + return 0; + } + if(curidx < MAX_AMBI_COEFFS) + matrix[cur][curidx] = value; + curidx++; + } + while(curidx < MAX_AMBI_COEFFS) + matrix[cur][curidx++] = 0.0f; + cur++; + } + else + { + ERR("Unexpected speakers command: %s\n", cmd); + return 0; + } + + cmd = my_strtok_r(NULL, " \t", saveptr); + if(cmd) + { + ERR("Unexpected junk on line: %s\n", cmd); + return 0; + } + } + + if(!gotgains) + { + ERR("Matrix order_gain not specified\n"); + return 0; + } + + return 1; +} + +void ambdec_init(AmbDecConf *conf) +{ + ALsizei i; + + memset(conf, 0, sizeof(*conf)); + AL_STRING_INIT(conf->Description); + for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) + { + AL_STRING_INIT(conf->Speakers[i].Name); + AL_STRING_INIT(conf->Speakers[i].Connection); + } +} + +void ambdec_deinit(AmbDecConf *conf) +{ + ALsizei i; + + alstr_reset(&conf->Description); + for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) + { + alstr_reset(&conf->Speakers[i].Name); + alstr_reset(&conf->Speakers[i].Connection); + } + memset(conf, 0, sizeof(*conf)); +} + +int ambdec_load(AmbDecConf *conf, const char *fname) +{ + char *buffer = NULL; + size_t maxlen = 0; + char *line; + FILE *f; + + f = al_fopen(fname, "r"); + if(!f) + { + ERR("Failed to open: %s\n", fname); + return 0; + } + + while((line=read_clipped_line(f, &buffer, &maxlen)) != NULL) + { + char *saveptr; + char *command; + + command = my_strtok_r(line, "/ \t", &saveptr); + if(!command) + { + ERR("Malformed line: %s\n", line); + goto fail; + } + + if(strcmp(command, "description") == 0) + { + char *value = my_strtok_r(NULL, "", &saveptr); + alstr_copy_cstr(&conf->Description, lstrip(value)); + } + else if(strcmp(command, "version") == 0) + { + line = my_strtok_r(NULL, "", &saveptr); + line = read_uint(&conf->Version, line, 10); + if(line && *line != '\0') + { + ERR("Extra junk after version: %s\n", line); + goto fail; + } + if(conf->Version != 3) + { + ERR("Unsupported version: %u\n", conf->Version); + goto fail; + } + } + else if(strcmp(command, "dec") == 0) + { + const char *dec = my_strtok_r(NULL, "/ \t", &saveptr); + if(strcmp(dec, "chan_mask") == 0) + { + line = my_strtok_r(NULL, "", &saveptr); + line = read_uint(&conf->ChanMask, line, 16); + if(line && *line != '\0') + { + ERR("Extra junk after mask: %s\n", line); + goto fail; + } + } + else if(strcmp(dec, "freq_bands") == 0) + { + line = my_strtok_r(NULL, "", &saveptr); + line = read_uint(&conf->FreqBands, line, 10); + if(line && *line != '\0') + { + ERR("Extra junk after freq_bands: %s\n", line); + goto fail; + } + if(conf->FreqBands != 1 && conf->FreqBands != 2) + { + ERR("Invalid freq_bands value: %u\n", conf->FreqBands); + goto fail; + } + } + else if(strcmp(dec, "speakers") == 0) + { + line = my_strtok_r(NULL, "", &saveptr); + line = read_int(&conf->NumSpeakers, line, 10); + if(line && *line != '\0') + { + ERR("Extra junk after speakers: %s\n", line); + goto fail; + } + if(conf->NumSpeakers > MAX_OUTPUT_CHANNELS) + { + ERR("Unsupported speaker count: %u\n", conf->NumSpeakers); + goto fail; + } + } + else if(strcmp(dec, "coeff_scale") == 0) + { + line = my_strtok_r(NULL, " \t", &saveptr); + if(strcmp(line, "n3d") == 0) + conf->CoeffScale = ADS_N3D; + else if(strcmp(line, "sn3d") == 0) + conf->CoeffScale = ADS_SN3D; + else if(strcmp(line, "fuma") == 0) + conf->CoeffScale = ADS_FuMa; + else + { + ERR("Unsupported coeff scale: %s\n", line); + goto fail; + } + } + else + { + ERR("Unexpected /dec option: %s\n", dec); + goto fail; + } + } + else if(strcmp(command, "opt") == 0) + { + const char *opt = my_strtok_r(NULL, "/ \t", &saveptr); + if(strcmp(opt, "xover_freq") == 0) + { + line = my_strtok_r(NULL, "", &saveptr); + line = read_float(&conf->XOverFreq, line); + if(line && *line != '\0') + { + ERR("Extra junk after xover_freq: %s\n", line); + goto fail; + } + } + else if(strcmp(opt, "xover_ratio") == 0) + { + line = my_strtok_r(NULL, "", &saveptr); + line = read_float(&conf->XOverRatio, line); + if(line && *line != '\0') + { + ERR("Extra junk after xover_ratio: %s\n", line); + goto fail; + } + } + else if(strcmp(opt, "input_scale") == 0 || strcmp(opt, "nfeff_comp") == 0 || + strcmp(opt, "delay_comp") == 0 || strcmp(opt, "level_comp") == 0) + { + /* Unused */ + my_strtok_r(NULL, " \t", &saveptr); + } + else + { + ERR("Unexpected /opt option: %s\n", opt); + goto fail; + } + } + else if(strcmp(command, "speakers") == 0) + { + const char *value = my_strtok_r(NULL, "/ \t", &saveptr); + if(strcmp(value, "{") != 0) + { + ERR("Expected { after %s command, got %s\n", command, value); + goto fail; + } + if(!load_ambdec_speakers(conf, f, &buffer, &maxlen, &saveptr)) + goto fail; + value = my_strtok_r(NULL, "/ \t", &saveptr); + if(!value) + { + line = read_clipped_line(f, &buffer, &maxlen); + if(!line) + { + ERR("Unexpected end of file\n"); + goto fail; + } + value = my_strtok_r(line, "/ \t", &saveptr); + } + if(strcmp(value, "}") != 0) + { + ERR("Expected } after speaker definitions, got %s\n", value); + goto fail; + } + } + else if(strcmp(command, "lfmatrix") == 0 || strcmp(command, "hfmatrix") == 0 || + strcmp(command, "matrix") == 0) + { + const char *value = my_strtok_r(NULL, "/ \t", &saveptr); + if(strcmp(value, "{") != 0) + { + ERR("Expected { after %s command, got %s\n", command, value); + goto fail; + } + if(conf->FreqBands == 1) + { + if(strcmp(command, "matrix") != 0) + { + ERR("Unexpected \"%s\" type for a single-band decoder\n", command); + goto fail; + } + if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers, + f, &buffer, &maxlen, &saveptr)) + goto fail; + } + else + { + if(strcmp(command, "lfmatrix") == 0) + { + if(!load_ambdec_matrix(conf->LFOrderGain, conf->LFMatrix, conf->NumSpeakers, + f, &buffer, &maxlen, &saveptr)) + goto fail; + } + else if(strcmp(command, "hfmatrix") == 0) + { + if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers, + f, &buffer, &maxlen, &saveptr)) + goto fail; + } + else + { + ERR("Unexpected \"%s\" type for a dual-band decoder\n", command); + goto fail; + } + } + value = my_strtok_r(NULL, "/ \t", &saveptr); + if(!value) + { + line = read_clipped_line(f, &buffer, &maxlen); + if(!line) + { + ERR("Unexpected end of file\n"); + goto fail; + } + value = my_strtok_r(line, "/ \t", &saveptr); + } + if(strcmp(value, "}") != 0) + { + ERR("Expected } after matrix definitions, got %s\n", value); + goto fail; + } + } + else if(strcmp(command, "end") == 0) + { + line = my_strtok_r(NULL, "/ \t", &saveptr); + if(line) + { + ERR("Unexpected junk on end: %s\n", line); + goto fail; + } + + fclose(f); + free(buffer); + return 1; + } + else + { + ERR("Unexpected command: %s\n", command); + goto fail; + } + + line = my_strtok_r(NULL, "/ \t", &saveptr); + if(line) + { + ERR("Unexpected junk on line: %s\n", line); + goto fail; + } + } + ERR("Unexpected end of file\n"); + +fail: + fclose(f); + free(buffer); + return 0; +} diff --git a/libs/openal-soft/Alc/ambdec.h b/libs/openal-soft/Alc/ambdec.h new file mode 100644 index 00000000..0bb84072 --- /dev/null +++ b/libs/openal-soft/Alc/ambdec.h @@ -0,0 +1,46 @@ +#ifndef AMBDEC_H +#define AMBDEC_H + +#include "alstring.h" +#include "alMain.h" + +/* Helpers to read .ambdec configuration files. */ + +enum AmbDecScaleType { + ADS_N3D, + ADS_SN3D, + ADS_FuMa, +}; +typedef struct AmbDecConf { + al_string Description; + ALuint Version; /* Must be 3 */ + + ALuint ChanMask; + ALuint FreqBands; /* Must be 1 or 2 */ + ALsizei NumSpeakers; + enum AmbDecScaleType CoeffScale; + + ALfloat XOverFreq; + ALfloat XOverRatio; + + struct { + al_string Name; + ALfloat Distance; + ALfloat Azimuth; + ALfloat Elevation; + al_string Connection; + } Speakers[MAX_OUTPUT_CHANNELS]; + + /* Unused when FreqBands == 1 */ + ALfloat LFOrderGain[MAX_AMBI_ORDER+1]; + ALfloat LFMatrix[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS]; + + ALfloat HFOrderGain[MAX_AMBI_ORDER+1]; + ALfloat HFMatrix[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS]; +} AmbDecConf; + +void ambdec_init(AmbDecConf *conf); +void ambdec_deinit(AmbDecConf *conf); +int ambdec_load(AmbDecConf *conf, const char *fname); + +#endif /* AMBDEC_H */ diff --git a/libs/openal-soft/Alc/backends/alsa.c b/libs/openal-soft/Alc/backends/alsa.c index 43c46484..79f2795a 100644 --- a/libs/openal-soft/Alc/backends/alsa.c +++ b/libs/openal-soft/Alc/backends/alsa.c @@ -199,15 +199,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 +221,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 @@ -237,16 +244,13 @@ static vector_DevMap CaptureDevices; static void clear_devlist(vector_DevMap *devlist) { - DevMap *iter, *end; - - iter = VECTOR_ITER_BEGIN(*devlist); - end = VECTOR_ITER_END(*devlist); - for(;iter != end;iter++) - { - AL_STRING_DEINIT(iter->name); - AL_STRING_DEINIT(iter->device_name); - } - VECTOR_RESIZE(*devlist, 0); +#define FREE_DEV(i) do { \ + AL_STRING_DEINIT((i)->name); \ + AL_STRING_DEINIT((i)->device_name); \ +} while(0) + VECTOR_FOR_EACH(DevMap, *devlist, FREE_DEV); + VECTOR_RESIZE(*devlist, 0, 0); +#undef FREE_DEV } @@ -272,11 +276,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)); @@ -321,7 +359,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; @@ -333,15 +372,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); @@ -413,7 +452,7 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self); static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self); static DECLARE_FORWARD2(ALCplaybackAlsa, ALCbackend, ALCenum, captureSamples, void*, ALCuint) static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, ALCuint, availableSamples) -static ALint64 ALCplaybackAlsa_getLatency(ALCplaybackAlsa *self); +static ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self); static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, lock) static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, unlock) DECLARE_DEFAULT_ALLOCATORS(ALCplaybackAlsa) @@ -588,7 +627,9 @@ static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr) { case -EAGAIN: continue; +#if ESTRPIPE != EPIPE case -ESTRPIPE: +#endif case -EPIPE: case -EINTR: ret = snd_pcm_recover(self->pcmHandle, ret, 1); @@ -630,12 +671,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_ITER_END(PlaybackDevices)) + 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 { @@ -654,7 +695,7 @@ 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; } @@ -705,7 +746,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; @@ -749,7 +790,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, @@ -762,20 +803,24 @@ 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) ERR("Failed to disable ALSA resampler\n"); } + else if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 1) < 0) + ERR("Failed to enable ALSA resampler\n"); CHECK(snd_pcm_hw_params_set_rate_near(self->pcmHandle, hp, &rate, NULL)); /* set buffer time (implicitly constrains period/buffer parameters) */ if((err=snd_pcm_hw_params_set_buffer_time_near(self->pcmHandle, hp, &bufferLen, NULL)) < 0) @@ -840,7 +885,7 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self) self->size = snd_pcm_frames_to_bytes(self->pcmHandle, device->UpdateSize); if(access == SND_PCM_ACCESS_RW_INTERLEAVED) { - self->buffer = malloc(self->size); + self->buffer = al_malloc(16, self->size); if(!self->buffer) { ERR("buffer malloc failed\n"); @@ -862,7 +907,7 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self) if(althrd_create(&self->thread, thread_func, self) != althrd_success) { ERR("Could not create playback thread\n"); - free(self->buffer); + al_free(self->buffer); self->buffer = NULL; return ALC_FALSE; } @@ -885,22 +930,29 @@ static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self) self->killNow = 1; althrd_join(self->thread, &res); - free(self->buffer); + al_free(self->buffer); self->buffer = NULL; } -static ALint64 ALCplaybackAlsa_getLatency(ALCplaybackAlsa *self) +static ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; snd_pcm_sframes_t delay = 0; + ClockLatency ret; int err; + ALCplaybackAlsa_lock(self); + ret.ClockTime = GetDeviceClockTime(device); if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0) { ERR("Failed to get pcm delay: %s\n", snd_strerror(err)); - return 0; + delay = 0; } - return maxi64((ALint64)delay*1000000000/device->Frequency, 0); + if(delay < 0) delay = 0; + ret.Latency = delay * DEVICE_CLOCK_RES / device->Frequency; + ALCplaybackAlsa_unlock(self); + + return ret; } @@ -913,7 +965,7 @@ typedef struct ALCcaptureAlsa { ALsizei size; ALboolean doCapture; - RingBuffer *ring; + ll_ringbuffer_t *ring; snd_pcm_sframes_t last_avail; } ALCcaptureAlsa; @@ -927,7 +979,7 @@ static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self); static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self); static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples); static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self); -static ALint64 ALCcaptureAlsa_getLatency(ALCcaptureAlsa *self); +static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self); static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, lock) static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, unlock) DECLARE_DEFAULT_ALLOCATORS(ALCcaptureAlsa) @@ -961,12 +1013,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_ITER_END(CaptureDevices)) + 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 { @@ -1023,7 +1075,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) */ @@ -1045,24 +1097,18 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name) if(needring) { - self->ring = CreateRingBuffer(FrameSizeFromDevFmt(device->FmtChans, device->FmtType), - device->UpdateSize*device->NumUpdates); + self->ring = ll_ringbuffer_create( + device->UpdateSize*device->NumUpdates + 1, + FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder) + ); if(!self->ring) { ERR("ring buffer create failed\n"); goto error2; } - - self->size = snd_pcm_frames_to_bytes(self->pcmHandle, periodSizeInFrames); - self->buffer = malloc(self->size); - if(!self->buffer) - { - ERR("buffer malloc failed\n"); - goto error2; - } } - al_string_copy_cstr(&device->DeviceName, name); + alstr_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; @@ -1071,9 +1117,7 @@ error: if(hp) snd_pcm_hw_params_free(hp); error2: - free(self->buffer); - self->buffer = NULL; - DestroyRingBuffer(self->ring); + ll_ringbuffer_free(self->ring); self->ring = NULL; snd_pcm_close(self->pcmHandle); @@ -1083,9 +1127,9 @@ error2: static void ALCcaptureAlsa_close(ALCcaptureAlsa *self) { snd_pcm_close(self->pcmHandle); - DestroyRingBuffer(self->ring); + ll_ringbuffer_free(self->ring); - free(self->buffer); + al_free(self->buffer); self->buffer = NULL; } @@ -1120,11 +1164,11 @@ static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self) void *ptr; size = snd_pcm_frames_to_bytes(self->pcmHandle, avail); - ptr = malloc(size); + ptr = al_malloc(16, size); if(ptr) { ALCcaptureAlsa_captureSamples(self, ptr, avail); - free(self->buffer); + al_free(self->buffer); self->buffer = ptr; self->size = size; } @@ -1141,7 +1185,7 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff if(self->ring) { - ReadRingBuffer(self->ring, buffer, samples); + ll_ringbuffer_read(self->ring, buffer, samples); return ALC_NO_ERROR; } @@ -1166,7 +1210,7 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff } else { - free(self->buffer); + al_free(self->buffer); self->buffer = NULL; self->size = 0; } @@ -1244,12 +1288,15 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self) while(avail > 0) { + ll_ringbuffer_data_t vec[2]; snd_pcm_sframes_t amt; - amt = snd_pcm_bytes_to_frames(self->pcmHandle, self->size); - if(avail < amt) amt = avail; + ll_ringbuffer_get_write_vector(self->ring, vec); + if(vec[0].len == 0) break; - amt = snd_pcm_readi(self->pcmHandle, self->buffer, amt); + amt = (vec[0].len < (snd_pcm_uframes_t)avail) ? + vec[0].len : (snd_pcm_uframes_t)avail; + amt = snd_pcm_readi(self->pcmHandle, vec[0].buf, amt); if(amt < 0) { ERR("read error: %s\n", snd_strerror(amt)); @@ -1273,32 +1320,39 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self) continue; } - WriteRingBuffer(self->ring, self->buffer, amt); + ll_ringbuffer_write_advance(self->ring, amt); avail -= amt; } - return RingBufferSize(self->ring); + return ll_ringbuffer_read_space(self->ring); } -static ALint64 ALCcaptureAlsa_getLatency(ALCcaptureAlsa *self) +static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; snd_pcm_sframes_t delay = 0; + ClockLatency ret; int err; + ALCcaptureAlsa_lock(self); + ret.ClockTime = GetDeviceClockTime(device); if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0) { ERR("Failed to get pcm delay: %s\n", snd_strerror(err)); - return 0; + delay = 0; } - return maxi64((ALint64)delay*1000000000/device->Frequency, 0); + if(delay < 0) delay = 0; + ret.Latency = delay * DEVICE_CLOCK_RES / device->Frequency; + ALCcaptureAlsa_unlock(self); + + return ret; } 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/libs/openal-soft/Alc/backends/base.c b/libs/openal-soft/Alc/backends/base.c index ebeb31bf..5f568d8e 100644 --- a/libs/openal-soft/Alc/backends/base.c +++ b/libs/openal-soft/Alc/backends/base.c @@ -4,17 +4,19 @@ #include #include "alMain.h" +#include "alu.h" #include "backends/base.h" +extern inline ALuint64 GetDeviceClockTime(ALCdevice *device); + /* Base ALCbackend method implementations. */ void ALCbackend_Construct(ALCbackend *self, ALCdevice *device) { - int ret; - self->mDevice = device; - ret = almtx_init(&self->mMutex, almtx_recursive); + int ret = almtx_init(&self->mMutex, almtx_recursive); assert(ret == althrd_success); + self->mDevice = device; } void ALCbackend_Destruct(ALCbackend *self) @@ -37,9 +39,27 @@ ALCuint ALCbackend_availableSamples(ALCbackend* UNUSED(self)) return 0; } -ALint64 ALCbackend_getLatency(ALCbackend* UNUSED(self)) +ClockLatency ALCbackend_getClockLatency(ALCbackend *self) { - return 0; + ALCdevice *device = self->mDevice; + ALuint refcount; + ClockLatency ret; + + 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; } void ALCbackend_lock(ALCbackend *self) @@ -59,157 +79,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, ALint64, getLatency) -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, ALint64, getLatency) -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/libs/openal-soft/Alc/backends/base.h b/libs/openal-soft/Alc/backends/base.h index f6b4b80a..8464fdee 100644 --- a/libs/openal-soft/Alc/backends/base.h +++ b/libs/openal-soft/Alc/backends/base.h @@ -5,6 +5,21 @@ #include "threads.h" +typedef struct ClockLatency { + ALint64 ClockTime; + ALint64 Latency; +} ClockLatency; + +/* Helper to get the current clock time from the device's ClockBase, and + * SamplesDone converted from the sample rate. + */ +inline ALuint64 GetDeviceClockTime(ALCdevice *device) +{ + return device->ClockBase + (device->SamplesDone * DEVICE_CLOCK_RES / + device->Frequency); +} + + struct ALCbackendVtable; typedef struct ALCbackend { @@ -20,7 +35,7 @@ void ALCbackend_Destruct(ALCbackend *self); ALCboolean ALCbackend_reset(ALCbackend *self); ALCenum ALCbackend_captureSamples(ALCbackend *self, void *buffer, ALCuint samples); ALCuint ALCbackend_availableSamples(ALCbackend *self); -ALint64 ALCbackend_getLatency(ALCbackend *self); +ClockLatency ALCbackend_getClockLatency(ALCbackend *self); void ALCbackend_lock(ALCbackend *self); void ALCbackend_unlock(ALCbackend *self); @@ -37,7 +52,7 @@ struct ALCbackendVtable { ALCenum (*const captureSamples)(ALCbackend*, void*, ALCuint); ALCuint (*const availableSamples)(ALCbackend*); - ALint64 (*const getLatency)(ALCbackend*); + ClockLatency (*const getClockLatency)(ALCbackend*); void (*const lock)(ALCbackend*); void (*const unlock)(ALCbackend*); @@ -54,7 +69,7 @@ DECLARE_THUNK(T, ALCbackend, ALCboolean, start) \ DECLARE_THUNK(T, ALCbackend, void, stop) \ DECLARE_THUNK2(T, ALCbackend, ALCenum, captureSamples, void*, ALCuint) \ DECLARE_THUNK(T, ALCbackend, ALCuint, availableSamples) \ -DECLARE_THUNK(T, ALCbackend, ALint64, getLatency) \ +DECLARE_THUNK(T, ALCbackend, ClockLatency, getClockLatency) \ DECLARE_THUNK(T, ALCbackend, void, lock) \ DECLARE_THUNK(T, ALCbackend, void, unlock) \ static void T##_ALCbackend_Delete(void *ptr) \ @@ -70,7 +85,7 @@ static const struct ALCbackendVtable T##_ALCbackend_vtable = { \ T##_ALCbackend_stop, \ T##_ALCbackend_captureSamples, \ T##_ALCbackend_availableSamples, \ - T##_ALCbackend_getLatency, \ + T##_ALCbackend_getClockLatency, \ T##_ALCbackend_lock, \ T##_ALCbackend_unlock, \ \ @@ -122,17 +137,19 @@ 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 *ALCsndioBackendFactory_getFactory(void); +ALCbackendFactory *ALCqsaBackendFactory_getFactory(void); ALCbackendFactory *ALCmmdevBackendFactory_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 *ALCloopbackFactory_getFactory(void); -ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type); - #endif /* AL_BACKENDS_BASE_H */ diff --git a/libs/openal-soft/Alc/backends/coreaudio.c b/libs/openal-soft/Alc/backends/coreaudio.c index d9264217..ec926d3d 100644 --- a/libs/openal-soft/Alc/backends/coreaudio.c +++ b/libs/openal-soft/Alc/backends/coreaudio.c @@ -33,6 +33,8 @@ #include #include +#include "backends/base.h" + typedef struct { AudioUnit audioUnit; @@ -45,23 +47,12 @@ typedef struct { AudioBufferList *bufferList; // Buffer for data coming from the input device ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling - RingBuffer *ring; + ll_ringbuffer_t *ring; } ca_data; static const ALCchar ca_device[] = "CoreAudio Default"; -static void destroy_buffer_list(AudioBufferList* list) -{ - if(list) - { - UInt32 i; - for(i = 0;i < list->mNumberBuffers;i++) - free(list->mBuffers[i].mData); - free(list); - } -} - static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSize) { AudioBufferList *list; @@ -83,68 +74,85 @@ static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSiz return list; } -static OSStatus ca_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) +static void destroy_buffer_list(AudioBufferList* list) { - ALCdevice *device = (ALCdevice*)inRefCon; - ca_data *data = (ca_data*)device->ExtraData; - - aluMixData(device, ioData->mBuffers[0].mData, - ioData->mBuffers[0].mDataByteSize / data->frameSize); - - return noErr; -} - -static OSStatus ca_capture_conversion_callback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, - AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void* inUserData) -{ - ALCdevice *device = (ALCdevice*)inUserData; - ca_data *data = (ca_data*)device->ExtraData; - - // Read from the ring buffer and store temporarily in a large buffer - ReadRingBuffer(data->ring, data->resampleBuffer, (ALsizei)(*ioNumberDataPackets)); - - // Set the input data - ioData->mNumberBuffers = 1; - ioData->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame; - ioData->mBuffers[0].mData = data->resampleBuffer; - ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * data->format.mBytesPerFrame; - - return noErr; -} - -static OSStatus ca_capture_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, - UInt32 inNumberFrames, AudioBufferList *ioData) -{ - ALCdevice *device = (ALCdevice*)inRefCon; - ca_data *data = (ca_data*)device->ExtraData; - AudioUnitRenderActionFlags flags = 0; - OSStatus err; - - // fill the bufferList with data from the input device - err = AudioUnitRender(data->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, data->bufferList); - if(err != noErr) + if(list) { - ERR("AudioUnitRender error: %d\n", err); - return err; + UInt32 i; + for(i = 0;i < list->mNumberBuffers;i++) + free(list->mBuffers[i].mData); + free(list); } +} - WriteRingBuffer(data->ring, data->bufferList->mBuffers[0].mData, inNumberFrames); + +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 void ALCcoreAudioPlayback_close(ALCcoreAudioPlayback *self); +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) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCcoreAudioPlayback, ALCbackend, self); + + self->frameSize = 0; + memset(&self->format, 0, sizeof(self->format)); +} + +static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self) +{ + ALCbackend_Destruct(STATIC_CAST(ALCbackend, self)); +} + + +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; + + ALCdevice_Lock(device); + aluMixData(device, ioData->mBuffers[0].mData, + ioData->mBuffers[0].mDataByteSize / self->frameSize); + ALCdevice_Unlock(device); 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 +169,47 @@ 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 void ALCcoreAudioPlayback_close(ALCcoreAudioPlayback *self) { - ca_data *data = (ca_data*)device->ExtraData; - - AudioUnitUninitialize(data->audioUnit); - AudioComponentInstanceDispose(data->audioUnit); - - free(data); - device->ExtraData = NULL; + AudioUnitUninitialize(self->audioUnit); + AudioComponentInstanceDispose(self->audioUnit); } -static ALCboolean ca_reset_playback(ALCdevice *device) +static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self) { - 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 +227,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"); @@ -238,7 +236,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device) if(device->Frequency != streamFormat.mSampleRate) { - device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize * + device->NumUpdates = (ALuint)((ALuint64)device->NumUpdates * streamFormat.mSampleRate / device->Frequency); device->Frequency = streamFormat.mSampleRate; @@ -313,7 +311,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 +319,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 +331,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 +341,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 +353,107 @@ 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 void ALCcoreAudioCapture_close(ALCcoreAudioCapture *self); +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 void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device) { + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCcoreAudioCapture, ALCbackend, self); + +} + +static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self) +{ + 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 +465,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 +486,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 +496,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 +505,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 +533,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 +541,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 +552,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 +561,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,7 +608,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName) case DevFmtX51Rear: case DevFmtX61: case DevFmtX71: - case DevFmtBFormat3D: + case DevFmtAmbi3D: ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans)); goto error; } @@ -541,8 +621,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 @@ -550,11 +630,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"); @@ -562,8 +642,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); @@ -571,7 +651,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); @@ -579,71 +659,71 @@ 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 = CreateRingBuffer(data->frameSize, (device->UpdateSize * data->sampleRateRatio) * device->NumUpdates); - if(data->ring == NULL) - goto error; + self->ring = ll_ringbuffer_create( + device->UpdateSize*self->sampleRateRatio*device->NumUpdates + 1, + self->frameSize + ); + if(!self->ring) goto error; - al_string_copy_cstr(&device->DeviceName, deviceName); + alstr_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; error: - DestroyRingBuffer(data->ring); - free(data->resampleBuffer); - destroy_buffer_list(data->bufferList); + ll_ringbuffer_free(self->ring); + self->ring = NULL; + free(self->resampleBuffer); + destroy_buffer_list(self->bufferList); - if(data->audioConverter) - AudioConverterDispose(data->audioConverter); - if(data->audioUnit) - AudioComponentInstanceDispose(data->audioUnit); - - free(data); - device->ExtraData = NULL; + if(self->audioConverter) + AudioConverterDispose(self->audioConverter); + if(self->audioUnit) + AudioComponentInstanceDispose(self->audioUnit); return ALC_INVALID_VALUE; } -static void ca_close_capture(ALCdevice *device) + +static void ALCcoreAudioCapture_close(ALCcoreAudioCapture *self) { - ca_data *data = (ca_data*)device->ExtraData; + ll_ringbuffer_free(self->ring); + self->ring = NULL; - DestroyRingBuffer(data->ring); - free(data->resampleBuffer); - destroy_buffer_list(data->bufferList); + free(self->resampleBuffer); - AudioConverterDispose(data->audioConverter); - AudioComponentInstanceDispose(data->audioUnit); + destroy_buffer_list(self->bufferList); - free(data); - device->ExtraData = NULL; + AudioConverterDispose(self->audioConverter); + AudioComponentInstanceDispose(self->audioUnit); } -static void ca_start_capture(ALCdevice *device) +static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self) { - 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; UInt32 frameCount; OSStatus err; @@ -657,14 +737,15 @@ static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint sa // 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].mNumberChannels = self->format.mChannelsPerFrame; + list->mBuffers[0].mDataByteSize = samples * self->frameSize; 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, list, NULL + ); if(err != noErr) { ERR("AudioConverterFillComplexBuffer error: %d\n", err); @@ -673,38 +754,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 RingBufferSize(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) { @@ -716,3 +806,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/libs/openal-soft/Alc/backends/dsound.c b/libs/openal-soft/Alc/backends/dsound.c index f27ab37b..f730dc95 100644 --- a/libs/openal-soft/Alc/backends/dsound.c +++ b/libs/openal-soft/Alc/backends/dsound.c @@ -123,7 +123,7 @@ static void clear_devlist(vector_DevMap *list) { #define DEINIT_STR(i) AL_STRING_DEINIT((i)->name) VECTOR_FOR_EACH(DevMap, *list, DEINIT_STR); - VECTOR_RESIZE(*list, 0); + VECTOR_RESIZE(*list, 0, 0); #undef DEINIT_STR } @@ -145,18 +145,18 @@ 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_ITER_END(*devices)) break; + if(iter == VECTOR_END(*devices)) break; #undef MATCH_ENTRY count++; } @@ -165,7 +165,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); } @@ -199,7 +199,7 @@ static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self); static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self); static DECLARE_FORWARD2(ALCdsoundPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint) static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALCuint, availableSamples) -static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ClockLatency, getClockLatency) static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, lock) static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, unlock) DECLARE_DEFAULT_ALLOCATORS(ALCdsoundPlayback) @@ -244,7 +244,7 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr) 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); @@ -299,8 +299,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); @@ -341,23 +343,23 @@ 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_ITER_END(PlaybackDevices)) + if(iter == VECTOR_END(PlaybackDevices)) return ALC_INVALID_VALUE; guid = &iter->guid; } hr = DS_OK; - self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL); if(self->NotifyEvent == NULL) hr = E_FAIL; @@ -379,7 +381,7 @@ 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; } @@ -472,7 +474,7 @@ static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self) case DevFmtMono: OutputType.dwChannelMask = SPEAKER_FRONT_CENTER; break; - case DevFmtBFormat3D: + case DevFmtAmbi3D: device->FmtChans = DevFmtStereo; /*fall-through*/ case DevFmtStereo: @@ -525,7 +527,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; @@ -653,7 +655,8 @@ typedef struct ALCdsoundCapture { IDirectSoundCaptureBuffer *DSCbuffer; DWORD BufferBytes; DWORD Cursor; - RingBuffer *Ring; + + ll_ringbuffer_t *Ring; } ALCdsoundCapture; static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device); @@ -665,7 +668,7 @@ static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self); static void ALCdsoundCapture_stop(ALCdsoundCapture *self); static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples); static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self); -static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ClockLatency, getClockLatency) static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, lock) static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, unlock) DECLARE_DEFAULT_ALLOCATORS(ALCdsoundCapture) @@ -701,17 +704,17 @@ 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_ITER_END(CaptureDevices)) + if(iter == VECTOR_END(CaptureDevices)) return ALC_INVALID_VALUE; guid = &iter->guid; } @@ -731,99 +734,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 DevFmtBFormat3D: - break; - } - - InputType.Format.wFormatTag = WAVE_FORMAT_PCM; - InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans); - InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8; - InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8; - InputType.Format.nSamplesPerSec = device->Frequency; - InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign; - InputType.Format.cbSize = 0; - - if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat) - { - InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); - InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample; - if(device->FmtType == DevFmtFloat) - InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - else - InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - } - - samples = device->UpdateSize * device->NumUpdates; - samples = maxu(samples, 100 * device->Frequency / 1000); - - memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC)); - DSCBDescription.dwSize = sizeof(DSCBUFFERDESC); - DSCBDescription.dwFlags = 0; - DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign; - DSCBDescription.lpwfxFormat = &InputType.Format; - hr = IDirectSoundCapture_CreateCaptureBuffer(self->DSC, &DSCBDescription, &self->DSCbuffer, NULL); - } if(SUCCEEDED(hr)) { - self->Ring = CreateRingBuffer(InputType.Format.nBlockAlign, device->UpdateSize * device->NumUpdates); + self->Ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates + 1, + InputType.Format.nBlockAlign); if(self->Ring == NULL) hr = DSERR_OUTOFMEMORY; } @@ -832,7 +834,7 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi { ERR("Device init failed: 0x%08lx\n", hr); - DestroyRingBuffer(self->Ring); + ll_ringbuffer_free(self->Ring); self->Ring = NULL; if(self->DSCbuffer != NULL) IDirectSoundCaptureBuffer_Release(self->DSCbuffer); @@ -847,14 +849,14 @@ 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) { - DestroyRingBuffer(self->Ring); + ll_ringbuffer_free(self->Ring); self->Ring = NULL; if(self->DSCbuffer != NULL) @@ -897,7 +899,7 @@ static void ALCdsoundCapture_stop(ALCdsoundCapture *self) static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples) { - ReadRingBuffer(self->Ring, buffer, samples); + ll_ringbuffer_read(self->Ring, buffer, samples); return ALC_NO_ERROR; } @@ -913,7 +915,7 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self) if(!device->Connected) goto done; - FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); BufferBytes = self->BufferBytes; LastCursor = self->Cursor; @@ -929,9 +931,9 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self) } if(SUCCEEDED(hr)) { - WriteRingBuffer(self->Ring, ReadPtr1, ReadCnt1/FrameSize); + ll_ringbuffer_write(self->Ring, ReadPtr1, ReadCnt1/FrameSize); if(ReadPtr2 != NULL) - WriteRingBuffer(self->Ring, ReadPtr2, ReadCnt2/FrameSize); + ll_ringbuffer_write(self->Ring, ReadPtr2, ReadCnt2/FrameSize); hr = IDirectSoundCaptureBuffer_Unlock(self->DSCbuffer, ReadPtr1, ReadCnt1, ReadPtr2, ReadCnt2); @@ -945,14 +947,14 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self) } done: - return RingBufferSize(self->Ring); + return 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/libs/openal-soft/Alc/backends/jack.c b/libs/openal-soft/Alc/backends/jack.c index 69d1277a..ceff0b3a 100644 --- a/libs/openal-soft/Alc/backends/jack.c +++ b/libs/openal-soft/Alc/backends/jack.c @@ -54,6 +54,7 @@ static const ALCchar jackDevice[] = "JACK Default"; MAGIC(jack_get_ports); \ MAGIC(jack_free); \ MAGIC(jack_get_sample_rate); \ + MAGIC(jack_set_error_function); \ MAGIC(jack_set_process_callback); \ MAGIC(jack_set_buffer_size_callback); \ MAGIC(jack_set_buffer_size); \ @@ -62,6 +63,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 @@ -78,10 +80,12 @@ JACK_FUNCS(MAKE_FUNC); #define jack_get_ports pjack_get_ports #define jack_free pjack_free #define jack_get_sample_rate pjack_get_sample_rate +#define jack_set_error_function pjack_set_error_function #define jack_set_process_callback pjack_set_process_callback #define jack_set_buffer_size_callback pjack_set_buffer_size_callback #define jack_set_buffer_size pjack_set_buffer_size #define jack_get_buffer_size pjack_get_buffer_size +#define jack_error_callback (*pjack_error_callback) #endif @@ -94,26 +98,42 @@ static ALCboolean jack_load(void) #ifdef HAVE_DYNLOAD if(!jack_handle) { - jack_handle = LoadLib("libjack.so.0"); + al_string missing_funcs = AL_STRING_INIT_STATIC(); + +#ifdef _WIN32 +#define JACKLIB "libjack.dll" +#else +#define JACKLIB "libjack.so.0" +#endif + jack_handle = LoadLib(JACKLIB); if(!jack_handle) + { + WARN("Failed to load %s\n", JACKLIB); return ALC_FALSE; + } error = ALC_FALSE; #define LOAD_FUNC(f) do { \ p##f = 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 @@ -148,9 +168,9 @@ 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 ALint64 ALCjackPlayback_getLatency(ALCjackPlayback *self); -static void ALCjackPlayback_lock(ALCjackPlayback *self); -static void ALCjackPlayback_unlock(ALCjackPlayback *self); +static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self); +static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, unlock) DECLARE_DEFAULT_ALLOCATORS(ALCjackPlayback) DEFINE_ALCBACKEND_VTABLE(ALCjackPlayback); @@ -204,15 +224,19 @@ 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; + + 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) + ); if(!self->Ring) { ERR("Failed to reallocate ringbuffer\n"); @@ -230,7 +254,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); @@ -241,8 +265,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; @@ -252,8 +277,9 @@ 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; @@ -267,7 +293,7 @@ static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg) 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; } } @@ -355,7 +381,7 @@ 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; } @@ -377,7 +403,7 @@ static void ALCjackPlayback_close(ALCjackPlayback *self) 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++) @@ -397,14 +423,15 @@ static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self) 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; /* 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]; @@ -433,7 +460,9 @@ 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) + ); if(!self->Ring) { ERR("Failed to allocate ringbuffer\n"); @@ -448,7 +477,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)) { @@ -506,30 +535,26 @@ static void ALCjackPlayback_stop(ALCjackPlayback *self) } -static ALint64 ALCjackPlayback_getLatency(ALCjackPlayback *self) +static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - ALint64 latency; + ClockLatency ret; ALCjackPlayback_lock(self); - latency = ll_ringbuffer_read_space(self->Ring); + ret.ClockTime = GetDeviceClockTime(device); + ret.Latency = ll_ringbuffer_read_space(self->Ring) * DEVICE_CLOCK_RES / + device->Frequency; ALCjackPlayback_unlock(self); - return latency * 1000000000 / device->Frequency; + return ret; } -static void ALCjackPlayback_lock(ALCjackPlayback *self) +static void jack_msg_handler(const char *message) { - almtx_lock(&STATIC_CAST(ALCbackend,self)->mMutex); + WARN("%s\n", message); } -static void ALCjackPlayback_unlock(ALCjackPlayback *self) -{ - almtx_unlock(&STATIC_CAST(ALCbackend,self)->mMutex); -} - - typedef struct ALCjackBackendFactory { DERIVE_FROM_TYPE(ALCbackendFactory); } ALCjackBackendFactory; @@ -537,6 +562,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; @@ -545,7 +571,11 @@ 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(old_error_cb); if(client == NULL) { WARN("jack_client_open() failed, 0x%02x\n", status); diff --git a/libs/openal-soft/Alc/backends/loopback.c b/libs/openal-soft/Alc/backends/loopback.c index 3e577f78..8f23aad9 100644 --- a/libs/openal-soft/Alc/backends/loopback.c +++ b/libs/openal-soft/Alc/backends/loopback.c @@ -41,7 +41,7 @@ static ALCboolean ALCloopback_start(ALCloopback *self); static void ALCloopback_stop(ALCloopback *self); static DECLARE_FORWARD2(ALCloopback, ALCbackend, ALCenum, captureSamples, void*, ALCuint) static DECLARE_FORWARD(ALCloopback, ALCbackend, ALCuint, availableSamples) -static DECLARE_FORWARD(ALCloopback, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCloopback, ALCbackend, ClockLatency, getClockLatency) static DECLARE_FORWARD(ALCloopback, ALCbackend, void, lock) static DECLARE_FORWARD(ALCloopback, ALCbackend, void, unlock) DECLARE_DEFAULT_ALLOCATORS(ALCloopback) @@ -59,7 +59,7 @@ 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; } diff --git a/libs/openal-soft/Alc/backends/mmdevapi.c b/libs/openal-soft/Alc/backends/mmdevapi.c index 0134a46c..9afb62f0 100644 --- a/libs/openal-soft/Alc/backends/mmdevapi.c +++ b/libs/openal-soft/Alc/backends/mmdevapi.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -43,6 +44,7 @@ #include "threads.h" #include "compat.h" #include "alstring.h" +#include "converter.h" #include "backends/base.h" @@ -52,6 +54,7 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0 DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14); DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0); +DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 ); #define MONO SPEAKER_FRONT_CENTER #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT) @@ -62,11 +65,14 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 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 " typedef struct { al_string name; + al_string endpoint_guid; // obtained from PKEY_AudioEndpoint_GUID , set to "Unknown device GUID" if absent. WCHAR *devid; } DevMap; TYPEDEF_VECTOR(DevMap, vector_DevMap) @@ -75,11 +81,12 @@ static void clear_devlist(vector_DevMap *list) { #define CLEAR_DEVMAP(i) do { \ AL_STRING_DEINIT((i)->name); \ + AL_STRING_DEINIT((i)->endpoint_guid); \ free((i)->devid); \ (i)->devid = NULL; \ } while(0) VECTOR_FOR_EACH(DevMap, *list, CLEAR_DEVMAP); - VECTOR_RESIZE(*list, 0); + VECTOR_RESIZE(*list, 0, 0); #undef CLEAR_DEVMAP } @@ -104,6 +111,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; @@ -119,19 +135,21 @@ static HRESULT WaitForResponse(ThreadRequest *req) } -static void get_device_name(IMMDevice *device, al_string *name) +static void get_device_name_and_guid(IMMDevice *device, al_string *name, al_string *guid) { IPropertyStore *ps; PROPVARIANT pvname; + 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"); + alstr_append_cstr(name, "Unknown Device Name"); + if(guid!=NULL)alstr_copy_cstr(guid, "Unknown Device GUID"); return; } @@ -141,17 +159,37 @@ static void get_device_name(IMMDevice *device, al_string *name) 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); + + if(guid!=NULL){ + PropVariantInit(&pvguid); + + hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&PKEY_AudioEndpoint_GUID, &pvguid); + if(FAILED(hr)) + { + WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr); + alstr_copy_cstr(guid, "Unknown Device GUID"); + } + else if(pvguid.vt == VT_LPWSTR) + alstr_copy_wcstr(guid, pvguid.pwszVal); + else + { + WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvguid.vt); + alstr_copy_cstr(guid, "Unknown Device GUID"); + } + + PropVariantClear(&pvguid); } - PropVariantClear(&pvname); IPropertyStore_Release(ps); } @@ -185,7 +223,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; @@ -193,38 +231,39 @@ static void add_device(IMMDevice *device, LPCWSTR devid, vector_DevMap *list) AL_STRING_INIT(tmpname); AL_STRING_INIT(entry.name); + AL_STRING_INIT(entry.endpoint_guid); entry.devid = strdupW(devid); - get_device_name(device, &tmpname); + get_device_name_and_guid(device, &tmpname, &entry.endpoint_guid); while(1) { 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_ITER_END(*list)) break; + if(iter == VECTOR_END(*list)) break; #undef MATCH_ENTRY count++; } - TRACE("Got device \"%s\", \"%ls\"\n", al_string_get_cstr(entry.name), 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); @@ -241,7 +280,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; @@ -258,11 +297,7 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve if(SUCCEEDED(hr) && count > 0) { clear_devlist(list); - if(!VECTOR_RESERVE(*list, count)) - { - IMMDeviceCollection_Release(coll); - return E_OUTOFMEMORY; - } + VECTOR_RESIZE(*list, 0, count); hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir, eMultimedia, &defdev); @@ -277,7 +312,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; @@ -379,7 +414,11 @@ 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: @@ -508,7 +547,7 @@ static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self); static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self); static DECLARE_FORWARD2(ALCmmdevPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint) static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, ALCuint, availableSamples) -static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self); +static ClockLatency ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback *self); static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, lock) static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, unlock) DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback) @@ -606,10 +645,10 @@ FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg) hr = IAudioRenderClient_GetBuffer(self->render, len, &buffer); if(SUCCEEDED(hr)) { - V0(device->Backend,lock)(); + ALCmmdevPlayback_lock(self); aluMixData(device, buffer, len); self->Padding = written + len; - V0(device->Backend,unlock)(); + ALCmmdevPlayback_unlock(self); hr = IAudioRenderClient_ReleaseBuffer(self->render, len, 0); } if(FAILED(hr)) @@ -667,13 +706,12 @@ static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX * return ALC_TRUE; } - static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *deviceName) { HRESULT hr = S_OK; - self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - self->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL); + self->MsgEvent = CreateEventW(NULL, FALSE, FALSE, NULL); if(self->NotifyEvent == NULL || self->MsgEvent == NULL) { ERR("Failed to create message events: %lu\n", GetLastError()); @@ -694,18 +732,32 @@ static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *devi } hr = E_FAIL; -#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, 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); - if(iter == VECTOR_ITER_END(PlaybackDevices)) +#undef MATCH_NAME + if(iter == VECTOR_END(PlaybackDevices)) + { + int len; + if((len=MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, NULL, 0)) > 0) + { + WCHAR *wname = calloc(sizeof(WCHAR), len); + MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, wname, len); +#define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0) + VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME); +#undef MATCH_NAME + free(wname); + } + } + if(iter == VECTOR_END(PlaybackDevices)) WARN("Failed to find device name matching \"%s\"\n", deviceName); else { 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; } -#undef MATCH_NAME } } @@ -761,8 +813,8 @@ static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self) if(SUCCEEDED(hr)) { self->client = ptr; - if(al_string_empty(device->DeviceName)) - get_device_name(self->mmdev, &device->DeviceName); + if(alstr_empty(device->DeviceName)) + get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL); } if(FAILED(hr)) @@ -854,8 +906,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; @@ -885,7 +937,7 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self) OutputType.Format.nChannels = 1; OutputType.dwChannelMask = MONO; break; - case DevFmtBFormat3D: + case DevFmtAmbi3D: device->FmtChans = DevFmtStereo; /*fall-through*/ case DevFmtStereo: @@ -1026,7 +1078,9 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self) OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; } get_device_formfactor(self->mmdev, &formfactor); - device->IsHeadphones = (device->FmtChans == DevFmtStereo && formfactor == Headphones); + device->IsHeadphones = (device->FmtChans == DevFmtStereo && + (formfactor == Headphones || formfactor == Headset) + ); SetDefaultWFXChannelOrder(device); @@ -1042,7 +1096,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; @@ -1139,10 +1193,17 @@ static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self) } -static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self) +static ClockLatency ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback *self) { ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - return (ALint64)self->Padding * 1000000000 / device->Frequency; + ClockLatency ret; + + ALCmmdevPlayback_lock(self); + ret.ClockTime = GetDeviceClockTime(device); + ret.Latency = self->Padding * DEVICE_CLOCK_RES / device->Frequency; + ALCmmdevPlayback_unlock(self); + + return ret; } @@ -1159,6 +1220,8 @@ typedef struct ALCmmdevCapture { HANDLE MsgEvent; + ChannelConverter *ChannelConv; + SampleConverter *SampleConv; ll_ringbuffer_t *Ring; volatile int killNow; @@ -1181,7 +1244,7 @@ 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, ALint64, getLatency) +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) @@ -1206,6 +1269,8 @@ static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device) self->MsgEvent = NULL; + self->ChannelConv = NULL; + self->SampleConv = NULL; self->Ring = NULL; self->killNow = 0; @@ -1216,6 +1281,9 @@ static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self) ll_ringbuffer_free(self->Ring); self->Ring = NULL; + DestroySampleConverter(&self->SampleConv); + DestroyChannelConverter(&self->ChannelConv); + if(self->NotifyEvent != NULL) CloseHandle(self->NotifyEvent); self->NotifyEvent = NULL; @@ -1235,6 +1303,8 @@ FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg) { ALCmmdevCapture *self = arg; ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + ALfloat *samples = NULL; + size_t samplesmax = 0; HRESULT hr; hr = CoInitialize(NULL); @@ -1257,33 +1327,75 @@ 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, data[0].len + ); + 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, data[1].len + ); + } + } + else + { + size_t framesize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, + device->AmbiOrder); + ALuint len1 = minu(data[0].len, numsamples); + ALuint len2 = minu(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)) @@ -1299,6 +1411,10 @@ FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg) ERR("WaitForSingleObjectEx error: 0x%lx\n", res); } + al_free(samples); + samples = NULL; + samplesmax = 0; + CoUninitialize(); return 0; } @@ -1308,8 +1424,8 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device { HRESULT hr = S_OK; - self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - self->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL); + self->MsgEvent = CreateEventW(NULL, FALSE, FALSE, NULL); if(self->NotifyEvent == NULL || self->MsgEvent == NULL) { ERR("Failed to create message events: %lu\n", GetLastError()); @@ -1330,18 +1446,32 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device } hr = E_FAIL; -#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, 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); - if(iter == VECTOR_ITER_END(CaptureDevices)) +#undef MATCH_NAME + if(iter == VECTOR_END(CaptureDevices)) + { + int len; + if((len=MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, NULL, 0)) > 0) + { + WCHAR *wname = calloc(sizeof(WCHAR), len); + MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, wname, len); +#define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0) + VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME); +#undef MATCH_NAME + free(wname); + } + } + if(iter == VECTOR_END(CaptureDevices)) WARN("Failed to find device name matching \"%s\"\n", deviceName); else { 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; } -#undef MATCH_NAME } } @@ -1415,8 +1545,8 @@ static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self) if(SUCCEEDED(hr)) { self->client = ptr; - if(al_string_empty(device->DeviceName)) - get_device_name(self->mmdev, &device->DeviceName); + if(alstr_empty(device->DeviceName)) + get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL); } if(FAILED(hr)) @@ -1467,6 +1597,7 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *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; @@ -1484,8 +1615,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) @@ -1519,38 +1654,33 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self) OutputType.dwChannelMask = X7DOT1; break; - case DevFmtBFormat3D: + 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 * @@ -1568,26 +1698,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("Did not 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->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, @@ -1608,7 +1819,9 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self) buffer_len = maxu(device->UpdateSize*device->NumUpdates + 1, 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) + ); if(!self->Ring) { ERR("Failed to allocate capture ring buffer\n"); @@ -1713,9 +1926,9 @@ 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 { DERIVE_FROM_TYPE(ALCbackendFactory); @@ -1739,7 +1952,7 @@ static BOOL MMDevApiLoad(void) ThreadRequest req; InitResult = E_FAIL; - req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL); + req.FinishedEvt = CreateEventW(NULL, FALSE, FALSE, NULL); if(req.FinishedEvt == NULL) ERR("Failed to create event: %lu\n", GetLastError()); else @@ -1787,7 +2000,7 @@ static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory* UN * 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; } @@ -1796,7 +2009,7 @@ static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), e { ThreadRequest req = { NULL, 0 }; - req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL); + req.FinishedEvt = CreateEventW(NULL, FALSE, FALSE, NULL); if(req.FinishedEvt == NULL) ERR("Failed to create event: %lu\n", GetLastError()); else diff --git a/libs/openal-soft/Alc/backends/null.c b/libs/openal-soft/Alc/backends/null.c index 99729c0a..e8c43782 100644 --- a/libs/openal-soft/Alc/backends/null.c +++ b/libs/openal-soft/Alc/backends/null.c @@ -51,7 +51,7 @@ static ALCboolean ALCnullBackend_start(ALCnullBackend *self); static void ALCnullBackend_stop(ALCnullBackend *self); static DECLARE_FORWARD2(ALCnullBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint) static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALCuint, availableSamples) -static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ClockLatency, getClockLatency) static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, lock) static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, unlock) DECLARE_DEFAULT_ALLOCATORS(ALCnullBackend) @@ -109,7 +109,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,7 +130,7 @@ 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; } diff --git a/libs/openal-soft/Alc/backends/opensl.c b/libs/openal-soft/Alc/backends/opensl.c index 7b8fdb25..aa1ff991 100644 --- a/libs/openal-soft/Alc/backends/opensl.c +++ b/libs/openal-soft/Alc/backends/opensl.c @@ -22,38 +22,24 @@ #include "config.h" #include +#include #include "alMain.h" #include "alu.h" +#include "compat.h" #include "threads.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,11 +65,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 DevFmtBFormat3D: break; + 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) @@ -123,168 +130,463 @@ 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; + alcnd_t mCond; + + 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 void ALCopenslPlayback_close(ALCopenslPlayback *self); +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; + alcnd_init(&self->mCond); - 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 != NULL) + VCALL0(self->mOutputMix,Destroy)(); + self->mOutputMix = NULL; + + if(self->mEngineObj != NULL) + VCALL0(self->mEngineObj,Destroy)(); + self->mEngineObj = NULL; + self->mEngine = NULL; + + ll_ringbuffer_free(self->mRing); + self->mRing = NULL; + + alcnd_destroy(&self->mCond); + + 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); + + alcnd_signal(&self->mCond); +} + + +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; + size_t padding; + + 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); + ALCopenslPlayback_unlock(self); + return 1; + } + + /* NOTE: The ringbuffer will be larger than the desired buffer metrics. + * Calculate the amount of extra space so we know how much to keep unused. + */ + padding = ll_ringbuffer_write_space(self->mRing) - device->NumUpdates; + + ALCopenslPlayback_lock(self); + while(ATOMIC_LOAD_SEQ(&self->mKillNow) == AL_FALSE && device->Connected) + { + size_t todo, len0, len1; + + if(ll_ringbuffer_write_space(self->mRing) <= padding) + { + 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); + break; + } + + /* 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 checking the write size and waiting for the condition + * variable here. This will cause alcnd_wait to wait until the + * *next* process() invocation signals the condition variable + * again. + * + * 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 completely + * 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->mRing) <= padding) + { + alcnd_wait(&self->mCond, &STATIC_CAST(ALCbackend,self)->mMutex); + continue; + } + } + + ll_ringbuffer_get_write_vector(self->mRing, data); + todo = data[0].len+data[1].len - padding; + + 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(!deviceName) - deviceName = opensl_device; - else if(strcmp(deviceName, opensl_device) != 0) + 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 void ALCopenslPlayback_close(ALCopenslPlayback *self) { - osl_data *data = Device->ExtraData; + if(self->mBufferQueueObj != NULL) + VCALL0(self->mBufferQueueObj,Destroy)(); + self->mBufferQueueObj = NULL; - if(data->bufferQueueObject != NULL) - VCALL0(data->bufferQueueObject,Destroy)(); - data->bufferQueueObject = NULL; + VCALL0(self->mOutputMix,Destroy)(); + self->mOutputMix = NULL; - VCALL0(data->outputMix,Destroy)(); - data->outputMix = NULL; - - VCALL0(data->engineObject,Destroy)(); - data->engineObject = NULL; - data->engine = NULL; - - free(data); - Device->ExtraData = NULL; + VCALL0(self->mEngineObj,Destroy)(); + self->mEngineObj = NULL; + self->mEngine = NULL; } -static ALCboolean opensl_reset_playback(ALCdevice *Device) +static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self) { - 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; } @@ -292,64 +594,35 @@ 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); + /* NOTE: Add an extra update since 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. + */ + self->mRing = ll_ringbuffer_create(device->NumUpdates + 1, + self->mFrameSize*device->UpdateSize); + + 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; } @@ -357,14 +630,27 @@ 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; + + /* 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). + */ + ALCopenslPlayback_lock(self); + ALCopenslPlayback_unlock(self); + alcnd_signal(&self->mCond); + althrd_join(self->mThread, &res); + + result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player); PRINTERR(result, "bufferQueue->GetInterface"); if(SL_RESULT_SUCCESS == result) { @@ -372,7 +658,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) { @@ -380,6 +667,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 { @@ -389,45 +681,455 @@ 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 void ALCopenslCapture_close(ALCopenslCapture *self); +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 + 1, + device->UpdateSize * self->mFrameSize); + + 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 void ALCopenslCapture_close(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; +} + +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); + 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); + 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/libs/openal-soft/Alc/backends/oss.c b/libs/openal-soft/Alc/backends/oss.c index 33d7750a..9c63d9d2 100644 --- a/libs/openal-soft/Alc/backends/oss.c +++ b/libs/openal-soft/Alc/backends/oss.c @@ -88,7 +88,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 +155,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 +163,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 +195,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,7 +243,7 @@ typedef struct ALCplaybackOSS { ALubyte *mix_data; int data_size; - volatile int killNow; + ATOMIC(ALenum) killNow; althrd_t thread; } ALCplaybackOSS; @@ -257,7 +258,7 @@ static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self); static void ALCplaybackOSS_stop(ALCplaybackOSS *self); static DECLARE_FORWARD2(ALCplaybackOSS, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint) static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALCuint, availableSamples) -static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ClockLatency, getClockLatency) static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, lock) static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, unlock) DECLARE_DEFAULT_ALLOCATORS(ALCplaybackOSS) @@ -268,42 +269,64 @@ 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_SEQ(&self->killNow) && device->Connected) { - 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); + 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); + break; } - len -= wrote; - WritePtr += wrote; + to_write -= wrote; + write_ptr += wrote; } } + ALCplaybackOSS_unlock(self); return 0; } @@ -313,6 +336,8 @@ static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device) { ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); SET_VTABLE2(ALCplaybackOSS, ALCbackend, self); + + ATOMIC_INIT(&self->killNow, AL_FALSE); } static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name) @@ -320,22 +345,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,7 +374,7 @@ 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; } @@ -387,18 +418,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 +444,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 +460,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 +471,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 +491,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) @@ -484,13 +508,9 @@ typedef struct ALCcaptureOSS { int fd; - ALubyte *read_data; - int data_size; + ll_ringbuffer_t *ring; - RingBuffer *ring; - int doCapture; - - volatile int killNow; + ATOMIC(ALenum) killNow; althrd_t thread; } ALCcaptureOSS; @@ -505,7 +525,7 @@ static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self); static void ALCcaptureOSS_stop(ALCcaptureOSS *self); static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples); static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self); -static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ClockLatency, getClockLatency) static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, lock) static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, unlock) DECLARE_DEFAULT_ALLOCATORS(ALCcaptureOSS) @@ -516,32 +536,55 @@ static int ALCcaptureOSS_recordProc(void *ptr) { ALCcaptureOSS *self = (ALCcaptureOSS*)ptr; ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; - int frameSize; - int amt; + 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)) { - amt = read(self->fd, self->read_data, self->data_size); - if(amt < 0) + ll_ringbuffer_data_t vec[2]; + + 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) { - ERR("read failed: %s\n", strerror(errno)); - ALCcaptureOSS_lock(self); + if(errno == EINTR) + continue; + ERR("select failed: %s\n", strerror(errno)); aluHandleDisconnect(device); - ALCcaptureOSS_unlock(self); break; } - if(amt == 0) + else if(sret == 0) { - al_nssleep(1000000); + WARN("select timeout\n"); continue; } - if(self->doCapture) - WriteRingBuffer(self->ring, self->read_data, amt/frameSize); + + 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); + ALCcaptureOSS_unlock(self); + break; + } + ll_ringbuffer_write_advance(self->ring, amt/frame_size); + } } return 0; @@ -552,6 +595,8 @@ static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device) { ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); SET_VTABLE2(ALCcaptureOSS, ALCbackend, self); + + ATOMIC_INIT(&self->killNow, AL_FALSE); } static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name) @@ -568,18 +613,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); @@ -609,7 +662,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 * @@ -639,7 +692,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); @@ -657,7 +710,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name) return ALC_INVALID_VALUE; } - self->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates); + self->ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates + 1, frameSize); if(!self->ring) { ERR("Ring buffer create failed\n"); @@ -666,60 +719,50 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name) return ALC_OUT_OF_MEMORY; } - self->data_size = info.fragsize; - self->read_data = calloc(1, self->data_size); - - self->killNow = 0; - if(althrd_create(&self->thread, ALCcaptureOSS_recordProc, self) != althrd_success) - { - device->ExtraData = NULL; - close(self->fd); - self->fd = -1; - return ALC_OUT_OF_MEMORY; - } - - al_string_copy_cstr(&device->DeviceName, name); + 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; - DestroyRingBuffer(self->ring); + ll_ringbuffer_free(self->ring); self->ring = NULL; - - free(self->read_data); - self->read_data = 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) { - ReadRingBuffer(self->ring, buffer, samples); + ll_ringbuffer_read(self->ring, buffer, samples); return ALC_NO_ERROR; } static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self) { - return RingBufferSize(self->ring); + return ll_ringbuffer_read_space(self->ring); } @@ -769,33 +812,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/libs/openal-soft/Alc/backends/portaudio.c b/libs/openal-soft/Alc/backends/portaudio.c index f45833c6..807e8000 100644 --- a/libs/openal-soft/Alc/backends/portaudio.c +++ b/libs/openal-soft/Alc/backends/portaudio.c @@ -145,7 +145,7 @@ static ALCboolean ALCportPlayback_start(ALCportPlayback *self); static void ALCportPlayback_stop(ALCportPlayback *self); static DECLARE_FORWARD2(ALCportPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint) static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ALCuint, availableSamples) -static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ClockLatency, getClockLatency) static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, lock) static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, unlock) DECLARE_DEFAULT_ALLOCATORS(ALCportPlayback) @@ -177,7 +177,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,7 +245,7 @@ retry_open: return ALC_INVALID_VALUE; } - al_string_copy_cstr(&device->DeviceName, name); + alstr_copy_cstr(&device->DeviceName, name); return ALC_NO_ERROR; @@ -340,7 +342,7 @@ static ALCboolean ALCportCapture_start(ALCportCapture *self); static void ALCportCapture_stop(ALCportCapture *self); static ALCenum ALCportCapture_captureSamples(ALCportCapture *self, ALCvoid *buffer, ALCuint samples); static ALCuint ALCportCapture_availableSamples(ALCportCapture *self); -static DECLARE_FORWARD(ALCportCapture, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCportCapture, ALCbackend, ClockLatency, getClockLatency) static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, lock) static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, unlock) DECLARE_DEFAULT_ALLOCATORS(ALCportCapture) @@ -397,7 +399,7 @@ 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); if(self->ring == NULL) return ALC_INVALID_VALUE; @@ -431,7 +433,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,7 +445,7 @@ 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; } diff --git a/libs/openal-soft/Alc/backends/pulseaudio.c b/libs/openal-soft/Alc/backends/pulseaudio.c index 9ad04a71..8c5469b2 100644 --- a/libs/openal-soft/Alc/backends/pulseaudio.c +++ b/libs/openal-soft/Alc/backends/pulseaudio.c @@ -182,6 +182,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 +193,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 +276,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; @@ -443,7 +451,7 @@ static void clear_devlist(vector_DevMap *list) #define DEINIT_STRS(i) (AL_STRING_DEINIT((i)->name),AL_STRING_DEINIT((i)->device_name)) VECTOR_FOR_EACH(DevMap, *list, DEINIT_STRS); #undef DEINIT_STRS - VECTOR_RESIZE(*list, 0); + VECTOR_RESIZE(*list, 0, 0); } @@ -489,7 +497,7 @@ static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self); static void ALCpulsePlayback_stop(ALCpulsePlayback *self); static DECLARE_FORWARD2(ALCpulsePlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint) static DECLARE_FORWARD(ALCpulsePlayback, ALCbackend, ALCuint, availableSamples) -static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self); +static ClockLatency ALCpulsePlayback_getClockLatency(ALCpulsePlayback *self); static void ALCpulsePlayback_lock(ALCpulsePlayback *self); static void ALCpulsePlayback_unlock(ALCpulsePlayback *self); DECLARE_DEFAULT_ALLOCATORS(ALCpulsePlayback) @@ -525,35 +533,35 @@ 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_ITER_END(PlaybackDevices)) return; + if(iter != VECTOR_END(PlaybackDevices)) return; #undef MATCH_INFO_NAME 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_ITER_END(PlaybackDevices)) break; + 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 +626,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) @@ -729,7 +742,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 +750,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 +764,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 +809,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 +817,31 @@ 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(!self->killNow && device->Connected) + { + 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); + 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,11 +850,12 @@ static int ALCpulsePlayback_mixerProc(void *ptr) pa_threaded_mainloop_wait(self->loop); continue; } - len -= len%update_size; + len -= len%self->attr.minreq; while(len > 0) { size_t newlen = len; + int ret; void *buf; pa_free_cb_t free_func = NULL; @@ -834,10 +867,15 @@ static int ALCpulsePlayback_mixerProc(void *ptr) aluMixData(device, buf, newlen/frame_size); - pa_stream_write(self->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE); + ret = pa_stream_write(self->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE); + if(ret != PA_OK) + { + ERR("Failed to write to stream: %d, %s\n", ret, pa_strerror(ret)); + break; + } len -= newlen; } - } while(!self->killNow && device->Connected); + } pa_threaded_mainloop_unlock(self->loop); return 0; @@ -858,12 +896,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_ITER_END(PlaybackDevices)) + 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 +932,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 +944,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); @@ -921,7 +959,7 @@ static void ALCpulsePlayback_close(ALCpulsePlayback *self) self->context = NULL; self->stream = NULL; - al_string_clear(&self->device_name); + alstr_clear(&self->device_name); } static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self) @@ -931,7 +969,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 +983,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 +1021,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,7 +1035,7 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self) case DevFmtMono: mapname = "mono"; break; - case DevFmtBFormat3D: + case DevFmtAmbi3D: device->FmtChans = DevFmtStereo; /*fall-through*/ case DevFmtStereo: @@ -1034,9 +1071,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); @@ -1051,10 +1088,12 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self) { /* Server updated our playback rate, so modify the buffer attribs * accordingly. */ - device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates / device->Frequency * - self->spec.rate + 0.5); + device->NumUpdates = (ALuint)clampd( + (ALdouble)device->NumUpdates/device->Frequency*self->spec.rate + 0.5, 2.0, 16.0 + ); + self->attr.minreq = device->UpdateSize * pa_frame_size(&self->spec); - self->attr.tlength = self->attr.minreq * clampu(device->NumUpdates, 2, 16); + self->attr.tlength = self->attr.minreq * device->NumUpdates; self->attr.maxlength = -1; self->attr.prebuf = 0; @@ -1068,10 +1107,30 @@ 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)((ALdouble)device->NumUpdates/len*device->UpdateSize + 0.5); - device->NumUpdates = clampu(device->NumUpdates, 2, 16); - device->UpdateSize = len; + device->NumUpdates = (ALuint)clampu64( + (self->attr.tlength + self->attr.minreq/2) / self->attr.minreq, 2, 16 + ); + 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 + * write enough audio to start playback. The lack of manual start control + * may have unintended consequences, but it's better than not starting at + * all. + */ + if(self->attr.prebuf != 0) + { + 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); + else + { + ERR("Large prebuf, %u samples (%u bytes), increasing device from %u samples", + len, self->attr.prebuf, device->UpdateSize*device->NumUpdates); + device->NumUpdates = (len+device->UpdateSize-1) / device->UpdateSize; + } + } pa_threaded_mainloop_unlock(self->loop); return ALC_TRUE; @@ -1113,11 +1172,14 @@ static void ALCpulsePlayback_stop(ALCpulsePlayback *self) } -static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self) +static ClockLatency ALCpulsePlayback_getClockLatency(ALCpulsePlayback *self) { pa_usec_t latency = 0; + ClockLatency ret; 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) { /* FIXME: if err = -PA_ERR_NODATA, it means we were called too soon @@ -1126,11 +1188,14 @@ static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self) * dummy value? Either way, it shouldn't be 0. */ if(err != -PA_ERR_NODATA) ERR("Failed to get stream latency: 0x%x\n", err); - return 0; + latency = 0; + neg = 0; } - if(neg) latency = 0; - return (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000; + ret.Latency = minu64(latency, U64(0xffffffffffffffff)/1000) * 1000; + pa_threaded_mainloop_unlock(self->loop); + + return ret; } @@ -1186,7 +1251,7 @@ static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self); static void ALCpulseCapture_stop(ALCpulseCapture *self); static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *buffer, ALCuint samples); static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self); -static ALint64 ALCpulseCapture_getLatency(ALCpulseCapture *self); +static ClockLatency ALCpulseCapture_getClockLatency(ALCpulseCapture *self); static void ALCpulseCapture_lock(ALCpulseCapture *self); static void ALCpulseCapture_unlock(ALCpulseCapture *self); DECLARE_DEFAULT_ALLOCATORS(ALCpulseCapture) @@ -1222,35 +1287,35 @@ 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_ITER_END(CaptureDevices)) return; + if(iter != VECTOR_END(CaptureDevices)) return; #undef MATCH_INFO_NAME 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_ITER_END(CaptureDevices)) break; + 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); } @@ -1343,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); } @@ -1351,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)); } @@ -1403,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; @@ -1413,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_ITER_END(CaptureDevices)) + 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)) @@ -1427,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: @@ -1452,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"); @@ -1481,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); @@ -1492,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); @@ -1521,23 +1622,26 @@ static void ALCpulseCapture_close(ALCpulseCapture *self) self->context = NULL; self->stream = NULL; - al_string_clear(&self->device_name); + alstr_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) @@ -1548,6 +1652,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; @@ -1587,6 +1692,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); @@ -1600,7 +1706,9 @@ static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self) if(device->Connected) { - 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)); @@ -1608,6 +1716,7 @@ static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self) } else if((size_t)got > self->cap_len) readable += got - self->cap_len; + pa_threaded_mainloop_unlock(self->loop); } if(self->last_readable < readable) @@ -1616,19 +1725,25 @@ static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self) } -static ALint64 ALCpulseCapture_getLatency(ALCpulseCapture *self) +static ClockLatency ALCpulseCapture_getClockLatency(ALCpulseCapture *self) { pa_usec_t latency = 0; - int neg; + ClockLatency ret; + int neg, err; - if(pa_stream_get_latency(self->stream, &latency, &neg) != 0) + 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("Failed to get stream latency!\n"); - return 0; + ERR("Failed to get stream latency: 0x%x\n", err); + latency = 0; + neg = 0; } - if(neg) latency = 0; - return (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000; + ret.Latency = minu64(latency, U64(0xffffffffffffffff)/1000) * 1000; + pa_threaded_mainloop_unlock(self->loop); + + return ret; } @@ -1732,14 +1847,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/libs/openal-soft/Alc/backends/qsa.c b/libs/openal-soft/Alc/backends/qsa.c index 291e49fc..58193c96 100644 --- a/libs/openal-soft/Alc/backends/qsa.c +++ b/libs/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; @@ -117,8 +119,7 @@ static void deviceList(int type, vector_DevMap *devmap) if(max_cards < 0) return; - VECTOR_RESERVE(*devmap, max_cards+1); - VECTOR_RESIZE(*devmap, 0); + VECTOR_RESIZE(*devmap, 0, max_cards+1); entry.name = strdup(qsaDevice); entry.card = 0; @@ -158,17 +159,40 @@ 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 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); + + +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); @@ -178,59 +202,55 @@ 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(!data->killNow) { - 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); + 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 && !data->killNow) + { + 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); break; @@ -239,11 +259,12 @@ FORCE_ALIGN static int qsa_proc_playback(void* ptr) } else { - write_ptr+=wrote; - len-=wrote; + write_ptr += wrote; + len -= wrote; } } } + V0(device->Backend,unlock)(); return 0; } @@ -252,8 +273,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; @@ -277,7 +299,7 @@ static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName) #define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0) VECTOR_FIND_IF(iter, const DevMap, DeviceNameMap, MATCH_DEVNAME); #undef MATCH_DEVNAME - if(iter == VECTOR_ITER_END(DeviceNameMap)) + if(iter == VECTOR_END(DeviceNameMap)) { free(data); return ALC_INVALID_DEVICE; @@ -300,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) { @@ -319,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) @@ -365,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) @@ -556,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; @@ -569,20 +592,20 @@ 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) + 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) @@ -592,12 +615,70 @@ static void qsa_stop_playback(ALCdevice* device) 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 ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name) +{ + return qsa_open_playback(self, name); +} + +static void PlaybackWrapper_close(PlaybackWrapper *self) +{ + qsa_close_playback(self); +} + +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 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 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; @@ -624,7 +705,7 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName) #define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0) VECTOR_FIND_IF(iter, const DevMap, CaptureNameMap, MATCH_DEVNAME); #undef MATCH_DEVNAME - if(iter == VECTOR_ITER_END(CaptureNameMap)) + if(iter == VECTOR_END(CaptureNameMap)) { free(data); return ALC_INVALID_DEVICE; @@ -647,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) { @@ -688,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; } @@ -709,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) @@ -742,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; @@ -780,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; @@ -855,27 +936,65 @@ 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 ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name) +{ + return qsa_open_capture(self, name); +} + +static void CaptureWrapper_close(CaptureWrapper *self) +{ + qsa_close_capture(self); +} + +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); @@ -886,15 +1005,22 @@ 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) { case ALL_DEVICE_PROBE: #define FREE_NAME(iter) free((iter)->name) VECTOR_FOR_EACH(DevMap, DeviceNameMap, FREE_NAME); + VECTOR_RESIZE(DeviceNameMap, 0, 0); #undef FREE_NAME - VECTOR_RESIZE(DeviceNameMap, 0); deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap); #define APPEND_DEVICE(iter) AppendAllDevicesList((iter)->name) @@ -905,8 +1031,8 @@ void alc_qsa_probe(enum DevProbe type) case CAPTURE_DEVICE_PROBE: #define FREE_NAME(iter) free((iter)->name) VECTOR_FOR_EACH(DevMap, CaptureNameMap, FREE_NAME); + VECTOR_RESIZE(CaptureNameMap, 0, 0); #undef FREE_NAME - VECTOR_RESIZE(CaptureNameMap, 0); deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap); #define APPEND_DEVICE(iter) AppendCaptureDeviceList((iter)->name) @@ -915,3 +1041,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/libs/openal-soft/Alc/backends/sndio.c b/libs/openal-soft/Alc/backends/sndio.c index 52bff13a..47e05353 100644 --- a/libs/openal-soft/Alc/backends/sndio.c +++ b/libs/openal-soft/Alc/backends/sndio.c @@ -28,19 +28,16 @@ #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; @@ -48,30 +45,72 @@ typedef struct { volatile 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 void ALCsndioBackend_close(ALCsndioBackend *self); +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); +} + +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(!self->killNow && device->Connected) { - 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 && !self->killNow) { - wrote = sio_write(data->sndHandle, WritePtr, len); + wrote = sio_write(self->sndHandle, WritePtr, len); if(wrote == 0) { ERR("sio_write failed\n"); @@ -90,45 +129,36 @@ 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 void ALCsndioBackend_close(ALCsndioBackend *self) { - sndio_data *data = device->ExtraData; - - sio_close(data->sndHandle); - free(data); - device->ExtraData = NULL; + sio_close(self->sndHandle); + self->sndHandle = NULL; } -static ALCboolean sndio_reset_playback(ALCdevice *device) +static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self) { - sndio_data *data = device->ExtraData; + ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; struct sio_par par; sio_initpar(&par); @@ -170,7 +200,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 +241,86 @@ 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) + self->killNow = 0; + 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(self->killNow) return; - data->killNow = 1; - althrd_join(data->thread, &res); + self->killNow = 1; + althrd_join(self->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 +331,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/libs/openal-soft/Alc/backends/solaris.c b/libs/openal-soft/Alc/backends/solaris.c index 6eaf1ee5..5dfb5084 100644 --- a/libs/openal-soft/Alc/backends/solaris.c +++ b/libs/openal-soft/Alc/backends/solaris.c @@ -50,7 +50,7 @@ typedef struct ALCsolarisBackend { ALubyte *mix_data; int data_size; - volatile int killNow; + ATOMIC(ALenum) killNow; althrd_t thread; } ALCsolarisBackend; @@ -65,7 +65,7 @@ static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self); static void ALCsolarisBackend_stop(ALCsolarisBackend *self); static DECLARE_FORWARD2(ALCsolarisBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint) static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ALCuint, availableSamples) -static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ClockLatency, getClockLatency) static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, lock) static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, unlock) DECLARE_DEFAULT_ALLOCATORS(ALCsolarisBackend) @@ -84,6 +84,7 @@ static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *devi SET_VTABLE2(ALCsolarisBackend, ALCbackend, self); self->fd = -1; + ATOMIC_INIT(&self->killNow, AL_FALSE); } static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self) @@ -103,43 +104,65 @@ 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_SEQ(&self->killNow) && device->Connected) { - 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); + 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); + break; } - len -= wrote; - WritePtr += wrote; + to_write -= wrote; + write_ptr += wrote; } } + ALCsolarisBackend_unlock(self); return 0; } @@ -162,7 +185,7 @@ 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; } @@ -177,8 +200,8 @@ 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 +209,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 +243,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 +265,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 +275,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 +285,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/libs/openal-soft/Alc/backends/wave.c b/libs/openal-soft/Alc/backends/wave.c index d54e21eb..bdf76edc 100644 --- a/libs/openal-soft/Alc/backends/wave.c +++ b/libs/openal-soft/Alc/backends/wave.c @@ -91,7 +91,7 @@ static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self); static void ALCwaveBackend_stop(ALCwaveBackend *self); static DECLARE_FORWARD2(ALCwaveBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint) static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ALCuint, availableSamples) -static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ClockLatency, getClockLatency) static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, lock) static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, unlock) DECLARE_DEFAULT_ALLOCATORS(ALCwaveBackend) @@ -127,7 +127,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) @@ -157,37 +157,41 @@ 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) { ALuint bytesize = BytesFromDevFmt(device->FmtType); - ALubyte *bytes = self->mBuffer; ALuint i; - if(bytesize == 1) + if(bytesize == 2) { - for(i = 0;i < self->mSize;i++) - fputc(bytes[i], self->mFile); - } - else if(bytesize == 2) - { - for(i = 0;i < self->mSize;i++) - fputc(bytes[i^1], self->mFile); + ALushort *samples = self->mBuffer; + ALuint len = self->mSize / 2; + for(i = 0;i < len;i++) + { + ALushort samp = samples[i]; + samples[i] = (samp>>8) | (samp<<8); + } } else if(bytesize == 4) { - for(i = 0;i < self->mSize;i++) - fputc(bytes[i^3], self->mFile); + ALuint *samples = self->mBuffer; + ALuint len = self->mSize / 4; + for(i = 0;i < len;i++) + { + ALuint samp = samples[i]; + samples[i] = (samp>>24) | ((samp>>8)&0x0000ff00) | + ((samp<<8)&0x00ff0000) | (samp<<24); + } } } - else - { - fs = fwrite(self->mBuffer, frameSize, device->UpdateSize, - self->mFile); - (void)fs; - } + + fs = fwrite(self->mBuffer, frameSize, device->UpdateSize, self->mFile); + (void)fs; if(ferror(self->mFile)) { ERR("Error writing to file\n"); @@ -224,7 +228,7 @@ 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; } @@ -247,7 +251,10 @@ static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self) clearerr(self->mFile); if(GetConfigValueBool(NULL, "wave", "bformat", 0)) - device->FmtChans = DevFmtBFormat3D; + { + device->FmtChans = DevFmtAmbi3D; + device->AmbiOrder = 1; + } switch(device->FmtType) { @@ -275,20 +282,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 DevFmtBFormat3D: + case DevFmtAmbi3D: + /* .amb output requires 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) @@ -310,11 +320,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)) @@ -333,7 +344,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) { diff --git a/libs/openal-soft/Alc/backends/winmm.c b/libs/openal-soft/Alc/backends/winmm.c index 3508ec49..b5b3cdb6 100644 --- a/libs/openal-soft/Alc/backends/winmm.c +++ b/libs/openal-soft/Alc/backends/winmm.c @@ -45,8 +45,8 @@ static vector_al_string CaptureDevices; static void clear_devlist(vector_al_string *list) { - VECTOR_FOR_EACH(al_string, *list, al_string_deinit); - VECTOR_RESIZE(*list, 0); + VECTOR_FOR_EACH(al_string, *list, alstr_reset); + VECTOR_RESIZE(*list, 0, 0); } @@ -58,7 +58,7 @@ static void ProbePlaybackDevices(void) clear_devlist(&PlaybackDevices); numdevs = waveOutGetNumDevs(); - VECTOR_RESERVE(PlaybackDevices, numdevs); + VECTOR_RESIZE(PlaybackDevices, 0, numdevs); for(i = 0;i < numdevs;i++) { WAVEOUTCAPSW WaveCaps; @@ -71,23 +71,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_ITER_END(PlaybackDevices)) break; + 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); } @@ -101,7 +101,7 @@ static void ProbeCaptureDevices(void) clear_devlist(&CaptureDevices); numdevs = waveInGetNumDevs(); - VECTOR_RESERVE(CaptureDevices, numdevs); + VECTOR_RESIZE(CaptureDevices, 0, numdevs); for(i = 0;i < numdevs;i++) { WAVEINCAPSW WaveCaps; @@ -114,23 +114,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_ITER_END(CaptureDevices)) break; + 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); } @@ -164,7 +164,7 @@ static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self); static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self); static DECLARE_FORWARD2(ALCwinmmPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint) static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ALCuint, availableSamples) -static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ClockLatency, getClockLatency) static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, lock) static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, unlock) DECLARE_DEFAULT_ALLOCATORS(ALCwinmmPlayback) @@ -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,14 +257,14 @@ 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_ITER_END(PlaybackDevices)) + if(iter == VECTOR_END(PlaybackDevices)) return ALC_INVALID_VALUE; #undef MATCH_DEVNAME - DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(PlaybackDevices)); + DeviceID = (UINT)(iter - VECTOR_BEGIN(PlaybackDevices)); retry_open: memset(&self->Format, 0, sizeof(WAVEFORMATEX)); @@ -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: @@ -380,7 +382,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++) @@ -430,7 +432,7 @@ typedef struct ALCwinmmCapture { HWAVEIN InHdl; - RingBuffer *Ring; + ll_ringbuffer_t *Ring; WAVEFORMATEX Format; @@ -451,7 +453,7 @@ static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self); static void ALCwinmmCapture_stop(ALCwinmmCapture *self); static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples); static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self); -static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ALint64, getLatency) +static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ClockLatency, getClockLatency) static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, lock) static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, unlock) DECLARE_DEFAULT_ALLOCATORS(ALCwinmmCapture) @@ -514,8 +516,9 @@ static int ALCwinmmCapture_captureProc(void *arg) break; WaveHdr = ((WAVEHDR*)msg.lParam); - WriteRingBuffer(self->Ring, (ALubyte*)WaveHdr->lpData, - WaveHdr->dwBytesRecorded/self->Format.nBlockAlign); + ll_ringbuffer_write(self->Ring, WaveHdr->lpData, + WaveHdr->dwBytesRecorded / self->Format.nBlockAlign + ); // Send buffer back to capture more data waveInAddBuffer(self->InHdl, WaveHdr, sizeof(WAVEHDR)); @@ -541,13 +544,13 @@ 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_ITER_END(CaptureDevices)) + if(iter == VECTOR_END(CaptureDevices)) return ALC_INVALID_VALUE; #undef MATCH_DEVNAME - DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(CaptureDevices)); + DeviceID = (UINT)(iter - VECTOR_BEGIN(CaptureDevices)); switch(device->FmtChans) { @@ -560,7 +563,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name) case DevFmtX51Rear: case DevFmtX61: case DevFmtX71: - case DevFmtBFormat3D: + case DevFmtAmbi3D: return ALC_INVALID_ENUM; } @@ -581,7 +584,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; @@ -603,7 +606,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name) if(CapturedDataSize < (self->Format.nSamplesPerSec / 10)) CapturedDataSize = self->Format.nSamplesPerSec / 10; - self->Ring = CreateRingBuffer(self->Format.nBlockAlign, CapturedDataSize); + self->Ring = ll_ringbuffer_create(CapturedDataSize+1, self->Format.nBlockAlign); if(!self->Ring) goto failure; InitRef(&self->WaveBuffersCommitted, 0); @@ -633,7 +636,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name) 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: @@ -644,8 +647,7 @@ failure: free(BufferData); } - if(self->Ring) - DestroyRingBuffer(self->Ring); + ll_ringbuffer_free(self->Ring); self->Ring = NULL; if(self->InHdl) @@ -678,7 +680,7 @@ static void ALCwinmmCapture_close(ALCwinmmCapture *self) } free(buffer); - DestroyRingBuffer(self->Ring); + ll_ringbuffer_free(self->Ring); self->Ring = NULL; // Close the Wave device @@ -699,25 +701,25 @@ static void ALCwinmmCapture_stop(ALCwinmmCapture *self) static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples) { - ReadRingBuffer(self->Ring, buffer, samples); + ll_ringbuffer_read(self->Ring, buffer, samples); return ALC_NO_ERROR; } static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self) { - return RingBufferSize(self->Ring); + return 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/libs/openal-soft/Alc/bformatdec.c b/libs/openal-soft/Alc/bformatdec.c new file mode 100644 index 00000000..ba6daac5 --- /dev/null +++ b/libs/openal-soft/Alc/bformatdec.c @@ -0,0 +1,612 @@ + +#include "config.h" + +#include "bformatdec.h" +#include "ambdec.h" +#include "mixer_defs.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, ALsizei count) +{ + ALfloat coeff, d, x; + ALfloat z1, z2; + ALsizei 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; +} + + +void splitterap_init(SplitterAllpass *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->z1 = 0.0f; +} + +void splitterap_clear(SplitterAllpass *splitter) +{ + splitter->z1 = 0.0f; +} + +void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count) +{ + ALfloat coeff, d, x; + ALfloat z1; + ALsizei i; + + coeff = splitter->coeff; + z1 = splitter->z1; + for(i = 0;i < count;i++) + { + x = samples[i]; + + d = x - coeff*z1; + x = z1 + coeff*d; + z1 = d; + + samples[i] = x; + } + splitter->z1 = z1; +} + + +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) */ +}; + + +enum FreqBand { + FB_HighFreq, + FB_LowFreq, + FB_Max +}; + +/* These points are in AL coordinates! */ +static const ALfloat Ambi3DPoints[8][3] = { + { -0.577350269f, 0.577350269f, -0.577350269f }, + { 0.577350269f, 0.577350269f, -0.577350269f }, + { -0.577350269f, 0.577350269f, 0.577350269f }, + { 0.577350269f, 0.577350269f, 0.577350269f }, + { -0.577350269f, -0.577350269f, -0.577350269f }, + { 0.577350269f, -0.577350269f, -0.577350269f }, + { -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 RowMixerFunc MixMatrixRow = MixRow_C; + + +static alonce_flag bformatdec_inited = AL_ONCE_FLAG_INIT; + +static void init_bformatdec(void) +{ + MixMatrixRow = SelectRowMixer(); +} + + +/* NOTE: BandSplitter filters are unused with single-band decoding */ +typedef struct BFormatDec { + ALboolean Enabled[MAX_OUTPUT_CHANNELS]; + + union { + alignas(16) ALfloat Dual[MAX_OUTPUT_CHANNELS][FB_Max][MAX_AMBI_COEFFS]; + alignas(16) ALfloat Single[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS]; + } Matrix; + + BandSplitter XOver[MAX_AMBI_COEFFS]; + + ALfloat (*Samples)[BUFFERSIZE]; + /* These two alias into Samples */ + ALfloat (*SamplesHF)[BUFFERSIZE]; + ALfloat (*SamplesLF)[BUFFERSIZE]; + + alignas(16) ALfloat ChannelMix[BUFFERSIZE]; + + struct { + BandSplitter XOver; + ALfloat Gains[FB_Max]; + } UpSampler[4]; + + ALsizei NumChannels; + ALboolean DualBand; +} BFormatDec; + +BFormatDec *bformatdec_alloc() +{ + alcall_once(&bformatdec_inited, init_bformatdec); + return al_calloc(16, sizeof(BFormatDec)); +} + +void bformatdec_free(BFormatDec *dec) +{ + if(dec) + { + al_free(dec->Samples); + dec->Samples = NULL; + dec->SamplesHF = NULL; + dec->SamplesLF = NULL; + + memset(dec, 0, sizeof(*dec)); + al_free(dec); + } +} + +void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount, ALuint srate, const ALsizei chanmap[MAX_OUTPUT_CHANNELS]) +{ + static const ALsizei map2DTo3D[MAX_AMBI2D_COEFFS] = { + 0, 1, 3, 4, 8, 9, 15 + }; + const ALfloat *coeff_scale = UnitScale; + bool periphonic; + ALfloat ratio; + ALsizei i; + + al_free(dec->Samples); + dec->Samples = NULL; + dec->SamplesHF = NULL; + dec->SamplesLF = NULL; + + dec->NumChannels = chancount; + dec->Samples = al_calloc(16, dec->NumChannels*2 * sizeof(dec->Samples[0])); + dec->SamplesHF = dec->Samples; + dec->SamplesLF = dec->SamplesHF + dec->NumChannels; + + for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) + dec->Enabled[i] = AL_FALSE; + for(i = 0;i < conf->NumSpeakers;i++) + dec->Enabled[chanmap[i]] = AL_TRUE; + + 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[i].XOver, ratio); + if((conf->ChanMask&AMBI_PERIPHONIC_MASK)) + { + periphonic = true; + + dec->UpSampler[0].Gains[FB_HighFreq] = (dec->NumChannels > 9) ? W_SCALE3D_THIRD : + (dec->NumChannels > 4) ? W_SCALE3D_SECOND : 1.0f; + dec->UpSampler[0].Gains[FB_LowFreq] = 1.0f; + for(i = 1;i < 4;i++) + { + dec->UpSampler[i].Gains[FB_HighFreq] = (dec->NumChannels > 9) ? XYZ_SCALE3D_THIRD : + (dec->NumChannels > 4) ? XYZ_SCALE3D_SECOND : 1.0f; + dec->UpSampler[i].Gains[FB_LowFreq] = 1.0f; + } + } + else + { + periphonic = false; + + dec->UpSampler[0].Gains[FB_HighFreq] = (dec->NumChannels > 5) ? W_SCALE2D_THIRD : + (dec->NumChannels > 3) ? W_SCALE2D_SECOND : 1.0f; + dec->UpSampler[0].Gains[FB_LowFreq] = 1.0f; + for(i = 1;i < 3;i++) + { + dec->UpSampler[i].Gains[FB_HighFreq] = (dec->NumChannels > 5) ? XYZ_SCALE2D_THIRD : + (dec->NumChannels > 3) ? XYZ_SCALE2D_SECOND : 1.0f; + dec->UpSampler[i].Gains[FB_LowFreq] = 1.0f; + } + dec->UpSampler[3].Gains[FB_HighFreq] = 0.0f; + dec->UpSampler[3].Gains[FB_LowFreq] = 0.0f; + } + + memset(&dec->Matrix, 0, sizeof(dec->Matrix)); + if(conf->FreqBands == 1) + { + dec->DualBand = AL_FALSE; + for(i = 0;i < conf->NumSpeakers;i++) + { + ALsizei chan = chanmap[i]; + ALfloat gain; + ALsizei j, k; + + if(!periphonic) + { + for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;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; + } + } + else + { + for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++) + { + if(j == 0) gain = conf->HFOrderGain[0]; + else if(j == 1) gain = conf->HFOrderGain[1]; + else if(j == 4) gain = conf->HFOrderGain[2]; + else if(j == 9) gain = conf->HFOrderGain[3]; + if((conf->ChanMask&(1<Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[j] * + gain; + } + } + } + } + else + { + dec->DualBand = AL_TRUE; + + ratio = conf->XOverFreq / (ALfloat)srate; + for(i = 0;i < MAX_AMBI_COEFFS;i++) + bandsplit_init(&dec->XOver[i], ratio); + + ratio = powf(10.0f, conf->XOverRatio / 40.0f); + for(i = 0;i < conf->NumSpeakers;i++) + { + ALsizei chan = chanmap[i]; + ALfloat gain; + ALsizei j, k; + + if(!periphonic) + { + for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;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; + } + for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;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; + } + } + else + { + for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++) + { + if(j == 0) gain = conf->HFOrderGain[0] * ratio; + else if(j == 1) gain = conf->HFOrderGain[1] * ratio; + 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; + } + for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++) + { + if(j == 0) gain = conf->LFOrderGain[0] / ratio; + else if(j == 1) gain = conf->LFOrderGain[1] / ratio; + 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; + } + } + } + } +} + + +void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo) +{ + ALsizei chan, i; + + OutBuffer = ASSUME_ALIGNED(OutBuffer, 16); + if(dec->DualBand) + { + for(i = 0;i < dec->NumChannels;i++) + bandsplit_process(&dec->XOver[i], dec->SamplesHF[i], dec->SamplesLF[i], + InSamples[i], SamplesToDo); + + for(chan = 0;chan < OutChannels;chan++) + { + if(!dec->Enabled[chan]) + continue; + + memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat)); + MixMatrixRow(dec->ChannelMix, dec->Matrix.Dual[chan][FB_HighFreq], + SAFE_CONST(ALfloatBUFFERSIZE*,dec->SamplesHF), dec->NumChannels, 0, + SamplesToDo + ); + MixMatrixRow(dec->ChannelMix, dec->Matrix.Dual[chan][FB_LowFreq], + SAFE_CONST(ALfloatBUFFERSIZE*,dec->SamplesLF), dec->NumChannels, 0, + SamplesToDo + ); + + for(i = 0;i < SamplesToDo;i++) + OutBuffer[chan][i] += dec->ChannelMix[i]; + } + } + else + { + for(chan = 0;chan < OutChannels;chan++) + { + if(!dec->Enabled[chan]) + continue; + + memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat)); + MixMatrixRow(dec->ChannelMix, dec->Matrix.Single[chan], InSamples, + dec->NumChannels, 0, SamplesToDo); + + 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], ALsizei InChannels, ALsizei SamplesToDo) +{ + ALsizei i; + + /* 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[i].XOver, + dec->Samples[FB_HighFreq], dec->Samples[FB_LowFreq], + InSamples[i], SamplesToDo + ); + + /* Now write each band to the output. */ + MixMatrixRow(OutBuffer[i], dec->UpSampler[i].Gains, + SAFE_CONST(ALfloatBUFFERSIZE*,dec->Samples), FB_Max, 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]; + + BandSplitter XOver[4]; + + ALfloat Gains[4][MAX_OUTPUT_CHANNELS][FB_Max]; +} AmbiUpsampler; + +AmbiUpsampler *ambiup_alloc() +{ + alcall_once(&bformatdec_inited, init_bformatdec); + return al_calloc(16, sizeof(AmbiUpsampler)); +} + +void ambiup_free(struct AmbiUpsampler *ambiup) +{ + al_free(ambiup); +} + +void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device) +{ + ALfloat ratio; + size_t i; + + ratio = 400.0f / (ALfloat)device->Frequency; + for(i = 0;i < 4;i++) + bandsplit_init(&ambiup->XOver[i], ratio); + + memset(ambiup->Gains, 0, sizeof(ambiup->Gains)); + if(device->Dry.CoeffCount > 0) + { + ALfloat encgains[8][MAX_OUTPUT_CHANNELS]; + ALsizei j; + size_t k; + + for(i = 0;i < COUNTOF(Ambi3DPoints);i++) + { + ALfloat coeffs[MAX_AMBI_COEFFS] = { 0.0f }; + CalcDirectionCoeffs(Ambi3DPoints[i], 0.0f, coeffs); + ComputePanningGains(device->Dry, coeffs, 1.0f, encgains[i]); + } + + /* 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++) + { + ALfloat hfgain=0.0f, lfgain=0.0f; + for(k = 0;k < COUNTOF(Ambi3DDecoder);k++) + { + hfgain += Ambi3DDecoder[k][FB_HighFreq][i]*encgains[k][j]; + lfgain += Ambi3DDecoder[k][FB_LowFreq][i]*encgains[k][j]; + } + ambiup->Gains[i][j][FB_HighFreq] = hfgain; + ambiup->Gains[i][j][FB_LowFreq] = lfgain; + } + } + } + else + { + /* Assumes full 3D/periphonic on the input and output mixes! */ + ALfloat w_scale = (device->Dry.NumChannels > 9) ? W_SCALE3D_THIRD : + (device->Dry.NumChannels > 4) ? W_SCALE3D_SECOND : 1.0f; + ALfloat xyz_scale = (device->Dry.NumChannels > 9) ? XYZ_SCALE3D_THIRD : + (device->Dry.NumChannels > 4) ? XYZ_SCALE3D_SECOND : 1.0f; + 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][FB_HighFreq] = scale * ((i==0) ? w_scale : xyz_scale); + ambiup->Gains[i][index][FB_LowFreq] = scale; + } + } + } +} + +void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo) +{ + ALsizei i, j; + + for(i = 0;i < 4;i++) + { + bandsplit_process(&ambiup->XOver[i], + ambiup->Samples[FB_HighFreq], ambiup->Samples[FB_LowFreq], + 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 + ); + } +} diff --git a/libs/openal-soft/Alc/bformatdec.h b/libs/openal-soft/Alc/bformatdec.h new file mode 100644 index 00000000..8f44fc2a --- /dev/null +++ b/libs/openal-soft/Alc/bformatdec.h @@ -0,0 +1,75 @@ +#ifndef BFORMATDEC_H +#define BFORMATDEC_H + +#include "alMain.h" + + +/* These are the necessary scales for first-order HF responses to play over + * higher-order 2D (non-periphonic) decoders. + */ +#define W_SCALE2D_SECOND 1.224744871f /* sqrt(1.5) */ +#define XYZ_SCALE2D_SECOND 1.0f +#define W_SCALE2D_THIRD 1.414213562f /* sqrt(2) */ +#define XYZ_SCALE2D_THIRD 1.082392196f + +/* These are the necessary scales for first-order HF responses to play over + * higher-order 3D (periphonic) decoders. + */ +#define W_SCALE3D_SECOND 1.341640787f /* sqrt(1.8) */ +#define XYZ_SCALE3D_SECOND 1.0f +#define W_SCALE3D_THIRD 1.695486018f +#define XYZ_SCALE3D_THIRD 1.136697713f + + +struct AmbDecConf; +struct BFormatDec; +struct AmbiUpsampler; + + +struct BFormatDec *bformatdec_alloc(); +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], 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], ALsizei InChannels, ALsizei SamplesToDo); + + +/* Stand-alone first-order upsampler. Kept here because it shares some stuff + * with bformatdec. + */ +struct AmbiUpsampler *ambiup_alloc(); +void ambiup_free(struct AmbiUpsampler *ambiup); +void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device); + +void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei 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, 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 freq_mult); +void splitterap_clear(SplitterAllpass *splitter); +void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count); + +#endif /* BFORMATDEC_H */ diff --git a/libs/openal-soft/Alc/bs2b.c b/libs/openal-soft/Alc/bs2b.c index 6c3f052b..e235e547 100644 --- a/libs/openal-soft/Alc/bs2b.c +++ b/libs/openal-soft/Alc/bs2b.c @@ -129,4 +129,59 @@ void bs2b_clear(struct bs2b *bs2b) memset(&bs2b->last_sample, 0, sizeof(bs2b->last_sample)); } /* bs2b_clear */ -extern inline void bs2b_cross_feed(struct bs2b *bs2b, float *restrict samples); +void bs2b_cross_feed(struct bs2b *bs2b, float *restrict Left, float *restrict Right, int SamplesToDo) +{ + float lsamples[128][2]; + float rsamples[128][2]; + int base; + + for(base = 0;base < SamplesToDo;) + { + int todo = mini(128, SamplesToDo-base); + int i; + + /* Process left input */ + lsamples[0][0] = bs2b->a0_lo*Left[0] + + bs2b->b1_lo*bs2b->last_sample[0].lo; + lsamples[0][1] = bs2b->a0_hi*Left[0] + + bs2b->a1_hi*bs2b->last_sample[0].asis + + bs2b->b1_hi*bs2b->last_sample[0].hi; + for(i = 1;i < todo;i++) + { + lsamples[i][0] = bs2b->a0_lo*Left[i] + + bs2b->b1_lo*lsamples[i-1][0]; + lsamples[i][1] = bs2b->a0_hi*Left[i] + + bs2b->a1_hi*Left[i-1] + + bs2b->b1_hi*lsamples[i-1][1]; + } + bs2b->last_sample[0].asis = Left[i-1]; + bs2b->last_sample[0].lo = lsamples[i-1][0]; + bs2b->last_sample[0].hi = lsamples[i-1][1]; + + /* Process right input */ + rsamples[0][0] = bs2b->a0_lo*Right[0] + + bs2b->b1_lo*bs2b->last_sample[1].lo; + rsamples[0][1] = bs2b->a0_hi*Right[0] + + bs2b->a1_hi*bs2b->last_sample[1].asis + + bs2b->b1_hi*bs2b->last_sample[1].hi; + for(i = 1;i < todo;i++) + { + rsamples[i][0] = bs2b->a0_lo*Right[i] + + bs2b->b1_lo*rsamples[i-1][0]; + rsamples[i][1] = bs2b->a0_hi*Right[i] + + bs2b->a1_hi*Right[i-1] + + bs2b->b1_hi*rsamples[i-1][1]; + } + bs2b->last_sample[1].asis = Right[i-1]; + bs2b->last_sample[1].lo = rsamples[i-1][0]; + bs2b->last_sample[1].hi = rsamples[i-1][1]; + + /* Crossfeed */ + for(i = 0;i < todo;i++) + *(Left++) = lsamples[i][1] + rsamples[i][0]; + for(i = 0;i < todo;i++) + *(Right++) = rsamples[i][1] + lsamples[i][0]; + + base += todo; + } +} /* bs2b_cross_feed */ diff --git a/libs/openal-soft/Alc/bsinc.c b/libs/openal-soft/Alc/bsinc.c index f795120f..ad83c42b 100644 --- a/libs/openal-soft/Alc/bsinc.c +++ b/libs/openal-soft/Alc/bsinc.c @@ -979,3 +979,4103 @@ alignas(16) const ALfloat bsincTab[18840] = /* 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 }; + +alignas(16) const ALfloat sinc4Tab[4096][4] = +{ + { +1.972846707e-17f, +1.000000000e+00f, +1.972846707e-17f, -7.947592825e-19f }, + { -1.234844220e-04f, +9.999998638e-01f, +1.236324705e-04f, -2.493930525e-06f }, + { -2.468208365e-04f, +9.999994551e-01f, +2.474130305e-04f, -4.998192808e-06f }, + { -3.700092848e-04f, +9.999987739e-01f, +3.713417210e-04f, -7.512801591e-06f }, + { -4.930498082e-04f, +9.999978203e-01f, +4.954185828e-04f, -1.003777162e-05f }, + { -6.159424479e-04f, +9.999965942e-01f, +6.196436566e-04f, -1.257311763e-05f }, + { -7.386872455e-04f, +9.999950956e-01f, +7.440169831e-04f, -1.511885438e-05f }, + { -8.612842424e-04f, +9.999933246e-01f, +8.685386028e-04f, -1.767499661e-05f }, + { -9.837334803e-04f, +9.999912812e-01f, +9.932085563e-04f, -2.024155907e-05f }, + { -1.106035001e-03f, +9.999889652e-01f, +1.118026884e-03f, -2.281855651e-05f }, + { -1.228188846e-03f, +9.999863768e-01f, +1.242993626e-03f, -2.540600368e-05f }, + { -1.350195058e-03f, +9.999835160e-01f, +1.368108822e-03f, -2.800391533e-05f }, + { -1.472053677e-03f, +9.999803827e-01f, +1.493372514e-03f, -3.061230620e-05f }, + { -1.593764748e-03f, +9.999769770e-01f, +1.618784740e-03f, -3.323119106e-05f }, + { -1.715328311e-03f, +9.999732988e-01f, +1.744345541e-03f, -3.586058466e-05f }, + { -1.836744408e-03f, +9.999693481e-01f, +1.870054956e-03f, -3.850050175e-05f }, + { -1.958013083e-03f, +9.999651250e-01f, +1.995913026e-03f, -4.115095708e-05f }, + { -2.079134377e-03f, +9.999606295e-01f, +2.121919790e-03f, -4.381196542e-05f }, + { -2.200108333e-03f, +9.999558615e-01f, +2.248075287e-03f, -4.648354151e-05f }, + { -2.320934994e-03f, +9.999508211e-01f, +2.374379558e-03f, -4.916570012e-05f }, + { -2.441614402e-03f, +9.999455083e-01f, +2.500832641e-03f, -5.185845600e-05f }, + { -2.562146601e-03f, +9.999399230e-01f, +2.627434576e-03f, -5.456182391e-05f }, + { -2.682531632e-03f, +9.999340653e-01f, +2.754185402e-03f, -5.727581862e-05f }, + { -2.802769538e-03f, +9.999279352e-01f, +2.881085158e-03f, -6.000045487e-05f }, + { -2.922860364e-03f, +9.999215326e-01f, +3.008133883e-03f, -6.273574744e-05f }, + { -3.042804151e-03f, +9.999148577e-01f, +3.135331617e-03f, -6.548171107e-05f }, + { -3.162600944e-03f, +9.999079103e-01f, +3.262678397e-03f, -6.823836055e-05f }, + { -3.282250785e-03f, +9.999006906e-01f, +3.390174264e-03f, -7.100571061e-05f }, + { -3.401753717e-03f, +9.998931984e-01f, +3.517819255e-03f, -7.378377604e-05f }, + { -3.521109785e-03f, +9.998854338e-01f, +3.645613409e-03f, -7.657257158e-05f }, + { -3.640319031e-03f, +9.998773969e-01f, +3.773556765e-03f, -7.937211202e-05f }, + { -3.759381500e-03f, +9.998690875e-01f, +3.901649361e-03f, -8.218241209e-05f }, + { -3.878297235e-03f, +9.998605058e-01f, +4.029891236e-03f, -8.500348659e-05f }, + { -3.997066279e-03f, +9.998516517e-01f, +4.158282427e-03f, -8.783535025e-05f }, + { -4.115688677e-03f, +9.998425252e-01f, +4.286822973e-03f, -9.067801786e-05f }, + { -4.234164473e-03f, +9.998331264e-01f, +4.415512912e-03f, -9.353150418e-05f }, + { -4.352493710e-03f, +9.998234552e-01f, +4.544352281e-03f, -9.639582397e-05f }, + { -4.470676433e-03f, +9.998135116e-01f, +4.673341119e-03f, -9.927099199e-05f }, + { -4.588712686e-03f, +9.998032957e-01f, +4.802479464e-03f, -1.021570230e-04f }, + { -4.706602513e-03f, +9.997928075e-01f, +4.931767353e-03f, -1.050539318e-04f }, + { -4.824345959e-03f, +9.997820469e-01f, +5.061204824e-03f, -1.079617332e-04f }, + { -4.941943068e-03f, +9.997710141e-01f, +5.190791913e-03f, -1.108804418e-04f }, + { -5.059393885e-03f, +9.997597089e-01f, +5.320528660e-03f, -1.138100725e-04f }, + { -5.176698454e-03f, +9.997481313e-01f, +5.450415101e-03f, -1.167506400e-04f }, + { -5.293856820e-03f, +9.997362815e-01f, +5.580451273e-03f, -1.197021592e-04f }, + { -5.410869028e-03f, +9.997241594e-01f, +5.710637213e-03f, -1.226646447e-04f }, + { -5.527735122e-03f, +9.997117650e-01f, +5.840972959e-03f, -1.256381113e-04f }, + { -5.644455148e-03f, +9.996990983e-01f, +5.971458547e-03f, -1.286225739e-04f }, + { -5.761029151e-03f, +9.996861593e-01f, +6.102094014e-03f, -1.316180471e-04f }, + { -5.877457176e-03f, +9.996729481e-01f, +6.232879398e-03f, -1.346245458e-04f }, + { -5.993739268e-03f, +9.996594646e-01f, +6.363814734e-03f, -1.376420846e-04f }, + { -6.109875472e-03f, +9.996457089e-01f, +6.494900059e-03f, -1.406706784e-04f }, + { -6.225865834e-03f, +9.996316809e-01f, +6.626135411e-03f, -1.437103420e-04f }, + { -6.341710400e-03f, +9.996173807e-01f, +6.757520824e-03f, -1.467610900e-04f }, + { -6.457409214e-03f, +9.996028083e-01f, +6.889056337e-03f, -1.498229373e-04f }, + { -6.572962323e-03f, +9.995879637e-01f, +7.020741983e-03f, -1.528958987e-04f }, + { -6.688369772e-03f, +9.995728468e-01f, +7.152577801e-03f, -1.559799888e-04f }, + { -6.803631606e-03f, +9.995574578e-01f, +7.284563826e-03f, -1.590752225e-04f }, + { -6.918747873e-03f, +9.995417966e-01f, +7.416700094e-03f, -1.621816145e-04f }, + { -7.033718618e-03f, +9.995258632e-01f, +7.548986640e-03f, -1.652991797e-04f }, + { -7.148543887e-03f, +9.995096577e-01f, +7.681423501e-03f, -1.684279326e-04f }, + { -7.263223725e-03f, +9.994931800e-01f, +7.814010713e-03f, -1.715678882e-04f }, + { -7.377758181e-03f, +9.994764302e-01f, +7.946748310e-03f, -1.747190612e-04f }, + { -7.492147298e-03f, +9.994594082e-01f, +8.079636328e-03f, -1.778814663e-04f }, + { -7.606391125e-03f, +9.994421141e-01f, +8.212674803e-03f, -1.810551183e-04f }, + { -7.720489707e-03f, +9.994245479e-01f, +8.345863769e-03f, -1.842400320e-04f }, + { -7.834443092e-03f, +9.994067096e-01f, +8.479203263e-03f, -1.874362221e-04f }, + { -7.948251325e-03f, +9.993885992e-01f, +8.612693320e-03f, -1.906437034e-04f }, + { -8.061914453e-03f, +9.993702168e-01f, +8.746333973e-03f, -1.938624906e-04f }, + { -8.175432524e-03f, +9.993515622e-01f, +8.880125258e-03f, -1.970925985e-04f }, + { -8.288805583e-03f, +9.993326357e-01f, +9.014067211e-03f, -2.003340419e-04f }, + { -8.402033679e-03f, +9.993134371e-01f, +9.148159865e-03f, -2.035868356e-04f }, + { -8.515116858e-03f, +9.992939664e-01f, +9.282403256e-03f, -2.068509942e-04f }, + { -8.628055168e-03f, +9.992742238e-01f, +9.416797418e-03f, -2.101265325e-04f }, + { -8.740848654e-03f, +9.992542091e-01f, +9.551342385e-03f, -2.134134654e-04f }, + { -8.853497366e-03f, +9.992339225e-01f, +9.686038192e-03f, -2.167118075e-04f }, + { -8.966001349e-03f, +9.992133639e-01f, +9.820884872e-03f, -2.200215735e-04f }, + { -9.078360653e-03f, +9.991925333e-01f, +9.955882461e-03f, -2.233427784e-04f }, + { -9.190575323e-03f, +9.991714307e-01f, +1.009103099e-02f, -2.266754367e-04f }, + { -9.302645409e-03f, +9.991500563e-01f, +1.022633050e-02f, -2.300195634e-04f }, + { -9.414570956e-03f, +9.991284099e-01f, +1.036178102e-02f, -2.333751730e-04f }, + { -9.526352014e-03f, +9.991064916e-01f, +1.049738258e-02f, -2.367422804e-04f }, + { -9.637988631e-03f, +9.990843014e-01f, +1.063313522e-02f, -2.401209002e-04f }, + { -9.749480853e-03f, +9.990618393e-01f, +1.076903897e-02f, -2.435110474e-04f }, + { -9.860828729e-03f, +9.990391053e-01f, +1.090509386e-02f, -2.469127365e-04f }, + { -9.972032308e-03f, +9.990160995e-01f, +1.104129994e-02f, -2.503259824e-04f }, + { -1.008309164e-02f, +9.989928219e-01f, +1.117765722e-02f, -2.537507998e-04f }, + { -1.019400677e-02f, +9.989692724e-01f, +1.131416575e-02f, -2.571872034e-04f }, + { -1.030477774e-02f, +9.989454512e-01f, +1.145082556e-02f, -2.606352080e-04f }, + { -1.041540461e-02f, +9.989213581e-01f, +1.158763668e-02f, -2.640948284e-04f }, + { -1.052588743e-02f, +9.988969933e-01f, +1.172459914e-02f, -2.675660791e-04f }, + { -1.063622623e-02f, +9.988723567e-01f, +1.186171298e-02f, -2.710489751e-04f }, + { -1.074642108e-02f, +9.988474483e-01f, +1.199897823e-02f, -2.745435310e-04f }, + { -1.085647202e-02f, +9.988222682e-01f, +1.213639492e-02f, -2.780497616e-04f }, + { -1.096637910e-02f, +9.987968164e-01f, +1.227396308e-02f, -2.815676816e-04f }, + { -1.107614236e-02f, +9.987710929e-01f, +1.241168275e-02f, -2.850973058e-04f }, + { -1.118576186e-02f, +9.987450978e-01f, +1.254955397e-02f, -2.886386488e-04f }, + { -1.129523765e-02f, +9.987188309e-01f, +1.268757675e-02f, -2.921917254e-04f }, + { -1.140456977e-02f, +9.986922924e-01f, +1.282575114e-02f, -2.957565503e-04f }, + { -1.151375828e-02f, +9.986654823e-01f, +1.296407716e-02f, -2.993331383e-04f }, + { -1.162280322e-02f, +9.986384006e-01f, +1.310255485e-02f, -3.029215041e-04f }, + { -1.173170465e-02f, +9.986110473e-01f, +1.324118424e-02f, -3.065216624e-04f }, + { -1.184046260e-02f, +9.985834223e-01f, +1.337996537e-02f, -3.101336280e-04f }, + { -1.194907714e-02f, +9.985555259e-01f, +1.351889825e-02f, -3.137574155e-04f }, + { -1.205754831e-02f, +9.985273578e-01f, +1.365798293e-02f, -3.173930397e-04f }, + { -1.216587616e-02f, +9.984989183e-01f, +1.379721944e-02f, -3.210405152e-04f }, + { -1.227406075e-02f, +9.984702072e-01f, +1.393660781e-02f, -3.246998569e-04f }, + { -1.238210211e-02f, +9.984412247e-01f, +1.407614806e-02f, -3.283710794e-04f }, + { -1.249000030e-02f, +9.984119707e-01f, +1.421584024e-02f, -3.320541975e-04f }, + { -1.259775538e-02f, +9.983824452e-01f, +1.435568436e-02f, -3.357492258e-04f }, + { -1.270536739e-02f, +9.983526483e-01f, +1.449568047e-02f, -3.394561791e-04f }, + { -1.281283638e-02f, +9.983225800e-01f, +1.463582860e-02f, -3.431750720e-04f }, + { -1.292016240e-02f, +9.982922403e-01f, +1.477612876e-02f, -3.469059193e-04f }, + { -1.302734550e-02f, +9.982616292e-01f, +1.491658101e-02f, -3.506487357e-04f }, + { -1.313438573e-02f, +9.982307467e-01f, +1.505718535e-02f, -3.544035359e-04f }, + { -1.324128315e-02f, +9.981995929e-01f, +1.519794184e-02f, -3.581703346e-04f }, + { -1.334803780e-02f, +9.981681678e-01f, +1.533885049e-02f, -3.619491465e-04f }, + { -1.345464973e-02f, +9.981364714e-01f, +1.547991134e-02f, -3.657399862e-04f }, + { -1.356111900e-02f, +9.981045038e-01f, +1.562112441e-02f, -3.695428686e-04f }, + { -1.366744566e-02f, +9.980722648e-01f, +1.576248975e-02f, -3.733578082e-04f }, + { -1.377362975e-02f, +9.980397546e-01f, +1.590400737e-02f, -3.771848197e-04f }, + { -1.387967133e-02f, +9.980069732e-01f, +1.604567730e-02f, -3.810239179e-04f }, + { -1.398557045e-02f, +9.979739207e-01f, +1.618749959e-02f, -3.848751175e-04f }, + { -1.409132716e-02f, +9.979405969e-01f, +1.632947425e-02f, -3.887384331e-04f }, + { -1.419694151e-02f, +9.979070020e-01f, +1.647160132e-02f, -3.926138794e-04f }, + { -1.430241355e-02f, +9.978731359e-01f, +1.661388082e-02f, -3.965014710e-04f }, + { -1.440774333e-02f, +9.978389988e-01f, +1.675631280e-02f, -4.004012227e-04f }, + { -1.451293091e-02f, +9.978045905e-01f, +1.689889726e-02f, -4.043131492e-04f }, + { -1.461797634e-02f, +9.977699112e-01f, +1.704163425e-02f, -4.082372651e-04f }, + { -1.472287966e-02f, +9.977349608e-01f, +1.718452380e-02f, -4.121735850e-04f }, + { -1.482764093e-02f, +9.976997395e-01f, +1.732756592e-02f, -4.161221238e-04f }, + { -1.493226021e-02f, +9.976642471e-01f, +1.747076066e-02f, -4.200828959e-04f }, + { -1.503673754e-02f, +9.976284837e-01f, +1.761410804e-02f, -4.240559161e-04f }, + { -1.514107297e-02f, +9.975924494e-01f, +1.775760809e-02f, -4.280411991e-04f }, + { -1.524526657e-02f, +9.975561442e-01f, +1.790126083e-02f, -4.320387594e-04f }, + { -1.534931837e-02f, +9.975195680e-01f, +1.804506631e-02f, -4.360486119e-04f }, + { -1.545322843e-02f, +9.974827210e-01f, +1.818902453e-02f, -4.400707710e-04f }, + { -1.555699680e-02f, +9.974456031e-01f, +1.833313554e-02f, -4.441052515e-04f }, + { -1.566062355e-02f, +9.974082143e-01f, +1.847739937e-02f, -4.481520680e-04f }, + { -1.576410871e-02f, +9.973705548e-01f, +1.862181603e-02f, -4.522112352e-04f }, + { -1.586745234e-02f, +9.973326244e-01f, +1.876638556e-02f, -4.562827677e-04f }, + { -1.597065449e-02f, +9.972944233e-01f, +1.891110799e-02f, -4.603666801e-04f }, + { -1.607371522e-02f, +9.972559515e-01f, +1.905598334e-02f, -4.644629872e-04f }, + { -1.617663458e-02f, +9.972172089e-01f, +1.920101164e-02f, -4.685717034e-04f }, + { -1.627941262e-02f, +9.971781956e-01f, +1.934619292e-02f, -4.726928435e-04f }, + { -1.638204939e-02f, +9.971389117e-01f, +1.949152721e-02f, -4.768264221e-04f }, + { -1.648454495e-02f, +9.970993571e-01f, +1.963701453e-02f, -4.809724538e-04f }, + { -1.658689935e-02f, +9.970595319e-01f, +1.978265492e-02f, -4.851309533e-04f }, + { -1.668911265e-02f, +9.970194362e-01f, +1.992844839e-02f, -4.893019351e-04f }, + { -1.679118489e-02f, +9.969790698e-01f, +2.007439498e-02f, -4.934854139e-04f }, + { -1.689311613e-02f, +9.969384329e-01f, +2.022049471e-02f, -4.976814042e-04f }, + { -1.699490642e-02f, +9.968975255e-01f, +2.036674762e-02f, -5.018899208e-04f }, + { -1.709655581e-02f, +9.968563476e-01f, +2.051315372e-02f, -5.061109782e-04f }, + { -1.719806437e-02f, +9.968148993e-01f, +2.065971304e-02f, -5.103445911e-04f }, + { -1.729943214e-02f, +9.967731805e-01f, +2.080642562e-02f, -5.145907739e-04f }, + { -1.740065918e-02f, +9.967311913e-01f, +2.095329147e-02f, -5.188495414e-04f }, + { -1.750174553e-02f, +9.966889317e-01f, +2.110031063e-02f, -5.231209082e-04f }, + { -1.760269126e-02f, +9.966464017e-01f, +2.124748312e-02f, -5.274048887e-04f }, + { -1.770349642e-02f, +9.966036014e-01f, +2.139480896e-02f, -5.317014977e-04f }, + { -1.780416106e-02f, +9.965605309e-01f, +2.154228819e-02f, -5.360107497e-04f }, + { -1.790468523e-02f, +9.965171900e-01f, +2.168992083e-02f, -5.403326592e-04f }, + { -1.800506899e-02f, +9.964735789e-01f, +2.183770690e-02f, -5.446672410e-04f }, + { -1.810531240e-02f, +9.964296976e-01f, +2.198564643e-02f, -5.490145094e-04f }, + { -1.820541550e-02f, +9.963855461e-01f, +2.213373945e-02f, -5.533744792e-04f }, + { -1.830537836e-02f, +9.963411244e-01f, +2.228198598e-02f, -5.577471650e-04f }, + { -1.840520102e-02f, +9.962964326e-01f, +2.243038605e-02f, -5.621325811e-04f }, + { -1.850488354e-02f, +9.962514707e-01f, +2.257893968e-02f, -5.665307423e-04f }, + { -1.860442598e-02f, +9.962062387e-01f, +2.272764690e-02f, -5.709416631e-04f }, + { -1.870382839e-02f, +9.961607366e-01f, +2.287650774e-02f, -5.753653581e-04f }, + { -1.880309082e-02f, +9.961149645e-01f, +2.302552221e-02f, -5.798018418e-04f }, + { -1.890221333e-02f, +9.960689225e-01f, +2.317469035e-02f, -5.842511287e-04f }, + { -1.900119597e-02f, +9.960226105e-01f, +2.332401218e-02f, -5.887132334e-04f }, + { -1.910003880e-02f, +9.959760285e-01f, +2.347348772e-02f, -5.931881705e-04f }, + { -1.919874188e-02f, +9.959291766e-01f, +2.362311700e-02f, -5.976759545e-04f }, + { -1.929730525e-02f, +9.958820549e-01f, +2.377290004e-02f, -6.021765999e-04f }, + { -1.939572898e-02f, +9.958346633e-01f, +2.392283688e-02f, -6.066901213e-04f }, + { -1.949401312e-02f, +9.957870019e-01f, +2.407292752e-02f, -6.112165332e-04f }, + { -1.959215772e-02f, +9.957390708e-01f, +2.422317200e-02f, -6.157558500e-04f }, + { -1.969016283e-02f, +9.956908698e-01f, +2.437357035e-02f, -6.203080865e-04f }, + { -1.978802853e-02f, +9.956423992e-01f, +2.452412258e-02f, -6.248732570e-04f }, + { -1.988575485e-02f, +9.955936589e-01f, +2.467482872e-02f, -6.294513760e-04f }, + { -1.998334185e-02f, +9.955446489e-01f, +2.482568879e-02f, -6.340424582e-04f }, + { -2.008078960e-02f, +9.954953692e-01f, +2.497670282e-02f, -6.386465180e-04f }, + { -2.017809815e-02f, +9.954458200e-01f, +2.512787083e-02f, -6.432635698e-04f }, + { -2.027526754e-02f, +9.953960012e-01f, +2.527919285e-02f, -6.478936283e-04f }, + { -2.037229785e-02f, +9.953459129e-01f, +2.543066889e-02f, -6.525367078e-04f }, + { -2.046918912e-02f, +9.952955551e-01f, +2.558229899e-02f, -6.571928229e-04f }, + { -2.056594140e-02f, +9.952449278e-01f, +2.573408316e-02f, -6.618619881e-04f }, + { -2.066255477e-02f, +9.951940310e-01f, +2.588602143e-02f, -6.665442179e-04f }, + { -2.075902926e-02f, +9.951428649e-01f, +2.603811383e-02f, -6.712395266e-04f }, + { -2.085536494e-02f, +9.950914294e-01f, +2.619036036e-02f, -6.759479289e-04f }, + { -2.095156187e-02f, +9.950397245e-01f, +2.634276107e-02f, -6.806694392e-04f }, + { -2.104762010e-02f, +9.949877504e-01f, +2.649531596e-02f, -6.854040719e-04f }, + { -2.114353968e-02f, +9.949355069e-01f, +2.664802507e-02f, -6.901518415e-04f }, + { -2.123932068e-02f, +9.948829943e-01f, +2.680088842e-02f, -6.949127625e-04f }, + { -2.133496314e-02f, +9.948302124e-01f, +2.695390602e-02f, -6.996868493e-04f }, + { -2.143046713e-02f, +9.947771613e-01f, +2.710707790e-02f, -7.044741164e-04f }, + { -2.152583271e-02f, +9.947238411e-01f, +2.726040409e-02f, -7.092745782e-04f }, + { -2.162105992e-02f, +9.946702518e-01f, +2.741388460e-02f, -7.140882492e-04f }, + { -2.171614883e-02f, +9.946163934e-01f, +2.756751946e-02f, -7.189151437e-04f }, + { -2.181109950e-02f, +9.945622660e-01f, +2.772130869e-02f, -7.237552763e-04f }, + { -2.190591197e-02f, +9.945078696e-01f, +2.787525232e-02f, -7.286086614e-04f }, + { -2.200058631e-02f, +9.944532042e-01f, +2.802935035e-02f, -7.334753133e-04f }, + { -2.209512257e-02f, +9.943982699e-01f, +2.818360282e-02f, -7.383552465e-04f }, + { -2.218952082e-02f, +9.943430666e-01f, +2.833800975e-02f, -7.432484754e-04f }, + { -2.228378110e-02f, +9.942875945e-01f, +2.849257115e-02f, -7.481550144e-04f }, + { -2.237790348e-02f, +9.942318536e-01f, +2.864728706e-02f, -7.530748780e-04f }, + { -2.247188802e-02f, +9.941758439e-01f, +2.880215748e-02f, -7.580080805e-04f }, + { -2.256573476e-02f, +9.941195654e-01f, +2.895718245e-02f, -7.629546363e-04f }, + { -2.265944377e-02f, +9.940630182e-01f, +2.911236198e-02f, -7.679145598e-04f }, + { -2.275301511e-02f, +9.940062023e-01f, +2.926769610e-02f, -7.728878654e-04f }, + { -2.284644883e-02f, +9.939491178e-01f, +2.942318481e-02f, -7.778745675e-04f }, + { -2.293974499e-02f, +9.938917647e-01f, +2.957882816e-02f, -7.828746805e-04f }, + { -2.303290364e-02f, +9.938341429e-01f, +2.973462615e-02f, -7.878882187e-04f }, + { -2.312592486e-02f, +9.937762527e-01f, +2.989057881e-02f, -7.929151965e-04f }, + { -2.321880869e-02f, +9.937180939e-01f, +3.004668616e-02f, -7.979556282e-04f }, + { -2.331155519e-02f, +9.936596666e-01f, +3.020294821e-02f, -8.030095283e-04f }, + { -2.340416442e-02f, +9.936009710e-01f, +3.035936500e-02f, -8.080769110e-04f }, + { -2.349663644e-02f, +9.935420069e-01f, +3.051593653e-02f, -8.131577908e-04f }, + { -2.358897131e-02f, +9.934827745e-01f, +3.067266283e-02f, -8.182521819e-04f }, + { -2.368116908e-02f, +9.934232738e-01f, +3.082954391e-02f, -8.233600987e-04f }, + { -2.377322981e-02f, +9.933635047e-01f, +3.098657981e-02f, -8.284815556e-04f }, + { -2.386515357e-02f, +9.933034675e-01f, +3.114377053e-02f, -8.336165668e-04f }, + { -2.395694040e-02f, +9.932431620e-01f, +3.130111610e-02f, -8.387651466e-04f }, + { -2.404859038e-02f, +9.931825884e-01f, +3.145861654e-02f, -8.439273095e-04f }, + { -2.414010355e-02f, +9.931217466e-01f, +3.161627187e-02f, -8.491030697e-04f }, + { -2.423147998e-02f, +9.930606368e-01f, +3.177408210e-02f, -8.542924415e-04f }, + { -2.432271972e-02f, +9.929992589e-01f, +3.193204725e-02f, -8.594954392e-04f }, + { -2.441382283e-02f, +9.929376130e-01f, +3.209016735e-02f, -8.647120771e-04f }, + { -2.450478938e-02f, +9.928756991e-01f, +3.224844242e-02f, -8.699423695e-04f }, + { -2.459561942e-02f, +9.928135173e-01f, +3.240687246e-02f, -8.751863308e-04f }, + { -2.468631300e-02f, +9.927510676e-01f, +3.256545750e-02f, -8.804439750e-04f }, + { -2.477687020e-02f, +9.926883501e-01f, +3.272419757e-02f, -8.857153167e-04f }, + { -2.486729107e-02f, +9.926253647e-01f, +3.288309267e-02f, -8.910003699e-04f }, + { -2.495757566e-02f, +9.925621116e-01f, +3.304214283e-02f, -8.962991490e-04f }, + { -2.504772404e-02f, +9.924985907e-01f, +3.320134807e-02f, -9.016116683e-04f }, + { -2.513773626e-02f, +9.924348021e-01f, +3.336070839e-02f, -9.069379419e-04f }, + { -2.522761239e-02f, +9.923707459e-01f, +3.352022383e-02f, -9.122779842e-04f }, + { -2.531735249e-02f, +9.923064221e-01f, +3.367989440e-02f, -9.176318093e-04f }, + { -2.540695661e-02f, +9.922418307e-01f, +3.383972011e-02f, -9.229994316e-04f }, + { -2.549642481e-02f, +9.921769718e-01f, +3.399970099e-02f, -9.283808652e-04f }, + { -2.558575716e-02f, +9.921118453e-01f, +3.415983706e-02f, -9.337761243e-04f }, + { -2.567495371e-02f, +9.920464515e-01f, +3.432012832e-02f, -9.391852233e-04f }, + { -2.576401452e-02f, +9.919807902e-01f, +3.448057480e-02f, -9.446081762e-04f }, + { -2.585293966e-02f, +9.919148616e-01f, +3.464117652e-02f, -9.500449974e-04f }, + { -2.594172918e-02f, +9.918486656e-01f, +3.480193349e-02f, -9.554957009e-04f }, + { -2.603038315e-02f, +9.917822024e-01f, +3.496284573e-02f, -9.609603011e-04f }, + { -2.611890161e-02f, +9.917154719e-01f, +3.512391325e-02f, -9.664388120e-04f }, + { -2.620728464e-02f, +9.916484743e-01f, +3.528513608e-02f, -9.719312480e-04f }, + { -2.629553230e-02f, +9.915812094e-01f, +3.544651423e-02f, -9.774376231e-04f }, + { -2.638364463e-02f, +9.915136775e-01f, +3.560804772e-02f, -9.829579515e-04f }, + { -2.647162171e-02f, +9.914458785e-01f, +3.576973656e-02f, -9.884922474e-04f }, + { -2.655946360e-02f, +9.913778125e-01f, +3.593158078e-02f, -9.940405249e-04f }, + { -2.664717035e-02f, +9.913094795e-01f, +3.609358038e-02f, -9.996027983e-04f }, + { -2.673474203e-02f, +9.912408796e-01f, +3.625573538e-02f, -1.005179082e-03f }, + { -2.682217869e-02f, +9.911720128e-01f, +3.641804581e-02f, -1.010769389e-03f }, + { -2.690948040e-02f, +9.911028791e-01f, +3.658051167e-02f, -1.016373735e-03f }, + { -2.699664722e-02f, +9.910334786e-01f, +3.674313298e-02f, -1.021992133e-03f }, + { -2.708367921e-02f, +9.909638114e-01f, +3.690590976e-02f, -1.027624597e-03f }, + { -2.717057642e-02f, +9.908938774e-01f, +3.706884203e-02f, -1.033271142e-03f }, + { -2.725733893e-02f, +9.908236768e-01f, +3.723192979e-02f, -1.038931782e-03f }, + { -2.734396678e-02f, +9.907532095e-01f, +3.739517307e-02f, -1.044606531e-03f }, + { -2.743046005e-02f, +9.906824756e-01f, +3.755857188e-02f, -1.050295402e-03f }, + { -2.751681879e-02f, +9.906114753e-01f, +3.772212623e-02f, -1.055998411e-03f }, + { -2.760304307e-02f, +9.905402084e-01f, +3.788583615e-02f, -1.061715570e-03f }, + { -2.768913294e-02f, +9.904686751e-01f, +3.804970164e-02f, -1.067446895e-03f }, + { -2.777508847e-02f, +9.903968753e-01f, +3.821372273e-02f, -1.073192399e-03f }, + { -2.786090972e-02f, +9.903248092e-01f, +3.837789942e-02f, -1.078952096e-03f }, + { -2.794659675e-02f, +9.902524768e-01f, +3.854223173e-02f, -1.084726001e-03f }, + { -2.803214962e-02f, +9.901798782e-01f, +3.870671968e-02f, -1.090514127e-03f }, + { -2.811756839e-02f, +9.901070133e-01f, +3.887136328e-02f, -1.096316489e-03f }, + { -2.820285313e-02f, +9.900338822e-01f, +3.903616254e-02f, -1.102133100e-03f }, + { -2.828800389e-02f, +9.899604850e-01f, +3.920111748e-02f, -1.107963974e-03f }, + { -2.837302075e-02f, +9.898868218e-01f, +3.936622812e-02f, -1.113809126e-03f }, + { -2.845790375e-02f, +9.898128925e-01f, +3.953149447e-02f, -1.119668570e-03f }, + { -2.854265296e-02f, +9.897386972e-01f, +3.969691653e-02f, -1.125542319e-03f }, + { -2.862726845e-02f, +9.896642360e-01f, +3.986249434e-02f, -1.131430388e-03f }, + { -2.871175028e-02f, +9.895895088e-01f, +4.002822789e-02f, -1.137332791e-03f }, + { -2.879609850e-02f, +9.895145159e-01f, +4.019411721e-02f, -1.143249541e-03f }, + { -2.888031318e-02f, +9.894392571e-01f, +4.036016230e-02f, -1.149180652e-03f }, + { -2.896439439e-02f, +9.893637326e-01f, +4.052636319e-02f, -1.155126140e-03f }, + { -2.904834218e-02f, +9.892879424e-01f, +4.069271988e-02f, -1.161086016e-03f }, + { -2.913215661e-02f, +9.892118865e-01f, +4.085923238e-02f, -1.167060297e-03f }, + { -2.921583776e-02f, +9.891355651e-01f, +4.102590072e-02f, -1.173048994e-03f }, + { -2.929938568e-02f, +9.890589780e-01f, +4.119272490e-02f, -1.179052123e-03f }, + { -2.938280043e-02f, +9.889821255e-01f, +4.135970494e-02f, -1.185069698e-03f }, + { -2.946608208e-02f, +9.889050075e-01f, +4.152684085e-02f, -1.191101731e-03f }, + { -2.954923069e-02f, +9.888276241e-01f, +4.169413264e-02f, -1.197148238e-03f }, + { -2.963224632e-02f, +9.887499753e-01f, +4.186158033e-02f, -1.203209232e-03f }, + { -2.971512904e-02f, +9.886720613e-01f, +4.202918393e-02f, -1.209284727e-03f }, + { -2.979787891e-02f, +9.885938819e-01f, +4.219694344e-02f, -1.215374737e-03f }, + { -2.988049599e-02f, +9.885154374e-01f, +4.236485889e-02f, -1.221479275e-03f }, + { -2.996298034e-02f, +9.884367276e-01f, +4.253293028e-02f, -1.227598357e-03f }, + { -3.004533202e-02f, +9.883577528e-01f, +4.270115763e-02f, -1.233731995e-03f }, + { -3.012755111e-02f, +9.882785129e-01f, +4.286954095e-02f, -1.239880203e-03f }, + { -3.020963766e-02f, +9.881990080e-01f, +4.303808025e-02f, -1.246042996e-03f }, + { -3.029159174e-02f, +9.881192381e-01f, +4.320677555e-02f, -1.252220388e-03f }, + { -3.037341341e-02f, +9.880392034e-01f, +4.337562684e-02f, -1.258412391e-03f }, + { -3.045510273e-02f, +9.879589037e-01f, +4.354463416e-02f, -1.264619020e-03f }, + { -3.053665977e-02f, +9.878783393e-01f, +4.371379750e-02f, -1.270840289e-03f }, + { -3.061808459e-02f, +9.877975101e-01f, +4.388311688e-02f, -1.277076211e-03f }, + { -3.069937725e-02f, +9.877164161e-01f, +4.405259231e-02f, -1.283326801e-03f }, + { -3.078053782e-02f, +9.876350576e-01f, +4.422222380e-02f, -1.289592072e-03f }, + { -3.086156636e-02f, +9.875534344e-01f, +4.439201136e-02f, -1.295872038e-03f }, + { -3.094246293e-02f, +9.874715467e-01f, +4.456195501e-02f, -1.302166714e-03f }, + { -3.102322761e-02f, +9.873893944e-01f, +4.473205475e-02f, -1.308476111e-03f }, + { -3.110386044e-02f, +9.873069778e-01f, +4.490231060e-02f, -1.314800245e-03f }, + { -3.118436150e-02f, +9.872242967e-01f, +4.507272256e-02f, -1.321139130e-03f }, + { -3.126473085e-02f, +9.871413513e-01f, +4.524329065e-02f, -1.327492778e-03f }, + { -3.134496856e-02f, +9.870581415e-01f, +4.541401487e-02f, -1.333861204e-03f }, + { -3.142507468e-02f, +9.869746676e-01f, +4.558489524e-02f, -1.340244422e-03f }, + { -3.150504929e-02f, +9.868909295e-01f, +4.575593177e-02f, -1.346642444e-03f }, + { -3.158489244e-02f, +9.868069272e-01f, +4.592712446e-02f, -1.353055286e-03f }, + { -3.166460421e-02f, +9.867226608e-01f, +4.609847333e-02f, -1.359482960e-03f }, + { -3.174418465e-02f, +9.866381305e-01f, +4.626997838e-02f, -1.365925481e-03f }, + { -3.182363382e-02f, +9.865533361e-01f, +4.644163964e-02f, -1.372382862e-03f }, + { -3.190295181e-02f, +9.864682779e-01f, +4.661345709e-02f, -1.378855117e-03f }, + { -3.198213866e-02f, +9.863829557e-01f, +4.678543077e-02f, -1.385342260e-03f }, + { -3.206119444e-02f, +9.862973698e-01f, +4.695756066e-02f, -1.391844303e-03f }, + { -3.214011922e-02f, +9.862115201e-01f, +4.712984679e-02f, -1.398361262e-03f }, + { -3.221891307e-02f, +9.861254067e-01f, +4.730228917e-02f, -1.404893149e-03f }, + { -3.229757604e-02f, +9.860390297e-01f, +4.747488779e-02f, -1.411439979e-03f }, + { -3.237610820e-02f, +9.859523890e-01f, +4.764764268e-02f, -1.418001764e-03f }, + { -3.245450962e-02f, +9.858654848e-01f, +4.782055384e-02f, -1.424578519e-03f }, + { -3.253278036e-02f, +9.857783172e-01f, +4.799362127e-02f, -1.431170257e-03f }, + { -3.261092049e-02f, +9.856908861e-01f, +4.816684499e-02f, -1.437776992e-03f }, + { -3.268893007e-02f, +9.856031917e-01f, +4.834022501e-02f, -1.444398738e-03f }, + { -3.276680917e-02f, +9.855152339e-01f, +4.851376133e-02f, -1.451035507e-03f }, + { -3.284455785e-02f, +9.854270129e-01f, +4.868745396e-02f, -1.457687314e-03f }, + { -3.292217617e-02f, +9.853385286e-01f, +4.886130292e-02f, -1.464354173e-03f }, + { -3.299966421e-02f, +9.852497812e-01f, +4.903530820e-02f, -1.471036096e-03f }, + { -3.307702203e-02f, +9.851607708e-01f, +4.920946982e-02f, -1.477733098e-03f }, + { -3.315424969e-02f, +9.850714973e-01f, +4.938378778e-02f, -1.484445192e-03f }, + { -3.323134726e-02f, +9.849819608e-01f, +4.955826209e-02f, -1.491172391e-03f }, + { -3.330831480e-02f, +9.848921614e-01f, +4.973289276e-02f, -1.497914710e-03f }, + { -3.338515239e-02f, +9.848020991e-01f, +4.990767980e-02f, -1.504672161e-03f }, + { -3.346186007e-02f, +9.847117740e-01f, +5.008262322e-02f, -1.511444759e-03f }, + { -3.353843793e-02f, +9.846211862e-01f, +5.025772301e-02f, -1.518232516e-03f }, + { -3.361488603e-02f, +9.845303356e-01f, +5.043297919e-02f, -1.525035446e-03f }, + { -3.369120443e-02f, +9.844392225e-01f, +5.060839177e-02f, -1.531853564e-03f }, + { -3.376739320e-02f, +9.843478467e-01f, +5.078396076e-02f, -1.538686881e-03f }, + { -3.384345240e-02f, +9.842562085e-01f, +5.095968615e-02f, -1.545535412e-03f }, + { -3.391938210e-02f, +9.841643077e-01f, +5.113556795e-02f, -1.552399171e-03f }, + { -3.399518237e-02f, +9.840721446e-01f, +5.131160618e-02f, -1.559278170e-03f }, + { -3.407085327e-02f, +9.839797191e-01f, +5.148780084e-02f, -1.566172424e-03f }, + { -3.414639488e-02f, +9.838870314e-01f, +5.166415193e-02f, -1.573081945e-03f }, + { -3.422180724e-02f, +9.837940814e-01f, +5.184065947e-02f, -1.580006747e-03f }, + { -3.429709044e-02f, +9.837008692e-01f, +5.201732345e-02f, -1.586946844e-03f }, + { -3.437224453e-02f, +9.836073950e-01f, +5.219414389e-02f, -1.593902249e-03f }, + { -3.444726959e-02f, +9.835136587e-01f, +5.237112079e-02f, -1.600872975e-03f }, + { -3.452216568e-02f, +9.834196603e-01f, +5.254825415e-02f, -1.607859036e-03f }, + { -3.459693287e-02f, +9.833254001e-01f, +5.272554398e-02f, -1.614860445e-03f }, + { -3.467157122e-02f, +9.832308780e-01f, +5.290299029e-02f, -1.621877216e-03f }, + { -3.474608080e-02f, +9.831360941e-01f, +5.308059308e-02f, -1.628909362e-03f }, + { -3.482046167e-02f, +9.830410484e-01f, +5.325835236e-02f, -1.635956896e-03f }, + { -3.489471391e-02f, +9.829457410e-01f, +5.343626813e-02f, -1.643019832e-03f }, + { -3.496883758e-02f, +9.828501720e-01f, +5.361434040e-02f, -1.650098183e-03f }, + { -3.504283275e-02f, +9.827543414e-01f, +5.379256918e-02f, -1.657191962e-03f }, + { -3.511669948e-02f, +9.826582494e-01f, +5.397095446e-02f, -1.664301183e-03f }, + { -3.519043784e-02f, +9.825618958e-01f, +5.414949625e-02f, -1.671425859e-03f }, + { -3.526404790e-02f, +9.824652809e-01f, +5.432819456e-02f, -1.678566004e-03f }, + { -3.533752973e-02f, +9.823684047e-01f, +5.450704939e-02f, -1.685721630e-03f }, + { -3.541088338e-02f, +9.822712671e-01f, +5.468606075e-02f, -1.692892751e-03f }, + { -3.548410894e-02f, +9.821738684e-01f, +5.486522863e-02f, -1.700079381e-03f }, + { -3.555720646e-02f, +9.820762086e-01f, +5.504455306e-02f, -1.707281532e-03f }, + { -3.563017602e-02f, +9.819782876e-01f, +5.522403402e-02f, -1.714499218e-03f }, + { -3.570301768e-02f, +9.818801056e-01f, +5.540367152e-02f, -1.721732452e-03f }, + { -3.577573150e-02f, +9.817816627e-01f, +5.558346557e-02f, -1.728981247e-03f }, + { -3.584831757e-02f, +9.816829589e-01f, +5.576341617e-02f, -1.736245617e-03f }, + { -3.592077593e-02f, +9.815839943e-01f, +5.594352333e-02f, -1.743525575e-03f }, + { -3.599310667e-02f, +9.814847688e-01f, +5.612378704e-02f, -1.750821133e-03f }, + { -3.606530984e-02f, +9.813852827e-01f, +5.630420731e-02f, -1.758132306e-03f }, + { -3.613738552e-02f, +9.812855360e-01f, +5.648478415e-02f, -1.765459107e-03f }, + { -3.620933378e-02f, +9.811855286e-01f, +5.666551755e-02f, -1.772801548e-03f }, + { -3.628115468e-02f, +9.810852607e-01f, +5.684640752e-02f, -1.780159642e-03f }, + { -3.635284829e-02f, +9.809847324e-01f, +5.702745407e-02f, -1.787533404e-03f }, + { -3.642441467e-02f, +9.808839437e-01f, +5.720865720e-02f, -1.794922846e-03f }, + { -3.649585390e-02f, +9.807828947e-01f, +5.739001690e-02f, -1.802327981e-03f }, + { -3.656716604e-02f, +9.806815854e-01f, +5.757153318e-02f, -1.809748823e-03f }, + { -3.663835117e-02f, +9.805800159e-01f, +5.775320605e-02f, -1.817185384e-03f }, + { -3.670940934e-02f, +9.804781863e-01f, +5.793503550e-02f, -1.824637677e-03f }, + { -3.678034064e-02f, +9.803760966e-01f, +5.811702155e-02f, -1.832105717e-03f }, + { -3.685114512e-02f, +9.802737469e-01f, +5.829916418e-02f, -1.839589515e-03f }, + { -3.692182285e-02f, +9.801711372e-01f, +5.848146341e-02f, -1.847089085e-03f }, + { -3.699237391e-02f, +9.800682677e-01f, +5.866391923e-02f, -1.854604440e-03f }, + { -3.706279835e-02f, +9.799651384e-01f, +5.884653165e-02f, -1.862135594e-03f }, + { -3.713309626e-02f, +9.798617493e-01f, +5.902930066e-02f, -1.869682558e-03f }, + { -3.720326769e-02f, +9.797581006e-01f, +5.921222628e-02f, -1.877245347e-03f }, + { -3.727331272e-02f, +9.796541922e-01f, +5.939530849e-02f, -1.884823973e-03f }, + { -3.734323142e-02f, +9.795500243e-01f, +5.957854731e-02f, -1.892418449e-03f }, + { -3.741302385e-02f, +9.794455969e-01f, +5.976194274e-02f, -1.900028788e-03f }, + { -3.748269008e-02f, +9.793409101e-01f, +5.994549476e-02f, -1.907655004e-03f }, + { -3.755223018e-02f, +9.792359640e-01f, +6.012920340e-02f, -1.915297108e-03f }, + { -3.762164423e-02f, +9.791307585e-01f, +6.031306864e-02f, -1.922955116e-03f }, + { -3.769093228e-02f, +9.790252939e-01f, +6.049709048e-02f, -1.930629038e-03f }, + { -3.776009441e-02f, +9.789195701e-01f, +6.068126894e-02f, -1.938318888e-03f }, + { -3.782913069e-02f, +9.788135872e-01f, +6.086560401e-02f, -1.946024680e-03f }, + { -3.789804118e-02f, +9.787073453e-01f, +6.105009568e-02f, -1.953746425e-03f }, + { -3.796682595e-02f, +9.786008444e-01f, +6.123474396e-02f, -1.961484138e-03f }, + { -3.803548508e-02f, +9.784940847e-01f, +6.141954886e-02f, -1.969237830e-03f }, + { -3.810401864e-02f, +9.783870661e-01f, +6.160451036e-02f, -1.977007515e-03f }, + { -3.817242668e-02f, +9.782797888e-01f, +6.178962847e-02f, -1.984793206e-03f }, + { -3.824070929e-02f, +9.781722528e-01f, +6.197490319e-02f, -1.992594915e-03f }, + { -3.830886653e-02f, +9.780644583e-01f, +6.216033452e-02f, -2.000412656e-03f }, + { -3.837689847e-02f, +9.779564051e-01f, +6.234592246e-02f, -2.008246441e-03f }, + { -3.844480518e-02f, +9.778480935e-01f, +6.253166701e-02f, -2.016096283e-03f }, + { -3.851258673e-02f, +9.777395235e-01f, +6.271756817e-02f, -2.023962196e-03f }, + { -3.858024318e-02f, +9.776306952e-01f, +6.290362593e-02f, -2.031844191e-03f }, + { -3.864777462e-02f, +9.775216086e-01f, +6.308984030e-02f, -2.039742281e-03f }, + { -3.871518110e-02f, +9.774122638e-01f, +6.327621127e-02f, -2.047656480e-03f }, + { -3.878246270e-02f, +9.773026608e-01f, +6.346273885e-02f, -2.055586801e-03f }, + { -3.884961949e-02f, +9.771927998e-01f, +6.364942303e-02f, -2.063533255e-03f }, + { -3.891665153e-02f, +9.770826809e-01f, +6.383626381e-02f, -2.071495856e-03f }, + { -3.898355890e-02f, +9.769723040e-01f, +6.402326120e-02f, -2.079474617e-03f }, + { -3.905034167e-02f, +9.768616692e-01f, +6.421041518e-02f, -2.087469549e-03f }, + { -3.911699991e-02f, +9.767507766e-01f, +6.439772576e-02f, -2.095480667e-03f }, + { -3.918353368e-02f, +9.766396264e-01f, +6.458519293e-02f, -2.103507983e-03f }, + { -3.924994306e-02f, +9.765282185e-01f, +6.477281670e-02f, -2.111551509e-03f }, + { -3.931622812e-02f, +9.764165530e-01f, +6.496059705e-02f, -2.119611258e-03f }, + { -3.938238892e-02f, +9.763046301e-01f, +6.514853400e-02f, -2.127687243e-03f }, + { -3.944842554e-02f, +9.761924497e-01f, +6.533662753e-02f, -2.135779477e-03f }, + { -3.951433805e-02f, +9.760800119e-01f, +6.552487765e-02f, -2.143887971e-03f }, + { -3.958012651e-02f, +9.759673169e-01f, +6.571328435e-02f, -2.152012740e-03f }, + { -3.964579101e-02f, +9.758543646e-01f, +6.590184763e-02f, -2.160153795e-03f }, + { -3.971133160e-02f, +9.757411552e-01f, +6.609056749e-02f, -2.168311149e-03f }, + { -3.977674836e-02f, +9.756276888e-01f, +6.627944392e-02f, -2.176484815e-03f }, + { -3.984204137e-02f, +9.755139653e-01f, +6.646847692e-02f, -2.184674805e-03f }, + { -3.990721068e-02f, +9.753999849e-01f, +6.665766648e-02f, -2.192881133e-03f }, + { -3.997225637e-02f, +9.752857476e-01f, +6.684701262e-02f, -2.201103809e-03f }, + { -4.003717851e-02f, +9.751712536e-01f, +6.703651531e-02f, -2.209342848e-03f }, + { -4.010197718e-02f, +9.750565028e-01f, +6.722617456e-02f, -2.217598262e-03f }, + { -4.016665244e-02f, +9.749414954e-01f, +6.741599037e-02f, -2.225870062e-03f }, + { -4.023120436e-02f, +9.748262314e-01f, +6.760596272e-02f, -2.234158263e-03f }, + { -4.029563302e-02f, +9.747107110e-01f, +6.779609163e-02f, -2.242462875e-03f }, + { -4.035993848e-02f, +9.745949341e-01f, +6.798637707e-02f, -2.250783913e-03f }, + { -4.042412082e-02f, +9.744789009e-01f, +6.817681906e-02f, -2.259121387e-03f }, + { -4.048818011e-02f, +9.743626113e-01f, +6.836741758e-02f, -2.267475312e-03f }, + { -4.055211641e-02f, +9.742460656e-01f, +6.855817263e-02f, -2.275845698e-03f }, + { -4.061592981e-02f, +9.741292638e-01f, +6.874908421e-02f, -2.284232560e-03f }, + { -4.067962036e-02f, +9.740122059e-01f, +6.894015231e-02f, -2.292635908e-03f }, + { -4.074318815e-02f, +9.738948920e-01f, +6.913137692e-02f, -2.301055756e-03f }, + { -4.080663324e-02f, +9.737773222e-01f, +6.932275805e-02f, -2.309492116e-03f }, + { -4.086995570e-02f, +9.736594966e-01f, +6.951429569e-02f, -2.317945001e-03f }, + { -4.093315561e-02f, +9.735414153e-01f, +6.970598983e-02f, -2.326414422e-03f }, + { -4.099623304e-02f, +9.734230782e-01f, +6.989784046e-02f, -2.334900392e-03f }, + { -4.105918805e-02f, +9.733044855e-01f, +7.008984759e-02f, -2.343402924e-03f }, + { -4.112202073e-02f, +9.731856373e-01f, +7.028201121e-02f, -2.351922030e-03f }, + { -4.118473113e-02f, +9.730665337e-01f, +7.047433131e-02f, -2.360457723e-03f }, + { -4.124731934e-02f, +9.729471746e-01f, +7.066680788e-02f, -2.369010014e-03f }, + { -4.130978543e-02f, +9.728275603e-01f, +7.085944093e-02f, -2.377578915e-03f }, + { -4.137212946e-02f, +9.727076907e-01f, +7.105223044e-02f, -2.386164441e-03f }, + { -4.143435151e-02f, +9.725875660e-01f, +7.124517640e-02f, -2.394766601e-03f }, + { -4.149645164e-02f, +9.724671862e-01f, +7.143827882e-02f, -2.403385410e-03f }, + { -4.155842994e-02f, +9.723465513e-01f, +7.163153769e-02f, -2.412020878e-03f }, + { -4.162028648e-02f, +9.722256616e-01f, +7.182495300e-02f, -2.420673020e-03f }, + { -4.168202131e-02f, +9.721045170e-01f, +7.201852474e-02f, -2.429341845e-03f }, + { -4.174363453e-02f, +9.719831176e-01f, +7.221225291e-02f, -2.438027368e-03f }, + { -4.180512620e-02f, +9.718614635e-01f, +7.240613750e-02f, -2.446729599e-03f }, + { -4.186649638e-02f, +9.717395548e-01f, +7.260017851e-02f, -2.455448552e-03f }, + { -4.192774516e-02f, +9.716173916e-01f, +7.279437592e-02f, -2.464184239e-03f }, + { -4.198887261e-02f, +9.714949738e-01f, +7.298872973e-02f, -2.472936671e-03f }, + { -4.204987879e-02f, +9.713723017e-01f, +7.318323994e-02f, -2.481705861e-03f }, + { -4.211076378e-02f, +9.712493753e-01f, +7.337790653e-02f, -2.490491821e-03f }, + { -4.217152766e-02f, +9.711261947e-01f, +7.357272950e-02f, -2.499294563e-03f }, + { -4.223217049e-02f, +9.710027598e-01f, +7.376770885e-02f, -2.508114099e-03f }, + { -4.229269235e-02f, +9.708790709e-01f, +7.396284455e-02f, -2.516950442e-03f }, + { -4.235309331e-02f, +9.707551280e-01f, +7.415813662e-02f, -2.525803603e-03f }, + { -4.241337343e-02f, +9.706309312e-01f, +7.435358503e-02f, -2.534673595e-03f }, + { -4.247353281e-02f, +9.705064805e-01f, +7.454918978e-02f, -2.543560430e-03f }, + { -4.253357150e-02f, +9.703817761e-01f, +7.474495086e-02f, -2.552464119e-03f }, + { -4.259348958e-02f, +9.702568180e-01f, +7.494086827e-02f, -2.561384675e-03f }, + { -4.265328712e-02f, +9.701316062e-01f, +7.513694200e-02f, -2.570322110e-03f }, + { -4.271296420e-02f, +9.700061410e-01f, +7.533317203e-02f, -2.579276435e-03f }, + { -4.277252088e-02f, +9.698804222e-01f, +7.552955836e-02f, -2.588247664e-03f }, + { -4.283195724e-02f, +9.697544501e-01f, +7.572610098e-02f, -2.597235807e-03f }, + { -4.289127336e-02f, +9.696282248e-01f, +7.592279989e-02f, -2.606240877e-03f }, + { -4.295046931e-02f, +9.695017462e-01f, +7.611965506e-02f, -2.615262886e-03f }, + { -4.300954515e-02f, +9.693750144e-01f, +7.631666650e-02f, -2.624301846e-03f }, + { -4.306850096e-02f, +9.692480296e-01f, +7.651383420e-02f, -2.633357769e-03f }, + { -4.312733682e-02f, +9.691207919e-01f, +7.671115814e-02f, -2.642430666e-03f }, + { -4.318605280e-02f, +9.689933012e-01f, +7.690863832e-02f, -2.651520549e-03f }, + { -4.324464897e-02f, +9.688655578e-01f, +7.710627472e-02f, -2.660627431e-03f }, + { -4.330312541e-02f, +9.687375615e-01f, +7.730406734e-02f, -2.669751324e-03f }, + { -4.336148218e-02f, +9.686093127e-01f, +7.750201617e-02f, -2.678892238e-03f }, + { -4.341971936e-02f, +9.684808113e-01f, +7.770012120e-02f, -2.688050187e-03f }, + { -4.347783703e-02f, +9.683520574e-01f, +7.789838241e-02f, -2.697225181e-03f }, + { -4.353583525e-02f, +9.682230510e-01f, +7.809679980e-02f, -2.706417234e-03f }, + { -4.359371410e-02f, +9.680937924e-01f, +7.829537336e-02f, -2.715626355e-03f }, + { -4.365147366e-02f, +9.679642815e-01f, +7.849410308e-02f, -2.724852558e-03f }, + { -4.370911400e-02f, +9.678345184e-01f, +7.869298894e-02f, -2.734095855e-03f }, + { -4.376663518e-02f, +9.677045033e-01f, +7.889203094e-02f, -2.743356256e-03f }, + { -4.382403729e-02f, +9.675742361e-01f, +7.909122907e-02f, -2.752633774e-03f }, + { -4.388132040e-02f, +9.674437171e-01f, +7.929058330e-02f, -2.761928420e-03f }, + { -4.393848458e-02f, +9.673129462e-01f, +7.949009365e-02f, -2.771240206e-03f }, + { -4.399552991e-02f, +9.671819235e-01f, +7.968976008e-02f, -2.780569145e-03f }, + { -4.405245646e-02f, +9.670506492e-01f, +7.988958260e-02f, -2.789915247e-03f }, + { -4.410926430e-02f, +9.669191233e-01f, +8.008956119e-02f, -2.799278524e-03f }, + { -4.416595350e-02f, +9.667873459e-01f, +8.028969584e-02f, -2.808658988e-03f }, + { -4.422252415e-02f, +9.666553170e-01f, +8.048998653e-02f, -2.818056650e-03f }, + { -4.427897631e-02f, +9.665230369e-01f, +8.069043326e-02f, -2.827471523e-03f }, + { -4.433531006e-02f, +9.663905054e-01f, +8.089103602e-02f, -2.836903618e-03f }, + { -4.439152547e-02f, +9.662577228e-01f, +8.109179479e-02f, -2.846352946e-03f }, + { -4.444762262e-02f, +9.661246891e-01f, +8.129270955e-02f, -2.855819519e-03f }, + { -4.450360157e-02f, +9.659914044e-01f, +8.149378031e-02f, -2.865303349e-03f }, + { -4.455946242e-02f, +9.658578688e-01f, +8.169500704e-02f, -2.874804447e-03f }, + { -4.461520522e-02f, +9.657240824e-01f, +8.189638974e-02f, -2.884322825e-03f }, + { -4.467083005e-02f, +9.655900451e-01f, +8.209792838e-02f, -2.893858495e-03f }, + { -4.472633699e-02f, +9.654557573e-01f, +8.229962297e-02f, -2.903411467e-03f }, + { -4.478172612e-02f, +9.653212188e-01f, +8.250147348e-02f, -2.912981754e-03f }, + { -4.483699749e-02f, +9.651864298e-01f, +8.270347990e-02f, -2.922569366e-03f }, + { -4.489215120e-02f, +9.650513905e-01f, +8.290564223e-02f, -2.932174316e-03f }, + { -4.494718732e-02f, +9.649161008e-01f, +8.310796044e-02f, -2.941796615e-03f }, + { -4.500210591e-02f, +9.647805608e-01f, +8.331043452e-02f, -2.951436274e-03f }, + { -4.505690705e-02f, +9.646447707e-01f, +8.351306447e-02f, -2.961093304e-03f }, + { -4.511159083e-02f, +9.645087305e-01f, +8.371585026e-02f, -2.970767718e-03f }, + { -4.516615730e-02f, +9.643724404e-01f, +8.391879188e-02f, -2.980459527e-03f }, + { -4.522060655e-02f, +9.642359003e-01f, +8.412188933e-02f, -2.990168741e-03f }, + { -4.527493866e-02f, +9.640991104e-01f, +8.432514258e-02f, -2.999895373e-03f }, + { -4.532915369e-02f, +9.639620708e-01f, +8.452855162e-02f, -3.009639433e-03f }, + { -4.538325172e-02f, +9.638247816e-01f, +8.473211644e-02f, -3.019400934e-03f }, + { -4.543723283e-02f, +9.636872427e-01f, +8.493583703e-02f, -3.029179886e-03f }, + { -4.549109708e-02f, +9.635494545e-01f, +8.513971336e-02f, -3.038976300e-03f }, + { -4.554484457e-02f, +9.634114168e-01f, +8.534374543e-02f, -3.048790189e-03f }, + { -4.559847535e-02f, +9.632731298e-01f, +8.554793322e-02f, -3.058621563e-03f }, + { -4.565198951e-02f, +9.631345937e-01f, +8.575227672e-02f, -3.068470433e-03f }, + { -4.570538712e-02f, +9.629958084e-01f, +8.595677591e-02f, -3.078336812e-03f }, + { -4.575866826e-02f, +9.628567741e-01f, +8.616143077e-02f, -3.088220709e-03f }, + { -4.581183299e-02f, +9.627174908e-01f, +8.636624129e-02f, -3.098122137e-03f }, + { -4.586488140e-02f, +9.625779587e-01f, +8.657120747e-02f, -3.108041106e-03f }, + { -4.591781356e-02f, +9.624381778e-01f, +8.677632927e-02f, -3.117977628e-03f }, + { -4.597062955e-02f, +9.622981482e-01f, +8.698160668e-02f, -3.127931713e-03f }, + { -4.602332944e-02f, +9.621578700e-01f, +8.718703970e-02f, -3.137903374e-03f }, + { -4.607591330e-02f, +9.620173433e-01f, +8.739262830e-02f, -3.147892621e-03f }, + { -4.612838122e-02f, +9.618765682e-01f, +8.759837247e-02f, -3.157899465e-03f }, + { -4.618073326e-02f, +9.617355447e-01f, +8.780427220e-02f, -3.167923918e-03f }, + { -4.623296951e-02f, +9.615942731e-01f, +8.801032746e-02f, -3.177965990e-03f }, + { -4.628509003e-02f, +9.614527532e-01f, +8.821653824e-02f, -3.188025693e-03f }, + { -4.633709491e-02f, +9.613109853e-01f, +8.842290452e-02f, -3.198103037e-03f }, + { -4.638898421e-02f, +9.611689695e-01f, +8.862942629e-02f, -3.208198035e-03f }, + { -4.644075802e-02f, +9.610267057e-01f, +8.883610354e-02f, -3.218310696e-03f }, + { -4.649241641e-02f, +9.608841941e-01f, +8.904293624e-02f, -3.228441031e-03f }, + { -4.654395945e-02f, +9.607414349e-01f, +8.924992437e-02f, -3.238589053e-03f }, + { -4.659538723e-02f, +9.605984280e-01f, +8.945706793e-02f, -3.248754771e-03f }, + { -4.664669981e-02f, +9.604551736e-01f, +8.966436689e-02f, -3.258938197e-03f }, + { -4.669789727e-02f, +9.603116718e-01f, +8.987182124e-02f, -3.269139342e-03f }, + { -4.674897969e-02f, +9.601679226e-01f, +9.007943096e-02f, -3.279358217e-03f }, + { -4.679994715e-02f, +9.600239261e-01f, +9.028719604e-02f, -3.289594832e-03f }, + { -4.685079971e-02f, +9.598796825e-01f, +9.049511645e-02f, -3.299849199e-03f }, + { -4.690153746e-02f, +9.597351919e-01f, +9.070319217e-02f, -3.310121328e-03f }, + { -4.695216048e-02f, +9.595904542e-01f, +9.091142320e-02f, -3.320411230e-03f }, + { -4.700266883e-02f, +9.594454696e-01f, +9.111980950e-02f, -3.330718916e-03f }, + { -4.705306259e-02f, +9.593002383e-01f, +9.132835108e-02f, -3.341044398e-03f }, + { -4.710334184e-02f, +9.591547602e-01f, +9.153704790e-02f, -3.351387685e-03f }, + { -4.715350666e-02f, +9.590090355e-01f, +9.174589994e-02f, -3.361748789e-03f }, + { -4.720355712e-02f, +9.588630643e-01f, +9.195490720e-02f, -3.372127720e-03f }, + { -4.725349330e-02f, +9.587168467e-01f, +9.216406964e-02f, -3.382524489e-03f }, + { -4.730331527e-02f, +9.585703827e-01f, +9.237338726e-02f, -3.392939107e-03f }, + { -4.735302311e-02f, +9.584236724e-01f, +9.258286004e-02f, -3.403371585e-03f }, + { -4.740261690e-02f, +9.582767160e-01f, +9.279248795e-02f, -3.413821934e-03f }, + { -4.745209672e-02f, +9.581295136e-01f, +9.300227097e-02f, -3.424290163e-03f }, + { -4.750146263e-02f, +9.579820651e-01f, +9.321220909e-02f, -3.434776284e-03f }, + { -4.755071471e-02f, +9.578343708e-01f, +9.342230229e-02f, -3.445280308e-03f }, + { -4.759985305e-02f, +9.576864307e-01f, +9.363255055e-02f, -3.455802245e-03f }, + { -4.764887772e-02f, +9.575382449e-01f, +9.384295385e-02f, -3.466342106e-03f }, + { -4.769778879e-02f, +9.573898135e-01f, +9.405351217e-02f, -3.476899901e-03f }, + { -4.774658634e-02f, +9.572411365e-01f, +9.426422548e-02f, -3.487475641e-03f }, + { -4.779527045e-02f, +9.570922142e-01f, +9.447509378e-02f, -3.498069337e-03f }, + { -4.784384120e-02f, +9.569430465e-01f, +9.468611704e-02f, -3.508680999e-03f }, + { -4.789229866e-02f, +9.567936336e-01f, +9.489729524e-02f, -3.519310638e-03f }, + { -4.794064290e-02f, +9.566439756e-01f, +9.510862835e-02f, -3.529958264e-03f }, + { -4.798887401e-02f, +9.564940726e-01f, +9.532011637e-02f, -3.540623888e-03f }, + { -4.803699206e-02f, +9.563439245e-01f, +9.553175927e-02f, -3.551307520e-03f }, + { -4.808499713e-02f, +9.561935317e-01f, +9.574355703e-02f, -3.562009171e-03f }, + { -4.813288929e-02f, +9.560428941e-01f, +9.595550962e-02f, -3.572728851e-03f }, + { -4.818066862e-02f, +9.558920118e-01f, +9.616761703e-02f, -3.583466571e-03f }, + { -4.822833521e-02f, +9.557408849e-01f, +9.637987924e-02f, -3.594222342e-03f }, + { -4.827588911e-02f, +9.555895136e-01f, +9.659229622e-02f, -3.604996173e-03f }, + { -4.832333042e-02f, +9.554378979e-01f, +9.680486796e-02f, -3.615788075e-03f }, + { -4.837065921e-02f, +9.552860379e-01f, +9.701759443e-02f, -3.626598058e-03f }, + { -4.841787556e-02f, +9.551339337e-01f, +9.723047561e-02f, -3.637426133e-03f }, + { -4.846497954e-02f, +9.549815854e-01f, +9.744351149e-02f, -3.648272311e-03f }, + { -4.851197123e-02f, +9.548289931e-01f, +9.765670203e-02f, -3.659136601e-03f }, + { -4.855885071e-02f, +9.546761570e-01f, +9.787004721e-02f, -3.670019013e-03f }, + { -4.860561805e-02f, +9.545230770e-01f, +9.808354703e-02f, -3.680919559e-03f }, + { -4.865227334e-02f, +9.543697533e-01f, +9.829720144e-02f, -3.691838248e-03f }, + { -4.869881664e-02f, +9.542161860e-01f, +9.851101044e-02f, -3.702775091e-03f }, + { -4.874524804e-02f, +9.540623752e-01f, +9.872497399e-02f, -3.713730097e-03f }, + { -4.879156762e-02f, +9.539083209e-01f, +9.893909208e-02f, -3.724703278e-03f }, + { -4.883777544e-02f, +9.537540234e-01f, +9.915336469e-02f, -3.735694643e-03f }, + { -4.888387160e-02f, +9.535994826e-01f, +9.936779178e-02f, -3.746704203e-03f }, + { -4.892985616e-02f, +9.534446987e-01f, +9.958237334e-02f, -3.757731967e-03f }, + { -4.897572920e-02f, +9.532896717e-01f, +9.979710935e-02f, -3.768777946e-03f }, + { -4.902149080e-02f, +9.531344018e-01f, +1.000119998e-01f, -3.779842151e-03f }, + { -4.906714105e-02f, +9.529788891e-01f, +1.002270446e-01f, -3.790924590e-03f }, + { -4.911268001e-02f, +9.528231336e-01f, +1.004422438e-01f, -3.802025274e-03f }, + { -4.915810776e-02f, +9.526671355e-01f, +1.006575974e-01f, -3.813144214e-03f }, + { -4.920342438e-02f, +9.525108949e-01f, +1.008731053e-01f, -3.824281419e-03f }, + { -4.924862995e-02f, +9.523544118e-01f, +1.010887674e-01f, -3.835436900e-03f }, + { -4.929372455e-02f, +9.521976863e-01f, +1.013045839e-01f, -3.846610666e-03f }, + { -4.933870825e-02f, +9.520407186e-01f, +1.015205546e-01f, -3.857802728e-03f }, + { -4.938358114e-02f, +9.518835087e-01f, +1.017366796e-01f, -3.869013095e-03f }, + { -4.942834328e-02f, +9.517260568e-01f, +1.019529588e-01f, -3.880241778e-03f }, + { -4.947299476e-02f, +9.515683630e-01f, +1.021693921e-01f, -3.891488786e-03f }, + { -4.951753566e-02f, +9.514104273e-01f, +1.023859797e-01f, -3.902754129e-03f }, + { -4.956196605e-02f, +9.512522498e-01f, +1.026027213e-01f, -3.914037818e-03f }, + { -4.960628601e-02f, +9.510938306e-01f, +1.028196171e-01f, -3.925339862e-03f }, + { -4.965049562e-02f, +9.509351700e-01f, +1.030366669e-01f, -3.936660271e-03f }, + { -4.969459496e-02f, +9.507762678e-01f, +1.032538708e-01f, -3.947999054e-03f }, + { -4.973858410e-02f, +9.506171243e-01f, +1.034712288e-01f, -3.959356223e-03f }, + { -4.978246313e-02f, +9.504577395e-01f, +1.036887408e-01f, -3.970731786e-03f }, + { -4.982623211e-02f, +9.502981136e-01f, +1.039064067e-01f, -3.982125754e-03f }, + { -4.986989114e-02f, +9.501382466e-01f, +1.041242266e-01f, -3.993538136e-03f }, + { -4.991344028e-02f, +9.499781386e-01f, +1.043422005e-01f, -4.004968942e-03f }, + { -4.995687962e-02f, +9.498177898e-01f, +1.045603282e-01f, -4.016418182e-03f }, + { -5.000020923e-02f, +9.496572003e-01f, +1.047786099e-01f, -4.027885865e-03f }, + { -5.004342920e-02f, +9.494963700e-01f, +1.049970454e-01f, -4.039372002e-03f }, + { -5.008653959e-02f, +9.493352993e-01f, +1.052156347e-01f, -4.050876601e-03f }, + { -5.012954049e-02f, +9.491739880e-01f, +1.054343779e-01f, -4.062399673e-03f }, + { -5.017243198e-02f, +9.490124364e-01f, +1.056532748e-01f, -4.073941228e-03f }, + { -5.021521414e-02f, +9.488506446e-01f, +1.058723254e-01f, -4.085501274e-03f }, + { -5.025788704e-02f, +9.486886126e-01f, +1.060915299e-01f, -4.097079822e-03f }, + { -5.030045076e-02f, +9.485263405e-01f, +1.063108880e-01f, -4.108676881e-03f }, + { -5.034290538e-02f, +9.483638285e-01f, +1.065303997e-01f, -4.120292461e-03f }, + { -5.038525098e-02f, +9.482010767e-01f, +1.067500652e-01f, -4.131926571e-03f }, + { -5.042748764e-02f, +9.480380851e-01f, +1.069698842e-01f, -4.143579220e-03f }, + { -5.046961543e-02f, +9.478748538e-01f, +1.071898568e-01f, -4.155250420e-03f }, + { -5.051163444e-02f, +9.477113830e-01f, +1.074099830e-01f, -4.166940178e-03f }, + { -5.055354474e-02f, +9.475476728e-01f, +1.076302628e-01f, -4.178648505e-03f }, + { -5.059534642e-02f, +9.473837233e-01f, +1.078506960e-01f, -4.190375409e-03f }, + { -5.063703954e-02f, +9.472195345e-01f, +1.080712828e-01f, -4.202120901e-03f }, + { -5.067862420e-02f, +9.470551065e-01f, +1.082920229e-01f, -4.213884989e-03f }, + { -5.072010046e-02f, +9.468904396e-01f, +1.085129166e-01f, -4.225667684e-03f }, + { -5.076146841e-02f, +9.467255337e-01f, +1.087339636e-01f, -4.237468994e-03f }, + { -5.080272812e-02f, +9.465603890e-01f, +1.089551640e-01f, -4.249288929e-03f }, + { -5.084387968e-02f, +9.463950056e-01f, +1.091765177e-01f, -4.261127499e-03f }, + { -5.088492316e-02f, +9.462293836e-01f, +1.093980247e-01f, -4.272984712e-03f }, + { -5.092585865e-02f, +9.460635230e-01f, +1.096196851e-01f, -4.284860578e-03f }, + { -5.096668621e-02f, +9.458974241e-01f, +1.098414987e-01f, -4.296755107e-03f }, + { -5.100740594e-02f, +9.457310868e-01f, +1.100634655e-01f, -4.308668306e-03f }, + { -5.104801790e-02f, +9.455645113e-01f, +1.102855855e-01f, -4.320600187e-03f }, + { -5.108852219e-02f, +9.453976978e-01f, +1.105078587e-01f, -4.332550758e-03f }, + { -5.112891887e-02f, +9.452306462e-01f, +1.107302850e-01f, -4.344520028e-03f }, + { -5.116920802e-02f, +9.450633568e-01f, +1.109528645e-01f, -4.356508006e-03f }, + { -5.120938973e-02f, +9.448958296e-01f, +1.111755970e-01f, -4.368514702e-03f }, + { -5.124946408e-02f, +9.447280647e-01f, +1.113984826e-01f, -4.380540125e-03f }, + { -5.128943114e-02f, +9.445600623e-01f, +1.116215212e-01f, -4.392584284e-03f }, + { -5.132929099e-02f, +9.443918223e-01f, +1.118447128e-01f, -4.404647188e-03f }, + { -5.136904371e-02f, +9.442233450e-01f, +1.120680573e-01f, -4.416728846e-03f }, + { -5.140868939e-02f, +9.440546305e-01f, +1.122915548e-01f, -4.428829267e-03f }, + { -5.144822810e-02f, +9.438856788e-01f, +1.125152052e-01f, -4.440948461e-03f }, + { -5.148765991e-02f, +9.437164901e-01f, +1.127390085e-01f, -4.453086436e-03f }, + { -5.152698491e-02f, +9.435470644e-01f, +1.129629646e-01f, -4.465243201e-03f }, + { -5.156620319e-02f, +9.433774019e-01f, +1.131870735e-01f, -4.477418765e-03f }, + { -5.160531481e-02f, +9.432075026e-01f, +1.134113352e-01f, -4.489613138e-03f }, + { -5.164431986e-02f, +9.430373667e-01f, +1.136357497e-01f, -4.501826329e-03f }, + { -5.168321841e-02f, +9.428669944e-01f, +1.138603169e-01f, -4.514058345e-03f }, + { -5.172201055e-02f, +9.426963856e-01f, +1.140850368e-01f, -4.526309197e-03f }, + { -5.176069636e-02f, +9.425255405e-01f, +1.143099093e-01f, -4.538578892e-03f }, + { -5.179927591e-02f, +9.423544592e-01f, +1.145349345e-01f, -4.550867441e-03f }, + { -5.183774929e-02f, +9.421831418e-01f, +1.147601122e-01f, -4.563174852e-03f }, + { -5.187611657e-02f, +9.420115885e-01f, +1.149854425e-01f, -4.575501133e-03f }, + { -5.191437783e-02f, +9.418397993e-01f, +1.152109254e-01f, -4.587846293e-03f }, + { -5.195253316e-02f, +9.416677743e-01f, +1.154365607e-01f, -4.600210342e-03f }, + { -5.199058263e-02f, +9.414955136e-01f, +1.156623486e-01f, -4.612593288e-03f }, + { -5.202852632e-02f, +9.413230174e-01f, +1.158882888e-01f, -4.624995140e-03f }, + { -5.206636432e-02f, +9.411502858e-01f, +1.161143815e-01f, -4.637415906e-03f }, + { -5.210409670e-02f, +9.409773188e-01f, +1.163406265e-01f, -4.649855595e-03f }, + { -5.214172354e-02f, +9.408041166e-01f, +1.165670239e-01f, -4.662314217e-03f }, + { -5.217924493e-02f, +9.406306792e-01f, +1.167935736e-01f, -4.674791779e-03f }, + { -5.221666093e-02f, +9.404570069e-01f, +1.170202755e-01f, -4.687288290e-03f }, + { -5.225397164e-02f, +9.402830997e-01f, +1.172471298e-01f, -4.699803759e-03f }, + { -5.229117713e-02f, +9.401089576e-01f, +1.174741362e-01f, -4.712338195e-03f }, + { -5.232827748e-02f, +9.399345809e-01f, +1.177012947e-01f, -4.724891606e-03f }, + { -5.236527277e-02f, +9.397599696e-01f, +1.179286055e-01f, -4.737464000e-03f }, + { -5.240216308e-02f, +9.395851238e-01f, +1.181560683e-01f, -4.750055387e-03f }, + { -5.243894849e-02f, +9.394100437e-01f, +1.183836832e-01f, -4.762665774e-03f }, + { -5.247562909e-02f, +9.392347294e-01f, +1.186114502e-01f, -4.775295171e-03f }, + { -5.251220495e-02f, +9.390591808e-01f, +1.188393692e-01f, -4.787943585e-03f }, + { -5.254867615e-02f, +9.388833983e-01f, +1.190674401e-01f, -4.800611026e-03f }, + { -5.258504277e-02f, +9.387073818e-01f, +1.192956630e-01f, -4.813297501e-03f }, + { -5.262130489e-02f, +9.385311316e-01f, +1.195240378e-01f, -4.826003020e-03f }, + { -5.265746259e-02f, +9.383546476e-01f, +1.197525644e-01f, -4.838727590e-03f }, + { -5.269351596e-02f, +9.381779301e-01f, +1.199812429e-01f, -4.851471219e-03f }, + { -5.272946506e-02f, +9.380009790e-01f, +1.202100732e-01f, -4.864233917e-03f }, + { -5.276531000e-02f, +9.378237946e-01f, +1.204390553e-01f, -4.877015692e-03f }, + { -5.280105083e-02f, +9.376463769e-01f, +1.206681891e-01f, -4.889816551e-03f }, + { -5.283668764e-02f, +9.374687261e-01f, +1.208974746e-01f, -4.902636504e-03f }, + { -5.287222052e-02f, +9.372908423e-01f, +1.211269118e-01f, -4.915475558e-03f }, + { -5.290764955e-02f, +9.371127255e-01f, +1.213565006e-01f, -4.928333721e-03f }, + { -5.294297479e-02f, +9.369343759e-01f, +1.215862410e-01f, -4.941211003e-03f }, + { -5.297819635e-02f, +9.367557936e-01f, +1.218161330e-01f, -4.954107411e-03f }, + { -5.301331428e-02f, +9.365769788e-01f, +1.220461765e-01f, -4.967022953e-03f }, + { -5.304832868e-02f, +9.363979314e-01f, +1.222763714e-01f, -4.979957637e-03f }, + { -5.308323963e-02f, +9.362186517e-01f, +1.225067179e-01f, -4.992911472e-03f }, + { -5.311804720e-02f, +9.360391397e-01f, +1.227372157e-01f, -5.005884466e-03f }, + { -5.315275148e-02f, +9.358593955e-01f, +1.229678649e-01f, -5.018876627e-03f }, + { -5.318735255e-02f, +9.356794194e-01f, +1.231986655e-01f, -5.031887963e-03f }, + { -5.322185049e-02f, +9.354992113e-01f, +1.234296174e-01f, -5.044918481e-03f }, + { -5.325624537e-02f, +9.353187714e-01f, +1.236607205e-01f, -5.057968191e-03f }, + { -5.329053728e-02f, +9.351380998e-01f, +1.238919749e-01f, -5.071037100e-03f }, + { -5.332472630e-02f, +9.349571966e-01f, +1.241233805e-01f, -5.084125216e-03f }, + { -5.335881251e-02f, +9.347760620e-01f, +1.243549372e-01f, -5.097232546e-03f }, + { -5.339279600e-02f, +9.345946960e-01f, +1.245866451e-01f, -5.110359100e-03f }, + { -5.342667683e-02f, +9.344130987e-01f, +1.248185041e-01f, -5.123504885e-03f }, + { -5.346045510e-02f, +9.342312704e-01f, +1.250505141e-01f, -5.136669908e-03f }, + { -5.349413088e-02f, +9.340492110e-01f, +1.252826751e-01f, -5.149854178e-03f }, + { -5.352770425e-02f, +9.338669207e-01f, +1.255149871e-01f, -5.163057703e-03f }, + { -5.356117530e-02f, +9.336843996e-01f, +1.257474501e-01f, -5.176280490e-03f }, + { -5.359454410e-02f, +9.335016478e-01f, +1.259800639e-01f, -5.189522547e-03f }, + { -5.362781074e-02f, +9.333186655e-01f, +1.262128286e-01f, -5.202783882e-03f }, + { -5.366097530e-02f, +9.331354527e-01f, +1.264457441e-01f, -5.216064503e-03f }, + { -5.369403785e-02f, +9.329520096e-01f, +1.266788105e-01f, -5.229364417e-03f }, + { -5.372699848e-02f, +9.327683363e-01f, +1.269120275e-01f, -5.242683633e-03f }, + { -5.375985728e-02f, +9.325844328e-01f, +1.271453953e-01f, -5.256022157e-03f }, + { -5.379261431e-02f, +9.324002994e-01f, +1.273789138e-01f, -5.269379998e-03f }, + { -5.382526966e-02f, +9.322159361e-01f, +1.276125829e-01f, -5.282757164e-03f }, + { -5.385782342e-02f, +9.320313430e-01f, +1.278464026e-01f, -5.296153661e-03f }, + { -5.389027566e-02f, +9.318465203e-01f, +1.280803728e-01f, -5.309569498e-03f }, + { -5.392262647e-02f, +9.316614680e-01f, +1.283144936e-01f, -5.323004682e-03f }, + { -5.395487592e-02f, +9.314761864e-01f, +1.285487648e-01f, -5.336459220e-03f }, + { -5.398702410e-02f, +9.312906754e-01f, +1.287831865e-01f, -5.349933121e-03f }, + { -5.401907109e-02f, +9.311049353e-01f, +1.290177586e-01f, -5.363426391e-03f }, + { -5.405101697e-02f, +9.309189660e-01f, +1.292524811e-01f, -5.376939039e-03f }, + { -5.408286181e-02f, +9.307327679e-01f, +1.294873538e-01f, -5.390471071e-03f }, + { -5.411460571e-02f, +9.305463408e-01f, +1.297223769e-01f, -5.404022496e-03f }, + { -5.414624875e-02f, +9.303596851e-01f, +1.299575502e-01f, -5.417593320e-03f }, + { -5.417779099e-02f, +9.301728008e-01f, +1.301928737e-01f, -5.431183551e-03f }, + { -5.420923253e-02f, +9.299856879e-01f, +1.304283474e-01f, -5.444793197e-03f }, + { -5.424057345e-02f, +9.297983467e-01f, +1.306639711e-01f, -5.458422264e-03f }, + { -5.427181382e-02f, +9.296107773e-01f, +1.308997450e-01f, -5.472070761e-03f }, + { -5.430295374e-02f, +9.294229797e-01f, +1.311356689e-01f, -5.485738694e-03f }, + { -5.433399327e-02f, +9.292349540e-01f, +1.313717428e-01f, -5.499426070e-03f }, + { -5.436493251e-02f, +9.290467005e-01f, +1.316079667e-01f, -5.513132898e-03f }, + { -5.439577153e-02f, +9.288582192e-01f, +1.318443405e-01f, -5.526859184e-03f }, + { -5.442651041e-02f, +9.286695102e-01f, +1.320808642e-01f, -5.540604935e-03f }, + { -5.445714924e-02f, +9.284805736e-01f, +1.323175377e-01f, -5.554370159e-03f }, + { -5.448768810e-02f, +9.282914097e-01f, +1.325543610e-01f, -5.568154863e-03f }, + { -5.451812706e-02f, +9.281020184e-01f, +1.327913341e-01f, -5.581959054e-03f }, + { -5.454846622e-02f, +9.279123998e-01f, +1.330284568e-01f, -5.595782739e-03f }, + { -5.457870565e-02f, +9.277225543e-01f, +1.332657293e-01f, -5.609625925e-03f }, + { -5.460884543e-02f, +9.275324817e-01f, +1.335031514e-01f, -5.623488619e-03f }, + { -5.463888565e-02f, +9.273421823e-01f, +1.337407230e-01f, -5.637370829e-03f }, + { -5.466882638e-02f, +9.271516562e-01f, +1.339784442e-01f, -5.651272561e-03f }, + { -5.469866771e-02f, +9.269609034e-01f, +1.342163150e-01f, -5.665193823e-03f }, + { -5.472840972e-02f, +9.267699242e-01f, +1.344543351e-01f, -5.679134621e-03f }, + { -5.475805249e-02f, +9.265787186e-01f, +1.346925048e-01f, -5.693094963e-03f }, + { -5.478759611e-02f, +9.263872867e-01f, +1.349308237e-01f, -5.707074854e-03f }, + { -5.481704064e-02f, +9.261956287e-01f, +1.351692920e-01f, -5.721074303e-03f }, + { -5.484638619e-02f, +9.260037447e-01f, +1.354079097e-01f, -5.735093317e-03f }, + { -5.487563282e-02f, +9.258116348e-01f, +1.356466765e-01f, -5.749131901e-03f }, + { -5.490478063e-02f, +9.256192991e-01f, +1.358855926e-01f, -5.763190063e-03f }, + { -5.493382968e-02f, +9.254267378e-01f, +1.361246578e-01f, -5.777267810e-03f }, + { -5.496278007e-02f, +9.252339509e-01f, +1.363638722e-01f, -5.791365148e-03f }, + { -5.499163187e-02f, +9.250409386e-01f, +1.366032356e-01f, -5.805482085e-03f }, + { -5.502038517e-02f, +9.248477010e-01f, +1.368427480e-01f, -5.819618626e-03f }, + { -5.504904005e-02f, +9.246542383e-01f, +1.370824095e-01f, -5.833774780e-03f }, + { -5.507759658e-02f, +9.244605504e-01f, +1.373222199e-01f, -5.847950552e-03f }, + { -5.510605486e-02f, +9.242666377e-01f, +1.375621792e-01f, -5.862145949e-03f }, + { -5.513441497e-02f, +9.240725001e-01f, +1.378022873e-01f, -5.876360977e-03f }, + { -5.516267698e-02f, +9.238781378e-01f, +1.380425443e-01f, -5.890595645e-03f }, + { -5.519084097e-02f, +9.236835509e-01f, +1.382829500e-01f, -5.904849957e-03f }, + { -5.521890704e-02f, +9.234887396e-01f, +1.385235045e-01f, -5.919123921e-03f }, + { -5.524687526e-02f, +9.232937039e-01f, +1.387642076e-01f, -5.933417543e-03f }, + { -5.527474572e-02f, +9.230984441e-01f, +1.390050594e-01f, -5.947730830e-03f }, + { -5.530251848e-02f, +9.229029601e-01f, +1.392460598e-01f, -5.962063788e-03f }, + { -5.533019365e-02f, +9.227072522e-01f, +1.394872087e-01f, -5.976416423e-03f }, + { -5.535777130e-02f, +9.225113204e-01f, +1.397285061e-01f, -5.990788743e-03f }, + { -5.538525151e-02f, +9.223151649e-01f, +1.399699520e-01f, -6.005180754e-03f }, + { -5.541263436e-02f, +9.221187858e-01f, +1.402115463e-01f, -6.019592461e-03f }, + { -5.543991994e-02f, +9.219221832e-01f, +1.404532889e-01f, -6.034023872e-03f }, + { -5.546710832e-02f, +9.217253573e-01f, +1.406951799e-01f, -6.048474993e-03f }, + { -5.549419960e-02f, +9.215283081e-01f, +1.409372192e-01f, -6.062945830e-03f }, + { -5.552119385e-02f, +9.213310358e-01f, +1.411794066e-01f, -6.077436389e-03f }, + { -5.554809115e-02f, +9.211335405e-01f, +1.414217423e-01f, -6.091946678e-03f }, + { -5.557489159e-02f, +9.209358223e-01f, +1.416642261e-01f, -6.106476701e-03f }, + { -5.560159525e-02f, +9.207378813e-01f, +1.419068580e-01f, -6.121026465e-03f }, + { -5.562820221e-02f, +9.205397178e-01f, +1.421496379e-01f, -6.135595977e-03f }, + { -5.565471255e-02f, +9.203413317e-01f, +1.423925658e-01f, -6.150185242e-03f }, + { -5.568112636e-02f, +9.201427233e-01f, +1.426356417e-01f, -6.164794268e-03f }, + { -5.570744371e-02f, +9.199438926e-01f, +1.428788655e-01f, -6.179423059e-03f }, + { -5.573366470e-02f, +9.197448398e-01f, +1.431222371e-01f, -6.194071622e-03f }, + { -5.575978940e-02f, +9.195455649e-01f, +1.433657566e-01f, -6.208739964e-03f }, + { -5.578581789e-02f, +9.193460682e-01f, +1.436094238e-01f, -6.223428090e-03f }, + { -5.581175026e-02f, +9.191463497e-01f, +1.438532387e-01f, -6.238136006e-03f }, + { -5.583758659e-02f, +9.189464096e-01f, +1.440972013e-01f, -6.252863718e-03f }, + { -5.586332696e-02f, +9.187462479e-01f, +1.443413115e-01f, -6.267611233e-03f }, + { -5.588897145e-02f, +9.185458649e-01f, +1.445855693e-01f, -6.282378556e-03f }, + { -5.591452015e-02f, +9.183452606e-01f, +1.448299746e-01f, -6.297165693e-03f }, + { -5.593997314e-02f, +9.181444351e-01f, +1.450745274e-01f, -6.311972650e-03f }, + { -5.596533050e-02f, +9.179433886e-01f, +1.453192276e-01f, -6.326799433e-03f }, + { -5.599059231e-02f, +9.177421213e-01f, +1.455640752e-01f, -6.341646048e-03f }, + { -5.601575866e-02f, +9.175406331e-01f, +1.458090702e-01f, -6.356512501e-03f }, + { -5.604082963e-02f, +9.173389243e-01f, +1.460542124e-01f, -6.371398797e-03f }, + { -5.606580530e-02f, +9.171369950e-01f, +1.462995018e-01f, -6.386304943e-03f }, + { -5.609068575e-02f, +9.169348454e-01f, +1.465449385e-01f, -6.401230944e-03f }, + { -5.611547107e-02f, +9.167324754e-01f, +1.467905223e-01f, -6.416176806e-03f }, + { -5.614016133e-02f, +9.165298853e-01f, +1.470362531e-01f, -6.431142534e-03f }, + { -5.616475663e-02f, +9.163270752e-01f, +1.472821311e-01f, -6.446128134e-03f }, + { -5.618925704e-02f, +9.161240451e-01f, +1.475281560e-01f, -6.461133612e-03f }, + { -5.621366265e-02f, +9.159207954e-01f, +1.477743278e-01f, -6.476158974e-03f }, + { -5.623797353e-02f, +9.157173259e-01f, +1.480206466e-01f, -6.491204225e-03f }, + { -5.626218978e-02f, +9.155136370e-01f, +1.482671122e-01f, -6.506269371e-03f }, + { -5.628631147e-02f, +9.153097287e-01f, +1.485137245e-01f, -6.521354417e-03f }, + { -5.631033869e-02f, +9.151056011e-01f, +1.487604837e-01f, -6.536459369e-03f }, + { -5.633427152e-02f, +9.149012543e-01f, +1.490073895e-01f, -6.551584232e-03f }, + { -5.635811004e-02f, +9.146966886e-01f, +1.492544420e-01f, -6.566729012e-03f }, + { -5.638185433e-02f, +9.144919040e-01f, +1.495016410e-01f, -6.581893714e-03f }, + { -5.640550448e-02f, +9.142869006e-01f, +1.497489866e-01f, -6.597078344e-03f }, + { -5.642906057e-02f, +9.140816786e-01f, +1.499964787e-01f, -6.612282907e-03f }, + { -5.645252268e-02f, +9.138762381e-01f, +1.502441173e-01f, -6.627507408e-03f }, + { -5.647589090e-02f, +9.136705792e-01f, +1.504919022e-01f, -6.642751853e-03f }, + { -5.649916531e-02f, +9.134647021e-01f, +1.507398335e-01f, -6.658016248e-03f }, + { -5.652234599e-02f, +9.132586068e-01f, +1.509879110e-01f, -6.673300597e-03f }, + { -5.654543302e-02f, +9.130522936e-01f, +1.512361348e-01f, -6.688604905e-03f }, + { -5.656842649e-02f, +9.128457625e-01f, +1.514845048e-01f, -6.703929179e-03f }, + { -5.659132648e-02f, +9.126390136e-01f, +1.517330210e-01f, -6.719273423e-03f }, + { -5.661413307e-02f, +9.124320471e-01f, +1.519816832e-01f, -6.734637642e-03f }, + { -5.663684634e-02f, +9.122248632e-01f, +1.522304914e-01f, -6.750021842e-03f }, + { -5.665946638e-02f, +9.120174619e-01f, +1.524794457e-01f, -6.765426027e-03f }, + { -5.668199328e-02f, +9.118098434e-01f, +1.527285458e-01f, -6.780850204e-03f }, + { -5.670442710e-02f, +9.116020078e-01f, +1.529777918e-01f, -6.796294376e-03f }, + { -5.672676795e-02f, +9.113939552e-01f, +1.532271837e-01f, -6.811758549e-03f }, + { -5.674901589e-02f, +9.111856858e-01f, +1.534767213e-01f, -6.827242728e-03f }, + { -5.677117101e-02f, +9.109771996e-01f, +1.537264047e-01f, -6.842746919e-03f }, + { -5.679323340e-02f, +9.107684969e-01f, +1.539762337e-01f, -6.858271125e-03f }, + { -5.681520314e-02f, +9.105595777e-01f, +1.542262084e-01f, -6.873815352e-03f }, + { -5.683708031e-02f, +9.103504422e-01f, +1.544763286e-01f, -6.889379605e-03f }, + { -5.685886499e-02f, +9.101410905e-01f, +1.547265943e-01f, -6.904963889e-03f }, + { -5.688055727e-02f, +9.099315228e-01f, +1.549770055e-01f, -6.920568209e-03f }, + { -5.690215723e-02f, +9.097217391e-01f, +1.552275620e-01f, -6.936192569e-03f }, + { -5.692366496e-02f, +9.095117396e-01f, +1.554782640e-01f, -6.951836974e-03f }, + { -5.694508053e-02f, +9.093015244e-01f, +1.557291112e-01f, -6.967501430e-03f }, + { -5.696640402e-02f, +9.090910937e-01f, +1.559801037e-01f, -6.983185940e-03f }, + { -5.698763554e-02f, +9.088804476e-01f, +1.562312413e-01f, -6.998890510e-03f }, + { -5.700877514e-02f, +9.086695861e-01f, +1.564825241e-01f, -7.014615145e-03f }, + { -5.702982292e-02f, +9.084585096e-01f, +1.567339520e-01f, -7.030359849e-03f }, + { -5.705077897e-02f, +9.082472180e-01f, +1.569855249e-01f, -7.046124626e-03f }, + { -5.707164336e-02f, +9.080357115e-01f, +1.572372428e-01f, -7.061909482e-03f }, + { -5.709241618e-02f, +9.078239902e-01f, +1.574891056e-01f, -7.077714420e-03f }, + { -5.711309750e-02f, +9.076120543e-01f, +1.577411133e-01f, -7.093539446e-03f }, + { -5.713368743e-02f, +9.073999039e-01f, +1.579932657e-01f, -7.109384565e-03f }, + { -5.715418602e-02f, +9.071875391e-01f, +1.582455630e-01f, -7.125249780e-03f }, + { -5.717459338e-02f, +9.069749601e-01f, +1.584980049e-01f, -7.141135096e-03f }, + { -5.719490958e-02f, +9.067621670e-01f, +1.587505915e-01f, -7.157040517e-03f }, + { -5.721513471e-02f, +9.065491599e-01f, +1.590033227e-01f, -7.172966049e-03f }, + { -5.723526885e-02f, +9.063359389e-01f, +1.592561984e-01f, -7.188911695e-03f }, + { -5.725531208e-02f, +9.061225043e-01f, +1.595092186e-01f, -7.204877460e-03f }, + { -5.727526448e-02f, +9.059088560e-01f, +1.597623832e-01f, -7.220863348e-03f }, + { -5.729512615e-02f, +9.056949943e-01f, +1.600156922e-01f, -7.236869364e-03f }, + { -5.731489716e-02f, +9.054809193e-01f, +1.602691455e-01f, -7.252895512e-03f }, + { -5.733457759e-02f, +9.052666311e-01f, +1.605227430e-01f, -7.268941796e-03f }, + { -5.735416753e-02f, +9.050521298e-01f, +1.607764848e-01f, -7.285008220e-03f }, + { -5.737366706e-02f, +9.048374156e-01f, +1.610303707e-01f, -7.301094788e-03f }, + { -5.739307627e-02f, +9.046224887e-01f, +1.612844007e-01f, -7.317201506e-03f }, + { -5.741239524e-02f, +9.044073490e-01f, +1.615385747e-01f, -7.333328376e-03f }, + { -5.743162405e-02f, +9.041919969e-01f, +1.617928927e-01f, -7.349475404e-03f }, + { -5.745076279e-02f, +9.039764323e-01f, +1.620473546e-01f, -7.365642593e-03f }, + { -5.746981153e-02f, +9.037606555e-01f, +1.623019604e-01f, -7.381829947e-03f }, + { -5.748877036e-02f, +9.035446666e-01f, +1.625567100e-01f, -7.398037471e-03f }, + { -5.750763937e-02f, +9.033284656e-01f, +1.628116034e-01f, -7.414265167e-03f }, + { -5.752641864e-02f, +9.031120528e-01f, +1.630666404e-01f, -7.430513042e-03f }, + { -5.754510825e-02f, +9.028954283e-01f, +1.633218211e-01f, -7.446781098e-03f }, + { -5.756370829e-02f, +9.026785922e-01f, +1.635771453e-01f, -7.463069339e-03f }, + { -5.758221883e-02f, +9.024615447e-01f, +1.638326131e-01f, -7.479377769e-03f }, + { -5.760063997e-02f, +9.022442858e-01f, +1.640882243e-01f, -7.495706392e-03f }, + { -5.761897178e-02f, +9.020268157e-01f, +1.643439789e-01f, -7.512055213e-03f }, + { -5.763721435e-02f, +9.018091345e-01f, +1.645998769e-01f, -7.528424234e-03f }, + { -5.765536777e-02f, +9.015912425e-01f, +1.648559182e-01f, -7.544813460e-03f }, + { -5.767343210e-02f, +9.013731396e-01f, +1.651121027e-01f, -7.561222894e-03f }, + { -5.769140745e-02f, +9.011548261e-01f, +1.653684303e-01f, -7.577652540e-03f }, + { -5.770929389e-02f, +9.009363021e-01f, +1.656249011e-01f, -7.594102403e-03f }, + { -5.772709151e-02f, +9.007175676e-01f, +1.658815150e-01f, -7.610572484e-03f }, + { -5.774480038e-02f, +9.004986230e-01f, +1.661382718e-01f, -7.627062789e-03f }, + { -5.776242060e-02f, +9.002794682e-01f, +1.663951716e-01f, -7.643573321e-03f }, + { -5.777995224e-02f, +9.000601034e-01f, +1.666522142e-01f, -7.660104084e-03f }, + { -5.779739540e-02f, +8.998405288e-01f, +1.669093997e-01f, -7.676655080e-03f }, + { -5.781475014e-02f, +8.996207445e-01f, +1.671667279e-01f, -7.693226314e-03f }, + { -5.783201657e-02f, +8.994007506e-01f, +1.674241988e-01f, -7.709817790e-03f }, + { -5.784919475e-02f, +8.991805472e-01f, +1.676818124e-01f, -7.726429509e-03f }, + { -5.786628478e-02f, +8.989601346e-01f, +1.679395686e-01f, -7.743061477e-03f }, + { -5.788328673e-02f, +8.987395128e-01f, +1.681974672e-01f, -7.759713697e-03f }, + { -5.790020070e-02f, +8.985186819e-01f, +1.684555084e-01f, -7.776386171e-03f }, + { -5.791702676e-02f, +8.982976422e-01f, +1.687136919e-01f, -7.793078904e-03f }, + { -5.793376500e-02f, +8.980763937e-01f, +1.689720178e-01f, -7.809791898e-03f }, + { -5.795041550e-02f, +8.978549365e-01f, +1.692304860e-01f, -7.826525157e-03f }, + { -5.796697834e-02f, +8.976332709e-01f, +1.694890963e-01f, -7.843278685e-03f }, + { -5.798345361e-02f, +8.974113969e-01f, +1.697478489e-01f, -7.860052484e-03f }, + { -5.799984140e-02f, +8.971893148e-01f, +1.700067435e-01f, -7.876846558e-03f }, + { -5.801614178e-02f, +8.969670245e-01f, +1.702657802e-01f, -7.893660909e-03f }, + { -5.803235484e-02f, +8.967445263e-01f, +1.705249588e-01f, -7.910495542e-03f }, + { -5.804848066e-02f, +8.965218203e-01f, +1.707842794e-01f, -7.927350459e-03f }, + { -5.806451933e-02f, +8.962989066e-01f, +1.710437419e-01f, -7.944225664e-03f }, + { -5.808047093e-02f, +8.960757854e-01f, +1.713033461e-01f, -7.961121159e-03f }, + { -5.809633554e-02f, +8.958524568e-01f, +1.715630920e-01f, -7.978036948e-03f }, + { -5.811211325e-02f, +8.956289209e-01f, +1.718229797e-01f, -7.994973033e-03f }, + { -5.812780414e-02f, +8.954051779e-01f, +1.720830089e-01f, -8.011929418e-03f }, + { -5.814340830e-02f, +8.951812279e-01f, +1.723431797e-01f, -8.028906105e-03f }, + { -5.815892580e-02f, +8.949570711e-01f, +1.726034920e-01f, -8.045903098e-03f }, + { -5.817435673e-02f, +8.947327076e-01f, +1.728639457e-01f, -8.062920399e-03f }, + { -5.818970119e-02f, +8.945081375e-01f, +1.731245407e-01f, -8.079958012e-03f }, + { -5.820495924e-02f, +8.942833610e-01f, +1.733852771e-01f, -8.097015938e-03f }, + { -5.822013097e-02f, +8.940583782e-01f, +1.736461547e-01f, -8.114094182e-03f }, + { -5.823521647e-02f, +8.938331892e-01f, +1.739071734e-01f, -8.131192745e-03f }, + { -5.825021582e-02f, +8.936077942e-01f, +1.741683333e-01f, -8.148311631e-03f }, + { -5.826512910e-02f, +8.933821934e-01f, +1.744296343e-01f, -8.165450843e-03f }, + { -5.827995640e-02f, +8.931563868e-01f, +1.746910762e-01f, -8.182610382e-03f }, + { -5.829469780e-02f, +8.929303746e-01f, +1.749526590e-01f, -8.199790252e-03f }, + { -5.830935339e-02f, +8.927041569e-01f, +1.752143827e-01f, -8.216990455e-03f }, + { -5.832392325e-02f, +8.924777339e-01f, +1.754762472e-01f, -8.234210994e-03f }, + { -5.833840746e-02f, +8.922511057e-01f, +1.757382524e-01f, -8.251451871e-03f }, + { -5.835280610e-02f, +8.920242725e-01f, +1.760003983e-01f, -8.268713090e-03f }, + { -5.836711927e-02f, +8.917972344e-01f, +1.762626848e-01f, -8.285994652e-03f }, + { -5.838134704e-02f, +8.915699915e-01f, +1.765251119e-01f, -8.303296561e-03f }, + { -5.839548949e-02f, +8.913425440e-01f, +1.767876794e-01f, -8.320618817e-03f }, + { -5.840954672e-02f, +8.911148920e-01f, +1.770503873e-01f, -8.337961425e-03f }, + { -5.842351880e-02f, +8.908870357e-01f, +1.773132356e-01f, -8.355324387e-03f }, + { -5.843740582e-02f, +8.906589751e-01f, +1.775762242e-01f, -8.372707704e-03f }, + { -5.845120786e-02f, +8.904307105e-01f, +1.778393529e-01f, -8.390111379e-03f }, + { -5.846492501e-02f, +8.902022419e-01f, +1.781026219e-01f, -8.407535414e-03f }, + { -5.847855735e-02f, +8.899735696e-01f, +1.783660309e-01f, -8.424979813e-03f }, + { -5.849210496e-02f, +8.897446936e-01f, +1.786295800e-01f, -8.442444576e-03f }, + { -5.850556793e-02f, +8.895156142e-01f, +1.788932690e-01f, -8.459929707e-03f }, + { -5.851894634e-02f, +8.892863313e-01f, +1.791570979e-01f, -8.477435207e-03f }, + { -5.853224028e-02f, +8.890568452e-01f, +1.794210666e-01f, -8.494961078e-03f }, + { -5.854544983e-02f, +8.888271561e-01f, +1.796851751e-01f, -8.512507324e-03f }, + { -5.855857506e-02f, +8.885972640e-01f, +1.799494234e-01f, -8.530073945e-03f }, + { -5.857161608e-02f, +8.883671691e-01f, +1.802138112e-01f, -8.547660944e-03f }, + { -5.858457295e-02f, +8.881368715e-01f, +1.804783386e-01f, -8.565268323e-03f }, + { -5.859744577e-02f, +8.879063714e-01f, +1.807430055e-01f, -8.582896084e-03f }, + { -5.861023462e-02f, +8.876756689e-01f, +1.810078118e-01f, -8.600544229e-03f }, + { -5.862293958e-02f, +8.874447642e-01f, +1.812727576e-01f, -8.618212759e-03f }, + { -5.863556073e-02f, +8.872136574e-01f, +1.815378426e-01f, -8.635901678e-03f }, + { -5.864809816e-02f, +8.869823487e-01f, +1.818030668e-01f, -8.653610986e-03f }, + { -5.866055196e-02f, +8.867508381e-01f, +1.820684302e-01f, -8.671340686e-03f }, + { -5.867292220e-02f, +8.865191259e-01f, +1.823339328e-01f, -8.689090780e-03f }, + { -5.868520898e-02f, +8.862872121e-01f, +1.825995743e-01f, -8.706861268e-03f }, + { -5.869741236e-02f, +8.860550970e-01f, +1.828653548e-01f, -8.724652154e-03f }, + { -5.870953245e-02f, +8.858227806e-01f, +1.831312743e-01f, -8.742463438e-03f }, + { -5.872156932e-02f, +8.855902631e-01f, +1.833973325e-01f, -8.760295123e-03f }, + { -5.873352306e-02f, +8.853575446e-01f, +1.836635296e-01f, -8.778147210e-03f }, + { -5.874539374e-02f, +8.851246253e-01f, +1.839298653e-01f, -8.796019700e-03f }, + { -5.875718146e-02f, +8.848915054e-01f, +1.841963397e-01f, -8.813912596e-03f }, + { -5.876888630e-02f, +8.846581849e-01f, +1.844629526e-01f, -8.831825899e-03f }, + { -5.878050834e-02f, +8.844246640e-01f, +1.847297040e-01f, -8.849759611e-03f }, + { -5.879204767e-02f, +8.841909429e-01f, +1.849965939e-01f, -8.867713732e-03f }, + { -5.880350436e-02f, +8.839570217e-01f, +1.852636221e-01f, -8.885688265e-03f }, + { -5.881487851e-02f, +8.837229005e-01f, +1.855307886e-01f, -8.903683211e-03f }, + { -5.882617019e-02f, +8.834885795e-01f, +1.857980933e-01f, -8.921698572e-03f }, + { -5.883737950e-02f, +8.832540589e-01f, +1.860655362e-01f, -8.939734348e-03f }, + { -5.884850651e-02f, +8.830193387e-01f, +1.863331172e-01f, -8.957790542e-03f }, + { -5.885955131e-02f, +8.827844191e-01f, +1.866008361e-01f, -8.975867154e-03f }, + { -5.887051398e-02f, +8.825493003e-01f, +1.868686931e-01f, -8.993964185e-03f }, + { -5.888139461e-02f, +8.823139824e-01f, +1.871366879e-01f, -9.012081638e-03f }, + { -5.889219328e-02f, +8.820784655e-01f, +1.874048205e-01f, -9.030219514e-03f }, + { -5.890291007e-02f, +8.818427498e-01f, +1.876730909e-01f, -9.048377812e-03f }, + { -5.891354507e-02f, +8.816068354e-01f, +1.879414989e-01f, -9.066556536e-03f }, + { -5.892409837e-02f, +8.813707226e-01f, +1.882100446e-01f, -9.084755685e-03f }, + { -5.893457003e-02f, +8.811344113e-01f, +1.884787277e-01f, -9.102975262e-03f }, + { -5.894496016e-02f, +8.808979018e-01f, +1.887475484e-01f, -9.121215266e-03f }, + { -5.895526884e-02f, +8.806611943e-01f, +1.890165064e-01f, -9.139475700e-03f }, + { -5.896549614e-02f, +8.804242888e-01f, +1.892856017e-01f, -9.157756564e-03f }, + { -5.897564215e-02f, +8.801871854e-01f, +1.895548343e-01f, -9.176057859e-03f }, + { -5.898570696e-02f, +8.799498845e-01f, +1.898242041e-01f, -9.194379586e-03f }, + { -5.899569065e-02f, +8.797123860e-01f, +1.900937110e-01f, -9.212721746e-03f }, + { -5.900559330e-02f, +8.794746902e-01f, +1.903633550e-01f, -9.231084340e-03f }, + { -5.901541499e-02f, +8.792367971e-01f, +1.906331359e-01f, -9.249467369e-03f }, + { -5.902515582e-02f, +8.789987070e-01f, +1.909030537e-01f, -9.267870833e-03f }, + { -5.903481587e-02f, +8.787604199e-01f, +1.911731083e-01f, -9.286294734e-03f }, + { -5.904439521e-02f, +8.785219361e-01f, +1.914432997e-01f, -9.304739072e-03f }, + { -5.905389393e-02f, +8.782832556e-01f, +1.917136278e-01f, -9.323203848e-03f }, + { -5.906331213e-02f, +8.780443786e-01f, +1.919840925e-01f, -9.341689062e-03f }, + { -5.907264987e-02f, +8.778053053e-01f, +1.922546937e-01f, -9.360194716e-03f }, + { -5.908190725e-02f, +8.775660358e-01f, +1.925254314e-01f, -9.378720810e-03f }, + { -5.909108434e-02f, +8.773265703e-01f, +1.927963055e-01f, -9.397267344e-03f }, + { -5.910018124e-02f, +8.770869088e-01f, +1.930673159e-01f, -9.415834320e-03f }, + { -5.910919802e-02f, +8.768470516e-01f, +1.933384626e-01f, -9.434421737e-03f }, + { -5.911813478e-02f, +8.766069987e-01f, +1.936097455e-01f, -9.453029596e-03f }, + { -5.912699159e-02f, +8.763667504e-01f, +1.938811645e-01f, -9.471657898e-03f }, + { -5.913576853e-02f, +8.761263068e-01f, +1.941527195e-01f, -9.490306643e-03f }, + { -5.914446570e-02f, +8.758856680e-01f, +1.944244105e-01f, -9.508975831e-03f }, + { -5.915308317e-02f, +8.756448342e-01f, +1.946962373e-01f, -9.527665464e-03f }, + { -5.916162104e-02f, +8.754038055e-01f, +1.949682000e-01f, -9.546375540e-03f }, + { -5.917007938e-02f, +8.751625821e-01f, +1.952402985e-01f, -9.565106062e-03f }, + { -5.917845827e-02f, +8.749211640e-01f, +1.955125326e-01f, -9.583857028e-03f }, + { -5.918675781e-02f, +8.746795516e-01f, +1.957849023e-01f, -9.602628439e-03f }, + { -5.919497807e-02f, +8.744377448e-01f, +1.960574076e-01f, -9.621420295e-03f }, + { -5.920311914e-02f, +8.741957439e-01f, +1.963300483e-01f, -9.640232597e-03f }, + { -5.921118110e-02f, +8.739535491e-01f, +1.966028244e-01f, -9.659065344e-03f }, + { -5.921916404e-02f, +8.737111603e-01f, +1.968757357e-01f, -9.677918538e-03f }, + { -5.922706804e-02f, +8.734685779e-01f, +1.971487824e-01f, -9.696792177e-03f }, + { -5.923489319e-02f, +8.732258019e-01f, +1.974219642e-01f, -9.715686262e-03f }, + { -5.924263957e-02f, +8.729828325e-01f, +1.976952810e-01f, -9.734600793e-03f }, + { -5.925030726e-02f, +8.727396699e-01f, +1.979687329e-01f, -9.753535770e-03f }, + { -5.925789634e-02f, +8.724963141e-01f, +1.982423197e-01f, -9.772491192e-03f }, + { -5.926540690e-02f, +8.722527654e-01f, +1.985160414e-01f, -9.791467061e-03f }, + { -5.927283903e-02f, +8.720090239e-01f, +1.987898979e-01f, -9.810463375e-03f }, + { -5.928019281e-02f, +8.717650897e-01f, +1.990638891e-01f, -9.829480135e-03f }, + { -5.928746832e-02f, +8.715209630e-01f, +1.993380149e-01f, -9.848517340e-03f }, + { -5.929466565e-02f, +8.712766439e-01f, +1.996122753e-01f, -9.867574990e-03f }, + { -5.930178487e-02f, +8.710321326e-01f, +1.998866701e-01f, -9.886653086e-03f }, + { -5.930882608e-02f, +8.707874293e-01f, +2.001611994e-01f, -9.905751626e-03f }, + { -5.931578936e-02f, +8.705425340e-01f, +2.004358631e-01f, -9.924870610e-03f }, + { -5.932267479e-02f, +8.702974470e-01f, +2.007106609e-01f, -9.944010038e-03f }, + { -5.932948245e-02f, +8.700521683e-01f, +2.009855930e-01f, -9.963169910e-03f }, + { -5.933621243e-02f, +8.698066982e-01f, +2.012606592e-01f, -9.982350225e-03f }, + { -5.934286482e-02f, +8.695610368e-01f, +2.015358594e-01f, -1.000155098e-02f }, + { -5.934943969e-02f, +8.693151841e-01f, +2.018111936e-01f, -1.002077218e-02f }, + { -5.935593714e-02f, +8.690691405e-01f, +2.020866616e-01f, -1.004001383e-02f }, + { -5.936235724e-02f, +8.688229060e-01f, +2.023622635e-01f, -1.005927591e-02f }, + { -5.936870008e-02f, +8.685764808e-01f, +2.026379991e-01f, -1.007855843e-02f }, + { -5.937496574e-02f, +8.683298650e-01f, +2.029138683e-01f, -1.009786140e-02f }, + { -5.938115430e-02f, +8.680830587e-01f, +2.031898712e-01f, -1.011718480e-02f }, + { -5.938726586e-02f, +8.678360622e-01f, +2.034660075e-01f, -1.013652864e-02f }, + { -5.939330049e-02f, +8.675888756e-01f, +2.037422772e-01f, -1.015589292e-02f }, + { -5.939925828e-02f, +8.673414990e-01f, +2.040186803e-01f, -1.017527764e-02f }, + { -5.940513931e-02f, +8.670939326e-01f, +2.042952167e-01f, -1.019468279e-02f }, + { -5.941094367e-02f, +8.668461765e-01f, +2.045718862e-01f, -1.021410838e-02f }, + { -5.941667144e-02f, +8.665982309e-01f, +2.048486889e-01f, -1.023355441e-02f }, + { -5.942232269e-02f, +8.663500959e-01f, +2.051256246e-01f, -1.025302087e-02f }, + { -5.942789753e-02f, +8.661017718e-01f, +2.054026933e-01f, -1.027250776e-02f }, + { -5.943339603e-02f, +8.658532585e-01f, +2.056798948e-01f, -1.029201508e-02f }, + { -5.943881827e-02f, +8.656045563e-01f, +2.059572292e-01f, -1.031154284e-02f }, + { -5.944416434e-02f, +8.653556654e-01f, +2.062346962e-01f, -1.033109103e-02f }, + { -5.944943433e-02f, +8.651065858e-01f, +2.065122959e-01f, -1.035065964e-02f }, + { -5.945462831e-02f, +8.648573178e-01f, +2.067900282e-01f, -1.037024868e-02f }, + { -5.945974637e-02f, +8.646078615e-01f, +2.070678930e-01f, -1.038985815e-02f }, + { -5.946478859e-02f, +8.643582170e-01f, +2.073458902e-01f, -1.040948805e-02f }, + { -5.946975506e-02f, +8.641083845e-01f, +2.076240197e-01f, -1.042913837e-02f }, + { -5.947464586e-02f, +8.638583642e-01f, +2.079022815e-01f, -1.044880911e-02f }, + { -5.947946108e-02f, +8.636081562e-01f, +2.081806754e-01f, -1.046850028e-02f }, + { -5.948420080e-02f, +8.633577606e-01f, +2.084592015e-01f, -1.048821187e-02f }, + { -5.948886510e-02f, +8.631071776e-01f, +2.087378595e-01f, -1.050794387e-02f }, + { -5.949345407e-02f, +8.628564073e-01f, +2.090166495e-01f, -1.052769630e-02f }, + { -5.949796779e-02f, +8.626054500e-01f, +2.092955714e-01f, -1.054746914e-02f }, + { -5.950240634e-02f, +8.623543057e-01f, +2.095746250e-01f, -1.056726239e-02f }, + { -5.950676981e-02f, +8.621029746e-01f, +2.098538103e-01f, -1.058707606e-02f }, + { -5.951105828e-02f, +8.618514569e-01f, +2.101331273e-01f, -1.060691015e-02f }, + { -5.951527184e-02f, +8.615997527e-01f, +2.104125758e-01f, -1.062676464e-02f }, + { -5.951941056e-02f, +8.613478622e-01f, +2.106921557e-01f, -1.064663954e-02f }, + { -5.952347454e-02f, +8.610957855e-01f, +2.109718671e-01f, -1.066653486e-02f }, + { -5.952746386e-02f, +8.608435227e-01f, +2.112517097e-01f, -1.068645057e-02f }, + { -5.953137860e-02f, +8.605910741e-01f, +2.115316835e-01f, -1.070638669e-02f }, + { -5.953521884e-02f, +8.603384398e-01f, +2.118117885e-01f, -1.072634322e-02f }, + { -5.953898467e-02f, +8.600856199e-01f, +2.120920245e-01f, -1.074632015e-02f }, + { -5.954267617e-02f, +8.598326146e-01f, +2.123723915e-01f, -1.076631747e-02f }, + { -5.954629343e-02f, +8.595794240e-01f, +2.126528894e-01f, -1.078633519e-02f }, + { -5.954983652e-02f, +8.593260484e-01f, +2.129335181e-01f, -1.080637331e-02f }, + { -5.955330554e-02f, +8.590724878e-01f, +2.132142776e-01f, -1.082643182e-02f }, + { -5.955670057e-02f, +8.588187423e-01f, +2.134951677e-01f, -1.084651073e-02f }, + { -5.956002168e-02f, +8.585648123e-01f, +2.137761883e-01f, -1.086661002e-02f }, + { -5.956326897e-02f, +8.583106977e-01f, +2.140573395e-01f, -1.088672970e-02f }, + { -5.956644252e-02f, +8.580563988e-01f, +2.143386210e-01f, -1.090686977e-02f }, + { -5.956954241e-02f, +8.578019157e-01f, +2.146200329e-01f, -1.092703022e-02f }, + { -5.957256872e-02f, +8.575472486e-01f, +2.149015750e-01f, -1.094721106e-02f }, + { -5.957552154e-02f, +8.572923976e-01f, +2.151832473e-01f, -1.096741227e-02f }, + { -5.957840096e-02f, +8.570373629e-01f, +2.154650497e-01f, -1.098763386e-02f }, + { -5.958120705e-02f, +8.567821446e-01f, +2.157469820e-01f, -1.100787583e-02f }, + { -5.958393990e-02f, +8.565267430e-01f, +2.160290443e-01f, -1.102813817e-02f }, + { -5.958659959e-02f, +8.562711580e-01f, +2.163112364e-01f, -1.104842088e-02f }, + { -5.958918621e-02f, +8.560153900e-01f, +2.165935582e-01f, -1.106872396e-02f }, + { -5.959169984e-02f, +8.557594390e-01f, +2.168760097e-01f, -1.108904740e-02f }, + { -5.959414057e-02f, +8.555033052e-01f, +2.171585908e-01f, -1.110939121e-02f }, + { -5.959650847e-02f, +8.552469888e-01f, +2.174413013e-01f, -1.112975538e-02f }, + { -5.959880364e-02f, +8.549904899e-01f, +2.177241413e-01f, -1.115013991e-02f }, + { -5.960102614e-02f, +8.547338087e-01f, +2.180071107e-01f, -1.117054480e-02f }, + { -5.960317608e-02f, +8.544769453e-01f, +2.182902092e-01f, -1.119097004e-02f }, + { -5.960525353e-02f, +8.542198999e-01f, +2.185734369e-01f, -1.121141563e-02f }, + { -5.960725858e-02f, +8.539626726e-01f, +2.188567938e-01f, -1.123188157e-02f }, + { -5.960919130e-02f, +8.537052637e-01f, +2.191402796e-01f, -1.125236786e-02f }, + { -5.961105179e-02f, +8.534476731e-01f, +2.194238943e-01f, -1.127287449e-02f }, + { -5.961284013e-02f, +8.531899012e-01f, +2.197076378e-01f, -1.129340147e-02f }, + { -5.961455639e-02f, +8.529319481e-01f, +2.199915101e-01f, -1.131394878e-02f }, + { -5.961620067e-02f, +8.526738139e-01f, +2.202755110e-01f, -1.133451643e-02f }, + { -5.961777305e-02f, +8.524154987e-01f, +2.205596406e-01f, -1.135510441e-02f }, + { -5.961927360e-02f, +8.521570028e-01f, +2.208438986e-01f, -1.137571272e-02f }, + { -5.962070242e-02f, +8.518983262e-01f, +2.211282850e-01f, -1.139634135e-02f }, + { -5.962205959e-02f, +8.516394693e-01f, +2.214127997e-01f, -1.141699032e-02f }, + { -5.962334519e-02f, +8.513804320e-01f, +2.216974426e-01f, -1.143765960e-02f }, + { -5.962455930e-02f, +8.511212145e-01f, +2.219822138e-01f, -1.145834920e-02f }, + { -5.962570201e-02f, +8.508618171e-01f, +2.222671129e-01f, -1.147905912e-02f }, + { -5.962677341e-02f, +8.506022399e-01f, +2.225521401e-01f, -1.149978935e-02f }, + { -5.962777357e-02f, +8.503424830e-01f, +2.228372951e-01f, -1.152053989e-02f }, + { -5.962870257e-02f, +8.500825465e-01f, +2.231225780e-01f, -1.154131073e-02f }, + { -5.962956051e-02f, +8.498224307e-01f, +2.234079885e-01f, -1.156210188e-02f }, + { -5.963034747e-02f, +8.495621358e-01f, +2.236935267e-01f, -1.158291333e-02f }, + { -5.963106352e-02f, +8.493016617e-01f, +2.239791925e-01f, -1.160374507e-02f }, + { -5.963170876e-02f, +8.490410088e-01f, +2.242649857e-01f, -1.162459711e-02f }, + { -5.963228326e-02f, +8.487801771e-01f, +2.245509063e-01f, -1.164546944e-02f }, + { -5.963278711e-02f, +8.485191669e-01f, +2.248369542e-01f, -1.166636205e-02f }, + { -5.963322039e-02f, +8.482579783e-01f, +2.251231293e-01f, -1.168727495e-02f }, + { -5.963358319e-02f, +8.479966114e-01f, +2.254094315e-01f, -1.170820813e-02f }, + { -5.963387559e-02f, +8.477350664e-01f, +2.256958607e-01f, -1.172916159e-02f }, + { -5.963409767e-02f, +8.474733435e-01f, +2.259824169e-01f, -1.175013531e-02f }, + { -5.963424952e-02f, +8.472114428e-01f, +2.262690999e-01f, -1.177112931e-02f }, + { -5.963433122e-02f, +8.469493644e-01f, +2.265559097e-01f, -1.179214358e-02f }, + { -5.963434285e-02f, +8.466871086e-01f, +2.268428462e-01f, -1.181317810e-02f }, + { -5.963428449e-02f, +8.464246755e-01f, +2.271299092e-01f, -1.183423289e-02f }, + { -5.963415624e-02f, +8.461620653e-01f, +2.274170988e-01f, -1.185530793e-02f }, + { -5.963395817e-02f, +8.458992780e-01f, +2.277044148e-01f, -1.187640322e-02f }, + { -5.963369036e-02f, +8.456363139e-01f, +2.279918571e-01f, -1.189751876e-02f }, + { -5.963335291e-02f, +8.453731732e-01f, +2.282794257e-01f, -1.191865455e-02f }, + { -5.963294588e-02f, +8.451098559e-01f, +2.285671204e-01f, -1.193981057e-02f }, + { -5.963246938e-02f, +8.448463623e-01f, +2.288549412e-01f, -1.196098683e-02f }, + { -5.963192347e-02f, +8.445826924e-01f, +2.291428879e-01f, -1.198218333e-02f }, + { -5.963130824e-02f, +8.443188466e-01f, +2.294309606e-01f, -1.200340005e-02f }, + { -5.963062378e-02f, +8.440548248e-01f, +2.297191590e-01f, -1.202463700e-02f }, + { -5.962987017e-02f, +8.437906274e-01f, +2.300074831e-01f, -1.204589416e-02f }, + { -5.962904749e-02f, +8.435262543e-01f, +2.302959329e-01f, -1.206717155e-02f }, + { -5.962815583e-02f, +8.432617059e-01f, +2.305845082e-01f, -1.208846914e-02f }, + { -5.962719526e-02f, +8.429969822e-01f, +2.308732090e-01f, -1.210978695e-02f }, + { -5.962616588e-02f, +8.427320835e-01f, +2.311620351e-01f, -1.213112496e-02f }, + { -5.962506776e-02f, +8.424670098e-01f, +2.314509865e-01f, -1.215248317e-02f }, + { -5.962390099e-02f, +8.422017613e-01f, +2.317400630e-01f, -1.217386158e-02f }, + { -5.962266566e-02f, +8.419363383e-01f, +2.320292646e-01f, -1.219526018e-02f }, + { -5.962136183e-02f, +8.416707408e-01f, +2.323185913e-01f, -1.221667896e-02f }, + { -5.961998961e-02f, +8.414049690e-01f, +2.326080428e-01f, -1.223811793e-02f }, + { -5.961854906e-02f, +8.411390231e-01f, +2.328976192e-01f, -1.225957708e-02f }, + { -5.961704028e-02f, +8.408729033e-01f, +2.331873202e-01f, -1.228105640e-02f }, + { -5.961546335e-02f, +8.406066096e-01f, +2.334771460e-01f, -1.230255589e-02f }, + { -5.961381834e-02f, +8.403401423e-01f, +2.337670962e-01f, -1.232407555e-02f }, + { -5.961210535e-02f, +8.400735015e-01f, +2.340571709e-01f, -1.234561537e-02f }, + { -5.961032446e-02f, +8.398066874e-01f, +2.343473700e-01f, -1.236717535e-02f }, + { -5.960847575e-02f, +8.395397001e-01f, +2.346376934e-01f, -1.238875548e-02f }, + { -5.960655929e-02f, +8.392725398e-01f, +2.349281409e-01f, -1.241035575e-02f }, + { -5.960457519e-02f, +8.390052067e-01f, +2.352187125e-01f, -1.243197617e-02f }, + { -5.960252351e-02f, +8.387377009e-01f, +2.355094081e-01f, -1.245361672e-02f }, + { -5.960040434e-02f, +8.384700225e-01f, +2.358002277e-01f, -1.247527741e-02f }, + { -5.959821777e-02f, +8.382021718e-01f, +2.360911710e-01f, -1.249695823e-02f }, + { -5.959596388e-02f, +8.379341490e-01f, +2.363822381e-01f, -1.251865917e-02f }, + { -5.959364274e-02f, +8.376659540e-01f, +2.366734288e-01f, -1.254038023e-02f }, + { -5.959125445e-02f, +8.373975872e-01f, +2.369647431e-01f, -1.256212141e-02f }, + { -5.958879909e-02f, +8.371290487e-01f, +2.372561808e-01f, -1.258388269e-02f }, + { -5.958627674e-02f, +8.368603387e-01f, +2.375477418e-01f, -1.260566408e-02f }, + { -5.958368747e-02f, +8.365914572e-01f, +2.378394262e-01f, -1.262746557e-02f }, + { -5.958103139e-02f, +8.363224045e-01f, +2.381312337e-01f, -1.264928715e-02f }, + { -5.957830856e-02f, +8.360531808e-01f, +2.384231642e-01f, -1.267112882e-02f }, + { -5.957551907e-02f, +8.357837861e-01f, +2.387152178e-01f, -1.269299057e-02f }, + { -5.957266301e-02f, +8.355142207e-01f, +2.390073943e-01f, -1.271487241e-02f }, + { -5.956974045e-02f, +8.352444848e-01f, +2.392996936e-01f, -1.273677432e-02f }, + { -5.956675149e-02f, +8.349745784e-01f, +2.395921155e-01f, -1.275869629e-02f }, + { -5.956369619e-02f, +8.347045017e-01f, +2.398846601e-01f, -1.278063833e-02f }, + { -5.956057466e-02f, +8.344342550e-01f, +2.401773273e-01f, -1.280260043e-02f }, + { -5.955738696e-02f, +8.341638383e-01f, +2.404701168e-01f, -1.282458258e-02f }, + { -5.955413318e-02f, +8.338932518e-01f, +2.407630287e-01f, -1.284658477e-02f }, + { -5.955081340e-02f, +8.336224957e-01f, +2.410560629e-01f, -1.286860701e-02f }, + { -5.954742771e-02f, +8.333515702e-01f, +2.413492191e-01f, -1.289064929e-02f }, + { -5.954397620e-02f, +8.330804754e-01f, +2.416424975e-01f, -1.291271159e-02f }, + { -5.954045893e-02f, +8.328092115e-01f, +2.419358978e-01f, -1.293479392e-02f }, + { -5.953687600e-02f, +8.325377786e-01f, +2.422294200e-01f, -1.295689627e-02f }, + { -5.953322748e-02f, +8.322661770e-01f, +2.425230639e-01f, -1.297901864e-02f }, + { -5.952951347e-02f, +8.319944067e-01f, +2.428168296e-01f, -1.300116101e-02f }, + { -5.952573404e-02f, +8.317224679e-01f, +2.431107168e-01f, -1.302332339e-02f }, + { -5.952188928e-02f, +8.314503608e-01f, +2.434047255e-01f, -1.304550576e-02f }, + { -5.951797926e-02f, +8.311780856e-01f, +2.436988556e-01f, -1.306770813e-02f }, + { -5.951400408e-02f, +8.309056424e-01f, +2.439931070e-01f, -1.308993048e-02f }, + { -5.950996381e-02f, +8.306330313e-01f, +2.442874796e-01f, -1.311217281e-02f }, + { -5.950585853e-02f, +8.303602527e-01f, +2.445819733e-01f, -1.313443511e-02f }, + { -5.950168834e-02f, +8.300873065e-01f, +2.448765881e-01f, -1.315671739e-02f }, + { -5.949745330e-02f, +8.298141930e-01f, +2.451713237e-01f, -1.317901962e-02f }, + { -5.949315351e-02f, +8.295409124e-01f, +2.454661802e-01f, -1.320134181e-02f }, + { -5.948878905e-02f, +8.292674647e-01f, +2.457611574e-01f, -1.322368395e-02f }, + { -5.948436000e-02f, +8.289938502e-01f, +2.460562553e-01f, -1.324604604e-02f }, + { -5.947986644e-02f, +8.287200691e-01f, +2.463514737e-01f, -1.326842806e-02f }, + { -5.947530845e-02f, +8.284461214e-01f, +2.466468125e-01f, -1.329083001e-02f }, + { -5.947068612e-02f, +8.281720074e-01f, +2.469422717e-01f, -1.331325190e-02f }, + { -5.946599953e-02f, +8.278977272e-01f, +2.472378511e-01f, -1.333569370e-02f }, + { -5.946124877e-02f, +8.276232810e-01f, +2.475335507e-01f, -1.335815541e-02f }, + { -5.945643390e-02f, +8.273486689e-01f, +2.478293703e-01f, -1.338063703e-02f }, + { -5.945155503e-02f, +8.270738912e-01f, +2.481253099e-01f, -1.340313855e-02f }, + { -5.944661222e-02f, +8.267989479e-01f, +2.484213694e-01f, -1.342565996e-02f }, + { -5.944160557e-02f, +8.265238393e-01f, +2.487175486e-01f, -1.344820127e-02f }, + { -5.943653515e-02f, +8.262485655e-01f, +2.490138475e-01f, -1.347076245e-02f }, + { -5.943140104e-02f, +8.259731267e-01f, +2.493102660e-01f, -1.349334351e-02f }, + { -5.942620334e-02f, +8.256975230e-01f, +2.496068040e-01f, -1.351594444e-02f }, + { -5.942094212e-02f, +8.254217546e-01f, +2.499034613e-01f, -1.353856523e-02f }, + { -5.941561746e-02f, +8.251458217e-01f, +2.502002379e-01f, -1.356120587e-02f }, + { -5.941022944e-02f, +8.248697244e-01f, +2.504971337e-01f, -1.358386636e-02f }, + { -5.940477816e-02f, +8.245934629e-01f, +2.507941486e-01f, -1.360654670e-02f }, + { -5.939926369e-02f, +8.243170373e-01f, +2.510912825e-01f, -1.362924687e-02f }, + { -5.939368611e-02f, +8.240404479e-01f, +2.513885353e-01f, -1.365196686e-02f }, + { -5.938804550e-02f, +8.237636948e-01f, +2.516859068e-01f, -1.367470668e-02f }, + { -5.938234196e-02f, +8.234867781e-01f, +2.519833971e-01f, -1.369746632e-02f }, + { -5.937657555e-02f, +8.232096981e-01f, +2.522810059e-01f, -1.372024576e-02f }, + { -5.937074636e-02f, +8.229324548e-01f, +2.525787333e-01f, -1.374304500e-02f }, + { -5.936485448e-02f, +8.226550485e-01f, +2.528765790e-01f, -1.376586404e-02f }, + { -5.935889999e-02f, +8.223774794e-01f, +2.531745431e-01f, -1.378870286e-02f }, + { -5.935288297e-02f, +8.220997475e-01f, +2.534726253e-01f, -1.381156147e-02f }, + { -5.934680349e-02f, +8.218218531e-01f, +2.537708257e-01f, -1.383443984e-02f }, + { -5.934066165e-02f, +8.215437962e-01f, +2.540691440e-01f, -1.385733799e-02f }, + { -5.933445753e-02f, +8.212655772e-01f, +2.543675803e-01f, -1.388025589e-02f }, + { -5.932819120e-02f, +8.209871961e-01f, +2.546661344e-01f, -1.390319354e-02f }, + { -5.932186275e-02f, +8.207086532e-01f, +2.549648062e-01f, -1.392615094e-02f }, + { -5.931547226e-02f, +8.204299485e-01f, +2.552635955e-01f, -1.394912807e-02f }, + { -5.930901982e-02f, +8.201510823e-01f, +2.555625024e-01f, -1.397212493e-02f }, + { -5.930250550e-02f, +8.198720547e-01f, +2.558615267e-01f, -1.399514152e-02f }, + { -5.929592939e-02f, +8.195928658e-01f, +2.561606683e-01f, -1.401817782e-02f }, + { -5.928929157e-02f, +8.193135159e-01f, +2.564599271e-01f, -1.404123383e-02f }, + { -5.928259213e-02f, +8.190340052e-01f, +2.567593031e-01f, -1.406430953e-02f }, + { -5.927583113e-02f, +8.187543337e-01f, +2.570587960e-01f, -1.408740493e-02f }, + { -5.926900867e-02f, +8.184745017e-01f, +2.573584058e-01f, -1.411052002e-02f }, + { -5.926212483e-02f, +8.181945092e-01f, +2.576581325e-01f, -1.413365478e-02f }, + { -5.925517969e-02f, +8.179143566e-01f, +2.579579758e-01f, -1.415680921e-02f }, + { -5.924817333e-02f, +8.176340439e-01f, +2.582579358e-01f, -1.417998331e-02f }, + { -5.924110584e-02f, +8.173535714e-01f, +2.585580122e-01f, -1.420317705e-02f }, + { -5.923397729e-02f, +8.170729391e-01f, +2.588582051e-01f, -1.422639045e-02f }, + { -5.922678777e-02f, +8.167921473e-01f, +2.591585142e-01f, -1.424962348e-02f }, + { -5.921953735e-02f, +8.165111960e-01f, +2.594589396e-01f, -1.427287614e-02f }, + { -5.921222613e-02f, +8.162300856e-01f, +2.597594810e-01f, -1.429614843e-02f }, + { -5.920485418e-02f, +8.159488162e-01f, +2.600601385e-01f, -1.431944033e-02f }, + { -5.919742158e-02f, +8.156673878e-01f, +2.603609118e-01f, -1.434275184e-02f }, + { -5.918992842e-02f, +8.153858007e-01f, +2.606618010e-01f, -1.436608295e-02f }, + { -5.918237478e-02f, +8.151040551e-01f, +2.609628058e-01f, -1.438943365e-02f }, + { -5.917476074e-02f, +8.148221512e-01f, +2.612639263e-01f, -1.441280393e-02f }, + { -5.916708638e-02f, +8.145400890e-01f, +2.615651622e-01f, -1.443619379e-02f }, + { -5.915935179e-02f, +8.142578688e-01f, +2.618665135e-01f, -1.445960321e-02f }, + { -5.915155704e-02f, +8.139754907e-01f, +2.621679801e-01f, -1.448303220e-02f }, + { -5.914370221e-02f, +8.136929549e-01f, +2.624695618e-01f, -1.450648073e-02f }, + { -5.913578740e-02f, +8.134102615e-01f, +2.627712587e-01f, -1.452994881e-02f }, + { -5.912781267e-02f, +8.131274108e-01f, +2.630730705e-01f, -1.455343641e-02f }, + { -5.911977812e-02f, +8.128444029e-01f, +2.633749972e-01f, -1.457694355e-02f }, + { -5.911168382e-02f, +8.125612380e-01f, +2.636770386e-01f, -1.460047020e-02f }, + { -5.910352985e-02f, +8.122779162e-01f, +2.639791948e-01f, -1.462401636e-02f }, + { -5.909531630e-02f, +8.119944377e-01f, +2.642814655e-01f, -1.464758202e-02f }, + { -5.908704325e-02f, +8.117108027e-01f, +2.645838506e-01f, -1.467116717e-02f }, + { -5.907871078e-02f, +8.114270114e-01f, +2.648863501e-01f, -1.469477181e-02f }, + { -5.907031897e-02f, +8.111430638e-01f, +2.651889639e-01f, -1.471839591e-02f }, + { -5.906186790e-02f, +8.108589603e-01f, +2.654916918e-01f, -1.474203949e-02f }, + { -5.905335765e-02f, +8.105747008e-01f, +2.657945338e-01f, -1.476570252e-02f }, + { -5.904478831e-02f, +8.102902857e-01f, +2.660974897e-01f, -1.478938500e-02f }, + { -5.903615996e-02f, +8.100057151e-01f, +2.664005594e-01f, -1.481308692e-02f }, + { -5.902747268e-02f, +8.097209892e-01f, +2.667037429e-01f, -1.483680827e-02f }, + { -5.901872654e-02f, +8.094361081e-01f, +2.670070400e-01f, -1.486054904e-02f }, + { -5.900992164e-02f, +8.091510719e-01f, +2.673104506e-01f, -1.488430923e-02f }, + { -5.900105805e-02f, +8.088658810e-01f, +2.676139746e-01f, -1.490808882e-02f }, + { -5.899213585e-02f, +8.085805354e-01f, +2.679176120e-01f, -1.493188780e-02f }, + { -5.898315513e-02f, +8.082950352e-01f, +2.682213626e-01f, -1.495570618e-02f }, + { -5.897411596e-02f, +8.080093808e-01f, +2.685252263e-01f, -1.497954392e-02f }, + { -5.896501843e-02f, +8.077235722e-01f, +2.688292029e-01f, -1.500340104e-02f }, + { -5.895586262e-02f, +8.074376096e-01f, +2.691332925e-01f, -1.502727751e-02f }, + { -5.894664861e-02f, +8.071514932e-01f, +2.694374949e-01f, -1.505117334e-02f }, + { -5.893737648e-02f, +8.068652231e-01f, +2.697418099e-01f, -1.507508850e-02f }, + { -5.892804632e-02f, +8.065787996e-01f, +2.700462376e-01f, -1.509902300e-02f }, + { -5.891865819e-02f, +8.062922227e-01f, +2.703507777e-01f, -1.512297682e-02f }, + { -5.890921220e-02f, +8.060054928e-01f, +2.706554301e-01f, -1.514694995e-02f }, + { -5.889970841e-02f, +8.057186098e-01f, +2.709601949e-01f, -1.517094238e-02f }, + { -5.889014690e-02f, +8.054315741e-01f, +2.712650718e-01f, -1.519495411e-02f }, + { -5.888052777e-02f, +8.051443857e-01f, +2.715700607e-01f, -1.521898512e-02f }, + { -5.887085108e-02f, +8.048570449e-01f, +2.718751616e-01f, -1.524303541e-02f }, + { -5.886111692e-02f, +8.045695518e-01f, +2.721803743e-01f, -1.526710496e-02f }, + { -5.885132538e-02f, +8.042819066e-01f, +2.724856987e-01f, -1.529119377e-02f }, + { -5.884147653e-02f, +8.039941094e-01f, +2.727911348e-01f, -1.531530183e-02f }, + { -5.883157045e-02f, +8.037061604e-01f, +2.730966824e-01f, -1.533942912e-02f }, + { -5.882160722e-02f, +8.034180599e-01f, +2.734023414e-01f, -1.536357563e-02f }, + { -5.881158693e-02f, +8.031298079e-01f, +2.737081117e-01f, -1.538774137e-02f }, + { -5.880150966e-02f, +8.028414047e-01f, +2.740139932e-01f, -1.541192631e-02f }, + { -5.879137549e-02f, +8.025528503e-01f, +2.743199858e-01f, -1.543613045e-02f }, + { -5.878118449e-02f, +8.022641451e-01f, +2.746260894e-01f, -1.546035377e-02f }, + { -5.877093675e-02f, +8.019752891e-01f, +2.749323038e-01f, -1.548459628e-02f }, + { -5.876063235e-02f, +8.016862825e-01f, +2.752386291e-01f, -1.550885795e-02f }, + { -5.875027137e-02f, +8.013971255e-01f, +2.755450649e-01f, -1.553313878e-02f }, + { -5.873985390e-02f, +8.011078183e-01f, +2.758516114e-01f, -1.555743875e-02f }, + { -5.872938000e-02f, +8.008183610e-01f, +2.761582682e-01f, -1.558175787e-02f }, + { -5.871884977e-02f, +8.005287538e-01f, +2.764650354e-01f, -1.560609611e-02f }, + { -5.870826329e-02f, +8.002389969e-01f, +2.767719129e-01f, -1.563045347e-02f }, + { -5.869762062e-02f, +7.999490904e-01f, +2.770789004e-01f, -1.565482993e-02f }, + { -5.868692187e-02f, +7.996590346e-01f, +2.773859980e-01f, -1.567922549e-02f }, + { -5.867616710e-02f, +7.993688295e-01f, +2.776932054e-01f, -1.570364014e-02f }, + { -5.866535639e-02f, +7.990784754e-01f, +2.780005227e-01f, -1.572807386e-02f }, + { -5.865448983e-02f, +7.987879725e-01f, +2.783079496e-01f, -1.575252665e-02f }, + { -5.864356750e-02f, +7.984973209e-01f, +2.786154861e-01f, -1.577699850e-02f }, + { -5.863258948e-02f, +7.982065207e-01f, +2.789231321e-01f, -1.580148939e-02f }, + { -5.862155585e-02f, +7.979155722e-01f, +2.792308874e-01f, -1.582599931e-02f }, + { -5.861046669e-02f, +7.976244755e-01f, +2.795387520e-01f, -1.585052826e-02f }, + { -5.859932207e-02f, +7.973332308e-01f, +2.798467258e-01f, -1.587507622e-02f }, + { -5.858812209e-02f, +7.970418383e-01f, +2.801548085e-01f, -1.589964318e-02f }, + { -5.857686682e-02f, +7.967502981e-01f, +2.804630002e-01f, -1.592422914e-02f }, + { -5.856555634e-02f, +7.964586105e-01f, +2.807713007e-01f, -1.594883407e-02f }, + { -5.855419074e-02f, +7.961667755e-01f, +2.810797098e-01f, -1.597345798e-02f }, + { -5.854277008e-02f, +7.958747933e-01f, +2.813882276e-01f, -1.599810084e-02f }, + { -5.853129446e-02f, +7.955826642e-01f, +2.816968539e-01f, -1.602276266e-02f }, + { -5.851976395e-02f, +7.952903884e-01f, +2.820055885e-01f, -1.604744341e-02f }, + { -5.850817864e-02f, +7.949979658e-01f, +2.823144314e-01f, -1.607214309e-02f }, + { -5.849653859e-02f, +7.947053969e-01f, +2.826233824e-01f, -1.609686168e-02f }, + { -5.848484391e-02f, +7.944126816e-01f, +2.829324415e-01f, -1.612159918e-02f }, + { -5.847309465e-02f, +7.941198202e-01f, +2.832416085e-01f, -1.614635558e-02f }, + { -5.846129092e-02f, +7.938268129e-01f, +2.835508833e-01f, -1.617113085e-02f }, + { -5.844943277e-02f, +7.935336599e-01f, +2.838602658e-01f, -1.619592500e-02f }, + { -5.843752031e-02f, +7.932403612e-01f, +2.841697559e-01f, -1.622073801e-02f }, + { -5.842555359e-02f, +7.929469172e-01f, +2.844793535e-01f, -1.624556986e-02f }, + { -5.841353272e-02f, +7.926533279e-01f, +2.847890584e-01f, -1.627042056e-02f }, + { -5.840145776e-02f, +7.923595935e-01f, +2.850988707e-01f, -1.629529008e-02f }, + { -5.838932879e-02f, +7.920657142e-01f, +2.854087900e-01f, -1.632017842e-02f }, + { -5.837714590e-02f, +7.917716902e-01f, +2.857188164e-01f, -1.634508556e-02f }, + { -5.836490917e-02f, +7.914775217e-01f, +2.860289498e-01f, -1.637001149e-02f }, + { -5.835261867e-02f, +7.911832088e-01f, +2.863391899e-01f, -1.639495620e-02f }, + { -5.834027449e-02f, +7.908887516e-01f, +2.866495368e-01f, -1.641991969e-02f }, + { -5.832787671e-02f, +7.905941505e-01f, +2.869599902e-01f, -1.644490193e-02f }, + { -5.831542540e-02f, +7.902994055e-01f, +2.872705501e-01f, -1.646990291e-02f }, + { -5.830292065e-02f, +7.900045168e-01f, +2.875812164e-01f, -1.649492264e-02f }, + { -5.829036253e-02f, +7.897094846e-01f, +2.878919890e-01f, -1.651996108e-02f }, + { -5.827775113e-02f, +7.894143091e-01f, +2.882028676e-01f, -1.654501824e-02f }, + { -5.826508653e-02f, +7.891189904e-01f, +2.885138523e-01f, -1.657009409e-02f }, + { -5.825236880e-02f, +7.888235287e-01f, +2.888249429e-01f, -1.659518863e-02f }, + { -5.823959802e-02f, +7.885279242e-01f, +2.891361394e-01f, -1.662030185e-02f }, + { -5.822677428e-02f, +7.882321771e-01f, +2.894474415e-01f, -1.664543373e-02f }, + { -5.821389766e-02f, +7.879362875e-01f, +2.897588491e-01f, -1.667058426e-02f }, + { -5.820096823e-02f, +7.876402556e-01f, +2.900703623e-01f, -1.669575344e-02f }, + { -5.818798608e-02f, +7.873440816e-01f, +2.903819808e-01f, -1.672094124e-02f }, + { -5.817495128e-02f, +7.870477656e-01f, +2.906937045e-01f, -1.674614765e-02f }, + { -5.816186392e-02f, +7.867513079e-01f, +2.910055333e-01f, -1.677137267e-02f }, + { -5.814872407e-02f, +7.864547086e-01f, +2.913174672e-01f, -1.679661628e-02f }, + { -5.813553181e-02f, +7.861579679e-01f, +2.916295059e-01f, -1.682187847e-02f }, + { -5.812228722e-02f, +7.858610859e-01f, +2.919416495e-01f, -1.684715923e-02f }, + { -5.810899039e-02f, +7.855640629e-01f, +2.922538976e-01f, -1.687245854e-02f }, + { -5.809564139e-02f, +7.852668990e-01f, +2.925662504e-01f, -1.689777639e-02f }, + { -5.808224030e-02f, +7.849695943e-01f, +2.928787076e-01f, -1.692311278e-02f }, + { -5.806878721e-02f, +7.846721491e-01f, +2.931912691e-01f, -1.694846768e-02f }, + { -5.805528218e-02f, +7.843745636e-01f, +2.935039348e-01f, -1.697384108e-02f }, + { -5.804172530e-02f, +7.840768378e-01f, +2.938167045e-01f, -1.699923298e-02f }, + { -5.802811666e-02f, +7.837789720e-01f, +2.941295783e-01f, -1.702464336e-02f }, + { -5.801445632e-02f, +7.834809664e-01f, +2.944425560e-01f, -1.705007220e-02f }, + { -5.800074437e-02f, +7.831828211e-01f, +2.947556373e-01f, -1.707551950e-02f }, + { -5.798698089e-02f, +7.828845363e-01f, +2.950688223e-01f, -1.710098524e-02f }, + { -5.797316596e-02f, +7.825861121e-01f, +2.953821109e-01f, -1.712646941e-02f }, + { -5.795929965e-02f, +7.822875489e-01f, +2.956955028e-01f, -1.715197200e-02f }, + { -5.794538205e-02f, +7.819888466e-01f, +2.960089980e-01f, -1.717749299e-02f }, + { -5.793141324e-02f, +7.816900056e-01f, +2.963225964e-01f, -1.720303237e-02f }, + { -5.791739329e-02f, +7.813910260e-01f, +2.966362979e-01f, -1.722859012e-02f }, + { -5.790332228e-02f, +7.810919079e-01f, +2.969501022e-01f, -1.725416625e-02f }, + { -5.788920029e-02f, +7.807926515e-01f, +2.972640094e-01f, -1.727976072e-02f }, + { -5.787502741e-02f, +7.804932570e-01f, +2.975780194e-01f, -1.730537353e-02f }, + { -5.786080371e-02f, +7.801937247e-01f, +2.978921319e-01f, -1.733100467e-02f }, + { -5.784652927e-02f, +7.798940546e-01f, +2.982063468e-01f, -1.735665412e-02f }, + { -5.783220417e-02f, +7.795942469e-01f, +2.985206642e-01f, -1.738232187e-02f }, + { -5.781782849e-02f, +7.792943018e-01f, +2.988350837e-01f, -1.740800791e-02f }, + { -5.780340230e-02f, +7.789942195e-01f, +2.991496054e-01f, -1.743371222e-02f }, + { -5.778892569e-02f, +7.786940002e-01f, +2.994642291e-01f, -1.745943479e-02f }, + { -5.777439874e-02f, +7.783936440e-01f, +2.997789547e-01f, -1.748517560e-02f }, + { -5.775982152e-02f, +7.780931511e-01f, +3.000937821e-01f, -1.751093465e-02f }, + { -5.774519411e-02f, +7.777925218e-01f, +3.004087111e-01f, -1.753671192e-02f }, + { -5.773051660e-02f, +7.774917560e-01f, +3.007237417e-01f, -1.756250739e-02f }, + { -5.771578906e-02f, +7.771908542e-01f, +3.010388736e-01f, -1.758832105e-02f }, + { -5.770101157e-02f, +7.768898163e-01f, +3.013541069e-01f, -1.761415290e-02f }, + { -5.768618420e-02f, +7.765886426e-01f, +3.016694414e-01f, -1.764000291e-02f }, + { -5.767130705e-02f, +7.762873333e-01f, +3.019848769e-01f, -1.766587107e-02f }, + { -5.765638018e-02f, +7.759858886e-01f, +3.023004134e-01f, -1.769175736e-02f }, + { -5.764140368e-02f, +7.756843086e-01f, +3.026160507e-01f, -1.771766179e-02f }, + { -5.762637762e-02f, +7.753825935e-01f, +3.029317887e-01f, -1.774358432e-02f }, + { -5.761130208e-02f, +7.750807434e-01f, +3.032476274e-01f, -1.776952495e-02f }, + { -5.759617715e-02f, +7.747787586e-01f, +3.035635665e-01f, -1.779548366e-02f }, + { -5.758100289e-02f, +7.744766393e-01f, +3.038796059e-01f, -1.782146044e-02f }, + { -5.756577939e-02f, +7.741743855e-01f, +3.041957456e-01f, -1.784745527e-02f }, + { -5.755050673e-02f, +7.738719975e-01f, +3.045119855e-01f, -1.787346814e-02f }, + { -5.753518499e-02f, +7.735694755e-01f, +3.048283253e-01f, -1.789949905e-02f }, + { -5.751981424e-02f, +7.732668197e-01f, +3.051447650e-01f, -1.792554796e-02f }, + { -5.750439457e-02f, +7.729640301e-01f, +3.054613045e-01f, -1.795161487e-02f }, + { -5.748892604e-02f, +7.726611070e-01f, +3.057779436e-01f, -1.797769977e-02f }, + { -5.747340875e-02f, +7.723580506e-01f, +3.060946822e-01f, -1.800380263e-02f }, + { -5.745784276e-02f, +7.720548611e-01f, +3.064115203e-01f, -1.802992345e-02f }, + { -5.744222816e-02f, +7.717515386e-01f, +3.067284576e-01f, -1.805606221e-02f }, + { -5.742656503e-02f, +7.714480832e-01f, +3.070454941e-01f, -1.808221890e-02f }, + { -5.741085343e-02f, +7.711444953e-01f, +3.073626297e-01f, -1.810839350e-02f }, + { -5.739509346e-02f, +7.708407749e-01f, +3.076798642e-01f, -1.813458600e-02f }, + { -5.737928519e-02f, +7.705369222e-01f, +3.079971975e-01f, -1.816079639e-02f }, + { -5.736342870e-02f, +7.702329374e-01f, +3.083146294e-01f, -1.818702464e-02f }, + { -5.734752407e-02f, +7.699288208e-01f, +3.086321600e-01f, -1.821327074e-02f }, + { -5.733157137e-02f, +7.696245724e-01f, +3.089497890e-01f, -1.823953469e-02f }, + { -5.731557068e-02f, +7.693201924e-01f, +3.092675163e-01f, -1.826581646e-02f }, + { -5.729952209e-02f, +7.690156811e-01f, +3.095853418e-01f, -1.829211604e-02f }, + { -5.728342566e-02f, +7.687110385e-01f, +3.099032654e-01f, -1.831843341e-02f }, + { -5.726728148e-02f, +7.684062649e-01f, +3.102212870e-01f, -1.834476857e-02f }, + { -5.725108962e-02f, +7.681013605e-01f, +3.105394064e-01f, -1.837112149e-02f }, + { -5.723485017e-02f, +7.677963254e-01f, +3.108576236e-01f, -1.839749217e-02f }, + { -5.721856321e-02f, +7.674911598e-01f, +3.111759383e-01f, -1.842388058e-02f }, + { -5.720222880e-02f, +7.671858639e-01f, +3.114943506e-01f, -1.845028671e-02f }, + { -5.718584702e-02f, +7.668804379e-01f, +3.118128601e-01f, -1.847671054e-02f }, + { -5.716941797e-02f, +7.665748819e-01f, +3.121314670e-01f, -1.850315207e-02f }, + { -5.715294170e-02f, +7.662691962e-01f, +3.124501709e-01f, -1.852961127e-02f }, + { -5.713641831e-02f, +7.659633808e-01f, +3.127689718e-01f, -1.855608813e-02f }, + { -5.711984787e-02f, +7.656574360e-01f, +3.130878696e-01f, -1.858258264e-02f }, + { -5.710323045e-02f, +7.653513620e-01f, +3.134068642e-01f, -1.860909478e-02f }, + { -5.708656614e-02f, +7.650451589e-01f, +3.137259554e-01f, -1.863562453e-02f }, + { -5.706985501e-02f, +7.647388269e-01f, +3.140451431e-01f, -1.866217188e-02f }, + { -5.705309714e-02f, +7.644323662e-01f, +3.143644272e-01f, -1.868873682e-02f }, + { -5.703629260e-02f, +7.641257769e-01f, +3.146838075e-01f, -1.871531932e-02f }, + { -5.701944149e-02f, +7.638190593e-01f, +3.150032840e-01f, -1.874191938e-02f }, + { -5.700254386e-02f, +7.635122136e-01f, +3.153228565e-01f, -1.876853697e-02f }, + { -5.698559981e-02f, +7.632052398e-01f, +3.156425249e-01f, -1.879517209e-02f }, + { -5.696860941e-02f, +7.628981382e-01f, +3.159622890e-01f, -1.882182471e-02f }, + { -5.695157273e-02f, +7.625909090e-01f, +3.162821488e-01f, -1.884849482e-02f }, + { -5.693448985e-02f, +7.622835524e-01f, +3.166021042e-01f, -1.887518241e-02f }, + { -5.691736086e-02f, +7.619760684e-01f, +3.169221549e-01f, -1.890188745e-02f }, + { -5.690018582e-02f, +7.616684574e-01f, +3.172423009e-01f, -1.892860994e-02f }, + { -5.688296482e-02f, +7.613607194e-01f, +3.175625421e-01f, -1.895534986e-02f }, + { -5.686569793e-02f, +7.610528546e-01f, +3.178828783e-01f, -1.898210719e-02f }, + { -5.684838523e-02f, +7.607448634e-01f, +3.182033094e-01f, -1.900888191e-02f }, + { -5.683102681e-02f, +7.604367457e-01f, +3.185238353e-01f, -1.903567401e-02f }, + { -5.681362272e-02f, +7.601285018e-01f, +3.188444558e-01f, -1.906248348e-02f }, + { -5.679617306e-02f, +7.598201319e-01f, +3.191651709e-01f, -1.908931029e-02f }, + { -5.677867790e-02f, +7.595116361e-01f, +3.194859804e-01f, -1.911615443e-02f }, + { -5.676113731e-02f, +7.592030147e-01f, +3.198068842e-01f, -1.914301589e-02f }, + { -5.674355138e-02f, +7.588942678e-01f, +3.201278822e-01f, -1.916989465e-02f }, + { -5.672592019e-02f, +7.585853955e-01f, +3.204489742e-01f, -1.919679069e-02f }, + { -5.670824380e-02f, +7.582763982e-01f, +3.207701601e-01f, -1.922370400e-02f }, + { -5.669052229e-02f, +7.579672759e-01f, +3.210914398e-01f, -1.925063455e-02f }, + { -5.667275575e-02f, +7.576580288e-01f, +3.214128132e-01f, -1.927758234e-02f }, + { -5.665494425e-02f, +7.573486571e-01f, +3.217342801e-01f, -1.930454735e-02f }, + { -5.663708786e-02f, +7.570391610e-01f, +3.220558404e-01f, -1.933152956e-02f }, + { -5.661918667e-02f, +7.567295407e-01f, +3.223774940e-01f, -1.935852895e-02f }, + { -5.660124075e-02f, +7.564197963e-01f, +3.226992408e-01f, -1.938554550e-02f }, + { -5.658325018e-02f, +7.561099280e-01f, +3.230210806e-01f, -1.941257921e-02f }, + { -5.656521503e-02f, +7.557999361e-01f, +3.233430133e-01f, -1.943963005e-02f }, + { -5.654713538e-02f, +7.554898206e-01f, +3.236650389e-01f, -1.946669801e-02f }, + { -5.652901131e-02f, +7.551795818e-01f, +3.239871571e-01f, -1.949378307e-02f }, + { -5.651084290e-02f, +7.548692198e-01f, +3.243093678e-01f, -1.952088522e-02f }, + { -5.649263022e-02f, +7.545587349e-01f, +3.246316709e-01f, -1.954800443e-02f }, + { -5.647437335e-02f, +7.542481271e-01f, +3.249540663e-01f, -1.957514069e-02f }, + { -5.645607236e-02f, +7.539373968e-01f, +3.252765539e-01f, -1.960229398e-02f }, + { -5.643772734e-02f, +7.536265440e-01f, +3.255991335e-01f, -1.962946429e-02f }, + { -5.641933835e-02f, +7.533155690e-01f, +3.259218051e-01f, -1.965665160e-02f }, + { -5.640090548e-02f, +7.530044718e-01f, +3.262445684e-01f, -1.968385589e-02f }, + { -5.638242880e-02f, +7.526932528e-01f, +3.265674233e-01f, -1.971107714e-02f }, + { -5.636390839e-02f, +7.523819121e-01f, +3.268903698e-01f, -1.973831534e-02f }, + { -5.634534432e-02f, +7.520704498e-01f, +3.272134077e-01f, -1.976557047e-02f }, + { -5.632673668e-02f, +7.517588662e-01f, +3.275365369e-01f, -1.979284251e-02f }, + { -5.630808553e-02f, +7.514471614e-01f, +3.278597572e-01f, -1.982013145e-02f }, + { -5.628939096e-02f, +7.511353356e-01f, +3.281830685e-01f, -1.984743727e-02f }, + { -5.627065304e-02f, +7.508233890e-01f, +3.285064707e-01f, -1.987475995e-02f }, + { -5.625187185e-02f, +7.505113218e-01f, +3.288299637e-01f, -1.990209947e-02f }, + { -5.623304746e-02f, +7.501991341e-01f, +3.291535473e-01f, -1.992945582e-02f }, + { -5.621417995e-02f, +7.498868261e-01f, +3.294772215e-01f, -1.995682897e-02f }, + { -5.619526939e-02f, +7.495743981e-01f, +3.298009860e-01f, -1.998421892e-02f }, + { -5.617631587e-02f, +7.492618501e-01f, +3.301248407e-01f, -2.001162564e-02f }, + { -5.615731946e-02f, +7.489491824e-01f, +3.304487856e-01f, -2.003904911e-02f }, + { -5.613828023e-02f, +7.486363951e-01f, +3.307728205e-01f, -2.006648932e-02f }, + { -5.611919827e-02f, +7.483234885e-01f, +3.310969453e-01f, -2.009394625e-02f }, + { -5.610007364e-02f, +7.480104627e-01f, +3.314211598e-01f, -2.012141988e-02f }, + { -5.608090642e-02f, +7.476973179e-01f, +3.317454639e-01f, -2.014891020e-02f }, + { -5.606169669e-02f, +7.473840543e-01f, +3.320698576e-01f, -2.017641718e-02f }, + { -5.604244453e-02f, +7.470706720e-01f, +3.323943405e-01f, -2.020394081e-02f }, + { -5.602315001e-02f, +7.467571712e-01f, +3.327189127e-01f, -2.023148107e-02f }, + { -5.600381321e-02f, +7.464435522e-01f, +3.330435740e-01f, -2.025903794e-02f }, + { -5.598443420e-02f, +7.461298151e-01f, +3.333683243e-01f, -2.028661141e-02f }, + { -5.596501306e-02f, +7.458159600e-01f, +3.336931634e-01f, -2.031420145e-02f }, + { -5.594554986e-02f, +7.455019872e-01f, +3.340180912e-01f, -2.034180804e-02f }, + { -5.592604469e-02f, +7.451878969e-01f, +3.343431076e-01f, -2.036943118e-02f }, + { -5.590649761e-02f, +7.448736891e-01f, +3.346682125e-01f, -2.039707084e-02f }, + { -5.588690871e-02f, +7.445593642e-01f, +3.349934057e-01f, -2.042472700e-02f }, + { -5.586727805e-02f, +7.442449222e-01f, +3.353186871e-01f, -2.045239964e-02f }, + { -5.584760571e-02f, +7.439303635e-01f, +3.356440565e-01f, -2.048008875e-02f }, + { -5.582789178e-02f, +7.436156880e-01f, +3.359695139e-01f, -2.050779431e-02f }, + { -5.580813632e-02f, +7.433008961e-01f, +3.362950591e-01f, -2.053551629e-02f }, + { -5.578833941e-02f, +7.429859879e-01f, +3.366206920e-01f, -2.056325469e-02f }, + { -5.576850113e-02f, +7.426709636e-01f, +3.369464124e-01f, -2.059100947e-02f }, + { -5.574862155e-02f, +7.423558234e-01f, +3.372722202e-01f, -2.061878063e-02f }, + { -5.572870075e-02f, +7.420405674e-01f, +3.375981153e-01f, -2.064656814e-02f }, + { -5.570873880e-02f, +7.417251959e-01f, +3.379240976e-01f, -2.067437199e-02f }, + { -5.568873578e-02f, +7.414097090e-01f, +3.382501669e-01f, -2.070219216e-02f }, + { -5.566869176e-02f, +7.410941069e-01f, +3.385763231e-01f, -2.073002862e-02f }, + { -5.564860682e-02f, +7.407783897e-01f, +3.389025660e-01f, -2.075788136e-02f }, + { -5.562848104e-02f, +7.404625577e-01f, +3.392288956e-01f, -2.078575035e-02f }, + { -5.560831448e-02f, +7.401466111e-01f, +3.395553117e-01f, -2.081363559e-02f }, + { -5.558810723e-02f, +7.398305500e-01f, +3.398818142e-01f, -2.084153705e-02f }, + { -5.556785936e-02f, +7.395143746e-01f, +3.402084029e-01f, -2.086945472e-02f }, + { -5.554757095e-02f, +7.391980851e-01f, +3.405350777e-01f, -2.089738856e-02f }, + { -5.552724207e-02f, +7.388816817e-01f, +3.408618385e-01f, -2.092533857e-02f }, + { -5.550687279e-02f, +7.385651645e-01f, +3.411886851e-01f, -2.095330472e-02f }, + { -5.548646320e-02f, +7.382485338e-01f, +3.415156174e-01f, -2.098128700e-02f }, + { -5.546601336e-02f, +7.379317896e-01f, +3.418426354e-01f, -2.100928539e-02f }, + { -5.544552335e-02f, +7.376149323e-01f, +3.421697387e-01f, -2.103729986e-02f }, + { -5.542499324e-02f, +7.372979620e-01f, +3.424969274e-01f, -2.106533040e-02f }, + { -5.540442312e-02f, +7.369808788e-01f, +3.428242013e-01f, -2.109337699e-02f }, + { -5.538381306e-02f, +7.366636830e-01f, +3.431515602e-01f, -2.112143960e-02f }, + { -5.536316312e-02f, +7.363463747e-01f, +3.434790041e-01f, -2.114951822e-02f }, + { -5.534247339e-02f, +7.360289541e-01f, +3.438065327e-01f, -2.117761283e-02f }, + { -5.532174395e-02f, +7.357114214e-01f, +3.441341461e-01f, -2.120572342e-02f }, + { -5.530097485e-02f, +7.353937768e-01f, +3.444618439e-01f, -2.123384995e-02f }, + { -5.528016619e-02f, +7.350760204e-01f, +3.447896261e-01f, -2.126199241e-02f }, + { -5.525931803e-02f, +7.347581525e-01f, +3.451174926e-01f, -2.129015078e-02f }, + { -5.523843046e-02f, +7.344401732e-01f, +3.454454432e-01f, -2.131832504e-02f }, + { -5.521750353e-02f, +7.341220827e-01f, +3.457734778e-01f, -2.134651517e-02f }, + { -5.519653734e-02f, +7.338038812e-01f, +3.461015962e-01f, -2.137472115e-02f }, + { -5.517553195e-02f, +7.334855689e-01f, +3.464297984e-01f, -2.140294296e-02f }, + { -5.515448744e-02f, +7.331671459e-01f, +3.467580842e-01f, -2.143118058e-02f }, + { -5.513340388e-02f, +7.328486125e-01f, +3.470864534e-01f, -2.145943399e-02f }, + { -5.511228134e-02f, +7.325299688e-01f, +3.474149060e-01f, -2.148770317e-02f }, + { -5.509111991e-02f, +7.322112150e-01f, +3.477434418e-01f, -2.151598810e-02f }, + { -5.506991966e-02f, +7.318923513e-01f, +3.480720606e-01f, -2.154428875e-02f }, + { -5.504868065e-02f, +7.315733778e-01f, +3.484007623e-01f, -2.157260512e-02f }, + { -5.502740298e-02f, +7.312542948e-01f, +3.487295469e-01f, -2.160093718e-02f }, + { -5.500608670e-02f, +7.309351024e-01f, +3.490584141e-01f, -2.162928490e-02f }, + { -5.498473189e-02f, +7.306158008e-01f, +3.493873638e-01f, -2.165764827e-02f }, + { -5.496333863e-02f, +7.302963903e-01f, +3.497163959e-01f, -2.168602727e-02f }, + { -5.494190699e-02f, +7.299768709e-01f, +3.500455102e-01f, -2.171442188e-02f }, + { -5.492043705e-02f, +7.296572429e-01f, +3.503747067e-01f, -2.174283207e-02f }, + { -5.489892888e-02f, +7.293375064e-01f, +3.507039852e-01f, -2.177125783e-02f }, + { -5.487738256e-02f, +7.290176616e-01f, +3.510333455e-01f, -2.179969913e-02f }, + { -5.485579815e-02f, +7.286977088e-01f, +3.513627875e-01f, -2.182815596e-02f }, + { -5.483417574e-02f, +7.283776481e-01f, +3.516923111e-01f, -2.185662829e-02f }, + { -5.481251539e-02f, +7.280574796e-01f, +3.520219162e-01f, -2.188511610e-02f }, + { -5.479081718e-02f, +7.277372036e-01f, +3.523516026e-01f, -2.191361938e-02f }, + { -5.476908119e-02f, +7.274168203e-01f, +3.526813701e-01f, -2.194213809e-02f }, + { -5.474730748e-02f, +7.270963298e-01f, +3.530112187e-01f, -2.197067223e-02f }, + { -5.472549614e-02f, +7.267757323e-01f, +3.533411482e-01f, -2.199922176e-02f }, + { -5.470364724e-02f, +7.264550280e-01f, +3.536711584e-01f, -2.202778667e-02f }, + { -5.468176084e-02f, +7.261342170e-01f, +3.540012493e-01f, -2.205636694e-02f }, + { -5.465983703e-02f, +7.258132997e-01f, +3.543314207e-01f, -2.208496254e-02f }, + { -5.463787587e-02f, +7.254922761e-01f, +3.546616725e-01f, -2.211357346e-02f }, + { -5.461587745e-02f, +7.251711464e-01f, +3.549920045e-01f, -2.214219967e-02f }, + { -5.459384183e-02f, +7.248499108e-01f, +3.553224165e-01f, -2.217084115e-02f }, + { -5.457176909e-02f, +7.245285695e-01f, +3.556529085e-01f, -2.219949788e-02f }, + { -5.454965931e-02f, +7.242071227e-01f, +3.559834804e-01f, -2.222816984e-02f }, + { -5.452751254e-02f, +7.238855706e-01f, +3.563141319e-01f, -2.225685701e-02f }, + { -5.450532888e-02f, +7.235639133e-01f, +3.566448630e-01f, -2.228555936e-02f }, + { -5.448310839e-02f, +7.232421510e-01f, +3.569756734e-01f, -2.231427688e-02f }, + { -5.446085115e-02f, +7.229202840e-01f, +3.573065632e-01f, -2.234300954e-02f }, + { -5.443855722e-02f, +7.225983123e-01f, +3.576375321e-01f, -2.237175733e-02f }, + { -5.441622669e-02f, +7.222762363e-01f, +3.579685799e-01f, -2.240052021e-02f }, + { -5.439385962e-02f, +7.219540560e-01f, +3.582997067e-01f, -2.242929816e-02f }, + { -5.437145610e-02f, +7.216317716e-01f, +3.586309121e-01f, -2.245809118e-02f }, + { -5.434901618e-02f, +7.213093834e-01f, +3.589621962e-01f, -2.248689923e-02f }, + { -5.432653996e-02f, +7.209868915e-01f, +3.592935587e-01f, -2.251572229e-02f }, + { -5.430402749e-02f, +7.206642961e-01f, +3.596249995e-01f, -2.254456034e-02f }, + { -5.428147885e-02f, +7.203415974e-01f, +3.599565185e-01f, -2.257341336e-02f }, + { -5.425889412e-02f, +7.200187955e-01f, +3.602881155e-01f, -2.260228132e-02f }, + { -5.423627337e-02f, +7.196958907e-01f, +3.606197904e-01f, -2.263116421e-02f }, + { -5.421361667e-02f, +7.193728831e-01f, +3.609515431e-01f, -2.266006200e-02f }, + { -5.419092410e-02f, +7.190497730e-01f, +3.612833734e-01f, -2.268897467e-02f }, + { -5.416819572e-02f, +7.187265605e-01f, +3.616152812e-01f, -2.271790220e-02f }, + { -5.414543161e-02f, +7.184032457e-01f, +3.619472663e-01f, -2.274684456e-02f }, + { -5.412263185e-02f, +7.180798289e-01f, +3.622793287e-01f, -2.277580174e-02f }, + { -5.409979650e-02f, +7.177563103e-01f, +3.626114681e-01f, -2.280477371e-02f }, + { -5.407692564e-02f, +7.174326900e-01f, +3.629436844e-01f, -2.283376044e-02f }, + { -5.405401935e-02f, +7.171089682e-01f, +3.632759775e-01f, -2.286276192e-02f }, + { -5.403107768e-02f, +7.167851452e-01f, +3.636083473e-01f, -2.289177812e-02f }, + { -5.400810073e-02f, +7.164612210e-01f, +3.639407936e-01f, -2.292080903e-02f }, + { -5.398508856e-02f, +7.161371959e-01f, +3.642733163e-01f, -2.294985461e-02f }, + { -5.396204124e-02f, +7.158130701e-01f, +3.646059152e-01f, -2.297891485e-02f }, + { -5.393895884e-02f, +7.154888437e-01f, +3.649385903e-01f, -2.300798972e-02f }, + { -5.391584144e-02f, +7.151645170e-01f, +3.652713412e-01f, -2.303707919e-02f }, + { -5.389268911e-02f, +7.148400900e-01f, +3.656041680e-01f, -2.306618326e-02f }, + { -5.386950193e-02f, +7.145155631e-01f, +3.659370705e-01f, -2.309530189e-02f }, + { -5.384627996e-02f, +7.141909364e-01f, +3.662700485e-01f, -2.312443506e-02f }, + { -5.382302328e-02f, +7.138662100e-01f, +3.666031020e-01f, -2.315358275e-02f }, + { -5.379973196e-02f, +7.135413841e-01f, +3.669362306e-01f, -2.318274493e-02f }, + { -5.377640607e-02f, +7.132164590e-01f, +3.672694344e-01f, -2.321192159e-02f }, + { -5.375304569e-02f, +7.128914348e-01f, +3.676027132e-01f, -2.324111270e-02f }, + { -5.372965088e-02f, +7.125663118e-01f, +3.679360669e-01f, -2.327031823e-02f }, + { -5.370622173e-02f, +7.122410900e-01f, +3.682694952e-01f, -2.329953816e-02f }, + { -5.368275829e-02f, +7.119157697e-01f, +3.686029981e-01f, -2.332877247e-02f }, + { -5.365926065e-02f, +7.115903510e-01f, +3.689365754e-01f, -2.335802114e-02f }, + { -5.363572888e-02f, +7.112648342e-01f, +3.692702270e-01f, -2.338728414e-02f }, + { -5.361216304e-02f, +7.109392194e-01f, +3.696039527e-01f, -2.341656145e-02f }, + { -5.358856321e-02f, +7.106135068e-01f, +3.699377525e-01f, -2.344585305e-02f }, + { -5.356492947e-02f, +7.102876966e-01f, +3.702716261e-01f, -2.347515891e-02f }, + { -5.354126188e-02f, +7.099617891e-01f, +3.706055734e-01f, -2.350447901e-02f }, + { -5.351756051e-02f, +7.096357842e-01f, +3.709395943e-01f, -2.353381332e-02f }, + { -5.349382545e-02f, +7.093096823e-01f, +3.712736886e-01f, -2.356316182e-02f }, + { -5.347005675e-02f, +7.089834836e-01f, +3.716078562e-01f, -2.359252450e-02f }, + { -5.344625449e-02f, +7.086571882e-01f, +3.719420970e-01f, -2.362190131e-02f }, + { -5.342241875e-02f, +7.083307963e-01f, +3.722764108e-01f, -2.365129225e-02f }, + { -5.339854959e-02f, +7.080043080e-01f, +3.726107975e-01f, -2.368069728e-02f }, + { -5.337464709e-02f, +7.076777237e-01f, +3.729452569e-01f, -2.371011639e-02f }, + { -5.335071132e-02f, +7.073510434e-01f, +3.732797888e-01f, -2.373954954e-02f }, + { -5.332674235e-02f, +7.070242673e-01f, +3.736143933e-01f, -2.376899672e-02f }, + { -5.330274025e-02f, +7.066973957e-01f, +3.739490700e-01f, -2.379845790e-02f }, + { -5.327870509e-02f, +7.063704287e-01f, +3.742838189e-01f, -2.382793305e-02f }, + { -5.325463695e-02f, +7.060433665e-01f, +3.746186398e-01f, -2.385742216e-02f }, + { -5.323053590e-02f, +7.057162093e-01f, +3.749535326e-01f, -2.388692520e-02f }, + { -5.320640200e-02f, +7.053889572e-01f, +3.752884971e-01f, -2.391644214e-02f }, + { -5.318223533e-02f, +7.050616105e-01f, +3.756235333e-01f, -2.394597296e-02f }, + { -5.315803596e-02f, +7.047341694e-01f, +3.759586408e-01f, -2.397551763e-02f }, + { -5.313380397e-02f, +7.044066339e-01f, +3.762938197e-01f, -2.400507614e-02f }, + { -5.310953941e-02f, +7.040790044e-01f, +3.766290698e-01f, -2.403464845e-02f }, + { -5.308524237e-02f, +7.037512810e-01f, +3.769643909e-01f, -2.406423454e-02f }, + { -5.306091292e-02f, +7.034234638e-01f, +3.772997828e-01f, -2.409383439e-02f }, + { -5.303655112e-02f, +7.030955531e-01f, +3.776352455e-01f, -2.412344797e-02f }, + { -5.301215705e-02f, +7.027675491e-01f, +3.779707788e-01f, -2.415307527e-02f }, + { -5.298773078e-02f, +7.024394519e-01f, +3.783063825e-01f, -2.418271624e-02f }, + { -5.296327238e-02f, +7.021112617e-01f, +3.786420566e-01f, -2.421237087e-02f }, + { -5.293878193e-02f, +7.017829788e-01f, +3.789778008e-01f, -2.424203914e-02f }, + { -5.291425948e-02f, +7.014546032e-01f, +3.793136151e-01f, -2.427172102e-02f }, + { -5.288970511e-02f, +7.011261352e-01f, +3.796494992e-01f, -2.430141648e-02f }, + { -5.286511890e-02f, +7.007975749e-01f, +3.799854530e-01f, -2.433112550e-02f }, + { -5.284050092e-02f, +7.004689226e-01f, +3.803214765e-01f, -2.436084805e-02f }, + { -5.281585123e-02f, +7.001401785e-01f, +3.806575694e-01f, -2.439058412e-02f }, + { -5.279116991e-02f, +6.998113426e-01f, +3.809937316e-01f, -2.442033366e-02f }, + { -5.276645702e-02f, +6.994824153e-01f, +3.813299629e-01f, -2.445009667e-02f }, + { -5.274171264e-02f, +6.991533966e-01f, +3.816662633e-01f, -2.447987311e-02f }, + { -5.271693684e-02f, +6.988242869e-01f, +3.820026325e-01f, -2.450966296e-02f }, + { -5.269212969e-02f, +6.984950861e-01f, +3.823390705e-01f, -2.453946619e-02f }, + { -5.266729126e-02f, +6.981657947e-01f, +3.826755771e-01f, -2.456928278e-02f }, + { -5.264242161e-02f, +6.978364126e-01f, +3.830121520e-01f, -2.459911271e-02f }, + { -5.261752083e-02f, +6.975069402e-01f, +3.833487953e-01f, -2.462895594e-02f }, + { -5.259258898e-02f, +6.971773775e-01f, +3.836855068e-01f, -2.465881245e-02f }, + { -5.256762613e-02f, +6.968477249e-01f, +3.840222862e-01f, -2.468868221e-02f }, + { -5.254263236e-02f, +6.965179824e-01f, +3.843591335e-01f, -2.471856521e-02f }, + { -5.251760772e-02f, +6.961881503e-01f, +3.846960485e-01f, -2.474846141e-02f }, + { -5.249255230e-02f, +6.958582288e-01f, +3.850330311e-01f, -2.477837080e-02f }, + { -5.246746616e-02f, +6.955282179e-01f, +3.853700811e-01f, -2.480829333e-02f }, + { -5.244234938e-02f, +6.951981180e-01f, +3.857071983e-01f, -2.483822900e-02f }, + { -5.241720201e-02f, +6.948679292e-01f, +3.860443828e-01f, -2.486817776e-02f }, + { -5.239202415e-02f, +6.945376516e-01f, +3.863816342e-01f, -2.489813960e-02f }, + { -5.236681584e-02f, +6.942072855e-01f, +3.867189524e-01f, -2.492811449e-02f }, + { -5.234157717e-02f, +6.938768311e-01f, +3.870563373e-01f, -2.495810241e-02f }, + { -5.231630820e-02f, +6.935462886e-01f, +3.873937888e-01f, -2.498810332e-02f }, + { -5.229100900e-02f, +6.932156580e-01f, +3.877313067e-01f, -2.501811720e-02f }, + { -5.226567965e-02f, +6.928849397e-01f, +3.880688909e-01f, -2.504814403e-02f }, + { -5.224032022e-02f, +6.925541338e-01f, +3.884065411e-01f, -2.507818379e-02f }, + { -5.221493076e-02f, +6.922232404e-01f, +3.887442574e-01f, -2.510823643e-02f }, + { -5.218951136e-02f, +6.918922599e-01f, +3.890820394e-01f, -2.513830194e-02f }, + { -5.216406208e-02f, +6.915611922e-01f, +3.894198871e-01f, -2.516838029e-02f }, + { -5.213858300e-02f, +6.912300378e-01f, +3.897578004e-01f, -2.519847146e-02f }, + { -5.211307417e-02f, +6.908987966e-01f, +3.900957790e-01f, -2.522857541e-02f }, + { -5.208753568e-02f, +6.905674690e-01f, +3.904338229e-01f, -2.525869212e-02f }, + { -5.206196759e-02f, +6.902360551e-01f, +3.907719318e-01f, -2.528882157e-02f }, + { -5.203636997e-02f, +6.899045550e-01f, +3.911101057e-01f, -2.531896373e-02f }, + { -5.201074290e-02f, +6.895729691e-01f, +3.914483444e-01f, -2.534911857e-02f }, + { -5.198508643e-02f, +6.892412974e-01f, +3.917866477e-01f, -2.537928607e-02f }, + { -5.195940064e-02f, +6.889095401e-01f, +3.921250156e-01f, -2.540946619e-02f }, + { -5.193368560e-02f, +6.885776975e-01f, +3.924634478e-01f, -2.543965891e-02f }, + { -5.190794138e-02f, +6.882457697e-01f, +3.928019442e-01f, -2.546986421e-02f }, + { -5.188216805e-02f, +6.879137568e-01f, +3.931405046e-01f, -2.550008206e-02f }, + { -5.185636568e-02f, +6.875816592e-01f, +3.934791290e-01f, -2.553031243e-02f }, + { -5.183053433e-02f, +6.872494769e-01f, +3.938178171e-01f, -2.556055529e-02f }, + { -5.180467408e-02f, +6.869172102e-01f, +3.941565688e-01f, -2.559081062e-02f }, + { -5.177878499e-02f, +6.865848593e-01f, +3.944953840e-01f, -2.562107838e-02f }, + { -5.175286714e-02f, +6.862524243e-01f, +3.948342626e-01f, -2.565135856e-02f }, + { -5.172692059e-02f, +6.859199054e-01f, +3.951732042e-01f, -2.568165113e-02f }, + { -5.170094541e-02f, +6.855873028e-01f, +3.955122090e-01f, -2.571195605e-02f }, + { -5.167494167e-02f, +6.852546167e-01f, +3.958512766e-01f, -2.574227331e-02f }, + { -5.164890945e-02f, +6.849218472e-01f, +3.961904069e-01f, -2.577260287e-02f }, + { -5.162284880e-02f, +6.845889947e-01f, +3.965295998e-01f, -2.580294470e-02f }, + { -5.159675980e-02f, +6.842560591e-01f, +3.968688551e-01f, -2.583329878e-02f }, + { -5.157064252e-02f, +6.839230409e-01f, +3.972081727e-01f, -2.586366508e-02f }, + { -5.154449702e-02f, +6.835899400e-01f, +3.975475525e-01f, -2.589404358e-02f }, + { -5.151832338e-02f, +6.832567567e-01f, +3.978869942e-01f, -2.592443425e-02f }, + { -5.149212166e-02f, +6.829234913e-01f, +3.982264977e-01f, -2.595483705e-02f }, + { -5.146589193e-02f, +6.825901438e-01f, +3.985660630e-01f, -2.598525196e-02f }, + { -5.143963427e-02f, +6.822567144e-01f, +3.989056898e-01f, -2.601567895e-02f }, + { -5.141334873e-02f, +6.819232035e-01f, +3.992453779e-01f, -2.604611800e-02f }, + { -5.138703539e-02f, +6.815896110e-01f, +3.995851274e-01f, -2.607656908e-02f }, + { -5.136069431e-02f, +6.812559373e-01f, +3.999249379e-01f, -2.610703216e-02f }, + { -5.133432558e-02f, +6.809221825e-01f, +4.002648093e-01f, -2.613750720e-02f }, + { -5.130792924e-02f, +6.805883468e-01f, +4.006047415e-01f, -2.616799420e-02f }, + { -5.128150537e-02f, +6.802544304e-01f, +4.009447344e-01f, -2.619849310e-02f }, + { -5.125505405e-02f, +6.799204334e-01f, +4.012847878e-01f, -2.622900390e-02f }, + { -5.122857533e-02f, +6.795863561e-01f, +4.016249015e-01f, -2.625952655e-02f }, + { -5.120206929e-02f, +6.792521986e-01f, +4.019650754e-01f, -2.629006103e-02f }, + { -5.117553600e-02f, +6.789179612e-01f, +4.023053093e-01f, -2.632060732e-02f }, + { -5.114897551e-02f, +6.785836440e-01f, +4.026456031e-01f, -2.635116538e-02f }, + { -5.112238791e-02f, +6.782492471e-01f, +4.029859567e-01f, -2.638173519e-02f }, + { -5.109577326e-02f, +6.779147709e-01f, +4.033263699e-01f, -2.641231671e-02f }, + { -5.106913162e-02f, +6.775802154e-01f, +4.036668425e-01f, -2.644290992e-02f }, + { -5.104246307e-02f, +6.772455809e-01f, +4.040073744e-01f, -2.647351480e-02f }, + { -5.101576767e-02f, +6.769108675e-01f, +4.043479654e-01f, -2.650413130e-02f }, + { -5.098904550e-02f, +6.765760755e-01f, +4.046886155e-01f, -2.653475941e-02f }, + { -5.096229661e-02f, +6.762412050e-01f, +4.050293243e-01f, -2.656539910e-02f }, + { -5.093552108e-02f, +6.759062562e-01f, +4.053700919e-01f, -2.659605033e-02f }, + { -5.090871897e-02f, +6.755712292e-01f, +4.057109180e-01f, -2.662671308e-02f }, + { -5.088189036e-02f, +6.752361244e-01f, +4.060518025e-01f, -2.665738731e-02f }, + { -5.085503531e-02f, +6.749009418e-01f, +4.063927452e-01f, -2.668807301e-02f }, + { -5.082815388e-02f, +6.745656817e-01f, +4.067337460e-01f, -2.671877013e-02f }, + { -5.080124615e-02f, +6.742303442e-01f, +4.070748047e-01f, -2.674947866e-02f }, + { -5.077431218e-02f, +6.738949295e-01f, +4.074159212e-01f, -2.678019856e-02f }, + { -5.074735205e-02f, +6.735594378e-01f, +4.077570954e-01f, -2.681092981e-02f }, + { -5.072036581e-02f, +6.732238693e-01f, +4.080983270e-01f, -2.684167237e-02f }, + { -5.069335353e-02f, +6.728882242e-01f, +4.084396159e-01f, -2.687242621e-02f }, + { -5.066631529e-02f, +6.725525027e-01f, +4.087809621e-01f, -2.690319131e-02f }, + { -5.063925115e-02f, +6.722167049e-01f, +4.091223652e-01f, -2.693396764e-02f }, + { -5.061216118e-02f, +6.718808311e-01f, +4.094638252e-01f, -2.696475516e-02f }, + { -5.058504544e-02f, +6.715448814e-01f, +4.098053420e-01f, -2.699555385e-02f }, + { -5.055790401e-02f, +6.712088560e-01f, +4.101469153e-01f, -2.702636369e-02f }, + { -5.053073694e-02f, +6.708727551e-01f, +4.104885450e-01f, -2.705718463e-02f }, + { -5.050354431e-02f, +6.705365788e-01f, +4.108302310e-01f, -2.708801665e-02f }, + { -5.047632619e-02f, +6.702003275e-01f, +4.111719731e-01f, -2.711885972e-02f }, + { -5.044908263e-02f, +6.698640012e-01f, +4.115137711e-01f, -2.714971381e-02f }, + { -5.042181372e-02f, +6.695276002e-01f, +4.118556250e-01f, -2.718057889e-02f }, + { -5.039451951e-02f, +6.691911246e-01f, +4.121975345e-01f, -2.721145493e-02f }, + { -5.036720007e-02f, +6.688545746e-01f, +4.125394995e-01f, -2.724234191e-02f }, + { -5.033985546e-02f, +6.685179504e-01f, +4.128815199e-01f, -2.727323979e-02f }, + { -5.031248577e-02f, +6.681812522e-01f, +4.132235955e-01f, -2.730414853e-02f }, + { -5.028509105e-02f, +6.678444802e-01f, +4.135657261e-01f, -2.733506812e-02f }, + { -5.025767136e-02f, +6.675076346e-01f, +4.139079116e-01f, -2.736599852e-02f }, + { -5.023022678e-02f, +6.671707155e-01f, +4.142501518e-01f, -2.739693971e-02f }, + { -5.020275738e-02f, +6.668337231e-01f, +4.145924466e-01f, -2.742789164e-02f }, + { -5.017526321e-02f, +6.664966577e-01f, +4.149347959e-01f, -2.745885430e-02f }, + { -5.014774435e-02f, +6.661595193e-01f, +4.152771994e-01f, -2.748982764e-02f }, + { -5.012020087e-02f, +6.658223083e-01f, +4.156196570e-01f, -2.752081165e-02f }, + { -5.009263282e-02f, +6.654850247e-01f, +4.159621686e-01f, -2.755180629e-02f }, + { -5.006504027e-02f, +6.651476689e-01f, +4.163047340e-01f, -2.758281153e-02f }, + { -5.003742330e-02f, +6.648102408e-01f, +4.166473531e-01f, -2.761382734e-02f }, + { -5.000978197e-02f, +6.644727409e-01f, +4.169900257e-01f, -2.764485368e-02f }, + { -4.998211634e-02f, +6.641351691e-01f, +4.173327517e-01f, -2.767589054e-02f }, + { -4.995442649e-02f, +6.637975258e-01f, +4.176755308e-01f, -2.770693788e-02f }, + { -4.992671247e-02f, +6.634598110e-01f, +4.180183630e-01f, -2.773799566e-02f }, + { -4.989897435e-02f, +6.631220251e-01f, +4.183612481e-01f, -2.776906386e-02f }, + { -4.987121220e-02f, +6.627841681e-01f, +4.187041859e-01f, -2.780014245e-02f }, + { -4.984342609e-02f, +6.624462403e-01f, +4.190471763e-01f, -2.783123139e-02f }, + { -4.981561608e-02f, +6.621082419e-01f, +4.193902192e-01f, -2.786233066e-02f }, + { -4.978778224e-02f, +6.617701729e-01f, +4.197333143e-01f, -2.789344022e-02f }, + { -4.975992463e-02f, +6.614320337e-01f, +4.200764615e-01f, -2.792456005e-02f }, + { -4.973204333e-02f, +6.610938245e-01f, +4.204196607e-01f, -2.795569010e-02f }, + { -4.970413838e-02f, +6.607555453e-01f, +4.207629117e-01f, -2.798683036e-02f }, + { -4.967620987e-02f, +6.604171963e-01f, +4.211062143e-01f, -2.801798079e-02f }, + { -4.964825786e-02f, +6.600787779e-01f, +4.214495685e-01f, -2.804914136e-02f }, + { -4.962028241e-02f, +6.597402901e-01f, +4.217929740e-01f, -2.808031203e-02f }, + { -4.959228359e-02f, +6.594017331e-01f, +4.221364307e-01f, -2.811149279e-02f }, + { -4.956426147e-02f, +6.590631072e-01f, +4.224799384e-01f, -2.814268358e-02f }, + { -4.953621610e-02f, +6.587244125e-01f, +4.228234970e-01f, -2.817388439e-02f }, + { -4.950814756e-02f, +6.583856492e-01f, +4.231671063e-01f, -2.820509519e-02f }, + { -4.948005591e-02f, +6.580468175e-01f, +4.235107662e-01f, -2.823631593e-02f }, + { -4.945194122e-02f, +6.577079175e-01f, +4.238544765e-01f, -2.826754660e-02f }, + { -4.942380356e-02f, +6.573689495e-01f, +4.241982371e-01f, -2.829878715e-02f }, + { -4.939564297e-02f, +6.570299137e-01f, +4.245420477e-01f, -2.833003756e-02f }, + { -4.936745955e-02f, +6.566908102e-01f, +4.248859083e-01f, -2.836129779e-02f }, + { -4.933925334e-02f, +6.563516392e-01f, +4.252298187e-01f, -2.839256782e-02f }, + { -4.931102441e-02f, +6.560124009e-01f, +4.255737788e-01f, -2.842384761e-02f }, + { -4.928277284e-02f, +6.556730955e-01f, +4.259177883e-01f, -2.845513713e-02f }, + { -4.925449868e-02f, +6.553337232e-01f, +4.262618471e-01f, -2.848643635e-02f }, + { -4.922620200e-02f, +6.549942842e-01f, +4.266059551e-01f, -2.851774523e-02f }, + { -4.919788286e-02f, +6.546547786e-01f, +4.269501121e-01f, -2.854906375e-02f }, + { -4.916954133e-02f, +6.543152066e-01f, +4.272943179e-01f, -2.858039187e-02f }, + { -4.914117748e-02f, +6.539755685e-01f, +4.276385725e-01f, -2.861172956e-02f }, + { -4.911279137e-02f, +6.536358644e-01f, +4.279828756e-01f, -2.864307678e-02f }, + { -4.908438306e-02f, +6.532960945e-01f, +4.283272270e-01f, -2.867443352e-02f }, + { -4.905595262e-02f, +6.529562589e-01f, +4.286716267e-01f, -2.870579972e-02f }, + { -4.902750012e-02f, +6.526163580e-01f, +4.290160745e-01f, -2.873717537e-02f }, + { -4.899902562e-02f, +6.522763918e-01f, +4.293605701e-01f, -2.876856043e-02f }, + { -4.897052918e-02f, +6.519363605e-01f, +4.297051136e-01f, -2.879995486e-02f }, + { -4.894201087e-02f, +6.515962644e-01f, +4.300497046e-01f, -2.883135864e-02f }, + { -4.891347075e-02f, +6.512561036e-01f, +4.303943430e-01f, -2.886277173e-02f }, + { -4.888490889e-02f, +6.509158783e-01f, +4.307390288e-01f, -2.889419410e-02f }, + { -4.885632535e-02f, +6.505755887e-01f, +4.310837617e-01f, -2.892562571e-02f }, + { -4.882772020e-02f, +6.502352350e-01f, +4.314285415e-01f, -2.895706654e-02f }, + { -4.879909350e-02f, +6.498948173e-01f, +4.317733681e-01f, -2.898851656e-02f }, + { -4.877044532e-02f, +6.495543359e-01f, +4.321182414e-01f, -2.901997572e-02f }, + { -4.874177572e-02f, +6.492137910e-01f, +4.324631612e-01f, -2.905144399e-02f }, + { -4.871308476e-02f, +6.488731826e-01f, +4.328081273e-01f, -2.908292135e-02f }, + { -4.868437251e-02f, +6.485325111e-01f, +4.331531396e-01f, -2.911440776e-02f }, + { -4.865563904e-02f, +6.481917765e-01f, +4.334981980e-01f, -2.914590319e-02f }, + { -4.862688440e-02f, +6.478509792e-01f, +4.338433021e-01f, -2.917740760e-02f }, + { -4.859810867e-02f, +6.475101192e-01f, +4.341884520e-01f, -2.920892096e-02f }, + { -4.856931190e-02f, +6.471691968e-01f, +4.345336475e-01f, -2.924044325e-02f }, + { -4.854049416e-02f, +6.468282121e-01f, +4.348788883e-01f, -2.927197441e-02f }, + { -4.851165552e-02f, +6.464871653e-01f, +4.352241743e-01f, -2.930351443e-02f }, + { -4.848279603e-02f, +6.461460567e-01f, +4.355695055e-01f, -2.933506327e-02f }, + { -4.845391577e-02f, +6.458048863e-01f, +4.359148815e-01f, -2.936662089e-02f }, + { -4.842501479e-02f, +6.454636545e-01f, +4.362603023e-01f, -2.939818727e-02f }, + { -4.839609316e-02f, +6.451223613e-01f, +4.366057677e-01f, -2.942976236e-02f }, + { -4.836715094e-02f, +6.447810070e-01f, +4.369512775e-01f, -2.946134614e-02f }, + { -4.833818821e-02f, +6.444395917e-01f, +4.372968316e-01f, -2.949293857e-02f }, + { -4.830920501e-02f, +6.440981157e-01f, +4.376424298e-01f, -2.952453962e-02f }, + { -4.828020142e-02f, +6.437565791e-01f, +4.379880720e-01f, -2.955614925e-02f }, + { -4.825117750e-02f, +6.434149821e-01f, +4.383337580e-01f, -2.958776743e-02f }, + { -4.822213331e-02f, +6.430733249e-01f, +4.386794876e-01f, -2.961939414e-02f }, + { -4.819306891e-02f, +6.427316077e-01f, +4.390252607e-01f, -2.965102932e-02f }, + { -4.816398438e-02f, +6.423898306e-01f, +4.393710771e-01f, -2.968267296e-02f }, + { -4.813487976e-02f, +6.420479939e-01f, +4.397169367e-01f, -2.971432501e-02f }, + { -4.810575513e-02f, +6.417060978e-01f, +4.400628392e-01f, -2.974598544e-02f }, + { -4.807661056e-02f, +6.413641423e-01f, +4.404087847e-01f, -2.977765422e-02f }, + { -4.804744609e-02f, +6.410221278e-01f, +4.407547728e-01f, -2.980933131e-02f }, + { -4.801826180e-02f, +6.406800544e-01f, +4.411008034e-01f, -2.984101669e-02f }, + { -4.798905775e-02f, +6.403379223e-01f, +4.414468764e-01f, -2.987271031e-02f }, + { -4.795983400e-02f, +6.399957316e-01f, +4.417929916e-01f, -2.990441214e-02f }, + { -4.793059062e-02f, +6.396534826e-01f, +4.421391489e-01f, -2.993612215e-02f }, + { -4.790132766e-02f, +6.393111754e-01f, +4.424853480e-01f, -2.996784030e-02f }, + { -4.787204520e-02f, +6.389688103e-01f, +4.428315888e-01f, -2.999956656e-02f }, + { -4.784274329e-02f, +6.386263874e-01f, +4.431778713e-01f, -3.003130090e-02f }, + { -4.781342200e-02f, +6.382839069e-01f, +4.435241951e-01f, -3.006304328e-02f }, + { -4.778408139e-02f, +6.379413690e-01f, +4.438705601e-01f, -3.009479366e-02f }, + { -4.775472152e-02f, +6.375987738e-01f, +4.442169663e-01f, -3.012655201e-02f }, + { -4.772534246e-02f, +6.372561217e-01f, +4.445634133e-01f, -3.015831830e-02f }, + { -4.769594427e-02f, +6.369134126e-01f, +4.449099012e-01f, -3.019009249e-02f }, + { -4.766652701e-02f, +6.365706469e-01f, +4.452564296e-01f, -3.022187454e-02f }, + { -4.763709074e-02f, +6.362278248e-01f, +4.456029984e-01f, -3.025366443e-02f }, + { -4.760763553e-02f, +6.358849463e-01f, +4.459496076e-01f, -3.028546212e-02f }, + { -4.757816144e-02f, +6.355420117e-01f, +4.462962568e-01f, -3.031726757e-02f }, + { -4.754866853e-02f, +6.351990212e-01f, +4.466429460e-01f, -3.034908075e-02f }, + { -4.751915687e-02f, +6.348559750e-01f, +4.469896750e-01f, -3.038090162e-02f }, + { -4.748962651e-02f, +6.345128733e-01f, +4.473364437e-01f, -3.041273015e-02f }, + { -4.746007752e-02f, +6.341697161e-01f, +4.476832518e-01f, -3.044456630e-02f }, + { -4.743050997e-02f, +6.338265038e-01f, +4.480300992e-01f, -3.047641003e-02f }, + { -4.740092391e-02f, +6.334832365e-01f, +4.483769858e-01f, -3.050826133e-02f }, + { -4.737131940e-02f, +6.331399144e-01f, +4.487239113e-01f, -3.054012013e-02f }, + { -4.734169651e-02f, +6.327965377e-01f, +4.490708757e-01f, -3.057198642e-02f }, + { -4.731205530e-02f, +6.324531066e-01f, +4.494178788e-01f, -3.060386016e-02f }, + { -4.728239584e-02f, +6.321096212e-01f, +4.497649203e-01f, -3.063574131e-02f }, + { -4.725271818e-02f, +6.317660818e-01f, +4.501120002e-01f, -3.066762983e-02f }, + { -4.722302238e-02f, +6.314224885e-01f, +4.504591183e-01f, -3.069952570e-02f }, + { -4.719330852e-02f, +6.310788415e-01f, +4.508062744e-01f, -3.073142887e-02f }, + { -4.716357665e-02f, +6.307351410e-01f, +4.511534683e-01f, -3.076333930e-02f }, + { -4.713382682e-02f, +6.303913872e-01f, +4.515006999e-01f, -3.079525698e-02f }, + { -4.710405912e-02f, +6.300475802e-01f, +4.518479691e-01f, -3.082718185e-02f }, + { -4.707427359e-02f, +6.297037204e-01f, +4.521952756e-01f, -3.085911388e-02f }, + { -4.704447029e-02f, +6.293598077e-01f, +4.525426193e-01f, -3.089105304e-02f }, + { -4.701464930e-02f, +6.290158425e-01f, +4.528900001e-01f, -3.092299929e-02f }, + { -4.698481067e-02f, +6.286718249e-01f, +4.532374178e-01f, -3.095495259e-02f }, + { -4.695495446e-02f, +6.283277552e-01f, +4.535848721e-01f, -3.098691291e-02f }, + { -4.692508073e-02f, +6.279836334e-01f, +4.539323631e-01f, -3.101888022e-02f }, + { -4.689518955e-02f, +6.276394598e-01f, +4.542798904e-01f, -3.105085447e-02f }, + { -4.686528098e-02f, +6.272952345e-01f, +4.546274539e-01f, -3.108283564e-02f }, + { -4.683535507e-02f, +6.269509578e-01f, +4.549750535e-01f, -3.111482367e-02f }, + { -4.680541190e-02f, +6.266066299e-01f, +4.553226890e-01f, -3.114681855e-02f }, + { -4.677545152e-02f, +6.262622508e-01f, +4.556703603e-01f, -3.117882023e-02f }, + { -4.674547399e-02f, +6.259178209e-01f, +4.560180671e-01f, -3.121082867e-02f }, + { -4.671547937e-02f, +6.255733402e-01f, +4.563658094e-01f, -3.124284384e-02f }, + { -4.668546773e-02f, +6.252288091e-01f, +4.567135869e-01f, -3.127486571e-02f }, + { -4.665543913e-02f, +6.248842275e-01f, +4.570613994e-01f, -3.130689423e-02f }, + { -4.662539362e-02f, +6.245395959e-01f, +4.574092469e-01f, -3.133892937e-02f }, + { -4.659533127e-02f, +6.241949143e-01f, +4.577571292e-01f, -3.137097110e-02f }, + { -4.656525215e-02f, +6.238501829e-01f, +4.581050460e-01f, -3.140301937e-02f }, + { -4.653515630e-02f, +6.235054019e-01f, +4.584529973e-01f, -3.143507415e-02f }, + { -4.650504379e-02f, +6.231605715e-01f, +4.588009829e-01f, -3.146713541e-02f }, + { -4.647491468e-02f, +6.228156918e-01f, +4.591490025e-01f, -3.149920310e-02f }, + { -4.644476904e-02f, +6.224707632e-01f, +4.594970561e-01f, -3.153127720e-02f }, + { -4.641460692e-02f, +6.221257857e-01f, +4.598451435e-01f, -3.156335765e-02f }, + { -4.638442839e-02f, +6.217807595e-01f, +4.601932645e-01f, -3.159544443e-02f }, + { -4.635423350e-02f, +6.214356849e-01f, +4.605414189e-01f, -3.162753750e-02f }, + { -4.632402231e-02f, +6.210905619e-01f, +4.608896066e-01f, -3.165963682e-02f }, + { -4.629379489e-02f, +6.207453909e-01f, +4.612378274e-01f, -3.169174236e-02f }, + { -4.626355130e-02f, +6.204001719e-01f, +4.615860812e-01f, -3.172385407e-02f }, + { -4.623329159e-02f, +6.200549052e-01f, +4.619343678e-01f, -3.175597193e-02f }, + { -4.620301584e-02f, +6.197095909e-01f, +4.622826870e-01f, -3.178809589e-02f }, + { -4.617272409e-02f, +6.193642293e-01f, +4.626310387e-01f, -3.182022591e-02f }, + { -4.614241641e-02f, +6.190188205e-01f, +4.629794226e-01f, -3.185236196e-02f }, + { -4.611209285e-02f, +6.186733647e-01f, +4.633278387e-01f, -3.188450401e-02f }, + { -4.608175349e-02f, +6.183278621e-01f, +4.636762867e-01f, -3.191665200e-02f }, + { -4.605139837e-02f, +6.179823129e-01f, +4.640247666e-01f, -3.194880592e-02f }, + { -4.602102757e-02f, +6.176367173e-01f, +4.643732781e-01f, -3.198096571e-02f }, + { -4.599064113e-02f, +6.172910754e-01f, +4.647218210e-01f, -3.201313134e-02f }, + { -4.596023912e-02f, +6.169453874e-01f, +4.650703953e-01f, -3.204530278e-02f }, + { -4.592982160e-02f, +6.165996536e-01f, +4.654190007e-01f, -3.207747998e-02f }, + { -4.589938863e-02f, +6.162538740e-01f, +4.657676371e-01f, -3.210966291e-02f }, + { -4.586894027e-02f, +6.159080490e-01f, +4.661163043e-01f, -3.214185153e-02f }, + { -4.583847658e-02f, +6.155621786e-01f, +4.664650021e-01f, -3.217404580e-02f }, + { -4.580799762e-02f, +6.152162631e-01f, +4.668137304e-01f, -3.220624568e-02f }, + { -4.577750344e-02f, +6.148703026e-01f, +4.671624891e-01f, -3.223845114e-02f }, + { -4.574699411e-02f, +6.145242974e-01f, +4.675112778e-01f, -3.227066214e-02f }, + { -4.571646969e-02f, +6.141782476e-01f, +4.678600966e-01f, -3.230287864e-02f }, + { -4.568593024e-02f, +6.138321534e-01f, +4.682089452e-01f, -3.233510060e-02f }, + { -4.565537582e-02f, +6.134860150e-01f, +4.685578234e-01f, -3.236732799e-02f }, + { -4.562480648e-02f, +6.131398326e-01f, +4.689067311e-01f, -3.239956076e-02f }, + { -4.559422228e-02f, +6.127936063e-01f, +4.692556681e-01f, -3.243179888e-02f }, + { -4.556362330e-02f, +6.124473364e-01f, +4.696046343e-01f, -3.246404230e-02f }, + { -4.553300957e-02f, +6.121010230e-01f, +4.699536294e-01f, -3.249629100e-02f }, + { -4.550238117e-02f, +6.117546663e-01f, +4.703026534e-01f, -3.252854493e-02f }, + { -4.547173816e-02f, +6.114082665e-01f, +4.706517061e-01f, -3.256080405e-02f }, + { -4.544108058e-02f, +6.110618238e-01f, +4.710007872e-01f, -3.259306833e-02f }, + { -4.541040851e-02f, +6.107153384e-01f, +4.713498966e-01f, -3.262533772e-02f }, + { -4.537972200e-02f, +6.103688104e-01f, +4.716990342e-01f, -3.265761219e-02f }, + { -4.534902111e-02f, +6.100222401e-01f, +4.720481998e-01f, -3.268989170e-02f }, + { -4.531830590e-02f, +6.096756276e-01f, +4.723973932e-01f, -3.272217621e-02f }, + { -4.528757642e-02f, +6.093289732e-01f, +4.727466142e-01f, -3.275446568e-02f }, + { -4.525683274e-02f, +6.089822769e-01f, +4.730958628e-01f, -3.278676008e-02f }, + { -4.522607492e-02f, +6.086355390e-01f, +4.734451386e-01f, -3.281905935e-02f }, + { -4.519530301e-02f, +6.082887597e-01f, +4.737944416e-01f, -3.285136347e-02f }, + { -4.516451708e-02f, +6.079419391e-01f, +4.741437716e-01f, -3.288367240e-02f }, + { -4.513371718e-02f, +6.075950775e-01f, +4.744931285e-01f, -3.291598609e-02f }, + { -4.510290336e-02f, +6.072481750e-01f, +4.748425119e-01f, -3.294830451e-02f }, + { -4.507207570e-02f, +6.069012318e-01f, +4.751919219e-01f, -3.298062762e-02f }, + { -4.504123425e-02f, +6.065542481e-01f, +4.755413581e-01f, -3.301295538e-02f }, + { -4.501037906e-02f, +6.062072240e-01f, +4.758908206e-01f, -3.304528775e-02f }, + { -4.497951020e-02f, +6.058601599e-01f, +4.762403090e-01f, -3.307762468e-02f }, + { -4.494862772e-02f, +6.055130558e-01f, +4.765898232e-01f, -3.310996615e-02f }, + { -4.491773168e-02f, +6.051659119e-01f, +4.769393630e-01f, -3.314231211e-02f }, + { -4.488682214e-02f, +6.048187284e-01f, +4.772889284e-01f, -3.317466252e-02f }, + { -4.485589916e-02f, +6.044715056e-01f, +4.776385190e-01f, -3.320701734e-02f }, + { -4.482496280e-02f, +6.041242435e-01f, +4.779881348e-01f, -3.323937653e-02f }, + { -4.479401311e-02f, +6.037769424e-01f, +4.783377756e-01f, -3.327174006e-02f }, + { -4.476305016e-02f, +6.034296025e-01f, +4.786874411e-01f, -3.330410788e-02f }, + { -4.473207400e-02f, +6.030822239e-01f, +4.790371313e-01f, -3.333647996e-02f }, + { -4.470108468e-02f, +6.027348068e-01f, +4.793868460e-01f, -3.336885624e-02f }, + { -4.467008228e-02f, +6.023873514e-01f, +4.797365850e-01f, -3.340123671e-02f }, + { -4.463906683e-02f, +6.020398580e-01f, +4.800863481e-01f, -3.343362130e-02f }, + { -4.460803842e-02f, +6.016923266e-01f, +4.804361352e-01f, -3.346600999e-02f }, + { -4.457699708e-02f, +6.013447575e-01f, +4.807859461e-01f, -3.349840273e-02f }, + { -4.454594288e-02f, +6.009971508e-01f, +4.811357806e-01f, -3.353079949e-02f }, + { -4.451487588e-02f, +6.006495068e-01f, +4.814856386e-01f, -3.356320022e-02f }, + { -4.448379613e-02f, +6.003018256e-01f, +4.818355198e-01f, -3.359560488e-02f }, + { -4.445270370e-02f, +5.999541074e-01f, +4.821854242e-01f, -3.362801344e-02f }, + { -4.442159863e-02f, +5.996063524e-01f, +4.825353516e-01f, -3.366042585e-02f }, + { -4.439048099e-02f, +5.992585607e-01f, +4.828853017e-01f, -3.369284207e-02f }, + { -4.435935084e-02f, +5.989107327e-01f, +4.832352745e-01f, -3.372526206e-02f }, + { -4.432820823e-02f, +5.985628683e-01f, +4.835852697e-01f, -3.375768579e-02f }, + { -4.429705321e-02f, +5.982149679e-01f, +4.839352871e-01f, -3.379011321e-02f }, + { -4.426588586e-02f, +5.978670316e-01f, +4.842853267e-01f, -3.382254427e-02f }, + { -4.423470622e-02f, +5.975190596e-01f, +4.846353882e-01f, -3.385497895e-02f }, + { -4.420351435e-02f, +5.971710521e-01f, +4.849854715e-01f, -3.388741720e-02f }, + { -4.417231031e-02f, +5.968230092e-01f, +4.853355764e-01f, -3.391985897e-02f }, + { -4.414109415e-02f, +5.964749312e-01f, +4.856857028e-01f, -3.395230423e-02f }, + { -4.410986594e-02f, +5.961268182e-01f, +4.860358504e-01f, -3.398475294e-02f }, + { -4.407862573e-02f, +5.957786704e-01f, +4.863860191e-01f, -3.401720506e-02f }, + { -4.404737358e-02f, +5.954304881e-01f, +4.867362087e-01f, -3.404966054e-02f }, + { -4.401610954e-02f, +5.950822713e-01f, +4.870864191e-01f, -3.408211934e-02f }, + { -4.398483367e-02f, +5.947340203e-01f, +4.874366500e-01f, -3.411458143e-02f }, + { -4.395354603e-02f, +5.943857352e-01f, +4.877869014e-01f, -3.414704676e-02f }, + { -4.392224667e-02f, +5.940374163e-01f, +4.881371730e-01f, -3.417951529e-02f }, + { -4.389093566e-02f, +5.936890637e-01f, +4.884874647e-01f, -3.421198698e-02f }, + { -4.385961304e-02f, +5.933406776e-01f, +4.888377763e-01f, -3.424446179e-02f }, + { -4.382827888e-02f, +5.929922582e-01f, +4.891881076e-01f, -3.427693968e-02f }, + { -4.379693323e-02f, +5.926438057e-01f, +4.895384585e-01f, -3.430942060e-02f }, + { -4.376557615e-02f, +5.922953202e-01f, +4.898888288e-01f, -3.434190452e-02f }, + { -4.373420769e-02f, +5.919468020e-01f, +4.902392184e-01f, -3.437439139e-02f }, + { -4.370282792e-02f, +5.915982512e-01f, +4.905896269e-01f, -3.440688117e-02f }, + { -4.367143688e-02f, +5.912496680e-01f, +4.909400544e-01f, -3.443937382e-02f }, + { -4.364003464e-02f, +5.909010526e-01f, +4.912905006e-01f, -3.447186930e-02f }, + { -4.360862124e-02f, +5.905524052e-01f, +4.916409653e-01f, -3.450436756e-02f }, + { -4.357719676e-02f, +5.902037260e-01f, +4.919914484e-01f, -3.453686857e-02f }, + { -4.354576123e-02f, +5.898550151e-01f, +4.923419497e-01f, -3.456937229e-02f }, + { -4.351431473e-02f, +5.895062727e-01f, +4.926924690e-01f, -3.460187866e-02f }, + { -4.348285730e-02f, +5.891574990e-01f, +4.930430062e-01f, -3.463438766e-02f }, + { -4.345138900e-02f, +5.888086942e-01f, +4.933935611e-01f, -3.466689923e-02f }, + { -4.341990989e-02f, +5.884598586e-01f, +4.937441335e-01f, -3.469941334e-02f }, + { -4.338842003e-02f, +5.881109921e-01f, +4.940947233e-01f, -3.473192994e-02f }, + { -4.335691946e-02f, +5.877620952e-01f, +4.944453302e-01f, -3.476444900e-02f }, + { -4.332540825e-02f, +5.874131678e-01f, +4.947959542e-01f, -3.479697046e-02f }, + { -4.329388645e-02f, +5.870642103e-01f, +4.951465949e-01f, -3.482949429e-02f }, + { -4.326235411e-02f, +5.867152227e-01f, +4.954972524e-01f, -3.486202044e-02f }, + { -4.323081130e-02f, +5.863662054e-01f, +4.958479263e-01f, -3.489454888e-02f }, + { -4.319925807e-02f, +5.860171584e-01f, +4.961986166e-01f, -3.492707956e-02f }, + { -4.316769447e-02f, +5.856680820e-01f, +4.965493230e-01f, -3.495961243e-02f }, + { -4.313612056e-02f, +5.853189763e-01f, +4.969000454e-01f, -3.499214746e-02f }, + { -4.310453640e-02f, +5.849698415e-01f, +4.972507836e-01f, -3.502468461e-02f }, + { -4.307294203e-02f, +5.846206778e-01f, +4.976015374e-01f, -3.505722382e-02f }, + { -4.304133752e-02f, +5.842714854e-01f, +4.979523067e-01f, -3.508976506e-02f }, + { -4.300972293e-02f, +5.839222644e-01f, +4.983030913e-01f, -3.512230829e-02f }, + { -4.297809830e-02f, +5.835730151e-01f, +4.986538910e-01f, -3.515485346e-02f }, + { -4.294646369e-02f, +5.832237377e-01f, +4.990047056e-01f, -3.518740053e-02f }, + { -4.291481916e-02f, +5.828744322e-01f, +4.993555351e-01f, -3.521994946e-02f }, + { -4.288316476e-02f, +5.825250990e-01f, +4.997063791e-01f, -3.525250020e-02f }, + { -4.285150055e-02f, +5.821757381e-01f, +5.000572376e-01f, -3.528505271e-02f }, + { -4.281982659e-02f, +5.818263498e-01f, +5.004081103e-01f, -3.531760695e-02f }, + { -4.278814292e-02f, +5.814769342e-01f, +5.007589972e-01f, -3.535016287e-02f }, + { -4.275644960e-02f, +5.811274916e-01f, +5.011098979e-01f, -3.538272044e-02f }, + { -4.272474669e-02f, +5.807780221e-01f, +5.014608124e-01f, -3.541527960e-02f }, + { -4.269303424e-02f, +5.804285259e-01f, +5.018117405e-01f, -3.544784032e-02f }, + { -4.266131231e-02f, +5.800790031e-01f, +5.021626819e-01f, -3.548040255e-02f }, + { -4.262958096e-02f, +5.797294541e-01f, +5.025136366e-01f, -3.551296626e-02f }, + { -4.259784023e-02f, +5.793798788e-01f, +5.028646044e-01f, -3.554553138e-02f }, + { -4.256609018e-02f, +5.790302776e-01f, +5.032155850e-01f, -3.557809789e-02f }, + { -4.253433087e-02f, +5.786806507e-01f, +5.035665783e-01f, -3.561066574e-02f }, + { -4.250256235e-02f, +5.783309981e-01f, +5.039175842e-01f, -3.564323489e-02f }, + { -4.247078467e-02f, +5.779813201e-01f, +5.042686025e-01f, -3.567580528e-02f }, + { -4.243899790e-02f, +5.776316168e-01f, +5.046196329e-01f, -3.570837689e-02f }, + { -4.240720208e-02f, +5.772818885e-01f, +5.049706753e-01f, -3.574094966e-02f }, + { -4.237539726e-02f, +5.769321353e-01f, +5.053217296e-01f, -3.577352355e-02f }, + { -4.234358351e-02f, +5.765823574e-01f, +5.056727956e-01f, -3.580609851e-02f }, + { -4.231176088e-02f, +5.762325550e-01f, +5.060238731e-01f, -3.583867451e-02f }, + { -4.227992942e-02f, +5.758827283e-01f, +5.063749619e-01f, -3.587125150e-02f }, + { -4.224808919e-02f, +5.755328774e-01f, +5.067260619e-01f, -3.590382944e-02f }, + { -4.221624023e-02f, +5.751830026e-01f, +5.070771728e-01f, -3.593640828e-02f }, + { -4.218438261e-02f, +5.748331040e-01f, +5.074282945e-01f, -3.596898797e-02f }, + { -4.215251638e-02f, +5.744831818e-01f, +5.077794269e-01f, -3.600156848e-02f }, + { -4.212064159e-02f, +5.741332362e-01f, +5.081305697e-01f, -3.603414976e-02f }, + { -4.208875830e-02f, +5.737832673e-01f, +5.084817229e-01f, -3.606673176e-02f }, + { -4.205686655e-02f, +5.734332754e-01f, +5.088328861e-01f, -3.609931444e-02f }, + { -4.202496641e-02f, +5.730832607e-01f, +5.091840593e-01f, -3.613189776e-02f }, + { -4.199305793e-02f, +5.727332232e-01f, +5.095352422e-01f, -3.616448168e-02f }, + { -4.196114116e-02f, +5.723831633e-01f, +5.098864348e-01f, -3.619706614e-02f }, + { -4.192921615e-02f, +5.720330810e-01f, +5.102376367e-01f, -3.622965110e-02f }, + { -4.189728297e-02f, +5.716829766e-01f, +5.105888479e-01f, -3.626223652e-02f }, + { -4.186534165e-02f, +5.713328502e-01f, +5.109400682e-01f, -3.629482236e-02f }, + { -4.183339226e-02f, +5.709827020e-01f, +5.112912974e-01f, -3.632740857e-02f }, + { -4.180143485e-02f, +5.706325323e-01f, +5.116425353e-01f, -3.635999510e-02f }, + { -4.176946947e-02f, +5.702823411e-01f, +5.119937817e-01f, -3.639258191e-02f }, + { -4.173749618e-02f, +5.699321287e-01f, +5.123450366e-01f, -3.642516896e-02f }, + { -4.170551503e-02f, +5.695818953e-01f, +5.126962996e-01f, -3.645775620e-02f }, + { -4.167352607e-02f, +5.692316410e-01f, +5.130475706e-01f, -3.649034358e-02f }, + { -4.164152936e-02f, +5.688813660e-01f, +5.133988495e-01f, -3.652293107e-02f }, + { -4.160952494e-02f, +5.685310706e-01f, +5.137501361e-01f, -3.655551861e-02f }, + { -4.157751288e-02f, +5.681807548e-01f, +5.141014302e-01f, -3.658810617e-02f }, + { -4.154549322e-02f, +5.678304188e-01f, +5.144527316e-01f, -3.662069369e-02f }, + { -4.151346602e-02f, +5.674800629e-01f, +5.148040402e-01f, -3.665328113e-02f }, + { -4.148143133e-02f, +5.671296873e-01f, +5.151553558e-01f, -3.668586844e-02f }, + { -4.144938921e-02f, +5.667792920e-01f, +5.155066781e-01f, -3.671845559e-02f }, + { -4.141733970e-02f, +5.664288773e-01f, +5.158580071e-01f, -3.675104252e-02f }, + { -4.138528287e-02f, +5.660784434e-01f, +5.162093426e-01f, -3.678362919e-02f }, + { -4.135321875e-02f, +5.657279905e-01f, +5.165606843e-01f, -3.681621556e-02f }, + { -4.132114741e-02f, +5.653775186e-01f, +5.169120321e-01f, -3.684880158e-02f }, + { -4.128906890e-02f, +5.650270281e-01f, +5.172633859e-01f, -3.688138720e-02f }, + { -4.125698327e-02f, +5.646765191e-01f, +5.176147454e-01f, -3.691397237e-02f }, + { -4.122489058e-02f, +5.643259918e-01f, +5.179661105e-01f, -3.694655706e-02f }, + { -4.119279087e-02f, +5.639754463e-01f, +5.183174810e-01f, -3.697914122e-02f }, + { -4.116068419e-02f, +5.636248829e-01f, +5.186688568e-01f, -3.701172480e-02f }, + { -4.112857061e-02f, +5.632743017e-01f, +5.190202376e-01f, -3.704430775e-02f }, + { -4.109645017e-02f, +5.629237029e-01f, +5.193716233e-01f, -3.707689003e-02f }, + { -4.106432293e-02f, +5.625730867e-01f, +5.197230137e-01f, -3.710947160e-02f }, + { -4.103218893e-02f, +5.622224532e-01f, +5.200744086e-01f, -3.714205241e-02f }, + { -4.100004824e-02f, +5.618718027e-01f, +5.204258079e-01f, -3.717463240e-02f }, + { -4.096790089e-02f, +5.615211353e-01f, +5.207772113e-01f, -3.720721154e-02f }, + { -4.093574695e-02f, +5.611704512e-01f, +5.211286188e-01f, -3.723978979e-02f }, + { -4.090358647e-02f, +5.608197506e-01f, +5.214800301e-01f, -3.727236708e-02f }, + { -4.087141949e-02f, +5.604690337e-01f, +5.218314450e-01f, -3.730494338e-02f }, + { -4.083924608e-02f, +5.601183006e-01f, +5.221828634e-01f, -3.733751865e-02f }, + { -4.080706628e-02f, +5.597675516e-01f, +5.225342852e-01f, -3.737009283e-02f }, + { -4.077488014e-02f, +5.594167868e-01f, +5.228857100e-01f, -3.740266587e-02f }, + { -4.074268771e-02f, +5.590660064e-01f, +5.232371378e-01f, -3.743523774e-02f }, + { -4.071048906e-02f, +5.587152105e-01f, +5.235885684e-01f, -3.746780839e-02f }, + { -4.067828422e-02f, +5.583643994e-01f, +5.239400015e-01f, -3.750037777e-02f }, + { -4.064607325e-02f, +5.580135732e-01f, +5.242914371e-01f, -3.753294582e-02f }, + { -4.061385621e-02f, +5.576627322e-01f, +5.246428750e-01f, -3.756551252e-02f }, + { -4.058163314e-02f, +5.573118764e-01f, +5.249943149e-01f, -3.759807780e-02f }, + { -4.054940409e-02f, +5.569610062e-01f, +5.253457567e-01f, -3.763064163e-02f }, + { -4.051716913e-02f, +5.566101215e-01f, +5.256972002e-01f, -3.766320395e-02f }, + { -4.048492829e-02f, +5.562592228e-01f, +5.260486453e-01f, -3.769576473e-02f }, + { -4.045268163e-02f, +5.559083100e-01f, +5.264000917e-01f, -3.772832390e-02f }, + { -4.042042920e-02f, +5.555573834e-01f, +5.267515394e-01f, -3.776088143e-02f }, + { -4.038817105e-02f, +5.552064433e-01f, +5.271029880e-01f, -3.779343727e-02f }, + { -4.035590724e-02f, +5.548554896e-01f, +5.274544375e-01f, -3.782599137e-02f }, + { -4.032363781e-02f, +5.545045228e-01f, +5.278058876e-01f, -3.785854369e-02f }, + { -4.029136282e-02f, +5.541535428e-01f, +5.281573382e-01f, -3.789109417e-02f }, + { -4.025908231e-02f, +5.538025499e-01f, +5.285087891e-01f, -3.792364277e-02f }, + { -4.022679634e-02f, +5.534515443e-01f, +5.288602402e-01f, -3.795618945e-02f }, + { -4.019450496e-02f, +5.531005262e-01f, +5.292116912e-01f, -3.798873415e-02f }, + { -4.016220822e-02f, +5.527494957e-01f, +5.295631419e-01f, -3.802127683e-02f }, + { -4.012990617e-02f, +5.523984531e-01f, +5.299145923e-01f, -3.805381745e-02f }, + { -4.009759887e-02f, +5.520473984e-01f, +5.302660421e-01f, -3.808635594e-02f }, + { -4.006528635e-02f, +5.516963319e-01f, +5.306174912e-01f, -3.811889228e-02f }, + { -4.003296867e-02f, +5.513452538e-01f, +5.309689393e-01f, -3.815142640e-02f }, + { -4.000064589e-02f, +5.509941643e-01f, +5.313203863e-01f, -3.818395826e-02f }, + { -3.996831805e-02f, +5.506430634e-01f, +5.316718320e-01f, -3.821648782e-02f }, + { -3.993598521e-02f, +5.502919514e-01f, +5.320232763e-01f, -3.824901502e-02f }, + { -3.990364741e-02f, +5.499408286e-01f, +5.323747189e-01f, -3.828153982e-02f }, + { -3.987130471e-02f, +5.495896949e-01f, +5.327261597e-01f, -3.831406217e-02f }, + { -3.983895715e-02f, +5.492385508e-01f, +5.330775985e-01f, -3.834658202e-02f }, + { -3.980660479e-02f, +5.488873962e-01f, +5.334290351e-01f, -3.837909933e-02f }, + { -3.977424767e-02f, +5.485362314e-01f, +5.337804694e-01f, -3.841161404e-02f }, + { -3.974188585e-02f, +5.481850566e-01f, +5.341319012e-01f, -3.844412612e-02f }, + { -3.970951938e-02f, +5.478338720e-01f, +5.344833302e-01f, -3.847663550e-02f }, + { -3.967714830e-02f, +5.474826777e-01f, +5.348347564e-01f, -3.850914215e-02f }, + { -3.964477267e-02f, +5.471314739e-01f, +5.351861795e-01f, -3.854164601e-02f }, + { -3.961239253e-02f, +5.467802608e-01f, +5.355375994e-01f, -3.857414703e-02f }, + { -3.958000795e-02f, +5.464290386e-01f, +5.358890159e-01f, -3.860664518e-02f }, + { -3.954761896e-02f, +5.460778075e-01f, +5.362404287e-01f, -3.863914039e-02f }, + { -3.951522561e-02f, +5.457265676e-01f, +5.365918378e-01f, -3.867163263e-02f }, + { -3.948282796e-02f, +5.453753190e-01f, +5.369432430e-01f, -3.870412184e-02f }, + { -3.945042606e-02f, +5.450240621e-01f, +5.372946440e-01f, -3.873660797e-02f }, + { -3.941801995e-02f, +5.446727970e-01f, +5.376460407e-01f, -3.876909098e-02f }, + { -3.938560969e-02f, +5.443215238e-01f, +5.379974330e-01f, -3.880157082e-02f }, + { -3.935319533e-02f, +5.439702427e-01f, +5.383488206e-01f, -3.883404743e-02f }, + { -3.932077690e-02f, +5.436189539e-01f, +5.387002033e-01f, -3.886652078e-02f }, + { -3.928835448e-02f, +5.432676576e-01f, +5.390515810e-01f, -3.889899081e-02f }, + { -3.925592809e-02f, +5.429163540e-01f, +5.394029536e-01f, -3.893145747e-02f }, + { -3.922349780e-02f, +5.425650432e-01f, +5.397543207e-01f, -3.896392071e-02f }, + { -3.919106365e-02f, +5.422137254e-01f, +5.401056823e-01f, -3.899638049e-02f }, + { -3.915862569e-02f, +5.418624009e-01f, +5.404570382e-01f, -3.902883675e-02f }, + { -3.912618397e-02f, +5.415110697e-01f, +5.408083882e-01f, -3.906128945e-02f }, + { -3.909373854e-02f, +5.411597321e-01f, +5.411597321e-01f, -3.909373854e-02f }, + { -3.906128945e-02f, +5.408083882e-01f, +5.415110697e-01f, -3.912618397e-02f }, + { -3.902883675e-02f, +5.404570382e-01f, +5.418624009e-01f, -3.915862569e-02f }, + { -3.899638049e-02f, +5.401056823e-01f, +5.422137254e-01f, -3.919106365e-02f }, + { -3.896392071e-02f, +5.397543207e-01f, +5.425650432e-01f, -3.922349780e-02f }, + { -3.893145747e-02f, +5.394029536e-01f, +5.429163540e-01f, -3.925592809e-02f }, + { -3.889899081e-02f, +5.390515810e-01f, +5.432676576e-01f, -3.928835448e-02f }, + { -3.886652078e-02f, +5.387002033e-01f, +5.436189539e-01f, -3.932077690e-02f }, + { -3.883404743e-02f, +5.383488206e-01f, +5.439702427e-01f, -3.935319533e-02f }, + { -3.880157082e-02f, +5.379974330e-01f, +5.443215238e-01f, -3.938560969e-02f }, + { -3.876909098e-02f, +5.376460407e-01f, +5.446727970e-01f, -3.941801995e-02f }, + { -3.873660797e-02f, +5.372946440e-01f, +5.450240621e-01f, -3.945042606e-02f }, + { -3.870412184e-02f, +5.369432430e-01f, +5.453753190e-01f, -3.948282796e-02f }, + { -3.867163263e-02f, +5.365918378e-01f, +5.457265676e-01f, -3.951522561e-02f }, + { -3.863914039e-02f, +5.362404287e-01f, +5.460778075e-01f, -3.954761896e-02f }, + { -3.860664518e-02f, +5.358890159e-01f, +5.464290386e-01f, -3.958000795e-02f }, + { -3.857414703e-02f, +5.355375994e-01f, +5.467802608e-01f, -3.961239253e-02f }, + { -3.854164601e-02f, +5.351861795e-01f, +5.471314739e-01f, -3.964477267e-02f }, + { -3.850914215e-02f, +5.348347564e-01f, +5.474826777e-01f, -3.967714830e-02f }, + { -3.847663550e-02f, +5.344833302e-01f, +5.478338720e-01f, -3.970951938e-02f }, + { -3.844412612e-02f, +5.341319012e-01f, +5.481850566e-01f, -3.974188585e-02f }, + { -3.841161404e-02f, +5.337804694e-01f, +5.485362314e-01f, -3.977424767e-02f }, + { -3.837909933e-02f, +5.334290351e-01f, +5.488873962e-01f, -3.980660479e-02f }, + { -3.834658202e-02f, +5.330775985e-01f, +5.492385508e-01f, -3.983895715e-02f }, + { -3.831406217e-02f, +5.327261597e-01f, +5.495896949e-01f, -3.987130471e-02f }, + { -3.828153982e-02f, +5.323747189e-01f, +5.499408286e-01f, -3.990364741e-02f }, + { -3.824901502e-02f, +5.320232763e-01f, +5.502919514e-01f, -3.993598521e-02f }, + { -3.821648782e-02f, +5.316718320e-01f, +5.506430634e-01f, -3.996831805e-02f }, + { -3.818395826e-02f, +5.313203863e-01f, +5.509941643e-01f, -4.000064589e-02f }, + { -3.815142640e-02f, +5.309689393e-01f, +5.513452538e-01f, -4.003296867e-02f }, + { -3.811889228e-02f, +5.306174912e-01f, +5.516963319e-01f, -4.006528635e-02f }, + { -3.808635594e-02f, +5.302660421e-01f, +5.520473984e-01f, -4.009759887e-02f }, + { -3.805381745e-02f, +5.299145923e-01f, +5.523984531e-01f, -4.012990617e-02f }, + { -3.802127683e-02f, +5.295631419e-01f, +5.527494957e-01f, -4.016220822e-02f }, + { -3.798873415e-02f, +5.292116912e-01f, +5.531005262e-01f, -4.019450496e-02f }, + { -3.795618945e-02f, +5.288602402e-01f, +5.534515443e-01f, -4.022679634e-02f }, + { -3.792364277e-02f, +5.285087891e-01f, +5.538025499e-01f, -4.025908231e-02f }, + { -3.789109417e-02f, +5.281573382e-01f, +5.541535428e-01f, -4.029136282e-02f }, + { -3.785854369e-02f, +5.278058876e-01f, +5.545045228e-01f, -4.032363781e-02f }, + { -3.782599137e-02f, +5.274544375e-01f, +5.548554896e-01f, -4.035590724e-02f }, + { -3.779343727e-02f, +5.271029880e-01f, +5.552064433e-01f, -4.038817105e-02f }, + { -3.776088143e-02f, +5.267515394e-01f, +5.555573834e-01f, -4.042042920e-02f }, + { -3.772832390e-02f, +5.264000917e-01f, +5.559083100e-01f, -4.045268163e-02f }, + { -3.769576473e-02f, +5.260486453e-01f, +5.562592228e-01f, -4.048492829e-02f }, + { -3.766320395e-02f, +5.256972002e-01f, +5.566101215e-01f, -4.051716913e-02f }, + { -3.763064163e-02f, +5.253457567e-01f, +5.569610062e-01f, -4.054940409e-02f }, + { -3.759807780e-02f, +5.249943149e-01f, +5.573118764e-01f, -4.058163314e-02f }, + { -3.756551252e-02f, +5.246428750e-01f, +5.576627322e-01f, -4.061385621e-02f }, + { -3.753294582e-02f, +5.242914371e-01f, +5.580135732e-01f, -4.064607325e-02f }, + { -3.750037777e-02f, +5.239400015e-01f, +5.583643994e-01f, -4.067828422e-02f }, + { -3.746780839e-02f, +5.235885684e-01f, +5.587152105e-01f, -4.071048906e-02f }, + { -3.743523774e-02f, +5.232371378e-01f, +5.590660064e-01f, -4.074268771e-02f }, + { -3.740266587e-02f, +5.228857100e-01f, +5.594167868e-01f, -4.077488014e-02f }, + { -3.737009283e-02f, +5.225342852e-01f, +5.597675516e-01f, -4.080706628e-02f }, + { -3.733751865e-02f, +5.221828634e-01f, +5.601183006e-01f, -4.083924608e-02f }, + { -3.730494338e-02f, +5.218314450e-01f, +5.604690337e-01f, -4.087141949e-02f }, + { -3.727236708e-02f, +5.214800301e-01f, +5.608197506e-01f, -4.090358647e-02f }, + { -3.723978979e-02f, +5.211286188e-01f, +5.611704512e-01f, -4.093574695e-02f }, + { -3.720721154e-02f, +5.207772113e-01f, +5.615211353e-01f, -4.096790089e-02f }, + { -3.717463240e-02f, +5.204258079e-01f, +5.618718027e-01f, -4.100004824e-02f }, + { -3.714205241e-02f, +5.200744086e-01f, +5.622224532e-01f, -4.103218893e-02f }, + { -3.710947160e-02f, +5.197230137e-01f, +5.625730867e-01f, -4.106432293e-02f }, + { -3.707689003e-02f, +5.193716233e-01f, +5.629237029e-01f, -4.109645017e-02f }, + { -3.704430775e-02f, +5.190202376e-01f, +5.632743017e-01f, -4.112857061e-02f }, + { -3.701172480e-02f, +5.186688568e-01f, +5.636248829e-01f, -4.116068419e-02f }, + { -3.697914122e-02f, +5.183174810e-01f, +5.639754463e-01f, -4.119279087e-02f }, + { -3.694655706e-02f, +5.179661105e-01f, +5.643259918e-01f, -4.122489058e-02f }, + { -3.691397237e-02f, +5.176147454e-01f, +5.646765191e-01f, -4.125698327e-02f }, + { -3.688138720e-02f, +5.172633859e-01f, +5.650270281e-01f, -4.128906890e-02f }, + { -3.684880158e-02f, +5.169120321e-01f, +5.653775186e-01f, -4.132114741e-02f }, + { -3.681621556e-02f, +5.165606843e-01f, +5.657279905e-01f, -4.135321875e-02f }, + { -3.678362919e-02f, +5.162093426e-01f, +5.660784434e-01f, -4.138528287e-02f }, + { -3.675104252e-02f, +5.158580071e-01f, +5.664288773e-01f, -4.141733970e-02f }, + { -3.671845559e-02f, +5.155066781e-01f, +5.667792920e-01f, -4.144938921e-02f }, + { -3.668586844e-02f, +5.151553558e-01f, +5.671296873e-01f, -4.148143133e-02f }, + { -3.665328113e-02f, +5.148040402e-01f, +5.674800629e-01f, -4.151346602e-02f }, + { -3.662069369e-02f, +5.144527316e-01f, +5.678304188e-01f, -4.154549322e-02f }, + { -3.658810617e-02f, +5.141014302e-01f, +5.681807548e-01f, -4.157751288e-02f }, + { -3.655551861e-02f, +5.137501361e-01f, +5.685310706e-01f, -4.160952494e-02f }, + { -3.652293107e-02f, +5.133988495e-01f, +5.688813660e-01f, -4.164152936e-02f }, + { -3.649034358e-02f, +5.130475706e-01f, +5.692316410e-01f, -4.167352607e-02f }, + { -3.645775620e-02f, +5.126962996e-01f, +5.695818953e-01f, -4.170551503e-02f }, + { -3.642516896e-02f, +5.123450366e-01f, +5.699321287e-01f, -4.173749618e-02f }, + { -3.639258191e-02f, +5.119937817e-01f, +5.702823411e-01f, -4.176946947e-02f }, + { -3.635999510e-02f, +5.116425353e-01f, +5.706325323e-01f, -4.180143485e-02f }, + { -3.632740857e-02f, +5.112912974e-01f, +5.709827020e-01f, -4.183339226e-02f }, + { -3.629482236e-02f, +5.109400682e-01f, +5.713328502e-01f, -4.186534165e-02f }, + { -3.626223652e-02f, +5.105888479e-01f, +5.716829766e-01f, -4.189728297e-02f }, + { -3.622965110e-02f, +5.102376367e-01f, +5.720330810e-01f, -4.192921615e-02f }, + { -3.619706614e-02f, +5.098864348e-01f, +5.723831633e-01f, -4.196114116e-02f }, + { -3.616448168e-02f, +5.095352422e-01f, +5.727332232e-01f, -4.199305793e-02f }, + { -3.613189776e-02f, +5.091840593e-01f, +5.730832607e-01f, -4.202496641e-02f }, + { -3.609931444e-02f, +5.088328861e-01f, +5.734332754e-01f, -4.205686655e-02f }, + { -3.606673176e-02f, +5.084817229e-01f, +5.737832673e-01f, -4.208875830e-02f }, + { -3.603414976e-02f, +5.081305697e-01f, +5.741332362e-01f, -4.212064159e-02f }, + { -3.600156848e-02f, +5.077794269e-01f, +5.744831818e-01f, -4.215251638e-02f }, + { -3.596898797e-02f, +5.074282945e-01f, +5.748331040e-01f, -4.218438261e-02f }, + { -3.593640828e-02f, +5.070771728e-01f, +5.751830026e-01f, -4.221624023e-02f }, + { -3.590382944e-02f, +5.067260619e-01f, +5.755328774e-01f, -4.224808919e-02f }, + { -3.587125150e-02f, +5.063749619e-01f, +5.758827283e-01f, -4.227992942e-02f }, + { -3.583867451e-02f, +5.060238731e-01f, +5.762325550e-01f, -4.231176088e-02f }, + { -3.580609851e-02f, +5.056727956e-01f, +5.765823574e-01f, -4.234358351e-02f }, + { -3.577352355e-02f, +5.053217296e-01f, +5.769321353e-01f, -4.237539726e-02f }, + { -3.574094966e-02f, +5.049706753e-01f, +5.772818885e-01f, -4.240720208e-02f }, + { -3.570837689e-02f, +5.046196329e-01f, +5.776316168e-01f, -4.243899790e-02f }, + { -3.567580528e-02f, +5.042686025e-01f, +5.779813201e-01f, -4.247078467e-02f }, + { -3.564323489e-02f, +5.039175842e-01f, +5.783309981e-01f, -4.250256235e-02f }, + { -3.561066574e-02f, +5.035665783e-01f, +5.786806507e-01f, -4.253433087e-02f }, + { -3.557809789e-02f, +5.032155850e-01f, +5.790302776e-01f, -4.256609018e-02f }, + { -3.554553138e-02f, +5.028646044e-01f, +5.793798788e-01f, -4.259784023e-02f }, + { -3.551296626e-02f, +5.025136366e-01f, +5.797294541e-01f, -4.262958096e-02f }, + { -3.548040255e-02f, +5.021626819e-01f, +5.800790031e-01f, -4.266131231e-02f }, + { -3.544784032e-02f, +5.018117405e-01f, +5.804285259e-01f, -4.269303424e-02f }, + { -3.541527960e-02f, +5.014608124e-01f, +5.807780221e-01f, -4.272474669e-02f }, + { -3.538272044e-02f, +5.011098979e-01f, +5.811274916e-01f, -4.275644960e-02f }, + { -3.535016287e-02f, +5.007589972e-01f, +5.814769342e-01f, -4.278814292e-02f }, + { -3.531760695e-02f, +5.004081103e-01f, +5.818263498e-01f, -4.281982659e-02f }, + { -3.528505271e-02f, +5.000572376e-01f, +5.821757381e-01f, -4.285150055e-02f }, + { -3.525250020e-02f, +4.997063791e-01f, +5.825250990e-01f, -4.288316476e-02f }, + { -3.521994946e-02f, +4.993555351e-01f, +5.828744322e-01f, -4.291481916e-02f }, + { -3.518740053e-02f, +4.990047056e-01f, +5.832237377e-01f, -4.294646369e-02f }, + { -3.515485346e-02f, +4.986538910e-01f, +5.835730151e-01f, -4.297809830e-02f }, + { -3.512230829e-02f, +4.983030913e-01f, +5.839222644e-01f, -4.300972293e-02f }, + { -3.508976506e-02f, +4.979523067e-01f, +5.842714854e-01f, -4.304133752e-02f }, + { -3.505722382e-02f, +4.976015374e-01f, +5.846206778e-01f, -4.307294203e-02f }, + { -3.502468461e-02f, +4.972507836e-01f, +5.849698415e-01f, -4.310453640e-02f }, + { -3.499214746e-02f, +4.969000454e-01f, +5.853189763e-01f, -4.313612056e-02f }, + { -3.495961243e-02f, +4.965493230e-01f, +5.856680820e-01f, -4.316769447e-02f }, + { -3.492707956e-02f, +4.961986166e-01f, +5.860171584e-01f, -4.319925807e-02f }, + { -3.489454888e-02f, +4.958479263e-01f, +5.863662054e-01f, -4.323081130e-02f }, + { -3.486202044e-02f, +4.954972524e-01f, +5.867152227e-01f, -4.326235411e-02f }, + { -3.482949429e-02f, +4.951465949e-01f, +5.870642103e-01f, -4.329388645e-02f }, + { -3.479697046e-02f, +4.947959542e-01f, +5.874131678e-01f, -4.332540825e-02f }, + { -3.476444900e-02f, +4.944453302e-01f, +5.877620952e-01f, -4.335691946e-02f }, + { -3.473192994e-02f, +4.940947233e-01f, +5.881109921e-01f, -4.338842003e-02f }, + { -3.469941334e-02f, +4.937441335e-01f, +5.884598586e-01f, -4.341990989e-02f }, + { -3.466689923e-02f, +4.933935611e-01f, +5.888086942e-01f, -4.345138900e-02f }, + { -3.463438766e-02f, +4.930430062e-01f, +5.891574990e-01f, -4.348285730e-02f }, + { -3.460187866e-02f, +4.926924690e-01f, +5.895062727e-01f, -4.351431473e-02f }, + { -3.456937229e-02f, +4.923419497e-01f, +5.898550151e-01f, -4.354576123e-02f }, + { -3.453686857e-02f, +4.919914484e-01f, +5.902037260e-01f, -4.357719676e-02f }, + { -3.450436756e-02f, +4.916409653e-01f, +5.905524052e-01f, -4.360862124e-02f }, + { -3.447186930e-02f, +4.912905006e-01f, +5.909010526e-01f, -4.364003464e-02f }, + { -3.443937382e-02f, +4.909400544e-01f, +5.912496680e-01f, -4.367143688e-02f }, + { -3.440688117e-02f, +4.905896269e-01f, +5.915982512e-01f, -4.370282792e-02f }, + { -3.437439139e-02f, +4.902392184e-01f, +5.919468020e-01f, -4.373420769e-02f }, + { -3.434190452e-02f, +4.898888288e-01f, +5.922953202e-01f, -4.376557615e-02f }, + { -3.430942060e-02f, +4.895384585e-01f, +5.926438057e-01f, -4.379693323e-02f }, + { -3.427693968e-02f, +4.891881076e-01f, +5.929922582e-01f, -4.382827888e-02f }, + { -3.424446179e-02f, +4.888377763e-01f, +5.933406776e-01f, -4.385961304e-02f }, + { -3.421198698e-02f, +4.884874647e-01f, +5.936890637e-01f, -4.389093566e-02f }, + { -3.417951529e-02f, +4.881371730e-01f, +5.940374163e-01f, -4.392224667e-02f }, + { -3.414704676e-02f, +4.877869014e-01f, +5.943857352e-01f, -4.395354603e-02f }, + { -3.411458143e-02f, +4.874366500e-01f, +5.947340203e-01f, -4.398483367e-02f }, + { -3.408211934e-02f, +4.870864191e-01f, +5.950822713e-01f, -4.401610954e-02f }, + { -3.404966054e-02f, +4.867362087e-01f, +5.954304881e-01f, -4.404737358e-02f }, + { -3.401720506e-02f, +4.863860191e-01f, +5.957786704e-01f, -4.407862573e-02f }, + { -3.398475294e-02f, +4.860358504e-01f, +5.961268182e-01f, -4.410986594e-02f }, + { -3.395230423e-02f, +4.856857028e-01f, +5.964749312e-01f, -4.414109415e-02f }, + { -3.391985897e-02f, +4.853355764e-01f, +5.968230092e-01f, -4.417231031e-02f }, + { -3.388741720e-02f, +4.849854715e-01f, +5.971710521e-01f, -4.420351435e-02f }, + { -3.385497895e-02f, +4.846353882e-01f, +5.975190596e-01f, -4.423470622e-02f }, + { -3.382254427e-02f, +4.842853267e-01f, +5.978670316e-01f, -4.426588586e-02f }, + { -3.379011321e-02f, +4.839352871e-01f, +5.982149679e-01f, -4.429705321e-02f }, + { -3.375768579e-02f, +4.835852697e-01f, +5.985628683e-01f, -4.432820823e-02f }, + { -3.372526206e-02f, +4.832352745e-01f, +5.989107327e-01f, -4.435935084e-02f }, + { -3.369284207e-02f, +4.828853017e-01f, +5.992585607e-01f, -4.439048099e-02f }, + { -3.366042585e-02f, +4.825353516e-01f, +5.996063524e-01f, -4.442159863e-02f }, + { -3.362801344e-02f, +4.821854242e-01f, +5.999541074e-01f, -4.445270370e-02f }, + { -3.359560488e-02f, +4.818355198e-01f, +6.003018256e-01f, -4.448379613e-02f }, + { -3.356320022e-02f, +4.814856386e-01f, +6.006495068e-01f, -4.451487588e-02f }, + { -3.353079949e-02f, +4.811357806e-01f, +6.009971508e-01f, -4.454594288e-02f }, + { -3.349840273e-02f, +4.807859461e-01f, +6.013447575e-01f, -4.457699708e-02f }, + { -3.346600999e-02f, +4.804361352e-01f, +6.016923266e-01f, -4.460803842e-02f }, + { -3.343362130e-02f, +4.800863481e-01f, +6.020398580e-01f, -4.463906683e-02f }, + { -3.340123671e-02f, +4.797365850e-01f, +6.023873514e-01f, -4.467008228e-02f }, + { -3.336885624e-02f, +4.793868460e-01f, +6.027348068e-01f, -4.470108468e-02f }, + { -3.333647996e-02f, +4.790371313e-01f, +6.030822239e-01f, -4.473207400e-02f }, + { -3.330410788e-02f, +4.786874411e-01f, +6.034296025e-01f, -4.476305016e-02f }, + { -3.327174006e-02f, +4.783377756e-01f, +6.037769424e-01f, -4.479401311e-02f }, + { -3.323937653e-02f, +4.779881348e-01f, +6.041242435e-01f, -4.482496280e-02f }, + { -3.320701734e-02f, +4.776385190e-01f, +6.044715056e-01f, -4.485589916e-02f }, + { -3.317466252e-02f, +4.772889284e-01f, +6.048187284e-01f, -4.488682214e-02f }, + { -3.314231211e-02f, +4.769393630e-01f, +6.051659119e-01f, -4.491773168e-02f }, + { -3.310996615e-02f, +4.765898232e-01f, +6.055130558e-01f, -4.494862772e-02f }, + { -3.307762468e-02f, +4.762403090e-01f, +6.058601599e-01f, -4.497951020e-02f }, + { -3.304528775e-02f, +4.758908206e-01f, +6.062072240e-01f, -4.501037906e-02f }, + { -3.301295538e-02f, +4.755413581e-01f, +6.065542481e-01f, -4.504123425e-02f }, + { -3.298062762e-02f, +4.751919219e-01f, +6.069012318e-01f, -4.507207570e-02f }, + { -3.294830451e-02f, +4.748425119e-01f, +6.072481750e-01f, -4.510290336e-02f }, + { -3.291598609e-02f, +4.744931285e-01f, +6.075950775e-01f, -4.513371718e-02f }, + { -3.288367240e-02f, +4.741437716e-01f, +6.079419391e-01f, -4.516451708e-02f }, + { -3.285136347e-02f, +4.737944416e-01f, +6.082887597e-01f, -4.519530301e-02f }, + { -3.281905935e-02f, +4.734451386e-01f, +6.086355390e-01f, -4.522607492e-02f }, + { -3.278676008e-02f, +4.730958628e-01f, +6.089822769e-01f, -4.525683274e-02f }, + { -3.275446568e-02f, +4.727466142e-01f, +6.093289732e-01f, -4.528757642e-02f }, + { -3.272217621e-02f, +4.723973932e-01f, +6.096756276e-01f, -4.531830590e-02f }, + { -3.268989170e-02f, +4.720481998e-01f, +6.100222401e-01f, -4.534902111e-02f }, + { -3.265761219e-02f, +4.716990342e-01f, +6.103688104e-01f, -4.537972200e-02f }, + { -3.262533772e-02f, +4.713498966e-01f, +6.107153384e-01f, -4.541040851e-02f }, + { -3.259306833e-02f, +4.710007872e-01f, +6.110618238e-01f, -4.544108058e-02f }, + { -3.256080405e-02f, +4.706517061e-01f, +6.114082665e-01f, -4.547173816e-02f }, + { -3.252854493e-02f, +4.703026534e-01f, +6.117546663e-01f, -4.550238117e-02f }, + { -3.249629100e-02f, +4.699536294e-01f, +6.121010230e-01f, -4.553300957e-02f }, + { -3.246404230e-02f, +4.696046343e-01f, +6.124473364e-01f, -4.556362330e-02f }, + { -3.243179888e-02f, +4.692556681e-01f, +6.127936063e-01f, -4.559422228e-02f }, + { -3.239956076e-02f, +4.689067311e-01f, +6.131398326e-01f, -4.562480648e-02f }, + { -3.236732799e-02f, +4.685578234e-01f, +6.134860150e-01f, -4.565537582e-02f }, + { -3.233510060e-02f, +4.682089452e-01f, +6.138321534e-01f, -4.568593024e-02f }, + { -3.230287864e-02f, +4.678600966e-01f, +6.141782476e-01f, -4.571646969e-02f }, + { -3.227066214e-02f, +4.675112778e-01f, +6.145242974e-01f, -4.574699411e-02f }, + { -3.223845114e-02f, +4.671624891e-01f, +6.148703026e-01f, -4.577750344e-02f }, + { -3.220624568e-02f, +4.668137304e-01f, +6.152162631e-01f, -4.580799762e-02f }, + { -3.217404580e-02f, +4.664650021e-01f, +6.155621786e-01f, -4.583847658e-02f }, + { -3.214185153e-02f, +4.661163043e-01f, +6.159080490e-01f, -4.586894027e-02f }, + { -3.210966291e-02f, +4.657676371e-01f, +6.162538740e-01f, -4.589938863e-02f }, + { -3.207747998e-02f, +4.654190007e-01f, +6.165996536e-01f, -4.592982160e-02f }, + { -3.204530278e-02f, +4.650703953e-01f, +6.169453874e-01f, -4.596023912e-02f }, + { -3.201313134e-02f, +4.647218210e-01f, +6.172910754e-01f, -4.599064113e-02f }, + { -3.198096571e-02f, +4.643732781e-01f, +6.176367173e-01f, -4.602102757e-02f }, + { -3.194880592e-02f, +4.640247666e-01f, +6.179823129e-01f, -4.605139837e-02f }, + { -3.191665200e-02f, +4.636762867e-01f, +6.183278621e-01f, -4.608175349e-02f }, + { -3.188450401e-02f, +4.633278387e-01f, +6.186733647e-01f, -4.611209285e-02f }, + { -3.185236196e-02f, +4.629794226e-01f, +6.190188205e-01f, -4.614241641e-02f }, + { -3.182022591e-02f, +4.626310387e-01f, +6.193642293e-01f, -4.617272409e-02f }, + { -3.178809589e-02f, +4.622826870e-01f, +6.197095909e-01f, -4.620301584e-02f }, + { -3.175597193e-02f, +4.619343678e-01f, +6.200549052e-01f, -4.623329159e-02f }, + { -3.172385407e-02f, +4.615860812e-01f, +6.204001719e-01f, -4.626355130e-02f }, + { -3.169174236e-02f, +4.612378274e-01f, +6.207453909e-01f, -4.629379489e-02f }, + { -3.165963682e-02f, +4.608896066e-01f, +6.210905619e-01f, -4.632402231e-02f }, + { -3.162753750e-02f, +4.605414189e-01f, +6.214356849e-01f, -4.635423350e-02f }, + { -3.159544443e-02f, +4.601932645e-01f, +6.217807595e-01f, -4.638442839e-02f }, + { -3.156335765e-02f, +4.598451435e-01f, +6.221257857e-01f, -4.641460692e-02f }, + { -3.153127720e-02f, +4.594970561e-01f, +6.224707632e-01f, -4.644476904e-02f }, + { -3.149920310e-02f, +4.591490025e-01f, +6.228156918e-01f, -4.647491468e-02f }, + { -3.146713541e-02f, +4.588009829e-01f, +6.231605715e-01f, -4.650504379e-02f }, + { -3.143507415e-02f, +4.584529973e-01f, +6.235054019e-01f, -4.653515630e-02f }, + { -3.140301937e-02f, +4.581050460e-01f, +6.238501829e-01f, -4.656525215e-02f }, + { -3.137097110e-02f, +4.577571292e-01f, +6.241949143e-01f, -4.659533127e-02f }, + { -3.133892937e-02f, +4.574092469e-01f, +6.245395959e-01f, -4.662539362e-02f }, + { -3.130689423e-02f, +4.570613994e-01f, +6.248842275e-01f, -4.665543913e-02f }, + { -3.127486571e-02f, +4.567135869e-01f, +6.252288091e-01f, -4.668546773e-02f }, + { -3.124284384e-02f, +4.563658094e-01f, +6.255733402e-01f, -4.671547937e-02f }, + { -3.121082867e-02f, +4.560180671e-01f, +6.259178209e-01f, -4.674547399e-02f }, + { -3.117882023e-02f, +4.556703603e-01f, +6.262622508e-01f, -4.677545152e-02f }, + { -3.114681855e-02f, +4.553226890e-01f, +6.266066299e-01f, -4.680541190e-02f }, + { -3.111482367e-02f, +4.549750535e-01f, +6.269509578e-01f, -4.683535507e-02f }, + { -3.108283564e-02f, +4.546274539e-01f, +6.272952345e-01f, -4.686528098e-02f }, + { -3.105085447e-02f, +4.542798904e-01f, +6.276394598e-01f, -4.689518955e-02f }, + { -3.101888022e-02f, +4.539323631e-01f, +6.279836334e-01f, -4.692508073e-02f }, + { -3.098691291e-02f, +4.535848721e-01f, +6.283277552e-01f, -4.695495446e-02f }, + { -3.095495259e-02f, +4.532374178e-01f, +6.286718249e-01f, -4.698481067e-02f }, + { -3.092299929e-02f, +4.528900001e-01f, +6.290158425e-01f, -4.701464930e-02f }, + { -3.089105304e-02f, +4.525426193e-01f, +6.293598077e-01f, -4.704447029e-02f }, + { -3.085911388e-02f, +4.521952756e-01f, +6.297037204e-01f, -4.707427359e-02f }, + { -3.082718185e-02f, +4.518479691e-01f, +6.300475802e-01f, -4.710405912e-02f }, + { -3.079525698e-02f, +4.515006999e-01f, +6.303913872e-01f, -4.713382682e-02f }, + { -3.076333930e-02f, +4.511534683e-01f, +6.307351410e-01f, -4.716357665e-02f }, + { -3.073142887e-02f, +4.508062744e-01f, +6.310788415e-01f, -4.719330852e-02f }, + { -3.069952570e-02f, +4.504591183e-01f, +6.314224885e-01f, -4.722302238e-02f }, + { -3.066762983e-02f, +4.501120002e-01f, +6.317660818e-01f, -4.725271818e-02f }, + { -3.063574131e-02f, +4.497649203e-01f, +6.321096212e-01f, -4.728239584e-02f }, + { -3.060386016e-02f, +4.494178788e-01f, +6.324531066e-01f, -4.731205530e-02f }, + { -3.057198642e-02f, +4.490708757e-01f, +6.327965377e-01f, -4.734169651e-02f }, + { -3.054012013e-02f, +4.487239113e-01f, +6.331399144e-01f, -4.737131940e-02f }, + { -3.050826133e-02f, +4.483769858e-01f, +6.334832365e-01f, -4.740092391e-02f }, + { -3.047641003e-02f, +4.480300992e-01f, +6.338265038e-01f, -4.743050997e-02f }, + { -3.044456630e-02f, +4.476832518e-01f, +6.341697161e-01f, -4.746007752e-02f }, + { -3.041273015e-02f, +4.473364437e-01f, +6.345128733e-01f, -4.748962651e-02f }, + { -3.038090162e-02f, +4.469896750e-01f, +6.348559750e-01f, -4.751915687e-02f }, + { -3.034908075e-02f, +4.466429460e-01f, +6.351990212e-01f, -4.754866853e-02f }, + { -3.031726757e-02f, +4.462962568e-01f, +6.355420117e-01f, -4.757816144e-02f }, + { -3.028546212e-02f, +4.459496076e-01f, +6.358849463e-01f, -4.760763553e-02f }, + { -3.025366443e-02f, +4.456029984e-01f, +6.362278248e-01f, -4.763709074e-02f }, + { -3.022187454e-02f, +4.452564296e-01f, +6.365706469e-01f, -4.766652701e-02f }, + { -3.019009249e-02f, +4.449099012e-01f, +6.369134126e-01f, -4.769594427e-02f }, + { -3.015831830e-02f, +4.445634133e-01f, +6.372561217e-01f, -4.772534246e-02f }, + { -3.012655201e-02f, +4.442169663e-01f, +6.375987738e-01f, -4.775472152e-02f }, + { -3.009479366e-02f, +4.438705601e-01f, +6.379413690e-01f, -4.778408139e-02f }, + { -3.006304328e-02f, +4.435241951e-01f, +6.382839069e-01f, -4.781342200e-02f }, + { -3.003130090e-02f, +4.431778713e-01f, +6.386263874e-01f, -4.784274329e-02f }, + { -2.999956656e-02f, +4.428315888e-01f, +6.389688103e-01f, -4.787204520e-02f }, + { -2.996784030e-02f, +4.424853480e-01f, +6.393111754e-01f, -4.790132766e-02f }, + { -2.993612215e-02f, +4.421391489e-01f, +6.396534826e-01f, -4.793059062e-02f }, + { -2.990441214e-02f, +4.417929916e-01f, +6.399957316e-01f, -4.795983400e-02f }, + { -2.987271031e-02f, +4.414468764e-01f, +6.403379223e-01f, -4.798905775e-02f }, + { -2.984101669e-02f, +4.411008034e-01f, +6.406800544e-01f, -4.801826180e-02f }, + { -2.980933131e-02f, +4.407547728e-01f, +6.410221278e-01f, -4.804744609e-02f }, + { -2.977765422e-02f, +4.404087847e-01f, +6.413641423e-01f, -4.807661056e-02f }, + { -2.974598544e-02f, +4.400628392e-01f, +6.417060978e-01f, -4.810575513e-02f }, + { -2.971432501e-02f, +4.397169367e-01f, +6.420479939e-01f, -4.813487976e-02f }, + { -2.968267296e-02f, +4.393710771e-01f, +6.423898306e-01f, -4.816398438e-02f }, + { -2.965102932e-02f, +4.390252607e-01f, +6.427316077e-01f, -4.819306891e-02f }, + { -2.961939414e-02f, +4.386794876e-01f, +6.430733249e-01f, -4.822213331e-02f }, + { -2.958776743e-02f, +4.383337580e-01f, +6.434149821e-01f, -4.825117750e-02f }, + { -2.955614925e-02f, +4.379880720e-01f, +6.437565791e-01f, -4.828020142e-02f }, + { -2.952453962e-02f, +4.376424298e-01f, +6.440981157e-01f, -4.830920501e-02f }, + { -2.949293857e-02f, +4.372968316e-01f, +6.444395917e-01f, -4.833818821e-02f }, + { -2.946134614e-02f, +4.369512775e-01f, +6.447810070e-01f, -4.836715094e-02f }, + { -2.942976236e-02f, +4.366057677e-01f, +6.451223613e-01f, -4.839609316e-02f }, + { -2.939818727e-02f, +4.362603023e-01f, +6.454636545e-01f, -4.842501479e-02f }, + { -2.936662089e-02f, +4.359148815e-01f, +6.458048863e-01f, -4.845391577e-02f }, + { -2.933506327e-02f, +4.355695055e-01f, +6.461460567e-01f, -4.848279603e-02f }, + { -2.930351443e-02f, +4.352241743e-01f, +6.464871653e-01f, -4.851165552e-02f }, + { -2.927197441e-02f, +4.348788883e-01f, +6.468282121e-01f, -4.854049416e-02f }, + { -2.924044325e-02f, +4.345336475e-01f, +6.471691968e-01f, -4.856931190e-02f }, + { -2.920892096e-02f, +4.341884520e-01f, +6.475101192e-01f, -4.859810867e-02f }, + { -2.917740760e-02f, +4.338433021e-01f, +6.478509792e-01f, -4.862688440e-02f }, + { -2.914590319e-02f, +4.334981980e-01f, +6.481917765e-01f, -4.865563904e-02f }, + { -2.911440776e-02f, +4.331531396e-01f, +6.485325111e-01f, -4.868437251e-02f }, + { -2.908292135e-02f, +4.328081273e-01f, +6.488731826e-01f, -4.871308476e-02f }, + { -2.905144399e-02f, +4.324631612e-01f, +6.492137910e-01f, -4.874177572e-02f }, + { -2.901997572e-02f, +4.321182414e-01f, +6.495543359e-01f, -4.877044532e-02f }, + { -2.898851656e-02f, +4.317733681e-01f, +6.498948173e-01f, -4.879909350e-02f }, + { -2.895706654e-02f, +4.314285415e-01f, +6.502352350e-01f, -4.882772020e-02f }, + { -2.892562571e-02f, +4.310837617e-01f, +6.505755887e-01f, -4.885632535e-02f }, + { -2.889419410e-02f, +4.307390288e-01f, +6.509158783e-01f, -4.888490889e-02f }, + { -2.886277173e-02f, +4.303943430e-01f, +6.512561036e-01f, -4.891347075e-02f }, + { -2.883135864e-02f, +4.300497046e-01f, +6.515962644e-01f, -4.894201087e-02f }, + { -2.879995486e-02f, +4.297051136e-01f, +6.519363605e-01f, -4.897052918e-02f }, + { -2.876856043e-02f, +4.293605701e-01f, +6.522763918e-01f, -4.899902562e-02f }, + { -2.873717537e-02f, +4.290160745e-01f, +6.526163580e-01f, -4.902750012e-02f }, + { -2.870579972e-02f, +4.286716267e-01f, +6.529562589e-01f, -4.905595262e-02f }, + { -2.867443352e-02f, +4.283272270e-01f, +6.532960945e-01f, -4.908438306e-02f }, + { -2.864307678e-02f, +4.279828756e-01f, +6.536358644e-01f, -4.911279137e-02f }, + { -2.861172956e-02f, +4.276385725e-01f, +6.539755685e-01f, -4.914117748e-02f }, + { -2.858039187e-02f, +4.272943179e-01f, +6.543152066e-01f, -4.916954133e-02f }, + { -2.854906375e-02f, +4.269501121e-01f, +6.546547786e-01f, -4.919788286e-02f }, + { -2.851774523e-02f, +4.266059551e-01f, +6.549942842e-01f, -4.922620200e-02f }, + { -2.848643635e-02f, +4.262618471e-01f, +6.553337232e-01f, -4.925449868e-02f }, + { -2.845513713e-02f, +4.259177883e-01f, +6.556730955e-01f, -4.928277284e-02f }, + { -2.842384761e-02f, +4.255737788e-01f, +6.560124009e-01f, -4.931102441e-02f }, + { -2.839256782e-02f, +4.252298187e-01f, +6.563516392e-01f, -4.933925334e-02f }, + { -2.836129779e-02f, +4.248859083e-01f, +6.566908102e-01f, -4.936745955e-02f }, + { -2.833003756e-02f, +4.245420477e-01f, +6.570299137e-01f, -4.939564297e-02f }, + { -2.829878715e-02f, +4.241982371e-01f, +6.573689495e-01f, -4.942380356e-02f }, + { -2.826754660e-02f, +4.238544765e-01f, +6.577079175e-01f, -4.945194122e-02f }, + { -2.823631593e-02f, +4.235107662e-01f, +6.580468175e-01f, -4.948005591e-02f }, + { -2.820509519e-02f, +4.231671063e-01f, +6.583856492e-01f, -4.950814756e-02f }, + { -2.817388439e-02f, +4.228234970e-01f, +6.587244125e-01f, -4.953621610e-02f }, + { -2.814268358e-02f, +4.224799384e-01f, +6.590631072e-01f, -4.956426147e-02f }, + { -2.811149279e-02f, +4.221364307e-01f, +6.594017331e-01f, -4.959228359e-02f }, + { -2.808031203e-02f, +4.217929740e-01f, +6.597402901e-01f, -4.962028241e-02f }, + { -2.804914136e-02f, +4.214495685e-01f, +6.600787779e-01f, -4.964825786e-02f }, + { -2.801798079e-02f, +4.211062143e-01f, +6.604171963e-01f, -4.967620987e-02f }, + { -2.798683036e-02f, +4.207629117e-01f, +6.607555453e-01f, -4.970413838e-02f }, + { -2.795569010e-02f, +4.204196607e-01f, +6.610938245e-01f, -4.973204333e-02f }, + { -2.792456005e-02f, +4.200764615e-01f, +6.614320337e-01f, -4.975992463e-02f }, + { -2.789344022e-02f, +4.197333143e-01f, +6.617701729e-01f, -4.978778224e-02f }, + { -2.786233066e-02f, +4.193902192e-01f, +6.621082419e-01f, -4.981561608e-02f }, + { -2.783123139e-02f, +4.190471763e-01f, +6.624462403e-01f, -4.984342609e-02f }, + { -2.780014245e-02f, +4.187041859e-01f, +6.627841681e-01f, -4.987121220e-02f }, + { -2.776906386e-02f, +4.183612481e-01f, +6.631220251e-01f, -4.989897435e-02f }, + { -2.773799566e-02f, +4.180183630e-01f, +6.634598110e-01f, -4.992671247e-02f }, + { -2.770693788e-02f, +4.176755308e-01f, +6.637975258e-01f, -4.995442649e-02f }, + { -2.767589054e-02f, +4.173327517e-01f, +6.641351691e-01f, -4.998211634e-02f }, + { -2.764485368e-02f, +4.169900257e-01f, +6.644727409e-01f, -5.000978197e-02f }, + { -2.761382734e-02f, +4.166473531e-01f, +6.648102408e-01f, -5.003742330e-02f }, + { -2.758281153e-02f, +4.163047340e-01f, +6.651476689e-01f, -5.006504027e-02f }, + { -2.755180629e-02f, +4.159621686e-01f, +6.654850247e-01f, -5.009263282e-02f }, + { -2.752081165e-02f, +4.156196570e-01f, +6.658223083e-01f, -5.012020087e-02f }, + { -2.748982764e-02f, +4.152771994e-01f, +6.661595193e-01f, -5.014774435e-02f }, + { -2.745885430e-02f, +4.149347959e-01f, +6.664966577e-01f, -5.017526321e-02f }, + { -2.742789164e-02f, +4.145924466e-01f, +6.668337231e-01f, -5.020275738e-02f }, + { -2.739693971e-02f, +4.142501518e-01f, +6.671707155e-01f, -5.023022678e-02f }, + { -2.736599852e-02f, +4.139079116e-01f, +6.675076346e-01f, -5.025767136e-02f }, + { -2.733506812e-02f, +4.135657261e-01f, +6.678444802e-01f, -5.028509105e-02f }, + { -2.730414853e-02f, +4.132235955e-01f, +6.681812522e-01f, -5.031248577e-02f }, + { -2.727323979e-02f, +4.128815199e-01f, +6.685179504e-01f, -5.033985546e-02f }, + { -2.724234191e-02f, +4.125394995e-01f, +6.688545746e-01f, -5.036720007e-02f }, + { -2.721145493e-02f, +4.121975345e-01f, +6.691911246e-01f, -5.039451951e-02f }, + { -2.718057889e-02f, +4.118556250e-01f, +6.695276002e-01f, -5.042181372e-02f }, + { -2.714971381e-02f, +4.115137711e-01f, +6.698640012e-01f, -5.044908263e-02f }, + { -2.711885972e-02f, +4.111719731e-01f, +6.702003275e-01f, -5.047632619e-02f }, + { -2.708801665e-02f, +4.108302310e-01f, +6.705365788e-01f, -5.050354431e-02f }, + { -2.705718463e-02f, +4.104885450e-01f, +6.708727551e-01f, -5.053073694e-02f }, + { -2.702636369e-02f, +4.101469153e-01f, +6.712088560e-01f, -5.055790401e-02f }, + { -2.699555385e-02f, +4.098053420e-01f, +6.715448814e-01f, -5.058504544e-02f }, + { -2.696475516e-02f, +4.094638252e-01f, +6.718808311e-01f, -5.061216118e-02f }, + { -2.693396764e-02f, +4.091223652e-01f, +6.722167049e-01f, -5.063925115e-02f }, + { -2.690319131e-02f, +4.087809621e-01f, +6.725525027e-01f, -5.066631529e-02f }, + { -2.687242621e-02f, +4.084396159e-01f, +6.728882242e-01f, -5.069335353e-02f }, + { -2.684167237e-02f, +4.080983270e-01f, +6.732238693e-01f, -5.072036581e-02f }, + { -2.681092981e-02f, +4.077570954e-01f, +6.735594378e-01f, -5.074735205e-02f }, + { -2.678019856e-02f, +4.074159212e-01f, +6.738949295e-01f, -5.077431218e-02f }, + { -2.674947866e-02f, +4.070748047e-01f, +6.742303442e-01f, -5.080124615e-02f }, + { -2.671877013e-02f, +4.067337460e-01f, +6.745656817e-01f, -5.082815388e-02f }, + { -2.668807301e-02f, +4.063927452e-01f, +6.749009418e-01f, -5.085503531e-02f }, + { -2.665738731e-02f, +4.060518025e-01f, +6.752361244e-01f, -5.088189036e-02f }, + { -2.662671308e-02f, +4.057109180e-01f, +6.755712292e-01f, -5.090871897e-02f }, + { -2.659605033e-02f, +4.053700919e-01f, +6.759062562e-01f, -5.093552108e-02f }, + { -2.656539910e-02f, +4.050293243e-01f, +6.762412050e-01f, -5.096229661e-02f }, + { -2.653475941e-02f, +4.046886155e-01f, +6.765760755e-01f, -5.098904550e-02f }, + { -2.650413130e-02f, +4.043479654e-01f, +6.769108675e-01f, -5.101576767e-02f }, + { -2.647351480e-02f, +4.040073744e-01f, +6.772455809e-01f, -5.104246307e-02f }, + { -2.644290992e-02f, +4.036668425e-01f, +6.775802154e-01f, -5.106913162e-02f }, + { -2.641231671e-02f, +4.033263699e-01f, +6.779147709e-01f, -5.109577326e-02f }, + { -2.638173519e-02f, +4.029859567e-01f, +6.782492471e-01f, -5.112238791e-02f }, + { -2.635116538e-02f, +4.026456031e-01f, +6.785836440e-01f, -5.114897551e-02f }, + { -2.632060732e-02f, +4.023053093e-01f, +6.789179612e-01f, -5.117553600e-02f }, + { -2.629006103e-02f, +4.019650754e-01f, +6.792521986e-01f, -5.120206929e-02f }, + { -2.625952655e-02f, +4.016249015e-01f, +6.795863561e-01f, -5.122857533e-02f }, + { -2.622900390e-02f, +4.012847878e-01f, +6.799204334e-01f, -5.125505405e-02f }, + { -2.619849310e-02f, +4.009447344e-01f, +6.802544304e-01f, -5.128150537e-02f }, + { -2.616799420e-02f, +4.006047415e-01f, +6.805883468e-01f, -5.130792924e-02f }, + { -2.613750720e-02f, +4.002648093e-01f, +6.809221825e-01f, -5.133432558e-02f }, + { -2.610703216e-02f, +3.999249379e-01f, +6.812559373e-01f, -5.136069431e-02f }, + { -2.607656908e-02f, +3.995851274e-01f, +6.815896110e-01f, -5.138703539e-02f }, + { -2.604611800e-02f, +3.992453779e-01f, +6.819232035e-01f, -5.141334873e-02f }, + { -2.601567895e-02f, +3.989056898e-01f, +6.822567144e-01f, -5.143963427e-02f }, + { -2.598525196e-02f, +3.985660630e-01f, +6.825901438e-01f, -5.146589193e-02f }, + { -2.595483705e-02f, +3.982264977e-01f, +6.829234913e-01f, -5.149212166e-02f }, + { -2.592443425e-02f, +3.978869942e-01f, +6.832567567e-01f, -5.151832338e-02f }, + { -2.589404358e-02f, +3.975475525e-01f, +6.835899400e-01f, -5.154449702e-02f }, + { -2.586366508e-02f, +3.972081727e-01f, +6.839230409e-01f, -5.157064252e-02f }, + { -2.583329878e-02f, +3.968688551e-01f, +6.842560591e-01f, -5.159675980e-02f }, + { -2.580294470e-02f, +3.965295998e-01f, +6.845889947e-01f, -5.162284880e-02f }, + { -2.577260287e-02f, +3.961904069e-01f, +6.849218472e-01f, -5.164890945e-02f }, + { -2.574227331e-02f, +3.958512766e-01f, +6.852546167e-01f, -5.167494167e-02f }, + { -2.571195605e-02f, +3.955122090e-01f, +6.855873028e-01f, -5.170094541e-02f }, + { -2.568165113e-02f, +3.951732042e-01f, +6.859199054e-01f, -5.172692059e-02f }, + { -2.565135856e-02f, +3.948342626e-01f, +6.862524243e-01f, -5.175286714e-02f }, + { -2.562107838e-02f, +3.944953840e-01f, +6.865848593e-01f, -5.177878499e-02f }, + { -2.559081062e-02f, +3.941565688e-01f, +6.869172102e-01f, -5.180467408e-02f }, + { -2.556055529e-02f, +3.938178171e-01f, +6.872494769e-01f, -5.183053433e-02f }, + { -2.553031243e-02f, +3.934791290e-01f, +6.875816592e-01f, -5.185636568e-02f }, + { -2.550008206e-02f, +3.931405046e-01f, +6.879137568e-01f, -5.188216805e-02f }, + { -2.546986421e-02f, +3.928019442e-01f, +6.882457697e-01f, -5.190794138e-02f }, + { -2.543965891e-02f, +3.924634478e-01f, +6.885776975e-01f, -5.193368560e-02f }, + { -2.540946619e-02f, +3.921250156e-01f, +6.889095401e-01f, -5.195940064e-02f }, + { -2.537928607e-02f, +3.917866477e-01f, +6.892412974e-01f, -5.198508643e-02f }, + { -2.534911857e-02f, +3.914483444e-01f, +6.895729691e-01f, -5.201074290e-02f }, + { -2.531896373e-02f, +3.911101057e-01f, +6.899045550e-01f, -5.203636997e-02f }, + { -2.528882157e-02f, +3.907719318e-01f, +6.902360551e-01f, -5.206196759e-02f }, + { -2.525869212e-02f, +3.904338229e-01f, +6.905674690e-01f, -5.208753568e-02f }, + { -2.522857541e-02f, +3.900957790e-01f, +6.908987966e-01f, -5.211307417e-02f }, + { -2.519847146e-02f, +3.897578004e-01f, +6.912300378e-01f, -5.213858300e-02f }, + { -2.516838029e-02f, +3.894198871e-01f, +6.915611922e-01f, -5.216406208e-02f }, + { -2.513830194e-02f, +3.890820394e-01f, +6.918922599e-01f, -5.218951136e-02f }, + { -2.510823643e-02f, +3.887442574e-01f, +6.922232404e-01f, -5.221493076e-02f }, + { -2.507818379e-02f, +3.884065411e-01f, +6.925541338e-01f, -5.224032022e-02f }, + { -2.504814403e-02f, +3.880688909e-01f, +6.928849397e-01f, -5.226567965e-02f }, + { -2.501811720e-02f, +3.877313067e-01f, +6.932156580e-01f, -5.229100900e-02f }, + { -2.498810332e-02f, +3.873937888e-01f, +6.935462886e-01f, -5.231630820e-02f }, + { -2.495810241e-02f, +3.870563373e-01f, +6.938768311e-01f, -5.234157717e-02f }, + { -2.492811449e-02f, +3.867189524e-01f, +6.942072855e-01f, -5.236681584e-02f }, + { -2.489813960e-02f, +3.863816342e-01f, +6.945376516e-01f, -5.239202415e-02f }, + { -2.486817776e-02f, +3.860443828e-01f, +6.948679292e-01f, -5.241720201e-02f }, + { -2.483822900e-02f, +3.857071983e-01f, +6.951981180e-01f, -5.244234938e-02f }, + { -2.480829333e-02f, +3.853700811e-01f, +6.955282179e-01f, -5.246746616e-02f }, + { -2.477837080e-02f, +3.850330311e-01f, +6.958582288e-01f, -5.249255230e-02f }, + { -2.474846141e-02f, +3.846960485e-01f, +6.961881503e-01f, -5.251760772e-02f }, + { -2.471856521e-02f, +3.843591335e-01f, +6.965179824e-01f, -5.254263236e-02f }, + { -2.468868221e-02f, +3.840222862e-01f, +6.968477249e-01f, -5.256762613e-02f }, + { -2.465881245e-02f, +3.836855068e-01f, +6.971773775e-01f, -5.259258898e-02f }, + { -2.462895594e-02f, +3.833487953e-01f, +6.975069402e-01f, -5.261752083e-02f }, + { -2.459911271e-02f, +3.830121520e-01f, +6.978364126e-01f, -5.264242161e-02f }, + { -2.456928278e-02f, +3.826755771e-01f, +6.981657947e-01f, -5.266729126e-02f }, + { -2.453946619e-02f, +3.823390705e-01f, +6.984950861e-01f, -5.269212969e-02f }, + { -2.450966296e-02f, +3.820026325e-01f, +6.988242869e-01f, -5.271693684e-02f }, + { -2.447987311e-02f, +3.816662633e-01f, +6.991533966e-01f, -5.274171264e-02f }, + { -2.445009667e-02f, +3.813299629e-01f, +6.994824153e-01f, -5.276645702e-02f }, + { -2.442033366e-02f, +3.809937316e-01f, +6.998113426e-01f, -5.279116991e-02f }, + { -2.439058412e-02f, +3.806575694e-01f, +7.001401785e-01f, -5.281585123e-02f }, + { -2.436084805e-02f, +3.803214765e-01f, +7.004689226e-01f, -5.284050092e-02f }, + { -2.433112550e-02f, +3.799854530e-01f, +7.007975749e-01f, -5.286511890e-02f }, + { -2.430141648e-02f, +3.796494992e-01f, +7.011261352e-01f, -5.288970511e-02f }, + { -2.427172102e-02f, +3.793136151e-01f, +7.014546032e-01f, -5.291425948e-02f }, + { -2.424203914e-02f, +3.789778008e-01f, +7.017829788e-01f, -5.293878193e-02f }, + { -2.421237087e-02f, +3.786420566e-01f, +7.021112617e-01f, -5.296327238e-02f }, + { -2.418271624e-02f, +3.783063825e-01f, +7.024394519e-01f, -5.298773078e-02f }, + { -2.415307527e-02f, +3.779707788e-01f, +7.027675491e-01f, -5.301215705e-02f }, + { -2.412344797e-02f, +3.776352455e-01f, +7.030955531e-01f, -5.303655112e-02f }, + { -2.409383439e-02f, +3.772997828e-01f, +7.034234638e-01f, -5.306091292e-02f }, + { -2.406423454e-02f, +3.769643909e-01f, +7.037512810e-01f, -5.308524237e-02f }, + { -2.403464845e-02f, +3.766290698e-01f, +7.040790044e-01f, -5.310953941e-02f }, + { -2.400507614e-02f, +3.762938197e-01f, +7.044066339e-01f, -5.313380397e-02f }, + { -2.397551763e-02f, +3.759586408e-01f, +7.047341694e-01f, -5.315803596e-02f }, + { -2.394597296e-02f, +3.756235333e-01f, +7.050616105e-01f, -5.318223533e-02f }, + { -2.391644214e-02f, +3.752884971e-01f, +7.053889572e-01f, -5.320640200e-02f }, + { -2.388692520e-02f, +3.749535326e-01f, +7.057162093e-01f, -5.323053590e-02f }, + { -2.385742216e-02f, +3.746186398e-01f, +7.060433665e-01f, -5.325463695e-02f }, + { -2.382793305e-02f, +3.742838189e-01f, +7.063704287e-01f, -5.327870509e-02f }, + { -2.379845790e-02f, +3.739490700e-01f, +7.066973957e-01f, -5.330274025e-02f }, + { -2.376899672e-02f, +3.736143933e-01f, +7.070242673e-01f, -5.332674235e-02f }, + { -2.373954954e-02f, +3.732797888e-01f, +7.073510434e-01f, -5.335071132e-02f }, + { -2.371011639e-02f, +3.729452569e-01f, +7.076777237e-01f, -5.337464709e-02f }, + { -2.368069728e-02f, +3.726107975e-01f, +7.080043080e-01f, -5.339854959e-02f }, + { -2.365129225e-02f, +3.722764108e-01f, +7.083307963e-01f, -5.342241875e-02f }, + { -2.362190131e-02f, +3.719420970e-01f, +7.086571882e-01f, -5.344625449e-02f }, + { -2.359252450e-02f, +3.716078562e-01f, +7.089834836e-01f, -5.347005675e-02f }, + { -2.356316182e-02f, +3.712736886e-01f, +7.093096823e-01f, -5.349382545e-02f }, + { -2.353381332e-02f, +3.709395943e-01f, +7.096357842e-01f, -5.351756051e-02f }, + { -2.350447901e-02f, +3.706055734e-01f, +7.099617891e-01f, -5.354126188e-02f }, + { -2.347515891e-02f, +3.702716261e-01f, +7.102876966e-01f, -5.356492947e-02f }, + { -2.344585305e-02f, +3.699377525e-01f, +7.106135068e-01f, -5.358856321e-02f }, + { -2.341656145e-02f, +3.696039527e-01f, +7.109392194e-01f, -5.361216304e-02f }, + { -2.338728414e-02f, +3.692702270e-01f, +7.112648342e-01f, -5.363572888e-02f }, + { -2.335802114e-02f, +3.689365754e-01f, +7.115903510e-01f, -5.365926065e-02f }, + { -2.332877247e-02f, +3.686029981e-01f, +7.119157697e-01f, -5.368275829e-02f }, + { -2.329953816e-02f, +3.682694952e-01f, +7.122410900e-01f, -5.370622173e-02f }, + { -2.327031823e-02f, +3.679360669e-01f, +7.125663118e-01f, -5.372965088e-02f }, + { -2.324111270e-02f, +3.676027132e-01f, +7.128914348e-01f, -5.375304569e-02f }, + { -2.321192159e-02f, +3.672694344e-01f, +7.132164590e-01f, -5.377640607e-02f }, + { -2.318274493e-02f, +3.669362306e-01f, +7.135413841e-01f, -5.379973196e-02f }, + { -2.315358275e-02f, +3.666031020e-01f, +7.138662100e-01f, -5.382302328e-02f }, + { -2.312443506e-02f, +3.662700485e-01f, +7.141909364e-01f, -5.384627996e-02f }, + { -2.309530189e-02f, +3.659370705e-01f, +7.145155631e-01f, -5.386950193e-02f }, + { -2.306618326e-02f, +3.656041680e-01f, +7.148400900e-01f, -5.389268911e-02f }, + { -2.303707919e-02f, +3.652713412e-01f, +7.151645170e-01f, -5.391584144e-02f }, + { -2.300798972e-02f, +3.649385903e-01f, +7.154888437e-01f, -5.393895884e-02f }, + { -2.297891485e-02f, +3.646059152e-01f, +7.158130701e-01f, -5.396204124e-02f }, + { -2.294985461e-02f, +3.642733163e-01f, +7.161371959e-01f, -5.398508856e-02f }, + { -2.292080903e-02f, +3.639407936e-01f, +7.164612210e-01f, -5.400810073e-02f }, + { -2.289177812e-02f, +3.636083473e-01f, +7.167851452e-01f, -5.403107768e-02f }, + { -2.286276192e-02f, +3.632759775e-01f, +7.171089682e-01f, -5.405401935e-02f }, + { -2.283376044e-02f, +3.629436844e-01f, +7.174326900e-01f, -5.407692564e-02f }, + { -2.280477371e-02f, +3.626114681e-01f, +7.177563103e-01f, -5.409979650e-02f }, + { -2.277580174e-02f, +3.622793287e-01f, +7.180798289e-01f, -5.412263185e-02f }, + { -2.274684456e-02f, +3.619472663e-01f, +7.184032457e-01f, -5.414543161e-02f }, + { -2.271790220e-02f, +3.616152812e-01f, +7.187265605e-01f, -5.416819572e-02f }, + { -2.268897467e-02f, +3.612833734e-01f, +7.190497730e-01f, -5.419092410e-02f }, + { -2.266006200e-02f, +3.609515431e-01f, +7.193728831e-01f, -5.421361667e-02f }, + { -2.263116421e-02f, +3.606197904e-01f, +7.196958907e-01f, -5.423627337e-02f }, + { -2.260228132e-02f, +3.602881155e-01f, +7.200187955e-01f, -5.425889412e-02f }, + { -2.257341336e-02f, +3.599565185e-01f, +7.203415974e-01f, -5.428147885e-02f }, + { -2.254456034e-02f, +3.596249995e-01f, +7.206642961e-01f, -5.430402749e-02f }, + { -2.251572229e-02f, +3.592935587e-01f, +7.209868915e-01f, -5.432653996e-02f }, + { -2.248689923e-02f, +3.589621962e-01f, +7.213093834e-01f, -5.434901618e-02f }, + { -2.245809118e-02f, +3.586309121e-01f, +7.216317716e-01f, -5.437145610e-02f }, + { -2.242929816e-02f, +3.582997067e-01f, +7.219540560e-01f, -5.439385962e-02f }, + { -2.240052021e-02f, +3.579685799e-01f, +7.222762363e-01f, -5.441622669e-02f }, + { -2.237175733e-02f, +3.576375321e-01f, +7.225983123e-01f, -5.443855722e-02f }, + { -2.234300954e-02f, +3.573065632e-01f, +7.229202840e-01f, -5.446085115e-02f }, + { -2.231427688e-02f, +3.569756734e-01f, +7.232421510e-01f, -5.448310839e-02f }, + { -2.228555936e-02f, +3.566448630e-01f, +7.235639133e-01f, -5.450532888e-02f }, + { -2.225685701e-02f, +3.563141319e-01f, +7.238855706e-01f, -5.452751254e-02f }, + { -2.222816984e-02f, +3.559834804e-01f, +7.242071227e-01f, -5.454965931e-02f }, + { -2.219949788e-02f, +3.556529085e-01f, +7.245285695e-01f, -5.457176909e-02f }, + { -2.217084115e-02f, +3.553224165e-01f, +7.248499108e-01f, -5.459384183e-02f }, + { -2.214219967e-02f, +3.549920045e-01f, +7.251711464e-01f, -5.461587745e-02f }, + { -2.211357346e-02f, +3.546616725e-01f, +7.254922761e-01f, -5.463787587e-02f }, + { -2.208496254e-02f, +3.543314207e-01f, +7.258132997e-01f, -5.465983703e-02f }, + { -2.205636694e-02f, +3.540012493e-01f, +7.261342170e-01f, -5.468176084e-02f }, + { -2.202778667e-02f, +3.536711584e-01f, +7.264550280e-01f, -5.470364724e-02f }, + { -2.199922176e-02f, +3.533411482e-01f, +7.267757323e-01f, -5.472549614e-02f }, + { -2.197067223e-02f, +3.530112187e-01f, +7.270963298e-01f, -5.474730748e-02f }, + { -2.194213809e-02f, +3.526813701e-01f, +7.274168203e-01f, -5.476908119e-02f }, + { -2.191361938e-02f, +3.523516026e-01f, +7.277372036e-01f, -5.479081718e-02f }, + { -2.188511610e-02f, +3.520219162e-01f, +7.280574796e-01f, -5.481251539e-02f }, + { -2.185662829e-02f, +3.516923111e-01f, +7.283776481e-01f, -5.483417574e-02f }, + { -2.182815596e-02f, +3.513627875e-01f, +7.286977088e-01f, -5.485579815e-02f }, + { -2.179969913e-02f, +3.510333455e-01f, +7.290176616e-01f, -5.487738256e-02f }, + { -2.177125783e-02f, +3.507039852e-01f, +7.293375064e-01f, -5.489892888e-02f }, + { -2.174283207e-02f, +3.503747067e-01f, +7.296572429e-01f, -5.492043705e-02f }, + { -2.171442188e-02f, +3.500455102e-01f, +7.299768709e-01f, -5.494190699e-02f }, + { -2.168602727e-02f, +3.497163959e-01f, +7.302963903e-01f, -5.496333863e-02f }, + { -2.165764827e-02f, +3.493873638e-01f, +7.306158008e-01f, -5.498473189e-02f }, + { -2.162928490e-02f, +3.490584141e-01f, +7.309351024e-01f, -5.500608670e-02f }, + { -2.160093718e-02f, +3.487295469e-01f, +7.312542948e-01f, -5.502740298e-02f }, + { -2.157260512e-02f, +3.484007623e-01f, +7.315733778e-01f, -5.504868065e-02f }, + { -2.154428875e-02f, +3.480720606e-01f, +7.318923513e-01f, -5.506991966e-02f }, + { -2.151598810e-02f, +3.477434418e-01f, +7.322112150e-01f, -5.509111991e-02f }, + { -2.148770317e-02f, +3.474149060e-01f, +7.325299688e-01f, -5.511228134e-02f }, + { -2.145943399e-02f, +3.470864534e-01f, +7.328486125e-01f, -5.513340388e-02f }, + { -2.143118058e-02f, +3.467580842e-01f, +7.331671459e-01f, -5.515448744e-02f }, + { -2.140294296e-02f, +3.464297984e-01f, +7.334855689e-01f, -5.517553195e-02f }, + { -2.137472115e-02f, +3.461015962e-01f, +7.338038812e-01f, -5.519653734e-02f }, + { -2.134651517e-02f, +3.457734778e-01f, +7.341220827e-01f, -5.521750353e-02f }, + { -2.131832504e-02f, +3.454454432e-01f, +7.344401732e-01f, -5.523843046e-02f }, + { -2.129015078e-02f, +3.451174926e-01f, +7.347581525e-01f, -5.525931803e-02f }, + { -2.126199241e-02f, +3.447896261e-01f, +7.350760204e-01f, -5.528016619e-02f }, + { -2.123384995e-02f, +3.444618439e-01f, +7.353937768e-01f, -5.530097485e-02f }, + { -2.120572342e-02f, +3.441341461e-01f, +7.357114214e-01f, -5.532174395e-02f }, + { -2.117761283e-02f, +3.438065327e-01f, +7.360289541e-01f, -5.534247339e-02f }, + { -2.114951822e-02f, +3.434790041e-01f, +7.363463747e-01f, -5.536316312e-02f }, + { -2.112143960e-02f, +3.431515602e-01f, +7.366636830e-01f, -5.538381306e-02f }, + { -2.109337699e-02f, +3.428242013e-01f, +7.369808788e-01f, -5.540442312e-02f }, + { -2.106533040e-02f, +3.424969274e-01f, +7.372979620e-01f, -5.542499324e-02f }, + { -2.103729986e-02f, +3.421697387e-01f, +7.376149323e-01f, -5.544552335e-02f }, + { -2.100928539e-02f, +3.418426354e-01f, +7.379317896e-01f, -5.546601336e-02f }, + { -2.098128700e-02f, +3.415156174e-01f, +7.382485338e-01f, -5.548646320e-02f }, + { -2.095330472e-02f, +3.411886851e-01f, +7.385651645e-01f, -5.550687279e-02f }, + { -2.092533857e-02f, +3.408618385e-01f, +7.388816817e-01f, -5.552724207e-02f }, + { -2.089738856e-02f, +3.405350777e-01f, +7.391980851e-01f, -5.554757095e-02f }, + { -2.086945472e-02f, +3.402084029e-01f, +7.395143746e-01f, -5.556785936e-02f }, + { -2.084153705e-02f, +3.398818142e-01f, +7.398305500e-01f, -5.558810723e-02f }, + { -2.081363559e-02f, +3.395553117e-01f, +7.401466111e-01f, -5.560831448e-02f }, + { -2.078575035e-02f, +3.392288956e-01f, +7.404625577e-01f, -5.562848104e-02f }, + { -2.075788136e-02f, +3.389025660e-01f, +7.407783897e-01f, -5.564860682e-02f }, + { -2.073002862e-02f, +3.385763231e-01f, +7.410941069e-01f, -5.566869176e-02f }, + { -2.070219216e-02f, +3.382501669e-01f, +7.414097090e-01f, -5.568873578e-02f }, + { -2.067437199e-02f, +3.379240976e-01f, +7.417251959e-01f, -5.570873880e-02f }, + { -2.064656814e-02f, +3.375981153e-01f, +7.420405674e-01f, -5.572870075e-02f }, + { -2.061878063e-02f, +3.372722202e-01f, +7.423558234e-01f, -5.574862155e-02f }, + { -2.059100947e-02f, +3.369464124e-01f, +7.426709636e-01f, -5.576850113e-02f }, + { -2.056325469e-02f, +3.366206920e-01f, +7.429859879e-01f, -5.578833941e-02f }, + { -2.053551629e-02f, +3.362950591e-01f, +7.433008961e-01f, -5.580813632e-02f }, + { -2.050779431e-02f, +3.359695139e-01f, +7.436156880e-01f, -5.582789178e-02f }, + { -2.048008875e-02f, +3.356440565e-01f, +7.439303635e-01f, -5.584760571e-02f }, + { -2.045239964e-02f, +3.353186871e-01f, +7.442449222e-01f, -5.586727805e-02f }, + { -2.042472700e-02f, +3.349934057e-01f, +7.445593642e-01f, -5.588690871e-02f }, + { -2.039707084e-02f, +3.346682125e-01f, +7.448736891e-01f, -5.590649761e-02f }, + { -2.036943118e-02f, +3.343431076e-01f, +7.451878969e-01f, -5.592604469e-02f }, + { -2.034180804e-02f, +3.340180912e-01f, +7.455019872e-01f, -5.594554986e-02f }, + { -2.031420145e-02f, +3.336931634e-01f, +7.458159600e-01f, -5.596501306e-02f }, + { -2.028661141e-02f, +3.333683243e-01f, +7.461298151e-01f, -5.598443420e-02f }, + { -2.025903794e-02f, +3.330435740e-01f, +7.464435522e-01f, -5.600381321e-02f }, + { -2.023148107e-02f, +3.327189127e-01f, +7.467571712e-01f, -5.602315001e-02f }, + { -2.020394081e-02f, +3.323943405e-01f, +7.470706720e-01f, -5.604244453e-02f }, + { -2.017641718e-02f, +3.320698576e-01f, +7.473840543e-01f, -5.606169669e-02f }, + { -2.014891020e-02f, +3.317454639e-01f, +7.476973179e-01f, -5.608090642e-02f }, + { -2.012141988e-02f, +3.314211598e-01f, +7.480104627e-01f, -5.610007364e-02f }, + { -2.009394625e-02f, +3.310969453e-01f, +7.483234885e-01f, -5.611919827e-02f }, + { -2.006648932e-02f, +3.307728205e-01f, +7.486363951e-01f, -5.613828023e-02f }, + { -2.003904911e-02f, +3.304487856e-01f, +7.489491824e-01f, -5.615731946e-02f }, + { -2.001162564e-02f, +3.301248407e-01f, +7.492618501e-01f, -5.617631587e-02f }, + { -1.998421892e-02f, +3.298009860e-01f, +7.495743981e-01f, -5.619526939e-02f }, + { -1.995682897e-02f, +3.294772215e-01f, +7.498868261e-01f, -5.621417995e-02f }, + { -1.992945582e-02f, +3.291535473e-01f, +7.501991341e-01f, -5.623304746e-02f }, + { -1.990209947e-02f, +3.288299637e-01f, +7.505113218e-01f, -5.625187185e-02f }, + { -1.987475995e-02f, +3.285064707e-01f, +7.508233890e-01f, -5.627065304e-02f }, + { -1.984743727e-02f, +3.281830685e-01f, +7.511353356e-01f, -5.628939096e-02f }, + { -1.982013145e-02f, +3.278597572e-01f, +7.514471614e-01f, -5.630808553e-02f }, + { -1.979284251e-02f, +3.275365369e-01f, +7.517588662e-01f, -5.632673668e-02f }, + { -1.976557047e-02f, +3.272134077e-01f, +7.520704498e-01f, -5.634534432e-02f }, + { -1.973831534e-02f, +3.268903698e-01f, +7.523819121e-01f, -5.636390839e-02f }, + { -1.971107714e-02f, +3.265674233e-01f, +7.526932528e-01f, -5.638242880e-02f }, + { -1.968385589e-02f, +3.262445684e-01f, +7.530044718e-01f, -5.640090548e-02f }, + { -1.965665160e-02f, +3.259218051e-01f, +7.533155690e-01f, -5.641933835e-02f }, + { -1.962946429e-02f, +3.255991335e-01f, +7.536265440e-01f, -5.643772734e-02f }, + { -1.960229398e-02f, +3.252765539e-01f, +7.539373968e-01f, -5.645607236e-02f }, + { -1.957514069e-02f, +3.249540663e-01f, +7.542481271e-01f, -5.647437335e-02f }, + { -1.954800443e-02f, +3.246316709e-01f, +7.545587349e-01f, -5.649263022e-02f }, + { -1.952088522e-02f, +3.243093678e-01f, +7.548692198e-01f, -5.651084290e-02f }, + { -1.949378307e-02f, +3.239871571e-01f, +7.551795818e-01f, -5.652901131e-02f }, + { -1.946669801e-02f, +3.236650389e-01f, +7.554898206e-01f, -5.654713538e-02f }, + { -1.943963005e-02f, +3.233430133e-01f, +7.557999361e-01f, -5.656521503e-02f }, + { -1.941257921e-02f, +3.230210806e-01f, +7.561099280e-01f, -5.658325018e-02f }, + { -1.938554550e-02f, +3.226992408e-01f, +7.564197963e-01f, -5.660124075e-02f }, + { -1.935852895e-02f, +3.223774940e-01f, +7.567295407e-01f, -5.661918667e-02f }, + { -1.933152956e-02f, +3.220558404e-01f, +7.570391610e-01f, -5.663708786e-02f }, + { -1.930454735e-02f, +3.217342801e-01f, +7.573486571e-01f, -5.665494425e-02f }, + { -1.927758234e-02f, +3.214128132e-01f, +7.576580288e-01f, -5.667275575e-02f }, + { -1.925063455e-02f, +3.210914398e-01f, +7.579672759e-01f, -5.669052229e-02f }, + { -1.922370400e-02f, +3.207701601e-01f, +7.582763982e-01f, -5.670824380e-02f }, + { -1.919679069e-02f, +3.204489742e-01f, +7.585853955e-01f, -5.672592019e-02f }, + { -1.916989465e-02f, +3.201278822e-01f, +7.588942678e-01f, -5.674355138e-02f }, + { -1.914301589e-02f, +3.198068842e-01f, +7.592030147e-01f, -5.676113731e-02f }, + { -1.911615443e-02f, +3.194859804e-01f, +7.595116361e-01f, -5.677867790e-02f }, + { -1.908931029e-02f, +3.191651709e-01f, +7.598201319e-01f, -5.679617306e-02f }, + { -1.906248348e-02f, +3.188444558e-01f, +7.601285018e-01f, -5.681362272e-02f }, + { -1.903567401e-02f, +3.185238353e-01f, +7.604367457e-01f, -5.683102681e-02f }, + { -1.900888191e-02f, +3.182033094e-01f, +7.607448634e-01f, -5.684838523e-02f }, + { -1.898210719e-02f, +3.178828783e-01f, +7.610528546e-01f, -5.686569793e-02f }, + { -1.895534986e-02f, +3.175625421e-01f, +7.613607194e-01f, -5.688296482e-02f }, + { -1.892860994e-02f, +3.172423009e-01f, +7.616684574e-01f, -5.690018582e-02f }, + { -1.890188745e-02f, +3.169221549e-01f, +7.619760684e-01f, -5.691736086e-02f }, + { -1.887518241e-02f, +3.166021042e-01f, +7.622835524e-01f, -5.693448985e-02f }, + { -1.884849482e-02f, +3.162821488e-01f, +7.625909090e-01f, -5.695157273e-02f }, + { -1.882182471e-02f, +3.159622890e-01f, +7.628981382e-01f, -5.696860941e-02f }, + { -1.879517209e-02f, +3.156425249e-01f, +7.632052398e-01f, -5.698559981e-02f }, + { -1.876853697e-02f, +3.153228565e-01f, +7.635122136e-01f, -5.700254386e-02f }, + { -1.874191938e-02f, +3.150032840e-01f, +7.638190593e-01f, -5.701944149e-02f }, + { -1.871531932e-02f, +3.146838075e-01f, +7.641257769e-01f, -5.703629260e-02f }, + { -1.868873682e-02f, +3.143644272e-01f, +7.644323662e-01f, -5.705309714e-02f }, + { -1.866217188e-02f, +3.140451431e-01f, +7.647388269e-01f, -5.706985501e-02f }, + { -1.863562453e-02f, +3.137259554e-01f, +7.650451589e-01f, -5.708656614e-02f }, + { -1.860909478e-02f, +3.134068642e-01f, +7.653513620e-01f, -5.710323045e-02f }, + { -1.858258264e-02f, +3.130878696e-01f, +7.656574360e-01f, -5.711984787e-02f }, + { -1.855608813e-02f, +3.127689718e-01f, +7.659633808e-01f, -5.713641831e-02f }, + { -1.852961127e-02f, +3.124501709e-01f, +7.662691962e-01f, -5.715294170e-02f }, + { -1.850315207e-02f, +3.121314670e-01f, +7.665748819e-01f, -5.716941797e-02f }, + { -1.847671054e-02f, +3.118128601e-01f, +7.668804379e-01f, -5.718584702e-02f }, + { -1.845028671e-02f, +3.114943506e-01f, +7.671858639e-01f, -5.720222880e-02f }, + { -1.842388058e-02f, +3.111759383e-01f, +7.674911598e-01f, -5.721856321e-02f }, + { -1.839749217e-02f, +3.108576236e-01f, +7.677963254e-01f, -5.723485017e-02f }, + { -1.837112149e-02f, +3.105394064e-01f, +7.681013605e-01f, -5.725108962e-02f }, + { -1.834476857e-02f, +3.102212870e-01f, +7.684062649e-01f, -5.726728148e-02f }, + { -1.831843341e-02f, +3.099032654e-01f, +7.687110385e-01f, -5.728342566e-02f }, + { -1.829211604e-02f, +3.095853418e-01f, +7.690156811e-01f, -5.729952209e-02f }, + { -1.826581646e-02f, +3.092675163e-01f, +7.693201924e-01f, -5.731557068e-02f }, + { -1.823953469e-02f, +3.089497890e-01f, +7.696245724e-01f, -5.733157137e-02f }, + { -1.821327074e-02f, +3.086321600e-01f, +7.699288208e-01f, -5.734752407e-02f }, + { -1.818702464e-02f, +3.083146294e-01f, +7.702329374e-01f, -5.736342870e-02f }, + { -1.816079639e-02f, +3.079971975e-01f, +7.705369222e-01f, -5.737928519e-02f }, + { -1.813458600e-02f, +3.076798642e-01f, +7.708407749e-01f, -5.739509346e-02f }, + { -1.810839350e-02f, +3.073626297e-01f, +7.711444953e-01f, -5.741085343e-02f }, + { -1.808221890e-02f, +3.070454941e-01f, +7.714480832e-01f, -5.742656503e-02f }, + { -1.805606221e-02f, +3.067284576e-01f, +7.717515386e-01f, -5.744222816e-02f }, + { -1.802992345e-02f, +3.064115203e-01f, +7.720548611e-01f, -5.745784276e-02f }, + { -1.800380263e-02f, +3.060946822e-01f, +7.723580506e-01f, -5.747340875e-02f }, + { -1.797769977e-02f, +3.057779436e-01f, +7.726611070e-01f, -5.748892604e-02f }, + { -1.795161487e-02f, +3.054613045e-01f, +7.729640301e-01f, -5.750439457e-02f }, + { -1.792554796e-02f, +3.051447650e-01f, +7.732668197e-01f, -5.751981424e-02f }, + { -1.789949905e-02f, +3.048283253e-01f, +7.735694755e-01f, -5.753518499e-02f }, + { -1.787346814e-02f, +3.045119855e-01f, +7.738719975e-01f, -5.755050673e-02f }, + { -1.784745527e-02f, +3.041957456e-01f, +7.741743855e-01f, -5.756577939e-02f }, + { -1.782146044e-02f, +3.038796059e-01f, +7.744766393e-01f, -5.758100289e-02f }, + { -1.779548366e-02f, +3.035635665e-01f, +7.747787586e-01f, -5.759617715e-02f }, + { -1.776952495e-02f, +3.032476274e-01f, +7.750807434e-01f, -5.761130208e-02f }, + { -1.774358432e-02f, +3.029317887e-01f, +7.753825935e-01f, -5.762637762e-02f }, + { -1.771766179e-02f, +3.026160507e-01f, +7.756843086e-01f, -5.764140368e-02f }, + { -1.769175736e-02f, +3.023004134e-01f, +7.759858886e-01f, -5.765638018e-02f }, + { -1.766587107e-02f, +3.019848769e-01f, +7.762873333e-01f, -5.767130705e-02f }, + { -1.764000291e-02f, +3.016694414e-01f, +7.765886426e-01f, -5.768618420e-02f }, + { -1.761415290e-02f, +3.013541069e-01f, +7.768898163e-01f, -5.770101157e-02f }, + { -1.758832105e-02f, +3.010388736e-01f, +7.771908542e-01f, -5.771578906e-02f }, + { -1.756250739e-02f, +3.007237417e-01f, +7.774917560e-01f, -5.773051660e-02f }, + { -1.753671192e-02f, +3.004087111e-01f, +7.777925218e-01f, -5.774519411e-02f }, + { -1.751093465e-02f, +3.000937821e-01f, +7.780931511e-01f, -5.775982152e-02f }, + { -1.748517560e-02f, +2.997789547e-01f, +7.783936440e-01f, -5.777439874e-02f }, + { -1.745943479e-02f, +2.994642291e-01f, +7.786940002e-01f, -5.778892569e-02f }, + { -1.743371222e-02f, +2.991496054e-01f, +7.789942195e-01f, -5.780340230e-02f }, + { -1.740800791e-02f, +2.988350837e-01f, +7.792943018e-01f, -5.781782849e-02f }, + { -1.738232187e-02f, +2.985206642e-01f, +7.795942469e-01f, -5.783220417e-02f }, + { -1.735665412e-02f, +2.982063468e-01f, +7.798940546e-01f, -5.784652927e-02f }, + { -1.733100467e-02f, +2.978921319e-01f, +7.801937247e-01f, -5.786080371e-02f }, + { -1.730537353e-02f, +2.975780194e-01f, +7.804932570e-01f, -5.787502741e-02f }, + { -1.727976072e-02f, +2.972640094e-01f, +7.807926515e-01f, -5.788920029e-02f }, + { -1.725416625e-02f, +2.969501022e-01f, +7.810919079e-01f, -5.790332228e-02f }, + { -1.722859012e-02f, +2.966362979e-01f, +7.813910260e-01f, -5.791739329e-02f }, + { -1.720303237e-02f, +2.963225964e-01f, +7.816900056e-01f, -5.793141324e-02f }, + { -1.717749299e-02f, +2.960089980e-01f, +7.819888466e-01f, -5.794538205e-02f }, + { -1.715197200e-02f, +2.956955028e-01f, +7.822875489e-01f, -5.795929965e-02f }, + { -1.712646941e-02f, +2.953821109e-01f, +7.825861121e-01f, -5.797316596e-02f }, + { -1.710098524e-02f, +2.950688223e-01f, +7.828845363e-01f, -5.798698089e-02f }, + { -1.707551950e-02f, +2.947556373e-01f, +7.831828211e-01f, -5.800074437e-02f }, + { -1.705007220e-02f, +2.944425560e-01f, +7.834809664e-01f, -5.801445632e-02f }, + { -1.702464336e-02f, +2.941295783e-01f, +7.837789720e-01f, -5.802811666e-02f }, + { -1.699923298e-02f, +2.938167045e-01f, +7.840768378e-01f, -5.804172530e-02f }, + { -1.697384108e-02f, +2.935039348e-01f, +7.843745636e-01f, -5.805528218e-02f }, + { -1.694846768e-02f, +2.931912691e-01f, +7.846721491e-01f, -5.806878721e-02f }, + { -1.692311278e-02f, +2.928787076e-01f, +7.849695943e-01f, -5.808224030e-02f }, + { -1.689777639e-02f, +2.925662504e-01f, +7.852668990e-01f, -5.809564139e-02f }, + { -1.687245854e-02f, +2.922538976e-01f, +7.855640629e-01f, -5.810899039e-02f }, + { -1.684715923e-02f, +2.919416495e-01f, +7.858610859e-01f, -5.812228722e-02f }, + { -1.682187847e-02f, +2.916295059e-01f, +7.861579679e-01f, -5.813553181e-02f }, + { -1.679661628e-02f, +2.913174672e-01f, +7.864547086e-01f, -5.814872407e-02f }, + { -1.677137267e-02f, +2.910055333e-01f, +7.867513079e-01f, -5.816186392e-02f }, + { -1.674614765e-02f, +2.906937045e-01f, +7.870477656e-01f, -5.817495128e-02f }, + { -1.672094124e-02f, +2.903819808e-01f, +7.873440816e-01f, -5.818798608e-02f }, + { -1.669575344e-02f, +2.900703623e-01f, +7.876402556e-01f, -5.820096823e-02f }, + { -1.667058426e-02f, +2.897588491e-01f, +7.879362875e-01f, -5.821389766e-02f }, + { -1.664543373e-02f, +2.894474415e-01f, +7.882321771e-01f, -5.822677428e-02f }, + { -1.662030185e-02f, +2.891361394e-01f, +7.885279242e-01f, -5.823959802e-02f }, + { -1.659518863e-02f, +2.888249429e-01f, +7.888235287e-01f, -5.825236880e-02f }, + { -1.657009409e-02f, +2.885138523e-01f, +7.891189904e-01f, -5.826508653e-02f }, + { -1.654501824e-02f, +2.882028676e-01f, +7.894143091e-01f, -5.827775113e-02f }, + { -1.651996108e-02f, +2.878919890e-01f, +7.897094846e-01f, -5.829036253e-02f }, + { -1.649492264e-02f, +2.875812164e-01f, +7.900045168e-01f, -5.830292065e-02f }, + { -1.646990291e-02f, +2.872705501e-01f, +7.902994055e-01f, -5.831542540e-02f }, + { -1.644490193e-02f, +2.869599902e-01f, +7.905941505e-01f, -5.832787671e-02f }, + { -1.641991969e-02f, +2.866495368e-01f, +7.908887516e-01f, -5.834027449e-02f }, + { -1.639495620e-02f, +2.863391899e-01f, +7.911832088e-01f, -5.835261867e-02f }, + { -1.637001149e-02f, +2.860289498e-01f, +7.914775217e-01f, -5.836490917e-02f }, + { -1.634508556e-02f, +2.857188164e-01f, +7.917716902e-01f, -5.837714590e-02f }, + { -1.632017842e-02f, +2.854087900e-01f, +7.920657142e-01f, -5.838932879e-02f }, + { -1.629529008e-02f, +2.850988707e-01f, +7.923595935e-01f, -5.840145776e-02f }, + { -1.627042056e-02f, +2.847890584e-01f, +7.926533279e-01f, -5.841353272e-02f }, + { -1.624556986e-02f, +2.844793535e-01f, +7.929469172e-01f, -5.842555359e-02f }, + { -1.622073801e-02f, +2.841697559e-01f, +7.932403612e-01f, -5.843752031e-02f }, + { -1.619592500e-02f, +2.838602658e-01f, +7.935336599e-01f, -5.844943277e-02f }, + { -1.617113085e-02f, +2.835508833e-01f, +7.938268129e-01f, -5.846129092e-02f }, + { -1.614635558e-02f, +2.832416085e-01f, +7.941198202e-01f, -5.847309465e-02f }, + { -1.612159918e-02f, +2.829324415e-01f, +7.944126816e-01f, -5.848484391e-02f }, + { -1.609686168e-02f, +2.826233824e-01f, +7.947053969e-01f, -5.849653859e-02f }, + { -1.607214309e-02f, +2.823144314e-01f, +7.949979658e-01f, -5.850817864e-02f }, + { -1.604744341e-02f, +2.820055885e-01f, +7.952903884e-01f, -5.851976395e-02f }, + { -1.602276266e-02f, +2.816968539e-01f, +7.955826642e-01f, -5.853129446e-02f }, + { -1.599810084e-02f, +2.813882276e-01f, +7.958747933e-01f, -5.854277008e-02f }, + { -1.597345798e-02f, +2.810797098e-01f, +7.961667755e-01f, -5.855419074e-02f }, + { -1.594883407e-02f, +2.807713007e-01f, +7.964586105e-01f, -5.856555634e-02f }, + { -1.592422914e-02f, +2.804630002e-01f, +7.967502981e-01f, -5.857686682e-02f }, + { -1.589964318e-02f, +2.801548085e-01f, +7.970418383e-01f, -5.858812209e-02f }, + { -1.587507622e-02f, +2.798467258e-01f, +7.973332308e-01f, -5.859932207e-02f }, + { -1.585052826e-02f, +2.795387520e-01f, +7.976244755e-01f, -5.861046669e-02f }, + { -1.582599931e-02f, +2.792308874e-01f, +7.979155722e-01f, -5.862155585e-02f }, + { -1.580148939e-02f, +2.789231321e-01f, +7.982065207e-01f, -5.863258948e-02f }, + { -1.577699850e-02f, +2.786154861e-01f, +7.984973209e-01f, -5.864356750e-02f }, + { -1.575252665e-02f, +2.783079496e-01f, +7.987879725e-01f, -5.865448983e-02f }, + { -1.572807386e-02f, +2.780005227e-01f, +7.990784754e-01f, -5.866535639e-02f }, + { -1.570364014e-02f, +2.776932054e-01f, +7.993688295e-01f, -5.867616710e-02f }, + { -1.567922549e-02f, +2.773859980e-01f, +7.996590346e-01f, -5.868692187e-02f }, + { -1.565482993e-02f, +2.770789004e-01f, +7.999490904e-01f, -5.869762062e-02f }, + { -1.563045347e-02f, +2.767719129e-01f, +8.002389969e-01f, -5.870826329e-02f }, + { -1.560609611e-02f, +2.764650354e-01f, +8.005287538e-01f, -5.871884977e-02f }, + { -1.558175787e-02f, +2.761582682e-01f, +8.008183610e-01f, -5.872938000e-02f }, + { -1.555743875e-02f, +2.758516114e-01f, +8.011078183e-01f, -5.873985390e-02f }, + { -1.553313878e-02f, +2.755450649e-01f, +8.013971255e-01f, -5.875027137e-02f }, + { -1.550885795e-02f, +2.752386291e-01f, +8.016862825e-01f, -5.876063235e-02f }, + { -1.548459628e-02f, +2.749323038e-01f, +8.019752891e-01f, -5.877093675e-02f }, + { -1.546035377e-02f, +2.746260894e-01f, +8.022641451e-01f, -5.878118449e-02f }, + { -1.543613045e-02f, +2.743199858e-01f, +8.025528503e-01f, -5.879137549e-02f }, + { -1.541192631e-02f, +2.740139932e-01f, +8.028414047e-01f, -5.880150966e-02f }, + { -1.538774137e-02f, +2.737081117e-01f, +8.031298079e-01f, -5.881158693e-02f }, + { -1.536357563e-02f, +2.734023414e-01f, +8.034180599e-01f, -5.882160722e-02f }, + { -1.533942912e-02f, +2.730966824e-01f, +8.037061604e-01f, -5.883157045e-02f }, + { -1.531530183e-02f, +2.727911348e-01f, +8.039941094e-01f, -5.884147653e-02f }, + { -1.529119377e-02f, +2.724856987e-01f, +8.042819066e-01f, -5.885132538e-02f }, + { -1.526710496e-02f, +2.721803743e-01f, +8.045695518e-01f, -5.886111692e-02f }, + { -1.524303541e-02f, +2.718751616e-01f, +8.048570449e-01f, -5.887085108e-02f }, + { -1.521898512e-02f, +2.715700607e-01f, +8.051443857e-01f, -5.888052777e-02f }, + { -1.519495411e-02f, +2.712650718e-01f, +8.054315741e-01f, -5.889014690e-02f }, + { -1.517094238e-02f, +2.709601949e-01f, +8.057186098e-01f, -5.889970841e-02f }, + { -1.514694995e-02f, +2.706554301e-01f, +8.060054928e-01f, -5.890921220e-02f }, + { -1.512297682e-02f, +2.703507777e-01f, +8.062922227e-01f, -5.891865819e-02f }, + { -1.509902300e-02f, +2.700462376e-01f, +8.065787996e-01f, -5.892804632e-02f }, + { -1.507508850e-02f, +2.697418099e-01f, +8.068652231e-01f, -5.893737648e-02f }, + { -1.505117334e-02f, +2.694374949e-01f, +8.071514932e-01f, -5.894664861e-02f }, + { -1.502727751e-02f, +2.691332925e-01f, +8.074376096e-01f, -5.895586262e-02f }, + { -1.500340104e-02f, +2.688292029e-01f, +8.077235722e-01f, -5.896501843e-02f }, + { -1.497954392e-02f, +2.685252263e-01f, +8.080093808e-01f, -5.897411596e-02f }, + { -1.495570618e-02f, +2.682213626e-01f, +8.082950352e-01f, -5.898315513e-02f }, + { -1.493188780e-02f, +2.679176120e-01f, +8.085805354e-01f, -5.899213585e-02f }, + { -1.490808882e-02f, +2.676139746e-01f, +8.088658810e-01f, -5.900105805e-02f }, + { -1.488430923e-02f, +2.673104506e-01f, +8.091510719e-01f, -5.900992164e-02f }, + { -1.486054904e-02f, +2.670070400e-01f, +8.094361081e-01f, -5.901872654e-02f }, + { -1.483680827e-02f, +2.667037429e-01f, +8.097209892e-01f, -5.902747268e-02f }, + { -1.481308692e-02f, +2.664005594e-01f, +8.100057151e-01f, -5.903615996e-02f }, + { -1.478938500e-02f, +2.660974897e-01f, +8.102902857e-01f, -5.904478831e-02f }, + { -1.476570252e-02f, +2.657945338e-01f, +8.105747008e-01f, -5.905335765e-02f }, + { -1.474203949e-02f, +2.654916918e-01f, +8.108589603e-01f, -5.906186790e-02f }, + { -1.471839591e-02f, +2.651889639e-01f, +8.111430638e-01f, -5.907031897e-02f }, + { -1.469477181e-02f, +2.648863501e-01f, +8.114270114e-01f, -5.907871078e-02f }, + { -1.467116717e-02f, +2.645838506e-01f, +8.117108027e-01f, -5.908704325e-02f }, + { -1.464758202e-02f, +2.642814655e-01f, +8.119944377e-01f, -5.909531630e-02f }, + { -1.462401636e-02f, +2.639791948e-01f, +8.122779162e-01f, -5.910352985e-02f }, + { -1.460047020e-02f, +2.636770386e-01f, +8.125612380e-01f, -5.911168382e-02f }, + { -1.457694355e-02f, +2.633749972e-01f, +8.128444029e-01f, -5.911977812e-02f }, + { -1.455343641e-02f, +2.630730705e-01f, +8.131274108e-01f, -5.912781267e-02f }, + { -1.452994881e-02f, +2.627712587e-01f, +8.134102615e-01f, -5.913578740e-02f }, + { -1.450648073e-02f, +2.624695618e-01f, +8.136929549e-01f, -5.914370221e-02f }, + { -1.448303220e-02f, +2.621679801e-01f, +8.139754907e-01f, -5.915155704e-02f }, + { -1.445960321e-02f, +2.618665135e-01f, +8.142578688e-01f, -5.915935179e-02f }, + { -1.443619379e-02f, +2.615651622e-01f, +8.145400890e-01f, -5.916708638e-02f }, + { -1.441280393e-02f, +2.612639263e-01f, +8.148221512e-01f, -5.917476074e-02f }, + { -1.438943365e-02f, +2.609628058e-01f, +8.151040551e-01f, -5.918237478e-02f }, + { -1.436608295e-02f, +2.606618010e-01f, +8.153858007e-01f, -5.918992842e-02f }, + { -1.434275184e-02f, +2.603609118e-01f, +8.156673878e-01f, -5.919742158e-02f }, + { -1.431944033e-02f, +2.600601385e-01f, +8.159488162e-01f, -5.920485418e-02f }, + { -1.429614843e-02f, +2.597594810e-01f, +8.162300856e-01f, -5.921222613e-02f }, + { -1.427287614e-02f, +2.594589396e-01f, +8.165111960e-01f, -5.921953735e-02f }, + { -1.424962348e-02f, +2.591585142e-01f, +8.167921473e-01f, -5.922678777e-02f }, + { -1.422639045e-02f, +2.588582051e-01f, +8.170729391e-01f, -5.923397729e-02f }, + { -1.420317705e-02f, +2.585580122e-01f, +8.173535714e-01f, -5.924110584e-02f }, + { -1.417998331e-02f, +2.582579358e-01f, +8.176340439e-01f, -5.924817333e-02f }, + { -1.415680921e-02f, +2.579579758e-01f, +8.179143566e-01f, -5.925517969e-02f }, + { -1.413365478e-02f, +2.576581325e-01f, +8.181945092e-01f, -5.926212483e-02f }, + { -1.411052002e-02f, +2.573584058e-01f, +8.184745017e-01f, -5.926900867e-02f }, + { -1.408740493e-02f, +2.570587960e-01f, +8.187543337e-01f, -5.927583113e-02f }, + { -1.406430953e-02f, +2.567593031e-01f, +8.190340052e-01f, -5.928259213e-02f }, + { -1.404123383e-02f, +2.564599271e-01f, +8.193135159e-01f, -5.928929157e-02f }, + { -1.401817782e-02f, +2.561606683e-01f, +8.195928658e-01f, -5.929592939e-02f }, + { -1.399514152e-02f, +2.558615267e-01f, +8.198720547e-01f, -5.930250550e-02f }, + { -1.397212493e-02f, +2.555625024e-01f, +8.201510823e-01f, -5.930901982e-02f }, + { -1.394912807e-02f, +2.552635955e-01f, +8.204299485e-01f, -5.931547226e-02f }, + { -1.392615094e-02f, +2.549648062e-01f, +8.207086532e-01f, -5.932186275e-02f }, + { -1.390319354e-02f, +2.546661344e-01f, +8.209871961e-01f, -5.932819120e-02f }, + { -1.388025589e-02f, +2.543675803e-01f, +8.212655772e-01f, -5.933445753e-02f }, + { -1.385733799e-02f, +2.540691440e-01f, +8.215437962e-01f, -5.934066165e-02f }, + { -1.383443984e-02f, +2.537708257e-01f, +8.218218531e-01f, -5.934680349e-02f }, + { -1.381156147e-02f, +2.534726253e-01f, +8.220997475e-01f, -5.935288297e-02f }, + { -1.378870286e-02f, +2.531745431e-01f, +8.223774794e-01f, -5.935889999e-02f }, + { -1.376586404e-02f, +2.528765790e-01f, +8.226550485e-01f, -5.936485448e-02f }, + { -1.374304500e-02f, +2.525787333e-01f, +8.229324548e-01f, -5.937074636e-02f }, + { -1.372024576e-02f, +2.522810059e-01f, +8.232096981e-01f, -5.937657555e-02f }, + { -1.369746632e-02f, +2.519833971e-01f, +8.234867781e-01f, -5.938234196e-02f }, + { -1.367470668e-02f, +2.516859068e-01f, +8.237636948e-01f, -5.938804550e-02f }, + { -1.365196686e-02f, +2.513885353e-01f, +8.240404479e-01f, -5.939368611e-02f }, + { -1.362924687e-02f, +2.510912825e-01f, +8.243170373e-01f, -5.939926369e-02f }, + { -1.360654670e-02f, +2.507941486e-01f, +8.245934629e-01f, -5.940477816e-02f }, + { -1.358386636e-02f, +2.504971337e-01f, +8.248697244e-01f, -5.941022944e-02f }, + { -1.356120587e-02f, +2.502002379e-01f, +8.251458217e-01f, -5.941561746e-02f }, + { -1.353856523e-02f, +2.499034613e-01f, +8.254217546e-01f, -5.942094212e-02f }, + { -1.351594444e-02f, +2.496068040e-01f, +8.256975230e-01f, -5.942620334e-02f }, + { -1.349334351e-02f, +2.493102660e-01f, +8.259731267e-01f, -5.943140104e-02f }, + { -1.347076245e-02f, +2.490138475e-01f, +8.262485655e-01f, -5.943653515e-02f }, + { -1.344820127e-02f, +2.487175486e-01f, +8.265238393e-01f, -5.944160557e-02f }, + { -1.342565996e-02f, +2.484213694e-01f, +8.267989479e-01f, -5.944661222e-02f }, + { -1.340313855e-02f, +2.481253099e-01f, +8.270738912e-01f, -5.945155503e-02f }, + { -1.338063703e-02f, +2.478293703e-01f, +8.273486689e-01f, -5.945643390e-02f }, + { -1.335815541e-02f, +2.475335507e-01f, +8.276232810e-01f, -5.946124877e-02f }, + { -1.333569370e-02f, +2.472378511e-01f, +8.278977272e-01f, -5.946599953e-02f }, + { -1.331325190e-02f, +2.469422717e-01f, +8.281720074e-01f, -5.947068612e-02f }, + { -1.329083001e-02f, +2.466468125e-01f, +8.284461214e-01f, -5.947530845e-02f }, + { -1.326842806e-02f, +2.463514737e-01f, +8.287200691e-01f, -5.947986644e-02f }, + { -1.324604604e-02f, +2.460562553e-01f, +8.289938502e-01f, -5.948436000e-02f }, + { -1.322368395e-02f, +2.457611574e-01f, +8.292674647e-01f, -5.948878905e-02f }, + { -1.320134181e-02f, +2.454661802e-01f, +8.295409124e-01f, -5.949315351e-02f }, + { -1.317901962e-02f, +2.451713237e-01f, +8.298141930e-01f, -5.949745330e-02f }, + { -1.315671739e-02f, +2.448765881e-01f, +8.300873065e-01f, -5.950168834e-02f }, + { -1.313443511e-02f, +2.445819733e-01f, +8.303602527e-01f, -5.950585853e-02f }, + { -1.311217281e-02f, +2.442874796e-01f, +8.306330313e-01f, -5.950996381e-02f }, + { -1.308993048e-02f, +2.439931070e-01f, +8.309056424e-01f, -5.951400408e-02f }, + { -1.306770813e-02f, +2.436988556e-01f, +8.311780856e-01f, -5.951797926e-02f }, + { -1.304550576e-02f, +2.434047255e-01f, +8.314503608e-01f, -5.952188928e-02f }, + { -1.302332339e-02f, +2.431107168e-01f, +8.317224679e-01f, -5.952573404e-02f }, + { -1.300116101e-02f, +2.428168296e-01f, +8.319944067e-01f, -5.952951347e-02f }, + { -1.297901864e-02f, +2.425230639e-01f, +8.322661770e-01f, -5.953322748e-02f }, + { -1.295689627e-02f, +2.422294200e-01f, +8.325377786e-01f, -5.953687600e-02f }, + { -1.293479392e-02f, +2.419358978e-01f, +8.328092115e-01f, -5.954045893e-02f }, + { -1.291271159e-02f, +2.416424975e-01f, +8.330804754e-01f, -5.954397620e-02f }, + { -1.289064929e-02f, +2.413492191e-01f, +8.333515702e-01f, -5.954742771e-02f }, + { -1.286860701e-02f, +2.410560629e-01f, +8.336224957e-01f, -5.955081340e-02f }, + { -1.284658477e-02f, +2.407630287e-01f, +8.338932518e-01f, -5.955413318e-02f }, + { -1.282458258e-02f, +2.404701168e-01f, +8.341638383e-01f, -5.955738696e-02f }, + { -1.280260043e-02f, +2.401773273e-01f, +8.344342550e-01f, -5.956057466e-02f }, + { -1.278063833e-02f, +2.398846601e-01f, +8.347045017e-01f, -5.956369619e-02f }, + { -1.275869629e-02f, +2.395921155e-01f, +8.349745784e-01f, -5.956675149e-02f }, + { -1.273677432e-02f, +2.392996936e-01f, +8.352444848e-01f, -5.956974045e-02f }, + { -1.271487241e-02f, +2.390073943e-01f, +8.355142207e-01f, -5.957266301e-02f }, + { -1.269299057e-02f, +2.387152178e-01f, +8.357837861e-01f, -5.957551907e-02f }, + { -1.267112882e-02f, +2.384231642e-01f, +8.360531808e-01f, -5.957830856e-02f }, + { -1.264928715e-02f, +2.381312337e-01f, +8.363224045e-01f, -5.958103139e-02f }, + { -1.262746557e-02f, +2.378394262e-01f, +8.365914572e-01f, -5.958368747e-02f }, + { -1.260566408e-02f, +2.375477418e-01f, +8.368603387e-01f, -5.958627674e-02f }, + { -1.258388269e-02f, +2.372561808e-01f, +8.371290487e-01f, -5.958879909e-02f }, + { -1.256212141e-02f, +2.369647431e-01f, +8.373975872e-01f, -5.959125445e-02f }, + { -1.254038023e-02f, +2.366734288e-01f, +8.376659540e-01f, -5.959364274e-02f }, + { -1.251865917e-02f, +2.363822381e-01f, +8.379341490e-01f, -5.959596388e-02f }, + { -1.249695823e-02f, +2.360911710e-01f, +8.382021718e-01f, -5.959821777e-02f }, + { -1.247527741e-02f, +2.358002277e-01f, +8.384700225e-01f, -5.960040434e-02f }, + { -1.245361672e-02f, +2.355094081e-01f, +8.387377009e-01f, -5.960252351e-02f }, + { -1.243197617e-02f, +2.352187125e-01f, +8.390052067e-01f, -5.960457519e-02f }, + { -1.241035575e-02f, +2.349281409e-01f, +8.392725398e-01f, -5.960655929e-02f }, + { -1.238875548e-02f, +2.346376934e-01f, +8.395397001e-01f, -5.960847575e-02f }, + { -1.236717535e-02f, +2.343473700e-01f, +8.398066874e-01f, -5.961032446e-02f }, + { -1.234561537e-02f, +2.340571709e-01f, +8.400735015e-01f, -5.961210535e-02f }, + { -1.232407555e-02f, +2.337670962e-01f, +8.403401423e-01f, -5.961381834e-02f }, + { -1.230255589e-02f, +2.334771460e-01f, +8.406066096e-01f, -5.961546335e-02f }, + { -1.228105640e-02f, +2.331873202e-01f, +8.408729033e-01f, -5.961704028e-02f }, + { -1.225957708e-02f, +2.328976192e-01f, +8.411390231e-01f, -5.961854906e-02f }, + { -1.223811793e-02f, +2.326080428e-01f, +8.414049690e-01f, -5.961998961e-02f }, + { -1.221667896e-02f, +2.323185913e-01f, +8.416707408e-01f, -5.962136183e-02f }, + { -1.219526018e-02f, +2.320292646e-01f, +8.419363383e-01f, -5.962266566e-02f }, + { -1.217386158e-02f, +2.317400630e-01f, +8.422017613e-01f, -5.962390099e-02f }, + { -1.215248317e-02f, +2.314509865e-01f, +8.424670098e-01f, -5.962506776e-02f }, + { -1.213112496e-02f, +2.311620351e-01f, +8.427320835e-01f, -5.962616588e-02f }, + { -1.210978695e-02f, +2.308732090e-01f, +8.429969822e-01f, -5.962719526e-02f }, + { -1.208846914e-02f, +2.305845082e-01f, +8.432617059e-01f, -5.962815583e-02f }, + { -1.206717155e-02f, +2.302959329e-01f, +8.435262543e-01f, -5.962904749e-02f }, + { -1.204589416e-02f, +2.300074831e-01f, +8.437906274e-01f, -5.962987017e-02f }, + { -1.202463700e-02f, +2.297191590e-01f, +8.440548248e-01f, -5.963062378e-02f }, + { -1.200340005e-02f, +2.294309606e-01f, +8.443188466e-01f, -5.963130824e-02f }, + { -1.198218333e-02f, +2.291428879e-01f, +8.445826924e-01f, -5.963192347e-02f }, + { -1.196098683e-02f, +2.288549412e-01f, +8.448463623e-01f, -5.963246938e-02f }, + { -1.193981057e-02f, +2.285671204e-01f, +8.451098559e-01f, -5.963294588e-02f }, + { -1.191865455e-02f, +2.282794257e-01f, +8.453731732e-01f, -5.963335291e-02f }, + { -1.189751876e-02f, +2.279918571e-01f, +8.456363139e-01f, -5.963369036e-02f }, + { -1.187640322e-02f, +2.277044148e-01f, +8.458992780e-01f, -5.963395817e-02f }, + { -1.185530793e-02f, +2.274170988e-01f, +8.461620653e-01f, -5.963415624e-02f }, + { -1.183423289e-02f, +2.271299092e-01f, +8.464246755e-01f, -5.963428449e-02f }, + { -1.181317810e-02f, +2.268428462e-01f, +8.466871086e-01f, -5.963434285e-02f }, + { -1.179214358e-02f, +2.265559097e-01f, +8.469493644e-01f, -5.963433122e-02f }, + { -1.177112931e-02f, +2.262690999e-01f, +8.472114428e-01f, -5.963424952e-02f }, + { -1.175013531e-02f, +2.259824169e-01f, +8.474733435e-01f, -5.963409767e-02f }, + { -1.172916159e-02f, +2.256958607e-01f, +8.477350664e-01f, -5.963387559e-02f }, + { -1.170820813e-02f, +2.254094315e-01f, +8.479966114e-01f, -5.963358319e-02f }, + { -1.168727495e-02f, +2.251231293e-01f, +8.482579783e-01f, -5.963322039e-02f }, + { -1.166636205e-02f, +2.248369542e-01f, +8.485191669e-01f, -5.963278711e-02f }, + { -1.164546944e-02f, +2.245509063e-01f, +8.487801771e-01f, -5.963228326e-02f }, + { -1.162459711e-02f, +2.242649857e-01f, +8.490410088e-01f, -5.963170876e-02f }, + { -1.160374507e-02f, +2.239791925e-01f, +8.493016617e-01f, -5.963106352e-02f }, + { -1.158291333e-02f, +2.236935267e-01f, +8.495621358e-01f, -5.963034747e-02f }, + { -1.156210188e-02f, +2.234079885e-01f, +8.498224307e-01f, -5.962956051e-02f }, + { -1.154131073e-02f, +2.231225780e-01f, +8.500825465e-01f, -5.962870257e-02f }, + { -1.152053989e-02f, +2.228372951e-01f, +8.503424830e-01f, -5.962777357e-02f }, + { -1.149978935e-02f, +2.225521401e-01f, +8.506022399e-01f, -5.962677341e-02f }, + { -1.147905912e-02f, +2.222671129e-01f, +8.508618171e-01f, -5.962570201e-02f }, + { -1.145834920e-02f, +2.219822138e-01f, +8.511212145e-01f, -5.962455930e-02f }, + { -1.143765960e-02f, +2.216974426e-01f, +8.513804320e-01f, -5.962334519e-02f }, + { -1.141699032e-02f, +2.214127997e-01f, +8.516394693e-01f, -5.962205959e-02f }, + { -1.139634135e-02f, +2.211282850e-01f, +8.518983262e-01f, -5.962070242e-02f }, + { -1.137571272e-02f, +2.208438986e-01f, +8.521570028e-01f, -5.961927360e-02f }, + { -1.135510441e-02f, +2.205596406e-01f, +8.524154987e-01f, -5.961777305e-02f }, + { -1.133451643e-02f, +2.202755110e-01f, +8.526738139e-01f, -5.961620067e-02f }, + { -1.131394878e-02f, +2.199915101e-01f, +8.529319481e-01f, -5.961455639e-02f }, + { -1.129340147e-02f, +2.197076378e-01f, +8.531899012e-01f, -5.961284013e-02f }, + { -1.127287449e-02f, +2.194238943e-01f, +8.534476731e-01f, -5.961105179e-02f }, + { -1.125236786e-02f, +2.191402796e-01f, +8.537052637e-01f, -5.960919130e-02f }, + { -1.123188157e-02f, +2.188567938e-01f, +8.539626726e-01f, -5.960725858e-02f }, + { -1.121141563e-02f, +2.185734369e-01f, +8.542198999e-01f, -5.960525353e-02f }, + { -1.119097004e-02f, +2.182902092e-01f, +8.544769453e-01f, -5.960317608e-02f }, + { -1.117054480e-02f, +2.180071107e-01f, +8.547338087e-01f, -5.960102614e-02f }, + { -1.115013991e-02f, +2.177241413e-01f, +8.549904899e-01f, -5.959880364e-02f }, + { -1.112975538e-02f, +2.174413013e-01f, +8.552469888e-01f, -5.959650847e-02f }, + { -1.110939121e-02f, +2.171585908e-01f, +8.555033052e-01f, -5.959414057e-02f }, + { -1.108904740e-02f, +2.168760097e-01f, +8.557594390e-01f, -5.959169984e-02f }, + { -1.106872396e-02f, +2.165935582e-01f, +8.560153900e-01f, -5.958918621e-02f }, + { -1.104842088e-02f, +2.163112364e-01f, +8.562711580e-01f, -5.958659959e-02f }, + { -1.102813817e-02f, +2.160290443e-01f, +8.565267430e-01f, -5.958393990e-02f }, + { -1.100787583e-02f, +2.157469820e-01f, +8.567821446e-01f, -5.958120705e-02f }, + { -1.098763386e-02f, +2.154650497e-01f, +8.570373629e-01f, -5.957840096e-02f }, + { -1.096741227e-02f, +2.151832473e-01f, +8.572923976e-01f, -5.957552154e-02f }, + { -1.094721106e-02f, +2.149015750e-01f, +8.575472486e-01f, -5.957256872e-02f }, + { -1.092703022e-02f, +2.146200329e-01f, +8.578019157e-01f, -5.956954241e-02f }, + { -1.090686977e-02f, +2.143386210e-01f, +8.580563988e-01f, -5.956644252e-02f }, + { -1.088672970e-02f, +2.140573395e-01f, +8.583106977e-01f, -5.956326897e-02f }, + { -1.086661002e-02f, +2.137761883e-01f, +8.585648123e-01f, -5.956002168e-02f }, + { -1.084651073e-02f, +2.134951677e-01f, +8.588187423e-01f, -5.955670057e-02f }, + { -1.082643182e-02f, +2.132142776e-01f, +8.590724878e-01f, -5.955330554e-02f }, + { -1.080637331e-02f, +2.129335181e-01f, +8.593260484e-01f, -5.954983652e-02f }, + { -1.078633519e-02f, +2.126528894e-01f, +8.595794240e-01f, -5.954629343e-02f }, + { -1.076631747e-02f, +2.123723915e-01f, +8.598326146e-01f, -5.954267617e-02f }, + { -1.074632015e-02f, +2.120920245e-01f, +8.600856199e-01f, -5.953898467e-02f }, + { -1.072634322e-02f, +2.118117885e-01f, +8.603384398e-01f, -5.953521884e-02f }, + { -1.070638669e-02f, +2.115316835e-01f, +8.605910741e-01f, -5.953137860e-02f }, + { -1.068645057e-02f, +2.112517097e-01f, +8.608435227e-01f, -5.952746386e-02f }, + { -1.066653486e-02f, +2.109718671e-01f, +8.610957855e-01f, -5.952347454e-02f }, + { -1.064663954e-02f, +2.106921557e-01f, +8.613478622e-01f, -5.951941056e-02f }, + { -1.062676464e-02f, +2.104125758e-01f, +8.615997527e-01f, -5.951527184e-02f }, + { -1.060691015e-02f, +2.101331273e-01f, +8.618514569e-01f, -5.951105828e-02f }, + { -1.058707606e-02f, +2.098538103e-01f, +8.621029746e-01f, -5.950676981e-02f }, + { -1.056726239e-02f, +2.095746250e-01f, +8.623543057e-01f, -5.950240634e-02f }, + { -1.054746914e-02f, +2.092955714e-01f, +8.626054500e-01f, -5.949796779e-02f }, + { -1.052769630e-02f, +2.090166495e-01f, +8.628564073e-01f, -5.949345407e-02f }, + { -1.050794387e-02f, +2.087378595e-01f, +8.631071776e-01f, -5.948886510e-02f }, + { -1.048821187e-02f, +2.084592015e-01f, +8.633577606e-01f, -5.948420080e-02f }, + { -1.046850028e-02f, +2.081806754e-01f, +8.636081562e-01f, -5.947946108e-02f }, + { -1.044880911e-02f, +2.079022815e-01f, +8.638583642e-01f, -5.947464586e-02f }, + { -1.042913837e-02f, +2.076240197e-01f, +8.641083845e-01f, -5.946975506e-02f }, + { -1.040948805e-02f, +2.073458902e-01f, +8.643582170e-01f, -5.946478859e-02f }, + { -1.038985815e-02f, +2.070678930e-01f, +8.646078615e-01f, -5.945974637e-02f }, + { -1.037024868e-02f, +2.067900282e-01f, +8.648573178e-01f, -5.945462831e-02f }, + { -1.035065964e-02f, +2.065122959e-01f, +8.651065858e-01f, -5.944943433e-02f }, + { -1.033109103e-02f, +2.062346962e-01f, +8.653556654e-01f, -5.944416434e-02f }, + { -1.031154284e-02f, +2.059572292e-01f, +8.656045563e-01f, -5.943881827e-02f }, + { -1.029201508e-02f, +2.056798948e-01f, +8.658532585e-01f, -5.943339603e-02f }, + { -1.027250776e-02f, +2.054026933e-01f, +8.661017718e-01f, -5.942789753e-02f }, + { -1.025302087e-02f, +2.051256246e-01f, +8.663500959e-01f, -5.942232269e-02f }, + { -1.023355441e-02f, +2.048486889e-01f, +8.665982309e-01f, -5.941667144e-02f }, + { -1.021410838e-02f, +2.045718862e-01f, +8.668461765e-01f, -5.941094367e-02f }, + { -1.019468279e-02f, +2.042952167e-01f, +8.670939326e-01f, -5.940513931e-02f }, + { -1.017527764e-02f, +2.040186803e-01f, +8.673414990e-01f, -5.939925828e-02f }, + { -1.015589292e-02f, +2.037422772e-01f, +8.675888756e-01f, -5.939330049e-02f }, + { -1.013652864e-02f, +2.034660075e-01f, +8.678360622e-01f, -5.938726586e-02f }, + { -1.011718480e-02f, +2.031898712e-01f, +8.680830587e-01f, -5.938115430e-02f }, + { -1.009786140e-02f, +2.029138683e-01f, +8.683298650e-01f, -5.937496574e-02f }, + { -1.007855843e-02f, +2.026379991e-01f, +8.685764808e-01f, -5.936870008e-02f }, + { -1.005927591e-02f, +2.023622635e-01f, +8.688229060e-01f, -5.936235724e-02f }, + { -1.004001383e-02f, +2.020866616e-01f, +8.690691405e-01f, -5.935593714e-02f }, + { -1.002077218e-02f, +2.018111936e-01f, +8.693151841e-01f, -5.934943969e-02f }, + { -1.000155098e-02f, +2.015358594e-01f, +8.695610368e-01f, -5.934286482e-02f }, + { -9.982350225e-03f, +2.012606592e-01f, +8.698066982e-01f, -5.933621243e-02f }, + { -9.963169910e-03f, +2.009855930e-01f, +8.700521683e-01f, -5.932948245e-02f }, + { -9.944010038e-03f, +2.007106609e-01f, +8.702974470e-01f, -5.932267479e-02f }, + { -9.924870610e-03f, +2.004358631e-01f, +8.705425340e-01f, -5.931578936e-02f }, + { -9.905751626e-03f, +2.001611994e-01f, +8.707874293e-01f, -5.930882608e-02f }, + { -9.886653086e-03f, +1.998866701e-01f, +8.710321326e-01f, -5.930178487e-02f }, + { -9.867574990e-03f, +1.996122753e-01f, +8.712766439e-01f, -5.929466565e-02f }, + { -9.848517340e-03f, +1.993380149e-01f, +8.715209630e-01f, -5.928746832e-02f }, + { -9.829480135e-03f, +1.990638891e-01f, +8.717650897e-01f, -5.928019281e-02f }, + { -9.810463375e-03f, +1.987898979e-01f, +8.720090239e-01f, -5.927283903e-02f }, + { -9.791467061e-03f, +1.985160414e-01f, +8.722527654e-01f, -5.926540690e-02f }, + { -9.772491192e-03f, +1.982423197e-01f, +8.724963141e-01f, -5.925789634e-02f }, + { -9.753535770e-03f, +1.979687329e-01f, +8.727396699e-01f, -5.925030726e-02f }, + { -9.734600793e-03f, +1.976952810e-01f, +8.729828325e-01f, -5.924263957e-02f }, + { -9.715686262e-03f, +1.974219642e-01f, +8.732258019e-01f, -5.923489319e-02f }, + { -9.696792177e-03f, +1.971487824e-01f, +8.734685779e-01f, -5.922706804e-02f }, + { -9.677918538e-03f, +1.968757357e-01f, +8.737111603e-01f, -5.921916404e-02f }, + { -9.659065344e-03f, +1.966028244e-01f, +8.739535491e-01f, -5.921118110e-02f }, + { -9.640232597e-03f, +1.963300483e-01f, +8.741957439e-01f, -5.920311914e-02f }, + { -9.621420295e-03f, +1.960574076e-01f, +8.744377448e-01f, -5.919497807e-02f }, + { -9.602628439e-03f, +1.957849023e-01f, +8.746795516e-01f, -5.918675781e-02f }, + { -9.583857028e-03f, +1.955125326e-01f, +8.749211640e-01f, -5.917845827e-02f }, + { -9.565106062e-03f, +1.952402985e-01f, +8.751625821e-01f, -5.917007938e-02f }, + { -9.546375540e-03f, +1.949682000e-01f, +8.754038055e-01f, -5.916162104e-02f }, + { -9.527665464e-03f, +1.946962373e-01f, +8.756448342e-01f, -5.915308317e-02f }, + { -9.508975831e-03f, +1.944244105e-01f, +8.758856680e-01f, -5.914446570e-02f }, + { -9.490306643e-03f, +1.941527195e-01f, +8.761263068e-01f, -5.913576853e-02f }, + { -9.471657898e-03f, +1.938811645e-01f, +8.763667504e-01f, -5.912699159e-02f }, + { -9.453029596e-03f, +1.936097455e-01f, +8.766069987e-01f, -5.911813478e-02f }, + { -9.434421737e-03f, +1.933384626e-01f, +8.768470516e-01f, -5.910919802e-02f }, + { -9.415834320e-03f, +1.930673159e-01f, +8.770869088e-01f, -5.910018124e-02f }, + { -9.397267344e-03f, +1.927963055e-01f, +8.773265703e-01f, -5.909108434e-02f }, + { -9.378720810e-03f, +1.925254314e-01f, +8.775660358e-01f, -5.908190725e-02f }, + { -9.360194716e-03f, +1.922546937e-01f, +8.778053053e-01f, -5.907264987e-02f }, + { -9.341689062e-03f, +1.919840925e-01f, +8.780443786e-01f, -5.906331213e-02f }, + { -9.323203848e-03f, +1.917136278e-01f, +8.782832556e-01f, -5.905389393e-02f }, + { -9.304739072e-03f, +1.914432997e-01f, +8.785219361e-01f, -5.904439521e-02f }, + { -9.286294734e-03f, +1.911731083e-01f, +8.787604199e-01f, -5.903481587e-02f }, + { -9.267870833e-03f, +1.909030537e-01f, +8.789987070e-01f, -5.902515582e-02f }, + { -9.249467369e-03f, +1.906331359e-01f, +8.792367971e-01f, -5.901541499e-02f }, + { -9.231084340e-03f, +1.903633550e-01f, +8.794746902e-01f, -5.900559330e-02f }, + { -9.212721746e-03f, +1.900937110e-01f, +8.797123860e-01f, -5.899569065e-02f }, + { -9.194379586e-03f, +1.898242041e-01f, +8.799498845e-01f, -5.898570696e-02f }, + { -9.176057859e-03f, +1.895548343e-01f, +8.801871854e-01f, -5.897564215e-02f }, + { -9.157756564e-03f, +1.892856017e-01f, +8.804242888e-01f, -5.896549614e-02f }, + { -9.139475700e-03f, +1.890165064e-01f, +8.806611943e-01f, -5.895526884e-02f }, + { -9.121215266e-03f, +1.887475484e-01f, +8.808979018e-01f, -5.894496016e-02f }, + { -9.102975262e-03f, +1.884787277e-01f, +8.811344113e-01f, -5.893457003e-02f }, + { -9.084755685e-03f, +1.882100446e-01f, +8.813707226e-01f, -5.892409837e-02f }, + { -9.066556536e-03f, +1.879414989e-01f, +8.816068354e-01f, -5.891354507e-02f }, + { -9.048377812e-03f, +1.876730909e-01f, +8.818427498e-01f, -5.890291007e-02f }, + { -9.030219514e-03f, +1.874048205e-01f, +8.820784655e-01f, -5.889219328e-02f }, + { -9.012081638e-03f, +1.871366879e-01f, +8.823139824e-01f, -5.888139461e-02f }, + { -8.993964185e-03f, +1.868686931e-01f, +8.825493003e-01f, -5.887051398e-02f }, + { -8.975867154e-03f, +1.866008361e-01f, +8.827844191e-01f, -5.885955131e-02f }, + { -8.957790542e-03f, +1.863331172e-01f, +8.830193387e-01f, -5.884850651e-02f }, + { -8.939734348e-03f, +1.860655362e-01f, +8.832540589e-01f, -5.883737950e-02f }, + { -8.921698572e-03f, +1.857980933e-01f, +8.834885795e-01f, -5.882617019e-02f }, + { -8.903683211e-03f, +1.855307886e-01f, +8.837229005e-01f, -5.881487851e-02f }, + { -8.885688265e-03f, +1.852636221e-01f, +8.839570217e-01f, -5.880350436e-02f }, + { -8.867713732e-03f, +1.849965939e-01f, +8.841909429e-01f, -5.879204767e-02f }, + { -8.849759611e-03f, +1.847297040e-01f, +8.844246640e-01f, -5.878050834e-02f }, + { -8.831825899e-03f, +1.844629526e-01f, +8.846581849e-01f, -5.876888630e-02f }, + { -8.813912596e-03f, +1.841963397e-01f, +8.848915054e-01f, -5.875718146e-02f }, + { -8.796019700e-03f, +1.839298653e-01f, +8.851246253e-01f, -5.874539374e-02f }, + { -8.778147210e-03f, +1.836635296e-01f, +8.853575446e-01f, -5.873352306e-02f }, + { -8.760295123e-03f, +1.833973325e-01f, +8.855902631e-01f, -5.872156932e-02f }, + { -8.742463438e-03f, +1.831312743e-01f, +8.858227806e-01f, -5.870953245e-02f }, + { -8.724652154e-03f, +1.828653548e-01f, +8.860550970e-01f, -5.869741236e-02f }, + { -8.706861268e-03f, +1.825995743e-01f, +8.862872121e-01f, -5.868520898e-02f }, + { -8.689090780e-03f, +1.823339328e-01f, +8.865191259e-01f, -5.867292220e-02f }, + { -8.671340686e-03f, +1.820684302e-01f, +8.867508381e-01f, -5.866055196e-02f }, + { -8.653610986e-03f, +1.818030668e-01f, +8.869823487e-01f, -5.864809816e-02f }, + { -8.635901678e-03f, +1.815378426e-01f, +8.872136574e-01f, -5.863556073e-02f }, + { -8.618212759e-03f, +1.812727576e-01f, +8.874447642e-01f, -5.862293958e-02f }, + { -8.600544229e-03f, +1.810078118e-01f, +8.876756689e-01f, -5.861023462e-02f }, + { -8.582896084e-03f, +1.807430055e-01f, +8.879063714e-01f, -5.859744577e-02f }, + { -8.565268323e-03f, +1.804783386e-01f, +8.881368715e-01f, -5.858457295e-02f }, + { -8.547660944e-03f, +1.802138112e-01f, +8.883671691e-01f, -5.857161608e-02f }, + { -8.530073945e-03f, +1.799494234e-01f, +8.885972640e-01f, -5.855857506e-02f }, + { -8.512507324e-03f, +1.796851751e-01f, +8.888271561e-01f, -5.854544983e-02f }, + { -8.494961078e-03f, +1.794210666e-01f, +8.890568452e-01f, -5.853224028e-02f }, + { -8.477435207e-03f, +1.791570979e-01f, +8.892863313e-01f, -5.851894634e-02f }, + { -8.459929707e-03f, +1.788932690e-01f, +8.895156142e-01f, -5.850556793e-02f }, + { -8.442444576e-03f, +1.786295800e-01f, +8.897446936e-01f, -5.849210496e-02f }, + { -8.424979813e-03f, +1.783660309e-01f, +8.899735696e-01f, -5.847855735e-02f }, + { -8.407535414e-03f, +1.781026219e-01f, +8.902022419e-01f, -5.846492501e-02f }, + { -8.390111379e-03f, +1.778393529e-01f, +8.904307105e-01f, -5.845120786e-02f }, + { -8.372707704e-03f, +1.775762242e-01f, +8.906589751e-01f, -5.843740582e-02f }, + { -8.355324387e-03f, +1.773132356e-01f, +8.908870357e-01f, -5.842351880e-02f }, + { -8.337961425e-03f, +1.770503873e-01f, +8.911148920e-01f, -5.840954672e-02f }, + { -8.320618817e-03f, +1.767876794e-01f, +8.913425440e-01f, -5.839548949e-02f }, + { -8.303296561e-03f, +1.765251119e-01f, +8.915699915e-01f, -5.838134704e-02f }, + { -8.285994652e-03f, +1.762626848e-01f, +8.917972344e-01f, -5.836711927e-02f }, + { -8.268713090e-03f, +1.760003983e-01f, +8.920242725e-01f, -5.835280610e-02f }, + { -8.251451871e-03f, +1.757382524e-01f, +8.922511057e-01f, -5.833840746e-02f }, + { -8.234210994e-03f, +1.754762472e-01f, +8.924777339e-01f, -5.832392325e-02f }, + { -8.216990455e-03f, +1.752143827e-01f, +8.927041569e-01f, -5.830935339e-02f }, + { -8.199790252e-03f, +1.749526590e-01f, +8.929303746e-01f, -5.829469780e-02f }, + { -8.182610382e-03f, +1.746910762e-01f, +8.931563868e-01f, -5.827995640e-02f }, + { -8.165450843e-03f, +1.744296343e-01f, +8.933821934e-01f, -5.826512910e-02f }, + { -8.148311631e-03f, +1.741683333e-01f, +8.936077942e-01f, -5.825021582e-02f }, + { -8.131192745e-03f, +1.739071734e-01f, +8.938331892e-01f, -5.823521647e-02f }, + { -8.114094182e-03f, +1.736461547e-01f, +8.940583782e-01f, -5.822013097e-02f }, + { -8.097015938e-03f, +1.733852771e-01f, +8.942833610e-01f, -5.820495924e-02f }, + { -8.079958012e-03f, +1.731245407e-01f, +8.945081375e-01f, -5.818970119e-02f }, + { -8.062920399e-03f, +1.728639457e-01f, +8.947327076e-01f, -5.817435673e-02f }, + { -8.045903098e-03f, +1.726034920e-01f, +8.949570711e-01f, -5.815892580e-02f }, + { -8.028906105e-03f, +1.723431797e-01f, +8.951812279e-01f, -5.814340830e-02f }, + { -8.011929418e-03f, +1.720830089e-01f, +8.954051779e-01f, -5.812780414e-02f }, + { -7.994973033e-03f, +1.718229797e-01f, +8.956289209e-01f, -5.811211325e-02f }, + { -7.978036948e-03f, +1.715630920e-01f, +8.958524568e-01f, -5.809633554e-02f }, + { -7.961121159e-03f, +1.713033461e-01f, +8.960757854e-01f, -5.808047093e-02f }, + { -7.944225664e-03f, +1.710437419e-01f, +8.962989066e-01f, -5.806451933e-02f }, + { -7.927350459e-03f, +1.707842794e-01f, +8.965218203e-01f, -5.804848066e-02f }, + { -7.910495542e-03f, +1.705249588e-01f, +8.967445263e-01f, -5.803235484e-02f }, + { -7.893660909e-03f, +1.702657802e-01f, +8.969670245e-01f, -5.801614178e-02f }, + { -7.876846558e-03f, +1.700067435e-01f, +8.971893148e-01f, -5.799984140e-02f }, + { -7.860052484e-03f, +1.697478489e-01f, +8.974113969e-01f, -5.798345361e-02f }, + { -7.843278685e-03f, +1.694890963e-01f, +8.976332709e-01f, -5.796697834e-02f }, + { -7.826525157e-03f, +1.692304860e-01f, +8.978549365e-01f, -5.795041550e-02f }, + { -7.809791898e-03f, +1.689720178e-01f, +8.980763937e-01f, -5.793376500e-02f }, + { -7.793078904e-03f, +1.687136919e-01f, +8.982976422e-01f, -5.791702676e-02f }, + { -7.776386171e-03f, +1.684555084e-01f, +8.985186819e-01f, -5.790020070e-02f }, + { -7.759713697e-03f, +1.681974672e-01f, +8.987395128e-01f, -5.788328673e-02f }, + { -7.743061477e-03f, +1.679395686e-01f, +8.989601346e-01f, -5.786628478e-02f }, + { -7.726429509e-03f, +1.676818124e-01f, +8.991805472e-01f, -5.784919475e-02f }, + { -7.709817790e-03f, +1.674241988e-01f, +8.994007506e-01f, -5.783201657e-02f }, + { -7.693226314e-03f, +1.671667279e-01f, +8.996207445e-01f, -5.781475014e-02f }, + { -7.676655080e-03f, +1.669093997e-01f, +8.998405288e-01f, -5.779739540e-02f }, + { -7.660104084e-03f, +1.666522142e-01f, +9.000601034e-01f, -5.777995224e-02f }, + { -7.643573321e-03f, +1.663951716e-01f, +9.002794682e-01f, -5.776242060e-02f }, + { -7.627062789e-03f, +1.661382718e-01f, +9.004986230e-01f, -5.774480038e-02f }, + { -7.610572484e-03f, +1.658815150e-01f, +9.007175676e-01f, -5.772709151e-02f }, + { -7.594102403e-03f, +1.656249011e-01f, +9.009363021e-01f, -5.770929389e-02f }, + { -7.577652540e-03f, +1.653684303e-01f, +9.011548261e-01f, -5.769140745e-02f }, + { -7.561222894e-03f, +1.651121027e-01f, +9.013731396e-01f, -5.767343210e-02f }, + { -7.544813460e-03f, +1.648559182e-01f, +9.015912425e-01f, -5.765536777e-02f }, + { -7.528424234e-03f, +1.645998769e-01f, +9.018091345e-01f, -5.763721435e-02f }, + { -7.512055213e-03f, +1.643439789e-01f, +9.020268157e-01f, -5.761897178e-02f }, + { -7.495706392e-03f, +1.640882243e-01f, +9.022442858e-01f, -5.760063997e-02f }, + { -7.479377769e-03f, +1.638326131e-01f, +9.024615447e-01f, -5.758221883e-02f }, + { -7.463069339e-03f, +1.635771453e-01f, +9.026785922e-01f, -5.756370829e-02f }, + { -7.446781098e-03f, +1.633218211e-01f, +9.028954283e-01f, -5.754510825e-02f }, + { -7.430513042e-03f, +1.630666404e-01f, +9.031120528e-01f, -5.752641864e-02f }, + { -7.414265167e-03f, +1.628116034e-01f, +9.033284656e-01f, -5.750763937e-02f }, + { -7.398037471e-03f, +1.625567100e-01f, +9.035446666e-01f, -5.748877036e-02f }, + { -7.381829947e-03f, +1.623019604e-01f, +9.037606555e-01f, -5.746981153e-02f }, + { -7.365642593e-03f, +1.620473546e-01f, +9.039764323e-01f, -5.745076279e-02f }, + { -7.349475404e-03f, +1.617928927e-01f, +9.041919969e-01f, -5.743162405e-02f }, + { -7.333328376e-03f, +1.615385747e-01f, +9.044073490e-01f, -5.741239524e-02f }, + { -7.317201506e-03f, +1.612844007e-01f, +9.046224887e-01f, -5.739307627e-02f }, + { -7.301094788e-03f, +1.610303707e-01f, +9.048374156e-01f, -5.737366706e-02f }, + { -7.285008220e-03f, +1.607764848e-01f, +9.050521298e-01f, -5.735416753e-02f }, + { -7.268941796e-03f, +1.605227430e-01f, +9.052666311e-01f, -5.733457759e-02f }, + { -7.252895512e-03f, +1.602691455e-01f, +9.054809193e-01f, -5.731489716e-02f }, + { -7.236869364e-03f, +1.600156922e-01f, +9.056949943e-01f, -5.729512615e-02f }, + { -7.220863348e-03f, +1.597623832e-01f, +9.059088560e-01f, -5.727526448e-02f }, + { -7.204877460e-03f, +1.595092186e-01f, +9.061225043e-01f, -5.725531208e-02f }, + { -7.188911695e-03f, +1.592561984e-01f, +9.063359389e-01f, -5.723526885e-02f }, + { -7.172966049e-03f, +1.590033227e-01f, +9.065491599e-01f, -5.721513471e-02f }, + { -7.157040517e-03f, +1.587505915e-01f, +9.067621670e-01f, -5.719490958e-02f }, + { -7.141135096e-03f, +1.584980049e-01f, +9.069749601e-01f, -5.717459338e-02f }, + { -7.125249780e-03f, +1.582455630e-01f, +9.071875391e-01f, -5.715418602e-02f }, + { -7.109384565e-03f, +1.579932657e-01f, +9.073999039e-01f, -5.713368743e-02f }, + { -7.093539446e-03f, +1.577411133e-01f, +9.076120543e-01f, -5.711309750e-02f }, + { -7.077714420e-03f, +1.574891056e-01f, +9.078239902e-01f, -5.709241618e-02f }, + { -7.061909482e-03f, +1.572372428e-01f, +9.080357115e-01f, -5.707164336e-02f }, + { -7.046124626e-03f, +1.569855249e-01f, +9.082472180e-01f, -5.705077897e-02f }, + { -7.030359849e-03f, +1.567339520e-01f, +9.084585096e-01f, -5.702982292e-02f }, + { -7.014615145e-03f, +1.564825241e-01f, +9.086695861e-01f, -5.700877514e-02f }, + { -6.998890510e-03f, +1.562312413e-01f, +9.088804476e-01f, -5.698763554e-02f }, + { -6.983185940e-03f, +1.559801037e-01f, +9.090910937e-01f, -5.696640402e-02f }, + { -6.967501430e-03f, +1.557291112e-01f, +9.093015244e-01f, -5.694508053e-02f }, + { -6.951836974e-03f, +1.554782640e-01f, +9.095117396e-01f, -5.692366496e-02f }, + { -6.936192569e-03f, +1.552275620e-01f, +9.097217391e-01f, -5.690215723e-02f }, + { -6.920568209e-03f, +1.549770055e-01f, +9.099315228e-01f, -5.688055727e-02f }, + { -6.904963889e-03f, +1.547265943e-01f, +9.101410905e-01f, -5.685886499e-02f }, + { -6.889379605e-03f, +1.544763286e-01f, +9.103504422e-01f, -5.683708031e-02f }, + { -6.873815352e-03f, +1.542262084e-01f, +9.105595777e-01f, -5.681520314e-02f }, + { -6.858271125e-03f, +1.539762337e-01f, +9.107684969e-01f, -5.679323340e-02f }, + { -6.842746919e-03f, +1.537264047e-01f, +9.109771996e-01f, -5.677117101e-02f }, + { -6.827242728e-03f, +1.534767213e-01f, +9.111856858e-01f, -5.674901589e-02f }, + { -6.811758549e-03f, +1.532271837e-01f, +9.113939552e-01f, -5.672676795e-02f }, + { -6.796294376e-03f, +1.529777918e-01f, +9.116020078e-01f, -5.670442710e-02f }, + { -6.780850204e-03f, +1.527285458e-01f, +9.118098434e-01f, -5.668199328e-02f }, + { -6.765426027e-03f, +1.524794457e-01f, +9.120174619e-01f, -5.665946638e-02f }, + { -6.750021842e-03f, +1.522304914e-01f, +9.122248632e-01f, -5.663684634e-02f }, + { -6.734637642e-03f, +1.519816832e-01f, +9.124320471e-01f, -5.661413307e-02f }, + { -6.719273423e-03f, +1.517330210e-01f, +9.126390136e-01f, -5.659132648e-02f }, + { -6.703929179e-03f, +1.514845048e-01f, +9.128457625e-01f, -5.656842649e-02f }, + { -6.688604905e-03f, +1.512361348e-01f, +9.130522936e-01f, -5.654543302e-02f }, + { -6.673300597e-03f, +1.509879110e-01f, +9.132586068e-01f, -5.652234599e-02f }, + { -6.658016248e-03f, +1.507398335e-01f, +9.134647021e-01f, -5.649916531e-02f }, + { -6.642751853e-03f, +1.504919022e-01f, +9.136705792e-01f, -5.647589090e-02f }, + { -6.627507408e-03f, +1.502441173e-01f, +9.138762381e-01f, -5.645252268e-02f }, + { -6.612282907e-03f, +1.499964787e-01f, +9.140816786e-01f, -5.642906057e-02f }, + { -6.597078344e-03f, +1.497489866e-01f, +9.142869006e-01f, -5.640550448e-02f }, + { -6.581893714e-03f, +1.495016410e-01f, +9.144919040e-01f, -5.638185433e-02f }, + { -6.566729012e-03f, +1.492544420e-01f, +9.146966886e-01f, -5.635811004e-02f }, + { -6.551584232e-03f, +1.490073895e-01f, +9.149012543e-01f, -5.633427152e-02f }, + { -6.536459369e-03f, +1.487604837e-01f, +9.151056011e-01f, -5.631033869e-02f }, + { -6.521354417e-03f, +1.485137245e-01f, +9.153097287e-01f, -5.628631147e-02f }, + { -6.506269371e-03f, +1.482671122e-01f, +9.155136370e-01f, -5.626218978e-02f }, + { -6.491204225e-03f, +1.480206466e-01f, +9.157173259e-01f, -5.623797353e-02f }, + { -6.476158974e-03f, +1.477743278e-01f, +9.159207954e-01f, -5.621366265e-02f }, + { -6.461133612e-03f, +1.475281560e-01f, +9.161240451e-01f, -5.618925704e-02f }, + { -6.446128134e-03f, +1.472821311e-01f, +9.163270752e-01f, -5.616475663e-02f }, + { -6.431142534e-03f, +1.470362531e-01f, +9.165298853e-01f, -5.614016133e-02f }, + { -6.416176806e-03f, +1.467905223e-01f, +9.167324754e-01f, -5.611547107e-02f }, + { -6.401230944e-03f, +1.465449385e-01f, +9.169348454e-01f, -5.609068575e-02f }, + { -6.386304943e-03f, +1.462995018e-01f, +9.171369950e-01f, -5.606580530e-02f }, + { -6.371398797e-03f, +1.460542124e-01f, +9.173389243e-01f, -5.604082963e-02f }, + { -6.356512501e-03f, +1.458090702e-01f, +9.175406331e-01f, -5.601575866e-02f }, + { -6.341646048e-03f, +1.455640752e-01f, +9.177421213e-01f, -5.599059231e-02f }, + { -6.326799433e-03f, +1.453192276e-01f, +9.179433886e-01f, -5.596533050e-02f }, + { -6.311972650e-03f, +1.450745274e-01f, +9.181444351e-01f, -5.593997314e-02f }, + { -6.297165693e-03f, +1.448299746e-01f, +9.183452606e-01f, -5.591452015e-02f }, + { -6.282378556e-03f, +1.445855693e-01f, +9.185458649e-01f, -5.588897145e-02f }, + { -6.267611233e-03f, +1.443413115e-01f, +9.187462479e-01f, -5.586332696e-02f }, + { -6.252863718e-03f, +1.440972013e-01f, +9.189464096e-01f, -5.583758659e-02f }, + { -6.238136006e-03f, +1.438532387e-01f, +9.191463497e-01f, -5.581175026e-02f }, + { -6.223428090e-03f, +1.436094238e-01f, +9.193460682e-01f, -5.578581789e-02f }, + { -6.208739964e-03f, +1.433657566e-01f, +9.195455649e-01f, -5.575978940e-02f }, + { -6.194071622e-03f, +1.431222371e-01f, +9.197448398e-01f, -5.573366470e-02f }, + { -6.179423059e-03f, +1.428788655e-01f, +9.199438926e-01f, -5.570744371e-02f }, + { -6.164794268e-03f, +1.426356417e-01f, +9.201427233e-01f, -5.568112636e-02f }, + { -6.150185242e-03f, +1.423925658e-01f, +9.203413317e-01f, -5.565471255e-02f }, + { -6.135595977e-03f, +1.421496379e-01f, +9.205397178e-01f, -5.562820221e-02f }, + { -6.121026465e-03f, +1.419068580e-01f, +9.207378813e-01f, -5.560159525e-02f }, + { -6.106476701e-03f, +1.416642261e-01f, +9.209358223e-01f, -5.557489159e-02f }, + { -6.091946678e-03f, +1.414217423e-01f, +9.211335405e-01f, -5.554809115e-02f }, + { -6.077436389e-03f, +1.411794066e-01f, +9.213310358e-01f, -5.552119385e-02f }, + { -6.062945830e-03f, +1.409372192e-01f, +9.215283081e-01f, -5.549419960e-02f }, + { -6.048474993e-03f, +1.406951799e-01f, +9.217253573e-01f, -5.546710832e-02f }, + { -6.034023872e-03f, +1.404532889e-01f, +9.219221832e-01f, -5.543991994e-02f }, + { -6.019592461e-03f, +1.402115463e-01f, +9.221187858e-01f, -5.541263436e-02f }, + { -6.005180754e-03f, +1.399699520e-01f, +9.223151649e-01f, -5.538525151e-02f }, + { -5.990788743e-03f, +1.397285061e-01f, +9.225113204e-01f, -5.535777130e-02f }, + { -5.976416423e-03f, +1.394872087e-01f, +9.227072522e-01f, -5.533019365e-02f }, + { -5.962063788e-03f, +1.392460598e-01f, +9.229029601e-01f, -5.530251848e-02f }, + { -5.947730830e-03f, +1.390050594e-01f, +9.230984441e-01f, -5.527474572e-02f }, + { -5.933417543e-03f, +1.387642076e-01f, +9.232937039e-01f, -5.524687526e-02f }, + { -5.919123921e-03f, +1.385235045e-01f, +9.234887396e-01f, -5.521890704e-02f }, + { -5.904849957e-03f, +1.382829500e-01f, +9.236835509e-01f, -5.519084097e-02f }, + { -5.890595645e-03f, +1.380425443e-01f, +9.238781378e-01f, -5.516267698e-02f }, + { -5.876360977e-03f, +1.378022873e-01f, +9.240725001e-01f, -5.513441497e-02f }, + { -5.862145949e-03f, +1.375621792e-01f, +9.242666377e-01f, -5.510605486e-02f }, + { -5.847950552e-03f, +1.373222199e-01f, +9.244605504e-01f, -5.507759658e-02f }, + { -5.833774780e-03f, +1.370824095e-01f, +9.246542383e-01f, -5.504904005e-02f }, + { -5.819618626e-03f, +1.368427480e-01f, +9.248477010e-01f, -5.502038517e-02f }, + { -5.805482085e-03f, +1.366032356e-01f, +9.250409386e-01f, -5.499163187e-02f }, + { -5.791365148e-03f, +1.363638722e-01f, +9.252339509e-01f, -5.496278007e-02f }, + { -5.777267810e-03f, +1.361246578e-01f, +9.254267378e-01f, -5.493382968e-02f }, + { -5.763190063e-03f, +1.358855926e-01f, +9.256192991e-01f, -5.490478063e-02f }, + { -5.749131901e-03f, +1.356466765e-01f, +9.258116348e-01f, -5.487563282e-02f }, + { -5.735093317e-03f, +1.354079097e-01f, +9.260037447e-01f, -5.484638619e-02f }, + { -5.721074303e-03f, +1.351692920e-01f, +9.261956287e-01f, -5.481704064e-02f }, + { -5.707074854e-03f, +1.349308237e-01f, +9.263872867e-01f, -5.478759611e-02f }, + { -5.693094963e-03f, +1.346925048e-01f, +9.265787186e-01f, -5.475805249e-02f }, + { -5.679134621e-03f, +1.344543351e-01f, +9.267699242e-01f, -5.472840972e-02f }, + { -5.665193823e-03f, +1.342163150e-01f, +9.269609034e-01f, -5.469866771e-02f }, + { -5.651272561e-03f, +1.339784442e-01f, +9.271516562e-01f, -5.466882638e-02f }, + { -5.637370829e-03f, +1.337407230e-01f, +9.273421823e-01f, -5.463888565e-02f }, + { -5.623488619e-03f, +1.335031514e-01f, +9.275324817e-01f, -5.460884543e-02f }, + { -5.609625925e-03f, +1.332657293e-01f, +9.277225543e-01f, -5.457870565e-02f }, + { -5.595782739e-03f, +1.330284568e-01f, +9.279123998e-01f, -5.454846622e-02f }, + { -5.581959054e-03f, +1.327913341e-01f, +9.281020184e-01f, -5.451812706e-02f }, + { -5.568154863e-03f, +1.325543610e-01f, +9.282914097e-01f, -5.448768810e-02f }, + { -5.554370159e-03f, +1.323175377e-01f, +9.284805736e-01f, -5.445714924e-02f }, + { -5.540604935e-03f, +1.320808642e-01f, +9.286695102e-01f, -5.442651041e-02f }, + { -5.526859184e-03f, +1.318443405e-01f, +9.288582192e-01f, -5.439577153e-02f }, + { -5.513132898e-03f, +1.316079667e-01f, +9.290467005e-01f, -5.436493251e-02f }, + { -5.499426070e-03f, +1.313717428e-01f, +9.292349540e-01f, -5.433399327e-02f }, + { -5.485738694e-03f, +1.311356689e-01f, +9.294229797e-01f, -5.430295374e-02f }, + { -5.472070761e-03f, +1.308997450e-01f, +9.296107773e-01f, -5.427181382e-02f }, + { -5.458422264e-03f, +1.306639711e-01f, +9.297983467e-01f, -5.424057345e-02f }, + { -5.444793197e-03f, +1.304283474e-01f, +9.299856879e-01f, -5.420923253e-02f }, + { -5.431183551e-03f, +1.301928737e-01f, +9.301728008e-01f, -5.417779099e-02f }, + { -5.417593320e-03f, +1.299575502e-01f, +9.303596851e-01f, -5.414624875e-02f }, + { -5.404022496e-03f, +1.297223769e-01f, +9.305463408e-01f, -5.411460571e-02f }, + { -5.390471071e-03f, +1.294873538e-01f, +9.307327679e-01f, -5.408286181e-02f }, + { -5.376939039e-03f, +1.292524811e-01f, +9.309189660e-01f, -5.405101697e-02f }, + { -5.363426391e-03f, +1.290177586e-01f, +9.311049353e-01f, -5.401907109e-02f }, + { -5.349933121e-03f, +1.287831865e-01f, +9.312906754e-01f, -5.398702410e-02f }, + { -5.336459220e-03f, +1.285487648e-01f, +9.314761864e-01f, -5.395487592e-02f }, + { -5.323004682e-03f, +1.283144936e-01f, +9.316614680e-01f, -5.392262647e-02f }, + { -5.309569498e-03f, +1.280803728e-01f, +9.318465203e-01f, -5.389027566e-02f }, + { -5.296153661e-03f, +1.278464026e-01f, +9.320313430e-01f, -5.385782342e-02f }, + { -5.282757164e-03f, +1.276125829e-01f, +9.322159361e-01f, -5.382526966e-02f }, + { -5.269379998e-03f, +1.273789138e-01f, +9.324002994e-01f, -5.379261431e-02f }, + { -5.256022157e-03f, +1.271453953e-01f, +9.325844328e-01f, -5.375985728e-02f }, + { -5.242683633e-03f, +1.269120275e-01f, +9.327683363e-01f, -5.372699848e-02f }, + { -5.229364417e-03f, +1.266788105e-01f, +9.329520096e-01f, -5.369403785e-02f }, + { -5.216064503e-03f, +1.264457441e-01f, +9.331354527e-01f, -5.366097530e-02f }, + { -5.202783882e-03f, +1.262128286e-01f, +9.333186655e-01f, -5.362781074e-02f }, + { -5.189522547e-03f, +1.259800639e-01f, +9.335016478e-01f, -5.359454410e-02f }, + { -5.176280490e-03f, +1.257474501e-01f, +9.336843996e-01f, -5.356117530e-02f }, + { -5.163057703e-03f, +1.255149871e-01f, +9.338669207e-01f, -5.352770425e-02f }, + { -5.149854178e-03f, +1.252826751e-01f, +9.340492110e-01f, -5.349413088e-02f }, + { -5.136669908e-03f, +1.250505141e-01f, +9.342312704e-01f, -5.346045510e-02f }, + { -5.123504885e-03f, +1.248185041e-01f, +9.344130987e-01f, -5.342667683e-02f }, + { -5.110359100e-03f, +1.245866451e-01f, +9.345946960e-01f, -5.339279600e-02f }, + { -5.097232546e-03f, +1.243549372e-01f, +9.347760620e-01f, -5.335881251e-02f }, + { -5.084125216e-03f, +1.241233805e-01f, +9.349571966e-01f, -5.332472630e-02f }, + { -5.071037100e-03f, +1.238919749e-01f, +9.351380998e-01f, -5.329053728e-02f }, + { -5.057968191e-03f, +1.236607205e-01f, +9.353187714e-01f, -5.325624537e-02f }, + { -5.044918481e-03f, +1.234296174e-01f, +9.354992113e-01f, -5.322185049e-02f }, + { -5.031887963e-03f, +1.231986655e-01f, +9.356794194e-01f, -5.318735255e-02f }, + { -5.018876627e-03f, +1.229678649e-01f, +9.358593955e-01f, -5.315275148e-02f }, + { -5.005884466e-03f, +1.227372157e-01f, +9.360391397e-01f, -5.311804720e-02f }, + { -4.992911472e-03f, +1.225067179e-01f, +9.362186517e-01f, -5.308323963e-02f }, + { -4.979957637e-03f, +1.222763714e-01f, +9.363979314e-01f, -5.304832868e-02f }, + { -4.967022953e-03f, +1.220461765e-01f, +9.365769788e-01f, -5.301331428e-02f }, + { -4.954107411e-03f, +1.218161330e-01f, +9.367557936e-01f, -5.297819635e-02f }, + { -4.941211003e-03f, +1.215862410e-01f, +9.369343759e-01f, -5.294297479e-02f }, + { -4.928333721e-03f, +1.213565006e-01f, +9.371127255e-01f, -5.290764955e-02f }, + { -4.915475558e-03f, +1.211269118e-01f, +9.372908423e-01f, -5.287222052e-02f }, + { -4.902636504e-03f, +1.208974746e-01f, +9.374687261e-01f, -5.283668764e-02f }, + { -4.889816551e-03f, +1.206681891e-01f, +9.376463769e-01f, -5.280105083e-02f }, + { -4.877015692e-03f, +1.204390553e-01f, +9.378237946e-01f, -5.276531000e-02f }, + { -4.864233917e-03f, +1.202100732e-01f, +9.380009790e-01f, -5.272946506e-02f }, + { -4.851471219e-03f, +1.199812429e-01f, +9.381779301e-01f, -5.269351596e-02f }, + { -4.838727590e-03f, +1.197525644e-01f, +9.383546476e-01f, -5.265746259e-02f }, + { -4.826003020e-03f, +1.195240378e-01f, +9.385311316e-01f, -5.262130489e-02f }, + { -4.813297501e-03f, +1.192956630e-01f, +9.387073818e-01f, -5.258504277e-02f }, + { -4.800611026e-03f, +1.190674401e-01f, +9.388833983e-01f, -5.254867615e-02f }, + { -4.787943585e-03f, +1.188393692e-01f, +9.390591808e-01f, -5.251220495e-02f }, + { -4.775295171e-03f, +1.186114502e-01f, +9.392347294e-01f, -5.247562909e-02f }, + { -4.762665774e-03f, +1.183836832e-01f, +9.394100437e-01f, -5.243894849e-02f }, + { -4.750055387e-03f, +1.181560683e-01f, +9.395851238e-01f, -5.240216308e-02f }, + { -4.737464000e-03f, +1.179286055e-01f, +9.397599696e-01f, -5.236527277e-02f }, + { -4.724891606e-03f, +1.177012947e-01f, +9.399345809e-01f, -5.232827748e-02f }, + { -4.712338195e-03f, +1.174741362e-01f, +9.401089576e-01f, -5.229117713e-02f }, + { -4.699803759e-03f, +1.172471298e-01f, +9.402830997e-01f, -5.225397164e-02f }, + { -4.687288290e-03f, +1.170202755e-01f, +9.404570069e-01f, -5.221666093e-02f }, + { -4.674791779e-03f, +1.167935736e-01f, +9.406306792e-01f, -5.217924493e-02f }, + { -4.662314217e-03f, +1.165670239e-01f, +9.408041166e-01f, -5.214172354e-02f }, + { -4.649855595e-03f, +1.163406265e-01f, +9.409773188e-01f, -5.210409670e-02f }, + { -4.637415906e-03f, +1.161143815e-01f, +9.411502858e-01f, -5.206636432e-02f }, + { -4.624995140e-03f, +1.158882888e-01f, +9.413230174e-01f, -5.202852632e-02f }, + { -4.612593288e-03f, +1.156623486e-01f, +9.414955136e-01f, -5.199058263e-02f }, + { -4.600210342e-03f, +1.154365607e-01f, +9.416677743e-01f, -5.195253316e-02f }, + { -4.587846293e-03f, +1.152109254e-01f, +9.418397993e-01f, -5.191437783e-02f }, + { -4.575501133e-03f, +1.149854425e-01f, +9.420115885e-01f, -5.187611657e-02f }, + { -4.563174852e-03f, +1.147601122e-01f, +9.421831418e-01f, -5.183774929e-02f }, + { -4.550867441e-03f, +1.145349345e-01f, +9.423544592e-01f, -5.179927591e-02f }, + { -4.538578892e-03f, +1.143099093e-01f, +9.425255405e-01f, -5.176069636e-02f }, + { -4.526309197e-03f, +1.140850368e-01f, +9.426963856e-01f, -5.172201055e-02f }, + { -4.514058345e-03f, +1.138603169e-01f, +9.428669944e-01f, -5.168321841e-02f }, + { -4.501826329e-03f, +1.136357497e-01f, +9.430373667e-01f, -5.164431986e-02f }, + { -4.489613138e-03f, +1.134113352e-01f, +9.432075026e-01f, -5.160531481e-02f }, + { -4.477418765e-03f, +1.131870735e-01f, +9.433774019e-01f, -5.156620319e-02f }, + { -4.465243201e-03f, +1.129629646e-01f, +9.435470644e-01f, -5.152698491e-02f }, + { -4.453086436e-03f, +1.127390085e-01f, +9.437164901e-01f, -5.148765991e-02f }, + { -4.440948461e-03f, +1.125152052e-01f, +9.438856788e-01f, -5.144822810e-02f }, + { -4.428829267e-03f, +1.122915548e-01f, +9.440546305e-01f, -5.140868939e-02f }, + { -4.416728846e-03f, +1.120680573e-01f, +9.442233450e-01f, -5.136904371e-02f }, + { -4.404647188e-03f, +1.118447128e-01f, +9.443918223e-01f, -5.132929099e-02f }, + { -4.392584284e-03f, +1.116215212e-01f, +9.445600623e-01f, -5.128943114e-02f }, + { -4.380540125e-03f, +1.113984826e-01f, +9.447280647e-01f, -5.124946408e-02f }, + { -4.368514702e-03f, +1.111755970e-01f, +9.448958296e-01f, -5.120938973e-02f }, + { -4.356508006e-03f, +1.109528645e-01f, +9.450633568e-01f, -5.116920802e-02f }, + { -4.344520028e-03f, +1.107302850e-01f, +9.452306462e-01f, -5.112891887e-02f }, + { -4.332550758e-03f, +1.105078587e-01f, +9.453976978e-01f, -5.108852219e-02f }, + { -4.320600187e-03f, +1.102855855e-01f, +9.455645113e-01f, -5.104801790e-02f }, + { -4.308668306e-03f, +1.100634655e-01f, +9.457310868e-01f, -5.100740594e-02f }, + { -4.296755107e-03f, +1.098414987e-01f, +9.458974241e-01f, -5.096668621e-02f }, + { -4.284860578e-03f, +1.096196851e-01f, +9.460635230e-01f, -5.092585865e-02f }, + { -4.272984712e-03f, +1.093980247e-01f, +9.462293836e-01f, -5.088492316e-02f }, + { -4.261127499e-03f, +1.091765177e-01f, +9.463950056e-01f, -5.084387968e-02f }, + { -4.249288929e-03f, +1.089551640e-01f, +9.465603890e-01f, -5.080272812e-02f }, + { -4.237468994e-03f, +1.087339636e-01f, +9.467255337e-01f, -5.076146841e-02f }, + { -4.225667684e-03f, +1.085129166e-01f, +9.468904396e-01f, -5.072010046e-02f }, + { -4.213884989e-03f, +1.082920229e-01f, +9.470551065e-01f, -5.067862420e-02f }, + { -4.202120901e-03f, +1.080712828e-01f, +9.472195345e-01f, -5.063703954e-02f }, + { -4.190375409e-03f, +1.078506960e-01f, +9.473837233e-01f, -5.059534642e-02f }, + { -4.178648505e-03f, +1.076302628e-01f, +9.475476728e-01f, -5.055354474e-02f }, + { -4.166940178e-03f, +1.074099830e-01f, +9.477113830e-01f, -5.051163444e-02f }, + { -4.155250420e-03f, +1.071898568e-01f, +9.478748538e-01f, -5.046961543e-02f }, + { -4.143579220e-03f, +1.069698842e-01f, +9.480380851e-01f, -5.042748764e-02f }, + { -4.131926571e-03f, +1.067500652e-01f, +9.482010767e-01f, -5.038525098e-02f }, + { -4.120292461e-03f, +1.065303997e-01f, +9.483638285e-01f, -5.034290538e-02f }, + { -4.108676881e-03f, +1.063108880e-01f, +9.485263405e-01f, -5.030045076e-02f }, + { -4.097079822e-03f, +1.060915299e-01f, +9.486886126e-01f, -5.025788704e-02f }, + { -4.085501274e-03f, +1.058723254e-01f, +9.488506446e-01f, -5.021521414e-02f }, + { -4.073941228e-03f, +1.056532748e-01f, +9.490124364e-01f, -5.017243198e-02f }, + { -4.062399673e-03f, +1.054343779e-01f, +9.491739880e-01f, -5.012954049e-02f }, + { -4.050876601e-03f, +1.052156347e-01f, +9.493352993e-01f, -5.008653959e-02f }, + { -4.039372002e-03f, +1.049970454e-01f, +9.494963700e-01f, -5.004342920e-02f }, + { -4.027885865e-03f, +1.047786099e-01f, +9.496572003e-01f, -5.000020923e-02f }, + { -4.016418182e-03f, +1.045603282e-01f, +9.498177898e-01f, -4.995687962e-02f }, + { -4.004968942e-03f, +1.043422005e-01f, +9.499781386e-01f, -4.991344028e-02f }, + { -3.993538136e-03f, +1.041242266e-01f, +9.501382466e-01f, -4.986989114e-02f }, + { -3.982125754e-03f, +1.039064067e-01f, +9.502981136e-01f, -4.982623211e-02f }, + { -3.970731786e-03f, +1.036887408e-01f, +9.504577395e-01f, -4.978246313e-02f }, + { -3.959356223e-03f, +1.034712288e-01f, +9.506171243e-01f, -4.973858410e-02f }, + { -3.947999054e-03f, +1.032538708e-01f, +9.507762678e-01f, -4.969459496e-02f }, + { -3.936660271e-03f, +1.030366669e-01f, +9.509351700e-01f, -4.965049562e-02f }, + { -3.925339862e-03f, +1.028196171e-01f, +9.510938306e-01f, -4.960628601e-02f }, + { -3.914037818e-03f, +1.026027213e-01f, +9.512522498e-01f, -4.956196605e-02f }, + { -3.902754129e-03f, +1.023859797e-01f, +9.514104273e-01f, -4.951753566e-02f }, + { -3.891488786e-03f, +1.021693921e-01f, +9.515683630e-01f, -4.947299476e-02f }, + { -3.880241778e-03f, +1.019529588e-01f, +9.517260568e-01f, -4.942834328e-02f }, + { -3.869013095e-03f, +1.017366796e-01f, +9.518835087e-01f, -4.938358114e-02f }, + { -3.857802728e-03f, +1.015205546e-01f, +9.520407186e-01f, -4.933870825e-02f }, + { -3.846610666e-03f, +1.013045839e-01f, +9.521976863e-01f, -4.929372455e-02f }, + { -3.835436900e-03f, +1.010887674e-01f, +9.523544118e-01f, -4.924862995e-02f }, + { -3.824281419e-03f, +1.008731053e-01f, +9.525108949e-01f, -4.920342438e-02f }, + { -3.813144214e-03f, +1.006575974e-01f, +9.526671355e-01f, -4.915810776e-02f }, + { -3.802025274e-03f, +1.004422438e-01f, +9.528231336e-01f, -4.911268001e-02f }, + { -3.790924590e-03f, +1.002270446e-01f, +9.529788891e-01f, -4.906714105e-02f }, + { -3.779842151e-03f, +1.000119998e-01f, +9.531344018e-01f, -4.902149080e-02f }, + { -3.768777946e-03f, +9.979710935e-02f, +9.532896717e-01f, -4.897572920e-02f }, + { -3.757731967e-03f, +9.958237334e-02f, +9.534446987e-01f, -4.892985616e-02f }, + { -3.746704203e-03f, +9.936779178e-02f, +9.535994826e-01f, -4.888387160e-02f }, + { -3.735694643e-03f, +9.915336469e-02f, +9.537540234e-01f, -4.883777544e-02f }, + { -3.724703278e-03f, +9.893909208e-02f, +9.539083209e-01f, -4.879156762e-02f }, + { -3.713730097e-03f, +9.872497399e-02f, +9.540623752e-01f, -4.874524804e-02f }, + { -3.702775091e-03f, +9.851101044e-02f, +9.542161860e-01f, -4.869881664e-02f }, + { -3.691838248e-03f, +9.829720144e-02f, +9.543697533e-01f, -4.865227334e-02f }, + { -3.680919559e-03f, +9.808354703e-02f, +9.545230770e-01f, -4.860561805e-02f }, + { -3.670019013e-03f, +9.787004721e-02f, +9.546761570e-01f, -4.855885071e-02f }, + { -3.659136601e-03f, +9.765670203e-02f, +9.548289931e-01f, -4.851197123e-02f }, + { -3.648272311e-03f, +9.744351149e-02f, +9.549815854e-01f, -4.846497954e-02f }, + { -3.637426133e-03f, +9.723047561e-02f, +9.551339337e-01f, -4.841787556e-02f }, + { -3.626598058e-03f, +9.701759443e-02f, +9.552860379e-01f, -4.837065921e-02f }, + { -3.615788075e-03f, +9.680486796e-02f, +9.554378979e-01f, -4.832333042e-02f }, + { -3.604996173e-03f, +9.659229622e-02f, +9.555895136e-01f, -4.827588911e-02f }, + { -3.594222342e-03f, +9.637987924e-02f, +9.557408849e-01f, -4.822833521e-02f }, + { -3.583466571e-03f, +9.616761703e-02f, +9.558920118e-01f, -4.818066862e-02f }, + { -3.572728851e-03f, +9.595550962e-02f, +9.560428941e-01f, -4.813288929e-02f }, + { -3.562009171e-03f, +9.574355703e-02f, +9.561935317e-01f, -4.808499713e-02f }, + { -3.551307520e-03f, +9.553175927e-02f, +9.563439245e-01f, -4.803699206e-02f }, + { -3.540623888e-03f, +9.532011637e-02f, +9.564940726e-01f, -4.798887401e-02f }, + { -3.529958264e-03f, +9.510862835e-02f, +9.566439756e-01f, -4.794064290e-02f }, + { -3.519310638e-03f, +9.489729524e-02f, +9.567936336e-01f, -4.789229866e-02f }, + { -3.508680999e-03f, +9.468611704e-02f, +9.569430465e-01f, -4.784384120e-02f }, + { -3.498069337e-03f, +9.447509378e-02f, +9.570922142e-01f, -4.779527045e-02f }, + { -3.487475641e-03f, +9.426422548e-02f, +9.572411365e-01f, -4.774658634e-02f }, + { -3.476899901e-03f, +9.405351217e-02f, +9.573898135e-01f, -4.769778879e-02f }, + { -3.466342106e-03f, +9.384295385e-02f, +9.575382449e-01f, -4.764887772e-02f }, + { -3.455802245e-03f, +9.363255055e-02f, +9.576864307e-01f, -4.759985305e-02f }, + { -3.445280308e-03f, +9.342230229e-02f, +9.578343708e-01f, -4.755071471e-02f }, + { -3.434776284e-03f, +9.321220909e-02f, +9.579820651e-01f, -4.750146263e-02f }, + { -3.424290163e-03f, +9.300227097e-02f, +9.581295136e-01f, -4.745209672e-02f }, + { -3.413821934e-03f, +9.279248795e-02f, +9.582767160e-01f, -4.740261690e-02f }, + { -3.403371585e-03f, +9.258286004e-02f, +9.584236724e-01f, -4.735302311e-02f }, + { -3.392939107e-03f, +9.237338726e-02f, +9.585703827e-01f, -4.730331527e-02f }, + { -3.382524489e-03f, +9.216406964e-02f, +9.587168467e-01f, -4.725349330e-02f }, + { -3.372127720e-03f, +9.195490720e-02f, +9.588630643e-01f, -4.720355712e-02f }, + { -3.361748789e-03f, +9.174589994e-02f, +9.590090355e-01f, -4.715350666e-02f }, + { -3.351387685e-03f, +9.153704790e-02f, +9.591547602e-01f, -4.710334184e-02f }, + { -3.341044398e-03f, +9.132835108e-02f, +9.593002383e-01f, -4.705306259e-02f }, + { -3.330718916e-03f, +9.111980950e-02f, +9.594454696e-01f, -4.700266883e-02f }, + { -3.320411230e-03f, +9.091142320e-02f, +9.595904542e-01f, -4.695216048e-02f }, + { -3.310121328e-03f, +9.070319217e-02f, +9.597351919e-01f, -4.690153746e-02f }, + { -3.299849199e-03f, +9.049511645e-02f, +9.598796825e-01f, -4.685079971e-02f }, + { -3.289594832e-03f, +9.028719604e-02f, +9.600239261e-01f, -4.679994715e-02f }, + { -3.279358217e-03f, +9.007943096e-02f, +9.601679226e-01f, -4.674897969e-02f }, + { -3.269139342e-03f, +8.987182124e-02f, +9.603116718e-01f, -4.669789727e-02f }, + { -3.258938197e-03f, +8.966436689e-02f, +9.604551736e-01f, -4.664669981e-02f }, + { -3.248754771e-03f, +8.945706793e-02f, +9.605984280e-01f, -4.659538723e-02f }, + { -3.238589053e-03f, +8.924992437e-02f, +9.607414349e-01f, -4.654395945e-02f }, + { -3.228441031e-03f, +8.904293624e-02f, +9.608841941e-01f, -4.649241641e-02f }, + { -3.218310696e-03f, +8.883610354e-02f, +9.610267057e-01f, -4.644075802e-02f }, + { -3.208198035e-03f, +8.862942629e-02f, +9.611689695e-01f, -4.638898421e-02f }, + { -3.198103037e-03f, +8.842290452e-02f, +9.613109853e-01f, -4.633709491e-02f }, + { -3.188025693e-03f, +8.821653824e-02f, +9.614527532e-01f, -4.628509003e-02f }, + { -3.177965990e-03f, +8.801032746e-02f, +9.615942731e-01f, -4.623296951e-02f }, + { -3.167923918e-03f, +8.780427220e-02f, +9.617355447e-01f, -4.618073326e-02f }, + { -3.157899465e-03f, +8.759837247e-02f, +9.618765682e-01f, -4.612838122e-02f }, + { -3.147892621e-03f, +8.739262830e-02f, +9.620173433e-01f, -4.607591330e-02f }, + { -3.137903374e-03f, +8.718703970e-02f, +9.621578700e-01f, -4.602332944e-02f }, + { -3.127931713e-03f, +8.698160668e-02f, +9.622981482e-01f, -4.597062955e-02f }, + { -3.117977628e-03f, +8.677632927e-02f, +9.624381778e-01f, -4.591781356e-02f }, + { -3.108041106e-03f, +8.657120747e-02f, +9.625779587e-01f, -4.586488140e-02f }, + { -3.098122137e-03f, +8.636624129e-02f, +9.627174908e-01f, -4.581183299e-02f }, + { -3.088220709e-03f, +8.616143077e-02f, +9.628567741e-01f, -4.575866826e-02f }, + { -3.078336812e-03f, +8.595677591e-02f, +9.629958084e-01f, -4.570538712e-02f }, + { -3.068470433e-03f, +8.575227672e-02f, +9.631345937e-01f, -4.565198951e-02f }, + { -3.058621563e-03f, +8.554793322e-02f, +9.632731298e-01f, -4.559847535e-02f }, + { -3.048790189e-03f, +8.534374543e-02f, +9.634114168e-01f, -4.554484457e-02f }, + { -3.038976300e-03f, +8.513971336e-02f, +9.635494545e-01f, -4.549109708e-02f }, + { -3.029179886e-03f, +8.493583703e-02f, +9.636872427e-01f, -4.543723283e-02f }, + { -3.019400934e-03f, +8.473211644e-02f, +9.638247816e-01f, -4.538325172e-02f }, + { -3.009639433e-03f, +8.452855162e-02f, +9.639620708e-01f, -4.532915369e-02f }, + { -2.999895373e-03f, +8.432514258e-02f, +9.640991104e-01f, -4.527493866e-02f }, + { -2.990168741e-03f, +8.412188933e-02f, +9.642359003e-01f, -4.522060655e-02f }, + { -2.980459527e-03f, +8.391879188e-02f, +9.643724404e-01f, -4.516615730e-02f }, + { -2.970767718e-03f, +8.371585026e-02f, +9.645087305e-01f, -4.511159083e-02f }, + { -2.961093304e-03f, +8.351306447e-02f, +9.646447707e-01f, -4.505690705e-02f }, + { -2.951436274e-03f, +8.331043452e-02f, +9.647805608e-01f, -4.500210591e-02f }, + { -2.941796615e-03f, +8.310796044e-02f, +9.649161008e-01f, -4.494718732e-02f }, + { -2.932174316e-03f, +8.290564223e-02f, +9.650513905e-01f, -4.489215120e-02f }, + { -2.922569366e-03f, +8.270347990e-02f, +9.651864298e-01f, -4.483699749e-02f }, + { -2.912981754e-03f, +8.250147348e-02f, +9.653212188e-01f, -4.478172612e-02f }, + { -2.903411467e-03f, +8.229962297e-02f, +9.654557573e-01f, -4.472633699e-02f }, + { -2.893858495e-03f, +8.209792838e-02f, +9.655900451e-01f, -4.467083005e-02f }, + { -2.884322825e-03f, +8.189638974e-02f, +9.657240824e-01f, -4.461520522e-02f }, + { -2.874804447e-03f, +8.169500704e-02f, +9.658578688e-01f, -4.455946242e-02f }, + { -2.865303349e-03f, +8.149378031e-02f, +9.659914044e-01f, -4.450360157e-02f }, + { -2.855819519e-03f, +8.129270955e-02f, +9.661246891e-01f, -4.444762262e-02f }, + { -2.846352946e-03f, +8.109179479e-02f, +9.662577228e-01f, -4.439152547e-02f }, + { -2.836903618e-03f, +8.089103602e-02f, +9.663905054e-01f, -4.433531006e-02f }, + { -2.827471523e-03f, +8.069043326e-02f, +9.665230369e-01f, -4.427897631e-02f }, + { -2.818056650e-03f, +8.048998653e-02f, +9.666553170e-01f, -4.422252415e-02f }, + { -2.808658988e-03f, +8.028969584e-02f, +9.667873459e-01f, -4.416595350e-02f }, + { -2.799278524e-03f, +8.008956119e-02f, +9.669191233e-01f, -4.410926430e-02f }, + { -2.789915247e-03f, +7.988958260e-02f, +9.670506492e-01f, -4.405245646e-02f }, + { -2.780569145e-03f, +7.968976008e-02f, +9.671819235e-01f, -4.399552991e-02f }, + { -2.771240206e-03f, +7.949009365e-02f, +9.673129462e-01f, -4.393848458e-02f }, + { -2.761928420e-03f, +7.929058330e-02f, +9.674437171e-01f, -4.388132040e-02f }, + { -2.752633774e-03f, +7.909122907e-02f, +9.675742361e-01f, -4.382403729e-02f }, + { -2.743356256e-03f, +7.889203094e-02f, +9.677045033e-01f, -4.376663518e-02f }, + { -2.734095855e-03f, +7.869298894e-02f, +9.678345184e-01f, -4.370911400e-02f }, + { -2.724852558e-03f, +7.849410308e-02f, +9.679642815e-01f, -4.365147366e-02f }, + { -2.715626355e-03f, +7.829537336e-02f, +9.680937924e-01f, -4.359371410e-02f }, + { -2.706417234e-03f, +7.809679980e-02f, +9.682230510e-01f, -4.353583525e-02f }, + { -2.697225181e-03f, +7.789838241e-02f, +9.683520574e-01f, -4.347783703e-02f }, + { -2.688050187e-03f, +7.770012120e-02f, +9.684808113e-01f, -4.341971936e-02f }, + { -2.678892238e-03f, +7.750201617e-02f, +9.686093127e-01f, -4.336148218e-02f }, + { -2.669751324e-03f, +7.730406734e-02f, +9.687375615e-01f, -4.330312541e-02f }, + { -2.660627431e-03f, +7.710627472e-02f, +9.688655578e-01f, -4.324464897e-02f }, + { -2.651520549e-03f, +7.690863832e-02f, +9.689933012e-01f, -4.318605280e-02f }, + { -2.642430666e-03f, +7.671115814e-02f, +9.691207919e-01f, -4.312733682e-02f }, + { -2.633357769e-03f, +7.651383420e-02f, +9.692480296e-01f, -4.306850096e-02f }, + { -2.624301846e-03f, +7.631666650e-02f, +9.693750144e-01f, -4.300954515e-02f }, + { -2.615262886e-03f, +7.611965506e-02f, +9.695017462e-01f, -4.295046931e-02f }, + { -2.606240877e-03f, +7.592279989e-02f, +9.696282248e-01f, -4.289127336e-02f }, + { -2.597235807e-03f, +7.572610098e-02f, +9.697544501e-01f, -4.283195724e-02f }, + { -2.588247664e-03f, +7.552955836e-02f, +9.698804222e-01f, -4.277252088e-02f }, + { -2.579276435e-03f, +7.533317203e-02f, +9.700061410e-01f, -4.271296420e-02f }, + { -2.570322110e-03f, +7.513694200e-02f, +9.701316062e-01f, -4.265328712e-02f }, + { -2.561384675e-03f, +7.494086827e-02f, +9.702568180e-01f, -4.259348958e-02f }, + { -2.552464119e-03f, +7.474495086e-02f, +9.703817761e-01f, -4.253357150e-02f }, + { -2.543560430e-03f, +7.454918978e-02f, +9.705064805e-01f, -4.247353281e-02f }, + { -2.534673595e-03f, +7.435358503e-02f, +9.706309312e-01f, -4.241337343e-02f }, + { -2.525803603e-03f, +7.415813662e-02f, +9.707551280e-01f, -4.235309331e-02f }, + { -2.516950442e-03f, +7.396284455e-02f, +9.708790709e-01f, -4.229269235e-02f }, + { -2.508114099e-03f, +7.376770885e-02f, +9.710027598e-01f, -4.223217049e-02f }, + { -2.499294563e-03f, +7.357272950e-02f, +9.711261947e-01f, -4.217152766e-02f }, + { -2.490491821e-03f, +7.337790653e-02f, +9.712493753e-01f, -4.211076378e-02f }, + { -2.481705861e-03f, +7.318323994e-02f, +9.713723017e-01f, -4.204987879e-02f }, + { -2.472936671e-03f, +7.298872973e-02f, +9.714949738e-01f, -4.198887261e-02f }, + { -2.464184239e-03f, +7.279437592e-02f, +9.716173916e-01f, -4.192774516e-02f }, + { -2.455448552e-03f, +7.260017851e-02f, +9.717395548e-01f, -4.186649638e-02f }, + { -2.446729599e-03f, +7.240613750e-02f, +9.718614635e-01f, -4.180512620e-02f }, + { -2.438027368e-03f, +7.221225291e-02f, +9.719831176e-01f, -4.174363453e-02f }, + { -2.429341845e-03f, +7.201852474e-02f, +9.721045170e-01f, -4.168202131e-02f }, + { -2.420673020e-03f, +7.182495300e-02f, +9.722256616e-01f, -4.162028648e-02f }, + { -2.412020878e-03f, +7.163153769e-02f, +9.723465513e-01f, -4.155842994e-02f }, + { -2.403385410e-03f, +7.143827882e-02f, +9.724671862e-01f, -4.149645164e-02f }, + { -2.394766601e-03f, +7.124517640e-02f, +9.725875660e-01f, -4.143435151e-02f }, + { -2.386164441e-03f, +7.105223044e-02f, +9.727076907e-01f, -4.137212946e-02f }, + { -2.377578915e-03f, +7.085944093e-02f, +9.728275603e-01f, -4.130978543e-02f }, + { -2.369010014e-03f, +7.066680788e-02f, +9.729471746e-01f, -4.124731934e-02f }, + { -2.360457723e-03f, +7.047433131e-02f, +9.730665337e-01f, -4.118473113e-02f }, + { -2.351922030e-03f, +7.028201121e-02f, +9.731856373e-01f, -4.112202073e-02f }, + { -2.343402924e-03f, +7.008984759e-02f, +9.733044855e-01f, -4.105918805e-02f }, + { -2.334900392e-03f, +6.989784046e-02f, +9.734230782e-01f, -4.099623304e-02f }, + { -2.326414422e-03f, +6.970598983e-02f, +9.735414153e-01f, -4.093315561e-02f }, + { -2.317945001e-03f, +6.951429569e-02f, +9.736594966e-01f, -4.086995570e-02f }, + { -2.309492116e-03f, +6.932275805e-02f, +9.737773222e-01f, -4.080663324e-02f }, + { -2.301055756e-03f, +6.913137692e-02f, +9.738948920e-01f, -4.074318815e-02f }, + { -2.292635908e-03f, +6.894015231e-02f, +9.740122059e-01f, -4.067962036e-02f }, + { -2.284232560e-03f, +6.874908421e-02f, +9.741292638e-01f, -4.061592981e-02f }, + { -2.275845698e-03f, +6.855817263e-02f, +9.742460656e-01f, -4.055211641e-02f }, + { -2.267475312e-03f, +6.836741758e-02f, +9.743626113e-01f, -4.048818011e-02f }, + { -2.259121387e-03f, +6.817681906e-02f, +9.744789009e-01f, -4.042412082e-02f }, + { -2.250783913e-03f, +6.798637707e-02f, +9.745949341e-01f, -4.035993848e-02f }, + { -2.242462875e-03f, +6.779609163e-02f, +9.747107110e-01f, -4.029563302e-02f }, + { -2.234158263e-03f, +6.760596272e-02f, +9.748262314e-01f, -4.023120436e-02f }, + { -2.225870062e-03f, +6.741599037e-02f, +9.749414954e-01f, -4.016665244e-02f }, + { -2.217598262e-03f, +6.722617456e-02f, +9.750565028e-01f, -4.010197718e-02f }, + { -2.209342848e-03f, +6.703651531e-02f, +9.751712536e-01f, -4.003717851e-02f }, + { -2.201103809e-03f, +6.684701262e-02f, +9.752857476e-01f, -3.997225637e-02f }, + { -2.192881133e-03f, +6.665766648e-02f, +9.753999849e-01f, -3.990721068e-02f }, + { -2.184674805e-03f, +6.646847692e-02f, +9.755139653e-01f, -3.984204137e-02f }, + { -2.176484815e-03f, +6.627944392e-02f, +9.756276888e-01f, -3.977674836e-02f }, + { -2.168311149e-03f, +6.609056749e-02f, +9.757411552e-01f, -3.971133160e-02f }, + { -2.160153795e-03f, +6.590184763e-02f, +9.758543646e-01f, -3.964579101e-02f }, + { -2.152012740e-03f, +6.571328435e-02f, +9.759673169e-01f, -3.958012651e-02f }, + { -2.143887971e-03f, +6.552487765e-02f, +9.760800119e-01f, -3.951433805e-02f }, + { -2.135779477e-03f, +6.533662753e-02f, +9.761924497e-01f, -3.944842554e-02f }, + { -2.127687243e-03f, +6.514853400e-02f, +9.763046301e-01f, -3.938238892e-02f }, + { -2.119611258e-03f, +6.496059705e-02f, +9.764165530e-01f, -3.931622812e-02f }, + { -2.111551509e-03f, +6.477281670e-02f, +9.765282185e-01f, -3.924994306e-02f }, + { -2.103507983e-03f, +6.458519293e-02f, +9.766396264e-01f, -3.918353368e-02f }, + { -2.095480667e-03f, +6.439772576e-02f, +9.767507766e-01f, -3.911699991e-02f }, + { -2.087469549e-03f, +6.421041518e-02f, +9.768616692e-01f, -3.905034167e-02f }, + { -2.079474617e-03f, +6.402326120e-02f, +9.769723040e-01f, -3.898355890e-02f }, + { -2.071495856e-03f, +6.383626381e-02f, +9.770826809e-01f, -3.891665153e-02f }, + { -2.063533255e-03f, +6.364942303e-02f, +9.771927998e-01f, -3.884961949e-02f }, + { -2.055586801e-03f, +6.346273885e-02f, +9.773026608e-01f, -3.878246270e-02f }, + { -2.047656480e-03f, +6.327621127e-02f, +9.774122638e-01f, -3.871518110e-02f }, + { -2.039742281e-03f, +6.308984030e-02f, +9.775216086e-01f, -3.864777462e-02f }, + { -2.031844191e-03f, +6.290362593e-02f, +9.776306952e-01f, -3.858024318e-02f }, + { -2.023962196e-03f, +6.271756817e-02f, +9.777395235e-01f, -3.851258673e-02f }, + { -2.016096283e-03f, +6.253166701e-02f, +9.778480935e-01f, -3.844480518e-02f }, + { -2.008246441e-03f, +6.234592246e-02f, +9.779564051e-01f, -3.837689847e-02f }, + { -2.000412656e-03f, +6.216033452e-02f, +9.780644583e-01f, -3.830886653e-02f }, + { -1.992594915e-03f, +6.197490319e-02f, +9.781722528e-01f, -3.824070929e-02f }, + { -1.984793206e-03f, +6.178962847e-02f, +9.782797888e-01f, -3.817242668e-02f }, + { -1.977007515e-03f, +6.160451036e-02f, +9.783870661e-01f, -3.810401864e-02f }, + { -1.969237830e-03f, +6.141954886e-02f, +9.784940847e-01f, -3.803548508e-02f }, + { -1.961484138e-03f, +6.123474396e-02f, +9.786008444e-01f, -3.796682595e-02f }, + { -1.953746425e-03f, +6.105009568e-02f, +9.787073453e-01f, -3.789804118e-02f }, + { -1.946024680e-03f, +6.086560401e-02f, +9.788135872e-01f, -3.782913069e-02f }, + { -1.938318888e-03f, +6.068126894e-02f, +9.789195701e-01f, -3.776009441e-02f }, + { -1.930629038e-03f, +6.049709048e-02f, +9.790252939e-01f, -3.769093228e-02f }, + { -1.922955116e-03f, +6.031306864e-02f, +9.791307585e-01f, -3.762164423e-02f }, + { -1.915297108e-03f, +6.012920340e-02f, +9.792359640e-01f, -3.755223018e-02f }, + { -1.907655004e-03f, +5.994549476e-02f, +9.793409101e-01f, -3.748269008e-02f }, + { -1.900028788e-03f, +5.976194274e-02f, +9.794455969e-01f, -3.741302385e-02f }, + { -1.892418449e-03f, +5.957854731e-02f, +9.795500243e-01f, -3.734323142e-02f }, + { -1.884823973e-03f, +5.939530849e-02f, +9.796541922e-01f, -3.727331272e-02f }, + { -1.877245347e-03f, +5.921222628e-02f, +9.797581006e-01f, -3.720326769e-02f }, + { -1.869682558e-03f, +5.902930066e-02f, +9.798617493e-01f, -3.713309626e-02f }, + { -1.862135594e-03f, +5.884653165e-02f, +9.799651384e-01f, -3.706279835e-02f }, + { -1.854604440e-03f, +5.866391923e-02f, +9.800682677e-01f, -3.699237391e-02f }, + { -1.847089085e-03f, +5.848146341e-02f, +9.801711372e-01f, -3.692182285e-02f }, + { -1.839589515e-03f, +5.829916418e-02f, +9.802737469e-01f, -3.685114512e-02f }, + { -1.832105717e-03f, +5.811702155e-02f, +9.803760966e-01f, -3.678034064e-02f }, + { -1.824637677e-03f, +5.793503550e-02f, +9.804781863e-01f, -3.670940934e-02f }, + { -1.817185384e-03f, +5.775320605e-02f, +9.805800159e-01f, -3.663835117e-02f }, + { -1.809748823e-03f, +5.757153318e-02f, +9.806815854e-01f, -3.656716604e-02f }, + { -1.802327981e-03f, +5.739001690e-02f, +9.807828947e-01f, -3.649585390e-02f }, + { -1.794922846e-03f, +5.720865720e-02f, +9.808839437e-01f, -3.642441467e-02f }, + { -1.787533404e-03f, +5.702745407e-02f, +9.809847324e-01f, -3.635284829e-02f }, + { -1.780159642e-03f, +5.684640752e-02f, +9.810852607e-01f, -3.628115468e-02f }, + { -1.772801548e-03f, +5.666551755e-02f, +9.811855286e-01f, -3.620933378e-02f }, + { -1.765459107e-03f, +5.648478415e-02f, +9.812855360e-01f, -3.613738552e-02f }, + { -1.758132306e-03f, +5.630420731e-02f, +9.813852827e-01f, -3.606530984e-02f }, + { -1.750821133e-03f, +5.612378704e-02f, +9.814847688e-01f, -3.599310667e-02f }, + { -1.743525575e-03f, +5.594352333e-02f, +9.815839943e-01f, -3.592077593e-02f }, + { -1.736245617e-03f, +5.576341617e-02f, +9.816829589e-01f, -3.584831757e-02f }, + { -1.728981247e-03f, +5.558346557e-02f, +9.817816627e-01f, -3.577573150e-02f }, + { -1.721732452e-03f, +5.540367152e-02f, +9.818801056e-01f, -3.570301768e-02f }, + { -1.714499218e-03f, +5.522403402e-02f, +9.819782876e-01f, -3.563017602e-02f }, + { -1.707281532e-03f, +5.504455306e-02f, +9.820762086e-01f, -3.555720646e-02f }, + { -1.700079381e-03f, +5.486522863e-02f, +9.821738684e-01f, -3.548410894e-02f }, + { -1.692892751e-03f, +5.468606075e-02f, +9.822712671e-01f, -3.541088338e-02f }, + { -1.685721630e-03f, +5.450704939e-02f, +9.823684047e-01f, -3.533752973e-02f }, + { -1.678566004e-03f, +5.432819456e-02f, +9.824652809e-01f, -3.526404790e-02f }, + { -1.671425859e-03f, +5.414949625e-02f, +9.825618958e-01f, -3.519043784e-02f }, + { -1.664301183e-03f, +5.397095446e-02f, +9.826582494e-01f, -3.511669948e-02f }, + { -1.657191962e-03f, +5.379256918e-02f, +9.827543414e-01f, -3.504283275e-02f }, + { -1.650098183e-03f, +5.361434040e-02f, +9.828501720e-01f, -3.496883758e-02f }, + { -1.643019832e-03f, +5.343626813e-02f, +9.829457410e-01f, -3.489471391e-02f }, + { -1.635956896e-03f, +5.325835236e-02f, +9.830410484e-01f, -3.482046167e-02f }, + { -1.628909362e-03f, +5.308059308e-02f, +9.831360941e-01f, -3.474608080e-02f }, + { -1.621877216e-03f, +5.290299029e-02f, +9.832308780e-01f, -3.467157122e-02f }, + { -1.614860445e-03f, +5.272554398e-02f, +9.833254001e-01f, -3.459693287e-02f }, + { -1.607859036e-03f, +5.254825415e-02f, +9.834196603e-01f, -3.452216568e-02f }, + { -1.600872975e-03f, +5.237112079e-02f, +9.835136587e-01f, -3.444726959e-02f }, + { -1.593902249e-03f, +5.219414389e-02f, +9.836073950e-01f, -3.437224453e-02f }, + { -1.586946844e-03f, +5.201732345e-02f, +9.837008692e-01f, -3.429709044e-02f }, + { -1.580006747e-03f, +5.184065947e-02f, +9.837940814e-01f, -3.422180724e-02f }, + { -1.573081945e-03f, +5.166415193e-02f, +9.838870314e-01f, -3.414639488e-02f }, + { -1.566172424e-03f, +5.148780084e-02f, +9.839797191e-01f, -3.407085327e-02f }, + { -1.559278170e-03f, +5.131160618e-02f, +9.840721446e-01f, -3.399518237e-02f }, + { -1.552399171e-03f, +5.113556795e-02f, +9.841643077e-01f, -3.391938210e-02f }, + { -1.545535412e-03f, +5.095968615e-02f, +9.842562085e-01f, -3.384345240e-02f }, + { -1.538686881e-03f, +5.078396076e-02f, +9.843478467e-01f, -3.376739320e-02f }, + { -1.531853564e-03f, +5.060839177e-02f, +9.844392225e-01f, -3.369120443e-02f }, + { -1.525035446e-03f, +5.043297919e-02f, +9.845303356e-01f, -3.361488603e-02f }, + { -1.518232516e-03f, +5.025772301e-02f, +9.846211862e-01f, -3.353843793e-02f }, + { -1.511444759e-03f, +5.008262322e-02f, +9.847117740e-01f, -3.346186007e-02f }, + { -1.504672161e-03f, +4.990767980e-02f, +9.848020991e-01f, -3.338515239e-02f }, + { -1.497914710e-03f, +4.973289276e-02f, +9.848921614e-01f, -3.330831480e-02f }, + { -1.491172391e-03f, +4.955826209e-02f, +9.849819608e-01f, -3.323134726e-02f }, + { -1.484445192e-03f, +4.938378778e-02f, +9.850714973e-01f, -3.315424969e-02f }, + { -1.477733098e-03f, +4.920946982e-02f, +9.851607708e-01f, -3.307702203e-02f }, + { -1.471036096e-03f, +4.903530820e-02f, +9.852497812e-01f, -3.299966421e-02f }, + { -1.464354173e-03f, +4.886130292e-02f, +9.853385286e-01f, -3.292217617e-02f }, + { -1.457687314e-03f, +4.868745396e-02f, +9.854270129e-01f, -3.284455785e-02f }, + { -1.451035507e-03f, +4.851376133e-02f, +9.855152339e-01f, -3.276680917e-02f }, + { -1.444398738e-03f, +4.834022501e-02f, +9.856031917e-01f, -3.268893007e-02f }, + { -1.437776992e-03f, +4.816684499e-02f, +9.856908861e-01f, -3.261092049e-02f }, + { -1.431170257e-03f, +4.799362127e-02f, +9.857783172e-01f, -3.253278036e-02f }, + { -1.424578519e-03f, +4.782055384e-02f, +9.858654848e-01f, -3.245450962e-02f }, + { -1.418001764e-03f, +4.764764268e-02f, +9.859523890e-01f, -3.237610820e-02f }, + { -1.411439979e-03f, +4.747488779e-02f, +9.860390297e-01f, -3.229757604e-02f }, + { -1.404893149e-03f, +4.730228917e-02f, +9.861254067e-01f, -3.221891307e-02f }, + { -1.398361262e-03f, +4.712984679e-02f, +9.862115201e-01f, -3.214011922e-02f }, + { -1.391844303e-03f, +4.695756066e-02f, +9.862973698e-01f, -3.206119444e-02f }, + { -1.385342260e-03f, +4.678543077e-02f, +9.863829557e-01f, -3.198213866e-02f }, + { -1.378855117e-03f, +4.661345709e-02f, +9.864682779e-01f, -3.190295181e-02f }, + { -1.372382862e-03f, +4.644163964e-02f, +9.865533361e-01f, -3.182363382e-02f }, + { -1.365925481e-03f, +4.626997838e-02f, +9.866381305e-01f, -3.174418465e-02f }, + { -1.359482960e-03f, +4.609847333e-02f, +9.867226608e-01f, -3.166460421e-02f }, + { -1.353055286e-03f, +4.592712446e-02f, +9.868069272e-01f, -3.158489244e-02f }, + { -1.346642444e-03f, +4.575593177e-02f, +9.868909295e-01f, -3.150504929e-02f }, + { -1.340244422e-03f, +4.558489524e-02f, +9.869746676e-01f, -3.142507468e-02f }, + { -1.333861204e-03f, +4.541401487e-02f, +9.870581415e-01f, -3.134496856e-02f }, + { -1.327492778e-03f, +4.524329065e-02f, +9.871413513e-01f, -3.126473085e-02f }, + { -1.321139130e-03f, +4.507272256e-02f, +9.872242967e-01f, -3.118436150e-02f }, + { -1.314800245e-03f, +4.490231060e-02f, +9.873069778e-01f, -3.110386044e-02f }, + { -1.308476111e-03f, +4.473205475e-02f, +9.873893944e-01f, -3.102322761e-02f }, + { -1.302166714e-03f, +4.456195501e-02f, +9.874715467e-01f, -3.094246293e-02f }, + { -1.295872038e-03f, +4.439201136e-02f, +9.875534344e-01f, -3.086156636e-02f }, + { -1.289592072e-03f, +4.422222380e-02f, +9.876350576e-01f, -3.078053782e-02f }, + { -1.283326801e-03f, +4.405259231e-02f, +9.877164161e-01f, -3.069937725e-02f }, + { -1.277076211e-03f, +4.388311688e-02f, +9.877975101e-01f, -3.061808459e-02f }, + { -1.270840289e-03f, +4.371379750e-02f, +9.878783393e-01f, -3.053665977e-02f }, + { -1.264619020e-03f, +4.354463416e-02f, +9.879589037e-01f, -3.045510273e-02f }, + { -1.258412391e-03f, +4.337562684e-02f, +9.880392034e-01f, -3.037341341e-02f }, + { -1.252220388e-03f, +4.320677555e-02f, +9.881192381e-01f, -3.029159174e-02f }, + { -1.246042996e-03f, +4.303808025e-02f, +9.881990080e-01f, -3.020963766e-02f }, + { -1.239880203e-03f, +4.286954095e-02f, +9.882785129e-01f, -3.012755111e-02f }, + { -1.233731995e-03f, +4.270115763e-02f, +9.883577528e-01f, -3.004533202e-02f }, + { -1.227598357e-03f, +4.253293028e-02f, +9.884367276e-01f, -2.996298034e-02f }, + { -1.221479275e-03f, +4.236485889e-02f, +9.885154374e-01f, -2.988049599e-02f }, + { -1.215374737e-03f, +4.219694344e-02f, +9.885938819e-01f, -2.979787891e-02f }, + { -1.209284727e-03f, +4.202918393e-02f, +9.886720613e-01f, -2.971512904e-02f }, + { -1.203209232e-03f, +4.186158033e-02f, +9.887499753e-01f, -2.963224632e-02f }, + { -1.197148238e-03f, +4.169413264e-02f, +9.888276241e-01f, -2.954923069e-02f }, + { -1.191101731e-03f, +4.152684085e-02f, +9.889050075e-01f, -2.946608208e-02f }, + { -1.185069698e-03f, +4.135970494e-02f, +9.889821255e-01f, -2.938280043e-02f }, + { -1.179052123e-03f, +4.119272490e-02f, +9.890589780e-01f, -2.929938568e-02f }, + { -1.173048994e-03f, +4.102590072e-02f, +9.891355651e-01f, -2.921583776e-02f }, + { -1.167060297e-03f, +4.085923238e-02f, +9.892118865e-01f, -2.913215661e-02f }, + { -1.161086016e-03f, +4.069271988e-02f, +9.892879424e-01f, -2.904834218e-02f }, + { -1.155126140e-03f, +4.052636319e-02f, +9.893637326e-01f, -2.896439439e-02f }, + { -1.149180652e-03f, +4.036016230e-02f, +9.894392571e-01f, -2.888031318e-02f }, + { -1.143249541e-03f, +4.019411721e-02f, +9.895145159e-01f, -2.879609850e-02f }, + { -1.137332791e-03f, +4.002822789e-02f, +9.895895088e-01f, -2.871175028e-02f }, + { -1.131430388e-03f, +3.986249434e-02f, +9.896642360e-01f, -2.862726845e-02f }, + { -1.125542319e-03f, +3.969691653e-02f, +9.897386972e-01f, -2.854265296e-02f }, + { -1.119668570e-03f, +3.953149447e-02f, +9.898128925e-01f, -2.845790375e-02f }, + { -1.113809126e-03f, +3.936622812e-02f, +9.898868218e-01f, -2.837302075e-02f }, + { -1.107963974e-03f, +3.920111748e-02f, +9.899604850e-01f, -2.828800389e-02f }, + { -1.102133100e-03f, +3.903616254e-02f, +9.900338822e-01f, -2.820285313e-02f }, + { -1.096316489e-03f, +3.887136328e-02f, +9.901070133e-01f, -2.811756839e-02f }, + { -1.090514127e-03f, +3.870671968e-02f, +9.901798782e-01f, -2.803214962e-02f }, + { -1.084726001e-03f, +3.854223173e-02f, +9.902524768e-01f, -2.794659675e-02f }, + { -1.078952096e-03f, +3.837789942e-02f, +9.903248092e-01f, -2.786090972e-02f }, + { -1.073192399e-03f, +3.821372273e-02f, +9.903968753e-01f, -2.777508847e-02f }, + { -1.067446895e-03f, +3.804970164e-02f, +9.904686751e-01f, -2.768913294e-02f }, + { -1.061715570e-03f, +3.788583615e-02f, +9.905402084e-01f, -2.760304307e-02f }, + { -1.055998411e-03f, +3.772212623e-02f, +9.906114753e-01f, -2.751681879e-02f }, + { -1.050295402e-03f, +3.755857188e-02f, +9.906824756e-01f, -2.743046005e-02f }, + { -1.044606531e-03f, +3.739517307e-02f, +9.907532095e-01f, -2.734396678e-02f }, + { -1.038931782e-03f, +3.723192979e-02f, +9.908236768e-01f, -2.725733893e-02f }, + { -1.033271142e-03f, +3.706884203e-02f, +9.908938774e-01f, -2.717057642e-02f }, + { -1.027624597e-03f, +3.690590976e-02f, +9.909638114e-01f, -2.708367921e-02f }, + { -1.021992133e-03f, +3.674313298e-02f, +9.910334786e-01f, -2.699664722e-02f }, + { -1.016373735e-03f, +3.658051167e-02f, +9.911028791e-01f, -2.690948040e-02f }, + { -1.010769389e-03f, +3.641804581e-02f, +9.911720128e-01f, -2.682217869e-02f }, + { -1.005179082e-03f, +3.625573538e-02f, +9.912408796e-01f, -2.673474203e-02f }, + { -9.996027983e-04f, +3.609358038e-02f, +9.913094795e-01f, -2.664717035e-02f }, + { -9.940405249e-04f, +3.593158078e-02f, +9.913778125e-01f, -2.655946360e-02f }, + { -9.884922474e-04f, +3.576973656e-02f, +9.914458785e-01f, -2.647162171e-02f }, + { -9.829579515e-04f, +3.560804772e-02f, +9.915136775e-01f, -2.638364463e-02f }, + { -9.774376231e-04f, +3.544651423e-02f, +9.915812094e-01f, -2.629553230e-02f }, + { -9.719312480e-04f, +3.528513608e-02f, +9.916484743e-01f, -2.620728464e-02f }, + { -9.664388120e-04f, +3.512391325e-02f, +9.917154719e-01f, -2.611890161e-02f }, + { -9.609603011e-04f, +3.496284573e-02f, +9.917822024e-01f, -2.603038315e-02f }, + { -9.554957009e-04f, +3.480193349e-02f, +9.918486656e-01f, -2.594172918e-02f }, + { -9.500449974e-04f, +3.464117652e-02f, +9.919148616e-01f, -2.585293966e-02f }, + { -9.446081762e-04f, +3.448057480e-02f, +9.919807902e-01f, -2.576401452e-02f }, + { -9.391852233e-04f, +3.432012832e-02f, +9.920464515e-01f, -2.567495371e-02f }, + { -9.337761243e-04f, +3.415983706e-02f, +9.921118453e-01f, -2.558575716e-02f }, + { -9.283808652e-04f, +3.399970099e-02f, +9.921769718e-01f, -2.549642481e-02f }, + { -9.229994316e-04f, +3.383972011e-02f, +9.922418307e-01f, -2.540695661e-02f }, + { -9.176318093e-04f, +3.367989440e-02f, +9.923064221e-01f, -2.531735249e-02f }, + { -9.122779842e-04f, +3.352022383e-02f, +9.923707459e-01f, -2.522761239e-02f }, + { -9.069379419e-04f, +3.336070839e-02f, +9.924348021e-01f, -2.513773626e-02f }, + { -9.016116683e-04f, +3.320134807e-02f, +9.924985907e-01f, -2.504772404e-02f }, + { -8.962991490e-04f, +3.304214283e-02f, +9.925621116e-01f, -2.495757566e-02f }, + { -8.910003699e-04f, +3.288309267e-02f, +9.926253647e-01f, -2.486729107e-02f }, + { -8.857153167e-04f, +3.272419757e-02f, +9.926883501e-01f, -2.477687020e-02f }, + { -8.804439750e-04f, +3.256545750e-02f, +9.927510676e-01f, -2.468631300e-02f }, + { -8.751863308e-04f, +3.240687246e-02f, +9.928135173e-01f, -2.459561942e-02f }, + { -8.699423695e-04f, +3.224844242e-02f, +9.928756991e-01f, -2.450478938e-02f }, + { -8.647120771e-04f, +3.209016735e-02f, +9.929376130e-01f, -2.441382283e-02f }, + { -8.594954392e-04f, +3.193204725e-02f, +9.929992589e-01f, -2.432271972e-02f }, + { -8.542924415e-04f, +3.177408210e-02f, +9.930606368e-01f, -2.423147998e-02f }, + { -8.491030697e-04f, +3.161627187e-02f, +9.931217466e-01f, -2.414010355e-02f }, + { -8.439273095e-04f, +3.145861654e-02f, +9.931825884e-01f, -2.404859038e-02f }, + { -8.387651466e-04f, +3.130111610e-02f, +9.932431620e-01f, -2.395694040e-02f }, + { -8.336165668e-04f, +3.114377053e-02f, +9.933034675e-01f, -2.386515357e-02f }, + { -8.284815556e-04f, +3.098657981e-02f, +9.933635047e-01f, -2.377322981e-02f }, + { -8.233600987e-04f, +3.082954391e-02f, +9.934232738e-01f, -2.368116908e-02f }, + { -8.182521819e-04f, +3.067266283e-02f, +9.934827745e-01f, -2.358897131e-02f }, + { -8.131577908e-04f, +3.051593653e-02f, +9.935420069e-01f, -2.349663644e-02f }, + { -8.080769110e-04f, +3.035936500e-02f, +9.936009710e-01f, -2.340416442e-02f }, + { -8.030095283e-04f, +3.020294821e-02f, +9.936596666e-01f, -2.331155519e-02f }, + { -7.979556282e-04f, +3.004668616e-02f, +9.937180939e-01f, -2.321880869e-02f }, + { -7.929151965e-04f, +2.989057881e-02f, +9.937762527e-01f, -2.312592486e-02f }, + { -7.878882187e-04f, +2.973462615e-02f, +9.938341429e-01f, -2.303290364e-02f }, + { -7.828746805e-04f, +2.957882816e-02f, +9.938917647e-01f, -2.293974499e-02f }, + { -7.778745675e-04f, +2.942318481e-02f, +9.939491178e-01f, -2.284644883e-02f }, + { -7.728878654e-04f, +2.926769610e-02f, +9.940062023e-01f, -2.275301511e-02f }, + { -7.679145598e-04f, +2.911236198e-02f, +9.940630182e-01f, -2.265944377e-02f }, + { -7.629546363e-04f, +2.895718245e-02f, +9.941195654e-01f, -2.256573476e-02f }, + { -7.580080805e-04f, +2.880215748e-02f, +9.941758439e-01f, -2.247188802e-02f }, + { -7.530748780e-04f, +2.864728706e-02f, +9.942318536e-01f, -2.237790348e-02f }, + { -7.481550144e-04f, +2.849257115e-02f, +9.942875945e-01f, -2.228378110e-02f }, + { -7.432484754e-04f, +2.833800975e-02f, +9.943430666e-01f, -2.218952082e-02f }, + { -7.383552465e-04f, +2.818360282e-02f, +9.943982699e-01f, -2.209512257e-02f }, + { -7.334753133e-04f, +2.802935035e-02f, +9.944532042e-01f, -2.200058631e-02f }, + { -7.286086614e-04f, +2.787525232e-02f, +9.945078696e-01f, -2.190591197e-02f }, + { -7.237552763e-04f, +2.772130869e-02f, +9.945622660e-01f, -2.181109950e-02f }, + { -7.189151437e-04f, +2.756751946e-02f, +9.946163934e-01f, -2.171614883e-02f }, + { -7.140882492e-04f, +2.741388460e-02f, +9.946702518e-01f, -2.162105992e-02f }, + { -7.092745782e-04f, +2.726040409e-02f, +9.947238411e-01f, -2.152583271e-02f }, + { -7.044741164e-04f, +2.710707790e-02f, +9.947771613e-01f, -2.143046713e-02f }, + { -6.996868493e-04f, +2.695390602e-02f, +9.948302124e-01f, -2.133496314e-02f }, + { -6.949127625e-04f, +2.680088842e-02f, +9.948829943e-01f, -2.123932068e-02f }, + { -6.901518415e-04f, +2.664802507e-02f, +9.949355069e-01f, -2.114353968e-02f }, + { -6.854040719e-04f, +2.649531596e-02f, +9.949877504e-01f, -2.104762010e-02f }, + { -6.806694392e-04f, +2.634276107e-02f, +9.950397245e-01f, -2.095156187e-02f }, + { -6.759479289e-04f, +2.619036036e-02f, +9.950914294e-01f, -2.085536494e-02f }, + { -6.712395266e-04f, +2.603811383e-02f, +9.951428649e-01f, -2.075902926e-02f }, + { -6.665442179e-04f, +2.588602143e-02f, +9.951940310e-01f, -2.066255477e-02f }, + { -6.618619881e-04f, +2.573408316e-02f, +9.952449278e-01f, -2.056594140e-02f }, + { -6.571928229e-04f, +2.558229899e-02f, +9.952955551e-01f, -2.046918912e-02f }, + { -6.525367078e-04f, +2.543066889e-02f, +9.953459129e-01f, -2.037229785e-02f }, + { -6.478936283e-04f, +2.527919285e-02f, +9.953960012e-01f, -2.027526754e-02f }, + { -6.432635698e-04f, +2.512787083e-02f, +9.954458200e-01f, -2.017809815e-02f }, + { -6.386465180e-04f, +2.497670282e-02f, +9.954953692e-01f, -2.008078960e-02f }, + { -6.340424582e-04f, +2.482568879e-02f, +9.955446489e-01f, -1.998334185e-02f }, + { -6.294513760e-04f, +2.467482872e-02f, +9.955936589e-01f, -1.988575485e-02f }, + { -6.248732570e-04f, +2.452412258e-02f, +9.956423992e-01f, -1.978802853e-02f }, + { -6.203080865e-04f, +2.437357035e-02f, +9.956908698e-01f, -1.969016283e-02f }, + { -6.157558500e-04f, +2.422317200e-02f, +9.957390708e-01f, -1.959215772e-02f }, + { -6.112165332e-04f, +2.407292752e-02f, +9.957870019e-01f, -1.949401312e-02f }, + { -6.066901213e-04f, +2.392283688e-02f, +9.958346633e-01f, -1.939572898e-02f }, + { -6.021765999e-04f, +2.377290004e-02f, +9.958820549e-01f, -1.929730525e-02f }, + { -5.976759545e-04f, +2.362311700e-02f, +9.959291766e-01f, -1.919874188e-02f }, + { -5.931881705e-04f, +2.347348772e-02f, +9.959760285e-01f, -1.910003880e-02f }, + { -5.887132334e-04f, +2.332401218e-02f, +9.960226105e-01f, -1.900119597e-02f }, + { -5.842511287e-04f, +2.317469035e-02f, +9.960689225e-01f, -1.890221333e-02f }, + { -5.798018418e-04f, +2.302552221e-02f, +9.961149645e-01f, -1.880309082e-02f }, + { -5.753653581e-04f, +2.287650774e-02f, +9.961607366e-01f, -1.870382839e-02f }, + { -5.709416631e-04f, +2.272764690e-02f, +9.962062387e-01f, -1.860442598e-02f }, + { -5.665307423e-04f, +2.257893968e-02f, +9.962514707e-01f, -1.850488354e-02f }, + { -5.621325811e-04f, +2.243038605e-02f, +9.962964326e-01f, -1.840520102e-02f }, + { -5.577471650e-04f, +2.228198598e-02f, +9.963411244e-01f, -1.830537836e-02f }, + { -5.533744792e-04f, +2.213373945e-02f, +9.963855461e-01f, -1.820541550e-02f }, + { -5.490145094e-04f, +2.198564643e-02f, +9.964296976e-01f, -1.810531240e-02f }, + { -5.446672410e-04f, +2.183770690e-02f, +9.964735789e-01f, -1.800506899e-02f }, + { -5.403326592e-04f, +2.168992083e-02f, +9.965171900e-01f, -1.790468523e-02f }, + { -5.360107497e-04f, +2.154228819e-02f, +9.965605309e-01f, -1.780416106e-02f }, + { -5.317014977e-04f, +2.139480896e-02f, +9.966036014e-01f, -1.770349642e-02f }, + { -5.274048887e-04f, +2.124748312e-02f, +9.966464017e-01f, -1.760269126e-02f }, + { -5.231209082e-04f, +2.110031063e-02f, +9.966889317e-01f, -1.750174553e-02f }, + { -5.188495414e-04f, +2.095329147e-02f, +9.967311913e-01f, -1.740065918e-02f }, + { -5.145907739e-04f, +2.080642562e-02f, +9.967731805e-01f, -1.729943214e-02f }, + { -5.103445911e-04f, +2.065971304e-02f, +9.968148993e-01f, -1.719806437e-02f }, + { -5.061109782e-04f, +2.051315372e-02f, +9.968563476e-01f, -1.709655581e-02f }, + { -5.018899208e-04f, +2.036674762e-02f, +9.968975255e-01f, -1.699490642e-02f }, + { -4.976814042e-04f, +2.022049471e-02f, +9.969384329e-01f, -1.689311613e-02f }, + { -4.934854139e-04f, +2.007439498e-02f, +9.969790698e-01f, -1.679118489e-02f }, + { -4.893019351e-04f, +1.992844839e-02f, +9.970194362e-01f, -1.668911265e-02f }, + { -4.851309533e-04f, +1.978265492e-02f, +9.970595319e-01f, -1.658689935e-02f }, + { -4.809724538e-04f, +1.963701453e-02f, +9.970993571e-01f, -1.648454495e-02f }, + { -4.768264221e-04f, +1.949152721e-02f, +9.971389117e-01f, -1.638204939e-02f }, + { -4.726928435e-04f, +1.934619292e-02f, +9.971781956e-01f, -1.627941262e-02f }, + { -4.685717034e-04f, +1.920101164e-02f, +9.972172089e-01f, -1.617663458e-02f }, + { -4.644629872e-04f, +1.905598334e-02f, +9.972559515e-01f, -1.607371522e-02f }, + { -4.603666801e-04f, +1.891110799e-02f, +9.972944233e-01f, -1.597065449e-02f }, + { -4.562827677e-04f, +1.876638556e-02f, +9.973326244e-01f, -1.586745234e-02f }, + { -4.522112352e-04f, +1.862181603e-02f, +9.973705548e-01f, -1.576410871e-02f }, + { -4.481520680e-04f, +1.847739937e-02f, +9.974082143e-01f, -1.566062355e-02f }, + { -4.441052515e-04f, +1.833313554e-02f, +9.974456031e-01f, -1.555699680e-02f }, + { -4.400707710e-04f, +1.818902453e-02f, +9.974827210e-01f, -1.545322843e-02f }, + { -4.360486119e-04f, +1.804506631e-02f, +9.975195680e-01f, -1.534931837e-02f }, + { -4.320387594e-04f, +1.790126083e-02f, +9.975561442e-01f, -1.524526657e-02f }, + { -4.280411991e-04f, +1.775760809e-02f, +9.975924494e-01f, -1.514107297e-02f }, + { -4.240559161e-04f, +1.761410804e-02f, +9.976284837e-01f, -1.503673754e-02f }, + { -4.200828959e-04f, +1.747076066e-02f, +9.976642471e-01f, -1.493226021e-02f }, + { -4.161221238e-04f, +1.732756592e-02f, +9.976997395e-01f, -1.482764093e-02f }, + { -4.121735850e-04f, +1.718452380e-02f, +9.977349608e-01f, -1.472287966e-02f }, + { -4.082372651e-04f, +1.704163425e-02f, +9.977699112e-01f, -1.461797634e-02f }, + { -4.043131492e-04f, +1.689889726e-02f, +9.978045905e-01f, -1.451293091e-02f }, + { -4.004012227e-04f, +1.675631280e-02f, +9.978389988e-01f, -1.440774333e-02f }, + { -3.965014710e-04f, +1.661388082e-02f, +9.978731359e-01f, -1.430241355e-02f }, + { -3.926138794e-04f, +1.647160132e-02f, +9.979070020e-01f, -1.419694151e-02f }, + { -3.887384331e-04f, +1.632947425e-02f, +9.979405969e-01f, -1.409132716e-02f }, + { -3.848751175e-04f, +1.618749959e-02f, +9.979739207e-01f, -1.398557045e-02f }, + { -3.810239179e-04f, +1.604567730e-02f, +9.980069732e-01f, -1.387967133e-02f }, + { -3.771848197e-04f, +1.590400737e-02f, +9.980397546e-01f, -1.377362975e-02f }, + { -3.733578082e-04f, +1.576248975e-02f, +9.980722648e-01f, -1.366744566e-02f }, + { -3.695428686e-04f, +1.562112441e-02f, +9.981045038e-01f, -1.356111900e-02f }, + { -3.657399862e-04f, +1.547991134e-02f, +9.981364714e-01f, -1.345464973e-02f }, + { -3.619491465e-04f, +1.533885049e-02f, +9.981681678e-01f, -1.334803780e-02f }, + { -3.581703346e-04f, +1.519794184e-02f, +9.981995929e-01f, -1.324128315e-02f }, + { -3.544035359e-04f, +1.505718535e-02f, +9.982307467e-01f, -1.313438573e-02f }, + { -3.506487357e-04f, +1.491658101e-02f, +9.982616292e-01f, -1.302734550e-02f }, + { -3.469059193e-04f, +1.477612876e-02f, +9.982922403e-01f, -1.292016240e-02f }, + { -3.431750720e-04f, +1.463582860e-02f, +9.983225800e-01f, -1.281283638e-02f }, + { -3.394561791e-04f, +1.449568047e-02f, +9.983526483e-01f, -1.270536739e-02f }, + { -3.357492258e-04f, +1.435568436e-02f, +9.983824452e-01f, -1.259775538e-02f }, + { -3.320541975e-04f, +1.421584024e-02f, +9.984119707e-01f, -1.249000030e-02f }, + { -3.283710794e-04f, +1.407614806e-02f, +9.984412247e-01f, -1.238210211e-02f }, + { -3.246998569e-04f, +1.393660781e-02f, +9.984702072e-01f, -1.227406075e-02f }, + { -3.210405152e-04f, +1.379721944e-02f, +9.984989183e-01f, -1.216587616e-02f }, + { -3.173930397e-04f, +1.365798293e-02f, +9.985273578e-01f, -1.205754831e-02f }, + { -3.137574155e-04f, +1.351889825e-02f, +9.985555259e-01f, -1.194907714e-02f }, + { -3.101336280e-04f, +1.337996537e-02f, +9.985834223e-01f, -1.184046260e-02f }, + { -3.065216624e-04f, +1.324118424e-02f, +9.986110473e-01f, -1.173170465e-02f }, + { -3.029215041e-04f, +1.310255485e-02f, +9.986384006e-01f, -1.162280322e-02f }, + { -2.993331383e-04f, +1.296407716e-02f, +9.986654823e-01f, -1.151375828e-02f }, + { -2.957565503e-04f, +1.282575114e-02f, +9.986922924e-01f, -1.140456977e-02f }, + { -2.921917254e-04f, +1.268757675e-02f, +9.987188309e-01f, -1.129523765e-02f }, + { -2.886386488e-04f, +1.254955397e-02f, +9.987450978e-01f, -1.118576186e-02f }, + { -2.850973058e-04f, +1.241168275e-02f, +9.987710929e-01f, -1.107614236e-02f }, + { -2.815676816e-04f, +1.227396308e-02f, +9.987968164e-01f, -1.096637910e-02f }, + { -2.780497616e-04f, +1.213639492e-02f, +9.988222682e-01f, -1.085647202e-02f }, + { -2.745435310e-04f, +1.199897823e-02f, +9.988474483e-01f, -1.074642108e-02f }, + { -2.710489751e-04f, +1.186171298e-02f, +9.988723567e-01f, -1.063622623e-02f }, + { -2.675660791e-04f, +1.172459914e-02f, +9.988969933e-01f, -1.052588743e-02f }, + { -2.640948284e-04f, +1.158763668e-02f, +9.989213581e-01f, -1.041540461e-02f }, + { -2.606352080e-04f, +1.145082556e-02f, +9.989454512e-01f, -1.030477774e-02f }, + { -2.571872034e-04f, +1.131416575e-02f, +9.989692724e-01f, -1.019400677e-02f }, + { -2.537507998e-04f, +1.117765722e-02f, +9.989928219e-01f, -1.008309164e-02f }, + { -2.503259824e-04f, +1.104129994e-02f, +9.990160995e-01f, -9.972032308e-03f }, + { -2.469127365e-04f, +1.090509386e-02f, +9.990391053e-01f, -9.860828729e-03f }, + { -2.435110474e-04f, +1.076903897e-02f, +9.990618393e-01f, -9.749480853e-03f }, + { -2.401209002e-04f, +1.063313522e-02f, +9.990843014e-01f, -9.637988631e-03f }, + { -2.367422804e-04f, +1.049738258e-02f, +9.991064916e-01f, -9.526352014e-03f }, + { -2.333751730e-04f, +1.036178102e-02f, +9.991284099e-01f, -9.414570956e-03f }, + { -2.300195634e-04f, +1.022633050e-02f, +9.991500563e-01f, -9.302645409e-03f }, + { -2.266754367e-04f, +1.009103099e-02f, +9.991714307e-01f, -9.190575323e-03f }, + { -2.233427784e-04f, +9.955882461e-03f, +9.991925333e-01f, -9.078360653e-03f }, + { -2.200215735e-04f, +9.820884872e-03f, +9.992133639e-01f, -8.966001349e-03f }, + { -2.167118075e-04f, +9.686038192e-03f, +9.992339225e-01f, -8.853497366e-03f }, + { -2.134134654e-04f, +9.551342385e-03f, +9.992542091e-01f, -8.740848654e-03f }, + { -2.101265325e-04f, +9.416797418e-03f, +9.992742238e-01f, -8.628055168e-03f }, + { -2.068509942e-04f, +9.282403256e-03f, +9.992939664e-01f, -8.515116858e-03f }, + { -2.035868356e-04f, +9.148159865e-03f, +9.993134371e-01f, -8.402033679e-03f }, + { -2.003340419e-04f, +9.014067211e-03f, +9.993326357e-01f, -8.288805583e-03f }, + { -1.970925985e-04f, +8.880125258e-03f, +9.993515622e-01f, -8.175432524e-03f }, + { -1.938624906e-04f, +8.746333973e-03f, +9.993702168e-01f, -8.061914453e-03f }, + { -1.906437034e-04f, +8.612693320e-03f, +9.993885992e-01f, -7.948251325e-03f }, + { -1.874362221e-04f, +8.479203263e-03f, +9.994067096e-01f, -7.834443092e-03f }, + { -1.842400320e-04f, +8.345863769e-03f, +9.994245479e-01f, -7.720489707e-03f }, + { -1.810551183e-04f, +8.212674803e-03f, +9.994421141e-01f, -7.606391125e-03f }, + { -1.778814663e-04f, +8.079636328e-03f, +9.994594082e-01f, -7.492147298e-03f }, + { -1.747190612e-04f, +7.946748310e-03f, +9.994764302e-01f, -7.377758181e-03f }, + { -1.715678882e-04f, +7.814010713e-03f, +9.994931800e-01f, -7.263223725e-03f }, + { -1.684279326e-04f, +7.681423501e-03f, +9.995096577e-01f, -7.148543887e-03f }, + { -1.652991797e-04f, +7.548986640e-03f, +9.995258632e-01f, -7.033718618e-03f }, + { -1.621816145e-04f, +7.416700094e-03f, +9.995417966e-01f, -6.918747873e-03f }, + { -1.590752225e-04f, +7.284563826e-03f, +9.995574578e-01f, -6.803631606e-03f }, + { -1.559799888e-04f, +7.152577801e-03f, +9.995728468e-01f, -6.688369772e-03f }, + { -1.528958987e-04f, +7.020741983e-03f, +9.995879637e-01f, -6.572962323e-03f }, + { -1.498229373e-04f, +6.889056337e-03f, +9.996028083e-01f, -6.457409214e-03f }, + { -1.467610900e-04f, +6.757520824e-03f, +9.996173807e-01f, -6.341710400e-03f }, + { -1.437103420e-04f, +6.626135411e-03f, +9.996316809e-01f, -6.225865834e-03f }, + { -1.406706784e-04f, +6.494900059e-03f, +9.996457089e-01f, -6.109875472e-03f }, + { -1.376420846e-04f, +6.363814734e-03f, +9.996594646e-01f, -5.993739268e-03f }, + { -1.346245458e-04f, +6.232879398e-03f, +9.996729481e-01f, -5.877457176e-03f }, + { -1.316180471e-04f, +6.102094014e-03f, +9.996861593e-01f, -5.761029151e-03f }, + { -1.286225739e-04f, +5.971458547e-03f, +9.996990983e-01f, -5.644455148e-03f }, + { -1.256381113e-04f, +5.840972959e-03f, +9.997117650e-01f, -5.527735122e-03f }, + { -1.226646447e-04f, +5.710637213e-03f, +9.997241594e-01f, -5.410869028e-03f }, + { -1.197021592e-04f, +5.580451273e-03f, +9.997362815e-01f, -5.293856820e-03f }, + { -1.167506400e-04f, +5.450415101e-03f, +9.997481313e-01f, -5.176698454e-03f }, + { -1.138100725e-04f, +5.320528660e-03f, +9.997597089e-01f, -5.059393885e-03f }, + { -1.108804418e-04f, +5.190791913e-03f, +9.997710141e-01f, -4.941943068e-03f }, + { -1.079617332e-04f, +5.061204824e-03f, +9.997820469e-01f, -4.824345959e-03f }, + { -1.050539318e-04f, +4.931767353e-03f, +9.997928075e-01f, -4.706602513e-03f }, + { -1.021570230e-04f, +4.802479464e-03f, +9.998032957e-01f, -4.588712686e-03f }, + { -9.927099199e-05f, +4.673341119e-03f, +9.998135116e-01f, -4.470676433e-03f }, + { -9.639582397e-05f, +4.544352281e-03f, +9.998234552e-01f, -4.352493710e-03f }, + { -9.353150418e-05f, +4.415512912e-03f, +9.998331264e-01f, -4.234164473e-03f }, + { -9.067801786e-05f, +4.286822973e-03f, +9.998425252e-01f, -4.115688677e-03f }, + { -8.783535025e-05f, +4.158282427e-03f, +9.998516517e-01f, -3.997066279e-03f }, + { -8.500348659e-05f, +4.029891236e-03f, +9.998605058e-01f, -3.878297235e-03f }, + { -8.218241209e-05f, +3.901649361e-03f, +9.998690875e-01f, -3.759381500e-03f }, + { -7.937211202e-05f, +3.773556765e-03f, +9.998773969e-01f, -3.640319031e-03f }, + { -7.657257158e-05f, +3.645613409e-03f, +9.998854338e-01f, -3.521109785e-03f }, + { -7.378377604e-05f, +3.517819255e-03f, +9.998931984e-01f, -3.401753717e-03f }, + { -7.100571061e-05f, +3.390174264e-03f, +9.999006906e-01f, -3.282250785e-03f }, + { -6.823836055e-05f, +3.262678397e-03f, +9.999079103e-01f, -3.162600944e-03f }, + { -6.548171107e-05f, +3.135331617e-03f, +9.999148577e-01f, -3.042804151e-03f }, + { -6.273574744e-05f, +3.008133883e-03f, +9.999215326e-01f, -2.922860364e-03f }, + { -6.000045487e-05f, +2.881085158e-03f, +9.999279352e-01f, -2.802769538e-03f }, + { -5.727581862e-05f, +2.754185402e-03f, +9.999340653e-01f, -2.682531632e-03f }, + { -5.456182391e-05f, +2.627434576e-03f, +9.999399230e-01f, -2.562146601e-03f }, + { -5.185845600e-05f, +2.500832641e-03f, +9.999455083e-01f, -2.441614402e-03f }, + { -4.916570012e-05f, +2.374379558e-03f, +9.999508211e-01f, -2.320934994e-03f }, + { -4.648354151e-05f, +2.248075287e-03f, +9.999558615e-01f, -2.200108333e-03f }, + { -4.381196542e-05f, +2.121919790e-03f, +9.999606295e-01f, -2.079134377e-03f }, + { -4.115095708e-05f, +1.995913026e-03f, +9.999651250e-01f, -1.958013083e-03f }, + { -3.850050175e-05f, +1.870054956e-03f, +9.999693481e-01f, -1.836744408e-03f }, + { -3.586058466e-05f, +1.744345541e-03f, +9.999732988e-01f, -1.715328311e-03f }, + { -3.323119106e-05f, +1.618784740e-03f, +9.999769770e-01f, -1.593764748e-03f }, + { -3.061230620e-05f, +1.493372514e-03f, +9.999803827e-01f, -1.472053677e-03f }, + { -2.800391533e-05f, +1.368108822e-03f, +9.999835160e-01f, -1.350195058e-03f }, + { -2.540600368e-05f, +1.242993626e-03f, +9.999863768e-01f, -1.228188846e-03f }, + { -2.281855651e-05f, +1.118026884e-03f, +9.999889652e-01f, -1.106035001e-03f }, + { -2.024155907e-05f, +9.932085563e-04f, +9.999912812e-01f, -9.837334803e-04f }, + { -1.767499661e-05f, +8.685386028e-04f, +9.999933246e-01f, -8.612842424e-04f }, + { -1.511885438e-05f, +7.440169831e-04f, +9.999950956e-01f, -7.386872455e-04f }, + { -1.257311763e-05f, +6.196436566e-04f, +9.999965942e-01f, -6.159424479e-04f }, + { -1.003777162e-05f, +4.954185828e-04f, +9.999978203e-01f, -4.930498082e-04f }, + { -7.512801591e-06f, +3.713417210e-04f, +9.999987739e-01f, -3.700092848e-04f }, + { -4.998192808e-06f, +2.474130305e-04f, +9.999994551e-01f, -2.468208365e-04f }, + { -2.493930525e-06f, +1.236324705e-04f, +9.999998638e-01f, -1.234844220e-04f }, +}; diff --git a/libs/openal-soft/Alc/compat.h b/libs/openal-soft/Alc/compat.h index f54ef9ce..247ed05b 100644 --- a/libs/openal-soft/Alc/compat.h +++ b/libs/openal-soft/Alc/compat.h @@ -1,6 +1,8 @@ #ifndef AL_COMPAT_H #define AL_COMPAT_H +#include "alstring.h" + #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN @@ -23,10 +25,33 @@ FILE *al_fopen(const char *fname, const char *mode); #endif +struct FileMapping { +#ifdef _WIN32 + HANDLE file; + HANDLE fmap; +#else + int fd; +#endif + void *ptr; + size_t len; +}; +struct FileMapping MapFileToMem(const char *fname); +void UnmapFileMem(const struct FileMapping *mapping); + +al_string GetProcPath(void); + #ifdef HAVE_DYNLOAD void *LoadLib(const char *name); 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 + #endif /* AL_COMPAT_H */ diff --git a/libs/openal-soft/Alc/converter.c b/libs/openal-soft/Alc/converter.c new file mode 100644 index 00000000..5cfe7031 --- /dev/null +++ b/libs/openal-soft/Alc/converter.c @@ -0,0 +1,466 @@ + +#include "config.h" + +#include "converter.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 = fastf2i(minf((ALdouble)srcRate / dstRate, MAX_PITCH)*FRACTIONONE + 0.5f); + converter->mIncrement = maxi(step, 1); + if(converter->mIncrement == FRACTIONONE) + converter->mResample = Resample_copy32_C; + else + { + /* TODO: Allow other resamplers. */ + BsincPrepare(converter->mIncrement, &converter->mState.bsinc); + converter->mResample = SelectResampler(BSincResampler); + } + 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_POST_SAMPLES+MAX_PRE_SAMPLES && + MAX_POST_SAMPLES+MAX_PRE_SAMPLES-prepcount >= srcframes) + { + /* Not enough input samples to generate an output sample. */ + return 0; + } + + DataSize64 = prepcount; + DataSize64 += srcframes; + DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES; + 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_POST_SAMPLES+MAX_PRE_SAMPLES)); + + if(prepcount < MAX_POST_SAMPLES+MAX_PRE_SAMPLES && + MAX_POST_SAMPLES+MAX_PRE_SAMPLES-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_POST_SAMPLES+MAX_PRE_SAMPLES; + 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_PRE_SAMPLES+MAX_POST_SAMPLES, 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_PRE_SAMPLES, 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(MAX_PRE_SAMPLES+MAX_POST_SAMPLES, + prepcount+toread-(DataPosFrac>>FRACTIONBITS)); + 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/libs/openal-soft/Alc/converter.h b/libs/openal-soft/Alc/converter.h new file mode 100644 index 00000000..9934fa4b --- /dev/null +++ b/libs/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_PRE_SAMPLES+MAX_POST_SAMPLES]; + } 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/libs/openal-soft/Alc/effects/autowah.c b/libs/openal-soft/Alc/effects/autowah.c deleted file mode 100644 index e97083e0..00000000 --- a/libs/openal-soft/Alc/effects/autowah.c +++ /dev/null @@ -1,270 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 2013 by Anis A. Hireche, Nasca Octavian Paul - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * Or go to http://www.gnu.org/copyleft/lgpl.html - */ - -#include - -#include "config.h" -#include "alu.h" -#include "alFilter.h" -#include "alError.h" -#include "alMain.h" -#include "alAuxEffectSlot.h" - - -/* Auto-wah is simply a low-pass filter with a cutoff frequency that shifts up - * or down depending on the input signal, and a resonant peak at the cutoff. - * - * Currently, we assume a cutoff frequency range of 20hz (no amplitude) to - * 20khz (peak gain). Peak gain is assumed to be in normalized scale. - */ - -typedef struct ALautowahState { - DERIVE_FROM_TYPE(ALeffectState); - - /* Effect gains for each channel */ - ALfloat Gain[MAX_OUTPUT_CHANNELS]; - - /* Effect parameters */ - ALfloat AttackRate; - ALfloat ReleaseRate; - ALfloat Resonance; - ALfloat PeakGain; - ALfloat GainCtrl; - ALfloat Frequency; - - /* Samples processing */ - ALfilterState LowPass; -} ALautowahState; - -static ALvoid ALautowahState_Destruct(ALautowahState *UNUSED(state)) -{ -} - -static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device) -{ - state->Frequency = (ALfloat)device->Frequency; - return AL_TRUE; -} - -static ALvoid ALautowahState_update(ALautowahState *state, ALCdevice *device, const ALeffectslot *slot) -{ - ALfloat attackTime, releaseTime; - - attackTime = slot->EffectProps.Autowah.AttackTime * state->Frequency; - releaseTime = slot->EffectProps.Autowah.ReleaseTime * state->Frequency; - - state->AttackRate = powf(1.0f/GAIN_SILENCE_THRESHOLD, 1.0f/attackTime); - state->ReleaseRate = powf(GAIN_SILENCE_THRESHOLD/1.0f, 1.0f/releaseTime); - state->PeakGain = slot->EffectProps.Autowah.PeakGain; - state->Resonance = slot->EffectProps.Autowah.Resonance; - - ComputeAmbientGains(device, slot->Gain, state->Gain); -} - -static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE], ALuint NumChannels) -{ - ALuint it, kt; - ALuint base; - - for(base = 0;base < SamplesToDo;) - { - ALfloat temps[256]; - ALuint td = minu(256, SamplesToDo-base); - ALfloat gain = state->GainCtrl; - - for(it = 0;it < td;it++) - { - ALfloat smp = SamplesIn[it+base]; - ALfloat a[3], b[3]; - ALfloat alpha, w0; - ALfloat amplitude; - ALfloat cutoff; - - /* Similar to compressor, we get the current amplitude of the - * incoming signal, and attack or release to reach it. */ - amplitude = fabsf(smp); - if(amplitude > gain) - gain = minf(gain*state->AttackRate, amplitude); - else if(amplitude < gain) - gain = maxf(gain*state->ReleaseRate, amplitude); - gain = maxf(gain, GAIN_SILENCE_THRESHOLD); - - /* FIXME: What range does the filter cover? */ - cutoff = lerp(20.0f, 20000.0f, minf(gain/state->PeakGain, 1.0f)); - - /* The code below is like calling ALfilterState_setParams with - * ALfilterType_LowPass. However, instead of passing a bandwidth, - * we use the resonance property for Q. This also inlines the call. - */ - w0 = F_TAU * cutoff / state->Frequency; - - /* FIXME: Resonance controls the resonant peak, or Q. How? Not sure - * that Q = resonance*0.1. */ - alpha = sinf(w0) / (2.0f * state->Resonance*0.1f); - b[0] = (1.0f - cosf(w0)) / 2.0f; - b[1] = 1.0f - cosf(w0); - b[2] = (1.0f - cosf(w0)) / 2.0f; - a[0] = 1.0f + alpha; - a[1] = -2.0f * cosf(w0); - a[2] = 1.0f - alpha; - - state->LowPass.a1 = a[1] / a[0]; - state->LowPass.a2 = a[2] / a[0]; - state->LowPass.b1 = b[1] / a[0]; - state->LowPass.b2 = b[2] / a[0]; - state->LowPass.input_gain = b[0] / a[0]; - - temps[it] = ALfilterState_processSingle(&state->LowPass, smp); - } - state->GainCtrl = gain; - - for(kt = 0;kt < NumChannels;kt++) - { - ALfloat gain = state->Gain[kt]; - if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) - continue; - - for(it = 0;it < td;it++) - SamplesOut[kt][base+it] += gain * temps[it]; - } - - base += td; - } -} - -DECLARE_DEFAULT_ALLOCATORS(ALautowahState) - -DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState); - - -typedef struct ALautowahStateFactory { - DERIVE_FROM_TYPE(ALeffectStateFactory); -} ALautowahStateFactory; - -static ALeffectState *ALautowahStateFactory_create(ALautowahStateFactory *UNUSED(factory)) -{ - ALautowahState *state; - - state = ALautowahState_New(sizeof(*state)); - if(!state) return NULL; - SET_VTABLE2(ALautowahState, ALeffectState, state); - - state->AttackRate = 1.0f; - state->ReleaseRate = 1.0f; - state->Resonance = 2.0f; - state->PeakGain = 1.0f; - state->GainCtrl = 1.0f; - - ALfilterState_clear(&state->LowPass); - - return STATIC_CAST(ALeffectState, state); -} - -DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALautowahStateFactory); - -ALeffectStateFactory *ALautowahStateFactory_getFactory(void) -{ - static ALautowahStateFactory AutowahFactory = { { GET_VTABLE2(ALautowahStateFactory, ALeffectStateFactory) } }; - - return STATIC_CAST(ALeffectStateFactory, &AutowahFactory); -} - - -void ALautowah_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -void ALautowah_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals) -{ - ALautowah_setParami(effect, context, param, vals[0]); -} -void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val) -{ - ALeffectProps *props = &effect->Props; - switch(param) - { - case AL_AUTOWAH_ATTACK_TIME: - if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); - props->Autowah.AttackTime = val; - break; - - case AL_AUTOWAH_RELEASE_TIME: - if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); - props->Autowah.ReleaseTime = val; - break; - - case AL_AUTOWAH_RESONANCE: - if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); - props->Autowah.Resonance = val; - break; - - case AL_AUTOWAH_PEAK_GAIN: - if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN)) - SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); - props->Autowah.PeakGain = val; - break; - - default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); - } -} -void ALautowah_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals) -{ - ALautowah_setParamf(effect, context, param, vals[0]); -} - -void ALautowah_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val)) -{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); } -void ALautowah_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals) -{ - ALautowah_getParami(effect, context, param, vals); -} -void ALautowah_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val) -{ - const ALeffectProps *props = &effect->Props; - switch(param) - { - case AL_AUTOWAH_ATTACK_TIME: - *val = props->Autowah.AttackTime; - break; - - case AL_AUTOWAH_RELEASE_TIME: - *val = props->Autowah.ReleaseTime; - break; - - case AL_AUTOWAH_RESONANCE: - *val = props->Autowah.Resonance; - break; - - case AL_AUTOWAH_PEAK_GAIN: - *val = props->Autowah.PeakGain; - break; - - default: - SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); - } -} -void ALautowah_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals) -{ - ALautowah_getParamf(effect, context, param, vals); -} - -DEFINE_ALEFFECT_VTABLE(ALautowah); diff --git a/libs/openal-soft/Alc/effects/chorus.c b/libs/openal-soft/Alc/effects/chorus.c index 7aa5898b..f4383aa5 100644 --- a/libs/openal-soft/Alc/effects/chorus.c +++ b/libs/openal-soft/Alc/effects/chorus.c @@ -39,9 +39,9 @@ typedef struct ALchorusState { DERIVE_FROM_TYPE(ALeffectState); ALfloat *SampleBuffer[2]; - ALuint BufferLength; - ALuint offset; - ALuint lfo_range; + ALsizei BufferLength; + ALsizei offset; + ALsizei lfo_range; ALfloat lfo_scale; ALint lfo_disp; @@ -55,27 +55,51 @@ typedef struct ALchorusState { ALfloat feedback; } ALchorusState; -static ALvoid ALchorusState_Destruct(ALchorusState *state) +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, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels); +DECLARE_DEFAULT_ALLOCATORS(ALchorusState) + +DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState); + + +static void ALchorusState_Construct(ALchorusState *state) { - free(state->SampleBuffer[0]); + ALeffectState_Construct(STATIC_CAST(ALeffectState, state)); + SET_VTABLE2(ALchorusState, ALeffectState, state); + + state->BufferLength = 0; state->SampleBuffer[0] = NULL; state->SampleBuffer[1] = NULL; + state->offset = 0; + state->lfo_range = 1; + state->waveform = CWF_Triangle; +} + +static ALvoid ALchorusState_Destruct(ALchorusState *state) +{ + al_free(state->SampleBuffer[0]); + state->SampleBuffer[0] = NULL; + state->SampleBuffer[1] = NULL; + + ALeffectState_Destruct(STATIC_CAST(ALeffectState,state)); } static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device) { - ALuint maxlen; - ALuint it; + ALsizei maxlen; + ALsizei it; - maxlen = fastf2u(AL_CHORUS_MAX_DELAY * 3.0f * Device->Frequency) + 1; + maxlen = fastf2i(AL_CHORUS_MAX_DELAY * 2.0f * Device->Frequency) + 1; maxlen = NextPowerOf2(maxlen); if(maxlen != state->BufferLength) { - void *temp; - - temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2); + 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; @@ -91,15 +115,14 @@ static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Dev return AL_TRUE; } -static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, const ALeffectslot *Slot) +static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props) { - static const ALfloat left_dir[3] = { -1.0f, 0.0f, 0.0f }; - static const ALfloat right_dir[3] = { 1.0f, 0.0f, 0.0f }; ALfloat frequency = (ALfloat)Device->Frequency; + ALfloat coeffs[MAX_AMBI_COEFFS]; ALfloat rate; ALint phase; - switch(Slot->EffectProps.Chorus.Waveform) + switch(props->Chorus.Waveform) { case AL_CHORUS_WAVEFORM_TRIANGLE: state->waveform = CWF_Triangle; @@ -108,16 +131,19 @@ static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, cons state->waveform = CWF_Sinusoid; break; } - state->depth = Slot->EffectProps.Chorus.Depth; - state->feedback = Slot->EffectProps.Chorus.Feedback; - state->delay = fastf2i(Slot->EffectProps.Chorus.Delay * frequency); + state->feedback = props->Chorus.Feedback; + state->delay = fastf2i(props->Chorus.Delay * frequency); + /* The LFO depth is scaled to be relative to the sample delay. */ + state->depth = props->Chorus.Depth * state->delay; /* Gains for left and right sides */ - ComputeDirectionalGains(Device, left_dir, Slot->Gain, state->Gain[0]); - ComputeDirectionalGains(Device, right_dir, Slot->Gain, state->Gain[1]); + CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs); + ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[0]); + CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs); + ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[1]); - phase = Slot->EffectProps.Chorus.Phase; - rate = Slot->EffectProps.Chorus.Rate; + phase = props->Chorus.Phase; + rate = props->Chorus.Rate; if(!(rate > 0.0f)) { state->lfo_scale = 0.0f; @@ -127,7 +153,7 @@ static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, cons else { /* Calculate LFO coefficient */ - state->lfo_range = fastf2u(frequency/rate + 0.5f); + state->lfo_range = fastf2i(frequency/rate + 0.5f); switch(state->waveform) { case CWF_Triangle: @@ -139,115 +165,108 @@ static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, cons } /* Calculate lfo phase displacement */ - state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f)); + if(phase >= 0) + state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f)); + else + state->lfo_disp = fastf2i(state->lfo_range * ((360+phase)/360.0f)); } } -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, 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; + ALfloat *restrict leftbuf = state->SampleBuffer[0]; + ALfloat *restrict rightbuf = state->SampleBuffer[1]; + const ALsizei bufmask = state->BufferLength-1; + const ALfloat feedback = state->feedback; + ALsizei offset = state->offset; + ALsizei i, c; + ALsizei base; for(base = 0;base < SamplesToDo;) { + const ALsizei todo = mini(128, SamplesToDo-base); ALfloat temps[128][2]; - ALuint td = minu(128, SamplesToDo-base); + ALint moddelays[2][128]; switch(state->waveform) { case CWF_Triangle: - ProcessTriangle(state, td, SamplesIn+base, temps); + GetTriangleDelays(moddelays[0], offset%state->lfo_range, state->lfo_range, + state->lfo_scale, state->depth, state->delay, todo); + GetTriangleDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range, + state->lfo_range, state->lfo_scale, state->depth, state->delay, + todo); break; case CWF_Sinusoid: - ProcessSinusoid(state, td, SamplesIn+base, temps); + GetSinusoidDelays(moddelays[0], offset%state->lfo_range, state->lfo_range, + state->lfo_scale, state->depth, state->delay, todo); + GetSinusoidDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range, + state->lfo_range, state->lfo_scale, state->depth, state->delay, + todo); break; } - for(kt = 0;kt < NumChannels;kt++) + for(i = 0;i < todo;i++) { - ALfloat gain = state->Gain[0][kt]; + leftbuf[offset&bufmask] = SamplesIn[0][base+i]; + temps[i][0] = leftbuf[(offset-moddelays[0][i])&bufmask] * feedback; + leftbuf[offset&bufmask] += temps[i][0]; + + rightbuf[offset&bufmask] = SamplesIn[0][base+i]; + temps[i][1] = rightbuf[(offset-moddelays[1][i])&bufmask] * feedback; + rightbuf[offset&bufmask] += temps[i][1]; + + offset++; + } + + for(c = 0;c < NumChannels;c++) + { + ALfloat gain = state->Gain[0][c]; if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) { - for(it = 0;it < td;it++) - SamplesOut[kt][it+base] += temps[it][0] * gain; + for(i = 0;i < todo;i++) + SamplesOut[c][i+base] += temps[i][0] * gain; } - gain = state->Gain[1][kt]; + gain = state->Gain[1][c]; if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) { - for(it = 0;it < td;it++) - SamplesOut[kt][it+base] += temps[it][1] * gain; + for(i = 0;i < todo;i++) + SamplesOut[c][i+base] += temps[i][1] * gain; } } - base += td; + base += todo; } + + state->offset = offset; } -DECLARE_DEFAULT_ALLOCATORS(ALchorusState) - -DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState); - typedef struct ALchorusStateFactory { DERIVE_FROM_TYPE(ALeffectStateFactory); @@ -257,16 +276,8 @@ static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(f { ALchorusState *state; - state = ALchorusState_New(sizeof(*state)); + NEW_OBJ0(state, ALchorusState)(); if(!state) return NULL; - SET_VTABLE2(ALchorusState, ALeffectState, state); - - state->BufferLength = 0; - state->SampleBuffer[0] = NULL; - state->SampleBuffer[1] = NULL; - state->offset = 0; - state->lfo_range = 1; - state->waveform = CWF_Triangle; return STATIC_CAST(ALeffectState, state); } diff --git a/libs/openal-soft/Alc/effects/compressor.c b/libs/openal-soft/Alc/effects/compressor.c index 9859a085..25c7a3dc 100644 --- a/libs/openal-soft/Alc/effects/compressor.c +++ b/libs/openal-soft/Alc/effects/compressor.c @@ -31,7 +31,7 @@ typedef struct ALcompressorState { DERIVE_FROM_TYPE(ALeffectState); /* Effect gains for each channel */ - ALfloat Gain[MAX_OUTPUT_CHANNELS]; + ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS]; /* Effect parameters */ ALboolean Enabled; @@ -40,8 +40,29 @@ typedef struct ALcompressorState { ALfloat GainCtrl; } ALcompressorState; -static ALvoid ALcompressorState_Destruct(ALcompressorState *UNUSED(state)) +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, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels); +DECLARE_DEFAULT_ALLOCATORS(ALcompressorState) + +DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState); + + +static void ALcompressorState_Construct(ALcompressorState *state) { + ALeffectState_Construct(STATIC_CAST(ALeffectState, state)); + SET_VTABLE2(ALcompressorState, ALeffectState, state); + + state->Enabled = AL_TRUE; + state->AttackRate = 0.0f; + state->ReleaseRate = 0.0f; + state->GainCtrl = 1.0f; +} + +static ALvoid ALcompressorState_Destruct(ALcompressorState *state) +{ + ALeffectState_Destruct(STATIC_CAST(ALeffectState,state)); } static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device) @@ -55,85 +76,107 @@ static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdev return AL_TRUE; } -static ALvoid ALcompressorState_update(ALcompressorState *state, ALCdevice *device, const ALeffectslot *slot) +static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props) { - state->Enabled = slot->EffectProps.Compressor.OnOff; + ALuint i; - ComputeAmbientGains(device, slot->Gain, state->Gain); + state->Enabled = props->Compressor.OnOff; + + 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], + slot->Params.Gain, state->Gain[i]); } -static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE], ALuint NumChannels) +static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels) { - ALuint it, kt; - ALuint base; + ALsizei i, j, k; + ALsizei base; for(base = 0;base < SamplesToDo;) { - ALfloat temps[256]; - ALuint td = minu(256, SamplesToDo-base); + ALfloat temps[64][4]; + ALsizei td = mini(64, SamplesToDo-base); + + /* Load samples into the temp buffer first. */ + for(j = 0;j < 4;j++) + { + for(i = 0;i < td;i++) + temps[i][j] = SamplesIn[j][i+base]; + } if(state->Enabled) { - ALfloat output, smp, amplitude; ALfloat gain = state->GainCtrl; + ALfloat output, amplitude; - for(it = 0;it < td;it++) + for(i = 0;i < td;i++) { - smp = SamplesIn[it+base]; - - amplitude = fabsf(smp); + /* Roughly calculate the maximum amplitude from the 4-channel + * signal, and attack or release the gain control to reach it. + */ + amplitude = fabsf(temps[i][0]); + amplitude = maxf(amplitude + fabsf(temps[i][1]), + maxf(amplitude + fabsf(temps[i][2]), + amplitude + fabsf(temps[i][3]))); if(amplitude > gain) gain = minf(gain+state->AttackRate, amplitude); else if(amplitude < gain) gain = maxf(gain-state->ReleaseRate, amplitude); - output = 1.0f / clampf(gain, 0.5f, 2.0f); - temps[it] = smp * output; + /* Apply the inverse of the gain control to normalize/compress + * the volume. */ + output = 1.0f / clampf(gain, 0.5f, 2.0f); + for(j = 0;j < 4;j++) + temps[i][j] *= output; } state->GainCtrl = gain; } else { - ALfloat output, smp, amplitude; ALfloat gain = state->GainCtrl; + ALfloat output, amplitude; - for(it = 0;it < td;it++) + for(i = 0;i < td;i++) { - smp = SamplesIn[it+base]; - + /* Same as above, except the amplitude is forced to 1. This + * helps ensure smooth gain changes when the compressor is + * turned on and off. + */ amplitude = 1.0f; if(amplitude > gain) gain = minf(gain+state->AttackRate, amplitude); else if(amplitude < gain) gain = maxf(gain-state->ReleaseRate, amplitude); - output = 1.0f / clampf(gain, 0.5f, 2.0f); - temps[it] = smp * output; + output = 1.0f / clampf(gain, 0.5f, 2.0f); + for(j = 0;j < 4;j++) + temps[i][j] *= output; } state->GainCtrl = gain; } - - for(kt = 0;kt < NumChannels;kt++) + /* Now mix to the output. */ + for(j = 0;j < 4;j++) { - ALfloat gain = state->Gain[kt]; - if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) - continue; + for(k = 0;k < NumChannels;k++) + { + ALfloat gain = state->Gain[j][k]; + if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) + continue; - for(it = 0;it < td;it++) - SamplesOut[kt][base+it] += gain * temps[it]; + for(i = 0;i < td;i++) + SamplesOut[k][base+i] += gain * temps[i][j]; + } } base += td; } } -DECLARE_DEFAULT_ALLOCATORS(ALcompressorState) - -DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState); - typedef struct ALcompressorStateFactory { DERIVE_FROM_TYPE(ALeffectStateFactory); @@ -143,14 +186,8 @@ static ALeffectState *ALcompressorStateFactory_create(ALcompressorStateFactory * { ALcompressorState *state; - state = ALcompressorState_New(sizeof(*state)); + NEW_OBJ0(state, ALcompressorState)(); if(!state) return NULL; - SET_VTABLE2(ALcompressorState, ALeffectState, state); - - state->Enabled = AL_TRUE; - state->AttackRate = 0.0f; - state->ReleaseRate = 0.0f; - state->GainCtrl = 1.0f; return STATIC_CAST(ALeffectState, state); } diff --git a/libs/openal-soft/Alc/effects/dedicated.c b/libs/openal-soft/Alc/effects/dedicated.c index dc581ace..52d0ec6d 100644 --- a/libs/openal-soft/Alc/effects/dedicated.c +++ b/libs/openal-soft/Alc/effects/dedicated.c @@ -35,9 +35,29 @@ typedef struct ALdedicatedState { ALfloat gains[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, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels); +DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState) -static ALvoid ALdedicatedState_Destruct(ALdedicatedState *UNUSED(state)) +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) +{ + ALeffectState_Destruct(STATIC_CAST(ALeffectState,state)); } static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state), ALCdevice *UNUSED(device)) @@ -45,7 +65,7 @@ static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state), return AL_TRUE; } -static ALvoid ALdedicatedState_update(ALdedicatedState *state, ALCdevice *device, const ALeffectslot *Slot) +static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCdevice *device, const ALeffectslot *Slot, const ALeffectProps *props) { ALfloat Gain; ALuint i; @@ -53,47 +73,57 @@ static ALvoid ALdedicatedState_update(ALdedicatedState *state, ALCdevice *device for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) state->gains[i] = 0.0f; - Gain = Slot->Gain * Slot->EffectProps.Dedicated.Gain; - if(Slot->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, 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; + } } - else if(Slot->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, 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; + } else { - static const ALfloat front_dir[3] = { 0.0f, 0.0f, -1.0f }; - ComputeDirectionalGains(device, front_dir, Gain, state->gains); + ALfloat coeffs[MAX_AMBI_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); } } } -static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, 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; + ALsizei i, c; + SamplesIn = ASSUME_ALIGNED(SamplesIn, 16); + SamplesOut = ASSUME_ALIGNED(SamplesOut, 16); for(c = 0;c < NumChannels;c++) { - if(!(fabsf(gains[c]) > GAIN_SILENCE_THRESHOLD)) + const ALfloat gain = state->gains[c]; + if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) continue; for(i = 0;i < SamplesToDo;i++) - SamplesOut[c][i] += SamplesIn[i] * gains[c]; + SamplesOut[c][i] += SamplesIn[0][i] * gain; } } -DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState) - -DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState); - typedef struct ALdedicatedStateFactory { DERIVE_FROM_TYPE(ALeffectStateFactory); @@ -102,14 +132,9 @@ typedef struct ALdedicatedStateFactory { ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(factory)) { ALdedicatedState *state; - ALsizei s; - state = ALdedicatedState_New(sizeof(*state)); + NEW_OBJ0(state, ALdedicatedState)(); if(!state) return NULL; - SET_VTABLE2(ALdedicatedState, ALeffectState, state); - - for(s = 0;s < MAX_OUTPUT_CHANNELS;s++) - state->gains[s] = 0.0f; return STATIC_CAST(ALeffectState, state); } diff --git a/libs/openal-soft/Alc/effects/distortion.c b/libs/openal-soft/Alc/effects/distortion.c index 221cec39..4619894d 100644 --- a/libs/openal-soft/Alc/effects/distortion.c +++ b/libs/openal-soft/Alc/effects/distortion.c @@ -43,8 +43,27 @@ typedef struct ALdistortionState { ALfloat edge_coeff; } ALdistortionState; -static ALvoid ALdistortionState_Destruct(ALdistortionState *UNUSED(state)) +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, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels); +DECLARE_DEFAULT_ALLOCATORS(ALdistortionState) + +DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState); + + +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) +{ + ALeffectState_Destruct(STATIC_CAST(ALeffectState,state)); } static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state), ALCdevice *UNUSED(device)) @@ -52,104 +71,95 @@ static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state) return AL_TRUE; } -static ALvoid ALdistortionState_update(ALdistortionState *state, ALCdevice *Device, const ALeffectslot *Slot) +static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props) { ALfloat frequency = (ALfloat)Device->Frequency; ALfloat bandwidth; ALfloat cutoff; ALfloat edge; - /* Store distorted signal attenuation settings */ - state->attenuation = Slot->EffectProps.Distortion.Gain; + /* Store distorted signal attenuation settings. */ + state->attenuation = props->Distortion.Gain; - /* Store waveshaper edge settings */ - edge = sinf(Slot->EffectProps.Distortion.Edge * (F_PI_2)); + /* 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 = Slot->EffectProps.Distortion.LowpassCutoff; - /* Bandwidth value is constant in octaves */ + cutoff = props->Distortion.LowpassCutoff; + /* Bandwidth value is constant in octaves. */ bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f); + /* Multiply sampling frequency by the amount of oversampling done during + * processing. + */ ALfilterState_setParams(&state->lowpass, ALfilterType_LowPass, 1.0f, cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth) ); - /* Bandpass filter */ - cutoff = Slot->EffectProps.Distortion.EQCenter; - /* Convert bandwidth in Hz to octaves */ - bandwidth = Slot->EffectProps.Distortion.EQBandwidth / (cutoff * 0.67f); + cutoff = props->Distortion.EQCenter; + /* Convert bandwidth in Hz to octaves. */ + bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f); ALfilterState_setParams(&state->bandpass, ALfilterType_BandPass, 1.0f, cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth) ); - ComputeAmbientGains(Device, Slot->Gain, state->Gain); + ComputeAmbientGains(Device->Dry, Slot->Params.Gain, state->Gain); } -static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, 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) { const ALfloat fc = state->edge_coeff; - ALuint base; - ALuint it; - ALuint ot; - ALuint kt; + ALsizei it, kt; + ALsizei base; for(base = 0;base < SamplesToDo;) { - float oversample_buffer[64][4]; - ALuint td = minu(64, SamplesToDo-base); + float buffer[2][64 * 4]; + ALsizei td = mini(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. */ + /* 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 */ + /* Fill oversample buffer using zero stuffing. */ for(it = 0;it < td;it++) { - oversample_buffer[it][0] = SamplesIn[it+base]; - oversample_buffer[it][1] = 0.0f; - oversample_buffer[it][2] = 0.0f; - oversample_buffer[it][3] = 0.0f; + /* Multiply the sample by the amount of oversampling to maintain + * the signal's power. + */ + buffer[0][it*4 + 0] = SamplesIn[0][it+base] * 4.0f; + buffer[0][it*4 + 1] = 0.0f; + buffer[0][it*4 + 2] = 0.0f; + buffer[0][it*4 + 3] = 0.0f; } - /* First step, do lowpass filtering of original signal, */ - /* additionally perform buffer interpolation and lowpass */ - /* cutoff for oversampling (which is fortunately first */ - /* step of distortion). So combine three operations into */ - /* the one. */ - for(it = 0;it < td;it++) + /* 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); + + /* 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*4;it++) { - for(ot = 0;ot < 4;ot++) - { - ALfloat smp; - smp = ALfilterState_processSingle(&state->lowpass, oversample_buffer[it][ot]); + ALfloat smp = buffer[1][it]; - /* Restore signal power by multiplying sample by amount of oversampling */ - oversample_buffer[it][ot] = smp * 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[0][it] = smp; } - for(it = 0;it < td;it++) - { - /* Second step, do distortion using waveshaper function */ - /* to emulate signal processing during tube overdriving. */ - /* Three steps of waveshaping are intended to modify */ - /* waveform without boost/clipping/attenuation process. */ - for(ot = 0;ot < 4;ot++) - { - ALfloat smp = oversample_buffer[it][ot]; - - smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)); - smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f; - smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)); - - /* Third step, do bandpass filtering of distorted signal */ - smp = ALfilterState_processSingle(&state->bandpass, smp); - oversample_buffer[it][ot] = smp; - } - } + /* Third step, do bandpass filtering of distorted signal. */ + ALfilterState_process(&state->bandpass, buffer[1], buffer[0], td*4); for(kt = 0;kt < NumChannels;kt++) { @@ -161,17 +171,13 @@ static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint Samples continue; for(it = 0;it < td;it++) - SamplesOut[kt][base+it] += gain * oversample_buffer[it][0]; + SamplesOut[kt][base+it] += gain * buffer[1][it*4]; } base += td; } } -DECLARE_DEFAULT_ALLOCATORS(ALdistortionState) - -DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState); - typedef struct ALdistortionStateFactory { DERIVE_FROM_TYPE(ALeffectStateFactory); @@ -181,12 +187,8 @@ static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory * { ALdistortionState *state; - state = ALdistortionState_New(sizeof(*state)); + NEW_OBJ0(state, ALdistortionState)(); if(!state) return NULL; - SET_VTABLE2(ALdistortionState, ALeffectState, state); - - ALfilterState_clear(&state->lowpass); - ALfilterState_clear(&state->bandpass); return STATIC_CAST(ALeffectState, state); } diff --git a/libs/openal-soft/Alc/effects/echo.c b/libs/openal-soft/Alc/effects/echo.c index f5a53c36..c0b1bf63 100644 --- a/libs/openal-soft/Alc/effects/echo.c +++ b/libs/openal-soft/Alc/effects/echo.c @@ -34,14 +34,14 @@ 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]; @@ -50,28 +50,53 @@ typedef struct ALechoState { ALfilterState 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, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels); +DECLARE_DEFAULT_ALLOCATORS(ALechoState) + +DEFINE_ALEFFECTSTATE_VTABLE(ALechoState); + + +static void ALechoState_Construct(ALechoState *state) +{ + ALeffectState_Construct(STATIC_CAST(ALeffectState, state)); + SET_VTABLE2(ALechoState, ALeffectState, state); + + state->BufferLength = 0; + state->SampleBuffer = NULL; + + state->Tap[0].delay = 0; + state->Tap[1].delay = 0; + state->Offset = 0; + + ALfilterState_clear(&state->Filter); +} + static ALvoid ALechoState_Destruct(ALechoState *state) { - free(state->SampleBuffer); + al_free(state->SampleBuffer); state->SampleBuffer = NULL; + ALeffectState_Destruct(STATIC_CAST(ALeffectState,state)); } static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device) { - ALuint maxlen, i; + ALsizei maxlen, i; // Use the next power of 2 for the buffer length, so the tap offsets can be // wrapped using a mask instead of a modulo - maxlen = fastf2u(AL_ECHO_MAX_DELAY * Device->Frequency) + 1; - maxlen += fastf2u(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1; + maxlen = fastf2i(AL_ECHO_MAX_DELAY * Device->Frequency) + 1; + maxlen += fastf2i(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1; maxlen = NextPowerOf2(maxlen); if(maxlen != state->BufferLength) { - void *temp; - - temp = realloc(state->SampleBuffer, maxlen * sizeof(ALfloat)); + void *temp = al_calloc(16, maxlen * sizeof(ALfloat)); if(!temp) return AL_FALSE; + + al_free(state->SampleBuffer); state->SampleBuffer = temp; state->BufferLength = maxlen; } @@ -81,50 +106,60 @@ static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device) return AL_TRUE; } -static ALvoid ALechoState_update(ALechoState *state, ALCdevice *Device, const ALeffectslot *Slot) +static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props) { - ALfloat pandir[3] = { 0.0f, 0.0f, 0.0f }; ALuint frequency = Device->Frequency; - ALfloat gain, lrpan; + ALfloat coeffs[MAX_AMBI_COEFFS]; + ALfloat gain, lrpan, spread; - state->Tap[0].delay = fastf2u(Slot->EffectProps.Echo.Delay * frequency) + 1; - state->Tap[1].delay = fastf2u(Slot->EffectProps.Echo.LRDelay * frequency); + state->Tap[0].delay = fastf2i(props->Echo.Delay * frequency) + 1; + state->Tap[1].delay = fastf2i(props->Echo.LRDelay * frequency); state->Tap[1].delay += state->Tap[0].delay; - lrpan = Slot->EffectProps.Echo.Spread; + spread = props->Echo.Spread; + if(spread < 0.0f) lrpan = -1.0f; + else lrpan = 1.0f; + /* Convert echo spread (where 0 = omni, +/-1 = directional) to coverage + * spread (where 0 = point, tau = omni). + */ + spread = asinf(1.0f - fabsf(spread))*4.0f; - state->FeedGain = Slot->EffectProps.Echo.Feedback; + state->FeedGain = props->Echo.Feedback; - gain = minf(1.0f - Slot->EffectProps.Echo.Damping, 0.01f); + gain = maxf(1.0f - props->Echo.Damping, 0.0625f); /* Limit -24dB */ ALfilterState_setParams(&state->Filter, ALfilterType_HighShelf, gain, LOWPASSFREQREF/frequency, - calc_rcpQ_from_slope(gain, 0.75f)); + calc_rcpQ_from_slope(gain, 1.0f)); - gain = Slot->Gain; + gain = Slot->Params.Gain; /* First tap panning */ - pandir[0] = -lrpan; - ComputeDirectionalGains(Device, pandir, gain, state->Gain[0]); + CalcAngleCoeffs(-F_PI_2*lrpan, 0.0f, spread, coeffs); + ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[0]); /* Second tap panning */ - pandir[0] = +lrpan; - ComputeDirectionalGains(Device, pandir, gain, state->Gain[1]); + CalcAngleCoeffs( F_PI_2*lrpan, 0.0f, spread, coeffs); + ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[1]); } -static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, 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 smp; - 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; + ALsizei offset = state->Offset; + ALfloat x[2], y[2], in, out; + ALsizei base, k; + ALsizei 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]; for(base = 0;base < SamplesToDo;) { ALfloat temps[128][2]; - ALuint td = minu(128, SamplesToDo-base); + ALsizei td = mini(128, SamplesToDo-base); for(i = 0;i < td;i++) { @@ -135,8 +170,14 @@ static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const // Apply damping and feedback gain to the second tap, and mix in the // new sample - smp = ALfilterState_processSingle(&state->Filter, temps[i][1]+SamplesIn[i+base]); - state->SampleBuffer[offset&mask] = smp * state->FeedGain; + 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; + + state->SampleBuffer[offset&mask] = out * state->FeedGain; offset++; } @@ -159,14 +200,14 @@ static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const 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->Offset = offset; } -DECLARE_DEFAULT_ALLOCATORS(ALechoState) - -DEFINE_ALEFFECTSTATE_VTABLE(ALechoState); - typedef struct ALechoStateFactory { DERIVE_FROM_TYPE(ALeffectStateFactory); @@ -176,18 +217,8 @@ ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory)) { ALechoState *state; - state = ALechoState_New(sizeof(*state)); + NEW_OBJ0(state, ALechoState)(); if(!state) return NULL; - SET_VTABLE2(ALechoState, ALeffectState, state); - - state->BufferLength = 0; - state->SampleBuffer = NULL; - - state->Tap[0].delay = 0; - state->Tap[1].delay = 0; - state->Offset = 0; - - ALfilterState_clear(&state->Filter); return STATIC_CAST(ALeffectState, state); } diff --git a/libs/openal-soft/Alc/effects/equalizer.c b/libs/openal-soft/Alc/effects/equalizer.c index 244667ab..030eacc9 100644 --- a/libs/openal-soft/Alc/effects/equalizer.c +++ b/libs/openal-soft/Alc/effects/equalizer.c @@ -71,18 +71,50 @@ * filter coefficients" by Robert Bristow-Johnson * * 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_OUTPUT_CHANNELS]; + ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS]; /* Effect parameters */ - ALfilterState filter[4]; + ALfilterState filter[4][MAX_EFFECT_CHANNELS]; + + ALfloat SampleBuffer[4][MAX_EFFECT_CHANNELS][MAX_UPDATE_SAMPLES]; } ALequalizerState; -static ALvoid ALequalizerState_Destruct(ALequalizerState *UNUSED(state)) +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, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels); +DECLARE_DEFAULT_ALLOCATORS(ALequalizerState) + +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) +{ + ALeffectState_Destruct(STATIC_CAST(ALeffectState,state)); } static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state), ALCdevice *UNUSED(device)) @@ -90,82 +122,96 @@ static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state), return AL_TRUE; } -static ALvoid ALequalizerState_update(ALequalizerState *state, ALCdevice *device, const ALeffectslot *slot) +static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props) { ALfloat frequency = (ALfloat)device->Frequency; ALfloat gain, freq_mult; + ALuint i; - ComputeAmbientGains(device, slot->Gain, state->Gain); + 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]); /* 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(slot->EffectProps.Equalizer.LowGain); - freq_mult = slot->EffectProps.Equalizer.LowCutoff/frequency; - ALfilterState_setParams(&state->filter[0], ALfilterType_LowShelf, + gain = maxf(sqrtf(props->Equalizer.LowGain), 0.0625f); /* Limit -24dB */ + freq_mult = props->Equalizer.LowCutoff/frequency; + ALfilterState_setParams(&state->filter[0][0], ALfilterType_LowShelf, gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f) ); + /* Copy the filter coefficients for the other input channels. */ + for(i = 1;i < MAX_EFFECT_CHANNELS;i++) + ALfilterState_copyParams(&state->filter[0][i], &state->filter[0][0]); - gain = slot->EffectProps.Equalizer.Mid1Gain; - freq_mult = slot->EffectProps.Equalizer.Mid1Center/frequency; - ALfilterState_setParams(&state->filter[1], ALfilterType_Peaking, - gain, freq_mult, calc_rcpQ_from_bandwidth(freq_mult, slot->EffectProps.Equalizer.Mid1Width) + gain = maxf(props->Equalizer.Mid1Gain, 0.0625f); + 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++) + ALfilterState_copyParams(&state->filter[1][i], &state->filter[1][0]); - gain = slot->EffectProps.Equalizer.Mid2Gain; - freq_mult = slot->EffectProps.Equalizer.Mid2Center/frequency; - ALfilterState_setParams(&state->filter[2], ALfilterType_Peaking, - gain, freq_mult, calc_rcpQ_from_bandwidth(freq_mult, slot->EffectProps.Equalizer.Mid2Width) + gain = maxf(props->Equalizer.Mid2Gain, 0.0625f); + 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++) + ALfilterState_copyParams(&state->filter[2][i], &state->filter[2][0]); - gain = sqrtf(slot->EffectProps.Equalizer.HighGain); - freq_mult = slot->EffectProps.Equalizer.HighCutoff/frequency; - ALfilterState_setParams(&state->filter[3], ALfilterType_HighShelf, + gain = maxf(sqrtf(props->Equalizer.HighGain), 0.0625f); + 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++) + ALfilterState_copyParams(&state->filter[3][i], &state->filter[3][0]); } -static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, 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) { - ALuint base; - ALuint it; - ALuint kt; - ALuint ft; + ALfloat (*Samples)[MAX_EFFECT_CHANNELS][MAX_UPDATE_SAMPLES] = state->SampleBuffer; + ALsizei it, kt, ft; + ALsizei base; for(base = 0;base < SamplesToDo;) { - ALfloat temps[256]; - ALuint td = minu(256, SamplesToDo-base); + ALsizei td = mini(MAX_UPDATE_SAMPLES, SamplesToDo-base); - for(it = 0;it < td;it++) + 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++) { - ALfloat smp = SamplesIn[base+it]; + for(kt = 0;kt < NumChannels;kt++) + { + ALfloat gain = state->Gain[ft][kt]; + if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) + continue; - for(ft = 0;ft < 4;ft++) - smp = ALfilterState_processSingle(&state->filter[ft], smp); - - temps[it] = smp; - } - - for(kt = 0;kt < NumChannels;kt++) - { - ALfloat gain = state->Gain[kt]; - if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) - continue; - - for(it = 0;it < td;it++) - SamplesOut[kt][base+it] += gain * temps[it]; + for(it = 0;it < td;it++) + SamplesOut[kt][base+it] += gain * Samples[3][ft][it]; + } } base += td; } } -DECLARE_DEFAULT_ALLOCATORS(ALequalizerState) - -DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState); - typedef struct ALequalizerStateFactory { DERIVE_FROM_TYPE(ALeffectStateFactory); @@ -174,16 +220,9 @@ typedef struct ALequalizerStateFactory { ALeffectState *ALequalizerStateFactory_create(ALequalizerStateFactory *UNUSED(factory)) { ALequalizerState *state; - int it; - state = ALequalizerState_New(sizeof(*state)); + NEW_OBJ0(state, ALequalizerState)(); if(!state) return NULL; - SET_VTABLE2(ALequalizerState, ALeffectState, state); - - /* Initialize sample history only on filter creation to avoid */ - /* sound clicks if filter settings were changed in runtime. */ - for(it = 0; it < 4; it++) - ALfilterState_clear(&state->filter[it]); return STATIC_CAST(ALeffectState, state); } diff --git a/libs/openal-soft/Alc/effects/flanger.c b/libs/openal-soft/Alc/effects/flanger.c index f6191abd..b25b95cc 100644 --- a/libs/openal-soft/Alc/effects/flanger.c +++ b/libs/openal-soft/Alc/effects/flanger.c @@ -39,9 +39,9 @@ typedef struct ALflangerState { DERIVE_FROM_TYPE(ALeffectState); ALfloat *SampleBuffer[2]; - ALuint BufferLength; - ALuint offset; - ALuint lfo_range; + ALsizei BufferLength; + ALsizei offset; + ALsizei lfo_range; ALfloat lfo_scale; ALint lfo_disp; @@ -55,27 +55,51 @@ typedef struct ALflangerState { ALfloat feedback; } ALflangerState; -static ALvoid ALflangerState_Destruct(ALflangerState *state) +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, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels); +DECLARE_DEFAULT_ALLOCATORS(ALflangerState) + +DEFINE_ALEFFECTSTATE_VTABLE(ALflangerState); + + +static void ALflangerState_Construct(ALflangerState *state) { - free(state->SampleBuffer[0]); + 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; + ALsizei maxlen; + ALsizei it; - maxlen = fastf2u(AL_FLANGER_MAX_DELAY * 3.0f * Device->Frequency) + 1; + maxlen = fastf2i(AL_FLANGER_MAX_DELAY * 2.0f * Device->Frequency) + 1; maxlen = NextPowerOf2(maxlen); if(maxlen != state->BufferLength) { - void *temp; - - temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2); + 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; @@ -91,15 +115,14 @@ static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *D return AL_TRUE; } -static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, const ALeffectslot *Slot) +static ALvoid ALflangerState_update(ALflangerState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props) { - static const ALfloat left_dir[3] = { -1.0f, 0.0f, 0.0f }; - static const ALfloat right_dir[3] = { 1.0f, 0.0f, 0.0f }; ALfloat frequency = (ALfloat)Device->Frequency; + ALfloat coeffs[MAX_AMBI_COEFFS]; ALfloat rate; ALint phase; - switch(Slot->EffectProps.Flanger.Waveform) + switch(props->Flanger.Waveform) { case AL_FLANGER_WAVEFORM_TRIANGLE: state->waveform = FWF_Triangle; @@ -108,16 +131,19 @@ static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, co state->waveform = FWF_Sinusoid; break; } - state->depth = Slot->EffectProps.Flanger.Depth; - state->feedback = Slot->EffectProps.Flanger.Feedback; - state->delay = fastf2i(Slot->EffectProps.Flanger.Delay * frequency); + state->feedback = props->Flanger.Feedback; + state->delay = fastf2i(props->Flanger.Delay * frequency); + /* The LFO depth is scaled to be relative to the sample delay. */ + state->depth = props->Flanger.Depth * state->delay; /* Gains for left and right sides */ - ComputeDirectionalGains(Device, left_dir, Slot->Gain, state->Gain[0]); - ComputeDirectionalGains(Device, right_dir, Slot->Gain, state->Gain[1]); + CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs); + ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[0]); + CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs); + ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[1]); - phase = Slot->EffectProps.Flanger.Phase; - rate = Slot->EffectProps.Flanger.Rate; + phase = props->Flanger.Phase; + rate = props->Flanger.Rate; if(!(rate > 0.0f)) { state->lfo_scale = 0.0f; @@ -127,7 +153,7 @@ static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, co else { /* Calculate LFO coefficient */ - state->lfo_range = fastf2u(frequency/rate + 0.5f); + state->lfo_range = fastf2i(frequency/rate + 0.5f); switch(state->waveform) { case FWF_Triangle: @@ -139,115 +165,107 @@ static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, co } /* Calculate lfo phase displacement */ - state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f)); + if(phase >= 0) + state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f)); + else + state->lfo_disp = fastf2i(state->lfo_range * ((360+phase)/360.0f)); } } -static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *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 ALflangerState *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(ALflangerState *state, const ALuint SamplesToDo, \ - const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \ -{ \ - const ALuint bufmask = state->BufferLength-1; \ - ALfloat *restrict leftbuf = state->SampleBuffer[0]; \ - ALfloat *restrict rightbuf = state->SampleBuffer[1]; \ - ALuint offset = state->offset; \ - const ALfloat feedback = state->feedback; \ - ALuint it; \ - \ - for(it = 0;it < SamplesToDo;it++) \ - { \ - ALint delay_left, delay_right; \ - Func(&delay_left, &delay_right, offset, state); \ - \ - out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \ - leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback; \ - \ - out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \ - rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback; \ - \ - offset++; \ - } \ - state->offset = offset; \ -} - -DECL_TEMPLATE(Triangle) -DECL_TEMPLATE(Sinusoid) - -#undef DECL_TEMPLATE - -static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) +static ALvoid ALflangerState_process(ALflangerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels) { - ALuint it, kt; - ALuint base; + ALfloat *restrict leftbuf = state->SampleBuffer[0]; + ALfloat *restrict rightbuf = state->SampleBuffer[1]; + const ALsizei bufmask = state->BufferLength-1; + const ALfloat feedback = state->feedback; + ALsizei offset = state->offset; + ALsizei i, c; + ALsizei base; for(base = 0;base < SamplesToDo;) { + const ALsizei todo = mini(128, SamplesToDo-base); ALfloat temps[128][2]; - ALuint td = minu(128, SamplesToDo-base); + ALint moddelays[2][128]; switch(state->waveform) { case FWF_Triangle: - ProcessTriangle(state, td, SamplesIn+base, temps); + GetTriangleDelays(moddelays[0], offset%state->lfo_range, state->lfo_range, + state->lfo_scale, state->depth, state->delay, todo); + GetTriangleDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range, + state->lfo_range, state->lfo_scale, state->depth, state->delay, + todo); break; case FWF_Sinusoid: - ProcessSinusoid(state, td, SamplesIn+base, temps); + GetSinusoidDelays(moddelays[0], offset%state->lfo_range, state->lfo_range, + state->lfo_scale, state->depth, state->delay, todo); + GetSinusoidDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range, + state->lfo_range, state->lfo_scale, state->depth, state->delay, + todo); break; } - for(kt = 0;kt < NumChannels;kt++) + for(i = 0;i < todo;i++) { - ALfloat gain = state->Gain[0][kt]; + leftbuf[offset&bufmask] = SamplesIn[0][base+i]; + temps[i][0] = leftbuf[(offset-moddelays[0][i])&bufmask] * feedback; + leftbuf[offset&bufmask] += temps[i][0]; + + rightbuf[offset&bufmask] = SamplesIn[0][base+i]; + temps[i][1] = rightbuf[(offset-moddelays[1][i])&bufmask] * feedback; + rightbuf[offset&bufmask] += temps[i][1]; + + offset++; + } + + for(c = 0;c < NumChannels;c++) + { + ALfloat gain = state->Gain[0][c]; if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) { - for(it = 0;it < td;it++) - SamplesOut[kt][it+base] += temps[it][0] * gain; + for(i = 0;i < todo;i++) + SamplesOut[c][i+base] += temps[i][0] * gain; } - gain = state->Gain[1][kt]; + gain = state->Gain[1][c]; if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) { - for(it = 0;it < td;it++) - SamplesOut[kt][it+base] += temps[it][1] * gain; + for(i = 0;i < todo;i++) + SamplesOut[c][i+base] += temps[i][1] * gain; } } - base += td; + base += todo; } + + state->offset = offset; } -DECLARE_DEFAULT_ALLOCATORS(ALflangerState) - -DEFINE_ALEFFECTSTATE_VTABLE(ALflangerState); - typedef struct ALflangerStateFactory { DERIVE_FROM_TYPE(ALeffectStateFactory); @@ -257,16 +275,8 @@ ALeffectState *ALflangerStateFactory_create(ALflangerStateFactory *UNUSED(factor { ALflangerState *state; - state = ALflangerState_New(sizeof(*state)); + NEW_OBJ0(state, ALflangerState)(); if(!state) return NULL; - SET_VTABLE2(ALflangerState, ALeffectState, state); - - state->BufferLength = 0; - state->SampleBuffer[0] = NULL; - state->SampleBuffer[1] = NULL; - state->offset = 0; - state->lfo_range = 1; - state->waveform = FWF_Triangle; return STATIC_CAST(ALeffectState, state); } diff --git a/libs/openal-soft/Alc/effects/modulator.c b/libs/openal-soft/Alc/effects/modulator.c index 32d25c76..5de88fcd 100644 --- a/libs/openal-soft/Alc/effects/modulator.c +++ b/libs/openal-soft/Alc/effects/modulator.c @@ -33,78 +33,55 @@ typedef struct ALmodulatorState { DERIVE_FROM_TYPE(ALeffectState); - enum { - SINUSOID, - SAWTOOTH, - SQUARE - } Waveform; + void (*Process)(ALfloat*, const ALfloat*, ALsizei, const ALsizei, ALsizei); - ALuint index; - ALuint step; + ALsizei index; + ALsizei step; - ALfloat Gain[MAX_OUTPUT_CHANNELS]; + ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS]; - ALfilterState Filter; + ALfilterState Filter[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, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels); +DECLARE_DEFAULT_ALLOCATORS(ALmodulatorState) + +DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState); + + #define WAVEFORM_FRACBITS 24 #define WAVEFORM_FRACONE (1<> (WAVEFORM_FRACBITS - 1)) & 1); } #define DECL_TEMPLATE(func) \ -static void Process##func(ALmodulatorState *state, ALuint SamplesToDo, \ - const ALfloat *restrict SamplesIn, \ - ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) \ +static void Modulate##func(ALfloat *restrict dst, const ALfloat *restrict src,\ + ALsizei index, const ALsizei step, ALsizei todo) \ { \ - const ALuint step = state->step; \ - ALuint index = state->index; \ - ALuint base; \ - \ - for(base = 0;base < SamplesToDo;) \ + ALsizei i; \ + for(i = 0;i < todo;i++) \ { \ - ALfloat temps[256]; \ - ALuint td = minu(256, SamplesToDo-base); \ - ALuint i, k; \ - \ - for(i = 0;i < td;i++) \ - { \ - ALfloat samp; \ - samp = SamplesIn[base+i]; \ - samp = ALfilterState_processSingle(&state->Filter, samp); \ - \ - index += step; \ - index &= WAVEFORM_FRACMASK; \ - temps[i] = samp * func(index); \ - } \ - \ - for(k = 0;k < NumChannels;k++) \ - { \ - ALfloat gain = state->Gain[k]; \ - if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) \ - continue; \ - \ - for(i = 0;i < td;i++) \ - SamplesOut[k][base+i] += gain * temps[i]; \ - } \ - \ - base += td; \ + index += step; \ + index &= WAVEFORM_FRACMASK; \ + dst[i] = src[i] * func(index); \ } \ - state->index = index; \ } DECL_TEMPLATE(Sin) @@ -114,8 +91,23 @@ DECL_TEMPLATE(Square) #undef DECL_TEMPLATE -static ALvoid ALmodulatorState_Destruct(ALmodulatorState *UNUSED(state)) +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) +{ + ALeffectState_Destruct(STATIC_CAST(ALeffectState,state)); } static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *UNUSED(state), ALCdevice *UNUSED(device)) @@ -123,55 +115,79 @@ static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *UNUSED(state), return AL_TRUE; } -static ALvoid ALmodulatorState_update(ALmodulatorState *state, ALCdevice *Device, const ALeffectslot *Slot) +static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props) { ALfloat cw, a; + ALsizei i; - if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SINUSOID) - state->Waveform = SINUSOID; - else if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH) - state->Waveform = SAWTOOTH; - else if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE) - state->Waveform = SQUARE; + if(props->Modulator.Waveform == AL_RING_MODULATOR_SINUSOID) + state->Process = ModulateSin; + else if(props->Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH) + state->Process = ModulateSaw; + else /*if(Slot->Params.EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)*/ + state->Process = ModulateSquare; - state->step = fastf2u(Slot->EffectProps.Modulator.Frequency*WAVEFORM_FRACONE / + state->step = fastf2i(props->Modulator.Frequency*WAVEFORM_FRACONE / Device->Frequency); if(state->step == 0) state->step = 1; /* Custom filter coeffs, which match the old version instead of a low-shelf. */ - cw = cosf(F_TAU * Slot->EffectProps.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); - state->Filter.a1 = -a; - state->Filter.a2 = 0.0f; - state->Filter.b1 = -a; - state->Filter.b2 = 0.0f; - state->Filter.input_gain = a; - - ComputeAmbientGains(Device, Slot->Gain, state->Gain); -} - -static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) -{ - switch(state->Waveform) + for(i = 0;i < MAX_EFFECT_CHANNELS;i++) { - case SINUSOID: - ProcessSin(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels); - break; - - case SAWTOOTH: - ProcessSaw(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels); - break; - - case SQUARE: - ProcessSquare(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels); - break; + state->Filter[i].b0 = a; + state->Filter[i].b1 = -a; + state->Filter[i].b2 = 0.0f; + state->Filter[i].a1 = -a; + state->Filter[i].a2 = 0.0f; } + + 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]); } -DECLARE_DEFAULT_ALLOCATORS(ALmodulatorState) +static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels) +{ + const ALsizei step = state->step; + ALsizei index = state->index; + ALsizei base; -DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState); + for(base = 0;base < SamplesToDo;) + { + ALfloat temps[2][128]; + ALsizei td = mini(128, SamplesToDo-base); + ALsizei i, j, k; + + for(j = 0;j < MAX_EFFECT_CHANNELS;j++) + { + ALfilterState_process(&state->Filter[j], temps[0], &SamplesIn[j][base], td); + state->Process(temps[1], temps[0], index, step, td); + + 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]; + } + } + + for(i = 0;i < td;i++) + { + index += step; + index &= WAVEFORM_FRACMASK; + } + base += td; + } + state->index = index; +} typedef struct ALmodulatorStateFactory { @@ -182,14 +198,8 @@ static ALeffectState *ALmodulatorStateFactory_create(ALmodulatorStateFactory *UN { ALmodulatorState *state; - state = ALmodulatorState_New(sizeof(*state)); + NEW_OBJ0(state, ALmodulatorState)(); if(!state) return NULL; - SET_VTABLE2(ALmodulatorState, ALeffectState, state); - - state->index = 0; - state->step = 1; - - ALfilterState_clear(&state->Filter); return STATIC_CAST(ALeffectState, state); } diff --git a/libs/openal-soft/Alc/effects/null.c b/libs/openal-soft/Alc/effects/null.c index adc4ca81..3812d8fc 100644 --- a/libs/openal-soft/Alc/effects/null.c +++ b/libs/openal-soft/Alc/effects/null.c @@ -13,12 +13,35 @@ typedef struct ALnullState { DERIVE_FROM_TYPE(ALeffectState); } 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, ALsizei samplesToDo, const ALfloatBUFFERSIZE*restrict samplesIn, ALfloatBUFFERSIZE*restrict samplesOut, ALsizei NumChannels); +static void *ALnullState_New(size_t size); +static void ALnullState_Delete(void *ptr); + +/* Define the ALeffectState vtable for this type. */ +DEFINE_ALEFFECTSTATE_VTABLE(ALnullState); + + +/* This constructs the effect state. It's called when the object is first + * created. Make sure to call the parent Construct function first, and set the + * vtable! + */ +static void ALnullState_Construct(ALnullState *state) +{ + ALeffectState_Construct(STATIC_CAST(ALeffectState, state)); + SET_VTABLE2(ALnullState, ALeffectState, state); +} /* This destructs (not free!) the effect state. It's called only when the - * effect slot is no longer used. + * effect slot is no longer used. Make sure to call the parent Destruct + * function before returning! */ -static ALvoid ALnullState_Destruct(ALnullState* UNUSED(state)) +static ALvoid ALnullState_Destruct(ALnullState *state) { + ALeffectState_Destruct(STATIC_CAST(ALeffectState,state)); } /* This updates the device-dependant effect state. This is called on @@ -33,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), ALCdevice* UNUSED(device), const ALeffectslot* UNUSED(slot)) +static ALvoid ALnullState_update(ALnullState* UNUSED(state), const ALCdevice* UNUSED(device), const ALeffectslot* UNUSED(slot), const ALeffectProps* UNUSED(props)) { } @@ -41,29 +64,26 @@ static ALvoid ALnullState_update(ALnullState* UNUSED(state), ALCdevice* UNUSED(d * input to the output buffer. The result should be added to the output buffer, * not replace it. */ -static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALuint UNUSED(samplesToDo), const ALfloat *restrict UNUSED(samplesIn), 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)) { } /* This allocates memory to store the object, before it gets constructed. - * DECLARE_DEFAULT_ALLOCATORS can be used to declate a default method. + * DECLARE_DEFAULT_ALLOCATORS can be used to declare a default method. */ static void *ALnullState_New(size_t size) { - return malloc(size); + return al_malloc(16, size); } /* This frees the memory used by the object, after it has been destructed. - * DECLARE_DEFAULT_ALLOCATORS can be used to declate a default method. + * DECLARE_DEFAULT_ALLOCATORS can be used to declare a default method. */ static void ALnullState_Delete(void *ptr) { - free(ptr); + al_free(ptr); } -/* Define the forwards and the ALeffectState vtable for this type. */ -DEFINE_ALEFFECTSTATE_VTABLE(ALnullState); - typedef struct ALnullStateFactory { DERIVE_FROM_TYPE(ALeffectStateFactory); @@ -74,10 +94,8 @@ ALeffectState *ALnullStateFactory_create(ALnullStateFactory *UNUSED(factory)) { ALnullState *state; - state = ALnullState_New(sizeof(*state)); + NEW_OBJ0(state, ALnullState)(); if(!state) return NULL; - /* Set vtables for inherited types. */ - SET_VTABLE2(ALnullState, ALeffectState, state); return STATIC_CAST(ALeffectState, state); } @@ -88,7 +106,6 @@ DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALnullStateFactory); ALeffectStateFactory *ALnullStateFactory_getFactory(void) { static ALnullStateFactory NullFactory = { { GET_VTABLE2(ALnullStateFactory, ALeffectStateFactory) } }; - return STATIC_CAST(ALeffectStateFactory, &NullFactory); } diff --git a/libs/openal-soft/Alc/effects/reverb.c b/libs/openal-soft/Alc/effects/reverb.c index e1013309..867abbcc 100644 --- a/libs/openal-soft/Alc/effects/reverb.c +++ b/libs/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 @@ -30,159 +30,440 @@ #include "alEffect.h" #include "alFilter.h" #include "alError.h" - +#include "mixer_defs.h" /* This is the maximum number of samples processed for each inner loop * iteration. */ #define MAX_UPDATE_SAMPLES 256 -typedef struct DelayLine +/* 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 + +#ifdef __GNUC__ +#define UNEXPECTED(x) __builtin_expect((bool)(x), 0) +#else +#define UNEXPECTED(x) (x) +#endif + +static MixerFunc MixSamples = Mix_C; +static RowMixerFunc MixRowSamples = MixRow_C; + +static alonce_flag mixfunc_inited = AL_ONCE_FLAG_INIT; +static void init_mixfunc(void) { - // 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; + MixSamples = SelectMixer(); + MixRowSamples = SelectRowMixer(); +} + +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)[4]; +} DelayLineI; + +typedef struct VecAllpass { + DelayLineI Delay; + ALsizei Offset[4][2]; +} VecAllpass; 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 - ALfilterState LpFilter; - ALfilterState HpFilter; // EAX only + /* Master effect filters */ + struct { + ALfilterState Lp; + ALfilterState Hp; /* EAX only */ + } Filter[4]; + + /* Core delay line (early reflections and late reverb tap from this). */ + DelayLineI Delay; + + /* Tap points for early reflection delay. */ + ALsizei EarlyDelayTap[4][2]; + ALfloat EarlyDelayCoeff[4]; + + /* Tap points for late reverb feed and delay. */ + ALsizei LateFeedTap; + ALsizei LateDelayTap[4][2]; + + /* The feed-back and feed-forward all-pass coefficient. */ + ALfloat ApFeedCoeff; + + /* Coefficients for the all-pass and line scattering matrices. */ + ALfloat MixX; + ALfloat MixY; struct { - // Modulator delay line. - DelayLine Delay; + /* 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; - // The vibrato time is tracked with an index over a modulus-wrapped - // range (in samples). + /* An echo line is used to complete the second half of the early + * reflections. + */ + DelayLineI Delay; + ALsizei Offset[4][2]; + ALfloat Coeff[4]; + + /* The gain for each output channel based on 3D panning. */ + ALfloat CurrentGain[4][MAX_OUTPUT_CHANNELS]; + ALfloat PanGain[4][MAX_OUTPUT_CHANNELS]; + } Early; + + struct { + /* 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. + /* The depth of frequency change (also in samples) and its filter. */ ALfloat Depth; ALfloat Coeff; ALfloat Filter; - } Mod; - - // Initial effect delay. - DelayLine Delay; - // The tap points for the initial delay. First tap goes to early - // reflections, the last to late reverb. - ALuint DelayTap[2]; + } Mod; /* EAX only */ struct { - // Output gain for early reflections. - ALfloat Gain; + /* Attenuation to compensate for the modal density and decay rate of + * the late lines. + */ + ALfloat DensityGain; - // Early reflections are done with 4 delay lines. - ALfloat Coeff[4]; - DelayLine Delay[4]; - ALuint Offset[4]; + /* A recursive delay line is used fill in the reverb tail. */ + DelayLineI Delay; + ALsizei Offset[4][2]; - // The gain for each output channel based on 3D panning (only for the - // EAX path). - ALfloat PanGain[4][MAX_OUTPUT_CHANNELS]; - } Early; + /* T60 decay filters are used to simulate absorption. */ + struct { + ALfloat LFCoeffs[3]; + ALfloat HFCoeffs[3]; + ALfloat MidCoeff; + /* The LF and HF filters keep a state of the last input and last + * output sample. + */ + ALfloat States[2][2]; + } Filters[4]; - // Decorrelator delay line. - DelayLine Decorrelator; - // There are actually 4 decorrelator taps, but the first occurs at the - // initial sample. - ALuint DecoTap[3]; + /* A Gerzon vector all-pass filter is used to simulate diffusion. */ + VecAllpass VecAp; - struct { - // Output gain for late reverb. - ALfloat Gain; - - // Attenuation to compensate for the modal density and decay rate of - // the late lines. - ALfloat DensityGain; - - // The feed-back and feed-forward all-pass coefficient. - ALfloat ApFeedCoeff; - - // Mixing matrix coefficient. - ALfloat MixCoeff; - - // Late reverb has 4 parallel all-pass filters. - ALfloat ApCoeff[4]; - DelayLine ApDelay[4]; - ALuint ApOffset[4]; - - // In addition to 4 cyclical delay lines. - ALfloat Coeff[4]; - DelayLine Delay[4]; - ALuint Offset[4]; - - // The cyclical delay lines are 1-pole low-pass filtered. - ALfloat LpCoeff[4]; - ALfloat LpSample[4]; - - // The gain for each output channel based on 3D panning (only for the - // EAX path). - ALfloat PanGain[4][MAX_OUTPUT_CHANNELS]; + /* 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; + /* Indicates the cross-fade point for delay line reads [0,FADE_SAMPLES]. */ + ALsizei FadeCount; - // Echo delay and all-pass lines. - DelayLine Delay; - DelayLine ApDelay; - - ALfloat Coeff; - ALfloat ApFeedCoeff; - ALfloat ApCoeff; - - ALuint Offset; - ALuint ApOffset; - - // The echo line is 1-pole low-pass filtered. - ALfloat LpCoeff; - ALfloat LpSample; - - // Echo mixing coefficient. - ALfloat MixCoeff; - } Echo; - - // The current read offset for all delay lines. - ALuint Offset; - - // The gain for each output channel (non-EAX path only; aliased from - // Late.PanGain) - ALfloat (*Gain)[MAX_OUTPUT_CHANNELS]; + /* The current write offset for all delay lines. */ + ALsizei Offset; /* Temporary storage used when processing. */ - ALfloat ReverbSamples[MAX_UPDATE_SAMPLES][4]; - ALfloat EarlySamples[MAX_UPDATE_SAMPLES][4]; + alignas(16) ALfloat AFormatSamples[4][MAX_UPDATE_SAMPLES]; + alignas(16) ALfloat ReverbSamples[4][MAX_UPDATE_SAMPLES]; + alignas(16) ALfloat EarlySamples[4][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, 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) +{ + 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(i = 0;i < 4;i++) + { + ALfilterState_clear(&state->Filter[i].Lp); + ALfilterState_clear(&state->Filter[i].Hp); + } + + state->Delay.Mask = 0; + state->Delay.Line = NULL; + + for(i = 0;i < 4;i++) + { + state->EarlyDelayTap[i][0] = 0; + state->EarlyDelayTap[i][1] = 0; + state->EarlyDelayCoeff[i] = 0.0f; + } + + state->LateFeedTap = 0; + + for(i = 0;i < 4;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 < 4;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->Mod.Index = 0; + state->Mod.Range = 1; + state->Mod.Depth = 0.0f; + state->Mod.Coeff = 0.0f; + state->Mod.Filter = 0.0f; + + state->Late.DensityGain = 0.0f; + + 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 < 4;i++) + { + state->Late.Offset[i][0] = 0; + state->Late.Offset[i][1] = 0; + + state->Late.VecAp.Offset[i][0] = 0; + state->Late.VecAp.Offset[i][1] = 0; + + for(j = 0;j < 3;j++) + { + state->Late.Filters[i].LFCoeffs[j] = 0.0f; + state->Late.Filters[i].HFCoeffs[j] = 0.0f; + } + state->Late.Filters[i].MidCoeff = 0.0f; + + state->Late.Filters[i].States[0][0] = 0.0f; + state->Late.Filters[i].States[0][1] = 0.0f; + state->Late.Filters[i].States[1][0] = 0.0f; + state->Late.Filters[i].States[1][1] = 0.0f; + } + + for(i = 0;i < 4;i++) + { + for(j = 0;j < MAX_OUTPUT_CHANNELS;j++) + { + 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->FadeCount = 0; + state->Offset = 0; +} + +static ALvoid ALreverbState_Destruct(ALreverbState *State) +{ + al_free(State->SampleBuffer); + State->SampleBuffer = NULL; + + ALeffectState_Destruct(STATIC_CAST(ALeffectState,State)); +} + +/* 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; + /* 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 */ +/* 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. +/* The all-pass and delay lines have a variable length dependent on the + * effect's density parameter. The resulting density multiplier is: + * + * multiplier = 1 + (density * LINE_MULTIPLIER) + * + * Thus the line multiplier below will result in a maximum density multiplier + * of 10. */ -static const ALfloat MODULATION_DEPTH_COEFF = 0.1f; +static const ALfloat LINE_MULTIPLIER = 9.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 5m (up to 50m with the density multiplier), we get + * the following taps: + */ +static const ALfloat EARLY_TAP_LENGTHS[4] = +{ + 0.000000e+0f, 1.010676e-3f, 2.126553e-3f, 3.358580e-3f +}; + +/* 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[4] = +{ + 4.854840e-4f, 5.360178e-4f, 5.918117e-4f, 6.534130e-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 5m, we get: + */ +static const ALfloat EARLY_LINE_LENGTHS[4] = +{ + 2.992520e-3f, 5.456575e-3f, 7.688329e-3f, 9.709681e-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[4] = +{ + 8.091400e-4f, 1.019453e-3f, 1.407968e-3f, 1.618280e-3f +}; + +/* 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 5m average room, we get: + */ +static const ALfloat LATE_LINE_LENGTHS[4] = +{ + 9.709681e-3f, 1.223343e-2f, 1.689561e-2f, 1.941936e-2f +}; + +/* This coefficient is used to define the sinus depth according to the + * modulation depth property. This value must be below half the shortest late + * line length (0.0097/2 = ~0.0048), otherwise with certain parameters (high + * mod time, low density) the downswing can sample before the input. + */ +static const ALfloat MODULATION_DEPTH_COEFF = 1.0f / 4096.0f; /* A filter is used to avoid the terrible distortion caused by changing * modulation time and/or depth. To be consistent across different sample @@ -192,469 +473,52 @@ static const ALfloat MODULATION_DEPTH_COEFF = 0.1f; static const ALfloat MODULATION_FILTER_COEFF = 0.048f; static const ALfloat MODULATION_FILTER_CONST = 100000.0f; -// When diffusion is above 0, an all-pass filter is used to take the edge off -// the echo effect. It uses the following line length (in seconds). -static const ALfloat ECHO_ALLPASS_LENGTH = 0.0133f; -// Input into the late reverb is decorrelated between four channels. Their -// timings are dependent on a fraction and multiplier. See the -// UpdateDecorrelator() routine for the calculations involved. -static const ALfloat DECO_FRACTION = 0.15f; -static const ALfloat DECO_MULTIPLIER = 2.0f; - -// All delay line lengths are specified in seconds. - -// The lengths of the early delay lines. -static const ALfloat EARLY_LINE_LENGTH[4] = +/* Prior to VS2013, MSVC lacks the round() family of functions. */ +#if defined(_MSC_VER) && _MSC_VER < 1800 +static inline long lroundf(float val) { - 0.0015f, 0.0045f, 0.0135f, 0.0405f -}; + if(val < 0.0) + return fastf2i(ceilf(val-0.5f)); + return fastf2i(floorf(val+0.5f)); +} +#endif -// The lengths of the late all-pass delay lines. -static const ALfloat ALLPASS_LINE_LENGTH[4] = + +/************************************** + * Device Update * + **************************************/ + +/* Given the allocated sample buffer, this function updates each delay line + * offset. + */ +static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLineI *Delay) { - 0.0151f, 0.0167f, 0.0183f, 0.0200f, -}; - -// The lengths of the late cyclical delay lines. -static const ALfloat LATE_LINE_LENGTH[4] = -{ - 0.0211f, 0.0311f, 0.0461f, 0.0680f -}; - -// The late cyclical delay lines have a variable length dependent on the -// effect's density parameter (inverted for some reason) and this multiplier. -static const ALfloat LATE_LINE_MULTIPLIER = 4.0f; - - -// Basic delay line input/output routines. -static inline ALfloat DelayLineOut(DelayLine *Delay, ALuint offset) -{ - return Delay->Line[offset&Delay->Mask]; + union { + ALfloat *f; + ALfloat (*f4)[4]; + } u; + u.f = &sampleBuffer[(ptrdiff_t)Delay->Line * 4]; + Delay->Line = u.f4; } -static inline ALvoid DelayLineIn(DelayLine *Delay, ALuint offset, ALfloat in) -{ - Delay->Line[offset&Delay->Mask] = in; -} - -// Given an input sample, this function produces modulation for the late -// reverb. -static inline ALfloat EAXModulation(ALreverbState *State, ALuint offset, ALfloat in) -{ - ALfloat sinus, frac, fdelay; - ALfloat out0, out1; - ALuint delay; - - // 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 * State->Mod.Index / State->Mod.Range); - - // Step the modulation index forward, keeping it bound to its range. - State->Mod.Index = (State->Mod.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. - State->Mod.Filter = lerp(State->Mod.Filter, State->Mod.Depth, - State->Mod.Coeff); - - // Calculate the read offset and fraction between it and the next sample. - frac = modff(State->Mod.Filter*sinus + 1.0f, &fdelay); - delay = fastf2u(fdelay); - - // Get the two samples crossed by the offset, and feed the delay line - // with the next input sample. - out0 = DelayLineOut(&State->Mod.Delay, offset - delay); - out1 = DelayLineOut(&State->Mod.Delay, offset - delay - 1); - DelayLineIn(&State->Mod.Delay, offset, in); - - // The output is obtained by linearly interpolating the two samples that - // were acquired above. - return lerp(out0, out1, frac); -} - -// Given some input sample, this function produces four-channel outputs for the -// early reflections. -static inline ALvoid EarlyReflection(ALreverbState *State, ALuint todo, ALfloat (*restrict out)[4]) -{ - ALfloat d[4], v, f[4]; - ALuint i; - - for(i = 0;i < todo;i++) - { - ALuint offset = State->Offset+i; - - // Obtain the decayed results of each early delay line. - d[0] = DelayLineOut(&State->Early.Delay[0], offset-State->Early.Offset[0]) * State->Early.Coeff[0]; - d[1] = DelayLineOut(&State->Early.Delay[1], offset-State->Early.Offset[1]) * State->Early.Coeff[1]; - d[2] = DelayLineOut(&State->Early.Delay[2], offset-State->Early.Offset[2]) * State->Early.Coeff[2]; - d[3] = DelayLineOut(&State->Early.Delay[3], offset-State->Early.Offset[3]) * State->Early.Coeff[3]; - - /* The following uses a lossless scattering junction from waveguide - * theory. It actually amounts to a householder mixing matrix, which - * will produce a maximally diffuse response, and means this can - * probably be considered a simple feed-back delay network (FDN). - * N - * --- - * \ - * v = 2/N / d_i - * --- - * i=1 - */ - v = (d[0] + d[1] + d[2] + d[3]) * 0.5f; - // The junction is loaded with the input here. - v += DelayLineOut(&State->Delay, offset-State->DelayTap[0]); - - // Calculate the feed values for the delay lines. - f[0] = v - d[0]; - f[1] = v - d[1]; - f[2] = v - d[2]; - f[3] = v - d[3]; - - // Re-feed the delay lines. - DelayLineIn(&State->Early.Delay[0], offset, f[0]); - DelayLineIn(&State->Early.Delay[1], offset, f[1]); - DelayLineIn(&State->Early.Delay[2], offset, f[2]); - DelayLineIn(&State->Early.Delay[3], offset, f[3]); - - // Output the results of the junction for all four channels. - out[i][0] = State->Early.Gain * f[0]; - out[i][1] = State->Early.Gain * f[1]; - out[i][2] = State->Early.Gain * f[2]; - out[i][3] = State->Early.Gain * f[3]; - } -} - -// Basic attenuated all-pass input/output routine. -static inline ALfloat AllpassInOut(DelayLine *Delay, ALuint outOffset, ALuint inOffset, ALfloat in, ALfloat feedCoeff, ALfloat coeff) -{ - ALfloat out, feed; - - out = DelayLineOut(Delay, outOffset); - feed = feedCoeff * in; - DelayLineIn(Delay, inOffset, (feedCoeff * (out - feed)) + in); - - // The time-based attenuation is only applied to the delay output to - // keep it from affecting the feed-back path (which is already controlled - // by the all-pass feed coefficient). - return (coeff * out) - feed; -} - -// All-pass input/output routine for late reverb. -static inline ALfloat LateAllPassInOut(ALreverbState *State, ALuint offset, ALuint index, ALfloat in) -{ - return AllpassInOut(&State->Late.ApDelay[index], - offset - State->Late.ApOffset[index], - offset, in, State->Late.ApFeedCoeff, - State->Late.ApCoeff[index]); -} - -// Low-pass filter input/output routine for late reverb. -static inline ALfloat LateLowPassInOut(ALreverbState *State, ALuint index, ALfloat in) -{ - in = lerp(in, State->Late.LpSample[index], State->Late.LpCoeff[index]); - State->Late.LpSample[index] = in; - return in; -} - -// Given four decorrelated input samples, this function produces four-channel -// output for the late reverb. -static inline ALvoid LateReverb(ALreverbState *State, ALuint todo, ALfloat (*restrict out)[4]) -{ - ALfloat d[4], f[4]; - ALuint i; - - for(i = 0;i < todo;i++) - { - ALuint offset = State->Offset+i; - - f[0] = DelayLineOut(&State->Decorrelator, offset); - f[1] = DelayLineOut(&State->Decorrelator, offset-State->DecoTap[0]); - f[2] = DelayLineOut(&State->Decorrelator, offset-State->DecoTap[1]); - f[3] = DelayLineOut(&State->Decorrelator, offset-State->DecoTap[2]); - - // Obtain the decayed results of the cyclical delay lines, and add the - // corresponding input channels. 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 1 to 3 to 2 and - // back to 0. - d[0] = LateLowPassInOut(State, 2, f[2]); - d[1] = LateLowPassInOut(State, 0, f[0]); - d[2] = LateLowPassInOut(State, 3, f[3]); - d[3] = LateLowPassInOut(State, 1, f[1]); - - // To help increase diffusion, run each line through an all-pass filter. - // When there is no diffusion, the shortest all-pass filter will feed - // the shortest delay line. - d[0] = LateAllPassInOut(State, 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] )); - - // Output the results of the matrix for all four channels, attenuated by - // the late reverb gain (which is attenuated by the 'x' mix coefficient). - // Mix early reflections and late reverb. - out[i][0] += State->Late.Gain * f[0]; - out[i][1] += State->Late.Gain * f[1]; - out[i][2] += State->Late.Gain * f[2]; - out[i][3] += State->Late.Gain * f[3]; - - // 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]); - } -} - -// Given an input sample, this function mixes echo into the four-channel late -// reverb. -static inline ALvoid EAXEcho(ALreverbState *State, ALuint todo, ALfloat (*restrict late)[4]) -{ - ALfloat out, feed; - ALuint i; - - for(i = 0;i < todo;i++) - { - ALuint offset = State->Offset+i; - - // Get the latest attenuated echo sample for output. - feed = DelayLineOut(&State->Echo.Delay, offset-State->Echo.Offset) * - State->Echo.Coeff; - - // Mix the output into the late reverb channels. - out = State->Echo.MixCoeff * feed; - late[i][0] += out; - late[i][1] += out; - late[i][2] += out; - late[i][3] += out; - - // Mix the energy-attenuated input with the output and pass it through - // the echo low-pass filter. - feed += DelayLineOut(&State->Delay, offset-State->DelayTap[1]) * - State->Echo.DensityGain; - feed = lerp(feed, State->Echo.LpSample, State->Echo.LpCoeff); - State->Echo.LpSample = feed; - - // Then the echo all-pass filter. - feed = AllpassInOut(&State->Echo.ApDelay, 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, offset, feed); - } -} - -// Perform the non-EAX reverb pass on a given input sample, resulting in -// four-channel output. -static inline ALvoid VerbPass(ALreverbState *State, ALuint todo, const ALfloat *in, ALfloat (*restrict out)[4]) -{ - ALuint i; - - // Low-pass filter the incoming samples. - for(i = 0;i < todo;i++) - DelayLineIn(&State->Delay, State->Offset+i, - ALfilterState_processSingle(&State->LpFilter, in[i]) - ); - - // Calculate the early reflection from the first delay tap. - EarlyReflection(State, todo, out); - - // Feed the decorrelator from the energy-attenuated output of the second - // delay tap. - for(i = 0;i < todo;i++) - { - ALuint offset = State->Offset+i; - ALfloat sample = DelayLineOut(&State->Delay, offset - State->DelayTap[1]) * - State->Late.DensityGain; - DelayLineIn(&State->Decorrelator, offset, sample); - } - - // Calculate the late reverb from the decorrelator taps. - LateReverb(State, todo, out); - - // Step all delays forward one sample. - State->Offset += todo; -} - -// Perform the EAX reverb pass on a given input sample, resulting in four- -// channel output. -static inline ALvoid EAXVerbPass(ALreverbState *State, ALuint todo, const ALfloat *input, ALfloat (*restrict early)[4], ALfloat (*restrict late)[4]) -{ - ALuint i; - - // Band-pass and modulate the incoming samples. - for(i = 0;i < todo;i++) - { - ALfloat sample = input[i]; - sample = ALfilterState_processSingle(&State->LpFilter, sample); - sample = ALfilterState_processSingle(&State->HpFilter, sample); - - // Perform any modulation on the input. - sample = EAXModulation(State, State->Offset+i, sample); - - // Feed the initial delay line. - DelayLineIn(&State->Delay, State->Offset+i, sample); - } - - // Calculate the early reflection from the first delay tap. - EarlyReflection(State, todo, early); - - // Feed the decorrelator from the energy-attenuated output of the second - // delay tap. - for(i = 0;i < todo;i++) - { - ALuint offset = State->Offset+i; - ALfloat sample = DelayLineOut(&State->Delay, offset - State->DelayTap[1]) * - State->Late.DensityGain; - DelayLineIn(&State->Decorrelator, offset, sample); - } - - // Calculate the late reverb from the decorrelator taps. - memset(late, 0, sizeof(*late)*todo); - LateReverb(State, todo, late); - - // Calculate and mix in any echo. - EAXEcho(State, todo, late); - - // Step all delays forward. - State->Offset += todo; -} - -static ALvoid ALreverbState_processStandard(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) -{ - ALfloat (*restrict out)[4] = State->ReverbSamples; - ALuint index, c, i, l; - - /* Process reverb for these samples. */ - for(index = 0;index < SamplesToDo;) - { - ALuint todo = minu(SamplesToDo-index, MAX_UPDATE_SAMPLES); - - VerbPass(State, todo, &SamplesIn[index], out); - - for(l = 0;l < 4;l++) - { - for(c = 0;c < NumChannels;c++) - { - ALfloat gain = State->Gain[l][c]; - if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) - continue; - for(i = 0;i < todo;i++) - SamplesOut[c][index+i] += gain*out[i][l]; - } - } - - index += todo; - } -} - -static ALvoid ALreverbState_processEax(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) -{ - ALfloat (*restrict early)[4] = State->EarlySamples; - ALfloat (*restrict late)[4] = State->ReverbSamples; - ALuint index, c, i, l; - ALfloat gain; - - /* Process reverb for these samples. */ - for(index = 0;index < SamplesToDo;) - { - ALuint todo = minu(SamplesToDo-index, MAX_UPDATE_SAMPLES); - - EAXVerbPass(State, todo, &SamplesIn[index], early, late); - - for(l = 0;l < 4;l++) - { - for(c = 0;c < NumChannels;c++) - { - gain = State->Early.PanGain[l][c]; - if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) - { - for(i = 0;i < todo;i++) - SamplesOut[c][index+i] += gain*early[i][l]; - } - gain = State->Late.PanGain[l][c]; - if(fabsf(gain) > GAIN_SILENCE_THRESHOLD) - { - for(i = 0;i < todo;i++) - SamplesOut[c][index+i] += gain*late[i][l]; - } - } - } - - index += todo; - } -} - -static ALvoid ALreverbState_process(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) -{ - if(State->IsEax) - ALreverbState_processEax(State, SamplesToDo, SamplesIn, SamplesOut, NumChannels); - else - ALreverbState_processStandard(State, SamplesToDo, SamplesIn, SamplesOut, NumChannels); -} - -// Given the allocated sample buffer, this function updates each delay line -// offset. -static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLine *Delay) -{ - Delay->Line = &sampleBuffer[(ptrdiff_t)Delay->Line]; -} - -// 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) +/* 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 = fastf2i(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(*)[4])offset; + + /* Return the sample count for accumulation. */ return samples; } @@ -662,144 +526,138 @@ 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; - ALfloat *newBuffer = NULL; + 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); - totalSamples += CalcLineLength(length, totalSamples, frequency, 1, - &State->Mod.Delay); + multiplier = 1.0f + LINE_MULTIPLIER; - // The initial delay is the sum of the reflections and late reverb - // delays. This must include space for storing a loop update to feed the - // early reflections, decorrelator, and echo. + /* 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; - totalSamples += CalcLineLength(length, totalSamples, frequency, - MAX_UPDATE_SAMPLES, &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 decorrelator line is calculated from the lowest reverb density (a - // parameter value of 1). This must include space for storing a loop update - // to feed the late reverb. - length = (DECO_FRACTION * DECO_MULTIPLIER * DECO_MULTIPLIER) * - LATE_LINE_LENGTH[0] * (1.0f + LATE_LINE_MULTIPLIER); + EARLY_TAP_LENGTHS[3]*multiplier + + AL_EAXREVERB_MAX_LATE_REVERB_DELAY + + (LATE_LINE_LENGTHS[3] - LATE_LINE_LENGTHS[0])*0.25f*multiplier; totalSamples += CalcLineLength(length, totalSamples, frequency, MAX_UPDATE_SAMPLES, - &State->Decorrelator); + &State->Delay); - // The late all-pass lines. - for(index = 0;index < 4;index++) - totalSamples += CalcLineLength(ALLPASS_LINE_LENGTH[index], totalSamples, - frequency, 0, &State->Late.ApDelay[index]); + /* The early vector all-pass line. */ + length = EARLY_ALLPASS_LENGTHS[3] * multiplier; + totalSamples += CalcLineLength(length, totalSamples, frequency, 0, + &State->Early.VecAp.Delay); - // 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 early reflection line. */ + length = EARLY_LINE_LENGTHS[3] * multiplier; + totalSamples += CalcLineLength(length, totalSamples, frequency, 0, + &State->Early.Delay); - // The echo all-pass and delay lines. - totalSamples += CalcLineLength(ECHO_ALLPASS_LENGTH, totalSamples, - frequency, 0, &State->Echo.ApDelay); - totalSamples += CalcLineLength(AL_EAXREVERB_MAX_ECHO_TIME, totalSamples, - frequency, 0, &State->Echo.Delay); + /* The late vector all-pass line. */ + length = LATE_ALLPASS_LENGTHS[3] * 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, and includes the maximum + * modulation-related delay. The modulator's delay is calculated from the + * maximum modulation time and depth coefficient, and halved for the low- + * to-high frequency swing. + */ + length = maxf(AL_EAXREVERB_MAX_ECHO_TIME, LATE_LINE_LENGTHS[3]*multiplier) + + AL_EAXREVERB_MAX_MODULATION_TIME*MODULATION_DEPTH_COEFF/2.0f; + totalSamples += CalcLineLength(length, totalSamples, frequency, 0, + &State->Late.Delay); if(totalSamples != State->TotalSamples) { - TRACE("New reverb buffer length: %u samples (%f sec)\n", totalSamples, totalSamples/(float)frequency); - newBuffer = realloc(State->SampleBuffer, sizeof(ALfloat) * totalSamples); - if(newBuffer == NULL) - return AL_FALSE; + ALfloat *newBuffer; + + TRACE("New reverb buffer length: %ux4 samples\n", totalSamples); + newBuffer = al_calloc(16, sizeof(ALfloat[4]) * totalSamples); + if(!newBuffer) return AL_FALSE; + + al_free(State->SampleBuffer); State->SampleBuffer = newBuffer; 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); - RealizeLineOffset(State->SampleBuffer, &State->Decorrelator); - for(index = 0;index < 4;index++) - { - RealizeLineOffset(State->SampleBuffer, &State->Early.Delay[index]); - RealizeLineOffset(State->SampleBuffer, &State->Late.ApDelay[index]); - RealizeLineOffset(State->SampleBuffer, &State->Late.Delay[index]); - } - RealizeLineOffset(State->SampleBuffer, &State->Mod.Delay); - RealizeLineOffset(State->SampleBuffer, &State->Echo.ApDelay); - RealizeLineOffset(State->SampleBuffer, &State->Echo.Delay); + 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); - // 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, i; + 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. + /* Calculate the modulation filter coefficient. Notice that the exponent + * is calculated given the current sample rate. This ensures that the + * resulting filter response over time is consistent across all sample + * rates. + */ State->Mod.Coeff = powf(MODULATION_FILTER_COEFF, MODULATION_FILTER_CONST / frequency); - // The early reflection and late all-pass filter line lengths are static, - // so their offsets only need to be calculated once. - for(index = 0;index < 4;index++) - { - State->Early.Offset[index] = fastf2u(EARLY_LINE_LENGTH[index] * - frequency); - State->Late.ApOffset[index] = fastf2u(ALLPASS_LINE_LENGTH[index] * - frequency); - } + multiplier = 1.0f + LINE_MULTIPLIER; - // 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. */ + for(i = 0;i < 4;i++) + State->LateFeedTap = fastf2i((AL_EAXREVERB_MAX_REFLECTIONS_DELAY + + EARLY_TAP_LENGTHS[3]*multiplier) * + frequency); return AL_TRUE; } -// Calculate a decay coefficient given the length of each cycle and the time -// until the decay reaches -60 dB. -static inline ALfloat CalcDecayCoeff(ALfloat length, ALfloat decayTime) +/************************************** + * 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(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 @@ -809,32 +667,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) { ALfloat limitRatio; @@ -846,355 +706,1188 @@ static ALfloat CalcLimitedHfRatio(ALfloat hfRatio, ALfloat airAbsorptionGainHF, 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. */ + * ratio. Also need to limit the result to a minimum of 0.1, just like + * the HF ratio parameter. + */ return clampf(limitRatio, 0.1f, hfRatio); } -// Calculate the coefficient for a HF (and eventually LF) decay damping -// filter. -static inline ALfloat CalcDampingCoeff(ALfloat hfRatio, ALfloat length, ALfloat decayTime, ALfloat decayCoeff, ALfloat cw) +/* 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; + coeffs[0] = 1.0f; + coeffs[1] = 0.0f; + coeffs[2] = 0.0f; - // Damping is done with a 1-pole filter, so g needs to be squared. - g *= g; - if(g < 0.9999f) /* 1-epsilon */ - { - /* Be careful with gains < 0.001, as that causes the coefficient - * head towards 1, which will flatten the signal. */ - g = maxf(g, 0.001f); - coeff = (1 - g*cw - sqrtf(2*g*(1-cw) - g*g*(1 - cw*cw))) / - (1 - g); - } - - // Very low decay times will produce minimal output, so apply an - // upper bound to the coefficient. - coeff = minf(coeff, 0.98f); + return; } - 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]) +{ + ALfloat g, g2, cw, a; + + if(gain >= 1.0f) + { + coeffs[0] = 1.0f; + coeffs[1] = 0.0f; + coeffs[2] = 0.0f; + + return; + } + + /* 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; +} + +/* 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 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) and a general + * gain (7th coefficient) 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 *midcoeff) +{ + ALfloat lfGain = CalcDecayCoeff(length, lfDecayTime); + ALfloat mfGain = CalcDecayCoeff(length, mfDecayTime); + ALfloat hfGain = CalcDecayCoeff(length, hfDecayTime); + + if(lfGain < mfGain) + { + if(mfGain < hfGain) + { + CalcLowShelfCoeffs(mfGain / hfGain, hfW, lfcoeffs); + CalcHighpassCoeffs(lfGain / mfGain, lfW, hfcoeffs); + *midcoeff = hfGain; + } + else if(mfGain > hfGain) + { + CalcHighpassCoeffs(lfGain / mfGain, lfW, lfcoeffs); + CalcLowpassCoeffs(hfGain / mfGain, hfW, hfcoeffs); + *midcoeff = mfGain; + } + else + { + lfcoeffs[0] = 1.0f; + lfcoeffs[1] = 0.0f; + lfcoeffs[2] = 0.0f; + CalcHighpassCoeffs(lfGain / mfGain, lfW, hfcoeffs); + *midcoeff = mfGain; + } + } + else if(lfGain > mfGain) + { + if(mfGain < hfGain) + { + ALfloat hg = mfGain / lfGain; + ALfloat lg = mfGain / hfGain; + + CalcHighShelfCoeffs(hg, lfW, lfcoeffs); + CalcLowShelfCoeffs(lg, hfW, hfcoeffs); + *midcoeff = maxf(lfGain, hfGain) / maxf(hg, lg); + } + else if(mfGain > hfGain) + { + CalcHighShelfCoeffs(mfGain / lfGain, lfW, lfcoeffs); + CalcLowpassCoeffs(hfGain / mfGain, hfW, hfcoeffs); + *midcoeff = lfGain; + } + else + { + lfcoeffs[0] = 1.0f; + lfcoeffs[1] = 0.0f; + lfcoeffs[2] = 0.0f; + CalcHighShelfCoeffs(mfGain / lfGain, lfW, hfcoeffs); + *midcoeff = lfGain; + } + } + else + { + lfcoeffs[0] = 1.0f; + lfcoeffs[1] = 0.0f; + lfcoeffs[2] = 0.0f; + + if(mfGain < hfGain) + { + CalcLowShelfCoeffs(mfGain / hfGain, hfW, hfcoeffs); + *midcoeff = hfGain; + } + else if(mfGain > hfGain) + { + CalcLowpassCoeffs(hfGain / mfGain, hfW, hfcoeffs); + *midcoeff = mfGain; + } + else + { + hfcoeffs[3] = 1.0f; + hfcoeffs[4] = 0.0f; + hfcoeffs[5] = 0.0f; + *midcoeff = mfGain; + } + } +} + +/* 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(const ALfloat modTime, const ALfloat modDepth, + const ALuint frequency, ALreverbState *State) { ALuint range; /* Modulation is calculated in two parts. * - * The modulation time effects the sinus applied to the change in - * frequency. An index out of the current time range (both in samples) - * is incremented each sample. The range is bound to a reasonable - * minimum (1 sample) and when the timing changes, the index is rescaled - * to the new range (to keep the sinus consistent). + * The modulation time effects the speed of the sinus. An index out of the + * current range (both in samples) is incremented each sample, so a longer + * time implies a larger range. 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); + range = maxi(fastf2i(modTime*frequency), 1); State->Mod.Index = (ALuint)(State->Mod.Index * (ALuint64)range / State->Mod.Range); State->Mod.Range = range; - /* The modulation depth effects the amount of frequency change over the - * range of the sinus. It needs to be scaled by the modulation time so - * that a given depth produces a consistent change in frequency over all - * ranges of time. Since the depth is applied to a sinus value, it needs - * to be halfed once for the sinus range and again for the sinus swing + /* The modulation depth effects the scale of the sinus, which changes how + * much extra delay is added to the delay line. This delay changing over + * time changes the pitch, creating the modulation effect. The scale needs + * to be multiplied by the modulation time so that a given depth produces a + * consistent shift in frequency over all ranges of time. Since the depth + * is applied to a sinus value, it needs to be halved 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; + State->Mod.Depth = modDepth * MODULATION_DEPTH_COEFF * modTime / 2.0f * + frequency; } -// Update the offsets for the initial effect delay line. -static ALvoid UpdateDelayLine(ALfloat earlyDelay, ALfloat lateDelay, ALuint frequency, ALreverbState *State) +/* 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) { - // Calculate the initial delay taps. - State->DelayTap[0] = fastf2u(earlyDelay * frequency); - State->DelayTap[1] = fastf2u((earlyDelay + lateDelay) * frequency); -} + ALfloat multiplier, length; + ALuint i; -// Update the early reflections gain and line coefficients. -static ALvoid UpdateEarlyLines(ALfloat reverbGain, ALfloat earlyGain, ALfloat lateDelay, ALreverbState *State) -{ - ALuint index; + multiplier = 1.0f + density*LINE_MULTIPLIER; - // Calculate the early reflections gain (from the master effect gain, and - // reflections gain parameters) with a constant attenuation of 0.5. - State->Early.Gain = 0.5f * reverbGain * earlyGain; - - // Calculate the gain (coefficient) for each early delay line using the - // late delay time. This expands the early reflections to the start of - // the late reverb. - for(index = 0;index < 4;index++) - State->Early.Coeff[index] = CalcDecayCoeff(EARLY_LINE_LENGTH[index], - lateDelay); -} - -// Update the offsets for the decorrelator line. -static ALvoid UpdateDecorrelator(ALfloat density, ALuint frequency, ALreverbState *State) -{ - ALuint index; - ALfloat length; - - /* The late reverb inputs are decorrelated to smooth the reverb tail and - * reduce harsh echos. The first tap occurs immediately, while the - * remaining taps are delayed by multiples of a fraction of the smallest - * cyclical delay time. + /* 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. * - * offset[index] = (FRACTION (MULTIPLIER^index)) smallest_delay + * 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(index = 0;index < 3;index++) + for(i = 0;i < 4;i++) { - length = (DECO_FRACTION * powf(DECO_MULTIPLIER, (ALfloat)index)) * - LATE_LINE_LENGTH[0] * (1.0f + (density * LATE_LINE_MULTIPLIER)); - State->DecoTap[index] = fastf2u(length * frequency); + length = earlyDelay + EARLY_TAP_LENGTHS[i]*multiplier; + State->EarlyDelayTap[i][1] = fastf2i(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 + fastf2i(length * frequency); } } -// Update the late reverb gains, line lengths, and line coefficients. -static ALvoid UpdateLateLines(ALfloat reverbGain, ALfloat lateGain, ALfloat xMix, ALfloat density, ALfloat decayTime, ALfloat diffusion, ALfloat echoDepth, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALreverbState *State) +/* Update the early reflection line lengths and gain coefficients. */ +static ALvoid UpdateEarlyLines(const ALfloat density, const ALfloat decayTime, const ALuint frequency, ALreverbState *State) { - ALfloat length; - ALuint index; + ALfloat multiplier, length; + ALsizei i; - /* Calculate the late reverb gain (from the master effect gain, and late - * reverb gain parameters). Since the output is tapped prior to the - * application of the next delay line coefficients, this gain needs to be - * attenuated by the 'x' mixing matrix coefficient as well. 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 = reverbGain * lateGain * xMix * - (1.0f - (echoDepth*0.5f*(1.0f - diffusion))); + multiplier = 1.0f + density*LINE_MULTIPLIER; + + for(i = 0;i < 4;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. */ + State->Early.VecAp.Offset[i][1] = fastf2i(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. */ + State->Early.Offset[i][1] = fastf2i(length * frequency); + + /* Calculate the gain (coefficient) for each line. */ + State->Early.Coeff[i] = CalcDecayCoeff(length, decayTime); + } +} + +/* 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, ALreverbState *State) +{ + 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); + multiplier = 1.0f + density*LINE_MULTIPLIER; + 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. + */ + bandWeights[0] = lfW; + bandWeights[1] = hfW - lfW; + bandWeights[2] = F_TAU - hfW; State->Late.DensityGain = CalcDensityGain( - CalcDecayCoeff(length, decayTime) + 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 < 4;i++) { - // Calculate the gain (coefficient) for each all-pass line. - State->Late.ApCoeff[index] = CalcDecayCoeff( - ALLPASS_LINE_LENGTH[index], decayTime - ); + /* Calculate the length (in seconds) of each 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. */ + State->Late.VecAp.Offset[i][1] = fastf2i(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. */ + State->Late.Offset[i][1] = fastf2i(length * frequency); - // Calculate the damping coefficient for each low-pass filter. - State->Late.LpCoeff[index] = 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, State->Late.Filters[i].LFCoeffs, + State->Late.Filters[i].HFCoeffs, + &State->Late.Filters[i].MidCoeff); } } -// Update the echo gain, line offset, line coefficients, and mixing -// coefficients. -static ALvoid UpdateEchoLine(ALfloat reverbGain, ALfloat lateGain, ALfloat echoTime, ALfloat decayTime, ALfloat diffusion, ALfloat echoDepth, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALreverbState *State) +/* 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. + */ +static aluMatrixf GetTransformFromVector(const ALfloat *vec) { - // Update the offset and coefficient for the echo delay line. - State->Echo.Offset = fastf2u(echoTime * frequency); + aluMatrixf zfocus, xrot, yrot; + aluMatrixf tmp1, tmp2; + ALfloat length; + ALfloat sa, a; - // Calculate the decay coefficient for the echo line. - State->Echo.Coeff = CalcDecayCoeff(echoTime, decayTime); + length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]); - // Calculate the energy-based attenuation coefficient for the echo delay - // line. - State->Echo.DensityGain = CalcDensityGain(State->Echo.Coeff); - - // Calculate the echo all-pass feed coefficient. - State->Echo.ApFeedCoeff = 0.5f * powf(diffusion, 2.0f); - - // Calculate the echo all-pass attenuation coefficient. - State->Echo.ApCoeff = CalcDecayCoeff(ECHO_ALLPASS_LENGTH, decayTime); - - // Calculate the damping coefficient for each low-pass filter. - State->Echo.LpCoeff = CalcDampingCoeff(hfRatio, echoTime, decayTime, - State->Echo.Coeff, cw); - - /* Calculate the echo mixing coefficients. The first is applied to the - * echo itself. The second is used to attenuate the late reverb when - * echo depth is high and diffusion is low, so the echo is slightly - * stronger than the decorrelated echos in the reverb tail. + /* Define a Z-focus (X in Ambisonics) transform, given the panning vector + * length. */ - State->Echo.MixCoeff = reverbGain * lateGain * echoDepth; + 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) + ); + + /* 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; } -// Update the early and late 3D panning gains. -static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, ALfloat Gain, 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) { - static const ALfloat EarlyPanAngles[4] = { - DEG2RAD(0.0f), DEG2RAD(-90.0f), DEG2RAD(90.0f), DEG2RAD(180.0f) - }, LatePanAngles[4] = { - DEG2RAD(45.0f), DEG2RAD(-45.0f), DEG2RAD(135.0f), DEG2RAD(-135.0f) - }; - ALfloat length, ev, az; - ALuint i; + aluMatrixf transform, rot; + ALsizei i; - length = sqrtf(ReflectionsPan[0]*ReflectionsPan[0] + ReflectionsPan[1]*ReflectionsPan[1] + ReflectionsPan[2]*ReflectionsPan[2]); - if(!(length > FLT_EPSILON)) - { - for(i = 0;i < 4;i++) - ComputeAngleGains(Device, EarlyPanAngles[i], 0.0f, Gain, State->Early.PanGain[i]); - } - else - { - ev = asinf(clampf(ReflectionsPan[1]/length, -1.0f, 1.0f)); - az = atan2f(ReflectionsPan[0], ReflectionsPan[2]); + STATIC_CAST(ALeffectState,State)->OutBuffer = Device->FOAOut.Buffer; + STATIC_CAST(ALeffectState,State)->OutChannels = Device->FOAOut.NumChannels; - length = minf(length, 1.0f); - for(i = 0;i < 4;i++) - { - /* This is essentially just a lerp, but takes the shortest path - * with respect to circular wrapping. e.g. - * -135 -> +/-180 -> +135 - * instead of - * -135 -> 0 -> +135 */ - float offset, naz, nev; - naz = EarlyPanAngles[i] + (modff((az-EarlyPanAngles[i])*length/F_TAU + 1.5f, &offset)-0.5f)*F_TAU; - nev = (modff((ev )*length/F_TAU + 1.5f, &offset)-0.5f)*F_TAU; - ComputeAngleGains(Device, naz, nev, Gain, State->Early.PanGain[i]); - } - } + /* 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. + */ + rot = GetTransformFromVector(ReflectionsPan); + 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]); - length = sqrtf(LateReverbPan[0]*LateReverbPan[0] + LateReverbPan[1]*LateReverbPan[1] + LateReverbPan[2]*LateReverbPan[2]); - if(!(length > FLT_EPSILON)) - { - for(i = 0;i < 4;i++) - ComputeAngleGains(Device, LatePanAngles[i], 0.0f, Gain, State->Late.PanGain[i]); - } - else - { - ev = asinf(clampf(LateReverbPan[1]/length, -1.0f, 1.0f)); - az = atan2f(LateReverbPan[0], LateReverbPan[2]); - - length = minf(length, 1.0f); - for(i = 0;i < 4;i++) - { - float offset, naz, nev; - naz = LatePanAngles[i] + (modff((az-LatePanAngles[i])*length/F_TAU + 1.5f, &offset)-0.5f)*F_TAU; - nev = (modff((ev )*length/F_TAU + 1.5f, &offset)-0.5f)*F_TAU; - ComputeAngleGains(Device, naz, nev, Gain, State->Late.PanGain[i]); - } - } + rot = GetTransformFromVector(LateReverbPan); + 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]); +#undef MATRIX_MULT } -static ALvoid ALreverbState_update(ALreverbState *State, ALCdevice *Device, const ALeffectslot *Slot) +static ALvoid ALreverbState_update(ALreverbState *State, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props) { - const ALeffectProps *props = &Slot->EffectProps; ALuint frequency = Device->Frequency; - ALfloat lfscale, hfscale, hfRatio; - ALfloat gainlf, gainhf; - ALfloat cw, x, y; + ALfloat lfScale, hfScale, hfRatio; + ALfloat lfDecayTime, hfDecayTime; + ALfloat gain, gainlf, gainhf; + ALsizei i; - if(Slot->EffectType == AL_EFFECT_EAXREVERB && !EmulateEAXReverb) + if(Slot->Params.EffectType == AL_EFFECT_EAXREVERB && !EmulateEAXReverb) State->IsEax = AL_TRUE; - else if(Slot->EffectType == AL_EFFECT_REVERB || EmulateEAXReverb) + 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->LpFilter, 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->HpFilter, ALfilterType_LowShelf, - gainlf, lfscale, calc_rcpQ_from_slope(gainlf, 0.75f)); + /* Calculate the master filters */ + hfScale = 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); + ALfilterState_setParams(&State->Filter[0].Lp, ALfilterType_HighShelf, + gainhf, hfScale, calc_rcpQ_from_slope(gainhf, 1.0f)); + lfScale = props->Reverb.LFReference / frequency; + gainlf = maxf(props->Reverb.GainLF, 0.001f); + ALfilterState_setParams(&State->Filter[0].Hp, ALfilterType_LowShelf, + gainlf, lfScale, calc_rcpQ_from_slope(gainlf, 1.0f)); + for(i = 1;i < 4;i++) + { + ALfilterState_copyParams(&State->Filter[i].Lp, &State->Filter[0].Lp); + ALfilterState_copyParams(&State->Filter[i].Hp, &State->Filter[0].Hp); + } - // Update the modulator line. - UpdateModulator(props->Reverb.ModulationTime, props->Reverb.ModulationDepth, - frequency, State); - - // Update the initial effect delay. + /* Update the main effect delay and associated taps. */ UpdateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay, - frequency, State); + props->Reverb.Density, props->Reverb.DecayTime, frequency, + State); - // Update the early lines. - UpdateEarlyLines(props->Reverb.Gain, props->Reverb.ReflectionsGain, - props->Reverb.LateReverbDelay, State); + /* Calculate the all-pass feed-back/forward coefficient. */ + State->ApFeedCoeff = sqrtf(0.5f) * powf(props->Reverb.Diffusion, 2.0f); - // Update the decorrelator. - UpdateDecorrelator(props->Reverb.Density, frequency, State); + /* Update the early lines. */ + UpdateEarlyLines(props->Reverb.Density, props->Reverb.DecayTime, + frequency, State); - // 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; + /* 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. + /* 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); - cw = cosf(F_TAU * hfscale); - // Update the late lines. - UpdateLateLines(props->Reverb.Gain, props->Reverb.LateReverbGain, 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.Gain, props->Reverb.LateReverbGain, - props->Reverb.EchoTime, props->Reverb.DecayTime, - props->Reverb.Diffusion, props->Reverb.EchoDepth, - hfRatio, cw, frequency, State); + /* Update the modulator line. */ + UpdateModulator(props->Reverb.ModulationTime, props->Reverb.ModulationDepth, + frequency, State); - // Update early and late 3D panning. + /* Update the late lines. */ + UpdateLateLines(props->Reverb.Density, props->Reverb.Diffusion, + lfDecayTime, props->Reverb.DecayTime, hfDecayTime, + F_TAU * lfScale, F_TAU * hfScale, + props->Reverb.EchoTime, props->Reverb.EchoDepth, + frequency, State); + + /* Update early and late 3D panning. */ + gain = props->Reverb.Gain * Slot->Params.Gain * ReverbBoost; Update3DPanning(Device, props->Reverb.ReflectionsPan, - props->Reverb.LateReverbPan, - Slot->Gain * ReverbBoost, State); + props->Reverb.LateReverbPan, gain, + props->Reverb.ReflectionsGain, + props->Reverb.LateReverbGain, State); + + /* Determine if delay-line cross-fading is required. */ + for(i = 0;i < 4;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; + } + } } -static ALvoid ALreverbState_Destruct(ALreverbState *State) +/************************************** + * Effect Processing * + **************************************/ + +/* Basic delay line input/output routines. */ +static inline ALfloat DelayLineOut(const DelayLineI *Delay, const ALsizei offset, const ALsizei c) { - free(State->SampleBuffer); - State->SampleBuffer = NULL; + return Delay->Line[offset&Delay->Mask][c]; } -DECLARE_DEFAULT_ALLOCATORS(ALreverbState) +/* Cross-faded delay line output routine. Instead of interpolating the + * offsets, this interpolates (cross-fades) the outputs at each offset. + */ +static inline ALfloat FadedDelayLineOut(const DelayLineI *Delay, const ALsizei off0, + const ALsizei off1, const ALsizei c, const ALfloat mu) +{ + return lerp(Delay->Line[off0&Delay->Mask][c], Delay->Line[off1&Delay->Mask][c], mu); +} +#define DELAY_OUT_Faded(d, o0, o1, c, mu) FadedDelayLineOut(d, o0, o1, c, mu) +#define DELAY_OUT_Unfaded(d, o0, o1, c, mu) DelayLineOut(d, o0, c) -DEFINE_ALEFFECTSTATE_VTABLE(ALreverbState); +static inline ALvoid DelayLineIn(DelayLineI *Delay, const ALsizei offset, const ALsizei c, const ALfloat in) +{ + Delay->Line[offset&Delay->Mask][c] = in; +} + +static inline ALvoid DelayLineIn4(DelayLineI *Delay, ALsizei offset, const ALfloat in[4]) +{ + ALsizei i; + offset &= Delay->Mask; + for(i = 0;i < 4;i++) + Delay->Line[offset][i] = in[i]; +} + +static inline ALvoid DelayLineIn4Rev(DelayLineI *Delay, ALsizei offset, const ALfloat in[4]) +{ + ALsizei i; + offset &= Delay->Mask; + for(i = 0;i < 4;i++) + Delay->Line[offset][i] = in[3-i]; +} + +static void CalcModulationDelays(ALreverbState *State, ALint *restrict delays, const ALsizei todo) +{ + ALfloat sinus, range; + ALsizei index, i; + + index = State->Mod.Index; + range = State->Mod.Filter; + for(i = 0;i < todo;i++) + { + /* Calculate the sinus rhythm (dependent on modulation time and the + * sampling rate). + */ + sinus = sinf(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. */ + delays[i] = lroundf(range*sinus); + } + State->Mod.Index = index; + State->Mod.Filter = range; +} + +/* 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 vec, const ALfloat xCoeff, const ALfloat yCoeff) +{ + const ALfloat f[4] = { vec[0], vec[1], vec[2], vec[3] }; + + vec[0] = xCoeff*f[0] + yCoeff*( f[1] + -f[2] + f[3]); + vec[1] = xCoeff*f[1] + yCoeff*(-f[0] + f[2] + f[3]); + vec[2] = xCoeff*f[2] + yCoeff*( f[0] + -f[1] + f[3]); + vec[3] = xCoeff*f[3] + yCoeff*(-f[0] + -f[1] + -f[2] ); +} + +/* 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 vec, const ALsizei offset, \ + const ALfloat feedCoeff, const ALfloat xCoeff, \ + const ALfloat yCoeff, const ALfloat mu, \ + VecAllpass *Vap) \ +{ \ + ALfloat input; \ + ALfloat f[4]; \ + ALsizei i; \ + \ + (void)mu; /* Ignore for Unfaded. */ \ + \ + for(i = 0;i < 4;i++) \ + { \ + input = vec[i]; \ + vec[i] = DELAY_OUT_##T(&Vap->Delay, offset-Vap->Offset[i][0], \ + offset-Vap->Offset[i][1], i, mu) - \ + feedCoeff*input; \ + f[i] = input + feedCoeff*vec[i]; \ + } \ + \ + VectorPartialScatter(f, xCoeff, yCoeff); \ + \ + DelayLineIn4(&Vap->Delay, offset, f); \ +} +DECL_TEMPLATE(Unfaded) +DECL_TEMPLATE(Faded) +#undef DECL_TEMPLATE + +/* A helper to reverse vector components. */ +static inline void VectorReverse(ALfloat vec[4]) +{ + const ALfloat f[4] = { vec[0], vec[1], vec[2], vec[3] }; + + vec[0] = f[3]; + vec[1] = f[2]; + vec[2] = f[1]; + vec[3] = f[0]; +} + +/* 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 ALvoid 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[4]; \ + ALsizei i, j; \ + \ + for(i = 0;i < todo;i++) \ + { \ + for(j = 0;j < 4;j++) \ + f[j] = DELAY_OUT_##T(&State->Delay, \ + offset-State->EarlyDelayTap[j][0], \ + offset-State->EarlyDelayTap[j][1], j, fade \ + ) * State->EarlyDelayCoeff[j]; \ + \ + VectorAllpass_##T(f, offset, apFeedCoeff, mixX, mixY, fade, \ + &State->Early.VecAp); \ + \ + DelayLineIn4Rev(&State->Early.Delay, offset, f); \ + \ + for(j = 0;j < 4;j++) \ + f[j] += DELAY_OUT_##T(&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 < 4;j++) \ + out[j][i] = f[j]; \ + \ + VectorReverse(f); \ + \ + VectorPartialScatter(f, mixX, mixY); \ + \ + DelayLineIn4(&State->Delay, offset-State->LateFeedTap, f); \ + \ + 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 coeffs[3], ALfloat state[2]) +{ + ALfloat out = coeffs[0]*in + coeffs[1]*state[0] + coeffs[2]*state[1]; + + state[0] = in; + state[1] = out; + + return out; +} + +/* Applies the two T60 damping filter sections. */ +static inline ALfloat LateT60Filter(const ALsizei index, const ALfloat in, ALreverbState *State) +{ + ALfloat out = FirstOrderFilter(in, State->Late.Filters[index].LFCoeffs, + State->Late.Filters[index].States[0]); + + return State->Late.Filters[index].MidCoeff * + FirstOrderFilter(out, State->Late.Filters[index].HFCoeffs, + State->Late.Filters[index].States[1]); +} + +/* 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 static specializations are used for transitional (cross-faded) delay + * line processing and non-transitional processing. + */ +#define DECL_TEMPLATE(T) \ +static ALvoid 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; \ + ALint moddelay[MAX_UPDATE_SAMPLES]; \ + ALsizei delay; \ + ALsizei offset; \ + ALsizei i, j; \ + ALfloat f[4]; \ + \ + CalcModulationDelays(State, moddelay, todo); \ + \ + offset = State->Offset; \ + for(i = 0;i < todo;i++) \ + { \ + for(j = 0;j < 4;j++) \ + f[j] = DELAY_OUT_##T(&State->Delay, \ + offset-State->LateDelayTap[j][0], \ + offset-State->LateDelayTap[j][1], j, fade \ + ) * State->Late.DensityGain; \ + \ + delay = offset - moddelay[i]; \ + for(j = 0;j < 4;j++) \ + f[j] += DELAY_OUT_##T(&State->Late.Delay, \ + delay-State->Late.Offset[j][0], \ + delay-State->Late.Offset[j][1], j, fade \ + ); \ + \ + for(j = 0;j < 4;j++) \ + f[j] = LateT60Filter(j, f[j], State); \ + \ + VectorAllpass_##T(f, offset, apFeedCoeff, mixX, mixY, fade, \ + &State->Late.VecAp); \ + \ + for(j = 0;j < 4;j++) \ + out[j][i] = f[j]; \ + \ + VectorReverse(f); \ + \ + VectorPartialScatter(f, mixX, mixY); \ + \ + DelayLineIn4(&State->Late.Delay, offset, f); \ + \ + offset++; \ + fade += FadeStep; \ + } \ +} +DECL_TEMPLATE(Unfaded) +DECL_TEMPLATE(Faded) +#undef DECL_TEMPLATE + +typedef ALfloat (*ProcMethodType)(ALreverbState *State, const ALsizei todo, ALfloat fade, + const ALfloat (*restrict input)[MAX_UPDATE_SAMPLES], + ALfloat (*restrict early)[MAX_UPDATE_SAMPLES], ALfloat (*restrict late)[MAX_UPDATE_SAMPLES]); + +/* Perform the non-EAX reverb pass on a given input sample, resulting in + * four-channel output. + */ +static ALfloat VerbPass(ALreverbState *State, const ALsizei todo, ALfloat fade, + const ALfloat (*restrict input)[MAX_UPDATE_SAMPLES], + ALfloat (*restrict early)[MAX_UPDATE_SAMPLES], + ALfloat (*restrict late)[MAX_UPDATE_SAMPLES]) +{ + ALsizei 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); + + /* Feed the initial delay line. */ + for(i = 0;i < todo;i++) + DelayLineIn(&State->Delay, State->Offset+i, c, early[0][i]); + } + + if(fade < 1.0f) + { + /* 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 one sample. */ + State->Offset += todo; + + return fade; +} + +/* Perform the EAX reverb pass on a given input sample, resulting in four- + * channel output. + */ +static ALfloat EAXVerbPass(ALreverbState *State, const ALsizei todo, ALfloat fade, + const ALfloat (*restrict input)[MAX_UPDATE_SAMPLES], + ALfloat (*restrict early)[MAX_UPDATE_SAMPLES], + ALfloat (*restrict late)[MAX_UPDATE_SAMPLES]) +{ + ALsizei i, c; + + for(c = 0;c < 4;c++) + { + /* Band-pass the incoming samples. Use the early output lines for temp + * storage. + */ + ALfilterState_process(&State->Filter[c].Lp, early[0], input[c], todo); + ALfilterState_process(&State->Filter[c].Hp, early[1], early[0], todo); + + /* Feed the initial delay line. */ + for(i = 0;i < todo;i++) + DelayLineIn(&State->Delay, State->Offset+i, c, early[1][i]); + } + + if(fade < 1.0f) + { + /* 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; + + return fade; +} + +static ALvoid ALreverbState_process(ALreverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels) +{ + ProcMethodType ReverbProc = State->IsEax ? EAXVerbPass : VerbPass; + ALfloat (*restrict afmt)[MAX_UPDATE_SAMPLES] = State->AFormatSamples; + ALfloat (*restrict early)[MAX_UPDATE_SAMPLES] = State->EarlySamples; + ALfloat (*restrict late)[MAX_UPDATE_SAMPLES] = State->ReverbSamples; + ALsizei fadeCount = State->FadeCount; + ALfloat fade = (ALfloat)fadeCount / FADE_SAMPLES; + ALsizei base, c; + + /* Process reverb for these samples. */ + for(base = 0;base < SamplesToDo;) + { + 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-Format to A-Format for processing. */ + memset(afmt, 0, sizeof(*afmt)*4); + for(c = 0;c < 4;c++) + MixRowSamples(afmt[c], B2A.m[c], + SamplesIn, MAX_EFFECT_CHANNELS, base, todo + ); + + /* Process the samples for reverb. */ + fade = ReverbProc(State, todo, fade, afmt, early, late); + if(UNEXPECTED(fadeCount < FADE_SAMPLES) && (fadeCount += todo) >= FADE_SAMPLES) + { + /* Update the cross-fading delay line taps. */ + fadeCount = FADE_SAMPLES; + fade = 1.0f; + for(c = 0;c < 4;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++) + 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; + } + State->FadeCount = fadeCount; +} typedef struct ALreverbStateFactory { @@ -1204,92 +1897,11 @@ typedef struct ALreverbStateFactory { static ALeffectState *ALreverbStateFactory_create(ALreverbStateFactory* UNUSED(factory)) { ALreverbState *state; - ALuint index, l; - state = ALreverbState_New(sizeof(*state)); + alcall_once(&mixfunc_inited, init_mixfunc); + + NEW_OBJ0(state, ALreverbState)(); if(!state) return NULL; - SET_VTABLE2(ALreverbState, ALeffectState, state); - - state->TotalSamples = 0; - state->SampleBuffer = NULL; - - ALfilterState_clear(&state->LpFilter); - ALfilterState_clear(&state->HpFilter); - - state->Mod.Delay.Mask = 0; - state->Mod.Delay.Line = NULL; - state->Mod.Index = 0; - state->Mod.Range = 1; - state->Mod.Depth = 0.0f; - state->Mod.Coeff = 0.0f; - state->Mod.Filter = 0.0f; - - state->Delay.Mask = 0; - state->Delay.Line = NULL; - state->DelayTap[0] = 0; - state->DelayTap[1] = 0; - - state->Early.Gain = 0.0f; - for(index = 0;index < 4;index++) - { - state->Early.Coeff[index] = 0.0f; - state->Early.Delay[index].Mask = 0; - state->Early.Delay[index].Line = NULL; - state->Early.Offset[index] = 0; - } - - state->Decorrelator.Mask = 0; - state->Decorrelator.Line = NULL; - state->DecoTap[0] = 0; - state->DecoTap[1] = 0; - state->DecoTap[2] = 0; - - state->Late.Gain = 0.0f; - state->Late.DensityGain = 0.0f; - state->Late.ApFeedCoeff = 0.0f; - state->Late.MixCoeff = 0.0f; - for(index = 0;index < 4;index++) - { - state->Late.ApCoeff[index] = 0.0f; - state->Late.ApDelay[index].Mask = 0; - state->Late.ApDelay[index].Line = NULL; - state->Late.ApOffset[index] = 0; - - state->Late.Coeff[index] = 0.0f; - state->Late.Delay[index].Mask = 0; - state->Late.Delay[index].Line = NULL; - state->Late.Offset[index] = 0; - - state->Late.LpCoeff[index] = 0.0f; - state->Late.LpSample[index] = 0.0f; - } - - for(l = 0;l < 4;l++) - { - for(index = 0;index < MAX_OUTPUT_CHANNELS;index++) - { - state->Early.PanGain[l][index] = 0.0f; - state->Late.PanGain[l][index] = 0.0f; - } - } - - state->Echo.DensityGain = 0.0f; - state->Echo.Delay.Mask = 0; - state->Echo.Delay.Line = NULL; - state->Echo.ApDelay.Mask = 0; - state->Echo.ApDelay.Line = NULL; - state->Echo.Coeff = 0.0f; - state->Echo.ApFeedCoeff = 0.0f; - state->Echo.ApCoeff = 0.0f; - state->Echo.Offset = 0; - state->Echo.ApOffset = 0; - state->Echo.LpCoeff = 0.0f; - state->Echo.LpSample = 0.0f; - state->Echo.MixCoeff = 0.0f; - - state->Offset = 0; - - state->Gain = state->Late.PanGain; return STATIC_CAST(ALeffectState, state); } @@ -1460,20 +2072,16 @@ 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); - LockContext(context); props->Reverb.ReflectionsPan[0] = vals[0]; props->Reverb.ReflectionsPan[1] = vals[1]; props->Reverb.ReflectionsPan[2] = vals[2]; - UnlockContext(context); break; case AL_EAXREVERB_LATE_REVERB_PAN: if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2]))) SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE); - LockContext(context); props->Reverb.LateReverbPan[0] = vals[0]; props->Reverb.LateReverbPan[1] = vals[1]; props->Reverb.LateReverbPan[2] = vals[2]; - UnlockContext(context); break; default: @@ -1594,18 +2202,14 @@ void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum switch(param) { case AL_EAXREVERB_REFLECTIONS_PAN: - LockContext(context); vals[0] = props->Reverb.ReflectionsPan[0]; vals[1] = props->Reverb.ReflectionsPan[1]; vals[2] = props->Reverb.ReflectionsPan[2]; - UnlockContext(context); break; case AL_EAXREVERB_LATE_REVERB_PAN: - LockContext(context); vals[0] = props->Reverb.LateReverbPan[0]; vals[1] = props->Reverb.LateReverbPan[1]; vals[2] = props->Reverb.LateReverbPan[2]; - UnlockContext(context); break; default: diff --git a/libs/openal-soft/Alc/helpers.c b/libs/openal-soft/Alc/helpers.c index 9a9273c4..7f6349b3 100644 --- a/libs/openal-soft/Alc/helpers.c +++ b/libs/openal-soft/Alc/helpers.c @@ -32,6 +32,7 @@ #include #include #include +#include #ifdef HAVE_MALLOC_H #include #endif @@ -39,6 +40,11 @@ #include #endif +#ifdef __FreeBSD__ +#include +#include +#endif + #ifndef AL_NO_UID_DEFS #if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H) #define INITGUID @@ -61,10 +67,12 @@ DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xa DEFINE_GUID(IID_IAudioCaptureClient, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4,0xde, 0x18,0x5c,0x39,0x5c,0xd3,0x17); #ifdef HAVE_MMDEVAPI +#include #include #include DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14); DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0); +DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 ); #endif #endif #endif /* AL_NO_UID_DEFS */ @@ -89,6 +97,10 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x #endif #ifndef _WIN32 +#include +#include +#include +#include #include #elif defined(_WIN32_IE) #include @@ -105,8 +117,11 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x extern inline ALuint NextPowerOf2(ALuint value); +extern inline size_t RoundUp(size_t value, size_t r); +extern inline ALuint64 ScaleRound(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale); +extern inline ALuint64 ScaleFloor(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale); +extern inline ALuint64 ScaleCeil(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale); extern inline ALint fastf2i(ALfloat f); -extern inline ALuint fastf2u(ALfloat f); ALuint CPUCapFlags = 0; @@ -227,8 +242,42 @@ void FillCPUCaps(ALuint capfilter) #endif #endif #ifdef HAVE_NEON - /* Assume Neon support if compiled with it */ - caps |= CPU_CAP_NEON; + FILE *file = fopen("/proc/cpuinfo", "rt"); + if(!file) + ERR("Failed to open /proc/cpuinfo, cannot check for NEON support\n"); + else + { + 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; + while((str=strstr(str, "neon")) != NULL) + { + if(isspace(*(str-1)) && (str[4] == 0 || isspace(str[4]))) + { + caps |= CPU_CAP_NEON; + break; + } + str++; + } + break; + } + + fclose(file); + file = NULL; + } #endif TRACE("Extensions:%s%s%s%s%s%s\n", @@ -236,67 +285,24 @@ 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; } -void *al_malloc(size_t alignment, size_t size) -{ -#if defined(HAVE_ALIGNED_ALLOC) - size = (size+(alignment-1))&~(alignment-1); - return aligned_alloc(alignment, size); -#elif defined(HAVE_POSIX_MEMALIGN) - void *ret; - if(posix_memalign(&ret, alignment, size) == 0) - return ret; - return NULL; -#elif defined(HAVE__ALIGNED_MALLOC) - return _aligned_malloc(size, alignment); -#else - char *ret = malloc(size+alignment); - if(ret != NULL) - { - *(ret++) = 0x00; - while(((ptrdiff_t)ret&(alignment-1)) != 0) - *(ret++) = 0x55; - } - return ret; -#endif -} - -void *al_calloc(size_t alignment, size_t size) -{ - void *ret = al_malloc(alignment, size); - if(ret) memset(ret, 0, size); - return ret; -} - -void al_free(void *ptr) -{ -#if defined(HAVE_ALIGNED_ALLOC) || defined(HAVE_POSIX_MEMALIGN) - free(ptr); -#elif defined(HAVE__ALIGNED_MALLOC) - _aligned_free(ptr); -#else - if(ptr != NULL) - { - char *finder = ptr; - do { - --finder; - } while(*finder == 0x55); - free(finder); - } -#endif -} - - void SetMixerFPUMode(FPUCtl *ctl) { #ifdef HAVE_FENV_H fegetenv(STATIC_CAST(fenv_t, ctl)); +#ifdef _WIN32 + /* HACK: A nasty bug in MinGW-W64 causes fegetenv and fesetenv to not save + * and restore the FPU rounding mode, so we have to do it manually. Don't + * know if this also applies to MSVC. + */ + ctl->round_mode = fegetround(); +#endif #if defined(__GNUC__) && defined(HAVE_SSE) /* FIXME: Some fegetenv implementations can get the SSE environment too? * How to tell when it does? */ @@ -343,6 +349,9 @@ void RestoreFPUMode(const FPUCtl *ctl) { #ifdef HAVE_FENV_H fesetenv(STATIC_CAST(fenv_t, ctl)); +#ifdef _WIN32 + fesetround(ctl->round_mode); +#endif #if defined(__GNUC__) && defined(HAVE_SSE) if((CPUCapFlags&CPU_CAP_SSE)) __asm__ __volatile__("ldmxcsr %0" : : "m" (*&ctl->sse_state)); @@ -366,11 +375,62 @@ void RestoreFPUMode(const FPUCtl *ctl) 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 +static WCHAR *strrchrW(WCHAR *str, WCHAR ch) +{ + WCHAR *ret = NULL; + while(*str) + { + if(*str == ch) + ret = str; + ++str; + } + return ret; +} + +al_string GetProcPath(void) +{ + al_string ret = AL_STRING_INIT_STATIC(); + WCHAR *pathname, *sep; + DWORD pathlen; + DWORD len; + + pathlen = 256; + pathname = malloc(pathlen * sizeof(pathname[0])); + while(pathlen > 0 && (len=GetModuleFileNameW(NULL, pathname, pathlen)) == pathlen) + { + free(pathname); + pathlen <<= 1; + pathname = malloc(pathlen * sizeof(pathname[0])); + } + if(len == 0) + { + free(pathname); + ERR("Failed to get process name: error %lu\n", GetLastError()); + return ret; + } + + pathname[len] = 0; + if((sep = strrchrW(pathname, '\\'))) + { + WCHAR *sep2 = strrchrW(pathname, '/'); + if(sep2) *sep2 = 0; + else *sep = 0; + } + else if((sep = strrchrW(pathname, '/'))) + *sep = 0; + alstr_copy_wcstr(&ret, pathname); + free(pathname); + + TRACE("Got: %s\n", alstr_get_cstr(ret)); + return ret; +} + + static WCHAR *FromUTF8(const char *str) { WCHAR *out = NULL; @@ -476,379 +536,69 @@ void al_print(const char *type, const char *func, const char *fmt, ...) static inline int is_slash(int c) { return (c == '\\' || c == '/'); } -FILE *OpenDataFile(const char *fname, const char *subdir) -{ - static const int ids[2] = { CSIDL_APPDATA, CSIDL_COMMON_APPDATA }; - WCHAR *wname=NULL, *wsubdir=NULL; - FILE *f; - size_t i; - - wname = FromUTF8(fname); - if(!wname) - { - ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname); - return NULL; - } - - /* If the path is absolute, open it directly. */ - if(wname[0] != '\0' && wname[1] == ':' && is_slash(wname[2])) - { - f = _wfopen(wname, L"rb"); - if(f) TRACE("Opened %s\n", fname); - else WARN("Could not open %s\n", fname); - free(wname); - return f; - } - - /* Try the current directory first before the data directories. */ - if((f=_wfopen(wname, L"rb")) != NULL) - { - TRACE("Opened %s\n", fname); - free(wname); - return f; - } - - wsubdir = FromUTF8(subdir); - if(!wsubdir) - { - ERR("Failed to convert UTF-8 subdir: \"%s\"\n", subdir); - free(wname); - return NULL; - } - - for(i = 0;i < COUNTOF(ids);i++) - { - WCHAR buffer[PATH_MAX]; - size_t len; - - if(SHGetSpecialFolderPathW(NULL, buffer, ids[i], FALSE) == FALSE) - continue; - - len = lstrlenW(buffer); - if(len > 0 && is_slash(buffer[len-1])) - buffer[--len] = '\0'; - _snwprintf(buffer+len, PATH_MAX-len, L"/%ls/%ls", wsubdir, wname); - len = lstrlenW(buffer); - while(len > 0) - { - --len; - if(buffer[len] == '/') - buffer[len] = '\\'; - } - - if((f=_wfopen(buffer, L"rb")) != NULL) - { - al_string filepath = AL_STRING_INIT_STATIC(); - al_string_copy_wcstr(&filepath, buffer); - TRACE("Opened %s\n", al_string_get_cstr(filepath)); - al_string_deinit(&filepath); - break; - } - } - free(wname); - free(wsubdir); - - if(f == NULL) - WARN("Could not open %s\\%s\n", subdir, fname); - return f; -} - - -static size_t strlenW(const WCHAR *str) -{ - const WCHAR *end = str; - while(*end) ++end; - return end-str; -} - -static const WCHAR *strchrW(const WCHAR *str, WCHAR ch) -{ - for(;*str != 0;++str) - { - if(*str == ch) - return str; - } - return NULL; -} - -static const WCHAR *strrchrW(const WCHAR *str, WCHAR ch) -{ - const WCHAR *ret = NULL; - for(;*str != 0;++str) - { - if(*str == ch) - ret = str; - } - return ret; -} - -static const WCHAR *strstrW(const WCHAR *haystack, const WCHAR *needle) -{ - size_t len = strlenW(needle); - while(*haystack != 0) - { - if(CompareStringW(GetThreadLocale(), NORM_IGNORECASE, - haystack, len, needle, len) == CSTR_EQUAL) - return haystack; - - do { - ++haystack; - } while(((*haystack)&0xC000) == 0x8000); - } - return NULL; -} - - -/* Compares the filename in the find data with the match string. The match - * string may contain the "%r" marker to signifiy a sample rate (really any - * positive integer), "%%" to signify a single '%', or "%s" for a (non-greedy) - * string. - */ -static int MatchFilter(const WCHAR *match, const WIN32_FIND_DATAW *fdata) -{ - const WCHAR *name = fdata->cFileName; - int ret = 1; - - do { - const WCHAR *p = strchrW(match, '%'); - if(!p) - ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, - match, -1, name, -1) == CSTR_EQUAL; - else - { - int len = p-match; - ret = lstrlenW(name) >= len; - if(ret) - ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, - match, len, name, len) == CSTR_EQUAL; - if(ret) - { - match += len; - name += len; - - ++p; - if(*p == 'r') - { - unsigned long l = 0; - while(*name >= '0' && *name <= '9') - { - l = l*10 + (*name-'0'); - ++name; - } - ret = l > 0; - ++p; - } - else if(*p == 's') - { - const WCHAR *next = p+1; - if(*next != '\0' && *next != '%') - { - const WCHAR *next_p = strchrW(next, '%'); - const WCHAR *m; - - if(!next_p) - m = strstrW(name, next); - else - { - WCHAR *tmp = malloc((next_p - next + 1) * 2); - memcpy(tmp, next, (next_p - next) * 2); - tmp[next_p - next] = 0; - - m = strstrW(name, tmp); - - free(tmp); - } - - ret = !!m; - if(ret) - { - size_t l; - if(next_p) l = next_p - next; - else l = strlenW(next); - - name = m + l; - next += l; - } - } - p = next; - } - } - } - - match = p; - } while(ret && match && *match); - - return ret; -} - -static void RecurseDirectorySearch(const char *path, const WCHAR *match, vector_al_string *results) +static void DirectorySearch(const char *path, const char *ext, vector_al_string *results) { + al_string pathstr = AL_STRING_INIT_STATIC(); WIN32_FIND_DATAW fdata; - const WCHAR *sep, *p; + WCHAR *wpath; HANDLE hdl; - if(!match[0]) - return; + alstr_copy_cstr(&pathstr, path); + alstr_append_cstr(&pathstr, "\\*"); + alstr_append_cstr(&pathstr, ext); - /* Find the last directory separator and the next '%' marker in the match - * string. */ - sep = strrchrW(match, '\\'); - p = strchrW(match, '%'); + TRACE("Searching %s\n", alstr_get_cstr(pathstr)); - /* If there's no separator, test the files in the specified path against - * the match string, and add the results. */ - if(!sep) + wpath = FromUTF8(alstr_get_cstr(pathstr)); + + hdl = FindFirstFileW(wpath, &fdata); + if(hdl != INVALID_HANDLE_VALUE) { - al_string pathstr = AL_STRING_INIT_STATIC(); - WCHAR *wpath; + size_t base = VECTOR_SIZE(*results); + do { + al_string str = AL_STRING_INIT_STATIC(); + 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); - TRACE("Searching %s for %ls\n", path, match); - - al_string_append_cstr(&pathstr, path); - al_string_append_cstr(&pathstr, "\\*.*"); - wpath = FromUTF8(al_string_get_cstr(pathstr)); - - hdl = FindFirstFileW(wpath, &fdata); - if(hdl != INVALID_HANDLE_VALUE) - { - size_t base = VECTOR_SIZE(*results); - do { - if(MatchFilter(match, &fdata)) - { - 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)); - VECTOR_PUSH_BACK(*results, str); - } - } while(FindNextFileW(hdl, &fdata)); - FindClose(hdl); - - if(VECTOR_SIZE(*results) > base) - qsort(VECTOR_ITER_BEGIN(*results)+base, VECTOR_SIZE(*results)-base, - sizeof(VECTOR_FRONT(*results)), StringSortCompare); - } - - free(wpath); - al_string_deinit(&pathstr); - - return; + if(VECTOR_SIZE(*results) > base) + qsort(VECTOR_BEGIN(*results)+base, VECTOR_SIZE(*results)-base, + sizeof(VECTOR_FRONT(*results)), StringSortCompare); } - /* If there's no '%' marker, or it's after the final separator, append the - * remaining directories to the path and recurse into it with the remaining - * filename portion. */ - if(!p || p-sep >= 0) - { - al_string npath = AL_STRING_INIT_STATIC(); - al_string_append_cstr(&npath, path); - al_string_append_char(&npath, '\\'); - al_string_append_wrange(&npath, match, sep); - - TRACE("Recursing into %s with %ls\n", al_string_get_cstr(npath), sep+1); - RecurseDirectorySearch(al_string_get_cstr(npath), sep+1, results); - - al_string_deinit(&npath); - return; - } - - /* Look for the last separator before the '%' marker, and the first - * separator after it. */ - sep = strchrW(match, '\\'); - if(sep-p >= 0) sep = NULL; - for(;;) - { - const WCHAR *next = strchrW(sep?sep+1:match, '\\'); - if(next-p < 0) - { - al_string npath = AL_STRING_INIT_STATIC(); - WCHAR *nwpath, *nwmatch; - - /* Append up to the last directory before the one with a '%'. */ - al_string_copy_cstr(&npath, path); - if(sep) - { - al_string_append_char(&npath, '\\'); - al_string_append_wrange(&npath, match, sep); - } - al_string_append_cstr(&npath, "\\*.*"); - nwpath = FromUTF8(al_string_get_cstr(npath)); - - /* Take the directory name containing a '%' as a new string to - * match against. */ - if(!sep) - { - nwmatch = calloc(2, next-match+1); - memcpy(nwmatch, match, (next-match)*2); - } - else - { - nwmatch = calloc(2, next-(sep+1)+1); - memcpy(nwmatch, sep+1, (next-(sep+1))*2); - } - - /* For each matching directory name, recurse into it with the - * remaining string. */ - TRACE("Searching %s for %ls\n", al_string_get_cstr(npath), nwmatch); - hdl = FindFirstFileW(nwpath, &fdata); - if(hdl != INVALID_HANDLE_VALUE) - { - do { - if(MatchFilter(nwmatch, &fdata)) - { - al_string ndir = AL_STRING_INIT_STATIC(); - al_string_copy(&ndir, npath); - al_string_append_char(&ndir, '\\'); - al_string_append_wcstr(&ndir, fdata.cFileName); - TRACE("Recursing %s with %ls\n", al_string_get_cstr(ndir), next+1); - RecurseDirectorySearch(al_string_get_cstr(ndir), next+1, results); - al_string_deinit(&ndir); - } - } while(FindNextFileW(hdl, &fdata)); - FindClose(hdl); - } - - free(nwmatch); - free(nwpath); - al_string_deinit(&npath); - break; - } - sep = next; - } + free(wpath); + alstr_reset(&pathstr); } -vector_al_string SearchDataFiles(const char *match, const char *subdir) +vector_al_string SearchDataFiles(const char *ext, const char *subdir) { static const int ids[2] = { CSIDL_APPDATA, CSIDL_COMMON_APPDATA }; static RefCount search_lock; vector_al_string results = VECTOR_INIT_STATIC(); - WCHAR *wmatch; size_t i; - while(ATOMIC_EXCHANGE(uint, &search_lock, 1) == 1) + while(ATOMIC_EXCHANGE_SEQ(&search_lock, 1) == 1) althrd_yield(); - wmatch = FromUTF8(match); - if(!wmatch) - { - ERR("Failed to convert UTF-8 filename: \"%s\"\n", match); - return results; - } - for(i = 0;wmatch[i];++i) - { - if(wmatch[i] == '/') - wmatch[i] = '\\'; - } - /* If the path is absolute, use it directly. */ - if(isalpha(wmatch[0]) && wmatch[1] == ':' && is_slash(wmatch[2])) + if(isalpha(subdir[0]) && subdir[1] == ':' && is_slash(subdir[2])) { - char drv[3] = { (char)wmatch[0], ':', 0 }; - RecurseDirectorySearch(drv, wmatch+3, &results); + al_string path = AL_STRING_INIT_STATIC(); + 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(alstr_get_cstr(path), ext, &results); + + alstr_reset(&path); } - else if(wmatch[0] == '\\' && wmatch[1] == '\\' && wmatch[2] == '?' && wmatch[3] == '\\') - RecurseDirectorySearch("\\\\?", wmatch+4, &results); + else if(subdir[0] == '\\' && subdir[1] == '\\' && subdir[2] == '?' && subdir[3] == '\\') + DirectorySearch(subdir, ext, &results); else { al_string path = AL_STRING_INIT_STATIC(); @@ -857,29 +607,29 @@ vector_al_string SearchDataFiles(const char *match, 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); - *VECTOR_ITER_END(path) = 0; + *VECTOR_END(path) = 0; } } 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); - *VECTOR_ITER_END(path) = 0; + *VECTOR_END(path) = 0; } free(cwdbuf); } #define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0) VECTOR_FOR_EACH(char, path, FIX_SLASH); #undef FIX_SLASH - RecurseDirectorySearch(al_string_get_cstr(path), wmatch, &results); + DirectorySearch(alstr_get_cstr(path), ext, &results); /* Search the local and global data dirs. */ for(i = 0;i < COUNTOF(ids);i++) @@ -887,29 +637,151 @@ vector_al_string SearchDataFiles(const char *match, const char *subdir) WCHAR buffer[PATH_MAX]; 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 - RecurseDirectorySearch(al_string_get_cstr(path), wmatch, &results); + DirectorySearch(alstr_get_cstr(path), ext, &results); } } - al_string_deinit(&path); + alstr_reset(&path); } - free(wmatch); - ATOMIC_STORE(&search_lock, 0); + ATOMIC_STORE_SEQ(&search_lock, 0); return results; } + +struct FileMapping MapFileToMem(const char *fname) +{ + struct FileMapping ret = { NULL, NULL, NULL, 0 }; + MEMORY_BASIC_INFORMATION meminfo; + HANDLE file, fmap; + WCHAR *wname; + void *ptr; + + wname = FromUTF8(fname); + + file = CreateFileW(wname, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if(file == INVALID_HANDLE_VALUE) + { + ERR("Failed to open %s: %lu\n", fname, GetLastError()); + free(wname); + return ret; + } + free(wname); + wname = NULL; + + fmap = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL); + if(!fmap) + { + ERR("Failed to create map for %s: %lu\n", fname, GetLastError()); + CloseHandle(file); + return ret; + } + + ptr = MapViewOfFile(fmap, FILE_MAP_READ, 0, 0, 0); + if(!ptr) + { + ERR("Failed to map %s: %lu\n", fname, GetLastError()); + CloseHandle(fmap); + CloseHandle(file); + return ret; + } + + if(VirtualQuery(ptr, &meminfo, sizeof(meminfo)) != sizeof(meminfo)) + { + ERR("Failed to get map size for %s: %lu\n", fname, GetLastError()); + UnmapViewOfFile(ptr); + CloseHandle(fmap); + CloseHandle(file); + return ret; + } + + ret.file = file; + ret.fmap = fmap; + ret.ptr = ptr; + ret.len = meminfo.RegionSize; + return ret; +} + +void UnmapFileMem(const struct FileMapping *mapping) +{ + UnmapViewOfFile(mapping->ptr); + CloseHandle(mapping->fmap); + CloseHandle(mapping->file); +} + #else +al_string GetProcPath(void) +{ + al_string ret = AL_STRING_INIT_STATIC(); + char *pathname, *sep; + size_t pathlen; + +#ifdef __FreeBSD__ + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; + mib[3] = getpid(); + if (sysctl(mib, 4, NULL, &pathlen, NULL, 0) == -1) { + WARN("Failed to sysctl kern.proc.pathname.%d: %s\n", mib[3], strerror(errno)); + return ret; + } + + pathname = malloc(pathlen + 1); + sysctl(mib, 4, (void*)pathname, &pathlen, NULL, 0); + pathname[pathlen] = 0; +#else + const char *fname; + 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; +#endif + + sep = strrchr(pathname, '/'); + if(sep) + alstr_copy_range(&ret, pathname, sep); + else + alstr_copy_cstr(&ret, pathname); + free(pathname); + + TRACE("Got: %s\n", alstr_get_cstr(ret)); + return ret; +} + + #ifdef HAVE_DLFCN_H void *LoadLib(const char *name) @@ -955,272 +827,56 @@ void al_print(const char *type, const char *func, const char *fmt, ...) } -FILE *OpenDataFile(const char *fname, const char *subdir) +static void DirectorySearch(const char *path, const char *ext, vector_al_string *results) { - char buffer[PATH_MAX] = ""; - const char *str, *next; - FILE *f; + size_t extlen = strlen(ext); + DIR *dir; - if(fname[0] == '/') + TRACE("Searching %s for *%s\n", path, ext); + dir = opendir(path); + if(dir != NULL) { - if((f=al_fopen(fname, "rb")) != NULL) + size_t base = VECTOR_SIZE(*results); + struct dirent *dirent; + while((dirent=readdir(dir)) != NULL) { - TRACE("Opened %s\n", fname); - return f; + al_string str; + size_t len; + if(strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0) + continue; + + len = strlen(dirent->d_name); + if(!(len > extlen)) + continue; + if(strcasecmp(dirent->d_name+len-extlen, ext) != 0) + continue; + + AL_STRING_INIT(str); + alstr_copy_cstr(&str, path); + if(VECTOR_BACK(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); } - WARN("Could not open %s\n", fname); - return NULL; - } + closedir(dir); - if((f=al_fopen(fname, "rb")) != NULL) - { - TRACE("Opened %s\n", fname); - return f; - } - - if((str=getenv("XDG_DATA_HOME")) != NULL && str[0] != '\0') - snprintf(buffer, sizeof(buffer), "%s/%s/%s", str, subdir, fname); - else if((str=getenv("HOME")) != NULL && str[0] != '\0') - snprintf(buffer, sizeof(buffer), "%s/.local/share/%s/%s", str, subdir, fname); - if(buffer[0]) - { - if((f=al_fopen(buffer, "rb")) != NULL) - { - TRACE("Opened %s\n", buffer); - return f; - } - } - - if((str=getenv("XDG_DATA_DIRS")) == NULL || str[0] == '\0') - str = "/usr/local/share/:/usr/share/"; - - next = str; - while((str=next) != NULL && str[0] != '\0') - { - size_t len; - next = strchr(str, ':'); - - if(!next) - len = strlen(str); - else - { - len = next - str; - next++; - } - - if(len > sizeof(buffer)-1) - len = sizeof(buffer)-1; - strncpy(buffer, str, len); - buffer[len] = '\0'; - snprintf(buffer+len, sizeof(buffer)-len, "/%s/%s", subdir, fname); - - if((f=al_fopen(buffer, "rb")) != NULL) - { - TRACE("Opened %s\n", buffer); - return f; - } - } - WARN("Could not open %s/%s\n", subdir, fname); - - return NULL; -} - - -static int MatchFilter(const char *name, const char *match) -{ - int ret = 1; - - do { - const char *p = strchr(match, '%'); - if(!p) - ret = strcmp(match, name) == 0; - else - { - size_t len = p-match; - ret = strncmp(match, name, len) == 0; - if(ret) - { - match += len; - name += len; - - ++p; - if(*p == 'r') - { - char *end; - ret = strtoul(name, &end, 10) > 0; - if(ret) name = end; - ++p; - } - else if(*p == 's') - { - const char *next = p+1; - if(*next != '\0' && *next != '%') - { - const char *next_p = strchr(next, '%'); - const char *m; - - if(!next_p) - m = strstr(name, next); - else - { - char *tmp = malloc(next_p - next + 1); - memcpy(tmp, next, next_p - next); - tmp[next_p - next] = 0; - - m = strstr(name, tmp); - - free(tmp); - } - - ret = !!m; - if(ret) - { - size_t l; - if(next_p) l = next_p - next; - else l = strlen(next); - - name = m + l; - next += l; - } - } - p = next; - } - } - } - - match = p; - } while(ret && match && *match); - - return ret; -} - -static void RecurseDirectorySearch(const char *path, const char *match, vector_al_string *results) -{ - char *sep, *p; - - if(!match[0]) - return; - - sep = strrchr(match, '/'); - p = strchr(match, '%'); - - if(!sep) - { - DIR *dir; - - TRACE("Searching %s for %s\n", path?path:"/", match); - dir = opendir(path?path:"/"); - if(dir != NULL) - { - size_t base = VECTOR_SIZE(*results); - struct dirent *dirent; - while((dirent=readdir(dir)) != NULL) - { - al_string str; - if(strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0 || - !MatchFilter(dirent->d_name, match)) - continue; - - AL_STRING_INIT(str); - if(path) al_string_copy_cstr(&str, path); - al_string_append_char(&str, '/'); - al_string_append_cstr(&str, dirent->d_name); - TRACE("Got result %s\n", al_string_get_cstr(str)); - VECTOR_PUSH_BACK(*results, str); - } - closedir(dir); - - if(VECTOR_SIZE(*results) > base) - qsort(VECTOR_ITER_BEGIN(*results)+base, VECTOR_SIZE(*results)-base, - sizeof(VECTOR_FRONT(*results)), StringSortCompare); - } - - return; - } - - if(!p || p-sep >= 0) - { - al_string npath = AL_STRING_INIT_STATIC(); - if(path) al_string_append_cstr(&npath, path); - al_string_append_char(&npath, '/'); - al_string_append_range(&npath, match, sep); - - TRACE("Recursing into %s with %s\n", al_string_get_cstr(npath), sep+1); - RecurseDirectorySearch(al_string_get_cstr(npath), sep+1, results); - - al_string_deinit(&npath); - return; - } - - sep = strchr(match, '/'); - if(sep-p >= 0) sep = NULL; - for(;;) - { - char *next = strchr(sep?sep+1:match, '/'); - if(next-p < 0) - { - al_string npath = AL_STRING_INIT_STATIC(); - al_string nmatch = AL_STRING_INIT_STATIC(); - const char *tomatch; - DIR *dir; - - if(!sep) - { - al_string_append_cstr(&npath, path?path:"/."); - tomatch = match; - } - else - { - if(path) al_string_append_cstr(&npath, path); - al_string_append_char(&npath, '/'); - al_string_append_range(&npath, match, sep); - - al_string_append_range(&nmatch, sep+1, next); - tomatch = al_string_get_cstr(nmatch); - } - - TRACE("Searching %s for %s\n", al_string_get_cstr(npath), tomatch); - dir = opendir(path?path:"/"); - if(dir != NULL) - { - al_string ndir = AL_STRING_INIT_STATIC(); - struct dirent *dirent; - - while((dirent=readdir(dir)) != NULL) - { - if(strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0 || - !MatchFilter(dirent->d_name, tomatch)) - continue; - al_string_copy(&ndir, npath); - al_string_append_char(&ndir, '/'); - al_string_append_cstr(&ndir, dirent->d_name); - TRACE("Recursing %s with %s\n", al_string_get_cstr(ndir), next+1); - RecurseDirectorySearch(al_string_get_cstr(ndir), next+1, results); - } - closedir(dir); - - al_string_deinit(&ndir); - } - - al_string_deinit(&nmatch); - al_string_deinit(&npath); - break; - } - - sep = next; + if(VECTOR_SIZE(*results) > base) + qsort(VECTOR_BEGIN(*results)+base, VECTOR_SIZE(*results)-base, + sizeof(VECTOR_FRONT(*results)), StringSortCompare); } } -vector_al_string SearchDataFiles(const char *match, const char *subdir) +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(match[0] == '/') - RecurseDirectorySearch(NULL, match+1, &results); + if(subdir[0] == '/') + DirectorySearch(subdir, ext, &results); else { al_string path = AL_STRING_INIT_STATIC(); @@ -1229,29 +885,33 @@ vector_al_string SearchDataFiles(const char *match, const char *subdir) /* Search the app-local directory. */ if((str=getenv("ALSOFT_LOCAL_PATH")) && *str != '\0') - { - strncpy(cwdbuf, str, sizeof(cwdbuf)-1); - cwdbuf[sizeof(cwdbuf)-1] = '\0'; - } - else if(!getcwd(cwdbuf, sizeof(cwdbuf))) - strcpy(cwdbuf, "."); - RecurseDirectorySearch(cwdbuf, match, &results); + DirectorySearch(str, ext, &results); + else if(getcwd(cwdbuf, sizeof(cwdbuf))) + DirectorySearch(cwdbuf, ext, &results); + else + DirectorySearch(".", ext, &results); // Search local data dir if((str=getenv("XDG_DATA_HOME")) != NULL && str[0] != '\0') { - al_string_append_cstr(&path, str); - al_string_append_char(&path, '/'); - al_string_append_cstr(&path, subdir); + alstr_copy_cstr(&path, str); + if(VECTOR_BACK(path) != '/') + 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_append_cstr(&path, str); - al_string_append_cstr(&path, "/.local/share/"); - al_string_append_cstr(&path, subdir); + alstr_copy_cstr(&path, str); + if(VECTOR_BACK(path) == '/') + { + VECTOR_POP_BACK(path); + *VECTOR_END(path) = 0; + } + alstr_append_cstr(&path, "/.local/share/"); + alstr_append_cstr(&path, subdir); + DirectorySearch(alstr_get_cstr(path), ext, &results); } - if(!al_string_empty(path)) - RecurseDirectorySearch(al_string_get_cstr(path), match, &results); // Search global data dirs if((str=getenv("XDG_DATA_DIRS")) == NULL || str[0] == '\0') @@ -1262,30 +922,71 @@ vector_al_string SearchDataFiles(const char *match, const char *subdir) { next = strchr(str, ':'); if(!next) - al_string_copy_cstr(&path, str); + alstr_copy_cstr(&path, str); else { - al_string_clear(&path); - al_string_append_range(&path, str, next); + alstr_copy_range(&path, str, next); ++next; } - if(!al_string_empty(path)) + if(!alstr_empty(path)) { - al_string_append_char(&path, '/'); - al_string_append_cstr(&path, subdir); + if(VECTOR_BACK(path) != '/') + alstr_append_char(&path, '/'); + alstr_append_cstr(&path, subdir); - RecurseDirectorySearch(al_string_get_cstr(path), match, &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; } + +struct FileMapping MapFileToMem(const char *fname) +{ + struct FileMapping ret = { -1, NULL, 0 }; + struct stat sbuf; + void *ptr; + int fd; + + fd = open(fname, O_RDONLY, 0); + if(fd == -1) + { + ERR("Failed to open %s: (%d) %s\n", fname, errno, strerror(errno)); + return ret; + } + if(fstat(fd, &sbuf) == -1) + { + ERR("Failed to stat %s: (%d) %s\n", fname, errno, strerror(errno)); + close(fd); + return ret; + } + + ptr = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if(ptr == MAP_FAILED) + { + ERR("Failed to map %s: (%d) %s\n", fname, errno, strerror(errno)); + close(fd); + return ret; + } + + ret.fd = fd; + ret.ptr = ptr; + ret.len = sbuf.st_size; + return ret; +} + +void UnmapFileMem(const struct FileMapping *mapping) +{ + munmap(mapping->ptr, mapping->len); + close(mapping->fd); +} + #endif @@ -1314,93 +1015,26 @@ void SetRTPriority(void) } -ALboolean vector_reserve(char *ptr, size_t base_size, size_t obj_size, size_t obj_count, ALboolean exact) +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 alstr_clear(al_string *str) { - vector_ *vecptr = (vector_*)ptr; - if((*vecptr ? (*vecptr)->Capacity : 0) < obj_count) + if(!alstr_empty(*str)) { - size_t old_size = (*vecptr ? (*vecptr)->Size : 0); - void *temp; - - /* Use the next power-of-2 size if we don't need to allocate the exact - * amount. This is preferred when regularly increasing the vector since - * it means fewer reallocations. Though it means it also wastes some - * memory. */ - if(exact == AL_FALSE && obj_count < INT_MAX) - obj_count = NextPowerOf2((ALuint)obj_count); - - /* Need to be explicit with the caller type's base size, because it - * could have extra padding before the start of the array (that is, - * sizeof(*vector_) may not equal base_size). */ - temp = realloc(*vecptr, base_size + obj_size*obj_count); - if(temp == NULL) return AL_FALSE; - - *vecptr = temp; - (*vecptr)->Capacity = obj_count; - (*vecptr)->Size = old_size; + /* Reserve one more character than the total size of the string. This + * is to ensure we have space to add a null terminator in the string + * data so it can be used as a C-style string. + */ + VECTOR_RESIZE(*str, 0, 1); + VECTOR_ELEM(*str, 0) = 0; } - return AL_TRUE; } -ALboolean vector_resize(char *ptr, size_t base_size, size_t obj_size, size_t obj_count) -{ - vector_ *vecptr = (vector_*)ptr; - if(*vecptr || obj_count > 0) - { - if(!vector_reserve((char*)vecptr, base_size, obj_size, obj_count, AL_TRUE)) - return AL_FALSE; - (*vecptr)->Size = obj_count; - } - return AL_TRUE; -} - -ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_pos, const void *datstart, const void *datend) -{ - vector_ *vecptr = (vector_*)ptr; - if(datstart != datend) - { - ptrdiff_t ins_elem = (*vecptr ? ((char*)ins_pos - ((char*)(*vecptr) + base_size)) : - ((char*)ins_pos - (char*)NULL)) / - obj_size; - ptrdiff_t numins = ((const char*)datend - (const char*)datstart) / obj_size; - - assert(numins > 0); - if((size_t)numins + VECTOR_SIZE(*vecptr) < (size_t)numins || - !vector_reserve((char*)vecptr, base_size, obj_size, VECTOR_SIZE(*vecptr)+numins, AL_TRUE)) - return AL_FALSE; - - /* NOTE: ins_pos may have been invalidated if *vecptr moved. Use ins_elem instead. */ - if((size_t)ins_elem < (*vecptr)->Size) - { - memmove((char*)(*vecptr) + base_size + ((ins_elem+numins)*obj_size), - (char*)(*vecptr) + base_size + ((ins_elem )*obj_size), - ((*vecptr)->Size-ins_elem)*obj_size); - } - memcpy((char*)(*vecptr) + base_size + (ins_elem*obj_size), - datstart, numins*obj_size); - (*vecptr)->Size += numins; - } - return AL_TRUE; -} - - -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); - -void al_string_clear(al_string *str) -{ - /* Reserve one more character than the total size of the string. This is to - * ensure we have space to add a null terminator in the string data so it - * can be used as a C-style string. */ - VECTOR_RESERVE(*str, 1); - VECTOR_RESIZE(*str, 0); - *VECTOR_ITER_END(*str) = 0; -} - -static inline int al_string_compare(const al_string_char_type *str1, 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); @@ -1411,99 +1045,121 @@ 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); - VECTOR_RESERVE(*str, len+1); - VECTOR_RESIZE(*str, 0); - VECTOR_INSERT(*str, VECTOR_ITER_END(*str), VECTOR_ITER_BEGIN(from), VECTOR_ITER_BEGIN(from)+len); - *VECTOR_ITER_END(*str) = 0; + size_t len = alstr_length(from); + size_t i; + + VECTOR_RESIZE(*str, len, len+1); + for(i = 0;i < len;i++) + VECTOR_ELEM(*str, i) = VECTOR_ELEM(from, i); + 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); - VECTOR_RESERVE(*str, len+1); - VECTOR_RESIZE(*str, 0); - VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, from+len); - *VECTOR_ITER_END(*str) = 0; + size_t i; + + VECTOR_RESIZE(*str, len, len+1); + for(i = 0;i < len;i++) + VECTOR_ELEM(*str, i) = from[i]; + VECTOR_ELEM(*str, i) = 0; } -void al_string_append_char(al_string *str, const al_string_char_type c) +void alstr_copy_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to) { - VECTOR_RESERVE(*str, al_string_length(*str)+2); - VECTOR_PUSH_BACK(*str, c); - *VECTOR_ITER_END(*str) = 0; + size_t len = to - from; + size_t i; + + VECTOR_RESIZE(*str, len, len+1); + for(i = 0;i < len;i++) + VECTOR_ELEM(*str, i) = from[i]; + VECTOR_ELEM(*str, i) = 0; } -void al_string_append_cstr(al_string *str, const al_string_char_type *from) +void alstr_append_char(al_string *str, const al_string_char_type c) +{ + size_t len = alstr_length(*str); + VECTOR_RESIZE(*str, len, len+2); + VECTOR_PUSH_BACK(*str, c); + VECTOR_ELEM(*str, len+1) = 0; +} + +void alstr_append_cstr(al_string *str, const al_string_char_type *from) { size_t len = strlen(from); if(len != 0) { - VECTOR_RESERVE(*str, al_string_length(*str)+len+1); - VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, from+len); - *VECTOR_ITER_END(*str) = 0; + size_t base = alstr_length(*str); + size_t i; + + VECTOR_RESIZE(*str, base+len, base+len+1); + for(i = 0;i < len;i++) + VECTOR_ELEM(*str, base+i) = from[i]; + VECTOR_ELEM(*str, base+i) = 0; } } -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) { - if(to != from) + size_t len = to - from; + if(len != 0) { - VECTOR_RESERVE(*str, al_string_length(*str)+(to-from)+1); - VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, to); - *VECTOR_ITER_END(*str) = 0; + size_t base = alstr_length(*str); + size_t i; + + VECTOR_RESIZE(*str, base+len, base+len+1); + for(i = 0;i < len;i++) + VECTOR_ELEM(*str, base+i) = from[i]; + VECTOR_ELEM(*str, base+i) = 0; } } #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) { - VECTOR_RESERVE(*str, len); - VECTOR_RESIZE(*str, len-1); + VECTOR_RESIZE(*str, len-1, len); WideCharToMultiByte(CP_UTF8, 0, from, -1, &VECTOR_FRONT(*str), len, NULL, NULL); - *VECTOR_ITER_END(*str) = 0; + VECTOR_ELEM(*str, len-1) = 0; } } -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 strlen = al_string_length(*str); - VECTOR_RESERVE(*str, strlen+len); - VECTOR_RESIZE(*str, strlen+len-1); - WideCharToMultiByte(CP_UTF8, 0, from, -1, &VECTOR_FRONT(*str) + strlen, len, NULL, NULL); - *VECTOR_ITER_END(*str) = 0; + 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_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 strlen = al_string_length(*str); - VECTOR_RESERVE(*str, strlen+len+1); - VECTOR_RESIZE(*str, strlen+len); - WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), &VECTOR_FRONT(*str) + strlen, len+1, NULL, NULL); - *VECTOR_ITER_END(*str) = 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; } } #endif diff --git a/libs/openal-soft/Alc/hrtf.c b/libs/openal-soft/Alc/hrtf.c index 467232d7..d9968154 100644 --- a/libs/openal-soft/Alc/hrtf.c +++ b/libs/openal-soft/Alc/hrtf.c @@ -28,14 +28,16 @@ #include "alMain.h" #include "alSource.h" #include "alu.h" +#include "bformatdec.h" #include "hrtf.h" #include "compat.h" +#include "almalloc.h" /* 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_EV_COUNT (5) @@ -44,18 +46,10 @@ #define MIN_AZ_COUNT (1) #define MAX_AZ_COUNT (128) -struct Hrtf { - ALuint sampleRate; - ALuint irSize; - ALubyte evCount; - - const ALubyte *azCount; - const ALushort *evOffset; - const ALshort *coeffs; - const ALubyte *delays; - - al_string filename; - struct Hrtf *next; +struct HrtfEntry { + struct HrtfEntry *next; + struct Hrtf *handle; + char filename[]; }; static const ALchar magicMarker00[8] = "MinPHR00"; @@ -63,389 +57,374 @@ static const ALchar magicMarker01[8] = "MinPHR01"; /* First value for pass-through coefficients (remaining are 0), used for omni- * directional sounds. */ -static const ALfloat PassthruCoeff = 32767.0f * 0.707106781187f/*sqrt(0.5)*/; +static 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 indices given the polar elevation in radians. - * This will return two indices between 0 and (evcount - 1) and an - * interpolation factor between 0.0 and 1.0. + +/* 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. */ -static void CalcEvIndices(ALuint evcount, ALfloat ev, ALuint *evidx, ALfloat *evmu) +static ALsizei CalcEvIndex(ALsizei evcount, ALfloat ev, ALfloat *mu) { - ev = (F_PI_2 + ev) * (evcount-1) / F_PI; - evidx[0] = fastf2u(ev); - evidx[1] = minu(evidx[0] + 1, evcount-1); - *evmu = ev - evidx[0]; + ALsizei idx; + ev = (F_PI_2+ev) * (evcount-1) / F_PI; + idx = mini(fastf2i(ev), evcount-1); + + *mu = ev - idx; + return idx; } -/* Calculate the azimuth indices given the polar azimuth in radians. This - * will return two indices between 0 and (azcount - 1) and an interpolation - * factor between 0.0 and 1.0. +/* 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. */ -static void CalcAzIndices(ALuint azcount, ALfloat az, ALuint *azidx, ALfloat *azmu) +static ALsizei CalcAzIndex(ALsizei azcount, ALfloat az, ALfloat *mu) { - az = (F_TAU + az) * azcount / F_TAU; - azidx[0] = fastf2u(az) % azcount; - azidx[1] = (azidx[0] + 1) % azcount; - *azmu = az - floorf(az); + ALsizei idx; + az = (F_TAU+az) * azcount / F_TAU; + + idx = fastf2i(az) % azcount; + *mu = az - floorf(az); + return idx; } -/* Calculates static HRIR coefficients and delays for the given polar - * elevation and azimuth in radians. Linear interpolation is used to - * increase the apparent resolution of the HRIR data set. The coefficients - * are also normalized and attenuated by the specified gain. +/* Calculates static HRIR coefficients and delays for the given polar elevation + * and azimuth in radians. The coefficients are normalized. */ -void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays) +void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat spread, ALfloat (*coeffs)[2], ALsizei *delays) { - ALuint evidx[2], lidx[4], ridx[4]; - ALfloat mu[3], blend[4]; - ALuint i; + ALsizei evidx, azidx, idx[4]; + ALsizei evoffset; + ALfloat emu, amu[2]; + ALfloat blend[4]; + ALfloat dirfact; + ALsizei i, c; - /* Claculate elevation indices and interpolation factor. */ - CalcEvIndices(Hrtf->evCount, elevation, evidx, &mu[2]); + dirfact = 1.0f - (spread / F_TAU); - for(i = 0;i < 2;i++) + /* Claculate the lower elevation index. */ + evidx = CalcEvIndex(Hrtf->evCount, elevation, &emu); + evoffset = Hrtf->evOffset[evidx]; + + /* Calculate lower azimuth index. */ + azidx= CalcAzIndex(Hrtf->azCount[evidx], azimuth, &amu[0]); + + /* Calculate the lower HRIR indices. */ + idx[0] = evoffset + azidx; + idx[1] = evoffset + ((azidx+1) % Hrtf->azCount[evidx]); + if(evidx < Hrtf->evCount-1) { - ALuint azcount = Hrtf->azCount[evidx[i]]; - ALuint evoffset = Hrtf->evOffset[evidx[i]]; - ALuint azidx[2]; + /* Increment elevation to the next (upper) index. */ + evidx++; + evoffset = Hrtf->evOffset[evidx]; - /* Calculate azimuth indices and interpolation factor for this elevation. */ - CalcAzIndices(azcount, azimuth, azidx, &mu[i]); + /* Calculate upper azimuth index. */ + azidx = CalcAzIndex(Hrtf->azCount[evidx], azimuth, &amu[1]); - /* Calculate a set of linear HRIR indices for left and right channels. */ - lidx[i*2 + 0] = evoffset + azidx[0]; - lidx[i*2 + 1] = evoffset + azidx[1]; - ridx[i*2 + 0] = evoffset + ((azcount-azidx[0]) % azcount); - ridx[i*2 + 1] = evoffset + ((azcount-azidx[1]) % azcount); - } - - /* Calculate 4 blending weights for 2D bilinear interpolation. */ - blend[0] = (1.0f-mu[0]) * (1.0f-mu[2]); - blend[1] = ( mu[0]) * (1.0f-mu[2]); - blend[2] = (1.0f-mu[1]) * ( mu[2]); - blend[3] = ( mu[1]) * ( mu[2]); - - /* Calculate the HRIR delays using linear interpolation. */ - delays[0] = fastf2u((Hrtf->delays[lidx[0]]*blend[0] + Hrtf->delays[lidx[1]]*blend[1] + - Hrtf->delays[lidx[2]]*blend[2] + Hrtf->delays[lidx[3]]*blend[3]) * - dirfact + 0.5f) << HRTFDELAY_BITS; - delays[1] = fastf2u((Hrtf->delays[ridx[0]]*blend[0] + Hrtf->delays[ridx[1]]*blend[1] + - Hrtf->delays[ridx[2]]*blend[2] + Hrtf->delays[ridx[3]]*blend[3]) * - dirfact + 0.5f) << HRTFDELAY_BITS; - - /* Calculate the sample offsets for the HRIR indices. */ - lidx[0] *= Hrtf->irSize; - lidx[1] *= Hrtf->irSize; - lidx[2] *= Hrtf->irSize; - lidx[3] *= Hrtf->irSize; - ridx[0] *= Hrtf->irSize; - ridx[1] *= Hrtf->irSize; - ridx[2] *= Hrtf->irSize; - ridx[3] *= Hrtf->irSize; - - /* Calculate the normalized and attenuated HRIR coefficients using linear - * interpolation when there is enough gain to warrant it. Zero the - * coefficients if gain is too low. - */ - if(gain > 0.0001f) - { - ALfloat c; - - i = 0; - c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] + - Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]); - coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain * (1.0f/32767.0f); - c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] + - Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]); - coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain * (1.0f/32767.0f); - - for(i = 1;i < Hrtf->irSize;i++) - { - c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] + - Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]); - coeffs[i][0] = lerp(0.0f, c, dirfact) * gain * (1.0f/32767.0f); - c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] + - Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]); - coeffs[i][1] = lerp(0.0f, c, dirfact) * gain * (1.0f/32767.0f); - } + /* Calculate the upper HRIR indices. */ + idx[2] = evoffset + azidx; + idx[3] = evoffset + ((azidx+1) % Hrtf->azCount[evidx]); } else { - for(i = 0;i < Hrtf->irSize;i++) - { - coeffs[i][0] = 0.0f; - coeffs[i][1] = 0.0f; - } - } -} - -/* Calculates the moving HRIR target coefficients, target delays, and - * stepping values for the given polar elevation and azimuth in radians. - * Linear interpolation is used to increase the apparent resolution of the - * HRIR data set. The coefficients are also normalized and attenuated by the - * specified gain. Stepping resolution and count is determined using the - * given delta factor between 0.0 and 1.0. - */ -ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat delta, ALint counter, ALfloat (*coeffs)[2], ALuint *delays, ALfloat (*coeffStep)[2], ALint *delayStep) -{ - ALuint evidx[2], lidx[4], ridx[4]; - ALfloat mu[3], blend[4]; - ALfloat left, right; - ALfloat steps; - ALuint i; - - /* Claculate elevation indices and interpolation factor. */ - CalcEvIndices(Hrtf->evCount, elevation, evidx, &mu[2]); - - for(i = 0;i < 2;i++) - { - ALuint azcount = Hrtf->azCount[evidx[i]]; - ALuint evoffset = Hrtf->evOffset[evidx[i]]; - ALuint azidx[2]; - - /* Calculate azimuth indices and interpolation factor for this elevation. */ - CalcAzIndices(azcount, azimuth, azidx, &mu[i]); - - /* Calculate a set of linear HRIR indices for left and right channels. */ - lidx[i*2 + 0] = evoffset + azidx[0]; - lidx[i*2 + 1] = evoffset + azidx[1]; - ridx[i*2 + 0] = evoffset + ((azcount-azidx[0]) % azcount); - ridx[i*2 + 1] = evoffset + ((azcount-azidx[1]) % azcount); + /* 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 the stepping parameters. - steps = maxf(floorf(delta*Hrtf->sampleRate + 0.5f), 1.0f); - delta = 1.0f / steps; - - /* Calculate 4 blending weights for 2D bilinear interpolation. */ - blend[0] = (1.0f-mu[0]) * (1.0f-mu[2]); - blend[1] = ( mu[0]) * (1.0f-mu[2]); - blend[2] = (1.0f-mu[1]) * ( mu[2]); - blend[3] = ( mu[1]) * ( mu[2]); - - /* Calculate the HRIR delays using linear interpolation. Then calculate - * the delay stepping values using the target and previous running - * delays. + /* Calculate bilinear blending weights, attenuated according to the + * directional panning factor. */ - left = (ALfloat)(delays[0] - (delayStep[0] * counter)); - right = (ALfloat)(delays[1] - (delayStep[1] * counter)); + 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; - delays[0] = fastf2u((Hrtf->delays[lidx[0]]*blend[0] + Hrtf->delays[lidx[1]]*blend[1] + - Hrtf->delays[lidx[2]]*blend[2] + Hrtf->delays[lidx[3]]*blend[3]) * - dirfact + 0.5f) << HRTFDELAY_BITS; - delays[1] = fastf2u((Hrtf->delays[ridx[0]]*blend[0] + Hrtf->delays[ridx[1]]*blend[1] + - Hrtf->delays[ridx[2]]*blend[2] + Hrtf->delays[ridx[3]]*blend[3]) * - dirfact + 0.5f) << HRTFDELAY_BITS; - - delayStep[0] = fastf2i(delta * (delays[0] - left)); - delayStep[1] = fastf2i(delta * (delays[1] - right)); + /* Calculate the blended HRIR delays. */ + delays[0] = fastf2i( + Hrtf->delays[idx[0]][0]*blend[0] + Hrtf->delays[idx[1]][0]*blend[1] + + Hrtf->delays[idx[2]][0]*blend[2] + Hrtf->delays[idx[3]][0]*blend[3] + 0.5f + ); + delays[1] = fastf2i( + Hrtf->delays[idx[0]][1]*blend[0] + Hrtf->delays[idx[1]][1]*blend[1] + + Hrtf->delays[idx[2]][1]*blend[2] + Hrtf->delays[idx[3]][1]*blend[3] + 0.5f + ); /* Calculate the sample offsets for the HRIR indices. */ - lidx[0] *= Hrtf->irSize; - lidx[1] *= Hrtf->irSize; - lidx[2] *= Hrtf->irSize; - lidx[3] *= Hrtf->irSize; - ridx[0] *= Hrtf->irSize; - ridx[1] *= Hrtf->irSize; - ridx[2] *= Hrtf->irSize; - ridx[3] *= Hrtf->irSize; + idx[0] *= Hrtf->irSize; + idx[1] *= Hrtf->irSize; + idx[2] *= Hrtf->irSize; + idx[3] *= Hrtf->irSize; - /* Calculate the normalized and attenuated target HRIR coefficients using - * linear interpolation when there is enough gain to warrant it. Zero - * the target coefficients if gain is too low. Then calculate the - * coefficient stepping values using the target and previous running - * coefficients. - */ - if(gain > 0.0001f) + /* 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++) { - ALfloat c; - - i = 0; - left = coeffs[i][0] - (coeffStep[i][0] * counter); - right = coeffs[i][1] - (coeffStep[i][1] * counter); - - c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] + - Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]); - coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain * (1.0f/32767.0f); - c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] + - Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]); - coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain * (1.0f/32767.0f); - - coeffStep[i][0] = delta * (coeffs[i][0] - left); - coeffStep[i][1] = delta * (coeffs[i][1] - right); - - for(i = 1;i < Hrtf->irSize;i++) - { - left = coeffs[i][0] - (coeffStep[i][0] * counter); - right = coeffs[i][1] - (coeffStep[i][1] * counter); - - c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] + - Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]); - coeffs[i][0] = lerp(0.0f, c, dirfact) * gain * (1.0f/32767.0f); - c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] + - Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]); - coeffs[i][1] = lerp(0.0f, c, dirfact) * gain * (1.0f/32767.0f); - - coeffStep[i][0] = delta * (coeffs[i][0] - left); - coeffStep[i][1] = delta * (coeffs[i][1] - right); - } + coeffs[i][0] = 0.0f; + coeffs[i][1] = 0.0f; } - else + for(c = 0;c < 4;c++) { for(i = 0;i < Hrtf->irSize;i++) { - left = coeffs[i][0] - (coeffStep[i][0] * counter); - right = coeffs[i][1] - (coeffStep[i][1] * counter); - - coeffs[i][0] = 0.0f; - coeffs[i][1] = 0.0f; - - coeffStep[i][0] = delta * -left; - coeffStep[i][1] = delta * -right; + coeffs[i][0] += Hrtf->coeffs[idx[c]+i][0] * blend[c]; + coeffs[i][1] += Hrtf->coeffs[idx[c]+i][1] * blend[c]; } } - - /* The stepping count is the number of samples necessary for the HRIR to - * complete its transition. The mixer will only apply stepping for this - * many samples. - */ - return fastf2u(steps); } -/* Calculates HRTF coefficients for B-Format channels (only up to first-order). - * Note that these will decode a B-Format output mix, which uses FuMa ordering - * and scaling, not N3D! - */ -void GetBFormatHrtfCoeffs(const struct Hrtf *Hrtf, const ALuint num_chans, ALfloat (**coeffs_list)[2], ALuint **delay_list) +ALsizei BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei NumChannels, const ALfloat (*restrict AmbiPoints)[2], const ALfloat (*restrict AmbiMatrix)[2][MAX_AMBI_COEFFS], ALsizei AmbiCount) { - ALuint elev_idx, azi_idx; - ALfloat scale; - ALuint i, c; +/* 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; + ALsizei idx[HRTF_AMBI_MAX_CHANNELS]; + ALsizei min_delay = HRTF_HISTORY_LENGTH; + ALfloat temps[3][HRIR_LENGTH]; + ALsizei max_length = 0; + ALsizei i, c, b; - assert(num_chans <= 4); - - for(c = 0;c < num_chans;c++) + for(c = 0;c < AmbiCount;c++) { - ALfloat (*coeffs)[2] = coeffs_list[c]; - ALuint *delay = delay_list[c]; + ALuint evidx, azidx; + ALuint evoffset; + ALuint azcount; - for(i = 0;i < Hrtf->irSize;i++) - { - coeffs[i][0] = 0.0f; - coeffs[i][1] = 0.0f; - } - delay[0] = 0; - delay[1] = 0; + /* Calculate elevation index. */ + evidx = (ALsizei)floorf((F_PI_2 + AmbiPoints[c][0]) * + (Hrtf->evCount-1)/F_PI + 0.5f); + evidx = mini(evidx, Hrtf->evCount-1); + + azcount = Hrtf->azCount[evidx]; + evoffset = Hrtf->evOffset[evidx]; + + /* Calculate azimuth index for this elevation. */ + azidx = (ALsizei)floorf((F_TAU+AmbiPoints[c][1]) * + azcount/F_TAU + 0.5f) % azcount; + + /* Calculate indices for left and right channels. */ + idx[c] = evoffset + azidx; + + min_delay = mini(min_delay, mini(Hrtf->delays[idx[c]][0], Hrtf->delays[idx[c]][1])); } - /* NOTE: HRTF coefficients are generated by combining all the HRIRs in the - * dataset, with each entry scaled according to how much it contributes to - * the given B-Format channel based on its direction (including negative - * contributions!). - */ - scale = 0.0f; - for(elev_idx = 0;elev_idx < Hrtf->evCount;elev_idx++) + memset(temps, 0, sizeof(temps)); + bandsplit_init(&splitter, 400.0f / (ALfloat)Hrtf->sampleRate); + for(c = 0;c < AmbiCount;c++) { - ALfloat elev = (ALfloat)elev_idx/(ALfloat)(Hrtf->evCount-1)*F_PI - F_PI_2; - ALuint evoffset = Hrtf->evOffset[elev_idx]; - ALuint azcount = Hrtf->azCount[elev_idx]; + 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; - scale += (ALfloat)azcount; + max_length = maxi(max_length, + mini(maxi(ldelay, rdelay) + Hrtf->irSize, HRIR_LENGTH) + ); - for(azi_idx = 0;azi_idx < azcount;azi_idx++) + if(NUM_BANDS == 1) { - ALuint lidx, ridx; - ALfloat ambi_coeffs[4]; - ALfloat az, gain; - ALfloat x, y, z; - - lidx = evoffset + azi_idx; - ridx = evoffset + ((azcount-azi_idx) % azcount); - - az = (ALfloat)azi_idx / (ALfloat)azcount * F_TAU; - if(az > F_PI) az -= F_TAU; - - x = cosf(-az) * cosf(elev); - y = sinf(-az) * cosf(elev); - z = sinf(elev); - - ambi_coeffs[0] = 1.414213562f; - ambi_coeffs[1] = x; - ambi_coeffs[2] = y; - ambi_coeffs[3] = z; - - for(c = 0;c < num_chans;c++) + for(i = 0;i < NumChannels;++i) { - ALfloat (*coeffs)[2] = coeffs_list[c]; - ALuint *delay = delay_list[c]; - - /* NOTE: Always include the total delay average since the - * channels need to have matching delays. */ - delay[0] += Hrtf->delays[lidx]; - delay[1] += Hrtf->delays[ridx]; - - gain = ambi_coeffs[c]; - if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) - continue; - - for(i = 0;i < Hrtf->irSize;i++) + ALsizei lidx = ldelay, ridx = rdelay; + ALsizei j = 0; + while(lidx < HRIR_LENGTH && ridx < HRIR_LENGTH && j < Hrtf->irSize) { - coeffs[i][0] += Hrtf->coeffs[lidx*Hrtf->irSize + i]*(1.0f/32767.0f) * gain; - coeffs[i][1] += Hrtf->coeffs[ridx*Hrtf->irSize + i]*(1.0f/32767.0f) * gain; + state->Chan[i].Coeffs[lidx++][0] += fir[j][0] * AmbiMatrix[c][0][i]; + state->Chan[i].Coeffs[ridx++][1] += fir[j][1] * AmbiMatrix[c][0][i]; + j++; + } + } + } + else + { + /* 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][0]; + bandsplit_process(&splitter, temps[0], temps[1], temps[2], HRIR_LENGTH); + + /* Apply left ear response with delay. */ + for(i = 0;i < NumChannels;++i) + { + for(b = 0;b < NUM_BANDS;b++) + { + ALsizei lidx = ldelay; + ALsizei j = 0; + while(lidx < HRIR_LENGTH) + state->Chan[i].Coeffs[lidx++][0] += temps[b][j++] * AmbiMatrix[c][b][i]; + } + } + + /* 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][1]; + bandsplit_process(&splitter, temps[0], temps[1], temps[2], HRIR_LENGTH); + + /* Apply right ear response with delay. */ + for(i = 0;i < NumChannels;++i) + { + for(b = 0;b < NUM_BANDS;b++) + { + ALsizei ridx = rdelay; + ALsizei j = 0; + while(ridx < HRIR_LENGTH) + state->Chan[i].Coeffs[ridx++][1] += temps[b][j++] * AmbiMatrix[c][b][i]; } } } } + /* Round up to the next IR size multiple. */ + max_length = RoundUp(max_length, MOD_IR_SIZE); - scale = 1.0f/scale; - - for(c = 0;c < num_chans;c++) - { - ALfloat (*coeffs)[2] = coeffs_list[c]; - ALuint *delay = delay_list[c]; - - for(i = 0;i < Hrtf->irSize;i++) - { - coeffs[i][0] *= scale; - coeffs[i][1] *= scale; - } - delay[0] = minu((ALuint)((ALfloat)delay[0] * scale), HRTF_HISTORY_LENGTH-1); - delay[0] <<= HRTFDELAY_BITS; - delay[1] = minu((ALuint)((ALfloat)delay[1] * scale), HRTF_HISTORY_LENGTH-1); - delay[1] <<= HRTFDELAY_BITS; - } + TRACE("Skipped min delay: %d, new combined length: %d\n", min_delay, max_length); + return max_length; +#undef NUM_BANDS } -static struct Hrtf *LoadHrtf00(FILE *f) +static struct Hrtf *CreateHrtfStore(ALuint rate, ALsizei irSize, 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->evCount = evCount; + + /* Set up pointers to storage following the main HRTF struct. */ + _azCount = (ALubyte*)(base + offset); Hrtf->azCount = _azCount; + offset += sizeof(_azCount[0])*evCount; + + offset = RoundUp(offset, sizeof(ALushort)); /* Align for ushort fields */ + _evOffset = (ALushort*)(base + offset); Hrtf->evOffset = _evOffset; + offset += sizeof(_evOffset[0])*evCount; + + offset = RoundUp(offset, 16); /* Align for coefficients using SIMD */ + _coeffs = (ALfloat(*)[2])(base + offset); Hrtf->coeffs = _coeffs; + offset += sizeof(_coeffs[0])*irSize*irCount; + + _delays = (ALubyte(*)[2])(base + offset); Hrtf->delays = _delays; + offset += sizeof(_delays[0])*irCount; + + /* 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]; + } + + assert(offset == total); + } + + 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_ALuint(const ALubyte **data, size_t *len) +{ + ALint 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; - ALubyte *delays = NULL; - ALuint i, j; + ALfloat (*coeffs)[2] = NULL; + ALubyte (*delays)[2] = NULL; + ALsizei i, j; - rate = fgetc(f); - rate |= fgetc(f)<<8; - rate |= fgetc(f)<<16; - rate |= fgetc(f)<<24; + if(datalen < 9) + { + ERR("Unexpected end of %s data (req %d, rem "SZFMT")\n", filename, 9, datalen); + return NULL; + } - irCount = fgetc(f); - irCount |= fgetc(f)<<8; + rate = GetLE_ALuint(&data, &datalen); - irSize = fgetc(f); - irSize |= fgetc(f)<<8; + irCount = GetLE_ALushort(&data, &datalen); - evCount = fgetc(f); + irSize = GetLE_ALushort(&data, &datalen); + + evCount = GetLE_ALubyte(&data, &datalen); if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE)) { @@ -459,10 +438,15 @@ static struct Hrtf *LoadHrtf00(FILE *f) evCount, MIN_EV_COUNT, MAX_EV_COUNT); failed = AL_TRUE; } - if(failed) return NULL; + if(datalen < evCount*2u) + { + ERR("Unexpected end of %s data (req %d, rem "SZFMT")\n", filename, evCount*2, datalen); + return NULL; + } + azCount = malloc(sizeof(azCount[0])*evCount); evOffset = malloc(sizeof(evOffset[0])*evCount); if(azCount == NULL || evOffset == NULL) @@ -473,12 +457,10 @@ static struct Hrtf *LoadHrtf00(FILE *f) if(!failed) { - evOffset[0] = fgetc(f); - evOffset[0] |= fgetc(f)<<8; + evOffset[0] = GetLE_ALushort(&data, &datalen); for(i = 1;i < evCount;i++) { - evOffset[i] = fgetc(f); - evOffset[i] |= fgetc(f)<<8; + evOffset[i] = GetLE_ALushort(&data, &datalen); if(evOffset[i] <= evOffset[i-1]) { ERR("Invalid evOffset: evOffset[%d]=%d (last=%d)\n", @@ -523,86 +505,90 @@ static struct Hrtf *LoadHrtf00(FILE *f) if(!failed) { - for(i = 0;i < irCount*irSize;i+=irSize) + size_t reqsize = 2*irSize*irCount + irCount; + if(datalen < reqsize) { - for(j = 0;j < irSize;j++) - { - ALshort coeff; - coeff = fgetc(f); - coeff |= fgetc(f)<<8; - coeffs[i+j] = coeff; - } + ERR("Unexpected end of %s data (req "SZFMT", rem "SZFMT")\n", + filename, reqsize, datalen); + failed = AL_TRUE; } + } + + if(!failed) + { for(i = 0;i < irCount;i++) { - delays[i] = fgetc(f); - 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] > maxDelay) { - ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i], maxDelay); + ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i][0], maxDelay); failed = AL_TRUE; } } - - if(feof(f)) - { - ERR("Premature end of data\n"); - failed = AL_TRUE; - } } if(!failed) { - Hrtf = malloc(sizeof(struct Hrtf)); - if(Hrtf == NULL) + /* 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; - if(!failed) - { - Hrtf->sampleRate = rate; - Hrtf->irSize = irSize; - Hrtf->evCount = evCount; - Hrtf->azCount = azCount; - Hrtf->evOffset = evOffset; - Hrtf->coeffs = coeffs; - Hrtf->delays = delays; - AL_STRING_INIT(Hrtf->filename); - Hrtf->next = NULL; - return Hrtf; + 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, evCount, irCount, azCount, + evOffset, coeffs, delays, filename); } free(azCount); free(evOffset); free(coeffs); free(delays); - return NULL; + return Hrtf; } - -static struct Hrtf *LoadHrtf01(FILE *f) +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; - ALubyte *azCount = NULL; + ALuint rate = 0; + ALushort irCount = 0; + ALushort irSize = 0; + ALubyte evCount = 0; + const ALubyte *azCount = NULL; ALushort *evOffset = NULL; - ALshort *coeffs = NULL; - ALubyte *delays = NULL; - ALuint i, j; + ALfloat (*coeffs)[2] = NULL; + ALubyte (*delays)[2] = NULL; + ALsizei i, j; - rate = fgetc(f); - rate |= fgetc(f)<<8; - rate |= fgetc(f)<<16; - rate |= fgetc(f)<<24; + if(datalen < 6) + { + ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", filename, 6, datalen); + return NULL; + } - irSize = fgetc(f); + rate = GetLE_ALuint(&data, &datalen); - evCount = fgetc(f); + irSize = GetLE_ALubyte(&data, &datalen); + + evCount = GetLE_ALubyte(&data, &datalen); if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE)) { @@ -616,11 +602,17 @@ static struct Hrtf *LoadHrtf01(FILE *f) evCount, MIN_EV_COUNT, MAX_EV_COUNT); failed = AL_TRUE; } - if(failed) return NULL; - azCount = malloc(sizeof(azCount[0])*evCount); + 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); + evOffset = malloc(sizeof(evOffset[0])*evCount); if(azCount == NULL || evOffset == NULL) { @@ -632,7 +624,6 @@ static struct Hrtf *LoadHrtf01(FILE *f) { for(i = 0;i < evCount;i++) { - azCount[i] = fgetc(f); if(azCount[i] < MIN_AZ_COUNT || azCount[i] > MAX_AZ_COUNT) { ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n", @@ -663,238 +654,501 @@ static struct Hrtf *LoadHrtf01(FILE *f) if(!failed) { - for(i = 0;i < irCount*irSize;i+=irSize) + size_t reqsize = 2*irSize*irCount + irCount; + if(datalen < reqsize) { - for(j = 0;j < irSize;j++) - { - ALshort coeff; - coeff = fgetc(f); - coeff |= fgetc(f)<<8; - coeffs[i+j] = coeff; - } + ERR("Unexpected end of %s data (req "SZFMT", rem "SZFMT"\n", + filename, reqsize, datalen); + failed = AL_TRUE; } + } + + if(!failed) + { for(i = 0;i < irCount;i++) { - delays[i] = fgetc(f); - 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] > maxDelay) { - ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i], maxDelay); + ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i][0], maxDelay); failed = AL_TRUE; } } - - if(feof(f)) - { - ERR("Premature end of data\n"); - failed = AL_TRUE; - } } if(!failed) { - Hrtf = malloc(sizeof(struct Hrtf)); - if(Hrtf == NULL) + /* 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]; + } } + + Hrtf = CreateHrtfStore(rate, irSize, evCount, irCount, azCount, + evOffset, coeffs, delays, filename); } - if(!failed) - { - Hrtf->sampleRate = rate; - Hrtf->irSize = irSize; - Hrtf->evCount = evCount; - Hrtf->azCount = azCount; - Hrtf->evOffset = evOffset; - Hrtf->coeffs = coeffs; - Hrtf->delays = delays; - AL_STRING_INIT(Hrtf->filename); - Hrtf->next = NULL; - return Hrtf; - } - - free(azCount); free(evOffset); free(coeffs); free(delays); - return NULL; + 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(), *filename, NULL }; - HrtfEntry *iter; + EnumeratedHrtf entry = { AL_STRING_INIT_STATIC(), NULL }; + struct HrtfEntry *loaded_entry; + const EnumeratedHrtf *iter; const char *name; + const char *ext; int i; - name = strrchr(al_string_get_cstr(entry.filename), '/'); - if(!name) name = strrchr(al_string_get_cstr(entry.filename), '\\'); - if(!name) name = al_string_get_cstr(entry.filename); - else ++name; - - entry.hrtf = LoadedHrtfs; - while(entry.hrtf) + /* Check if this file has already been loaded globally. */ + loaded_entry = LoadedHrtfs; + while(loaded_entry) { - if(al_string_cmp(entry.filename, entry.hrtf->filename) == 0) + 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 + break; - entry.hrtf = entry.hrtf->next; + } + loaded_entry = loaded_entry->next; } - if(!entry.hrtf) + if(!loaded_entry) { - struct Hrtf *hrtf = NULL; - ALchar magic[8]; - FILE *f; + TRACE("Got new file \"%s\"\n", alstr_get_cstr(filename)); - TRACE("Loading %s...\n", al_string_get_cstr(entry.filename)); - f = al_fopen(al_string_get_cstr(entry.filename), "rb"); - if(f == NULL) - { - ERR("Could not open %s\n", al_string_get_cstr(entry.filename)); - goto error; - } - - if(fread(magic, 1, sizeof(magic), f) != sizeof(magic)) - ERR("Failed to read header from %s\n", al_string_get_cstr(entry.filename)); - else - { - if(memcmp(magic, magicMarker00, sizeof(magicMarker00)) == 0) - { - TRACE("Detected data set format v0\n"); - hrtf = LoadHrtf00(f); - } - else if(memcmp(magic, magicMarker01, sizeof(magicMarker01)) == 0) - { - TRACE("Detected data set format v1\n"); - hrtf = LoadHrtf01(f); - } - else - ERR("Invalid header in %s: \"%.8s\"\n", al_string_get_cstr(entry.filename), magic); - } - fclose(f); - - if(!hrtf) - { - ERR("Failed to load %s\n", al_string_get_cstr(entry.filename)); - goto error; - } - - al_string_copy(&hrtf->filename, entry.filename); - hrtf->next = LoadedHrtfs; - LoadedHrtfs = hrtf; - TRACE("Loaded HRTF support for format: %s %uhz\n", - DevFmtChannelsString(DevFmtStereo), hrtf->sampleRate); - entry.hrtf = hrtf; + loaded_entry = al_calloc(DEF_ALIGN, + FAM_SIZE(struct HrtfEntry, filename, alstr_length(filename)+1) + ); + loaded_entry->next = LoadedHrtfs; + loaded_entry->handle = NULL; + strcpy(loaded_entry->filename, alstr_get_cstr(filename)); + LoadedHrtfs = loaded_entry; } /* 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, '.'); i = 0; do { - al_string_copy_cstr(&entry.name, name); + 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, 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_ITER_END(*list)); + } 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(entry.filename)); + TRACE("Adding entry \"%s\" from file \"%s\"\n", alstr_get_cstr(entry.name), + alstr_get_cstr(filename)); VECTOR_PUSH_BACK(*list, entry); - return; - -error: - al_string_deinit(&entry.filename); } -vector_HrtfEntry EnumerateHrtf(const_al_string devname) +/* Unfortunate that we have to duplicate AddFileEntry to take a memory buffer + * for input instead of opening the given filename. + */ +static void AddBuiltInEntry(vector_EnumeratedHrtf *list, const_al_string filename, size_t residx) { - vector_HrtfEntry list = VECTOR_INIT_STATIC(); - const char *fnamelist = "%s.mhr"; + EnumeratedHrtf entry = { AL_STRING_INIT_STATIC(), NULL }; + struct HrtfEntry *loaded_entry; + struct Hrtf *hrtf = NULL; + const EnumeratedHrtf *iter; + const char *name; + const char *ext; + int i; - ConfigValueStr(al_string_get_cstr(devname), NULL, "hrtf_tables", &fnamelist); - while(fnamelist && *fnamelist) + loaded_entry = LoadedHrtfs; + while(loaded_entry) { - while(isspace(*fnamelist) || *fnamelist == ',') - fnamelist++; - if(*fnamelist != '\0') + 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 + + break; + } + loaded_entry = loaded_entry->next; + } + + if(!loaded_entry) + { + size_t namelen = alstr_length(filename)+32; + + TRACE("Got new file \"%s\"\n", alstr_get_cstr(filename)); + + loaded_entry = al_calloc(DEF_ALIGN, + FAM_SIZE(struct HrtfEntry, filename, namelen) + ); + loaded_entry->next = LoadedHrtfs; + loaded_entry->handle = hrtf; + snprintf(loaded_entry->filename, namelen, "!"SZFMT"_%s", + residx, alstr_get_cstr(filename)); + LoadedHrtfs = loaded_entry; + } + + /* 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, '.'); + + i = 0; + do { + 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); + alstr_append_cstr(&entry.name, str); + } + ++i; + +#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", alstr_get_cstr(entry.name)); + VECTOR_PUSH_BACK(*list, entry); +} + + +#define IDR_DEFAULT_44100_MHR 1 +#define IDR_DEFAULT_48000_MHR 2 + +#ifndef ALSOFT_EMBED_HRTF_DATA + +static const ALubyte *GetResource(int UNUSED(name), size_t *size) +{ + *size = 0; + return NULL; +} + +#else + +#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) + { + *size = sizeof(hrtf_default_44100); + return hrtf_default_44100; + } + if(name == IDR_DEFAULT_48000_MHR) + { + *size = sizeof(hrtf_default_48000); + return hrtf_default_48000; + } + *size = 0; + return NULL; +} +#endif + +vector_EnumeratedHrtf EnumerateHrtf(const_al_string devname) +{ + vector_EnumeratedHrtf list = VECTOR_INIT_STATIC(); + const char *defaulthrtf = ""; + const char *pathlist = ""; + bool usedefaults = true; + + if(ConfigValueStr(alstr_get_cstr(devname), NULL, "hrtf-paths", &pathlist)) + { + al_string pname = AL_STRING_INIT_STATIC(); + while(pathlist && *pathlist) { const char *next, *end; - next = strchr(fnamelist, ','); - if(!next) - end = fnamelist + strlen(fnamelist); - else + while(isspace(*pathlist) || *pathlist == ',') + pathlist++; + if(*pathlist == '\0') + continue; + + next = strchr(pathlist, ','); + if(next) end = next++; - - while(end != fnamelist && isspace(*(end-1))) - --end; - if(end != fnamelist) + else { - al_string fname = AL_STRING_INIT_STATIC(); - vector_al_string flist; - - al_string_append_range(&fname, fnamelist, end); - - flist = SearchDataFiles(al_string_get_cstr(fname), "openal/hrtf"); - VECTOR_FOR_EACH_PARAMS(al_string, flist, AddFileEntry, &list); - VECTOR_DEINIT(flist); - - al_string_deinit(&fname); + end = pathlist + strlen(pathlist); + usedefaults = false; } - fnamelist = next; + while(end != pathlist && isspace(*(end-1))) + --end; + if(end != pathlist) + { + vector_al_string flist; + size_t i; + + alstr_copy_range(&pname, pathlist, end); + + 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); + } + + pathlist = next; + } + + alstr_reset(&pname); + } + 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, i; + + flist = SearchDataFiles(".mhr", "openal/hrtf"); + 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) + { + 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) + { + alstr_copy_cstr(&ename, "Built-In 48000hz"); + AddBuiltInEntry(&list, ename, IDR_DEFAULT_48000_MHR); + } + alstr_reset(&ename); + } + + if(VECTOR_SIZE(list) > 1 && ConfigValueStr(alstr_get_cstr(devname), NULL, "default-hrtf", &defaulthrtf)) + { + const EnumeratedHrtf *iter; + /* Find the preferred HRTF and move it to the front of the list. */ +#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)) + { + EnumeratedHrtf entry = *iter; + memmove(&VECTOR_ELEM(list,1), &VECTOR_ELEM(list,0), + (iter-VECTOR_BEGIN(list))*sizeof(EnumeratedHrtf)); + VECTOR_ELEM(list,0) = entry; } } return list; } -void FreeHrtfList(vector_HrtfEntry *list) +void FreeHrtfList(vector_EnumeratedHrtf *list) { -#define CLEAR_ENTRY(i) do { \ - al_string_deinit(&(i)->name); \ - al_string_deinit(&(i)->filename); \ -} 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 } - -ALuint GetHrtfSampleRate(const struct Hrtf *Hrtf) +struct Hrtf *GetLoadedHrtf(struct HrtfEntry *entry) { - return Hrtf->sampleRate; + struct Hrtf *hrtf = NULL; + struct FileMapping fmap; + const ALubyte *rdata; + const char *name; + size_t 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, "!"SZFMT"%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 "SZFMT", %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(magicMarker01)) + ERR("%s data is too short ("SZFMT" bytes)\n", name, rsize); + 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; } -ALuint GetHrtfIrSize(const struct Hrtf *Hrtf) + +void Hrtf_IncRef(struct Hrtf *hrtf) { - return Hrtf->irSize; + 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 = NULL; + struct HrtfEntry *Hrtf = LoadedHrtfs; + LoadedHrtfs = NULL; - while((Hrtf=LoadedHrtfs) != NULL) + while(Hrtf != NULL) { - LoadedHrtfs = Hrtf->next; - free((void*)Hrtf->azCount); - free((void*)Hrtf->evOffset); - free((void*)Hrtf->coeffs); - free((void*)Hrtf->delays); - al_string_deinit(&Hrtf->filename); - free(Hrtf); + struct HrtfEntry *next = Hrtf->next; + al_free(Hrtf->handle); + al_free(Hrtf); + Hrtf = next; } } diff --git a/libs/openal-soft/Alc/hrtf.h b/libs/openal-soft/Alc/hrtf.h index 6dcd6948..5de28f90 100644 --- a/libs/openal-soft/Alc/hrtf.h +++ b/libs/openal-soft/Alc/hrtf.h @@ -4,37 +4,49 @@ #include "AL/al.h" #include "AL/alc.h" +#include "alMain.h" #include "alstring.h" +#include "atomic.h" -enum DevFmtChannels; -struct Hrtf; +/* The maximum number of virtual speakers used to generate HRTF coefficients + * for decoding B-Format. + */ +#define HRTF_AMBI_MAX_CHANNELS 16 -typedef struct HrtfEntry { - al_string name; - al_string filename; - const struct Hrtf *hrtf; -} HrtfEntry; -TYPEDEF_VECTOR(HrtfEntry, vector_HrtfEntry) +struct HrtfEntry; + +struct Hrtf { + RefCount ref; + + ALuint sampleRate; + ALsizei irSize; + ALubyte evCount; + + const ALubyte *azCount; + const ALushort *evOffset; + const ALfloat (*coeffs)[2]; + const ALubyte (*delays)[2]; +}; -#define HRIR_BITS (7) -#define HRIR_LENGTH (1< + +#include "alu.h" +#include "almalloc.h" + +#define RMS_WINDOW_SIZE (1<<7) +#define RMS_WINDOW_MASK (RMS_WINDOW_SIZE-1) +#define RMS_VALUE_MAX (1<<24) + +#define LOOKAHEAD_SIZE (1<<13) +#define LOOKAHEAD_MASK (LOOKAHEAD_SIZE-1) + +static_assert(RMS_VALUE_MAX < (UINT_MAX / RMS_WINDOW_SIZE), "RMS_VALUE_MAX is too big"); + +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; + +/* 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 = maxf(-6.0f, log10f(Comp->Envelope[i])); + 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; +} + +ALuint GetCompressorSampleRate(const Compressor *Comp) +{ + return Comp->SampleRate; +} + +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/libs/openal-soft/Alc/mixer.c b/libs/openal-soft/Alc/mixer.c index 712075f1..b5c4605b 100644 --- a/libs/openal-soft/Alc/mixer.c +++ b/libs/openal-soft/Alc/mixer.c @@ -41,65 +41,83 @@ 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 *frac_arr, ALuint *pos_arr, ALuint size); - -alignas(16) union ResamplerCoeffs ResampleCoeffs; +extern inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size); -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!"); +/* BSinc requires up to 11 extra samples before the current position, and 12 after. */ +static_assert(MAX_PRE_SAMPLES >= 11, "MAX_PRE_SAMPLES must be at least 11!"); +static_assert(MAX_POST_SAMPLES >= 12, "MAX_POST_SAMPLES must be at least 12!"); -static HrtfMixerFunc MixHrtfSamples = MixHrtf_C; +enum Resampler ResamplerDefault = LinearResampler; + static MixerFunc MixSamples = Mix_C; -static ResamplerFunc ResampleSamples = Resample_point32_C; +static HrtfMixerFunc MixHrtfSamples = MixHrtf_C; +HrtfMixerBlendFunc MixHrtfBlendSamples = MixHrtfBlend_C; -static inline HrtfMixerFunc SelectHrtfMixer(void) +MixerFunc SelectMixer(void) { -#ifdef HAVE_SSE - if((CPUCapFlags&CPU_CAP_SSE)) - return MixHrtf_SSE; -#endif -#ifdef HAVE_NEON - if((CPUCapFlags&CPU_CAP_NEON)) - return MixHrtf_Neon; -#endif - - return MixHrtf_C; -} - -static inline MixerFunc SelectMixer(void) -{ -#ifdef HAVE_SSE - if((CPUCapFlags&CPU_CAP_SSE)) - return Mix_SSE; -#endif #ifdef HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return Mix_Neon; #endif - +#ifdef HAVE_SSE + if((CPUCapFlags&CPU_CAP_SSE)) + return Mix_SSE; +#endif return Mix_C; } -static inline ResamplerFunc SelectResampler(enum Resampler resampler) +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_point32_C; case LinearResampler: +#ifdef HAVE_NEON + if((CPUCapFlags&CPU_CAP_NEON)) + return Resample_lerp32_Neon; +#endif #ifdef HAVE_SSE4_1 if((CPUCapFlags&CPU_CAP_SSE4_1)) return Resample_lerp32_SSE41; @@ -110,6 +128,10 @@ static inline ResamplerFunc SelectResampler(enum Resampler resampler) #endif return Resample_lerp32_C; case FIR4Resampler: +#ifdef HAVE_NEON + if((CPUCapFlags&CPU_CAP_NEON)) + return Resample_fir4_32_Neon; +#endif #ifdef HAVE_SSE4_1 if((CPUCapFlags&CPU_CAP_SSE4_1)) return Resample_fir4_32_SSE41; @@ -119,17 +141,11 @@ static inline ResamplerFunc SelectResampler(enum Resampler resampler) 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_NEON + if((CPUCapFlags&CPU_CAP_NEON)) + return Resample_bsinc32_Neon; +#endif #ifdef HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) return Resample_bsinc32_SSE; @@ -141,162 +157,55 @@ static inline ResamplerFunc SelectResampler(enum Resampler resampler) } -/* 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; + ResamplerDefault = PointResampler; else if(strcasecmp(str, "linear") == 0) - resampler = LinearResampler; + ResamplerDefault = LinearResampler; else if(strcasecmp(str, "sinc4") == 0) - resampler = FIR4Resampler; - else if(strcasecmp(str, "sinc8") == 0) - resampler = FIR8Resampler; + ResamplerDefault = FIR4Resampler; else if(strcasecmp(str, "bsinc") == 0) - resampler = BSincResampler; - else if(strcasecmp(str, "cubic") == 0) + ResamplerDefault = BSincResampler; + else if(strcasecmp(str, "cubic") == 0 || strcasecmp(str, "sinc8") == 0) { - WARN("Resampler option \"cubic\" is deprecated, using sinc4\n"); - resampler = FIR4Resampler; + WARN("Resampler option \"%s\" is deprecated, using sinc4\n", str); + ResamplerDefault = FIR4Resampler; } else { char *end; long n = strtol(str, &end, 0); if(*end == '\0' && (n == PointResampler || n == LinearResampler || n == FIR4Resampler)) - resampler = n; + ResamplerDefault = 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); - } - + MixHrtfBlendSamples = SelectHrtfBlendMixer(); MixHrtfSamples = SelectHrtfMixer(); MixSamples = SelectMixer(); - ResampleSamples = SelectResampler(resampler); } static inline ALfloat Sample_ALbyte(ALbyte val) -{ return val * (1.0f/127.0f); } +{ return val * (1.0f/128.0f); } static inline ALfloat Sample_ALshort(ALshort val) -{ return val * (1.0f/32767.0f); } +{ return val * (1.0f/32768.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)\ +static inline void Load_##T(ALfloat *dst, const T *src, ALint srcstep, ALsizei samples)\ { \ - ALuint i; \ + ALsizei i; \ for(i = 0;i < samples;i++) \ dst[i] = Sample_##T(src[i*srcstep]); \ } @@ -307,7 +216,7 @@ DECL_TEMPLATE(ALfloat) #undef DECL_TEMPLATE -static void LoadSamples(ALfloat *dst, const ALvoid *src, ALuint srcstep, enum FmtType srctype, ALuint samples) +static void LoadSamples(ALfloat *dst, const ALvoid *src, ALint srcstep, enum FmtType srctype, ALsizei samples) { switch(srctype) { @@ -323,9 +232,9 @@ static void LoadSamples(ALfloat *dst, const ALvoid *src, ALuint srcstep, enum Fm } } -static inline void SilenceSamples(ALfloat *dst, ALuint samples) +static inline void SilenceSamples(ALfloat *dst, ALsizei samples) { - ALuint i; + ALsizei i; for(i = 0;i < samples;i++) dst[i] = 0.0f; } @@ -333,9 +242,9 @@ static inline void SilenceSamples(ALfloat *dst, ALuint samples) static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter, ALfloat *restrict dst, const ALfloat *restrict src, - ALuint numsamples, enum ActiveFilters type) + ALsizei numsamples, enum ActiveFilters type) { - ALuint i; + ALsizei i; switch(type) { case AF_None: @@ -356,7 +265,7 @@ static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter for(i = 0;i < numsamples;) { ALfloat temp[256]; - ALuint todo = minu(256, numsamples-i); + ALsizei todo = mini(256, numsamples-i); ALfilterState_process(lpfilter, temp, src+i, todo); ALfilterState_process(hpfilter, dst+i, temp, todo); @@ -368,39 +277,45 @@ static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter } -ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint SamplesToDo) +ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei SamplesToDo) { - ResamplerFunc Resample; ALbufferlistitem *BufferListItem; - ALuint DataPosInt, DataPosFrac; - ALboolean Looping; - ALuint increment; - ALenum State; - ALuint OutPos; - ALuint NumChannels; - ALuint SampleSize; + ALbufferlistitem *BufferLoopItem; + ALsizei NumChannels, SampleSize; + ResamplerFunc Resample; + ALsizei DataPosInt; + ALsizei DataPosFrac; ALint64 DataSize64; - ALuint IrSize; - ALuint chan, j; + ALint increment; + ALsizei Counter; + ALsizei OutPos; + ALsizei IrSize; + bool isplaying; + bool firstpass; + ALsizei chan; + ALsizei send; /* Get source info */ - State = Source->state; - BufferListItem = ATOMIC_LOAD(&Source->current_buffer); - DataPosInt = Source->position; - DataPosFrac = Source->position_fraction; - Looping = Source->Looping; - NumChannels = Source->NumChannels; - SampleSize = Source->SampleSize; + isplaying = true; /* Will only be called while playing. */ + 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->Hrtf ? GetHrtfIrSize(Device->Hrtf) : 0); + IrSize = (Device->HrtfHandle ? Device->HrtfHandle->irSize : 0); Resample = ((increment == FRACTIONONE && DataPosFrac == 0) ? - Resample_copy32_C : ResampleSamples); + Resample_copy32_C : voice->Resampler); + Counter = (voice->Flags&VOICE_IS_FADING) ? SamplesToDo : 0; + firstpass = true; OutPos = 0; + do { - ALuint SrcBufferSize, DstBufferSize; + ALsizei SrcBufferSize, DstBufferSize; /* Figure out how many buffer samples will be needed */ DataSize64 = SamplesToDo-OutPos; @@ -409,7 +324,7 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam DataSize64 >>= FRACTIONBITS; DataSize64 += MAX_POST_SAMPLES+MAX_PRE_SAMPLES; - SrcBufferSize = (ALuint)mini64(DataSize64, BUFFERSIZE); + SrcBufferSize = (ALsizei)mini64(DataSize64, BUFFERSIZE); /* Figure out how many samples we can actually mix from this. */ DataSize64 = SrcBufferSize; @@ -417,8 +332,8 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam DataSize64 <<= FRACTIONBITS; DataSize64 -= DataPosFrac; - DstBufferSize = (ALuint)((DataSize64+(increment-1)) / increment); - DstBufferSize = minu(DstBufferSize, (SamplesToDo-OutPos)); + DstBufferSize = (ALsizei)((DataSize64+(increment-1)) / increment); + DstBufferSize = mini(DstBufferSize, (SamplesToDo-OutPos)); /* Some mixers like having a multiple of 4, so try to give that unless * this is the last update. */ @@ -429,7 +344,7 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam { const ALfloat *ResampledData; ALfloat *SrcData = Device->SourceData; - ALuint SrcDataSize; + ALsizei SrcDataSize; /* Load the previous samples into the source data first. */ memcpy(SrcData, voice->PrevSamples[chan], MAX_PRE_SAMPLES*sizeof(ALfloat)); @@ -439,23 +354,22 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam { const ALbuffer *ALBuffer = BufferListItem->buffer; const ALubyte *Data = ALBuffer->data; - ALuint DataSize; - ALuint pos; + ALsizei DataSize; /* 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) + if(!BufferLoopItem || DataPosInt >= ALBuffer->LoopEnd) { - Looping = AL_FALSE; + BufferLoopItem = NULL; /* 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); + DataSize = minu(SrcBufferSize - SrcDataSize, + ALBuffer->SampleLen - DataPosInt); - LoadSamples(&SrcData[SrcDataSize], &Data[pos * NumChannels*SampleSize], + LoadSamples(&SrcData[SrcDataSize], &Data[DataPosInt * NumChannels*SampleSize], NumChannels, ALBuffer->FmtType, DataSize); SrcDataSize += DataSize; @@ -464,23 +378,21 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam } else { - ALuint LoopStart = ALBuffer->LoopStart; - ALuint LoopEnd = ALBuffer->LoopEnd; + ALsizei LoopStart = ALBuffer->LoopStart; + ALsizei 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); + DataSize = minu(SrcBufferSize - SrcDataSize, LoopEnd - DataPosInt); - LoadSamples(&SrcData[SrcDataSize], &Data[pos * NumChannels*SampleSize], + LoadSamples(&SrcData[SrcDataSize], &Data[DataPosInt * NumChannels*SampleSize], NumChannels, ALBuffer->FmtType, DataSize); SrcDataSize += DataSize; DataSize = LoopEnd-LoopStart; while(SrcBufferSize > SrcDataSize) { - DataSize = minu(SrcBufferSize - SrcDataSize, DataSize); + DataSize = mini(SrcBufferSize - SrcDataSize, DataSize); LoadSamples(&SrcData[SrcDataSize], &Data[LoopStart * NumChannels*SampleSize], NumChannels, ALBuffer->FmtType, DataSize); @@ -492,7 +404,7 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam { /* Crawl the buffer queue to fill in the temp buffer */ ALbufferlistitem *tmpiter = BufferListItem; - ALuint pos = DataPosInt; + ALsizei pos = DataPosInt; while(tmpiter && SrcBufferSize > SrcDataSize) { @@ -500,7 +412,7 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam if((ALBuffer=tmpiter->buffer) != NULL) { const ALubyte *Data = ALBuffer->data; - ALuint DataSize = ALBuffer->SampleLen; + ALsizei DataSize = ALBuffer->SampleLen; /* Skip the data already played */ if(DataSize <= pos) @@ -517,9 +429,9 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam SrcDataSize += DataSize; } } - tmpiter = tmpiter->next; - if(!tmpiter && Looping) - tmpiter = ATOMIC_LOAD(&Source->queue); + tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire); + if(!tmpiter && BufferLoopItem) + tmpiter = BufferLoopItem; else if(!tmpiter) { SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize); @@ -535,43 +447,164 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam ); /* Now resample, then filter and mix to the appropriate outputs. */ - ResampledData = Resample(&voice->SincState, + ResampledData = Resample(&voice->ResampleState, &SrcData[MAX_PRE_SAMPLES], DataPosFrac, increment, Device->ResampledData, DstBufferSize ); { - DirectParams *parms = &voice->Direct; + DirectParams *parms = &voice->Direct.Params[chan]; const ALfloat *samples; samples = DoFilters( - &parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass, - Device->FilteredData, ResampledData, DstBufferSize, - parms->Filters[chan].ActiveType + &parms->LowPass, &parms->HighPass, Device->FilteredData, + ResampledData, DstBufferSize, voice->Direct.FilterType ); - if(!voice->IsHrtf) - MixSamples(samples, parms->OutChannels, parms->OutBuffer, parms->Gains[chan], - parms->Counter, OutPos, DstBufferSize); + 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->NFCtrlData; + 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) \ + { \ + NfcFilterUpdate##order(&parms->NFCtrlFilter[order-1], 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 - MixHrtfSamples(parms->OutBuffer, samples, parms->Counter, voice->Offset, - OutPos, IrSize, &parms->Hrtf[chan].Params, - &parms->Hrtf[chan].State, DstBufferSize); + { + 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 = SAFE_CONST(ALfloat2*,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 = SAFE_CONST(ALfloat2*,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(j = 0;j < Device->NumAuxSends;j++) + for(send = 0;send < Device->NumAuxSends;send++) { - SendParams *parms = &voice->Send[j]; + SendParams *parms = &voice->Send[send].Params[chan]; const ALfloat *samples; - if(!parms->OutBuffer) + if(!voice->Send[send].Buffer) continue; samples = DoFilters( - &parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass, - Device->FilteredData, ResampledData, DstBufferSize, - parms->Filters[chan].ActiveType + &parms->LowPass, &parms->HighPass, Device->FilteredData, + 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 ); - MixSamples(samples, 1, parms->OutBuffer, &parms->Gains[chan], - parms->Counter, OutPos, DstBufferSize); } } /* Update positions */ @@ -581,17 +614,16 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam OutPos += DstBufferSize; voice->Offset += DstBufferSize; - voice->Direct.Counter = maxu(voice->Direct.Counter, DstBufferSize) - DstBufferSize; - for(j = 0;j < Device->NumAuxSends;j++) - voice->Send[j].Counter = maxu(voice->Send[j].Counter, DstBufferSize) - DstBufferSize; + Counter = maxi(DstBufferSize, Counter) - DstBufferSize; + firstpass = false; /* Handle looping sources */ while(1) { const ALbuffer *ALBuffer; - ALuint DataSize = 0; - ALuint LoopStart = 0; - ALuint LoopEnd = 0; + ALsizei DataSize = 0; + ALsizei LoopStart = 0; + ALsizei LoopEnd = 0; if((ALBuffer=BufferListItem->buffer) != NULL) { @@ -602,7 +634,7 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam break; } - if(Looping && Source->SourceType == AL_STATIC) + if(BufferLoopItem && Source->SourceType == AL_STATIC) { assert(LoopEnd > LoopStart); DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart; @@ -612,14 +644,13 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam if(DataSize > DataPosInt) break; - if(!(BufferListItem=BufferListItem->next)) + BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire); + if(!BufferListItem) { - if(Looping) - BufferListItem = ATOMIC_LOAD(&Source->queue); - else + BufferListItem = BufferLoopItem; + if(!BufferListItem) { - State = AL_STOPPED; - BufferListItem = NULL; + isplaying = false; DataPosInt = 0; DataPosFrac = 0; break; @@ -628,11 +659,13 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam DataPosInt -= DataSize; } - } while(State == AL_PLAYING && OutPos < SamplesToDo); + } while(isplaying && OutPos < SamplesToDo); + + voice->Flags |= VOICE_IS_FADING; /* Update source info */ - Source->state = State; - ATOMIC_STORE(&Source->current_buffer, BufferListItem); - Source->position = DataPosInt; - Source->position_fraction = DataPosFrac; + 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); + return isplaying; } diff --git a/libs/openal-soft/Alc/mixer_c.c b/libs/openal-soft/Alc/mixer_c.c index ef37b730..87f2fe90 100644 --- a/libs/openal-soft/Alc/mixer_c.c +++ b/libs/openal-soft/Alc/mixer_c.c @@ -8,18 +8,17 @@ #include "alAuxEffectSlot.h" -static inline ALfloat point32(const ALfloat *vals, ALuint UNUSED(frac)) +static inline ALfloat point32(const ALfloat *restrict vals, ALsizei UNUSED(frac)) { return vals[0]; } -static inline ALfloat lerp32(const ALfloat *vals, ALuint frac) +static inline ALfloat lerp32(const ALfloat *restrict vals, ALsizei frac) { return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); } -static inline ALfloat fir4_32(const ALfloat *vals, ALuint frac) +static inline ALfloat fir4_32(const ALfloat *restrict vals, ALsizei frac) { return resample_fir4(vals[-1], vals[0], vals[1], vals[2], frac); } -static inline ALfloat fir8_32(const ALfloat *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 *src, ALuint UNUSED(frac), - ALuint UNUSED(increment), ALfloat *restrict dst, ALuint numsamples) +const ALfloat *Resample_copy32_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. */ @@ -31,11 +30,11 @@ const ALfloat *Resample_copy32_C(const BsincState* UNUSED(state), const ALfloat } #define DECL_TEMPLATE(Sampler) \ -const ALfloat *Resample_##Sampler##_C(const BsincState* UNUSED(state), \ - const ALfloat *src, ALuint frac, ALuint increment, \ - ALfloat *restrict dst, ALuint numsamples) \ +const ALfloat *Resample_##Sampler##_C(const InterpState* UNUSED(state), \ + const ALfloat *restrict src, ALsizei frac, ALint increment, \ + ALfloat *restrict dst, ALsizei numsamples) \ { \ - ALuint i; \ + ALsizei i; \ for(i = 0;i < numsamples;i++) \ { \ dst[i] = Sampler(src, frac); \ @@ -50,21 +49,20 @@ const ALfloat *Resample_##Sampler##_C(const BsincState* UNUSED(state), \ 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 *src, ALuint frac, - ALuint increment, ALfloat *restrict dst, ALuint dstlen) +const ALfloat *Resample_bsinc32_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 sf = state->sf; - const ALuint m = state->m; - const ALint l = state->l; - ALuint j_f, pi, i; + const ALfloat sf = state->bsinc.sf; + const ALsizei m = state->bsinc.m; + ALsizei j_f, pi, i; ALfloat pf, r; - ALint j_s; + src += state->bsinc.l; for(i = 0;i < dstlen;i++) { // Calculate the phase index and factor. @@ -73,16 +71,15 @@ const ALfloat *Resample_bsinc32_C(const BsincState *state, const ALfloat *src, A pf = (frac & ((1<coeffs[pi].filter; - scd = state->coeffs[pi].scDelta; - phd = state->coeffs[pi].phDelta; - spd = state->coeffs[pi].spDelta; + fil = ASSUME_ALIGNED(state->bsinc.coeffs[pi].filter, 16); + scd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].scDelta, 16); + phd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].phDelta, 16); + spd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].spDelta, 16); // 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]; + for(j_f = 0;j_f < m;j_f++) + r += (fil[j_f] + sf*scd[j_f] + pf*(phd[j_f] + sf*spd[j_f])) * src[j_f]; dst[i] = r; frac += increment; @@ -93,84 +90,93 @@ const ALfloat *Resample_bsinc32_C(const BsincState *state, const ALfloat *src, A } -void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples) +void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples) { - ALuint i; - for(i = 0;i < numsamples;i++) - *(dst++) = ALfilterState_processSingle(filter, *(src++)); -} - - -static inline void SetupCoeffs(ALfloat (*restrict OutCoeffs)[2], - const HrtfParams *hrtfparams, - ALuint IrSize, ALuint Counter) -{ - ALuint c; - for(c = 0;c < IrSize;c++) + ALsizei i; + if(numsamples > 1) { - OutCoeffs[c][0] = hrtfparams->Coeffs[c][0] - (hrtfparams->CoeffStep[c][0]*Counter); - OutCoeffs[c][1] = hrtfparams->Coeffs[c][1] - (hrtfparams->CoeffStep[c][1]*Counter); + 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], +static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2], + const ALsizei IrSize, + const ALfloat (*restrict Coeffs)[2], ALfloat left, ALfloat right) { - ALuint c; + ALsizei c; for(c = 0;c < IrSize;c++) { - const ALuint off = (Offset+c)&HRIR_MASK; + 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 "mixer_inc.c" #undef MixHrtf -void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], - MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize) +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, step; - ALuint c; + ALfloat gain, delta, step; + ALsizei c; + + delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f; for(c = 0;c < OutChans;c++) { - ALuint pos = 0; - gain = Gains[c].Current; - step = Gains[c].Step; - if(step != 0.0f && Counter > 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); for(;pos < minsize;pos++) { OutBuffer[c][OutPos+pos] += data[pos]*gain; gain += step; } if(pos == Counter) - gain = Gains[c].Target; - Gains[c].Current = gain; + gain = TargetGains[c]; + CurrentGains[c] = gain; } if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) @@ -179,3 +185,24 @@ void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[B 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; + + 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/libs/openal-soft/Alc/mixer_defs.h b/libs/openal-soft/Alc/mixer_defs.h index 3c32278b..462d6179 100644 --- a/libs/openal-soft/Alc/mixer_defs.h +++ b/libs/openal-soft/Alc/mixer_defs.h @@ -8,73 +8,122 @@ struct MixGains; -struct HrtfParams; +struct MixHrtfParams; struct HrtfState; /* C resamplers */ -const ALfloat *Resample_copy32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); -const ALfloat *Resample_point32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); -const ALfloat *Resample_lerp32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); -const ALfloat *Resample_fir4_32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); -const ALfloat *Resample_fir8_32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); -const ALfloat *Resample_bsinc32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen); +const ALfloat *Resample_copy32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen); +const ALfloat *Resample_point32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen); +const ALfloat *Resample_lerp32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen); +const ALfloat *Resample_fir4_32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen); +const ALfloat *Resample_bsinc32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen); /* C mixers */ -void MixHrtf_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, - ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize, - const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate, - ALuint BufferSize); -void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], - struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize); +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 OutBuffer)[BUFFERSIZE], const ALfloat *data, - ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize, - const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate, - ALuint BufferSize); -void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], - struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize); +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(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size) +inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size) { - ALuint i; + ALsizei i; pos_arr[0] = 0; frac_arr[0] = frac; for(i = 1;i < size;i++) { - ALuint frac_tmp = frac_arr[i-1] + increment; + 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_bsinc32_SSE(const BsincState *state, const ALfloat *src, ALuint frac, - ALuint increment, ALfloat *restrict dst, ALuint dstlen); +const ALfloat *Resample_lerp32_SSE2(const InterpState *state, const ALfloat *restrict src, + ALsizei frac, ALint increment, ALfloat *restrict dst, + ALsizei numsamples); +const ALfloat *Resample_lerp32_SSE41(const InterpState *state, const ALfloat *restrict src, + ALsizei frac, ALint increment, ALfloat *restrict dst, + ALsizei numsamples); -const ALfloat *Resample_lerp32_SSE2(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, - ALfloat *restrict dst, ALuint numsamples); -const ALfloat *Resample_lerp32_SSE41(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, - ALfloat *restrict dst, ALuint numsamples); +const ALfloat *Resample_fir4_32_SSE3(const InterpState *state, const ALfloat *restrict src, + ALsizei frac, ALint increment, ALfloat *restrict dst, + ALsizei numsamples); +const ALfloat *Resample_fir4_32_SSE41(const InterpState *state, const ALfloat *restrict src, + ALsizei frac, ALint increment, ALfloat *restrict dst, + ALsizei numsamples); -const ALfloat *Resample_fir4_32_SSE3(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, - ALfloat *restrict dst, ALuint numsamples); -const ALfloat *Resample_fir4_32_SSE41(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, - ALfloat *restrict dst, ALuint numsamples); - -const ALfloat *Resample_fir8_32_SSE3(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, - ALfloat *restrict dst, ALuint numsamples); -const ALfloat *Resample_fir8_32_SSE41(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, - ALfloat *restrict dst, ALuint numsamples); +const ALfloat *Resample_bsinc32_SSE(const InterpState *state, const ALfloat *restrict src, + ALsizei frac, ALint increment, ALfloat *restrict dst, + ALsizei dstlen); /* Neon mixers */ -void MixHrtf_Neon(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, - ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize, - const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate, - ALuint BufferSize); -void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], - struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize); +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_lerp32_Neon(const InterpState *state, const ALfloat *restrict src, + ALsizei frac, ALint increment, ALfloat *restrict dst, + ALsizei numsamples); +const ALfloat *Resample_fir4_32_Neon(const InterpState *state, const ALfloat *restrict src, + ALsizei frac, ALint increment, ALfloat *restrict dst, + ALsizei numsamples); +const ALfloat *Resample_bsinc32_Neon(const InterpState *state, const ALfloat *restrict src, + ALsizei frac, ALint increment, ALfloat *restrict dst, + ALsizei dstlen); #endif /* MIXER_DEFS_H */ diff --git a/libs/openal-soft/Alc/mixer_inc.c b/libs/openal-soft/Alc/mixer_inc.c index a82930cc..3c9d4dc5 100644 --- a/libs/openal-soft/Alc/mixer_inc.c +++ b/libs/openal-soft/Alc/mixer_inc.c @@ -6,74 +6,109 @@ #include "hrtf.h" #include "mixer_defs.h" #include "align.h" +#include "alu.h" -static inline void SetupCoeffs(ALfloat (*restrict OutCoeffs)[2], - const HrtfParams *hrtfparams, - ALuint IrSize, ALuint Counter); -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], +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 OutBuffer)[BUFFERSIZE], const ALfloat *data, - ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize, - const HrtfParams *hrtfparams, HrtfState *hrtfstate, ALuint BufferSize) +void MixHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut, + const ALfloat *data, ALsizei Offset, ALsizei OutPos, + const ALsizei IrSize, MixHrtfParams *hrtfparams, HrtfState *hrtfstate, + ALsizei BufferSize) { - alignas(16) ALfloat Coeffs[HRIR_LENGTH][2]; - ALuint Delay[2]; + 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; - ALuint pos; + ALsizei i; - SetupCoeffs(Coeffs, hrtfparams, IrSize, Counter); - Delay[0] = hrtfparams->Delay[0] - (hrtfparams->DelayStep[0]*Counter); - Delay[1] = hrtfparams->Delay[1] - (hrtfparams->DelayStep[1]*Counter); - - pos = 0; - for(;pos < BufferSize && pos < Counter;pos++) + LeftOut += OutPos; + RightOut += OutPos; + for(i = 0;i < BufferSize;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)); + 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; - Delay[0] += hrtfparams->DelayStep[0]; - Delay[1] += hrtfparams->DelayStep[1]; - - hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f; - hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f; - Offset++; - - ApplyCoeffsStep(Offset, hrtfstate->Values, IrSize, Coeffs, hrtfparams->CoeffStep, left, right); - OutBuffer[0][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0]; - OutBuffer[1][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1]; - OutPos++; - } - - Delay[0] >>= HRTFDELAY_BITS; - Delay[1] >>= HRTFDELAY_BITS; - for(;pos < BufferSize;pos++) - { - 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++; + 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); - OutBuffer[0][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0]; - OutBuffer[1][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1]; - OutPos++; + *(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; + + 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; + + 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/libs/openal-soft/Alc/mixer_neon.c b/libs/openal-soft/Alc/mixer_neon.c index a89caeae..51191783 100644 --- a/libs/openal-soft/Alc/mixer_neon.c +++ b/libs/openal-soft/Alc/mixer_neon.c @@ -7,65 +7,195 @@ #include "alMain.h" #include "alu.h" #include "hrtf.h" +#include "mixer_defs.h" -static inline void SetupCoeffs(ALfloat (*restrict OutCoeffs)[2], - const HrtfParams *hrtfparams, - ALuint IrSize, ALuint Counter) +const ALfloat *Resample_lerp32_Neon(const InterpState* UNUSED(state), + const ALfloat *restrict src, ALsizei frac, ALint increment, + ALfloat *restrict dst, ALsizei numsamples) { - ALuint c; - float32x4_t counter4; + 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; + + InitiatePositionArrays(frac, increment, frac_, pos_, 4); + + frac4 = vld1q_s32(frac_); + pos4 = vld1q_s32(pos_); + + for(i = 0;numsamples-i > 3;i += 4) { - float32x2_t counter2 = vdup_n_f32(-(float)Counter); - counter4 = vcombine_f32(counter2, counter2); + 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); } - for(c = 0;c < IrSize;c += 2) + + if(i < numsamples) { - float32x4_t step4 = vld1q_f32((float32_t*)hrtfparams->CoeffStep[c]); - float32x4_t coeffs = vld1q_f32((float32_t*)hrtfparams->Coeffs[c]); - coeffs = vmlaq_f32(coeffs, step4, counter4); - vst1q_f32((float32_t*)OutCoeffs[c], coeffs); + /* 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; } -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 ALfloat *Resample_fir4_32_Neon(const InterpState* UNUSED(state), + const ALfloat *restrict src, ALsizei frac, ALint increment, + ALfloat *restrict dst, ALsizei numsamples) { - 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]); + const int32x4_t increment4 = vdupq_n_s32(increment*4); + 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; - vals = vmlaq_f32(vals, coefs, leftright4); - coefs = vaddq_f32(coefs, deltas); + InitiatePositionArrays(frac, increment, frac_, pos_, 4); - 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); + frac4 = vld1q_s32(frac_); + pos4 = vld1q_s32(pos_); + + --src; + for(i = 0;numsamples-i > 3;i += 4) + { + const float32x4_t val0 = vld1q_f32(&src[pos_[0]]); + const float32x4_t val1 = vld1q_f32(&src[pos_[1]]); + const float32x4_t val2 = vld1q_f32(&src[pos_[2]]); + const float32x4_t val3 = vld1q_f32(&src[pos_[3]]); + float32x4_t k0 = vld1q_f32(sinc4Tab[frac_[0]]); + float32x4_t k1 = vld1q_f32(sinc4Tab[frac_[1]]); + float32x4_t k2 = vld1q_f32(sinc4Tab[frac_[2]]); + float32x4_t k3 = vld1q_f32(sinc4Tab[frac_[3]]); + float32x4_t out; + + k0 = vmulq_f32(k0, val0); + k1 = vmulq_f32(k1, val1); + k2 = vmulq_f32(k2, val2); + k3 = vmulq_f32(k3, val3); + k0 = vcombine_f32(vpadd_f32(vget_low_f32(k0), vget_high_f32(k0)), + vpadd_f32(vget_low_f32(k1), vget_high_f32(k1))); + k2 = vcombine_f32(vpadd_f32(vget_low_f32(k2), vget_high_f32(k2)), + vpadd_f32(vget_low_f32(k3), vget_high_f32(k3))); + out = vcombine_f32(vpadd_f32(vget_low_f32(k0), vget_high_f32(k0)), + vpadd_f32(vget_low_f32(k2), vget_high_f32(k2))); + + 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); + vst1q_s32(frac_, frac4); } + + 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 = frac_[0]; + do { + dst[i] = resample_fir4(src[pos], src[pos+1], src[pos+2], src[pos+3], frac); + + frac += increment; + pos += frac>>FRACTIONBITS; + frac &= FRACTIONMASK; + } while(++i < numsamples); + } + return dst; } -static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], - const ALuint IrSize, - ALfloat (*restrict Coeffs)[2], +const ALfloat *Resample_bsinc32_Neon(const InterpState *state, + const ALfloat *restrict src, ALsizei frac, ALint increment, + ALfloat *restrict dst, ALsizei dstlen) +{ + const float32x4_t sf4 = vdupq_n_f32(state->bsinc.sf); + const ALsizei m = state->bsinc.m; + const ALfloat *fil, *scd, *phd, *spd; + ALsizei pi, i, j; + float32x4_t r4; + ALfloat pf; + + 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<bsinc.coeffs[pi].filter, 16); + scd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].scDelta, 16); + phd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].phDelta, 16); + spd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].spDelta, 16); + + // Apply the scale and phase interpolated filter. + r4 = vdupq_n_f32(0.0f); + { + const float32x4_t pf4 = vdupq_n_f32(pf); + for(j = 0;j < m;j+=4) + { + /* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */ + const float32x4_t f4 = vmlaq_f32(vmlaq_f32(vld1q_f32(&fil[j]), + sf4, vld1q_f32(&scd[j])), + pf4, vmlaq_f32(vld1q_f32(&phd[j]), + sf4, vld1q_f32(&spd[j]) + ) + ); + /* r += f*src */ + r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j])); + } + } + r4 = vaddq_f32(r4, vcombine_f32(vrev64_f32(vget_high_f32(r4)), + vrev64_f32(vget_low_f32(r4)))); + dst[i] = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); + + frac += increment; + src += frac>>FRACTIONBITS; + frac &= FRACTIONMASK; + } + return dst; +} + + +static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2], + const ALsizei IrSize, + const ALfloat (*restrict Coeffs)[2], ALfloat left, ALfloat right) { - ALuint c; + ALsizei c; float32x4_t leftright4; { float32x2_t leftright2 = vdup_n_f32(0.0); @@ -73,10 +203,12 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], 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 ALuint o0 = (Offset+c)&HRIR_MASK; - const ALuint o1 = (o0+1)&HRIR_MASK; + 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]); @@ -89,36 +221,68 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2], } #define MixHrtf MixHrtf_Neon +#define MixHrtfBlend MixHrtfBlend_Neon +#define MixDirectHrtf MixDirectHrtf_Neon #include "mixer_inc.c" #undef MixHrtf -void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], - MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize) +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, step; + ALfloat gain, delta, step; float32x4_t gain4; - ALuint c; + ALsizei c; + + 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++) { - ALuint pos = 0; - gain = Gains[c].Current; - step = Gains[c].Step; - if(step != 0.0f && Counter > 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) + { + 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 = Gains[c].Target; - Gains[c].Current = gain; + gain = TargetGains[c]; + 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; } @@ -137,3 +301,31 @@ void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer 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; + + 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/libs/openal-soft/Alc/mixer_sse.c b/libs/openal-soft/Alc/mixer_sse.c index 090b7a5a..68786573 100644 --- a/libs/openal-soft/Alc/mixer_sse.c +++ b/libs/openal-soft/Alc/mixer_sse.c @@ -12,18 +12,18 @@ #include "mixer_defs.h" -const ALfloat *Resample_bsinc32_SSE(const BsincState *state, const ALfloat *src, ALuint frac, - ALuint increment, ALfloat *restrict dst, ALuint dstlen) +const ALfloat *Resample_bsinc32_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 __m128 sf4 = _mm_set1_ps(state->bsinc.sf); + const ALsizei m = state->bsinc.m; const ALfloat *fil, *scd, *phd, *spd; - ALuint pi, j_f, i; + ALsizei pi, i, j; ALfloat pf; - ALint j_s; __m128 r4; + src += state->bsinc.l; for(i = 0;i < dstlen;i++) { // Calculate the phase index and factor. @@ -32,32 +32,30 @@ const ALfloat *Resample_bsinc32_SSE(const BsincState *state, const ALfloat *src, pf = (frac & ((1<coeffs[pi].filter; - scd = state->coeffs[pi].scDelta; - phd = state->coeffs[pi].phDelta; - spd = state->coeffs[pi].spDelta; + fil = ASSUME_ALIGNED(state->bsinc.coeffs[pi].filter, 16); + scd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].scDelta, 16); + phd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].phDelta, 16); + spd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].spDelta, 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 LD4(x) _mm_load_ps(x) +#define ULD4(x) _mm_loadu_ps(x) +#define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z)) + for(j = 0;j < m;j+=4) { - 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(LD4(&fil[j]), sf4, LD4(&scd[j])), + pf4, MLA4(LD4(&phd[j]), sf4, LD4(&spd[j])) ); - r4 = _mm_add_ps(r4, _mm_mul_ps(f4, _mm_loadu_ps(&src[j_s]))); + /* r += f*src */ + r4 = MLA4(r4, f4, ULD4(&src[j])); } +#undef MLA4 +#undef ULD4 +#undef LD4 } 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)); @@ -71,99 +69,22 @@ const ALfloat *Resample_bsinc32_SSE(const BsincState *state, const ALfloat *src, } -static inline void SetupCoeffs(ALfloat (*restrict OutCoeffs)[2], - const HrtfParams *hrtfparams, - ALuint IrSize, ALuint Counter) -{ - const __m128 counter4 = _mm_set1_ps((float)Counter); - __m128 coeffs, step4; - ALuint i; - - for(i = 0;i < IrSize;i += 2) - { - step4 = _mm_load_ps(&hrtfparams->CoeffStep[i][0]); - coeffs = _mm_load_ps(&hrtfparams->Coeffs[i][0]); - coeffs = _mm_sub_ps(coeffs, _mm_mul_ps(step4, counter4)); - _mm_store_ps(&OutCoeffs[i][0], coeffs); - } -} - -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]); @@ -173,7 +94,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]); @@ -192,7 +113,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]); @@ -203,25 +124,30 @@ 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 -void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE], - MixGains *Gains, 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, step; + ALfloat gain, delta, step; __m128 gain4; - ALuint c; + ALsizei c; + + delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f; for(c = 0;c < OutChans;c++) { - ALuint pos = 0; - gain = Gains[c].Current; - step = Gains[c].Step; - if(step != 0.0f && Counter > 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) { @@ -254,11 +180,11 @@ void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer) gain += step; } if(pos == Counter) - gain = Gains[c].Target; - Gains[c].Current = gain; + gain = TargetGains[c]; + 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; } @@ -277,3 +203,28 @@ void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer) OutBuffer[c][OutPos+pos] += data[pos]*gain; } } + +void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize) +{ + __m128 gain4; + ALsizei c; + + for(c = 0;c < InChans;c++) + { + ALsizei pos = 0; + ALfloat gain = Gains[c]; + if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) + continue; + + gain4 = _mm_set1_ps(gain); + for(;BufferSize-pos > 3;pos += 4) + { + const __m128 val4 = _mm_load_ps(&data[c][InPos+pos]); + __m128 dry4 = _mm_load_ps(&OutBuffer[pos]); + dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4)); + _mm_store_ps(&OutBuffer[pos], dry4); + } + for(;pos < BufferSize;pos++) + OutBuffer[pos] += data[c][InPos+pos]*gain; + } +} diff --git a/libs/openal-soft/Alc/mixer_sse2.c b/libs/openal-soft/Alc/mixer_sse2.c index 32f29227..84cc35dd 100644 --- a/libs/openal-soft/Alc/mixer_sse2.c +++ b/libs/openal-soft/Alc/mixer_sse2.c @@ -27,17 +27,18 @@ #include "mixer_defs.h" -const ALfloat *Resample_lerp32_SSE2(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment, - ALfloat *restrict dst, ALuint numsamples) +const ALfloat *Resample_lerp32_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); - alignas(16) union { ALuint i[4]; float f[4]; } pos_; - alignas(16) union { 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; InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4); diff --git a/libs/openal-soft/Alc/mixer_sse3.c b/libs/openal-soft/Alc/mixer_sse3.c index 7085c537..78603c87 100644 --- a/libs/openal-soft/Alc/mixer_sse3.c +++ b/libs/openal-soft/Alc/mixer_sse3.c @@ -31,16 +31,17 @@ #include "mixer_defs.h" -const ALfloat *Resample_fir4_32_SSE3(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment, - ALfloat *restrict dst, ALuint numsamples) +const ALfloat *Resample_fir4_32_SSE3(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 __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK); - alignas(16) union { ALuint i[4]; float f[4]; } pos_; - alignas(16) union { ALuint i[4]; float f[4]; } frac_; + 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; InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4); @@ -54,10 +55,10 @@ const ALfloat *Resample_fir4_32_SSE3(const BsincState* UNUSED(state), const ALfl 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 k0 = _mm_load_ps(sinc4Tab[frac_.i[0]]); + __m128 k1 = _mm_load_ps(sinc4Tab[frac_.i[1]]); + __m128 k2 = _mm_load_ps(sinc4Tab[frac_.i[2]]); + __m128 k3 = _mm_load_ps(sinc4Tab[frac_.i[3]]); __m128 out; k0 = _mm_mul_ps(k0, val0); @@ -94,69 +95,3 @@ const ALfloat *Resample_fir4_32_SSE3(const BsincState* UNUSED(state), const ALfl } return dst; } - -const ALfloat *Resample_fir8_32_SSE3(const BsincState* UNUSED(state), const ALfloat *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); - alignas(16) union { ALuint i[4]; float f[4]; } pos_; - alignas(16) union { ALuint i[4]; float f[4]; } frac_; - __m128i frac4, pos4; - ALuint pos; - ALuint i, 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/libs/openal-soft/Alc/mixer_sse41.c b/libs/openal-soft/Alc/mixer_sse41.c index e832e5df..613a89ff 100644 --- a/libs/openal-soft/Alc/mixer_sse41.c +++ b/libs/openal-soft/Alc/mixer_sse41.c @@ -28,17 +28,18 @@ #include "mixer_defs.h" -const ALfloat *Resample_lerp32_SSE41(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment, - ALfloat *restrict dst, ALuint numsamples) +const ALfloat *Resample_lerp32_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); - alignas(16) union { ALuint i[4]; float f[4]; } pos_; - alignas(16) union { 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; InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4); @@ -84,16 +85,17 @@ const ALfloat *Resample_lerp32_SSE41(const BsincState* UNUSED(state), const ALfl return dst; } -const ALfloat *Resample_fir4_32_SSE41(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment, - ALfloat *restrict dst, ALuint numsamples) +const ALfloat *Resample_fir4_32_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 __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK); - alignas(16) union { ALuint i[4]; float f[4]; } pos_; - alignas(16) union { ALuint i[4]; float f[4]; } frac_; + 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; InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4); @@ -107,10 +109,10 @@ const ALfloat *Resample_fir4_32_SSE41(const BsincState* UNUSED(state), const ALf 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 k0 = _mm_load_ps(sinc4Tab[frac_.i[0]]); + __m128 k1 = _mm_load_ps(sinc4Tab[frac_.i[1]]); + __m128 k2 = _mm_load_ps(sinc4Tab[frac_.i[2]]); + __m128 k3 = _mm_load_ps(sinc4Tab[frac_.i[3]]); __m128 out; k0 = _mm_mul_ps(k0, val0); @@ -150,75 +152,3 @@ const ALfloat *Resample_fir4_32_SSE41(const BsincState* UNUSED(state), const ALf } return dst; } - -const ALfloat *Resample_fir8_32_SSE41(const BsincState* UNUSED(state), const ALfloat *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); - alignas(16) union { ALuint i[4]; float f[4]; } pos_; - alignas(16) union { ALuint i[4]; float f[4]; } frac_; - __m128i frac4, pos4; - ALuint pos; - ALuint i, 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/libs/openal-soft/Alc/nfcfilter.c b/libs/openal-soft/Alc/nfcfilter.c new file mode 100644 index 00000000..758863c9 --- /dev/null +++ b/libs/openal-soft/Alc/nfcfilter.c @@ -0,0 +1,418 @@ + +#include "config.h" + +#include "nfcfilter.h" + +#include "alu.h" + + +/* Near-field control filters are the basis for handling the near-field effect. + * The near-field effect is a bass-boost present in the directional components + * of a recorded signal, created as a result of the wavefront curvature (itself + * a function of sound distance). Proper reproduction dictates this be + * compensated for using a bass-cut given the playback speaker distance, to + * avoid excessive bass in the playback. + * + * For real-time rendered audio, emulating the near-field effect based on the + * sound source's distance, and subsequently compensating for it at output + * based on the speaker distances, can create a more realistic perception of + * sound distance beyond a simple 1/r attenuation. + * + * These filters do just that. Each one applies a low-shelf filter, created as + * the combination of a bass-boost for a given sound source distance (near- + * field emulation) along with a bass-cut for a given control/speaker distance + * (near-field compensation). + * + * Note that it is necessary to apply a cut along with the boost, since the + * boost alone is unstable in higher-order ambisonics as it causes an infinite + * DC gain (even first-order ambisonics requires there to be no DC offset for + * the boost to work). Consequently, ambisonics requires a control parameter to + * be used to avoid an unstable boost-only filter. NFC-HOA defines this control + * as a reference delay, calculated with: + * + * reference_delay = control_distance / speed_of_sound + * + * This means w0 (for input) or w1 (for output) should be set to: + * + * wN = 1 / (reference_delay * sample_rate) + * + * when dealing with NFC-HOA content. For FOA input content, which does not + * specify a reference_delay variable, w0 should be set to 0 to apply only + * near-field compensation for output. It's important that w1 be a finite, + * positive, non-0 value or else the bass-boost will become unstable again. + * Also, w0 should not be too large compared to w1, to avoid excessively loud + * low frequencies. + */ + +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 }*/ +}; + +void NfcFilterCreate1(NfcFilter *nfc, const float w0, const float w1) +{ + float b_00, g_0; + float r; + + memset(nfc, 0, sizeof(*nfc)); + + nfc->g = 1.0f; + nfc->coeffs[0] = 1.0f; + + /* Calculate bass-boost coefficients. */ + r = 0.5f * w0; + b_00 = B[1][0] * r; + g_0 = 1.0f + b_00; + + nfc->coeffs[0] *= g_0; + nfc->coeffs[1] = (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->g /= g_0; + nfc->coeffs[0] /= g_0; + nfc->coeffs[1+1] = (2.0f * b_00) / g_0; +} + +void NfcFilterAdjust1(NfcFilter *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->coeffs[0] = nfc->g * g_0; + nfc->coeffs[1] = (2.0f * b_00) / g_0; +} + +void NfcFilterUpdate1(NfcFilter *nfc, ALfloat *restrict dst, const float *restrict src, const int count) +{ + const float b0 = nfc->coeffs[0]; + const float a0 = nfc->coeffs[1]; + const float a1 = nfc->coeffs[2]; + float z1 = nfc->history[0]; + int i; + + for(i = 0;i < count;i++) + { + float out = src[i] * b0; + float y; + + y = out - (a1*z1); + out = y + (a0*z1); + z1 += y; + + dst[i] = out; + } + nfc->history[0] = z1; +} + + +void NfcFilterCreate2(NfcFilter *nfc, const float w0, const float w1) +{ + float b_10, b_11, g_1; + float r; + + memset(nfc, 0, sizeof(*nfc)); + + nfc->g = 1.0f; + nfc->coeffs[0] = 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->coeffs[0] *= g_1; + nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1; + nfc->coeffs[2] = (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->g /= g_1; + nfc->coeffs[0] /= g_1; + nfc->coeffs[2+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1; + nfc->coeffs[2+2] = (4.0f * b_11) / g_1; +} + +void NfcFilterAdjust2(NfcFilter *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->coeffs[0] = nfc->g * g_1; + nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1; + nfc->coeffs[2] = (4.0f * b_11) / g_1; +} + +void NfcFilterUpdate2(NfcFilter *nfc, ALfloat *restrict dst, const float *restrict src, const int count) +{ + const float b0 = nfc->coeffs[0]; + const float a00 = nfc->coeffs[1]; + const float a01 = nfc->coeffs[2]; + const float a10 = nfc->coeffs[3]; + const float a11 = nfc->coeffs[4]; + float z1 = nfc->history[0]; + float z2 = nfc->history[1]; + int i; + + for(i = 0;i < count;i++) + { + float out = src[i] * b0; + float y; + + y = out - (a10*z1) - (a11*z2); + out = y + (a00*z1) + (a01*z2); + z2 += z1; + z1 += y; + + dst[i] = out; + } + nfc->history[0] = z1; + nfc->history[1] = z2; +} + + +void NfcFilterCreate3(NfcFilter *nfc, const float w0, const float w1) +{ + float b_10, b_11, g_1; + float b_00, g_0; + float r; + + memset(nfc, 0, sizeof(*nfc)); + + nfc->g = 1.0f; + nfc->coeffs[0] = 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->coeffs[0] *= g_1; + nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1; + nfc->coeffs[2] = (4.0f * b_11) / g_1; + + b_00 = B[3][2] * r; + g_0 = 1.0f + b_00; + + nfc->coeffs[0] *= g_0; + nfc->coeffs[2+1] = (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->g /= g_1; + nfc->coeffs[0] /= g_1; + nfc->coeffs[3+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1; + nfc->coeffs[3+2] = (4.0f * b_11) / g_1; + + b_00 = B[3][2] * r; + g_0 = 1.0f + b_00; + + nfc->g /= g_0; + nfc->coeffs[0] /= g_0; + nfc->coeffs[3+2+1] = (2.0f * b_00) / g_0; +} + +void NfcFilterAdjust3(NfcFilter *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->coeffs[0] = nfc->g * g_1; + nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1; + nfc->coeffs[2] = (4.0f * b_11) / g_1; + + b_00 = B[3][2] * r; + g_0 = 1.0f + b_00; + + nfc->coeffs[0] *= g_0; + nfc->coeffs[2+1] = (2.0f * b_00) / g_0; +} + +void NfcFilterUpdate3(NfcFilter *nfc, ALfloat *restrict dst, const float *restrict src, const int count) +{ + const float b0 = nfc->coeffs[0]; + const float a00 = nfc->coeffs[1]; + const float a01 = nfc->coeffs[2]; + const float a02 = nfc->coeffs[3]; + const float a10 = nfc->coeffs[4]; + const float a11 = nfc->coeffs[5]; + const float a12 = nfc->coeffs[6]; + float z1 = nfc->history[0]; + float z2 = nfc->history[1]; + float z3 = nfc->history[2]; + int i; + + for(i = 0;i < count;i++) + { + float out = src[i] * b0; + float y; + + y = out - (a10*z1) - (a11*z2); + out = y + (a00*z1) + (a01*z2); + z2 += z1; + z1 += y; + + y = out - (a12*z3); + out = y + (a02*z3); + z3 += y; + + dst[i] = out; + } + nfc->history[0] = z1; + nfc->history[1] = z2; + nfc->history[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 NfcFilterUpdate(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/libs/openal-soft/Alc/nfcfilter.h b/libs/openal-soft/Alc/nfcfilter.h new file mode 100644 index 00000000..199849fb --- /dev/null +++ b/libs/openal-soft/Alc/nfcfilter.h @@ -0,0 +1,37 @@ +#ifndef NFCFILTER_H +#define NFCFILTER_H + +#include "alMain.h" + +typedef struct NfcFilter { + float g; + float coeffs[MAX_AMBI_ORDER*2 + 1]; + float history[MAX_AMBI_ORDER]; +} 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. + */ + +/* Near-field control filter for first-order ambisonic channels (1-3). */ +void NfcFilterCreate1(NfcFilter *nfc, const float w0, const float w1); +void NfcFilterAdjust1(NfcFilter *nfc, const float w0); +void NfcFilterUpdate1(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count); + +/* Near-field control filter for second-order ambisonic channels (4-8). */ +void NfcFilterCreate2(NfcFilter *nfc, const float w0, const float w1); +void NfcFilterAdjust2(NfcFilter *nfc, const float w0); +void NfcFilterUpdate2(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count); + +/* Near-field control filter for third-order ambisonic channels (9-15). */ +void NfcFilterCreate3(NfcFilter *nfc, const float w0, const float w1); +void NfcFilterAdjust3(NfcFilter *nfc, const float w0); +void NfcFilterUpdate3(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count); + +#endif /* NFCFILTER_H */ diff --git a/libs/openal-soft/Alc/panning.c b/libs/openal-soft/Alc/panning.c index 6305bff7..0abf18db 100644 --- a/libs/openal-soft/Alc/panning.c +++ b/libs/openal-soft/Alc/panning.c @@ -27,19 +27,19 @@ #include #include "alMain.h" -#include "AL/al.h" -#include "AL/alc.h" +#include "alAuxEffectSlot.h" #include "alu.h" #include "bool.h" +#include "ambdec.h" +#include "bformatdec.h" +#include "uhjfilter.h" +#include "bs2b.h" -#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) +extern inline void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]); -static const ALuint FuMa2ACN[MAX_AMBI_COEFFS] = { +static const ALsizei FuMa2ACN[MAX_AMBI_COEFFS] = { 0, /* W */ 3, /* X */ 1, /* Y */ @@ -57,11 +57,36 @@ static const ALuint FuMa2ACN[MAX_AMBI_COEFFS] = { 15, /* P */ 9, /* Q */ }; +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. FuMa - * decoder coefficients should be divided by these values to get N3D decoder - * coefficients. +/* 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) */ @@ -82,35 +107,8 @@ static const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = { }; -void ComputeAmbientGains(const ALCdevice *device, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) +void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]) { - ALuint i; - - for(i = 0;i < device->NumChannels;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(device->AmbiCoeffs[i][0]) * ingain; - } - for(;i < MAX_OUTPUT_CHANNELS;i++) - gains[i] = 0.0f; -} - -void ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat elevation, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) -{ - ALfloat dir[3] = { - sinf(angle) * cosf(elevation), - sinf(elevation), - -cosf(angle) * cosf(elevation) - }; - ComputeDirectionalGains(device, dir, ingain, gains); -} - -void ComputeDirectionalGains(const ALCdevice *device, const ALfloat dir[3], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) -{ - ALfloat coeffs[MAX_AMBI_COEFFS]; - ALuint i, j; /* Convert from OpenAL coords to Ambisonics. */ ALfloat x = -dir[2]; ALfloat y = -dir[0]; @@ -137,34 +135,150 @@ void ComputeDirectionalGains(const ALCdevice *device, const ALfloat dir[3], ALfl coeffs[14] = 5.123475383f * z * (x*x - y*y); /* ACN 14 = sqrt(105)/2 * Z * (X*X - Y*Y) */ coeffs[15] = 2.091650066f * x * (x*x - 3.0f*y*y); /* ACN 15 = sqrt(35/8) * X * (X*X - 3*Y*Y) */ - for(i = 0;i < device->NumChannels;i++) + if(spread > 0.0f) + { + /* Implement the spread by using a spherical source that subtends the + * angle spread. See: + * http://www.ppsloan.org/publications/StupidSH36.pdf - Appendix A3 + * + * When adjusted for N3D normalization instead of SN3D, these + * calculations are: + * + * ZH0 = -sqrt(pi) * (-1+ca); + * ZH1 = 0.5*sqrt(pi) * sa*sa; + * ZH2 = -0.5*sqrt(pi) * ca*(-1+ca)*(ca+1); + * ZH3 = -0.125*sqrt(pi) * (-1+ca)*(ca+1)*(5*ca*ca - 1); + * ZH4 = -0.125*sqrt(pi) * ca*(-1+ca)*(ca+1)*(7*ca*ca - 3); + * ZH5 = -0.0625*sqrt(pi) * (-1+ca)*(ca+1)*(21*ca*ca*ca*ca - 14*ca*ca + 1); + * + * The gain of the source is compensated for size, so that the + * loundness doesn't depend on the spread. Thus: + * + * ZH0 = 1.0f; + * ZH1 = 0.5f * (ca+1.0f); + * ZH2 = 0.5f * (ca+1.0f)*ca; + * ZH3 = 0.125f * (ca+1.0f)*(5.0f*ca*ca - 1.0f); + * ZH4 = 0.125f * (ca+1.0f)*(7.0f*ca*ca - 3.0f)*ca; + * ZH5 = 0.0625f * (ca+1.0f)*(21.0f*ca*ca*ca*ca - 14.0f*ca*ca + 1.0f); + */ + ALfloat ca = 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 = scale; + ALfloat ZH1_norm = 0.5f * (ca+1.f) * scale; + ALfloat ZH2_norm = 0.5f * (ca+1.f)*ca * scale; + ALfloat ZH3_norm = 0.125f * (ca+1.f)*(5.f*ca*ca-1.f) * scale; + + /* Zeroth-order */ + coeffs[0] *= ZH0_norm; + /* First-order */ + coeffs[1] *= ZH1_norm; + coeffs[2] *= ZH1_norm; + coeffs[3] *= ZH1_norm; + /* Second-order */ + coeffs[4] *= ZH2_norm; + coeffs[5] *= ZH2_norm; + coeffs[6] *= ZH2_norm; + coeffs[7] *= ZH2_norm; + coeffs[8] *= ZH2_norm; + /* Third-order */ + coeffs[9] *= ZH3_norm; + coeffs[10] *= ZH3_norm; + coeffs[11] *= ZH3_norm; + coeffs[12] *= ZH3_norm; + coeffs[13] *= ZH3_norm; + coeffs[14] *= ZH3_norm; + coeffs[15] *= ZH3_norm; + } +} + +void CalcAnglePairwiseCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_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, ALsizei numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) +{ + ALsizei i; + + for(i = 0;i < numchans;i++) + gains[i] = chancoeffs[i][0] * 1.414213562f * ingain; + for(;i < MAX_OUTPUT_CHANNELS;i++) + gains[i] = 0.0f; +} + +void ComputeAmbientGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) +{ + ALfloat gain = 0.0f; + ALsizei 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, ALsizei numchans, ALsizei numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) +{ + ALsizei i, j; + + for(i = 0;i < numchans;i++) { float gain = 0.0f; - for(j = 0;j < MAX_AMBI_COEFFS;j++) - gain += device->AmbiCoeffs[i][j]*coeffs[j]; - gains[i] = gain * ingain; + for(j = 0;j < numcoeffs;j++) + gain += chancoeffs[i][j]*coeffs[j]; + gains[i] = clampf(gain, 0.0f, 1.0f) * ingain; } for(;i < MAX_OUTPUT_CHANNELS;i++) gains[i] = 0.0f; } -void ComputeBFormatGains(const ALCdevice *device, const ALfloat mtx[4], 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, j; + ALsizei i; - for(i = 0;i < device->NumChannels;i++) + for(i = 0;i < numchans;i++) + gains[i] = chanmap[i].Scale * coeffs[chanmap[i].Index] * ingain; + for(;i < MAX_OUTPUT_CHANNELS;i++) + gains[i] = 0.0f; +} + +void ComputeFirstOrderGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) +{ + ALsizei i, j; + + for(i = 0;i < numchans;i++) { float gain = 0.0f; for(j = 0;j < 4;j++) - gain += device->AmbiCoeffs[i][j] * mtx[j]; - gains[i] = gain * ingain; + gain += chancoeffs[i][j] * mtx[j]; + gains[i] = clampf(gain, 0.0f, 1.0f) * ingain; } for(;i < MAX_OUTPUT_CHANNELS;i++) gains[i] = 0.0f; } +void ComputeFirstOrderGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) +{ + ALsizei i; -DECL_CONST static inline const char *GetLabelFromChannel(enum Channel channel) + for(i = 0;i < numchans;i++) + gains[i] = chanmap[i].Scale * mtx[chanmap[i].Index] * ingain; + for(;i < MAX_OUTPUT_CHANNELS;i++) + gains[i] = 0.0f; +} + + +static inline const char *GetLabelFromChannel(enum Channel channel) { switch(channel) { @@ -178,10 +292,31 @@ DECL_CONST static inline const char *GetLabelFromChannel(enum Channel channel) case SideLeft: return "side-left"; case SideRight: return "side-right"; - case BFormatW: return "bformat-w"; - case BFormatX: return "bformat-x"; - case BFormatY: return "bformat-y"; - case BFormatZ: return "bformat-z"; + case UpperFrontLeft: return "upper-front-left"; + case UpperFrontRight: return "upper-front-right"; + case UpperBackLeft: return "upper-back-left"; + case UpperBackRight: return "upper-back-right"; + case LowerFrontLeft: return "lower-front-left"; + case LowerFrontRight: return "lower-front-right"; + case LowerBackLeft: return "lower-back-left"; + case LowerBackRight: return "lower-back-right"; + + case Aux0: return "aux-0"; + case Aux1: return "aux-1"; + case Aux2: return "aux-2"; + case Aux3: return "aux-3"; + case Aux4: return "aux-4"; + case Aux5: return "aux-5"; + case Aux6: return "aux-6"; + case Aux7: return "aux-7"; + case Aux8: return "aux-8"; + case Aux9: return "aux-9"; + case Aux10: return "aux-10"; + case Aux11: return "aux-11"; + case Aux12: return "aux-12"; + case Aux13: return "aux-13"; + case Aux14: return "aux-14"; + case Aux15: return "aux-15"; case InvalidChannel: break; } @@ -194,364 +329,943 @@ typedef struct ChannelMap { ChannelConfig Config; } ChannelMap; -static void SetChannelMap(ALCdevice *device, const ChannelMap *chanmap, size_t count, ALfloat ambiscale, ALboolean isfuma) +static void SetChannelMap(const enum Channel *devchans, ChannelConfig *ambicoeffs, + const ChannelMap *chanmap, size_t count, ALsizei *outcount) { size_t j, k; - ALuint i; + ALsizei i; - device->AmbiScale = ambiscale; - for(i = 0;i < MAX_OUTPUT_CHANNELS && device->ChannelName[i] != InvalidChannel;i++) + for(i = 0;i < MAX_OUTPUT_CHANNELS && devchans[i] != InvalidChannel;i++) { - if(device->ChannelName[i] == LFE) + if(devchans[i] == LFE) { for(j = 0;j < MAX_AMBI_COEFFS;j++) - device->AmbiCoeffs[i][j] = 0.0f; + ambicoeffs[i][j] = 0.0f; continue; } for(j = 0;j < count;j++) { - if(device->ChannelName[i] == chanmap[j].ChanName) - { - if(isfuma) - { - /* Reformat FuMa -> ACN/N3D */ - for(k = 0;k < MAX_AMBI_COEFFS;++k) - { - ALuint acn = FuMa2ACN[k]; - device->AmbiCoeffs[i][acn] = chanmap[j].Config[k] / FuMa2N3DScale[acn]; - } - } - else - { - for(k = 0;k < MAX_AMBI_COEFFS;++k) - device->AmbiCoeffs[i][k] = chanmap[j].Config[k]; - } - break; - } + if(devchans[i] != chanmap[j].ChanName) + continue; + + for(k = 0;k < MAX_AMBI_COEFFS;++k) + ambicoeffs[i][k] = chanmap[j].Config[k]; + break; } if(j == count) - ERR("Failed to match %s channel (%u) in config\n", GetLabelFromChannel(device->ChannelName[i]), i); + ERR("Failed to match %s channel (%u) in channel map\n", GetLabelFromChannel(devchans[i]), i); } - device->NumChannels = i; + *outcount = i; } -static bool LoadChannelSetup(ALCdevice *device) +static bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALsizei speakermap[MAX_OUTPUT_CHANNELS]) { - static const enum Channel mono_chans[1] = { - FrontCenter - }, stereo_chans[2] = { - FrontLeft, FrontRight - }, quad_chans[4] = { - FrontLeft, FrontRight, - BackLeft, BackRight - }, surround51_chans[5] = { - FrontLeft, FrontRight, FrontCenter, - SideLeft, SideRight - }, surround51rear_chans[5] = { - FrontLeft, FrontRight, FrontCenter, - BackLeft, BackRight - }, surround61_chans[6] = { - FrontLeft, FrontRight, - FrontCenter, BackCenter, - SideLeft, SideRight - }, surround71_chans[7] = { - FrontLeft, FrontRight, FrontCenter, - BackLeft, BackRight, - SideLeft, SideRight - }; - ChannelMap chanmap[MAX_OUTPUT_CHANNELS]; - const enum Channel *channels = NULL; - const char *layout = NULL; - ALfloat ambiscale = 1.0f; - size_t count = 0; - int isfuma; - int order; - size_t i; + ALsizei i; - switch(device->FmtChans) + for(i = 0;i < conf->NumSpeakers;i++) { - case DevFmtMono: - layout = "mono"; - channels = mono_chans; - count = COUNTOF(mono_chans); - break; - case DevFmtStereo: - layout = "stereo"; - channels = stereo_chans; - count = COUNTOF(stereo_chans); - break; - case DevFmtQuad: - layout = "quad"; - channels = quad_chans; - count = COUNTOF(quad_chans); - break; - case DevFmtX51: - layout = "surround51"; - channels = surround51_chans; - count = COUNTOF(surround51_chans); - break; - case DevFmtX51Rear: - layout = "surround51rear"; - channels = surround51rear_chans; - count = COUNTOF(surround51rear_chans); - break; - case DevFmtX61: - layout = "surround61"; - channels = surround61_chans; - count = COUNTOF(surround61_chans); - break; - case DevFmtX71: - layout = "surround71"; - channels = surround71_chans; - count = COUNTOF(surround71_chans); - break; - case DevFmtBFormat3D: - break; - } + int c = -1; - if(!layout) - return false; - else - { - char name[32] = {0}; - const char *type; - char eol; - - snprintf(name, sizeof(name), "%s/type", layout); - if(!ConfigValueStr(al_string_get_cstr(device->DeviceName), "layouts", name, &type)) - return false; - - if(sscanf(type, " %31[^: ] : %d%c", name, &order, &eol) != 2) + /* NOTE: AmbDec does not define any standard speaker names, however + * for this to work we have to by able to find the output channel + * the speaker definition corresponds to. Therefore, OpenAL Soft + * requires these channel labels to be recognized: + * + * LF = Front left + * RF = Front right + * LS = Side left + * RS = Side right + * LB = Back left + * RB = Back right + * CE = Front center + * CB = Back center + * + * Additionally, surround51 will acknowledge back speakers for side + * channels, and surround51rear will acknowledge side speakers for + * back channels, to avoid issues with an ambdec expecting 5.1 to + * use the side channels when the device is configured for back, + * and vice-versa. + */ + if(alstr_cmp_cstr(conf->Speakers[i].Name, "LF") == 0) + c = GetChannelIdxByName(device->RealOut, FrontLeft); + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "RF") == 0) + c = GetChannelIdxByName(device->RealOut, FrontRight); + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "CE") == 0) + c = GetChannelIdxByName(device->RealOut, FrontCenter); + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "LS") == 0) { - ERR("Invalid type value '%s' (expected name:order) for layout %s\n", type, layout); - return false; + if(device->FmtChans == DevFmtX51Rear) + c = GetChannelIdxByName(device->RealOut, BackLeft); + else + c = GetChannelIdxByName(device->RealOut, SideLeft); } - - if(strcasecmp(name, "fuma") == 0) - isfuma = 1; - else if(strcasecmp(name, "n3d") == 0) - isfuma = 0; + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "RS") == 0) + { + if(device->FmtChans == DevFmtX51Rear) + c = GetChannelIdxByName(device->RealOut, BackRight); + else + c = GetChannelIdxByName(device->RealOut, SideRight); + } + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "LB") == 0) + { + if(device->FmtChans == DevFmtX51) + c = GetChannelIdxByName(device->RealOut, SideLeft); + else + c = GetChannelIdxByName(device->RealOut, BackLeft); + } + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "RB") == 0) + { + if(device->FmtChans == DevFmtX51) + c = GetChannelIdxByName(device->RealOut, SideRight); + else + c = GetChannelIdxByName(device->RealOut, BackRight); + } + else if(alstr_cmp_cstr(conf->Speakers[i].Name, "CB") == 0) + c = GetChannelIdxByName(device->RealOut, BackCenter); else { - ERR("Unhandled type name '%s' (expected FuMa or N3D) for layout %s\n", name, layout); - return false; - } + const char *name = alstr_get_cstr(conf->Speakers[i].Name); + unsigned int n; + char ch; - if(order == 3) - ambiscale = THIRD_ORDER_SCALE; - else if(order == 2) - ambiscale = SECOND_ORDER_SCALE; - else if(order == 1) - ambiscale = FIRST_ORDER_SCALE; - else if(order == 0) - ambiscale = ZERO_ORDER_SCALE; - else + if(sscanf(name, "AUX%u%c", &n, &ch) == 1 && n < 16) + c = GetChannelIdxByName(device->RealOut, Aux0+n); + else + { + ERR("AmbDec speaker label \"%s\" not recognized\n", name); + return false; + } + } + if(c == -1) { - ERR("Unhandled type order %d (expected 0, 1, 2, or 3) for layout %s\n", order, layout); + ERR("Failed to lookup AmbDec speaker label %s\n", + alstr_get_cstr(conf->Speakers[i].Name)); return false; } + speakermap[i] = c; } - for(i = 0;i < count;i++) - { - float coeffs[MAX_AMBI_COEFFS] = {0.0f}; - const char *channame; - char chanlayout[32]; - const char *value; - int props = 0; - char eol = 0; - int j; - - chanmap[i].ChanName = channels[i]; - channame = GetLabelFromChannel(channels[i]); - - snprintf(chanlayout, sizeof(chanlayout), "%s/%s", layout, channame); - if(!ConfigValueStr(al_string_get_cstr(device->DeviceName), "layouts", chanlayout, &value)) - { - ERR("Missing channel %s\n", channame); - return false; - } - if(order == 3) - props = sscanf(value, " %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %c", - &coeffs[0], &coeffs[1], &coeffs[2], &coeffs[3], - &coeffs[4], &coeffs[5], &coeffs[6], &coeffs[7], - &coeffs[8], &coeffs[9], &coeffs[10], &coeffs[11], - &coeffs[12], &coeffs[13], &coeffs[14], &coeffs[15], - &eol - ); - else if(order == 2) - props = sscanf(value, " %f %f %f %f %f %f %f %f %f %c", - &coeffs[0], &coeffs[1], &coeffs[2], - &coeffs[3], &coeffs[4], &coeffs[5], - &coeffs[6], &coeffs[7], &coeffs[8], - &eol - ); - else if(order == 1) - props = sscanf(value, " %f %f %f %f %c", - &coeffs[0], &coeffs[1], - &coeffs[2], &coeffs[3], - &eol - ); - else if(order == 0) - props = sscanf(value, " %f %c", &coeffs[0], &eol); - if(props == 0) - { - ERR("Failed to parse option %s properties\n", chanlayout); - return false; - } - - if(props > (order+1)*(order+1)) - { - ERR("Excess elements in option %s (expected %d)\n", chanlayout, (order+1)*(order+1)); - return false; - } - - for(j = 0;j < MAX_AMBI_COEFFS;++j) - chanmap[i].Config[j] = coeffs[j]; - } - SetChannelMap(device, chanmap, count, ambiscale, isfuma); return true; } -ALvoid aluInitPanning(ALCdevice *device) + +static const ChannelMap MonoCfg[1] = { + { FrontCenter, { 1.0f } }, +}, StereoCfg[2] = { + { FrontLeft, { 5.00000000e-1f, 2.88675135e-1f, 0.0f, 1.19573156e-1f } }, + { FrontRight, { 5.00000000e-1f, -2.88675135e-1f, 0.0f, 1.19573156e-1f } }, +}, QuadCfg[4] = { + { 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[5] = { + { SideLeft, { 3.33001372e-1f, 1.89085671e-1f, 0.0f, -2.00041334e-1f, -2.12309737e-2f, 0.0f, 0.0f, 0.0f, -1.14573483e-2f } }, + { FrontLeft, { 1.47751298e-1f, 1.28994110e-1f, 0.0f, 1.15190495e-1f, 7.44949143e-2f, 0.0f, 0.0f, 0.0f, -6.47739980e-3f } }, + { FrontCenter, { 7.73595729e-2f, 0.00000000e+0f, 0.0f, 9.71390298e-2f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, 5.18625335e-2f } }, + { FrontRight, { 1.47751298e-1f, -1.28994110e-1f, 0.0f, 1.15190495e-1f, -7.44949143e-2f, 0.0f, 0.0f, 0.0f, -6.47739980e-3f } }, + { SideRight, { 3.33001372e-1f, -1.89085671e-1f, 0.0f, -2.00041334e-1f, 2.12309737e-2f, 0.0f, 0.0f, 0.0f, -1.14573483e-2f } }, +}, X51RearCfg[5] = { + { BackLeft, { 3.33001372e-1f, 1.89085671e-1f, 0.0f, -2.00041334e-1f, -2.12309737e-2f, 0.0f, 0.0f, 0.0f, -1.14573483e-2f } }, + { FrontLeft, { 1.47751298e-1f, 1.28994110e-1f, 0.0f, 1.15190495e-1f, 7.44949143e-2f, 0.0f, 0.0f, 0.0f, -6.47739980e-3f } }, + { FrontCenter, { 7.73595729e-2f, 0.00000000e+0f, 0.0f, 9.71390298e-2f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, 5.18625335e-2f } }, + { FrontRight, { 1.47751298e-1f, -1.28994110e-1f, 0.0f, 1.15190495e-1f, -7.44949143e-2f, 0.0f, 0.0f, 0.0f, -6.47739980e-3f } }, + { BackRight, { 3.33001372e-1f, -1.89085671e-1f, 0.0f, -2.00041334e-1f, 2.12309737e-2f, 0.0f, 0.0f, 0.0f, -1.14573483e-2f } }, +}, X61Cfg[6] = { + { SideLeft, { 2.04462744e-1f, 2.17178497e-1f, 0.0f, -4.39990188e-2f, -2.60787329e-2f, 0.0f, 0.0f, 0.0f, -6.87238843e-2f } }, + { FrontLeft, { 1.18130342e-1f, 9.34633906e-2f, 0.0f, 1.08553749e-1f, 6.80658795e-2f, 0.0f, 0.0f, 0.0f, 1.08999485e-2f } }, + { FrontCenter, { 7.73595729e-2f, 0.00000000e+0f, 0.0f, 9.71390298e-2f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, 5.18625335e-2f } }, + { FrontRight, { 1.18130342e-1f, -9.34633906e-2f, 0.0f, 1.08553749e-1f, -6.80658795e-2f, 0.0f, 0.0f, 0.0f, 1.08999485e-2f } }, + { SideRight, { 2.04462744e-1f, -2.17178497e-1f, 0.0f, -4.39990188e-2f, 2.60787329e-2f, 0.0f, 0.0f, 0.0f, -6.87238843e-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, bool periphonic) { - /* 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 } }, - }, StereoCfg[2] = { - { FrontLeft, { 0.707106781f, 0.0f, 0.5f, 0.0f } }, - { FrontRight, { 0.707106781f, 0.0f, -0.5f, 0.0f } }, - }, QuadCfg[4] = { - { FrontLeft, { 0.353553f, 0.306184f, 0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.117186f } }, - { FrontRight, { 0.353553f, 0.306184f, -0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.117186f } }, - { BackLeft, { 0.353553f, -0.306184f, 0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.117186f } }, - { BackRight, { 0.353553f, -0.306184f, -0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.117186f } }, - }, X51SideCfg[5] = { - { FrontLeft, { 0.208954f, 0.212846f, 0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f, 0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f, 0.047490f } }, - { FrontRight, { 0.208954f, 0.212846f, -0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f, -0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f, -0.047490f } }, - { 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 } }, - { SideLeft, { 0.470936f, -0.369626f, 0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f, -0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f, -0.043968f } }, - { SideRight, { 0.470936f, -0.369626f, -0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f, 0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f, 0.043968f } }, - }, X51RearCfg[5] = { - { FrontLeft, { 0.208954f, 0.212846f, 0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f, 0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f, 0.047490f } }, - { FrontRight, { 0.208954f, 0.212846f, -0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f, -0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f, -0.047490f } }, - { 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.470936f, -0.369626f, 0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f, -0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f, -0.043968f } }, - { BackRight, { 0.470936f, -0.369626f, -0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f, 0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f, 0.043968f } }, - }, 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 } }, - }, BFormat3D[4] = { - { BFormatW, { 1.0f, 0.0f, 0.0f, 0.0f } }, - { BFormatX, { 0.0f, 1.0f, 0.0f, 0.0f } }, - { BFormatY, { 0.0f, 0.0f, 1.0f, 0.0f } }, - { BFormatZ, { 0.0f, 0.0f, 0.0f, 1.0f } }, - }; - const ChannelMap *chanmap = NULL; - ALfloat ambiscale = 1.0f; - size_t count = 0; + const char *devname = alstr_get_cstr(device->DeviceName); + ALsizei i; - device->AmbiScale = 1.0f; - memset(device->AmbiCoeffs, 0, sizeof(device->AmbiCoeffs)); - device->NumChannels = 0; - - if(device->Hrtf) + if(GetConfigValueBool(devname, "decoder", "nfc", 1) && ctrl_dist > 0.0f) { - ALfloat (*coeffs_list[4])[2]; - ALuint *delay_list[4]; - ALuint i; + /* NFC is only used when AvgSpeakerDist is greater than 0, and + * METERS_PER_UNIT is also greater than 0. In addition, NFC can only be + * used when rendering to an ambisonic buffer. + */ + device->AvgSpeakerDist = ctrl_dist; - count = COUNTOF(BFormat3D); - chanmap = BFormat3D; - ambiscale = 1.0f; + device->Dry.NumChannelsPerOrder[0] = 1; + if(periphonic) + for(i = 1;i < order+1;i++) + device->Dry.NumChannelsPerOrder[i] = (i+1)*(i+1) - i*i; + else + for(i = 1;i < order+1;i++) + device->Dry.NumChannelsPerOrder[i] = (i*2+1) - ((i-1)*2+1); + for(;i < MAX_AMBI_ORDER+1;i++) + device->Dry.NumChannelsPerOrder[i] = 0; + } +} - for(i = 0;i < count;i++) - device->ChannelName[i] = chanmap[i].ChanName; - for(;i < MAX_OUTPUT_CHANNELS;i++) - device->ChannelName[i] = InvalidChannel; - SetChannelMap(device, chanmap, count, ambiscale, AL_TRUE); +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; + ALsizei total = 0; + ALsizei i; - for(i = 0;i < 4;++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++) { - static const enum Channel inputs[4] = { BFormatW, BFormatX, BFormatY, BFormatZ }; - int chan = GetChannelIdxByName(device, inputs[i]); - coeffs_list[i] = device->Hrtf_Params[chan].Coeffs; - delay_list[i] = device->Hrtf_Params[chan].Delay; - } - GetBFormatHrtfCoeffs(device->Hrtf, 4, coeffs_list, delay_list); + ALsizei chan = speakermap[i]; + ALfloat delay; - return; + /* 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(LoadChannelSetup(device)) - return; + 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; + ALsizei coeffcount = 0; + ALsizei count = 0; + ALsizei i, j; 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 = 4; break; case DevFmtX51: count = COUNTOF(X51SideCfg); chanmap = X51SideCfg; - ambiscale = THIRD_ORDER_SCALE; + coeffcount = 9; break; case DevFmtX51Rear: count = COUNTOF(X51RearCfg); chanmap = X51RearCfg; - ambiscale = THIRD_ORDER_SCALE; + coeffcount = 9; break; case DevFmtX61: count = COUNTOF(X61Cfg); chanmap = X61Cfg; - ambiscale = THIRD_ORDER_SCALE; + coeffcount = 9; break; case DevFmtX71: count = COUNTOF(X71Cfg); chanmap = X71Cfg; - ambiscale = THIRD_ORDER_SCALE; + coeffcount = 16; break; - case DevFmtBFormat3D: - count = COUNTOF(BFormat3D); - chanmap = BFormat3D; - ambiscale = 1.0f; + case DevFmtAmbi3D: break; } - SetChannelMap(device, chanmap, count, ambiscale, AL_TRUE); + if(device->FmtChans == DevFmtAmbi3D) + { + 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) ?*/ UnitScale; + ALfloat nfc_delay = 0.0f; + + count = (device->AmbiOrder == 3) ? 16 : + (device->AmbiOrder == 2) ? 9 : + (device->AmbiOrder == 1) ? 4 : 1; + for(i = 0;i < count;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->AmbiOrder < 2) + { + device->FOAOut.Ambi = device->Dry.Ambi; + device->FOAOut.CoeffCount = device->Dry.CoeffCount; + device->FOAOut.NumChannels = 0; + } + else + { + /* FOA output is always ACN+N3D for higher-order ambisonic output. + * The upsampler expects this and will convert it for output. + */ + memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi)); + for(i = 0;i < 4;i++) + { + device->FOAOut.Ambi.Map[i].Scale = 1.0f; + device->FOAOut.Ambi.Map[i].Index = i; + } + device->FOAOut.CoeffCount = 0; + device->FOAOut.NumChannels = 4; + + ambiup_reset(device->AmbiUp, device); + } + + if(ConfigValueFloat(devname, "decoder", "nfc-ref-delay", &nfc_delay) && nfc_delay > 0.0f) + { + nfc_delay = clampf(nfc_delay, 0.001f, 1000.0f); + InitNearFieldCtrl(device, nfc_delay * SPEEDOFSOUNDMETRESPERSEC, + device->AmbiOrder, true); + } + } + else + { + ALfloat w_scale, xyz_scale; + + SetChannelMap(device->RealOut.ChannelName, device->Dry.Ambi.Coeffs, + chanmap, count, &device->Dry.NumChannels); + device->Dry.CoeffCount = coeffcount; + + w_scale = (device->Dry.CoeffCount > 9) ? W_SCALE2D_THIRD : + (device->Dry.CoeffCount > 4) ? W_SCALE2D_SECOND : 1.0f; + xyz_scale = (device->Dry.CoeffCount > 9) ? XYZ_SCALE2D_THIRD : + (device->Dry.CoeffCount > 4) ? XYZ_SCALE2D_SECOND : 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] * w_scale; + for(j = 1;j < 4;j++) + 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 ALsizei speakermap[MAX_OUTPUT_CHANNELS]) +{ + ChannelMap chanmap[MAX_OUTPUT_CHANNELS]; + const ALfloat *coeff_scale = UnitScale; + 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&AMBI_PERIPHONIC_MASK)) + { + if(conf->ChanMask > 0x1ff) + { + w_scale = W_SCALE3D_THIRD; + xyz_scale = XYZ_SCALE3D_THIRD; + } + else if(conf->ChanMask > 0xf) + { + w_scale = W_SCALE3D_SECOND; + xyz_scale = XYZ_SCALE3D_SECOND; + } + } + else + { + if(conf->ChanMask > 0x1ff) + { + w_scale = W_SCALE2D_THIRD; + xyz_scale = XYZ_SCALE2D_THIRD; + } + else if(conf->ChanMask > 0xf) + { + w_scale = W_SCALE2D_SECOND; + xyz_scale = XYZ_SCALE2D_SECOND; + } + } + + if(conf->CoeffScale == ADS_SN3D) + coeff_scale = SN3D2N3DScale; + else if(conf->CoeffScale == ADS_FuMa) + coeff_scale = FuMa2N3DScale; + + for(i = 0;i < conf->NumSpeakers;i++) + { + ALsizei chan = speakermap[i]; + ALfloat gain; + ALsizei k = 0; + + for(j = 0;j < MAX_AMBI_COEFFS;j++) + chanmap[i].Config[j] = 0.0f; + + chanmap[i].ChanName = device->RealOut.ChannelName[chan]; + for(j = 0;j < MAX_AMBI_COEFFS;j++) + { + if(j == 0) gain = conf->HFOrderGain[0]; + else if(j == 1) gain = conf->HFOrderGain[1]; + else if(j == 4) gain = conf->HFOrderGain[2]; + else if(j == 9) gain = conf->HFOrderGain[3]; + if((conf->ChanMask&(1<HFMatrix[i][k++] / coeff_scale[j] * gain; + } + } + + SetChannelMap(device->RealOut.ChannelName, device->Dry.Ambi.Coeffs, chanmap, + 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] * w_scale; + for(j = 1;j < 4;j++) + 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 ALsizei speakermap[MAX_OUTPUT_CHANNELS]) +{ + ALfloat avg_dist; + ALsizei count; + ALsizei i; + + if((conf->ChanMask&AMBI_PERIPHONIC_MASK)) + { + count = (conf->ChanMask > 0x1ff) ? 16 : + (conf->ChanMask > 0xf) ? 9 : 4; + for(i = 0;i < count;i++) + { + device->Dry.Ambi.Map[i].Scale = 1.0f; + device->Dry.Ambi.Map[i].Index = i; + } + } + else + { + static const int map[MAX_AMBI2D_COEFFS] = { 0, 1, 3, 4, 8, 9, 15 }; + + count = (conf->ChanMask > 0x1ff) ? 7 : + (conf->ChanMask > 0xf) ? 5 : 3; + for(i = 0;i < count;i++) + { + device->Dry.Ambi.Map[i].Scale = 1.0f; + device->Dry.Ambi.Map[i].Index = map[i]; + } + } + device->Dry.CoeffCount = 0; + device->Dry.NumChannels = count; + + TRACE("Enabling %s-band %s-order%s ambisonic decoder\n", + (conf->FreqBands == 1) ? "single" : "dual", + (conf->ChanMask > 0xf) ? (conf->ChanMask > 0x1ff) ? "third" : "second" : "first", + (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? " periphonic" : "" + ); + bformatdec_reset(device->AmbiDecoder, conf, count, device->Frequency, speakermap); + + 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) + ); + + InitDistanceComp(device, conf, speakermap); +} + +static void InitHrtfPanning(ALCdevice *device) +{ + /* NOTE: azimuth goes clockwise. */ + static const ALfloat AmbiPoints[][2] = { + { 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 AmbiMatrixFOA[][2][MAX_AMBI_COEFFS] = { + { { 1.88982237e-001f, 0.00000000e+000f, 1.90399923e-001f, 0.00000000e+000f }, { 7.14285714e-002f, 0.00000000e+000f, 1.24646009e-001f, 0.00000000e+000f } }, + { { 1.88982237e-001f, 1.09057783e-001f, 1.09208910e-001f, 1.09057783e-001f }, { 7.14285714e-002f, 7.13950780e-002f, 7.14940135e-002f, 7.13950780e-002f } }, + { { 1.88982237e-001f, -1.09057783e-001f, 1.09208910e-001f, 1.09057783e-001f }, { 7.14285714e-002f, -7.13950780e-002f, 7.14940135e-002f, 7.13950780e-002f } }, + { { 1.88982237e-001f, -1.09057783e-001f, 1.09208910e-001f, -1.09057783e-001f }, { 7.14285714e-002f, -7.13950780e-002f, 7.14940135e-002f, -7.13950780e-002f } }, + { { 1.88982237e-001f, 1.09057783e-001f, 1.09208910e-001f, -1.09057783e-001f }, { 7.14285714e-002f, 7.13950780e-002f, 7.14940135e-002f, -7.13950780e-002f } }, + { { 1.88982237e-001f, 0.00000000e+000f, 0.00000000e+000f, 1.88281281e-001f }, { 7.14285714e-002f, 0.00000000e+000f, 0.00000000e+000f, 1.23259031e-001f } }, + { { 1.88982237e-001f, -1.88281281e-001f, 0.00000000e+000f, 0.00000000e+000f }, { 7.14285714e-002f, -1.23259031e-001f, 0.00000000e+000f, 0.00000000e+000f } }, + { { 1.88982237e-001f, 0.00000000e+000f, 0.00000000e+000f, -1.88281281e-001f }, { 7.14285714e-002f, 0.00000000e+000f, 0.00000000e+000f, -1.23259031e-001f } }, + { { 1.88982237e-001f, 1.88281281e-001f, 0.00000000e+000f, 0.00000000e+000f }, { 7.14285714e-002f, 1.23259031e-001f, 0.00000000e+000f, 0.00000000e+000f } }, + { { 1.88982237e-001f, 1.09057783e-001f, -1.09208910e-001f, 1.09057783e-001f }, { 7.14285714e-002f, 7.13950780e-002f, -7.14940135e-002f, 7.13950780e-002f } }, + { { 1.88982237e-001f, -1.09057783e-001f, -1.09208910e-001f, 1.09057783e-001f }, { 7.14285714e-002f, -7.13950780e-002f, -7.14940135e-002f, 7.13950780e-002f } }, + { { 1.88982237e-001f, -1.09057783e-001f, -1.09208910e-001f, -1.09057783e-001f }, { 7.14285714e-002f, -7.13950780e-002f, -7.14940135e-002f, -7.13950780e-002f } }, + { { 1.88982237e-001f, 1.09057783e-001f, -1.09208910e-001f, -1.09057783e-001f }, { 7.14285714e-002f, 7.13950780e-002f, -7.14940135e-002f, -7.13950780e-002f } }, + { { 1.88982237e-001f, 0.00000000e+000f, -1.90399923e-001f, 0.00000000e+000f }, { 7.14285714e-002f, 0.00000000e+000f, -1.24646009e-001f, 0.00000000e+000f } } + }, AmbiMatrixHOA[][2][MAX_AMBI_COEFFS] = { + { { 1.43315266e-001f, 0.00000000e+000f, 1.90399923e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 1.18020996e-001f, 0.00000000e+000f, 0.00000000e+000f }, { 7.26741039e-002f, 0.00000000e+000f, 1.24646009e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 1.49618920e-001f, 0.00000000e+000f, 0.00000000e+000f } }, + { { 1.40852210e-001f, 1.09057783e-001f, 1.09208910e-001f, 1.09057783e-001f, 7.58818830e-002f, 7.66295578e-002f, -3.28314629e-004f, 7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, 7.13950780e-002f, 7.14940135e-002f, 7.13950780e-002f, 9.61978444e-002f, 9.71456952e-002f, -4.16214759e-004f, 9.71456952e-002f, 0.00000000e+000f } }, + { { 1.40852210e-001f, -1.09057783e-001f, 1.09208910e-001f, 1.09057783e-001f, -7.58818830e-002f, -7.66295578e-002f, -3.28314629e-004f, 7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, -7.13950780e-002f, 7.14940135e-002f, 7.13950780e-002f, -9.61978444e-002f, -9.71456952e-002f, -4.16214759e-004f, 9.71456952e-002f, 0.00000000e+000f } }, + { { 1.40852210e-001f, -1.09057783e-001f, 1.09208910e-001f, -1.09057783e-001f, 7.58818830e-002f, -7.66295578e-002f, -3.28314629e-004f, -7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, -7.13950780e-002f, 7.14940135e-002f, -7.13950780e-002f, 9.61978444e-002f, -9.71456952e-002f, -4.16214759e-004f, -9.71456952e-002f, 0.00000000e+000f } }, + { { 1.40852210e-001f, 1.09057783e-001f, 1.09208910e-001f, -1.09057783e-001f, -7.58818830e-002f, 7.66295578e-002f, -3.28314629e-004f, -7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, 7.13950780e-002f, 7.14940135e-002f, -7.13950780e-002f, -9.61978444e-002f, 9.71456952e-002f, -4.16214759e-004f, -9.71456952e-002f, 0.00000000e+000f } }, + { { 1.39644596e-001f, 0.00000000e+000f, 0.00000000e+000f, 1.88281281e-001f, 0.00000000e+000f, 0.00000000e+000f, -5.83538687e-002f, 0.00000000e+000f, 1.01835015e-001f }, { 7.08127349e-002f, 0.00000000e+000f, 0.00000000e+000f, 1.23259031e-001f, 0.00000000e+000f, 0.00000000e+000f, -7.39770307e-002f, 0.00000000e+000f, 1.29099445e-001f } }, + { { 1.39644596e-001f, -1.88281281e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, -5.83538687e-002f, 0.00000000e+000f, -1.01835015e-001f }, { 7.08127349e-002f, -1.23259031e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, -7.39770307e-002f, 0.00000000e+000f, -1.29099445e-001f } }, + { { 1.39644596e-001f, 0.00000000e+000f, 0.00000000e+000f, -1.88281281e-001f, 0.00000000e+000f, 0.00000000e+000f, -5.83538687e-002f, 0.00000000e+000f, 1.01835015e-001f }, { 7.08127349e-002f, 0.00000000e+000f, 0.00000000e+000f, -1.23259031e-001f, 0.00000000e+000f, 0.00000000e+000f, -7.39770307e-002f, 0.00000000e+000f, 1.29099445e-001f } }, + { { 1.39644596e-001f, 1.88281281e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, -5.83538687e-002f, 0.00000000e+000f, -1.01835015e-001f }, { 7.08127349e-002f, 1.23259031e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, -7.39770307e-002f, 0.00000000e+000f, -1.29099445e-001f } }, + { { 1.40852210e-001f, 1.09057783e-001f, -1.09208910e-001f, 1.09057783e-001f, 7.58818830e-002f, -7.66295578e-002f, -3.28314629e-004f, -7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, 7.13950780e-002f, -7.14940135e-002f, 7.13950780e-002f, 9.61978444e-002f, -9.71456952e-002f, -4.16214759e-004f, -9.71456952e-002f, 0.00000000e+000f } }, + { { 1.40852210e-001f, -1.09057783e-001f, -1.09208910e-001f, 1.09057783e-001f, -7.58818830e-002f, 7.66295578e-002f, -3.28314629e-004f, -7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, -7.13950780e-002f, -7.14940135e-002f, 7.13950780e-002f, -9.61978444e-002f, 9.71456952e-002f, -4.16214759e-004f, -9.71456952e-002f, 0.00000000e+000f } }, + { { 1.40852210e-001f, -1.09057783e-001f, -1.09208910e-001f, -1.09057783e-001f, 7.58818830e-002f, 7.66295578e-002f, -3.28314629e-004f, 7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, -7.13950780e-002f, -7.14940135e-002f, -7.13950780e-002f, 9.61978444e-002f, 9.71456952e-002f, -4.16214759e-004f, 9.71456952e-002f, 0.00000000e+000f } }, + { { 1.40852210e-001f, 1.09057783e-001f, -1.09208910e-001f, -1.09057783e-001f, -7.58818830e-002f, -7.66295578e-002f, -3.28314629e-004f, 7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, 7.13950780e-002f, -7.14940135e-002f, -7.13950780e-002f, -9.61978444e-002f, -9.71456952e-002f, -4.16214759e-004f, 9.71456952e-002f, 0.00000000e+000f } }, + { { 1.43315266e-001f, 0.00000000e+000f, -1.90399923e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 1.18020996e-001f, 0.00000000e+000f, 0.00000000e+000f }, { 7.26741039e-002f, 0.00000000e+000f, -1.24646009e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 1.49618920e-001f, 0.00000000e+000f, 0.00000000e+000f } }, + }; + const ALfloat (*AmbiMatrix)[2][MAX_AMBI_COEFFS] = device->AmbiUp ? AmbiMatrixHOA : + AmbiMatrixFOA; + ALsizei count = device->AmbiUp ? 9 : 4; + ALsizei i; + + static_assert(COUNTOF(AmbiPoints) <= HRTF_AMBI_MAX_CHANNELS, "HRTF_AMBI_MAX_CHANNELS is too small"); + + 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 = 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++) + { + device->FOAOut.Ambi.Map[i].Scale = 1.0f; + device->FOAOut.Ambi.Map[i].Index = i; + } + device->FOAOut.CoeffCount = 0; + device->FOAOut.NumChannels = 4; + + ambiup_reset(device->AmbiUp, device); + } + else + { + device->FOAOut.Ambi = device->Dry.Ambi; + device->FOAOut.CoeffCount = device->Dry.CoeffCount; + device->FOAOut.NumChannels = 0; + } + + device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder); + + device->Hrtf->IrSize = BuildBFormatHrtf(device->HrtfHandle, + device->Hrtf, device->Dry.NumChannels, + AmbiPoints, AmbiMatrix, COUNTOF(AmbiPoints) + ); +} + +static void InitUhjPanning(ALCdevice *device) +{ + ALsizei count = 3; + ALsizei i; + + for(i = 0;i < count;i++) + { + ALsizei acn = FuMa2ACN[i]; + device->Dry.Ambi.Map[i].Scale = 1.0f/FuMa2N3DScale[acn]; + device->Dry.Ambi.Map[i].Index = acn; + } + device->Dry.CoeffCount = 0; + device->Dry.NumChannels = count; + + 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; + + 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; + } + + if(device->FmtChans != DevFmtStereo) + { + 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->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT; + + ambdec_init(&conf); + + devname = alstr_get_cstr(device->DeviceName); + switch(device->FmtChans) + { + case DevFmtQuad: layout = "quad"; break; + case DevFmtX51: /* fall-through */ + case DevFmtX51Rear: layout = "surround51"; break; + case DevFmtX61: layout = "surround61"; break; + case DevFmtX71: layout = "surround71"; break; + /* Mono, Stereo, and Ambisonics output don't use custom decoders. */ + case DevFmtMono: + case DevFmtStereo: + case DevFmtAmbi3D: + break; + } + if(layout) + { + const char *fname; + if(ConfigValueStr(devname, "decoder", layout, &fname)) + { + if(!ambdec_load(&conf, fname)) + ERR("Failed to load layout file %s\n", fname); + else + { + if(conf.ChanMask > 0xffff) + ERR("Unsupported channel mask 0x%04x (max 0xffff)\n", conf.ChanMask); + else + { + if(MakeSpeakerMap(device, &conf, speakermap)) + pconf = &conf; + } + } + } + } + + if(pconf && GetConfigValueBool(devname, "decoder", "hq-mode", 0)) + { + ambiup_free(device->AmbiUp); + device->AmbiUp = NULL; + if(!device->AmbiDecoder) + device->AmbiDecoder = bformatdec_alloc(); + } + else + { + bformatdec_free(device->AmbiDecoder); + device->AmbiDecoder = NULL; + if(device->FmtChans == DevFmtAmbi3D && device->AmbiOrder > 1) + { + if(!device->AmbiUp) + device->AmbiUp = ambiup_alloc(); + } + else + { + ambiup_free(device->AmbiUp); + device->AmbiUp = NULL; + } + } + + if(!pconf) + InitPanning(device); + else if(device->AmbiDecoder) + InitHQPanning(device, pconf, speakermap); + else + InitCustomPanning(device, pconf, speakermap); + + ambdec_deinit(&conf); + return; + } + + bformatdec_free(device->AmbiDecoder); + device->AmbiDecoder = NULL; + + headphones = device->IsHeadphones; + if(device->Type != Loopback) + { + const char *mode; + if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "stereo-mode", &mode)) + { + if(strcasecmp(mode, "headphones") == 0) + headphones = true; + else if(strcasecmp(mode, "speakers") == 0) + headphones = false; + else if(strcasecmp(mode, "auto") != 0) + ERR("Unexpected stereo-mode: %s\n", mode); + } + } + + if(hrtf_userreq == Hrtf_Default) + { + bool usehrtf = (headphones && hrtf_appreq != Hrtf_Disable) || + (hrtf_appreq == Hrtf_Enable); + if(!usehrtf) goto no_hrtf; + + device->HrtfStatus = ALC_HRTF_ENABLED_SOFT; + if(headphones && hrtf_appreq != Hrtf_Disable) + device->HrtfStatus = ALC_HRTF_HEADPHONES_DETECTED_SOFT; + } + else + { + if(hrtf_userreq != Hrtf_Enable) + { + if(hrtf_appreq == Hrtf_Enable) + device->HrtfStatus = ALC_HRTF_DENIED_SOFT; + goto no_hrtf; + } + device->HrtfStatus = ALC_HRTF_REQUIRED_SOFT; + } + + if(VECTOR_SIZE(device->HrtfList) == 0) + { + VECTOR_DEINIT(device->HrtfList); + device->HrtfList = EnumerateHrtf(device->DeviceName); + } + + if(hrtf_id >= 0 && (size_t)hrtf_id < VECTOR_SIZE(device->HrtfList)) + { + const EnumeratedHrtf *entry = &VECTOR_ELEM(device->HrtfList, hrtf_id); + struct Hrtf *hrtf = GetLoadedHrtf(entry->hrtf); + if(hrtf && hrtf->sampleRate == device->Frequency) + { + device->HrtfHandle = hrtf; + alstr_copy(&device->HrtfName, entry->name); + } + else if(hrtf) + Hrtf_DecRef(hrtf); + } + + for(i = 0;!device->HrtfHandle && i < VECTOR_SIZE(device->HrtfList);i++) + { + const EnumeratedHrtf *entry = &VECTOR_ELEM(device->HrtfList, i); + struct Hrtf *hrtf = GetLoadedHrtf(entry->hrtf); + if(hrtf && hrtf->sampleRate == device->Frequency) + { + device->HrtfHandle = hrtf; + alstr_copy(&device->HrtfName, entry->name); + } + else if(hrtf) + Hrtf_DecRef(hrtf); + } + + if(device->HrtfHandle) + { + if(old_hrtf) + Hrtf_DecRef(old_hrtf); + old_hrtf = NULL; + + device->Render_Mode = HrtfRender; + if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "hrtf-mode", &mode)) + { + if(strcasecmp(mode, "full") == 0) + device->Render_Mode = HrtfRender; + else if(strcasecmp(mode, "basic") == 0) + device->Render_Mode = NormalRender; + else + ERR("Unexpected hrtf-mode: %s\n", mode); + } + + 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); + device->AmbiUp = NULL; + } + 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->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); + device->AmbiUp = NULL; + + bs2blevel = ((headphones && hrtf_appreq != Hrtf_Disable) || + (hrtf_appreq == Hrtf_Enable)) ? 5 : 0; + if(device->Type != Loopback) + 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); + TRACE("BS2B enabled\n"); + InitPanning(device); + return; + } + + TRACE("BS2B disabled\n"); + + if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "stereo-encoding", &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) + { + device->Uhj_Encoder = al_calloc(16, sizeof(Uhj2Encoder)); + TRACE("UHJ enabled\n"); + InitUhjPanning(device); + return; + } + + TRACE("UHJ disabled\n"); + InitPanning(device); +} + + +void aluInitEffectPanning(ALeffectslot *slot) +{ + ALsizei i; + + memset(slot->ChanMap, 0, sizeof(slot->ChanMap)); + slot->NumChannels = 0; + + for(i = 0;i < MAX_EFFECT_CHANNELS;i++) + { + slot->ChanMap[i].Scale = 1.0f; + slot->ChanMap[i].Index = i; + } + slot->NumChannels = i; } diff --git a/libs/openal-soft/Alc/uhjfilter.c b/libs/openal-soft/Alc/uhjfilter.c new file mode 100644 index 00000000..8e2febae --- /dev/null +++ b/libs/openal-soft/Alc/uhjfilter.c @@ -0,0 +1,134 @@ + +#include "config.h" + +#include "alu.h" +#include "uhjfilter.h" + +/* This is the maximum number of samples processed for each inner loop + * iteration. */ +#define MAX_UPDATE_SAMPLES 128 + + +static const ALfloat Filter1Coeff[4] = { + 0.6923878f, 0.9360654322959f, 0.9882295226860f, 0.9987488452737f +}; +static const ALfloat Filter2Coeff[4] = { + 0.4021921162426f, 0.8561710882420f, 0.9722909545651f, 0.9952884791278f +}; + +static void allpass_process(AllPassState *state, ALfloat *restrict dst, const ALfloat *restrict src, const ALfloat aa, ALsizei todo) +{ + ALsizei i; + + if(todo > 1) + { + 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]; + } +} + + +/* NOTE: There seems to be a bit of an inconsistency in how this encoding is + * supposed to work. Some references, such as + * + * http://members.tripod.com/martin_leese/Ambisonic/UHJ_file_format.html + * + * specify a pre-scaling of sqrt(2) on the W channel input, while other + * references, such as + * + * https://en.wikipedia.org/wiki/Ambisonic_UHJ_format#Encoding.5B1.5D + * and + * https://wiki.xiph.org/Ambisonics#UHJ_format + * + * do not. The sqrt(2) scaling is in line with B-Format decoder coefficients + * which include such a scaling for the W channel input, however the original + * source for this equation is a 1985 paper by Michael Gerzon, which does not + * apparently include the scaling. Applying the extra scaling creates a louder + * result with a narrower stereo image compared to not scaling, and I don't + * know which is the intended result. + */ + +void 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]; + ALsizei base, i; + + for(base = 0;base < SamplesToDo;) + { + ALsizei todo = mini(SamplesToDo - base, MAX_UPDATE_SAMPLES); + + /* 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); + /* 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); + for(i = 1;i < todo;i++) + D[i] = 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); + for(i = 0;i < todo;i++) + D[i] += temp[0][i]; + + /* S = 0.9396926*W + 0.1855740*X */ + 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); + for(i = 1;i < todo;i++) + S[i] = temp[0][i-1]; + + /* Left = (S + D)/2.0 */ + for(i = 0;i < todo;i++) + *(LeftOut++) += (S[i] + D[i]) * 0.5f; + /* Right = (S - D)/2.0 */ + for(i = 0;i < todo;i++) + *(RightOut++) += (S[i] - D[i]) * 0.5f; + + base += todo; + } +} diff --git a/libs/openal-soft/Alc/uhjfilter.h b/libs/openal-soft/Alc/uhjfilter.h new file mode 100644 index 00000000..48c2fde6 --- /dev/null +++ b/libs/openal-soft/Alc/uhjfilter.h @@ -0,0 +1,49 @@ +#ifndef UHJFILTER_H +#define UHJFILTER_H + +#include "AL/al.h" + +#include "alMain.h" + +typedef struct AllPassState { + ALfloat x[2]; /* Last two input samples */ + ALfloat y[2]; /* Last two output samples */ +} AllPassState; + +/* Encoding 2-channel UHJ from B-Format is done as: + * + * S = 0.9396926*W + 0.1855740*X + * D = j(-0.3420201*W + 0.5098604*X) + 0.6554516*Y + * + * Left = (S + D)/2.0 + * Right = (S - D)/2.0 + * + * where j is a wide-band +90 degree phase shift. + * + * The phase shift is done using a Hilbert transform, described here: + * https://web.archive.org/web/20060708031958/http://www.biochem.oulu.fi/~oniemita/dsp/hilbert/ + * It works using 2 sets of 4 chained filters. The first filter chain produces + * a phase shift of varying magnitude over a wide range of frequencies, while + * the second filter chain produces a phase shift 90 degrees ahead of the + * first over the same range. + * + * Combining these two stages requires the use of three filter chains. S- + * channel output uses a Filter1 chain on the W and X channel mix, while the D- + * channel output uses a Filter1 chain on the Y channel plus a Filter2 chain on + * the W and X channel mix. This results in the W and X input mix on the D- + * channel output having the required +90 degree phase shift relative to the + * other inputs. + */ + +typedef struct Uhj2Encoder { + AllPassState Filter1_WX[4]; + AllPassState Filter1_Y[4]; + AllPassState Filter2_WX[4]; +} Uhj2Encoder; + +/* Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input + * 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/libs/openal-soft/Alc/vector.h b/libs/openal-soft/Alc/vector.h index c1fc925d..ed0780e3 100644 --- a/libs/openal-soft/Alc/vector.h +++ b/libs/openal-soft/Alc/vector.h @@ -5,11 +5,8 @@ #include -/* "Base" vector type, designed to alias with the actual vector types. */ -typedef struct vector__s { - size_t Capacity; - size_t Size; -} *vector_; +#include "almalloc.h" + #define TYPEDEF_VECTOR(T, N) typedef struct { \ size_t Capacity; \ @@ -27,38 +24,47 @@ typedef const _##N* const_##N; #define VECTOR_INIT(_x) do { (_x) = NULL; } while(0) #define VECTOR_INIT_STATIC() NULL -#define VECTOR_DEINIT(_x) do { free((_x)); (_x) = NULL; } while(0) +#define VECTOR_DEINIT(_x) do { al_free((_x)); (_x) = NULL; } while(0) -/* Helper to increase a vector's reserve. Do not call directly. */ -ALboolean vector_reserve(char *ptr, size_t base_size, size_t obj_size, size_t obj_count, ALboolean exact); -#define VECTOR_RESERVE(_x, _c) (vector_reserve((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_c), AL_TRUE)) - -ALboolean vector_resize(char *ptr, size_t base_size, size_t obj_size, size_t obj_count); -#define VECTOR_RESIZE(_x, _c) (vector_resize((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_c))) +#define VECTOR_RESIZE(_x, _s, _c) do { \ + size_t _size = (_s); \ + size_t _cap = (_c); \ + if(_size > _cap) \ + _cap = _size; \ + \ + if(!(_x) && _cap == 0) \ + break; \ + \ + if(((_x) ? (_x)->Capacity : 0) < _cap) \ + { \ + ptrdiff_t data_offset = (char*)((_x)->Data) - (char*)(_x); \ + size_t old_size = ((_x) ? (_x)->Size : 0); \ + void *temp; \ + \ + temp = al_calloc(16, data_offset + sizeof((_x)->Data[0])*_cap); \ + assert(temp != NULL); \ + if((_x)) \ + memcpy(((char*)temp)+data_offset, (_x)->Data, \ + sizeof((_x)->Data[0])*old_size); \ + \ + al_free((_x)); \ + (_x) = temp; \ + (_x)->Capacity = _cap; \ + } \ + (_x)->Size = _size; \ +} while(0) \ #define VECTOR_CAPACITY(_x) ((_x) ? (_x)->Capacity : 0) #define VECTOR_SIZE(_x) ((_x) ? (_x)->Size : 0) -#define VECTOR_ITER_BEGIN(_x) ((_x) ? (_x)->Data + 0 : NULL) -#define VECTOR_ITER_END(_x) ((_x) ? (_x)->Data + (_x)->Size : NULL) +#define VECTOR_BEGIN(_x) ((_x) ? (_x)->Data + 0 : NULL) +#define VECTOR_END(_x) ((_x) ? (_x)->Data + (_x)->Size : NULL) -ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_pos, const void *datstart, const void *datend); -#ifdef __GNUC__ -#define TYPE_CHECK(T1, T2) __builtin_types_compatible_p(T1, T2) -#define VECTOR_INSERT(_x, _i, _s, _e) __extension__({ \ - ALboolean _r; \ - static_assert(TYPE_CHECK(__typeof((_x)->Data[0]), __typeof(*(_i))), "Incompatible insertion iterator"); \ - static_assert(TYPE_CHECK(__typeof((_x)->Data[0]), __typeof(*(_s))), "Incompatible insertion source type"); \ - static_assert(TYPE_CHECK(__typeof(*(_s)), __typeof(*(_e))), "Incompatible iterator sources"); \ - _r = vector_insert((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_i), (_s), (_e)); \ - _r; \ -}) -#else -#define VECTOR_INSERT(_x, _i, _s, _e) (vector_insert((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_i), (_s), (_e))) -#endif - -#define VECTOR_PUSH_BACK(_x, _obj) (vector_reserve((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), VECTOR_SIZE(_x)+1, AL_FALSE) && \ - (((_x)->Data[(_x)->Size++] = (_obj)),AL_TRUE)) +#define VECTOR_PUSH_BACK(_x, _obj) do { \ + size_t _pbsize = VECTOR_SIZE(_x)+1; \ + VECTOR_RESIZE(_x, _pbsize, _pbsize); \ + (_x)->Data[(_x)->Size-1] = (_obj); \ +} while(0) #define VECTOR_POP_BACK(_x) ((void)((_x)->Size--)) #define VECTOR_BACK(_x) ((_x)->Data[(_x)->Size-1]) @@ -67,22 +73,15 @@ ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_ #define VECTOR_ELEM(_x, _o) ((_x)->Data[(_o)]) #define VECTOR_FOR_EACH(_t, _x, _f) do { \ - _t *_iter = VECTOR_ITER_BEGIN((_x)); \ - _t *_end = VECTOR_ITER_END((_x)); \ + _t *_iter = VECTOR_BEGIN((_x)); \ + _t *_end = VECTOR_END((_x)); \ for(;_iter != _end;++_iter) \ _f(_iter); \ } while(0) -#define VECTOR_FOR_EACH_PARAMS(_t, _x, _f, ...) do { \ - _t *_iter = VECTOR_ITER_BEGIN((_x)); \ - _t *_end = VECTOR_ITER_END((_x)); \ - for(;_iter != _end;++_iter) \ - _f(__VA_ARGS__, _iter); \ -} while(0) - #define VECTOR_FIND_IF(_i, _t, _x, _f) do { \ - _t *_iter = VECTOR_ITER_BEGIN((_x)); \ - _t *_end = VECTOR_ITER_END((_x)); \ + _t *_iter = VECTOR_BEGIN((_x)); \ + _t *_end = VECTOR_END((_x)); \ for(;_iter != _end;++_iter) \ { \ if(_f(_iter)) \ @@ -91,15 +90,4 @@ ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_ (_i) = _iter; \ } while(0) -#define VECTOR_FIND_IF_PARMS(_i, _t, _x, _f, ...) do { \ - _t *_iter = VECTOR_ITER_BEGIN((_x)); \ - _t *_end = VECTOR_ITER_END((_x)); \ - for(;_iter != _end;++_iter) \ - { \ - if(_f(__VA_ARGS__, _iter)) \ - break; \ - } \ - (_i) = _iter; \ -} while(0) - #endif /* AL_VECTOR_H */ diff --git a/libs/openal-soft/CMakeLists.txt b/libs/openal-soft/CMakeLists.txt index 6b7815b2..a871f4cf 100644 --- a/libs/openal-soft/CMakeLists.txt +++ b/libs/openal-soft/CMakeLists.txt @@ -1,15 +1,21 @@ # CMake build file list for OpenAL -CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +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,10 +27,12 @@ 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) @@ -33,26 +41,32 @@ OPTION(ALSOFT_DLOPEN "Check for the dlopen API for loading optional libs" ON) OPTION(ALSOFT_WERROR "Treat compile warnings as errors" OFF) -OPTION(ALSOFT_UTILS "Build and install utility programs" OFF) +OPTION(ALSOFT_UTILS "Build and install utility programs" ON) OPTION(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" OFF) -OPTION(ALSOFT_EXAMPLES "Build and install example programs" OFF) -OPTION(ALSOFT_TESTS "Build and install test programs" OFF) +OPTION(ALSOFT_EXAMPLES "Build and install example programs" ON) +OPTION(ALSOFT_TESTS "Build and install test programs" ON) -OPTION(ALSOFT_CONFIG "Install alsoft.conf sample configuration file" OFF) -OPTION(ALSOFT_HRTF_DEFS "Install HRTF definition files" OFF) -OPTION(ALSOFT_INSTALL "Install headers and libraries" OFF) +OPTION(ALSOFT_CONFIG "Install alsoft.conf sample configuration file" ON) +OPTION(ALSOFT_HRTF_DEFS "Install HRTF definition files" ON) +OPTION(ALSOFT_AMBDEC_PRESETS "Install AmbDec preset files" ON) +OPTION(ALSOFT_INSTALL "Install headers and libraries" ON) + +if(DEFINED SHARE_INSTALL_DIR) + message(WARNING "SHARE_INSTALL_DIR is deprecated. Use the variables provided by the GNUInstallDirs module instead") + set(CMAKE_INSTALL_DATADIR "${SHARE_INSTALL_DIR}") +endif() + +if(DEFINED LIB_SUFFIX) + message(WARNING "LIB_SUFFIX is deprecated. Use the variables provided by the GNUInstallDirs module instead") +endif() -set(SHARE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share" CACHE STRING "The share install dir") - - -IF(NOT WIN32) - SET(LIBNAME openal) -ELSE() - SET(LIBNAME OpenAL32) +IF(WIN32) ADD_DEFINITIONS("-D_WIN32 -D_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 # required IDs are defined. @@ -71,20 +85,21 @@ ELSE() ENDIF() +SET(EXTRA_LIBS "") +SET(EXTRA_LDFLAGS "") + # 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(EXTRA_LDFLAGS "${EXTRA_LDFLAGS} -L/usr/lib") ENDIF() -SET(OPENAL_LIB_NAME ${LIBNAME} PARENT_SCOPE) - IF(NOT LIBTYPE) SET(LIBTYPE SHARED) 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}") @@ -106,17 +121,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) + ADD_DEFINITIONS(-D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600) ENDIF() ENDIF() UNSET(OLD_REQUIRED_FLAGS) @@ -158,6 +178,8 @@ 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") @@ -172,6 +194,37 @@ IF(CMAKE_COMPILER_IS_GNUCC) SET(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}") ENDIF() +# Check if we have a proper timespec declaration +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") +ENDIF() + +# Some systems may need libatomic for C11 atomic functions to work +SET(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) +SET(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES} atomic) +CHECK_C_SOURCE_COMPILES("#include +int _Atomic foo = ATOMIC_VAR_INIT(0); +int main() +{ + return atomic_fetch_add(&foo, 2); +}" +HAVE_LIBATOMIC) +IF(NOT HAVE_LIBATOMIC) + SET(CMAKE_REQUIRED_LIBRARIES "${OLD_REQUIRED_LIBRARIES}") +ELSE() + SET(EXTRA_LIBS atomic ${EXTRA_LIBS}) +ENDIF() +UNSET(OLD_REQUIRED_LIBRARIES) + +# 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 variable length arrays CHECK_C_SOURCE_COMPILES( "int main(int argc, char *argv[]) @@ -213,21 +266,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 foo = ATOMIC_VAR_INIT(0); int main() { - 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 @@ -240,10 +288,11 @@ 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) - ADD_DEFINITIONS("/wd4098") + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} /wd4098") IF(NOT DXSDK_DIR) STRING(REGEX REPLACE "\\\\" "/" DXSDK_DIR "$ENV{DXSDK_DIR}") @@ -265,25 +314,14 @@ IF(MSVC) ENDFOREACH(flag_var) ENDIF() ELSE() - ADD_DEFINITIONS(-Winline -Wall) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Winline -Wall") CHECK_C_COMPILER_FLAG(-Wextra HAVE_W_EXTRA) IF(HAVE_W_EXTRA) - ADD_DEFINITIONS(-Wextra) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Wextra") ENDIF() IF(ALSOFT_WERROR) - ADD_DEFINITIONS(-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) - ADD_DEFINITIONS(-fPIC) - ENDIF() + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Werror") ENDIF() # We want RelWithDebInfo to actually include debug stuff (define _DEBUG @@ -294,6 +332,11 @@ ELSE() ENDIF() ENDFOREACH() + CHECK_C_COMPILER_FLAG(-fno-math-errno HAVE_FNO_MATH_ERRNO) + IF(HAVE_FNO_MATH_ERRNO) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fno-math-errno") + ENDIF() + CHECK_C_SOURCE_COMPILES("int foo() __attribute__((destructor)); int main() {return 0;}" HAVE_GCC_DESTRUCTOR) @@ -310,7 +353,7 @@ int main() HAVE_STATIC_LIBGCC_SWITCH ) if(HAVE_STATIC_LIBGCC_SWITCH) - set(EXTRA_LIBS ${EXTRA_LIBS} -static-libgcc) + set(EXTRA_LDFLAGS "${EXTRA_LDFLAGS} -static-libgcc") endif() set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES}) unset(OLD_REQUIRED_LIBRARIES) @@ -345,7 +388,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) - ADD_DEFINITIONS(-fvisibility=hidden) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden") ENDIF() ENDIF() @@ -358,27 +401,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 "") -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() +SET(FPU_NEON_SWITCH "") + +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,6 +502,7 @@ CHECK_SYMBOL_EXISTS(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN) CHECK_SYMBOL_EXISTS(_aligned_malloc malloc.h HAVE__ALIGNED_MALLOC) CHECK_SYMBOL_EXISTS(lrintf math.h HAVE_LRINTF) CHECK_SYMBOL_EXISTS(modff math.h HAVE_MODFF) +CHECK_SYMBOL_EXISTS(log2f math.h HAVE_LOG2F) IF(NOT HAVE_C99_VLA) CHECK_SYMBOL_EXISTS(alloca malloc.h HAVE_ALLOCA) IF(NOT HAVE_ALLOCA) @@ -535,9 +596,9 @@ IF(NOT HAVE_WINDOWS_H) CHECK_C_COMPILER_FLAG(-pthread HAVE_PTHREAD) IF(HAVE_PTHREAD) - ADD_DEFINITIONS(-pthread) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -pthread") SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -pthread") - SET(EXTRA_LIBS ${EXTRA_LIBS} -pthread) + SET(EXTRA_LDFLAGS "${EXTRA_LDFLAGS} -pthread") ENDIF() CHECK_LIBRARY_EXISTS(pthread pthread_create "" HAVE_LIBPTHREAD) @@ -562,6 +623,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() @@ -578,6 +649,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() @@ -609,7 +689,8 @@ IF(NOT HAVE_STDINT_H) ENDIF() -SET(COMMON_OBJS common/atomic.c +SET(COMMON_OBJS common/almalloc.c + common/atomic.c common/rwlock.c common/threads.c common/uintmap.c @@ -631,7 +712,8 @@ SET(ALC_OBJS Alc/ALc.c Alc/alcConfig.c Alc/alcRing.c Alc/bs2b.c - Alc/effects/autowah.c + Alc/converter.c + Alc/mastering.c Alc/effects/chorus.c Alc/effects/compressor.c Alc/effects/dedicated.c @@ -645,6 +727,10 @@ SET(ALC_OBJS Alc/ALc.c Alc/helpers.c Alc/bsinc.c Alc/hrtf.c + Alc/uhjfilter.c + Alc/ambdec.c + Alc/bformatdec.c + Alc/nfcfilter.c Alc/panning.c Alc/mixer.c Alc/mixer_c.c @@ -761,6 +847,10 @@ IF(HAVE_ARM_NEON_H) IF(ALSOFT_CPUEXT_NEON) SET(HAVE_NEON 1) SET(ALC_OBJS ${ALC_OBJS} Alc/mixer_neon.c) + IF(FPU_NEON_SWITCH) + SET_SOURCE_FILES_PROPERTIES(Alc/mixer_neon.c PROPERTIES + COMPILE_FLAGS "${FPU_NEON_SWITCH}") + ENDIF() SET(CPU_EXTS "${CPU_EXTS}, Neon") ENDIF() ENDIF() @@ -798,9 +888,6 @@ 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() ENDIF() ENDIF() IF(ALSOFT_REQUIRE_ALSA AND NOT HAVE_ALSA) @@ -816,8 +903,8 @@ 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() ENDIF() ENDIF() @@ -834,9 +921,6 @@ 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() ENDIF() ENDIF() IF(ALSOFT_REQUIRE_SOLARIS AND NOT HAVE_SOLARIS) @@ -853,9 +937,6 @@ 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() ENDIF() ENDIF() IF(ALSOFT_REQUIRE_SNDIO AND NOT HAVE_SNDIO) @@ -872,9 +953,6 @@ 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() ENDIF() ENDIF() IF(ALSOFT_REQUIRE_QSA AND NOT HAVE_QSA) @@ -910,9 +988,6 @@ 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() ENDIF() ENDIF() @@ -947,9 +1022,6 @@ 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() ENDIF() ENDIF() IF(ALSOFT_REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO) @@ -966,9 +1038,6 @@ 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() ENDIF() ENDIF() IF(ALSOFT_REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO) @@ -985,9 +1054,6 @@ 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() ENDIF() ENDIF() IF(ALSOFT_REQUIRE_JACK AND NOT HAVE_JACK) @@ -1057,6 +1123,40 @@ ENDIF() # This is always available SET(BACKENDS "${BACKENDS} Null") +option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library footprint)" OFF) +if(ALSOFT_EMBED_HRTF_DATA) + 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") + ADD_CUSTOM_COMMAND(OUTPUT "${BIN2H_COMMAND}" + COMMAND ${CMAKE_COMMAND} "${NATIVE_SRC_DIR}" + COMMAND ${CMAKE_COMMAND} --build . --config "Release" + WORKING_DIRECTORY "${NATIVE_BIN_DIR}" + DEPENDS "${NATIVE_SRC_DIR}CMakeLists.txt" "${NATIVE_SRC_DIR}bin2h.c" + VERBATIM + ) + + 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 "${BIN2H_COMMAND}" "${infile}" + COMMENT "Generating ${FILENAME}.h" + 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() + + IF(ALSOFT_UTILS AND NOT ALSOFT_NO_CONFIG_UTIL) add_subdirectory(utils/alsoft-config) ENDIF() @@ -1064,13 +1164,7 @@ IF(ALSOFT_EXAMPLES) FIND_PACKAGE(SDL2) IF(SDL2_FOUND) FIND_PACKAGE(SDL_sound) - IF(SDL_SOUND_FOUND AND CMAKE_VERSION VERSION_LESS "2.8.8") - INCLUDE_DIRECTORIES(${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}) - ENDIF() FIND_PACKAGE(FFmpeg COMPONENTS AVFORMAT AVCODEC AVUTIL SWSCALE SWRESAMPLE) - IF(FFMPEG_FOUND AND CMAKE_VERSION VERSION_LESS "2.8.8") - INCLUDE_DIRECTORIES(${FFMPEG_INCLUDE_DIRS}) - ENDIF() ENDIF() ENDIF() @@ -1079,12 +1173,38 @@ IF(LIBTYPE STREQUAL "STATIC") SET(PKG_CONFIG_CFLAGS -DAL_LIBTYPE_STATIC ${PKG_CONFIG_CFLAGS}) ENDIF() +IF(NOT WIN32) + SET(LIBNAME "openal") +ELSE() + SET(LIBNAME "OpenAL32") +ENDIF() + +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() + # Needed for openal.pc.in SET(prefix ${CMAKE_INSTALL_PREFIX}) SET(exec_prefix "\${prefix}") -SET(libdir "\${exec_prefix}/lib${LIB_SUFFIX}") -SET(bindir "\${exec_prefix}/bin") -SET(includedir "\${prefix}/include") +SET(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") +SET(bindir "\${exec_prefix}/${CMAKE_INSTALL_BINDIR}") +SET(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") SET(PACKAGE_VERSION "${LIB_VERSION}") # End configuration @@ -1096,101 +1216,147 @@ CONFIGURE_FILE( "${OpenAL_BINARY_DIR}/openal.pc" @ONLY) -# Build a common library with reusable helpers -ADD_LIBRARY(common STATIC ${COMMON_OBJS}) -IF(NOT LIBTYPE STREQUAL "STATIC") - SET_PROPERTY(TARGET common PROPERTY POSITION_INDEPENDENT_CODE TRUE) -ENDIF() +MACRO(ADD_INCLUDE_DIRS TRGT TESTVAR INCVAR) + IF(${TESTVAR}) + SET_PROPERTY(TARGET ${TRGT} APPEND PROPERTY INCLUDE_DIRECTORIES ${${INCVAR}}) + ENDIF() +ENDMACRO() + +UNSET(HAS_ROUTER) +SET(IMPL_TARGET OpenAL) # Build main library IF(LIBTYPE STREQUAL "STATIC") - ADD_LIBRARY(${LIBNAME} STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS}) + 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_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 "") + IF(WIN32 AND ALSOFT_BUILD_ROUTER) + ADD_LIBRARY(OpenAL SHARED router/router.c router/alc.c router/al.c ${COMMON_OBJS}) + SET_PROPERTY(TARGET OpenAL APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) + SET_PROPERTY(TARGET OpenAL APPEND PROPERTY + COMPILE_DEFINITIONS AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES + ) + SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS ${EXTRA_LDFLAGS}) + IF(MSVC) + SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS " /SUBSYSTEM:WINDOWS") + ELSEIF(CMAKE_COMPILER_IS_GNUCC) + SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS " -mwindows") + ENDIF() + 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) - 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") + SET(LIBNAME "soft_oal") + SET(IMPL_TARGET soft_oal) + ENDIF() + + ADD_LIBRARY(${IMPL_TARGET} SHARED ${COMMON_OBJS} ${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} +) +SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND PROPERTY INCLUDE_DIRECTORIES + "${OpenAL_SOURCE_DIR}/OpenAL32/Include" "${OpenAL_SOURCE_DIR}/Alc" +) +ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_ALSA ALSA_INCLUDE_DIRS) +ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_OSS OSS_INCLUDE_DIRS) +ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_SOLARIS AUDIOIO_INCLUDE_DIRS) +ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_SNDIO SOUNDIO_INCLUDE_DIRS) +ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_QSA QSA_INCLUDE_DIRS) +ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_DSOUND DSOUND_INCLUDE_DIRS) +ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_PORTAUDIO PORTAUDIO_INCLUDE_DIRS) +ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_PULSEAUDIO PULSEAUDIO_INCLUDE_DIRS) +ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_JACK JACK_INCLUDE_DIRS) +SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND PROPERTY + COMPILE_DEFINITIONS AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES +) +IF(WIN32 AND ALSOFT_NO_UID_DEFS) + SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND PROPERTY COMPILE_DEFINITIONS AL_NO_UID_DEFS) +ENDIF() +SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) +SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY LINK_FLAGS ${EXTRA_LDFLAGS}) + +TARGET_LINK_LIBRARIES(${IMPL_TARGET} ${EXTRA_LIBS}) +IF(TARGET build_version) + ADD_DEPENDENCIES(${IMPL_TARGET} build_version) +ENDIF() + +IF(WIN32) + IF(MSVC) + SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY LINK_FLAGS " /SUBSYSTEM:WINDOWS") + ELSEIF(CMAKE_COMPILER_IS_GNUCC) + SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY LINK_FLAGS " -mwindows") + ENDIF() + + if(NOT LIBTYPE STREQUAL "STATIC") + 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_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() - IF(NOT DLLTOOL_EXECUTABLE) - MESSAGE(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation") - ENDIF() - ELSE() - SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES LINK_FLAGS "-Wl,--output-def,${LIBNAME}.def") - ADD_CUSTOM_COMMAND(TARGET ${LIBNAME} POST_BUILD - COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" ${LIBNAME}.def - COMMAND "${DLLTOOL_EXECUTABLE}" -d ${LIBNAME}.def -l ${LIBNAME}.lib -D ${LIBNAME}.dll - COMMENT "Stripping ordinals from ${LIBNAME}.def and generating ${LIBNAME}.lib..." - VERBATIM - ) ENDIF() ENDIF() ENDIF() -TARGET_LINK_LIBRARIES(${LIBNAME} common ${EXTRA_LIBS}) -TARGET_INCLUDE_DIRECTORIES(${LIBNAME} PUBLIC include) - IF(ALSOFT_INSTALL) - # Add an install target here - INSTALL(TARGETS ${LIBNAME} - RUNTIME DESTINATION bin - LIBRARY DESTINATION "lib${LIB_SUFFIX}" - ARCHIVE DESTINATION "lib${LIB_SUFFIX}" + 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 include/AL/efx.h include/AL/efx-creative.h include/AL/efx-presets.h - DESTINATION include/AL + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/AL ) INSTALL(FILES "${OpenAL_BINARY_DIR}/openal.pc" - DESTINATION "lib${LIB_SUFFIX}/pkgconfig") + 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}") @@ -1207,10 +1373,15 @@ IF(WIN32) ENDIF() ENDIF() +if(ALSOFT_EMBED_HRTF_DATA) + message(STATUS "Embedding HRTF datasets") + message(STATUS "") +endif() + # Install alsoft.conf configuration file IF(ALSOFT_CONFIG) INSTALL(FILES alsoftrc.sample - DESTINATION ${SHARE_INSTALL_DIR}/openal + DESTINATION ${CMAKE_INSTALL_DATADIR}/openal ) MESSAGE(STATUS "Installing sample configuration") MESSAGE(STATUS "") @@ -1220,31 +1391,48 @@ ENDIF() IF(ALSOFT_HRTF_DEFS) INSTALL(FILES hrtf/default-44100.mhr hrtf/default-48000.mhr - DESTINATION ${SHARE_INSTALL_DIR}/openal/hrtf + DESTINATION ${CMAKE_INSTALL_DATADIR}/openal/hrtf ) MESSAGE(STATUS "Installing HRTF definitions") MESSAGE(STATUS "") ENDIF() +# Install AmbDec presets +IF(ALSOFT_AMBDEC_PRESETS) + INSTALL(FILES presets/3D7.1.ambdec + presets/hexagon.ambdec + presets/itu5.1.ambdec + presets/rectangle.ambdec + presets/square.ambdec + presets/presets.txt + DESTINATION ${CMAKE_INSTALL_DATADIR}/openal/presets + ) + MESSAGE(STATUS "Installing AmbDec presets") + MESSAGE(STATUS "") +ENDIF() + IF(ALSOFT_UTILS) ADD_EXECUTABLE(openal-info utils/openal-info.c) - TARGET_LINK_LIBRARIES(openal-info ${LIBNAME}) + SET_PROPERTY(TARGET openal-info APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) + TARGET_LINK_LIBRARIES(openal-info 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) ENDIF() ADD_EXECUTABLE(bsincgen utils/bsincgen.c) + SET_PROPERTY(TARGET bsincgen APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) IF(HAVE_LIBM) TARGET_LINK_LIBRARIES(bsincgen m) ENDIF() IF(ALSOFT_INSTALL) INSTALL(TARGETS openal-info makehrtf bsincgen - RUNTIME DESTINATION bin - LIBRARY DESTINATION "lib${LIB_SUFFIX}" - ARCHIVE DESTINATION "lib${LIB_SUFFIX}" + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) ENDIF() @@ -1256,113 +1444,130 @@ IF(ALSOFT_UTILS) ENDIF() IF(ALSOFT_TESTS) - ADD_LIBRARY(test-common STATIC examples/common/alhelpers.c) + SET(TEST_COMMON_OBJS examples/common/alhelpers.c) - ADD_EXECUTABLE(altonegen examples/altonegen.c) - TARGET_LINK_LIBRARIES(altonegen test-common ${LIBNAME}) + ADD_EXECUTABLE(altonegen examples/altonegen.c ${COMMON_OBJS} ${TEST_COMMON_OBJS}) + TARGET_LINK_LIBRARIES(altonegen OpenAL) + SET_PROPERTY(TARGET altonegen APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) - IF(ALSOFT_INSTALL) - INSTALL(TARGETS altonegen - RUNTIME DESTINATION bin - LIBRARY DESTINATION "lib${LIB_SUFFIX}" - ARCHIVE DESTINATION "lib${LIB_SUFFIX}" - ) - ENDIF() + IF(ALSOFT_INSTALL) + INSTALL(TARGETS altonegen + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + ENDIF() - MESSAGE(STATUS "Building test programs") - MESSAGE(STATUS "") + MESSAGE(STATUS "Building test programs") + MESSAGE(STATUS "") ENDIF() IF(ALSOFT_EXAMPLES) - IF(SDL2_FOUND AND SDL_SOUND_FOUND) - ADD_LIBRARY(ex-common STATIC examples/common/alhelpers.c - examples/common/sdl_sound.c) - SET_PROPERTY(TARGET ex-common APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} - ${SDL_SOUND_INCLUDE_DIR}) + ADD_EXECUTABLE(alrecord examples/alrecord.c ${COMMON_OBJS}) + TARGET_LINK_LIBRARIES(alrecord OpenAL) + SET_PROPERTY(TARGET alrecord APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS}) - 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 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 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 INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} - ${SDL_SOUND_INCLUDE_DIR}) + IF(SDL2_FOUND) + IF(SDL_SOUND_FOUND) + SET(EX_COMMON_OBJS examples/common/alhelpers.c) - 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 INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} - ${SDL_SOUND_INCLUDE_DIR}) - - 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 INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} - ${SDL_SOUND_INCLUDE_DIR}) - - IF(ALSOFT_INSTALL) - INSTALL(TARGETS alstream alreverb allatency alloopback alhrtf - RUNTIME DESTINATION bin - LIBRARY DESTINATION "lib${LIB_SUFFIX}" - ARCHIVE DESTINATION "lib${LIB_SUFFIX}" + ADD_EXECUTABLE(alstream examples/alstream.c ${COMMON_OBJS} ${EX_COMMON_OBJS}) + TARGET_LINK_LIBRARIES(alstream ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} OpenAL) + 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} ) + + ADD_EXECUTABLE(alreverb examples/alreverb.c ${COMMON_OBJS} ${EX_COMMON_OBJS}) + TARGET_LINK_LIBRARIES(alreverb ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} OpenAL) + 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} + ) + + ADD_EXECUTABLE(allatency examples/allatency.c ${COMMON_OBJS} ${EX_COMMON_OBJS}) + TARGET_LINK_LIBRARIES(allatency ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} OpenAL) + 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} + ) + + ADD_EXECUTABLE(alloopback examples/alloopback.c ${COMMON_OBJS} ${EX_COMMON_OBJS}) + TARGET_LINK_LIBRARIES(alloopback ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} OpenAL) + 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(alhrtf examples/alhrtf.c ${COMMON_OBJS} ${EX_COMMON_OBJS}) + TARGET_LINK_LIBRARIES(alhrtf ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} OpenAL) + 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} + ) + + 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} + ) + 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 INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} - ${FFMPEG_INCLUDE_DIRS}) + IF(FFVER_OK) + ADD_EXECUTABLE(alffplay examples/alffplay.cpp ${COMMON_OBJS}) + TARGET_LINK_LIBRARIES(alffplay ${SDL2_LIBRARY} ${FFMPEG_LIBRARIES} OpenAL) + 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(ALSOFT_INSTALL) INSTALL(TARGETS alffplay - RUNTIME DESTINATION bin - LIBRARY DESTINATION "lib${LIB_SUFFIX}" - ARCHIVE DESTINATION "lib${LIB_SUFFIX}" + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + 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() ENDIF() - -if(MEGA) - install(TARGETS ${LIBNAME} RUNTIME DESTINATION . LIBRARY DESTINATION .) -ENDIF() diff --git a/libs/openal-soft/ChangeLog b/libs/openal-soft/ChangeLog index 189872cf..5300be12 100644 --- a/libs/openal-soft/ChangeLog +++ b/libs/openal-soft/ChangeLog @@ -1,3 +1,120 @@ +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/libs/openal-soft/OpenAL32/Include/alAuxEffectSlot.h b/libs/openal-soft/OpenAL32/Include/alAuxEffectSlot.h index 0f0d3ef8..5be9ae7a 100644 --- a/libs/openal-soft/OpenAL32/Include/alAuxEffectSlot.h +++ b/libs/openal-soft/OpenAL32/Include/alAuxEffectSlot.h @@ -4,6 +4,7 @@ #include "alMain.h" #include "alEffect.h" +#include "atomic.h" #include "align.h" #ifdef __cplusplus @@ -14,15 +15,22 @@ struct ALeffectStateVtable; struct ALeffectslot; typedef struct ALeffectState { + RefCount Ref; const struct ALeffectStateVtable *vtbl; + + ALfloat (*OutBuffer)[BUFFERSIZE]; + ALsizei OutChannels; } ALeffectState; +void ALeffectState_Construct(ALeffectState *state); +void ALeffectState_Destruct(ALeffectState *state); + struct ALeffectStateVtable { void (*const Destruct)(ALeffectState *state); ALboolean (*const deviceUpdate)(ALeffectState *state, ALCdevice *device); - void (*const update)(ALeffectState *state, ALCdevice *device, const struct ALeffectslot *slot); - void (*const process)(ALeffectState *state, ALuint samplesToDo, const ALfloat *restrict samplesIn, ALfloat (*restrict samplesOut)[BUFFERSIZE], ALuint numChannels); + void (*const update)(ALeffectState *state, const ALCdevice *device, 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); }; @@ -30,8 +38,8 @@ struct ALeffectStateVtable { #define DEFINE_ALEFFECTSTATE_VTABLE(T) \ DECLARE_THUNK(T, ALeffectState, void, Destruct) \ DECLARE_THUNK1(T, ALeffectState, ALboolean, deviceUpdate, ALCdevice*) \ -DECLARE_THUNK2(T, ALeffectState, void, update, ALCdevice*, const ALeffectslot*) \ -DECLARE_THUNK4(T, ALeffectState, void, process, ALuint, const ALfloat*restrict, ALfloatBUFFERSIZE*restrict, ALuint) \ +DECLARE_THUNK3(T, ALeffectState, void, update, const ALCdevice*, 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)); } \ \ @@ -64,36 +72,101 @@ static const struct ALeffectStateFactoryVtable T##_ALeffectStateFactory_vtable = } +#define MAX_EFFECT_CHANNELS (4) + + +struct ALeffectslotArray { + ALsizei count; + struct ALeffectslot *slot[]; +}; + + +struct ALeffectslotProps { + ALfloat Gain; + ALboolean AuxSendAuto; + + ALenum Type; + ALeffectProps Props; + + ALeffectState *State; + + ATOMIC(struct ALeffectslotProps*) next; +}; + + typedef struct ALeffectslot { - ALenum EffectType; - ALeffectProps EffectProps; + ALfloat Gain; + ALboolean AuxSendAuto; - volatile ALfloat Gain; - volatile ALboolean AuxSendAuto; + struct { + ALenum Type; + ALeffectProps Props; - ATOMIC(ALenum) NeedsUpdate; - ALeffectState *EffectState; + ALeffectState *State; + } Effect; - alignas(16) ALfloat WetBuffer[1][BUFFERSIZE]; + ATOMIC_FLAG PropsClean; RefCount ref; + ATOMIC(struct ALeffectslotProps*) Update; + ATOMIC(struct ALeffectslotProps*) FreeList; + + struct { + ALfloat Gain; + ALboolean AuxSendAuto; + + ALenum EffectType; + ALeffectState *EffectState; + + ALfloat RoomRolloff; /* Added to the source's room rolloff, not multiplied. */ + ALfloat DecayTime; + ALfloat DecayHFRatio; + ALboolean DecayHFLimit; + ALfloat AirAbsorptionGainHF; + } Params; + /* Self ID */ ALuint id; + + 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 + * 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]; } 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*)LookupUIntMapKey(&context->EffectSlotMap, id); } +{ return (struct ALeffectslot*)LookupUIntMapKeyNoLock(&context->EffectSlotMap, id); } inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id) -{ return (struct ALeffectslot*)RemoveUIntMapKey(&context->EffectSlotMap, id); } +{ return (struct ALeffectslot*)RemoveUIntMapKeyNoLock(&context->EffectSlotMap, id); } ALenum InitEffectSlot(ALeffectslot *slot); +void DeinitEffectSlot(ALeffectslot *slot); +void UpdateEffectSlotProps(ALeffectslot *slot); +void UpdateAllEffectSlotProps(ALCcontext *context); ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context); ALeffectStateFactory *ALnullStateFactory_getFactory(void); ALeffectStateFactory *ALreverbStateFactory_getFactory(void); -ALeffectStateFactory *ALautowahStateFactory_getFactory(void); ALeffectStateFactory *ALchorusStateFactory_getFactory(void); ALeffectStateFactory *ALcompressorStateFactory_getFactory(void); ALeffectStateFactory *ALdistortionStateFactory_getFactory(void); diff --git a/libs/openal-soft/OpenAL32/Include/alBuffer.h b/libs/openal-soft/OpenAL32/Include/alBuffer.h index dd046da8..b13e1231 100644 --- a/libs/openal-soft/OpenAL32/Include/alBuffer.h +++ b/libs/openal-soft/OpenAL32/Include/alBuffer.h @@ -17,28 +17,26 @@ enum UserFmtType { UserFmtUInt = AL_UNSIGNED_INT_SOFT, UserFmtFloat = AL_FLOAT_SOFT, UserFmtDouble = AL_DOUBLE_SOFT, - UserFmtByte3 = AL_BYTE3_SOFT, - UserFmtUByte3 = AL_UNSIGNED_BYTE3_SOFT, - UserFmtMulaw, - UserFmtAlaw, + UserFmtMulaw = AL_MULAW_SOFT, + UserFmtAlaw = 0x10000000, 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 = 0x10000000, /* WXY */ - UserFmtBFormat3D, /* WXYZ */ + 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 */ }; -ALuint BytesFromUserFmt(enum UserFmtType type) DECL_CONST; -ALuint ChannelsFromUserFmt(enum UserFmtChannels chans) DECL_CONST; -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); } @@ -63,9 +61,9 @@ enum FmtChannels { }; #define MAX_INPUT_CHANNELS (8) -ALuint BytesFromFmt(enum FmtType type) DECL_CONST; -ALuint ChannelsFromFmt(enum FmtChannels chans) DECL_CONST; -inline ALuint FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type) +ALsizei BytesFromFmt(enum FmtType type); +ALsizei ChannelsFromFmt(enum FmtChannels chans); +inline ALsizei FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type) { return ChannelsFromFmt(chans) * BytesFromFmt(type); } @@ -80,14 +78,15 @@ typedef struct ALbuffer { enum FmtChannels FmtChannels; enum FmtType FmtType; + ALuint BytesAlloc; enum UserFmtChannels OriginalChannels; enum UserFmtType OriginalType; ALsizei OriginalSize; ALsizei OriginalAlign; - ALsizei LoopStart; - ALsizei LoopEnd; + ALsizei LoopStart; + ALsizei LoopEnd; ATOMIC(ALsizei) UnpackAlign; ATOMIC(ALsizei) PackAlign; @@ -106,10 +105,19 @@ 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*)LookupUIntMapKey(&device->BufferMap, id); } +{ return (struct ALbuffer*)LookupUIntMapKeyNoLock(&device->BufferMap, id); } inline struct ALbuffer *RemoveBuffer(ALCdevice *device, ALuint id) -{ return (struct ALbuffer*)RemoveUIntMapKey(&device->BufferMap, id); } +{ return (struct ALbuffer*)RemoveUIntMapKeyNoLock(&device->BufferMap, id); } ALvoid ReleaseALBuffers(ALCdevice *device); diff --git a/libs/openal-soft/OpenAL32/Include/alEffect.h b/libs/openal-soft/OpenAL32/Include/alEffect.h index 91ee782f..80f029a9 100644 --- a/libs/openal-soft/OpenAL32/Include/alEffect.h +++ b/libs/openal-soft/OpenAL32/Include/alEffect.h @@ -10,17 +10,16 @@ extern "C" { struct ALeffect; enum { - EAXREVERB = 0, - REVERB, - AUTOWAH, - CHORUS, - COMPRESSOR, - DISTORTION, - ECHO, - EQUALIZER, - FLANGER, - MODULATOR, - DEDICATED, + AL__EAXREVERB = 0, + AL__REVERB, + AL__CHORUS, + AL__COMPRESSOR, + AL__DISTORTION, + AL__ECHO, + AL__EQUALIZER, + AL__FLANGER, + AL__MODULATOR, + AL__DEDICATED, MAX_EFFECTS }; @@ -51,7 +50,6 @@ const struct ALeffectVtable T##_vtable = { \ extern const struct ALeffectVtable ALeaxreverb_vtable; extern const struct ALeffectVtable ALreverb_vtable; -extern const struct ALeffectVtable ALautowah_vtable; extern const struct ALeffectVtable ALchorus_vtable; extern const struct ALeffectVtable ALcompressor_vtable; extern const struct ALeffectVtable ALdistortion_vtable; @@ -93,13 +91,6 @@ typedef union ALeffectProps { ALfloat LFReference; } Reverb; - struct { - ALfloat AttackTime; - ALfloat ReleaseTime; - ALfloat PeakGain; - ALfloat Resonance; - } Autowah; - struct { ALint Waveform; ALint Phase; @@ -176,10 +167,19 @@ typedef struct ALeffect { 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*)LookupUIntMapKey(&device->EffectMap, id); } +{ return (struct ALeffect*)LookupUIntMapKeyNoLock(&device->EffectMap, id); } inline struct ALeffect *RemoveEffect(ALCdevice *device, ALuint id) -{ return (struct ALeffect*)RemoveUIntMapKey(&device->EffectMap, id); } +{ return (struct ALeffect*)RemoveUIntMapKeyNoLock(&device->EffectMap, id); } inline ALboolean IsReverbEffect(ALenum type) { return type == AL_EFFECT_REVERB || type == AL_EFFECT_EAXREVERB; } diff --git a/libs/openal-soft/OpenAL32/Include/alFilter.h b/libs/openal-soft/OpenAL32/Include/alFilter.h index 6f44e3b7..227d50cf 100644 --- a/libs/openal-soft/OpenAL32/Include/alFilter.h +++ b/libs/openal-soft/OpenAL32/Include/alFilter.h @@ -42,13 +42,11 @@ typedef enum ALfilterType { typedef struct ALfilterState { ALfloat x[2]; /* History of two last input samples */ ALfloat y[2]; /* History of two last output samples */ + ALfloat b0, b1, b2; /* Transfer function coefficients "b" */ ALfloat a1, a2; /* Transfer function coefficients "a" (a0 is pre-applied) */ - ALfloat b1, b2; /* Transfer function coefficients "b" (b0 is input_gain) */ - ALfloat input_gain; - - void (*process)(struct ALfilterState *self, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples); } ALfilterState; -#define ALfilterState_process(a, ...) ((a)->process((a), __VA_ARGS__)) +/* 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. @@ -79,26 +77,18 @@ inline void ALfilterState_clear(ALfilterState *filter) void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat rcpQ); -inline ALfloat ALfilterState_processSingle(ALfilterState *filter, ALfloat sample) +inline void ALfilterState_copyParams(ALfilterState *restrict dst, const ALfilterState *restrict src) { - ALfloat outsmp; - - outsmp = filter->input_gain * sample + - 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] = sample; - filter->y[1] = filter->y[0]; - filter->y[0] = outsmp; - - return outsmp; + dst->b0 = src->b0; + dst->b1 = src->b1; + dst->b2 = src->b2; + dst->a1 = src->a1; + dst->a2 = src->a2; } -void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples); +void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples); -inline void ALfilterState_processPassthru(ALfilterState *filter, const ALfloat *src, ALuint numsamples) +inline void ALfilterState_processPassthru(ALfilterState *filter, const ALfloat *restrict src, ALsizei numsamples) { if(numsamples >= 2) { @@ -151,10 +141,19 @@ typedef struct ALfilter { #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*)LookupUIntMapKey(&device->FilterMap, id); } +{ return (struct ALfilter*)LookupUIntMapKeyNoLock(&device->FilterMap, id); } inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id) -{ return (struct ALfilter*)RemoveUIntMapKey(&device->FilterMap, id); } +{ return (struct ALfilter*)RemoveUIntMapKeyNoLock(&device->FilterMap, id); } ALvoid ReleaseALFilters(ALCdevice *device); diff --git a/libs/openal-soft/OpenAL32/Include/alListener.h b/libs/openal-soft/OpenAL32/Include/alListener.h index c9bd9be0..f1a92b0d 100644 --- a/libs/openal-soft/OpenAL32/Include/alListener.h +++ b/libs/openal-soft/OpenAL32/Include/alListener.h @@ -8,20 +8,57 @@ extern "C" { #endif +struct ALlistenerProps { + ALfloat Position[3]; + ALfloat Velocity[3]; + ALfloat Forward[3]; + ALfloat Up[3]; + ALfloat Gain; + ALfloat MetersPerUnit; + + ALfloat DopplerFactor; + ALfloat DopplerVelocity; + ALfloat SpeedOfSound; + ALboolean SourceDistanceModel; + enum DistanceModel DistanceModel; + + ATOMIC(struct ALlistenerProps*) next; +}; + typedef struct ALlistener { - aluVector Position; - aluVector Velocity; - 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; + ALfloat MetersPerUnit; + + /* 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 { - aluMatrixd Matrix; + aluMatrixf Matrix; aluVector Velocity; + + ALfloat Gain; + ALfloat MetersPerUnit; + + ALfloat DopplerFactor; + ALfloat SpeedOfSound; + + ALboolean SourceDistanceModel; + enum DistanceModel DistanceModel; } Params; } ALlistener; +void UpdateListenerProps(ALCcontext *context); + #ifdef __cplusplus } #endif diff --git a/libs/openal-soft/OpenAL32/Include/alMain.h b/libs/openal-soft/OpenAL32/Include/alMain.h index 8f1fd956..b14d6747 100644 --- a/libs/openal-soft/OpenAL32/Include/alMain.h +++ b/libs/openal-soft/OpenAL32/Include/alMain.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -20,6 +21,124 @@ #include "AL/alc.h" #include "AL/alext.h" +#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" + +#ifndef ALC_SOFT_loopback2 +#define ALC_SOFT_loopback2 1 +#define ALC_AMBISONIC_LAYOUT_SOFT 0x1997 +#define ALC_AMBISONIC_SCALING_SOFT 0x1998 +#define ALC_AMBISONIC_ORDER_SOFT 0x1999 + +#define ALC_BFORMAT3D_SOFT 0x1508 + +/* Ambisonic layouts */ +#define ALC_ACN_SOFT 0x1600 +#define ALC_FUMA_SOFT 0x1601 + +/* Ambisonic scalings (normalization) */ +/*#define ALC_FUMA_SOFT*/ +#define ALC_SN3D_SOFT 0x1602 +#define ALC_N3D_SOFT 0x1603 + +typedef ALCboolean (ALC_APIENTRY*LPALCISAMBISONICFORMATSUPPORTEDSOFT)(ALCdevice *device, ALCenum layout, ALCenum scaling, ALsizei order); +#ifdef AL_ALEXT_PROTOTYPES +ALC_API ALCboolean ALC_APIENTRY alcIsAmbisonicFormatSupportedSOFT(ALCdevice *device, ALCenum layout, ALCenum scaling, ALsizei order); +#endif +#endif + +#ifndef ALC_SOFT_device_clock +#define ALC_SOFT_device_clock 1 +typedef int64_t ALCint64SOFT; +typedef uint64_t ALCuint64SOFT; +#define ALC_DEVICE_CLOCK_SOFT 0x1600 +#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); +#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 + #if defined(_WIN64) #define SZFMT "%I64u" @@ -30,24 +149,59 @@ #endif -#include "static_assert.h" -#include "align.h" -#include "atomic.h" -#include "uintmap.h" -#include "vector.h" -#include "alstring.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 -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 __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. + */ +#define SAFE_CONST(T, var) __extension__({ \ + T _tmp = (var); \ + (const T)_tmp; \ +}) +#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)) #endif + + +#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 @@ -81,13 +235,17 @@ typedef ALuint64SOFT ALuint64; #endif #ifdef __GNUC__ -#define DECL_CONST __attribute__((const)) #define DECL_FORMAT(x, y, z) __attribute__((format(x, (y), (z)))) #else -#define DECL_CONST #define DECL_FORMAT(x, y, z) #endif +/* 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)) + #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 @@ -119,7 +277,7 @@ 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 @@ -208,6 +366,12 @@ static void T##_Delete(void *ptr) { al_free(ptr); } { \ 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 @@ -215,6 +379,8 @@ extern "C" { #endif struct Hrtf; +struct HrtfEntry; +struct Compressor; #define DEFAULT_OUTPUT_RATE (44100) @@ -236,6 +402,31 @@ inline ALuint NextPowerOf2(ALuint value) return value+1; } +/** 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); +} + +/* Scales the given value using 64-bit integer math, rounding the result. */ +inline ALuint64 ScaleRound(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale) +{ + return (val*new_scale + old_scale/2) / old_scale; +} + +/* Scales the given value using 64-bit integer math, flooring the result. */ +inline ALuint64 ScaleFloor(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale) +{ + return val * new_scale / old_scale; +} + +/* Scales the given value using 64-bit integer math, ceiling the result. */ +inline ALuint64 ScaleCeil(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale) +{ + return (val*new_scale + old_scale-1) / old_scale; +} + /* Fast float-to-int conversion. Assumes the FPU is already in round-to-zero * mode. */ inline ALint fastf2i(ALfloat f) @@ -252,45 +443,12 @@ inline ALint fastf2i(ALfloat f) #endif } -/* Fast float-to-uint conversion. Assumes the FPU is already in round-to-zero - * mode. */ -inline ALuint fastf2u(ALfloat f) -{ return fastf2i(f); } - enum DevProbe { ALL_DEVICE_PROBE, CAPTURE_DEVICE_PROBE }; -typedef struct { - ALCenum (*OpenPlayback)(ALCdevice*, const ALCchar*); - void (*ClosePlayback)(ALCdevice*); - ALCboolean (*ResetPlayback)(ALCdevice*); - ALCboolean (*StartPlayback)(ALCdevice*); - void (*StopPlayback)(ALCdevice*); - - ALCenum (*OpenCapture)(ALCdevice*, const ALCchar*); - void (*CloseCapture)(ALCdevice*); - void (*StartCapture)(ALCdevice*); - void (*StopCapture)(ALCdevice*); - ALCenum (*CaptureSamples)(ALCdevice*, void*, ALCuint); - ALCuint (*AvailableSamples)(ALCdevice*); -} 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; @@ -317,10 +475,31 @@ enum Channel { SideLeft, SideRight, - BFormatW, - BFormatX, - BFormatY, - BFormatZ, + UpperFrontLeft, + UpperFrontRight, + UpperBackLeft, + UpperBackRight, + LowerFrontLeft, + LowerFrontRight, + LowerBackLeft, + LowerBackRight, + + Aux0, + Aux1, + Aux2, + Aux3, + Aux4, + Aux5, + Aux6, + Aux7, + Aux8, + Aux9, + Aux10, + Aux11, + Aux12, + Aux13, + Aux14, + Aux15, InvalidChannel }; @@ -345,23 +524,37 @@ 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, - DevFmtBFormat3D, - DevFmtChannelsDefault = DevFmtStereo }; -#define MAX_OUTPUT_CHANNELS (8) +#define MAX_OUTPUT_CHANNELS (16) -ALuint BytesFromDevFmt(enum DevFmtType type) DECL_CONST; -ALuint ChannelsFromDevFmt(enum DevFmtChannels chans) DECL_CONST; -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 AmbiLayout { + AmbiLayout_FuMa = ALC_FUMA_SOFT, /* FuMa channel order */ + AmbiLayout_ACN = ALC_ACN_SOFT, /* ACN channel order */ + + 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 */ + + AmbiNorm_Default = AmbiNorm_SN3D +}; + extern const struct EffectList { const char *name; @@ -378,25 +571,57 @@ enum DeviceType { }; -enum HrtfMode { - DisabledHrtf, - BasicHrtf, - FullHrtf +enum RenderMode { + NormalRender, + StereoPair, + HrtfRender }; /* The maximum number of Ambisonics coefficients. For a given order (o), the * size needed will be (o+1)**2, thus zero-order has 1, first-order has 4, - * second-order has 9, and third-order has 16. */ -#define MAX_AMBI_COEFFS 16 + * second-order has 9, third-order has 16, and fourth-order has 25. + */ +#define MAX_AMBI_ORDER 3 +#define MAX_AMBI_COEFFS ((MAX_AMBI_ORDER+1) * (MAX_AMBI_ORDER+1)) + +/* A bitmask of ambisonic channels with height information. If none of these + * channels are used/needed, there's no height (e.g. with most surround sound + * speaker setups). This only specifies up to 4th order, which is the highest + * order a 32-bit mask value can specify (a 64-bit mask could handle up to 7th + * order). This is ACN ordering, with bit 0 being ACN 0, etc. + */ +#define AMBI_PERIPHONIC_MASK (0xfe7ce4) + +/* The maximum number of Ambisonic coefficients for 2D (non-periphonic) + * representation. This is 2 per each order above zero-order, plus 1 for zero- + * order. Or simply, o*2 + 1. + */ +#define MAX_AMBI2D_COEFFS (MAX_AMBI_ORDER*2 + 1) + typedef ALfloat ChannelConfig[MAX_AMBI_COEFFS]; +typedef struct BFChannelConfig { + ALfloat Scale; + ALsizei Index; +} BFChannelConfig; + +typedef union AmbiConfig { + /* Ambisonic coefficients for mixing to the dry buffer. */ + ChannelConfig Coeffs[MAX_OUTPUT_CHANNELS]; + /* Coefficient channel mapping for mixing to the dry buffer. */ + BFChannelConfig Map[MAX_OUTPUT_CHANNELS]; +} AmbiConfig; #define HRTF_HISTORY_BITS (6) #define HRTF_HISTORY_LENGTH (1<Device); } - -inline void UnlockContext(ALCcontext *context) -{ ALCdevice_Unlock(context->Device); } - - -void *al_malloc(size_t alignment, size_t size); -void *al_calloc(size_t alignment, size_t size); -void al_free(void *ptr); - typedef struct { #ifdef HAVE_FENV_H DERIVE_FROM_TYPE(fenv_t); +#ifdef _WIN32 + int round_mode; +#endif #else int state; #endif @@ -607,15 +910,19 @@ typedef struct { } 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) -typedef struct RingBuffer RingBuffer; -RingBuffer *CreateRingBuffer(ALsizei frame_size, ALsizei length); -void DestroyRingBuffer(RingBuffer *ring); -ALsizei RingBufferSize(RingBuffer *ring); -void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len); -void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len); - typedef struct ll_ringbuffer ll_ringbuffer_t; typedef struct ll_ringbuffer_data { char *buf; @@ -651,26 +958,26 @@ void SetRTPriority(void); void SetDefaultChannelOrder(ALCdevice *device); void SetDefaultWFXChannelOrder(ALCdevice *device); -const ALCchar *DevFmtTypeString(enum DevFmtType type) DECL_CONST; -const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans) DECL_CONST; +const ALCchar *DevFmtTypeString(enum DevFmtType type); +const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans); /** * GetChannelIdxByName * - * Returns the device's channel index given a channel name (e.g. FrontCenter), - * or -1 if it doesn't exist. + * Returns the index for the given channel name (e.g. FrontCenter), or -1 if it + * doesn't exist. */ -inline ALint GetChannelIdxByName(const ALCdevice *device, enum Channel chan) +inline ALint GetChannelIndex(const enum Channel names[MAX_OUTPUT_CHANNELS], enum Channel chan) { - ALint i = 0; + ALint i; for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) { - if(device->ChannelName[i] == chan) + if(names[i] == chan) return i; } return -1; } - +#define GetChannelIdxByName(x, c) GetChannelIndex((x).ChannelName, (c)) extern FILE *LogFile; @@ -681,6 +988,13 @@ void al_print(const char *type, const char *func, const char *fmt, ...) DECL_FOR #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, @@ -698,16 +1012,19 @@ extern enum LogLevel LogLevel; #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) @@ -725,15 +1042,42 @@ enum { void FillCPUCaps(ALuint capfilter); -FILE *OpenDataFile(const char *fname, const char *subdir); - 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. */ +/* Small hack to use a pointer-to-array types as a normal argument type. + * Shouldn't be used directly. + */ typedef ALfloat ALfloatBUFFERSIZE[BUFFERSIZE]; +typedef ALfloat ALfloat2[2]; +/* 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. + */ +struct 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); + +ALuint GetCompressorSampleRate(const struct Compressor *Comp); + +void ApplyCompression(struct Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo, + ALfloat (*restrict OutBuffer)[BUFFERSIZE]); + #ifdef __cplusplus } #endif diff --git a/libs/openal-soft/OpenAL32/Include/alSource.h b/libs/openal-soft/OpenAL32/Include/alSource.h index 13596161..03034ce8 100644 --- a/libs/openal-soft/OpenAL32/Include/alSource.h +++ b/libs/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" { @@ -17,95 +20,48 @@ struct ALsource; typedef struct ALbufferlistitem { struct ALbuffer *buffer; - struct ALbufferlistitem *volatile next; - struct ALbufferlistitem *volatile prev; + ATOMIC(struct ALbufferlistitem*) next; } ALbufferlistitem; -typedef struct ALvoice { - struct ALsource *volatile Source; - - /** Method to update mixing parameters. */ - ALvoid (*Update)(struct ALvoice *self, const struct ALsource *source, const ALCcontext *context); - - /** Current target parameters used for mixing. */ - ALint Step; - - ALboolean IsHrtf; - - ALuint Offset; /* Number of output samples mixed since starting. */ - - alignas(16) ALfloat PrevSamples[MAX_INPUT_CHANNELS][MAX_PRE_SAMPLES]; - - BsincState SincState; - - DirectParams Direct; - SendParams Send[MAX_SENDS]; -} ALvoice; - - typedef struct ALsource { /** Source properties. */ - volatile ALfloat Pitch; - volatile ALfloat Gain; - volatile ALfloat OuterGain; - volatile ALfloat MinGain; - volatile ALfloat MaxGain; - volatile ALfloat InnerAngle; - volatile ALfloat OuterAngle; - volatile ALfloat RefDistance; - volatile ALfloat MaxDistance; - volatile ALfloat RollOffFactor; - aluVector Position; - aluVector Velocity; - aluVector Direction; - volatile ALfloat Orientation[2][3]; - volatile ALboolean HeadRelative; - volatile ALboolean Looping; - volatile enum DistanceModel DistanceModel; - volatile ALboolean DirectChannels; + ALfloat Pitch; + ALfloat Gain; + ALfloat OuterGain; + ALfloat MinGain; + ALfloat MaxGain; + ALfloat InnerAngle; + ALfloat OuterAngle; + ALfloat RefDistance; + ALfloat MaxDistance; + 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; - volatile ALboolean DryGainHFAuto; - volatile ALboolean WetGainAuto; - volatile ALboolean WetGainHFAuto; - volatile ALfloat OuterGainHF; + ALboolean DryGainHFAuto; + ALboolean WetGainAuto; + ALboolean WetGainHFAuto; + ALfloat OuterGainHF; - volatile ALfloat AirAbsorptionFactor; - volatile ALfloat RoomRolloffFactor; - volatile ALfloat DopplerFactor; + ALfloat AirAbsorptionFactor; + ALfloat RoomRolloffFactor; + ALfloat DopplerFactor; - volatile ALfloat Radius; - - /** - * Last user-specified offset, and the offset type (bytes, samples, or - * seconds). + /* NOTE: Stereo pan angles are specified in radians, counter-clockwise + * rather than clockwise. */ - ALdouble Offset; - ALenum OffsetType; + ALfloat StereoPan[2]; - /** Source type (static, streaming, or undetermined) */ - volatile ALint SourceType; - - /** Source state (initial, playing, paused, or stopped) */ - volatile ALenum state; - ALenum new_state; - - /** - * Source offset in samples, relative to the currently playing buffer, NOT - * the whole queue, and the fractional (fixed-point) offset to the next - * sample. - */ - ALuint position; - ALuint position_fraction; - - /** Source Buffer Queue info. */ - ATOMIC(ALbufferlistitem*) queue; - ATOMIC(ALbufferlistitem*) current_buffer; - RWLock queue_lock; - - /** Current buffer sample info. */ - ALuint NumChannels; - ALuint SampleSize; + ALfloat Radius; /** Direct filter and auxiliary send info. */ struct { @@ -122,22 +78,46 @@ typedef struct ALsource { ALfloat HFReference; ALfloat GainLF; ALfloat LFReference; - } Send[MAX_SENDS]; + } *Send; - /** Source needs to update its mixing parameters. */ - ATOMIC(ALenum) NeedsUpdate; + /** + * Last user-specified offset, and the offset type (bytes, samples, or + * seconds). + */ + ALdouble Offset; + ALenum OffsetType; + + /** Source type (static, streaming, or undetermined) */ + ALint SourceType; + + /** Source state (initial, playing, paused, or stopped) */ + ATOMIC(ALenum) state; + + /** Source Buffer Queue head. */ + RWLock queue_lock; + ALbufferlistitem *queue; + + ATOMIC_FLAG PropsClean; /** Self ID */ ALuint id; } ALsource; -inline struct ALsource *LookupSource(ALCcontext *context, ALuint id) -{ return (struct ALsource*)LookupUIntMapKey(&context->SourceMap, id); } -inline struct ALsource *RemoveSource(ALCcontext *context, ALuint id) -{ return (struct ALsource*)RemoveUIntMapKey(&context->SourceMap, id); } +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); } -ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state); -ALboolean ApplyOffset(ALsource *Source); +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 ReleaseALSources(ALCcontext *Context); diff --git a/libs/openal-soft/OpenAL32/Include/alu.h b/libs/openal-soft/OpenAL32/Include/alu.h index a309c4ab..794bdec9 100644 --- a/libs/openal-soft/OpenAL32/Include/alu.h +++ b/libs/openal-soft/OpenAL32/Include/alu.h @@ -13,9 +13,11 @@ #include "alMain.h" #include "alBuffer.h" #include "alFilter.h" +#include "alAuxEffectSlot.h" #include "hrtf.h" #include "align.h" +#include "nfcfilter.h" #include "math_defs.h" @@ -33,9 +35,30 @@ extern "C" { #endif struct ALsource; +struct ALbufferlistitem; struct ALvoice; +struct ALeffectslot; +#define DITHER_RNG_SEED 22222 + + +enum SpatializeMode { + SpatializeOff = AL_FALSE, + SpatializeOn = AL_TRUE, + SpatializeAuto = AL_AUTO_SOFT +}; + +enum Resampler { + PointResampler, + LinearResampler, + FIR4Resampler, + BSincResampler, + + ResamplerMax = BSincResampler +}; +extern enum Resampler ResamplerDefault; + /* The number of distinct scale and phase intervals within the filter table. */ #define BSINC_SCALE_BITS 4 #define BSINC_SCALE_COUNT (1<m[row][0] = m0; - matrix->m[row][1] = m1; - matrix->m[row][2] = m2; - matrix->m[row][3] = m3; -} - -inline void aluMatrixdSet(aluMatrixd *matrix, ALdouble m00, ALdouble m01, ALdouble m02, ALdouble m03, - ALdouble m10, ALdouble m11, ALdouble m12, ALdouble m13, - ALdouble m20, ALdouble m21, ALdouble m22, ALdouble m23, - ALdouble m30, ALdouble m31, ALdouble m32, ALdouble m33) -{ - aluMatrixdSetRow(matrix, 0, m00, m01, m02, m03); - aluMatrixdSetRow(matrix, 1, m10, m11, m12, m13); - aluMatrixdSetRow(matrix, 2, m20, m21, m22, m23); - aluMatrixdSetRow(matrix, 3, m30, m31, m32, m33); -} - - enum ActiveFilters { AF_None = 0, AF_LowPass = 1, @@ -130,74 +140,200 @@ enum ActiveFilters { }; -typedef struct MixGains { - ALfloat Current; - ALfloat Step; - ALfloat Target; -} MixGains; +typedef struct MixHrtfParams { + const ALfloat (*Coeffs)[2]; + ALsizei Delay[2]; + ALfloat Gain; + ALfloat GainStep; +} MixHrtfParams; typedef struct DirectParams { - ALfloat (*OutBuffer)[BUFFERSIZE]; - ALuint OutChannels; + ALfilterState LowPass; + ALfilterState HighPass; - /* If not 'moving', gain/coefficients are set directly without fading. */ - ALboolean Moving; - /* Stepping counter for gain/coefficient fading. */ - ALuint Counter; - /* Last direction (relative to listener) and gain of a moving source. */ - aluVector LastDir; - ALfloat LastGain; + NfcFilter NFCtrlFilter[MAX_AMBI_ORDER]; struct { - enum ActiveFilters ActiveType; - ALfilterState LowPass; - ALfilterState HighPass; - } Filters[MAX_INPUT_CHANNELS]; - - struct { - HrtfParams Params; + HrtfParams Old; + HrtfParams Target; HrtfState State; - } Hrtf[MAX_INPUT_CHANNELS]; - MixGains Gains[MAX_INPUT_CHANNELS][MAX_OUTPUT_CHANNELS]; + } Hrtf; + + struct { + ALfloat Current[MAX_OUTPUT_CHANNELS]; + ALfloat Target[MAX_OUTPUT_CHANNELS]; + } Gains; } DirectParams; typedef struct SendParams { - ALfloat (*OutBuffer)[BUFFERSIZE]; - - ALboolean Moving; - ALuint Counter; + ALfilterState LowPass; + ALfilterState HighPass; struct { - enum ActiveFilters ActiveType; - ALfilterState LowPass; - ALfilterState HighPass; - } Filters[MAX_INPUT_CHANNELS]; - - /* Gain control, which applies to each input channel to a single (mono) - * output buffer. */ - MixGains Gains[MAX_INPUT_CHANNELS]; + ALfloat Current[MAX_OUTPUT_CHANNELS]; + ALfloat Target[MAX_OUTPUT_CHANNELS]; + } Gains; } SendParams; -typedef const ALfloat* (*ResamplerFunc)(const BsincState *state, - const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen -); +struct ALvoiceProps { + ATOMIC(struct ALvoiceProps*) next; -typedef void (*MixerFunc)(const ALfloat *data, ALuint OutChans, - ALfloat (*restrict OutBuffer)[BUFFERSIZE], struct MixGains *Gains, - ALuint Counter, ALuint OutPos, ALuint BufferSize); -typedef void (*HrtfMixerFunc)(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data, - ALuint Counter, ALuint Offset, ALuint OutPos, - const ALuint IrSize, const HrtfParams *hrtfparams, - HrtfState *hrtfstate, ALuint BufferSize); + ALfloat Pitch; + ALfloat Gain; + ALfloat OuterGain; + ALfloat MinGain; + ALfloat MaxGain; + ALfloat InnerAngle; + ALfloat OuterAngle; + ALfloat RefDistance; + ALfloat MaxDistance; + ALfloat RolloffFactor; + ALfloat Position[3]; + ALfloat Velocity[3]; + ALfloat Direction[3]; + ALfloat Orientation[2][3]; + ALboolean HeadRelative; + enum DistanceModel DistanceModel; + enum Resampler Resampler; + ALboolean DirectChannels; + enum SpatializeMode SpatializeMode; + ALboolean DryGainHFAuto; + ALboolean WetGainAuto; + ALboolean WetGainHFAuto; + ALfloat OuterGainHF; + + ALfloat AirAbsorptionFactor; + ALfloat RoomRolloffFactor; + ALfloat DopplerFactor; + + ALfloat StereoPan[2]; + + ALfloat Radius; + + /** Direct filter and auxiliary send info. */ + struct { + ALfloat Gain; + ALfloat GainHF; + ALfloat HFReference; + ALfloat GainLF; + ALfloat LFReference; + } Direct; + struct { + struct ALeffectslot *Slot; + ALfloat Gain; + ALfloat GainHF; + ALfloat HFReference; + ALfloat GainLF; + ALfloat LFReference; + } Send[]; +}; + +/* If not 'fading', gain targets are used directly without fading. */ +#define VOICE_IS_FADING (1<<0) +#define VOICE_HAS_HRTF (1<<1) +#define VOICE_HAS_NFC (1<<2) + +typedef struct ALvoice { + struct ALvoiceProps *Props; + + ATOMIC(struct ALvoiceProps*) Update; + ATOMIC(struct ALvoiceProps*) FreeList; + + ATOMIC(struct ALsource*) Source; + ATOMIC(bool) Playing; + + /** + * 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(ALuint) position; + ATOMIC(ALsizei) position_fraction; + + /* Current buffer queue item being played. */ + ATOMIC(struct ALbufferlistitem*) current_buffer; + + /* Buffer queue item to loop to at end of queue (will be NULL for non- + * looping voices). + */ + ATOMIC(struct ALbufferlistitem*) loop_buffer; + + /** + * Number of channels and bytes-per-sample for the attached source's + * buffer(s). + */ + ALsizei NumChannels; + ALsizei SampleSize; + + /** Current target parameters used for mixing. */ + ALint Step; + + ResamplerFunc Resampler; + + ALuint Flags; + + ALuint Offset; /* Number of output samples mixed since starting. */ + + alignas(16) ALfloat PrevSamples[MAX_INPUT_CHANNELS][MAX_PRE_SAMPLES]; + + InterpState ResampleState; + + struct { + enum ActiveFilters FilterType; + DirectParams Params[MAX_INPUT_CHANNELS]; + + ALfloat (*Buffer)[BUFFERSIZE]; + ALsizei Channels; + ALsizei ChannelsPerOrder[MAX_AMBI_ORDER+1]; + } Direct; + + struct { + enum ActiveFilters FilterType; + SendParams Params[MAX_INPUT_CHANNELS]; + + ALfloat (*Buffer)[BUFFERSIZE]; + ALsizei Channels; + } Send[]; +} ALvoice; + +void DeinitVoice(ALvoice *voice); + + +typedef void (*MixerFunc)(const ALfloat *data, ALsizei OutChans, + ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALfloat *CurrentGains, + const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos, + ALsizei BufferSize); +typedef void (*RowMixerFunc)(ALfloat *OutBuffer, const ALfloat *gains, + const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, + ALsizei InPos, ALsizei BufferSize); +typedef void (*HrtfMixerFunc)(ALfloat *restrict LeftOut, ALfloat *restrict RightOut, + const ALfloat *data, ALsizei Offset, ALsizei OutPos, + const ALsizei IrSize, MixHrtfParams *hrtfparams, + HrtfState *hrtfstate, ALsizei BufferSize); +typedef void (*HrtfMixerBlendFunc)(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); +typedef void (*HrtfDirectMixerFunc)(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); + + +#define GAIN_MIX_MAX (16.0f) /* +24dB */ #define GAIN_SILENCE_THRESHOLD (0.00001f) /* -100dB */ #define SPEEDOFSOUNDMETRESPERSEC (343.3f) #define AIRABSORBGAINHF (0.99426f) /* -0.05dB */ +/* Target gain for the reverb decay feedback reaching the decay time. */ +#define REVERB_DECAY_GAIN (0.001f) /* -60 dB */ + #define FRACTIONBITS (12) #define FRACTIONONE (1< 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, ALsizei numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]); +void ComputeAmbientGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]); /** - * ComputeBFormatGains + * ComputePanningGains * - * Sets channel gains for a given (first-order) B-Format channel. The matrix is - * a 1x4 'slice' of the rotation matrix for a given channel used to orient the - * coefficients. + * Computes panning gains using the given channel decoder coefficients and the + * pre-calculated direction or angle coefficients. */ -void ComputeBFormatGains(const ALCdevice *device, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]); +#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, 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]); + +/** + * ComputeFirstOrderGains + * + * Sets channel gains for a first-order ambisonics input channel. The matrix is + * 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, 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]); -ALvoid UpdateContextSources(ALCcontext *context); +ALboolean MixSource(struct ALvoice *voice, struct ALsource *Source, ALCdevice *Device, ALsizei SamplesToDo); -ALvoid CalcSourceParams(struct ALvoice *voice, const struct ALsource *source, const ALCcontext *ALContext); -ALvoid CalcNonAttnSourceParams(struct ALvoice *voice, const struct ALsource *source, const ALCcontext *ALContext); - -ALvoid MixSource(struct ALvoice *voice, struct ALsource *source, ALCdevice *Device, ALuint SamplesToDo); - -ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size); +void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples); /* Caller must lock the device. */ -ALvoid aluHandleDisconnect(ALCdevice *device); +void aluHandleDisconnect(ALCdevice *device); extern ALfloat ConeScale; extern ALfloat ZScale; diff --git a/libs/openal-soft/OpenAL32/Include/bs2b.h b/libs/openal-soft/OpenAL32/Include/bs2b.h index 903c6bc5..e845d906 100644 --- a/libs/openal-soft/OpenAL32/Include/bs2b.h +++ b/libs/openal-soft/OpenAL32/Include/bs2b.h @@ -63,10 +63,10 @@ struct bs2b { * [0] - first channel, [1] - second channel */ struct t_last_sample { - float asis[2]; - float lo[2]; - float hi[2]; - } last_sample; + float asis; + float lo; + float hi; + } last_sample[2]; }; /* Clear buffers and set new coefficients with new crossfeed level and sample @@ -85,38 +85,7 @@ int bs2b_get_srate(struct bs2b *bs2b); /* Clear buffer */ void bs2b_clear(struct bs2b *bs2b); -/* Crossfeeds one stereo sample that are pointed by sample. - * [0] - first channel, [1] - second channel. - * Returns crossfided sample by sample pointer. - */ -inline void bs2b_cross_feed(struct bs2b *bs2b, float *restrict sample) -{ -/* Single pole IIR filter. - * O[n] = a0*I[n] + a1*I[n-1] + b1*O[n-1] - */ - -/* Lowpass filter */ -#define lo_filter(in, out_1) (bs2b->a0_lo*(in) + bs2b->b1_lo*(out_1)) - -/* Highboost filter */ -#define hi_filter(in, in_1, out_1) (bs2b->a0_hi*(in) + bs2b->a1_hi*(in_1) + bs2b->b1_hi*(out_1)) - - /* Lowpass filter */ - bs2b->last_sample.lo[0] = lo_filter(sample[0], bs2b->last_sample.lo[0]); - bs2b->last_sample.lo[1] = lo_filter(sample[1], bs2b->last_sample.lo[1]); - - /* Highboost filter */ - bs2b->last_sample.hi[0] = hi_filter(sample[0], bs2b->last_sample.asis[0], bs2b->last_sample.hi[0]); - bs2b->last_sample.hi[1] = hi_filter(sample[1], bs2b->last_sample.asis[1], bs2b->last_sample.hi[1]); - bs2b->last_sample.asis[0] = sample[0]; - bs2b->last_sample.asis[1] = sample[1]; - - /* Crossfeed */ - sample[0] = bs2b->last_sample.hi[0] + bs2b->last_sample.lo[1]; - sample[1] = bs2b->last_sample.hi[1] + bs2b->last_sample.lo[0]; -#undef hi_filter -#undef lo_filter -} /* bs2b_cross_feed */ +void bs2b_cross_feed(struct bs2b *bs2b, float *restrict Left, float *restrict Right, int SamplesToDo); #ifdef __cplusplus } /* extern "C" */ diff --git a/libs/openal-soft/OpenAL32/alAuxEffectSlot.c b/libs/openal-soft/OpenAL32/alAuxEffectSlot.c index c1314301..02288a31 100644 --- a/libs/openal-soft/OpenAL32/alAuxEffectSlot.c +++ b/libs/openal-soft/OpenAL32/alAuxEffectSlot.c @@ -29,16 +29,19 @@ #include "alAuxEffectSlot.h" #include "alThunk.h" #include "alError.h" +#include "alListener.h" #include "alSource.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); -static ALenum AddEffectSlotArray(ALCcontext *Context, ALeffectslot **start, ALsizei count); -static void RemoveEffectSlotArray(ALCcontext *Context, const ALeffectslot *slot); - - static UIntMap EffectStateFactoryMap; static inline ALeffectStateFactory *getFactoryByType(ALenum type) { @@ -48,24 +51,32 @@ static inline ALeffectStateFactory *getFactoryByType(ALenum type) return NULL; } +static void ALeffectState_IncRef(ALeffectState *state); +static void ALeffectState_DecRef(ALeffectState *state); + +#define DO_UPDATEPROPS() do { \ + if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) \ + UpdateEffectSlotProps(slot); \ + else \ + ATOMIC_FLAG_CLEAR(&slot->PropsClean, almemory_order_release); \ +} while(0) + AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots) { ALCcontext *context; - VECTOR(ALeffectslot*) slotvec; + ALeffectslot **tmpslots = NULL; ALsizei cur; ALenum err; context = GetContextRef(); if(!context) return; - VECTOR_INIT(slotvec); - if(!(n >= 0)) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - if(!VECTOR_RESERVE(slotvec, n)) - SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done); + tmpslots = al_malloc(DEF_ALIGN, sizeof(ALeffectslot*)*n); + LockEffectSlotsWrite(context); for(cur = 0;cur < n;cur++) { ALeffectslot *slot = al_calloc(16, sizeof(ALeffectslot)); @@ -73,37 +84,57 @@ AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslo if(!slot || (err=InitEffectSlot(slot)) != AL_NO_ERROR) { al_free(slot); + UnlockEffectSlotsWrite(context); + 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); + err = InsertUIntMapEntryNoLock(&context->EffectSlotMap, slot->id, slot); if(err != AL_NO_ERROR) { FreeThunkEntry(slot->id); - DELETE_OBJ(slot->EffectState); + ALeffectState_DecRef(slot->Effect.State); + if(slot->Params.EffectState) + ALeffectState_DecRef(slot->Params.EffectState); al_free(slot); + UnlockEffectSlotsWrite(context); alDeleteAuxiliaryEffectSlots(cur, effectslots); SET_ERROR_AND_GOTO(context, err, done); } - VECTOR_PUSH_BACK(slotvec, slot); + aluInitEffectPanning(slot); + tmpslots[cur] = slot; effectslots[cur] = slot->id; } - err = AddEffectSlotArray(context, VECTOR_ITER_BEGIN(slotvec), n); - if(err != AL_NO_ERROR) + if(n > 0) { - alDeleteAuxiliaryEffectSlots(cur, effectslots); - SET_ERROR_AND_GOTO(context, err, done); + struct ALeffectslotArray *curarray = ATOMIC_LOAD(&context->ActiveAuxSlots, almemory_order_acquire); + struct ALeffectslotArray *newarray = NULL; + ALsizei newcount = curarray->count + n; + ALCdevice *device; + + newarray = al_calloc(DEF_ALIGN, FAM_SIZE(struct ALeffectslotArray, slot, newcount)); + newarray->count = newcount; + memcpy(newarray->slot, tmpslots, sizeof(ALeffectslot*)*n); + if(curarray) + memcpy(newarray->slot+n, curarray->slot, sizeof(ALeffectslot*)*curarray->count); + + newarray = ATOMIC_EXCHANGE_PTR(&context->ActiveAuxSlots, newarray, + almemory_order_acq_rel); + device = context->Device; + while((ATOMIC_LOAD(&device->MixCount, almemory_order_acquire)&1)) + althrd_yield(); + al_free(newarray); } + UnlockEffectSlotsWrite(context); done: - VECTOR_DEINIT(slotvec); - + al_free(tmpslots); ALCcontext_DecRef(context); } @@ -116,6 +147,7 @@ AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint * context = GetContextRef(); if(!context) return; + LockEffectSlotsWrite(context); if(!(n >= 0)) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); for(i = 0;i < n;i++) @@ -127,20 +159,51 @@ AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint * } // All effectslots are valid + if(n > 0) + { + struct ALeffectslotArray *curarray = ATOMIC_LOAD(&context->ActiveAuxSlots, almemory_order_acquire); + struct ALeffectslotArray *newarray = NULL; + ALsizei newcount = curarray->count - n; + ALCdevice *device; + ALsizei j, k; + + assert(newcount >= 0); + newarray = al_calloc(DEF_ALIGN, FAM_SIZE(struct ALeffectslotArray, slot, newcount)); + newarray->count = newcount; + for(i = j = 0;i < newarray->count;) + { + slot = curarray->slot[j++]; + for(k = 0;k < n;k++) + { + if(slot->id == effectslots[k]) + break; + } + if(k == n) + newarray->slot[i++] = slot; + } + + newarray = ATOMIC_EXCHANGE_PTR(&context->ActiveAuxSlots, newarray, + almemory_order_acq_rel); + device = context->Device; + while((ATOMIC_LOAD(&device->MixCount, almemory_order_acquire)&1)) + althrd_yield(); + al_free(newarray); + } + for(i = 0;i < n;i++) { if((slot=RemoveEffectSlot(context, effectslots[i])) == NULL) continue; FreeThunkEntry(slot->id); - RemoveEffectSlotArray(context, slot); - DELETE_OBJ(slot->EffectState); + DeinitEffectSlot(slot); memset(slot, 0, sizeof(*slot)); al_free(slot); } done: + UnlockEffectSlotsWrite(context); ALCcontext_DecRef(context); } @@ -152,7 +215,9 @@ AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot) context = GetContextRef(); if(!context) return AL_FALSE; + LockEffectSlotsRead(context); ret = (LookupEffectSlot(context, effectslot) ? AL_TRUE : AL_FALSE); + UnlockEffectSlotsRead(context); ALCcontext_DecRef(context); @@ -170,35 +235,43 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param context = GetContextRef(); if(!context) return; - device = context->Device; + WriteLock(&context->PropLock); + LockEffectSlotsRead(context); if((slot=LookupEffectSlot(context, effectslot)) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); switch(param) { case AL_EFFECTSLOT_EFFECT: + device = context->Device; + + LockEffectsRead(device); effect = (value ? LookupEffect(device, value) : NULL); if(!(value == 0 || effect != NULL)) + { + UnlockEffectsRead(device); SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - + } err = InitializeEffect(device, slot, effect); + UnlockEffectsRead(device); + if(err != AL_NO_ERROR) SET_ERROR_AND_GOTO(context, err, done); - ATOMIC_STORE(&context->UpdateSources, AL_TRUE); break; case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO: if(!(value == AL_TRUE || value == AL_FALSE)) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - slot->AuxSendAuto = value; - ATOMIC_STORE(&context->UpdateSources, AL_TRUE); break; default: SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); } + DO_UPDATEPROPS(); done: + UnlockEffectSlotsRead(context); + WriteUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -217,6 +290,7 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum para context = GetContextRef(); if(!context) return; + LockEffectSlotsRead(context); if(LookupEffectSlot(context, effectslot) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); switch(param) @@ -226,6 +300,7 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum para } done: + UnlockEffectSlotsRead(context); ALCcontext_DecRef(context); } @@ -237,6 +312,8 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param context = GetContextRef(); if(!context) return; + WriteLock(&context->PropLock); + LockEffectSlotsRead(context); if((slot=LookupEffectSlot(context, effectslot)) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); switch(param) @@ -244,16 +321,17 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param case AL_EFFECTSLOT_GAIN: if(!(value >= 0.0f && value <= 1.0f)) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - slot->Gain = value; - ATOMIC_STORE(&slot->NeedsUpdate, AL_TRUE); break; default: SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); } + DO_UPDATEPROPS(); done: + UnlockEffectSlotsRead(context); + WriteUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -271,6 +349,7 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum para context = GetContextRef(); if(!context) return; + LockEffectSlotsRead(context); if(LookupEffectSlot(context, effectslot) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); switch(param) @@ -280,6 +359,7 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum para } done: + UnlockEffectSlotsRead(context); ALCcontext_DecRef(context); } @@ -291,6 +371,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum pa context = GetContextRef(); if(!context) return; + LockEffectSlotsRead(context); if((slot=LookupEffectSlot(context, effectslot)) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); switch(param) @@ -304,6 +385,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum pa } done: + UnlockEffectSlotsRead(context); ALCcontext_DecRef(context); } @@ -322,6 +404,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum p context = GetContextRef(); if(!context) return; + LockEffectSlotsRead(context); if(LookupEffectSlot(context, effectslot) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); switch(param) @@ -331,6 +414,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum p } done: + UnlockEffectSlotsRead(context); ALCcontext_DecRef(context); } @@ -342,6 +426,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum pa context = GetContextRef(); if(!context) return; + LockEffectSlotsRead(context); if((slot=LookupEffectSlot(context, effectslot)) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); switch(param) @@ -355,6 +440,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum pa } done: + UnlockEffectSlotsRead(context); ALCcontext_DecRef(context); } @@ -372,6 +458,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum p context = GetContextRef(); if(!context) return; + LockEffectSlotsRead(context); if(LookupEffectSlot(context, effectslot) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); switch(param) @@ -381,47 +468,18 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum p } done: + UnlockEffectSlotsRead(context); ALCcontext_DecRef(context); } -static ALenum AddEffectSlotArray(ALCcontext *context, ALeffectslot **start, ALsizei count) -{ - ALenum err = AL_NO_ERROR; - - LockContext(context); - if(!VECTOR_INSERT(context->ActiveAuxSlots, VECTOR_ITER_END(context->ActiveAuxSlots), start, start+count)) - err = AL_OUT_OF_MEMORY; - UnlockContext(context); - - return err; -} - -static void RemoveEffectSlotArray(ALCcontext *context, const ALeffectslot *slot) -{ - ALeffectslot **iter; - - LockContext(context); -#define MATCH_SLOT(_i) (slot == *(_i)) - VECTOR_FIND_IF(iter, ALeffectslot*, context->ActiveAuxSlots, MATCH_SLOT); - if(iter != VECTOR_ITER_END(context->ActiveAuxSlots)) - { - *iter = VECTOR_BACK(context->ActiveAuxSlots); - VECTOR_POP_BACK(context->ActiveAuxSlots); - } -#undef MATCH_SLOT - UnlockContext(context); -} - - void InitEffectFactoryMap(void) { - InitUIntMap(&EffectStateFactoryMap, ~0); + InitUIntMap(&EffectStateFactoryMap, INT_MAX); InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_NULL, ALnullStateFactory_getFactory); InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EAXREVERB, ALreverbStateFactory_getFactory); InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_REVERB, ALreverbStateFactory_getFactory); - InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_AUTOWAH, ALautowahStateFactory_getFactory); InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_CHORUS, ALchorusStateFactory_getFactory); InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_COMPRESSOR, ALcompressorStateFactory_getFactory); InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DISTORTION, ALdistortionStateFactory_getFactory); @@ -442,12 +500,12 @@ void DeinitEffectFactoryMap(void) ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect) { ALenum newtype = (effect ? effect->type : AL_EFFECT_NULL); - ALeffectStateFactory *factory; + struct ALeffectslotProps *props; + ALeffectState *State; - if(newtype != EffectSlot->EffectType) + if(newtype != EffectSlot->Effect.Type) { - ALeffectState *State; - FPUCtl oldMode; + ALeffectStateFactory *factory; factory = getFactoryByType(newtype); if(!factory) @@ -456,92 +514,211 @@ ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *e return AL_INVALID_ENUM; } State = V0(factory,create)(); - if(!State) - return AL_OUT_OF_MEMORY; + if(!State) return AL_OUT_OF_MEMORY; - SetMixerFPUMode(&oldMode); - - ALCdevice_Lock(Device); + START_MIXER_MODE(); + almtx_lock(&Device->BackendLock); + State->OutBuffer = Device->Dry.Buffer; + State->OutChannels = Device->Dry.NumChannels; if(V(State,deviceUpdate)(Device) == AL_FALSE) { - ALCdevice_Unlock(Device); - RestoreFPUMode(&oldMode); - DELETE_OBJ(State); + almtx_unlock(&Device->BackendLock); + LEAVE_MIXER_MODE(); + ALeffectState_DecRef(State); return AL_OUT_OF_MEMORY; } + almtx_unlock(&Device->BackendLock); + END_MIXER_MODE(); - State = ExchangePtr((XchgPtr*)&EffectSlot->EffectState, State); if(!effect) { - memset(&EffectSlot->EffectProps, 0, sizeof(EffectSlot->EffectProps)); - EffectSlot->EffectType = AL_EFFECT_NULL; + EffectSlot->Effect.Type = AL_EFFECT_NULL; + memset(&EffectSlot->Effect.Props, 0, sizeof(EffectSlot->Effect.Props)); } else { - memcpy(&EffectSlot->EffectProps, &effect->Props, sizeof(effect->Props)); - EffectSlot->EffectType = effect->type; + EffectSlot->Effect.Type = effect->type; + EffectSlot->Effect.Props = effect->Props; } - /* FIXME: This should be done asynchronously, but since the EffectState - * object was changed, it needs an update before its Process method can - * be called. */ - ATOMIC_STORE(&EffectSlot->NeedsUpdate, AL_FALSE); - V(EffectSlot->EffectState,update)(Device, EffectSlot); - ALCdevice_Unlock(Device); - - RestoreFPUMode(&oldMode); - - DELETE_OBJ(State); - State = NULL; + ALeffectState_DecRef(EffectSlot->Effect.State); + EffectSlot->Effect.State = State; } - else + else if(effect) + EffectSlot->Effect.Props = effect->Props; + + /* Remove state references from old effect slot property updates. */ + props = ATOMIC_LOAD_SEQ(&EffectSlot->FreeList); + while(props) { - if(effect) - { - ALCdevice_Lock(Device); - memcpy(&EffectSlot->EffectProps, &effect->Props, sizeof(effect->Props)); - ALCdevice_Unlock(Device); - ATOMIC_STORE(&EffectSlot->NeedsUpdate, AL_TRUE); - } + if(props->State) + ALeffectState_DecRef(props->State); + props->State = NULL; + props = ATOMIC_LOAD(&props->next, almemory_order_relaxed); } return AL_NO_ERROR; } +static void ALeffectState_IncRef(ALeffectState *state) +{ + uint ref; + ref = IncrementRef(&state->Ref); + TRACEREF("%p increasing refcount to %u\n", state, ref); +} + +static void ALeffectState_DecRef(ALeffectState *state) +{ + uint ref; + ref = DecrementRef(&state->Ref); + TRACEREF("%p decreasing refcount to %u\n", state, ref); + if(ref == 0) DELETE_OBJ(state); +} + + +void ALeffectState_Construct(ALeffectState *state) +{ + InitRef(&state->Ref, 1); + + state->OutBuffer = NULL; + state->OutChannels = 0; +} + +void ALeffectState_Destruct(ALeffectState *UNUSED(state)) +{ +} + + ALenum InitEffectSlot(ALeffectslot *slot) { ALeffectStateFactory *factory; - ALuint i, c; - slot->EffectType = AL_EFFECT_NULL; + slot->Effect.Type = AL_EFFECT_NULL; factory = getFactoryByType(AL_EFFECT_NULL); - if(!(slot->EffectState=V0(factory,create)())) + if(!(slot->Effect.State=V0(factory,create)())) return AL_OUT_OF_MEMORY; slot->Gain = 1.0; slot->AuxSendAuto = AL_TRUE; - ATOMIC_INIT(&slot->NeedsUpdate, AL_FALSE); - for(c = 0;c < 1;c++) - { - for(i = 0;i < BUFFERSIZE;i++) - slot->WetBuffer[c][i] = 0.0f; - } + 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; + ALeffectState_IncRef(slot->Effect.State); + slot->Params.EffectState = slot->Effect.State; + slot->Params.RoomRolloff = 0.0f; + slot->Params.DecayTime = 0.0f; + slot->Params.DecayHFRatio = 0.0f; + slot->Params.DecayHFLimit = AL_FALSE; + slot->Params.AirAbsorptionGainHF = 1.0f; + return AL_NO_ERROR; } +void DeinitEffectSlot(ALeffectslot *slot) +{ + struct ALeffectslotProps *props; + size_t count = 0; + + props = ATOMIC_LOAD_SEQ(&slot->Update); + if(props) + { + 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 = ATOMIC_LOAD(&props->next, almemory_order_relaxed); + if(props->State) ALeffectState_DecRef(props->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) +{ + 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); + if(!props) + props = al_calloc(16, sizeof(*props)); + else + { + struct ALeffectslotProps *next; + do { + next = ATOMIC_LOAD(&props->next, almemory_order_relaxed); + } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&slot->FreeList, &props, next, + almemory_order_seq_cst, almemory_order_acquire) == 0); + } + + /* Copy in current property values. */ + props->Gain = slot->Gain; + props->AuxSendAuto = slot->AuxSendAuto; + + 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 = props->State; + props->State = slot->Effect.State; + + /* Set the new container for updating internal parameters. */ + 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. + */ + ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &slot->FreeList, props); + } + + if(oldstate) + ALeffectState_DecRef(oldstate); +} + +void UpdateAllEffectSlotProps(ALCcontext *context) +{ + struct ALeffectslotArray *auxslots; + ALsizei i; + + LockEffectSlotsRead(context); + auxslots = ATOMIC_LOAD(&context->ActiveAuxSlots, almemory_order_acquire); + for(i = 0;i < auxslots->count;i++) + { + ALeffectslot *slot = auxslots->slot[i]; + if(!ATOMIC_FLAG_TEST_AND_SET(&slot->PropsClean, almemory_order_acq_rel)) + UpdateEffectSlotProps(slot); + } + UnlockEffectSlotsRead(context); +} + ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context) { ALsizei pos; for(pos = 0;pos < Context->EffectSlotMap.size;pos++) { - ALeffectslot *temp = Context->EffectSlotMap.array[pos].value; - Context->EffectSlotMap.array[pos].value = NULL; + ALeffectslot *temp = Context->EffectSlotMap.values[pos]; + Context->EffectSlotMap.values[pos] = NULL; - DELETE_OBJ(temp->EffectState); + DeinitEffectSlot(temp); FreeThunkEntry(temp->id); memset(temp, 0, sizeof(ALeffectslot)); diff --git a/libs/openal-soft/OpenAL32/alBuffer.c b/libs/openal-soft/OpenAL32/alBuffer.c index 904fd61d..376741bd 100644 --- a/libs/openal-soft/OpenAL32/alBuffer.c +++ b/libs/openal-soft/OpenAL32/alBuffer.c @@ -36,15 +36,19 @@ #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 ALsizei FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type); +extern inline ALsizei FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type); -static ALboolean IsValidType(ALenum type) DECL_CONST; -static ALboolean IsValidChannels(ALenum channels) DECL_CONST; -static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans, enum UserFmtType *type) DECL_CONST; -static ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum FmtType *type) DECL_CONST; +static ALboolean IsValidType(ALenum type); +static ALboolean IsValidChannels(ALenum channels); +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); @@ -85,10 +89,12 @@ AL_API ALvoid AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers) context = GetContextRef(); if(!context) return; + device = context->Device; + + LockBuffersWrite(device); if(!(n >= 0)) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - device = context->Device; for(i = 0;i < n;i++) { if(!buffers[i]) @@ -108,6 +114,7 @@ AL_API ALvoid AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers) } done: + UnlockBuffersWrite(device); ALCcontext_DecRef(context); } @@ -119,8 +126,10 @@ AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer) context = GetContextRef(); if(!context) return AL_FALSE; + LockBuffersRead(context->Device); ret = ((!buffer || LookupBuffer(context->Device, buffer)) ? AL_TRUE : AL_FALSE); + UnlockBuffersRead(context->Device); ALCcontext_DecRef(context); @@ -130,13 +139,13 @@ 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) { - enum UserFmtChannels srcchannels; - enum UserFmtType srctype; + enum UserFmtChannels srcchannels = UserFmtMono; + enum UserFmtType srctype = UserFmtByte; ALCdevice *device; ALCcontext *context; ALbuffer *albuf; ALenum newformat = AL_NONE; - ALuint framesize; + ALsizei framesize; ALsizei align; ALenum err; @@ -144,6 +153,7 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi 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)) @@ -151,7 +161,7 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi if(DecomposeUserFormat(format, &srcchannels, &srctype) == AL_FALSE) SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); - align = ATOMIC_LOAD(&albuf->UnpackAlign); + align = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign); if(SanitizeAlignment(srctype, &align) == AL_FALSE) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); switch(srctype) @@ -173,8 +183,6 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi case UserFmtInt: case UserFmtUInt: - case UserFmtByte3: - case UserFmtUByte3: case UserFmtDouble: framesize = FrameSizeFromUserFmt(srcchannels, srctype) * align; if((size%framesize) != 0) @@ -272,25 +280,27 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi } 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; - enum UserFmtType srctype; + enum UserFmtChannels srcchannels = UserFmtMono; + enum UserFmtType srctype = UserFmtByte; ALCdevice *device; ALCcontext *context; ALbuffer *albuf; - ALuint byte_align; - ALuint channels; - ALuint bytes; + ALsizei byte_align; + ALsizei channels; + ALsizei 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)) @@ -299,7 +309,7 @@ AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, cons SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); WriteLock(&albuf->lock); - align = ATOMIC_LOAD(&albuf->UnpackAlign); + align = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign); if(SanitizeAlignment(srctype, &align) == AL_FALSE) { WriteUnlock(&albuf->lock); @@ -351,6 +361,7 @@ AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, cons WriteUnlock(&albuf->lock); done: + UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -369,6 +380,7 @@ AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, 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)) @@ -376,7 +388,7 @@ AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, if(IsValidType(type) == AL_FALSE || IsValidChannels(channels) == AL_FALSE) SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); - align = ATOMIC_LOAD(&albuf->UnpackAlign); + align = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign); if(SanitizeAlignment(type, &align) == AL_FALSE) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); if((samples%align) != 0) @@ -388,6 +400,7 @@ AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, SET_ERROR_AND_GOTO(context, err, done); done: + UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -404,6 +417,7 @@ AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, 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)) @@ -412,7 +426,7 @@ AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); WriteLock(&albuf->lock); - align = ATOMIC_LOAD(&albuf->UnpackAlign); + align = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign); if(SanitizeAlignment(type, &align) == AL_FALSE) { WriteUnlock(&albuf->lock); @@ -441,6 +455,7 @@ AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, WriteUnlock(&albuf->lock); done: + UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -457,6 +472,7 @@ AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, 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)) @@ -465,7 +481,7 @@ AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); ReadLock(&albuf->lock); - align = ATOMIC_LOAD(&albuf->PackAlign); + align = ATOMIC_LOAD_SEQ(&albuf->PackAlign); if(SanitizeAlignment(type, &align) == AL_FALSE) { ReadUnlock(&albuf->lock); @@ -494,6 +510,7 @@ AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ReadUnlock(&albuf->lock); done: + UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -524,6 +541,7 @@ 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); @@ -534,6 +552,7 @@ AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat UNUSED(va } done: + UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -547,6 +566,7 @@ 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); @@ -557,6 +577,7 @@ AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat UNUSED(v } done: + UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -570,6 +591,7 @@ 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); @@ -582,6 +604,7 @@ AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *v } done: + UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -596,6 +619,7 @@ 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); @@ -604,13 +628,13 @@ AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value) case AL_UNPACK_BLOCK_ALIGNMENT_SOFT: if(!(value >= 0)) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - ATOMIC_STORE(&albuf->UnpackAlign, value); + 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); + ATOMIC_STORE_SEQ(&albuf->PackAlign, value); break; default: @@ -618,6 +642,7 @@ AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value) } done: + UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -666,6 +691,7 @@ 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); @@ -697,6 +723,7 @@ AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *val } done: + UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -711,6 +738,7 @@ 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); @@ -732,6 +760,7 @@ AL_API ALvoid AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *val } done: + UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -745,6 +774,7 @@ 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); @@ -757,6 +787,7 @@ AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *valu } done: + UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -777,6 +808,7 @@ 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); @@ -789,6 +821,7 @@ AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *valu } done: + UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -803,6 +836,7 @@ 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); @@ -842,11 +876,11 @@ AL_API ALvoid AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value 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: @@ -854,6 +888,7 @@ AL_API ALvoid AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value } done: + UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -867,6 +902,7 @@ 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); @@ -879,6 +915,7 @@ AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1 } done: + UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -908,6 +945,7 @@ 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); @@ -927,6 +965,7 @@ AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values } done: + UnlockBuffersRead(device); ALCcontext_DecRef(context); } @@ -940,14 +979,14 @@ done: */ ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, ALboolean storesrc) { + enum FmtChannels DstChannels = FmtMono; + enum FmtType DstType = FmtByte; ALuint NewChannels, NewBytes; - enum FmtChannels DstChannels; - enum FmtType DstType; ALuint64 newsize; - ALvoid *temp; - if(DecomposeFormat(NewFormat, &DstChannels, &DstType) == AL_FALSE || - (long)SrcChannels != (long)DstChannels) + if(DecomposeFormat(NewFormat, &DstChannels, &DstType) == AL_FALSE) + return AL_INVALID_ENUM; + if((long)SrcChannels != (long)DstChannels) return AL_INVALID_ENUM; NewChannels = ChannelsFromFmt(DstChannels); @@ -966,13 +1005,25 @@ ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames, return AL_INVALID_OPERATION; } - temp = realloc(ALBuf->data, (size_t)newsize); - if(!temp && newsize) + /* 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 + * buffer's AL_SIZE would not be very reliable for accounting buffer memory + * 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(newsize != ALBuf->BytesAlloc) { - WriteUnlock(&ALBuf->lock); - return AL_OUT_OF_MEMORY; + void *temp = al_calloc(16, (size_t)newsize); + if(!temp && newsize) + { + WriteUnlock(&ALBuf->lock); + return AL_OUT_OF_MEMORY; + } + al_free(ALBuf->data); + ALBuf->data = temp; + ALBuf->BytesAlloc = (ALuint)newsize; } - ALBuf->data = temp; if(data != NULL) ConvertData(ALBuf->data, (enum UserFmtType)DstType, data, SrcType, NewChannels, frames, align); @@ -1021,7 +1072,7 @@ ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames, } -ALuint BytesFromUserFmt(enum UserFmtType type) +ALsizei BytesFromUserFmt(enum UserFmtType type) { switch(type) { @@ -1033,8 +1084,6 @@ ALuint BytesFromUserFmt(enum UserFmtType type) 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 */ @@ -1042,7 +1091,7 @@ ALuint BytesFromUserFmt(enum UserFmtType type) } return 0; } -ALuint ChannelsFromUserFmt(enum UserFmtChannels chans) +ALsizei ChannelsFromUserFmt(enum UserFmtChannels chans) { switch(chans) { @@ -1137,7 +1186,7 @@ static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans, return AL_FALSE; } -ALuint BytesFromFmt(enum FmtType type) +ALsizei BytesFromFmt(enum FmtType type) { switch(type) { @@ -1147,7 +1196,7 @@ ALuint BytesFromFmt(enum FmtType type) } return 0; } -ALuint ChannelsFromFmt(enum FmtChannels chans) +ALsizei ChannelsFromFmt(enum FmtChannels chans) { switch(chans) { @@ -1201,13 +1250,13 @@ static ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum Fm { AL_7POINT1_16_SOFT, FmtX71, FmtShort }, { AL_7POINT1_32F_SOFT, FmtX71, FmtFloat }, - { AL_FORMAT_BFORMAT2D_8, FmtBFormat2D, FmtByte }, - { AL_FORMAT_BFORMAT2D_16, FmtBFormat2D, FmtShort }, - { AL_FORMAT_BFORMAT2D_FLOAT32, FmtBFormat2D, FmtFloat }, + { AL_BFORMAT2D_8_SOFT, FmtBFormat2D, FmtByte }, + { AL_BFORMAT2D_16_SOFT, FmtBFormat2D, FmtShort }, + { AL_BFORMAT2D_32F_SOFT, FmtBFormat2D, FmtFloat }, - { AL_FORMAT_BFORMAT3D_8, FmtBFormat3D, FmtByte }, - { AL_FORMAT_BFORMAT3D_16, FmtBFormat3D, FmtShort }, - { AL_FORMAT_BFORMAT3D_FLOAT32, FmtBFormat3D, FmtFloat }, + { AL_BFORMAT3D_8_SOFT, FmtBFormat3D, FmtByte }, + { AL_BFORMAT3D_16_SOFT, FmtBFormat3D, FmtShort }, + { AL_BFORMAT3D_32F_SOFT, FmtBFormat3D, FmtFloat }, }; ALuint i; @@ -1275,8 +1324,7 @@ static ALboolean IsValidType(ALenum type) 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; @@ -1293,6 +1341,8 @@ static ALboolean IsValidChannels(ALenum channels) 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; @@ -1305,7 +1355,7 @@ ALbuffer *NewBuffer(ALCcontext *context) ALbuffer *buffer; ALenum err; - buffer = calloc(1, sizeof(ALbuffer)); + buffer = al_calloc(16, sizeof(ALbuffer)); if(!buffer) SET_ERROR_AND_RETURN_VALUE(context, AL_OUT_OF_MEMORY, NULL); RWLockInit(&buffer->lock); @@ -1317,7 +1367,7 @@ ALbuffer *NewBuffer(ALCcontext *context) { FreeThunkEntry(buffer->id); memset(buffer, 0, sizeof(ALbuffer)); - free(buffer); + al_free(buffer); SET_ERROR_AND_RETURN_VALUE(context, err, NULL); } @@ -1330,10 +1380,10 @@ void DeleteBuffer(ALCdevice *device, ALbuffer *buffer) RemoveBuffer(device, buffer->id); FreeThunkEntry(buffer->id); - free(buffer->data); + al_free(buffer->data); memset(buffer, 0, sizeof(*buffer)); - free(buffer); + al_free(buffer); } @@ -1347,13 +1397,13 @@ ALvoid ReleaseALBuffers(ALCdevice *device) ALsizei i; for(i = 0;i < device->BufferMap.size;i++) { - ALbuffer *temp = device->BufferMap.array[i].value; - device->BufferMap.array[i].value = NULL; + ALbuffer *temp = device->BufferMap.values[i]; + device->BufferMap.values[i] = NULL; - free(temp->data); + al_free(temp->data); FreeThunkEntry(temp->id); memset(temp, 0, sizeof(ALbuffer)); - free(temp); + al_free(temp); } } diff --git a/libs/openal-soft/OpenAL32/alEffect.c b/libs/openal-soft/OpenAL32/alEffect.c index 0bfe11b9..6761e661 100644 --- a/libs/openal-soft/OpenAL32/alEffect.c +++ b/libs/openal-soft/OpenAL32/alEffect.c @@ -34,6 +34,10 @@ 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 ALboolean IsReverbEffect(ALenum type); @@ -56,11 +60,11 @@ AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects) device = context->Device; for(cur = 0;cur < n;cur++) { - ALeffect *effect = calloc(1, sizeof(ALeffect)); + ALeffect *effect = al_calloc(16, sizeof(ALeffect)); ALenum err = AL_OUT_OF_MEMORY; if(!effect || (err=InitEffect(effect)) != AL_NO_ERROR) { - free(effect); + al_free(effect); alDeleteEffects(cur, effects); SET_ERROR_AND_GOTO(context, err, done); } @@ -72,7 +76,7 @@ AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects) { FreeThunkEntry(effect->id); memset(effect, 0, sizeof(ALeffect)); - free(effect); + al_free(effect); alDeleteEffects(cur, effects); SET_ERROR_AND_GOTO(context, err, done); @@ -95,10 +99,10 @@ AL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects) context = GetContextRef(); if(!context) return; + device = context->Device; + LockEffectsWrite(device); if(!(n >= 0)) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - - device = context->Device; for(i = 0;i < n;i++) { if(effects[i] && LookupEffect(device, effects[i]) == NULL) @@ -111,10 +115,11 @@ AL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects) FreeThunkEntry(effect->id); memset(effect, 0, sizeof(*effect)); - free(effect); + al_free(effect); } done: + UnlockEffectsWrite(device); ALCcontext_DecRef(context); } @@ -126,8 +131,10 @@ AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect) Context = GetContextRef(); if(!Context) return AL_FALSE; + LockEffectsRead(Context->Device); result = ((!effect || LookupEffect(Context->Device, effect)) ? AL_TRUE : AL_FALSE); + UnlockEffectsRead(Context->Device); ALCcontext_DecRef(Context); @@ -144,6 +151,7 @@ AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value) if(!Context) return; Device = Context->Device; + LockEffectsWrite(Device); if((ALEffect=LookupEffect(Device, effect)) == NULL) alSetError(Context, AL_INVALID_NAME); else @@ -170,6 +178,7 @@ AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value) V(ALEffect,setParami)(Context, param, value); } } + UnlockEffectsWrite(Device); ALCcontext_DecRef(Context); } @@ -191,6 +200,7 @@ AL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *v if(!Context) return; Device = Context->Device; + LockEffectsWrite(Device); if((ALEffect=LookupEffect(Device, effect)) == NULL) alSetError(Context, AL_INVALID_NAME); else @@ -198,6 +208,7 @@ AL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *v /* Call the appropriate handler */ V(ALEffect,setParamiv)(Context, param, values); } + UnlockEffectsWrite(Device); ALCcontext_DecRef(Context); } @@ -212,6 +223,7 @@ AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat value) if(!Context) return; Device = Context->Device; + LockEffectsWrite(Device); if((ALEffect=LookupEffect(Device, effect)) == NULL) alSetError(Context, AL_INVALID_NAME); else @@ -219,6 +231,7 @@ AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat value) /* Call the appropriate handler */ V(ALEffect,setParamf)(Context, param, value); } + UnlockEffectsWrite(Device); ALCcontext_DecRef(Context); } @@ -233,6 +246,7 @@ AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat if(!Context) return; Device = Context->Device; + LockEffectsWrite(Device); if((ALEffect=LookupEffect(Device, effect)) == NULL) alSetError(Context, AL_INVALID_NAME); else @@ -240,6 +254,7 @@ AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat /* Call the appropriate handler */ V(ALEffect,setParamfv)(Context, param, values); } + UnlockEffectsWrite(Device); ALCcontext_DecRef(Context); } @@ -254,6 +269,7 @@ AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value if(!Context) return; Device = Context->Device; + LockEffectsRead(Device); if((ALEffect=LookupEffect(Device, effect)) == NULL) alSetError(Context, AL_INVALID_NAME); else @@ -266,6 +282,7 @@ AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value V(ALEffect,getParami)(Context, param, value); } } + UnlockEffectsRead(Device); ALCcontext_DecRef(Context); } @@ -287,6 +304,7 @@ AL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *valu if(!Context) return; Device = Context->Device; + LockEffectsRead(Device); if((ALEffect=LookupEffect(Device, effect)) == NULL) alSetError(Context, AL_INVALID_NAME); else @@ -294,6 +312,7 @@ AL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *valu /* Call the appropriate handler */ V(ALEffect,getParamiv)(Context, param, values); } + UnlockEffectsRead(Device); ALCcontext_DecRef(Context); } @@ -308,6 +327,7 @@ AL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *val if(!Context) return; Device = Context->Device; + LockEffectsRead(Device); if((ALEffect=LookupEffect(Device, effect)) == NULL) alSetError(Context, AL_INVALID_NAME); else @@ -315,6 +335,7 @@ AL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *val /* Call the appropriate handler */ V(ALEffect,getParamf)(Context, param, value); } + UnlockEffectsRead(Device); ALCcontext_DecRef(Context); } @@ -329,6 +350,7 @@ AL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *va if(!Context) return; Device = Context->Device; + LockEffectsRead(Device); if((ALEffect=LookupEffect(Device, effect)) == NULL) alSetError(Context, AL_INVALID_NAME); else @@ -336,6 +358,7 @@ AL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *va /* Call the appropriate handler */ V(ALEffect,getParamfv)(Context, param, values); } + UnlockEffectsRead(Device); ALCcontext_DecRef(Context); } @@ -352,13 +375,13 @@ ALvoid ReleaseALEffects(ALCdevice *device) ALsizei i; for(i = 0;i < device->EffectMap.size;i++) { - ALeffect *temp = device->EffectMap.array[i].value; - device->EffectMap.array[i].value = NULL; + ALeffect *temp = device->EffectMap.values[i]; + device->EffectMap.values[i] = NULL; // Release effect structure FreeThunkEntry(temp->id); memset(temp, 0, sizeof(ALeffect)); - free(temp); + al_free(temp); } } @@ -427,13 +450,6 @@ static void InitEffectParams(ALeffect *effect, ALenum type) effect->Props.Reverb.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT; SET_VTABLE1(ALreverb, effect); break; - case AL_EFFECT_AUTOWAH: - effect->Props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME; - effect->Props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN; - effect->Props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME; - effect->Props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE; - SET_VTABLE1(ALautowah, effect); - break; case AL_EFFECT_CHORUS: effect->Props.Chorus.Waveform = AL_CHORUS_DEFAULT_WAVEFORM; effect->Props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE; @@ -651,9 +667,9 @@ ALvoid LoadReverbPreset(const char *name, ALeffect *effect) return; } - if(!DisabledEffects[EAXREVERB]) + if(!DisabledEffects[AL__EAXREVERB]) InitEffectParams(effect, AL_EFFECT_EAXREVERB); - else if(!DisabledEffects[REVERB]) + else if(!DisabledEffects[AL__REVERB]) InitEffectParams(effect, AL_EFFECT_REVERB); else InitEffectParams(effect, AL_EFFECT_NULL); diff --git a/libs/openal-soft/OpenAL32/alError.c b/libs/openal-soft/OpenAL32/alError.c index b38d8dfe..6c953977 100644 --- a/libs/openal-soft/OpenAL32/alError.c +++ b/libs/openal-soft/OpenAL32/alError.c @@ -36,6 +36,8 @@ ALboolean TrapALError = AL_FALSE; ALvoid alSetError(ALCcontext *Context, ALenum errorCode) { ALenum curerr = AL_NO_ERROR; + + WARN("Error generated on context %p, code 0x%04x\n", Context, errorCode); if(TrapALError) { #ifdef _WIN32 @@ -46,7 +48,8 @@ ALvoid alSetError(ALCcontext *Context, ALenum errorCode) raise(SIGTRAP); #endif } - ATOMIC_COMPARE_EXCHANGE_STRONG(ALenum, &Context->LastError, &curerr, errorCode); + + (void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&Context->LastError, &curerr, errorCode)); } AL_API ALenum AL_APIENTRY alGetError(void) @@ -57,6 +60,8 @@ AL_API ALenum AL_APIENTRY alGetError(void) Context = GetContextRef(); if(!Context) { + WARN("Querying error state on null context (implicitly 0x%04x)\n", + AL_INVALID_OPERATION); if(TrapALError) { #ifdef _WIN32 @@ -69,7 +74,7 @@ AL_API ALenum AL_APIENTRY alGetError(void) return AL_INVALID_OPERATION; } - errorCode = ATOMIC_EXCHANGE(ALenum, &Context->LastError, AL_NO_ERROR); + errorCode = ATOMIC_EXCHANGE_SEQ(&Context->LastError, AL_NO_ERROR); ALCcontext_DecRef(Context); diff --git a/libs/openal-soft/OpenAL32/alExtension.c b/libs/openal-soft/OpenAL32/alExtension.c index 609cc969..c85e9007 100644 --- a/libs/openal-soft/OpenAL32/alExtension.c +++ b/libs/openal-soft/OpenAL32/alExtension.c @@ -36,20 +36,17 @@ const struct EffectList EffectList[] = { - { "eaxreverb", EAXREVERB, "AL_EFFECT_EAXREVERB", AL_EFFECT_EAXREVERB }, - { "reverb", REVERB, "AL_EFFECT_REVERB", AL_EFFECT_REVERB }, -#if 0 - { "autowah", AUTOWAH, "AL_EFFECT_AUTOWAH", AL_EFFECT_AUTOWAH }, -#endif - { "chorus", CHORUS, "AL_EFFECT_CHORUS", AL_EFFECT_CHORUS }, - { "compressor", COMPRESSOR, "AL_EFFECT_COMPRESSOR", AL_EFFECT_COMPRESSOR }, - { "distortion", DISTORTION, "AL_EFFECT_DISTORTION", AL_EFFECT_DISTORTION }, - { "echo", ECHO, "AL_EFFECT_ECHO", AL_EFFECT_ECHO }, - { "equalizer", EQUALIZER, "AL_EFFECT_EQUALIZER", AL_EFFECT_EQUALIZER }, - { "flanger", FLANGER, "AL_EFFECT_FLANGER", AL_EFFECT_FLANGER }, - { "modulator", MODULATOR, "AL_EFFECT_RING_MODULATOR", AL_EFFECT_RING_MODULATOR }, - { "dedicated", DEDICATED, "AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT", AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT }, - { "dedicated", DEDICATED, "AL_EFFECT_DEDICATED_DIALOGUE", AL_EFFECT_DEDICATED_DIALOGUE }, + { "eaxreverb", AL__EAXREVERB, "AL_EFFECT_EAXREVERB", AL_EFFECT_EAXREVERB }, + { "reverb", AL__REVERB, "AL_EFFECT_REVERB", AL_EFFECT_REVERB }, + { "chorus", AL__CHORUS, "AL_EFFECT_CHORUS", AL_EFFECT_CHORUS }, + { "compressor", AL__COMPRESSOR, "AL_EFFECT_COMPRESSOR", AL_EFFECT_COMPRESSOR }, + { "distortion", AL__DISTORTION, "AL_EFFECT_DISTORTION", AL_EFFECT_DISTORTION }, + { "echo", AL__ECHO, "AL_EFFECT_ECHO", AL_EFFECT_ECHO }, + { "equalizer", AL__EQUALIZER, "AL_EFFECT_EQUALIZER", AL_EFFECT_EQUALIZER }, + { "flanger", AL__FLANGER, "AL_EFFECT_FLANGER", AL_EFFECT_FLANGER }, + { "modulator", AL__MODULATOR, "AL_EFFECT_RING_MODULATOR", AL_EFFECT_RING_MODULATOR }, + { "dedicated", AL__DEDICATED, "AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT", AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT }, + { "dedicated", AL__DEDICATED, "AL_EFFECT_DEDICATED_DIALOGUE", AL_EFFECT_DEDICATED_DIALOGUE }, { NULL, 0, NULL, (ALenum)0 } }; diff --git a/libs/openal-soft/OpenAL32/alFilter.c b/libs/openal-soft/OpenAL32/alFilter.c index 8e04ec2a..2a2b9621 100644 --- a/libs/openal-soft/OpenAL32/alFilter.c +++ b/libs/openal-soft/OpenAL32/alFilter.c @@ -29,11 +29,15 @@ #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 *src, ALuint numsamples); -extern inline ALfloat ALfilterState_processSingle(ALfilterState *filter, ALfloat sample); +extern inline void ALfilterState_copyParams(ALfilterState *restrict dst, const ALfilterState *restrict src); +extern inline void ALfilterState_processPassthru(ALfilterState *filter, const ALfloat *restrict src, ALsizei numsamples); extern inline ALfloat calc_rcpQ_from_slope(ALfloat gain, ALfloat slope); extern inline ALfloat calc_rcpQ_from_bandwidth(ALfloat freq_mult, ALfloat bandwidth); @@ -56,7 +60,7 @@ AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters) device = context->Device; for(cur = 0;cur < n;cur++) { - ALfilter *filter = calloc(1, sizeof(ALfilter)); + ALfilter *filter = al_calloc(16, sizeof(ALfilter)); if(!filter) { alDeleteFilters(cur, filters); @@ -71,7 +75,7 @@ AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters) { FreeThunkEntry(filter->id); memset(filter, 0, sizeof(ALfilter)); - free(filter); + al_free(filter); alDeleteFilters(cur, filters); SET_ERROR_AND_GOTO(context, err, done); @@ -94,10 +98,10 @@ AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters) context = GetContextRef(); if(!context) return; + device = context->Device; + LockFiltersWrite(device); if(!(n >= 0)) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - - device = context->Device; for(i = 0;i < n;i++) { if(filters[i] && LookupFilter(device, filters[i]) == NULL) @@ -110,10 +114,11 @@ AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters) FreeThunkEntry(filter->id); memset(filter, 0, sizeof(*filter)); - free(filter); + al_free(filter); } done: + UnlockFiltersWrite(device); ALCcontext_DecRef(context); } @@ -125,8 +130,10 @@ AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter) Context = GetContextRef(); if(!Context) return AL_FALSE; + LockFiltersRead(Context->Device); result = ((!filter || LookupFilter(Context->Device, filter)) ? AL_TRUE : AL_FALSE); + UnlockFiltersRead(Context->Device); ALCcontext_DecRef(Context); @@ -143,6 +150,7 @@ AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value) if(!Context) return; Device = Context->Device; + LockFiltersWrite(Device); if((ALFilter=LookupFilter(Device, filter)) == NULL) alSetError(Context, AL_INVALID_NAME); else @@ -161,6 +169,7 @@ AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value) ALfilter_SetParami(ALFilter, Context, param, value); } } + UnlockFiltersWrite(Device); ALCcontext_DecRef(Context); } @@ -182,6 +191,7 @@ AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *v if(!Context) return; Device = Context->Device; + LockFiltersWrite(Device); if((ALFilter=LookupFilter(Device, filter)) == NULL) alSetError(Context, AL_INVALID_NAME); else @@ -189,6 +199,7 @@ AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *v /* Call the appropriate handler */ ALfilter_SetParamiv(ALFilter, Context, param, values); } + UnlockFiltersWrite(Device); ALCcontext_DecRef(Context); } @@ -203,6 +214,7 @@ AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat value) if(!Context) return; Device = Context->Device; + LockFiltersWrite(Device); if((ALFilter=LookupFilter(Device, filter)) == NULL) alSetError(Context, AL_INVALID_NAME); else @@ -210,6 +222,7 @@ AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat value) /* Call the appropriate handler */ ALfilter_SetParamf(ALFilter, Context, param, value); } + UnlockFiltersWrite(Device); ALCcontext_DecRef(Context); } @@ -224,6 +237,7 @@ AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat if(!Context) return; Device = Context->Device; + LockFiltersWrite(Device); if((ALFilter=LookupFilter(Device, filter)) == NULL) alSetError(Context, AL_INVALID_NAME); else @@ -231,6 +245,7 @@ AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat /* Call the appropriate handler */ ALfilter_SetParamfv(ALFilter, Context, param, values); } + UnlockFiltersWrite(Device); ALCcontext_DecRef(Context); } @@ -245,6 +260,7 @@ AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value if(!Context) return; Device = Context->Device; + LockFiltersRead(Device); if((ALFilter=LookupFilter(Device, filter)) == NULL) alSetError(Context, AL_INVALID_NAME); else @@ -257,6 +273,7 @@ AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value ALfilter_GetParami(ALFilter, Context, param, value); } } + UnlockFiltersRead(Device); ALCcontext_DecRef(Context); } @@ -278,6 +295,7 @@ AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *valu if(!Context) return; Device = Context->Device; + LockFiltersRead(Device); if((ALFilter=LookupFilter(Device, filter)) == NULL) alSetError(Context, AL_INVALID_NAME); else @@ -285,6 +303,7 @@ AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *valu /* Call the appropriate handler */ ALfilter_GetParamiv(ALFilter, Context, param, values); } + UnlockFiltersRead(Device); ALCcontext_DecRef(Context); } @@ -299,6 +318,7 @@ AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *val if(!Context) return; Device = Context->Device; + LockFiltersRead(Device); if((ALFilter=LookupFilter(Device, filter)) == NULL) alSetError(Context, AL_INVALID_NAME); else @@ -306,6 +326,7 @@ AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *val /* Call the appropriate handler */ ALfilter_GetParamf(ALFilter, Context, param, value); } + UnlockFiltersRead(Device); ALCcontext_DecRef(Context); } @@ -320,6 +341,7 @@ AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *va if(!Context) return; Device = Context->Device; + LockFiltersRead(Device); if((ALFilter=LookupFilter(Device, filter)) == NULL) alSetError(Context, AL_INVALID_NAME); else @@ -327,6 +349,7 @@ AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *va /* Call the appropriate handler */ ALfilter_GetParamfv(ALFilter, Context, param, values); } + UnlockFiltersRead(Device); ALCcontext_DecRef(Context); } @@ -340,7 +363,7 @@ void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat g ALfloat b[3] = { 1.0f, 0.0f, 0.0f }; // Limit gain to -100dB - gain = maxf(gain, 0.00001f); + assert(gain > 0.00001f); w0 = F_TAU * freq_mult; sin_w0 = sinf(w0); @@ -406,11 +429,9 @@ void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat g 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]; - filter->input_gain = b[0] / a[0]; - - filter->process = ALfilterState_processC; } @@ -613,13 +634,13 @@ ALvoid ReleaseALFilters(ALCdevice *device) ALsizei i; for(i = 0;i < device->FilterMap.size;i++) { - ALfilter *temp = device->FilterMap.array[i].value; - device->FilterMap.array[i].value = NULL; + ALfilter *temp = device->FilterMap.values[i]; + device->FilterMap.values[i] = NULL; // Release filter structure FreeThunkEntry(temp->id); memset(temp, 0, sizeof(ALfilter)); - free(temp); + al_free(temp); } } diff --git a/libs/openal-soft/OpenAL32/alListener.c b/libs/openal-soft/OpenAL32/alListener.c index 66865473..d117c0ca 100644 --- a/libs/openal-soft/OpenAL32/alListener.c +++ b/libs/openal-soft/OpenAL32/alListener.c @@ -33,29 +33,29 @@ AL_API ALvoid AL_APIENTRY alListenerf(ALenum param, ALfloat value) context = GetContextRef(); if(!context) return; + WriteLock(&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; - ATOMIC_STORE(&context->UpdateSources, AL_TRUE); break; case AL_METERS_PER_UNIT: if(!(value >= 0.0f && isfinite(value))) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - context->Listener->MetersPerUnit = value; - ATOMIC_STORE(&context->UpdateSources, AL_TRUE); break; default: SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); } + if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) + UpdateListenerProps(context); done: + WriteUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -67,33 +67,33 @@ AL_API ALvoid AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat val context = GetContextRef(); if(!context) return; + WriteLock(&context->PropLock); switch(param) { case AL_POSITION: if(!(isfinite(value1) && isfinite(value2) && isfinite(value3))) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - - LockContext(context); - aluVectorSet(&context->Listener->Position, value1, value2, value3, 1.0f); - ATOMIC_STORE(&context->UpdateSources, AL_TRUE); - UnlockContext(context); + context->Listener->Position[0] = value1; + context->Listener->Position[1] = value2; + context->Listener->Position[2] = value3; break; case AL_VELOCITY: if(!(isfinite(value1) && isfinite(value2) && isfinite(value3))) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - - LockContext(context); - aluVectorSet(&context->Listener->Velocity, value1, value2, value3, 0.0f); - ATOMIC_STORE(&context->UpdateSources, AL_TRUE); - UnlockContext(context); + context->Listener->Velocity[0] = value1; + context->Listener->Velocity[1] = value2; + context->Listener->Velocity[2] = value3; break; default: SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); } + if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) + UpdateListenerProps(context); done: + WriteUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -121,6 +121,7 @@ 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); switch(param) @@ -129,8 +130,6 @@ AL_API ALvoid AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values) if(!(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]) && isfinite(values[3]) && isfinite(values[4]) && isfinite(values[5]))) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - - LockContext(context); /* AT then UP */ context->Listener->Forward[0] = values[0]; context->Listener->Forward[1] = values[1]; @@ -138,15 +137,16 @@ AL_API ALvoid AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values) context->Listener->Up[0] = values[3]; context->Listener->Up[1] = values[4]; context->Listener->Up[2] = values[5]; - ATOMIC_STORE(&context->UpdateSources, AL_TRUE); - UnlockContext(context); break; default: SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); } + if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) + UpdateListenerProps(context); done: + WriteUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -158,13 +158,17 @@ AL_API ALvoid AL_APIENTRY alListeneri(ALenum param, ALint UNUSED(value)) context = GetContextRef(); if(!context) return; + WriteLock(&context->PropLock); switch(param) { default: SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); } + if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) + UpdateListenerProps(context); done: + WriteUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -184,13 +188,17 @@ AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, A context = GetContextRef(); if(!context) return; + WriteLock(&context->PropLock); switch(param) { default: SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); } + if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) + UpdateListenerProps(context); done: + WriteUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -224,6 +232,7 @@ 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) @@ -231,8 +240,11 @@ AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values) default: SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); } + if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) + UpdateListenerProps(context); done: + WriteUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -244,6 +256,7 @@ 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) @@ -261,6 +274,7 @@ AL_API ALvoid AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value) } done: + ReadUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -272,24 +286,21 @@ 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) { case AL_POSITION: - LockContext(context); - *value1 = context->Listener->Position.v[0]; - *value2 = context->Listener->Position.v[1]; - *value3 = context->Listener->Position.v[2]; - UnlockContext(context); + *value1 = context->Listener->Position[0]; + *value2 = context->Listener->Position[1]; + *value3 = context->Listener->Position[2]; break; case AL_VELOCITY: - LockContext(context); - *value1 = context->Listener->Velocity.v[0]; - *value2 = context->Listener->Velocity.v[1]; - *value3 = context->Listener->Velocity.v[2]; - UnlockContext(context); + *value1 = context->Listener->Velocity[0]; + *value2 = context->Listener->Velocity[1]; + *value3 = context->Listener->Velocity[2]; break; default: @@ -297,6 +308,7 @@ AL_API ALvoid AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat } done: + ReadUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -321,12 +333,12 @@ 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) { case AL_ORIENTATION: - LockContext(context); // AT then UP values[0] = context->Listener->Forward[0]; values[1] = context->Listener->Forward[1]; @@ -334,7 +346,6 @@ AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values) values[3] = context->Listener->Up[0]; values[4] = context->Listener->Up[1]; values[5] = context->Listener->Up[2]; - UnlockContext(context); break; default: @@ -342,6 +353,7 @@ AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values) } done: + ReadUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -353,6 +365,7 @@ 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) @@ -362,6 +375,7 @@ AL_API ALvoid AL_APIENTRY alGetListeneri(ALenum param, ALint *value) } done: + ReadUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -373,24 +387,21 @@ 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) { case AL_POSITION: - LockContext(context); - *value1 = (ALint)context->Listener->Position.v[0]; - *value2 = (ALint)context->Listener->Position.v[1]; - *value3 = (ALint)context->Listener->Position.v[2]; - UnlockContext(context); + *value1 = (ALint)context->Listener->Position[0]; + *value2 = (ALint)context->Listener->Position[1]; + *value3 = (ALint)context->Listener->Position[2]; break; case AL_VELOCITY: - LockContext(context); - *value1 = (ALint)context->Listener->Velocity.v[0]; - *value2 = (ALint)context->Listener->Velocity.v[1]; - *value3 = (ALint)context->Listener->Velocity.v[2]; - UnlockContext(context); + *value1 = (ALint)context->Listener->Velocity[0]; + *value2 = (ALint)context->Listener->Velocity[1]; + *value3 = (ALint)context->Listener->Velocity[2]; break; default: @@ -398,6 +409,7 @@ AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *valu } done: + ReadUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -417,12 +429,12 @@ 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) { case AL_ORIENTATION: - LockContext(context); // AT then UP values[0] = (ALint)context->Listener->Forward[0]; values[1] = (ALint)context->Listener->Forward[1]; @@ -430,7 +442,6 @@ AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values) values[3] = (ALint)context->Listener->Up[0]; values[4] = (ALint)context->Listener->Up[1]; values[5] = (ALint)context->Listener->Up[2]; - UnlockContext(context); break; default: @@ -438,5 +449,62 @@ AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values) } done: + ReadUnlock(&context->PropLock); ALCcontext_DecRef(context); } + + +void UpdateListenerProps(ALCcontext *context) +{ + ALlistener *listener = context->Listener; + struct ALlistenerProps *props; + + /* Get an unused proprty container, or allocate a new one as needed. */ + props = ATOMIC_LOAD(&listener->FreeList, almemory_order_acquire); + if(!props) + props = al_calloc(16, sizeof(*props)); + else + { + struct ALlistenerProps *next; + do { + next = ATOMIC_LOAD(&props->next, almemory_order_relaxed); + } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&listener->FreeList, &props, next, + almemory_order_seq_cst, almemory_order_acquire) == 0); + } + + /* Copy in current property values. */ + props->Position[0] = listener->Position[0]; + props->Position[1] = listener->Position[1]; + props->Position[2] = listener->Position[2]; + + props->Velocity[0] = listener->Velocity[0]; + props->Velocity[1] = listener->Velocity[1]; + props->Velocity[2] = listener->Velocity[2]; + + 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]; + + props->Gain = listener->Gain; + props->MetersPerUnit = listener->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(&listener->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 ALlistenerProps*, &listener->FreeList, props); + } +} diff --git a/libs/openal-soft/OpenAL32/alSource.c b/libs/openal-soft/OpenAL32/alSource.c index 0d454882..be191fdb 100644 --- a/libs/openal-soft/OpenAL32/alSource.c +++ b/libs/openal-soft/OpenAL32/alSource.c @@ -37,16 +37,24 @@ #include "backends/base.h" #include "threads.h" +#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 ALvoid InitSourceParams(ALsource *Source); -static ALint64 GetSourceSampleOffset(ALsource *Source); -static ALdouble GetSourceSecOffset(ALsource *Source); -static ALvoid GetSourceOffsets(ALsource *Source, ALenum name, ALdouble *offsets, ALdouble updateLen); -static ALboolean GetSampleOffset(ALsource *Source, ALuint *offset, ALuint *frac); +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); +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); typedef enum SourceProp { srcPitch = AL_PITCH, @@ -96,16 +104,24 @@ typedef enum SourceProp { srcSampleLengthSOFT = AL_SAMPLE_LENGTH_SOFT, srcSecLengthSOFT = AL_SEC_LENGTH_SOFT, - /* AL_SOFT_buffer_sub_data / AL_SOFT_buffer_samples */ - srcSampleRWOffsetsSOFT = AL_SAMPLE_RW_OFFSETS_SOFT, - srcByteRWOffsetsSOFT = AL_BYTE_RW_OFFSETS_SOFT, - /* AL_SOFT_source_latency */ srcSampleOffsetLatencySOFT = AL_SAMPLE_OFFSET_LATENCY_SOFT, srcSecOffsetLatencySOFT = AL_SEC_OFFSET_LATENCY_SOFT, + /* AL_EXT_STEREO_ANGLES */ + srcAngles = AL_STEREO_ANGLES, + + /* AL_EXT_SOURCE_RADIUS */ + srcRadius = AL_SOURCE_RADIUS, + /* AL_EXT_BFORMAT */ srcOrientation = AL_ORIENTATION, + + /* AL_SOFT_source_resampler */ + srcResampler = AL_SOURCE_RESAMPLER_SOFT, + + /* AL_SOFT_source_spatialize */ + srcSpatialize = AL_SOURCE_SPATIALIZE_SOFT, } SourceProp; static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALfloat *values); @@ -116,6 +132,56 @@ 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 ALvoice *GetSourceVoice(const ALsource *source, const ALCcontext *context) +{ + ALvoice **voice = context->Voices; + ALvoice **voice_end = voice + context->VoiceCount; + while(voice != voice_end) + { + if(ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire) == source) + return *voice; + ++voice; + } + 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) +{ + ALenum state = ATOMIC_LOAD(&source->state, almemory_order_acquire); + return state == AL_PLAYING || 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) + { + ALenum state = AL_PLAYING; + if(ATOMIC_COMPARE_EXCHANGE_STRONG(&source->state, &state, AL_STOPPED, + almemory_order_acq_rel, almemory_order_acquire)) + return AL_STOPPED; + return state; + } + return ATOMIC_LOAD(&source->state, almemory_order_acquire); +} + +/** + * 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); +} + static ALint FloatValsByProp(ALenum prop) { if(prop != (ALenum)((SourceProp)prop)) @@ -153,10 +219,12 @@ static ALint FloatValsByProp(ALenum prop) 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_RW_OFFSETS_SOFT: - case AL_BYTE_RW_OFFSETS_SOFT: + case AL_STEREO_ANGLES: return 2; case AL_POSITION: @@ -216,11 +284,13 @@ static ALint DoubleValsByProp(ALenum prop) 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_RW_OFFSETS_SOFT: - case AL_BYTE_RW_OFFSETS_SOFT: case AL_SEC_OFFSET_LATENCY_SOFT: + case AL_STEREO_ANGLES: return 2; case AL_POSITION: @@ -280,12 +350,11 @@ static ALint IntValsByProp(ALenum prop) 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_RW_OFFSETS_SOFT: - case AL_BYTE_RW_OFFSETS_SOFT: - return 2; - case AL_POSITION: case AL_VELOCITY: case AL_DIRECTION: @@ -299,6 +368,8 @@ static ALint IntValsByProp(ALenum prop) break; /* i64 only */ case AL_SEC_OFFSET_LATENCY_SOFT: break; /* Double only */ + case AL_STEREO_ANGLES: + break; /* Float/double only */ } return 0; } @@ -341,10 +412,11 @@ static ALint Int64ValsByProp(ALenum prop) 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_RW_OFFSETS_SOFT: - case AL_BYTE_RW_OFFSETS_SOFT: case AL_SAMPLE_OFFSET_LATENCY_SOFT: return 2; @@ -359,6 +431,8 @@ static ALint Int64ValsByProp(ALenum prop) case AL_SEC_OFFSET_LATENCY_SOFT: break; /* Double only */ + case AL_STEREO_ANGLES: + break; /* Float/double only */ } return 0; } @@ -369,14 +443,22 @@ static ALint Int64ValsByProp(ALenum prop) SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE); \ } while(0) +#define DO_UPDATEPROPS() do { \ + ALvoice *voice; \ + if(SourceShouldUpdate(Source, Context) && \ + (voice=GetSourceVoice(Source, Context)) != NULL) \ + UpdateSourceProps(Source, voice, device->NumAuxSends); \ + else \ + ATOMIC_FLAG_CLEAR(&Source->PropsClean, almemory_order_release); \ +} while(0) + static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALfloat *values) { + ALCdevice *device = Context->Device; ALint ival; switch(prop) { - case AL_BYTE_RW_OFFSETS_SOFT: - case AL_SAMPLE_RW_OFFSETS_SOFT: case AL_BYTE_LENGTH_SOFT: case AL_SAMPLE_LENGTH_SOFT: case AL_SEC_LENGTH_SOFT: @@ -388,98 +470,98 @@ static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp p CHECKVAL(*values >= 0.0f); Source->Pitch = *values; - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + DO_UPDATEPROPS(); return AL_TRUE; case AL_CONE_INNER_ANGLE: CHECKVAL(*values >= 0.0f && *values <= 360.0f); Source->InnerAngle = *values; - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + DO_UPDATEPROPS(); return AL_TRUE; case AL_CONE_OUTER_ANGLE: CHECKVAL(*values >= 0.0f && *values <= 360.0f); Source->OuterAngle = *values; - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + DO_UPDATEPROPS(); return AL_TRUE; case AL_GAIN: CHECKVAL(*values >= 0.0f); Source->Gain = *values; - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + DO_UPDATEPROPS(); return AL_TRUE; case AL_MAX_DISTANCE: CHECKVAL(*values >= 0.0f); Source->MaxDistance = *values; - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + DO_UPDATEPROPS(); return AL_TRUE; case AL_ROLLOFF_FACTOR: CHECKVAL(*values >= 0.0f); - Source->RollOffFactor = *values; - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + Source->RolloffFactor = *values; + DO_UPDATEPROPS(); return AL_TRUE; case AL_REFERENCE_DISTANCE: CHECKVAL(*values >= 0.0f); Source->RefDistance = *values; - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + DO_UPDATEPROPS(); return AL_TRUE; case AL_MIN_GAIN: - CHECKVAL(*values >= 0.0f && *values <= 1.0f); + CHECKVAL(*values >= 0.0f); Source->MinGain = *values; - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + DO_UPDATEPROPS(); return AL_TRUE; case AL_MAX_GAIN: - CHECKVAL(*values >= 0.0f && *values <= 1.0f); + CHECKVAL(*values >= 0.0f); Source->MaxGain = *values; - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + DO_UPDATEPROPS(); return AL_TRUE; case AL_CONE_OUTER_GAIN: CHECKVAL(*values >= 0.0f && *values <= 1.0f); Source->OuterGain = *values; - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + DO_UPDATEPROPS(); return AL_TRUE; case AL_CONE_OUTER_GAINHF: CHECKVAL(*values >= 0.0f && *values <= 1.0f); Source->OuterGainHF = *values; - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + DO_UPDATEPROPS(); return AL_TRUE; case AL_AIR_ABSORPTION_FACTOR: CHECKVAL(*values >= 0.0f && *values <= 10.0f); Source->AirAbsorptionFactor = *values; - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + DO_UPDATEPROPS(); return AL_TRUE; case AL_ROOM_ROLLOFF_FACTOR: CHECKVAL(*values >= 0.0f && *values <= 10.0f); Source->RoomRolloffFactor = *values; - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + DO_UPDATEPROPS(); return AL_TRUE; case AL_DOPPLER_FACTOR: CHECKVAL(*values >= 0.0f && *values <= 1.0f); Source->DopplerFactor = *values; - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + DO_UPDATEPROPS(); return AL_TRUE; case AL_SEC_OFFSET: @@ -487,66 +569,87 @@ static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_BYTE_OFFSET: CHECKVAL(*values >= 0.0f); - LockContext(Context); Source->OffsetType = prop; Source->Offset = *values; - if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) && - !Context->DeferUpdates) + if(IsPlayingOrPaused(Source)) { - 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) { + WriteLock(&Source->queue_lock); + if(ApplyOffset(Source, voice) == AL_FALSE) + { + WriteUnlock(&Source->queue_lock); + ALCdevice_Unlock(Context->Device); + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE); + } WriteUnlock(&Source->queue_lock); - UnlockContext(Context); - SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE); } - WriteUnlock(&Source->queue_lock); + ALCdevice_Unlock(Context->Device); } - UnlockContext(Context); + return AL_TRUE; + + case AL_SOURCE_RADIUS: + CHECKVAL(*values >= 0.0f && isfinite(*values)); + + Source->Radius = *values; + DO_UPDATEPROPS(); + return AL_TRUE; + + case AL_STEREO_ANGLES: + CHECKVAL(isfinite(values[0]) && isfinite(values[1])); + + Source->StereoPan[0] = values[0]; + Source->StereoPan[1] = values[1]; + DO_UPDATEPROPS(); return AL_TRUE; case AL_POSITION: CHECKVAL(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2])); - LockContext(Context); - aluVectorSet(&Source->Position, values[0], values[1], values[2], 1.0f); - UnlockContext(Context); - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + Source->Position[0] = values[0]; + Source->Position[1] = values[1]; + Source->Position[2] = values[2]; + DO_UPDATEPROPS(); return AL_TRUE; case AL_VELOCITY: CHECKVAL(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2])); - LockContext(Context); - aluVectorSet(&Source->Velocity, values[0], values[1], values[2], 0.0f); - UnlockContext(Context); - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + Source->Velocity[0] = values[0]; + Source->Velocity[1] = values[1]; + Source->Velocity[2] = values[2]; + DO_UPDATEPROPS(); return AL_TRUE; case AL_DIRECTION: CHECKVAL(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2])); - LockContext(Context); - aluVectorSet(&Source->Direction, values[0], values[1], values[2], 0.0f); - UnlockContext(Context); - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + Source->Direction[0] = values[0]; + Source->Direction[1] = values[1]; + Source->Direction[2] = values[2]; + DO_UPDATEPROPS(); return AL_TRUE; case AL_ORIENTATION: CHECKVAL(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]) && isfinite(values[3]) && isfinite(values[4]) && isfinite(values[5])); - LockContext(Context); Source->Orientation[0][0] = values[0]; Source->Orientation[0][1] = values[1]; Source->Orientation[0][2] = values[2]; Source->Orientation[1][0] = values[3]; Source->Orientation[1][1] = values[4]; Source->Orientation[1][2] = values[5]; - UnlockContext(Context); - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + DO_UPDATEPROPS(); return AL_TRUE; @@ -559,6 +662,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); @@ -585,7 +690,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) @@ -594,8 +698,6 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_SOURCE_TYPE: case AL_BUFFERS_QUEUED: case AL_BUFFERS_PROCESSED: - case AL_SAMPLE_RW_OFFSETS_SOFT: - case AL_BYTE_RW_OFFSETS_SOFT: case AL_BYTE_LENGTH_SOFT: case AL_SAMPLE_LENGTH_SOFT: case AL_SEC_LENGTH_SOFT: @@ -606,61 +708,85 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p CHECKVAL(*values == AL_FALSE || *values == AL_TRUE); Source->HeadRelative = (ALboolean)*values; - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + DO_UPDATEPROPS(); return AL_TRUE; case AL_LOOPING: CHECKVAL(*values == AL_FALSE || *values == AL_TRUE); + WriteLock(&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(); + } + } + WriteUnlock(&Source->queue_lock); return AL_TRUE; case AL_BUFFER: - CHECKVAL(*values == 0 || (buffer=LookupBuffer(device, *values)) != NULL); - - WriteLock(&Source->queue_lock); - if(!(Source->state == AL_STOPPED || Source->state == AL_INITIAL)) + LockBuffersRead(device); + if(!(*values == 0 || (buffer=LookupBuffer(device, *values)) != NULL)) { - WriteUnlock(&Source->queue_lock); - SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_OPERATION, AL_FALSE); + UnlockBuffersRead(device); + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE); } + WriteLock(&Source->queue_lock); + { + ALenum state = GetSourceState(Source, GetSourceVoice(Source, Context)); + if(state == AL_PLAYING || state == AL_PAUSED) + { + WriteUnlock(&Source->queue_lock); + UnlockBuffersRead(device); + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_OPERATION, AL_FALSE); + } + } + + oldlist = Source->queue; if(buffer != NULL) { /* Add the selected buffer to a one-item queue */ - newlist = malloc(sizeof(ALbufferlistitem)); + ALbufferlistitem *newlist = al_calloc(DEF_ALIGN, sizeof(ALbufferlistitem)); newlist->buffer = buffer; - newlist->next = NULL; - newlist->prev = NULL; + ATOMIC_INIT(&newlist->next, NULL); IncrementRef(&buffer->ref); /* Source is now Static */ Source->SourceType = AL_STATIC; - - ReadLock(&buffer->lock); - Source->NumChannels = ChannelsFromFmt(buffer->FmtChannels); - Source->SampleSize = BytesFromFmt(buffer->FmtType); - ReadUnlock(&buffer->lock); + 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); /* Delete all elements in the previous queue */ while(oldlist != NULL) { ALbufferlistitem *temp = oldlist; - oldlist = temp->next; + oldlist = ATOMIC_LOAD(&temp->next, almemory_order_relaxed); if(temp->buffer) DecrementRef(&temp->buffer->ref); - free(temp); + al_free(temp); } return AL_TRUE; @@ -669,29 +795,38 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_BYTE_OFFSET: CHECKVAL(*values >= 0); - LockContext(Context); Source->OffsetType = prop; Source->Offset = *values; - if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) && - !Context->DeferUpdates) + if(IsPlayingOrPaused(Source)) { - WriteLock(&Source->queue_lock); - if(ApplyOffset(Source) == AL_FALSE) + ALvoice *voice; + + ALCdevice_Lock(Context->Device); + voice = GetSourceVoice(Source, Context); + if(voice) { + WriteLock(&Source->queue_lock); + if(ApplyOffset(Source, voice) == AL_FALSE) + { + WriteUnlock(&Source->queue_lock); + ALCdevice_Unlock(Context->Device); + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE); + } WriteUnlock(&Source->queue_lock); - UnlockContext(Context); - SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE); } - WriteUnlock(&Source->queue_lock); + ALCdevice_Unlock(Context->Device); } - UnlockContext(Context); return AL_TRUE; case AL_DIRECT_FILTER: - CHECKVAL(*values == 0 || (filter=LookupFilter(device, *values)) != NULL); + LockFiltersRead(device); + if(!(*values == 0 || (filter=LookupFilter(device, *values)) != NULL)) + { + UnlockFiltersRead(device); + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE); + } - LockContext(Context); if(!filter) { Source->Direct.Gain = 1.0f; @@ -708,36 +843,36 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p Source->Direct.GainLF = filter->GainLF; Source->Direct.LFReference = filter->LFReference; } - UnlockContext(Context); - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + UnlockFiltersRead(device); + DO_UPDATEPROPS(); return AL_TRUE; case AL_DIRECT_FILTER_GAINHF_AUTO: CHECKVAL(*values == AL_FALSE || *values == AL_TRUE); Source->DryGainHFAuto = *values; - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + DO_UPDATEPROPS(); return AL_TRUE; case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: CHECKVAL(*values == AL_FALSE || *values == AL_TRUE); Source->WetGainAuto = *values; - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + DO_UPDATEPROPS(); return AL_TRUE; case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: CHECKVAL(*values == AL_FALSE || *values == AL_TRUE); Source->WetGainHFAuto = *values; - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + DO_UPDATEPROPS(); return AL_TRUE; case AL_DIRECT_CHANNELS_SOFT: CHECKVAL(*values == AL_FALSE || *values == AL_TRUE); Source->DirectChannels = *values; - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + DO_UPDATEPROPS(); return AL_TRUE; case AL_DISTANCE_MODEL: @@ -751,25 +886,36 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p Source->DistanceModel = *values; if(Context->SourceDistanceModel) - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + 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: - LockContext(Context); - if(!((ALuint)values[1] < device->NumAuxSends && + LockEffectSlotsRead(Context); + LockFiltersRead(device); + if(!((ALuint)values[1] < (ALuint)device->NumAuxSends && (values[0] == 0 || (slot=LookupEffectSlot(Context, values[0])) != NULL) && (values[2] == 0 || (filter=LookupFilter(device, values[2])) != NULL))) { - UnlockContext(Context); + UnlockFiltersRead(device); + UnlockEffectSlotsRead(Context); SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE); } - /* Add refcount on the new slot, and release the previous slot */ - if(slot) IncrementRef(&slot->ref); - slot = ExchangePtr((XchgPtr*)&Source->Send[values[1]].Slot, slot); - if(slot) DecrementRef(&slot->ref); - if(!filter) { /* Disable filter */ @@ -787,8 +933,35 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p Source->Send[values[1]].GainLF = filter->GainLF; Source->Send[values[1]].LFReference = filter->LFReference; } - UnlockContext(Context); - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + UnlockFiltersRead(device); + + 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 an + * active source, in case the slot is about to be deleted. + */ + if((voice=GetSourceVoice(Source, Context)) != NULL) + UpdateSourceProps(Source, voice, device->NumAuxSends); + else + ATOMIC_FLAG_CLEAR(&Source->PropsClean, almemory_order_release); + } + else + { + if(slot) IncrementRef(&slot->ref); + if(Source->Send[values[1]].Slot) + DecrementRef(&Source->Send[values[1]].Slot->ref); + Source->Send[values[1]].Slot = slot; + DO_UPDATEPROPS(); + } + UnlockEffectSlotsRead(Context); + return AL_TRUE; @@ -807,6 +980,7 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_CONE_OUTER_GAINHF: case AL_AIR_ABSORPTION_FACTOR: case AL_ROOM_ROLLOFF_FACTOR: + case AL_SOURCE_RADIUS: fvals[0] = (ALfloat)*values; return SetSourcefv(Source, Context, (int)prop, fvals); @@ -831,6 +1005,7 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_SAMPLE_OFFSET_LATENCY_SOFT: case AL_SEC_OFFSET_LATENCY_SOFT: + case AL_STEREO_ANGLES: break; } @@ -849,8 +1024,6 @@ static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp case AL_BUFFERS_QUEUED: case AL_BUFFERS_PROCESSED: case AL_SOURCE_STATE: - case AL_SAMPLE_RW_OFFSETS_SOFT: - case AL_BYTE_RW_OFFSETS_SOFT: case AL_SAMPLE_OFFSET_LATENCY_SOFT: case AL_BYTE_LENGTH_SOFT: case AL_SAMPLE_LENGTH_SOFT: @@ -870,6 +1043,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; @@ -909,6 +1084,7 @@ static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp case AL_CONE_OUTER_GAINHF: case AL_AIR_ABSORPTION_FACTOR: case AL_ROOM_ROLLOFF_FACTOR: + case AL_SOURCE_RADIUS: fvals[0] = (ALfloat)*values; return SetSourcefv(Source, Context, (int)prop, fvals); @@ -932,6 +1108,7 @@ static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp return SetSourcefv(Source, Context, (int)prop, fvals); case AL_SEC_OFFSET_LATENCY_SOFT: + case AL_STEREO_ANGLES: break; } @@ -946,8 +1123,8 @@ static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp p { ALCdevice *device = Context->Device; ALbufferlistitem *BufferList; - ALdouble offsets[2]; - ALdouble updateLen; + ClockLatency clocktime; + ALuint64 srcclock; ALint ivals[3]; ALboolean err; @@ -966,7 +1143,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: @@ -996,10 +1173,7 @@ static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_SEC_OFFSET: case AL_SAMPLE_OFFSET: case AL_BYTE_OFFSET: - LockContext(Context); - GetSourceOffsets(Source, prop, offsets, 0.0); - UnlockContext(Context); - *values = offsets[0]; + *values = GetSourceOffset(Source, prop, Context); return AL_TRUE; case AL_CONE_OUTER_GAINHF: @@ -1020,7 +1194,7 @@ static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_SEC_LENGTH_SOFT: ReadLock(&Source->queue_lock); - if(!(BufferList=ATOMIC_LOAD(&Source->queue))) + if(!(BufferList=Source->queue)) *values = 0; else { @@ -1033,61 +1207,67 @@ static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp p freq = buffer->Frequency; length += buffer->SampleLen; } - } while((BufferList=BufferList->next) != NULL); + BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed); + } while(BufferList != NULL); *values = (ALdouble)length / (ALdouble)freq; } ReadUnlock(&Source->queue_lock); return AL_TRUE; - case AL_SAMPLE_RW_OFFSETS_SOFT: - case AL_BYTE_RW_OFFSETS_SOFT: - LockContext(Context); - updateLen = (ALdouble)device->UpdateSize / device->Frequency; - GetSourceOffsets(Source, prop, values, updateLen); - UnlockContext(Context); + case AL_SOURCE_RADIUS: + *values = Source->Radius; + return AL_TRUE; + + case AL_STEREO_ANGLES: + values[0] = Source->StereoPan[0]; + values[1] = Source->StereoPan[1]; return AL_TRUE; case AL_SEC_OFFSET_LATENCY_SOFT: - LockContext(Context); - values[0] = GetSourceSecOffset(Source); - values[1] = (ALdouble)(V0(device->Backend,getLatency)()) / - 1000000000.0; - UnlockContext(Context); + /* 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, Context, &srcclock); + clocktime = V0(device->Backend,getClockLatency)(); + if(srcclock == (ALuint64)clocktime.ClockTime) + values[1] = (ALdouble)clocktime.Latency / 1000000000.0; + else + { + /* If the clock time incremented, reduce the latency by that + * much since it's that much closer to the source offset it got + * earlier. + */ + ALuint64 diff = clocktime.ClockTime - srcclock; + values[1] = (ALdouble)(clocktime.Latency - minu64(clocktime.Latency, diff)) / + 1000000000.0; + } return AL_TRUE; case AL_POSITION: - LockContext(Context); - values[0] = Source->Position.v[0]; - values[1] = Source->Position.v[1]; - values[2] = Source->Position.v[2]; - UnlockContext(Context); + values[0] = Source->Position[0]; + values[1] = Source->Position[1]; + values[2] = Source->Position[2]; return AL_TRUE; case AL_VELOCITY: - LockContext(Context); - values[0] = Source->Velocity.v[0]; - values[1] = Source->Velocity.v[1]; - values[2] = Source->Velocity.v[2]; - UnlockContext(Context); + values[0] = Source->Velocity[0]; + values[1] = Source->Velocity[1]; + values[2] = Source->Velocity[2]; return AL_TRUE; case AL_DIRECTION: - LockContext(Context); - values[0] = Source->Direction.v[0]; - values[1] = Source->Direction.v[1]; - values[2] = Source->Direction.v[2]; - UnlockContext(Context); + values[0] = Source->Direction[0]; + values[1] = Source->Direction[1]; + values[2] = Source->Direction[2]; return AL_TRUE; case AL_ORIENTATION: - LockContext(Context); values[0] = Source->Orientation[0][0]; values[1] = Source->Orientation[0][1]; values[2] = Source->Orientation[0][2]; values[3] = Source->Orientation[1][0]; values[4] = Source->Orientation[1][1]; values[5] = Source->Orientation[1][2]; - UnlockContext(Context); return AL_TRUE; /* 1x int */ @@ -1104,6 +1284,8 @@ static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp p 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; @@ -1137,19 +1319,18 @@ static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_BUFFER: ReadLock(&Source->queue_lock); - BufferList = (Source->SourceType == AL_STATIC) ? ATOMIC_LOAD(&Source->queue) : - ATOMIC_LOAD(&Source->current_buffer); + BufferList = (Source->SourceType == AL_STATIC) ? Source->queue : NULL; *values = (BufferList && BufferList->buffer) ? BufferList->buffer->id : 0; ReadUnlock(&Source->queue_lock); return AL_TRUE; case AL_SOURCE_STATE: - *values = Source->state; + *values = GetSourceState(Source, GetSourceVoice(Source, Context)); return AL_TRUE; case AL_BYTE_LENGTH_SOFT: ReadLock(&Source->queue_lock); - if(!(BufferList=ATOMIC_LOAD(&Source->queue))) + if(!(BufferList=Source->queue)) *values = 0; else { @@ -1180,7 +1361,8 @@ static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p length += buffer->SampleLen / sample_align * byte_align; } - } while((BufferList=BufferList->next) != NULL); + BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed); + } while(BufferList != NULL); *values = length; } ReadUnlock(&Source->queue_lock); @@ -1188,7 +1370,7 @@ static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p case AL_SAMPLE_LENGTH_SOFT: ReadLock(&Source->queue_lock); - if(!(BufferList=ATOMIC_LOAD(&Source->queue))) + if(!(BufferList=Source->queue)) *values = 0; else { @@ -1196,7 +1378,8 @@ static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p do { ALbuffer *buffer = BufferList->buffer; if(buffer) length += buffer->SampleLen; - } while((BufferList=BufferList->next) != NULL); + BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed); + } while(BufferList != NULL); *values = length; } ReadUnlock(&Source->queue_lock); @@ -1204,14 +1387,15 @@ static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p 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); + BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed); + } while(BufferList != NULL); *values = count; } ReadUnlock(&Source->queue_lock); @@ -1227,13 +1411,21 @@ 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_SEQ(&voice->current_buffer); + else if(ATOMIC_LOAD_SEQ(&Source->state) == AL_INITIAL) + Current = BufferList; + while(BufferList && BufferList != Current) { played++; - BufferList = BufferList->next; + BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next, + almemory_order_relaxed); } *values = played; } @@ -1264,6 +1456,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: @@ -1283,20 +1483,11 @@ static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p 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]; return err; - /* 2x float/double */ - case AL_SAMPLE_RW_OFFSETS_SOFT: - case AL_BYTE_RW_OFFSETS_SOFT: - if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE) - { - values[0] = (ALint)dvals[0]; - values[1] = (ALint)dvals[1]; - } - return err; - /* 3x float/double */ case AL_POSITION: case AL_VELOCITY: @@ -1326,6 +1517,8 @@ static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p break; /* i64 only */ case AL_SEC_OFFSET_LATENCY_SOFT: break; /* Double only */ + case AL_STEREO_ANGLES: + break; /* Float/double only */ case AL_DIRECT_FILTER: case AL_AUXILIARY_SEND_FILTER: @@ -1339,6 +1532,8 @@ static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint64 *values) { ALCdevice *device = Context->Device; + ClockLatency clocktime; + ALuint64 srcclock; ALdouble dvals[6]; ALint ivals[3]; ALboolean err; @@ -1346,10 +1541,22 @@ static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp switch(prop) { case AL_SAMPLE_OFFSET_LATENCY_SOFT: - LockContext(Context); - values[0] = GetSourceSampleOffset(Source); - values[1] = V0(device->Backend,getLatency)(); - UnlockContext(Context); + /* 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, Context, &srcclock); + clocktime = V0(device->Backend,getClockLatency)(); + if(srcclock == (ALuint64)clocktime.ClockTime) + values[1] = clocktime.Latency; + else + { + /* If the clock time incremented, reduce the latency by that + * much since it's that much closer to the source offset it got + * earlier. + */ + ALuint64 diff = clocktime.ClockTime - srcclock; + values[1] = clocktime.Latency - minu64(clocktime.Latency, diff); + } return AL_TRUE; /* 1x float/double */ @@ -1371,20 +1578,11 @@ static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp 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]; return err; - /* 2x float/double */ - case AL_SAMPLE_RW_OFFSETS_SOFT: - case AL_BYTE_RW_OFFSETS_SOFT: - if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE) - { - values[0] = (ALint64)dvals[0]; - values[1] = (ALint64)dvals[1]; - } - return err; - /* 3x float/double */ case AL_POSITION: case AL_VELOCITY: @@ -1424,6 +1622,8 @@ static ALboolean GetSourcei64v(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: if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE) *values = ivals[0]; return err; @@ -1447,6 +1647,8 @@ static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp case AL_SEC_OFFSET_LATENCY_SOFT: break; /* Double only */ + case AL_STEREO_ANGLES: + break; /* Float/double only */ } ERR("Unexpected property: 0x%04x\n", prop); @@ -1456,6 +1658,7 @@ static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp AL_API ALvoid AL_APIENTRY alGenSources(ALsizei n, ALuint *sources) { + ALCdevice *device; ALCcontext *context; ALsizei cur = 0; ALenum err; @@ -1465,6 +1668,7 @@ AL_API ALvoid AL_APIENTRY alGenSources(ALsizei n, ALuint *sources) if(!(n >= 0)) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + device = context->Device; for(cur = 0;cur < n;cur++) { ALsource *source = al_calloc(16, sizeof(ALsource)); @@ -1473,7 +1677,7 @@ AL_API ALvoid AL_APIENTRY alGenSources(ALsizei n, ALuint *sources) alDeleteSources(cur, sources); SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done); } - InitSourceParams(source); + InitSourceParams(source, device->NumAuxSends); err = NewThunkEntry(&source->id); if(err == AL_NO_ERROR) @@ -1498,14 +1702,15 @@ done: AL_API ALvoid AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources) { + ALCdevice *device; ALCcontext *context; - ALbufferlistitem *BufferList; ALsource *Source; - ALsizei i, j; + ALsizei i; context = GetContextRef(); if(!context) return; + LockSourcesWrite(context); if(!(n >= 0)) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); @@ -1515,48 +1720,31 @@ AL_API ALvoid AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources) if(LookupSource(context, sources[i]) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); } + device = context->Device; for(i = 0;i < n;i++) { - ALvoice *voice, *voice_end; + ALvoice *voice; 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) + ALCdevice_Lock(device); + if((voice=GetSourceVoice(Source, context)) != NULL) { - ALsource *old = Source; - if(COMPARE_EXCHANGE(&voice->Source, &old, NULL)) - break; - voice++; + ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed); + ATOMIC_STORE(&voice->Playing, false, almemory_order_release); } - UnlockContext(context); + ALCdevice_Unlock(device); - BufferList = ATOMIC_EXCHANGE(ALbufferlistitem*, &Source->queue, NULL); - while(BufferList != NULL) - { - ALbufferlistitem *next = BufferList->next; - if(BufferList->buffer != NULL) - DecrementRef(&BufferList->buffer->ref); - free(BufferList); - BufferList = next; - } - - for(j = 0;j < MAX_SENDS;++j) - { - if(Source->Send[j].Slot) - DecrementRef(&Source->Send[j].Slot->ref); - Source->Send[j].Slot = NULL; - } + DeinitSource(Source, device->NumAuxSends); memset(Source, 0, sizeof(*Source)); al_free(Source); } done: + UnlockSourcesWrite(context); ALCcontext_DecRef(context); } @@ -1569,7 +1757,9 @@ AL_API ALboolean AL_APIENTRY alIsSource(ALuint source) context = GetContextRef(); if(!context) return AL_FALSE; + LockSourcesRead(context); ret = (LookupSource(context, source) ? AL_TRUE : AL_FALSE); + UnlockSourcesRead(context); ALCcontext_DecRef(context); @@ -1585,12 +1775,16 @@ AL_API ALvoid AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value) Context = GetContextRef(); if(!Context) return; + WriteLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!(FloatValsByProp(param) == 1)) alSetError(Context, AL_INVALID_ENUM); else SetSourcefv(Source, Context, param, &value); + UnlockSourcesRead(Context); + WriteUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1603,6 +1797,8 @@ AL_API ALvoid AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1 Context = GetContextRef(); if(!Context) return; + WriteLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!(FloatValsByProp(param) == 3)) @@ -1612,6 +1808,8 @@ AL_API ALvoid AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1 ALfloat fvals[3] = { value1, value2, value3 }; SetSourcefv(Source, Context, param, fvals); } + UnlockSourcesRead(Context); + WriteUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1624,6 +1822,8 @@ AL_API ALvoid AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat Context = GetContextRef(); if(!Context) return; + WriteLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!values) @@ -1632,6 +1832,8 @@ AL_API ALvoid AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat alSetError(Context, AL_INVALID_ENUM); else SetSourcefv(Source, Context, param, values); + UnlockSourcesRead(Context); + WriteUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1645,6 +1847,8 @@ AL_API ALvoid AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble va Context = GetContextRef(); if(!Context) return; + WriteLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!(DoubleValsByProp(param) == 1)) @@ -1654,6 +1858,8 @@ AL_API ALvoid AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble va ALfloat fval = (ALfloat)value; SetSourcefv(Source, Context, param, &fval); } + UnlockSourcesRead(Context); + WriteUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1666,6 +1872,8 @@ AL_API ALvoid AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble v Context = GetContextRef(); if(!Context) return; + WriteLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!(DoubleValsByProp(param) == 3)) @@ -1675,6 +1883,8 @@ AL_API ALvoid AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble v ALfloat fvals[3] = { (ALfloat)value1, (ALfloat)value2, (ALfloat)value3 }; SetSourcefv(Source, Context, param, fvals); } + UnlockSourcesRead(Context); + WriteUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1688,6 +1898,8 @@ AL_API ALvoid AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdo Context = GetContextRef(); if(!Context) return; + WriteLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!values) @@ -1703,6 +1915,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); ALCcontext_DecRef(Context); } @@ -1716,12 +1930,16 @@ AL_API ALvoid AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value) Context = GetContextRef(); if(!Context) return; + WriteLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!(IntValsByProp(param) == 1)) alSetError(Context, AL_INVALID_ENUM); else SetSourceiv(Source, Context, param, &value); + UnlockSourcesRead(Context); + WriteUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1734,6 +1952,8 @@ AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, AL Context = GetContextRef(); if(!Context) return; + WriteLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!(IntValsByProp(param) == 3)) @@ -1743,6 +1963,8 @@ AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, AL ALint ivals[3] = { value1, value2, value3 }; SetSourceiv(Source, Context, param, ivals); } + UnlockSourcesRead(Context); + WriteUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1755,6 +1977,8 @@ AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *val Context = GetContextRef(); if(!Context) return; + WriteLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!values) @@ -1763,6 +1987,8 @@ AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *val alSetError(Context, AL_INVALID_ENUM); else SetSourceiv(Source, Context, param, values); + UnlockSourcesRead(Context); + WriteUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1776,12 +2002,16 @@ AL_API ALvoid AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SO Context = GetContextRef(); if(!Context) return; + WriteLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!(Int64ValsByProp(param) == 1)) alSetError(Context, AL_INVALID_ENUM); else SetSourcei64v(Source, Context, param, &value); + UnlockSourcesRead(Context); + WriteUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1794,6 +2024,8 @@ AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOF Context = GetContextRef(); if(!Context) return; + WriteLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!(Int64ValsByProp(param) == 3)) @@ -1803,6 +2035,8 @@ AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOF ALint64SOFT i64vals[3] = { value1, value2, value3 }; SetSourcei64v(Source, Context, param, i64vals); } + UnlockSourcesRead(Context); + WriteUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1815,6 +2049,8 @@ AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALin Context = GetContextRef(); if(!Context) return; + WriteLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!values) @@ -1823,6 +2059,8 @@ AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALin alSetError(Context, AL_INVALID_ENUM); else SetSourcei64v(Source, Context, param, values); + UnlockSourcesRead(Context); + WriteUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1836,6 +2074,8 @@ AL_API ALvoid AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *val Context = GetContextRef(); if(!Context) return; + ReadLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!value) @@ -1848,6 +2088,8 @@ AL_API ALvoid AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *val if(GetSourcedv(Source, Context, param, &dval)) *value = (ALfloat)dval; } + UnlockSourcesRead(Context); + ReadUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1861,6 +2103,8 @@ AL_API ALvoid AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *va Context = GetContextRef(); if(!Context) return; + ReadLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!(value1 && value2 && value3)) @@ -1877,6 +2121,8 @@ AL_API ALvoid AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *va *value3 = (ALfloat)dvals[2]; } } + UnlockSourcesRead(Context); + ReadUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1891,6 +2137,8 @@ AL_API ALvoid AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *va Context = GetContextRef(); if(!Context) return; + ReadLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!values) @@ -1907,6 +2155,8 @@ AL_API ALvoid AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *va values[i] = (ALfloat)dvals[i]; } } + UnlockSourcesRead(Context); + ReadUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1920,6 +2170,8 @@ AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble * Context = GetContextRef(); if(!Context) return; + ReadLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!value) @@ -1928,6 +2180,8 @@ AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble * alSetError(Context, AL_INVALID_ENUM); else GetSourcedv(Source, Context, param, value); + UnlockSourcesRead(Context); + ReadUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1940,6 +2194,8 @@ AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble Context = GetContextRef(); if(!Context) return; + ReadLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!(value1 && value2 && value3)) @@ -1956,6 +2212,8 @@ AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value3 = dvals[2]; } } + UnlockSourcesRead(Context); + ReadUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1968,6 +2226,8 @@ AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble Context = GetContextRef(); if(!Context) return; + ReadLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!values) @@ -1976,6 +2236,8 @@ AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble alSetError(Context, AL_INVALID_ENUM); else GetSourcedv(Source, Context, param, values); + UnlockSourcesRead(Context); + ReadUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -1989,6 +2251,8 @@ AL_API ALvoid AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value Context = GetContextRef(); if(!Context) return; + ReadLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!value) @@ -1997,6 +2261,8 @@ AL_API ALvoid AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value alSetError(Context, AL_INVALID_ENUM); else GetSourceiv(Source, Context, param, value); + UnlockSourcesRead(Context); + ReadUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -2010,6 +2276,8 @@ AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1 Context = GetContextRef(); if(!Context) return; + ReadLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!(value1 && value2 && value3)) @@ -2026,6 +2294,8 @@ AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1 *value3 = ivals[2]; } } + UnlockSourcesRead(Context); + ReadUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -2039,6 +2309,8 @@ AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values Context = GetContextRef(); if(!Context) return; + ReadLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!values) @@ -2047,6 +2319,8 @@ AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values alSetError(Context, AL_INVALID_ENUM); else GetSourceiv(Source, Context, param, values); + UnlockSourcesRead(Context); + ReadUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -2060,6 +2334,8 @@ AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64S Context = GetContextRef(); if(!Context) return; + ReadLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!value) @@ -2068,6 +2344,8 @@ AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64S alSetError(Context, AL_INVALID_ENUM); else GetSourcei64v(Source, Context, param, value); + UnlockSourcesRead(Context); + ReadUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -2080,6 +2358,8 @@ AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64 Context = GetContextRef(); if(!Context) return; + ReadLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!(value1 && value2 && value3)) @@ -2096,6 +2376,8 @@ AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64 *value3 = i64vals[2]; } } + UnlockSourcesRead(Context); + ReadUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -2108,6 +2390,8 @@ AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64 Context = GetContextRef(); if(!Context) return; + ReadLock(&Context->PropLock); + LockSourcesRead(Context); if((Source=LookupSource(Context, source)) == NULL) alSetError(Context, AL_INVALID_NAME); else if(!values) @@ -2116,6 +2400,8 @@ AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64 alSetError(Context, AL_INVALID_ENUM); else GetSourcei64v(Source, Context, param, values); + UnlockSourcesRead(Context); + ReadUnlock(&Context->PropLock); ALCcontext_DecRef(Context); } @@ -2128,12 +2414,15 @@ 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); if(!(n >= 0)) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); for(i = 0;i < n;i++) @@ -2142,35 +2431,160 @@ AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); } - LockContext(context); + device = context->Device; + ALCdevice_Lock(device); + /* If the device is disconnected, go right to stopped. */ + if(!device->Connected) + { + for(i = 0;i < n;i++) + { + source = LookupSource(context, sources[i]); + ATOMIC_STORE(&source->state, AL_STOPPED, almemory_order_relaxed); + } + ALCdevice_Unlock(device); + goto done; + } + while(n > context->MaxVoices-context->VoiceCount) { - ALvoice *temp = NULL; - ALsizei newcount; - - newcount = context->MaxVoices << 1; - if(newcount > 0) - temp = realloc(context->Voices, newcount * sizeof(context->Voices[0])); - if(!temp) + ALsizei newcount = context->MaxVoices << 1; + if(context->MaxVoices >= newcount) { - UnlockContext(context); + ALCdevice_Unlock(device); SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done); } - memset(&temp[context->MaxVoices], 0, (newcount-context->MaxVoices) * sizeof(temp[0])); - - context->Voices = temp; - context->MaxVoices = newcount; + AllocateVoices(context, newcount, device->NumAuxSends); } for(i = 0;i < n;i++) { + ALbufferlistitem *BufferList; + ALbuffer *buffer = NULL; + bool start_fading = false; + ALsizei s; + source = LookupSource(context, sources[i]); - if(context->DeferUpdates) source->new_state = AL_PLAYING; - else SetSourceState(source, context, AL_PLAYING); + WriteLock(&source->queue_lock); + /* Check that there is a queue containing at least one valid, non zero + * length Buffer. + */ + BufferList = source->queue; + while(BufferList) + { + if((buffer=BufferList->buffer) != NULL && buffer->SampleLen > 0) + break; + BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed); + } + + /* If there's nothing to play, go right to stopped. */ + if(!BufferList) + { + /* 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. + */ + ATOMIC_STORE(&source->state, AL_STOPPED, almemory_order_relaxed); + source->OffsetType = AL_NONE; + source->Offset = 0.0; + goto finish_play; + } + + voice = GetSourceVoice(source, context); + switch(GetSourceState(source, voice)) + { + 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); + goto finish_play; + + case AL_PAUSED: + assert(voice != NULL); + /* A source that's paused simply resumes. */ + ATOMIC_STORE(&voice->Playing, true, almemory_order_release); + ATOMIC_STORE(&source->state, AL_PLAYING, almemory_order_release); + goto finish_play; + + default: + break; + } + + /* Make sure this source isn't already active, and if not, look for an + * unused voice to put it in. + */ + assert(voice == NULL); + for(j = 0;j < context->VoiceCount;j++) + { + if(ATOMIC_LOAD(&context->Voices[j]->Source, almemory_order_acquire) == NULL) + { + voice = context->Voices[j]; + break; + } + } + if(voice == NULL) + voice = context->Voices[context->VoiceCount++]; + ATOMIC_STORE(&voice->Playing, false, almemory_order_release); + + ATOMIC_FLAG_TEST_AND_SET(&source->PropsClean, almemory_order_acquire); + UpdateSourceProps(source, voice, device->NumAuxSends); + + /* 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(source->OffsetType != AL_NONE) + { + ApplyOffset(source, voice); + 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; + } + + voice->NumChannels = ChannelsFromFmt(buffer->FmtChannels); + voice->SampleSize = BytesFromFmt(buffer->FmtType); + + /* 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; + memset(voice->Direct.Params, 0, sizeof(voice->Direct.Params[0])*voice->NumChannels); + for(s = 0;s < device->NumAuxSends;s++) + memset(voice->Send[s].Params, 0, sizeof(voice->Send[s].Params[0])*voice->NumChannels); + if(device->AvgSpeakerDist > 0.0f) + { + ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC / + (device->AvgSpeakerDist * device->Frequency); + for(j = 0;j < voice->NumChannels;j++) + { + NfcFilterCreate1(&voice->Direct.Params[j].NFCtrlFilter[0], 0.0f, w1); + NfcFilterCreate2(&voice->Direct.Params[j].NFCtrlFilter[1], 0.0f, w1); + NfcFilterCreate3(&voice->Direct.Params[j].NFCtrlFilter[2], 0.0f, w1); + } + } + + ATOMIC_STORE(&voice->Source, source, almemory_order_relaxed); + ATOMIC_STORE(&voice->Playing, true, almemory_order_release); + ATOMIC_STORE(&source->state, AL_PLAYING, almemory_order_release); + finish_play: + WriteUnlock(&source->queue_lock); } - UnlockContext(context); + ALCdevice_Unlock(device); done: + UnlockSourcesRead(context); ALCcontext_DecRef(context); } @@ -2181,12 +2595,15 @@ 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); if(!(n >= 0)) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); for(i = 0;i < n;i++) @@ -2195,16 +2612,26 @@ AL_API ALvoid AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); } - LockContext(context); + device = context->Device; + ALCdevice_Lock(device); for(i = 0;i < n;i++) { source = LookupSource(context, sources[i]); - if(context->DeferUpdates) source->new_state = AL_PAUSED; - else SetSourceState(source, context, AL_PAUSED); + WriteLock(&source->queue_lock); + if((voice=GetSourceVoice(source, context)) != NULL) + { + ATOMIC_STORE(&voice->Playing, false, almemory_order_release); + while((ATOMIC_LOAD(&device->MixCount, almemory_order_acquire)&1)) + althrd_yield(); + } + if(GetSourceState(source, voice) == AL_PLAYING) + ATOMIC_STORE(&source->state, AL_PAUSED, almemory_order_release); + WriteUnlock(&source->queue_lock); } - UnlockContext(context); + ALCdevice_Unlock(device); done: + UnlockSourcesRead(context); ALCcontext_DecRef(context); } @@ -2215,12 +2642,15 @@ 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); if(!(n >= 0)) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); for(i = 0;i < n;i++) @@ -2229,16 +2659,29 @@ AL_API ALvoid AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); } - 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_STOPPED); + WriteLock(&source->queue_lock); + if((voice=GetSourceVoice(source, context)) != NULL) + { + ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed); + ATOMIC_STORE(&voice->Playing, false, almemory_order_release); + while((ATOMIC_LOAD(&device->MixCount, almemory_order_acquire)&1)) + althrd_yield(); + } + if(ATOMIC_LOAD(&source->state, almemory_order_acquire) != AL_INITIAL) + ATOMIC_STORE(&source->state, AL_STOPPED, almemory_order_relaxed); + source->OffsetType = AL_NONE; + source->Offset = 0.0; + WriteUnlock(&source->queue_lock); } - UnlockContext(context); + ALCdevice_Unlock(device); done: + UnlockSourcesRead(context); ALCcontext_DecRef(context); } @@ -2249,12 +2692,15 @@ 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); if(!(n >= 0)) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); for(i = 0;i < n;i++) @@ -2263,16 +2709,29 @@ AL_API ALvoid AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); } - 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); + WriteLock(&source->queue_lock); + if((voice=GetSourceVoice(source, context)) != NULL) + { + ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed); + ATOMIC_STORE(&voice->Playing, false, almemory_order_release); + while((ATOMIC_LOAD(&device->MixCount, almemory_order_acquire)&1)) + althrd_yield(); + } + if(ATOMIC_LOAD(&source->state, almemory_order_acquire) != AL_INITIAL) + ATOMIC_STORE(&source->state, AL_INITIAL, almemory_order_relaxed); + source->OffsetType = AL_NONE; + source->Offset = 0.0; + WriteUnlock(&source->queue_lock); } - UnlockContext(context); + ALCdevice_Unlock(device); done: + UnlockSourcesRead(context); ALCcontext_DecRef(context); } @@ -2295,6 +2754,7 @@ AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALu device = context->Device; + LockSourcesRead(context); if(!(nb >= 0)) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); if((source=LookupSource(context, src)) == NULL) @@ -2309,7 +2769,7 @@ AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALu } /* Check for a valid Buffer, for its frequency and format */ - BufferList = ATOMIC_LOAD(&source->queue); + BufferList = source->queue; while(BufferList) { if(BufferList->buffer) @@ -2317,9 +2777,10 @@ AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALu BufferFmt = BufferList->buffer; break; } - BufferList = BufferList->next; + BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed); } + LockBuffersRead(device); BufferListStart = NULL; BufferList = NULL; for(i = 0;i < nb;i++) @@ -2333,20 +2794,17 @@ AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALu if(!BufferListStart) { - BufferListStart = malloc(sizeof(ALbufferlistitem)); - BufferListStart->buffer = buffer; - BufferListStart->next = NULL; - BufferListStart->prev = NULL; + BufferListStart = al_calloc(DEF_ALIGN, sizeof(ALbufferlistitem)); BufferList = BufferListStart; } else { - BufferList->next = malloc(sizeof(ALbufferlistitem)); - BufferList->next->buffer = buffer; - BufferList->next->next = NULL; - BufferList->next->prev = BufferList; - BufferList = BufferList->next; + ALbufferlistitem *item = al_calloc(DEF_ALIGN, sizeof(ALbufferlistitem)); + ATOMIC_STORE(&BufferList->next, item, almemory_order_relaxed); + BufferList = item; } + BufferList->buffer = buffer; + ATOMIC_INIT(&BufferList->next, NULL); if(!buffer) continue; /* Hold a read lock on each buffer being queued while checking all @@ -2356,12 +2814,7 @@ AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALu IncrementRef(&buffer->ref); if(BufferFmt == NULL) - { BufferFmt = buffer; - - source->NumChannels = ChannelsFromFmt(buffer->FmtChannels); - source->SampleSize = BytesFromFmt(buffer->FmtType); - } else if(BufferFmt->Frequency != buffer->Frequency || BufferFmt->OriginalChannels != buffer->OriginalChannels || BufferFmt->OriginalType != buffer->OriginalType) @@ -2372,46 +2825,48 @@ AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALu buffer_error: /* A buffer failed (invalid ID or format), so unlock and release * each buffer we had. */ - while(BufferList != NULL) + while(BufferListStart) { - ALbufferlistitem *prev = BufferList->prev; - if((buffer=BufferList->buffer) != NULL) + ALbufferlistitem *next = ATOMIC_LOAD(&BufferListStart->next, + almemory_order_relaxed); + if((buffer=BufferListStart->buffer) != NULL) { DecrementRef(&buffer->ref); ReadUnlock(&buffer->lock); } - free(BufferList); - BufferList = prev; + al_free(BufferListStart); + BufferListStart = next; } + UnlockBuffersRead(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->prev; + BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed); } + UnlockBuffersRead(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; - - BufferListStart->prev = BufferList; - 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); } - BufferList = NULL; - ATOMIC_COMPARE_EXCHANGE_STRONG(ALbufferlistitem*, &source->current_buffer, &BufferList, BufferListStart); WriteUnlock(&source->queue_lock); done: + UnlockSourcesRead(context); ALCcontext_DecRef(context); } @@ -2419,65 +2874,64 @@ AL_API ALvoid AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint { ALCcontext *context; ALsource *source; - ALbufferlistitem *NewHead; ALbufferlistitem *OldHead; + ALbufferlistitem *OldTail; ALbufferlistitem *Current; - ALsizei i; - - if(nb == 0) - return; + ALvoice *voice; + ALsizei i = 0; context = GetContextRef(); if(!context) return; + LockSourcesRead(context); if(!(nb >= 0)) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); if((source=LookupSource(context, src)) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + /* Nothing to unqueue. */ + if(nb == 0) goto done; + WriteLock(&source->queue_lock); - /* Find the new buffer queue head */ - NewHead = ATOMIC_LOAD(&source->queue); - Current = ATOMIC_LOAD(&source->current_buffer); - for(i = 0;i < nb && NewHead;i++) - { - if(NewHead == Current) - break; - NewHead = NewHead->next; - } - if(source->Looping || source->SourceType != AL_STREAMING || i != nb) + if(source->Looping || source->SourceType != AL_STREAMING) { WriteUnlock(&source->queue_lock); - /* Trying to unqueue pending buffers, or a buffer that wasn't queued. */ + /* Trying to unqueue buffers on a looping or non-streaming source. */ + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } + + /* Find the new buffer queue head */ + OldTail = source->queue; + Current = NULL; + if((voice=GetSourceVoice(source, context)) != NULL) + Current = ATOMIC_LOAD_SEQ(&voice->current_buffer); + else if(ATOMIC_LOAD_SEQ(&source->state) == AL_INITIAL) + Current = OldTail; + if(OldTail != Current) + { + for(i = 1;i < nb;i++) + { + ALbufferlistitem *next = ATOMIC_LOAD(&OldTail->next, almemory_order_relaxed); + 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); } /* Swap it, and cut the new head from the old. */ - OldHead = ATOMIC_EXCHANGE(ALbufferlistitem*, &source->queue, NewHead); - if(NewHead) - { - ALCdevice *device = context->Device; - ALbufferlistitem *OldTail = NewHead->prev; - uint count; - - /* Cut the new head's link back to the old body. The mixer is robust - * enough to handle the link back going away. Once the active mix (if - * any) is complete, it's safe to finish cutting the old tail from the - * new head. */ - NewHead->prev = NULL; - if(((count=ReadRef(&device->MixCount))&1) != 0) - { - while(count == ReadRef(&device->MixCount)) - althrd_yield(); - } - OldTail->next = NULL; - } + OldHead = source->queue; + source->queue = ATOMIC_EXCHANGE_PTR(&OldTail->next, NULL, almemory_order_acq_rel); WriteUnlock(&source->queue_lock); while(OldHead != NULL) { - ALbufferlistitem *next = OldHead->next; + ALbufferlistitem *next = ATOMIC_LOAD(&OldHead->next, almemory_order_relaxed); ALbuffer *buffer = OldHead->buffer; if(!buffer) @@ -2488,27 +2942,34 @@ AL_API ALvoid AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint DecrementRef(&buffer->ref); } - free(OldHead); + al_free(OldHead); OldHead = next; } done: + UnlockSourcesRead(context); ALCcontext_DecRef(context); } -static ALvoid InitSourceParams(ALsource *Source) +static void InitSourceParams(ALsource *Source, ALsizei num_sends) { - ALuint i; + ALsizei i; RWLockInit(&Source->queue_lock); Source->InnerAngle = 360.0f; Source->OuterAngle = 360.0f; Source->Pitch = 1.0f; - aluVectorSet(&Source->Position, 0.0f, 0.0f, 0.0f, 1.0f); - aluVectorSet(&Source->Velocity, 0.0f, 0.0f, 0.0f, 0.0f); - aluVectorSet(&Source->Direction, 0.0f, 0.0f, 0.0f, 0.0f); + Source->Position[0] = 0.0f; + Source->Position[1] = 0.0f; + Source->Position[2] = 0.0f; + Source->Velocity[0] = 0.0f; + Source->Velocity[1] = 0.0f; + Source->Velocity[2] = 0.0f; + Source->Direction[0] = 0.0f; + Source->Direction[1] = 0.0f; + Source->Direction[2] = 0.0f; Source->Orientation[0][0] = 0.0f; Source->Orientation[0][1] = 0.0f; Source->Orientation[0][2] = -1.0f; @@ -2517,8 +2978,7 @@ static ALvoid InitSourceParams(ALsource *Source) Source->Orientation[1][2] = 0.0f; Source->RefDistance = 1.0f; Source->MaxDistance = FLT_MAX; - Source->RollOffFactor = 1.0f; - Source->Looping = AL_FALSE; + Source->RolloffFactor = 1.0f; Source->Gain = 1.0f; Source->MinGain = 0.0f; Source->MaxGain = 1.0f; @@ -2531,27 +2991,27 @@ static ALvoid 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->state = AL_INITIAL; - Source->new_state = AL_NONE; - Source->SourceType = AL_UNDETERMINED; - Source->Offset = -1.0; - - ATOMIC_INIT(&Source->queue, NULL); - ATOMIC_INIT(&Source->current_buffer, NULL); - Source->Direct.Gain = 1.0f; Source->Direct.GainHF = 1.0f; Source->Direct.HFReference = LOWPASSFREQREF; Source->Direct.GainLF = 1.0f; Source->Direct.LFReference = HIGHPASSFREQREF; - for(i = 0;i < MAX_SENDS;i++) + Source->Send = 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; @@ -2559,179 +3019,200 @@ static ALvoid InitSourceParams(ALsource *Source) Source->Send[i].LFReference = HIGHPASSFREQREF; } - ATOMIC_INIT(&Source->NeedsUpdate, AL_TRUE); + Source->Offset = 0.0; + Source->OffsetType = AL_NONE; + Source->SourceType = AL_UNDETERMINED; + ATOMIC_INIT(&Source->state, AL_INITIAL); + + Source->queue = NULL; + + /* 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); } - -/* SetSourceState - * - * Sets the source's new play state given its current state. - */ -ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state) +static void DeinitSource(ALsource *source, ALsizei num_sends) { - WriteLock(&Source->queue_lock); - if(state == AL_PLAYING) + ALbufferlistitem *BufferList; + ALsizei i; + + BufferList = source->queue; + while(BufferList != NULL) { - 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; - Source->position = 0; - Source->position_fraction = 0; - ATOMIC_STORE(&Source->current_buffer, BufferList); - discontinuity = AL_TRUE; - } - else - { - Source->state = AL_PLAYING; - discontinuity = AL_FALSE; - } - - // Check if an Offset has been set - if(Source->Offset >= 0.0) - { - 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; - } - - /* Clear previous samples if playback is discontinuous. */ - if(discontinuity) - memset(voice->PrevSamples, 0, sizeof(voice->PrevSamples)); - - voice->Direct.Moving = AL_FALSE; - voice->Direct.Counter = 0; - for(i = 0;i < MAX_INPUT_CHANNELS;i++) - { - ALsizei j; - for(j = 0;j < HRTF_HISTORY_LENGTH;j++) - voice->Direct.Hrtf[i].State.History[j] = 0.0f; - for(j = 0;j < HRIR_LENGTH;j++) - { - voice->Direct.Hrtf[i].State.Values[j][0] = 0.0f; - voice->Direct.Hrtf[i].State.Values[j][1] = 0.0f; - } - } - for(i = 0;i < (ALsizei)device->NumAuxSends;i++) - { - voice->Send[i].Moving = AL_FALSE; - voice->Send[i].Counter = 0; - } - - if(BufferList->buffer->FmtChannels == FmtMono) - voice->Update = CalcSourceParams; - else - voice->Update = CalcNonAttnSourceParams; - - ATOMIC_STORE(&Source->NeedsUpdate, AL_TRUE); + ALbufferlistitem *next = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed); + if(BufferList->buffer != NULL) + DecrementRef(&BufferList->buffer->ref); + al_free(BufferList); + BufferList = next; } - else if(state == AL_PAUSED) + source->queue = NULL; + + if(source->Send) { - if(Source->state == AL_PLAYING) - Source->state = AL_PAUSED; - } - else if(state == AL_STOPPED) - { - do_stop: - if(Source->state != AL_INITIAL) + for(i = 0;i < num_sends;i++) { - Source->state = AL_STOPPED; - ATOMIC_STORE(&Source->current_buffer, NULL); + if(source->Send[i].Slot) + DecrementRef(&source->Send[i].Slot->ref); + source->Send[i].Slot = NULL; } - Source->Offset = -1.0; + al_free(source->Send); + source->Send = NULL; } - else if(state == AL_INITIAL) - { - if(Source->state != AL_INITIAL) - { - Source->state = AL_INITIAL; - Source->position = 0; - Source->position_fraction = 0; - ATOMIC_STORE(&Source->current_buffer, ATOMIC_LOAD(&Source->queue)); - } - Source->Offset = -1.0; - } - WriteUnlock(&Source->queue_lock); } +static void UpdateSourceProps(ALsource *source, ALvoice *voice, ALsizei num_sends) +{ + struct ALvoiceProps *props; + ALsizei i; + + /* Get an unused property container, or allocate a new one as needed. */ + props = ATOMIC_LOAD(&voice->FreeList, almemory_order_acquire); + if(!props) + props = al_calloc(16, FAM_SIZE(struct ALvoiceProps, Send, num_sends)); + else + { + struct ALvoiceProps *next; + do { + next = ATOMIC_LOAD(&props->next, almemory_order_relaxed); + } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&voice->FreeList, &props, next, + almemory_order_acq_rel, almemory_order_acquire) == 0); + } + + /* Copy in current property values. */ + 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++) + props->Position[i] = source->Position[i]; + for(i = 0;i < 3;i++) + props->Velocity[i] = source->Velocity[i]; + for(i = 0;i < 3;i++) + props->Direction[i] = source->Direction[i]; + for(i = 0;i < 2;i++) + { + ALsizei j; + for(j = 0;j < 3;j++) + props->Orientation[i][j] = source->Orientation[i][j]; + } + props->HeadRelative = source->HeadRelative; + props->DistanceModel = source->DistanceModel; + props->Resampler = source->Resampler; + props->DirectChannels = source->DirectChannels; + props->SpatializeMode = source->Spatialize; + + props->DryGainHFAuto = source->DryGainHFAuto; + props->WetGainAuto = source->WetGainAuto; + props->WetGainHFAuto = source->WetGainHFAuto; + props->OuterGainHF = source->OuterGainHF; + + props->AirAbsorptionFactor = source->AirAbsorptionFactor; + props->RoomRolloffFactor = source->RoomRolloffFactor; + props->DopplerFactor = source->DopplerFactor; + + props->StereoPan[0] = source->StereoPan[0]; + props->StereoPan[1] = source->StereoPan[1]; + + props->Radius = source->Radius; + + 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++) + { + 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_PTR(&voice->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 ALvoiceProps*, &voice->FreeList, props); + } +} + +void UpdateAllSourceProps(ALCcontext *context) +{ + ALsizei num_sends = context->Device->NumAuxSends; + ALsizei pos; + + for(pos = 0;pos < context->VoiceCount;pos++) + { + 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); + } +} + + /* 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). */ -ALint64 GetSourceSampleOffset(ALsource *Source) +static ALint64 GetSourceSampleOffset(ALsource *Source, ALCcontext *context, ALuint64 *clocktime) { - const ALbufferlistitem *BufferList; + ALCdevice *device = context->Device; const ALbufferlistitem *Current; ALuint64 readPos; + ALuint refcount; + ALvoice *voice; ReadLock(&Source->queue_lock); - if(Source->state != AL_PLAYING && Source->state != AL_PAUSED) - { - ReadUnlock(&Source->queue_lock); - return 0; - } + do { + Current = NULL; + readPos = 0; + while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1)) + althrd_yield(); + *clocktime = GetDeviceClockTime(device); - /* NOTE: This is the offset into the *current* buffer, so add the length of - * any played buffers */ - readPos = (ALuint64)Source->position << 32; - readPos |= (ALuint64)Source->position_fraction << (32-FRACTIONBITS); - BufferList = ATOMIC_LOAD(&Source->queue); - Current = ATOMIC_LOAD(&Source->current_buffer); - while(BufferList && BufferList != Current) + voice = GetSourceVoice(Source, context); + if(voice) + { + Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed); + + 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) + { + if(BufferList->buffer) + readPos += (ALuint64)BufferList->buffer->SampleLen << 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 @@ -2739,174 +3220,175 @@ ALint64 GetSourceSampleOffset(ALsource *Source) * 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) +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; + ALdouble offset; + ALvoice *voice; ReadLock(&Source->queue_lock); - if(Source->state != AL_PLAYING && Source->state != AL_PAUSED) - { - ReadUnlock(&Source->queue_lock); - return 0.0; - } + do { + Current = NULL; + readPos = 0; + while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1)) + althrd_yield(); + *clocktime = GetDeviceClockTime(device); - /* NOTE: This is the offset into the *current* buffer, so add the length of - * any played buffers */ - readPos = (ALuint64)Source->position << FRACTIONBITS; - readPos |= (ALuint64)Source->position_fraction; - BufferList = ATOMIC_LOAD(&Source->queue); - Current = ATOMIC_LOAD(&Source->current_buffer); - while(BufferList && BufferList != Current) - { - const ALbuffer *buffer = BufferList->buffer; - if(buffer != NULL) + voice = GetSourceVoice(Source, context); + if(voice) { - if(!Buffer) Buffer = buffer; - readPos += (ALuint64)buffer->SampleLen << FRACTIONBITS; - } - BufferList = BufferList->next; - } + Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed); - while(BufferList && !Buffer) + readPos = (ALuint64)ATOMIC_LOAD(&voice->position, almemory_order_relaxed) << + FRACTIONBITS; + readPos |= ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed); + } + ATOMIC_THREAD_FENCE(almemory_order_acquire); + } while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_order_relaxed)); + + offset = 0.0; + if(voice) { - Buffer = BufferList->buffer; - BufferList = BufferList->next; + const ALbufferlistitem *BufferList = Source->queue; + const ALbuffer *BufferFmt = NULL; + while(BufferList && BufferList != Current) + { + const ALbuffer *buffer = BufferList->buffer; + if(buffer != NULL) + { + if(!BufferFmt) BufferFmt = buffer; + readPos += (ALuint64)buffer->SampleLen << FRACTIONBITS; + } + BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next, + almemory_order_relaxed); + } + + while(BufferList && !BufferFmt) + { + BufferFmt = BufferList->buffer; + BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next, + almemory_order_relaxed); + } + assert(BufferFmt != NULL); + + offset = (ALdouble)readPos / (ALdouble)FRACTIONONE / + (ALdouble)BufferFmt->Frequency; } - assert(Buffer != NULL); ReadUnlock(&Source->queue_lock); - return (ALdouble)readPos / (ALdouble)FRACTIONONE / (ALdouble)Buffer->Frequency; + return offset; } -/* GetSourceOffsets +/* GetSourceOffset * - * Gets the current read and write offsets for the given Source, in the - * appropriate format (Bytes, Samples or Seconds). The offsets are relative to - * the start of the queue (not the start of the current buffer). + * Gets the current read offset for the given Source, in the appropriate format + * (Bytes, Samples or Seconds). The offset is relative to the start of the + * queue (not the start of the current buffer). */ -static ALvoid GetSourceOffsets(ALsource *Source, ALenum name, ALdouble *offset, ALdouble updateLen) +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, writePos; - ALuint totalBufferLen; + ALuint readPos; + ALsizei readPosFrac; + ALuint refcount; + ALdouble offset; + ALvoice *voice; ReadLock(&Source->queue_lock); - if(Source->state != AL_PLAYING && Source->state != AL_PAUSED) - { - offset[0] = 0.0; - offset[1] = 0.0; - ReadUnlock(&Source->queue_lock); - return; - } - - if(updateLen > 0.0 && updateLen < 0.015) - updateLen = 0.015; - - /* NOTE: This is the offset into the *current* buffer, so add the length of - * any played buffers */ - totalBufferLen = 0; - readPos = Source->position; - readPosFrac = Source->position_fraction; - BufferList = ATOMIC_LOAD(&Source->queue); - Current = ATOMIC_LOAD(&Source->current_buffer); - while(BufferList != NULL) - { - const ALbuffer *buffer; - readFin = readFin || (BufferList == Current); - if((buffer=BufferList->buffer) != NULL) + do { + Current = NULL; + readPos = readPosFrac = 0; + while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1)) + althrd_yield(); + 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(Source->state == AL_PLAYING) - writePos = readPos + (ALuint)(updateLen*Buffer->Frequency + 0.5f); - else - writePos = readPos; - - if(Source->Looping) + offset = 0.0; + if(voice) { - readPos %= totalBufferLen; - writePos %= totalBufferLen; - } - else - { - /* Wrap positions back to 0 */ - if(readPos >= totalBufferLen) - readPos = readPosFrac = 0; - if(writePos >= totalBufferLen) - writePos = 0; - } + const ALbufferlistitem *BufferList = Source->queue; + const ALbuffer *BufferFmt = NULL; + ALboolean readFin = AL_FALSE; + ALuint totalBufferLen = 0; - switch(name) - { - case AL_SEC_OFFSET: - offset[0] = (readPos + (ALdouble)readPosFrac/FRACTIONONE)/Buffer->Frequency; - offset[1] = (ALdouble)writePos/Buffer->Frequency; - break; - - case AL_SAMPLE_OFFSET: - case AL_SAMPLE_RW_OFFSETS_SOFT: - offset[0] = readPos + (ALdouble)readPosFrac/FRACTIONONE; - offset[1] = (ALdouble)writePos; - break; - - case AL_BYTE_OFFSET: - case AL_BYTE_RW_OFFSETS_SOFT: - if(Buffer->OriginalType == UserFmtIMA4) + while(BufferList != NULL) + { + const ALbuffer *buffer; + readFin = readFin || (BufferList == Current); + if((buffer=BufferList->buffer) != NULL) { - ALsizei align = (Buffer->OriginalAlign-1)/2 + 4; - ALuint BlockSize = align * ChannelsFromFmt(Buffer->FmtChannels); - ALuint FrameBlockSize = Buffer->OriginalAlign; + if(!BufferFmt) BufferFmt = buffer; + totalBufferLen += buffer->SampleLen; + if(!readFin) readPos += buffer->SampleLen; + } + BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next, + almemory_order_relaxed); + } + assert(BufferFmt != NULL); - /* Round down to nearest ADPCM block */ - offset[0] = (ALdouble)(readPos / FrameBlockSize * BlockSize); - if(Source->state != AL_PLAYING) - offset[1] = offset[0]; + 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 { - /* Round up to nearest ADPCM block */ - offset[1] = (ALdouble)((writePos+FrameBlockSize-1) / - FrameBlockSize * BlockSize); + ALuint FrameSize = FrameSizeFromUserFmt(BufferFmt->OriginalChannels, + BufferFmt->OriginalType); + offset = (ALdouble)(readPos * FrameSize); } - } - else if(Buffer->OriginalType == UserFmtMSADPCM) - { - ALsizei align = (Buffer->OriginalAlign-2)/2 + 7; - ALuint BlockSize = align * ChannelsFromFmt(Buffer->FmtChannels); - ALuint FrameBlockSize = Buffer->OriginalAlign; - - /* Round down to nearest ADPCM block */ - offset[0] = (ALdouble)(readPos / FrameBlockSize * BlockSize); - if(Source->state != AL_PLAYING) - offset[1] = offset[0]; - else - { - /* Round up to nearest ADPCM block */ - offset[1] = (ALdouble)((writePos+FrameBlockSize-1) / - FrameBlockSize * BlockSize); - } - } - else - { - ALuint FrameSize = FrameSizeFromUserFmt(Buffer->OriginalChannels, Buffer->OriginalType); - offset[0] = (ALdouble)(readPos * FrameSize); - offset[1] = (ALdouble)(writePos * FrameSize); - } - break; + break; + } } ReadUnlock(&Source->queue_lock); + return offset; } @@ -2915,19 +3397,20 @@ static ALvoid GetSourceOffsets(ALsource *Source, ALenum name, ALdouble *offset, * 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 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; @@ -2936,16 +3419,15 @@ ALboolean ApplyOffset(ALsource *Source) if(bufferLen > offset-totalBufferLen) { /* Offset is in this buffer */ - ATOMIC_STORE(&Source->current_buffer, BufferList); - - Source->position = offset - totalBufferLen; - 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 += bufferLen; - BufferList = BufferList->next; + BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed); } /* Offset is out of range of the queue */ @@ -2959,26 +3441,25 @@ 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; + if((BufferFmt=BufferList->buffer) != NULL) break; - } - BufferList = BufferList->next; + BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next, + almemory_order_relaxed); } - if(!Buffer) + if(!BufferFmt) { - Source->Offset = -1.0; + Source->OffsetType = AL_NONE; + Source->Offset = 0.0; return AL_FALSE; } @@ -2987,36 +3468,38 @@ 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 * ChannelsFromUserFmt(BufferFmt->OriginalChannels); + *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 * ChannelsFromUserFmt(BufferFmt->OriginalChannels); + *offset *= BufferFmt->OriginalAlign; } else - *offset /= FrameSizeFromUserFmt(Buffer->OriginalChannels, Buffer->OriginalType); + *offset /= FrameSizeFromUserFmt(BufferFmt->OriginalChannels, + BufferFmt->OriginalType); *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->Offset = -1.0; + Source->OffsetType = AL_NONE; + Source->Offset = 0.0; return AL_TRUE; } @@ -3028,30 +3511,14 @@ static ALboolean GetSampleOffset(ALsource *Source, ALuint *offset, ALuint *frac) */ ALvoid ReleaseALSources(ALCcontext *Context) { - ALbufferlistitem *item; + ALCdevice *device = Context->Device; ALsizei pos; - ALuint j; for(pos = 0;pos < Context->SourceMap.size;pos++) { - ALsource *temp = Context->SourceMap.array[pos].value; - Context->SourceMap.array[pos].value = NULL; + ALsource *temp = Context->SourceMap.values[pos]; + Context->SourceMap.values[pos] = NULL; - item = ATOMIC_EXCHANGE(ALbufferlistitem*, &temp->queue, NULL); - while(item != NULL) - { - ALbufferlistitem *next = item->next; - if(item->buffer != NULL) - DecrementRef(&item->buffer->ref); - free(item); - item = next; - } - - for(j = 0;j < MAX_SENDS;++j) - { - if(temp->Send[j].Slot) - DecrementRef(&temp->Send[j].Slot->ref); - temp->Send[j].Slot = NULL; - } + DeinitSource(temp, device->NumAuxSends); FreeThunkEntry(temp->id); memset(temp, 0, sizeof(*temp)); diff --git a/libs/openal-soft/OpenAL32/alState.c b/libs/openal-soft/OpenAL32/alState.c index dca41363..a7dc1006 100644 --- a/libs/openal-soft/OpenAL32/alState.c +++ b/libs/openal-soft/OpenAL32/alState.c @@ -20,12 +20,15 @@ #include "config.h" +#include "version.h" + #include #include "alMain.h" #include "AL/alc.h" #include "AL/al.h" #include "AL/alext.h" #include "alError.h" +#include "alListener.h" #include "alSource.h" #include "alAuxEffectSlot.h" @@ -44,6 +47,12 @@ 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 alSinc4Resampler[] = "4-Point Sinc"; +static const ALchar alBSincResampler[] = "Band-limited Sinc (12/24)"; + AL_API ALvoid AL_APIENTRY alEnable(ALenum capability) { ALCcontext *context; @@ -51,18 +60,21 @@ AL_API ALvoid AL_APIENTRY alEnable(ALenum capability) context = GetContextRef(); if(!context) return; + WriteLock(&context->PropLock); switch(capability) { case AL_SOURCE_DISTANCE_MODEL: context->SourceDistanceModel = AL_TRUE; - ATOMIC_STORE(&context->UpdateSources, AL_TRUE); break; default: SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); } + if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) + UpdateListenerProps(context); done: + WriteUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -73,18 +85,21 @@ AL_API ALvoid AL_APIENTRY alDisable(ALenum capability) context = GetContextRef(); if(!context) return; + WriteLock(&context->PropLock); switch(capability) { case AL_SOURCE_DISTANCE_MODEL: context->SourceDistanceModel = AL_FALSE; - ATOMIC_STORE(&context->UpdateSources, AL_TRUE); break; default: SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); } + if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) + UpdateListenerProps(context); done: + WriteUnlock(&context->PropLock); ALCcontext_DecRef(context); } @@ -143,7 +158,22 @@ AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum pname) break; case AL_DEFERRED_UPDATES_SOFT: - value = context->DeferUpdates; + if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) + value = AL_TRUE; + break; + + case AL_GAIN_LIMIT_SOFT: + if(GAIN_MIX_MAX/context->GainBoost != 0.0f) + 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: @@ -183,7 +213,20 @@ AL_API ALdouble AL_APIENTRY alGetDouble(ALenum pname) break; case AL_DEFERRED_UPDATES_SOFT: - value = (ALdouble)context->DeferUpdates; + if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) + value = (ALdouble)AL_TRUE; + break; + + case AL_GAIN_LIMIT_SOFT: + 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: @@ -223,7 +266,20 @@ AL_API ALfloat AL_APIENTRY alGetFloat(ALenum pname) break; case AL_DEFERRED_UPDATES_SOFT: - value = (ALfloat)context->DeferUpdates; + if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) + value = (ALfloat)AL_TRUE; + break; + + case AL_GAIN_LIMIT_SOFT: + 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: @@ -263,7 +319,20 @@ AL_API ALint AL_APIENTRY alGetInteger(ALenum pname) break; case AL_DEFERRED_UPDATES_SOFT: - value = (ALint)context->DeferUpdates; + if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) + value = (ALint)AL_TRUE; + break; + + case AL_GAIN_LIMIT_SOFT: + 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: @@ -303,7 +372,20 @@ AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname) break; case AL_DEFERRED_UPDATES_SOFT: - value = (ALint64SOFT)context->DeferUpdates; + if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) + value = (ALint64SOFT)AL_TRUE; + break; + + case AL_GAIN_LIMIT_SOFT: + 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: @@ -329,6 +411,9 @@ AL_API ALvoid AL_APIENTRY alGetBooleanv(ALenum pname, ALboolean *values) case AL_DISTANCE_MODEL: 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; } @@ -362,6 +447,9 @@ AL_API ALvoid AL_APIENTRY alGetDoublev(ALenum pname, ALdouble *values) case AL_DISTANCE_MODEL: 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; } @@ -395,6 +483,9 @@ AL_API ALvoid AL_APIENTRY alGetFloatv(ALenum pname, ALfloat *values) case AL_DISTANCE_MODEL: 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; } @@ -428,6 +519,9 @@ AL_API ALvoid AL_APIENTRY alGetIntegerv(ALenum pname, ALint *values) case AL_DISTANCE_MODEL: 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; } @@ -459,6 +553,9 @@ AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values) case AL_DISTANCE_MODEL: 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; } @@ -547,8 +644,11 @@ AL_API ALvoid AL_APIENTRY alDopplerFactor(ALfloat value) if(!(value >= 0.0f && isfinite(value))) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + WriteLock(&context->PropLock); context->DopplerFactor = value; - ATOMIC_STORE(&context->UpdateSources, AL_TRUE); + if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) + UpdateListenerProps(context); + WriteUnlock(&context->PropLock); done: ALCcontext_DecRef(context); @@ -564,8 +664,11 @@ AL_API ALvoid AL_APIENTRY alDopplerVelocity(ALfloat value) if(!(value >= 0.0f && isfinite(value))) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + WriteLock(&context->PropLock); context->DopplerVelocity = value; - ATOMIC_STORE(&context->UpdateSources, AL_TRUE); + if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) + UpdateListenerProps(context); + WriteUnlock(&context->PropLock); done: ALCcontext_DecRef(context); @@ -581,8 +684,11 @@ AL_API ALvoid AL_APIENTRY alSpeedOfSound(ALfloat value) if(!(value > 0.0f && isfinite(value))) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + WriteLock(&context->PropLock); context->SpeedOfSound = value; - ATOMIC_STORE(&context->UpdateSources, AL_TRUE); + if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) + UpdateListenerProps(context); + WriteUnlock(&context->PropLock); done: ALCcontext_DecRef(context); @@ -601,9 +707,14 @@ AL_API ALvoid AL_APIENTRY alDistanceModel(ALenum value) value == AL_NONE)) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + WriteLock(&context->PropLock); context->DistanceModel = value; if(!context->SourceDistanceModel) - ATOMIC_STORE(&context->UpdateSources, AL_TRUE); + { + if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) + UpdateListenerProps(context); + } + WriteUnlock(&context->PropLock); done: ALCcontext_DecRef(context); @@ -633,3 +744,36 @@ 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, + alSinc4Resampler, alBSincResampler, + }; + 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)) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + value = ResamplerNames[index]; + break; + + default: + SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); + } + +done: + ALCcontext_DecRef(context); + + return value; +} diff --git a/libs/openal-soft/OpenAL32/alThunk.c b/libs/openal-soft/OpenAL32/alThunk.c index 0cebad42..0d90760a 100644 --- a/libs/openal-soft/OpenAL32/alThunk.c +++ b/libs/openal-soft/OpenAL32/alThunk.c @@ -25,15 +25,17 @@ #include "alMain.h" #include "alThunk.h" +#include "almalloc.h" -static ATOMIC(ALenum) *ThunkArray; -static ALuint ThunkArraySize; + +static ATOMIC_FLAG *ThunkArray; +static ALsizei ThunkArraySize; static RWLock ThunkLock; void ThunkInit(void) { RWLockInit(&ThunkLock); - ThunkArraySize = 1; + ThunkArraySize = 1024; ThunkArray = al_calloc(16, ThunkArraySize * sizeof(*ThunkArray)); } @@ -47,12 +49,12 @@ void ThunkExit(void) ALenum NewThunkEntry(ALuint *index) { void *NewList; - ALuint i; + ALsizei i; ReadLock(&ThunkLock); for(i = 0;i < ThunkArraySize;i++) { - if(ATOMIC_EXCHANGE(ALenum, &ThunkArray[i], AL_TRUE) == AL_FALSE) + if(!ATOMIC_FLAG_TEST_AND_SET(&ThunkArray[i], almemory_order_acq_rel)) { ReadUnlock(&ThunkLock); *index = i+1; @@ -67,7 +69,7 @@ ALenum NewThunkEntry(ALuint *index) */ for(;i < ThunkArraySize;i++) { - if(ATOMIC_EXCHANGE(ALenum, &ThunkArray[i], AL_TRUE) == AL_FALSE) + if(!ATOMIC_FLAG_TEST_AND_SET(&ThunkArray[i], almemory_order_acq_rel)) { WriteUnlock(&ThunkLock); *index = i+1; @@ -87,17 +89,20 @@ ALenum NewThunkEntry(ALuint *index) ThunkArray = NewList; ThunkArraySize *= 2; - ATOMIC_STORE(&ThunkArray[i], AL_TRUE); + ATOMIC_FLAG_TEST_AND_SET(&ThunkArray[i], almemory_order_seq_cst); + *index = ++i; + + for(;i < ThunkArraySize;i++) + ATOMIC_FLAG_CLEAR(&ThunkArray[i], almemory_order_relaxed); 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); + if(index > 0 && (ALsizei)index <= ThunkArraySize) + ATOMIC_FLAG_CLEAR(&ThunkArray[index-1], almemory_order_release); ReadUnlock(&ThunkLock); } diff --git a/libs/openal-soft/OpenAL32/sample_cvt.c b/libs/openal-soft/OpenAL32/sample_cvt.c index a02b217e..89c2a408 100644 --- a/libs/openal-soft/OpenAL32/sample_cvt.c +++ b/libs/openal-soft/OpenAL32/sample_cvt.c @@ -174,14 +174,6 @@ 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]; } @@ -498,320 +490,128 @@ static void EncodeMSADPCMBlock(ALmsadpcm *dst, const ALshort *src, ALint *sample } -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]); -} +/* 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; } -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; - } -} +DECL_TEMPLATE(ALbyte); +DECL_TEMPLATE(ALubyte); +DECL_TEMPLATE(ALshort); +DECL_TEMPLATE(ALushort); +DECL_TEMPLATE(ALint); +DECL_TEMPLATE(ALuint); +DECL_TEMPLATE(ALalaw); +DECL_TEMPLATE(ALmulaw); -static inline ALint DecodeUByte3(ALubyte3 val) -{ - if(IS_LITTLE_ENDIAN) - return (val.b[2]<<16) | (val.b[1]<<8) | (val.b[0]); - return (val.b[0]<<16) | (val.b[1]<<8) | val.b[2]; -} - -static inline ALubyte3 EncodeUByte3(ALint val) -{ - if(IS_LITTLE_ENDIAN) - { - ALubyte3 ret = {{ val, val>>8, val>>16 }}; - return ret; - } - else - { - ALubyte3 ret = {{ val>>16, val>>8, val }}; - return ret; - } -} - - -static inline ALbyte Conv_ALbyte_ALbyte(ALbyte val) -{ return val; } -static inline ALbyte Conv_ALbyte_ALubyte(ALubyte val) -{ return val-128; } -static inline ALbyte Conv_ALbyte_ALshort(ALshort val) -{ return val>>8; } -static inline ALbyte Conv_ALbyte_ALushort(ALushort val) -{ return (val>>8)-128; } -static inline ALbyte Conv_ALbyte_ALint(ALint val) -{ return val>>24; } -static inline ALbyte Conv_ALbyte_ALuint(ALuint val) -{ return (val>>24)-128; } -static inline ALbyte Conv_ALbyte_ALfloat(ALfloat val) -{ - if(val > 1.0f) return 127; - if(val < -1.0f) return -128; - return (ALint)(val * 127.0f); -} -static inline ALbyte Conv_ALbyte_ALdouble(ALdouble val) -{ - if(val > 1.0) return 127; - if(val < -1.0) return -128; - return (ALint)(val * 127.0); -} -static inline ALbyte Conv_ALbyte_ALmulaw(ALmulaw val) -{ return Conv_ALbyte_ALshort(DecodeMuLaw(val)); } -static inline ALbyte Conv_ALbyte_ALalaw(ALalaw val) -{ return Conv_ALbyte_ALshort(DecodeALaw(val)); } -static inline ALbyte Conv_ALbyte_ALbyte3(ALbyte3 val) -{ return DecodeByte3(val)>>16; } -static inline ALbyte Conv_ALbyte_ALubyte3(ALubyte3 val) -{ return (DecodeUByte3(val)>>16)-128; } - -static inline ALubyte Conv_ALubyte_ALbyte(ALbyte val) -{ return val+128; } -static inline ALubyte Conv_ALubyte_ALubyte(ALubyte val) -{ return val; } -static inline ALubyte Conv_ALubyte_ALshort(ALshort val) -{ return (val>>8)+128; } -static inline ALubyte Conv_ALubyte_ALushort(ALushort val) -{ return val>>8; } -static inline ALubyte Conv_ALubyte_ALint(ALint val) -{ return (val>>24)+128; } -static inline ALubyte Conv_ALubyte_ALuint(ALuint val) -{ return val>>24; } -static inline ALubyte Conv_ALubyte_ALfloat(ALfloat val) -{ - if(val > 1.0f) return 255; - if(val < -1.0f) return 0; - return (ALint)(val * 127.0f) + 128; -} -static inline ALubyte Conv_ALubyte_ALdouble(ALdouble val) -{ - if(val > 1.0) return 255; - if(val < -1.0) return 0; - return (ALint)(val * 127.0) + 128; -} -static inline ALubyte Conv_ALubyte_ALmulaw(ALmulaw val) -{ return Conv_ALubyte_ALshort(DecodeMuLaw(val)); } -static inline ALubyte Conv_ALubyte_ALalaw(ALalaw val) -{ return Conv_ALubyte_ALshort(DecodeALaw(val)); } -static inline ALubyte Conv_ALubyte_ALbyte3(ALbyte3 val) -{ return (DecodeByte3(val)>>16)+128; } -static inline ALubyte Conv_ALubyte_ALubyte3(ALubyte3 val) -{ return DecodeUByte3(val)>>16; } - -static inline ALshort Conv_ALshort_ALbyte(ALbyte val) -{ return val<<8; } -static inline ALshort Conv_ALshort_ALubyte(ALubyte val) -{ return (val-128)<<8; } -static inline ALshort Conv_ALshort_ALshort(ALshort val) -{ return val; } -static inline ALshort Conv_ALshort_ALushort(ALushort val) -{ return val-32768; } -static inline ALshort Conv_ALshort_ALint(ALint val) -{ return val>>16; } -static inline ALshort Conv_ALshort_ALuint(ALuint val) -{ return (val>>16)-32768; } -static inline ALshort Conv_ALshort_ALfloat(ALfloat val) -{ - if(val > 1.0f) return 32767; - if(val < -1.0f) return -32768; - return (ALint)(val * 32767.0f); -} -static inline ALshort Conv_ALshort_ALdouble(ALdouble val) -{ - if(val > 1.0) return 32767; - if(val < -1.0) return -32768; - return (ALint)(val * 32767.0); -} -static inline ALshort Conv_ALshort_ALmulaw(ALmulaw val) -{ return Conv_ALshort_ALshort(DecodeMuLaw(val)); } -static inline ALshort Conv_ALshort_ALalaw(ALalaw val) -{ return Conv_ALshort_ALshort(DecodeALaw(val)); } -static inline ALshort Conv_ALshort_ALbyte3(ALbyte3 val) -{ return DecodeByte3(val)>>8; } -static inline ALshort Conv_ALshort_ALubyte3(ALubyte3 val) -{ return (DecodeUByte3(val)>>8)-32768; } - -static inline ALushort Conv_ALushort_ALbyte(ALbyte val) -{ return (val+128)<<8; } -static inline ALushort Conv_ALushort_ALubyte(ALubyte val) -{ return val<<8; } -static inline ALushort Conv_ALushort_ALshort(ALshort val) -{ return val+32768; } -static inline ALushort Conv_ALushort_ALushort(ALushort val) -{ return val; } -static inline ALushort Conv_ALushort_ALint(ALint val) -{ return (val>>16)+32768; } -static inline ALushort Conv_ALushort_ALuint(ALuint val) -{ return val>>16; } -static inline ALushort Conv_ALushort_ALfloat(ALfloat val) -{ - if(val > 1.0f) return 65535; - if(val < -1.0f) return 0; - return (ALint)(val * 32767.0f) + 32768; -} -static inline ALushort Conv_ALushort_ALdouble(ALdouble val) -{ - if(val > 1.0) return 65535; - if(val < -1.0) return 0; - return (ALint)(val * 32767.0) + 32768; -} -static inline ALushort Conv_ALushort_ALmulaw(ALmulaw val) -{ return Conv_ALushort_ALshort(DecodeMuLaw(val)); } -static inline ALushort Conv_ALushort_ALalaw(ALalaw val) -{ return Conv_ALushort_ALshort(DecodeALaw(val)); } -static inline ALushort Conv_ALushort_ALbyte3(ALbyte3 val) -{ return (DecodeByte3(val)>>8)+32768; } -static inline ALushort Conv_ALushort_ALubyte3(ALubyte3 val) -{ return DecodeUByte3(val)>>8; } - -static inline ALint Conv_ALint_ALbyte(ALbyte val) -{ return val<<24; } -static inline ALint Conv_ALint_ALubyte(ALubyte val) -{ return (val-128)<<24; } -static inline ALint Conv_ALint_ALshort(ALshort val) -{ return val<<16; } -static inline ALint Conv_ALint_ALushort(ALushort val) -{ return (val-32768)<<16; } -static inline ALint Conv_ALint_ALint(ALint val) -{ return val; } -static inline ALint Conv_ALint_ALuint(ALuint val) -{ return val-2147483648u; } -static inline ALint Conv_ALint_ALfloat(ALfloat val) -{ - if(val > 1.0f) return 2147483647; - if(val < -1.0f) return -2147483647-1; - return (ALint)(val*16777215.0f) << 7; -} -static inline ALint Conv_ALint_ALdouble(ALdouble val) -{ - if(val > 1.0) return 2147483647; - if(val < -1.0) return -2147483647-1; - return (ALint)(val * 2147483647.0); -} -static inline ALint Conv_ALint_ALmulaw(ALmulaw val) -{ return Conv_ALint_ALshort(DecodeMuLaw(val)); } -static inline ALint Conv_ALint_ALalaw(ALalaw val) -{ return Conv_ALint_ALshort(DecodeALaw(val)); } -static inline ALint Conv_ALint_ALbyte3(ALbyte3 val) -{ return DecodeByte3(val)<<8; } -static inline ALint Conv_ALint_ALubyte3(ALubyte3 val) -{ return (DecodeUByte3(val)-8388608)<<8; } - -static inline ALuint Conv_ALuint_ALbyte(ALbyte val) -{ return (val+128)<<24; } -static inline ALuint Conv_ALuint_ALubyte(ALubyte val) -{ return val<<24; } -static inline ALuint Conv_ALuint_ALshort(ALshort val) -{ return (val+32768)<<16; } -static inline ALuint Conv_ALuint_ALushort(ALushort val) -{ return val<<16; } -static inline ALuint Conv_ALuint_ALint(ALint val) -{ return val+2147483648u; } -static inline ALuint Conv_ALuint_ALuint(ALuint val) -{ return val; } -static inline ALuint Conv_ALuint_ALfloat(ALfloat val) -{ - if(val > 1.0f) return 4294967295u; - if(val < -1.0f) return 0; - return ((ALint)(val*16777215.0f)<<7) + 2147483648u; -} -static inline ALuint Conv_ALuint_ALdouble(ALdouble val) -{ - if(val > 1.0) return 4294967295u; - if(val < -1.0) return 0; - return (ALint)(val * 2147483647.0) + 2147483648u; -} -static inline ALuint Conv_ALuint_ALmulaw(ALmulaw val) -{ return Conv_ALuint_ALshort(DecodeMuLaw(val)); } -static inline ALuint Conv_ALuint_ALalaw(ALalaw val) -{ return Conv_ALuint_ALshort(DecodeALaw(val)); } -static inline ALuint Conv_ALuint_ALbyte3(ALbyte3 val) -{ return (DecodeByte3(val)+8388608)<<8; } -static inline ALuint Conv_ALuint_ALubyte3(ALubyte3 val) -{ return DecodeUByte3(val)<<8; } - -static inline ALfloat Conv_ALfloat_ALbyte(ALbyte val) -{ return val * (1.0f/127.0f); } -static inline ALfloat Conv_ALfloat_ALubyte(ALubyte val) -{ return (val-128) * (1.0f/127.0f); } -static inline ALfloat Conv_ALfloat_ALshort(ALshort val) -{ return val * (1.0f/32767.0f); } -static inline ALfloat Conv_ALfloat_ALushort(ALushort val) -{ return (val-32768) * (1.0f/32767.0f); } -static inline ALfloat Conv_ALfloat_ALint(ALint val) -{ return (ALfloat)(val>>7) * (1.0f/16777215.0f); } -static inline ALfloat Conv_ALfloat_ALuint(ALuint val) -{ return (ALfloat)((ALint)(val>>7)-16777216) * (1.0f/16777215.0f); } +/* 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 ALfloat Conv_ALfloat_ALmulaw(ALmulaw val) -{ return Conv_ALfloat_ALshort(DecodeMuLaw(val)); } -static inline ALfloat Conv_ALfloat_ALalaw(ALalaw val) -{ return Conv_ALfloat_ALshort(DecodeALaw(val)); } -static inline ALfloat Conv_ALfloat_ALbyte3(ALbyte3 val) -{ return (ALfloat)(DecodeByte3(val) * (1.0/8388607.0)); } -static inline ALfloat Conv_ALfloat_ALubyte3(ALubyte3 val) -{ return (ALfloat)((DecodeUByte3(val)-8388608) * (1.0/8388607.0)); } - -static inline ALdouble Conv_ALdouble_ALbyte(ALbyte val) -{ return val * (1.0/127.0); } -static inline ALdouble Conv_ALdouble_ALubyte(ALubyte val) -{ return (val-128) * (1.0/127.0); } -static inline ALdouble Conv_ALdouble_ALshort(ALshort val) -{ return val * (1.0/32767.0); } -static inline ALdouble Conv_ALdouble_ALushort(ALushort val) -{ return (val-32768) * (1.0/32767.0); } -static inline ALdouble Conv_ALdouble_ALint(ALint val) -{ return val * (1.0/2147483647.0); } -static inline ALdouble Conv_ALdouble_ALuint(ALuint val) -{ return (ALint)(val-2147483648u) * (1.0/2147483647.0); } static inline ALdouble Conv_ALdouble_ALfloat(ALfloat val) -{ return (val==val) ? val : 0.0f; } +{ return (val==val) ? (ALdouble)val : 0.0; } static inline ALdouble Conv_ALdouble_ALdouble(ALdouble val) { return (val==val) ? val : 0.0; } -static inline ALdouble Conv_ALdouble_ALmulaw(ALmulaw val) -{ return Conv_ALdouble_ALshort(DecodeMuLaw(val)); } -static inline ALdouble Conv_ALdouble_ALalaw(ALalaw val) -{ return Conv_ALdouble_ALshort(DecodeALaw(val)); } -static inline ALdouble Conv_ALdouble_ALbyte3(ALbyte3 val) -{ return DecodeByte3(val) * (1.0/8388607.0); } -static inline ALdouble Conv_ALdouble_ALubyte3(ALubyte3 val) -{ return (DecodeUByte3(val)-8388608) * (1.0/8388607.0); } +#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/128.0f)) +DECL_TEMPLATE2(ALdouble, byte, (1.0/128.0)) +DECL_TEMPLATE2(ALfloat, short, (1.0f/32768.0f)) +DECL_TEMPLATE2(ALdouble, short, (1.0/32768.0)) +DECL_TEMPLATE2(ALdouble, int, (1.0/2147483648.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/16777216.0f); } +static inline ALfloat Conv_ALfloat_ALuint(ALuint val) +{ return (ALfloat)(Conv_ALint_ALuint(val)>>7) * (1.0f/16777216.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) \ +{ \ + val *= (FT)smax + 1; \ + if(val >= (FT)smax) return smax; \ + if(val <= (FT)smin) return smin; \ + return (AL##T)val; \ +} \ +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) +{ + val *= 16777216.0f; + if(val >= 16777215.0f) return 0x7fffff80/*16777215 << 7*/; + if(val <= -16777216.0f) return 0x80000000/*-16777216 << 7*/; + return (ALint)val << 7; +} +static inline ALuint Conv_ALuint_ALfloat(ALfloat val) +{ return Conv_ALuint_ALint(Conv_ALint_ALfloat(val)); } + +#undef DECL_TEMPLATE + +/* 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)); } - -DECL_TEMPLATE(ALbyte) -DECL_TEMPLATE(ALubyte) -DECL_TEMPLATE(ALshort) -DECL_TEMPLATE(ALushort) -DECL_TEMPLATE(ALint) -DECL_TEMPLATE(ALuint) -DECL_TEMPLATE(ALfloat) -DECL_TEMPLATE(ALdouble) -static inline ALmulaw Conv_ALmulaw_ALmulaw(ALmulaw val) -{ return val; } -DECL_TEMPLATE(ALalaw) -DECL_TEMPLATE(ALbyte3) -DECL_TEMPLATE(ALubyte3) - -#undef DECL_TEMPLATE - -#define DECL_TEMPLATE(T) \ +{ 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)); } +{ 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) @@ -821,53 +621,14 @@ DECL_TEMPLATE(ALint) DECL_TEMPLATE(ALuint) DECL_TEMPLATE(ALfloat) DECL_TEMPLATE(ALdouble) -DECL_TEMPLATE(ALmulaw) -static inline ALalaw Conv_ALalaw_ALalaw(ALalaw val) -{ return val; } -DECL_TEMPLATE(ALbyte3) -DECL_TEMPLATE(ALubyte3) #undef DECL_TEMPLATE -#define DECL_TEMPLATE(T) \ -static inline ALbyte3 Conv_ALbyte3_##T(T val) \ -{ return EncodeByte3(Conv_ALint_##T(val)>>8); } - -DECL_TEMPLATE(ALbyte) -DECL_TEMPLATE(ALubyte) -DECL_TEMPLATE(ALshort) -DECL_TEMPLATE(ALushort) -DECL_TEMPLATE(ALint) -DECL_TEMPLATE(ALuint) -DECL_TEMPLATE(ALfloat) -DECL_TEMPLATE(ALdouble) -DECL_TEMPLATE(ALmulaw) -DECL_TEMPLATE(ALalaw) -static inline ALbyte3 Conv_ALbyte3_ALbyte3(ALbyte3 val) -{ return val; } -DECL_TEMPLATE(ALubyte3) - -#undef DECL_TEMPLATE - -#define DECL_TEMPLATE(T) \ -static inline ALubyte3 Conv_ALubyte3_##T(T val) \ -{ return EncodeUByte3(Conv_ALuint_##T(val)>>8); } - -DECL_TEMPLATE(ALbyte) -DECL_TEMPLATE(ALubyte) -DECL_TEMPLATE(ALshort) -DECL_TEMPLATE(ALushort) -DECL_TEMPLATE(ALint) -DECL_TEMPLATE(ALuint) -DECL_TEMPLATE(ALfloat) -DECL_TEMPLATE(ALdouble) -DECL_TEMPLATE(ALmulaw) -DECL_TEMPLATE(ALalaw) -DECL_TEMPLATE(ALbyte3) -static inline ALubyte3 Conv_ALubyte3_ALubyte3(ALubyte3 val) -{ return val; } - -#undef DECL_TEMPLATE +/* Define 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) \ @@ -892,9 +653,7 @@ 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_TEMPLATE(T, ALalaw) DECL_TEMPLATE2(ALbyte) DECL_TEMPLATE2(ALubyte) @@ -906,8 +665,6 @@ DECL_TEMPLATE2(ALfloat) DECL_TEMPLATE2(ALdouble) DECL_TEMPLATE2(ALmulaw) DECL_TEMPLATE2(ALalaw) -DECL_TEMPLATE2(ALbyte3) -DECL_TEMPLATE2(ALubyte3) #undef DECL_TEMPLATE2 #undef DECL_TEMPLATE @@ -957,8 +714,6 @@ DECL_TEMPLATE(ALfloat) DECL_TEMPLATE(ALdouble) DECL_TEMPLATE(ALmulaw) DECL_TEMPLATE(ALalaw) -DECL_TEMPLATE(ALbyte3) -DECL_TEMPLATE(ALubyte3) #undef DECL_TEMPLATE @@ -1010,8 +765,6 @@ DECL_TEMPLATE(ALfloat) DECL_TEMPLATE(ALdouble) DECL_TEMPLATE(ALmulaw) DECL_TEMPLATE(ALalaw) -DECL_TEMPLATE(ALbyte3) -DECL_TEMPLATE(ALubyte3) #undef DECL_TEMPLATE @@ -1063,8 +816,6 @@ DECL_TEMPLATE(ALfloat) DECL_TEMPLATE(ALdouble) DECL_TEMPLATE(ALmulaw) DECL_TEMPLATE(ALalaw) -DECL_TEMPLATE(ALbyte3) -DECL_TEMPLATE(ALubyte3) #undef DECL_TEMPLATE @@ -1114,8 +865,6 @@ DECL_TEMPLATE(ALfloat) DECL_TEMPLATE(ALdouble) DECL_TEMPLATE(ALmulaw) DECL_TEMPLATE(ALalaw) -DECL_TEMPLATE(ALbyte3) -DECL_TEMPLATE(ALubyte3) #undef DECL_TEMPLATE @@ -1192,12 +941,6 @@ static void Convert_##T(T *dst, const ALvoid *src, enum UserFmtType srcType, \ 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; \ } \ } @@ -1213,8 +956,6 @@ DECL_TEMPLATE(ALmulaw) DECL_TEMPLATE(ALalaw) DECL_TEMPLATE(ALima4) DECL_TEMPLATE(ALmsadpcm) -DECL_TEMPLATE(ALbyte3) -DECL_TEMPLATE(ALubyte3) #undef DECL_TEMPLATE @@ -1259,11 +1000,5 @@ void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src, enum case UserFmtMSADPCM: Convert_ALmsadpcm(dst, src, srcType, numchans, len, align); break; - case UserFmtByte3: - Convert_ALbyte3(dst, src, srcType, numchans, len, align); - break; - case UserFmtUByte3: - Convert_ALubyte3(dst, src, srcType, numchans, len, align); - break; } } diff --git a/libs/openal-soft/alsoftrc.sample b/libs/openal-soft/alsoftrc.sample index 039dba6a..3e7d0eec 100644 --- a/libs/openal-soft/alsoftrc.sample +++ b/libs/openal-soft/alsoftrc.sample @@ -48,7 +48,10 @@ ## channels: # Sets the output channel configuration. If left unspecified, one will try to # be detected from the system, and defaulting to stereo. The available values -# are: mono, stereo, quad, surround51, surround51rear, surround61, surround71 +# are: mono, stereo, quad, surround51, surround51rear, surround61, surround71, +# ambi1, ambi2, ambi3. Note that the ambi* configurations provide ambisonic +# channels of the given order (using ACN ordering and SN3D normalization by +# default), which need to be decoded to play correctly on speakers. #channels = ## sample-type: @@ -78,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 @@ -86,6 +89,20 @@ # Valid settings are auto, speakers, and headphones. #stereo-mode = auto +## 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 +# configurations. Valid settings are: fuma, acn+sn3d, acn+n3d +#ambi-format = acn+sn3d + ## hrtf: # Controls HRTF processing. These filters provide better spatialization of # sounds while using headphones, but do require a bit more CPU power. The @@ -96,22 +113,24 @@ # respectively. #hrtf = auto -## hrtf_tables: -# Specifies a comma-separated list of files containing HRTF data sets. The -# format of the files are described in hrtf.txt. The filenames may contain -# these markers, which will be replaced as needed: -# %r - Device sampling rate -# %s - Non-greedy string (up to the following matching characters) -# %% - Percent sign (%) -# The listed files are relative to system-dependant data directories. On -# Windows this is: +## default-hrtf: +# Specifies the default HRTF to use. When multiple HRTFs are available, this +# determines the preferred one to use if none are specifically requested. Note +# that this is the enumerated HRTF name, not necessarily the filename. +#default-hrtf = + +## hrtf-paths: +# Specifies a comma-separated list of paths containing HRTF data sets. The +# format of the files are described in docs/hrtf.txt. The files within the +# directories must have the .mhr file extension to be recognized. By default, +# OS-dependent data paths will be used. They will also be used if the list +# ends with a comma. On Windows this is: # $AppData\openal\hrtf # And on other systems, it's (in order): # $XDG_DATA_HOME/openal/hrtf (defaults to $HOME/.local/share/openal/hrtf) # $XDG_DATA_DIRS/openal/hrtf (defaults to /usr/local/share/openal/hrtf and # /usr/share/openal/hrtf) -# An absolute path may also be specified, if the given file is elsewhere. -#hrtf_tables = %s.mhr +#hrtf-paths = ## cf_level: # Sets the crossfeed level for stereo output. Valid values are: @@ -131,7 +150,6 @@ # 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). @@ -156,13 +174,38 @@ # 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 + +## 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 +# value is logarithmic, so +6 will be a scale of (approximately) 2x, +12 will +# be a scale of 4x, etc. Similarly, -6 will be x1/2, and -12 is about x1/4. A +# value of 0 means no change. +#volume-adjust = 0 ## excludefx: (global) # Sets which effects to exclude, preventing apps from using them. This can @@ -192,6 +235,69 @@ # of a context error. On Windows, a breakpoint exception is generated. #trap-al-error = false +## +## Ambisonic decoder stuff +## +[decoder] + +## hq-mode: +# Enables a high-quality ambisonic decoder. This mode is capable of frequency- +# dependent processing, creating a better reproduction of 3D sound rendering +# over surround sound speakers. Enabling this also requires specifying decoder +# configuration files for the appropriate speaker configuration you intend to +# use (see the quad, surround51, etc options below). Currently, up to third- +# order decoding is supported. +hq-mode = false + +## distance-comp: +# 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 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 +# docs/ambdec.txt for a description of the file format. +quad = + +## surround51: +# Decoder configuration file for 5.1 Surround (Side and Rear) channel output. +# See docs/ambdec.txt for a description of the file format. +surround51 = + +## surround61: +# Decoder configuration file for 6.1 Surround channel output. See +# docs/ambdec.txt for a description of the file format. +surround61 = + +## surround71: +# Decoder configuration file for 7.1 Surround channel output. See +# docs/ambdec.txt for a description of the file format. Note: This can be used +# to enable 3D7.1 with the appropriate configuration and speaker placement, +# see docs/3D7.1.txt. +surround71 = + ## ## Reverb effect stuff (includes EAX reverb) ## diff --git a/libs/openal-soft/appveyor.yml b/libs/openal-soft/appveyor.yml new file mode 100644 index 00000000..eab4f397 --- /dev/null +++ b/libs/openal-soft/appveyor.yml @@ -0,0 +1,19 @@ +version: 1.18.2.{build} + +environment: + matrix: + - GEN: "Visual Studio 14 2015" + CFG: Release + - 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%" -DALSOFT_REQUIRE_WINMM=ON -DALSOFT_REQUIRE_DSOUND=ON -DALSOFT_REQUIRE_MMDEVAPI=ON -DALSOFT_EMBED_HRTF_DATA=YES .. + - cmake --build . --config %CFG% --clean-first + diff --git a/libs/openal-soft/cmake/CheckSharedFunctionExists.cmake b/libs/openal-soft/cmake/CheckSharedFunctionExists.cmake index 7975f233..c691fa9c 100644 --- a/libs/openal-soft/cmake/CheckSharedFunctionExists.cmake +++ b/libs/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/libs/openal-soft/cmake/FindDSound.cmake b/libs/openal-soft/cmake/FindDSound.cmake index 0ddf98aa..4078deb5 100644 --- a/libs/openal-soft/cmake/FindDSound.cmake +++ b/libs/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/libs/openal-soft/cmake/FindFFmpeg.cmake b/libs/openal-soft/cmake/FindFFmpeg.cmake index 96cbb6ed..c489c2c3 100644 --- a/libs/openal-soft/cmake/FindFFmpeg.cmake +++ b/libs/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/libs/openal-soft/cmake/FindOSS.cmake b/libs/openal-soft/cmake/FindOSS.cmake index 88ee66ad..feffb451 100644 --- a/libs/openal-soft/cmake/FindOSS.cmake +++ b/libs/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/libs/openal-soft/cmake/FindWindowsSDK.cmake b/libs/openal-soft/cmake/FindWindowsSDK.cmake new file mode 100644 index 00000000..e136b897 --- /dev/null +++ b/libs/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/libs/openal-soft/include/align.h b/libs/openal-soft/common/align.h similarity index 100% rename from libs/openal-soft/include/align.h rename to libs/openal-soft/common/align.h diff --git a/libs/openal-soft/common/almalloc.c b/libs/openal-soft/common/almalloc.c new file mode 100644 index 00000000..8c1c5794 --- /dev/null +++ b/libs/openal-soft/common/almalloc.c @@ -0,0 +1,62 @@ + +#include "config.h" + +#include "almalloc.h" + +#include +#include +#ifdef HAVE_MALLOC_H +#include +#endif +#ifdef HAVE_WINDOWS_H +#include +#endif + +void *al_malloc(size_t alignment, size_t size) +{ +#if defined(HAVE_ALIGNED_ALLOC) + size = (size+(alignment-1))&~(alignment-1); + return aligned_alloc(alignment, size); +#elif defined(HAVE_POSIX_MEMALIGN) + void *ret; + if(posix_memalign(&ret, alignment, size) == 0) + return ret; + return NULL; +#elif defined(HAVE__ALIGNED_MALLOC) + return _aligned_malloc(size, alignment); +#else + char *ret = malloc(size+alignment); + if(ret != NULL) + { + *(ret++) = 0x00; + while(((ptrdiff_t)ret&(alignment-1)) != 0) + *(ret++) = 0x55; + } + return ret; +#endif +} + +void *al_calloc(size_t alignment, size_t size) +{ + void *ret = al_malloc(alignment, size); + if(ret) memset(ret, 0, size); + return ret; +} + +void al_free(void *ptr) +{ +#if defined(HAVE_ALIGNED_ALLOC) || defined(HAVE_POSIX_MEMALIGN) + free(ptr); +#elif defined(HAVE__ALIGNED_MALLOC) + _aligned_free(ptr); +#else + if(ptr != NULL) + { + char *finder = ptr; + do { + --finder; + } while(*finder == 0x55); + free(finder); + } +#endif +} diff --git a/libs/openal-soft/common/almalloc.h b/libs/openal-soft/common/almalloc.h new file mode 100644 index 00000000..8eadb5b3 --- /dev/null +++ b/libs/openal-soft/common/almalloc.h @@ -0,0 +1,21 @@ +#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); + +#ifdef __cplusplus +} +#endif + +#endif /* AL_MALLOC_H */ diff --git a/libs/openal-soft/common/atomic.c b/libs/openal-soft/common/atomic.c index 3cdb77f4..7a8fe6d8 100644 --- a/libs/openal-soft/common/atomic.c +++ b/libs/openal-soft/common/atomic.c @@ -8,6 +8,3 @@ extern inline void InitRef(RefCount *ptr, uint value); extern inline uint ReadRef(RefCount *ptr); extern inline uint IncrementRef(RefCount *ptr); extern inline uint DecrementRef(RefCount *ptr); - -extern inline int ExchangeInt(volatile int *ptr, int newval); -extern inline void *ExchangePtr(XchgPtr *ptr, void *newval); diff --git a/libs/openal-soft/common/atomic.h b/libs/openal-soft/common/atomic.h new file mode 100644 index 00000000..874d510d --- /dev/null +++ b/libs/openal-soft/common/atomic.h @@ -0,0 +1,425 @@ +#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_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 */ + +#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)) : \ + (void*)_al_invalid_atomic_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_SEQ(ptr); } +inline uint IncrementRef(RefCount *ptr) +{ return ATOMIC_ADD_SEQ(ptr, 1)+1; } +inline uint DecrementRef(RefCount *ptr) +{ return ATOMIC_SUB_SEQ(ptr, 1)-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/libs/openal-soft/include/bool.h b/libs/openal-soft/common/bool.h similarity index 100% rename from libs/openal-soft/include/bool.h rename to libs/openal-soft/common/bool.h diff --git a/libs/openal-soft/common/math_defs.h b/libs/openal-soft/common/math_defs.h new file mode 100644 index 00000000..cbe9091f --- /dev/null +++ b/libs/openal-soft/common/math_defs.h @@ -0,0 +1,35 @@ +#ifndef AL_MATH_DEFS_H +#define AL_MATH_DEFS_H + +#include +#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 + +#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 + +#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/libs/openal-soft/common/rwlock.c b/libs/openal-soft/common/rwlock.c index cfa3aee4..67cf3acf 100644 --- a/libs/openal-soft/common/rwlock.c +++ b/libs/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/libs/openal-soft/include/rwlock.h b/libs/openal-soft/common/rwlock.h similarity index 67% rename from libs/openal-soft/include/rwlock.h rename to libs/openal-soft/common/rwlock.h index 158a0670..8e36fa1a 100644 --- a/libs/openal-soft/include/rwlock.h +++ b/libs/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/libs/openal-soft/include/static_assert.h b/libs/openal-soft/common/static_assert.h similarity index 100% rename from libs/openal-soft/include/static_assert.h rename to libs/openal-soft/common/static_assert.h diff --git a/libs/openal-soft/common/threads.c b/libs/openal-soft/common/threads.c index 127b9083..dbd196e8 100644 --- a/libs/openal-soft/common/threads.c +++ b/libs/openal-soft/common/threads.c @@ -55,7 +55,7 @@ extern inline int altss_set(altss_t tss_id, void *val); #endif -#define THREAD_STACK_SIZE (1*1024*1024) /* 1MB */ +#define THREAD_STACK_SIZE (2*1024*1024) /* 2MB */ #ifdef _WIN32 @@ -194,7 +194,8 @@ int althrd_sleep(const struct timespec *ts, struct timespec* UNUSED(rem)) int almtx_init(almtx_t *mtx, int type) { if(!mtx) return althrd_error; - type &= ~(almtx_recursive|almtx_timed); + + type &= ~almtx_recursive; if(type != almtx_plain) return althrd_error; @@ -207,27 +208,10 @@ void almtx_destroy(almtx_t *mtx) DeleteCriticalSection(mtx); } -int almtx_timedlock(almtx_t *mtx, const struct timespec *ts) +int almtx_timedlock(almtx_t* UNUSED(mtx), const struct timespec* UNUSED(ts)) { - int ret; - - if(!mtx || !ts) - return althrd_error; - - while((ret=almtx_trylock(mtx)) == althrd_busy) - { - struct timespec now; - - if(ts->tv_sec < 0 || ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000 || - altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC) - return althrd_error; - if(now.tv_sec > ts->tv_sec || (now.tv_sec == ts->tv_sec && now.tv_nsec >= ts->tv_nsec)) - return althrd_timedout; - - althrd_yield(); - } - - return ret; + /* Windows CRITICAL_SECTIONs don't seem to have a timedlock method. */ + return althrd_error; } #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 @@ -264,10 +248,19 @@ int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_poi if(altimespec_get(&curtime, AL_TIME_UTC) != AL_TIME_UTC) return althrd_error; - sleeptime = (time_point->tv_nsec - curtime.tv_nsec + 999999)/1000000; - sleeptime += (time_point->tv_sec - curtime.tv_sec)*1000; - if(SleepConditionVariableCS(cond, mtx, sleeptime) != 0) - return althrd_success; + 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 += (DWORD)(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; } @@ -306,8 +299,8 @@ int alcnd_init(alcnd_t *cond) InitRef(&icond->wait_count, 0); - icond->events[SIGNAL] = CreateEvent(NULL, FALSE, FALSE, NULL); - icond->events[BROADCAST] = CreateEvent(NULL, TRUE, FALSE, NULL); + icond->events[SIGNAL] = CreateEventW(NULL, FALSE, FALSE, NULL); + icond->events[BROADCAST] = CreateEventW(NULL, TRUE, FALSE, NULL); if(!icond->events[SIGNAL] || !icond->events[BROADCAST]) { if(icond->events[SIGNAL]) @@ -364,8 +357,15 @@ int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_poi if(altimespec_get(&curtime, AL_TIME_UTC) != AL_TIME_UTC) return althrd_error; - sleeptime = (time_point->tv_nsec - curtime.tv_nsec + 999999)/1000000; - sleeptime += (time_point->tv_sec - curtime.tv_sec)*1000; + + if(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 += (DWORD)(time_point->tv_sec - curtime.tv_sec)*1000; + } IncrementRef(&icond->wait_count); LeaveCriticalSection(mtx); @@ -413,8 +413,8 @@ static void NTAPI altss_callback(void* UNUSED(handle), DWORD reason, void* UNUSE LockUIntMapRead(&TlsDestructors); for(i = 0;i < TlsDestructors.size;i++) { - void *ptr = altss_get(TlsDestructors.array[i].key); - altss_dtor_t callback = (altss_dtor_t)TlsDestructors.array[i].value; + void *ptr = altss_get(TlsDestructors.keys[i]); + altss_dtor_t callback = (altss_dtor_t)TlsDestructors.values[i]; if(ptr && callback) callback(ptr); } @@ -500,6 +500,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 @@ -531,6 +533,8 @@ int althrd_create(althrd_t *thr, althrd_start_t func, void *arg) { thread_cntr *cntr; pthread_attr_t attr; + size_t stackmult = 1; + int err; cntr = malloc(sizeof(*cntr)); if(!cntr) return althrd_nomem; @@ -540,7 +544,8 @@ int althrd_create(althrd_t *thr, althrd_start_t func, void *arg) free(cntr); return althrd_error; } - if(pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE) != 0) +retry_stacksize: + if(pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE*stackmult) != 0) { pthread_attr_destroy(&attr); free(cntr); @@ -549,15 +554,30 @@ int althrd_create(althrd_t *thr, althrd_start_t func, void *arg) cntr->func = func; cntr->arg = arg; - if(pthread_create(thr, &attr, althrd_starter, cntr) != 0) + if((err=pthread_create(thr, &attr, althrd_starter, cntr)) == 0) { pthread_attr_destroy(&attr); - free(cntr); - return althrd_error; + return althrd_success; + } + + if(err == EINVAL) + { + /* If an invalid stack size, try increasing it (limit x4, 8MB). */ + if(stackmult < 4) + { + stackmult *= 2; + goto retry_stacksize; + } + /* If still nothing, try defaults and hope they're good enough. */ + if(pthread_create(thr, NULL, althrd_starter, cntr) == 0) + { + pthread_attr_destroy(&attr); + return althrd_success; + } } pthread_attr_destroy(&attr); - - return althrd_success; + free(cntr); + return althrd_error; } int althrd_detach(althrd_t thr) @@ -584,8 +604,13 @@ 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) @@ -621,36 +646,16 @@ void almtx_destroy(almtx_t *mtx) int almtx_timedlock(almtx_t *mtx, const struct timespec *ts) { - int ret; - #ifdef HAVE_PTHREAD_MUTEX_TIMEDLOCK - ret = pthread_mutex_timedlock(mtx, ts); + int ret = pthread_mutex_timedlock(mtx, ts); switch(ret) { case 0: return althrd_success; case ETIMEDOUT: return althrd_timedout; case EBUSY: return althrd_busy; } - return althrd_error; -#else - if(!mtx || !ts) - return althrd_error; - - while((ret=almtx_trylock(mtx)) == althrd_busy) - { - struct timespec now; - - if(ts->tv_sec < 0 || ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000 || - altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC) - return althrd_error; - if(now.tv_sec > ts->tv_sec || (now.tv_sec == ts->tv_sec && now.tv_nsec >= ts->tv_nsec)) - return althrd_timedout; - - althrd_yield(); - } - - return ret; #endif + return althrd_error; } int alcnd_init(alcnd_t *cond) diff --git a/libs/openal-soft/include/threads.h b/libs/openal-soft/common/threads.h similarity index 95% rename from libs/openal-soft/include/threads.h rename to libs/openal-soft/common/threads.h index a11405f7..c2848ee7 100644 --- a/libs/openal-soft/include/threads.h +++ b/libs/openal-soft/common/threads.h @@ -33,17 +33,11 @@ typedef void (*altss_dtor_t)(void*); #include -#if !defined(_TIMESPEC_DEFINED) && !(defined(_MSC_VER) && (_MSC_VER >= 1900)) -#define _TIMESPEC_DEFINED +#ifndef HAVE_STRUCT_TIMESPEC struct timespec { time_t tv_sec; long tv_nsec; }; - -struct itimerspec { - struct timespec it_interval; - struct timespec it_value; -}; #endif typedef DWORD althrd_t; diff --git a/libs/openal-soft/common/uintmap.c b/libs/openal-soft/common/uintmap.c index b7a9a29c..98ed3191 100644 --- a/libs/openal-soft/common/uintmap.c +++ b/libs/openal-soft/common/uintmap.c @@ -6,6 +6,8 @@ #include #include +#include "almalloc.h" + extern inline void LockUIntMapRead(UIntMap *map); extern inline void UnlockUIntMapRead(UIntMap *map); @@ -15,9 +17,10 @@ extern inline void UnlockUIntMapWrite(UIntMap *map); void InitUIntMap(UIntMap *map, ALsizei limit) { - map->array = NULL; + map->keys = NULL; + map->values = NULL; map->size = 0; - map->maxsize = 0; + map->capacity = 0; map->limit = limit; RWLockInit(&map->lock); } @@ -25,13 +28,19 @@ void InitUIntMap(UIntMap *map, ALsizei limit) void ResetUIntMap(UIntMap *map) { WriteLock(&map->lock); - free(map->array); - map->array = NULL; + al_free(map->keys); + map->keys = NULL; + map->values = NULL; map->size = 0; - map->maxsize = 0; + map->capacity = 0; WriteUnlock(&map->lock); } +void RelimitUIntMapNoLock(UIntMap *map, ALsizei limit) +{ + map->limit = limit; +} + ALenum InsertUIntMapEntry(UIntMap *map, ALuint key, ALvoid *value) { ALsizei pos = 0; @@ -39,80 +48,186 @@ 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->array[mid].key < 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->array[low].key < key) - low++; - pos = low; + { + pos = i+1; + count -= step+1; + } + } while(count > 0); } - if(pos == map->size || map->array[pos].key != key) + 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; } - if(map->size == map->maxsize) + if(map->size == map->capacity) { - ALvoid *temp = NULL; - ALsizei newsize; + ALuint *keys = NULL; + ALvoid **values; + ALsizei newcap, keylen; - newsize = (map->maxsize ? (map->maxsize<<1) : 4); - if(newsize >= map->maxsize) - temp = realloc(map->array, newsize*sizeof(map->array[0])); - if(!temp) + newcap = (map->capacity ? (map->capacity<<1) : 4); + if(map->limit > 0 && newcap > map->limit) + newcap = map->limit; + if(newcap > map->capacity) + { + /* Round the memory size for keys up to a multiple of the + * pointer size. + */ + keylen = newcap * sizeof(map->keys[0]); + keylen += sizeof(map->values[0]) - 1; + keylen -= keylen%sizeof(map->values[0]); + + keys = al_malloc(16, keylen + newcap*sizeof(map->values[0])); + } + if(!keys) { WriteUnlock(&map->lock); return AL_OUT_OF_MEMORY; } - map->array = temp; - map->maxsize = newsize; + values = (ALvoid**)((ALbyte*)keys + keylen); + + if(map->keys) + { + memcpy(keys, map->keys, map->size*sizeof(map->keys[0])); + memcpy(values, map->values, map->size*sizeof(map->values[0])); + } + al_free(map->keys); + map->keys = keys; + map->values = values; + map->capacity = newcap; } if(pos < map->size) - memmove(&map->array[pos+1], &map->array[pos], - (map->size-pos)*sizeof(map->array[0])); + { + memmove(&map->keys[pos+1], &map->keys[pos], + (map->size-pos)*sizeof(map->keys[0])); + memmove(&map->values[pos+1], &map->values[pos], + (map->size-pos)*sizeof(map->values[0])); + } map->size++; } - map->array[pos].key = key; - map->array[pos].value = value; + map->keys[pos] = key; + map->values[pos] = value; WriteUnlock(&map->lock); return AL_NO_ERROR; } +ALenum InsertUIntMapEntryNoLock(UIntMap *map, ALuint key, ALvoid *value) +{ + ALsizei pos = 0; + + if(map->size > 0) + { + ALsizei count = map->size; + do { + ALsizei step = count>>1; + ALsizei i = pos+step; + if(!(map->keys[i] < key)) + count = step; + else + { + pos = i+1; + count -= step+1; + } + } while(count > 0); + } + + if(pos == map->size || map->keys[pos] != key) + { + if(map->size >= map->limit) + return AL_OUT_OF_MEMORY; + + if(map->size == map->capacity) + { + ALuint *keys = NULL; + ALvoid **values; + ALsizei newcap, keylen; + + newcap = (map->capacity ? (map->capacity<<1) : 4); + if(map->limit > 0 && newcap > map->limit) + newcap = map->limit; + if(newcap > map->capacity) + { + /* Round the memory size for keys up to a multiple of the + * pointer size. + */ + keylen = newcap * sizeof(map->keys[0]); + keylen += sizeof(map->values[0]) - 1; + keylen -= keylen%sizeof(map->values[0]); + + keys = al_malloc(16, keylen + newcap*sizeof(map->values[0])); + } + if(!keys) + return AL_OUT_OF_MEMORY; + values = (ALvoid**)((ALbyte*)keys + keylen); + + if(map->keys) + { + memcpy(keys, map->keys, map->size*sizeof(map->keys[0])); + memcpy(values, map->values, map->size*sizeof(map->values[0])); + } + al_free(map->keys); + map->keys = keys; + map->values = values; + map->capacity = newcap; + } + + if(pos < map->size) + { + memmove(&map->keys[pos+1], &map->keys[pos], + (map->size-pos)*sizeof(map->keys[0])); + memmove(&map->values[pos+1], &map->values[pos], + (map->size-pos)*sizeof(map->values[0])); + } + map->size++; + } + map->keys[pos] = key; + map->values[pos] = value; + + return AL_NO_ERROR; +} + ALvoid *RemoveUIntMapKey(UIntMap *map, ALuint key) { ALvoid *ptr = NULL; WriteLock(&map->lock); if(map->size > 0) { - ALsizei low = 0; - ALsizei high = map->size - 1; - while(low < high) - { - ALsizei mid = low + (high-low)/2; - if(map->array[mid].key < key) - low = mid + 1; + 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->array[low].key == key) + { + pos = i+1; + count -= step+1; + } + } while(count > 0); + if(pos < map->size && map->keys[pos] == key) { - ptr = map->array[low].value; - if(low < map->size-1) - memmove(&map->array[low], &map->array[low+1], - (map->size-1-low)*sizeof(map->array[0])); + 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--; } } @@ -120,25 +235,85 @@ ALvoid *RemoveUIntMapKey(UIntMap *map, ALuint key) return ptr; } +ALvoid *RemoveUIntMapKeyNoLock(UIntMap *map, ALuint key) +{ + ALvoid *ptr = NULL; + if(map->size > 0) + { + ALsizei pos = 0; + ALsizei count = map->size; + do { + ALsizei step = count>>1; + ALsizei i = pos+step; + if(!(map->keys[i] < key)) + count = step; + else + { + 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--; + } + } + return ptr; +} + ALvoid *LookupUIntMapKey(UIntMap *map, ALuint key) { ALvoid *ptr = NULL; ReadLock(&map->lock); if(map->size > 0) { - ALsizei low = 0; - ALsizei high = map->size - 1; - while(low < high) - { - ALsizei mid = low + (high-low)/2; - if(map->array[mid].key < key) - low = mid + 1; + 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->array[low].key == key) - ptr = map->array[low].value; + { + 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 pos = 0; + ALsizei count = map->size; + do { + ALsizei step = count>>1; + ALsizei i = pos+step; + if(!(map->keys[i] < key)) + count = step; + else + { + pos = i+1; + count -= step+1; + } + } while(count > 0); + if(pos < map->size && map->keys[pos] == key) + return map->values[pos]; + } + return NULL; +} diff --git a/libs/openal-soft/include/uintmap.h b/libs/openal-soft/common/uintmap.h similarity index 61% rename from libs/openal-soft/include/uintmap.h rename to libs/openal-soft/common/uintmap.h index 2c4c5e7a..47bd0d42 100644 --- a/libs/openal-soft/include/uintmap.h +++ b/libs/openal-soft/common/uintmap.h @@ -9,23 +9,27 @@ extern "C" { #endif typedef struct UIntMap { - struct { - ALuint key; - ALvoid *value; - } *array; + ALuint *keys; + /* Shares memory with keys. */ + ALvoid **values; + ALsizei size; - ALsizei maxsize; + ALsizei capacity; ALsizei limit; RWLock lock; } UIntMap; -#define UINTMAP_STATIC_INITIALIZE_N(_n) { NULL, 0, 0, (_n), RWLOCK_STATIC_INITIALIZE } -#define UINTMAP_STATIC_INITIALIZE UINTMAP_STATIC_INITIALIZE_N(~0) +#define UINTMAP_STATIC_INITIALIZE_N(_n) { NULL, NULL, 0, 0, (_n), RWLOCK_STATIC_INITIALIZE } +#define UINTMAP_STATIC_INITIALIZE UINTMAP_STATIC_INITIALIZE_N(INT_MAX) void InitUIntMap(UIntMap *map, ALsizei limit); void ResetUIntMap(UIntMap *map); +void RelimitUIntMapNoLock(UIntMap *map, ALsizei limit); ALenum InsertUIntMapEntry(UIntMap *map, ALuint key, ALvoid *value); +ALenum InsertUIntMapEntryNoLock(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); } diff --git a/libs/openal-soft/config.h.in b/libs/openal-soft/config.h.in index b59226ee..b0fc5d2b 100644 --- a/libs/openal-soft/config.h.in +++ b/libs/openal-soft/config.h.in @@ -2,18 +2,15 @@ #define AL_API ${EXPORT_DECL} #define ALC_API ${EXPORT_DECL} -/* Define to the library version */ -#define ALSOFT_VERSION "${LIB_VERSION}" - -#ifdef IN_IDE_PARSER -/* KDevelop's parser doesn't recognize the C99-standard restrict keyword, but - * recent versions (at least 4.5.1) do recognize GCC's __restrict. */ -#define restrict __restrict -#endif - /* Define any available alignment declaration */ #define ALIGN(x) ${ALIGN_DECL} +/* Define 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 C11 aligned_alloc function */ #cmakedefine HAVE_ALIGNED_ALLOC @@ -83,6 +80,9 @@ /* 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 strtof function */ #cmakedefine HAVE_STRTOF @@ -194,6 +194,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/libs/openal-soft/docs/3D7.1.txt b/libs/openal-soft/docs/3D7.1.txt new file mode 100644 index 00000000..1d40bec6 --- /dev/null +++ b/libs/openal-soft/docs/3D7.1.txt @@ -0,0 +1,82 @@ +Overview +======== + +3D7.1 is a custom speaker layout designed by Simon Goodwin at Codemasters[1]. +Typical surround sound setups, like quad, 5.1, 6.1, and 7.1, only produce audio +on a 2D horizontal plane with no verticality, which means the envelopment of +"surround" sound is limited to left, right, front, and back panning. Sounds +that should come from above or below will still only play in 2D since there is +no height difference in the speaker array. + +To work around this, 3D7.1 was designed so that some speakers are placed higher +than the listener while others are lower, in a particular configuration that +tries to provide balanced output and maintain some compatibility with existing +audio content and software. Software that recognizes this setup, or can be +configured for it, can then take advantage of the height difference and +increase the perception of verticality for true 3D audio. The result is that +sounds can be perceived as coming from left, right, front, and back, as well as +up and down. + +[1] http://www.codemasters.com/research/3D_sound_for_3D_games.pdf + + +Hardware Setup +============== + +Setting up 3D7.1 requires an audio device capable of raw 8-channel or 7.1 +output, along with a 7.1 speaker kit. The speakers should be hooked up to the +device in the usual way, with front-left and front-right output going to the +front-left and front-right speakers, etc. The placement of the speakers should +be set up according to the table below. Azimuth is the horizontal angle in +degrees, with 0 directly in front and positive values go /left/, and elevation +is the vertical angle in degrees, with 0 at head level and positive values go +/up/. + +------------------------------------------------------------ +- Speaker label | Azimuth | Elevation | New label - +------------------------------------------------------------ +- Front left | 51 | 24 | Upper front left - +- Front right | -51 | 24 | Upper front right - +- Front center | 0 | 0 | Front center - +- Subwoofer/LFE | N/A | N/A | Subwoofer/LFE - +- Side left | 129 | -24 | Lower back left - +- Side right | -129 | -24 | Lower back right - +- Back left | 180 | 55 | Upper back center - +- Back right | 0 | -55 | Lower front center - +------------------------------------------------------------ + +Note that this speaker layout *IS NOT* compatible with standard 7.1 content. +Audio that should be played from the back will come out at the wrong location +since the back speakers are placed in the lower front and upper back positions. +However, this speaker layout *IS* more or less compatible with standard 5.1 +content. Though slightly tilted, to a listener sitting a bit further back from +the center, the front and side speakers will be close enough to their intended +locations that the output won't be too off. + + +Software Setup +============== + +To enable 3D7.1 on OpenAL Soft, first make sure the audio device is configured +for 7.1 output. Then in the alsoft-config utility, under the Renderer tab, +select the 3D7.1.ambdec preset for the 7.1 Surround decoder configuration. And +that's it. Any applications using OpenAL Soft can take advantage of fully 3D +audio, and multi-channel sounds will be properly remixed for the speaker +layout. + +Playback can be improved by (copying and) modifying the 3D7.1.ambdec preset, +changing the specified speaker distances to match the the real distance (in +meters) from the center of the speaker array, then enable High Quality Mode in +alsoft-config. That will improve the quality when the speakers are not all +equidistant. + +Note that care must be taken that the audio device is not treated as a "true" +7.1 device by non-3D7.1-capable applications. In particular, the audio server +should not try to upmix stereo and 5.1 content to "fill out" the back speakers, +and non-3D7.1 apps should be set to either stereo or 5.1 output. + +As such, if your system is capable of it, it may be useful to define a virtual +5.1 device that maps the front, side, and LFE channels to the main device for +output and disables upmixing, then use that virtual 5.1 device for apps that do +normal stereo or surround sound output, and use the main device for apps that +understand 3D7.1 output. diff --git a/libs/openal-soft/docs/ambdec.txt b/libs/openal-soft/docs/ambdec.txt new file mode 100644 index 00000000..1f328937 --- /dev/null +++ b/libs/openal-soft/docs/ambdec.txt @@ -0,0 +1,189 @@ +AmbDec Configuration Files +========================== + +AmbDec configuration files were developed by Fons Adriaensen as part of the +AmbDec program . + +The file works by specifying a decoder matrix or matrices which transform +ambisonic channels into speaker feeds. Single-band decoders specify a single +matrix that transforms all frequencies, while dual-band decoders specifies two +matrices where one transforms low frequency sounds and the other transforms +high frequency sounds. See docs/ambisonics.txt for more general information +about ambisonics. + +Starting with OpenAL Soft 1.18, version 3 of the file format is supported as a +means of specifying custom surround sound speaker layouts. These configuration +files are also used to enable the high-quality ambisonic decoder. + + +File Format +=========== + +As of this writing, there is no official documentation of the .ambdec file +format. However, the format as OpenAL Soft sees it is as follows: + +The file is plain text. Comments start with a hash/pound character (#). There +may be any amount of whitespace in between the option and parameter values. +Strings are *not* enclosed in quotation marks. + +/description +Specifies a text description of the configuration. Ignored by OpenAL Soft. + +/version +Declares the format version used by the configuration file. OpenAL Soft +currently only supports version 3. + +/dec/chan_mask +Specifies a hexadecimal mask value of ambisonic input channels used by this +decoder. Counting up from the least significant bit, bit 0 maps to Ambisonic +Channel Number (ACN) 0, bit 1 maps to ACN 1, etc. As an example, a value of 'b' +enables bits 0, 1, and 3 (1011 in binary), which correspond to ACN 0, 1, and 3 +(first-order horizontal). + +/dec/freq_bands +Specifies the number of frequency bands used by the decoder. This must be 1 for +single-band or 2 for dual-band. + +/dec/speakers +Specifies the number of output speakers to decode to. + +/dec/coeff_scale +Specifies the scaling used by the decoder coefficients. Currently recognized +types are fuma, sn3d, and n3d, for Furse-Malham (FuMa), semi-normalized (SN3D), +and fully normalized (N3D) scaling, respectively. + +/opt/input_scale +Specifies the scaling used by the ambisonic input data. As OpenAL Soft renders +the data itself and knows the scaling, this is ignored. + +/opt/nfeff_comp +Specifies whether near-field effect compensation is off (not applied at all), +applied on input (faster, less accurate with varying speaker distances) or +output (slower, more accurate with varying speaker distances). Ignored by +OpenAL Soft. + +/opt/delay_comp +Specifies whether delay compensation is applied for output. This is used to +correct for time variations caused by different speaker distances. As OpenAL +Soft has its own config option for this, this is ignored. + +/opt/level_comp +Specifies whether gain compensation is applied for output. This is used to +correct for volume variations caused by different speaker distances. As OpenAL +Soft has its own config option for this, this is ignored. + +/opt/xover_freq +Specifies the crossover frequency for dual-band decoders. Frequencies less than +this are fed to the low-frequency matrix, and frequencies greater than this are +fed to the high-frequency matrix. Unused for single-band decoders. + +/opt/xover_ratio +Specifies the volume ratio between the frequency bands. Values greater than 0 +decrease the low-frequency output by half the specified value and increase the +high-frequency output by half the specified value, while values less than 0 +increase the low-frequency output and decrease the high-frequency output to +similar effect. Unused for single-band decoders. + +/speakers/{ +Begins the output speaker definitions. A speaker is defined using the add_spkr +command, and there must be a matching number of speaker definitions as the +specified speaker count. The definitions are ended with a "/}". + +add_spkr +Defines an output speaker. The ID is a string identifier for the output speaker +(see Speaker IDs below). The distance is in meters from the center-point of the +physical speaker array. The azimuth is the horizontal angle of the speaker, in +degrees, where 0 is directly front and positive values go left. The elevation +is the vertical angle of the speaker, in degrees, where 0 is directly front and +positive goes upward. The connection string is the JACK port name the speaker +should connect to. Currently, OpenAL Soft uses the ID and distance, and ignores +the rest. + +/lfmatrix/{ +Begins the low-frequency decoder matrix definition. The definition should +include an order_gain command to specify the base gain for the ambisonic +orders. Each matrix row is defined using the add_row command, and there must be +a matching number of rows as the number of speakers. Additionally the row +definitions are in the same order as the speaker definitions. The definitions +are ended with a "/}". Only valid for dual-band decoders. + +/hfmatrix/{ +Begins the high-frequency decoder matrix definition. The definition should +include an order_gain command to specify the base gain for the ambisonic +orders. Each matrix row is defined using the add_row command, and there must be +a matching number of rows as the number of speakers, Additionally the row +definitions are in the same order as the speaker definitions. The definitions +are ended with a "/}". Only valid for dual-band decoders. + +/matrix/{ +Begins the decoder matrix definition. The definition should include an +order_gain command to specify the base gain for the ambisonic orders. Each +matrix row is defined using the add_row command, and there must be a matching +number of rows as the number of speakers. Additionally the row definitions are +in the same order as the speaker definitions. The definitions are ended with a +"/}". Only valid for single-band decoders. + +order_gain +Specifies the base gain for the zeroth-, first-, second-, and third-order +coefficients in the given matrix, automatically scaling the related +coefficients. This should be specified at the beginning of the matrix +definition. + +add_row ... +Specifies a row of coefficients for the matrix. There should be one coefficient +for each enabled bit in the channel mask, and corresponds to the matching ACN +channel. + +/end +Marks the end of the configuration file. + + +Speaker IDs +=========== + +The AmbDec program uses the speaker ID as a label to display in its config +dialog, but does not otherwise use it for any particular purpose. However, +since OpenAL Soft needs to match a speaker definition to an output channel, the +speaker ID is used to identify what output channel it correspond to. Therefore, +OpenAL Soft requires these channel labels to be recognized: + +LF = Front left +RF = Front right +LS = Side left +RS = Side right +LB = Back left +RB = Back right +CE = Front center +CB = Back center + +Additionally, configuration files for surround51 will acknowledge back speakers +for side channels, and surround51rear will acknowledge side speakers for back +channels, to avoid issues with a configuration expecting 5.1 to use the side +channels when the device is configured for back, or vice-versa. + +Furthermore, OpenAL Soft does not require a speaker definition for each output +channel the configuration is used with. So for example a 5.1 configuration may +omit a front center speaker definition, in which case the front center output +channel will not contribute to the ambisonic decode (though OpenAL Soft will +still use it in certain scenarios, such as the AL_EFFECT_DEDICATED_DIALOGUE +effect). + + +Creating Configuration Files +============================ + +Configuration files can be created or modified by hand in a text editor. The +AmbDec program also has a GUI for creating and editing them. However, these +methods rely on you having the coefficients to fill in... they won't be +generated for you. + +Another option is to use the Ambisonic Decoder Toolbox +. This is a collection of +MATLAB and GNU Octave scripts that can generate AmbDec configuration files from +an array of speaker definitions (labels and positions). If you're familiar with +using MATLAB or GNU Octave, this may be a good option. + +There are plans for OpenAL Soft to include a utility to generate coefficients +and make configuration files. However, calculating proper coefficients for +anything other than regular or semi-regular speaker setups is somewhat of a +black art, so may take some time. diff --git a/libs/openal-soft/docs/ambisonics.txt b/libs/openal-soft/docs/ambisonics.txt new file mode 100644 index 00000000..b50e76c2 --- /dev/null +++ b/libs/openal-soft/docs/ambisonics.txt @@ -0,0 +1,126 @@ +OpenAL Soft's renderer has advanced quite a bit since its start with panned +stereo output. Among these advancements is support for surround sound output, +using psychoacoustic modeling and more accurate plane wave reconstruction. The +concepts in use may not be immediately obvious to people just getting into 3D +audio, or people who only have more indirect experience through the use of 3D +audio APIs, so this document aims to introduce the ideas and purpose of +Ambisonics as used by OpenAL Soft. + + +What Is It? +=========== + +Originally developed in the 1970s by Michael Gerzon and a team others, +Ambisonics was created as a means of recording and playing back 3D sound. +Taking advantage of the way sound waves propogate, it is possible to record a +fully 3D soundfield using as few as 4 channels (or even just 3, if you don't +mind dropping down to 2 dimensions like many surround sound systems are). This +representation is called B-Format. It was designed to handle audio independent +of any specific speaker layout, so with a proper decoder the same recording can +be played back on a variety of speaker setups, from quadrophonic and hexagonal +to cubic and other periphonic (with height) layouts. + +Although it was developed decades ago, various factors held ambisonics back +from really taking hold in the consumer market. However, given the solid +theories backing it, as well as the potential and practical benefits on offer, +it continued to be a topic of research over the years, with improvements being +made over the original design. One of the improvements made is the use of +Spherical Harmonics to increase the number of channels for greater spatial +definition. Where the original 4-channel design is termed as "First-Order +Ambisonics", or FOA, the increased channel count through the use of Spherical +Harmonics is termed as "Higher-Order Ambisonics", or HOA. The details of higher +order ambisonics are out of the scope of this document, but know that the added +channels are still independent of any speaker layout, and aim to further +improve the spatial detail for playback. + +Today, the processing power available on even low-end computers means real-time +Ambisonics processing is possible. Not only can decoders be implemented in +software, but so can encoders, synthesizing a soundfield using multiple panned +sources, thus taking advantage of what ambisonics offers in a virtual audio +environment. + + +How Does It Help? +================= + +Positional sound has come a long way from pan-pot stereo (aka pair-wise). +Although useful at the time, the issues became readily apparent when trying to +extend it for surround sound. Pan-pot doesn't work as well for depth (front- +back) or vertical panning, it has a rather small "sweet spot" (the area the +head needs to be in to perceive the sound in its intended direction), and it +misses key distance-related details of sound waves. + +Ambisonics takes a different approach. It uses all available speakers to help +localize a sound, and it also takes into account how the brain localizes low +frequency sounds compared to high frequency ones -- a so-called psychoacoustic +model. It may seem counter-intuitive (if a sound is coming from the front-left, +surely just play it on the front-left speaker?), but to properly model a sound +coming from where a speaker doesn't exist, more needs to be done to construct a +proper sound wave that's perceived to come from the intended direction. Doing +this creates a larger sweet spot, allowing the perceived sound direction to +remain correct over a larger area around the center of the speakers. + +In addition, Ambisonics can encode the near-field effect of sounds, effectively +capturing the sound distance. The near-field effect is a subtle low-frequency +boost as a result of wave-front curvature, and properly compensating for this +occuring with the output speakers (as well as emulating it with a synthesized +soundfield) can create an improved sense of distance for sounds that move near +or far. + + +How Is It Used? +=============== + +As a 3D audio API, OpenAL is tasked with playing 3D sound as best it can with +the speaker setup the user has. Since the OpenAL API does not explicitly handle +the output channel configuration, it has a lot of leeway in how to deal with +the audio before it's played back for the user to hear. Consequently, OpenAL +Soft (or any other OpenAL implementation that wishes to) can render using +Ambisonics and decode the ambisonic mix for a high level of accuracy over what +simple pan-pot could provide. + +This is effectively what the high-quality mode option does, when given an +appropriate decoder configuation for the playback channel layout. 3D rendering +and effect mixing is done to an ambisonic buffer, which is later decoded for +output utilizing the benefits available to ambisonic processing. + +The basic, non-high-quality, renderer uses similar principles, however it skips +the frequency-dependent processing (so low frequency sounds are treated the +same as high frequency sounds) and does some creative manipulation of the +involved math to skip the intermediate ambisonic buffer, rendering more +directly to the output while still taking advantage of all the available +speakers to reconstruct the sound wave. This method trades away some playback +quality for less memory and processor usage. + +In addition to providing good support for surround sound playback, Ambisonics +also has benefits with stereo output. 2-channel UHJ is a stereo-compatible +format that encodes some surround sound information using a wide-band 90-degree +phase shift filter. It works by taking a B-Format signal, and deriving a +frontal stereo mix with the rear sounds attenuated and filtered in with it. +Although the result is not as good as 3-channel (2D) B-Format, it has the +distinct advantage of only using 2 channels and being compatible with stereo +output. This means it will sound just fine when played as-is through a normal +stereo device, or it may optionally be fed to a properly configured surround +sound receiver which can extract the encoded information and restore some of +the original surround sound signal. + + +What Are Its Limitations? +========================= + +As good as Ambisonics is, it's not a magic bullet that can overcome all +problems. One of the bigger issues it has is dealing with irregular speaker +setups, such as 5.1 surround sound. The problem mainly lies in the imbalanced +speaker positioning -- there are three speakers within the front 60-degree area +(meaning only 30-degree gaps in between each of the three speakers), while only +two speakers cover the back 140-degree area, leaving 80-degree gaps on the +sides. It should be noted that this problem is inherent to the speaker layout +itself; there isn't much that can be done to get an optimal surround sound +response, with ambisonics or not. It will do the best it can, but there are +trade-offs between detail and accuracy. + +Another issue lies with HRTF. While it's certainly possible to play an +ambisonic mix using HRTF and retain a sense of 3D sound, doing so with a high +degree of spatial detail requires a fair amount of resources, in both memory +and processing time. And even with it, mixing sounds with HRTF directly will +still be better for positional accuracy. diff --git a/libs/openal-soft/env-vars.txt b/libs/openal-soft/docs/env-vars.txt similarity index 100% rename from libs/openal-soft/env-vars.txt rename to libs/openal-soft/docs/env-vars.txt diff --git a/libs/openal-soft/hrtf.txt b/libs/openal-soft/docs/hrtf.txt similarity index 100% rename from libs/openal-soft/hrtf.txt rename to libs/openal-soft/docs/hrtf.txt diff --git a/libs/openal-soft/examples/alffplay.c b/libs/openal-soft/examples/alffplay.c deleted file mode 100644 index ab67d216..00000000 --- a/libs/openal-soft/examples/alffplay.c +++ /dev/null @@ -1,1533 +0,0 @@ -/* - * alffplay.c - * - * A pedagogical video player that really works! Now with seeking features. - * - * Code based on FFplay, Copyright (c) 2003 Fabrice Bellard, and a tutorial by - * Martin Bohme . - * - * Requires C99. - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "threads.h" -#include "bool.h" - -#include "AL/al.h" -#include "AL/alc.h" -#include "AL/alext.h" - - -static bool has_latency_check = false; -static LPALGETSOURCEDVSOFT alGetSourcedvSOFT; - -#define AUDIO_BUFFER_TIME 100 /* In milliseconds, per-buffer */ -#define AUDIO_BUFFER_QUEUE_SIZE 8 /* Number of buffers to queue */ -#define MAX_AUDIOQ_SIZE (5 * 16 * 1024) /* Bytes of compressed audio data to keep queued */ -#define MAX_VIDEOQ_SIZE (5 * 256 * 1024) /* Bytes of compressed video data to keep queued */ -#define AV_SYNC_THRESHOLD 0.01 -#define AV_NOSYNC_THRESHOLD 10.0 -#define SAMPLE_CORRECTION_MAX_DIFF 0.1 -#define AUDIO_DIFF_AVG_NB 20 -#define VIDEO_PICTURE_QUEUE_SIZE 16 - -enum { - FF_UPDATE_EVENT = SDL_USEREVENT, - FF_REFRESH_EVENT, - FF_QUIT_EVENT -}; - - -typedef struct PacketQueue { - AVPacketList *first_pkt, *last_pkt; - volatile int nb_packets; - volatile int size; - volatile bool flushing; - almtx_t mutex; - alcnd_t cond; -} PacketQueue; - -typedef struct VideoPicture { - SDL_Texture *bmp; - int width, height; /* Logical image size (actual size may be larger) */ - volatile bool updated; - double pts; -} VideoPicture; - -typedef struct AudioState { - AVStream *st; - - PacketQueue q; - AVPacket pkt; - - /* Used for clock difference average computation */ - double diff_accum; - double diff_avg_coef; - double diff_threshold; - - /* Time (in seconds) of the next sample to be buffered */ - double current_pts; - - /* Decompressed sample frame, and swresample context for conversion */ - AVFrame *decoded_aframe; - struct SwrContext *swres_ctx; - - /* Conversion format, for what gets fed to OpenAL */ - int dst_ch_layout; - enum AVSampleFormat dst_sample_fmt; - - /* Storage of converted samples */ - uint8_t *samples; - ssize_t samples_len; /* In samples */ - ssize_t samples_pos; - int samples_max; - - /* OpenAL format */ - ALenum format; - ALint frame_size; - - ALuint source; - ALuint buffer[AUDIO_BUFFER_QUEUE_SIZE]; - ALuint buffer_idx; - almtx_t src_mutex; - - althrd_t thread; -} AudioState; - -typedef struct VideoState { - AVStream *st; - - PacketQueue q; - - double clock; - double frame_timer; - double frame_last_pts; - double frame_last_delay; - double current_pts; - /* time (av_gettime) at which we updated current_pts - used to have running video pts */ - int64_t current_pts_time; - - /* Decompressed video frame, and swscale context for conversion */ - AVFrame *decoded_vframe; - struct SwsContext *swscale_ctx; - - VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE]; - int pictq_size, pictq_rindex, pictq_windex; - almtx_t pictq_mutex; - alcnd_t pictq_cond; - - althrd_t thread; -} VideoState; - -typedef struct MovieState { - AVFormatContext *pFormatCtx; - int videoStream, audioStream; - - volatile bool seek_req; - int64_t seek_pos; - - int av_sync_type; - - int64_t external_clock_base; - - AudioState audio; - VideoState video; - - althrd_t parse_thread; - - char filename[1024]; - - volatile bool quit; -} MovieState; - -enum { - AV_SYNC_AUDIO_MASTER, - AV_SYNC_VIDEO_MASTER, - AV_SYNC_EXTERNAL_MASTER, - - DEFAULT_AV_SYNC_TYPE = AV_SYNC_EXTERNAL_MASTER -}; - -static AVPacket flush_pkt = { .data = (uint8_t*)"FLUSH" }; - -static void packet_queue_init(PacketQueue *q) -{ - memset(q, 0, sizeof(PacketQueue)); - almtx_init(&q->mutex, almtx_plain); - alcnd_init(&q->cond); -} -static int packet_queue_put(PacketQueue *q, AVPacket *pkt) -{ - AVPacketList *pkt1; - if(pkt != &flush_pkt && !pkt->buf && av_dup_packet(pkt) < 0) - return -1; - - pkt1 = av_malloc(sizeof(AVPacketList)); - if(!pkt1) return -1; - pkt1->pkt = *pkt; - pkt1->next = NULL; - - almtx_lock(&q->mutex); - if(!q->last_pkt) - q->first_pkt = pkt1; - else - q->last_pkt->next = pkt1; - q->last_pkt = pkt1; - q->nb_packets++; - q->size += pkt1->pkt.size; - almtx_unlock(&q->mutex); - - alcnd_signal(&q->cond); - return 0; -} -static int packet_queue_get(PacketQueue *q, AVPacket *pkt, MovieState *state) -{ - AVPacketList *pkt1; - int ret = -1; - - almtx_lock(&q->mutex); - while(!state->quit) - { - pkt1 = q->first_pkt; - if(pkt1) - { - q->first_pkt = pkt1->next; - if(!q->first_pkt) - q->last_pkt = NULL; - q->nb_packets--; - q->size -= pkt1->pkt.size; - *pkt = pkt1->pkt; - av_free(pkt1); - ret = 1; - break; - } - - if(q->flushing) - { - ret = 0; - break; - } - alcnd_wait(&q->cond, &q->mutex); - } - almtx_unlock(&q->mutex); - return ret; -} -static void packet_queue_clear(PacketQueue *q) -{ - AVPacketList *pkt, *pkt1; - - almtx_lock(&q->mutex); - for(pkt = q->first_pkt;pkt != NULL;pkt = pkt1) - { - pkt1 = pkt->next; - if(pkt->pkt.data != flush_pkt.data) - av_free_packet(&pkt->pkt); - av_freep(&pkt); - } - q->last_pkt = NULL; - q->first_pkt = NULL; - q->nb_packets = 0; - q->size = 0; - almtx_unlock(&q->mutex); -} -static void packet_queue_flush(PacketQueue *q) -{ - almtx_lock(&q->mutex); - q->flushing = true; - almtx_unlock(&q->mutex); - alcnd_signal(&q->cond); -} -static void packet_queue_deinit(PacketQueue *q) -{ - packet_queue_clear(q); - alcnd_destroy(&q->cond); - almtx_destroy(&q->mutex); -} - - -static double get_audio_clock(AudioState *state) -{ - double pts; - - almtx_lock(&state->src_mutex); - /* The audio clock is the timestamp of the sample currently being heard. - * It's based on 4 components: - * 1 - The timestamp of the next sample to buffer (state->current_pts) - * 2 - The length of the source's buffer queue (AL_SEC_LENGTH_SOFT) - * 3 - The offset OpenAL is currently at in the source (the first value - * from AL_SEC_OFFSET_LATENCY_SOFT) - * 4 - The latency between OpenAL and the DAC (the second value from - * AL_SEC_OFFSET_LATENCY_SOFT) - * - * Subtracting the length of the source queue from the next sample's - * timestamp gives the timestamp of the sample at start of the source - * queue. Adding the source offset to that results in the timestamp for - * OpenAL's current position, and subtracting the source latency from that - * gives the timestamp of the sample currently at the DAC. - */ - pts = state->current_pts; - if(state->source) - { - ALdouble offset[2] = { 0.0, 0.0 }; - ALdouble queue_len = 0.0; - ALint status; - - /* NOTE: The source state must be checked last, in case an underrun - * occurs and the source stops between retrieving the offset+latency - * and getting the state. */ - if(has_latency_check) - { - alGetSourcedvSOFT(state->source, AL_SEC_OFFSET_LATENCY_SOFT, offset); - alGetSourcedvSOFT(state->source, AL_SEC_LENGTH_SOFT, &queue_len); - } - else - { - ALint ioffset, ilen; - alGetSourcei(state->source, AL_SAMPLE_OFFSET, &ioffset); - alGetSourcei(state->source, AL_SAMPLE_LENGTH_SOFT, &ilen); - offset[0] = (double)ioffset / state->st->codec->sample_rate; - queue_len = (double)ilen / state->st->codec->sample_rate; - } - alGetSourcei(state->source, AL_SOURCE_STATE, &status); - - /* If the source is AL_STOPPED, then there was an underrun and all - * buffers are processed, so ignore the source queue. The audio thread - * will put the source into an AL_INITIAL state and clear the queue - * when it starts recovery. */ - if(status != AL_STOPPED) - pts = pts - queue_len + offset[0]; - if(status == AL_PLAYING) - pts = pts - offset[1]; - } - almtx_unlock(&state->src_mutex); - - return (pts >= 0.0) ? pts : 0.0; -} -static double get_video_clock(VideoState *state) -{ - double delta = (av_gettime() - state->current_pts_time) / 1000000.0; - return state->current_pts + delta; -} -static double get_external_clock(MovieState *movState) -{ - return (av_gettime()-movState->external_clock_base) / 1000000.0; -} - -double get_master_clock(MovieState *movState) -{ - if(movState->av_sync_type == AV_SYNC_VIDEO_MASTER) - return get_video_clock(&movState->video); - if(movState->av_sync_type == AV_SYNC_AUDIO_MASTER) - return get_audio_clock(&movState->audio); - return get_external_clock(movState); -} - -/* Return how many samples to skip to maintain sync (negative means to - * duplicate samples). */ -static int synchronize_audio(MovieState *movState) -{ - double diff, avg_diff; - double ref_clock; - - if(movState->av_sync_type == AV_SYNC_AUDIO_MASTER) - return 0; - - ref_clock = get_master_clock(movState); - diff = ref_clock - get_audio_clock(&movState->audio); - - if(!(diff < AV_NOSYNC_THRESHOLD)) - { - /* Difference is TOO big; reset diff stuff */ - movState->audio.diff_accum = 0.0; - return 0; - } - - /* Accumulate the diffs */ - movState->audio.diff_accum = movState->audio.diff_accum*movState->audio.diff_avg_coef + diff; - avg_diff = movState->audio.diff_accum*(1.0 - movState->audio.diff_avg_coef); - if(fabs(avg_diff) < movState->audio.diff_threshold) - return 0; - - /* Constrain the per-update difference to avoid exceedingly large skips */ - if(!(diff <= SAMPLE_CORRECTION_MAX_DIFF)) - diff = SAMPLE_CORRECTION_MAX_DIFF; - else if(!(diff >= -SAMPLE_CORRECTION_MAX_DIFF)) - diff = -SAMPLE_CORRECTION_MAX_DIFF; - return (int)(diff*movState->audio.st->codec->sample_rate); -} - -static int audio_decode_frame(MovieState *movState) -{ - AVPacket *pkt = &movState->audio.pkt; - - while(!movState->quit) - { - while(!movState->quit && pkt->size == 0) - { - av_free_packet(pkt); - - /* Get the next packet */ - int err; - if((err=packet_queue_get(&movState->audio.q, pkt, movState)) <= 0) - { - if(err == 0) - break; - return err; - } - if(pkt->data == flush_pkt.data) - { - avcodec_flush_buffers(movState->audio.st->codec); - movState->audio.diff_accum = 0.0; - movState->audio.current_pts = av_q2d(movState->audio.st->time_base)*pkt->pts; - - alSourceRewind(movState->audio.source); - alSourcei(movState->audio.source, AL_BUFFER, 0); - - av_new_packet(pkt, 0); - - return -1; - } - - /* If provided, update w/ pts */ - if(pkt->pts != AV_NOPTS_VALUE) - movState->audio.current_pts = av_q2d(movState->audio.st->time_base)*pkt->pts; - } - - AVFrame *frame = movState->audio.decoded_aframe; - int got_frame = 0; - int len1 = avcodec_decode_audio4(movState->audio.st->codec, frame, - &got_frame, pkt); - if(len1 < 0) break; - - if(len1 <= pkt->size) - { - /* Move the unread data to the front and clear the end bits */ - int remaining = pkt->size - len1; - memmove(pkt->data, &pkt->data[len1], remaining); - av_shrink_packet(pkt, remaining); - } - - if(!got_frame || frame->nb_samples <= 0) - { - av_frame_unref(frame); - continue; - } - - if(frame->nb_samples > movState->audio.samples_max) - { - av_freep(&movState->audio.samples); - av_samples_alloc( - &movState->audio.samples, NULL, movState->audio.st->codec->channels, - frame->nb_samples, movState->audio.dst_sample_fmt, 0 - ); - movState->audio.samples_max = frame->nb_samples; - } - /* Return the amount of sample frames converted */ - int data_size = swr_convert(movState->audio.swres_ctx, - &movState->audio.samples, frame->nb_samples, - (const uint8_t**)frame->data, frame->nb_samples - ); - - av_frame_unref(frame); - return data_size; - } - - return -1; -} - -static int read_audio(MovieState *movState, uint8_t *samples, int length) -{ - int sample_skip = synchronize_audio(movState); - int audio_size = 0; - - /* Read the next chunk of data, refill the buffer, and queue it - * on the source */ - length /= movState->audio.frame_size; - while(audio_size < length) - { - if(movState->audio.samples_len <= 0 || movState->audio.samples_pos >= movState->audio.samples_len) - { - int frame_len = audio_decode_frame(movState); - if(frame_len < 0) return -1; - - movState->audio.samples_len = frame_len; - if(movState->audio.samples_len == 0) - break; - - movState->audio.samples_pos = (movState->audio.samples_len < sample_skip) ? - movState->audio.samples_len : sample_skip; - sample_skip -= movState->audio.samples_pos; - - movState->audio.current_pts += (double)movState->audio.samples_pos / - (double)movState->audio.st->codec->sample_rate; - continue; - } - - int rem = length - audio_size; - if(movState->audio.samples_pos >= 0) - { - int n = movState->audio.frame_size; - int len = movState->audio.samples_len - movState->audio.samples_pos; - if(rem > len) rem = len; - memcpy(samples + audio_size*n, - movState->audio.samples + movState->audio.samples_pos*n, - rem*n); - } - else - { - int n = movState->audio.frame_size; - int len = -movState->audio.samples_pos; - if(rem > len) rem = len; - - /* Add samples by copying the first sample */ - if(n == 1) - { - uint8_t sample = ((uint8_t*)movState->audio.samples)[0]; - uint8_t *q = (uint8_t*)samples + audio_size; - for(int i = 0;i < rem;i++) - *(q++) = sample; - } - else if(n == 2) - { - uint16_t sample = ((uint16_t*)movState->audio.samples)[0]; - uint16_t *q = (uint16_t*)samples + audio_size; - for(int i = 0;i < rem;i++) - *(q++) = sample; - } - else if(n == 4) - { - uint32_t sample = ((uint32_t*)movState->audio.samples)[0]; - uint32_t *q = (uint32_t*)samples + audio_size; - for(int i = 0;i < rem;i++) - *(q++) = sample; - } - else if(n == 8) - { - uint64_t sample = ((uint64_t*)movState->audio.samples)[0]; - uint64_t *q = (uint64_t*)samples + audio_size; - for(int i = 0;i < rem;i++) - *(q++) = sample; - } - else - { - uint8_t *sample = movState->audio.samples; - uint8_t *q = samples + audio_size*n; - for(int i = 0;i < rem;i++) - { - memcpy(q, sample, n); - q += n; - } - } - } - - movState->audio.samples_pos += rem; - movState->audio.current_pts += (double)rem / movState->audio.st->codec->sample_rate; - audio_size += rem; - } - - return audio_size * movState->audio.frame_size; -} - -static int audio_thread(void *userdata) -{ - MovieState *movState = (MovieState*)userdata; - uint8_t *samples = NULL; - ALsizei buffer_len; - ALenum fmt; - - alGenBuffers(AUDIO_BUFFER_QUEUE_SIZE, movState->audio.buffer); - alGenSources(1, &movState->audio.source); - - alSourcei(movState->audio.source, AL_SOURCE_RELATIVE, AL_TRUE); - alSourcei(movState->audio.source, AL_ROLLOFF_FACTOR, 0); - - av_new_packet(&movState->audio.pkt, 0); - - /* Find a suitable format for OpenAL. */ - movState->audio.format = AL_NONE; - if(movState->audio.st->codec->sample_fmt == AV_SAMPLE_FMT_U8 || - movState->audio.st->codec->sample_fmt == AV_SAMPLE_FMT_U8P) - { - movState->audio.dst_sample_fmt = AV_SAMPLE_FMT_U8; - movState->audio.frame_size = 1; - if(movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_7POINT1 && - alIsExtensionPresent("AL_EXT_MCFORMATS") && - (fmt=alGetEnumValue("AL_FORMAT_71CHN8")) != AL_NONE && fmt != -1) - { - movState->audio.dst_ch_layout = movState->audio.st->codec->channel_layout; - movState->audio.frame_size *= 8; - movState->audio.format = fmt; - } - if((movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_5POINT1 || - movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_5POINT1_BACK) && - alIsExtensionPresent("AL_EXT_MCFORMATS") && - (fmt=alGetEnumValue("AL_FORMAT_51CHN8")) != AL_NONE && fmt != -1) - { - movState->audio.dst_ch_layout = movState->audio.st->codec->channel_layout; - movState->audio.frame_size *= 6; - movState->audio.format = fmt; - } - if(movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_MONO) - { - movState->audio.dst_ch_layout = AV_CH_LAYOUT_MONO; - movState->audio.frame_size *= 1; - movState->audio.format = AL_FORMAT_MONO8; - } - if(movState->audio.format == AL_NONE) - { - movState->audio.dst_ch_layout = AV_CH_LAYOUT_STEREO; - movState->audio.frame_size *= 2; - movState->audio.format = AL_FORMAT_STEREO8; - } - } - if((movState->audio.st->codec->sample_fmt == AV_SAMPLE_FMT_FLT || - movState->audio.st->codec->sample_fmt == AV_SAMPLE_FMT_FLTP) && - alIsExtensionPresent("AL_EXT_FLOAT32")) - { - movState->audio.dst_sample_fmt = AV_SAMPLE_FMT_FLT; - movState->audio.frame_size = 4; - if(movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_7POINT1 && - alIsExtensionPresent("AL_EXT_MCFORMATS") && - (fmt=alGetEnumValue("AL_FORMAT_71CHN32")) != AL_NONE && fmt != -1) - { - movState->audio.dst_ch_layout = movState->audio.st->codec->channel_layout; - movState->audio.frame_size *= 8; - movState->audio.format = fmt; - } - if((movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_5POINT1 || - movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_5POINT1_BACK) && - alIsExtensionPresent("AL_EXT_MCFORMATS") && - (fmt=alGetEnumValue("AL_FORMAT_51CHN32")) != AL_NONE && fmt != -1) - { - movState->audio.dst_ch_layout = movState->audio.st->codec->channel_layout; - movState->audio.frame_size *= 6; - movState->audio.format = fmt; - } - if(movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_MONO) - { - movState->audio.dst_ch_layout = AV_CH_LAYOUT_MONO; - movState->audio.frame_size *= 1; - movState->audio.format = AL_FORMAT_MONO_FLOAT32; - } - if(movState->audio.format == AL_NONE) - { - movState->audio.dst_ch_layout = AV_CH_LAYOUT_STEREO; - movState->audio.frame_size *= 2; - movState->audio.format = AL_FORMAT_STEREO_FLOAT32; - } - } - if(movState->audio.format == AL_NONE) - { - movState->audio.dst_sample_fmt = AV_SAMPLE_FMT_S16; - movState->audio.frame_size = 2; - if(movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_7POINT1 && - alIsExtensionPresent("AL_EXT_MCFORMATS") && - (fmt=alGetEnumValue("AL_FORMAT_71CHN16")) != AL_NONE && fmt != -1) - { - movState->audio.dst_ch_layout = movState->audio.st->codec->channel_layout; - movState->audio.frame_size *= 8; - movState->audio.format = fmt; - } - if((movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_5POINT1 || - movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_5POINT1_BACK) && - alIsExtensionPresent("AL_EXT_MCFORMATS") && - (fmt=alGetEnumValue("AL_FORMAT_51CHN16")) != AL_NONE && fmt != -1) - { - movState->audio.dst_ch_layout = movState->audio.st->codec->channel_layout; - movState->audio.frame_size *= 6; - movState->audio.format = fmt; - } - if(movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_MONO) - { - movState->audio.dst_ch_layout = AV_CH_LAYOUT_MONO; - movState->audio.frame_size *= 1; - movState->audio.format = AL_FORMAT_MONO16; - } - if(movState->audio.format == AL_NONE) - { - movState->audio.dst_ch_layout = AV_CH_LAYOUT_STEREO; - movState->audio.frame_size *= 2; - movState->audio.format = AL_FORMAT_STEREO16; - } - } - buffer_len = AUDIO_BUFFER_TIME * movState->audio.st->codec->sample_rate / 1000 * - movState->audio.frame_size; - samples = av_malloc(buffer_len); - - movState->audio.samples = NULL; - movState->audio.samples_max = 0; - movState->audio.samples_pos = 0; - movState->audio.samples_len = 0; - - if(!(movState->audio.decoded_aframe=av_frame_alloc())) - { - fprintf(stderr, "Failed to allocate audio frame\n"); - goto finish; - } - - movState->audio.swres_ctx = swr_alloc_set_opts(NULL, - movState->audio.dst_ch_layout, - movState->audio.dst_sample_fmt, - movState->audio.st->codec->sample_rate, - movState->audio.st->codec->channel_layout ? - movState->audio.st->codec->channel_layout : - (uint64_t)av_get_default_channel_layout(movState->audio.st->codec->channels), - movState->audio.st->codec->sample_fmt, - movState->audio.st->codec->sample_rate, - 0, NULL - ); - if(!movState->audio.swres_ctx || swr_init(movState->audio.swres_ctx) != 0) - { - fprintf(stderr, "Failed to initialize audio converter\n"); - goto finish; - } - - almtx_lock(&movState->audio.src_mutex); - while(alGetError() == AL_NO_ERROR && !movState->quit) - { - /* First remove any processed buffers. */ - ALint processed; - alGetSourcei(movState->audio.source, AL_BUFFERS_PROCESSED, &processed); - alSourceUnqueueBuffers(movState->audio.source, processed, (ALuint[AUDIO_BUFFER_QUEUE_SIZE]){}); - - /* Refill the buffer queue. */ - ALint queued; - alGetSourcei(movState->audio.source, AL_BUFFERS_QUEUED, &queued); - while(queued < AUDIO_BUFFER_QUEUE_SIZE) - { - int audio_size; - - /* Read the next chunk of data, fill the buffer, and queue it on - * the source */ - audio_size = read_audio(movState, samples, buffer_len); - if(audio_size < 0) break; - - ALuint bufid = movState->audio.buffer[movState->audio.buffer_idx++]; - movState->audio.buffer_idx %= AUDIO_BUFFER_QUEUE_SIZE; - - alBufferData(bufid, movState->audio.format, samples, audio_size, - movState->audio.st->codec->sample_rate); - alSourceQueueBuffers(movState->audio.source, 1, &bufid); - queued++; - } - - /* Check that the source is playing. */ - ALint state; - alGetSourcei(movState->audio.source, AL_SOURCE_STATE, &state); - if(state == AL_STOPPED) - { - /* AL_STOPPED means there was an underrun. Double-check that all - * processed buffers are removed, then rewind the source to get it - * back into an AL_INITIAL state. */ - alGetSourcei(movState->audio.source, AL_BUFFERS_PROCESSED, &processed); - alSourceUnqueueBuffers(movState->audio.source, processed, (ALuint[AUDIO_BUFFER_QUEUE_SIZE]){}); - alSourceRewind(movState->audio.source); - continue; - } - - almtx_unlock(&movState->audio.src_mutex); - - /* (re)start the source if needed, and wait for a buffer to finish */ - if(state != AL_PLAYING && state != AL_PAUSED) - { - alGetSourcei(movState->audio.source, AL_BUFFERS_QUEUED, &queued); - if(queued > 0) alSourcePlay(movState->audio.source); - } - SDL_Delay(AUDIO_BUFFER_TIME); - - almtx_lock(&movState->audio.src_mutex); - } - almtx_unlock(&movState->audio.src_mutex); - -finish: - av_frame_free(&movState->audio.decoded_aframe); - swr_free(&movState->audio.swres_ctx); - - av_freep(&samples); - av_freep(&movState->audio.samples); - - alDeleteSources(1, &movState->audio.source); - alDeleteBuffers(AUDIO_BUFFER_QUEUE_SIZE, movState->audio.buffer); - - return 0; -} - - -static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque) -{ - (void)interval; - - SDL_PushEvent(&(SDL_Event){ .user={.type=FF_REFRESH_EVENT, .data1=opaque} }); - return 0; /* 0 means stop timer */ -} - -/* Schedule a video refresh in 'delay' ms */ -static void schedule_refresh(MovieState *movState, int delay) -{ - SDL_AddTimer(delay, sdl_refresh_timer_cb, movState); -} - -static void video_display(MovieState *movState, SDL_Window *screen, SDL_Renderer *renderer) -{ - VideoPicture *vp = &movState->video.pictq[movState->video.pictq_rindex]; - - if(!vp->bmp) - return; - - float aspect_ratio; - int win_w, win_h; - int w, h, x, y; - - if(movState->video.st->codec->sample_aspect_ratio.num == 0) - aspect_ratio = 0.0f; - else - { - aspect_ratio = av_q2d(movState->video.st->codec->sample_aspect_ratio) * - movState->video.st->codec->width / - movState->video.st->codec->height; - } - if(aspect_ratio <= 0.0f) - { - aspect_ratio = (float)movState->video.st->codec->width / - (float)movState->video.st->codec->height; - } - - SDL_GetWindowSize(screen, &win_w, &win_h); - h = win_h; - w = ((int)rint(h * aspect_ratio) + 3) & ~3; - if(w > win_w) - { - w = win_w; - h = ((int)rint(w / aspect_ratio) + 3) & ~3; - } - x = (win_w - w) / 2; - y = (win_h - h) / 2; - - SDL_RenderCopy(renderer, vp->bmp, - &(SDL_Rect){ .x=0, .y=0, .w=vp->width, .h=vp->height }, - &(SDL_Rect){ .x=x, .y=y, .w=w, .h=h } - ); - SDL_RenderPresent(renderer); -} - -static void video_refresh_timer(MovieState *movState, SDL_Window *screen, SDL_Renderer *renderer) -{ - if(!movState->video.st) - { - schedule_refresh(movState, 100); - return; - } - - almtx_lock(&movState->video.pictq_mutex); -retry: - if(movState->video.pictq_size == 0) - schedule_refresh(movState, 1); - else - { - VideoPicture *vp = &movState->video.pictq[movState->video.pictq_rindex]; - double actual_delay, delay, sync_threshold, ref_clock, diff; - - movState->video.current_pts = vp->pts; - movState->video.current_pts_time = av_gettime(); - - delay = vp->pts - movState->video.frame_last_pts; /* the pts from last time */ - if(delay <= 0 || delay >= 1.0) - { - /* if incorrect delay, use previous one */ - delay = movState->video.frame_last_delay; - } - /* save for next time */ - movState->video.frame_last_delay = delay; - movState->video.frame_last_pts = vp->pts; - - /* Update delay to sync to clock if not master source. */ - if(movState->av_sync_type != AV_SYNC_VIDEO_MASTER) - { - ref_clock = get_master_clock(movState); - diff = vp->pts - ref_clock; - - /* Skip or repeat the frame. Take delay into account. */ - sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; - if(fabs(diff) < AV_NOSYNC_THRESHOLD) - { - if(diff <= -sync_threshold) - delay = 0; - else if(diff >= sync_threshold) - delay = 2 * delay; - } - } - - movState->video.frame_timer += delay; - /* Compute the REAL delay. */ - actual_delay = movState->video.frame_timer - (av_gettime() / 1000000.0); - if(!(actual_delay >= 0.010)) - { - /* We don't have time to handle this picture, just skip to the next one. */ - movState->video.pictq_rindex = (movState->video.pictq_rindex+1)%VIDEO_PICTURE_QUEUE_SIZE; - movState->video.pictq_size--; - alcnd_signal(&movState->video.pictq_cond); - goto retry; - } - schedule_refresh(movState, (int)(actual_delay*1000.0 + 0.5)); - - /* Show the picture! */ - video_display(movState, screen, renderer); - - /* Update queue for next picture. */ - movState->video.pictq_rindex = (movState->video.pictq_rindex+1)%VIDEO_PICTURE_QUEUE_SIZE; - movState->video.pictq_size--; - alcnd_signal(&movState->video.pictq_cond); - } - almtx_unlock(&movState->video.pictq_mutex); -} - - -static void update_picture(MovieState *movState, bool *first_update, SDL_Window *screen, SDL_Renderer *renderer) -{ - VideoPicture *vp = &movState->video.pictq[movState->video.pictq_windex]; - - /* allocate or resize the buffer! */ - if(!vp->bmp || vp->width != movState->video.st->codec->width || - vp->height != movState->video.st->codec->height) - { - if(vp->bmp) - SDL_DestroyTexture(vp->bmp); - vp->bmp = SDL_CreateTexture( - renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, - movState->video.st->codec->coded_width, movState->video.st->codec->coded_height - ); - if(!vp->bmp) - fprintf(stderr, "Failed to create YV12 texture!\n"); - vp->width = movState->video.st->codec->width; - vp->height = movState->video.st->codec->height; - - if(*first_update && vp->width > 0 && vp->height > 0) - { - /* For the first update, set the window size to the video size. */ - *first_update = false; - - int w = vp->width; - int h = vp->height; - if(movState->video.st->codec->sample_aspect_ratio.num != 0 && - movState->video.st->codec->sample_aspect_ratio.den != 0) - { - double aspect_ratio = av_q2d(movState->video.st->codec->sample_aspect_ratio); - if(aspect_ratio >= 1.0) - w = (int)(w*aspect_ratio + 0.5); - else if(aspect_ratio > 0.0) - h = (int)(h/aspect_ratio + 0.5); - } - SDL_SetWindowSize(screen, w, h); - } - } - - if(vp->bmp) - { - AVFrame *frame = movState->video.decoded_vframe; - void *pixels = NULL; - int pitch = 0; - - if(movState->video.st->codec->pix_fmt == PIX_FMT_YUV420P) - SDL_UpdateYUVTexture(vp->bmp, NULL, - frame->data[0], frame->linesize[0], - frame->data[1], frame->linesize[1], - frame->data[2], frame->linesize[2] - ); - else if(SDL_LockTexture(vp->bmp, NULL, &pixels, &pitch) != 0) - fprintf(stderr, "Failed to lock texture\n"); - else - { - // Convert the image into YUV format that SDL uses - int coded_w = movState->video.st->codec->coded_width; - int coded_h = movState->video.st->codec->coded_height; - int w = movState->video.st->codec->width; - int h = movState->video.st->codec->height; - if(!movState->video.swscale_ctx) - movState->video.swscale_ctx = sws_getContext( - w, h, movState->video.st->codec->pix_fmt, - w, h, PIX_FMT_YUV420P, SWS_X, NULL, NULL, NULL - ); - - /* point pict at the queue */ - AVPicture pict; - pict.data[0] = pixels; - pict.data[2] = pict.data[0] + coded_w*coded_h; - pict.data[1] = pict.data[2] + coded_w*coded_h/4; - - pict.linesize[0] = pitch; - pict.linesize[2] = pitch / 2; - pict.linesize[1] = pitch / 2; - - sws_scale(movState->video.swscale_ctx, (const uint8_t**)frame->data, - frame->linesize, 0, h, pict.data, pict.linesize); - SDL_UnlockTexture(vp->bmp); - } - } - - almtx_lock(&movState->video.pictq_mutex); - vp->updated = true; - almtx_unlock(&movState->video.pictq_mutex); - alcnd_signal(&movState->video.pictq_cond); -} - -static int queue_picture(MovieState *movState, double pts) -{ - /* Wait until we have space for a new pic */ - almtx_lock(&movState->video.pictq_mutex); - while(movState->video.pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !movState->quit) - alcnd_wait(&movState->video.pictq_cond, &movState->video.pictq_mutex); - almtx_unlock(&movState->video.pictq_mutex); - - if(movState->quit) - return -1; - - VideoPicture *vp = &movState->video.pictq[movState->video.pictq_windex]; - - /* We have to create/update the picture in the main thread */ - vp->updated = false; - SDL_PushEvent(&(SDL_Event){ .user={.type=FF_UPDATE_EVENT, .data1=movState} }); - - /* Wait until the picture is updated. */ - almtx_lock(&movState->video.pictq_mutex); - while(!vp->updated && !movState->quit) - alcnd_wait(&movState->video.pictq_cond, &movState->video.pictq_mutex); - almtx_unlock(&movState->video.pictq_mutex); - if(movState->quit) - return -1; - vp->pts = pts; - - movState->video.pictq_windex = (movState->video.pictq_windex+1)%VIDEO_PICTURE_QUEUE_SIZE; - almtx_lock(&movState->video.pictq_mutex); - movState->video.pictq_size++; - almtx_unlock(&movState->video.pictq_mutex); - - return 0; -} - -static double synchronize_video(MovieState *movState, double pts) -{ - double frame_delay; - - if(pts == 0.0) /* if we aren't given a pts, set it to the clock */ - pts = movState->video.clock; - else /* if we have pts, set video clock to it */ - movState->video.clock = pts; - - /* update the video clock */ - frame_delay = av_q2d(movState->video.st->codec->time_base); - /* if we are repeating a frame, adjust clock accordingly */ - frame_delay += movState->video.decoded_vframe->repeat_pict * (frame_delay * 0.5); - movState->video.clock += frame_delay; - return pts; -} - -int video_thread(void *arg) -{ - MovieState *movState = (MovieState*)arg; - AVPacket *packet = (AVPacket[1]){}; - int64_t saved_pts, pkt_pts; - int frameFinished; - - movState->video.decoded_vframe = av_frame_alloc(); - while(packet_queue_get(&movState->video.q, packet, movState) >= 0) - { - if(packet->data == flush_pkt.data) - { - avcodec_flush_buffers(movState->video.st->codec); - - almtx_lock(&movState->video.pictq_mutex); - movState->video.pictq_size = 0; - movState->video.pictq_rindex = 0; - movState->video.pictq_windex = 0; - almtx_unlock(&movState->video.pictq_mutex); - - movState->video.clock = av_q2d(movState->video.st->time_base)*packet->pts; - movState->video.current_pts = movState->video.clock; - movState->video.current_pts_time = av_gettime(); - continue; - } - - pkt_pts = packet->pts; - - /* Decode video frame */ - avcodec_decode_video2(movState->video.st->codec, movState->video.decoded_vframe, - &frameFinished, packet); - if(pkt_pts != AV_NOPTS_VALUE && !movState->video.decoded_vframe->opaque) - { - /* Store the packet's original pts in the frame, in case the frame - * is not finished decoding yet. */ - saved_pts = pkt_pts; - movState->video.decoded_vframe->opaque = &saved_pts; - } - - av_free_packet(packet); - - if(frameFinished) - { - double pts = av_q2d(movState->video.st->time_base); - if(packet->dts != AV_NOPTS_VALUE) - pts *= packet->dts; - else if(movState->video.decoded_vframe->opaque) - pts *= *(int64_t*)movState->video.decoded_vframe->opaque; - else - pts *= 0.0; - movState->video.decoded_vframe->opaque = NULL; - - pts = synchronize_video(movState, pts); - if(queue_picture(movState, pts) < 0) - break; - } - } - - sws_freeContext(movState->video.swscale_ctx); - movState->video.swscale_ctx = NULL; - av_frame_free(&movState->video.decoded_vframe); - return 0; -} - - -static int stream_component_open(MovieState *movState, int stream_index) -{ - AVFormatContext *pFormatCtx = movState->pFormatCtx; - AVCodecContext *codecCtx; - AVCodec *codec; - - if(stream_index < 0 || (unsigned int)stream_index >= pFormatCtx->nb_streams) - return -1; - - /* Get a pointer to the codec context for the video stream, and open the - * associated codec */ - codecCtx = pFormatCtx->streams[stream_index]->codec; - - codec = avcodec_find_decoder(codecCtx->codec_id); - if(!codec || avcodec_open2(codecCtx, codec, NULL) < 0) - { - fprintf(stderr, "Unsupported codec!\n"); - return -1; - } - - /* Initialize and start the media type handler */ - switch(codecCtx->codec_type) - { - case AVMEDIA_TYPE_AUDIO: - movState->audioStream = stream_index; - movState->audio.st = pFormatCtx->streams[stream_index]; - - /* Averaging filter for audio sync */ - movState->audio.diff_avg_coef = exp(log(0.01) / AUDIO_DIFF_AVG_NB); - /* Correct audio only if larger error than this */ - movState->audio.diff_threshold = 2.0 * 0.050/* 50 ms */; - - memset(&movState->audio.pkt, 0, sizeof(movState->audio.pkt)); - if(althrd_create(&movState->audio.thread, audio_thread, movState) != althrd_success) - { - movState->audioStream = -1; - movState->audio.st = NULL; - } - break; - - case AVMEDIA_TYPE_VIDEO: - movState->videoStream = stream_index; - movState->video.st = pFormatCtx->streams[stream_index]; - - movState->video.current_pts_time = av_gettime(); - movState->video.frame_timer = (double)movState->video.current_pts_time / - 1000000.0; - movState->video.frame_last_delay = 40e-3; - - if(althrd_create(&movState->video.thread, video_thread, movState) != althrd_success) - { - movState->videoStream = -1; - movState->video.st = NULL; - } - break; - - default: - break; - } - - return 0; -} - -static int decode_interrupt_cb(void *ctx) -{ - return ((MovieState*)ctx)->quit; -} - -int decode_thread(void *arg) -{ - MovieState *movState = (MovieState *)arg; - AVFormatContext *fmtCtx = movState->pFormatCtx; - AVPacket *packet = (AVPacket[1]){}; - int video_index = -1; - int audio_index = -1; - - movState->videoStream = -1; - movState->audioStream = -1; - - /* Dump information about file onto standard error */ - av_dump_format(fmtCtx, 0, movState->filename, 0); - - /* Find the first video and audio streams */ - for(unsigned int i = 0;i < fmtCtx->nb_streams;i++) - { - if(fmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) - video_index = i; - else if(fmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) - audio_index = i; - } - movState->external_clock_base = av_gettime(); - if(audio_index >= 0) - stream_component_open(movState, audio_index); - if(video_index >= 0) - stream_component_open(movState, video_index); - - if(movState->videoStream < 0 && movState->audioStream < 0) - { - fprintf(stderr, "%s: could not open codecs\n", movState->filename); - goto fail; - } - - /* Main packet handling loop */ - while(!movState->quit) - { - if(movState->seek_req) - { - int64_t seek_target = movState->seek_pos; - int stream_index= -1; - - /* Prefer seeking on the video stream. */ - if(movState->videoStream >= 0) - stream_index = movState->videoStream; - else if(movState->audioStream >= 0) - stream_index = movState->audioStream; - - /* Get a seek timestamp for the appropriate stream. */ - int64_t timestamp = seek_target; - if(stream_index >= 0) - timestamp = av_rescale_q(seek_target, AV_TIME_BASE_Q, fmtCtx->streams[stream_index]->time_base); - - if(av_seek_frame(movState->pFormatCtx, stream_index, timestamp, 0) < 0) - fprintf(stderr, "%s: error while seeking\n", movState->pFormatCtx->filename); - else - { - /* Seek successful, clear the packet queues and send a special - * 'flush' packet with the new stream clock time. */ - if(movState->audioStream >= 0) - { - packet_queue_clear(&movState->audio.q); - flush_pkt.pts = av_rescale_q(seek_target, AV_TIME_BASE_Q, - fmtCtx->streams[movState->audioStream]->time_base - ); - packet_queue_put(&movState->audio.q, &flush_pkt); - } - if(movState->videoStream >= 0) - { - packet_queue_clear(&movState->video.q); - flush_pkt.pts = av_rescale_q(seek_target, AV_TIME_BASE_Q, - fmtCtx->streams[movState->videoStream]->time_base - ); - packet_queue_put(&movState->video.q, &flush_pkt); - } - movState->external_clock_base = av_gettime() - seek_target; - } - movState->seek_req = false; - } - - if(movState->audio.q.size >= MAX_AUDIOQ_SIZE || - movState->video.q.size >= MAX_VIDEOQ_SIZE) - { - SDL_Delay(10); - continue; - } - - if(av_read_frame(movState->pFormatCtx, packet) < 0) - { - packet_queue_flush(&movState->video.q); - packet_queue_flush(&movState->audio.q); - break; - } - - /* Place the packet in the queue it's meant for, or discard it. */ - if(packet->stream_index == movState->videoStream) - packet_queue_put(&movState->video.q, packet); - else if(packet->stream_index == movState->audioStream) - packet_queue_put(&movState->audio.q, packet); - else - av_free_packet(packet); - } - - /* all done - wait for it */ - while(!movState->quit) - { - if(movState->audio.q.nb_packets == 0 && movState->video.q.nb_packets == 0) - break; - SDL_Delay(100); - } - -fail: - movState->quit = true; - packet_queue_flush(&movState->video.q); - packet_queue_flush(&movState->audio.q); - - if(movState->videoStream >= 0) - althrd_join(movState->video.thread, NULL); - if(movState->audioStream >= 0) - althrd_join(movState->audio.thread, NULL); - - SDL_PushEvent(&(SDL_Event){ .user={.type=FF_QUIT_EVENT, .data1=movState} }); - - return 0; -} - - -static void stream_seek(MovieState *movState, double incr) -{ - if(!movState->seek_req) - { - double newtime = get_master_clock(movState)+incr; - if(newtime <= 0.0) movState->seek_pos = 0; - else movState->seek_pos = (int64_t)(newtime * AV_TIME_BASE); - movState->seek_req = true; - } -} - -int main(int argc, char *argv[]) -{ - SDL_Event event; - MovieState *movState; - bool first_update = true; - SDL_Window *screen; - SDL_Renderer *renderer; - ALCdevice *device; - ALCcontext *context; - - if(argc < 2) - { - fprintf(stderr, "Usage: %s \n", argv[0]); - return 1; - } - /* Register all formats and codecs */ - av_register_all(); - /* Initialize networking protocols */ - avformat_network_init(); - - if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) - { - fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError()); - return 1; - } - - /* Make a window to put our video */ - screen = SDL_CreateWindow("alffplay", 0, 0, 640, 480, SDL_WINDOW_RESIZABLE); - if(!screen) - { - fprintf(stderr, "SDL: could not set video mode - exiting\n"); - return 1; - } - /* Make a renderer to handle the texture image surface and rendering. */ - renderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_ACCELERATED); - if(renderer) - { - SDL_RendererInfo rinf; - bool ok = false; - - /* Make sure the renderer supports YV12 textures. If not, fallback to a - * software renderer. */ - if(SDL_GetRendererInfo(renderer, &rinf) == 0) - { - for(Uint32 i = 0;!ok && i < rinf.num_texture_formats;i++) - ok = (rinf.texture_formats[i] == SDL_PIXELFORMAT_YV12); - } - if(!ok) - { - fprintf(stderr, "YV12 pixelformat textures not supported on renderer %s\n", rinf.name); - SDL_DestroyRenderer(renderer); - renderer = NULL; - } - } - if(!renderer) - renderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_SOFTWARE); - if(!renderer) - { - fprintf(stderr, "SDL: could not create renderer - exiting\n"); - return 1; - } - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); - SDL_RenderFillRect(renderer, NULL); - SDL_RenderPresent(renderer); - - /* Open an audio device */ - device = alcOpenDevice(NULL); - if(!device) - { - fprintf(stderr, "OpenAL: could not open device - exiting\n"); - return 1; - } - context = alcCreateContext(device, NULL); - if(!context) - { - fprintf(stderr, "OpenAL: could not create context - exiting\n"); - return 1; - } - if(alcMakeContextCurrent(context) == ALC_FALSE) - { - fprintf(stderr, "OpenAL: could not make context current - exiting\n"); - return 1; - } - - if(!alIsExtensionPresent("AL_SOFT_source_length")) - { - fprintf(stderr, "Required AL_SOFT_source_length not supported - exiting\n"); - return 1; - } - - if(!alIsExtensionPresent("AL_SOFT_source_latency")) - fprintf(stderr, "AL_SOFT_source_latency not supported, audio may be a bit laggy.\n"); - else - { - alGetSourcedvSOFT = alGetProcAddress("alGetSourcedvSOFT"); - has_latency_check = true; - } - - - movState = av_mallocz(sizeof(MovieState)); - - av_strlcpy(movState->filename, argv[1], sizeof(movState->filename)); - - packet_queue_init(&movState->audio.q); - packet_queue_init(&movState->video.q); - - almtx_init(&movState->video.pictq_mutex, almtx_plain); - alcnd_init(&movState->video.pictq_cond); - almtx_init(&movState->audio.src_mutex, almtx_recursive); - - movState->av_sync_type = DEFAULT_AV_SYNC_TYPE; - - movState->pFormatCtx = avformat_alloc_context(); - movState->pFormatCtx->interrupt_callback = (AVIOInterruptCB){.callback=decode_interrupt_cb, .opaque=movState}; - - if(avio_open2(&movState->pFormatCtx->pb, movState->filename, AVIO_FLAG_READ, - &movState->pFormatCtx->interrupt_callback, NULL)) - { - fprintf(stderr, "Failed to open %s\n", movState->filename); - return 1; - } - - /* Open movie file */ - if(avformat_open_input(&movState->pFormatCtx, movState->filename, NULL, NULL) != 0) - { - fprintf(stderr, "Failed to open %s\n", movState->filename); - return 1; - } - - /* Retrieve stream information */ - if(avformat_find_stream_info(movState->pFormatCtx, NULL) < 0) - { - fprintf(stderr, "%s: failed to find stream info\n", movState->filename); - return 1; - } - - schedule_refresh(movState, 40); - - - if(althrd_create(&movState->parse_thread, decode_thread, movState) != althrd_success) - { - fprintf(stderr, "Failed to create parse thread!\n"); - return 1; - } - while(SDL_WaitEvent(&event) == 1) - { - switch(event.type) - { - case SDL_KEYDOWN: - switch(event.key.keysym.sym) - { - case SDLK_ESCAPE: - movState->quit = true; - break; - - case SDLK_LEFT: - stream_seek(movState, -10.0); - break; - case SDLK_RIGHT: - stream_seek(movState, 10.0); - break; - case SDLK_UP: - stream_seek(movState, 30.0); - break; - case SDLK_DOWN: - stream_seek(movState, -30.0); - break; - - default: - break; - } - break; - - case SDL_WINDOWEVENT: - switch(event.window.event) - { - case SDL_WINDOWEVENT_RESIZED: - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); - SDL_RenderFillRect(renderer, NULL); - break; - - default: - break; - } - break; - - case SDL_QUIT: - movState->quit = true; - break; - - case FF_UPDATE_EVENT: - update_picture(event.user.data1, &first_update, screen, renderer); - break; - - case FF_REFRESH_EVENT: - video_refresh_timer(event.user.data1, screen, renderer); - break; - - case FF_QUIT_EVENT: - althrd_join(movState->parse_thread, NULL); - - avformat_close_input(&movState->pFormatCtx); - - almtx_destroy(&movState->audio.src_mutex); - almtx_destroy(&movState->video.pictq_mutex); - alcnd_destroy(&movState->video.pictq_cond); - packet_queue_deinit(&movState->video.q); - packet_queue_deinit(&movState->audio.q); - - alcMakeContextCurrent(NULL); - alcDestroyContext(context); - alcCloseDevice(device); - - SDL_Quit(); - exit(0); - - default: - break; - } - } - - fprintf(stderr, "SDL_WaitEvent error - %s\n", SDL_GetError()); - return 1; -} diff --git a/libs/openal-soft/examples/alffplay.cpp b/libs/openal-soft/examples/alffplay.cpp new file mode 100644 index 00000000..10c00553 --- /dev/null +++ b/libs/openal-soft/examples/alffplay.cpp @@ -0,0 +1,1570 @@ +/* + * An example showing how to play a stream sync'd to video, using ffmpeg. + * + * Requires C++11. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "libavcodec/avcodec.h" +#include "libavformat/avformat.h" +#include "libavformat/avio.h" +#include "libavutil/time.h" +#include "libavutil/pixfmt.h" +#include "libavutil/avstring.h" +#include "libavutil/channel_layout.h" +#include "libswscale/swscale.h" +#include "libswresample/swresample.h" +} + +#include "SDL.h" + +#include "AL/alc.h" +#include "AL/al.h" +#include "AL/alext.h" + +namespace +{ + +static const std::string AppName("alffplay"); + +static bool do_direct_out = false; +static bool has_latency_check = false; +static LPALGETSOURCEDVSOFT alGetSourcedvSOFT; + +#define AUDIO_BUFFER_TIME 100 /* In milliseconds, per-buffer */ +#define AUDIO_BUFFER_QUEUE_SIZE 8 /* Number of buffers to queue */ +#define MAX_QUEUE_SIZE (15 * 1024 * 1024) /* Bytes of compressed data to keep queued */ +#define AV_SYNC_THRESHOLD 0.01 +#define AV_NOSYNC_THRESHOLD 10.0 +#define SAMPLE_CORRECTION_MAX_DIFF 0.05 +#define AUDIO_DIFF_AVG_NB 20 +#define VIDEO_PICTURE_QUEUE_SIZE 16 + +enum { + FF_UPDATE_EVENT = SDL_USEREVENT, + FF_REFRESH_EVENT, + FF_MOVIE_DONE_EVENT +}; + +enum { + AV_SYNC_AUDIO_MASTER, + AV_SYNC_VIDEO_MASTER, + AV_SYNC_EXTERNAL_MASTER, + + DEFAULT_AV_SYNC_TYPE = AV_SYNC_EXTERNAL_MASTER +}; + + +struct PacketQueue { + std::deque mPackets; + std::atomic mTotalSize; + std::atomic mFinished; + std::mutex mMutex; + std::condition_variable mCond; + + PacketQueue() : mTotalSize(0), mFinished(false) + { } + ~PacketQueue() + { clear(); } + + int put(const AVPacket *pkt); + int peek(AVPacket *pkt, std::atomic &quit_var); + void pop(); + + void clear(); + void finish(); +}; + + +struct MovieState; + +struct AudioState { + MovieState *mMovie; + + AVStream *mStream; + AVCodecContext *mCodecCtx; + + PacketQueue mQueue; + + /* Used for clock difference average computation */ + struct { + std::atomic Clocks; /* In microseconds */ + double Accum; + double AvgCoeff; + double Threshold; + int AvgCount; + } mDiff; + + /* Time (in seconds) of the next sample to be buffered */ + double mCurrentPts; + + /* Decompressed sample frame, and swresample context for conversion */ + AVFrame *mDecodedFrame; + struct SwrContext *mSwresCtx; + + /* Conversion format, for what gets fed to Alure */ + int mDstChanLayout; + enum AVSampleFormat mDstSampleFmt; + + /* Storage of converted samples */ + uint8_t *mSamples; + int mSamplesLen; /* In samples */ + int mSamplesPos; + int mSamplesMax; + + /* OpenAL format */ + ALenum mFormat; + ALsizei mFrameSize; + + std::recursive_mutex mSrcMutex; + ALuint mSource; + ALuint mBuffers[AUDIO_BUFFER_QUEUE_SIZE]; + ALsizei mBufferIdx; + + AudioState(MovieState *movie) + : mMovie(movie), mStream(nullptr), mCodecCtx(nullptr) + , mDiff{{0}, 0.0, 0.0, 0.0, 0}, mCurrentPts(0.0), mDecodedFrame(nullptr) + , mSwresCtx(nullptr), mDstChanLayout(0), mDstSampleFmt(AV_SAMPLE_FMT_NONE) + , mSamples(nullptr), mSamplesLen(0), mSamplesPos(0), mSamplesMax(0) + , mFormat(AL_NONE), mFrameSize(0), mSource(0), mBufferIdx(0) + { + for(auto &buf : mBuffers) + buf = 0; + } + ~AudioState() + { + if(mSource) + alDeleteSources(1, &mSource); + alDeleteBuffers(AUDIO_BUFFER_QUEUE_SIZE, mBuffers); + + av_frame_free(&mDecodedFrame); + swr_free(&mSwresCtx); + + av_freep(&mSamples); + + avcodec_free_context(&mCodecCtx); + } + + double getClock(); + + int getSync(); + int decodeFrame(); + int readAudio(uint8_t *samples, int length); + + int handler(); +}; + +struct VideoState { + MovieState *mMovie; + + AVStream *mStream; + AVCodecContext *mCodecCtx; + + PacketQueue mQueue; + + double mClock; + double mFrameTimer; + double mFrameLastPts; + double mFrameLastDelay; + double mCurrentPts; + /* time (av_gettime) at which we updated mCurrentPts - used to have running video pts */ + int64_t mCurrentPtsTime; + + /* Decompressed video frame, and swscale context for conversion */ + AVFrame *mDecodedFrame; + struct SwsContext *mSwscaleCtx; + + struct Picture { + SDL_Texture *mImage; + int mWidth, mHeight; /* Logical image size (actual size may be larger) */ + std::atomic mUpdated; + double mPts; + + Picture() + : mImage(nullptr), mWidth(0), mHeight(0), mUpdated(false), mPts(0.0) + { } + ~Picture() + { + if(mImage) + SDL_DestroyTexture(mImage); + mImage = nullptr; + } + }; + std::array mPictQ; + size_t mPictQSize, mPictQRead, mPictQWrite; + std::mutex mPictQMutex; + std::condition_variable mPictQCond; + bool mFirstUpdate; + std::atomic mEOS; + std::atomic mFinalUpdate; + + VideoState(MovieState *movie) + : mMovie(movie), mStream(nullptr), mCodecCtx(nullptr), mClock(0.0) + , mFrameTimer(0.0), mFrameLastPts(0.0), mFrameLastDelay(0.0) + , mCurrentPts(0.0), mCurrentPtsTime(0), mDecodedFrame(nullptr) + , mSwscaleCtx(nullptr), mPictQSize(0), mPictQRead(0), mPictQWrite(0) + , mFirstUpdate(true), mEOS(false), mFinalUpdate(false) + { } + ~VideoState() + { + sws_freeContext(mSwscaleCtx); + mSwscaleCtx = nullptr; + av_frame_free(&mDecodedFrame); + avcodec_free_context(&mCodecCtx); + } + + double getClock(); + + static Uint32 SDLCALL sdl_refresh_timer_cb(Uint32 interval, void *opaque); + void schedRefresh(int delay); + void display(SDL_Window *screen, SDL_Renderer *renderer); + void refreshTimer(SDL_Window *screen, SDL_Renderer *renderer); + void updatePicture(SDL_Window *screen, SDL_Renderer *renderer); + int queuePicture(double pts); + double synchronize(double pts); + int handler(); +}; + +struct MovieState { + AVFormatContext *mFormatCtx; + int mVideoStream, mAudioStream; + + int mAVSyncType; + + int64_t mExternalClockBase; + + std::atomic mQuit; + + AudioState mAudio; + VideoState mVideo; + + std::thread mParseThread; + std::thread mAudioThread; + std::thread mVideoThread; + + std::string mFilename; + + MovieState(std::string fname) + : mFormatCtx(nullptr), mVideoStream(0), mAudioStream(0) + , mAVSyncType(DEFAULT_AV_SYNC_TYPE), mExternalClockBase(0), mQuit(false) + , mAudio(this), mVideo(this), mFilename(std::move(fname)) + { } + ~MovieState() + { + mQuit = true; + if(mParseThread.joinable()) + mParseThread.join(); + avformat_close_input(&mFormatCtx); + } + + static int decode_interrupt_cb(void *ctx); + bool prepare(); + void setTitle(SDL_Window *window); + + double getClock(); + + double getMasterClock(); + + int streamComponentOpen(int stream_index); + int parse_handler(); +}; + + +int PacketQueue::put(const AVPacket *pkt) +{ + std::unique_lock lock(mMutex); + mPackets.push_back(AVPacket{}); + if(av_packet_ref(&mPackets.back(), pkt) != 0) + { + mPackets.pop_back(); + return -1; + } + mTotalSize += mPackets.back().size; + lock.unlock(); + + mCond.notify_one(); + return 0; +} + +int PacketQueue::peek(AVPacket *pkt, std::atomic &quit_var) +{ + std::unique_lock lock(mMutex); + while(!quit_var.load()) + { + if(!mPackets.empty()) + { + if(av_packet_ref(pkt, &mPackets.front()) != 0) + return -1; + return 1; + } + + if(mFinished.load()) + return 0; + mCond.wait(lock); + } + return -1; +} + +void PacketQueue::pop() +{ + std::unique_lock lock(mMutex); + AVPacket *pkt = &mPackets.front(); + mTotalSize -= pkt->size; + av_packet_unref(pkt); + mPackets.pop_front(); +} + +void PacketQueue::clear() +{ + std::unique_lock lock(mMutex); + std::for_each(mPackets.begin(), mPackets.end(), + [](AVPacket &pkt) { av_packet_unref(&pkt); } + ); + mPackets.clear(); + mTotalSize = 0; +} +void PacketQueue::finish() +{ + std::unique_lock lock(mMutex); + mFinished = true; + lock.unlock(); + mCond.notify_all(); +} + + +double AudioState::getClock() +{ + double pts; + + std::unique_lock lock(mSrcMutex); + /* The audio clock is the timestamp of the sample currently being heard. + * It's based on 4 components: + * 1 - The timestamp of the next sample to buffer (state->current_pts) + * 2 - The length of the source's buffer queue + * 3 - The offset OpenAL is currently at in the source (the first value + * from AL_SEC_OFFSET_LATENCY_SOFT) + * 4 - The latency between OpenAL and the DAC (the second value from + * AL_SEC_OFFSET_LATENCY_SOFT) + * + * Subtracting the length of the source queue from the next sample's + * timestamp gives the timestamp of the sample at start of the source + * queue. Adding the source offset to that results in the timestamp for + * OpenAL's current position, and subtracting the source latency from that + * gives the timestamp of the sample currently at the DAC. + */ + pts = mCurrentPts; + if(mSource) + { + ALdouble offset[2]; + ALint queue_size; + ALint status; + + /* NOTE: The source state must be checked last, in case an underrun + * occurs and the source stops between retrieving the offset+latency + * and getting the state. */ + if(has_latency_check) + { + alGetSourcedvSOFT(mSource, AL_SEC_OFFSET_LATENCY_SOFT, offset); + alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queue_size); + } + else + { + ALint ioffset; + alGetSourcei(mSource, AL_SAMPLE_OFFSET, &ioffset); + alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queue_size); + offset[0] = (double)ioffset / (double)mCodecCtx->sample_rate; + offset[1] = 0.0f; + } + alGetSourcei(mSource, AL_SOURCE_STATE, &status); + + /* If the source is AL_STOPPED, then there was an underrun and all + * buffers are processed, so ignore the source queue. The audio thread + * will put the source into an AL_INITIAL state and clear the queue + * when it starts recovery. */ + if(status != AL_STOPPED) + pts -= queue_size*((double)AUDIO_BUFFER_TIME/1000.0) - offset[0]; + if(status == AL_PLAYING) + pts -= offset[1]; + } + lock.unlock(); + + return std::max(pts, 0.0); +} + +int AudioState::getSync() +{ + double diff, avg_diff, ref_clock; + + if(mMovie->mAVSyncType == AV_SYNC_AUDIO_MASTER) + return 0; + + ref_clock = mMovie->getMasterClock(); + diff = ref_clock - getClock(); + + if(!(fabs(diff) < AV_NOSYNC_THRESHOLD)) + { + /* Difference is TOO big; reset diff stuff */ + mDiff.Accum = 0.0; + return 0; + } + + /* Accumulate the diffs */ + mDiff.Accum = mDiff.Accum*mDiff.AvgCoeff + diff; + avg_diff = mDiff.Accum*(1.0 - mDiff.AvgCoeff); + if(fabs(avg_diff) < mDiff.Threshold) + return 0; + + /* Constrain the per-update difference to avoid exceedingly large skips */ + if(!(diff <= SAMPLE_CORRECTION_MAX_DIFF)) + diff = SAMPLE_CORRECTION_MAX_DIFF; + else if(!(diff >= -SAMPLE_CORRECTION_MAX_DIFF)) + diff = -SAMPLE_CORRECTION_MAX_DIFF; + return (int)(diff*mCodecCtx->sample_rate); +} + +int AudioState::decodeFrame() +{ + while(!mMovie->mQuit.load()) + { + while(!mMovie->mQuit.load()) + { + /* Get the next packet */ + AVPacket pkt{}; + if(mQueue.peek(&pkt, mMovie->mQuit) <= 0) + return -1; + + int ret = avcodec_send_packet(mCodecCtx, &pkt); + if(ret != AVERROR(EAGAIN)) + { + if(ret < 0) + std::cerr<< "Failed to send encoded packet: 0x"<nb_samples <= 0) + { + av_frame_unref(mDecodedFrame); + continue; + } + + /* If provided, update w/ pts */ + int64_t pts = av_frame_get_best_effort_timestamp(mDecodedFrame); + if(pts != AV_NOPTS_VALUE) + mCurrentPts = av_q2d(mStream->time_base)*pts; + + if(mDecodedFrame->nb_samples > mSamplesMax) + { + av_freep(&mSamples); + av_samples_alloc( + &mSamples, nullptr, mCodecCtx->channels, + mDecodedFrame->nb_samples, mDstSampleFmt, 0 + ); + mSamplesMax = mDecodedFrame->nb_samples; + } + /* Return the amount of sample frames converted */ + int data_size = swr_convert(mSwresCtx, &mSamples, mDecodedFrame->nb_samples, + (const uint8_t**)mDecodedFrame->data, mDecodedFrame->nb_samples + ); + + av_frame_unref(mDecodedFrame); + return data_size; + } + + return 0; +} + +/* Duplicates the sample at in to out, count times. The frame size is a + * multiple of the template type size. + */ +template +static void sample_dup(uint8_t *out, const uint8_t *in, int count, int frame_size) +{ + const T *sample = reinterpret_cast(in); + T *dst = reinterpret_cast(out); + if(frame_size == sizeof(T)) + std::fill_n(dst, count, *sample); + else + { + /* NOTE: frame_size is a multiple of sizeof(T). */ + int type_mult = frame_size / sizeof(T); + int i = 0; + std::generate_n(dst, count*type_mult, + [sample,type_mult,&i]() -> T + { + T ret = sample[i]; + i = (i+1)%type_mult; + return ret; + } + ); + } +} + + +int AudioState::readAudio(uint8_t *samples, int length) +{ + int sample_skip = getSync(); + int audio_size = 0; + + /* Read the next chunk of data, refill the buffer, and queue it + * on the source */ + length /= mFrameSize; + while(audio_size < length) + { + if(mSamplesLen <= 0 || mSamplesPos >= mSamplesLen) + { + int frame_len = decodeFrame(); + if(frame_len <= 0) break; + + mSamplesLen = frame_len; + mSamplesPos = std::min(mSamplesLen, sample_skip); + sample_skip -= mSamplesPos; + + mCurrentPts += (double)mSamplesPos / (double)mCodecCtx->sample_rate; + continue; + } + + int rem = length - audio_size; + if(mSamplesPos >= 0) + { + int len = mSamplesLen - mSamplesPos; + if(rem > len) rem = len; + memcpy(samples, mSamples + mSamplesPos*mFrameSize, rem*mFrameSize); + } + else + { + rem = std::min(rem, -mSamplesPos); + + /* Add samples by copying the first sample */ + if((mFrameSize&7) == 0) + sample_dup(samples, mSamples, rem, mFrameSize); + else if((mFrameSize&3) == 0) + sample_dup(samples, mSamples, rem, mFrameSize); + else if((mFrameSize&1) == 0) + sample_dup(samples, mSamples, rem, mFrameSize); + else + sample_dup(samples, mSamples, rem, mFrameSize); + } + + mSamplesPos += rem; + mCurrentPts += (double)rem / mCodecCtx->sample_rate; + samples += rem*mFrameSize; + audio_size += rem; + } + + if(audio_size < length && audio_size > 0) + { + int rem = length - audio_size; + std::fill_n(samples, rem*mFrameSize, + (mDstSampleFmt == AV_SAMPLE_FMT_U8) ? 0x80 : 0x00); + mCurrentPts += (double)rem / mCodecCtx->sample_rate; + audio_size += rem; + } + + return audio_size * mFrameSize; +} + + +int AudioState::handler() +{ + std::unique_lock lock(mSrcMutex); + ALenum fmt; + + /* Find a suitable format for Alure. */ + mDstChanLayout = 0; + if(mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8 || mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8P) + { + mDstSampleFmt = AV_SAMPLE_FMT_U8; + mFrameSize = 1; + if(mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1 && + alIsExtensionPresent("AL_EXT_MCFORMATS") && + (fmt=alGetEnumValue("AL_FORMAT_71CHN8")) != AL_NONE && fmt != -1) + { + mDstChanLayout = mCodecCtx->channel_layout; + mFrameSize *= 8; + mFormat = fmt; + } + if((mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1 || + mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1_BACK) && + alIsExtensionPresent("AL_EXT_MCFORMATS") && + (fmt=alGetEnumValue("AL_FORMAT_51CHN8")) != AL_NONE && fmt != -1) + { + mDstChanLayout = mCodecCtx->channel_layout; + mFrameSize *= 6; + mFormat = fmt; + } + if(mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO) + { + mDstChanLayout = mCodecCtx->channel_layout; + mFrameSize *= 1; + mFormat = AL_FORMAT_MONO8; + } + if(!mDstChanLayout) + { + mDstChanLayout = AV_CH_LAYOUT_STEREO; + mFrameSize *= 2; + mFormat = AL_FORMAT_STEREO8; + } + } + if((mCodecCtx->sample_fmt == AV_SAMPLE_FMT_FLT || mCodecCtx->sample_fmt == AV_SAMPLE_FMT_FLTP) && + alIsExtensionPresent("AL_EXT_FLOAT32")) + { + mDstSampleFmt = AV_SAMPLE_FMT_FLT; + mFrameSize = 4; + if(mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1 && + alIsExtensionPresent("AL_EXT_MCFORMATS") && + (fmt=alGetEnumValue("AL_FORMAT_71CHN32")) != AL_NONE && fmt != -1) + { + mDstChanLayout = mCodecCtx->channel_layout; + mFrameSize *= 8; + mFormat = fmt; + } + if((mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1 || + mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1_BACK) && + alIsExtensionPresent("AL_EXT_MCFORMATS") && + (fmt=alGetEnumValue("AL_FORMAT_51CHN32")) != AL_NONE && fmt != -1) + { + mDstChanLayout = mCodecCtx->channel_layout; + mFrameSize *= 6; + mFormat = fmt; + } + if(mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO) + { + mDstChanLayout = mCodecCtx->channel_layout; + mFrameSize *= 1; + mFormat = AL_FORMAT_MONO_FLOAT32; + } + if(!mDstChanLayout) + { + mDstChanLayout = AV_CH_LAYOUT_STEREO; + mFrameSize *= 2; + mFormat = AL_FORMAT_STEREO_FLOAT32; + } + } + if(!mDstChanLayout) + { + mDstSampleFmt = AV_SAMPLE_FMT_S16; + mFrameSize = 2; + if(mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1 && + alIsExtensionPresent("AL_EXT_MCFORMATS") && + (fmt=alGetEnumValue("AL_FORMAT_71CHN16")) != AL_NONE && fmt != -1) + { + mDstChanLayout = mCodecCtx->channel_layout; + mFrameSize *= 8; + mFormat = fmt; + } + if((mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1 || + mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1_BACK) && + alIsExtensionPresent("AL_EXT_MCFORMATS") && + (fmt=alGetEnumValue("AL_FORMAT_51CHN16")) != AL_NONE && fmt != -1) + { + mDstChanLayout = mCodecCtx->channel_layout; + mFrameSize *= 6; + mFormat = fmt; + } + if(mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO) + { + mDstChanLayout = mCodecCtx->channel_layout; + mFrameSize *= 1; + mFormat = AL_FORMAT_MONO16; + } + if(!mDstChanLayout) + { + mDstChanLayout = AV_CH_LAYOUT_STEREO; + mFrameSize *= 2; + mFormat = AL_FORMAT_STEREO16; + } + } + ALsizei buffer_len = mCodecCtx->sample_rate * AUDIO_BUFFER_TIME / 1000 * + mFrameSize; + void *samples = av_malloc(buffer_len); + + mSamples = NULL; + mSamplesMax = 0; + mSamplesPos = 0; + mSamplesLen = 0; + + if(!(mDecodedFrame=av_frame_alloc())) + { + std::cerr<< "Failed to allocate audio frame" <sample_rate, + mCodecCtx->channel_layout ? mCodecCtx->channel_layout : + (uint64_t)av_get_default_channel_layout(mCodecCtx->channels), + mCodecCtx->sample_fmt, mCodecCtx->sample_rate, + 0, nullptr + ); + if(!mSwresCtx || swr_init(mSwresCtx) != 0) + { + std::cerr<< "Failed to initialize audio converter" <mQuit.load()) + { + /* First remove any processed buffers. */ + ALint processed; + alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed); + if(processed > 0) + { + std::array tmp; + alSourceUnqueueBuffers(mSource, processed, tmp.data()); + } + + /* Refill the buffer queue. */ + ALint queued; + alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued); + while(queued < AUDIO_BUFFER_QUEUE_SIZE) + { + int audio_size; + + /* Read the next chunk of data, fill the buffer, and queue it on + * the source */ + audio_size = readAudio(reinterpret_cast(samples), buffer_len); + if(audio_size <= 0) break; + + ALuint bufid = mBuffers[mBufferIdx++]; + mBufferIdx %= AUDIO_BUFFER_QUEUE_SIZE; + + alBufferData(bufid, mFormat, samples, audio_size, mCodecCtx->sample_rate); + alSourceQueueBuffers(mSource, 1, &bufid); + queued++; + } + if(queued == 0) + break; + + /* Check that the source is playing. */ + ALint state; + alGetSourcei(mSource, AL_SOURCE_STATE, &state); + if(state == AL_STOPPED) + { + /* AL_STOPPED means there was an underrun. Rewind the source to get + * it back into an AL_INITIAL state. + */ + alSourceRewind(mSource); + continue; + } + + lock.unlock(); + + /* (re)start the source if needed, and wait for a buffer to finish */ + if(state != AL_PLAYING && state != AL_PAUSED) + alSourcePlay(mSource); + SDL_Delay(AUDIO_BUFFER_TIME / 3); + + lock.lock(); + } + +finish: + alSourceRewind(mSource); + alSourcei(mSource, AL_BUFFER, 0); + + av_frame_free(&mDecodedFrame); + swr_free(&mSwresCtx); + + av_freep(&mSamples); + + return 0; +} + + +double VideoState::getClock() +{ + double delta = (av_gettime() - mCurrentPtsTime) / 1000000.0; + return mCurrentPts + delta; +} + +Uint32 SDLCALL VideoState::sdl_refresh_timer_cb(Uint32 /*interval*/, void *opaque) +{ + SDL_Event evt{}; + evt.user.type = FF_REFRESH_EVENT; + evt.user.data1 = opaque; + SDL_PushEvent(&evt); + return 0; /* 0 means stop timer */ +} + +/* Schedules an FF_REFRESH_EVENT event to occur in 'delay' ms. */ +void VideoState::schedRefresh(int delay) +{ + SDL_AddTimer(delay, sdl_refresh_timer_cb, this); +} + +/* Called by VideoState::refreshTimer to display the next video frame. */ +void VideoState::display(SDL_Window *screen, SDL_Renderer *renderer) +{ + Picture *vp = &mPictQ[mPictQRead]; + + if(!vp->mImage) + return; + + float aspect_ratio; + int win_w, win_h; + int w, h, x, y; + + if(mCodecCtx->sample_aspect_ratio.num == 0) + aspect_ratio = 0.0f; + else + { + aspect_ratio = av_q2d(mCodecCtx->sample_aspect_ratio) * mCodecCtx->width / + mCodecCtx->height; + } + if(aspect_ratio <= 0.0f) + aspect_ratio = (float)mCodecCtx->width / (float)mCodecCtx->height; + + SDL_GetWindowSize(screen, &win_w, &win_h); + h = win_h; + w = ((int)rint(h * aspect_ratio) + 3) & ~3; + if(w > win_w) + { + w = win_w; + h = ((int)rint(w / aspect_ratio) + 3) & ~3; + } + x = (win_w - w) / 2; + y = (win_h - h) / 2; + + SDL_Rect src_rect{ 0, 0, vp->mWidth, vp->mHeight }; + SDL_Rect dst_rect{ x, y, w, h }; + SDL_RenderCopy(renderer, vp->mImage, &src_rect, &dst_rect); + SDL_RenderPresent(renderer); +} + +/* FF_REFRESH_EVENT handler called on the main thread where the SDL_Renderer + * was created. It handles the display of the next decoded video frame (if not + * falling behind), and sets up the timer for the following video frame. + */ +void VideoState::refreshTimer(SDL_Window *screen, SDL_Renderer *renderer) +{ + if(!mStream) + { + if(mEOS) + { + mFinalUpdate = true; + std::unique_lock(mPictQMutex).unlock(); + mPictQCond.notify_all(); + return; + } + schedRefresh(100); + return; + } + + std::unique_lock lock(mPictQMutex); +retry: + if(mPictQSize == 0) + { + if(mEOS) + mFinalUpdate = true; + else + schedRefresh(1); + lock.unlock(); + mPictQCond.notify_all(); + return; + } + + Picture *vp = &mPictQ[mPictQRead]; + mCurrentPts = vp->mPts; + mCurrentPtsTime = av_gettime(); + + /* Get delay using the frame pts and the pts from last frame. */ + double delay = vp->mPts - mFrameLastPts; + if(delay <= 0 || delay >= 1.0) + { + /* If incorrect delay, use previous one. */ + delay = mFrameLastDelay; + } + /* Save for next frame. */ + mFrameLastDelay = delay; + mFrameLastPts = vp->mPts; + + /* Update delay to sync to clock if not master source. */ + if(mMovie->mAVSyncType != AV_SYNC_VIDEO_MASTER) + { + double ref_clock = mMovie->getMasterClock(); + double diff = vp->mPts - ref_clock; + + /* Skip or repeat the frame. Take delay into account. */ + double sync_threshold = std::min(delay, AV_SYNC_THRESHOLD); + if(fabs(diff) < AV_NOSYNC_THRESHOLD) + { + if(diff <= -sync_threshold) + delay = 0; + else if(diff >= sync_threshold) + delay *= 2.0; + } + } + + mFrameTimer += delay; + /* Compute the REAL delay. */ + double actual_delay = mFrameTimer - (av_gettime() / 1000000.0); + if(!(actual_delay >= 0.010)) + { + /* We don't have time to handle this picture, just skip to the next one. */ + mPictQRead = (mPictQRead+1)%mPictQ.size(); + mPictQSize--; + goto retry; + } + schedRefresh((int)(actual_delay*1000.0 + 0.5)); + + /* Show the picture! */ + display(screen, renderer); + + /* Update queue for next picture. */ + mPictQRead = (mPictQRead+1)%mPictQ.size(); + mPictQSize--; + lock.unlock(); + mPictQCond.notify_all(); +} + +/* FF_UPDATE_EVENT handler, updates the picture's texture. It's called on the + * main thread where the renderer was created. + */ +void VideoState::updatePicture(SDL_Window *screen, SDL_Renderer *renderer) +{ + Picture *vp = &mPictQ[mPictQWrite]; + bool fmt_updated = false; + + /* allocate or resize the buffer! */ + if(!vp->mImage || vp->mWidth != mCodecCtx->width || vp->mHeight != mCodecCtx->height) + { + fmt_updated = true; + if(vp->mImage) + SDL_DestroyTexture(vp->mImage); + vp->mImage = SDL_CreateTexture( + renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, + mCodecCtx->coded_width, mCodecCtx->coded_height + ); + if(!vp->mImage) + std::cerr<< "Failed to create YV12 texture!" <mWidth = mCodecCtx->width; + vp->mHeight = mCodecCtx->height; + + if(mFirstUpdate && vp->mWidth > 0 && vp->mHeight > 0) + { + /* For the first update, set the window size to the video size. */ + mFirstUpdate = false; + + int w = vp->mWidth; + int h = vp->mHeight; + if(mCodecCtx->sample_aspect_ratio.den != 0) + { + double aspect_ratio = av_q2d(mCodecCtx->sample_aspect_ratio); + if(aspect_ratio >= 1.0) + w = (int)(w*aspect_ratio + 0.5); + else if(aspect_ratio > 0.0) + h = (int)(h/aspect_ratio + 0.5); + } + SDL_SetWindowSize(screen, w, h); + } + } + + if(vp->mImage) + { + AVFrame *frame = mDecodedFrame; + void *pixels = nullptr; + int pitch = 0; + + if(mCodecCtx->pix_fmt == AV_PIX_FMT_YUV420P) + SDL_UpdateYUVTexture(vp->mImage, nullptr, + frame->data[0], frame->linesize[0], + frame->data[1], frame->linesize[1], + frame->data[2], frame->linesize[2] + ); + else if(SDL_LockTexture(vp->mImage, nullptr, &pixels, &pitch) != 0) + std::cerr<< "Failed to lock texture" <coded_width; + int coded_h = mCodecCtx->coded_height; + int w = mCodecCtx->width; + int h = mCodecCtx->height; + if(!mSwscaleCtx || fmt_updated) + { + sws_freeContext(mSwscaleCtx); + mSwscaleCtx = sws_getContext( + w, h, mCodecCtx->pix_fmt, + w, h, AV_PIX_FMT_YUV420P, 0, + nullptr, nullptr, nullptr + ); + } + + /* point pict at the queue */ + uint8_t *pict_data[3]; + pict_data[0] = reinterpret_cast(pixels); + pict_data[1] = pict_data[0] + coded_w*coded_h; + pict_data[2] = pict_data[1] + coded_w*coded_h/4; + + int pict_linesize[3]; + pict_linesize[0] = pitch; + pict_linesize[1] = pitch / 2; + pict_linesize[2] = pitch / 2; + + sws_scale(mSwscaleCtx, (const uint8_t**)frame->data, + frame->linesize, 0, h, pict_data, pict_linesize); + SDL_UnlockTexture(vp->mImage); + } + } + + std::unique_lock lock(mPictQMutex); + vp->mUpdated = true; + lock.unlock(); + mPictQCond.notify_one(); +} + +int VideoState::queuePicture(double pts) +{ + /* Wait until we have space for a new pic */ + std::unique_lock lock(mPictQMutex); + while(mPictQSize >= mPictQ.size() && !mMovie->mQuit.load()) + mPictQCond.wait(lock); + lock.unlock(); + + if(mMovie->mQuit.load()) + return -1; + + Picture *vp = &mPictQ[mPictQWrite]; + + /* We have to create/update the picture in the main thread */ + vp->mUpdated = false; + SDL_Event evt{}; + evt.user.type = FF_UPDATE_EVENT; + evt.user.data1 = this; + SDL_PushEvent(&evt); + + /* Wait until the picture is updated. */ + lock.lock(); + while(!vp->mUpdated && !mMovie->mQuit.load()) + mPictQCond.wait(lock); + if(mMovie->mQuit.load()) + return -1; + vp->mPts = pts; + + mPictQWrite = (mPictQWrite+1)%mPictQ.size(); + mPictQSize++; + lock.unlock(); + + return 0; +} + +double VideoState::synchronize(double pts) +{ + double frame_delay; + + if(pts == 0.0) /* if we aren't given a pts, set it to the clock */ + pts = mClock; + else /* if we have pts, set video clock to it */ + mClock = pts; + + /* update the video clock */ + frame_delay = av_q2d(mCodecCtx->time_base); + /* if we are repeating a frame, adjust clock accordingly */ + frame_delay += mDecodedFrame->repeat_pict * (frame_delay * 0.5); + mClock += frame_delay; + return pts; +} + +int VideoState::handler() +{ + mDecodedFrame = av_frame_alloc(); + while(!mMovie->mQuit) + { + while(!mMovie->mQuit) + { + AVPacket packet{}; + if(mQueue.peek(&packet, mMovie->mQuit) <= 0) + goto finish; + + int ret = avcodec_send_packet(mCodecCtx, &packet); + if(ret != AVERROR(EAGAIN)) + { + if(ret < 0) + std::cerr<< "Failed to send encoded packet: 0x"<time_base) * av_frame_get_best_effort_timestamp(mDecodedFrame) + ); + if(queuePicture(pts) < 0) + break; + av_frame_unref(mDecodedFrame); + } +finish: + mEOS = true; + av_frame_free(&mDecodedFrame); + + std::unique_lock lock(mPictQMutex); + if(mMovie->mQuit) + { + mPictQRead = 0; + mPictQWrite = 0; + mPictQSize = 0; + } + while(!mFinalUpdate) + mPictQCond.wait(lock); + + return 0; +} + + +int MovieState::decode_interrupt_cb(void *ctx) +{ + return reinterpret_cast(ctx)->mQuit; +} + +bool MovieState::prepare() +{ + mFormatCtx = avformat_alloc_context(); + mFormatCtx->interrupt_callback.callback = decode_interrupt_cb; + mFormatCtx->interrupt_callback.opaque = this; + if(avio_open2(&mFormatCtx->pb, mFilename.c_str(), AVIO_FLAG_READ, + &mFormatCtx->interrupt_callback, nullptr)) + { + std::cerr<< "Failed to open "<= mFormatCtx->nb_streams) + return -1; + + /* Get a pointer to the codec context for the stream, and open the + * associated codec. + */ + AVCodecContext *avctx = avcodec_alloc_context3(nullptr); + if(!avctx) return -1; + + if(avcodec_parameters_to_context(avctx, mFormatCtx->streams[stream_index]->codecpar)) + { + avcodec_free_context(&avctx); + return -1; + } + + AVCodec *codec = avcodec_find_decoder(avctx->codec_id); + if(!codec || avcodec_open2(avctx, codec, nullptr) < 0) + { + std::cerr<< "Unsupported codec: "<codec_id) + << " (0x"<codec_id<codec_type) + { + case AVMEDIA_TYPE_AUDIO: + mAudioStream = stream_index; + mAudio.mStream = mFormatCtx->streams[stream_index]; + mAudio.mCodecCtx = avctx; + + /* Averaging filter for audio sync */ + mAudio.mDiff.AvgCoeff = exp(log(0.01) / AUDIO_DIFF_AVG_NB); + /* Correct audio only if larger error than this */ + mAudio.mDiff.Threshold = 0.050/* 50 ms */; + + mAudioThread = std::thread(std::mem_fn(&AudioState::handler), &mAudio); + break; + + case AVMEDIA_TYPE_VIDEO: + mVideoStream = stream_index; + mVideo.mStream = mFormatCtx->streams[stream_index]; + mVideo.mCodecCtx = avctx; + + mVideo.mCurrentPtsTime = av_gettime(); + mVideo.mFrameTimer = (double)mVideo.mCurrentPtsTime / 1000000.0; + mVideo.mFrameLastDelay = 40e-3; + + mVideoThread = std::thread(std::mem_fn(&VideoState::handler), &mVideo); + break; + + default: + avcodec_free_context(&avctx); + break; + } + + return 0; +} + +int MovieState::parse_handler() +{ + int video_index = -1; + int audio_index = -1; + + mVideoStream = -1; + mAudioStream = -1; + + /* Dump information about file onto standard error */ + av_dump_format(mFormatCtx, 0, mFilename.c_str(), 0); + + /* Find the first video and audio streams */ + for(unsigned int i = 0;i < mFormatCtx->nb_streams;i++) + { + if(mFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) + video_index = i; + else if(mFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) + audio_index = i; + } + /* Start the external clock in 50ms, to give the audio and video + * components time to start without needing to skip ahead. + */ + mExternalClockBase = av_gettime() + 50000; + if(audio_index >= 0) + streamComponentOpen(audio_index); + if(video_index >= 0) + streamComponentOpen(video_index); + + if(mVideoStream < 0 && mAudioStream < 0) + { + std::cerr<< mFilename<<": could not open codecs" <= MAX_QUEUE_SIZE) + { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + continue; + } + + AVPacket packet; + if(av_read_frame(mFormatCtx, &packet) < 0) + break; + + /* Copy the packet in the queue it's meant for. */ + if(packet.stream_index == mVideoStream) + mVideo.mQueue.put(&packet); + else if(packet.stream_index == mAudioStream) + mAudio.mQueue.put(&packet); + av_packet_unref(&packet); + } + mVideo.mQueue.finish(); + mAudio.mQueue.finish(); + + /* all done - wait for it */ + if(mVideoThread.joinable()) + mVideoThread.join(); + if(mAudioThread.joinable()) + mAudioThread.join(); + + mVideo.mEOS = true; + std::unique_lock lock(mVideo.mPictQMutex); + while(!mVideo.mFinalUpdate) + mVideo.mPictQCond.wait(lock); + lock.unlock(); + + SDL_Event evt{}; + evt.user.type = FF_MOVIE_DONE_EVENT; + SDL_PushEvent(&evt); + + return 0; +} + +} // namespace + + +int main(int argc, char *argv[]) +{ + std::unique_ptr movState; + + if(argc < 2) + { + std::cerr<< "Usage: "<] [-direct] " < ALCdevice* + { + ALCdevice *dev = NULL; + if(argc > 3 && strcmp(argv[1], "-device") == 0) + { + fileidx = 3; + dev = alcOpenDevice(argv[2]); + if(dev) return dev; + std::cerr<< "Failed to open \""<(new MovieState(argv[fileidx++])); + if(!movState->prepare()) movState = nullptr; + } + if(!movState) + { + std::cerr<< "Could not start a video" <setTitle(screen); + + /* Default to going to the next movie at the end of one. */ + enum class EomAction { + Next, Quit + } eom_action = EomAction::Next; + SDL_Event event; + while(SDL_WaitEvent(&event) == 1) + { + switch(event.type) + { + case SDL_KEYDOWN: + switch(event.key.keysym.sym) + { + case SDLK_ESCAPE: + movState->mQuit = true; + eom_action = EomAction::Quit; + break; + + case SDLK_n: + movState->mQuit = true; + eom_action = EomAction::Next; + break; + + default: + break; + } + break; + + case SDL_WINDOWEVENT: + switch(event.window.event) + { + case SDL_WINDOWEVENT_RESIZED: + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderFillRect(renderer, nullptr); + break; + + default: + break; + } + break; + + case SDL_QUIT: + movState->mQuit = true; + eom_action = EomAction::Quit; + break; + + case FF_UPDATE_EVENT: + reinterpret_cast(event.user.data1)->updatePicture( + screen, renderer + ); + break; + + case FF_REFRESH_EVENT: + reinterpret_cast(event.user.data1)->refreshTimer( + screen, renderer + ); + break; + + case FF_MOVIE_DONE_EVENT: + if(eom_action != EomAction::Quit) + { + movState = nullptr; + while(fileidx < argc && !movState) + { + movState = std::unique_ptr(new MovieState(argv[fileidx++])); + if(!movState->prepare()) movState = nullptr; + } + if(movState) + { + movState->setTitle(screen); + break; + } + } + + /* Nothing more to play. Shut everything down and quit. */ + movState = nullptr; + + alcMakeContextCurrent(nullptr); + alcDestroyContext(context); + alcCloseDevice(device); + + SDL_DestroyRenderer(renderer); + renderer = nullptr; + SDL_DestroyWindow(screen); + screen = nullptr; + + SDL_Quit(); + exit(0); + + default: + break; + } + } + + std::cerr<< "SDL_WaitEvent error - "< #include +#include + #include "AL/al.h" #include "AL/alc.h" #include "AL/alext.h" #include "common/alhelpers.h" -#include "common/sdl_sound.h" #ifndef M_PI @@ -44,47 +45,63 @@ static LPALCGETSTRINGISOFT alcGetStringiSOFT; static LPALCRESETDEVICESOFT alcResetDeviceSOFT; /* LoadBuffer loads the named audio file into an OpenAL buffer object, and - * returns the new buffer ID. */ + * returns the new buffer ID. + */ static ALuint LoadSound(const char *filename) { - ALenum err, format, type, channels; - ALuint rate, buffer; - size_t datalen; - void *data; - FilePtr sound; + Sound_Sample *sample; + ALenum err, format; + ALuint buffer; + Uint32 slen; /* Open the audio file */ - sound = openAudioFile(filename, 1000); - if(!sound) + sample = Sound_NewSampleFromFile(filename, NULL, 65536); + if(!sample) { fprintf(stderr, "Could not open audio in %s\n", filename); - closeAudioFile(sound); return 0; } /* Get the sound format, and figure out the OpenAL format */ - if(getAudioInfo(sound, &rate, &channels, &type) != 0) + if(sample->actual.channels == 1) { - fprintf(stderr, "Error getting audio info for %s\n", filename); - closeAudioFile(sound); - return 0; + if(sample->actual.format == AUDIO_U8) + format = AL_FORMAT_MONO8; + else if(sample->actual.format == AUDIO_S16SYS) + format = AL_FORMAT_MONO16; + else + { + fprintf(stderr, "Unsupported sample format: 0x%04x\n", sample->actual.format); + Sound_FreeSample(sample); + return 0; + } } - - format = GetFormat(channels, type, NULL); - if(format == AL_NONE) + else if(sample->actual.channels == 2) { - fprintf(stderr, "Unsupported format (%s, %s) for %s\n", - ChannelsName(channels), TypeName(type), filename); - closeAudioFile(sound); + if(sample->actual.format == AUDIO_U8) + format = AL_FORMAT_STEREO8; + else if(sample->actual.format == AUDIO_S16SYS) + format = AL_FORMAT_STEREO16; + else + { + fprintf(stderr, "Unsupported sample format: 0x%04x\n", sample->actual.format); + Sound_FreeSample(sample); + return 0; + } + } + else + { + fprintf(stderr, "Unsupported channel count: %d\n", sample->actual.channels); + Sound_FreeSample(sample); return 0; } /* Decode the whole audio stream to a buffer. */ - data = decodeAudioStream(sound, &datalen); - if(!data) + slen = Sound_DecodeAll(sample); + if(!sample->buffer || slen == 0) { fprintf(stderr, "Failed to read audio from %s\n", filename); - closeAudioFile(sound); + Sound_FreeSample(sample); return 0; } @@ -92,9 +109,8 @@ static ALuint LoadSound(const char *filename) * close the file. */ buffer = 0; alGenBuffers(1, &buffer); - alBufferData(buffer, format, data, datalen, rate); - free(data); - closeAudioFile(sound); + alBufferData(buffer, format, sample->buffer, slen, sample->actual.rate); + Sound_FreeSample(sample); /* Check if an error occured, and clean up if so. */ err = alGetError(); @@ -113,6 +129,7 @@ static ALuint LoadSound(const char *filename) int main(int argc, char **argv) { ALCdevice *device; + ALboolean has_angle_ext; ALuint source, buffer; const char *soundname; const char *hrtfname; @@ -121,28 +138,18 @@ int main(int argc, char **argv) ALdouble angle; ALenum state; - /* Print out usage if no file was specified */ - if(argc < 2 || (strcmp(argv[1], "-hrtf") == 0 && argc < 4)) + /* Print out usage if no arguments were specified */ + if(argc < 2) { - fprintf(stderr, "Usage: %s [-hrtf ] \n", argv[0]); + fprintf(stderr, "Usage: %s [-device ] [-hrtf ] \n", argv[0]); return 1; } - /* Initialize OpenAL with the default device, and check for HRTF support. */ - if(InitAL() != 0) + /* Initialize OpenAL, and check for HRTF support. */ + argv++; argc--; + if(InitAL(&argv, &argc) != 0) return 1; - if(strcmp(argv[1], "-hrtf") == 0) - { - hrtfname = argv[2]; - soundname = argv[3]; - } - else - { - hrtfname = NULL; - soundname = argv[1]; - } - device = alcGetContextsDevice(alcGetCurrentContext()); if(!alcIsExtensionPresent(device, "ALC_SOFT_HRTF")) { @@ -157,6 +164,24 @@ int main(int argc, char **argv) LOAD_PROC(device, alcResetDeviceSOFT); #undef LOAD_PROC + /* Check for the AL_EXT_STEREO_ANGLES extension to be able to also rotate + * stereo sources. + */ + has_angle_ext = alIsExtensionPresent("AL_EXT_STEREO_ANGLES"); + printf("AL_EXT_STEREO_ANGLES%s found\n", has_angle_ext?"":" not"); + + /* Check for user-preferred HRTF */ + if(strcmp(argv[0], "-hrtf") == 0) + { + hrtfname = argv[1]; + soundname = argv[2]; + } + else + { + hrtfname = NULL; + soundname = argv[0]; + } + /* Enumerate available HRTFs, and reset the device using one. */ alcGetIntegerv(device, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf); if(!num_hrtf) @@ -178,19 +203,22 @@ int main(int argc, char **argv) index = i; } + i = 0; + attr[i++] = ALC_HRTF_SOFT; + attr[i++] = ALC_TRUE; if(index == -1) { if(hrtfname) printf("HRTF \"%s\" not found\n", hrtfname); - index = 0; + printf("Using default HRTF...\n"); } - printf("Selecting HRTF %d...\n", index); - - attr[0] = ALC_HRTF_SOFT; - attr[1] = ALC_TRUE; - attr[2] = ALC_HRTF_ID_SOFT; - attr[3] = index; - attr[4] = 0; + else + { + printf("Selecting HRTF %d...\n", index); + attr[i++] = ALC_HRTF_ID_SOFT; + attr[i++] = index; + } + attr[i] = 0; if(!alcResetDeviceSOFT(device, attr)) printf("Failed to reset device: %s\n", alcGetString(device, alcGetError(device))); @@ -207,10 +235,14 @@ int main(int argc, char **argv) } fflush(stdout); + /* Initialize SDL_sound. */ + Sound_Init(); + /* Load the sound into a buffer. */ buffer = LoadSound(soundname); if(!buffer) { + Sound_Quit(); CloseAL(); return 1; } @@ -229,19 +261,33 @@ int main(int argc, char **argv) do { al_nssleep(10000000); - /* Rotate the source around the listener by about 1/4 cycle per second. - * Only affects mono sounds. + /* Rotate the source around the listener by about 1/4 cycle per second, + * and keep it within -pi...+pi. */ angle += 0.01 * M_PI * 0.5; + if(angle > M_PI) + angle -= M_PI*2.0; + + /* This only rotates mono sounds. */ alSource3f(source, AL_POSITION, (ALfloat)sin(angle), 0.0f, -(ALfloat)cos(angle)); + if(has_angle_ext) + { + /* This rotates stereo sounds with the AL_EXT_STEREO_ANGLES + * extension. Angles are specified counter-clockwise in radians. + */ + ALfloat angles[2] = { (ALfloat)(M_PI/6.0 - angle), (ALfloat)(-M_PI/6.0 - angle) }; + alSourcefv(source, AL_STEREO_ANGLES, angles); + } + alGetSourcei(source, AL_SOURCE_STATE, &state); } while(alGetError() == AL_NO_ERROR && state == AL_PLAYING); - /* All done. Delete resources, and close OpenAL. */ + /* All done. Delete resources, and close down SDL_sound and OpenAL. */ alDeleteSources(1, &source); alDeleteBuffers(1, &buffer); + Sound_Quit(); CloseAL(); return 0; diff --git a/libs/openal-soft/examples/allatency.c b/libs/openal-soft/examples/allatency.c index afef43ca..d561373f 100644 --- a/libs/openal-soft/examples/allatency.c +++ b/libs/openal-soft/examples/allatency.c @@ -27,17 +27,15 @@ #include #include +#include + #include "AL/al.h" #include "AL/alc.h" #include "AL/alext.h" #include "common/alhelpers.h" -#include "common/sdl_sound.h" -static LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT = wrap_BufferSamples; -static LPALISBUFFERFORMATSUPPORTEDSOFT alIsBufferFormatSupportedSOFT; - static LPALSOURCEDSOFT alSourcedSOFT; static LPALSOURCE3DSOFT alSource3dSOFT; static LPALSOURCEDVSOFT alSourcedvSOFT; @@ -52,47 +50,63 @@ static LPALGETSOURCE3I64SOFT alGetSource3i64SOFT; static LPALGETSOURCEI64VSOFT alGetSourcei64vSOFT; /* LoadBuffer loads the named audio file into an OpenAL buffer object, and - * returns the new buffer ID. */ + * returns the new buffer ID. + */ static ALuint LoadSound(const char *filename) { - ALenum err, format, type, channels; - ALuint rate, buffer; - size_t datalen; - void *data; - FilePtr sound; + Sound_Sample *sample; + ALenum err, format; + ALuint buffer; + Uint32 slen; /* Open the audio file */ - sound = openAudioFile(filename, 1000); - if(!sound) + sample = Sound_NewSampleFromFile(filename, NULL, 65536); + if(!sample) { fprintf(stderr, "Could not open audio in %s\n", filename); - closeAudioFile(sound); return 0; } /* Get the sound format, and figure out the OpenAL format */ - if(getAudioInfo(sound, &rate, &channels, &type) != 0) + if(sample->actual.channels == 1) { - fprintf(stderr, "Error getting audio info for %s\n", filename); - closeAudioFile(sound); - return 0; + if(sample->actual.format == AUDIO_U8) + format = AL_FORMAT_MONO8; + else if(sample->actual.format == AUDIO_S16SYS) + format = AL_FORMAT_MONO16; + else + { + fprintf(stderr, "Unsupported sample format: 0x%04x\n", sample->actual.format); + Sound_FreeSample(sample); + return 0; + } } - - format = GetFormat(channels, type, alIsBufferFormatSupportedSOFT); - if(format == AL_NONE) + else if(sample->actual.channels == 2) { - fprintf(stderr, "Unsupported format (%s, %s) for %s\n", - ChannelsName(channels), TypeName(type), filename); - closeAudioFile(sound); + if(sample->actual.format == AUDIO_U8) + format = AL_FORMAT_STEREO8; + else if(sample->actual.format == AUDIO_S16SYS) + format = AL_FORMAT_STEREO16; + else + { + fprintf(stderr, "Unsupported sample format: 0x%04x\n", sample->actual.format); + Sound_FreeSample(sample); + return 0; + } + } + else + { + fprintf(stderr, "Unsupported channel count: %d\n", sample->actual.channels); + Sound_FreeSample(sample); return 0; } /* Decode the whole audio stream to a buffer. */ - data = decodeAudioStream(sound, &datalen); - if(!data) + slen = Sound_DecodeAll(sample); + if(!sample->buffer || slen == 0) { fprintf(stderr, "Failed to read audio from %s\n", filename); - closeAudioFile(sound); + Sound_FreeSample(sample); return 0; } @@ -100,17 +114,15 @@ static ALuint LoadSound(const char *filename) * close the file. */ buffer = 0; alGenBuffers(1, &buffer); - alBufferSamplesSOFT(buffer, rate, format, BytesToFrames(datalen, channels, type), - channels, type, data); - free(data); - closeAudioFile(sound); + alBufferData(buffer, format, sample->buffer, slen, sample->actual.rate); + Sound_FreeSample(sample); /* Check if an error occured, and clean up if so. */ err = alGetError(); if(err != AL_NO_ERROR) { fprintf(stderr, "OpenAL Error: %s\n", alGetString(err)); - if(alIsBuffer(buffer)) + if(buffer && alIsBuffer(buffer)) alDeleteBuffers(1, &buffer); return 0; } @@ -125,15 +137,16 @@ int main(int argc, char **argv) ALdouble offsets[2]; ALenum state; - /* Print out usage if no file was specified */ + /* Print out usage if no arguments were specified */ if(argc < 2) { - fprintf(stderr, "Usage: %s \n", argv[0]); + fprintf(stderr, "Usage: %s [-device ] \n", argv[0]); return 1; } - /* Initialize OpenAL with the default device, and check for EFX support. */ - if(InitAL() != 0) + /* Initialize OpenAL, and check for source_latency support. */ + argv++; argc--; + if(InitAL(&argv, &argc) != 0) return 1; if(!alIsExtensionPresent("AL_SOFT_source_latency")) @@ -157,18 +170,16 @@ int main(int argc, char **argv) LOAD_PROC(alGetSourcei64SOFT); LOAD_PROC(alGetSource3i64SOFT); LOAD_PROC(alGetSourcei64vSOFT); - - if(alIsExtensionPresent("AL_SOFT_buffer_samples")) - { - LOAD_PROC(alBufferSamplesSOFT); - LOAD_PROC(alIsBufferFormatSupportedSOFT); - } #undef LOAD_PROC + /* Initialize SDL_sound. */ + Sound_Init(); + /* Load the sound into a buffer. */ - buffer = LoadSound(argv[1]); + buffer = LoadSound(argv[0]); if(!buffer) { + Sound_Quit(); CloseAL(); return 1; } @@ -194,10 +205,11 @@ int main(int argc, char **argv) } while(alGetError() == AL_NO_ERROR && state == AL_PLAYING); printf("\n"); - /* All done. Delete resources, and close OpenAL. */ + /* All done. Delete resources, and close down SDL_sound and OpenAL. */ alDeleteSources(1, &source); alDeleteBuffers(1, &buffer); + Sound_Quit(); CloseAL(); return 0; diff --git a/libs/openal-soft/examples/alloopback.c b/libs/openal-soft/examples/alloopback.c index c5dee36d..16553f9b 100644 --- a/libs/openal-soft/examples/alloopback.c +++ b/libs/openal-soft/examples/alloopback.c @@ -38,6 +38,13 @@ #include "common/alhelpers.h" +#ifndef SDL_AUDIO_MASK_BITSIZE +#define SDL_AUDIO_MASK_BITSIZE (0xFF) +#endif +#ifndef SDL_AUDIO_BITSIZE +#define SDL_AUDIO_BITSIZE(x) (x & SDL_AUDIO_MASK_BITSIZE) +#endif + #ifndef M_PI #define M_PI (3.14159265358979323846) #endif @@ -61,6 +68,35 @@ void SDLCALL RenderSDLSamples(void *userdata, Uint8 *stream, int len) } +static const char *ChannelsName(ALCenum chans) +{ + switch(chans) + { + case ALC_MONO_SOFT: return "Mono"; + case ALC_STEREO_SOFT: return "Stereo"; + case ALC_QUAD_SOFT: return "Quadraphonic"; + case ALC_5POINT1_SOFT: return "5.1 Surround"; + case ALC_6POINT1_SOFT: return "6.1 Surround"; + case ALC_7POINT1_SOFT: return "7.1 Surround"; + } + return "Unknown Channels"; +} + +static const char *TypeName(ALCenum type) +{ + switch(type) + { + case ALC_BYTE_SOFT: return "S8"; + case ALC_UNSIGNED_BYTE_SOFT: return "U8"; + case ALC_SHORT_SOFT: return "S16"; + case ALC_UNSIGNED_SHORT_SOFT: return "U16"; + case ALC_INT_SOFT: return "S32"; + case ALC_UNSIGNED_INT_SOFT: return "U32"; + case ALC_FLOAT_SOFT: return "Float32"; + } + return "Unknown Type"; +} + /* Creates a one second buffer containing a sine wave, and returns the new * buffer ID. */ static ALuint CreateSineWave(void) @@ -169,7 +205,7 @@ int main(int argc, char *argv[]) attrs[6] = 0; /* end of list */ - playback.FrameSize = FramesToBytes(1, attrs[1], attrs[3]); + playback.FrameSize = obtained.channels * SDL_AUDIO_BITSIZE(obtained.format) / 8; /* Initialize OpenAL loopback device, using our format attributes. */ playback.Device = alcLoopbackOpenDeviceSOFT(NULL); diff --git a/libs/openal-soft/examples/alrecord.c b/libs/openal-soft/examples/alrecord.c new file mode 100644 index 00000000..0835b468 --- /dev/null +++ b/libs/openal-soft/examples/alrecord.c @@ -0,0 +1,386 @@ +/* + * OpenAL Recording Example + * + * Copyright (c) 2017 by Chris Robinson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* This file contains a relatively simple recorder. */ + +#include +#include +#include +#include + +#include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" + +#include "common/alhelpers.h" + + +#if defined(_WIN64) +#define SZFMT "%I64u" +#elif defined(_WIN32) +#define SZFMT "%u" +#else +#define SZFMT "%zu" +#endif + + +static void fwrite16le(ALushort val, FILE *f) +{ + ALubyte data[2] = { val&0xff, (val>>8)&0xff }; + fwrite(data, 1, 2, f); +} + +static void fwrite32le(ALuint val, FILE *f) +{ + ALubyte data[4] = { val&0xff, (val>>8)&0xff, (val>>16)&0xff, (val>>24)&0xff }; + fwrite(data, 1, 4, f); +} + + +typedef struct Recorder { + ALCdevice *mDevice; + + FILE *mFile; + long mDataSizeOffset; + size_t mDataSize; + float mRecTime; + + int mChannels; + int mBits; + int mSampleRate; + size_t mFrameSize; + ALbyte *mBuffer; + ALsizei mBufferSize; +} Recorder; + +int main(int argc, char **argv) +{ + static const char optlist[] = +" --channels/-c Set channel count (1 or 2)\n" +" --bits/-b Set channel count (8, 16, or 32)\n" +" --rate/-r Set sample rate (8000 to 96000)\n" +" --time/-t