diff options
author | Sven Gothel <[email protected]> | 2023-11-28 12:51:46 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2023-11-28 12:51:46 +0100 |
commit | 1aaf4f070011490bcece50394b9b32dfa593fd9e (patch) | |
tree | 17d68284e401a35eea3d3a574d986d446a60763a | |
parent | 6e7cee4fa9a8af03f28ca26cd89f8357390dfc90 (diff) | |
parent | 571b546f35eead77ce109f8d4dd6c3de3199d573 (diff) |
Merge remote-tracking branch 'upstream/master'
195 files changed, 13985 insertions, 8977 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a94c760..b1c3b413 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,6 @@ name: CI -on: [push] +on: [push, pull_request] jobs: build: @@ -14,6 +14,7 @@ jobs: name: "Win32-Release", os: windows-latest, cmake_opts: "-A Win32 \ + -DALSOFT_TESTS=ON \ -DALSOFT_BUILD_ROUTER=ON \ -DALSOFT_REQUIRE_WINMM=ON \ -DALSOFT_REQUIRE_DSOUND=ON \ @@ -24,6 +25,7 @@ jobs: name: "Win32-Debug", os: windows-latest, cmake_opts: "-A Win32 \ + -DALSOFT_TESTS=ON \ -DALSOFT_BUILD_ROUTER=ON \ -DALSOFT_REQUIRE_WINMM=ON \ -DALSOFT_REQUIRE_DSOUND=ON \ @@ -34,6 +36,7 @@ jobs: name: "Win64-Release", os: windows-latest, cmake_opts: "-A x64 \ + -DALSOFT_TESTS=ON \ -DALSOFT_BUILD_ROUTER=ON \ -DALSOFT_REQUIRE_WINMM=ON \ -DALSOFT_REQUIRE_DSOUND=ON \ @@ -44,6 +47,7 @@ jobs: name: "Win64-Debug", os: windows-latest, cmake_opts: "-A x64 \ + -DALSOFT_TESTS=ON \ -DALSOFT_BUILD_ROUTER=ON \ -DALSOFT_REQUIRE_WINMM=ON \ -DALSOFT_REQUIRE_DSOUND=ON \ @@ -51,9 +55,34 @@ jobs: build_type: "Debug" } - { + name: "Win64-UWP", + os: windows-latest, + cmake_opts: "-A x64 \ + -DALSOFT_TESTS=OFF \ + -DCMAKE_SYSTEM_NAME=WindowsStore \ + \"-DCMAKE_SYSTEM_VERSION=10.0\" \ + -DALSOFT_BUILD_ROUTER=ON \ + -DALSOFT_REQUIRE_WASAPI=ON", + build_type: "Release" + } + - { name: "macOS-Release", os: macos-latest, - cmake_opts: "-DALSOFT_REQUIRE_COREAUDIO=ON", + cmake_opts: "-DALSOFT_REQUIRE_COREAUDIO=ON \ + -DALSOFT_TESTS=ON", + build_type: "Release" + } + - { + name: "iOS-Release", + os: macos-latest, + cmake_opts: "-GXcode \ + -DCMAKE_SYSTEM_NAME=iOS \ + -DALSOFT_REQUIRE_COREAUDIO=ON \ + -DALSOFT_UTILS=OFF \ + -DALSOFT_EXAMPLES=OFF \ + -DALSOFT_TESTS=OFF \ + -DALSOFT_INSTALL=OFF \ + \"-DCMAKE_OSX_ARCHITECTURES=arm64\"", build_type: "Release" } - { @@ -65,7 +94,8 @@ jobs: -DALSOFT_REQUIRE_PORTAUDIO=ON \ -DALSOFT_REQUIRE_PULSEAUDIO=ON \ -DALSOFT_REQUIRE_JACK=ON \ - -DALSOFT_REQUIRE_PIPEWIRE=ON", + -DALSOFT_REQUIRE_PIPEWIRE=ON \ + -DALSOFT_TESTS=ON", deps_cmdline: "sudo apt update && sudo apt-get install -qq \ libpulse-dev \ portaudio19-dev \ @@ -78,7 +108,7 @@ jobs: } steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Install Dependencies shell: bash @@ -97,6 +127,12 @@ jobs: run: | cmake --build build --config ${{matrix.config.build_type}} + - name: Test + shell: bash + run: | + cd build + ctest + - name: Create Archive if: ${{ matrix.config.os == 'windows-latest' }} shell: bash @@ -109,7 +145,7 @@ jobs: - name: Upload Archive # Upload package as an artifact of this workflow. - uses: actions/[email protected] + uses: actions/[email protected] if: ${{ matrix.config.os == 'windows-latest' }} with: name: soft_oal-${{matrix.config.name}} diff --git a/.github/workflows/makemhr.yml b/.github/workflows/makemhr.yml index 7bde284c..65486150 100644 --- a/.github/workflows/makemhr.yml +++ b/.github/workflows/makemhr.yml @@ -55,7 +55,7 @@ jobs: copy "libmysofa/windows/third-party/zlib-1.2.11/bin/zlib.dll" "Artifacts/zlib.dll" - name: Upload makemhr artifact - uses: actions/[email protected] + uses: actions/[email protected] with: name: makemhr path: "Artifacts/" @@ -1,6 +1,7 @@ build*/ winbuild win64build +.vs/ ## kdevelop *.kdev4 diff --git a/CMakeLists.txt b/CMakeLists.txt index 85571b45..2d572908 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ # CMake build file list for OpenAL -CMAKE_MINIMUM_REQUIRED(VERSION 3.2.0) +cmake_minimum_required(VERSION 3.13) +enable_testing() if(APPLE) # The workaround for try_compile failing with code signing @@ -28,11 +29,10 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS") FORCE) endif() endif() +elseif(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") + set(ALSOFT_UWP TRUE) endif() -set(CMAKE_C_VISIBILITY_PRESET hidden) -set(CMAKE_CXX_VISIBILITY_PRESET hidden) - if(COMMAND CMAKE_POLICY) cmake_policy(SET CMP0003 NEW) cmake_policy(SET CMP0005 NEW) @@ -76,8 +76,8 @@ if(NOT CMAKE_DEBUG_POSTFIX) endif() set(DEFAULT_TARGET_PROPS - # Require C++14. - CXX_STANDARD 14 + # Require C++17. + CXX_STANDARD 17 CXX_STANDARD_REQUIRED TRUE # Prefer C11, but support C99 and earlier when possible. C_STANDARD 11) @@ -109,6 +109,7 @@ option(ALSOFT_UTILS "Build utility programs" ON) option(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" OFF) option(ALSOFT_EXAMPLES "Build example programs" ON) +option(ALSOFT_TESTS "Build test programs" OFF) option(ALSOFT_INSTALL "Install main library" ON) option(ALSOFT_INSTALL_CONFIG "Install alsoft.conf sample configuration file" ON) @@ -148,6 +149,8 @@ set(CPP_DEFS ) # C pre-processor, not C++ set(INC_PATHS ) set(C_FLAGS ) set(LINKER_FLAGS ) +set(LINKER_FLAGS_DEBUG ) +set(LINKER_FLAGS_RELEASE ) set(EXTRA_LIBS ) if(WIN32) @@ -165,7 +168,7 @@ elseif(APPLE) endif() -# QNX's gcc do not uses /usr/include and /usr/lib pathes by default +# QNX's gcc do not uses /usr/include and /usr/lib paths by default if("${CMAKE_C_PLATFORM_ID}" STREQUAL "QNX") set(INC_PATHS ${INC_PATHS} /usr/include) set(LINKER_FLAGS ${LINKER_FLAGS} -L/usr/lib) @@ -186,6 +189,20 @@ set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0) set(EXPORT_DECL "") +# Some systems erroneously require the __STDC_FORMAT_MACROS macro to be defined +# to get the fixed-width integer type formatter macros. +check_cxx_source_compiles("#include <cinttypes> +#include <cstdio> +int main() +{ + int64_t i64{}; + std::printf(\"%\" PRId64, i64); +}" +HAVE_STDC_FORMAT_MACROS) +if(NOT HAVE_STDC_FORMAT_MACROS) + set(CPP_DEFS ${CPP_DEFS} __STDC_FORMAT_MACROS) +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) @@ -341,32 +358,32 @@ else() endif() # Set visibility/export options if available -if(WIN32) - if(NOT LIBTYPE STREQUAL "STATIC") +if(NOT LIBTYPE STREQUAL "STATIC") + if(WIN32) set(EXPORT_DECL "__declspec(dllexport)") - endif() -else() - set(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") - # Yes GCC, really don't accept visibility modes you don't support - set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Wattributes -Werror") - - check_c_source_compiles("int foo() __attribute__((visibility(\"protected\"))); - int main() {return 0;}" HAVE_GCC_PROTECTED_VISIBILITY) - if(HAVE_GCC_PROTECTED_VISIBILITY) - if(NOT LIBTYPE STREQUAL "STATIC") - set(EXPORT_DECL "__attribute__((visibility(\"protected\")))") - endif() else() - check_c_source_compiles("int foo() __attribute__((visibility(\"default\"))); - int main() {return 0;}" HAVE_GCC_DEFAULT_VISIBILITY) - if(HAVE_GCC_DEFAULT_VISIBILITY) - if(NOT LIBTYPE STREQUAL "STATIC") + set(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") + # Yes GCC, really don't accept visibility modes you don't support + set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Wattributes -Werror") + + check_c_source_compiles("int foo() __attribute__((visibility(\"protected\"))); + int main() {return 0;}" HAVE_GCC_PROTECTED_VISIBILITY) + if(HAVE_GCC_PROTECTED_VISIBILITY) + set(EXPORT_DECL "__attribute__((visibility(\"protected\")))") + else() + check_c_source_compiles("int foo() __attribute__((visibility(\"default\"))); + int main() {return 0;}" HAVE_GCC_DEFAULT_VISIBILITY) + if(HAVE_GCC_DEFAULT_VISIBILITY) set(EXPORT_DECL "__attribute__((visibility(\"default\")))") endif() endif() - endif() + if(HAVE_GCC_PROTECTED_VISIBILITY OR HAVE_GCC_DEFAULT_VISIBILITY) + set(CMAKE_C_VISIBILITY_PRESET hidden) + set(CMAKE_CXX_VISIBILITY_PRESET hidden) + endif() - set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}") + set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}") + endif() endif() @@ -452,6 +469,7 @@ if(ALSOFT_REQUIRE_NEON AND NOT HAVE_NEON) endif() +set(ALSOFT_FORCE_ALIGN ) set(SSE_FLAGS ) set(FPMATH_SET "0") if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2) @@ -476,6 +494,7 @@ if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2) # OSs don't guarantee this on 32-bit, so externally-callable # functions need to ensure an aligned stack. set(EXPORT_DECL "${EXPORT_DECL}__attribute__((force_align_arg_pointer))") + set(ALSOFT_FORCE_ALIGN "__attribute__((force_align_arg_pointer))") endif() endif() endif() @@ -517,7 +536,7 @@ if(HAVE_LIBRT) set(RT_LIB rt) endif() -# Check for the dlopen API (for dynamicly loading backend libs) +# Check for the dlopen API (for dynamically loading backend libs) if(ALSOFT_DLOPEN) check_include_file(dlfcn.h HAVE_DLFCN_H) check_library_exists(dl dlopen "" HAVE_LIBDL) @@ -599,20 +618,21 @@ check_symbol_exists(getopt unistd.h HAVE_GETOPT) # router, and certain tools and examples. set(COMMON_OBJS common/albit.h - common/albyte.h common/alcomplex.cpp common/alcomplex.h - common/aldeque.h common/alfstream.cpp common/alfstream.h common/almalloc.cpp common/almalloc.h common/alnumbers.h common/alnumeric.h - common/aloptional.h + common/alsem.cpp + common/alsem.h common/alspan.h common/alstring.cpp common/alstring.h + common/althrd_setname.cpp + common/althrd_setname.h common/altraits.h common/atomic.h common/comptr.h @@ -620,6 +640,8 @@ set(COMMON_OBJS common/dynload.h common/intrusive_ptr.h common/opthelpers.h + common/pffft.cpp + common/pffft.h common/phase_shifter.h common/polyphase_resampler.cpp common/polyphase_resampler.h @@ -628,8 +650,6 @@ set(COMMON_OBJS common/ringbuffer.h common/strutils.cpp common/strutils.h - common/threads.cpp - common/threads.h common/vecmat.h common/vector.h) @@ -752,6 +772,9 @@ set(OPENAL_OBJS al/auxeffectslot.h al/buffer.cpp al/buffer.h + al/debug.cpp + al/debug.h + al/direct_defs.h al/effect.cpp al/effect.h al/effects/autowah.cpp @@ -808,6 +831,9 @@ set(ALC_OBJS alc/effects/pshifter.cpp alc/effects/reverb.cpp alc/effects/vmorpher.cpp + alc/events.cpp + alc/events.h + alc/export_list.h alc/inprogext.h alc/panning.cpp) @@ -825,7 +851,6 @@ if(ALSOFT_EAX) al/eax/fx_slot_index.h al/eax/fx_slots.cpp al/eax/fx_slots.h - al/eax/globals.cpp al/eax/globals.h al/eax/utils.cpp al/eax/utils.h @@ -1002,37 +1027,39 @@ endif() # Check Windows-only backends if(WIN32) - # Check MMSystem backend - option(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON) - option(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF) - if(ALSOFT_BACKEND_WINMM) - set(HAVE_WINMM 1) - set(BACKENDS "${BACKENDS} WinMM,") - set(ALC_OBJS ${ALC_OBJS} alc/backends/winmm.cpp alc/backends/winmm.h) - # There doesn't seem to be good way to search for winmm.lib for MSVC. - # find_library doesn't find it without being told to look in a specific - # place in the WindowsSDK, but it links anyway. If there ends up being - # Windows targets without this, another means to detect it is needed. - set(EXTRA_LIBS winmm ${EXTRA_LIBS}) - endif() - - # Check DSound backend - option(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON) - option(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF) - if(ALSOFT_BACKEND_DSOUND) - check_include_file(dsound.h HAVE_DSOUND_H) - if(DXSDK_DIR) - find_path(DSOUND_INCLUDE_DIR NAMES "dsound.h" - PATHS "${DXSDK_DIR}" PATH_SUFFIXES include - DOC "The DirectSound include directory") + if (NOT ALSOFT_UWP) + # Check MMSystem backend + option(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON) + option(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF) + if(ALSOFT_BACKEND_WINMM) + set(HAVE_WINMM 1) + set(BACKENDS "${BACKENDS} WinMM,") + set(ALC_OBJS ${ALC_OBJS} alc/backends/winmm.cpp alc/backends/winmm.h) + # There doesn't seem to be good way to search for winmm.lib for MSVC. + # find_library doesn't find it without being told to look in a specific + # place in the WindowsSDK, but it links anyway. If there ends up being + # Windows targets without this, another means to detect it is needed. + set(EXTRA_LIBS winmm ${EXTRA_LIBS}) endif() - if(HAVE_DSOUND_H OR DSOUND_INCLUDE_DIR) - set(HAVE_DSOUND 1) - set(BACKENDS "${BACKENDS} DirectSound,") - set(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h) - if(NOT HAVE_DSOUND_H) - set(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIR}) + # Check DSound backend + option(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON) + option(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF) + if(ALSOFT_BACKEND_DSOUND) + check_include_file(dsound.h HAVE_DSOUND_H) + if(DXSDK_DIR) + find_path(DSOUND_INCLUDE_DIR NAMES "dsound.h" + PATHS "${DXSDK_DIR}" PATH_SUFFIXES include + DOC "The DirectSound include directory") + endif() + if(HAVE_DSOUND_H OR DSOUND_INCLUDE_DIR) + set(HAVE_DSOUND 1) + set(BACKENDS "${BACKENDS} DirectSound,") + set(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h) + + if(NOT HAVE_DSOUND_H) + set(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIR}) + endif() endif() endif() endif() @@ -1152,7 +1179,8 @@ if(ALSOFT_BACKEND_OPENSL) set(HAVE_OPENSL 1) set(ALC_OBJS ${ALC_OBJS} alc/backends/opensl.cpp alc/backends/opensl.h) set(BACKENDS "${BACKENDS} OpenSL,") - set(EXTRA_LIBS "OpenSL::OpenSLES" ${EXTRA_LIBS}) + set(EXTRA_LIBS ${OPENSL_LIBRARIES} ${EXTRA_LIBS}) + set(INC_PATHS ${INC_PATHS} ${OPENSL_INCLUDE_DIRS}) endif() endif() if(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL) @@ -1322,11 +1350,11 @@ configure_file( @ONLY) -add_library(common STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS}) -target_include_directories(common PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/include) -target_compile_definitions(common PRIVATE ${CPP_DEFS}) -target_compile_options(common PRIVATE ${C_FLAGS}) -set_target_properties(common PROPERTIES ${DEFAULT_TARGET_PROPS} POSITION_INDEPENDENT_CODE TRUE) +add_library(alcommon STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS}) +target_include_directories(alcommon PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/include) +target_compile_definitions(alcommon PRIVATE ${CPP_DEFS}) +target_compile_options(alcommon PRIVATE ${C_FLAGS}) +set_target_properties(alcommon PROPERTIES ${DEFAULT_TARGET_PROPS} POSITION_INDEPENDENT_CODE TRUE) unset(HAS_ROUTER) @@ -1361,7 +1389,7 @@ else() PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}" ${CPP_DEFS}) target_compile_options(OpenAL PRIVATE ${C_FLAGS}) - target_link_libraries(OpenAL PRIVATE common ${LINKER_FLAGS}) + target_link_libraries(OpenAL PRIVATE alcommon ${LINKER_FLAGS}) target_include_directories(OpenAL PUBLIC $<BUILD_INTERFACE:${OpenAL_SOURCE_DIR}/include> @@ -1392,13 +1420,31 @@ else() if(WIN32) set_target_properties(${IMPL_TARGET} PROPERTIES PREFIX "") endif() - target_link_libraries(${IMPL_TARGET} PRIVATE common ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) + target_link_libraries(${IMPL_TARGET} PRIVATE alcommon ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) + + if(ALSOFT_UWP) + set(ALSOFT_CPPWINRT_VERSION "2.0.230706.1" CACHE STRING "The soft-oal default cppwinrt version") + + find_program(NUGET_EXE NAMES nuget) + if(NOT NUGET_EXE) + message("NUGET.EXE not found.") + message(FATAL_ERROR "Please install this executable, and run CMake again.") + endif() + + exec_program(${NUGET_EXE} + ARGS install "Microsoft.Windows.CppWinRT" -Version ${ALSOFT_CPPWINRT_VERSION} -ExcludeVersion -OutputDirectory "\"${CMAKE_BINARY_DIR}/packages\"") + + set_target_properties(${IMPL_TARGET} PROPERTIES + VS_PROJECT_IMPORT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.props + ) + target_link_libraries(${IMPL_TARGET} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.targets) + endif() if(NOT WIN32 AND NOT APPLE) # FIXME: This doesn't put a dependency on the version script. Changing # the version script will not cause a relink as it should. - set_property(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY - LINK_FLAGS " -Wl,--version-script=${OpenAL_SOURCE_DIR}/libopenal.version") + target_link_options(${IMPL_TARGET} PRIVATE + "-Wl,--version-script=${OpenAL_SOURCE_DIR}/libopenal.version") endif() if(APPLE AND ALSOFT_OSX_FRAMEWORK) @@ -1475,8 +1521,7 @@ if(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC" 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") + target_link_options(OpenAL PRIVATE "-Wl,--output-def,OpenAL32.def") add_custom_command(TARGET OpenAL POST_BUILD COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def COMMAND "${CMAKE_DLLTOOL}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll @@ -1507,7 +1552,10 @@ if(FPMATH_SET) message(STATUS "Building with SSE${FPMATH_SET} codegen") message(STATUS "") endif() - +if(ALSOFT_UWP) + message(STATUS "Building with UWP support") + message(STATUS "") +endif() if(ALSOFT_EAX) message(STATUS "Building with legacy EAX extension support") message(STATUS "") @@ -1593,7 +1641,7 @@ if(ALSOFT_UTILS) target_include_directories(uhjdecoder PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common) target_compile_options(uhjdecoder PRIVATE ${C_FLAGS}) - target_link_libraries(uhjdecoder PUBLIC common + target_link_libraries(uhjdecoder PUBLIC alcommon PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG}) set_target_properties(uhjdecoder PROPERTIES ${DEFAULT_TARGET_PROPS}) @@ -1602,7 +1650,7 @@ if(ALSOFT_UTILS) target_include_directories(uhjencoder PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common) target_compile_options(uhjencoder PRIVATE ${C_FLAGS}) - target_link_libraries(uhjencoder PUBLIC common + target_link_libraries(uhjencoder PUBLIC alcommon PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG}) set_target_properties(uhjencoder PROPERTIES ${DEFAULT_TARGET_PROPS}) endif() @@ -1615,7 +1663,7 @@ if(ALSOFT_UTILS) target_compile_definitions(sofa-support PRIVATE ${CPP_DEFS}) target_include_directories(sofa-support PUBLIC ${OpenAL_SOURCE_DIR}/common) target_compile_options(sofa-support PRIVATE ${C_FLAGS}) - target_link_libraries(sofa-support PUBLIC common MySOFA::MySOFA PRIVATE ${LINKER_FLAGS}) + target_link_libraries(sofa-support PUBLIC alcommon MySOFA::MySOFA PRIVATE ${LINKER_FLAGS}) set_target_properties(sofa-support PROPERTIES ${DEFAULT_TARGET_PROPS}) set(MAKEMHR_SRCS @@ -1657,22 +1705,22 @@ endif() # Add a static library with common functions used by multiple example targets -add_library(ex-common STATIC EXCLUDE_FROM_ALL +add_library(al-excommon STATIC EXCLUDE_FROM_ALL examples/common/alhelpers.c examples/common/alhelpers.h) -target_compile_definitions(ex-common PUBLIC ${CPP_DEFS}) -target_include_directories(ex-common PUBLIC ${OpenAL_SOURCE_DIR}/common) -target_compile_options(ex-common PUBLIC ${C_FLAGS}) -target_link_libraries(ex-common PUBLIC OpenAL PRIVATE ${RT_LIB}) -set_target_properties(ex-common PROPERTIES ${DEFAULT_TARGET_PROPS}) +target_compile_definitions(al-excommon PUBLIC ${CPP_DEFS}) +target_include_directories(al-excommon PUBLIC ${OpenAL_SOURCE_DIR}/common) +target_compile_options(al-excommon PUBLIC ${C_FLAGS}) +target_link_libraries(al-excommon PUBLIC OpenAL PRIVATE ${RT_LIB}) +set_target_properties(al-excommon PROPERTIES ${DEFAULT_TARGET_PROPS}) if(ALSOFT_EXAMPLES) add_executable(altonegen examples/altonegen.c) - target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} ex-common ${UNICODE_FLAG}) + target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} al-excommon ${UNICODE_FLAG}) set_target_properties(altonegen PROPERTIES ${DEFAULT_TARGET_PROPS}) add_executable(alrecord examples/alrecord.c) - target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} ex-common ${UNICODE_FLAG}) + target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} al-excommon ${UNICODE_FLAG}) set_target_properties(alrecord PROPERTIES ${DEFAULT_TARGET_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) @@ -1683,43 +1731,43 @@ if(ALSOFT_EXAMPLES) if(SNDFILE_FOUND) add_executable(alplay examples/alplay.c) - target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common + target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${UNICODE_FLAG}) set_target_properties(alplay PROPERTIES ${DEFAULT_TARGET_PROPS}) add_executable(alstream examples/alstream.c) - target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common + target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${UNICODE_FLAG}) set_target_properties(alstream PROPERTIES ${DEFAULT_TARGET_PROPS}) add_executable(alreverb examples/alreverb.c) - target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common + target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${UNICODE_FLAG}) set_target_properties(alreverb PROPERTIES ${DEFAULT_TARGET_PROPS}) add_executable(almultireverb examples/almultireverb.c) target_link_libraries(almultireverb - PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${MATH_LIB} ${UNICODE_FLAG}) + PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${MATH_LIB} ${UNICODE_FLAG}) set_target_properties(almultireverb PROPERTIES ${DEFAULT_TARGET_PROPS}) add_executable(allatency examples/allatency.c) - target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common + target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${UNICODE_FLAG}) set_target_properties(allatency PROPERTIES ${DEFAULT_TARGET_PROPS}) add_executable(alhrtf examples/alhrtf.c) target_link_libraries(alhrtf - PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${MATH_LIB} ${UNICODE_FLAG}) + PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${MATH_LIB} ${UNICODE_FLAG}) set_target_properties(alhrtf PROPERTIES ${DEFAULT_TARGET_PROPS}) add_executable(alstreamcb examples/alstreamcb.cpp) - target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common + target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${UNICODE_FLAG}) set_target_properties(alstreamcb PROPERTIES ${DEFAULT_TARGET_PROPS}) add_executable(alconvolve examples/alconvolve.c) - target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} common SndFile::SndFile ex-common - ${UNICODE_FLAG}) + target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} alcommon SndFile::SndFile + al-excommon ${UNICODE_FLAG}) set_target_properties(alconvolve PROPERTIES ${DEFAULT_TARGET_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) @@ -1733,7 +1781,7 @@ if(ALSOFT_EXAMPLES) if(SDL2_FOUND) add_executable(alloopback examples/alloopback.c) target_link_libraries(alloopback - PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ex-common ${MATH_LIB}) + PRIVATE ${LINKER_FLAGS} SDL2::SDL2 al-excommon ${MATH_LIB}) set_target_properties(alloopback PROPERTIES ${DEFAULT_TARGET_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) @@ -1770,7 +1818,7 @@ if(ALSOFT_EXAMPLES) add_executable(alffplay examples/alffplay.cpp) target_include_directories(alffplay PRIVATE ${FFMPEG_INCLUDE_DIRS}) target_link_libraries(alffplay - PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ${FFMPEG_LIBRARIES} ex-common) + PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ${FFMPEG_LIBRARIES} al-excommon) set_target_properties(alffplay PROPERTIES ${DEFAULT_TARGET_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) @@ -1782,6 +1830,10 @@ if(ALSOFT_EXAMPLES) message(STATUS "") endif() +if (ALSOFT_TESTS) +add_subdirectory(tests) +endif() + if(EXTRA_INSTALLS) install(TARGETS ${EXTRA_INSTALLS} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} diff --git a/LICENSE-pffft b/LICENSE-pffft new file mode 100644 index 00000000..5f1a8493 --- /dev/null +++ b/LICENSE-pffft @@ -0,0 +1,38 @@ +A modified PFFFT is included, with the following license. + +Copyright (c) 2023 Christopher Robinson + +Copyright (c) 2013 Julien Pommier ( [email protected] ) + +Copyright (c) 2004 the University Corporation for Atmospheric +Research ("UCAR"). All rights reserved. Developed by NCAR's +Computational and Information Systems Laboratory, UCAR, +www.cisl.ucar.edu. + +Redistribution and use of the Software in source and binary forms, +with or without modification, is permitted provided that the +following conditions are met: + +- Neither the names of NCAR's Computational and Information Systems +Laboratory, the University Corporation for Atmospheric Research, +nor the names of its sponsors or contributors may be used to +endorse or promote products derived from this Software without +specific prior written permission. + +- Redistributions of source code must retain the above copyright +notices, this list of conditions, and the disclaimer below. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions, and the disclaimer below in the +documentation and/or other materials provided with the +distribution. + +THIS 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 CONTRIBUTORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL 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 WITH THE +SOFTWARE. diff --git a/OpenALConfig.cmake.in b/OpenALConfig.cmake.in index 128c1a4e..9704d3c4 100644 --- a/OpenALConfig.cmake.in +++ b/OpenALConfig.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.1...3.18) include("${CMAKE_CURRENT_LIST_DIR}/OpenALTargets.cmake") @@ -64,6 +64,33 @@ as application-agnostic behavior of the library. See alsoftrc.sample for available settings. +Language Bindings +----------------- + +As a C API, OpenAL Soft can be used directly by any language that can use +functions with C linkage. For languages that can't directly use C-style +headers, bindings may be developed to allow code written in that language to +call into the library. Some bindings for some languages are listed here. + +C# Bindings: +* [OpenTK](https://opentk.net/) includes low-level C# bindings for the OpenAL +API, including some extensions. It also includes utility libraries for math and +linear algebra, which can be useful for 3D calculations. + +Java Bindings: +* [JOAL](https://jogamp.org/joal/www/), part of the JogAmp project, includes +Java bindings for the OpenAL API, usable with OpenAL Soft. It also includes a +higher level Sound3D Toolkit API and utility functions to make easier use of +OpenAL features and capabilities. + +Python Bindings: +* [PyOpenAL](https://pypi.org/project/PyOpenAL/). Also includes methods to play +wave files and, with PyOgg, also Vorbis, Opus, and FLAC. + +Other bindings for these and other languages also exist. This list will grow as +more bindings are found. + + Acknowledgements ---------------- diff --git a/al/auxeffectslot.cpp b/al/auxeffectslot.cpp index 285da1d4..fb646389 100644 --- a/al/auxeffectslot.cpp +++ b/al/auxeffectslot.cpp @@ -47,6 +47,7 @@ #include "core/except.h" #include "core/fpu_ctrl.h" #include "core/logging.h" +#include "direct_defs.h" #include "effect.h" #include "opthelpers.h" @@ -165,7 +166,7 @@ void AddActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext *co curarray = context->mActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel); context->mDevice->waitForMix(); - al::destroy_n(curarray->end(), curarray->size()); + std::destroy_n(curarray->end(), curarray->size()); delete curarray; } @@ -204,7 +205,7 @@ void RemoveActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext curarray = context->mActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel); context->mDevice->waitForMix(); - al::destroy_n(curarray->end(), curarray->size()); + std::destroy_n(curarray->end(), curarray->size()); delete curarray; } @@ -229,7 +230,7 @@ EffectSlotType EffectSlotTypeFromEnum(ALenum type) case AL_EFFECT_EAXREVERB: return EffectSlotType::EAXReverb; case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return EffectSlotType::DedicatedLFE; case AL_EFFECT_DEDICATED_DIALOGUE: return EffectSlotType::DedicatedDialog; - case AL_EFFECT_CONVOLUTION_REVERB_SOFT: return EffectSlotType::Convolution; + case AL_EFFECT_CONVOLUTION_SOFT: return EffectSlotType::Convolution; } ERR("Unhandled effect enum: 0x%04x\n", type); return EffectSlotType::None; @@ -238,7 +239,7 @@ EffectSlotType EffectSlotTypeFromEnum(ALenum type) bool EnsureEffectSlots(ALCcontext *context, size_t needed) { size_t count{std::accumulate(context->mEffectSlotList.cbegin(), - context->mEffectSlotList.cend(), size_t{0}, + context->mEffectSlotList.cend(), 0_uz, [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })}; @@ -285,11 +286,13 @@ ALeffectslot *AllocEffectSlot(ALCcontext *context) void FreeEffectSlot(ALCcontext *context, ALeffectslot *slot) { + context->mEffectSlotNames.erase(slot->id); + const ALuint id{slot->id - 1}; const size_t lidx{id >> 6}; const ALuint slidx{id & 0x3f}; - al::destroy_at(slot); + std::destroy_at(slot); context->mEffectSlotList[lidx].FreeMask |= 1_u64 << slidx; context->mNumEffectSlots--; @@ -309,12 +312,10 @@ inline void UpdateProps(ALeffectslot *slot, ALCcontext *context) } // namespace -AL_API void AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots) -START_API_FUNC +AL_API DECL_FUNC2(void, alGenAuxiliaryEffectSlots, ALsizei, ALuint*) +FORCE_ALIGN void AL_APIENTRY alGenAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n, + ALuint *effectslots) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(n < 0) UNLIKELY context->setError(AL_INVALID_VALUE, "Generating %d effect slots", n); if(n <= 0) UNLIKELY return; @@ -327,7 +328,7 @@ START_API_FUNC device->AuxiliaryEffectSlotMax, context->mNumEffectSlots, n); return; } - if(!EnsureEffectSlots(context.get(), static_cast<ALuint>(n))) + if(!EnsureEffectSlots(context, static_cast<ALuint>(n))) { context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d effectslot%s", n, (n==1) ? "" : "s"); @@ -336,29 +337,26 @@ START_API_FUNC if(n == 1) { - ALeffectslot *slot{AllocEffectSlot(context.get())}; + ALeffectslot *slot{AllocEffectSlot(context)}; effectslots[0] = slot->id; } else { - al::vector<ALuint> ids; + std::vector<ALuint> ids; ALsizei count{n}; ids.reserve(static_cast<ALuint>(count)); do { - ALeffectslot *slot{AllocEffectSlot(context.get())}; + ALeffectslot *slot{AllocEffectSlot(context)}; ids.emplace_back(slot->id); } while(--count); std::copy(ids.cbegin(), ids.cend(), effectslots); } } -END_API_FUNC -AL_API void AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots) -START_API_FUNC +AL_API DECL_FUNC2(void, alDeleteAuxiliaryEffectSlots, ALsizei, const ALuint*) +FORCE_ALIGN void AL_APIENTRY alDeleteAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n, + const ALuint *effectslots) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(n < 0) UNLIKELY context->setError(AL_INVALID_VALUE, "Deleting %d effect slots", n); if(n <= 0) UNLIKELY return; @@ -366,7 +364,7 @@ START_API_FUNC std::lock_guard<std::mutex> _{context->mEffectSlotLock}; if(n == 1) { - ALeffectslot *slot{LookupEffectSlot(context.get(), effectslots[0])}; + ALeffectslot *slot{LookupEffectSlot(context, effectslots[0])}; if(!slot) UNLIKELY { context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslots[0]); @@ -378,15 +376,15 @@ START_API_FUNC effectslots[0]); return; } - RemoveActiveEffectSlots({&slot, 1u}, context.get()); - FreeEffectSlot(context.get(), slot); + RemoveActiveEffectSlots({&slot, 1u}, context); + FreeEffectSlot(context, slot); } else { - auto slots = al::vector<ALeffectslot*>(static_cast<ALuint>(n)); + auto slots = std::vector<ALeffectslot*>(static_cast<ALuint>(n)); for(size_t i{0};i < slots.size();++i) { - ALeffectslot *slot{LookupEffectSlot(context.get(), effectslots[i])}; + ALeffectslot *slot{LookupEffectSlot(context, effectslots[i])}; if(!slot) UNLIKELY { context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslots[i]); @@ -410,30 +408,24 @@ START_API_FUNC slots.erase(slots_end, slots.end()); /* All effectslots are valid, remove and delete them */ - RemoveActiveEffectSlots(slots, context.get()); + RemoveActiveEffectSlots(slots, context); for(ALeffectslot *slot : slots) - FreeEffectSlot(context.get(), slot); + FreeEffectSlot(context, slot); } } -END_API_FUNC -AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot) -START_API_FUNC +AL_API DECL_FUNC1(ALboolean, alIsAuxiliaryEffectSlot, ALuint) +FORCE_ALIGN ALboolean AL_APIENTRY alIsAuxiliaryEffectSlotDirect(ALCcontext *context, + ALuint effectslot) noexcept { - ContextRef context{GetContextRef()}; - if(context) LIKELY - { - std::lock_guard<std::mutex> _{context->mEffectSlotLock}; - if(LookupEffectSlot(context.get(), effectslot) != nullptr) - return AL_TRUE; - } + std::lock_guard<std::mutex> _{context->mEffectSlotLock}; + if(LookupEffectSlot(context, effectslot) != nullptr) + return AL_TRUE; return AL_FALSE; } -END_API_FUNC -AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid) -START_API_FUNC +AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid) noexcept { ContextRef context{GetContextRef()}; if(!context) UNLIKELY return; @@ -454,10 +446,8 @@ START_API_FUNC AddActiveEffectSlots({&slot, 1}, context.get()); slot->mState = SlotState::Playing; } -END_API_FUNC -AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids) -START_API_FUNC +AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids) noexcept { ContextRef context{GetContextRef()}; if(!context) UNLIKELY return; @@ -466,7 +456,7 @@ START_API_FUNC context->setError(AL_INVALID_VALUE, "Playing %d effect slots", n); if(n <= 0) UNLIKELY return; - auto slots = al::vector<ALeffectslot*>(static_cast<ALuint>(n)); + auto slots = std::vector<ALeffectslot*>(static_cast<ALuint>(n)); std::lock_guard<std::mutex> _{context->mEffectSlotLock}; for(size_t i{0};i < slots.size();++i) { @@ -489,10 +479,8 @@ START_API_FUNC for(auto slot : slots) slot->mState = SlotState::Playing; } -END_API_FUNC -AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid) -START_API_FUNC +AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid) noexcept { ContextRef context{GetContextRef()}; if(!context) UNLIKELY return; @@ -508,10 +496,8 @@ START_API_FUNC RemoveActiveEffectSlots({&slot, 1}, context.get()); slot->mState = SlotState::Stopped; } -END_API_FUNC -AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids) -START_API_FUNC +AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids) noexcept { ContextRef context{GetContextRef()}; if(!context) UNLIKELY return; @@ -520,7 +506,7 @@ START_API_FUNC context->setError(AL_INVALID_VALUE, "Stopping %d effect slots", n); if(n <= 0) UNLIKELY return; - auto slots = al::vector<ALeffectslot*>(static_cast<ALuint>(n)); + auto slots = std::vector<ALeffectslot*>(static_cast<ALuint>(n)); std::lock_guard<std::mutex> _{context->mEffectSlotLock}; for(size_t i{0};i < slots.size();++i) { @@ -538,18 +524,15 @@ START_API_FUNC for(auto slot : slots) slot->mState = SlotState::Stopped; } -END_API_FUNC -AL_API void AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint value) -START_API_FUNC +AL_API DECL_FUNC3(void, alAuxiliaryEffectSloti, ALuint, ALenum, ALint) +FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot, + ALenum param, ALint value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; std::lock_guard<std::mutex> __{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot); + ALeffectslot *slot = LookupEffectSlot(context, effectslot); if(!slot) UNLIKELY return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); @@ -565,12 +548,12 @@ START_API_FUNC std::lock_guard<std::mutex> ___{device->EffectLock}; ALeffect *effect{value ? LookupEffect(device, static_cast<ALuint>(value)) : nullptr}; if(effect) - err = slot->initEffect(effect->type, effect->Props, context.get()); + err = slot->initEffect(effect->id, effect->type, effect->Props, context); else { if(value != 0) return context->setError(AL_INVALID_VALUE, "Invalid effect ID %u", value); - err = slot->initEffect(AL_EFFECT_NULL, EffectProps{}, context.get()); + err = slot->initEffect(0, AL_EFFECT_NULL, EffectProps{}, context); } } if(err != AL_NO_ERROR) UNLIKELY @@ -581,9 +564,9 @@ START_API_FUNC if(slot->mState == SlotState::Initial) UNLIKELY { slot->mPropsDirty = false; - slot->updateProps(context.get()); + slot->updateProps(context); - AddActiveEffectSlots({&slot, 1}, context.get()); + AddActiveEffectSlots({&slot, 1}, context); slot->mState = SlotState::Playing; return; } @@ -599,7 +582,7 @@ START_API_FUNC break; case AL_EFFECTSLOT_TARGET_SOFT: - target = LookupEffectSlot(context.get(), static_cast<ALuint>(value)); + target = LookupEffectSlot(context, static_cast<ALuint>(value)); if(value && !target) return context->setError(AL_INVALID_VALUE, "Invalid effect slot target ID"); if(slot->Target == target) UNLIKELY @@ -623,7 +606,7 @@ START_API_FUNC if(target) IncrementRef(target->ref); DecrementRef(oldtarget->ref); slot->Target = target; - slot->updateProps(context.get()); + slot->updateProps(context); return; } @@ -677,12 +660,12 @@ START_API_FUNC return context->setError(AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param); } - UpdateProps(slot, context.get()); + UpdateProps(slot, context); } -END_API_FUNC -AL_API void AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotiv, ALuint, ALenum, const ALint*) +FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot, + ALenum param, const ALint *values) noexcept { switch(param) { @@ -691,15 +674,12 @@ START_API_FUNC case AL_EFFECTSLOT_TARGET_SOFT: case AL_EFFECTSLOT_STATE_SOFT: case AL_BUFFER: - alAuxiliaryEffectSloti(effectslot, param, values[0]); + alAuxiliaryEffectSlotiDirect(context, effectslot, param, values[0]); return; } - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot); + ALeffectslot *slot = LookupEffectSlot(context, effectslot); if(!slot) UNLIKELY return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); @@ -710,17 +690,14 @@ START_API_FUNC "Invalid effect slot integer-vector property 0x%04x", param); } } -END_API_FUNC -AL_API void AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat value) -START_API_FUNC +AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotf, ALuint, ALenum, ALfloat) +FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot, + ALenum param, ALfloat value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; std::lock_guard<std::mutex> __{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot); + ALeffectslot *slot = LookupEffectSlot(context, effectslot); if(!slot) UNLIKELY return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); @@ -738,25 +715,22 @@ START_API_FUNC return context->setError(AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param); } - UpdateProps(slot, context.get()); + UpdateProps(slot, context); } -END_API_FUNC -AL_API void AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotfv, ALuint, ALenum, const ALfloat*) +FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot, + ALenum param, const ALfloat *values) noexcept { switch(param) { case AL_EFFECTSLOT_GAIN: - alAuxiliaryEffectSlotf(effectslot, param, values[0]); + alAuxiliaryEffectSlotfDirect(context, effectslot, param, values[0]); return; } - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot); + ALeffectslot *slot = LookupEffectSlot(context, effectslot); if(!slot) UNLIKELY return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); @@ -767,22 +741,23 @@ START_API_FUNC "Invalid effect slot float-vector property 0x%04x", param); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *value) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSloti, ALuint, ALenum, ALint*) +FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotiDirect(ALCcontext *context, + ALuint effectslot, ALenum param, ALint *value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot); + ALeffectslot *slot = LookupEffectSlot(context, effectslot); if(!slot) UNLIKELY return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); switch(param) { + case AL_EFFECTSLOT_EFFECT: + *value = static_cast<ALint>(slot->EffectId); + break; + case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO: *value = slot->AuxSendAuto ? AL_TRUE : AL_FALSE; break; @@ -809,10 +784,10 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotiv, ALuint, ALenum, ALint*) +FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotivDirect(ALCcontext *context, + ALuint effectslot, ALenum param, ALint *values) noexcept { switch(param) { @@ -821,15 +796,12 @@ START_API_FUNC case AL_EFFECTSLOT_TARGET_SOFT: case AL_EFFECTSLOT_STATE_SOFT: case AL_BUFFER: - alGetAuxiliaryEffectSloti(effectslot, param, values); + alGetAuxiliaryEffectSlotiDirect(context, effectslot, param, values); return; } - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot); + ALeffectslot *slot = LookupEffectSlot(context, effectslot); if(!slot) UNLIKELY return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); @@ -840,16 +812,13 @@ START_API_FUNC param); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *value) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotf, ALuint, ALenum, ALfloat*) +FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotfDirect(ALCcontext *context, + ALuint effectslot, ALenum param, ALfloat *value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot); + ALeffectslot *slot = LookupEffectSlot(context, effectslot); if(!slot) UNLIKELY return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); @@ -863,23 +832,20 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotfv, ALuint, ALenum, ALfloat*) +FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotfvDirect(ALCcontext *context, + ALuint effectslot, ALenum param, ALfloat *values) noexcept { switch(param) { case AL_EFFECTSLOT_GAIN: - alGetAuxiliaryEffectSlotf(effectslot, param, values); + alGetAuxiliaryEffectSlotfDirect(context, effectslot, param, values); return; } - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot); + ALeffectslot *slot = LookupEffectSlot(context, effectslot); if(!slot) UNLIKELY return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); @@ -890,7 +856,6 @@ START_API_FUNC param); } } -END_API_FUNC ALeffectslot::ALeffectslot(ALCcontext *context) @@ -926,7 +891,7 @@ ALeffectslot::~ALeffectslot() mSlot->InUse = false; } -ALenum ALeffectslot::initEffect(ALenum effectType, const EffectProps &effectProps, +ALenum ALeffectslot::initEffect(ALuint effectId, ALenum effectType, const EffectProps &effectProps, ALCcontext *context) { EffectSlotType newtype{EffectSlotTypeFromEnum(effectType)}; @@ -955,6 +920,7 @@ ALenum ALeffectslot::initEffect(ALenum effectType, const EffectProps &effectProp } else if(newtype != EffectSlotType::None) Effect.Props = effectProps; + EffectId = effectId; /* Remove state references from old effect slot property updates. */ EffectSlotProps *props{context->mFreeEffectslotProps.load()}; @@ -1003,6 +969,17 @@ void ALeffectslot::updateProps(ALCcontext *context) } } +void ALeffectslot::SetName(ALCcontext* context, ALuint id, std::string_view name) +{ + std::lock_guard<std::mutex> _{context->mEffectSlotLock}; + + auto slot = LookupEffectSlot(context, id); + if(!slot) UNLIKELY + return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", id); + + context->mEffectSlotNames.insert_or_assign(id, name); +} + void UpdateAllEffectSlotProps(ALCcontext *context) { std::lock_guard<std::mutex> _{context->mEffectSlotLock}; @@ -1023,11 +1000,14 @@ void UpdateAllEffectSlotProps(ALCcontext *context) EffectSlotSubList::~EffectSlotSubList() { + if(!EffectSlots) + return; + uint64_t usemask{~FreeMask}; while(usemask) { const int idx{al::countr_zero(usemask)}; - al::destroy_at(EffectSlots+idx); + std::destroy_at(EffectSlots+idx); usemask &= ~(1_u64 << idx); } FreeMask = ~usemask; @@ -1272,7 +1252,7 @@ void ALeffectslot::eax_fx_slot_load_effect(int version, ALenum altype) void ALeffectslot::eax_fx_slot_set_volume() { - const auto volume = clamp(eax_.lVolume, EAXFXSLOT_MINVOLUME, EAXFXSLOT_MAXVOLUME); + const auto volume = std::clamp(eax_.lVolume, EAXFXSLOT_MINVOLUME, EAXFXSLOT_MAXVOLUME); const auto gain = level_mb_to_gain(static_cast<float>(volume)); eax_set_efx_slot_gain(gain); } @@ -1470,7 +1450,8 @@ void ALeffectslot::eax_set_efx_slot_effect(EaxEffect &effect) { #define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] " - const auto error = initEffect(effect.al_effect_type_, effect.al_effect_props_, eax_al_context_); + const auto error = initEffect(0, effect.al_effect_type_, effect.al_effect_props_, + eax_al_context_); if(error != AL_NO_ERROR) { ERR(EAX_PREFIX "%s\n", "Failed to initialize an effect."); diff --git a/al/auxeffectslot.h b/al/auxeffectslot.h index 3e9a2a4e..1ad0ffc4 100644 --- a/al/auxeffectslot.h +++ b/al/auxeffectslot.h @@ -3,6 +3,7 @@ #include <atomic> #include <cstddef> +#include <string_view> #include "AL/al.h" #include "AL/alc.h" @@ -45,6 +46,7 @@ enum class SlotState : ALenum { }; struct ALeffectslot { + ALuint EffectId{}; float Gain{1.0f}; bool AuxSendAuto{true}; ALeffectslot *Target{nullptr}; @@ -73,9 +75,12 @@ struct ALeffectslot { ALeffectslot& operator=(const ALeffectslot&) = delete; ~ALeffectslot(); - ALenum initEffect(ALenum effectType, const EffectProps &effectProps, ALCcontext *context); + ALenum initEffect(ALuint effectId, ALenum effectType, const EffectProps &effectProps, + ALCcontext *context); void updateProps(ALCcontext *context); + static void SetName(ALCcontext *context, ALuint id, std::string_view name); + /* This can be new'd for the context's default effect slot. */ DEF_NEWDEL(ALeffectslot) diff --git a/al/buffer.cpp b/al/buffer.cpp index ee506596..7d043036 100644 --- a/al/buffer.cpp +++ b/al/buffer.cpp @@ -26,6 +26,7 @@ #include <array> #include <atomic> #include <cassert> +#include <cstddef> #include <cstdint> #include <cstdlib> #include <cstring> @@ -35,28 +36,31 @@ #include <mutex> #include <new> #include <numeric> +#include <optional> #include <stdexcept> #include <utility> +#include <vector> #include "AL/al.h" #include "AL/alc.h" #include "AL/alext.h" #include "albit.h" -#include "albyte.h" #include "alc/context.h" #include "alc/device.h" #include "alc/inprogext.h" #include "almalloc.h" #include "alnumeric.h" -#include "aloptional.h" #include "atomic.h" #include "core/except.h" #include "core/logging.h" #include "core/voice.h" +#include "direct_defs.h" #include "opthelpers.h" #ifdef ALSOFT_EAX +#include <unordered_set> + #include "eax/globals.h" #include "eax/x_ram.h" #endif // ALSOFT_EAX @@ -64,14 +68,14 @@ namespace { -al::optional<AmbiLayout> AmbiLayoutFromEnum(ALenum layout) +std::optional<AmbiLayout> AmbiLayoutFromEnum(ALenum layout) { switch(layout) { case AL_FUMA_SOFT: return AmbiLayout::FuMa; case AL_ACN_SOFT: return AmbiLayout::ACN; } - return al::nullopt; + return std::nullopt; } ALenum EnumFromAmbiLayout(AmbiLayout layout) { @@ -83,7 +87,7 @@ ALenum EnumFromAmbiLayout(AmbiLayout layout) throw std::runtime_error{"Invalid AmbiLayout: "+std::to_string(int(layout))}; } -al::optional<AmbiScaling> AmbiScalingFromEnum(ALenum scale) +std::optional<AmbiScaling> AmbiScalingFromEnum(ALenum scale) { switch(scale) { @@ -91,7 +95,7 @@ al::optional<AmbiScaling> AmbiScalingFromEnum(ALenum scale) case AL_SN3D_SOFT: return AmbiScaling::SN3D; case AL_N3D_SOFT: return AmbiScaling::N3D; } - return al::nullopt; + return std::nullopt; } ALenum EnumFromAmbiScaling(AmbiScaling scale) { @@ -106,7 +110,7 @@ ALenum EnumFromAmbiScaling(AmbiScaling scale) } #ifdef ALSOFT_EAX -al::optional<EaxStorage> EaxStorageFromEnum(ALenum scale) +std::optional<EaxStorage> EaxStorageFromEnum(ALenum scale) { switch(scale) { @@ -114,7 +118,7 @@ al::optional<EaxStorage> EaxStorageFromEnum(ALenum scale) case AL_STORAGE_ACCESSIBLE: return EaxStorage::Accessible; case AL_STORAGE_HARDWARE: return EaxStorage::Hardware; } - return al::nullopt; + return std::nullopt; } ALenum EnumFromEaxStorage(EaxStorage storage) { @@ -170,7 +174,7 @@ constexpr ALbitfieldSOFT INVALID_MAP_FLAGS{~unsigned(AL_MAP_READ_BIT_SOFT | AL_M bool EnsureBuffers(ALCdevice *device, size_t needed) { - size_t count{std::accumulate(device->BufferList.cbegin(), device->BufferList.cend(), size_t{0}, + size_t count{std::accumulate(device->BufferList.cbegin(), device->BufferList.cend(), 0_uz, [](size_t cur, const BufferSubList &sublist) noexcept -> size_t { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })}; @@ -218,11 +222,13 @@ void FreeBuffer(ALCdevice *device, ALbuffer *buffer) eax_x_ram_clear(*device, *buffer); #endif // ALSOFT_EAX + device->mBufferNames.erase(buffer->id); + const ALuint id{buffer->id - 1}; const size_t lidx{id >> 6}; const ALuint slidx{id & 0x3f}; - al::destroy_at(buffer); + std::destroy_at(buffer); device->BufferList[lidx].FreeMask |= 1_u64 << slidx; } @@ -277,7 +283,7 @@ ALuint SanitizeAlignment(FmtType type, ALuint align) /** Loads the specified data into the buffer, using the specified format. */ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, - const FmtChannels DstChannels, const FmtType DstType, const al::byte *SrcData, + const FmtChannels DstChannels, const FmtType DstType, const std::byte *SrcData, ALbitfieldSOFT access) { if(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0) UNLIKELY @@ -343,7 +349,7 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, */ if(newsize != ALBuf->mDataStorage.size()) { - auto newdata = al::vector<al::byte,16>(newsize, al::byte{}); + auto newdata = decltype(ALBuf->mDataStorage)(newsize, std::byte{}); if((access&AL_PRESERVE_DATA_BIT_SOFT)) { const size_t tocopy{minz(newdata.size(), ALBuf->mDataStorage.size())}; @@ -437,7 +443,7 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, /** Prepares the buffer to use caller-specified storage. */ void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, - const FmtChannels DstChannels, const FmtType DstType, al::byte *sdata, const ALuint sdatalen) + const FmtChannels DstChannels, const FmtType DstType, std::byte *sdata, const ALuint sdatalen) { if(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0) UNLIKELY return context->setError(AL_INVALID_OPERATION, "Modifying storage for in-use buffer %u", @@ -458,6 +464,7 @@ void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, { case FmtUByte: return alignof(ALubyte); case FmtShort: return alignof(ALshort); + case FmtInt: return alignof(ALint); case FmtFloat: return alignof(ALfloat); case FmtDouble: return alignof(ALdouble); case FmtMulaw: return alignof(ALubyte); @@ -506,7 +513,7 @@ void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, #endif decltype(ALBuf->mDataStorage){}.swap(ALBuf->mDataStorage); - ALBuf->mData = {static_cast<al::byte*>(sdata), sdatalen}; + ALBuf->mData = {static_cast<std::byte*>(sdata), sdatalen}; #ifdef ALSOFT_EAX eax_x_ram_clear(*context->mALDevice, *ALBuf); @@ -536,108 +543,120 @@ void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, struct DecompResult { FmtChannels channels; FmtType type; }; -al::optional<DecompResult> DecomposeUserFormat(ALenum format) +std::optional<DecompResult> DecomposeUserFormat(ALenum format) { struct FormatMap { ALenum format; FmtChannels channels; FmtType type; }; - static const std::array<FormatMap,63> UserFmtList{{ - { AL_FORMAT_MONO8, FmtMono, FmtUByte }, - { AL_FORMAT_MONO16, FmtMono, FmtShort }, - { AL_FORMAT_MONO_FLOAT32, FmtMono, FmtFloat }, - { AL_FORMAT_MONO_DOUBLE_EXT, FmtMono, FmtDouble }, - { AL_FORMAT_MONO_IMA4, FmtMono, FmtIMA4 }, - { AL_FORMAT_MONO_MSADPCM_SOFT, FmtMono, FmtMSADPCM }, - { AL_FORMAT_MONO_MULAW, FmtMono, FmtMulaw }, - { AL_FORMAT_MONO_ALAW_EXT, FmtMono, FmtAlaw }, - - { AL_FORMAT_STEREO8, FmtStereo, FmtUByte }, - { AL_FORMAT_STEREO16, FmtStereo, FmtShort }, - { AL_FORMAT_STEREO_FLOAT32, FmtStereo, FmtFloat }, - { AL_FORMAT_STEREO_DOUBLE_EXT, FmtStereo, FmtDouble }, - { AL_FORMAT_STEREO_IMA4, FmtStereo, FmtIMA4 }, - { AL_FORMAT_STEREO_MSADPCM_SOFT, FmtStereo, FmtMSADPCM }, - { AL_FORMAT_STEREO_MULAW, FmtStereo, FmtMulaw }, - { AL_FORMAT_STEREO_ALAW_EXT, FmtStereo, FmtAlaw }, - - { AL_FORMAT_REAR8, FmtRear, FmtUByte }, - { AL_FORMAT_REAR16, FmtRear, FmtShort }, - { AL_FORMAT_REAR32, FmtRear, FmtFloat }, - { AL_FORMAT_REAR_MULAW, FmtRear, FmtMulaw }, - - { AL_FORMAT_QUAD8_LOKI, FmtQuad, FmtUByte }, - { AL_FORMAT_QUAD16_LOKI, FmtQuad, FmtShort }, - - { AL_FORMAT_QUAD8, FmtQuad, FmtUByte }, - { AL_FORMAT_QUAD16, FmtQuad, FmtShort }, - { AL_FORMAT_QUAD32, FmtQuad, FmtFloat }, - { AL_FORMAT_QUAD_MULAW, FmtQuad, FmtMulaw }, - - { AL_FORMAT_51CHN8, FmtX51, FmtUByte }, - { AL_FORMAT_51CHN16, FmtX51, FmtShort }, - { AL_FORMAT_51CHN32, FmtX51, FmtFloat }, - { AL_FORMAT_51CHN_MULAW, FmtX51, FmtMulaw }, - - { AL_FORMAT_61CHN8, FmtX61, FmtUByte }, - { AL_FORMAT_61CHN16, FmtX61, FmtShort }, - { AL_FORMAT_61CHN32, FmtX61, FmtFloat }, - { AL_FORMAT_61CHN_MULAW, FmtX61, FmtMulaw }, - - { AL_FORMAT_71CHN8, FmtX71, FmtUByte }, - { AL_FORMAT_71CHN16, FmtX71, FmtShort }, - { AL_FORMAT_71CHN32, FmtX71, FmtFloat }, - { AL_FORMAT_71CHN_MULAW, FmtX71, FmtMulaw }, - - { AL_FORMAT_BFORMAT2D_8, FmtBFormat2D, FmtUByte }, - { AL_FORMAT_BFORMAT2D_16, FmtBFormat2D, FmtShort }, - { AL_FORMAT_BFORMAT2D_FLOAT32, FmtBFormat2D, FmtFloat }, - { AL_FORMAT_BFORMAT2D_MULAW, FmtBFormat2D, FmtMulaw }, - - { AL_FORMAT_BFORMAT3D_8, FmtBFormat3D, FmtUByte }, - { AL_FORMAT_BFORMAT3D_16, FmtBFormat3D, FmtShort }, - { AL_FORMAT_BFORMAT3D_FLOAT32, FmtBFormat3D, FmtFloat }, - { AL_FORMAT_BFORMAT3D_MULAW, FmtBFormat3D, FmtMulaw }, - - { AL_FORMAT_UHJ2CHN8_SOFT, FmtUHJ2, FmtUByte }, - { AL_FORMAT_UHJ2CHN16_SOFT, FmtUHJ2, FmtShort }, - { AL_FORMAT_UHJ2CHN_FLOAT32_SOFT, FmtUHJ2, FmtFloat }, - { AL_FORMAT_UHJ2CHN_MULAW_SOFT, FmtUHJ2, FmtMulaw }, - { AL_FORMAT_UHJ2CHN_ALAW_SOFT, FmtUHJ2, FmtAlaw }, - { AL_FORMAT_UHJ2CHN_IMA4_SOFT, FmtUHJ2, FmtIMA4 }, - { AL_FORMAT_UHJ2CHN_MSADPCM_SOFT, FmtUHJ2, FmtMSADPCM }, - - { AL_FORMAT_UHJ3CHN8_SOFT, FmtUHJ3, FmtUByte }, - { AL_FORMAT_UHJ3CHN16_SOFT, FmtUHJ3, FmtShort }, - { AL_FORMAT_UHJ3CHN_FLOAT32_SOFT, FmtUHJ3, FmtFloat }, - { AL_FORMAT_UHJ3CHN_MULAW_SOFT, FmtUHJ3, FmtMulaw }, - { AL_FORMAT_UHJ3CHN_ALAW_SOFT, FmtUHJ3, FmtAlaw }, - - { AL_FORMAT_UHJ4CHN8_SOFT, FmtUHJ4, FmtUByte }, - { AL_FORMAT_UHJ4CHN16_SOFT, FmtUHJ4, FmtShort }, - { AL_FORMAT_UHJ4CHN_FLOAT32_SOFT, FmtUHJ4, FmtFloat }, - { AL_FORMAT_UHJ4CHN_MULAW_SOFT, FmtUHJ4, FmtMulaw }, - { AL_FORMAT_UHJ4CHN_ALAW_SOFT, FmtUHJ4, FmtAlaw }, - }}; + static constexpr std::array UserFmtList{ + FormatMap{AL_FORMAT_MONO8, FmtMono, FmtUByte }, + FormatMap{AL_FORMAT_MONO16, FmtMono, FmtShort }, + FormatMap{AL_FORMAT_MONO_I32, FmtMono, FmtInt }, + FormatMap{AL_FORMAT_MONO_FLOAT32, FmtMono, FmtFloat }, + FormatMap{AL_FORMAT_MONO_DOUBLE_EXT, FmtMono, FmtDouble }, + FormatMap{AL_FORMAT_MONO_IMA4, FmtMono, FmtIMA4 }, + FormatMap{AL_FORMAT_MONO_MSADPCM_SOFT, FmtMono, FmtMSADPCM}, + FormatMap{AL_FORMAT_MONO_MULAW, FmtMono, FmtMulaw }, + FormatMap{AL_FORMAT_MONO_ALAW_EXT, FmtMono, FmtAlaw }, + + FormatMap{AL_FORMAT_STEREO8, FmtStereo, FmtUByte }, + FormatMap{AL_FORMAT_STEREO16, FmtStereo, FmtShort }, + FormatMap{AL_FORMAT_STEREO_I32, FmtStereo, FmtInt }, + FormatMap{AL_FORMAT_STEREO_FLOAT32, FmtStereo, FmtFloat }, + FormatMap{AL_FORMAT_STEREO_DOUBLE_EXT, FmtStereo, FmtDouble }, + FormatMap{AL_FORMAT_STEREO_IMA4, FmtStereo, FmtIMA4 }, + FormatMap{AL_FORMAT_STEREO_MSADPCM_SOFT, FmtStereo, FmtMSADPCM}, + FormatMap{AL_FORMAT_STEREO_MULAW, FmtStereo, FmtMulaw }, + FormatMap{AL_FORMAT_STEREO_ALAW_EXT, FmtStereo, FmtAlaw }, + + FormatMap{AL_FORMAT_REAR8, FmtRear, FmtUByte}, + FormatMap{AL_FORMAT_REAR16, FmtRear, FmtShort}, + FormatMap{AL_FORMAT_REAR32, FmtRear, FmtFloat}, + FormatMap{AL_FORMAT_REAR_I32, FmtRear, FmtInt }, + FormatMap{AL_FORMAT_REAR_FLOAT32, FmtRear, FmtFloat}, + FormatMap{AL_FORMAT_REAR_MULAW, FmtRear, FmtMulaw}, + + FormatMap{AL_FORMAT_QUAD8_LOKI, FmtQuad, FmtUByte}, + FormatMap{AL_FORMAT_QUAD16_LOKI, FmtQuad, FmtShort}, + + FormatMap{AL_FORMAT_QUAD8, FmtQuad, FmtUByte}, + FormatMap{AL_FORMAT_QUAD16, FmtQuad, FmtShort}, + FormatMap{AL_FORMAT_QUAD32, FmtQuad, FmtFloat}, + FormatMap{AL_FORMAT_QUAD_I32, FmtQuad, FmtInt }, + FormatMap{AL_FORMAT_QUAD_FLOAT32, FmtQuad, FmtFloat}, + FormatMap{AL_FORMAT_QUAD_MULAW, FmtQuad, FmtMulaw}, + + FormatMap{AL_FORMAT_51CHN8, FmtX51, FmtUByte}, + FormatMap{AL_FORMAT_51CHN16, FmtX51, FmtShort}, + FormatMap{AL_FORMAT_51CHN32, FmtX51, FmtFloat}, + FormatMap{AL_FORMAT_51CHN_I32, FmtX51, FmtInt }, + FormatMap{AL_FORMAT_51CHN_FLOAT32, FmtX51, FmtFloat}, + FormatMap{AL_FORMAT_51CHN_MULAW, FmtX51, FmtMulaw}, + + FormatMap{AL_FORMAT_61CHN8, FmtX61, FmtUByte}, + FormatMap{AL_FORMAT_61CHN16, FmtX61, FmtShort}, + FormatMap{AL_FORMAT_61CHN32, FmtX61, FmtFloat}, + FormatMap{AL_FORMAT_61CHN_I32, FmtX61, FmtInt }, + FormatMap{AL_FORMAT_61CHN_FLOAT32, FmtX61, FmtFloat}, + FormatMap{AL_FORMAT_61CHN_MULAW, FmtX61, FmtMulaw}, + + FormatMap{AL_FORMAT_71CHN8, FmtX71, FmtUByte}, + FormatMap{AL_FORMAT_71CHN16, FmtX71, FmtShort}, + FormatMap{AL_FORMAT_71CHN32, FmtX71, FmtFloat}, + FormatMap{AL_FORMAT_71CHN_I32, FmtX71, FmtInt }, + FormatMap{AL_FORMAT_71CHN_FLOAT32, FmtX71, FmtFloat}, + FormatMap{AL_FORMAT_71CHN_MULAW, FmtX71, FmtMulaw}, + + FormatMap{AL_FORMAT_BFORMAT2D_8, FmtBFormat2D, FmtUByte}, + FormatMap{AL_FORMAT_BFORMAT2D_16, FmtBFormat2D, FmtShort}, + FormatMap{AL_FORMAT_BFORMAT2D_FLOAT32, FmtBFormat2D, FmtFloat}, + FormatMap{AL_FORMAT_BFORMAT2D_MULAW, FmtBFormat2D, FmtMulaw}, + + FormatMap{AL_FORMAT_BFORMAT3D_8, FmtBFormat3D, FmtUByte}, + FormatMap{AL_FORMAT_BFORMAT3D_16, FmtBFormat3D, FmtShort}, + FormatMap{AL_FORMAT_BFORMAT3D_FLOAT32, FmtBFormat3D, FmtFloat}, + FormatMap{AL_FORMAT_BFORMAT3D_MULAW, FmtBFormat3D, FmtMulaw}, + + FormatMap{AL_FORMAT_UHJ2CHN8_SOFT, FmtUHJ2, FmtUByte }, + FormatMap{AL_FORMAT_UHJ2CHN16_SOFT, FmtUHJ2, FmtShort }, + FormatMap{AL_FORMAT_UHJ2CHN_I32, FmtUHJ2, FmtInt }, + FormatMap{AL_FORMAT_UHJ2CHN_FLOAT32_SOFT, FmtUHJ2, FmtFloat }, + FormatMap{AL_FORMAT_UHJ2CHN_MULAW_SOFT, FmtUHJ2, FmtMulaw }, + FormatMap{AL_FORMAT_UHJ2CHN_ALAW_SOFT, FmtUHJ2, FmtAlaw }, + FormatMap{AL_FORMAT_UHJ2CHN_IMA4_SOFT, FmtUHJ2, FmtIMA4 }, + FormatMap{AL_FORMAT_UHJ2CHN_MSADPCM_SOFT, FmtUHJ2, FmtMSADPCM}, + + FormatMap{AL_FORMAT_UHJ3CHN8_SOFT, FmtUHJ3, FmtUByte}, + FormatMap{AL_FORMAT_UHJ3CHN16_SOFT, FmtUHJ3, FmtShort}, + FormatMap{AL_FORMAT_UHJ3CHN_I32, FmtUHJ3, FmtInt }, + FormatMap{AL_FORMAT_UHJ3CHN_FLOAT32_SOFT, FmtUHJ3, FmtFloat}, + FormatMap{AL_FORMAT_UHJ3CHN_MULAW_SOFT, FmtUHJ3, FmtMulaw}, + FormatMap{AL_FORMAT_UHJ3CHN_ALAW_SOFT, FmtUHJ3, FmtAlaw }, + + FormatMap{AL_FORMAT_UHJ4CHN8_SOFT, FmtUHJ4, FmtUByte}, + FormatMap{AL_FORMAT_UHJ4CHN16_SOFT, FmtUHJ4, FmtShort}, + FormatMap{AL_FORMAT_UHJ4CHN_I32, FmtUHJ4, FmtInt }, + FormatMap{AL_FORMAT_UHJ4CHN_FLOAT32_SOFT, FmtUHJ4, FmtFloat}, + FormatMap{AL_FORMAT_UHJ4CHN_MULAW_SOFT, FmtUHJ4, FmtMulaw}, + FormatMap{AL_FORMAT_UHJ4CHN_ALAW_SOFT, FmtUHJ4, FmtAlaw }, + }; for(const auto &fmt : UserFmtList) { if(fmt.format == format) - return al::make_optional<DecompResult>({fmt.channels, fmt.type}); + return DecompResult{fmt.channels, fmt.type}; } - return al::nullopt; + return std::nullopt; } } // namespace -AL_API void AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers) -START_API_FUNC +AL_API DECL_FUNC2(void, alGenBuffers, ALsizei, ALuint*) +FORCE_ALIGN void AL_APIENTRY alGenBuffersDirect(ALCcontext *context, ALsizei n, ALuint *buffers) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(n < 0) UNLIKELY context->setError(AL_INVALID_VALUE, "Generating %d buffers", n); if(n <= 0) UNLIKELY return; @@ -661,7 +680,7 @@ START_API_FUNC /* Store the allocated buffer IDs in a separate local list, to avoid * modifying the user storage in case of failure. */ - al::vector<ALuint> ids; + std::vector<ALuint> ids; ids.reserve(static_cast<ALuint>(n)); do { ALbuffer *buffer{AllocBuffer(device)}; @@ -670,14 +689,11 @@ START_API_FUNC std::copy(ids.begin(), ids.end(), buffers); } } -END_API_FUNC -AL_API void AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers) -START_API_FUNC +AL_API DECL_FUNC2(void, alDeleteBuffers, ALsizei, const ALuint*) +FORCE_ALIGN void AL_APIENTRY alDeleteBuffersDirect(ALCcontext *context, ALsizei n, + const ALuint *buffers) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(n < 0) UNLIKELY context->setError(AL_INVALID_VALUE, "Deleting %d buffers", n); if(n <= 0) UNLIKELY return; @@ -714,35 +730,32 @@ START_API_FUNC }; std::for_each(buffers, buffers_end, delete_buffer); } -END_API_FUNC -AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer) -START_API_FUNC +AL_API DECL_FUNC1(ALboolean, alIsBuffer, ALuint) +FORCE_ALIGN ALboolean AL_APIENTRY alIsBufferDirect(ALCcontext *context, ALuint buffer) noexcept { - ContextRef context{GetContextRef()}; - if(context) LIKELY - { - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard<std::mutex> _{device->BufferLock}; - if(!buffer || LookupBuffer(device, buffer)) - return AL_TRUE; - } + ALCdevice *device{context->mALDevice.get()}; + std::lock_guard<std::mutex> _{device->BufferLock}; + if(!buffer || LookupBuffer(device, buffer)) + return AL_TRUE; return AL_FALSE; } -END_API_FUNC - -AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq) -START_API_FUNC -{ alBufferStorageSOFT(buffer, format, data, size, freq, 0); } -END_API_FUNC -AL_API void AL_APIENTRY alBufferStorageSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags) -START_API_FUNC +AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq) noexcept { - ContextRef context{GetContextRef()}; + auto context = GetContextRef(); if(!context) UNLIKELY return; + alBufferStorageDirectSOFT(context.get(), buffer, format, data, size, freq, 0); +} +FORCE_ALIGN void AL_APIENTRY alBufferDataDirect(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq) noexcept +{ alBufferStorageDirectSOFT(context, buffer, format, data, size, freq, 0); } + +AL_API DECL_FUNCEXT6(void, alBufferStorage,SOFT, ALuint, ALenum, const ALvoid*, ALsizei, ALsizei, ALbitfieldSOFT) +FORCE_ALIGN void AL_APIENTRY alBufferStorageDirectSOFT(ALCcontext *context, ALuint buffer, + ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags) noexcept +{ ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; @@ -766,20 +779,16 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format); else { - LoadData(context.get(), albuf, freq, static_cast<ALuint>(size), usrfmt->channels, - usrfmt->type, static_cast<const al::byte*>(data), flags); + LoadData(context, albuf, freq, static_cast<ALuint>(size), usrfmt->channels, + usrfmt->type, static_cast<const std::byte*>(data), flags); } } } -END_API_FUNC -void AL_APIENTRY alBufferDataStatic(const ALuint buffer, ALenum format, ALvoid *data, ALsizei size, - ALsizei freq) -START_API_FUNC +DECL_FUNC5(void, alBufferDataStatic, ALuint, ALenum, ALvoid*, ALsizei, ALsizei) +FORCE_ALIGN void AL_APIENTRY alBufferDataStaticDirect(ALCcontext *context, const ALuint buffer, + ALenum format, ALvoid *data, ALsizei size, ALsizei freq) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; @@ -795,17 +804,14 @@ START_API_FUNC if(!usrfmt) UNLIKELY return context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format); - PrepareUserPtr(context.get(), albuf, freq, usrfmt->channels, usrfmt->type, - static_cast<al::byte*>(data), static_cast<ALuint>(size)); + PrepareUserPtr(context, albuf, freq, usrfmt->channels, usrfmt->type, + static_cast<std::byte*>(data), static_cast<ALuint>(size)); } -END_API_FUNC -AL_API void* AL_APIENTRY alMapBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access) -START_API_FUNC +AL_API DECL_FUNCEXT4(void*, alMapBuffer,SOFT, ALuint, ALsizei, ALsizei, ALbitfieldSOFT) +FORCE_ALIGN void* AL_APIENTRY alMapBufferDirectSOFT(ALCcontext *context, ALuint buffer, + ALsizei offset, ALsizei length, ALbitfieldSOFT access) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return nullptr; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; @@ -852,14 +858,10 @@ START_API_FUNC return nullptr; } -END_API_FUNC -AL_API void AL_APIENTRY alUnmapBufferSOFT(ALuint buffer) -START_API_FUNC +AL_API DECL_FUNCEXT1(void, alUnmapBuffer,SOFT, ALuint) +FORCE_ALIGN void AL_APIENTRY alUnmapBufferDirectSOFT(ALCcontext *context, ALuint buffer) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; @@ -875,14 +877,11 @@ START_API_FUNC albuf->MappedSize = 0; } } -END_API_FUNC -AL_API void AL_APIENTRY alFlushMappedBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length) -START_API_FUNC +AL_API DECL_FUNCEXT3(void, alFlushMappedBuffer,SOFT, ALuint, ALsizei, ALsizei) +FORCE_ALIGN void AL_APIENTRY alFlushMappedBufferDirectSOFT(ALCcontext *context, ALuint buffer, + ALsizei offset, ALsizei length) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; @@ -900,21 +899,18 @@ START_API_FUNC else { /* FIXME: Need to use some method of double-buffering for the mixer and - * app to hold separate memory, which can be safely transfered + * app to hold separate memory, which can be safely transferred * asynchronously. Currently we just say the app shouldn't write where * OpenAL's reading, and hope for the best... */ std::atomic_thread_fence(std::memory_order_seq_cst); } } -END_API_FUNC -AL_API void AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) -START_API_FUNC +AL_API DECL_FUNCEXT5(void, alBufferSubData,SOFT, ALuint, ALenum, const ALvoid*, ALsizei, ALsizei) +FORCE_ALIGN void AL_APIENTRY alBufferSubDataDirectSOFT(ALCcontext *context, ALuint buffer, + ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; @@ -966,61 +962,12 @@ START_API_FUNC assert(al::to_underlying(usrfmt->type) == al::to_underlying(albuf->mType)); memcpy(albuf->mData.data()+offset, data, static_cast<ALuint>(length)); } -END_API_FUNC - - -AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint /*buffer*/, ALuint /*samplerate*/, - ALenum /*internalformat*/, ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/, - const ALvoid* /*data*/) -START_API_FUNC -{ - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - - context->setError(AL_INVALID_OPERATION, "alBufferSamplesSOFT not supported"); -} -END_API_FUNC - -AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint /*buffer*/, ALsizei /*offset*/, - ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/, const ALvoid* /*data*/) -START_API_FUNC -{ - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - - context->setError(AL_INVALID_OPERATION, "alBufferSubSamplesSOFT not supported"); -} -END_API_FUNC - -AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint /*buffer*/, ALsizei /*offset*/, - ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/, ALvoid* /*data*/) -START_API_FUNC -{ - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - - context->setError(AL_INVALID_OPERATION, "alGetBufferSamplesSOFT not supported"); -} -END_API_FUNC -AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum /*format*/) -START_API_FUNC -{ - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return AL_FALSE; - - context->setError(AL_INVALID_OPERATION, "alIsBufferFormatSupportedSOFT not supported"); - return AL_FALSE; -} -END_API_FUNC - -AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat /*value*/) -START_API_FUNC +AL_API DECL_FUNC3(void, alBufferf, ALuint, ALenum, ALfloat) +FORCE_ALIGN void AL_APIENTRY alBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param, + ALfloat /*value*/) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; @@ -1032,15 +979,11 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param); } } -END_API_FUNC -AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, - ALfloat /*value1*/, ALfloat /*value2*/, ALfloat /*value3*/) -START_API_FUNC +AL_API DECL_FUNC5(void, alBuffer3f, ALuint, ALenum, ALfloat, ALfloat, ALfloat) +FORCE_ALIGN void AL_APIENTRY alBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param, + ALfloat /*value1*/, ALfloat /*value2*/, ALfloat /*value3*/) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; @@ -1052,14 +995,11 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param); } } -END_API_FUNC -AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alBufferfv, ALuint, ALenum, const ALfloat*) +FORCE_ALIGN void AL_APIENTRY alBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param, + const ALfloat *values) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; @@ -1073,15 +1013,12 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param); } } -END_API_FUNC -AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value) -START_API_FUNC +AL_API DECL_FUNC3(void, alBufferi, ALuint, ALenum, ALint) +FORCE_ALIGN void AL_APIENTRY alBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param, + ALint value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; @@ -1135,15 +1072,11 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param); } } -END_API_FUNC -AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, - ALint /*value1*/, ALint /*value2*/, ALint /*value3*/) -START_API_FUNC +AL_API DECL_FUNC5(void, alBuffer3i, ALuint, ALenum, ALint, ALint, ALint) +FORCE_ALIGN void AL_APIENTRY alBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param, + ALint /*value1*/, ALint /*value2*/, ALint /*value3*/) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; @@ -1155,36 +1088,31 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param); } } -END_API_FUNC -AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alBufferiv, ALuint, ALenum, const ALint*) +FORCE_ALIGN void AL_APIENTRY alBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param, + const ALint *values) noexcept { - if(values) + if(!values) UNLIKELY + return context->setError(AL_INVALID_VALUE, "NULL pointer"); + + switch(param) { - switch(param) - { - case AL_UNPACK_BLOCK_ALIGNMENT_SOFT: - case AL_PACK_BLOCK_ALIGNMENT_SOFT: - case AL_AMBISONIC_LAYOUT_SOFT: - case AL_AMBISONIC_SCALING_SOFT: - case AL_UNPACK_AMBISONIC_ORDER_SOFT: - alBufferi(buffer, param, values[0]); - return; - } + case AL_UNPACK_BLOCK_ALIGNMENT_SOFT: + case AL_PACK_BLOCK_ALIGNMENT_SOFT: + case AL_AMBISONIC_LAYOUT_SOFT: + case AL_AMBISONIC_SCALING_SOFT: + case AL_UNPACK_AMBISONIC_ORDER_SOFT: + alBufferiDirect(context, buffer, param, values[0]); + return; } - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; ALbuffer *albuf = LookupBuffer(device, buffer); if(!albuf) UNLIKELY context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(!values) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); else switch(param) { case AL_LOOP_POINTS_SOFT: @@ -1206,15 +1134,12 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x", param); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetBufferf, ALuint, ALenum, ALfloat*) +FORCE_ALIGN void AL_APIENTRY alGetBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param, + ALfloat *value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; @@ -1234,14 +1159,11 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) -START_API_FUNC +AL_API DECL_FUNC5(void, alGetBuffer3f, ALuint, ALenum, ALfloat*, ALfloat*, ALfloat*) +FORCE_ALIGN void AL_APIENTRY alGetBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param, + ALfloat *value1, ALfloat *value2, ALfloat *value3) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; @@ -1255,21 +1177,18 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetBufferfv, ALuint, ALenum, ALfloat*) +FORCE_ALIGN void AL_APIENTRY alGetBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param, + ALfloat *values) noexcept { switch(param) { case AL_SEC_LENGTH_SOFT: - alGetBufferf(buffer, param, values); + alGetBufferfDirect(context, buffer, param, values); return; } - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; @@ -1283,15 +1202,12 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetBufferi, ALuint, ALenum, ALint*) +FORCE_ALIGN void AL_APIENTRY alGetBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param, + ALint *value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; ALbuffer *albuf = LookupBuffer(device, buffer); @@ -1351,14 +1267,11 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) -START_API_FUNC +AL_API DECL_FUNC5(void, alGetBuffer3i, ALuint, ALenum, ALint*, ALint*, ALint*) +FORCE_ALIGN void AL_APIENTRY alGetBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param, + ALint *value1, ALint *value2, ALint *value3) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; if(LookupBuffer(device, buffer) == nullptr) UNLIKELY @@ -1371,10 +1284,10 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetBufferiv, ALuint, ALenum, ALint*) +FORCE_ALIGN void AL_APIENTRY alGetBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param, + ALint *values) noexcept { switch(param) { @@ -1390,13 +1303,10 @@ START_API_FUNC case AL_AMBISONIC_LAYOUT_SOFT: case AL_AMBISONIC_SCALING_SOFT: case AL_UNPACK_AMBISONIC_ORDER_SOFT: - alGetBufferi(buffer, param, values); + alGetBufferiDirect(context, buffer, param, values); return; } - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; ALbuffer *albuf = LookupBuffer(device, buffer); @@ -1415,16 +1325,12 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x", param); } } -END_API_FUNC -AL_API void AL_APIENTRY alBufferCallbackSOFT(ALuint buffer, ALenum format, ALsizei freq, - ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) -START_API_FUNC +AL_API DECL_FUNCEXT5(void, alBufferCallback,SOFT, ALuint, ALenum, ALsizei, ALBUFFERCALLBACKTYPESOFT, ALvoid*) +FORCE_ALIGN void AL_APIENTRY alBufferCallbackDirectSOFT(ALCcontext *context, ALuint buffer, + ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; @@ -1441,18 +1347,15 @@ START_API_FUNC if(!usrfmt) UNLIKELY context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format); else - PrepareCallback(context.get(), albuf, freq, usrfmt->channels, usrfmt->type, callback, + PrepareCallback(context, albuf, freq, usrfmt->channels, usrfmt->type, callback, userptr); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetBufferPtrSOFT(ALuint buffer, ALenum param, ALvoid **value) -START_API_FUNC +AL_API DECL_FUNCEXT3(void, alGetBufferPtr,SOFT, ALuint, ALenum, ALvoid**) +FORCE_ALIGN void AL_APIENTRY alGetBufferPtrDirectSOFT(ALCcontext *context, ALuint buffer, + ALenum param, ALvoid **value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; ALbuffer *albuf = LookupBuffer(device, buffer); @@ -1463,7 +1366,7 @@ START_API_FUNC else switch(param) { case AL_BUFFER_CALLBACK_FUNCTION_SOFT: - *value = reinterpret_cast<void*>(albuf->mCallback); + *value = al::bit_cast<void*>(albuf->mCallback); break; case AL_BUFFER_CALLBACK_USER_PARAM_SOFT: *value = albuf->mUserData; @@ -1473,14 +1376,11 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid buffer pointer property 0x%04x", param); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetBuffer3PtrSOFT(ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3) -START_API_FUNC +AL_API DECL_FUNCEXT5(void, alGetBuffer3Ptr,SOFT, ALuint, ALenum, ALvoid**, ALvoid**, ALvoid**) +FORCE_ALIGN void AL_APIENTRY alGetBuffer3PtrDirectSOFT(ALCcontext *context, ALuint buffer, + ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; if(LookupBuffer(device, buffer) == nullptr) UNLIKELY @@ -1493,22 +1393,19 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid buffer 3-pointer property 0x%04x", param); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetBufferPtrvSOFT(ALuint buffer, ALenum param, ALvoid **values) -START_API_FUNC +AL_API DECL_FUNCEXT3(void, alGetBufferPtrv,SOFT, ALuint, ALenum, ALvoid**) +FORCE_ALIGN void AL_APIENTRY alGetBufferPtrvDirectSOFT(ALCcontext *context, ALuint buffer, + ALenum param, ALvoid **values) noexcept { switch(param) { case AL_BUFFER_CALLBACK_FUNCTION_SOFT: case AL_BUFFER_CALLBACK_USER_PARAM_SOFT: - alGetBufferPtrSOFT(buffer, param, values); + alGetBufferPtrDirectSOFT(context, buffer, param, values); return; } - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->BufferLock}; if(LookupBuffer(device, buffer) == nullptr) UNLIKELY @@ -1521,16 +1418,69 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid buffer pointer-vector property 0x%04x", param); } } -END_API_FUNC + + +AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint /*buffer*/, ALuint /*samplerate*/, + ALenum /*internalformat*/, ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/, + const ALvoid* /*data*/) noexcept +{ + ContextRef context{GetContextRef()}; + if(!context) UNLIKELY return; + + context->setError(AL_INVALID_OPERATION, "alBufferSamplesSOFT not supported"); +} + +AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint /*buffer*/, ALsizei /*offset*/, + ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/, const ALvoid* /*data*/) noexcept +{ + ContextRef context{GetContextRef()}; + if(!context) UNLIKELY return; + + context->setError(AL_INVALID_OPERATION, "alBufferSubSamplesSOFT not supported"); +} + +AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint /*buffer*/, ALsizei /*offset*/, + ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/, ALvoid* /*data*/) noexcept +{ + ContextRef context{GetContextRef()}; + if(!context) UNLIKELY return; + + context->setError(AL_INVALID_OPERATION, "alGetBufferSamplesSOFT not supported"); +} + +AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum /*format*/) noexcept +{ + ContextRef context{GetContextRef()}; + if(!context) UNLIKELY return AL_FALSE; + + context->setError(AL_INVALID_OPERATION, "alIsBufferFormatSupportedSOFT not supported"); + return AL_FALSE; +} + + +void ALbuffer::SetName(ALCcontext *context, ALuint id, std::string_view name) +{ + ALCdevice *device{context->mALDevice.get()}; + std::lock_guard<std::mutex> _{device->BufferLock}; + + auto buffer = LookupBuffer(device, id); + if(!buffer) UNLIKELY + return context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", id); + + device->mBufferNames.insert_or_assign(id, name); +} BufferSubList::~BufferSubList() { + if(!Buffers) + return; + uint64_t usemask{~FreeMask}; while(usemask) { const int idx{al::countr_zero(usemask)}; - al::destroy_at(Buffers+idx); + std::destroy_at(Buffers+idx); usemask &= ~(1_u64 << idx); } FreeMask = ~usemask; @@ -1540,93 +1490,82 @@ BufferSubList::~BufferSubList() #ifdef ALSOFT_EAX -FORCE_ALIGN ALboolean AL_APIENTRY EAXSetBufferMode(ALsizei n, const ALuint* buffers, ALint value) -START_API_FUNC +FORCE_ALIGN DECL_FUNC3(ALboolean, EAXSetBufferMode, ALsizei, const ALuint*, ALint) +FORCE_ALIGN ALboolean AL_APIENTRY EAXSetBufferModeDirect(ALCcontext *context, ALsizei n, + const ALuint *buffers, ALint value) noexcept { #define EAX_PREFIX "[EAXSetBufferMode] " - const auto context = ContextRef{GetContextRef()}; - if(!context) - { - ERR(EAX_PREFIX "%s\n", "No current context."); - return ALC_FALSE; - } - if(!eax_g_is_enabled) { context->setError(AL_INVALID_OPERATION, EAX_PREFIX "%s", "EAX not enabled."); - return ALC_FALSE; + return AL_FALSE; } const auto storage = EaxStorageFromEnum(value); if(!storage) { context->setError(AL_INVALID_ENUM, EAX_PREFIX "Unsupported X-RAM mode 0x%x", value); - return ALC_FALSE; + return AL_FALSE; } if(n == 0) - return ALC_TRUE; + return AL_TRUE; if(n < 0) { context->setError(AL_INVALID_VALUE, EAX_PREFIX "Buffer count %d out of range", n); - return ALC_FALSE; + return AL_FALSE; } if(!buffers) { context->setError(AL_INVALID_VALUE, EAX_PREFIX "%s", "Null AL buffers"); - return ALC_FALSE; + return AL_FALSE; } auto device = context->mALDevice.get(); std::lock_guard<std::mutex> device_lock{device->BufferLock}; - size_t total_needed{0}; - // Validate the buffers. - // - for(auto i = 0;i < n;++i) + /* Special-case setting a single buffer, to avoid extraneous allocations. */ + if(n == 1) { - const auto bufid = buffers[i]; + const auto bufid = buffers[0]; if(bufid == AL_NONE) - continue; + return AL_TRUE; const auto buffer = LookupBuffer(device, bufid); if(!buffer) UNLIKELY { ERR(EAX_PREFIX "Invalid buffer ID %u.\n", bufid); - return ALC_FALSE; + return AL_FALSE; } /* TODO: Is the store location allowed to change for in-use buffers, or * only when not set/queued on a source? */ - if(*storage == EaxStorage::Hardware && !buffer->eax_x_ram_is_hardware) + if(*storage == EaxStorage::Hardware) { - /* FIXME: This doesn't account for duplicate buffers. When the same - * buffer ID is specified multiple times in the provided list, it - * counts each instance as more memory that needs to fit in X-RAM. - */ - if(std::numeric_limits<size_t>::max()-buffer->OriginalSize < total_needed) UNLIKELY + if(!buffer->eax_x_ram_is_hardware + && buffer->OriginalSize > device->eax_x_ram_free_size) UNLIKELY { - context->setError(AL_OUT_OF_MEMORY, EAX_PREFIX "Size overflow (%u + %zu)\n", - buffer->OriginalSize, total_needed); - return ALC_FALSE; + context->setError(AL_OUT_OF_MEMORY, + EAX_PREFIX "Out of X-RAM memory (need: %u, avail: %u)", buffer->OriginalSize, + device->eax_x_ram_free_size); + return AL_FALSE; } - total_needed += buffer->OriginalSize; + + eax_x_ram_apply(*device, *buffer); } - } - if(total_needed > device->eax_x_ram_free_size) - { - context->setError(AL_OUT_OF_MEMORY,EAX_PREFIX "Out of X-RAM memory (need: %zu, avail: %u)", - total_needed, device->eax_x_ram_free_size); - return ALC_FALSE; + else + eax_x_ram_clear(*device, *buffer); + buffer->eax_x_ram_mode = *storage; + return AL_TRUE; } - // Update the mode. - // + /* Validate the buffers. */ + std::unordered_set<ALbuffer*> buflist; for(auto i = 0;i < n;++i) { const auto bufid = buffers[i]; @@ -1634,8 +1573,47 @@ START_API_FUNC continue; const auto buffer = LookupBuffer(device, bufid); - assert(buffer); + if(!buffer) UNLIKELY + { + ERR(EAX_PREFIX "Invalid buffer ID %u.\n", bufid); + return AL_FALSE; + } + + /* TODO: Is the store location allowed to change for in-use buffers, or + * only when not set/queued on a source? + */ + + buflist.emplace(buffer); + } + + if(*storage == EaxStorage::Hardware) + { + size_t total_needed{0}; + for(ALbuffer *buffer : buflist) + { + if(!buffer->eax_x_ram_is_hardware) + { + if(std::numeric_limits<size_t>::max()-buffer->OriginalSize < total_needed) UNLIKELY + { + context->setError(AL_OUT_OF_MEMORY, EAX_PREFIX "Size overflow (%u + %zu)\n", + buffer->OriginalSize, total_needed); + return AL_FALSE; + } + total_needed += buffer->OriginalSize; + } + } + if(total_needed > device->eax_x_ram_free_size) + { + context->setError(AL_OUT_OF_MEMORY, + EAX_PREFIX "Out of X-RAM memory (need: %zu, avail: %u)", total_needed, + device->eax_x_ram_free_size); + return AL_FALSE; + } + } + /* Update the mode. */ + for(ALbuffer *buffer : buflist) + { if(*storage == EaxStorage::Hardware) eax_x_ram_apply(*device, *buffer); else @@ -1647,20 +1625,13 @@ START_API_FUNC #undef EAX_PREFIX } -END_API_FUNC -FORCE_ALIGN ALenum AL_APIENTRY EAXGetBufferMode(ALuint buffer, ALint* pReserved) -START_API_FUNC +FORCE_ALIGN DECL_FUNC2(ALenum, EAXGetBufferMode, ALuint, ALint*) +FORCE_ALIGN ALenum AL_APIENTRY EAXGetBufferModeDirect(ALCcontext *context, ALuint buffer, + ALint *pReserved) noexcept { #define EAX_PREFIX "[EAXGetBufferMode] " - const auto context = ContextRef{GetContextRef()}; - if(!context) - { - ERR(EAX_PREFIX "%s\n", "No current context."); - return AL_NONE; - } - if(!eax_g_is_enabled) { context->setError(AL_INVALID_OPERATION, EAX_PREFIX "%s", "EAX not enabled."); @@ -1687,6 +1658,5 @@ START_API_FUNC #undef EAX_PREFIX } -END_API_FUNC #endif // ALSOFT_EAX diff --git a/al/buffer.h b/al/buffer.h index 64ebe1f3..f936cf98 100644 --- a/al/buffer.h +++ b/al/buffer.h @@ -2,10 +2,11 @@ #define AL_BUFFER_H #include <atomic> +#include <cstddef> +#include <string_view> #include "AL/al.h" -#include "albyte.h" #include "alc/inprogext.h" #include "almalloc.h" #include "atomic.h" @@ -26,7 +27,7 @@ enum class EaxStorage : uint8_t { struct ALbuffer : public BufferStorage { ALbitfieldSOFT Access{0u}; - al::vector<al::byte,16> mDataStorage; + al::vector<std::byte,16> mDataStorage; ALuint OriginalSize{0}; @@ -47,6 +48,8 @@ struct ALbuffer : public BufferStorage { /* Self ID */ ALuint id{0}; + static void SetName(ALCcontext *context, ALuint id, std::string_view name); + DISABLE_ALLOC() #ifdef ALSOFT_EAX diff --git a/al/debug.cpp b/al/debug.cpp new file mode 100644 index 00000000..b76ec9af --- /dev/null +++ b/al/debug.cpp @@ -0,0 +1,570 @@ +#include "config.h" + +#include "debug.h" + +#include <algorithm> +#include <array> +#include <cstring> +#include <mutex> +#include <optional> +#include <stddef.h> +#include <stdexcept> +#include <string> +#include <utility> + +#include "AL/al.h" + +#include "alc/context.h" +#include "alc/inprogext.h" +#include "alspan.h" +#include "auxeffectslot.h" +#include "buffer.h" +#include "core/logging.h" +#include "direct_defs.h" +#include "effect.h" +#include "filter.h" +#include "opthelpers.h" +#include "source.h" + + +/* Declared here to prevent compilers from thinking it should be inlined, which + * GCC warns about increasing code size. + */ +DebugGroup::~DebugGroup() = default; + +namespace { + +static_assert(DebugSeverityBase+DebugSeverityCount <= 32, "Too many debug bits"); + +template<typename T, T ...Vals> +constexpr auto make_array_sequence(std::integer_sequence<T, Vals...>) +{ return std::array<T,sizeof...(Vals)>{Vals...}; } + +template<typename T, size_t N> +constexpr auto make_array_sequence() +{ return make_array_sequence(std::make_integer_sequence<T,N>{}); } + + +constexpr std::optional<DebugSource> GetDebugSource(ALenum source) noexcept +{ + switch(source) + { + case AL_DEBUG_SOURCE_API_EXT: return DebugSource::API; + case AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT: return DebugSource::System; + case AL_DEBUG_SOURCE_THIRD_PARTY_EXT: return DebugSource::ThirdParty; + case AL_DEBUG_SOURCE_APPLICATION_EXT: return DebugSource::Application; + case AL_DEBUG_SOURCE_OTHER_EXT: return DebugSource::Other; + } + return std::nullopt; +} + +constexpr std::optional<DebugType> GetDebugType(ALenum type) noexcept +{ + switch(type) + { + case AL_DEBUG_TYPE_ERROR_EXT: return DebugType::Error; + case AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT: return DebugType::DeprecatedBehavior; + case AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT: return DebugType::UndefinedBehavior; + case AL_DEBUG_TYPE_PORTABILITY_EXT: return DebugType::Portability; + case AL_DEBUG_TYPE_PERFORMANCE_EXT: return DebugType::Performance; + case AL_DEBUG_TYPE_MARKER_EXT: return DebugType::Marker; + case AL_DEBUG_TYPE_PUSH_GROUP_EXT: return DebugType::PushGroup; + case AL_DEBUG_TYPE_POP_GROUP_EXT: return DebugType::PopGroup; + case AL_DEBUG_TYPE_OTHER_EXT: return DebugType::Other; + } + return std::nullopt; +} + +constexpr std::optional<DebugSeverity> GetDebugSeverity(ALenum severity) noexcept +{ + switch(severity) + { + case AL_DEBUG_SEVERITY_HIGH_EXT: return DebugSeverity::High; + case AL_DEBUG_SEVERITY_MEDIUM_EXT: return DebugSeverity::Medium; + case AL_DEBUG_SEVERITY_LOW_EXT: return DebugSeverity::Low; + case AL_DEBUG_SEVERITY_NOTIFICATION_EXT: return DebugSeverity::Notification; + } + return std::nullopt; +} + + +ALenum GetDebugSourceEnum(DebugSource source) +{ + switch(source) + { + case DebugSource::API: return AL_DEBUG_SOURCE_API_EXT; + case DebugSource::System: return AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT; + case DebugSource::ThirdParty: return AL_DEBUG_SOURCE_THIRD_PARTY_EXT; + case DebugSource::Application: return AL_DEBUG_SOURCE_APPLICATION_EXT; + case DebugSource::Other: return AL_DEBUG_SOURCE_OTHER_EXT; + } + throw std::runtime_error{"Unexpected debug source value "+std::to_string(al::to_underlying(source))}; +} + +ALenum GetDebugTypeEnum(DebugType type) +{ + switch(type) + { + case DebugType::Error: return AL_DEBUG_TYPE_ERROR_EXT; + case DebugType::DeprecatedBehavior: return AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT; + case DebugType::UndefinedBehavior: return AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT; + case DebugType::Portability: return AL_DEBUG_TYPE_PORTABILITY_EXT; + case DebugType::Performance: return AL_DEBUG_TYPE_PERFORMANCE_EXT; + case DebugType::Marker: return AL_DEBUG_TYPE_MARKER_EXT; + case DebugType::PushGroup: return AL_DEBUG_TYPE_PUSH_GROUP_EXT; + case DebugType::PopGroup: return AL_DEBUG_TYPE_POP_GROUP_EXT; + case DebugType::Other: return AL_DEBUG_TYPE_OTHER_EXT; + } + throw std::runtime_error{"Unexpected debug type value "+std::to_string(al::to_underlying(type))}; +} + +ALenum GetDebugSeverityEnum(DebugSeverity severity) +{ + switch(severity) + { + case DebugSeverity::High: return AL_DEBUG_SEVERITY_HIGH_EXT; + case DebugSeverity::Medium: return AL_DEBUG_SEVERITY_MEDIUM_EXT; + case DebugSeverity::Low: return AL_DEBUG_SEVERITY_LOW_EXT; + case DebugSeverity::Notification: return AL_DEBUG_SEVERITY_NOTIFICATION_EXT; + } + throw std::runtime_error{"Unexpected debug severity value "+std::to_string(al::to_underlying(severity))}; +} + + +const char *GetDebugSourceName(DebugSource source) +{ + switch(source) + { + case DebugSource::API: return "API"; + case DebugSource::System: return "Audio System"; + case DebugSource::ThirdParty: return "Third Party"; + case DebugSource::Application: return "Application"; + case DebugSource::Other: return "Other"; + } + return "<invalid source>"; +} + +const char *GetDebugTypeName(DebugType type) +{ + switch(type) + { + case DebugType::Error: return "Error"; + case DebugType::DeprecatedBehavior: return "Deprecated Behavior"; + case DebugType::UndefinedBehavior: return "Undefined Behavior"; + case DebugType::Portability: return "Portability"; + case DebugType::Performance: return "Performance"; + case DebugType::Marker: return "Marker"; + case DebugType::PushGroup: return "Push Group"; + case DebugType::PopGroup: return "Pop Group"; + case DebugType::Other: return "Other"; + } + return "<invalid type>"; +} + +const char *GetDebugSeverityName(DebugSeverity severity) +{ + switch(severity) + { + case DebugSeverity::High: return "High"; + case DebugSeverity::Medium: return "Medium"; + case DebugSeverity::Low: return "Low"; + case DebugSeverity::Notification: return "Notification"; + } + return "<invalid severity>"; +} + +} // namespace + + +void ALCcontext::sendDebugMessage(std::unique_lock<std::mutex> &debuglock, DebugSource source, + DebugType type, ALuint id, DebugSeverity severity, std::string_view message) +{ + if(!mDebugEnabled.load()) UNLIKELY + return; + + if(message.length() >= MaxDebugMessageLength) UNLIKELY + { + ERR("Debug message too long (%zu >= %d):\n-> %.*s\n", message.length(), + MaxDebugMessageLength, static_cast<int>(message.length()), message.data()); + return; + } + + DebugGroup &debug = mDebugGroups.back(); + + const uint64_t idfilter{(1_u64 << (DebugSourceBase+al::to_underlying(source))) + | (1_u64 << (DebugTypeBase+al::to_underlying(type))) + | (uint64_t{id} << 32)}; + auto iditer = std::lower_bound(debug.mIdFilters.cbegin(), debug.mIdFilters.cend(), idfilter); + if(iditer != debug.mIdFilters.cend() && *iditer == idfilter) + return; + + const uint filter{(1u << (DebugSourceBase+al::to_underlying(source))) + | (1u << (DebugTypeBase+al::to_underlying(type))) + | (1u << (DebugSeverityBase+al::to_underlying(severity)))}; + auto iter = std::lower_bound(debug.mFilters.cbegin(), debug.mFilters.cend(), filter); + if(iter != debug.mFilters.cend() && *iter == filter) + return; + + if(mDebugCb) + { + auto callback = mDebugCb; + auto param = mDebugParam; + debuglock.unlock(); + callback(GetDebugSourceEnum(source), GetDebugTypeEnum(type), id, + GetDebugSeverityEnum(severity), static_cast<ALsizei>(message.length()), message.data(), + param); + } + else + { + if(mDebugLog.size() < MaxDebugLoggedMessages) + mDebugLog.emplace_back(source, type, id, severity, message); + else UNLIKELY + ERR("Debug message log overflow. Lost message:\n" + " Source: %s\n" + " Type: %s\n" + " ID: %u\n" + " Severity: %s\n" + " Message: \"%.*s\"\n", + GetDebugSourceName(source), GetDebugTypeName(type), id, + GetDebugSeverityName(severity), static_cast<int>(message.length()), + message.data()); + } +} + + +FORCE_ALIGN DECL_FUNCEXT2(void, alDebugMessageCallback,EXT, ALDEBUGPROCEXT, void*) +FORCE_ALIGN void AL_APIENTRY alDebugMessageCallbackDirectEXT(ALCcontext *context, + ALDEBUGPROCEXT callback, void *userParam) noexcept +{ + std::lock_guard<std::mutex> _{context->mDebugCbLock}; + context->mDebugCb = callback; + context->mDebugParam = userParam; +} + + +FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageInsert,EXT, ALenum, ALenum, ALuint, ALenum, ALsizei, const ALchar*) +FORCE_ALIGN void AL_APIENTRY alDebugMessageInsertDirectEXT(ALCcontext *context, ALenum source, + ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) noexcept +{ + if(!context->mContextFlags.test(ContextFlags::DebugBit)) + return; + + if(!message) UNLIKELY + return context->setError(AL_INVALID_VALUE, "Null message pointer"); + + auto msgview = (length < 0) ? std::string_view{message} + : std::string_view{message, static_cast<uint>(length)}; + if(msgview.length() >= MaxDebugMessageLength) UNLIKELY + return context->setError(AL_INVALID_VALUE, "Debug message too long (%zu >= %d)", + msgview.length(), MaxDebugMessageLength); + + auto dsource = GetDebugSource(source); + if(!dsource) + return context->setError(AL_INVALID_ENUM, "Invalid debug source 0x%04x", source); + if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application) + return context->setError(AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source); + + auto dtype = GetDebugType(type); + if(!dtype) + return context->setError(AL_INVALID_ENUM, "Invalid debug type 0x%04x", type); + + auto dseverity = GetDebugSeverity(severity); + if(!dseverity) + return context->setError(AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity); + + context->debugMessage(*dsource, *dtype, id, *dseverity, msgview); +} + + +FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageControl,EXT, ALenum, ALenum, ALenum, ALsizei, const ALuint*, ALboolean) +FORCE_ALIGN void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, ALenum source, + ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) noexcept +{ + if(count > 0) + { + if(!ids) + return context->setError(AL_INVALID_VALUE, "IDs is null with non-0 count"); + if(source == AL_DONT_CARE_EXT) + return context->setError(AL_INVALID_OPERATION, + "Debug source cannot be AL_DONT_CARE_EXT with IDs"); + if(type == AL_DONT_CARE_EXT) + return context->setError(AL_INVALID_OPERATION, + "Debug type cannot be AL_DONT_CARE_EXT with IDs"); + if(severity != AL_DONT_CARE_EXT) + return context->setError(AL_INVALID_OPERATION, + "Debug severity must be AL_DONT_CARE_EXT with IDs"); + } + + if(enable != AL_TRUE && enable != AL_FALSE) + return context->setError(AL_INVALID_ENUM, "Invalid debug enable %d", enable); + + static constexpr size_t ElemCount{DebugSourceCount + DebugTypeCount + DebugSeverityCount}; + static constexpr auto Values = make_array_sequence<uint8_t,ElemCount>(); + + auto srcIndices = al::span{Values}.subspan(DebugSourceBase,DebugSourceCount); + if(source != AL_DONT_CARE_EXT) + { + auto dsource = GetDebugSource(source); + if(!dsource) + return context->setError(AL_INVALID_ENUM, "Invalid debug source 0x%04x", source); + srcIndices = srcIndices.subspan(al::to_underlying(*dsource), 1); + } + + auto typeIndices = al::span{Values}.subspan(DebugTypeBase,DebugTypeCount); + if(type != AL_DONT_CARE_EXT) + { + auto dtype = GetDebugType(type); + if(!dtype) + return context->setError(AL_INVALID_ENUM, "Invalid debug type 0x%04x", type); + typeIndices = typeIndices.subspan(al::to_underlying(*dtype), 1); + } + + auto svrIndices = al::span{Values}.subspan(DebugSeverityBase,DebugSeverityCount); + if(severity != AL_DONT_CARE_EXT) + { + auto dseverity = GetDebugSeverity(severity); + if(!dseverity) + return context->setError(AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity); + svrIndices = svrIndices.subspan(al::to_underlying(*dseverity), 1); + } + + std::lock_guard<std::mutex> _{context->mDebugCbLock}; + DebugGroup &debug = context->mDebugGroups.back(); + if(count > 0) + { + const uint filterbase{(1u<<srcIndices[0]) | (1u<<typeIndices[0])}; + + for(const uint id : al::span{ids, static_cast<uint>(count)}) + { + const uint64_t filter{filterbase | (uint64_t{id} << 32)}; + + auto iter = std::lower_bound(debug.mIdFilters.cbegin(), debug.mIdFilters.cend(), + filter); + if(!enable && (iter == debug.mIdFilters.cend() || *iter != filter)) + debug.mIdFilters.insert(iter, filter); + else if(enable && iter != debug.mIdFilters.cend() && *iter == filter) + debug.mIdFilters.erase(iter); + } + } + else + { + auto apply_filter = [enable,&debug](const uint filter) + { + auto iter = std::lower_bound(debug.mFilters.cbegin(), debug.mFilters.cend(), filter); + if(!enable && (iter == debug.mFilters.cend() || *iter != filter)) + debug.mFilters.insert(iter, filter); + else if(enable && iter != debug.mFilters.cend() && *iter == filter) + debug.mFilters.erase(iter); + }; + auto apply_severity = [apply_filter,svrIndices](const uint filter) + { + std::for_each(svrIndices.cbegin(), svrIndices.cend(), + [apply_filter,filter](const uint idx){ apply_filter(filter | (1<<idx)); }); + }; + auto apply_type = [apply_severity,typeIndices](const uint filter) + { + std::for_each(typeIndices.cbegin(), typeIndices.cend(), + [apply_severity,filter](const uint idx){ apply_severity(filter | (1<<idx)); }); + }; + std::for_each(srcIndices.cbegin(), srcIndices.cend(), + [apply_type](const uint idx){ apply_type(1<<idx); }); + } +} + + +FORCE_ALIGN DECL_FUNCEXT4(void, alPushDebugGroup,EXT, ALenum, ALuint, ALsizei, const ALchar*) +FORCE_ALIGN void AL_APIENTRY alPushDebugGroupDirectEXT(ALCcontext *context, ALenum source, + ALuint id, ALsizei length, const ALchar *message) noexcept +{ + if(length < 0) + { + size_t newlen{std::strlen(message)}; + if(newlen >= MaxDebugMessageLength) UNLIKELY + return context->setError(AL_INVALID_VALUE, "Debug message too long (%zu >= %d)", + newlen, MaxDebugMessageLength); + length = static_cast<ALsizei>(newlen); + } + else if(length >= MaxDebugMessageLength) UNLIKELY + return context->setError(AL_INVALID_VALUE, "Debug message too long (%d >= %d)", length, + MaxDebugMessageLength); + + auto dsource = GetDebugSource(source); + if(!dsource) + return context->setError(AL_INVALID_ENUM, "Invalid debug source 0x%04x", source); + if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application) + return context->setError(AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source); + + std::unique_lock<std::mutex> debuglock{context->mDebugCbLock}; + if(context->mDebugGroups.size() >= MaxDebugGroupDepth) + { + debuglock.unlock(); + return context->setError(AL_STACK_OVERFLOW_EXT, "Pushing too many debug groups"); + } + + context->mDebugGroups.emplace_back(*dsource, id, + std::string_view{message, static_cast<uint>(length)}); + auto &oldback = *(context->mDebugGroups.end()-2); + auto &newback = context->mDebugGroups.back(); + + newback.mFilters = oldback.mFilters; + newback.mIdFilters = oldback.mIdFilters; + + if(context->mContextFlags.test(ContextFlags::DebugBit)) + context->sendDebugMessage(debuglock, newback.mSource, DebugType::PushGroup, newback.mId, + DebugSeverity::Notification, newback.mMessage); +} + +FORCE_ALIGN DECL_FUNCEXT(void, alPopDebugGroup,EXT) +FORCE_ALIGN void AL_APIENTRY alPopDebugGroupDirectEXT(ALCcontext *context) noexcept +{ + std::unique_lock<std::mutex> debuglock{context->mDebugCbLock}; + if(context->mDebugGroups.size() <= 1) + { + debuglock.unlock(); + return context->setError(AL_STACK_UNDERFLOW_EXT, + "Attempting to pop the default debug group"); + } + + DebugGroup &debug = context->mDebugGroups.back(); + const auto source = debug.mSource; + const auto id = debug.mId; + std::string message{std::move(debug.mMessage)}; + + context->mDebugGroups.pop_back(); + if(context->mContextFlags.test(ContextFlags::DebugBit)) + context->sendDebugMessage(debuglock, source, DebugType::PopGroup, id, + DebugSeverity::Notification, message); +} + + +FORCE_ALIGN DECL_FUNCEXT8(ALuint, alGetDebugMessageLog,EXT, ALuint, ALsizei, ALenum*, ALenum*, ALuint*, ALenum*, ALsizei*, ALchar*) +FORCE_ALIGN ALuint AL_APIENTRY alGetDebugMessageLogDirectEXT(ALCcontext *context, ALuint count, + ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, + ALsizei *lengths, ALchar *logBuf) noexcept +{ + if(logBufSize < 0) + { + context->setError(AL_INVALID_VALUE, "Negative debug log buffer size"); + return 0; + } + + std::lock_guard<std::mutex> _{context->mDebugCbLock}; + ALsizei logBufWritten{0}; + for(ALuint i{0};i < count;++i) + { + if(context->mDebugLog.empty()) + return i; + + auto &entry = context->mDebugLog.front(); + const size_t tocopy{entry.mMessage.size() + 1}; + if(logBuf) + { + const size_t avail{static_cast<ALuint>(logBufSize - logBufWritten)}; + if(avail < tocopy) + return i; + std::copy_n(entry.mMessage.data(), tocopy, logBuf+logBufWritten); + logBufWritten += static_cast<ALsizei>(tocopy); + } + + if(sources) sources[i] = GetDebugSourceEnum(entry.mSource); + if(types) types[i] = GetDebugTypeEnum(entry.mType); + if(ids) ids[i] = entry.mId; + if(severities) severities[i] = GetDebugSeverityEnum(entry.mSeverity); + if(lengths) lengths[i] = static_cast<ALsizei>(tocopy); + + context->mDebugLog.pop_front(); + } + + return count; +} + +FORCE_ALIGN DECL_FUNCEXT4(void, alObjectLabel,EXT, ALenum, ALuint, ALsizei, const ALchar*) +FORCE_ALIGN void AL_APIENTRY alObjectLabelDirectEXT(ALCcontext *context, ALenum identifier, + ALuint name, ALsizei length, const ALchar *label) noexcept +{ + if(!label && length != 0) UNLIKELY + return context->setError(AL_INVALID_VALUE, "Null label pointer"); + + auto objname = (length < 0) ? std::string_view{label} + : std::string_view{label, static_cast<uint>(length)}; + if(objname.length() >= MaxObjectLabelLength) UNLIKELY + return context->setError(AL_INVALID_VALUE, "Object label length too long (%zu >= %d)", + objname.length(), MaxObjectLabelLength); + + if(identifier == AL_SOURCE_EXT) + return ALsource::SetName(context, name, objname); + if(identifier == AL_BUFFER) + return ALbuffer::SetName(context, name, objname); + if(identifier == AL_FILTER_EXT) + return ALfilter::SetName(context, name, objname); + if(identifier == AL_EFFECT_EXT) + return ALeffect::SetName(context, name, objname); + if(identifier == AL_AUXILIARY_EFFECT_SLOT_EXT) + return ALeffectslot::SetName(context, name, objname); + + return context->setError(AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier); +} + +FORCE_ALIGN DECL_FUNCEXT5(void, alGetObjectLabel,EXT, ALenum, ALuint, ALsizei, ALsizei*, ALchar*) +FORCE_ALIGN void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALenum identifier, + ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) noexcept +{ + if(bufSize < 0) UNLIKELY + return context->setError(AL_INVALID_VALUE, "Negative label bufSize"); + + if(!label && !length) UNLIKELY + return context->setError(AL_INVALID_VALUE, "Null length and label"); + if(label && bufSize == 0) UNLIKELY + return context->setError(AL_INVALID_VALUE, "Zero label bufSize"); + + auto copy_name = [name,bufSize,length,label](std::unordered_map<ALuint,std::string> &names) + { + std::string_view objname; + + auto iter = names.find(name); + if(iter != names.end()) + objname = iter->second; + + if(!label) + *length = static_cast<ALsizei>(objname.length()); + else + { + const size_t tocopy{minz(objname.length(), static_cast<uint>(bufSize)-1)}; + std::memcpy(label, objname.data(), tocopy); + label[tocopy] = '\0'; + if(length) + *length = static_cast<ALsizei>(tocopy); + } + }; + + if(identifier == AL_SOURCE_EXT) + { + std::lock_guard _{context->mSourceLock}; + copy_name(context->mSourceNames); + } + else if(identifier == AL_BUFFER) + { + ALCdevice *device{context->mALDevice.get()}; + std::lock_guard _{device->BufferLock}; + copy_name(device->mBufferNames); + } + else if(identifier == AL_FILTER_EXT) + { + ALCdevice *device{context->mALDevice.get()}; + std::lock_guard _{device->FilterLock}; + copy_name(device->mFilterNames); + } + else if(identifier == AL_EFFECT_EXT) + { + ALCdevice *device{context->mALDevice.get()}; + std::lock_guard _{device->EffectLock}; + copy_name(device->mEffectNames); + } + else if(identifier == AL_AUXILIARY_EFFECT_SLOT_EXT) + { + std::lock_guard _{context->mEffectSlotLock}; + copy_name(context->mEffectSlotNames); + } + else + context->setError(AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier); +} diff --git a/al/debug.h b/al/debug.h new file mode 100644 index 00000000..351be9c0 --- /dev/null +++ b/al/debug.h @@ -0,0 +1,69 @@ +#ifndef AL_DEBUG_H +#define AL_DEBUG_H + +#include <stdint.h> +#include <string> +#include <vector> + +using uint = unsigned int; + + +/* Somewhat arbitrary. Avoid letting it get out of control if the app enables + * logging but never reads it. + */ +inline constexpr uint8_t MaxDebugLoggedMessages{64}; +inline constexpr uint16_t MaxDebugMessageLength{1024}; +inline constexpr uint8_t MaxDebugGroupDepth{64}; +inline constexpr uint16_t MaxObjectLabelLength{1024}; + + +inline constexpr uint DebugSourceBase{0}; +enum class DebugSource : uint8_t { + API = 0, + System, + ThirdParty, + Application, + Other, +}; +inline constexpr uint DebugSourceCount{5}; + +inline constexpr uint DebugTypeBase{DebugSourceBase + DebugSourceCount}; +enum class DebugType : uint8_t { + Error = 0, + DeprecatedBehavior, + UndefinedBehavior, + Portability, + Performance, + Marker, + PushGroup, + PopGroup, + Other, +}; +inline constexpr uint DebugTypeCount{9}; + +inline constexpr uint DebugSeverityBase{DebugTypeBase + DebugTypeCount}; +enum class DebugSeverity : uint8_t { + High = 0, + Medium, + Low, + Notification, +}; +inline constexpr uint DebugSeverityCount{4}; + +struct DebugGroup { + const uint mId; + const DebugSource mSource; + std::string mMessage; + std::vector<uint> mFilters; + std::vector<uint64_t> mIdFilters; + + template<typename T> + DebugGroup(DebugSource source, uint id, T&& message) + : mId{id}, mSource{source}, mMessage{std::forward<T>(message)} + { } + DebugGroup(const DebugGroup&) = default; + DebugGroup(DebugGroup&&) = default; + ~DebugGroup(); +}; + +#endif /* AL_DEBUG_H */ diff --git a/al/direct_defs.h b/al/direct_defs.h new file mode 100644 index 00000000..7526b611 --- /dev/null +++ b/al/direct_defs.h @@ -0,0 +1,127 @@ +#ifndef AL_DIRECT_DEFS_H +#define AL_DIRECT_DEFS_H + +namespace detail_ { + +template<typename T> +constexpr T DefaultVal() noexcept { return T{}; } + +template<> +constexpr void DefaultVal() noexcept { } + +} // namespace detail_ + +#define DECL_FUNC(R, Name) \ +R AL_APIENTRY Name(void) noexcept \ +{ \ + auto context = GetContextRef(); \ + if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ + return Name##Direct(context.get()); \ +} + +#define DECL_FUNC1(R, Name, T1) \ +R AL_APIENTRY Name(T1 a) noexcept \ +{ \ + auto context = GetContextRef(); \ + if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ + return Name##Direct(context.get(), a); \ +} + +#define DECL_FUNC2(R, Name, T1, T2) \ +R AL_APIENTRY Name(T1 a, T2 b) noexcept \ +{ \ + auto context = GetContextRef(); \ + if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ + return Name##Direct(context.get(), a, b); \ +} + +#define DECL_FUNC3(R, Name, T1, T2, T3) \ +R AL_APIENTRY Name(T1 a, T2 b, T3 c) noexcept \ +{ \ + auto context = GetContextRef(); \ + if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ + return Name##Direct(context.get(), a, b, c); \ +} + +#define DECL_FUNC4(R, Name, T1, T2, T3, T4) \ +R AL_APIENTRY Name(T1 a, T2 b, T3 c, T4 d) noexcept \ +{ \ + auto context = GetContextRef(); \ + if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ + return Name##Direct(context.get(), a, b, c, d); \ +} + +#define DECL_FUNC5(R, Name, T1, T2, T3, T4, T5) \ +R AL_APIENTRY Name(T1 a, T2 b, T3 c, T4 d, T5 e) noexcept \ +{ \ + auto context = GetContextRef(); \ + if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ + return Name##Direct(context.get(), a, b, c, d, e); \ +} + + +#define DECL_FUNCEXT(R, Name,Ext) \ +R AL_APIENTRY Name##Ext(void) noexcept \ +{ \ + auto context = GetContextRef(); \ + if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ + return Name##Direct##Ext(context.get()); \ +} + +#define DECL_FUNCEXT1(R, Name,Ext, T1) \ +R AL_APIENTRY Name##Ext(T1 a) noexcept \ +{ \ + auto context = GetContextRef(); \ + if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ + return Name##Direct##Ext(context.get(), a); \ +} + +#define DECL_FUNCEXT2(R, Name,Ext, T1, T2) \ +R AL_APIENTRY Name##Ext(T1 a, T2 b) noexcept \ +{ \ + auto context = GetContextRef(); \ + if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ + return Name##Direct##Ext(context.get(), a, b); \ +} + +#define DECL_FUNCEXT3(R, Name,Ext, T1, T2, T3) \ +R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c) noexcept \ +{ \ + auto context = GetContextRef(); \ + if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ + return Name##Direct##Ext(context.get(), a, b, c); \ +} + +#define DECL_FUNCEXT4(R, Name,Ext, T1, T2, T3, T4) \ +R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d) noexcept \ +{ \ + auto context = GetContextRef(); \ + if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ + return Name##Direct##Ext(context.get(), a, b, c, d); \ +} + +#define DECL_FUNCEXT5(R, Name,Ext, T1, T2, T3, T4, T5) \ +R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d, T5 e) noexcept \ +{ \ + auto context = GetContextRef(); \ + if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ + return Name##Direct##Ext(context.get(), a, b, c, d, e); \ +} + +#define DECL_FUNCEXT6(R, Name,Ext, T1, T2, T3, T4, T5, T6) \ +R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d, T5 e, T6 f) noexcept \ +{ \ + auto context = GetContextRef(); \ + if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ + return Name##Direct##Ext(context.get(), a, b, c, d, e, f); \ +} + +#define DECL_FUNCEXT8(R, Name,Ext, T1, T2, T3, T4, T5, T6, T7, T8) \ +R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g, T8 h) noexcept \ +{ \ + auto context = GetContextRef(); \ + if(!context) UNLIKELY return detail_::DefaultVal<R>(); \ + return Name##Direct##Ext(context.get(), a, b, c, d, e, f, g, h); \ +} + +#endif /* AL_DIRECT_DEFS_H */ diff --git a/al/eax/api.h b/al/eax/api.h index d254da1f..18d93ef8 100644 --- a/al/eax/api.h +++ b/al/eax/api.h @@ -10,17 +10,18 @@ // +#include <array> #include <cfloat> #include <cstdint> #include <cstring> - -#include <array> +#ifdef _WIN32 +#include <guiddef.h> +#endif #include "AL/al.h" -#ifndef GUID_DEFINED -#define GUID_DEFINED +#ifndef _WIN32 typedef struct _GUID { std::uint32_t Data1; std::uint16_t Data2; @@ -28,16 +29,16 @@ typedef struct _GUID { std::uint8_t Data4[8]; } GUID; -#ifndef _SYS_GUID_OPERATOR_EQ_ -#define _SYS_GUID_OPERATOR_EQ_ inline bool operator==(const GUID& lhs, const GUID& rhs) noexcept { return std::memcmp(&lhs, &rhs, sizeof(GUID)) == 0; } inline bool operator!=(const GUID& lhs, const GUID& rhs) noexcept { return !(lhs == rhs); } -#endif // _SYS_GUID_OPERATOR_EQ_ -#endif // GUID_DEFINED +#endif // _WIN32 +#define DECL_EQOP(T) \ +friend bool operator==(const T &lhs, const T &rhs) noexcept { return std::memcmp(&lhs, &rhs, sizeof(T)) == 0; } \ +friend bool operator!=(const T &lhs, const T &rhs) noexcept { return !(lhs == rhs); } extern const GUID DSPROPSETID_EAX_ReverbProperties; @@ -613,7 +614,7 @@ struct EAX30SOURCEPROPERTIES { float flOcclusionLFRatio; // occlusion low-frequency level re. main control float flOcclusionRoomRatio; // relative occlusion control for room effect float flOcclusionDirectRatio; // relative occlusion control for direct path - long lExclusion; // main exlusion control (attenuation at high frequencies) + long lExclusion; // main exclusion control (attenuation at high frequencies) float flExclusionLFRatio; // exclusion low-frequency level re. main control long lOutsideVolumeHF; // outside sound cone level at high frequencies float flDopplerFactor; // like DS3D flDopplerFactor but per source @@ -836,6 +837,7 @@ struct EAXREVERBPROPERTIES { float flLFReference; // reference low frequency float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect unsigned long ulFlags; // modifies the behavior of properties + DECL_EQOP(EAXREVERBPROPERTIES) }; // EAXREVERBPROPERTIES @@ -965,6 +967,7 @@ enum EAXAGCCOMPRESSOR_PROPERTY : unsigned int { struct EAXAGCCOMPRESSORPROPERTIES { unsigned long ulOnOff; // Switch Compressor on or off + DECL_EQOP(EAXAGCCOMPRESSORPROPERTIES) }; // EAXAGCCOMPRESSORPROPERTIES @@ -991,6 +994,7 @@ struct EAXAUTOWAHPROPERTIES { float flReleaseTime; // Release time (seconds) long lResonance; // Resonance (mB) long lPeakLevel; // Peak level (mB) + DECL_EQOP(EAXAUTOWAHPROPERTIES) }; // EAXAUTOWAHPROPERTIES @@ -1038,6 +1042,7 @@ struct EAXCHORUSPROPERTIES { float flDepth; // Depth (0 to 1) float flFeedback; // Feedback (-1 to 1) float flDelay; // Delay (seconds) + DECL_EQOP(EAXCHORUSPROPERTIES) }; // EAXCHORUSPROPERTIES @@ -1086,6 +1091,7 @@ struct EAXDISTORTIONPROPERTIES { float flLowPassCutOff; // Controls the cut-off of the filter pre-distortion (Hz) float flEQCenter; // Controls the center frequency of the EQ post-distortion (Hz) float flEQBandwidth; // Controls the bandwidth of the EQ post-distortion (Hz) + DECL_EQOP(EAXDISTORTIONPROPERTIES) }; // EAXDISTORTIONPROPERTIES @@ -1130,6 +1136,7 @@ struct EAXECHOPROPERTIES { float flDamping; // Controls a low-pass filter that dampens the echoes (0 to 1) float flFeedback; // Controls the duration of echo repetition (0 to 1) float flSpread; // Controls the left-right spread of the echoes + DECL_EQOP(EAXECHOPROPERTIES) }; // EAXECHOPROPERTIES @@ -1184,6 +1191,7 @@ struct EAXEQUALIZERPROPERTIES { float flMid2Width; // (octaves) long lHighGain; // (mB) float flHighCutOff; // (Hz) + DECL_EQOP(EAXEQUALIZERPROPERTIES) }; // EAXEQUALIZERPROPERTIES @@ -1255,6 +1263,7 @@ struct EAXFLANGERPROPERTIES { float flDepth; // Depth (0 to 1) float flFeedback; // Feedback (0 to 1) float flDelay; // Delay (seconds) + DECL_EQOP(EAXFLANGERPROPERTIES) }; // EAXFLANGERPROPERTIES @@ -1305,6 +1314,7 @@ struct EAXFREQUENCYSHIFTERPROPERTIES { float flFrequency; // (Hz) unsigned long ulLeftDirection; // see enum above unsigned long ulRightDirection; // see enum above + DECL_EQOP(EAXFREQUENCYSHIFTERPROPERTIES) }; // EAXFREQUENCYSHIFTERPROPERTIES @@ -1383,6 +1393,7 @@ struct EAXVOCALMORPHERPROPERTIES { long lPhonemeBCoarseTuning; // (semitones) unsigned long ulWaveform; // Waveform selector - see enum above float flRate; // (Hz) + DECL_EQOP(EAXVOCALMORPHERPROPERTIES) }; // EAXVOCALMORPHERPROPERTIES @@ -1425,6 +1436,7 @@ enum EAXPITCHSHIFTER_PROPERTY : unsigned int { struct EAXPITCHSHIFTERPROPERTIES { long lCoarseTune; // Amount of pitch shift (semitones) long lFineTune; // Amount of pitch shift (cents) + DECL_EQOP(EAXPITCHSHIFTERPROPERTIES) }; // EAXPITCHSHIFTERPROPERTIES @@ -1460,6 +1472,7 @@ struct EAXRINGMODULATORPROPERTIES { float flFrequency; // Frequency of modulation (Hz) float flHighPassCutOff; // Cut-off frequency of high-pass filter (Hz) unsigned long ulWaveform; // Waveform selector - see enum above + DECL_EQOP(EAXRINGMODULATORPROPERTIES) }; // EAXRINGMODULATORPROPERTIES @@ -1490,4 +1503,5 @@ using LPEAXGET = ALenum(AL_APIENTRY*)( ALvoid* property_buffer, ALuint property_size); +#undef DECL_EQOP #endif // !EAX_API_INCLUDED diff --git a/al/eax/call.h b/al/eax/call.h index 5ec33b0f..45ff328c 100644 --- a/al/eax/call.h +++ b/al/eax/call.h @@ -55,13 +55,13 @@ public: fail_too_small(); const auto count = minz(mPropertyBufferSize / sizeof(TValue), max_count); - return al::as_span(static_cast<TValue*>(mPropertyBuffer), count); + return {static_cast<TValue*>(mPropertyBuffer), count}; } template<typename TValue> al::span<TValue> get_values() const { - return get_values<TValue>(~size_t{}); + return get_values<TValue>(~0_uz); } template<typename TException, typename TValue> diff --git a/al/eax/effect.h b/al/eax/effect.h index a0b4e71b..afe4d94d 100644 --- a/al/eax/effect.h +++ b/al/eax/effect.h @@ -4,60 +4,55 @@ #include <cassert> #include <memory> +#include <variant> #include "alnumeric.h" #include "AL/al.h" #include "core/effects/base.h" #include "call.h" -struct EaxEffectErrorMessages -{ +struct EaxEffectErrorMessages { static constexpr auto unknown_property_id() noexcept { return "Unknown property id."; } static constexpr auto unknown_version() noexcept { return "Unknown version."; } }; // EaxEffectErrorMessages -/* TODO: Use std::variant (C++17). */ -enum class EaxEffectType { - None, Reverb, Chorus, Autowah, Compressor, Distortion, Echo, Equalizer, Flanger, - FrequencyShifter, Modulator, PitchShifter, VocalMorpher -}; -struct EaxEffectProps { - EaxEffectType mType; - union { - EAXREVERBPROPERTIES mReverb; - EAXCHORUSPROPERTIES mChorus; - EAXAUTOWAHPROPERTIES mAutowah; - EAXAGCCOMPRESSORPROPERTIES mCompressor; - EAXDISTORTIONPROPERTIES mDistortion; - EAXECHOPROPERTIES mEcho; - EAXEQUALIZERPROPERTIES mEqualizer; - EAXFLANGERPROPERTIES mFlanger; - EAXFREQUENCYSHIFTERPROPERTIES mFrequencyShifter; - EAXRINGMODULATORPROPERTIES mModulator; - EAXPITCHSHIFTERPROPERTIES mPitchShifter; - EAXVOCALMORPHERPROPERTIES mVocalMorpher; - }; -}; - -constexpr ALenum EnumFromEaxEffectType(const EaxEffectProps &props) +using EaxEffectProps = std::variant<std::monostate, + EAXREVERBPROPERTIES, + EAXCHORUSPROPERTIES, + EAXAUTOWAHPROPERTIES, + EAXAGCCOMPRESSORPROPERTIES, + EAXDISTORTIONPROPERTIES, + EAXECHOPROPERTIES, + EAXEQUALIZERPROPERTIES, + EAXFLANGERPROPERTIES, + EAXFREQUENCYSHIFTERPROPERTIES, + EAXRINGMODULATORPROPERTIES, + EAXPITCHSHIFTERPROPERTIES, + EAXVOCALMORPHERPROPERTIES>; + +template<typename... Ts> +struct overloaded : Ts... { using Ts::operator()...; }; + +template<typename... Ts> +overloaded(Ts...) -> overloaded<Ts...>; + +constexpr ALenum EnumFromEaxEffectType(const EaxEffectProps &props) noexcept { - switch(props.mType) - { - case EaxEffectType::None: break; - case EaxEffectType::Reverb: return AL_EFFECT_EAXREVERB; - case EaxEffectType::Chorus: return AL_EFFECT_CHORUS; - case EaxEffectType::Autowah: return AL_EFFECT_AUTOWAH; - case EaxEffectType::Compressor: return AL_EFFECT_COMPRESSOR; - case EaxEffectType::Distortion: return AL_EFFECT_DISTORTION; - case EaxEffectType::Echo: return AL_EFFECT_ECHO; - case EaxEffectType::Equalizer: return AL_EFFECT_EQUALIZER; - case EaxEffectType::Flanger: return AL_EFFECT_FLANGER; - case EaxEffectType::FrequencyShifter: return AL_EFFECT_FREQUENCY_SHIFTER; - case EaxEffectType::Modulator: return AL_EFFECT_RING_MODULATOR; - case EaxEffectType::PitchShifter: return AL_EFFECT_PITCH_SHIFTER; - case EaxEffectType::VocalMorpher: return AL_EFFECT_VOCAL_MORPHER; - } - return AL_EFFECT_NULL; + return std::visit(overloaded{ + [](const std::monostate&) noexcept { return AL_EFFECT_NULL; }, + [](const EAXREVERBPROPERTIES&) noexcept { return AL_EFFECT_EAXREVERB; }, + [](const EAXCHORUSPROPERTIES&) noexcept { return AL_EFFECT_CHORUS; }, + [](const EAXAUTOWAHPROPERTIES&) noexcept { return AL_EFFECT_AUTOWAH; }, + [](const EAXAGCCOMPRESSORPROPERTIES&) noexcept { return AL_EFFECT_COMPRESSOR; }, + [](const EAXDISTORTIONPROPERTIES&) noexcept { return AL_EFFECT_DISTORTION; }, + [](const EAXECHOPROPERTIES&) noexcept { return AL_EFFECT_ECHO; }, + [](const EAXEQUALIZERPROPERTIES&) noexcept { return AL_EFFECT_EQUALIZER; }, + [](const EAXFLANGERPROPERTIES&) noexcept { return AL_EFFECT_FLANGER; }, + [](const EAXFREQUENCYSHIFTERPROPERTIES&) noexcept { return AL_EFFECT_FREQUENCY_SHIFTER; }, + [](const EAXRINGMODULATORPROPERTIES&) noexcept { return AL_EFFECT_RING_MODULATOR; }, + [](const EAXPITCHSHIFTERPROPERTIES&) noexcept { return AL_EFFECT_PITCH_SHIFTER; }, + [](const EAXVOCALMORPHERPROPERTIES&) noexcept { return AL_EFFECT_VOCAL_MORPHER; } + }, props); } struct EaxReverbCommitter { @@ -300,30 +295,30 @@ public: } -#define EAXCALL(T, Callable, ...) \ - if(T == EaxEffectType::Reverb) \ +#define EAXCALL(Props, Callable, ...) \ + if(std::holds_alternative<EAXREVERBPROPERTIES>(Props)) \ return Callable<EaxReverbCommitter>(__VA_ARGS__); \ - if(T == EaxEffectType::Chorus) \ + if(std::holds_alternative<EAXCHORUSPROPERTIES>(Props)) \ return Callable<EaxChorusCommitter>(__VA_ARGS__); \ - if(T == EaxEffectType::Autowah) \ + if(std::holds_alternative<EAXAUTOWAHPROPERTIES>(Props)) \ return Callable<EaxAutowahCommitter>(__VA_ARGS__); \ - if(T == EaxEffectType::Compressor) \ + if(std::holds_alternative<EAXAGCCOMPRESSORPROPERTIES>(Props)) \ return Callable<EaxCompressorCommitter>(__VA_ARGS__); \ - if(T == EaxEffectType::Distortion) \ + if(std::holds_alternative<EAXDISTORTIONPROPERTIES>(Props)) \ return Callable<EaxDistortionCommitter>(__VA_ARGS__); \ - if(T == EaxEffectType::Echo) \ + if(std::holds_alternative<EAXECHOPROPERTIES>(Props)) \ return Callable<EaxEchoCommitter>(__VA_ARGS__); \ - if(T == EaxEffectType::Equalizer) \ + if(std::holds_alternative<EAXEQUALIZERPROPERTIES>(Props)) \ return Callable<EaxEqualizerCommitter>(__VA_ARGS__); \ - if(T == EaxEffectType::Flanger) \ + if(std::holds_alternative<EAXFLANGERPROPERTIES>(Props)) \ return Callable<EaxFlangerCommitter>(__VA_ARGS__); \ - if(T == EaxEffectType::FrequencyShifter) \ + if(std::holds_alternative<EAXFREQUENCYSHIFTERPROPERTIES>(Props)) \ return Callable<EaxFrequencyShifterCommitter>(__VA_ARGS__); \ - if(T == EaxEffectType::Modulator) \ + if(std::holds_alternative<EAXRINGMODULATORPROPERTIES>(Props)) \ return Callable<EaxModulatorCommitter>(__VA_ARGS__); \ - if(T == EaxEffectType::PitchShifter) \ + if(std::holds_alternative<EAXPITCHSHIFTERPROPERTIES>(Props)) \ return Callable<EaxPitchShifterCommitter>(__VA_ARGS__); \ - if(T == EaxEffectType::VocalMorpher) \ + if(std::holds_alternative<EAXVOCALMORPHERPROPERTIES>(Props)) \ return Callable<EaxVocalMorpherCommitter>(__VA_ARGS__); \ return Callable<EaxNullCommitter>(__VA_ARGS__) @@ -332,7 +327,7 @@ public: { return T::Set(std::forward<Args>(args)...); } static void call_set(const EaxCall &call, EaxEffectProps &props) - { EAXCALL(props.mType, call_set, call, props); } + { EAXCALL(props, call_set, call, props); } void set(const EaxCall &call) { @@ -353,7 +348,7 @@ public: { return T::Get(std::forward<Args>(args)...); } static void call_get(const EaxCall &call, const EaxEffectProps &props) - { EAXCALL(props.mType, call_get, call, props); } + { EAXCALL(props, call_get, call, props); } void get(const EaxCall &call) { @@ -373,7 +368,7 @@ public: { return T{props_, al_effect_props_}.commit(std::forward<Args>(args)...); } bool call_commit(const EaxEffectProps &props) - { EAXCALL(props.mType, call_commit, props); } + { EAXCALL(props, call_commit, props); } bool commit(int eax_version) { diff --git a/al/eax/exception.cpp b/al/eax/exception.cpp index 435e7442..e4945d88 100644 --- a/al/eax/exception.cpp +++ b/al/eax/exception.cpp @@ -6,54 +6,27 @@ #include <string> -EaxException::EaxException(const char *context, const char *message) +EaxException::EaxException(std::string_view context, std::string_view message) : std::runtime_error{make_message(context, message)} { } EaxException::~EaxException() = default; -std::string EaxException::make_message(const char *context, const char *message) +std::string EaxException::make_message(std::string_view context, std::string_view message) { - const auto context_size = (context ? std::string::traits_type::length(context) : 0); - const auto has_contex = (context_size > 0); - - const auto message_size = (message ? std::string::traits_type::length(message) : 0); - const auto has_message = (message_size > 0); - - if (!has_contex && !has_message) - { - return std::string{}; - } - - static constexpr char left_prefix[] = "["; - const auto left_prefix_size = std::string::traits_type::length(left_prefix); - - static constexpr char right_prefix[] = "] "; - const auto right_prefix_size = std::string::traits_type::length(right_prefix); - - const auto what_size = - ( - has_contex ? - left_prefix_size + context_size + right_prefix_size : - 0) + - message_size + - 1; - auto what = std::string{}; - what.reserve(what_size); - - if (has_contex) - { - what.append(left_prefix, left_prefix_size); - what.append(context, context_size); - what.append(right_prefix, right_prefix_size); - } + if(context.empty() && message.empty()) + return what; - if (has_message) + what.reserve((!context.empty() ? context.size() + 3 : 0) + message.length() + 1); + if(!context.empty()) { - what.append(message, message_size); + what += "["; + what += context; + what += "] "; } + what += message; return what; } diff --git a/al/eax/exception.h b/al/eax/exception.h index 3ae88cdc..336654f0 100644 --- a/al/eax/exception.h +++ b/al/eax/exception.h @@ -1,16 +1,16 @@ #ifndef EAX_EXCEPTION_INCLUDED #define EAX_EXCEPTION_INCLUDED - #include <stdexcept> #include <string> +#include <string_view> class EaxException : public std::runtime_error { - static std::string make_message(const char *context, const char *message); + static std::string make_message(std::string_view context, std::string_view message); public: - EaxException(const char *context, const char *message); + EaxException(std::string_view context, std::string_view message); ~EaxException() override; }; // EaxException diff --git a/al/eax/fx_slot_index.h b/al/eax/fx_slot_index.h index 63dba037..9f350d9b 100644 --- a/al/eax/fx_slot_index.h +++ b/al/eax/fx_slot_index.h @@ -3,17 +3,16 @@ #include <cstddef> +#include <optional> -#include "aloptional.h" #include "api.h" using EaxFxSlotIndexValue = std::size_t; -class EaxFxSlotIndex : public al::optional<EaxFxSlotIndexValue> -{ +class EaxFxSlotIndex : public std::optional<EaxFxSlotIndexValue> { public: - using al::optional<EaxFxSlotIndexValue>::optional; + using std::optional<EaxFxSlotIndexValue>::optional; EaxFxSlotIndex& operator=(const EaxFxSlotIndexValue &value) { set(value); return *this; } EaxFxSlotIndex& operator=(const GUID &guid) { set(guid); return *this; } diff --git a/al/eax/globals.cpp b/al/eax/globals.cpp deleted file mode 100644 index 80e9dbfe..00000000 --- a/al/eax/globals.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "config.h" - -#include "globals.h" - - -bool eax_g_is_enabled = true; - - -const char eax1_ext_name[] = "EAX"; -const char eax2_ext_name[] = "EAX2.0"; -const char eax3_ext_name[] = "EAX3.0"; -const char eax4_ext_name[] = "EAX4.0"; -const char eax5_ext_name[] = "EAX5.0"; - -const char eax_x_ram_ext_name[] = "EAX-RAM"; - -const char eax_eax_set_func_name[] = "EAXSet"; -const char eax_eax_get_func_name[] = "EAXGet"; - -const char eax_eax_set_buffer_mode_func_name[] = "EAXSetBufferMode"; -const char eax_eax_get_buffer_mode_func_name[] = "EAXGetBufferMode"; diff --git a/al/eax/globals.h b/al/eax/globals.h index 1b4d63b8..ff05d009 100644 --- a/al/eax/globals.h +++ b/al/eax/globals.h @@ -1,22 +1,20 @@ #ifndef EAX_GLOBALS_INCLUDED #define EAX_GLOBALS_INCLUDED +inline bool eax_g_is_enabled{true}; -extern bool eax_g_is_enabled; +inline constexpr char eax1_ext_name[]{"EAX"}; +inline constexpr char eax2_ext_name[]{"EAX2.0"}; +inline constexpr char eax3_ext_name[]{"EAX3.0"}; +inline constexpr char eax4_ext_name[]{"EAX4.0"}; +inline constexpr char eax5_ext_name[]{"EAX5.0"}; +inline constexpr char eax_x_ram_ext_name[]{"EAX-RAM"}; -extern const char eax1_ext_name[]; -extern const char eax2_ext_name[]; -extern const char eax3_ext_name[]; -extern const char eax4_ext_name[]; -extern const char eax5_ext_name[]; +inline constexpr char eax_eax_set_func_name[]{"EAXSet"}; +inline constexpr char eax_eax_get_func_name[]{"EAXGet"}; -extern const char eax_x_ram_ext_name[]; - -extern const char eax_eax_set_func_name[]; -extern const char eax_eax_get_func_name[]; - -extern const char eax_eax_set_buffer_mode_func_name[]; -extern const char eax_eax_get_buffer_mode_func_name[]; +inline constexpr char eax_eax_set_buffer_mode_func_name[]{"EAXSetBufferMode"}; +inline constexpr char eax_eax_get_buffer_mode_func_name[]{"EAXGetBufferMode"}; #endif // !EAX_GLOBALS_INCLUDED diff --git a/al/eax/utils.cpp b/al/eax/utils.cpp index b3ed6ca1..53599ac5 100644 --- a/al/eax/utils.cpp +++ b/al/eax/utils.cpp @@ -8,7 +8,7 @@ #include "core/logging.h" -void eax_log_exception(const char *message) noexcept +void eax_log_exception(std::string_view message) noexcept { const auto exception_ptr = std::current_exception(); assert(exception_ptr); @@ -18,9 +18,9 @@ void eax_log_exception(const char *message) noexcept } catch(const std::exception& ex) { const auto ex_message = ex.what(); - ERR("%s %s\n", message ? message : "", ex_message); + ERR("%.*s %s\n", static_cast<int>(message.length()), message.data(), ex_message); } catch(...) { - ERR("%s %s\n", message ? message : "", "Generic exception."); + ERR("%.*s %s\n", static_cast<int>(message.length()), message.data(), "Generic exception."); } } diff --git a/al/eax/utils.h b/al/eax/utils.h index 8ff75a18..8e0f975f 100644 --- a/al/eax/utils.h +++ b/al/eax/utils.h @@ -4,8 +4,11 @@ #include <algorithm> #include <cstdint> #include <string> +#include <string_view> #include <type_traits> +#include "opthelpers.h" + using EaxDirtyFlags = unsigned int; struct EaxAlLowPassParam { @@ -13,16 +16,13 @@ struct EaxAlLowPassParam { float gain_hf; }; -void eax_log_exception(const char *message) noexcept; +void eax_log_exception(std::string_view message) noexcept; template<typename TException, typename TValue> -void eax_validate_range( - const char* value_name, - const TValue& value, - const TValue& min_value, +void eax_validate_range(std::string_view value_name, const TValue& value, const TValue& min_value, const TValue& max_value) { - if (value >= min_value && value <= max_value) + if(value >= min_value && value <= max_value) LIKELY return; const auto message = diff --git a/al/eax/x_ram.h b/al/eax/x_ram.h index 438b9916..d10fe697 100644 --- a/al/eax/x_ram.h +++ b/al/eax/x_ram.h @@ -25,14 +25,7 @@ constexpr auto AL_STORAGE_HARDWARE_NAME = "AL_STORAGE_HARDWARE"; constexpr auto AL_STORAGE_ACCESSIBLE_NAME = "AL_STORAGE_ACCESSIBLE"; -ALboolean AL_APIENTRY EAXSetBufferMode( - ALsizei n, - const ALuint* buffers, - ALint value); - -ALenum AL_APIENTRY EAXGetBufferMode( - ALuint buffer, - ALint* pReserved); - +ALboolean AL_APIENTRY EAXSetBufferMode(ALsizei n, const ALuint *buffers, ALint value) noexcept; +ALenum AL_APIENTRY EAXGetBufferMode(ALuint buffer, ALint *pReserved) noexcept; #endif // !EAX_X_RAM_INCLUDED diff --git a/al/effect.cpp b/al/effect.cpp index bde89912..3e48e91b 100644 --- a/al/effect.cpp +++ b/al/effect.cpp @@ -31,6 +31,7 @@ #include <new> #include <numeric> #include <utility> +#include <vector> #include "AL/al.h" #include "AL/alc.h" @@ -48,8 +49,8 @@ #include "alstring.h" #include "core/except.h" #include "core/logging.h" +#include "direct_defs.h" #include "opthelpers.h" -#include "vector.h" #ifdef ALSOFT_EAX #include <cassert> @@ -73,7 +74,7 @@ const EffectList gEffectList[16]{ { "vmorpher", VMORPHER_EFFECT, AL_EFFECT_VOCAL_MORPHER }, { "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT }, { "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_DIALOGUE }, - { "convolution", CONVOLUTION_EFFECT, AL_EFFECT_CONVOLUTION_REVERB_SOFT }, + { "convolution", CONVOLUTION_EFFECT, AL_EFFECT_CONVOLUTION_SOFT }, }; bool DisabledEffects[MAX_EFFECTS]; @@ -112,7 +113,7 @@ constexpr EffectPropsItem EffectPropsList[] = { { AL_EFFECT_VOCAL_MORPHER, VmorpherEffectProps, VmorpherEffectVtable }, { AL_EFFECT_DEDICATED_DIALOGUE, DedicatedEffectProps, DedicatedEffectVtable }, { AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, DedicatedEffectProps, DedicatedEffectVtable }, - { AL_EFFECT_CONVOLUTION_REVERB_SOFT, ConvolutionEffectProps, ConvolutionEffectVtable }, + { AL_EFFECT_CONVOLUTION_SOFT, ConvolutionEffectProps, ConvolutionEffectVtable }, }; @@ -161,7 +162,7 @@ void InitEffectParams(ALeffect *effect, ALenum type) bool EnsureEffects(ALCdevice *device, size_t needed) { - size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), size_t{0}, + size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), 0_uz, [](size_t cur, const EffectSubList &sublist) noexcept -> size_t { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })}; @@ -206,11 +207,13 @@ ALeffect *AllocEffect(ALCdevice *device) void FreeEffect(ALCdevice *device, ALeffect *effect) { + device->mEffectNames.erase(effect->id); + const ALuint id{effect->id - 1}; const size_t lidx{id >> 6}; const ALuint slidx{id & 0x3f}; - al::destroy_at(effect); + std::destroy_at(effect); device->EffectList[lidx].FreeMask |= 1_u64 << slidx; } @@ -230,12 +233,9 @@ inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) } // namespace -AL_API void AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects) -START_API_FUNC +AL_API DECL_FUNC2(void, alGenEffects, ALsizei, ALuint*) +FORCE_ALIGN void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(n < 0) UNLIKELY context->setError(AL_INVALID_VALUE, "Generating %d effects", n); if(n <= 0) UNLIKELY return; @@ -259,7 +259,7 @@ START_API_FUNC /* Store the allocated buffer IDs in a separate local list, to avoid * modifying the user storage in case of failure. */ - al::vector<ALuint> ids; + std::vector<ALuint> ids; ids.reserve(static_cast<ALuint>(n)); do { ALeffect *effect{AllocEffect(device)}; @@ -268,14 +268,11 @@ START_API_FUNC std::copy(ids.cbegin(), ids.cend(), effects); } } -END_API_FUNC -AL_API void AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects) -START_API_FUNC +AL_API DECL_FUNC2(void, alDeleteEffects, ALsizei, const ALuint*) +FORCE_ALIGN void AL_APIENTRY alDeleteEffectsDirect(ALCcontext *context, ALsizei n, + const ALuint *effects) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(n < 0) UNLIKELY context->setError(AL_INVALID_VALUE, "Deleting %d effects", n); if(n <= 0) UNLIKELY return; @@ -303,29 +300,21 @@ START_API_FUNC }; std::for_each(effects, effects_end, delete_effect); } -END_API_FUNC -AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect) -START_API_FUNC +AL_API DECL_FUNC1(ALboolean, alIsEffect, ALuint) +FORCE_ALIGN ALboolean AL_APIENTRY alIsEffectDirect(ALCcontext *context, ALuint effect) noexcept { - ContextRef context{GetContextRef()}; - if(context) LIKELY - { - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard<std::mutex> _{device->EffectLock}; - if(!effect || LookupEffect(device, effect)) - return AL_TRUE; - } + ALCdevice *device{context->mALDevice.get()}; + std::lock_guard<std::mutex> _{device->EffectLock}; + if(!effect || LookupEffect(device, effect)) + return AL_TRUE; return AL_FALSE; } -END_API_FUNC -AL_API void AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value) -START_API_FUNC +AL_API DECL_FUNC3(void, alEffecti, ALuint, ALenum, ALint) +FORCE_ALIGN void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, ALenum param, + ALint value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->EffectLock}; @@ -361,21 +350,18 @@ START_API_FUNC context->setError(e.errorCode(), "%s", e.what()); } } -END_API_FUNC -AL_API void AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alEffectiv, ALuint, ALenum, const ALint*) +FORCE_ALIGN void AL_APIENTRY alEffectivDirect(ALCcontext *context, ALuint effect, ALenum param, + const ALint *values) noexcept { switch(param) { case AL_EFFECT_TYPE: - alEffecti(effect, param, values[0]); + alEffectiDirect(context, effect, param, values[0]); return; } - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->EffectLock}; @@ -391,14 +377,11 @@ START_API_FUNC context->setError(e.errorCode(), "%s", e.what()); } } -END_API_FUNC -AL_API void AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat value) -START_API_FUNC +AL_API DECL_FUNC3(void, alEffectf, ALuint, ALenum, ALfloat) +FORCE_ALIGN void AL_APIENTRY alEffectfDirect(ALCcontext *context, ALuint effect, ALenum param, + ALfloat value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->EffectLock}; @@ -414,14 +397,11 @@ START_API_FUNC context->setError(e.errorCode(), "%s", e.what()); } } -END_API_FUNC -AL_API void AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alEffectfv, ALuint, ALenum, const ALfloat*) +FORCE_ALIGN void AL_APIENTRY alEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param, + const ALfloat *values) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->EffectLock}; @@ -437,14 +417,11 @@ START_API_FUNC context->setError(e.errorCode(), "%s", e.what()); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetEffecti, ALuint, ALenum, ALint*) +FORCE_ALIGN void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effect, ALenum param, + ALint *value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->EffectLock}; @@ -462,21 +439,18 @@ START_API_FUNC context->setError(e.errorCode(), "%s", e.what()); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetEffectiv, ALuint, ALenum, ALint*) +FORCE_ALIGN void AL_APIENTRY alGetEffectivDirect(ALCcontext *context, ALuint effect, ALenum param, + ALint *values) noexcept { switch(param) { case AL_EFFECT_TYPE: - alGetEffecti(effect, param, values); + alGetEffectiDirect(context, effect, param, values); return; } - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->EffectLock}; @@ -492,14 +466,11 @@ START_API_FUNC context->setError(e.errorCode(), "%s", e.what()); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *value) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetEffectf, ALuint, ALenum, ALfloat*) +FORCE_ALIGN void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effect, ALenum param, + ALfloat *value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->EffectLock}; @@ -515,14 +486,11 @@ START_API_FUNC context->setError(e.errorCode(), "%s", e.what()); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetEffectfv, ALuint, ALenum, ALfloat*) +FORCE_ALIGN void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param, + ALfloat *values) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->EffectLock}; @@ -538,7 +506,6 @@ START_API_FUNC context->setError(e.errorCode(), "%s", e.what()); } } -END_API_FUNC void InitEffect(ALeffect *effect) @@ -546,13 +513,29 @@ void InitEffect(ALeffect *effect) InitEffectParams(effect, AL_EFFECT_NULL); } +void ALeffect::SetName(ALCcontext* context, ALuint id, std::string_view name) +{ + ALCdevice *device{context->mALDevice.get()}; + std::lock_guard<std::mutex> _{device->EffectLock}; + + auto effect = LookupEffect(device, id); + if(!effect) UNLIKELY + return context->setError(AL_INVALID_NAME, "Invalid effect ID %u", id); + + device->mEffectNames.insert_or_assign(id, name); +} + + EffectSubList::~EffectSubList() { + if(!Effects) + return; + uint64_t usemask{~FreeMask}; while(usemask) { const int idx{al::countr_zero(usemask)}; - al::destroy_at(Effects+idx); + std::destroy_at(Effects+idx); usemask &= ~(1_u64 << idx); } FreeMask = ~usemask; diff --git a/al/effect.h b/al/effect.h index a1d43313..27e9dd72 100644 --- a/al/effect.h +++ b/al/effect.h @@ -1,6 +1,8 @@ #ifndef AL_EFFECT_H #define AL_EFFECT_H +#include <string_view> + #include "AL/al.h" #include "AL/efx.h" @@ -50,6 +52,8 @@ struct ALeffect { /* Self ID */ ALuint id{0u}; + static void SetName(ALCcontext *context, ALuint id, std::string_view name); + DISABLE_ALLOC() }; diff --git a/al/effects/autowah.cpp b/al/effects/autowah.cpp index 129318f4..1a8b43fc 100644 --- a/al/effects/autowah.cpp +++ b/al/effects/autowah.cpp @@ -192,19 +192,16 @@ template<> template<> bool AutowahCommitter::commit(const EaxEffectProps &props) { - if(props.mType == mEaxProps.mType - && mEaxProps.mAutowah.flAttackTime == props.mAutowah.flAttackTime - && mEaxProps.mAutowah.flReleaseTime == props.mAutowah.flReleaseTime - && mEaxProps.mAutowah.lResonance == props.mAutowah.lResonance - && mEaxProps.mAutowah.lPeakLevel == props.mAutowah.lPeakLevel) + if(props == mEaxProps) return false; mEaxProps = props; - mAlProps.Autowah.AttackTime = props.mAutowah.flAttackTime; - mAlProps.Autowah.ReleaseTime = props.mAutowah.flReleaseTime; - mAlProps.Autowah.Resonance = level_mb_to_gain(static_cast<float>(props.mAutowah.lResonance)); - mAlProps.Autowah.PeakGain = level_mb_to_gain(static_cast<float>(props.mAutowah.lPeakLevel)); + auto &eaxprops = std::get<EAXAUTOWAHPROPERTIES>(props); + mAlProps.Autowah.AttackTime = eaxprops.flAttackTime; + mAlProps.Autowah.ReleaseTime = eaxprops.flReleaseTime; + mAlProps.Autowah.Resonance = level_mb_to_gain(static_cast<float>(eaxprops.lResonance)); + mAlProps.Autowah.PeakGain = level_mb_to_gain(static_cast<float>(eaxprops.lPeakLevel)); return true; } @@ -212,39 +209,46 @@ bool AutowahCommitter::commit(const EaxEffectProps &props) template<> void AutowahCommitter::SetDefaults(EaxEffectProps &props) { - props.mType = EaxEffectType::Autowah; - props.mAutowah.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME; - props.mAutowah.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME; - props.mAutowah.lResonance = EAXAUTOWAH_DEFAULTRESONANCE; - props.mAutowah.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL; + static constexpr EAXAUTOWAHPROPERTIES defprops{[] + { + EAXAUTOWAHPROPERTIES ret{}; + ret.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME; + ret.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME; + ret.lResonance = EAXAUTOWAH_DEFAULTRESONANCE; + ret.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL; + return ret; + }()}; + props = defprops; } template<> -void AutowahCommitter::Get(const EaxCall &call, const EaxEffectProps &props) +void AutowahCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) { + auto &props = std::get<EAXAUTOWAHPROPERTIES>(props_); switch(call.get_property_id()) { case EAXAUTOWAH_NONE: break; - case EAXAUTOWAH_ALLPARAMETERS: call.set_value<Exception>(props.mAutowah); break; - case EAXAUTOWAH_ATTACKTIME: call.set_value<Exception>(props.mAutowah.flAttackTime); break; - case EAXAUTOWAH_RELEASETIME: call.set_value<Exception>(props.mAutowah.flReleaseTime); break; - case EAXAUTOWAH_RESONANCE: call.set_value<Exception>(props.mAutowah.lResonance); break; - case EAXAUTOWAH_PEAKLEVEL: call.set_value<Exception>(props.mAutowah.lPeakLevel); break; + case EAXAUTOWAH_ALLPARAMETERS: call.set_value<Exception>(props); break; + case EAXAUTOWAH_ATTACKTIME: call.set_value<Exception>(props.flAttackTime); break; + case EAXAUTOWAH_RELEASETIME: call.set_value<Exception>(props.flReleaseTime); break; + case EAXAUTOWAH_RESONANCE: call.set_value<Exception>(props.lResonance); break; + case EAXAUTOWAH_PEAKLEVEL: call.set_value<Exception>(props.lPeakLevel); break; default: fail_unknown_property_id(); } } template<> -void AutowahCommitter::Set(const EaxCall &call, EaxEffectProps &props) +void AutowahCommitter::Set(const EaxCall &call, EaxEffectProps &props_) { + auto &props = std::get<EAXAUTOWAHPROPERTIES>(props_); switch(call.get_property_id()) { case EAXAUTOWAH_NONE: break; - case EAXAUTOWAH_ALLPARAMETERS: defer<AllValidator>(call, props.mAutowah); break; - case EAXAUTOWAH_ATTACKTIME: defer<AttackTimeValidator>(call, props.mAutowah.flAttackTime); break; - case EAXAUTOWAH_RELEASETIME: defer<ReleaseTimeValidator>(call, props.mAutowah.flReleaseTime); break; - case EAXAUTOWAH_RESONANCE: defer<ResonanceValidator>(call, props.mAutowah.lResonance); break; - case EAXAUTOWAH_PEAKLEVEL: defer<PeakLevelValidator>(call, props.mAutowah.lPeakLevel); break; + case EAXAUTOWAH_ALLPARAMETERS: defer<AllValidator>(call, props); break; + case EAXAUTOWAH_ATTACKTIME: defer<AttackTimeValidator>(call, props.flAttackTime); break; + case EAXAUTOWAH_RELEASETIME: defer<ReleaseTimeValidator>(call, props.flReleaseTime); break; + case EAXAUTOWAH_RESONANCE: defer<ResonanceValidator>(call, props.lResonance); break; + case EAXAUTOWAH_PEAKLEVEL: defer<PeakLevelValidator>(call, props.lPeakLevel); break; default: fail_unknown_property_id(); } } diff --git a/al/effects/chorus.cpp b/al/effects/chorus.cpp index 305259a4..90b38e4d 100644 --- a/al/effects/chorus.cpp +++ b/al/effects/chorus.cpp @@ -1,13 +1,13 @@ #include "config.h" +#include <optional> #include <stdexcept> #include "AL/al.h" #include "AL/efx.h" #include "alc/effects/base.h" -#include "aloptional.h" #include "core/logging.h" #include "effects.h" @@ -27,14 +27,14 @@ static_assert(FlangerMaxDelay >= AL_FLANGER_MAX_DELAY, "Flanger max delay too sm static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch"); static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch"); -inline al::optional<ChorusWaveform> WaveformFromEnum(ALenum type) +inline std::optional<ChorusWaveform> WaveformFromEnum(ALenum type) { switch(type) { case AL_CHORUS_WAVEFORM_SINUSOID: return ChorusWaveform::Sinusoid; case AL_CHORUS_WAVEFORM_TRIANGLE: return ChorusWaveform::Triangle; } - return al::nullopt; + return std::nullopt; } inline ALenum EnumFromWaveform(ChorusWaveform type) { @@ -294,9 +294,7 @@ namespace { struct EaxChorusTraits { using Props = EAXCHORUSPROPERTIES; using Committer = EaxChorusCommitter; - static constexpr auto Field = &EaxEffectProps::mChorus; - static constexpr auto eax_effect_type() { return EaxEffectType::Chorus; } static constexpr auto efx_effect() { return AL_EFFECT_CHORUS; } static constexpr auto eax_none_param_id() { return EAXCHORUS_NONE; } @@ -361,9 +359,7 @@ struct EaxChorusTraits { struct EaxFlangerTraits { using Props = EAXFLANGERPROPERTIES; using Committer = EaxFlangerCommitter; - static constexpr auto Field = &EaxEffectProps::mFlanger; - static constexpr auto eax_effect_type() { return EaxEffectType::Flanger; } static constexpr auto efx_effect() { return AL_EFFECT_FLANGER; } static constexpr auto eax_none_param_id() { return EAXFLANGER_NONE; } @@ -431,8 +427,6 @@ struct ChorusFlangerEffect { using Committer = typename Traits::Committer; using Exception = typename Committer::Exception; - static constexpr auto Field = Traits::Field; - struct WaveformValidator { void operator()(unsigned long ulWaveform) const { @@ -514,8 +508,7 @@ struct ChorusFlangerEffect { public: static void SetDefaults(EaxEffectProps &props) { - auto&& all = props.*Field; - props.mType = Traits::eax_effect_type(); + auto&& all = props.emplace<typename Traits::Props>(); all.ulWaveform = Traits::eax_default_waveform(); all.lPhase = Traits::eax_default_phase(); all.flRate = Traits::eax_default_rate(); @@ -527,7 +520,7 @@ public: static void Get(const EaxCall &call, const EaxEffectProps &props) { - auto&& all = props.*Field; + auto&& all = std::get<typename Traits::Props>(props); switch(call.get_property_id()) { case Traits::eax_none_param_id(): @@ -568,7 +561,7 @@ public: static void Set(const EaxCall &call, EaxEffectProps &props) { - auto&& all = props.*Field; + auto&& all = std::get<typename Traits::Props>(props); switch(call.get_property_id()) { case Traits::eax_none_param_id(): @@ -609,18 +602,11 @@ public: static bool Commit(const EaxEffectProps &props, EaxEffectProps &props_, EffectProps &al_props_) { - if(props.mType == props_.mType) - { - auto&& src = props_.*Field; - auto&& dst = props.*Field; - if(dst.ulWaveform == src.ulWaveform && dst.lPhase == src.lPhase - && dst.flRate == src.flRate && dst.flDepth == src.flDepth - && dst.flFeedback == src.flFeedback && dst.flDelay == src.flDelay) - return false; - } + if(props == props_) + return false; props_ = props; - auto&& dst = props.*Field; + auto&& dst = std::get<typename Traits::Props>(props); al_props_.Chorus.Waveform = Traits::eax_waveform(dst.ulWaveform); al_props_.Chorus.Phase = static_cast<int>(dst.lPhase); diff --git a/al/effects/compressor.cpp b/al/effects/compressor.cpp index a4aa8e77..ca8af84f 100644 --- a/al/effects/compressor.cpp +++ b/al/effects/compressor.cpp @@ -118,43 +118,43 @@ template<> template<> bool CompressorCommitter::commit(const EaxEffectProps &props) { - if(props.mType == mEaxProps.mType - && props.mCompressor.ulOnOff == mEaxProps.mCompressor.ulOnOff) + if(props == mEaxProps) return false; mEaxProps = props; - mAlProps.Compressor.OnOff = (props.mCompressor.ulOnOff != 0); + mAlProps.Compressor.OnOff = (std::get<EAXAGCCOMPRESSORPROPERTIES>(props).ulOnOff != 0); return true; } template<> void CompressorCommitter::SetDefaults(EaxEffectProps &props) { - props.mType = EaxEffectType::Compressor; - props.mCompressor.ulOnOff = EAXAGCCOMPRESSOR_DEFAULTONOFF; + props = EAXAGCCOMPRESSORPROPERTIES{EAXAGCCOMPRESSOR_DEFAULTONOFF}; } template<> -void CompressorCommitter::Get(const EaxCall &call, const EaxEffectProps &props) +void CompressorCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) { + auto &props = std::get<EAXAGCCOMPRESSORPROPERTIES>(props_); switch(call.get_property_id()) { case EAXAGCCOMPRESSOR_NONE: break; - case EAXAGCCOMPRESSOR_ALLPARAMETERS: call.set_value<Exception>(props.mCompressor); break; - case EAXAGCCOMPRESSOR_ONOFF: call.set_value<Exception>(props.mCompressor.ulOnOff); break; + case EAXAGCCOMPRESSOR_ALLPARAMETERS: call.set_value<Exception>(props); break; + case EAXAGCCOMPRESSOR_ONOFF: call.set_value<Exception>(props.ulOnOff); break; default: fail_unknown_property_id(); } } template<> -void CompressorCommitter::Set(const EaxCall &call, EaxEffectProps &props) +void CompressorCommitter::Set(const EaxCall &call, EaxEffectProps &props_) { + auto &props = std::get<EAXAGCCOMPRESSORPROPERTIES>(props_); switch(call.get_property_id()) { case EAXAGCCOMPRESSOR_NONE: break; - case EAXAGCCOMPRESSOR_ALLPARAMETERS: defer<AllValidator>(call, props.mCompressor); break; - case EAXAGCCOMPRESSOR_ONOFF: defer<OnOffValidator>(call, props.mCompressor.ulOnOff); break; + case EAXAGCCOMPRESSOR_ALLPARAMETERS: defer<AllValidator>(call, props); break; + case EAXAGCCOMPRESSOR_ONOFF: defer<OnOffValidator>(call, props.ulOnOff); break; default: fail_unknown_property_id(); } } diff --git a/al/effects/convolution.cpp b/al/effects/convolution.cpp index 8e850fd3..9c091e53 100644 --- a/al/effects/convolution.cpp +++ b/al/effects/convolution.cpp @@ -1,6 +1,8 @@ #include "config.h" +#include <cmath> + #include "AL/al.h" #include "alc/inprogext.h" @@ -36,12 +38,25 @@ void Convolution_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/) param}; } } -void Convolution_setParamfv(EffectProps *props, ALenum param, const float *vals) +void Convolution_setParamfv(EffectProps *props, ALenum param, const float *values) { switch(param) { + case AL_CONVOLUTION_ORIENTATION_SOFT: + if(!(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]) + && std::isfinite(values[3]) && std::isfinite(values[4]) && std::isfinite(values[5]))) + throw effect_exception{AL_INVALID_VALUE, "Property 0x%04x value out of range", param}; + + props->Convolution.OrientAt[0] = values[0]; + props->Convolution.OrientAt[1] = values[1]; + props->Convolution.OrientAt[2] = values[2]; + props->Convolution.OrientUp[0] = values[3]; + props->Convolution.OrientUp[1] = values[4]; + props->Convolution.OrientUp[2] = values[5]; + break; + default: - Convolution_setParamf(props, param, vals[0]); + Convolution_setParamf(props, param, values[0]); } } @@ -71,18 +86,29 @@ void Convolution_getParamf(const EffectProps* /*props*/, ALenum param, float* /* param}; } } -void Convolution_getParamfv(const EffectProps *props, ALenum param, float *vals) +void Convolution_getParamfv(const EffectProps *props, ALenum param, float *values) { switch(param) { + case AL_CONVOLUTION_ORIENTATION_SOFT: + values[0] = props->Convolution.OrientAt[0]; + values[1] = props->Convolution.OrientAt[1]; + values[2] = props->Convolution.OrientAt[2]; + values[3] = props->Convolution.OrientUp[0]; + values[4] = props->Convolution.OrientUp[1]; + values[5] = props->Convolution.OrientUp[2]; + break; + default: - Convolution_getParamf(props, param, vals); + Convolution_getParamf(props, param, values); } } EffectProps genDefaultProps() noexcept { EffectProps props{}; + props.Convolution.OrientAt = {0.0f, 0.0f, -1.0f}; + props.Convolution.OrientUp = {0.0f, 1.0f, 0.0f}; return props; } diff --git a/al/effects/distortion.cpp b/al/effects/distortion.cpp index ee298ddf..e046d8e7 100644 --- a/al/effects/distortion.cpp +++ b/al/effects/distortion.cpp @@ -207,20 +207,17 @@ template<> template<> bool DistortionCommitter::commit(const EaxEffectProps &props) { - if(props.mType == mEaxProps.mType && mEaxProps.mDistortion.flEdge == props.mDistortion.flEdge - && mEaxProps.mDistortion.lGain == props.mDistortion.lGain - && mEaxProps.mDistortion.flLowPassCutOff == props.mDistortion.flLowPassCutOff - && mEaxProps.mDistortion.flEQCenter == props.mDistortion.flEQCenter - && mEaxProps.mDistortion.flEQBandwidth == props.mDistortion.flEQBandwidth) + if(props == mEaxProps) return false; mEaxProps = props; - mAlProps.Distortion.Edge = props.mDistortion.flEdge; - mAlProps.Distortion.Gain = level_mb_to_gain(static_cast<float>(props.mDistortion.lGain)); - mAlProps.Distortion.LowpassCutoff = props.mDistortion.flLowPassCutOff; - mAlProps.Distortion.EQCenter = props.mDistortion.flEQCenter; - mAlProps.Distortion.EQBandwidth = props.mDistortion.flEdge; + auto &eaxprops = std::get<EAXDISTORTIONPROPERTIES>(props); + mAlProps.Distortion.Edge = eaxprops.flEdge; + mAlProps.Distortion.Gain = level_mb_to_gain(static_cast<float>(eaxprops.lGain)); + mAlProps.Distortion.LowpassCutoff = eaxprops.flLowPassCutOff; + mAlProps.Distortion.EQCenter = eaxprops.flEQCenter; + mAlProps.Distortion.EQBandwidth = eaxprops.flEdge; return true; } @@ -228,42 +225,49 @@ bool DistortionCommitter::commit(const EaxEffectProps &props) template<> void DistortionCommitter::SetDefaults(EaxEffectProps &props) { - props.mType = EaxEffectType::Distortion; - props.mDistortion.flEdge = EAXDISTORTION_DEFAULTEDGE; - props.mDistortion.lGain = EAXDISTORTION_DEFAULTGAIN; - props.mDistortion.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF; - props.mDistortion.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER; - props.mDistortion.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH; + static constexpr EAXDISTORTIONPROPERTIES defprops{[] + { + EAXDISTORTIONPROPERTIES ret{}; + ret.flEdge = EAXDISTORTION_DEFAULTEDGE; + ret.lGain = EAXDISTORTION_DEFAULTGAIN; + ret.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF; + ret.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER; + ret.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH; + return ret; + }()}; + props = defprops; } template<> -void DistortionCommitter::Get(const EaxCall &call, const EaxEffectProps &props) +void DistortionCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) { + auto &props = std::get<EAXDISTORTIONPROPERTIES>(props_); switch(call.get_property_id()) { case EAXDISTORTION_NONE: break; - case EAXDISTORTION_ALLPARAMETERS: call.set_value<Exception>(props.mDistortion); break; - case EAXDISTORTION_EDGE: call.set_value<Exception>(props.mDistortion.flEdge); break; - case EAXDISTORTION_GAIN: call.set_value<Exception>(props.mDistortion.lGain); break; - case EAXDISTORTION_LOWPASSCUTOFF: call.set_value<Exception>(props.mDistortion.flLowPassCutOff); break; - case EAXDISTORTION_EQCENTER: call.set_value<Exception>(props.mDistortion.flEQCenter); break; - case EAXDISTORTION_EQBANDWIDTH: call.set_value<Exception>(props.mDistortion.flEQBandwidth); break; + case EAXDISTORTION_ALLPARAMETERS: call.set_value<Exception>(props); break; + case EAXDISTORTION_EDGE: call.set_value<Exception>(props.flEdge); break; + case EAXDISTORTION_GAIN: call.set_value<Exception>(props.lGain); break; + case EAXDISTORTION_LOWPASSCUTOFF: call.set_value<Exception>(props.flLowPassCutOff); break; + case EAXDISTORTION_EQCENTER: call.set_value<Exception>(props.flEQCenter); break; + case EAXDISTORTION_EQBANDWIDTH: call.set_value<Exception>(props.flEQBandwidth); break; default: fail_unknown_property_id(); } } template<> -void DistortionCommitter::Set(const EaxCall &call, EaxEffectProps &props) +void DistortionCommitter::Set(const EaxCall &call, EaxEffectProps &props_) { + auto &props = std::get<EAXDISTORTIONPROPERTIES>(props_); switch(call.get_property_id()) { case EAXDISTORTION_NONE: break; - case EAXDISTORTION_ALLPARAMETERS: defer<AllValidator>(call, props.mDistortion); break; - case EAXDISTORTION_EDGE: defer<EdgeValidator>(call, props.mDistortion.flEdge); break; - case EAXDISTORTION_GAIN: defer<GainValidator>(call, props.mDistortion.lGain); break; - case EAXDISTORTION_LOWPASSCUTOFF: defer<LowPassCutOffValidator>(call, props.mDistortion.flLowPassCutOff); break; - case EAXDISTORTION_EQCENTER: defer<EqCenterValidator>(call, props.mDistortion.flEQCenter); break; - case EAXDISTORTION_EQBANDWIDTH: defer<EqBandwidthValidator>(call, props.mDistortion.flEQBandwidth); break; + case EAXDISTORTION_ALLPARAMETERS: defer<AllValidator>(call, props); break; + case EAXDISTORTION_EDGE: defer<EdgeValidator>(call, props.flEdge); break; + case EAXDISTORTION_GAIN: defer<GainValidator>(call, props.lGain); break; + case EAXDISTORTION_LOWPASSCUTOFF: defer<LowPassCutOffValidator>(call, props.flLowPassCutOff); break; + case EAXDISTORTION_EQCENTER: defer<EqCenterValidator>(call, props.flEQCenter); break; + case EAXDISTORTION_EQBANDWIDTH: defer<EqBandwidthValidator>(call, props.flEQBandwidth); break; default: fail_unknown_property_id(); } } diff --git a/al/effects/echo.cpp b/al/effects/echo.cpp index 2eb37603..48aacef3 100644 --- a/al/effects/echo.cpp +++ b/al/effects/echo.cpp @@ -204,20 +204,17 @@ template<> template<> bool EchoCommitter::commit(const EaxEffectProps &props) { - if(props.mType == mEaxProps.mType && mEaxProps.mEcho.flDelay == props.mEcho.flDelay - && mEaxProps.mEcho.flLRDelay == props.mEcho.flLRDelay - && mEaxProps.mEcho.flDamping == props.mEcho.flDamping - && mEaxProps.mEcho.flFeedback == props.mEcho.flFeedback - && mEaxProps.mEcho.flSpread == props.mEcho.flSpread) + if(props == mEaxProps) return false; mEaxProps = props; - mAlProps.Echo.Delay = props.mEcho.flDelay; - mAlProps.Echo.LRDelay = props.mEcho.flLRDelay; - mAlProps.Echo.Damping = props.mEcho.flDamping; - mAlProps.Echo.Feedback = props.mEcho.flFeedback; - mAlProps.Echo.Spread = props.mEcho.flSpread; + auto &eaxprops = std::get<EAXECHOPROPERTIES>(props); + mAlProps.Echo.Delay = eaxprops.flDelay; + mAlProps.Echo.LRDelay = eaxprops.flLRDelay; + mAlProps.Echo.Damping = eaxprops.flDamping; + mAlProps.Echo.Feedback = eaxprops.flFeedback; + mAlProps.Echo.Spread = eaxprops.flSpread; return true; } @@ -225,42 +222,49 @@ bool EchoCommitter::commit(const EaxEffectProps &props) template<> void EchoCommitter::SetDefaults(EaxEffectProps &props) { - props.mType = EaxEffectType::Echo; - props.mEcho.flDelay = EAXECHO_DEFAULTDELAY; - props.mEcho.flLRDelay = EAXECHO_DEFAULTLRDELAY; - props.mEcho.flDamping = EAXECHO_DEFAULTDAMPING; - props.mEcho.flFeedback = EAXECHO_DEFAULTFEEDBACK; - props.mEcho.flSpread = EAXECHO_DEFAULTSPREAD; + static constexpr EAXECHOPROPERTIES defprops{[] + { + EAXECHOPROPERTIES ret{}; + ret.flDelay = EAXECHO_DEFAULTDELAY; + ret.flLRDelay = EAXECHO_DEFAULTLRDELAY; + ret.flDamping = EAXECHO_DEFAULTDAMPING; + ret.flFeedback = EAXECHO_DEFAULTFEEDBACK; + ret.flSpread = EAXECHO_DEFAULTSPREAD; + return ret; + }()}; + props = defprops; } template<> -void EchoCommitter::Get(const EaxCall &call, const EaxEffectProps &props) +void EchoCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) { + auto &props = std::get<EAXECHOPROPERTIES>(props_); switch(call.get_property_id()) { case EAXECHO_NONE: break; - case EAXECHO_ALLPARAMETERS: call.set_value<Exception>(props.mEcho); break; - case EAXECHO_DELAY: call.set_value<Exception>(props.mEcho.flDelay); break; - case EAXECHO_LRDELAY: call.set_value<Exception>(props.mEcho.flLRDelay); break; - case EAXECHO_DAMPING: call.set_value<Exception>(props.mEcho.flDamping); break; - case EAXECHO_FEEDBACK: call.set_value<Exception>(props.mEcho.flFeedback); break; - case EAXECHO_SPREAD: call.set_value<Exception>(props.mEcho.flSpread); break; + case EAXECHO_ALLPARAMETERS: call.set_value<Exception>(props); break; + case EAXECHO_DELAY: call.set_value<Exception>(props.flDelay); break; + case EAXECHO_LRDELAY: call.set_value<Exception>(props.flLRDelay); break; + case EAXECHO_DAMPING: call.set_value<Exception>(props.flDamping); break; + case EAXECHO_FEEDBACK: call.set_value<Exception>(props.flFeedback); break; + case EAXECHO_SPREAD: call.set_value<Exception>(props.flSpread); break; default: fail_unknown_property_id(); } } template<> -void EchoCommitter::Set(const EaxCall &call, EaxEffectProps &props) +void EchoCommitter::Set(const EaxCall &call, EaxEffectProps &props_) { + auto &props = std::get<EAXECHOPROPERTIES>(props_); switch(call.get_property_id()) { case EAXECHO_NONE: break; - case EAXECHO_ALLPARAMETERS: defer<AllValidator>(call, props.mEcho); break; - case EAXECHO_DELAY: defer<DelayValidator>(call, props.mEcho.flDelay); break; - case EAXECHO_LRDELAY: defer<LrDelayValidator>(call, props.mEcho.flLRDelay); break; - case EAXECHO_DAMPING: defer<DampingValidator>(call, props.mEcho.flDamping); break; - case EAXECHO_FEEDBACK: defer<FeedbackValidator>(call, props.mEcho.flFeedback); break; - case EAXECHO_SPREAD: defer<SpreadValidator>(call, props.mEcho.flSpread); break; + case EAXECHO_ALLPARAMETERS: defer<AllValidator>(call, props); break; + case EAXECHO_DELAY: defer<DelayValidator>(call, props.flDelay); break; + case EAXECHO_LRDELAY: defer<LrDelayValidator>(call, props.flLRDelay); break; + case EAXECHO_DAMPING: defer<DampingValidator>(call, props.flDamping); break; + case EAXECHO_FEEDBACK: defer<FeedbackValidator>(call, props.flFeedback); break; + case EAXECHO_SPREAD: defer<SpreadValidator>(call, props.flSpread); break; default: fail_unknown_property_id(); } } diff --git a/al/effects/equalizer.cpp b/al/effects/equalizer.cpp index 7dc703db..76d5bdef 100644 --- a/al/effects/equalizer.cpp +++ b/al/effects/equalizer.cpp @@ -322,30 +322,22 @@ template<> template<> bool EqualizerCommitter::commit(const EaxEffectProps &props) { - if(props.mType == mEaxProps.mType && mEaxProps.mEqualizer.lLowGain == props.mEqualizer.lLowGain - && mEaxProps.mEqualizer.flLowCutOff == props.mEqualizer.flLowCutOff - && mEaxProps.mEqualizer.lMid1Gain == props.mEqualizer.lMid1Gain - && mEaxProps.mEqualizer.flMid1Center == props.mEqualizer.flMid1Center - && mEaxProps.mEqualizer.flMid1Width == props.mEqualizer.flMid1Width - && mEaxProps.mEqualizer.lMid2Gain == props.mEqualizer.lMid2Gain - && mEaxProps.mEqualizer.flMid2Center == props.mEqualizer.flMid2Center - && mEaxProps.mEqualizer.flMid2Width == props.mEqualizer.flMid2Width - && mEaxProps.mEqualizer.lHighGain == props.mEqualizer.lHighGain - && mEaxProps.mEqualizer.flHighCutOff == props.mEqualizer.flHighCutOff) + if(props == mEaxProps) return false; mEaxProps = props; - mAlProps.Equalizer.LowGain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lLowGain)); - mAlProps.Equalizer.LowCutoff = props.mEqualizer.flLowCutOff; - mAlProps.Equalizer.Mid1Gain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lMid1Gain)); - mAlProps.Equalizer.Mid1Center = props.mEqualizer.flMid1Center; - mAlProps.Equalizer.Mid1Width = props.mEqualizer.flMid1Width; - mAlProps.Equalizer.Mid2Gain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lMid2Gain)); - mAlProps.Equalizer.Mid2Center = props.mEqualizer.flMid2Center; - mAlProps.Equalizer.Mid2Width = props.mEqualizer.flMid2Width; - mAlProps.Equalizer.HighGain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lHighGain)); - mAlProps.Equalizer.HighCutoff = props.mEqualizer.flHighCutOff; + auto &eaxprops = std::get<EAXEQUALIZERPROPERTIES>(props); + mAlProps.Equalizer.LowGain = level_mb_to_gain(static_cast<float>(eaxprops.lLowGain)); + mAlProps.Equalizer.LowCutoff = eaxprops.flLowCutOff; + mAlProps.Equalizer.Mid1Gain = level_mb_to_gain(static_cast<float>(eaxprops.lMid1Gain)); + mAlProps.Equalizer.Mid1Center = eaxprops.flMid1Center; + mAlProps.Equalizer.Mid1Width = eaxprops.flMid1Width; + mAlProps.Equalizer.Mid2Gain = level_mb_to_gain(static_cast<float>(eaxprops.lMid2Gain)); + mAlProps.Equalizer.Mid2Center = eaxprops.flMid2Center; + mAlProps.Equalizer.Mid2Width = eaxprops.flMid2Width; + mAlProps.Equalizer.HighGain = level_mb_to_gain(static_cast<float>(eaxprops.lHighGain)); + mAlProps.Equalizer.HighCutoff = eaxprops.flHighCutOff; return true; } @@ -353,57 +345,64 @@ bool EqualizerCommitter::commit(const EaxEffectProps &props) template<> void EqualizerCommitter::SetDefaults(EaxEffectProps &props) { - props.mType = EaxEffectType::Equalizer; - props.mEqualizer.lLowGain = EAXEQUALIZER_DEFAULTLOWGAIN; - props.mEqualizer.flLowCutOff = EAXEQUALIZER_DEFAULTLOWCUTOFF; - props.mEqualizer.lMid1Gain = EAXEQUALIZER_DEFAULTMID1GAIN; - props.mEqualizer.flMid1Center = EAXEQUALIZER_DEFAULTMID1CENTER; - props.mEqualizer.flMid1Width = EAXEQUALIZER_DEFAULTMID1WIDTH; - props.mEqualizer.lMid2Gain = EAXEQUALIZER_DEFAULTMID2GAIN; - props.mEqualizer.flMid2Center = EAXEQUALIZER_DEFAULTMID2CENTER; - props.mEqualizer.flMid2Width = EAXEQUALIZER_DEFAULTMID2WIDTH; - props.mEqualizer.lHighGain = EAXEQUALIZER_DEFAULTHIGHGAIN; - props.mEqualizer.flHighCutOff = EAXEQUALIZER_DEFAULTHIGHCUTOFF; + static constexpr EAXEQUALIZERPROPERTIES defprops{[] + { + EAXEQUALIZERPROPERTIES ret{}; + ret.lLowGain = EAXEQUALIZER_DEFAULTLOWGAIN; + ret.flLowCutOff = EAXEQUALIZER_DEFAULTLOWCUTOFF; + ret.lMid1Gain = EAXEQUALIZER_DEFAULTMID1GAIN; + ret.flMid1Center = EAXEQUALIZER_DEFAULTMID1CENTER; + ret.flMid1Width = EAXEQUALIZER_DEFAULTMID1WIDTH; + ret.lMid2Gain = EAXEQUALIZER_DEFAULTMID2GAIN; + ret.flMid2Center = EAXEQUALIZER_DEFAULTMID2CENTER; + ret.flMid2Width = EAXEQUALIZER_DEFAULTMID2WIDTH; + ret.lHighGain = EAXEQUALIZER_DEFAULTHIGHGAIN; + ret.flHighCutOff = EAXEQUALIZER_DEFAULTHIGHCUTOFF; + return ret; + }()}; + props = defprops; } template<> -void EqualizerCommitter::Get(const EaxCall &call, const EaxEffectProps &props) +void EqualizerCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) { + auto &props = std::get<EAXEQUALIZERPROPERTIES>(props_); switch(call.get_property_id()) { case EAXEQUALIZER_NONE: break; - case EAXEQUALIZER_ALLPARAMETERS: call.set_value<Exception>(props.mEqualizer); break; - case EAXEQUALIZER_LOWGAIN: call.set_value<Exception>(props.mEqualizer.lLowGain); break; - case EAXEQUALIZER_LOWCUTOFF: call.set_value<Exception>(props.mEqualizer.flLowCutOff); break; - case EAXEQUALIZER_MID1GAIN: call.set_value<Exception>(props.mEqualizer.lMid1Gain); break; - case EAXEQUALIZER_MID1CENTER: call.set_value<Exception>(props.mEqualizer.flMid1Center); break; - case EAXEQUALIZER_MID1WIDTH: call.set_value<Exception>(props.mEqualizer.flMid1Width); break; - case EAXEQUALIZER_MID2GAIN: call.set_value<Exception>(props.mEqualizer.lMid2Gain); break; - case EAXEQUALIZER_MID2CENTER: call.set_value<Exception>(props.mEqualizer.flMid2Center); break; - case EAXEQUALIZER_MID2WIDTH: call.set_value<Exception>(props.mEqualizer.flMid2Width); break; - case EAXEQUALIZER_HIGHGAIN: call.set_value<Exception>(props.mEqualizer.lHighGain); break; - case EAXEQUALIZER_HIGHCUTOFF: call.set_value<Exception>(props.mEqualizer.flHighCutOff); break; + case EAXEQUALIZER_ALLPARAMETERS: call.set_value<Exception>(props); break; + case EAXEQUALIZER_LOWGAIN: call.set_value<Exception>(props.lLowGain); break; + case EAXEQUALIZER_LOWCUTOFF: call.set_value<Exception>(props.flLowCutOff); break; + case EAXEQUALIZER_MID1GAIN: call.set_value<Exception>(props.lMid1Gain); break; + case EAXEQUALIZER_MID1CENTER: call.set_value<Exception>(props.flMid1Center); break; + case EAXEQUALIZER_MID1WIDTH: call.set_value<Exception>(props.flMid1Width); break; + case EAXEQUALIZER_MID2GAIN: call.set_value<Exception>(props.lMid2Gain); break; + case EAXEQUALIZER_MID2CENTER: call.set_value<Exception>(props.flMid2Center); break; + case EAXEQUALIZER_MID2WIDTH: call.set_value<Exception>(props.flMid2Width); break; + case EAXEQUALIZER_HIGHGAIN: call.set_value<Exception>(props.lHighGain); break; + case EAXEQUALIZER_HIGHCUTOFF: call.set_value<Exception>(props.flHighCutOff); break; default: fail_unknown_property_id(); } } template<> -void EqualizerCommitter::Set(const EaxCall &call, EaxEffectProps &props) +void EqualizerCommitter::Set(const EaxCall &call, EaxEffectProps &props_) { + auto &props = std::get<EAXEQUALIZERPROPERTIES>(props_); switch(call.get_property_id()) { case EAXEQUALIZER_NONE: break; - case EAXEQUALIZER_ALLPARAMETERS: defer<AllValidator>(call, props.mEqualizer); break; - case EAXEQUALIZER_LOWGAIN: defer<LowGainValidator>(call, props.mEqualizer.lLowGain); break; - case EAXEQUALIZER_LOWCUTOFF: defer<LowCutOffValidator>(call, props.mEqualizer.flLowCutOff); break; - case EAXEQUALIZER_MID1GAIN: defer<Mid1GainValidator>(call, props.mEqualizer.lMid1Gain); break; - case EAXEQUALIZER_MID1CENTER: defer<Mid1CenterValidator>(call, props.mEqualizer.flMid1Center); break; - case EAXEQUALIZER_MID1WIDTH: defer<Mid1WidthValidator>(call, props.mEqualizer.flMid1Width); break; - case EAXEQUALIZER_MID2GAIN: defer<Mid2GainValidator>(call, props.mEqualizer.lMid2Gain); break; - case EAXEQUALIZER_MID2CENTER: defer<Mid2CenterValidator>(call, props.mEqualizer.flMid2Center); break; - case EAXEQUALIZER_MID2WIDTH: defer<Mid2WidthValidator>(call, props.mEqualizer.flMid2Width); break; - case EAXEQUALIZER_HIGHGAIN: defer<HighGainValidator>(call, props.mEqualizer.lHighGain); break; - case EAXEQUALIZER_HIGHCUTOFF: defer<HighCutOffValidator>(call, props.mEqualizer.flHighCutOff); break; + case EAXEQUALIZER_ALLPARAMETERS: defer<AllValidator>(call, props); break; + case EAXEQUALIZER_LOWGAIN: defer<LowGainValidator>(call, props.lLowGain); break; + case EAXEQUALIZER_LOWCUTOFF: defer<LowCutOffValidator>(call, props.flLowCutOff); break; + case EAXEQUALIZER_MID1GAIN: defer<Mid1GainValidator>(call, props.lMid1Gain); break; + case EAXEQUALIZER_MID1CENTER: defer<Mid1CenterValidator>(call, props.flMid1Center); break; + case EAXEQUALIZER_MID1WIDTH: defer<Mid1WidthValidator>(call, props.flMid1Width); break; + case EAXEQUALIZER_MID2GAIN: defer<Mid2GainValidator>(call, props.lMid2Gain); break; + case EAXEQUALIZER_MID2CENTER: defer<Mid2CenterValidator>(call, props.flMid2Center); break; + case EAXEQUALIZER_MID2WIDTH: defer<Mid2WidthValidator>(call, props.flMid2Width); break; + case EAXEQUALIZER_HIGHGAIN: defer<HighGainValidator>(call, props.lHighGain); break; + case EAXEQUALIZER_HIGHCUTOFF: defer<HighCutOffValidator>(call, props.flHighCutOff); break; default: fail_unknown_property_id(); } } diff --git a/al/effects/fshifter.cpp b/al/effects/fshifter.cpp index 949db203..37c372c3 100644 --- a/al/effects/fshifter.cpp +++ b/al/effects/fshifter.cpp @@ -1,13 +1,13 @@ #include "config.h" +#include <optional> #include <stdexcept> #include "AL/al.h" #include "AL/efx.h" #include "alc/effects/base.h" -#include "aloptional.h" #include "effects.h" #ifdef ALSOFT_EAX @@ -20,7 +20,7 @@ namespace { -al::optional<FShifterDirection> DirectionFromEmum(ALenum value) +std::optional<FShifterDirection> DirectionFromEmum(ALenum value) { switch(value) { @@ -28,7 +28,7 @@ al::optional<FShifterDirection> DirectionFromEmum(ALenum value) case AL_FREQUENCY_SHIFTER_DIRECTION_UP: return FShifterDirection::Up; case AL_FREQUENCY_SHIFTER_DIRECTION_OFF: return FShifterDirection::Off; } - return al::nullopt; + return std::nullopt; } ALenum EnumFromDirection(FShifterDirection dir) { @@ -200,10 +200,7 @@ template<> template<> bool FrequencyShifterCommitter::commit(const EaxEffectProps &props) { - if(props.mType == mEaxProps.mType - && mEaxProps.mFrequencyShifter.flFrequency == props.mFrequencyShifter.flFrequency - && mEaxProps.mFrequencyShifter.ulLeftDirection == props.mFrequencyShifter.ulLeftDirection - && mEaxProps.mFrequencyShifter.ulRightDirection == props.mFrequencyShifter.ulRightDirection) + if(props == mEaxProps) return false; mEaxProps = props; @@ -217,9 +214,10 @@ bool FrequencyShifterCommitter::commit(const EaxEffectProps &props) return FShifterDirection::Off; }; - mAlProps.Fshifter.Frequency = props.mFrequencyShifter.flFrequency; - mAlProps.Fshifter.LeftDirection = get_direction(props.mFrequencyShifter.ulLeftDirection); - mAlProps.Fshifter.RightDirection = get_direction(props.mFrequencyShifter.ulRightDirection); + auto &eaxprops = std::get<EAXFREQUENCYSHIFTERPROPERTIES>(props); + mAlProps.Fshifter.Frequency = eaxprops.flFrequency; + mAlProps.Fshifter.LeftDirection = get_direction(eaxprops.ulLeftDirection); + mAlProps.Fshifter.RightDirection = get_direction(eaxprops.ulRightDirection); return true; } @@ -227,36 +225,43 @@ bool FrequencyShifterCommitter::commit(const EaxEffectProps &props) template<> void FrequencyShifterCommitter::SetDefaults(EaxEffectProps &props) { - props.mType = EaxEffectType::FrequencyShifter; - props.mFrequencyShifter.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY; - props.mFrequencyShifter.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION; - props.mFrequencyShifter.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION; + static constexpr EAXFREQUENCYSHIFTERPROPERTIES defprops{[] + { + EAXFREQUENCYSHIFTERPROPERTIES ret{}; + ret.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY; + ret.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION; + ret.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION; + return ret; + }()}; + props = defprops; } template<> -void FrequencyShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props) +void FrequencyShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) { + auto &props = std::get<EAXFREQUENCYSHIFTERPROPERTIES>(props_); switch(call.get_property_id()) { case EAXFREQUENCYSHIFTER_NONE: break; - case EAXFREQUENCYSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props.mFrequencyShifter); break; - case EAXFREQUENCYSHIFTER_FREQUENCY: call.set_value<Exception>(props.mFrequencyShifter.flFrequency); break; - case EAXFREQUENCYSHIFTER_LEFTDIRECTION: call.set_value<Exception>(props.mFrequencyShifter.ulLeftDirection); break; - case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: call.set_value<Exception>(props.mFrequencyShifter.ulRightDirection); break; + case EAXFREQUENCYSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props); break; + case EAXFREQUENCYSHIFTER_FREQUENCY: call.set_value<Exception>(props.flFrequency); break; + case EAXFREQUENCYSHIFTER_LEFTDIRECTION: call.set_value<Exception>(props.ulLeftDirection); break; + case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: call.set_value<Exception>(props.ulRightDirection); break; default: fail_unknown_property_id(); } } template<> -void FrequencyShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props) +void FrequencyShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props_) { + auto &props = std::get<EAXFREQUENCYSHIFTERPROPERTIES>(props_); switch(call.get_property_id()) { case EAXFREQUENCYSHIFTER_NONE: break; - case EAXFREQUENCYSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props.mFrequencyShifter); break; - case EAXFREQUENCYSHIFTER_FREQUENCY: defer<FrequencyValidator>(call, props.mFrequencyShifter.flFrequency); break; - case EAXFREQUENCYSHIFTER_LEFTDIRECTION: defer<LeftDirectionValidator>(call, props.mFrequencyShifter.ulLeftDirection); break; - case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: defer<RightDirectionValidator>(call, props.mFrequencyShifter.ulRightDirection); break; + case EAXFREQUENCYSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props); break; + case EAXFREQUENCYSHIFTER_FREQUENCY: defer<FrequencyValidator>(call, props.flFrequency); break; + case EAXFREQUENCYSHIFTER_LEFTDIRECTION: defer<LeftDirectionValidator>(call, props.ulLeftDirection); break; + case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: defer<RightDirectionValidator>(call, props.ulRightDirection); break; default: fail_unknown_property_id(); } } diff --git a/al/effects/modulator.cpp b/al/effects/modulator.cpp index 5f37d08f..366e7ef7 100644 --- a/al/effects/modulator.cpp +++ b/al/effects/modulator.cpp @@ -1,13 +1,13 @@ #include "config.h" +#include <optional> #include <stdexcept> #include "AL/al.h" #include "AL/efx.h" #include "alc/effects/base.h" -#include "aloptional.h" #include "effects.h" #ifdef ALSOFT_EAX @@ -20,7 +20,7 @@ namespace { -al::optional<ModulatorWaveform> WaveformFromEmum(ALenum value) +std::optional<ModulatorWaveform> WaveformFromEmum(ALenum value) { switch(value) { @@ -28,7 +28,7 @@ al::optional<ModulatorWaveform> WaveformFromEmum(ALenum value) case AL_RING_MODULATOR_SAWTOOTH: return ModulatorWaveform::Sawtooth; case AL_RING_MODULATOR_SQUARE: return ModulatorWaveform::Square; } - return al::nullopt; + return std::nullopt; } ALenum EnumFromWaveform(ModulatorWaveform type) { @@ -206,10 +206,7 @@ template<> template<> bool ModulatorCommitter::commit(const EaxEffectProps &props) { - if(props.mType == mEaxProps.mType - && mEaxProps.mModulator.flFrequency == props.mModulator.flFrequency - && mEaxProps.mModulator.flHighPassCutOff == props.mModulator.flHighPassCutOff - && mEaxProps.mModulator.ulWaveform == props.mModulator.ulWaveform) + if(props == mEaxProps) return false; mEaxProps = props; @@ -225,9 +222,10 @@ bool ModulatorCommitter::commit(const EaxEffectProps &props) return ModulatorWaveform::Sinusoid; }; - mAlProps.Modulator.Frequency = props.mModulator.flFrequency; - mAlProps.Modulator.HighPassCutoff = props.mModulator.flHighPassCutOff; - mAlProps.Modulator.Waveform = get_waveform(props.mModulator.ulWaveform); + auto &eaxprops = std::get<EAXRINGMODULATORPROPERTIES>(props); + mAlProps.Modulator.Frequency = eaxprops.flFrequency; + mAlProps.Modulator.HighPassCutoff = eaxprops.flHighPassCutOff; + mAlProps.Modulator.Waveform = get_waveform(eaxprops.ulWaveform); return true; } @@ -235,36 +233,43 @@ bool ModulatorCommitter::commit(const EaxEffectProps &props) template<> void ModulatorCommitter::SetDefaults(EaxEffectProps &props) { - props.mType = EaxEffectType::Modulator; - props.mModulator.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY; - props.mModulator.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF; - props.mModulator.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM; + static constexpr EAXRINGMODULATORPROPERTIES defprops{[] + { + EAXRINGMODULATORPROPERTIES ret{}; + ret.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY; + ret.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF; + ret.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM; + return ret; + }()}; + props = defprops; } template<> -void ModulatorCommitter::Get(const EaxCall &call, const EaxEffectProps &props) +void ModulatorCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) { + auto &props = std::get<EAXRINGMODULATORPROPERTIES>(props_); switch(call.get_property_id()) { case EAXRINGMODULATOR_NONE: break; - case EAXRINGMODULATOR_ALLPARAMETERS: call.set_value<Exception>(props.mModulator); break; - case EAXRINGMODULATOR_FREQUENCY: call.set_value<Exception>(props.mModulator.flFrequency); break; - case EAXRINGMODULATOR_HIGHPASSCUTOFF: call.set_value<Exception>(props.mModulator.flHighPassCutOff); break; - case EAXRINGMODULATOR_WAVEFORM: call.set_value<Exception>(props.mModulator.ulWaveform); break; + case EAXRINGMODULATOR_ALLPARAMETERS: call.set_value<Exception>(props); break; + case EAXRINGMODULATOR_FREQUENCY: call.set_value<Exception>(props.flFrequency); break; + case EAXRINGMODULATOR_HIGHPASSCUTOFF: call.set_value<Exception>(props.flHighPassCutOff); break; + case EAXRINGMODULATOR_WAVEFORM: call.set_value<Exception>(props.ulWaveform); break; default: fail_unknown_property_id(); } } template<> -void ModulatorCommitter::Set(const EaxCall &call, EaxEffectProps &props) +void ModulatorCommitter::Set(const EaxCall &call, EaxEffectProps &props_) { + auto &props = std::get<EAXRINGMODULATORPROPERTIES>(props_); switch (call.get_property_id()) { case EAXRINGMODULATOR_NONE: break; - case EAXRINGMODULATOR_ALLPARAMETERS: defer<AllValidator>(call, props.mModulator); break; - case EAXRINGMODULATOR_FREQUENCY: defer<FrequencyValidator>(call, props.mModulator.flFrequency); break; - case EAXRINGMODULATOR_HIGHPASSCUTOFF: defer<HighPassCutOffValidator>(call, props.mModulator.flHighPassCutOff); break; - case EAXRINGMODULATOR_WAVEFORM: defer<WaveformValidator>(call, props.mModulator.ulWaveform); break; + case EAXRINGMODULATOR_ALLPARAMETERS: defer<AllValidator>(call, props); break; + case EAXRINGMODULATOR_FREQUENCY: defer<FrequencyValidator>(call, props.flFrequency); break; + case EAXRINGMODULATOR_HIGHPASSCUTOFF: defer<HighPassCutOffValidator>(call, props.flHighPassCutOff); break; + case EAXRINGMODULATOR_WAVEFORM: defer<WaveformValidator>(call, props.ulWaveform); break; default: fail_unknown_property_id(); } } diff --git a/al/effects/null.cpp b/al/effects/null.cpp index 0bbc183a..1e8787e7 100644 --- a/al/effects/null.cpp +++ b/al/effects/null.cpp @@ -120,7 +120,7 @@ template<> template<> bool NullCommitter::commit(const EaxEffectProps &props) { - const bool ret{props.mType != mEaxProps.mType}; + const bool ret{props != mEaxProps}; mEaxProps = props; return ret; } @@ -128,8 +128,7 @@ bool NullCommitter::commit(const EaxEffectProps &props) template<> void NullCommitter::SetDefaults(EaxEffectProps &props) { - props = EaxEffectProps{}; - props.mType = EaxEffectType::None; + props.emplace<std::monostate>(); } template<> diff --git a/al/effects/pshifter.cpp b/al/effects/pshifter.cpp index 634eb186..10824016 100644 --- a/al/effects/pshifter.cpp +++ b/al/effects/pshifter.cpp @@ -141,15 +141,14 @@ template<> template<> bool PitchShifterCommitter::commit(const EaxEffectProps &props) { - if(props.mType == mEaxProps.mType - && mEaxProps.mPitchShifter.lCoarseTune == props.mPitchShifter.lCoarseTune - && mEaxProps.mPitchShifter.lFineTune == props.mPitchShifter.lFineTune) + if(props == mEaxProps) return false; mEaxProps = props; - mAlProps.Pshifter.CoarseTune = static_cast<int>(mEaxProps.mPitchShifter.lCoarseTune); - mAlProps.Pshifter.FineTune = static_cast<int>(mEaxProps.mPitchShifter.lFineTune); + auto &eaxprops = std::get<EAXPITCHSHIFTERPROPERTIES>(props); + mAlProps.Pshifter.CoarseTune = static_cast<int>(eaxprops.lCoarseTune); + mAlProps.Pshifter.FineTune = static_cast<int>(eaxprops.lFineTune); return true; } @@ -157,33 +156,34 @@ bool PitchShifterCommitter::commit(const EaxEffectProps &props) template<> void PitchShifterCommitter::SetDefaults(EaxEffectProps &props) { - props.mType = EaxEffectType::PitchShifter; - props.mPitchShifter.lCoarseTune = EAXPITCHSHIFTER_DEFAULTCOARSETUNE; - props.mPitchShifter.lFineTune = EAXPITCHSHIFTER_DEFAULTFINETUNE; + props = EAXPITCHSHIFTERPROPERTIES{EAXPITCHSHIFTER_DEFAULTCOARSETUNE, + EAXPITCHSHIFTER_DEFAULTFINETUNE}; } template<> -void PitchShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props) +void PitchShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) { + auto &props = std::get<EAXPITCHSHIFTERPROPERTIES>(props_); switch(call.get_property_id()) { case EAXPITCHSHIFTER_NONE: break; - case EAXPITCHSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props.mPitchShifter); break; - case EAXPITCHSHIFTER_COARSETUNE: call.set_value<Exception>(props.mPitchShifter.lCoarseTune); break; - case EAXPITCHSHIFTER_FINETUNE: call.set_value<Exception>(props.mPitchShifter.lFineTune); break; + case EAXPITCHSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props); break; + case EAXPITCHSHIFTER_COARSETUNE: call.set_value<Exception>(props.lCoarseTune); break; + case EAXPITCHSHIFTER_FINETUNE: call.set_value<Exception>(props.lFineTune); break; default: fail_unknown_property_id(); } } template<> -void PitchShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props) +void PitchShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props_) { + auto &props = std::get<EAXPITCHSHIFTERPROPERTIES>(props_); switch(call.get_property_id()) { case EAXPITCHSHIFTER_NONE: break; - case EAXPITCHSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props.mPitchShifter); break; - case EAXPITCHSHIFTER_COARSETUNE: defer<CoarseTuneValidator>(call, props.mPitchShifter.lCoarseTune); break; - case EAXPITCHSHIFTER_FINETUNE: defer<FineTuneValidator>(call, props.mPitchShifter.lFineTune); break; + case EAXPITCHSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props); break; + case EAXPITCHSHIFTER_COARSETUNE: defer<CoarseTuneValidator>(call, props.lCoarseTune); break; + case EAXPITCHSHIFTER_FINETUNE: defer<FineTuneValidator>(call, props.lFineTune); break; default: fail_unknown_property_id(); } } diff --git a/al/effects/reverb.cpp b/al/effects/reverb.cpp index 440d7b4e..b037443f 100644 --- a/al/effects/reverb.cpp +++ b/al/effects/reverb.cpp @@ -945,7 +945,7 @@ struct EnvironmentSizeDeferrer2 { if ((props.dwFlags & EAX2LISTENERFLAGS_DECAYTIMESCALE) != 0) { - props.flDecayTime = clamp( + props.flDecayTime = std::clamp( props.flDecayTime * scale, EAXREVERB_MINDECAYTIME, EAXREVERB_MAXDECAYTIME); @@ -954,7 +954,7 @@ struct EnvironmentSizeDeferrer2 { if ((props.dwFlags & EAX2LISTENERFLAGS_REFLECTIONSSCALE) != 0 && (props.dwFlags & EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE) != 0) { - props.lReflections = clamp( + props.lReflections = std::clamp( props.lReflections - static_cast<long>(gain_to_level_mb(scale)), EAXREVERB_MINREFLECTIONS, EAXREVERB_MAXREFLECTIONS); @@ -962,7 +962,7 @@ struct EnvironmentSizeDeferrer2 { if ((props.dwFlags & EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE) != 0) { - props.flReflectionsDelay = clamp( + props.flReflectionsDelay = std::clamp( props.flReflectionsDelay * scale, EAXREVERB_MINREFLECTIONSDELAY, EAXREVERB_MAXREFLECTIONSDELAY); @@ -972,7 +972,7 @@ struct EnvironmentSizeDeferrer2 { { const auto log_scalar = ((props.dwFlags & EAXREVERBFLAGS_DECAYTIMESCALE) != 0) ? 2'000.0F : 3'000.0F; - props.lReverb = clamp( + props.lReverb = std::clamp( props.lReverb - static_cast<long>(std::log10(scale) * log_scalar), EAXREVERB_MINREVERB, EAXREVERB_MAXREVERB); @@ -980,7 +980,7 @@ struct EnvironmentSizeDeferrer2 { if ((props.dwFlags & EAX2LISTENERFLAGS_REVERBDELAYSCALE) != 0) { - props.flReverbDelay = clamp( + props.flReverbDelay = std::clamp( props.flReverbDelay * scale, EAXREVERB_MINREVERBDELAY, EAXREVERB_MAXREVERBDELAY); @@ -1015,7 +1015,7 @@ struct EnvironmentSizeDeferrer3 { if ((props.ulFlags & EAXREVERBFLAGS_DECAYTIMESCALE) != 0) { - props.flDecayTime = clamp( + props.flDecayTime = std::clamp( props.flDecayTime * scale, EAXREVERB_MINDECAYTIME, EAXREVERB_MAXDECAYTIME); @@ -1024,7 +1024,7 @@ struct EnvironmentSizeDeferrer3 { if ((props.ulFlags & EAXREVERBFLAGS_REFLECTIONSSCALE) != 0 && (props.ulFlags & EAXREVERBFLAGS_REFLECTIONSDELAYSCALE) != 0) { - props.lReflections = clamp( + props.lReflections = std::clamp( props.lReflections - static_cast<long>(gain_to_level_mb(scale)), EAXREVERB_MINREFLECTIONS, EAXREVERB_MAXREFLECTIONS); @@ -1032,7 +1032,7 @@ struct EnvironmentSizeDeferrer3 { if ((props.ulFlags & EAXREVERBFLAGS_REFLECTIONSDELAYSCALE) != 0) { - props.flReflectionsDelay = clamp( + props.flReflectionsDelay = std::clamp( props.flReflectionsDelay * scale, EAXREVERB_MINREFLECTIONSDELAY, EAXREVERB_MAXREFLECTIONSDELAY); @@ -1041,7 +1041,7 @@ struct EnvironmentSizeDeferrer3 { if ((props.ulFlags & EAXREVERBFLAGS_REVERBSCALE) != 0) { const auto log_scalar = ((props.ulFlags & EAXREVERBFLAGS_DECAYTIMESCALE) != 0) ? 2'000.0F : 3'000.0F; - props.lReverb = clamp( + props.lReverb = std::clamp( props.lReverb - static_cast<long>(std::log10(scale) * log_scalar), EAXREVERB_MINREVERB, EAXREVERB_MAXREVERB); @@ -1049,7 +1049,7 @@ struct EnvironmentSizeDeferrer3 { if ((props.ulFlags & EAXREVERBFLAGS_REVERBDELAYSCALE) != 0) { - props.flReverbDelay = clamp( + props.flReverbDelay = std::clamp( props.flReverbDelay * scale, EAXREVERB_MINREVERBDELAY, EAXREVERB_MAXREVERBDELAY); @@ -1057,7 +1057,7 @@ struct EnvironmentSizeDeferrer3 { if ((props.ulFlags & EAXREVERBFLAGS_ECHOTIMESCALE) != 0) { - props.flEchoTime = clamp( + props.flEchoTime = std::clamp( props.flEchoTime * scale, EAXREVERB_MINECHOTIME, EAXREVERB_MAXECHOTIME); @@ -1065,7 +1065,7 @@ struct EnvironmentSizeDeferrer3 { if ((props.ulFlags & EAXREVERBFLAGS_MODULATIONTIMESCALE) != 0) { - props.flModulationTime = clamp( + props.flModulationTime = std::clamp( props.flModulationTime * scale, EAXREVERB_MINMODULATIONTIME, EAXREVERB_MAXMODULATIONTIME); @@ -1089,48 +1089,35 @@ struct EaxReverbCommitter::Exception : public EaxReverbEffectException void EaxReverbCommitter::translate(const EAX_REVERBPROPERTIES& src, EaxEffectProps& dst) noexcept { assert(src.environment <= EAX1REVERB_MAXENVIRONMENT); - dst.mType = EaxEffectType::Reverb; - dst.mReverb = EAXREVERB_PRESETS[src.environment]; - dst.mReverb.flDecayTime = src.fDecayTime_sec; - dst.mReverb.flDecayHFRatio = src.fDamping; - dst.mReverb.lReverb = mini(static_cast<int>(gain_to_level_mb(src.fVolume)), 0); + auto&& eaxprops = dst.emplace<EAXREVERBPROPERTIES>(EAXREVERB_PRESETS[src.environment]); + eaxprops.flDecayTime = src.fDecayTime_sec; + eaxprops.flDecayHFRatio = src.fDamping; + eaxprops.lReverb = mini(static_cast<int>(gain_to_level_mb(src.fVolume)), 0); } void EaxReverbCommitter::translate(const EAX20LISTENERPROPERTIES& src, EaxEffectProps& dst) noexcept { assert(src.dwEnvironment <= EAX1REVERB_MAXENVIRONMENT); - const auto& env = EAXREVERB_PRESETS[src.dwEnvironment]; - dst.mType = EaxEffectType::Reverb; - dst.mReverb.ulEnvironment = src.dwEnvironment; - dst.mReverb.flEnvironmentSize = src.flEnvironmentSize; - dst.mReverb.flEnvironmentDiffusion = src.flEnvironmentDiffusion; - dst.mReverb.lRoom = src.lRoom; - dst.mReverb.lRoomHF = src.lRoomHF; - dst.mReverb.lRoomLF = env.lRoomLF; - dst.mReverb.flDecayTime = src.flDecayTime; - dst.mReverb.flDecayHFRatio = src.flDecayHFRatio; - dst.mReverb.flDecayLFRatio = env.flDecayLFRatio; - dst.mReverb.lReflections = src.lReflections; - dst.mReverb.flReflectionsDelay = src.flReflectionsDelay; - dst.mReverb.vReflectionsPan = env.vReflectionsPan; - dst.mReverb.lReverb = src.lReverb; - dst.mReverb.flReverbDelay = src.flReverbDelay; - dst.mReverb.vReverbPan = env.vReverbPan; - dst.mReverb.flEchoTime = env.flEchoTime; - dst.mReverb.flEchoDepth = env.flEchoDepth; - dst.mReverb.flModulationTime = env.flModulationTime; - dst.mReverb.flModulationDepth = env.flModulationDepth; - dst.mReverb.flAirAbsorptionHF = src.flAirAbsorptionHF; - dst.mReverb.flHFReference = env.flHFReference; - dst.mReverb.flLFReference = env.flLFReference; - dst.mReverb.flRoomRolloffFactor = src.flRoomRolloffFactor; - dst.mReverb.ulFlags = src.dwFlags; + auto&& eaxprops = dst.emplace<EAXREVERBPROPERTIES>(EAXREVERB_PRESETS[src.dwEnvironment]); + eaxprops.ulEnvironment = src.dwEnvironment; + eaxprops.flEnvironmentSize = src.flEnvironmentSize; + eaxprops.flEnvironmentDiffusion = src.flEnvironmentDiffusion; + eaxprops.lRoom = src.lRoom; + eaxprops.lRoomHF = src.lRoomHF; + eaxprops.flDecayTime = src.flDecayTime; + eaxprops.flDecayHFRatio = src.flDecayHFRatio; + eaxprops.lReflections = src.lReflections; + eaxprops.flReflectionsDelay = src.flReflectionsDelay; + eaxprops.lReverb = src.lReverb; + eaxprops.flReverbDelay = src.flReverbDelay; + eaxprops.flAirAbsorptionHF = src.flAirAbsorptionHF; + eaxprops.flRoomRolloffFactor = src.flRoomRolloffFactor; + eaxprops.ulFlags = src.dwFlags; } void EaxReverbCommitter::translate(const EAXREVERBPROPERTIES& src, EaxEffectProps& dst) noexcept { - dst.mType = EaxEffectType::Reverb; - dst.mReverb = src; + dst = src; } bool EaxReverbCommitter::commit(const EAX_REVERBPROPERTIES &props) @@ -1156,41 +1143,41 @@ bool EaxReverbCommitter::commit(const EAXREVERBPROPERTIES &props) bool EaxReverbCommitter::commit(const EaxEffectProps &props) { - if(props.mType == mEaxProps.mType - && memcmp(&props.mReverb, &mEaxProps.mReverb, sizeof(mEaxProps.mReverb)) == 0) + if(props == mEaxProps) return false; mEaxProps = props; - const auto size = props.mReverb.flEnvironmentSize; + auto &eaxprops = std::get<EAXREVERBPROPERTIES>(props); + const auto size = eaxprops.flEnvironmentSize; const auto density = (size * size * size) / 16.0F; mAlProps.Reverb.Density = std::min(density, AL_EAXREVERB_MAX_DENSITY); - mAlProps.Reverb.Diffusion = props.mReverb.flEnvironmentDiffusion; - mAlProps.Reverb.Gain = level_mb_to_gain(static_cast<float>(props.mReverb.lRoom)); - mAlProps.Reverb.GainHF = level_mb_to_gain(static_cast<float>(props.mReverb.lRoomHF)); - mAlProps.Reverb.GainLF = level_mb_to_gain(static_cast<float>(props.mReverb.lRoomLF)); - mAlProps.Reverb.DecayTime = props.mReverb.flDecayTime; - mAlProps.Reverb.DecayHFRatio = props.mReverb.flDecayHFRatio; - mAlProps.Reverb.DecayLFRatio = mEaxProps.mReverb.flDecayLFRatio; - mAlProps.Reverb.ReflectionsGain = level_mb_to_gain(static_cast<float>(props.mReverb.lReflections)); - mAlProps.Reverb.ReflectionsDelay = props.mReverb.flReflectionsDelay; - mAlProps.Reverb.ReflectionsPan[0] = props.mReverb.vReflectionsPan.x; - mAlProps.Reverb.ReflectionsPan[1] = props.mReverb.vReflectionsPan.y; - mAlProps.Reverb.ReflectionsPan[2] = props.mReverb.vReflectionsPan.z; - mAlProps.Reverb.LateReverbGain = level_mb_to_gain(static_cast<float>(props.mReverb.lReverb)); - mAlProps.Reverb.LateReverbDelay = props.mReverb.flReverbDelay; - mAlProps.Reverb.LateReverbPan[0] = props.mReverb.vReverbPan.x; - mAlProps.Reverb.LateReverbPan[1] = props.mReverb.vReverbPan.y; - mAlProps.Reverb.LateReverbPan[2] = props.mReverb.vReverbPan.z; - mAlProps.Reverb.EchoTime = props.mReverb.flEchoTime; - mAlProps.Reverb.EchoDepth = props.mReverb.flEchoDepth; - mAlProps.Reverb.ModulationTime = props.mReverb.flModulationTime; - mAlProps.Reverb.ModulationDepth = props.mReverb.flModulationDepth; - mAlProps.Reverb.AirAbsorptionGainHF = level_mb_to_gain(props.mReverb.flAirAbsorptionHF); - mAlProps.Reverb.HFReference = props.mReverb.flHFReference; - mAlProps.Reverb.LFReference = props.mReverb.flLFReference; - mAlProps.Reverb.RoomRolloffFactor = props.mReverb.flRoomRolloffFactor; - mAlProps.Reverb.DecayHFLimit = ((props.mReverb.ulFlags & EAXREVERBFLAGS_DECAYHFLIMIT) != 0); + mAlProps.Reverb.Diffusion = eaxprops.flEnvironmentDiffusion; + mAlProps.Reverb.Gain = level_mb_to_gain(static_cast<float>(eaxprops.lRoom)); + mAlProps.Reverb.GainHF = level_mb_to_gain(static_cast<float>(eaxprops.lRoomHF)); + mAlProps.Reverb.GainLF = level_mb_to_gain(static_cast<float>(eaxprops.lRoomLF)); + mAlProps.Reverb.DecayTime = eaxprops.flDecayTime; + mAlProps.Reverb.DecayHFRatio = eaxprops.flDecayHFRatio; + mAlProps.Reverb.DecayLFRatio = eaxprops.flDecayLFRatio; + mAlProps.Reverb.ReflectionsGain = level_mb_to_gain(static_cast<float>(eaxprops.lReflections)); + mAlProps.Reverb.ReflectionsDelay = eaxprops.flReflectionsDelay; + mAlProps.Reverb.ReflectionsPan[0] = eaxprops.vReflectionsPan.x; + mAlProps.Reverb.ReflectionsPan[1] = eaxprops.vReflectionsPan.y; + mAlProps.Reverb.ReflectionsPan[2] = eaxprops.vReflectionsPan.z; + mAlProps.Reverb.LateReverbGain = level_mb_to_gain(static_cast<float>(eaxprops.lReverb)); + mAlProps.Reverb.LateReverbDelay = eaxprops.flReverbDelay; + mAlProps.Reverb.LateReverbPan[0] = eaxprops.vReverbPan.x; + mAlProps.Reverb.LateReverbPan[1] = eaxprops.vReverbPan.y; + mAlProps.Reverb.LateReverbPan[2] = eaxprops.vReverbPan.z; + mAlProps.Reverb.EchoTime = eaxprops.flEchoTime; + mAlProps.Reverb.EchoDepth = eaxprops.flEchoDepth; + mAlProps.Reverb.ModulationTime = eaxprops.flModulationTime; + mAlProps.Reverb.ModulationDepth = eaxprops.flModulationDepth; + mAlProps.Reverb.AirAbsorptionGainHF = level_mb_to_gain(eaxprops.flAirAbsorptionHF); + mAlProps.Reverb.HFReference = eaxprops.flHFReference; + mAlProps.Reverb.LFReference = eaxprops.flLFReference; + mAlProps.Reverb.RoomRolloffFactor = eaxprops.flRoomRolloffFactor; + mAlProps.Reverb.DecayHFLimit = ((eaxprops.ulFlags & EAXREVERBFLAGS_DECAYHFLIMIT) != 0); return true; } @@ -1212,8 +1199,7 @@ void EaxReverbCommitter::SetDefaults(EAXREVERBPROPERTIES &props) void EaxReverbCommitter::SetDefaults(EaxEffectProps &props) { - props.mType = EaxEffectType::Reverb; - SetDefaults(props.mReverb); + SetDefaults(props.emplace<EAXREVERBPROPERTIES>()); } @@ -1290,7 +1276,7 @@ void EaxReverbCommitter::Get(const EaxCall &call, const EAXREVERBPROPERTIES &pro void EaxReverbCommitter::Get(const EaxCall &call, const EaxEffectProps &props) { - Get(call, props.mReverb); + Get(call, std::get<EAXREVERBPROPERTIES>(props)); } @@ -1493,7 +1479,7 @@ void EaxReverbCommitter::Set(const EaxCall &call, EAXREVERBPROPERTIES &props) void EaxReverbCommitter::Set(const EaxCall &call, EaxEffectProps &props) { - Set(call, props.mReverb); + Set(call, std::get<EAXREVERBPROPERTIES>(props)); } #endif // ALSOFT_EAX diff --git a/al/effects/vmorpher.cpp b/al/effects/vmorpher.cpp index 21ea3680..f2551229 100644 --- a/al/effects/vmorpher.cpp +++ b/al/effects/vmorpher.cpp @@ -1,13 +1,13 @@ #include "config.h" +#include <optional> #include <stdexcept> #include "AL/al.h" #include "AL/efx.h" #include "alc/effects/base.h" -#include "aloptional.h" #include "effects.h" #ifdef ALSOFT_EAX @@ -20,7 +20,7 @@ namespace { -al::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val) +std::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val) { #define HANDLE_PHENOME(x) case AL_VOCAL_MORPHER_PHONEME_ ## x: \ return VMorpherPhenome::x @@ -57,7 +57,7 @@ al::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val) HANDLE_PHENOME(V); HANDLE_PHENOME(Z); } - return al::nullopt; + return std::nullopt; #undef HANDLE_PHENOME } ALenum EnumFromPhenome(VMorpherPhenome phenome) @@ -100,7 +100,7 @@ ALenum EnumFromPhenome(VMorpherPhenome phenome) #undef HANDLE_PHENOME } -al::optional<VMorpherWaveform> WaveformFromEmum(ALenum value) +std::optional<VMorpherWaveform> WaveformFromEmum(ALenum value) { switch(value) { @@ -108,7 +108,7 @@ al::optional<VMorpherWaveform> WaveformFromEmum(ALenum value) case AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE: return VMorpherWaveform::Triangle; case AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH: return VMorpherWaveform::Sawtooth; } - return al::nullopt; + return std::nullopt; } ALenum EnumFromWaveform(VMorpherWaveform type) { @@ -355,13 +355,7 @@ template<> template<> bool VocalMorpherCommitter::commit(const EaxEffectProps &props) { - if(props.mType == mEaxProps.mType - && mEaxProps.mVocalMorpher.ulPhonemeA == props.mVocalMorpher.ulPhonemeA - && mEaxProps.mVocalMorpher.lPhonemeACoarseTuning == props.mVocalMorpher.lPhonemeACoarseTuning - && mEaxProps.mVocalMorpher.ulPhonemeB == props.mVocalMorpher.ulPhonemeB - && mEaxProps.mVocalMorpher.lPhonemeBCoarseTuning == props.mVocalMorpher.lPhonemeBCoarseTuning - && mEaxProps.mVocalMorpher.ulWaveform == props.mVocalMorpher.ulWaveform - && mEaxProps.mVocalMorpher.flRate == props.mVocalMorpher.flRate) + if(props == mEaxProps) return false; mEaxProps = props; @@ -413,12 +407,13 @@ bool VocalMorpherCommitter::commit(const EaxEffectProps &props) return VMorpherWaveform::Sinusoid; }; - mAlProps.Vmorpher.PhonemeA = get_phoneme(props.mVocalMorpher.ulPhonemeA); - mAlProps.Vmorpher.PhonemeACoarseTuning = static_cast<int>(props.mVocalMorpher.lPhonemeACoarseTuning); - mAlProps.Vmorpher.PhonemeB = get_phoneme(props.mVocalMorpher.ulPhonemeB); - mAlProps.Vmorpher.PhonemeBCoarseTuning = static_cast<int>(props.mVocalMorpher.lPhonemeBCoarseTuning); - mAlProps.Vmorpher.Waveform = get_waveform(props.mVocalMorpher.ulWaveform); - mAlProps.Vmorpher.Rate = props.mVocalMorpher.flRate; + auto &eaxprops = std::get<EAXVOCALMORPHERPROPERTIES>(props); + mAlProps.Vmorpher.PhonemeA = get_phoneme(eaxprops.ulPhonemeA); + mAlProps.Vmorpher.PhonemeACoarseTuning = static_cast<int>(eaxprops.lPhonemeACoarseTuning); + mAlProps.Vmorpher.PhonemeB = get_phoneme(eaxprops.ulPhonemeB); + mAlProps.Vmorpher.PhonemeBCoarseTuning = static_cast<int>(eaxprops.lPhonemeBCoarseTuning); + mAlProps.Vmorpher.Waveform = get_waveform(eaxprops.ulWaveform); + mAlProps.Vmorpher.Rate = eaxprops.flRate; return true; } @@ -426,49 +421,55 @@ bool VocalMorpherCommitter::commit(const EaxEffectProps &props) template<> void VocalMorpherCommitter::SetDefaults(EaxEffectProps &props) { - props.mType = EaxEffectType::VocalMorpher; - props.mVocalMorpher.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA; - props.mVocalMorpher.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING; - props.mVocalMorpher.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB; - props.mVocalMorpher.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING; - props.mVocalMorpher.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM; - props.mVocalMorpher.flRate = EAXVOCALMORPHER_DEFAULTRATE; + static constexpr EAXVOCALMORPHERPROPERTIES defprops{[] + { + EAXVOCALMORPHERPROPERTIES ret{}; + ret.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA; + ret.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING; + ret.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB; + ret.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING; + ret.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM; + ret.flRate = EAXVOCALMORPHER_DEFAULTRATE; + return ret; + }()}; + props = defprops; } template<> -void VocalMorpherCommitter::Get(const EaxCall &call, const EaxEffectProps &props) +void VocalMorpherCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) { + auto &props = std::get<EAXVOCALMORPHERPROPERTIES>(props_); switch(call.get_property_id()) { case EAXVOCALMORPHER_NONE: break; case EAXVOCALMORPHER_ALLPARAMETERS: - call.set_value<Exception>(props.mVocalMorpher); + call.set_value<Exception>(props); break; case EAXVOCALMORPHER_PHONEMEA: - call.set_value<Exception>(props.mVocalMorpher.ulPhonemeA); + call.set_value<Exception>(props.ulPhonemeA); break; case EAXVOCALMORPHER_PHONEMEACOARSETUNING: - call.set_value<Exception>(props.mVocalMorpher.lPhonemeACoarseTuning); + call.set_value<Exception>(props.lPhonemeACoarseTuning); break; case EAXVOCALMORPHER_PHONEMEB: - call.set_value<Exception>(props.mVocalMorpher.ulPhonemeB); + call.set_value<Exception>(props.ulPhonemeB); break; case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: - call.set_value<Exception>(props.mVocalMorpher.lPhonemeBCoarseTuning); + call.set_value<Exception>(props.lPhonemeBCoarseTuning); break; case EAXVOCALMORPHER_WAVEFORM: - call.set_value<Exception>(props.mVocalMorpher.ulWaveform); + call.set_value<Exception>(props.ulWaveform); break; case EAXVOCALMORPHER_RATE: - call.set_value<Exception>(props.mVocalMorpher.flRate); + call.set_value<Exception>(props.flRate); break; default: @@ -477,39 +478,40 @@ void VocalMorpherCommitter::Get(const EaxCall &call, const EaxEffectProps &props } template<> -void VocalMorpherCommitter::Set(const EaxCall &call, EaxEffectProps &props) +void VocalMorpherCommitter::Set(const EaxCall &call, EaxEffectProps &props_) { + auto &props = std::get<EAXVOCALMORPHERPROPERTIES>(props_); switch(call.get_property_id()) { case EAXVOCALMORPHER_NONE: break; case EAXVOCALMORPHER_ALLPARAMETERS: - defer<AllValidator>(call, props.mVocalMorpher); + defer<AllValidator>(call, props); break; case EAXVOCALMORPHER_PHONEMEA: - defer<PhonemeAValidator>(call, props.mVocalMorpher.ulPhonemeA); + defer<PhonemeAValidator>(call, props.ulPhonemeA); break; case EAXVOCALMORPHER_PHONEMEACOARSETUNING: - defer<PhonemeACoarseTuningValidator>(call, props.mVocalMorpher.lPhonemeACoarseTuning); + defer<PhonemeACoarseTuningValidator>(call, props.lPhonemeACoarseTuning); break; case EAXVOCALMORPHER_PHONEMEB: - defer<PhonemeBValidator>(call, props.mVocalMorpher.ulPhonemeB); + defer<PhonemeBValidator>(call, props.ulPhonemeB); break; case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: - defer<PhonemeBCoarseTuningValidator>(call, props.mVocalMorpher.lPhonemeBCoarseTuning); + defer<PhonemeBCoarseTuningValidator>(call, props.lPhonemeBCoarseTuning); break; case EAXVOCALMORPHER_WAVEFORM: - defer<WaveformValidator>(call, props.mVocalMorpher.ulWaveform); + defer<WaveformValidator>(call, props.ulWaveform); break; case EAXVOCALMORPHER_RATE: - defer<RateValidator>(call, props.mVocalMorpher.flRate); + defer<RateValidator>(call, props.flRate); break; default: diff --git a/al/error.cpp b/al/error.cpp index afa7019a..c2359477 100644 --- a/al/error.cpp +++ b/al/error.cpp @@ -29,25 +29,32 @@ #include <csignal> #include <cstdarg> #include <cstdio> +#include <cstdlib> #include <cstring> +#include <limits> #include <mutex> +#include <vector> #include "AL/al.h" #include "AL/alc.h" +#include "al/debug.h" +#include "alc/alconfig.h" #include "alc/context.h" #include "almalloc.h" +#include "alstring.h" #include "core/except.h" #include "core/logging.h" +#include "direct_defs.h" #include "opthelpers.h" -#include "vector.h" +#include "strutils.h" bool TrapALError{false}; void ALCcontext::setError(ALenum errorCode, const char *msg, ...) { - auto message = al::vector<char>(256); + auto message = std::vector<char>(256); va_list args, args2; va_start(args, msg); @@ -61,8 +68,13 @@ void ALCcontext::setError(ALenum errorCode, const char *msg, ...) va_end(args2); va_end(args); - if(msglen >= 0) msg = message.data(); - else msg = "<internal error constructing message>"; + if(msglen >= 0) + msg = message.data(); + else + { + msg = "<internal error constructing message>"; + msglen = static_cast<int>(strlen(msg)); + } WARN("Error generated on context %p, code 0x%04x, \"%s\"\n", decltype(std::declval<void*>()){this}, errorCode, msg); @@ -79,15 +91,36 @@ void ALCcontext::setError(ALenum errorCode, const char *msg, ...) ALenum curerr{AL_NO_ERROR}; mLastError.compare_exchange_strong(curerr, errorCode); + + debugMessage(DebugSource::API, DebugType::Error, 0, DebugSeverity::High, + {msg, static_cast<uint>(msglen)}); } -AL_API ALenum AL_APIENTRY alGetError(void) -START_API_FUNC +/* Special-case alGetError since it (potentially) raises a debug signal and + * returns a non-default value for a null context. + */ +AL_API ALenum AL_APIENTRY alGetError(void) noexcept { - ContextRef context{GetContextRef()}; + auto context = GetContextRef(); if(!context) UNLIKELY { - static constexpr ALenum deferror{AL_INVALID_OPERATION}; + static const ALenum deferror{[](const char *envname, const char *optname) -> ALenum + { + auto optstr = al::getenv(envname); + if(!optstr) + optstr = ConfigValueStr(nullptr, "game_compat", optname); + + if(optstr) + { + char *end{}; + auto value = std::strtoul(optstr->c_str(), &end, 0); + if(end && *end == '\0' && value <= std::numeric_limits<ALenum>::max()) + return static_cast<ALenum>(value); + ERR("Invalid default error value: \"%s\"", optstr->c_str()); + } + return AL_INVALID_OPERATION; + }("__ALSOFT_DEFAULT_ERROR", "default-error")}; + WARN("Querying error state on null context (implicitly 0x%04x)\n", deferror); if(TrapALError) { @@ -100,7 +133,10 @@ START_API_FUNC } return deferror; } + return alGetErrorDirect(context.get()); +} +FORCE_ALIGN ALenum AL_APIENTRY alGetErrorDirect(ALCcontext *context) noexcept +{ return context->mLastError.exchange(AL_NO_ERROR); } -END_API_FUNC diff --git a/al/event.cpp b/al/event.cpp index 1bc39d1e..8b76ceff 100644 --- a/al/event.cpp +++ b/al/event.cpp @@ -10,14 +10,15 @@ #include <memory> #include <mutex> #include <new> +#include <optional> #include <string> +#include <string_view> #include <thread> #include <utility> #include "AL/al.h" #include "AL/alc.h" -#include "albyte.h" #include "alc/context.h" #include "alc/effects/base.h" #include "alc/inprogext.h" @@ -26,12 +27,21 @@ #include "core/except.h" #include "core/logging.h" #include "core/voice_change.h" +#include "debug.h" +#include "direct_defs.h" #include "opthelpers.h" #include "ringbuffer.h" -#include "threads.h" -static int EventThread(ALCcontext *context) +namespace { + +template<typename... Ts> +struct overloaded : Ts... { using Ts::operator()...; }; + +template<typename... Ts> +overloaded(Ts...) -> overloaded<Ts...>; + +int EventThread(ALCcontext *context) { RingBuffer *ring{context->mAsyncEvents.get()}; bool quitnow{false}; @@ -46,74 +56,101 @@ static int EventThread(ALCcontext *context) std::lock_guard<std::mutex> _{context->mEventCbLock}; do { - auto *evt_ptr = reinterpret_cast<AsyncEvent*>(evt_data.buf); + auto *evt_ptr = std::launder(reinterpret_cast<AsyncEvent*>(evt_data.buf)); evt_data.buf += sizeof(AsyncEvent); evt_data.len -= 1; - AsyncEvent evt{*evt_ptr}; - al::destroy_at(evt_ptr); + AsyncEvent event{std::move(*evt_ptr)}; + std::destroy_at(evt_ptr); ring->readAdvance(1); - quitnow = evt.EnumType == AsyncEvent::KillThread; + quitnow = std::holds_alternative<AsyncKillThread>(event); if(quitnow) UNLIKELY break; - if(evt.EnumType == AsyncEvent::ReleaseEffectState) - { - al::intrusive_ptr<EffectState>{evt.u.mEffectState}; - continue; - } - auto enabledevts = context->mEnabledEvts.load(std::memory_order_acquire); - if(!context->mEventCb || !enabledevts.test(evt.EnumType)) - continue; - - if(evt.EnumType == AsyncEvent::SourceStateChange) + auto proc_killthread = [](AsyncKillThread&) { }; + auto proc_release = [](AsyncEffectReleaseEvent &evt) + { + al::intrusive_ptr<EffectState>{evt.mEffectState}; + }; + auto proc_srcstate = [context,enabledevts](AsyncSourceStateEvent &evt) { + if(!context->mEventCb + || !enabledevts.test(al::to_underlying(AsyncEnableBits::SourceState))) + return; + ALuint state{}; - std::string msg{"Source ID " + std::to_string(evt.u.srcstate.id)}; + std::string msg{"Source ID " + std::to_string(evt.mId)}; msg += " state has changed to "; - switch(evt.u.srcstate.state) + switch(evt.mState) { - case AsyncEvent::SrcState::Reset: + case AsyncSrcState::Reset: msg += "AL_INITIAL"; state = AL_INITIAL; break; - case AsyncEvent::SrcState::Stop: + case AsyncSrcState::Stop: msg += "AL_STOPPED"; state = AL_STOPPED; break; - case AsyncEvent::SrcState::Play: + case AsyncSrcState::Play: msg += "AL_PLAYING"; state = AL_PLAYING; break; - case AsyncEvent::SrcState::Pause: + case AsyncSrcState::Pause: msg += "AL_PAUSED"; state = AL_PAUSED; break; } - context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.u.srcstate.id, - state, static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam); - } - else if(evt.EnumType == AsyncEvent::BufferCompleted) + context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.mId, state, + static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam); + }; + auto proc_buffercomp = [context,enabledevts](AsyncBufferCompleteEvent &evt) { - std::string msg{std::to_string(evt.u.bufcomp.count)}; - if(evt.u.bufcomp.count == 1) msg += " buffer completed"; + if(!context->mEventCb + || !enabledevts.test(al::to_underlying(AsyncEnableBits::BufferCompleted))) + return; + + std::string msg{std::to_string(evt.mCount)}; + if(evt.mCount == 1) msg += " buffer completed"; else msg += " buffers completed"; - context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.u.bufcomp.id, - evt.u.bufcomp.count, static_cast<ALsizei>(msg.length()), msg.c_str(), - context->mEventParam); - } - else if(evt.EnumType == AsyncEvent::Disconnected) + context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.mId, evt.mCount, + static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam); + }; + auto proc_disconnect = [context,enabledevts](AsyncDisconnectEvent &evt) { - context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0, - static_cast<ALsizei>(strlen(evt.u.disconnect.msg)), evt.u.disconnect.msg, - context->mEventParam); - } + const std::string_view message{evt.msg}; + + context->debugMessage(DebugSource::System, DebugType::Error, 0, + DebugSeverity::High, message); + + if(context->mEventCb + && enabledevts.test(al::to_underlying(AsyncEnableBits::Disconnected))) + context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0, + static_cast<ALsizei>(message.length()), message.data(), + context->mEventParam); + }; + + std::visit(overloaded{proc_srcstate, proc_buffercomp, proc_release, proc_disconnect, + proc_killthread}, event); } while(evt_data.len != 0); } return 0; } +constexpr std::optional<AsyncEnableBits> GetEventType(ALenum etype) noexcept +{ + switch(etype) + { + case AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT: return AsyncEnableBits::BufferCompleted; + case AL_EVENT_TYPE_DISCONNECTED_SOFT: return AsyncEnableBits::Disconnected; + case AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT: return AsyncEnableBits::SourceState; + } + return std::nullopt; +} + +} // namespace + + void StartEventThrd(ALCcontext *ctx) { try { @@ -138,7 +175,7 @@ void StopEventThrd(ALCcontext *ctx) evt_data = ring->getWriteVector().first; } while(evt_data.len == 0); } - al::construct_at(reinterpret_cast<AsyncEvent*>(evt_data.buf), AsyncEvent::KillThread); + std::ignore = InitAsyncEvent<AsyncKillThread>(evt_data.buf); ring->writeAdvance(1); ctx->mEventSem.post(); @@ -146,34 +183,22 @@ void StopEventThrd(ALCcontext *ctx) ctx->mEventThread.join(); } -AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable) -START_API_FUNC +AL_API DECL_FUNCEXT3(void, alEventControl,SOFT, ALsizei, const ALenum*, ALboolean) +FORCE_ALIGN void AL_APIENTRY alEventControlDirectSOFT(ALCcontext *context, ALsizei count, + const ALenum *types, ALboolean enable) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(count < 0) context->setError(AL_INVALID_VALUE, "Controlling %d events", count); if(count <= 0) return; if(!types) return context->setError(AL_INVALID_VALUE, "NULL pointer"); ContextBase::AsyncEventBitset flags{}; - const ALenum *types_end = types+count; - auto bad_type = std::find_if_not(types, types_end, - [&flags](ALenum type) noexcept -> bool - { - if(type == AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT) - flags.set(AsyncEvent::BufferCompleted); - else if(type == AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT) - flags.set(AsyncEvent::SourceStateChange); - else if(type == AL_EVENT_TYPE_DISCONNECTED_SOFT) - flags.set(AsyncEvent::Disconnected); - else - return false; - return true; - } - ); - if(bad_type != types_end) - return context->setError(AL_INVALID_ENUM, "Invalid event type 0x%04x", *bad_type); + for(ALenum evttype : al::span{types, static_cast<uint>(count)}) + { + auto etype = GetEventType(evttype); + if(!etype) + return context->setError(AL_INVALID_ENUM, "Invalid event type 0x%04x", evttype); + flags.set(al::to_underlying(*etype)); + } if(enable) { @@ -199,17 +224,12 @@ START_API_FUNC std::lock_guard<std::mutex> _{context->mEventCbLock}; } } -END_API_FUNC -AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam) -START_API_FUNC +AL_API DECL_FUNCEXT2(void, alEventCallback,SOFT, ALEVENTPROCSOFT, void*) +FORCE_ALIGN void AL_APIENTRY alEventCallbackDirectSOFT(ALCcontext *context, + ALEVENTPROCSOFT callback, void *userParam) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - - std::lock_guard<std::mutex> _{context->mPropLock}; - std::lock_guard<std::mutex> __{context->mEventCbLock}; + std::lock_guard<std::mutex> _{context->mEventCbLock}; context->mEventCb = callback; context->mEventParam = userParam; } -END_API_FUNC diff --git a/al/extension.cpp b/al/extension.cpp index 3ead0af8..6d1ac327 100644 --- a/al/extension.cpp +++ b/al/extension.cpp @@ -30,15 +30,13 @@ #include "alc/context.h" #include "alstring.h" #include "core/except.h" +#include "direct_defs.h" #include "opthelpers.h" -AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extName) -START_API_FUNC +AL_API DECL_FUNC1(ALboolean, alIsExtensionPresent, const ALchar*) +FORCE_ALIGN ALboolean AL_APIENTRY alIsExtensionPresentDirect(ALCcontext *context, const ALchar *extName) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return AL_FALSE; - if(!extName) UNLIKELY { context->setError(AL_INVALID_VALUE, "NULL pointer"); @@ -46,37 +44,36 @@ START_API_FUNC } size_t len{strlen(extName)}; - const char *ptr{context->mExtensionList}; - while(ptr && *ptr) + for(std::string_view ext : context->mExtensions) { - if(al::strncasecmp(ptr, extName, len) == 0 && (ptr[len] == '\0' || isspace(ptr[len]))) + if(len == ext.length() && al::strncasecmp(ext.data(), extName, len) == 0) return AL_TRUE; - - if((ptr=strchr(ptr, ' ')) != nullptr) - { - do { - ++ptr; - } while(isspace(*ptr)); - } } return AL_FALSE; } -END_API_FUNC -AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName) -START_API_FUNC +AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName) noexcept +{ + if(!funcName) return nullptr; + return alcGetProcAddress(nullptr, funcName); +} + +FORCE_ALIGN ALvoid* AL_APIENTRY alGetProcAddressDirect(ALCcontext*, const ALchar *funcName) noexcept { if(!funcName) return nullptr; return alcGetProcAddress(nullptr, funcName); } -END_API_FUNC -AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName) -START_API_FUNC +AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName) noexcept +{ + if(!enumName) return static_cast<ALenum>(0); + return alcGetEnumValue(nullptr, enumName); +} + +FORCE_ALIGN ALenum AL_APIENTRY alGetEnumValueDirect(ALCcontext*, const ALchar *enumName) noexcept { if(!enumName) return static_cast<ALenum>(0); return alcGetEnumValue(nullptr, enumName); } -END_API_FUNC diff --git a/al/filter.cpp b/al/filter.cpp index 73efa01f..f0a078b7 100644 --- a/al/filter.cpp +++ b/al/filter.cpp @@ -31,6 +31,7 @@ #include <mutex> #include <new> #include <numeric> +#include <vector> #include "AL/al.h" #include "AL/alc.h" @@ -42,8 +43,8 @@ #include "almalloc.h" #include "alnumeric.h" #include "core/except.h" +#include "direct_defs.h" #include "opthelpers.h" -#include "vector.h" namespace { @@ -73,20 +74,157 @@ filter_exception::filter_exception(ALenum code, const char* msg, ...) : mErrorCo filter_exception::~filter_exception() = default; -#define DEFINE_ALFILTER_VTABLE(T) \ -const ALfilter::Vtable T##_vtable = { \ - T##_setParami, T##_setParamiv, T##_setParamf, T##_setParamfv, \ - T##_getParami, T##_getParamiv, T##_getParamf, T##_getParamfv, \ +void InitFilterParams(ALfilter *filter, ALenum type) +{ + if(type == AL_FILTER_LOWPASS) + { + filter->Gain = AL_LOWPASS_DEFAULT_GAIN; + filter->GainHF = AL_LOWPASS_DEFAULT_GAINHF; + filter->HFReference = LOWPASSFREQREF; + filter->GainLF = 1.0f; + filter->LFReference = HIGHPASSFREQREF; + filter->mTypeVariant.emplace<LowpassFilterTable>(); + } + else if(type == AL_FILTER_HIGHPASS) + { + filter->Gain = AL_HIGHPASS_DEFAULT_GAIN; + filter->GainHF = 1.0f; + filter->HFReference = LOWPASSFREQREF; + filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF; + filter->LFReference = HIGHPASSFREQREF; + filter->mTypeVariant.emplace<HighpassFilterTable>(); + } + else if(type == AL_FILTER_BANDPASS) + { + filter->Gain = AL_BANDPASS_DEFAULT_GAIN; + filter->GainHF = AL_BANDPASS_DEFAULT_GAINHF; + filter->HFReference = LOWPASSFREQREF; + filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF; + filter->LFReference = HIGHPASSFREQREF; + filter->mTypeVariant.emplace<BandpassFilterTable>(); + } + else + { + filter->Gain = 1.0f; + filter->GainHF = 1.0f; + filter->HFReference = LOWPASSFREQREF; + filter->GainLF = 1.0f; + filter->LFReference = HIGHPASSFREQREF; + filter->mTypeVariant.emplace<NullFilterTable>(); + } + filter->type = type; } -void ALlowpass_setParami(ALfilter*, ALenum param, int) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; } -void ALlowpass_setParamiv(ALfilter*, ALenum param, const int*) +bool EnsureFilters(ALCdevice *device, size_t needed) +{ + size_t count{std::accumulate(device->FilterList.cbegin(), device->FilterList.cend(), 0_uz, + [](size_t cur, const FilterSubList &sublist) noexcept -> size_t + { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })}; + + while(needed > count) + { + if(device->FilterList.size() >= 1<<25) UNLIKELY + return false; + + device->FilterList.emplace_back(); + auto sublist = device->FilterList.end() - 1; + sublist->FreeMask = ~0_u64; + sublist->Filters = static_cast<ALfilter*>(al_calloc(alignof(ALfilter), sizeof(ALfilter)*64)); + if(!sublist->Filters) UNLIKELY + { + device->FilterList.pop_back(); + return false; + } + count += 64; + } + return true; +} + + +ALfilter *AllocFilter(ALCdevice *device) +{ + auto sublist = std::find_if(device->FilterList.begin(), device->FilterList.end(), + [](const FilterSubList &entry) noexcept -> bool + { return entry.FreeMask != 0; }); + auto lidx = static_cast<ALuint>(std::distance(device->FilterList.begin(), sublist)); + auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask)); + ASSUME(slidx < 64); + + ALfilter *filter{al::construct_at(sublist->Filters + slidx)}; + InitFilterParams(filter, AL_FILTER_NULL); + + /* Add 1 to avoid filter ID 0. */ + filter->id = ((lidx<<6) | slidx) + 1; + + sublist->FreeMask &= ~(1_u64 << slidx); + + return filter; +} + +void FreeFilter(ALCdevice *device, ALfilter *filter) { - throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer-vector property 0x%04x", - param}; + device->mFilterNames.erase(filter->id); + + const ALuint id{filter->id - 1}; + const size_t lidx{id >> 6}; + const ALuint slidx{id & 0x3f}; + + std::destroy_at(filter); + + device->FilterList[lidx].FreeMask |= 1_u64 << slidx; } -void ALlowpass_setParamf(ALfilter *filter, ALenum param, float val) + + +inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) +{ + const size_t lidx{(id-1) >> 6}; + const ALuint slidx{(id-1) & 0x3f}; + + if(lidx >= device->FilterList.size()) UNLIKELY + return nullptr; + FilterSubList &sublist = device->FilterList[lidx]; + if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY + return nullptr; + return sublist.Filters + slidx; +} + +} // namespace + +/* Null filter parameter handlers */ +template<> +void FilterTable<NullFilterTable>::setParami(ALfilter*, ALenum param, int) +{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +template<> +void FilterTable<NullFilterTable>::setParamiv(ALfilter*, ALenum param, const int*) +{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +template<> +void FilterTable<NullFilterTable>::setParamf(ALfilter*, ALenum param, float) +{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +template<> +void FilterTable<NullFilterTable>::setParamfv(ALfilter*, ALenum param, const float*) +{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +template<> +void FilterTable<NullFilterTable>::getParami(const ALfilter*, ALenum param, int*) +{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +template<> +void FilterTable<NullFilterTable>::getParamiv(const ALfilter*, ALenum param, int*) +{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +template<> +void FilterTable<NullFilterTable>::getParamf(const ALfilter*, ALenum param, float*) +{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +template<> +void FilterTable<NullFilterTable>::getParamfv(const ALfilter*, ALenum param, float*) +{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } + +/* Lowpass parameter handlers */ +template<> +void FilterTable<LowpassFilterTable>::setParami(ALfilter*, ALenum param, int) +{ throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; } +template<> +void FilterTable<LowpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values) +{ setParami(filter, param, values[0]); } +template<> +void FilterTable<LowpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val) { switch(param) { @@ -106,17 +244,17 @@ void ALlowpass_setParamf(ALfilter *filter, ALenum param, float val) throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param}; } } -void ALlowpass_setParamfv(ALfilter *filter, ALenum param, const float *vals) -{ ALlowpass_setParamf(filter, param, vals[0]); } - -void ALlowpass_getParami(const ALfilter*, ALenum param, int*) +template<> +void FilterTable<LowpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals) +{ setParamf(filter, param, vals[0]); } +template<> +void FilterTable<LowpassFilterTable>::getParami(const ALfilter*, ALenum param, int*) { throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; } -void ALlowpass_getParamiv(const ALfilter*, ALenum param, int*) -{ - throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer-vector property 0x%04x", - param}; -} -void ALlowpass_getParamf(const ALfilter *filter, ALenum param, float *val) +template<> +void FilterTable<LowpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values) +{ getParami(filter, param, values); } +template<> +void FilterTable<LowpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val) { switch(param) { @@ -132,20 +270,19 @@ void ALlowpass_getParamf(const ALfilter *filter, ALenum param, float *val) throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param}; } } -void ALlowpass_getParamfv(const ALfilter *filter, ALenum param, float *vals) -{ ALlowpass_getParamf(filter, param, vals); } - -DEFINE_ALFILTER_VTABLE(ALlowpass); - +template<> +void FilterTable<LowpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals) +{ getParamf(filter, param, vals); } -void ALhighpass_setParami(ALfilter*, ALenum param, int) +/* Highpass parameter handlers */ +template<> +void FilterTable<HighpassFilterTable>::setParami(ALfilter*, ALenum param, int) { throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; } -void ALhighpass_setParamiv(ALfilter*, ALenum param, const int*) -{ - throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer-vector property 0x%04x", - param}; -} -void ALhighpass_setParamf(ALfilter *filter, ALenum param, float val) +template<> +void FilterTable<HighpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values) +{ setParami(filter, param, values[0]); } +template<> +void FilterTable<HighpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val) { switch(param) { @@ -165,17 +302,17 @@ void ALhighpass_setParamf(ALfilter *filter, ALenum param, float val) throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param}; } } -void ALhighpass_setParamfv(ALfilter *filter, ALenum param, const float *vals) -{ ALhighpass_setParamf(filter, param, vals[0]); } - -void ALhighpass_getParami(const ALfilter*, ALenum param, int*) +template<> +void FilterTable<HighpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals) +{ setParamf(filter, param, vals[0]); } +template<> +void FilterTable<HighpassFilterTable>::getParami(const ALfilter*, ALenum param, int*) { throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; } -void ALhighpass_getParamiv(const ALfilter*, ALenum param, int*) -{ - throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer-vector property 0x%04x", - param}; -} -void ALhighpass_getParamf(const ALfilter *filter, ALenum param, float *val) +template<> +void FilterTable<HighpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values) +{ getParami(filter, param, values); } +template<> +void FilterTable<HighpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val) { switch(param) { @@ -191,20 +328,19 @@ void ALhighpass_getParamf(const ALfilter *filter, ALenum param, float *val) throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param}; } } -void ALhighpass_getParamfv(const ALfilter *filter, ALenum param, float *vals) -{ ALhighpass_getParamf(filter, param, vals); } - -DEFINE_ALFILTER_VTABLE(ALhighpass); - +template<> +void FilterTable<HighpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals) +{ getParamf(filter, param, vals); } -void ALbandpass_setParami(ALfilter*, ALenum param, int) +/* Bandpass parameter handlers */ +template<> +void FilterTable<BandpassFilterTable>::setParami(ALfilter*, ALenum param, int) { throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; } -void ALbandpass_setParamiv(ALfilter*, ALenum param, const int*) -{ - throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer-vector property 0x%04x", - param}; -} -void ALbandpass_setParamf(ALfilter *filter, ALenum param, float val) +template<> +void FilterTable<BandpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values) +{ setParami(filter, param, values[0]); } +template<> +void FilterTable<BandpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val) { switch(param) { @@ -230,17 +366,17 @@ void ALbandpass_setParamf(ALfilter *filter, ALenum param, float val) throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param}; } } -void ALbandpass_setParamfv(ALfilter *filter, ALenum param, const float *vals) -{ ALbandpass_setParamf(filter, param, vals[0]); } - -void ALbandpass_getParami(const ALfilter*, ALenum param, int*) +template<> +void FilterTable<BandpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals) +{ setParamf(filter, param, vals[0]); } +template<> +void FilterTable<BandpassFilterTable>::getParami(const ALfilter*, ALenum param, int*) { throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; } -void ALbandpass_getParamiv(const ALfilter*, ALenum param, int*) -{ - throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer-vector property 0x%04x", - param}; -} -void ALbandpass_getParamf(const ALfilter *filter, ALenum param, float *val) +template<> +void FilterTable<BandpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values) +{ getParami(filter, param, values); } +template<> +void FilterTable<BandpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val) { switch(param) { @@ -260,153 +396,14 @@ void ALbandpass_getParamf(const ALfilter *filter, ALenum param, float *val) throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param}; } } -void ALbandpass_getParamfv(const ALfilter *filter, ALenum param, float *vals) -{ ALbandpass_getParamf(filter, param, vals); } - -DEFINE_ALFILTER_VTABLE(ALbandpass); - - -void ALnullfilter_setParami(ALfilter*, ALenum param, int) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } -void ALnullfilter_setParamiv(ALfilter*, ALenum param, const int*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } -void ALnullfilter_setParamf(ALfilter*, ALenum param, float) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } -void ALnullfilter_setParamfv(ALfilter*, ALenum param, const float*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } - -void ALnullfilter_getParami(const ALfilter*, ALenum param, int*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } -void ALnullfilter_getParamiv(const ALfilter*, ALenum param, int*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } -void ALnullfilter_getParamf(const ALfilter*, ALenum param, float*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } -void ALnullfilter_getParamfv(const ALfilter*, ALenum param, float*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } - -DEFINE_ALFILTER_VTABLE(ALnullfilter); - - -void InitFilterParams(ALfilter *filter, ALenum type) -{ - if(type == AL_FILTER_LOWPASS) - { - filter->Gain = AL_LOWPASS_DEFAULT_GAIN; - filter->GainHF = AL_LOWPASS_DEFAULT_GAINHF; - filter->HFReference = LOWPASSFREQREF; - filter->GainLF = 1.0f; - filter->LFReference = HIGHPASSFREQREF; - filter->vtab = &ALlowpass_vtable; - } - else if(type == AL_FILTER_HIGHPASS) - { - filter->Gain = AL_HIGHPASS_DEFAULT_GAIN; - filter->GainHF = 1.0f; - filter->HFReference = LOWPASSFREQREF; - filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF; - filter->LFReference = HIGHPASSFREQREF; - filter->vtab = &ALhighpass_vtable; - } - else if(type == AL_FILTER_BANDPASS) - { - filter->Gain = AL_BANDPASS_DEFAULT_GAIN; - filter->GainHF = AL_BANDPASS_DEFAULT_GAINHF; - filter->HFReference = LOWPASSFREQREF; - filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF; - filter->LFReference = HIGHPASSFREQREF; - filter->vtab = &ALbandpass_vtable; - } - else - { - filter->Gain = 1.0f; - filter->GainHF = 1.0f; - filter->HFReference = LOWPASSFREQREF; - filter->GainLF = 1.0f; - filter->LFReference = HIGHPASSFREQREF; - filter->vtab = &ALnullfilter_vtable; - } - filter->type = type; -} - -bool EnsureFilters(ALCdevice *device, size_t needed) -{ - size_t count{std::accumulate(device->FilterList.cbegin(), device->FilterList.cend(), size_t{0}, - [](size_t cur, const FilterSubList &sublist) noexcept -> size_t - { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })}; - - while(needed > count) - { - if(device->FilterList.size() >= 1<<25) UNLIKELY - return false; - - device->FilterList.emplace_back(); - auto sublist = device->FilterList.end() - 1; - sublist->FreeMask = ~0_u64; - sublist->Filters = static_cast<ALfilter*>(al_calloc(alignof(ALfilter), sizeof(ALfilter)*64)); - if(!sublist->Filters) UNLIKELY - { - device->FilterList.pop_back(); - return false; - } - count += 64; - } - return true; -} - - -ALfilter *AllocFilter(ALCdevice *device) -{ - auto sublist = std::find_if(device->FilterList.begin(), device->FilterList.end(), - [](const FilterSubList &entry) noexcept -> bool - { return entry.FreeMask != 0; }); - auto lidx = static_cast<ALuint>(std::distance(device->FilterList.begin(), sublist)); - auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask)); - ASSUME(slidx < 64); - - ALfilter *filter{al::construct_at(sublist->Filters + slidx)}; - InitFilterParams(filter, AL_FILTER_NULL); - - /* Add 1 to avoid filter ID 0. */ - filter->id = ((lidx<<6) | slidx) + 1; - - sublist->FreeMask &= ~(1_u64 << slidx); - - return filter; -} - -void FreeFilter(ALCdevice *device, ALfilter *filter) -{ - const ALuint id{filter->id - 1}; - const size_t lidx{id >> 6}; - const ALuint slidx{id & 0x3f}; - - al::destroy_at(filter); - - device->FilterList[lidx].FreeMask |= 1_u64 << slidx; -} - - -inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) -{ - const size_t lidx{(id-1) >> 6}; - const ALuint slidx{(id-1) & 0x3f}; - - if(lidx >= device->FilterList.size()) UNLIKELY - return nullptr; - FilterSubList &sublist = device->FilterList[lidx]; - if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY - return nullptr; - return sublist.Filters + slidx; -} +template<> +void FilterTable<BandpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals) +{ getParamf(filter, param, vals); } -} // namespace -AL_API void AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters) -START_API_FUNC +AL_API DECL_FUNC2(void, alGenFilters, ALsizei, ALuint*) +FORCE_ALIGN void AL_APIENTRY alGenFiltersDirect(ALCcontext *context, ALsizei n, ALuint *filters) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(n < 0) UNLIKELY context->setError(AL_INVALID_VALUE, "Generating %d filters", n); if(n <= 0) UNLIKELY return; @@ -430,7 +427,7 @@ START_API_FUNC /* Store the allocated buffer IDs in a separate local list, to avoid * modifying the user storage in case of failure. */ - al::vector<ALuint> ids; + std::vector<ALuint> ids; ids.reserve(static_cast<ALuint>(n)); do { ALfilter *filter{AllocFilter(device)}; @@ -439,14 +436,11 @@ START_API_FUNC std::copy(ids.begin(), ids.end(), filters); } } -END_API_FUNC -AL_API void AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters) -START_API_FUNC +AL_API DECL_FUNC2(void, alDeleteFilters, ALsizei, const ALuint*) +FORCE_ALIGN void AL_APIENTRY alDeleteFiltersDirect(ALCcontext *context, ALsizei n, + const ALuint *filters) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(n < 0) UNLIKELY context->setError(AL_INVALID_VALUE, "Deleting %d filters", n); if(n <= 0) UNLIKELY return; @@ -474,71 +468,58 @@ START_API_FUNC }; std::for_each(filters, filters_end, delete_filter); } -END_API_FUNC -AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter) -START_API_FUNC +AL_API DECL_FUNC1(ALboolean, alIsFilter, ALuint) +FORCE_ALIGN ALboolean AL_APIENTRY alIsFilterDirect(ALCcontext *context, ALuint filter) noexcept { - ContextRef context{GetContextRef()}; - if(context) LIKELY - { - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard<std::mutex> _{device->FilterLock}; - if(!filter || LookupFilter(device, filter)) - return AL_TRUE; - } + ALCdevice *device{context->mALDevice.get()}; + std::lock_guard<std::mutex> _{device->FilterLock}; + if(!filter || LookupFilter(device, filter)) + return AL_TRUE; return AL_FALSE; } -END_API_FUNC -AL_API void AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value) -START_API_FUNC +AL_API DECL_FUNC3(void, alFilteri, ALuint, ALenum, ALint) +FORCE_ALIGN void AL_APIENTRY alFilteriDirect(ALCcontext *context, ALuint filter, ALenum param, + ALint value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->FilterLock}; ALfilter *alfilt{LookupFilter(device, filter)}; if(!alfilt) UNLIKELY context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter); - else + else if(param == AL_FILTER_TYPE) { - if(param == AL_FILTER_TYPE) - { - if(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS - || value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS) - InitFilterParams(alfilt, value); - else - context->setError(AL_INVALID_VALUE, "Invalid filter type 0x%04x", value); - } - else try - { - /* Call the appropriate handler */ - alfilt->setParami(param, value); - } - catch(filter_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + if(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS + || value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS) + InitFilterParams(alfilt, value); + else + context->setError(AL_INVALID_VALUE, "Invalid filter type 0x%04x", value); + } + else try + { + /* Call the appropriate handler */ + std::visit([alfilt,param,value](auto&& thunk){thunk.setParami(alfilt, param, value);}, + alfilt->mTypeVariant); + } + catch(filter_exception &e) { + context->setError(e.errorCode(), "%s", e.what()); } } -END_API_FUNC -AL_API void AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alFilteriv, ALuint, ALenum, const ALint*) +FORCE_ALIGN void AL_APIENTRY alFilterivDirect(ALCcontext *context, ALuint filter, ALenum param, + const ALint *values) noexcept { switch(param) { case AL_FILTER_TYPE: - alFilteri(filter, param, values[0]); + alFilteriDirect(context, filter, param, values[0]); return; } - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->FilterLock}; @@ -548,20 +529,18 @@ START_API_FUNC else try { /* Call the appropriate handler */ - alfilt->setParamiv(param, values); + std::visit([alfilt,param,values](auto&& thunk){thunk.setParamiv(alfilt, param, values);}, + alfilt->mTypeVariant); } catch(filter_exception &e) { context->setError(e.errorCode(), "%s", e.what()); } } -END_API_FUNC -AL_API void AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat value) -START_API_FUNC +AL_API DECL_FUNC3(void, alFilterf, ALuint, ALenum, ALfloat) +FORCE_ALIGN void AL_APIENTRY alFilterfDirect(ALCcontext *context, ALuint filter, ALenum param, + ALfloat value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->FilterLock}; @@ -571,20 +550,18 @@ START_API_FUNC else try { /* Call the appropriate handler */ - alfilt->setParamf(param, value); + std::visit([alfilt,param,value](auto&& thunk){thunk.setParamf(alfilt, param, value);}, + alfilt->mTypeVariant); } catch(filter_exception &e) { context->setError(e.errorCode(), "%s", e.what()); } } -END_API_FUNC -AL_API void AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alFilterfv, ALuint, ALenum, const ALfloat*) +FORCE_ALIGN void AL_APIENTRY alFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param, + const ALfloat *values) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->FilterLock}; @@ -594,55 +571,48 @@ START_API_FUNC else try { /* Call the appropriate handler */ - alfilt->setParamfv(param, values); + std::visit([alfilt,param,values](auto&& thunk){thunk.setParamfv(alfilt, param, values);}, + alfilt->mTypeVariant); } catch(filter_exception &e) { context->setError(e.errorCode(), "%s", e.what()); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetFilteri, ALuint, ALenum, ALint*) +FORCE_ALIGN void AL_APIENTRY alGetFilteriDirect(ALCcontext *context, ALuint filter, ALenum param, + ALint *value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->FilterLock}; const ALfilter *alfilt{LookupFilter(device, filter)}; if(!alfilt) UNLIKELY context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter); - else + else if(param == AL_FILTER_TYPE) + *value = alfilt->type; + else try { - if(param == AL_FILTER_TYPE) - *value = alfilt->type; - else try - { - /* Call the appropriate handler */ - alfilt->getParami(param, value); - } - catch(filter_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + /* Call the appropriate handler */ + std::visit([alfilt,param,value](auto&& thunk){thunk.getParami(alfilt, param, value);}, + alfilt->mTypeVariant); + } + catch(filter_exception &e) { + context->setError(e.errorCode(), "%s", e.what()); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetFilteriv, ALuint, ALenum, ALint*) +FORCE_ALIGN void AL_APIENTRY alGetFilterivDirect(ALCcontext *context, ALuint filter, ALenum param, + ALint *values) noexcept { switch(param) { case AL_FILTER_TYPE: - alGetFilteri(filter, param, values); + alGetFilteriDirect(context, filter, param, values); return; } - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->FilterLock}; @@ -652,20 +622,18 @@ START_API_FUNC else try { /* Call the appropriate handler */ - alfilt->getParamiv(param, values); + std::visit([alfilt,param,values](auto&& thunk){thunk.getParamiv(alfilt, param, values);}, + alfilt->mTypeVariant); } catch(filter_exception &e) { context->setError(e.errorCode(), "%s", e.what()); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *value) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetFilterf, ALuint, ALenum, ALfloat*) +FORCE_ALIGN void AL_APIENTRY alGetFilterfDirect(ALCcontext *context, ALuint filter, ALenum param, + ALfloat *value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->FilterLock}; @@ -675,20 +643,18 @@ START_API_FUNC else try { /* Call the appropriate handler */ - alfilt->getParamf(param, value); + std::visit([alfilt,param,value](auto&& thunk){thunk.getParamf(alfilt, param, value);}, + alfilt->mTypeVariant); } catch(filter_exception &e) { context->setError(e.errorCode(), "%s", e.what()); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetFilterfv, ALuint, ALenum, ALfloat*) +FORCE_ALIGN void AL_APIENTRY alGetFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param, + ALfloat *values) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; std::lock_guard<std::mutex> _{device->FilterLock}; @@ -698,22 +664,38 @@ START_API_FUNC else try { /* Call the appropriate handler */ - alfilt->getParamfv(param, values); + std::visit([alfilt,param,values](auto&& thunk){thunk.getParamfv(alfilt, param, values);}, + alfilt->mTypeVariant); } catch(filter_exception &e) { context->setError(e.errorCode(), "%s", e.what()); } } -END_API_FUNC + + +void ALfilter::SetName(ALCcontext *context, ALuint id, std::string_view name) +{ + ALCdevice *device{context->mALDevice.get()}; + std::lock_guard<std::mutex> _{device->FilterLock}; + + auto filter = LookupFilter(device, id); + if(!filter) UNLIKELY + return context->setError(AL_INVALID_NAME, "Invalid filter ID %u", id); + + device->mFilterNames.insert_or_assign(id, name); +} FilterSubList::~FilterSubList() { + if(!Filters) + return; + uint64_t usemask{~FreeMask}; while(usemask) { const int idx{al::countr_zero(usemask)}; - al::destroy_at(Filters+idx); + std::destroy_at(Filters+idx); usemask &= ~(1_u64 << idx); } FreeMask = ~usemask; diff --git a/al/filter.h b/al/filter.h index 65a9e30f..505900d4 100644 --- a/al/filter.h +++ b/al/filter.h @@ -1,6 +1,8 @@ #ifndef AL_FILTER_H #define AL_FILTER_H +#include <string_view> +#include <variant> #include "AL/al.h" #include "AL/alc.h" @@ -12,6 +14,25 @@ #define HIGHPASSFREQREF 250.0f +template<typename T> +struct FilterTable { + static void setParami(struct ALfilter*, ALenum, int); + static void setParamiv(struct ALfilter*, ALenum, const int*); + static void setParamf(struct ALfilter*, ALenum, float); + static void setParamfv(struct ALfilter*, ALenum, const float*); + + static void getParami(const struct ALfilter*, ALenum, int*); + static void getParamiv(const struct ALfilter*, ALenum, int*); + static void getParamf(const struct ALfilter*, ALenum, float*); + static void getParamfv(const struct ALfilter*, ALenum, float*); +}; + +struct NullFilterTable : public FilterTable<NullFilterTable> { }; +struct LowpassFilterTable : public FilterTable<LowpassFilterTable> { }; +struct HighpassFilterTable : public FilterTable<HighpassFilterTable> { }; +struct BandpassFilterTable : public FilterTable<BandpassFilterTable> { }; + + struct ALfilter { ALenum type{AL_FILTER_NULL}; @@ -21,30 +42,14 @@ struct ALfilter { float GainLF{1.0f}; float LFReference{HIGHPASSFREQREF}; - struct Vtable { - void (*const setParami )(ALfilter *filter, ALenum param, int val); - void (*const setParamiv)(ALfilter *filter, ALenum param, const int *vals); - void (*const setParamf )(ALfilter *filter, ALenum param, float val); - void (*const setParamfv)(ALfilter *filter, ALenum param, const float *vals); - - void (*const getParami )(const ALfilter *filter, ALenum param, int *val); - void (*const getParamiv)(const ALfilter *filter, ALenum param, int *vals); - void (*const getParamf )(const ALfilter *filter, ALenum param, float *val); - void (*const getParamfv)(const ALfilter *filter, ALenum param, float *vals); - }; - const Vtable *vtab{nullptr}; + using TableTypes = std::variant<NullFilterTable,LowpassFilterTable,HighpassFilterTable, + BandpassFilterTable>; + TableTypes mTypeVariant; /* Self ID */ ALuint id{0}; - void setParami(ALenum param, int value) { vtab->setParami(this, param, value); } - void setParamiv(ALenum param, const int *values) { vtab->setParamiv(this, param, values); } - void setParamf(ALenum param, float value) { vtab->setParamf(this, param, value); } - void setParamfv(ALenum param, const float *values) { vtab->setParamfv(this, param, values); } - void getParami(ALenum param, int *value) const { vtab->getParami(this, param, value); } - void getParamiv(ALenum param, int *values) const { vtab->getParamiv(this, param, values); } - void getParamf(ALenum param, float *value) const { vtab->getParamf(this, param, value); } - void getParamfv(ALenum param, float *values) const { vtab->getParamfv(this, param, values); } + static void SetName(ALCcontext *context, ALuint id, std::string_view name); DISABLE_ALLOC() }; diff --git a/al/listener.cpp b/al/listener.cpp index 06d7c370..ea2ebb3f 100644 --- a/al/listener.cpp +++ b/al/listener.cpp @@ -33,6 +33,7 @@ #include "almalloc.h" #include "atomic.h" #include "core/except.h" +#include "direct_defs.h" #include "opthelpers.h" @@ -68,12 +69,9 @@ inline void CommitAndUpdateProps(ALCcontext *context) } // namespace -AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value) -START_API_FUNC +AL_API DECL_FUNC2(void, alListenerf, ALenum, ALfloat) +FORCE_ALIGN void AL_APIENTRY alListenerfDirect(ALCcontext *context, ALenum param, ALfloat value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALlistener &listener = context->mListener; std::lock_guard<std::mutex> _{context->mPropLock}; switch(param) @@ -82,28 +80,25 @@ START_API_FUNC if(!(value >= 0.0f && std::isfinite(value))) return context->setError(AL_INVALID_VALUE, "Listener gain out of range"); listener.Gain = value; - UpdateProps(context.get()); + UpdateProps(context); break; case AL_METERS_PER_UNIT: if(!(value >= AL_MIN_METERS_PER_UNIT && value <= AL_MAX_METERS_PER_UNIT)) return context->setError(AL_INVALID_VALUE, "Listener meters per unit out of range"); listener.mMetersPerUnit = value; - UpdateProps(context.get()); + UpdateProps(context); break; default: context->setError(AL_INVALID_ENUM, "Invalid listener float property"); } } -END_API_FUNC -AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) -START_API_FUNC +AL_API DECL_FUNC4(void, alListener3f, ALenum, ALfloat, ALfloat, ALfloat) +FORCE_ALIGN void AL_APIENTRY alListener3fDirect(ALCcontext *context, ALenum param, ALfloat value1, + ALfloat value2, ALfloat value3) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALlistener &listener = context->mListener; std::lock_guard<std::mutex> _{context->mPropLock}; switch(param) @@ -114,7 +109,7 @@ START_API_FUNC listener.Position[0] = value1; listener.Position[1] = value2; listener.Position[2] = value3; - CommitAndUpdateProps(context.get()); + CommitAndUpdateProps(context); break; case AL_VELOCITY: @@ -123,40 +118,34 @@ START_API_FUNC listener.Velocity[0] = value1; listener.Velocity[1] = value2; listener.Velocity[2] = value3; - CommitAndUpdateProps(context.get()); + CommitAndUpdateProps(context); break; default: context->setError(AL_INVALID_ENUM, "Invalid listener 3-float property"); } } -END_API_FUNC -AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values) -START_API_FUNC +AL_API DECL_FUNC2(void, alListenerfv, ALenum, const ALfloat*) +FORCE_ALIGN void AL_APIENTRY alListenerfvDirect(ALCcontext *context, ALenum param, + const ALfloat *values) noexcept { - if(values) + if(!values) UNLIKELY + return context->setError(AL_INVALID_VALUE, "NULL pointer"); + + switch(param) { - switch(param) - { - case AL_GAIN: - case AL_METERS_PER_UNIT: - alListenerf(param, values[0]); - return; + case AL_GAIN: + case AL_METERS_PER_UNIT: + alListenerfDirect(context, param, values[0]); + return; - case AL_POSITION: - case AL_VELOCITY: - alListener3f(param, values[0], values[1], values[2]); - return; - } + case AL_POSITION: + case AL_VELOCITY: + alListener3fDirect(context, param, values[0], values[1], values[2]); + return; } - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - - if(!values) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); - ALlistener &listener = context->mListener; std::lock_guard<std::mutex> _{context->mPropLock}; switch(param) @@ -172,22 +161,18 @@ START_API_FUNC listener.OrientUp[0] = values[3]; listener.OrientUp[1] = values[4]; listener.OrientUp[2] = values[5]; - CommitAndUpdateProps(context.get()); + CommitAndUpdateProps(context); break; default: context->setError(AL_INVALID_ENUM, "Invalid listener float-vector property"); } } -END_API_FUNC -AL_API void AL_APIENTRY alListeneri(ALenum param, ALint /*value*/) -START_API_FUNC +AL_API DECL_FUNC2(void, alListeneri, ALenum, ALint) +FORCE_ALIGN void AL_APIENTRY alListeneriDirect(ALCcontext *context, ALenum param, ALint /*value*/) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; switch(param) { @@ -195,23 +180,20 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid listener integer property"); } } -END_API_FUNC -AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3) -START_API_FUNC +AL_API DECL_FUNC4(void, alListener3i, ALenum, ALint, ALint, ALint) +FORCE_ALIGN void AL_APIENTRY alListener3iDirect(ALCcontext *context, ALenum param, ALint value1, + ALint value2, ALint value3) noexcept { switch(param) { case AL_POSITION: case AL_VELOCITY: - alListener3f(param, static_cast<ALfloat>(value1), static_cast<ALfloat>(value2), - static_cast<ALfloat>(value3)); + alListener3fDirect(context, param, static_cast<ALfloat>(value1), + static_cast<ALfloat>(value2), static_cast<ALfloat>(value3)); return; } - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; switch(param) { @@ -219,55 +201,48 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property"); } } -END_API_FUNC -AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values) -START_API_FUNC +AL_API DECL_FUNC2(void, alListeneriv, ALenum, const ALint*) +FORCE_ALIGN void AL_APIENTRY alListenerivDirect(ALCcontext *context, ALenum param, + const ALint *values) noexcept { - if(values) + if(!values) UNLIKELY + return context->setError(AL_INVALID_VALUE, "NULL pointer"); + + switch(param) { - ALfloat fvals[6]; - switch(param) - { - case AL_POSITION: - case AL_VELOCITY: - alListener3f(param, static_cast<ALfloat>(values[0]), static_cast<ALfloat>(values[1]), - static_cast<ALfloat>(values[2])); - return; + case AL_POSITION: + case AL_VELOCITY: + alListener3fDirect(context, param, static_cast<ALfloat>(values[0]), + static_cast<ALfloat>(values[1]), static_cast<ALfloat>(values[2])); + return; - case AL_ORIENTATION: - fvals[0] = static_cast<ALfloat>(values[0]); - fvals[1] = static_cast<ALfloat>(values[1]); - fvals[2] = static_cast<ALfloat>(values[2]); - fvals[3] = static_cast<ALfloat>(values[3]); - fvals[4] = static_cast<ALfloat>(values[4]); - fvals[5] = static_cast<ALfloat>(values[5]); - alListenerfv(param, fvals); - return; - } + case AL_ORIENTATION: + const ALfloat fvals[6]{ + static_cast<ALfloat>(values[0]), + static_cast<ALfloat>(values[1]), + static_cast<ALfloat>(values[2]), + static_cast<ALfloat>(values[3]), + static_cast<ALfloat>(values[4]), + static_cast<ALfloat>(values[5]), + }; + alListenerfvDirect(context, param, fvals); + return; } - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; - if(!values) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) + switch(param) { default: context->setError(AL_INVALID_ENUM, "Invalid listener integer-vector property"); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value) -START_API_FUNC +AL_API DECL_FUNC2(void, alGetListenerf, ALenum, ALfloat*) +FORCE_ALIGN void AL_APIENTRY alGetListenerfDirect(ALCcontext *context, ALenum param, + ALfloat *value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALlistener &listener = context->mListener; std::lock_guard<std::mutex> _{context->mPropLock}; if(!value) @@ -286,14 +261,11 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid listener float property"); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) -START_API_FUNC +AL_API DECL_FUNC4(void, alGetListener3f, ALenum, ALfloat*, ALfloat*, ALfloat*) +FORCE_ALIGN void AL_APIENTRY alGetListener3fDirect(ALCcontext *context, ALenum param, + ALfloat *value1, ALfloat *value2, ALfloat *value3) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALlistener &listener = context->mListener; std::lock_guard<std::mutex> _{context->mPropLock}; if(!value1 || !value2 || !value3) @@ -316,27 +288,24 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid listener 3-float property"); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values) -START_API_FUNC +AL_API DECL_FUNC2(void, alGetListenerfv, ALenum, ALfloat*) +FORCE_ALIGN void AL_APIENTRY alGetListenerfvDirect(ALCcontext *context, ALenum param, + ALfloat *values) noexcept { switch(param) { case AL_GAIN: case AL_METERS_PER_UNIT: - alGetListenerf(param, values); + alGetListenerfDirect(context, param, values); return; case AL_POSITION: case AL_VELOCITY: - alGetListener3f(param, values+0, values+1, values+2); + alGetListener3fDirect(context, param, values+0, values+1, values+2); return; } - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALlistener &listener = context->mListener; std::lock_guard<std::mutex> _{context->mPropLock}; if(!values) @@ -357,15 +326,11 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid listener float-vector property"); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value) -START_API_FUNC +AL_API DECL_FUNC2(void, alGetListeneri, ALenum, ALint*) +FORCE_ALIGN void AL_APIENTRY alGetListeneriDirect(ALCcontext *context, ALenum param, ALint *value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; if(!value) context->setError(AL_INVALID_VALUE, "NULL pointer"); @@ -375,14 +340,11 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid listener integer property"); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3) -START_API_FUNC +AL_API DECL_FUNC4(void, alGetListener3i, ALenum, ALint*, ALint*, ALint*) +FORCE_ALIGN void AL_APIENTRY alGetListener3iDirect(ALCcontext *context, ALenum param, + ALint *value1, ALint *value2, ALint *value3) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALlistener &listener = context->mListener; std::lock_guard<std::mutex> _{context->mPropLock}; if(!value1 || !value2 || !value3) @@ -405,22 +367,19 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property"); } } -END_API_FUNC -AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values) -START_API_FUNC +AL_API DECL_FUNC2(void, alGetListeneriv, ALenum, ALint*) +FORCE_ALIGN void AL_APIENTRY alGetListenerivDirect(ALCcontext *context, ALenum param, + ALint *values) noexcept { switch(param) { case AL_POSITION: case AL_VELOCITY: - alGetListener3i(param, values+0, values+1, values+2); + alGetListener3iDirect(context, param, values+0, values+1, values+2); return; } - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - ALlistener &listener = context->mListener; std::lock_guard<std::mutex> _{context->mPropLock}; if(!values) @@ -441,4 +400,3 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid listener integer-vector property"); } } -END_API_FUNC diff --git a/al/source.cpp b/al/source.cpp index cba33862..fe5bba40 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -27,20 +27,22 @@ #include <atomic> #include <cassert> #include <chrono> +#include <cinttypes> #include <climits> #include <cmath> #include <cstdint> #include <functional> -#include <inttypes.h> #include <iterator> #include <limits> #include <memory> #include <mutex> #include <new> #include <numeric> +#include <optional> #include <stdexcept> #include <thread> #include <utility> +#include <vector> #include "AL/al.h" #include "AL/alc.h" @@ -55,7 +57,6 @@ #include "alc/inprogext.h" #include "almalloc.h" #include "alnumeric.h" -#include "aloptional.h" #include "alspan.h" #include "atomic.h" #include "auxeffectslot.h" @@ -67,22 +68,21 @@ #include "core/filters/splitter.h" #include "core/logging.h" #include "core/voice_change.h" +#include "direct_defs.h" #include "event.h" #include "filter.h" #include "opthelpers.h" #include "ringbuffer.h" -#include "threads.h" #ifdef ALSOFT_EAX #include <cassert> #endif // ALSOFT_EAX -bool sBufferSubDataCompat{false}; - namespace { using namespace std::placeholders; using std::chrono::nanoseconds; +using seconds_d = std::chrono::duration<double>; Voice *GetSourceVoice(ALsource *source, ALCcontext *context) { @@ -95,7 +95,7 @@ Voice *GetSourceVoice(ALsource *source, ALCcontext *context) if(voice->mSourceID.load(std::memory_order_acquire) == sid) return voice; } - source->VoiceIdx = INVALID_VOICE_IDX; + source->VoiceIdx = InvalidVoiceIndex; return nullptr; } @@ -281,7 +281,8 @@ double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *cl * (Bytes, Samples or Seconds). The offset is relative to the start of the * queue (not the start of the current buffer). */ -double GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) +template<typename T> +NOINLINE T GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) { ALCdevice *device{context->mALDevice.get()}; const VoiceBufferItem *Current{}; @@ -304,7 +305,7 @@ double GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) } while(refcount != device->MixCount.load(std::memory_order_relaxed)); if(!voice) - return 0.0; + return T{0}; const ALbuffer *BufferFmt{nullptr}; auto BufferList = Source->mQueue.cbegin(); @@ -321,24 +322,48 @@ double GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) } ASSUME(BufferFmt != nullptr); - double offset{}; + T offset{}; switch(name) { case AL_SEC_OFFSET: - offset = static_cast<double>(readPos) + readPosFrac/double{MixerFracOne}; - offset /= BufferFmt->mSampleRate; + if constexpr(std::is_floating_point_v<T>) + { + offset = static_cast<T>(readPos) + static_cast<T>(readPosFrac)/T{MixerFracOne}; + offset /= static_cast<T>(BufferFmt->mSampleRate); + } + else + { + readPos /= BufferFmt->mSampleRate; + offset = static_cast<T>(clampi64(readPos, std::numeric_limits<T>::min(), + std::numeric_limits<T>::max())); + } break; case AL_SAMPLE_OFFSET: - offset = static_cast<double>(readPos) + readPosFrac/double{MixerFracOne}; + if constexpr(std::is_floating_point_v<T>) + offset = static_cast<T>(readPos) + static_cast<T>(readPosFrac)/T{MixerFracOne}; + else + offset = static_cast<T>(clampi64(readPos, std::numeric_limits<T>::min(), + std::numeric_limits<T>::max())); break; case AL_BYTE_OFFSET: const ALuint BlockSamples{BufferFmt->mBlockAlign}; const ALuint BlockSize{BufferFmt->blockSizeFromFmt()}; - /* Round down to the block boundary. */ - offset = static_cast<double>(readPos / BlockSamples) * BlockSize; + readPos = readPos / BlockSamples * BlockSize; + + if constexpr(std::is_floating_point_v<T>) + offset = static_cast<T>(readPos); + else + { + if(readPos > std::numeric_limits<T>::max()) + offset = RoundDown(std::numeric_limits<T>::max(), static_cast<T>(BlockSize)); + else if(readPos < std::numeric_limits<T>::min()) + offset = RoundUp(std::numeric_limits<T>::min(), static_cast<T>(BlockSize)); + else + offset = static_cast<T>(readPos); + } break; } return offset; @@ -349,7 +374,8 @@ double GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) * Gets the length of the given Source's buffer queue, in the appropriate * format (Bytes, Samples or Seconds). */ -double GetSourceLength(const ALsource *source, ALenum name) +template<typename T> +NOINLINE T GetSourceLength(const ALsource *source, ALenum name) { uint64_t length{0}; const ALbuffer *BufferFmt{nullptr}; @@ -360,25 +386,40 @@ double GetSourceLength(const ALsource *source, ALenum name) length += listitem.mSampleLen; } if(length == 0) - return 0.0; + return T{0}; ASSUME(BufferFmt != nullptr); switch(name) { case AL_SEC_LENGTH_SOFT: - return static_cast<double>(length) / BufferFmt->mSampleRate; + if constexpr(std::is_floating_point_v<T>) + return static_cast<T>(length) / static_cast<T>(BufferFmt->mSampleRate); + else + return static_cast<T>(minu64(length/BufferFmt->mSampleRate, + std::numeric_limits<T>::max())); case AL_SAMPLE_LENGTH_SOFT: - return static_cast<double>(length); + if constexpr(std::is_floating_point_v<T>) + return static_cast<T>(length); + else + return static_cast<T>(minu64(length, std::numeric_limits<T>::max())); case AL_BYTE_LENGTH_SOFT: const ALuint BlockSamples{BufferFmt->mBlockAlign}; const ALuint BlockSize{BufferFmt->blockSizeFromFmt()}; - /* Round down to the block boundary. */ - return static_cast<double>(length / BlockSamples) * BlockSize; + length = length / BlockSamples * BlockSize; + + if constexpr(std::is_floating_point_v<T>) + return static_cast<T>(length); + else + { + if(length > std::numeric_limits<T>::max()) + return RoundDown(std::numeric_limits<T>::max(), static_cast<T>(BlockSize)); + return static_cast<T>(length); + } } - return 0.0; + return T{0}; } @@ -392,11 +433,11 @@ struct VoicePos { * GetSampleOffset * * Retrieves the voice position, fixed-point fraction, and bufferlist item - * using the givem offset type and offset. If the offset is out of range, + * using the given offset type and offset. If the offset is out of range, * returns an empty optional. */ -al::optional<VoicePos> GetSampleOffset(al::deque<ALbufferQueueItem> &BufferList, ALenum OffsetType, - double Offset) +std::optional<VoicePos> GetSampleOffset(std::deque<ALbufferQueueItem> &BufferList, + ALenum OffsetType, double Offset) { /* Find the first valid Buffer in the Queue */ const ALbuffer *BufferFmt{nullptr}; @@ -406,7 +447,7 @@ al::optional<VoicePos> GetSampleOffset(al::deque<ALbufferQueueItem> &BufferList, if(BufferFmt) break; } if(!BufferFmt) UNLIKELY - return al::nullopt; + return std::nullopt; /* Get sample frame offset */ int64_t offset{}; @@ -452,12 +493,12 @@ al::optional<VoicePos> GetSampleOffset(al::deque<ALbufferQueueItem> &BufferList, if(offset < 0) { if(offset < std::numeric_limits<int>::min()) - return al::nullopt; + return std::nullopt; return VoicePos{static_cast<int>(offset), frac, &BufferList.front()}; } if(BufferFmt->mCallback) - return al::nullopt; + return std::nullopt; int64_t totalBufferLen{0}; for(auto &item : BufferList) @@ -473,7 +514,7 @@ al::optional<VoicePos> GetSampleOffset(al::deque<ALbufferQueueItem> &BufferList, } /* Offset is out of range of the queue */ - return al::nullopt; + return std::nullopt; } @@ -676,8 +717,7 @@ inline ALenum GetSourceState(ALsource *source, Voice *voice) bool EnsureSources(ALCcontext *context, size_t needed) { - size_t count{std::accumulate(context->mSourceList.cbegin(), context->mSourceList.cend(), - size_t{0}, + size_t count{std::accumulate(context->mSourceList.cbegin(), context->mSourceList.cend(), 0_uz, [](size_t cur, const SourceSubList &sublist) noexcept -> size_t { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })}; @@ -722,6 +762,8 @@ ALsource *AllocSource(ALCcontext *context) void FreeSource(ALCcontext *context, ALsource *source) { + context->mSourceNames.erase(source->id); + const ALuint id{source->id - 1}; const size_t lidx{id >> 6}; const ALuint slidx{id & 0x3f}; @@ -738,7 +780,7 @@ void FreeSource(ALCcontext *context, ALsource *source) SendVoiceChanges(context, vchg); } - al::destroy_at(source); + std::destroy_at(source); context->mSourceList[lidx].FreeMask |= 1_u64 << slidx; context->mNumSources--; @@ -758,56 +800,55 @@ inline ALsource *LookupSource(ALCcontext *context, ALuint id) noexcept return sublist.Sources + slidx; } -inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept +auto LookupBuffer = [](ALCdevice *device, auto id) noexcept -> ALbuffer* { - const size_t lidx{(id-1) >> 6}; - const ALuint slidx{(id-1) & 0x3f}; + const auto lidx{(id-1) >> 6}; + const auto slidx{(id-1) & 0x3f}; if(lidx >= device->BufferList.size()) UNLIKELY return nullptr; - BufferSubList &sublist = device->BufferList[lidx]; + BufferSubList &sublist = device->BufferList[static_cast<size_t>(lidx)]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Buffers + slidx; -} + return sublist.Buffers + static_cast<size_t>(slidx); +}; -inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) noexcept +auto LookupFilter = [](ALCdevice *device, auto id) noexcept -> ALfilter* { - const size_t lidx{(id-1) >> 6}; - const ALuint slidx{(id-1) & 0x3f}; + const auto lidx{(id-1) >> 6}; + const auto slidx{(id-1) & 0x3f}; if(lidx >= device->FilterList.size()) UNLIKELY return nullptr; - FilterSubList &sublist = device->FilterList[lidx]; + FilterSubList &sublist = device->FilterList[static_cast<size_t>(lidx)]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Filters + slidx; -} + return sublist.Filters + static_cast<size_t>(slidx); +}; -inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept +auto LookupEffectSlot = [](ALCcontext *context, auto id) noexcept -> ALeffectslot* { - const size_t lidx{(id-1) >> 6}; - const ALuint slidx{(id-1) & 0x3f}; + const auto lidx{(id-1) >> 6}; + const auto slidx{(id-1) & 0x3f}; if(lidx >= context->mEffectSlotList.size()) UNLIKELY return nullptr; - EffectSlotSubList &sublist{context->mEffectSlotList[lidx]}; + EffectSlotSubList &sublist{context->mEffectSlotList[static_cast<size_t>(lidx)]}; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.EffectSlots + slidx; -} + return sublist.EffectSlots + static_cast<size_t>(slidx); +}; -al::optional<SourceStereo> StereoModeFromEnum(ALenum mode) +auto StereoModeFromEnum = [](auto mode) noexcept -> std::optional<SourceStereo> { switch(mode) { case AL_NORMAL_SOFT: return SourceStereo::Normal; case AL_SUPER_STEREO_SOFT: return SourceStereo::Enhanced; } - WARN("Unsupported stereo mode: 0x%04x\n", mode); - return al::nullopt; -} + return std::nullopt; +}; ALenum EnumFromStereoMode(SourceStereo mode) { switch(mode) @@ -818,7 +859,7 @@ ALenum EnumFromStereoMode(SourceStereo mode) throw std::runtime_error{"Invalid SourceStereo: "+std::to_string(int(mode))}; } -al::optional<SpatializeMode> SpatializeModeFromEnum(ALenum mode) +auto SpatializeModeFromEnum = [](auto mode) noexcept -> std::optional<SpatializeMode> { switch(mode) { @@ -826,9 +867,8 @@ al::optional<SpatializeMode> SpatializeModeFromEnum(ALenum mode) case AL_TRUE: return SpatializeMode::On; case AL_AUTO_SOFT: return SpatializeMode::Auto; } - WARN("Unsupported spatialize mode: 0x%04x\n", mode); - return al::nullopt; -} + return std::nullopt; +}; ALenum EnumFromSpatializeMode(SpatializeMode mode) { switch(mode) @@ -840,7 +880,7 @@ ALenum EnumFromSpatializeMode(SpatializeMode mode) throw std::runtime_error{"Invalid SpatializeMode: "+std::to_string(int(mode))}; } -al::optional<DirectMode> DirectModeFromEnum(ALenum mode) +auto DirectModeFromEnum = [](auto mode) noexcept -> std::optional<DirectMode> { switch(mode) { @@ -848,9 +888,8 @@ al::optional<DirectMode> DirectModeFromEnum(ALenum mode) case AL_DROP_UNMATCHED_SOFT: return DirectMode::DropMismatch; case AL_REMIX_UNMATCHED_SOFT: return DirectMode::RemixMismatch; } - WARN("Unsupported direct mode: 0x%04x\n", mode); - return al::nullopt; -} + return std::nullopt; +}; ALenum EnumFromDirectMode(DirectMode mode) { switch(mode) @@ -862,7 +901,7 @@ ALenum EnumFromDirectMode(DirectMode mode) throw std::runtime_error{"Invalid DirectMode: "+std::to_string(int(mode))}; } -al::optional<DistanceModel> DistanceModelFromALenum(ALenum model) +auto DistanceModelFromALenum = [](auto model) noexcept -> std::optional<DistanceModel> { switch(model) { @@ -874,8 +913,8 @@ al::optional<DistanceModel> DistanceModelFromALenum(ALenum model) case AL_EXPONENT_DISTANCE: return DistanceModel::Exponent; case AL_EXPONENT_DISTANCE_CLAMPED: return DistanceModel::ExponentClamped; } - return al::nullopt; -} + return std::nullopt; +}; ALenum ALenumFromDistanceModel(DistanceModel model) { switch(model) @@ -973,8 +1012,6 @@ enum SourceProp : ALenum { }; -constexpr size_t MaxValues{6u}; - constexpr ALuint IntValsByProp(ALenum prop) { switch(static_cast<SourceProp>(prop)) @@ -1276,10 +1313,6 @@ constexpr ALuint DoubleValsByProp(ALenum prop) } -void SetSourcefv(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, const al::span<const float> values); -void SetSourceiv(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, const al::span<const int> values); -void SetSourcei64v(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, const al::span<const int64_t> values); - struct check_exception : std::exception { }; struct check_size_exception final : check_exception { @@ -1327,10 +1360,39 @@ inline void CommitAndUpdateSourceProps(ALsource *source, ALCcontext *context) #endif +template<typename T> +struct PropType { }; +template<> +struct PropType<ALint> { static const char *Name() { return "integer"; } }; +template<> +struct PropType<ALint64SOFT> { static const char *Name() { return "int64"; } }; +template<> +struct PropType<ALfloat> { static const char *Name() { return "float"; } }; +template<> +struct PropType<ALdouble> { static const char *Name() { return "double"; } }; + +template<typename T> +struct HexPrinter { + char mStr[sizeof(T)*2 + 3]{}; + HexPrinter(T value) + { + using ST = std::make_signed_t<std::remove_cv_t<T>>; + if constexpr(std::is_same_v<ST,int>) + std::snprintf(mStr, std::size(mStr), "0x%x", value); + else if constexpr(std::is_same_v<ST,long>) + std::snprintf(mStr, std::size(mStr), "0x%lx", value); + else if constexpr(std::is_same_v<ST,long long>) + std::snprintf(mStr, std::size(mStr), "0x%llx", value); + } + + const char *c_str() const noexcept { return mStr; } +}; + + /** - * Returns a pair of lambdas to check the following setters and getters. + * Returns a pair of lambdas to check the following setter. * - * The first lambda checks the size of the span is valid for its given size, + * The first lambda checks the size of the span is valid for the required size, * setting the proper context error and throwing a check_size_exception if it * fails. * @@ -1357,19 +1419,33 @@ auto GetCheckers(ALCcontext *const Context, const SourceProp prop, const al::spa ); } -void SetSourcefv(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, - const al::span<const float> values) -try { - /* Structured bindings would be nice (C++17). */ - auto Checkers = GetCheckers(Context, prop, values); - auto &CheckSize = Checkers.first; - auto &CheckValue = Checkers.second; - int ival; +template<typename T> +NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, + const al::span<const T> values) try +{ + auto&& [CheckSize, CheckValue] = GetCheckers(Context, prop, values); + ALCdevice *device{Context->mALDevice.get()}; switch(prop) { + case AL_SOURCE_STATE: + case AL_SOURCE_TYPE: + case AL_BUFFERS_QUEUED: + case AL_BUFFERS_PROCESSED: + if constexpr(std::is_integral_v<T>) + { + /* Query only */ + return Context->setError(AL_INVALID_OPERATION, + "Setting read-only source property 0x%04x", prop); + } + break; + + case AL_BYTE_LENGTH_SOFT: + case AL_SAMPLE_LENGTH_SOFT: case AL_SEC_LENGTH_SOFT: + case AL_SAMPLE_OFFSET_LATENCY_SOFT: case AL_SEC_OFFSET_LATENCY_SOFT: + case AL_SAMPLE_OFFSET_CLOCK_SOFT: case AL_SEC_OFFSET_CLOCK_SOFT: /* Query only */ return Context->setError(AL_INVALID_OPERATION, @@ -1377,712 +1453,540 @@ try { case AL_PITCH: CheckSize(1); - CheckValue(values[0] >= 0.0f); + CheckValue(values[0] >= T{0}); - Source->Pitch = values[0]; + Source->Pitch = static_cast<float>(values[0]); return UpdateSourceProps(Source, Context); case AL_CONE_INNER_ANGLE: CheckSize(1); - CheckValue(values[0] >= 0.0f && values[0] <= 360.0f); + CheckValue(values[0] >= T{0} && values[0] <= T{360}); - Source->InnerAngle = values[0]; + Source->InnerAngle = static_cast<float>(values[0]); return CommitAndUpdateSourceProps(Source, Context); case AL_CONE_OUTER_ANGLE: CheckSize(1); - CheckValue(values[0] >= 0.0f && values[0] <= 360.0f); + CheckValue(values[0] >= T{0} && values[0] <= T{360}); - Source->OuterAngle = values[0]; + Source->OuterAngle = static_cast<float>(values[0]); return CommitAndUpdateSourceProps(Source, Context); case AL_GAIN: CheckSize(1); - CheckValue(values[0] >= 0.0f); + CheckValue(values[0] >= T{0}); - Source->Gain = values[0]; + Source->Gain = static_cast<float>(values[0]); return UpdateSourceProps(Source, Context); case AL_MAX_DISTANCE: CheckSize(1); - CheckValue(values[0] >= 0.0f); + CheckValue(values[0] >= T{0}); - Source->MaxDistance = values[0]; + Source->MaxDistance = static_cast<float>(values[0]); return CommitAndUpdateSourceProps(Source, Context); case AL_ROLLOFF_FACTOR: CheckSize(1); - CheckValue(values[0] >= 0.0f); + CheckValue(values[0] >= T{0}); - Source->RolloffFactor = values[0]; + Source->RolloffFactor = static_cast<float>(values[0]); return CommitAndUpdateSourceProps(Source, Context); case AL_REFERENCE_DISTANCE: CheckSize(1); - CheckValue(values[0] >= 0.0f); + CheckValue(values[0] >= T{0}); - Source->RefDistance = values[0]; + Source->RefDistance = static_cast<float>(values[0]); return CommitAndUpdateSourceProps(Source, Context); case AL_MIN_GAIN: CheckSize(1); - CheckValue(values[0] >= 0.0f); + CheckValue(values[0] >= T{0}); - Source->MinGain = values[0]; + Source->MinGain = static_cast<float>(values[0]); return UpdateSourceProps(Source, Context); case AL_MAX_GAIN: CheckSize(1); - CheckValue(values[0] >= 0.0f); + CheckValue(values[0] >= T{0}); - Source->MaxGain = values[0]; + Source->MaxGain = static_cast<float>(values[0]); return UpdateSourceProps(Source, Context); case AL_CONE_OUTER_GAIN: CheckSize(1); - CheckValue(values[0] >= 0.0f && values[0] <= 1.0f); + CheckValue(values[0] >= T{0} && values[0] <= T{1}); - Source->OuterGain = values[0]; + Source->OuterGain = static_cast<float>(values[0]); return UpdateSourceProps(Source, Context); case AL_CONE_OUTER_GAINHF: CheckSize(1); - CheckValue(values[0] >= 0.0f && values[0] <= 1.0f); + CheckValue(values[0] >= T{0} && values[0] <= T{1}); - Source->OuterGainHF = values[0]; + Source->OuterGainHF = static_cast<float>(values[0]); return UpdateSourceProps(Source, Context); case AL_AIR_ABSORPTION_FACTOR: CheckSize(1); - CheckValue(values[0] >= 0.0f && values[0] <= 10.0f); + CheckValue(values[0] >= T{0} && values[0] <= T{10}); - Source->AirAbsorptionFactor = values[0]; + Source->AirAbsorptionFactor = static_cast<float>(values[0]); return UpdateSourceProps(Source, Context); case AL_ROOM_ROLLOFF_FACTOR: CheckSize(1); - CheckValue(values[0] >= 0.0f && values[0] <= 10.0f); + CheckValue(values[0] >= T{0} && values[0] <= T{1}); - Source->RoomRolloffFactor = values[0]; + Source->RoomRolloffFactor = static_cast<float>(values[0]); return UpdateSourceProps(Source, Context); case AL_DOPPLER_FACTOR: CheckSize(1); - CheckValue(values[0] >= 0.0f && values[0] <= 1.0f); + CheckValue(values[0] >= T{0} && values[0] <= T{1}); - Source->DopplerFactor = values[0]; + Source->DopplerFactor = static_cast<float>(values[0]); return UpdateSourceProps(Source, Context); + + case AL_SOURCE_RELATIVE: + if constexpr(std::is_integral_v<T>) + { + CheckSize(1); + CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE); + + Source->HeadRelative = values[0] != AL_FALSE; + return CommitAndUpdateSourceProps(Source, Context); + } + break; + + case AL_LOOPING: + if constexpr(std::is_integral_v<T>) + { + CheckSize(1); + CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE); + + Source->Looping = values[0] != AL_FALSE; + if(Voice *voice{GetSourceVoice(Source, Context)}) + { + if(Source->Looping) + voice->mLoopBuffer.store(&Source->mQueue.front(), std::memory_order_release); + else + voice->mLoopBuffer.store(nullptr, std::memory_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. + */ + device->waitForMix(); + } + return; + } + break; + + case AL_BUFFER: + if constexpr(std::is_integral_v<T>) + { + CheckSize(1); + { + const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))}; + if(state == AL_PLAYING || state == AL_PAUSED) + return Context->setError(AL_INVALID_OPERATION, + "Setting buffer on playing or paused source %u", Source->id); + } + std::deque<ALbufferQueueItem> oldlist; + if(values[0]) + { + using UT = std::make_unsigned_t<T>; + std::lock_guard<std::mutex> _{device->BufferLock}; + ALbuffer *buffer{LookupBuffer(device, static_cast<UT>(values[0]))}; + if(!buffer) UNLIKELY + return Context->setError(AL_INVALID_VALUE, "Invalid buffer ID %s", + std::to_string(values[0]).c_str()); + if(buffer->MappedAccess && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) UNLIKELY + return Context->setError(AL_INVALID_OPERATION, + "Setting non-persistently mapped buffer %u", buffer->id); + if(buffer->mCallback && ReadRef(buffer->ref) != 0) UNLIKELY + return Context->setError(AL_INVALID_OPERATION, + "Setting already-set callback buffer %u", buffer->id); + + /* Add the selected buffer to a one-item queue */ + std::deque<ALbufferQueueItem> newlist; + newlist.emplace_back(); + newlist.back().mCallback = buffer->mCallback; + newlist.back().mUserData = buffer->mUserData; + newlist.back().mBlockAlign = buffer->mBlockAlign; + newlist.back().mSampleLen = buffer->mSampleLen; + newlist.back().mLoopStart = buffer->mLoopStart; + newlist.back().mLoopEnd = buffer->mLoopEnd; + newlist.back().mSamples = buffer->mData.data(); + newlist.back().mBuffer = buffer; + IncrementRef(buffer->ref); + + /* Source is now Static */ + Source->SourceType = AL_STATIC; + Source->mQueue.swap(oldlist); + Source->mQueue.swap(newlist); + } + else + { + /* Source is now Undetermined */ + Source->SourceType = AL_UNDETERMINED; + Source->mQueue.swap(oldlist); + } + + /* Delete all elements in the previous queue */ + for(auto &item : oldlist) + { + if(ALbuffer *buffer{item.mBuffer}) + DecrementRef(buffer->ref); + } + return; + } + break; + + case AL_SEC_OFFSET: case AL_SAMPLE_OFFSET: case AL_BYTE_OFFSET: CheckSize(1); - CheckValue(std::isfinite(values[0])); + if constexpr(std::is_floating_point_v<T>) + CheckValue(std::isfinite(values[0])); if(Voice *voice{GetSourceVoice(Source, Context)}) { - auto vpos = GetSampleOffset(Source->mQueue, prop, values[0]); + auto vpos = GetSampleOffset(Source->mQueue, prop, static_cast<double>(values[0])); if(!vpos) return Context->setError(AL_INVALID_VALUE, "Invalid offset"); if(SetVoiceOffset(voice, *vpos, Source, Context, Context->mALDevice.get())) return; } Source->OffsetType = prop; - Source->Offset = values[0]; + Source->Offset = static_cast<double>(values[0]); return; case AL_SAMPLE_RW_OFFSETS_SOFT: + if(sBufferSubDataCompat) + { + if constexpr(std::is_integral_v<T>) + { + /* Query only */ + return Context->setError(AL_INVALID_OPERATION, + "Setting read-only source property 0x%04x", prop); + } + } break; case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/ if(sBufferSubDataCompat) + { + if constexpr(std::is_integral_v<T>) + { + /* Query only */ + return Context->setError(AL_INVALID_OPERATION, + "Setting read-only source property 0x%04x", prop); + } break; + } CheckSize(1); - CheckValue(values[0] >= 0.0f && std::isfinite(values[0])); + if constexpr(std::is_floating_point_v<T>) + CheckValue(values[0] >= T{0} && std::isfinite(static_cast<float>(values[0]))); + else + CheckValue(values[0] >= T{0}); - Source->Radius = values[0]; + Source->Radius = static_cast<float>(values[0]); return UpdateSourceProps(Source, Context); case AL_SUPER_STEREO_WIDTH_SOFT: CheckSize(1); - CheckValue(values[0] >= 0.0f && values[0] <= 1.0f); + CheckValue(values[0] >= T{0} && values[0] <= T{1}); - Source->EnhWidth = values[0]; + Source->EnhWidth = static_cast<float>(values[0]); return UpdateSourceProps(Source, Context); case AL_STEREO_ANGLES: CheckSize(2); - CheckValue(std::isfinite(values[0]) && std::isfinite(values[1])); + if constexpr(std::is_floating_point_v<T>) + CheckValue(std::isfinite(static_cast<float>(values[0])) + && std::isfinite(static_cast<float>(values[1]))); - Source->StereoPan[0] = values[0]; - Source->StereoPan[1] = values[1]; + Source->StereoPan[0] = static_cast<float>(values[0]); + Source->StereoPan[1] = static_cast<float>(values[1]); return UpdateSourceProps(Source, Context); case AL_POSITION: CheckSize(3); - CheckValue(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2])); - - Source->Position[0] = values[0]; - Source->Position[1] = values[1]; - Source->Position[2] = values[2]; + if constexpr(std::is_floating_point_v<T>) + CheckValue(std::isfinite(static_cast<float>(values[0])) + && std::isfinite(static_cast<float>(values[1])) + && std::isfinite(static_cast<float>(values[2]))); + + Source->Position[0] = static_cast<float>(values[0]); + Source->Position[1] = static_cast<float>(values[1]); + Source->Position[2] = static_cast<float>(values[2]); return CommitAndUpdateSourceProps(Source, Context); case AL_VELOCITY: CheckSize(3); - CheckValue(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2])); - - Source->Velocity[0] = values[0]; - Source->Velocity[1] = values[1]; - Source->Velocity[2] = values[2]; + if constexpr(std::is_floating_point_v<T>) + CheckValue(std::isfinite(static_cast<float>(values[0])) + && std::isfinite(static_cast<float>(values[1])) + && std::isfinite(static_cast<float>(values[2]))); + + Source->Velocity[0] = static_cast<float>(values[0]); + Source->Velocity[1] = static_cast<float>(values[1]); + Source->Velocity[2] = static_cast<float>(values[2]); return CommitAndUpdateSourceProps(Source, Context); case AL_DIRECTION: CheckSize(3); - CheckValue(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2])); - - Source->Direction[0] = values[0]; - Source->Direction[1] = values[1]; - Source->Direction[2] = values[2]; + if constexpr(std::is_floating_point_v<T>) + CheckValue(std::isfinite(static_cast<float>(values[0])) + && std::isfinite(static_cast<float>(values[1])) + && std::isfinite(static_cast<float>(values[2]))); + + Source->Direction[0] = static_cast<float>(values[0]); + Source->Direction[1] = static_cast<float>(values[1]); + Source->Direction[2] = static_cast<float>(values[2]); return CommitAndUpdateSourceProps(Source, Context); case AL_ORIENTATION: CheckSize(6); - CheckValue(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]) - && std::isfinite(values[3]) && std::isfinite(values[4]) && std::isfinite(values[5])); - - Source->OrientAt[0] = values[0]; - Source->OrientAt[1] = values[1]; - Source->OrientAt[2] = values[2]; - Source->OrientUp[0] = values[3]; - Source->OrientUp[1] = values[4]; - Source->OrientUp[2] = values[5]; + if constexpr(std::is_floating_point_v<T>) + CheckValue(std::isfinite(static_cast<float>(values[0])) + && std::isfinite(static_cast<float>(values[1])) + && std::isfinite(static_cast<float>(values[2])) + && std::isfinite(static_cast<float>(values[3])) + && std::isfinite(static_cast<float>(values[4])) + && std::isfinite(static_cast<float>(values[5]))); + + Source->OrientAt[0] = static_cast<float>(values[0]); + Source->OrientAt[1] = static_cast<float>(values[1]); + Source->OrientAt[2] = static_cast<float>(values[2]); + Source->OrientUp[0] = static_cast<float>(values[3]); + Source->OrientUp[1] = static_cast<float>(values[4]); + Source->OrientUp[2] = static_cast<float>(values[5]); return UpdateSourceProps(Source, Context); - case AL_SOURCE_RELATIVE: - case AL_LOOPING: - case AL_SOURCE_STATE: - case AL_SOURCE_TYPE: - case AL_DISTANCE_MODEL: - case AL_DIRECT_FILTER_GAINHF_AUTO: - case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: - case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: - case AL_DIRECT_CHANNELS_SOFT: - case AL_SOURCE_RESAMPLER_SOFT: - case AL_SOURCE_SPATIALIZE_SOFT: - case AL_BYTE_LENGTH_SOFT: - case AL_SAMPLE_LENGTH_SOFT: - case AL_STEREO_MODE_SOFT: - CheckSize(1); - ival = static_cast<int>(values[0]); - return SetSourceiv(Source, Context, prop, {&ival, 1u}); - - case AL_BUFFERS_QUEUED: - case AL_BUFFERS_PROCESSED: - CheckSize(1); - ival = static_cast<int>(static_cast<ALuint>(values[0])); - return SetSourceiv(Source, Context, prop, {&ival, 1u}); - - case AL_BUFFER: case AL_DIRECT_FILTER: - case AL_AUXILIARY_SEND_FILTER: - case AL_SAMPLE_OFFSET_LATENCY_SOFT: - case AL_SAMPLE_OFFSET_CLOCK_SOFT: - break; - } - - ERR("Unexpected property: 0x%04x\n", prop); - Context->setError(AL_INVALID_ENUM, "Invalid source float property 0x%04x", prop); -} -catch(check_exception&) { -} - -void SetSourceiv(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, - const al::span<const int> values) -try { - auto Checkers = GetCheckers(Context, prop, values); - auto &CheckSize = Checkers.first; - auto &CheckValue = Checkers.second; - ALCdevice *device{Context->mALDevice.get()}; - ALeffectslot *slot{nullptr}; - al::deque<ALbufferQueueItem> oldlist; - std::unique_lock<std::mutex> slotlock; - float fvals[6]; - - switch(prop) - { - case AL_SOURCE_STATE: - case AL_SOURCE_TYPE: - case AL_BUFFERS_QUEUED: - case AL_BUFFERS_PROCESSED: - case AL_BYTE_LENGTH_SOFT: - case AL_SAMPLE_LENGTH_SOFT: - /* Query only */ - return Context->setError(AL_INVALID_OPERATION, - "Setting read-only source property 0x%04x", prop); - - case AL_SOURCE_RELATIVE: - CheckSize(1); - CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE); - - Source->HeadRelative = values[0] != AL_FALSE; - return CommitAndUpdateSourceProps(Source, Context); - - case AL_LOOPING: - CheckSize(1); - CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE); - - Source->Looping = values[0] != AL_FALSE; - if(Voice *voice{GetSourceVoice(Source, Context)}) + if constexpr(std::is_integral_v<T>) { - if(Source->Looping) - voice->mLoopBuffer.store(&Source->mQueue.front(), std::memory_order_release); + CheckSize(1); + const auto filterid = static_cast<std::make_unsigned_t<T>>(values[0]); + if(values[0]) + { + std::lock_guard<std::mutex> _{device->FilterLock}; + ALfilter *filter{LookupFilter(device, filterid)}; + if(!filter) UNLIKELY + return Context->setError(AL_INVALID_VALUE, "Invalid filter ID %s", + std::to_string(filterid).c_str()); + Source->Direct.Gain = filter->Gain; + Source->Direct.GainHF = filter->GainHF; + Source->Direct.HFReference = filter->HFReference; + Source->Direct.GainLF = filter->GainLF; + Source->Direct.LFReference = filter->LFReference; + } else - voice->mLoopBuffer.store(nullptr, std::memory_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. - */ - device->waitForMix(); + { + Source->Direct.Gain = 1.0f; + Source->Direct.GainHF = 1.0f; + Source->Direct.HFReference = LOWPASSFREQREF; + Source->Direct.GainLF = 1.0f; + Source->Direct.LFReference = HIGHPASSFREQREF; + } + return UpdateSourceProps(Source, Context); } - return; + break; - case AL_BUFFER: - CheckSize(1); - { - const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))}; - if(state == AL_PLAYING || state == AL_PAUSED) - return Context->setError(AL_INVALID_OPERATION, - "Setting buffer on playing or paused source %u", Source->id); - } - if(values[0]) - { - std::lock_guard<std::mutex> _{device->BufferLock}; - ALbuffer *buffer{LookupBuffer(device, static_cast<ALuint>(values[0]))}; - if(!buffer) - return Context->setError(AL_INVALID_VALUE, "Invalid buffer ID %u", - static_cast<ALuint>(values[0])); - if(buffer->MappedAccess && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) - return Context->setError(AL_INVALID_OPERATION, - "Setting non-persistently mapped buffer %u", buffer->id); - if(buffer->mCallback && ReadRef(buffer->ref) != 0) - return Context->setError(AL_INVALID_OPERATION, - "Setting already-set callback buffer %u", buffer->id); - - /* Add the selected buffer to a one-item queue */ - al::deque<ALbufferQueueItem> newlist; - newlist.emplace_back(); - newlist.back().mCallback = buffer->mCallback; - newlist.back().mUserData = buffer->mUserData; - newlist.back().mBlockAlign = buffer->mBlockAlign; - newlist.back().mSampleLen = buffer->mSampleLen; - newlist.back().mLoopStart = buffer->mLoopStart; - newlist.back().mLoopEnd = buffer->mLoopEnd; - newlist.back().mSamples = buffer->mData.data(); - newlist.back().mBuffer = buffer; - IncrementRef(buffer->ref); - - /* Source is now Static */ - Source->SourceType = AL_STATIC; - Source->mQueue.swap(oldlist); - Source->mQueue.swap(newlist); - } - else + case AL_DIRECT_FILTER_GAINHF_AUTO: + if constexpr(std::is_integral_v<T>) { - /* Source is now Undetermined */ - Source->SourceType = AL_UNDETERMINED; - Source->mQueue.swap(oldlist); - } + CheckSize(1); + CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE); - /* Delete all elements in the previous queue */ - for(auto &item : oldlist) - { - if(ALbuffer *buffer{item.mBuffer}) - DecrementRef(buffer->ref); + Source->DryGainHFAuto = values[0] != AL_FALSE; + return UpdateSourceProps(Source, Context); } - return; - - case AL_SEC_OFFSET: - case AL_SAMPLE_OFFSET: - case AL_BYTE_OFFSET: - CheckSize(1); + break; - if(Voice *voice{GetSourceVoice(Source, Context)}) + case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: + if constexpr(std::is_integral_v<T>) { - auto vpos = GetSampleOffset(Source->mQueue, prop, values[0]); - if(!vpos) return Context->setError(AL_INVALID_VALUE, "Invalid source offset"); + CheckSize(1); + CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE); - if(SetVoiceOffset(voice, *vpos, Source, Context, device)) - return; - } - Source->OffsetType = prop; - Source->Offset = values[0]; - return; - - case AL_DIRECT_FILTER: - CheckSize(1); - if(values[0]) - { - std::lock_guard<std::mutex> _{device->FilterLock}; - ALfilter *filter{LookupFilter(device, static_cast<ALuint>(values[0]))}; - if(!filter) - return Context->setError(AL_INVALID_VALUE, "Invalid filter ID %u", - static_cast<ALuint>(values[0])); - Source->Direct.Gain = filter->Gain; - Source->Direct.GainHF = filter->GainHF; - Source->Direct.HFReference = filter->HFReference; - Source->Direct.GainLF = filter->GainLF; - Source->Direct.LFReference = filter->LFReference; - } - else - { - Source->Direct.Gain = 1.0f; - Source->Direct.GainHF = 1.0f; - Source->Direct.HFReference = LOWPASSFREQREF; - Source->Direct.GainLF = 1.0f; - Source->Direct.LFReference = HIGHPASSFREQREF; + Source->WetGainAuto = values[0] != AL_FALSE; + return UpdateSourceProps(Source, Context); } - return UpdateSourceProps(Source, Context); - - case AL_DIRECT_FILTER_GAINHF_AUTO: - CheckSize(1); - CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE); - - Source->DryGainHFAuto = values[0] != AL_FALSE; - return UpdateSourceProps(Source, Context); - - case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: - CheckSize(1); - CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE); - - Source->WetGainAuto = values[0] != AL_FALSE; - return UpdateSourceProps(Source, Context); + break; case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: - CheckSize(1); - CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE); + if constexpr(std::is_integral_v<T>) + { + CheckSize(1); + CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE); - Source->WetGainHFAuto = values[0] != AL_FALSE; - return UpdateSourceProps(Source, Context); + Source->WetGainHFAuto = values[0] != AL_FALSE; + return UpdateSourceProps(Source, Context); + } + break; case AL_DIRECT_CHANNELS_SOFT: - CheckSize(1); - if(auto mode = DirectModeFromEnum(values[0])) + if constexpr(std::is_integral_v<T>) { - Source->DirectChannels = *mode; - return UpdateSourceProps(Source, Context); + CheckSize(1); + if(auto mode = DirectModeFromEnum(values[0])) + { + Source->DirectChannels = *mode; + return UpdateSourceProps(Source, Context); + } + return Context->setError(AL_INVALID_VALUE, "Invalid direct channels mode: %s\n", + HexPrinter{values[0]}.c_str()); } - Context->setError(AL_INVALID_VALUE, "Unsupported AL_DIRECT_CHANNELS_SOFT: 0x%04x\n", - values[0]); - return; + break; case AL_DISTANCE_MODEL: - CheckSize(1); - if(auto model = DistanceModelFromALenum(values[0])) + if constexpr(std::is_integral_v<T>) { - Source->mDistanceModel = *model; - if(Context->mSourceDistanceModel) - UpdateSourceProps(Source, Context); - return; + CheckSize(1); + if(auto model = DistanceModelFromALenum(values[0])) + { + Source->mDistanceModel = *model; + if(Context->mSourceDistanceModel) + UpdateSourceProps(Source, Context); + return; + } + return Context->setError(AL_INVALID_VALUE, "Invalid distance model: %s\n", + HexPrinter{values[0]}.c_str()); } - Context->setError(AL_INVALID_VALUE, "Distance model out of range: 0x%04x", values[0]); - return; + break; case AL_SOURCE_RESAMPLER_SOFT: - CheckSize(1); - CheckValue(values[0] >= 0 && values[0] <= static_cast<int>(Resampler::Max)); - - Source->mResampler = static_cast<Resampler>(values[0]); - return UpdateSourceProps(Source, Context); - - case AL_SOURCE_SPATIALIZE_SOFT: - CheckSize(1); - if(auto mode = SpatializeModeFromEnum(values[0])) + if constexpr(std::is_integral_v<T>) { - Source->mSpatialize = *mode; - return UpdateSourceProps(Source, Context); - } - Context->setError(AL_INVALID_VALUE, "Unsupported AL_SOURCE_SPATIALIZE_SOFT: 0x%04x\n", - values[0]); - return; + CheckSize(1); + CheckValue(values[0] >= 0 && values[0] <= static_cast<int>(Resampler::Max)); - case AL_STEREO_MODE_SOFT: - CheckSize(1); - { - const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))}; - if(state == AL_PLAYING || state == AL_PAUSED) - return Context->setError(AL_INVALID_OPERATION, - "Modifying stereo mode on playing or paused source %u", Source->id); - } - if(auto mode = StereoModeFromEnum(values[0])) - { - Source->mStereoMode = *mode; - return; - } - Context->setError(AL_INVALID_VALUE, "Unsupported AL_STEREO_MODE_SOFT: 0x%04x\n", - values[0]); - return; - - case AL_AUXILIARY_SEND_FILTER: - CheckSize(3); - slotlock = std::unique_lock<std::mutex>{Context->mEffectSlotLock}; - if(values[0] && (slot=LookupEffectSlot(Context, static_cast<ALuint>(values[0]))) == nullptr) - return Context->setError(AL_INVALID_VALUE, "Invalid effect ID %u", values[0]); - if(static_cast<ALuint>(values[1]) >= device->NumAuxSends) - return Context->setError(AL_INVALID_VALUE, "Invalid send %u", values[1]); - - if(values[2]) - { - std::lock_guard<std::mutex> _{device->FilterLock}; - ALfilter *filter{LookupFilter(device, static_cast<ALuint>(values[2]))}; - if(!filter) - return Context->setError(AL_INVALID_VALUE, "Invalid filter ID %u", values[2]); - - auto &send = Source->Send[static_cast<ALuint>(values[1])]; - send.Gain = filter->Gain; - send.GainHF = filter->GainHF; - send.HFReference = filter->HFReference; - send.GainLF = filter->GainLF; - send.LFReference = filter->LFReference; - } - else - { - /* Disable filter */ - auto &send = Source->Send[static_cast<ALuint>(values[1])]; - send.Gain = 1.0f; - send.GainHF = 1.0f; - send.HFReference = LOWPASSFREQREF; - send.GainLF = 1.0f; - send.LFReference = HIGHPASSFREQREF; + Source->mResampler = static_cast<Resampler>(values[0]); + return UpdateSourceProps(Source, Context); } + break; - /* We must force an update if the current auxiliary slot is valid and - * about to be changed on an active source, in case the old slot is - * about to be deleted. - */ - if(Source->Send[static_cast<ALuint>(values[1])].Slot - && slot != Source->Send[static_cast<ALuint>(values[1])].Slot - && IsPlayingOrPaused(Source)) - { - /* Add refcount on the new slot, and release the previous slot */ - if(slot) IncrementRef(slot->ref); - if(auto *oldslot = Source->Send[static_cast<ALuint>(values[1])].Slot) - DecrementRef(oldslot->ref); - Source->Send[static_cast<ALuint>(values[1])].Slot = slot; - - Voice *voice{GetSourceVoice(Source, Context)}; - if(voice) UpdateSourceProps(Source, voice, Context); - else Source->mPropsDirty = true; - } - else + case AL_SOURCE_SPATIALIZE_SOFT: + if constexpr(std::is_integral_v<T>) { - if(slot) IncrementRef(slot->ref); - if(auto *oldslot = Source->Send[static_cast<ALuint>(values[1])].Slot) - DecrementRef(oldslot->ref); - Source->Send[static_cast<ALuint>(values[1])].Slot = slot; - UpdateSourceProps(Source, Context); + CheckSize(1); + if(auto mode = SpatializeModeFromEnum(values[0])) + { + Source->mSpatialize = *mode; + return UpdateSourceProps(Source, Context); + } + return Context->setError(AL_INVALID_VALUE, "Invalid source spatialize mode: %s\n", + HexPrinter{values[0]}.c_str()); } - return; - - - case AL_SAMPLE_RW_OFFSETS_SOFT: - if(sBufferSubDataCompat) - /* Query only */ - return Context->setError(AL_INVALID_OPERATION, - "Setting read-only source property 0x%04x", prop); - break; - - case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/ - if(sBufferSubDataCompat) - return Context->setError(AL_INVALID_OPERATION, - "Setting read-only source property 0x%04x", prop); - /*fall-through*/ - - /* 1x float */ - case AL_CONE_INNER_ANGLE: - case AL_CONE_OUTER_ANGLE: - case AL_PITCH: - case AL_GAIN: - case AL_MIN_GAIN: - case AL_MAX_GAIN: - case AL_REFERENCE_DISTANCE: - case AL_ROLLOFF_FACTOR: - case AL_CONE_OUTER_GAIN: - case AL_MAX_DISTANCE: - case AL_DOPPLER_FACTOR: - case AL_CONE_OUTER_GAINHF: - case AL_AIR_ABSORPTION_FACTOR: - case AL_ROOM_ROLLOFF_FACTOR: - case AL_SEC_LENGTH_SOFT: - case AL_SUPER_STEREO_WIDTH_SOFT: - CheckSize(1); - fvals[0] = static_cast<float>(values[0]); - return SetSourcefv(Source, Context, prop, {fvals, 1u}); - - /* 3x float */ - case AL_POSITION: - case AL_VELOCITY: - case AL_DIRECTION: - CheckSize(3); - fvals[0] = static_cast<float>(values[0]); - fvals[1] = static_cast<float>(values[1]); - fvals[2] = static_cast<float>(values[2]); - return SetSourcefv(Source, Context, prop, {fvals, 3u}); - - /* 6x float */ - case AL_ORIENTATION: - CheckSize(6); - fvals[0] = static_cast<float>(values[0]); - fvals[1] = static_cast<float>(values[1]); - fvals[2] = static_cast<float>(values[2]); - fvals[3] = static_cast<float>(values[3]); - fvals[4] = static_cast<float>(values[4]); - fvals[5] = static_cast<float>(values[5]); - return SetSourcefv(Source, Context, prop, {fvals, 6u}); - - case AL_SAMPLE_OFFSET_LATENCY_SOFT: - case AL_SEC_OFFSET_LATENCY_SOFT: - case AL_SEC_OFFSET_CLOCK_SOFT: - case AL_SAMPLE_OFFSET_CLOCK_SOFT: - case AL_STEREO_ANGLES: break; - } - ERR("Unexpected property: 0x%04x\n", prop); - Context->setError(AL_INVALID_ENUM, "Invalid source integer property 0x%04x", prop); -} -catch(check_exception&) { -} - -void SetSourcei64v(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, - const al::span<const int64_t> values) -try { - auto Checkers = GetCheckers(Context, prop, values); - auto &CheckSize = Checkers.first; - auto &CheckValue = Checkers.second; - float fvals[MaxValues]; - int ivals[MaxValues]; - - switch(prop) - { - case AL_SOURCE_TYPE: - case AL_BUFFERS_QUEUED: - case AL_BUFFERS_PROCESSED: - case AL_SOURCE_STATE: - case AL_BYTE_LENGTH_SOFT: - case AL_SAMPLE_LENGTH_SOFT: - case AL_SAMPLE_OFFSET_LATENCY_SOFT: - case AL_SAMPLE_OFFSET_CLOCK_SOFT: - /* Query only */ - return Context->setError(AL_INVALID_OPERATION, - "Setting read-only source property 0x%04x", prop); - - /* 1x int */ - case AL_SOURCE_RELATIVE: - case AL_LOOPING: - case AL_SEC_OFFSET: - case AL_SAMPLE_OFFSET: - case AL_BYTE_OFFSET: - case AL_DIRECT_FILTER_GAINHF_AUTO: - case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: - case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: - case AL_DIRECT_CHANNELS_SOFT: - case AL_DISTANCE_MODEL: - case AL_SOURCE_RESAMPLER_SOFT: - case AL_SOURCE_SPATIALIZE_SOFT: case AL_STEREO_MODE_SOFT: - CheckSize(1); - CheckValue(values[0] <= INT_MAX && values[0] >= INT_MIN); - - ivals[0] = static_cast<int>(values[0]); - return SetSourceiv(Source, Context, prop, {ivals, 1u}); - - /* 1x uint */ - case AL_BUFFER: - case AL_DIRECT_FILTER: - CheckSize(1); - CheckValue(values[0] <= UINT_MAX && values[0] >= 0); - - ivals[0] = static_cast<int>(values[0]); - return SetSourceiv(Source, Context, prop, {ivals, 1u}); - - /* 3x uint */ - case AL_AUXILIARY_SEND_FILTER: - CheckSize(3); - CheckValue(values[0] <= UINT_MAX && values[0] >= 0 && values[1] <= UINT_MAX - && values[1] >= 0 && values[2] <= UINT_MAX && values[2] >= 0); - - ivals[0] = static_cast<int>(values[0]); - ivals[1] = static_cast<int>(values[1]); - ivals[2] = static_cast<int>(values[2]); - return SetSourceiv(Source, Context, prop, {ivals, 3u}); - - case AL_SAMPLE_RW_OFFSETS_SOFT: - if(sBufferSubDataCompat) + if constexpr(std::is_integral_v<T>) { - /* Query only */ - return Context->setError(AL_INVALID_OPERATION, - "Setting read-only source property 0x%04x", prop); + CheckSize(1); + { + const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))}; + if(state == AL_PLAYING || state == AL_PAUSED) + return Context->setError(AL_INVALID_OPERATION, + "Modifying stereo mode on playing or paused source %u", Source->id); + } + if(auto mode = StereoModeFromEnum(values[0])) + { + Source->mStereoMode = *mode; + return; + } + return Context->setError(AL_INVALID_VALUE, "Invalid stereo mode: %s\n", + HexPrinter{values[0]}.c_str()); } break; - case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/ - if(sBufferSubDataCompat) - return Context->setError(AL_INVALID_OPERATION, - "Setting read-only source property 0x%04x", prop); - /*fall-through*/ - - /* 1x float */ - case AL_CONE_INNER_ANGLE: - case AL_CONE_OUTER_ANGLE: - case AL_PITCH: - case AL_GAIN: - case AL_MIN_GAIN: - case AL_MAX_GAIN: - case AL_REFERENCE_DISTANCE: - case AL_ROLLOFF_FACTOR: - case AL_CONE_OUTER_GAIN: - case AL_MAX_DISTANCE: - case AL_DOPPLER_FACTOR: - case AL_CONE_OUTER_GAINHF: - case AL_AIR_ABSORPTION_FACTOR: - case AL_ROOM_ROLLOFF_FACTOR: - case AL_SEC_LENGTH_SOFT: - case AL_SUPER_STEREO_WIDTH_SOFT: - CheckSize(1); - fvals[0] = static_cast<float>(values[0]); - return SetSourcefv(Source, Context, prop, {fvals, 1u}); + case AL_AUXILIARY_SEND_FILTER: + if constexpr(std::is_integral_v<T>) + { + CheckSize(3); + const auto slotid = static_cast<std::make_unsigned_t<T>>(values[0]); + const auto sendidx = static_cast<std::make_unsigned_t<T>>(values[1]); + const auto filterid = static_cast<std::make_unsigned_t<T>>(values[2]); + + std::unique_lock slotlock{Context->mEffectSlotLock}; + ALeffectslot *slot{}; + if(values[0]) + { + if((slot=LookupEffectSlot(Context, slotid)) == nullptr) UNLIKELY + return Context->setError(AL_INVALID_VALUE, "Invalid effect ID %s", + std::to_string(slotid).c_str()); + } - /* 3x float */ - case AL_POSITION: - case AL_VELOCITY: - case AL_DIRECTION: - CheckSize(3); - fvals[0] = static_cast<float>(values[0]); - fvals[1] = static_cast<float>(values[1]); - fvals[2] = static_cast<float>(values[2]); - return SetSourcefv(Source, Context, prop, {fvals, 3u}); + if(sendidx >= device->NumAuxSends) UNLIKELY + return Context->setError(AL_INVALID_VALUE, "Invalid send %s", + std::to_string(sendidx).c_str()); + auto &send = Source->Send[static_cast<size_t>(sendidx)]; - /* 6x float */ - case AL_ORIENTATION: - CheckSize(6); - fvals[0] = static_cast<float>(values[0]); - fvals[1] = static_cast<float>(values[1]); - fvals[2] = static_cast<float>(values[2]); - fvals[3] = static_cast<float>(values[3]); - fvals[4] = static_cast<float>(values[4]); - fvals[5] = static_cast<float>(values[5]); - return SetSourcefv(Source, Context, prop, {fvals, 6u}); + if(values[2]) + { + std::lock_guard<std::mutex> _{device->FilterLock}; + ALfilter *filter{LookupFilter(device, filterid)}; + if(!filter) UNLIKELY + return Context->setError(AL_INVALID_VALUE, "Invalid filter ID %s", + std::to_string(filterid).c_str()); + + send.Gain = filter->Gain; + send.GainHF = filter->GainHF; + send.HFReference = filter->HFReference; + send.GainLF = filter->GainLF; + send.LFReference = filter->LFReference; + } + else + { + /* Disable filter */ + send.Gain = 1.0f; + send.GainHF = 1.0f; + send.HFReference = LOWPASSFREQREF; + send.GainLF = 1.0f; + send.LFReference = HIGHPASSFREQREF; + } - case AL_SEC_OFFSET_LATENCY_SOFT: - case AL_SEC_OFFSET_CLOCK_SOFT: - case AL_STEREO_ANGLES: + /* We must force an update if the current auxiliary slot is valid + * and about to be changed on an active source, in case the old + * slot is about to be deleted. + */ + if(send.Slot && slot != send.Slot && IsPlayingOrPaused(Source)) + { + /* Add refcount on the new slot, and release the previous slot */ + if(slot) IncrementRef(slot->ref); + if(auto *oldslot = send.Slot) + DecrementRef(oldslot->ref); + send.Slot = slot; + + Voice *voice{GetSourceVoice(Source, Context)}; + if(voice) UpdateSourceProps(Source, voice, Context); + else Source->mPropsDirty = true; + } + else + { + if(slot) IncrementRef(slot->ref); + if(auto *oldslot = send.Slot) + DecrementRef(oldslot->ref); + send.Slot = slot; + UpdateSourceProps(Source, Context); + } + return; + } break; } - ERR("Unexpected property: 0x%04x\n", prop); - Context->setError(AL_INVALID_ENUM, "Invalid source integer64 property 0x%04x", prop); + ERR("Unexpected %s property: 0x%04x\n", PropType<T>::Name(), prop); + Context->setError(AL_INVALID_ENUM, "Invalid source %s property 0x%04x", PropType<T>::Name(), + prop); } catch(check_exception&) { } @@ -2100,245 +2004,292 @@ auto GetSizeChecker(ALCcontext *const Context, const SourceProp prop, const al:: }; } -bool GetSourcedv(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, const al::span<double> values); -bool GetSourceiv(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, const al::span<int> values); -bool GetSourcei64v(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, const al::span<int64_t> values); - -bool GetSourcedv(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, - const al::span<double> values) -try { +template<typename T> +[[nodiscard]] NOINLINE +bool GetProperty(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, + const al::span<T> values) try +{ + using std::chrono::duration_cast; auto CheckSize = GetSizeChecker(Context, prop, values); ALCdevice *device{Context->mALDevice.get()}; - ClockLatency clocktime; - nanoseconds srcclock; - int ivals[MaxValues]; - bool err; switch(prop) { case AL_GAIN: CheckSize(1); - values[0] = Source->Gain; + values[0] = static_cast<T>(Source->Gain); return true; case AL_PITCH: CheckSize(1); - values[0] = Source->Pitch; + values[0] = static_cast<T>(Source->Pitch); return true; case AL_MAX_DISTANCE: CheckSize(1); - values[0] = Source->MaxDistance; + values[0] = static_cast<T>(Source->MaxDistance); return true; case AL_ROLLOFF_FACTOR: CheckSize(1); - values[0] = Source->RolloffFactor; + values[0] = static_cast<T>(Source->RolloffFactor); return true; case AL_REFERENCE_DISTANCE: CheckSize(1); - values[0] = Source->RefDistance; + values[0] = static_cast<T>(Source->RefDistance); return true; case AL_CONE_INNER_ANGLE: CheckSize(1); - values[0] = Source->InnerAngle; + values[0] = static_cast<T>(Source->InnerAngle); return true; case AL_CONE_OUTER_ANGLE: CheckSize(1); - values[0] = Source->OuterAngle; + values[0] = static_cast<T>(Source->OuterAngle); return true; case AL_MIN_GAIN: CheckSize(1); - values[0] = Source->MinGain; + values[0] = static_cast<T>(Source->MinGain); return true; case AL_MAX_GAIN: CheckSize(1); - values[0] = Source->MaxGain; + values[0] = static_cast<T>(Source->MaxGain); return true; case AL_CONE_OUTER_GAIN: CheckSize(1); - values[0] = Source->OuterGain; + values[0] = static_cast<T>(Source->OuterGain); return true; case AL_SEC_OFFSET: case AL_SAMPLE_OFFSET: case AL_BYTE_OFFSET: CheckSize(1); - values[0] = GetSourceOffset(Source, prop, Context); + values[0] = GetSourceOffset<T>(Source, prop, Context); return true; case AL_CONE_OUTER_GAINHF: CheckSize(1); - values[0] = Source->OuterGainHF; + values[0] = static_cast<T>(Source->OuterGainHF); return true; case AL_AIR_ABSORPTION_FACTOR: CheckSize(1); - values[0] = Source->AirAbsorptionFactor; + values[0] = static_cast<T>(Source->AirAbsorptionFactor); return true; case AL_ROOM_ROLLOFF_FACTOR: CheckSize(1); - values[0] = Source->RoomRolloffFactor; + values[0] = static_cast<T>(Source->RoomRolloffFactor); return true; case AL_DOPPLER_FACTOR: CheckSize(1); - values[0] = Source->DopplerFactor; + values[0] = static_cast<T>(Source->DopplerFactor); return true; case AL_SAMPLE_RW_OFFSETS_SOFT: + if constexpr(std::is_integral_v<T>) + { + if(sBufferSubDataCompat) + { + CheckSize(2); + values[0] = GetSourceOffset<T>(Source, AL_SAMPLE_OFFSET, Context); + /* FIXME: values[1] should be ahead of values[0] by the device + * update time. It needs to clamp or wrap the length of the + * buffer queue. + */ + values[1] = values[0]; + return true; + } + } break; case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/ - if(sBufferSubDataCompat) - break; + if constexpr(std::is_floating_point_v<T>) + { + if(sBufferSubDataCompat) + break; - CheckSize(1); - values[0] = Source->Radius; - return true; + CheckSize(1); + values[0] = static_cast<T>(Source->Radius); + return true; + } + else + { + if(sBufferSubDataCompat) + { + CheckSize(2); + values[0] = GetSourceOffset<T>(Source, AL_BYTE_OFFSET, Context); + /* FIXME: values[1] should be ahead of values[0] by the device + * update time. It needs to clamp or wrap the length of the + * buffer queue. + */ + values[1] = values[0]; + return true; + } + break; + } case AL_SUPER_STEREO_WIDTH_SOFT: CheckSize(1); - values[0] = Source->EnhWidth; + values[0] = static_cast<T>(Source->EnhWidth); return true; case AL_BYTE_LENGTH_SOFT: case AL_SAMPLE_LENGTH_SOFT: case AL_SEC_LENGTH_SOFT: CheckSize(1); - values[0] = GetSourceLength(Source, prop); + values[0] = GetSourceLength<T>(Source, prop); return true; case AL_STEREO_ANGLES: - CheckSize(2); - values[0] = Source->StereoPan[0]; - values[1] = Source->StereoPan[1]; - return true; + if constexpr(std::is_floating_point_v<T>) + { + CheckSize(2); + values[0] = static_cast<T>(Source->StereoPan[0]); + values[1] = static_cast<T>(Source->StereoPan[1]); + return true; + } + break; - case AL_SEC_OFFSET_LATENCY_SOFT: - CheckSize(2); - /* 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); + case AL_SAMPLE_OFFSET_LATENCY_SOFT: + if constexpr(std::is_same_v<T,int64_t>) { - std::lock_guard<std::mutex> _{device->StateLock}; - clocktime = GetClockLatency(device, device->Backend.get()); + CheckSize(2); + /* Get the source offset with the clock time first. Then get the + * clock time with the device latency. Order is important. + */ + ClockLatency clocktime{}; + nanoseconds srcclock{}; + values[0] = GetSourceSampleOffset(Source, Context, &srcclock); + { + std::lock_guard<std::mutex> _{device->StateLock}; + clocktime = GetClockLatency(device, device->Backend.get()); + } + if(srcclock == clocktime.ClockTime) + values[1] = nanoseconds{clocktime.Latency}.count(); + 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. + */ + const auto diff = std::min(clocktime.Latency, clocktime.ClockTime-srcclock); + values[1] = nanoseconds{clocktime.Latency - diff}.count(); + } + return true; } - if(srcclock == clocktime.ClockTime) - values[1] = static_cast<double>(clocktime.Latency.count()) / 1000000000.0; - else + break; + + case AL_SAMPLE_OFFSET_CLOCK_SOFT: + if constexpr(std::is_same_v<T,int64_t>) + { + CheckSize(2); + nanoseconds srcclock{}; + values[0] = GetSourceSampleOffset(Source, Context, &srcclock); + values[1] = srcclock.count(); + return true; + } + break; + + case AL_SEC_OFFSET_LATENCY_SOFT: + if constexpr(std::is_same_v<T,double>) { - /* If the clock time incremented, reduce the latency by that much - * since it's that much closer to the source offset it got earlier. + CheckSize(2); + /* Get the source offset with the clock time first. Then get the + * clock time with the device latency. Order is important. */ - const nanoseconds diff{clocktime.ClockTime - srcclock}; - const nanoseconds latency{clocktime.Latency - std::min(clocktime.Latency, diff)}; - values[1] = static_cast<double>(latency.count()) / 1000000000.0; + ClockLatency clocktime{}; + nanoseconds srcclock{}; + values[0] = GetSourceSecOffset(Source, Context, &srcclock); + { + std::lock_guard<std::mutex> _{device->StateLock}; + clocktime = GetClockLatency(device, device->Backend.get()); + } + if(srcclock == clocktime.ClockTime) + values[1] = duration_cast<seconds_d>(clocktime.Latency).count(); + 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. + */ + const auto diff = std::min(clocktime.Latency, clocktime.ClockTime-srcclock); + values[1] = duration_cast<seconds_d>(clocktime.Latency - diff).count(); + } + return true; } - return true; + break; case AL_SEC_OFFSET_CLOCK_SOFT: - CheckSize(2); - values[0] = GetSourceSecOffset(Source, Context, &srcclock); - values[1] = static_cast<double>(srcclock.count()) / 1000000000.0; - return true; + if constexpr(std::is_same_v<T,double>) + { + CheckSize(2); + nanoseconds srcclock{}; + values[0] = GetSourceSecOffset(Source, Context, &srcclock); + values[1] = duration_cast<seconds_d>(srcclock).count(); + return true; + } + break; case AL_POSITION: CheckSize(3); - values[0] = Source->Position[0]; - values[1] = Source->Position[1]; - values[2] = Source->Position[2]; + values[0] = static_cast<T>(Source->Position[0]); + values[1] = static_cast<T>(Source->Position[1]); + values[2] = static_cast<T>(Source->Position[2]); return true; case AL_VELOCITY: CheckSize(3); - values[0] = Source->Velocity[0]; - values[1] = Source->Velocity[1]; - values[2] = Source->Velocity[2]; + values[0] = static_cast<T>(Source->Velocity[0]); + values[1] = static_cast<T>(Source->Velocity[1]); + values[2] = static_cast<T>(Source->Velocity[2]); return true; case AL_DIRECTION: CheckSize(3); - values[0] = Source->Direction[0]; - values[1] = Source->Direction[1]; - values[2] = Source->Direction[2]; + values[0] = static_cast<T>(Source->Direction[0]); + values[1] = static_cast<T>(Source->Direction[1]); + values[2] = static_cast<T>(Source->Direction[2]); return true; case AL_ORIENTATION: CheckSize(6); - values[0] = Source->OrientAt[0]; - values[1] = Source->OrientAt[1]; - values[2] = Source->OrientAt[2]; - values[3] = Source->OrientUp[0]; - values[4] = Source->OrientUp[1]; - values[5] = Source->OrientUp[2]; + values[0] = static_cast<T>(Source->OrientAt[0]); + values[1] = static_cast<T>(Source->OrientAt[1]); + values[2] = static_cast<T>(Source->OrientAt[2]); + values[3] = static_cast<T>(Source->OrientUp[0]); + values[4] = static_cast<T>(Source->OrientUp[1]); + values[5] = static_cast<T>(Source->OrientUp[2]); return true; - /* 1x int */ - case AL_SOURCE_RELATIVE: - case AL_LOOPING: - case AL_SOURCE_STATE: - case AL_BUFFERS_QUEUED: - case AL_BUFFERS_PROCESSED: - case AL_SOURCE_TYPE: - case AL_DIRECT_FILTER_GAINHF_AUTO: - case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: - case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: - case AL_DIRECT_CHANNELS_SOFT: - case AL_DISTANCE_MODEL: - case AL_SOURCE_RESAMPLER_SOFT: - case AL_SOURCE_SPATIALIZE_SOFT: - case AL_STEREO_MODE_SOFT: - CheckSize(1); - if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false) - values[0] = static_cast<double>(ivals[0]); - return err; - - case AL_BUFFER: - case AL_DIRECT_FILTER: - case AL_AUXILIARY_SEND_FILTER: - case AL_SAMPLE_OFFSET_LATENCY_SOFT: - case AL_SAMPLE_OFFSET_CLOCK_SOFT: - break; - } - - ERR("Unexpected property: 0x%04x\n", prop); - Context->setError(AL_INVALID_ENUM, "Invalid source double property 0x%04x", prop); - return false; -} -catch(check_exception&) { - return false; -} - -bool GetSourceiv(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, - const al::span<int> values) -try { - auto CheckSize = GetSizeChecker(Context, prop, values); - double dvals[MaxValues]; - bool err; - switch(prop) - { case AL_SOURCE_RELATIVE: - CheckSize(1); - values[0] = Source->HeadRelative; - return true; + if constexpr(std::is_integral_v<T>) + { + CheckSize(1); + values[0] = Source->HeadRelative; + return true; + } + break; case AL_LOOPING: - CheckSize(1); - values[0] = Source->Looping; - return true; + if constexpr(std::is_integral_v<T>) + { + CheckSize(1); + values[0] = Source->Looping; + return true; + } + break; case AL_BUFFER: - CheckSize(1); + if constexpr(std::is_integral_v<T>) { + CheckSize(1); ALbufferQueueItem *BufferList{}; /* HACK: This query should technically only return the buffer set * on a static source. However, some apps had used it to detect @@ -2356,377 +2307,150 @@ try { BufferList = static_cast<ALbufferQueueItem*>(Current); } ALbuffer *buffer{BufferList ? BufferList->mBuffer : nullptr}; - values[0] = buffer ? static_cast<int>(buffer->id) : 0; + values[0] = buffer ? static_cast<T>(buffer->id) : T{0}; + return true; } - return true; + break; case AL_SOURCE_STATE: - CheckSize(1); - values[0] = GetSourceState(Source, GetSourceVoice(Source, Context)); - return true; + if constexpr(std::is_integral_v<T>) + { + CheckSize(1); + values[0] = GetSourceState(Source, GetSourceVoice(Source, Context)); + return true; + } + break; case AL_BUFFERS_QUEUED: - CheckSize(1); - values[0] = static_cast<int>(Source->mQueue.size()); - return true; - - case AL_BUFFERS_PROCESSED: - CheckSize(1); - if(Source->Looping || Source->SourceType != AL_STREAMING) + if constexpr(std::is_integral_v<T>) { - /* Buffers on a looping source are in a perpetual state of PENDING, - * so don't report any as PROCESSED - */ - values[0] = 0; + CheckSize(1); + values[0] = static_cast<T>(Source->mQueue.size()); + return true; } - else + break; + + case AL_BUFFERS_PROCESSED: + if constexpr(std::is_integral_v<T>) { - int played{0}; - if(Source->state != AL_INITIAL) + CheckSize(1); + if(Source->Looping || Source->SourceType != AL_STREAMING) + { + /* Buffers on a looping source are in a perpetual state of + * PENDING, so don't report any as PROCESSED + */ + values[0] = 0; + } + else { - const VoiceBufferItem *Current{nullptr}; - if(Voice *voice{GetSourceVoice(Source, Context)}) - Current = voice->mCurrentBuffer.load(std::memory_order_relaxed); - for(auto &item : Source->mQueue) + int played{0}; + if(Source->state != AL_INITIAL) { - if(&item == Current) - break; - ++played; + const VoiceBufferItem *Current{nullptr}; + if(Voice *voice{GetSourceVoice(Source, Context)}) + Current = voice->mCurrentBuffer.load(std::memory_order_relaxed); + for(auto &item : Source->mQueue) + { + if(&item == Current) + break; + ++played; + } } + values[0] = played; } - values[0] = played; + return true; } - return true; + break; case AL_SOURCE_TYPE: - CheckSize(1); - values[0] = Source->SourceType; - return true; - - case AL_DIRECT_FILTER_GAINHF_AUTO: - CheckSize(1); - values[0] = Source->DryGainHFAuto; - return true; - - case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: - CheckSize(1); - values[0] = Source->WetGainAuto; - return true; - - case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: - CheckSize(1); - values[0] = Source->WetGainHFAuto; - return true; - - case AL_DIRECT_CHANNELS_SOFT: - CheckSize(1); - values[0] = EnumFromDirectMode(Source->DirectChannels); - return true; - - case AL_DISTANCE_MODEL: - CheckSize(1); - values[0] = ALenumFromDistanceModel(Source->mDistanceModel); - return true; - - case AL_BYTE_LENGTH_SOFT: - case AL_SAMPLE_LENGTH_SOFT: - case AL_SEC_LENGTH_SOFT: - CheckSize(1); - values[0] = static_cast<int>(mind(GetSourceLength(Source, prop), - std::numeric_limits<int>::max())); - return true; - - case AL_SOURCE_RESAMPLER_SOFT: - CheckSize(1); - values[0] = static_cast<int>(Source->mResampler); - return true; - - case AL_SOURCE_SPATIALIZE_SOFT: - CheckSize(1); - values[0] = EnumFromSpatializeMode(Source->mSpatialize); - return true; - - case AL_STEREO_MODE_SOFT: - CheckSize(1); - values[0] = EnumFromStereoMode(Source->mStereoMode); - return true; - - case AL_SAMPLE_RW_OFFSETS_SOFT: - if(sBufferSubDataCompat) + if constexpr(std::is_integral_v<T>) { - CheckSize(2); - const auto offset = GetSourceOffset(Source, AL_SAMPLE_OFFSET, Context); - /* FIXME: values[1] should be ahead of values[0] by the device - * update time. It needs to clamp or wrap the length of the buffer - * queue. - */ - values[0] = static_cast<int>(mind(offset, std::numeric_limits<int>::max())); - values[1] = values[0]; + CheckSize(1); + values[0] = Source->SourceType; return true; } break; - case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/ - if(sBufferSubDataCompat) + + case AL_DIRECT_FILTER_GAINHF_AUTO: + if constexpr(std::is_integral_v<T>) { - CheckSize(2); - const auto offset = GetSourceOffset(Source, AL_BYTE_OFFSET, Context); - /* FIXME: values[1] should be ahead of values[0] by the device - * update time. It needs to clamp or wrap the length of the buffer - * queue. - */ - values[0] = static_cast<int>(mind(offset, std::numeric_limits<int>::max())); - values[1] = values[0]; + CheckSize(1); + values[0] = Source->DryGainHFAuto; return true; } - /*fall-through*/ - - /* 1x float/double */ - case AL_CONE_INNER_ANGLE: - case AL_CONE_OUTER_ANGLE: - case AL_PITCH: - case AL_GAIN: - case AL_MIN_GAIN: - case AL_MAX_GAIN: - case AL_REFERENCE_DISTANCE: - case AL_ROLLOFF_FACTOR: - case AL_CONE_OUTER_GAIN: - case AL_MAX_DISTANCE: - case AL_SEC_OFFSET: - case AL_SAMPLE_OFFSET: - case AL_BYTE_OFFSET: - case AL_DOPPLER_FACTOR: - case AL_AIR_ABSORPTION_FACTOR: - case AL_ROOM_ROLLOFF_FACTOR: - case AL_CONE_OUTER_GAINHF: - case AL_SUPER_STEREO_WIDTH_SOFT: - CheckSize(1); - if((err=GetSourcedv(Source, Context, prop, {dvals, 1u})) != false) - values[0] = static_cast<int>(dvals[0]); - return err; + break; - /* 3x float/double */ - case AL_POSITION: - case AL_VELOCITY: - case AL_DIRECTION: - CheckSize(3); - if((err=GetSourcedv(Source, Context, prop, {dvals, 3u})) != false) + case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: + if constexpr(std::is_integral_v<T>) { - values[0] = static_cast<int>(dvals[0]); - values[1] = static_cast<int>(dvals[1]); - values[2] = static_cast<int>(dvals[2]); + CheckSize(1); + values[0] = Source->WetGainAuto; + return true; } - return err; + break; - /* 6x float/double */ - case AL_ORIENTATION: - CheckSize(6); - if((err=GetSourcedv(Source, Context, prop, {dvals, 6u})) != false) + case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: + if constexpr(std::is_integral_v<T>) { - values[0] = static_cast<int>(dvals[0]); - values[1] = static_cast<int>(dvals[1]); - values[2] = static_cast<int>(dvals[2]); - values[3] = static_cast<int>(dvals[3]); - values[4] = static_cast<int>(dvals[4]); - values[5] = static_cast<int>(dvals[5]); + CheckSize(1); + values[0] = Source->WetGainHFAuto; + return true; } - return err; - - case AL_SAMPLE_OFFSET_LATENCY_SOFT: - case AL_SAMPLE_OFFSET_CLOCK_SOFT: - break; /* i64 only */ - case AL_SEC_OFFSET_LATENCY_SOFT: - case AL_SEC_OFFSET_CLOCK_SOFT: - break; /* Double only */ - case AL_STEREO_ANGLES: - break; /* Float/double only */ - - case AL_DIRECT_FILTER: - case AL_AUXILIARY_SEND_FILTER: - break; /* ??? */ - } - - ERR("Unexpected property: 0x%04x\n", prop); - Context->setError(AL_INVALID_ENUM, "Invalid source integer property 0x%04x", prop); - return false; -} -catch(check_exception&) { - return false; -} - -bool GetSourcei64v(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, - const al::span<int64_t> values) -try { - auto CheckSize = GetSizeChecker(Context, prop, values); - ALCdevice *device{Context->mALDevice.get()}; - ClockLatency clocktime; - nanoseconds srcclock; - double dvals[MaxValues]; - int ivals[MaxValues]; - bool err; - - switch(prop) - { - case AL_BYTE_LENGTH_SOFT: - case AL_SAMPLE_LENGTH_SOFT: - case AL_SEC_LENGTH_SOFT: - CheckSize(1); - values[0] = static_cast<int64_t>(GetSourceLength(Source, prop)); - return true; + break; - case AL_SAMPLE_OFFSET_LATENCY_SOFT: - CheckSize(2); - /* 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); - { - std::lock_guard<std::mutex> _{device->StateLock}; - clocktime = GetClockLatency(device, device->Backend.get()); - } - if(srcclock == clocktime.ClockTime) - values[1] = clocktime.Latency.count(); - else + case AL_DIRECT_CHANNELS_SOFT: + if constexpr(std::is_integral_v<T>) { - /* If the clock time incremented, reduce the latency by that much - * since it's that much closer to the source offset it got earlier. - */ - const nanoseconds diff{clocktime.ClockTime - srcclock}; - values[1] = nanoseconds{clocktime.Latency - std::min(clocktime.Latency, diff)}.count(); + CheckSize(1); + values[0] = EnumFromDirectMode(Source->DirectChannels); + return true; } - return true; - - case AL_SAMPLE_OFFSET_CLOCK_SOFT: - CheckSize(2); - values[0] = GetSourceSampleOffset(Source, Context, &srcclock); - values[1] = srcclock.count(); - return true; + break; - case AL_SAMPLE_RW_OFFSETS_SOFT: - if(sBufferSubDataCompat) + case AL_DISTANCE_MODEL: + if constexpr(std::is_integral_v<T>) { - CheckSize(2); - /* FIXME: values[1] should be ahead of values[0] by the device - * update time. It needs to clamp or wrap the length of the buffer - * queue. - */ - values[0] = static_cast<int64_t>(GetSourceOffset(Source, AL_SAMPLE_OFFSET, Context)); - values[1] = values[0]; + CheckSize(1); + values[0] = ALenumFromDistanceModel(Source->mDistanceModel); return true; } break; - case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/ - if(sBufferSubDataCompat) + + case AL_SOURCE_RESAMPLER_SOFT: + if constexpr(std::is_integral_v<T>) { - CheckSize(2); - /* FIXME: values[1] should be ahead of values[0] by the device - * update time. It needs to clamp or wrap the length of the buffer - * queue. - */ - values[0] = static_cast<int64_t>(GetSourceOffset(Source, AL_BYTE_OFFSET, Context)); - values[1] = values[0]; + CheckSize(1); + values[0] = static_cast<T>(Source->mResampler); return true; } - /*fall-through*/ - - /* 1x float/double */ - case AL_CONE_INNER_ANGLE: - case AL_CONE_OUTER_ANGLE: - case AL_PITCH: - case AL_GAIN: - case AL_MIN_GAIN: - case AL_MAX_GAIN: - case AL_REFERENCE_DISTANCE: - case AL_ROLLOFF_FACTOR: - case AL_CONE_OUTER_GAIN: - case AL_MAX_DISTANCE: - case AL_SEC_OFFSET: - case AL_SAMPLE_OFFSET: - case AL_BYTE_OFFSET: - case AL_DOPPLER_FACTOR: - case AL_AIR_ABSORPTION_FACTOR: - case AL_ROOM_ROLLOFF_FACTOR: - case AL_CONE_OUTER_GAINHF: - case AL_SUPER_STEREO_WIDTH_SOFT: - CheckSize(1); - if((err=GetSourcedv(Source, Context, prop, {dvals, 1u})) != false) - values[0] = static_cast<int64_t>(dvals[0]); - return err; + break; - /* 3x float/double */ - case AL_POSITION: - case AL_VELOCITY: - case AL_DIRECTION: - CheckSize(3); - if((err=GetSourcedv(Source, Context, prop, {dvals, 3u})) != false) + case AL_SOURCE_SPATIALIZE_SOFT: + if constexpr(std::is_integral_v<T>) { - values[0] = static_cast<int64_t>(dvals[0]); - values[1] = static_cast<int64_t>(dvals[1]); - values[2] = static_cast<int64_t>(dvals[2]); + CheckSize(1); + values[0] = EnumFromSpatializeMode(Source->mSpatialize); + return true; } - return err; + break; - /* 6x float/double */ - case AL_ORIENTATION: - CheckSize(6); - if((err=GetSourcedv(Source, Context, prop, {dvals, 6u})) != false) + case AL_STEREO_MODE_SOFT: + if constexpr(std::is_integral_v<T>) { - values[0] = static_cast<int64_t>(dvals[0]); - values[1] = static_cast<int64_t>(dvals[1]); - values[2] = static_cast<int64_t>(dvals[2]); - values[3] = static_cast<int64_t>(dvals[3]); - values[4] = static_cast<int64_t>(dvals[4]); - values[5] = static_cast<int64_t>(dvals[5]); + CheckSize(1); + values[0] = EnumFromStereoMode(Source->mStereoMode); + return true; } - return err; - - /* 1x int */ - case AL_SOURCE_RELATIVE: - case AL_LOOPING: - case AL_SOURCE_STATE: - case AL_BUFFERS_QUEUED: - case AL_BUFFERS_PROCESSED: - case AL_SOURCE_TYPE: - case AL_DIRECT_FILTER_GAINHF_AUTO: - case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO: - case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO: - case AL_DIRECT_CHANNELS_SOFT: - case AL_DISTANCE_MODEL: - case AL_SOURCE_RESAMPLER_SOFT: - case AL_SOURCE_SPATIALIZE_SOFT: - case AL_STEREO_MODE_SOFT: - CheckSize(1); - if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false) - values[0] = ivals[0]; - return err; + break; - /* 1x uint */ - case AL_BUFFER: case AL_DIRECT_FILTER: - CheckSize(1); - if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false) - values[0] = static_cast<ALuint>(ivals[0]); - return err; - - /* 3x uint */ case AL_AUXILIARY_SEND_FILTER: - CheckSize(3); - if((err=GetSourceiv(Source, Context, prop, {ivals, 3u})) != false) - { - values[0] = static_cast<ALuint>(ivals[0]); - values[1] = static_cast<ALuint>(ivals[1]); - values[2] = static_cast<ALuint>(ivals[2]); - } - return err; - - case AL_SEC_OFFSET_LATENCY_SOFT: - case AL_SEC_OFFSET_CLOCK_SOFT: - break; /* Double only */ - case AL_STEREO_ANGLES: - break; /* Float/double only */ + break; } - ERR("Unexpected property: 0x%04x\n", prop); - Context->setError(AL_INVALID_ENUM, "Invalid source integer64 property 0x%04x", prop); + ERR("Unexpected %s query property: 0x%04x\n", PropType<T>::Name(), prop); + Context->setError(AL_INVALID_ENUM, "Invalid source %s query property 0x%04x", + PropType<T>::Name(), prop); return false; } catch(check_exception&) { @@ -2905,12 +2629,9 @@ void StartSources(ALCcontext *const context, const al::span<ALsource*> srchandle } // namespace -AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources) -START_API_FUNC +AL_API DECL_FUNC2(void, alGenSources, ALsizei, ALuint*) +FORCE_ALIGN void AL_APIENTRY alGenSourcesDirect(ALCcontext *context, ALsizei n, ALuint *sources) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(n < 0) UNLIKELY context->setError(AL_INVALID_VALUE, "Generating %d sources", n); if(n <= 0) UNLIKELY return; @@ -2923,7 +2644,7 @@ START_API_FUNC device->SourcesMax, context->mNumSources, n); return; } - if(!EnsureSources(context.get(), static_cast<ALuint>(n))) + if(!EnsureSources(context, static_cast<ALuint>(n))) { context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d source%s", n, (n==1)?"":"s"); return; @@ -2931,36 +2652,33 @@ START_API_FUNC if(n == 1) { - ALsource *source{AllocSource(context.get())}; + ALsource *source{AllocSource(context)}; sources[0] = source->id; #ifdef ALSOFT_EAX - source->eaxInitialize(context.get()); + source->eaxInitialize(context); #endif // ALSOFT_EAX } else { - al::vector<ALuint> ids; + std::vector<ALuint> ids; ids.reserve(static_cast<ALuint>(n)); do { - ALsource *source{AllocSource(context.get())}; + ALsource *source{AllocSource(context)}; ids.emplace_back(source->id); #ifdef ALSOFT_EAX - source->eaxInitialize(context.get()); + source->eaxInitialize(context); #endif // ALSOFT_EAX } while(--n); std::copy(ids.cbegin(), ids.cend(), sources); } } -END_API_FUNC -AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources) -START_API_FUNC +AL_API DECL_FUNC2(void, alDeleteSources, ALsizei, const ALuint*) +FORCE_ALIGN void AL_APIENTRY alDeleteSourcesDirect(ALCcontext *context, ALsizei n, + const ALuint *sources) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(n < 0) UNLIKELY context->setError(AL_INVALID_VALUE, "Deleting %d sources", n); if(n <= 0) UNLIKELY return; @@ -2968,8 +2686,8 @@ START_API_FUNC std::lock_guard<std::mutex> _{context->mSourceLock}; /* Check that all Sources are valid */ - auto validate_source = [&context](const ALuint sid) -> bool - { return LookupSource(context.get(), sid) != nullptr; }; + auto validate_source = [context](const ALuint sid) -> bool + { return LookupSource(context, sid) != nullptr; }; const ALuint *sources_end = sources + n; auto invsrc = std::find_if_not(sources, sources_end, validate_source); @@ -2979,577 +2697,462 @@ START_API_FUNC /* All good. Delete source IDs. */ auto delete_source = [&context](const ALuint sid) -> void { - ALsource *src{LookupSource(context.get(), sid)}; - if(src) FreeSource(context.get(), src); + ALsource *src{LookupSource(context, sid)}; + if(src) FreeSource(context, src); }; std::for_each(sources, sources_end, delete_source); } -END_API_FUNC -AL_API ALboolean AL_APIENTRY alIsSource(ALuint source) -START_API_FUNC +AL_API DECL_FUNC1(ALboolean, alIsSource, ALuint) +FORCE_ALIGN ALboolean AL_APIENTRY alIsSourceDirect(ALCcontext *context, ALuint source) noexcept { - ContextRef context{GetContextRef()}; - if(context) LIKELY - { - std::lock_guard<std::mutex> _{context->mSourceLock}; - if(LookupSource(context.get(), source) != nullptr) - return AL_TRUE; - } + std::lock_guard<std::mutex> _{context->mSourceLock}; + if(LookupSource(context, source) != nullptr) + return AL_TRUE; return AL_FALSE; } -END_API_FUNC -AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value) -START_API_FUNC +AL_API DECL_FUNC3(void, alSourcef, ALuint, ALenum, ALfloat) +FORCE_ALIGN void AL_APIENTRY alSourcefDirect(ALCcontext *context, ALuint source, ALenum param, + ALfloat value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; std::lock_guard<std::mutex> __{context->mSourceLock}; - ALsource *Source = LookupSource(context.get(), source); + ALsource *Source = LookupSource(context, source); if(!Source) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - else - SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u}); + return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + + SetProperty<float>(Source, context, static_cast<SourceProp>(param), al::span{&value, 1u}); } -END_API_FUNC -AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) -START_API_FUNC +AL_API DECL_FUNC5(void, alSource3f, ALuint, ALenum, ALfloat, ALfloat, ALfloat) +FORCE_ALIGN void AL_APIENTRY alSource3fDirect(ALCcontext *context, ALuint source, ALenum param, + ALfloat value1, ALfloat value2, ALfloat value3) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; std::lock_guard<std::mutex> __{context->mSourceLock}; - ALsource *Source = LookupSource(context.get(), source); + ALsource *Source = LookupSource(context, source); if(!Source) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - else - { - const float fvals[3]{ value1, value2, value3 }; - SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals); - } + return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + + const float fvals[3]{ value1, value2, value3 }; + SetProperty<float>(Source, context, static_cast<SourceProp>(param), al::span{fvals}); } -END_API_FUNC -AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alSourcefv, ALuint, ALenum, const ALfloat*) +FORCE_ALIGN void AL_APIENTRY alSourcefvDirect(ALCcontext *context, ALuint source, ALenum param, + const ALfloat *values) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; std::lock_guard<std::mutex> __{context->mSourceLock}; - ALsource *Source = LookupSource(context.get(), source); + ALsource *Source = LookupSource(context, source); if(!Source) UNLIKELY return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); if(!values) UNLIKELY return context->setError(AL_INVALID_VALUE, "NULL pointer"); const ALuint count{FloatValsByProp(param)}; - SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {values, count}); + SetProperty(Source, context, static_cast<SourceProp>(param), al::span{values, count}); } -END_API_FUNC -AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value) -START_API_FUNC +AL_API DECL_FUNCEXT3(void, alSourced,SOFT, ALuint, ALenum, ALdouble) +FORCE_ALIGN void AL_APIENTRY alSourcedDirectSOFT(ALCcontext *context, ALuint source, ALenum param, + ALdouble value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; std::lock_guard<std::mutex> __{context->mSourceLock}; - ALsource *Source = LookupSource(context.get(), source); + ALsource *Source = LookupSource(context, source); if(!Source) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - else - { - const float fval[1]{static_cast<float>(value)}; - SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fval); - } + return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + + SetProperty<double>(Source, context, static_cast<SourceProp>(param), al::span{&value, 1}); } -END_API_FUNC -AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3) -START_API_FUNC +AL_API DECL_FUNCEXT5(void, alSource3d,SOFT, ALuint, ALenum, ALdouble, ALdouble, ALdouble) +FORCE_ALIGN void AL_APIENTRY alSource3dDirectSOFT(ALCcontext *context, ALuint source, ALenum param, + ALdouble value1, ALdouble value2, ALdouble value3) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; std::lock_guard<std::mutex> __{context->mSourceLock}; - ALsource *Source = LookupSource(context.get(), source); + ALsource *Source = LookupSource(context, source); if(!Source) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - else - { - const float fvals[3]{static_cast<float>(value1), static_cast<float>(value2), - static_cast<float>(value3)}; - SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals); - } + return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + + const double dvals[3]{value1, value2, value3}; + SetProperty<double>(Source, context, static_cast<SourceProp>(param), al::span{dvals}); } -END_API_FUNC -AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values) -START_API_FUNC +AL_API DECL_FUNCEXT3(void, alSourcedv,SOFT, ALuint, ALenum, const ALdouble*) +FORCE_ALIGN void AL_APIENTRY alSourcedvDirectSOFT(ALCcontext *context, ALuint source, ALenum param, + const ALdouble *values) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; std::lock_guard<std::mutex> __{context->mSourceLock}; - ALsource *Source = LookupSource(context.get(), source); + ALsource *Source = LookupSource(context, source); if(!Source) UNLIKELY return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); if(!values) UNLIKELY return context->setError(AL_INVALID_VALUE, "NULL pointer"); const ALuint count{DoubleValsByProp(param)}; - float fvals[MaxValues]; - std::copy_n(values, count, fvals); - SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {fvals, count}); + SetProperty(Source, context, static_cast<SourceProp>(param), al::span{values, count}); } -END_API_FUNC -AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value) -START_API_FUNC +AL_API DECL_FUNC3(void, alSourcei, ALuint, ALenum, ALint) +FORCE_ALIGN void AL_APIENTRY alSourceiDirect(ALCcontext *context, ALuint source, ALenum param, + ALint value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; std::lock_guard<std::mutex> __{context->mSourceLock}; - ALsource *Source = LookupSource(context.get(), source); + ALsource *Source = LookupSource(context, source); if(!Source) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - else - SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u}); + return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + + SetProperty<int>(Source, context, static_cast<SourceProp>(param), al::span{&value, 1u}); } -END_API_FUNC -AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) -START_API_FUNC +AL_API DECL_FUNC5(void, alSource3i, ALuint, ALenum, ALint, ALint, ALint) +FORCE_ALIGN void AL_APIENTRY alSource3iDirect(ALCcontext *context, ALuint source, ALenum param, + ALint value1, ALint value2, ALint value3) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; std::lock_guard<std::mutex> __{context->mSourceLock}; - ALsource *Source = LookupSource(context.get(), source); + ALsource *Source = LookupSource(context, source); if(!Source) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - else - { - const int ivals[3]{ value1, value2, value3 }; - SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), ivals); - } + return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + + const int ivals[3]{ value1, value2, value3 }; + SetProperty<int>(Source, context, static_cast<SourceProp>(param), al::span{ivals}); } -END_API_FUNC -AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alSourceiv, ALuint, ALenum, const ALint*) +FORCE_ALIGN void AL_APIENTRY alSourceivDirect(ALCcontext *context, ALuint source, ALenum param, + const ALint *values) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; std::lock_guard<std::mutex> __{context->mSourceLock}; - ALsource *Source = LookupSource(context.get(), source); + ALsource *Source = LookupSource(context, source); if(!Source) UNLIKELY return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); if(!values) UNLIKELY return context->setError(AL_INVALID_VALUE, "NULL pointer"); const ALuint count{IntValsByProp(param)}; - SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {values, count}); + SetProperty(Source, context, static_cast<SourceProp>(param), al::span{values, count}); } -END_API_FUNC -AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value) -START_API_FUNC +AL_API DECL_FUNCEXT3(void, alSourcei64,SOFT, ALuint, ALenum, ALint64SOFT) +FORCE_ALIGN void AL_APIENTRY alSourcei64DirectSOFT(ALCcontext *context, ALuint source, + ALenum param, ALint64SOFT value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; std::lock_guard<std::mutex> __{context->mSourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + ALsource *Source{LookupSource(context, source)}; if(!Source) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - else - SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u}); + return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + + SetProperty<int64_t>(Source, context, static_cast<SourceProp>(param), al::span{&value, 1u}); } -END_API_FUNC -AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3) -START_API_FUNC +AL_API DECL_FUNCEXT5(void, alSource3i64,SOFT, ALuint, ALenum, ALint64SOFT, ALint64SOFT, ALint64SOFT) +FORCE_ALIGN void AL_APIENTRY alSource3i64DirectSOFT(ALCcontext *context, ALuint source, + ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; std::lock_guard<std::mutex> __{context->mSourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + ALsource *Source{LookupSource(context, source)}; if(!Source) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - else - { - const int64_t i64vals[3]{ value1, value2, value3 }; - SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), i64vals); - } + return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + + const int64_t i64vals[3]{ value1, value2, value3 }; + SetProperty<int64_t>(Source, context, static_cast<SourceProp>(param), al::span{i64vals}); } -END_API_FUNC -AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values) -START_API_FUNC +AL_API DECL_FUNCEXT3(void, alSourcei64v,SOFT, ALuint, ALenum, const ALint64SOFT*) +FORCE_ALIGN void AL_APIENTRY alSourcei64vDirectSOFT(ALCcontext *context, ALuint source, + ALenum param, const ALint64SOFT *values) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; std::lock_guard<std::mutex> __{context->mSourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + ALsource *Source{LookupSource(context, source)}; if(!Source) UNLIKELY return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); if(!values) UNLIKELY return context->setError(AL_INVALID_VALUE, "NULL pointer"); const ALuint count{Int64ValsByProp(param)}; - SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {values, count}); + SetProperty(Source, context, static_cast<SourceProp>(param), al::span{values, count}); } -END_API_FUNC -AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetSourcef, ALuint, ALenum, ALfloat*) +FORCE_ALIGN void AL_APIENTRY alGetSourcefDirect(ALCcontext *context, ALuint source, ALenum param, + ALfloat *value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mSourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + ALsource *Source{LookupSource(context, source)}; if(!Source) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!value) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else - { - double dval[1]; - if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dval)) - *value = static_cast<float>(dval[0]); - } + return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + if(!value) UNLIKELY + return context->setError(AL_INVALID_VALUE, "NULL pointer"); + + std::ignore = GetProperty(Source, context, static_cast<SourceProp>(param), al::span{value, 1}); } -END_API_FUNC -AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) -START_API_FUNC +AL_API DECL_FUNC5(void, alGetSource3f, ALuint, ALenum, ALfloat*, ALfloat*, ALfloat*) +FORCE_ALIGN void AL_APIENTRY alGetSource3fDirect(ALCcontext *context, ALuint source, ALenum param, + ALfloat *value1, ALfloat *value2, ALfloat *value3) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mSourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + ALsource *Source{LookupSource(context, source)}; if(!Source) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!(value1 && value2 && value3)) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else + return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + if(!(value1 && value2 && value3)) UNLIKELY + return context->setError(AL_INVALID_VALUE, "NULL pointer"); + + float fvals[3]; + if(GetProperty<float>(Source, context, static_cast<SourceProp>(param), al::span{fvals})) { - double dvals[3]; - if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals)) - { - *value1 = static_cast<float>(dvals[0]); - *value2 = static_cast<float>(dvals[1]); - *value3 = static_cast<float>(dvals[2]); - } + *value1 = fvals[0]; + *value2 = fvals[1]; + *value3 = fvals[2]; } } -END_API_FUNC -AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetSourcefv, ALuint, ALenum, ALfloat*) +FORCE_ALIGN void AL_APIENTRY alGetSourcefvDirect(ALCcontext *context, ALuint source, ALenum param, + ALfloat *values) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mSourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + ALsource *Source{LookupSource(context, source)}; if(!Source) UNLIKELY return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); if(!values) UNLIKELY return context->setError(AL_INVALID_VALUE, "NULL pointer"); const ALuint count{FloatValsByProp(param)}; - double dvals[MaxValues]; - if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {dvals, count})) - std::copy_n(dvals, count, values); + std::ignore = GetProperty(Source, context, static_cast<SourceProp>(param), + al::span{values, count}); } -END_API_FUNC -AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value) -START_API_FUNC +AL_API DECL_FUNCEXT3(void, alGetSourced,SOFT, ALuint, ALenum, ALdouble*) +FORCE_ALIGN void AL_APIENTRY alGetSourcedDirectSOFT(ALCcontext *context, ALuint source, + ALenum param, ALdouble *value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mSourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + ALsource *Source{LookupSource(context, source)}; if(!Source) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!value) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else - GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {value, 1u}); + return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + if(!value) UNLIKELY + return context->setError(AL_INVALID_VALUE, "NULL pointer"); + + std::ignore = GetProperty(Source, context, static_cast<SourceProp>(param), al::span{value, 1}); } -END_API_FUNC -AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3) -START_API_FUNC +AL_API DECL_FUNCEXT5(void, alGetSource3d,SOFT, ALuint, ALenum, ALdouble*, ALdouble*, ALdouble*) +FORCE_ALIGN void AL_APIENTRY alGetSource3dDirectSOFT(ALCcontext *context, ALuint source, + ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mSourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + ALsource *Source{LookupSource(context, source)}; if(!Source) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!(value1 && value2 && value3)) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else + return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + if(!(value1 && value2 && value3)) UNLIKELY + return context->setError(AL_INVALID_VALUE, "NULL pointer"); + + double dvals[3]; + if(GetProperty<double>(Source, context, static_cast<SourceProp>(param), al::span{dvals})) { - double dvals[3]; - if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals)) - { - *value1 = dvals[0]; - *value2 = dvals[1]; - *value3 = dvals[2]; - } + *value1 = dvals[0]; + *value2 = dvals[1]; + *value3 = dvals[2]; } } -END_API_FUNC -AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values) -START_API_FUNC +AL_API DECL_FUNCEXT3(void, alGetSourcedv,SOFT, ALuint, ALenum, ALdouble*) +FORCE_ALIGN void AL_APIENTRY alGetSourcedvDirectSOFT(ALCcontext *context, ALuint source, + ALenum param, ALdouble *values) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mSourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + ALsource *Source{LookupSource(context, source)}; if(!Source) UNLIKELY return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); if(!values) UNLIKELY return context->setError(AL_INVALID_VALUE, "NULL pointer"); const ALuint count{DoubleValsByProp(param)}; - GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {values, count}); + std::ignore = GetProperty(Source, context, static_cast<SourceProp>(param), + al::span{values, count}); } -END_API_FUNC -AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetSourcei, ALuint, ALenum, ALint*) +FORCE_ALIGN void AL_APIENTRY alGetSourceiDirect(ALCcontext *context, ALuint source, ALenum param, + ALint *value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mSourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + ALsource *Source{LookupSource(context, source)}; if(!Source) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!value) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else - GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {value, 1u}); + return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + if(!value) UNLIKELY + return context->setError(AL_INVALID_VALUE, "NULL pointer"); + + std::ignore = GetProperty(Source, context, static_cast<SourceProp>(param), al::span{value, 1}); } -END_API_FUNC -AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) -START_API_FUNC +AL_API DECL_FUNC5(void, alGetSource3i, ALuint, ALenum, ALint*, ALint*, ALint*) +FORCE_ALIGN void AL_APIENTRY alGetSource3iDirect(ALCcontext *context, ALuint source, ALenum param, + ALint *value1, ALint *value2, ALint *value3) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mSourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + ALsource *Source{LookupSource(context, source)}; if(!Source) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!(value1 && value2 && value3)) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else + return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + if(!(value1 && value2 && value3)) UNLIKELY + return context->setError(AL_INVALID_VALUE, "NULL pointer"); + + int ivals[3]; + if(GetProperty<int>(Source, context, static_cast<SourceProp>(param), al::span{ivals})) { - int ivals[3]; - if(GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), ivals)) - { - *value1 = ivals[0]; - *value2 = ivals[1]; - *value3 = ivals[2]; - } + *value1 = ivals[0]; + *value2 = ivals[1]; + *value3 = ivals[2]; } } -END_API_FUNC -AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values) -START_API_FUNC +AL_API DECL_FUNC3(void, alGetSourceiv, ALuint, ALenum, ALint*) +FORCE_ALIGN void AL_APIENTRY alGetSourceivDirect(ALCcontext *context, ALuint source, ALenum param, + ALint *values) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mSourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + ALsource *Source{LookupSource(context, source)}; if(!Source) UNLIKELY return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); if(!values) UNLIKELY return context->setError(AL_INVALID_VALUE, "NULL pointer"); const ALuint count{IntValsByProp(param)}; - GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {values, count}); + std::ignore = GetProperty(Source, context, static_cast<SourceProp>(param), + al::span{values, count}); } -END_API_FUNC -AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value) -START_API_FUNC +AL_API DECL_FUNCEXT3(void, alGetSourcei64,SOFT, ALuint, ALenum, ALint64SOFT*) +FORCE_ALIGN void AL_APIENTRY alGetSourcei64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mSourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + ALsource *Source{LookupSource(context, source)}; if(!Source) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!value) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else - GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {value, 1u}); + return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + if(!value) UNLIKELY + return context->setError(AL_INVALID_VALUE, "NULL pointer"); + + std::ignore = GetProperty(Source, context, static_cast<SourceProp>(param), al::span{value, 1}); } -END_API_FUNC -AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3) -START_API_FUNC +AL_API DECL_FUNCEXT5(void, alGetSource3i64,SOFT, ALuint, ALenum, ALint64SOFT*, ALint64SOFT*, ALint64SOFT*) +FORCE_ALIGN void AL_APIENTRY alGetSource3i64DirectSOFT(ALCcontext *context, ALuint source, + ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mSourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + ALsource *Source{LookupSource(context, source)}; if(!Source) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - else if(!(value1 && value2 && value3)) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else + return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + if(!(value1 && value2 && value3)) UNLIKELY + return context->setError(AL_INVALID_VALUE, "NULL pointer"); + + int64_t i64vals[3]; + if(GetProperty<int64_t>(Source, context, static_cast<SourceProp>(param), al::span{i64vals})) { - int64_t i64vals[3]; - if(GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), i64vals)) - { - *value1 = i64vals[0]; - *value2 = i64vals[1]; - *value3 = i64vals[2]; - } + *value1 = i64vals[0]; + *value2 = i64vals[1]; + *value3 = i64vals[2]; } } -END_API_FUNC -AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values) -START_API_FUNC +AL_API DECL_FUNCEXT3(void, alGetSourcei64v,SOFT, ALuint, ALenum, ALint64SOFT*) +FORCE_ALIGN void AL_APIENTRY alGetSourcei64vDirectSOFT(ALCcontext *context, ALuint source, + ALenum param, ALint64SOFT *values) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mSourceLock}; - ALsource *Source{LookupSource(context.get(), source)}; + ALsource *Source{LookupSource(context, source)}; if(!Source) UNLIKELY return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); if(!values) UNLIKELY return context->setError(AL_INVALID_VALUE, "NULL pointer"); const ALuint count{Int64ValsByProp(param)}; - GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {values, count}); + std::ignore = GetProperty(Source, context, static_cast<SourceProp>(param), + al::span{values, count}); } -END_API_FUNC -AL_API void AL_APIENTRY alSourcePlay(ALuint source) -START_API_FUNC +AL_API DECL_FUNC1(void, alSourcePlay, ALuint) +FORCE_ALIGN void AL_APIENTRY alSourcePlayDirect(ALCcontext *context, ALuint source) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mSourceLock}; - ALsource *srchandle{LookupSource(context.get(), source)}; + ALsource *srchandle{LookupSource(context, source)}; if(!srchandle) return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - StartSources(context.get(), {&srchandle, 1}); + StartSources(context, {&srchandle, 1}); } -END_API_FUNC -void AL_APIENTRY alSourcePlayAtTimeSOFT(ALuint source, ALint64SOFT start_time) -START_API_FUNC +FORCE_ALIGN DECL_FUNCEXT2(void, alSourcePlayAtTime,SOFT, ALuint, ALint64SOFT) +FORCE_ALIGN void AL_APIENTRY alSourcePlayAtTimeDirectSOFT(ALCcontext *context, ALuint source, + ALint64SOFT start_time) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(start_time < 0) UNLIKELY return context->setError(AL_INVALID_VALUE, "Invalid time point %" PRId64, start_time); std::lock_guard<std::mutex> _{context->mSourceLock}; - ALsource *srchandle{LookupSource(context.get(), source)}; + ALsource *srchandle{LookupSource(context, source)}; if(!srchandle) return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - StartSources(context.get(), {&srchandle, 1}, nanoseconds{start_time}); + StartSources(context, {&srchandle, 1}, nanoseconds{start_time}); } -END_API_FUNC -AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources) -START_API_FUNC +AL_API DECL_FUNC2(void, alSourcePlayv, ALsizei, const ALuint*) +FORCE_ALIGN void AL_APIENTRY alSourcePlayvDirect(ALCcontext *context, ALsizei n, + const ALuint *sources) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(n < 0) UNLIKELY context->setError(AL_INVALID_VALUE, "Playing %d sources", n); if(n <= 0) UNLIKELY return; - al::vector<ALsource*> extra_sources; + std::vector<ALsource*> extra_sources; std::array<ALsource*,8> source_storage; al::span<ALsource*> srchandles; if(static_cast<ALuint>(n) <= source_storage.size()) LIKELY - srchandles = {source_storage.data(), static_cast<ALuint>(n)}; + srchandles = al::span{source_storage}.first(static_cast<ALuint>(n)); else { extra_sources.resize(static_cast<ALuint>(n)); - srchandles = {extra_sources.data(), extra_sources.size()}; + srchandles = extra_sources; } std::lock_guard<std::mutex> _{context->mSourceLock}; for(auto &srchdl : srchandles) { - srchdl = LookupSource(context.get(), *sources); + srchdl = LookupSource(context, *sources); if(!srchdl) UNLIKELY return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources); ++sources; } - StartSources(context.get(), srchandles); + StartSources(context, srchandles); } -END_API_FUNC -void AL_APIENTRY alSourcePlayAtTimevSOFT(ALsizei n, const ALuint *sources, ALint64SOFT start_time) -START_API_FUNC +FORCE_ALIGN DECL_FUNCEXT3(void, alSourcePlayAtTimev,SOFT, ALsizei, const ALuint*, ALint64SOFT) +FORCE_ALIGN void AL_APIENTRY alSourcePlayAtTimevDirectSOFT(ALCcontext *context, ALsizei n, + const ALuint *sources, ALint64SOFT start_time) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(n < 0) UNLIKELY context->setError(AL_INVALID_VALUE, "Playing %d sources", n); if(n <= 0) UNLIKELY return; @@ -3557,61 +3160,57 @@ START_API_FUNC if(start_time < 0) UNLIKELY return context->setError(AL_INVALID_VALUE, "Invalid time point %" PRId64, start_time); - al::vector<ALsource*> extra_sources; + std::vector<ALsource*> extra_sources; std::array<ALsource*,8> source_storage; al::span<ALsource*> srchandles; if(static_cast<ALuint>(n) <= source_storage.size()) LIKELY - srchandles = {source_storage.data(), static_cast<ALuint>(n)}; + srchandles = al::span{source_storage}.first(static_cast<ALuint>(n)); else { extra_sources.resize(static_cast<ALuint>(n)); - srchandles = {extra_sources.data(), extra_sources.size()}; + srchandles = extra_sources; } std::lock_guard<std::mutex> _{context->mSourceLock}; for(auto &srchdl : srchandles) { - srchdl = LookupSource(context.get(), *sources); + srchdl = LookupSource(context, *sources); if(!srchdl) return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources); ++sources; } - StartSources(context.get(), srchandles, nanoseconds{start_time}); + StartSources(context, srchandles, nanoseconds{start_time}); } -END_API_FUNC -AL_API void AL_APIENTRY alSourcePause(ALuint source) -START_API_FUNC -{ alSourcePausev(1, &source); } -END_API_FUNC +AL_API DECL_FUNC1(void, alSourcePause, ALuint) +FORCE_ALIGN void AL_APIENTRY alSourcePauseDirect(ALCcontext *context, ALuint source) noexcept +{ alSourcePausevDirect(context, 1, &source); } -AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources) -START_API_FUNC +AL_API DECL_FUNC2(void, alSourcePausev, ALsizei, const ALuint*) +FORCE_ALIGN void AL_APIENTRY alSourcePausevDirect(ALCcontext *context, ALsizei n, + const ALuint *sources) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(n < 0) UNLIKELY context->setError(AL_INVALID_VALUE, "Pausing %d sources", n); if(n <= 0) UNLIKELY return; - al::vector<ALsource*> extra_sources; + std::vector<ALsource*> extra_sources; std::array<ALsource*,8> source_storage; al::span<ALsource*> srchandles; if(static_cast<ALuint>(n) <= source_storage.size()) LIKELY - srchandles = {source_storage.data(), static_cast<ALuint>(n)}; + srchandles = al::span{source_storage}.first(static_cast<ALuint>(n)); else { extra_sources.resize(static_cast<ALuint>(n)); - srchandles = {extra_sources.data(), extra_sources.size()}; + srchandles = extra_sources; } std::lock_guard<std::mutex> _{context->mSourceLock}; for(auto &srchdl : srchandles) { - srchdl = LookupSource(context.get(), *sources); + srchdl = LookupSource(context, *sources); if(!srchdl) return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources); ++sources; @@ -3624,14 +3223,14 @@ START_API_FUNC VoiceChange *tail{}, *cur{}; for(ALsource *source : srchandles) { - Voice *voice{GetSourceVoice(source, context.get())}; + Voice *voice{GetSourceVoice(source, context)}; if(GetSourceState(source, voice) == AL_PLAYING) { if(!cur) - cur = tail = GetVoiceChanger(context.get()); + cur = tail = GetVoiceChanger(context); else { - cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed); + cur->mNext.store(GetVoiceChanger(context), std::memory_order_relaxed); cur = cur->mNext.load(std::memory_order_relaxed); } cur->mVoice = voice; @@ -3641,7 +3240,7 @@ START_API_FUNC } if(tail) LIKELY { - SendVoiceChanges(context.get(), tail); + SendVoiceChanges(context, tail); /* Second, now that the voice changes have been sent, because it's * possible that the voice stopped after it was detected playing and * before the voice got paused, recheck that the source is still @@ -3649,45 +3248,41 @@ START_API_FUNC */ for(ALsource *source : srchandles) { - Voice *voice{GetSourceVoice(source, context.get())}; + Voice *voice{GetSourceVoice(source, context)}; if(GetSourceState(source, voice) == AL_PLAYING) source->state = AL_PAUSED; } } } -END_API_FUNC -AL_API void AL_APIENTRY alSourceStop(ALuint source) -START_API_FUNC -{ alSourceStopv(1, &source); } -END_API_FUNC +AL_API DECL_FUNC1(void, alSourceStop, ALuint) +FORCE_ALIGN void AL_APIENTRY alSourceStopDirect(ALCcontext *context, ALuint source) noexcept +{ alSourceStopvDirect(context, 1, &source); } -AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources) -START_API_FUNC +AL_API DECL_FUNC2(void, alSourceStopv, ALsizei, const ALuint*) +FORCE_ALIGN void AL_APIENTRY alSourceStopvDirect(ALCcontext *context, ALsizei n, + const ALuint *sources) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(n < 0) UNLIKELY context->setError(AL_INVALID_VALUE, "Stopping %d sources", n); if(n <= 0) UNLIKELY return; - al::vector<ALsource*> extra_sources; + std::vector<ALsource*> extra_sources; std::array<ALsource*,8> source_storage; al::span<ALsource*> srchandles; if(static_cast<ALuint>(n) <= source_storage.size()) LIKELY - srchandles = {source_storage.data(), static_cast<ALuint>(n)}; + srchandles = al::span{source_storage}.first(static_cast<ALuint>(n)); else { extra_sources.resize(static_cast<ALuint>(n)); - srchandles = {extra_sources.data(), extra_sources.size()}; + srchandles = extra_sources; } std::lock_guard<std::mutex> _{context->mSourceLock}; for(auto &srchdl : srchandles) { - srchdl = LookupSource(context.get(), *sources); + srchdl = LookupSource(context, *sources); if(!srchdl) return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources); ++sources; @@ -3696,13 +3291,13 @@ START_API_FUNC VoiceChange *tail{}, *cur{}; for(ALsource *source : srchandles) { - if(Voice *voice{GetSourceVoice(source, context.get())}) + if(Voice *voice{GetSourceVoice(source, context)}) { if(!cur) - cur = tail = GetVoiceChanger(context.get()); + cur = tail = GetVoiceChanger(context); else { - cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed); + cur->mNext.store(GetVoiceChanger(context), std::memory_order_relaxed); cur = cur->mNext.load(std::memory_order_relaxed); } voice->mPendingChange.store(true, std::memory_order_relaxed); @@ -3713,44 +3308,40 @@ START_API_FUNC } source->Offset = 0.0; source->OffsetType = AL_NONE; - source->VoiceIdx = INVALID_VOICE_IDX; + source->VoiceIdx = InvalidVoiceIndex; } if(tail) LIKELY - SendVoiceChanges(context.get(), tail); + SendVoiceChanges(context, tail); } -END_API_FUNC -AL_API void AL_APIENTRY alSourceRewind(ALuint source) -START_API_FUNC -{ alSourceRewindv(1, &source); } -END_API_FUNC +AL_API DECL_FUNC1(void, alSourceRewind, ALuint) +FORCE_ALIGN void AL_APIENTRY alSourceRewindDirect(ALCcontext *context, ALuint source) noexcept +{ alSourceRewindvDirect(context, 1, &source); } -AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources) -START_API_FUNC +AL_API DECL_FUNC2(void, alSourceRewindv, ALsizei, const ALuint*) +FORCE_ALIGN void AL_APIENTRY alSourceRewindvDirect(ALCcontext *context, ALsizei n, + const ALuint *sources) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(n < 0) UNLIKELY context->setError(AL_INVALID_VALUE, "Rewinding %d sources", n); if(n <= 0) UNLIKELY return; - al::vector<ALsource*> extra_sources; + std::vector<ALsource*> extra_sources; std::array<ALsource*,8> source_storage; al::span<ALsource*> srchandles; if(static_cast<ALuint>(n) <= source_storage.size()) LIKELY - srchandles = {source_storage.data(), static_cast<ALuint>(n)}; + srchandles = al::span{source_storage}.first(static_cast<ALuint>(n)); else { extra_sources.resize(static_cast<ALuint>(n)); - srchandles = {extra_sources.data(), extra_sources.size()}; + srchandles = extra_sources; } std::lock_guard<std::mutex> _{context->mSourceLock}; for(auto &srchdl : srchandles) { - srchdl = LookupSource(context.get(), *sources); + srchdl = LookupSource(context, *sources); if(!srchdl) return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources); ++sources; @@ -3759,14 +3350,14 @@ START_API_FUNC VoiceChange *tail{}, *cur{}; for(ALsource *source : srchandles) { - Voice *voice{GetSourceVoice(source, context.get())}; + Voice *voice{GetSourceVoice(source, context)}; if(source->state != AL_INITIAL) { if(!cur) - cur = tail = GetVoiceChanger(context.get()); + cur = tail = GetVoiceChanger(context); else { - cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed); + cur->mNext.store(GetVoiceChanger(context), std::memory_order_relaxed); cur = cur->mNext.load(std::memory_order_relaxed); } if(voice) @@ -3778,26 +3369,23 @@ START_API_FUNC } source->Offset = 0.0; source->OffsetType = AL_NONE; - source->VoiceIdx = INVALID_VOICE_IDX; + source->VoiceIdx = InvalidVoiceIndex; } if(tail) LIKELY - SendVoiceChanges(context.get(), tail); + SendVoiceChanges(context, tail); } -END_API_FUNC -AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALuint *buffers) -START_API_FUNC +AL_API DECL_FUNC3(void, alSourceQueueBuffers, ALuint, ALsizei, const ALuint*) +FORCE_ALIGN void AL_APIENTRY alSourceQueueBuffersDirect(ALCcontext *context, ALuint src, + ALsizei nb, const ALuint *buffers) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(nb < 0) UNLIKELY context->setError(AL_INVALID_VALUE, "Queueing %d buffers", nb); if(nb <= 0) UNLIKELY return; std::lock_guard<std::mutex> _{context->mSourceLock}; - ALsource *source{LookupSource(context.get(),src)}; + ALsource *source{LookupSource(context,src)}; if(!source) UNLIKELY return context->setError(AL_INVALID_NAME, "Invalid source ID %u", src); @@ -3912,20 +3500,17 @@ START_API_FUNC (iter-1)->mNext.store(al::to_address(iter), std::memory_order_release); } } -END_API_FUNC -AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint *buffers) -START_API_FUNC +AL_API DECL_FUNC3(void, alSourceUnqueueBuffers, ALuint, ALsizei, ALuint*) +FORCE_ALIGN void AL_APIENTRY alSourceUnqueueBuffersDirect(ALCcontext *context, ALuint src, + ALsizei nb, ALuint *buffers) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(nb < 0) UNLIKELY context->setError(AL_INVALID_VALUE, "Unqueueing %d buffers", nb); if(nb <= 0) UNLIKELY return; std::lock_guard<std::mutex> _{context->mSourceLock}; - ALsource *source{LookupSource(context.get(),src)}; + ALsource *source{LookupSource(context,src)}; if(!source) UNLIKELY return context->setError(AL_INVALID_NAME, "Invalid source ID %u", src); @@ -3940,7 +3525,7 @@ START_API_FUNC if(source->state != AL_INITIAL) LIKELY { VoiceBufferItem *Current{nullptr}; - if(Voice *voice{GetSourceVoice(source, context.get())}) + if(Voice *voice{GetSourceVoice(source, context)}) Current = voice->mCurrentBuffer.load(std::memory_order_relaxed); for(auto &item : source->mQueue) { @@ -3965,18 +3550,15 @@ START_API_FUNC source->mQueue.pop_front(); } while(--nb); } -END_API_FUNC -AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint, ALsizei, const ALuint*) -START_API_FUNC +AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint, ALsizei, const ALuint*) noexcept { ContextRef context{GetContextRef()}; if(!context) UNLIKELY return; context->setError(AL_INVALID_OPERATION, "alSourceQueueBufferLayersSOFT not supported"); } -END_API_FUNC ALsource::ALsource() @@ -4028,14 +3610,29 @@ void UpdateAllSourceProps(ALCcontext *context) } } +void ALsource::SetName(ALCcontext *context, ALuint id, std::string_view name) +{ + std::lock_guard<std::mutex> _{context->mSourceLock}; + + auto source = LookupSource(context, id); + if(!source) UNLIKELY + return context->setError(AL_INVALID_NAME, "Invalid source ID %u", id); + + context->mSourceNames.insert_or_assign(id, name); +} + + SourceSubList::~SourceSubList() { + if(!Sources) + return; + uint64_t usemask{~FreeMask}; while(usemask) { const int idx{al::countr_zero(usemask)}; usemask &= ~(1_u64 << idx); - al::destroy_at(Sources+idx); + std::destroy_at(Sources+idx); } FreeMask = ~usemask; al_free(Sources); @@ -4097,7 +3694,8 @@ ALsource* ALsource::EaxLookupSource(ALCcontext& al_context, ALuint source_id) no void ALsource::eax_set_sends_defaults(EaxSends& sends, const EaxFxSlotIds& ids) noexcept { - for (auto i = size_t{}; i < EAX_MAX_FXSLOTS; ++i) { + for(size_t i{0};i < EAX_MAX_FXSLOTS;++i) + { auto& send = sends[i]; send.guidReceivingFXSlotID = *(ids[i]); send.lSend = EAXSOURCE_DEFAULTSEND; @@ -4209,7 +3807,8 @@ void ALsource::eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS& slots) noex void ALsource::eax5_set_speaker_levels_defaults(EaxSpeakerLevels& speaker_levels) noexcept { - for (auto i = size_t{}; i < eax_max_speakers; ++i) { + for(size_t i{0};i < eax_max_speakers;++i) + { auto& speaker_level = speaker_levels[i]; speaker_level.lSpeakerID = static_cast<long>(EAXSPEAKER_FRONT_LEFT + i); speaker_level.lLevel = EAXSOURCE_DEFAULTSPEAKERLEVEL; @@ -4251,7 +3850,7 @@ void ALsource::eax1_translate(const Eax1Props& src, Eax5Props& dst) noexcept else { dst.source.ulFlags &= ~EAXSOURCEFLAGS_ROOMAUTO; - dst.sends[0].lSend = clamp(static_cast<long>(gain_to_level_mb(src.fMix)), + dst.sends[0].lSend = std::clamp(static_cast<long>(gain_to_level_mb(src.fMix)), EAXSOURCE_MINSEND, EAXSOURCE_MAXSEND); } } @@ -4280,7 +3879,7 @@ void ALsource::eax2_translate(const Eax2Props& src, Eax5Props& dst) noexcept dst.source.ulFlags = src.dwFlags; dst.source.flMacroFXFactor = EAXSOURCE_DEFAULTMACROFXFACTOR; - // Set everyting else to defaults. + // Set everything else to defaults. // eax5_set_sends_defaults(dst.sends); eax5_set_active_fx_slots_defaults(dst.active_fx_slots); @@ -4294,7 +3893,7 @@ void ALsource::eax3_translate(const Eax3Props& src, Eax5Props& dst) noexcept static_cast<Eax3Props&>(dst.source) = src; dst.source.flMacroFXFactor = EAXSOURCE_DEFAULTMACROFXFACTOR; - // Set everyting else to defaults. + // Set everything else to defaults. // eax5_set_sends_defaults(dst.sends); eax5_set_active_fx_slots_defaults(dst.active_fx_slots); @@ -4312,7 +3911,7 @@ void ALsource::eax4_translate(const Eax4Props& src, Eax5Props& dst) noexcept // dst.sends = src.sends; - for (auto i = size_t{}; i < EAX_MAX_FXSLOTS; ++i) + for(size_t i{0};i < EAX_MAX_FXSLOTS;++i) dst.sends[i].guidReceivingFXSlotID = *(eax5_fx_slot_ids[i]); // Active FX slots. @@ -4374,19 +3973,21 @@ EaxAlLowPassParam ALsource::eax_create_direct_filter_param() const noexcept static_cast<float>(mEax.source.lDirectHF) + static_cast<float>(mEax.source.lObstruction); - for (auto i = std::size_t{}; i < EAX_MAX_FXSLOTS; ++i) + for(size_t i{0};i < EAX_MAX_FXSLOTS;++i) { if(!mEaxActiveFxSlots[i]) continue; - if(has_source_occlusion) { + if(has_source_occlusion) + { const auto& fx_slot = mEaxAlContext->eaxGetFxSlot(i); const auto& fx_slot_eax = fx_slot.eax_get_eax_fx_slot(); const auto is_environmental_fx = ((fx_slot_eax.ulFlags & EAXFXSLOTFLAGS_ENVIRONMENT) != 0); const auto is_primary = (mEaxPrimaryFxSlotId.value_or(-1) == fx_slot.eax_get_index()); const auto is_listener_environment = (is_environmental_fx && is_primary); - if(is_listener_environment) { + if(is_listener_environment) + { gain_mb += eax_calculate_dst_occlusion_mb( mEax.source.lOcclusion, mEax.source.flOcclusionDirectRatio, @@ -4398,7 +3999,8 @@ EaxAlLowPassParam ALsource::eax_create_direct_filter_param() const noexcept const auto& send = mEax.sends[i]; - if(send.lOcclusion != 0) { + if(send.lOcclusion != 0) + { gain_mb += eax_calculate_dst_occlusion_mb( send.lOcclusion, send.flOcclusionDirectRatio, @@ -4473,8 +4075,9 @@ void ALsource::eax_update_direct_filter() void ALsource::eax_update_room_filters() { - for (auto i = size_t{}; i < EAX_MAX_FXSLOTS; ++i) { - if (!mEaxActiveFxSlots[i]) + for(size_t i{0};i < EAX_MAX_FXSLOTS;++i) + { + if(!mEaxActiveFxSlots[i]) continue; auto& fx_slot = mEaxAlContext->eaxGetFxSlot(i); @@ -4486,7 +4089,7 @@ void ALsource::eax_update_room_filters() void ALsource::eax_set_efx_outer_gain_hf() { - OuterGainHF = clamp( + OuterGainHF = std::clamp( level_mb_to_gain(static_cast<float>(mEax.source.lOutsideVolumeHF)), AL_MIN_CONE_OUTER_GAINHF, AL_MAX_CONE_OUTER_GAINHF); @@ -5280,7 +4883,7 @@ void ALsource::eax_commit_active_fx_slots() // Deactivate EFX auxiliary effect slots for inactive slots. Active slots // will be updated with the room filters. - for(auto i = size_t{}; i < EAX_MAX_FXSLOTS; ++i) + for(size_t i{0};i < EAX_MAX_FXSLOTS;++i) { if(!mEaxActiveFxSlots[i]) eax_set_al_source_send(nullptr, i, EaxAlLowPassParam{1.0f, 1.0f}); diff --git a/al/source.h b/al/source.h index ac97c8a7..c7694f83 100644 --- a/al/source.h +++ b/al/source.h @@ -4,9 +4,10 @@ #include <array> #include <atomic> #include <cstddef> +#include <deque> #include <iterator> #include <limits> -#include <deque> +#include <string_view> #include "AL/al.h" #include "AL/alc.h" @@ -14,7 +15,6 @@ #include "alc/alu.h" #include "alc/context.h" #include "alc/inprogext.h" -#include "aldeque.h" #include "almalloc.h" #include "alnumeric.h" #include "atomic.h" @@ -39,9 +39,9 @@ enum class SourceStereo : bool { #define DEFAULT_SENDS 2 -#define INVALID_VOICE_IDX static_cast<ALuint>(-1) +inline constexpr ALuint InvalidVoiceIndex{std::numeric_limits<ALuint>::max()}; -extern bool sBufferSubDataCompat; +inline bool sBufferSubDataCompat{false}; struct ALbufferQueueItem : public VoiceBufferItem { ALbuffer *mBuffer{nullptr}; @@ -138,14 +138,14 @@ struct ALsource { ALenum state{AL_INITIAL}; /** Source Buffer Queue head. */ - al::deque<ALbufferQueueItem> mQueue; + std::deque<ALbufferQueueItem> mQueue; bool mPropsDirty{true}; /* Index into the context's Voices array. Lazily updated, only checked and * reset when looking up the voice. */ - ALuint VoiceIdx{INVALID_VOICE_IDX}; + ALuint VoiceIdx{InvalidVoiceIndex}; /** Self ID */ ALuint id{0}; @@ -157,6 +157,8 @@ struct ALsource { ALsource(const ALsource&) = delete; ALsource& operator=(const ALsource&) = delete; + static void SetName(ALCcontext *context, ALuint id, std::string_view name); + DISABLE_ALLOC() #ifdef ALSOFT_EAX diff --git a/al/state.cpp b/al/state.cpp index 86d81b13..5131edd9 100644 --- a/al/state.cpp +++ b/al/state.cpp @@ -24,7 +24,9 @@ #include <atomic> #include <cmath> +#include <cstring> #include <mutex> +#include <optional> #include <stdexcept> #include <string> @@ -32,16 +34,18 @@ #include "AL/alc.h" #include "AL/alext.h" +#include "al/debug.h" +#include "albit.h" #include "alc/alu.h" #include "alc/context.h" #include "alc/inprogext.h" #include "alnumeric.h" -#include "aloptional.h" #include "atomic.h" #include "core/context.h" #include "core/except.h" #include "core/mixer/defs.h" #include "core/voice.h" +#include "direct_defs.h" #include "intrusive_ptr.h" #include "opthelpers.h" #include "strutils.h" @@ -67,6 +71,8 @@ constexpr ALchar alErrInvalidEnum[] = "Invalid Enum"; constexpr ALchar alErrInvalidValue[] = "Invalid Value"; constexpr ALchar alErrInvalidOp[] = "Invalid Operation"; constexpr ALchar alErrOutOfMemory[] = "Out of Memory"; +constexpr ALchar alStackOverflow[] = "Stack Overflow"; +constexpr ALchar alStackUnderflow[] = "Stack Underflow"; /* Resampler strings */ template<Resampler rtype> struct ResamplerName { }; @@ -103,7 +109,7 @@ const ALchar *GetResamplerName(const Resampler rtype) throw std::runtime_error{"Unexpected resampler index"}; } -al::optional<DistanceModel> DistanceModelFromALenum(ALenum model) +std::optional<DistanceModel> DistanceModelFromALenum(ALenum model) { switch(model) { @@ -115,7 +121,7 @@ al::optional<DistanceModel> DistanceModelFromALenum(ALenum model) case AL_EXPONENT_DISTANCE: return DistanceModel::Exponent; case AL_EXPONENT_DISTANCE_CLAMPED: return DistanceModel::ExponentClamped; } - return al::nullopt; + return std::nullopt; } ALenum ALenumFromDistanceModel(DistanceModel model) { @@ -132,626 +138,352 @@ ALenum ALenumFromDistanceModel(DistanceModel model) throw std::runtime_error{"Unexpected distance model "+std::to_string(static_cast<int>(model))}; } -} // namespace - -/* WARNING: Non-standard export! Not part of any extension, or exposed in the - * alcFunctions list. - */ -AL_API const ALchar* AL_APIENTRY alsoft_get_version(void) -START_API_FUNC -{ - static const auto spoof = al::getenv("ALSOFT_SPOOF_VERSION"); - if(spoof) return spoof->c_str(); - return ALSOFT_VERSION; -} -END_API_FUNC - -#define DO_UPDATEPROPS() do { \ - if(!context->mDeferUpdates) \ - UpdateContextProps(context.get()); \ - else \ - context->mPropsDirty = true; \ -} while(0) - - -AL_API void AL_APIENTRY alEnable(ALenum capability) -START_API_FUNC -{ - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - - switch(capability) - { - case AL_SOURCE_DISTANCE_MODEL: - { - std::lock_guard<std::mutex> _{context->mPropLock}; - context->mSourceDistanceModel = true; - DO_UPDATEPROPS(); - } - break; - - case AL_STOP_SOURCES_ON_DISCONNECT_SOFT: - context->setError(AL_INVALID_OPERATION, "Re-enabling AL_STOP_SOURCES_ON_DISCONNECT_SOFT not yet supported"); - break; - - default: - context->setError(AL_INVALID_VALUE, "Invalid enable property 0x%04x", capability); - } -} -END_API_FUNC - -AL_API void AL_APIENTRY alDisable(ALenum capability) -START_API_FUNC -{ - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - - switch(capability) - { - case AL_SOURCE_DISTANCE_MODEL: - { - std::lock_guard<std::mutex> _{context->mPropLock}; - context->mSourceDistanceModel = false; - DO_UPDATEPROPS(); - } - break; - - case AL_STOP_SOURCES_ON_DISCONNECT_SOFT: - context->mStopVoicesOnDisconnect = false; - break; - - default: - context->setError(AL_INVALID_VALUE, "Invalid disable property 0x%04x", capability); - } -} -END_API_FUNC - -AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability) -START_API_FUNC -{ - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return AL_FALSE; - - std::lock_guard<std::mutex> _{context->mPropLock}; - ALboolean value{AL_FALSE}; - switch(capability) - { - case AL_SOURCE_DISTANCE_MODEL: - value = context->mSourceDistanceModel ? AL_TRUE : AL_FALSE; - break; - - case AL_STOP_SOURCES_ON_DISCONNECT_SOFT: - value = context->mStopVoicesOnDisconnect ? AL_TRUE : AL_FALSE; - break; - - default: - context->setError(AL_INVALID_VALUE, "Invalid is enabled property 0x%04x", capability); - } - - return value; -} -END_API_FUNC - -AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum pname) -START_API_FUNC +enum PropertyValue : ALenum { + DopplerFactor = AL_DOPPLER_FACTOR, + DopplerVelocity = AL_DOPPLER_VELOCITY, + DistanceModel = AL_DISTANCE_MODEL, + SpeedOfSound = AL_SPEED_OF_SOUND, + DeferredUpdates = AL_DEFERRED_UPDATES_SOFT, + GainLimit = AL_GAIN_LIMIT_SOFT, + NumResamplers = AL_NUM_RESAMPLERS_SOFT, + DefaultResampler = AL_DEFAULT_RESAMPLER_SOFT, + DebugLoggedMessages = AL_DEBUG_LOGGED_MESSAGES_EXT, + DebugNextLoggedMessageLength = AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT, + MaxDebugMessageLength = AL_MAX_DEBUG_MESSAGE_LENGTH_EXT, + MaxDebugLoggedMessages = AL_MAX_DEBUG_LOGGED_MESSAGES_EXT, + MaxDebugGroupDepth = AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT, + MaxLabelLength = AL_MAX_LABEL_LENGTH_EXT, + ContextFlags = AL_CONTEXT_FLAGS_EXT, +#ifdef ALSOFT_EAX + EaxRamSize = AL_EAX_RAM_SIZE, + EaxRamFree = AL_EAX_RAM_FREE, +#endif +}; + +template<typename T> +struct PropertyCastType { + template<typename U> + constexpr auto operator()(U&& value) const noexcept + { return static_cast<T>(std::forward<U>(value)); } +}; +/* Special-case ALboolean to be an actual bool instead of a char type. */ +template<> +struct PropertyCastType<ALboolean> { + template<typename U> + constexpr ALboolean operator()(U&& value) const noexcept + { return static_cast<bool>(std::forward<U>(value)) ? AL_TRUE : AL_FALSE; } +}; + + +template<typename T> +void GetValue(ALCcontext *context, ALenum pname, T *values) { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return AL_FALSE; + auto cast_value = PropertyCastType<T>{}; - std::lock_guard<std::mutex> _{context->mPropLock}; - ALboolean value{AL_FALSE}; - switch(pname) + switch(static_cast<PropertyValue>(pname)) { case AL_DOPPLER_FACTOR: - if(context->mDopplerFactor != 0.0f) - value = AL_TRUE; - break; + *values = cast_value(context->mDopplerFactor); + return; case AL_DOPPLER_VELOCITY: - if(context->mDopplerVelocity != 0.0f) - value = AL_TRUE; - break; - - case AL_DISTANCE_MODEL: - if(context->mDistanceModel == DistanceModel::Default) - value = AL_TRUE; - break; + if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY + context->debugMessage(DebugSource::API, DebugType::DeprecatedBehavior, 0, + DebugSeverity::Medium, + "AL_DOPPLER_VELOCITY is deprecated in AL 1.1, use AL_SPEED_OF_SOUND; " + "AL_DOPPLER_VELOCITY -> AL_SPEED_OF_SOUND / 343.3f"); + *values = cast_value(context->mDopplerVelocity); + return; case AL_SPEED_OF_SOUND: - if(context->mSpeedOfSound != 0.0f) - value = AL_TRUE; - break; - - case AL_DEFERRED_UPDATES_SOFT: - if(context->mDeferUpdates) - value = AL_TRUE; - break; + *values = cast_value(context->mSpeedOfSound); + return; case AL_GAIN_LIMIT_SOFT: - if(GainMixMax/context->mGainBoost != 0.0f) - value = AL_TRUE; - break; - - case AL_NUM_RESAMPLERS_SOFT: - /* Always non-0. */ - value = AL_TRUE; - break; - - case AL_DEFAULT_RESAMPLER_SOFT: - value = static_cast<int>(ResamplerDefault) ? AL_TRUE : AL_FALSE; - break; - - default: - context->setError(AL_INVALID_VALUE, "Invalid boolean property 0x%04x", pname); - } - - return value; -} -END_API_FUNC - -AL_API ALdouble AL_APIENTRY alGetDouble(ALenum pname) -START_API_FUNC -{ - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return 0.0; - - std::lock_guard<std::mutex> _{context->mPropLock}; - ALdouble value{0.0}; - switch(pname) - { - case AL_DOPPLER_FACTOR: - value = context->mDopplerFactor; - break; - - case AL_DOPPLER_VELOCITY: - value = context->mDopplerVelocity; - break; - - case AL_DISTANCE_MODEL: - value = static_cast<ALdouble>(ALenumFromDistanceModel(context->mDistanceModel)); - break; - - case AL_SPEED_OF_SOUND: - value = context->mSpeedOfSound; - break; + *values = cast_value(GainMixMax / context->mGainBoost); + return; case AL_DEFERRED_UPDATES_SOFT: - if(context->mDeferUpdates) - value = static_cast<ALdouble>(AL_TRUE); - break; - - case AL_GAIN_LIMIT_SOFT: - value = ALdouble{GainMixMax}/context->mGainBoost; - break; - - case AL_NUM_RESAMPLERS_SOFT: - value = static_cast<ALdouble>(Resampler::Max) + 1.0; - break; - - case AL_DEFAULT_RESAMPLER_SOFT: - value = static_cast<ALdouble>(ResamplerDefault); - break; - - default: - context->setError(AL_INVALID_VALUE, "Invalid double property 0x%04x", pname); - } - - return value; -} -END_API_FUNC - -AL_API ALfloat AL_APIENTRY alGetFloat(ALenum pname) -START_API_FUNC -{ - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return 0.0f; - - std::lock_guard<std::mutex> _{context->mPropLock}; - ALfloat value{0.0f}; - switch(pname) - { - case AL_DOPPLER_FACTOR: - value = context->mDopplerFactor; - break; - - case AL_DOPPLER_VELOCITY: - value = context->mDopplerVelocity; - break; + *values = cast_value(context->mDeferUpdates ? AL_TRUE : AL_FALSE); + return; case AL_DISTANCE_MODEL: - value = static_cast<ALfloat>(ALenumFromDistanceModel(context->mDistanceModel)); - break; - - case AL_SPEED_OF_SOUND: - value = context->mSpeedOfSound; - break; - - case AL_DEFERRED_UPDATES_SOFT: - if(context->mDeferUpdates) - value = static_cast<ALfloat>(AL_TRUE); - break; - - case AL_GAIN_LIMIT_SOFT: - value = GainMixMax/context->mGainBoost; - break; + *values = cast_value(ALenumFromDistanceModel(context->mDistanceModel)); + return; case AL_NUM_RESAMPLERS_SOFT: - value = static_cast<ALfloat>(Resampler::Max) + 1.0f; - break; + *values = cast_value(al::to_underlying(Resampler::Max) + 1); + return; case AL_DEFAULT_RESAMPLER_SOFT: - value = static_cast<ALfloat>(ResamplerDefault); - break; + *values = cast_value(al::to_underlying(ResamplerDefault)); + return; - default: - context->setError(AL_INVALID_VALUE, "Invalid float property 0x%04x", pname); + case AL_DEBUG_LOGGED_MESSAGES_EXT: + { + std::lock_guard<std::mutex> _{context->mDebugCbLock}; + *values = cast_value(context->mDebugLog.size()); + return; } - return value; -} -END_API_FUNC - -AL_API ALint AL_APIENTRY alGetInteger(ALenum pname) -START_API_FUNC -{ - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return 0; - - std::lock_guard<std::mutex> _{context->mPropLock}; - ALint value{0}; - switch(pname) + case AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT: { - case AL_DOPPLER_FACTOR: - value = static_cast<ALint>(context->mDopplerFactor); - break; + std::lock_guard<std::mutex> _{context->mDebugCbLock}; + *values = cast_value(context->mDebugLog.empty() ? 0_uz + : (context->mDebugLog.front().mMessage.size()+1)); + return; + } - case AL_DOPPLER_VELOCITY: - value = static_cast<ALint>(context->mDopplerVelocity); - break; + case AL_MAX_DEBUG_MESSAGE_LENGTH_EXT: + *values = cast_value(MaxDebugMessageLength); + return; - case AL_DISTANCE_MODEL: - value = ALenumFromDistanceModel(context->mDistanceModel); - break; + case AL_MAX_DEBUG_LOGGED_MESSAGES_EXT: + *values = cast_value(MaxDebugLoggedMessages); + return; - case AL_SPEED_OF_SOUND: - value = static_cast<ALint>(context->mSpeedOfSound); - break; + case AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT: + *values = cast_value(MaxDebugGroupDepth); + return; - case AL_DEFERRED_UPDATES_SOFT: - if(context->mDeferUpdates) - value = AL_TRUE; - break; + case AL_MAX_LABEL_LENGTH_EXT: + *values = cast_value(MaxObjectLabelLength); + return; - case AL_GAIN_LIMIT_SOFT: - value = static_cast<ALint>(GainMixMax/context->mGainBoost); - break; - - case AL_NUM_RESAMPLERS_SOFT: - value = static_cast<int>(Resampler::Max) + 1; - break; - - case AL_DEFAULT_RESAMPLER_SOFT: - value = static_cast<int>(ResamplerDefault); - break; + case AL_CONTEXT_FLAGS_EXT: + *values = cast_value(context->mContextFlags.to_ulong()); + return; #ifdef ALSOFT_EAX #define EAX_ERROR "[alGetInteger] EAX not enabled." case AL_EAX_RAM_SIZE: - if (eax_g_is_enabled) - { - value = eax_x_ram_max_size; - } - else + if(eax_g_is_enabled) { - context->setError(AL_INVALID_VALUE, EAX_ERROR); + *values = cast_value(eax_x_ram_max_size); + return; } - - break; + context->setError(AL_INVALID_ENUM, EAX_ERROR); + return; case AL_EAX_RAM_FREE: - if (eax_g_is_enabled) + if(eax_g_is_enabled) { auto device = context->mALDevice.get(); std::lock_guard<std::mutex> device_lock{device->BufferLock}; - - value = static_cast<ALint>(device->eax_x_ram_free_size); + *values = cast_value(device->eax_x_ram_free_size); + return; } - else - { - context->setError(AL_INVALID_VALUE, EAX_ERROR); - } - - break; + context->setError(AL_INVALID_ENUM, EAX_ERROR); + return; #undef EAX_ERROR #endif // ALSOFT_EAX - - default: - context->setError(AL_INVALID_VALUE, "Invalid integer property 0x%04x", pname); } + context->setError(AL_INVALID_ENUM, "Invalid context property 0x%04x", pname); +} - return value; + +inline void UpdateProps(ALCcontext *context) +{ + if(!context->mDeferUpdates) + UpdateContextProps(context); + else + context->mPropsDirty = true; } -END_API_FUNC -AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname) -START_API_FUNC +} // namespace + +/* WARNING: Non-standard export! Not part of any extension, or exposed in the + * alcFunctions list. + */ +AL_API const ALchar* AL_APIENTRY alsoft_get_version(void) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return 0_i64; + static const auto spoof = al::getenv("ALSOFT_SPOOF_VERSION"); + if(spoof) return spoof->c_str(); + return ALSOFT_VERSION; +} - std::lock_guard<std::mutex> _{context->mPropLock}; - ALint64SOFT value{0}; - switch(pname) - { - case AL_DOPPLER_FACTOR: - value = static_cast<ALint64SOFT>(context->mDopplerFactor); - break; - case AL_DOPPLER_VELOCITY: - value = static_cast<ALint64SOFT>(context->mDopplerVelocity); +AL_API DECL_FUNC1(void, alEnable, ALenum) +FORCE_ALIGN void AL_APIENTRY alEnableDirect(ALCcontext *context, ALenum capability) noexcept +{ + switch(capability) + { + case AL_SOURCE_DISTANCE_MODEL: + { + std::lock_guard<std::mutex> _{context->mPropLock}; + context->mSourceDistanceModel = true; + UpdateProps(context); + } break; - case AL_DISTANCE_MODEL: - value = ALenumFromDistanceModel(context->mDistanceModel); + case AL_DEBUG_OUTPUT_EXT: + context->mDebugEnabled = true; break; - case AL_SPEED_OF_SOUND: - value = static_cast<ALint64SOFT>(context->mSpeedOfSound); + case AL_STOP_SOURCES_ON_DISCONNECT_SOFT: + context->setError(AL_INVALID_OPERATION, "Re-enabling AL_STOP_SOURCES_ON_DISCONNECT_SOFT not yet supported"); break; - case AL_DEFERRED_UPDATES_SOFT: - if(context->mDeferUpdates) - value = AL_TRUE; - break; + default: + context->setError(AL_INVALID_VALUE, "Invalid enable property 0x%04x", capability); + } +} - case AL_GAIN_LIMIT_SOFT: - value = static_cast<ALint64SOFT>(GainMixMax/context->mGainBoost); +AL_API DECL_FUNC1(void, alDisable, ALenum) +FORCE_ALIGN void AL_APIENTRY alDisableDirect(ALCcontext *context, ALenum capability) noexcept +{ + switch(capability) + { + case AL_SOURCE_DISTANCE_MODEL: + { + std::lock_guard<std::mutex> _{context->mPropLock}; + context->mSourceDistanceModel = false; + UpdateProps(context); + } break; - case AL_NUM_RESAMPLERS_SOFT: - value = static_cast<ALint64SOFT>(Resampler::Max) + 1; + case AL_DEBUG_OUTPUT_EXT: + context->mDebugEnabled = false; break; - case AL_DEFAULT_RESAMPLER_SOFT: - value = static_cast<ALint64SOFT>(ResamplerDefault); + case AL_STOP_SOURCES_ON_DISCONNECT_SOFT: + context->mStopVoicesOnDisconnect = false; break; default: - context->setError(AL_INVALID_VALUE, "Invalid integer64 property 0x%04x", pname); + context->setError(AL_INVALID_VALUE, "Invalid disable property 0x%04x", capability); } - - return value; } -END_API_FUNC -AL_API ALvoid* AL_APIENTRY alGetPointerSOFT(ALenum pname) -START_API_FUNC +AL_API DECL_FUNC1(ALboolean, alIsEnabled, ALenum) +FORCE_ALIGN ALboolean AL_APIENTRY alIsEnabledDirect(ALCcontext *context, ALenum capability) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return nullptr; - std::lock_guard<std::mutex> _{context->mPropLock}; - void *value{nullptr}; - switch(pname) + ALboolean value{AL_FALSE}; + switch(capability) { - case AL_EVENT_CALLBACK_FUNCTION_SOFT: - value = reinterpret_cast<void*>(context->mEventCb); + case AL_SOURCE_DISTANCE_MODEL: + value = context->mSourceDistanceModel ? AL_TRUE : AL_FALSE; break; - case AL_EVENT_CALLBACK_USER_PARAM_SOFT: - value = context->mEventParam; + case AL_DEBUG_OUTPUT_EXT: + value = context->mDebugEnabled ? AL_TRUE : AL_FALSE; + break; + + case AL_STOP_SOURCES_ON_DISCONNECT_SOFT: + value = context->mStopVoicesOnDisconnect ? AL_TRUE : AL_FALSE; break; default: - context->setError(AL_INVALID_VALUE, "Invalid pointer property 0x%04x", pname); + context->setError(AL_INVALID_VALUE, "Invalid is enabled property 0x%04x", capability); } return value; } -END_API_FUNC -AL_API void AL_APIENTRY alGetBooleanv(ALenum pname, ALboolean *values) -START_API_FUNC -{ - if(values) - { - switch(pname) - { - case AL_DOPPLER_FACTOR: - case AL_DOPPLER_VELOCITY: - case AL_DISTANCE_MODEL: - case AL_SPEED_OF_SOUND: - case AL_DEFERRED_UPDATES_SOFT: - case AL_GAIN_LIMIT_SOFT: - case AL_NUM_RESAMPLERS_SOFT: - case AL_DEFAULT_RESAMPLER_SOFT: - values[0] = alGetBoolean(pname); - return; - } - } +#define DECL_GETFUNC(R, Name, Ext) \ +AL_API R AL_APIENTRY Name##Ext(ALenum pname) noexcept \ +{ \ + R value{}; \ + auto context = GetContextRef(); \ + if(!context) UNLIKELY return value; \ + Name##vDirect##Ext(GetContextRef().get(), pname, &value); \ + return value; \ +} \ +FORCE_ALIGN R AL_APIENTRY Name##Direct##Ext(ALCcontext *context, ALenum pname) noexcept \ +{ \ + R value{}; \ + Name##vDirect##Ext(context, pname, &value); \ + return value; \ +} - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; +DECL_GETFUNC(ALboolean, alGetBoolean,) +DECL_GETFUNC(ALdouble, alGetDouble,) +DECL_GETFUNC(ALfloat, alGetFloat,) +DECL_GETFUNC(ALint, alGetInteger,) - if(!values) - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(pname) - { - default: - context->setError(AL_INVALID_VALUE, "Invalid boolean-vector property 0x%04x", pname); - } -} -END_API_FUNC +DECL_GETFUNC(ALint64SOFT, alGetInteger64,SOFT) +DECL_GETFUNC(ALvoid*, alGetPointer,SOFT) -AL_API void AL_APIENTRY alGetDoublev(ALenum pname, ALdouble *values) -START_API_FUNC -{ - if(values) - { - switch(pname) - { - case AL_DOPPLER_FACTOR: - case AL_DOPPLER_VELOCITY: - case AL_DISTANCE_MODEL: - case AL_SPEED_OF_SOUND: - case AL_DEFERRED_UPDATES_SOFT: - case AL_GAIN_LIMIT_SOFT: - case AL_NUM_RESAMPLERS_SOFT: - case AL_DEFAULT_RESAMPLER_SOFT: - values[0] = alGetDouble(pname); - return; - } - } +#undef DECL_GETFUNC - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(!values) - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(pname) - { - default: - context->setError(AL_INVALID_VALUE, "Invalid double-vector property 0x%04x", pname); - } +AL_API DECL_FUNC2(void, alGetBooleanv, ALenum, ALboolean*) +FORCE_ALIGN void AL_APIENTRY alGetBooleanvDirect(ALCcontext *context, ALenum pname, ALboolean *values) noexcept +{ + if(!values) UNLIKELY + return context->setError(AL_INVALID_VALUE, "NULL pointer"); + GetValue(context, pname, values); } -END_API_FUNC -AL_API void AL_APIENTRY alGetFloatv(ALenum pname, ALfloat *values) -START_API_FUNC +AL_API DECL_FUNC2(void, alGetDoublev, ALenum, ALdouble*) +FORCE_ALIGN void AL_APIENTRY alGetDoublevDirect(ALCcontext *context, ALenum pname, ALdouble *values) noexcept { - if(values) - { - switch(pname) - { - case AL_DOPPLER_FACTOR: - case AL_DOPPLER_VELOCITY: - case AL_DISTANCE_MODEL: - case AL_SPEED_OF_SOUND: - case AL_DEFERRED_UPDATES_SOFT: - case AL_GAIN_LIMIT_SOFT: - case AL_NUM_RESAMPLERS_SOFT: - case AL_DEFAULT_RESAMPLER_SOFT: - values[0] = alGetFloat(pname); - return; - } - } - - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - - if(!values) - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(pname) - { - default: - context->setError(AL_INVALID_VALUE, "Invalid float-vector property 0x%04x", pname); - } + if(!values) UNLIKELY + return context->setError(AL_INVALID_VALUE, "NULL pointer"); + GetValue(context, pname, values); } -END_API_FUNC -AL_API void AL_APIENTRY alGetIntegerv(ALenum pname, ALint *values) -START_API_FUNC +AL_API DECL_FUNC2(void, alGetFloatv, ALenum, ALfloat*) +FORCE_ALIGN void AL_APIENTRY alGetFloatvDirect(ALCcontext *context, ALenum pname, ALfloat *values) noexcept { - if(values) - { - switch(pname) - { - case AL_DOPPLER_FACTOR: - case AL_DOPPLER_VELOCITY: - case AL_DISTANCE_MODEL: - case AL_SPEED_OF_SOUND: - case AL_DEFERRED_UPDATES_SOFT: - case AL_GAIN_LIMIT_SOFT: - case AL_NUM_RESAMPLERS_SOFT: - case AL_DEFAULT_RESAMPLER_SOFT: - values[0] = alGetInteger(pname); - return; - } - } - - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - - if(!values) - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(pname) - { - default: - context->setError(AL_INVALID_VALUE, "Invalid integer-vector property 0x%04x", pname); - } + if(!values) UNLIKELY + return context->setError(AL_INVALID_VALUE, "NULL pointer"); + GetValue(context, pname, values); } -END_API_FUNC -AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values) -START_API_FUNC +AL_API DECL_FUNC2(void, alGetIntegerv, ALenum, ALint*) +FORCE_ALIGN void AL_APIENTRY alGetIntegervDirect(ALCcontext *context, ALenum pname, ALint *values) noexcept { - if(values) - { - switch(pname) - { - case AL_DOPPLER_FACTOR: - case AL_DOPPLER_VELOCITY: - case AL_DISTANCE_MODEL: - case AL_SPEED_OF_SOUND: - case AL_DEFERRED_UPDATES_SOFT: - case AL_GAIN_LIMIT_SOFT: - case AL_NUM_RESAMPLERS_SOFT: - case AL_DEFAULT_RESAMPLER_SOFT: - values[0] = alGetInteger64SOFT(pname); - return; - } - } - - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; + if(!values) UNLIKELY + return context->setError(AL_INVALID_VALUE, "NULL pointer"); + GetValue(context, pname, values); +} - if(!values) - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(pname) - { - default: - context->setError(AL_INVALID_VALUE, "Invalid integer64-vector property 0x%04x", pname); - } +AL_API DECL_FUNCEXT2(void, alGetInteger64v,SOFT, ALenum, ALint64SOFT*) +FORCE_ALIGN void AL_APIENTRY alGetInteger64vDirectSOFT(ALCcontext *context, ALenum pname, ALint64SOFT *values) noexcept +{ + if(!values) UNLIKELY + return context->setError(AL_INVALID_VALUE, "NULL pointer"); + GetValue(context, pname, values); } -END_API_FUNC -AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, ALvoid **values) -START_API_FUNC +AL_API DECL_FUNCEXT2(void, alGetPointerv,SOFT, ALenum, ALvoid**) +FORCE_ALIGN void AL_APIENTRY alGetPointervDirectSOFT(ALCcontext *context, ALenum pname, ALvoid **values) noexcept { - if(values) + if(!values) UNLIKELY + return context->setError(AL_INVALID_VALUE, "NULL pointer"); + + switch(pname) { - switch(pname) - { - case AL_EVENT_CALLBACK_FUNCTION_SOFT: - case AL_EVENT_CALLBACK_USER_PARAM_SOFT: - values[0] = alGetPointerSOFT(pname); - return; - } - } + case AL_EVENT_CALLBACK_FUNCTION_SOFT: + *values = al::bit_cast<void*>(context->mEventCb); + break; - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; + case AL_EVENT_CALLBACK_USER_PARAM_SOFT: + *values = context->mEventParam; + break; + + case AL_DEBUG_CALLBACK_FUNCTION_EXT: + *values = al::bit_cast<void*>(context->mDebugCb); + break; + + case AL_DEBUG_CALLBACK_USER_PARAM_EXT: + *values = context->mDebugParam; + break; - if(!values) - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(pname) - { default: - context->setError(AL_INVALID_VALUE, "Invalid pointer-vector property 0x%04x", pname); + context->setError(AL_INVALID_ENUM, "Invalid context pointer property 0x%04x", pname); } } -END_API_FUNC -AL_API const ALchar* AL_APIENTRY alGetString(ALenum pname) -START_API_FUNC +AL_API DECL_FUNC1(const ALchar*, alGetString, ALenum) +FORCE_ALIGN const ALchar* AL_APIENTRY alGetStringDirect(ALCcontext *context, ALenum pname) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return nullptr; - const ALchar *value{nullptr}; switch(pname) { @@ -768,7 +500,7 @@ START_API_FUNC break; case AL_EXTENSIONS: - value = context->mExtensionList; + value = context->mExtensionsString.c_str(); break; case AL_NO_ERROR: @@ -795,112 +527,79 @@ START_API_FUNC value = alErrOutOfMemory; break; + case AL_STACK_OVERFLOW_EXT: + value = alStackOverflow; + break; + + case AL_STACK_UNDERFLOW_EXT: + value = alStackUnderflow; + break; + default: context->setError(AL_INVALID_VALUE, "Invalid string property 0x%04x", pname); } return value; } -END_API_FUNC -AL_API void AL_APIENTRY alDopplerFactor(ALfloat value) -START_API_FUNC +AL_API DECL_FUNC1(void, alDopplerFactor, ALfloat) +FORCE_ALIGN void AL_APIENTRY alDopplerFactorDirect(ALCcontext *context, ALfloat value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(!(value >= 0.0f && std::isfinite(value))) context->setError(AL_INVALID_VALUE, "Doppler factor %f out of range", value); else { std::lock_guard<std::mutex> _{context->mPropLock}; context->mDopplerFactor = value; - DO_UPDATEPROPS(); + UpdateProps(context); } } -END_API_FUNC -AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value) -START_API_FUNC +AL_API DECL_FUNC1(void, alSpeedOfSound, ALfloat) +FORCE_ALIGN void AL_APIENTRY alSpeedOfSoundDirect(ALCcontext *context, ALfloat value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - - if(!(value >= 0.0f && std::isfinite(value))) - context->setError(AL_INVALID_VALUE, "Doppler velocity %f out of range", value); - else - { - std::lock_guard<std::mutex> _{context->mPropLock}; - context->mDopplerVelocity = value; - DO_UPDATEPROPS(); - } -} -END_API_FUNC - -AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value) -START_API_FUNC -{ - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(!(value > 0.0f && std::isfinite(value))) context->setError(AL_INVALID_VALUE, "Speed of sound %f out of range", value); else { std::lock_guard<std::mutex> _{context->mPropLock}; context->mSpeedOfSound = value; - DO_UPDATEPROPS(); + UpdateProps(context); } } -END_API_FUNC -AL_API void AL_APIENTRY alDistanceModel(ALenum value) -START_API_FUNC +AL_API DECL_FUNC1(void, alDistanceModel, ALenum) +FORCE_ALIGN void AL_APIENTRY alDistanceModelDirect(ALCcontext *context, ALenum value) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - if(auto model = DistanceModelFromALenum(value)) { std::lock_guard<std::mutex> _{context->mPropLock}; context->mDistanceModel = *model; if(!context->mSourceDistanceModel) - DO_UPDATEPROPS(); + UpdateProps(context); } else context->setError(AL_INVALID_VALUE, "Distance model 0x%04x out of range", value); } -END_API_FUNC -AL_API void AL_APIENTRY alDeferUpdatesSOFT(void) -START_API_FUNC +AL_API DECL_FUNCEXT(void, alDeferUpdates,SOFT) +FORCE_ALIGN void AL_APIENTRY alDeferUpdatesDirectSOFT(ALCcontext *context) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; context->deferUpdates(); } -END_API_FUNC -AL_API void AL_APIENTRY alProcessUpdatesSOFT(void) -START_API_FUNC +AL_API DECL_FUNCEXT(void, alProcessUpdates,SOFT) +FORCE_ALIGN void AL_APIENTRY alProcessUpdatesDirectSOFT(ALCcontext *context) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return; - std::lock_guard<std::mutex> _{context->mPropLock}; context->processUpdates(); } -END_API_FUNC -AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index) -START_API_FUNC +AL_API DECL_FUNCEXT2(const ALchar*, alGetStringi,SOFT, ALenum,ALsizei) +FORCE_ALIGN const ALchar* AL_APIENTRY alGetStringiDirectSOFT(ALCcontext *context, ALenum pname, ALsizei index) noexcept { - ContextRef context{GetContextRef()}; - if(!context) UNLIKELY return nullptr; - const ALchar *value{nullptr}; switch(pname) { @@ -916,7 +615,28 @@ START_API_FUNC } return value; } -END_API_FUNC + + +AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value) noexcept +{ + ContextRef context{GetContextRef()}; + if(!context) UNLIKELY return; + + if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY + context->debugMessage(DebugSource::API, DebugType::DeprecatedBehavior, 0, + DebugSeverity::Medium, + "alDopplerVelocity is deprecated in AL 1.1, use alSpeedOfSound; " + "alDopplerVelocity(x) -> alSpeedOfSound(343.3f * x)"); + + if(!(value >= 0.0f && std::isfinite(value))) + context->setError(AL_INVALID_VALUE, "Doppler velocity %f out of range", value); + else + { + std::lock_guard<std::mutex> _{context->mPropLock}; + context->mDopplerVelocity = value; + UpdateProps(context.get()); + } +} void UpdateContextProps(ALCcontext *context) diff --git a/alc/alc.cpp b/alc/alc.cpp index af8ff55d..6017e743 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -38,6 +38,7 @@ #include <climits> #include <cmath> #include <csignal> +#include <cstddef> #include <cstdint> #include <cstdio> #include <cstdlib> @@ -48,11 +49,13 @@ #include <memory> #include <mutex> #include <new> +#include <optional> #include <stddef.h> #include <stdexcept> #include <string> #include <type_traits> #include <utility> +#include <vector> #include "AL/al.h" #include "AL/alc.h" @@ -61,16 +64,16 @@ #include "al/auxeffectslot.h" #include "al/buffer.h" +#include "al/debug.h" #include "al/effect.h" #include "al/filter.h" #include "al/listener.h" #include "al/source.h" +#include "alc/events.h" #include "albit.h" -#include "albyte.h" #include "alconfig.h" #include "almalloc.h" #include "alnumeric.h" -#include "aloptional.h" #include "alspan.h" #include "alstring.h" #include "alu.h" @@ -96,12 +99,11 @@ #include "core/voice_change.h" #include "device.h" #include "effects/base.h" +#include "export_list.h" #include "inprogext.h" #include "intrusive_ptr.h" #include "opthelpers.h" #include "strutils.h" -#include "threads.h" -#include "vector.h" #include "backends/base.h" #include "backends/null.h" @@ -161,13 +163,6 @@ #endif // ALSOFT_EAX -FILE *gLogFile{stderr}; -#ifdef _DEBUG -LogLevel gLogLevel{LogLevel::Warning}; -#else -LogLevel gLogLevel{LogLevel::Error}; -#endif - /************************************************ * Library initialization ************************************************/ @@ -179,7 +174,7 @@ BOOL APIENTRY DllMain(HINSTANCE module, DWORD reason, LPVOID /*reserved*/) case DLL_PROCESS_ATTACH: /* Pin the DLL so we won't get unloaded until the process terminates */ GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - reinterpret_cast<WCHAR*>(module), &module); + al::bit_cast<WCHAR*>(module), &module); break; } return TRUE; @@ -261,673 +256,6 @@ BackendFactory *PlaybackFactory{}; BackendFactory *CaptureFactory{}; -/************************************************ - * Functions, enums, and errors - ************************************************/ -#define DECL(x) { #x, reinterpret_cast<void*>(x) } -const struct { - const char *funcName; - void *address; -} alcFunctions[] = { - DECL(alcCreateContext), - DECL(alcMakeContextCurrent), - DECL(alcProcessContext), - DECL(alcSuspendContext), - DECL(alcDestroyContext), - DECL(alcGetCurrentContext), - DECL(alcGetContextsDevice), - DECL(alcOpenDevice), - DECL(alcCloseDevice), - DECL(alcGetError), - DECL(alcIsExtensionPresent), - DECL(alcGetProcAddress), - DECL(alcGetEnumValue), - DECL(alcGetString), - DECL(alcGetIntegerv), - DECL(alcCaptureOpenDevice), - DECL(alcCaptureCloseDevice), - DECL(alcCaptureStart), - DECL(alcCaptureStop), - DECL(alcCaptureSamples), - - DECL(alcSetThreadContext), - DECL(alcGetThreadContext), - - DECL(alcLoopbackOpenDeviceSOFT), - DECL(alcIsRenderFormatSupportedSOFT), - DECL(alcRenderSamplesSOFT), - - DECL(alcDevicePauseSOFT), - DECL(alcDeviceResumeSOFT), - - DECL(alcGetStringiSOFT), - DECL(alcResetDeviceSOFT), - - DECL(alcGetInteger64vSOFT), - - DECL(alcReopenDeviceSOFT), - - DECL(alEnable), - DECL(alDisable), - DECL(alIsEnabled), - DECL(alGetString), - DECL(alGetBooleanv), - DECL(alGetIntegerv), - DECL(alGetFloatv), - DECL(alGetDoublev), - DECL(alGetBoolean), - DECL(alGetInteger), - DECL(alGetFloat), - DECL(alGetDouble), - DECL(alGetError), - DECL(alIsExtensionPresent), - DECL(alGetProcAddress), - DECL(alGetEnumValue), - DECL(alListenerf), - DECL(alListener3f), - DECL(alListenerfv), - DECL(alListeneri), - DECL(alListener3i), - DECL(alListeneriv), - DECL(alGetListenerf), - DECL(alGetListener3f), - DECL(alGetListenerfv), - DECL(alGetListeneri), - DECL(alGetListener3i), - DECL(alGetListeneriv), - DECL(alGenSources), - DECL(alDeleteSources), - DECL(alIsSource), - DECL(alSourcef), - DECL(alSource3f), - DECL(alSourcefv), - DECL(alSourcei), - DECL(alSource3i), - DECL(alSourceiv), - DECL(alGetSourcef), - DECL(alGetSource3f), - DECL(alGetSourcefv), - DECL(alGetSourcei), - DECL(alGetSource3i), - DECL(alGetSourceiv), - DECL(alSourcePlayv), - DECL(alSourceStopv), - DECL(alSourceRewindv), - DECL(alSourcePausev), - DECL(alSourcePlay), - DECL(alSourceStop), - DECL(alSourceRewind), - DECL(alSourcePause), - DECL(alSourceQueueBuffers), - DECL(alSourceUnqueueBuffers), - DECL(alGenBuffers), - DECL(alDeleteBuffers), - DECL(alIsBuffer), - DECL(alBufferData), - DECL(alBufferf), - DECL(alBuffer3f), - DECL(alBufferfv), - DECL(alBufferi), - DECL(alBuffer3i), - DECL(alBufferiv), - DECL(alGetBufferf), - DECL(alGetBuffer3f), - DECL(alGetBufferfv), - DECL(alGetBufferi), - DECL(alGetBuffer3i), - DECL(alGetBufferiv), - DECL(alDopplerFactor), - DECL(alDopplerVelocity), - DECL(alSpeedOfSound), - DECL(alDistanceModel), - - DECL(alGenFilters), - DECL(alDeleteFilters), - DECL(alIsFilter), - DECL(alFilteri), - DECL(alFilteriv), - DECL(alFilterf), - DECL(alFilterfv), - DECL(alGetFilteri), - DECL(alGetFilteriv), - DECL(alGetFilterf), - DECL(alGetFilterfv), - DECL(alGenEffects), - DECL(alDeleteEffects), - DECL(alIsEffect), - DECL(alEffecti), - DECL(alEffectiv), - DECL(alEffectf), - DECL(alEffectfv), - DECL(alGetEffecti), - DECL(alGetEffectiv), - DECL(alGetEffectf), - DECL(alGetEffectfv), - DECL(alGenAuxiliaryEffectSlots), - DECL(alDeleteAuxiliaryEffectSlots), - DECL(alIsAuxiliaryEffectSlot), - DECL(alAuxiliaryEffectSloti), - DECL(alAuxiliaryEffectSlotiv), - DECL(alAuxiliaryEffectSlotf), - DECL(alAuxiliaryEffectSlotfv), - DECL(alGetAuxiliaryEffectSloti), - DECL(alGetAuxiliaryEffectSlotiv), - DECL(alGetAuxiliaryEffectSlotf), - DECL(alGetAuxiliaryEffectSlotfv), - - DECL(alDeferUpdatesSOFT), - DECL(alProcessUpdatesSOFT), - - DECL(alSourcedSOFT), - DECL(alSource3dSOFT), - DECL(alSourcedvSOFT), - DECL(alGetSourcedSOFT), - DECL(alGetSource3dSOFT), - DECL(alGetSourcedvSOFT), - DECL(alSourcei64SOFT), - DECL(alSource3i64SOFT), - DECL(alSourcei64vSOFT), - DECL(alGetSourcei64SOFT), - DECL(alGetSource3i64SOFT), - DECL(alGetSourcei64vSOFT), - - DECL(alGetStringiSOFT), - - DECL(alBufferStorageSOFT), - DECL(alMapBufferSOFT), - DECL(alUnmapBufferSOFT), - DECL(alFlushMappedBufferSOFT), - - DECL(alEventControlSOFT), - DECL(alEventCallbackSOFT), - DECL(alGetPointerSOFT), - DECL(alGetPointervSOFT), - - DECL(alBufferCallbackSOFT), - DECL(alGetBufferPtrSOFT), - DECL(alGetBuffer3PtrSOFT), - DECL(alGetBufferPtrvSOFT), - - DECL(alAuxiliaryEffectSlotPlaySOFT), - DECL(alAuxiliaryEffectSlotPlayvSOFT), - DECL(alAuxiliaryEffectSlotStopSOFT), - DECL(alAuxiliaryEffectSlotStopvSOFT), - - DECL(alSourcePlayAtTimeSOFT), - DECL(alSourcePlayAtTimevSOFT), - - DECL(alBufferSubDataSOFT), - - DECL(alBufferDataStatic), -#ifdef ALSOFT_EAX -}, eaxFunctions[] = { - DECL(EAXGet), - DECL(EAXSet), - DECL(EAXGetBufferMode), - DECL(EAXSetBufferMode), -#endif -}; -#undef DECL - -#define DECL(x) { #x, (x) } -constexpr struct { - const ALCchar *enumName; - ALCenum value; -} alcEnumerations[] = { - DECL(ALC_INVALID), - DECL(ALC_FALSE), - DECL(ALC_TRUE), - - DECL(ALC_MAJOR_VERSION), - DECL(ALC_MINOR_VERSION), - DECL(ALC_ATTRIBUTES_SIZE), - DECL(ALC_ALL_ATTRIBUTES), - DECL(ALC_DEFAULT_DEVICE_SPECIFIER), - DECL(ALC_DEVICE_SPECIFIER), - DECL(ALC_ALL_DEVICES_SPECIFIER), - DECL(ALC_DEFAULT_ALL_DEVICES_SPECIFIER), - DECL(ALC_EXTENSIONS), - DECL(ALC_FREQUENCY), - DECL(ALC_REFRESH), - DECL(ALC_SYNC), - DECL(ALC_MONO_SOURCES), - DECL(ALC_STEREO_SOURCES), - DECL(ALC_CAPTURE_DEVICE_SPECIFIER), - DECL(ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER), - DECL(ALC_CAPTURE_SAMPLES), - DECL(ALC_CONNECTED), - - DECL(ALC_EFX_MAJOR_VERSION), - DECL(ALC_EFX_MINOR_VERSION), - DECL(ALC_MAX_AUXILIARY_SENDS), - - DECL(ALC_FORMAT_CHANNELS_SOFT), - DECL(ALC_FORMAT_TYPE_SOFT), - - DECL(ALC_MONO_SOFT), - DECL(ALC_STEREO_SOFT), - DECL(ALC_QUAD_SOFT), - DECL(ALC_5POINT1_SOFT), - DECL(ALC_6POINT1_SOFT), - DECL(ALC_7POINT1_SOFT), - DECL(ALC_BFORMAT3D_SOFT), - - DECL(ALC_BYTE_SOFT), - DECL(ALC_UNSIGNED_BYTE_SOFT), - DECL(ALC_SHORT_SOFT), - DECL(ALC_UNSIGNED_SHORT_SOFT), - DECL(ALC_INT_SOFT), - DECL(ALC_UNSIGNED_INT_SOFT), - DECL(ALC_FLOAT_SOFT), - - DECL(ALC_HRTF_SOFT), - DECL(ALC_DONT_CARE_SOFT), - DECL(ALC_HRTF_STATUS_SOFT), - DECL(ALC_HRTF_DISABLED_SOFT), - DECL(ALC_HRTF_ENABLED_SOFT), - DECL(ALC_HRTF_DENIED_SOFT), - DECL(ALC_HRTF_REQUIRED_SOFT), - DECL(ALC_HRTF_HEADPHONES_DETECTED_SOFT), - DECL(ALC_HRTF_UNSUPPORTED_FORMAT_SOFT), - DECL(ALC_NUM_HRTF_SPECIFIERS_SOFT), - DECL(ALC_HRTF_SPECIFIER_SOFT), - DECL(ALC_HRTF_ID_SOFT), - - DECL(ALC_AMBISONIC_LAYOUT_SOFT), - DECL(ALC_AMBISONIC_SCALING_SOFT), - DECL(ALC_AMBISONIC_ORDER_SOFT), - DECL(ALC_ACN_SOFT), - DECL(ALC_FUMA_SOFT), - DECL(ALC_N3D_SOFT), - DECL(ALC_SN3D_SOFT), - - DECL(ALC_OUTPUT_LIMITER_SOFT), - - DECL(ALC_DEVICE_CLOCK_SOFT), - DECL(ALC_DEVICE_LATENCY_SOFT), - DECL(ALC_DEVICE_CLOCK_LATENCY_SOFT), - DECL(AL_SAMPLE_OFFSET_CLOCK_SOFT), - DECL(AL_SEC_OFFSET_CLOCK_SOFT), - - DECL(ALC_OUTPUT_MODE_SOFT), - DECL(ALC_ANY_SOFT), - DECL(ALC_STEREO_BASIC_SOFT), - DECL(ALC_STEREO_UHJ_SOFT), - DECL(ALC_STEREO_HRTF_SOFT), - DECL(ALC_SURROUND_5_1_SOFT), - DECL(ALC_SURROUND_6_1_SOFT), - DECL(ALC_SURROUND_7_1_SOFT), - - DECL(ALC_NO_ERROR), - DECL(ALC_INVALID_DEVICE), - DECL(ALC_INVALID_CONTEXT), - DECL(ALC_INVALID_ENUM), - DECL(ALC_INVALID_VALUE), - DECL(ALC_OUT_OF_MEMORY), - - - DECL(AL_INVALID), - DECL(AL_NONE), - DECL(AL_FALSE), - DECL(AL_TRUE), - - DECL(AL_SOURCE_RELATIVE), - DECL(AL_CONE_INNER_ANGLE), - DECL(AL_CONE_OUTER_ANGLE), - DECL(AL_PITCH), - DECL(AL_POSITION), - DECL(AL_DIRECTION), - DECL(AL_VELOCITY), - DECL(AL_LOOPING), - DECL(AL_BUFFER), - DECL(AL_GAIN), - DECL(AL_MIN_GAIN), - DECL(AL_MAX_GAIN), - DECL(AL_ORIENTATION), - DECL(AL_REFERENCE_DISTANCE), - DECL(AL_ROLLOFF_FACTOR), - DECL(AL_CONE_OUTER_GAIN), - DECL(AL_MAX_DISTANCE), - DECL(AL_SEC_OFFSET), - DECL(AL_SAMPLE_OFFSET), - DECL(AL_BYTE_OFFSET), - DECL(AL_SOURCE_TYPE), - DECL(AL_STATIC), - DECL(AL_STREAMING), - DECL(AL_UNDETERMINED), - DECL(AL_METERS_PER_UNIT), - DECL(AL_LOOP_POINTS_SOFT), - DECL(AL_DIRECT_CHANNELS_SOFT), - - DECL(AL_DIRECT_FILTER), - DECL(AL_AUXILIARY_SEND_FILTER), - DECL(AL_AIR_ABSORPTION_FACTOR), - DECL(AL_ROOM_ROLLOFF_FACTOR), - DECL(AL_CONE_OUTER_GAINHF), - DECL(AL_DIRECT_FILTER_GAINHF_AUTO), - DECL(AL_AUXILIARY_SEND_FILTER_GAIN_AUTO), - DECL(AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO), - - DECL(AL_SOURCE_STATE), - DECL(AL_INITIAL), - DECL(AL_PLAYING), - DECL(AL_PAUSED), - DECL(AL_STOPPED), - - DECL(AL_BUFFERS_QUEUED), - DECL(AL_BUFFERS_PROCESSED), - - DECL(AL_FORMAT_MONO8), - DECL(AL_FORMAT_MONO16), - DECL(AL_FORMAT_MONO_FLOAT32), - DECL(AL_FORMAT_MONO_DOUBLE_EXT), - DECL(AL_FORMAT_STEREO8), - DECL(AL_FORMAT_STEREO16), - DECL(AL_FORMAT_STEREO_FLOAT32), - DECL(AL_FORMAT_STEREO_DOUBLE_EXT), - DECL(AL_FORMAT_MONO_IMA4), - DECL(AL_FORMAT_STEREO_IMA4), - DECL(AL_FORMAT_MONO_MSADPCM_SOFT), - DECL(AL_FORMAT_STEREO_MSADPCM_SOFT), - DECL(AL_FORMAT_QUAD8_LOKI), - DECL(AL_FORMAT_QUAD16_LOKI), - DECL(AL_FORMAT_QUAD8), - DECL(AL_FORMAT_QUAD16), - DECL(AL_FORMAT_QUAD32), - DECL(AL_FORMAT_51CHN8), - DECL(AL_FORMAT_51CHN16), - DECL(AL_FORMAT_51CHN32), - DECL(AL_FORMAT_61CHN8), - DECL(AL_FORMAT_61CHN16), - DECL(AL_FORMAT_61CHN32), - DECL(AL_FORMAT_71CHN8), - DECL(AL_FORMAT_71CHN16), - DECL(AL_FORMAT_71CHN32), - DECL(AL_FORMAT_REAR8), - DECL(AL_FORMAT_REAR16), - DECL(AL_FORMAT_REAR32), - DECL(AL_FORMAT_MONO_MULAW), - DECL(AL_FORMAT_MONO_MULAW_EXT), - DECL(AL_FORMAT_STEREO_MULAW), - DECL(AL_FORMAT_STEREO_MULAW_EXT), - DECL(AL_FORMAT_QUAD_MULAW), - DECL(AL_FORMAT_51CHN_MULAW), - DECL(AL_FORMAT_61CHN_MULAW), - DECL(AL_FORMAT_71CHN_MULAW), - DECL(AL_FORMAT_REAR_MULAW), - DECL(AL_FORMAT_MONO_ALAW_EXT), - DECL(AL_FORMAT_STEREO_ALAW_EXT), - - DECL(AL_FORMAT_BFORMAT2D_8), - DECL(AL_FORMAT_BFORMAT2D_16), - DECL(AL_FORMAT_BFORMAT2D_FLOAT32), - DECL(AL_FORMAT_BFORMAT2D_MULAW), - DECL(AL_FORMAT_BFORMAT3D_8), - DECL(AL_FORMAT_BFORMAT3D_16), - DECL(AL_FORMAT_BFORMAT3D_FLOAT32), - DECL(AL_FORMAT_BFORMAT3D_MULAW), - - DECL(AL_FREQUENCY), - DECL(AL_BITS), - DECL(AL_CHANNELS), - DECL(AL_SIZE), - DECL(AL_UNPACK_BLOCK_ALIGNMENT_SOFT), - DECL(AL_PACK_BLOCK_ALIGNMENT_SOFT), - - DECL(AL_SOURCE_RADIUS), - - DECL(AL_SAMPLE_OFFSET_LATENCY_SOFT), - DECL(AL_SEC_OFFSET_LATENCY_SOFT), - - DECL(AL_STEREO_ANGLES), - - DECL(AL_UNUSED), - DECL(AL_PENDING), - DECL(AL_PROCESSED), - - DECL(AL_NO_ERROR), - DECL(AL_INVALID_NAME), - DECL(AL_INVALID_ENUM), - DECL(AL_INVALID_VALUE), - DECL(AL_INVALID_OPERATION), - DECL(AL_OUT_OF_MEMORY), - - DECL(AL_VENDOR), - DECL(AL_VERSION), - DECL(AL_RENDERER), - DECL(AL_EXTENSIONS), - - DECL(AL_DOPPLER_FACTOR), - DECL(AL_DOPPLER_VELOCITY), - DECL(AL_DISTANCE_MODEL), - DECL(AL_SPEED_OF_SOUND), - DECL(AL_SOURCE_DISTANCE_MODEL), - DECL(AL_DEFERRED_UPDATES_SOFT), - DECL(AL_GAIN_LIMIT_SOFT), - - DECL(AL_INVERSE_DISTANCE), - DECL(AL_INVERSE_DISTANCE_CLAMPED), - DECL(AL_LINEAR_DISTANCE), - DECL(AL_LINEAR_DISTANCE_CLAMPED), - DECL(AL_EXPONENT_DISTANCE), - DECL(AL_EXPONENT_DISTANCE_CLAMPED), - - DECL(AL_FILTER_TYPE), - DECL(AL_FILTER_NULL), - DECL(AL_FILTER_LOWPASS), - DECL(AL_FILTER_HIGHPASS), - DECL(AL_FILTER_BANDPASS), - - DECL(AL_LOWPASS_GAIN), - DECL(AL_LOWPASS_GAINHF), - - DECL(AL_HIGHPASS_GAIN), - DECL(AL_HIGHPASS_GAINLF), - - DECL(AL_BANDPASS_GAIN), - DECL(AL_BANDPASS_GAINHF), - DECL(AL_BANDPASS_GAINLF), - - DECL(AL_EFFECT_TYPE), - DECL(AL_EFFECT_NULL), - DECL(AL_EFFECT_REVERB), - DECL(AL_EFFECT_EAXREVERB), - DECL(AL_EFFECT_CHORUS), - DECL(AL_EFFECT_DISTORTION), - DECL(AL_EFFECT_ECHO), - DECL(AL_EFFECT_FLANGER), - DECL(AL_EFFECT_PITCH_SHIFTER), - DECL(AL_EFFECT_FREQUENCY_SHIFTER), - DECL(AL_EFFECT_VOCAL_MORPHER), - DECL(AL_EFFECT_RING_MODULATOR), - DECL(AL_EFFECT_AUTOWAH), - DECL(AL_EFFECT_COMPRESSOR), - DECL(AL_EFFECT_EQUALIZER), - DECL(AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT), - DECL(AL_EFFECT_DEDICATED_DIALOGUE), - - DECL(AL_EFFECTSLOT_EFFECT), - DECL(AL_EFFECTSLOT_GAIN), - DECL(AL_EFFECTSLOT_AUXILIARY_SEND_AUTO), - DECL(AL_EFFECTSLOT_NULL), - - DECL(AL_EAXREVERB_DENSITY), - DECL(AL_EAXREVERB_DIFFUSION), - DECL(AL_EAXREVERB_GAIN), - DECL(AL_EAXREVERB_GAINHF), - DECL(AL_EAXREVERB_GAINLF), - DECL(AL_EAXREVERB_DECAY_TIME), - DECL(AL_EAXREVERB_DECAY_HFRATIO), - DECL(AL_EAXREVERB_DECAY_LFRATIO), - DECL(AL_EAXREVERB_REFLECTIONS_GAIN), - DECL(AL_EAXREVERB_REFLECTIONS_DELAY), - DECL(AL_EAXREVERB_REFLECTIONS_PAN), - DECL(AL_EAXREVERB_LATE_REVERB_GAIN), - DECL(AL_EAXREVERB_LATE_REVERB_DELAY), - DECL(AL_EAXREVERB_LATE_REVERB_PAN), - DECL(AL_EAXREVERB_ECHO_TIME), - DECL(AL_EAXREVERB_ECHO_DEPTH), - DECL(AL_EAXREVERB_MODULATION_TIME), - DECL(AL_EAXREVERB_MODULATION_DEPTH), - DECL(AL_EAXREVERB_AIR_ABSORPTION_GAINHF), - DECL(AL_EAXREVERB_HFREFERENCE), - DECL(AL_EAXREVERB_LFREFERENCE), - DECL(AL_EAXREVERB_ROOM_ROLLOFF_FACTOR), - DECL(AL_EAXREVERB_DECAY_HFLIMIT), - - DECL(AL_REVERB_DENSITY), - DECL(AL_REVERB_DIFFUSION), - DECL(AL_REVERB_GAIN), - DECL(AL_REVERB_GAINHF), - DECL(AL_REVERB_DECAY_TIME), - DECL(AL_REVERB_DECAY_HFRATIO), - DECL(AL_REVERB_REFLECTIONS_GAIN), - DECL(AL_REVERB_REFLECTIONS_DELAY), - DECL(AL_REVERB_LATE_REVERB_GAIN), - DECL(AL_REVERB_LATE_REVERB_DELAY), - DECL(AL_REVERB_AIR_ABSORPTION_GAINHF), - DECL(AL_REVERB_ROOM_ROLLOFF_FACTOR), - DECL(AL_REVERB_DECAY_HFLIMIT), - - DECL(AL_CHORUS_WAVEFORM), - DECL(AL_CHORUS_PHASE), - DECL(AL_CHORUS_RATE), - DECL(AL_CHORUS_DEPTH), - DECL(AL_CHORUS_FEEDBACK), - DECL(AL_CHORUS_DELAY), - - DECL(AL_DISTORTION_EDGE), - DECL(AL_DISTORTION_GAIN), - DECL(AL_DISTORTION_LOWPASS_CUTOFF), - DECL(AL_DISTORTION_EQCENTER), - DECL(AL_DISTORTION_EQBANDWIDTH), - - DECL(AL_ECHO_DELAY), - DECL(AL_ECHO_LRDELAY), - DECL(AL_ECHO_DAMPING), - DECL(AL_ECHO_FEEDBACK), - DECL(AL_ECHO_SPREAD), - - DECL(AL_FLANGER_WAVEFORM), - DECL(AL_FLANGER_PHASE), - DECL(AL_FLANGER_RATE), - DECL(AL_FLANGER_DEPTH), - DECL(AL_FLANGER_FEEDBACK), - DECL(AL_FLANGER_DELAY), - - DECL(AL_FREQUENCY_SHIFTER_FREQUENCY), - DECL(AL_FREQUENCY_SHIFTER_LEFT_DIRECTION), - DECL(AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION), - - DECL(AL_RING_MODULATOR_FREQUENCY), - DECL(AL_RING_MODULATOR_HIGHPASS_CUTOFF), - DECL(AL_RING_MODULATOR_WAVEFORM), - - DECL(AL_PITCH_SHIFTER_COARSE_TUNE), - DECL(AL_PITCH_SHIFTER_FINE_TUNE), - - DECL(AL_COMPRESSOR_ONOFF), - - DECL(AL_EQUALIZER_LOW_GAIN), - DECL(AL_EQUALIZER_LOW_CUTOFF), - DECL(AL_EQUALIZER_MID1_GAIN), - DECL(AL_EQUALIZER_MID1_CENTER), - DECL(AL_EQUALIZER_MID1_WIDTH), - DECL(AL_EQUALIZER_MID2_GAIN), - DECL(AL_EQUALIZER_MID2_CENTER), - DECL(AL_EQUALIZER_MID2_WIDTH), - DECL(AL_EQUALIZER_HIGH_GAIN), - DECL(AL_EQUALIZER_HIGH_CUTOFF), - - DECL(AL_DEDICATED_GAIN), - - DECL(AL_AUTOWAH_ATTACK_TIME), - DECL(AL_AUTOWAH_RELEASE_TIME), - DECL(AL_AUTOWAH_RESONANCE), - DECL(AL_AUTOWAH_PEAK_GAIN), - - DECL(AL_VOCAL_MORPHER_PHONEMEA), - DECL(AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING), - DECL(AL_VOCAL_MORPHER_PHONEMEB), - DECL(AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING), - DECL(AL_VOCAL_MORPHER_WAVEFORM), - DECL(AL_VOCAL_MORPHER_RATE), - - DECL(AL_EFFECTSLOT_TARGET_SOFT), - - DECL(AL_NUM_RESAMPLERS_SOFT), - DECL(AL_DEFAULT_RESAMPLER_SOFT), - DECL(AL_SOURCE_RESAMPLER_SOFT), - DECL(AL_RESAMPLER_NAME_SOFT), - - DECL(AL_SOURCE_SPATIALIZE_SOFT), - DECL(AL_AUTO_SOFT), - - DECL(AL_MAP_READ_BIT_SOFT), - DECL(AL_MAP_WRITE_BIT_SOFT), - DECL(AL_MAP_PERSISTENT_BIT_SOFT), - DECL(AL_PRESERVE_DATA_BIT_SOFT), - - DECL(AL_EVENT_CALLBACK_FUNCTION_SOFT), - DECL(AL_EVENT_CALLBACK_USER_PARAM_SOFT), - DECL(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT), - DECL(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT), - DECL(AL_EVENT_TYPE_DISCONNECTED_SOFT), - - DECL(AL_DROP_UNMATCHED_SOFT), - DECL(AL_REMIX_UNMATCHED_SOFT), - - DECL(AL_AMBISONIC_LAYOUT_SOFT), - DECL(AL_AMBISONIC_SCALING_SOFT), - DECL(AL_FUMA_SOFT), - DECL(AL_ACN_SOFT), - DECL(AL_SN3D_SOFT), - DECL(AL_N3D_SOFT), - - DECL(AL_BUFFER_CALLBACK_FUNCTION_SOFT), - DECL(AL_BUFFER_CALLBACK_USER_PARAM_SOFT), - - DECL(AL_UNPACK_AMBISONIC_ORDER_SOFT), - - DECL(AL_EFFECT_CONVOLUTION_REVERB_SOFT), - DECL(AL_EFFECTSLOT_STATE_SOFT), - - DECL(AL_FORMAT_UHJ2CHN8_SOFT), - DECL(AL_FORMAT_UHJ2CHN16_SOFT), - DECL(AL_FORMAT_UHJ2CHN_FLOAT32_SOFT), - DECL(AL_FORMAT_UHJ3CHN8_SOFT), - DECL(AL_FORMAT_UHJ3CHN16_SOFT), - DECL(AL_FORMAT_UHJ3CHN_FLOAT32_SOFT), - DECL(AL_FORMAT_UHJ4CHN8_SOFT), - DECL(AL_FORMAT_UHJ4CHN16_SOFT), - DECL(AL_FORMAT_UHJ4CHN_FLOAT32_SOFT), - DECL(AL_STEREO_MODE_SOFT), - DECL(AL_NORMAL_SOFT), - DECL(AL_SUPER_STEREO_SOFT), - DECL(AL_SUPER_STEREO_WIDTH_SOFT), - - DECL(AL_FORMAT_UHJ2CHN_MULAW_SOFT), - DECL(AL_FORMAT_UHJ2CHN_ALAW_SOFT), - DECL(AL_FORMAT_UHJ2CHN_IMA4_SOFT), - DECL(AL_FORMAT_UHJ2CHN_MSADPCM_SOFT), - DECL(AL_FORMAT_UHJ3CHN_MULAW_SOFT), - DECL(AL_FORMAT_UHJ3CHN_ALAW_SOFT), - DECL(AL_FORMAT_UHJ4CHN_MULAW_SOFT), - DECL(AL_FORMAT_UHJ4CHN_ALAW_SOFT), - - DECL(AL_STOP_SOURCES_ON_DISCONNECT_SOFT), - -#ifdef ALSOFT_EAX -}, eaxEnumerations[] = { - DECL(AL_EAX_RAM_SIZE), - DECL(AL_EAX_RAM_FREE), - DECL(AL_STORAGE_AUTOMATIC), - DECL(AL_STORAGE_HARDWARE), - DECL(AL_STORAGE_ACCESSIBLE), -#endif // ALSOFT_EAX -}; -#undef DECL - constexpr ALCchar alcNoError[] = "No Error"; constexpr ALCchar alcErrInvalidDevice[] = "Invalid Device"; constexpr ALCchar alcErrInvalidContext[] = "Invalid Context"; @@ -974,16 +302,20 @@ constexpr ALCchar alcNoDeviceExtList[] = "ALC_ENUMERATE_ALL_EXT " "ALC_ENUMERATION_EXT " "ALC_EXT_CAPTURE " + "ALC_EXTX_direct_context " "ALC_EXT_EFX " "ALC_EXT_thread_local_context " "ALC_SOFT_loopback " "ALC_SOFT_loopback_bformat " - "ALC_SOFT_reopen_device"; + "ALC_SOFT_reopen_device " + "ALC_SOFT_system_events"; constexpr ALCchar alcExtensionList[] = "ALC_ENUMERATE_ALL_EXT " "ALC_ENUMERATION_EXT " "ALC_EXT_CAPTURE " + "ALC_EXT_debug " "ALC_EXT_DEDICATED " + "ALC_EXTX_direct_context " "ALC_EXT_disconnect " "ALC_EXT_EFX " "ALC_EXT_thread_local_context " @@ -994,7 +326,8 @@ constexpr ALCchar alcExtensionList[] = "ALC_SOFT_output_limiter " "ALC_SOFT_output_mode " "ALC_SOFT_pause_device " - "ALC_SOFT_reopen_device"; + "ALC_SOFT_reopen_device " + "ALC_SOFT_system_events"; constexpr int alcMajorVersion{1}; constexpr int alcMinorVersion{1}; @@ -1008,8 +341,8 @@ using DeviceRef = al::intrusive_ptr<ALCdevice>; /************************************************ * Device lists ************************************************/ -al::vector<ALCdevice*> DeviceList; -al::vector<ALCcontext*> ContextList; +std::vector<ALCdevice*> DeviceList; +std::vector<ALCcontext*> ContextList; std::recursive_mutex ListLock; @@ -1034,7 +367,7 @@ void alc_initconfig(void) if(logf) gLogFile = logf; else { - auto u8name = wstr_to_utf8(logfile->c_str()); + auto u8name = wstr_to_utf8(*logfile); ERR("Failed to open log file '%s'\n", u8name.c_str()); } } @@ -1051,7 +384,7 @@ void alc_initconfig(void) ALSOFT_GIT_BRANCH); { std::string names; - if(al::size(BackendList) < 1) + if(std::size(BackendList) < 1) names = "(none)"; else { @@ -1171,13 +504,7 @@ void alc_initconfig(void) } Voice::InitMixer(ConfigValueStr(nullptr, nullptr, "resampler")); - auto uhjfiltopt = ConfigValueStr(nullptr, "uhj", "decode-filter"); - if(!uhjfiltopt) - { - if((uhjfiltopt = ConfigValueStr(nullptr, "uhj", "filter"))) - WARN("uhj/filter is deprecated, please use uhj/decode-filter\n"); - } - if(uhjfiltopt) + if(auto uhjfiltopt = ConfigValueStr(nullptr, "uhj", "decode-filter")) { if(al::strcasecmp(uhjfiltopt->c_str(), "fir256") == 0) UhjDecodeQuality = UhjQualityType::FIR256; @@ -1188,7 +515,7 @@ void alc_initconfig(void) else WARN("Unsupported uhj/decode-filter: %s\n", uhjfiltopt->c_str()); } - if((uhjfiltopt = ConfigValueStr(nullptr, "uhj", "encode-filter"))) + if(auto uhjfiltopt = ConfigValueStr(nullptr, "uhj", "encode-filter")) { if(al::strcasecmp(uhjfiltopt->c_str(), "fir256") == 0) UhjEncodeQuality = UhjQualityType::FIR256; @@ -1407,48 +734,58 @@ void ProbeCaptureDeviceList() struct DevFmtPair { DevFmtChannels chans; DevFmtType type; }; -al::optional<DevFmtPair> DecomposeDevFormat(ALenum format) +std::optional<DevFmtPair> DecomposeDevFormat(ALenum format) { static const struct { ALenum format; DevFmtChannels channels; DevFmtType type; } list[] = { - { AL_FORMAT_MONO8, DevFmtMono, DevFmtUByte }, - { AL_FORMAT_MONO16, DevFmtMono, DevFmtShort }, + { AL_FORMAT_MONO8, DevFmtMono, DevFmtUByte }, + { AL_FORMAT_MONO16, DevFmtMono, DevFmtShort }, + { AL_FORMAT_MONO_I32, DevFmtMono, DevFmtInt }, { AL_FORMAT_MONO_FLOAT32, DevFmtMono, DevFmtFloat }, - { AL_FORMAT_STEREO8, DevFmtStereo, DevFmtUByte }, - { AL_FORMAT_STEREO16, DevFmtStereo, DevFmtShort }, + { AL_FORMAT_STEREO8, DevFmtStereo, DevFmtUByte }, + { AL_FORMAT_STEREO16, DevFmtStereo, DevFmtShort }, + { AL_FORMAT_STEREO_I32, DevFmtStereo, DevFmtInt }, { AL_FORMAT_STEREO_FLOAT32, DevFmtStereo, DevFmtFloat }, - { AL_FORMAT_QUAD8, DevFmtQuad, DevFmtUByte }, - { AL_FORMAT_QUAD16, DevFmtQuad, DevFmtShort }, - { AL_FORMAT_QUAD32, DevFmtQuad, DevFmtFloat }, - - { AL_FORMAT_51CHN8, DevFmtX51, DevFmtUByte }, - { AL_FORMAT_51CHN16, DevFmtX51, DevFmtShort }, - { AL_FORMAT_51CHN32, DevFmtX51, DevFmtFloat }, - - { AL_FORMAT_61CHN8, DevFmtX61, DevFmtUByte }, - { AL_FORMAT_61CHN16, DevFmtX61, DevFmtShort }, - { AL_FORMAT_61CHN32, DevFmtX61, DevFmtFloat }, - - { AL_FORMAT_71CHN8, DevFmtX71, DevFmtUByte }, - { AL_FORMAT_71CHN16, DevFmtX71, DevFmtShort }, - { AL_FORMAT_71CHN32, DevFmtX71, DevFmtFloat }, + { AL_FORMAT_QUAD8, DevFmtQuad, DevFmtUByte }, + { AL_FORMAT_QUAD16, DevFmtQuad, DevFmtShort }, + { AL_FORMAT_QUAD32, DevFmtQuad, DevFmtFloat }, + { AL_FORMAT_QUAD_I32, DevFmtQuad, DevFmtInt }, + { AL_FORMAT_QUAD_FLOAT32, DevFmtQuad, DevFmtFloat }, + + { AL_FORMAT_51CHN8, DevFmtX51, DevFmtUByte }, + { AL_FORMAT_51CHN16, DevFmtX51, DevFmtShort }, + { AL_FORMAT_51CHN32, DevFmtX51, DevFmtFloat }, + { AL_FORMAT_51CHN_I32, DevFmtX51, DevFmtInt }, + { AL_FORMAT_51CHN_FLOAT32, DevFmtX51, DevFmtFloat }, + + { AL_FORMAT_61CHN8, DevFmtX61, DevFmtUByte }, + { AL_FORMAT_61CHN16, DevFmtX61, DevFmtShort }, + { AL_FORMAT_61CHN32, DevFmtX61, DevFmtFloat }, + { AL_FORMAT_61CHN_I32, DevFmtX61, DevFmtInt }, + { AL_FORMAT_61CHN_FLOAT32, DevFmtX61, DevFmtFloat }, + + { AL_FORMAT_71CHN8, DevFmtX71, DevFmtUByte }, + { AL_FORMAT_71CHN16, DevFmtX71, DevFmtShort }, + { AL_FORMAT_71CHN32, DevFmtX71, DevFmtFloat }, + { AL_FORMAT_71CHN_I32, DevFmtX71, DevFmtInt }, + { AL_FORMAT_71CHN_FLOAT32, DevFmtX71, DevFmtFloat }, }; for(const auto &item : list) { if(item.format == format) - return al::make_optional<DevFmtPair>({item.channels, item.type}); + return DevFmtPair{item.channels, item.type}; } - return al::nullopt; + return std::nullopt; } -al::optional<DevFmtType> DevFmtTypeFromEnum(ALCenum type) +std::optional<DevFmtType> DevFmtTypeFromEnum(ALCenum type) { switch(type) { @@ -1461,7 +798,7 @@ al::optional<DevFmtType> DevFmtTypeFromEnum(ALCenum type) case ALC_FLOAT_SOFT: return DevFmtFloat; } WARN("Unsupported format type: 0x%04x\n", type); - return al::nullopt; + return std::nullopt; } ALCenum EnumFromDevFmt(DevFmtType type) { @@ -1478,7 +815,7 @@ ALCenum EnumFromDevFmt(DevFmtType type) throw std::runtime_error{"Invalid DevFmtType: "+std::to_string(int(type))}; } -al::optional<DevFmtChannels> DevFmtChannelsFromEnum(ALCenum channels) +std::optional<DevFmtChannels> DevFmtChannelsFromEnum(ALCenum channels) { switch(channels) { @@ -1491,7 +828,7 @@ al::optional<DevFmtChannels> DevFmtChannelsFromEnum(ALCenum channels) case ALC_BFORMAT3D_SOFT: return DevFmtAmbi3D; } WARN("Unsupported format channels: 0x%04x\n", channels); - return al::nullopt; + return std::nullopt; } ALCenum EnumFromDevFmt(DevFmtChannels channels) { @@ -1511,7 +848,7 @@ ALCenum EnumFromDevFmt(DevFmtChannels channels) throw std::runtime_error{"Invalid DevFmtChannels: "+std::to_string(int(channels))}; } -al::optional<DevAmbiLayout> DevAmbiLayoutFromEnum(ALCenum layout) +std::optional<DevAmbiLayout> DevAmbiLayoutFromEnum(ALCenum layout) { switch(layout) { @@ -1519,7 +856,7 @@ al::optional<DevAmbiLayout> DevAmbiLayoutFromEnum(ALCenum layout) case ALC_ACN_SOFT: return DevAmbiLayout::ACN; } WARN("Unsupported ambisonic layout: 0x%04x\n", layout); - return al::nullopt; + return std::nullopt; } ALCenum EnumFromDevAmbi(DevAmbiLayout layout) { @@ -1531,7 +868,7 @@ ALCenum EnumFromDevAmbi(DevAmbiLayout layout) throw std::runtime_error{"Invalid DevAmbiLayout: "+std::to_string(int(layout))}; } -al::optional<DevAmbiScaling> DevAmbiScalingFromEnum(ALCenum scaling) +std::optional<DevAmbiScaling> DevAmbiScalingFromEnum(ALCenum scaling) { switch(scaling) { @@ -1540,7 +877,7 @@ al::optional<DevAmbiScaling> DevAmbiScalingFromEnum(ALCenum scaling) case ALC_N3D_SOFT: return DevAmbiScaling::N3D; } WARN("Unsupported ambisonic scaling: 0x%04x\n", scaling); - return al::nullopt; + return std::nullopt; } ALCenum EnumFromDevAmbi(DevAmbiScaling scaling) { @@ -1554,95 +891,60 @@ ALCenum EnumFromDevAmbi(DevAmbiScaling scaling) } -/* Downmixing channel arrays, to map the given format's missing channels to - * existing ones. Based on Wine's DSound downmix values, which are based on - * PulseAudio's. +/* Downmixing channel arrays, to map a device format's missing channels to + * existing ones. Based on what PipeWire does, though simplified. */ -constexpr std::array<InputRemixMap::TargetMix,2> FrontStereoSplit{{ - {FrontLeft, 0.5f}, {FrontRight, 0.5f} -}}; -constexpr std::array<InputRemixMap::TargetMix,1> FrontLeft9{{ - {FrontLeft, 1.0f/9.0f} -}}; -constexpr std::array<InputRemixMap::TargetMix,1> FrontRight9{{ - {FrontRight, 1.0f/9.0f} -}}; -constexpr std::array<InputRemixMap::TargetMix,2> BackMonoToFrontSplit{{ - {FrontLeft, 0.5f/9.0f}, {FrontRight, 0.5f/9.0f} -}}; -constexpr std::array<InputRemixMap::TargetMix,2> LeftStereoSplit{{ - {FrontLeft, 0.5f}, {BackLeft, 0.5f} -}}; -constexpr std::array<InputRemixMap::TargetMix,2> RightStereoSplit{{ - {FrontRight, 0.5f}, {BackRight, 0.5f} -}}; -constexpr std::array<InputRemixMap::TargetMix,2> BackStereoSplit{{ - {BackLeft, 0.5f}, {BackRight, 0.5f} -}}; -constexpr std::array<InputRemixMap::TargetMix,2> SideStereoSplit{{ - {SideLeft, 0.5f}, {SideRight, 0.5f} -}}; -constexpr std::array<InputRemixMap::TargetMix,1> ToSideLeft{{ - {SideLeft, 1.0f} -}}; -constexpr std::array<InputRemixMap::TargetMix,1> ToSideRight{{ - {SideRight, 1.0f} -}}; -constexpr std::array<InputRemixMap::TargetMix,2> BackLeftSplit{{ - {SideLeft, 0.5f}, {BackCenter, 0.5f} -}}; -constexpr std::array<InputRemixMap::TargetMix,2> BackRightSplit{{ - {SideRight, 0.5f}, {BackCenter, 0.5f} -}}; - -const std::array<InputRemixMap,6> StereoDownmix{{ - { FrontCenter, FrontStereoSplit }, - { SideLeft, FrontLeft9 }, - { SideRight, FrontRight9 }, - { BackLeft, FrontLeft9 }, - { BackRight, FrontRight9 }, - { BackCenter, BackMonoToFrontSplit }, -}}; -const std::array<InputRemixMap,4> QuadDownmix{{ - { FrontCenter, FrontStereoSplit }, - { SideLeft, LeftStereoSplit }, - { SideRight, RightStereoSplit }, - { BackCenter, BackStereoSplit }, -}}; -const std::array<InputRemixMap,3> X51Downmix{{ - { BackLeft, ToSideLeft }, - { BackRight, ToSideRight }, - { BackCenter, SideStereoSplit }, -}}; -const std::array<InputRemixMap,2> X61Downmix{{ - { BackLeft, BackLeftSplit }, - { BackRight, BackRightSplit }, -}}; -const std::array<InputRemixMap,1> X71Downmix{{ - { BackCenter, BackStereoSplit }, -}}; - - -/** Stores the latest ALC device error. */ -void alcSetError(ALCdevice *device, ALCenum errorCode) -{ - WARN("Error generated on device %p, code 0x%04x\n", voidp{device}, errorCode); - if(TrapALCError) - { -#ifdef _WIN32 - /* DebugBreak() will cause an exception if there is no debugger */ - if(IsDebuggerPresent()) - DebugBreak(); -#elif defined(SIGTRAP) - raise(SIGTRAP); -#endif - } - - if(device) - device->LastError.store(errorCode); - else - LastNullDeviceError.store(errorCode); -} +constexpr float inv_sqrt2f{static_cast<float>(1.0 / al::numbers::sqrt2)}; +constexpr std::array FrontStereo3dB{ + InputRemixMap::TargetMix{FrontLeft, inv_sqrt2f}, + InputRemixMap::TargetMix{FrontRight, inv_sqrt2f} +}; +constexpr std::array FrontStereo6dB{ + InputRemixMap::TargetMix{FrontLeft, 0.5f}, + InputRemixMap::TargetMix{FrontRight, 0.5f} +}; +constexpr std::array SideStereo3dB{ + InputRemixMap::TargetMix{SideLeft, inv_sqrt2f}, + InputRemixMap::TargetMix{SideRight, inv_sqrt2f} +}; +constexpr std::array BackStereo3dB{ + InputRemixMap::TargetMix{BackLeft, inv_sqrt2f}, + InputRemixMap::TargetMix{BackRight, inv_sqrt2f} +}; +constexpr std::array FrontLeft3dB{InputRemixMap::TargetMix{FrontLeft, inv_sqrt2f}}; +constexpr std::array FrontRight3dB{InputRemixMap::TargetMix{FrontRight, inv_sqrt2f}}; +constexpr std::array SideLeft0dB{InputRemixMap::TargetMix{SideLeft, 1.0f}}; +constexpr std::array SideRight0dB{InputRemixMap::TargetMix{SideRight, 1.0f}}; +constexpr std::array BackLeft0dB{InputRemixMap::TargetMix{BackLeft, 1.0f}}; +constexpr std::array BackRight0dB{InputRemixMap::TargetMix{BackRight, 1.0f}}; +constexpr std::array BackCenter3dB{InputRemixMap::TargetMix{BackCenter, inv_sqrt2f}}; + +constexpr std::array StereoDownmix{ + InputRemixMap{FrontCenter, FrontStereo3dB}, + InputRemixMap{SideLeft, FrontLeft3dB}, + InputRemixMap{SideRight, FrontRight3dB}, + InputRemixMap{BackLeft, FrontLeft3dB}, + InputRemixMap{BackRight, FrontRight3dB}, + InputRemixMap{BackCenter, FrontStereo6dB}, +}; +constexpr std::array QuadDownmix{ + InputRemixMap{FrontCenter, FrontStereo3dB}, + InputRemixMap{SideLeft, BackLeft0dB}, + InputRemixMap{SideRight, BackRight0dB}, + InputRemixMap{BackCenter, BackStereo3dB}, +}; +constexpr std::array X51Downmix{ + InputRemixMap{BackLeft, SideLeft0dB}, + InputRemixMap{BackRight, SideRight0dB}, + InputRemixMap{BackCenter, SideStereo3dB}, +}; +constexpr std::array X61Downmix{ + InputRemixMap{BackLeft, BackCenter3dB}, + InputRemixMap{BackRight, BackCenter3dB}, +}; +constexpr std::array X71Downmix{ + InputRemixMap{BackCenter, BackStereo3dB}, +}; std::unique_ptr<Compressor> CreateDeviceLimiter(const ALCdevice *device, const float threshold) @@ -1695,13 +997,13 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) uint numMono{device->NumMonoSources}; uint numStereo{device->NumStereoSources}; uint numSends{device->NumAuxSends}; - al::optional<StereoEncoding> stereomode; - al::optional<bool> optlimit; - al::optional<uint> optsrate; - al::optional<DevFmtChannels> optchans; - al::optional<DevFmtType> opttype; - al::optional<DevAmbiLayout> optlayout; - al::optional<DevAmbiScaling> optscale; + std::optional<StereoEncoding> stereomode; + std::optional<bool> optlimit; + std::optional<uint> optsrate; + std::optional<DevFmtChannels> optchans; + std::optional<DevFmtType> opttype; + std::optional<DevAmbiLayout> optlayout; + std::optional<DevAmbiScaling> optscale; uint period_size{DEFAULT_UPDATE_SIZE}; uint buffer_size{DEFAULT_UPDATE_SIZE * DEFAULT_NUM_UPDATES}; int hrtf_id{-1}; @@ -1844,7 +1146,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) if(attrList && attrList[0]) { ALenum outmode{ALC_ANY_SOFT}; - al::optional<bool> opthrtf; + std::optional<bool> opthrtf; int freqAttr{}; #define ATTRIBUTE(a) a: TRACE("%s = %d\n", #a, attrList[attrIdx + 1]); @@ -1904,7 +1206,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) else if(attrList[attrIdx + 1] == ALC_TRUE) opthrtf = true; else if(attrList[attrIdx + 1] == ALC_DONT_CARE_SOFT) - opthrtf = al::nullopt; + opthrtf = std::nullopt; break; case ATTRIBUTE(ALC_HRTF_ID_SOFT) @@ -1917,7 +1219,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) else if(attrList[attrIdx + 1] == ALC_TRUE) optlimit = true; else if(attrList[attrIdx + 1] == ALC_DONT_CARE_SOFT) - optlimit = al::nullopt; + optlimit = std::nullopt; break; case ATTRIBUTE(ALC_OUTPUT_MODE_SOFT) @@ -2542,6 +1844,11 @@ ContextRef VerifyContext(ALCcontext *context) } // namespace +FORCE_ALIGN void ALC_APIENTRY alsoft_set_log_callback(LPALSOFTLOGCALLBACK callback, void *userptr) noexcept +{ + al_set_log_callback(callback, userptr); +} + /** Returns a new reference to the currently active context for this thread. */ ContextRef GetContextRef(void) { @@ -2562,58 +1869,86 @@ ContextRef GetContextRef(void) return ContextRef{context}; } +void alcSetError(ALCdevice *device, ALCenum errorCode) +{ + WARN("Error generated on device %p, code 0x%04x\n", voidp{device}, errorCode); + if(TrapALCError) + { +#ifdef _WIN32 + /* DebugBreak() will cause an exception if there is no debugger */ + if(IsDebuggerPresent()) + DebugBreak(); +#elif defined(SIGTRAP) + raise(SIGTRAP); +#endif + } + + if(device) + device->LastError.store(errorCode); + else + LastNullDeviceError.store(errorCode); +} /************************************************ * Standard ALC functions ************************************************/ -ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device) -START_API_FUNC +ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device) noexcept { DeviceRef dev{VerifyDevice(device)}; if(dev) return dev->LastError.exchange(ALC_NO_ERROR); return LastNullDeviceError.exchange(ALC_NO_ERROR); } -END_API_FUNC -ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context) -START_API_FUNC +ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context) noexcept { - if(!SuspendDefers) - return; - ContextRef ctx{VerifyContext(context)}; if(!ctx) + { alcSetError(nullptr, ALC_INVALID_CONTEXT); - else + return; + } + + if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY + ctx->debugMessage(DebugSource::API, DebugType::Portability, 0, DebugSeverity::Medium, + "alcSuspendContext behavior is not portable -- some implementations suspend all " + "rendering, some only defer property changes, and some are completely no-op; consider " + "using alcDevicePauseSOFT to suspend all rendering, or alDeferUpdatesSOFT to only " + "defer property changes"); + + if(SuspendDefers) { std::lock_guard<std::mutex> _{ctx->mPropLock}; ctx->deferUpdates(); } } -END_API_FUNC -ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context) -START_API_FUNC +ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context) noexcept { - if(!SuspendDefers) - return; - ContextRef ctx{VerifyContext(context)}; if(!ctx) + { alcSetError(nullptr, ALC_INVALID_CONTEXT); - else + return; + } + + if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY + ctx->debugMessage(DebugSource::API, DebugType::Portability, 0, DebugSeverity::Medium, + "alcProcessContext behavior is not portable -- some implementations resume rendering, " + "some apply deferred property changes, and some are completely no-op; consider using " + "alcDeviceResumeSOFT to resume rendering, or alProcessUpdatesSOFT to apply deferred " + "property changes"); + + if(SuspendDefers) { std::lock_guard<std::mutex> _{ctx->mPropLock}; ctx->processUpdates(); } } -END_API_FUNC -ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum param) -START_API_FUNC +ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum param) noexcept { const ALCchar *value{nullptr}; @@ -2732,7 +2067,6 @@ START_API_FUNC return value; } -END_API_FUNC static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span<int> values) @@ -3056,8 +2390,7 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span<int> return 0; } -ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) -START_API_FUNC +ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) noexcept { DeviceRef dev{VerifyDevice(device)}; if(size <= 0 || values == nullptr) @@ -3065,10 +2398,8 @@ START_API_FUNC else GetIntegerv(dev.get(), param, {values, static_cast<uint>(size)}); } -END_API_FUNC -ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALCsizei size, ALCint64SOFT *values) -START_API_FUNC +ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALCsizei size, ALCint64SOFT *values) noexcept { DeviceRef dev{VerifyDevice(device)}; if(size <= 0 || values == nullptr) @@ -3078,7 +2409,7 @@ START_API_FUNC } if(!dev || dev->Type == DeviceType::Capture) { - auto ivals = al::vector<int>(static_cast<uint>(size)); + auto ivals = std::vector<int>(static_cast<uint>(size)); if(size_t got{GetIntegerv(dev.get(), pname, ivals)}) std::copy_n(ivals.begin(), got, values); return; @@ -3197,17 +2528,15 @@ START_API_FUNC break; default: - auto ivals = al::vector<int>(static_cast<uint>(size)); + auto ivals = std::vector<int>(static_cast<uint>(size)); if(size_t got{GetIntegerv(dev.get(), pname, ivals)}) std::copy_n(ivals.begin(), got, values); break; } } -END_API_FUNC -ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extName) -START_API_FUNC +ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extName) noexcept { DeviceRef dev{VerifyDevice(device)}; if(!extName) @@ -3231,11 +2560,12 @@ START_API_FUNC } return ALC_FALSE; } -END_API_FUNC -ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcName) -START_API_FUNC +ALCvoid* ALC_APIENTRY alcGetProcAddress2(ALCdevice *device, const ALCchar *funcName) noexcept +{ return alcGetProcAddress(device, funcName); } + +ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcName) noexcept { if(!funcName) { @@ -3243,6 +2573,7 @@ START_API_FUNC alcSetError(dev.get(), ALC_INVALID_VALUE); return nullptr; } + #ifdef ALSOFT_EAX if(eax_g_is_enabled) { @@ -3260,11 +2591,9 @@ START_API_FUNC } return nullptr; } -END_API_FUNC -ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumName) -START_API_FUNC +ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumName) noexcept { if(!enumName) { @@ -3272,6 +2601,7 @@ START_API_FUNC alcSetError(dev.get(), ALC_INVALID_VALUE); return 0; } + #ifdef ALSOFT_EAX if(eax_g_is_enabled) { @@ -3290,11 +2620,9 @@ START_API_FUNC return 0; } -END_API_FUNC -ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrList) -START_API_FUNC +ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrList) noexcept { /* Explicitly hold the list lock while taking the StateLock in case the * device is asynchronously destroyed, to ensure this new context is @@ -3320,7 +2648,34 @@ START_API_FUNC return nullptr; } - ContextRef context{new ALCcontext{dev}}; + ContextFlagBitset ctxflags{0}; + if(attrList) + { + for(size_t i{0};attrList[i];i+=2) + { + if(attrList[i] == ALC_CONTEXT_FLAGS_EXT) + { + ctxflags = static_cast<ALuint>(attrList[i+1]); + break; + } + } + } + + ContextRef context{[](auto&& ...args) -> ContextRef + { + try { + return ContextRef{new ALCcontext{std::forward<decltype(args)>(args)...}}; + } + catch(std::exception& e) { + ERR("Failed to create ALCcontext: %s\n", e.what()); + return ContextRef{}; + } + }(dev, ctxflags)}; + if(!context) + { + alcSetError(dev.get(), ALC_OUT_OF_MEMORY); + return nullptr; + } context->init(); if(auto volopt = dev->configValue<float>(nullptr, "volume-adjust")) @@ -3374,7 +2729,7 @@ START_API_FUNC if(ALeffectslot *slot{context->mDefaultSlot.get()}) { - ALenum sloterr{slot->initEffect(ALCcontext::sDefaultEffect.type, + ALenum sloterr{slot->initEffect(0, ALCcontext::sDefaultEffect.type, ALCcontext::sDefaultEffect.Props, context.get())}; if(sloterr == AL_NO_ERROR) slot->updateProps(context.get()); @@ -3385,10 +2740,8 @@ START_API_FUNC TRACE("Created context %p\n", voidp{context.get()}); return context.release(); } -END_API_FUNC -ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) -START_API_FUNC +ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) noexcept { std::unique_lock<std::recursive_mutex> listlock{ListLock}; auto iter = std::lower_bound(ContextList.begin(), ContextList.end(), context); @@ -3414,26 +2767,20 @@ START_API_FUNC Device->Flags.reset(DeviceRunning); } } -END_API_FUNC -ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) -START_API_FUNC +ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) noexcept { ALCcontext *Context{ALCcontext::getThreadContext()}; if(!Context) Context = ALCcontext::sGlobalContext.load(); return Context; } -END_API_FUNC /** Returns the currently active thread-local context. */ -ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) -START_API_FUNC +ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) noexcept { return ALCcontext::getThreadContext(); } -END_API_FUNC -ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) -START_API_FUNC +ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) noexcept { /* context must be valid or nullptr */ ContextRef ctx; @@ -3467,11 +2814,9 @@ START_API_FUNC return ALC_TRUE; } -END_API_FUNC /** Makes the given context the active context for the current thread. */ -ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context) -START_API_FUNC +ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context) noexcept { /* context must be valid or nullptr */ ContextRef ctx; @@ -3490,11 +2835,9 @@ START_API_FUNC return ALC_TRUE; } -END_API_FUNC -ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *Context) -START_API_FUNC +ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *Context) noexcept { ContextRef ctx{VerifyContext(Context)}; if(!ctx) @@ -3504,11 +2847,9 @@ START_API_FUNC } return ctx->mALDevice.get(); } -END_API_FUNC -ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) -START_API_FUNC +ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) noexcept { InitConfig(); @@ -3565,9 +2906,26 @@ START_API_FUNC device->NumAuxSends = DefaultSends; try { + /* We need to ensure the device name isn't too long. The string_view is + * printed using the "%.*s" formatter, which uses an int for the + * precision/length. It wouldn't be a significant problem if larger + * values simply printed fewer characters due to truncation, but + * negative values are ignored, treating it like a normal null- + * terminated string, and string_views don't need to be null- + * terminated. + * + * Other than the annoyance of checking, this shouldn't be a problem. + * Two billion bytes is enough for a device name. + */ + const std::string_view devname{deviceName ? deviceName : ""}; + if(devname.length() >= std::numeric_limits<int>::max()) + throw al::backend_exception{al::backend_error::NoDevice, + "Device name too long (%zu >= %d)", devname.length(), + std::numeric_limits<int>::max()}; + auto backend = PlaybackFactory->createBackend(device.get(), BackendType::Playback); std::lock_guard<std::recursive_mutex> _{ListLock}; - backend->open(deviceName); + backend->open(devname); device->Backend = std::move(backend); } catch(al::backend_exception &e) { @@ -3586,10 +2944,8 @@ START_API_FUNC TRACE("Created device %p, \"%s\"\n", voidp{device.get()}, device->DeviceName.c_str()); return device.release(); } -END_API_FUNC -ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) -START_API_FUNC +ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) noexcept { std::unique_lock<std::recursive_mutex> listlock{ListLock}; auto iter = std::lower_bound(DeviceList.begin(), DeviceList.end(), device); @@ -3611,7 +2967,7 @@ START_API_FUNC DeviceList.erase(iter); std::unique_lock<std::mutex> statelock{dev->StateLock}; - al::vector<ContextRef> orphanctxs; + std::vector<ContextRef> orphanctxs; for(ContextBase *ctx : *dev->mContexts.load()) { auto ctxiter = std::lower_bound(ContextList.begin(), ContextList.end(), ctx); @@ -3636,14 +2992,12 @@ START_API_FUNC return ALC_TRUE; } -END_API_FUNC /************************************************ * ALC capture functions ************************************************/ -ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei samples) -START_API_FUNC +ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei samples) noexcept { InitConfig(); @@ -3688,14 +3042,20 @@ START_API_FUNC device->UpdateSize = static_cast<uint>(samples); device->BufferSize = static_cast<uint>(samples); + TRACE("Capture format: %s, %s, %uhz, %u / %u buffer\n", DevFmtChannelsString(device->FmtChans), + DevFmtTypeString(device->FmtType), device->Frequency, device->UpdateSize, + device->BufferSize); + try { - TRACE("Capture format: %s, %s, %uhz, %u / %u buffer\n", - DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType), - device->Frequency, device->UpdateSize, device->BufferSize); + const std::string_view devname{deviceName ? deviceName : ""}; + if(devname.length() >= std::numeric_limits<int>::max()) + throw al::backend_exception{al::backend_error::NoDevice, + "Device name too long (%zu >= %d)", devname.length(), + std::numeric_limits<int>::max()}; auto backend = CaptureFactory->createBackend(device.get(), BackendType::Capture); std::lock_guard<std::recursive_mutex> _{ListLock}; - backend->open(deviceName); + backend->open(devname); device->Backend = std::move(backend); } catch(al::backend_exception &e) { @@ -3714,10 +3074,8 @@ START_API_FUNC TRACE("Created capture device %p, \"%s\"\n", voidp{device.get()}, device->DeviceName.c_str()); return device.release(); } -END_API_FUNC -ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) -START_API_FUNC +ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) noexcept { std::unique_lock<std::recursive_mutex> listlock{ListLock}; auto iter = std::lower_bound(DeviceList.begin(), DeviceList.end(), device); @@ -3743,10 +3101,8 @@ START_API_FUNC return ALC_TRUE; } -END_API_FUNC -ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) -START_API_FUNC +ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) noexcept { DeviceRef dev{VerifyDevice(device)}; if(!dev || dev->Type != DeviceType::Capture) @@ -3772,10 +3128,8 @@ START_API_FUNC } } } -END_API_FUNC -ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) -START_API_FUNC +ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) noexcept { DeviceRef dev{VerifyDevice(device)}; if(!dev || dev->Type != DeviceType::Capture) @@ -3788,10 +3142,8 @@ START_API_FUNC dev->Flags.reset(DeviceRunning); } } -END_API_FUNC -ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) -START_API_FUNC +ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) noexcept { DeviceRef dev{VerifyDevice(device)}; if(!dev || dev->Type != DeviceType::Capture) @@ -3818,9 +3170,8 @@ START_API_FUNC return; } - backend->captureSamples(static_cast<al::byte*>(buffer), usamples); + backend->captureSamples(static_cast<std::byte*>(buffer), usamples); } -END_API_FUNC /************************************************ @@ -3828,8 +3179,7 @@ END_API_FUNC ************************************************/ /** Open a loopback device, for manual rendering. */ -ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName) -START_API_FUNC +ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName) noexcept { InitConfig(); @@ -3886,13 +3236,11 @@ START_API_FUNC TRACE("Created loopback device %p\n", voidp{device.get()}); return device.release(); } -END_API_FUNC /** * Determines if the loopback device supports the given format for rendering. */ -ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type) -START_API_FUNC +ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type) noexcept { DeviceRef dev{VerifyDevice(device)}; if(!dev || dev->Type != DeviceType::Loopback) @@ -3908,23 +3256,26 @@ START_API_FUNC return ALC_FALSE; } -END_API_FUNC /** * Renders some samples into a buffer, using the format last set by the * attributes given to alcCreateContext. */ -FORCE_ALIGN ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) -START_API_FUNC +#if defined(__GNUC__) && defined(__i386__) +/* Needed on x86-32 even without SSE codegen, since the mixer may still use SSE + * and GCC assumes the stack is aligned (x86-64 ABI guarantees alignment). + */ +[[gnu::force_align_arg_pointer]] +#endif +ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) noexcept { - if(!device || device->Type != DeviceType::Loopback) + if(!device || device->Type != DeviceType::Loopback) UNLIKELY alcSetError(device, ALC_INVALID_DEVICE); - else if(samples < 0 || (samples > 0 && buffer == nullptr)) + else if(samples < 0 || (samples > 0 && buffer == nullptr)) UNLIKELY alcSetError(device, ALC_INVALID_VALUE); else device->renderSamples(buffer, static_cast<uint>(samples), device->channelsFromFmt()); } -END_API_FUNC /************************************************ @@ -3932,8 +3283,7 @@ END_API_FUNC ************************************************/ /** Pause the DSP to stop audio processing. */ -ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device) -START_API_FUNC +ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device) noexcept { DeviceRef dev{VerifyDevice(device)}; if(!dev || dev->Type != DeviceType::Playback) @@ -3947,11 +3297,9 @@ START_API_FUNC dev->Flags.set(DevicePaused); } } -END_API_FUNC /** Resume the DSP to restart audio processing. */ -ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) -START_API_FUNC +ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) noexcept { DeviceRef dev{VerifyDevice(device)}; if(!dev || dev->Type != DeviceType::Playback) @@ -3982,7 +3330,6 @@ START_API_FUNC DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType), device->Frequency, device->UpdateSize, device->BufferSize); } -END_API_FUNC /************************************************ @@ -3990,8 +3337,7 @@ END_API_FUNC ************************************************/ /** Gets a string parameter at the given index. */ -ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index) -START_API_FUNC +ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index) noexcept { DeviceRef dev{VerifyDevice(device)}; if(!dev || dev->Type == DeviceType::Capture) @@ -4011,11 +3357,9 @@ START_API_FUNC return nullptr; } -END_API_FUNC /** Resets the given device output, using the specified attribute list. */ -ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs) -START_API_FUNC +ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs) noexcept { std::unique_lock<std::recursive_mutex> listlock{ListLock}; DeviceRef dev{VerifyDevice(device)}; @@ -4037,7 +3381,6 @@ START_API_FUNC return ResetDeviceParams(dev.get(), attribs) ? ALC_TRUE : ALC_FALSE; } -END_API_FUNC /************************************************ @@ -4046,8 +3389,7 @@ END_API_FUNC /** Reopens the given device output, using the specified name and attribute list. */ FORCE_ALIGN ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, - const ALCchar *deviceName, const ALCint *attribs) -START_API_FUNC + const ALCchar *deviceName, const ALCint *attribs) noexcept { if(deviceName) { @@ -4075,8 +3417,14 @@ START_API_FUNC BackendPtr newbackend; try { + const std::string_view devname{deviceName ? deviceName : ""}; + if(devname.length() >= std::numeric_limits<int>::max()) + throw al::backend_exception{al::backend_error::NoDevice, + "Device name too long (%zu >= %d)", devname.length(), + std::numeric_limits<int>::max()}; + newbackend = PlaybackFactory->createBackend(dev.get(), BackendType::Playback); - newbackend->open(deviceName); + newbackend->open(devname); } catch(al::backend_exception &e) { listlock.unlock(); @@ -4122,4 +3470,37 @@ START_API_FUNC ResetDeviceParams(dev.get(), attribs); return ALC_TRUE; } -END_API_FUNC + +/************************************************ + * ALC event query functions + ************************************************/ + +FORCE_ALIGN ALCenum ALC_APIENTRY alcEventIsSupportedSOFT(ALCenum eventType, ALCenum deviceType) noexcept +{ + auto etype = alc::GetEventType(eventType); + if(!etype) + { + WARN("Invalid event type: 0x%04x\n", eventType); + alcSetError(nullptr, ALC_INVALID_ENUM); + return ALC_EVENT_NOT_SUPPORTED_SOFT; + } + + auto supported = alc::EventSupport::NoSupport; + switch(deviceType) + { + case ALC_PLAYBACK_DEVICE_SOFT: + if(PlaybackFactory) + supported = PlaybackFactory->queryEventSupport(*etype, BackendType::Playback); + break; + + case ALC_CAPTURE_DEVICE_SOFT: + if(CaptureFactory) + supported = CaptureFactory->queryEventSupport(*etype, BackendType::Capture); + break; + + default: + WARN("Invalid device type: 0x%04x\n", deviceType); + alcSetError(nullptr, ALC_INVALID_ENUM); + } + return al::to_underlying(supported); +} diff --git a/alc/alconfig.cpp b/alc/alconfig.cpp index b0544b89..cfa178fb 100644 --- a/alc/alconfig.cpp +++ b/alc/alconfig.cpp @@ -45,6 +45,13 @@ #include "strutils.h" #include "vector.h" +#if defined(ALSOFT_UWP) +#include <winrt/Windows.Media.Core.h> // !!This is important!! +#include <winrt/Windows.Storage.h> +#include <winrt/Windows.Foundation.h> +#include <winrt/Windows.Foundation.Collections.h> +using namespace winrt; +#endif namespace { @@ -52,7 +59,7 @@ struct ConfigEntry { std::string key; std::string value; }; -al::vector<ConfigEntry> ConfOpts; +std::vector<ConfigEntry> ConfOpts; std::string &lstrip(std::string &line) @@ -258,7 +265,7 @@ void LoadConfigFromFile(std::istream &f) } } - TRACE(" found '%s' = '%s'\n", fullKey.c_str(), value.c_str()); + TRACE(" setting '%s' = '%s'\n", fullKey.c_str(), value.c_str()); /* Check if we already have this option set */ auto find_key = [&fullKey](const ConfigEntry &entry) -> bool @@ -309,17 +316,14 @@ const char *GetConfigValue(const char *devName, const char *blockName, const cha { return entry.key == key; }); if(iter != ConfOpts.cend()) { - TRACE("Found %s = \"%s\"\n", key.c_str(), iter->value.c_str()); + TRACE("Found option %s = \"%s\"\n", key.c_str(), iter->value.c_str()); if(!iter->value.empty()) return iter->value.c_str(); return nullptr; } if(!devName) - { - TRACE("Key %s not found\n", key.c_str()); return nullptr; - } return GetConfigValue(nullptr, blockName, keyName); } @@ -329,9 +333,16 @@ const char *GetConfigValue(const char *devName, const char *blockName, const cha #ifdef _WIN32 void ReadALConfig() { - WCHAR buffer[MAX_PATH]; - if(SHGetSpecialFolderPathW(nullptr, buffer, CSIDL_APPDATA, FALSE) != FALSE) +#if !defined(_GAMING_XBOX) { +#if !defined(ALSOFT_UWP) + WCHAR buffer[MAX_PATH]; + if (!SHGetSpecialFolderPathW(nullptr, buffer, CSIDL_APPDATA, FALSE)) + return; +#else + winrt::Windows::Storage::ApplicationDataContainer localSettings = winrt::Windows::Storage::ApplicationData::Current().LocalSettings(); + auto buffer = Windows::Storage::ApplicationData::Current().RoamingFolder().Path(); +#endif std::string filepath{wstr_to_utf8(buffer)}; filepath += "\\alsoft.ini"; @@ -340,6 +351,8 @@ void ReadALConfig() if(f.is_open()) LoadConfigFromFile(f); } +#endif + std::string ppath{GetProcBinary().path}; if(!ppath.empty()) @@ -483,40 +496,40 @@ void ReadALConfig() } #endif -al::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName) +std::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName) { if(const char *val{GetConfigValue(devName, blockName, keyName)}) return val; - return al::nullopt; + return std::nullopt; } -al::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName) +std::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName) { if(const char *val{GetConfigValue(devName, blockName, keyName)}) return static_cast<int>(std::strtol(val, nullptr, 0)); - return al::nullopt; + return std::nullopt; } -al::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName) +std::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName) { if(const char *val{GetConfigValue(devName, blockName, keyName)}) return static_cast<unsigned int>(std::strtoul(val, nullptr, 0)); - return al::nullopt; + return std::nullopt; } -al::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName) +std::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName) { if(const char *val{GetConfigValue(devName, blockName, keyName)}) return std::strtof(val, nullptr); - return al::nullopt; + return std::nullopt; } -al::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName) +std::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName) { if(const char *val{GetConfigValue(devName, blockName, keyName)}) return al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0 || al::strcasecmp(val, "true")==0 || atoi(val) != 0; - return al::nullopt; + return std::nullopt; } bool GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, bool def) diff --git a/alc/alconfig.h b/alc/alconfig.h index df2830cc..1eb44405 100644 --- a/alc/alconfig.h +++ b/alc/alconfig.h @@ -1,18 +1,18 @@ #ifndef ALCONFIG_H #define ALCONFIG_H +#include <optional> #include <string> -#include "aloptional.h" void ReadALConfig(); bool GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, bool def); -al::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName); -al::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName); -al::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName); -al::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName); -al::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName); +std::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName); +std::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName); +std::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName); +std::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName); +std::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName); #endif /* ALCONFIG_H */ diff --git a/alc/alu.cpp b/alc/alu.cpp index e9ad68b1..e0858b18 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -36,6 +36,7 @@ #include <limits> #include <memory> #include <new> +#include <optional> #include <stdint.h> #include <utility> @@ -76,7 +77,6 @@ #include "opthelpers.h" #include "ringbuffer.h" #include "strutils.h" -#include "threads.h" #include "vecmat.h" #include "vector.h" @@ -135,12 +135,6 @@ float ZScale{1.0f}; float NfcScale{1.0f}; -struct ChanMap { - Channel channel; - float angle; - float elevation; -}; - using HrtfDirectMixerFunc = void(*)(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut, const al::span<const FloatBufferLine> InSamples, float2 *AccumSamples, float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize); @@ -285,8 +279,8 @@ ResamplerFunc PrepareResampler(Resampler resampler, uint increment, InterpState void DeviceBase::ProcessHrtf(const size_t SamplesToDo) { /* HRTF is stereo output only. */ - const uint lidx{RealOut.ChannelIndex[FrontLeft]}; - const uint ridx{RealOut.ChannelIndex[FrontRight]}; + const size_t lidx{RealOut.ChannelIndex[FrontLeft]}; + const size_t ridx{RealOut.ChannelIndex[FrontRight]}; MixDirectHrtf(RealOut.Buffer[lidx], RealOut.Buffer[ridx], Dry.Buffer, HrtfAccumData, mHrtfState->mTemp.data(), mHrtfState->mChannels.data(), mHrtfState->mIrSize, SamplesToDo); @@ -300,9 +294,9 @@ void DeviceBase::ProcessAmbiDec(const size_t SamplesToDo) void DeviceBase::ProcessAmbiDecStablized(const size_t SamplesToDo) { /* Decode with front image stablization. */ - const uint lidx{RealOut.ChannelIndex[FrontLeft]}; - const uint ridx{RealOut.ChannelIndex[FrontRight]}; - const uint cidx{RealOut.ChannelIndex[FrontCenter]}; + const size_t lidx{RealOut.ChannelIndex[FrontLeft]}; + const size_t ridx{RealOut.ChannelIndex[FrontRight]}; + const size_t cidx{RealOut.ChannelIndex[FrontCenter]}; AmbiDecoder->processStablize(RealOut.Buffer, Dry.Buffer.data(), lidx, ridx, cidx, SamplesToDo); @@ -311,8 +305,8 @@ void DeviceBase::ProcessAmbiDecStablized(const size_t SamplesToDo) void DeviceBase::ProcessUhj(const size_t SamplesToDo) { /* UHJ is stereo output only. */ - const uint lidx{RealOut.ChannelIndex[FrontLeft]}; - const uint ridx{RealOut.ChannelIndex[FrontRight]}; + const size_t lidx{RealOut.ChannelIndex[FrontLeft]}; + const size_t ridx{RealOut.ChannelIndex[FrontRight]}; /* Encode to stereo-compatible 2-channel UHJ output. */ mUhjEncoder->encode(RealOut.Buffer[lidx].data(), RealOut.Buffer[ridx].data(), @@ -325,8 +319,8 @@ void DeviceBase::ProcessBs2b(const size_t SamplesToDo) AmbiDecoder->process(RealOut.Buffer, Dry.Buffer.data(), SamplesToDo); /* BS2B is stereo output only. */ - const uint lidx{RealOut.ChannelIndex[FrontLeft]}; - const uint ridx{RealOut.ChannelIndex[FrontRight]}; + const size_t lidx{RealOut.ChannelIndex[FrontLeft]}; + const size_t ridx{RealOut.ChannelIndex[FrontRight]}; /* Now apply the BS2B binaural/crossfeed filter. */ bs2b_cross_feed(Bs2b.get(), RealOut.Buffer[lidx].data(), RealOut.Buffer[ridx].data(), @@ -376,28 +370,28 @@ void UpsampleBFormatTransform( } -inline auto& GetAmbiScales(AmbiScaling scaletype) noexcept +constexpr auto GetAmbiScales(AmbiScaling scaletype) noexcept { switch(scaletype) { - case AmbiScaling::FuMa: return AmbiScale::FromFuMa(); - case AmbiScaling::SN3D: return AmbiScale::FromSN3D(); - case AmbiScaling::UHJ: return AmbiScale::FromUHJ(); + case AmbiScaling::FuMa: return al::span{AmbiScale::FromFuMa}; + case AmbiScaling::SN3D: return al::span{AmbiScale::FromSN3D}; + case AmbiScaling::UHJ: return al::span{AmbiScale::FromUHJ}; case AmbiScaling::N3D: break; } - return AmbiScale::FromN3D(); + return al::span{AmbiScale::FromN3D}; } -inline auto& GetAmbiLayout(AmbiLayout layouttype) noexcept +constexpr auto GetAmbiLayout(AmbiLayout layouttype) noexcept { - if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa(); - return AmbiIndex::FromACN(); + if(layouttype == AmbiLayout::FuMa) return al::span{AmbiIndex::FromFuMa}; + return al::span{AmbiIndex::FromACN}; } -inline auto& GetAmbi2DLayout(AmbiLayout layouttype) noexcept +constexpr auto GetAmbi2DLayout(AmbiLayout layouttype) noexcept { - if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa2D(); - return AmbiIndex::FromACN2D(); + if(layouttype == AmbiLayout::FuMa) return al::span{AmbiIndex::FromFuMa2D}; + return al::span{AmbiIndex::FromACN2D}; } @@ -490,9 +484,8 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBa auto evt_vec = ring->getWriteVector(); if(evt_vec.first.len > 0) LIKELY { - AsyncEvent *evt{al::construct_at(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf), - AsyncEvent::ReleaseEffectState)}; - evt->u.mEffectState = oldstate; + auto &evt = InitAsyncEvent<AsyncEffectReleaseEvent>(evt_vec.first.buf); + evt.mEffectState = oldstate; ring->writeAdvance(1); } else @@ -521,27 +514,76 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBa } -/* Scales the given azimuth toward the side (+/- pi/2 radians) for positions in - * front. +/* Scales the azimuth of the given vector by 3 if it's in front. Effectively + * scales +/-30 degrees to +/-90 degrees, leaving > +90 and < -90 alone. */ -inline float ScaleAzimuthFront(float azimuth, float scale) +inline std::array<float,3> ScaleAzimuthFront3(std::array<float,3> pos) { - const float abs_azi{std::fabs(azimuth)}; - if(!(abs_azi >= al::numbers::pi_v<float>*0.5f)) - return std::copysign(minf(abs_azi*scale, al::numbers::pi_v<float>*0.5f), azimuth); - return azimuth; + if(pos[2] < 0.0f) + { + /* Normalize the length of the x,z components for a 2D vector of the + * azimuth angle. Negate Z since {0,0,-1} is angle 0. + */ + const float len2d{std::sqrt(pos[0]*pos[0] + pos[2]*pos[2])}; + float x{pos[0] / len2d}; + float z{-pos[2] / len2d}; + + /* Z > cos(pi/6) = -30 < azimuth < 30 degrees. */ + if(z > 0.866025403785f) + { + /* Triple the angle represented by x,z. */ + x = x*3.0f - x*x*x*4.0f; + z = z*z*z*4.0f - z*3.0f; + + /* Scale the vector back to fit in 3D. */ + pos[0] = x * len2d; + pos[2] = -z * len2d; + } + else + { + /* If azimuth >= 30 degrees, clamp to 90 degrees. */ + pos[0] = std::copysign(len2d, pos[0]); + pos[2] = 0.0f; + } + } + return pos; } -/* Wraps the given value in radians to stay between [-pi,+pi] */ -inline float WrapRadians(float r) +/* Scales the azimuth of the given vector by 1.5 (3/2) if it's in front. */ +inline std::array<float,3> ScaleAzimuthFront3_2(std::array<float,3> pos) { - static constexpr float Pi{al::numbers::pi_v<float>}; - static constexpr float Pi2{Pi*2.0f}; - if(r > Pi) return std::fmod(Pi+r, Pi2) - Pi; - if(r < -Pi) return Pi - std::fmod(Pi-r, Pi2); - return r; + if(pos[2] < 0.0f) + { + const float len2d{std::sqrt(pos[0]*pos[0] + pos[2]*pos[2])}; + float x{pos[0] / len2d}; + float z{-pos[2] / len2d}; + + /* Z > cos(pi/3) = -60 < azimuth < 60 degrees. */ + if(z > 0.5f) + { + /* Halve the angle represented by x,z. */ + x = std::copysign(std::sqrt((1.0f - z) * 0.5f), x); + z = std::sqrt((1.0f + z) * 0.5f); + + /* Triple the angle represented by x,z. */ + x = x*3.0f - x*x*x*4.0f; + z = z*z*z*4.0f - z*3.0f; + + /* Scale the vector back to fit in 3D. */ + pos[0] = x * len2d; + pos[2] = -z * len2d; + } + else + { + /* If azimuth >= 60 degrees, clamp to 90 degrees. */ + pos[0] = std::copysign(len2d, pos[0]); + pos[2] = 0.0f; + } + } + return pos; } + /* Begin ambisonic rotation helpers. * * Rotating first-order B-Format just needs a straight-forward X/Y/Z rotation @@ -561,19 +603,18 @@ inline float WrapRadians(float r) * precomputed since they're constant. The second-order coefficients are * followed by the third-order coefficients, etc. */ -template<size_t L> -constexpr size_t CalcRotatorSize() -{ return (L*2 + 1)*(L*2 + 1) + CalcRotatorSize<L-1>(); } - -template<> constexpr size_t CalcRotatorSize<0>() = delete; -template<> constexpr size_t CalcRotatorSize<1>() = delete; -template<> constexpr size_t CalcRotatorSize<2>() { return 5*5; } +constexpr size_t CalcRotatorSize(size_t l) noexcept +{ + if(l >= 2) + return (l*2 + 1)*(l*2 + 1) + CalcRotatorSize(l-1); + return 0; +} struct RotatorCoeffs { struct CoeffValues { float u, v, w; }; - std::array<CoeffValues,CalcRotatorSize<MaxAmbiOrder>()> mCoeffs{}; + std::array<CoeffValues,CalcRotatorSize(MaxAmbiOrder)> mCoeffs{}; RotatorCoeffs() { @@ -585,17 +626,38 @@ struct RotatorCoeffs { { for(int m{-l};m <= l;++m) { - // compute u,v,w terms of Eq.8.1 (Table I) - const bool d{m == 0}; // the delta function d_m0 - const float denom{static_cast<float>((std::abs(n) == l) ? - (2*l) * (2*l - 1) : (l*l - n*n))}; - - const int abs_m{std::abs(m)}; - coeffs->u = std::sqrt(static_cast<float>(l*l - m*m)/denom); - coeffs->v = std::sqrt(static_cast<float>(l+abs_m-1) * - static_cast<float>(l+abs_m) / denom) * (1.0f+d) * (1.0f - 2.0f*d) * 0.5f; - coeffs->w = std::sqrt(static_cast<float>(l-abs_m-1) * - static_cast<float>(l-abs_m) / denom) * (1.0f-d) * -0.5f; + /* compute u,v,w terms of Eq.8.1 (Table I) + * + * const bool d{m == 0}; // the delta function d_m0 + * const double denom{(std::abs(n) == l) ? + * (2*l) * (2*l - 1) : (l*l - n*n)}; + * + * const int abs_m{std::abs(m)}; + * coeffs->u = std::sqrt((l*l - m*m) / denom); + * coeffs->v = std::sqrt((l+abs_m-1) * (l+abs_m) / denom) * + * (1.0+d) * (1.0 - 2.0*d) * 0.5; + * coeffs->w = std::sqrt((l-abs_m-1) * (l-abs_m) / denom) * + * (1.0-d) * -0.5; + */ + + const double denom{static_cast<double>((std::abs(n) == l) ? + (2*l) * (2*l - 1) : (l*l - n*n))}; + + if(m == 0) + { + coeffs->u = static_cast<float>(std::sqrt(l * l / denom)); + coeffs->v = static_cast<float>(std::sqrt((l-1) * l / denom) * -1.0); + coeffs->w = 0.0f; + } + else + { + const int abs_m{std::abs(m)}; + coeffs->u = static_cast<float>(std::sqrt((l*l - m*m) / denom)); + coeffs->v = static_cast<float>(std::sqrt((l+abs_m-1) * (l+abs_m) / denom) * + 0.5); + coeffs->w = static_cast<float>(std::sqrt((l-abs_m-1) * (l-abs_m) / denom) * + -0.5); + } ++coeffs; } } @@ -679,26 +741,36 @@ void AmbiRotator(AmbiRotateMatrix &matrix, const int order) float r{0.0f}; // computes Eq.8.1 - const float u{coeffs->u}; - if(u != 0.0f) r += u * U(l, m, n, last_band, matrix); - const float v{coeffs->v}; - if(v != 0.0f) r += v * V(l, m, n, last_band, matrix); - const float w{coeffs->w}; - if(w != 0.0f) r += w * W(l, m, n, last_band, matrix); + if(const float u{coeffs->u}; u != 0.0f) + r += u * U(l, m, n, last_band, matrix); + if(const float v{coeffs->v}; v != 0.0f) + r += v * V(l, m, n, last_band, matrix); + if(const float w{coeffs->w}; w != 0.0f) + r += w * W(l, m, n, last_band, matrix); matrix[y][x] = r; ++coeffs; } } last_band = band_idx; - band_idx += static_cast<uint>(l)*size_t{2} + 1; + band_idx += static_cast<uint>(l)*2_uz + 1; } } /* End ambisonic rotation helpers. */ -constexpr float Deg2Rad(float x) noexcept -{ return static_cast<float>(al::numbers::pi / 180.0 * x); } +constexpr float sin30{0.5f}; +constexpr float cos30{0.866025403785f}; +constexpr float sin45{al::numbers::sqrt2_v<float>*0.5f}; +constexpr float cos45{al::numbers::sqrt2_v<float>*0.5f}; +constexpr float sin110{ 0.939692620786f}; +constexpr float cos110{-0.342020143326f}; + +struct ChanPosMap { + Channel channel; + std::array<float,3> pos; +}; + struct GainTriplet { float Base, HF, LF; }; @@ -707,45 +779,45 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con const al::span<const GainTriplet,MAX_SENDS> WetGain, EffectSlot *(&SendSlots)[MAX_SENDS], const VoiceProps *props, const ContextParams &Context, DeviceBase *Device) { - static constexpr ChanMap MonoMap[1]{ - { FrontCenter, 0.0f, 0.0f } + static constexpr ChanPosMap MonoMap[1]{ + { FrontCenter, std::array{0.0f, 0.0f, -1.0f} } }, RearMap[2]{ - { BackLeft, Deg2Rad(-150.0f), Deg2Rad(0.0f) }, - { BackRight, Deg2Rad( 150.0f), Deg2Rad(0.0f) } + { BackLeft, std::array{-sin30, 0.0f, cos30} }, + { BackRight, std::array{ sin30, 0.0f, cos30} }, }, 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) } + { FrontLeft, std::array{-sin45, 0.0f, -cos45} }, + { FrontRight, std::array{ sin45, 0.0f, -cos45} }, + { BackLeft, std::array{-sin45, 0.0f, cos45} }, + { BackRight, std::array{ sin45, 0.0f, cos45} }, }, 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) } + { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, + { FrontRight, std::array{ sin30, 0.0f, -cos30} }, + { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, + { LFE, {} }, + { SideLeft, std::array{-sin110, 0.0f, -cos110} }, + { SideRight, std::array{ sin110, 0.0f, -cos110} }, }, 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) } + { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, + { FrontRight, std::array{ sin30, 0.0f, -cos30} }, + { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, + { LFE, {} }, + { BackCenter, std::array{ 0.0f, 0.0f, 1.0f} }, + { SideLeft, std::array{-1.0f, 0.0f, 0.0f} }, + { SideRight, std::array{ 1.0f, 0.0f, 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) } + { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, + { FrontRight, std::array{ sin30, 0.0f, -cos30} }, + { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, + { LFE, {} }, + { BackLeft, std::array{-sin30, 0.0f, cos30} }, + { BackRight, std::array{ sin30, 0.0f, cos30} }, + { SideLeft, std::array{ -1.0f, 0.0f, 0.0f} }, + { SideRight, std::array{ 1.0f, 0.0f, 0.0f} }, }; - ChanMap StereoMap[2]{ - { FrontLeft, Deg2Rad(-30.0f), Deg2Rad(0.0f) }, - { FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) } + ChanPosMap StereoMap[2]{ + { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, + { FrontRight, std::array{ sin30, 0.0f, -cos30} }, }; const auto Frequency = static_cast<float>(Device->Frequency); @@ -763,7 +835,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con } DirectMode DirectChannels{props->DirectChannels}; - const ChanMap *chans{nullptr}; + const ChanPosMap *chans{nullptr}; switch(voice->mFmtChannels) { case FmtMono: @@ -775,11 +847,13 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con case FmtStereo: if(DirectChannels == DirectMode::Off) { - /* Convert counter-clockwise to clock-wise, and wrap between - * [-pi,+pi]. - */ - StereoMap[0].angle = WrapRadians(-props->StereoPan[0]); - StereoMap[1].angle = WrapRadians(-props->StereoPan[1]); + for(size_t i{0};i < 2;++i) + { + /* StereoPan is counter-clockwise in radians. */ + const float a{props->StereoPan[i]}; + StereoMap[i].pos[0] = -std::sin(a); + StereoMap[i].pos[2] = -std::cos(a); + } } chans = StereoMap; break; @@ -845,32 +919,21 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con auto calc_coeffs = [xpos,ypos,zpos](RenderMode mode) { if(mode != RenderMode::Pairwise) - return CalcDirectionCoeffs({xpos, ypos, zpos}); - - /* Clamp Y, in case rounding errors caused it to end up outside - * of -1...+1. - */ - const float ev{std::asin(clampf(ypos, -1.0f, 1.0f))}; - /* Negate Z for right-handed coords with -Z in front. */ - const float az{std::atan2(xpos, -zpos)}; - - /* A scalar of 1.5 for plain stereo results in +/-60 degrees - * being moved to +/-90 degrees for direct right and left - * speaker responses. - */ - return CalcAngleCoeffs(ScaleAzimuthFront(az, 1.5f), ev, 0.0f); + return CalcDirectionCoeffs(std::array{xpos, ypos, zpos}, 0.0f); + const auto pos = ScaleAzimuthFront3_2(std::array{xpos, ypos, zpos}); + return CalcDirectionCoeffs(pos, 0.0f); }; - auto&& scales = GetAmbiScales(voice->mAmbiScaling); + const auto scales = GetAmbiScales(voice->mAmbiScaling); auto coeffs = calc_coeffs(Device->mRenderMode); if(!(coverage > 0.0f)) { - ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base*scales[0], + ComputePanGains(&Device->Dry, coeffs, DryGain.Base*scales[0], voice->mChans[0].mDryParams.Gains.Target); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base*scales[0], + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base*scales[0], voice->mChans[0].mWetParams[i].Gains.Target); } } @@ -922,25 +985,25 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con { if(voice->mAmbiOrder == 1) { - auto&& upsampler = Is2DAmbisonic(voice->mFmtChannels) ? - AmbiScale::FirstOrder2DUp : AmbiScale::FirstOrderUp; + const auto upsampler = Is2DAmbisonic(voice->mFmtChannels) ? + al::span{AmbiScale::FirstOrder2DUp} : al::span{AmbiScale::FirstOrderUp}; UpsampleBFormatTransform(mixmatrix, upsampler, shrot, Device->mAmbiOrder); } else if(voice->mAmbiOrder == 2) { - auto&& upsampler = Is2DAmbisonic(voice->mFmtChannels) ? - AmbiScale::SecondOrder2DUp : AmbiScale::SecondOrderUp; + const auto upsampler = Is2DAmbisonic(voice->mFmtChannels) ? + al::span{AmbiScale::SecondOrder2DUp} : al::span{AmbiScale::SecondOrderUp}; UpsampleBFormatTransform(mixmatrix, upsampler, shrot, Device->mAmbiOrder); } else if(voice->mAmbiOrder == 3) { - auto&& upsampler = Is2DAmbisonic(voice->mFmtChannels) ? - AmbiScale::ThirdOrder2DUp : AmbiScale::ThirdOrderUp; + const auto upsampler = Is2DAmbisonic(voice->mFmtChannels) ? + al::span{AmbiScale::ThirdOrder2DUp} : al::span{AmbiScale::ThirdOrderUp}; UpsampleBFormatTransform(mixmatrix, upsampler, shrot, Device->mAmbiOrder); } else if(voice->mAmbiOrder == 4) { - auto&& upsampler = AmbiScale::FourthOrder2DUp; + const auto upsampler = al::span{AmbiScale::FourthOrder2DUp}; UpsampleBFormatTransform(mixmatrix, upsampler, shrot, Device->mAmbiOrder); } else @@ -974,13 +1037,13 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con for(size_t x{0};x < MaxAmbiChannels;++x) coeffs[x] += mixmatrix[acn][x] * scale; - ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base, + ComputePanGains(&Device->Dry, coeffs, DryGain.Base, voice->mChans[c].mDryParams.Gains.Target); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base, voice->mChans[c].mWetParams[i].Gains.Target); } @@ -1028,12 +1091,12 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con if(chans[c].channel == LFE) continue; - const auto coeffs = CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f); + const auto coeffs = CalcDirectionCoeffs(chans[c].pos, 0.0f); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base, voice->mChans[c].mWetParams[i].Gains.Target); } } @@ -1047,21 +1110,21 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con if(Distance > std::numeric_limits<float>::epsilon()) { - const float src_ev{std::asin(clampf(ypos, -1.0f, 1.0f))}; - const float src_az{std::atan2(xpos, -zpos)}; - if(voice->mFmtChannels == FmtMono) { + const float src_ev{std::asin(clampf(ypos, -1.0f, 1.0f))}; + const float src_az{std::atan2(xpos, -zpos)}; + Device->mHrtf->getCoeffs(src_ev, src_az, Distance*NfcScale, Spread, voice->mChans[0].mDryParams.Hrtf.Target.Coeffs, voice->mChans[0].mDryParams.Hrtf.Target.Delay); voice->mChans[0].mDryParams.Hrtf.Target.Gain = DryGain.Base; - const auto coeffs = CalcAngleCoeffs(src_az, src_ev, Spread); + const auto coeffs = CalcDirectionCoeffs(std::array{xpos, ypos, zpos}, Spread); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base, voice->mChans[0].mWetParams[i].Gains.Target); } } @@ -1077,28 +1140,32 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con * the source position, at full spread (pi*2), each channel is * left unchanged. */ - const float ev{lerpf(src_ev, chans[c].elevation, inv_pi_v<float>/2.0f * Spread)}; - - float az{chans[c].angle - src_az}; - if(az < -pi_v<float>) az += pi_v<float>*2.0f; - else if(az > pi_v<float>) az -= pi_v<float>*2.0f; - - az *= inv_pi_v<float>/2.0f * Spread; + const float a{1.0f - (inv_pi_v<float>/2.0f)*Spread}; + std::array pos{ + lerpf(chans[c].pos[0], xpos, a), + lerpf(chans[c].pos[1], ypos, a), + lerpf(chans[c].pos[2], zpos, a)}; + const float len{std::sqrt(pos[0]*pos[0] + pos[1]*pos[1] + pos[2]*pos[2])}; + if(len < 1.0f) + { + pos[0] /= len; + pos[1] /= len; + pos[2] /= len; + } - az += src_az; - if(az < -pi_v<float>) az += pi_v<float>*2.0f; - else if(az > pi_v<float>) az -= pi_v<float>*2.0f; + const float ev{std::asin(clampf(pos[1], -1.0f, 1.0f))}; + const float az{std::atan2(pos[0], -pos[2])}; Device->mHrtf->getCoeffs(ev, az, Distance*NfcScale, 0.0f, voice->mChans[c].mDryParams.Hrtf.Target.Coeffs, voice->mChans[c].mDryParams.Hrtf.Target.Delay); voice->mChans[c].mDryParams.Hrtf.Target.Gain = DryGain.Base; - const auto coeffs = CalcAngleCoeffs(az, ev, 0.0f); + const auto coeffs = CalcDirectionCoeffs(pos, 0.0f); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base, voice->mChans[c].mWetParams[i].Gains.Target); } } @@ -1124,19 +1191,21 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con /* Get the HRIR coefficients and delays for this channel * position. */ - Device->mHrtf->getCoeffs(chans[c].elevation, chans[c].angle, - std::numeric_limits<float>::infinity(), spread, + const float ev{std::asin(chans[c].pos[1])}; + const float az{std::atan2(chans[c].pos[0], -chans[c].pos[2])}; + + Device->mHrtf->getCoeffs(ev, az, std::numeric_limits<float>::infinity(), spread, voice->mChans[c].mDryParams.Hrtf.Target.Coeffs, voice->mChans[c].mDryParams.Hrtf.Target.Delay); voice->mChans[c].mDryParams.Hrtf.Target.Gain = DryGain.Base; /* Normal panning for auxiliary sends. */ - const auto coeffs = CalcAngleCoeffs(chans[c].angle, chans[c].elevation, spread); + const auto coeffs = CalcDirectionCoeffs(chans[c].pos, spread); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base, voice->mChans[c].mWetParams[i].Gains.Target); } } @@ -1171,19 +1240,18 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con auto calc_coeffs = [xpos,ypos,zpos,Spread](RenderMode mode) { if(mode != RenderMode::Pairwise) - return CalcDirectionCoeffs({xpos, ypos, zpos}, Spread); - const float ev{std::asin(clampf(ypos, -1.0f, 1.0f))}; - const float az{std::atan2(xpos, -zpos)}; - return CalcAngleCoeffs(ScaleAzimuthFront(az, 1.5f), ev, Spread); + return CalcDirectionCoeffs(std::array{xpos, ypos, zpos}, Spread); + const auto pos = ScaleAzimuthFront3_2(std::array{xpos, ypos, zpos}); + return CalcDirectionCoeffs(pos, Spread); }; const auto coeffs = calc_coeffs(Device->mRenderMode); - ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base, + ComputePanGains(&Device->Dry, coeffs, DryGain.Base, voice->mChans[0].mDryParams.Gains.Target); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base, voice->mChans[0].mWetParams[i].Gains.Target); } } @@ -1191,9 +1259,6 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con { using namespace al::numbers; - const float src_ev{std::asin(clampf(ypos, -1.0f, 1.0f))}; - const float src_az{std::atan2(xpos, -zpos)}; - for(size_t c{0};c < num_channels;c++) { /* Special-case LFE */ @@ -1213,29 +1278,29 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con * at the source position, at full spread (pi*2), each * channel position is left unchanged. */ - const float ev{lerpf(src_ev, chans[c].elevation, - inv_pi_v<float>/2.0f * Spread)}; - - float az{chans[c].angle - src_az}; - if(az < -pi_v<float>) az += pi_v<float>*2.0f; - else if(az > pi_v<float>) az -= pi_v<float>*2.0f; - - az *= inv_pi_v<float>/2.0f * Spread; - - az += src_az; - if(az < -pi_v<float>) az += pi_v<float>*2.0f; - else if(az > pi_v<float>) az -= pi_v<float>*2.0f; + const float a{1.0f - (inv_pi_v<float>/2.0f)*Spread}; + std::array pos{ + lerpf(chans[c].pos[0], xpos, a), + lerpf(chans[c].pos[1], ypos, a), + lerpf(chans[c].pos[2], zpos, a)}; + const float len{std::sqrt(pos[0]*pos[0] + pos[1]*pos[1] + pos[2]*pos[2])}; + if(len < 1.0f) + { + pos[0] /= len; + pos[1] /= len; + pos[2] /= len; + } if(Device->mRenderMode == RenderMode::Pairwise) - az = ScaleAzimuthFront(az, 3.0f); - const auto coeffs = CalcAngleCoeffs(az, ev, 0.0f); + pos = ScaleAzimuthFront3(pos); + const auto coeffs = CalcDirectionCoeffs(pos, 0.0f); - ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base, + ComputePanGains(&Device->Dry, coeffs, DryGain.Base, voice->mChans[c].mDryParams.Gains.Target); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base, voice->mChans[c].mWetParams[i].Gains.Target); } } @@ -1274,16 +1339,15 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con continue; } - const auto coeffs = CalcAngleCoeffs((Device->mRenderMode == RenderMode::Pairwise) - ? ScaleAzimuthFront(chans[c].angle, 3.0f) : chans[c].angle, - chans[c].elevation, spread); + const auto coeffs = CalcDirectionCoeffs((Device->mRenderMode==RenderMode::Pairwise) + ? ScaleAzimuthFront3(chans[c].pos) : chans[c].pos, spread); - ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base, + ComputePanGains(&Device->Dry, coeffs, DryGain.Base, voice->mChans[c].mDryParams.Gains.Target); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base, voice->mChans[c].mWetParams[i].Gains.Target); } } @@ -1700,22 +1764,21 @@ void SendSourceStateEvent(ContextBase *context, uint id, VChangeState state) auto evt_vec = ring->getWriteVector(); if(evt_vec.first.len < 1) return; - AsyncEvent *evt{al::construct_at(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf), - AsyncEvent::SourceStateChange)}; - evt->u.srcstate.id = id; + auto &evt = InitAsyncEvent<AsyncSourceStateEvent>(evt_vec.first.buf); + evt.mId = id; switch(state) { case VChangeState::Reset: - evt->u.srcstate.state = AsyncEvent::SrcState::Reset; + evt.mState = AsyncSrcState::Reset; break; case VChangeState::Stop: - evt->u.srcstate.state = AsyncEvent::SrcState::Stop; + evt.mState = AsyncSrcState::Stop; break; case VChangeState::Play: - evt->u.srcstate.state = AsyncEvent::SrcState::Play; + evt.mState = AsyncSrcState::Play; break; case VChangeState::Pause: - evt->u.srcstate.state = AsyncEvent::SrcState::Pause; + evt.mState = AsyncSrcState::Pause; break; /* Shouldn't happen. */ case VChangeState::Restart: @@ -1812,7 +1875,7 @@ void ProcessVoiceChanges(ContextBase *ctx) } oldvoice->mPendingChange.store(false, std::memory_order_release); } - if(sendevt && enabledevt.test(AsyncEvent::SourceStateChange)) + if(sendevt && enabledevt.test(al::to_underlying(AsyncEnableBits::SourceState))) SendSourceStateEvent(ctx, cur->mSourceID, cur->mState); next = cur->mNext.load(std::memory_order_acquire); @@ -1855,7 +1918,7 @@ void ProcessContexts(DeviceBase *device, const uint SamplesToDo) const EffectSlotArray &auxslots = *ctx->mActiveAuxSlots.load(std::memory_order_acquire); const al::span<Voice*> voices{ctx->getVoicesSpanAcquired()}; - /* Process pending propery updates for objects on the context. */ + /* Process pending property updates for objects on the context. */ ProcessParamUpdates(ctx, auxslots, voices); /* Clear auxiliary effect slot mixing buffers. */ @@ -2165,28 +2228,26 @@ void DeviceBase::handleDisconnect(const char *msg, ...) IncrementRef(MixCount); if(Connected.exchange(false, std::memory_order_acq_rel)) { - AsyncEvent evt{AsyncEvent::Disconnected}; + AsyncEvent evt{std::in_place_type<AsyncDisconnectEvent>}; + auto &disconnect = std::get<AsyncDisconnectEvent>(evt); va_list args; va_start(args, msg); - int msglen{vsnprintf(evt.u.disconnect.msg, sizeof(evt.u.disconnect.msg), msg, args)}; + int msglen{vsnprintf(disconnect.msg, sizeof(disconnect.msg), msg, args)}; va_end(args); - if(msglen < 0 || static_cast<size_t>(msglen) >= sizeof(evt.u.disconnect.msg)) - evt.u.disconnect.msg[sizeof(evt.u.disconnect.msg)-1] = 0; + if(msglen < 0 || static_cast<size_t>(msglen) >= sizeof(disconnect.msg)) + disconnect.msg[sizeof(disconnect.msg)-1] = 0; for(ContextBase *ctx : *mContexts.load()) { - if(ctx->mEnabledEvts.load(std::memory_order_acquire).test(AsyncEvent::Disconnected)) + RingBuffer *ring{ctx->mAsyncEvents.get()}; + auto evt_data = ring->getWriteVector().first; + if(evt_data.len > 0) { - RingBuffer *ring{ctx->mAsyncEvents.get()}; - auto evt_data = ring->getWriteVector().first; - if(evt_data.len > 0) - { - al::construct_at(reinterpret_cast<AsyncEvent*>(evt_data.buf), evt); - ring->writeAdvance(1); - ctx->mEventSem.post(); - } + al::construct_at(reinterpret_cast<AsyncEvent*>(evt_data.buf), evt); + ring->writeAdvance(1); + ctx->mEventSem.post(); } if(!ctx->mStopVoicesOnDisconnect) @@ -2,14 +2,14 @@ #define ALU_H #include <bitset> - -#include "aloptional.h" +#include <optional> +#include <stdint.h> struct ALCcontext; struct ALCdevice; struct EffectSlot; -enum class StereoEncoding : unsigned char; +enum class StereoEncoding : uint8_t; constexpr float GainMixMax{1000.0f}; /* +60dB */ @@ -31,7 +31,7 @@ void aluInit(CompatFlagBitset flags, const float nfcscale); * Set up the appropriate panning method and mixing method given the device * properties. */ -void aluInitRenderer(ALCdevice *device, int hrtf_id, al::optional<StereoEncoding> stereomode); +void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncoding> stereomode); void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context); diff --git a/alc/backends/alsa.cpp b/alc/backends/alsa.cpp index d620a83c..0d9ff30d 100644 --- a/alc/backends/alsa.cpp +++ b/alc/backends/alsa.cpp @@ -31,22 +31,22 @@ #include <exception> #include <functional> #include <memory> +#include <mutex> #include <string> #include <thread> #include <utility> +#include <vector> -#include "albyte.h" +#include "albit.h" #include "alc/alconfig.h" #include "almalloc.h" #include "alnumeric.h" -#include "aloptional.h" +#include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" #include "core/logging.h" #include "dynload.h" #include "ringbuffer.h" -#include "threads.h" -#include "vector.h" #include <alsa/asoundlib.h> @@ -248,8 +248,8 @@ struct DevMap { { } }; -al::vector<DevMap> PlaybackDevices; -al::vector<DevMap> CaptureDevices; +std::vector<DevMap> PlaybackDevices; +std::vector<DevMap> CaptureDevices; const char *prefix_name(snd_pcm_stream_t stream) @@ -258,9 +258,9 @@ const char *prefix_name(snd_pcm_stream_t stream) return (stream==SND_PCM_STREAM_PLAYBACK) ? "device-prefix" : "capture-prefix"; } -al::vector<DevMap> probe_devices(snd_pcm_stream_t stream) +std::vector<DevMap> probe_devices(snd_pcm_stream_t stream) { - al::vector<DevMap> devlist; + std::vector<DevMap> devlist; snd_ctl_card_info_t *info; snd_ctl_card_info_malloc(&info); @@ -427,7 +427,7 @@ struct AlsaPlayback final : public BackendBase { int mixerProc(); int mixerNoMMapProc(); - void open(const char *name) override; + void open(std::string_view name) override; bool reset() override; void start() override; void stop() override; @@ -439,7 +439,7 @@ struct AlsaPlayback final : public BackendBase { std::mutex mMutex; uint mFrameStep{}; - al::vector<al::byte> mBuffer; + std::vector<std::byte> mBuffer; std::atomic<bool> mKillNow{true}; std::thread mThread; @@ -585,7 +585,7 @@ int AlsaPlayback::mixerNoMMapProc() continue; } - al::byte *WritePtr{mBuffer.data()}; + std::byte *WritePtr{mBuffer.data()}; avail = snd_pcm_bytes_to_frames(mPcmHandle, static_cast<ssize_t>(mBuffer.size())); std::lock_guard<std::mutex> _{mMutex}; mDevice->renderSamples(WritePtr, static_cast<uint>(avail), mFrameStep); @@ -626,10 +626,10 @@ int AlsaPlayback::mixerNoMMapProc() } -void AlsaPlayback::open(const char *name) +void AlsaPlayback::open(std::string_view name) { std::string driver{"default"}; - if(name) + if(!name.empty()) { if(PlaybackDevices.empty()) PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK); @@ -638,7 +638,7 @@ void AlsaPlayback::open(const char *name) [name](const DevMap &entry) -> bool { return entry.name == name; }); if(iter == PlaybackDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%s\" not found", name}; + "Device name \"%.*s\" not found", static_cast<int>(name.length()), name.data()}; driver = iter->device_name; } else @@ -871,16 +871,16 @@ struct AlsaCapture final : public BackendBase { AlsaCapture(DeviceBase *device) noexcept : BackendBase{device} { } ~AlsaCapture() override; - void open(const char *name) override; + void open(std::string_view name) override; void start() override; void stop() override; - void captureSamples(al::byte *buffer, uint samples) override; + void captureSamples(std::byte *buffer, uint samples) override; uint availableSamples() override; ClockLatency getClockLatency() override; snd_pcm_t *mPcmHandle{nullptr}; - al::vector<al::byte> mBuffer; + std::vector<std::byte> mBuffer; bool mDoCapture{false}; RingBufferPtr mRing{nullptr}; @@ -898,10 +898,10 @@ AlsaCapture::~AlsaCapture() } -void AlsaCapture::open(const char *name) +void AlsaCapture::open(std::string_view name) { std::string driver{"default"}; - if(name) + if(!name.empty()) { if(CaptureDevices.empty()) CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE); @@ -910,7 +910,7 @@ void AlsaCapture::open(const char *name) [name](const DevMap &entry) -> bool { return entry.name == name; }); if(iter == CaptureDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%s\" not found", name}; + "Device name \"%.*s\" not found", static_cast<int>(name.length()), name.data()}; driver = iter->device_name; } else @@ -1024,7 +1024,7 @@ void AlsaCapture::stop() /* The ring buffer implicitly captures when checking availability. * Direct access needs to explicitly capture it into temp storage. */ - auto temp = al::vector<al::byte>( + auto temp = std::vector<std::byte>( static_cast<size_t>(snd_pcm_frames_to_bytes(mPcmHandle, avail))); captureSamples(temp.data(), avail); mBuffer = std::move(temp); @@ -1035,7 +1035,7 @@ void AlsaCapture::stop() mDoCapture = false; } -void AlsaCapture::captureSamples(al::byte *buffer, uint samples) +void AlsaCapture::captureSamples(std::byte *buffer, uint samples) { if(mRing) { @@ -1093,7 +1093,7 @@ void AlsaCapture::captureSamples(al::byte *buffer, uint samples) } if(samples > 0) std::fill_n(buffer, snd_pcm_frames_to_bytes(mPcmHandle, samples), - al::byte((mDevice->FmtType == DevFmtUByte) ? 0x80 : 0)); + std::byte((mDevice->FmtType == DevFmtUByte) ? 0x80 : 0)); } uint AlsaCapture::availableSamples() @@ -1205,7 +1205,7 @@ bool AlsaBackendFactory::init() error = false; #define LOAD_FUNC(f) do { \ - p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(alsa_handle, #f)); \ + p##f = al::bit_cast<decltype(p##f)>(GetSymbol(alsa_handle, #f)); \ if(p##f == nullptr) { \ error = true; \ missing_funcs += "\n" #f; \ diff --git a/alc/backends/base.cpp b/alc/backends/base.cpp index e5ad8494..ab3ad028 100644 --- a/alc/backends/base.cpp +++ b/alc/backends/base.cpp @@ -14,7 +14,6 @@ #include "albit.h" #include "core/logging.h" -#include "aloptional.h" #endif #include "atomic.h" @@ -38,7 +37,7 @@ backend_exception::~backend_exception() = default; bool BackendBase::reset() { throw al::backend_exception{al::backend_error::DeviceError, "Invalid BackendBase call"}; } -void BackendBase::captureSamples(al::byte*, uint) +void BackendBase::captureSamples(std::byte*, uint) { } uint BackendBase::availableSamples() diff --git a/alc/backends/base.h b/alc/backends/base.h index b6b3d922..eea0d238 100644 --- a/alc/backends/base.h +++ b/alc/backends/base.h @@ -3,13 +3,15 @@ #include <chrono> #include <cstdarg> +#include <cstddef> #include <memory> #include <ratio> #include <string> +#include <string_view> -#include "albyte.h" #include "core/device.h" #include "core/except.h" +#include "alc/events.h" using uint = unsigned int; @@ -20,13 +22,13 @@ struct ClockLatency { }; struct BackendBase { - virtual void open(const char *name) = 0; + virtual void open(std::string_view name) = 0; virtual bool reset(); virtual void start() = 0; virtual void stop() = 0; - virtual void captureSamples(al::byte *buffer, uint samples); + virtual void captureSamples(std::byte *buffer, uint samples); virtual uint availableSamples(); virtual ClockLatency getClockLatency(); @@ -78,6 +80,9 @@ struct BackendFactory { virtual bool querySupport(BackendType type) = 0; + virtual alc::EventSupport queryEventSupport(alc::EventType, BackendType) + { return alc::EventSupport::NoSupport; } + virtual std::string probe(BackendType type) = 0; virtual BackendPtr createBackend(DeviceBase *device, BackendType type) = 0; diff --git a/alc/backends/coreaudio.cpp b/alc/backends/coreaudio.cpp index 8b0e75fd..16b0781e 100644 --- a/alc/backends/coreaudio.cpp +++ b/alc/backends/coreaudio.cpp @@ -22,16 +22,17 @@ #include "coreaudio.h" -#include <inttypes.h> +#include <cinttypes> +#include <cmath> +#include <memory> #include <stdint.h> #include <stdio.h> #include <stdlib.h> +#include <string> #include <string.h> #include <unistd.h> - -#include <cmath> -#include <memory> -#include <string> +#include <vector> +#include <optional> #include "alnumeric.h" #include "core/converter.h" @@ -42,18 +43,40 @@ #include <AudioUnit/AudioUnit.h> #include <AudioToolbox/AudioToolbox.h> - -namespace { - #if TARGET_OS_IOS || TARGET_OS_TV #define CAN_ENUMERATE 0 #else +#include <IOKit/audio/IOAudioTypes.h> #define CAN_ENUMERATE 1 #endif +namespace { + constexpr auto OutputElement = 0; constexpr auto InputElement = 1; +struct FourCCPrinter { + char mString[sizeof(UInt32) + 1]{}; + + constexpr FourCCPrinter(UInt32 code) noexcept + { + for(size_t i{0};i < sizeof(UInt32);++i) + { + const auto ch = static_cast<char>(code & 0xff); + /* If this breaks early it'll leave the first byte null, to get + * read as a 0-length string. + */ + if(ch <= 0x1f || ch >= 0x7f) + break; + mString[sizeof(UInt32)-1-i] = ch; + code >>= 8; + } + } + constexpr FourCCPrinter(int code) noexcept : FourCCPrinter{static_cast<UInt32>(code)} { } + + constexpr const char *c_str() const noexcept { return mString; } +}; + #if CAN_ENUMERATE struct DeviceEntry { AudioDeviceID mId; @@ -147,7 +170,8 @@ UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture) &propSize); if(err) { - ERR("kAudioDevicePropertyStreamConfiguration size query failed: %u\n", err); + ERR("kAudioDevicePropertyStreamConfiguration size query failed: '%s' (%u)\n", + FourCCPrinter{err}.c_str(), err); return 0; } @@ -158,7 +182,8 @@ UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture) buflist); if(err) { - ERR("kAudioDevicePropertyStreamConfiguration query failed: %u\n", err); + ERR("kAudioDevicePropertyStreamConfiguration query failed: '%s' (%u)\n", + FourCCPrinter{err}.c_str(), err); return 0; } @@ -182,7 +207,7 @@ void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture) auto devIds = std::vector<AudioDeviceID>(propSize/sizeof(AudioDeviceID), kAudioDeviceUnknown); if(auto err = GetHwProperty(kAudioHardwarePropertyDevices, propSize, devIds.data())) { - ERR("Failed to get device list: %u\n", err); + ERR("Failed to get device list: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err); return; } @@ -247,6 +272,48 @@ void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture) newdevs.swap(list); } +struct DeviceHelper { + DeviceHelper() + { + AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice, + kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain}; + OSStatus status = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil); + if (status != noErr) + ERR("AudioObjectAddPropertyListener fail: %d", status); + } + ~DeviceHelper() + { + AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice, + kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain}; + OSStatus status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil); + if (status != noErr) + ERR("AudioObjectRemovePropertyListener fail: %d", status); + } + + static OSStatus DeviceListenerProc(AudioObjectID /*inObjectID*/, UInt32 inNumberAddresses, + const AudioObjectPropertyAddress *inAddresses, void* /*inClientData*/) + { + for(UInt32 i = 0; i < inNumberAddresses; ++i) + { + switch(inAddresses[i].mSelector) + { + case kAudioHardwarePropertyDefaultOutputDevice: + case kAudioHardwarePropertyDefaultSystemOutputDevice: + alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback, + "Default playback device changed: "+std::to_string(inAddresses[i].mSelector)); + break; + case kAudioHardwarePropertyDefaultInputDevice: + alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture, + "Default capture device changed: "+std::to_string(inAddresses[i].mSelector)); + break; + } + } + return noErr; + } +}; + +static std::optional<DeviceHelper> sDeviceHelper; + #else static constexpr char ca_device[] = "CoreAudio Default"; @@ -260,15 +327,8 @@ struct CoreAudioPlayback final : public BackendBase { OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) noexcept; - static OSStatus MixerProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, - AudioBufferList *ioData) noexcept - { - return static_cast<CoreAudioPlayback*>(inRefCon)->MixerProc(ioActionFlags, inTimeStamp, - inBusNumber, inNumberFrames, ioData); - } - void open(const char *name) override; + void open(std::string_view name) override; bool reset() override; void start() override; void stop() override; @@ -301,11 +361,11 @@ OSStatus CoreAudioPlayback::MixerProc(AudioUnitRenderActionFlags*, const AudioTi } -void CoreAudioPlayback::open(const char *name) +void CoreAudioPlayback::open(std::string_view name) { #if CAN_ENUMERATE AudioDeviceID audioDevice{kAudioDeviceUnknown}; - if(!name) + if(name.empty()) GetHwProperty(kAudioHardwarePropertyDefaultOutputDevice, sizeof(audioDevice), &audioDevice); else @@ -318,16 +378,16 @@ void CoreAudioPlayback::open(const char *name) auto devmatch = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), find_name); if(devmatch == PlaybackList.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%s\" not found", name}; + "Device name \"%.*s\" not found", static_cast<int>(name.length()), name.data()}; audioDevice = devmatch->mId; } #else - if(!name) + if(name.empty()) name = ca_device; - else if(strcmp(name, ca_device) != 0) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", - name}; + else if(name != ca_device) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", + static_cast<int>(name.length()), name.data()}; #endif /* open the default output unit */ @@ -351,7 +411,7 @@ void CoreAudioPlayback::open(const char *name) OSStatus err{AudioComponentInstanceNew(comp, &audioUnit)}; if(err != noErr) throw al::backend_exception{al::backend_error::NoDevice, - "Could not create component instance: %u", err}; + "Could not create component instance: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; #if CAN_ENUMERATE if(audioDevice != kAudioDeviceUnknown) @@ -362,7 +422,7 @@ void CoreAudioPlayback::open(const char *name) err = AudioUnitInitialize(audioUnit); if(err != noErr) throw al::backend_exception{al::backend_error::DeviceError, - "Could not initialize audio unit: %u", err}; + "Could not initialize audio unit: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; /* WARNING: I don't know if "valid" audio unit values are guaranteed to be * non-0. If not, this logic is broken. @@ -375,7 +435,7 @@ void CoreAudioPlayback::open(const char *name) mAudioUnit = audioUnit; #if CAN_ENUMERATE - if(name) + if(!name.empty()) mDevice->DeviceName = name; else { @@ -388,6 +448,21 @@ void CoreAudioPlayback::open(const char *name) if(!devname.empty()) mDevice->DeviceName = std::move(devname); else mDevice->DeviceName = "Unknown Device Name"; } + + if(audioDevice != kAudioDeviceUnknown) + { + UInt32 type{}; + err = GetDevProperty(audioDevice, kAudioDevicePropertyDataSource, false, + kAudioObjectPropertyElementMaster, sizeof(type), &type); + if(err != noErr) + ERR("Failed to get audio device type: %u\n", err); + else + { + TRACE("Got device type '%s'\n", FourCCPrinter{type}.c_str()); + mDevice->Flags.set(DirectEar, (type == kIOAudioOutputPortSubTypeHeadphones)); + } + } + #else mDevice->DeviceName = name; #endif @@ -397,7 +472,7 @@ bool CoreAudioPlayback::reset() { OSStatus err{AudioUnitUninitialize(mAudioUnit)}; if(err != noErr) - ERR("-- AudioUnitUninitialize failed.\n"); + ERR("AudioUnitUninitialize failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err); /* retrieve default output unit's properties (output side) */ AudioStreamBasicDescription streamFormat{}; @@ -406,7 +481,8 @@ bool CoreAudioPlayback::reset() OutputElement, &streamFormat, &size); if(err != noErr || size != sizeof(streamFormat)) { - ERR("AudioUnitGetProperty failed\n"); + ERR("AudioUnitGetProperty(StreamFormat) failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), + err); return false; } @@ -473,7 +549,8 @@ bool CoreAudioPlayback::reset() OutputElement, &streamFormat, sizeof(streamFormat)); if(err != noErr) { - ERR("AudioUnitSetProperty failed\n"); + ERR("AudioUnitSetProperty(StreamFormat) failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), + err); return false; } @@ -482,14 +559,16 @@ bool CoreAudioPlayback::reset() /* setup callback */ mFrameSize = mDevice->frameSizeFromFmt(); AURenderCallbackStruct input{}; - input.inputProc = CoreAudioPlayback::MixerProcC; + input.inputProc = [](void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) noexcept + { return static_cast<CoreAudioPlayback*>(inRefCon)->MixerProc(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); }; input.inputProcRefCon = this; err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, OutputElement, &input, sizeof(AURenderCallbackStruct)); if(err != noErr) { - ERR("AudioUnitSetProperty failed\n"); + ERR("AudioUnitSetProperty(SetRenderCallback) failed: '%s' (%u)\n", + FourCCPrinter{err}.c_str(), err); return false; } @@ -497,7 +576,7 @@ bool CoreAudioPlayback::reset() err = AudioUnitInitialize(mAudioUnit); if(err != noErr) { - ERR("AudioUnitInitialize failed\n"); + ERR("AudioUnitInitialize failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err); return false; } @@ -509,14 +588,14 @@ void CoreAudioPlayback::start() const OSStatus err{AudioOutputUnitStart(mAudioUnit)}; if(err != noErr) throw al::backend_exception{al::backend_error::DeviceError, - "AudioOutputUnitStart failed: %d", err}; + "AudioOutputUnitStart failed: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; } void CoreAudioPlayback::stop() { OSStatus err{AudioOutputUnitStop(mAudioUnit)}; if(err != noErr) - ERR("AudioOutputUnitStop failed\n"); + ERR("AudioOutputUnitStop failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err); } @@ -527,18 +606,11 @@ struct CoreAudioCapture final : public BackendBase { OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) noexcept; - static OSStatus RecordProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, - AudioBufferList *ioData) noexcept - { - return static_cast<CoreAudioCapture*>(inRefCon)->RecordProc(ioActionFlags, inTimeStamp, - inBusNumber, inNumberFrames, ioData); - } - void open(const char *name) override; + void open(std::string_view name) override; void start() override; void stop() override; - void captureSamples(al::byte *buffer, uint samples) override; + void captureSamples(std::byte *buffer, uint samples) override; uint availableSamples() override; AudioUnit mAudioUnit{0}; @@ -548,7 +620,7 @@ struct CoreAudioCapture final : public BackendBase { SampleConverterPtr mConverter; - al::vector<char> mCaptureData; + std::vector<char> mCaptureData; RingBufferPtr mRing{nullptr}; @@ -568,7 +640,7 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags, AudioBufferList*) noexcept { union { - al::byte _[maxz(sizeof(AudioBufferList), offsetof(AudioBufferList, mBuffers[1]))]; + std::byte _[maxz(sizeof(AudioBufferList), offsetof(AudioBufferList, mBuffers[1]))]; AudioBufferList list; } audiobuf{}; @@ -581,7 +653,7 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags, inNumberFrames, &audiobuf.list)}; if(err != noErr) { - ERR("AudioUnitRender capture error: %d\n", err); + ERR("AudioUnitRender capture error: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err); return err; } @@ -590,11 +662,11 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags, } -void CoreAudioCapture::open(const char *name) +void CoreAudioCapture::open(std::string_view name) { #if CAN_ENUMERATE AudioDeviceID audioDevice{kAudioDeviceUnknown}; - if(!name) + if(name.empty()) GetHwProperty(kAudioHardwarePropertyDefaultInputDevice, sizeof(audioDevice), &audioDevice); else @@ -607,16 +679,16 @@ void CoreAudioCapture::open(const char *name) auto devmatch = std::find_if(CaptureList.cbegin(), CaptureList.cend(), find_name); if(devmatch == CaptureList.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%s\" not found", name}; + "Device name \"%.*s\" not found", static_cast<int>(name.length()), name.data()}; audioDevice = devmatch->mId; } #else - if(!name) + if(name.empty()) name = ca_device; - else if(strcmp(name, ca_device) != 0) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", - name}; + else if(name != ca_device) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", + static_cast<int>(name.length()), name.data()}; #endif AudioComponentDescription desc{}; @@ -640,7 +712,7 @@ void CoreAudioCapture::open(const char *name) OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)}; if(err != noErr) throw al::backend_exception{al::backend_error::NoDevice, - "Could not create component instance: %u", err}; + "Could not create component instance: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; // Turn off AudioUnit output UInt32 enableIO{0}; @@ -648,7 +720,8 @@ void CoreAudioCapture::open(const char *name) kAudioUnitScope_Output, OutputElement, &enableIO, sizeof(enableIO)); if(err != noErr) throw al::backend_exception{al::backend_error::DeviceError, - "Could not disable audio unit output property: %u", err}; + "Could not disable audio unit output property: '%s' (%u)", FourCCPrinter{err}.c_str(), + err}; // Turn on AudioUnit input enableIO = 1; @@ -656,7 +729,8 @@ void CoreAudioCapture::open(const char *name) kAudioUnitScope_Input, InputElement, &enableIO, sizeof(enableIO)); if(err != noErr) throw al::backend_exception{al::backend_error::DeviceError, - "Could not enable audio unit input property: %u", err}; + "Could not enable audio unit input property: '%s' (%u)", FourCCPrinter{err}.c_str(), + err}; #if CAN_ENUMERATE if(audioDevice != kAudioDeviceUnknown) @@ -666,14 +740,15 @@ void CoreAudioCapture::open(const char *name) // set capture callback AURenderCallbackStruct input{}; - input.inputProc = CoreAudioCapture::RecordProcC; + input.inputProc = [](void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) noexcept + { return static_cast<CoreAudioCapture*>(inRefCon)->RecordProc(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); }; input.inputProcRefCon = this; err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, InputElement, &input, sizeof(AURenderCallbackStruct)); if(err != noErr) throw al::backend_exception{al::backend_error::DeviceError, - "Could not set capture callback: %u", err}; + "Could not set capture callback: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; // Disable buffer allocation for capture UInt32 flag{0}; @@ -681,13 +756,14 @@ void CoreAudioCapture::open(const char *name) kAudioUnitScope_Output, InputElement, &flag, sizeof(flag)); if(err != noErr) throw al::backend_exception{al::backend_error::DeviceError, - "Could not disable buffer allocation property: %u", err}; + "Could not disable buffer allocation property: '%s' (%u)", FourCCPrinter{err}.c_str(), + err}; // Initialize the device err = AudioUnitInitialize(mAudioUnit); if(err != noErr) throw al::backend_exception{al::backend_error::DeviceError, - "Could not initialize audio unit: %u", err}; + "Could not initialize audio unit: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; // Get the hardware format AudioStreamBasicDescription hardwareFormat{}; @@ -696,7 +772,7 @@ void CoreAudioCapture::open(const char *name) InputElement, &hardwareFormat, &propertySize); if(err != noErr || propertySize != sizeof(hardwareFormat)) throw al::backend_exception{al::backend_error::DeviceError, - "Could not get input format: %u", err}; + "Could not get input format: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; // Set up the requested format description AudioStreamBasicDescription requestedFormat{}; @@ -777,7 +853,7 @@ void CoreAudioCapture::open(const char *name) InputElement, &outputFormat, sizeof(outputFormat)); if(err != noErr) throw al::backend_exception{al::backend_error::DeviceError, - "Could not set input format: %u", err}; + "Could not set input format: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; /* Calculate the minimum AudioUnit output format frame count for the pre- * conversion ring buffer. Ensure at least 100ms for the total buffer. @@ -796,7 +872,7 @@ void CoreAudioCapture::open(const char *name) kAudioUnitScope_Global, OutputElement, &outputFrameCount, &propertySize); if(err != noErr || propertySize != sizeof(outputFrameCount)) throw al::backend_exception{al::backend_error::DeviceError, - "Could not get input frame count: %u", err}; + "Could not get input frame count: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; mCaptureData.resize(outputFrameCount * mFrameSize); @@ -810,7 +886,7 @@ void CoreAudioCapture::open(const char *name) mDevice->Frequency, Resampler::FastBSinc24); #if CAN_ENUMERATE - if(name) + if(!name.empty()) mDevice->DeviceName = name; else { @@ -834,17 +910,17 @@ void CoreAudioCapture::start() OSStatus err{AudioOutputUnitStart(mAudioUnit)}; if(err != noErr) throw al::backend_exception{al::backend_error::DeviceError, - "AudioOutputUnitStart failed: %d", err}; + "AudioOutputUnitStart failed: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; } void CoreAudioCapture::stop() { OSStatus err{AudioOutputUnitStop(mAudioUnit)}; if(err != noErr) - ERR("AudioOutputUnitStop failed\n"); + ERR("AudioOutputUnitStop failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err); } -void CoreAudioCapture::captureSamples(al::byte *buffer, uint samples) +void CoreAudioCapture::captureSamples(std::byte *buffer, uint samples) { if(!mConverter) { @@ -882,7 +958,13 @@ BackendFactory &CoreAudioBackendFactory::getFactory() return factory; } -bool CoreAudioBackendFactory::init() { return true; } +bool CoreAudioBackendFactory::init() +{ +#if CAN_ENUMERATE + sDeviceHelper.emplace(); +#endif + return true; +} bool CoreAudioBackendFactory::querySupport(BackendType type) { return type == BackendType::Playback || type == BackendType::Capture; } @@ -930,3 +1012,18 @@ BackendPtr CoreAudioBackendFactory::createBackend(DeviceBase *device, BackendTyp return BackendPtr{new CoreAudioCapture{device}}; return nullptr; } + +alc::EventSupport CoreAudioBackendFactory::queryEventSupport(alc::EventType eventType, BackendType) +{ + switch(eventType) + { + case alc::EventType::DefaultDeviceChanged: + return alc::EventSupport::FullSupport; + + case alc::EventType::DeviceAdded: + case alc::EventType::DeviceRemoved: + case alc::EventType::Count: + break; + } + return alc::EventSupport::NoSupport; +} diff --git a/alc/backends/coreaudio.h b/alc/backends/coreaudio.h index 1252edde..6ea4307c 100644 --- a/alc/backends/coreaudio.h +++ b/alc/backends/coreaudio.h @@ -9,6 +9,8 @@ public: bool querySupport(BackendType type) override; + alc::EventSupport queryEventSupport(alc::EventType eventType, BackendType type) override; + std::string probe(BackendType type) override; BackendPtr createBackend(DeviceBase *device, BackendType type) override; diff --git a/alc/backends/dsound.cpp b/alc/backends/dsound.cpp index f549c0fe..58aa69b2 100644 --- a/alc/backends/dsound.cpp +++ b/alc/backends/dsound.cpp @@ -25,10 +25,6 @@ #define WIN32_LEAN_AND_MEAN #include <windows.h> -#include <stdlib.h> -#include <stdio.h> -#include <memory.h> - #include <cguid.h> #include <mmreg.h> #ifndef _WAVEFORMATEXTENSIBLE_ @@ -36,15 +32,22 @@ #include <ksmedia.h> #endif +#include <algorithm> #include <atomic> #include <cassert> -#include <thread> +#include <functional> +#include <memory.h> +#include <mutex> +#include <stdlib.h> +#include <stdio.h> #include <string> +#include <thread> #include <vector> -#include <algorithm> -#include <functional> +#include "albit.h" #include "alnumeric.h" +#include "alspan.h" +#include "althrd_setname.h" #include "comptr.h" #include "core/device.h" #include "core/helpers.h" @@ -52,7 +55,6 @@ #include "dynload.h" #include "ringbuffer.h" #include "strutils.h" -#include "threads.h" /* MinGW-w64 needs this for some unknown reason now. */ using LPCWAVEFORMATEX = const WAVEFORMATEX*; @@ -129,10 +131,10 @@ struct DevMap { { } }; -al::vector<DevMap> PlaybackDevices; -al::vector<DevMap> CaptureDevices; +std::vector<DevMap> PlaybackDevices; +std::vector<DevMap> CaptureDevices; -bool checkName(const al::vector<DevMap> &list, const std::string &name) +bool checkName(const al::span<DevMap> list, const std::string &name) { auto match_name = [&name](const DevMap &entry) -> bool { return entry.name == name; }; @@ -144,7 +146,7 @@ BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR*, voi if(!guid) return TRUE; - auto& devices = *static_cast<al::vector<DevMap>*>(data); + auto& devices = *static_cast<std::vector<DevMap>*>(data); const std::string basename{DEVNAME_HEAD + wstr_to_utf8(desc)}; int count{1}; @@ -176,7 +178,7 @@ struct DSoundPlayback final : public BackendBase { int mixerProc(); - void open(const char *name) override; + void open(std::string_view name) override; bool reset() override; void start() override; void stop() override; @@ -299,24 +301,22 @@ FORCE_ALIGN int DSoundPlayback::mixerProc() return 0; } -void DSoundPlayback::open(const char *name) +void DSoundPlayback::open(std::string_view name) { HRESULT hr; if(PlaybackDevices.empty()) { /* Initialize COM to prevent name truncation */ - HRESULT hrcom{CoInitialize(nullptr)}; + ComWrapper com{}; hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices); if(FAILED(hr)) ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr); - if(SUCCEEDED(hrcom)) - CoUninitialize(); } const GUID *guid{nullptr}; - if(!name && !PlaybackDevices.empty()) + if(name.empty() && !PlaybackDevices.empty()) { - name = PlaybackDevices[0].name.c_str(); + name = PlaybackDevices[0].name; guid = &PlaybackDevices[0].guid; } else @@ -332,7 +332,8 @@ void DSoundPlayback::open(const char *name) [&id](const DevMap &entry) -> bool { return entry.guid == id; }); if(iter == PlaybackDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%s\" not found", name}; + "Device name \"%.*s\" not found", static_cast<int>(name.length()), + name.data()}; } guid = &iter->guid; } @@ -347,7 +348,7 @@ void DSoundPlayback::open(const char *name) //DirectSound Init code ComPtr<IDirectSound> ds; if(SUCCEEDED(hr)) - hr = DirectSoundCreate(guid, ds.getPtr(), nullptr); + hr = DirectSoundCreate(guid, al::out_ptr(ds), nullptr); if(SUCCEEDED(hr)) hr = ds->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY); if(FAILED(hr)) @@ -460,7 +461,7 @@ retry_open: DSBUFFERDESC DSBDescription{}; DSBDescription.dwSize = sizeof(DSBDescription); DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER; - hr = mDS->CreateSoundBuffer(&DSBDescription, mPrimaryBuffer.getPtr(), nullptr); + hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mPrimaryBuffer), nullptr); } if(SUCCEEDED(hr)) hr = mPrimaryBuffer->SetFormat(&OutputType.Format); @@ -480,7 +481,7 @@ retry_open: DSBDescription.dwBufferBytes = mDevice->BufferSize * OutputType.Format.nBlockAlign; DSBDescription.lpwfxFormat = &OutputType.Format; - hr = mDS->CreateSoundBuffer(&DSBDescription, mBuffer.getPtr(), nullptr); + hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mBuffer), nullptr); if(FAILED(hr) && mDevice->FmtType == DevFmtFloat) { mDevice->FmtType = DevFmtShort; @@ -490,12 +491,9 @@ retry_open: if(SUCCEEDED(hr)) { - void *ptr; - hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, &ptr); + hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, al::out_ptr(mNotifies)); if(SUCCEEDED(hr)) { - mNotifies = ComPtr<IDirectSoundNotify>{static_cast<IDirectSoundNotify*>(ptr)}; - uint num_updates{mDevice->BufferSize / mDevice->UpdateSize}; assert(num_updates <= MAX_UPDATES); @@ -550,10 +548,10 @@ struct DSoundCapture final : public BackendBase { DSoundCapture(DeviceBase *device) noexcept : BackendBase{device} { } ~DSoundCapture() override; - void open(const char *name) override; + void open(std::string_view name) override; void start() override; void stop() override; - void captureSamples(al::byte *buffer, uint samples) override; + void captureSamples(std::byte *buffer, uint samples) override; uint availableSamples() override; ComPtr<IDirectSoundCapture> mDSC; @@ -577,24 +575,22 @@ DSoundCapture::~DSoundCapture() } -void DSoundCapture::open(const char *name) +void DSoundCapture::open(std::string_view name) { HRESULT hr; if(CaptureDevices.empty()) { /* Initialize COM to prevent name truncation */ - HRESULT hrcom{CoInitialize(nullptr)}; + ComWrapper com{}; hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices); if(FAILED(hr)) ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr); - if(SUCCEEDED(hrcom)) - CoUninitialize(); } const GUID *guid{nullptr}; - if(!name && !CaptureDevices.empty()) + if(name.empty() && !CaptureDevices.empty()) { - name = CaptureDevices[0].name.c_str(); + name = CaptureDevices[0].name; guid = &CaptureDevices[0].guid; } else @@ -610,7 +606,8 @@ void DSoundCapture::open(const char *name) [&id](const DevMap &entry) -> bool { return entry.guid == id; }); if(iter == CaptureDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%s\" not found", name}; + "Device name \"%.*s\" not found", static_cast<int>(name.length()), + name.data()}; } guid = &iter->guid; } @@ -679,9 +676,9 @@ void DSoundCapture::open(const char *name) DSCBDescription.lpwfxFormat = &InputType.Format; //DirectSoundCapture Init code - hr = DirectSoundCaptureCreate(guid, mDSC.getPtr(), nullptr); + hr = DirectSoundCaptureCreate(guid, al::out_ptr(mDSC), nullptr); if(SUCCEEDED(hr)) - mDSC->CreateCaptureBuffer(&DSCBDescription, mDSCbuffer.getPtr(), nullptr); + mDSC->CreateCaptureBuffer(&DSCBDescription, al::out_ptr(mDSCbuffer), nullptr); if(SUCCEEDED(hr)) mRing = RingBuffer::Create(mDevice->BufferSize, InputType.Format.nBlockAlign, false); @@ -719,7 +716,7 @@ void DSoundCapture::stop() } } -void DSoundCapture::captureSamples(al::byte *buffer, uint samples) +void DSoundCapture::captureSamples(std::byte *buffer, uint samples) { mRing->read(buffer, samples); } uint DSoundCapture::availableSamples() @@ -781,7 +778,7 @@ bool DSoundBackendFactory::init() } #define LOAD_FUNC(f) do { \ - p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(ds_handle, #f)); \ + p##f = al::bit_cast<decltype(p##f)>(GetSymbol(ds_handle, #f)); \ if(!p##f) \ { \ CloseLib(ds_handle); \ @@ -814,8 +811,8 @@ std::string DSoundBackendFactory::probe(BackendType type) }; /* Initialize COM to prevent name truncation */ + ComWrapper com{}; HRESULT hr; - HRESULT hrcom{CoInitialize(nullptr)}; switch(type) { case BackendType::Playback: @@ -834,8 +831,6 @@ std::string DSoundBackendFactory::probe(BackendType type) std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device); break; } - if(SUCCEEDED(hrcom)) - CoUninitialize(); return outnames; } diff --git a/alc/backends/jack.cpp b/alc/backends/jack.cpp index 791002ca..a0a5c440 100644 --- a/alc/backends/jack.cpp +++ b/alc/backends/jack.cpp @@ -22,23 +22,26 @@ #include "jack.h" +#include <array> #include <cstdlib> #include <cstdio> #include <cstring> #include <memory.h> - -#include <array> +#include <mutex> #include <thread> #include <functional> +#include <vector> +#include "albit.h" #include "alc/alconfig.h" #include "alnumeric.h" +#include "alsem.h" +#include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" #include "core/logging.h" #include "dynload.h" #include "ringbuffer.h" -#include "threads.h" #include <jack/jack.h> #include <jack/ringbuffer.h> @@ -126,7 +129,7 @@ bool jack_load() error = false; #define LOAD_FUNC(f) do { \ - p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f)); \ + p##f = al::bit_cast<decltype(p##f)>(GetSymbol(jack_handle, #f)); \ if(p##f == nullptr) { \ error = true; \ missing_funcs += "\n" #f; \ @@ -135,7 +138,7 @@ bool jack_load() JACK_FUNCS(LOAD_FUNC); #undef LOAD_FUNC /* Optional symbols. These don't exist in all versions of JACK. */ -#define LOAD_SYM(f) p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f)) +#define LOAD_SYM(f) p##f = al::bit_cast<decltype(p##f)>(GetSymbol(jack_handle, #f)) LOAD_SYM(jack_error_callback); #undef LOAD_SYM @@ -167,10 +170,10 @@ struct DeviceEntry { { } }; -al::vector<DeviceEntry> PlaybackList; +std::vector<DeviceEntry> PlaybackList; -void EnumerateDevices(jack_client_t *client, al::vector<DeviceEntry> &list) +void EnumerateDevices(jack_client_t *client, std::vector<DeviceEntry> &list) { std::remove_reference_t<decltype(list)>{}.swap(list); @@ -295,7 +298,7 @@ struct JackPlayback final : public BackendBase { int mixerProc(); - void open(const char *name) override; + void open(std::string_view name) override; bool reset() override; void start() override; void stop() override; @@ -457,7 +460,7 @@ int JackPlayback::mixerProc() } -void JackPlayback::open(const char *name) +void JackPlayback::open(std::string_view name) { if(!mClient) { @@ -481,9 +484,9 @@ void JackPlayback::open(const char *name) if(PlaybackList.empty()) EnumerateDevices(mClient, PlaybackList); - if(!name && !PlaybackList.empty()) + if(name.empty() && !PlaybackList.empty()) { - name = PlaybackList[0].mName.c_str(); + name = PlaybackList[0].mName; mPortPattern = PlaybackList[0].mPattern; } else @@ -493,14 +496,10 @@ void JackPlayback::open(const char *name) auto iter = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), check_name); if(iter == PlaybackList.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%s\" not found", name?name:""}; + "Device name \"%.*s\" not found", static_cast<int>(name.length()), name.data()}; mPortPattern = iter->mPattern; } - mRTMixing = GetConfigValueBool(name, "jack", "rt-mix", true); - jack_set_process_callback(mClient, - mRTMixing ? &JackPlayback::processRtC : &JackPlayback::processC, this); - mDevice->DeviceName = name; } @@ -511,6 +510,10 @@ bool JackPlayback::reset() std::for_each(mPort.begin(), mPort.end(), unregister_port); mPort.fill(nullptr); + mRTMixing = GetConfigValueBool(mDevice->DeviceName.c_str(), "jack", "rt-mix", true); + jack_set_process_callback(mClient, + mRTMixing ? &JackPlayback::processRtC : &JackPlayback::processC, this); + /* Ignore the requested buffer metrics and just keep one JACK-sized buffer * ready for when requested. */ @@ -586,7 +589,7 @@ void JackPlayback::start() throw al::backend_exception{al::backend_error::DeviceError, "No playback ports found"}; } - for(size_t i{0};i < al::size(mPort) && mPort[i];++i) + for(size_t i{0};i < std::size(mPort) && mPort[i];++i) { if(!pnames[i]) { diff --git a/alc/backends/loopback.cpp b/alc/backends/loopback.cpp index bf4ab246..2972fc01 100644 --- a/alc/backends/loopback.cpp +++ b/alc/backends/loopback.cpp @@ -30,7 +30,7 @@ namespace { struct LoopbackBackend final : public BackendBase { LoopbackBackend(DeviceBase *device) noexcept : BackendBase{device} { } - void open(const char *name) override; + void open(std::string_view name) override; bool reset() override; void start() override; void stop() override; @@ -39,7 +39,7 @@ struct LoopbackBackend final : public BackendBase { }; -void LoopbackBackend::open(const char *name) +void LoopbackBackend::open(std::string_view name) { mDevice->DeviceName = name; } diff --git a/alc/backends/null.cpp b/alc/backends/null.cpp index 5a8fc255..3c68e4ce 100644 --- a/alc/backends/null.cpp +++ b/alc/backends/null.cpp @@ -30,10 +30,10 @@ #include <functional> #include <thread> -#include "core/device.h" +#include "althrd_setname.h" #include "almalloc.h" +#include "core/device.h" #include "core/helpers.h" -#include "threads.h" namespace { @@ -50,7 +50,7 @@ struct NullBackend final : public BackendBase { int mixerProc(); - void open(const char *name) override; + void open(std::string_view name) override; bool reset() override; void start() override; void stop() override; @@ -105,13 +105,13 @@ int NullBackend::mixerProc() } -void NullBackend::open(const char *name) +void NullBackend::open(std::string_view name) { - if(!name) + if(name.empty()) name = nullDevice; - else if(strcmp(name, nullDevice) != 0) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", - name}; + else if(name != nullDevice) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", + static_cast<int>(name.length()), name.data()}; mDevice->DeviceName = name; } diff --git a/alc/backends/oboe.cpp b/alc/backends/oboe.cpp index 461f5a6a..b7bab19a 100644 --- a/alc/backends/oboe.cpp +++ b/alc/backends/oboe.cpp @@ -28,7 +28,9 @@ struct OboePlayback final : public BackendBase, public oboe::AudioStreamCallback oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) override; - void open(const char *name) override; + void onErrorAfterClose(oboe::AudioStream* /* audioStream */, oboe::Result /* error */) override; + + void open(std::string_view name) override; bool reset() override; void start() override; void stop() override; @@ -46,14 +48,21 @@ oboe::DataCallbackResult OboePlayback::onAudioReady(oboe::AudioStream *oboeStrea return oboe::DataCallbackResult::Continue; } +void OboePlayback::onErrorAfterClose(oboe::AudioStream* audioStream, oboe::Result error) +{ + if (error == oboe::Result::ErrorDisconnected) { + mDevice->handleDisconnect("Oboe AudioStream was disconnected: %s", oboe::convertToText(error)); + } + TRACE("Error was %s", oboe::convertToText(error)); +} -void OboePlayback::open(const char *name) +void OboePlayback::open(std::string_view name) { - if(!name) + if(name.empty()) name = device_name; - else if(std::strcmp(name, device_name) != 0) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", - name}; + else if(name != device_name) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", + static_cast<int>(name.length()), name.data()}; /* Open a basic output stream, just to ensure it can work. */ oboe::ManagedStream stream; @@ -197,8 +206,7 @@ void OboePlayback::stop() { oboe::Result result{mStream->stop()}; if(result != oboe::Result::OK) - throw al::backend_exception{al::backend_error::DeviceError, "Failed to stop stream: %s", - oboe::convertToText(result)}; + ERR("Failed to stop stream: %s\n", oboe::convertToText(result)); } @@ -212,10 +220,10 @@ struct OboeCapture final : public BackendBase, public oboe::AudioStreamCallback oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) override; - void open(const char *name) override; + void open(std::string_view name) override; void start() override; void stop() override; - void captureSamples(al::byte *buffer, uint samples) override; + void captureSamples(std::byte *buffer, uint samples) override; uint availableSamples() override; }; @@ -227,13 +235,13 @@ oboe::DataCallbackResult OboeCapture::onAudioReady(oboe::AudioStream*, void *aud } -void OboeCapture::open(const char *name) +void OboeCapture::open(std::string_view name) { - if(!name) + if(name.empty()) name = device_name; - else if(std::strcmp(name, device_name) != 0) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", - name}; + else if(name != device_name) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", + static_cast<int>(name.length()), name.data()}; oboe::AudioStreamBuilder builder; builder.setDirection(oboe::Direction::Input) @@ -315,14 +323,13 @@ void OboeCapture::stop() { const oboe::Result result{mStream->stop()}; if(result != oboe::Result::OK) - throw al::backend_exception{al::backend_error::DeviceError, "Failed to stop stream: %s", - oboe::convertToText(result)}; + ERR("Failed to stop stream: %s\n", oboe::convertToText(result)); } uint OboeCapture::availableSamples() { return static_cast<uint>(mRing->readSpace()); } -void OboeCapture::captureSamples(al::byte *buffer, uint samples) +void OboeCapture::captureSamples(std::byte *buffer, uint samples) { mRing->read(buffer, samples); } } // namespace diff --git a/alc/backends/opensl.cpp b/alc/backends/opensl.cpp index f5b98fb8..61e3c9a7 100644 --- a/alc/backends/opensl.cpp +++ b/alc/backends/opensl.cpp @@ -26,20 +26,22 @@ #include <stdlib.h> #include <jni.h> -#include <new> #include <array> #include <cstring> +#include <mutex> +#include <new> #include <thread> #include <functional> #include "albit.h" #include "alnumeric.h" +#include "alsem.h" +#include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" #include "core/logging.h" #include "opthelpers.h" #include "ringbuffer.h" -#include "threads.h" #include <SLES/OpenSLES.h> #include <SLES/OpenSLES_Android.h> @@ -159,12 +161,10 @@ struct OpenSLPlayback final : public BackendBase { ~OpenSLPlayback() override; void process(SLAndroidSimpleBufferQueueItf bq) noexcept; - static void processC(SLAndroidSimpleBufferQueueItf bq, void *context) noexcept - { static_cast<OpenSLPlayback*>(context)->process(bq); } int mixerProc(); - void open(const char *name) override; + void open(std::string_view name) override; bool reset() override; void start() override; void stop() override; @@ -312,13 +312,13 @@ int OpenSLPlayback::mixerProc() } -void OpenSLPlayback::open(const char *name) +void OpenSLPlayback::open(std::string_view name) { - if(!name) + if(name.empty()) name = opensl_device; - else if(strcmp(name, opensl_device) != 0) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", - name}; + else if(name != opensl_device) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", + static_cast<int>(name.length()), name.data()}; /* There's only one device, so if it's already open, there's nothing to do. */ if(mEngineObj) return; @@ -564,7 +564,9 @@ void OpenSLPlayback::start() PrintErr(result, "bufferQueue->GetInterface"); if(SL_RESULT_SUCCESS == result) { - result = VCALL(bufferQueue,RegisterCallback)(&OpenSLPlayback::processC, this); + result = VCALL(bufferQueue,RegisterCallback)( + [](SLAndroidSimpleBufferQueueItf bq, void *context) noexcept + { static_cast<OpenSLPlayback*>(context)->process(bq); }, this); PrintErr(result, "bufferQueue->RegisterCallback"); } if(SL_RESULT_SUCCESS != result) @@ -642,13 +644,11 @@ struct OpenSLCapture final : public BackendBase { ~OpenSLCapture() override; void process(SLAndroidSimpleBufferQueueItf bq) noexcept; - static void processC(SLAndroidSimpleBufferQueueItf bq, void *context) noexcept - { static_cast<OpenSLCapture*>(context)->process(bq); } - void open(const char *name) override; + void open(std::string_view name) override; void start() override; void stop() override; - void captureSamples(al::byte *buffer, uint samples) override; + void captureSamples(std::byte *buffer, uint samples) override; uint availableSamples() override; /* engine interfaces */ @@ -686,13 +686,13 @@ void OpenSLCapture::process(SLAndroidSimpleBufferQueueItf) noexcept } -void OpenSLCapture::open(const char* name) +void OpenSLCapture::open(std::string_view name) { - if(!name) + if(name.empty()) name = opensl_device; - else if(strcmp(name, opensl_device) != 0) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", - name}; + else if(name != opensl_device) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", + static_cast<int>(name.length()), name.data()}; SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)}; PrintErr(result, "slCreateEngine"); @@ -813,13 +813,15 @@ void OpenSLCapture::open(const char* name) } if(SL_RESULT_SUCCESS == result) { - result = VCALL(bufferQueue,RegisterCallback)(&OpenSLCapture::processC, this); + result = VCALL(bufferQueue,RegisterCallback)( + [](SLAndroidSimpleBufferQueueItf bq, void *context) noexcept + { static_cast<OpenSLCapture*>(context)->process(bq); }, this); PrintErr(result, "bufferQueue->RegisterCallback"); } if(SL_RESULT_SUCCESS == result) { const uint chunk_size{mDevice->UpdateSize * mFrameSize}; - const auto silence = (mDevice->FmtType == DevFmtUByte) ? al::byte{0x80} : al::byte{0}; + const auto silence = (mDevice->FmtType == DevFmtUByte) ? std::byte{0x80} : std::byte{0}; auto data = mRing->getWriteVector(); std::fill_n(data.first.buf, data.first.len*chunk_size, silence); @@ -883,7 +885,7 @@ void OpenSLCapture::stop() } } -void OpenSLCapture::captureSamples(al::byte *buffer, uint samples) +void OpenSLCapture::captureSamples(std::byte *buffer, uint samples) { const uint update_size{mDevice->UpdateSize}; const uint chunk_size{update_size * mFrameSize}; @@ -932,7 +934,7 @@ void OpenSLCapture::captureSamples(al::byte *buffer, uint samples) return; /* For each buffer chunk that was fully read, queue another writable buffer - * chunk to keep the OpenSL queue full. This is rather convulated, as a + * chunk to keep the OpenSL queue full. This is rather convoluted, as a * result of the ring buffer holding more elements than are writable at a * given time. The end of the write vector increments when the read pointer * advances, which will "expose" a previously unwritable element. So for diff --git a/alc/backends/oss.cpp b/alc/backends/oss.cpp index 6d4fa261..87d3ba35 100644 --- a/alc/backends/oss.cpp +++ b/alc/backends/oss.cpp @@ -31,27 +31,23 @@ #include <algorithm> #include <atomic> #include <cerrno> -#include <cstdio> #include <cstring> #include <exception> #include <functional> #include <memory> -#include <new> #include <string> #include <thread> #include <utility> +#include <vector> -#include "albyte.h" #include "alc/alconfig.h" #include "almalloc.h" #include "alnumeric.h" -#include "aloptional.h" +#include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" #include "core/logging.h" #include "ringbuffer.h" -#include "threads.h" -#include "vector.h" #include <sys/soundcard.h> @@ -92,22 +88,22 @@ struct DevMap { std::string device_name; }; -al::vector<DevMap> PlaybackDevices; -al::vector<DevMap> CaptureDevices; +std::vector<DevMap> PlaybackDevices; +std::vector<DevMap> CaptureDevices; #ifdef ALC_OSS_COMPAT #define DSP_CAP_OUTPUT 0x00020000 #define DSP_CAP_INPUT 0x00010000 -void ALCossListPopulate(al::vector<DevMap> &devlist, int type) +void ALCossListPopulate(std::vector<DevMap> &devlist, int type) { devlist.emplace_back(DevMap{DefaultName, (type==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback}); } #else -void ALCossListAppend(al::vector<DevMap> &list, al::span<const char> handle, al::span<const char> path) +void ALCossListAppend(std::vector<DevMap> &list, al::span<const char> handle, al::span<const char> path) { #ifdef ALC_OSS_DEVNODE_TRUC for(size_t i{0};i < path.size();++i) @@ -152,7 +148,7 @@ void ALCossListAppend(al::vector<DevMap> &list, al::span<const char> handle, al: TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str()); } -void ALCossListPopulate(al::vector<DevMap> &devlist, int type_flag) +void ALCossListPopulate(std::vector<DevMap> &devlist, int type_flag) { int fd{open("/dev/mixer", O_RDONLY)}; if(fd < 0) @@ -231,14 +227,14 @@ struct OSSPlayback final : public BackendBase { int mixerProc(); - void open(const char *name) override; + void open(std::string_view name) override; bool reset() override; void start() override; void stop() override; int mFd{-1}; - al::vector<al::byte> mMixData; + std::vector<std::byte> mMixData; std::atomic<bool> mKillNow{true}; std::thread mThread; @@ -284,7 +280,7 @@ int OSSPlayback::mixerProc() continue; } - al::byte *write_ptr{mMixData.data()}; + std::byte *write_ptr{mMixData.data()}; size_t to_write{mMixData.size()}; mDevice->renderSamples(write_ptr, static_cast<uint>(to_write/frame_size), frame_step); while(to_write > 0 && !mKillNow.load(std::memory_order_acquire)) @@ -308,10 +304,10 @@ int OSSPlayback::mixerProc() } -void OSSPlayback::open(const char *name) +void OSSPlayback::open(std::string_view name) { const char *devname{DefaultPlayback.c_str()}; - if(!name) + if(name.empty()) name = DefaultName; else { @@ -324,7 +320,7 @@ void OSSPlayback::open(const char *name) ); if(iter == PlaybackDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%s\" not found", name}; + "Device name \"%.*s\" not found", static_cast<int>(name.length()), name.data()}; devname = iter->device_name.c_str(); } @@ -447,10 +443,10 @@ struct OSScapture final : public BackendBase { int recordProc(); - void open(const char *name) override; + void open(std::string_view name) override; void start() override; void stop() override; - void captureSamples(al::byte *buffer, uint samples) override; + void captureSamples(std::byte *buffer, uint samples) override; uint availableSamples() override; int mFd{-1}; @@ -516,10 +512,10 @@ int OSScapture::recordProc() } -void OSScapture::open(const char *name) +void OSScapture::open(std::string_view name) { const char *devname{DefaultCapture.c_str()}; - if(!name) + if(name.empty()) name = DefaultName; else { @@ -532,7 +528,7 @@ void OSScapture::open(const char *name) ); if(iter == CaptureDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%s\" not found", name}; + "Device name \"%.*s\" not found", static_cast<int>(name.length()), name.data()}; devname = iter->device_name.c_str(); } @@ -620,7 +616,7 @@ void OSScapture::stop() ERR("Error resetting device: %s\n", strerror(errno)); } -void OSScapture::captureSamples(al::byte *buffer, uint samples) +void OSScapture::captureSamples(std::byte *buffer, uint samples) { mRing->read(buffer, samples); } uint OSScapture::availableSamples() diff --git a/alc/backends/pipewire.cpp b/alc/backends/pipewire.cpp index c6569a74..6a001d7a 100644 --- a/alc/backends/pipewire.cpp +++ b/alc/backends/pipewire.cpp @@ -24,6 +24,7 @@ #include <algorithm> #include <atomic> +#include <cstddef> #include <cstring> #include <cerrno> #include <chrono> @@ -31,16 +32,16 @@ #include <list> #include <memory> #include <mutex> +#include <optional> #include <stdint.h> #include <thread> #include <type_traits> #include <utility> -#include "albyte.h" +#include "albit.h" #include "alc/alconfig.h" #include "almalloc.h" #include "alnumeric.h" -#include "aloptional.h" #include "alspan.h" #include "alstring.h" #include "core/devformat.h" @@ -107,12 +108,12 @@ template<typename T, size_t N> constexpr auto get_pod_body(const spa_pod *pod) noexcept { return al::span<T,N>{static_cast<T*>(SPA_POD_BODY(pod)), N}; } -constexpr auto make_pod_builder(void *data, uint32_t size) noexcept -{ return SPA_POD_BUILDER_INIT(data, size); } - constexpr auto get_array_value_type(const spa_pod *pod) noexcept { return SPA_POD_ARRAY_VALUE_TYPE(pod); } +constexpr auto make_pod_builder(void *data, uint32_t size) noexcept +{ return SPA_POD_BUILDER_INIT(data, size); } + constexpr auto PwIdAny = PW_ID_ANY; } // namespace @@ -120,6 +121,44 @@ _Pragma("GCC diagnostic pop") namespace { +struct PodDynamicBuilder { +private: + std::vector<std::byte> mStorage; + spa_pod_builder mPod{}; + + int overflow(uint32_t size) noexcept + { + try { + mStorage.resize(size); + } + catch(...) { + ERR("Failed to resize POD storage\n"); + return -ENOMEM; + } + mPod.data = mStorage.data(); + mPod.size = size; + return 0; + } + +public: + PodDynamicBuilder(uint32_t initSize=0) : mStorage(initSize) + , mPod{make_pod_builder(mStorage.data(), initSize)} + { + static constexpr auto callbacks{[] + { + spa_pod_builder_callbacks cb{}; + cb.version = SPA_VERSION_POD_BUILDER_CALLBACKS; + cb.overflow = [](void *data, uint32_t size) noexcept + { return static_cast<PodDynamicBuilder*>(data)->overflow(size); }; + return cb; + }()}; + + spa_pod_builder_set_callbacks(&mPod, &callbacks, this); + } + + spa_pod_builder *get() noexcept { return &mPod; } +}; + /* Added in 0.3.33, but we currently only require 0.3.23. */ #ifndef PW_KEY_NODE_RATE #define PW_KEY_NODE_RATE "node.rate" @@ -210,7 +249,7 @@ bool pwire_load() } #define LOAD_FUNC(f) do { \ - p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(pwire_handle, #f)); \ + p##f = al::bit_cast<decltype(p##f)>(GetSymbol(pwire_handle, #f)); \ if(p##f == nullptr) missing_funcs += "\n" #f; \ } while(0); PWIRE_FUNCS(LOAD_FUNC) @@ -304,12 +343,12 @@ al::span<const Pod_t<T>> get_array_span(const spa_pod *pod) } template<uint32_t T> -al::optional<Pod_t<T>> get_value(const spa_pod *value) +std::optional<Pod_t<T>> get_value(const spa_pod *value) { Pod_t<T> val{}; if(PodInfo<T>::get_value(value, &val) == 0) return val; - return al::nullopt; + return std::nullopt; } /* Internally, PipeWire types "inherit" from each other, but this is hidden @@ -328,11 +367,11 @@ To as(From) noexcept = delete; * - pw_metadata */ template<> -pw_proxy* as(pw_registry *reg) noexcept { return reinterpret_cast<pw_proxy*>(reg); } +pw_proxy* as(pw_registry *reg) noexcept { return al::bit_cast<pw_proxy*>(reg); } template<> -pw_proxy* as(pw_node *node) noexcept { return reinterpret_cast<pw_proxy*>(node); } +pw_proxy* as(pw_node *node) noexcept { return al::bit_cast<pw_proxy*>(node); } template<> -pw_proxy* as(pw_metadata *mdata) noexcept { return reinterpret_cast<pw_proxy*>(mdata); } +pw_proxy* as(pw_metadata *mdata) noexcept { return al::bit_cast<pw_proxy*>(mdata); } struct PwContextDeleter { @@ -433,8 +472,75 @@ using MainloopLockGuard = std::lock_guard<ThreadMainloop>; * devices provided by the server. */ -struct NodeProxy; -struct MetadataProxy; +/* A generic PipeWire node proxy object used to track changes to sink and + * source nodes. + */ +struct NodeProxy { + static constexpr pw_node_events CreateNodeEvents() + { + pw_node_events ret{}; + ret.version = PW_VERSION_NODE_EVENTS; + ret.info = [](void *object, const pw_node_info *info) noexcept + { static_cast<NodeProxy*>(object)->infoCallback(info); }; + ret.param = [](void *object, int seq, uint32_t id, uint32_t index, uint32_t next, const spa_pod *param) noexcept + { static_cast<NodeProxy*>(object)->paramCallback(seq, id, index, next, param); }; + return ret; + } + + uint32_t mId{}; + + PwNodePtr mNode{}; + spa_hook mListener{}; + + NodeProxy(uint32_t id, PwNodePtr node) + : mId{id}, mNode{std::move(node)} + { + static constexpr pw_node_events nodeEvents{CreateNodeEvents()}; + ppw_node_add_listener(mNode.get(), &mListener, &nodeEvents, this); + + /* Track changes to the enumerable and current formats (indicates the + * default and active format, which is what we're interested in). + */ + uint32_t fmtids[]{SPA_PARAM_EnumFormat, SPA_PARAM_Format}; + ppw_node_subscribe_params(mNode.get(), std::data(fmtids), std::size(fmtids)); + } + ~NodeProxy() + { spa_hook_remove(&mListener); } + + + void infoCallback(const pw_node_info *info) noexcept; + + void paramCallback(int seq, uint32_t id, uint32_t index, uint32_t next, const spa_pod *param) noexcept; +}; + +/* A metadata proxy object used to query the default sink and source. */ +struct MetadataProxy { + static constexpr pw_metadata_events CreateMetadataEvents() + { + pw_metadata_events ret{}; + ret.version = PW_VERSION_METADATA_EVENTS; + ret.property = [](void *object, uint32_t id, const char *key, const char *type, const char *value) noexcept + { return static_cast<MetadataProxy*>(object)->propertyCallback(id, key, type, value); }; + return ret; + } + + uint32_t mId{}; + + PwMetadataPtr mMetadata{}; + spa_hook mListener{}; + + MetadataProxy(uint32_t id, PwMetadataPtr mdata) + : mId{id}, mMetadata{std::move(mdata)} + { + static constexpr pw_metadata_events metadataEvents{CreateMetadataEvents()}; + ppw_metadata_add_listener(mMetadata.get(), &mListener, &metadataEvents, this); + } + ~MetadataProxy() + { spa_hook_remove(&mListener); } + + int propertyCallback(uint32_t id, const char *key, const char *type, const char *value) noexcept; +}; + /* The global thread watching for global events. This particular class responds * to objects being added to or removed from the registry. @@ -450,8 +556,8 @@ struct EventManager { /* A list of proxy objects watching for events about changes to objects in * the registry. */ - std::vector<NodeProxy*> mNodeList; - MetadataProxy *mDefaultMetadata{nullptr}; + std::vector<std::unique_ptr<NodeProxy>> mNodeList; + std::optional<MetadataProxy> mDefaultMetadata; /* Initialization handling. When init() is called, mInitSeq is set to a * SequenceID that marks the end of populating the registry. As objects of @@ -463,24 +569,28 @@ struct EventManager { std::atomic<bool> mHasAudio{false}; int mInitSeq{}; + ~EventManager() { if(mLoop) mLoop.stop(); } + bool init(); - ~EventManager(); void kill(); auto lock() const { return mLoop.lock(); } auto unlock() const { return mLoop.unlock(); } + inline bool initIsDone(std::memory_order m=std::memory_order_seq_cst) noexcept + { return mInitDone.load(m); } + /** * Waits for initialization to finish. The event manager must *NOT* be * locked when calling this. */ void waitForInit() { - if(!mInitDone.load(std::memory_order_acquire)) UNLIKELY + if(!initIsDone(std::memory_order_acquire)) UNLIKELY { MainloopUniqueLock plock{mLoop}; - plock.wait([this](){ return mInitDone.load(std::memory_order_acquire); }); + plock.wait([this](){ return initIsDone(std::memory_order_acquire); }); } } @@ -496,7 +606,7 @@ struct EventManager { plock.wait([this,&has_audio]() { has_audio = mHasAudio.load(std::memory_order_acquire); - return has_audio || mInitDone.load(std::memory_order_acquire); + return has_audio || initIsDone(std::memory_order_acquire); }); return has_audio; } @@ -506,38 +616,34 @@ struct EventManager { /* If initialization isn't done, update the sequence ID so it won't * complete until after currently scheduled events. */ - if(!mInitDone.load(std::memory_order_relaxed)) + if(!initIsDone(std::memory_order_relaxed)) mInitSeq = ppw_core_sync(mCore.get(), PW_ID_CORE, mInitSeq); } void addCallback(uint32_t id, uint32_t permissions, const char *type, uint32_t version, - const spa_dict *props); - static void addCallbackC(void *object, uint32_t id, uint32_t permissions, const char *type, - uint32_t version, const spa_dict *props) - { static_cast<EventManager*>(object)->addCallback(id, permissions, type, version, props); } + const spa_dict *props) noexcept; - void removeCallback(uint32_t id); - static void removeCallbackC(void *object, uint32_t id) - { static_cast<EventManager*>(object)->removeCallback(id); } + void removeCallback(uint32_t id) noexcept; static constexpr pw_registry_events CreateRegistryEvents() { pw_registry_events ret{}; ret.version = PW_VERSION_REGISTRY_EVENTS; - ret.global = &EventManager::addCallbackC; - ret.global_remove = &EventManager::removeCallbackC; + ret.global = [](void *object, uint32_t id, uint32_t permissions, const char *type, uint32_t version, const spa_dict *props) noexcept + { static_cast<EventManager*>(object)->addCallback(id, permissions, type, version, props); }; + ret.global_remove = [](void *object, uint32_t id) noexcept + { static_cast<EventManager*>(object)->removeCallback(id); }; return ret; } - void coreCallback(uint32_t id, int seq); - static void coreCallbackC(void *object, uint32_t id, int seq) - { static_cast<EventManager*>(object)->coreCallback(id, seq); } + void coreCallback(uint32_t id, int seq) noexcept; static constexpr pw_core_events CreateCoreEvents() { pw_core_events ret{}; ret.version = PW_VERSION_CORE_EVENTS; - ret.done = &EventManager::coreCallbackC; + ret.done = [](void *object, uint32_t id, int seq) noexcept + { static_cast<EventManager*>(object)->coreCallback(id, seq); }; return ret; } }; @@ -570,12 +676,23 @@ struct DeviceNode { static std::vector<DeviceNode> sList; static DeviceNode &Add(uint32_t id); static DeviceNode *Find(uint32_t id); + static DeviceNode *FindByDevName(std::string_view devname); static void Remove(uint32_t id); - static std::vector<DeviceNode> &GetList() noexcept { return sList; } + static auto GetList() noexcept { return al::span{sList}; } + + void parseSampleRate(const spa_pod *value, bool force_update) noexcept; + void parsePositions(const spa_pod *value, bool force_update) noexcept; + void parseChannelCount(const spa_pod *value, bool force_update) noexcept; - void parseSampleRate(const spa_pod *value) noexcept; - void parsePositions(const spa_pod *value) noexcept; - void parseChannelCount(const spa_pod *value) noexcept; + void callEvent(alc::EventType type, std::string_view message) + { + /* Source nodes aren't recognized for playback, only Sink and Duplex + * nodes are. All node types are recognized for capture. + */ + if(mType != NodeType::Source) + alc::Event(type, alc::DeviceType::Playback, message); + alc::Event(type, alc::DeviceType::Capture, message); + } }; std::vector<DeviceNode> DeviceNode::sList; std::string DefaultSinkDevice; @@ -601,8 +718,7 @@ DeviceNode &DeviceNode::Add(uint32_t id) auto match = std::find_if(sList.begin(), sList.end(), match_id); if(match != sList.end()) return *match; - sList.emplace_back(); - auto &n = sList.back(); + auto &n = sList.emplace_back(); n.mId = id; return n; } @@ -618,6 +734,17 @@ DeviceNode *DeviceNode::Find(uint32_t id) return nullptr; } +DeviceNode *DeviceNode::FindByDevName(std::string_view devname) +{ + auto match_id = [devname](DeviceNode &n) noexcept -> bool + { return n.mDevName == devname; }; + + auto match = std::find_if(sList.begin(), sList.end(), match_id); + if(match != sList.end()) return al::to_address(match); + + return nullptr; +} + void DeviceNode::Remove(uint32_t id) { auto match_id = [id](DeviceNode &n) noexcept -> bool @@ -625,6 +752,11 @@ void DeviceNode::Remove(uint32_t id) if(n.mId != id) return false; TRACE("Removing device \"%s\"\n", n.mDevName.c_str()); + if(gEventHandler.initIsDone(std::memory_order_relaxed)) + { + const std::string msg{"Device removed: "+n.mName}; + n.callEvent(alc::EventType::DeviceRemoved, msg); + } return true; }; @@ -674,7 +806,7 @@ bool MatchChannelMap(const al::span<const uint32_t> map0, const spa_audio_channe return true; } -void DeviceNode::parseSampleRate(const spa_pod *value) noexcept +void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexcept { /* TODO: Can this be anything else? Long, Float, Double? */ uint32_t nvals{}, choiceType{}; @@ -683,7 +815,7 @@ void DeviceNode::parseSampleRate(const spa_pod *value) noexcept const uint podType{get_pod_type(value)}; if(podType != SPA_TYPE_Int) { - WARN("Unhandled sample rate POD type: %u\n", podType); + WARN(" Unhandled sample rate POD type: %u\n", podType); return; } @@ -691,15 +823,15 @@ void DeviceNode::parseSampleRate(const spa_pod *value) noexcept { if(nvals != 3) { - WARN("Unexpected SPA_CHOICE_Range count: %u\n", nvals); + WARN(" Unexpected SPA_CHOICE_Range count: %u\n", nvals); return; } auto srates = get_pod_body<int32_t,3>(value); /* [0] is the default, [1] is the min, and [2] is the max. */ - TRACE("Device ID %" PRIu64 " sample rate: %d (range: %d -> %d)\n", mSerial, srates[0], - srates[1], srates[2]); - mSampleRate = static_cast<uint>(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE)); + TRACE(" sample rate: %d (range: %d -> %d)\n", srates[0], srates[1], srates[2]); + if(!mSampleRate || force_update) + mSampleRate = static_cast<uint>(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE)); return; } @@ -707,7 +839,7 @@ void DeviceNode::parseSampleRate(const spa_pod *value) noexcept { if(nvals == 0) { - WARN("Unexpected SPA_CHOICE_Enum count: %u\n", nvals); + WARN(" Unexpected SPA_CHOICE_Enum count: %u\n", nvals); return; } auto srates = get_pod_body<int32_t>(value, nvals); @@ -719,7 +851,7 @@ void DeviceNode::parseSampleRate(const spa_pod *value) noexcept others += ", "; others += std::to_string(srates[i]); } - TRACE("Device ID %" PRIu64 " sample rate: %d (%s)\n", mSerial, srates[0], others.c_str()); + TRACE(" sample rate: %d (%s)\n", srates[0], others.c_str()); /* Pick the first rate listed that's within the allowed range (default * rate if possible). */ @@ -727,7 +859,8 @@ void DeviceNode::parseSampleRate(const spa_pod *value) noexcept { if(rate >= MIN_OUTPUT_RATE && rate <= MAX_OUTPUT_RATE) { - mSampleRate = static_cast<uint>(rate); + if(!mSampleRate || force_update) + mSampleRate = static_cast<uint>(rate); break; } } @@ -738,119 +871,100 @@ void DeviceNode::parseSampleRate(const spa_pod *value) noexcept { if(nvals != 1) { - WARN("Unexpected SPA_CHOICE_None count: %u\n", nvals); + WARN(" Unexpected SPA_CHOICE_None count: %u\n", nvals); return; } auto srates = get_pod_body<int32_t,1>(value); - TRACE("Device ID %" PRIu64 " sample rate: %d\n", mSerial, srates[0]); - mSampleRate = static_cast<uint>(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE)); + TRACE(" sample rate: %d\n", srates[0]); + if(!mSampleRate || force_update) + mSampleRate = static_cast<uint>(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE)); return; } - WARN("Unhandled sample rate choice type: %u\n", choiceType); + WARN(" Unhandled sample rate choice type: %u\n", choiceType); } -void DeviceNode::parsePositions(const spa_pod *value) noexcept +void DeviceNode::parsePositions(const spa_pod *value, bool force_update) noexcept { + uint32_t choiceCount{}, choiceType{}; + value = spa_pod_get_values(value, &choiceCount, &choiceType); + + if(choiceType != SPA_CHOICE_None || choiceCount != 1) + { + ERR(" Unexpected positions choice: type=%u, count=%u\n", choiceType, choiceCount); + return; + } + const auto chanmap = get_array_span<SPA_TYPE_Id>(value); if(chanmap.empty()) return; - mIs51Rear = false; - - if(MatchChannelMap(chanmap, X714Map)) - mChannels = DevFmtX714; - else if(MatchChannelMap(chanmap, X71Map)) - mChannels = DevFmtX71; - else if(MatchChannelMap(chanmap, X61Map)) - mChannels = DevFmtX61; - else if(MatchChannelMap(chanmap, X51Map)) - mChannels = DevFmtX51; - else if(MatchChannelMap(chanmap, X51RearMap)) - { - mChannels = DevFmtX51; - mIs51Rear = true; - } - else if(MatchChannelMap(chanmap, QuadMap)) - mChannels = DevFmtQuad; - else if(MatchChannelMap(chanmap, StereoMap)) - mChannels = DevFmtStereo; - else - mChannels = DevFmtMono; - TRACE("Device ID %" PRIu64 " got %zu position%s for %s%s\n", mSerial, chanmap.size(), - (chanmap.size()==1)?"":"s", DevFmtChannelsString(mChannels), mIs51Rear?"(rear)":""); + if(mChannels == InvalidChannelConfig || force_update) + { + mIs51Rear = false; + + if(MatchChannelMap(chanmap, X714Map)) + mChannels = DevFmtX714; + else if(MatchChannelMap(chanmap, X71Map)) + mChannels = DevFmtX71; + else if(MatchChannelMap(chanmap, X61Map)) + mChannels = DevFmtX61; + else if(MatchChannelMap(chanmap, X51Map)) + mChannels = DevFmtX51; + else if(MatchChannelMap(chanmap, X51RearMap)) + { + mChannels = DevFmtX51; + mIs51Rear = true; + } + else if(MatchChannelMap(chanmap, QuadMap)) + mChannels = DevFmtQuad; + else if(MatchChannelMap(chanmap, StereoMap)) + mChannels = DevFmtStereo; + else + mChannels = DevFmtMono; + } + TRACE(" %zu position%s for %s%s\n", chanmap.size(), (chanmap.size()==1)?"":"s", + DevFmtChannelsString(mChannels), mIs51Rear?"(rear)":""); } -void DeviceNode::parseChannelCount(const spa_pod *value) noexcept +void DeviceNode::parseChannelCount(const spa_pod *value, bool force_update) noexcept { /* As a fallback with just a channel count, just assume mono or stereo. */ + uint32_t choiceCount{}, choiceType{}; + value = spa_pod_get_values(value, &choiceCount, &choiceType); + + if(choiceType != SPA_CHOICE_None || choiceCount != 1) + { + ERR(" Unexpected positions choice: type=%u, count=%u\n", choiceType, choiceCount); + return; + } + const auto chancount = get_value<SPA_TYPE_Int>(value); if(!chancount) return; - mIs51Rear = false; + if(mChannels == InvalidChannelConfig || force_update) + { + mIs51Rear = false; - if(*chancount >= 2) - mChannels = DevFmtStereo; - else if(*chancount >= 1) - mChannels = DevFmtMono; - TRACE("Device ID %" PRIu64 " got %d channel%s for %s\n", mSerial, *chancount, - (*chancount==1)?"":"s", DevFmtChannelsString(mChannels)); + if(*chancount >= 2) + mChannels = DevFmtStereo; + else if(*chancount >= 1) + mChannels = DevFmtMono; + } + TRACE(" %d channel%s for %s\n", *chancount, (*chancount==1)?"":"s", + DevFmtChannelsString(mChannels)); } constexpr char MonitorPrefix[]{"Monitor of "}; -constexpr auto MonitorPrefixLen = al::size(MonitorPrefix) - 1; +constexpr auto MonitorPrefixLen = std::size(MonitorPrefix) - 1; constexpr char AudioSinkClass[]{"Audio/Sink"}; constexpr char AudioSourceClass[]{"Audio/Source"}; constexpr char AudioSourceVirtualClass[]{"Audio/Source/Virtual"}; constexpr char AudioDuplexClass[]{"Audio/Duplex"}; constexpr char StreamClass[]{"Stream/"}; -/* A generic PipeWire node proxy object used to track changes to sink and - * source nodes. - */ -struct NodeProxy { - static constexpr pw_node_events CreateNodeEvents() - { - pw_node_events ret{}; - ret.version = PW_VERSION_NODE_EVENTS; - ret.info = &NodeProxy::infoCallbackC; - ret.param = &NodeProxy::paramCallbackC; - return ret; - } - - uint32_t mId{}; - - PwNodePtr mNode{}; - spa_hook mListener{}; - - NodeProxy(uint32_t id, PwNodePtr node) - : mId{id}, mNode{std::move(node)} - { - static constexpr pw_node_events nodeEvents{CreateNodeEvents()}; - ppw_node_add_listener(mNode.get(), &mListener, &nodeEvents, this); - - /* Track changes to the enumerable formats (indicates the default - * format, which is what we're interested in). - */ - uint32_t fmtids[]{SPA_PARAM_EnumFormat}; - ppw_node_subscribe_params(mNode.get(), al::data(fmtids), al::size(fmtids)); - } - ~NodeProxy() - { spa_hook_remove(&mListener); } - - - void infoCallback(const pw_node_info *info); - static void infoCallbackC(void *object, const pw_node_info *info) - { static_cast<NodeProxy*>(object)->infoCallback(info); } - - void paramCallback(int seq, uint32_t id, uint32_t index, uint32_t next, const spa_pod *param); - static void paramCallbackC(void *object, int seq, uint32_t id, uint32_t index, uint32_t next, - const spa_pod *param) - { static_cast<NodeProxy*>(object)->paramCallback(seq, id, index, next, param); } -}; - -void NodeProxy::infoCallback(const pw_node_info *info) +void NodeProxy::infoCallback(const pw_node_info *info) noexcept { /* We only care about property changes here (media class, name/desc). * Format changes will automatically invoke the param callback. @@ -888,6 +1002,7 @@ void NodeProxy::infoCallback(const pw_node_info *info) #ifdef PW_KEY_OBJECT_SERIAL if(const char *serial_str{spa_dict_lookup(info->props, PW_KEY_OBJECT_SERIAL)}) { + errno = 0; char *serial_end{}; serial_id = std::strtoull(serial_str, &serial_end, 0); if(*serial_end != '\0' || errno == ERANGE) @@ -898,15 +1013,40 @@ void NodeProxy::infoCallback(const pw_node_info *info) } #endif + std::string name; + if(nodeName && *nodeName) name = nodeName; + else name = "PipeWire node #"+std::to_string(info->id); + const char *form_factor{spa_dict_lookup(info->props, PW_KEY_DEVICE_FORM_FACTOR)}; TRACE("Got %s device \"%s\"%s%s%s\n", AsString(ntype), devName ? devName : "(nil)", form_factor?" (":"", form_factor?form_factor:"", form_factor?")":""); - TRACE(" \"%s\" = ID %" PRIu64 "\n", nodeName ? nodeName : "(nil)", serial_id); + TRACE(" \"%s\" = ID %" PRIu64 "\n", name.c_str(), serial_id); DeviceNode &node = DeviceNode::Add(info->id); node.mSerial = serial_id; - if(nodeName && *nodeName) node.mName = nodeName; - else node.mName = "PipeWire node #"+std::to_string(info->id); + /* This method is called both to notify about a new sink/source node, + * and update properties for the node. It's unclear what properties can + * change for an existing node without being removed first, so err on + * the side of caution: send a DeviceAdded event when the name differs, + * and send a DeviceRemoved event if it had a name that's being + * replaced. + * + * This is overkill if the name or devname can't change. + */ + if(node.mName != name) + { + if(gEventHandler.initIsDone(std::memory_order_relaxed)) + { + if(!node.mName.empty()) + { + const std::string msg{"Device removed: "+node.mName}; + node.callEvent(alc::EventType::DeviceRemoved, msg); + } + const std::string msg{"Device added: "+name}; + node.callEvent(alc::EventType::DeviceAdded, msg); + } + node.mName = std::move(name); + } node.mDevName = devName ? devName : ""; node.mType = ntype; node.mIsHeadphones = form_factor && (al::strcasecmp(form_factor, "headphones") == 0 @@ -914,57 +1054,30 @@ void NodeProxy::infoCallback(const pw_node_info *info) } } -void NodeProxy::paramCallback(int, uint32_t id, uint32_t, uint32_t, const spa_pod *param) +void NodeProxy::paramCallback(int, uint32_t id, uint32_t, uint32_t, const spa_pod *param) noexcept { - if(id == SPA_PARAM_EnumFormat) + if(id == SPA_PARAM_EnumFormat || id == SPA_PARAM_Format) { DeviceNode *node{DeviceNode::Find(mId)}; if(!node) UNLIKELY return; + TRACE("Device ID %" PRIu64 " %s format:\n", node->mSerial, + (id == SPA_PARAM_EnumFormat) ? "enumerable" : "current"); + + const bool force_update{id == SPA_PARAM_Format}; if(const spa_pod_prop *prop{spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_rate)}) - node->parseSampleRate(&prop->value); + node->parseSampleRate(&prop->value, force_update); if(const spa_pod_prop *prop{spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_position)}) - node->parsePositions(&prop->value); + node->parsePositions(&prop->value, force_update); else if((prop=spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_channels)) != nullptr) - node->parseChannelCount(&prop->value); + node->parseChannelCount(&prop->value, force_update); } } -/* A metadata proxy object used to query the default sink and source. */ -struct MetadataProxy { - static constexpr pw_metadata_events CreateMetadataEvents() - { - pw_metadata_events ret{}; - ret.version = PW_VERSION_METADATA_EVENTS; - ret.property = &MetadataProxy::propertyCallbackC; - return ret; - } - - uint32_t mId{}; - - PwMetadataPtr mMetadata{}; - spa_hook mListener{}; - - MetadataProxy(uint32_t id, PwMetadataPtr mdata) - : mId{id}, mMetadata{std::move(mdata)} - { - static constexpr pw_metadata_events metadataEvents{CreateMetadataEvents()}; - ppw_metadata_add_listener(mMetadata.get(), &mListener, &metadataEvents, this); - } - ~MetadataProxy() - { spa_hook_remove(&mListener); } - - - int propertyCallback(uint32_t id, const char *key, const char *type, const char *value); - static int propertyCallbackC(void *object, uint32_t id, const char *key, const char *type, - const char *value) - { return static_cast<MetadataProxy*>(object)->propertyCallback(id, key, type, value); } -}; - int MetadataProxy::propertyCallback(uint32_t id, const char *key, const char *type, - const char *value) + const char *value) noexcept { if(id != PW_ID_CORE) return 0; @@ -997,7 +1110,7 @@ int MetadataProxy::propertyCallback(uint32_t id, const char *key, const char *ty auto get_json_string = [](spa_json *iter) { - al::optional<std::string> str; + std::optional<std::string> str; const char *val{}; int len{spa_json_next(iter, &val)}; @@ -1020,9 +1133,35 @@ int MetadataProxy::propertyCallback(uint32_t id, const char *key, const char *ty TRACE("Got default %s device \"%s\"\n", isCapture ? "capture" : "playback", propValue->c_str()); if(!isCapture) - DefaultSinkDevice = std::move(*propValue); + { + if(DefaultSinkDevice != *propValue) + { + if(gEventHandler.mInitDone.load(std::memory_order_relaxed)) + { + auto entry = DeviceNode::FindByDevName(*propValue); + const std::string msg{"Default playback device changed: "+ + (entry ? entry->mName : std::string{})}; + alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback, + msg); + } + DefaultSinkDevice = std::move(*propValue); + } + } else - DefaultSourceDevice = std::move(*propValue); + { + if(DefaultSourceDevice != *propValue) + { + if(gEventHandler.mInitDone.load(std::memory_order_relaxed)) + { + auto entry = DeviceNode::FindByDevName(*propValue); + const std::string msg{"Default capture device changed: "+ + (entry ? entry->mName : std::string{})}; + alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture, + msg); + } + DefaultSourceDevice = std::move(*propValue); + } + } } else { @@ -1044,7 +1183,7 @@ bool EventManager::init() return false; } - mContext = mLoop.newContext(pw_properties_new(PW_KEY_CONFIG_NAME, "client-rt.conf", nullptr)); + mContext = mLoop.newContext(); if(!mContext) { ERR("Failed to create PipeWire event context (errno: %d)\n", errno); @@ -1085,26 +1224,12 @@ bool EventManager::init() return true; } -EventManager::~EventManager() -{ - if(mLoop) mLoop.stop(); - - for(NodeProxy *node : mNodeList) - al::destroy_at(node); - if(mDefaultMetadata) - al::destroy_at(mDefaultMetadata); -} - void EventManager::kill() { if(mLoop) mLoop.stop(); - for(NodeProxy *node : mNodeList) - al::destroy_at(node); + mDefaultMetadata.reset(); mNodeList.clear(); - if(mDefaultMetadata) - al::destroy_at(mDefaultMetadata); - mDefaultMetadata = nullptr; mRegistry = nullptr; mCore = nullptr; @@ -1113,7 +1238,7 @@ void EventManager::kill() } void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t version, - const spa_dict *props) + const spa_dict *props) noexcept { /* We're only interested in interface nodes. */ if(std::strcmp(type, PW_TYPE_INTERFACE_Node) == 0) @@ -1136,7 +1261,7 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t /* Create the proxy object. */ auto node = PwNodePtr{static_cast<pw_node*>(pw_registry_bind(mRegistry.get(), id, type, - version, sizeof(NodeProxy)))}; + version, 0))}; if(!node) { ERR("Failed to create node proxy object (errno: %d)\n", errno); @@ -1146,8 +1271,7 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t /* Initialize the NodeProxy to hold the node object, add it to the * active node list, and update the sync point. */ - auto *proxy = static_cast<NodeProxy*>(pw_proxy_get_user_data(as<pw_proxy*>(node.get()))); - mNodeList.emplace_back(al::construct_at(proxy, id, std::move(node))); + mNodeList.emplace_back(std::make_unique<NodeProxy>(id, std::move(node))); syncInit(); /* Signal any waiters that we have found a source or sink for audio @@ -1174,42 +1298,32 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t } auto mdata = PwMetadataPtr{static_cast<pw_metadata*>(pw_registry_bind(mRegistry.get(), id, - type, version, sizeof(MetadataProxy)))}; + type, version, 0))}; if(!mdata) { ERR("Failed to create metadata proxy object (errno: %d)\n", errno); return; } - auto *proxy = static_cast<MetadataProxy*>( - pw_proxy_get_user_data(as<pw_proxy*>(mdata.get()))); - mDefaultMetadata = al::construct_at(proxy, id, std::move(mdata)); + mDefaultMetadata.emplace(id, std::move(mdata)); syncInit(); } } -void EventManager::removeCallback(uint32_t id) +void EventManager::removeCallback(uint32_t id) noexcept { DeviceNode::Remove(id); - auto clear_node = [id](NodeProxy *node) noexcept - { - if(node->mId != id) - return false; - al::destroy_at(node); - return true; - }; + auto clear_node = [id](std::unique_ptr<NodeProxy> &node) noexcept + { return node->mId == id; }; auto node_end = std::remove_if(mNodeList.begin(), mNodeList.end(), clear_node); mNodeList.erase(node_end, mNodeList.end()); if(mDefaultMetadata && mDefaultMetadata->mId == id) - { - al::destroy_at(mDefaultMetadata); - mDefaultMetadata = nullptr; - } + mDefaultMetadata.reset(); } -void EventManager::coreCallback(uint32_t id, int seq) +void EventManager::coreCallback(uint32_t id, int seq) noexcept { if(id == PW_ID_CORE && seq == mInitSeq) { @@ -1275,20 +1389,11 @@ spa_audio_info_raw make_spa_info(DeviceBase *device, bool is51rear, use_f32p_e u } class PipeWirePlayback final : public BackendBase { - void stateChangedCallback(pw_stream_state old, pw_stream_state state, const char *error); - static void stateChangedCallbackC(void *data, pw_stream_state old, pw_stream_state state, - const char *error) - { static_cast<PipeWirePlayback*>(data)->stateChangedCallback(old, state, error); } - - void ioChangedCallback(uint32_t id, void *area, uint32_t size); - static void ioChangedCallbackC(void *data, uint32_t id, void *area, uint32_t size) - { static_cast<PipeWirePlayback*>(data)->ioChangedCallback(id, area, size); } - - void outputCallback(); - static void outputCallbackC(void *data) - { static_cast<PipeWirePlayback*>(data)->outputCallback(); } + void stateChangedCallback(pw_stream_state old, pw_stream_state state, const char *error) noexcept; + void ioChangedCallback(uint32_t id, void *area, uint32_t size) noexcept; + void outputCallback() noexcept; - void open(const char *name) override; + void open(std::string_view name) override; bool reset() override; void start() override; void stop() override; @@ -1309,9 +1414,12 @@ class PipeWirePlayback final : public BackendBase { { pw_stream_events ret{}; ret.version = PW_VERSION_STREAM_EVENTS; - ret.state_changed = &PipeWirePlayback::stateChangedCallbackC; - ret.io_changed = &PipeWirePlayback::ioChangedCallbackC; - ret.process = &PipeWirePlayback::outputCallbackC; + ret.state_changed = [](void *data, pw_stream_state old, pw_stream_state state, const char *error) noexcept + { static_cast<PipeWirePlayback*>(data)->stateChangedCallback(old, state, error); }; + ret.io_changed = [](void *data, uint32_t id, void *area, uint32_t size) noexcept + { static_cast<PipeWirePlayback*>(data)->ioChangedCallback(id, area, size); }; + ret.process = [](void *data) noexcept + { static_cast<PipeWirePlayback*>(data)->outputCallback(); }; return ret; } @@ -1327,10 +1435,10 @@ public: }; -void PipeWirePlayback::stateChangedCallback(pw_stream_state, pw_stream_state, const char*) +void PipeWirePlayback::stateChangedCallback(pw_stream_state, pw_stream_state, const char*) noexcept { mLoop.signal(false); } -void PipeWirePlayback::ioChangedCallback(uint32_t id, void *area, uint32_t size) +void PipeWirePlayback::ioChangedCallback(uint32_t id, void *area, uint32_t size) noexcept { switch(id) { @@ -1341,7 +1449,7 @@ void PipeWirePlayback::ioChangedCallback(uint32_t id, void *area, uint32_t size) } } -void PipeWirePlayback::outputCallback() +void PipeWirePlayback::outputCallback() noexcept { pw_buffer *pw_buf{pw_stream_dequeue_buffer(mStream.get())}; if(!pw_buf) UNLIKELY return; @@ -1373,29 +1481,27 @@ void PipeWirePlayback::outputCallback() length = minu(length, data.maxsize/sizeof(float)); *chanptr_end = static_cast<float*>(data.data); ++chanptr_end; - } - - mDevice->renderSamples({mChannelPtrs.get(), chanptr_end}, length); - for(const auto &data : datas) - { data.chunk->offset = 0; data.chunk->stride = sizeof(float); data.chunk->size = length * sizeof(float); } + + mDevice->renderSamples({mChannelPtrs.get(), chanptr_end}, length); + pw_buf->size = length; pw_stream_queue_buffer(mStream.get(), pw_buf); } -void PipeWirePlayback::open(const char *name) +void PipeWirePlayback::open(std::string_view name) { static std::atomic<uint> OpenCount{0}; uint64_t targetid{PwIdAny}; std::string devname{}; gEventHandler.waitForInit(); - if(!name) + if(name.empty()) { EventWatcherLockGuard _{gEventHandler}; auto&& devlist = DeviceNode::GetList(); @@ -1430,7 +1536,7 @@ void PipeWirePlayback::open(const char *name) auto match = std::find_if(devlist.cbegin(), devlist.cend(), match_name); if(match == devlist.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%s\" not found", name}; + "Device name \"%.*s\" not found", static_cast<int>(name.length()), name.data()}; targetid = match->mSerial; devname = match->mName; @@ -1523,14 +1629,10 @@ bool PipeWirePlayback::reset() */ spa_audio_info_raw info{make_spa_info(mDevice, is51rear, ForceF32Planar)}; - /* TODO: How to tell what an appropriate size is? Examples just use this - * magic value. - */ - constexpr uint32_t pod_buffer_size{1024}; - auto pod_buffer = std::make_unique<al::byte[]>(pod_buffer_size); - spa_pod_builder b{make_pod_builder(pod_buffer.get(), pod_buffer_size)}; + static constexpr uint32_t pod_buffer_size{1024}; + PodDynamicBuilder b(pod_buffer_size); - const spa_pod *params{spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info)}; + const spa_pod *params{spa_format_audio_raw_build(b.get(), SPA_PARAM_EnumFormat, &info)}; if(!params) throw al::backend_exception{al::backend_error::DeviceError, "Failed to set PipeWire audio format parameters"}; @@ -1674,7 +1776,10 @@ void PipeWirePlayback::start() } #endif if(!--wait_count) + { + ERR("Timeout getting PipeWire stream buffering info\n"); break; + } plock.unlock(); std::this_thread::sleep_for(milliseconds{20}); @@ -1769,7 +1874,7 @@ ClockLatency PipeWirePlayback::getClockLatency() delay -= monoclock - nanoseconds{ptime.now}; /* Return the mixer time and delay. Clamp the delay to no less than 0, - * incase timer drift got that severe. + * in case timer drift got that severe. */ ClockLatency ret{}; ret.ClockTime = mixtime; @@ -1780,19 +1885,13 @@ ClockLatency PipeWirePlayback::getClockLatency() class PipeWireCapture final : public BackendBase { - void stateChangedCallback(pw_stream_state old, pw_stream_state state, const char *error); - static void stateChangedCallbackC(void *data, pw_stream_state old, pw_stream_state state, - const char *error) - { static_cast<PipeWireCapture*>(data)->stateChangedCallback(old, state, error); } - - void inputCallback(); - static void inputCallbackC(void *data) - { static_cast<PipeWireCapture*>(data)->inputCallback(); } + void stateChangedCallback(pw_stream_state old, pw_stream_state state, const char *error) noexcept; + void inputCallback() noexcept; - void open(const char *name) override; + void open(std::string_view name) override; void start() override; void stop() override; - void captureSamples(al::byte *buffer, uint samples) override; + void captureSamples(std::byte *buffer, uint samples) override; uint availableSamples() override; uint64_t mTargetId{PwIdAny}; @@ -1808,8 +1907,10 @@ class PipeWireCapture final : public BackendBase { { pw_stream_events ret{}; ret.version = PW_VERSION_STREAM_EVENTS; - ret.state_changed = &PipeWireCapture::stateChangedCallbackC; - ret.process = &PipeWireCapture::inputCallbackC; + ret.state_changed = [](void *data, pw_stream_state old, pw_stream_state state, const char *error) noexcept + { static_cast<PipeWireCapture*>(data)->stateChangedCallback(old, state, error); }; + ret.process = [](void *data) noexcept + { static_cast<PipeWireCapture*>(data)->inputCallback(); }; return ret; } @@ -1821,10 +1922,10 @@ public: }; -void PipeWireCapture::stateChangedCallback(pw_stream_state, pw_stream_state, const char*) +void PipeWireCapture::stateChangedCallback(pw_stream_state, pw_stream_state, const char*) noexcept { mLoop.signal(false); } -void PipeWireCapture::inputCallback() +void PipeWireCapture::inputCallback() noexcept { pw_buffer *pw_buf{pw_stream_dequeue_buffer(mStream.get())}; if(!pw_buf) UNLIKELY return; @@ -1839,14 +1940,14 @@ void PipeWireCapture::inputCallback() } -void PipeWireCapture::open(const char *name) +void PipeWireCapture::open(std::string_view name) { static std::atomic<uint> OpenCount{0}; uint64_t targetid{PwIdAny}; std::string devname{}; gEventHandler.waitForInit(); - if(!name) + if(name.empty()) { EventWatcherLockGuard _{gEventHandler}; auto&& devlist = DeviceNode::GetList(); @@ -1884,16 +1985,17 @@ void PipeWireCapture::open(const char *name) auto match_name = [name](const DeviceNode &n) -> bool { return n.mType != NodeType::Sink && n.mName == name; }; auto match = std::find_if(devlist.cbegin(), devlist.cend(), match_name); - if(match == devlist.cend() && std::strncmp(name, MonitorPrefix, MonitorPrefixLen) == 0) + if(match == devlist.cend() && name.length() >= MonitorPrefixLen + && std::strncmp(name.data(), MonitorPrefix, MonitorPrefixLen) == 0) { - const char *sinkname{name + MonitorPrefixLen}; + const std::string_view sinkname{name.substr(MonitorPrefixLen)}; auto match_sinkname = [sinkname](const DeviceNode &n) -> bool { return n.mType == NodeType::Sink && n.mName == sinkname; }; match = std::find_if(devlist.cbegin(), devlist.cend(), match_sinkname); } if(match == devlist.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%s\" not found", name}; + "Device name \"%.*s\" not found", static_cast<int>(name.length()), name.data()}; targetid = match->mSerial; devname = name; @@ -1952,11 +2054,10 @@ void PipeWireCapture::open(const char *name) } spa_audio_info_raw info{make_spa_info(mDevice, is51rear, UseDevType)}; - constexpr uint32_t pod_buffer_size{1024}; - auto pod_buffer = std::make_unique<al::byte[]>(pod_buffer_size); - spa_pod_builder b{make_pod_builder(pod_buffer.get(), pod_buffer_size)}; + static constexpr uint32_t pod_buffer_size{1024}; + PodDynamicBuilder b(pod_buffer_size); - const spa_pod *params[]{spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info)}; + const spa_pod *params[]{spa_format_audio_raw_build(b.get(), SPA_PARAM_EnumFormat, &info)}; if(!params[0]) throw al::backend_exception{al::backend_error::DeviceError, "Failed to set PipeWire audio format parameters"}; @@ -2054,7 +2155,7 @@ void PipeWireCapture::stop() uint PipeWireCapture::availableSamples() { return static_cast<uint>(mRing->readSpace()); } -void PipeWireCapture::captureSamples(al::byte *buffer, uint samples) +void PipeWireCapture::captureSamples(std::byte *buffer, uint samples) { mRing->read(buffer, samples); } } // namespace @@ -2164,3 +2265,18 @@ BackendFactory &PipeWireBackendFactory::getFactory() static PipeWireBackendFactory factory{}; return factory; } + +alc::EventSupport PipeWireBackendFactory::queryEventSupport(alc::EventType eventType, BackendType) +{ + switch(eventType) + { + case alc::EventType::DefaultDeviceChanged: + case alc::EventType::DeviceAdded: + case alc::EventType::DeviceRemoved: + return alc::EventSupport::FullSupport; + + case alc::EventType::Count: + break; + } + return alc::EventSupport::NoSupport; +} diff --git a/alc/backends/pipewire.h b/alc/backends/pipewire.h index 5f930239..5493684f 100644 --- a/alc/backends/pipewire.h +++ b/alc/backends/pipewire.h @@ -13,6 +13,8 @@ public: bool querySupport(BackendType type) override; + alc::EventSupport queryEventSupport(alc::EventType eventType, BackendType type) override; + std::string probe(BackendType type) override; BackendPtr createBackend(DeviceBase *device, BackendType type) override; diff --git a/alc/backends/portaudio.cpp b/alc/backends/portaudio.cpp index 9c94587d..979a54d6 100644 --- a/alc/backends/portaudio.cpp +++ b/alc/backends/portaudio.cpp @@ -26,6 +26,7 @@ #include <cstdlib> #include <cstring> +#include "albit.h" #include "alc/alconfig.h" #include "alnumeric.h" #include "core/device.h" @@ -85,7 +86,7 @@ struct PortPlayback final : public BackendBase { framesPerBuffer, timeInfo, statusFlags); } - void open(const char *name) override; + void open(std::string_view name) override; bool reset() override; void start() override; void stop() override; @@ -115,13 +116,13 @@ int PortPlayback::writeCallback(const void*, void *outputBuffer, unsigned long f } -void PortPlayback::open(const char *name) +void PortPlayback::open(std::string_view name) { - if(!name) + if(name.empty()) name = pa_device; - else if(strcmp(name, pa_device) != 0) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", - name}; + else if(name != pa_device) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", + static_cast<int>(name.length()), name.data()}; PaStreamParameters params{}; auto devidopt = ConfigValueInt(nullptr, "port", "device"); @@ -244,10 +245,10 @@ struct PortCapture final : public BackendBase { framesPerBuffer, timeInfo, statusFlags); } - void open(const char *name) override; + void open(std::string_view name) override; void start() override; void stop() override; - void captureSamples(al::byte *buffer, uint samples) override; + void captureSamples(std::byte *buffer, uint samples) override; uint availableSamples() override; PaStream *mStream{nullptr}; @@ -275,13 +276,13 @@ int PortCapture::readCallback(const void *inputBuffer, void*, unsigned long fram } -void PortCapture::open(const char *name) +void PortCapture::open(std::string_view name) { - if(!name) + if(name.empty()) name = pa_device; - else if(strcmp(name, pa_device) != 0) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", - name}; + else if(name != pa_device) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", + static_cast<int>(name.length()), name.data()}; uint samples{mDevice->BufferSize}; samples = maxu(samples, 100 * mDevice->Frequency / 1000); @@ -348,7 +349,7 @@ void PortCapture::stop() uint PortCapture::availableSamples() { return static_cast<uint>(mRing->readSpace()); } -void PortCapture::captureSamples(al::byte *buffer, uint samples) +void PortCapture::captureSamples(std::byte *buffer, uint samples) { mRing->read(buffer, samples); } } // namespace @@ -376,7 +377,7 @@ bool PortBackendFactory::init() return false; #define LOAD_FUNC(f) do { \ - p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(pa_handle, #f)); \ + p##f = al::bit_cast<decltype(p##f)>(GetSymbol(pa_handle, #f)); \ if(p##f == nullptr) \ { \ CloseLib(pa_handle); \ diff --git a/alc/backends/pulseaudio.cpp b/alc/backends/pulseaudio.cpp index 4b0e316f..bebc182d 100644 --- a/alc/backends/pulseaudio.cpp +++ b/alc/backends/pulseaudio.cpp @@ -31,17 +31,18 @@ #include <cstring> #include <limits> #include <mutex> +#include <optional> #include <stdint.h> #include <stdlib.h> #include <string> #include <sys/types.h> #include <utility> +#include <vector> -#include "albyte.h" +#include "albit.h" #include "alc/alconfig.h" #include "almalloc.h" #include "alnumeric.h" -#include "aloptional.h" #include "alspan.h" #include "core/devformat.h" #include "core/device.h" @@ -49,7 +50,6 @@ #include "dynload.h" #include "opthelpers.h" #include "strutils.h" -#include "vector.h" #include <pulse/pulseaudio.h> @@ -65,6 +65,8 @@ using uint = unsigned int; MAGIC(pa_context_get_state); \ MAGIC(pa_context_disconnect); \ MAGIC(pa_context_set_state_callback); \ + MAGIC(pa_context_set_subscribe_callback); \ + MAGIC(pa_context_subscribe); \ MAGIC(pa_context_errno); \ MAGIC(pa_context_connect); \ MAGIC(pa_context_get_server_info); \ @@ -136,6 +138,8 @@ PULSE_FUNCS(MAKE_FUNC) #define pa_context_get_state ppa_context_get_state #define pa_context_disconnect ppa_context_disconnect #define pa_context_set_state_callback ppa_context_set_state_callback +#define pa_context_set_subscribe_callback ppa_context_set_subscribe_callback +#define pa_context_subscribe ppa_context_subscribe #define pa_context_errno ppa_context_errno #define pa_context_connect ppa_context_connect #define pa_context_get_server_info ppa_context_get_server_info @@ -270,6 +274,9 @@ constexpr pa_context_flags_t& operator|=(pa_context_flags_t &lhs, pa_context_fla return lhs; } +constexpr pa_subscription_mask_t operator|(pa_subscription_mask_t lhs, pa_subscription_mask_t rhs) +{ return pa_subscription_mask_t(lhs | al::to_underlying(rhs)); } + struct DevMap { std::string name; @@ -282,8 +289,8 @@ bool checkName(const al::span<const DevMap> list, const std::string &name) return std::find_if(list.cbegin(), list.cend(), match_name) != list.cend(); } -al::vector<DevMap> PlaybackDevices; -al::vector<DevMap> CaptureDevices; +std::vector<DevMap> PlaybackDevices; +std::vector<DevMap> CaptureDevices; /* Global flags and properties */ @@ -291,13 +298,14 @@ pa_context_flags_t pulse_ctx_flags; class PulseMainloop { pa_threaded_mainloop *mLoop{}; + pa_context *mContext{}; public: PulseMainloop() = default; PulseMainloop(const PulseMainloop&) = delete; PulseMainloop(PulseMainloop&& rhs) noexcept : mLoop{rhs.mLoop} { rhs.mLoop = nullptr; } explicit PulseMainloop(pa_threaded_mainloop *loop) noexcept : mLoop{loop} { } - ~PulseMainloop() { if(mLoop) pa_threaded_mainloop_free(mLoop); } + ~PulseMainloop(); PulseMainloop& operator=(const PulseMainloop&) = delete; PulseMainloop& operator=(PulseMainloop&& rhs) noexcept @@ -316,6 +324,7 @@ public: auto stop() const { return pa_threaded_mainloop_stop(mLoop); } auto getApi() const { return pa_threaded_mainloop_get_api(mLoop); } + auto getContext() const noexcept { return mContext; } auto lock() const { return pa_threaded_mainloop_lock(mLoop); } auto unlock() const { return pa_threaded_mainloop_unlock(mLoop); } @@ -329,7 +338,7 @@ public: static void streamSuccessCallbackC(pa_stream *stream, int success, void *pdata) noexcept { static_cast<PulseMainloop*>(pdata)->streamSuccessCallback(stream, success); } - void close(pa_context *context, pa_stream *stream=nullptr); + void close(pa_stream *stream=nullptr); void deviceSinkCallback(pa_context*, const pa_sink_info *info, int eol) noexcept @@ -420,6 +429,41 @@ struct MainloopUniqueLock : public std::unique_lock<PulseMainloop> { } + void setEventHandler() + { + pa_operation *op{pa_context_subscribe(mutex()->mContext, + PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE, + [](pa_context*, int, void *pdata) noexcept + { static_cast<PulseMainloop*>(pdata)->signal(); }, + mutex())}; + waitForOperation(op); + + /* Watch for device added/removed events. + * + * TODO: Also track the "default" device, in as much as PulseAudio has + * the concept of a default device (whatever device is opened when not + * specifying a specific sink or source name). There doesn't seem to be + * an event for this. + */ + auto handler = [](pa_context*, pa_subscription_event_type_t t, uint32_t, void*) noexcept + { + const auto eventFacility = (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK); + if(eventFacility == PA_SUBSCRIPTION_EVENT_SINK + || eventFacility == PA_SUBSCRIPTION_EVENT_SOURCE) + { + const auto deviceType = (eventFacility == PA_SUBSCRIPTION_EVENT_SINK) + ? alc::DeviceType::Playback : alc::DeviceType::Capture; + const auto eventType = (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK); + if(eventType == PA_SUBSCRIPTION_EVENT_NEW) + alc::Event(alc::EventType::DeviceAdded, deviceType, "Device added"); + else if(eventType == PA_SUBSCRIPTION_EVENT_REMOVE) + alc::Event(alc::EventType::DeviceRemoved, deviceType, "Device removed"); + } + }; + pa_context_set_subscribe_callback(mutex()->mContext, handler, nullptr); + } + + void contextStateCallback(pa_context *context) noexcept { pa_context_state_t state{pa_context_get_state(context)}; @@ -434,31 +478,46 @@ struct MainloopUniqueLock : public std::unique_lock<PulseMainloop> { mutex()->signal(); } - pa_context *connectContext(); - pa_stream *connectStream(const char *device_name, pa_context *context, pa_stream_flags_t flags, + void connectContext(); + pa_stream *connectStream(const char *device_name, pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec, pa_channel_map *chanmap, BackendType type); }; using MainloopLockGuard = std::lock_guard<PulseMainloop>; +PulseMainloop::~PulseMainloop() +{ + if(mContext) + { + MainloopUniqueLock _{*this}; + pa_context_disconnect(mContext); + pa_context_unref(mContext); + } + if(mLoop) + pa_threaded_mainloop_free(mLoop); +} + -pa_context *MainloopUniqueLock::connectContext() +void MainloopUniqueLock::connectContext() { - pa_context *context{pa_context_new(mutex()->getApi(), nullptr)}; - if(!context) throw al::backend_exception{al::backend_error::OutOfMemory, + if(mutex()->mContext) + return; + + mutex()->mContext = pa_context_new(mutex()->getApi(), nullptr); + if(!mutex()->mContext) throw al::backend_exception{al::backend_error::OutOfMemory, "pa_context_new() failed"}; - pa_context_set_state_callback(context, [](pa_context *ctx, void *pdata) noexcept + pa_context_set_state_callback(mutex()->mContext, [](pa_context *ctx, void *pdata) noexcept { return static_cast<MainloopUniqueLock*>(pdata)->contextStateCallback(ctx); }, this); int err; - if((err=pa_context_connect(context, nullptr, pulse_ctx_flags, nullptr)) >= 0) + if((err=pa_context_connect(mutex()->mContext, nullptr, pulse_ctx_flags, nullptr)) >= 0) { pa_context_state_t state; - while((state=pa_context_get_state(context)) != PA_CONTEXT_READY) + while((state=pa_context_get_state(mutex()->mContext)) != PA_CONTEXT_READY) { if(!PA_CONTEXT_IS_GOOD(state)) { - err = pa_context_errno(context); + err = pa_context_errno(mutex()->mContext); if(err > 0) err = -err; break; } @@ -466,27 +525,25 @@ pa_context *MainloopUniqueLock::connectContext() wait(); } } - pa_context_set_state_callback(context, nullptr, nullptr); + pa_context_set_state_callback(mutex()->mContext, nullptr, nullptr); if(err < 0) { - pa_context_unref(context); + pa_context_unref(mutex()->mContext); + mutex()->mContext = nullptr; throw al::backend_exception{al::backend_error::DeviceError, "Context did not connect (%s)", pa_strerror(err)}; } - - return context; } -pa_stream *MainloopUniqueLock::connectStream(const char *device_name, pa_context *context, - pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec, pa_channel_map *chanmap, - BackendType type) +pa_stream *MainloopUniqueLock::connectStream(const char *device_name, pa_stream_flags_t flags, + pa_buffer_attr *attr, pa_sample_spec *spec, pa_channel_map *chanmap, BackendType type) { const char *stream_id{(type==BackendType::Playback) ? "Playback Stream" : "Capture Stream"}; - pa_stream *stream{pa_stream_new(context, stream_id, spec, chanmap)}; + pa_stream *stream{pa_stream_new(mutex()->mContext, stream_id, spec, chanmap)}; if(!stream) throw al::backend_exception{al::backend_error::OutOfMemory, "pa_stream_new() failed (%s)", - pa_strerror(pa_context_errno(context))}; + pa_strerror(pa_context_errno(mutex()->mContext))}; pa_stream_set_state_callback(stream, [](pa_stream *strm, void *pdata) noexcept { return static_cast<MainloopUniqueLock*>(pdata)->streamStateCallback(strm); }, this); @@ -506,7 +563,7 @@ pa_stream *MainloopUniqueLock::connectStream(const char *device_name, pa_context { if(!PA_STREAM_IS_GOOD(state)) { - err = pa_context_errno(context); + err = pa_context_errno(mutex()->mContext); pa_stream_unref(stream); throw al::backend_exception{al::backend_error::DeviceError, "%s did not get ready (%s)", stream_id, pa_strerror(err)}; @@ -519,75 +576,57 @@ pa_stream *MainloopUniqueLock::connectStream(const char *device_name, pa_context return stream; } -void PulseMainloop::close(pa_context *context, pa_stream *stream) +void PulseMainloop::close(pa_stream *stream) { - MainloopUniqueLock _{*this}; - if(stream) - { - pa_stream_set_state_callback(stream, nullptr, nullptr); - pa_stream_set_moved_callback(stream, nullptr, nullptr); - pa_stream_set_write_callback(stream, nullptr, nullptr); - pa_stream_set_buffer_attr_callback(stream, nullptr, nullptr); - pa_stream_disconnect(stream); - pa_stream_unref(stream); - } + if(!stream) + return; - pa_context_disconnect(context); - pa_context_unref(context); + MainloopUniqueLock _{*this}; + pa_stream_set_state_callback(stream, nullptr, nullptr); + pa_stream_set_moved_callback(stream, nullptr, nullptr); + pa_stream_set_write_callback(stream, nullptr, nullptr); + pa_stream_set_buffer_attr_callback(stream, nullptr, nullptr); + pa_stream_disconnect(stream); + pa_stream_unref(stream); } void PulseMainloop::probePlaybackDevices() { - pa_context *context{}; - PlaybackDevices.clear(); try { MainloopUniqueLock plock{*this}; auto sink_callback = [](pa_context *ctx, const pa_sink_info *info, int eol, void *pdata) noexcept { return static_cast<PulseMainloop*>(pdata)->deviceSinkCallback(ctx, info, eol); }; - context = plock.connectContext(); - pa_operation *op{pa_context_get_sink_info_by_name(context, nullptr, sink_callback, this)}; + pa_operation *op{pa_context_get_sink_info_by_name(mContext, nullptr, sink_callback, this)}; plock.waitForOperation(op); - op = pa_context_get_sink_info_list(context, sink_callback, this); + op = pa_context_get_sink_info_list(mContext, sink_callback, this); plock.waitForOperation(op); - - pa_context_disconnect(context); - pa_context_unref(context); - context = nullptr; } catch(std::exception &e) { ERR("Error enumerating devices: %s\n", e.what()); - if(context) close(context); } } void PulseMainloop::probeCaptureDevices() { - pa_context *context{}; - CaptureDevices.clear(); try { MainloopUniqueLock plock{*this}; auto src_callback = [](pa_context *ctx, const pa_source_info *info, int eol, void *pdata) noexcept { return static_cast<PulseMainloop*>(pdata)->deviceSourceCallback(ctx, info, eol); }; - context = plock.connectContext(); - pa_operation *op{pa_context_get_source_info_by_name(context, nullptr, src_callback, this)}; + pa_operation *op{pa_context_get_source_info_by_name(mContext, nullptr, src_callback, + this)}; plock.waitForOperation(op); - op = pa_context_get_source_info_list(context, src_callback, this); + op = pa_context_get_source_info_list(mContext, src_callback, this); plock.waitForOperation(op); - - pa_context_disconnect(context); - pa_context_unref(context); - context = nullptr; } catch(std::exception &e) { ERR("Error enumerating devices: %s\n", e.what()); - if(context) close(context); } } @@ -607,7 +646,7 @@ struct PulsePlayback final : public BackendBase { void sinkNameCallback(pa_context *context, const pa_sink_info *info, int eol) noexcept; void streamMovedCallback(pa_stream *stream) noexcept; - void open(const char *name) override; + void open(std::string_view name) override; bool reset() override; void start() override; void stop() override; @@ -615,14 +654,13 @@ struct PulsePlayback final : public BackendBase { PulseMainloop mMainloop; - al::optional<std::string> mDeviceName{al::nullopt}; + std::optional<std::string> mDeviceName{std::nullopt}; bool mIs51Rear{false}; pa_buffer_attr mAttr; pa_sample_spec mSpec; pa_stream *mStream{nullptr}; - pa_context *mContext{nullptr}; uint mFrameSize{0u}; @@ -630,14 +668,7 @@ struct PulsePlayback final : public BackendBase { }; PulsePlayback::~PulsePlayback() -{ - if(!mContext) - return; - - mMainloop.close(mContext, mStream); - mContext = nullptr; - mStream = nullptr; -} +{ if(mStream) mMainloop.close(mStream); } void PulsePlayback::bufferAttrCallback(pa_stream *stream) noexcept @@ -750,14 +781,14 @@ void PulsePlayback::streamMovedCallback(pa_stream *stream) noexcept } -void PulsePlayback::open(const char *name) +void PulsePlayback::open(std::string_view name) { mMainloop = PulseMainloop::Create(); mMainloop.start(); const char *pulse_name{nullptr}; const char *dev_name{nullptr}; - if(name) + if(!name.empty()) { if(PlaybackDevices.empty()) mMainloop.probePlaybackDevices(); @@ -766,13 +797,13 @@ void PulsePlayback::open(const char *name) [name](const DevMap &entry) -> bool { return entry.name == name; }); if(iter == PlaybackDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%s\" not found", name}; + "Device name \"%.*s\" not found", static_cast<int>(name.length()), name.data()}; pulse_name = iter->device_name.c_str(); dev_name = iter->name.c_str(); } MainloopUniqueLock plock{mMainloop}; - mContext = plock.connectContext(); + plock.connectContext(); pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | PA_STREAM_FIX_CHANNELS}; @@ -790,7 +821,7 @@ void PulsePlayback::open(const char *name) if(defname) pulse_name = defname->c_str(); } TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)"); - mStream = plock.connectStream(pulse_name, mContext, flags, nullptr, &spec, nullptr, + mStream = plock.connectStream(pulse_name, flags, nullptr, &spec, nullptr, BackendType::Playback); pa_stream_set_moved_callback(mStream, [](pa_stream *stream, void *pdata) noexcept @@ -803,7 +834,7 @@ void PulsePlayback::open(const char *name) { auto name_callback = [](pa_context *context, const pa_sink_info *info, int eol, void *pdata) noexcept { return static_cast<PulsePlayback*>(pdata)->sinkNameCallback(context, info, eol); }; - pa_operation *op{pa_context_get_sink_info_by_name(mContext, + pa_operation *op{pa_context_get_sink_info_by_name(mMainloop.getContext(), pa_stream_get_device_name(mStream), name_callback, this)}; plock.waitForOperation(op); } @@ -829,7 +860,8 @@ bool PulsePlayback::reset() auto info_callback = [](pa_context *context, const pa_sink_info *info, int eol, void *pdata) noexcept { return static_cast<PulsePlayback*>(pdata)->sinkInfoCallback(context, info, eol); }; - pa_operation *op{pa_context_get_sink_info_by_name(mContext, deviceName, info_callback, this)}; + pa_operation *op{pa_context_get_sink_info_by_name(mMainloop.getContext(), deviceName, + info_callback, this)}; plock.waitForOperation(op); pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING | @@ -916,7 +948,7 @@ bool PulsePlayback::reset() mAttr.minreq = mDevice->UpdateSize * frame_size; mAttr.fragsize = ~0u; - mStream = plock.connectStream(deviceName, mContext, flags, &mAttr, &mSpec, &chanmap, + mStream = plock.connectStream(deviceName, flags, &mAttr, &mSpec, &chanmap, BackendType::Playback); pa_stream_set_state_callback(mStream, [](pa_stream *stream, void *pdata) noexcept @@ -1033,42 +1065,34 @@ struct PulseCapture final : public BackendBase { void sourceNameCallback(pa_context *context, const pa_source_info *info, int eol) noexcept; void streamMovedCallback(pa_stream *stream) noexcept; - void open(const char *name) override; + void open(std::string_view name) override; void start() override; void stop() override; - void captureSamples(al::byte *buffer, uint samples) override; + void captureSamples(std::byte *buffer, uint samples) override; uint availableSamples() override; ClockLatency getClockLatency() override; PulseMainloop mMainloop; - al::optional<std::string> mDeviceName{al::nullopt}; + std::optional<std::string> mDeviceName{std::nullopt}; - al::span<const al::byte> mCapBuffer; + al::span<const std::byte> mCapBuffer; size_t mHoleLength{0}; size_t mPacketLength{0}; uint mLastReadable{0u}; - al::byte mSilentVal{}; + std::byte mSilentVal{}; pa_buffer_attr mAttr{}; pa_sample_spec mSpec{}; pa_stream *mStream{nullptr}; - pa_context *mContext{nullptr}; DEF_NEWDEL(PulseCapture) }; PulseCapture::~PulseCapture() -{ - if(!mContext) - return; - - mMainloop.close(mContext, mStream); - mContext = nullptr; - mStream = nullptr; -} +{ if(mStream) mMainloop.close(mStream); } void PulseCapture::streamStateCallback(pa_stream *stream) noexcept @@ -1098,7 +1122,7 @@ void PulseCapture::streamMovedCallback(pa_stream *stream) noexcept } -void PulseCapture::open(const char *name) +void PulseCapture::open(std::string_view name) { if(!mMainloop) { @@ -1107,7 +1131,7 @@ void PulseCapture::open(const char *name) } const char *pulse_name{nullptr}; - if(name) + if(!name.empty()) { if(CaptureDevices.empty()) mMainloop.probeCaptureDevices(); @@ -1116,13 +1140,13 @@ void PulseCapture::open(const char *name) [name](const DevMap &entry) -> bool { return entry.name == name; }); if(iter == CaptureDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%s\" not found", name}; + "Device name \"%.*s\" not found", static_cast<int>(name.length()), name.data()}; pulse_name = iter->device_name.c_str(); mDevice->DeviceName = iter->name; } MainloopUniqueLock plock{mMainloop}; - mContext = plock.connectContext(); + plock.connectContext(); pa_channel_map chanmap{}; switch(mDevice->FmtChans) @@ -1158,7 +1182,7 @@ void PulseCapture::open(const char *name) switch(mDevice->FmtType) { case DevFmtUByte: - mSilentVal = al::byte(0x80); + mSilentVal = std::byte(0x80); mSpec.format = PA_SAMPLE_U8; break; case DevFmtShort: @@ -1194,7 +1218,7 @@ void PulseCapture::open(const char *name) flags |= PA_STREAM_DONT_MOVE; TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)"); - mStream = plock.connectStream(pulse_name, mContext, flags, &mAttr, &mSpec, &chanmap, + mStream = plock.connectStream(pulse_name, flags, &mAttr, &mSpec, &chanmap, BackendType::Capture); pa_stream_set_moved_callback(mStream, [](pa_stream *stream, void *pdata) noexcept @@ -1208,7 +1232,7 @@ void PulseCapture::open(const char *name) { auto name_callback = [](pa_context *context, const pa_source_info *info, int eol, void *pdata) noexcept { return static_cast<PulseCapture*>(pdata)->sourceNameCallback(context, info, eol); }; - pa_operation *op{pa_context_get_source_info_by_name(mContext, + pa_operation *op{pa_context_get_source_info_by_name(mMainloop.getContext(), pa_stream_get_device_name(mStream), name_callback, this)}; plock.waitForOperation(op); } @@ -1230,9 +1254,9 @@ void PulseCapture::stop() plock.waitForOperation(op); } -void PulseCapture::captureSamples(al::byte *buffer, uint samples) +void PulseCapture::captureSamples(std::byte *buffer, uint samples) { - al::span<al::byte> dstbuf{buffer, samples * pa_frame_size(&mSpec)}; + al::span<std::byte> dstbuf{buffer, samples * pa_frame_size(&mSpec)}; /* Capture is done in fragment-sized chunks, so we loop until we get all * that's available. @@ -1281,7 +1305,7 @@ void PulseCapture::captureSamples(al::byte *buffer, uint samples) if(pa_stream_peek(mStream, &capbuf, &caplen) < 0) UNLIKELY { mDevice->handleDisconnect("Failed retrieving capture samples: %s", - pa_strerror(pa_context_errno(mContext))); + pa_strerror(pa_context_errno(mMainloop.getContext()))); break; } plock.unlock(); @@ -1290,7 +1314,7 @@ void PulseCapture::captureSamples(al::byte *buffer, uint samples) if(!capbuf) UNLIKELY mHoleLength = caplen; else - mCapBuffer = {static_cast<const al::byte*>(capbuf), caplen}; + mCapBuffer = {static_cast<const std::byte*>(capbuf), caplen}; mPacketLength = caplen; } if(!dstbuf.empty()) @@ -1381,7 +1405,7 @@ bool PulseBackendFactory::init() } #define LOAD_FUNC(x) do { \ - p##x = reinterpret_cast<decltype(p##x)>(GetSymbol(pulse_handle, #x)); \ + p##x = al::bit_cast<decltype(p##x)>(GetSymbol(pulse_handle, #x)); \ if(!(p##x)) { \ ret = false; \ missing_funcs += "\n" #x; \ @@ -1412,9 +1436,8 @@ bool PulseBackendFactory::init() } MainloopUniqueLock plock{gGlobalMainloop}; - pa_context *context{plock.connectContext()}; - pa_context_disconnect(context); - pa_context_unref(context); + plock.connectContext(); + plock.setEventHandler(); return true; } catch(...) { @@ -1467,3 +1490,18 @@ BackendFactory &PulseBackendFactory::getFactory() static PulseBackendFactory factory{}; return factory; } + +alc::EventSupport PulseBackendFactory::queryEventSupport(alc::EventType eventType, BackendType) +{ + switch(eventType) + { + case alc::EventType::DeviceAdded: + case alc::EventType::DeviceRemoved: + return alc::EventSupport::FullSupport; + + case alc::EventType::DefaultDeviceChanged: + case alc::EventType::Count: + break; + } + return alc::EventSupport::NoSupport; +} diff --git a/alc/backends/pulseaudio.h b/alc/backends/pulseaudio.h index 6690fe8a..4752a891 100644 --- a/alc/backends/pulseaudio.h +++ b/alc/backends/pulseaudio.h @@ -9,6 +9,8 @@ public: bool querySupport(BackendType type) override; + alc::EventSupport queryEventSupport(alc::EventType eventType, BackendType type) override; + std::string probe(BackendType type) override; BackendPtr createBackend(DeviceBase *device, BackendType type) override; diff --git a/alc/backends/sdl2.cpp b/alc/backends/sdl2.cpp index a4a5a9ac..f5ed4316 100644 --- a/alc/backends/sdl2.cpp +++ b/alc/backends/sdl2.cpp @@ -53,10 +53,8 @@ struct Sdl2Backend final : public BackendBase { ~Sdl2Backend() override; void audioCallback(Uint8 *stream, int len) noexcept; - static void audioCallbackC(void *ptr, Uint8 *stream, int len) noexcept - { static_cast<Sdl2Backend*>(ptr)->audioCallback(stream, len); } - void open(const char *name) override; + void open(std::string_view name) override; bool reset() override; void start() override; void stop() override; @@ -86,7 +84,7 @@ void Sdl2Backend::audioCallback(Uint8 *stream, int len) noexcept mDevice->renderSamples(stream, ulen / mFrameSize, mDevice->channelsFromFmt()); } -void Sdl2Backend::open(const char *name) +void Sdl2Backend::open(std::string_view name) { SDL_AudioSpec want{}, have{}; @@ -103,23 +101,37 @@ void Sdl2Backend::open(const char *name) } want.channels = (mDevice->FmtChans == DevFmtMono) ? 1 : 2; want.samples = static_cast<Uint16>(minu(mDevice->UpdateSize, 8192)); - want.callback = &Sdl2Backend::audioCallbackC; + want.callback = [](void *ptr, Uint8 *stream, int len) noexcept + { return static_cast<Sdl2Backend*>(ptr)->audioCallback(stream, len); }; want.userdata = this; /* Passing nullptr to SDL_OpenAudioDevice opens a default, which isn't * necessarily the first in the list. */ SDL_AudioDeviceID devid; - if(!name || strcmp(name, defaultDeviceName) == 0) + if(name.empty() || name == defaultDeviceName) + { + name = defaultDeviceName; devid = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE); + } else { const size_t prefix_len = strlen(DEVNAME_PREFIX); - if(strncmp(name, DEVNAME_PREFIX, prefix_len) == 0) - devid = SDL_OpenAudioDevice(name+prefix_len, SDL_FALSE, &want, &have, + if(name.length() >= prefix_len && strncmp(name.data(), DEVNAME_PREFIX, prefix_len) == 0) + { + /* Copy the string_view to a string to ensure it's null terminated + * for this call. + */ + const std::string devname{name.substr(prefix_len)}; + devid = SDL_OpenAudioDevice(devname.c_str(), SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE); + } else - devid = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE); + { + const std::string devname{name}; + devid = SDL_OpenAudioDevice(devname.c_str(), SDL_FALSE, &want, &have, + SDL_AUDIO_ALLOW_ANY_CHANGE); + } } if(!devid) throw al::backend_exception{al::backend_error::NoDevice, "%s", SDL_GetError()}; @@ -161,7 +173,7 @@ void Sdl2Backend::open(const char *name) mFmtType = devtype; mUpdateSize = have.samples; - mDevice->DeviceName = name ? name : defaultDeviceName; + mDevice->DeviceName = name; } bool Sdl2Backend::reset() diff --git a/alc/backends/sndio.cpp b/alc/backends/sndio.cpp index 077e77f2..d54c337b 100644 --- a/alc/backends/sndio.cpp +++ b/alc/backends/sndio.cpp @@ -22,21 +22,21 @@ #include "sndio.h" +#include <cinttypes> #include <functional> -#include <inttypes.h> #include <poll.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <thread> +#include <vector> #include "alnumeric.h" +#include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" #include "core/logging.h" #include "ringbuffer.h" -#include "threads.h" -#include "vector.h" #include <sndio.h> @@ -57,7 +57,7 @@ struct SndioPlayback final : public BackendBase { int mixerProc(); - void open(const char *name) override; + void open(std::string_view name) override; bool reset() override; void start() override; void stop() override; @@ -65,7 +65,7 @@ struct SndioPlayback final : public BackendBase { sio_hdl *mSndHandle{nullptr}; uint mFrameStep{}; - al::vector<al::byte> mBuffer; + std::vector<std::byte> mBuffer; std::atomic<bool> mKillNow{true}; std::thread mThread; @@ -91,7 +91,7 @@ int SndioPlayback::mixerProc() while(!mKillNow.load(std::memory_order_acquire) && mDevice->Connected.load(std::memory_order_acquire)) { - al::span<al::byte> buffer{mBuffer}; + al::span<std::byte> buffer{mBuffer}; mDevice->renderSamples(buffer.data(), static_cast<uint>(buffer.size() / frameSize), frameStep); @@ -112,13 +112,13 @@ int SndioPlayback::mixerProc() } -void SndioPlayback::open(const char *name) +void SndioPlayback::open(std::string_view name) { - if(!name) + if(name.empty()) name = sndio_device; - else if(strcmp(name, sndio_device) != 0) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", - name}; + else if(name != sndio_device) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", + static_cast<int>(name.length()), name.data()}; sio_hdl *sndHandle{sio_open(nullptr, SIO_PLAY, 0)}; if(!sndHandle) @@ -231,9 +231,9 @@ retry_params: mBuffer.resize(mDevice->UpdateSize * par.pchan*par.bps); if(par.sig == 1) - std::fill(mBuffer.begin(), mBuffer.end(), al::byte{}); + std::fill(mBuffer.begin(), mBuffer.end(), std::byte{}); else if(par.bits == 8) - std::fill_n(mBuffer.data(), mBuffer.size(), al::byte(0x80)); + std::fill_n(mBuffer.data(), mBuffer.size(), std::byte(0x80)); else if(par.bits == 16) std::fill_n(reinterpret_cast<uint16_t*>(mBuffer.data()), mBuffer.size()/2, 0x8000); else if(par.bits == 32) @@ -280,10 +280,10 @@ struct SndioCapture final : public BackendBase { int recordProc(); - void open(const char *name) override; + void open(std::string_view name) override; void start() override; void stop() override; - void captureSamples(al::byte *buffer, uint samples) override; + void captureSamples(std::byte *buffer, uint samples) override; uint availableSamples() override; sio_hdl *mSndHandle{nullptr}; @@ -349,7 +349,7 @@ int SndioCapture::recordProc() continue; auto data = mRing->getWriteVector(); - al::span<al::byte> buffer{data.first.buf, data.first.len*frameSize}; + al::span<std::byte> buffer{data.first.buf, data.first.len*frameSize}; while(!buffer.empty()) { size_t got{sio_read(mSndHandle, buffer.data(), buffer.size())}; @@ -382,13 +382,13 @@ int SndioCapture::recordProc() } -void SndioCapture::open(const char *name) +void SndioCapture::open(std::string_view name) { - if(!name) + if(name.empty()) name = sndio_device; - else if(strcmp(name, sndio_device) != 0) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", - name}; + else if(name != sndio_device) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", + static_cast<int>(name.length()), name.data()}; mSndHandle = sio_open(nullptr, SIO_REC, true); if(mSndHandle == nullptr) @@ -436,7 +436,7 @@ void SndioCapture::open(const char *name) if(!sio_setpar(mSndHandle, &par) || !sio_getpar(mSndHandle, &par)) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to set device praameters"}; + "Failed to set device parameters"}; if(par.bps > 1 && par.le != SIO_LE_NATIVE) throw al::backend_exception{al::backend_error::DeviceError, @@ -496,7 +496,7 @@ void SndioCapture::stop() ERR("Error stopping device\n"); } -void SndioCapture::captureSamples(al::byte *buffer, uint samples) +void SndioCapture::captureSamples(std::byte *buffer, uint samples) { mRing->read(buffer, samples); } uint SndioCapture::availableSamples() diff --git a/alc/backends/solaris.cpp b/alc/backends/solaris.cpp index 791609ce..38f9db19 100644 --- a/alc/backends/solaris.cpp +++ b/alc/backends/solaris.cpp @@ -35,17 +35,16 @@ #include <poll.h> #include <math.h> #include <string.h> +#include <vector> #include <thread> #include <functional> -#include "albyte.h" #include "alc/alconfig.h" +#include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" #include "core/logging.h" -#include "threads.h" -#include "vector.h" #include <sys/audioio.h> @@ -63,7 +62,7 @@ struct SolarisBackend final : public BackendBase { int mixerProc(); - void open(const char *name) override; + void open(std::string_view name) override; bool reset() override; void start() override; void stop() override; @@ -71,7 +70,7 @@ struct SolarisBackend final : public BackendBase { int mFd{-1}; uint mFrameStep{}; - al::vector<al::byte> mBuffer; + std::vector<std::byte> mBuffer; std::atomic<bool> mKillNow{true}; std::thread mThread; @@ -116,7 +115,7 @@ int SolarisBackend::mixerProc() continue; } - al::byte *write_ptr{mBuffer.data()}; + std::byte *write_ptr{mBuffer.data()}; size_t to_write{mBuffer.size()}; mDevice->renderSamples(write_ptr, static_cast<uint>(to_write/frame_size), frame_step); while(to_write > 0 && !mKillNow.load(std::memory_order_acquire)) @@ -140,13 +139,13 @@ int SolarisBackend::mixerProc() } -void SolarisBackend::open(const char *name) +void SolarisBackend::open(std::string_view name) { - if(!name) + if(name.empty()) name = solaris_device; - else if(strcmp(name, solaris_device) != 0) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", - name}; + else if(name != solaris_device) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", + static_cast<int>(name.length()), name.data()}; int fd{::open(solaris_driver.c_str(), O_WRONLY)}; if(fd == -1) @@ -231,7 +230,7 @@ bool SolarisBackend::reset() setDefaultChannelOrder(); mBuffer.resize(mDevice->UpdateSize * size_t{frame_size}); - std::fill(mBuffer.begin(), mBuffer.end(), al::byte{}); + std::fill(mBuffer.begin(), mBuffer.end(), std::byte{}); return true; } diff --git a/alc/backends/wasapi.cpp b/alc/backends/wasapi.cpp index e834eef4..3ee98457 100644 --- a/alc/backends/wasapi.cpp +++ b/alc/backends/wasapi.cpp @@ -31,7 +31,9 @@ #include <wtypes.h> #include <mmdeviceapi.h> +#include <audiosessiontypes.h> #include <audioclient.h> +#include <spatialaudioclient.h> #include <cguid.h> #include <devpropdef.h> #include <mmreg.h> @@ -59,6 +61,8 @@ #include "albit.h" #include "alc/alconfig.h" #include "alnumeric.h" +#include "alspan.h" +#include "althrd_setname.h" #include "comptr.h" #include "core/converter.h" #include "core/device.h" @@ -66,8 +70,22 @@ #include "core/logging.h" #include "ringbuffer.h" #include "strutils.h" -#include "threads.h" +#if defined(ALSOFT_UWP) + +#include <winrt/Windows.Media.Core.h> // !!This is important!! +#include <winrt/Windows.Foundation.Collections.h> +#include <winrt/Windows.Devices.h> +#include <winrt/Windows.Foundation.h> +#include <winrt/Windows.Devices.Enumeration.h> +#include <winrt/Windows.Media.Devices.h> + +using namespace winrt; +using namespace Windows::Foundation; +using namespace Windows::Media::Devices; +using namespace Windows::Devices::Enumeration; +using namespace Windows::Media::Devices; +#endif /* Some headers seem to define these as macros for __uuidof, which is annoying * since some headers don't declare them at all. Hopefully the ifdef is enough @@ -79,11 +97,11 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x #ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); #endif - +#if !defined(ALSOFT_UWP) 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 namespace { @@ -91,9 +109,9 @@ using std::chrono::nanoseconds; using std::chrono::milliseconds; using std::chrono::seconds; -using ReferenceTime = std::chrono::duration<REFERENCE_TIME,std::ratio<1,10000000>>; +using ReferenceTime = std::chrono::duration<REFERENCE_TIME,std::ratio<1,10'000'000>>; -inline constexpr ReferenceTime operator "" _reftime(unsigned long long int n) noexcept +constexpr ReferenceTime operator "" _reftime(unsigned long long int n) noexcept { return ReferenceTime{static_cast<REFERENCE_TIME>(n)}; } @@ -124,15 +142,61 @@ constexpr DWORD X61Mask{MaskFromTopBits(X6DOT1)}; constexpr DWORD X71Mask{MaskFromTopBits(X7DOT1)}; constexpr DWORD X714Mask{MaskFromTopBits(X7DOT1DOT4)}; + +#ifndef _MSC_VER +constexpr AudioObjectType operator|(AudioObjectType lhs, AudioObjectType rhs) noexcept +{ return static_cast<AudioObjectType>(lhs | al::to_underlying(rhs)); } +#endif + +constexpr AudioObjectType ChannelMask_Mono{AudioObjectType_FrontCenter}; +constexpr AudioObjectType ChannelMask_Stereo{AudioObjectType_FrontLeft + | AudioObjectType_FrontRight}; +constexpr AudioObjectType ChannelMask_Quad{AudioObjectType_FrontLeft | AudioObjectType_FrontRight + | AudioObjectType_BackLeft | AudioObjectType_BackRight}; +constexpr AudioObjectType ChannelMask_X51{AudioObjectType_FrontLeft | AudioObjectType_FrontRight + | AudioObjectType_FrontCenter | AudioObjectType_LowFrequency | AudioObjectType_SideLeft + | AudioObjectType_SideRight}; +constexpr AudioObjectType ChannelMask_X51Rear{AudioObjectType_FrontLeft + | AudioObjectType_FrontRight | AudioObjectType_FrontCenter | AudioObjectType_LowFrequency + | AudioObjectType_BackLeft | AudioObjectType_BackRight}; +constexpr AudioObjectType ChannelMask_X61{AudioObjectType_FrontLeft | AudioObjectType_FrontRight + | AudioObjectType_FrontCenter | AudioObjectType_LowFrequency | AudioObjectType_SideLeft + | AudioObjectType_SideRight | AudioObjectType_BackCenter}; +constexpr AudioObjectType ChannelMask_X71{AudioObjectType_FrontLeft | AudioObjectType_FrontRight + | AudioObjectType_FrontCenter | AudioObjectType_LowFrequency | AudioObjectType_SideLeft + | AudioObjectType_SideRight | AudioObjectType_BackLeft | AudioObjectType_BackRight}; +constexpr AudioObjectType ChannelMask_X714{AudioObjectType_FrontLeft | AudioObjectType_FrontRight + | AudioObjectType_FrontCenter | AudioObjectType_LowFrequency | AudioObjectType_SideLeft + | AudioObjectType_SideRight | AudioObjectType_BackLeft | AudioObjectType_BackRight + | AudioObjectType_TopFrontLeft | AudioObjectType_TopFrontRight | AudioObjectType_TopBackLeft + | AudioObjectType_TopBackRight}; + + constexpr char DevNameHead[] = "OpenAL Soft on "; -constexpr size_t DevNameHeadLen{al::size(DevNameHead) - 1}; +constexpr size_t DevNameHeadLen{std::size(DevNameHead) - 1}; + + +template<typename... Ts> +struct overloaded : Ts... { using Ts::operator()...; }; + +template<typename... Ts> +overloaded(Ts...) -> overloaded<Ts...>; + + +template<typename T> +constexpr auto as_unsigned(T value) noexcept +{ + using UT = std::make_unsigned_t<T>; + return static_cast<UT>(value); +} /* Scales the given reftime value, rounding the result. */ -inline uint RefTime2Samples(const ReferenceTime &val, uint srate) +template<typename T> +constexpr uint RefTime2Samples(const ReferenceTime &val, T srate) noexcept { const auto retval = (val*srate + ReferenceTime{seconds{1}}/2) / seconds{1}; - return static_cast<uint>(mini64(retval, std::numeric_limits<uint>::max())); + return static_cast<uint>(std::min<decltype(retval)>(retval, std::numeric_limits<uint>::max())); } @@ -142,7 +206,7 @@ class GuidPrinter { public: GuidPrinter(const GUID &guid) { - std::snprintf(mMsg, al::size(mMsg), "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", + std::snprintf(mMsg, std::size(mMsg), "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", DWORD{guid.Data1}, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); } @@ -178,28 +242,73 @@ struct DevMap { , endpoint_guid{std::forward<T1>(guid_)} , devid{std::forward<T2>(devid_)} { } + /* To prevent GCC from complaining it doesn't want to inline this. */ + ~DevMap(); }; +DevMap::~DevMap() = default; -bool checkName(const al::vector<DevMap> &list, const std::string &name) +bool checkName(const al::span<DevMap> list, const std::string_view name) { - auto match_name = [&name](const DevMap &entry) -> bool - { return entry.name == name; }; + auto match_name = [name](const DevMap &entry) -> bool { return entry.name == name; }; return std::find_if(list.cbegin(), list.cend(), match_name) != list.cend(); } -al::vector<DevMap> PlaybackDevices; -al::vector<DevMap> CaptureDevices; + +struct DeviceList { + auto lock() noexcept(noexcept(mMutex.lock())) { return mMutex.lock(); } + auto unlock() noexcept(noexcept(mMutex.unlock())) { return mMutex.unlock(); } + +private: + std::mutex mMutex; + std::vector<DevMap> mPlayback; + std::vector<DevMap> mCapture; + std::wstring mPlaybackDefaultId; + std::wstring mCaptureDefaultId; + + friend struct DeviceListLock; +}; +struct DeviceListLock : public std::unique_lock<DeviceList> { + using std::unique_lock<DeviceList>::unique_lock; + + auto& getPlaybackList() const noexcept { return mutex()->mPlayback; } + auto& getCaptureList() const noexcept { return mutex()->mCapture; } + + void setPlaybackDefaultId(std::wstring_view devid) const { mutex()->mPlaybackDefaultId = devid; } + std::wstring_view getPlaybackDefaultId() const noexcept { return mutex()->mPlaybackDefaultId; } + void setCaptureDefaultId(std::wstring_view devid) const { mutex()->mCaptureDefaultId = devid; } + std::wstring_view getCaptureDefaultId() const noexcept { return mutex()->mCaptureDefaultId; } +}; + +DeviceList gDeviceList; + + +#if defined(ALSOFT_UWP) +enum EDataFlow { + eRender = 0, + eCapture = (eRender + 1), + eAll = (eCapture + 1), + EDataFlow_enum_count = (eAll + 1) +}; +#endif + +#if defined(ALSOFT_UWP) +using DeviceHandle = Windows::Devices::Enumeration::DeviceInformation; +using EventRegistrationToken = winrt::event_token; +#else +using DeviceHandle = ComPtr<IMMDevice>; +#endif using NameGUIDPair = std::pair<std::string,std::string>; -NameGUIDPair get_device_name_and_guid(IMMDevice *device) +static NameGUIDPair GetDeviceNameAndGuid(const DeviceHandle &device) { static constexpr char UnknownName[]{"Unknown Device Name"}; static constexpr char UnknownGuid[]{"Unknown Device GUID"}; +#if !defined(ALSOFT_UWP) std::string name, guid; ComPtr<IPropertyStore> ps; - HRESULT hr = device->OpenPropertyStore(STGM_READ, ps.getPtr()); + HRESULT hr{device->OpenPropertyStore(STGM_READ, al::out_ptr(ps))}; if(FAILED(hr)) { WARN("OpenPropertyStore failed: 0x%08lx\n", hr); @@ -207,42 +316,48 @@ NameGUIDPair get_device_name_and_guid(IMMDevice *device) } PropVariant pvprop; - hr = ps->GetValue(reinterpret_cast<const PROPERTYKEY&>(DEVPKEY_Device_FriendlyName), pvprop.get()); + hr = ps->GetValue(al::bit_cast<PROPERTYKEY>(DEVPKEY_Device_FriendlyName), pvprop.get()); if(FAILED(hr)) - { WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr); - name += UnknownName; - } else if(pvprop->vt == VT_LPWSTR) - name += wstr_to_utf8(pvprop->pwszVal); + name = wstr_to_utf8(pvprop->pwszVal); else - { - WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvprop->vt); - name += UnknownName; - } + WARN("Unexpected Device_FriendlyName PROPVARIANT type: 0x%04x\n", pvprop->vt); pvprop.clear(); - hr = ps->GetValue(reinterpret_cast<const PROPERTYKEY&>(PKEY_AudioEndpoint_GUID), pvprop.get()); + hr = ps->GetValue(al::bit_cast<PROPERTYKEY>(PKEY_AudioEndpoint_GUID), pvprop.get()); if(FAILED(hr)) - { WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr); - guid = UnknownGuid; - } else if(pvprop->vt == VT_LPWSTR) guid = wstr_to_utf8(pvprop->pwszVal); else - { - WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvprop->vt); - guid = UnknownGuid; + WARN("Unexpected AudioEndpoint_GUID PROPVARIANT type: 0x%04x\n", pvprop->vt); +#else + std::string name{wstr_to_utf8(device.Name())}; + std::string guid; + // device->Id is DeviceInterfacePath: \\?\SWD#MMDEVAPI#{0.0.0.00000000}.{a21c17a0-fc1d-405e-ab5a-b513422b57d1}#{e6327cad-dcec-4949-ae8a-991e976a79d2} + auto devIfPath = device.Id(); + if(auto devIdStart = wcsstr(devIfPath.data(), L"}.")) + { + devIdStart += 2; // L"}." + if(auto devIdStartEnd = wcschr(devIdStart, L'#')) + { + std::wstring wDevId{devIdStart, static_cast<size_t>(devIdStartEnd - devIdStart)}; + guid = wstr_to_utf8(wDevId.c_str()); + std::transform(guid.begin(), guid.end(), guid.begin(), + [](char ch) { return static_cast<char>(std::toupper(ch)); }); + } } - +#endif + if(name.empty()) name = UnknownName; + if(guid.empty()) guid = UnknownGuid; return std::make_pair(std::move(name), std::move(guid)); } - -EndpointFormFactor get_device_formfactor(IMMDevice *device) +#if !defined(ALSOFT_UWP) +EndpointFormFactor GetDeviceFormfactor(IMMDevice *device) { ComPtr<IPropertyStore> ps; - HRESULT hr{device->OpenPropertyStore(STGM_READ, ps.getPtr())}; + HRESULT hr{device->OpenPropertyStore(STGM_READ, al::out_ptr(ps))}; if(FAILED(hr)) { WARN("OpenPropertyStore failed: 0x%08lx\n", hr); @@ -260,90 +375,422 @@ EndpointFormFactor get_device_formfactor(IMMDevice *device) WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform->vt); return formfactor; } +#endif -void add_device(IMMDevice *device, const WCHAR *devid, al::vector<DevMap> &list) +#if defined(ALSOFT_UWP) +struct DeviceHelper final : public IActivateAudioInterfaceCompletionHandler +#else +struct DeviceHelper final : private IMMNotificationClient +#endif { - for(auto &entry : list) + DeviceHelper() { - if(entry.devid == devid) - return; +#if defined(ALSOFT_UWP) + /* TODO: UWP also needs to watch for device added/removed events and + * dynamically add/remove devices from the lists. + */ + mActiveClientEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr); + + mRenderDeviceChangedToken = MediaDevice::DefaultAudioRenderDeviceChanged([this](const IInspectable& /*sender*/, const DefaultAudioRenderDeviceChangedEventArgs& args) { + if (args.Role() == AudioDeviceRole::Default) + { + const std::string msg{ "Default playback device changed: " + + wstr_to_utf8(args.Id())}; + alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback, + msg); + } + }); + + mCaptureDeviceChangedToken = MediaDevice::DefaultAudioCaptureDeviceChanged([this](const IInspectable& /*sender*/, const DefaultAudioCaptureDeviceChangedEventArgs& args) { + if (args.Role() == AudioDeviceRole::Default) + { + const std::string msg{ "Default capture device changed: " + + wstr_to_utf8(args.Id()) }; + alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture, + msg); + } + }); +#endif + } + ~DeviceHelper() + { +#if defined(ALSOFT_UWP) + MediaDevice::DefaultAudioRenderDeviceChanged(mRenderDeviceChangedToken); + MediaDevice::DefaultAudioCaptureDeviceChanged(mCaptureDeviceChangedToken); + + if(mActiveClientEvent != nullptr) + CloseHandle(mActiveClientEvent); + mActiveClientEvent = nullptr; +#else + if(mEnumerator) + mEnumerator->UnregisterEndpointNotificationCallback(this); + mEnumerator = nullptr; +#endif } - auto name_guid = get_device_name_and_guid(device); + /** -------------------------- IUnknown ----------------------------- */ + std::atomic<ULONG> mRefCount{1}; + STDMETHODIMP_(ULONG) AddRef() noexcept override { return mRefCount.fetch_add(1u) + 1u; } + STDMETHODIMP_(ULONG) Release() noexcept override { return mRefCount.fetch_sub(1u) - 1u; } + + STDMETHODIMP QueryInterface(const IID& IId, void **UnknownPtrPtr) noexcept override + { + // Three rules of QueryInterface: + // https://docs.microsoft.com/en-us/windows/win32/com/rules-for-implementing-queryinterface + // 1. Objects must have identity. + // 2. The set of interfaces on an object instance must be static. + // 3. It must be possible to query successfully for any interface on an object from any other interface. + + // If ppvObject(the address) is nullptr, then this method returns E_POINTER. + if(!UnknownPtrPtr) + return E_POINTER; + + // https://docs.microsoft.com/en-us/windows/win32/com/implementing-reference-counting + // Whenever a client calls a method(or API function), such as QueryInterface, that returns a new interface + // pointer, the method being called is responsible for incrementing the reference count through the returned + // pointer. For example, when a client first creates an object, it receives an interface pointer to an object + // that, from the client's point of view, has a reference count of one. If the client then calls AddRef on the + // interface pointer, the reference count becomes two. The client must call Release twice on the interface + // pointer to drop all of its references to the object. +#if defined(ALSOFT_UWP) + if(IId == __uuidof(IActivateAudioInterfaceCompletionHandler)) + { + *UnknownPtrPtr = static_cast<IActivateAudioInterfaceCompletionHandler*>(this); + AddRef(); + return S_OK; + } +#else + if(IId == __uuidof(IMMNotificationClient)) + { + *UnknownPtrPtr = static_cast<IMMNotificationClient*>(this); + AddRef(); + return S_OK; + } +#endif + else if(IId == __uuidof(IAgileObject) || IId == __uuidof(IUnknown)) + { + *UnknownPtrPtr = static_cast<IUnknown*>(this); + AddRef(); + return S_OK; + } + + // This method returns S_OK if the interface is supported, and E_NOINTERFACE otherwise. + *UnknownPtrPtr = nullptr; + return E_NOINTERFACE; + } - int count{1}; - std::string newname{name_guid.first}; - while(checkName(list, newname)) +#if defined(ALSOFT_UWP) + /** ----------------------- IActivateAudioInterfaceCompletionHandler ------------ */ + HRESULT ActivateCompleted(IActivateAudioInterfaceAsyncOperation*) override { - newname = name_guid.first; - newname += " #"; - newname += std::to_string(++count); + SetEvent(mActiveClientEvent); + + // Need to return S_OK + return S_OK; } - list.emplace_back(std::move(newname), std::move(name_guid.second), devid); - const DevMap &newentry = list.back(); +#else + /** ----------------------- IMMNotificationClient ------------ */ + STDMETHODIMP OnDeviceStateChanged(LPCWSTR /*pwstrDeviceId*/, DWORD /*dwNewState*/) noexcept override { return S_OK; } - TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", newentry.name.c_str(), - newentry.endpoint_guid.c_str(), newentry.devid.c_str()); -} + STDMETHODIMP OnDeviceAdded(LPCWSTR pwstrDeviceId) noexcept override + { + ComPtr<IMMDevice> device; + HRESULT hr{mEnumerator->GetDevice(pwstrDeviceId, al::out_ptr(device))}; + if(FAILED(hr)) + { + ERR("Failed to get device: 0x%08lx\n", hr); + return S_OK; + } -WCHAR *get_device_id(IMMDevice *device) -{ - WCHAR *devid; + ComPtr<IMMEndpoint> endpoint; + hr = device->QueryInterface(__uuidof(IMMEndpoint), al::out_ptr(endpoint)); + if(FAILED(hr)) + { + ERR("Failed to get device endpoint: 0x%08lx\n", hr); + return S_OK; + } - const HRESULT hr{device->GetId(&devid)}; - if(FAILED(hr)) + EDataFlow flowdir{}; + hr = endpoint->GetDataFlow(&flowdir); + if(FAILED(hr)) + { + ERR("Failed to get endpoint data flow: 0x%08lx\n", hr); + return S_OK; + } + + auto devlock = DeviceListLock{gDeviceList}; + auto &list = (flowdir==eRender) ? devlock.getPlaybackList() : devlock.getCaptureList(); + + if(AddDevice(device, pwstrDeviceId, list)) + { + const auto devtype = (flowdir==eRender) ? alc::DeviceType::Playback + : alc::DeviceType::Capture; + const std::string msg{"Device added: "+list.back().name}; + alc::Event(alc::EventType::DeviceAdded, devtype, msg); + } + + return S_OK; + } + + STDMETHODIMP OnDeviceRemoved(LPCWSTR pwstrDeviceId) noexcept override { - ERR("Failed to get device id: %lx\n", hr); - return nullptr; + auto devlock = DeviceListLock{gDeviceList}; + for(auto flowdir : std::array{eRender, eCapture}) + { + auto &list = (flowdir==eRender) ? devlock.getPlaybackList() : devlock.getCaptureList(); + auto devtype = (flowdir==eRender)?alc::DeviceType::Playback : alc::DeviceType::Capture; + + /* Find the ID in the list to remove. */ + auto iter = std::find_if(list.begin(), list.end(), + [pwstrDeviceId](const DevMap &entry) noexcept + { return pwstrDeviceId == entry.devid; }); + if(iter == list.end()) continue; + + TRACE("Removing device \"%s\", \"%s\", \"%ls\"\n", iter->name.c_str(), + iter->endpoint_guid.c_str(), iter->devid.c_str()); + + std::string msg{"Device removed: "+std::move(iter->name)}; + list.erase(iter); + + alc::Event(alc::EventType::DeviceRemoved, devtype, msg); + } + return S_OK; } - return devid; -} + STDMETHODIMP OnPropertyValueChanged(LPCWSTR /*pwstrDeviceId*/, const PROPERTYKEY /*key*/) noexcept override { return S_OK; } -void probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, al::vector<DevMap> &list) -{ - al::vector<DevMap>{}.swap(list); + STDMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId) noexcept override + { + if(role != eMultimedia) + return S_OK; - ComPtr<IMMDeviceCollection> coll; - HRESULT hr{devenum->EnumAudioEndpoints(flowdir, DEVICE_STATE_ACTIVE, coll.getPtr())}; - if(FAILED(hr)) + const std::wstring_view devid{pwstrDefaultDeviceId ? pwstrDefaultDeviceId + : std::wstring_view{}}; + if(flow == eRender) + { + DeviceListLock{gDeviceList}.setPlaybackDefaultId(devid); + const std::string msg{"Default playback device changed: " + wstr_to_utf8(devid)}; + alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback, msg); + } + else if(flow == eCapture) + { + DeviceListLock{gDeviceList}.setCaptureDefaultId(devid); + const std::string msg{"Default capture device changed: " + wstr_to_utf8(devid)}; + alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture, msg); + } + return S_OK; + } +#endif + + /** -------------------------- DeviceHelper ----------------------------- */ + HRESULT init() { - ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr); - return; +#if !defined(ALSOFT_UWP) + HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, + __uuidof(IMMDeviceEnumerator), al::out_ptr(mEnumerator))}; + if(SUCCEEDED(hr)) + mEnumerator->RegisterEndpointNotificationCallback(this); + else + WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr); + return hr; +#else + return S_OK; +#endif } - UINT count{0}; - hr = coll->GetCount(&count); - if(SUCCEEDED(hr) && count > 0) - list.reserve(count); + HRESULT openDevice(std::wstring_view devid, EDataFlow flow, DeviceHandle& device) + { +#if !defined(ALSOFT_UWP) + HRESULT hr{E_FAIL}; + if(mEnumerator) + { + if(devid.empty()) + hr = mEnumerator->GetDefaultAudioEndpoint(flow, eMultimedia, al::out_ptr(device)); + else + hr = mEnumerator->GetDevice(devid.data(), al::out_ptr(device)); + } + return hr; +#else + const auto deviceRole = Windows::Media::Devices::AudioDeviceRole::Default; + auto devIfPath = + devid.empty() ? (flow == eRender ? MediaDevice::GetDefaultAudioRenderId(deviceRole) : MediaDevice::GetDefaultAudioCaptureId(deviceRole)) + : winrt::hstring(devid.data()); + if (devIfPath.empty()) + return E_POINTER; + + auto&& deviceInfo = DeviceInformation::CreateFromIdAsync(devIfPath, nullptr, DeviceInformationKind::DeviceInterface).get(); + if (!deviceInfo) + return E_NOINTERFACE; + device = deviceInfo; + return S_OK; +#endif + } - ComPtr<IMMDevice> device; - hr = devenum->GetDefaultAudioEndpoint(flowdir, eMultimedia, device.getPtr()); - if(SUCCEEDED(hr)) +#if !defined(ALSOFT_UWP) + static HRESULT activateAudioClient(_In_ DeviceHandle &device, REFIID iid, void **ppv) + { return device->Activate(iid, CLSCTX_INPROC_SERVER, nullptr, ppv); } +#else + HRESULT activateAudioClient(_In_ DeviceHandle &device, _In_ REFIID iid, void **ppv) { - if(WCHAR *devid{get_device_id(device.get())}) + ComPtr<IActivateAudioInterfaceAsyncOperation> asyncOp; + HRESULT hr{ActivateAudioInterfaceAsync(device.Id().data(), iid, nullptr, this, + al::out_ptr(asyncOp))}; + if(FAILED(hr)) + return hr; + + /* I don't like waiting for INFINITE time, but the activate operation + * can take an indefinite amount of time since it can require user + * input. + */ + DWORD res{WaitForSingleObjectEx(mActiveClientEvent, INFINITE, FALSE)}; + if(res != WAIT_OBJECT_0) { - add_device(device.get(), devid, list); - CoTaskMemFree(devid); + ERR("WaitForSingleObjectEx error: 0x%lx\n", res); + return E_FAIL; } - device = nullptr; + + HRESULT hrActivateRes{E_FAIL}; + ComPtr<IUnknown> punkAudioIface; + hr = asyncOp->GetActivateResult(&hrActivateRes, al::out_ptr(punkAudioIface)); + if(SUCCEEDED(hr)) hr = hrActivateRes; + if(FAILED(hr)) return hr; + + return punkAudioIface->QueryInterface(iid, ppv); } +#endif - for(UINT i{0};i < count;++i) + std::wstring probeDevices(EDataFlow flowdir, std::vector<DevMap> &list) { - hr = coll->Item(i, device.getPtr()); - if(FAILED(hr)) continue; + std::wstring defaultId; + std::vector<DevMap>{}.swap(list); + +#if !defined(ALSOFT_UWP) + ComPtr<IMMDeviceCollection> coll; + HRESULT hr{mEnumerator->EnumAudioEndpoints(flowdir, DEVICE_STATE_ACTIVE, + al::out_ptr(coll))}; + if(FAILED(hr)) + { + ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr); + return defaultId; + } + + UINT count{0}; + hr = coll->GetCount(&count); + if(SUCCEEDED(hr) && count > 0) + list.reserve(count); + + ComPtr<IMMDevice> device; + hr = mEnumerator->GetDefaultAudioEndpoint(flowdir, eMultimedia, al::out_ptr(device)); + if(SUCCEEDED(hr)) + { + if(WCHAR *devid{GetDeviceId(device.get())}) + { + defaultId = devid; + CoTaskMemFree(devid); + } + device = nullptr; + } + + for(UINT i{0};i < count;++i) + { + hr = coll->Item(i, al::out_ptr(device)); + if(FAILED(hr)) + continue; - if(WCHAR *devid{get_device_id(device.get())}) + if(WCHAR *devid{GetDeviceId(device.get())}) + { + std::ignore = AddDevice(device, devid, list); + CoTaskMemFree(devid); + } + device = nullptr; + } +#else + const auto deviceRole = Windows::Media::Devices::AudioDeviceRole::Default; + auto DefaultAudioId = flowdir == eRender ? MediaDevice::GetDefaultAudioRenderId(deviceRole) + : MediaDevice::GetDefaultAudioCaptureId(deviceRole); + if(!DefaultAudioId.empty()) { - add_device(device.get(), devid, list); - CoTaskMemFree(devid); + auto deviceInfo = DeviceInformation::CreateFromIdAsync(DefaultAudioId, nullptr, + DeviceInformationKind::DeviceInterface).get(); + if(deviceInfo) + defaultId = deviceInfo.Id().data(); } - device = nullptr; + + // Get the string identifier of the audio renderer + auto AudioSelector = flowdir == eRender ? MediaDevice::GetAudioRenderSelector() : MediaDevice::GetAudioCaptureSelector(); + + // Setup the asynchronous callback + auto&& DeviceInfoCollection = DeviceInformation::FindAllAsync(AudioSelector, /*PropertyList*/nullptr, DeviceInformationKind::DeviceInterface).get(); + if(DeviceInfoCollection) + { + try { + auto deviceCount = DeviceInfoCollection.Size(); + for(unsigned int i{0};i < deviceCount;++i) + { + auto deviceInfo = DeviceInfoCollection.GetAt(i); + if(deviceInfo) + std::ignore = AddDevice(deviceInfo, deviceInfo.Id().data(), list); + } + } + catch (const winrt::hresult_error& /*ex*/) { + } + } +#endif + + return defaultId; } -} +private: + static bool AddDevice(const DeviceHandle &device, const WCHAR *devid, std::vector<DevMap> &list) + { + for(auto &entry : list) + { + if(entry.devid == devid) + return false; + } + + auto name_guid = GetDeviceNameAndGuid(device); + int count{1}; + std::string newname{name_guid.first}; + while(checkName(list, newname)) + { + newname = name_guid.first; + newname += " #"; + newname += std::to_string(++count); + } + list.emplace_back(std::move(newname), std::move(name_guid.second), devid); + const DevMap &newentry = list.back(); + + TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", newentry.name.c_str(), + newentry.endpoint_guid.c_str(), newentry.devid.c_str()); + return true; + } + +#if !defined(ALSOFT_UWP) + static WCHAR *GetDeviceId(IMMDevice *device) + { + WCHAR *devid; + + const HRESULT hr{device->GetId(&devid)}; + if(FAILED(hr)) + { + ERR("Failed to get device id: %lx\n", hr); + return nullptr; + } + + return devid; + } + ComPtr<IMMDeviceEnumerator> mEnumerator{nullptr}; + +#else + + HANDLE mActiveClientEvent{nullptr}; + + EventRegistrationToken mRenderDeviceChangedToken; + EventRegistrationToken mCaptureDeviceChangedToken; +#endif +}; bool MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in) { @@ -430,51 +877,51 @@ enum class MsgType { StartDevice, StopDevice, CloseDevice, - EnumeratePlayback, - EnumerateCapture, - Count, - QuitThread = Count + QuitThread }; -constexpr char MessageStr[static_cast<size_t>(MsgType::Count)][20]{ - "Open Device", - "Reset Device", - "Start Device", - "Stop Device", - "Close Device", - "Enumerate Playback", - "Enumerate Capture" -}; +constexpr const char *GetMessageTypeName(MsgType type) noexcept +{ + switch(type) + { + case MsgType::OpenDevice: return "Open Device"; + case MsgType::ResetDevice: return "Reset Device"; + case MsgType::StartDevice: return "Start Device"; + case MsgType::StopDevice: return "Stop Device"; + case MsgType::CloseDevice: return "Close Device"; + case MsgType::QuitThread: break; + } + return ""; +} /* Proxy interface used by the message handler. */ struct WasapiProxy { virtual ~WasapiProxy() = default; - virtual HRESULT openProxy(const char *name) = 0; + virtual HRESULT openProxy(std::string_view name) = 0; virtual void closeProxy() = 0; virtual HRESULT resetProxy() = 0; virtual HRESULT startProxy() = 0; - virtual void stopProxy() = 0; + virtual void stopProxy() = 0; struct Msg { MsgType mType; WasapiProxy *mProxy; - const char *mParam; + std::string_view mParam; std::promise<HRESULT> mPromise; explicit operator bool() const noexcept { return mType != MsgType::QuitThread; } }; - static std::thread sThread; - static std::deque<Msg> mMsgQueue; - static std::mutex mMsgQueueLock; - static std::condition_variable mMsgQueueCond; - static std::mutex sThreadLock; - static size_t sInitCount; + static inline std::deque<Msg> mMsgQueue; + static inline std::mutex mMsgQueueLock; + static inline std::condition_variable mMsgQueueCond; + + static inline std::optional<DeviceHelper> sDeviceHelper; - std::future<HRESULT> pushMessage(MsgType type, const char *param=nullptr) + std::future<HRESULT> pushMessage(MsgType type, std::string_view param={}) { std::promise<HRESULT> promise; std::future<HRESULT> future{promise.get_future()}; @@ -492,7 +939,7 @@ struct WasapiProxy { std::future<HRESULT> future{promise.get_future()}; { std::lock_guard<std::mutex> _{mMsgQueueLock}; - mMsgQueue.emplace_back(Msg{type, nullptr, nullptr, std::move(promise)}); + mMsgQueue.emplace_back(Msg{type, nullptr, {}, std::move(promise)}); } mMsgQueueCond.notify_one(); return future; @@ -508,65 +955,41 @@ struct WasapiProxy { } static int messageHandler(std::promise<HRESULT> *promise); - - static HRESULT InitThread() - { - std::lock_guard<std::mutex> _{sThreadLock}; - HRESULT res{S_OK}; - if(!sThread.joinable()) - { - std::promise<HRESULT> promise; - auto future = promise.get_future(); - - sThread = std::thread{&WasapiProxy::messageHandler, &promise}; - res = future.get(); - if(FAILED(res)) - { - sThread.join(); - return res; - } - } - ++sInitCount; - return res; - } - - static void DeinitThread() - { - std::lock_guard<std::mutex> _{sThreadLock}; - if(!--sInitCount && sThread.joinable()) - { - pushMessageStatic(MsgType::QuitThread); - sThread.join(); - } - } }; -std::thread WasapiProxy::sThread; -std::deque<WasapiProxy::Msg> WasapiProxy::mMsgQueue; -std::mutex WasapiProxy::mMsgQueueLock; -std::condition_variable WasapiProxy::mMsgQueueCond; -std::mutex WasapiProxy::sThreadLock; -size_t WasapiProxy::sInitCount{0}; int WasapiProxy::messageHandler(std::promise<HRESULT> *promise) { TRACE("Starting message thread\n"); - HRESULT hr{CoInitializeEx(nullptr, COINIT_MULTITHREADED)}; - if(FAILED(hr)) + ComWrapper com{COINIT_MULTITHREADED}; + if(!com) { - WARN("Failed to initialize COM: 0x%08lx\n", hr); - promise->set_value(hr); + WARN("Failed to initialize COM: 0x%08lx\n", com.status()); + promise->set_value(com.status()); return 0; } - promise->set_value(S_OK); + + HRESULT hr{sDeviceHelper.emplace().init()}; + promise->set_value(hr); promise = nullptr; + if(FAILED(hr)) + goto skip_loop; + + { + auto devlock = DeviceListLock{gDeviceList}; + auto defaultId = sDeviceHelper->probeDevices(eRender, devlock.getPlaybackList()); + if(!defaultId.empty()) devlock.setPlaybackDefaultId(defaultId); + defaultId = sDeviceHelper->probeDevices(eCapture, devlock.getCaptureList()); + if(!defaultId.empty()) devlock.setCaptureDefaultId(defaultId); + } TRACE("Starting message loop\n"); while(Msg msg{popMessage()}) { - TRACE("Got message \"%s\" (0x%04x, this=%p, param=%p)\n", - MessageStr[static_cast<size_t>(msg.mType)], static_cast<uint>(msg.mType), - static_cast<void*>(msg.mProxy), static_cast<const void*>(msg.mParam)); + TRACE("Got message \"%s\" (0x%04x, this=%p, param=\"%.*s\")\n", + GetMessageTypeName(msg.mType), static_cast<uint>(msg.mType), + static_cast<void*>(msg.mProxy), static_cast<int>(msg.mParam.length()), + msg.mParam.data()); switch(msg.mType) { @@ -595,27 +1018,6 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise) msg.mPromise.set_value(S_OK); continue; - case MsgType::EnumeratePlayback: - case MsgType::EnumerateCapture: - { - void *ptr{}; - hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, - IID_IMMDeviceEnumerator, &ptr); - if(FAILED(hr)) - msg.mPromise.set_value(hr); - else - { - ComPtr<IMMDeviceEnumerator> devenum{static_cast<IMMDeviceEnumerator*>(ptr)}; - - if(msg.mType == MsgType::EnumeratePlayback) - probe_devices(devenum.get(), eRender, PlaybackDevices); - else if(msg.mType == MsgType::EnumerateCapture) - probe_devices(devenum.get(), eCapture, CaptureDevices); - msg.mPromise.set_value(S_OK); - } - continue; - } - case MsgType::QuitThread: break; } @@ -623,20 +1025,22 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise) msg.mPromise.set_value(E_FAIL); } TRACE("Message loop finished\n"); - CoUninitialize(); + +skip_loop: + sDeviceHelper.reset(); return 0; } - struct WasapiPlayback final : public BackendBase, WasapiProxy { WasapiPlayback(DeviceBase *device) noexcept : BackendBase{device} { } ~WasapiPlayback() override; int mixerProc(); + int mixerSpatialProc(); - void open(const char *name) override; - HRESULT openProxy(const char *name) override; + void open(std::string_view name) override; + HRESULT openProxy(std::string_view name) override; void closeProxy() override; bool reset() override; @@ -648,10 +1052,22 @@ struct WasapiPlayback final : public BackendBase, WasapiProxy { ClockLatency getClockLatency() override; + void prepareFormat(WAVEFORMATEXTENSIBLE &OutputType); + void finalizeFormat(WAVEFORMATEXTENSIBLE &OutputType); + HRESULT mOpenStatus{E_FAIL}; - ComPtr<IMMDevice> mMMDev{nullptr}; - ComPtr<IAudioClient> mClient{nullptr}; - ComPtr<IAudioRenderClient> mRender{nullptr}; + DeviceHandle mMMDev{nullptr}; + + struct PlainDevice { + ComPtr<IAudioClient> mClient{nullptr}; + ComPtr<IAudioRenderClient> mRender{nullptr}; + }; + struct SpatialDevice { + ComPtr<ISpatialAudioClient> mClient{nullptr}; + ComPtr<ISpatialAudioObjectRenderStream> mRender{nullptr}; + AudioObjectType mStaticMask{}; + }; + std::variant<std::monostate,PlainDevice,SpatialDevice> mAudio; HANDLE mNotifyEvent{nullptr}; UINT32 mOrigBufferSize{}, mOrigUpdateSize{}; @@ -673,10 +1089,7 @@ struct WasapiPlayback final : public BackendBase, WasapiProxy { WasapiPlayback::~WasapiPlayback() { if(SUCCEEDED(mOpenStatus)) - { pushMessage(MsgType::CloseDevice).wait(); - DeinitThread(); - } mOpenStatus = E_FAIL; if(mNotifyEvent != nullptr) @@ -687,24 +1100,29 @@ WasapiPlayback::~WasapiPlayback() FORCE_ALIGN int WasapiPlayback::mixerProc() { - HRESULT hr{CoInitializeEx(nullptr, COINIT_MULTITHREADED)}; - if(FAILED(hr)) + ComWrapper com{COINIT_MULTITHREADED}; + if(!com) { - ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr); - mDevice->handleDisconnect("COM init failed: 0x%08lx", hr); + ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", com.status()); + mDevice->handleDisconnect("COM init failed: 0x%08lx", com.status()); return 1; } + auto &audio = std::get<PlainDevice>(mAudio); + SetRTPriority(); althrd_setname(MIXER_THREAD_NAME); const uint frame_size{mFormat.Format.nChannels * mFormat.Format.wBitsPerSample / 8u}; const uint update_size{mOrigUpdateSize}; const UINT32 buffer_len{mOrigBufferSize}; + const void *resbufferptr{}; + + mBufferFilled = 0; while(!mKillNow.load(std::memory_order_relaxed)) { UINT32 written; - hr = mClient->GetCurrentPadding(&written); + HRESULT hr{audio.mClient->GetCurrentPadding(&written)}; if(FAILED(hr)) { ERR("Failed to get padding: 0x%08lx\n", hr); @@ -723,7 +1141,7 @@ FORCE_ALIGN int WasapiPlayback::mixerProc() } BYTE *buffer; - hr = mRender->GetBuffer(len, &buffer); + hr = audio.mRender->GetBuffer(len, &buffer); if(SUCCEEDED(hr)) { if(mResampler) @@ -735,22 +1153,15 @@ FORCE_ALIGN int WasapiPlayback::mixerProc() { mDevice->renderSamples(mResampleBuffer.get(), mDevice->UpdateSize, mFormat.Format.nChannels); + resbufferptr = mResampleBuffer.get(); mBufferFilled = mDevice->UpdateSize; } - const void *src{mResampleBuffer.get()}; - uint srclen{mBufferFilled}; - uint got{mResampler->convert(&src, &srclen, buffer, len-done)}; + uint got{mResampler->convert(&resbufferptr, &mBufferFilled, buffer, len-done)}; buffer += got*frame_size; done += got; mPadding.store(written + done, std::memory_order_relaxed); - if(srclen) - { - const char *bsrc{static_cast<const char*>(src)}; - std::copy(bsrc, bsrc + srclen*frame_size, mResampleBuffer.get()); - } - mBufferFilled = srclen; } } else @@ -759,7 +1170,7 @@ FORCE_ALIGN int WasapiPlayback::mixerProc() mDevice->renderSamples(buffer, len, mFormat.Format.nChannels); mPadding.store(written + len, std::memory_order_relaxed); } - hr = mRender->ReleaseBuffer(len, 0); + hr = audio.mRender->ReleaseBuffer(len, 0); } if(FAILED(hr)) { @@ -770,12 +1181,126 @@ FORCE_ALIGN int WasapiPlayback::mixerProc() } mPadding.store(0u, std::memory_order_release); - CoUninitialize(); + return 0; +} + +FORCE_ALIGN int WasapiPlayback::mixerSpatialProc() +{ + ComWrapper com{COINIT_MULTITHREADED}; + if(!com) + { + ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", com.status()); + mDevice->handleDisconnect("COM init failed: 0x%08lx", com.status()); + return 1; + } + + auto &audio = std::get<SpatialDevice>(mAudio); + + SetRTPriority(); + althrd_setname(MIXER_THREAD_NAME); + + std::vector<ComPtr<ISpatialAudioObject>> channels; + std::vector<float*> buffers; + std::vector<float*> resbuffers; + std::vector<const void*> tmpbuffers; + + /* TODO: Set mPadding appropriately. There doesn't seem to be a way to + * update it dynamically based on the stream, so a fixed size may be the + * best we can do. + */ + mPadding.store(mOrigBufferSize-mOrigUpdateSize, std::memory_order_release); + + mBufferFilled = 0; + while(!mKillNow.load(std::memory_order_relaxed)) + { + if(DWORD res{WaitForSingleObjectEx(mNotifyEvent, 1000, FALSE)}; res != WAIT_OBJECT_0) + { + ERR("WaitForSingleObjectEx error: 0x%lx\n", res); + + HRESULT hr{audio.mRender->Reset()}; + if(FAILED(hr)) + { + ERR("ISpatialAudioObjectRenderStream::Reset failed: 0x%08lx\n", hr); + mDevice->handleDisconnect("Device lost: 0x%08lx", hr); + break; + } + } + + UINT32 dynamicCount{}, framesToDo{}; + HRESULT hr{audio.mRender->BeginUpdatingAudioObjects(&dynamicCount, &framesToDo)}; + if(SUCCEEDED(hr)) + { + if(channels.empty()) UNLIKELY + { + auto flags = as_unsigned(audio.mStaticMask); + channels.reserve(as_unsigned(al::popcount(flags))); + while(flags) + { + auto id = decltype(flags){1} << al::countr_zero(flags); + flags &= ~id; + + channels.emplace_back(); + audio.mRender->ActivateSpatialAudioObject(static_cast<AudioObjectType>(id), + al::out_ptr(channels.back())); + } + buffers.resize(channels.size()); + if(mResampler) + { + tmpbuffers.resize(buffers.size()); + resbuffers.resize(buffers.size()); + for(size_t i{0};i < tmpbuffers.size();++i) + resbuffers[i] = reinterpret_cast<float*>(mResampleBuffer.get()) + + mDevice->UpdateSize*i; + } + } + + /* We have to call to get each channel's buffer individually every + * update, unfortunately. + */ + std::transform(channels.cbegin(), channels.cend(), buffers.begin(), + [](const ComPtr<ISpatialAudioObject> &obj) -> float* + { + BYTE *buffer{}; + UINT32 size{}; + obj->GetBuffer(&buffer, &size); + return reinterpret_cast<float*>(buffer); + }); + + if(!mResampler) + mDevice->renderSamples(buffers, framesToDo); + else + { + std::lock_guard<std::mutex> _{mMutex}; + for(UINT32 pos{0};pos < framesToDo;) + { + if(mBufferFilled == 0) + { + mDevice->renderSamples(resbuffers, mDevice->UpdateSize); + std::copy(resbuffers.cbegin(), resbuffers.cend(), tmpbuffers.begin()); + mBufferFilled = mDevice->UpdateSize; + } + + const uint got{mResampler->convertPlanar(tmpbuffers.data(), &mBufferFilled, + reinterpret_cast<void*const*>(buffers.data()), framesToDo-pos)}; + for(auto &buf : buffers) + buf += got; + pos += got; + } + } + + hr = audio.mRender->EndUpdatingAudioObjects(); + } + + if(FAILED(hr)) + ERR("Failed to update playback objects: 0x%08lx\n", hr); + } + mPadding.store(0u, std::memory_order_release); + return 0; } -void WasapiPlayback::open(const char *name) +void WasapiPlayback::open(std::string_view name) { if(SUCCEEDED(mOpenStatus)) throw al::backend_exception{al::backend_error::DeviceError, @@ -789,132 +1314,69 @@ void WasapiPlayback::open(const char *name) "Failed to create notify events"}; } - HRESULT hr{InitThread()}; - if(FAILED(hr)) - { - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to init COM thread: 0x%08lx", hr}; - } - - if(name) + if(name.length() >= DevNameHeadLen + && std::strncmp(name.data(), DevNameHead, DevNameHeadLen) == 0) { - if(PlaybackDevices.empty()) - pushMessage(MsgType::EnumeratePlayback); - if(std::strncmp(name, DevNameHead, DevNameHeadLen) == 0) - { - name += DevNameHeadLen; - if(*name == '\0') - name = nullptr; - } + name = name.substr(DevNameHeadLen); } mOpenStatus = pushMessage(MsgType::OpenDevice, name).get(); if(FAILED(mOpenStatus)) - { - DeinitThread(); throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx", mOpenStatus}; - } } -HRESULT WasapiPlayback::openProxy(const char *name) +HRESULT WasapiPlayback::openProxy(std::string_view name) { - const wchar_t *devid{nullptr}; - if(name) + std::string devname; + std::wstring devid; + if(!name.empty()) { - auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(), + auto devlock = DeviceListLock{gDeviceList}; + auto list = al::span{devlock.getPlaybackList()}; + auto iter = std::find_if(list.cbegin(), list.cend(), [name](const DevMap &entry) -> bool { return entry.name == name || entry.endpoint_guid == name; }); - if(iter == PlaybackDevices.cend()) + if(iter == list.cend()) { const std::wstring wname{utf8_to_wstr(name)}; - iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(), + iter = std::find_if(list.cbegin(), list.cend(), [&wname](const DevMap &entry) -> bool { return entry.devid == wname; }); } - if(iter == PlaybackDevices.cend()) + if(iter == list.cend()) { - WARN("Failed to find device name matching \"%s\"\n", name); + WARN("Failed to find device name matching \"%.*s\"\n", static_cast<int>(name.length()), + name.data()); return E_FAIL; } - name = iter->name.c_str(); - devid = iter->devid.c_str(); + devname = iter->name; + devid = iter->devid; } - void *ptr; - ComPtr<IMMDevice> mmdev; - HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, - IID_IMMDeviceEnumerator, &ptr)}; - if(SUCCEEDED(hr)) - { - ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)}; - if(!devid) - hr = enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, mmdev.getPtr()); - else - hr = enumerator->GetDevice(devid, mmdev.getPtr()); - } + HRESULT hr{sDeviceHelper->openDevice(devid, eRender, mMMDev)}; if(FAILED(hr)) { - WARN("Failed to open device \"%s\"\n", name?name:"(default)"); + WARN("Failed to open device \"%s\"\n", devname.empty() ? "(default)" : devname.c_str()); return hr; } + if(!devname.empty()) + mDevice->DeviceName = DevNameHead + std::move(devname); + else + mDevice->DeviceName = DevNameHead + GetDeviceNameAndGuid(mMMDev).first; - mClient = nullptr; - mMMDev = std::move(mmdev); - if(name) mDevice->DeviceName = std::string{DevNameHead} + name; - else mDevice->DeviceName = DevNameHead + get_device_name_and_guid(mMMDev.get()).first; - - return hr; + return S_OK; } void WasapiPlayback::closeProxy() { - mClient = nullptr; + mAudio.emplace<std::monostate>(); mMMDev = nullptr; } -bool WasapiPlayback::reset() -{ - HRESULT hr{pushMessage(MsgType::ResetDevice).get()}; - if(FAILED(hr)) - throw al::backend_exception{al::backend_error::DeviceError, "0x%08lx", hr}; - return true; -} - -HRESULT WasapiPlayback::resetProxy() +void WasapiPlayback::prepareFormat(WAVEFORMATEXTENSIBLE &OutputType) { - mClient = nullptr; - - void *ptr; - HRESULT hr{mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr)}; - if(FAILED(hr)) - { - ERR("Failed to reactivate audio client: 0x%08lx\n", hr); - return hr; - } - mClient = ComPtr<IAudioClient>{static_cast<IAudioClient*>(ptr)}; - - WAVEFORMATEX *wfx; - hr = mClient->GetMixFormat(&wfx); - if(FAILED(hr)) - { - ERR("Failed to get mix format: 0x%08lx\n", hr); - return hr; - } - TraceFormat("Device mix format", wfx); - - WAVEFORMATEXTENSIBLE OutputType; - if(!MakeExtensible(&OutputType, wfx)) - { - CoTaskMemFree(wfx); - return E_FAIL; - } - CoTaskMemFree(wfx); - wfx = nullptr; - - const ReferenceTime per_time{ReferenceTime{seconds{mDevice->UpdateSize}} / mDevice->Frequency}; - const ReferenceTime buf_time{ReferenceTime{seconds{mDevice->BufferSize}} / mDevice->Frequency}; bool isRear51{false}; if(!mDevice->Flags.test(FrequencyRequest)) @@ -928,7 +1390,7 @@ HRESULT WasapiPlayback::resetProxy() const uint32_t chancount{OutputType.Format.nChannels}; const DWORD chanmask{OutputType.dwChannelMask}; if(chancount >= 12 && (chanmask&X714Mask) == X7DOT1DOT4) - mDevice->FmtChans = DevFmtX71; + mDevice->FmtChans = DevFmtX714; else if(chancount >= 8 && (chanmask&X71Mask) == X7DOT1) mDevice->FmtChans = DevFmtX71; else if(chancount >= 7 && (chanmask&X61Mask) == X6DOT1) @@ -1030,141 +1492,426 @@ HRESULT WasapiPlayback::resetProxy() OutputType.Format.wBitsPerSample / 8); OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec * OutputType.Format.nBlockAlign; +} - TraceFormat("Requesting playback format", &OutputType.Format); - hr = mClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx); - if(FAILED(hr)) +void WasapiPlayback::finalizeFormat(WAVEFORMATEXTENSIBLE &OutputType) +{ + if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "wasapi", "allow-resampler", true)) + mDevice->Frequency = OutputType.Format.nSamplesPerSec; + else + mDevice->Frequency = minu(mDevice->Frequency, OutputType.Format.nSamplesPerSec); + + const uint32_t chancount{OutputType.Format.nChannels}; + const DWORD chanmask{OutputType.dwChannelMask}; + /* Don't update the channel format if the requested format fits what's + * supported. + */ + bool chansok{false}; + if(mDevice->Flags.test(ChannelsRequest)) + { + /* When requesting a channel configuration, make sure it fits the + * mask's lsb (to ensure no gaps in the output channels). If there's no + * mask, assume the request fits with enough channels. + */ + switch(mDevice->FmtChans) + { + case DevFmtMono: + chansok = (chancount >= 1 && ((chanmask&MonoMask) == MONO || !chanmask)); + break; + case DevFmtStereo: + chansok = (chancount >= 2 && ((chanmask&StereoMask) == STEREO || !chanmask)); + break; + case DevFmtQuad: + chansok = (chancount >= 4 && ((chanmask&QuadMask) == QUAD || !chanmask)); + break; + case DevFmtX51: + chansok = (chancount >= 6 && ((chanmask&X51Mask) == X5DOT1 + || (chanmask&X51RearMask) == X5DOT1REAR || !chanmask)); + break; + case DevFmtX61: + chansok = (chancount >= 7 && ((chanmask&X61Mask) == X6DOT1 || !chanmask)); + break; + case DevFmtX71: + case DevFmtX3D71: + chansok = (chancount >= 8 && ((chanmask&X71Mask) == X7DOT1 || !chanmask)); + break; + case DevFmtX714: + chansok = (chancount >= 12 && ((chanmask&X714Mask) == X7DOT1DOT4 || !chanmask)); + case DevFmtAmbi3D: + break; + } + } + if(!chansok) { - WARN("Failed to check format support: 0x%08lx\n", hr); - hr = mClient->GetMixFormat(&wfx); + if(chancount >= 12 && (chanmask&X714Mask) == X7DOT1DOT4) + mDevice->FmtChans = DevFmtX714; + else if(chancount >= 8 && (chanmask&X71Mask) == X7DOT1) + mDevice->FmtChans = DevFmtX71; + else if(chancount >= 7 && (chanmask&X61Mask) == X6DOT1) + mDevice->FmtChans = DevFmtX61; + else if(chancount >= 6 && ((chanmask&X51Mask) == X5DOT1 + || (chanmask&X51RearMask) == X5DOT1REAR)) + mDevice->FmtChans = DevFmtX51; + else if(chancount >= 4 && (chanmask&QuadMask) == QUAD) + mDevice->FmtChans = DevFmtQuad; + else if(chancount >= 2 && ((chanmask&StereoMask) == STEREO || !chanmask)) + mDevice->FmtChans = DevFmtStereo; + else if(chancount >= 1 && ((chanmask&MonoMask) == MONO || !chanmask)) + mDevice->FmtChans = DevFmtMono; + else + { + ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, + OutputType.dwChannelMask); + mDevice->FmtChans = DevFmtStereo; + OutputType.Format.nChannels = 2; + OutputType.dwChannelMask = STEREO; + } } - if(FAILED(hr)) + + if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_PCM)) { - ERR("Failed to find a supported format: 0x%08lx\n", hr); - return hr; + if(OutputType.Format.wBitsPerSample == 8) + mDevice->FmtType = DevFmtUByte; + else if(OutputType.Format.wBitsPerSample == 16) + mDevice->FmtType = DevFmtShort; + else if(OutputType.Format.wBitsPerSample == 32) + mDevice->FmtType = DevFmtInt; + else + { + mDevice->FmtType = DevFmtShort; + OutputType.Format.wBitsPerSample = 16; + } + } + else if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) + { + mDevice->FmtType = DevFmtFloat; + OutputType.Format.wBitsPerSample = 32; + } + else + { + ERR("Unhandled format sub-type: %s\n", GuidPrinter{OutputType.SubFormat}.c_str()); + mDevice->FmtType = DevFmtShort; + if(OutputType.Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE) + OutputType.Format.wFormatTag = WAVE_FORMAT_PCM; + OutputType.Format.wBitsPerSample = 16; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; } + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; +} - if(wfx != nullptr) + +bool WasapiPlayback::reset() +{ + HRESULT hr{pushMessage(MsgType::ResetDevice).get()}; + if(FAILED(hr)) + throw al::backend_exception{al::backend_error::DeviceError, "0x%08lx", hr}; + return true; +} + +HRESULT WasapiPlayback::resetProxy() +{ + if(GetConfigValueBool(mDevice->DeviceName.c_str(), "wasapi", "spatial-api", false)) { - TraceFormat("Got playback format", wfx); - if(!MakeExtensible(&OutputType, wfx)) + auto &audio = mAudio.emplace<SpatialDevice>(); + HRESULT hr{sDeviceHelper->activateAudioClient(mMMDev, __uuidof(ISpatialAudioClient), + al::out_ptr(audio.mClient))}; + if(FAILED(hr)) { - CoTaskMemFree(wfx); - return E_FAIL; + ERR("Failed to activate spatial audio client: 0x%08lx\n", hr); + goto no_spatial; } - CoTaskMemFree(wfx); - wfx = nullptr; - if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "wasapi", "allow-resampler", true)) - mDevice->Frequency = OutputType.Format.nSamplesPerSec; - else - mDevice->Frequency = minu(mDevice->Frequency, OutputType.Format.nSamplesPerSec); + ComPtr<IAudioFormatEnumerator> fmtenum; + hr = audio.mClient->GetSupportedAudioObjectFormatEnumerator(al::out_ptr(fmtenum)); + if(FAILED(hr)) + { + ERR("Failed to get format enumerator: 0x%08lx\n", hr); + goto no_spatial; + } - const uint32_t chancount{OutputType.Format.nChannels}; - const DWORD chanmask{OutputType.dwChannelMask}; - /* Don't update the channel format if the requested format fits what's - * supported. - */ - bool chansok{false}; - if(mDevice->Flags.test(ChannelsRequest)) + UINT32 fmtcount{}; + hr = fmtenum->GetCount(&fmtcount); + if(FAILED(hr) || fmtcount == 0) { - /* When requesting a channel configuration, make sure it fits the - * mask's lsb (to ensure no gaps in the output channels). If - * there's no mask, assume the request fits with enough channels. - */ - switch(mDevice->FmtChans) + ERR("Failed to get format count: 0x%08lx\n", hr); + goto no_spatial; + } + + WAVEFORMATEX *preferredFormat{}; + hr = fmtenum->GetFormat(0, &preferredFormat); + if(FAILED(hr)) + { + ERR("Failed to get preferred format: 0x%08lx\n", hr); + goto no_spatial; + } + TraceFormat("Preferred mix format", preferredFormat); + + UINT32 maxFrames{}; + hr = audio.mClient->GetMaxFrameCount(preferredFormat, &maxFrames); + if(FAILED(hr)) + ERR("Failed to get max frames: 0x%08lx\n", hr); + else + TRACE("Max sample frames: %u\n", maxFrames); + for(UINT32 i{1};i < fmtcount;++i) + { + WAVEFORMATEX *otherFormat{}; + hr = fmtenum->GetFormat(i, &otherFormat); + if(FAILED(hr)) + ERR("Failed to format %u: 0x%08lx\n", i+1, hr); + else { - case DevFmtMono: - chansok = (chancount >= 1 && ((chanmask&MonoMask) == MONO || !chanmask)); - break; - case DevFmtStereo: - chansok = (chancount >= 2 && ((chanmask&StereoMask) == STEREO || !chanmask)); - break; - case DevFmtQuad: - chansok = (chancount >= 4 && ((chanmask&QuadMask) == QUAD || !chanmask)); - break; - case DevFmtX51: - chansok = (chancount >= 6 && ((chanmask&X51Mask) == X5DOT1 - || (chanmask&X51RearMask) == X5DOT1REAR || !chanmask)); - break; - case DevFmtX61: - chansok = (chancount >= 7 && ((chanmask&X61Mask) == X6DOT1 || !chanmask)); - break; - case DevFmtX71: - case DevFmtX3D71: - chansok = (chancount >= 8 && ((chanmask&X71Mask) == X7DOT1 || !chanmask)); - break; - case DevFmtX714: - chansok = (chancount >= 12 && ((chanmask&X714Mask) == X7DOT1DOT4 || !chanmask)); - case DevFmtAmbi3D: - break; + TraceFormat("Other mix format", otherFormat); + UINT32 otherMaxFrames{}; + hr = audio.mClient->GetMaxFrameCount(otherFormat, &otherMaxFrames); + if(FAILED(hr)) + ERR("Failed to get max frames: 0x%08lx\n", hr); + else + TRACE("Max sample frames: %u\n", otherMaxFrames); } } - if(!chansok) + + WAVEFORMATEXTENSIBLE OutputType; + if(!MakeExtensible(&OutputType, preferredFormat)) + goto no_spatial; + + /* Force 32-bit float. This is currently required for planar output. */ + if(OutputType.Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE + && OutputType.Format.wFormatTag != WAVE_FORMAT_IEEE_FLOAT) + { + OutputType.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; + OutputType.Format.cbSize = 0; + } + if(OutputType.Format.wBitsPerSample != 32) { + OutputType.Format.nAvgBytesPerSec = OutputType.Format.nAvgBytesPerSec * 32u + / OutputType.Format.wBitsPerSample; + OutputType.Format.nBlockAlign = static_cast<WORD>(OutputType.Format.nBlockAlign * 32 + / OutputType.Format.wBitsPerSample); + OutputType.Format.wBitsPerSample = 32; + } + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + + /* Match the output rate if not requesting anything specific. */ + if(!mDevice->Flags.test(FrequencyRequest)) + mDevice->Frequency = OutputType.Format.nSamplesPerSec; + + bool isRear51{false}; + if(!mDevice->Flags.test(ChannelsRequest)) + { + const uint32_t chancount{OutputType.Format.nChannels}; + const DWORD chanmask{OutputType.dwChannelMask}; if(chancount >= 12 && (chanmask&X714Mask) == X7DOT1DOT4) mDevice->FmtChans = DevFmtX714; else if(chancount >= 8 && (chanmask&X71Mask) == X7DOT1) mDevice->FmtChans = DevFmtX71; else if(chancount >= 7 && (chanmask&X61Mask) == X6DOT1) mDevice->FmtChans = DevFmtX61; - else if(chancount >= 6 && ((chanmask&X51Mask) == X5DOT1 - || (chanmask&X51RearMask) == X5DOT1REAR)) + else if(chancount >= 6 && (chanmask&X51Mask) == X5DOT1) mDevice->FmtChans = DevFmtX51; + else if(chancount >= 6 && (chanmask&X51RearMask) == X5DOT1REAR) + { + mDevice->FmtChans = DevFmtX51; + isRear51 = true; + } else if(chancount >= 4 && (chanmask&QuadMask) == QUAD) mDevice->FmtChans = DevFmtQuad; else if(chancount >= 2 && ((chanmask&StereoMask) == STEREO || !chanmask)) mDevice->FmtChans = DevFmtStereo; - else if(chancount >= 1 && ((chanmask&MonoMask) == MONO || !chanmask)) - mDevice->FmtChans = DevFmtMono; - else + /* HACK: Don't autoselect mono. Wine returns this and makes the + * audio terrible. + */ + else if(!(chancount >= 1 && ((chanmask&MonoMask) == MONO || !chanmask))) + ERR("Unhandled channel config: %d -- 0x%08lx\n", chancount, chanmask); + } + else + { + const uint32_t chancount{OutputType.Format.nChannels}; + const DWORD chanmask{OutputType.dwChannelMask}; + isRear51 = (chancount == 6 && (chanmask&X51RearMask) == X5DOT1REAR); + } + + auto getTypeMask = [isRear51](DevFmtChannels chans) noexcept + { + switch(chans) { - ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, - OutputType.dwChannelMask); - mDevice->FmtChans = DevFmtStereo; - OutputType.Format.nChannels = 2; - OutputType.dwChannelMask = STEREO; + case DevFmtMono: return ChannelMask_Mono; + case DevFmtStereo: return ChannelMask_Stereo; + case DevFmtQuad: return ChannelMask_Quad; + case DevFmtX51: return isRear51 ? ChannelMask_X51Rear : ChannelMask_X51; + case DevFmtX61: return ChannelMask_X61; + case DevFmtX3D71: + case DevFmtX71: return ChannelMask_X71; + case DevFmtX714: return ChannelMask_X714; + case DevFmtAmbi3D: + break; } + return ChannelMask_Stereo; + }; + + SpatialAudioObjectRenderStreamActivationParams streamParams{}; + streamParams.ObjectFormat = &OutputType.Format; + streamParams.StaticObjectTypeMask = getTypeMask(mDevice->FmtChans); + streamParams.Category = AudioCategory_Media; + streamParams.EventHandle = mNotifyEvent; + + PropVariant paramProp{}; + paramProp->vt = VT_BLOB; + paramProp->blob.cbSize = sizeof(streamParams); + paramProp->blob.pBlobData = reinterpret_cast<BYTE*>(&streamParams); + + hr = audio.mClient->ActivateSpatialAudioStream(paramProp.get(), + __uuidof(ISpatialAudioObjectRenderStream), al::out_ptr(audio.mRender)); + if(FAILED(hr)) + { + ERR("Failed to activate spatial audio stream: 0x%08lx\n", hr); + goto no_spatial; } - if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_PCM)) + audio.mStaticMask = streamParams.StaticObjectTypeMask; + mFormat = OutputType; + + mDevice->FmtType = DevFmtFloat; + mDevice->Flags.reset(DirectEar).set(Virtualization); + if(streamParams.StaticObjectTypeMask == ChannelMask_Stereo) + mDevice->FmtChans = DevFmtStereo; + if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "wasapi", "allow-resampler", true)) + mDevice->Frequency = OutputType.Format.nSamplesPerSec; + else + mDevice->Frequency = minu(mDevice->Frequency, OutputType.Format.nSamplesPerSec); + + setDefaultWFXChannelOrder(); + + /* FIXME: Get the real update and buffer size. Presumably the actual + * device is configured once ActivateSpatialAudioStream succeeds, and + * an IAudioClient from the same IMMDevice accesses the same device + * configuration. This isn't obviously correct, but for now assume + * IAudioClient::GetDevicePeriod returns the current device period time + * that ISpatialAudioObjectRenderStream will try to wake up at. + * + * Unfortunately this won't get the buffer size of the + * ISpatialAudioObjectRenderStream, so we only assume there's two + * periods. + */ + mOrigUpdateSize = mDevice->UpdateSize; + mOrigBufferSize = mOrigUpdateSize*2; + ReferenceTime per_time{ReferenceTime{seconds{mDevice->UpdateSize}} / mDevice->Frequency}; + + ComPtr<IAudioClient> tmpClient; + hr = sDeviceHelper->activateAudioClient(mMMDev, __uuidof(IAudioClient), + al::out_ptr(tmpClient)); + if(FAILED(hr)) + ERR("Failed to activate audio client: 0x%08lx\n", hr); + else { - if(OutputType.Format.wBitsPerSample == 8) - mDevice->FmtType = DevFmtUByte; - else if(OutputType.Format.wBitsPerSample == 16) - mDevice->FmtType = DevFmtShort; - else if(OutputType.Format.wBitsPerSample == 32) - mDevice->FmtType = DevFmtInt; + hr = tmpClient->GetDevicePeriod(&reinterpret_cast<REFERENCE_TIME&>(per_time), nullptr); + if(FAILED(hr)) + ERR("Failed to get device period: 0x%08lx\n", hr); else { - mDevice->FmtType = DevFmtShort; - OutputType.Format.wBitsPerSample = 16; + mOrigUpdateSize = RefTime2Samples(per_time, mFormat.Format.nSamplesPerSec); + mOrigBufferSize = mOrigUpdateSize*2; } } - else if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) + tmpClient = nullptr; + + mDevice->UpdateSize = RefTime2Samples(per_time, mDevice->Frequency); + mDevice->BufferSize = mDevice->UpdateSize*2; + + mResampler = nullptr; + mResampleBuffer = nullptr; + mBufferFilled = 0; + if(mDevice->Frequency != mFormat.Format.nSamplesPerSec) { - mDevice->FmtType = DevFmtFloat; - OutputType.Format.wBitsPerSample = 32; + const auto flags = as_unsigned(streamParams.StaticObjectTypeMask); + const auto channelCount = as_unsigned(al::popcount(flags)); + mResampler = SampleConverter::Create(mDevice->FmtType, mDevice->FmtType, + channelCount, mDevice->Frequency, mFormat.Format.nSamplesPerSec, + Resampler::FastBSinc24); + mResampleBuffer = std::make_unique<char[]>(size_t{mDevice->UpdateSize} * channelCount * + mFormat.Format.wBitsPerSample / 8); + + TRACE("Created converter for %s/%s format, dst: %luhz (%u), src: %uhz (%u)\n", + DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType), + mFormat.Format.nSamplesPerSec, mOrigUpdateSize, mDevice->Frequency, + mDevice->UpdateSize); } - else + + return S_OK; + } + +no_spatial: + mDevice->Flags.reset(Virtualization); + + auto &audio = mAudio.emplace<PlainDevice>(); + HRESULT hr{sDeviceHelper->activateAudioClient(mMMDev, __uuidof(IAudioClient), + al::out_ptr(audio.mClient))}; + if(FAILED(hr)) + { + ERR("Failed to reactivate audio client: 0x%08lx\n", hr); + return hr; + } + + WAVEFORMATEX *wfx; + hr = audio.mClient->GetMixFormat(&wfx); + if(FAILED(hr)) + { + ERR("Failed to get mix format: 0x%08lx\n", hr); + return hr; + } + TraceFormat("Device mix format", wfx); + + WAVEFORMATEXTENSIBLE OutputType; + if(!MakeExtensible(&OutputType, wfx)) + { + CoTaskMemFree(wfx); + return E_FAIL; + } + CoTaskMemFree(wfx); + wfx = nullptr; + + const ReferenceTime per_time{ReferenceTime{seconds{mDevice->UpdateSize}} / mDevice->Frequency}; + const ReferenceTime buf_time{ReferenceTime{seconds{mDevice->BufferSize}} / mDevice->Frequency}; + + prepareFormat(OutputType); + + TraceFormat("Requesting playback format", &OutputType.Format); + hr = audio.mClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx); + if(FAILED(hr)) + { + WARN("Failed to check format support: 0x%08lx\n", hr); + hr = audio.mClient->GetMixFormat(&wfx); + } + if(FAILED(hr)) + { + ERR("Failed to find a supported format: 0x%08lx\n", hr); + return hr; + } + + if(wfx != nullptr) + { + TraceFormat("Got playback format", wfx); + if(!MakeExtensible(&OutputType, wfx)) { - ERR("Unhandled format sub-type: %s\n", GuidPrinter{OutputType.SubFormat}.c_str()); - mDevice->FmtType = DevFmtShort; - if(OutputType.Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE) - OutputType.Format.wFormatTag = WAVE_FORMAT_PCM; - OutputType.Format.wBitsPerSample = 16; - OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + CoTaskMemFree(wfx); + return E_FAIL; } - OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + CoTaskMemFree(wfx); + wfx = nullptr; + + finalizeFormat(OutputType); } mFormat = OutputType; - const EndpointFormFactor formfactor{get_device_formfactor(mMMDev.get())}; +#if !defined(ALSOFT_UWP) + const EndpointFormFactor formfactor{GetDeviceFormfactor(mMMDev.get())}; mDevice->Flags.set(DirectEar, (formfactor == Headphones || formfactor == Headset)); - +#else + mDevice->Flags.set(DirectEar, false); +#endif setDefaultWFXChannelOrder(); - hr = mClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + hr = audio.mClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buf_time.count(), 0, &OutputType.Format, nullptr); if(FAILED(hr)) { @@ -1174,15 +1921,22 @@ HRESULT WasapiPlayback::resetProxy() UINT32 buffer_len{}; ReferenceTime min_per{}; - hr = mClient->GetDevicePeriod(&reinterpret_cast<REFERENCE_TIME&>(min_per), nullptr); + hr = audio.mClient->GetDevicePeriod(&reinterpret_cast<REFERENCE_TIME&>(min_per), nullptr); if(SUCCEEDED(hr)) - hr = mClient->GetBufferSize(&buffer_len); + hr = audio.mClient->GetBufferSize(&buffer_len); if(FAILED(hr)) { ERR("Failed to get audio buffer info: 0x%08lx\n", hr); return hr; } + hr = audio.mClient->SetEventHandle(mNotifyEvent); + if(FAILED(hr)) + { + ERR("Failed to set event handle: 0x%08lx\n", hr); + return hr; + } + /* Find the nearest multiple of the period size to the update size */ if(min_per < per_time) min_per *= maxi64((per_time + min_per/2) / min_per, 1); @@ -1212,13 +1966,6 @@ HRESULT WasapiPlayback::resetProxy() mDevice->UpdateSize); } - hr = mClient->SetEventHandle(mNotifyEvent); - if(FAILED(hr)) - { - ERR("Failed to set event handle: 0x%08lx\n", hr); - return hr; - } - return hr; } @@ -1235,33 +1982,61 @@ HRESULT WasapiPlayback::startProxy() { ResetEvent(mNotifyEvent); - HRESULT hr{mClient->Start()}; - if(FAILED(hr)) + auto mstate_fallback = [](std::monostate) -> HRESULT + { return E_FAIL; }; + auto start_plain = [&](PlainDevice &audio) -> HRESULT { - ERR("Failed to start audio client: 0x%08lx\n", hr); - return hr; - } + HRESULT hr{audio.mClient->Start()}; + if(FAILED(hr)) + { + ERR("Failed to start audio client: 0x%08lx\n", hr); + return hr; + } - void *ptr; - hr = mClient->GetService(IID_IAudioRenderClient, &ptr); - if(SUCCEEDED(hr)) + hr = audio.mClient->GetService(__uuidof(IAudioRenderClient), al::out_ptr(audio.mRender)); + if(SUCCEEDED(hr)) + { + try { + mKillNow.store(false, std::memory_order_release); + mThread = std::thread{std::mem_fn(&WasapiPlayback::mixerProc), this}; + } + catch(...) { + audio.mRender = nullptr; + ERR("Failed to start thread\n"); + hr = E_FAIL; + } + } + if(FAILED(hr)) + audio.mClient->Stop(); + return hr; + }; + auto start_spatial = [&](SpatialDevice &audio) -> HRESULT { - mRender = ComPtr<IAudioRenderClient>{static_cast<IAudioRenderClient*>(ptr)}; + HRESULT hr{audio.mRender->Start()}; + if(FAILED(hr)) + { + ERR("Failed to start spatial audio stream: 0x%08lx\n", hr); + return hr; + } + try { mKillNow.store(false, std::memory_order_release); - mThread = std::thread{std::mem_fn(&WasapiPlayback::mixerProc), this}; + mThread = std::thread{std::mem_fn(&WasapiPlayback::mixerSpatialProc), this}; } catch(...) { - mRender = nullptr; ERR("Failed to start thread\n"); hr = E_FAIL; } - } - if(FAILED(hr)) - mClient->Stop(); + if(FAILED(hr)) + { + audio.mRender->Stop(); + audio.mRender->Reset(); + } + return hr; + }; - return hr; + return std::visit(overloaded{mstate_fallback, start_plain, start_spatial}, mAudio); } @@ -1270,14 +2045,25 @@ void WasapiPlayback::stop() void WasapiPlayback::stopProxy() { - if(!mRender || !mThread.joinable()) + if(!mThread.joinable()) return; mKillNow.store(true, std::memory_order_release); mThread.join(); - mRender = nullptr; - mClient->Stop(); + auto mstate_fallback = [](std::monostate) -> void + { }; + auto stop_plain = [](PlainDevice &audio) -> void + { + audio.mRender = nullptr; + audio.mClient->Stop(); + }; + auto stop_spatial = [](SpatialDevice &audio) -> void + { + audio.mRender->Stop(); + audio.mRender->Reset(); + }; + std::visit(overloaded{mstate_fallback, stop_plain, stop_spatial}, mAudio); } @@ -1306,8 +2092,8 @@ struct WasapiCapture final : public BackendBase, WasapiProxy { int recordProc(); - void open(const char *name) override; - HRESULT openProxy(const char *name) override; + void open(std::string_view name) override; + HRESULT openProxy(std::string_view name) override; void closeProxy() override; HRESULT resetProxy() override; @@ -1316,11 +2102,11 @@ struct WasapiCapture final : public BackendBase, WasapiProxy { void stop() override; void stopProxy() override; - void captureSamples(al::byte *buffer, uint samples) override; + void captureSamples(std::byte *buffer, uint samples) override; uint availableSamples() override; HRESULT mOpenStatus{E_FAIL}; - ComPtr<IMMDevice> mMMDev{nullptr}; + DeviceHandle mMMDev{nullptr}; ComPtr<IAudioClient> mClient{nullptr}; ComPtr<IAudioCaptureClient> mCapture{nullptr}; HANDLE mNotifyEvent{nullptr}; @@ -1338,10 +2124,7 @@ struct WasapiCapture final : public BackendBase, WasapiProxy { WasapiCapture::~WasapiCapture() { if(SUCCEEDED(mOpenStatus)) - { pushMessage(MsgType::CloseDevice).wait(); - DeinitThread(); - } mOpenStatus = E_FAIL; if(mNotifyEvent != nullptr) @@ -1352,21 +2135,21 @@ WasapiCapture::~WasapiCapture() FORCE_ALIGN int WasapiCapture::recordProc() { - HRESULT hr{CoInitializeEx(nullptr, COINIT_MULTITHREADED)}; - if(FAILED(hr)) + ComWrapper com{COINIT_MULTITHREADED}; + if(!com) { - ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr); - mDevice->handleDisconnect("COM init failed: 0x%08lx", hr); + ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", com.status()); + mDevice->handleDisconnect("COM init failed: 0x%08lx", com.status()); return 1; } althrd_setname(RECORD_THREAD_NAME); - al::vector<float> samples; + std::vector<float> samples; while(!mKillNow.load(std::memory_order_relaxed)) { UINT32 avail; - hr = mCapture->GetNextPacketSize(&avail); + HRESULT hr{mCapture->GetNextPacketSize(&avail)}; if(FAILED(hr)) ERR("Failed to get next packet size: 0x%08lx\n", hr); else if(avail > 0) @@ -1437,12 +2220,11 @@ FORCE_ALIGN int WasapiCapture::recordProc() ERR("WaitForSingleObjectEx error: 0x%lx\n", res); } - CoUninitialize(); return 0; } -void WasapiCapture::open(const char *name) +void WasapiCapture::open(std::string_view name) { if(SUCCEEDED(mOpenStatus)) throw al::backend_exception{al::backend_error::DeviceError, @@ -1456,34 +2238,18 @@ void WasapiCapture::open(const char *name) "Failed to create notify events"}; } - HRESULT hr{InitThread()}; - if(FAILED(hr)) - { - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to init COM thread: 0x%08lx", hr}; - } - - if(name) + if(name.length() >= DevNameHeadLen + && std::strncmp(name.data(), DevNameHead, DevNameHeadLen) == 0) { - if(CaptureDevices.empty()) - pushMessage(MsgType::EnumerateCapture); - if(std::strncmp(name, DevNameHead, DevNameHeadLen) == 0) - { - name += DevNameHeadLen; - if(*name == '\0') - name = nullptr; - } + name = name.substr(DevNameHeadLen); } mOpenStatus = pushMessage(MsgType::OpenDevice, name).get(); if(FAILED(mOpenStatus)) - { - DeinitThread(); throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx", mOpenStatus}; - } - hr = pushMessage(MsgType::ResetDevice).get(); + HRESULT hr{pushMessage(MsgType::ResetDevice).get()}; if(FAILED(hr)) { if(hr == E_OUTOFMEMORY) @@ -1492,52 +2258,47 @@ void WasapiCapture::open(const char *name) } } -HRESULT WasapiCapture::openProxy(const char *name) +HRESULT WasapiCapture::openProxy(std::string_view name) { - const wchar_t *devid{nullptr}; - if(name) + std::string devname; + std::wstring devid; + if(!name.empty()) { - auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(), + auto devlock = DeviceListLock{gDeviceList}; + auto devlist = al::span{devlock.getCaptureList()}; + auto iter = std::find_if(devlist.cbegin(), devlist.cend(), [name](const DevMap &entry) -> bool { return entry.name == name || entry.endpoint_guid == name; }); - if(iter == CaptureDevices.cend()) + if(iter == devlist.cend()) { const std::wstring wname{utf8_to_wstr(name)}; - iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(), + iter = std::find_if(devlist.cbegin(), devlist.cend(), [&wname](const DevMap &entry) -> bool { return entry.devid == wname; }); } - if(iter == CaptureDevices.cend()) + if(iter == devlist.cend()) { - WARN("Failed to find device name matching \"%s\"\n", name); + WARN("Failed to find device name matching \"%.*s\"\n", static_cast<int>(name.length()), + name.data()); return E_FAIL; } - name = iter->name.c_str(); - devid = iter->devid.c_str(); + devname = iter->name; + devid = iter->devid; } - void *ptr; - HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, - IID_IMMDeviceEnumerator, &ptr)}; - if(SUCCEEDED(hr)) - { - ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)}; - if(!devid) - hr = enumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, mMMDev.getPtr()); - else - hr = enumerator->GetDevice(devid, mMMDev.getPtr()); - } + HRESULT hr{sDeviceHelper->openDevice(devid, eCapture, mMMDev)}; if(FAILED(hr)) { - WARN("Failed to open device \"%s\"\n", name?name:"(default)"); + WARN("Failed to open device \"%s\"\n", devname.empty() ? "(default)" : devname.c_str()); return hr; } - mClient = nullptr; - if(name) mDevice->DeviceName = std::string{DevNameHead} + name; - else mDevice->DeviceName = DevNameHead + get_device_name_and_guid(mMMDev.get()).first; + if(!devname.empty()) + mDevice->DeviceName = DevNameHead + std::move(devname); + else + mDevice->DeviceName = DevNameHead + GetDeviceNameAndGuid(mMMDev).first; - return hr; + return S_OK; } void WasapiCapture::closeProxy() @@ -1550,14 +2311,13 @@ HRESULT WasapiCapture::resetProxy() { mClient = nullptr; - void *ptr; - HRESULT hr{mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr)}; + HRESULT hr{sDeviceHelper->activateAudioClient(mMMDev, __uuidof(IAudioClient), + al::out_ptr(mClient))}; if(FAILED(hr)) { ERR("Failed to reactivate audio client: 0x%08lx\n", hr); return hr; } - mClient = ComPtr<IAudioClient>{static_cast<IAudioClient*>(ptr)}; WAVEFORMATEX *wfx; hr = mClient->GetMixFormat(&wfx); @@ -1849,11 +2609,9 @@ HRESULT WasapiCapture::startProxy() return hr; } - void *ptr; - hr = mClient->GetService(IID_IAudioCaptureClient, &ptr); + hr = mClient->GetService(__uuidof(IAudioCaptureClient), al::out_ptr(mCapture)); if(SUCCEEDED(hr)) { - mCapture = ComPtr<IAudioCaptureClient>{static_cast<IAudioCaptureClient*>(ptr)}; try { mKillNow.store(false, std::memory_order_release); mThread = std::thread{std::mem_fn(&WasapiCapture::recordProc), this}; @@ -1892,7 +2650,7 @@ void WasapiCapture::stopProxy() } -void WasapiCapture::captureSamples(al::byte *buffer, uint samples) +void WasapiCapture::captureSamples(std::byte *buffer, uint samples) { mRing->read(buffer, samples); } uint WasapiCapture::availableSamples() @@ -1904,34 +2662,13 @@ uint WasapiCapture::availableSamples() bool WasapiBackendFactory::init() { static HRESULT InitResult{E_FAIL}; - if(FAILED(InitResult)) try { - auto res = std::async(std::launch::async, []() -> HRESULT - { - HRESULT hr{CoInitializeEx(nullptr, COINIT_MULTITHREADED)}; - if(FAILED(hr)) - { - WARN("Failed to initialize COM: 0x%08lx\n", hr); - return hr; - } - - void *ptr{}; - hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, - IID_IMMDeviceEnumerator, &ptr); - if(FAILED(hr)) - { - WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr); - CoUninitialize(); - return hr; - } - static_cast<IMMDeviceEnumerator*>(ptr)->Release(); - CoUninitialize(); - - return S_OK; - }); + std::promise<HRESULT> promise; + auto future = promise.get_future(); - InitResult = res.get(); + std::thread{&WasapiProxy::messageHandler, &promise}.detach(); + InitResult = future.get(); } catch(...) { } @@ -1944,34 +2681,45 @@ bool WasapiBackendFactory::querySupport(BackendType type) std::string WasapiBackendFactory::probe(BackendType type) { - struct ProxyControl { - HRESULT mResult{}; - ProxyControl() { mResult = WasapiProxy::InitThread(); } - ~ProxyControl() { if(SUCCEEDED(mResult)) WasapiProxy::DeinitThread(); } - }; - ProxyControl proxy; - std::string outnames; - if(FAILED(proxy.mResult)) - return outnames; + auto devlock = DeviceListLock{gDeviceList}; switch(type) { case BackendType::Playback: - WasapiProxy::pushMessageStatic(MsgType::EnumeratePlayback).wait(); - for(const DevMap &entry : PlaybackDevices) { - /* +1 to also append the null char (to ensure a null-separated list - * and double-null terminated list). - */ - outnames.append(DevNameHead).append(entry.name.c_str(), entry.name.length()+1); + auto defaultId = devlock.getPlaybackDefaultId(); + for(const DevMap &entry : devlock.getPlaybackList()) + { + if(entry.devid != defaultId) + { + /* +1 to also append the null char (to ensure a null- + * separated list and double-null terminated list). + */ + outnames.append(DevNameHead).append(entry.name.c_str(), entry.name.length()+1); + continue; + } + /* Default device goes first. */ + std::string name{DevNameHead + entry.name}; + outnames.insert(0, name.c_str(), name.length()+1); + } } break; case BackendType::Capture: - WasapiProxy::pushMessageStatic(MsgType::EnumerateCapture).wait(); - for(const DevMap &entry : CaptureDevices) - outnames.append(DevNameHead).append(entry.name.c_str(), entry.name.length()+1); + { + auto defaultId = devlock.getCaptureDefaultId(); + for(const DevMap &entry : devlock.getCaptureList()) + { + if(entry.devid != defaultId) + { + outnames.append(DevNameHead).append(entry.name.c_str(), entry.name.length()+1); + continue; + } + std::string name{DevNameHead + entry.name}; + outnames.insert(0, name.c_str(), name.length()+1); + } + } break; } @@ -1992,3 +2740,22 @@ BackendFactory &WasapiBackendFactory::getFactory() static WasapiBackendFactory factory{}; return factory; } + +alc::EventSupport WasapiBackendFactory::queryEventSupport(alc::EventType eventType, BackendType) +{ + switch(eventType) + { + case alc::EventType::DefaultDeviceChanged: + return alc::EventSupport::FullSupport; + + case alc::EventType::DeviceAdded: + case alc::EventType::DeviceRemoved: +#if !defined(ALSOFT_UWP) + return alc::EventSupport::FullSupport; +#endif + + case alc::EventType::Count: + break; + } + return alc::EventSupport::NoSupport; +} diff --git a/alc/backends/wasapi.h b/alc/backends/wasapi.h index bb2671ee..12fd95ef 100644 --- a/alc/backends/wasapi.h +++ b/alc/backends/wasapi.h @@ -9,6 +9,8 @@ public: bool querySupport(BackendType type) override; + alc::EventSupport queryEventSupport(alc::EventType eventType, BackendType type) override; + std::string probe(BackendType type) override; BackendPtr createBackend(DeviceBase *device, BackendType type) override; diff --git a/alc/backends/wave.cpp b/alc/backends/wave.cpp index 1b40640c..794d5cb8 100644 --- a/alc/backends/wave.cpp +++ b/alc/backends/wave.cpp @@ -32,19 +32,18 @@ #include <exception> #include <functional> #include <thread> +#include <vector> #include "albit.h" -#include "albyte.h" #include "alc/alconfig.h" #include "almalloc.h" #include "alnumeric.h" +#include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" #include "core/logging.h" #include "opthelpers.h" #include "strutils.h" -#include "threads.h" -#include "vector.h" namespace { @@ -97,7 +96,7 @@ struct WaveBackend final : public BackendBase { int mixerProc(); - void open(const char *name) override; + void open(std::string_view name) override; bool reset() override; void start() override; void stop() override; @@ -105,7 +104,7 @@ struct WaveBackend final : public BackendBase { FILE *mFile{nullptr}; long mDataStart{-1}; - al::vector<al::byte> mBuffer; + std::vector<std::byte> mBuffer; std::atomic<bool> mKillNow{true}; std::thread mThread; @@ -155,13 +154,13 @@ int WaveBackend::mixerProc() if(bytesize == 2) { - const size_t len{mBuffer.size() & ~size_t{1}}; + const size_t len{mBuffer.size() & ~1_uz}; for(size_t i{0};i < len;i+=2) std::swap(mBuffer[i], mBuffer[i+1]); } else if(bytesize == 4) { - const size_t len{mBuffer.size() & ~size_t{3}}; + const size_t len{mBuffer.size() & ~3_uz}; for(size_t i{0};i < len;i+=4) { std::swap(mBuffer[i ], mBuffer[i+3]); @@ -195,24 +194,24 @@ int WaveBackend::mixerProc() return 0; } -void WaveBackend::open(const char *name) +void WaveBackend::open(std::string_view name) { auto fname = ConfigValueStr(nullptr, "wave", "file"); if(!fname) throw al::backend_exception{al::backend_error::NoDevice, "No wave output filename"}; - if(!name) + if(name.empty()) name = waveDevice; - else if(strcmp(name, waveDevice) != 0) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", - name}; + else if(name != waveDevice) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", + static_cast<int>(name.length()), name.data()}; /* There's only one "device", so if it's already open, we're done. */ if(mFile) return; #ifdef _WIN32 { - std::wstring wname{utf8_to_wstr(fname->c_str())}; + std::wstring wname{utf8_to_wstr(fname.value())}; mFile = _wfopen(wname.c_str(), L"wb"); } #else diff --git a/alc/backends/winmm.cpp b/alc/backends/winmm.cpp index 38e1193f..f0fb0a1c 100644 --- a/alc/backends/winmm.cpp +++ b/alc/backends/winmm.cpp @@ -39,12 +39,13 @@ #include <functional> #include "alnumeric.h" +#include "alsem.h" +#include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" #include "core/logging.h" #include "ringbuffer.h" #include "strutils.h" -#include "threads.h" #ifndef WAVE_FORMAT_IEEE_FLOAT #define WAVE_FORMAT_IEEE_FLOAT 0x0003 @@ -55,10 +56,10 @@ namespace { #define DEVNAME_HEAD "OpenAL Soft on " -al::vector<std::string> PlaybackDevices; -al::vector<std::string> CaptureDevices; +std::vector<std::string> PlaybackDevices; +std::vector<std::string> CaptureDevices; -bool checkName(const al::vector<std::string> &list, const std::string &name) +bool checkName(const std::vector<std::string> &list, const std::string &name) { return std::find(list.cbegin(), list.cend(), name) != list.cend(); } void ProbePlaybackDevices(void) @@ -134,7 +135,7 @@ struct WinMMPlayback final : public BackendBase { int mixerProc(); - void open(const char *name) override; + void open(std::string_view name) override; bool reset() override; void start() override; void stop() override; @@ -166,7 +167,7 @@ WinMMPlayback::~WinMMPlayback() /* WinMMPlayback::waveOutProc * - * Posts a message to 'WinMMPlayback::mixerProc' everytime a WaveOut Buffer is + * Posts a message to 'WinMMPlayback::mixerProc' every time a WaveOut Buffer is * completed and returns to the application (for more data) */ void CALLBACK WinMMPlayback::waveOutProc(HWAVEOUT, UINT msg, DWORD_PTR, DWORD_PTR) noexcept @@ -207,18 +208,18 @@ FORCE_ALIGN int WinMMPlayback::mixerProc() } -void WinMMPlayback::open(const char *name) +void WinMMPlayback::open(std::string_view name) { if(PlaybackDevices.empty()) ProbePlaybackDevices(); // Find the Device ID matching the deviceName if valid - auto iter = name ? + auto iter = !name.empty() ? std::find(PlaybackDevices.cbegin(), PlaybackDevices.cend(), name) : PlaybackDevices.cbegin(); if(iter == PlaybackDevices.cend()) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", - name}; + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", + static_cast<int>(name.length()), name.data()}; auto DeviceID = static_cast<UINT>(std::distance(PlaybackDevices.cbegin(), iter)); DevFmtType fmttype{mDevice->FmtType}; @@ -369,10 +370,10 @@ struct WinMMCapture final : public BackendBase { int captureProc(); - void open(const char *name) override; + void open(std::string_view name) override; void start() override; void stop() override; - void captureSamples(al::byte *buffer, uint samples) override; + void captureSamples(std::byte *buffer, uint samples) override; uint availableSamples() override; std::atomic<uint> mReadable{0u}; @@ -405,7 +406,7 @@ WinMMCapture::~WinMMCapture() /* WinMMCapture::waveInProc * - * Posts a message to 'WinMMCapture::captureProc' everytime a WaveIn Buffer is + * Posts a message to 'WinMMCapture::captureProc' every time a WaveIn Buffer is * completed and returns to the application (with more data). */ void CALLBACK WinMMCapture::waveInProc(HWAVEIN, UINT msg, DWORD_PTR, DWORD_PTR) noexcept @@ -445,18 +446,18 @@ int WinMMCapture::captureProc() } -void WinMMCapture::open(const char *name) +void WinMMCapture::open(std::string_view name) { if(CaptureDevices.empty()) ProbeCaptureDevices(); // Find the Device ID matching the deviceName if valid - auto iter = name ? + auto iter = !name.empty() ? std::find(CaptureDevices.cbegin(), CaptureDevices.cend(), name) : CaptureDevices.cbegin(); if(iter == CaptureDevices.cend()) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", - name}; + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", + static_cast<int>(name.length()), name.data()}; auto DeviceID = static_cast<UINT>(std::distance(CaptureDevices.cbegin(), iter)); switch(mDevice->FmtChans) @@ -571,7 +572,7 @@ void WinMMCapture::stop() mIdx = 0; } -void WinMMCapture::captureSamples(al::byte *buffer, uint samples) +void WinMMCapture::captureSamples(std::byte *buffer, uint samples) { mRing->read(buffer, samples); } uint WinMMCapture::availableSamples() diff --git a/alc/context.cpp b/alc/context.cpp index e02c549b..ffc2743e 100644 --- a/alc/context.cpp +++ b/alc/context.cpp @@ -4,21 +4,27 @@ #include "context.h" #include <algorithm> +#include <array> +#include <cstring> #include <functional> #include <limits> #include <numeric> #include <stddef.h> #include <stdexcept> +#include <string_view> +#include <utility> #include "AL/efx.h" #include "al/auxeffectslot.h" +#include "al/debug.h" #include "al/source.h" #include "al/effect.h" #include "al/event.h" #include "al/listener.h" #include "albit.h" #include "alc/alu.h" +#include "alspan.h" #include "core/async_event.h" #include "core/device.h" #include "core/effectslot.h" @@ -42,47 +48,53 @@ using namespace std::placeholders; using voidp = void*; /* Default context extensions */ -constexpr ALchar alExtList[] = - "AL_EXT_ALAW " - "AL_EXT_BFORMAT " - "AL_EXT_DOUBLE " - "AL_EXT_EXPONENT_DISTANCE " - "AL_EXT_FLOAT32 " - "AL_EXT_IMA4 " - "AL_EXT_LINEAR_DISTANCE " - "AL_EXT_MCFORMATS " - "AL_EXT_MULAW " - "AL_EXT_MULAW_BFORMAT " - "AL_EXT_MULAW_MCFORMATS " - "AL_EXT_OFFSET " - "AL_EXT_source_distance_model " - "AL_EXT_SOURCE_RADIUS " - "AL_EXT_STATIC_BUFFER " - "AL_EXT_STEREO_ANGLES " - "AL_LOKI_quadriphonic " - "AL_SOFT_bformat_ex " - "AL_SOFTX_bformat_hoa " - "AL_SOFT_block_alignment " - "AL_SOFT_buffer_length_query " - "AL_SOFT_callback_buffer " - "AL_SOFTX_convolution_reverb " - "AL_SOFT_deferred_updates " - "AL_SOFT_direct_channels " - "AL_SOFT_direct_channels_remix " - "AL_SOFT_effect_target " - "AL_SOFT_events " - "AL_SOFT_gain_clamp_ex " - "AL_SOFTX_hold_on_disconnect " - "AL_SOFT_loop_points " - "AL_SOFTX_map_buffer " - "AL_SOFT_MSADPCM " - "AL_SOFT_source_latency " - "AL_SOFT_source_length " - "AL_SOFT_source_resampler " - "AL_SOFT_source_spatialize " - "AL_SOFT_source_start_delay " - "AL_SOFT_UHJ " - "AL_SOFT_UHJ_ex"; +std::vector<std::string_view> getContextExtensions() noexcept +{ + return std::vector<std::string_view>{ + "AL_EXT_ALAW", + "AL_EXT_BFORMAT", + "AL_EXT_debug", + "AL_EXTX_direct_context", + "AL_EXT_DOUBLE", + "AL_EXT_EXPONENT_DISTANCE", + "AL_EXT_FLOAT32", + "AL_EXT_IMA4", + "AL_EXT_LINEAR_DISTANCE", + "AL_EXT_MCFORMATS", + "AL_EXT_MULAW", + "AL_EXT_MULAW_BFORMAT", + "AL_EXT_MULAW_MCFORMATS", + "AL_EXT_OFFSET", + "AL_EXT_source_distance_model", + "AL_EXT_SOURCE_RADIUS", + "AL_EXT_STATIC_BUFFER", + "AL_EXT_STEREO_ANGLES", + "AL_LOKI_quadriphonic", + "AL_SOFT_bformat_ex", + "AL_SOFTX_bformat_hoa", + "AL_SOFT_block_alignment", + "AL_SOFT_buffer_length_query", + "AL_SOFT_callback_buffer", + "AL_SOFTX_convolution_effect", + "AL_SOFT_deferred_updates", + "AL_SOFT_direct_channels", + "AL_SOFT_direct_channels_remix", + "AL_SOFT_effect_target", + "AL_SOFT_events", + "AL_SOFT_gain_clamp_ex", + "AL_SOFTX_hold_on_disconnect", + "AL_SOFT_loop_points", + "AL_SOFTX_map_buffer", + "AL_SOFT_MSADPCM", + "AL_SOFT_source_latency", + "AL_SOFT_source_length", + "AL_SOFT_source_resampler", + "AL_SOFT_source_spatialize", + "AL_SOFT_source_start_delay", + "AL_SOFT_UHJ", + "AL_SOFT_UHJ_ex", + }; +} } // namespace @@ -90,10 +102,9 @@ constexpr ALchar alExtList[] = std::atomic<bool> ALCcontext::sGlobalContextLock{false}; std::atomic<ALCcontext*> ALCcontext::sGlobalContext{nullptr}; -thread_local ALCcontext *ALCcontext::sLocalContext{nullptr}; ALCcontext::ThreadCtx::~ThreadCtx() { - if(ALCcontext *ctx{ALCcontext::sLocalContext}) + if(ALCcontext *ctx{std::exchange(ALCcontext::sLocalContext, nullptr)}) { const bool result{ctx->releaseIfNoDelete()}; ERR("Context %p current for thread being destroyed%s!\n", voidp{ctx}, @@ -105,23 +116,18 @@ thread_local ALCcontext::ThreadCtx ALCcontext::sThreadContext; ALeffect ALCcontext::sDefaultEffect; -#ifdef __MINGW32__ -ALCcontext *ALCcontext::getThreadContext() noexcept -{ return sLocalContext; } -void ALCcontext::setThreadContext(ALCcontext *context) noexcept -{ sThreadContext.set(context); } -#endif - -ALCcontext::ALCcontext(al::intrusive_ptr<ALCdevice> device) - : ContextBase{device.get()}, mALDevice{std::move(device)} +ALCcontext::ALCcontext(al::intrusive_ptr<ALCdevice> device, ContextFlagBitset flags) + : ContextBase{device.get()}, mALDevice{std::move(device)}, mContextFlags{flags} { + mDebugGroups.emplace_back(DebugSource::Other, 0, std::string{}); + mDebugEnabled.store(mContextFlags.test(ContextFlags::DebugBit), std::memory_order_relaxed); } ALCcontext::~ALCcontext() { TRACE("Freeing context %p\n", voidp{this}); - size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), size_t{0u}, + size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), 0_uz, [](size_t cur, const SourceSubList &sublist) noexcept -> size_t { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })}; if(count > 0) @@ -134,7 +140,7 @@ ALCcontext::~ALCcontext() #endif // ALSOFT_EAX mDefaultSlot = nullptr; - count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), size_t{0u}, + count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), 0_uz, [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); }); if(count > 0) @@ -170,26 +176,41 @@ void ALCcontext::init() mCurrentVoiceChange.store(cur, std::memory_order_relaxed); } - mExtensionList = alExtList; + mExtensions = getContextExtensions(); if(sBufferSubDataCompat) { - std::string extlist{mExtensionList}; - - const auto pos = extlist.find("AL_EXT_SOURCE_RADIUS "); - if(pos != std::string::npos) - extlist.replace(pos, 20, "AL_SOFT_buffer_sub_data"); - else - extlist += " AL_SOFT_buffer_sub_data"; - - mExtensionListOverride = std::move(extlist); - mExtensionList = mExtensionListOverride.c_str(); + auto iter = std::find(mExtensions.begin(), mExtensions.end(), "AL_EXT_SOURCE_RADIUS"); + if(iter != mExtensions.end()) mExtensions.erase(iter); + /* TODO: Would be nice to sort this alphabetically. Needs case- + * insensitive searching. + */ + mExtensions.emplace_back("AL_SOFT_buffer_sub_data"); } #ifdef ALSOFT_EAX eax_initialize_extensions(); #endif // ALSOFT_EAX + if(!mExtensions.empty()) + { + const size_t len{std::accumulate(mExtensions.cbegin()+1, mExtensions.cend(), + mExtensions.front().length(), + [](size_t current, std::string_view ext) noexcept + { return current + ext.length() + 1; })}; + + std::string extensions; + extensions.reserve(len); + extensions += mExtensions.front(); + for(std::string_view ext : al::span{mExtensions}.subspan<1>()) + { + extensions += ' '; + extensions += ext; + } + + mExtensionsString = std::move(extensions); + } + mParams.Position = alu::Vector{0.0f, 0.0f, 0.0f, 1.0f}; mParams.Matrix = alu::Matrix::Identity(); mParams.Velocity = alu::Vector{}; @@ -295,6 +316,7 @@ void ALCcontext::applyAllUpdates() mHoldUpdates.store(false, std::memory_order_release); } + #ifdef ALSOFT_EAX namespace { @@ -447,43 +469,15 @@ void ALCcontext::eax_initialize_extensions() if(!eax_g_is_enabled) return; - const auto string_max_capacity = - std::strlen(mExtensionList) + 1 + - std::strlen(eax1_ext_name) + 1 + - std::strlen(eax2_ext_name) + 1 + - std::strlen(eax3_ext_name) + 1 + - std::strlen(eax4_ext_name) + 1 + - std::strlen(eax5_ext_name) + 1 + - std::strlen(eax_x_ram_ext_name) + 1; - - std::string extlist; - extlist.reserve(string_max_capacity); - + mExtensions.emplace(mExtensions.begin(), eax_x_ram_ext_name); if(eaxIsCapable()) { - extlist += eax1_ext_name; - extlist += ' '; - - extlist += eax2_ext_name; - extlist += ' '; - - extlist += eax3_ext_name; - extlist += ' '; - - extlist += eax4_ext_name; - extlist += ' '; - - extlist += eax5_ext_name; - extlist += ' '; + mExtensions.emplace(mExtensions.begin(), eax5_ext_name); + mExtensions.emplace(mExtensions.begin(), eax4_ext_name); + mExtensions.emplace(mExtensions.begin(), eax3_ext_name); + mExtensions.emplace(mExtensions.begin(), eax2_ext_name); + mExtensions.emplace(mExtensions.begin(), eax1_ext_name); } - - extlist += eax_x_ram_ext_name; - extlist += ' '; - - extlist += mExtensionList; - - mExtensionListOverride = std::move(extlist); - mExtensionList = mExtensionListOverride.c_str(); } void ALCcontext::eax_initialize() @@ -1018,50 +1012,20 @@ void ALCcontext::eaxCommit() eax_update_sources(); } -namespace { -class EaxSetException : public EaxException { -public: - explicit EaxSetException(const char* message) - : EaxException{"EAX_SET", message} - {} -}; - -[[noreturn]] void eax_fail_set(const char* message) +FORCE_ALIGN ALenum AL_APIENTRY EAXSet(const GUID *a, ALuint b, ALuint c, ALvoid *d, ALuint e) noexcept { - throw EaxSetException{message}; -} - -class EaxGetException : public EaxException { -public: - explicit EaxGetException(const char* message) - : EaxException{"EAX_GET", message} - {} -}; - -[[noreturn]] void eax_fail_get(const char* message) -{ - throw EaxGetException{message}; + auto context = GetContextRef(); + if(!context) UNLIKELY return AL_INVALID_OPERATION; + return EAXSetDirect(context.get(), a, b, c, d, e); } -} // namespace - - -FORCE_ALIGN ALenum AL_APIENTRY EAXSet( - const GUID* property_set_id, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_value, +FORCE_ALIGN ALenum AL_APIENTRY EAXSetDirect(ALCcontext *context, const GUID *property_set_id, + ALuint property_id, ALuint property_source_id, ALvoid *property_value, ALuint property_value_size) noexcept try { - auto context = GetContextRef(); - - if(!context) - eax_fail_set("No current context."); - std::lock_guard<std::mutex> prop_lock{context->mPropLock}; - return context->eax_eax_set( property_set_id, property_id, @@ -1069,27 +1033,26 @@ try property_value, property_value_size); } -catch (...) +catch(...) { eax_log_exception(__func__); return AL_INVALID_OPERATION; } -FORCE_ALIGN ALenum AL_APIENTRY EAXGet( - const GUID* property_set_id, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_value, - ALuint property_value_size) noexcept -try + +FORCE_ALIGN ALenum AL_APIENTRY EAXGet(const GUID *a, ALuint b, ALuint c, ALvoid *d, ALuint e) noexcept { auto context = GetContextRef(); + if(!context) UNLIKELY return AL_INVALID_OPERATION; + return EAXGetDirect(context.get(), a, b, c, d, e); +} - if(!context) - eax_fail_get("No current context."); - +FORCE_ALIGN ALenum AL_APIENTRY EAXGetDirect(ALCcontext *context, const GUID *property_set_id, + ALuint property_id, ALuint property_source_id, ALvoid *property_value, + ALuint property_value_size) noexcept +try +{ std::lock_guard<std::mutex> prop_lock{context->mPropLock}; - return context->eax_eax_get( property_set_id, property_id, @@ -1097,7 +1060,7 @@ try property_value, property_value_size); } -catch (...) +catch(...) { eax_log_exception(__func__); return AL_INVALID_OPERATION; diff --git a/alc/context.h b/alc/context.h index e8efdbf1..201c8873 100644 --- a/alc/context.h +++ b/alc/context.h @@ -2,10 +2,15 @@ #define ALC_CONTEXT_H #include <atomic> +#include <deque> #include <memory> #include <mutex> #include <stdint.h> +#include <string> +#include <string_view> +#include <unordered_map> #include <utility> +#include <vector> #include "AL/al.h" #include "AL/alc.h" @@ -16,8 +21,8 @@ #include "alnumeric.h" #include "atomic.h" #include "core/context.h" +#include "inprogext.h" #include "intrusive_ptr.h" -#include "vector.h" #ifdef ALSOFT_EAX #include "al/eax/call.h" @@ -30,10 +35,39 @@ struct ALeffect; struct ALeffectslot; struct ALsource; +struct DebugGroup; + +enum class DebugSource : uint8_t; +enum class DebugType : uint8_t; +enum class DebugSeverity : uint8_t; using uint = unsigned int; +enum ContextFlags { + DebugBit = 0, /* ALC_CONTEXT_DEBUG_BIT_EXT */ +}; +using ContextFlagBitset = std::bitset<sizeof(ALuint)*8>; + + +struct DebugLogEntry { + const DebugSource mSource; + const DebugType mType; + const DebugSeverity mSeverity; + const uint mId; + + std::string mMessage; + + template<typename T> + DebugLogEntry(DebugSource source, DebugType type, uint id, DebugSeverity severity, T&& message) + : mSource{source}, mType{type}, mSeverity{severity}, mId{id} + , mMessage{std::forward<T>(message)} + { } + DebugLogEntry(const DebugLogEntry&) = default; + DebugLogEntry(DebugLogEntry&&) = default; +}; + + struct SourceSubList { uint64_t FreeMask{~0_u64}; ALsource *Sources{nullptr}; /* 64 */ @@ -76,6 +110,9 @@ struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase { std::atomic<ALenum> mLastError{AL_NO_ERROR}; + const ContextFlagBitset mContextFlags; + std::atomic<bool> mDebugEnabled{false}; + DistanceModel mDistanceModel{DistanceModel::Default}; bool mSourceDistanceModel{false}; @@ -88,25 +125,32 @@ struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase { ALEVENTPROCSOFT mEventCb{}; void *mEventParam{nullptr}; + std::mutex mDebugCbLock; + ALDEBUGPROCEXT mDebugCb{}; + void *mDebugParam{nullptr}; + std::vector<DebugGroup> mDebugGroups; + std::deque<DebugLogEntry> mDebugLog; + ALlistener mListener{}; - al::vector<SourceSubList> mSourceList; + std::vector<SourceSubList> mSourceList; ALuint mNumSources{0}; std::mutex mSourceLock; - al::vector<EffectSlotSubList> mEffectSlotList; + std::vector<EffectSlotSubList> mEffectSlotList; ALuint mNumEffectSlots{0u}; std::mutex mEffectSlotLock; /* Default effect slot */ std::unique_ptr<ALeffectslot> mDefaultSlot; - const char *mExtensionList{nullptr}; - - std::string mExtensionListOverride{}; + std::vector<std::string_view> mExtensions; + std::string mExtensionsString{}; + std::unordered_map<ALuint,std::string> mSourceNames; + std::unordered_map<ALuint,std::string> mEffectSlotNames; - ALCcontext(al::intrusive_ptr<ALCdevice> device); + ALCcontext(al::intrusive_ptr<ALCdevice> device, ContextFlagBitset flags); ALCcontext(const ALCcontext&) = delete; ALCcontext& operator=(const ALCcontext&) = delete; ~ALCcontext(); @@ -149,13 +193,25 @@ struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase { #endif void setError(ALenum errorCode, const char *msg, ...); + void sendDebugMessage(std::unique_lock<std::mutex> &debuglock, DebugSource source, + DebugType type, ALuint id, DebugSeverity severity, std::string_view message); + + void debugMessage(DebugSource source, DebugType type, ALuint id, DebugSeverity severity, + std::string_view message) + { + if(!mDebugEnabled.load(std::memory_order_relaxed)) LIKELY + return; + std::unique_lock<std::mutex> debuglock{mDebugCbLock}; + sendDebugMessage(debuglock, source, type, id, severity, message); + } + /* Process-wide current context */ static std::atomic<bool> sGlobalContextLock; static std::atomic<ALCcontext*> sGlobalContext; private: /* Thread-local current context. */ - static thread_local ALCcontext *sLocalContext; + static inline thread_local ALCcontext *sLocalContext{}; /* Thread-local context handling. This handles attempting to release the * context which may have been left current when the thread is destroyed. @@ -168,17 +224,8 @@ private: static thread_local ThreadCtx sThreadContext; public: - /* HACK: MinGW generates bad code when accessing an extern thread_local - * object. Add a wrapper function for it that only accesses it where it's - * defined. - */ -#ifdef __MINGW32__ - static ALCcontext *getThreadContext() noexcept; - static void setThreadContext(ALCcontext *context) noexcept; -#else static ALCcontext *getThreadContext() noexcept { return sLocalContext; } static void setThreadContext(ALCcontext *context) noexcept { sThreadContext.set(context); } -#endif /* Default effect that applies to sources that don't have an effect on send 0. */ static ALeffect sDefaultEffect; diff --git a/alc/device.cpp b/alc/device.cpp index 66b13c5e..27aa6f36 100644 --- a/alc/device.cpp +++ b/alc/device.cpp @@ -34,19 +34,19 @@ ALCdevice::~ALCdevice() Backend = nullptr; - size_t count{std::accumulate(BufferList.cbegin(), BufferList.cend(), size_t{0u}, + size_t count{std::accumulate(BufferList.cbegin(), BufferList.cend(), 0_uz, [](size_t cur, const BufferSubList &sublist) noexcept -> size_t { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })}; if(count > 0) WARN("%zu Buffer%s not deleted\n", count, (count==1)?"":"s"); - count = std::accumulate(EffectList.cbegin(), EffectList.cend(), size_t{0u}, + count = std::accumulate(EffectList.cbegin(), EffectList.cend(), 0_uz, [](size_t cur, const EffectSubList &sublist) noexcept -> size_t { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); }); if(count > 0) WARN("%zu Effect%s not deleted\n", count, (count==1)?"":"s"); - count = std::accumulate(FilterList.cbegin(), FilterList.cend(), size_t{0u}, + count = std::accumulate(FilterList.cbegin(), FilterList.cend(), 0_uz, [](size_t cur, const FilterSubList &sublist) noexcept -> size_t { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); }); if(count > 0) diff --git a/alc/device.h b/alc/device.h index ef50f53e..66f37a7e 100644 --- a/alc/device.h +++ b/alc/device.h @@ -4,9 +4,12 @@ #include <atomic> #include <memory> #include <mutex> +#include <optional> #include <stdint.h> #include <string> +#include <unordered_map> #include <utility> +#include <vector> #include "AL/alc.h" #include "AL/alext.h" @@ -17,7 +20,6 @@ #include "core/device.h" #include "inprogext.h" #include "intrusive_ptr.h" -#include "vector.h" #ifdef ALSOFT_EAX #include "al/eax/x_ram.h" @@ -94,7 +96,7 @@ struct ALCdevice : public al::intrusive_ref<ALCdevice>, DeviceBase { uint AuxiliaryEffectSlotMax{}; std::string mHrtfName; - al::vector<std::string> mHrtfList; + std::vector<std::string> mHrtfList; ALCenum mHrtfStatus{ALC_FALSE}; enum class OutputMode1 : ALCenum { @@ -117,21 +119,25 @@ struct ALCdevice : public al::intrusive_ref<ALCdevice>, DeviceBase { // Map of Buffers for this device std::mutex BufferLock; - al::vector<BufferSubList> BufferList; + std::vector<BufferSubList> BufferList; // Map of Effects for this device std::mutex EffectLock; - al::vector<EffectSubList> EffectList; + std::vector<EffectSubList> EffectList; // Map of Filters for this device std::mutex FilterLock; - al::vector<FilterSubList> FilterList; + std::vector<FilterSubList> FilterList; #ifdef ALSOFT_EAX ALuint eax_x_ram_free_size{eax_x_ram_max_size}; #endif // ALSOFT_EAX + std::unordered_map<ALuint,std::string> mBufferNames; + std::unordered_map<ALuint,std::string> mEffectNames; + std::unordered_map<ALuint,std::string> mFilterNames; + ALCdevice(DeviceType type); ~ALCdevice(); @@ -141,25 +147,28 @@ struct ALCdevice : public al::intrusive_ref<ALCdevice>, DeviceBase { { return GetConfigValueBool(DeviceName.c_str(), block, key, def); } template<typename T> - inline al::optional<T> configValue(const char *block, const char *key) = delete; + inline std::optional<T> configValue(const char *block, const char *key) = delete; DEF_NEWDEL(ALCdevice) }; template<> -inline al::optional<std::string> ALCdevice::configValue(const char *block, const char *key) +inline std::optional<std::string> ALCdevice::configValue(const char *block, const char *key) { return ConfigValueStr(DeviceName.c_str(), block, key); } template<> -inline al::optional<int> ALCdevice::configValue(const char *block, const char *key) +inline std::optional<int> ALCdevice::configValue(const char *block, const char *key) { return ConfigValueInt(DeviceName.c_str(), block, key); } template<> -inline al::optional<uint> ALCdevice::configValue(const char *block, const char *key) +inline std::optional<uint> ALCdevice::configValue(const char *block, const char *key) { return ConfigValueUInt(DeviceName.c_str(), block, key); } template<> -inline al::optional<float> ALCdevice::configValue(const char *block, const char *key) +inline std::optional<float> ALCdevice::configValue(const char *block, const char *key) { return ConfigValueFloat(DeviceName.c_str(), block, key); } template<> -inline al::optional<bool> ALCdevice::configValue(const char *block, const char *key) +inline std::optional<bool> ALCdevice::configValue(const char *block, const char *key) { return ConfigValueBool(DeviceName.c_str(), block, key); } +/** Stores the latest ALC device error. */ +void alcSetError(ALCdevice *device, ALCenum errorCode); + #endif diff --git a/alc/effects/chorus.cpp b/alc/effects/chorus.cpp index 10ccf9f6..9cbc922f 100644 --- a/alc/effects/chorus.cpp +++ b/alc/effects/chorus.cpp @@ -25,6 +25,7 @@ #include <climits> #include <cstdlib> #include <iterator> +#include <vector> #include "alc/effects/base.h" #include "almalloc.h" @@ -41,7 +42,6 @@ #include "core/resampler_limits.h" #include "intrusive_ptr.h" #include "opthelpers.h" -#include "vector.h" namespace { @@ -49,7 +49,7 @@ namespace { using uint = unsigned int; struct ChorusState final : public EffectState { - al::vector<float,16> mDelayBuffer; + std::vector<float> mDelayBuffer; uint mOffset{0}; uint mLfoOffset{0}; @@ -125,16 +125,16 @@ void ChorusState::update(const ContextBase *Context, const EffectSlot *Slot, /* Gains for left and right sides */ static constexpr auto inv_sqrt2 = static_cast<float>(1.0 / al::numbers::sqrt2); - static constexpr auto lcoeffs_pw = CalcDirectionCoeffs({-1.0f, 0.0f, 0.0f}); - static constexpr auto rcoeffs_pw = CalcDirectionCoeffs({ 1.0f, 0.0f, 0.0f}); - static constexpr auto lcoeffs_nrml = CalcDirectionCoeffs({-inv_sqrt2, 0.0f, inv_sqrt2}); - static constexpr auto rcoeffs_nrml = CalcDirectionCoeffs({ inv_sqrt2, 0.0f, inv_sqrt2}); + static constexpr auto lcoeffs_pw = CalcDirectionCoeffs(std::array{-1.0f, 0.0f, 0.0f}); + static constexpr auto rcoeffs_pw = CalcDirectionCoeffs(std::array{ 1.0f, 0.0f, 0.0f}); + static constexpr auto lcoeffs_nrml = CalcDirectionCoeffs(std::array{-inv_sqrt2, 0.0f, inv_sqrt2}); + static constexpr auto rcoeffs_nrml = CalcDirectionCoeffs(std::array{ inv_sqrt2, 0.0f, inv_sqrt2}); auto &lcoeffs = (device->mRenderMode != RenderMode::Pairwise) ? lcoeffs_nrml : lcoeffs_pw; auto &rcoeffs = (device->mRenderMode != RenderMode::Pairwise) ? rcoeffs_nrml : rcoeffs_pw; mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, lcoeffs.data(), Slot->Gain, mGains[0].Target); - ComputePanGains(target.Main, rcoeffs.data(), Slot->Gain, mGains[1].Target); + ComputePanGains(target.Main, lcoeffs, Slot->Gain, mGains[0].Target); + ComputePanGains(target.Main, rcoeffs, Slot->Gain, mGains[1].Target); float rate{props->Chorus.Rate}; if(!(rate > 0.0f)) diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index 7f36c415..517e6b08 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -17,7 +17,6 @@ #include <arm_neon.h> #endif -#include "albyte.h" #include "alcomplex.h" #include "almalloc.h" #include "alnumbers.h" @@ -35,44 +34,49 @@ #include "core/fmt_traits.h" #include "core/mixer.h" #include "intrusive_ptr.h" +#include "pffft.h" #include "polyphase_resampler.h" #include "vector.h" namespace { -/* Convolution reverb is implemented using a segmented overlap-add method. The - * impulse response is broken up into multiple segments of 128 samples, and - * each segment has an FFT applied with a 256-sample buffer (the latter half - * left silent) to get its frequency-domain response. The resulting response - * has its positive/non-mirrored frequencies saved (129 bins) in each segment. +/* Convolution is implemented using a segmented overlap-add method. The impulse + * response is split into multiple segments of 128 samples, and each segment + * has an FFT applied with a 256-sample buffer (the latter half left silent) to + * get its frequency-domain response. The resulting response has its positive/ + * non-mirrored frequencies saved (129 bins) in each segment. Note that since + * the 0- and half-frequency bins are real for a real signal, their imaginary + * components are always 0 and can be dropped, allowing their real components + * to be combined so only 128 complex values are stored for the 129 bins. * - * Input samples are similarly broken up into 128-sample segments, with an FFT - * applied to each new incoming segment to get its 129 bins. A history of FFT'd - * input segments is maintained, equal to the length of the impulse response. + * Input samples are similarly broken up into 128-sample segments, with a 256- + * sample FFT applied to each new incoming segment to get its 129 bins. A + * history of FFT'd input segments is maintained, equal to the number of + * impulse response segments. * - * To apply the reverberation, each impulse response segment is convolved with + * To apply the convolution, each impulse response segment is convolved with * its paired input segment (using complex multiplies, far cheaper than FIRs), - * accumulating into a 256-bin FFT buffer. The input history is then shifted to - * align with later impulse response segments for next time. + * accumulating into a 129-bin FFT buffer. The input history is then shifted to + * align with later impulse response segments for the next input segment. * * An inverse FFT is then applied to the accumulated FFT buffer to get a 256- * sample time-domain response for output, which is split in two halves. The * first half is the 128-sample output, and the second half is a 128-sample * (really, 127) delayed extension, which gets added to the output next time. - * Convolving two time-domain responses of lengths N and M results in a time- - * domain signal of length N+M-1, and this holds true regardless of the - * convolution being applied in the frequency domain, so these "overflow" - * samples need to be accounted for. + * Convolving two time-domain responses of length N results in a time-domain + * signal of length N*2 - 1, and this holds true regardless of the convolution + * being applied in the frequency domain, so these "overflow" samples need to + * be accounted for. * - * To avoid a delay with gathering enough input samples to apply an FFT with, - * the first segment is applied directly in the time-domain as the samples come - * in. Once enough have been retrieved, the FFT is applied on the input and - * it's paired with the remaining (FFT'd) filter segments for processing. + * To avoid a delay with gathering enough input samples for the FFT, the first + * segment is applied directly in the time-domain as the samples come in. Once + * enough have been retrieved, the FFT is applied on the input and it's paired + * with the remaining (FFT'd) filter segments for processing. */ -void LoadSamples(float *RESTRICT dst, const al::byte *src, const size_t srcstep, FmtType srctype, +void LoadSamples(float *RESTRICT dst, const std::byte *src, const size_t srcstep, FmtType srctype, const size_t samples) noexcept { #define HANDLE_FMT(T) case T: al::LoadSampleArray<T>(dst, src, srcstep, samples); break @@ -80,6 +84,7 @@ void LoadSamples(float *RESTRICT dst, const al::byte *src, const size_t srcstep, { HANDLE_FMT(FmtUByte); HANDLE_FMT(FmtShort); + HANDLE_FMT(FmtInt); HANDLE_FMT(FmtFloat); HANDLE_FMT(FmtDouble); HANDLE_FMT(FmtMulaw); @@ -94,40 +99,43 @@ void LoadSamples(float *RESTRICT dst, const al::byte *src, const size_t srcstep, } -inline auto& GetAmbiScales(AmbiScaling scaletype) noexcept +constexpr auto GetAmbiScales(AmbiScaling scaletype) noexcept { switch(scaletype) { - case AmbiScaling::FuMa: return AmbiScale::FromFuMa(); - case AmbiScaling::SN3D: return AmbiScale::FromSN3D(); - case AmbiScaling::UHJ: return AmbiScale::FromUHJ(); + case AmbiScaling::FuMa: return al::span{AmbiScale::FromFuMa}; + case AmbiScaling::SN3D: return al::span{AmbiScale::FromSN3D}; + case AmbiScaling::UHJ: return al::span{AmbiScale::FromUHJ}; case AmbiScaling::N3D: break; } - return AmbiScale::FromN3D(); + return al::span{AmbiScale::FromN3D}; } -inline auto& GetAmbiLayout(AmbiLayout layouttype) noexcept +constexpr auto GetAmbiLayout(AmbiLayout layouttype) noexcept { - if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa(); - return AmbiIndex::FromACN(); + if(layouttype == AmbiLayout::FuMa) return al::span{AmbiIndex::FromFuMa}; + return al::span{AmbiIndex::FromACN}; } -inline auto& GetAmbi2DLayout(AmbiLayout layouttype) noexcept +constexpr auto GetAmbi2DLayout(AmbiLayout layouttype) noexcept { - if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa2D(); - return AmbiIndex::FromACN2D(); + if(layouttype == AmbiLayout::FuMa) return al::span{AmbiIndex::FromFuMa2D}; + return al::span{AmbiIndex::FromACN2D}; } -struct ChanMap { +constexpr float sin30{0.5f}; +constexpr float cos30{0.866025403785f}; +constexpr float sin45{al::numbers::sqrt2_v<float>*0.5f}; +constexpr float cos45{al::numbers::sqrt2_v<float>*0.5f}; +constexpr float sin110{ 0.939692620786f}; +constexpr float cos110{-0.342020143326f}; + +struct ChanPosMap { Channel channel; - float angle; - float elevation; + std::array<float,3> pos; }; -constexpr float Deg2Rad(float x) noexcept -{ return static_cast<float>(al::numbers::pi / 180.0 * x); } - using complex_f = std::complex<float>; @@ -181,6 +189,13 @@ void apply_fir(al::span<float> dst, const float *RESTRICT src, const float *REST #endif } + +struct PFFFTSetupDeleter { + void operator()(PFFFT_Setup *ptr) { pffft_destroy_setup(ptr); } +}; +using PFFFTSetupPtr = std::unique_ptr<PFFFT_Setup,PFFFTSetupDeleter>; + + struct ConvolutionState final : public EffectState { FmtChannels mChannels{}; AmbiLayout mAmbiLayout{}; @@ -188,11 +203,13 @@ struct ConvolutionState final : public EffectState { uint mAmbiOrder{}; size_t mFifoPos{0}; - std::array<float,ConvolveUpdateSamples*2> mInput{}; + alignas(16) std::array<float,ConvolveUpdateSamples*2> mInput{}; al::vector<std::array<float,ConvolveUpdateSamples>,16> mFilter; al::vector<std::array<float,ConvolveUpdateSamples*2>,16> mOutput; - alignas(16) std::array<complex_f,ConvolveUpdateSize> mFftBuffer{}; + PFFFTSetupPtr mFft{}; + alignas(16) std::array<float,ConvolveUpdateSize> mFftBuffer{}; + alignas(16) std::array<float,ConvolveUpdateSize> mFftWorkBuffer{}; size_t mCurrentSegment{0}; size_t mNumConvolveSegs{0}; @@ -204,9 +221,8 @@ struct ConvolutionState final : public EffectState { float Current[MAX_OUTPUT_CHANNELS]{}; float Target[MAX_OUTPUT_CHANNELS]{}; }; - using ChannelDataArray = al::FlexArray<ChannelData>; - std::unique_ptr<ChannelDataArray> mChans; - std::unique_ptr<complex_f[]> mComplexData; + std::vector<ChannelData> mChans; + al::vector<float,16> mComplexData; ConvolutionState() = default; @@ -229,7 +245,7 @@ struct ConvolutionState final : public EffectState { void ConvolutionState::NormalMix(const al::span<FloatBufferLine> samplesOut, const size_t samplesToDo) { - for(auto &chan : *mChans) + for(auto &chan : mChans) MixSamples({chan.mBuffer.data(), samplesToDo}, samplesOut, chan.Current, chan.Target, samplesToDo, 0); } @@ -237,7 +253,7 @@ void ConvolutionState::NormalMix(const al::span<FloatBufferLine> samplesOut, void ConvolutionState::UpsampleMix(const al::span<FloatBufferLine> samplesOut, const size_t samplesToDo) { - for(auto &chan : *mChans) + for(auto &chan : mChans) { const al::span<float> src{chan.mBuffer.data(), samplesToDo}; chan.mFilter.processScale(src, chan.mHfScale, chan.mLfScale); @@ -251,19 +267,23 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag using UhjDecoderType = UhjDecoder<512>; static constexpr auto DecoderPadding = UhjDecoderType::sInputPadding; - constexpr uint MaxConvolveAmbiOrder{1u}; + static constexpr uint MaxConvolveAmbiOrder{1u}; + + if(!mFft) + mFft = PFFFTSetupPtr{pffft_new_setup(ConvolveUpdateSize, PFFFT_REAL)}; mFifoPos = 0; mInput.fill(0.0f); decltype(mFilter){}.swap(mFilter); decltype(mOutput){}.swap(mOutput); - mFftBuffer.fill(complex_f{}); + mFftBuffer.fill(0.0f); + mFftWorkBuffer.fill(0.0f); mCurrentSegment = 0; mNumConvolveSegs = 0; - mChans = nullptr; - mComplexData = nullptr; + decltype(mChans){}.swap(mChans); + decltype(mComplexData){}.swap(mComplexData); /* An empty buffer doesn't need a convolution filter. */ if(!buffer || buffer->mSampleLen < 1) return; @@ -273,12 +293,11 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag mAmbiScaling = IsUHJ(mChannels) ? AmbiScaling::UHJ : buffer->mAmbiScaling; mAmbiOrder = minu(buffer->mAmbiOrder, MaxConvolveAmbiOrder); - constexpr size_t m{ConvolveUpdateSize/2 + 1}; const auto bytesPerSample = BytesFromFmt(buffer->mType); const auto realChannels = buffer->channelsFromFmt(); const auto numChannels = (mChannels == FmtUHJ2) ? 3u : ChannelsFromFmt(mChannels, mAmbiOrder); - mChans = ChannelDataArray::Create(numChannels); + mChans.resize(numChannels); /* The impulse response needs to have the same sample rate as the input and * output. The bsinc24 resampler is decent, but there is high-frequency @@ -293,7 +312,7 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag buffer->mSampleRate); const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->Frequency)}; - for(auto &e : *mChans) + for(auto &e : mChans) e.mFilter = splitter; mFilter.resize(numChannels, {}); @@ -307,9 +326,8 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag mNumConvolveSegs = (resampledCount+(ConvolveUpdateSamples-1)) / ConvolveUpdateSamples; mNumConvolveSegs = maxz(mNumConvolveSegs, 2) - 1; - const size_t complex_length{mNumConvolveSegs * m * (numChannels+1)}; - mComplexData = std::make_unique<complex_f[]>(complex_length); - std::fill_n(mComplexData.get(), complex_length, complex_f{}); + const size_t complex_length{mNumConvolveSegs * ConvolveUpdateSize * (numChannels+1)}; + mComplexData.resize(complex_length, 0.0f); /* Load the samples from the buffer. */ const size_t srclinelength{RoundUp(buffer->mSampleLen+DecoderPadding, 16)}; @@ -330,7 +348,10 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag auto ressamples = std::make_unique<double[]>(buffer->mSampleLen + (resampler ? resampledCount : 0)); - complex_f *filteriter = mComplexData.get() + mNumConvolveSegs*m; + auto ffttmp = al::vector<float,16>(ConvolveUpdateSize); + auto fftbuffer = std::vector<std::complex<double>>(ConvolveUpdateSize); + + float *filteriter = mComplexData.data() + mNumConvolveSegs*ConvolveUpdateSize; for(size_t c{0};c < numChannels;++c) { /* Resample to match the device. */ @@ -351,71 +372,85 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag std::transform(ressamples.get(), ressamples.get()+first_size, mFilter[c].rbegin(), [](const double d) noexcept -> float { return static_cast<float>(d); }); - auto fftbuffer = std::vector<std::complex<double>>(ConvolveUpdateSize); size_t done{first_size}; for(size_t s{0};s < mNumConvolveSegs;++s) { const size_t todo{minz(resampledCount-done, ConvolveUpdateSamples)}; + /* Apply a double-precision forward FFT for more precise frequency + * measurements. + */ auto iter = std::copy_n(&ressamples[done], todo, fftbuffer.begin()); done += todo; std::fill(iter, fftbuffer.end(), std::complex<double>{}); + forward_fft(al::span{fftbuffer}); - forward_fft(al::as_span(fftbuffer)); - filteriter = std::copy_n(fftbuffer.cbegin(), m, filteriter); + /* Convert to, and pack in, a float buffer for PFFFT. Note that the + * first bin stores the real component of the half-frequency bin in + * the imaginary component. Also scale the FFT by its length so the + * iFFT'd output will be normalized. + */ + static constexpr float fftscale{1.0f / float{ConvolveUpdateSize}}; + for(size_t i{0};i < ConvolveUpdateSamples;++i) + { + ffttmp[i*2 ] = static_cast<float>(fftbuffer[i].real()) * fftscale; + ffttmp[i*2 + 1] = static_cast<float>((i == 0) ? + fftbuffer[ConvolveUpdateSamples].real() : fftbuffer[i].imag()) * fftscale; + } + /* Reorder backward to make it suitable for pffft_zconvolve and the + * subsequent pffft_transform(..., PFFFT_BACKWARD). + */ + pffft_zreorder(mFft.get(), ffttmp.data(), al::to_address(filteriter), PFFFT_BACKWARD); + filteriter += ConvolveUpdateSize; } } } void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps* /*props*/, const EffectTarget target) + const EffectProps *props, const EffectTarget target) { - /* NOTE: Stereo and Rear are slightly different from normal mixing (as - * defined in alu.cpp). These are 45 degrees from center, rather than the - * 30 degrees used there. - * - * TODO: LFE is not mixed to output. This will require each buffer channel + /* TODO: LFE is not mixed to output. This will require each buffer channel * to have its own output target since the main mixing buffer won't have an * LFE channel (due to being B-Format). */ - static constexpr ChanMap MonoMap[1]{ - { FrontCenter, 0.0f, 0.0f } + static constexpr ChanPosMap MonoMap[1]{ + { FrontCenter, std::array{0.0f, 0.0f, -1.0f} } }, StereoMap[2]{ - { FrontLeft, Deg2Rad(-45.0f), Deg2Rad(0.0f) }, - { FrontRight, Deg2Rad( 45.0f), Deg2Rad(0.0f) } + { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, + { FrontRight, std::array{ sin30, 0.0f, -cos30} }, }, RearMap[2]{ - { BackLeft, Deg2Rad(-135.0f), Deg2Rad(0.0f) }, - { BackRight, Deg2Rad( 135.0f), Deg2Rad(0.0f) } + { BackLeft, std::array{-sin30, 0.0f, cos30} }, + { BackRight, std::array{ sin30, 0.0f, cos30} }, }, 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) } + { FrontLeft, std::array{-sin45, 0.0f, -cos45} }, + { FrontRight, std::array{ sin45, 0.0f, -cos45} }, + { BackLeft, std::array{-sin45, 0.0f, cos45} }, + { BackRight, std::array{ sin45, 0.0f, cos45} }, }, 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) } + { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, + { FrontRight, std::array{ sin30, 0.0f, -cos30} }, + { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, + { LFE, {} }, + { SideLeft, std::array{-sin110, 0.0f, -cos110} }, + { SideRight, std::array{ sin110, 0.0f, -cos110} }, }, 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) } + { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, + { FrontRight, std::array{ sin30, 0.0f, -cos30} }, + { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, + { LFE, {} }, + { BackCenter, std::array{ 0.0f, 0.0f, 1.0f} }, + { SideLeft, std::array{-1.0f, 0.0f, 0.0f} }, + { SideRight, std::array{ 1.0f, 0.0f, 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) } + { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, + { FrontRight, std::array{ sin30, 0.0f, -cos30} }, + { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, + { LFE, {} }, + { BackLeft, std::array{-sin30, 0.0f, cos30} }, + { BackRight, std::array{ sin30, 0.0f, cos30} }, + { SideLeft, std::array{ -1.0f, 0.0f, 0.0f} }, + { SideRight, std::array{ 1.0f, 0.0f, 0.0f} }, }; if(mNumConvolveSegs < 1) UNLIKELY @@ -423,7 +458,7 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot mMix = &ConvolutionState::NormalMix; - for(auto &chan : *mChans) + for(auto &chan : mChans) std::fill(std::begin(chan.Target), std::end(chan.Target), 0.0f); const float gain{slot->Gain}; if(IsAmbisonic(mChannels)) @@ -432,46 +467,66 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot if(mChannels == FmtUHJ2 && !device->mUhjEncoder) { mMix = &ConvolutionState::UpsampleMix; - (*mChans)[0].mHfScale = 1.0f; - (*mChans)[0].mLfScale = DecoderBase::sWLFScale; - (*mChans)[1].mHfScale = 1.0f; - (*mChans)[1].mLfScale = DecoderBase::sXYLFScale; - (*mChans)[2].mHfScale = 1.0f; - (*mChans)[2].mLfScale = DecoderBase::sXYLFScale; + mChans[0].mHfScale = 1.0f; + mChans[0].mLfScale = DecoderBase::sWLFScale; + mChans[1].mHfScale = 1.0f; + mChans[1].mLfScale = DecoderBase::sXYLFScale; + mChans[2].mHfScale = 1.0f; + mChans[2].mLfScale = DecoderBase::sXYLFScale; } else if(device->mAmbiOrder > mAmbiOrder) { mMix = &ConvolutionState::UpsampleMix; const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder, device->m2DMixing); - (*mChans)[0].mHfScale = scales[0]; - (*mChans)[0].mLfScale = 1.0f; - for(size_t i{1};i < mChans->size();++i) + mChans[0].mHfScale = scales[0]; + mChans[0].mLfScale = 1.0f; + for(size_t i{1};i < mChans.size();++i) { - (*mChans)[i].mHfScale = scales[1]; - (*mChans)[i].mLfScale = 1.0f; + mChans[i].mHfScale = scales[1]; + mChans[i].mLfScale = 1.0f; } } mOutTarget = target.Main->Buffer; - auto&& scales = GetAmbiScales(mAmbiScaling); + alu::Vector N{props->Convolution.OrientAt[0], props->Convolution.OrientAt[1], + props->Convolution.OrientAt[2], 0.0f}; + N.normalize(); + alu::Vector V{props->Convolution.OrientUp[0], props->Convolution.OrientUp[1], + props->Convolution.OrientUp[2], 0.0f}; + V.normalize(); + /* Build and normalize right-vector */ + alu::Vector U{N.cross_product(V)}; + U.normalize(); + + const float mixmatrix[4][4]{ + {1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, U[0], -U[1], U[2]}, + {0.0f, -V[0], V[1], -V[2]}, + {0.0f, -N[0], N[1], -N[2]}, + }; + + const auto scales = GetAmbiScales(mAmbiScaling); const uint8_t *index_map{Is2DAmbisonic(mChannels) ? GetAmbi2DLayout(mAmbiLayout).data() : GetAmbiLayout(mAmbiLayout).data()}; std::array<float,MaxAmbiChannels> coeffs{}; - for(size_t c{0u};c < mChans->size();++c) + for(size_t c{0u};c < mChans.size();++c) { const size_t acn{index_map[c]}; - coeffs[acn] = scales[acn]; - ComputePanGains(target.Main, coeffs.data(), gain, (*mChans)[c].Target); - coeffs[acn] = 0.0f; + const float scale{scales[acn]}; + + for(size_t x{0};x < 4;++x) + coeffs[x] = mixmatrix[acn][x] * scale; + + ComputePanGains(target.Main, coeffs, gain, mChans[c].Target); } } else { DeviceBase *device{context->mDevice}; - al::span<const ChanMap> chanmap{}; + al::span<const ChanPosMap> chanmap{}; switch(mChannels) { case FmtMono: chanmap = MonoMap; break; @@ -493,28 +548,55 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot mOutTarget = target.Main->Buffer; if(device->mRenderMode == RenderMode::Pairwise) { - auto ScaleAzimuthFront = [](float azimuth, float scale) -> float + /* Scales the azimuth of the given vector by 3 if it's in front. + * Effectively scales +/-30 degrees to +/-90 degrees, leaving > +90 + * and < -90 alone. + */ + auto ScaleAzimuthFront = [](std::array<float,3> pos) -> std::array<float,3> { - constexpr float half_pi{al::numbers::pi_v<float>*0.5f}; - const float abs_azi{std::fabs(azimuth)}; - if(!(abs_azi >= half_pi)) - return std::copysign(minf(abs_azi*scale, half_pi), azimuth); - return azimuth; + if(pos[2] < 0.0f) + { + /* Normalize the length of the x,z components for a 2D + * vector of the azimuth angle. Negate Z since {0,0,-1} is + * angle 0. + */ + const float len2d{std::sqrt(pos[0]*pos[0] + pos[2]*pos[2])}; + float x{pos[0] / len2d}; + float z{-pos[2] / len2d}; + + /* Z > cos(pi/6) = -30 < azimuth < 30 degrees. */ + if(z > cos30) + { + /* Triple the angle represented by x,z. */ + x = x*3.0f - x*x*x*4.0f; + z = z*z*z*4.0f - z*3.0f; + + /* Scale the vector back to fit in 3D. */ + pos[0] = x * len2d; + pos[2] = -z * len2d; + } + else + { + /* If azimuth >= 30 degrees, clamp to 90 degrees. */ + pos[0] = std::copysign(len2d, pos[0]); + pos[2] = 0.0f; + } + } + return pos; }; for(size_t i{0};i < chanmap.size();++i) { if(chanmap[i].channel == LFE) continue; - const auto coeffs = CalcAngleCoeffs(ScaleAzimuthFront(chanmap[i].angle, 2.0f), - chanmap[i].elevation, 0.0f); - ComputePanGains(target.Main, coeffs.data(), gain, (*mChans)[i].Target); + const auto coeffs = CalcDirectionCoeffs(ScaleAzimuthFront(chanmap[i].pos), 0.0f); + ComputePanGains(target.Main, coeffs, gain, mChans[i].Target); } } else for(size_t i{0};i < chanmap.size();++i) { if(chanmap[i].channel == LFE) continue; - const auto coeffs = CalcAngleCoeffs(chanmap[i].angle, chanmap[i].elevation, 0.0f); - ComputePanGains(target.Main, coeffs.data(), gain, (*mChans)[i].Target); + const auto coeffs = CalcDirectionCoeffs(chanmap[i].pos, 0.0f); + ComputePanGains(target.Main, coeffs, gain, mChans[i].Target); } } } @@ -525,9 +607,7 @@ void ConvolutionState::process(const size_t samplesToDo, if(mNumConvolveSegs < 1) UNLIKELY return; - constexpr size_t m{ConvolveUpdateSize/2 + 1}; size_t curseg{mCurrentSegment}; - auto &chans = *mChans; for(size_t base{0u};base < samplesToDo;) { @@ -539,9 +619,9 @@ void ConvolutionState::process(const size_t samplesToDo, /* Apply the FIR for the newly retrieved input samples, and combine it * with the inverse FFT'd output samples. */ - for(size_t c{0};c < chans.size();++c) + for(size_t c{0};c < mChans.size();++c) { - auto buf_iter = chans[c].mBuffer.begin() + base; + auto buf_iter = mChans[c].mBuffer.begin() + base; apply_fir({buf_iter, todo}, mInput.data()+1 + mFifoPos, mFilter[c].data()); auto fifo_iter = mOutput[c].begin() + mFifoPos; @@ -557,59 +637,49 @@ void ConvolutionState::process(const size_t samplesToDo, /* Move the newest input to the front for the next iteration's history. */ std::copy(mInput.cbegin()+ConvolveUpdateSamples, mInput.cend(), mInput.begin()); + std::fill(mInput.begin()+ConvolveUpdateSamples, mInput.end(), 0.0f); - /* Calculate the frequency domain response and add the relevant + /* Calculate the frequency-domain response and add the relevant * frequency bins to the FFT history. */ - auto fftiter = std::copy_n(mInput.cbegin(), ConvolveUpdateSamples, mFftBuffer.begin()); - std::fill(fftiter, mFftBuffer.end(), complex_f{}); - forward_fft(al::as_span(mFftBuffer)); + pffft_transform(mFft.get(), mInput.data(), mComplexData.data() + curseg*ConvolveUpdateSize, + mFftWorkBuffer.data(), PFFFT_FORWARD); - std::copy_n(mFftBuffer.cbegin(), m, &mComplexData[curseg*m]); - - const complex_f *RESTRICT filter{mComplexData.get() + mNumConvolveSegs*m}; - for(size_t c{0};c < chans.size();++c) + const float *filter{mComplexData.data() + mNumConvolveSegs*ConvolveUpdateSize}; + for(size_t c{0};c < mChans.size();++c) { - std::fill_n(mFftBuffer.begin(), m, complex_f{}); - /* Convolve each input segment with its IR filter counterpart * (aligned in time). */ - const complex_f *RESTRICT input{&mComplexData[curseg*m]}; + mFftBuffer.fill(0.0f); + const float *input{&mComplexData[curseg*ConvolveUpdateSize]}; for(size_t s{curseg};s < mNumConvolveSegs;++s) { - for(size_t i{0};i < m;++i,++input,++filter) - mFftBuffer[i] += *input * *filter; + pffft_zconvolve_accumulate(mFft.get(), input, filter, mFftBuffer.data()); + input += ConvolveUpdateSize; + filter += ConvolveUpdateSize; } - input = mComplexData.get(); + input = mComplexData.data(); for(size_t s{0};s < curseg;++s) { - for(size_t i{0};i < m;++i,++input,++filter) - mFftBuffer[i] += *input * *filter; + pffft_zconvolve_accumulate(mFft.get(), input, filter, mFftBuffer.data()); + input += ConvolveUpdateSize; + filter += ConvolveUpdateSize; } - /* Reconstruct the mirrored/negative frequencies to do a proper - * inverse FFT. - */ - for(size_t i{m};i < ConvolveUpdateSize;++i) - mFftBuffer[i] = std::conj(mFftBuffer[ConvolveUpdateSize-i]); - /* Apply iFFT to get the 256 (really 255) samples for output. The * 128 output samples are combined with the last output's 127 * second-half samples (and this output's second half is * subsequently saved for next time). */ - inverse_fft(al::as_span(mFftBuffer)); + pffft_transform(mFft.get(), mFftBuffer.data(), mFftBuffer.data(), + mFftWorkBuffer.data(), PFFFT_BACKWARD); - /* The iFFT'd response is scaled up by the number of bins, so apply - * the inverse to normalize the output. - */ + /* The filter was attenuated, so the response is already scaled. */ for(size_t i{0};i < ConvolveUpdateSamples;++i) - mOutput[c][i] = - (mFftBuffer[i].real()+mOutput[c][ConvolveUpdateSamples+i]) * - (1.0f/float{ConvolveUpdateSize}); + mOutput[c][i] = mFftBuffer[i] + mOutput[c][ConvolveUpdateSamples+i]; for(size_t i{0};i < ConvolveUpdateSamples;++i) - mOutput[c][ConvolveUpdateSamples+i] = mFftBuffer[ConvolveUpdateSamples+i].real(); + mOutput[c][ConvolveUpdateSamples+i] = mFftBuffer[ConvolveUpdateSamples+i]; } /* Shift the input history. */ diff --git a/alc/effects/dedicated.cpp b/alc/effects/dedicated.cpp index 047e6761..a9131bfa 100644 --- a/alc/effects/dedicated.cpp +++ b/alc/effects/dedicated.cpp @@ -74,7 +74,7 @@ void DedicatedState::update(const ContextBase*, const EffectSlot *slot, if(slot->EffectType == EffectSlotType::DedicatedLFE) { - const uint idx{target.RealOut ? target.RealOut->ChannelIndex[LFE] : InvalidChannelIndex}; + const size_t idx{target.RealOut ? target.RealOut->ChannelIndex[LFE] : InvalidChannelIndex}; if(idx != InvalidChannelIndex) { mOutTarget = target.RealOut->Buffer; @@ -85,7 +85,7 @@ void DedicatedState::update(const ContextBase*, const EffectSlot *slot, { /* Dialog goes to the front-center speaker if it exists, otherwise it * plays from the front-center location. */ - const uint idx{target.RealOut ? target.RealOut->ChannelIndex[FrontCenter] + const size_t idx{target.RealOut ? target.RealOut->ChannelIndex[FrontCenter] : InvalidChannelIndex}; if(idx != InvalidChannelIndex) { @@ -94,10 +94,10 @@ void DedicatedState::update(const ContextBase*, const EffectSlot *slot, } else { - static constexpr auto coeffs = CalcDirectionCoeffs({0.0f, 0.0f, -1.0f}); + static constexpr auto coeffs = CalcDirectionCoeffs(std::array{0.0f, 0.0f, -1.0f}); mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, coeffs.data(), Gain, mTargetGains); + ComputePanGains(target.Main, coeffs, Gain, mTargetGains); } } } diff --git a/alc/effects/distortion.cpp b/alc/effects/distortion.cpp index b4e2167e..3d77ff35 100644 --- a/alc/effects/distortion.cpp +++ b/alc/effects/distortion.cpp @@ -95,10 +95,10 @@ void DistortionState::update(const ContextBase *context, const EffectSlot *slot, bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f); mBandpass.setParamsFromBandwidth(BiquadType::BandPass, cutoff/frequency/4.0f, 1.0f, bandwidth); - static constexpr auto coeffs = CalcDirectionCoeffs({0.0f, 0.0f, -1.0f}); + static constexpr auto coeffs = CalcDirectionCoeffs(std::array{0.0f, 0.0f, -1.0f}); mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, coeffs.data(), slot->Gain*props->Distortion.Gain, mGain); + ComputePanGains(target.Main, coeffs, slot->Gain*props->Distortion.Gain, mGain); } void DistortionState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) diff --git a/alc/effects/echo.cpp b/alc/effects/echo.cpp index a69529dc..714649c9 100644 --- a/alc/effects/echo.cpp +++ b/alc/effects/echo.cpp @@ -25,6 +25,7 @@ #include <cstdlib> #include <iterator> #include <tuple> +#include <vector> #include "alc/effects/base.h" #include "almalloc.h" @@ -39,7 +40,6 @@ #include "core/mixer.h" #include "intrusive_ptr.h" #include "opthelpers.h" -#include "vector.h" namespace { @@ -49,7 +49,7 @@ using uint = unsigned int; constexpr float LowpassFreqRef{5000.0f}; struct EchoState final : public EffectState { - al::vector<float,16> mSampleBuffer; + std::vector<float> mSampleBuffer; // The echo is two tap. The delay is the number of samples from before the // current offset @@ -87,7 +87,7 @@ void EchoState::deviceUpdate(const DeviceBase *Device, const BufferStorage*) const uint maxlen{NextPowerOf2(float2uint(EchoMaxDelay*frequency + 0.5f) + float2uint(EchoMaxLRDelay*frequency + 0.5f))}; if(maxlen != mSampleBuffer.size()) - al::vector<float,16>(maxlen).swap(mSampleBuffer); + decltype(mSampleBuffer)(maxlen).swap(mSampleBuffer); std::fill(mSampleBuffer.begin(), mSampleBuffer.end(), 0.0f); for(auto &e : mGains) @@ -118,8 +118,8 @@ void EchoState::update(const ContextBase *context, const EffectSlot *slot, const auto coeffs1 = CalcAngleCoeffs( angle, 0.0f, 0.0f); mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, coeffs0.data(), slot->Gain, mGains[0].Target); - ComputePanGains(target.Main, coeffs1.data(), slot->Gain, mGains[1].Target); + ComputePanGains(target.Main, coeffs0, slot->Gain, mGains[0].Target); + ComputePanGains(target.Main, coeffs1, slot->Gain, mGains[1].Target); } void EchoState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) diff --git a/alc/effects/fshifter.cpp b/alc/effects/fshifter.cpp index 3e6a7385..d3989e84 100644 --- a/alc/effects/fshifter.cpp +++ b/alc/effects/fshifter.cpp @@ -164,16 +164,16 @@ void FshifterState::update(const ContextBase *context, const EffectSlot *slot, } static constexpr auto inv_sqrt2 = static_cast<float>(1.0 / al::numbers::sqrt2); - static constexpr auto lcoeffs_pw = CalcDirectionCoeffs({-1.0f, 0.0f, 0.0f}); - static constexpr auto rcoeffs_pw = CalcDirectionCoeffs({ 1.0f, 0.0f, 0.0f}); - static constexpr auto lcoeffs_nrml = CalcDirectionCoeffs({-inv_sqrt2, 0.0f, inv_sqrt2}); - static constexpr auto rcoeffs_nrml = CalcDirectionCoeffs({ inv_sqrt2, 0.0f, inv_sqrt2}); + static constexpr auto lcoeffs_pw = CalcDirectionCoeffs(std::array{-1.0f, 0.0f, 0.0f}); + static constexpr auto rcoeffs_pw = CalcDirectionCoeffs(std::array{ 1.0f, 0.0f, 0.0f}); + static constexpr auto lcoeffs_nrml = CalcDirectionCoeffs(std::array{-inv_sqrt2, 0.0f, inv_sqrt2}); + static constexpr auto rcoeffs_nrml = CalcDirectionCoeffs(std::array{ inv_sqrt2, 0.0f, inv_sqrt2}); auto &lcoeffs = (device->mRenderMode != RenderMode::Pairwise) ? lcoeffs_nrml : lcoeffs_pw; auto &rcoeffs = (device->mRenderMode != RenderMode::Pairwise) ? rcoeffs_nrml : rcoeffs_pw; mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, lcoeffs.data(), slot->Gain, mGains[0].Target); - ComputePanGains(target.Main, rcoeffs.data(), slot->Gain, mGains[1].Target); + ComputePanGains(target.Main, lcoeffs, slot->Gain, mGains[0].Target); + ComputePanGains(target.Main, rcoeffs, slot->Gain, mGains[1].Target); } void FshifterState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) diff --git a/alc/effects/modulator.cpp b/alc/effects/modulator.cpp index 14ee5004..f99ba19c 100644 --- a/alc/effects/modulator.cpp +++ b/alc/effects/modulator.cpp @@ -45,43 +45,49 @@ namespace { using uint = unsigned int; -#define MAX_UPDATE_SAMPLES 128 +inline float Sin(uint index, float scale) +{ return std::sin(static_cast<float>(index) * scale); } -#define WAVEFORM_FRACBITS 24 -#define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS) -#define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1) +inline float Saw(uint index, float scale) +{ return static_cast<float>(index)*scale - 1.0f; } -inline float Sin(uint index) -{ - constexpr float scale{al::numbers::pi_v<float>*2.0f / WAVEFORM_FRACONE}; - return std::sin(static_cast<float>(index) * scale); -} +inline float Square(uint index, float scale) +{ return (static_cast<float>(index)*scale < 0.5f)*2.0f - 1.0f; } -inline float Saw(uint index) -{ return static_cast<float>(index)*(2.0f/WAVEFORM_FRACONE) - 1.0f; } +inline float One(uint, float) +{ return 1.0f; } -inline float Square(uint index) -{ return static_cast<float>(static_cast<int>((index>>(WAVEFORM_FRACBITS-2))&2) - 1); } +struct ModulatorState final : public EffectState { + template<float (&func)(uint,float)> + void Modulate(size_t todo) + { + const uint range{mRange}; + const float scale{mIndexScale}; + uint index{mIndex}; -inline float One(uint) { return 1.0f; } + ASSUME(range > 1); + ASSUME(todo > 0); -template<float (&func)(uint)> -void Modulate(float *RESTRICT dst, uint index, const uint step, size_t todo) -{ - for(size_t i{0u};i < todo;i++) - { - index += step; - index &= WAVEFORM_FRACMASK; - dst[i] = func(index); + for(size_t i{0};i < todo;) + { + size_t rem{minz(todo-i, range-index)}; + do { + mModSamples[i++] = func(index++, scale); + } while(--rem); + if(index == range) + index = 0; + } + mIndex = index; } -} - -struct ModulatorState final : public EffectState { - void (*mGetSamples)(float*RESTRICT, uint, const uint, size_t){}; + void (ModulatorState::*mGenModSamples)(size_t){}; uint mIndex{0}; - uint mStep{1}; + uint mRange{1}; + float mIndexScale{0.0f}; + + alignas(16) FloatBufferLine mModSamples{}; + alignas(16) FloatBufferLine mBuffer{}; struct { uint mTargetChannel{InvalidChannelIndex}; @@ -102,6 +108,13 @@ struct ModulatorState final : public EffectState { DEF_NEWDEL(ModulatorState) }; +template<> +void ModulatorState::Modulate<One>(size_t todo) +{ + std::fill_n(mModSamples.begin(), todo, 1.0f); + mIndex = 0; +} + void ModulatorState::deviceUpdate(const DeviceBase*, const BufferStorage*) { for(auto &e : mChans) @@ -117,17 +130,46 @@ void ModulatorState::update(const ContextBase *context, const EffectSlot *slot, { const DeviceBase *device{context->mDevice}; - const float step{props->Modulator.Frequency / static_cast<float>(device->Frequency)}; - mStep = fastf2u(clampf(step*WAVEFORM_FRACONE, 0.0f, float{WAVEFORM_FRACONE-1})); - - if(mStep == 0) - mGetSamples = Modulate<One>; + /* The effective frequency will be adjusted to have a whole number of + * samples per cycle (at 48khz, that allows 8000, 6857.14, 6000, 5333.33, + * 4800, etc). We could do better by using fixed-point stepping over a sin + * function, with additive synthesis for the square and sawtooth waveforms, + * but that may need a more efficient sin function since it needs to do + * many iterations per sample. + */ + const float samplesPerCycle{props->Modulator.Frequency > 0.0f + ? static_cast<float>(device->Frequency)/props->Modulator.Frequency + 0.5f + : 1.0f}; + const uint range{static_cast<uint>(clampf(samplesPerCycle, 1.0f, + static_cast<float>(device->Frequency)))}; + mIndex = static_cast<uint>(uint64_t{mIndex} * range / mRange); + mRange = range; + + if(mRange == 1) + { + mIndexScale = 0.0f; + mGenModSamples = &ModulatorState::Modulate<One>; + } else if(props->Modulator.Waveform == ModulatorWaveform::Sinusoid) - mGetSamples = Modulate<Sin>; + { + mIndexScale = al::numbers::pi_v<float>*2.0f / static_cast<float>(mRange); + mGenModSamples = &ModulatorState::Modulate<Sin>; + } else if(props->Modulator.Waveform == ModulatorWaveform::Sawtooth) - mGetSamples = Modulate<Saw>; + { + mIndexScale = 2.0f / static_cast<float>(mRange-1); + mGenModSamples = &ModulatorState::Modulate<Saw>; + } else /*if(props->Modulator.Waveform == ModulatorWaveform::Square)*/ - mGetSamples = Modulate<Square>; + { + /* For square wave, the range should be even (there should be an equal + * number of high and low samples). An odd number of samples per cycle + * would need a more complex value generator. + */ + mRange = (mRange+1) & ~1u; + mIndexScale = 1.0f / static_cast<float>(mRange-1); + mGenModSamples = &ModulatorState::Modulate<Square>; + } float f0norm{props->Modulator.HighPassCutoff / static_cast<float>(device->Frequency)}; f0norm = clampf(f0norm, 1.0f/512.0f, 0.49f); @@ -147,34 +189,22 @@ void ModulatorState::update(const ContextBase *context, const EffectSlot *slot, void ModulatorState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) { - for(size_t base{0u};base < samplesToDo;) - { - alignas(16) float modsamples[MAX_UPDATE_SAMPLES]; - const size_t td{minz(MAX_UPDATE_SAMPLES, samplesToDo-base)}; - - mGetSamples(modsamples, mIndex, mStep, td); - mIndex += static_cast<uint>(mStep * td); - mIndex &= WAVEFORM_FRACMASK; + (this->*mGenModSamples)(samplesToDo); - auto chandata = std::begin(mChans); - for(const auto &input : samplesIn) + auto chandata = std::begin(mChans); + for(const auto &input : samplesIn) + { + const size_t outidx{chandata->mTargetChannel}; + if(outidx != InvalidChannelIndex) { - const size_t outidx{chandata->mTargetChannel}; - if(outidx != InvalidChannelIndex) - { - alignas(16) float temps[MAX_UPDATE_SAMPLES]; - - chandata->mFilter.process({&input[base], td}, temps); - for(size_t i{0u};i < td;i++) - temps[i] *= modsamples[i]; - - MixSamples({temps, td}, samplesOut[outidx].data()+base, chandata->mCurrentGain, - chandata->mTargetGain, samplesToDo-base); - } - ++chandata; - } + chandata->mFilter.process({input.data(), samplesToDo}, mBuffer.data()); + for(size_t i{0u};i < samplesToDo;++i) + mBuffer[i] *= mModSamples[i]; - base += td; + MixSamples({mBuffer.data(), samplesToDo}, samplesOut[outidx].data(), + chandata->mCurrentGain, chandata->mTargetGain, minz(samplesToDo, 64)); + } + ++chandata; } } diff --git a/alc/effects/pshifter.cpp b/alc/effects/pshifter.cpp index 426a2264..0c27be30 100644 --- a/alc/effects/pshifter.cpp +++ b/alc/effects/pshifter.cpp @@ -28,7 +28,6 @@ #include <iterator> #include "alc/effects/base.h" -#include "alcomplex.h" #include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" @@ -40,6 +39,7 @@ #include "core/mixer.h" #include "core/mixer/defs.h" #include "intrusive_ptr.h" +#include "pffft.h" struct ContextBase; @@ -74,6 +74,12 @@ struct Windower { const Windower gWindow{}; +struct PFFFTSetupDeleter { + void operator()(PFFFT_Setup *ptr) { pffft_destroy_setup(ptr); } +}; +using PFFFTSetupPtr = std::unique_ptr<PFFFT_Setup,PFFFTSetupDeleter>; + + struct FrequencyBin { float Magnitude; float FreqBin; @@ -93,7 +99,9 @@ struct PshifterState final : public EffectState { std::array<float,StftHalfSize+1> mSumPhase; std::array<float,StftSize> mOutputAccum; - std::array<complex_f,StftSize> mFftBuffer; + PFFFTSetupPtr mFft; + alignas(16) std::array<float,StftSize> mFftBuffer; + alignas(16) std::array<float,StftSize> mFftWorkBuffer; std::array<FrequencyBin,StftHalfSize+1> mAnalysisBuffer; std::array<FrequencyBin,StftHalfSize+1> mSynthesisBuffer; @@ -126,12 +134,15 @@ void PshifterState::deviceUpdate(const DeviceBase*, const BufferStorage*) mLastPhase.fill(0.0f); mSumPhase.fill(0.0f); mOutputAccum.fill(0.0f); - mFftBuffer.fill(complex_f{}); + mFftBuffer.fill(0.0f); mAnalysisBuffer.fill(FrequencyBin{}); mSynthesisBuffer.fill(FrequencyBin{}); std::fill(std::begin(mCurrentGains), std::end(mCurrentGains), 0.0f); std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f); + + if(!mFft) + mFft = PFFFTSetupPtr{pffft_new_setup(StftSize, PFFFT_REAL)}; } void PshifterState::update(const ContextBase*, const EffectSlot *slot, @@ -142,10 +153,10 @@ void PshifterState::update(const ContextBase*, const EffectSlot *slot, mPitchShiftI = clampu(fastf2u(pitch*MixerFracOne), MixerFracHalf, MixerFracOne*2); mPitchShift = static_cast<float>(mPitchShiftI) * float{1.0f/MixerFracOne}; - static constexpr auto coeffs = CalcDirectionCoeffs({0.0f, 0.0f, -1.0f}); + static constexpr auto coeffs = CalcDirectionCoeffs(std::array{0.0f, 0.0f, -1.0f}); mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, coeffs.data(), slot->Gain, mTargetGains); + ComputePanGains(target.Main, coeffs, slot->Gain, mTargetGains); } void PshifterState::process(const size_t samplesToDo, @@ -186,15 +197,19 @@ void PshifterState::process(const size_t samplesToDo, mFftBuffer[k] = mFIFO[src] * gWindow.mData[k]; for(size_t src{0u}, k{StftSize-mPos};src < mPos;++src,++k) mFftBuffer[k] = mFIFO[src] * gWindow.mData[k]; - forward_fft(al::as_span(mFftBuffer)); + pffft_transform_ordered(mFft.get(), mFftBuffer.data(), mFftBuffer.data(), + mFftWorkBuffer.data(), PFFFT_FORWARD); /* Analyze the obtained data. Since the real FFT is symmetric, only * StftHalfSize+1 samples are needed. */ - for(size_t k{0u};k < StftHalfSize+1;k++) + for(size_t k{0u};k < StftHalfSize+1;++k) { - const float magnitude{std::abs(mFftBuffer[k])}; - const float phase{std::arg(mFftBuffer[k])}; + const auto cplx = (k == 0) ? complex_f{mFftBuffer[0]} : + (k == StftHalfSize) ? complex_f{mFftBuffer[1]} : + complex_f{mFftBuffer[k*2], mFftBuffer[k*2 + 1]}; + const float magnitude{std::abs(cplx)}; + const float phase{std::arg(cplx)}; /* Compute the phase difference from the last update and subtract * the expected phase difference for this bin. @@ -266,21 +281,29 @@ void PshifterState::process(const size_t samplesToDo, tmp -= static_cast<float>(qpd + (qpd%2)); mSumPhase[k] = tmp * al::numbers::pi_v<float>; - mFftBuffer[k] = std::polar(mSynthesisBuffer[k].Magnitude, mSumPhase[k]); + const complex_f cplx{std::polar(mSynthesisBuffer[k].Magnitude, mSumPhase[k])}; + if(k == 0) + mFftBuffer[0] = cplx.real(); + else if(k == StftHalfSize) + mFftBuffer[1] = cplx.real(); + else + { + mFftBuffer[k*2 + 0] = cplx.real(); + mFftBuffer[k*2 + 1] = cplx.imag(); + } } - for(size_t k{StftHalfSize+1};k < StftSize;++k) - mFftBuffer[k] = std::conj(mFftBuffer[StftSize-k]); /* Apply an inverse FFT to get the time-domain signal, and accumulate * for the output with windowing. */ - inverse_fft(al::as_span(mFftBuffer)); + pffft_transform_ordered(mFft.get(), mFftBuffer.data(), mFftBuffer.data(), + mFftWorkBuffer.data(), PFFFT_BACKWARD); static constexpr float scale{3.0f / OversampleFactor / StftSize}; for(size_t dst{mPos}, k{0u};dst < StftSize;++dst,++k) - mOutputAccum[dst] += gWindow.mData[k]*mFftBuffer[k].real() * scale; + mOutputAccum[dst] += gWindow.mData[k]*mFftBuffer[k] * scale; for(size_t dst{0u}, k{StftSize-mPos};dst < mPos;++dst,++k) - mOutputAccum[dst] += gWindow.mData[k]*mFftBuffer[k].real() * scale; + mOutputAccum[dst] += gWindow.mData[k]*mFftBuffer[k] * scale; /* Copy out the accumulated result, then clear for the next iteration. */ std::copy_n(mOutputAccum.begin() + mPos, StftStep, mFIFO.begin() + mPos); diff --git a/alc/effects/reverb.cpp b/alc/effects/reverb.cpp index 3875bedb..0f1fcca1 100644 --- a/alc/effects/reverb.cpp +++ b/alc/effects/reverb.cpp @@ -98,8 +98,6 @@ struct CubicFilter { constexpr CubicFilter gCubicTable; -using namespace std::placeholders; - /* Max samples per process iteration. Used to limit the size needed for * temporary buffers. Must be a multiple of 4 for SIMD alignment. */ @@ -122,12 +120,9 @@ constexpr size_t NUM_LINES{4u}; constexpr float MODULATION_DEPTH_COEFF{0.05f}; -/* 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. +/* The B-Format to (W-normalized) A-Format conversion matrix. This produces a + * tetrahedral array of discrete signals (boosted by a factor of sqrt(3), to + * reduce the error introduced in the conversion). */ alignas(16) constexpr float B2A[NUM_LINES][NUM_LINES]{ { 0.5f, 0.5f, 0.5f, 0.5f }, @@ -136,7 +131,9 @@ alignas(16) constexpr float B2A[NUM_LINES][NUM_LINES]{ { 0.5f, -0.5f, 0.5f, -0.5f } }; -/* Converts A-Format to B-Format for early reflections. */ +/* Converts (W-normalized) A-Format to B-Format for early reflections (scaled + * by 1/sqrt(3) to compensate for the boost in the B2A matrix). + */ alignas(16) constexpr std::array<std::array<float,NUM_LINES>,NUM_LINES> EarlyA2B{{ {{ 0.5f, 0.5f, 0.5f, 0.5f }}, {{ 0.5f, -0.5f, 0.5f, -0.5f }}, @@ -144,7 +141,11 @@ alignas(16) constexpr std::array<std::array<float,NUM_LINES>,NUM_LINES> EarlyA2B {{ 0.5f, 0.5f, -0.5f, -0.5f }} }}; -/* Converts A-Format to B-Format for late reverb. */ +/* Converts (W-normalized) A-Format to B-Format for late reverb (scaled + * by 1/sqrt(3) to compensate for the boost in the B2A matrix). The response + * is rotated around Z (ambisonic X) so that the front lines are placed + * horizontally in front, and the rear lines are placed vertically in back. + */ constexpr auto InvSqrt2 = static_cast<float>(1.0/al::numbers::sqrt2); alignas(16) constexpr std::array<std::array<float,NUM_LINES>,NUM_LINES> LateA2B{{ {{ 0.5f, 0.5f, 0.5f, 0.5f }}, @@ -330,6 +331,39 @@ struct DelayLineI { } while(--td); } } + + /* Writes the given input lines to the delay buffer, applying a geometric + * reflection. This effectively applies the matrix + * + * [ -1/2 +1/2 +1/2 +1/2 ] + * [ +1/2 -1/2 +1/2 +1/2 ] + * [ +1/2 +1/2 -1/2 +1/2 ] + * [ +1/2 +1/2 +1/2 -1/2 ] + * + * to the four input lines when writing to the delay buffer. The effect on + * the B-Format signal is negating X,Y,Z, moving each response to its + * spatially opposite location. + */ + void writeReflected(size_t offset, const al::span<const ReverbUpdateLine,NUM_LINES> in, + const size_t count) const noexcept + { + ASSUME(count > 0); + for(size_t i{0u};i < count;) + { + offset &= Mask; + size_t td{minz(Mask+1 - offset, count - i)}; + do { + const std::array src{in[0][i], in[1][i], in[2][i], in[3][i]}; + ++i; + + Line[offset][0] = ( src[1] + src[2] + src[3] - src[0]) * 0.5f; + Line[offset][1] = (src[0] + src[2] + src[3] - src[1]) * 0.5f; + Line[offset][2] = (src[0] + src[1] + src[3] - src[2]) * 0.5f; + Line[offset][3] = (src[0] + src[1] + src[2] - src[3]) * 0.5f; + ++offset; + } while(--td); + } + } }; struct VecAllpass { @@ -461,8 +495,9 @@ struct ReverbPipeline { void updateDelayLine(const float earlyDelay, const float lateDelay, const float density_mult, const float decayTime, const float frequency); - void update3DPanning(const float *ReflectionsPan, const float *LateReverbPan, - const float earlyGain, const float lateGain, const bool doUpmix, const MixParams *mainMix); + void update3DPanning(const al::span<const float,3> ReflectionsPan, + const al::span<const float,3> LateReverbPan, const float earlyGain, const float lateGain, + const bool doUpmix, const MixParams *mainMix); void processEarly(size_t offset, const size_t samplesToDo, const al::span<ReverbUpdateLine,NUM_LINES> tempSamples, @@ -643,8 +678,8 @@ inline float CalcDelayLengthMult(float density) */ void ReverbState::allocLines(const float frequency) { - /* All delay line lengths are calculated to accomodate the full range of - * lengths given their respective paramters. + /* All delay line lengths are calculated to accommodate the full range of + * lengths given their respective parameters. */ size_t totalSamples{0u}; @@ -1017,8 +1052,12 @@ void ReverbPipeline::updateDelayLine(const float earlyDelay, const float lateDel mEarlyDelayTap[i][1] = float2uint((earlyDelay+length) * frequency); mEarlyDelayCoeff[i] = CalcDecayCoeff(length, decayTime); + /* Reduce the late delay tap by the shortest early delay line length to + * compensate for the late line input being fed by the delayed early + * output. + */ length = (LATE_LINE_LENGTHS[i] - LATE_LINE_LENGTHS.front())/float{NUM_LINES}*density_mult + - lateDelay; + std::max(lateDelay - EARLY_LINE_LENGTHS[0]*density_mult, 0.0f); mLateDelayTap[i][1] = float2uint(length * frequency); } } @@ -1028,7 +1067,7 @@ void ReverbPipeline::updateDelayLine(const float earlyDelay, const float lateDel * focal strength. This function results in a B-Format transformation matrix * that spatially focuses the signal in the desired direction. */ -std::array<std::array<float,4>,4> GetTransformFromVector(const float *vec) +std::array<std::array<float,4>,4> GetTransformFromVector(const al::span<const float,3> vec) { /* Normalize the panning vector according to the N3D scale, which has an * extra sqrt(3) term on the directional components. Converting from OpenAL @@ -1041,9 +1080,10 @@ std::array<std::array<float,4>,4> GetTransformFromVector(const float *vec) float mag{std::sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2])}; if(mag > 1.0f) { - norm[0] = vec[0] / mag * -al::numbers::sqrt3_v<float>; - norm[1] = vec[1] / mag * al::numbers::sqrt3_v<float>; - norm[2] = vec[2] / mag * al::numbers::sqrt3_v<float>; + const float scale{al::numbers::sqrt3_v<float> / mag}; + norm[0] = vec[0] * -scale; + norm[1] = vec[1] * scale; + norm[2] = vec[2] * scale; mag = 1.0f; } else @@ -1066,8 +1106,9 @@ std::array<std::array<float,4>,4> GetTransformFromVector(const float *vec) } /* Update the early and late 3D panning gains. */ -void ReverbPipeline::update3DPanning(const float *ReflectionsPan, const float *LateReverbPan, - const float earlyGain, const float lateGain, const bool doUpmix, const MixParams *mainMix) +void ReverbPipeline::update3DPanning(const al::span<const float,3> ReflectionsPan, + const al::span<const float,3> LateReverbPan, const float earlyGain, const float lateGain, + const bool doUpmix, const MixParams *mainMix) { /* Create matrices that transform a B-Format signal according to the * panning vectors. @@ -1105,9 +1146,9 @@ void ReverbPipeline::update3DPanning(const float *ReflectionsPan, const float *L auto latecoeffs = mult_matrix(latemat); for(size_t i{0u};i < NUM_LINES;i++) - ComputePanGains(mainMix, earlycoeffs[i].data(), earlyGain, mEarly.TargetGains[i]); + ComputePanGains(mainMix, earlycoeffs[i], earlyGain, mEarly.TargetGains[i]); for(size_t i{0u};i < NUM_LINES;i++) - ComputePanGains(mainMix, latecoeffs[i].data(), lateGain, mLate.TargetGains[i]); + ComputePanGains(mainMix, latecoeffs[i], lateGain, mLate.TargetGains[i]); } else { @@ -1137,9 +1178,9 @@ void ReverbPipeline::update3DPanning(const float *ReflectionsPan, const float *L auto latecoeffs = mult_matrix(LateA2B, latemat); for(size_t i{0u};i < NUM_LINES;i++) - ComputePanGains(mainMix, earlycoeffs[i].data(), earlyGain, mEarly.TargetGains[i]); + ComputePanGains(mainMix, earlycoeffs[i], earlyGain, mEarly.TargetGains[i]); for(size_t i{0u};i < NUM_LINES;i++) - ComputePanGains(mainMix, latecoeffs[i].data(), lateGain, mLate.TargetGains[i]); + ComputePanGains(mainMix, latecoeffs[i], lateGain, mLate.TargetGains[i]); } } @@ -1244,9 +1285,36 @@ void ReverbState::update(const ContextBase *Context, const EffectSlot *Slot, props->Reverb.DecayTime, hfDecayTime, lf0norm, hf0norm, frequency); } - const float decaySamples{(props->Reverb.ReflectionsDelay + props->Reverb.LateReverbDelay - + props->Reverb.DecayTime) * frequency}; - pipeline.mFadeSampleCount = static_cast<size_t>(minf(decaySamples, 1'000'000.0f)); + /* Calculate the gain at the start of the late reverb stage, and the gain + * difference from the decay target (0.001, or -60dB). + */ + const float decayBase{props->Reverb.ReflectionsGain * props->Reverb.LateReverbGain}; + const float decayDiff{ReverbDecayGain / decayBase}; + + if(decayDiff < 1.0f) + { + /* Given the DecayTime (the amount of time for the late reverb to decay + * by -60dB), calculate the time to decay to -60dB from the start of + * the late reverb. + */ + const float diffTime{std::log10(decayDiff)*(20.0f / -60.0f) * props->Reverb.DecayTime}; + + const float decaySamples{(props->Reverb.ReflectionsDelay + props->Reverb.LateReverbDelay + + diffTime) * frequency}; + /* Limit to 100,000 samples (a touch over 2 seconds at 48khz) to + * avoid excessive double-processing. + */ + pipeline.mFadeSampleCount = static_cast<size_t>(minf(decaySamples, 100'000.0f)); + } + else + { + /* Otherwise, if the late reverb already starts at -60dB or less, only + * include the time to get to the late reverb. + */ + const float decaySamples{(props->Reverb.ReflectionsDelay + props->Reverb.LateReverbDelay) + * frequency}; + pipeline.mFadeSampleCount = static_cast<size_t>(minf(decaySamples, 100'000.0f)); + } } @@ -1303,7 +1371,9 @@ inline auto VectorPartialScatter(const std::array<float,NUM_LINES> &RESTRICT in, }}; } -/* Utilizes the above, but reverses the input channels. */ +/* Utilizes the above, but also applies a geometric reflection on the input + * channels. + */ void VectorScatterRevDelayIn(const DelayLineI delay, size_t offset, const float xCoeff, const float yCoeff, const al::span<const ReverbUpdateLine,NUM_LINES> in, const size_t count) { @@ -1314,9 +1384,13 @@ void VectorScatterRevDelayIn(const DelayLineI delay, size_t offset, const float offset &= delay.Mask; size_t td{minz(delay.Mask+1 - offset, count-i)}; do { - std::array<float,NUM_LINES> f; - for(size_t j{0u};j < NUM_LINES;j++) - f[NUM_LINES-1-j] = in[j][i]; + std::array src{in[0][i], in[1][i], in[2][i], in[3][i]}; + std::array f{ + ( src[1] + src[2] + src[3] - src[0]) * 0.5f, + (src[0] + src[2] + src[3] - src[1]) * 0.5f, + (src[0] + src[1] + src[3] - src[2]) * 0.5f, + (src[0] + src[1] + src[2] - src[3]) * 0.5f + }; ++i; delay.Line[offset++] = VectorPartialScatter(f, xCoeff, yCoeff); @@ -1330,9 +1404,6 @@ void VectorScatterRevDelayIn(const DelayLineI delay, size_t offset, const float * It works by vectorizing a regular all-pass filter and replacing the delay * element with a scattering matrix (like the one above) and a diagonal * matrix of delay elements. - * - * Two static specializations are used for transitional (cross-faded) delay - * line processing and non-transitional processing. */ void VecAllpass::process(const al::span<ReverbUpdateLine,NUM_LINES> samples, size_t offset, const float xCoeff, const float yCoeff, const size_t todo) @@ -1379,14 +1450,13 @@ void VecAllpass::process(const al::span<ReverbUpdateLine,NUM_LINES> samples, siz * 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 + * The early lines are then reflected about the origin 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), + * Finally, the early response is reflected, scattered (based on diffusion), * and fed into the late reverb section of the main delay line. */ void ReverbPipeline::processEarly(size_t offset, const size_t samplesToDo, @@ -1442,8 +1512,7 @@ void ReverbPipeline::processEarly(size_t offset, const size_t samplesToDo, /* Apply a delay and bounce to generate secondary reflections, combine * with the primary reflections and write out the result for mixing. */ - for(size_t j{0u};j < NUM_LINES;j++) - early_delay.write(offset, NUM_LINES-1-j, tempSamples[j].data(), todo); + early_delay.writeReflected(offset, tempSamples, todo); for(size_t j{0u};j < NUM_LINES;j++) { size_t feedb_tap{offset - mEarly.Offset[j]}; @@ -1455,8 +1524,9 @@ void ReverbPipeline::processEarly(size_t offset, const size_t samplesToDo, feedb_tap &= early_delay.Mask; size_t td{minz(early_delay.Mask+1 - feedb_tap, todo - i)}; do { - tempSamples[j][i] += early_delay.Line[feedb_tap++][j]*feedb_coeff; - out[i] = tempSamples[j][i]; + float sample{early_delay.Line[feedb_tap++][j]}; + out[i] = tempSamples[j][i] + sample*feedb_coeff; + tempSamples[j][i] = sample; ++i; } while(--td); } @@ -1475,14 +1545,19 @@ void ReverbPipeline::processEarly(size_t offset, const size_t samplesToDo, void Modulation::calcDelays(size_t todo) { - constexpr float mod_scale{al::numbers::pi_v<float> * 2.0f / MOD_FRACONE}; uint idx{Index}; const uint step{Step}; const float depth{Depth}; for(size_t i{0};i < todo;++i) { idx += step; - const float lfo{std::sin(static_cast<float>(idx&MOD_FRACMASK) * mod_scale)}; + const float x{static_cast<float>(idx&MOD_FRACMASK) * (1.0f/MOD_FRACONE)}; + /* Approximate sin(x*2pi). As long as it roughly fits a sinusoid shape + * and stays within [-1...+1], it needn't be perfect. + */ + const float lfo{!(idx&(MOD_FRACONE>>1)) + ? ((-16.0f * x * x) + (8.0f * x)) + : ((16.0f * x * x) + (-8.0f * x) + (-16.0f * x) + 8.0f)}; ModDelays[i] = (lfo+1.0f) * depth; } Index = idx; diff --git a/alc/events.cpp b/alc/events.cpp new file mode 100644 index 00000000..1010a338 --- /dev/null +++ b/alc/events.cpp @@ -0,0 +1,95 @@ + +#include "config.h" + +#include "events.h" + +#include "alspan.h" +#include "core/logging.h" +#include "device.h" + + +namespace { + +ALCenum EnumFromEventType(const alc::EventType type) +{ + switch(type) + { + case alc::EventType::DefaultDeviceChanged: return ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT; + case alc::EventType::DeviceAdded: return ALC_EVENT_TYPE_DEVICE_ADDED_SOFT; + case alc::EventType::DeviceRemoved: return ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT; + case alc::EventType::Count: break; + } + throw std::runtime_error{"Invalid EventType: "+std::to_string(al::to_underlying(type))}; +} + +} // namespace + +namespace alc { + +std::optional<alc::EventType> GetEventType(ALCenum type) +{ + switch(type) + { + case ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT: return alc::EventType::DefaultDeviceChanged; + case ALC_EVENT_TYPE_DEVICE_ADDED_SOFT: return alc::EventType::DeviceAdded; + case ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT: return alc::EventType::DeviceRemoved; + } + return std::nullopt; +} + +void Event(EventType eventType, DeviceType deviceType, ALCdevice *device, std::string_view message) noexcept +{ + auto eventlock = std::unique_lock{EventMutex}; + if(EventCallback && EventsEnabled.test(al::to_underlying(eventType))) + EventCallback(EnumFromEventType(eventType), al::to_underlying(deviceType), device, + static_cast<ALCsizei>(message.length()), message.data(), EventUserPtr); +} + +} // namespace alc + +FORCE_ALIGN ALCboolean ALC_APIENTRY alcEventControlSOFT(ALCsizei count, const ALCenum *events, + ALCboolean enable) noexcept +{ + if(enable != ALC_FALSE && enable != ALC_TRUE) + { + alcSetError(nullptr, ALC_INVALID_ENUM); + return ALC_FALSE; + } + if(count < 0) + { + alcSetError(nullptr, ALC_INVALID_VALUE); + return ALC_FALSE; + } + if(count == 0) + return ALC_TRUE; + if(!events) + { + alcSetError(nullptr, ALC_INVALID_VALUE); + return ALC_FALSE; + } + + alc::EventBitSet eventSet{0}; + for(ALCenum type : al::span{events, static_cast<ALCuint>(count)}) + { + auto etype = alc::GetEventType(type); + if(!etype) + { + WARN("Invalid event type: 0x%04x\n", type); + alcSetError(nullptr, ALC_INVALID_ENUM); + return ALC_FALSE; + } + eventSet.set(al::to_underlying(*etype)); + } + + auto eventlock = std::unique_lock{alc::EventMutex}; + if(enable) alc::EventsEnabled |= eventSet; + else alc::EventsEnabled &= ~eventSet; + return ALC_TRUE; +} + +FORCE_ALIGN void ALC_APIENTRY alcEventCallbackSOFT(ALCEVENTPROCTYPESOFT callback, void *userParam) noexcept +{ + auto eventlock = std::unique_lock{alc::EventMutex}; + alc::EventCallback = callback; + alc::EventUserPtr = userParam; +} diff --git a/alc/events.h b/alc/events.h new file mode 100644 index 00000000..3f53ec76 --- /dev/null +++ b/alc/events.h @@ -0,0 +1,50 @@ +#ifndef ALC_EVENTS_H +#define ALC_EVENTS_H + +#include "inprogext.h" +#include "opthelpers.h" + +#include <bitset> +#include <mutex> +#include <optional> +#include <string_view> + + +namespace alc { + +enum class EventType : uint8_t { + DefaultDeviceChanged, + DeviceAdded, + DeviceRemoved, + + Count +}; + +std::optional<alc::EventType> GetEventType(ALCenum type); + +enum class EventSupport : ALCenum { + FullSupport = ALC_EVENT_SUPPORTED_SOFT, + NoSupport = ALC_EVENT_NOT_SUPPORTED_SOFT, +}; + +enum class DeviceType : ALCenum { + Playback = ALC_PLAYBACK_DEVICE_SOFT, + Capture = ALC_CAPTURE_DEVICE_SOFT, +}; + +using EventBitSet = std::bitset<al::to_underlying(EventType::Count)>; +inline EventBitSet EventsEnabled{0}; + +inline std::mutex EventMutex; + +inline ALCEVENTPROCTYPESOFT EventCallback{}; +inline void *EventUserPtr{}; + +void Event(EventType eventType, DeviceType deviceType, ALCdevice *device, std::string_view message) noexcept; + +inline void Event(EventType eventType, DeviceType deviceType, std::string_view message) noexcept +{ Event(eventType, deviceType, nullptr, message); } + +} // namespace alc + +#endif /* ALC_EVENTS_H */ diff --git a/alc/export_list.h b/alc/export_list.h new file mode 100644 index 00000000..c5af1ab0 --- /dev/null +++ b/alc/export_list.h @@ -0,0 +1,915 @@ +#ifndef ALC_EXPORT_LIST_H +#define ALC_EXPORT_LIST_H + +#include "AL/alc.h" +#include "AL/al.h" +#include "AL/alext.h" + +#include "inprogext.h" +#ifdef ALSOFT_EAX +#include "context.h" +#include "al/eax/x_ram.h" +#endif + + +struct FuncExport { + const char *funcName; + void *address; +}; +#define DECL(x) { #x, reinterpret_cast<void*>(x) } +inline const FuncExport alcFunctions[]{ + DECL(alcCreateContext), + DECL(alcMakeContextCurrent), + DECL(alcProcessContext), + DECL(alcSuspendContext), + DECL(alcDestroyContext), + DECL(alcGetCurrentContext), + DECL(alcGetContextsDevice), + DECL(alcOpenDevice), + DECL(alcCloseDevice), + DECL(alcGetError), + DECL(alcIsExtensionPresent), + DECL(alcGetProcAddress), + DECL(alcGetEnumValue), + DECL(alcGetString), + DECL(alcGetIntegerv), + DECL(alcCaptureOpenDevice), + DECL(alcCaptureCloseDevice), + DECL(alcCaptureStart), + DECL(alcCaptureStop), + DECL(alcCaptureSamples), + + DECL(alcSetThreadContext), + DECL(alcGetThreadContext), + + DECL(alcLoopbackOpenDeviceSOFT), + DECL(alcIsRenderFormatSupportedSOFT), + DECL(alcRenderSamplesSOFT), + + DECL(alcDevicePauseSOFT), + DECL(alcDeviceResumeSOFT), + + DECL(alcGetStringiSOFT), + DECL(alcResetDeviceSOFT), + + DECL(alcGetInteger64vSOFT), + + DECL(alcReopenDeviceSOFT), + + DECL(alcEventIsSupportedSOFT), + DECL(alcEventControlSOFT), + DECL(alcEventCallbackSOFT), + + DECL(alEnable), + DECL(alDisable), + DECL(alIsEnabled), + DECL(alGetString), + DECL(alGetBooleanv), + DECL(alGetIntegerv), + DECL(alGetFloatv), + DECL(alGetDoublev), + DECL(alGetBoolean), + DECL(alGetInteger), + DECL(alGetFloat), + DECL(alGetDouble), + DECL(alGetError), + DECL(alIsExtensionPresent), + DECL(alGetProcAddress), + DECL(alGetEnumValue), + DECL(alListenerf), + DECL(alListener3f), + DECL(alListenerfv), + DECL(alListeneri), + DECL(alListener3i), + DECL(alListeneriv), + DECL(alGetListenerf), + DECL(alGetListener3f), + DECL(alGetListenerfv), + DECL(alGetListeneri), + DECL(alGetListener3i), + DECL(alGetListeneriv), + DECL(alGenSources), + DECL(alDeleteSources), + DECL(alIsSource), + DECL(alSourcef), + DECL(alSource3f), + DECL(alSourcefv), + DECL(alSourcei), + DECL(alSource3i), + DECL(alSourceiv), + DECL(alGetSourcef), + DECL(alGetSource3f), + DECL(alGetSourcefv), + DECL(alGetSourcei), + DECL(alGetSource3i), + DECL(alGetSourceiv), + DECL(alSourcePlayv), + DECL(alSourceStopv), + DECL(alSourceRewindv), + DECL(alSourcePausev), + DECL(alSourcePlay), + DECL(alSourceStop), + DECL(alSourceRewind), + DECL(alSourcePause), + DECL(alSourceQueueBuffers), + DECL(alSourceUnqueueBuffers), + DECL(alGenBuffers), + DECL(alDeleteBuffers), + DECL(alIsBuffer), + DECL(alBufferData), + DECL(alBufferf), + DECL(alBuffer3f), + DECL(alBufferfv), + DECL(alBufferi), + DECL(alBuffer3i), + DECL(alBufferiv), + DECL(alGetBufferf), + DECL(alGetBuffer3f), + DECL(alGetBufferfv), + DECL(alGetBufferi), + DECL(alGetBuffer3i), + DECL(alGetBufferiv), + DECL(alDopplerFactor), + DECL(alDopplerVelocity), + DECL(alSpeedOfSound), + DECL(alDistanceModel), + + DECL(alGenFilters), + DECL(alDeleteFilters), + DECL(alIsFilter), + DECL(alFilteri), + DECL(alFilteriv), + DECL(alFilterf), + DECL(alFilterfv), + DECL(alGetFilteri), + DECL(alGetFilteriv), + DECL(alGetFilterf), + DECL(alGetFilterfv), + DECL(alGenEffects), + DECL(alDeleteEffects), + DECL(alIsEffect), + DECL(alEffecti), + DECL(alEffectiv), + DECL(alEffectf), + DECL(alEffectfv), + DECL(alGetEffecti), + DECL(alGetEffectiv), + DECL(alGetEffectf), + DECL(alGetEffectfv), + DECL(alGenAuxiliaryEffectSlots), + DECL(alDeleteAuxiliaryEffectSlots), + DECL(alIsAuxiliaryEffectSlot), + DECL(alAuxiliaryEffectSloti), + DECL(alAuxiliaryEffectSlotiv), + DECL(alAuxiliaryEffectSlotf), + DECL(alAuxiliaryEffectSlotfv), + DECL(alGetAuxiliaryEffectSloti), + DECL(alGetAuxiliaryEffectSlotiv), + DECL(alGetAuxiliaryEffectSlotf), + DECL(alGetAuxiliaryEffectSlotfv), + + DECL(alDeferUpdatesSOFT), + DECL(alProcessUpdatesSOFT), + + DECL(alSourcedSOFT), + DECL(alSource3dSOFT), + DECL(alSourcedvSOFT), + DECL(alGetSourcedSOFT), + DECL(alGetSource3dSOFT), + DECL(alGetSourcedvSOFT), + DECL(alSourcei64SOFT), + DECL(alSource3i64SOFT), + DECL(alSourcei64vSOFT), + DECL(alGetSourcei64SOFT), + DECL(alGetSource3i64SOFT), + DECL(alGetSourcei64vSOFT), + + DECL(alGetStringiSOFT), + + DECL(alBufferStorageSOFT), + DECL(alMapBufferSOFT), + DECL(alUnmapBufferSOFT), + DECL(alFlushMappedBufferSOFT), + + DECL(alEventControlSOFT), + DECL(alEventCallbackSOFT), + DECL(alGetPointerSOFT), + DECL(alGetPointervSOFT), + + DECL(alBufferCallbackSOFT), + DECL(alGetBufferPtrSOFT), + DECL(alGetBuffer3PtrSOFT), + DECL(alGetBufferPtrvSOFT), + + DECL(alAuxiliaryEffectSlotPlaySOFT), + DECL(alAuxiliaryEffectSlotPlayvSOFT), + DECL(alAuxiliaryEffectSlotStopSOFT), + DECL(alAuxiliaryEffectSlotStopvSOFT), + + DECL(alSourcePlayAtTimeSOFT), + DECL(alSourcePlayAtTimevSOFT), + + DECL(alBufferSubDataSOFT), + + DECL(alBufferDataStatic), + + DECL(alDebugMessageCallbackEXT), + DECL(alDebugMessageInsertEXT), + DECL(alDebugMessageControlEXT), + DECL(alPushDebugGroupEXT), + DECL(alPopDebugGroupEXT), + DECL(alGetDebugMessageLogEXT), + + /* Direct Context functions */ + DECL(alcGetProcAddress2), + DECL(alEnableDirect), + DECL(alDisableDirect), + DECL(alIsEnabledDirect), + DECL(alDopplerFactorDirect), + DECL(alSpeedOfSoundDirect), + DECL(alDistanceModelDirect), + DECL(alGetStringDirect), + DECL(alGetBooleanvDirect), + DECL(alGetIntegervDirect), + DECL(alGetFloatvDirect), + DECL(alGetDoublevDirect), + DECL(alGetBooleanDirect), + DECL(alGetIntegerDirect), + DECL(alGetFloatDirect), + DECL(alGetDoubleDirect), + + DECL(alGetErrorDirect), + DECL(alIsExtensionPresentDirect), + DECL(alGetProcAddress), + DECL(alGetEnumValueDirect), + + DECL(alListeneriDirect), + DECL(alListener3iDirect), + DECL(alListenerivDirect), + DECL(alListenerfDirect), + DECL(alListener3fDirect), + DECL(alListenerfvDirect), + DECL(alGetListeneriDirect), + DECL(alGetListener3iDirect), + DECL(alGetListenerivDirect), + DECL(alGetListenerfDirect), + DECL(alGetListener3fDirect), + DECL(alGetListenerfvDirect), + + DECL(alGenBuffersDirect), + DECL(alDeleteBuffersDirect), + DECL(alIsBufferDirect), + DECL(alBufferDataDirect), + DECL(alBufferiDirect), + DECL(alBuffer3iDirect), + DECL(alBufferivDirect), + DECL(alBufferfDirect), + DECL(alBuffer3fDirect), + DECL(alBufferfvDirect), + DECL(alGetBufferiDirect), + DECL(alGetBuffer3iDirect), + DECL(alGetBufferivDirect), + DECL(alGetBufferfDirect), + DECL(alGetBuffer3fDirect), + DECL(alGetBufferfvDirect), + + DECL(alGenSourcesDirect), + DECL(alDeleteSourcesDirect), + DECL(alIsSourceDirect), + DECL(alSourcePlayDirect), + DECL(alSourceStopDirect), + DECL(alSourcePauseDirect), + DECL(alSourceRewindDirect), + DECL(alSourcePlayvDirect), + DECL(alSourceStopvDirect), + DECL(alSourcePausevDirect), + DECL(alSourceRewindvDirect), + DECL(alSourceiDirect), + DECL(alSource3iDirect), + DECL(alSourceivDirect), + DECL(alSourcefDirect), + DECL(alSource3fDirect), + DECL(alSourcefvDirect), + DECL(alGetSourceiDirect), + DECL(alGetSource3iDirect), + DECL(alGetSourceivDirect), + DECL(alGetSourcefDirect), + DECL(alGetSource3fDirect), + DECL(alGetSourcefvDirect), + DECL(alSourceQueueBuffersDirect), + DECL(alSourceUnqueueBuffersDirect), + + DECL(alGenFiltersDirect), + DECL(alDeleteFiltersDirect), + DECL(alIsFilterDirect), + DECL(alFilteriDirect), + DECL(alFilterivDirect), + DECL(alFilterfDirect), + DECL(alFilterfvDirect), + DECL(alGetFilteriDirect), + DECL(alGetFilterivDirect), + DECL(alGetFilterfDirect), + DECL(alGetFilterfvDirect), + DECL(alGenEffectsDirect), + DECL(alDeleteEffectsDirect), + DECL(alIsEffectDirect), + DECL(alEffectiDirect), + DECL(alEffectivDirect), + DECL(alEffectfDirect), + DECL(alEffectfvDirect), + DECL(alGetEffectiDirect), + DECL(alGetEffectivDirect), + DECL(alGetEffectfDirect), + DECL(alGetEffectfvDirect), + DECL(alGenAuxiliaryEffectSlotsDirect), + DECL(alDeleteAuxiliaryEffectSlotsDirect), + DECL(alIsAuxiliaryEffectSlotDirect), + DECL(alAuxiliaryEffectSlotiDirect), + DECL(alAuxiliaryEffectSlotivDirect), + DECL(alAuxiliaryEffectSlotfDirect), + DECL(alAuxiliaryEffectSlotfvDirect), + DECL(alGetAuxiliaryEffectSlotiDirect), + DECL(alGetAuxiliaryEffectSlotivDirect), + DECL(alGetAuxiliaryEffectSlotfDirect), + DECL(alGetAuxiliaryEffectSlotfvDirect), + + DECL(alDeferUpdatesDirectSOFT), + DECL(alProcessUpdatesDirectSOFT), + DECL(alGetStringiDirectSOFT), + + DECL(alBufferDataStaticDirect), + DECL(alBufferCallbackDirectSOFT), + DECL(alBufferSubDataDirectSOFT), + DECL(alBufferStorageDirectSOFT), + DECL(alMapBufferDirectSOFT), + DECL(alUnmapBufferDirectSOFT), + DECL(alFlushMappedBufferDirectSOFT), + + DECL(alSourcei64DirectSOFT), + DECL(alSource3i64DirectSOFT), + DECL(alSourcei64vDirectSOFT), + DECL(alSourcedDirectSOFT), + DECL(alSource3dDirectSOFT), + DECL(alSourcedvDirectSOFT), + DECL(alGetSourcei64DirectSOFT), + DECL(alGetSource3i64DirectSOFT), + DECL(alGetSourcei64vDirectSOFT), + DECL(alGetSourcedDirectSOFT), + DECL(alGetSource3dDirectSOFT), + DECL(alGetSourcedvDirectSOFT), + DECL(alSourcePlayAtTimeDirectSOFT), + DECL(alSourcePlayAtTimevDirectSOFT), + + DECL(alEventControlDirectSOFT), + DECL(alEventCallbackDirectSOFT), + + DECL(alDebugMessageCallbackDirectEXT), + DECL(alDebugMessageInsertDirectEXT), + DECL(alDebugMessageControlDirectEXT), + DECL(alPushDebugGroupDirectEXT), + DECL(alPopDebugGroupDirectEXT), + DECL(alGetDebugMessageLogDirectEXT), + DECL(alObjectLabelEXT), + DECL(alObjectLabelDirectEXT), + DECL(alGetObjectLabelEXT), + DECL(alGetObjectLabelDirectEXT), + + /* Extra functions */ + DECL(alsoft_set_log_callback), +#ifdef ALSOFT_EAX +}, eaxFunctions[]{ + DECL(EAXGet), + DECL(EAXSet), + DECL(EAXGetBufferMode), + DECL(EAXSetBufferMode), + + DECL(EAXGetDirect), + DECL(EAXSetDirect), + DECL(EAXGetBufferModeDirect), + DECL(EAXSetBufferModeDirect), +#endif +}; +#undef DECL + +struct EnumExport { + const char *enumName; + int value; +}; +#define DECL(x) { #x, (x) } +inline const EnumExport alcEnumerations[]{ + DECL(ALC_INVALID), + DECL(ALC_FALSE), + DECL(ALC_TRUE), + + DECL(ALC_MAJOR_VERSION), + DECL(ALC_MINOR_VERSION), + DECL(ALC_ATTRIBUTES_SIZE), + DECL(ALC_ALL_ATTRIBUTES), + DECL(ALC_DEFAULT_DEVICE_SPECIFIER), + DECL(ALC_DEVICE_SPECIFIER), + DECL(ALC_ALL_DEVICES_SPECIFIER), + DECL(ALC_DEFAULT_ALL_DEVICES_SPECIFIER), + DECL(ALC_EXTENSIONS), + DECL(ALC_FREQUENCY), + DECL(ALC_REFRESH), + DECL(ALC_SYNC), + DECL(ALC_MONO_SOURCES), + DECL(ALC_STEREO_SOURCES), + DECL(ALC_CAPTURE_DEVICE_SPECIFIER), + DECL(ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER), + DECL(ALC_CAPTURE_SAMPLES), + DECL(ALC_CONNECTED), + + DECL(ALC_EFX_MAJOR_VERSION), + DECL(ALC_EFX_MINOR_VERSION), + DECL(ALC_MAX_AUXILIARY_SENDS), + + DECL(ALC_FORMAT_CHANNELS_SOFT), + DECL(ALC_FORMAT_TYPE_SOFT), + + DECL(ALC_MONO_SOFT), + DECL(ALC_STEREO_SOFT), + DECL(ALC_QUAD_SOFT), + DECL(ALC_5POINT1_SOFT), + DECL(ALC_6POINT1_SOFT), + DECL(ALC_7POINT1_SOFT), + DECL(ALC_BFORMAT3D_SOFT), + + DECL(ALC_BYTE_SOFT), + DECL(ALC_UNSIGNED_BYTE_SOFT), + DECL(ALC_SHORT_SOFT), + DECL(ALC_UNSIGNED_SHORT_SOFT), + DECL(ALC_INT_SOFT), + DECL(ALC_UNSIGNED_INT_SOFT), + DECL(ALC_FLOAT_SOFT), + + DECL(ALC_HRTF_SOFT), + DECL(ALC_DONT_CARE_SOFT), + DECL(ALC_HRTF_STATUS_SOFT), + DECL(ALC_HRTF_DISABLED_SOFT), + DECL(ALC_HRTF_ENABLED_SOFT), + DECL(ALC_HRTF_DENIED_SOFT), + DECL(ALC_HRTF_REQUIRED_SOFT), + DECL(ALC_HRTF_HEADPHONES_DETECTED_SOFT), + DECL(ALC_HRTF_UNSUPPORTED_FORMAT_SOFT), + DECL(ALC_NUM_HRTF_SPECIFIERS_SOFT), + DECL(ALC_HRTF_SPECIFIER_SOFT), + DECL(ALC_HRTF_ID_SOFT), + + DECL(ALC_AMBISONIC_LAYOUT_SOFT), + DECL(ALC_AMBISONIC_SCALING_SOFT), + DECL(ALC_AMBISONIC_ORDER_SOFT), + DECL(ALC_ACN_SOFT), + DECL(ALC_FUMA_SOFT), + DECL(ALC_N3D_SOFT), + DECL(ALC_SN3D_SOFT), + + DECL(ALC_OUTPUT_LIMITER_SOFT), + + DECL(ALC_DEVICE_CLOCK_SOFT), + DECL(ALC_DEVICE_LATENCY_SOFT), + DECL(ALC_DEVICE_CLOCK_LATENCY_SOFT), + DECL(AL_SAMPLE_OFFSET_CLOCK_SOFT), + DECL(AL_SEC_OFFSET_CLOCK_SOFT), + + DECL(ALC_OUTPUT_MODE_SOFT), + DECL(ALC_ANY_SOFT), + DECL(ALC_STEREO_BASIC_SOFT), + DECL(ALC_STEREO_UHJ_SOFT), + DECL(ALC_STEREO_HRTF_SOFT), + DECL(ALC_SURROUND_5_1_SOFT), + DECL(ALC_SURROUND_6_1_SOFT), + DECL(ALC_SURROUND_7_1_SOFT), + + DECL(ALC_NO_ERROR), + DECL(ALC_INVALID_DEVICE), + DECL(ALC_INVALID_CONTEXT), + DECL(ALC_INVALID_ENUM), + DECL(ALC_INVALID_VALUE), + DECL(ALC_OUT_OF_MEMORY), + + DECL(ALC_CONTEXT_FLAGS_EXT), + DECL(ALC_CONTEXT_DEBUG_BIT_EXT), + + DECL(ALC_PLAYBACK_DEVICE_SOFT), + DECL(ALC_CAPTURE_DEVICE_SOFT), + DECL(ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT), + DECL(ALC_EVENT_TYPE_DEVICE_ADDED_SOFT), + DECL(ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT), + + + DECL(AL_INVALID), + DECL(AL_NONE), + DECL(AL_FALSE), + DECL(AL_TRUE), + + DECL(AL_SOURCE_RELATIVE), + DECL(AL_CONE_INNER_ANGLE), + DECL(AL_CONE_OUTER_ANGLE), + DECL(AL_PITCH), + DECL(AL_POSITION), + DECL(AL_DIRECTION), + DECL(AL_VELOCITY), + DECL(AL_LOOPING), + DECL(AL_BUFFER), + DECL(AL_GAIN), + DECL(AL_MIN_GAIN), + DECL(AL_MAX_GAIN), + DECL(AL_ORIENTATION), + DECL(AL_REFERENCE_DISTANCE), + DECL(AL_ROLLOFF_FACTOR), + DECL(AL_CONE_OUTER_GAIN), + DECL(AL_MAX_DISTANCE), + DECL(AL_SEC_OFFSET), + DECL(AL_SAMPLE_OFFSET), + DECL(AL_BYTE_OFFSET), + DECL(AL_SOURCE_TYPE), + DECL(AL_STATIC), + DECL(AL_STREAMING), + DECL(AL_UNDETERMINED), + DECL(AL_METERS_PER_UNIT), + DECL(AL_LOOP_POINTS_SOFT), + DECL(AL_DIRECT_CHANNELS_SOFT), + + DECL(AL_DIRECT_FILTER), + DECL(AL_AUXILIARY_SEND_FILTER), + DECL(AL_AIR_ABSORPTION_FACTOR), + DECL(AL_ROOM_ROLLOFF_FACTOR), + DECL(AL_CONE_OUTER_GAINHF), + DECL(AL_DIRECT_FILTER_GAINHF_AUTO), + DECL(AL_AUXILIARY_SEND_FILTER_GAIN_AUTO), + DECL(AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO), + + DECL(AL_SOURCE_STATE), + DECL(AL_INITIAL), + DECL(AL_PLAYING), + DECL(AL_PAUSED), + DECL(AL_STOPPED), + + DECL(AL_BUFFERS_QUEUED), + DECL(AL_BUFFERS_PROCESSED), + + DECL(AL_FORMAT_MONO8), + DECL(AL_FORMAT_MONO16), + DECL(AL_FORMAT_MONO_FLOAT32), + DECL(AL_FORMAT_MONO_DOUBLE_EXT), + DECL(AL_FORMAT_STEREO8), + DECL(AL_FORMAT_STEREO16), + DECL(AL_FORMAT_STEREO_FLOAT32), + DECL(AL_FORMAT_STEREO_DOUBLE_EXT), + DECL(AL_FORMAT_MONO_IMA4), + DECL(AL_FORMAT_STEREO_IMA4), + DECL(AL_FORMAT_MONO_MSADPCM_SOFT), + DECL(AL_FORMAT_STEREO_MSADPCM_SOFT), + DECL(AL_FORMAT_QUAD8_LOKI), + DECL(AL_FORMAT_QUAD16_LOKI), + DECL(AL_FORMAT_QUAD8), + DECL(AL_FORMAT_QUAD16), + DECL(AL_FORMAT_QUAD32), + DECL(AL_FORMAT_51CHN8), + DECL(AL_FORMAT_51CHN16), + DECL(AL_FORMAT_51CHN32), + DECL(AL_FORMAT_61CHN8), + DECL(AL_FORMAT_61CHN16), + DECL(AL_FORMAT_61CHN32), + DECL(AL_FORMAT_71CHN8), + DECL(AL_FORMAT_71CHN16), + DECL(AL_FORMAT_71CHN32), + DECL(AL_FORMAT_REAR8), + DECL(AL_FORMAT_REAR16), + DECL(AL_FORMAT_REAR32), + DECL(AL_FORMAT_MONO_MULAW), + DECL(AL_FORMAT_MONO_MULAW_EXT), + DECL(AL_FORMAT_STEREO_MULAW), + DECL(AL_FORMAT_STEREO_MULAW_EXT), + DECL(AL_FORMAT_QUAD_MULAW), + DECL(AL_FORMAT_51CHN_MULAW), + DECL(AL_FORMAT_61CHN_MULAW), + DECL(AL_FORMAT_71CHN_MULAW), + DECL(AL_FORMAT_REAR_MULAW), + DECL(AL_FORMAT_MONO_ALAW_EXT), + DECL(AL_FORMAT_STEREO_ALAW_EXT), + + DECL(AL_FORMAT_BFORMAT2D_8), + DECL(AL_FORMAT_BFORMAT2D_16), + DECL(AL_FORMAT_BFORMAT2D_FLOAT32), + DECL(AL_FORMAT_BFORMAT2D_MULAW), + DECL(AL_FORMAT_BFORMAT3D_8), + DECL(AL_FORMAT_BFORMAT3D_16), + DECL(AL_FORMAT_BFORMAT3D_FLOAT32), + DECL(AL_FORMAT_BFORMAT3D_MULAW), + + DECL(AL_FORMAT_UHJ2CHN8_SOFT), + DECL(AL_FORMAT_UHJ2CHN16_SOFT), + DECL(AL_FORMAT_UHJ2CHN_FLOAT32_SOFT), + DECL(AL_FORMAT_UHJ3CHN8_SOFT), + DECL(AL_FORMAT_UHJ3CHN16_SOFT), + DECL(AL_FORMAT_UHJ3CHN_FLOAT32_SOFT), + DECL(AL_FORMAT_UHJ4CHN8_SOFT), + DECL(AL_FORMAT_UHJ4CHN16_SOFT), + DECL(AL_FORMAT_UHJ4CHN_FLOAT32_SOFT), + DECL(AL_STEREO_MODE_SOFT), + DECL(AL_NORMAL_SOFT), + DECL(AL_SUPER_STEREO_SOFT), + DECL(AL_SUPER_STEREO_WIDTH_SOFT), + + DECL(AL_FORMAT_UHJ2CHN_MULAW_SOFT), + DECL(AL_FORMAT_UHJ2CHN_ALAW_SOFT), + DECL(AL_FORMAT_UHJ2CHN_IMA4_SOFT), + DECL(AL_FORMAT_UHJ2CHN_MSADPCM_SOFT), + DECL(AL_FORMAT_UHJ3CHN_MULAW_SOFT), + DECL(AL_FORMAT_UHJ3CHN_ALAW_SOFT), + DECL(AL_FORMAT_UHJ4CHN_MULAW_SOFT), + DECL(AL_FORMAT_UHJ4CHN_ALAW_SOFT), + + DECL(AL_FORMAT_MONO_I32), + DECL(AL_FORMAT_STEREO_I32), + DECL(AL_FORMAT_REAR_I32), + DECL(AL_FORMAT_QUAD_I32), + DECL(AL_FORMAT_51CHN_I32), + DECL(AL_FORMAT_61CHN_I32), + DECL(AL_FORMAT_71CHN_I32), + DECL(AL_FORMAT_UHJ2CHN_I32), + DECL(AL_FORMAT_UHJ3CHN_I32), + DECL(AL_FORMAT_UHJ4CHN_I32), + + DECL(AL_FORMAT_REAR_FLOAT32), + DECL(AL_FORMAT_QUAD_FLOAT32), + DECL(AL_FORMAT_51CHN_FLOAT32), + DECL(AL_FORMAT_61CHN_FLOAT32), + DECL(AL_FORMAT_71CHN_FLOAT32), + + DECL(AL_FREQUENCY), + DECL(AL_BITS), + DECL(AL_CHANNELS), + DECL(AL_SIZE), + DECL(AL_UNPACK_BLOCK_ALIGNMENT_SOFT), + DECL(AL_PACK_BLOCK_ALIGNMENT_SOFT), + + DECL(AL_SOURCE_RADIUS), + + DECL(AL_SAMPLE_OFFSET_LATENCY_SOFT), + DECL(AL_SEC_OFFSET_LATENCY_SOFT), + + DECL(AL_STEREO_ANGLES), + + DECL(AL_UNUSED), + DECL(AL_PENDING), + DECL(AL_PROCESSED), + + DECL(AL_NO_ERROR), + DECL(AL_INVALID_NAME), + DECL(AL_INVALID_ENUM), + DECL(AL_INVALID_VALUE), + DECL(AL_INVALID_OPERATION), + DECL(AL_OUT_OF_MEMORY), + + DECL(AL_VENDOR), + DECL(AL_VERSION), + DECL(AL_RENDERER), + DECL(AL_EXTENSIONS), + + DECL(AL_DOPPLER_FACTOR), + DECL(AL_DOPPLER_VELOCITY), + DECL(AL_DISTANCE_MODEL), + DECL(AL_SPEED_OF_SOUND), + DECL(AL_SOURCE_DISTANCE_MODEL), + DECL(AL_DEFERRED_UPDATES_SOFT), + DECL(AL_GAIN_LIMIT_SOFT), + + DECL(AL_INVERSE_DISTANCE), + DECL(AL_INVERSE_DISTANCE_CLAMPED), + DECL(AL_LINEAR_DISTANCE), + DECL(AL_LINEAR_DISTANCE_CLAMPED), + DECL(AL_EXPONENT_DISTANCE), + DECL(AL_EXPONENT_DISTANCE_CLAMPED), + + DECL(AL_FILTER_TYPE), + DECL(AL_FILTER_NULL), + DECL(AL_FILTER_LOWPASS), + DECL(AL_FILTER_HIGHPASS), + DECL(AL_FILTER_BANDPASS), + + DECL(AL_LOWPASS_GAIN), + DECL(AL_LOWPASS_GAINHF), + + DECL(AL_HIGHPASS_GAIN), + DECL(AL_HIGHPASS_GAINLF), + + DECL(AL_BANDPASS_GAIN), + DECL(AL_BANDPASS_GAINHF), + DECL(AL_BANDPASS_GAINLF), + + DECL(AL_EFFECT_TYPE), + DECL(AL_EFFECT_NULL), + DECL(AL_EFFECT_REVERB), + DECL(AL_EFFECT_EAXREVERB), + DECL(AL_EFFECT_CHORUS), + DECL(AL_EFFECT_DISTORTION), + DECL(AL_EFFECT_ECHO), + DECL(AL_EFFECT_FLANGER), + DECL(AL_EFFECT_PITCH_SHIFTER), + DECL(AL_EFFECT_FREQUENCY_SHIFTER), + DECL(AL_EFFECT_VOCAL_MORPHER), + DECL(AL_EFFECT_RING_MODULATOR), + DECL(AL_EFFECT_AUTOWAH), + DECL(AL_EFFECT_COMPRESSOR), + DECL(AL_EFFECT_EQUALIZER), + DECL(AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT), + DECL(AL_EFFECT_DEDICATED_DIALOGUE), + + DECL(AL_EFFECTSLOT_EFFECT), + DECL(AL_EFFECTSLOT_GAIN), + DECL(AL_EFFECTSLOT_AUXILIARY_SEND_AUTO), + DECL(AL_EFFECTSLOT_NULL), + + DECL(AL_EAXREVERB_DENSITY), + DECL(AL_EAXREVERB_DIFFUSION), + DECL(AL_EAXREVERB_GAIN), + DECL(AL_EAXREVERB_GAINHF), + DECL(AL_EAXREVERB_GAINLF), + DECL(AL_EAXREVERB_DECAY_TIME), + DECL(AL_EAXREVERB_DECAY_HFRATIO), + DECL(AL_EAXREVERB_DECAY_LFRATIO), + DECL(AL_EAXREVERB_REFLECTIONS_GAIN), + DECL(AL_EAXREVERB_REFLECTIONS_DELAY), + DECL(AL_EAXREVERB_REFLECTIONS_PAN), + DECL(AL_EAXREVERB_LATE_REVERB_GAIN), + DECL(AL_EAXREVERB_LATE_REVERB_DELAY), + DECL(AL_EAXREVERB_LATE_REVERB_PAN), + DECL(AL_EAXREVERB_ECHO_TIME), + DECL(AL_EAXREVERB_ECHO_DEPTH), + DECL(AL_EAXREVERB_MODULATION_TIME), + DECL(AL_EAXREVERB_MODULATION_DEPTH), + DECL(AL_EAXREVERB_AIR_ABSORPTION_GAINHF), + DECL(AL_EAXREVERB_HFREFERENCE), + DECL(AL_EAXREVERB_LFREFERENCE), + DECL(AL_EAXREVERB_ROOM_ROLLOFF_FACTOR), + DECL(AL_EAXREVERB_DECAY_HFLIMIT), + + DECL(AL_REVERB_DENSITY), + DECL(AL_REVERB_DIFFUSION), + DECL(AL_REVERB_GAIN), + DECL(AL_REVERB_GAINHF), + DECL(AL_REVERB_DECAY_TIME), + DECL(AL_REVERB_DECAY_HFRATIO), + DECL(AL_REVERB_REFLECTIONS_GAIN), + DECL(AL_REVERB_REFLECTIONS_DELAY), + DECL(AL_REVERB_LATE_REVERB_GAIN), + DECL(AL_REVERB_LATE_REVERB_DELAY), + DECL(AL_REVERB_AIR_ABSORPTION_GAINHF), + DECL(AL_REVERB_ROOM_ROLLOFF_FACTOR), + DECL(AL_REVERB_DECAY_HFLIMIT), + + DECL(AL_CHORUS_WAVEFORM), + DECL(AL_CHORUS_PHASE), + DECL(AL_CHORUS_RATE), + DECL(AL_CHORUS_DEPTH), + DECL(AL_CHORUS_FEEDBACK), + DECL(AL_CHORUS_DELAY), + + DECL(AL_DISTORTION_EDGE), + DECL(AL_DISTORTION_GAIN), + DECL(AL_DISTORTION_LOWPASS_CUTOFF), + DECL(AL_DISTORTION_EQCENTER), + DECL(AL_DISTORTION_EQBANDWIDTH), + + DECL(AL_ECHO_DELAY), + DECL(AL_ECHO_LRDELAY), + DECL(AL_ECHO_DAMPING), + DECL(AL_ECHO_FEEDBACK), + DECL(AL_ECHO_SPREAD), + + DECL(AL_FLANGER_WAVEFORM), + DECL(AL_FLANGER_PHASE), + DECL(AL_FLANGER_RATE), + DECL(AL_FLANGER_DEPTH), + DECL(AL_FLANGER_FEEDBACK), + DECL(AL_FLANGER_DELAY), + + DECL(AL_FREQUENCY_SHIFTER_FREQUENCY), + DECL(AL_FREQUENCY_SHIFTER_LEFT_DIRECTION), + DECL(AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION), + + DECL(AL_RING_MODULATOR_FREQUENCY), + DECL(AL_RING_MODULATOR_HIGHPASS_CUTOFF), + DECL(AL_RING_MODULATOR_WAVEFORM), + + DECL(AL_PITCH_SHIFTER_COARSE_TUNE), + DECL(AL_PITCH_SHIFTER_FINE_TUNE), + + DECL(AL_COMPRESSOR_ONOFF), + + DECL(AL_EQUALIZER_LOW_GAIN), + DECL(AL_EQUALIZER_LOW_CUTOFF), + DECL(AL_EQUALIZER_MID1_GAIN), + DECL(AL_EQUALIZER_MID1_CENTER), + DECL(AL_EQUALIZER_MID1_WIDTH), + DECL(AL_EQUALIZER_MID2_GAIN), + DECL(AL_EQUALIZER_MID2_CENTER), + DECL(AL_EQUALIZER_MID2_WIDTH), + DECL(AL_EQUALIZER_HIGH_GAIN), + DECL(AL_EQUALIZER_HIGH_CUTOFF), + + DECL(AL_DEDICATED_GAIN), + + DECL(AL_AUTOWAH_ATTACK_TIME), + DECL(AL_AUTOWAH_RELEASE_TIME), + DECL(AL_AUTOWAH_RESONANCE), + DECL(AL_AUTOWAH_PEAK_GAIN), + + DECL(AL_VOCAL_MORPHER_PHONEMEA), + DECL(AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING), + DECL(AL_VOCAL_MORPHER_PHONEMEB), + DECL(AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING), + DECL(AL_VOCAL_MORPHER_WAVEFORM), + DECL(AL_VOCAL_MORPHER_RATE), + + DECL(AL_EFFECTSLOT_TARGET_SOFT), + + DECL(AL_NUM_RESAMPLERS_SOFT), + DECL(AL_DEFAULT_RESAMPLER_SOFT), + DECL(AL_SOURCE_RESAMPLER_SOFT), + DECL(AL_RESAMPLER_NAME_SOFT), + + DECL(AL_SOURCE_SPATIALIZE_SOFT), + DECL(AL_AUTO_SOFT), + + DECL(AL_MAP_READ_BIT_SOFT), + DECL(AL_MAP_WRITE_BIT_SOFT), + DECL(AL_MAP_PERSISTENT_BIT_SOFT), + DECL(AL_PRESERVE_DATA_BIT_SOFT), + + DECL(AL_EVENT_CALLBACK_FUNCTION_SOFT), + DECL(AL_EVENT_CALLBACK_USER_PARAM_SOFT), + DECL(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT), + DECL(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT), + DECL(AL_EVENT_TYPE_DISCONNECTED_SOFT), + + DECL(AL_DROP_UNMATCHED_SOFT), + DECL(AL_REMIX_UNMATCHED_SOFT), + + DECL(AL_AMBISONIC_LAYOUT_SOFT), + DECL(AL_AMBISONIC_SCALING_SOFT), + DECL(AL_FUMA_SOFT), + DECL(AL_ACN_SOFT), + DECL(AL_SN3D_SOFT), + DECL(AL_N3D_SOFT), + + DECL(AL_BUFFER_CALLBACK_FUNCTION_SOFT), + DECL(AL_BUFFER_CALLBACK_USER_PARAM_SOFT), + + DECL(AL_UNPACK_AMBISONIC_ORDER_SOFT), + + DECL(AL_EFFECT_CONVOLUTION_SOFT), + DECL(AL_EFFECTSLOT_STATE_SOFT), + + DECL(AL_DONT_CARE_EXT), + DECL(AL_DEBUG_OUTPUT_EXT), + DECL(AL_DEBUG_CALLBACK_FUNCTION_EXT), + DECL(AL_DEBUG_CALLBACK_USER_PARAM_EXT), + DECL(AL_DEBUG_SOURCE_API_EXT), + DECL(AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT), + DECL(AL_DEBUG_SOURCE_THIRD_PARTY_EXT), + DECL(AL_DEBUG_SOURCE_APPLICATION_EXT), + DECL(AL_DEBUG_SOURCE_OTHER_EXT), + DECL(AL_DEBUG_TYPE_ERROR_EXT), + DECL(AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT), + DECL(AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT), + DECL(AL_DEBUG_TYPE_PORTABILITY_EXT), + DECL(AL_DEBUG_TYPE_PERFORMANCE_EXT), + DECL(AL_DEBUG_TYPE_MARKER_EXT), + DECL(AL_DEBUG_TYPE_PUSH_GROUP_EXT), + DECL(AL_DEBUG_TYPE_POP_GROUP_EXT), + DECL(AL_DEBUG_TYPE_OTHER_EXT), + DECL(AL_DEBUG_SEVERITY_HIGH_EXT), + DECL(AL_DEBUG_SEVERITY_MEDIUM_EXT), + DECL(AL_DEBUG_SEVERITY_LOW_EXT), + DECL(AL_DEBUG_SEVERITY_NOTIFICATION_EXT), + DECL(AL_DEBUG_LOGGED_MESSAGES_EXT), + DECL(AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT), + DECL(AL_MAX_DEBUG_MESSAGE_LENGTH_EXT), + DECL(AL_MAX_DEBUG_LOGGED_MESSAGES_EXT), + DECL(AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT), + DECL(AL_MAX_LABEL_LENGTH_EXT), + DECL(AL_STACK_OVERFLOW_EXT), + DECL(AL_STACK_UNDERFLOW_EXT), + DECL(AL_BUFFER_EXT), + DECL(AL_SOURCE_EXT), + DECL(AL_FILTER_EXT), + DECL(AL_EFFECT_EXT), + DECL(AL_AUXILIARY_EFFECT_SLOT_EXT), + + DECL(AL_STOP_SOURCES_ON_DISCONNECT_SOFT), +#ifdef ALSOFT_EAX +}, eaxEnumerations[]{ + DECL(AL_EAX_RAM_SIZE), + DECL(AL_EAX_RAM_FREE), + DECL(AL_STORAGE_AUTOMATIC), + DECL(AL_STORAGE_HARDWARE), + DECL(AL_STORAGE_ACCESSIBLE), +#endif // ALSOFT_EAX +}; +#undef DECL + +#endif /* ALC_EXPORT_LIST_H */ diff --git a/alc/inprogext.h b/alc/inprogext.h index ccb9a4be..c5b09f13 100644 --- a/alc/inprogext.h +++ b/alc/inprogext.h @@ -16,15 +16,23 @@ typedef unsigned int ALbitfieldSOFT; #define AL_MAP_WRITE_BIT_SOFT 0x00000002 #define AL_MAP_PERSISTENT_BIT_SOFT 0x00000004 #define AL_PRESERVE_DATA_BIT_SOFT 0x00000008 -typedef void (AL_APIENTRY*LPALBUFFERSTORAGESOFT)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags); -typedef void* (AL_APIENTRY*LPALMAPBUFFERSOFT)(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access); -typedef void (AL_APIENTRY*LPALUNMAPBUFFERSOFT)(ALuint buffer); -typedef void (AL_APIENTRY*LPALFLUSHMAPPEDBUFFERSOFT)(ALuint buffer, ALsizei offset, ALsizei length); +typedef void (AL_APIENTRY*LPALBUFFERSTORAGESOFT)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY*LPALMAPBUFFERSOFT)(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALUNMAPBUFFERSOFT)(ALuint buffer) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALFLUSHMAPPEDBUFFERSOFT)(ALuint buffer, ALsizei offset, ALsizei length) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALBUFFERSTORAGEDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY*LPALMAPBUFFERDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALUNMAPBUFFERDIRECTSOFT)(ALCcontext *context, ALuint buffer) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALFLUSHMAPPEDBUFFERDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALsizei offset, ALsizei length) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alBufferStorageSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags); -AL_API void* AL_APIENTRY alMapBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access); -AL_API void AL_APIENTRY alUnmapBufferSOFT(ALuint buffer); -AL_API void AL_APIENTRY alFlushMappedBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length); +AL_API void AL_APIENTRY alBufferStorageSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags) AL_API_NOEXCEPT; +AL_API void* AL_APIENTRY alMapBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alUnmapBufferSOFT(ALuint buffer) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alFlushMappedBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferStorageDirectSOFT(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags) AL_API_NOEXCEPT; +void* AL_APIENTRY alMapBufferDirectSOFT(ALCcontext *context, ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access) AL_API_NOEXCEPT; +void AL_APIENTRY alUnmapBufferDirectSOFT(ALCcontext *context, ALuint buffer) AL_API_NOEXCEPT; +void AL_APIENTRY alFlushMappedBufferDirectSOFT(ALCcontext *context, ALuint buffer, ALsizei offset, ALsizei length) AL_API_NOEXCEPT; #endif #endif @@ -33,19 +41,20 @@ AL_API void AL_APIENTRY alFlushMappedBufferSOFT(ALuint buffer, ALsizei offset, A #define AL_UNPACK_AMBISONIC_ORDER_SOFT 0x199D #endif -#ifndef AL_SOFT_convolution_reverb -#define AL_SOFT_convolution_reverb -#define AL_EFFECT_CONVOLUTION_REVERB_SOFT 0xA000 -#define AL_EFFECTSLOT_STATE_SOFT 0x199D -typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTPLAYSOFT)(ALuint slotid); -typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTPLAYVSOFT)(ALsizei n, const ALuint *slotids); -typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTSTOPSOFT)(ALuint slotid); -typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTSTOPVSOFT)(ALsizei n, const ALuint *slotids); +#ifndef AL_SOFT_convolution_effect +#define AL_SOFT_convolution_effect +#define AL_EFFECT_CONVOLUTION_SOFT 0xA000 +#define AL_CONVOLUTION_ORIENTATION_SOFT 0x100F /* same as AL_ORIENTATION */ +#define AL_EFFECTSLOT_STATE_SOFT 0x199E +typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTPLAYSOFT)(ALuint slotid) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTPLAYVSOFT)(ALsizei n, const ALuint *slotids) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTSTOPSOFT)(ALuint slotid) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTSTOPVSOFT)(ALsizei n, const ALuint *slotids) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid); -AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids); -AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid); -AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids); +AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids) AL_API_NOEXCEPT; #endif #endif @@ -55,16 +64,374 @@ AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint * #endif -/* Non-standard export. Not part of any extension. */ -AL_API const ALchar* AL_APIENTRY alsoft_get_version(void); +#ifndef AL_EXT_direct_context +#define AL_EXT_direct_context +typedef ALCvoid* (ALC_APIENTRY *LPALCGETPROCADDRESS2)(ALCdevice *device, const ALCchar *funcname) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALENABLEDIRECT)(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDISABLEDIRECT)(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISENABLEDDIRECT)(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDOPPLERFACTORDIRECT)(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSPEEDOFSOUNDDIRECT)(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDISTANCEMODELDIRECT)(ALCcontext *context, ALenum distanceModel) AL_API_NOEXCEPT17; +typedef const ALchar* (AL_APIENTRY *LPALGETSTRINGDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBOOLEANVDIRECT)(ALCcontext *context, ALenum param, ALboolean *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETINTEGERVDIRECT)(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFLOATVDIRECT)(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETDOUBLEVDIRECT)(ALCcontext *context, ALenum param, ALdouble *values) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALGETBOOLEANDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef ALint (AL_APIENTRY *LPALGETINTEGERDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef ALfloat (AL_APIENTRY *LPALGETFLOATDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef ALdouble (AL_APIENTRY *LPALGETDOUBLEDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPALGETERRORDIRECT)(ALCcontext *context) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENTDIRECT)(ALCcontext *context, const ALchar *extname) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY *LPALGETPROCADDRESSDIRECT)(ALCcontext *context, const ALchar *fname) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPALGETENUMVALUEDIRECT)(ALCcontext *context, const ALchar *ename) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERFDIRECT)(ALCcontext *context, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENER3FDIRECT)(ALCcontext *context, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERFVDIRECT)(ALCcontext *context, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERIDIRECT)(ALCcontext *context, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENER3IDIRECT)(ALCcontext *context, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERIVDIRECT)(ALCcontext *context, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERFDIRECT)(ALCcontext *context, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENER3FDIRECT)(ALCcontext *context, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERFVDIRECT)(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERIDIRECT)(ALCcontext *context, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENER3IDIRECT)(ALCcontext *context, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERIVDIRECT)(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENSOURCESDIRECT)(ALCcontext *context, ALsizei n, ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETESOURCESDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISSOURCEDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEFDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3FDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEFVDIRECT)(ALCcontext *context, ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEIDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3IDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEIVDIRECT)(ALCcontext *context, ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEFDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3FDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEFVDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEIDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3IDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEIVDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPLAYVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCESTOPVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEREWINDVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPAUSEVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPLAYDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCESTOPDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEREWINDDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPAUSEDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERSDIRECT)(ALCcontext *context, ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERSDIRECT)(ALCcontext *context, ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENBUFFERSDIRECT)(ALCcontext *context, ALsizei n, ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEBUFFERSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISBUFFERDIRECT)(ALCcontext *context, ALuint buffer) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERDATADIRECT)(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERFDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFER3FDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERFVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERIDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFER3IDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERIVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERFDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFER3FDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERFVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERIDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFER3IDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERIVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT17; +/* ALC_EXT_EFX */ +typedef void (AL_APIENTRY *LPALGENEFFECTSDIRECT)(ALCcontext *context, ALsizei n, ALuint *effects) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEEFFECTSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *effects) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISEFFECTDIRECT)(ALCcontext *context, ALuint effect) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTIDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTIVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTFDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTFVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTIDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTIVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTFDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTFVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENFILTERSDIRECT)(ALCcontext *context, ALsizei n, ALuint *filters) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEFILTERSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *filters) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISFILTERDIRECT)(ALCcontext *context, ALuint filter) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERIDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERIVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERFDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERFVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERIDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERIVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERFDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERFVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTSDIRECT)(ALCcontext *context, ALsizei n, ALuint *effectslots) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *effectslots) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOTDIRECT)(ALCcontext *context, ALuint effectslot) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +/* AL_EXT_BUFFER_DATA_STATIC */ +typedef void (AL_APIENTRY *LPALBUFFERDATASTATICDIRECT)(ALCcontext *context, ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq) AL_API_NOEXCEPT17; +/* AL_EXT_debug */ +typedef void (AL_APIENTRY*LPALDEBUGMESSAGECALLBACKDIRECTEXT)(ALCcontext *context, ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALDEBUGMESSAGEINSERTDIRECTEXT)(ALCcontext *context, ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALDEBUGMESSAGECONTROLDIRECTEXT)(ALCcontext *context, ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALPUSHDEBUGGROUPDIRECTEXT)(ALCcontext *context, ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALPOPDEBUGGROUPDIRECTEXT)(ALCcontext *context) AL_API_NOEXCEPT17; +typedef ALuint (AL_APIENTRY*LPALGETDEBUGMESSAGELOGDIRECTEXT)(ALCcontext *context, ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALOBJECTLABELDIRECTEXT)(ALCcontext *context, ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETOBJECTLABELDIRECTEXT)(ALCcontext *context, ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT17; +/* AL_EXT_FOLDBACK */ +typedef void (AL_APIENTRY *LPALREQUESTFOLDBACKSTARTDIRECT)(ALCcontext *context, ALenum mode, ALsizei count, ALsizei length, ALfloat *mem, LPALFOLDBACKCALLBACK callback) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALREQUESTFOLDBACKSTOPDIRECT)(ALCcontext *context) AL_API_NOEXCEPT17; +/* AL_SOFT_buffer_sub_data */ +typedef void (AL_APIENTRY *LPALBUFFERSUBDATADIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) AL_API_NOEXCEPT17; +/* AL_SOFT_source_latency */ +typedef void (AL_APIENTRY *LPALSOURCEDDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3DDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble,ALdouble,ALdouble) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEDVDIRECTSOFT)(ALCcontext*,ALuint,ALenum,const ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEDDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3DDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEDVDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEI64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3I64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEI64VDIRECTSOFT)(ALCcontext*,ALuint,ALenum,const ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEI64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3I64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEI64VDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17; +/* AL_SOFT_deferred_updates */ +typedef void (AL_APIENTRY *LPALDEFERUPDATESDIRECTSOFT)(ALCcontext *context) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALPROCESSUPDATESDIRECTSOFT)(ALCcontext *context) AL_API_NOEXCEPT17; +/* AL_SOFT_source_resampler */ +typedef const ALchar* (AL_APIENTRY *LPALGETSTRINGIDIRECTSOFT)(ALCcontext *context, ALenum pname, ALsizei index) AL_API_NOEXCEPT17; +/* AL_SOFT_events */ +typedef void (AL_APIENTRY *LPALEVENTCONTROLDIRECTSOFT)(ALCcontext *context, ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEVENTCALLBACKDIRECTSOFT)(ALCcontext *context, ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY *LPALGETPOINTERDIRECTSOFT)(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETPOINTERVDIRECTSOFT)(ALCcontext *context, ALenum pname, void **values) AL_API_NOEXCEPT17; +/* AL_SOFT_callback_buffer */ +typedef void (AL_APIENTRY *LPALBUFFERCALLBACKDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERPTRDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFER3PTRDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERPTRVDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **values) AL_API_NOEXCEPT17; +/* AL_SOFT_source_start_delay */ +typedef void (AL_APIENTRY *LPALSOURCEPLAYATTIMEDIRECTSOFT)(ALCcontext *context, ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPLAYATTIMEVDIRECTSOFT)(ALCcontext *context, ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT17; +/* EAX */ +typedef ALenum (AL_APIENTRY *LPEAXSETDIRECT)(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint property_source_id, ALvoid *property_buffer, ALuint property_size) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPEAXGETDIRECT)(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint property_source_id, ALvoid *property_value, ALuint property_value_size) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPEAXSETBUFFERMODEDIRECT)(ALCcontext *context, ALsizei n, const ALuint *buffers, ALint value) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPEAXGETBUFFERMODEDIRECT)(ALCcontext *context, ALuint buffer, ALint *pReserved) AL_API_NOEXCEPT17; +#ifdef AL_ALEXT_PROTOTYPES +ALCvoid* AL_APIENTRY alcGetProcAddress2(ALCdevice *device, const ALchar *funcName) AL_API_NOEXCEPT; + +void AL_APIENTRY alEnableDirect(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT; +void AL_APIENTRY alDisableDirect(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsEnabledDirect(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT; + +void AL_APIENTRY alDopplerFactorDirect(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alSpeedOfSoundDirect(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alDistanceModelDirect(ALCcontext *context, ALenum distanceModel) AL_API_NOEXCEPT; + +const ALchar* AL_APIENTRY alGetStringDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBooleanvDirect(ALCcontext *context, ALenum param, ALboolean *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetIntegervDirect(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFloatvDirect(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetDoublevDirect(ALCcontext *context, ALenum param, ALdouble *values) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alGetBooleanDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; +ALint AL_APIENTRY alGetIntegerDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; +ALfloat AL_APIENTRY alGetFloatDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; +ALdouble AL_APIENTRY alGetDoubleDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; + +ALenum AL_APIENTRY alGetErrorDirect(ALCcontext *context) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsExtensionPresentDirect(ALCcontext *context, const ALchar *extname) AL_API_NOEXCEPT; +void* AL_APIENTRY alGetProcAddressDirect(ALCcontext *context, const ALchar *fname) AL_API_NOEXCEPT; +ALenum AL_APIENTRY alGetEnumValueDirect(ALCcontext *context, const ALchar *ename) AL_API_NOEXCEPT; + +void AL_APIENTRY alListenerfDirect(ALCcontext *context, ALenum param, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alListener3fDirect(ALCcontext *context, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +void AL_APIENTRY alListenerfvDirect(ALCcontext *context, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alListeneriDirect(ALCcontext *context, ALenum param, ALint value) AL_API_NOEXCEPT; +void AL_APIENTRY alListener3iDirect(ALCcontext *context, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +void AL_APIENTRY alListenerivDirect(ALCcontext *context, ALenum param, const ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListenerfDirect(ALCcontext *context, ALenum param, ALfloat *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListener3fDirect(ALCcontext *context, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListenerfvDirect(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListeneriDirect(ALCcontext *context, ALenum param, ALint *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListener3iDirect(ALCcontext *context, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListenerivDirect(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenSourcesDirect(ALCcontext *context, ALsizei n, ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteSourcesDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsSourceDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcefDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alSource3fDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcefvDirect(ALCcontext *context, ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceiDirect(ALCcontext *context, ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT; +void AL_APIENTRY alSource3iDirect(ALCcontext *context, ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceivDirect(ALCcontext *context, ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcefDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSource3fDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcefvDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourceiDirect(ALCcontext *context, ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSource3iDirect(ALCcontext *context, ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourceivDirect(ALCcontext *context, ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePlayDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceStopDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceRewindDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePauseDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePlayvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceStopvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceRewindvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePausevDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceQueueBuffersDirect(ALCcontext *context, ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceUnqueueBuffersDirect(ALCcontext *context, ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenBuffersDirect(ALCcontext *context, ALsizei n, ALuint *buffers) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteBuffersDirect(ALCcontext *context, ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsBufferDirect(ALCcontext *context, ALuint buffer) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferDataDirect(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT; +void AL_APIENTRY alBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteEffectsDirect(ALCcontext *context, ALsizei n, const ALuint *effects) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsEffectDirect(ALCcontext *context, ALuint effect) AL_API_NOEXCEPT; +void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, ALenum param, ALint iValue) AL_API_NOEXCEPT; +void AL_APIENTRY alEffectivDirect(ALCcontext *context, ALuint effect, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alEffectfDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +void AL_APIENTRY alEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effect, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetEffectivDirect(ALCcontext *context, ALuint effect, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenFiltersDirect(ALCcontext *context, ALsizei n, ALuint *filters) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteFiltersDirect(ALCcontext *context, ALsizei n, const ALuint *filters) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsFilterDirect(ALCcontext *context, ALuint filter) AL_API_NOEXCEPT; +void AL_APIENTRY alFilteriDirect(ALCcontext *context, ALuint filter, ALenum param, ALint iValue) AL_API_NOEXCEPT; +void AL_APIENTRY alFilterivDirect(ALCcontext *context, ALuint filter, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alFilterfDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +void AL_APIENTRY alFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFilteriDirect(ALCcontext *context, ALuint filter, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFilterivDirect(ALCcontext *context, ALuint filter, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFilterfDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n, ALuint *effectslots) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n, const ALuint *effectslots) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsAuxiliaryEffectSlotDirect(ALCcontext *context, ALuint effectslot) AL_API_NOEXCEPT; +void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint iValue) AL_API_NOEXCEPT; +void AL_APIENTRY alAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +void AL_APIENTRY alAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; + +void AL_APIENTRY alBufferDataStaticDirect(ALCcontext *context, ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq) AL_API_NOEXCEPT; + +void AL_APIENTRY alDebugMessageCallbackDirectEXT(ALCcontext *context, ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT; +void AL_APIENTRY alDebugMessageInsertDirectEXT(ALCcontext *context, ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT; +void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT; +void AL_APIENTRY alPushDebugGroupDirectEXT(ALCcontext *context, ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT; +void AL_APIENTRY alPopDebugGroupDirectEXT(ALCcontext *context) AL_API_NOEXCEPT; +ALuint AL_APIENTRY alGetDebugMessageLogDirectEXT(ALCcontext *context, ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT; +void AL_APIENTRY alObjectLabelDirectEXT(ALCcontext *context, ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT; +void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT; + +void AL_APIENTRY alRequestFoldbackStartDirect(ALCcontext *context, ALenum mode, ALsizei count, ALsizei length, ALfloat *mem, LPALFOLDBACKCALLBACK callback) AL_API_NOEXCEPT; +void AL_APIENTRY alRequestFoldbackStopDirect(ALCcontext *context) AL_API_NOEXCEPT; + +void AL_APIENTRY alBufferSubDataDirectSOFT(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) AL_API_NOEXCEPT; + +void AL_APIENTRY alSourcedDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble value) AL_API_NOEXCEPT; +void AL_APIENTRY alSource3dDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcedvDirectSOFT(ALCcontext *context, ALuint source, ALenum param, const ALdouble *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcedDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSource3dDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcedvDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *values) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcei64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT value) AL_API_NOEXCEPT; +void AL_APIENTRY alSource3i64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcei64vDirectSOFT(ALCcontext *context, ALuint source, ALenum param, const ALint64SOFT *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcei64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSource3i64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcei64vDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *values) AL_API_NOEXCEPT; + +void AL_APIENTRY alDeferUpdatesDirectSOFT(ALCcontext *context) AL_API_NOEXCEPT; +void AL_APIENTRY alProcessUpdatesDirectSOFT(ALCcontext *context) AL_API_NOEXCEPT; + +const ALchar* AL_APIENTRY alGetStringiDirectSOFT(ALCcontext *context, ALenum pname, ALsizei index) AL_API_NOEXCEPT; + +void AL_APIENTRY alEventControlDirectSOFT(ALCcontext *context, ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT; +void AL_APIENTRY alEventCallbackDirectSOFT(ALCcontext *context, ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT; +void* AL_APIENTRY alGetPointerDirectSOFT(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT; +void AL_APIENTRY alGetPointervDirectSOFT(ALCcontext *context, ALenum pname, void **values) AL_API_NOEXCEPT; + +void AL_APIENTRY alBufferCallbackDirectSOFT(ALCcontext *context, ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferPtrDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBuffer3PtrDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **ptr0, ALvoid **ptr1, ALvoid **ptr2) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferPtrvDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT; + +void AL_APIENTRY alSourcePlayAtTimeDirectSOFT(ALCcontext *context, ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePlayAtTimevDirectSOFT(ALCcontext *context, ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT; + +ALenum AL_APIENTRY EAXSetDirect(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint property_source_id, ALvoid *property_value, ALuint property_value_size) AL_API_NOEXCEPT; +ALenum AL_APIENTRY EAXGetDirect(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint property_source_id, ALvoid *property_value, ALuint property_value_size) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY EAXSetBufferModeDirect(ALCcontext *context, ALsizei n, const ALuint *buffers, ALint value) AL_API_NOEXCEPT; +ALenum AL_APIENTRY EAXGetBufferModeDirect(ALCcontext *context, ALuint buffer, ALint *pReserved) AL_API_NOEXCEPT; +#endif +#endif + +#ifndef AL_EXT_int32 +#define AL_EXT_int32 +#define AL_FORMAT_MONO_I32 0x1202 /* Same as AL_FORMAT_MONO32 */ +#define AL_FORMAT_STEREO_I32 0x1203 /* Same as AL_FORMAT_STEREO32 */ +#define AL_FORMAT_REAR_I32 0x19DB +#define AL_FORMAT_QUAD_I32 0x19DC +#define AL_FORMAT_51CHN_I32 0x19DD +#define AL_FORMAT_61CHN_I32 0x19DE +#define AL_FORMAT_71CHN_I32 0x19DF +#define AL_FORMAT_UHJ2CHN_I32 0x19E0 +#define AL_FORMAT_UHJ3CHN_I32 0x19E1 +#define AL_FORMAT_UHJ4CHN_I32 0x19E2 + +#define AL_FORMAT_REAR_FLOAT32 0x19E3 +#define AL_FORMAT_QUAD_FLOAT32 0x19E4 +#define AL_FORMAT_51CHN_FLOAT32 0x19E5 +#define AL_FORMAT_61CHN_FLOAT32 0x19E6 +#define AL_FORMAT_71CHN_FLOAT32 0x19E7 +#endif + +/* Non-standard exports. Not part of any extension. */ +AL_API const ALchar* AL_APIENTRY alsoft_get_version(void) noexcept; + +typedef void (ALC_APIENTRY*LPALSOFTLOGCALLBACK)(void *userptr, char level, const char *message, int length) noexcept; +void ALC_APIENTRY alsoft_set_log_callback(LPALSOFTLOGCALLBACK callback, void *userptr) noexcept; /* Functions from abandoned extensions. Only here for binary compatibility. */ AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint src, ALsizei nb, - const ALuint *buffers); + const ALuint *buffers) noexcept; -AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname); -AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values); +AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values) AL_API_NOEXCEPT; +ALint64SOFT AL_APIENTRY alGetInteger64DirectSOFT(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT; +void AL_APIENTRY alGetInteger64vDirectSOFT(ALCcontext *context, ALenum pname, ALint64SOFT *values) AL_API_NOEXCEPT; #ifdef __cplusplus } /* extern "C" */ diff --git a/alc/panning.cpp b/alc/panning.cpp index d118f99c..b512a42a 100644 --- a/alc/panning.cpp +++ b/alc/panning.cpp @@ -32,7 +32,9 @@ #include <memory> #include <new> #include <numeric> +#include <optional> #include <string> +#include <vector> #include "AL/al.h" #include "AL/alc.h" @@ -45,7 +47,6 @@ #include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" -#include "aloptional.h" #include "alspan.h" #include "alstring.h" #include "alu.h" @@ -313,17 +314,17 @@ void InitDistanceComp(ALCdevice *device, const al::span<const Channel> channels, } -inline auto& GetAmbiScales(DevAmbiScaling scaletype) noexcept +constexpr auto GetAmbiScales(DevAmbiScaling scaletype) noexcept { - if(scaletype == DevAmbiScaling::FuMa) return AmbiScale::FromFuMa(); - if(scaletype == DevAmbiScaling::SN3D) return AmbiScale::FromSN3D(); - return AmbiScale::FromN3D(); + if(scaletype == DevAmbiScaling::FuMa) return al::span{AmbiScale::FromFuMa}; + if(scaletype == DevAmbiScaling::SN3D) return al::span{AmbiScale::FromSN3D}; + return al::span{AmbiScale::FromN3D}; } -inline auto& GetAmbiLayout(DevAmbiLayout layouttype) noexcept +constexpr auto GetAmbiLayout(DevAmbiLayout layouttype) noexcept { - if(layouttype == DevAmbiLayout::FuMa) return AmbiIndex::FromFuMa(); - return AmbiIndex::FromACN(); + if(layouttype == DevAmbiLayout::FuMa) return al::span{AmbiIndex::FromFuMa}; + return al::span{AmbiIndex::FromACN}; } @@ -346,16 +347,16 @@ DecoderView MakeDecoderView(ALCdevice *device, const AmbDecConf *conf, } std::copy_n(std::begin(conf->HFOrderGain), - std::min(al::size(conf->HFOrderGain), al::size(decoder.mOrderGain)), + std::min(std::size(conf->HFOrderGain), std::size(decoder.mOrderGain)), std::begin(decoder.mOrderGain)); std::copy_n(std::begin(conf->LFOrderGain), - std::min(al::size(conf->LFOrderGain), al::size(decoder.mOrderGainLF)), + std::min(std::size(conf->LFOrderGain), std::size(decoder.mOrderGainLF)), std::begin(decoder.mOrderGainLF)); const auto num_coeffs = decoder.mIs3D ? AmbiChannelsFromOrder(decoder.mOrder) : Ambi2DChannelsFromOrder(decoder.mOrder); - const auto idx_map = decoder.mIs3D ? AmbiIndex::FromACN().data() - : AmbiIndex::FromACN2D().data(); + const auto idx_map = decoder.mIs3D ? AmbiIndex::FromACN.data() + : AmbiIndex::FromACN2D.data(); const auto hfmatrix = conf->HFMatrix; const auto lfmatrix = conf->LFMatrix; @@ -600,13 +601,13 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= case DevFmtX714: decoder = X714Config; break; case DevFmtX3D71: decoder = X3D71Config; break; case DevFmtAmbi3D: - auto&& acnmap = GetAmbiLayout(device->mAmbiLayout); - auto&& n3dscale = GetAmbiScales(device->mAmbiScale); + const auto acnmap = GetAmbiLayout(device->mAmbiLayout); + const auto n3dscale = GetAmbiScales(device->mAmbiScale); /* For DevFmtAmbi3D, the ambisonic order is already set. */ const size_t count{AmbiChannelsFromOrder(device->mAmbiOrder)}; std::transform(acnmap.begin(), acnmap.begin()+count, std::begin(device->Dry.AmbiMap), - [&n3dscale](const uint8_t &acn) noexcept -> BFChannelConfig + [n3dscale](const uint8_t &acn) noexcept -> BFChannelConfig { return BFChannelConfig{1.0f/n3dscale[acn], acn}; }); AllocChannels(device, count, 0); device->m2DMixing = false; @@ -628,7 +629,7 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= const size_t ambicount{decoder.mIs3D ? AmbiChannelsFromOrder(decoder.mOrder) : Ambi2DChannelsFromOrder(decoder.mOrder)}; const bool dual_band{hqdec && !decoder.mCoeffsLF.empty()}; - al::vector<ChannelDec> chancoeffs, chancoeffslf; + std::vector<ChannelDec> chancoeffs, chancoeffslf; for(size_t i{0u};i < decoder.mChannels.size();++i) { const uint idx{device->channelIdxByName(decoder.mChannels[i])}; @@ -639,8 +640,8 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= continue; } - auto ordermap = decoder.mIs3D ? AmbiIndex::OrderFromChannel().data() - : AmbiIndex::OrderFrom2DChannel().data(); + auto ordermap = decoder.mIs3D ? AmbiIndex::OrderFromChannel.data() + : AmbiIndex::OrderFrom2DChannel.data(); chancoeffs.resize(maxz(chancoeffs.size(), idx+1u), ChannelDec{}); al::span<const float,MaxAmbiChannels> src{decoder.mCoeffs[i]}; @@ -662,11 +663,11 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= device->mAmbiOrder = decoder.mOrder; device->m2DMixing = !decoder.mIs3D; - const al::span<const uint8_t> acnmap{decoder.mIs3D ? AmbiIndex::FromACN().data() : - AmbiIndex::FromACN2D().data(), ambicount}; - auto&& coeffscale = GetAmbiScales(decoder.mScaling); + const al::span<const uint8_t> acnmap{decoder.mIs3D ? AmbiIndex::FromACN.data() : + AmbiIndex::FromACN2D.data(), ambicount}; + const auto coeffscale = GetAmbiScales(decoder.mScaling); std::transform(acnmap.begin(), acnmap.end(), std::begin(device->Dry.AmbiMap), - [&coeffscale](const uint8_t &acn) noexcept + [coeffscale](const uint8_t &acn) noexcept { return BFChannelConfig{1.0f/coeffscale[acn], acn}; }); AllocChannels(device, ambicount, device->channelsFromFmt()); @@ -676,7 +677,7 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= /* Only enable the stablizer if the decoder does not output to the * front-center channel. */ - const auto cidx = device->RealOut.ChannelIndex[FrontCenter]; + const size_t cidx{device->RealOut.ChannelIndex[FrontCenter]}; bool hasfc{false}; if(cidx < chancoeffs.size()) { @@ -818,9 +819,9 @@ void InitHrtfPanning(ALCdevice *device) /*RMS 8.340921354e-01f, 7.182670250e-01f, 5.107426573e-01f, 2.541870634e-01f*/ }; - static_assert(al::size(AmbiPoints1O) == al::size(AmbiMatrix1O), "First-Order Ambisonic HRTF mismatch"); - static_assert(al::size(AmbiPoints2O) == al::size(AmbiMatrix2O), "Second-Order Ambisonic HRTF mismatch"); - static_assert(al::size(AmbiPoints3O) == al::size(AmbiMatrix3O), "Third-Order Ambisonic HRTF mismatch"); + static_assert(std::size(AmbiPoints1O) == std::size(AmbiMatrix1O), "First-Order Ambisonic HRTF mismatch"); + static_assert(std::size(AmbiPoints2O) == std::size(AmbiMatrix2O), "Second-Order Ambisonic HRTF mismatch"); + static_assert(std::size(AmbiPoints3O) == std::size(AmbiMatrix3O), "Third-Order Ambisonic HRTF mismatch"); /* A 700hz crossover frequency provides tighter sound imaging at the sweet * spot with ambisonic decoding, as the distance between the ears is closer @@ -901,7 +902,7 @@ void InitHrtfPanning(ALCdevice *device) device->m2DMixing = false; const size_t count{AmbiChannelsFromOrder(ambi_order)}; - std::transform(AmbiIndex::FromACN().begin(), AmbiIndex::FromACN().begin()+count, + std::transform(AmbiIndex::FromACN.begin(), AmbiIndex::FromACN.begin()+count, std::begin(device->Dry.AmbiMap), [](const uint8_t &index) noexcept { return BFChannelConfig{1.0f, index}; } ); @@ -924,16 +925,16 @@ void InitUhjPanning(ALCdevice *device) device->mAmbiOrder = 1; device->m2DMixing = true; - auto acnmap_begin = AmbiIndex::FromFuMa2D().begin(); + auto acnmap_begin = AmbiIndex::FromFuMa2D.begin(); std::transform(acnmap_begin, acnmap_begin + count, std::begin(device->Dry.AmbiMap), [](const uint8_t &acn) noexcept -> BFChannelConfig - { return BFChannelConfig{1.0f/AmbiScale::FromUHJ()[acn], acn}; }); + { return BFChannelConfig{1.0f/AmbiScale::FromUHJ[acn], acn}; }); AllocChannels(device, count, device->channelsFromFmt()); } } // namespace -void aluInitRenderer(ALCdevice *device, int hrtf_id, al::optional<StereoEncoding> stereomode) +void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncoding> stereomode) { /* Hold the HRTF the device last used, in case it's used again. */ HrtfStorePtr old_hrtf{std::move(device->mHrtf)}; @@ -978,28 +979,39 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, al::optional<StereoEncoding { ERR("Failed to load layout file %s\n", config); ERR(" %s\n", err->c_str()); + return false; } else if(conf.NumSpeakers > MAX_OUTPUT_CHANNELS) + { ERR("Unsupported decoder speaker count %zu (max %d)\n", conf.NumSpeakers, MAX_OUTPUT_CHANNELS); + return false; + } else if(conf.ChanMask > Ambi3OrderMask) + { ERR("Unsupported decoder channel mask 0x%04x (max 0x%x)\n", conf.ChanMask, Ambi3OrderMask); - else - { - device->mXOverFreq = clampf(conf.XOverFreq, 100.0f, 1000.0f); - - decoder_store = std::make_unique<DecoderConfig<DualBand,MAX_OUTPUT_CHANNELS>>(); - decoder = MakeDecoderView(device, &conf, *decoder_store); - for(size_t i{0};i < decoder.mChannels.size();++i) - speakerdists[i] = conf.Speakers[i].Distance; + return false; } + + TRACE("Using %s decoder: \"%s\"\n", DevFmtChannelsString(device->FmtChans), + conf.Description.c_str()); + device->mXOverFreq = clampf(conf.XOverFreq, 100.0f, 1000.0f); + + decoder_store = std::make_unique<DecoderConfig<DualBand,MAX_OUTPUT_CHANNELS>>(); + decoder = MakeDecoderView(device, &conf, *decoder_store); + for(size_t i{0};i < decoder.mChannels.size();++i) + speakerdists[i] = conf.Speakers[i].Distance; + return true; }; + bool usingCustom{false}; if(layout) { if(auto decopt = device->configValue<std::string>("decoder", layout)) - load_config(decopt->c_str()); + usingCustom = load_config(decopt->c_str()); } + if(!usingCustom && device->FmtChans != DevFmtAmbi3D) + TRACE("Using built-in %s decoder\n", DevFmtChannelsString(device->FmtChans)); /* Enable the stablizer only for formats that have front-left, front- * right, and front-center outputs. @@ -1143,7 +1155,7 @@ void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context) slot->mWetBuffer.resize(count); - auto acnmap_begin = AmbiIndex::FromACN().begin(); + auto acnmap_begin = AmbiIndex::FromACN.begin(); auto iter = std::transform(acnmap_begin, acnmap_begin + count, slot->Wet.AmbiMap.begin(), [](const uint8_t &acn) noexcept -> BFChannelConfig { return BFChannelConfig{1.0f, acn}; }); diff --git a/alsoftrc.sample b/alsoftrc.sample index 2906cca4..42802184 100644 --- a/alsoftrc.sample +++ b/alsoftrc.sample @@ -56,10 +56,10 @@ # Sets the default output channel configuration. If left unspecified, one will # try to be detected from the system, with a fallback to stereo. The available # values are: mono, stereo, quad, surround51, surround61, surround71, -# surround3d71, ambi1, ambi2, ambi3. Note that the ambi* configurations output -# ambisonic channels of the given order (using ACN ordering and SN3D -# normalization by default), which need to be decoded to play correctly on -# speakers. +# surround714, surround3d71, ambi1, ambi2, ambi3. Note that the ambi* +# configurations output 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: @@ -343,6 +343,11 @@ # docs/ambdec.txt for a description of the file format. #surround71 = +## surround714: +# Decoder configuration file for 7.1.4 Surround channel output. See +# docs/ambdec.txt for a description of the file format. +#surround714 = + ## surround3d71: # Decoder configuration file for 3D7.1 Surround channel output. See # docs/ambdec.txt for a description of the file format. See also @@ -574,6 +579,12 @@ ## [wasapi] +## spatial-api: +# Specifies whether to use a Spatial Audio stream for playback. This may +# provide expanded capabilities for surround sound and with-height speaker +# configurations. Very experimental. +#spatial-api = false + ## allow-resampler: # Specifies whether to allow an extra resampler pass on the output. Enabling # this will allow the playback device to be set to a different sample rate @@ -638,6 +649,15 @@ ## [game_compat] +## default-error: (global) +# An error value returned by alGetError when there's no current context. The +# default value is AL_INVALID_OPERATION, which lets the caller know the +# operation could not be executed. Some applications may erroneously call +# alGetError without a current context and expect 0 (AL_NO_ERROR), however +# that may cause other applications to think earlier AL calls succeeded when +# they actually failed. +#default-error = 0xA004 + ## nfc-scale: (global) # A meters-per-unit distance scale applied to NFC filters. If a game doesn't # use real-world meters for in-game units, the filters may create a too-near diff --git a/appveyor.yml b/appveyor.yml index aa155af4..e86f7a30 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,8 +1,8 @@ version: 1.23.1.{build} environment: - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - GEN: "Visual Studio 15 2017" + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + GEN: "Visual Studio 16 2019" matrix: - ARCH: Win32 CFG: Release diff --git a/cmake/FindOpenSL.cmake b/cmake/FindOpenSL.cmake index 00428749..3df54d44 100644 --- a/cmake/FindOpenSL.cmake +++ b/cmake/FindOpenSL.cmake @@ -2,8 +2,9 @@ # Find the OpenSL libraries # # This module defines the following variables and targets: -# OPENSL_FOUND - True if OPENSL was found -# OpenSL::OpenSLES - The OpenSLES target +# OPENSL_FOUND - True if OPENSL was found +# OPENSL_INCLUDE_DIRS - The OpenSL include paths +# OPENSL_LIBRARIES - The OpenSL libraries to link # #============================================================================= @@ -53,11 +54,8 @@ find_package_handle_standard_args(OpenSL REQUIRED_VARS OPENSL_LIBRARY OPENSL_INC OPENSL_ANDROID_INCLUDE_DIR) if(OPENSL_FOUND) - add_library(OpenSL::OpenSLES UNKNOWN IMPORTED) - set_target_properties(OpenSL::OpenSLES PROPERTIES - IMPORTED_LOCATION ${OPENSL_LIBRARY} - INTERFACE_INCLUDE_DIRECTORIES ${OPENSL_INCLUDE_DIR} - INTERFACE_INCLUDE_DIRECTORIES ${OPENSL_ANDROID_INCLUDE_DIR}) + set(OPENSL_LIBRARIES ${OPENSL_LIBRARY}) + set(OPENSL_INCLUDE_DIRS ${OPENSL_INCLUDE_DIR} ${OPENSL_ANDROID_INCLUDE_DIR}) endif() mark_as_advanced(OPENSL_INCLUDE_DIR OPENSL_ANDROID_INCLUDE_DIR OPENSL_LIBRARY) diff --git a/common/albit.h b/common/albit.h index ad596208..82a4a00d 100644 --- a/common/albit.h +++ b/common/albit.h @@ -2,7 +2,9 @@ #define AL_BIT_H #include <cstdint> +#include <cstring> #include <limits> +#include <new> #include <type_traits> #if !defined(__GNUC__) && (defined(_WIN32) || defined(_WIN64)) #include <intrin.h> @@ -10,6 +12,16 @@ namespace al { +template<typename To, typename From> +std::enable_if_t<sizeof(To) == sizeof(From) && std::is_trivially_copyable_v<From> + && std::is_trivially_copyable_v<To>, +To> bit_cast(const From &src) noexcept +{ + alignas(To) char dst[sizeof(To)]; + std::memcpy(&dst[0], &src, sizeof(To)); + return *std::launder(reinterpret_cast<To*>(&dst[0])); +} + #ifdef __BYTE_ORDER__ enum class endian { little = __ORDER_LITTLE_ENDIAN__, diff --git a/common/albyte.h b/common/albyte.h deleted file mode 100644 index be586869..00000000 --- a/common/albyte.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef AL_BYTE_H -#define AL_BYTE_H - -#include <cstddef> -#include <cstdint> -#include <limits> -#include <type_traits> - -using uint = unsigned int; - -namespace al { - -using byte = unsigned char; - -} // namespace al - -#endif /* AL_BYTE_H */ diff --git a/common/alcomplex.cpp b/common/alcomplex.cpp index 4420a1bb..82a0c43c 100644 --- a/common/alcomplex.cpp +++ b/common/alcomplex.cpp @@ -4,6 +4,7 @@ #include "alcomplex.h" #include <algorithm> +#include <array> #include <cassert> #include <cmath> #include <cstddef> @@ -20,6 +21,7 @@ namespace { using ushort = unsigned short; using ushort2 = std::pair<ushort,ushort>; +using complex_d = std::complex<double>; constexpr size_t BitReverseCounter(size_t log2_size) noexcept { @@ -34,7 +36,7 @@ template<size_t N> struct BitReverser { static_assert(N <= sizeof(ushort)*8, "Too many bits for the bit-reversal table."); - ushort2 mData[BitReverseCounter(N)]{}; + std::array<ushort2,BitReverseCounter(N)> mData{}; constexpr BitReverser() { @@ -44,12 +46,13 @@ struct BitReverser { /* Bit-reversal permutation applied to a sequence of fftsize items. */ for(size_t idx{1u};idx < fftsize-1;++idx) { - size_t revidx{0u}, imask{idx}; - for(size_t i{0};i < N;++i) - { - revidx = (revidx<<1) | (imask&1); - imask >>= 1; - } + size_t revidx{idx}; + revidx = ((revidx&0xaaaaaaaa) >> 1) | ((revidx&0x55555555) << 1); + revidx = ((revidx&0xcccccccc) >> 2) | ((revidx&0x33333333) << 2); + revidx = ((revidx&0xf0f0f0f0) >> 4) | ((revidx&0x0f0f0f0f) << 4); + revidx = ((revidx&0xff00ff00) >> 8) | ((revidx&0x00ff00ff) << 8); + revidx = (revidx >> 16) | ((revidx&0x0000ffff) << 16); + revidx >>= 32-N; if(idx < revidx) { @@ -58,14 +61,13 @@ struct BitReverser { ++ret_i; } } - assert(ret_i == al::size(mData)); + assert(ret_i == std::size(mData)); } }; -/* These bit-reversal swap tables support up to 10-bit indices (1024 elements), - * which is the largest used by OpenAL Soft's filters and effects. Larger FFT - * requests, used by some utilities where performance is less important, will - * use a slower table-less path. +/* These bit-reversal swap tables support up to 11-bit indices (2048 elements), + * which is large enough for the filters and effects in OpenAL Soft. Larger FFT + * requests will use a slower table-less path. */ constexpr BitReverser<2> BitReverser2{}; constexpr BitReverser<3> BitReverser3{}; @@ -76,7 +78,8 @@ constexpr BitReverser<7> BitReverser7{}; constexpr BitReverser<8> BitReverser8{}; constexpr BitReverser<9> BitReverser9{}; constexpr BitReverser<10> BitReverser10{}; -constexpr std::array<al::span<const ushort2>,11> gBitReverses{{ +constexpr BitReverser<11> BitReverser11{}; +constexpr std::array<al::span<const ushort2>,12> gBitReverses{{ {}, {}, BitReverser2.mData, BitReverser3.mData, @@ -86,14 +89,29 @@ constexpr std::array<al::span<const ushort2>,11> gBitReverses{{ BitReverser7.mData, BitReverser8.mData, BitReverser9.mData, - BitReverser10.mData + BitReverser10.mData, + BitReverser11.mData +}}; + +/* Lookup table for std::polar(1, pi / (1<<index)); */ +template<typename T> +constexpr std::array<std::complex<T>,gBitReverses.size()-1> gArgAngle{{ + {static_cast<T>(-1.00000000000000000e+00), static_cast<T>(0.00000000000000000e+00)}, + {static_cast<T>( 0.00000000000000000e+00), static_cast<T>(1.00000000000000000e+00)}, + {static_cast<T>( 7.07106781186547524e-01), static_cast<T>(7.07106781186547524e-01)}, + {static_cast<T>( 9.23879532511286756e-01), static_cast<T>(3.82683432365089772e-01)}, + {static_cast<T>( 9.80785280403230449e-01), static_cast<T>(1.95090322016128268e-01)}, + {static_cast<T>( 9.95184726672196886e-01), static_cast<T>(9.80171403295606020e-02)}, + {static_cast<T>( 9.98795456205172393e-01), static_cast<T>(4.90676743274180143e-02)}, + {static_cast<T>( 9.99698818696204220e-01), static_cast<T>(2.45412285229122880e-02)}, + {static_cast<T>( 9.99924701839144541e-01), static_cast<T>(1.22715382857199261e-02)}, + {static_cast<T>( 9.99981175282601143e-01), static_cast<T>(6.13588464915447536e-03)}, + {static_cast<T>( 9.99995293809576172e-01), static_cast<T>(3.06795676296597627e-03)} }}; } // namespace -template<typename Real> -std::enable_if_t<std::is_floating_point<Real>::value> -complex_fft(const al::span<std::complex<Real>> buffer, const al::type_identity_t<Real> sign) +void complex_fft(const al::span<std::complex<double>> buffer, const double sign) { const size_t fftsize{buffer.size()}; /* Get the number of bits used for indexing. Simplifies bit-reversal and @@ -101,48 +119,82 @@ complex_fft(const al::span<std::complex<Real>> buffer, const al::type_identity_t */ const size_t log2_size{static_cast<size_t>(al::countr_zero(fftsize))}; - if(log2_size >= gBitReverses.size()) UNLIKELY + if(log2_size < gBitReverses.size()) LIKELY { - for(size_t idx{1u};idx < fftsize-1;++idx) + for(auto &rev : gBitReverses[log2_size]) + std::swap(buffer[rev.first], buffer[rev.second]); + + /* Iterative form of Danielson-Lanczos lemma */ + for(size_t i{0};i < log2_size;++i) { - size_t revidx{0u}, imask{idx}; - for(size_t i{0};i < log2_size;++i) + const size_t step2{1_uz << i}; + const size_t step{2_uz << i}; + /* The first iteration of the inner loop would have u=1, which we + * can simplify to remove a number of complex multiplies. + */ + for(size_t k{0};k < fftsize;k+=step) { - revidx = (revidx<<1) | (imask&1); - imask >>= 1; + const complex_d temp{buffer[k+step2]}; + buffer[k+step2] = buffer[k] - temp; + buffer[k] += temp; } - if(idx < revidx) - std::swap(buffer[idx], buffer[revidx]); + const complex_d w{gArgAngle<double>[i].real(), gArgAngle<double>[i].imag()*sign}; + complex_d u{w}; + for(size_t j{1};j < step2;j++) + { + for(size_t k{j};k < fftsize;k+=step) + { + const complex_d temp{buffer[k+step2] * u}; + buffer[k+step2] = buffer[k] - temp; + buffer[k] += temp; + } + u *= w; + } } } - else for(auto &rev : gBitReverses[log2_size]) - std::swap(buffer[rev.first], buffer[rev.second]); - - /* Iterative form of Danielson-Lanczos lemma */ - const Real pi{al::numbers::pi_v<Real> * sign}; - size_t step2{1u}; - for(size_t i{0};i < log2_size;++i) + else { - const Real arg{pi / static_cast<Real>(step2)}; + for(size_t idx{1u};idx < fftsize-1;++idx) + { + size_t revidx{idx}; + revidx = ((revidx&0xaaaaaaaa) >> 1) | ((revidx&0x55555555) << 1); + revidx = ((revidx&0xcccccccc) >> 2) | ((revidx&0x33333333) << 2); + revidx = ((revidx&0xf0f0f0f0) >> 4) | ((revidx&0x0f0f0f0f) << 4); + revidx = ((revidx&0xff00ff00) >> 8) | ((revidx&0x00ff00ff) << 8); + revidx = (revidx >> 16) | ((revidx&0x0000ffff) << 16); + revidx >>= 32-log2_size; + + if(idx < revidx) + std::swap(buffer[idx], buffer[revidx]); + } - /* TODO: Would std::polar(1.0, arg) be any better? */ - const std::complex<Real> w{std::cos(arg), std::sin(arg)}; - std::complex<Real> u{1.0, 0.0}; - const size_t step{step2 << 1}; - for(size_t j{0};j < step2;j++) + const double pi{al::numbers::pi * sign}; + for(size_t i{0};i < log2_size;++i) { - for(size_t k{j};k < fftsize;k+=step) + const size_t step2{1_uz << i}; + const size_t step{2_uz << i}; + for(size_t k{0};k < fftsize;k+=step) { - std::complex<Real> temp{buffer[k+step2] * u}; + const complex_d temp{buffer[k+step2]}; buffer[k+step2] = buffer[k] - temp; buffer[k] += temp; } - u *= w; + const double arg{pi / static_cast<double>(step2)}; + const complex_d w{std::polar(1.0, arg)}; + complex_d u{w}; + for(size_t j{1};j < step2;j++) + { + for(size_t k{j};k < fftsize;k+=step) + { + const complex_d temp{buffer[k+step2] * u}; + buffer[k+step2] = buffer[k] - temp; + buffer[k] += temp; + } + u *= w; + } } - - step2 <<= 1; } } @@ -165,7 +217,3 @@ void complex_hilbert(const al::span<std::complex<double>> buffer) forward_fft(buffer); } - - -template void complex_fft<>(const al::span<std::complex<float>> buffer, const float sign); -template void complex_fft<>(const al::span<std::complex<double>> buffer, const double sign); diff --git a/common/alcomplex.h b/common/alcomplex.h index 794c3526..f730ba9e 100644 --- a/common/alcomplex.h +++ b/common/alcomplex.h @@ -11,27 +11,23 @@ * FFT and 1 is inverse FFT. Applies the Discrete Fourier Transform (DFT) to * the data supplied in the buffer, which MUST BE power of two. */ -template<typename Real> -std::enable_if_t<std::is_floating_point<Real>::value> -complex_fft(const al::span<std::complex<Real>> buffer, const al::type_identity_t<Real> sign); +void complex_fft(const al::span<std::complex<double>> buffer, const double sign); /** * Calculate the frequency-domain response of the time-domain signal in the * provided buffer, which MUST BE power of two. */ -template<typename Real, size_t N> -std::enable_if_t<std::is_floating_point<Real>::value> -forward_fft(const al::span<std::complex<Real>,N> buffer) -{ complex_fft(buffer.subspan(0), -1); } +template<size_t N> +void forward_fft(const al::span<std::complex<double>,N> buffer) +{ complex_fft(al::span<std::complex<double>>{buffer}, -1.0); } /** * Calculate the time-domain signal of the frequency-domain response in the * provided buffer, which MUST BE power of two. */ -template<typename Real, size_t N> -std::enable_if_t<std::is_floating_point<Real>::value> -inverse_fft(const al::span<std::complex<Real>,N> buffer) -{ complex_fft(buffer.subspan(0), 1); } +template<size_t N> +void inverse_fft(const al::span<std::complex<double>,N> buffer) +{ complex_fft(al::span<std::complex<double>>{buffer}, +1.0); } /** * Calculate the complex helical sequence (discrete-time analytical signal) of diff --git a/common/aldeque.h b/common/aldeque.h deleted file mode 100644 index 3f99bf00..00000000 --- a/common/aldeque.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef ALDEQUE_H -#define ALDEQUE_H - -#include <deque> - -#include "almalloc.h" - - -namespace al { - -template<typename T> -using deque = std::deque<T, al::allocator<T>>; - -} // namespace al - -#endif /* ALDEQUE_H */ diff --git a/common/almalloc.h b/common/almalloc.h index a795fc3b..873473ca 100644 --- a/common/almalloc.h +++ b/common/almalloc.h @@ -117,7 +117,9 @@ constexpr T *to_address(T *p) noexcept template<typename T> constexpr auto to_address(const T &p) noexcept -{ return to_address(p.operator->()); } +{ + return ::al::to_address(p.operator->()); +} template<typename T, typename ...Args> @@ -125,71 +127,6 @@ constexpr T* construct_at(T *ptr, Args&& ...args) noexcept(std::is_nothrow_constructible<T, Args...>::value) { return ::new(static_cast<void*>(ptr)) T{std::forward<Args>(args)...}; } -/* At least VS 2015 complains that 'ptr' is unused when the given type's - * destructor is trivial (a no-op). So disable that warning for this call. - */ -DIAGNOSTIC_PUSH -msc_pragma(warning(disable : 4100)) -template<typename T> -constexpr std::enable_if_t<!std::is_array<T>::value> -destroy_at(T *ptr) noexcept(std::is_nothrow_destructible<T>::value) -{ ptr->~T(); } -DIAGNOSTIC_POP -template<typename T> -constexpr std::enable_if_t<std::is_array<T>::value> -destroy_at(T *ptr) noexcept(std::is_nothrow_destructible<std::remove_all_extents_t<T>>::value) -{ - for(auto &elem : *ptr) - al::destroy_at(std::addressof(elem)); -} - -template<typename T> -constexpr void destroy(T first, T end) noexcept(noexcept(al::destroy_at(std::addressof(*first)))) -{ - while(first != end) - { - al::destroy_at(std::addressof(*first)); - ++first; - } -} - -template<typename T, typename N> -constexpr std::enable_if_t<std::is_integral<N>::value,T> -destroy_n(T first, N count) noexcept(noexcept(al::destroy_at(std::addressof(*first)))) -{ - if(count != 0) - { - do { - al::destroy_at(std::addressof(*first)); - ++first; - } while(--count); - } - return first; -} - - -template<typename T, typename N> -inline std::enable_if_t<std::is_integral<N>::value, -T> uninitialized_default_construct_n(T first, N count) -{ - using ValueT = typename std::iterator_traits<T>::value_type; - T current{first}; - if(count != 0) - { - try { - do { - ::new(static_cast<void*>(std::addressof(*current))) ValueT; - ++current; - } while(--count); - } - catch(...) { - al::destroy(first, current); - throw; - } - } - return current; -} - /* Storage for flexible array data. This is trivially destructible if type T is * trivially destructible. @@ -209,7 +146,7 @@ struct FlexArrayStorage { } FlexArrayStorage(size_t size) : mSize{size} - { al::uninitialized_default_construct_n(mArray, mSize); } + { std::uninitialized_default_construct_n(mArray, mSize); } ~FlexArrayStorage() = default; FlexArrayStorage(const FlexArrayStorage&) = delete; @@ -231,8 +168,8 @@ struct FlexArrayStorage<T,alignment,false> { } FlexArrayStorage(size_t size) : mSize{size} - { al::uninitialized_default_construct_n(mArray, mSize); } - ~FlexArrayStorage() { al::destroy_n(mArray, mSize); } + { std::uninitialized_default_construct_n(mArray, mSize); } + ~FlexArrayStorage() { std::destroy_n(mArray, mSize); } FlexArrayStorage(const FlexArrayStorage&) = delete; FlexArrayStorage& operator=(const FlexArrayStorage&) = delete; diff --git a/common/alnumbers.h b/common/alnumbers.h index 37a55410..e92d7b87 100644 --- a/common/alnumbers.h +++ b/common/alnumbers.h @@ -13,21 +13,21 @@ namespace detail_ { } // detail_ template<typename T> -static constexpr auto pi_v = detail_::as_fp<T>(3.141592653589793238462643383279502884L); +inline constexpr auto pi_v = detail_::as_fp<T>(3.141592653589793238462643383279502884L); template<typename T> -static constexpr auto inv_pi_v = detail_::as_fp<T>(0.318309886183790671537767526745028724L); +inline constexpr auto inv_pi_v = detail_::as_fp<T>(0.318309886183790671537767526745028724L); template<typename T> -static constexpr auto sqrt2_v = detail_::as_fp<T>(1.414213562373095048801688724209698079L); +inline constexpr auto sqrt2_v = detail_::as_fp<T>(1.414213562373095048801688724209698079L); template<typename T> -static constexpr auto sqrt3_v = detail_::as_fp<T>(1.732050807568877293527446341505872367L); +inline constexpr auto sqrt3_v = detail_::as_fp<T>(1.732050807568877293527446341505872367L); -static constexpr auto pi = pi_v<double>; -static constexpr auto inv_pi = inv_pi_v<double>; -static constexpr auto sqrt2 = sqrt2_v<double>; -static constexpr auto sqrt3 = sqrt3_v<double>; +inline constexpr auto pi = pi_v<double>; +inline constexpr auto inv_pi = inv_pi_v<double>; +inline constexpr auto sqrt2 = sqrt2_v<double>; +inline constexpr auto sqrt3 = sqrt3_v<double>; } // namespace numbers diff --git a/common/alnumeric.h b/common/alnumeric.h index d6919e40..6281b012 100644 --- a/common/alnumeric.h +++ b/common/alnumeric.h @@ -5,6 +5,7 @@ #include <cmath> #include <cstddef> #include <cstdint> +#include <type_traits> #ifdef HAVE_INTRIN_H #include <intrin.h> #endif @@ -12,12 +13,18 @@ #include <xmmintrin.h> #endif +#include "albit.h" #include "altraits.h" #include "opthelpers.h" -inline constexpr int64_t operator "" _i64(unsigned long long int n) noexcept { return static_cast<int64_t>(n); } -inline constexpr uint64_t operator "" _u64(unsigned long long int n) noexcept { return static_cast<uint64_t>(n); } +constexpr auto operator "" _i64(unsigned long long n) noexcept { return static_cast<int64_t>(n); } +constexpr auto operator "" _u64(unsigned long long n) noexcept { return static_cast<uint64_t>(n); } + +constexpr auto operator "" _z(unsigned long long n) noexcept +{ return static_cast<std::make_signed_t<size_t>>(n); } +constexpr auto operator "" _uz(unsigned long long n) noexcept { return static_cast<size_t>(n); } +constexpr auto operator "" _zu(unsigned long long n) noexcept { return static_cast<size_t>(n); } constexpr inline float minf(float a, float b) noexcept @@ -82,6 +89,9 @@ constexpr inline float cubic(float val1, float val2, float val3, float val4, flo return val1*a0 + val2*a1 + val3*a2 + val4*a3; } +constexpr inline double lerpd(double val1, double val2, double mu) noexcept +{ return val1 + (val2-val1)*mu; } + /** Find the next power-of-2 for non-power-of-2 numbers. */ inline uint32_t NextPowerOf2(uint32_t value) noexcept @@ -159,21 +169,16 @@ inline int float2int(float f) noexcept #elif (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP == 0) \ || ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) \ && !defined(__SSE_MATH__)) - int sign, shift, mant; - union { - float f; - int i; - } conv; + const int conv_i{al::bit_cast<int>(f)}; - conv.f = f; - sign = (conv.i>>31) | 1; - shift = ((conv.i>>23)&0xff) - (127+23); + const int sign{(conv_i>>31) | 1}; + const int shift{((conv_i>>23)&0xff) - (127+23)}; /* Over/underflow */ if(shift >= 31 || shift < -23) UNLIKELY return 0; - mant = (conv.i&0x7fffff) | 0x800000; + const int mant{(conv_i&0x7fffff) | 0x800000}; if(shift < 0) LIKELY return (mant >> -shift) * sign; return (mant << shift) * sign; @@ -195,25 +200,19 @@ inline int double2int(double d) noexcept #elif (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP < 2) \ || ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) \ && !defined(__SSE2_MATH__)) - int sign, shift; - int64_t mant; - union { - double d; - int64_t i64; - } conv; + const int64_t conv_i64{al::bit_cast<int64_t>(d)}; - conv.d = d; - sign = (conv.i64 >> 63) | 1; - shift = ((conv.i64 >> 52) & 0x7ff) - (1023 + 52); + const int sign{static_cast<int>(conv_i64 >> 63) | 1}; + const int shift{(static_cast<int>(conv_i64 >> 52) & 0x7ff) - (1023 + 52)}; /* Over/underflow */ if(shift >= 63 || shift < -52) UNLIKELY return 0; - mant = (conv.i64 & 0xfffffffffffff_i64) | 0x10000000000000_i64; + const int64_t mant{(conv_i64 & 0xfffffffffffff_i64) | 0x10000000000000_i64}; if(shift < 0) LIKELY - return (int)(mant >> -shift) * sign; - return (int)(mant << shift) * sign; + return static_cast<int>(mant >> -shift) * sign; + return static_cast<int>(mant << shift) * sign; #else @@ -246,19 +245,14 @@ inline float fast_roundf(float f) noexcept /* Integral limit, where sub-integral precision is not available for * floats. */ - static const float ilim[2]{ + static constexpr float ilim[2]{ 8388608.0f /* 0x1.0p+23 */, -8388608.0f /* -0x1.0p+23 */ }; - unsigned int sign, expo; - union { - float f; - unsigned int i; - } conv; + const unsigned int conv_i{al::bit_cast<unsigned int>(f)}; - conv.f = f; - sign = (conv.i>>31)&0x01; - expo = (conv.i>>23)&0xff; + const unsigned int sign{(conv_i>>31)&0x01}; + const unsigned int expo{(conv_i>>23)&0xff}; if(expo >= 150/*+23*/) UNLIKELY { @@ -283,12 +277,6 @@ inline float fast_roundf(float f) noexcept } -template<typename T> -constexpr const T& clamp(const T& value, const T& min_value, const T& max_value) noexcept -{ - return std::min(std::max(value, min_value), max_value); -} - // Converts level (mB) to gain. inline float level_mb_to_gain(float x) { diff --git a/common/aloptional.h b/common/aloptional.h deleted file mode 100644 index 6de16799..00000000 --- a/common/aloptional.h +++ /dev/null @@ -1,353 +0,0 @@ -#ifndef AL_OPTIONAL_H -#define AL_OPTIONAL_H - -#include <initializer_list> -#include <type_traits> -#include <utility> - -#include "almalloc.h" - -namespace al { - -struct nullopt_t { }; -struct in_place_t { }; - -constexpr nullopt_t nullopt{}; -constexpr in_place_t in_place{}; - -#define NOEXCEPT_AS(...) noexcept(noexcept(__VA_ARGS__)) - -namespace detail_ { -/* Base storage struct for an optional. Defines a trivial destructor, for types - * that can be trivially destructed. - */ -template<typename T, bool = std::is_trivially_destructible<T>::value> -struct optstore_base { - bool mHasValue{false}; - union { - char mDummy{}; - T mValue; - }; - - constexpr optstore_base() noexcept { } - template<typename ...Args> - constexpr explicit optstore_base(in_place_t, Args&& ...args) - noexcept(std::is_nothrow_constructible<T, Args...>::value) - : mHasValue{true}, mValue{std::forward<Args>(args)...} - { } - ~optstore_base() = default; -}; - -/* Specialization needing a non-trivial destructor. */ -template<typename T> -struct optstore_base<T, false> { - bool mHasValue{false}; - union { - char mDummy{}; - T mValue; - }; - - constexpr optstore_base() noexcept { } - template<typename ...Args> - constexpr explicit optstore_base(in_place_t, Args&& ...args) - noexcept(std::is_nothrow_constructible<T, Args...>::value) - : mHasValue{true}, mValue{std::forward<Args>(args)...} - { } - ~optstore_base() { if(mHasValue) al::destroy_at(std::addressof(mValue)); } -}; - -/* Next level of storage, which defines helpers to construct and destruct the - * stored object. - */ -template<typename T> -struct optstore_helper : public optstore_base<T> { - using optstore_base<T>::optstore_base; - - template<typename... Args> - constexpr void construct(Args&& ...args) noexcept(std::is_nothrow_constructible<T, Args...>::value) - { - al::construct_at(std::addressof(this->mValue), std::forward<Args>(args)...); - this->mHasValue = true; - } - - constexpr void reset() noexcept - { - if(this->mHasValue) - al::destroy_at(std::addressof(this->mValue)); - this->mHasValue = false; - } - - constexpr void assign(const optstore_helper &rhs) - noexcept(std::is_nothrow_copy_constructible<T>::value - && std::is_nothrow_copy_assignable<T>::value) - { - if(!rhs.mHasValue) - this->reset(); - else if(this->mHasValue) - this->mValue = rhs.mValue; - else - this->construct(rhs.mValue); - } - - constexpr void assign(optstore_helper&& rhs) - noexcept(std::is_nothrow_move_constructible<T>::value - && std::is_nothrow_move_assignable<T>::value) - { - if(!rhs.mHasValue) - this->reset(); - else if(this->mHasValue) - this->mValue = std::move(rhs.mValue); - else - this->construct(std::move(rhs.mValue)); - } -}; - -/* Define copy and move constructors and assignment operators, which may or may - * not be trivial. - */ -template<typename T, bool trivial_copy = std::is_trivially_copy_constructible<T>::value, - bool trivial_move = std::is_trivially_move_constructible<T>::value, - /* Trivial assignment is dependent on trivial construction+destruction. */ - bool = trivial_copy && std::is_trivially_copy_assignable<T>::value - && std::is_trivially_destructible<T>::value, - bool = trivial_move && std::is_trivially_move_assignable<T>::value - && std::is_trivially_destructible<T>::value> -struct optional_storage; - -/* Some versions of GCC have issues with 'this' in the following noexcept(...) - * statements, so this macro is a workaround. - */ -#define _this std::declval<optional_storage*>() - -/* Completely trivial. */ -template<typename T> -struct optional_storage<T, true, true, true, true> : public optstore_helper<T> { - using optstore_helper<T>::optstore_helper; - constexpr optional_storage() noexcept = default; - constexpr optional_storage(const optional_storage&) = default; - constexpr optional_storage(optional_storage&&) = default; - constexpr optional_storage& operator=(const optional_storage&) = default; - constexpr optional_storage& operator=(optional_storage&&) = default; -}; - -/* Non-trivial move assignment. */ -template<typename T> -struct optional_storage<T, true, true, true, false> : public optstore_helper<T> { - using optstore_helper<T>::optstore_helper; - constexpr optional_storage() noexcept = default; - constexpr optional_storage(const optional_storage&) = default; - constexpr optional_storage(optional_storage&&) = default; - constexpr optional_storage& operator=(const optional_storage&) = default; - constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs))) - { this->assign(std::move(rhs)); return *this; } -}; - -/* Non-trivial move construction. */ -template<typename T> -struct optional_storage<T, true, false, true, false> : public optstore_helper<T> { - using optstore_helper<T>::optstore_helper; - constexpr optional_storage() noexcept = default; - constexpr optional_storage(const optional_storage&) = default; - constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue))) - { if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); } - constexpr optional_storage& operator=(const optional_storage&) = default; - constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs))) - { this->assign(std::move(rhs)); return *this; } -}; - -/* Non-trivial copy assignment. */ -template<typename T> -struct optional_storage<T, true, true, false, true> : public optstore_helper<T> { - using optstore_helper<T>::optstore_helper; - constexpr optional_storage() noexcept = default; - constexpr optional_storage(const optional_storage&) = default; - constexpr optional_storage(optional_storage&&) = default; - constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs)) - { this->assign(rhs); return *this; } - constexpr optional_storage& operator=(optional_storage&&) = default; -}; - -/* Non-trivial copy construction. */ -template<typename T> -struct optional_storage<T, false, true, false, true> : public optstore_helper<T> { - using optstore_helper<T>::optstore_helper; - constexpr optional_storage() noexcept = default; - constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue)) - { if(rhs.mHasValue) this->construct(rhs.mValue); } - constexpr optional_storage(optional_storage&&) = default; - constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs)) - { this->assign(rhs); return *this; } - constexpr optional_storage& operator=(optional_storage&&) = default; -}; - -/* Non-trivial assignment. */ -template<typename T> -struct optional_storage<T, true, true, false, false> : public optstore_helper<T> { - using optstore_helper<T>::optstore_helper; - constexpr optional_storage() noexcept = default; - constexpr optional_storage(const optional_storage&) = default; - constexpr optional_storage(optional_storage&&) = default; - constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs)) - { this->assign(rhs); return *this; } - constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs))) - { this->assign(std::move(rhs)); return *this; } -}; - -/* Non-trivial assignment, non-trivial move construction. */ -template<typename T> -struct optional_storage<T, true, false, false, false> : public optstore_helper<T> { - using optstore_helper<T>::optstore_helper; - constexpr optional_storage() noexcept = default; - constexpr optional_storage(const optional_storage&) = default; - constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue))) - { if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); } - constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs)) - { this->assign(rhs); return *this; } - constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs))) - { this->assign(std::move(rhs)); return *this; } -}; - -/* Non-trivial assignment, non-trivial copy construction. */ -template<typename T> -struct optional_storage<T, false, true, false, false> : public optstore_helper<T> { - using optstore_helper<T>::optstore_helper; - constexpr optional_storage() noexcept = default; - constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue)) - { if(rhs.mHasValue) this->construct(rhs.mValue); } - constexpr optional_storage(optional_storage&&) = default; - constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs)) - { this->assign(rhs); return *this; } - constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs))) - { this->assign(std::move(rhs)); return *this; } -}; - -/* Completely non-trivial. */ -template<typename T> -struct optional_storage<T, false, false, false, false> : public optstore_helper<T> { - using optstore_helper<T>::optstore_helper; - constexpr optional_storage() noexcept = default; - constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue)) - { if(rhs.mHasValue) this->construct(rhs.mValue); } - constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue))) - { if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); } - constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs)) - { this->assign(rhs); return *this; } - constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs))) - { this->assign(std::move(rhs)); return *this; } -}; - -#undef _this - -} // namespace detail_ - -#define REQUIRES(...) std::enable_if_t<(__VA_ARGS__),bool> = true - -template<typename T> -class optional { - using storage_t = detail_::optional_storage<T>; - - storage_t mStore{}; - -public: - using value_type = T; - - constexpr optional() = default; - constexpr optional(const optional&) = default; - constexpr optional(optional&&) = default; - constexpr optional(nullopt_t) noexcept { } - template<typename ...Args> - constexpr explicit optional(in_place_t, Args&& ...args) - NOEXCEPT_AS(storage_t{al::in_place, std::forward<Args>(args)...}) - : mStore{al::in_place, std::forward<Args>(args)...} - { } - template<typename U, REQUIRES(std::is_constructible<T, U&&>::value - && !std::is_same<std::decay_t<U>, al::in_place_t>::value - && !std::is_same<std::decay_t<U>, optional<T>>::value - && std::is_convertible<U&&, T>::value)> - constexpr optional(U&& rhs) NOEXCEPT_AS(storage_t{al::in_place, std::forward<U>(rhs)}) - : mStore{al::in_place, std::forward<U>(rhs)} - { } - template<typename U, REQUIRES(std::is_constructible<T, U&&>::value - && !std::is_same<std::decay_t<U>, al::in_place_t>::value - && !std::is_same<std::decay_t<U>, optional<T>>::value - && !std::is_convertible<U&&, T>::value)> - constexpr explicit optional(U&& rhs) NOEXCEPT_AS(storage_t{al::in_place, std::forward<U>(rhs)}) - : mStore{al::in_place, std::forward<U>(rhs)} - { } - ~optional() = default; - - constexpr optional& operator=(const optional&) = default; - constexpr optional& operator=(optional&&) = default; - constexpr optional& operator=(nullopt_t) noexcept { mStore.reset(); return *this; } - template<typename U=T> - constexpr std::enable_if_t<std::is_constructible<T, U>::value - && std::is_assignable<T&, U>::value - && !std::is_same<std::decay_t<U>, optional<T>>::value - && (!std::is_same<std::decay_t<U>, T>::value || !std::is_scalar<U>::value), - optional&> operator=(U&& rhs) - { - if(mStore.mHasValue) - mStore.mValue = std::forward<U>(rhs); - else - mStore.construct(std::forward<U>(rhs)); - return *this; - } - - constexpr const T* operator->() const { return std::addressof(mStore.mValue); } - constexpr T* operator->() { return std::addressof(mStore.mValue); } - constexpr const T& operator*() const& { return mStore.mValue; } - constexpr T& operator*() & { return mStore.mValue; } - constexpr const T&& operator*() const&& { return std::move(mStore.mValue); } - constexpr T&& operator*() && { return std::move(mStore.mValue); } - - constexpr explicit operator bool() const noexcept { return mStore.mHasValue; } - constexpr bool has_value() const noexcept { return mStore.mHasValue; } - - constexpr T& value() & { return mStore.mValue; } - constexpr const T& value() const& { return mStore.mValue; } - constexpr T&& value() && { return std::move(mStore.mValue); } - constexpr const T&& value() const&& { return std::move(mStore.mValue); } - - template<typename U> - constexpr T value_or(U&& defval) const& - { return bool(*this) ? **this : static_cast<T>(std::forward<U>(defval)); } - template<typename U> - constexpr T value_or(U&& defval) && - { return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(defval)); } - - template<typename ...Args> - constexpr T& emplace(Args&& ...args) - { - mStore.reset(); - mStore.construct(std::forward<Args>(args)...); - return mStore.mValue; - } - template<typename U, typename ...Args> - constexpr std::enable_if_t<std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value, - T&> emplace(std::initializer_list<U> il, Args&& ...args) - { - mStore.reset(); - mStore.construct(il, std::forward<Args>(args)...); - return mStore.mValue; - } - - constexpr void reset() noexcept { mStore.reset(); } -}; - -template<typename T> -constexpr optional<std::decay_t<T>> make_optional(T&& arg) -{ return optional<std::decay_t<T>>{in_place, std::forward<T>(arg)}; } - -template<typename T, typename... Args> -constexpr optional<T> make_optional(Args&& ...args) -{ return optional<T>{in_place, std::forward<Args>(args)...}; } - -template<typename T, typename U, typename... Args> -constexpr optional<T> make_optional(std::initializer_list<U> il, Args&& ...args) -{ return optional<T>{in_place, il, std::forward<Args>(args)...}; } - -#undef REQUIRES -#undef NOEXCEPT_AS -} // namespace al - -#endif /* AL_OPTIONAL_H */ diff --git a/common/threads.cpp b/common/alsem.cpp index 19a6bbf0..6a92b35c 100644 --- a/common/threads.cpp +++ b/common/alsem.cpp @@ -20,11 +20,12 @@ #include "config.h" -#include "opthelpers.h" -#include "threads.h" +#include "alsem.h" #include <system_error> +#include "opthelpers.h" + #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN @@ -32,37 +33,6 @@ #include <limits> -void althrd_setname(const char *name) -{ -#if defined(_MSC_VER) && !defined(_M_ARM) - -#define MS_VC_EXCEPTION 0x406D1388 -#pragma pack(push,8) - struct { - DWORD dwType; // Must be 0x1000. - LPCSTR szName; // Pointer to name (in user addr space). - DWORD dwThreadID; // Thread ID (-1=caller thread). - DWORD dwFlags; // Reserved for future use, must be zero. - } info; -#pragma pack(pop) - info.dwType = 0x1000; - info.szName = name; - info.dwThreadID = ~DWORD{0}; - info.dwFlags = 0; - - __try { - RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info); - } - __except(EXCEPTION_CONTINUE_EXECUTION) { - } -#undef MS_VC_EXCEPTION - -#else - - (void)name; -#endif -} - namespace al { semaphore::semaphore(unsigned int initial) @@ -93,49 +63,8 @@ bool semaphore::try_wait() noexcept #else -#include <pthread.h> -#ifdef HAVE_PTHREAD_NP_H -#include <pthread_np.h> -#endif -#include <tuple> - -namespace { - -using setname_t1 = int(*)(const char*); -using setname_t2 = int(*)(pthread_t, const char*); -using setname_t3 = void(*)(pthread_t, const char*); -using setname_t4 = int(*)(pthread_t, const char*, void*); - -void setname_caller(setname_t1 func, const char *name) -{ func(name); } - -void setname_caller(setname_t2 func, const char *name) -{ func(pthread_self(), name); } - -void setname_caller(setname_t3 func, const char *name) -{ func(pthread_self(), name); } - -void setname_caller(setname_t4 func, const char *name) -{ func(pthread_self(), "%s", static_cast<void*>(const_cast<char*>(name))); } - -} // namespace - -void althrd_setname(const char *name) -{ -#if defined(HAVE_PTHREAD_SET_NAME_NP) - setname_caller(pthread_set_name_np, name); -#elif defined(HAVE_PTHREAD_SETNAME_NP) - setname_caller(pthread_setname_np, name); -#endif - /* Avoid unused function/parameter warnings. */ - std::ignore = name; - std::ignore = static_cast<void(*)(setname_t1,const char*)>(&setname_caller); - std::ignore = static_cast<void(*)(setname_t2,const char*)>(&setname_caller); - std::ignore = static_cast<void(*)(setname_t3,const char*)>(&setname_caller); - std::ignore = static_cast<void(*)(setname_t4,const char*)>(&setname_caller); -} - -#ifdef __APPLE__ +/* Do not try using libdispatch on systems where it is absent. */ +#if defined(AL_APPLE_HAVE_DISPATCH) namespace al { diff --git a/common/threads.h b/common/alsem.h index 1cdb5d8f..9f72d1c6 100644 --- a/common/threads.h +++ b/common/alsem.h @@ -1,30 +1,25 @@ -#ifndef AL_THREADS_H -#define AL_THREADS_H - -#if defined(__GNUC__) && defined(__i386__) -/* force_align_arg_pointer is required for proper function arguments aligning - * when SSE code is used. Some systems (Windows, QNX) do not guarantee our - * thread functions will be properly aligned on the stack, even though GCC may - * generate code with the assumption that it is. */ -#define FORCE_ALIGN __attribute__((force_align_arg_pointer)) -#else -#define FORCE_ALIGN -#endif +#ifndef COMMON_ALSEM_H +#define COMMON_ALSEM_H #if defined(__APPLE__) +#include <AvailabilityMacros.h> +#include <TargetConditionals.h> +#if (((MAC_OS_X_VERSION_MIN_REQUIRED > 1050) && !defined(__ppc__)) || TARGET_OS_IOS || TARGET_OS_TV) #include <dispatch/dispatch.h> +#define AL_APPLE_HAVE_DISPATCH 1 +#else +#include <semaphore.h> /* Fallback option for Apple without a working libdispatch */ +#endif #elif !defined(_WIN32) #include <semaphore.h> #endif -void althrd_setname(const char *name); - namespace al { class semaphore { #ifdef _WIN32 using native_type = void*; -#elif defined(__APPLE__) +#elif defined(AL_APPLE_HAVE_DISPATCH) using native_type = dispatch_semaphore_t; #else using native_type = sem_t; @@ -45,4 +40,4 @@ public: } // namespace al -#endif /* AL_THREADS_H */ +#endif /* COMMON_ALSEM_H */ diff --git a/common/alspan.h b/common/alspan.h index 1d6cdfe5..341ce7c8 100644 --- a/common/alspan.h +++ b/common/alspan.h @@ -12,41 +12,12 @@ namespace al { -template<typename T> -constexpr auto size(const T &cont) noexcept(noexcept(cont.size())) -> decltype(cont.size()) -{ return cont.size(); } - -template<typename T, size_t N> -constexpr size_t size(const T (&)[N]) noexcept -{ return N; } - - -template<typename T> -constexpr auto data(T &cont) noexcept(noexcept(cont.data())) -> decltype(cont.data()) -{ return cont.data(); } - -template<typename T> -constexpr auto data(const T &cont) noexcept(noexcept(cont.data())) -> decltype(cont.data()) -{ return cont.data(); } - -template<typename T, size_t N> -constexpr T* data(T (&arr)[N]) noexcept -{ return arr; } - -template<typename T> -constexpr const T* data(std::initializer_list<T> list) noexcept -{ return list.begin(); } - - constexpr size_t dynamic_extent{static_cast<size_t>(-1)}; template<typename T, size_t E=dynamic_extent> class span; namespace detail_ { - template<typename... Ts> - using void_t = void; - template<typename T> struct is_span_ : std::false_type { }; template<typename T, size_t E> @@ -65,16 +36,19 @@ namespace detail_ { constexpr bool has_size_and_data = false; template<typename T> constexpr bool has_size_and_data<T, - void_t<decltype(al::size(std::declval<T>())), decltype(al::data(std::declval<T>()))>> + std::void_t<decltype(std::size(std::declval<T>())),decltype(std::data(std::declval<T>()))>> = true; + template<typename C> + constexpr bool is_valid_container_type = !is_span_v<C> && !is_std_array_v<C> + && !std::is_array<C>::value && has_size_and_data<C>; + template<typename T, typename U> constexpr bool is_array_compatible = std::is_convertible<T(*)[],U(*)[]>::value; template<typename C, typename T> - constexpr bool is_valid_container = !is_span_v<C> && !is_std_array_v<C> - && !std::is_array<C>::value && has_size_and_data<C> - && is_array_compatible<std::remove_pointer_t<decltype(al::data(std::declval<C&>()))>,T>; + constexpr bool is_valid_container = is_valid_container_type<C> + && is_array_compatible<std::remove_pointer_t<decltype(std::data(std::declval<C&>()))>,T>; } // namespace detail_ #define REQUIRES(...) std::enable_if_t<(__VA_ARGS__),bool> = true @@ -102,30 +76,33 @@ public: template<bool is0=(extent == 0), REQUIRES(is0)> constexpr span() noexcept { } template<typename U> - constexpr explicit span(U iter, index_type) : mData{to_address(iter)} { } + constexpr explicit span(U iter, index_type) : mData{::al::to_address(iter)} { } template<typename U, typename V, REQUIRES(!std::is_convertible<V,size_t>::value)> - constexpr explicit span(U first, V) : mData{to_address(first)} { } + constexpr explicit span(U first, V) : mData{::al::to_address(first)} + {} constexpr span(type_identity_t<element_type> (&arr)[E]) noexcept - : span{al::data(arr), al::size(arr)} + : span{std::data(arr), std::size(arr)} + { } + constexpr span(std::array<value_type,E> &arr) noexcept + : span{std::data(arr), std::size(arr)} { } - constexpr span(std::array<value_type,E> &arr) noexcept : span{al::data(arr), al::size(arr)} { } template<typename U=T, REQUIRES(std::is_const<U>::value)> constexpr span(const std::array<value_type,E> &arr) noexcept - : span{al::data(arr), al::size(arr)} + : span{std::data(arr), std::size(arr)} { } template<typename U, REQUIRES(detail_::is_valid_container<U, element_type>)> - constexpr explicit span(U&& cont) : span{al::data(cont), al::size(cont)} { } + constexpr explicit span(U&& cont) : span{std::data(cont), std::size(cont)} { } template<typename U, index_type N, REQUIRES(!std::is_same<element_type,U>::value && detail_::is_array_compatible<U,element_type> && N == dynamic_extent)> constexpr explicit span(const span<U,N> &span_) noexcept - : span{al::data(span_), al::size(span_)} + : span{std::data(span_), std::size(span_)} { } template<typename U, index_type N, REQUIRES(!std::is_same<element_type,U>::value && detail_::is_array_compatible<U,element_type> && N == extent)> - constexpr span(const span<U,N> &span_) noexcept : span{al::data(span_), al::size(span_)} { } + constexpr span(const span<U,N> &span_) noexcept : span{std::data(span_), std::size(span_)} { } constexpr span(const span&) noexcept = default; constexpr span& operator=(const span &rhs) noexcept = default; @@ -215,30 +192,31 @@ public: constexpr span() noexcept = default; template<typename U> - constexpr span(U iter, index_type count) - : mData{to_address(iter)}, mDataEnd{to_address(iter)+count} + constexpr span(U iter, index_type count) : mData{::al::to_address(iter)}, mDataEnd{::al::to_address(iter) + count} { } template<typename U, typename V, REQUIRES(!std::is_convertible<V,size_t>::value)> - constexpr span(U first, V last) : span{to_address(first), static_cast<size_t>(last-first)} + constexpr span(U first, V last) : span{::al::to_address(first), static_cast<size_t>(last - first)} { } template<size_t N> constexpr span(type_identity_t<element_type> (&arr)[N]) noexcept - : span{al::data(arr), al::size(arr)} + : span{std::data(arr), std::size(arr)} { } template<size_t N> - constexpr span(std::array<value_type,N> &arr) noexcept : span{al::data(arr), al::size(arr)} { } + constexpr span(std::array<value_type,N> &arr) noexcept + : span{std::data(arr), std::size(arr)} + { } template<size_t N, typename U=T, REQUIRES(std::is_const<U>::value)> constexpr span(const std::array<value_type,N> &arr) noexcept - : span{al::data(arr), al::size(arr)} + : span{std::data(arr), std::size(arr)} { } template<typename U, REQUIRES(detail_::is_valid_container<U, element_type>)> - constexpr span(U&& cont) : span{al::data(cont), al::size(cont)} { } + constexpr span(U&& cont) : span{std::data(cont), std::size(cont)} { } template<typename U, size_t N, REQUIRES((!std::is_same<element_type,U>::value || extent != N) && detail_::is_array_compatible<U,element_type>)> - constexpr span(const span<U,N> &span_) noexcept : span{al::data(span_), al::size(span_)} { } + constexpr span(const span<U,N> &span_) noexcept : span{std::data(span_), std::size(span_)} { } constexpr span(const span&) noexcept = default; constexpr span& operator=(const span &rhs) noexcept = default; @@ -322,30 +300,21 @@ constexpr inline auto span<T,E>::subspan(size_t offset, size_t count) const span<element_type>{mData+offset, mData+offset+count}; } -/* Helpers to deal with the lack of user-defined deduction guides (C++17). */ -template<typename T, typename U> -constexpr auto as_span(T ptr, U count_or_end) -{ - using value_type = typename std::pointer_traits<T>::element_type; - return span<value_type>{ptr, count_or_end}; -} -template<typename T, size_t N> -constexpr auto as_span(T (&arr)[N]) noexcept { return span<T,N>{al::data(arr), al::size(arr)}; } -template<typename T, size_t N> -constexpr auto as_span(std::array<T,N> &arr) noexcept -{ return span<T,N>{al::data(arr), al::size(arr)}; } -template<typename T, size_t N> -constexpr auto as_span(const std::array<T,N> &arr) noexcept -{ return span<std::add_const_t<T>,N>{al::data(arr), al::size(arr)}; } -template<typename U, REQUIRES(!detail_::is_span_v<U> && !detail_::is_std_array_v<U> - && !std::is_array<U>::value && detail_::has_size_and_data<U>)> -constexpr auto as_span(U&& cont) -{ - using value_type = std::remove_pointer_t<decltype(al::data(std::declval<U&>()))>; - return span<value_type>{al::data(cont), al::size(cont)}; -} -template<typename T, size_t N> -constexpr auto as_span(span<T,N> span_) noexcept { return span_; } + +template<typename T, typename EndOrSize> +span(T, EndOrSize) -> span<std::remove_reference_t<decltype(*std::declval<T&>())>>; + +template<typename T, std::size_t N> +span(T (&)[N]) -> span<T, N>; + +template<typename T, std::size_t N> +span(std::array<T, N>&) -> span<T, N>; + +template<typename T, std::size_t N> +span(const std::array<T, N>&) -> span<const T, N>; + +template<typename C, REQUIRES(detail_::is_valid_container_type<C>)> +span(C&&) -> span<std::remove_pointer_t<decltype(std::data(std::declval<C&>()))>>; #undef REQUIRES diff --git a/common/althrd_setname.cpp b/common/althrd_setname.cpp new file mode 100644 index 00000000..22d33092 --- /dev/null +++ b/common/althrd_setname.cpp @@ -0,0 +1,76 @@ + +#include "config.h" + +#include "althrd_setname.h" + + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +void althrd_setname(const char *name [[maybe_unused]]) +{ +#if defined(_MSC_VER) && !defined(_M_ARM) + +#define MS_VC_EXCEPTION 0x406D1388 +#pragma pack(push,8) + struct { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. + } info; +#pragma pack(pop) + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = ~DWORD{0}; + info.dwFlags = 0; + + /* FIXME: How to do this on MinGW? */ + __try { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info); + } + __except(EXCEPTION_CONTINUE_EXECUTION) { + } +#undef MS_VC_EXCEPTION +#endif +} + +#else + +#include <pthread.h> +#ifdef HAVE_PTHREAD_NP_H +#include <pthread_np.h> +#endif + +namespace { + +using setname_t1 = int(*)(const char*); +using setname_t2 = int(*)(pthread_t, const char*); +using setname_t3 = void(*)(pthread_t, const char*); +using setname_t4 = int(*)(pthread_t, const char*, void*); + +[[maybe_unused]] void setname_caller(setname_t1 func, const char *name) +{ func(name); } + +[[maybe_unused]] void setname_caller(setname_t2 func, const char *name) +{ func(pthread_self(), name); } + +[[maybe_unused]] void setname_caller(setname_t3 func, const char *name) +{ func(pthread_self(), name); } + +[[maybe_unused]] void setname_caller(setname_t4 func, const char *name) +{ func(pthread_self(), "%s", static_cast<void*>(const_cast<char*>(name))); } + +} // namespace + +void althrd_setname(const char *name [[maybe_unused]]) +{ +#if defined(HAVE_PTHREAD_SET_NAME_NP) + setname_caller(pthread_set_name_np, name); +#elif defined(HAVE_PTHREAD_SETNAME_NP) + setname_caller(pthread_setname_np, name); +#endif +} + +#endif diff --git a/common/althrd_setname.h b/common/althrd_setname.h new file mode 100644 index 00000000..0e22c0a9 --- /dev/null +++ b/common/althrd_setname.h @@ -0,0 +1,6 @@ +#ifndef COMMON_ALTHRD_SETNAME_H +#define COMMON_ALTHRD_SETNAME_H + +void althrd_setname(const char *name); + +#endif /* COMMON_ALTHRD_SETNAME_H */ diff --git a/common/comptr.h b/common/comptr.h index cdc6dec0..9f8fd294 100644 --- a/common/comptr.h +++ b/common/comptr.h @@ -2,49 +2,84 @@ #define COMMON_COMPTR_H #include <cstddef> +#include <memory> +#include <type_traits> #include <utility> +#include <variant> -#include "opthelpers.h" +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <objbase.h> + +struct ComWrapper { + HRESULT mStatus{}; + + ComWrapper(void *reserved, DWORD coinit) + : mStatus{CoInitializeEx(reserved, coinit)} + { } + ComWrapper(DWORD coinit=COINIT_APARTMENTTHREADED) + : mStatus{CoInitializeEx(nullptr, coinit)} + { } + ComWrapper(ComWrapper&& rhs) { mStatus = std::exchange(rhs.mStatus, E_FAIL); } + ComWrapper(const ComWrapper&) = delete; + ~ComWrapper() { if(SUCCEEDED(mStatus)) CoUninitialize(); } + + ComWrapper& operator=(ComWrapper&& rhs) + { + if(SUCCEEDED(mStatus)) + CoUninitialize(); + mStatus = std::exchange(rhs.mStatus, E_FAIL); + return *this; + } + ComWrapper& operator=(const ComWrapper&) = delete; + + HRESULT status() const noexcept { return mStatus; } + explicit operator bool() const noexcept { return SUCCEEDED(status()); } + + void uninit() + { + if(SUCCEEDED(mStatus)) + CoUninitialize(); + mStatus = E_FAIL; + } +}; template<typename T> -class ComPtr { - T *mPtr{nullptr}; +struct ComPtr { + using element_type = T; + + static constexpr bool RefIsNoexcept{noexcept(std::declval<T&>().AddRef()) + && noexcept(std::declval<T&>().Release())}; -public: ComPtr() noexcept = default; - ComPtr(const ComPtr &rhs) : mPtr{rhs.mPtr} { if(mPtr) mPtr->AddRef(); } + ComPtr(const ComPtr &rhs) noexcept(RefIsNoexcept) : mPtr{rhs.mPtr} + { if(mPtr) mPtr->AddRef(); } ComPtr(ComPtr&& rhs) noexcept : mPtr{rhs.mPtr} { rhs.mPtr = nullptr; } ComPtr(std::nullptr_t) noexcept { } explicit ComPtr(T *ptr) noexcept : mPtr{ptr} { } ~ComPtr() { if(mPtr) mPtr->Release(); } - ComPtr& operator=(const ComPtr &rhs) + ComPtr& operator=(const ComPtr &rhs) noexcept(RefIsNoexcept) { - if(!rhs.mPtr) + if constexpr(RefIsNoexcept) { - if(mPtr) - mPtr->Release(); - mPtr = nullptr; + if(rhs.mPtr) rhs.mPtr->AddRef(); + if(mPtr) mPtr->Release(); + mPtr = rhs.mPtr; + return *this; } else { - rhs.mPtr->AddRef(); - try { - if(mPtr) - mPtr->Release(); - mPtr = rhs.mPtr; - } - catch(...) { - rhs.mPtr->Release(); - throw; - } + ComPtr tmp{rhs}; + if(mPtr) mPtr->Release(); + mPtr = tmp.release(); + return *this; } - return *this; } - ComPtr& operator=(ComPtr&& rhs) + ComPtr& operator=(ComPtr&& rhs) noexcept(RefIsNoexcept) { - if(&rhs != this) LIKELY + if(&rhs != this) { if(mPtr) mPtr->Release(); mPtr = std::exchange(rhs.mPtr, nullptr); @@ -52,17 +87,63 @@ public: return *this; } + void reset(T *ptr=nullptr) noexcept(RefIsNoexcept) + { + if(mPtr) mPtr->Release(); + mPtr = ptr; + } + explicit operator bool() const noexcept { return mPtr != nullptr; } T& operator*() const noexcept { return *mPtr; } T* operator->() const noexcept { return mPtr; } T* get() const noexcept { return mPtr; } - T** getPtr() noexcept { return &mPtr; } T* release() noexcept { return std::exchange(mPtr, nullptr); } void swap(ComPtr &rhs) noexcept { std::swap(mPtr, rhs.mPtr); } void swap(ComPtr&& rhs) noexcept { std::swap(mPtr, rhs.mPtr); } + +private: + T *mPtr{nullptr}; +}; + + +namespace al { + +template<typename SP, typename PT, typename ...Args> +class out_ptr_t { + static_assert(!std::is_same_v<PT,void*>); + + SP &mRes; + std::variant<PT,void*> mPtr{}; + +public: + out_ptr_t(SP &res) : mRes{res} { } + ~out_ptr_t() + { + auto set_res = [this](auto &ptr) + { mRes.reset(static_cast<PT>(ptr)); }; + std::visit(set_res, mPtr); + } + out_ptr_t(const out_ptr_t&) = delete; + + out_ptr_t& operator=(const out_ptr_t&) = delete; + + operator PT*() noexcept + { return &std::get<PT>(mPtr); } + + operator void**() noexcept + { return &mPtr.template emplace<void*>(); } }; +template<typename T=void, typename SP, typename ...Args> +auto out_ptr(SP &res) +{ + using ptype = typename SP::element_type*; + return out_ptr_t<SP,ptype>{res}; +} + +} // namespace al + #endif diff --git a/common/dynload.cpp b/common/dynload.cpp index f1c2a7eb..86c36e00 100644 --- a/common/dynload.cpp +++ b/common/dynload.cpp @@ -3,6 +3,7 @@ #include "dynload.h" +#include "albit.h" #include "strutils.h" #ifdef _WIN32 @@ -17,7 +18,7 @@ void *LoadLib(const char *name) void CloseLib(void *handle) { FreeLibrary(static_cast<HMODULE>(handle)); } void *GetSymbol(void *handle, const char *name) -{ return reinterpret_cast<void*>(GetProcAddress(static_cast<HMODULE>(handle), name)); } +{ return al::bit_cast<void*>(GetProcAddress(static_cast<HMODULE>(handle), name)); } #elif defined(HAVE_DLFCN_H) diff --git a/common/opthelpers.h b/common/opthelpers.h index 596c2455..dc43ccdb 100644 --- a/common/opthelpers.h +++ b/common/opthelpers.h @@ -19,10 +19,13 @@ #ifdef __GNUC__ #define force_inline [[gnu::always_inline]] inline +#define NOINLINE [[gnu::noinline]] #elif defined(_MSC_VER) #define force_inline __forceinline +#define NOINLINE __declspec(noinline) #else #define force_inline inline +#define NOINLINE #endif /* Unlike the likely attribute, ASSUME requires the condition to be true or diff --git a/common/pffft.cpp b/common/pffft.cpp new file mode 100644 index 00000000..71f71fa6 --- /dev/null +++ b/common/pffft.cpp @@ -0,0 +1,2259 @@ +//$ nobt + +/* Copyright (c) 2013 Julien Pommier ( [email protected] ) + * Copyright (c) 2023 Christopher Robinson + * + * Based on original fortran 77 code from FFTPACKv4 from NETLIB + * (http://www.netlib.org/fftpack), authored by Dr Paul Swarztrauber + * of NCAR, in 1985. + * + * As confirmed by the NCAR fftpack software curators, the following + * FFTPACKv5 license applies to FFTPACKv4 sources. My changes are + * released under the same terms. + * + * FFTPACK license: + * + * http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html + * + * Copyright (c) 2004 the University Corporation for Atmospheric + * Research ("UCAR"). All rights reserved. Developed by NCAR's + * Computational and Information Systems Laboratory, UCAR, + * www.cisl.ucar.edu. + * + * Redistribution and use of the Software in source and binary forms, + * with or without modification, is permitted provided that the + * following conditions are met: + * + * - Neither the names of NCAR's Computational and Information Systems + * Laboratory, the University Corporation for Atmospheric Research, + * nor the names of its sponsors or contributors may be used to + * endorse or promote products derived from this Software without + * specific prior written permission. + * + * - Redistributions of source code must retain the above copyright + * notices, this list of conditions, and the disclaimer below. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions, and the disclaimer below in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS 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 CONTRIBUTORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL 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 WITH THE + * SOFTWARE. + * + * + * PFFFT : a Pretty Fast FFT. + * + * This file is largerly based on the original FFTPACK implementation, modified + * in order to take advantage of SIMD instructions of modern CPUs. + */ + +#include "pffft.h" + +#include <array> +#include <assert.h> +#include <cmath> +#include <cstring> +#include <stdio.h> +#include <stdlib.h> +#include <vector> + +#include "albit.h" +#include "almalloc.h" +#include "alnumbers.h" +#include "alspan.h" +#include "opthelpers.h" + + +namespace { + +using uint = unsigned int; + + +/* Vector support macros: the rest of the code is independent of + * SSE/Altivec/NEON -- adding support for other platforms with 4-element + * vectors should be limited to these macros + */ + +/* Define PFFFT_SIMD_DISABLE if you want to use scalar code instead of SIMD code */ +//#define PFFFT_SIMD_DISABLE + +#ifndef PFFFT_SIMD_DISABLE +/* + * Altivec support macros + */ +#if defined(__ppc__) || defined(__ppc64__) || defined(__powerpc__) || defined(__powerpc64__) +typedef vector float v4sf; +#define SIMD_SZ 4 +#define VZERO() ((vector float) vec_splat_u8(0)) +#define VMUL(a,b) vec_madd(a,b, VZERO()) +#define VADD vec_add +#define VMADD vec_madd +#define VSUB vec_sub +#define LD_PS1 vec_splats +force_inline v4sf vset4(float a, float b, float c, float d) noexcept +{ + /* There a more efficient way to do this? */ + alignas(16) std::array<float,4> vals{{a, b, c, d}}; + return vec_ld(0, vals.data()); +} +#define VSET4 vset4 +#define VINSERT0(v, a) vec_insert((a), (v), 0) +#define VEXTRACT0(v) vec_extract((v), 0) + +force_inline void interleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + v4sf tmp{vec_mergeh(in1, in2)}; + out2 = vec_mergel(in1, in2); + out1 = tmp; +} +force_inline void uninterleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + v4sf tmp{vec_perm(in1, in2, (vector unsigned char)(0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27))}; + out2 = vec_perm(in1, in2, (vector unsigned char)(4,5,6,7,12,13,14,15,20,21,22,23,28,29,30,31)); + out1 = tmp; +} + +force_inline void vtranspose4(v4sf &x0, v4sf &x1, v4sf &x2, v4sf &x3) noexcept +{ + v4sf y0{vec_mergeh(x0, x2)}; + v4sf y1{vec_mergel(x0, x2)}; + v4sf y2{vec_mergeh(x1, x3)}; + v4sf y3{vec_mergel(x1, x3)}; + x0 = vec_mergeh(y0, y2); + x1 = vec_mergel(y0, y2); + x2 = vec_mergeh(y1, y3); + x3 = vec_mergel(y1, y3); +} + +#define VSWAPHL(a,b) vec_perm(a,b, (vector unsigned char)(16,17,18,19,20,21,22,23,8,9,10,11,12,13,14,15)) + +/* + * SSE1 support macros + */ +#elif defined(__x86_64__) || defined(__SSE__) || defined(_M_X64) || \ + (defined(_M_IX86_FP) && _M_IX86_FP >= 1) + +#include <xmmintrin.h> +typedef __m128 v4sf; +#define SIMD_SZ 4 // 4 floats by simd vector -- this is pretty much hardcoded in the preprocess/finalize functions anyway so you will have to work if you want to enable AVX with its 256-bit vectors. +#define VZERO _mm_setzero_ps +#define VMUL _mm_mul_ps +#define VADD _mm_add_ps +#define VMADD(a,b,c) _mm_add_ps(_mm_mul_ps(a,b), c) +#define VSUB _mm_sub_ps +#define LD_PS1 _mm_set1_ps +#define VSET4 _mm_setr_ps +#define VINSERT0(v, a) _mm_move_ss((v), _mm_set_ss(a)) +#define VEXTRACT0 _mm_cvtss_f32 + +force_inline void interleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + v4sf tmp{_mm_unpacklo_ps(in1, in2)}; + out2 = _mm_unpackhi_ps(in1, in2); + out1 = tmp; +} +force_inline void uninterleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + v4sf tmp{_mm_shuffle_ps(in1, in2, _MM_SHUFFLE(2,0,2,0))}; + out2 = _mm_shuffle_ps(in1, in2, _MM_SHUFFLE(3,1,3,1)); + out1 = tmp; +} + +force_inline void vtranspose4(v4sf &x0, v4sf &x1, v4sf &x2, v4sf &x3) noexcept +{ _MM_TRANSPOSE4_PS(x0, x1, x2, x3); } + +#define VSWAPHL(a,b) _mm_shuffle_ps(b, a, _MM_SHUFFLE(3,2,1,0)) + +/* + * ARM NEON support macros + */ +#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(__arm64) + +#include <arm_neon.h> +typedef float32x4_t v4sf; +#define SIMD_SZ 4 +#define VZERO() vdupq_n_f32(0) +#define VMUL vmulq_f32 +#define VADD vaddq_f32 +#define VMADD(a,b,c) vmlaq_f32(c,a,b) +#define VSUB vsubq_f32 +#define LD_PS1 vdupq_n_f32 +force_inline v4sf vset4(float a, float b, float c, float d) noexcept +{ + float32x4_t ret{vmovq_n_f32(a)}; + ret = vsetq_lane_f32(b, ret, 1); + ret = vsetq_lane_f32(c, ret, 2); + ret = vsetq_lane_f32(d, ret, 3); + return ret; +} +#define VSET4 vset4 +#define VINSERT0(v, a) vsetq_lane_f32((a), (v), 0) +#define VEXTRACT0(v) vgetq_lane_f32((v), 0) + +force_inline void interleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + float32x4x2_t tmp{vzipq_f32(in1, in2)}; + out1 = tmp.val[0]; + out2 = tmp.val[1]; +} +force_inline void uninterleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + float32x4x2_t tmp{vuzpq_f32(in1, in2)}; + out1 = tmp.val[0]; + out2 = tmp.val[1]; +} + +force_inline void vtranspose4(v4sf &x0, v4sf &x1, v4sf &x2, v4sf &x3) noexcept +{ + /* marginally faster version: + * asm("vtrn.32 %q0, %q1;\n" + * "vtrn.32 %q2, %q3\n + * "vswp %f0, %e2\n + * "vswp %f1, %e3" + * : "+w"(x0), "+w"(x1), "+w"(x2), "+w"(x3)::); + */ + float32x4x2_t t0_{vzipq_f32(x0, x2)}; + float32x4x2_t t1_{vzipq_f32(x1, x3)}; + float32x4x2_t u0_{vzipq_f32(t0_.val[0], t1_.val[0])}; + float32x4x2_t u1_{vzipq_f32(t0_.val[1], t1_.val[1])}; + x0 = u0_.val[0]; + x1 = u0_.val[1]; + x2 = u1_.val[0]; + x3 = u1_.val[1]; +} + +#define VSWAPHL(a,b) vcombine_f32(vget_low_f32(b), vget_high_f32(a)) + +/* + * Generic GCC vector macros + */ +#elif defined(__GNUC__) + +using v4sf [[gnu::vector_size(16), gnu::aligned(16)]] = float; +#define SIMD_SZ 4 +#define VZERO() v4sf{0,0,0,0} +#define VMUL(a,b) ((a) * (b)) +#define VADD(a,b) ((a) + (b)) +#define VMADD(a,b,c) ((a)*(b) + (c)) +#define VSUB(a,b) ((a) - (b)) + +constexpr force_inline v4sf ld_ps1(float a) noexcept { return v4sf{a, a, a, a}; } +#define LD_PS1 ld_ps1 +#define VSET4(a, b, c, d) v4sf{(a), (b), (c), (d)} +constexpr force_inline v4sf vinsert0(v4sf v, float a) noexcept +{ return v4sf{a, v[1], v[2], v[3]}; } +#define VINSERT0 vinsert0 +#define VEXTRACT0(v) ((v)[0]) + +force_inline v4sf unpacklo(v4sf a, v4sf b) noexcept +{ return v4sf{a[0], b[0], a[1], b[1]}; } +force_inline v4sf unpackhi(v4sf a, v4sf b) noexcept +{ return v4sf{a[2], b[2], a[3], b[3]}; } + +force_inline void interleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + v4sf tmp{unpacklo(in1, in2)}; + out2 = unpackhi(in1, in2); + out1 = tmp; +} +force_inline void uninterleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + v4sf tmp{in1[0], in1[2], in2[0], in2[2]}; + out2 = v4sf{in1[1], in1[3], in2[1], in2[3]}; + out1 = tmp; +} + +force_inline void vtranspose4(v4sf &x0, v4sf &x1, v4sf &x2, v4sf &x3) noexcept +{ + v4sf tmp0{unpacklo(x0, x1)}; + v4sf tmp2{unpacklo(x2, x3)}; + v4sf tmp1{unpackhi(x0, x1)}; + v4sf tmp3{unpackhi(x2, x3)}; + x0 = v4sf{tmp0[0], tmp0[1], tmp2[0], tmp2[1]}; + x1 = v4sf{tmp0[2], tmp0[3], tmp2[2], tmp2[3]}; + x2 = v4sf{tmp1[0], tmp1[1], tmp3[0], tmp3[1]}; + x3 = v4sf{tmp1[2], tmp1[3], tmp3[2], tmp3[3]}; +} + +force_inline v4sf vswaphl(v4sf a, v4sf b) noexcept +{ return v4sf{b[0], b[1], a[2], a[3]}; } +#define VSWAPHL vswaphl + +#else + +#warning "building with simd disabled !\n"; +#define PFFFT_SIMD_DISABLE // fallback to scalar code +#endif + +#endif /* PFFFT_SIMD_DISABLE */ + +// fallback mode for situations where SIMD is not available, use scalar mode instead +#ifdef PFFFT_SIMD_DISABLE +typedef float v4sf; +#define SIMD_SZ 1 +#define VZERO() 0.f +#define VMUL(a,b) ((a)*(b)) +#define VADD(a,b) ((a)+(b)) +#define VMADD(a,b,c) ((a)*(b)+(c)) +#define VSUB(a,b) ((a)-(b)) +#define LD_PS1(p) (p) +#endif + +inline bool valigned(const float *ptr) noexcept +{ + static constexpr uintptr_t alignmask{SIMD_SZ*4 - 1}; + return (reinterpret_cast<uintptr_t>(ptr) & alignmask) == 0; +} + +// shortcuts for complex multiplications +force_inline void vcplxmul(v4sf &ar, v4sf &ai, v4sf br, v4sf bi) noexcept +{ + v4sf tmp{VMUL(ar, bi)}; + ar = VSUB(VMUL(ar, br), VMUL(ai, bi)); + ai = VMADD(ai, br, tmp); +} +force_inline void vcplxmulconj(v4sf &ar, v4sf &ai, v4sf br, v4sf bi) noexcept +{ + v4sf tmp{VMUL(ar, bi)}; + ar = VMADD(ai, bi, VMUL(ar, br)); + ai = VSUB(VMUL(ai, br), tmp); +} + +#if !defined(PFFFT_SIMD_DISABLE) + +#define assertv4(v,f0,f1,f2,f3) assert(v##_f[0] == (f0) && v##_f[1] == (f1) && v##_f[2] == (f2) && v##_f[3] == (f3)) + +/* detect bugs with the vector support macros */ +[[maybe_unused]] void validate_pffft_simd() +{ + using float4 = std::array<float,4>; + static constexpr float f[16]{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + + float4 a0_f, a1_f, a2_f, a3_f, t_f, u_f; + v4sf a0_v, a1_v, a2_v, a3_v, t_v, u_v; + std::memcpy(&a0_v, f, 4*sizeof(float)); + std::memcpy(&a1_v, f+4, 4*sizeof(float)); + std::memcpy(&a2_v, f+8, 4*sizeof(float)); + std::memcpy(&a3_v, f+12, 4*sizeof(float)); + + t_v = VZERO(); t_f = al::bit_cast<float4>(t_v); + printf("VZERO=[%2g %2g %2g %2g]\n", t_f[0], t_f[1], t_f[2], t_f[3]); assertv4(t, 0, 0, 0, 0); + t_v = VADD(a1_v, a2_v); t_f = al::bit_cast<float4>(t_v); + printf("VADD(4:7,8:11)=[%2g %2g %2g %2g]\n", t_f[0], t_f[1], t_f[2], t_f[3]); assertv4(t, 12, 14, 16, 18); + t_v = VMUL(a1_v, a2_v); t_f = al::bit_cast<float4>(t_v); + printf("VMUL(4:7,8:11)=[%2g %2g %2g %2g]\n", t_f[0], t_f[1], t_f[2], t_f[3]); assertv4(t, 32, 45, 60, 77); + t_v = VMADD(a1_v, a2_v,a0_v); t_f = al::bit_cast<float4>(t_v); + printf("VMADD(4:7,8:11,0:3)=[%2g %2g %2g %2g]\n", t_f[0], t_f[1], t_f[2], t_f[3]); assertv4(t, 32, 46, 62, 80); + + interleave2(a1_v,a2_v,t_v,u_v); t_f = al::bit_cast<float4>(t_v); u_f = al::bit_cast<float4>(u_v); + printf("INTERLEAVE2(4:7,8:11)=[%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", t_f[0], t_f[1], t_f[2], t_f[3], u_f[0], u_f[1], u_f[2], u_f[3]); + assertv4(t, 4, 8, 5, 9); assertv4(u, 6, 10, 7, 11); + uninterleave2(a1_v,a2_v,t_v,u_v); t_f = al::bit_cast<float4>(t_v); u_f = al::bit_cast<float4>(u_v); + printf("UNINTERLEAVE2(4:7,8:11)=[%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", t_f[0], t_f[1], t_f[2], t_f[3], u_f[0], u_f[1], u_f[2], u_f[3]); + assertv4(t, 4, 6, 8, 10); assertv4(u, 5, 7, 9, 11); + + t_v=LD_PS1(f[15]); t_f = al::bit_cast<float4>(t_v); + printf("LD_PS1(15)=[%2g %2g %2g %2g]\n", t_f[0], t_f[1], t_f[2], t_f[3]); + assertv4(t, 15, 15, 15, 15); + t_v = VSWAPHL(a1_v, a2_v); t_f = al::bit_cast<float4>(t_v); + printf("VSWAPHL(4:7,8:11)=[%2g %2g %2g %2g]\n", t_f[0], t_f[1], t_f[2], t_f[3]); + assertv4(t, 8, 9, 6, 7); + vtranspose4(a0_v, a1_v, a2_v, a3_v); + a0_f = al::bit_cast<float4>(a0_v); + a1_f = al::bit_cast<float4>(a1_v); + a2_f = al::bit_cast<float4>(a2_v); + a3_f = al::bit_cast<float4>(a3_v); + printf("VTRANSPOSE4(0:3,4:7,8:11,12:15)=[%2g %2g %2g %2g] [%2g %2g %2g %2g] [%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", + a0_f[0], a0_f[1], a0_f[2], a0_f[3], a1_f[0], a1_f[1], a1_f[2], a1_f[3], + a2_f[0], a2_f[1], a2_f[2], a2_f[3], a3_f[0], a3_f[1], a3_f[2], a3_f[3]); + assertv4(a0, 0, 4, 8, 12); assertv4(a1, 1, 5, 9, 13); assertv4(a2, 2, 6, 10, 14); assertv4(a3, 3, 7, 11, 15); +} +#endif //!PFFFT_SIMD_DISABLE + +/* SSE and co like 16-bytes aligned pointers */ +#define MALLOC_V4SF_ALIGNMENT 64 // with a 64-byte alignment, we are even aligned on L2 cache lines... + +/* + passf2 and passb2 has been merged here, fsign = -1 for passf2, +1 for passb2 +*/ +NOINLINE void passf2_ps(const size_t ido, const size_t l1, const v4sf *cc, v4sf *RESTRICT ch, + const float *wa1, const float fsign) +{ + const size_t l1ido{l1*ido}; + if(ido <= 2) + { + for(size_t k{0};k < l1ido;k += ido, ch += ido, cc += 2*ido) + { + ch[0] = VADD(cc[0], cc[ido+0]); + ch[l1ido] = VSUB(cc[0], cc[ido+0]); + ch[1] = VADD(cc[1], cc[ido+1]); + ch[l1ido + 1] = VSUB(cc[1], cc[ido+1]); + } + } + else + { + for(size_t k{0};k < l1ido;k += ido, ch += ido, cc += 2*ido) + { + for(size_t i{0};i < ido-1;i += 2) + { + v4sf tr2{VSUB(cc[i+0], cc[i+ido+0])}; + v4sf ti2{VSUB(cc[i+1], cc[i+ido+1])}; + v4sf wr{LD_PS1(wa1[i])}, wi{LD_PS1(wa1[i+1]*fsign)}; + ch[i] = VADD(cc[i+0], cc[i+ido+0]); + ch[i+1] = VADD(cc[i+1], cc[i+ido+1]); + vcplxmul(tr2, ti2, wr, wi); + ch[i+l1ido] = tr2; + ch[i+l1ido+1] = ti2; + } + } + } +} + +/* + passf3 and passb3 has been merged here, fsign = -1 for passf3, +1 for passb3 +*/ +NOINLINE void passf3_ps(const size_t ido, const size_t l1, const v4sf *cc, v4sf *RESTRICT ch, + const float *wa1, const float *wa2, const float fsign) +{ + assert(ido > 2); + + const v4sf taur{LD_PS1(-0.5f)}; + const v4sf taui{LD_PS1(0.866025403784439f*fsign)}; + const size_t l1ido{l1*ido}; + for(size_t k{0};k < l1ido;k += ido, cc += 3*ido, ch +=ido) + { + for(size_t i{0};i < ido-1;i += 2) + { + v4sf tr2{VADD(cc[i+ido], cc[i+2*ido])}; + v4sf cr2{VMADD(taur, tr2, cc[i])}; + ch[i] = VADD(tr2, cc[i]); + v4sf ti2{VADD(cc[i+ido+1], cc[i+2*ido+1])}; + v4sf ci2{VMADD(taur, ti2, cc[i+1])}; + ch[i+1] = VADD(cc[i+1], ti2); + v4sf cr3{VMUL(taui, VSUB(cc[i+ido], cc[i+2*ido]))}; + v4sf ci3{VMUL(taui, VSUB(cc[i+ido+1], cc[i+2*ido+1]))}; + v4sf dr2{VSUB(cr2, ci3)}; + v4sf dr3{VADD(cr2, ci3)}; + v4sf di2{VADD(ci2, cr3)}; + v4sf di3{VSUB(ci2, cr3)}; + float wr1{wa1[i]}, wi1{fsign*wa1[i+1]}, wr2{wa2[i]}, wi2{fsign*wa2[i+1]}; + vcplxmul(dr2, di2, LD_PS1(wr1), LD_PS1(wi1)); + ch[i+l1ido] = dr2; + ch[i+l1ido + 1] = di2; + vcplxmul(dr3, di3, LD_PS1(wr2), LD_PS1(wi2)); + ch[i+2*l1ido] = dr3; + ch[i+2*l1ido+1] = di3; + } + } +} /* passf3 */ + +NOINLINE void passf4_ps(const size_t ido, const size_t l1, const v4sf *cc, v4sf *RESTRICT ch, + const float *wa1, const float *wa2, const float *wa3, const float fsign) +{ + /* fsign == -1 for forward transform and +1 for backward transform */ + const v4sf vsign{LD_PS1(fsign)}; + const size_t l1ido{l1*ido}; + if(ido == 2) + { + for(size_t k{0};k < l1ido;k += ido, ch += ido, cc += 4*ido) + { + v4sf tr1{VSUB(cc[0], cc[2*ido + 0])}; + v4sf tr2{VADD(cc[0], cc[2*ido + 0])}; + v4sf ti1{VSUB(cc[1], cc[2*ido + 1])}; + v4sf ti2{VADD(cc[1], cc[2*ido + 1])}; + v4sf ti4{VMUL(VSUB(cc[1*ido + 0], cc[3*ido + 0]), vsign)}; + v4sf tr4{VMUL(VSUB(cc[3*ido + 1], cc[1*ido + 1]), vsign)}; + v4sf tr3{VADD(cc[ido + 0], cc[3*ido + 0])}; + v4sf ti3{VADD(cc[ido + 1], cc[3*ido + 1])}; + + ch[0*l1ido + 0] = VADD(tr2, tr3); + ch[0*l1ido + 1] = VADD(ti2, ti3); + ch[1*l1ido + 0] = VADD(tr1, tr4); + ch[1*l1ido + 1] = VADD(ti1, ti4); + ch[2*l1ido + 0] = VSUB(tr2, tr3); + ch[2*l1ido + 1] = VSUB(ti2, ti3); + ch[3*l1ido + 0] = VSUB(tr1, tr4); + ch[3*l1ido + 1] = VSUB(ti1, ti4); + } + } + else + { + for(size_t k{0};k < l1ido;k += ido, ch+=ido, cc += 4*ido) + { + for(size_t i{0};i < ido-1;i+=2) + { + v4sf tr1{VSUB(cc[i + 0], cc[i + 2*ido + 0])}; + v4sf tr2{VADD(cc[i + 0], cc[i + 2*ido + 0])}; + v4sf ti1{VSUB(cc[i + 1], cc[i + 2*ido + 1])}; + v4sf ti2{VADD(cc[i + 1], cc[i + 2*ido + 1])}; + v4sf tr4{VMUL(VSUB(cc[i + 3*ido + 1], cc[i + 1*ido + 1]), vsign)}; + v4sf ti4{VMUL(VSUB(cc[i + 1*ido + 0], cc[i + 3*ido + 0]), vsign)}; + v4sf tr3{VADD(cc[i + ido + 0], cc[i + 3*ido + 0])}; + v4sf ti3{VADD(cc[i + ido + 1], cc[i + 3*ido + 1])}; + + ch[i] = VADD(tr2, tr3); + v4sf cr3{VSUB(tr2, tr3)}; + ch[i + 1] = VADD(ti2, ti3); + v4sf ci3{VSUB(ti2, ti3)}; + + v4sf cr2{VADD(tr1, tr4)}; + v4sf cr4{VSUB(tr1, tr4)}; + v4sf ci2{VADD(ti1, ti4)}; + v4sf ci4{VSUB(ti1, ti4)}; + float wr1{wa1[i]}, wi1{fsign*wa1[i+1]}; + vcplxmul(cr2, ci2, LD_PS1(wr1), LD_PS1(wi1)); + float wr2{wa2[i]}, wi2{fsign*wa2[i+1]}; + ch[i + l1ido] = cr2; + ch[i + l1ido + 1] = ci2; + + vcplxmul(cr3, ci3, LD_PS1(wr2), LD_PS1(wi2)); + float wr3{wa3[i]}, wi3{fsign*wa3[i+1]}; + ch[i + 2*l1ido] = cr3; + ch[i + 2*l1ido + 1] = ci3; + + vcplxmul(cr4, ci4, LD_PS1(wr3), LD_PS1(wi3)); + ch[i + 3*l1ido] = cr4; + ch[i + 3*l1ido + 1] = ci4; + } + } + } +} /* passf4 */ + +/* + * passf5 and passb5 has been merged here, fsign = -1 for passf5, +1 for passb5 + */ +NOINLINE void passf5_ps(const size_t ido, const size_t l1, const v4sf *cc, v4sf *RESTRICT ch, + const float *wa1, const float *wa2, const float *wa3, const float *wa4, const float fsign) +{ + const v4sf tr11{LD_PS1(0.309016994374947f)}; + const v4sf tr12{LD_PS1(-0.809016994374947f)}; + const v4sf ti11{LD_PS1(0.951056516295154f*fsign)}; + const v4sf ti12{LD_PS1(0.587785252292473f*fsign)}; + +#define cc_ref(a_1,a_2) cc[(a_2-1)*ido + (a_1) + 1] +#define ch_ref(a_1,a_3) ch[(a_3-1)*l1*ido + (a_1) + 1] + + assert(ido > 2); + for(size_t k{0};k < l1;++k, cc += 5*ido, ch += ido) + { + for(size_t i{0};i < ido-1;i += 2) + { + v4sf ti5{VSUB(cc_ref(i , 2), cc_ref(i , 5))}; + v4sf ti2{VADD(cc_ref(i , 2), cc_ref(i , 5))}; + v4sf ti4{VSUB(cc_ref(i , 3), cc_ref(i , 4))}; + v4sf ti3{VADD(cc_ref(i , 3), cc_ref(i , 4))}; + v4sf tr5{VSUB(cc_ref(i-1, 2), cc_ref(i-1, 5))}; + v4sf tr2{VADD(cc_ref(i-1, 2), cc_ref(i-1, 5))}; + v4sf tr4{VSUB(cc_ref(i-1, 3), cc_ref(i-1, 4))}; + v4sf tr3{VADD(cc_ref(i-1, 3), cc_ref(i-1, 4))}; + ch_ref(i-1, 1) = VADD(cc_ref(i-1, 1), VADD(tr2, tr3)); + ch_ref(i , 1) = VADD(cc_ref(i , 1), VADD(ti2, ti3)); + v4sf cr2{VADD(cc_ref(i-1, 1), VMADD(tr11, tr2, VMUL(tr12, tr3)))}; + v4sf ci2{VADD(cc_ref(i , 1), VMADD(tr11, ti2, VMUL(tr12, ti3)))}; + v4sf cr3{VADD(cc_ref(i-1, 1), VMADD(tr12, tr2, VMUL(tr11, tr3)))}; + v4sf ci3{VADD(cc_ref(i , 1), VMADD(tr12, ti2, VMUL(tr11, ti3)))}; + v4sf cr5{VMADD(ti11, tr5, VMUL(ti12, tr4))}; + v4sf ci5{VMADD(ti11, ti5, VMUL(ti12, ti4))}; + v4sf cr4{VSUB(VMUL(ti12, tr5), VMUL(ti11, tr4))}; + v4sf ci4{VSUB(VMUL(ti12, ti5), VMUL(ti11, ti4))}; + v4sf dr3{VSUB(cr3, ci4)}; + v4sf dr4{VADD(cr3, ci4)}; + v4sf di3{VADD(ci3, cr4)}; + v4sf di4{VSUB(ci3, cr4)}; + v4sf dr5{VADD(cr2, ci5)}; + v4sf dr2{VSUB(cr2, ci5)}; + v4sf di5{VSUB(ci2, cr5)}; + v4sf di2{VADD(ci2, cr5)}; + float wr1{wa1[i]}, wi1{fsign*wa1[i+1]}, wr2{wa2[i]}, wi2{fsign*wa2[i+1]}; + float wr3{wa3[i]}, wi3{fsign*wa3[i+1]}, wr4{wa4[i]}, wi4{fsign*wa4[i+1]}; + vcplxmul(dr2, di2, LD_PS1(wr1), LD_PS1(wi1)); + ch_ref(i - 1, 2) = dr2; + ch_ref(i, 2) = di2; + vcplxmul(dr3, di3, LD_PS1(wr2), LD_PS1(wi2)); + ch_ref(i - 1, 3) = dr3; + ch_ref(i, 3) = di3; + vcplxmul(dr4, di4, LD_PS1(wr3), LD_PS1(wi3)); + ch_ref(i - 1, 4) = dr4; + ch_ref(i, 4) = di4; + vcplxmul(dr5, di5, LD_PS1(wr4), LD_PS1(wi4)); + ch_ref(i - 1, 5) = dr5; + ch_ref(i, 5) = di5; + } + } +#undef ch_ref +#undef cc_ref +} + +NOINLINE void radf2_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, + v4sf *RESTRICT ch, const float *wa1) +{ + const size_t l1ido{l1*ido}; + for(size_t k{0};k < l1ido;k += ido) + { + v4sf a{cc[k]}, b{cc[k + l1ido]}; + ch[2*k] = VADD(a, b); + ch[2*(k+ido)-1] = VSUB(a, b); + } + if(ido < 2) + return; + if(ido != 2) + { + for(size_t k{0};k < l1ido;k += ido) + { + for(size_t i{2};i < ido;i += 2) + { + v4sf tr2{cc[i - 1 + k + l1ido]}, ti2{cc[i + k + l1ido]}; + v4sf br{cc[i - 1 + k]}, bi{cc[i + k]}; + vcplxmulconj(tr2, ti2, LD_PS1(wa1[i - 2]), LD_PS1(wa1[i - 1])); + ch[i + 2*k] = VADD(bi, ti2); + ch[2*(k+ido) - i] = VSUB(ti2, bi); + ch[i - 1 + 2*k] = VADD(br, tr2); + ch[2*(k+ido) - i -1] = VSUB(br, tr2); + } + } + if((ido&1) == 1) + return; + } + const v4sf minus_one{LD_PS1(-1.0f)}; + for(size_t k{0};k < l1ido;k += ido) + { + ch[2*k + ido] = VMUL(minus_one, cc[ido-1 + k + l1ido]); + ch[2*k + ido-1] = cc[k + ido-1]; + } +} /* radf2 */ + + +NOINLINE void radb2_ps(const size_t ido, const size_t l1, const v4sf *cc, v4sf *RESTRICT ch, + const float *wa1) +{ + const size_t l1ido{l1*ido}; + for(size_t k{0};k < l1ido;k += ido) + { + v4sf a{cc[2*k]}; + v4sf b{cc[2*(k+ido) - 1]}; + ch[k] = VADD(a, b); + ch[k + l1ido] = VSUB(a, b); + } + if(ido < 2) + return; + if(ido != 2) + { + for(size_t k{0};k < l1ido;k += ido) + { + for(size_t i{2};i < ido;i += 2) + { + v4sf a{cc[i-1 + 2*k]}; + v4sf b{cc[2*(k + ido) - i - 1]}; + v4sf c{cc[i+0 + 2*k]}; + v4sf d{cc[2*(k + ido) - i + 0]}; + ch[i-1 + k] = VADD(a, b); + v4sf tr2{VSUB(a, b)}; + ch[i+0 + k] = VSUB(c, d); + v4sf ti2{VADD(c, d)}; + vcplxmul(tr2, ti2, LD_PS1(wa1[i - 2]), LD_PS1(wa1[i - 1])); + ch[i-1 + k + l1ido] = tr2; + ch[i+0 + k + l1ido] = ti2; + } + } + if((ido&1) == 1) + return; + } + const v4sf minus_two{LD_PS1(-2.0f)}; + for(size_t k{0};k < l1ido;k += ido) + { + v4sf a{cc[2*k + ido-1]}; + v4sf b{cc[2*k + ido]}; + ch[k + ido-1] = VADD(a,a); + ch[k + ido-1 + l1ido] = VMUL(minus_two, b); + } +} /* radb2 */ + +void radf3_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, v4sf *RESTRICT ch, + const float *wa1, const float *wa2) +{ + const v4sf taur{LD_PS1(-0.5f)}; + const v4sf taui{LD_PS1(0.866025403784439f)}; + for(size_t k{0};k < l1;++k) + { + v4sf cr2{VADD(cc[(k + l1)*ido], cc[(k + 2*l1)*ido])}; + ch[ (3*k )*ido] = VADD(cc[k*ido], cr2); + ch[ (3*k + 2)*ido] = VMUL(taui, VSUB(cc[(k + l1*2)*ido], cc[(k + l1)*ido])); + ch[ido-1 + (3*k + 1)*ido] = VMADD(taur, cr2, cc[k*ido]); + } + if(ido == 1) + return; + for(size_t k{0};k < l1;++k) + { + for(size_t i{2};i < ido;i += 2) + { + const size_t ic{ido - i}; + v4sf wr1{LD_PS1(wa1[i - 2])}; + v4sf wi1{LD_PS1(wa1[i - 1])}; + v4sf dr2{cc[i - 1 + (k + l1)*ido]}; + v4sf di2{cc[i + (k + l1)*ido]}; + vcplxmulconj(dr2, di2, wr1, wi1); + + v4sf wr2{LD_PS1(wa2[i - 2])}; + v4sf wi2{LD_PS1(wa2[i - 1])}; + v4sf dr3{cc[i - 1 + (k + l1*2)*ido]}; + v4sf di3{cc[i + (k + l1*2)*ido]}; + vcplxmulconj(dr3, di3, wr2, wi2); + + v4sf cr2{VADD(dr2, dr3)}; + v4sf ci2{VADD(di2, di3)}; + ch[i - 1 + 3*k*ido] = VADD(cc[i - 1 + k*ido], cr2); + ch[i + 3*k*ido] = VADD(cc[i + k*ido], ci2); + v4sf tr2{VMADD(taur, cr2, cc[i - 1 + k*ido])}; + v4sf ti2{VMADD(taur, ci2, cc[i + k*ido])}; + v4sf tr3{VMUL(taui, VSUB(di2, di3))}; + v4sf ti3{VMUL(taui, VSUB(dr3, dr2))}; + ch[i - 1 + (3*k + 2)*ido] = VADD(tr2, tr3); + ch[ic - 1 + (3*k + 1)*ido] = VSUB(tr2, tr3); + ch[i + (3*k + 2)*ido] = VADD(ti2, ti3); + ch[ic + (3*k + 1)*ido] = VSUB(ti3, ti2); + } + } +} /* radf3 */ + + +void radb3_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, v4sf *RESTRICT ch, + const float *wa1, const float *wa2) +{ + static constexpr float taur{-0.5f}; + static constexpr float taui{0.866025403784439f}; + static constexpr float taui_2{taui*2.0f}; + + const v4sf vtaur{LD_PS1(taur)}; + const v4sf vtaui_2{LD_PS1(taui_2)}; + for(size_t k{0};k < l1;++k) + { + v4sf tr2 = cc[ido-1 + (3*k + 1)*ido]; + tr2 = VADD(tr2,tr2); + v4sf cr2 = VMADD(vtaur, tr2, cc[3*k*ido]); + ch[k*ido] = VADD(cc[3*k*ido], tr2); + v4sf ci3 = VMUL(vtaui_2, cc[(3*k + 2)*ido]); + ch[(k + l1)*ido] = VSUB(cr2, ci3); + ch[(k + 2*l1)*ido] = VADD(cr2, ci3); + } + if(ido == 1) + return; + const v4sf vtaui{LD_PS1(taui)}; + for(size_t k{0};k < l1;++k) + { + for(size_t i{2};i < ido;i += 2) + { + const size_t ic{ido - i}; + v4sf tr2{VADD(cc[i - 1 + (3*k + 2)*ido], cc[ic - 1 + (3*k + 1)*ido])}; + v4sf cr2{VMADD(vtaur, tr2, cc[i - 1 + 3*k*ido])}; + ch[i - 1 + k*ido] = VADD(cc[i - 1 + 3*k*ido], tr2); + v4sf ti2{VSUB(cc[i + (3*k + 2)*ido], cc[ic + (3*k + 1)*ido])}; + v4sf ci2{VMADD(vtaur, ti2, cc[i + 3*k*ido])}; + ch[i + k*ido] = VADD(cc[i + 3*k*ido], ti2); + v4sf cr3{VMUL(vtaui, VSUB(cc[i - 1 + (3*k + 2)*ido], cc[ic - 1 + (3*k + 1)*ido]))}; + v4sf ci3{VMUL(vtaui, VADD(cc[i + (3*k + 2)*ido], cc[ic + (3*k + 1)*ido]))}; + v4sf dr2{VSUB(cr2, ci3)}; + v4sf dr3{VADD(cr2, ci3)}; + v4sf di2{VADD(ci2, cr3)}; + v4sf di3{VSUB(ci2, cr3)}; + vcplxmul(dr2, di2, LD_PS1(wa1[i-2]), LD_PS1(wa1[i-1])); + ch[i - 1 + (k + l1)*ido] = dr2; + ch[i + (k + l1)*ido] = di2; + vcplxmul(dr3, di3, LD_PS1(wa2[i-2]), LD_PS1(wa2[i-1])); + ch[i - 1 + (k + 2*l1)*ido] = dr3; + ch[i + (k + 2*l1)*ido] = di3; + } + } +} /* radb3 */ + +NOINLINE void radf4_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, + v4sf *RESTRICT ch, const float *RESTRICT wa1, const float *RESTRICT wa2, + const float *RESTRICT wa3) +{ + const size_t l1ido{l1*ido}; + { + const v4sf *RESTRICT cc_{cc}, *RESTRICT cc_end{cc + l1ido}; + v4sf *RESTRICT ch_{ch}; + while(cc != cc_end) + { + // this loop represents between 25% and 40% of total radf4_ps cost ! + v4sf a0{cc[0]}, a1{cc[l1ido]}; + v4sf a2{cc[2*l1ido]}, a3{cc[3*l1ido]}; + v4sf tr1{VADD(a1, a3)}; + v4sf tr2{VADD(a0, a2)}; + ch[2*ido-1] = VSUB(a0, a2); + ch[2*ido ] = VSUB(a3, a1); + ch[0 ] = VADD(tr1, tr2); + ch[4*ido-1] = VSUB(tr2, tr1); + cc += ido; ch += 4*ido; + } + cc = cc_; + ch = ch_; + } + if(ido < 2) + return; + if(ido != 2) + { + for(size_t k{0};k < l1ido;k += ido) + { + const v4sf *RESTRICT pc{cc + 1 + k}; + for(size_t i{2};i < ido;i += 2, pc += 2) + { + const size_t ic{ido - i}; + + v4sf cr2{pc[1*l1ido+0]}; + v4sf ci2{pc[1*l1ido+1]}; + v4sf wr{LD_PS1(wa1[i - 2])}; + v4sf wi{LD_PS1(wa1[i - 1])}; + vcplxmulconj(cr2,ci2,wr,wi); + + v4sf cr3{pc[2*l1ido+0]}; + v4sf ci3{pc[2*l1ido+1]}; + wr = LD_PS1(wa2[i-2]); + wi = LD_PS1(wa2[i-1]); + vcplxmulconj(cr3, ci3, wr, wi); + + v4sf cr4{pc[3*l1ido]}; + v4sf ci4{pc[3*l1ido+1]}; + wr = LD_PS1(wa3[i-2]); + wi = LD_PS1(wa3[i-1]); + vcplxmulconj(cr4, ci4, wr, wi); + + /* at this point, on SSE, five of "cr2 cr3 cr4 ci2 ci3 ci4" should be loaded in registers */ + + v4sf tr1{VADD(cr2,cr4)}; + v4sf tr4{VSUB(cr4,cr2)}; + v4sf tr2{VADD(pc[0],cr3)}; + v4sf tr3{VSUB(pc[0],cr3)}; + ch[i - 1 + 4*k ] = VADD(tr2,tr1); + ch[ic - 1 + 4*k + 3*ido] = VSUB(tr2,tr1); // at this point tr1 and tr2 can be disposed + v4sf ti1{VADD(ci2,ci4)}; + v4sf ti4{VSUB(ci2,ci4)}; + ch[i - 1 + 4*k + 2*ido] = VADD(tr3,ti4); + ch[ic - 1 + 4*k + 1*ido] = VSUB(tr3,ti4); // dispose tr3, ti4 + v4sf ti2{VADD(pc[1],ci3)}; + v4sf ti3{VSUB(pc[1],ci3)}; + ch[i + 4*k ] = VADD(ti1, ti2); + ch[ic + 4*k + 3*ido] = VSUB(ti1, ti2); + ch[i + 4*k + 2*ido] = VADD(tr4, ti3); + ch[ic + 4*k + 1*ido] = VSUB(tr4, ti3); + } + } + if((ido&1) == 1) + return; + } + const v4sf minus_hsqt2{LD_PS1(al::numbers::sqrt2_v<float> * -0.5f)}; + for(size_t k{0};k < l1ido;k += ido) + { + v4sf a{cc[ido-1 + k + l1ido]}, b{cc[ido-1 + k + 3*l1ido]}; + v4sf c{cc[ido-1 + k]}, d{cc[ido-1 + k + 2*l1ido]}; + v4sf ti1{VMUL(minus_hsqt2, VADD(b, a))}; + v4sf tr1{VMUL(minus_hsqt2, VSUB(b, a))}; + ch[ido-1 + 4*k ] = VADD(c, tr1); + ch[ido-1 + 4*k + 2*ido] = VSUB(c, tr1); + ch[ 4*k + 1*ido] = VSUB(ti1, d); + ch[ 4*k + 3*ido] = VADD(ti1, d); + } +} /* radf4 */ + + +NOINLINE void radb4_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, + v4sf *RESTRICT ch, const float *RESTRICT wa1, const float *RESTRICT wa2, + const float *RESTRICT wa3) +{ + const v4sf two{LD_PS1(2.0f)}; + const size_t l1ido{l1*ido}; + { + const v4sf *RESTRICT cc_{cc}, *RESTRICT ch_end{ch + l1ido}; + v4sf *ch_{ch}; + while(ch != ch_end) + { + v4sf a{cc[0]}, b{cc[4*ido-1]}; + v4sf c{cc[2*ido]}, d{cc[2*ido-1]}; + v4sf tr3{VMUL(two,d)}; + v4sf tr2{VADD(a,b)}; + v4sf tr1{VSUB(a,b)}; + v4sf tr4{VMUL(two,c)}; + ch[0*l1ido] = VADD(tr2, tr3); + ch[2*l1ido] = VSUB(tr2, tr3); + ch[1*l1ido] = VSUB(tr1, tr4); + ch[3*l1ido] = VADD(tr1, tr4); + + cc += 4*ido; ch += ido; + } + cc = cc_; ch = ch_; + } + if(ido < 2) + return; + if(ido != 2) + { + for(size_t k{0};k < l1ido;k += ido) + { + const v4sf *RESTRICT pc{cc - 1 + 4*k}; + v4sf *RESTRICT ph{ch + k + 1}; + for(size_t i{2};i < ido;i += 2) + { + v4sf tr1{VSUB(pc[ i], pc[4*ido - i])}; + v4sf tr2{VADD(pc[ i], pc[4*ido - i])}; + v4sf ti4{VSUB(pc[2*ido + i], pc[2*ido - i])}; + v4sf tr3{VADD(pc[2*ido + i], pc[2*ido - i])}; + ph[0] = VADD(tr2, tr3); + v4sf cr3{VSUB(tr2, tr3)}; + + v4sf ti3{VSUB(pc[2*ido + i + 1], pc[2*ido - i + 1])}; + v4sf tr4{VADD(pc[2*ido + i + 1], pc[2*ido - i + 1])}; + v4sf cr2{VSUB(tr1, tr4)}; + v4sf cr4{VADD(tr1, tr4)}; + + v4sf ti1{VADD(pc[i + 1], pc[4*ido - i + 1])}; + v4sf ti2{VSUB(pc[i + 1], pc[4*ido - i + 1])}; + + ph[1] = VADD(ti2, ti3); ph += l1ido; + v4sf ci3{VSUB(ti2, ti3)}; + v4sf ci2{VADD(ti1, ti4)}; + v4sf ci4{VSUB(ti1, ti4)}; + vcplxmul(cr2, ci2, LD_PS1(wa1[i-2]), LD_PS1(wa1[i-1])); + ph[0] = cr2; + ph[1] = ci2; ph += l1ido; + vcplxmul(cr3, ci3, LD_PS1(wa2[i-2]), LD_PS1(wa2[i-1])); + ph[0] = cr3; + ph[1] = ci3; ph += l1ido; + vcplxmul(cr4, ci4, LD_PS1(wa3[i-2]), LD_PS1(wa3[i-1])); + ph[0] = cr4; + ph[1] = ci4; ph = ph - 3*l1ido + 2; + } + } + if((ido&1) == 1) + return; + } + const v4sf minus_sqrt2{LD_PS1(-1.414213562373095f)}; + for(size_t k{0};k < l1ido;k += ido) + { + const size_t i0{4*k + ido}; + v4sf c{cc[i0-1]}, d{cc[i0 + 2*ido-1]}; + v4sf a{cc[i0+0]}, b{cc[i0 + 2*ido+0]}; + v4sf tr1{VSUB(c,d)}; + v4sf tr2{VADD(c,d)}; + v4sf ti1{VADD(b,a)}; + v4sf ti2{VSUB(b,a)}; + ch[ido-1 + k + 0*l1ido] = VADD(tr2,tr2); + ch[ido-1 + k + 1*l1ido] = VMUL(minus_sqrt2, VSUB(ti1, tr1)); + ch[ido-1 + k + 2*l1ido] = VADD(ti2, ti2); + ch[ido-1 + k + 3*l1ido] = VMUL(minus_sqrt2, VADD(ti1, tr1)); + } +} /* radb4 */ + +void radf5_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, v4sf *RESTRICT ch, + const float *wa1, const float *wa2, const float *wa3, const float *wa4) +{ + const v4sf tr11{LD_PS1(0.309016994374947f)}; + const v4sf ti11{LD_PS1(0.951056516295154f)}; + const v4sf tr12{LD_PS1(-0.809016994374947f)}; + const v4sf ti12{LD_PS1(0.587785252292473f)}; + +#define cc_ref(a_1,a_2,a_3) cc[((a_3)*l1 + (a_2))*ido + a_1] +#define ch_ref(a_1,a_2,a_3) ch[((a_3)*5 + (a_2))*ido + a_1] + + /* Parameter adjustments */ + ch -= 1 + ido * 6; + cc -= 1 + ido * (1 + l1); + + /* Function Body */ + for(size_t k{1};k <= l1;++k) + { + v4sf cr2{VADD(cc_ref(1, k, 5), cc_ref(1, k, 2))}; + v4sf ci5{VSUB(cc_ref(1, k, 5), cc_ref(1, k, 2))}; + v4sf cr3{VADD(cc_ref(1, k, 4), cc_ref(1, k, 3))}; + v4sf ci4{VSUB(cc_ref(1, k, 4), cc_ref(1, k, 3))}; + ch_ref(1, 1, k) = VADD(cc_ref(1, k, 1), VADD(cr2, cr3)); + ch_ref(ido, 2, k) = VADD(cc_ref(1, k, 1), VMADD(tr11, cr2, VMUL(tr12, cr3))); + ch_ref(1, 3, k) = VMADD(ti11, ci5, VMUL(ti12, ci4)); + ch_ref(ido, 4, k) = VADD(cc_ref(1, k, 1), VMADD(tr12, cr2, VMUL(tr11, cr3))); + ch_ref(1, 5, k) = VSUB(VMUL(ti12, ci5), VMUL(ti11, ci4)); + //printf("pffft: radf5, k=%d ch_ref=%f, ci4=%f\n", k, ch_ref(1, 5, k), ci4); + } + if(ido == 1) + return; + + const size_t idp2{ido + 2}; + for(size_t k{1};k <= l1;++k) + { + for(size_t i{3};i <= ido;i += 2) + { + const size_t ic{idp2 - i}; + v4sf dr2{LD_PS1(wa1[i-3])}; + v4sf di2{LD_PS1(wa1[i-2])}; + v4sf dr3{LD_PS1(wa2[i-3])}; + v4sf di3{LD_PS1(wa2[i-2])}; + v4sf dr4{LD_PS1(wa3[i-3])}; + v4sf di4{LD_PS1(wa3[i-2])}; + v4sf dr5{LD_PS1(wa4[i-3])}; + v4sf di5{LD_PS1(wa4[i-2])}; + vcplxmulconj(dr2, di2, cc_ref(i-1, k, 2), cc_ref(i, k, 2)); + vcplxmulconj(dr3, di3, cc_ref(i-1, k, 3), cc_ref(i, k, 3)); + vcplxmulconj(dr4, di4, cc_ref(i-1, k, 4), cc_ref(i, k, 4)); + vcplxmulconj(dr5, di5, cc_ref(i-1, k, 5), cc_ref(i, k, 5)); + v4sf cr2{VADD(dr2, dr5)}; + v4sf ci5{VSUB(dr5, dr2)}; + v4sf cr5{VSUB(di2, di5)}; + v4sf ci2{VADD(di2, di5)}; + v4sf cr3{VADD(dr3, dr4)}; + v4sf ci4{VSUB(dr4, dr3)}; + v4sf cr4{VSUB(di3, di4)}; + v4sf ci3{VADD(di3, di4)}; + ch_ref(i - 1, 1, k) = VADD(cc_ref(i - 1, k, 1), VADD(cr2, cr3)); + ch_ref(i, 1, k) = VSUB(cc_ref(i, k, 1), VADD(ci2, ci3)); + v4sf tr2{VADD(cc_ref(i - 1, k, 1), VMADD(tr11, cr2, VMUL(tr12, cr3)))}; + v4sf ti2{VSUB(cc_ref(i, k, 1), VMADD(tr11, ci2, VMUL(tr12, ci3)))}; + v4sf tr3{VADD(cc_ref(i - 1, k, 1), VMADD(tr12, cr2, VMUL(tr11, cr3)))}; + v4sf ti3{VSUB(cc_ref(i, k, 1), VMADD(tr12, ci2, VMUL(tr11, ci3)))}; + v4sf tr5{VMADD(ti11, cr5, VMUL(ti12, cr4))}; + v4sf ti5{VMADD(ti11, ci5, VMUL(ti12, ci4))}; + v4sf tr4{VSUB(VMUL(ti12, cr5), VMUL(ti11, cr4))}; + v4sf ti4{VSUB(VMUL(ti12, ci5), VMUL(ti11, ci4))}; + ch_ref(i - 1, 3, k) = VSUB(tr2, tr5); + ch_ref(ic - 1, 2, k) = VADD(tr2, tr5); + ch_ref(i , 3, k) = VADD(ti5, ti2); + ch_ref(ic , 2, k) = VSUB(ti5, ti2); + ch_ref(i - 1, 5, k) = VSUB(tr3, tr4); + ch_ref(ic - 1, 4, k) = VADD(tr3, tr4); + ch_ref(i , 5, k) = VADD(ti4, ti3); + ch_ref(ic , 4, k) = VSUB(ti4, ti3); + } + } +#undef cc_ref +#undef ch_ref +} /* radf5 */ + +void radb5_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, v4sf *RESTRICT ch, + const float *wa1, const float *wa2, const float *wa3, const float *wa4) +{ + const v4sf tr11{LD_PS1(0.309016994374947f)}; + const v4sf ti11{LD_PS1(0.951056516295154f)}; + const v4sf tr12{LD_PS1(-0.809016994374947f)}; + const v4sf ti12{LD_PS1(0.587785252292473f)}; + +#define cc_ref(a_1,a_2,a_3) cc[((a_3)*5 + (a_2))*ido + a_1] +#define ch_ref(a_1,a_2,a_3) ch[((a_3)*l1 + (a_2))*ido + a_1] + + /* Parameter adjustments */ + ch -= 1 + ido*(1 + l1); + cc -= 1 + ido*6; + + /* Function Body */ + for(size_t k{1};k <= l1;++k) + { + v4sf ti5{VADD(cc_ref( 1, 3, k), cc_ref(1, 3, k))}; + v4sf ti4{VADD(cc_ref( 1, 5, k), cc_ref(1, 5, k))}; + v4sf tr2{VADD(cc_ref(ido, 2, k), cc_ref(ido, 2, k))}; + v4sf tr3{VADD(cc_ref(ido, 4, k), cc_ref(ido, 4, k))}; + ch_ref(1, k, 1) = VADD(cc_ref(1, 1, k), VADD(tr2, tr3)); + v4sf cr2{VADD(cc_ref(1, 1, k), VMADD(tr11, tr2, VMUL(tr12, tr3)))}; + v4sf cr3{VADD(cc_ref(1, 1, k), VMADD(tr12, tr2, VMUL(tr11, tr3)))}; + v4sf ci5{VMADD(ti11, ti5, VMUL(ti12, ti4))}; + v4sf ci4{VSUB(VMUL(ti12, ti5), VMUL(ti11, ti4))}; + ch_ref(1, k, 2) = VSUB(cr2, ci5); + ch_ref(1, k, 3) = VSUB(cr3, ci4); + ch_ref(1, k, 4) = VADD(cr3, ci4); + ch_ref(1, k, 5) = VADD(cr2, ci5); + } + if(ido == 1) + return; + + const size_t idp2{ido + 2}; + for(size_t k{1};k <= l1;++k) + { + for(size_t i{3};i <= ido;i += 2) + { + const size_t ic{idp2 - i}; + v4sf ti5{VADD(cc_ref(i , 3, k), cc_ref(ic , 2, k))}; + v4sf ti2{VSUB(cc_ref(i , 3, k), cc_ref(ic , 2, k))}; + v4sf ti4{VADD(cc_ref(i , 5, k), cc_ref(ic , 4, k))}; + v4sf ti3{VSUB(cc_ref(i , 5, k), cc_ref(ic , 4, k))}; + v4sf tr5{VSUB(cc_ref(i-1, 3, k), cc_ref(ic-1, 2, k))}; + v4sf tr2{VADD(cc_ref(i-1, 3, k), cc_ref(ic-1, 2, k))}; + v4sf tr4{VSUB(cc_ref(i-1, 5, k), cc_ref(ic-1, 4, k))}; + v4sf tr3{VADD(cc_ref(i-1, 5, k), cc_ref(ic-1, 4, k))}; + ch_ref(i - 1, k, 1) = VADD(cc_ref(i-1, 1, k), VADD(tr2, tr3)); + ch_ref(i , k, 1) = VADD(cc_ref(i , 1, k), VADD(ti2, ti3)); + v4sf cr2{VADD(cc_ref(i-1, 1, k), VMADD(tr11, tr2, VMUL(tr12, tr3)))}; + v4sf ci2{VADD(cc_ref(i , 1, k), VMADD(tr11, ti2, VMUL(tr12, ti3)))}; + v4sf cr3{VADD(cc_ref(i-1, 1, k), VMADD(tr12, tr2, VMUL(tr11, tr3)))}; + v4sf ci3{VADD(cc_ref(i , 1, k), VMADD(tr12, ti2, VMUL(tr11, ti3)))}; + v4sf cr5{VMADD(ti11, tr5, VMUL(ti12, tr4))}; + v4sf ci5{VMADD(ti11, ti5, VMUL(ti12, ti4))}; + v4sf cr4{VSUB(VMUL(ti12, tr5), VMUL(ti11, tr4))}; + v4sf ci4{VSUB(VMUL(ti12, ti5), VMUL(ti11, ti4))}; + v4sf dr3{VSUB(cr3, ci4)}; + v4sf dr4{VADD(cr3, ci4)}; + v4sf di3{VADD(ci3, cr4)}; + v4sf di4{VSUB(ci3, cr4)}; + v4sf dr5{VADD(cr2, ci5)}; + v4sf dr2{VSUB(cr2, ci5)}; + v4sf di5{VSUB(ci2, cr5)}; + v4sf di2{VADD(ci2, cr5)}; + vcplxmul(dr2, di2, LD_PS1(wa1[i-3]), LD_PS1(wa1[i-2])); + vcplxmul(dr3, di3, LD_PS1(wa2[i-3]), LD_PS1(wa2[i-2])); + vcplxmul(dr4, di4, LD_PS1(wa3[i-3]), LD_PS1(wa3[i-2])); + vcplxmul(dr5, di5, LD_PS1(wa4[i-3]), LD_PS1(wa4[i-2])); + + ch_ref(i-1, k, 2) = dr2; ch_ref(i, k, 2) = di2; + ch_ref(i-1, k, 3) = dr3; ch_ref(i, k, 3) = di3; + ch_ref(i-1, k, 4) = dr4; ch_ref(i, k, 4) = di4; + ch_ref(i-1, k, 5) = dr5; ch_ref(i, k, 5) = di5; + } + } +#undef cc_ref +#undef ch_ref +} /* radb5 */ + +NOINLINE v4sf *rfftf1_ps(const size_t n, const v4sf *input_readonly, v4sf *work1, v4sf *work2, + const float *wa, const al::span<const uint,15> ifac) +{ + assert(work1 != work2); + + const v4sf *in{input_readonly}; + v4sf *out{in == work2 ? work1 : work2}; + const size_t nf{ifac[1]}; + size_t l2{n}; + size_t iw{n-1}; + for(size_t k1{1};k1 <= nf;++k1) + { + size_t kh{nf - k1}; + size_t ip{ifac[kh + 2]}; + size_t l1{l2 / ip}; + size_t ido{n / l2}; + iw -= (ip - 1)*ido; + switch(ip) + { + case 5: + { + size_t ix2{iw + ido}; + size_t ix3{ix2 + ido}; + size_t ix4{ix3 + ido}; + radf5_ps(ido, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4]); + } + break; + case 4: + { + size_t ix2{iw + ido}; + size_t ix3{ix2 + ido}; + radf4_ps(ido, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3]); + } + break; + case 3: + { + size_t ix2{iw + ido}; + radf3_ps(ido, l1, in, out, &wa[iw], &wa[ix2]); + } + break; + case 2: + radf2_ps(ido, l1, in, out, &wa[iw]); + break; + default: + assert(0); + break; + } + l2 = l1; + if(out == work2) + { + out = work1; + in = work2; + } + else + { + out = work2; + in = work1; + } + } + return const_cast<v4sf*>(in); /* this is in fact the output .. */ +} /* rfftf1 */ + +NOINLINE v4sf *rfftb1_ps(const size_t n, const v4sf *input_readonly, v4sf *work1, v4sf *work2, + const float *wa, const al::span<const uint,15> ifac) +{ + assert(work1 != work2); + + const v4sf *in{input_readonly}; + v4sf *out{in == work2 ? work1 : work2}; + const size_t nf{ifac[1]}; + size_t l1{1}; + size_t iw{0}; + for(size_t k1{1};k1 <= nf;++k1) + { + size_t ip{ifac[k1 + 1]}; + size_t l2{ip*l1}; + size_t ido{n / l2}; + switch(ip) + { + case 5: + { + size_t ix2{iw + ido}; + size_t ix3{ix2 + ido}; + size_t ix4{ix3 + ido}; + radb5_ps(ido, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4]); + } + break; + case 4: + { + size_t ix2{iw + ido}; + size_t ix3{ix2 + ido}; + radb4_ps(ido, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3]); + } + break; + case 3: + { + size_t ix2{iw + ido}; + radb3_ps(ido, l1, in, out, &wa[iw], &wa[ix2]); + } + break; + case 2: + radb2_ps(ido, l1, in, out, &wa[iw]); + break; + default: + assert(0); + break; + } + l1 = l2; + iw += (ip - 1)*ido; + + if(out == work2) + { + out = work1; + in = work2; + } + else + { + out = work2; + in = work1; + } + } + return const_cast<v4sf*>(in); /* this is in fact the output .. */ +} + +v4sf *cfftf1_ps(const size_t n, const v4sf *input_readonly, v4sf *work1, v4sf *work2, + const float *wa, const al::span<const uint,15> ifac, const float fsign) +{ + assert(work1 != work2); + + const v4sf *in{input_readonly}; + v4sf *out{in == work2 ? work1 : work2}; + const size_t nf{ifac[1]}; + size_t l1{1}, iw{0}; + for(size_t k1{2};k1 <= nf+1;++k1) + { + const size_t ip{ifac[k1]}; + const size_t l2{ip*l1}; + const size_t ido{n / l2}; + const size_t idot{ido + ido}; + switch(ip) + { + case 5: + { + size_t ix2{iw + idot}; + size_t ix3{ix2 + idot}; + size_t ix4{ix3 + idot}; + passf5_ps(idot, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4], fsign); + } + break; + case 4: + { + size_t ix2{iw + idot}; + size_t ix3{ix2 + idot}; + passf4_ps(idot, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3], fsign); + } + break; + case 3: + { + size_t ix2{iw + idot}; + passf3_ps(idot, l1, in, out, &wa[iw], &wa[ix2], fsign); + } + break; + case 2: + passf2_ps(idot, l1, in, out, &wa[iw], fsign); + break; + default: + assert(0); + } + l1 = l2; + iw += (ip - 1)*idot; + if(out == work2) + { + out = work1; + in = work2; + } + else + { + out = work2; + in = work1; + } + } + + return const_cast<v4sf*>(in); /* this is in fact the output .. */ +} + + +uint decompose(const uint n, const al::span<uint,15> ifac, const al::span<const uint,4> ntryh) +{ + uint nl{n}, nf{0}; + for(const uint ntry : ntryh) + { + while(nl != 1) + { + const uint nq{nl / ntry}; + const uint nr{nl % ntry}; + if(nr != 0) break; + + ifac[2+nf++] = ntry; + nl = nq; + if(ntry == 2 && nf != 1) + { + for(size_t i{2};i <= nf;++i) + { + size_t ib{nf - i + 2}; + ifac[ib + 1] = ifac[ib]; + } + ifac[2] = 2; + } + } + } + ifac[0] = n; + ifac[1] = nf; + return nf; +} + +void rffti1_ps(const uint n, float *wa, const al::span<uint,15> ifac) +{ + static constexpr uint ntryh[]{4,2,3,5}; + + const uint nf{decompose(n, ifac, ntryh)}; + const double argh{2.0*al::numbers::pi / n}; + size_t is{0}; + size_t nfm1{nf - 1}; + size_t l1{1}; + for(size_t k1{0};k1 < nfm1;++k1) + { + const size_t ip{ifac[k1+2]}; + const size_t l2{l1*ip}; + const size_t ido{n / l2}; + const size_t ipm{ip - 1}; + size_t ld{0}; + for(size_t j{0};j < ipm;++j) + { + size_t i{is}; + ld += l1; + const double argld{static_cast<double>(ld)*argh}; + double fi{0.0}; + for(size_t ii{2};ii < ido;ii += 2) + { + fi += 1.0; + wa[i++] = static_cast<float>(std::cos(fi*argld)); + wa[i++] = static_cast<float>(std::sin(fi*argld)); + } + is += ido; + } + l1 = l2; + } +} /* rffti1 */ + +void cffti1_ps(const uint n, float *wa, const al::span<uint,15> ifac) +{ + static constexpr uint ntryh[]{5,3,4,2}; + + const uint nf{decompose(n, ifac, ntryh)}; + const double argh{2.0*al::numbers::pi / n}; + size_t i{1}; + size_t l1{1}; + for(size_t k1{0};k1 < nf;++k1) + { + const size_t ip{ifac[k1+2]}; + const size_t l2{l1*ip}; + const size_t ido{n / l2}; + const size_t idot{ido + ido + 2}; + const size_t ipm{ip - 1}; + size_t ld{0}; + for(size_t j{0};j < ipm;++j) + { + size_t i1{i}; + wa[i-1] = 1; + wa[i] = 0; + ld += l1; + const double argld{static_cast<double>(ld)*argh}; + double fi{0.0}; + for(size_t ii{3};ii < idot;ii += 2) + { + fi += 1.0; + wa[++i] = static_cast<float>(std::cos(fi*argld)); + wa[++i] = static_cast<float>(std::sin(fi*argld)); + } + if(ip > 5) + { + wa[i1-1] = wa[i-1]; + wa[i1] = wa[i]; + } + } + l1 = l2; + } +} /* cffti1 */ + +} // namespace + +void *pffft_aligned_malloc(size_t nb_bytes) +{ return al_malloc(MALLOC_V4SF_ALIGNMENT, nb_bytes); } + +void pffft_aligned_free(void *p) { al_free(p); } + +int pffft_simd_size() { return SIMD_SZ; } + +struct PFFFT_Setup { + uint N; + uint Ncvec; // nb of complex simd vectors (N/4 if PFFFT_COMPLEX, N/8 if PFFFT_REAL) + std::array<uint,15> ifac; + pffft_transform_t transform; + + float *twiddle; // N/4 elements + alignas(MALLOC_V4SF_ALIGNMENT) v4sf e[1]; // N/4*3 elements +}; + +PFFFT_Setup *pffft_new_setup(unsigned int N, pffft_transform_t transform) +{ + assert(transform == PFFFT_REAL || transform == PFFFT_COMPLEX); + assert(N > 0); + /* unfortunately, the fft size must be a multiple of 16 for complex FFTs + * and 32 for real FFTs -- a lot of stuff would need to be rewritten to + * handle other cases (or maybe just switch to a scalar fft, I don't know..) + */ + if(transform == PFFFT_REAL) + assert((N%(2*SIMD_SZ*SIMD_SZ)) == 0); + else + assert((N%(SIMD_SZ*SIMD_SZ)) == 0); + + const uint Ncvec = (transform == PFFFT_REAL ? N/2 : N)/SIMD_SZ; + const size_t storelen{offsetof(PFFFT_Setup, e[0]) + (2u*Ncvec * sizeof(v4sf))}; + + void *store{al_calloc(MALLOC_V4SF_ALIGNMENT, storelen)}; + if(!store) return nullptr; + + PFFFT_Setup *s{::new(store) PFFFT_Setup{}}; + s->N = N; + s->transform = transform; + /* nb of complex simd vectors */ + s->Ncvec = Ncvec; + s->twiddle = reinterpret_cast<float*>(&s->e[2u*Ncvec*(SIMD_SZ-1)/SIMD_SZ]); + + if constexpr(SIMD_SZ > 1) + { + auto e = std::vector<float>(2u*Ncvec*(SIMD_SZ-1), 0.0f); + for(size_t k{0};k < s->Ncvec;++k) + { + const size_t i{k / SIMD_SZ}; + const size_t j{k % SIMD_SZ}; + for(size_t m{0};m < SIMD_SZ-1;++m) + { + const double A{-2.0*al::numbers::pi*static_cast<double>((m+1)*k) / N}; + e[((i*3 + m)*2 + 0)*SIMD_SZ + j] = static_cast<float>(std::cos(A)); + e[((i*3 + m)*2 + 1)*SIMD_SZ + j] = static_cast<float>(std::sin(A)); + } + } + std::memcpy(s->e, e.data(), e.size()*sizeof(float)); + } + if(transform == PFFFT_REAL) + rffti1_ps(N/SIMD_SZ, s->twiddle, s->ifac); + else + cffti1_ps(N/SIMD_SZ, s->twiddle, s->ifac); + + /* check that N is decomposable with allowed prime factors */ + size_t m{1}; + for(size_t k{0};k < s->ifac[1];++k) + m *= s->ifac[2+k]; + + if(m != N/SIMD_SZ) + { + pffft_destroy_setup(s); + s = nullptr; + } + + return s; +} + + +void pffft_destroy_setup(PFFFT_Setup *s) +{ + std::destroy_at(s); + al_free(s); +} + +#if !defined(PFFFT_SIMD_DISABLE) + +namespace { + +/* [0 0 1 2 3 4 5 6 7 8] -> [0 8 7 6 5 4 3 2 1] */ +void reversed_copy(const size_t N, const v4sf *in, const int in_stride, v4sf *RESTRICT out) +{ + v4sf g0, g1; + interleave2(in[0], in[1], g0, g1); + in += in_stride; + + *--out = VSWAPHL(g0, g1); // [g0l, g0h], [g1l g1h] -> [g1l, g0h] + for(size_t k{1};k < N;++k) + { + v4sf h0, h1; + interleave2(in[0], in[1], h0, h1); + in += in_stride; + *--out = VSWAPHL(g1, h0); + *--out = VSWAPHL(h0, h1); + g1 = h1; + } + *--out = VSWAPHL(g1, g0); +} + +void unreversed_copy(const size_t N, const v4sf *in, v4sf *RESTRICT out, const int out_stride) +{ + v4sf g0{in[0]}, g1{g0}; + ++in; + for(size_t k{1};k < N;++k) + { + v4sf h0{*in++}; v4sf h1{*in++}; + g1 = VSWAPHL(g1, h0); + h0 = VSWAPHL(h0, h1); + uninterleave2(h0, g1, out[0], out[1]); + out += out_stride; + g1 = h1; + } + v4sf h0{*in++}, h1{g0}; + g1 = VSWAPHL(g1, h0); + h0 = VSWAPHL(h0, h1); + uninterleave2(h0, g1, out[0], out[1]); +} + +void pffft_cplx_finalize(const size_t Ncvec, const v4sf *in, v4sf *RESTRICT out, const v4sf *e) +{ + assert(in != out); + + const size_t dk{Ncvec/SIMD_SZ}; // number of 4x4 matrix blocks + for(size_t k{0};k < dk;++k) + { + v4sf r0{in[8*k+0]}, i0{in[8*k+1]}; + v4sf r1{in[8*k+2]}, i1{in[8*k+3]}; + v4sf r2{in[8*k+4]}, i2{in[8*k+5]}; + v4sf r3{in[8*k+6]}, i3{in[8*k+7]}; + vtranspose4(r0,r1,r2,r3); + vtranspose4(i0,i1,i2,i3); + vcplxmul(r1,i1,e[k*6+0],e[k*6+1]); + vcplxmul(r2,i2,e[k*6+2],e[k*6+3]); + vcplxmul(r3,i3,e[k*6+4],e[k*6+5]); + + v4sf sr0{VADD(r0,r2)}, dr0{VSUB(r0, r2)}; + v4sf sr1{VADD(r1,r3)}, dr1{VSUB(r1, r3)}; + v4sf si0{VADD(i0,i2)}, di0{VSUB(i0, i2)}; + v4sf si1{VADD(i1,i3)}, di1{VSUB(i1, i3)}; + + /* transformation for each column is: + * + * [1 1 1 1 0 0 0 0] [r0] + * [1 0 -1 0 0 -1 0 1] [r1] + * [1 -1 1 -1 0 0 0 0] [r2] + * [1 0 -1 0 0 1 0 -1] [r3] + * [0 0 0 0 1 1 1 1] * [i0] + * [0 1 0 -1 1 0 -1 0] [i1] + * [0 0 0 0 1 -1 1 -1] [i2] + * [0 -1 0 1 1 0 -1 0] [i3] + */ + + r0 = VADD(sr0, sr1); i0 = VADD(si0, si1); + r1 = VADD(dr0, di1); i1 = VSUB(di0, dr1); + r2 = VSUB(sr0, sr1); i2 = VSUB(si0, si1); + r3 = VSUB(dr0, di1); i3 = VADD(di0, dr1); + + *out++ = r0; *out++ = i0; *out++ = r1; *out++ = i1; + *out++ = r2; *out++ = i2; *out++ = r3; *out++ = i3; + } +} + +void pffft_cplx_preprocess(const size_t Ncvec, const v4sf *in, v4sf *RESTRICT out, const v4sf *e) +{ + assert(in != out); + + const size_t dk{Ncvec/SIMD_SZ}; // number of 4x4 matrix blocks + for(size_t k{0};k < dk;++k) + { + v4sf r0{in[8*k+0]}, i0{in[8*k+1]}; + v4sf r1{in[8*k+2]}, i1{in[8*k+3]}; + v4sf r2{in[8*k+4]}, i2{in[8*k+5]}; + v4sf r3{in[8*k+6]}, i3{in[8*k+7]}; + + v4sf sr0{VADD(r0,r2)}, dr0{VSUB(r0, r2)}; + v4sf sr1{VADD(r1,r3)}, dr1{VSUB(r1, r3)}; + v4sf si0{VADD(i0,i2)}, di0{VSUB(i0, i2)}; + v4sf si1{VADD(i1,i3)}, di1{VSUB(i1, i3)}; + + r0 = VADD(sr0, sr1); i0 = VADD(si0, si1); + r1 = VSUB(dr0, di1); i1 = VADD(di0, dr1); + r2 = VSUB(sr0, sr1); i2 = VSUB(si0, si1); + r3 = VADD(dr0, di1); i3 = VSUB(di0, dr1); + + vcplxmulconj(r1,i1,e[k*6+0],e[k*6+1]); + vcplxmulconj(r2,i2,e[k*6+2],e[k*6+3]); + vcplxmulconj(r3,i3,e[k*6+4],e[k*6+5]); + + vtranspose4(r0,r1,r2,r3); + vtranspose4(i0,i1,i2,i3); + + *out++ = r0; *out++ = i0; *out++ = r1; *out++ = i1; + *out++ = r2; *out++ = i2; *out++ = r3; *out++ = i3; + } +} + + +force_inline void pffft_real_finalize_4x4(const v4sf *in0, const v4sf *in1, const v4sf *in, + const v4sf *e, v4sf *RESTRICT out) +{ + v4sf r0{*in0}, i0{*in1}; + v4sf r1{*in++}; v4sf i1{*in++}; + v4sf r2{*in++}; v4sf i2{*in++}; + v4sf r3{*in++}; v4sf i3{*in++}; + vtranspose4(r0,r1,r2,r3); + vtranspose4(i0,i1,i2,i3); + + /* transformation for each column is: + * + * [1 1 1 1 0 0 0 0] [r0] + * [1 0 -1 0 0 -1 0 1] [r1] + * [1 0 -1 0 0 1 0 -1] [r2] + * [1 -1 1 -1 0 0 0 0] [r3] + * [0 0 0 0 1 1 1 1] * [i0] + * [0 -1 0 1 -1 0 1 0] [i1] + * [0 -1 0 1 1 0 -1 0] [i2] + * [0 0 0 0 -1 1 -1 1] [i3] + */ + + //cerr << "matrix initial, before e , REAL:\n 1: " << r0 << "\n 1: " << r1 << "\n 1: " << r2 << "\n 1: " << r3 << "\n"; + //cerr << "matrix initial, before e, IMAG :\n 1: " << i0 << "\n 1: " << i1 << "\n 1: " << i2 << "\n 1: " << i3 << "\n"; + + vcplxmul(r1,i1,e[0],e[1]); + vcplxmul(r2,i2,e[2],e[3]); + vcplxmul(r3,i3,e[4],e[5]); + + //cerr << "matrix initial, real part:\n 1: " << r0 << "\n 1: " << r1 << "\n 1: " << r2 << "\n 1: " << r3 << "\n"; + //cerr << "matrix initial, imag part:\n 1: " << i0 << "\n 1: " << i1 << "\n 1: " << i2 << "\n 1: " << i3 << "\n"; + + v4sf sr0{VADD(r0,r2)}, dr0{VSUB(r0,r2)}; + v4sf sr1{VADD(r1,r3)}, dr1{VSUB(r3,r1)}; + v4sf si0{VADD(i0,i2)}, di0{VSUB(i0,i2)}; + v4sf si1{VADD(i1,i3)}, di1{VSUB(i3,i1)}; + + r0 = VADD(sr0, sr1); + r3 = VSUB(sr0, sr1); + i0 = VADD(si0, si1); + i3 = VSUB(si1, si0); + r1 = VADD(dr0, di1); + r2 = VSUB(dr0, di1); + i1 = VSUB(dr1, di0); + i2 = VADD(dr1, di0); + + *out++ = r0; + *out++ = i0; + *out++ = r1; + *out++ = i1; + *out++ = r2; + *out++ = i2; + *out++ = r3; + *out++ = i3; +} + +NOINLINE void pffft_real_finalize(const size_t Ncvec, const v4sf *in, v4sf *RESTRICT out, + const v4sf *e) +{ + static constexpr float s{al::numbers::sqrt2_v<float>/2.0f}; + + assert(in != out); + const size_t dk{Ncvec/SIMD_SZ}; // number of 4x4 matrix blocks + /* fftpack order is f0r f1r f1i f2r f2i ... f(n-1)r f(n-1)i f(n)r */ + + const v4sf zero{VZERO()}; + const auto cr = al::bit_cast<std::array<float,SIMD_SZ>>(in[0]); + const auto ci = al::bit_cast<std::array<float,SIMD_SZ>>(in[Ncvec*2-1]); + pffft_real_finalize_4x4(&zero, &zero, in+1, e, out); + + /* [cr0 cr1 cr2 cr3 ci0 ci1 ci2 ci3] + * + * [Xr(1)] ] [1 1 1 1 0 0 0 0] + * [Xr(N/4) ] [0 0 0 0 1 s 0 -s] + * [Xr(N/2) ] [1 0 -1 0 0 0 0 0] + * [Xr(3N/4)] [0 0 0 0 1 -s 0 s] + * [Xi(1) ] [1 -1 1 -1 0 0 0 0] + * [Xi(N/4) ] [0 0 0 0 0 -s -1 -s] + * [Xi(N/2) ] [0 -1 0 1 0 0 0 0] + * [Xi(3N/4)] [0 0 0 0 0 -s 1 -s] + */ + + const float xr0{(cr[0]+cr[2]) + (cr[1]+cr[3])}; out[0] = VINSERT0(out[0], xr0); + const float xi0{(cr[0]+cr[2]) - (cr[1]+cr[3])}; out[1] = VINSERT0(out[1], xi0); + const float xr2{(cr[0]-cr[2])}; out[4] = VINSERT0(out[4], xr2); + const float xi2{(cr[3]-cr[1])}; out[5] = VINSERT0(out[5], xi2); + const float xr1{ ci[0] + s*(ci[1]-ci[3])}; out[2] = VINSERT0(out[2], xr1); + const float xi1{-ci[2] - s*(ci[1]+ci[3])}; out[3] = VINSERT0(out[3], xi1); + const float xr3{ ci[0] - s*(ci[1]-ci[3])}; out[6] = VINSERT0(out[6], xr3); + const float xi3{ ci[2] - s*(ci[1]+ci[3])}; out[7] = VINSERT0(out[7], xi3); + + for(size_t k{1};k < dk;++k) + pffft_real_finalize_4x4(&in[8*k-1], &in[8*k+0], in + 8*k+1, e + k*6, out + k*8); +} + +force_inline void pffft_real_preprocess_4x4(const v4sf *in, const v4sf *e, v4sf *RESTRICT out, + const bool first) +{ + v4sf r0{in[0]}, i0{in[1]}, r1{in[2]}, i1{in[3]}; + v4sf r2{in[4]}, i2{in[5]}, r3{in[6]}, i3{in[7]}; + + /* transformation for each column is: + * + * [1 1 1 1 0 0 0 0] [r0] + * [1 0 0 -1 0 -1 -1 0] [r1] + * [1 -1 -1 1 0 0 0 0] [r2] + * [1 0 0 -1 0 1 1 0] [r3] + * [0 0 0 0 1 -1 1 -1] * [i0] + * [0 -1 1 0 1 0 0 1] [i1] + * [0 0 0 0 1 1 -1 -1] [i2] + * [0 1 -1 0 1 0 0 1] [i3] + */ + + v4sf sr0{VADD(r0,r3)}, dr0{VSUB(r0,r3)}; + v4sf sr1{VADD(r1,r2)}, dr1{VSUB(r1,r2)}; + v4sf si0{VADD(i0,i3)}, di0{VSUB(i0,i3)}; + v4sf si1{VADD(i1,i2)}, di1{VSUB(i1,i2)}; + + r0 = VADD(sr0, sr1); + r2 = VSUB(sr0, sr1); + r1 = VSUB(dr0, si1); + r3 = VADD(dr0, si1); + i0 = VSUB(di0, di1); + i2 = VADD(di0, di1); + i1 = VSUB(si0, dr1); + i3 = VADD(si0, dr1); + + vcplxmulconj(r1,i1,e[0],e[1]); + vcplxmulconj(r2,i2,e[2],e[3]); + vcplxmulconj(r3,i3,e[4],e[5]); + + vtranspose4(r0,r1,r2,r3); + vtranspose4(i0,i1,i2,i3); + + if(!first) + { + *out++ = r0; + *out++ = i0; + } + *out++ = r1; + *out++ = i1; + *out++ = r2; + *out++ = i2; + *out++ = r3; + *out++ = i3; +} + +NOINLINE void pffft_real_preprocess(const size_t Ncvec, const v4sf *in, v4sf *RESTRICT out, + const v4sf *e) +{ + static constexpr float sqrt2{al::numbers::sqrt2_v<float>}; + + assert(in != out); + const size_t dk{Ncvec/SIMD_SZ}; // number of 4x4 matrix blocks + /* fftpack order is f0r f1r f1i f2r f2i ... f(n-1)r f(n-1)i f(n)r */ + + std::array<float,SIMD_SZ> Xr, Xi; + for(size_t k{0};k < SIMD_SZ;++k) + { + Xr[k] = VEXTRACT0(in[2*k]); + Xi[k] = VEXTRACT0(in[2*k + 1]); + } + + pffft_real_preprocess_4x4(in, e, out+1, true); // will write only 6 values + + /* [Xr0 Xr1 Xr2 Xr3 Xi0 Xi1 Xi2 Xi3] + * + * [cr0] [1 0 2 0 1 0 0 0] + * [cr1] [1 0 0 0 -1 0 -2 0] + * [cr2] [1 0 -2 0 1 0 0 0] + * [cr3] [1 0 0 0 -1 0 2 0] + * [ci0] [0 2 0 2 0 0 0 0] + * [ci1] [0 s 0 -s 0 -s 0 -s] + * [ci2] [0 0 0 0 0 -2 0 2] + * [ci3] [0 -s 0 s 0 -s 0 -s] + */ + for(size_t k{1};k < dk;++k) + pffft_real_preprocess_4x4(in+8*k, e + k*6, out-1+k*8, false); + + const float cr0{(Xr[0]+Xi[0]) + 2*Xr[2]}; + const float cr1{(Xr[0]-Xi[0]) - 2*Xi[2]}; + const float cr2{(Xr[0]+Xi[0]) - 2*Xr[2]}; + const float cr3{(Xr[0]-Xi[0]) + 2*Xi[2]}; + out[0] = VSET4(cr0, cr1, cr2, cr3); + const float ci0{ 2*(Xr[1]+Xr[3])}; + const float ci1{ sqrt2*(Xr[1]-Xr[3]) - sqrt2*(Xi[1]+Xi[3])}; + const float ci2{ 2*(Xi[3]-Xi[1])}; + const float ci3{-sqrt2*(Xr[1]-Xr[3]) - sqrt2*(Xi[1]+Xi[3])}; + out[2*Ncvec-1] = VSET4(ci0, ci1, ci2, ci3); +} + + +void pffft_transform_internal(const PFFFT_Setup *setup, const v4sf *vinput, v4sf *voutput, + v4sf *scratch, const pffft_direction_t direction, const bool ordered) +{ + assert(scratch != nullptr); + assert(voutput != scratch); + + const size_t Ncvec{setup->Ncvec}; + const bool nf_odd{(setup->ifac[1]&1) != 0}; + + v4sf *buff[2]{voutput, scratch}; + bool ib{nf_odd != ordered}; + if(direction == PFFFT_FORWARD) + { + /* Swap the initial work buffer for forward FFTs, which helps avoid an + * extra copy for output. + */ + ib = !ib; + if(setup->transform == PFFFT_REAL) + { + ib = (rfftf1_ps(Ncvec*2, vinput, buff[ib], buff[!ib], setup->twiddle, setup->ifac) == buff[1]); + pffft_real_finalize(Ncvec, buff[ib], buff[!ib], setup->e); + } + else + { + v4sf *tmp{buff[ib]}; + for(size_t k=0; k < Ncvec; ++k) + uninterleave2(vinput[k*2], vinput[k*2+1], tmp[k*2], tmp[k*2+1]); + + ib = (cfftf1_ps(Ncvec, buff[ib], buff[!ib], buff[ib], setup->twiddle, setup->ifac, -1.0f) == buff[1]); + pffft_cplx_finalize(Ncvec, buff[ib], buff[!ib], setup->e); + } + if(ordered) + pffft_zreorder(setup, reinterpret_cast<float*>(buff[!ib]), + reinterpret_cast<float*>(buff[ib]), PFFFT_FORWARD); + else + ib = !ib; + } + else + { + if(vinput == buff[ib]) + ib = !ib; // may happen when finput == foutput + + if(ordered) + { + pffft_zreorder(setup, reinterpret_cast<const float*>(vinput), + reinterpret_cast<float*>(buff[ib]), PFFFT_BACKWARD); + vinput = buff[ib]; + ib = !ib; + } + if(setup->transform == PFFFT_REAL) + { + pffft_real_preprocess(Ncvec, vinput, buff[ib], setup->e); + ib = (rfftb1_ps(Ncvec*2, buff[ib], buff[0], buff[1], setup->twiddle, setup->ifac) == buff[1]); + } + else + { + pffft_cplx_preprocess(Ncvec, vinput, buff[ib], setup->e); + ib = (cfftf1_ps(Ncvec, buff[ib], buff[0], buff[1], setup->twiddle, setup->ifac, +1.0f) == buff[1]); + for(size_t k{0};k < Ncvec;++k) + interleave2(buff[ib][k*2], buff[ib][k*2+1], buff[ib][k*2], buff[ib][k*2+1]); + } + } + + if(buff[ib] != voutput) + { + /* extra copy required -- this situation should only happen when finput == foutput */ + assert(vinput==voutput); + for(size_t k{0};k < Ncvec;++k) + { + v4sf a{buff[ib][2*k]}, b{buff[ib][2*k+1]}; + voutput[2*k] = a; voutput[2*k+1] = b; + } + } +} + +} // namespace + +void pffft_zreorder(const PFFFT_Setup *setup, const float *in, float *out, + pffft_direction_t direction) +{ + assert(in != out); + + const size_t N{setup->N}, Ncvec{setup->Ncvec}; + const v4sf *vin{reinterpret_cast<const v4sf*>(in)}; + v4sf *RESTRICT vout{reinterpret_cast<v4sf*>(out)}; + if(setup->transform == PFFFT_REAL) + { + const size_t dk{N/32}; + if(direction == PFFFT_FORWARD) + { + for(size_t k{0};k < dk;++k) + { + interleave2(vin[k*8 + 0], vin[k*8 + 1], vout[2*(0*dk + k) + 0], vout[2*(0*dk + k) + 1]); + interleave2(vin[k*8 + 4], vin[k*8 + 5], vout[2*(2*dk + k) + 0], vout[2*(2*dk + k) + 1]); + } + reversed_copy(dk, vin+2, 8, vout + N/SIMD_SZ/2); + reversed_copy(dk, vin+6, 8, vout + N/SIMD_SZ); + } + else + { + for(size_t k{0};k < dk;++k) + { + uninterleave2(vin[2*(0*dk + k) + 0], vin[2*(0*dk + k) + 1], vout[k*8 + 0], vout[k*8 + 1]); + uninterleave2(vin[2*(2*dk + k) + 0], vin[2*(2*dk + k) + 1], vout[k*8 + 4], vout[k*8 + 5]); + } + unreversed_copy(dk, vin + N/SIMD_SZ/4, vout + N/SIMD_SZ - 6, -8); + unreversed_copy(dk, vin + 3*N/SIMD_SZ/4, vout + N/SIMD_SZ - 2, -8); + } + } + else + { + if(direction == PFFFT_FORWARD) + { + for(size_t k{0};k < Ncvec;++k) + { + size_t kk{(k/4) + (k%4)*(Ncvec/4)}; + interleave2(vin[k*2], vin[k*2+1], vout[kk*2], vout[kk*2+1]); + } + } + else + { + for(size_t k{0};k < Ncvec;++k) + { + size_t kk{(k/4) + (k%4)*(Ncvec/4)}; + uninterleave2(vin[kk*2], vin[kk*2+1], vout[k*2], vout[k*2+1]); + } + } + } +} + +void pffft_zconvolve_scale_accumulate(const PFFFT_Setup *s, const float *a, const float *b, + float *ab, float scaling) +{ + const size_t Ncvec{s->Ncvec}; + const v4sf *RESTRICT va{reinterpret_cast<const v4sf*>(a)}; + const v4sf *RESTRICT vb{reinterpret_cast<const v4sf*>(b)}; + v4sf *RESTRICT vab{reinterpret_cast<v4sf*>(ab)}; + +#ifdef __arm__ + __builtin_prefetch(va); + __builtin_prefetch(vb); + __builtin_prefetch(vab); + __builtin_prefetch(va+2); + __builtin_prefetch(vb+2); + __builtin_prefetch(vab+2); + __builtin_prefetch(va+4); + __builtin_prefetch(vb+4); + __builtin_prefetch(vab+4); + __builtin_prefetch(va+6); + __builtin_prefetch(vb+6); + __builtin_prefetch(vab+6); +#ifndef __clang__ +#define ZCONVOLVE_USING_INLINE_NEON_ASM +#endif +#endif + + const float ar1{VEXTRACT0(va[0])}; + const float ai1{VEXTRACT0(va[1])}; + const float br1{VEXTRACT0(vb[0])}; + const float bi1{VEXTRACT0(vb[1])}; + const float abr1{VEXTRACT0(vab[0])}; + const float abi1{VEXTRACT0(vab[1])}; + +#ifdef ZCONVOLVE_USING_INLINE_ASM + /* Inline asm version, unfortunately miscompiled by clang 3.2, at least on + * Ubuntu. So this will be restricted to GCC. + * + * Does it still miscompile with Clang? Is it even needed with today's + * optimizers? + */ + const float *a_{a}, *b_{b}; float *ab_{ab}; + size_t N{Ncvec}; + asm volatile("mov r8, %2 \n" + "vdup.f32 q15, %4 \n" + "1: \n" + "pld [%0,#64] \n" + "pld [%1,#64] \n" + "pld [%2,#64] \n" + "pld [%0,#96] \n" + "pld [%1,#96] \n" + "pld [%2,#96] \n" + "vld1.f32 {q0,q1}, [%0,:128]! \n" + "vld1.f32 {q4,q5}, [%1,:128]! \n" + "vld1.f32 {q2,q3}, [%0,:128]! \n" + "vld1.f32 {q6,q7}, [%1,:128]! \n" + "vld1.f32 {q8,q9}, [r8,:128]! \n" + + "vmul.f32 q10, q0, q4 \n" + "vmul.f32 q11, q0, q5 \n" + "vmul.f32 q12, q2, q6 \n" + "vmul.f32 q13, q2, q7 \n" + "vmls.f32 q10, q1, q5 \n" + "vmla.f32 q11, q1, q4 \n" + "vld1.f32 {q0,q1}, [r8,:128]! \n" + "vmls.f32 q12, q3, q7 \n" + "vmla.f32 q13, q3, q6 \n" + "vmla.f32 q8, q10, q15 \n" + "vmla.f32 q9, q11, q15 \n" + "vmla.f32 q0, q12, q15 \n" + "vmla.f32 q1, q13, q15 \n" + "vst1.f32 {q8,q9},[%2,:128]! \n" + "vst1.f32 {q0,q1},[%2,:128]! \n" + "subs %3, #2 \n" + "bne 1b \n" + : "+r"(a_), "+r"(b_), "+r"(ab_), "+r"(N) : "r"(scaling) : "r8", "q0","q1","q2","q3","q4","q5","q6","q7","q8","q9", "q10","q11","q12","q13","q15","memory"); + +#else + + /* Default routine, works fine for non-arm cpus with current compilers. */ + const v4sf vscal{LD_PS1(scaling)}; + for(size_t i{0};i < Ncvec;i += 2) + { + v4sf ar4{va[2*i+0]}, ai4{va[2*i+1]}; + v4sf br4{vb[2*i+0]}, bi4{vb[2*i+1]}; + vcplxmul(ar4, ai4, br4, bi4); + vab[2*i+0] = VMADD(ar4, vscal, vab[2*i+0]); + vab[2*i+1] = VMADD(ai4, vscal, vab[2*i+1]); + ar4 = va[2*i+2]; ai4 = va[2*i+3]; + br4 = vb[2*i+2]; bi4 = vb[2*i+3]; + vcplxmul(ar4, ai4, br4, bi4); + vab[2*i+2] = VMADD(ar4, vscal, vab[2*i+2]); + vab[2*i+3] = VMADD(ai4, vscal, vab[2*i+3]); + } +#endif + + if(s->transform == PFFFT_REAL) + { + vab[0] = VINSERT0(vab[0], abr1 + ar1*br1*scaling); + vab[1] = VINSERT0(vab[1], abi1 + ai1*bi1*scaling); + } +} + +void pffft_zconvolve_accumulate(const PFFFT_Setup *s, const float *a, const float *b, float *ab) +{ + const size_t Ncvec{s->Ncvec}; + const v4sf *RESTRICT va{reinterpret_cast<const v4sf*>(a)}; + const v4sf *RESTRICT vb{reinterpret_cast<const v4sf*>(b)}; + v4sf *RESTRICT vab{reinterpret_cast<v4sf*>(ab)}; + +#ifdef __arm__ + __builtin_prefetch(va); + __builtin_prefetch(vb); + __builtin_prefetch(vab); + __builtin_prefetch(va+2); + __builtin_prefetch(vb+2); + __builtin_prefetch(vab+2); + __builtin_prefetch(va+4); + __builtin_prefetch(vb+4); + __builtin_prefetch(vab+4); + __builtin_prefetch(va+6); + __builtin_prefetch(vb+6); + __builtin_prefetch(vab+6); +#endif + + const float ar1{VEXTRACT0(va[0])}; + const float ai1{VEXTRACT0(va[1])}; + const float br1{VEXTRACT0(vb[0])}; + const float bi1{VEXTRACT0(vb[1])}; + const float abr1{VEXTRACT0(vab[0])}; + const float abi1{VEXTRACT0(vab[1])}; + + /* No inline assembly for this version. I'm not familiar enough with NEON + * assembly, and I don't know that it's needed with today's optimizers. + */ + for(size_t i{0};i < Ncvec;i += 2) + { + v4sf ar4{va[2*i+0]}, ai4{va[2*i+1]}; + v4sf br4{vb[2*i+0]}, bi4{vb[2*i+1]}; + vcplxmul(ar4, ai4, br4, bi4); + vab[2*i+0] = VADD(ar4, vab[2*i+0]); + vab[2*i+1] = VADD(ai4, vab[2*i+1]); + ar4 = va[2*i+2]; ai4 = va[2*i+3]; + br4 = vb[2*i+2]; bi4 = vb[2*i+3]; + vcplxmul(ar4, ai4, br4, bi4); + vab[2*i+2] = VADD(ar4, vab[2*i+2]); + vab[2*i+3] = VADD(ai4, vab[2*i+3]); + } + + if(s->transform == PFFFT_REAL) + { + vab[0] = VINSERT0(vab[0], abr1 + ar1*br1); + vab[1] = VINSERT0(vab[1], abi1 + ai1*bi1); + } +} + + +void pffft_transform(const PFFFT_Setup *setup, const float *input, float *output, float *work, + pffft_direction_t direction) +{ + assert(valigned(input) && valigned(output) && valigned(work)); + pffft_transform_internal(setup, reinterpret_cast<const v4sf*>(al::assume_aligned<16>(input)), + reinterpret_cast<v4sf*>(al::assume_aligned<16>(output)), + reinterpret_cast<v4sf*>(al::assume_aligned<16>(work)), direction, false); +} + +void pffft_transform_ordered(const PFFFT_Setup *setup, const float *input, float *output, + float *work, pffft_direction_t direction) +{ + assert(valigned(input) && valigned(output) && valigned(work)); + pffft_transform_internal(setup, reinterpret_cast<const v4sf*>(al::assume_aligned<16>(input)), + reinterpret_cast<v4sf*>(al::assume_aligned<16>(output)), + reinterpret_cast<v4sf*>(al::assume_aligned<16>(work)), direction, true); +} + +#else // defined(PFFFT_SIMD_DISABLE) + +// standard routine using scalar floats, without SIMD stuff. + +namespace { + +#define pffft_transform_internal_nosimd pffft_transform_internal +void pffft_transform_internal_nosimd(const PFFFT_Setup *setup, const float *input, float *output, + float *scratch, const pffft_direction_t direction, bool ordered) +{ + const size_t Ncvec{setup->Ncvec}; + const bool nf_odd{(setup->ifac[1]&1) != 0}; + + assert(scratch != nullptr); + + /* z-domain data for complex transforms is already ordered without SIMD. */ + if(setup->transform == PFFFT_COMPLEX) + ordered = false; + + float *buff[2]{output, scratch}; + bool ib{nf_odd != ordered}; + if(direction == PFFFT_FORWARD) + { + if(setup->transform == PFFFT_REAL) + ib = (rfftf1_ps(Ncvec*2, input, buff[ib], buff[!ib], setup->twiddle, setup->ifac) == buff[1]); + else + ib = (cfftf1_ps(Ncvec, input, buff[ib], buff[!ib], setup->twiddle, setup->ifac, -1.0f) == buff[1]); + if(ordered) + { + pffft_zreorder(setup, buff[ib], buff[!ib], PFFFT_FORWARD); + ib = !ib; + } + } + else + { + if (input == buff[ib]) + ib = !ib; // may happen when finput == foutput + + if(ordered) + { + pffft_zreorder(setup, input, buff[ib], PFFFT_BACKWARD); + input = buff[ib]; + ib = !ib; + } + if(setup->transform == PFFFT_REAL) + ib = (rfftb1_ps(Ncvec*2, input, buff[ib], buff[!ib], setup->twiddle, setup->ifac) == buff[1]); + else + ib = (cfftf1_ps(Ncvec, input, buff[ib], buff[!ib], setup->twiddle, setup->ifac, +1.0f) == buff[1]); + } + if(buff[ib] != output) + { + // extra copy required -- this situation should happens only when finput == foutput + assert(input==output); + for(size_t k{0};k < Ncvec;++k) + { + float a{buff[ib][2*k]}, b{buff[ib][2*k+1]}; + output[2*k] = a; output[2*k+1] = b; + } + } +} + +} // namespace + +#define pffft_zreorder_nosimd pffft_zreorder +void pffft_zreorder_nosimd(const PFFFT_Setup *setup, const float *in, float *RESTRICT out, + pffft_direction_t direction) +{ + const size_t N{setup->N}; + if(setup->transform == PFFFT_COMPLEX) + { + for(size_t k{0};k < 2*N;++k) + out[k] = in[k]; + return; + } + else if(direction == PFFFT_FORWARD) + { + float x_N{in[N-1]}; + for(size_t k{N-1};k > 1;--k) + out[k] = in[k-1]; + out[0] = in[0]; + out[1] = x_N; + } + else + { + float x_N{in[1]}; + for(size_t k{1};k < N-1;++k) + out[k] = in[k+1]; + out[0] = in[0]; + out[N-1] = x_N; + } +} + +void pffft_zconvolve_scale_accumulate(const PFFFT_Setup *s, const float *a, const float *b, + float *ab, float scaling) +{ + size_t Ncvec{s->Ncvec}; + + if(s->transform == PFFFT_REAL) + { + // take care of the fftpack ordering + ab[0] += a[0]*b[0]*scaling; + ab[2*Ncvec-1] += a[2*Ncvec-1]*b[2*Ncvec-1]*scaling; + ++ab; ++a; ++b; --Ncvec; + } + for(size_t i{0};i < Ncvec;++i) + { + float ar{a[2*i+0]}, ai{a[2*i+1]}; + const float br{b[2*i+0]}, bi{b[2*i+1]}; + vcplxmul(ar, ai, br, bi); + ab[2*i+0] += ar*scaling; + ab[2*i+1] += ai*scaling; + } +} + +void pffft_zconvolve_accumulate(const PFFFT_Setup *s, const float *a, const float *b, float *ab) +{ + size_t Ncvec{s->Ncvec}; + + if(s->transform == PFFFT_REAL) + { + // take care of the fftpack ordering + ab[0] += a[0]*b[0]; + ab[2*Ncvec-1] += a[2*Ncvec-1]*b[2*Ncvec-1]; + ++ab; ++a; ++b; --Ncvec; + } + for(size_t i{0};i < Ncvec;++i) + { + float ar{a[2*i+0]}, ai{a[2*i+1]}; + const float br{b[2*i+0]}, bi{b[2*i+1]}; + vcplxmul(ar, ai, br, bi); + ab[2*i+0] += ar; + ab[2*i+1] += ai; + } +} + + +void pffft_transform(const PFFFT_Setup *setup, const float *input, float *output, float *work, + pffft_direction_t direction) +{ + pffft_transform_internal(setup, input, output, work, direction, false); +} + +void pffft_transform_ordered(const PFFFT_Setup *setup, const float *input, float *output, + float *work, pffft_direction_t direction) +{ + pffft_transform_internal(setup, input, output, work, direction, true); +} + +#endif // defined(PFFFT_SIMD_DISABLE) diff --git a/common/pffft.h b/common/pffft.h new file mode 100644 index 00000000..9cff9e54 --- /dev/null +++ b/common/pffft.h @@ -0,0 +1,192 @@ +/* Copyright (c) 2013 Julien Pommier ( [email protected] ) + + Based on original fortran 77 code from FFTPACKv4 from NETLIB, + authored by Dr Paul Swarztrauber of NCAR, in 1985. + + As confirmed by the NCAR fftpack software curators, the following + FFTPACKv5 license applies to FFTPACKv4 sources. My changes are + released under the same terms. + + FFTPACK license: + + http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html + + Copyright (c) 2004 the University Corporation for Atmospheric + Research ("UCAR"). All rights reserved. Developed by NCAR's + Computational and Information Systems Laboratory, UCAR, + www.cisl.ucar.edu. + + Redistribution and use of the Software in source and binary forms, + with or without modification, is permitted provided that the + following conditions are met: + + - Neither the names of NCAR's Computational and Information Systems + Laboratory, the University Corporation for Atmospheric Research, + nor the names of its sponsors or contributors may be used to + endorse or promote products derived from this Software without + specific prior written permission. + + - Redistributions of source code must retain the above copyright + notices, this list of conditions, and the disclaimer below. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the disclaimer below in the + documentation and/or other materials provided with the + distribution. + + THIS 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 CONTRIBUTORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL 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 WITH THE + SOFTWARE. +*/ + +/* PFFFT : a Pretty Fast FFT. + * + * This is basically an adaptation of the single precision fftpack (v4) as + * found on netlib taking advantage of SIMD instructions found on CPUs such as + * Intel x86 (SSE1), PowerPC (Altivec), and Arm (NEON). + * + * For architectures where SIMD instructions aren't available, the code falls + * back to a scalar version. + * + * Restrictions: + * + * - 1D transforms only, with 32-bit single precision. + * + * - supports only transforms for inputs of length N of the form + * N=(2^a)*(3^b)*(5^c), given a >= 5, b >=0, c >= 0 (32, 48, 64, 96, 128, 144, + * 160, etc are all acceptable lengths). Performance is best for 128<=N<=8192. + * + * - all (float*) pointers for the functions below are expected to have a + * "SIMD-compatible" alignment, that is 16 bytes. + * + * You can allocate such buffers with the pffft_aligned_malloc function, and + * deallocate them with pffft_aligned_free (or with stuff like posix_memalign, + * aligned_alloc, etc). + * + * Note that for the z-domain data of real transforms, when in the canonical + * order (as interleaved complex numbers) both 0-frequency and half-frequency + * components, which are real, are assembled in the first entry as + * F(0)+i*F(n/2+1). The original fftpack placed F(n/2+1) at the end of the + * arrays instead. + */ + +#ifndef PFFFT_H +#define PFFFT_H + +#include <stddef.h> // for size_t +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* opaque struct holding internal stuff (precomputed twiddle factors) this + * struct can be shared by many threads as it contains only read-only data. + */ +typedef struct PFFFT_Setup PFFFT_Setup; + +#ifndef PFFFT_COMMON_ENUMS +#define PFFFT_COMMON_ENUMS + +/* direction of the transform */ +typedef enum { PFFFT_FORWARD, PFFFT_BACKWARD } pffft_direction_t; + +/* type of transform */ +typedef enum { PFFFT_REAL, PFFFT_COMPLEX } pffft_transform_t; + +#endif + +/** + * Prepare for performing transforms of size N -- the returned PFFFT_Setup + * structure is read-only so it can safely be shared by multiple concurrent + * threads. + */ +PFFFT_Setup *pffft_new_setup(unsigned int N, pffft_transform_t transform); +void pffft_destroy_setup(PFFFT_Setup *setup); + +/** + * Perform a Fourier transform. The z-domain data is stored in the most + * efficient order for transforming back or using for convolution, and as + * such, there's no guarantee to the order of the values. If you need to have + * its content sorted in the usual way, that is as an array of interleaved + * complex numbers, either use pffft_transform_ordered, or call pffft_zreorder + * after the forward fft and before the backward fft. + * + * Transforms are not scaled: PFFFT_BACKWARD(PFFFT_FORWARD(x)) = N*x. Typically + * you will want to scale the backward transform by 1/N. + * + * The 'work' pointer must point to an area of N (2*N for complex fft) floats, + * properly aligned. It cannot be NULL. + * + * The input and output parameters may alias. + */ +void pffft_transform(const PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction); + +/** + * Similar to pffft_transform, but handles the complex values in the usual form + * (interleaved complex numbers). This is similar to calling + * pffft_transform(..., PFFFT_FORWARD) followed by + * pffft_zreorder(..., PFFFT_FORWARD), or + * pffft_zreorder(..., PFFFT_BACKWARD) followed by + * pffft_transform(..., PFFFT_BACKWARD), for the given direction. + * + * The input and output parameters may alias. + */ +void pffft_transform_ordered(const PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction); + +/** + * Reorder the z-domain data. For PFFFT_FORWARD, it reorders from the internal + * representation to the "canonical" order (as interleaved complex numbers). + * For PFFFT_BACKWARD, it reorders from the canonical order to the internal + * order suitable for pffft_transform(..., PFFFT_BACKWARD) or + * pffft_zconvolve_accumulate. + * + * The input and output parameters should not alias. + */ +void pffft_zreorder(const PFFFT_Setup *setup, const float *input, float *output, pffft_direction_t direction); + +/** + * Perform a multiplication of the z-domain data in dft_a and dft_b, and scale + * and accumulate into dft_ab. The arrays should have been obtained with + * pffft_transform(..., PFFFT_FORWARD) or pffft_zreorder(..., PFFFT_BACKWARD) + * and should *not* be in the usual order (otherwise just perform the operation + * yourself as the dft coeffs are stored as interleaved complex numbers). + * + * The operation performed is: dft_ab += (dft_a * dft_b)*scaling + * + * The dft_a, dft_b, and dft_ab parameters may alias. + */ +void pffft_zconvolve_scale_accumulate(const PFFFT_Setup *setup, const float *dft_a, const float *dft_b, float *dft_ab, float scaling); + +/** + * Perform a multiplication of the z-domain data in dft_a and dft_b, and + * accumulate into dft_ab. + * + * The operation performed is: dft_ab += dft_a * dft_b + * + * The dft_a, dft_b, and dft_ab parameters may alias. + */ +void pffft_zconvolve_accumulate(const PFFFT_Setup *setup, const float *dft_a, const float *dft_b, float *dft_ab); + +/** + * The float buffers must have the correct alignment (16-byte boundary on intel + * and powerpc). This function may be used to obtain such correctly aligned + * buffers. + */ +void *pffft_aligned_malloc(size_t nb_bytes); +void pffft_aligned_free(void *ptr); + +/* Return 4 or 1 depending if vectorization was enable when building pffft.cpp. */ +int pffft_simd_size(); + +#ifdef __cplusplus +} +#endif + +#endif // PFFFT_H diff --git a/common/phase_shifter.h b/common/phase_shifter.h index 0d4166bc..e1a83dab 100644 --- a/common/phase_shifter.h +++ b/common/phase_shifter.h @@ -9,11 +9,14 @@ #include <array> #include <stddef.h> +#include <type_traits> #include "alcomplex.h" #include "alspan.h" +struct NoInit { }; + /* Implements a wide-band +90 degree phase-shift. Note that this should be * given one sample less of a delay (FilterSize/2 - 1) compared to the direct * signal delay (FilterSize/2) to properly align. @@ -53,14 +56,16 @@ struct PhaseShifterT { std::fill_n(fftBuffer.get(), fft_size, complex_d{}); fftBuffer[half_size] = 1.0; - forward_fft(al::as_span(fftBuffer.get(), fft_size)); - for(size_t i{0};i < half_size+1;++i) + forward_fft(al::span{fftBuffer.get(), fft_size}); + fftBuffer[0] *= std::numeric_limits<double>::epsilon(); + for(size_t i{1};i < half_size;++i) fftBuffer[i] = complex_d{-fftBuffer[i].imag(), fftBuffer[i].real()}; + fftBuffer[half_size] *= std::numeric_limits<double>::epsilon(); for(size_t i{half_size+1};i < fft_size;++i) fftBuffer[i] = std::conj(fftBuffer[fft_size - i]); - inverse_fft(al::as_span(fftBuffer.get(), fft_size)); + inverse_fft(al::span{fftBuffer.get(), fft_size}); - auto fftiter = fftBuffer.get() + half_size + (FilterSize/2 - 1); + auto fftiter = fftBuffer.get() + fft_size - 1; for(float &coeff : mCoeffs) { coeff = static_cast<float>(fftiter->real() / double{fft_size}); @@ -68,29 +73,12 @@ struct PhaseShifterT { } } + PhaseShifterT(NoInit) { } + void process(al::span<float> dst, const float *RESTRICT src) const; private: #if defined(HAVE_NEON) - /* There doesn't seem to be NEON intrinsics to do this kind of stipple - * shuffling, so there's two custom methods for it. - */ - static auto shuffle_2020(float32x4_t a, float32x4_t b) - { - float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 0))}; - ret = vsetq_lane_f32(vgetq_lane_f32(a, 2), ret, 1); - ret = vsetq_lane_f32(vgetq_lane_f32(b, 0), ret, 2); - ret = vsetq_lane_f32(vgetq_lane_f32(b, 2), ret, 3); - return ret; - } - static auto shuffle_3131(float32x4_t a, float32x4_t b) - { - float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 1))}; - ret = vsetq_lane_f32(vgetq_lane_f32(a, 3), ret, 1); - ret = vsetq_lane_f32(vgetq_lane_f32(b, 1), ret, 2); - ret = vsetq_lane_f32(vgetq_lane_f32(b, 3), ret, 3); - return ret; - } static auto unpacklo(float32x4_t a, float32x4_t b) { float32x2x2_t result{vzip_f32(vget_low_f32(a), vget_low_f32(b))}; @@ -171,9 +159,10 @@ inline void PhaseShifterT<S>::process(al::span<float> dst, const float *RESTRICT const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])}; const float32x4_t s0{vld1q_f32(&src[j*2])}; const float32x4_t s1{vld1q_f32(&src[j*2 + 4])}; + const float32x4x2_t values{vuzpq_f32(s0, s1)}; - r04 = vmlaq_f32(r04, shuffle_2020(s0, s1), coeffs); - r14 = vmlaq_f32(r14, shuffle_3131(s0, s1), coeffs); + r04 = vmlaq_f32(r04, values.val[0], coeffs); + r14 = vmlaq_f32(r14, values.val[1], coeffs); } src += 2; diff --git a/common/polyphase_resampler.cpp b/common/polyphase_resampler.cpp index 14f7e40d..9c569b3a 100644 --- a/common/polyphase_resampler.cpp +++ b/common/polyphase_resampler.cpp @@ -3,6 +3,8 @@ #include <algorithm> #include <cmath> +#include <numeric> +#include <stdexcept> #include "alnumbers.h" #include "opthelpers.h" @@ -14,34 +16,36 @@ constexpr double Epsilon{1e-9}; using uint = unsigned int; -/* This is the normalized cardinal sine (sinc) function. - * - * sinc(x) = { 1, x = 0 - * { sin(pi x) / (pi x), otherwise. - */ -double Sinc(const double x) -{ - if(std::abs(x) < Epsilon) UNLIKELY - return 1.0; - return std::sin(al::numbers::pi*x) / (al::numbers::pi*x); -} +#if __cpp_lib_math_special_functions >= 201603L +using std::cyl_bessel_i; + +#else /* 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 + * + * This implementation only handles nu = 0, and isn't the most precise (it + * starts with the largest value and accumulates successively smaller values, + * compounding the rounding and precision error), but it's good enough. */ -constexpr double BesselI_0(const double x) +template<typename T, typename U> +U cyl_bessel_i(T nu, U x) { - // Start at k=1 since k=0 is trivial. + if(nu != T{0}) + throw std::runtime_error{"cyl_bessel_i: nu != 0"}; + + /* Start at k=1 since k=0 is trivial. */ const double x2{x/2.0}; double term{1.0}; double sum{1.0}; int k{1}; - // Let the integration converge until the term of the sum is no longer - // significant. + /* Let the integration converge until the term of the sum is no longer + * significant. + */ double last_sum{}; do { const double y{x2 / k}; @@ -50,7 +54,20 @@ constexpr double BesselI_0(const double x) term *= y * y; sum += term; } while(sum != last_sum); - return sum; + return static_cast<U>(sum); +} +#endif + +/* This is the normalized cardinal sine (sinc) function. + * + * sinc(x) = { 1, x = 0 + * { sin(pi x) / (pi x), otherwise. + */ +double Sinc(const double x) +{ + if(std::abs(x) < Epsilon) UNLIKELY + return 1.0; + return std::sin(al::numbers::pi*x) / (al::numbers::pi*x); } /* Calculate a Kaiser window from the given beta value and a normalized k @@ -67,23 +84,11 @@ constexpr double BesselI_0(const double x) * * k = 2 i / M - 1, where 0 <= i <= M. */ -double Kaiser(const double b, const double k) +double Kaiser(const double beta, const double k, const double besseli_0_beta) { if(!(k >= -1.0 && k <= 1.0)) return 0.0; - return BesselI_0(b * std::sqrt(1.0 - k*k)) / BesselI_0(b); -} - -// Calculates the greatest common divisor of a and b. -constexpr uint Gcd(uint x, uint y) -{ - while(y > 0) - { - const uint z{y}; - y = x % y; - x = z; - } - return x; + return cyl_bessel_i(0, beta * std::sqrt(1.0 - k*k)) / besseli_0_beta; } /* Calculates the size (order) of the Kaiser window. Rejection is in dB and @@ -124,11 +129,11 @@ constexpr double CalcKaiserBeta(const double rejection) * p -- gain compensation factor when sampling * f_t -- normalized center frequency (or cutoff; 0.5 is nyquist) */ -double SincFilter(const uint l, const double b, const double gain, const double cutoff, - const uint i) +double SincFilter(const uint l, const double beta, const double besseli_0_beta, const double gain, + const double cutoff, const uint i) { const double x{static_cast<double>(i) - l}; - return Kaiser(b, x / l) * 2.0 * gain * cutoff * Sinc(2.0 * cutoff * x); + return Kaiser(beta, x/l, besseli_0_beta) * 2.0 * gain * cutoff * Sinc(2.0 * cutoff * x); } } // namespace @@ -137,7 +142,7 @@ double SincFilter(const uint l, const double b, const double gain, const double // that's used to cut frequencies above the destination nyquist. void PPhaseResampler::init(const uint srcRate, const uint dstRate) { - const uint gcd{Gcd(srcRate, dstRate)}; + const uint gcd{std::gcd(srcRate, dstRate)}; mP = dstRate / gcd; mQ = srcRate / gcd; @@ -160,11 +165,12 @@ void PPhaseResampler::init(const uint srcRate, const uint dstRate) // calculating the left offset to avoid increasing the transition width. const uint l{(CalcKaiserOrder(180.0, width)+1) / 2}; const double beta{CalcKaiserBeta(180.0)}; + const double besseli_0_beta{cyl_bessel_i(0, beta)}; mM = l*2 + 1; mL = l; mF.resize(mM); for(uint i{0};i < mM;i++) - mF[i] = SincFilter(l, beta, mP, cutoff, i); + mF[i] = SincFilter(l, beta, besseli_0_beta, mP, cutoff, i); } // Perform the upsample-filter-downsample resampling operation using a diff --git a/common/ringbuffer.cpp b/common/ringbuffer.cpp index 0aec1d49..af1f3669 100644 --- a/common/ringbuffer.cpp +++ b/common/ringbuffer.cpp @@ -29,9 +29,9 @@ #include "almalloc.h" -RingBufferPtr RingBuffer::Create(size_t sz, size_t elem_sz, int limit_writes) +RingBufferPtr RingBuffer::Create(std::size_t sz, std::size_t elem_sz, int limit_writes) { - size_t power_of_two{0u}; + std::size_t power_of_two{0u}; if(sz > 0) { power_of_two = sz; @@ -40,15 +40,14 @@ RingBufferPtr RingBuffer::Create(size_t sz, size_t elem_sz, int limit_writes) power_of_two |= power_of_two>>4; power_of_two |= power_of_two>>8; power_of_two |= power_of_two>>16; -#if SIZE_MAX > UINT_MAX - power_of_two |= power_of_two>>32; -#endif + if constexpr(SIZE_MAX > UINT_MAX) + power_of_two |= power_of_two>>32; } ++power_of_two; - if(power_of_two <= sz || power_of_two > std::numeric_limits<size_t>::max()/elem_sz) + if(power_of_two <= sz || power_of_two > std::numeric_limits<std::size_t>::max()/elem_sz) throw std::overflow_error{"Ring buffer size overflow"}; - const size_t bufbytes{power_of_two * elem_sz}; + const std::size_t bufbytes{power_of_two * elem_sz}; RingBufferPtr rb{new(FamCount(bufbytes)) RingBuffer{bufbytes}}; rb->mWriteSize = limit_writes ? sz : (power_of_two-1); rb->mSizeMask = power_of_two - 1; @@ -61,20 +60,20 @@ void RingBuffer::reset() noexcept { mWritePtr.store(0, std::memory_order_relaxed); mReadPtr.store(0, std::memory_order_relaxed); - std::fill_n(mBuffer.begin(), (mSizeMask+1)*mElemSize, al::byte{}); + std::fill_n(mBuffer.begin(), (mSizeMask+1)*mElemSize, std::byte{}); } -size_t RingBuffer::read(void *dest, size_t cnt) noexcept +std::size_t RingBuffer::read(void *dest, std::size_t cnt) noexcept { - const size_t free_cnt{readSpace()}; + const std::size_t free_cnt{readSpace()}; if(free_cnt == 0) return 0; - const size_t to_read{std::min(cnt, free_cnt)}; - size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask}; + const std::size_t to_read{std::min(cnt, free_cnt)}; + std::size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask}; - size_t n1, n2; - const size_t cnt2{read_ptr + to_read}; + std::size_t n1, n2; + const std::size_t cnt2{read_ptr + to_read}; if(cnt2 > mSizeMask+1) { n1 = mSizeMask+1 - read_ptr; @@ -87,7 +86,7 @@ size_t RingBuffer::read(void *dest, size_t cnt) noexcept } auto outiter = std::copy_n(mBuffer.begin() + read_ptr*mElemSize, n1*mElemSize, - static_cast<al::byte*>(dest)); + static_cast<std::byte*>(dest)); read_ptr += n1; if(n2 > 0) { @@ -98,16 +97,16 @@ size_t RingBuffer::read(void *dest, size_t cnt) noexcept return to_read; } -size_t RingBuffer::peek(void *dest, size_t cnt) const noexcept +std::size_t RingBuffer::peek(void *dest, std::size_t cnt) const noexcept { - const size_t free_cnt{readSpace()}; + const std::size_t free_cnt{readSpace()}; if(free_cnt == 0) return 0; - const size_t to_read{std::min(cnt, free_cnt)}; - size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask}; + const std::size_t to_read{std::min(cnt, free_cnt)}; + std::size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask}; - size_t n1, n2; - const size_t cnt2{read_ptr + to_read}; + std::size_t n1, n2; + const std::size_t cnt2{read_ptr + to_read}; if(cnt2 > mSizeMask+1) { n1 = mSizeMask+1 - read_ptr; @@ -120,22 +119,22 @@ size_t RingBuffer::peek(void *dest, size_t cnt) const noexcept } auto outiter = std::copy_n(mBuffer.begin() + read_ptr*mElemSize, n1*mElemSize, - static_cast<al::byte*>(dest)); + static_cast<std::byte*>(dest)); if(n2 > 0) std::copy_n(mBuffer.begin(), n2*mElemSize, outiter); return to_read; } -size_t RingBuffer::write(const void *src, size_t cnt) noexcept +std::size_t RingBuffer::write(const void *src, std::size_t cnt) noexcept { - const size_t free_cnt{writeSpace()}; + const std::size_t free_cnt{writeSpace()}; if(free_cnt == 0) return 0; - const size_t to_write{std::min(cnt, free_cnt)}; - size_t write_ptr{mWritePtr.load(std::memory_order_relaxed) & mSizeMask}; + const std::size_t to_write{std::min(cnt, free_cnt)}; + std::size_t write_ptr{mWritePtr.load(std::memory_order_relaxed) & mSizeMask}; - size_t n1, n2; - const size_t cnt2{write_ptr + to_write}; + std::size_t n1, n2; + const std::size_t cnt2{write_ptr + to_write}; if(cnt2 > mSizeMask+1) { n1 = mSizeMask+1 - write_ptr; @@ -147,7 +146,7 @@ size_t RingBuffer::write(const void *src, size_t cnt) noexcept n2 = 0; } - auto srcbytes = static_cast<const al::byte*>(src); + auto srcbytes = static_cast<const std::byte*>(src); std::copy_n(srcbytes, n1*mElemSize, mBuffer.begin() + write_ptr*mElemSize); write_ptr += n1; if(n2 > 0) @@ -164,26 +163,26 @@ auto RingBuffer::getReadVector() const noexcept -> DataPair { DataPair ret; - size_t w{mWritePtr.load(std::memory_order_acquire)}; - size_t r{mReadPtr.load(std::memory_order_acquire)}; + std::size_t w{mWritePtr.load(std::memory_order_acquire)}; + std::size_t r{mReadPtr.load(std::memory_order_acquire)}; w &= mSizeMask; r &= mSizeMask; - const size_t free_cnt{(w-r) & mSizeMask}; + const std::size_t free_cnt{(w-r) & mSizeMask}; - const size_t cnt2{r + free_cnt}; + const std::size_t cnt2{r + free_cnt}; if(cnt2 > mSizeMask+1) { /* Two part vector: the rest of the buffer after the current read ptr, * plus some from the start of the buffer. */ - ret.first.buf = const_cast<al::byte*>(mBuffer.data() + r*mElemSize); + ret.first.buf = const_cast<std::byte*>(mBuffer.data() + r*mElemSize); ret.first.len = mSizeMask+1 - r; - ret.second.buf = const_cast<al::byte*>(mBuffer.data()); + ret.second.buf = const_cast<std::byte*>(mBuffer.data()); ret.second.len = cnt2 & mSizeMask; } else { /* Single part vector: just the rest of the buffer */ - ret.first.buf = const_cast<al::byte*>(mBuffer.data() + r*mElemSize); + ret.first.buf = const_cast<std::byte*>(mBuffer.data() + r*mElemSize); ret.first.len = free_cnt; ret.second.buf = nullptr; ret.second.len = 0; @@ -196,25 +195,25 @@ auto RingBuffer::getWriteVector() const noexcept -> DataPair { DataPair ret; - size_t w{mWritePtr.load(std::memory_order_acquire)}; - size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask}; + std::size_t w{mWritePtr.load(std::memory_order_acquire)}; + std::size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask}; w &= mSizeMask; r &= mSizeMask; - const size_t free_cnt{(r-w-1) & mSizeMask}; + const std::size_t free_cnt{(r-w-1) & mSizeMask}; - const size_t cnt2{w + free_cnt}; + const std::size_t cnt2{w + free_cnt}; if(cnt2 > mSizeMask+1) { /* Two part vector: the rest of the buffer after the current write ptr, * plus some from the start of the buffer. */ - ret.first.buf = const_cast<al::byte*>(mBuffer.data() + w*mElemSize); + ret.first.buf = const_cast<std::byte*>(mBuffer.data() + w*mElemSize); ret.first.len = mSizeMask+1 - w; - ret.second.buf = const_cast<al::byte*>(mBuffer.data()); + ret.second.buf = const_cast<std::byte*>(mBuffer.data()); ret.second.len = cnt2 & mSizeMask; } else { - ret.first.buf = const_cast<al::byte*>(mBuffer.data() + w*mElemSize); + ret.first.buf = const_cast<std::byte*>(mBuffer.data() + w*mElemSize); ret.first.len = free_cnt; ret.second.buf = nullptr; ret.second.len = 0; diff --git a/common/ringbuffer.h b/common/ringbuffer.h index 2a3797b0..8c65c3af 100644 --- a/common/ringbuffer.h +++ b/common/ringbuffer.h @@ -2,11 +2,10 @@ #define RINGBUFFER_H #include <atomic> +#include <cstddef> #include <memory> -#include <stddef.h> #include <utility> -#include "albyte.h" #include "almalloc.h" @@ -18,23 +17,23 @@ struct RingBuffer { private: - std::atomic<size_t> mWritePtr{0u}; - std::atomic<size_t> mReadPtr{0u}; - size_t mWriteSize{0u}; - size_t mSizeMask{0u}; - size_t mElemSize{0u}; + std::atomic<std::size_t> mWritePtr{0u}; + std::atomic<std::size_t> mReadPtr{0u}; + std::size_t mWriteSize{0u}; + std::size_t mSizeMask{0u}; + std::size_t mElemSize{0u}; - al::FlexArray<al::byte, 16> mBuffer; + al::FlexArray<std::byte, 16> mBuffer; public: struct Data { - al::byte *buf; - size_t len; + std::byte *buf; + std::size_t len; }; using DataPair = std::pair<Data,Data>; - RingBuffer(const size_t count) : mBuffer{count} { } + RingBuffer(const std::size_t count) : mBuffer{count} { } /** Reset the read and write pointers to zero. This is not thread safe. */ void reset() noexcept; @@ -56,7 +55,7 @@ public: * Return the number of elements available for reading. This is the number * of elements in front of the read pointer and behind the write pointer. */ - size_t readSpace() const noexcept + std::size_t readSpace() const noexcept { const size_t w{mWritePtr.load(std::memory_order_acquire)}; const size_t r{mReadPtr.load(std::memory_order_acquire)}; @@ -67,14 +66,14 @@ public: * The copying data reader. Copy at most `cnt' elements into `dest'. * Returns the actual number of elements copied. */ - size_t read(void *dest, size_t cnt) noexcept; + std::size_t read(void *dest, std::size_t cnt) noexcept; /** * The copying data reader w/o read pointer advance. Copy at most `cnt' * elements into `dest'. Returns the actual number of elements copied. */ - size_t peek(void *dest, size_t cnt) const noexcept; + std::size_t peek(void *dest, std::size_t cnt) const noexcept; /** Advance the read pointer `cnt' places. */ - void readAdvance(size_t cnt) noexcept + void readAdvance(std::size_t cnt) noexcept { mReadPtr.fetch_add(cnt, std::memory_order_acq_rel); } @@ -82,7 +81,7 @@ public: * Return the number of elements available for writing. This is the number * of elements in front of the write pointer and behind the read pointer. */ - size_t writeSpace() const noexcept + std::size_t writeSpace() const noexcept { const size_t w{mWritePtr.load(std::memory_order_acquire)}; const size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask}; @@ -93,12 +92,12 @@ public: * The copying data writer. Copy at most `cnt' elements from `src'. Returns * the actual number of elements copied. */ - size_t write(const void *src, size_t cnt) noexcept; + std::size_t write(const void *src, std::size_t cnt) noexcept; /** Advance the write pointer `cnt' places. */ - void writeAdvance(size_t cnt) noexcept + void writeAdvance(std::size_t cnt) noexcept { mWritePtr.fetch_add(cnt, std::memory_order_acq_rel); } - size_t getElemSize() const noexcept { return mElemSize; } + std::size_t getElemSize() const noexcept { return mElemSize; } /** * Create a new ringbuffer to hold at least `sz' elements of `elem_sz' @@ -106,7 +105,7 @@ public: * (even if it is already a power of two, to ensure the requested amount * can be written). */ - static std::unique_ptr<RingBuffer> Create(size_t sz, size_t elem_sz, int limit_writes); + static std::unique_ptr<RingBuffer> Create(std::size_t sz, std::size_t elem_sz, int limit_writes); DEF_FAM_NEWDEL(RingBuffer, mBuffer) }; diff --git a/common/strutils.cpp b/common/strutils.cpp index d0418eff..f7868e2e 100644 --- a/common/strutils.cpp +++ b/common/strutils.cpp @@ -10,31 +10,32 @@ #define WIN32_LEAN_AND_MEAN #include <windows.h> -std::string wstr_to_utf8(const WCHAR *wstr) +std::string wstr_to_utf8(std::wstring_view wstr) { std::string ret; - int len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, nullptr, 0, nullptr, nullptr); + int len{WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.length()), nullptr, + 0, nullptr, nullptr)}; if(len > 0) { ret.resize(len); - WideCharToMultiByte(CP_UTF8, 0, wstr, -1, &ret[0], len, nullptr, nullptr); - ret.pop_back(); + WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.length()), &ret[0], len, + nullptr, nullptr); } return ret; } -std::wstring utf8_to_wstr(const char *str) +std::wstring utf8_to_wstr(std::string_view str) { std::wstring ret; - int len = MultiByteToWideChar(CP_UTF8, 0, str, -1, nullptr, 0); + int len{MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.length()), nullptr, + 0)}; if(len > 0) { ret.resize(len); - MultiByteToWideChar(CP_UTF8, 0, str, -1, &ret[0], len); - ret.pop_back(); + MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.length()), &ret[0], len); } return ret; @@ -43,21 +44,25 @@ std::wstring utf8_to_wstr(const char *str) namespace al { -al::optional<std::string> getenv(const char *envname) +std::optional<std::string> getenv(const char *envname) { +#ifdef _GAMING_XBOX + const char *str{::getenv(envname)}; +#else const char *str{std::getenv(envname)}; +#endif if(str && str[0] != '\0') return str; - return al::nullopt; + return std::nullopt; } #ifdef _WIN32 -al::optional<std::wstring> getenv(const WCHAR *envname) +std::optional<std::wstring> getenv(const WCHAR *envname) { const WCHAR *str{_wgetenv(envname)}; if(str && str[0] != L'\0') return str; - return al::nullopt; + return std::nullopt; } #endif diff --git a/common/strutils.h b/common/strutils.h index 0c7a0e22..7eee0c1d 100644 --- a/common/strutils.h +++ b/common/strutils.h @@ -1,22 +1,22 @@ #ifndef AL_STRUTILS_H #define AL_STRUTILS_H +#include <optional> #include <string> -#include "aloptional.h" - #ifdef _WIN32 +#include <string_view> #include <wchar.h> -std::string wstr_to_utf8(const wchar_t *wstr); -std::wstring utf8_to_wstr(const char *str); +std::string wstr_to_utf8(std::wstring_view wstr); +std::wstring utf8_to_wstr(std::string_view str); #endif namespace al { -al::optional<std::string> getenv(const char *envname); +std::optional<std::string> getenv(const char *envname); #ifdef _WIN32 -al::optional<std::wstring> getenv(const wchar_t *envname); +std::optional<std::wstring> getenv(const wchar_t *envname); #endif } // namespace al diff --git a/common/win_main_utf8.h b/common/win_main_utf8.h index 077af533..81734242 100644 --- a/common/win_main_utf8.h +++ b/common/win_main_utf8.h @@ -20,14 +20,20 @@ #define STATIC_CAST(...) static_cast<__VA_ARGS__> #define REINTERPRET_CAST(...) reinterpret_cast<__VA_ARGS__> +#define MAYBE_UNUSED [[maybe_unused]] #else #define STATIC_CAST(...) (__VA_ARGS__) #define REINTERPRET_CAST(...) (__VA_ARGS__) +#ifdef __GNUC__ +#define MAYBE_UNUSED __attribute__((__unused__)) +#else +#define MAYBE_UNUSED +#endif #endif -static FILE *my_fopen(const char *fname, const char *mode) +MAYBE_UNUSED static FILE *my_fopen(const char *fname, const char *mode) { wchar_t *wname=NULL, *wmode=NULL; int namelen, modelen; @@ -44,10 +50,11 @@ static FILE *my_fopen(const char *fname, const char *mode) } #ifdef __cplusplus - auto strbuf = std::make_unique<wchar_t[]>(static_cast<size_t>(namelen)+modelen); + auto strbuf = std::make_unique<wchar_t[]>(static_cast<size_t>(namelen) + + static_cast<size_t>(modelen)); wname = strbuf.get(); #else - wname = (wchar_t*)calloc(sizeof(wchar_t), (size_t)namelen + modelen); + wname = (wchar_t*)calloc(sizeof(wchar_t), (size_t)namelen + (size_t)modelen); #endif wmode = wname + namelen; MultiByteToWideChar(CP_UTF8, 0, fname, -1, wname, namelen); diff --git a/config.h.in b/config.h.in index 477d8c77..20df5b46 100644 --- a/config.h.in +++ b/config.h.in @@ -1,3 +1,6 @@ +/* Define the alignment attribute for externally callable functions. */ +#define FORCE_ALIGN @ALSOFT_FORCE_ALIGN@ + /* Define if deprecated EAX extensions are enabled */ #cmakedefine ALSOFT_EAX @@ -117,3 +120,6 @@ /* Define the installation data directory */ #cmakedefine ALSOFT_INSTALL_DATADIR "@ALSOFT_INSTALL_DATADIR@" + +/* Define whether build alsoft for winuwp */ +#cmakedefine ALSOFT_UWP diff --git a/core/ambdec.cpp b/core/ambdec.cpp index 8ca182c4..f98e1098 100644 --- a/core/ambdec.cpp +++ b/core/ambdec.cpp @@ -47,9 +47,9 @@ enum class ReaderScope { #else [[gnu::format(printf,2,3)]] #endif -al::optional<std::string> make_error(size_t linenum, const char *fmt, ...) +std::optional<std::string> make_error(size_t linenum, const char *fmt, ...) { - al::optional<std::string> ret; + std::optional<std::string> ret; auto &str = ret.emplace(); str.resize(256); @@ -77,7 +77,7 @@ al::optional<std::string> make_error(size_t linenum, const char *fmt, ...) AmbDecConf::~AmbDecConf() = default; -al::optional<std::string> AmbDecConf::load(const char *fname) noexcept +std::optional<std::string> AmbDecConf::load(const char *fname) noexcept { al::ifstream f{fname}; if(!f.is_open()) @@ -139,7 +139,7 @@ al::optional<std::string> AmbDecConf::load(const char *fname) noexcept { --toread; istr >> value; - if(curgain < al::size(gains)) + if(curgain < std::size(gains)) gains[curgain++] = value; } } @@ -291,7 +291,7 @@ al::optional<std::string> AmbDecConf::load(const char *fname) noexcept if(CoeffScale == AmbDecScale::Unset) return make_error(linenum, "No coefficient scaling defined"); - return al::nullopt; + return std::nullopt; } else return make_error(linenum, "Unexpected command: %s", command.c_str()); diff --git a/core/ambdec.h b/core/ambdec.h index 7f739781..19f68697 100644 --- a/core/ambdec.h +++ b/core/ambdec.h @@ -3,9 +3,9 @@ #include <array> #include <memory> +#include <optional> #include <string> -#include "aloptional.h" #include "core/ambidefs.h" /* Helpers to read .ambdec configuration files. */ @@ -49,7 +49,7 @@ struct AmbDecConf { ~AmbDecConf(); - al::optional<std::string> load(const char *fname) noexcept; + std::optional<std::string> load(const char *fname) noexcept; }; #endif /* CORE_AMBDEC_H */ diff --git a/core/ambidefs.cpp b/core/ambidefs.cpp index 70d6f356..2389ce6b 100644 --- a/core/ambidefs.cpp +++ b/core/ambidefs.cpp @@ -21,25 +21,25 @@ constexpr auto inv_sqrt3f = static_cast<float>(1.0/al::numbers::sqrt3); * will result in that channel being subsequently decoded for second-order as * if it was a first-order decoder for that same speaker array. */ -constexpr std::array<std::array<float,MaxAmbiOrder+1>,MaxAmbiOrder+1> HFScales{{ - {{ 4.000000000e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f }}, - {{ 4.000000000e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f }}, - {{ 2.981423970e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f }}, - {{ 2.359168820e+00f, 2.031565936e+00f, 1.444598386e+00f, 7.189495850e-01f }}, - /* 1.947005434e+00f, 1.764337084e+00f, 1.424707344e+00f, 9.755104127e-01f, 4.784482742e-01f */ -}}; +constexpr std::array HFScales{ + std::array{4.000000000e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f}, + std::array{4.000000000e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f}, + std::array{2.981423970e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f}, + std::array{2.359168820e+00f, 2.031565936e+00f, 1.444598386e+00f, 7.189495850e-01f}, + /*std::array{1.947005434e+00f, 1.764337084e+00f, 1.424707344e+00f, 9.755104127e-01f, 4.784482742e-01f}, */ +}; /* Same as above, but using a 10-point horizontal-only speaker array. Should * only be used when the device is mixing in 2D B-Format for horizontal-only * output. */ -constexpr std::array<std::array<float,MaxAmbiOrder+1>,MaxAmbiOrder+1> HFScales2D{{ - {{ 2.236067977e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f }}, - {{ 2.236067977e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f }}, - {{ 1.825741858e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f }}, - {{ 1.581138830e+00f, 1.460781803e+00f, 1.118033989e+00f, 6.050756345e-01f }}, - /* 1.414213562e+00f, 1.344997024e+00f, 1.144122806e+00f, 8.312538756e-01f, 4.370160244e-01f */ -}}; +constexpr std::array HFScales2D{ + std::array{2.236067977e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f}, + std::array{2.236067977e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f}, + std::array{1.825741858e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f}, + std::array{1.581138830e+00f, 1.460781803e+00f, 1.118033989e+00f, 6.050756345e-01f}, + /*std::array{1.414213562e+00f, 1.344997024e+00f, 1.144122806e+00f, 8.312538756e-01f, 4.370160244e-01f}, */ +}; /* This calculates a first-order "upsampler" matrix. It combines a first-order @@ -49,17 +49,17 @@ constexpr std::array<std::array<float,MaxAmbiOrder+1>,MaxAmbiOrder+1> HFScales2D * signal. While not perfect, this should accurately encode a lower-order * signal into a higher-order signal. */ -constexpr std::array<std::array<float,4>,8> FirstOrderDecoder{{ - {{ 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, }}, - {{ 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, }}, - {{ 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, }}, - {{ 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, }}, - {{ 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, }}, - {{ 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, }}, - {{ 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, }}, - {{ 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, }}, -}}; -constexpr std::array<AmbiChannelFloatArray,8> FirstOrderEncoder{{ +constexpr std::array FirstOrderDecoder{ + std::array{1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f}, + std::array{1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f}, + std::array{1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f}, + std::array{1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f}, + std::array{1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f}, + std::array{1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f}, + std::array{1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f}, + std::array{1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f}, +}; +constexpr std::array FirstOrderEncoder{ CalcAmbiCoeffs( inv_sqrt3f, inv_sqrt3f, inv_sqrt3f), CalcAmbiCoeffs( inv_sqrt3f, inv_sqrt3f, -inv_sqrt3f), CalcAmbiCoeffs(-inv_sqrt3f, inv_sqrt3f, inv_sqrt3f), @@ -68,25 +68,25 @@ constexpr std::array<AmbiChannelFloatArray,8> FirstOrderEncoder{{ CalcAmbiCoeffs( inv_sqrt3f, -inv_sqrt3f, -inv_sqrt3f), CalcAmbiCoeffs(-inv_sqrt3f, -inv_sqrt3f, inv_sqrt3f), CalcAmbiCoeffs(-inv_sqrt3f, -inv_sqrt3f, -inv_sqrt3f), -}}; +}; static_assert(FirstOrderDecoder.size() == FirstOrderEncoder.size(), "First-order mismatch"); /* This calculates a 2D first-order "upsampler" matrix. Same as the first-order * matrix, just using a more optimized speaker array for horizontal-only * content. */ -constexpr std::array<std::array<float,4>,4> FirstOrder2DDecoder{{ - {{ 2.500000000e-01f, 2.041241452e-01f, 0.0f, 2.041241452e-01f, }}, - {{ 2.500000000e-01f, 2.041241452e-01f, 0.0f, -2.041241452e-01f, }}, - {{ 2.500000000e-01f, -2.041241452e-01f, 0.0f, 2.041241452e-01f, }}, - {{ 2.500000000e-01f, -2.041241452e-01f, 0.0f, -2.041241452e-01f, }}, -}}; -constexpr std::array<AmbiChannelFloatArray,4> FirstOrder2DEncoder{{ +constexpr std::array FirstOrder2DDecoder{ + std::array{2.500000000e-01f, 2.041241452e-01f, 0.0f, 2.041241452e-01f}, + std::array{2.500000000e-01f, 2.041241452e-01f, 0.0f, -2.041241452e-01f}, + std::array{2.500000000e-01f, -2.041241452e-01f, 0.0f, 2.041241452e-01f}, + std::array{2.500000000e-01f, -2.041241452e-01f, 0.0f, -2.041241452e-01f}, +}; +constexpr std::array FirstOrder2DEncoder{ CalcAmbiCoeffs( inv_sqrt2f, 0.0f, inv_sqrt2f), CalcAmbiCoeffs( inv_sqrt2f, 0.0f, -inv_sqrt2f), CalcAmbiCoeffs(-inv_sqrt2f, 0.0f, inv_sqrt2f), CalcAmbiCoeffs(-inv_sqrt2f, 0.0f, -inv_sqrt2f), -}}; +}; static_assert(FirstOrder2DDecoder.size() == FirstOrder2DEncoder.size(), "First-order 2D mismatch"); @@ -94,21 +94,21 @@ static_assert(FirstOrder2DDecoder.size() == FirstOrder2DEncoder.size(), "First-o * matrix, just using a slightly more dense speaker array suitable for second- * order content. */ -constexpr std::array<std::array<float,9>,12> SecondOrderDecoder{{ - {{ 8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f, }}, - {{ 8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }}, - {{ 8.333333333e-02f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }}, - {{ 8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f, }}, - {{ 8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }}, - {{ 8.333333333e-02f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }}, - {{ 8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f, }}, - {{ 8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }}, - {{ 8.333333333e-02f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }}, - {{ 8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f, }}, - {{ 8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }}, - {{ 8.333333333e-02f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }}, -}}; -constexpr std::array<AmbiChannelFloatArray,12> SecondOrderEncoder{{ +constexpr std::array SecondOrderDecoder{ + std::array{8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f}, + std::array{8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f}, + std::array{8.333333333e-02f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f}, + std::array{8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f}, + std::array{8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f}, + std::array{8.333333333e-02f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f}, + std::array{8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f}, + std::array{8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f}, + std::array{8.333333333e-02f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f}, + std::array{8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f}, + std::array{8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f}, + std::array{8.333333333e-02f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f}, +}; +constexpr std::array SecondOrderEncoder{ CalcAmbiCoeffs( 0.000000000e+00f, -5.257311121e-01f, 8.506508084e-01f), CalcAmbiCoeffs(-8.506508084e-01f, 0.000000000e+00f, 5.257311121e-01f), CalcAmbiCoeffs(-5.257311121e-01f, 8.506508084e-01f, 0.000000000e+00f), @@ -121,29 +121,29 @@ constexpr std::array<AmbiChannelFloatArray,12> SecondOrderEncoder{{ CalcAmbiCoeffs( 0.000000000e+00f, 5.257311121e-01f, -8.506508084e-01f), CalcAmbiCoeffs( 8.506508084e-01f, 0.000000000e+00f, 5.257311121e-01f), CalcAmbiCoeffs(-5.257311121e-01f, -8.506508084e-01f, 0.000000000e+00f), -}}; +}; static_assert(SecondOrderDecoder.size() == SecondOrderEncoder.size(), "Second-order mismatch"); /* This calculates a 2D second-order "upsampler" matrix. Same as the second- * order matrix, just using a more optimized speaker array for horizontal-only * content. */ -constexpr std::array<std::array<float,9>,6> SecondOrder2DDecoder{{ - {{ 1.666666667e-01f, -9.622504486e-02f, 0.0f, 1.666666667e-01f, -1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f, }}, - {{ 1.666666667e-01f, -1.924500897e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.721325932e-01f, }}, - {{ 1.666666667e-01f, -9.622504486e-02f, 0.0f, -1.666666667e-01f, 1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f, }}, - {{ 1.666666667e-01f, 9.622504486e-02f, 0.0f, -1.666666667e-01f, -1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f, }}, - {{ 1.666666667e-01f, 1.924500897e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.721325932e-01f, }}, - {{ 1.666666667e-01f, 9.622504486e-02f, 0.0f, 1.666666667e-01f, 1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f, }}, -}}; -constexpr std::array<AmbiChannelFloatArray,6> SecondOrder2DEncoder{{ +constexpr std::array SecondOrder2DDecoder{ + std::array{1.666666667e-01f, -9.622504486e-02f, 0.0f, 1.666666667e-01f, -1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f}, + std::array{1.666666667e-01f, -1.924500897e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.721325932e-01f}, + std::array{1.666666667e-01f, -9.622504486e-02f, 0.0f, -1.666666667e-01f, 1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f}, + std::array{1.666666667e-01f, 9.622504486e-02f, 0.0f, -1.666666667e-01f, -1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f}, + std::array{1.666666667e-01f, 1.924500897e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.721325932e-01f}, + std::array{1.666666667e-01f, 9.622504486e-02f, 0.0f, 1.666666667e-01f, 1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f}, +}; +constexpr std::array SecondOrder2DEncoder{ CalcAmbiCoeffs(-0.50000000000f, 0.0f, 0.86602540379f), CalcAmbiCoeffs(-1.00000000000f, 0.0f, 0.00000000000f), CalcAmbiCoeffs(-0.50000000000f, 0.0f, -0.86602540379f), CalcAmbiCoeffs( 0.50000000000f, 0.0f, -0.86602540379f), CalcAmbiCoeffs( 1.00000000000f, 0.0f, 0.00000000000f), CalcAmbiCoeffs( 0.50000000000f, 0.0f, 0.86602540379f), -}}; +}; static_assert(SecondOrder2DDecoder.size() == SecondOrder2DEncoder.size(), "Second-order 2D mismatch"); @@ -152,29 +152,29 @@ static_assert(SecondOrder2DDecoder.size() == SecondOrder2DEncoder.size(), * matrix, just using a more dense speaker array suitable for third-order * content. */ -constexpr std::array<std::array<float,16>,20> ThirdOrderDecoder{{ - {{ 5.000000000e-02f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f, }}, - {{ 5.000000000e-02f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f, }}, - {{ 5.000000000e-02f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f, }}, - {{ 5.000000000e-02f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f, }}, - {{ 5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f, }}, - {{ 5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f, }}, - {{ 5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f, }}, - {{ 5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f, }}, - {{ 5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, -6.779013272e-02f, 1.659481923e-01f, 4.797944664e-02f, }}, - {{ 5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, 6.779013272e-02f, 1.659481923e-01f, -4.797944664e-02f, }}, - {{ 5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, -6.779013272e-02f, -1.659481923e-01f, 4.797944664e-02f, }}, - {{ 5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, 6.779013272e-02f, -1.659481923e-01f, -4.797944664e-02f, }}, - {{ 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f, }}, - {{ 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f, }}, - {{ 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f, }}, - {{ 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f, }}, - {{ 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f, }}, - {{ 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f, }}, - {{ 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f, }}, - {{ 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f, }}, -}}; -constexpr std::array<AmbiChannelFloatArray,20> ThirdOrderEncoder{{ +constexpr std::array ThirdOrderDecoder{ + std::array{5.000000000e-02f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f}, + std::array{5.000000000e-02f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f}, + std::array{5.000000000e-02f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f}, + std::array{5.000000000e-02f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f}, + std::array{5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f}, + std::array{5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f}, + std::array{5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f}, + std::array{5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f}, + std::array{5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, -6.779013272e-02f, 1.659481923e-01f, 4.797944664e-02f}, + std::array{5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, 6.779013272e-02f, 1.659481923e-01f, -4.797944664e-02f}, + std::array{5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, -6.779013272e-02f, -1.659481923e-01f, 4.797944664e-02f}, + std::array{5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, 6.779013272e-02f, -1.659481923e-01f, -4.797944664e-02f}, + std::array{5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f}, + std::array{5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f}, + std::array{5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f}, + std::array{5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f}, + std::array{5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f}, + std::array{5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f}, + std::array{5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f}, + std::array{5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f}, +}; +constexpr std::array ThirdOrderEncoder{ CalcAmbiCoeffs( 0.35682208976f, 0.93417235897f, 0.00000000000f), CalcAmbiCoeffs(-0.35682208976f, 0.93417235897f, 0.00000000000f), CalcAmbiCoeffs( 0.35682208976f, -0.93417235897f, 0.00000000000f), @@ -195,24 +195,24 @@ constexpr std::array<AmbiChannelFloatArray,20> ThirdOrderEncoder{{ CalcAmbiCoeffs( inv_sqrt3f, -inv_sqrt3f, -inv_sqrt3f), CalcAmbiCoeffs( -inv_sqrt3f, -inv_sqrt3f, inv_sqrt3f), CalcAmbiCoeffs( -inv_sqrt3f, -inv_sqrt3f, -inv_sqrt3f), -}}; +}; static_assert(ThirdOrderDecoder.size() == ThirdOrderEncoder.size(), "Third-order mismatch"); /* This calculates a 2D third-order "upsampler" matrix. Same as the third-order * matrix, just using a more optimized speaker array for horizontal-only * content. */ -constexpr std::array<std::array<float,16>,8> ThirdOrder2DDecoder{{ - {{ 1.250000000e-01f, -5.523559567e-02f, 0.0f, 1.333505242e-01f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, -1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 4.573941867e-02f, }}, - {{ 1.250000000e-01f, -1.333505242e-01f, 0.0f, 5.523559567e-02f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, 4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.104247249e-01f, }}, - {{ 1.250000000e-01f, -1.333505242e-01f, 0.0f, -5.523559567e-02f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, 4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.104247249e-01f, }}, - {{ 1.250000000e-01f, -5.523559567e-02f, 0.0f, -1.333505242e-01f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, -1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -4.573941867e-02f, }}, - {{ 1.250000000e-01f, 5.523559567e-02f, 0.0f, -1.333505242e-01f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, 1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -4.573941867e-02f, }}, - {{ 1.250000000e-01f, 1.333505242e-01f, 0.0f, -5.523559567e-02f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, -4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.104247249e-01f, }}, - {{ 1.250000000e-01f, 1.333505242e-01f, 0.0f, 5.523559567e-02f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, -4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.104247249e-01f, }}, - {{ 1.250000000e-01f, 5.523559567e-02f, 0.0f, 1.333505242e-01f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, 1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 4.573941867e-02f, }}, -}}; -constexpr std::array<AmbiChannelFloatArray,8> ThirdOrder2DEncoder{{ +constexpr std::array ThirdOrder2DDecoder{ + std::array{1.250000000e-01f, -5.523559567e-02f, 0.0f, 1.333505242e-01f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, -1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 4.573941867e-02f}, + std::array{1.250000000e-01f, -1.333505242e-01f, 0.0f, 5.523559567e-02f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, 4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.104247249e-01f}, + std::array{1.250000000e-01f, -1.333505242e-01f, 0.0f, -5.523559567e-02f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, 4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.104247249e-01f}, + std::array{1.250000000e-01f, -5.523559567e-02f, 0.0f, -1.333505242e-01f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, -1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -4.573941867e-02f}, + std::array{1.250000000e-01f, 5.523559567e-02f, 0.0f, -1.333505242e-01f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, 1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -4.573941867e-02f}, + std::array{1.250000000e-01f, 1.333505242e-01f, 0.0f, -5.523559567e-02f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, -4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.104247249e-01f}, + std::array{1.250000000e-01f, 1.333505242e-01f, 0.0f, 5.523559567e-02f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, -4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.104247249e-01f}, + std::array{1.250000000e-01f, 5.523559567e-02f, 0.0f, 1.333505242e-01f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, 1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 4.573941867e-02f}, +}; +constexpr std::array ThirdOrder2DEncoder{ CalcAmbiCoeffs(-0.38268343237f, 0.0f, 0.92387953251f), CalcAmbiCoeffs(-0.92387953251f, 0.0f, 0.38268343237f), CalcAmbiCoeffs(-0.92387953251f, 0.0f, -0.38268343237f), @@ -221,7 +221,7 @@ constexpr std::array<AmbiChannelFloatArray,8> ThirdOrder2DEncoder{{ CalcAmbiCoeffs( 0.92387953251f, 0.0f, -0.38268343237f), CalcAmbiCoeffs( 0.92387953251f, 0.0f, 0.38268343237f), CalcAmbiCoeffs( 0.38268343237f, 0.0f, 0.92387953251f), -}}; +}; static_assert(ThirdOrder2DDecoder.size() == ThirdOrder2DEncoder.size(), "Third-order 2D mismatch"); @@ -230,19 +230,19 @@ static_assert(ThirdOrder2DDecoder.size() == ThirdOrder2DEncoder.size(), "Third-o * the foreseeable future. This is only necessary for mixing horizontal-only * fourth-order content to 3D. */ -constexpr std::array<std::array<float,25>,10> FourthOrder2DDecoder{{ - {{ 1.000000000e-01f, 3.568220898e-02f, 0.0f, 1.098185471e-01f, 6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, 7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 5.620301997e-02f, 8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f, }}, - {{ 1.000000000e-01f, 9.341723590e-02f, 0.0f, 6.787159473e-02f, 9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, 2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -9.093839659e-02f, -5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f, }}, - {{ 1.000000000e-01f, 1.154700538e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.032795559e-01f, -9.561828875e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.014978717e-02f, }}, - {{ 1.000000000e-01f, 9.341723590e-02f, 0.0f, -6.787159473e-02f, -9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, 2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.093839659e-02f, 5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f, }}, - {{ 1.000000000e-01f, 3.568220898e-02f, 0.0f, -1.098185471e-01f, -6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, 7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -5.620301997e-02f, -8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f, }}, - {{ 1.000000000e-01f, -3.568220898e-02f, 0.0f, -1.098185471e-01f, 6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, -7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -5.620301997e-02f, 8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f, }}, - {{ 1.000000000e-01f, -9.341723590e-02f, 0.0f, -6.787159473e-02f, 9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, -2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.093839659e-02f, -5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f, }}, - {{ 1.000000000e-01f, -1.154700538e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.032795559e-01f, 9.561828875e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.014978717e-02f, }}, - {{ 1.000000000e-01f, -9.341723590e-02f, 0.0f, 6.787159473e-02f, -9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, -2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -9.093839659e-02f, 5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f, }}, - {{ 1.000000000e-01f, -3.568220898e-02f, 0.0f, 1.098185471e-01f, -6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, -7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 5.620301997e-02f, -8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f, }}, -}}; -constexpr std::array<AmbiChannelFloatArray,10> FourthOrder2DEncoder{{ +constexpr std::array FourthOrder2DDecoder{ + std::array{1.000000000e-01f, 3.568220898e-02f, 0.0f, 1.098185471e-01f, 6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, 7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 5.620301997e-02f, 8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f}, + std::array{1.000000000e-01f, 9.341723590e-02f, 0.0f, 6.787159473e-02f, 9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, 2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -9.093839659e-02f, -5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f}, + std::array{1.000000000e-01f, 1.154700538e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.032795559e-01f, -9.561828875e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.014978717e-02f}, + std::array{1.000000000e-01f, 9.341723590e-02f, 0.0f, -6.787159473e-02f, -9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, 2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.093839659e-02f, 5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f}, + std::array{1.000000000e-01f, 3.568220898e-02f, 0.0f, -1.098185471e-01f, -6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, 7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -5.620301997e-02f, -8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f}, + std::array{1.000000000e-01f, -3.568220898e-02f, 0.0f, -1.098185471e-01f, 6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, -7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -5.620301997e-02f, 8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f}, + std::array{1.000000000e-01f, -9.341723590e-02f, 0.0f, -6.787159473e-02f, 9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, -2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.093839659e-02f, -5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f}, + std::array{1.000000000e-01f, -1.154700538e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.032795559e-01f, 9.561828875e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.014978717e-02f}, + std::array{1.000000000e-01f, -9.341723590e-02f, 0.0f, 6.787159473e-02f, -9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, -2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -9.093839659e-02f, 5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f}, + std::array{1.000000000e-01f, -3.568220898e-02f, 0.0f, 1.098185471e-01f, -6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, -7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 5.620301997e-02f, -8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f}, +}; +constexpr std::array FourthOrder2DEncoder{ CalcAmbiCoeffs( 3.090169944e-01f, 0.000000000e+00f, 9.510565163e-01f), CalcAmbiCoeffs( 8.090169944e-01f, 0.000000000e+00f, 5.877852523e-01f), CalcAmbiCoeffs( 1.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f), @@ -253,12 +253,12 @@ constexpr std::array<AmbiChannelFloatArray,10> FourthOrder2DEncoder{{ CalcAmbiCoeffs(-1.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f), CalcAmbiCoeffs(-8.090169944e-01f, 0.000000000e+00f, 5.877852523e-01f), CalcAmbiCoeffs(-3.090169944e-01f, 0.000000000e+00f, 9.510565163e-01f), -}}; +}; static_assert(FourthOrder2DDecoder.size() == FourthOrder2DEncoder.size(), "Fourth-order 2D mismatch"); template<size_t N, size_t M> -auto CalcAmbiUpsampler(const std::array<std::array<float,N>,M> &decoder, +constexpr auto CalcAmbiUpsampler(const std::array<std::array<float,N>,M> &decoder, const std::array<AmbiChannelFloatArray,M> &encoder) { std::array<AmbiChannelFloatArray,N> res{}; @@ -279,13 +279,13 @@ auto CalcAmbiUpsampler(const std::array<std::array<float,N>,M> &decoder, } // namespace -const std::array<AmbiChannelFloatArray,4> AmbiScale::FirstOrderUp{CalcAmbiUpsampler(FirstOrderDecoder, FirstOrderEncoder)}; -const std::array<AmbiChannelFloatArray,4> AmbiScale::FirstOrder2DUp{CalcAmbiUpsampler(FirstOrder2DDecoder, FirstOrder2DEncoder)}; -const std::array<AmbiChannelFloatArray,9> AmbiScale::SecondOrderUp{CalcAmbiUpsampler(SecondOrderDecoder, SecondOrderEncoder)}; -const std::array<AmbiChannelFloatArray,9> AmbiScale::SecondOrder2DUp{CalcAmbiUpsampler(SecondOrder2DDecoder, SecondOrder2DEncoder)}; -const std::array<AmbiChannelFloatArray,16> AmbiScale::ThirdOrderUp{CalcAmbiUpsampler(ThirdOrderDecoder, ThirdOrderEncoder)}; -const std::array<AmbiChannelFloatArray,16> AmbiScale::ThirdOrder2DUp{CalcAmbiUpsampler(ThirdOrder2DDecoder, ThirdOrder2DEncoder)}; -const std::array<AmbiChannelFloatArray,25> AmbiScale::FourthOrder2DUp{CalcAmbiUpsampler(FourthOrder2DDecoder, FourthOrder2DEncoder)}; +const std::array<std::array<float,MaxAmbiChannels>,4> AmbiScale::FirstOrderUp{CalcAmbiUpsampler(FirstOrderDecoder, FirstOrderEncoder)}; +const std::array<std::array<float,MaxAmbiChannels>,4> AmbiScale::FirstOrder2DUp{CalcAmbiUpsampler(FirstOrder2DDecoder, FirstOrder2DEncoder)}; +const std::array<std::array<float,MaxAmbiChannels>,9> AmbiScale::SecondOrderUp{CalcAmbiUpsampler(SecondOrderDecoder, SecondOrderEncoder)}; +const std::array<std::array<float,MaxAmbiChannels>,9> AmbiScale::SecondOrder2DUp{CalcAmbiUpsampler(SecondOrder2DDecoder, SecondOrder2DEncoder)}; +const std::array<std::array<float,MaxAmbiChannels>,16> AmbiScale::ThirdOrderUp{CalcAmbiUpsampler(ThirdOrderDecoder, ThirdOrderEncoder)}; +const std::array<std::array<float,MaxAmbiChannels>,16> AmbiScale::ThirdOrder2DUp{CalcAmbiUpsampler(ThirdOrder2DDecoder, ThirdOrder2DEncoder)}; +const std::array<std::array<float,MaxAmbiChannels>,25> AmbiScale::FourthOrder2DUp{CalcAmbiUpsampler(FourthOrder2DDecoder, FourthOrder2DEncoder)}; std::array<float,MaxAmbiOrder+1> AmbiScale::GetHFOrderScales(const uint src_order, diff --git a/core/ambidefs.h b/core/ambidefs.h index b7d2bcd1..bea1a312 100644 --- a/core/ambidefs.h +++ b/core/ambidefs.h @@ -14,26 +14,26 @@ using uint = unsigned int; * needed will be (o+1)**2, thus zero-order has 1, first-order has 4, second- * order has 9, third-order has 16, and fourth-order has 25. */ -constexpr uint8_t MaxAmbiOrder{3}; +inline constexpr uint8_t MaxAmbiOrder{3}; constexpr inline size_t AmbiChannelsFromOrder(size_t order) noexcept { return (order+1) * (order+1); } -constexpr size_t MaxAmbiChannels{AmbiChannelsFromOrder(MaxAmbiOrder)}; +inline constexpr size_t MaxAmbiChannels{AmbiChannelsFromOrder(MaxAmbiOrder)}; /* A bitmask of ambisonic channels for 0 to 4th order. This only specifies up * to 4th order, which is the highest order a 32-bit mask value can specify (a * 64-bit mask could handle up to 7th order). */ -constexpr uint Ambi0OrderMask{0x00000001}; -constexpr uint Ambi1OrderMask{0x0000000f}; -constexpr uint Ambi2OrderMask{0x000001ff}; -constexpr uint Ambi3OrderMask{0x0000ffff}; -constexpr uint Ambi4OrderMask{0x01ffffff}; +inline constexpr uint Ambi0OrderMask{0x00000001}; +inline constexpr uint Ambi1OrderMask{0x0000000f}; +inline constexpr uint Ambi2OrderMask{0x000001ff}; +inline constexpr uint Ambi3OrderMask{0x0000ffff}; +inline constexpr uint Ambi4OrderMask{0x01ffffff}; /* A bitmask of ambisonic channels with height information. If none of these * channels are used/needed, there's no height (e.g. with most surround sound * speaker setups). This is ACN ordering, with bit 0 being ACN 0, etc. */ -constexpr uint AmbiPeriphonicMask{0xfe7ce4}; +inline constexpr uint AmbiPeriphonicMask{0xfe7ce4}; /* The maximum number of ambisonic channels for 2D (non-periphonic) * representation. This is 2 per each order above zero-order, plus 1 for zero- @@ -41,77 +41,61 @@ constexpr uint AmbiPeriphonicMask{0xfe7ce4}; */ constexpr inline size_t Ambi2DChannelsFromOrder(size_t order) noexcept { return order*2 + 1; } -constexpr size_t MaxAmbi2DChannels{Ambi2DChannelsFromOrder(MaxAmbiOrder)}; +inline constexpr size_t MaxAmbi2DChannels{Ambi2DChannelsFromOrder(MaxAmbiOrder)}; /* NOTE: These are scale factors as applied to Ambisonics content. Decoder * coefficients should be divided by these values to get proper scalings. */ struct AmbiScale { - static auto& FromN3D() noexcept - { - static constexpr const std::array<float,MaxAmbiChannels> ret{{ - 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 - }}; - return ret; - } - static auto& FromSN3D() noexcept - { - static constexpr const std::array<float,MaxAmbiChannels> ret{{ - 1.000000000f, /* ACN 0, sqrt(1) */ - 1.732050808f, /* ACN 1, sqrt(3) */ - 1.732050808f, /* ACN 2, sqrt(3) */ - 1.732050808f, /* ACN 3, sqrt(3) */ - 2.236067978f, /* ACN 4, sqrt(5) */ - 2.236067978f, /* ACN 5, sqrt(5) */ - 2.236067978f, /* ACN 6, sqrt(5) */ - 2.236067978f, /* ACN 7, sqrt(5) */ - 2.236067978f, /* ACN 8, sqrt(5) */ - 2.645751311f, /* ACN 9, sqrt(7) */ - 2.645751311f, /* ACN 10, sqrt(7) */ - 2.645751311f, /* ACN 11, sqrt(7) */ - 2.645751311f, /* ACN 12, sqrt(7) */ - 2.645751311f, /* ACN 13, sqrt(7) */ - 2.645751311f, /* ACN 14, sqrt(7) */ - 2.645751311f, /* ACN 15, sqrt(7) */ - }}; - return ret; - } - static auto& FromFuMa() noexcept - { - static constexpr const std::array<float,MaxAmbiChannels> ret{{ - 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) */ - }}; - return ret; - } - static auto& FromUHJ() noexcept - { - static constexpr const std::array<float,MaxAmbiChannels> ret{{ - 1.000000000f, /* ACN 0 (W), sqrt(1) */ - 1.224744871f, /* ACN 1 (Y), sqrt(3/2) */ - 1.224744871f, /* ACN 2 (Z), sqrt(3/2) */ - 1.224744871f, /* ACN 3 (X), sqrt(3/2) */ - /* Higher orders not relevant for UHJ. */ - 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, - }}; - return ret; - } + static inline constexpr std::array<float,MaxAmbiChannels> FromN3D{{ + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f + }}; + static inline constexpr std::array<float,MaxAmbiChannels> FromSN3D{{ + 1.000000000f, /* ACN 0, sqrt(1) */ + 1.732050808f, /* ACN 1, sqrt(3) */ + 1.732050808f, /* ACN 2, sqrt(3) */ + 1.732050808f, /* ACN 3, sqrt(3) */ + 2.236067978f, /* ACN 4, sqrt(5) */ + 2.236067978f, /* ACN 5, sqrt(5) */ + 2.236067978f, /* ACN 6, sqrt(5) */ + 2.236067978f, /* ACN 7, sqrt(5) */ + 2.236067978f, /* ACN 8, sqrt(5) */ + 2.645751311f, /* ACN 9, sqrt(7) */ + 2.645751311f, /* ACN 10, sqrt(7) */ + 2.645751311f, /* ACN 11, sqrt(7) */ + 2.645751311f, /* ACN 12, sqrt(7) */ + 2.645751311f, /* ACN 13, sqrt(7) */ + 2.645751311f, /* ACN 14, sqrt(7) */ + 2.645751311f, /* ACN 15, sqrt(7) */ + }}; + static inline constexpr std::array<float,MaxAmbiChannels> FromFuMa{{ + 1.414213562f, /* ACN 0 (W), sqrt(2) */ + 1.732050808f, /* ACN 1 (Y), sqrt(3) */ + 1.732050808f, /* ACN 2 (Z), sqrt(3) */ + 1.732050808f, /* ACN 3 (X), sqrt(3) */ + 1.936491673f, /* ACN 4 (V), sqrt(15)/2 */ + 1.936491673f, /* ACN 5 (T), sqrt(15)/2 */ + 2.236067978f, /* ACN 6 (R), sqrt(5) */ + 1.936491673f, /* ACN 7 (S), sqrt(15)/2 */ + 1.936491673f, /* ACN 8 (U), sqrt(15)/2 */ + 2.091650066f, /* ACN 9 (Q), sqrt(35/8) */ + 1.972026594f, /* ACN 10 (O), sqrt(35)/3 */ + 2.231093404f, /* ACN 11 (M), sqrt(224/45) */ + 2.645751311f, /* ACN 12 (K), sqrt(7) */ + 2.231093404f, /* ACN 13 (L), sqrt(224/45) */ + 1.972026594f, /* ACN 14 (N), sqrt(35)/3 */ + 2.091650066f, /* ACN 15 (P), sqrt(35/8) */ + }}; + static inline constexpr std::array<float,MaxAmbiChannels> FromUHJ{{ + 1.000000000f, /* ACN 0 (W), sqrt(1) */ + 1.224744871f, /* ACN 1 (Y), sqrt(3/2) */ + 1.224744871f, /* ACN 2 (Z), sqrt(3/2) */ + 1.224744871f, /* ACN 3 (X), sqrt(3/2) */ + /* Higher orders not relevant for UHJ. */ + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, + }}; /* Retrieves per-order HF scaling factors for "upsampling" ambisonic data. */ static std::array<float,MaxAmbiOrder+1> GetHFOrderScales(const uint src_order, @@ -127,72 +111,49 @@ struct AmbiScale { }; struct AmbiIndex { - static auto& FromFuMa() noexcept - { - static constexpr const std::array<uint8_t,MaxAmbiChannels> ret{{ - 0, /* W */ - 3, /* X */ - 1, /* Y */ - 2, /* Z */ - 6, /* R */ - 7, /* S */ - 5, /* T */ - 8, /* U */ - 4, /* V */ - 12, /* K */ - 13, /* L */ - 11, /* M */ - 14, /* N */ - 10, /* O */ - 15, /* P */ - 9, /* Q */ - }}; - return ret; - } - static auto& FromFuMa2D() noexcept - { - static constexpr const std::array<uint8_t,MaxAmbi2DChannels> ret{{ - 0, /* W */ - 3, /* X */ - 1, /* Y */ - 8, /* U */ - 4, /* V */ - 15, /* P */ - 9, /* Q */ - }}; - return ret; - } - - static auto& FromACN() noexcept - { - static constexpr const std::array<uint8_t,MaxAmbiChannels> ret{{ - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15 - }}; - return ret; - } - static auto& FromACN2D() noexcept - { - static constexpr const std::array<uint8_t,MaxAmbi2DChannels> ret{{ - 0, 1,3, 4,8, 9,15 - }}; - return ret; - } - - static auto& OrderFromChannel() noexcept - { - static constexpr const std::array<uint8_t,MaxAmbiChannels> ret{{ - 0, 1,1,1, 2,2,2,2,2, 3,3,3,3,3,3,3, - }}; - return ret; - } - static auto& OrderFrom2DChannel() noexcept - { - static constexpr const std::array<uint8_t,MaxAmbi2DChannels> ret{{ - 0, 1,1, 2,2, 3,3, - }}; - return ret; - } + static inline constexpr std::array<uint8_t,MaxAmbiChannels> FromFuMa{{ + 0, /* W */ + 3, /* X */ + 1, /* Y */ + 2, /* Z */ + 6, /* R */ + 7, /* S */ + 5, /* T */ + 8, /* U */ + 4, /* V */ + 12, /* K */ + 13, /* L */ + 11, /* M */ + 14, /* N */ + 10, /* O */ + 15, /* P */ + 9, /* Q */ + }}; + static inline constexpr std::array<uint8_t,MaxAmbi2DChannels> FromFuMa2D{{ + 0, /* W */ + 3, /* X */ + 1, /* Y */ + 8, /* U */ + 4, /* V */ + 15, /* P */ + 9, /* Q */ + }}; + + static inline constexpr std::array<uint8_t,MaxAmbiChannels> FromACN{{ + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15 + }}; + static inline constexpr std::array<uint8_t,MaxAmbi2DChannels> FromACN2D{{ + 0, 1,3, 4,8, 9,15 + }}; + + + static inline constexpr std::array<uint8_t,MaxAmbiChannels> OrderFromChannel{{ + 0, 1,1,1, 2,2,2,2,2, 3,3,3,3,3,3,3, + }}; + static inline constexpr std::array<uint8_t,MaxAmbi2DChannels> OrderFrom2DChannel{{ + 0, 1,1, 2,2, 3,3, + }}; }; diff --git a/core/async_event.h b/core/async_event.h index 5a2f5f91..f1ca0c7b 100644 --- a/core/async_event.h +++ b/core/async_event.h @@ -1,6 +1,9 @@ #ifndef CORE_EVENT_H #define CORE_EVENT_H +#include <stdint.h> +#include <variant> + #include "almalloc.h" struct EffectState; @@ -8,48 +11,53 @@ struct EffectState; using uint = unsigned int; -struct AsyncEvent { - enum : uint { - /* User event types. */ - SourceStateChange, - BufferCompleted, - Disconnected, - UserEventCount, - - /* Internal events, always processed. */ - ReleaseEffectState = 128, - - /* End event thread processing. */ - KillThread, - }; - - enum class SrcState { - Reset, - Stop, - Play, - Pause - }; - - const uint EnumType; - union { - char dummy; - struct { - uint id; - SrcState state; - } srcstate; - struct { - uint id; - uint count; - } bufcomp; - struct { - char msg[244]; - } disconnect; - EffectState *mEffectState; - } u{}; - - constexpr AsyncEvent(uint type) noexcept : EnumType{type} { } - - DISABLE_ALLOC() +enum class AsyncEnableBits : uint8_t { + SourceState, + BufferCompleted, + Disconnected, + Count +}; + + +enum class AsyncSrcState : uint8_t { + Reset, + Stop, + Play, + Pause +}; + +using AsyncKillThread = std::monostate; + +struct AsyncSourceStateEvent { + uint mId; + AsyncSrcState mState; }; +struct AsyncBufferCompleteEvent { + uint mId; + uint mCount; +}; + +struct AsyncDisconnectEvent { + char msg[244]; +}; + +struct AsyncEffectReleaseEvent { + EffectState *mEffectState; +}; + +using AsyncEvent = std::variant<AsyncKillThread, + AsyncSourceStateEvent, + AsyncBufferCompleteEvent, + AsyncEffectReleaseEvent, + AsyncDisconnectEvent>; + +template<typename T, typename ...Args> +auto &InitAsyncEvent(std::byte *evtbuf, Args&& ...args) +{ + auto *evt = al::construct_at(reinterpret_cast<AsyncEvent*>(evtbuf), std::in_place_type<T>, + std::forward<Args>(args)...); + return std::get<T>(*evt); +} + #endif diff --git a/core/bformatdec.cpp b/core/bformatdec.cpp index 129b9976..a308e185 100644 --- a/core/bformatdec.cpp +++ b/core/bformatdec.cpp @@ -16,33 +16,45 @@ #include "opthelpers.h" +namespace { + +template<typename... Ts> +struct overloaded : Ts... { using Ts::operator()...; }; + +template<typename... Ts> +overloaded(Ts...) -> overloaded<Ts...>; + +} // namespace + BFormatDec::BFormatDec(const size_t inchans, const al::span<const ChannelDec> coeffs, const al::span<const ChannelDec> coeffslf, const float xover_f0norm, std::unique_ptr<FrontStablizer> stablizer) - : mStablizer{std::move(stablizer)}, mDualBand{!coeffslf.empty()}, mChannelDec{inchans} + : mStablizer{std::move(stablizer)} { - if(!mDualBand) + if(coeffslf.empty()) { - for(size_t j{0};j < mChannelDec.size();++j) + auto &decoder = mChannelDec.emplace<std::vector<ChannelDecoderSingle>>(inchans); + for(size_t j{0};j < decoder.size();++j) { - float *outcoeffs{mChannelDec[j].mGains.Single}; + float *outcoeffs{decoder[j].mGains}; for(const ChannelDec &incoeffs : coeffs) *(outcoeffs++) = incoeffs[j]; } } else { - mChannelDec[0].mXOver.init(xover_f0norm); - for(size_t j{1};j < mChannelDec.size();++j) - mChannelDec[j].mXOver = mChannelDec[0].mXOver; + auto &decoder = mChannelDec.emplace<std::vector<ChannelDecoderDual>>(inchans); + decoder[0].mXOver.init(xover_f0norm); + for(size_t j{1};j < decoder.size();++j) + decoder[j].mXOver = decoder[0].mXOver; - for(size_t j{0};j < mChannelDec.size();++j) + for(size_t j{0};j < decoder.size();++j) { - float *outcoeffs{mChannelDec[j].mGains.Dual[sHFBand]}; + float *outcoeffs{decoder[j].mGains[sHFBand]}; for(const ChannelDec &incoeffs : coeffs) *(outcoeffs++) = incoeffs[j]; - outcoeffs = mChannelDec[j].mGains.Dual[sLFBand]; + outcoeffs = decoder[j].mGains[sLFBand]; for(const ChannelDec &incoeffs : coeffslf) *(outcoeffs++) = incoeffs[j]; } @@ -55,30 +67,32 @@ void BFormatDec::process(const al::span<FloatBufferLine> OutBuffer, { ASSUME(SamplesToDo > 0); - if(mDualBand) + auto decode_dualband = [=](std::vector<ChannelDecoderDual> &decoder) { + auto *input = InSamples; const al::span<float> hfSamples{mSamples[sHFBand].data(), SamplesToDo}; const al::span<float> lfSamples{mSamples[sLFBand].data(), SamplesToDo}; - for(auto &chandec : mChannelDec) + for(auto &chandec : decoder) { - chandec.mXOver.process({InSamples->data(), SamplesToDo}, hfSamples.data(), + chandec.mXOver.process({input->data(), SamplesToDo}, hfSamples.data(), lfSamples.data()); - MixSamples(hfSamples, OutBuffer, chandec.mGains.Dual[sHFBand], - chandec.mGains.Dual[sHFBand], 0, 0); - MixSamples(lfSamples, OutBuffer, chandec.mGains.Dual[sLFBand], - chandec.mGains.Dual[sLFBand], 0, 0); - ++InSamples; + MixSamples(hfSamples, OutBuffer, chandec.mGains[sHFBand], chandec.mGains[sHFBand],0,0); + MixSamples(lfSamples, OutBuffer, chandec.mGains[sLFBand], chandec.mGains[sLFBand],0,0); + ++input; } - } - else + }; + auto decode_singleband = [=](std::vector<ChannelDecoderSingle> &decoder) { - for(auto &chandec : mChannelDec) + auto *input = InSamples; + for(auto &chandec : decoder) { - MixSamples({InSamples->data(), SamplesToDo}, OutBuffer, chandec.mGains.Single, - chandec.mGains.Single, 0, 0); - ++InSamples; + MixSamples({input->data(), SamplesToDo}, OutBuffer, chandec.mGains, chandec.mGains, + 0, 0); + ++input; } - } + }; + + std::visit(overloaded{decode_dualband, decode_singleband}, mChannelDec); } void BFormatDec::processStablize(const al::span<FloatBufferLine> OutBuffer, diff --git a/core/bformatdec.h b/core/bformatdec.h index 7a27a5a4..3bb7f544 100644 --- a/core/bformatdec.h +++ b/core/bformatdec.h @@ -4,6 +4,8 @@ #include <array> #include <cstddef> #include <memory> +#include <variant> +#include <vector> #include "almalloc.h" #include "alspan.h" @@ -11,7 +13,6 @@ #include "bufferline.h" #include "devformat.h" #include "filters/splitter.h" -#include "vector.h" struct FrontStablizer; @@ -23,27 +24,20 @@ class BFormatDec { static constexpr size_t sLFBand{1}; static constexpr size_t sNumBands{2}; - struct ChannelDecoder { - union MatrixU { - float Dual[sNumBands][MAX_OUTPUT_CHANNELS]; - float Single[MAX_OUTPUT_CHANNELS]; - } mGains{}; + struct ChannelDecoderSingle { + float mGains[MAX_OUTPUT_CHANNELS]; + }; - /* NOTE: BandSplitter filter is unused with single-band decoding. */ + struct ChannelDecoderDual { BandSplitter mXOver; + float mGains[sNumBands][MAX_OUTPUT_CHANNELS]; }; alignas(16) std::array<FloatBufferLine,2> mSamples; const std::unique_ptr<FrontStablizer> mStablizer; - const bool mDualBand{false}; - - /* TODO: This should ideally be a FlexArray, since ChannelDecoder is rather - * small and only a few are needed (3, 4, 5, 7, typically). But that can - * only be used in a standard layout struct, and a std::unique_ptr member - * (mStablizer) causes GCC and Clang to warn it's not. - */ - al::vector<ChannelDecoder> mChannelDec; + + std::variant<std::vector<ChannelDecoderSingle>,std::vector<ChannelDecoderDual>> mChannelDec; public: BFormatDec(const size_t inchans, const al::span<const ChannelDec> coeffs, diff --git a/core/bsinc_tables.cpp b/core/bsinc_tables.cpp index 693645f4..41102e9a 100644 --- a/core/bsinc_tables.cpp +++ b/core/bsinc_tables.cpp @@ -7,48 +7,50 @@ #include <cmath> #include <limits> #include <memory> +#include <stddef.h> #include <stdexcept> #include "alnumbers.h" -#include "core/mixer/defs.h" +#include "alnumeric.h" +#include "bsinc_defs.h" +#include "resampler_limits.h" namespace { using uint = unsigned int; +#if __cpp_lib_math_special_functions >= 201603L +using std::cyl_bessel_i; -/* This is the normalized cardinal sine (sinc) function. - * - * sinc(x) = { 1, x = 0 - * { sin(pi x) / (pi x), otherwise. - */ -constexpr double Sinc(const double x) -{ - constexpr double epsilon{std::numeric_limits<double>::epsilon()}; - if(!(x > epsilon || x < -epsilon)) - return 1.0; - return std::sin(al::numbers::pi*x) / (al::numbers::pi*x); -} +#else /* 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 + * + * This implementation only handles nu = 0, and isn't the most precise (it + * starts with the largest value and accumulates successively smaller values, + * compounding the rounding and precision error), but it's good enough. */ -constexpr double BesselI_0(const double x) noexcept +template<typename T, typename U> +U cyl_bessel_i(T nu, U x) { + if(nu != T{0}) + throw std::runtime_error{"cyl_bessel_i: nu != 0"}; + /* Start at k=1 since k=0 is trivial. */ - const double x2{x / 2.0}; + const double x2{x/2.0}; double term{1.0}; double sum{1.0}; - double last_sum{}; int k{1}; /* Let the integration converge until the term of the sum is no longer * significant. */ + double last_sum{}; do { const double y{x2 / k}; ++k; @@ -56,8 +58,21 @@ constexpr double BesselI_0(const double x) noexcept term *= y * y; sum += term; } while(sum != last_sum); + return static_cast<U>(sum); +} +#endif - return sum; +/* This is the normalized cardinal sine (sinc) function. + * + * sinc(x) = { 1, x = 0 + * { sin(pi x) / (pi x), otherwise. + */ +constexpr double Sinc(const double x) +{ + constexpr double epsilon{std::numeric_limits<double>::epsilon()}; + if(!(x > epsilon || x < -epsilon)) + return 1.0; + return std::sin(al::numbers::pi*x) / (al::numbers::pi*x); } /* Calculate a Kaiser window from the given beta value and a normalized k @@ -78,7 +93,7 @@ constexpr double Kaiser(const double beta, const double k, const double besseli_ { if(!(k >= -1.0 && k <= 1.0)) return 0.0; - return BesselI_0(beta * std::sqrt(1.0 - k*k)) / besseli_0_beta; + return cyl_bessel_i(0, beta * std::sqrt(1.0 - k*k)) / besseli_0_beta; } /* Calculates the (normalized frequency) transition width of the Kaiser window. @@ -107,8 +122,6 @@ struct BSincHeader { double width{}; double beta{}; double scaleBase{}; - double scaleRange{}; - double besseli_0_beta{}; uint a[BSincScaleCount]{}; uint total_size{}; @@ -118,13 +131,11 @@ struct BSincHeader { width = CalcKaiserWidth(Rejection, Order); beta = CalcKaiserBeta(Rejection); scaleBase = width / 2.0; - scaleRange = 1.0 - scaleBase; - besseli_0_beta = BesselI_0(beta); uint num_points{Order+1}; for(uint si{0};si < BSincScaleCount;++si) { - const double scale{scaleBase + (scaleRange * (si+1) / BSincScaleCount)}; + const double scale{lerpd(scaleBase, 1.0, (si+1) / double{BSincScaleCount})}; const uint a_{std::min(static_cast<uint>(num_points / 2.0 / scale), num_points)}; const uint m{2 * a_}; @@ -142,26 +153,6 @@ constexpr BSincHeader bsinc12_hdr{60, 11}; constexpr BSincHeader bsinc24_hdr{60, 23}; -/* NOTE: GCC 5 has an issue with BSincHeader objects being in an anonymous - * namespace while also being used as non-type template parameters. - */ -#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 6 - -/* The number of sample points is double the a value (rounded up to a multiple - * of 4), and scale index 0 includes the doubling for downsampling. bsinc24 is - * currently the highest quality filter, and will use the most sample points. - */ -constexpr uint BSincPointsMax{(bsinc24_hdr.a[0]*2 + 3) & ~3u}; -static_assert(BSincPointsMax <= MaxResamplerPadding, "MaxResamplerPadding is too small"); - -template<size_t total_size> -struct BSincFilterArray { - alignas(16) std::array<float, total_size> mTable; - const BSincHeader &hdr; - - BSincFilterArray(const BSincHeader &hdr_) : hdr{hdr_} - { -#else template<const BSincHeader &hdr> struct BSincFilterArray { alignas(16) std::array<float, hdr.total_size> mTable{}; @@ -170,10 +161,12 @@ struct BSincFilterArray { { constexpr uint BSincPointsMax{(hdr.a[0]*2 + 3) & ~3u}; static_assert(BSincPointsMax <= MaxResamplerPadding, "MaxResamplerPadding is too small"); -#endif + using filter_type = double[BSincPhaseCount+1][BSincPointsMax]; auto filter = std::make_unique<filter_type[]>(BSincScaleCount); + const double besseli_0_beta{cyl_bessel_i(0, hdr.beta)}; + /* Calculate the Kaiser-windowed Sinc filter coefficients for each * scale and phase index. */ @@ -181,7 +174,7 @@ struct BSincFilterArray { { const uint m{hdr.a[si] * 2}; const size_t o{(BSincPointsMax-m) / 2}; - const double scale{hdr.scaleBase + (hdr.scaleRange * (si+1) / BSincScaleCount)}; + const double scale{lerpd(hdr.scaleBase, 1.0, (si+1) / double{BSincScaleCount})}; const double cutoff{scale - (hdr.scaleBase * std::max(1.0, scale*2.0))}; const auto a = static_cast<double>(hdr.a[si]); const double l{a - 1.0/BSincPhaseCount}; @@ -196,7 +189,7 @@ struct BSincFilterArray { for(uint i{0};i < m;++i) { const double x{i - phase}; - filter[si][pi][o+i] = Kaiser(hdr.beta, x/l, hdr.besseli_0_beta) * cutoff * + filter[si][pi][o+i] = Kaiser(hdr.beta, x/l, besseli_0_beta) * cutoff * Sinc(cutoff*x); } } @@ -265,13 +258,8 @@ struct BSincFilterArray { constexpr const float *getTable() const noexcept { return &mTable.front(); } }; -#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 6 -const BSincFilterArray<bsinc12_hdr.total_size> bsinc12_filter{bsinc12_hdr}; -const BSincFilterArray<bsinc24_hdr.total_size> bsinc24_filter{bsinc24_hdr}; -#else const BSincFilterArray<bsinc12_hdr> bsinc12_filter{}; const BSincFilterArray<bsinc24_hdr> bsinc24_filter{}; -#endif template<typename T> constexpr BSincTable GenerateBSincTable(const T &filter) @@ -279,7 +267,7 @@ constexpr BSincTable GenerateBSincTable(const T &filter) BSincTable ret{}; const BSincHeader &hdr = filter.getHeader(); ret.scaleBase = static_cast<float>(hdr.scaleBase); - ret.scaleRange = static_cast<float>(1.0 / hdr.scaleRange); + ret.scaleRange = static_cast<float>(1.0 / (1.0 - hdr.scaleBase)); for(size_t i{0};i < BSincScaleCount;++i) ret.m[i] = ((hdr.a[i]*2) + 3) & ~3u; ret.filterOffset[0] = 0; diff --git a/core/buffer_storage.cpp b/core/buffer_storage.cpp index 98ca2c1b..6ffab124 100644 --- a/core/buffer_storage.cpp +++ b/core/buffer_storage.cpp @@ -12,6 +12,7 @@ const char *NameFromFormat(FmtType type) noexcept { case FmtUByte: return "UInt8"; case FmtShort: return "Int16"; + case FmtInt: return "Int32"; case FmtFloat: return "Float"; case FmtDouble: return "Double"; case FmtMulaw: return "muLaw"; @@ -49,6 +50,7 @@ uint BytesFromFmt(FmtType type) noexcept { case FmtUByte: return sizeof(uint8_t); case FmtShort: return sizeof(int16_t); + case FmtInt: return sizeof(int32_t); case FmtFloat: return sizeof(float); case FmtDouble: return sizeof(double); case FmtMulaw: return sizeof(uint8_t); diff --git a/core/buffer_storage.h b/core/buffer_storage.h index 282d5b53..3b581b5e 100644 --- a/core/buffer_storage.h +++ b/core/buffer_storage.h @@ -2,8 +2,8 @@ #define CORE_BUFFER_STORAGE_H #include <atomic> +#include <cstddef> -#include "albyte.h" #include "alnumeric.h" #include "alspan.h" #include "ambidefs.h" @@ -15,6 +15,7 @@ using uint = unsigned int; enum FmtType : unsigned char { FmtUByte, FmtShort, + FmtInt, FmtFloat, FmtDouble, FmtMulaw, @@ -85,7 +86,7 @@ struct BufferStorage { CallbackType mCallback{nullptr}; void *mUserData{nullptr}; - al::span<al::byte> mData; + al::span<std::byte> mData; uint mSampleRate{0u}; FmtChannels mChannels{FmtMono}; diff --git a/core/context.cpp b/core/context.cpp index d68d8327..2ebbc7b1 100644 --- a/core/context.cpp +++ b/core/context.cpp @@ -2,7 +2,10 @@ #include "config.h" #include <cassert> +#include <limits> #include <memory> +#include <stdexcept> +#include <utility> #include "async_event.h" #include "context.h" @@ -51,7 +54,7 @@ ContextBase::~ContextBase() if(EffectSlotArray *curarray{mActiveAuxSlots.exchange(nullptr, std::memory_order_relaxed)}) { - al::destroy_n(curarray->end(), curarray->size()); + std::destroy_n(curarray->end(), curarray->size()); delete curarray; } @@ -63,12 +66,14 @@ ContextBase::~ContextBase() auto evt_vec = mAsyncEvents->getReadVector(); if(evt_vec.first.len > 0) { - al::destroy_n(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf), evt_vec.first.len); + std::destroy_n(std::launder(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf)), + evt_vec.first.len); count += evt_vec.first.len; } if(evt_vec.second.len > 0) { - al::destroy_n(reinterpret_cast<AsyncEvent*>(evt_vec.second.buf), evt_vec.second.len); + std::destroy_n(std::launder(reinterpret_cast<AsyncEvent*>(evt_vec.second.buf)), + evt_vec.second.len); count += evt_vec.second.len; } if(count > 0) diff --git a/core/context.h b/core/context.h index 9723eac3..ccb7dd3b 100644 --- a/core/context.h +++ b/core/context.h @@ -7,15 +7,15 @@ #include <cstddef> #include <memory> #include <thread> +#include <vector> #include "almalloc.h" +#include "alsem.h" #include "alspan.h" #include "async_event.h" #include "atomic.h" -#include "bufferline.h" -#include "threads.h" +#include "opthelpers.h" #include "vecmat.h" -#include "vector.h" struct DeviceBase; struct EffectSlot; @@ -25,8 +25,6 @@ struct Voice; struct VoiceChange; struct VoicePropsItem; -using uint = unsigned int; - constexpr float SpeedOfSoundMetersPerSec{343.3f}; @@ -137,7 +135,7 @@ struct ContextBase { std::thread mEventThread; al::semaphore mEventSem; std::unique_ptr<RingBuffer> mAsyncEvents; - using AsyncEventBitset = std::bitset<AsyncEvent::UserEventCount>; + using AsyncEventBitset = std::bitset<al::to_underlying(AsyncEnableBits::Count)>; std::atomic<AsyncEventBitset> mEnabledEvts{0u}; /* Asynchronous voice change actions are processed as a linked list of @@ -146,20 +144,20 @@ struct ContextBase { * in clusters that are stored in a vector for easy automatic cleanup. */ using VoiceChangeCluster = std::unique_ptr<VoiceChange[]>; - al::vector<VoiceChangeCluster> mVoiceChangeClusters; + std::vector<VoiceChangeCluster> mVoiceChangeClusters; using VoiceCluster = std::unique_ptr<Voice[]>; - al::vector<VoiceCluster> mVoiceClusters; + std::vector<VoiceCluster> mVoiceClusters; using VoicePropsCluster = std::unique_ptr<VoicePropsItem[]>; - al::vector<VoicePropsCluster> mVoicePropClusters; + std::vector<VoicePropsCluster> mVoicePropClusters; static constexpr size_t EffectSlotClusterSize{4}; EffectSlot *getEffectSlot(); using EffectSlotCluster = std::unique_ptr<EffectSlot[]>; - al::vector<EffectSlotCluster> mEffectSlotClusters; + std::vector<EffectSlotCluster> mEffectSlotClusters; ContextBase(DeviceBase *device); diff --git a/core/converter.cpp b/core/converter.cpp index a5141448..5b2f3e15 100644 --- a/core/converter.cpp +++ b/core/converter.cpp @@ -6,12 +6,12 @@ #include <algorithm> #include <cassert> #include <cmath> +#include <cstddef> #include <cstdint> #include <iterator> #include <limits.h> #include "albit.h" -#include "albyte.h" #include "alnumeric.h" #include "fpu_ctrl.h" @@ -219,7 +219,7 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint const uint SrcFrameSize{static_cast<uint>(mChan.size()) * mSrcTypeSize}; const uint DstFrameSize{static_cast<uint>(mChan.size()) * mDstTypeSize}; const uint increment{mIncrement}; - auto SamplesIn = static_cast<const al::byte*>(*src); + auto SamplesIn = static_cast<const std::byte*>(*src); uint NumSrcSamples{*srcframes}; FPUCtl mixer_mode{}; @@ -265,8 +265,8 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint for(size_t chan{0u};chan < mChan.size();chan++) { - const al::byte *SrcSamples{SamplesIn + mSrcTypeSize*chan}; - al::byte *DstSamples = static_cast<al::byte*>(dst) + mDstTypeSize*chan; + const std::byte *SrcSamples{SamplesIn + mSrcTypeSize*chan}; + std::byte *DstSamples = static_cast<std::byte*>(dst) + mDstTypeSize*chan; /* Load the previous samples into the source data first, then the * new samples from the input buffer. @@ -299,7 +299,7 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint SamplesIn += SrcFrameSize*srcread; NumSrcSamples -= srcread; - dst = static_cast<al::byte*>(dst) + DstFrameSize*DstSize; + dst = static_cast<std::byte*>(dst) + DstFrameSize*DstSize; pos += DstSize; } @@ -309,6 +309,98 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint return pos; } +uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void *const*dst, uint dstframes) +{ + const uint increment{mIncrement}; + uint NumSrcSamples{*srcframes}; + + FPUCtl mixer_mode{}; + uint pos{0}; + while(pos < dstframes && NumSrcSamples > 0) + { + const uint prepcount{mSrcPrepCount}; + const uint readable{minu(NumSrcSamples, BufferLineSize - prepcount)}; + + if(prepcount < MaxResamplerPadding && MaxResamplerPadding-prepcount >= readable) + { + /* Not enough input samples to generate an output sample. Store + * what we're given for later. + */ + for(size_t chan{0u};chan < mChan.size();chan++) + { + LoadSamples(&mChan[chan].PrevSamples[prepcount], + static_cast<const std::byte*>(src[chan]), 1, mSrcType, readable); + src[chan] = static_cast<const std::byte*>(src[chan]) + mSrcTypeSize*readable; + } + + mSrcPrepCount = prepcount + readable; + NumSrcSamples = 0; + break; + } + + float *RESTRICT SrcData{mSrcSamples}; + float *RESTRICT DstData{mDstSamples}; + uint DataPosFrac{mFracOffset}; + uint64_t DataSize64{prepcount}; + DataSize64 += readable; + DataSize64 -= MaxResamplerPadding; + DataSize64 <<= MixerFracBits; + DataSize64 -= DataPosFrac; + + /* If we have a full prep, we can generate at least one sample. */ + auto DstSize = static_cast<uint>( + clampu64((DataSize64 + increment-1)/increment, 1, BufferLineSize)); + DstSize = minu(DstSize, dstframes-pos); + + const uint DataPosEnd{DstSize*increment + DataPosFrac}; + const uint SrcDataEnd{DataPosEnd>>MixerFracBits}; + + assert(prepcount+readable >= SrcDataEnd); + const uint nextprep{minu(prepcount + readable - SrcDataEnd, MaxResamplerPadding)}; + + for(size_t chan{0u};chan < mChan.size();chan++) + { + /* Load the previous samples into the source data first, then the + * new samples from the input buffer. + */ + std::copy_n(mChan[chan].PrevSamples, prepcount, SrcData); + LoadSamples(SrcData + prepcount, src[chan], 1, mSrcType, readable); + + /* Store as many prep samples for next time as possible, given the + * number of output samples being generated. + */ + std::copy_n(SrcData+SrcDataEnd, nextprep, mChan[chan].PrevSamples); + std::fill(std::begin(mChan[chan].PrevSamples)+nextprep, + std::end(mChan[chan].PrevSamples), 0.0f); + + /* Now resample, and store the result in the output buffer. */ + mResample(&mState, SrcData+MaxResamplerEdge, DataPosFrac, increment, + {DstData, DstSize}); + + std::byte *DstSamples = static_cast<std::byte*>(dst[chan]) + pos*mDstTypeSize; + StoreSamples(DstSamples, DstData, 1, mDstType, DstSize); + } + + /* Update the number of prep samples still available, as well as the + * fractional offset. + */ + mSrcPrepCount = nextprep; + mFracOffset = DataPosEnd & MixerFracMask; + + /* Update the src and dst pointers in case there's still more to do. */ + const uint srcread{minu(NumSrcSamples, SrcDataEnd + mSrcPrepCount - prepcount)}; + for(size_t chan{0u};chan < mChan.size();chan++) + src[chan] = static_cast<const std::byte*>(src[chan]) + mSrcTypeSize*srcread; + NumSrcSamples -= srcread; + + pos += DstSize; + } + + *srcframes = NumSrcSamples; + + return pos; +} + void ChannelConverter::convert(const void *src, float *dst, uint frames) const { diff --git a/core/converter.h b/core/converter.h index 01becea2..49ca124d 100644 --- a/core/converter.h +++ b/core/converter.h @@ -36,6 +36,7 @@ struct SampleConverter { SampleConverter(size_t numchans) : mChan{numchans} { } uint convert(const void **src, uint *srcframes, void *dst, uint dstframes); + uint convertPlanar(const void **src, uint *srcframes, void *const*dst, uint dstframes); uint availableOut(uint srcframes) const; using SampleOffset = std::chrono::duration<int64_t, std::ratio<1,MixerFracOne>>; diff --git a/core/cpu_caps.cpp b/core/cpu_caps.cpp index d4b4d86c..1a064cf4 100644 --- a/core/cpu_caps.cpp +++ b/core/cpu_caps.cpp @@ -17,6 +17,7 @@ #include <intrin.h> #endif +#include <algorithm> #include <array> #include <cctype> #include <string> @@ -50,14 +51,14 @@ inline std::array<reg_type,4> get_cpuid(unsigned int f) } // namespace -al::optional<CPUInfo> GetCPUInfo() +std::optional<CPUInfo> GetCPUInfo() { CPUInfo ret; #ifdef CAN_GET_CPUID auto cpuregs = get_cpuid(0); if(cpuregs[0] == 0) - return al::nullopt; + return std::nullopt; const reg_type maxfunc{cpuregs[0]}; diff --git a/core/cpu_caps.h b/core/cpu_caps.h index ffd671d0..0826a49b 100644 --- a/core/cpu_caps.h +++ b/core/cpu_caps.h @@ -1,10 +1,9 @@ #ifndef CORE_CPU_CAPS_H #define CORE_CPU_CAPS_H +#include <optional> #include <string> -#include "aloptional.h" - extern int CPUCapFlags; enum { @@ -21,6 +20,6 @@ struct CPUInfo { int mCaps{0}; }; -al::optional<CPUInfo> GetCPUInfo(); +std::optional<CPUInfo> GetCPUInfo(); #endif /* CORE_CPU_CAPS_H */ diff --git a/core/cubic_tables.cpp b/core/cubic_tables.cpp index 73ec6b3f..5e7aafad 100644 --- a/core/cubic_tables.cpp +++ b/core/cubic_tables.cpp @@ -1,24 +1,16 @@ #include "cubic_tables.h" -#include <algorithm> #include <array> -#include <cassert> -#include <cmath> -#include <limits> -#include <memory> -#include <stdexcept> +#include <stddef.h> -#include "alnumbers.h" -#include "core/mixer/defs.h" +#include "cubic_defs.h" namespace { -using uint = unsigned int; - struct SplineFilterArray { - alignas(16) CubicCoefficients mTable[CubicPhaseCount]{}; + alignas(16) std::array<CubicCoefficients,CubicPhaseCount> mTable{}; constexpr SplineFilterArray() { @@ -49,7 +41,7 @@ struct SplineFilterArray { mTable[pi].mDeltas[3] = -mTable[pi].mCoeffs[3]; } - constexpr auto getTable() const noexcept { return al::as_span(mTable); } + constexpr auto& getTable() const noexcept { return mTable; } }; constexpr SplineFilterArray SplineFilter{}; diff --git a/core/dbus_wrap.cpp b/core/dbus_wrap.cpp index 7f221706..48419566 100644 --- a/core/dbus_wrap.cpp +++ b/core/dbus_wrap.cpp @@ -8,20 +8,16 @@ #include <mutex> #include <type_traits> +#include "albit.h" #include "logging.h" -void *dbus_handle{nullptr}; -#define DECL_FUNC(x) decltype(p##x) p##x{}; -DBUS_FUNCTIONS(DECL_FUNC) -#undef DECL_FUNC - void PrepareDBus() { static constexpr char libname[] = "libdbus-1.so.3"; auto load_func = [](auto &f, const char *name) -> void - { f = reinterpret_cast<std::remove_reference_t<decltype(f)>>(GetSymbol(dbus_handle, name)); }; + { f = al::bit_cast<std::remove_reference_t<decltype(f)>>(GetSymbol(dbus_handle, name)); }; #define LOAD_FUNC(x) do { \ load_func(p##x, #x); \ if(!p##x) \ diff --git a/core/dbus_wrap.h b/core/dbus_wrap.h index 09eaacf9..65f08942 100644 --- a/core/dbus_wrap.h +++ b/core/dbus_wrap.h @@ -28,8 +28,8 @@ MAGIC(dbus_message_iter_get_arg_type) \ MAGIC(dbus_message_iter_get_basic) \ MAGIC(dbus_set_error_from_message) -extern void *dbus_handle; -#define DECL_FUNC(x) extern decltype(x) *p##x; +inline void *dbus_handle{}; +#define DECL_FUNC(x) inline decltype(x) *p##x{}; DBUS_FUNCTIONS(DECL_FUNC) #undef DECL_FUNC diff --git a/core/device.h b/core/device.h index 9aaf7adb..b1ffc9ce 100644 --- a/core/device.h +++ b/core/device.h @@ -1,14 +1,13 @@ #ifndef CORE_DEVICE_H #define CORE_DEVICE_H -#include <stddef.h> - #include <array> #include <atomic> #include <bitset> #include <chrono> #include <memory> -#include <mutex> +#include <stddef.h> +#include <stdint.h> #include <string> #include "almalloc.h" @@ -43,20 +42,20 @@ using uint = unsigned int; #define DEFAULT_NUM_UPDATES 3 -enum class DeviceType : unsigned char { +enum class DeviceType : uint8_t { Playback, Capture, Loopback }; -enum class RenderMode : unsigned char { +enum class RenderMode : uint8_t { Normal, Pairwise, Hrtf }; -enum class StereoEncoding : unsigned char { +enum class StereoEncoding : uint8_t { Basic, Uhj, Hrtf, @@ -95,7 +94,7 @@ struct DistanceComp { }; -constexpr uint InvalidChannelIndex{~0u}; +constexpr uint8_t InvalidChannelIndex{static_cast<uint8_t>(~0u)}; struct BFChannelConfig { float Scale; @@ -113,8 +112,8 @@ struct MixParams { * source is expected to be a 3D ACN/N3D ambisonic buffer, and for each * channel [0...count), the given functor is called with the source channel * index, destination channel index, and the gain for that channel. If the - * destination channel is INVALID_CHANNEL_INDEX, the given source channel - * is not used for output. + * destination channel is InvalidChannelIndex, the given source channel is + * not used for output. */ template<typename F> void setAmbiMixParams(const MixParams &inmix, const float gainbase, F func) const @@ -123,14 +122,14 @@ struct MixParams { const size_t numOut{Buffer.size()}; for(size_t i{0};i < numIn;++i) { - auto idx = InvalidChannelIndex; - auto gain = 0.0f; + uint8_t idx{InvalidChannelIndex}; + float gain{0.0f}; for(size_t j{0};j < numOut;++j) { if(AmbiMap[j].Index == inmix.AmbiMap[i].Index) { - idx = static_cast<uint>(j); + idx = static_cast<uint8_t>(j); gain = AmbiMap[j].Scale * gainbase; break; } @@ -142,7 +141,7 @@ struct MixParams { struct RealMixParams { al::span<const InputRemixMap> RemixMap; - std::array<uint,MaxChannels> ChannelIndex{}; + std::array<uint8_t,MaxChannels> ChannelIndex{}; al::span<FloatBufferLine> Buffer; }; @@ -166,6 +165,11 @@ enum { // ear buds, etc). DirectEar, + /* Specifies if output is using speaker virtualization (e.g. Windows + * Spatial Audio). + */ + Virtualization, + DeviceFlagsCount }; @@ -325,9 +329,9 @@ struct DeviceBase { /** * Returns the index for the given channel name (e.g. FrontCenter), or - * INVALID_CHANNEL_INDEX if it doesn't exist. + * InvalidChannelIndex if it doesn't exist. */ - uint channelIdxByName(Channel chan) const noexcept + uint8_t channelIdxByName(Channel chan) const noexcept { return RealOut.ChannelIndex[chan]; } DISABLE_ALLOC() diff --git a/core/effects/base.h b/core/effects/base.h index 4ee19f37..83df7cf0 100644 --- a/core/effects/base.h +++ b/core/effects/base.h @@ -1,9 +1,9 @@ #ifndef CORE_EFFECTS_BASE_H #define CORE_EFFECTS_BASE_H +#include <array> #include <stddef.h> -#include "albyte.h" #include "almalloc.h" #include "alspan.h" #include "atomic.h" @@ -166,6 +166,11 @@ union EffectProps { struct { float Gain; } Dedicated; + + struct { + std::array<float,3> OrientAt; + std::array<float,3> OrientUp; + } Convolution; }; diff --git a/core/filters/nfc.h b/core/filters/nfc.h index 33f67a5f..4b8e68b5 100644 --- a/core/filters/nfc.h +++ b/core/filters/nfc.h @@ -39,7 +39,7 @@ public: * 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 + * average speaker distance, or based on the reference delay if outputting * NFC-HOA. It must not be negative, 0, or infinite. The source distance * should not be too small relative to the control distance. */ diff --git a/core/fmt_traits.h b/core/fmt_traits.h index f797f836..02473014 100644 --- a/core/fmt_traits.h +++ b/core/fmt_traits.h @@ -1,10 +1,9 @@ #ifndef CORE_FMT_TRAITS_H #define CORE_FMT_TRAITS_H -#include <stddef.h> +#include <cstddef> #include <stdint.h> -#include "albyte.h" #include "buffer_storage.h" @@ -22,36 +21,43 @@ struct FmtTypeTraits<FmtUByte> { using Type = uint8_t; template<typename OutT> - static constexpr inline OutT to(const Type val) noexcept - { return val*OutT{1.0/128.0} - OutT{1.0}; } + static constexpr OutT to(const Type val) noexcept { return val*OutT{1.0/128.0} - OutT{1.0}; } }; template<> struct FmtTypeTraits<FmtShort> { using Type = int16_t; template<typename OutT> - static constexpr inline OutT to(const Type val) noexcept { return val*OutT{1.0/32768.0}; } + static constexpr OutT to(const Type val) noexcept { return val*OutT{1.0/32768.0}; } +}; +template<> +struct FmtTypeTraits<FmtInt> { + using Type = int32_t; + + template<typename OutT> + static constexpr OutT to(const Type val) noexcept + { return static_cast<OutT>(val)*OutT{1.0/2147483648.0}; } }; template<> struct FmtTypeTraits<FmtFloat> { using Type = float; template<typename OutT> - static constexpr inline OutT to(const Type val) noexcept { return val; } + static constexpr OutT to(const Type val) noexcept { return val; } }; template<> struct FmtTypeTraits<FmtDouble> { using Type = double; template<typename OutT> - static constexpr inline OutT to(const Type val) noexcept { return static_cast<OutT>(val); } + static constexpr OutT to(const Type val) noexcept { return static_cast<OutT>(val); } }; template<> struct FmtTypeTraits<FmtMulaw> { using Type = uint8_t; template<typename OutT> - static constexpr inline OutT to(const Type val) noexcept + static constexpr OutT to(const Type val) noexcept { return muLawDecompressionTable[val] * OutT{1.0/32768.0}; } }; template<> @@ -59,14 +65,14 @@ struct FmtTypeTraits<FmtAlaw> { using Type = uint8_t; template<typename OutT> - static constexpr inline OutT to(const Type val) noexcept + static constexpr OutT to(const Type val) noexcept { return aLawDecompressionTable[val] * OutT{1.0/32768.0}; } }; template<FmtType SrcType, typename DstT> -inline void LoadSampleArray(DstT *RESTRICT dst, const al::byte *src, const size_t srcstep, - const size_t samples) noexcept +inline void LoadSampleArray(DstT *RESTRICT dst, const std::byte *src, const std::size_t srcstep, + const std::size_t samples) noexcept { using TypeTraits = FmtTypeTraits<SrcType>; using SampleType = typename TypeTraits::Type; diff --git a/core/fpu_ctrl.cpp b/core/fpu_ctrl.cpp index 0cf0d6e7..435855ad 100644 --- a/core/fpu_ctrl.cpp +++ b/core/fpu_ctrl.cpp @@ -8,38 +8,71 @@ #endif #ifdef HAVE_SSE_INTRINSICS #include <emmintrin.h> -#ifndef _MM_DENORMALS_ZERO_MASK +#elif defined(HAVE_SSE) +#include <xmmintrin.h> +#endif + +#if defined(HAVE_SSE) && !defined(_MM_DENORMALS_ZERO_MASK) /* Some headers seem to be missing these? */ #define _MM_DENORMALS_ZERO_MASK 0x0040u #define _MM_DENORMALS_ZERO_ON 0x0040u #endif -#endif #include "cpu_caps.h" +namespace { -void FPUCtl::enter() noexcept +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +[[gnu::target("sse")]] +#endif +[[maybe_unused]] +void disable_denormals(unsigned int *state [[maybe_unused]]) { - if(this->in_mode) return; - #if defined(HAVE_SSE_INTRINSICS) - this->sse_state = _mm_getcsr(); - unsigned int sseState{this->sse_state}; + *state = _mm_getcsr(); + unsigned int sseState{*state}; sseState &= ~(_MM_FLUSH_ZERO_MASK | _MM_DENORMALS_ZERO_MASK); sseState |= _MM_FLUSH_ZERO_ON | _MM_DENORMALS_ZERO_ON; _mm_setcsr(sseState); -#elif defined(__GNUC__) && defined(HAVE_SSE) +#elif defined(HAVE_SSE) - if((CPUCapFlags&CPU_CAP_SSE)) + *state = _mm_getcsr(); + unsigned int sseState{*state}; + sseState &= ~_MM_FLUSH_ZERO_MASK; + sseState |= _MM_FLUSH_ZERO_ON; + if((CPUCapFlags&CPU_CAP_SSE2)) { - __asm__ __volatile__("stmxcsr %0" : "=m" (*&this->sse_state)); - unsigned int sseState{this->sse_state}; - sseState |= 0x8000; /* set flush-to-zero */ - if((CPUCapFlags&CPU_CAP_SSE2)) - sseState |= 0x0040; /* set denormals-are-zero */ - __asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState)); + sseState &= ~_MM_DENORMALS_ZERO_MASK; + sseState |= _MM_DENORMALS_ZERO_ON; } + _mm_setcsr(sseState); +#endif +} + +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +[[gnu::target("sse")]] +#endif +[[maybe_unused]] +void reset_fpu(unsigned int state [[maybe_unused]]) +{ +#if defined(HAVE_SSE_INTRINSICS) || defined(HAVE_SSE) + _mm_setcsr(state); +#endif +} + +} // namespace + + +void FPUCtl::enter() noexcept +{ + if(this->in_mode) return; + +#if defined(HAVE_SSE_INTRINSICS) + disable_denormals(&this->sse_state); +#elif defined(HAVE_SSE) + if((CPUCapFlags&CPU_CAP_SSE)) + disable_denormals(&this->sse_state); #endif this->in_mode = true; @@ -50,12 +83,10 @@ void FPUCtl::leave() noexcept if(!this->in_mode) return; #if defined(HAVE_SSE_INTRINSICS) - _mm_setcsr(this->sse_state); - -#elif defined(__GNUC__) && defined(HAVE_SSE) - + reset_fpu(this->sse_state); +#elif defined(HAVE_SSE) if((CPUCapFlags&CPU_CAP_SSE)) - __asm__ __volatile__("ldmxcsr %0" : : "m" (*&this->sse_state)); + reset_fpu(this->sse_state); #endif this->in_mode = false; } diff --git a/core/helpers.cpp b/core/helpers.cpp index 99cf009c..5a996eee 100644 --- a/core/helpers.cpp +++ b/core/helpers.cpp @@ -3,29 +3,27 @@ #include "helpers.h" +#if defined(_WIN32) +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#endif + #include <algorithm> -#include <cerrno> -#include <cstdarg> #include <cstdlib> -#include <cstdio> #include <cstring> -#include <mutex> #include <limits> +#include <mutex> +#include <optional> #include <string> -#include <tuple> -#include "almalloc.h" -#include "alfstream.h" #include "alnumeric.h" -#include "aloptional.h" #include "alspan.h" #include "alstring.h" #include "logging.h" #include "strutils.h" -#include "vector.h" -/* Mixing thread piority level */ +/* Mixing thread priority level */ int RTPrioLevel{1}; /* Allow reducing the process's RTTime limit for RTKit. */ @@ -34,14 +32,15 @@ bool AllowRTTimeLimit{true}; #ifdef _WIN32 +#include <cctype> #include <shlobj.h> const PathNamePair &GetProcBinary() { - static al::optional<PathNamePair> procbin; + static std::optional<PathNamePair> procbin; if(procbin) return *procbin; - - auto fullpath = al::vector<WCHAR>(256); +#if !defined(ALSOFT_UWP) + auto fullpath = std::vector<WCHAR>(256); DWORD len{GetModuleFileNameW(nullptr, fullpath.data(), static_cast<DWORD>(fullpath.size()))}; while(len == fullpath.size()) { @@ -58,7 +57,16 @@ const PathNamePair &GetProcBinary() fullpath.resize(len); if(fullpath.back() != 0) fullpath.push_back(0); - +#else + auto exePath = __wargv[0]; + if (!exePath) + { + ERR("Failed to get process name: error %lu\n", GetLastError()); + procbin.emplace(); + return *procbin; + } + std::vector<WCHAR> fullpath{exePath, exePath + wcslen(exePath) + 1}; +#endif std::replace(fullpath.begin(), fullpath.end(), '/', '\\'); auto sep = std::find(fullpath.rbegin()+1, fullpath.rend(), '\\'); if(sep != fullpath.rend()) @@ -75,16 +83,16 @@ const PathNamePair &GetProcBinary() namespace { -void DirectorySearch(const char *path, const char *ext, al::vector<std::string> *const results) +void DirectorySearch(const char *path, const char *ext, std::vector<std::string> *const results) { std::string pathstr{path}; pathstr += "\\*"; pathstr += ext; TRACE("Searching %s\n", pathstr.c_str()); - std::wstring wpath{utf8_to_wstr(pathstr.c_str())}; + std::wstring wpath{utf8_to_wstr(pathstr)}; WIN32_FIND_DATAW fdata; - HANDLE hdl{FindFirstFileW(wpath.c_str(), &fdata)}; + HANDLE hdl{FindFirstFileExW(wpath.c_str(), FindExInfoStandard, &fdata, FindExSearchNameMatch, NULL, 0)}; if(hdl == INVALID_HANDLE_VALUE) return; const auto base = results->size(); @@ -97,7 +105,6 @@ void DirectorySearch(const char *path, const char *ext, al::vector<std::string> str += wstr_to_utf8(fdata.cFileName); } while(FindNextFileW(hdl, &fdata)); FindClose(hdl); - const al::span<std::string> newlist{results->data()+base, results->size()-base}; std::sort(newlist.begin(), newlist.end()); for(const auto &name : newlist) @@ -106,16 +113,16 @@ void DirectorySearch(const char *path, const char *ext, al::vector<std::string> } // namespace -al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir) +std::vector<std::string> SearchDataFiles(const char *ext, const char *subdir) { - auto is_slash = [](int c) noexcept -> int { return (c == '\\' || c == '/'); }; + auto is_slash = [](int c) noexcept { return (c == '\\' || c == '/'); }; static std::mutex search_lock; std::lock_guard<std::mutex> _{search_lock}; /* If the path is absolute, use it directly. */ - al::vector<std::string> results; - if(isalpha(subdir[0]) && subdir[1] == ':' && is_slash(subdir[2])) + std::vector<std::string> results; + if(std::isalpha(subdir[0]) && subdir[1] == ':' && is_slash(subdir[2])) { std::string path{subdir}; std::replace(path.begin(), path.end(), '/', '\\'); @@ -149,9 +156,9 @@ al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir) std::replace(path.begin(), path.end(), '/', '\\'); DirectorySearch(path.c_str(), ext, &results); +#if !defined(ALSOFT_UWP) && !defined(_GAMING_XBOX) /* Search the local and global data dirs. */ - static const int ids[2]{ CSIDL_APPDATA, CSIDL_COMMON_APPDATA }; - for(int id : ids) + for(auto id : std::array{CSIDL_APPDATA, CSIDL_COMMON_APPDATA}) { WCHAR buffer[MAX_PATH]; if(SHGetSpecialFolderPathW(nullptr, buffer, id, FALSE) == FALSE) @@ -165,24 +172,27 @@ al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir) DirectorySearch(path.c_str(), ext, &results); } +#endif return results; } void SetRTPriority(void) { +#if !defined(ALSOFT_UWP) if(RTPrioLevel > 0) { if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) ERR("Failed to set priority level for thread\n"); } +#endif } #else -#include <sys/types.h> -#include <unistd.h> +#include <cerrno> #include <dirent.h> +#include <unistd.h> #ifdef __FreeBSD__ #include <sys/sysctl.h> #endif @@ -197,7 +207,6 @@ void SetRTPriority(void) #include <sched.h> #endif #ifdef HAVE_RTKIT -#include <sys/time.h> #include <sys/resource.h> #include "dbus_wrap.h" @@ -209,10 +218,10 @@ void SetRTPriority(void) const PathNamePair &GetProcBinary() { - static al::optional<PathNamePair> procbin; + static std::optional<PathNamePair> procbin; if(procbin) return *procbin; - al::vector<char> pathname; + std::vector<char> pathname; #ifdef __FreeBSD__ size_t pathlen; int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; @@ -247,7 +256,7 @@ const PathNamePair &GetProcBinary() #ifndef __SWITCH__ if(pathname.empty()) { - static const char SelfLinkNames[][32]{ + const char *SelfLinkNames[]{ "/proc/self/exe", "/proc/self/file", "/proc/curproc/exe", @@ -295,7 +304,7 @@ const PathNamePair &GetProcBinary() namespace { -void DirectorySearch(const char *path, const char *ext, al::vector<std::string> *const results) +void DirectorySearch(const char *path, const char *ext, std::vector<std::string> *const results) { TRACE("Searching %s for *%s\n", path, ext); DIR *dir{opendir(path)}; @@ -331,12 +340,12 @@ void DirectorySearch(const char *path, const char *ext, al::vector<std::string> } // namespace -al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir) +std::vector<std::string> SearchDataFiles(const char *ext, const char *subdir) { static std::mutex search_lock; std::lock_guard<std::mutex> _{search_lock}; - al::vector<std::string> results; + std::vector<std::string> results; if(subdir[0] == '/') { DirectorySearch(subdir, ext, &results); @@ -348,7 +357,7 @@ al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir) DirectorySearch(localpath->c_str(), ext, &results); else { - al::vector<char> cwdbuf(256); + std::vector<char> cwdbuf(256); while(!getcwd(cwdbuf.data(), cwdbuf.size())) { if(errno != ERANGE) @@ -425,7 +434,7 @@ al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir) namespace { -bool SetRTPriorityPthread(int prio) +bool SetRTPriorityPthread(int prio [[maybe_unused]]) { int err{ENOTSUP}; #if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__) @@ -445,16 +454,12 @@ bool SetRTPriorityPthread(int prio) #endif err = pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); if(err == 0) return true; - -#else - - std::ignore = prio; #endif WARN("pthread_setschedparam failed: %s (%d)\n", std::strerror(err), err); return false; } -bool SetRTPriorityRTKit(int prio) +bool SetRTPriorityRTKit(int prio [[maybe_unused]]) { #ifdef HAVE_RTKIT if(!HasDBus()) @@ -547,7 +552,6 @@ bool SetRTPriorityRTKit(int prio) #else - std::ignore = prio; WARN("D-Bus not supported\n"); #endif return false; diff --git a/core/helpers.h b/core/helpers.h index f0bfcf1b..df51c116 100644 --- a/core/helpers.h +++ b/core/helpers.h @@ -1,18 +1,26 @@ #ifndef CORE_HELPERS_H #define CORE_HELPERS_H +#include <utility> #include <string> +#include <vector> -#include "vector.h" +struct PathNamePair { + std::string path, fname; -struct PathNamePair { std::string path, fname; }; + PathNamePair() = default; + template<typename T, typename U> + PathNamePair(T&& path_, U&& fname_) + : path{std::forward<T>(path_)}, fname{std::forward<U>(fname_)} + { } +}; const PathNamePair &GetProcBinary(void); extern int RTPrioLevel; extern bool AllowRTTimeLimit; void SetRTPriority(void); -al::vector<std::string> SearchDataFiles(const char *match, const char *subdir); +std::vector<std::string> SearchDataFiles(const char *match, const char *subdir); #endif /* CORE_HELPERS_H */ diff --git a/core/hrtf.cpp b/core/hrtf.cpp index d5c7573a..9a13a004 100644 --- a/core/hrtf.cpp +++ b/core/hrtf.cpp @@ -8,6 +8,7 @@ #include <cassert> #include <cctype> #include <cmath> +#include <cstddef> #include <cstdint> #include <cstdio> #include <cstring> @@ -16,16 +17,16 @@ #include <memory> #include <mutex> #include <numeric> +#include <optional> #include <type_traits> #include <utility> +#include <vector> #include "albit.h" -#include "albyte.h" #include "alfstream.h" #include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" -#include "aloptional.h" #include "alspan.h" #include "ambidefs.h" #include "filters/splitter.h" @@ -34,7 +35,6 @@ #include "mixer/hrtfdefs.h" #include "opthelpers.h" #include "polyphase_resampler.h" -#include "vector.h" namespace { @@ -98,10 +98,10 @@ constexpr char magicMarker03[8]{'M','i','n','P','H','R','0','3'}; constexpr auto PassthruCoeff = static_cast<float>(1.0/al::numbers::sqrt2); std::mutex LoadedHrtfLock; -al::vector<LoadedHrtf> LoadedHrtfs; +std::vector<LoadedHrtf> LoadedHrtfs; std::mutex EnumeratedHrtfLock; -al::vector<HrtfEntry> EnumeratedHrtfs; +std::vector<HrtfEntry> EnumeratedHrtfs; class databuf final : public std::streambuf { @@ -289,13 +289,13 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool mChannels[0].mSplitter.init(static_cast<float>(xover_norm)); for(size_t i{0};i < mChannels.size();++i) { - const size_t order{AmbiIndex::OrderFromChannel()[i]}; + const size_t order{AmbiIndex::OrderFromChannel[i]}; mChannels[i].mSplitter = mChannels[0].mSplitter; mChannels[i].mHfScale = AmbiOrderHFGain[order]; } uint min_delay{HrtfHistoryLength*HrirDelayFracOne}, max_delay{0}; - al::vector<ImpulseResponse> impres; impres.reserve(AmbiPoints.size()); + std::vector<ImpulseResponse> impres; impres.reserve(AmbiPoints.size()); auto calc_res = [Hrtf,&max_delay,&min_delay](const AngularPoint &pt) -> ImpulseResponse { auto &field = Hrtf->mFields[0]; @@ -331,7 +331,7 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool TRACE("Min delay: %.2f, max delay: %.2f, FIR length: %u\n", min_delay/double{HrirDelayFracOne}, max_delay/double{HrirDelayFracOne}, irSize); - auto tmpres = al::vector<std::array<double2,HrirLength>>(mChannels.size()); + auto tmpres = std::vector<std::array<double2,HrirLength>>(mChannels.size()); max_delay = 0; for(size_t c{0u};c < AmbiPoints.size();++c) { @@ -393,7 +393,7 @@ std::unique_ptr<HrtfStore> CreateHrtfStore(uint rate, uint8_t irSize, { Hrtf.reset(al::construct_at(static_cast<HrtfStore*>(ptr))); InitRef(Hrtf->mRef, 1u); - Hrtf->mSampleRate = rate; + Hrtf->mSampleRate = rate & 0xff'ff'ff; Hrtf->mIrSize = irSize; /* Set up pointers to storage following the main HRTF struct. */ @@ -425,7 +425,7 @@ std::unique_ptr<HrtfStore> CreateHrtfStore(uint rate, uint8_t irSize, std::uninitialized_copy_n(delays, irCount, delays_); /* Finally, assign the storage pointers. */ - Hrtf->mFields = al::as_span(field_, fields.size()); + Hrtf->mFields = {field_, fields.size()}; Hrtf->mElev = elev_; Hrtf->mCoeffs = coeffs_; Hrtf->mDelays = delays_; @@ -492,10 +492,10 @@ T> readle(std::istream &data) static_assert(num_bits <= sizeof(T)*8, "num_bits is too large for the type"); T ret{}; - al::byte b[sizeof(T)]{}; + std::byte b[sizeof(T)]{}; if(!data.read(reinterpret_cast<char*>(b), num_bits/8)) return static_cast<T>(EOF); - std::reverse_copy(std::begin(b), std::end(b), reinterpret_cast<al::byte*>(&ret)); + std::reverse_copy(std::begin(b), std::end(b), reinterpret_cast<std::byte*>(&ret)); return fixsign<num_bits>(ret); } @@ -529,7 +529,7 @@ std::unique_ptr<HrtfStore> LoadHrtf00(std::istream &data, const char *filename) return nullptr; } - auto elevs = al::vector<HrtfStore::Elevation>(evCount); + auto elevs = std::vector<HrtfStore::Elevation>(evCount); for(auto &elev : elevs) elev.irOffset = readle<uint16_t>(data); if(!data || data.eof()) @@ -571,8 +571,8 @@ std::unique_ptr<HrtfStore> LoadHrtf00(std::istream &data, const char *filename) return nullptr; } - auto coeffs = al::vector<HrirArray>(irCount, HrirArray{}); - auto delays = al::vector<ubyte2>(irCount); + auto coeffs = std::vector<HrirArray>(irCount, HrirArray{}); + auto delays = std::vector<ubyte2>(irCount); for(auto &hrir : coeffs) { for(auto &val : al::span<float2>{hrir.data(), irSize}) @@ -626,7 +626,7 @@ std::unique_ptr<HrtfStore> LoadHrtf01(std::istream &data, const char *filename) return nullptr; } - auto elevs = al::vector<HrtfStore::Elevation>(evCount); + auto elevs = std::vector<HrtfStore::Elevation>(evCount); for(auto &elev : elevs) elev.azCount = readle<uint8_t>(data); if(!data || data.eof()) @@ -649,8 +649,8 @@ std::unique_ptr<HrtfStore> LoadHrtf01(std::istream &data, const char *filename) elevs[i].irOffset = static_cast<ushort>(elevs[i-1].irOffset + elevs[i-1].azCount); const ushort irCount{static_cast<ushort>(elevs.back().irOffset + elevs.back().azCount)}; - auto coeffs = al::vector<HrirArray>(irCount, HrirArray{}); - auto delays = al::vector<ubyte2>(irCount); + auto coeffs = std::vector<HrirArray>(irCount, HrirArray{}); + auto delays = std::vector<ubyte2>(irCount); for(auto &hrir : coeffs) { for(auto &val : al::span<float2>{hrir.data(), irSize}) @@ -722,8 +722,8 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename) return nullptr; } - auto fields = al::vector<HrtfStore::Field>(fdCount); - auto elevs = al::vector<HrtfStore::Elevation>{}; + auto fields = std::vector<HrtfStore::Field>(fdCount); + auto elevs = std::vector<HrtfStore::Elevation>{}; for(size_t f{0};f < fdCount;f++) { const ushort distance{readle<uint16_t>(data)}; @@ -787,8 +787,8 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename) }); const auto irTotal = static_cast<ushort>(elevs.back().azCount + elevs.back().irOffset); - auto coeffs = al::vector<HrirArray>(irTotal, HrirArray{}); - auto delays = al::vector<ubyte2>(irTotal); + auto coeffs = std::vector<HrirArray>(irTotal, HrirArray{}); + auto delays = std::vector<ubyte2>(irTotal); if(channelType == ChanType_LeftOnly) { if(sampleType == SampleType_S16) @@ -881,10 +881,10 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename) if(fdCount > 1) { - auto fields_ = al::vector<HrtfStore::Field>(fields.size()); - auto elevs_ = al::vector<HrtfStore::Elevation>(elevs.size()); - auto coeffs_ = al::vector<HrirArray>(coeffs.size()); - auto delays_ = al::vector<ubyte2>(delays.size()); + auto fields_ = std::vector<HrtfStore::Field>(fields.size()); + auto elevs_ = std::vector<HrtfStore::Elevation>(elevs.size()); + auto coeffs_ = std::vector<HrirArray>(coeffs.size()); + auto delays_ = std::vector<ubyte2>(delays.size()); /* Simple reverse for the per-field elements. */ std::reverse_copy(fields.cbegin(), fields.cend(), fields_.begin()); @@ -983,8 +983,8 @@ std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data, const char *filename) return nullptr; } - auto fields = al::vector<HrtfStore::Field>(fdCount); - auto elevs = al::vector<HrtfStore::Elevation>{}; + auto fields = std::vector<HrtfStore::Field>(fdCount); + auto elevs = std::vector<HrtfStore::Elevation>{}; for(size_t f{0};f < fdCount;f++) { const ushort distance{readle<uint16_t>(data)}; @@ -1048,8 +1048,8 @@ std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data, const char *filename) }); const auto irTotal = static_cast<ushort>(elevs.back().azCount + elevs.back().irOffset); - auto coeffs = al::vector<HrirArray>(irTotal, HrirArray{}); - auto delays = al::vector<ubyte2>(irTotal); + auto coeffs = std::vector<HrirArray>(irTotal, HrirArray{}); + auto delays = std::vector<ubyte2>(irTotal); if(channelType == ChanType_LeftOnly) { for(auto &hrir : coeffs) @@ -1221,7 +1221,7 @@ al::span<const char> GetResource(int name) } // namespace -al::vector<std::string> EnumerateHrtf(al::optional<std::string> pathopt) +std::vector<std::string> EnumerateHrtf(std::optional<std::string> pathopt) { std::lock_guard<std::mutex> _{EnumeratedHrtfLock}; EnumeratedHrtfs.clear(); @@ -1270,7 +1270,7 @@ al::vector<std::string> EnumerateHrtf(al::optional<std::string> pathopt) AddBuiltInEntry("Built-In HRTF", IDR_DEFAULT_HRTF_MHR); } - al::vector<std::string> list; + std::vector<std::string> list; list.reserve(EnumeratedHrtfs.size()); for(auto &entry : EnumeratedHrtfs) list.emplace_back(entry.mDispName); @@ -1368,7 +1368,7 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate) TRACE("Resampling HRTF %s (%uhz -> %uhz)\n", name.c_str(), hrtf->mSampleRate, devrate); /* Calculate the last elevation's index and get the total IR count. */ - const size_t lastEv{std::accumulate(hrtf->mFields.begin(), hrtf->mFields.end(), size_t{0}, + const size_t lastEv{std::accumulate(hrtf->mFields.begin(), hrtf->mFields.end(), 0_uz, [](const size_t curval, const HrtfStore::Field &field) noexcept -> size_t { return curval + field.evCount; } ) - 1}; @@ -1394,7 +1394,7 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate) /* Scale the delays for the new sample rate. */ float max_delay{0.0f}; - auto new_delays = al::vector<float2>(irCount); + auto new_delays = std::vector<float2>(irCount); const float rate_scale{static_cast<float>(devrate)/static_cast<float>(hrtf->mSampleRate)}; for(size_t i{0};i < irCount;++i) { @@ -1430,7 +1430,7 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate) */ const float newIrSize{std::round(static_cast<float>(hrtf->mIrSize) * rate_scale)}; hrtf->mIrSize = static_cast<uint8_t>(minf(HrirLength, newIrSize)); - hrtf->mSampleRate = devrate; + hrtf->mSampleRate = devrate & 0xff'ff'ff; } TRACE("Loaded HRTF %s for sample rate %uhz, %u-sample filter\n", name.c_str(), diff --git a/core/hrtf.h b/core/hrtf.h index eb18682a..5e6e09a8 100644 --- a/core/hrtf.h +++ b/core/hrtf.h @@ -4,17 +4,17 @@ #include <array> #include <cstddef> #include <memory> +#include <optional> #include <string> +#include <vector> #include "almalloc.h" -#include "aloptional.h" #include "alspan.h" #include "atomic.h" #include "ambidefs.h" #include "bufferline.h" #include "mixer/hrtfdefs.h" #include "intrusive_ptr.h" -#include "vector.h" struct HrtfStore { @@ -83,7 +83,7 @@ struct DirectHrtfState { }; -al::vector<std::string> EnumerateHrtf(al::optional<std::string> pathopt); +std::vector<std::string> EnumerateHrtf(std::optional<std::string> pathopt); HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate); #endif /* CORE_HRTF_H */ diff --git a/core/logging.cpp b/core/logging.cpp index 34a95e5a..56ad0a0d 100644 --- a/core/logging.cpp +++ b/core/logging.cpp @@ -3,13 +3,17 @@ #include "logging.h" +#include <cctype> #include <cstdarg> #include <cstdio> +#include <cstring> +#include <mutex> +#include <optional> #include <string> +#include <vector> #include "alspan.h" #include "strutils.h" -#include "vector.h" #if defined(_WIN32) @@ -19,22 +23,74 @@ #include <android/log.h> #endif -void al_print(LogLevel level, FILE *logfile, const char *fmt, ...) + +FILE *gLogFile{stderr}; +#ifdef _DEBUG +LogLevel gLogLevel{LogLevel::Warning}; +#else +LogLevel gLogLevel{LogLevel::Error}; +#endif + + +namespace { + +enum class LogState : uint8_t { + FirstRun, + Ready, + Disable +}; + +std::mutex LogCallbackMutex; +LogState gLogState{LogState::FirstRun}; + +LogCallbackFunc gLogCallback{}; +void *gLogCallbackPtr{}; + +constexpr std::optional<char> GetLevelCode(LogLevel level) +{ + switch(level) + { + case LogLevel::Disable: break; + case LogLevel::Error: return 'E'; + case LogLevel::Warning: return 'W'; + case LogLevel::Trace: return 'I'; + } + return std::nullopt; +} + +} // namespace + +void al_set_log_callback(LogCallbackFunc callback, void *userptr) +{ + auto cblock = std::lock_guard{LogCallbackMutex}; + gLogCallback = callback; + gLogCallbackPtr = callback ? userptr : nullptr; + if(gLogState == LogState::FirstRun) + { + auto extlogopt = al::getenv("ALSOFT_DISABLE_LOG_CALLBACK"); + if(!extlogopt || *extlogopt != "1") + gLogState = LogState::Ready; + else + gLogState = LogState::Disable; + } +} + +void al_print(LogLevel level, const char *fmt, ...) { /* Kind of ugly since string literals are const char arrays with a size * that includes the null terminator, which we want to exclude from the * span. */ - auto prefix = al::as_span("[ALSOFT] (--) ").first<14>(); + auto prefix = al::span{"[ALSOFT] (--) "}.first<14>(); switch(level) { case LogLevel::Disable: break; - case LogLevel::Error: prefix = al::as_span("[ALSOFT] (EE) ").first<14>(); break; - case LogLevel::Warning: prefix = al::as_span("[ALSOFT] (WW) ").first<14>(); break; - case LogLevel::Trace: prefix = al::as_span("[ALSOFT] (II) ").first<14>(); break; + case LogLevel::Error: prefix = al::span{"[ALSOFT] (EE) "}.first<14>(); break; + case LogLevel::Warning: prefix = al::span{"[ALSOFT] (WW) "}.first<14>(); break; + case LogLevel::Trace: prefix = al::span{"[ALSOFT] (II) "}.first<14>(); break; } - al::vector<char> dynmsg; + std::vector<char> dynmsg; std::array<char,256> stcmsg{}; char *str{stcmsg.data()}; @@ -45,21 +101,28 @@ void al_print(LogLevel level, FILE *logfile, const char *fmt, ...) va_start(args, fmt); va_copy(args2, args); const int msglen{std::vsnprintf(msg.data(), msg.size(), fmt, args)}; - if(msglen >= 0 && static_cast<size_t>(msglen) >= msg.size()) UNLIKELY + if(msglen >= 0) { - dynmsg.resize(static_cast<size_t>(msglen)+prefix.size() + 1u); + if(static_cast<size_t>(msglen) >= msg.size()) UNLIKELY + { + dynmsg.resize(static_cast<size_t>(msglen)+prefix.size() + 1u); - str = dynmsg.data(); - auto prefend2 = std::copy_n(prefix.begin(), prefix.size(), dynmsg.begin()); - msg = {prefend2, dynmsg.end()}; + str = dynmsg.data(); + auto prefend2 = std::copy_n(prefix.begin(), prefix.size(), dynmsg.begin()); + msg = {prefend2, dynmsg.end()}; - std::vsnprintf(msg.data(), msg.size(), fmt, args2); + std::vsnprintf(msg.data(), msg.size(), fmt, args2); + } + msg = msg.first(static_cast<size_t>(msglen)); } + else + msg = {msg.data(), std::strlen(msg.data())}; va_end(args2); va_end(args); if(gLogLevel >= level) { + auto logfile = gLogFile; fputs(str, logfile); fflush(logfile); } @@ -86,4 +149,21 @@ void al_print(LogLevel level, FILE *logfile, const char *fmt, ...) }; __android_log_print(android_severity(level), "openal", "%s", str); #endif + + auto cblock = std::lock_guard{LogCallbackMutex}; + if(gLogState != LogState::Disable) + { + while(!msg.empty() && std::isspace(msg.back())) + { + msg.back() = '\0'; + msg = msg.first(msg.size()-1); + } + if(auto logcode = GetLevelCode(level); logcode && !msg.empty()) + { + if(gLogCallback) + gLogCallback(gLogCallbackPtr, *logcode, msg.data(), static_cast<int>(msg.size())); + else if(gLogState == LogState::FirstRun) + gLogState = LogState::Disable; + } + } } diff --git a/core/logging.h b/core/logging.h index f4b6ab56..06b7cdde 100644 --- a/core/logging.h +++ b/core/logging.h @@ -16,36 +16,23 @@ extern LogLevel gLogLevel; extern FILE *gLogFile; -#ifdef __USE_MINGW_ANSI_STDIO -[[gnu::format(gnu_printf,3,4)]] -#else -[[gnu::format(printf,3,4)]] -#endif -void al_print(LogLevel level, FILE *logfile, const char *fmt, ...); -#if (!defined(_WIN32) || defined(NDEBUG)) && !defined(__ANDROID__) -#define TRACE(...) do { \ - if(gLogLevel >= LogLevel::Trace) UNLIKELY \ - al_print(LogLevel::Trace, gLogFile, __VA_ARGS__); \ -} while(0) +using LogCallbackFunc = void(*)(void *userptr, char level, const char *message, int length) noexcept; -#define WARN(...) do { \ - if(gLogLevel >= LogLevel::Warning) UNLIKELY \ - al_print(LogLevel::Warning, gLogFile, __VA_ARGS__); \ -} while(0) +void al_set_log_callback(LogCallbackFunc callback, void *userptr); -#define ERR(...) do { \ - if(gLogLevel >= LogLevel::Error) UNLIKELY \ - al_print(LogLevel::Error, gLogFile, __VA_ARGS__); \ -} while(0) +#ifdef __USE_MINGW_ANSI_STDIO +[[gnu::format(gnu_printf,2,3)]] #else +[[gnu::format(printf,2,3)]] +#endif +void al_print(LogLevel level, const char *fmt, ...); -#define TRACE(...) al_print(LogLevel::Trace, gLogFile, __VA_ARGS__) +#define TRACE(...) al_print(LogLevel::Trace, __VA_ARGS__) -#define WARN(...) al_print(LogLevel::Warning, gLogFile, __VA_ARGS__) +#define WARN(...) al_print(LogLevel::Warning, __VA_ARGS__) -#define ERR(...) al_print(LogLevel::Error, gLogFile, __VA_ARGS__) -#endif +#define ERR(...) al_print(LogLevel::Error, __VA_ARGS__) #endif /* CORE_LOGGING_H */ diff --git a/core/mastering.cpp b/core/mastering.cpp index 97a4008e..4445719b 100644 --- a/core/mastering.cpp +++ b/core/mastering.cpp @@ -382,10 +382,10 @@ std::unique_ptr<Compressor> Compressor::Create(const size_t NumChans, const floa Compressor::~Compressor() { if(mHold) - al::destroy_at(mHold); + std::destroy_at(mHold); mHold = nullptr; if(mDelay) - al::destroy_n(mDelay, mNumChans); + std::destroy_n(mDelay, mNumChans); mDelay = nullptr; } diff --git a/core/mixer.cpp b/core/mixer.cpp index 066c57bd..806ac8b8 100644 --- a/core/mixer.cpp +++ b/core/mixer.cpp @@ -82,14 +82,13 @@ std::array<float,MaxAmbiChannels> CalcAmbiCoeffs(const float y, const float z, c return coeffs; } -void ComputePanGains(const MixParams *mix, const float*RESTRICT coeffs, const float ingain, - const al::span<float,MaxAmbiChannels> gains) +void ComputePanGains(const MixParams *mix, const al::span<const float,MaxAmbiChannels> coeffs, + const float ingain, const al::span<float,MaxAmbiChannels> gains) { auto ambimap = mix->AmbiMap.cbegin(); auto iter = std::transform(ambimap, ambimap+mix->Buffer.size(), gains.begin(), [coeffs,ingain](const BFChannelConfig &chanmap) noexcept -> float - { return chanmap.Scale * coeffs[chanmap.Index] * ingain; } - ); + { return chanmap.Scale * coeffs[chanmap.Index] * ingain; }); std::fill(iter, gains.end(), 0.0f); } diff --git a/core/mixer.h b/core/mixer.h index aa7597bb..9062ebac 100644 --- a/core/mixer.h +++ b/core/mixer.h @@ -58,7 +58,7 @@ std::array<float,MaxAmbiChannels> CalcAmbiCoeffs(const float y, const float z, c * vector must be normalized (unit length), and the spread is the angular width * of the sound (0...tau). */ -inline std::array<float,MaxAmbiChannels> CalcDirectionCoeffs(const float (&dir)[3], +inline std::array<float,MaxAmbiChannels> CalcDirectionCoeffs(const al::span<const float,3> dir, const float spread) { /* Convert from OpenAL coords to Ambisonics. */ @@ -71,7 +71,7 @@ inline std::array<float,MaxAmbiChannels> CalcDirectionCoeffs(const float (&dir)[ * Calculates ambisonic coefficients based on an OpenAL direction vector. The * vector must be normalized (unit length). */ -constexpr std::array<float,MaxAmbiChannels> CalcDirectionCoeffs(const float (&dir)[3]) +constexpr std::array<float,MaxAmbiChannels> CalcDirectionCoeffs(const al::span<const float,3> dir) { /* Convert from OpenAL coords to Ambisonics. */ return CalcAmbiCoeffs(-dir[0], dir[1], -dir[2]); @@ -103,7 +103,7 @@ inline std::array<float,MaxAmbiChannels> CalcAngleCoeffs(const float azimuth, * coeffs are a 'slice' of a transform matrix for the input channel, used to * scale and orient the sound samples. */ -void ComputePanGains(const MixParams *mix, const float*RESTRICT coeffs, const float ingain, - const al::span<float,MaxAmbiChannels> gains); +void ComputePanGains(const MixParams *mix, const al::span<const float,MaxAmbiChannels> coeffs, + const float ingain, const al::span<float,MaxAmbiChannels> gains); #endif /* CORE_MIXER_H */ diff --git a/core/mixer/mixer_neon.cpp b/core/mixer/mixer_neon.cpp index ef2936b3..ead775af 100644 --- a/core/mixer/mixer_neon.cpp +++ b/core/mixer/mixer_neon.cpp @@ -342,7 +342,7 @@ void Mix_<NEONTag>(const al::span<const float> InSamples, const al::span<FloatBu { const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f}; const auto min_len = minz(Counter, InSamples.size()); - const auto aligned_len = minz((min_len+3) & ~size_t{3}, InSamples.size()) - min_len; + const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len; for(FloatBufferLine &output : OutBuffer) MixLine(InSamples, al::assume_aligned<16>(output.data()+OutPos), *CurrentGains++, @@ -355,7 +355,7 @@ void Mix_<NEONTag>(const al::span<const float> InSamples, float *OutBuffer, floa { const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f}; const auto min_len = minz(Counter, InSamples.size()); - const auto aligned_len = minz((min_len+3) & ~size_t{3}, InSamples.size()) - min_len; + const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len; MixLine(InSamples, al::assume_aligned<16>(OutBuffer), CurrentGain, TargetGain, delta, min_len, aligned_len, Counter); diff --git a/core/mixer/mixer_sse.cpp b/core/mixer/mixer_sse.cpp index 0aa5d5fb..70f77c14 100644 --- a/core/mixer/mixer_sse.cpp +++ b/core/mixer/mixer_sse.cpp @@ -307,7 +307,7 @@ void Mix_<SSETag>(const al::span<const float> InSamples, const al::span<FloatBuf { const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f}; const auto min_len = minz(Counter, InSamples.size()); - const auto aligned_len = minz((min_len+3) & ~size_t{3}, InSamples.size()) - min_len; + const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len; for(FloatBufferLine &output : OutBuffer) MixLine(InSamples, al::assume_aligned<16>(output.data()+OutPos), *CurrentGains++, @@ -320,7 +320,7 @@ void Mix_<SSETag>(const al::span<const float> InSamples, float *OutBuffer, float { const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f}; const auto min_len = minz(Counter, InSamples.size()); - const auto aligned_len = minz((min_len+3) & ~size_t{3}, InSamples.size()) - min_len; + const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len; MixLine(InSamples, al::assume_aligned<16>(OutBuffer), CurrentGain, TargetGain, delta, min_len, aligned_len, Counter); diff --git a/core/uhjfilter.cpp b/core/uhjfilter.cpp index df50956a..28999e09 100644 --- a/core/uhjfilter.cpp +++ b/core/uhjfilter.cpp @@ -9,7 +9,9 @@ #include "alcomplex.h" #include "alnumeric.h" #include "opthelpers.h" +#include "pffft.h" #include "phase_shifter.h" +#include "vector.h" UhjQualityType UhjDecodeQuality{UhjQualityType::Default}; @@ -18,38 +20,141 @@ UhjQualityType UhjEncodeQuality{UhjQualityType::Default}; namespace { -const PhaseShifterT<UhjLength256> PShiftLq{}; -const PhaseShifterT<UhjLength512> PShiftHq{}; +struct PFFFTSetupDeleter { + void operator()(PFFFT_Setup *ptr) { pffft_destroy_setup(ptr); } +}; +using PFFFTSetupPtr = std::unique_ptr<PFFFT_Setup,PFFFTSetupDeleter>; +/* Convolution is implemented using a segmented overlap-add method. The filter + * response is broken up into multiple segments of 128 samples, and each + * segment has an FFT applied with a 256-sample buffer (the latter half left + * silent) to get its frequency-domain response. + * + * Input samples are similarly broken up into 128-sample segments, with a 256- + * sample FFT applied to each new incoming segment to get its frequency-domain + * response. A history of FFT'd input segments is maintained, equal to the + * number of filter response segments. + * + * To apply the convolution, each filter response segment is convolved with its + * paired input segment (using complex multiplies, far cheaper than time-domain + * FIRs), accumulating into an FFT buffer. The input history is then shifted to + * align with later filter response segments for the next input segment. + * + * An inverse FFT is then applied to the accumulated FFT buffer to get a 256- + * sample time-domain response for output, which is split in two halves. The + * first half is the 128-sample output, and the second half is a 128-sample + * (really, 127) delayed extension, which gets added to the output next time. + * Convolving two time-domain responses of length N results in a time-domain + * signal of length N*2 - 1, and this holds true regardless of the convolution + * being applied in the frequency domain, so these "overflow" samples need to + * be accounted for. + */ template<size_t N> -struct GetPhaseShifter; -template<> -struct GetPhaseShifter<UhjLength256> { static auto& Get() noexcept { return PShiftLq; } }; -template<> -struct GetPhaseShifter<UhjLength512> { static auto& Get() noexcept { return PShiftHq; } }; +struct SegmentedFilter { + static constexpr size_t sFftLength{256}; + static constexpr size_t sSampleLength{sFftLength / 2}; + static constexpr size_t sNumSegments{N/sSampleLength}; + static_assert(N >= sFftLength); + static_assert((N % sSampleLength) == 0); + PFFFTSetupPtr mFft; + alignas(16) std::array<float,sFftLength*sNumSegments> mFilterData; + + SegmentedFilter() + { + mFft = PFFFTSetupPtr{pffft_new_setup(sFftLength, PFFFT_REAL)}; + + using complex_d = std::complex<double>; + constexpr size_t fft_size{N}; + constexpr size_t half_size{fft_size / 2}; + + /* To set up the filter, we need to generate the desired response. + * Start with a pure delay that passes all frequencies through. + */ + auto fftBuffer = std::make_unique<complex_d[]>(fft_size); + std::fill_n(fftBuffer.get(), fft_size, complex_d{}); + fftBuffer[half_size] = 1.0; + + /* Convert to the frequency domain, shift the phase of each bin by +90 + * degrees, then convert back to the time domain. + * + * NOTE: The 0- and half-frequency are always real for a real signal. + * To maintain that and their phase (0 or pi), they're heavily + * attenuated instead of shifted like the others. + */ + forward_fft(al::span{fftBuffer.get(), fft_size}); + fftBuffer[0] *= std::numeric_limits<double>::epsilon(); + for(size_t i{1};i < half_size;++i) + fftBuffer[i] = complex_d{-fftBuffer[i].imag(), fftBuffer[i].real()}; + fftBuffer[half_size] *= std::numeric_limits<double>::epsilon(); + for(size_t i{half_size+1};i < fft_size;++i) + fftBuffer[i] = std::conj(fftBuffer[fft_size - i]); + inverse_fft(al::span{fftBuffer.get(), fft_size}); + + /* The segments of the filter are converted back to the frequency + * domain, each on their own (0 stuffed). + */ + auto fftBuffer2 = std::make_unique<complex_d[]>(sFftLength); + auto fftTmp = al::vector<float,16>(sFftLength); + float *filter{mFilterData.data()}; + for(size_t s{0};s < sNumSegments;++s) + { + for(size_t i{0};i < sSampleLength;++i) + fftBuffer2[i] = fftBuffer[sSampleLength*s + i].real() / double{fft_size}; + std::fill_n(fftBuffer2.get()+sSampleLength, sSampleLength, complex_d{}); + forward_fft(al::span{fftBuffer2.get(), sFftLength}); + + /* Convert to zdomain data for PFFFT, scaled by the FFT length so + * the iFFT result will be normalized. + */ + for(size_t i{0};i < sSampleLength;++i) + { + fftTmp[i*2 + 0] = static_cast<float>(fftBuffer2[i].real()) / float{sFftLength}; + fftTmp[i*2 + 1] = static_cast<float>((i == 0) ? fftBuffer2[sSampleLength].real() + : fftBuffer2[i].imag()) / float{sFftLength}; + } + pffft_zreorder(mFft.get(), fftTmp.data(), filter, PFFFT_BACKWARD); + filter += sFftLength; + } + } +}; + +template<size_t N> +const SegmentedFilter<N> gSegmentedFilter; + +template<size_t N> +const PhaseShifterT<N> PShifter; -constexpr float square(float x) noexcept -{ return x*x; } /* Filter coefficients for the 'base' all-pass IIR, which applies a frequency- * dependent phase-shift of N degrees. The output of the filter requires a 1- * sample delay. */ constexpr std::array<float,4> Filter1Coeff{{ - square(0.6923878f), square(0.9360654322959f), square(0.9882295226860f), - square(0.9987488452737f) + 0.479400865589f, 0.876218493539f, 0.976597589508f, 0.997499255936f }}; /* Filter coefficients for the offset all-pass IIR, which applies a frequency- * dependent phase-shift of N+90 degrees. */ constexpr std::array<float,4> Filter2Coeff{{ - square(0.4021921162426f), square(0.8561710882420f), square(0.9722909545651f), - square(0.9952884791278f) + 0.161758498368f, 0.733028932341f, 0.945349700329f, 0.990599156684f }}; } // namespace +void UhjAllPassFilter::processOne(const al::span<const float, 4> coeffs, float x) +{ + auto state = mState; + for(size_t i{0};i < 4;++i) + { + const float y{x*coeffs[i] + state[i].z[0]}; + state[i].z[0] = state[i].z[1]; + state[i].z[1] = y*coeffs[i] - x; + x = y; + } + mState = state; +} + void UhjAllPassFilter::process(const al::span<const float,4> coeffs, const al::span<const float> src, const bool updateState, float *RESTRICT dst) { @@ -92,7 +197,10 @@ template<size_t N> void UhjEncoder<N>::encode(float *LeftOut, float *RightOut, const al::span<const float*const,3> InSamples, const size_t SamplesToDo) { - const auto &PShift = GetPhaseShifter<N>::Get(); + static constexpr auto &Filter = gSegmentedFilter<N>; + static_assert(sFftLength == Filter.sFftLength); + static_assert(sSegmentSize == Filter.sSampleLength); + static_assert(sNumSegments == Filter.sNumSegments); ASSUME(SamplesToDo > 0); @@ -109,10 +217,71 @@ void UhjEncoder<N>::encode(float *LeftOut, float *RightOut, mS[i] = 0.9396926f*mW[i] + 0.1855740f*mX[i]; /* Precompute j(-0.3420201*W + 0.5098604*X) and store in mD. */ - std::transform(winput, winput+SamplesToDo, xinput, mWX.begin() + sWXInOffset, - [](const float w, const float x) noexcept -> float - { return -0.3420201f*w + 0.5098604f*x; }); - PShift.process({mD.data(), SamplesToDo}, mWX.data()); + size_t curseg{mCurrentSegment}; + for(size_t base{0};base < SamplesToDo;) + { + const size_t todo{minz(sSegmentSize-mFifoPos, SamplesToDo-base)}; + + /* Copy out the samples that were previously processed by the FFT. */ + std::copy_n(mWXInOut.begin()+mFifoPos, todo, mD.begin()+base); + + /* Transform the non-delayed input and store in the front half of the + * filter input. + */ + std::transform(winput+base, winput+base+todo, xinput+base, mWXInOut.begin()+mFifoPos, + [](const float w, const float x) noexcept -> float + { return -0.3420201f*w + 0.5098604f*x; }); + + mFifoPos += todo; + base += todo; + + /* Check whether the input buffer is filled with new samples. */ + if(mFifoPos < sSegmentSize) break; + mFifoPos = 0; + + /* Copy the new input to the next history segment, clearing the back + * half of the segment, and convert to the frequency domain. + */ + float *input{mWXHistory.data() + curseg*sFftLength}; + std::copy_n(mWXInOut.begin(), sSegmentSize, input); + std::fill_n(input+sSegmentSize, sSegmentSize, 0.0f); + + pffft_transform(Filter.mFft.get(), input, input, mWorkData.data(), PFFFT_FORWARD); + + /* Convolve each input segment with its IR filter counterpart (aligned + * in time, from newest to oldest). + */ + mFftBuffer.fill(0.0f); + const float *filter{Filter.mFilterData.data()}; + for(size_t s{curseg};s < sNumSegments;++s) + { + pffft_zconvolve_accumulate(Filter.mFft.get(), input, filter, mFftBuffer.data()); + input += sFftLength; + filter += sFftLength; + } + input = mWXHistory.data(); + for(size_t s{0};s < curseg;++s) + { + pffft_zconvolve_accumulate(Filter.mFft.get(), input, filter, mFftBuffer.data()); + input += sFftLength; + filter += sFftLength; + } + + /* Convert back to samples, writing to the output and storing the extra + * for next time. + */ + pffft_transform(Filter.mFft.get(), mFftBuffer.data(), mFftBuffer.data(), + mWorkData.data(), PFFFT_BACKWARD); + + for(size_t i{0};i < sSegmentSize;++i) + mWXInOut[i] = mFftBuffer[i] + mWXInOut[sSegmentSize+i]; + for(size_t i{0};i < sSegmentSize;++i) + mWXInOut[sSegmentSize+i] = mFftBuffer[sSegmentSize+i]; + + /* Shift the input history. */ + curseg = curseg ? (curseg-1) : (sNumSegments-1); + } + mCurrentSegment = curseg; /* D = j(-0.3420201*W + 0.5098604*X) + 0.6554516*Y */ for(size_t i{0};i < SamplesToDo;++i) @@ -122,7 +291,6 @@ void UhjEncoder<N>::encode(float *LeftOut, float *RightOut, std::copy(mW.cbegin()+SamplesToDo, mW.cbegin()+SamplesToDo+sFilterDelay, mW.begin()); std::copy(mX.cbegin()+SamplesToDo, mX.cbegin()+SamplesToDo+sFilterDelay, mX.begin()); std::copy(mY.cbegin()+SamplesToDo, mY.cbegin()+SamplesToDo+sFilterDelay, mY.begin()); - std::copy(mWX.cbegin()+SamplesToDo, mWX.cbegin()+SamplesToDo+sWXInOffset, mWX.begin()); /* Apply a delay to the existing output to align with the input delay. */ auto *delayBuffer = mDirectDelay.data(); @@ -133,7 +301,7 @@ void UhjEncoder<N>::encode(float *LeftOut, float *RightOut, float *inout{al::assume_aligned<16>(buffer)}; auto inout_end = inout + SamplesToDo; - if(SamplesToDo >= sFilterDelay) LIKELY + if(SamplesToDo >= sFilterDelay) { auto delay_end = std::rotate(inout, inout_end - sFilterDelay, inout_end); std::swap_ranges(inout, delay_end, distbuf); @@ -240,7 +408,7 @@ void UhjDecoder<N>::decode(const al::span<float*> samples, const size_t samplesT { static_assert(sInputPadding <= sMaxPadding, "Filter padding is too large"); - const auto &PShift = GetPhaseShifter<N>::Get(); + constexpr auto &PShift = PShifter<N>; ASSUME(samplesToDo > 0); @@ -313,11 +481,11 @@ void UhjDecoderIIR::decode(const al::span<float*> samples, const size_t samplesT const float *RESTRICT right{al::assume_aligned<16>(samples[1])}; /* S = Left + Right */ - for(size_t i{0};i < samplesToDo;++i) + for(size_t i{0};i < samplesToDo+sInputPadding;++i) mS[i] = left[i] + right[i]; /* D = Left - Right */ - for(size_t i{0};i < samplesToDo;++i) + for(size_t i{0};i < samplesToDo+sInputPadding;++i) mD[i] = left[i] - right[i]; } @@ -326,14 +494,13 @@ void UhjDecoderIIR::decode(const al::span<float*> samples, const size_t samplesT float *RESTRICT youtput{al::assume_aligned<16>(samples[2])}; /* Precompute j(0.828331*D + 0.767820*T) and store in xoutput. */ - std::transform(mD.cbegin(), mD.cbegin()+samplesToDo, youtput, mTemp.begin(), + std::transform(mD.cbegin(), mD.cbegin()+sInputPadding+samplesToDo, youtput, mTemp.begin(), [](const float d, const float t) noexcept { return 0.828331f*d + 0.767820f*t; }); - mFilter2DT.process(Filter2Coeff, {mTemp.data(), samplesToDo}, updateState, xoutput); + if(mFirstRun) mFilter2DT.processOne(Filter2Coeff, mTemp[0]); + mFilter2DT.process(Filter2Coeff, {mTemp.data()+1, samplesToDo}, updateState, xoutput); /* Apply filter1 to S and store in mTemp. */ - mTemp[0] = mDelayS; - mFilter1S.process(Filter1Coeff, {mS.data(), samplesToDo}, updateState, mTemp.data()+1); - if(updateState) LIKELY mDelayS = mTemp[samplesToDo]; + mFilter1S.process(Filter1Coeff, {mS.data(), samplesToDo}, updateState, mTemp.data()); /* W = 0.981532*S + 0.197484*j(0.828331*D + 0.767820*T) */ for(size_t i{0};i < samplesToDo;++i) @@ -346,12 +513,11 @@ void UhjDecoderIIR::decode(const al::span<float*> samples, const size_t samplesT /* Apply filter1 to (0.795968*D - 0.676392*T) and store in mTemp. */ std::transform(mD.cbegin(), mD.cbegin()+samplesToDo, youtput, youtput, [](const float d, const float t) noexcept { return 0.795968f*d - 0.676392f*t; }); - mTemp[0] = mDelayDT; - mFilter1DT.process(Filter1Coeff, {youtput, samplesToDo}, updateState, mTemp.data()+1); - if(updateState) LIKELY mDelayDT = mTemp[samplesToDo]; + mFilter1DT.process(Filter1Coeff, {youtput, samplesToDo}, updateState, mTemp.data()); /* Precompute j*S and store in youtput. */ - mFilter2S.process(Filter2Coeff, {mS.data(), samplesToDo}, updateState, youtput); + if(mFirstRun) mFilter2S.processOne(Filter2Coeff, mS[0]); + mFilter2S.process(Filter2Coeff, {mS.data()+1, samplesToDo}, updateState, youtput); /* Y = 0.795968*D - 0.676392*T + j(0.186633*S) */ for(size_t i{0};i < samplesToDo;++i) @@ -363,14 +529,14 @@ void UhjDecoderIIR::decode(const al::span<float*> samples, const size_t samplesT float *RESTRICT zoutput{al::assume_aligned<16>(samples[3])}; /* Apply filter1 to Q and store in mTemp. */ - mTemp[0] = mDelayQ; - mFilter1Q.process(Filter1Coeff, {zoutput, samplesToDo}, updateState, mTemp.data()+1); - if(updateState) LIKELY mDelayQ = mTemp[samplesToDo]; + mFilter1Q.process(Filter1Coeff, {zoutput, samplesToDo}, updateState, mTemp.data()); /* Z = 1.023332*Q */ for(size_t i{0};i < samplesToDo;++i) zoutput[i] = 1.023332f*mTemp[i]; } + + mFirstRun = false; } @@ -392,7 +558,7 @@ void UhjStereoDecoder<N>::decode(const al::span<float*> samples, const size_t sa { static_assert(sInputPadding <= sMaxPadding, "Filter padding is too large"); - const auto &PShift = GetPhaseShifter<N>::Get(); + constexpr auto &PShift = PShifter<N>; ASSUME(samplesToDo > 0); @@ -470,7 +636,7 @@ void UhjStereoDecoderIIR::decode(const al::span<float*> samples, const size_t sa const float *RESTRICT left{al::assume_aligned<16>(samples[0])}; const float *RESTRICT right{al::assume_aligned<16>(samples[1])}; - for(size_t i{0};i < samplesToDo;++i) + for(size_t i{0};i < samplesToDo+sInputPadding;++i) mS[i] = left[i] + right[i]; /* Pre-apply the width factor to the difference signal D. Smoothly @@ -480,7 +646,7 @@ void UhjStereoDecoderIIR::decode(const al::span<float*> samples, const size_t sa const float wcurrent{(mCurrentWidth < 0.0f) ? wtarget : mCurrentWidth}; if(wtarget == wcurrent || !updateState) { - for(size_t i{0};i < samplesToDo;++i) + for(size_t i{0};i < samplesToDo+sInputPadding;++i) mD[i] = (left[i] - right[i]) * wcurrent; mCurrentWidth = wcurrent; } @@ -493,6 +659,8 @@ void UhjStereoDecoderIIR::decode(const al::span<float*> samples, const size_t sa mD[i] = (left[i] - right[i]) * (wcurrent + wstep*fi); fi += 1.0f; } + for(size_t i{samplesToDo};i < samplesToDo+sInputPadding;++i) + mD[i] = (left[i] - right[i]) * wtarget; mCurrentWidth = wtarget; } } @@ -502,12 +670,11 @@ void UhjStereoDecoderIIR::decode(const al::span<float*> samples, const size_t sa float *RESTRICT youtput{al::assume_aligned<16>(samples[2])}; /* Apply filter1 to S and store in mTemp. */ - mTemp[0] = mDelayS; - mFilter1S.process(Filter1Coeff, {mS.data(), samplesToDo}, updateState, mTemp.data()+1); - if(updateState) LIKELY mDelayS = mTemp[samplesToDo]; + mFilter1S.process(Filter1Coeff, {mS.data(), samplesToDo}, updateState, mTemp.data()); /* Precompute j*D and store in xoutput. */ - mFilter2D.process(Filter2Coeff, {mD.data(), samplesToDo}, updateState, xoutput); + if(mFirstRun) mFilter2D.processOne(Filter2Coeff, mD[0]); + mFilter2D.process(Filter2Coeff, {mD.data()+1, samplesToDo}, updateState, xoutput); /* W = 0.6098637*S - 0.6896511*j*w*D */ for(size_t i{0};i < samplesToDo;++i) @@ -517,16 +684,17 @@ void UhjStereoDecoderIIR::decode(const al::span<float*> samples, const size_t sa xoutput[i] = 0.8624776f*mTemp[i] + 0.7626955f*xoutput[i]; /* Precompute j*S and store in youtput. */ - mFilter2S.process(Filter2Coeff, {mS.data(), samplesToDo}, updateState, youtput); + if(mFirstRun) mFilter2S.processOne(Filter2Coeff, mS[0]); + mFilter2S.process(Filter2Coeff, {mS.data()+1, samplesToDo}, updateState, youtput); /* Apply filter1 to D and store in mTemp. */ - mTemp[0] = mDelayD; - mFilter1D.process(Filter1Coeff, {mD.data(), samplesToDo}, updateState, mTemp.data()+1); - if(updateState) LIKELY mDelayD = mTemp[samplesToDo]; + mFilter1D.process(Filter1Coeff, {mD.data(), samplesToDo}, updateState, mTemp.data()); /* Y = 1.6822415*w*D - 0.2156194*j*S */ for(size_t i{0};i < samplesToDo;++i) youtput[i] = 1.6822415f*mTemp[i] - 0.2156194f*youtput[i]; + + mFirstRun = false; } diff --git a/core/uhjfilter.h b/core/uhjfilter.h index df308094..348dc7e1 100644 --- a/core/uhjfilter.h +++ b/core/uhjfilter.h @@ -29,6 +29,7 @@ struct UhjAllPassFilter { }; std::array<AllPassState,4> mState; + void processOne(const al::span<const float,4> coeffs, float x); void process(const al::span<const float,4> coeffs, const al::span<const float> src, const bool update, float *RESTRICT dst); }; @@ -50,7 +51,10 @@ struct UhjEncoderBase { template<size_t N> struct UhjEncoder final : public UhjEncoderBase { - static constexpr size_t sFilterDelay{N/2}; + static constexpr size_t sFftLength{256}; + static constexpr size_t sSegmentSize{sFftLength/2}; + static constexpr size_t sNumSegments{N/sSegmentSize}; + static constexpr size_t sFilterDelay{N/2 + sSegmentSize}; /* Delays and processing storage for the input signal. */ alignas(16) std::array<float,BufferLineSize+sFilterDelay> mW{}; @@ -60,11 +64,12 @@ struct UhjEncoder final : public UhjEncoderBase { alignas(16) std::array<float,BufferLineSize> mS{}; alignas(16) std::array<float,BufferLineSize> mD{}; - /* History and temp storage for the FIR filter. New samples should be - * written to index sFilterDelay*2 - 1. - */ - static constexpr size_t sWXInOffset{sFilterDelay*2 - 1}; - alignas(16) std::array<float,BufferLineSize + sFilterDelay*2> mWX{}; + /* History and temp storage for the convolution filter. */ + size_t mFifoPos{}, mCurrentSegment{}; + alignas(16) std::array<float,sFftLength> mWXInOut{}; + alignas(16) std::array<float,sFftLength> mFftBuffer{}; + alignas(16) std::array<float,sFftLength> mWorkData{}; + alignas(16) std::array<float,sFftLength*sNumSegments> mWXHistory{}; alignas(16) std::array<std::array<float,sFilterDelay>,2> mDirectDelay{}; @@ -77,8 +82,6 @@ struct UhjEncoder final : public UhjEncoderBase { */ void encode(float *LeftOut, float *RightOut, const al::span<const float*const,3> InSamples, const size_t SamplesToDo) override; - - DEF_NEWDEL(UhjEncoder) }; struct UhjEncoderIIR final : public UhjEncoderBase { @@ -160,17 +163,18 @@ struct UhjDecoder final : public DecoderBase { }; struct UhjDecoderIIR final : public DecoderBase { - /* FIXME: These IIR decoder filters actually have a 1-sample delay on the - * non-filtered components, which is not reflected in the source latency - * value. sInputPadding is 0, however, because it doesn't need any extra - * input samples. + /* These IIR decoder filters normally have a 1-sample delay on the non- + * filtered components. However, the filtered components are made to skip + * the first output sample and take one future sample, which puts it ahead + * by one sample. The first filtered output sample is cut to align it with + * the first non-filtered sample, similar to the FIR filters. */ - static constexpr size_t sInputPadding{0}; + static constexpr size_t sInputPadding{1}; - alignas(16) std::array<float,BufferLineSize> mS{}; - alignas(16) std::array<float,BufferLineSize> mD{}; - alignas(16) std::array<float,BufferLineSize+1> mTemp{}; - float mDelayS{}, mDelayDT{}, mDelayQ{}; + bool mFirstRun{true}; + alignas(16) std::array<float,BufferLineSize+sInputPadding> mS{}; + alignas(16) std::array<float,BufferLineSize+sInputPadding> mD{}; + alignas(16) std::array<float,BufferLineSize+sInputPadding> mTemp{}; UhjAllPassFilter mFilter1S; UhjAllPassFilter mFilter2DT; @@ -211,14 +215,14 @@ struct UhjStereoDecoder final : public DecoderBase { }; struct UhjStereoDecoderIIR final : public DecoderBase { - static constexpr size_t sInputPadding{0}; + static constexpr size_t sInputPadding{1}; + bool mFirstRun{true}; float mCurrentWidth{-1.0f}; - alignas(16) std::array<float,BufferLineSize> mS{}; - alignas(16) std::array<float,BufferLineSize> mD{}; - alignas(16) std::array<float,BufferLineSize+1> mTemp{}; - float mDelayS{}, mDelayD{}; + alignas(16) std::array<float,BufferLineSize+sInputPadding> mS{}; + alignas(16) std::array<float,BufferLineSize+sInputPadding> mD{}; + alignas(16) std::array<float,BufferLineSize> mTemp{}; UhjAllPassFilter mFilter1S; UhjAllPassFilter mFilter2D; diff --git a/core/uiddefs.cpp b/core/uiddefs.cpp index 244c01a5..9471bba5 100644 --- a/core/uiddefs.cpp +++ b/core/uiddefs.cpp @@ -19,12 +19,8 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80,0x DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf,0x08, 0x00,0xa0,0xc9,0x25,0xcd,0x16); DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e); -DEFINE_GUID(IID_IMMDeviceEnumerator, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6); -DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2); -DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2); -DEFINE_GUID(IID_IAudioCaptureClient, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4,0xde, 0x18,0x5c,0x39,0x5c,0xd3,0x17); -#ifdef HAVE_WASAPI +#if defined(HAVE_WASAPI) && !defined(ALSOFT_UWP) #include <wtypes.h> #include <devpropdef.h> #include <propkeydef.h> diff --git a/core/voice.cpp b/core/voice.cpp index e8fbcccd..3889c42d 100644 --- a/core/voice.cpp +++ b/core/voice.cpp @@ -12,13 +12,12 @@ #include <iterator> #include <memory> #include <new> +#include <optional> #include <stdlib.h> #include <utility> #include <vector> -#include "albyte.h" #include "alnumeric.h" -#include "aloptional.h" #include "alspan.h" #include "alstring.h" #include "ambidefs.h" @@ -129,7 +128,7 @@ inline HrtfMixerBlendFunc SelectHrtfBlendMixer() } // namespace -void Voice::InitMixer(al::optional<std::string> resampler) +void Voice::InitMixer(std::optional<std::string> resampler) { if(resampler) { @@ -227,10 +226,9 @@ void SendSourceStoppedEvent(ContextBase *context, uint id) auto evt_vec = ring->getWriteVector(); if(evt_vec.first.len < 1) return; - AsyncEvent *evt{al::construct_at(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf), - AsyncEvent::SourceStateChange)}; - evt->u.srcstate.id = id; - evt->u.srcstate.state = AsyncEvent::SrcState::Stop; + auto &evt = InitAsyncEvent<AsyncSourceStateEvent>(evt_vec.first.buf); + evt.mId = id; + evt.mState = AsyncSrcState::Stop; ring->writeAdvance(1); } @@ -264,7 +262,7 @@ const float *DoFilters(BiquadFilter &lpfilter, BiquadFilter &hpfilter, float *ds template<FmtType Type> -inline void LoadSamples(float *RESTRICT dstSamples, const al::byte *src, const size_t srcChan, +inline void LoadSamples(float *RESTRICT dstSamples, const std::byte *src, const size_t srcChan, const size_t srcOffset, const size_t srcStep, const size_t /*samplesPerBlock*/, const size_t samplesToLoad) noexcept { @@ -275,7 +273,7 @@ inline void LoadSamples(float *RESTRICT dstSamples, const al::byte *src, const s } template<> -inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const al::byte *src, +inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const std::byte *src, const size_t srcChan, const size_t srcOffset, const size_t srcStep, const size_t samplesPerBlock, const size_t samplesToLoad) noexcept { @@ -289,14 +287,15 @@ inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const al::byte *src /* NOTE: This could probably be optimized better. */ size_t wrote{0}; do { + static constexpr int MaxStepIndex{static_cast<int>(std::size(IMAStep_size)) - 1}; /* Each IMA4 block starts with a signed 16-bit sample, and a signed * 16-bit table index. The table index needs to be clamped. */ - int sample{src[srcChan*4] | (src[srcChan*4 + 1] << 8)}; - int index{src[srcChan*4 + 2] | (src[srcChan*4 + 3] << 8)}; + int sample{int(src[srcChan*4]) | (int(src[srcChan*4 + 1]) << 8)}; + int index{int(src[srcChan*4 + 2]) | (int(src[srcChan*4 + 3]) << 8)}; sample = (sample^0x8000) - 32768; - index = clampi((index^0x8000) - 32768, 0, al::size(IMAStep_size)-1); + index = clampi((index^0x8000) - 32768, 0, MaxStepIndex); if(skip == 0) { @@ -312,7 +311,7 @@ inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const al::byte *src sample = clampi(sample, -32768, 32767); index += IMA4Index_adjust[nibble]; - index = clampi(index, 0, al::size(IMAStep_size)-1); + index = clampi(index, 0, MaxStepIndex); return sample; }; @@ -325,17 +324,17 @@ inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const al::byte *src * always be less than the block size). They need to be decoded despite * being ignored for proper state on the remaining samples. */ - const al::byte *nibbleData{src + (srcStep+srcChan)*4}; + const std::byte *nibbleData{src + (srcStep+srcChan)*4}; size_t nibbleOffset{0}; const size_t startOffset{skip + 1}; for(;skip;--skip) { const size_t byteShift{(nibbleOffset&1) * 4}; - const size_t wordOffset{(nibbleOffset>>1) & ~size_t{3}}; + const size_t wordOffset{(nibbleOffset>>1) & ~3_uz}; const size_t byteOffset{wordOffset*srcStep + ((nibbleOffset>>1)&3u)}; ++nibbleOffset; - std::ignore = decode_sample((nibbleData[byteOffset]>>byteShift) & 15u); + std::ignore = decode_sample(uint(nibbleData[byteOffset]>>byteShift) & 15u); } /* Second, decode the rest of the block and write to the output, until @@ -345,11 +344,11 @@ inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const al::byte *src for(size_t i{0};i < todo;++i) { const size_t byteShift{(nibbleOffset&1) * 4}; - const size_t wordOffset{(nibbleOffset>>1) & ~size_t{3}}; + const size_t wordOffset{(nibbleOffset>>1) & ~3_uz}; const size_t byteOffset{wordOffset*srcStep + ((nibbleOffset>>1)&3u)}; ++nibbleOffset; - const int result{decode_sample((nibbleData[byteOffset]>>byteShift) & 15u)}; + const int result{decode_sample(uint(nibbleData[byteOffset]>>byteShift) & 15u)}; dstSamples[wrote++] = static_cast<float>(result) / 32768.0f; } if(wrote == samplesToLoad) @@ -360,7 +359,7 @@ inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const al::byte *src } template<> -inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const al::byte *src, +inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const std::byte *src, const size_t srcChan, const size_t srcOffset, const size_t srcStep, const size_t samplesPerBlock, const size_t samplesToLoad) noexcept { @@ -377,19 +376,19 @@ inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const al::byte * * nibble sample value. This is followed by the two initial 16-bit * sample history values. */ - const al::byte *input{src}; - const uint8_t blockpred{std::min(input[srcChan], uint8_t{6})}; + const std::byte *input{src}; + const uint8_t blockpred{std::min(uint8_t(input[srcChan]), uint8_t{6})}; input += srcStep; - int delta{input[2*srcChan + 0] | (input[2*srcChan + 1] << 8)}; + int delta{int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1]) << 8)}; input += srcStep*2; int sampleHistory[2]{}; - sampleHistory[0] = input[2*srcChan + 0] | (input[2*srcChan + 1]<<8); + sampleHistory[0] = int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1])<<8); input += srcStep*2; - sampleHistory[1] = input[2*srcChan + 0] | (input[2*srcChan + 1]<<8); + sampleHistory[1] = int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1])<<8); input += srcStep*2; - const auto coeffs = al::as_span(MSADPCMAdaptionCoeff[blockpred]); + const al::span coeffs{MSADPCMAdaptionCoeff[blockpred]}; delta = (delta^0x8000) - 32768; sampleHistory[0] = (sampleHistory[0]^0x8000) - 32768; sampleHistory[1] = (sampleHistory[1]^0x8000) - 32768; @@ -439,7 +438,7 @@ inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const al::byte * const size_t byteShift{((nibbleOffset&1)^1) * 4}; nibbleOffset += srcStep; - std::ignore = decode_sample((input[byteOffset]>>byteShift) & 15); + std::ignore = decode_sample(int(input[byteOffset]>>byteShift) & 15); } /* Now decode the rest of the block, until the end of the block or the @@ -452,7 +451,7 @@ inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const al::byte * const size_t byteShift{((nibbleOffset&1)^1) * 4}; nibbleOffset += srcStep; - const int sample{decode_sample((input[byteOffset]>>byteShift) & 15)}; + const int sample{decode_sample(int(input[byteOffset]>>byteShift) & 15)}; dstSamples[wrote++] = static_cast<float>(sample) / 32768.0f; } if(wrote == samplesToLoad) @@ -462,7 +461,7 @@ inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const al::byte * } while(true); } -void LoadSamples(float *dstSamples, const al::byte *src, const size_t srcChan, +void LoadSamples(float *dstSamples, const std::byte *src, const size_t srcChan, const size_t srcOffset, const FmtType srcType, const size_t srcStep, const size_t samplesPerBlock, const size_t samplesToLoad) noexcept { @@ -475,6 +474,7 @@ void LoadSamples(float *dstSamples, const al::byte *src, const size_t srcChan, { HANDLE_FMT(FmtUByte); HANDLE_FMT(FmtShort); + HANDLE_FMT(FmtInt); HANDLE_FMT(FmtFloat); HANDLE_FMT(FmtDouble); HANDLE_FMT(FmtMulaw); @@ -798,7 +798,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi using ResBufType = decltype(DeviceBase::mResampleData); static constexpr uint srcSizeMax{static_cast<uint>(ResBufType{}.size()-MaxResamplerEdge)}; - const auto prevSamples = al::as_span(mPrevSamples[chan]); + const al::span prevSamples{mPrevSamples[chan]}; const auto resampleBuffer = std::copy(prevSamples.cbegin(), prevSamples.cend(), Device->mResampleData.begin()) - MaxResamplerEdge; int intPos{DataPosInt}; @@ -1101,7 +1101,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi { const size_t byteOffset{blocksDone*mBytesPerBlock}; const size_t byteEnd{mNumCallbackBlocks*mBytesPerBlock}; - al::byte *data{BufferListItem->mSamples}; + std::byte *data{BufferListItem->mSamples}; std::copy(data+byteOffset, data+byteEnd, data); mNumCallbackBlocks -= blocksDone; mCallbackBlockBase += blocksDone; @@ -1145,16 +1145,15 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi /* Send any events now, after the position/buffer info was updated. */ const auto enabledevt = Context->mEnabledEvts.load(std::memory_order_acquire); - if(buffers_done > 0 && enabledevt.test(AsyncEvent::BufferCompleted)) + if(buffers_done > 0 && enabledevt.test(al::to_underlying(AsyncEnableBits::BufferCompleted))) { RingBuffer *ring{Context->mAsyncEvents.get()}; auto evt_vec = ring->getWriteVector(); if(evt_vec.first.len > 0) { - AsyncEvent *evt{al::construct_at(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf), - AsyncEvent::BufferCompleted)}; - evt->u.bufcomp.id = SourceID; - evt->u.bufcomp.count = buffers_done; + auto &evt = InitAsyncEvent<AsyncBufferCompleteEvent>(evt_vec.first.buf); + evt.mId = SourceID; + evt.mCount = buffers_done; ring->writeAdvance(1); } } @@ -1165,7 +1164,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi * ensures any residual noise fades to 0 amplitude. */ mPlayState.store(Stopping, std::memory_order_release); - if(enabledevt.test(AsyncEvent::SourceStateChange)) + if(enabledevt.test(al::to_underlying(AsyncEnableBits::SourceState))) SendSourceStoppedEvent(Context, SourceID); } } @@ -1275,7 +1274,7 @@ void Voice::prepare(DeviceBase *device) else if(mAmbiOrder && device->mAmbiOrder > mAmbiOrder) { const uint8_t *OrderFromChan{Is2DAmbisonic(mFmtChannels) ? - AmbiIndex::OrderFrom2DChannel().data() : AmbiIndex::OrderFromChannel().data()}; + AmbiIndex::OrderFrom2DChannel.data() : AmbiIndex::OrderFromChannel.data()}; const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder, device->m2DMixing); diff --git a/core/voice.h b/core/voice.h index 57ee7b01..a599eda8 100644 --- a/core/voice.h +++ b/core/voice.h @@ -5,13 +5,12 @@ #include <atomic> #include <bitset> #include <chrono> +#include <cstddef> #include <memory> -#include <stddef.h> +#include <optional> #include <string> -#include "albyte.h" #include "almalloc.h" -#include "aloptional.h" #include "alspan.h" #include "bufferline.h" #include "buffer_storage.h" @@ -100,7 +99,7 @@ struct VoiceBufferItem { uint mLoopStart{0u}; uint mLoopEnd{0u}; - al::byte *mSamples{nullptr}; + std::byte *mSamples{nullptr}; }; @@ -270,7 +269,7 @@ struct Voice { void prepare(DeviceBase *device); - static void InitMixer(al::optional<std::string> resampler); + static void InitMixer(std::optional<std::string> resampler); DEF_NEWDEL(Voice) }; diff --git a/docs/ambisonics.txt b/docs/ambisonics.txt index b1b111d6..7798c8f9 100644 --- a/docs/ambisonics.txt +++ b/docs/ambisonics.txt @@ -12,7 +12,7 @@ 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 +Taking advantage of the way sound waves propagate, 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 @@ -63,7 +63,7 @@ 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 +occurring 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. diff --git a/docs/env-vars.txt b/docs/env-vars.txt index 815a3098..0c15cbe9 100644 --- a/docs/env-vars.txt +++ b/docs/env-vars.txt @@ -78,6 +78,15 @@ Same as for __ALSOFT_REVERSE_Z, but for Y (up/down) panning. __ALSOFT_REVERSE_X Same as for __ALSOFT_REVERSE_Z, but for X (left/right) panning. +__ALSOFT_DEFAULT_ERROR +Applications that erroneously call alGetError prior to setting a context as +current may not like that OpenAL Soft returns 0xA004 (AL_INVALID_OPERATION), +indicating that the call could not be executed as there's no context to get the +error value from. This can be set to 0 (AL_NO_ERROR) to let such apps pass the +check despite the problem. Other applications, however, may see AL_NO_ERROR +returned and assume any previous AL calls succeeded when they actually failed, +so this should only be set when necessary. + __ALSOFT_SUSPEND_CONTEXT Due to the OpenAL spec not being very clear about them, behavior of the alcSuspendContext and alcProcessContext methods has varied, and because of diff --git a/examples/alconvolve.c b/examples/alconvolve.c index 93fd2eb4..8580d443 100644 --- a/examples/alconvolve.c +++ b/examples/alconvolve.c @@ -22,7 +22,7 @@ * THE SOFTWARE. */ -/* This file contains an example for applying convolution reverb to a source. */ +/* This file contains an example for applying convolution to a source. */ #include <assert.h> #include <inttypes.h> @@ -39,9 +39,9 @@ #include "common/alhelpers.h" -#ifndef AL_SOFT_convolution_reverb -#define AL_SOFT_convolution_reverb -#define AL_EFFECT_CONVOLUTION_REVERB_SOFT 0xA000 +#ifndef AL_SOFT_convolution_effect +#define AL_SOFT_convolution_effect +#define AL_EFFECT_CONVOLUTION_SOFT 0xA000 #endif @@ -278,21 +278,21 @@ static int UpdatePlayer(StreamPlayer *player) } -/* CreateEffect creates a new OpenAL effect object with a convolution reverb - * type, and returns the new effect ID. +/* CreateEffect creates a new OpenAL effect object with a convolution type, and + * returns the new effect ID. */ static ALuint CreateEffect(void) { ALuint effect = 0; ALenum err; - printf("Using Convolution Reverb\n"); + printf("Using Convolution\n"); - /* Create the effect object and set the convolution reverb effect type. */ + /* Create the effect object and set the convolution effect type. */ alGenEffects(1, &effect); - alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_CONVOLUTION_REVERB_SOFT); + alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_CONVOLUTION_SOFT); - /* Check if an error occured, and clean up if so. */ + /* Check if an error occurred, and clean up if so. */ err = alGetError(); if(err != AL_NO_ERROR) { @@ -391,7 +391,7 @@ static ALuint LoadSound(const char *filename) free(membuf); sf_close(sndfile); - /* Check if an error occured, and clean up if so. */ + /* Check if an error occurred, and clean up if so. */ err = alGetError(); if(err != AL_NO_ERROR) { @@ -423,10 +423,10 @@ int main(int argc, char **argv) if(InitAL(&argv, &argc) != 0) return 1; - if(!alIsExtensionPresent("AL_SOFTX_convolution_reverb")) + if(!alIsExtensionPresent("AL_SOFTX_convolution_effect")) { CloseAL(); - fprintf(stderr, "Error: Convolution revern not supported\n"); + fprintf(stderr, "Error: Convolution effect not supported\n"); return 1; } @@ -500,11 +500,11 @@ int main(int argc, char **argv) alGenAuxiliaryEffectSlots(1, &slot); /* Set the impulse response sound buffer on the effect slot. This allows - * effects to access it as needed. In this case, convolution reverb uses it - * as the filter source. NOTE: Unlike the effect object, the buffer *is* - * kept referenced and may not be changed or deleted as long as it's set, - * just like with a source. When another buffer is set, or the effect slot - * is deleted, the buffer reference is released. + * effects to access it as needed. In this case, convolution uses it as the + * filter source. NOTE: Unlike the effect object, the buffer *is* kept + * referenced and may not be changed or deleted as long as it's set, just + * like with a source. When another buffer is set, or the effect slot is + * deleted, the buffer reference is released. * * The effect slot's gain is reduced because the impulse responses I've * tested with result in excessively loud reverb. Is that normal? Even with diff --git a/examples/alffplay.cpp b/examples/alffplay.cpp index ae40a51a..1f02ef70 100644 --- a/examples/alffplay.cpp +++ b/examples/alffplay.cpp @@ -339,14 +339,14 @@ struct AudioState { } static void AL_APIENTRY eventCallbackC(ALenum eventType, ALuint object, ALuint param, - ALsizei length, const ALchar *message, void *userParam) + ALsizei length, const ALchar *message, void *userParam) noexcept { static_cast<AudioState*>(userParam)->eventCallback(eventType, object, param, length, message); } void eventCallback(ALenum eventType, ALuint object, ALuint param, ALsizei length, - const ALchar *message); + const ALchar *message) noexcept; - static ALsizei AL_APIENTRY bufferCallbackC(void *userptr, void *data, ALsizei size) + static ALsizei AL_APIENTRY bufferCallbackC(void *userptr, void *data, ALsizei size) noexcept { return static_cast<AudioState*>(userptr)->bufferCallback(data, size); } - ALsizei bufferCallback(void *data, ALsizei size); + ALsizei bufferCallback(void *data, ALsizei size) noexcept; nanoseconds getClockNoLock(); nanoseconds getClock() @@ -840,7 +840,7 @@ bool AudioState::readAudio(int sample_skip) void AL_APIENTRY AudioState::eventCallback(ALenum eventType, ALuint object, ALuint param, - ALsizei length, const ALchar *message) + ALsizei length, const ALchar *message) noexcept { if(eventType == AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT) { @@ -878,7 +878,7 @@ void AL_APIENTRY AudioState::eventCallback(ALenum eventType, ALuint object, ALui } } -ALsizei AudioState::bufferCallback(void *data, ALsizei size) +ALsizei AudioState::bufferCallback(void *data, ALsizei size) noexcept { ALsizei got{0}; diff --git a/examples/alhrtf.c b/examples/alhrtf.c index d878870e..7ea1b99e 100644 --- a/examples/alhrtf.c +++ b/examples/alhrtf.c @@ -121,7 +121,7 @@ static ALuint LoadSound(const char *filename) free(membuf); sf_close(sndfile); - /* Check if an error occured, and clean up if so. */ + /* Check if an error occurred, and clean up if so. */ err = alGetError(); if(err != AL_NO_ERROR) { diff --git a/examples/allatency.c b/examples/allatency.c index ab4a4ebc..01f4eb69 100644 --- a/examples/allatency.c +++ b/examples/allatency.c @@ -124,7 +124,7 @@ static ALuint LoadSound(const char *filename) free(membuf); sf_close(sndfile); - /* Check if an error occured, and clean up if so. */ + /* Check if an error occurred, and clean up if so. */ err = alGetError(); if(err != AL_NO_ERROR) { diff --git a/examples/alloopback.c b/examples/alloopback.c index 56cd420f..964a0cdb 100644 --- a/examples/alloopback.c +++ b/examples/alloopback.c @@ -118,7 +118,7 @@ static ALuint CreateSineWave(void) alGenBuffers(1, &buffer); alBufferData(buffer, AL_FORMAT_MONO16, data, sizeof(data), 44100); - /* Check if an error occured, and clean up if so. */ + /* Check if an error occurred, and clean up if so. */ err = alGetError(); if(err != AL_NO_ERROR) { diff --git a/examples/almultireverb.c b/examples/almultireverb.c index a77cc59e..dcb76c87 100644 --- a/examples/almultireverb.c +++ b/examples/almultireverb.c @@ -137,7 +137,7 @@ static int LoadEffect(ALuint effect, const EFXEAXREVERBPROPERTIES *reverb) alEffectf(effect, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, reverb->flRoomRolloffFactor); alEffecti(effect, AL_EAXREVERB_DECAY_HFLIMIT, reverb->iDecayHFLimit); - /* Check if an error occured, and return failure if so. */ + /* Check if an error occurred, and return failure if so. */ if((err=alGetError()) != AL_NO_ERROR) { fprintf(stderr, "Error setting up reverb: %s\n", alGetString(err)); @@ -210,7 +210,7 @@ static ALuint LoadSound(const char *filename) free(membuf); sf_close(sndfile); - /* Check if an error occured, and clean up if so. */ + /* Check if an error occurred, and clean up if so. */ err = alGetError(); if(err != AL_NO_ERROR) { @@ -493,7 +493,7 @@ int main(int argc, char **argv) } if(argc < 1) { - fprintf(stderr, "No filename spacified.\n"); + fprintf(stderr, "No filename specified.\n"); CloseAL(); return 1; } diff --git a/examples/alplay.c b/examples/alplay.c index 4291cb47..1eabcccd 100644 --- a/examples/alplay.c +++ b/examples/alplay.c @@ -266,7 +266,7 @@ static ALuint LoadSound(const char *filename) free(membuf); sf_close(sndfile); - /* Check if an error occured, and clean up if so. */ + /* Check if an error occurred, and clean up if so. */ err = alGetError(); if(err != AL_NO_ERROR) { diff --git a/examples/alreverb.c b/examples/alreverb.c index 11a3ac6b..ff49db25 100644 --- a/examples/alreverb.c +++ b/examples/alreverb.c @@ -132,7 +132,7 @@ static ALuint LoadEffect(const EFXEAXREVERBPROPERTIES *reverb) alEffecti(effect, AL_REVERB_DECAY_HFLIMIT, reverb->iDecayHFLimit); } - /* Check if an error occured, and clean up if so. */ + /* Check if an error occurred, and clean up if so. */ err = alGetError(); if(err != AL_NO_ERROR) { @@ -219,7 +219,7 @@ static ALuint LoadSound(const char *filename) free(membuf); sf_close(sndfile); - /* Check if an error occured, and clean up if so. */ + /* Check if an error occurred, and clean up if so. */ err = alGetError(); if(err != AL_NO_ERROR) { diff --git a/examples/alstreamcb.cpp b/examples/alstreamcb.cpp index a2e7b659..6cc24034 100644 --- a/examples/alstreamcb.cpp +++ b/examples/alstreamcb.cpp @@ -276,9 +276,9 @@ struct StreamPlayer { * but it allows the callback implementation to have a nice 'this' pointer * with normal member access. */ - static ALsizei AL_APIENTRY bufferCallbackC(void *userptr, void *data, ALsizei size) + static ALsizei AL_APIENTRY bufferCallbackC(void *userptr, void *data, ALsizei size) noexcept { return static_cast<StreamPlayer*>(userptr)->bufferCallback(data, size); } - ALsizei bufferCallback(void *data, ALsizei size) + ALsizei bufferCallback(void *data, ALsizei size) noexcept { /* NOTE: The callback *MUST* be real-time safe! That means no blocking, * no allocations or deallocations, no I/O, no page faults, or calls to diff --git a/examples/altonegen.c b/examples/altonegen.c index 75db2d6b..ec7f77bb 100644 --- a/examples/altonegen.c +++ b/examples/altonegen.c @@ -79,63 +79,72 @@ static inline ALuint dither_rng(ALuint *seed) return *seed; } -static void ApplySin(ALfloat *data, ALdouble g, ALuint srate, ALuint freq) +static void ApplySin(ALfloat *data, ALuint length, ALdouble g, ALuint srate, ALuint freq) { - ALdouble smps_per_cycle = (ALdouble)srate / freq; + ALdouble cycles_per_sample = (ALdouble)freq / srate; ALuint i; - for(i = 0;i < srate;i++) + for(i = 0;i < length;i++) { ALdouble ival; - data[i] += (ALfloat)(sin(modf(i/smps_per_cycle, &ival) * 2.0*M_PI) * g); + data[i] += (ALfloat)(sin(modf(i*cycles_per_sample, &ival) * 2.0*M_PI) * g); } } /* Generates waveforms using additive synthesis. Each waveform is constructed * by summing one or more sine waves, up to (and excluding) nyquist. */ -static ALuint CreateWave(enum WaveType type, ALuint freq, ALuint srate, ALfloat gain) +static ALuint CreateWave(enum WaveType type, ALuint seconds, ALuint freq, ALuint srate, + ALfloat gain) { ALuint seed = 22222; + ALuint num_samples; ALuint data_size; ALfloat *data; ALuint buffer; ALenum err; ALuint i; - data_size = (ALuint)(srate * sizeof(ALfloat)); + if(seconds > INT_MAX / srate / sizeof(ALfloat)) + { + fprintf(stderr, "Too many seconds: %u * %u * %zu > %d\n", seconds, srate, sizeof(ALfloat), + INT_MAX); + return 0; + } + + num_samples = seconds * srate; + + data_size = (ALuint)(num_samples * sizeof(ALfloat)); data = calloc(1, data_size); switch(type) { case WT_Sine: - ApplySin(data, 1.0, srate, freq); + ApplySin(data, num_samples, 1.0, srate, freq); break; case WT_Square: for(i = 1;freq*i < srate/2;i+=2) - ApplySin(data, 4.0/M_PI * 1.0/i, srate, freq*i); + ApplySin(data, num_samples, 4.0/M_PI * 1.0/i, srate, freq*i); break; case WT_Sawtooth: for(i = 1;freq*i < srate/2;i++) - ApplySin(data, 2.0/M_PI * ((i&1)*2 - 1.0) / i, srate, freq*i); + ApplySin(data, num_samples, 2.0/M_PI * ((i&1)*2 - 1.0) / i, srate, freq*i); break; case WT_Triangle: for(i = 1;freq*i < srate/2;i+=2) - ApplySin(data, 8.0/(M_PI*M_PI) * (1.0 - (i&2)) / (i*i), srate, freq*i); + ApplySin(data, num_samples, 8.0/(M_PI*M_PI) * (1.0 - (i&2)) / (i*i), srate, freq*i); break; case WT_Impulse: /* NOTE: Impulse isn't handled using additive synthesis, and is - * instead just a non-0 sample at a given rate. This can still be - * useful to test (other than resampling, the ALSOFT_DEFAULT_REVERB - * environment variable can prove useful here to test the reverb - * response). + * instead just a non-0 sample. This can be useful to test (other + * than resampling, the ALSOFT_DEFAULT_REVERB environment variable + * can test the reverb response). */ - for(i = 0;i < srate;i++) - data[i] = (i%(srate/freq)) ? 0.0f : 1.0f; + data[0] = 1.0f; break; case WT_WhiteNoise: /* NOTE: WhiteNoise is just uniform set of uncorrelated values, and * is not influenced by the waveform frequency. */ - for(i = 0;i < srate;i++) + for(i = 0;i < num_samples;i++) { ALuint rng0 = dither_rng(&seed); ALuint rng1 = dither_rng(&seed); @@ -146,7 +155,7 @@ static ALuint CreateWave(enum WaveType type, ALuint freq, ALuint srate, ALfloat if(gain != 1.0f) { - for(i = 0;i < srate;i++) + for(i = 0;i < num_samples;i++) data[i] *= gain; } @@ -156,7 +165,7 @@ static ALuint CreateWave(enum WaveType type, ALuint freq, ALuint srate, ALfloat alBufferData(buffer, AL_FORMAT_MONO_FLOAT32, data, (ALsizei)data_size, (ALsizei)srate); free(data); - /* Check if an error occured, and clean up if so. */ + /* Check if an error occurred, and clean up if so. */ err = alGetError(); if(err != AL_NO_ERROR) { @@ -175,8 +184,8 @@ int main(int argc, char *argv[]) enum WaveType wavetype = WT_Sine; const char *appname = argv[0]; ALuint source, buffer; - ALint last_pos, num_loops; - ALint max_loops = 4; + ALint last_pos; + ALint seconds = 4; ALint srate = -1; ALint tone_freq = 1000; ALCint dev_rate; @@ -220,7 +229,9 @@ int main(int argc, char *argv[]) else if(i+1 < argc && strcmp(argv[i], "-t") == 0) { i++; - max_loops = atoi(argv[i]) - 1; + seconds = atoi(argv[i]); + if(seconds <= 0) + seconds = 4; } else if(i+1 < argc && (strcmp(argv[i], "--waveform") == 0 || strcmp(argv[i], "-w") == 0)) { @@ -281,7 +292,7 @@ int main(int argc, char *argv[]) srate = dev_rate; /* Load the sound into a buffer. */ - buffer = CreateWave(wavetype, (ALuint)tone_freq, (ALuint)srate, gain); + buffer = CreateWave(wavetype, (ALuint)seconds, (ALuint)tone_freq, (ALuint)srate, gain); if(!buffer) { CloseAL(); @@ -289,7 +300,7 @@ int main(int argc, char *argv[]) } printf("Playing %dhz %s-wave tone with %dhz sample rate and %dhz output, for %d second%s...\n", - tone_freq, GetWaveTypeName(wavetype), srate, dev_rate, max_loops+1, max_loops?"s":""); + tone_freq, GetWaveTypeName(wavetype), srate, dev_rate, seconds, (seconds!=1)?"s":""); fflush(stdout); /* Create the source to play the sound with. */ @@ -299,21 +310,19 @@ int main(int argc, char *argv[]) assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source"); /* Play the sound for a while. */ - num_loops = 0; - last_pos = 0; - alSourcei(source, AL_LOOPING, (max_loops > 0) ? AL_TRUE : AL_FALSE); + last_pos = -1; alSourcePlay(source); do { ALint pos; al_nssleep(10000000); - alGetSourcei(source, AL_SAMPLE_OFFSET, &pos); alGetSourcei(source, AL_SOURCE_STATE, &state); - if(pos < last_pos && state == AL_PLAYING) + alGetSourcei(source, AL_SAMPLE_OFFSET, &pos); + pos /= srate; + + if(pos > last_pos) { - ++num_loops; - if(num_loops >= max_loops) - alSourcei(source, AL_LOOPING, AL_FALSE); - printf("%d...\n", max_loops - num_loops + 1); + last_pos = 0; + printf("%d...\n", seconds - pos); fflush(stdout); } last_pos = pos; diff --git a/include/AL/al.h b/include/AL/al.h index 5071fa5e..87274184 100644 --- a/include/AL/al.h +++ b/include/AL/al.h @@ -3,6 +3,25 @@ #ifdef __cplusplus extern "C" { + +#ifndef AL_DISABLE_NOEXCEPT +#define AL_API_NOEXCEPT noexcept +#if __cplusplus >= 201703L +#define AL_API_NOEXCEPT17 noexcept +#else +#define AL_API_NOEXCEPT17 +#endif + +#else /* AL_DISABLE_NOEXCEPT */ + +#define AL_API_NOEXCEPT +#define AL_API_NOEXCEPT17 +#endif + +#else /* __cplusplus */ + +#define AL_API_NOEXCEPT +#define AL_API_NOEXCEPT17 #endif #ifndef AL_API @@ -455,217 +474,217 @@ typedef void ALvoid; #ifndef AL_NO_PROTOTYPES /* Renderer State management. */ -AL_API void AL_APIENTRY alEnable(ALenum capability); -AL_API void AL_APIENTRY alDisable(ALenum capability); -AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability); +AL_API void AL_APIENTRY alEnable(ALenum capability) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alDisable(ALenum capability) AL_API_NOEXCEPT; +AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability) AL_API_NOEXCEPT; /* Context state setting. */ -AL_API void AL_APIENTRY alDopplerFactor(ALfloat value); -AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value); -AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value); -AL_API void AL_APIENTRY alDistanceModel(ALenum distanceModel); +AL_API void AL_APIENTRY alDopplerFactor(ALfloat value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alDistanceModel(ALenum distanceModel) AL_API_NOEXCEPT; /* Context state retrieval. */ -AL_API const ALchar* AL_APIENTRY alGetString(ALenum param); -AL_API void AL_APIENTRY alGetBooleanv(ALenum param, ALboolean *values); -AL_API void AL_APIENTRY alGetIntegerv(ALenum param, ALint *values); -AL_API void AL_APIENTRY alGetFloatv(ALenum param, ALfloat *values); -AL_API void AL_APIENTRY alGetDoublev(ALenum param, ALdouble *values); -AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum param); -AL_API ALint AL_APIENTRY alGetInteger(ALenum param); -AL_API ALfloat AL_APIENTRY alGetFloat(ALenum param); -AL_API ALdouble AL_APIENTRY alGetDouble(ALenum param); +AL_API const ALchar* AL_APIENTRY alGetString(ALenum param) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBooleanv(ALenum param, ALboolean *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetIntegerv(ALenum param, ALint *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetFloatv(ALenum param, ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetDoublev(ALenum param, ALdouble *values) AL_API_NOEXCEPT; +AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum param) AL_API_NOEXCEPT; +AL_API ALint AL_APIENTRY alGetInteger(ALenum param) AL_API_NOEXCEPT; +AL_API ALfloat AL_APIENTRY alGetFloat(ALenum param) AL_API_NOEXCEPT; +AL_API ALdouble AL_APIENTRY alGetDouble(ALenum param) AL_API_NOEXCEPT; /** * Obtain the first error generated in the AL context since the last call to * this function. */ -AL_API ALenum AL_APIENTRY alGetError(void); +AL_API ALenum AL_APIENTRY alGetError(void) AL_API_NOEXCEPT; /** Query for the presence of an extension on the AL context. */ -AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extname); +AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extname) AL_API_NOEXCEPT; /** * Retrieve the address of a function. The returned function may be context- * specific. */ -AL_API void* AL_APIENTRY alGetProcAddress(const ALchar *fname); +AL_API void* AL_APIENTRY alGetProcAddress(const ALchar *fname) AL_API_NOEXCEPT; /** * Retrieve the value of an enum. The returned value may be context-specific. */ -AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *ename); +AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *ename) AL_API_NOEXCEPT; /* Set listener parameters. */ -AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value); -AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values); -AL_API void AL_APIENTRY alListeneri(ALenum param, ALint value); -AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3); -AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values); +AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alListeneri(ALenum param, ALint value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values) AL_API_NOEXCEPT; /* Get listener parameters. */ -AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value); -AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values); -AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value); -AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3); -AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint *values); +AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint *values) AL_API_NOEXCEPT; /** Create source objects. */ -AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources); +AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources) AL_API_NOEXCEPT; /** Delete source objects. */ -AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources); +AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; /** Verify an ID is for a valid source. */ -AL_API ALboolean AL_APIENTRY alIsSource(ALuint source); +AL_API ALboolean AL_APIENTRY alIsSource(ALuint source) AL_API_NOEXCEPT; /* Set source parameters. */ -AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value); -AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values); -AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value); -AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3); -AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values); +AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT; /* Get source parameters. */ -AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value); -AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values); -AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value); -AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3); -AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values); +AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT; /** Play, restart, or resume a source, setting its state to AL_PLAYING. */ -AL_API void AL_APIENTRY alSourcePlay(ALuint source); +AL_API void AL_APIENTRY alSourcePlay(ALuint source) AL_API_NOEXCEPT; /** Stop a source, setting its state to AL_STOPPED if playing or paused. */ -AL_API void AL_APIENTRY alSourceStop(ALuint source); +AL_API void AL_APIENTRY alSourceStop(ALuint source) AL_API_NOEXCEPT; /** Rewind a source, setting its state to AL_INITIAL. */ -AL_API void AL_APIENTRY alSourceRewind(ALuint source); +AL_API void AL_APIENTRY alSourceRewind(ALuint source) AL_API_NOEXCEPT; /** Pause a source, setting its state to AL_PAUSED if playing. */ -AL_API void AL_APIENTRY alSourcePause(ALuint source); +AL_API void AL_APIENTRY alSourcePause(ALuint source) AL_API_NOEXCEPT; /** Play, restart, or resume a list of sources atomically. */ -AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources); +AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; /** Stop a list of sources atomically. */ -AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources); +AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; /** Rewind a list of sources atomically. */ -AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources); +AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; /** Pause a list of sources atomically. */ -AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources); +AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; /** Queue buffers onto a source */ -AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint source, ALsizei nb, const ALuint *buffers); +AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT; /** Unqueue processed buffers from a source */ -AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint source, ALsizei nb, ALuint *buffers); +AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT; /** Create buffer objects */ -AL_API void AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers); +AL_API void AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers) AL_API_NOEXCEPT; /** Delete buffer objects */ -AL_API void AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers); +AL_API void AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT; /** Verify an ID is a valid buffer (including the NULL buffer) */ -AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer); +AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer) AL_API_NOEXCEPT; /** * Copies data into the buffer, interpreting it using the specified format and * samplerate. */ -AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate); +AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT; /* Set buffer parameters. */ -AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat value); -AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values); -AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value); -AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3); -AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values); +AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT; /* Get buffer parameters. */ -AL_API void AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value); -AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values); -AL_API void AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value); -AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3); -AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values); +AL_API void AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT; #endif /* AL_NO_PROTOTYPES */ /* Pointer-to-function types, useful for storing dynamically loaded AL entry * points. */ -typedef void (AL_APIENTRY *LPALENABLE)(ALenum capability); -typedef void (AL_APIENTRY *LPALDISABLE)(ALenum capability); -typedef ALboolean (AL_APIENTRY *LPALISENABLED)(ALenum capability); -typedef const ALchar* (AL_APIENTRY *LPALGETSTRING)(ALenum param); -typedef void (AL_APIENTRY *LPALGETBOOLEANV)(ALenum param, ALboolean *values); -typedef void (AL_APIENTRY *LPALGETINTEGERV)(ALenum param, ALint *values); -typedef void (AL_APIENTRY *LPALGETFLOATV)(ALenum param, ALfloat *values); -typedef void (AL_APIENTRY *LPALGETDOUBLEV)(ALenum param, ALdouble *values); -typedef ALboolean (AL_APIENTRY *LPALGETBOOLEAN)(ALenum param); -typedef ALint (AL_APIENTRY *LPALGETINTEGER)(ALenum param); -typedef ALfloat (AL_APIENTRY *LPALGETFLOAT)(ALenum param); -typedef ALdouble (AL_APIENTRY *LPALGETDOUBLE)(ALenum param); -typedef ALenum (AL_APIENTRY *LPALGETERROR)(void); -typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENT)(const ALchar *extname); -typedef void* (AL_APIENTRY *LPALGETPROCADDRESS)(const ALchar *fname); -typedef ALenum (AL_APIENTRY *LPALGETENUMVALUE)(const ALchar *ename); -typedef void (AL_APIENTRY *LPALLISTENERF)(ALenum param, ALfloat value); -typedef void (AL_APIENTRY *LPALLISTENER3F)(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -typedef void (AL_APIENTRY *LPALLISTENERFV)(ALenum param, const ALfloat *values); -typedef void (AL_APIENTRY *LPALLISTENERI)(ALenum param, ALint value); -typedef void (AL_APIENTRY *LPALLISTENER3I)(ALenum param, ALint value1, ALint value2, ALint value3); -typedef void (AL_APIENTRY *LPALLISTENERIV)(ALenum param, const ALint *values); -typedef void (AL_APIENTRY *LPALGETLISTENERF)(ALenum param, ALfloat *value); -typedef void (AL_APIENTRY *LPALGETLISTENER3F)(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -typedef void (AL_APIENTRY *LPALGETLISTENERFV)(ALenum param, ALfloat *values); -typedef void (AL_APIENTRY *LPALGETLISTENERI)(ALenum param, ALint *value); -typedef void (AL_APIENTRY *LPALGETLISTENER3I)(ALenum param, ALint *value1, ALint *value2, ALint *value3); -typedef void (AL_APIENTRY *LPALGETLISTENERIV)(ALenum param, ALint *values); -typedef void (AL_APIENTRY *LPALGENSOURCES)(ALsizei n, ALuint *sources); -typedef void (AL_APIENTRY *LPALDELETESOURCES)(ALsizei n, const ALuint *sources); -typedef ALboolean (AL_APIENTRY *LPALISSOURCE)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCEF)(ALuint source, ALenum param, ALfloat value); -typedef void (AL_APIENTRY *LPALSOURCE3F)(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -typedef void (AL_APIENTRY *LPALSOURCEFV)(ALuint source, ALenum param, const ALfloat *values); -typedef void (AL_APIENTRY *LPALSOURCEI)(ALuint source, ALenum param, ALint value); -typedef void (AL_APIENTRY *LPALSOURCE3I)(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3); -typedef void (AL_APIENTRY *LPALSOURCEIV)(ALuint source, ALenum param, const ALint *values); -typedef void (AL_APIENTRY *LPALGETSOURCEF)(ALuint source, ALenum param, ALfloat *value); -typedef void (AL_APIENTRY *LPALGETSOURCE3F)(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -typedef void (AL_APIENTRY *LPALGETSOURCEFV)(ALuint source, ALenum param, ALfloat *values); -typedef void (AL_APIENTRY *LPALGETSOURCEI)(ALuint source, ALenum param, ALint *value); -typedef void (AL_APIENTRY *LPALGETSOURCE3I)(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3); -typedef void (AL_APIENTRY *LPALGETSOURCEIV)(ALuint source, ALenum param, ALint *values); -typedef void (AL_APIENTRY *LPALSOURCEPLAYV)(ALsizei n, const ALuint *sources); -typedef void (AL_APIENTRY *LPALSOURCESTOPV)(ALsizei n, const ALuint *sources); -typedef void (AL_APIENTRY *LPALSOURCEREWINDV)(ALsizei n, const ALuint *sources); -typedef void (AL_APIENTRY *LPALSOURCEPAUSEV)(ALsizei n, const ALuint *sources); -typedef void (AL_APIENTRY *LPALSOURCEPLAY)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCESTOP)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCEREWIND)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCEPAUSE)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint source, ALsizei nb, const ALuint *buffers); -typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint source, ALsizei nb, ALuint *buffers); -typedef void (AL_APIENTRY *LPALGENBUFFERS)(ALsizei n, ALuint *buffers); -typedef void (AL_APIENTRY *LPALDELETEBUFFERS)(ALsizei n, const ALuint *buffers); -typedef ALboolean (AL_APIENTRY *LPALISBUFFER)(ALuint buffer); -typedef void (AL_APIENTRY *LPALBUFFERDATA)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate); -typedef void (AL_APIENTRY *LPALBUFFERF)(ALuint buffer, ALenum param, ALfloat value); -typedef void (AL_APIENTRY *LPALBUFFER3F)(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -typedef void (AL_APIENTRY *LPALBUFFERFV)(ALuint buffer, ALenum param, const ALfloat *values); -typedef void (AL_APIENTRY *LPALBUFFERI)(ALuint buffer, ALenum param, ALint value); -typedef void (AL_APIENTRY *LPALBUFFER3I)(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3); -typedef void (AL_APIENTRY *LPALBUFFERIV)(ALuint buffer, ALenum param, const ALint *values); -typedef void (AL_APIENTRY *LPALGETBUFFERF)(ALuint buffer, ALenum param, ALfloat *value); -typedef void (AL_APIENTRY *LPALGETBUFFER3F)(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -typedef void (AL_APIENTRY *LPALGETBUFFERFV)(ALuint buffer, ALenum param, ALfloat *values); -typedef void (AL_APIENTRY *LPALGETBUFFERI)(ALuint buffer, ALenum param, ALint *value); -typedef void (AL_APIENTRY *LPALGETBUFFER3I)(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3); -typedef void (AL_APIENTRY *LPALGETBUFFERIV)(ALuint buffer, ALenum param, ALint *values); -typedef void (AL_APIENTRY *LPALDOPPLERFACTOR)(ALfloat value); -typedef void (AL_APIENTRY *LPALDOPPLERVELOCITY)(ALfloat value); -typedef void (AL_APIENTRY *LPALSPEEDOFSOUND)(ALfloat value); -typedef void (AL_APIENTRY *LPALDISTANCEMODEL)(ALenum distanceModel); +typedef void (AL_APIENTRY *LPALENABLE)(ALenum capability) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDISABLE)(ALenum capability) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISENABLED)(ALenum capability) AL_API_NOEXCEPT17; +typedef const ALchar* (AL_APIENTRY *LPALGETSTRING)(ALenum param) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBOOLEANV)(ALenum param, ALboolean *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETINTEGERV)(ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFLOATV)(ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETDOUBLEV)(ALenum param, ALdouble *values) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALGETBOOLEAN)(ALenum param) AL_API_NOEXCEPT17; +typedef ALint (AL_APIENTRY *LPALGETINTEGER)(ALenum param) AL_API_NOEXCEPT17; +typedef ALfloat (AL_APIENTRY *LPALGETFLOAT)(ALenum param) AL_API_NOEXCEPT17; +typedef ALdouble (AL_APIENTRY *LPALGETDOUBLE)(ALenum param) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPALGETERROR)(void) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENT)(const ALchar *extname) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY *LPALGETPROCADDRESS)(const ALchar *fname) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPALGETENUMVALUE)(const ALchar *ename) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERF)(ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENER3F)(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERFV)(ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERI)(ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENER3I)(ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERIV)(ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERF)(ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENER3F)(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERFV)(ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERI)(ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENER3I)(ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERIV)(ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENSOURCES)(ALsizei n, ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETESOURCES)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISSOURCE)(ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEF)(ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3F)(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEFV)(ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEI)(ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3I)(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEIV)(ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEF)(ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3F)(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEFV)(ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEI)(ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3I)(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEIV)(ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPLAYV)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCESTOPV)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEREWINDV)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPAUSEV)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPLAY)(ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCESTOP)(ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEREWIND)(ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPAUSE)(ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENBUFFERS)(ALsizei n, ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEBUFFERS)(ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISBUFFER)(ALuint buffer) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERDATA)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERF)(ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFER3F)(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERFV)(ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERI)(ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFER3I)(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERIV)(ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERF)(ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFER3F)(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERFV)(ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERI)(ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFER3I)(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERIV)(ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDOPPLERFACTOR)(ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDOPPLERVELOCITY)(ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSPEEDOFSOUND)(ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDISTANCEMODEL)(ALenum distanceModel) AL_API_NOEXCEPT17; #ifdef __cplusplus } /* extern "C" */ diff --git a/include/AL/alc.h b/include/AL/alc.h index 6d210333..73dcf08f 100644 --- a/include/AL/alc.h +++ b/include/AL/alc.h @@ -3,6 +3,25 @@ #ifdef __cplusplus extern "C" { + +#ifndef AL_DISABLE_NOEXCEPT +#define ALC_API_NOEXCEPT noexcept +#if __cplusplus >= 201703L +#define ALC_API_NOEXCEPT17 noexcept +#else +#define ALC_API_NOEXCEPT17 +#endif + +#else /* AL_DISABLE_NOEXCEPT */ + +#define ALC_API_NOEXCEPT +#define ALC_API_NOEXCEPT17 +#endif + +#else /* __cplusplus */ + +#define ALC_API_NOEXCEPT +#define ALC_API_NOEXCEPT17 #endif #ifndef ALC_API @@ -172,34 +191,34 @@ typedef void ALCvoid; /* Context management. */ /** Create and attach a context to the given device. */ -ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrlist); +ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrlist) ALC_API_NOEXCEPT; /** * Makes the given context the active process-wide context. Passing NULL clears * the active context. */ -ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context); +ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) ALC_API_NOEXCEPT; /** Resumes processing updates for the given context. */ -ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context); +ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context) ALC_API_NOEXCEPT; /** Suspends updates for the given context. */ -ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context); +ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context) ALC_API_NOEXCEPT; /** Remove a context from its device and destroys it. */ -ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context); +ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) ALC_API_NOEXCEPT; /** Returns the currently active context. */ -ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void); +ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) ALC_API_NOEXCEPT; /** Returns the device that a particular context is attached to. */ -ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context); +ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context) ALC_API_NOEXCEPT; /* Device management. */ /** Opens the named playback device. */ -ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename); +ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename) ALC_API_NOEXCEPT; /** Closes the given playback device. */ -ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device); +ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) ALC_API_NOEXCEPT; /* Error support. */ /** Obtain the most recent Device error. */ -ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device); +ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device) ALC_API_NOEXCEPT; /* Extension support. */ @@ -207,24 +226,24 @@ ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device); * Query for the presence of an extension on the device. Pass a NULL device to * query a device-inspecific extension. */ -ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname); +ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname) ALC_API_NOEXCEPT; /** * Retrieve the address of a function. Given a non-NULL device, the returned * function may be device-specific. */ -ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname); +ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname) ALC_API_NOEXCEPT; /** * Retrieve the value of an enum. Given a non-NULL device, the returned value * may be device-specific. */ -ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname); +ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname) ALC_API_NOEXCEPT; /* Query functions. */ /** Returns information about the device, and error strings. */ -ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param); +ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param) ALC_API_NOEXCEPT; /** Returns information about the device and the version of OpenAL. */ -ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values); +ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) ALC_API_NOEXCEPT; /* Capture functions. */ @@ -232,40 +251,40 @@ ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum pa * Opens the named capture device with the given frequency, format, and buffer * size. */ -ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize); +ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize) ALC_API_NOEXCEPT; /** Closes the given capture device. */ -ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device); +ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) ALC_API_NOEXCEPT; /** Starts capturing samples into the device buffer. */ -ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device); +ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) ALC_API_NOEXCEPT; /** Stops capturing samples. Samples in the device buffer remain available. */ -ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device); +ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) ALC_API_NOEXCEPT; /** Reads samples from the device buffer. */ -ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); +ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) ALC_API_NOEXCEPT; #endif /* ALC_NO_PROTOTYPES */ /* Pointer-to-function types, useful for storing dynamically loaded ALC entry * points. */ -typedef ALCcontext* (ALC_APIENTRY *LPALCCREATECONTEXT)(ALCdevice *device, const ALCint *attrlist); -typedef ALCboolean (ALC_APIENTRY *LPALCMAKECONTEXTCURRENT)(ALCcontext *context); -typedef void (ALC_APIENTRY *LPALCPROCESSCONTEXT)(ALCcontext *context); -typedef void (ALC_APIENTRY *LPALCSUSPENDCONTEXT)(ALCcontext *context); -typedef void (ALC_APIENTRY *LPALCDESTROYCONTEXT)(ALCcontext *context); -typedef ALCcontext* (ALC_APIENTRY *LPALCGETCURRENTCONTEXT)(void); -typedef ALCdevice* (ALC_APIENTRY *LPALCGETCONTEXTSDEVICE)(ALCcontext *context); -typedef ALCdevice* (ALC_APIENTRY *LPALCOPENDEVICE)(const ALCchar *devicename); -typedef ALCboolean (ALC_APIENTRY *LPALCCLOSEDEVICE)(ALCdevice *device); -typedef ALCenum (ALC_APIENTRY *LPALCGETERROR)(ALCdevice *device); -typedef ALCboolean (ALC_APIENTRY *LPALCISEXTENSIONPRESENT)(ALCdevice *device, const ALCchar *extname); -typedef ALCvoid* (ALC_APIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname); -typedef ALCenum (ALC_APIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname); -typedef const ALCchar* (ALC_APIENTRY *LPALCGETSTRING)(ALCdevice *device, ALCenum param); -typedef void (ALC_APIENTRY *LPALCGETINTEGERV)(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values); -typedef ALCdevice* (ALC_APIENTRY *LPALCCAPTUREOPENDEVICE)(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize); -typedef ALCboolean (ALC_APIENTRY *LPALCCAPTURECLOSEDEVICE)(ALCdevice *device); -typedef void (ALC_APIENTRY *LPALCCAPTURESTART)(ALCdevice *device); -typedef void (ALC_APIENTRY *LPALCCAPTURESTOP)(ALCdevice *device); -typedef void (ALC_APIENTRY *LPALCCAPTURESAMPLES)(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); +typedef ALCcontext* (ALC_APIENTRY *LPALCCREATECONTEXT)(ALCdevice *device, const ALCint *attrlist) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY *LPALCMAKECONTEXTCURRENT)(ALCcontext *context) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCPROCESSCONTEXT)(ALCcontext *context) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCSUSPENDCONTEXT)(ALCcontext *context) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCDESTROYCONTEXT)(ALCcontext *context) ALC_API_NOEXCEPT17; +typedef ALCcontext* (ALC_APIENTRY *LPALCGETCURRENTCONTEXT)(void) ALC_API_NOEXCEPT17; +typedef ALCdevice* (ALC_APIENTRY *LPALCGETCONTEXTSDEVICE)(ALCcontext *context) ALC_API_NOEXCEPT17; +typedef ALCdevice* (ALC_APIENTRY *LPALCOPENDEVICE)(const ALCchar *devicename) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY *LPALCCLOSEDEVICE)(ALCdevice *device) ALC_API_NOEXCEPT17; +typedef ALCenum (ALC_APIENTRY *LPALCGETERROR)(ALCdevice *device) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY *LPALCISEXTENSIONPRESENT)(ALCdevice *device, const ALCchar *extname) ALC_API_NOEXCEPT17; +typedef ALCvoid* (ALC_APIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname) ALC_API_NOEXCEPT17; +typedef ALCenum (ALC_APIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname) ALC_API_NOEXCEPT17; +typedef const ALCchar* (ALC_APIENTRY *LPALCGETSTRING)(ALCdevice *device, ALCenum param) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCGETINTEGERV)(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) ALC_API_NOEXCEPT17; +typedef ALCdevice* (ALC_APIENTRY *LPALCCAPTUREOPENDEVICE)(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY *LPALCCAPTURECLOSEDEVICE)(ALCdevice *device) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCCAPTURESTART)(ALCdevice *device) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCCAPTURESTOP)(ALCdevice *device) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCCAPTURESAMPLES)(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) ALC_API_NOEXCEPT17; #ifdef __cplusplus } /* extern "C" */ diff --git a/include/AL/alext.h b/include/AL/alext.h index d313a999..c75e0770 100644 --- a/include/AL/alext.h +++ b/include/AL/alext.h @@ -141,9 +141,9 @@ extern "C" { #ifndef AL_EXT_STATIC_BUFFER #define AL_EXT_STATIC_BUFFER 1 -typedef void (AL_APIENTRY*PFNALBUFFERDATASTATICPROC)(const ALuint,ALenum,ALvoid*,ALsizei,ALsizei); +typedef void (AL_APIENTRY*PFNALBUFFERDATASTATICPROC)(const ALuint,ALenum,ALvoid*,ALsizei,ALsizei) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -void AL_APIENTRY alBufferDataStatic(const ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq); +void AL_APIENTRY alBufferDataStatic(const ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq) AL_API_NOEXCEPT; #endif #endif @@ -159,11 +159,11 @@ void AL_APIENTRY alBufferDataStatic(const ALuint buffer, ALenum format, ALvoid * #ifndef ALC_EXT_thread_local_context #define ALC_EXT_thread_local_context 1 -typedef ALCboolean (ALC_APIENTRY*PFNALCSETTHREADCONTEXTPROC)(ALCcontext *context); -typedef ALCcontext* (ALC_APIENTRY*PFNALCGETTHREADCONTEXTPROC)(void); +typedef ALCboolean (ALC_APIENTRY*PFNALCSETTHREADCONTEXTPROC)(ALCcontext *context) ALC_API_NOEXCEPT17; +typedef ALCcontext* (ALC_APIENTRY*PFNALCGETTHREADCONTEXTPROC)(void) ALC_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context); -ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void); +ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context) ALC_API_NOEXCEPT; +ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) ALC_API_NOEXCEPT; #endif #endif @@ -176,9 +176,9 @@ ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void); #define AL_SOFT_buffer_sub_data 1 #define AL_BYTE_RW_OFFSETS_SOFT 0x1031 #define AL_SAMPLE_RW_OFFSETS_SOFT 0x1032 -typedef void (AL_APIENTRY*PFNALBUFFERSUBDATASOFTPROC)(ALuint,ALenum,const ALvoid*,ALsizei,ALsizei); +typedef void (AL_APIENTRY*PFNALBUFFERSUBDATASOFTPROC)(ALuint,ALenum,const ALvoid*,ALsizei,ALsizei) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const ALvoid *data,ALsizei offset,ALsizei length); +AL_API void AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const ALvoid *data,ALsizei offset,ALsizei length) AL_API_NOEXCEPT; #endif #endif @@ -195,12 +195,12 @@ AL_API void AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const AL #define AL_FOLDBACK_EVENT_STOP 0x4113 #define AL_FOLDBACK_MODE_MONO 0x4101 #define AL_FOLDBACK_MODE_STEREO 0x4102 -typedef void (AL_APIENTRY*LPALFOLDBACKCALLBACK)(ALenum,ALsizei); -typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTART)(ALenum,ALsizei,ALsizei,ALfloat*,LPALFOLDBACKCALLBACK); -typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTOP)(void); +typedef void (AL_APIENTRY*LPALFOLDBACKCALLBACK)(ALenum,ALsizei) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTART)(ALenum,ALsizei,ALsizei,ALfloat*,LPALFOLDBACKCALLBACK) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTOP)(void) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alRequestFoldbackStart(ALenum mode,ALsizei count,ALsizei length,ALfloat *mem,LPALFOLDBACKCALLBACK callback); -AL_API void AL_APIENTRY alRequestFoldbackStop(void); +AL_API void AL_APIENTRY alRequestFoldbackStart(ALenum mode,ALsizei count,ALsizei length,ALfloat *mem,LPALFOLDBACKCALLBACK callback) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alRequestFoldbackStop(void) AL_API_NOEXCEPT; #endif #endif @@ -263,15 +263,15 @@ AL_API void AL_APIENTRY alRequestFoldbackStop(void); #define AL_SAMPLE_LENGTH_SOFT 0x200A #define AL_SEC_LENGTH_SOFT 0x200B -typedef void (AL_APIENTRY*LPALBUFFERSAMPLESSOFT)(ALuint,ALuint,ALenum,ALsizei,ALenum,ALenum,const ALvoid*); -typedef void (AL_APIENTRY*LPALBUFFERSUBSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,const ALvoid*); -typedef void (AL_APIENTRY*LPALGETBUFFERSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,ALvoid*); -typedef ALboolean (AL_APIENTRY*LPALISBUFFERFORMATSUPPORTEDSOFT)(ALenum); +typedef void (AL_APIENTRY*LPALBUFFERSAMPLESSOFT)(ALuint,ALuint,ALenum,ALsizei,ALenum,ALenum,const ALvoid*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALBUFFERSUBSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,const ALvoid*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETBUFFERSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,ALvoid*) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY*LPALISBUFFERFORMATSUPPORTEDSOFT)(ALenum) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, ALuint samplerate, ALenum internalformat, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data); -AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data); -AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, ALvoid *data); -AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format); +AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, ALuint samplerate, ALenum internalformat, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, ALvoid *data) AL_API_NOEXCEPT; +AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format) AL_API_NOEXCEPT; #endif #endif @@ -302,13 +302,13 @@ AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format); #define ALC_6POINT1_SOFT 0x1505 #define ALC_7POINT1_SOFT 0x1506 -typedef ALCdevice* (ALC_APIENTRY*LPALCLOOPBACKOPENDEVICESOFT)(const ALCchar*); -typedef ALCboolean (ALC_APIENTRY*LPALCISRENDERFORMATSUPPORTEDSOFT)(ALCdevice*,ALCsizei,ALCenum,ALCenum); -typedef void (ALC_APIENTRY*LPALCRENDERSAMPLESSOFT)(ALCdevice*,ALCvoid*,ALCsizei); +typedef ALCdevice* (ALC_APIENTRY*LPALCLOOPBACKOPENDEVICESOFT)(const ALCchar*) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY*LPALCISRENDERFORMATSUPPORTEDSOFT)(ALCdevice*,ALCsizei,ALCenum,ALCenum) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY*LPALCRENDERSAMPLESSOFT)(ALCdevice*,ALCvoid*,ALCsizei) ALC_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName); -ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type); -ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); +ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName) AL_API_NOEXCEPT; +ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type) AL_API_NOEXCEPT; +ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) AL_API_NOEXCEPT; #endif #endif @@ -328,31 +328,31 @@ ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffe #define AL_SEC_OFFSET_LATENCY_SOFT 0x1201 typedef _alsoft_int64_t ALint64SOFT; typedef _alsoft_uint64_t ALuint64SOFT; -typedef void (AL_APIENTRY*LPALSOURCEDSOFT)(ALuint,ALenum,ALdouble); -typedef void (AL_APIENTRY*LPALSOURCE3DSOFT)(ALuint,ALenum,ALdouble,ALdouble,ALdouble); -typedef void (AL_APIENTRY*LPALSOURCEDVSOFT)(ALuint,ALenum,const ALdouble*); -typedef void (AL_APIENTRY*LPALGETSOURCEDSOFT)(ALuint,ALenum,ALdouble*); -typedef void (AL_APIENTRY*LPALGETSOURCE3DSOFT)(ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*); -typedef void (AL_APIENTRY*LPALGETSOURCEDVSOFT)(ALuint,ALenum,ALdouble*); -typedef void (AL_APIENTRY*LPALSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT); -typedef void (AL_APIENTRY*LPALSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT); -typedef void (AL_APIENTRY*LPALSOURCEI64VSOFT)(ALuint,ALenum,const ALint64SOFT*); -typedef void (AL_APIENTRY*LPALGETSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT*); -typedef void (AL_APIENTRY*LPALGETSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*); -typedef void (AL_APIENTRY*LPALGETSOURCEI64VSOFT)(ALuint,ALenum,ALint64SOFT*); +typedef void (AL_APIENTRY*LPALSOURCEDSOFT)(ALuint,ALenum,ALdouble) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALSOURCE3DSOFT)(ALuint,ALenum,ALdouble,ALdouble,ALdouble) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALSOURCEDVSOFT)(ALuint,ALenum,const ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETSOURCEDSOFT)(ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETSOURCE3DSOFT)(ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETSOURCEDVSOFT)(ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALSOURCEI64VSOFT)(ALuint,ALenum,const ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETSOURCEI64VSOFT)(ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value); -AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3); -AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values); -AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value); -AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3); -AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values); -AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value); -AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3); -AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values); -AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value); -AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3); -AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values); +AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values) AL_API_NOEXCEPT; #endif #endif @@ -364,11 +364,11 @@ AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64 #ifndef AL_SOFT_deferred_updates #define AL_SOFT_deferred_updates 1 #define AL_DEFERRED_UPDATES_SOFT 0xC002 -typedef void (AL_APIENTRY*LPALDEFERUPDATESSOFT)(void); -typedef void (AL_APIENTRY*LPALPROCESSUPDATESSOFT)(void); +typedef void (AL_APIENTRY*LPALDEFERUPDATESSOFT)(void) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALPROCESSUPDATESSOFT)(void) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alDeferUpdatesSOFT(void); -AL_API void AL_APIENTRY alProcessUpdatesSOFT(void); +AL_API void AL_APIENTRY alDeferUpdatesSOFT(void) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alProcessUpdatesSOFT(void) AL_API_NOEXCEPT; #endif #endif @@ -400,11 +400,11 @@ AL_API void AL_APIENTRY alProcessUpdatesSOFT(void); #ifndef ALC_SOFT_pause_device #define ALC_SOFT_pause_device 1 -typedef void (ALC_APIENTRY*LPALCDEVICEPAUSESOFT)(ALCdevice *device); -typedef void (ALC_APIENTRY*LPALCDEVICERESUMESOFT)(ALCdevice *device); +typedef void (ALC_APIENTRY*LPALCDEVICEPAUSESOFT)(ALCdevice *device) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY*LPALCDEVICERESUMESOFT)(ALCdevice *device) ALC_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device); -ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device); +ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device) ALC_API_NOEXCEPT; +ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) ALC_API_NOEXCEPT; #endif #endif @@ -448,11 +448,11 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device); #define ALC_NUM_HRTF_SPECIFIERS_SOFT 0x1994 #define ALC_HRTF_SPECIFIER_SOFT 0x1995 #define ALC_HRTF_ID_SOFT 0x1996 -typedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index); -typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs); +typedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs) ALC_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index); -ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs); +ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index) ALC_API_NOEXCEPT; +ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs) ALC_API_NOEXCEPT; #endif #endif @@ -467,9 +467,9 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi #define AL_DEFAULT_RESAMPLER_SOFT 0x1211 #define AL_SOURCE_RESAMPLER_SOFT 0x1212 #define AL_RESAMPLER_NAME_SOFT 0x1213 -typedef const ALchar* (AL_APIENTRY*LPALGETSTRINGISOFT)(ALenum pname, ALsizei index); +typedef const ALchar* (AL_APIENTRY*LPALGETSTRINGISOFT)(ALenum pname, ALsizei index) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index); +AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index) AL_API_NOEXCEPT; #endif #endif @@ -493,9 +493,9 @@ typedef _alsoft_uint64_t ALCuint64SOFT; #define ALC_DEVICE_CLOCK_LATENCY_SOFT 0x1602 #define AL_SAMPLE_OFFSET_CLOCK_SOFT 0x1202 #define AL_SEC_OFFSET_CLOCK_SOFT 0x1203 -typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values); +typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values) ALC_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values); +ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values) ALC_API_NOEXCEPT; #endif #endif @@ -552,27 +552,26 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, #define AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT 0x19A5 #define AL_EVENT_TYPE_DISCONNECTED_SOFT 0x19A6 typedef void (AL_APIENTRY*ALEVENTPROCSOFT)(ALenum eventType, ALuint object, ALuint param, - ALsizei length, const ALchar *message, - void *userParam); -typedef void (AL_APIENTRY*LPALEVENTCONTROLSOFT)(ALsizei count, const ALenum *types, ALboolean enable); -typedef void (AL_APIENTRY*LPALEVENTCALLBACKSOFT)(ALEVENTPROCSOFT callback, void *userParam); -typedef void* (AL_APIENTRY*LPALGETPOINTERSOFT)(ALenum pname); -typedef void (AL_APIENTRY*LPALGETPOINTERVSOFT)(ALenum pname, void **values); + ALsizei length, const ALchar *message, void *userParam) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALEVENTCONTROLSOFT)(ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALEVENTCALLBACKSOFT)(ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY*LPALGETPOINTERSOFT)(ALenum pname) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETPOINTERVSOFT)(ALenum pname, void **values) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable); -AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam); -AL_API void* AL_APIENTRY alGetPointerSOFT(ALenum pname); -AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, void **values); +AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT; +AL_API void* AL_APIENTRY alGetPointerSOFT(ALenum pname) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, void **values) AL_API_NOEXCEPT; #endif #endif #ifndef ALC_SOFT_reopen_device #define ALC_SOFT_reopen_device typedef ALCboolean (ALC_APIENTRY*LPALCREOPENDEVICESOFT)(ALCdevice *device, - const ALCchar *deviceName, const ALCint *attribs); + const ALCchar *deviceName, const ALCint *attribs) ALC_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, const ALCchar *deviceName, - const ALCint *attribs); + const ALCint *attribs) ALC_API_NOEXCEPT; #endif #endif @@ -580,16 +579,16 @@ ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, const ALCchar *de #define AL_SOFT_callback_buffer #define AL_BUFFER_CALLBACK_FUNCTION_SOFT 0x19A0 #define AL_BUFFER_CALLBACK_USER_PARAM_SOFT 0x19A1 -typedef ALsizei (AL_APIENTRY*ALBUFFERCALLBACKTYPESOFT)(ALvoid *userptr, ALvoid *sampledata, ALsizei numbytes); -typedef void (AL_APIENTRY*LPALBUFFERCALLBACKSOFT)(ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr); -typedef void (AL_APIENTRY*LPALGETBUFFERPTRSOFT)(ALuint buffer, ALenum param, ALvoid **value); -typedef void (AL_APIENTRY*LPALGETBUFFER3PTRSOFT)(ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3); -typedef void (AL_APIENTRY*LPALGETBUFFERPTRVSOFT)(ALuint buffer, ALenum param, ALvoid **values); +typedef ALsizei (AL_APIENTRY*ALBUFFERCALLBACKTYPESOFT)(ALvoid *userptr, ALvoid *sampledata, ALsizei numbytes) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALBUFFERCALLBACKSOFT)(ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETBUFFERPTRSOFT)(ALuint buffer, ALenum param, ALvoid **value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETBUFFER3PTRSOFT)(ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETBUFFERPTRVSOFT)(ALuint buffer, ALenum param, ALvoid **values) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alBufferCallbackSOFT(ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr); -AL_API void AL_APIENTRY alGetBufferPtrSOFT(ALuint buffer, ALenum param, ALvoid **ptr); -AL_API void AL_APIENTRY alGetBuffer3PtrSOFT(ALuint buffer, ALenum param, ALvoid **ptr0, ALvoid **ptr1, ALvoid **ptr2); -AL_API void AL_APIENTRY alGetBufferPtrvSOFT(ALuint buffer, ALenum param, ALvoid **ptr); +AL_API void AL_APIENTRY alBufferCallbackSOFT(ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBufferPtrSOFT(ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBuffer3PtrSOFT(ALuint buffer, ALenum param, ALvoid **ptr0, ALvoid **ptr1, ALvoid **ptr2) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBufferPtrvSOFT(ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT; #endif #endif @@ -640,11 +639,98 @@ AL_API void AL_APIENTRY alGetBufferPtrvSOFT(ALuint buffer, ALenum param, ALvoid #ifndef AL_SOFT_source_start_delay #define AL_SOFT_source_start_delay -typedef void (AL_APIENTRY*LPALSOURCEPLAYATTIMESOFT)(ALuint source, ALint64SOFT start_time); -typedef void (AL_APIENTRY*LPALSOURCEPLAYATTIMEVSOFT)(ALsizei n, const ALuint *sources, ALint64SOFT start_time); +typedef void (AL_APIENTRY*LPALSOURCEPLAYATTIMESOFT)(ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALSOURCEPLAYATTIMEVSOFT)(ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -void AL_APIENTRY alSourcePlayAtTimeSOFT(ALuint source, ALint64SOFT start_time); -void AL_APIENTRY alSourcePlayAtTimevSOFT(ALsizei n, const ALuint *sources, ALint64SOFT start_time); +void AL_APIENTRY alSourcePlayAtTimeSOFT(ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePlayAtTimevSOFT(ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT; +#endif +#endif + +#ifndef ALC_EXT_debug +#define ALC_EXT_debug +#define ALC_CONTEXT_FLAGS_EXT 0x19CF +#define ALC_CONTEXT_DEBUG_BIT_EXT 0x0001 +#endif + +#ifndef AL_EXT_debug +#define AL_EXT_debug +#define AL_DONT_CARE_EXT 0x0002 +#define AL_DEBUG_OUTPUT_EXT 0x19B2 +#define AL_DEBUG_CALLBACK_FUNCTION_EXT 0x19B3 +#define AL_DEBUG_CALLBACK_USER_PARAM_EXT 0x19B4 +#define AL_DEBUG_SOURCE_API_EXT 0x19B5 +#define AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT 0x19B6 +#define AL_DEBUG_SOURCE_THIRD_PARTY_EXT 0x19B7 +#define AL_DEBUG_SOURCE_APPLICATION_EXT 0x19B8 +#define AL_DEBUG_SOURCE_OTHER_EXT 0x19B9 +#define AL_DEBUG_TYPE_ERROR_EXT 0x19BA +#define AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT 0x19BB +#define AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT 0x19BC +#define AL_DEBUG_TYPE_PORTABILITY_EXT 0x19BD +#define AL_DEBUG_TYPE_PERFORMANCE_EXT 0x19BE +#define AL_DEBUG_TYPE_MARKER_EXT 0x19BF +#define AL_DEBUG_TYPE_PUSH_GROUP_EXT 0x19C0 +#define AL_DEBUG_TYPE_POP_GROUP_EXT 0x19C1 +#define AL_DEBUG_TYPE_OTHER_EXT 0x19C2 +#define AL_DEBUG_SEVERITY_HIGH_EXT 0x19C3 +#define AL_DEBUG_SEVERITY_MEDIUM_EXT 0x19C4 +#define AL_DEBUG_SEVERITY_LOW_EXT 0x19C5 +#define AL_DEBUG_SEVERITY_NOTIFICATION_EXT 0x19C6 +#define AL_DEBUG_LOGGED_MESSAGES_EXT 0x19C7 +#define AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT 0x19C8 +#define AL_MAX_DEBUG_MESSAGE_LENGTH_EXT 0x19C9 +#define AL_MAX_DEBUG_LOGGED_MESSAGES_EXT 0x19CA +#define AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT 0x19CB +#define AL_MAX_LABEL_LENGTH_EXT 0x19CC +#define AL_STACK_OVERFLOW_EXT 0x19CD +#define AL_STACK_UNDERFLOW_EXT 0x19CE +#define AL_CONTEXT_FLAGS_EXT 0x19CF +#define AL_BUFFER_EXT 0x1009 /* Same as AL_BUFFER */ +#define AL_SOURCE_EXT 0x19D0 +#define AL_FILTER_EXT 0x19D1 +#define AL_EFFECT_EXT 0x19D2 +#define AL_AUXILIARY_EFFECT_SLOT_EXT 0x19D3 + +typedef void (AL_APIENTRY*ALDEBUGPROCEXT)(ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message, void *userParam) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALDEBUGMESSAGECALLBACKEXT)(ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALDEBUGMESSAGEINSERTEXT)(ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALDEBUGMESSAGECONTROLEXT)(ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALPUSHDEBUGGROUPEXT)(ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALPOPDEBUGGROUPEXT)(void) AL_API_NOEXCEPT17; +typedef ALuint (AL_APIENTRY*LPALGETDEBUGMESSAGELOGEXT)(ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALOBJECTLABELEXT)(ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETOBJECTLABELEXT)(ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT17; +#ifdef AL_ALEXT_PROTOTYPES +void AL_APIENTRY alDebugMessageCallbackEXT(ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT; +void AL_APIENTRY alDebugMessageInsertEXT(ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT; +void AL_APIENTRY alDebugMessageControlEXT(ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT; +void AL_APIENTRY alPushDebugGroupEXT(ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT; +void AL_APIENTRY alPopDebugGroupEXT(void) AL_API_NOEXCEPT; +ALuint AL_APIENTRY alGetDebugMessageLogEXT(ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT; +void AL_APIENTRY alObjectLabelEXT(ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT; +void AL_APIENTRY alGetObjectLabelEXT(ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT; +#endif +#endif + +#ifndef ALC_SOFT_system_events +#define ALC_SOFT_system_events +#define ALC_PLAYBACK_DEVICE_SOFT 0x19D4 +#define ALC_CAPTURE_DEVICE_SOFT 0x19D5 +#define ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT 0x19D6 +#define ALC_EVENT_TYPE_DEVICE_ADDED_SOFT 0x19D7 +#define ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT 0x19D8 +#define ALC_EVENT_SUPPORTED_SOFT 0x19D9 +#define ALC_EVENT_NOT_SUPPORTED_SOFT 0x19DA +typedef void (ALC_APIENTRY*ALCEVENTPROCTYPESOFT)(ALCenum eventType, ALCenum deviceType, + ALCdevice *device, ALCsizei length, const ALCchar *message, void *userParam) ALC_API_NOEXCEPT17; +typedef ALCenum (ALC_APIENTRY*LPALCEVENTISSUPPORTEDSOFT)(ALCenum eventType, ALCenum deviceType) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY*LPALCEVENTCONTROLSOFT)(ALCsizei count, const ALCenum *events, ALCboolean enable) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY*LPALCEVENTCALLBACKSOFT)(ALCEVENTPROCTYPESOFT callback, void *userParam) ALC_API_NOEXCEPT17; +#ifdef AL_ALEXT_PROTOTYPES +ALCenum ALC_APIENTRY alcEventIsSupportedSOFT(ALCenum eventType, ALCenum deviceType) ALC_API_NOEXCEPT; +ALCboolean ALC_APIENTRY alcEventControlSOFT(ALCsizei count, const ALCenum *events, ALCboolean enable) ALC_API_NOEXCEPT; +void ALC_APIENTRY alcEventCallbackSOFT(ALCEVENTPROCTYPESOFT callback, void *userParam) ALC_API_NOEXCEPT; #endif #endif diff --git a/include/AL/efx.h b/include/AL/efx.h index 5ab64a64..f24222c3 100644 --- a/include/AL/efx.h +++ b/include/AL/efx.h @@ -204,80 +204,80 @@ extern "C" { /* Effect object function types. */ -typedef void (AL_APIENTRY *LPALGENEFFECTS)(ALsizei, ALuint*); -typedef void (AL_APIENTRY *LPALDELETEEFFECTS)(ALsizei, const ALuint*); -typedef ALboolean (AL_APIENTRY *LPALISEFFECT)(ALuint); -typedef void (AL_APIENTRY *LPALEFFECTI)(ALuint, ALenum, ALint); -typedef void (AL_APIENTRY *LPALEFFECTIV)(ALuint, ALenum, const ALint*); -typedef void (AL_APIENTRY *LPALEFFECTF)(ALuint, ALenum, ALfloat); -typedef void (AL_APIENTRY *LPALEFFECTFV)(ALuint, ALenum, const ALfloat*); -typedef void (AL_APIENTRY *LPALGETEFFECTI)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETEFFECTIV)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETEFFECTF)(ALuint, ALenum, ALfloat*); -typedef void (AL_APIENTRY *LPALGETEFFECTFV)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGENEFFECTS)(ALsizei, ALuint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEEFFECTS)(ALsizei, const ALuint*) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISEFFECT)(ALuint) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTI)(ALuint, ALenum, ALint) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTIV)(ALuint, ALenum, const ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTF)(ALuint, ALenum, ALfloat) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTFV)(ALuint, ALenum, const ALfloat*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTI)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTIV)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTF)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTFV)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17; /* Filter object function types. */ -typedef void (AL_APIENTRY *LPALGENFILTERS)(ALsizei, ALuint*); -typedef void (AL_APIENTRY *LPALDELETEFILTERS)(ALsizei, const ALuint*); -typedef ALboolean (AL_APIENTRY *LPALISFILTER)(ALuint); -typedef void (AL_APIENTRY *LPALFILTERI)(ALuint, ALenum, ALint); -typedef void (AL_APIENTRY *LPALFILTERIV)(ALuint, ALenum, const ALint*); -typedef void (AL_APIENTRY *LPALFILTERF)(ALuint, ALenum, ALfloat); -typedef void (AL_APIENTRY *LPALFILTERFV)(ALuint, ALenum, const ALfloat*); -typedef void (AL_APIENTRY *LPALGETFILTERI)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETFILTERIV)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETFILTERF)(ALuint, ALenum, ALfloat*); -typedef void (AL_APIENTRY *LPALGETFILTERFV)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGENFILTERS)(ALsizei, ALuint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEFILTERS)(ALsizei, const ALuint*) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISFILTER)(ALuint) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERI)(ALuint, ALenum, ALint) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERIV)(ALuint, ALenum, const ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERF)(ALuint, ALenum, ALfloat) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERFV)(ALuint, ALenum, const ALfloat*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERI)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERIV)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERF)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERFV)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17; /* Auxiliary Effect Slot object function types. */ -typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*); -typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTS)(ALsizei, const ALuint*); -typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOT)(ALuint); -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint); -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, const ALint*); -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat); -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, const ALfloat*); -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat*); -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTS)(ALsizei, const ALuint*) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOT)(ALuint) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, const ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, const ALfloat*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects); -AL_API void AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects); -AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect); -AL_API void AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue); -AL_API void AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *piValues); -AL_API void AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue); -AL_API void AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *pflValues); -AL_API void AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue); -AL_API void AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues); -AL_API void AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue); -AL_API void AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues); - -AL_API void AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters); -AL_API void AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters); -AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter); -AL_API void AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue); -AL_API void AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *piValues); -AL_API void AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue); -AL_API void AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *pflValues); -AL_API void AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue); -AL_API void AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues); -AL_API void AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue); -AL_API void AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues); - -AL_API void AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots); -AL_API void AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots); -AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot); -AL_API void AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue); -AL_API void AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *piValues); -AL_API void AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue); -AL_API void AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *pflValues); -AL_API void AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue); -AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues); -AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue); -AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues); +AL_API void AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects) AL_API_NOEXCEPT; +AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; + +AL_API void AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters) AL_API_NOEXCEPT; +AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; + +AL_API void AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots) AL_API_NOEXCEPT; +AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; #endif /* Filter ranges and defaults. */ diff --git a/router/al.cpp b/router/al.cpp index 06c314eb..6ed8a626 100644 --- a/router/al.cpp +++ b/router/al.cpp @@ -7,33 +7,31 @@ #include "router.h" -std::atomic<DriverIface*> CurrentCtxDriver{nullptr}; - -#define DECL_THUNK1(R,n,T1) AL_API R AL_APIENTRY n(T1 a) \ +#define DECL_THUNK1(R,n,T1) AL_API R AL_APIENTRY n(T1 a) noexcept \ { \ DriverIface *iface = GetThreadDriver(); \ if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \ return iface->n(a); \ } -#define DECL_THUNK2(R,n,T1,T2) AL_API R AL_APIENTRY n(T1 a, T2 b) \ +#define DECL_THUNK2(R,n,T1,T2) AL_API R AL_APIENTRY n(T1 a, T2 b) noexcept \ { \ DriverIface *iface = GetThreadDriver(); \ if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \ return iface->n(a, b); \ } -#define DECL_THUNK3(R,n,T1,T2,T3) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c) \ +#define DECL_THUNK3(R,n,T1,T2,T3) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c) noexcept \ { \ DriverIface *iface = GetThreadDriver(); \ if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \ return iface->n(a, b, c); \ } -#define DECL_THUNK4(R,n,T1,T2,T3,T4) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c, T4 d) \ +#define DECL_THUNK4(R,n,T1,T2,T3,T4) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c, T4 d) noexcept \ { \ DriverIface *iface = GetThreadDriver(); \ if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \ return iface->n(a, b, c, d); \ } -#define DECL_THUNK5(R,n,T1,T2,T3,T4,T5) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c, T4 d, T5 e) \ +#define DECL_THUNK5(R,n,T1,T2,T3,T4,T5) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c, T4 d, T5 e) noexcept \ { \ DriverIface *iface = GetThreadDriver(); \ if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \ @@ -44,7 +42,7 @@ std::atomic<DriverIface*> CurrentCtxDriver{nullptr}; /* Ugly hack for some apps calling alGetError without a current context, and * expecting it to be AL_NO_ERROR. */ -AL_API ALenum AL_APIENTRY alGetError(void) +AL_API ALenum AL_APIENTRY alGetError(void) noexcept { DriverIface *iface = GetThreadDriver(); if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); diff --git a/router/alc.cpp b/router/alc.cpp index 3aa3382b..fe9faa45 100644 --- a/router/alc.cpp +++ b/router/alc.cpp @@ -394,7 +394,7 @@ static void InitCtxFuncs(DriverIface &iface) } -ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename) +ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename) noexcept { ALCdevice *device = nullptr; ALint idx = 0; @@ -459,7 +459,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename) return device; } -ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) +ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) noexcept { ALint idx; @@ -475,7 +475,7 @@ ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) } -ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrlist) +ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrlist) noexcept { ALCcontext *context; ALint idx; @@ -498,7 +498,7 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin return context; } -ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) +ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) noexcept { ALint idx = -1; @@ -542,7 +542,7 @@ ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) return ALC_TRUE; } -ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context) +ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context) noexcept { if(context) { @@ -553,7 +553,7 @@ ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context) LastError.store(ALC_INVALID_CONTEXT); } -ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context) +ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context) noexcept { if(context) { @@ -564,7 +564,7 @@ ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context) LastError.store(ALC_INVALID_CONTEXT); } -ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) +ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) noexcept { ALint idx; @@ -578,14 +578,14 @@ ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) ContextIfaceMap.removeByKey(context); } -ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) +ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) noexcept { DriverIface *iface = GetThreadDriver(); if(!iface) iface = CurrentCtxDriver.load(); return iface ? iface->alcGetCurrentContext() : nullptr; } -ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context) +ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context) noexcept { if(context) { @@ -598,7 +598,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context) } -ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device) +ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device) noexcept { if(device) { @@ -609,7 +609,7 @@ ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device) return LastError.exchange(ALC_NO_ERROR); } -ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname) +ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname) noexcept { const char *ptr; size_t len; @@ -641,7 +641,7 @@ ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const A return ALC_FALSE; } -ALC_API void* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname) +ALC_API void* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname) noexcept { if(device) { @@ -661,7 +661,7 @@ ALC_API void* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *f return (iter != alcFunctions.cend()) ? iter->address : nullptr; } -ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname) +ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname) noexcept { if(device) { @@ -681,7 +681,7 @@ ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *e return (iter != alcEnumerations.cend()) ? iter->value : 0; } -ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param) +ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param) noexcept { if(device) { @@ -817,7 +817,7 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum para return nullptr; } -ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) +ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) noexcept { if(device) { @@ -872,7 +872,7 @@ ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsi } -ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize) +ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize) noexcept { ALCdevice *device = nullptr; ALint idx = 0; @@ -924,7 +924,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, return device; } -ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) +ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) noexcept { ALint idx; @@ -939,7 +939,7 @@ ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) return ALC_TRUE; } -ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) +ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) noexcept { if(device) { @@ -950,7 +950,7 @@ ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) LastError.store(ALC_INVALID_DEVICE); } -ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) +ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) noexcept { if(device) { @@ -961,7 +961,7 @@ ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) LastError.store(ALC_INVALID_DEVICE); } -ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) +ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) noexcept { if(device) { @@ -973,7 +973,7 @@ ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, } -ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context) +ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context) noexcept { ALCenum err = ALC_INVALID_CONTEXT; ALint idx; @@ -1009,7 +1009,7 @@ ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context) return ALC_FALSE; } -ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) +ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) noexcept { DriverIface *iface = GetThreadDriver(); if(iface) return iface->alcGetThreadContext(); diff --git a/router/router.cpp b/router/router.cpp index 3c891053..e2ba91d1 100644 --- a/router/router.cpp +++ b/router/router.cpp @@ -17,18 +17,9 @@ #include "version.h" -std::vector<DriverIfacePtr> DriverList; - -thread_local DriverIface *ThreadCtxDriver; - enum LogLevel LogLevel = LogLevel_Error; FILE *LogFile; -#ifdef __MINGW32__ -DriverIface *GetThreadDriver() noexcept { return ThreadCtxDriver; } -void SetThreadDriver(DriverIface *driver) noexcept { ThreadCtxDriver = driver; } -#endif - static void LoadDriverList(void); @@ -328,13 +319,12 @@ void LoadDriverList(void) WCHAR cwd_path[MAX_PATH+1] = L""; WCHAR proc_path[MAX_PATH+1] = L""; WCHAR sys_path[MAX_PATH+1] = L""; - int len; if(GetLoadedModuleDirectory(L"OpenAL32.dll", dll_path, MAX_PATH)) TRACE("Got DLL path %ls\n", dll_path); GetCurrentDirectoryW(MAX_PATH, cwd_path); - len = lstrlenW(cwd_path); + auto len = wcslen(cwd_path); if(len > 0 && (cwd_path[len-1] == '\\' || cwd_path[len-1] == '/')) cwd_path[len-1] = '\0'; TRACE("Got current working directory %ls\n", cwd_path); @@ -343,7 +333,7 @@ void LoadDriverList(void) TRACE("Got proc path %ls\n", proc_path); GetSystemDirectoryW(sys_path, MAX_PATH); - len = lstrlenW(sys_path); + len = wcslen(sys_path); if(len > 0 && (sys_path[len-1] == '\\' || sys_path[len-1] == '/')) sys_path[len-1] = '\0'; TRACE("Got system path %ls\n", sys_path); diff --git a/router/router.h b/router/router.h index 2a126d42..f5c7f455 100644 --- a/router/router.h +++ b/router/router.h @@ -173,21 +173,13 @@ struct DriverIface { }; using DriverIfacePtr = std::unique_ptr<DriverIface>; -extern std::vector<DriverIfacePtr> DriverList; - -extern thread_local DriverIface *ThreadCtxDriver; -extern std::atomic<DriverIface*> CurrentCtxDriver; - -/* HACK: MinGW generates bad code when accessing an extern thread_local object. - * Add a wrapper function for it that only accesses it where it's defined. - */ -#ifdef __MINGW32__ -DriverIface *GetThreadDriver() noexcept; -void SetThreadDriver(DriverIface *driver) noexcept; -#else +inline std::vector<DriverIfacePtr> DriverList; + +inline thread_local DriverIface *ThreadCtxDriver{}; +inline std::atomic<DriverIface*> CurrentCtxDriver{}; + inline DriverIface *GetThreadDriver() noexcept { return ThreadCtxDriver; } inline void SetThreadDriver(DriverIface *driver) noexcept { ThreadCtxDriver = driver; } -#endif class PtrIntMap { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 00000000..492587e8 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,24 @@ +add_executable(OpenAL_Tests)
+
+include(FetchContent)
+FetchContent_Declare(
+ googletest
+ GIT_REPOSITORY https://github.com/google/googletest.git
+ GIT_TAG main
+)
+# For Windows: Prevent overriding the parent project's compiler/linker settings
+set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+FetchContent_MakeAvailable(googletest)
+
+target_link_libraries(OpenAL_Tests PRIVATE
+ OpenAL
+ GTest::gtest_main
+)
+
+target_sources(OpenAL_Tests PRIVATE
+example.t.cpp
+)
+
+# This needs to come last
+include(GoogleTest)
+gtest_discover_tests(OpenAL_Tests)
diff --git a/tests/example.t.cpp b/tests/example.t.cpp new file mode 100644 index 00000000..30341e8b --- /dev/null +++ b/tests/example.t.cpp @@ -0,0 +1,16 @@ +#include <gtest/gtest.h>
+#include <AL/al.h>
+
+class ExampleTest : public ::testing::Test {
+};
+
+
+TEST_F(ExampleTest, Basic)
+{
+ // just making sure we compile
+ ALuint source, buffer;
+ ALfloat offset;
+ ALenum state;
+}
+
+
diff --git a/utils/makemhr/loaddef.cpp b/utils/makemhr/loaddef.cpp index e8092363..c8a98511 100644 --- a/utils/makemhr/loaddef.cpp +++ b/utils/makemhr/loaddef.cpp @@ -33,11 +33,10 @@ #include <iterator> #include <limits> #include <memory> -#include <cstdarg> +#include <optional> #include <vector> #include "alfstream.h" -#include "aloptional.h" #include "alspan.h" #include "alstring.h" #include "makemhr.h" @@ -1451,7 +1450,7 @@ static int ProcessMetrics(TokenReaderT *tr, const uint fftSize, const uint trunc } if(hData->mChannelType == CT_NONE) hData->mChannelType = CT_MONO; - const auto azs = al::as_span(azCounts).first<MAX_FD_COUNT>(); + const auto azs = al::span{azCounts}.first<MAX_FD_COUNT>(); if(!PrepareHrirData({distances, fdCount}, evCounts, azs, hData)) { fprintf(stderr, "Error: Out of memory.\n"); @@ -1755,7 +1754,7 @@ static int ProcessSources(TokenReaderT *tr, HrirDataT *hData, const uint outRate PPhaseResampler onsetResampler; onsetResampler.init(hData->mIrRate, OnsetRateMultiple*hData->mIrRate); - al::optional<PPhaseResampler> resampler; + std::optional<PPhaseResampler> resampler; if(outRate && outRate != hData->mIrRate) resampler.emplace().init(hData->mIrRate, outRate); const double rateScale{outRate ? static_cast<double>(outRate) / hData->mIrRate : 1.0}; diff --git a/utils/makemhr/loadsofa.cpp b/utils/makemhr/loadsofa.cpp index dcb0a35e..9bcfc38d 100644 --- a/utils/makemhr/loadsofa.cpp +++ b/utils/makemhr/loadsofa.cpp @@ -33,11 +33,11 @@ #include <iterator> #include <memory> #include <numeric> +#include <optional> #include <string> #include <thread> #include <vector> -#include "aloptional.h" #include "alspan.h" #include "makemhr.h" #include "polyphase_resampler.h" @@ -87,7 +87,7 @@ static bool PrepareLayout(const uint m, const float *xyzs, HrirDataT *hData) ++fi; } fprintf(stdout, "Using %u of %u IRs.\n", ir_total, m); - const auto azs = al::as_span(azCounts).first<MAX_FD_COUNT>(); + const auto azs = al::span{azCounts}.first<MAX_FD_COUNT>(); return PrepareHrirData({distances, fi}, evCounts, azs, hData); } @@ -265,7 +265,7 @@ static bool LoadResponses(MYSOFA_HRTF *sofaHrtf, HrirDataT *hData, const DelayTy double *hrirs = hData->mHrirsBase.data(); std::unique_ptr<double[]> restmp; - al::optional<PPhaseResampler> resampler; + std::optional<PPhaseResampler> resampler; if(outRate && outRate != hData->mIrRate) { resampler.emplace().init(hData->mIrRate, outRate); diff --git a/utils/makemhr/makemhr.cpp b/utils/makemhr/makemhr.cpp index ae301dc3..98e1b73f 100644 --- a/utils/makemhr/makemhr.cpp +++ b/utils/makemhr/makemhr.cpp @@ -297,10 +297,7 @@ static void MinimumPhase(const uint n, double *mags, complex_d *out) // Remove any DC offset the filter has. mags[0] = EPSILON; for(i = 0;i < n;i++) - { - auto a = std::exp(complex_d{0.0, out[i].imag()}); - out[i] = a * mags[i]; - } + out[i] = std::polar(mags[i], out[i].imag()); } diff --git a/utils/makemhr/makemhr.h b/utils/makemhr/makemhr.h index 13b5b2d1..aa18134d 100644 --- a/utils/makemhr/makemhr.h +++ b/utils/makemhr/makemhr.h @@ -113,12 +113,12 @@ void MagnitudeResponse(const uint n, const complex_d *in, double *out); // Performs a forward FFT. inline void FftForward(const uint n, complex_d *inout) -{ forward_fft(al::as_span(inout, n)); } +{ forward_fft(al::span{inout, n}); } // Performs an inverse FFT. inline void FftInverse(const uint n, complex_d *inout) { - inverse_fft(al::as_span(inout, n)); + inverse_fft(al::span{inout, n}); double f{1.0 / n}; for(uint i{0};i < n;i++) inout[i] *= f; diff --git a/utils/openal-info.c b/utils/openal-info.c index b646693c..8ef6ebde 100644 --- a/utils/openal-info.c +++ b/utils/openal-info.c @@ -167,11 +167,11 @@ static void printHRTFInfo(ALCdevice *device) alcGetIntegerv(device, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtfs); if(!num_hrtfs) - printf("No HRTFs found\n"); + printf("No HRTF profiles found\n"); else { ALCint i; - printf("Available HRTFs:\n"); + printf("Available HRTF profiles:\n"); for(i = 0;i < num_hrtfs;++i) { const ALCchar *name = alcGetStringiSOFT(device, ALC_HRTF_SPECIFIER_SOFT, i); @@ -213,6 +213,65 @@ static void printModeInfo(ALCdevice *device) alcGetIntegerv(device, ALC_FREQUENCY, 1, &srate); if(checkALCErrors(device) == ALC_NO_ERROR) printf("Device sample rate: %dhz\n", srate); + + if(alcIsExtensionPresent(device, "ALC_SOFT_HRTF")) + { + const ALCchar *hrtfname = "(disabled)"; + ALCint isenabled = 0; + + alcGetIntegerv(device, ALC_HRTF_SOFT, 1, &isenabled); + checkALCErrors(device); + if(isenabled == ALC_TRUE) + { + hrtfname = alcGetString(device, ALC_HRTF_SPECIFIER_SOFT); + checkALCErrors(device); + } + printf("Device HRTF profile: %s\n", hrtfname ? hrtfname : "<null>"); + } +} + +static void printALCSOFTSystemEventIsSupportedResult(LPALCEVENTISSUPPORTEDSOFT alcEventIsSupportedSOFT, ALCenum eventType, ALCenum deviceType) +{ + if (alcEventIsSupportedSOFT == NULL) + { + printf("ERROR (alcEventIsSupportedSOFT missing)\n"); + return; + } + ALCenum supported = alcEventIsSupportedSOFT(eventType, deviceType); + if (supported == ALC_EVENT_SUPPORTED_SOFT) + { + printf("SUPPORTED\n"); + } + else if (supported == ALC_EVENT_NOT_SUPPORTED_SOFT) + { + printf("NOT SUPPORTED\n"); + } + else + { + printf("UNEXPECTED VALUE : %d\n", supported); + } +} + +static void printALC_SOFT_system_event(void) +{ + if(alcIsExtensionPresent(NULL, "ALC_SOFT_system_events")) + { + LPALCEVENTISSUPPORTEDSOFT alcEventIsSupportedSOFT; + alcEventIsSupportedSOFT = FUNCTION_CAST(LPALCEVENTISSUPPORTEDSOFT, alGetProcAddress("alcEventIsSupportedSOFT")); + printf("Available events:\n"); + printf(" ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT for ALC_PLAYBACK_DEVICE_SOFT - "); + printALCSOFTSystemEventIsSupportedResult(alcEventIsSupportedSOFT, ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT, ALC_PLAYBACK_DEVICE_SOFT); + printf(" ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT for ALC_CAPTURE_DEVICE_SOFT - "); + printALCSOFTSystemEventIsSupportedResult(alcEventIsSupportedSOFT, ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT, ALC_CAPTURE_DEVICE_SOFT); + printf(" ALC_EVENT_TYPE_DEVICE_ADDED_SOFT for ALC_PLAYBACK_DEVICE_SOFT - "); + printALCSOFTSystemEventIsSupportedResult(alcEventIsSupportedSOFT, ALC_EVENT_TYPE_DEVICE_ADDED_SOFT, ALC_PLAYBACK_DEVICE_SOFT); + printf(" ALC_EVENT_TYPE_DEVICE_ADDED_SOFT for ALC_CAPTURE_DEVICE_SOFT - "); + printALCSOFTSystemEventIsSupportedResult(alcEventIsSupportedSOFT, ALC_EVENT_TYPE_DEVICE_ADDED_SOFT, ALC_CAPTURE_DEVICE_SOFT); + printf(" ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT for ALC_PLAYBACK_DEVICE_SOFT - "); + printALCSOFTSystemEventIsSupportedResult(alcEventIsSupportedSOFT, ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT, ALC_PLAYBACK_DEVICE_SOFT); + printf(" ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT for ALC_CAPTURE_DEVICE_SOFT - "); + printALCSOFTSystemEventIsSupportedResult(alcEventIsSupportedSOFT, ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT, ALC_CAPTURE_DEVICE_SOFT); + } } static void printALInfo(void) @@ -420,6 +479,7 @@ int main(int argc, char *argv[]) } printALCInfo(device); printHRTFInfo(device); + printALC_SOFT_system_event(); context = alcCreateContext(device, NULL); if(!context || alcMakeContextCurrent(context) == ALC_FALSE) diff --git a/utils/uhjdecoder.cpp b/utils/uhjdecoder.cpp index 6d992e30..c7efa376 100644 --- a/utils/uhjdecoder.cpp +++ b/utils/uhjdecoder.cpp @@ -26,15 +26,14 @@ #include <array> #include <complex> +#include <cstddef> #include <cstring> #include <memory> -#include <stddef.h> #include <string> #include <utility> #include <vector> #include "albit.h" -#include "albyte.h" #include "alcomplex.h" #include "almalloc.h" #include "alnumbers.h" @@ -64,7 +63,7 @@ using ushort = unsigned short; using uint = unsigned int; using complex_d = std::complex<double>; -using byte4 = std::array<al::byte,4>; +using byte4 = std::array<std::byte,4>; constexpr ubyte SUBTYPE_BFORMAT_FLOAT[]{ @@ -113,7 +112,7 @@ using FloatBufferSpan = al::span<float,BufferLineSize>; struct UhjDecoder { - constexpr static size_t sFilterDelay{1024}; + constexpr static std::size_t sFilterDelay{1024}; alignas(16) std::array<float,BufferLineSize+sFilterDelay> mS{}; alignas(16) std::array<float,BufferLineSize+sFilterDelay> mD{}; @@ -126,10 +125,10 @@ struct UhjDecoder { alignas(16) std::array<float,BufferLineSize + sFilterDelay*2> mTemp{}; - void decode(const float *RESTRICT InSamples, const size_t InChannels, - const al::span<FloatBufferLine> OutSamples, const size_t SamplesToDo); + void decode(const float *RESTRICT InSamples, const std::size_t InChannels, + const al::span<FloatBufferLine> OutSamples, const std::size_t SamplesToDo); void decode2(const float *RESTRICT InSamples, const al::span<FloatBufferLine> OutSamples, - const size_t SamplesToDo); + const std::size_t SamplesToDo); DEF_NEWDEL(UhjDecoder) }; @@ -210,8 +209,8 @@ const PhaseShifterT<UhjDecoder::sFilterDelay*2> PShift{}; * * Not halving produces a result matching the original input. */ -void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels, - const al::span<FloatBufferLine> OutSamples, const size_t SamplesToDo) +void UhjDecoder::decode(const float *RESTRICT InSamples, const std::size_t InChannels, + const al::span<FloatBufferLine> OutSamples, const std::size_t SamplesToDo) { ASSUME(SamplesToDo > 0); @@ -224,23 +223,23 @@ void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels */ /* S = Left + Right */ - for(size_t i{0};i < SamplesToDo;++i) + for(std::size_t i{0};i < SamplesToDo;++i) mS[sFilterDelay+i] = InSamples[i*InChannels + 0] + InSamples[i*InChannels + 1]; /* D = Left - Right */ - for(size_t i{0};i < SamplesToDo;++i) + for(std::size_t i{0};i < SamplesToDo;++i) mD[sFilterDelay+i] = InSamples[i*InChannels + 0] - InSamples[i*InChannels + 1]; if(InChannels > 2) { /* T */ - for(size_t i{0};i < SamplesToDo;++i) + for(std::size_t i{0};i < SamplesToDo;++i) mT[sFilterDelay+i] = InSamples[i*InChannels + 2]; } if(InChannels > 3) { /* Q */ - for(size_t i{0};i < SamplesToDo;++i) + for(std::size_t i{0};i < SamplesToDo;++i) mQ[sFilterDelay+i] = InSamples[i*InChannels + 3]; } @@ -251,7 +250,7 @@ void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels std::copy_n(mTemp.cbegin()+SamplesToDo, mDTHistory.size(), mDTHistory.begin()); PShift.process({xoutput, SamplesToDo}, mTemp.data()); - for(size_t i{0};i < SamplesToDo;++i) + for(std::size_t i{0};i < SamplesToDo;++i) { /* W = 0.981532*S + 0.197484*j(0.828331*D + 0.767820*T) */ woutput[i] = 0.981532f*mS[i] + 0.197484f*xoutput[i]; @@ -265,7 +264,7 @@ void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels std::copy_n(mTemp.cbegin()+SamplesToDo, mSHistory.size(), mSHistory.begin()); PShift.process({youtput, SamplesToDo}, mTemp.data()); - for(size_t i{0};i < SamplesToDo;++i) + for(std::size_t i{0};i < SamplesToDo;++i) { /* Y = 0.795968*D - 0.676392*T + j(0.186633*S) */ youtput[i] = 0.795968f*mD[i] - 0.676392f*mT[i] + 0.186633f*youtput[i]; @@ -275,7 +274,7 @@ void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels { float *zoutput{OutSamples[3].data()}; /* Z = 1.023332*Q */ - for(size_t i{0};i < SamplesToDo;++i) + for(std::size_t i{0};i < SamplesToDo;++i) zoutput[i] = 1.023332f*mQ[i]; } @@ -305,7 +304,7 @@ void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels * halving here is merely a -6dB reduction in output, but it's still incorrect. */ void UhjDecoder::decode2(const float *RESTRICT InSamples, - const al::span<FloatBufferLine> OutSamples, const size_t SamplesToDo) + const al::span<FloatBufferLine> OutSamples, const std::size_t SamplesToDo) { ASSUME(SamplesToDo > 0); @@ -314,11 +313,11 @@ void UhjDecoder::decode2(const float *RESTRICT InSamples, float *youtput{OutSamples[2].data()}; /* S = Left + Right */ - for(size_t i{0};i < SamplesToDo;++i) + for(std::size_t i{0};i < SamplesToDo;++i) mS[sFilterDelay+i] = InSamples[i*2 + 0] + InSamples[i*2 + 1]; /* D = Left - Right */ - for(size_t i{0};i < SamplesToDo;++i) + for(std::size_t i{0};i < SamplesToDo;++i) mD[sFilterDelay+i] = InSamples[i*2 + 0] - InSamples[i*2 + 1]; /* Precompute j*D and store in xoutput. */ @@ -327,7 +326,7 @@ void UhjDecoder::decode2(const float *RESTRICT InSamples, std::copy_n(mTemp.cbegin()+SamplesToDo, mDTHistory.size(), mDTHistory.begin()); PShift.process({xoutput, SamplesToDo}, mTemp.data()); - for(size_t i{0};i < SamplesToDo;++i) + for(std::size_t i{0};i < SamplesToDo;++i) { /* W = 0.981530*S + j*0.163585*D */ woutput[i] = 0.981530f*mS[i] + 0.163585f*xoutput[i]; @@ -341,7 +340,7 @@ void UhjDecoder::decode2(const float *RESTRICT InSamples, std::copy_n(mTemp.cbegin()+SamplesToDo, mSHistory.size(), mSHistory.begin()); PShift.process({youtput, SamplesToDo}, mTemp.data()); - for(size_t i{0};i < SamplesToDo;++i) + for(std::size_t i{0};i < SamplesToDo;++i) { /* Y = 0.762956*D + j*0.384230*S */ youtput[i] = 0.762956f*mD[i] + 0.384230f*youtput[i]; @@ -368,7 +367,7 @@ int main(int argc, char **argv) return 1; } - size_t num_files{0}, num_decoded{0}; + std::size_t num_files{0}, num_decoded{0}; bool use_general{true}; for(int fidx{1};fidx < argc;++fidx) { @@ -473,7 +472,7 @@ int main(int argc, char **argv) * be fed through the decoder after reaching the end of the input file * to ensure none of the original input is lost. */ - size_t LeadIn{UhjDecoder::sFilterDelay}; + std::size_t LeadIn{UhjDecoder::sFilterDelay}; sf_count_t LeadOut{UhjDecoder::sFilterDelay}; while(LeadOut > 0) { @@ -487,7 +486,7 @@ int main(int argc, char **argv) LeadOut -= remaining; } - auto got = static_cast<size_t>(sgot); + auto got = static_cast<std::size_t>(sgot); if(ininfo.channels > 2 || use_general) decoder->decode(inmem.get(), static_cast<uint>(ininfo.channels), decmem, got); else @@ -499,16 +498,16 @@ int main(int argc, char **argv) } got -= LeadIn; - for(size_t i{0};i < got;++i) + for(std::size_t i{0};i < got;++i) { /* Attenuate by -3dB for FuMa output levels. */ constexpr auto inv_sqrt2 = static_cast<float>(1.0/al::numbers::sqrt2); - for(size_t j{0};j < outchans;++j) + for(std::size_t j{0};j < outchans;++j) outmem[i*outchans + j] = f32AsLEBytes(decmem[j][LeadIn+i] * inv_sqrt2); } LeadIn = 0; - size_t wrote{fwrite(outmem.get(), sizeof(byte4)*outchans, got, outfile.get())}; + std::size_t wrote{fwrite(outmem.get(), sizeof(byte4)*outchans, got, outfile.get())}; if(wrote < got) { fprintf(stderr, "Error writing wave data: %s (%d)\n", strerror(errno), errno); diff --git a/utils/uhjencoder.cpp b/utils/uhjencoder.cpp index 34698993..154a1155 100644 --- a/utils/uhjencoder.cpp +++ b/utils/uhjencoder.cpp @@ -25,8 +25,8 @@ #include "config.h" #include <array> +#include <cinttypes> #include <cstring> -#include <inttypes.h> #include <memory> #include <stddef.h> #include <string> @@ -325,7 +325,7 @@ int main(int argc, char **argv) return false; for(const int id : a) { - if(std::find(b.begin(), b.end(), id) != b.end()) + if(std::find(b.begin(), b.end(), id) == b.end()) return false; } return true; @@ -502,11 +502,9 @@ int main(int argc, char **argv) got -= LeadIn; for(size_t c{0};c < uhjchans;++c) { - constexpr float max_val{8388607.0f / 8388608.0f}; - auto clamp = [](float v, float mn, float mx) noexcept - { return std::min(std::max(v, mn), mx); }; + static constexpr float max_val{8388607.0f / 8388608.0f}; for(size_t i{0};i < got;++i) - outmem[i*uhjchans + c] = clamp(encmem[c][LeadIn+i], -1.0f, max_val); + outmem[i*uhjchans + c] = std::clamp(encmem[c][LeadIn+i], -1.0f, max_val); } LeadIn = 0; |