From 8765f40dfd878e1041f9af593e416290031801a3 Mon Sep 17 00:00:00 2001 From: halx99 Date: Tue, 3 Dec 2024 01:41:28 +0800 Subject: [PATCH] Update openal-soft to 1.24.1 --- 3rdparty/CMakeLists.txt | 4 +- 3rdparty/README.md | 2 +- 3rdparty/openal/CMakeLists.txt | 327 ++- 3rdparty/openal/ChangeLog | 78 + 3rdparty/openal/OpenALConfig.cmake.in | 9 + 3rdparty/openal/README.md | 4 + 3rdparty/openal/al/auxeffectslot.cpp | 812 +++--- 3rdparty/openal/al/auxeffectslot.h | 109 +- 3rdparty/openal/al/buffer.cpp | 1512 +++++------ 3rdparty/openal/al/buffer.h | 33 +- 3rdparty/openal/al/debug.cpp | 293 ++- 3rdparty/openal/al/debug.h | 20 +- 3rdparty/openal/al/direct_defs.h | 76 +- 3rdparty/openal/al/eax/api.h | 59 +- 3rdparty/openal/al/eax/call.cpp | 57 +- 3rdparty/openal/al/eax/call.h | 30 +- 3rdparty/openal/al/eax/effect.h | 299 ++- 3rdparty/openal/al/eax/exception.h | 9 +- 3rdparty/openal/al/eax/fx_slots.h | 14 +- 3rdparty/openal/al/eax/globals.h | 16 +- 3rdparty/openal/al/eax/utils.cpp | 6 +- 3rdparty/openal/al/eax/x_ram.h | 1 - 3rdparty/openal/al/effect.cpp | 626 +++-- 3rdparty/openal/al/effect.h | 50 +- 3rdparty/openal/al/effects/autowah.cpp | 124 +- 3rdparty/openal/al/effects/chorus.cpp | 278 +- 3rdparty/openal/al/effects/compressor.cpp | 74 +- 3rdparty/openal/al/effects/convolution.cpp | 84 +- 3rdparty/openal/al/effects/dedicated.cpp | 94 +- 3rdparty/openal/al/effects/distortion.cpp | 122 +- 3rdparty/openal/al/effects/echo.cpp | 122 +- 3rdparty/openal/al/effects/effects.cpp | 6 - 3rdparty/openal/al/effects/effects.h | 102 +- 3rdparty/openal/al/effects/equalizer.cpp | 177 +- 3rdparty/openal/al/effects/fshifter.cpp | 124 +- 3rdparty/openal/al/effects/modulator.cpp | 140 +- 3rdparty/openal/al/effects/null.cpp | 67 +- 3rdparty/openal/al/effects/pshifter.cpp | 95 +- 3rdparty/openal/al/effects/reverb.cpp | 849 +++--- 3rdparty/openal/al/effects/vmorpher.cpp | 224 +- 3rdparty/openal/al/error.cpp | 95 +- 3rdparty/openal/al/error.h | 27 + 3rdparty/openal/al/event.cpp | 89 +- 3rdparty/openal/al/extension.cpp | 15 +- 3rdparty/openal/al/filter.cpp | 538 ++-- 3rdparty/openal/al/filter.h | 35 +- 3rdparty/openal/al/listener.cpp | 323 ++- 3rdparty/openal/al/listener.h | 4 +- 3rdparty/openal/al/source.cpp | 1836 ++++++------- 3rdparty/openal/al/source.h | 198 +- 3rdparty/openal/al/state.cpp | 350 ++- 3rdparty/openal/alc/alc.cpp | 1847 +++++++------ 3rdparty/openal/alc/alconfig.cpp | 444 ++-- 3rdparty/openal/alc/alconfig.h | 19 +- 3rdparty/openal/alc/alu.cpp | 1163 +++++---- 3rdparty/openal/alc/alu.h | 11 +- 3rdparty/openal/alc/backends/alsa.cpp | 300 ++- 3rdparty/openal/alc/backends/alsa.h | 10 +- 3rdparty/openal/alc/backends/base.cpp | 61 +- 3rdparty/openal/alc/backends/base.h | 51 +- 3rdparty/openal/alc/backends/coreaudio.cpp | 194 +- 3rdparty/openal/alc/backends/coreaudio.h | 12 +- 3rdparty/openal/alc/backends/dsound.cpp | 159 +- 3rdparty/openal/alc/backends/dsound.h | 10 +- 3rdparty/openal/alc/backends/jack.cpp | 276 +- 3rdparty/openal/alc/backends/jack.h | 10 +- 3rdparty/openal/alc/backends/loopback.cpp | 8 +- 3rdparty/openal/alc/backends/loopback.h | 10 +- 3rdparty/openal/alc/backends/null.cpp | 27 +- 3rdparty/openal/alc/backends/null.h | 10 +- 3rdparty/openal/alc/backends/oboe.cpp | 52 +- 3rdparty/openal/alc/backends/oboe.h | 10 +- 3rdparty/openal/alc/backends/opensl.cpp | 181 +- 3rdparty/openal/alc/backends/opensl.h | 10 +- 3rdparty/openal/alc/backends/oss.cpp | 221 +- 3rdparty/openal/alc/backends/oss.h | 10 +- 3rdparty/openal/alc/backends/otherio.cpp | 700 +++++ 3rdparty/openal/alc/backends/otherio.h | 21 + 3rdparty/openal/alc/backends/pipewire.cpp | 514 ++-- 3rdparty/openal/alc/backends/pipewire.h | 14 +- 3rdparty/openal/alc/backends/portaudio.cpp | 371 ++- 3rdparty/openal/alc/backends/portaudio.h | 10 +- 3rdparty/openal/alc/backends/pulseaudio.cpp | 316 +-- 3rdparty/openal/alc/backends/pulseaudio.h | 18 +- 3rdparty/openal/alc/backends/sdl2.cpp | 180 +- 3rdparty/openal/alc/backends/sdl2.h | 10 +- 3rdparty/openal/alc/backends/sndio.cpp | 209 +- 3rdparty/openal/alc/backends/sndio.h | 10 +- 3rdparty/openal/alc/backends/solaris.cpp | 48 +- 3rdparty/openal/alc/backends/solaris.h | 10 +- 3rdparty/openal/alc/backends/wasapi.cpp | 1093 ++++---- 3rdparty/openal/alc/backends/wasapi.h | 12 +- 3rdparty/openal/alc/backends/wave.cpp | 154 +- 3rdparty/openal/alc/backends/wave.h | 10 +- 3rdparty/openal/alc/backends/winmm.cpp | 139 +- 3rdparty/openal/alc/backends/winmm.h | 10 +- 3rdparty/openal/alc/context.cpp | 260 +- 3rdparty/openal/alc/context.h | 135 +- 3rdparty/openal/alc/device.cpp | 31 +- 3rdparty/openal/alc/device.h | 117 +- 3rdparty/openal/alc/effects/autowah.cpp | 102 +- 3rdparty/openal/alc/effects/base.h | 40 +- 3rdparty/openal/alc/effects/chorus.cpp | 206 +- 3rdparty/openal/alc/effects/compressor.cpp | 137 +- 3rdparty/openal/alc/effects/convolution.cpp | 486 ++-- 3rdparty/openal/alc/effects/dedicated.cpp | 57 +- 3rdparty/openal/alc/effects/distortion.cpp | 64 +- 3rdparty/openal/alc/effects/echo.cpp | 71 +- 3rdparty/openal/alc/effects/equalizer.cpp | 59 +- 3rdparty/openal/alc/effects/fshifter.cpp | 68 +- 3rdparty/openal/alc/effects/modulator.cpp | 137 +- 3rdparty/openal/alc/effects/null.cpp | 7 +- 3rdparty/openal/alc/effects/pshifter.cpp | 104 +- 3rdparty/openal/alc/effects/reverb.cpp | 1318 +++++----- 3rdparty/openal/alc/effects/vmorpher.cpp | 172 +- 3rdparty/openal/alc/events.cpp | 26 +- 3rdparty/openal/alc/events.h | 8 + 3rdparty/openal/alc/export_list.h | 108 +- 3rdparty/openal/alc/inprogext.h | 389 +-- 3rdparty/openal/alc/panning.cpp | 574 ++-- 3rdparty/openal/cmake/FindJACK.cmake | 2 +- 3rdparty/openal/cmake/FindSndIO.cmake | 31 + 3rdparty/openal/cmake/FindSoundIO.cmake | 32 - 3rdparty/openal/common/alassert.cpp | 44 + 3rdparty/openal/common/alassert.h | 24 + 3rdparty/openal/common/albit.h | 20 +- 3rdparty/openal/common/alcomplex.cpp | 94 +- 3rdparty/openal/common/alcomplex.h | 15 +- 3rdparty/openal/common/aldeque.h | 16 - 3rdparty/openal/common/alfstream.cpp | 26 - 3rdparty/openal/common/alfstream.h | 45 - 3rdparty/openal/common/almalloc.cpp | 61 - 3rdparty/openal/common/almalloc.h | 231 +- 3rdparty/openal/common/alnumbers.h | 10 +- 3rdparty/openal/common/alnumeric.h | 118 +- 3rdparty/openal/common/alsem.cpp | 5 +- 3rdparty/openal/common/alsem.h | 2 +- 3rdparty/openal/common/alspan.h | 431 +-- 3rdparty/openal/common/alstring.cpp | 55 +- 3rdparty/openal/common/alstring.h | 51 +- 3rdparty/openal/common/althrd_setname.cpp | 7 +- 3rdparty/openal/common/althreads.h | 143 + 3rdparty/openal/common/atomic.h | 87 +- 3rdparty/openal/common/comptr.h | 82 +- 3rdparty/openal/common/dynload.cpp | 7 +- 3rdparty/openal/common/flexarray.h | 139 + 3rdparty/openal/common/intrusive_ptr.h | 36 +- 3rdparty/openal/common/opthelpers.h | 20 +- 3rdparty/openal/common/pffft.cpp | 2308 +++++++++++++++++ 3rdparty/openal/common/pffft.h | 212 ++ 3rdparty/openal/common/phase_shifter.h | 260 +- .../openal/common/polyphase_resampler.cpp | 133 +- 3rdparty/openal/common/polyphase_resampler.h | 6 +- 3rdparty/openal/common/ringbuffer.cpp | 208 +- 3rdparty/openal/common/ringbuffer.h | 124 +- 3rdparty/openal/common/strutils.cpp | 22 +- 3rdparty/openal/common/strutils.h | 2 +- 3rdparty/openal/common/vecmat.h | 120 +- 3rdparty/openal/common/vector.h | 3 +- 3rdparty/openal/config.h.in | 91 +- 3rdparty/openal/config_backends.h.in | 35 + 3rdparty/openal/config_simd.h.in | 10 + 3rdparty/openal/core/ambdec.cpp | 49 +- 3rdparty/openal/core/ambdec.h | 15 +- 3rdparty/openal/core/ambidefs.cpp | 247 +- 3rdparty/openal/core/ambidefs.h | 54 +- 3rdparty/openal/core/async_event.h | 10 +- 3rdparty/openal/core/bformatdec.cpp | 81 +- 3rdparty/openal/core/bformatdec.h | 25 +- 3rdparty/openal/core/bs2b.cpp | 158 +- 3rdparty/openal/core/bs2b.h | 82 +- 3rdparty/openal/core/bsinc_tables.cpp | 180 +- 3rdparty/openal/core/bsinc_tables.h | 9 +- 3rdparty/openal/core/buffer_storage.cpp | 76 - 3rdparty/openal/core/buffer_storage.h | 58 +- 3rdparty/openal/core/bufferline.h | 2 +- 3rdparty/openal/core/context.cpp | 163 +- 3rdparty/openal/core/context.h | 56 +- 3rdparty/openal/core/converter.cpp | 240 +- 3rdparty/openal/core/converter.h | 20 +- 3rdparty/openal/core/cpu_caps.cpp | 11 +- 3rdparty/openal/core/cubic_defs.h | 6 +- 3rdparty/openal/core/cubic_tables.cpp | 140 +- 3rdparty/openal/core/cubic_tables.h | 39 +- 3rdparty/openal/core/dbus_wrap.cpp | 20 +- 3rdparty/openal/core/dbus_wrap.h | 7 + 3rdparty/openal/core/devformat.cpp | 2 + 3rdparty/openal/core/devformat.h | 9 +- 3rdparty/openal/core/device.cpp | 12 +- 3rdparty/openal/core/device.h | 206 +- 3rdparty/openal/core/effects/base.h | 263 +- 3rdparty/openal/core/effectslot.cpp | 10 +- 3rdparty/openal/core/effectslot.h | 25 +- 3rdparty/openal/core/except.cpp | 6 +- 3rdparty/openal/core/except.h | 17 +- 3rdparty/openal/core/filters/biquad.cpp | 13 +- 3rdparty/openal/core/filters/biquad.h | 11 +- 3rdparty/openal/core/filters/nfc.cpp | 78 +- 3rdparty/openal/core/filters/nfc.h | 33 +- 3rdparty/openal/core/filters/splitter.cpp | 18 +- 3rdparty/openal/core/filters/splitter.h | 6 +- 3rdparty/openal/core/fmt_traits.cpp | 8 +- 3rdparty/openal/core/fmt_traits.h | 62 +- 3rdparty/openal/core/fpu_ctrl.cpp | 81 +- 3rdparty/openal/core/fpu_ctrl.h | 23 +- 3rdparty/openal/core/front_stablizer.h | 1 + 3rdparty/openal/core/helpers.cpp | 518 ++-- 3rdparty/openal/core/helpers.h | 24 +- 3rdparty/openal/core/hrtf.cpp | 655 +++-- 3rdparty/openal/core/hrtf.h | 35 +- 3rdparty/openal/core/logging.cpp | 13 +- 3rdparty/openal/core/logging.h | 10 +- 3rdparty/openal/core/mastering.cpp | 320 ++- 3rdparty/openal/core/mastering.h | 52 +- 3rdparty/openal/core/mixer.cpp | 15 +- 3rdparty/openal/core/mixer.h | 24 +- 3rdparty/openal/core/mixer/defs.h | 66 +- 3rdparty/openal/core/mixer/hrtfbase.h | 79 +- 3rdparty/openal/core/mixer/mixer_c.cpp | 250 +- 3rdparty/openal/core/mixer/mixer_neon.cpp | 454 ++-- 3rdparty/openal/core/mixer/mixer_sse.cpp | 342 ++- 3rdparty/openal/core/mixer/mixer_sse2.cpp | 164 +- 3rdparty/openal/core/mixer/mixer_sse41.cpp | 164 +- 3rdparty/openal/core/resampler_limits.h | 4 +- 3rdparty/openal/core/rtkit.cpp | 24 +- 3rdparty/openal/core/storage_formats.cpp | 85 + 3rdparty/openal/core/storage_formats.h | 54 + 3rdparty/openal/core/uhjfilter.cpp | 536 ++-- 3rdparty/openal/core/uhjfilter.h | 136 +- 3rdparty/openal/core/uiddefs.cpp | 10 +- 3rdparty/openal/core/voice.cpp | 754 +++--- 3rdparty/openal/core/voice.h | 100 +- 3rdparty/openal/core/voice_change.h | 4 - 3rdparty/openal/include/AL/al.h | 16 +- 3rdparty/openal/include/AL/alc.h | 16 +- 3rdparty/openal/include/AL/alext.h | 358 ++- 3rdparty/openal/include/AL/efx-presets.h | 2 + 3rdparty/openal/include/AL/efx.h | 2 + 238 files changed, 21537 insertions(+), 16018 deletions(-) create mode 100644 3rdparty/openal/OpenALConfig.cmake.in create mode 100644 3rdparty/openal/al/error.h create mode 100644 3rdparty/openal/alc/backends/otherio.cpp create mode 100644 3rdparty/openal/alc/backends/otherio.h create mode 100644 3rdparty/openal/cmake/FindSndIO.cmake delete mode 100644 3rdparty/openal/cmake/FindSoundIO.cmake create mode 100644 3rdparty/openal/common/alassert.cpp create mode 100644 3rdparty/openal/common/alassert.h delete mode 100644 3rdparty/openal/common/aldeque.h delete mode 100644 3rdparty/openal/common/alfstream.cpp delete mode 100644 3rdparty/openal/common/alfstream.h delete mode 100644 3rdparty/openal/common/almalloc.cpp create mode 100644 3rdparty/openal/common/althreads.h create mode 100644 3rdparty/openal/common/flexarray.h create mode 100644 3rdparty/openal/common/pffft.cpp create mode 100644 3rdparty/openal/common/pffft.h create mode 100644 3rdparty/openal/config_backends.h.in create mode 100644 3rdparty/openal/config_simd.h.in create mode 100644 3rdparty/openal/core/storage_formats.cpp create mode 100644 3rdparty/openal/core/storage_formats.h diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index eb72a25bc256..82ae476249c2 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -379,11 +379,11 @@ if (AX_ENABLE_AUDIO) set(ALSOFT_CPPWINRT_VERSION ${AX_CPPWINRT_VERSION} CACHE STRING "" FORCE) endif() - ax_add_3rd(openal EXCLUDE_FROM_ALL TARGETS alcommon;OpenAL OPTIONS ${alsoft_opts}) + ax_add_3rd(openal EXCLUDE_FROM_ALL TARGETS OpenAL;alsoft.excommon;alsoft.common OPTIONS ${alsoft_opts}) target_include_directories(3rdparty INTERFACE openal) target_compile_definitions(3rdparty INTERFACE AX_USE_ALSOFT=1) - set_target_properties(OpenAL alcommon PROPERTIES CXX_STANDARD ${_AX_CXX_STD}) + set_target_properties(OpenAL alsoft.excommon alsoft.common PROPERTIES CXX_STANDARD ${_AX_CXX_STD}) if (AX_USE_ALSOFT_STATIC) target_compile_definitions(3rdparty INTERFACE AL_LIBTYPE_STATIC=1) diff --git a/3rdparty/README.md b/3rdparty/README.md index 8820554e0260..8f968e83445d 100644 --- a/3rdparty/README.md +++ b/3rdparty/README.md @@ -170,7 +170,7 @@ ## OpenAL Soft - [![Upstream](https://img.shields.io/github/v/tag/kcat/openal-soft?label=Upstream)](https://github.com/kcat/openal-soft) -- Version: 1.23.1-e714c8f (8659) +- Version: 1.24.1 - License: LGPL-2.1 ## OpenSSL diff --git a/3rdparty/openal/CMakeLists.txt b/3rdparty/openal/CMakeLists.txt index e0b6298fef07..212ca02c44da 100644 --- a/3rdparty/openal/CMakeLists.txt +++ b/3rdparty/openal/CMakeLists.txt @@ -100,6 +100,7 @@ include(GNUInstallDirs) find_package(PkgConfig) find_package(SDL2 QUIET) +# add_subdirectory(fmt-11.0.2 EXCLUDE_FROM_ALL) option(ALSOFT_DLOPEN "Check for the dlopen API for loading optional libs" ON) @@ -181,7 +182,7 @@ if(NOT LIBTYPE) endif() set(LIB_MAJOR_VERSION "1") -set(LIB_MINOR_VERSION "23") +set(LIB_MINOR_VERSION "24") set(LIB_REVISION "1") set(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}") set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0) @@ -203,31 +204,6 @@ 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) - if(NOT HAVE_POSIX_MEMALIGN_DEFAULT) - set(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) - set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600") - check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_POSIX) - if(NOT HAVE_POSIX_MEMALIGN_POSIX) - set(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS}) - else() - set(CPP_DEFS ${CPP_DEFS} _POSIX_C_SOURCE=200112L _XOPEN_SOURCE=600) - endif() - endif() - unset(OLD_REQUIRED_FLAGS) -endif() - -# C99 has restrict, but C++ does not, so we can only utilize __restrict. -check_cxx_source_compiles("int *__restrict foo; -int main() { return 0; }" HAVE___RESTRICT) -if(HAVE___RESTRICT) - set(CPP_DEFS ${CPP_DEFS} RESTRICT=__restrict) -else() - set(CPP_DEFS ${CPP_DEFS} "RESTRICT=") -endif() - # Some systems may need libatomic for atomic functions to work set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES} atomic) @@ -252,12 +228,18 @@ if(ANDROID) endif() if(MSVC) - set(CPP_DEFS ${CPP_DEFS} _CRT_SECURE_NO_WARNINGS) + # NOTE: _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR is temporary. When building on + # VS 2022 17.10 or newer, but using an older runtime, mutexes can crash + # when locked. Ideally the runtime should be updated on the system, but + # until the update becomes more widespread, this helps avoid some pain + # points. + set(CPP_DEFS ${CPP_DEFS} _CRT_SECURE_NO_WARNINGS _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR) check_cxx_compiler_flag(/permissive- HAVE_PERMISSIVE_SWITCH) if(HAVE_PERMISSIVE_SWITCH) set(C_FLAGS ${C_FLAGS} $<$:/permissive->) endif() - set(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4127 /wd4268 /wd4324 /wd5030 /wd5051) + set(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4127 /wd4268 /wd4324 /wd5030 /wd5051 + $<$:/EHsc>) if(NOT DXSDK_DIR) string(REGEX REPLACE "\\\\" "/" DXSDK_DIR "$ENV{DXSDK_DIR}") @@ -295,18 +277,17 @@ else() endif() endif() + check_cxx_compiler_flag(-Wno-interference-size HAVE_WNO_INTERFERENCE_SIZE) + if(HAVE_WNO_INTERFERENCE_SIZE) + set(C_FLAGS ${C_FLAGS} $<$:-Wno-interference-size>) + endif() + if(ALSOFT_WERROR) set(C_FLAGS ${C_FLAGS} -Werror) + # else() + # set(C_FLAGS ${C_FLAGS} -Werror=undef) endif() - # We want RelWithDebInfo to actually include debug stuff (define _DEBUG - # instead of NDEBUG) - foreach(flag_var CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO) - if(${flag_var} MATCHES "-DNDEBUG") - string(REGEX REPLACE "-DNDEBUG" "-D_DEBUG" ${flag_var} "${${flag_var}}") - endif() - endforeach() - check_c_compiler_flag(-fno-math-errno HAVE_FNO_MATH_ERRNO) if(HAVE_FNO_MATH_ERRNO) set(C_FLAGS ${C_FLAGS} -fno-math-errno) @@ -329,7 +310,7 @@ else() option(ALSOFT_STATIC_STDCXX "Static link libstdc++" OFF) if(ALSOFT_STATIC_STDCXX) set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) - set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lstdc++,--pop-state") + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-static-libstdc++") check_cxx_source_compiles("int main() { }" HAVE_STATIC_LIBSTDCXX_SWITCH) set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES}) unset(OLD_REQUIRED_LIBRARIES) @@ -337,14 +318,14 @@ else() if(NOT HAVE_STATIC_LIBSTDCXX_SWITCH) message(FATAL_ERROR "Cannot static link libstdc++") endif() - set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lstdc++,--pop-state") + set(LINKER_FLAGS ${LINKER_FLAGS} "-static-libstdc++") endif() if(WIN32) option(ALSOFT_STATIC_WINPTHREAD "Static link libwinpthread" OFF) if(ALSOFT_STATIC_WINPTHREAD) set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) - set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lwinpthread,--pop-state") + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lstdc++,-lwinpthread,--pop-state") check_cxx_source_compiles("int main() { }" HAVE_STATIC_LIBWINPTHREAD_SWITCH) set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES}) unset(OLD_REQUIRED_LIBRARIES) @@ -352,7 +333,7 @@ else() if(NOT HAVE_STATIC_LIBWINPTHREAD_SWITCH) message(FATAL_ERROR "Cannot static link libwinpthread") endif() - set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lwinpthread,--pop-state") + set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lstdc++,-lwinpthread,--pop-state") endif() endif() endif() @@ -420,7 +401,7 @@ if(ALSOFT_CPUEXT_SSE AND HAVE_XMMINTRIN_H) set(HAVE_SSE 1) endif() if(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE) - message(FATAL_ERROR "Failed to enabled required SSE CPU extensions") + message(FATAL_ERROR "Failed to enable required SSE CPU extensions") endif() option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON) @@ -465,7 +446,7 @@ if(ALSOFT_CPUEXT_NEON AND HAVE_ARM_NEON_H) endif() endif() if(ALSOFT_REQUIRE_NEON AND NOT HAVE_NEON) - message(FATAL_ERROR "Failed to enabled required ARM NEON CPU extensions") + message(FATAL_ERROR "Failed to enable required ARM NEON CPU extensions") endif() @@ -512,13 +493,9 @@ if(HAVE_SSE2) endif() -check_include_file(malloc.h HAVE_MALLOC_H) check_include_file(cpuid.h HAVE_CPUID_H) check_include_file(intrin.h HAVE_INTRIN_H) check_include_file(guiddef.h HAVE_GUIDDEF_H) -if(NOT HAVE_GUIDDEF_H) - check_include_file(initguid.h HAVE_INITGUID_H) -endif() # Some systems need libm for some math functions to work set(MATH_LIB ) @@ -565,8 +542,6 @@ if(HAVE_INTRIN_H) }" HAVE_CPUID_INTRINSIC) endif() -check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN) -check_symbol_exists(_aligned_malloc malloc.h HAVE__ALIGNED_MALLOC) check_symbol_exists(proc_pidpath libproc.h HAVE_PROC_PIDPATH) if(NOT WIN32) @@ -601,19 +576,15 @@ if(NOT WIN32) endif() endif() -check_symbol_exists(getopt unistd.h HAVE_GETOPT) - # Common sources used by both the OpenAL implementation library, the OpenAL # router, and certain tools and examples. set(COMMON_OBJS + common/alassert.cpp + common/alassert.h common/albit.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 @@ -624,13 +595,17 @@ set(COMMON_OBJS common/alstring.h common/althrd_setname.cpp common/althrd_setname.h + common/althreads.h common/altraits.h common/atomic.h common/comptr.h common/dynload.cpp common/dynload.h + common/flexarray.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 @@ -699,6 +674,8 @@ set(CORE_OBJS core/mixer.cpp core/mixer.h core/resampler_limits.h + core/storage_formats.cpp + core/storage_formats.h core/uhjfilter.cpp core/uhjfilter.h core/uiddefs.cpp @@ -745,7 +722,7 @@ if(NOT WIN32) endif() endif() if(ALSOFT_REQUIRE_RTKIT AND NOT HAVE_RTKIT) - message(FATAL_ERROR "Failed to enabled required RTKit support") + message(FATAL_ERROR "Failed to enable required RTKit support") endif() # Default mixers, always available @@ -783,6 +760,7 @@ set(OPENAL_OBJS al/effects/reverb.cpp al/effects/vmorpher.cpp al/error.cpp + al/error.h al/event.cpp al/event.h al/extension.cpp @@ -884,6 +862,7 @@ set(HAVE_PULSEAUDIO 0) set(HAVE_COREAUDIO 0) set(HAVE_OPENSL 0) set(HAVE_OBOE 0) +set(HAVE_OTHERIO 0) set(HAVE_WAVE 0) set(HAVE_SDL2 0) @@ -923,7 +902,7 @@ if(ALSOFT_BACKEND_PIPEWIRE AND PkgConfig_FOUND) endif() endif() if(ALSOFT_REQUIRE_PIPEWIRE AND NOT HAVE_PIPEWIRE) - message(FATAL_ERROR "Failed to enabled required PipeWire backend") + message(FATAL_ERROR "Failed to enable required PipeWire backend") endif() # Check PulseAudio backend @@ -940,7 +919,7 @@ if(ALSOFT_BACKEND_PULSEAUDIO) endif() endif() if(ALSOFT_REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO) - message(FATAL_ERROR "Failed to enabled required PulseAudio backend") + message(FATAL_ERROR "Failed to enable required PulseAudio backend") endif() if(NOT WIN32) @@ -987,31 +966,35 @@ if(NOT WIN32) endif() endif() - # Check SndIO backend - option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" ON) + # Check SndIO backend (disabled by default on non-BSDs) + if(BSD) + option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" ON) + else() + option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" OFF) + endif() option(ALSOFT_REQUIRE_SNDIO "Require SndIO backend" OFF) if(ALSOFT_BACKEND_SNDIO) - find_package(SoundIO) - if(SOUNDIO_FOUND) + find_package(SndIO) + if(SNDIO_FOUND) set(HAVE_SNDIO 1) set(BACKENDS "${BACKENDS} SndIO (linked),") set(ALC_OBJS ${ALC_OBJS} alc/backends/sndio.cpp alc/backends/sndio.h) - set(EXTRA_LIBS ${SOUNDIO_LIBRARIES} ${EXTRA_LIBS}) - set(INC_PATHS ${INC_PATHS} ${SOUNDIO_INCLUDE_DIRS}) + set(EXTRA_LIBS ${SNDIO_LIBRARIES} ${EXTRA_LIBS}) + set(INC_PATHS ${INC_PATHS} ${SNDIO_INCLUDE_DIRS}) endif() endif() endif() if(ALSOFT_REQUIRE_ALSA AND NOT HAVE_ALSA) - message(FATAL_ERROR "Failed to enabled required ALSA backend") + message(FATAL_ERROR "Failed to enable required ALSA backend") endif() if(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS) - message(FATAL_ERROR "Failed to enabled required OSS backend") + message(FATAL_ERROR "Failed to enable required OSS backend") endif() if(ALSOFT_REQUIRE_SOLARIS AND NOT HAVE_SOLARIS) - message(FATAL_ERROR "Failed to enabled required Solaris backend") + message(FATAL_ERROR "Failed to enable required Solaris backend") endif() if(ALSOFT_REQUIRE_SNDIO AND NOT HAVE_SNDIO) - message(FATAL_ERROR "Failed to enabled required SndIO backend") + message(FATAL_ERROR "Failed to enable required SndIO backend") endif() # Check Windows-only backends @@ -1062,17 +1045,32 @@ if(WIN32) set(HAVE_WASAPI 1) set(BACKENDS "${BACKENDS} WASAPI,") set(ALC_OBJS ${ALC_OBJS} alc/backends/wasapi.cpp alc/backends/wasapi.h) + + if(NOT ALSOFT_UWP) + set(EXTRA_LIBS avrt ${EXTRA_LIBS}) + endif() endif() endif() + + option(ALSOFT_BACKEND_OTHERIO "Enable OtherIO backend" OFF) + option(ALSOFT_REQUIRE_OTHERIO "Require OtherIO backend" OFF) + if(ALSOFT_BACKEND_OTHERIO) + set(HAVE_OTHERIO 1) + set(BACKENDS "${BACKENDS} OtherIO,") + set(ALC_OBJS ${ALC_OBJS} alc/backends/otherio.cpp alc/backends/otherio.h) + endif() endif() if(ALSOFT_REQUIRE_WINMM AND NOT HAVE_WINMM) - message(FATAL_ERROR "Failed to enabled required WinMM backend") + message(FATAL_ERROR "Failed to enable required WinMM backend") endif() if(ALSOFT_REQUIRE_DSOUND AND NOT HAVE_DSOUND) - message(FATAL_ERROR "Failed to enabled required DSound backend") + message(FATAL_ERROR "Failed to enable required DSound backend") endif() if(ALSOFT_REQUIRE_WASAPI AND NOT HAVE_WASAPI) - message(FATAL_ERROR "Failed to enabled required WASAPI backend") + message(FATAL_ERROR "Failed to enable required WASAPI backend") +endif() +if(ALSOFT_REQUIRE_OTHERIO AND NOT HAVE_OTHERIO) + message(FATAL_ERROR "Failed to enable required OtherIO backend") endif() # Check JACK backend @@ -1089,7 +1087,7 @@ if(ALSOFT_BACKEND_JACK) endif() endif() if(ALSOFT_REQUIRE_JACK AND NOT HAVE_JACK) - message(FATAL_ERROR "Failed to enabled required JACK backend") + message(FATAL_ERROR "Failed to enable required JACK backend") endif() # Check CoreAudio backend @@ -1124,7 +1122,7 @@ if(ALSOFT_BACKEND_COREAUDIO) endif() endif() if(ALSOFT_REQUIRE_COREAUDIO AND NOT HAVE_COREAUDIO) - message(FATAL_ERROR "Failed to enabled required CoreAudio backend") + message(FATAL_ERROR "Failed to enable required CoreAudio backend") endif() # Check for Oboe (Android) backend @@ -1135,7 +1133,7 @@ if(ALSOFT_BACKEND_OBOE) if(ANDROID) set(OBOE_SOURCE "" CACHE STRING "Source directory for Oboe.") if(OBOE_SOURCE) - add_subdirectory(${OBOE_SOURCE} ./oboe) + add_subdirectory(${OBOE_SOURCE} ./oboe EXCLUDE_FROM_ALL) set(OBOE_TARGET oboe) else() find_package(oboe CONFIG) @@ -1156,7 +1154,7 @@ if(ALSOFT_BACKEND_OBOE) endif() endif() if(ALSOFT_REQUIRE_OBOE AND NOT HAVE_OBOE) - message(FATAL_ERROR "Failed to enabled required Oboe backend") + message(FATAL_ERROR "Failed to enable required Oboe backend") endif() # Check for OpenSL (Android) backend @@ -1173,7 +1171,7 @@ if(ALSOFT_BACKEND_OPENSL) endif() endif() if(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL) - message(FATAL_ERROR "Failed to enabled required OpenSL backend") + message(FATAL_ERROR "Failed to enable required OpenSL backend") endif() # Check PortAudio backend @@ -1190,7 +1188,7 @@ if(ALSOFT_BACKEND_PORTAUDIO) endif() endif() if(ALSOFT_REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO) - message(FATAL_ERROR "Failed to enabled required PortAudio backend") + message(FATAL_ERROR "Failed to enable required PortAudio backend") endif() # Check for SDL2 backend @@ -1207,8 +1205,8 @@ if(ALSOFT_BACKEND_SDL2) message(STATUS "Could NOT find SDL2") endif() endif() -if(ALSOFT_REQUIRE_SDL2 AND NOT SDL2_FOUND) - message(FATAL_ERROR "Failed to enabled required SDL2 backend") +if(ALSOFT_REQUIRE_SDL2 AND NOT HAVE_SDL2) + message(FATAL_ERROR "Failed to enable required SDL2 backend") endif() # Optionally enable the Wave Writer backend @@ -1249,7 +1247,7 @@ if(ALSOFT_UPDATE_BUILD_VERSION AND GIT_FOUND AND EXISTS "${OpenAL_SOURCE_DIR}/.g VERBATIM ) - add_custom_target(build_version DEPENDS "${OpenAL_BINARY_DIR}/version_witness.txt") + add_custom_target(alsoft.build_version DEPENDS "${OpenAL_BINARY_DIR}/version_witness.txt") else() set(GIT_BRANCH "UNKNOWN") set(GIT_COMMIT_HASH "unknown") @@ -1330,22 +1328,45 @@ endif() configure_file( "${OpenAL_SOURCE_DIR}/config.h.in" "${OpenAL_BINARY_DIR}/config.h") +configure_file( + "${OpenAL_SOURCE_DIR}/config_backends.h.in" + "${OpenAL_BINARY_DIR}/config_backends.h") +configure_file( + "${OpenAL_SOURCE_DIR}/config_simd.h.in" + "${OpenAL_BINARY_DIR}/config_simd.h") configure_file( "${OpenAL_SOURCE_DIR}/openal.pc.in" "${OpenAL_BINARY_DIR}/openal.pc" @ONLY) -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) +add_library(alsoft.common STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS}) +target_include_directories(alsoft.common PRIVATE ${OpenAL_SOURCE_DIR}/include + PUBLIC ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common) +target_compile_definitions(alsoft.common PRIVATE ${CPP_DEFS}) +target_compile_options(alsoft.common PRIVATE ${C_FLAGS}) +set_target_properties(alsoft.common PROPERTIES ${DEFAULT_TARGET_PROPS} POSITION_INDEPENDENT_CODE TRUE) unset(HAS_ROUTER) set(IMPL_TARGET OpenAL) # Either OpenAL or soft_oal. + +set(NEED_ANALYZE_SOURCE_FILES "") +foreach(obj ${CORE_OBJS} ${OPENAL_OBJS} ${ALC_OBJS} ${COMMON_OBJS}) + IF (NOT ${obj} MATCHES "${CMAKE_BINARY_DIR}/default_hrtf.txt") + list(APPEND NEED_ANALYZE_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/${obj}") + endif() +endforeach() +IF (ALSOFT_UTILS) + list(APPEND NEED_ANALYZE_SOURCE_FILES "${CMAKE_SOURCE_DIR}/utils/openal-info.c") +endif() +SET(CLANG_TIDY_EXECUTABLE "clang-tidy") +if(DEFINED ENV{CLANG_TIDY_EXECUTABLE}) + SET(CLANG_TIDY_EXECUTABLE $ENV{CLANG_TIDY_EXECUTABLE}) +endif() +add_custom_target(clang-tidy-check ${CLANG_TIDY_EXECUTABLE} -format-style=file -p ${CMAKE_BINARY_DIR}/compile_commands.json ${NEED_ANALYZE_SOURCE_FILES} DEPENDS ${NEED_ANALYZE_SOURCE_FILES}) + # Build main library if(LIBTYPE STREQUAL "STATIC") add_library(${IMPL_TARGET} STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS} ${CORE_OBJS}) @@ -1375,7 +1396,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 alcommon ${LINKER_FLAGS}) + target_link_libraries(OpenAL PRIVATE alsoft.common ${LINKER_FLAGS}) target_include_directories(OpenAL PUBLIC $ @@ -1386,8 +1407,8 @@ else() ) set_target_properties(OpenAL PROPERTIES ${DEFAULT_TARGET_PROPS} PREFIX "" OUTPUT_NAME ${LIBNAME}) - if(TARGET build_version) - add_dependencies(OpenAL build_version) + if(TARGET alsoft.build_version) + add_dependencies(OpenAL alsoft.build_version) endif() set(HAS_ROUTER 1) @@ -1406,24 +1427,30 @@ else() if(WIN32) set_target_properties(${IMPL_TARGET} PROPERTIES PREFIX "") endif() - target_link_libraries(${IMPL_TARGET} PRIVATE alcommon ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) + target_link_libraries(${IMPL_TARGET} PRIVATE alsoft.common ${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() + find_package(cppwinrt CONFIG) + if (TARGET Microsoft::CppWinRT) + target_link_libraries(${IMPL_TARGET} PRIVATE Microsoft::CppWinRT) + else() + set(ALSOFT_CPPWINRT_VERSION "2.0.230706.1" CACHE STRING "The soft-oal default cppwinrt version") - exec_program(${NUGET_EXE} - ARGS install "Microsoft.Windows.CppWinRT" -Version ${ALSOFT_CPPWINRT_VERSION} -ExcludeVersion -OutputDirectory "\"${CMAKE_BINARY_DIR}/packages\"") + 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() - 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) + 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() endif() if(NOT WIN32 AND NOT APPLE) @@ -1488,12 +1515,12 @@ set_target_properties(${IMPL_TARGET} PROPERTIES ${DEFAULT_TARGET_PROPS} SOVERSION ${LIB_MAJOR_VERSION} ) target_compile_definitions(${IMPL_TARGET} - PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}" + PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}" ${CPP_DEFS}) target_compile_options(${IMPL_TARGET} PRIVATE ${C_FLAGS}) -if(TARGET build_version) - add_dependencies(${IMPL_TARGET} build_version) +if(TARGET alsoft.build_version) + add_dependencies(${IMPL_TARGET} alsoft.build_version) endif() if(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC") @@ -1507,7 +1534,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() - target_link_options(OpenAL PRIVATE "-Wl,--output-def,OpenAL32.def") + target_link_options(OpenAL PRIVATE "-Wl,--output-def,${PROJECT_BINARY_DIR}/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 @@ -1627,8 +1654,8 @@ 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 alcommon - PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG}) + target_link_libraries(uhjdecoder PUBLIC alsoft.common + PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG} alsoft::fmt) set_target_properties(uhjdecoder PROPERTIES ${DEFAULT_TARGET_PROPS}) add_executable(uhjencoder utils/uhjencoder.cpp) @@ -1636,8 +1663,8 @@ 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 alcommon - PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG}) + target_link_libraries(uhjencoder PUBLIC alsoft.common + PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG} alsoft::fmt) set_target_properties(uhjencoder PROPERTIES ${DEFAULT_TARGET_PROPS}) endif() @@ -1645,12 +1672,13 @@ if(ALSOFT_UTILS) set(SOFA_SUPPORT_SRCS utils/sofa-support.cpp utils/sofa-support.h) - add_library(sofa-support STATIC EXCLUDE_FROM_ALL ${SOFA_SUPPORT_SRCS}) - 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 alcommon MySOFA::MySOFA PRIVATE ${LINKER_FLAGS}) - set_target_properties(sofa-support PROPERTIES ${DEFAULT_TARGET_PROPS}) + add_library(alsoft.sofa-support STATIC EXCLUDE_FROM_ALL ${SOFA_SUPPORT_SRCS}) + target_compile_definitions(alsoft.sofa-support PRIVATE ${CPP_DEFS}) + target_include_directories(alsoft.sofa-support PUBLIC ${OpenAL_SOURCE_DIR}/common) + target_compile_options(alsoft.sofa-support PRIVATE ${C_FLAGS}) + target_link_libraries(alsoft.sofa-support PUBLIC alsoft.common MySOFA::MySOFA + PRIVATE ${LINKER_FLAGS} alsoft::fmt) + set_target_properties(alsoft.sofa-support PROPERTIES ${DEFAULT_TARGET_PROPS}) set(MAKEMHR_SRCS utils/makemhr/loaddef.cpp @@ -1659,15 +1687,13 @@ if(ALSOFT_UTILS) utils/makemhr/loadsofa.h utils/makemhr/makemhr.cpp utils/makemhr/makemhr.h) - if(NOT HAVE_GETOPT) - set(MAKEMHR_SRCS ${MAKEMHR_SRCS} utils/getopt.c utils/getopt.h) - endif() add_executable(makemhr ${MAKEMHR_SRCS}) target_compile_definitions(makemhr PRIVATE ${CPP_DEFS}) target_include_directories(makemhr PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/utils) target_compile_options(makemhr PRIVATE ${C_FLAGS}) - target_link_libraries(makemhr PRIVATE ${LINKER_FLAGS} sofa-support ${UNICODE_FLAG}) + target_link_libraries(makemhr PRIVATE ${LINKER_FLAGS} alsoft.sofa-support ${UNICODE_FLAG} + alsoft::fmt) set_target_properties(makemhr PROPERTIES ${DEFAULT_TARGET_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} makemhr) @@ -1678,7 +1704,8 @@ if(ALSOFT_UTILS) target_compile_definitions(sofa-info PRIVATE ${CPP_DEFS}) target_include_directories(sofa-info PRIVATE ${OpenAL_SOURCE_DIR}/utils) target_compile_options(sofa-info PRIVATE ${C_FLAGS}) - target_link_libraries(sofa-info PRIVATE ${LINKER_FLAGS} sofa-support ${UNICODE_FLAG}) + target_link_libraries(sofa-info PRIVATE ${LINKER_FLAGS} alsoft.sofa-support + ${UNICODE_FLAG} alsoft::fmt) set_target_properties(sofa-info PROPERTIES ${DEFAULT_TARGET_PROPS}) endif() message(STATUS "Building utility programs") @@ -1691,74 +1718,90 @@ endif() # Add a static library with common functions used by multiple example targets -add_library(al-excommon STATIC EXCLUDE_FROM_ALL +add_library(alsoft.excommon STATIC EXCLUDE_FROM_ALL examples/common/alhelpers.c examples/common/alhelpers.h) -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}) +target_compile_definitions(alsoft.excommon PUBLIC ${CPP_DEFS}) +target_include_directories(alsoft.excommon PUBLIC ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common) +target_compile_options(alsoft.excommon PUBLIC ${C_FLAGS}) +target_link_libraries(alsoft.excommon PUBLIC OpenAL PRIVATE ${RT_LIB}) +set_target_properties(alsoft.excommon PROPERTIES ${DEFAULT_TARGET_PROPS}) if(ALSOFT_EXAMPLES) add_executable(altonegen examples/altonegen.c) - target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} al-excommon ${UNICODE_FLAG}) + target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} alsoft.excommon + ${UNICODE_FLAG}) set_target_properties(altonegen PROPERTIES ${DEFAULT_TARGET_PROPS}) add_executable(alrecord examples/alrecord.c) - target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} al-excommon ${UNICODE_FLAG}) + target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} alsoft.excommon ${UNICODE_FLAG}) set_target_properties(alrecord PROPERTIES ${DEFAULT_TARGET_PROPS}) + add_executable(aldebug examples/aldebug.cpp) + target_link_libraries(aldebug PRIVATE ${LINKER_FLAGS} alsoft.excommon ${UNICODE_FLAG} + alsoft::fmt) + set_target_properties(aldebug PROPERTIES ${DEFAULT_TARGET_PROPS}) + + add_executable(allafplay examples/allafplay.cpp) + target_link_libraries(allafplay PRIVATE ${LINKER_FLAGS} alsoft.common alsoft.excommon + ${UNICODE_FLAG} alsoft::fmt) + set_target_properties(allafplay PROPERTIES ${DEFAULT_TARGET_PROPS}) + if(ALSOFT_INSTALL_EXAMPLES) - set(EXTRA_INSTALLS ${EXTRA_INSTALLS} altonegen alrecord) + set(EXTRA_INSTALLS ${EXTRA_INSTALLS} altonegen alrecord aldebug allafplay) endif() message(STATUS "Building example programs") if(SNDFILE_FOUND) add_executable(alplay examples/alplay.c) - target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon + target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.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 al-excommon + target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.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 al-excommon + target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.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 al-excommon ${MATH_LIB} ${UNICODE_FLAG}) + PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.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 al-excommon + target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.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 al-excommon ${MATH_LIB} ${UNICODE_FLAG}) + PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.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 al-excommon - ${UNICODE_FLAG}) + target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon + ${UNICODE_FLAG} alsoft::fmt) set_target_properties(alstreamcb PROPERTIES ${DEFAULT_TARGET_PROPS}) + add_executable(aldirect examples/aldirect.cpp) + target_link_libraries(aldirect PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon + ${UNICODE_FLAG} alsoft::fmt) + set_target_properties(aldirect PROPERTIES ${DEFAULT_TARGET_PROPS}) + add_executable(alconvolve examples/alconvolve.c) - target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} alcommon SndFile::SndFile - al-excommon ${UNICODE_FLAG}) + target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} alsoft.common SndFile::SndFile + alsoft.excommon ${UNICODE_FLAG}) set_target_properties(alconvolve PROPERTIES ${DEFAULT_TARGET_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alplay alstream alreverb almultireverb allatency - alhrtf) + alhrtf aldirect) endif() message(STATUS "Building SndFile example programs") @@ -1767,7 +1810,7 @@ if(ALSOFT_EXAMPLES) if(SDL2_FOUND) add_executable(alloopback examples/alloopback.c) target_link_libraries(alloopback - PRIVATE ${LINKER_FLAGS} SDL2::SDL2 al-excommon ${MATH_LIB}) + PRIVATE ${LINKER_FLAGS} SDL2::SDL2 alsoft.excommon ${MATH_LIB}) set_target_properties(alloopback PROPERTIES ${DEFAULT_TARGET_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) @@ -1804,7 +1847,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} al-excommon) + PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ${FFMPEG_LIBRARIES} alsoft.excommon alsoft::fmt) set_target_properties(alffplay PROPERTIES ${DEFAULT_TARGET_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) diff --git a/3rdparty/openal/ChangeLog b/3rdparty/openal/ChangeLog index e4236f85d224..16deaa2448b5 100644 --- a/3rdparty/openal/ChangeLog +++ b/3rdparty/openal/ChangeLog @@ -1,3 +1,81 @@ +openal-soft-1.24.1: + + Fixed compilation on PowerPC. + + Fixed compilation on some targets that lack lock-free 64-bit atomics. + + Fixed a crash when parsing certain option values. + + Fixed applying noexcept in the public headers with MSVC. + + Fixed building for UWP with vcpkg. + + Improved compatibility when compiling as C++20 or later. + + Integrated fmtlib for some examples and utilities. + +openal-soft-1.24.0: + + Updated library codebase to C++17. + + Implemented the ALC_SOFT_system_events extension. + + Implemented the AL_EXT_debug extension. + + Implemented the AL_EXT_direct_context extension. + + Implemented speaker configuration and headphones detection on CoreAudio. + + Fixed a potential crash with some extension functions on 32-bit Windows. + + Fixed a crash that can occur when stopping playback with the Oboe backend. + + Fixed calculating the reverb room rolloff. + + Fixed EAX occlusion, obstruction, and exclusion low-pass filter strength. + + Fixed EAX distance factor calculations. + + Fixed querying AL_EFFECTSLOT_EFFECT on auxiliary effect slots. + + Fixed compilation on some macOS systems that lack libdispatch. + + Fixed compilation as a subproject with MinGW. + + Changed the context error state to be thread-local. This is technically out + of spec, but necessary to avoid race conditions with multi-threaded use. + + Split the cubic resampler into 4-point spline and gaussian variants. The + latter prioritizing the suppression of aliasing distortion and harmonics, + the former not reducing high frequencies as much. + + Improved timing precision of starting delayed sources. + + Improved ring modulator quality. + + Improved performance of convolution reverb. + + Improved WASAPI device enumeration performance. + + Added UWP support. + + Added 'noexcept' to functions and function types when compiled as C++. As a + C API, OpenAL can't be expected to throw C++ exceptions, nor can it handle + them if they leave a callback. + + Added an experimental config option for using WASAPI spatial audio output. + + Added enumeration support to the PortAudio backend. + + Added compatibility options to override the AL_VENDOR, AL_VERSION, and + AL_RENDERER strings. + + Added an example to play LAF files. + + Disabled real-time mixing by default for PipeWire playback. + + Disabled the SndIO backend by default on non-BSD targets. + openal-soft-1.23.1: Implemented the AL_SOFT_UHJ_ex extension. diff --git a/3rdparty/openal/OpenALConfig.cmake.in b/3rdparty/openal/OpenALConfig.cmake.in new file mode 100644 index 000000000000..9704d3c496aa --- /dev/null +++ b/3rdparty/openal/OpenALConfig.cmake.in @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.1...3.18) + +include("${CMAKE_CURRENT_LIST_DIR}/OpenALTargets.cmake") + +set(OPENAL_FOUND ON) +set(OPENAL_INCLUDE_DIR $) +set(OPENAL_LIBRARY $) +set(OPENAL_DEFINITIONS $) +set(OPENAL_VERSION_STRING @PACKAGE_VERSION@) diff --git a/3rdparty/openal/README.md b/3rdparty/openal/README.md index dac53e71edff..4151a6cf6564 100644 --- a/3rdparty/openal/README.md +++ b/3rdparty/openal/README.md @@ -87,6 +87,10 @@ Python Bindings: * [PyOpenAL](https://pypi.org/project/PyOpenAL/). Also includes methods to play wave files and, with PyOgg, also Vorbis, Opus, and FLAC. +FreePascal/Lazarus Bindings: +* [ALSound](https://github.com/Lulu04/ALSound). Also includes a higher level +API and libsndfile support to simplify loading and playing sounds. + Other bindings for these and other languages also exist. This list will grow as more bindings are found. diff --git a/3rdparty/openal/al/auxeffectslot.cpp b/3rdparty/openal/al/auxeffectslot.cpp index 332524101ab0..039d7fcf5501 100644 --- a/3rdparty/openal/al/auxeffectslot.cpp +++ b/3rdparty/openal/al/auxeffectslot.cpp @@ -23,70 +23,81 @@ #include "auxeffectslot.h" #include -#include +#include #include +#include #include #include #include #include -#include +#include +#include +#include +#include #include "AL/al.h" #include "AL/alc.h" +#include "AL/alext.h" #include "AL/efx.h" #include "albit.h" #include "alc/alu.h" #include "alc/context.h" #include "alc/device.h" +#include "alc/effects/base.h" #include "alc/inprogext.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" +#include "atomic.h" #include "buffer.h" -#include "core/except.h" +#include "core/buffer_storage.h" +#include "core/device.h" #include "core/fpu_ctrl.h" #include "core/logging.h" #include "direct_defs.h" #include "effect.h" +#include "error.h" +#include "flexarray.h" #include "opthelpers.h" +#if ALSOFT_EAX +#include "eax/api.h" +#include "eax/call.h" +#include "eax/effect.h" +#include "eax/fx_slot_index.h" +#include "eax/utils.h" +#endif + namespace { -struct FactoryItem { - EffectSlotType Type; - EffectStateFactory* (&GetFactory)(void); -}; -constexpr FactoryItem FactoryList[] = { - { EffectSlotType::None, NullStateFactory_getFactory }, - { EffectSlotType::EAXReverb, ReverbStateFactory_getFactory }, - { EffectSlotType::Reverb, StdReverbStateFactory_getFactory }, - { EffectSlotType::Autowah, AutowahStateFactory_getFactory }, - { EffectSlotType::Chorus, ChorusStateFactory_getFactory }, - { EffectSlotType::Compressor, CompressorStateFactory_getFactory }, - { EffectSlotType::Distortion, DistortionStateFactory_getFactory }, - { EffectSlotType::Echo, EchoStateFactory_getFactory }, - { EffectSlotType::Equalizer, EqualizerStateFactory_getFactory }, - { EffectSlotType::Flanger, FlangerStateFactory_getFactory }, - { EffectSlotType::FrequencyShifter, FshifterStateFactory_getFactory }, - { EffectSlotType::RingModulator, ModulatorStateFactory_getFactory }, - { EffectSlotType::PitchShifter, PshifterStateFactory_getFactory }, - { EffectSlotType::VocalMorpher, VmorpherStateFactory_getFactory }, - { EffectSlotType::DedicatedDialog, DedicatedStateFactory_getFactory }, - { EffectSlotType::DedicatedLFE, DedicatedStateFactory_getFactory }, - { EffectSlotType::Convolution, ConvolutionStateFactory_getFactory }, -}; +using SubListAllocator = al::allocator>; EffectStateFactory *getFactoryByType(EffectSlotType type) { - auto iter = std::find_if(std::begin(FactoryList), std::end(FactoryList), - [type](const FactoryItem &item) noexcept -> bool - { return item.Type == type; }); - return (iter != std::end(FactoryList)) ? iter->GetFactory() : nullptr; + switch(type) + { + case EffectSlotType::None: return NullStateFactory_getFactory(); + case EffectSlotType::Reverb: return ReverbStateFactory_getFactory(); + case EffectSlotType::Chorus: return ChorusStateFactory_getFactory(); + case EffectSlotType::Autowah: return AutowahStateFactory_getFactory(); + case EffectSlotType::Compressor: return CompressorStateFactory_getFactory(); + case EffectSlotType::Convolution: return ConvolutionStateFactory_getFactory(); + case EffectSlotType::Dedicated: return DedicatedStateFactory_getFactory(); + case EffectSlotType::Distortion: return DistortionStateFactory_getFactory(); + case EffectSlotType::Echo: return EchoStateFactory_getFactory(); + case EffectSlotType::Equalizer: return EqualizerStateFactory_getFactory(); + case EffectSlotType::Flanger: return ChorusStateFactory_getFactory(); + case EffectSlotType::FrequencyShifter: return FshifterStateFactory_getFactory(); + case EffectSlotType::RingModulator: return ModulatorStateFactory_getFactory(); + case EffectSlotType::PitchShifter: return PshifterStateFactory_getFactory(); + case EffectSlotType::VocalMorpher: return VmorpherStateFactory_getFactory(); + } + return nullptr; } -inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept +auto LookupEffectSlot(ALCcontext *context, ALuint id) noexcept -> ALeffectslot* { const size_t lidx{(id-1) >> 6}; const ALuint slidx{(id-1) & 0x3f}; @@ -96,10 +107,10 @@ inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept EffectSlotSubList &sublist{context->mEffectSlotList[lidx]}; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.EffectSlots + slidx; + return al::to_address(sublist.EffectSlots->begin() + slidx); } -inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) noexcept +inline auto LookupEffect(al::Device *device, ALuint id) noexcept -> ALeffect* { const size_t lidx{(id-1) >> 6}; const ALuint slidx{(id-1) & 0x3f}; @@ -109,10 +120,10 @@ inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) noexcept EffectSubList &sublist = device->EffectList[lidx]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Effects + slidx; + return al::to_address(sublist.Effects->begin() + slidx); } -inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept +inline auto LookupBuffer(al::Device *device, ALuint id) noexcept -> ALbuffer* { const size_t lidx{(id-1) >> 6}; const ALuint slidx{(id-1) & 0x3f}; @@ -122,7 +133,7 @@ inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept BufferSubList &sublist = device->BufferList[lidx]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Buffers + slidx; + return al::to_address(sublist.Buffers->begin() + slidx); } @@ -130,44 +141,44 @@ void AddActiveEffectSlots(const al::span auxslots, ALCcontext *co { if(auxslots.empty()) return; EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)}; - size_t newcount{curarray->size() + auxslots.size()}; + if((curarray->size()>>1) > std::numeric_limits::max()-auxslots.size()) + throw std::runtime_error{"Too many active effect slots"}; - /* Insert the new effect slots into the head of the array, followed by the - * existing ones. + size_t newcount{(curarray->size()>>1) + auxslots.size()}; + if(newcount > std::numeric_limits::max()>>1) + throw std::runtime_error{"Too many active effect slots"}; + + /* Insert the new effect slots into the head of the new array, followed by + * the existing ones. */ - EffectSlotArray *newarray = EffectSlot::CreatePtrArray(newcount); - auto slotiter = std::transform(auxslots.begin(), auxslots.end(), newarray->begin(), - [](ALeffectslot *auxslot) noexcept { return auxslot->mSlot; }); - std::copy(curarray->begin(), curarray->end(), slotiter); + auto newarray = EffectSlot::CreatePtrArray(newcount<<1); + auto new_end = std::transform(auxslots.begin(), auxslots.end(), newarray->begin(), + std::mem_fn(&ALeffectslot::mSlot)); + new_end = std::copy_n(curarray->begin(), curarray->size()>>1, new_end); /* Remove any duplicates (first instance of each will be kept). */ - auto last = newarray->end(); for(auto start=newarray->begin()+1;;) { - last = std::remove(start, last, *(start-1)); - if(start == last) break; + new_end = std::remove(start, new_end, *(start-1)); + if(start == new_end) break; ++start; } - newcount = static_cast(std::distance(newarray->begin(), last)); + newcount = static_cast(std::distance(newarray->begin(), new_end)); /* Reallocate newarray if the new size ended up smaller from duplicate * removal. */ - if(newcount < newarray->size()) UNLIKELY + if(newcount < newarray->size()>>1) UNLIKELY { - curarray = newarray; - newarray = EffectSlot::CreatePtrArray(newcount); - std::copy_n(curarray->begin(), newcount, newarray->begin()); - delete curarray; - curarray = nullptr; + auto oldarray = std::move(newarray); + newarray = EffectSlot::CreatePtrArray(newcount<<1); + new_end = std::copy_n(oldarray->begin(), newcount, newarray->begin()); } - std::uninitialized_fill_n(newarray->end(), newcount, nullptr); - - curarray = context->mActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel); - context->mDevice->waitForMix(); + std::fill(new_end, newarray->end(), nullptr); - std::destroy_n(curarray->end(), curarray->size()); - delete curarray; + auto oldarray = context->mActiveAuxSlots.exchange(std::move(newarray), + std::memory_order_acq_rel); + std::ignore = context->mDevice->waitForMix(); } void RemoveActiveEffectSlots(const al::span auxslots, ALCcontext *context) @@ -178,9 +189,9 @@ void RemoveActiveEffectSlots(const al::span auxslots, ALCcontext /* Don't shrink the allocated array size since we don't know how many (if * any) of the effect slots to remove are in the array. */ - EffectSlotArray *newarray = EffectSlot::CreatePtrArray(curarray->size()); + auto newarray = EffectSlot::CreatePtrArray(curarray->size()); - auto new_end = std::copy(curarray->begin(), curarray->end(), newarray->begin()); + auto new_end = std::copy_n(curarray->begin(), curarray->size()>>1, newarray->begin()); /* Remove elements from newarray that match any ID in slotids. */ for(const ALeffectslot *auxslot : auxslots) { @@ -191,26 +202,21 @@ void RemoveActiveEffectSlots(const al::span auxslots, ALCcontext /* Reallocate with the new size. */ auto newsize = static_cast(std::distance(newarray->begin(), new_end)); - if(newsize != newarray->size()) LIKELY + if(newsize < newarray->size()>>1) LIKELY { - curarray = newarray; - newarray = EffectSlot::CreatePtrArray(newsize); - std::copy_n(curarray->begin(), newsize, newarray->begin()); - - delete curarray; - curarray = nullptr; + auto oldarray = std::move(newarray); + newarray = EffectSlot::CreatePtrArray(newsize<<1); + new_end = std::copy_n(oldarray->begin(), newsize, newarray->begin()); } - std::uninitialized_fill_n(newarray->end(), newsize, nullptr); + std::fill(new_end, newarray->end(), nullptr); - curarray = context->mActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel); - context->mDevice->waitForMix(); - - std::destroy_n(curarray->end(), curarray->size()); - delete curarray; + auto oldarray = context->mActiveAuxSlots.exchange(std::move(newarray), + std::memory_order_acq_rel); + std::ignore = context->mDevice->waitForMix(); } -EffectSlotType EffectSlotTypeFromEnum(ALenum type) +constexpr auto EffectSlotTypeFromEnum(ALenum type) noexcept -> EffectSlotType { switch(type) { @@ -227,17 +233,17 @@ EffectSlotType EffectSlotTypeFromEnum(ALenum type) case AL_EFFECT_AUTOWAH: return EffectSlotType::Autowah; case AL_EFFECT_COMPRESSOR: return EffectSlotType::Compressor; case AL_EFFECT_EQUALIZER: return EffectSlotType::Equalizer; - 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_EAXREVERB: return EffectSlotType::Reverb; + case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return EffectSlotType::Dedicated; + case AL_EFFECT_DEDICATED_DIALOGUE: return EffectSlotType::Dedicated; + case AL_EFFECT_CONVOLUTION_SOFT: return EffectSlotType::Convolution; } ERR("Unhandled effect enum: 0x%04x\n", type); return EffectSlotType::None; } -bool EnsureEffectSlots(ALCcontext *context, size_t needed) -{ +auto EnsureEffectSlots(ALCcontext *context, size_t needed) noexcept -> bool +try { size_t count{std::accumulate(context->mEffectSlotList.cbegin(), context->mEffectSlotList.cend(), 0_uz, [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t @@ -248,20 +254,17 @@ bool EnsureEffectSlots(ALCcontext *context, size_t needed) if(context->mEffectSlotList.size() >= 1<<25) UNLIKELY return false; - context->mEffectSlotList.emplace_back(); - auto sublist = context->mEffectSlotList.end() - 1; - sublist->FreeMask = ~0_u64; - sublist->EffectSlots = static_cast( - al_calloc(alignof(ALeffectslot), sizeof(ALeffectslot)*64)); - if(!sublist->EffectSlots) UNLIKELY - { - context->mEffectSlotList.pop_back(); - return false; - } - count += 64; + EffectSlotSubList sublist{}; + sublist.FreeMask = ~0_u64; + sublist.EffectSlots = SubListAllocator{}.allocate(1); + context->mEffectSlotList.emplace_back(std::move(sublist)); + count += std::tuple_size_v; } return true; } +catch(...) { + return false; +} ALeffectslot *AllocEffectSlot(ALCcontext *context) { @@ -272,7 +275,8 @@ ALeffectslot *AllocEffectSlot(ALCcontext *context) auto slidx = static_cast(al::countr_zero(sublist->FreeMask)); ASSUME(slidx < 64); - ALeffectslot *slot{al::construct_at(sublist->EffectSlots + slidx, context)}; + ALeffectslot *slot{al::construct_at(al::to_address(sublist->EffectSlots->begin() + slidx), + context)}; aluInitEffectPanning(slot->mSlot, context); /* Add 1 to avoid ID 0. */ @@ -312,255 +316,187 @@ inline void UpdateProps(ALeffectslot *slot, ALCcontext *context) } // namespace -AL_API DECL_FUNC2(void, alGenAuxiliaryEffectSlots, ALsizei, ALuint*) +AL_API DECL_FUNC2(void, alGenAuxiliaryEffectSlots, ALsizei,n, ALuint*,effectslots) FORCE_ALIGN void AL_APIENTRY alGenAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n, ALuint *effectslots) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Generating %d effect slots", n); +try { + if(n < 0) + throw al::context_error{AL_INVALID_VALUE, "Generating %d effect slots", n}; if(n <= 0) UNLIKELY return; - std::lock_guard _{context->mEffectSlotLock}; - ALCdevice *device{context->mALDevice.get()}; - if(static_cast(n) > device->AuxiliaryEffectSlotMax-context->mNumEffectSlots) - { - context->setError(AL_OUT_OF_MEMORY, "Exceeding %u effect slot limit (%u + %d)", - device->AuxiliaryEffectSlotMax, context->mNumEffectSlots, n); - return; - } - if(!EnsureEffectSlots(context, static_cast(n))) - { - context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d effectslot%s", n, - (n==1) ? "" : "s"); - return; - } + auto slotlock = std::lock_guard{context->mEffectSlotLock}; + auto *device = context->mALDevice.get(); - if(n == 1) - { - ALeffectslot *slot{AllocEffectSlot(context)}; - effectslots[0] = slot->id; + const al::span eids{effectslots, static_cast(n)}; + if(context->mNumEffectSlots > device->AuxiliaryEffectSlotMax + || eids.size() > device->AuxiliaryEffectSlotMax-context->mNumEffectSlots) + throw al::context_error{AL_OUT_OF_MEMORY, "Exceeding %u effect slot limit (%u + %d)", + device->AuxiliaryEffectSlotMax, context->mNumEffectSlots, n}; + + if(!EnsureEffectSlots(context, eids.size())) + throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d effectslot%s", n, + (n == 1) ? "" : "s"}; + + std::vector slots; + try { + if(eids.size() == 1) + { + /* Special handling for the easy and normal case. */ + eids[0] = AllocEffectSlot(context)->id; + } + else + { + slots.reserve(eids.size()); + std::generate_n(std::back_inserter(slots), eids.size(), + [context]{ return AllocEffectSlot(context); }); + + std::transform(slots.cbegin(), slots.cend(), eids.begin(), + [](ALeffectslot *slot) -> ALuint { return slot->id; }); + } } - else - { - std::vector ids; - ALsizei count{n}; - ids.reserve(static_cast(count)); - do { - ALeffectslot *slot{AllocEffectSlot(context)}; - ids.emplace_back(slot->id); - } while(--count); - std::copy(ids.cbegin(), ids.cend(), effectslots); + catch(std::exception& e) { + ERR("Exception allocating effectslot %zu of %d: %s\n", slots.size()+1, n, e.what()); + auto delete_effectslot = [context](ALeffectslot *slot) -> void + { FreeEffectSlot(context, slot); }; + std::for_each(slots.begin(), slots.end(), delete_effectslot); + throw al::context_error{AL_INVALID_OPERATION, "Exception allocating %d effectslots: %s", n, + e.what()}; } } +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); +} -AL_API DECL_FUNC2(void, alDeleteAuxiliaryEffectSlots, ALsizei, const ALuint*) +AL_API DECL_FUNC2(void, alDeleteAuxiliaryEffectSlots, ALsizei,n, const ALuint*,effectslots) FORCE_ALIGN void AL_APIENTRY alDeleteAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n, const ALuint *effectslots) noexcept -{ +try { if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Deleting %d effect slots", n); + throw al::context_error{AL_INVALID_VALUE, "Deleting %d effect slots", n}; if(n <= 0) UNLIKELY return; - std::lock_guard _{context->mEffectSlotLock}; + std::lock_guard slotlock{context->mEffectSlotLock}; if(n == 1) { - ALeffectslot *slot{LookupEffectSlot(context, effectslots[0])}; - if(!slot) UNLIKELY - { - context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslots[0]); - return; - } - if(ReadRef(slot->ref) != 0) UNLIKELY - { - context->setError(AL_INVALID_OPERATION, "Deleting in-use effect slot %u", - effectslots[0]); - return; - } + ALeffectslot *slot{LookupEffectSlot(context, *effectslots)}; + if(!slot) + throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", *effectslots}; + if(slot->ref.load(std::memory_order_relaxed) != 0) + throw al::context_error{AL_INVALID_OPERATION, "Deleting in-use effect slot %u", + *effectslots}; + RemoveActiveEffectSlots({&slot, 1u}, context); FreeEffectSlot(context, slot); } else { - auto slots = std::vector(static_cast(n)); - for(size_t i{0};i < slots.size();++i) - { - ALeffectslot *slot{LookupEffectSlot(context, effectslots[i])}; - if(!slot) UNLIKELY - { - context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslots[i]); - return; - } - if(ReadRef(slot->ref) != 0) UNLIKELY - { - context->setError(AL_INVALID_OPERATION, "Deleting in-use effect slot %u", - effectslots[i]); - return; - } - slots[i] = slot; - } - /* Remove any duplicates. */ - auto slots_end = slots.end(); - for(auto start=slots.begin()+1;start != slots_end;++start) + const al::span eids{effectslots, static_cast(n)}; + std::vector slots; + slots.reserve(eids.size()); + + auto lookupslot = [context](const ALuint eid) -> ALeffectslot* { - slots_end = std::remove(start, slots_end, *(start-1)); - if(start == slots_end) break; - } - slots.erase(slots_end, slots.end()); + ALeffectslot *slot{LookupEffectSlot(context, eid)}; + if(!slot) + throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", eid}; + if(slot->ref.load(std::memory_order_relaxed) != 0) + throw al::context_error{AL_INVALID_OPERATION, "Deleting in-use effect slot %u", + eid}; + return slot; + }; + std::transform(eids.cbegin(), eids.cend(), std::back_inserter(slots), lookupslot); /* All effectslots are valid, remove and delete them */ RemoveActiveEffectSlots(slots, context); - for(ALeffectslot *slot : slots) - FreeEffectSlot(context, slot); + + auto delete_effectslot = [context](const ALuint eid) -> void + { + if(ALeffectslot *slot{LookupEffectSlot(context, eid)}) + FreeEffectSlot(context, slot); + }; + std::for_each(eids.begin(), eids.end(), delete_effectslot); } } +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); +} -AL_API DECL_FUNC1(ALboolean, alIsAuxiliaryEffectSlot, ALuint) +AL_API DECL_FUNC1(ALboolean, alIsAuxiliaryEffectSlot, ALuint,effectslot) FORCE_ALIGN ALboolean AL_APIENTRY alIsAuxiliaryEffectSlotDirect(ALCcontext *context, ALuint effectslot) noexcept { - std::lock_guard _{context->mEffectSlotLock}; + std::lock_guard slotlock{context->mEffectSlotLock}; if(LookupEffectSlot(context, effectslot) != nullptr) return AL_TRUE; return AL_FALSE; } -AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid) noexcept +AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint) noexcept { ContextRef context{GetContextRef()}; if(!context) UNLIKELY return; - std::lock_guard _{context->mEffectSlotLock}; - ALeffectslot *slot{LookupEffectSlot(context.get(), slotid)}; - if(!slot) UNLIKELY - { - context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotid); - return; - } - if(slot->mState == SlotState::Playing) - return; - - slot->mPropsDirty = false; - slot->updateProps(context.get()); - - AddActiveEffectSlots({&slot, 1}, context.get()); - slot->mState = SlotState::Playing; + context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotPlaySOFT not supported"); } -AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids) noexcept +AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei, const ALuint*) noexcept { ContextRef context{GetContextRef()}; if(!context) UNLIKELY return; - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Playing %d effect slots", n); - if(n <= 0) UNLIKELY return; - - auto slots = std::vector(static_cast(n)); - std::lock_guard _{context->mEffectSlotLock}; - for(size_t i{0};i < slots.size();++i) - { - ALeffectslot *slot{LookupEffectSlot(context.get(), slotids[i])}; - if(!slot) UNLIKELY - { - context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotids[i]); - return; - } - - if(slot->mState != SlotState::Playing) - { - slot->mPropsDirty = false; - slot->updateProps(context.get()); - } - slots[i] = slot; - }; - - AddActiveEffectSlots(slots, context.get()); - for(auto slot : slots) - slot->mState = SlotState::Playing; + context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotPlayvSOFT not supported"); } -AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid) noexcept +AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint) noexcept { ContextRef context{GetContextRef()}; if(!context) UNLIKELY return; - std::lock_guard _{context->mEffectSlotLock}; - ALeffectslot *slot{LookupEffectSlot(context.get(), slotid)}; - if(!slot) UNLIKELY - { - context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotid); - return; - } - - RemoveActiveEffectSlots({&slot, 1}, context.get()); - slot->mState = SlotState::Stopped; + context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotStopSOFT not supported"); } -AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids) noexcept +AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei, const ALuint*) noexcept { ContextRef context{GetContextRef()}; if(!context) UNLIKELY return; - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Stopping %d effect slots", n); - if(n <= 0) UNLIKELY return; - - auto slots = std::vector(static_cast(n)); - std::lock_guard _{context->mEffectSlotLock}; - for(size_t i{0};i < slots.size();++i) - { - ALeffectslot *slot{LookupEffectSlot(context.get(), slotids[i])}; - if(!slot) UNLIKELY - { - context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotids[i]); - return; - } - - slots[i] = slot; - }; - - RemoveActiveEffectSlots(slots, context.get()); - for(auto slot : slots) - slot->mState = SlotState::Stopped; + context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotStopvSOFT not supported"); } -AL_API DECL_FUNC3(void, alAuxiliaryEffectSloti, ALuint, ALenum, ALint) +AL_API DECL_FUNC3(void, alAuxiliaryEffectSloti, ALuint,effectslot, ALenum,param, ALint,value) FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint value) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context, effectslot); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard slotlock{context->mEffectSlotLock}; + + ALeffectslot *slot{LookupEffectSlot(context, effectslot)}; if(!slot) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); + throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot}; ALeffectslot *target{}; - ALCdevice *device{}; ALenum err{}; switch(param) { case AL_EFFECTSLOT_EFFECT: - device = context->mALDevice.get(); - { - std::lock_guard ___{device->EffectLock}; - ALeffect *effect{value ? LookupEffect(device, static_cast(value)) : nullptr}; + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; + auto *effect = value ? LookupEffect(device, static_cast(value)) : nullptr; if(effect) 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); + throw al::context_error{AL_INVALID_VALUE, "Invalid effect ID %u", value}; err = slot->initEffect(0, AL_EFFECT_NULL, EffectProps{}, context); } } - if(err != AL_NO_ERROR) UNLIKELY - { - context->setError(err, "Effect initialization failed"); - return; - } + if(err != AL_NO_ERROR) + throw al::context_error{err, "Effect initialization failed"}; + if(slot->mState == SlotState::Initial) UNLIKELY { slot->mPropsDirty = false; @@ -570,21 +506,24 @@ FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, A slot->mState = SlotState::Playing; return; } - break; + UpdateProps(slot, context); + return; case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO: if(!(value == AL_TRUE || value == AL_FALSE)) - return context->setError(AL_INVALID_VALUE, - "Effect slot auxiliary send auto out of range"); - if(slot->AuxSendAuto == !!value) UNLIKELY - return; - slot->AuxSendAuto = !!value; - break; + throw al::context_error{AL_INVALID_VALUE, + "Effect slot auxiliary send auto out of range"}; + if(!(slot->AuxSendAuto == !!value)) LIKELY + { + slot->AuxSendAuto = !!value; + UpdateProps(slot, context); + } + return; case AL_EFFECTSLOT_TARGET_SOFT: target = LookupEffectSlot(context, static_cast(value)); if(value && !target) - return context->setError(AL_INVALID_VALUE, "Invalid effect slot target ID"); + throw al::context_error{AL_INVALID_VALUE, "Invalid effect slot target ID"}; if(slot->Target == target) UNLIKELY return; if(target) @@ -593,9 +532,9 @@ FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, A while(checker && checker != slot) checker = checker->Target; if(checker) - return context->setError(AL_INVALID_OPERATION, + throw al::context_error{AL_INVALID_OPERATION, "Setting target of effect slot ID %u to %u creates circular chain", slot->id, - target->id); + target->id}; } if(ALeffectslot *oldtarget{slot->Target}) @@ -612,61 +551,101 @@ FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, A if(target) IncrementRef(target->ref); slot->Target = target; - break; + UpdateProps(slot, context); + return; case AL_BUFFER: - device = context->mALDevice.get(); - - if(slot->mState == SlotState::Playing) - return context->setError(AL_INVALID_OPERATION, - "Setting buffer on playing effect slot %u", slot->id); - if(ALbuffer *buffer{slot->Buffer}) { - if(buffer->id == static_cast(value)) UNLIKELY + if(buffer->id == static_cast(value)) return; } - else if(value == 0) UNLIKELY + else if(value == 0) return; + if(slot->mState == SlotState::Playing) { - std::lock_guard ___{device->BufferLock}; + EffectStateFactory *factory{getFactoryByType(slot->Effect.Type)}; + assert(factory); + al::intrusive_ptr state{factory->create()}; + + auto *device = context->mALDevice.get(); + auto bufferlock = std::unique_lock{device->BufferLock}; ALbuffer *buffer{}; if(value) { buffer = LookupBuffer(device, static_cast(value)); - if(!buffer) return context->setError(AL_INVALID_VALUE, "Invalid buffer ID"); + if(!buffer) + throw al::context_error{AL_INVALID_VALUE, "Invalid buffer ID %u", value}; if(buffer->mCallback) - return context->setError(AL_INVALID_OPERATION, - "Callback buffer not valid for effects"); + throw al::context_error{AL_INVALID_OPERATION, + "Callback buffer not valid for effects"}; IncrementRef(buffer->ref); } + /* Stop the effect slot from processing while we switch buffers. */ + RemoveActiveEffectSlots({&slot, 1}, context); + if(ALbuffer *oldbuffer{slot->Buffer}) DecrementRef(oldbuffer->ref); slot->Buffer = buffer; + bufferlock.unlock(); + + state->mOutTarget = device->Dry.Buffer; + { + FPUCtl mixer_mode{}; + state->deviceUpdate(device, buffer); + } + slot->Effect.State = std::move(state); + + slot->mPropsDirty = false; + slot->updateProps(context); + AddActiveEffectSlots({&slot, 1}, context); + } + else + { + auto *device = context->mALDevice.get(); + auto bufferlock = std::unique_lock{device->BufferLock}; + ALbuffer *buffer{}; + if(value) + { + buffer = LookupBuffer(device, static_cast(value)); + if(!buffer) + throw al::context_error{AL_INVALID_VALUE, "Invalid buffer ID %u", value}; + if(buffer->mCallback) + throw al::context_error{AL_INVALID_OPERATION, + "Callback buffer not valid for effects"}; + + IncrementRef(buffer->ref); + } + + if(ALbuffer *oldbuffer{slot->Buffer}) + DecrementRef(oldbuffer->ref); + slot->Buffer = buffer; + bufferlock.unlock(); FPUCtl mixer_mode{}; auto *state = slot->Effect.State.get(); state->deviceUpdate(device, buffer); + slot->mPropsDirty = true; } - break; + return; case AL_EFFECTSLOT_STATE_SOFT: - return context->setError(AL_INVALID_OPERATION, "AL_EFFECTSLOT_STATE_SOFT is read-only"); - - default: - return context->setError(AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", - param); + throw al::context_error{AL_INVALID_OPERATION, "AL_EFFECTSLOT_STATE_SOFT is read-only"}; } - UpdateProps(slot, context); + + throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotiv, ALuint, ALenum, const ALint*) +AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotiv, ALuint,effectslot, ALenum,param, const ALint*,values) FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot, ALenum param, const ALint *values) noexcept -{ +try { switch(param) { case AL_EFFECTSLOT_EFFECT: @@ -674,121 +653,134 @@ FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotivDirect(ALCcontext *context, case AL_EFFECTSLOT_TARGET_SOFT: case AL_EFFECTSLOT_STATE_SOFT: case AL_BUFFER: - alAuxiliaryEffectSlotiDirect(context, effectslot, param, values[0]); + alAuxiliaryEffectSlotiDirect(context, effectslot, param, *values); return; } - std::lock_guard _{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context, effectslot); - if(!slot) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); + std::lock_guard slotlock{context->mEffectSlotLock}; + ALeffectslot *slot{LookupEffectSlot(context, effectslot)}; + if(!slot) + throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot}; switch(param) { default: - return context->setError(AL_INVALID_ENUM, - "Invalid effect slot integer-vector property 0x%04x", param); + break; } + throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot integer-vector property 0x%04x", + param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotf, ALuint, ALenum, ALfloat) +AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotf, ALuint,effectslot, ALenum,param, ALfloat,value) FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat value) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context, effectslot); - if(!slot) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard slotlock{context->mEffectSlotLock}; + + ALeffectslot *slot{LookupEffectSlot(context, effectslot)}; + if(!slot) + throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot}; switch(param) { case AL_EFFECTSLOT_GAIN: if(!(value >= 0.0f && value <= 1.0f)) - return context->setError(AL_INVALID_VALUE, "Effect slot gain out of range"); - if(slot->Gain == value) UNLIKELY - return; - slot->Gain = value; - break; - - default: - return context->setError(AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", - param); + throw al::context_error{AL_INVALID_VALUE, "Effect slot gain out of range"}; + if(!(slot->Gain == value)) LIKELY + { + slot->Gain = value; + UpdateProps(slot, context); + } + return; } - UpdateProps(slot, context); + + throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotfv, ALuint, ALenum, const ALfloat*) +AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotfv, ALuint,effectslot, ALenum,param, const ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot, ALenum param, const ALfloat *values) noexcept -{ +try { switch(param) { case AL_EFFECTSLOT_GAIN: - alAuxiliaryEffectSlotfDirect(context, effectslot, param, values[0]); + alAuxiliaryEffectSlotfDirect(context, effectslot, param, *values); return; } - std::lock_guard _{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context, effectslot); - if(!slot) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); + std::lock_guard slotlock{context->mEffectSlotLock}; + ALeffectslot *slot{LookupEffectSlot(context, effectslot)}; + if(!slot) + throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot}; switch(param) { default: - return context->setError(AL_INVALID_ENUM, - "Invalid effect slot float-vector property 0x%04x", param); + break; } + throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot float-vector property 0x%04x", + param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSloti, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSloti, ALuint,effectslot, ALenum,param, ALint*,value) FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint *value) noexcept -{ - std::lock_guard _{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context, effectslot); - if(!slot) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); +try { + std::lock_guard slotlock{context->mEffectSlotLock}; + ALeffectslot *slot{LookupEffectSlot(context, effectslot)}; + if(!slot) + throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot}; switch(param) { case AL_EFFECTSLOT_EFFECT: *value = static_cast(slot->EffectId); - break; + return; case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO: *value = slot->AuxSendAuto ? AL_TRUE : AL_FALSE; - break; + return; case AL_EFFECTSLOT_TARGET_SOFT: if(auto *target = slot->Target) *value = static_cast(target->id); else *value = 0; - break; + return; case AL_EFFECTSLOT_STATE_SOFT: *value = static_cast(slot->mState); - break; + return; case AL_BUFFER: if(auto *buffer = slot->Buffer) *value = static_cast(buffer->id); else *value = 0; - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param); + return; } + + throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotiv, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotiv, ALuint,effectslot, ALenum,param, ALint*,values) FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint *values) noexcept -{ +try { switch(param) { case AL_EFFECTSLOT_EFFECT: @@ -800,43 +792,47 @@ FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotivDirect(ALCcontext *contex return; } - std::lock_guard _{context->mEffectSlotLock}; + std::lock_guard slotlock{context->mEffectSlotLock}; ALeffectslot *slot = LookupEffectSlot(context, effectslot); - if(!slot) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); + if(!slot) + throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot}; switch(param) { default: - context->setError(AL_INVALID_ENUM, "Invalid effect slot integer-vector property 0x%04x", - param); + break; } + throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot integer-vector property 0x%04x", + param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotf, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotf, ALuint,effectslot, ALenum,param, ALfloat*,value) FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *value) noexcept -{ - std::lock_guard _{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context, effectslot); - if(!slot) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); +try { + std::lock_guard slotlock{context->mEffectSlotLock}; + ALeffectslot *slot{LookupEffectSlot(context, effectslot)}; + if(!slot) + throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot}; switch(param) { - case AL_EFFECTSLOT_GAIN: - *value = slot->Gain; - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param); + case AL_EFFECTSLOT_GAIN: *value = slot->Gain; return; } + + throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotfv, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotfv, ALuint,effectslot, ALenum,param, ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *values) noexcept -{ +try { switch(param) { case AL_EFFECTSLOT_GAIN: @@ -844,17 +840,21 @@ FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotfvDirect(ALCcontext *contex return; } - std::lock_guard _{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context, effectslot); - if(!slot) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); + std::lock_guard slotlock{context->mEffectSlotLock}; + ALeffectslot *slot{LookupEffectSlot(context, effectslot)}; + if(!slot) + throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot}; switch(param) { default: - context->setError(AL_INVALID_ENUM, "Invalid effect slot float-vector property 0x%04x", - param); + break; } + throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot float-vector property 0x%04x", + param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } @@ -880,12 +880,8 @@ ALeffectslot::~ALeffectslot() DecrementRef(Buffer->ref); Buffer = nullptr; - if(EffectSlotProps *props{mSlot->Update.exchange(nullptr)}) - { - TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n", - decltype(std::declval()){props}); - delete props; - } + if(auto *slot = mSlot->Update.exchange(nullptr, std::memory_order_relaxed)) + slot->State = nullptr; mSlot->mEffectState = nullptr; mSlot->InUse = false; @@ -905,8 +901,7 @@ ALenum ALeffectslot::initEffect(ALuint effectId, ALenum effectType, const Effect } al::intrusive_ptr state{factory->create()}; - ALCdevice *device{context->mALDevice.get()}; - std::unique_lock statelock{device->StateLock}; + auto *device = context->mALDevice.get(); state->mOutTarget = device->Dry.Buffer; { FPUCtl mixer_mode{}; @@ -923,7 +918,7 @@ ALenum ALeffectslot::initEffect(ALuint effectId, ALenum effectType, const Effect EffectId = effectId; /* Remove state references from old effect slot property updates. */ - EffectSlotProps *props{context->mFreeEffectslotProps.load()}; + EffectSlotProps *props{context->mFreeEffectSlotProps.load()}; while(props) { props->State = nullptr; @@ -933,20 +928,20 @@ ALenum ALeffectslot::initEffect(ALuint effectId, ALenum effectType, const Effect return AL_NO_ERROR; } -void ALeffectslot::updateProps(ALCcontext *context) +void ALeffectslot::updateProps(ALCcontext *context) const { /* Get an unused property container, or allocate a new one as needed. */ - EffectSlotProps *props{context->mFreeEffectslotProps.load(std::memory_order_relaxed)}; + EffectSlotProps *props{context->mFreeEffectSlotProps.load(std::memory_order_acquire)}; if(!props) - props = new EffectSlotProps{}; - else { - EffectSlotProps *next; - do { - next = props->next.load(std::memory_order_relaxed); - } while(context->mFreeEffectslotProps.compare_exchange_weak(props, next, - std::memory_order_seq_cst, std::memory_order_acquire) == 0); + context->allocEffectSlotProps(); + props = context->mFreeEffectSlotProps.load(std::memory_order_acquire); } + EffectSlotProps *next; + do { + next = props->next.load(std::memory_order_relaxed); + } while(!context->mFreeEffectSlotProps.compare_exchange_weak(props, next, + std::memory_order_acq_rel, std::memory_order_acquire)); /* Copy in current property values. */ props->Gain = Gain; @@ -965,35 +960,35 @@ void ALeffectslot::updateProps(ALCcontext *context) * freelist. */ props->State = nullptr; - AtomicReplaceHead(context->mFreeEffectslotProps, props); + AtomicReplaceHead(context->mFreeEffectSlotProps, props); } } void ALeffectslot::SetName(ALCcontext* context, ALuint id, std::string_view name) { - std::lock_guard _{context->mEffectSlotLock}; + std::lock_guard slotlock{context->mEffectSlotLock}; auto slot = LookupEffectSlot(context, id); - if(!slot) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", id); + if(!slot) + throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", id}; context->mEffectSlotNames.insert_or_assign(id, name); } void UpdateAllEffectSlotProps(ALCcontext *context) { - std::lock_guard _{context->mEffectSlotLock}; + std::lock_guard slotlock{context->mEffectSlotLock}; for(auto &sublist : context->mEffectSlotList) { uint64_t usemask{~sublist.FreeMask}; while(usemask) { - const int idx{al::countr_zero(usemask)}; + const auto idx = static_cast(al::countr_zero(usemask)); usemask &= ~(1_u64 << idx); - ALeffectslot *slot{sublist.EffectSlots + idx}; + auto &slot = (*sublist.EffectSlots)[idx]; - if(slot->mState != SlotState::Stopped && std::exchange(slot->mPropsDirty, false)) - slot->updateProps(context); + if(std::exchange(slot.mPropsDirty, false)) + slot.updateProps(context); } } } @@ -1007,15 +1002,15 @@ EffectSlotSubList::~EffectSlotSubList() while(usemask) { const int idx{al::countr_zero(usemask)}; - std::destroy_at(EffectSlots+idx); + std::destroy_at(al::to_address(EffectSlots->begin() + idx)); usemask &= ~(1_u64 << idx); } FreeMask = ~usemask; - al_free(EffectSlots); + SubListAllocator{}.deallocate(EffectSlots, 1); EffectSlots = nullptr; } -#ifdef ALSOFT_EAX +#if ALSOFT_EAX void ALeffectslot::eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index) { if(index >= EAX_MAX_FXSLOTS) @@ -1162,7 +1157,7 @@ void ALeffectslot::eax_fx_slot_set_defaults() eax_df_ = EaxDirtyFlags{}; } -void ALeffectslot::eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props) const +void ALeffectslot::eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props) { switch(call.get_property_id()) { @@ -1186,7 +1181,7 @@ void ALeffectslot::eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props) } } -void ALeffectslot::eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props) const +void ALeffectslot::eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props) { switch(call.get_property_id()) { @@ -1296,15 +1291,12 @@ void ALeffectslot::eax5_fx_slot_set_all(const EaxCall& call) bool ALeffectslot::eax_fx_slot_should_update_sources() const noexcept { - const auto dirty_bits = + static constexpr auto dirty_bits = eax_occlusion_dirty_bit | eax_occlusion_lf_ratio_dirty_bit | eax_flags_dirty_bit; - if((eax_df_ & dirty_bits) != EaxDirtyFlags{}) - return true; - - return false; + return (eax_df_ & dirty_bits) != EaxDirtyFlags{}; } // Returns `true` if all sources should be updated, or `false` otherwise. @@ -1490,7 +1482,7 @@ void ALeffectslot::eax_set_efx_slot_gain(ALfloat gain) if(gain < 0.0f || gain > 1.0f) ERR(EAX_PREFIX "Gain out of range (%f)\n", gain); - Gain = clampf(gain, 0.0f, 1.0f); + Gain = std::clamp(gain, 0.0f, 1.0f); mPropsDirty = true; #undef EAX_PREFIX @@ -1498,7 +1490,6 @@ void ALeffectslot::eax_set_efx_slot_gain(ALfloat gain) void ALeffectslot::EaxDeleter::operator()(ALeffectslot* effect_slot) { - assert(effect_slot); eax_delete_al_effect_slot(*effect_slot->eax_al_context_, *effect_slot); } @@ -1506,7 +1497,7 @@ EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context) { #define EAX_PREFIX "[EAX_MAKE_EFFECT_SLOT] " - std::unique_lock effect_slot_lock{context.mEffectSlotLock}; + std::lock_guard slotlock{context.mEffectSlotLock}; auto& device = *context.mALDevice; if(context.mNumEffectSlots == device.AuxiliaryEffectSlotMax) { @@ -1528,9 +1519,10 @@ void eax_delete_al_effect_slot(ALCcontext& context, ALeffectslot& effect_slot) { #define EAX_PREFIX "[EAX_DELETE_EFFECT_SLOT] " - std::lock_guard effect_slot_lock{context.mEffectSlotLock}; + std::lock_guard slotlock{context.mEffectSlotLock}; - if(ReadRef(effect_slot.ref) != 0) { + if(effect_slot.ref.load(std::memory_order_relaxed) != 0) + { ERR(EAX_PREFIX "Deleting in-use effect slot %u.\n", effect_slot.id); return; } diff --git a/3rdparty/openal/al/auxeffectslot.h b/3rdparty/openal/al/auxeffectslot.h index 1ad0ffc4d549..7a46fc3ea1f6 100644 --- a/3rdparty/openal/al/auxeffectslot.h +++ b/3rdparty/openal/al/auxeffectslot.h @@ -1,24 +1,26 @@ #ifndef AL_AUXEFFECTSLOT_H #define AL_AUXEFFECTSLOT_H +#include "config.h" + +#include #include -#include +#include #include +#include #include "AL/al.h" #include "AL/alc.h" -#include "AL/efx.h" -#include "alc/device.h" -#include "alc/effects/base.h" #include "almalloc.h" -#include "atomic.h" +#include "alnumeric.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "intrusive_ptr.h" -#include "vector.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include +#include "eax/api.h" #include "eax/call.h" #include "eax/effect.h" #include "eax/exception.h" @@ -27,10 +29,8 @@ #endif // ALSOFT_EAX struct ALbuffer; -struct ALeffect; -struct WetBuffer; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX class EaxFxSlotException : public EaxException { public: explicit EaxFxSlotException(const char* message) @@ -39,10 +39,8 @@ class EaxFxSlotException : public EaxException { }; #endif // ALSOFT_EAX -enum class SlotState : ALenum { - Initial = AL_INITIAL, - Playing = AL_PLAYING, - Stopped = AL_STOPPED, +enum class SlotState : bool { + Initial, Playing, }; struct ALeffectslot { @@ -52,18 +50,19 @@ struct ALeffectslot { ALeffectslot *Target{nullptr}; ALbuffer *Buffer{nullptr}; - struct { + struct EffectData { EffectSlotType Type{EffectSlotType::None}; - EffectProps Props{}; + EffectProps Props; al::intrusive_ptr State; - } Effect; + }; + EffectData Effect; bool mPropsDirty{true}; SlotState mState{SlotState::Initial}; - RefCount ref{0u}; + std::atomic ref{0u}; EffectSlot *mSlot{nullptr}; @@ -77,24 +76,21 @@ struct ALeffectslot { ALenum initEffect(ALuint effectId, ALenum effectType, const EffectProps &effectProps, ALCcontext *context); - void updateProps(ALCcontext *context); + void updateProps(ALCcontext *context) const; 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) - -#ifdef ALSOFT_EAX +#if ALSOFT_EAX public: void eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index); - EaxFxSlotIndexValue eax_get_index() const noexcept { return eax_fx_slot_index_; } - const EAX50FXSLOTPROPERTIES& eax_get_eax_fx_slot() const noexcept + [[nodiscard]] auto eax_get_index() const noexcept -> EaxFxSlotIndexValue { return eax_fx_slot_index_; } + [[nodiscard]] auto eax_get_eax_fx_slot() const noexcept -> const EAX50FXSLOTPROPERTIES& { return eax_; } // Returns `true` if all sources should be updated, or `false` otherwise. - bool eax_dispatch(const EaxCall& call) + [[nodiscard]] auto eax_dispatch(const EaxCall& call) -> bool { return call.is_get() ? eax_get(call) : eax_set(call); } void eax_commit(); @@ -198,6 +194,17 @@ struct ALeffectslot { } }; + struct Eax5FlagsValidator { + void operator()(unsigned long ulFlags) const + { + EaxRangeValidator{}( + "Flags", + ulFlags, + 0UL, + ~EAX50FXSLOTFLAGS_RESERVED); + } + }; + struct Eax5OcclusionValidator { void operator()(long lOcclusion) const { @@ -220,21 +227,13 @@ struct ALeffectslot { } }; - struct Eax5FlagsValidator { - void operator()(unsigned long ulFlags) const - { - EaxRangeValidator{}( - "Flags", - ulFlags, - 0UL, - ~EAX50FXSLOTFLAGS_RESERVED); - } - }; - struct Eax5AllValidator { void operator()(const EAX50FXSLOTPROPERTIES& all) const { - Eax4AllValidator{}(static_cast(all)); + Eax4GuidLoadEffectValidator{}(all.guidLoadEffect); + Eax4VolumeValidator{}(all.lVolume); + Eax4LockValidator{}(all.lLock); + Eax5FlagsValidator{}(all.ulFlags); Eax5OcclusionValidator{}(all.lOcclusion); Eax5OcclusionLfRatioValidator{}(all.flOcclusionLFRatio); } @@ -244,7 +243,7 @@ struct ALeffectslot { EaxFxSlotIndexValue eax_fx_slot_index_{}; int eax_version_{}; // Current EAX version. EaxDirtyFlags eax_df_{}; // Dirty flags for the current EAX version. - EaxEffectUPtr eax_effect_{}; + EaxEffectUPtr eax_effect_; Eax5State eax123_{}; // EAX1/EAX2/EAX3 state. Eax4State eax4_{}; // EAX4 state. Eax5State eax5_{}; // EAX5 state. @@ -282,14 +281,14 @@ struct ALeffectslot { dst = src; } - constexpr bool eax4_fx_slot_is_legacy() const noexcept + [[nodiscard]] constexpr auto eax4_fx_slot_is_legacy() const noexcept -> bool { return eax_fx_slot_index_ < 2; } void eax4_fx_slot_ensure_unlocked() const; - static ALenum eax_get_efx_effect_type(const GUID& guid); - const GUID& eax_get_eax_default_effect_guid() const noexcept; - long eax_get_eax_default_lock() const noexcept; + [[nodiscard]] static auto eax_get_efx_effect_type(const GUID& guid) -> ALenum; + [[nodiscard]] auto eax_get_eax_default_effect_guid() const noexcept -> const GUID&; + [[nodiscard]] auto eax_get_eax_default_lock() const noexcept -> long; void eax4_fx_slot_set_defaults(Eax4Props& props) noexcept; void eax5_fx_slot_set_defaults(Eax5Props& props) noexcept; @@ -298,8 +297,8 @@ struct ALeffectslot { void eax_fx_slot_set_current_defaults(); void eax_fx_slot_set_defaults(); - void eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props) const; - void eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props) const; + static void eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props); + static void eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props); void eax_fx_slot_get(const EaxCall& call) const; // Returns `true` if all sources should be updated, or `false` otherwise. bool eax_get(const EaxCall& call); @@ -312,7 +311,7 @@ struct ALeffectslot { void eax4_fx_slot_set_all(const EaxCall& call); void eax5_fx_slot_set_all(const EaxCall& call); - bool eax_fx_slot_should_update_sources() const noexcept; + [[nodiscard]] auto eax_fx_slot_should_update_sources() const noexcept -> bool; // Returns `true` if all sources should be updated, or `false` otherwise. bool eax4_fx_slot_set(const EaxCall& call); @@ -363,11 +362,27 @@ struct ALeffectslot { void UpdateAllEffectSlotProps(ALCcontext *context); -#ifdef ALSOFT_EAX +#if ALSOFT_EAX using EaxAlEffectSlotUPtr = std::unique_ptr; EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context); void eax_delete_al_effect_slot(ALCcontext& context, ALeffectslot& effect_slot); #endif // ALSOFT_EAX +struct EffectSlotSubList { + uint64_t FreeMask{~0_u64}; + gsl::owner*> EffectSlots{nullptr}; + + EffectSlotSubList() noexcept = default; + EffectSlotSubList(const EffectSlotSubList&) = delete; + EffectSlotSubList(EffectSlotSubList&& rhs) noexcept + : FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots} + { rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; } + ~EffectSlotSubList(); + + EffectSlotSubList& operator=(const EffectSlotSubList&) = delete; + EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; } +}; + #endif diff --git a/3rdparty/openal/al/buffer.cpp b/3rdparty/openal/al/buffer.cpp index 8ba874e4dd98..ac637c815f44 100644 --- a/3rdparty/openal/al/buffer.cpp +++ b/3rdparty/openal/al/buffer.cpp @@ -28,16 +28,16 @@ #include #include #include -#include #include #include #include #include #include -#include #include #include #include +#include +#include #include #include @@ -51,14 +51,16 @@ #include "alc/inprogext.h" #include "almalloc.h" #include "alnumeric.h" -#include "atomic.h" -#include "core/except.h" -#include "core/logging.h" +#include "alspan.h" +#include "core/device.h" +#include "core/resampler_limits.h" #include "core/voice.h" #include "direct_defs.h" +#include "error.h" +#include "intrusive_ptr.h" #include "opthelpers.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include #include "eax/globals.h" @@ -68,7 +70,9 @@ namespace { -std::optional AmbiLayoutFromEnum(ALenum layout) +using SubListAllocator = al::allocator>; + +constexpr auto AmbiLayoutFromEnum(ALenum layout) noexcept -> std::optional { switch(layout) { @@ -77,7 +81,7 @@ std::optional AmbiLayoutFromEnum(ALenum layout) } return std::nullopt; } -ALenum EnumFromAmbiLayout(AmbiLayout layout) +constexpr auto EnumFromAmbiLayout(AmbiLayout layout) -> ALenum { switch(layout) { @@ -87,7 +91,7 @@ ALenum EnumFromAmbiLayout(AmbiLayout layout) throw std::runtime_error{"Invalid AmbiLayout: "+std::to_string(int(layout))}; } -std::optional AmbiScalingFromEnum(ALenum scale) +constexpr auto AmbiScalingFromEnum(ALenum scale) noexcept -> std::optional { switch(scale) { @@ -97,7 +101,7 @@ std::optional AmbiScalingFromEnum(ALenum scale) } return std::nullopt; } -ALenum EnumFromAmbiScaling(AmbiScaling scale) +constexpr auto EnumFromAmbiScaling(AmbiScaling scale) -> ALenum { switch(scale) { @@ -109,8 +113,8 @@ ALenum EnumFromAmbiScaling(AmbiScaling scale) throw std::runtime_error{"Invalid AmbiScaling: "+std::to_string(int(scale))}; } -#ifdef ALSOFT_EAX -std::optional EaxStorageFromEnum(ALenum scale) +#if ALSOFT_EAX +constexpr auto EaxStorageFromEnum(ALenum scale) noexcept -> std::optional { switch(scale) { @@ -120,7 +124,7 @@ std::optional EaxStorageFromEnum(ALenum scale) } return std::nullopt; } -ALenum EnumFromEaxStorage(EaxStorage storage) +constexpr auto EnumFromEaxStorage(EaxStorage storage) -> ALenum { switch(storage) { @@ -132,8 +136,8 @@ ALenum EnumFromEaxStorage(EaxStorage storage) } -bool eax_x_ram_check_availability(const ALCdevice &device, const ALbuffer &buffer, - const ALuint newsize) noexcept +auto eax_x_ram_check_availability(const al::Device &device, const ALbuffer &buffer, + const ALuint newsize) noexcept -> bool { ALuint freemem{device.eax_x_ram_free_size}; /* If the buffer is currently in "hardware", add its memory to the free @@ -144,7 +148,7 @@ bool eax_x_ram_check_availability(const ALCdevice &device, const ALbuffer &buffe return freemem >= newsize; } -void eax_x_ram_apply(ALCdevice &device, ALbuffer &buffer) noexcept +void eax_x_ram_apply(al::Device &device, ALbuffer &buffer) noexcept { if(buffer.eax_x_ram_is_hardware) return; @@ -156,7 +160,7 @@ void eax_x_ram_apply(ALCdevice &device, ALbuffer &buffer) noexcept } } -void eax_x_ram_clear(ALCdevice& al_device, ALbuffer& al_buffer) +void eax_x_ram_clear(al::Device& al_device, ALbuffer& al_buffer) noexcept { if(al_buffer.eax_x_ram_is_hardware) al_device.eax_x_ram_free_size += al_buffer.OriginalSize; @@ -172,8 +176,8 @@ constexpr ALbitfieldSOFT INVALID_MAP_FLAGS{~unsigned(AL_MAP_READ_BIT_SOFT | AL_M AL_MAP_PERSISTENT_BIT_SOFT)}; -bool EnsureBuffers(ALCdevice *device, size_t needed) -{ +auto EnsureBuffers(al::Device *device, size_t needed) noexcept -> bool +try { 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(al::popcount(sublist.FreeMask)); })}; @@ -183,21 +187,19 @@ bool EnsureBuffers(ALCdevice *device, size_t needed) if(device->BufferList.size() >= 1<<25) UNLIKELY return false; - device->BufferList.emplace_back(); - auto sublist = device->BufferList.end() - 1; - sublist->FreeMask = ~0_u64; - sublist->Buffers = static_cast(al_calloc(alignof(ALbuffer), sizeof(ALbuffer)*64)); - if(!sublist->Buffers) UNLIKELY - { - device->BufferList.pop_back(); - return false; - } - count += 64; + BufferSubList sublist{}; + sublist.FreeMask = ~0_u64; + sublist.Buffers = SubListAllocator{}.allocate(1); + device->BufferList.emplace_back(std::move(sublist)); + count += std::tuple_size_v; } return true; } +catch(...) { + return false; +} -ALbuffer *AllocBuffer(ALCdevice *device) +auto AllocBuffer(al::Device *device) noexcept -> ALbuffer* { auto sublist = std::find_if(device->BufferList.begin(), device->BufferList.end(), [](const BufferSubList &entry) noexcept -> bool @@ -206,7 +208,7 @@ ALbuffer *AllocBuffer(ALCdevice *device) auto slidx = static_cast(al::countr_zero(sublist->FreeMask)); ASSUME(slidx < 64); - ALbuffer *buffer{al::construct_at(sublist->Buffers + slidx)}; + ALbuffer *buffer{al::construct_at(al::to_address(sublist->Buffers->begin() + slidx))}; /* Add 1 to avoid buffer ID 0. */ buffer->id = ((lidx<<6) | slidx) + 1; @@ -216,9 +218,9 @@ ALbuffer *AllocBuffer(ALCdevice *device) return buffer; } -void FreeBuffer(ALCdevice *device, ALbuffer *buffer) +void FreeBuffer(al::Device *device, ALbuffer *buffer) { -#ifdef ALSOFT_EAX +#if ALSOFT_EAX eax_x_ram_clear(*device, *buffer); #endif // ALSOFT_EAX @@ -233,7 +235,7 @@ void FreeBuffer(ALCdevice *device, ALbuffer *buffer) device->BufferList[lidx].FreeMask |= 1_u64 << slidx; } -inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) +auto LookupBuffer(al::Device *device, ALuint id) noexcept -> ALbuffer* { const size_t lidx{(id-1) >> 6}; const ALuint slidx{(id-1) & 0x3f}; @@ -243,11 +245,11 @@ inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) BufferSubList &sublist = device->BufferList[lidx]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Buffers + slidx; + return al::to_address(sublist.Buffers->begin() + slidx); } -ALuint SanitizeAlignment(FmtType type, ALuint align) +constexpr auto SanitizeAlignment(FmtType type, ALuint align) noexcept -> ALuint { if(align == 0) { @@ -267,34 +269,34 @@ ALuint SanitizeAlignment(FmtType type, ALuint align) if(type == FmtIMA4) { /* IMA4 block alignment must be a multiple of 8, plus 1. */ - if((align&7) == 1) return static_cast(align); + if((align&7) == 1) return align; return 0; } if(type == FmtMSADPCM) { /* MSADPCM block alignment must be a multiple of 2. */ - if((align&1) == 0) return static_cast(align); + if((align&1) == 0) return align; return 0; } - return static_cast(align); + return 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 std::byte *SrcData, +void LoadData(ALCcontext *context [[maybe_unused]], ALbuffer *ALBuf, ALsizei freq, ALuint size, + const FmtChannels DstChannels, const FmtType DstType, const al::span SrcData, ALbitfieldSOFT access) { - if(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0) UNLIKELY - return context->setError(AL_INVALID_OPERATION, "Modifying storage for in-use buffer %u", - ALBuf->id); + if(ALBuf->ref.load(std::memory_order_relaxed) != 0 || ALBuf->MappedAccess != 0) + throw al::context_error{AL_INVALID_OPERATION, "Modifying storage for in-use buffer %u", + ALBuf->id}; const ALuint unpackalign{ALBuf->UnpackAlign}; const ALuint align{SanitizeAlignment(DstType, unpackalign)}; - if(align < 1) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Invalid unpack alignment %u for %s samples", - unpackalign, NameFromFormat(DstType)); + if(align < 1) + throw al::context_error{AL_INVALID_VALUE, "Invalid unpack alignment %u for %s samples", + unpackalign, NameFromFormat(DstType)}; const ALuint ambiorder{IsBFormat(DstChannels) ? ALBuf->UnpackAmbiOrder : (IsUHJ(DstChannels) ? 1 : 0)}; @@ -302,12 +304,12 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, if((access&AL_PRESERVE_DATA_BIT_SOFT)) { /* Can only preserve data with the same format and alignment. */ - if(ALBuf->mChannels != DstChannels || ALBuf->mType != DstType) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Preserving data of mismatched format"); - if(ALBuf->mBlockAlign != align) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Preserving data of mismatched alignment"); - if(ALBuf->mAmbiOrder != ambiorder) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Preserving data of mismatched order"); + if(ALBuf->mChannels != DstChannels || ALBuf->mType != DstType) + throw al::context_error{AL_INVALID_VALUE, "Preserving data of mismatched format"}; + if(ALBuf->mBlockAlign != align) + throw al::context_error{AL_INVALID_VALUE, "Preserving data of mismatched alignment"}; + if(ALBuf->mAmbiOrder != ambiorder) + throw al::context_error{AL_INVALID_VALUE, "Preserving data of mismatched order"}; } /* Convert the size in bytes to blocks using the unpack block alignment. */ @@ -316,28 +318,28 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, ((DstType == FmtIMA4) ? (align-1)/2 + 4 : (DstType == FmtMSADPCM) ? (align-2)/2 + 7 : (align * BytesFromFmt(DstType)))}; - if((size%BlockSize) != 0) UNLIKELY - return context->setError(AL_INVALID_VALUE, + if((size%BlockSize) != 0) + throw al::context_error{AL_INVALID_VALUE, "Data size %d is not a multiple of frame size %d (%d unpack alignment)", - size, BlockSize, align); + size, BlockSize, align}; const ALuint blocks{size / BlockSize}; - if(blocks > std::numeric_limits::max()/align) UNLIKELY - return context->setError(AL_OUT_OF_MEMORY, - "Buffer size overflow, %d blocks x %d samples per block", blocks, align); - if(blocks > std::numeric_limits::max()/BlockSize) UNLIKELY - return context->setError(AL_OUT_OF_MEMORY, - "Buffer size overflow, %d frames x %d bytes per frame", blocks, BlockSize); + if(blocks > std::numeric_limits::max()/align) + throw al::context_error{AL_OUT_OF_MEMORY, + "Buffer size overflow, %d blocks x %d samples per block", blocks, align}; + if(blocks > std::numeric_limits::max()/BlockSize) + throw al::context_error{AL_OUT_OF_MEMORY, + "Buffer size overflow, %d frames x %d bytes per frame", blocks, BlockSize}; const size_t newsize{static_cast(blocks) * BlockSize}; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(ALBuf->eax_x_ram_mode == EaxStorage::Hardware) { - ALCdevice &device = *context->mALDevice; + auto &device = *context->mALDevice; if(!eax_x_ram_check_availability(device, *ALBuf, size)) - return context->setError(AL_OUT_OF_MEMORY, - "Out of X-RAM memory (avail: %u, needed: %u)", device.eax_x_ram_free_size, size); + throw al::context_error{AL_OUT_OF_MEMORY, + "Out of X-RAM memory (avail: %u, needed: %u)", device.eax_x_ram_free_size, size}; } #endif @@ -352,18 +354,18 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, 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())}; + const size_t tocopy{std::min(newdata.size(), ALBuf->mDataStorage.size())}; std::copy_n(ALBuf->mDataStorage.begin(), tocopy, newdata.begin()); } newdata.swap(ALBuf->mDataStorage); } ALBuf->mData = ALBuf->mDataStorage; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX eax_x_ram_clear(*context->mALDevice, *ALBuf); #endif - if(SrcData != nullptr && !ALBuf->mData.empty()) - std::copy_n(SrcData, blocks*BlockSize, ALBuf->mData.begin()); + if(!SrcData.empty() && !ALBuf->mData.empty()) + std::copy_n(SrcData.begin(), blocks*BlockSize, ALBuf->mData.begin()); ALBuf->mBlockAlign = (DstType == FmtIMA4 || DstType == FmtMSADPCM) ? align : 1; ALBuf->OriginalSize = size; @@ -382,26 +384,30 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, ALBuf->mLoopStart = 0; ALBuf->mLoopEnd = ALBuf->mSampleLen; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(eax_g_is_enabled && ALBuf->eax_x_ram_mode == EaxStorage::Hardware) eax_x_ram_apply(*context->mALDevice, *ALBuf); #endif } /** Prepares the buffer to use the specified callback, using the specified format. */ -void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, +void PrepareCallback(ALCcontext *context [[maybe_unused]], ALbuffer *ALBuf, ALsizei freq, const FmtChannels DstChannels, const FmtType DstType, ALBUFFERCALLBACKTYPESOFT callback, void *userptr) { - if(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0) UNLIKELY - return context->setError(AL_INVALID_OPERATION, "Modifying callback for in-use buffer %u", - ALBuf->id); + if(ALBuf->ref.load(std::memory_order_relaxed) != 0 || ALBuf->MappedAccess != 0) + throw al::context_error{AL_INVALID_OPERATION, "Modifying callback for in-use buffer %u", + ALBuf->id}; const ALuint ambiorder{IsBFormat(DstChannels) ? ALBuf->UnpackAmbiOrder : (IsUHJ(DstChannels) ? 1 : 0)}; const ALuint unpackalign{ALBuf->UnpackAlign}; const ALuint align{SanitizeAlignment(DstType, unpackalign)}; + if(align < 1) + throw al::context_error{AL_INVALID_VALUE, "Invalid unpack alignment %u for %s samples", + unpackalign, NameFromFormat(DstType)}; + const ALuint BlockSize{ChannelsFromFmt(DstChannels, ambiorder) * ((DstType == FmtIMA4) ? (align-1)/2 + 4 : (DstType == FmtMSADPCM) ? (align-2)/2 + 7 : @@ -420,7 +426,7 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, BufferVectorType(line_blocks*BlockSize).swap(ALBuf->mDataStorage); ALBuf->mData = ALBuf->mDataStorage; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX eax_x_ram_clear(*context->mALDevice, *ALBuf); #endif @@ -442,18 +448,18 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, } /** Prepares the buffer to use caller-specified storage. */ -void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, +void PrepareUserPtr(ALCcontext *context [[maybe_unused]], ALbuffer *ALBuf, ALsizei freq, 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", - ALBuf->id); + if(ALBuf->ref.load(std::memory_order_relaxed) != 0 || ALBuf->MappedAccess != 0) + throw al::context_error{AL_INVALID_OPERATION, "Modifying storage for in-use buffer %u", + ALBuf->id}; const ALuint unpackalign{ALBuf->UnpackAlign}; const ALuint align{SanitizeAlignment(DstType, unpackalign)}; - if(align < 1) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Invalid unpack alignment %u for %s samples", - unpackalign, NameFromFormat(DstType)); + if(align < 1) + throw al::context_error{AL_INVALID_VALUE, "Invalid unpack alignment %u for %s samples", + unpackalign, NameFromFormat(DstType)}; auto get_type_alignment = [](const FmtType type) noexcept -> ALuint { @@ -464,6 +470,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); @@ -475,8 +482,8 @@ void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, }; const auto typealign = get_type_alignment(DstType); if((reinterpret_cast(sdata) & (typealign-1)) != 0) - return context->setError(AL_INVALID_VALUE, "Pointer %p is misaligned for %s samples (%u)", - static_cast(sdata), NameFromFormat(DstType), typealign); + throw al::context_error{AL_INVALID_VALUE, "Pointer %p is misaligned for %s samples (%u)", + static_cast(sdata), NameFromFormat(DstType), typealign}; const ALuint ambiorder{IsBFormat(DstChannels) ? ALBuf->UnpackAmbiOrder : (IsUHJ(DstChannels) ? 1 : 0)}; @@ -487,34 +494,34 @@ void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ((DstType == FmtIMA4) ? (align-1)/2 + 4 : (DstType == FmtMSADPCM) ? (align-2)/2 + 7 : (align * BytesFromFmt(DstType)))}; - if((sdatalen%BlockSize) != 0) UNLIKELY - return context->setError(AL_INVALID_VALUE, + if((sdatalen%BlockSize) != 0) + throw al::context_error{AL_INVALID_VALUE, "Data size %u is not a multiple of frame size %u (%u unpack alignment)", - sdatalen, BlockSize, align); + sdatalen, BlockSize, align}; const ALuint blocks{sdatalen / BlockSize}; - if(blocks > std::numeric_limits::max()/align) UNLIKELY - return context->setError(AL_OUT_OF_MEMORY, - "Buffer size overflow, %d blocks x %d samples per block", blocks, align); - if(blocks > std::numeric_limits::max()/BlockSize) UNLIKELY - return context->setError(AL_OUT_OF_MEMORY, - "Buffer size overflow, %d frames x %d bytes per frame", blocks, BlockSize); + if(blocks > std::numeric_limits::max()/align) + throw al::context_error{AL_OUT_OF_MEMORY, + "Buffer size overflow, %d blocks x %d samples per block", blocks, align}; + if(blocks > std::numeric_limits::max()/BlockSize) + throw al::context_error{AL_OUT_OF_MEMORY, + "Buffer size overflow, %d frames x %d bytes per frame", blocks, BlockSize}; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(ALBuf->eax_x_ram_mode == EaxStorage::Hardware) { - ALCdevice &device = *context->mALDevice; + auto &device = *context->mALDevice; if(!eax_x_ram_check_availability(device, *ALBuf, sdatalen)) - return context->setError(AL_OUT_OF_MEMORY, + throw al::context_error{AL_OUT_OF_MEMORY, "Out of X-RAM memory (avail: %u, needed: %u)", device.eax_x_ram_free_size, - sdatalen); + sdatalen}; } #endif decltype(ALBuf->mDataStorage){}.swap(ALBuf->mDataStorage); - ALBuf->mData = {static_cast(sdata), sdatalen}; + ALBuf->mData = al::span{sdata, sdatalen}; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX eax_x_ram_clear(*context->mALDevice, *ALBuf); #endif @@ -534,7 +541,7 @@ void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALBuf->mLoopStart = 0; ALBuf->mLoopEnd = ALBuf->mSampleLen; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(ALBuf->eax_x_ram_mode == EaxStorage::Hardware) eax_x_ram_apply(*context->mALDevice, *ALBuf); #endif @@ -542,184 +549,180 @@ void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, struct DecompResult { FmtChannels channels; FmtType type; }; -std::optional DecomposeUserFormat(ALenum format) +auto DecomposeUserFormat(ALenum format) noexcept -> std::optional { struct FormatMap { ALenum format; - FmtChannels channels; - FmtType type; + DecompResult result; }; - static const std::array 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 }, - }}; - - for(const auto &fmt : UserFmtList) - { - if(fmt.format == format) - return DecompResult{fmt.channels, fmt.type}; - } + 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_I32, {FmtBFormat2D, FmtInt} }, + 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_BFORMAT2D_I32, {FmtBFormat3D, FmtInt} }, + 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_SOFT, {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_SOFT, {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_SOFT, {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} }, + }; + + auto iter = std::find_if(UserFmtList.cbegin(), UserFmtList.cend(), + [format](const FormatMap &fmt) noexcept { return fmt.format == format; }); + if(iter != UserFmtList.cend()) + return iter->result; return std::nullopt; } } // namespace -AL_API DECL_FUNC2(void, alGenBuffers, ALsizei, ALuint*) +AL_API DECL_FUNC2(void, alGenBuffers, ALsizei,n, ALuint*,buffers) FORCE_ALIGN void AL_APIENTRY alGenBuffersDirect(ALCcontext *context, ALsizei n, ALuint *buffers) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Generating %d buffers", n); +try { + if(n < 0) + throw al::context_error{AL_INVALID_VALUE, "Generating %d buffers", n}; if(n <= 0) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - if(!EnsureBuffers(device, static_cast(n))) - { - context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d buffer%s", n, (n==1)?"":"s"); - return; - } + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; - if(n == 1) LIKELY - { - /* Special handling for the easy and normal case. */ - ALbuffer *buffer{AllocBuffer(device)}; - buffers[0] = buffer->id; - } - else - { - /* Store the allocated buffer IDs in a separate local list, to avoid - * modifying the user storage in case of failure. - */ - std::vector ids; - ids.reserve(static_cast(n)); - do { - ALbuffer *buffer{AllocBuffer(device)}; - ids.emplace_back(buffer->id); - } while(--n); - std::copy(ids.begin(), ids.end(), buffers); - } + const al::span bids{buffers, static_cast(n)}; + if(!EnsureBuffers(device, bids.size())) + throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d buffer%s", n, + (n == 1) ? "" : "s"}; + + std::generate(bids.begin(), bids.end(), [device]{ return AllocBuffer(device)->id; }); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC2(void, alDeleteBuffers, ALsizei, const ALuint*) +AL_API DECL_FUNC2(void, alDeleteBuffers, ALsizei,n, const ALuint*,buffers) FORCE_ALIGN void AL_APIENTRY alDeleteBuffersDirect(ALCcontext *context, ALsizei n, const ALuint *buffers) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Deleting %d buffers", n); +try { + if(n < 0) + throw al::context_error{AL_INVALID_VALUE, "Deleting %d buffers", n}; if(n <= 0) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; /* First try to find any buffers that are invalid or in-use. */ - auto validate_buffer = [device, &context](const ALuint bid) -> bool + auto validate_buffer = [device](const ALuint bid) { - if(!bid) return true; + if(!bid) return; ALbuffer *ALBuf{LookupBuffer(device, bid)}; - if(!ALBuf) UNLIKELY - { - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", bid); - return false; - } - if(ReadRef(ALBuf->ref) != 0) UNLIKELY - { - context->setError(AL_INVALID_OPERATION, "Deleting in-use buffer %u", bid); - return false; - } - return true; + if(!ALBuf) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", bid}; + if(ALBuf->ref.load(std::memory_order_relaxed) != 0) + throw al::context_error{AL_INVALID_OPERATION, "Deleting in-use buffer %u", bid}; }; - const ALuint *buffers_end = buffers + n; - auto invbuf = std::find_if_not(buffers, buffers_end, validate_buffer); - if(invbuf != buffers_end) UNLIKELY return; + + const al::span bids{buffers, static_cast(n)}; + std::for_each(bids.begin(), bids.end(), validate_buffer); /* All good. Delete non-0 buffer IDs. */ auto delete_buffer = [device](const ALuint bid) -> void { - ALbuffer *buffer{bid ? LookupBuffer(device, bid) : nullptr}; - if(buffer) FreeBuffer(device, buffer); + if(ALbuffer *buffer{bid ? LookupBuffer(device, bid) : nullptr}) + FreeBuffer(device, buffer); }; - std::for_each(buffers, buffers_end, delete_buffer); + std::for_each(bids.begin(), bids.end(), delete_buffer); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC1(ALboolean, alIsBuffer, ALuint) +AL_API DECL_FUNC1(ALboolean, alIsBuffer, ALuint,buffer) FORCE_ALIGN ALboolean AL_APIENTRY alIsBufferDirect(ALCcontext *context, ALuint buffer) noexcept { - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; if(!buffer || LookupBuffer(device, buffer)) return AL_TRUE; return AL_FALSE; @@ -736,192 +739,194 @@ AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid 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) +AL_API DECL_FUNCEXT6(void, alBufferStorage,SOFT, ALuint,buffer, ALenum,format, const ALvoid*,data, ALsizei,size, ALsizei,freq, ALbitfieldSOFT,flags) 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 _{device->BufferLock}; - - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(size < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Negative storage size %d", size); - else if(freq < 1) UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid sample rate %d", freq); - else if((flags&INVALID_STORAGE_MASK) != 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid storage flags 0x%x", - flags&INVALID_STORAGE_MASK); - else if((flags&AL_MAP_PERSISTENT_BIT_SOFT) && !(flags&MAP_READ_WRITE_FLAGS)) UNLIKELY - context->setError(AL_INVALID_VALUE, - "Declaring persistently mapped storage without read or write access"); - else - { - auto usrfmt = DecomposeUserFormat(format); - if(!usrfmt) UNLIKELY - context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format); - else - { - LoadData(context, albuf, freq, static_cast(size), usrfmt->channels, - usrfmt->type, static_cast(data), flags); - } - } +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; + if(size < 0) + throw al::context_error{AL_INVALID_VALUE, "Negative storage size %d", size}; + if(freq < 1) + throw al::context_error{AL_INVALID_VALUE, "Invalid sample rate %d", freq}; + if((flags&INVALID_STORAGE_MASK) != 0) + throw al::context_error{AL_INVALID_VALUE, "Invalid storage flags 0x%x", + flags&INVALID_STORAGE_MASK}; + if((flags&AL_MAP_PERSISTENT_BIT_SOFT) && !(flags&MAP_READ_WRITE_FLAGS)) + throw al::context_error{AL_INVALID_VALUE, + "Declaring persistently mapped storage without read or write access"}; + + auto usrfmt = DecomposeUserFormat(format); + if(!usrfmt) + throw al::context_error{AL_INVALID_ENUM, "Invalid format 0x%04x", format}; + + auto bdata = static_cast(data); + LoadData(context, albuf, freq, static_cast(size), usrfmt->channels, usrfmt->type, + al::span{bdata, bdata ? static_cast(size) : 0u}, flags); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -DECL_FUNC5(void, alBufferDataStatic, ALuint, ALenum, ALvoid*, ALsizei, ALsizei) +FORCE_ALIGN DECL_FUNC5(void, alBufferDataStatic, ALuint,buffer, ALenum,format, ALvoid*,data, ALsizei,size, ALsizei,freq) FORCE_ALIGN void AL_APIENTRY alBufferDataStaticDirect(ALCcontext *context, const ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - if(size < 0) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Negative storage size %d", size); - if(freq < 1) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Invalid sample rate %d", freq); +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; + if(size < 0) + throw al::context_error{AL_INVALID_VALUE, "Negative storage size %d", size}; + if(freq < 1) + throw al::context_error{AL_INVALID_VALUE, "Invalid sample rate %d", freq}; auto usrfmt = DecomposeUserFormat(format); - if(!usrfmt) UNLIKELY - return context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format); + if(!usrfmt) + throw al::context_error{AL_INVALID_ENUM, "Invalid format 0x%04x", format}; PrepareUserPtr(context, albuf, freq, usrfmt->channels, usrfmt->type, static_cast(data), static_cast(size)); } +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); +} -AL_API DECL_FUNCEXT4(void*, alMapBuffer,SOFT, ALuint, ALsizei, ALsizei, ALbitfieldSOFT) +AL_API DECL_FUNCEXT4(void*, alMapBuffer,SOFT, ALuint,buffer, ALsizei,offset, ALsizei,length, ALbitfieldSOFT,access) FORCE_ALIGN void* AL_APIENTRY alMapBufferDirectSOFT(ALCcontext *context, ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if((access&INVALID_MAP_FLAGS) != 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid map flags 0x%x", access&INVALID_MAP_FLAGS); - else if(!(access&MAP_READ_WRITE_FLAGS)) UNLIKELY - context->setError(AL_INVALID_VALUE, "Mapping buffer %u without read or write access", - buffer); - else - { - ALbitfieldSOFT unavailable = (albuf->Access^access) & access; - if(ReadRef(albuf->ref) != 0 && !(access&AL_MAP_PERSISTENT_BIT_SOFT)) UNLIKELY - context->setError(AL_INVALID_OPERATION, - "Mapping in-use buffer %u without persistent mapping", buffer); - else if(albuf->MappedAccess != 0) UNLIKELY - context->setError(AL_INVALID_OPERATION, "Mapping already-mapped buffer %u", buffer); - else if((unavailable&AL_MAP_READ_BIT_SOFT)) UNLIKELY - context->setError(AL_INVALID_VALUE, - "Mapping buffer %u for reading without read access", buffer); - else if((unavailable&AL_MAP_WRITE_BIT_SOFT)) UNLIKELY - context->setError(AL_INVALID_VALUE, - "Mapping buffer %u for writing without write access", buffer); - else if((unavailable&AL_MAP_PERSISTENT_BIT_SOFT)) UNLIKELY - context->setError(AL_INVALID_VALUE, - "Mapping buffer %u persistently without persistent access", buffer); - else if(offset < 0 || length <= 0 - || static_cast(offset) >= albuf->OriginalSize - || static_cast(length) > albuf->OriginalSize - static_cast(offset)) - UNLIKELY - context->setError(AL_INVALID_VALUE, "Mapping invalid range %d+%d for buffer %u", - offset, length, buffer); - else - { - void *retval{albuf->mData.data() + offset}; - albuf->MappedAccess = access; - albuf->MappedOffset = offset; - albuf->MappedSize = length; - return retval; - } - } - +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; + if((access&INVALID_MAP_FLAGS) != 0) + throw al::context_error{AL_INVALID_VALUE, "Invalid map flags 0x%x", + access&INVALID_MAP_FLAGS}; + if(!(access&MAP_READ_WRITE_FLAGS)) + throw al::context_error{AL_INVALID_VALUE, "Mapping buffer %u without read or write access", + buffer}; + + const ALbitfieldSOFT unavailable{(albuf->Access^access) & access}; + if(albuf->ref.load(std::memory_order_relaxed) != 0 && !(access&AL_MAP_PERSISTENT_BIT_SOFT)) + throw al::context_error{AL_INVALID_OPERATION, + "Mapping in-use buffer %u without persistent mapping", buffer}; + if(albuf->MappedAccess != 0) + throw al::context_error{AL_INVALID_OPERATION, "Mapping already-mapped buffer %u", buffer}; + if((unavailable&AL_MAP_READ_BIT_SOFT)) + throw al::context_error{AL_INVALID_VALUE, + "Mapping buffer %u for reading without read access", buffer}; + if((unavailable&AL_MAP_WRITE_BIT_SOFT)) + throw al::context_error{AL_INVALID_VALUE, + "Mapping buffer %u for writing without write access", buffer}; + if((unavailable&AL_MAP_PERSISTENT_BIT_SOFT)) + throw al::context_error{AL_INVALID_VALUE, + "Mapping buffer %u persistently without persistent access", buffer}; + if(offset < 0 || length <= 0 || static_cast(offset) >= albuf->OriginalSize + || static_cast(length) > albuf->OriginalSize - static_cast(offset)) + throw al::context_error{AL_INVALID_VALUE, "Mapping invalid range %d+%d for buffer %u", + offset, length, buffer}; + + void *retval{albuf->mData.data() + offset}; + albuf->MappedAccess = access; + albuf->MappedOffset = offset; + albuf->MappedSize = length; + return retval; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); return nullptr; } -AL_API DECL_FUNCEXT1(void, alUnmapBuffer,SOFT, ALuint) +AL_API DECL_FUNCEXT1(void, alUnmapBuffer,SOFT, ALuint,buffer) FORCE_ALIGN void AL_APIENTRY alUnmapBufferDirectSOFT(ALCcontext *context, ALuint buffer) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(albuf->MappedAccess == 0) UNLIKELY - context->setError(AL_INVALID_OPERATION, "Unmapping unmapped buffer %u", buffer); - else - { - albuf->MappedAccess = 0; - albuf->MappedOffset = 0; - albuf->MappedSize = 0; - } +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; + if(albuf->MappedAccess == 0) + throw al::context_error{AL_INVALID_OPERATION, "Unmapping unmapped buffer %u", buffer}; + + albuf->MappedAccess = 0; + albuf->MappedOffset = 0; + albuf->MappedSize = 0; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNCEXT3(void, alFlushMappedBuffer,SOFT, ALuint, ALsizei, ALsizei) +AL_API DECL_FUNCEXT3(void, alFlushMappedBuffer,SOFT, ALuint,buffer, ALsizei,offset, ALsizei,length) FORCE_ALIGN void AL_APIENTRY alFlushMappedBufferDirectSOFT(ALCcontext *context, ALuint buffer, ALsizei offset, ALsizei length) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(!(albuf->MappedAccess&AL_MAP_WRITE_BIT_SOFT)) UNLIKELY - context->setError(AL_INVALID_OPERATION, "Flushing buffer %u while not mapped for writing", - buffer); - else if(offset < albuf->MappedOffset || length <= 0 +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; + if(!(albuf->MappedAccess&AL_MAP_WRITE_BIT_SOFT)) + throw al::context_error{AL_INVALID_OPERATION, + "Flushing buffer %u while not mapped for writing", buffer}; + if(offset < albuf->MappedOffset || length <= 0 || offset >= albuf->MappedOffset+albuf->MappedSize - || length > albuf->MappedOffset+albuf->MappedSize-offset) UNLIKELY - context->setError(AL_INVALID_VALUE, "Flushing invalid range %d+%d on buffer %u", offset, - length, buffer); - else - { - /* FIXME: Need to use some method of double-buffering for the mixer and - * app to hold separate memory, which can be safely 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); - } + || length > albuf->MappedOffset+albuf->MappedSize-offset) + throw al::context_error{AL_INVALID_VALUE, "Flushing invalid range %d+%d on buffer %u", + offset, length, buffer}; + + /* FIXME: Need to use some method of double-buffering for the mixer and 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); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNCEXT5(void, alBufferSubData,SOFT, ALuint, ALenum, const ALvoid*, ALsizei, ALsizei) +AL_API DECL_FUNCEXT5(void, alBufferSubData,SOFT, ALuint,buffer, ALenum,format, const ALvoid*,data, ALsizei,offset, ALsizei,length) FORCE_ALIGN void AL_APIENTRY alBufferSubDataDirectSOFT(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; auto usrfmt = DecomposeUserFormat(format); - if(!usrfmt) UNLIKELY - return context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format); + if(!usrfmt) + throw al::context_error{AL_INVALID_ENUM, "Invalid format 0x%04x", format}; const ALuint unpack_align{albuf->UnpackAlign}; const ALuint align{SanitizeAlignment(usrfmt->type, unpack_align)}; - if(align < 1) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Invalid unpack alignment %u", unpack_align); - if(usrfmt->channels != albuf->mChannels || usrfmt->type != albuf->mType) UNLIKELY - return context->setError(AL_INVALID_ENUM, "Unpacking data with mismatched format"); - if(align != albuf->mBlockAlign) UNLIKELY - return context->setError(AL_INVALID_VALUE, + if(align < 1) + throw al::context_error{AL_INVALID_VALUE, "Invalid unpack alignment %u", unpack_align}; + if(usrfmt->channels != albuf->mChannels || usrfmt->type != albuf->mType) + throw al::context_error{AL_INVALID_ENUM, "Unpacking data with mismatched format"}; + if(align != albuf->mBlockAlign) + throw al::context_error{AL_INVALID_VALUE, "Unpacking data with alignment %u does not match original alignment %u", align, - albuf->mBlockAlign); - if(albuf->isBFormat() && albuf->UnpackAmbiOrder != albuf->mAmbiOrder) UNLIKELY - return context->setError(AL_INVALID_VALUE, - "Unpacking data with mismatched ambisonic order"); - if(albuf->MappedAccess != 0) UNLIKELY - return context->setError(AL_INVALID_OPERATION, "Unpacking data into mapped buffer %u", - buffer); + albuf->mBlockAlign}; + if(albuf->isBFormat() && albuf->UnpackAmbiOrder != albuf->mAmbiOrder) + throw al::context_error{AL_INVALID_VALUE, + "Unpacking data with mismatched ambisonic order"}; + if(albuf->MappedAccess != 0) + throw al::context_error{AL_INVALID_OPERATION, "Unpacking data into mapped buffer %u", + buffer}; const ALuint num_chans{albuf->channelsFromFmt()}; const ALuint byte_align{ @@ -931,154 +936,178 @@ FORCE_ALIGN void AL_APIENTRY alBufferSubDataDirectSOFT(ALCcontext *context, ALui if(offset < 0 || length < 0 || static_cast(offset) > albuf->OriginalSize || static_cast(length) > albuf->OriginalSize-static_cast(offset)) - UNLIKELY - return context->setError(AL_INVALID_VALUE, "Invalid data sub-range %d+%d on buffer %u", - offset, length, buffer); - if((static_cast(offset)%byte_align) != 0) UNLIKELY - return context->setError(AL_INVALID_VALUE, + throw al::context_error{AL_INVALID_VALUE, "Invalid data sub-range %d+%d on buffer %u", + offset, length, buffer}; + if((static_cast(offset)%byte_align) != 0) + throw al::context_error{AL_INVALID_VALUE, "Sub-range offset %d is not a multiple of frame size %d (%d unpack alignment)", - offset, byte_align, align); - if((static_cast(length)%byte_align) != 0) UNLIKELY - return context->setError(AL_INVALID_VALUE, + offset, byte_align, align}; + if((static_cast(length)%byte_align) != 0) + throw al::context_error{AL_INVALID_VALUE, "Sub-range length %d is not a multiple of frame size %d (%d unpack alignment)", - length, byte_align, align); + length, byte_align, align}; - assert(al::to_underlying(usrfmt->type) == al::to_underlying(albuf->mType)); - memcpy(albuf->mData.data()+offset, data, static_cast(length)); + std::memcpy(albuf->mData.data()+offset, data, static_cast(length)); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alBufferf, ALuint, ALenum, ALfloat) +AL_API DECL_FUNC3(void, alBufferf, ALuint,buffer, ALenum,param, ALfloat,value) FORCE_ALIGN void AL_APIENTRY alBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param, - ALfloat /*value*/) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; + ALfloat value [[maybe_unused]]) noexcept +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; - if(LookupBuffer(device, buffer) == nullptr) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else switch(param) + if(LookupBuffer(device, buffer) == nullptr) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; + + switch(param) { default: - context->setError(AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param); + break; } + throw al::context_error{AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC5(void, alBuffer3f, ALuint, ALenum, ALfloat, ALfloat, ALfloat) +AL_API DECL_FUNC5(void, alBuffer3f, ALuint,buffer, ALenum,param, ALfloat,value1, ALfloat,value2, ALfloat,value3) FORCE_ALIGN void AL_APIENTRY alBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param, - ALfloat /*value1*/, ALfloat /*value2*/, ALfloat /*value3*/) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; + ALfloat value1 [[maybe_unused]], ALfloat value2 [[maybe_unused]], + ALfloat value3 [[maybe_unused]]) noexcept +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + if(LookupBuffer(device, buffer) == nullptr) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; - if(LookupBuffer(device, buffer) == nullptr) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else switch(param) + switch(param) { default: - context->setError(AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param); + break; } + throw al::context_error{AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alBufferfv, ALuint, ALenum, const ALfloat*) +AL_API DECL_FUNC3(void, alBufferfv, ALuint,buffer, ALenum,param, const ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param, const ALfloat *values) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - - if(LookupBuffer(device, buffer) == nullptr) 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) +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + if(LookupBuffer(device, buffer) == nullptr) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; + if(!values) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; + + switch(param) { default: - context->setError(AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param); + break; } + throw al::context_error{AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alBufferi, ALuint, ALenum, ALint) +AL_API DECL_FUNC3(void, alBufferi, ALuint,buffer, ALenum,param, ALint,value) FORCE_ALIGN void AL_APIENTRY alBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else switch(param) + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; + + switch(param) { case AL_UNPACK_BLOCK_ALIGNMENT_SOFT: - if(value < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid unpack block alignment %d", value); - else - albuf->UnpackAlign = static_cast(value); - break; + if(value < 0) + throw al::context_error{AL_INVALID_VALUE, "Invalid unpack block alignment %d", value}; + albuf->UnpackAlign = static_cast(value); + return; case AL_PACK_BLOCK_ALIGNMENT_SOFT: - if(value < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid pack block alignment %d", value); - else - albuf->PackAlign = static_cast(value); - break; + if(value < 0) + throw al::context_error{AL_INVALID_VALUE, "Invalid pack block alignment %d", value}; + albuf->PackAlign = static_cast(value); + return; case AL_AMBISONIC_LAYOUT_SOFT: - if(ReadRef(albuf->ref) != 0) UNLIKELY - context->setError(AL_INVALID_OPERATION, "Modifying in-use buffer %u's ambisonic layout", - buffer); - else if(const auto layout = AmbiLayoutFromEnum(value)) + if(albuf->ref.load(std::memory_order_relaxed) != 0) + throw al::context_error{AL_INVALID_OPERATION, + "Modifying in-use buffer %u's ambisonic layout", buffer}; + if(const auto layout = AmbiLayoutFromEnum(value)) + { albuf->mAmbiLayout = layout.value(); - else UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid unpack ambisonic layout %04x", value); - break; + return; + } + throw al::context_error{AL_INVALID_VALUE, "Invalid unpack ambisonic layout %04x", value}; case AL_AMBISONIC_SCALING_SOFT: - if(ReadRef(albuf->ref) != 0) UNLIKELY - context->setError(AL_INVALID_OPERATION, "Modifying in-use buffer %u's ambisonic scaling", - buffer); - else if(const auto scaling = AmbiScalingFromEnum(value)) + if(albuf->ref.load(std::memory_order_relaxed) != 0) + throw al::context_error{AL_INVALID_OPERATION, + "Modifying in-use buffer %u's ambisonic scaling", buffer}; + if(const auto scaling = AmbiScalingFromEnum(value)) + { albuf->mAmbiScaling = scaling.value(); - else UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid unpack ambisonic scaling %04x", value); - break; + return; + } + throw al::context_error{AL_INVALID_VALUE, "Invalid unpack ambisonic scaling %04x", value}; case AL_UNPACK_AMBISONIC_ORDER_SOFT: - if(value < 1 || value > 14) UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid unpack ambisonic order %d", value); - else - albuf->UnpackAmbiOrder = static_cast(value); - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param); + if(value < 1 || value > 14) + throw al::context_error{AL_INVALID_VALUE, "Invalid unpack ambisonic order %d", value}; + albuf->UnpackAmbiOrder = static_cast(value); + return; } + + throw al::context_error{AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC5(void, alBuffer3i, ALuint, ALenum, ALint, ALint, ALint) +AL_API DECL_FUNC5(void, alBuffer3i, ALuint,buffer, ALenum,param, ALint,value1, ALint,value2, ALint,value3) FORCE_ALIGN void AL_APIENTRY alBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param, - ALint /*value1*/, ALint /*value2*/, ALint /*value3*/) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; + ALint value1 [[maybe_unused]], ALint value2 [[maybe_unused]], ALint value3 [[maybe_unused]]) noexcept +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; - if(LookupBuffer(device, buffer) == nullptr) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else switch(param) + if(LookupBuffer(device, buffer) == nullptr) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; + + switch(param) { default: - context->setError(AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param); + break; } + throw al::context_error{AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alBufferiv, ALuint, ALenum, const ALint*) +AL_API DECL_FUNC3(void, alBufferiv, ALuint,buffer, ALenum,param, const ALint*,values) FORCE_ALIGN void AL_APIENTRY alBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param, const ALint *values) noexcept -{ - if(!values) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); +try { + if(!values) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; switch(param) { @@ -1087,85 +1116,95 @@ FORCE_ALIGN void AL_APIENTRY alBufferivDirect(ALCcontext *context, ALuint buffer case AL_AMBISONIC_LAYOUT_SOFT: case AL_AMBISONIC_SCALING_SOFT: case AL_UNPACK_AMBISONIC_ORDER_SOFT: - alBufferiDirect(context, buffer, param, values[0]); + alBufferiDirect(context, buffer, param, *values); return; } - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else switch(param) + switch(param) { case AL_LOOP_POINTS_SOFT: - if(ReadRef(albuf->ref) != 0) UNLIKELY - context->setError(AL_INVALID_OPERATION, "Modifying in-use buffer %u's loop points", - buffer); - else if(values[0] < 0 || values[0] >= values[1] - || static_cast(values[1]) > albuf->mSampleLen) UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid loop point range %d -> %d on buffer %u", - values[0], values[1], buffer); - else - { - albuf->mLoopStart = static_cast(values[0]); - albuf->mLoopEnd = static_cast(values[1]); - } - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x", param); + auto vals = al::span{values, 2_uz}; + if(albuf->ref.load(std::memory_order_relaxed) != 0) + throw al::context_error{AL_INVALID_OPERATION, + "Modifying in-use buffer %u's loop points", buffer}; + if(vals[0] < 0 || vals[0] >= vals[1] || static_cast(vals[1]) > albuf->mSampleLen) + throw al::context_error{AL_INVALID_VALUE, + "Invalid loop point range %d -> %d on buffer %u", vals[0], vals[1], buffer}; + + albuf->mLoopStart = static_cast(vals[0]); + albuf->mLoopEnd = static_cast(vals[1]); + return; } + + throw al::context_error{AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x", + param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alGetBufferf, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetBufferf, ALuint,buffer, ALenum,param, ALfloat*,value) FORCE_ALIGN void AL_APIENTRY alGetBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(!value) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; + if(!value) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; + + switch(param) { case AL_SEC_LENGTH_SOFT: *value = (albuf->mSampleRate < 1) ? 0.0f : (static_cast(albuf->mSampleLen) / static_cast(albuf->mSampleRate)); - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param); + return; } + + throw al::context_error{AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC5(void, alGetBuffer3f, ALuint, ALenum, ALfloat*, ALfloat*, ALfloat*) +AL_API DECL_FUNC5(void, alGetBuffer3f, ALuint,buffer, ALenum,param, ALfloat*,value1, ALfloat*,value2, ALfloat*,value3) FORCE_ALIGN void AL_APIENTRY alGetBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - - if(LookupBuffer(device, buffer) == nullptr) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(!value1 || !value2 || !value3) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + if(LookupBuffer(device, buffer) == nullptr) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; + if(!value1 || !value2 || !value3) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; + + switch(param) { default: - context->setError(AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param); + break; } + throw al::context_error{AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alGetBufferfv, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetBufferfv, ALuint,buffer, ALenum,param, ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alGetBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *values) noexcept -{ +try { switch(param) { case AL_SEC_LENGTH_SOFT: @@ -1173,106 +1212,121 @@ FORCE_ALIGN void AL_APIENTRY alGetBufferfvDirect(ALCcontext *context, ALuint buf return; } - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; - if(LookupBuffer(device, buffer) == nullptr) 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) + if(LookupBuffer(device, buffer) == nullptr) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; + if(!values) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; + + switch(param) { default: - context->setError(AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param); + break; } + throw al::context_error{AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alGetBufferi, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetBufferi, ALuint,buffer, ALenum,param, ALint*,value) FORCE_ALIGN void AL_APIENTRY alGetBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(!value) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; + if(!value) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; + + switch(param) { case AL_FREQUENCY: *value = static_cast(albuf->mSampleRate); - break; + return; case AL_BITS: *value = (albuf->mType == FmtIMA4 || albuf->mType == FmtMSADPCM) ? 4 : static_cast(albuf->bytesFromFmt() * 8); - break; + return; case AL_CHANNELS: *value = static_cast(albuf->channelsFromFmt()); - break; + return; case AL_SIZE: *value = albuf->mCallback ? 0 : static_cast(albuf->mData.size()); - break; + return; case AL_BYTE_LENGTH_SOFT: *value = static_cast(albuf->mSampleLen / albuf->mBlockAlign * albuf->blockSizeFromFmt()); - break; + return; case AL_SAMPLE_LENGTH_SOFT: *value = static_cast(albuf->mSampleLen); - break; + return; case AL_UNPACK_BLOCK_ALIGNMENT_SOFT: *value = static_cast(albuf->UnpackAlign); - break; + return; case AL_PACK_BLOCK_ALIGNMENT_SOFT: *value = static_cast(albuf->PackAlign); - break; + return; case AL_AMBISONIC_LAYOUT_SOFT: *value = EnumFromAmbiLayout(albuf->mAmbiLayout); - break; + return; case AL_AMBISONIC_SCALING_SOFT: *value = EnumFromAmbiScaling(albuf->mAmbiScaling); - break; + return; case AL_UNPACK_AMBISONIC_ORDER_SOFT: *value = static_cast(albuf->UnpackAmbiOrder); - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param); + return; } + + throw al::context_error{AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC5(void, alGetBuffer3i, ALuint, ALenum, ALint*, ALint*, ALint*) +AL_API DECL_FUNC5(void, alGetBuffer3i, ALuint,buffer, ALenum,param, ALint*,value1, ALint*,value2, ALint*,value3) FORCE_ALIGN void AL_APIENTRY alGetBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - if(LookupBuffer(device, buffer) == nullptr) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(!value1 || !value2 || !value3) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + if(LookupBuffer(device, buffer) == nullptr) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; + if(!value1 || !value2 || !value3) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; + + switch(param) { default: - context->setError(AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param); + break; } + throw al::context_error{AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alGetBufferiv, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetBufferiv, ALuint,buffer, ALenum,param, ALint*,values) FORCE_ALIGN void AL_APIENTRY alGetBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *values) noexcept -{ +try { switch(param) { case AL_FREQUENCY: @@ -1291,97 +1345,113 @@ FORCE_ALIGN void AL_APIENTRY alGetBufferivDirect(ALCcontext *context, ALuint buf return; } - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{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) + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; + if(!values) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; + + switch(param) { case AL_LOOP_POINTS_SOFT: - values[0] = static_cast(albuf->mLoopStart); - values[1] = static_cast(albuf->mLoopEnd); - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x", param); + auto vals = al::span{values, 2_uz}; + vals[0] = static_cast(albuf->mLoopStart); + vals[1] = static_cast(albuf->mLoopEnd); + return; } + + throw al::context_error{AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x", + param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNCEXT5(void, alBufferCallback,SOFT, ALuint, ALenum, ALsizei, ALBUFFERCALLBACKTYPESOFT, ALvoid*) +AL_API DECL_FUNCEXT5(void, alBufferCallback,SOFT, ALuint,buffer, ALenum,format, ALsizei,freq, ALBUFFERCALLBACKTYPESOFT,callback, ALvoid*,userptr) FORCE_ALIGN void AL_APIENTRY alBufferCallbackDirectSOFT(ALCcontext *context, ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(freq < 1) UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid sample rate %d", freq); - else if(callback == nullptr) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL callback"); - else - { - auto usrfmt = DecomposeUserFormat(format); - if(!usrfmt) UNLIKELY - context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format); - else - PrepareCallback(context, albuf, freq, usrfmt->channels, usrfmt->type, callback, - userptr); - } +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; + if(freq < 1) + throw al::context_error{AL_INVALID_VALUE, "Invalid sample rate %d", freq}; + if(callback == nullptr) + throw al::context_error{AL_INVALID_VALUE, "NULL callback"}; + + auto usrfmt = DecomposeUserFormat(format); + if(!usrfmt) + throw al::context_error{AL_INVALID_ENUM, "Invalid format 0x%04x", format}; + + PrepareCallback(context, albuf, freq, usrfmt->channels, usrfmt->type, callback, userptr); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNCEXT3(void, alGetBufferPtr,SOFT, ALuint, ALenum, ALvoid**) +AL_API DECL_FUNCEXT3(void, alGetBufferPtr,SOFT, ALuint,buffer, ALenum,param, ALvoid**,value) FORCE_ALIGN void AL_APIENTRY alGetBufferPtrDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(!value) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; + if(!value) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; + + switch(param) { case AL_BUFFER_CALLBACK_FUNCTION_SOFT: - *value = al::bit_cast(albuf->mCallback); - break; + *value = reinterpret_cast(albuf->mCallback); + return; case AL_BUFFER_CALLBACK_USER_PARAM_SOFT: *value = albuf->mUserData; - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer pointer property 0x%04x", param); + return; } + + throw al::context_error{AL_INVALID_ENUM, "Invalid buffer pointer property 0x%04x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNCEXT5(void, alGetBuffer3Ptr,SOFT, ALuint, ALenum, ALvoid**, ALvoid**, ALvoid**) +AL_API DECL_FUNCEXT5(void, alGetBuffer3Ptr,SOFT, ALuint,buffer, ALenum,param, ALvoid**,value1, ALvoid**,value2, ALvoid**,value3) FORCE_ALIGN void AL_APIENTRY alGetBuffer3PtrDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - if(LookupBuffer(device, buffer) == nullptr) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(!value1 || !value2 || !value3) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + if(LookupBuffer(device, buffer) == nullptr) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; + if(!value1 || !value2 || !value3) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; + + switch(param) { default: - context->setError(AL_INVALID_ENUM, "Invalid buffer 3-pointer property 0x%04x", param); + break; } + throw al::context_error{AL_INVALID_ENUM, "Invalid buffer 3-pointer property 0x%04x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNCEXT3(void, alGetBufferPtrv,SOFT, ALuint, ALenum, ALvoid**) +AL_API DECL_FUNCEXT3(void, alGetBufferPtrv,SOFT, ALuint,buffer, ALenum,param, ALvoid**,values) FORCE_ALIGN void AL_APIENTRY alGetBufferPtrvDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **values) noexcept -{ +try { switch(param) { case AL_BUFFER_CALLBACK_FUNCTION_SOFT: @@ -1390,17 +1460,24 @@ FORCE_ALIGN void AL_APIENTRY alGetBufferPtrvDirectSOFT(ALCcontext *context, ALui return; } - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - if(LookupBuffer(device, buffer) == nullptr) 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) + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + if(LookupBuffer(device, buffer) == nullptr) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; + if(!values) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; + + switch(param) { default: - context->setError(AL_INVALID_ENUM, "Invalid buffer pointer-vector property 0x%04x", param); + break; } + throw al::context_error{AL_INVALID_ENUM, "Invalid buffer pointer-vector property 0x%04x", + param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } @@ -1444,12 +1521,12 @@ AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum /*format*/) no void ALbuffer::SetName(ALCcontext *context, ALuint id, std::string_view name) { - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; auto buffer = LookupBuffer(device, id); - if(!buffer) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", id); + if(!buffer) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", id}; device->mBufferNames.insert_or_assign(id, name); } @@ -1464,66 +1541,48 @@ BufferSubList::~BufferSubList() while(usemask) { const int idx{al::countr_zero(usemask)}; - std::destroy_at(Buffers+idx); + std::destroy_at(al::to_address(Buffers->begin() + idx)); usemask &= ~(1_u64 << idx); } FreeMask = ~usemask; - al_free(Buffers); + SubListAllocator{}.deallocate(Buffers, 1); Buffers = nullptr; } -#ifdef ALSOFT_EAX -FORCE_ALIGN DECL_FUNC3(ALboolean, EAXSetBufferMode, ALsizei, const ALuint*, ALint) +#if ALSOFT_EAX +FORCE_ALIGN DECL_FUNC3(ALboolean, EAXSetBufferMode, ALsizei,n, const ALuint*,buffers, ALint,value) FORCE_ALIGN ALboolean AL_APIENTRY EAXSetBufferModeDirect(ALCcontext *context, ALsizei n, const ALuint *buffers, ALint value) noexcept -{ -#define EAX_PREFIX "[EAXSetBufferMode] " - +try { if(!eax_g_is_enabled) - { - context->setError(AL_INVALID_OPERATION, EAX_PREFIX "%s", "EAX not enabled."); - return AL_FALSE; - } + throw al::context_error{AL_INVALID_OPERATION, "EAX not enabled"}; const auto storage = EaxStorageFromEnum(value); if(!storage) - { - context->setError(AL_INVALID_ENUM, EAX_PREFIX "Unsupported X-RAM mode 0x%x", value); - return AL_FALSE; - } + throw al::context_error{AL_INVALID_ENUM, "Unsupported X-RAM mode 0x%x", value}; if(n == 0) return AL_TRUE; if(n < 0) - { - context->setError(AL_INVALID_VALUE, EAX_PREFIX "Buffer count %d out of range", n); - return AL_FALSE; - } - + throw al::context_error{AL_INVALID_VALUE, "Buffer count %d out of range", n}; if(!buffers) - { - context->setError(AL_INVALID_VALUE, EAX_PREFIX "%s", "Null AL buffers"); - return AL_FALSE; - } + throw al::context_error{AL_INVALID_VALUE, "Null AL buffers"}; auto device = context->mALDevice.get(); - std::lock_guard device_lock{device->BufferLock}; + std::lock_guard devlock{device->BufferLock}; /* Special-case setting a single buffer, to avoid extraneous allocations. */ if(n == 1) { - const auto bufid = buffers[0]; + const auto bufid = *buffers; if(bufid == AL_NONE) return AL_TRUE; const auto buffer = LookupBuffer(device, bufid); - if(!buffer) UNLIKELY - { - ERR(EAX_PREFIX "Invalid buffer ID %u.\n", bufid); - return AL_FALSE; - } + if(!buffer) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", bufid}; /* TODO: Is the store location allowed to change for in-use buffers, or * only when not set/queued on a source? @@ -1532,13 +1591,10 @@ FORCE_ALIGN ALboolean AL_APIENTRY EAXSetBufferModeDirect(ALCcontext *context, AL if(*storage == EaxStorage::Hardware) { if(!buffer->eax_x_ram_is_hardware - && buffer->OriginalSize > device->eax_x_ram_free_size) UNLIKELY - { - 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; - } + && buffer->OriginalSize > device->eax_x_ram_free_size) + throw al::context_error{AL_OUT_OF_MEMORY, + "Out of X-RAM memory (need: %u, avail: %u)", buffer->OriginalSize, + device->eax_x_ram_free_size}; eax_x_ram_apply(*device, *buffer); } @@ -1550,18 +1606,14 @@ FORCE_ALIGN ALboolean AL_APIENTRY EAXSetBufferModeDirect(ALCcontext *context, AL /* Validate the buffers. */ std::unordered_set buflist; - for(auto i = 0;i < n;++i) + for(const ALuint bufid : al::span{buffers, static_cast(n)}) { - const auto bufid = buffers[i]; if(bufid == AL_NONE) continue; const auto buffer = LookupBuffer(device, bufid); - if(!buffer) UNLIKELY - { - ERR(EAX_PREFIX "Invalid buffer ID %u.\n", bufid); - return AL_FALSE; - } + if(!buffer) + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", bufid}; /* TODO: Is the store location allowed to change for in-use buffers, or * only when not set/queued on a source? @@ -1577,22 +1629,16 @@ FORCE_ALIGN ALboolean AL_APIENTRY EAXSetBufferModeDirect(ALCcontext *context, AL { if(!buffer->eax_x_ram_is_hardware) { - if(std::numeric_limits::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; - } + if(std::numeric_limits::max() - buffer->OriginalSize < total_needed) + throw al::context_error{AL_OUT_OF_MEMORY, "Size overflow (%u + %zu)", + buffer->OriginalSize, total_needed}; + 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; - } + throw al::context_error{AL_OUT_OF_MEMORY, "Out of X-RAM memory (need: %zu, avail: %u)", + total_needed, device->eax_x_ram_free_size}; } /* Update the mode. */ @@ -1606,41 +1652,35 @@ FORCE_ALIGN ALboolean AL_APIENTRY EAXSetBufferModeDirect(ALCcontext *context, AL } return AL_TRUE; - -#undef EAX_PREFIX +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "[EAXSetBufferMode] %s", e.what()); + return AL_FALSE; } -FORCE_ALIGN DECL_FUNC2(ALenum, EAXGetBufferMode, ALuint, ALint*) +FORCE_ALIGN DECL_FUNC2(ALenum, EAXGetBufferMode, ALuint,buffer, ALint*,pReserved) FORCE_ALIGN ALenum AL_APIENTRY EAXGetBufferModeDirect(ALCcontext *context, ALuint buffer, ALint *pReserved) noexcept -{ -#define EAX_PREFIX "[EAXGetBufferMode] " +try { if(!eax_g_is_enabled) - { - context->setError(AL_INVALID_OPERATION, EAX_PREFIX "%s", "EAX not enabled."); - return AL_NONE; - } + throw al::context_error{AL_INVALID_OPERATION, "EAX not enabled."}; if(pReserved) - { - context->setError(AL_INVALID_VALUE, EAX_PREFIX "%s", "Non-null reserved parameter"); - return AL_NONE; - } + throw al::context_error{AL_INVALID_VALUE, "Non-null reserved parameter"}; auto device = context->mALDevice.get(); - std::lock_guard device_lock{device->BufferLock}; + std::lock_guard devlock{device->BufferLock}; const auto al_buffer = LookupBuffer(device, buffer); if(!al_buffer) - { - context->setError(AL_INVALID_NAME, EAX_PREFIX "Invalid buffer ID %u", buffer); - return AL_NONE; - } + throw al::context_error{AL_INVALID_NAME, "Invalid buffer ID %u", buffer}; return EnumFromEaxStorage(al_buffer->eax_x_ram_mode); - -#undef EAX_PREFIX +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "[EAXGetBufferMode] %s", e.what()); + return AL_NONE; } #endif // ALSOFT_EAX diff --git a/3rdparty/openal/al/buffer.h b/3rdparty/openal/al/buffer.h index f936cf98493f..187bc6bd1983 100644 --- a/3rdparty/openal/al/buffer.h +++ b/3rdparty/openal/al/buffer.h @@ -1,21 +1,25 @@ #ifndef AL_BUFFER_H #define AL_BUFFER_H +#include "config.h" + +#include #include #include +#include #include +#include #include "AL/al.h" +#include "AL/alc.h" #include "alc/inprogext.h" #include "almalloc.h" -#include "atomic.h" +#include "alnumeric.h" #include "core/buffer_storage.h" #include "vector.h" -#ifdef ALSOFT_EAX -#include "eax/x_ram.h" - +#if ALSOFT_EAX enum class EaxStorage : uint8_t { Automatic, Accessible, @@ -43,19 +47,34 @@ struct ALbuffer : public BufferStorage { ALuint mLoopEnd{0u}; /* Number of times buffer was attached to a source (deletion can only occur when 0) */ - RefCount ref{0u}; + std::atomic ref{0u}; /* Self ID */ ALuint id{0}; static void SetName(ALCcontext *context, ALuint id, std::string_view name); - DISABLE_ALLOC() + DISABLE_ALLOC -#ifdef ALSOFT_EAX +#if ALSOFT_EAX EaxStorage eax_x_ram_mode{EaxStorage::Automatic}; bool eax_x_ram_is_hardware{}; #endif // ALSOFT_EAX }; +struct BufferSubList { + uint64_t FreeMask{~0_u64}; + gsl::owner*> Buffers{nullptr}; + + BufferSubList() noexcept = default; + BufferSubList(const BufferSubList&) = delete; + BufferSubList(BufferSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Buffers{rhs.Buffers} + { rhs.FreeMask = ~0_u64; rhs.Buffers = nullptr; } + ~BufferSubList(); + + BufferSubList& operator=(const BufferSubList&) = delete; + BufferSubList& operator=(BufferSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(Buffers, rhs.Buffers); return *this; } +}; + #endif diff --git a/3rdparty/openal/al/debug.cpp b/3rdparty/openal/al/debug.cpp index f5914767a21e..8637c0505915 100644 --- a/3rdparty/openal/al/debug.cpp +++ b/3rdparty/openal/al/debug.cpp @@ -4,29 +4,44 @@ #include #include +#include #include +#include #include #include -#include #include #include +#include +#include #include #include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" #include "alc/context.h" -#include "alc/inprogext.h" +#include "alc/device.h" +#include "alnumeric.h" #include "alspan.h" +#include "alstring.h" #include "auxeffectslot.h" #include "buffer.h" #include "core/logging.h" +#include "core/voice.h" #include "direct_defs.h" #include "effect.h" +#include "error.h" #include "filter.h" +#include "intrusive_ptr.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"); @@ -40,7 +55,7 @@ constexpr auto make_array_sequence() { return make_array_sequence(std::make_integer_sequence{}); } -constexpr std::optional GetDebugSource(ALenum source) noexcept +constexpr auto GetDebugSource(ALenum source) noexcept -> std::optional { switch(source) { @@ -53,7 +68,7 @@ constexpr std::optional GetDebugSource(ALenum source) noexcept return std::nullopt; } -constexpr std::optional GetDebugType(ALenum type) noexcept +constexpr auto GetDebugType(ALenum type) noexcept -> std::optional { switch(type) { @@ -70,7 +85,7 @@ constexpr std::optional GetDebugType(ALenum type) noexcept return std::nullopt; } -constexpr std::optional GetDebugSeverity(ALenum severity) noexcept +constexpr auto GetDebugSeverity(ALenum severity) noexcept -> std::optional { switch(severity) { @@ -83,7 +98,7 @@ constexpr std::optional GetDebugSeverity(ALenum severity) noexcep } -ALenum GetDebugSourceEnum(DebugSource source) +constexpr auto GetDebugSourceEnum(DebugSource source) -> ALenum { switch(source) { @@ -96,7 +111,7 @@ ALenum GetDebugSourceEnum(DebugSource source) throw std::runtime_error{"Unexpected debug source value "+std::to_string(al::to_underlying(source))}; } -ALenum GetDebugTypeEnum(DebugType type) +constexpr auto GetDebugTypeEnum(DebugType type) -> ALenum { switch(type) { @@ -113,7 +128,7 @@ ALenum GetDebugTypeEnum(DebugType type) throw std::runtime_error{"Unexpected debug type value "+std::to_string(al::to_underlying(type))}; } -ALenum GetDebugSeverityEnum(DebugSeverity severity) +constexpr auto GetDebugSeverityEnum(DebugSeverity severity) -> ALenum { switch(severity) { @@ -126,7 +141,7 @@ ALenum GetDebugSeverityEnum(DebugSeverity severity) } -const char *GetDebugSourceName(DebugSource source) +constexpr auto GetDebugSourceName(DebugSource source) noexcept -> const char* { switch(source) { @@ -139,7 +154,7 @@ const char *GetDebugSourceName(DebugSource source) return ""; } -const char *GetDebugTypeName(DebugType type) +constexpr auto GetDebugTypeName(DebugType type) noexcept -> const char* { switch(type) { @@ -156,7 +171,7 @@ const char *GetDebugTypeName(DebugType type) return ""; } -const char *GetDebugSeverityName(DebugSeverity severity) +constexpr auto GetDebugSeverityName(DebugSeverity severity) noexcept -> const char* { switch(severity) { @@ -174,13 +189,13 @@ const char *GetDebugSeverityName(DebugSeverity severity) void ALCcontext::sendDebugMessage(std::unique_lock &debuglock, DebugSource source, DebugType type, ALuint id, DebugSeverity severity, std::string_view message) { - if(!mDebugEnabled.load()) UNLIKELY + if(!mDebugEnabled.load(std::memory_order_relaxed)) UNLIKELY return; if(message.length() >= MaxDebugMessageLength) UNLIKELY { ERR("Debug message too long (%zu >= %d):\n-> %.*s\n", message.length(), - MaxDebugMessageLength, static_cast(message.length()), message.data()); + MaxDebugMessageLength, al::sizei(message), message.data()); return; } @@ -221,77 +236,79 @@ void ALCcontext::sendDebugMessage(std::unique_lock &debuglock, Debug " Severity: %s\n" " Message: \"%.*s\"\n", GetDebugSourceName(source), GetDebugTypeName(type), id, - GetDebugSeverityName(severity), static_cast(message.length()), - message.data()); + GetDebugSeverityName(severity), al::sizei(message), message.data()); } } -FORCE_ALIGN DECL_FUNCEXT2(void, alDebugMessageCallback,EXT, ALDEBUGPROCEXT, void*) +FORCE_ALIGN DECL_FUNCEXT2(void, alDebugMessageCallback,EXT, ALDEBUGPROCEXT,callback, void*,userParam) FORCE_ALIGN void AL_APIENTRY alDebugMessageCallbackDirectEXT(ALCcontext *context, ALDEBUGPROCEXT callback, void *userParam) noexcept { - std::lock_guard _{context->mDebugCbLock}; + std::lock_guard debuglock{context->mDebugCbLock}; context->mDebugCb = callback; context->mDebugParam = userParam; } -FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageInsert,EXT, ALenum, ALenum, ALuint, ALenum, ALsizei, const ALchar*) +FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageInsert,EXT, ALenum,source, ALenum,type, ALuint,id, ALenum,severity, ALsizei,length, const ALchar*,message) FORCE_ALIGN void AL_APIENTRY alDebugMessageInsertDirectEXT(ALCcontext *context, ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) noexcept -{ +try { if(!context->mContextFlags.test(ContextFlags::DebugBit)) return; - if(!message) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Null message pointer"); + if(!message) + throw al::context_error{AL_INVALID_VALUE, "Null message pointer"}; auto msgview = (length < 0) ? std::string_view{message} : std::string_view{message, static_cast(length)}; - if(msgview.length() >= MaxDebugMessageLength) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Debug message too long (%zu >= %d)", - msgview.length(), MaxDebugMessageLength); + if(msgview.size() >= MaxDebugMessageLength) + throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%zu >= %d)", + msgview.size(), MaxDebugMessageLength}; auto dsource = GetDebugSource(source); if(!dsource) - return context->setError(AL_INVALID_ENUM, "Invalid debug source 0x%04x", source); + throw al::context_error{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); + throw al::context_error{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); + throw al::context_error{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); + throw al::context_error{AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity}; context->debugMessage(*dsource, *dtype, id, *dseverity, msgview); } +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); +} -FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageControl,EXT, ALenum, ALenum, ALenum, ALsizei, const ALuint*, ALboolean) +FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageControl,EXT, ALenum,source, ALenum,type, ALenum,severity, ALsizei,count, const ALuint*,ids, ALboolean,enable) FORCE_ALIGN void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) noexcept -{ +try { if(count > 0) { if(!ids) - return context->setError(AL_INVALID_VALUE, "IDs is null with non-0 count"); + throw al::context_error{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"); + throw al::context_error{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"); + throw al::context_error{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"); + throw al::context_error{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); + throw al::context_error{AL_INVALID_ENUM, "Invalid debug enable %d", enable}; static constexpr size_t ElemCount{DebugSourceCount + DebugTypeCount + DebugSeverityCount}; static constexpr auto Values = make_array_sequence(); @@ -301,7 +318,7 @@ FORCE_ALIGN void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, { auto dsource = GetDebugSource(source); if(!dsource) - return context->setError(AL_INVALID_ENUM, "Invalid debug source 0x%04x", source); + throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source}; srcIndices = srcIndices.subspan(al::to_underlying(*dsource), 1); } @@ -310,7 +327,7 @@ FORCE_ALIGN void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, { auto dtype = GetDebugType(type); if(!dtype) - return context->setError(AL_INVALID_ENUM, "Invalid debug type 0x%04x", type); + throw al::context_error{AL_INVALID_ENUM, "Invalid debug type 0x%04x", type}; typeIndices = typeIndices.subspan(al::to_underlying(*dtype), 1); } @@ -319,11 +336,11 @@ FORCE_ALIGN void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, { auto dseverity = GetDebugSeverity(severity); if(!dseverity) - return context->setError(AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity); + throw al::context_error{AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity}; svrIndices = svrIndices.subspan(al::to_underlying(*dseverity), 1); } - std::lock_guard _{context->mDebugCbLock}; + std::lock_guard debuglock{context->mDebugCbLock}; DebugGroup &debug = context->mDebugGroups.back(); if(count > 0) { @@ -365,36 +382,36 @@ FORCE_ALIGN void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, [apply_type](const uint idx){ apply_type(1<setError(e.errorCode(), "%s", e.what()); +} -FORCE_ALIGN DECL_FUNCEXT4(void, alPushDebugGroup,EXT, ALenum, ALuint, ALsizei, const ALchar*) +FORCE_ALIGN DECL_FUNCEXT4(void, alPushDebugGroup,EXT, ALenum,source, ALuint,id, ALsizei,length, const ALchar*,message) FORCE_ALIGN void AL_APIENTRY alPushDebugGroupDirectEXT(ALCcontext *context, ALenum source, ALuint id, ALsizei length, const ALchar *message) noexcept -{ +try { 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); + if(newlen >= MaxDebugMessageLength) + throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%zu >= %d)", newlen, + MaxDebugMessageLength}; length = static_cast(newlen); } - else if(length >= MaxDebugMessageLength) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Debug message too long (%d >= %d)", length, - MaxDebugMessageLength); + else if(length >= MaxDebugMessageLength) + throw al::context_error{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); + throw al::context_error{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); + throw al::context_error{AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source}; std::unique_lock debuglock{context->mDebugCbLock}; if(context->mDebugGroups.size() >= MaxDebugGroupDepth) - { - debuglock.unlock(); - return context->setError(AL_STACK_OVERFLOW_EXT, "Pushing too many debug groups"); - } + throw al::context_error{AL_STACK_OVERFLOW_EXT, "Pushing too many debug groups"}; context->mDebugGroups.emplace_back(*dsource, id, std::string_view{message, static_cast(length)}); @@ -408,17 +425,17 @@ FORCE_ALIGN void AL_APIENTRY alPushDebugGroupDirectEXT(ALCcontext *context, ALen context->sendDebugMessage(debuglock, newback.mSource, DebugType::PushGroup, newback.mId, DebugSeverity::Notification, newback.mMessage); } +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); +} FORCE_ALIGN DECL_FUNCEXT(void, alPopDebugGroup,EXT) FORCE_ALIGN void AL_APIENTRY alPopDebugGroupDirectEXT(ALCcontext *context) noexcept -{ +try { std::unique_lock debuglock{context->mDebugCbLock}; if(context->mDebugGroups.size() <= 1) - { - debuglock.unlock(); - return context->setError(AL_STACK_UNDERFLOW_EXT, - "Attempting to pop the default debug group"); - } + throw al::context_error{AL_STACK_UNDERFLOW_EXT, + "Attempting to pop the default debug group"}; DebugGroup &debug = context->mDebugGroups.back(); const auto source = debug.mSource; @@ -430,89 +447,112 @@ FORCE_ALIGN void AL_APIENTRY alPopDebugGroupDirectEXT(ALCcontext *context) noexc context->sendDebugMessage(debuglock, source, DebugType::PopGroup, id, DebugSeverity::Notification, message); } +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); +} -FORCE_ALIGN DECL_FUNCEXT8(ALuint, alGetDebugMessageLog,EXT, ALuint, ALsizei, ALenum*, ALenum*, ALuint*, ALenum*, ALsizei*, ALchar*) +FORCE_ALIGN DECL_FUNCEXT8(ALuint, alGetDebugMessageLog,EXT, ALuint,count, ALsizei,logBufSize, ALenum*,sources, ALenum*,types, ALuint*,ids, ALenum*,severities, ALsizei*,lengths, ALchar*,logBuf) 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 _{context->mDebugCbLock}; - ALsizei logBufWritten{0}; +try { + if(logBuf && logBufSize < 0) + throw al::context_error{AL_INVALID_VALUE, "Negative debug log buffer size"}; + + const auto sourcesSpan = al::span{sources, sources ? count : 0u}; + const auto typesSpan = al::span{types, types ? count : 0u}; + const auto idsSpan = al::span{ids, ids ? count : 0u}; + const auto severitiesSpan = al::span{severities, severities ? count : 0u}; + const auto lengthsSpan = al::span{lengths, lengths ? count : 0u}; + const auto logSpan = al::span{logBuf, logBuf ? static_cast(logBufSize) : 0u}; + + auto sourceiter = sourcesSpan.begin(); + auto typeiter = typesSpan.begin(); + auto iditer = idsSpan.begin(); + auto severityiter = severitiesSpan.begin(); + auto lengthiter = lengthsSpan.begin(); + auto logiter = logSpan.begin(); + + auto debuglock = std::lock_guard{context->mDebugCbLock}; 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 auto tocopy = size_t{entry.mMessage.size() + 1}; + if(al::to_address(logiter) != nullptr) { - const size_t avail{static_cast(logBufSize - logBufWritten)}; - if(avail < tocopy) + if(static_cast(std::distance(logiter, logSpan.end())) < tocopy) return i; - std::copy_n(entry.mMessage.data(), tocopy, logBuf+logBufWritten); - logBufWritten += static_cast(tocopy); + logiter = std::copy(entry.mMessage.cbegin(), entry.mMessage.cend(), logiter); + *(logiter++) = '\0'; } - 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(tocopy); + if(al::to_address(sourceiter) != nullptr) + *(sourceiter++) = GetDebugSourceEnum(entry.mSource); + if(al::to_address(typeiter) != nullptr) + *(typeiter++) = GetDebugTypeEnum(entry.mType); + if(al::to_address(iditer) != nullptr) + *(iditer++) = entry.mId; + if(al::to_address(severityiter) != nullptr) + *(severityiter++) = GetDebugSeverityEnum(entry.mSeverity); + if(al::to_address(lengthiter) != nullptr) + *(lengthiter++) = static_cast(tocopy); context->mDebugLog.pop_front(); } return count; } +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); + return 0; +} -FORCE_ALIGN DECL_FUNCEXT4(void, alObjectLabel,EXT, ALenum, ALuint, ALsizei, const ALchar*) +FORCE_ALIGN DECL_FUNCEXT4(void, alObjectLabel,EXT, ALenum,identifier, ALuint,name, ALsizei,length, const ALchar*,label) 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"); +try { + if(!label && length != 0) + throw al::context_error{AL_INVALID_VALUE, "Null label pointer"}; auto objname = (length < 0) ? std::string_view{label} : std::string_view{label, static_cast(length)}; - if(objname.length() >= MaxObjectLabelLength) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Object label length too long (%zu >= %d)", - objname.length(), MaxObjectLabelLength); + if(objname.size() >= MaxObjectLabelLength) + throw al::context_error{AL_INVALID_VALUE, "Object label length too long (%zu >= %d)", + objname.size(), 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); + switch(identifier) + { + case AL_SOURCE_EXT: ALsource::SetName(context, name, objname); return; + case AL_BUFFER: ALbuffer::SetName(context, name, objname); return; + case AL_FILTER_EXT: ALfilter::SetName(context, name, objname); return; + case AL_EFFECT_EXT: ALeffect::SetName(context, name, objname); return; + case AL_AUXILIARY_EFFECT_SLOT_EXT: ALeffectslot::SetName(context, name, objname); return; + } + + throw al::context_error{AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -FORCE_ALIGN DECL_FUNCEXT5(void, alGetObjectLabel,EXT, ALenum, ALuint, ALsizei, ALsizei*, ALchar*) +FORCE_ALIGN DECL_FUNCEXT5(void, alGetObjectLabel,EXT, ALenum,identifier, ALuint,name, ALsizei,bufSize, ALsizei*,length, ALchar*,label) 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"); +try { + if(bufSize < 0) + throw al::context_error{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"); + if(!label && !length) + throw al::context_error{AL_INVALID_VALUE, "Null length and label"}; + if(label && bufSize == 0) + throw al::context_error{AL_INVALID_VALUE, "Zero label bufSize"}; - auto copy_name = [name,bufSize,length,label](std::unordered_map &names) + const auto labelOut = al::span{label, label ? static_cast(bufSize) : 0u}; + auto copy_name = [name,length,labelOut](std::unordered_map &names) { std::string_view objname; @@ -520,13 +560,13 @@ FORCE_ALIGN void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALen if(iter != names.end()) objname = iter->second; - if(!label) - *length = static_cast(objname.length()); + if(labelOut.empty()) + *length = static_cast(objname.size()); else { - const size_t tocopy{minz(objname.length(), static_cast(bufSize)-1)}; - std::memcpy(label, objname.data(), tocopy); - label[tocopy] = '\0'; + const size_t tocopy{std::min(objname.size(), labelOut.size()-1)}; + auto oiter = std::copy_n(objname.cbegin(), tocopy, labelOut.begin()); + *oiter = '\0'; if(length) *length = static_cast(tocopy); } @@ -534,32 +574,35 @@ FORCE_ALIGN void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALen if(identifier == AL_SOURCE_EXT) { - std::lock_guard _{context->mSourceLock}; + std::lock_guard srclock{context->mSourceLock}; copy_name(context->mSourceNames); } else if(identifier == AL_BUFFER) { - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; + auto *device = context->mALDevice.get(); + auto buflock = 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}; + auto *device = context->mALDevice.get(); + auto buflock = 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}; + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->EffectLock}; copy_name(device->mEffectNames); } else if(identifier == AL_AUXILIARY_EFFECT_SLOT_EXT) { - std::lock_guard _{context->mEffectSlotLock}; + std::lock_guard slotlock{context->mEffectSlotLock}; copy_name(context->mEffectSlotNames); } else - context->setError(AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier); + throw al::context_error{AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } diff --git a/3rdparty/openal/al/debug.h b/3rdparty/openal/al/debug.h index 2764bb7f794a..d1792adb41e6 100644 --- a/3rdparty/openal/al/debug.h +++ b/3rdparty/openal/al/debug.h @@ -1,8 +1,9 @@ #ifndef AL_DEBUG_H #define AL_DEBUG_H -#include +#include #include +#include #include using uint = unsigned int; @@ -11,14 +12,14 @@ 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 std::uint8_t MaxDebugLoggedMessages{64}; +inline constexpr std::uint16_t MaxDebugMessageLength{1024}; +inline constexpr std::uint8_t MaxDebugGroupDepth{64}; +inline constexpr std::uint16_t MaxObjectLabelLength{1024}; inline constexpr uint DebugSourceBase{0}; -enum class DebugSource : uint8_t { +enum class DebugSource : std::uint8_t { API = 0, System, ThirdParty, @@ -28,7 +29,7 @@ enum class DebugSource : uint8_t { inline constexpr uint DebugSourceCount{5}; inline constexpr uint DebugTypeBase{DebugSourceBase + DebugSourceCount}; -enum class DebugType : uint8_t { +enum class DebugType : std::uint8_t { Error = 0, DeprecatedBehavior, UndefinedBehavior, @@ -42,7 +43,7 @@ enum class DebugType : uint8_t { inline constexpr uint DebugTypeCount{9}; inline constexpr uint DebugSeverityBase{DebugTypeBase + DebugTypeCount}; -enum class DebugSeverity : uint8_t { +enum class DebugSeverity : std::uint8_t { High = 0, Medium, Low, @@ -55,7 +56,7 @@ struct DebugGroup { const DebugSource mSource; std::string mMessage; std::vector mFilters; - std::vector mIdFilters; + std::vector mIdFilters; template DebugGroup(DebugSource source, uint id, T&& message) @@ -63,6 +64,7 @@ struct DebugGroup { { } DebugGroup(const DebugGroup&) = default; DebugGroup(DebugGroup&&) = default; + ~DebugGroup(); }; #endif /* AL_DEBUG_H */ diff --git a/3rdparty/openal/al/direct_defs.h b/3rdparty/openal/al/direct_defs.h index 7526b6112a4d..4119182f0158 100644 --- a/3rdparty/openal/al/direct_defs.h +++ b/3rdparty/openal/al/direct_defs.h @@ -12,116 +12,116 @@ constexpr void DefaultVal() noexcept { } } // namespace detail_ #define DECL_FUNC(R, Name) \ -R AL_APIENTRY Name(void) noexcept \ +auto AL_APIENTRY Name() noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ return Name##Direct(context.get()); \ } -#define DECL_FUNC1(R, Name, T1) \ -R AL_APIENTRY Name(T1 a) noexcept \ +#define DECL_FUNC1(R, Name, T1,n1) \ +auto AL_APIENTRY Name(T1 n1) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct(context.get(), a); \ + return Name##Direct(context.get(), n1); \ } -#define DECL_FUNC2(R, Name, T1, T2) \ -R AL_APIENTRY Name(T1 a, T2 b) noexcept \ +#define DECL_FUNC2(R, Name, T1,n1, T2,n2) \ +auto AL_APIENTRY Name(T1 n1, T2 n2) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct(context.get(), a, b); \ + return Name##Direct(context.get(), n1, n2); \ } -#define DECL_FUNC3(R, Name, T1, T2, T3) \ -R AL_APIENTRY Name(T1 a, T2 b, T3 c) noexcept \ +#define DECL_FUNC3(R, Name, T1,n1, T2,n2, T3,n3) \ +auto AL_APIENTRY Name(T1 n1, T2 n2, T3 n3) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct(context.get(), a, b, c); \ + return Name##Direct(context.get(), n1, n2, n3); \ } -#define DECL_FUNC4(R, Name, T1, T2, T3, T4) \ -R AL_APIENTRY Name(T1 a, T2 b, T3 c, T4 d) noexcept \ +#define DECL_FUNC4(R, Name, T1,n1, T2,n2, T3,n3, T4,n4) \ +auto AL_APIENTRY Name(T1 n1, T2 n2, T3 n3, T4 n4) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct(context.get(), a, b, c, d); \ + return Name##Direct(context.get(), n1, n2, n3, n4); \ } -#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 \ +#define DECL_FUNC5(R, Name, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5) \ +auto AL_APIENTRY Name(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct(context.get(), a, b, c, d, e); \ + return Name##Direct(context.get(), n1, n2, n3, n4, n5); \ } #define DECL_FUNCEXT(R, Name,Ext) \ -R AL_APIENTRY Name##Ext(void) noexcept \ +auto AL_APIENTRY Name##Ext() noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ return Name##Direct##Ext(context.get()); \ } -#define DECL_FUNCEXT1(R, Name,Ext, T1) \ -R AL_APIENTRY Name##Ext(T1 a) noexcept \ +#define DECL_FUNCEXT1(R, Name,Ext, T1,n1) \ +auto AL_APIENTRY Name##Ext(T1 n1) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct##Ext(context.get(), a); \ + return Name##Direct##Ext(context.get(), n1); \ } -#define DECL_FUNCEXT2(R, Name,Ext, T1, T2) \ -R AL_APIENTRY Name##Ext(T1 a, T2 b) noexcept \ +#define DECL_FUNCEXT2(R, Name,Ext, T1,n1, T2,n2) \ +auto AL_APIENTRY Name##Ext(T1 n1, T2 n2) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct##Ext(context.get(), a, b); \ + return Name##Direct##Ext(context.get(), n1, n2); \ } -#define DECL_FUNCEXT3(R, Name,Ext, T1, T2, T3) \ -R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c) noexcept \ +#define DECL_FUNCEXT3(R, Name,Ext, T1,n1, T2,n2, T3,n3) \ +auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct##Ext(context.get(), a, b, c); \ + return Name##Direct##Ext(context.get(), n1, n2, n3); \ } -#define DECL_FUNCEXT4(R, Name,Ext, T1, T2, T3, T4) \ -R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d) noexcept \ +#define DECL_FUNCEXT4(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4) \ +auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct##Ext(context.get(), a, b, c, d); \ + return Name##Direct##Ext(context.get(), n1, n2, n3, n4); \ } -#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 \ +#define DECL_FUNCEXT5(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5) \ +auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct##Ext(context.get(), a, b, c, d, e); \ + return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5); \ } -#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 \ +#define DECL_FUNCEXT6(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5, T6,n6) \ +auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5, T6 n6) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct##Ext(context.get(), a, b, c, d, e, f); \ + return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5, n6); \ } -#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 \ +#define DECL_FUNCEXT8(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5, T6,n6, T7,n7, T8,n8) \ +auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5, T6 n6, T7 n7, T8 n8) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct##Ext(context.get(), a, b, c, d, e, f, g, h); \ + return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5, n6, n7, n8); \ } #endif /* AL_DIRECT_DEFS_H */ diff --git a/3rdparty/openal/al/eax/api.h b/3rdparty/openal/al/eax/api.h index 18d93ef83cc8..8ac1e62fb8a4 100644 --- a/3rdparty/openal/al/eax/api.h +++ b/3rdparty/openal/al/eax/api.h @@ -14,6 +14,7 @@ #include #include #include +#include #ifdef _WIN32 #include #endif @@ -22,12 +23,12 @@ #ifndef _WIN32 -typedef struct _GUID { +using GUID = struct _GUID { /* NOLINT(*-reserved-identifier) */ std::uint32_t Data1; std::uint16_t Data2; std::uint16_t Data3; - std::uint8_t Data4[8]; -} GUID; + std::array Data4; +}; inline bool operator==(const GUID& lhs, const GUID& rhs) noexcept { return std::memcmp(&lhs, &rhs, sizeof(GUID)) == 0; } @@ -36,9 +37,14 @@ inline bool operator!=(const GUID& lhs, const GUID& rhs) noexcept { return !(lhs == rhs); } #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); } +/* TODO: This seems to create very inefficient comparisons. C++20 should allow + * creating default comparison operators, avoiding the need for this. + */ +#define DECL_EQOP(T, ...) \ +[[nodiscard]] auto get_members() const noexcept { return std::forward_as_tuple(__VA_ARGS__); } \ +[[nodiscard]] friend bool operator==(const T &lhs, const T &rhs) noexcept \ +{ return lhs.get_members() == rhs.get_members(); } \ +[[nodiscard]] friend bool operator!=(const T &lhs, const T &rhs) noexcept { return !(lhs == rhs); } extern const GUID DSPROPSETID_EAX_ReverbProperties; @@ -277,11 +283,13 @@ struct EAXVECTOR { float x; float y; float z; -}; // EAXVECTOR +}; +[[nodiscard]] inline bool operator==(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept -{ return std::memcmp(&lhs, &rhs, sizeof(EAXVECTOR)) == 0; } +{ return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z; } +[[nodiscard]] inline bool operator!=(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept { return !(lhs == rhs); } @@ -362,6 +370,7 @@ constexpr auto EAXCONTEXT_MINMACROFXFACTOR = 0.0F; constexpr auto EAXCONTEXT_MAXMACROFXFACTOR = 1.0F; constexpr auto EAXCONTEXT_DEFAULTMACROFXFACTOR = 0.0F; +constexpr auto EAXCONTEXT_DEFAULTLASTERROR = EAX_OK; extern const GUID EAXPROPERTYID_EAX40_FXSlot0; extern const GUID EAXPROPERTYID_EAX50_FXSlot0; @@ -654,11 +663,11 @@ struct EAXSPEAKERLEVELPROPERTIES { }; // EAXSPEAKERLEVELPROPERTIES struct EAX40ACTIVEFXSLOTS { - GUID guidActiveFXSlots[EAX40_MAX_ACTIVE_FXSLOTS]; + std::array guidActiveFXSlots; }; // EAX40ACTIVEFXSLOTS struct EAX50ACTIVEFXSLOTS { - GUID guidActiveFXSlots[EAX50_MAX_ACTIVE_FXSLOTS]; + std::array guidActiveFXSlots; }; // EAX50ACTIVEFXSLOTS // Use this structure for EAXSOURCE_OBSTRUCTIONPARAMETERS property. @@ -837,7 +846,11 @@ 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) + DECL_EQOP(EAXREVERBPROPERTIES, ulEnvironment, flEnvironmentSize, flEnvironmentDiffusion, lRoom, + lRoomHF, lRoomLF, flDecayTime, flDecayHFRatio, flDecayLFRatio, lReflections, + flReflectionsDelay, vReflectionsPan, lReverb, flReverbDelay, vReverbPan, flEchoTime, + flEchoDepth, flModulationTime, flModulationDepth, flAirAbsorptionHF, flHFReference, + flLFReference, flRoomRolloffFactor, ulFlags) }; // EAXREVERBPROPERTIES @@ -967,7 +980,7 @@ enum EAXAGCCOMPRESSOR_PROPERTY : unsigned int { struct EAXAGCCOMPRESSORPROPERTIES { unsigned long ulOnOff; // Switch Compressor on or off - DECL_EQOP(EAXAGCCOMPRESSORPROPERTIES) + DECL_EQOP(EAXAGCCOMPRESSORPROPERTIES, ulOnOff) }; // EAXAGCCOMPRESSORPROPERTIES @@ -994,7 +1007,7 @@ struct EAXAUTOWAHPROPERTIES { float flReleaseTime; // Release time (seconds) long lResonance; // Resonance (mB) long lPeakLevel; // Peak level (mB) - DECL_EQOP(EAXAUTOWAHPROPERTIES) + DECL_EQOP(EAXAUTOWAHPROPERTIES, flAttackTime, flReleaseTime, lResonance, lPeakLevel) }; // EAXAUTOWAHPROPERTIES @@ -1042,7 +1055,7 @@ struct EAXCHORUSPROPERTIES { float flDepth; // Depth (0 to 1) float flFeedback; // Feedback (-1 to 1) float flDelay; // Delay (seconds) - DECL_EQOP(EAXCHORUSPROPERTIES) + DECL_EQOP(EAXCHORUSPROPERTIES, ulWaveform, lPhase, flRate, flDepth, flFeedback, flDelay) }; // EAXCHORUSPROPERTIES @@ -1091,7 +1104,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) + DECL_EQOP(EAXDISTORTIONPROPERTIES, flEdge, lGain, flLowPassCutOff, flEQCenter, flEQBandwidth) }; // EAXDISTORTIONPROPERTIES @@ -1136,7 +1149,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) + DECL_EQOP(EAXECHOPROPERTIES, flDelay, flLRDelay, flDamping, flFeedback, flSpread) }; // EAXECHOPROPERTIES @@ -1191,7 +1204,8 @@ struct EAXEQUALIZERPROPERTIES { float flMid2Width; // (octaves) long lHighGain; // (mB) float flHighCutOff; // (Hz) - DECL_EQOP(EAXEQUALIZERPROPERTIES) + DECL_EQOP(EAXEQUALIZERPROPERTIES, lLowGain, flLowCutOff, lMid1Gain, flMid1Center, flMid1Width, + lMid2Gain, flMid2Center, flMid2Width, lHighGain, flHighCutOff) }; // EAXEQUALIZERPROPERTIES @@ -1263,7 +1277,7 @@ struct EAXFLANGERPROPERTIES { float flDepth; // Depth (0 to 1) float flFeedback; // Feedback (0 to 1) float flDelay; // Delay (seconds) - DECL_EQOP(EAXFLANGERPROPERTIES) + DECL_EQOP(EAXFLANGERPROPERTIES, ulWaveform, lPhase, flRate, flDepth, flFeedback, flDelay) }; // EAXFLANGERPROPERTIES @@ -1314,7 +1328,7 @@ struct EAXFREQUENCYSHIFTERPROPERTIES { float flFrequency; // (Hz) unsigned long ulLeftDirection; // see enum above unsigned long ulRightDirection; // see enum above - DECL_EQOP(EAXFREQUENCYSHIFTERPROPERTIES) + DECL_EQOP(EAXFREQUENCYSHIFTERPROPERTIES, flFrequency, ulLeftDirection, ulRightDirection) }; // EAXFREQUENCYSHIFTERPROPERTIES @@ -1393,7 +1407,8 @@ struct EAXVOCALMORPHERPROPERTIES { long lPhonemeBCoarseTuning; // (semitones) unsigned long ulWaveform; // Waveform selector - see enum above float flRate; // (Hz) - DECL_EQOP(EAXVOCALMORPHERPROPERTIES) + DECL_EQOP(EAXVOCALMORPHERPROPERTIES, ulPhonemeA, lPhonemeACoarseTuning, ulPhonemeB, + lPhonemeBCoarseTuning, ulWaveform, flRate) }; // EAXVOCALMORPHERPROPERTIES @@ -1436,7 +1451,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) + DECL_EQOP(EAXPITCHSHIFTERPROPERTIES, lCoarseTune, lFineTune) }; // EAXPITCHSHIFTERPROPERTIES @@ -1472,7 +1487,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) + DECL_EQOP(EAXRINGMODULATORPROPERTIES, flFrequency, flHighPassCutOff, ulWaveform) }; // EAXRINGMODULATORPROPERTIES diff --git a/3rdparty/openal/al/eax/call.cpp b/3rdparty/openal/al/eax/call.cpp index 689d5cf1d744..5c69d2e5ec97 100644 --- a/3rdparty/openal/al/eax/call.cpp +++ b/3rdparty/openal/al/eax/call.cpp @@ -15,15 +15,9 @@ class EaxCallException : public EaxException { } // namespace -EaxCall::EaxCall( - EaxCallType type, - const GUID& property_set_guid, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_buffer, - ALuint property_size) - : mCallType{type}, mVersion{0}, mPropertySetId{EaxCallPropertySetId::none} - , mIsDeferred{(property_id & deferred_flag) != 0} +EaxCall::EaxCall(EaxCallType type, const GUID &property_set_guid, ALuint property_id, + ALuint property_source_id, ALvoid *property_buffer, ALuint property_size) + : mCallType{type}, mIsDeferred{(property_id & deferred_flag) != 0} , mPropertyId{property_id & ~deferred_flag}, mPropertySourceId{property_source_id} , mPropertyBuffer{property_buffer}, mPropertyBufferSize{property_size} { @@ -146,23 +140,34 @@ EaxCall::EaxCall( fail("Unsupported property set id."); } - switch(mPropertyId) - { - case EAXCONTEXT_LASTERROR: - case EAXCONTEXT_SPEAKERCONFIG: - case EAXCONTEXT_EAXSESSION: - case EAXFXSLOT_NONE: - case EAXFXSLOT_ALLPARAMETERS: - case EAXFXSLOT_LOADEFFECT: - case EAXFXSLOT_VOLUME: - case EAXFXSLOT_LOCK: - case EAXFXSLOT_FLAGS: - case EAXFXSLOT_OCCLUSION: - case EAXFXSLOT_OCCLUSIONLFRATIO: - // EAX allow to set "defer" flag on immediate-only properties. - // If we don't clear our flag then "applyAllUpdates" in EAX context won't be called. - mIsDeferred = false; - break; + if(mPropertySetId == EaxCallPropertySetId::context) + { + switch(mPropertyId) + { + case EAXCONTEXT_LASTERROR: + case EAXCONTEXT_SPEAKERCONFIG: + case EAXCONTEXT_EAXSESSION: + // EAX allow to set "defer" flag on immediate-only properties. + // If we don't clear our flag then "applyAllUpdates" in EAX context won't be called. + mIsDeferred = false; + break; + } + } + else if(mPropertySetId == EaxCallPropertySetId::fx_slot) + { + switch(mPropertyId) + { + case EAXFXSLOT_NONE: + case EAXFXSLOT_ALLPARAMETERS: + case EAXFXSLOT_LOADEFFECT: + case EAXFXSLOT_VOLUME: + case EAXFXSLOT_LOCK: + case EAXFXSLOT_FLAGS: + case EAXFXSLOT_OCCLUSION: + case EAXFXSLOT_OCCLUSIONLFRATIO: + mIsDeferred = false; + break; + } } if(!mIsDeferred) diff --git a/3rdparty/openal/al/eax/call.h b/3rdparty/openal/al/eax/call.h index 45ff328c04b6..72f96bbe4934 100644 --- a/3rdparty/openal/al/eax/call.h +++ b/3rdparty/openal/al/eax/call.h @@ -31,16 +31,16 @@ class EaxCall { ALvoid* property_buffer, ALuint property_size); - bool is_get() const noexcept { return mCallType == EaxCallType::get; } - bool is_deferred() const noexcept { return mIsDeferred; } - int get_version() const noexcept { return mVersion; } - EaxCallPropertySetId get_property_set_id() const noexcept { return mPropertySetId; } - ALuint get_property_id() const noexcept { return mPropertyId; } - ALuint get_property_al_name() const noexcept { return mPropertySourceId; } - EaxFxSlotIndex get_fx_slot_index() const noexcept { return mFxSlotIndex; } + [[nodiscard]] auto is_get() const noexcept -> bool { return mCallType == EaxCallType::get; } + [[nodiscard]] auto is_deferred() const noexcept -> bool { return mIsDeferred; } + [[nodiscard]] auto get_version() const noexcept -> int { return mVersion; } + [[nodiscard]] auto get_property_set_id() const noexcept -> EaxCallPropertySetId { return mPropertySetId; } + [[nodiscard]] auto get_property_id() const noexcept -> ALuint { return mPropertyId; } + [[nodiscard]] auto get_property_al_name() const noexcept -> ALuint { return mPropertySourceId; } + [[nodiscard]] auto get_fx_slot_index() const noexcept -> EaxFxSlotIndex { return mFxSlotIndex; } template - TValue& get_value() const + [[nodiscard]] auto get_value() const -> TValue& { if(mPropertyBufferSize < sizeof(TValue)) fail_too_small(); @@ -49,32 +49,32 @@ class EaxCall { } template - al::span get_values(size_t max_count) const + [[nodiscard]] auto get_values(size_t max_count) const -> al::span { if(max_count == 0 || mPropertyBufferSize < sizeof(TValue)) fail_too_small(); - const auto count = minz(mPropertyBufferSize / sizeof(TValue), max_count); + const auto count = std::min(mPropertyBufferSize/sizeof(TValue), max_count); return {static_cast(mPropertyBuffer), count}; } template - al::span get_values() const + [[nodiscard]] auto get_values() const -> al::span { return get_values(~0_uz); } template - void set_value(const TValue& value) const + auto set_value(const TValue& value) const -> void { get_value() = value; } private: const EaxCallType mCallType; - int mVersion; - EaxFxSlotIndex mFxSlotIndex; - EaxCallPropertySetId mPropertySetId; + int mVersion{}; + EaxFxSlotIndex mFxSlotIndex{}; + EaxCallPropertySetId mPropertySetId{EaxCallPropertySetId::none}; bool mIsDeferred; const ALuint mPropertyId; diff --git a/3rdparty/openal/al/eax/effect.h b/3rdparty/openal/al/eax/effect.h index afe4d94d0b0f..c05139be3207 100644 --- a/3rdparty/openal/al/eax/effect.h +++ b/3rdparty/openal/al/eax/effect.h @@ -1,13 +1,12 @@ #ifndef EAX_EFFECT_INCLUDED #define EAX_EFFECT_INCLUDED - #include #include #include -#include "alnumeric.h" #include "AL/al.h" +#include "AL/alext.h" #include "core/effects/base.h" #include "call.h" @@ -36,7 +35,7 @@ struct overloaded : Ts... { using Ts::operator()...; }; template overloaded(Ts...) -> overloaded; -constexpr ALenum EnumFromEaxEffectType(const EaxEffectProps &props) noexcept +constexpr ALenum EnumFromEaxEffectType(const EaxEffectProps &props) { return std::visit(overloaded{ [](const std::monostate&) noexcept { return AL_EFFECT_NULL; }, @@ -100,7 +99,6 @@ struct EaxReverbCommitter { bool commit(const EAX_REVERBPROPERTIES &props); bool commit(const EAX20LISTENERPROPERTIES &props); bool commit(const EAXREVERBPROPERTIES &props); - bool commit(const EaxEffectProps &props); static void SetDefaults(EAX_REVERBPROPERTIES &props); static void SetDefaults(EAX20LISTENERPROPERTIES &props); @@ -110,26 +108,19 @@ struct EaxReverbCommitter { static void Get(const EaxCall &call, const EAX_REVERBPROPERTIES &props); static void Get(const EaxCall &call, const EAX20LISTENERPROPERTIES &props); static void Get(const EaxCall &call, const EAXREVERBPROPERTIES &props); - static void Get(const EaxCall &call, const EaxEffectProps &props); static void Set(const EaxCall &call, EAX_REVERBPROPERTIES &props); static void Set(const EaxCall &call, EAX20LISTENERPROPERTIES &props); static void Set(const EaxCall &call, EAXREVERBPROPERTIES &props); - static void Set(const EaxCall &call, EaxEffectProps &props); - static void translate(const EAX_REVERBPROPERTIES& src, EaxEffectProps& dst) noexcept; - static void translate(const EAX20LISTENERPROPERTIES& src, EaxEffectProps& dst) noexcept; - static void translate(const EAXREVERBPROPERTIES& src, EaxEffectProps& dst) noexcept; + static void translate(const EAX_REVERBPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept; + static void translate(const EAX20LISTENERPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept; }; template struct EaxCommitter { struct Exception; - EaxCommitter(EaxEffectProps &eaxprops, EffectProps &alprops) - : mEaxProps{eaxprops}, mAlProps{alprops} - { } - EaxEffectProps &mEaxProps; EffectProps &mAlProps; @@ -145,50 +136,155 @@ struct EaxCommitter { [[noreturn]] static void fail_unknown_property_id() { fail(EaxEffectErrorMessages::unknown_property_id()); } - bool commit(const EaxEffectProps &props); +private: + EaxCommitter(EaxEffectProps &eaxprops, EffectProps &alprops) + : mEaxProps{eaxprops}, mAlProps{alprops} + { } - static void SetDefaults(EaxEffectProps &props); - static void Get(const EaxCall &call, const EaxEffectProps &props); - static void Set(const EaxCall &call, EaxEffectProps &props); + friend T; }; struct EaxAutowahCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + EaxAutowahCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXAUTOWAHPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXAUTOWAHPROPERTIES &props); + static void Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props); }; struct EaxChorusCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + EaxChorusCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXCHORUSPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXCHORUSPROPERTIES &props); + static void Set(const EaxCall &call, EAXCHORUSPROPERTIES &props); }; struct EaxCompressorCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + EaxCompressorCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXAGCCOMPRESSORPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXAGCCOMPRESSORPROPERTIES &props); + static void Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props); }; struct EaxDistortionCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + EaxDistortionCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXDISTORTIONPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXDISTORTIONPROPERTIES &props); + static void Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props); }; struct EaxEchoCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + EaxEchoCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXECHOPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXECHOPROPERTIES &props); + static void Set(const EaxCall &call, EAXECHOPROPERTIES &props); }; struct EaxEqualizerCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + EaxEqualizerCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXEQUALIZERPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXEQUALIZERPROPERTIES &props); + static void Set(const EaxCall &call, EAXEQUALIZERPROPERTIES &props); }; struct EaxFlangerCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + EaxFlangerCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXFLANGERPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXFLANGERPROPERTIES &props); + static void Set(const EaxCall &call, EAXFLANGERPROPERTIES &props); }; struct EaxFrequencyShifterCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + EaxFrequencyShifterCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXFREQUENCYSHIFTERPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXFREQUENCYSHIFTERPROPERTIES &props); + static void Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props); }; struct EaxModulatorCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + EaxModulatorCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXRINGMODULATORPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXRINGMODULATORPROPERTIES &props); + static void Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props); }; struct EaxPitchShifterCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + EaxPitchShifterCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXPITCHSHIFTERPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXPITCHSHIFTERPROPERTIES &props); + static void Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props); }; struct EaxVocalMorpherCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + EaxVocalMorpherCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXVOCALMORPHERPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXVOCALMORPHERPROPERTIES &props); + static void Set(const EaxCall &call, EAXVOCALMORPHERPROPERTIES &props); }; struct EaxNullCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + EaxNullCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const std::monostate &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const std::monostate &props); + static void Set(const EaxCall &call, std::monostate &props); }; +template +struct CommitterFromProps { }; + +template<> struct CommitterFromProps { using type = EaxNullCommitter; }; +template<> struct CommitterFromProps { using type = EaxReverbCommitter; }; +template<> struct CommitterFromProps { using type = EaxChorusCommitter; }; +template<> struct CommitterFromProps { using type = EaxCompressorCommitter; }; +template<> struct CommitterFromProps { using type = EaxAutowahCommitter; }; +template<> struct CommitterFromProps { using type = EaxDistortionCommitter; }; +template<> struct CommitterFromProps { using type = EaxEchoCommitter; }; +template<> struct CommitterFromProps { using type = EaxEqualizerCommitter; }; +template<> struct CommitterFromProps { using type = EaxFlangerCommitter; }; +template<> struct CommitterFromProps { using type = EaxFrequencyShifterCommitter; }; +template<> struct CommitterFromProps { using type = EaxModulatorCommitter; }; +template<> struct CommitterFromProps { using type = EaxPitchShifterCommitter; }; +template<> struct CommitterFromProps { using type = EaxVocalMorpherCommitter; }; + +template +using CommitterFor = typename CommitterFromProps>>::type; + class EaxEffect { public: @@ -196,7 +292,7 @@ class EaxEffect { ~EaxEffect() = default; ALenum al_effect_type_{AL_EFFECT_NULL}; - EffectProps al_effect_props_{}; + EffectProps al_effect_props_; using Props1 = EAX_REVERBPROPERTIES; using Props2 = EAX20LISTENERPROPERTIES; @@ -225,7 +321,7 @@ class EaxEffect { int version_{}; bool changed_{}; - Props4 props_{}; + Props4 props_; State1 state1_{}; State2 state2_{}; State3 state3_{}; @@ -233,51 +329,39 @@ class EaxEffect { State4 state5_{}; - template - void call_set_defaults(Args&& ...args) - { return T::SetDefaults(std::forward(args)...); } - - void call_set_defaults(const ALenum altype, EaxEffectProps &props) + static void call_set_defaults(const ALenum altype, EaxEffectProps &props) { - if(altype == AL_EFFECT_EAXREVERB) - return call_set_defaults(props); - if(altype == AL_EFFECT_CHORUS) - return call_set_defaults(props); - if(altype == AL_EFFECT_AUTOWAH) - return call_set_defaults(props); - if(altype == AL_EFFECT_COMPRESSOR) - return call_set_defaults(props); - if(altype == AL_EFFECT_DISTORTION) - return call_set_defaults(props); - if(altype == AL_EFFECT_ECHO) - return call_set_defaults(props); - if(altype == AL_EFFECT_EQUALIZER) - return call_set_defaults(props); - if(altype == AL_EFFECT_FLANGER) - return call_set_defaults(props); - if(altype == AL_EFFECT_FREQUENCY_SHIFTER) - return call_set_defaults(props); - if(altype == AL_EFFECT_RING_MODULATOR) - return call_set_defaults(props); - if(altype == AL_EFFECT_PITCH_SHIFTER) - return call_set_defaults(props); - if(altype == AL_EFFECT_VOCAL_MORPHER) - return call_set_defaults(props); - return call_set_defaults(props); + switch(altype) + { + case AL_EFFECT_EAXREVERB: return EaxReverbCommitter::SetDefaults(props); + case AL_EFFECT_CHORUS: return EaxChorusCommitter::SetDefaults(props); + case AL_EFFECT_AUTOWAH: return EaxAutowahCommitter::SetDefaults(props); + case AL_EFFECT_COMPRESSOR: return EaxCompressorCommitter::SetDefaults(props); + case AL_EFFECT_DISTORTION: return EaxDistortionCommitter::SetDefaults(props); + case AL_EFFECT_ECHO: return EaxEchoCommitter::SetDefaults(props); + case AL_EFFECT_EQUALIZER: return EaxEqualizerCommitter::SetDefaults(props); + case AL_EFFECT_FLANGER: return EaxFlangerCommitter::SetDefaults(props); + case AL_EFFECT_FREQUENCY_SHIFTER: return EaxFrequencyShifterCommitter::SetDefaults(props); + case AL_EFFECT_RING_MODULATOR: return EaxModulatorCommitter::SetDefaults(props); + case AL_EFFECT_PITCH_SHIFTER: return EaxPitchShifterCommitter::SetDefaults(props); + case AL_EFFECT_VOCAL_MORPHER: return EaxVocalMorpherCommitter::SetDefaults(props); + case AL_EFFECT_NULL: break; + } + return EaxNullCommitter::SetDefaults(props); } template void init() { - call_set_defaults(state1_.d); + EaxReverbCommitter::SetDefaults(state1_.d); state1_.i = state1_.d; - call_set_defaults(state2_.d); + EaxReverbCommitter::SetDefaults(state2_.d); state2_.i = state2_.d; - call_set_defaults(state3_.d); + EaxReverbCommitter::SetDefaults(state3_.d); state3_.i = state3_.d; - call_set_defaults(state4_.d); + T::SetDefaults(state4_.d); state4_.i = state4_.d; - call_set_defaults(state5_.d); + T::SetDefaults(state5_.d); state5_.i = state5_.d; } @@ -285,9 +369,9 @@ class EaxEffect { { switch(eax_version) { - case 1: call_set_defaults(state1_.d); break; - case 2: call_set_defaults(state2_.d); break; - case 3: call_set_defaults(state3_.d); break; + case 1: EaxReverbCommitter::SetDefaults(state1_.d); break; + case 2: EaxReverbCommitter::SetDefaults(state2_.d); break; + case 3: EaxReverbCommitter::SetDefaults(state3_.d); break; case 4: call_set_defaults(altype, state4_.d); break; case 5: call_set_defaults(altype, state5_.d); break; } @@ -295,47 +379,20 @@ class EaxEffect { } -#define EAXCALL(Props, Callable, ...) \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - return Callable(__VA_ARGS__) - - template - static void call_set(Args&& ...args) - { return T::Set(std::forward(args)...); } - static void call_set(const EaxCall &call, EaxEffectProps &props) - { EAXCALL(props, call_set, call, props); } + { + return std::visit([&](auto &arg) + { return CommitterFor::Set(call, arg); }, + props); + } void set(const EaxCall &call) { switch(call.get_version()) { - case 1: call_set(call, state1_.d); break; - case 2: call_set(call, state2_.d); break; - case 3: call_set(call, state3_.d); break; + case 1: EaxReverbCommitter::Set(call, state1_.d); break; + case 2: EaxReverbCommitter::Set(call, state2_.d); break; + case 3: EaxReverbCommitter::Set(call, state3_.d); break; case 4: call_set(call, state4_.d); break; case 5: call_set(call, state5_.d); break; } @@ -343,32 +400,32 @@ class EaxEffect { } - template - static void call_get(Args&& ...args) - { return T::Get(std::forward(args)...); } - static void call_get(const EaxCall &call, const EaxEffectProps &props) - { EAXCALL(props, call_get, call, props); } + { + return std::visit([&](auto &arg) + { return CommitterFor::Get(call, arg); }, + props); + } - void get(const EaxCall &call) + void get(const EaxCall &call) const { switch(call.get_version()) { - case 1: call_get(call, state1_.d); break; - case 2: call_get(call, state2_.d); break; - case 3: call_get(call, state3_.d); break; + case 1: EaxReverbCommitter::Get(call, state1_.d); break; + case 2: EaxReverbCommitter::Get(call, state2_.d); break; + case 3: EaxReverbCommitter::Get(call, state3_.d); break; case 4: call_get(call, state4_.d); break; case 5: call_get(call, state5_.d); break; } } - template - bool call_commit(Args&& ...args) - { return T{props_, al_effect_props_}.commit(std::forward(args)...); } - bool call_commit(const EaxEffectProps &props) - { EAXCALL(props, call_commit, props); } + { + return std::visit([&](auto &arg) + { return CommitterFor{props_, al_effect_props_}.commit(arg); }, + props); + } bool commit(int eax_version) { @@ -383,15 +440,15 @@ class EaxEffect { { case 1: state1_.i = state1_.d; - ret |= call_commit(state1_.d); + ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state1_.d); break; case 2: state2_.i = state2_.d; - ret |= call_commit(state2_.d); + ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state2_.d); break; case 3: state3_.i = state3_.d; - ret |= call_commit(state3_.d); + ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state3_.d); break; case 4: state4_.i = state4_.d; diff --git a/3rdparty/openal/al/eax/exception.h b/3rdparty/openal/al/eax/exception.h index 336654f08af5..64cf7c49e738 100644 --- a/3rdparty/openal/al/eax/exception.h +++ b/3rdparty/openal/al/eax/exception.h @@ -10,9 +10,14 @@ class EaxException : public std::runtime_error { static std::string make_message(std::string_view context, std::string_view message); public: + EaxException() = delete; + EaxException(const EaxException&) = default; + EaxException(EaxException&&) = default; EaxException(std::string_view context, std::string_view message); ~EaxException() override; -}; // EaxException + auto operator=(const EaxException&) -> EaxException& = default; + auto operator=(EaxException&&) -> EaxException& = default; +}; -#endif // !EAX_EXCEPTION_INCLUDED +#endif /* EAX_EXCEPTION_INCLUDED */ diff --git a/3rdparty/openal/al/eax/fx_slots.h b/3rdparty/openal/al/eax/fx_slots.h index 18b2d3ad432e..d2d90b24671c 100644 --- a/3rdparty/openal/al/eax/fx_slots.h +++ b/3rdparty/openal/al/eax/fx_slots.h @@ -6,13 +6,10 @@ #include "al/auxeffectslot.h" -#include "api.h" -#include "call.h" #include "fx_slot_index.h" -class EaxFxSlots -{ +class EaxFxSlots { public: void initialize(ALCcontext& al_context); @@ -25,11 +22,9 @@ class EaxFxSlots } - const ALeffectslot& get( - EaxFxSlotIndex index) const; + [[nodiscard]] auto get(EaxFxSlotIndex index) const -> const ALeffectslot&; - ALeffectslot& get( - EaxFxSlotIndex index); + [[nodiscard]] auto get(EaxFxSlotIndex index) -> ALeffectslot&; private: using Items = std::array; @@ -39,8 +34,7 @@ class EaxFxSlots [[noreturn]] - static void fail( - const char* message); + static void fail(const char* message); void initialize_fx_slots(ALCcontext& al_context); }; // EaxFxSlots diff --git a/3rdparty/openal/al/eax/globals.h b/3rdparty/openal/al/eax/globals.h index ff05d009ff3c..6f3e9078f89a 100644 --- a/3rdparty/openal/al/eax/globals.h +++ b/3rdparty/openal/al/eax/globals.h @@ -3,18 +3,4 @@ inline bool eax_g_is_enabled{true}; -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"}; - -inline constexpr char eax_eax_set_func_name[]{"EAXSet"}; -inline constexpr char eax_eax_get_func_name[]{"EAXGet"}; - -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 +#endif /* EAX_GLOBALS_INCLUDED */ diff --git a/3rdparty/openal/al/eax/utils.cpp b/3rdparty/openal/al/eax/utils.cpp index 53599ac5af74..871ab764bd9f 100644 --- a/3rdparty/openal/al/eax/utils.cpp +++ b/3rdparty/openal/al/eax/utils.cpp @@ -5,6 +5,7 @@ #include #include +#include "alstring.h" #include "core/logging.h" @@ -17,10 +18,9 @@ void eax_log_exception(std::string_view message) noexcept std::rethrow_exception(exception_ptr); } catch(const std::exception& ex) { - const auto ex_message = ex.what(); - ERR("%.*s %s\n", static_cast(message.length()), message.data(), ex_message); + ERR("%.*s %s\n", al::sizei(message), message.data(), ex.what()); } catch(...) { - ERR("%.*s %s\n", static_cast(message.length()), message.data(), "Generic exception."); + ERR("%.*s %s\n", al::sizei(message), message.data(), "Generic exception."); } } diff --git a/3rdparty/openal/al/eax/x_ram.h b/3rdparty/openal/al/eax/x_ram.h index d10fe6973acb..3616550d2018 100644 --- a/3rdparty/openal/al/eax/x_ram.h +++ b/3rdparty/openal/al/eax/x_ram.h @@ -24,7 +24,6 @@ constexpr auto AL_STORAGE_AUTOMATIC_NAME = "AL_STORAGE_AUTOMATIC"; 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) noexcept; ALenum AL_APIENTRY EAXGetBufferMode(ALuint buffer, ALint *pReserved) noexcept; diff --git a/3rdparty/openal/al/effect.cpp b/3rdparty/openal/al/effect.cpp index c4b064078a9e..d72b9df2ef0c 100644 --- a/3rdparty/openal/al/effect.cpp +++ b/3rdparty/openal/al/effect.cpp @@ -28,9 +28,12 @@ #include #include #include -#include #include +#include +#include +#include #include +#include #include #include "AL/al.h" @@ -39,26 +42,23 @@ #include "AL/efx-presets.h" #include "AL/efx.h" +#include "al/effects/effects.h" #include "albit.h" #include "alc/context.h" #include "alc/device.h" -#include "alc/effects/base.h" #include "alc/inprogext.h" #include "almalloc.h" #include "alnumeric.h" +#include "alspan.h" #include "alstring.h" -#include "core/except.h" #include "core/logging.h" #include "direct_defs.h" +#include "error.h" +#include "intrusive_ptr.h" #include "opthelpers.h" -#ifdef ALSOFT_EAX -#include -#include "eax/exception.h" -#endif // ALSOFT_EAX - -const EffectList gEffectList[16]{ +const std::array gEffectList{{ { "eaxreverb", EAXREVERB_EFFECT, AL_EFFECT_EAXREVERB }, { "reverb", REVERB_EFFECT, AL_EFFECT_REVERB }, { "autowah", AUTOWAH_EFFECT, AL_EFFECT_AUTOWAH }, @@ -74,94 +74,73 @@ 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]; - - -effect_exception::effect_exception(ALenum code, const char *msg, ...) : mErrorCode{code} -{ - std::va_list args; - va_start(args, msg); - setMessage(msg, args); - va_end(args); -} -effect_exception::~effect_exception() = default; namespace { -struct EffectPropsItem { - ALenum Type; - const EffectProps &DefaultProps; - const EffectVtable &Vtable; -}; -constexpr EffectPropsItem EffectPropsList[] = { - { AL_EFFECT_NULL, NullEffectProps, NullEffectVtable }, - { AL_EFFECT_EAXREVERB, ReverbEffectProps, ReverbEffectVtable }, - { AL_EFFECT_REVERB, StdReverbEffectProps, StdReverbEffectVtable }, - { AL_EFFECT_AUTOWAH, AutowahEffectProps, AutowahEffectVtable }, - { AL_EFFECT_CHORUS, ChorusEffectProps, ChorusEffectVtable }, - { AL_EFFECT_COMPRESSOR, CompressorEffectProps, CompressorEffectVtable }, - { AL_EFFECT_DISTORTION, DistortionEffectProps, DistortionEffectVtable }, - { AL_EFFECT_ECHO, EchoEffectProps, EchoEffectVtable }, - { AL_EFFECT_EQUALIZER, EqualizerEffectProps, EqualizerEffectVtable }, - { AL_EFFECT_FLANGER, FlangerEffectProps, FlangerEffectVtable }, - { AL_EFFECT_FREQUENCY_SHIFTER, FshifterEffectProps, FshifterEffectVtable }, - { AL_EFFECT_RING_MODULATOR, ModulatorEffectProps, ModulatorEffectVtable }, - { AL_EFFECT_PITCH_SHIFTER, PshifterEffectProps, PshifterEffectVtable }, - { 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 }, -}; - - -void ALeffect_setParami(ALeffect *effect, ALenum param, int value) -{ effect->vtab->setParami(&effect->Props, param, value); } -void ALeffect_setParamiv(ALeffect *effect, ALenum param, const int *values) -{ effect->vtab->setParamiv(&effect->Props, param, values); } -void ALeffect_setParamf(ALeffect *effect, ALenum param, float value) -{ effect->vtab->setParamf(&effect->Props, param, value); } -void ALeffect_setParamfv(ALeffect *effect, ALenum param, const float *values) -{ effect->vtab->setParamfv(&effect->Props, param, values); } +using SubListAllocator = al::allocator>; -void ALeffect_getParami(const ALeffect *effect, ALenum param, int *value) -{ effect->vtab->getParami(&effect->Props, param, value); } -void ALeffect_getParamiv(const ALeffect *effect, ALenum param, int *values) -{ effect->vtab->getParamiv(&effect->Props, param, values); } -void ALeffect_getParamf(const ALeffect *effect, ALenum param, float *value) -{ effect->vtab->getParamf(&effect->Props, param, value); } -void ALeffect_getParamfv(const ALeffect *effect, ALenum param, float *values) -{ effect->vtab->getParamfv(&effect->Props, param, values); } - - -const EffectPropsItem *getEffectPropsItemByType(ALenum type) +constexpr auto GetDefaultProps(ALenum type) noexcept -> const EffectProps& { - auto iter = std::find_if(std::begin(EffectPropsList), std::end(EffectPropsList), - [type](const EffectPropsItem &item) noexcept -> bool - { return item.Type == type; }); - return (iter != std::end(EffectPropsList)) ? al::to_address(iter) : nullptr; + switch(type) + { + case AL_EFFECT_NULL: return NullEffectProps; + case AL_EFFECT_EAXREVERB: return ReverbEffectProps; + case AL_EFFECT_REVERB: return StdReverbEffectProps; + case AL_EFFECT_AUTOWAH: return AutowahEffectProps; + case AL_EFFECT_CHORUS: return ChorusEffectProps; + case AL_EFFECT_COMPRESSOR: return CompressorEffectProps; + case AL_EFFECT_DISTORTION: return DistortionEffectProps; + case AL_EFFECT_ECHO: return EchoEffectProps; + case AL_EFFECT_EQUALIZER: return EqualizerEffectProps; + case AL_EFFECT_FLANGER: return FlangerEffectProps; + case AL_EFFECT_FREQUENCY_SHIFTER: return FshifterEffectProps; + case AL_EFFECT_RING_MODULATOR: return ModulatorEffectProps; + case AL_EFFECT_PITCH_SHIFTER: return PshifterEffectProps; + case AL_EFFECT_VOCAL_MORPHER: return VmorpherEffectProps; + case AL_EFFECT_DEDICATED_DIALOGUE: return DedicatedDialogEffectProps; + case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return DedicatedLfeEffectProps; + case AL_EFFECT_CONVOLUTION_SOFT: return ConvolutionEffectProps; + } + return NullEffectProps; } -void InitEffectParams(ALeffect *effect, ALenum type) +void InitEffectParams(ALeffect *effect, ALenum type) noexcept { - const EffectPropsItem *item{getEffectPropsItemByType(type)}; - if(item) - { - effect->Props = item->DefaultProps; - effect->vtab = &item->Vtable; - } - else + switch(type) { - effect->Props = EffectProps{}; - effect->vtab = &NullEffectVtable; + case AL_EFFECT_NULL: effect->PropsVariant.emplace(); break; + case AL_EFFECT_EAXREVERB: effect->PropsVariant.emplace(); break; + case AL_EFFECT_REVERB: effect->PropsVariant.emplace(); break; + case AL_EFFECT_AUTOWAH: effect->PropsVariant.emplace(); break; + case AL_EFFECT_CHORUS: effect->PropsVariant.emplace(); break; + case AL_EFFECT_COMPRESSOR: effect->PropsVariant.emplace(); break; + case AL_EFFECT_DISTORTION: effect->PropsVariant.emplace(); break; + case AL_EFFECT_ECHO: effect->PropsVariant.emplace(); break; + case AL_EFFECT_EQUALIZER: effect->PropsVariant.emplace(); break; + case AL_EFFECT_FLANGER: effect->PropsVariant.emplace(); break; + case AL_EFFECT_FREQUENCY_SHIFTER: effect->PropsVariant.emplace(); break; + case AL_EFFECT_RING_MODULATOR: effect->PropsVariant.emplace(); break; + case AL_EFFECT_PITCH_SHIFTER: effect->PropsVariant.emplace(); break; + case AL_EFFECT_VOCAL_MORPHER: effect->PropsVariant.emplace(); break; + case AL_EFFECT_DEDICATED_DIALOGUE: + effect->PropsVariant.emplace(); + break; + case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: + effect->PropsVariant.emplace(); + break; + case AL_EFFECT_CONVOLUTION_SOFT: + effect->PropsVariant.emplace(); + break; } + effect->Props = GetDefaultProps(type); effect->type = type; } -bool EnsureEffects(ALCdevice *device, size_t needed) -{ +auto EnsureEffects(al::Device *device, size_t needed) noexcept -> bool +try { 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(al::popcount(sublist.FreeMask)); })}; @@ -171,21 +150,19 @@ bool EnsureEffects(ALCdevice *device, size_t needed) if(device->EffectList.size() >= 1<<25) UNLIKELY return false; - device->EffectList.emplace_back(); - auto sublist = device->EffectList.end() - 1; - sublist->FreeMask = ~0_u64; - sublist->Effects = static_cast(al_calloc(alignof(ALeffect), sizeof(ALeffect)*64)); - if(!sublist->Effects) UNLIKELY - { - device->EffectList.pop_back(); - return false; - } - count += 64; + EffectSubList sublist{}; + sublist.FreeMask = ~0_u64; + sublist.Effects = SubListAllocator{}.allocate(1); + device->EffectList.emplace_back(std::move(sublist)); + count += std::tuple_size_v; } return true; } +catch(...) { + return false; +} -ALeffect *AllocEffect(ALCdevice *device) +auto AllocEffect(al::Device *device) noexcept -> ALeffect* { auto sublist = std::find_if(device->EffectList.begin(), device->EffectList.end(), [](const EffectSubList &entry) noexcept -> bool @@ -194,7 +171,7 @@ ALeffect *AllocEffect(ALCdevice *device) auto slidx = static_cast(al::countr_zero(sublist->FreeMask)); ASSUME(slidx < 64); - ALeffect *effect{al::construct_at(sublist->Effects + slidx)}; + ALeffect *effect{al::construct_at(al::to_address(sublist->Effects->begin() + slidx))}; InitEffectParams(effect, AL_EFFECT_NULL); /* Add 1 to avoid effect ID 0. */ @@ -205,7 +182,7 @@ ALeffect *AllocEffect(ALCdevice *device) return effect; } -void FreeEffect(ALCdevice *device, ALeffect *effect) +void FreeEffect(al::Device *device, ALeffect *effect) { device->mEffectNames.erase(effect->id); @@ -218,7 +195,7 @@ void FreeEffect(ALCdevice *device, ALeffect *effect) device->EffectList[lidx].FreeMask |= 1_u64 << slidx; } -inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) +inline auto LookupEffect(al::Device *device, ALuint id) noexcept -> ALeffect* { const size_t lidx{(id-1) >> 6}; const ALuint slidx{(id-1) & 0x3f}; @@ -228,222 +205,223 @@ inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) EffectSubList &sublist = device->EffectList[lidx]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Effects + slidx; + return al::to_address(sublist.Effects->begin() + slidx); } } // namespace -AL_API DECL_FUNC2(void, alGenEffects, ALsizei, ALuint*) +AL_API DECL_FUNC2(void, alGenEffects, ALsizei,n, ALuint*,effects) FORCE_ALIGN void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Generating %d effects", n); +try { + if(n < 0) + throw al::context_error{AL_INVALID_VALUE, "Generating %d effects", n}; if(n <= 0) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; - if(!EnsureEffects(device, static_cast(n))) - { - context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d effect%s", n, (n==1)?"":"s"); - return; - } + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; - if(n == 1) LIKELY - { - /* Special handling for the easy and normal case. */ - ALeffect *effect{AllocEffect(device)}; - effects[0] = effect->id; - } - else - { - /* Store the allocated buffer IDs in a separate local list, to avoid - * modifying the user storage in case of failure. - */ - std::vector ids; - ids.reserve(static_cast(n)); - do { - ALeffect *effect{AllocEffect(device)}; - ids.emplace_back(effect->id); - } while(--n); - std::copy(ids.cbegin(), ids.cend(), effects); - } + const al::span eids{effects, static_cast(n)}; + if(!EnsureEffects(device, eids.size())) + throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d effect%s", n, + (n == 1) ? "" : "s"}; + + std::generate(eids.begin(), eids.end(), [device]{ return AllocEffect(device)->id; }); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC2(void, alDeleteEffects, ALsizei, const ALuint*) +AL_API DECL_FUNC2(void, alDeleteEffects, ALsizei,n, const ALuint*,effects) FORCE_ALIGN void AL_APIENTRY alDeleteEffectsDirect(ALCcontext *context, ALsizei n, const ALuint *effects) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Deleting %d effects", n); +try { + if(n < 0) + throw al::context_error{AL_INVALID_VALUE, "Deleting %d effects", n}; if(n <= 0) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; /* First try to find any effects that are invalid. */ auto validate_effect = [device](const ALuint eid) -> bool { return !eid || LookupEffect(device, eid) != nullptr; }; - const ALuint *effects_end = effects + n; - auto inveffect = std::find_if_not(effects, effects_end, validate_effect); - if(inveffect != effects_end) UNLIKELY - { - context->setError(AL_INVALID_NAME, "Invalid effect ID %u", *inveffect); - return; - } + const al::span eids{effects, static_cast(n)}; + auto inveffect = std::find_if_not(eids.begin(), eids.end(), validate_effect); + if(inveffect != eids.end()) + throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", *inveffect}; /* All good. Delete non-0 effect IDs. */ auto delete_effect = [device](ALuint eid) -> void { - ALeffect *effect{eid ? LookupEffect(device, eid) : nullptr}; - if(effect) FreeEffect(device, effect); + if(ALeffect *effect{eid ? LookupEffect(device, eid) : nullptr}) + FreeEffect(device, effect); }; - std::for_each(effects, effects_end, delete_effect); + std::for_each(eids.begin(), eids.end(), delete_effect); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC1(ALboolean, alIsEffect, ALuint) +AL_API DECL_FUNC1(ALboolean, alIsEffect, ALuint,effect) FORCE_ALIGN ALboolean AL_APIENTRY alIsEffectDirect(ALCcontext *context, ALuint effect) noexcept { - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; if(!effect || LookupEffect(device, effect)) return AL_TRUE; return AL_FALSE; } -AL_API DECL_FUNC3(void, alEffecti, ALuint, ALenum, ALint) +AL_API DECL_FUNC3(void, alEffecti, ALuint,effect, ALenum,param, ALint,value) FORCE_ALIGN void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, ALenum param, ALint value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; +try { + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; ALeffect *aleffect{LookupEffect(device, effect)}; - if(!aleffect) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect); - else if(param == AL_EFFECT_TYPE) + if(!aleffect) + throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect}; + + switch(param) { - bool isOk{value == AL_EFFECT_NULL}; - if(!isOk) + case AL_EFFECT_TYPE: + if(value != AL_EFFECT_NULL) { - for(const EffectList &effectitem : gEffectList) - { - if(value == effectitem.val && !DisabledEffects[effectitem.type]) - { - isOk = true; - break; - } - } + auto check_effect = [value](const EffectList &item) -> bool + { return value == item.val && !DisabledEffects.test(item.type); }; + if(!std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect)) + throw al::context_error{AL_INVALID_VALUE, "Effect type 0x%04x not supported", + value}; } - if(isOk) - InitEffectParams(aleffect, value); - else - context->setError(AL_INVALID_VALUE, "Effect type 0x%04x not supported", value); + InitEffectParams(aleffect, value); + return; } - else try + + /* Call the appropriate handler */ + std::visit([aleffect,param,value](auto &arg) { - /* Call the appropriate handler */ - ALeffect_setParami(aleffect, param, value); - } - catch(effect_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + using Type = std::remove_cv_t>; + using PropType = typename Type::prop_type; + return arg.SetParami(std::get(aleffect->Props), param, value); + }, aleffect->PropsVariant); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alEffectiv, ALuint, ALenum, const ALint*) +AL_API DECL_FUNC3(void, alEffectiv, ALuint,effect, ALenum,param, const ALint*,values) FORCE_ALIGN void AL_APIENTRY alEffectivDirect(ALCcontext *context, ALuint effect, ALenum param, const ALint *values) noexcept -{ +try { switch(param) { case AL_EFFECT_TYPE: - alEffectiDirect(context, effect, param, values[0]); + alEffectiDirect(context, effect, param, *values); return; } - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; ALeffect *aleffect{LookupEffect(device, effect)}; - if(!aleffect) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect); - else try + if(!aleffect) + throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect}; + + /* Call the appropriate handler */ + std::visit([aleffect,param,values](auto &arg) { - /* Call the appropriate handler */ - ALeffect_setParamiv(aleffect, param, values); - } - catch(effect_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + using Type = std::remove_cv_t>; + using PropType = typename Type::prop_type; + return arg.SetParamiv(std::get(aleffect->Props), param, values); + }, aleffect->PropsVariant); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alEffectf, ALuint, ALenum, ALfloat) +AL_API DECL_FUNC3(void, alEffectf, ALuint,effect, ALenum,param, ALfloat,value) FORCE_ALIGN void AL_APIENTRY alEffectfDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; +try { + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; ALeffect *aleffect{LookupEffect(device, effect)}; if(!aleffect) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect); - else try + throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect}; + + /* Call the appropriate handler */ + std::visit([aleffect,param,value](auto &arg) { - /* Call the appropriate handler */ - ALeffect_setParamf(aleffect, param, value); - } - catch(effect_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + using Type = std::remove_cv_t>; + using PropType = typename Type::prop_type; + return arg.SetParamf(std::get(aleffect->Props), param, value); + }, aleffect->PropsVariant); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alEffectfv, ALuint, ALenum, const ALfloat*) +AL_API DECL_FUNC3(void, alEffectfv, ALuint,effect, ALenum,param, const ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param, const ALfloat *values) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; +try { + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; ALeffect *aleffect{LookupEffect(device, effect)}; - if(!aleffect) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect); - else try + if(!aleffect) + throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect}; + + /* Call the appropriate handler */ + std::visit([aleffect,param,values](auto &arg) { - /* Call the appropriate handler */ - ALeffect_setParamfv(aleffect, param, values); - } - catch(effect_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + using Type = std::remove_cv_t>; + using PropType = typename Type::prop_type; + return arg.SetParamfv(std::get(aleffect->Props), param, values); + }, aleffect->PropsVariant); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alGetEffecti, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetEffecti, ALuint,effect, ALenum,param, ALint*,value) FORCE_ALIGN void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effect, ALenum param, ALint *value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; +try { + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; const ALeffect *aleffect{LookupEffect(device, effect)}; - if(!aleffect) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect); - else if(param == AL_EFFECT_TYPE) - *value = aleffect->type; - else try + if(!aleffect) + throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect}; + + switch(param) { - /* Call the appropriate handler */ - ALeffect_getParami(aleffect, param, value); - } - catch(effect_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); + case AL_EFFECT_TYPE: + *value = aleffect->type; + return; } + + /* Call the appropriate handler */ + std::visit([aleffect,param,value](auto &arg) + { + using Type = std::remove_cv_t>; + using PropType = typename Type::prop_type; + return arg.GetParami(std::get(aleffect->Props), param, value); + }, aleffect->PropsVariant); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alGetEffectiv, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetEffectiv, ALuint,effect, ALenum,param, ALint*,values) FORCE_ALIGN void AL_APIENTRY alGetEffectivDirect(ALCcontext *context, ALuint effect, ALenum param, ALint *values) noexcept -{ +try { switch(param) { case AL_EFFECT_TYPE: @@ -451,60 +429,69 @@ FORCE_ALIGN void AL_APIENTRY alGetEffectivDirect(ALCcontext *context, ALuint eff return; } - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; const ALeffect *aleffect{LookupEffect(device, effect)}; - if(!aleffect) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect); - else try + if(!aleffect) + throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect}; + + /* Call the appropriate handler */ + std::visit([aleffect,param,values](auto &arg) { - /* Call the appropriate handler */ - ALeffect_getParamiv(aleffect, param, values); - } - catch(effect_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + using Type = std::remove_cv_t>; + using PropType = typename Type::prop_type; + return arg.GetParamiv(std::get(aleffect->Props), param, values); + }, aleffect->PropsVariant); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alGetEffectf, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetEffectf, ALuint,effect, ALenum,param, ALfloat*,value) FORCE_ALIGN void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat *value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; +try { + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; const ALeffect *aleffect{LookupEffect(device, effect)}; - if(!aleffect) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect); - else try + if(!aleffect) + throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect}; + + /* Call the appropriate handler */ + std::visit([aleffect,param,value](auto &arg) { - /* Call the appropriate handler */ - ALeffect_getParamf(aleffect, param, value); - } - catch(effect_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + using Type = std::remove_cv_t>; + using PropType = typename Type::prop_type; + return arg.GetParamf(std::get(aleffect->Props), param, value); + }, aleffect->PropsVariant); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alGetEffectfv, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetEffectfv, ALuint,effect, ALenum,param, ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat *values) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; +try { + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; const ALeffect *aleffect{LookupEffect(device, effect)}; - if(!aleffect) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect); - else try + if(!aleffect) + throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect}; + + /* Call the appropriate handler */ + std::visit([aleffect,param,values](auto &arg) { - /* Call the appropriate handler */ - ALeffect_getParamfv(aleffect, param, values); - } - catch(effect_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + using Type = std::remove_cv_t>; + using PropType = typename Type::prop_type; + return arg.GetParamfv(std::get(aleffect->Props), param, values); + }, aleffect->PropsVariant); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } @@ -515,12 +502,12 @@ void InitEffect(ALeffect *effect) void ALeffect::SetName(ALCcontext* context, ALuint id, std::string_view name) { - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; auto effect = LookupEffect(device, id); - if(!effect) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect ID %u", id); + if(!effect) + throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", id}; device->mEffectNames.insert_or_assign(id, name); } @@ -535,20 +522,21 @@ EffectSubList::~EffectSubList() while(usemask) { const int idx{al::countr_zero(usemask)}; - std::destroy_at(Effects+idx); + std::destroy_at(al::to_address(Effects->begin()+idx)); usemask &= ~(1_u64 << idx); } FreeMask = ~usemask; - al_free(Effects); + SubListAllocator{}.deallocate(Effects, 1); Effects = nullptr; } -#define DECL(x) { #x, EFX_REVERB_PRESET_##x } -static const struct { - const char name[32]; +struct EffectPreset { + const char name[32]; /* NOLINT(*-avoid-c-arrays) */ EFXEAXREVERBPROPERTIES props; -} reverblist[] = { +}; +#define DECL(x) EffectPreset{#x, EFX_REVERB_PRESET_##x} +static constexpr std::array reverblist{ DECL(GENERIC), DECL(PADDEDCELL), DECL(ROOM), @@ -678,61 +666,62 @@ static const struct { }; #undef DECL -void LoadReverbPreset(const char *name, ALeffect *effect) +void LoadReverbPreset(const std::string_view name, ALeffect *effect) { - if(al::strcasecmp(name, "NONE") == 0) + using namespace std::string_view_literals; + + if(al::case_compare(name, "NONE"sv) == 0) { InitEffectParams(effect, AL_EFFECT_NULL); TRACE("Loading reverb '%s'\n", "NONE"); return; } - if(!DisabledEffects[EAXREVERB_EFFECT]) + if(!DisabledEffects.test(EAXREVERB_EFFECT)) InitEffectParams(effect, AL_EFFECT_EAXREVERB); - else if(!DisabledEffects[REVERB_EFFECT]) + else if(!DisabledEffects.test(REVERB_EFFECT)) InitEffectParams(effect, AL_EFFECT_REVERB); else InitEffectParams(effect, AL_EFFECT_NULL); for(const auto &reverbitem : reverblist) { - const EFXEAXREVERBPROPERTIES *props; - - if(al::strcasecmp(name, reverbitem.name) != 0) + if(al::case_compare(name, std::data(reverbitem.name)) != 0) continue; - TRACE("Loading reverb '%s'\n", reverbitem.name); - props = &reverbitem.props; - effect->Props.Reverb.Density = props->flDensity; - effect->Props.Reverb.Diffusion = props->flDiffusion; - effect->Props.Reverb.Gain = props->flGain; - effect->Props.Reverb.GainHF = props->flGainHF; - effect->Props.Reverb.GainLF = props->flGainLF; - effect->Props.Reverb.DecayTime = props->flDecayTime; - effect->Props.Reverb.DecayHFRatio = props->flDecayHFRatio; - effect->Props.Reverb.DecayLFRatio = props->flDecayLFRatio; - effect->Props.Reverb.ReflectionsGain = props->flReflectionsGain; - effect->Props.Reverb.ReflectionsDelay = props->flReflectionsDelay; - effect->Props.Reverb.ReflectionsPan[0] = props->flReflectionsPan[0]; - effect->Props.Reverb.ReflectionsPan[1] = props->flReflectionsPan[1]; - effect->Props.Reverb.ReflectionsPan[2] = props->flReflectionsPan[2]; - effect->Props.Reverb.LateReverbGain = props->flLateReverbGain; - effect->Props.Reverb.LateReverbDelay = props->flLateReverbDelay; - effect->Props.Reverb.LateReverbPan[0] = props->flLateReverbPan[0]; - effect->Props.Reverb.LateReverbPan[1] = props->flLateReverbPan[1]; - effect->Props.Reverb.LateReverbPan[2] = props->flLateReverbPan[2]; - effect->Props.Reverb.EchoTime = props->flEchoTime; - effect->Props.Reverb.EchoDepth = props->flEchoDepth; - effect->Props.Reverb.ModulationTime = props->flModulationTime; - effect->Props.Reverb.ModulationDepth = props->flModulationDepth; - effect->Props.Reverb.AirAbsorptionGainHF = props->flAirAbsorptionGainHF; - effect->Props.Reverb.HFReference = props->flHFReference; - effect->Props.Reverb.LFReference = props->flLFReference; - effect->Props.Reverb.RoomRolloffFactor = props->flRoomRolloffFactor; - effect->Props.Reverb.DecayHFLimit = props->iDecayHFLimit ? AL_TRUE : AL_FALSE; + TRACE("Loading reverb '%s'\n", std::data(reverbitem.name)); + const auto &props = reverbitem.props; + auto &dst = std::get(effect->Props); + dst.Density = props.flDensity; + dst.Diffusion = props.flDiffusion; + dst.Gain = props.flGain; + dst.GainHF = props.flGainHF; + dst.GainLF = props.flGainLF; + dst.DecayTime = props.flDecayTime; + dst.DecayHFRatio = props.flDecayHFRatio; + dst.DecayLFRatio = props.flDecayLFRatio; + dst.ReflectionsGain = props.flReflectionsGain; + dst.ReflectionsDelay = props.flReflectionsDelay; + dst.ReflectionsPan[0] = props.flReflectionsPan[0]; + dst.ReflectionsPan[1] = props.flReflectionsPan[1]; + dst.ReflectionsPan[2] = props.flReflectionsPan[2]; + dst.LateReverbGain = props.flLateReverbGain; + dst.LateReverbDelay = props.flLateReverbDelay; + dst.LateReverbPan[0] = props.flLateReverbPan[0]; + dst.LateReverbPan[1] = props.flLateReverbPan[1]; + dst.LateReverbPan[2] = props.flLateReverbPan[2]; + dst.EchoTime = props.flEchoTime; + dst.EchoDepth = props.flEchoDepth; + dst.ModulationTime = props.flModulationTime; + dst.ModulationDepth = props.flModulationDepth; + dst.AirAbsorptionGainHF = props.flAirAbsorptionGainHF; + dst.HFReference = props.flHFReference; + dst.LFReference = props.flLFReference; + dst.RoomRolloffFactor = props.flRoomRolloffFactor; + dst.DecayHFLimit = props.iDecayHFLimit ? AL_TRUE : AL_FALSE; return; } - WARN("Reverb preset '%s' not found\n", name); + WARN("Reverb preset '%.*s' not found\n", al::sizei(name), name.data()); } bool IsValidEffectType(ALenum type) noexcept @@ -740,10 +729,7 @@ bool IsValidEffectType(ALenum type) noexcept if(type == AL_EFFECT_NULL) return true; - for(const auto &effect_item : gEffectList) - { - if(type == effect_item.val && !DisabledEffects[effect_item.type]) - return true; - } - return false; + auto check_effect = [type](const EffectList &item) noexcept -> bool + { return type == item.val && !DisabledEffects.test(item.type); }; + return std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect); } diff --git a/3rdparty/openal/al/effect.h b/3rdparty/openal/al/effect.h index 27e9dd72dc30..8b2351488307 100644 --- a/3rdparty/openal/al/effect.h +++ b/3rdparty/openal/al/effect.h @@ -1,13 +1,20 @@ #ifndef AL_EFFECT_H #define AL_EFFECT_H +#include +#include +#include #include +#include #include "AL/al.h" +#include "AL/alc.h" #include "AL/efx.h" -#include "al/effects/effects.h" -#include "alc/effects/base.h" +#include "almalloc.h" +#include "alnumeric.h" +#include "core/effects/base.h" +#include "effects/effects.h" enum { @@ -29,38 +36,55 @@ enum { MAX_EFFECTS }; -extern bool DisabledEffects[MAX_EFFECTS]; - -extern float ReverbBoost; +inline std::bitset DisabledEffects; struct EffectList { - const char name[16]; - int type; + const char name[16]; /* NOLINT(*-avoid-c-arrays) */ + ALuint type; ALenum val; }; -extern const EffectList gEffectList[16]; +extern const std::array gEffectList; +using EffectHandlerVariant = std::variant; struct ALeffect { // Effect type (AL_EFFECT_NULL, ...) ALenum type{AL_EFFECT_NULL}; - EffectProps Props{}; - - const EffectVtable *vtab{nullptr}; + EffectHandlerVariant PropsVariant; + EffectProps Props; /* Self ID */ ALuint id{0u}; static void SetName(ALCcontext *context, ALuint id, std::string_view name); - DISABLE_ALLOC() + DISABLE_ALLOC }; void InitEffect(ALeffect *effect); -void LoadReverbPreset(const char *name, ALeffect *effect); +void LoadReverbPreset(const std::string_view name, ALeffect *effect); bool IsValidEffectType(ALenum type) noexcept; +struct EffectSubList { + uint64_t FreeMask{~0_u64}; + gsl::owner*> Effects{nullptr}; /* 64 */ + + EffectSubList() noexcept = default; + EffectSubList(const EffectSubList&) = delete; + EffectSubList(EffectSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Effects{rhs.Effects} + { rhs.FreeMask = ~0_u64; rhs.Effects = nullptr; } + ~EffectSubList(); + + EffectSubList& operator=(const EffectSubList&) = delete; + EffectSubList& operator=(EffectSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(Effects, rhs.Effects); return *this; } +}; + #endif diff --git a/3rdparty/openal/al/effects/autowah.cpp b/3rdparty/openal/al/effects/autowah.cpp index 1a8b43fcfadb..68d07281c70f 100644 --- a/3rdparty/openal/al/effects/autowah.cpp +++ b/3rdparty/openal/al/effects/autowah.cpp @@ -11,8 +11,9 @@ #include "alc/effects/base.h" #include "effects.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include "alnumeric.h" +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -20,102 +21,89 @@ namespace { -void Autowah_setParamf(EffectProps *props, ALenum param, float val) +constexpr EffectProps genDefaultProps() noexcept +{ + AutowahProps props{}; + props.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME; + props.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME; + props.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE; + props.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN; + return props; +} + +} // namespace + +const EffectProps AutowahEffectProps{genDefaultProps()}; + +void AutowahEffectHandler::SetParami(AutowahProps&, ALenum param, int) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; } +void AutowahEffectHandler::SetParamiv(AutowahProps&, ALenum param, const int*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", + param}; +} + +void AutowahEffectHandler::SetParamf(AutowahProps &props, ALenum param, float val) { switch(param) { case AL_AUTOWAH_ATTACK_TIME: if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME)) throw effect_exception{AL_INVALID_VALUE, "Autowah attack time out of range"}; - props->Autowah.AttackTime = val; + props.AttackTime = val; break; case AL_AUTOWAH_RELEASE_TIME: if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME)) throw effect_exception{AL_INVALID_VALUE, "Autowah release time out of range"}; - props->Autowah.ReleaseTime = val; + props.ReleaseTime = val; break; case AL_AUTOWAH_RESONANCE: if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE)) throw effect_exception{AL_INVALID_VALUE, "Autowah resonance out of range"}; - props->Autowah.Resonance = val; + props.Resonance = val; break; case AL_AUTOWAH_PEAK_GAIN: if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN)) throw effect_exception{AL_INVALID_VALUE, "Autowah peak gain out of range"}; - props->Autowah.PeakGain = val; + props.PeakGain = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param}; } } -void Autowah_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Autowah_setParamf(props, param, vals[0]); } +void AutowahEffectHandler::SetParamfv(AutowahProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, *vals); } -void Autowah_setParami(EffectProps*, ALenum param, int) +void AutowahEffectHandler::GetParami(const AutowahProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; } -void Autowah_setParamiv(EffectProps*, ALenum param, const int*) +void AutowahEffectHandler::GetParamiv(const AutowahProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param}; } -void Autowah_getParamf(const EffectProps *props, ALenum param, float *val) +void AutowahEffectHandler::GetParamf(const AutowahProps &props, ALenum param, float *val) { switch(param) { - case AL_AUTOWAH_ATTACK_TIME: - *val = props->Autowah.AttackTime; - break; - - case AL_AUTOWAH_RELEASE_TIME: - *val = props->Autowah.ReleaseTime; - break; - - case AL_AUTOWAH_RESONANCE: - *val = props->Autowah.Resonance; - break; - - case AL_AUTOWAH_PEAK_GAIN: - *val = props->Autowah.PeakGain; - break; + case AL_AUTOWAH_ATTACK_TIME: *val = props.AttackTime; break; + case AL_AUTOWAH_RELEASE_TIME: *val = props.ReleaseTime; break; + case AL_AUTOWAH_RESONANCE: *val = props.Resonance; break; + case AL_AUTOWAH_PEAK_GAIN: *val = props.PeakGain; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param}; } } -void Autowah_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Autowah_getParamf(props, param, vals); } - -void Autowah_getParami(const EffectProps*, ALenum param, int*) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; } -void Autowah_getParamiv(const EffectProps*, ALenum param, int*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", - param}; -} - -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME; - props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME; - props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE; - props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN; - return props; -} +void AutowahEffectHandler::GetParamfv(const AutowahProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } -} // namespace - -DEFINE_ALEFFECT_VTABLE(Autowah); - -const EffectProps AutowahEffectProps{genDefaultProps()}; - -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using AutowahCommitter = EaxCommitter; @@ -189,25 +177,25 @@ template<> throw Exception{message}; } -template<> -bool AutowahCommitter::commit(const EaxEffectProps &props) +bool EaxAutowahCommitter::commit(const EAXAUTOWAHPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; - - auto &eaxprops = std::get(props); - mAlProps.Autowah.AttackTime = eaxprops.flAttackTime; - mAlProps.Autowah.ReleaseTime = eaxprops.flReleaseTime; - mAlProps.Autowah.Resonance = level_mb_to_gain(static_cast(eaxprops.lResonance)); - mAlProps.Autowah.PeakGain = level_mb_to_gain(static_cast(eaxprops.lPeakLevel)); + mAlProps = [&]{ + AutowahProps ret{}; + ret.AttackTime = props.flAttackTime; + ret.ReleaseTime = props.flReleaseTime; + ret.Resonance = level_mb_to_gain(static_cast(props.lResonance)); + ret.PeakGain = level_mb_to_gain(static_cast(props.lPeakLevel)); + return ret; + }(); return true; } -template<> -void AutowahCommitter::SetDefaults(EaxEffectProps &props) +void EaxAutowahCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXAUTOWAHPROPERTIES defprops{[] { @@ -221,10 +209,8 @@ void AutowahCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void AutowahCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxAutowahCommitter::Get(const EaxCall &call, const EAXAUTOWAHPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXAUTOWAH_NONE: break; @@ -237,10 +223,8 @@ void AutowahCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) } } -template<> -void AutowahCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxAutowahCommitter::Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXAUTOWAH_NONE: break; diff --git a/3rdparty/openal/al/effects/chorus.cpp b/3rdparty/openal/al/effects/chorus.cpp index 90b38e4d4eed..7d8569bcd61a 100644 --- a/3rdparty/openal/al/effects/chorus.cpp +++ b/3rdparty/openal/al/effects/chorus.cpp @@ -7,13 +7,11 @@ #include "AL/al.h" #include "AL/efx.h" -#include "alc/effects/base.h" -#include "core/logging.h" #include "effects.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include -#include "alnumeric.h" +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -27,7 +25,7 @@ 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 std::optional WaveformFromEnum(ALenum type) +constexpr std::optional WaveformFromEnum(ALenum type) noexcept { switch(type) { @@ -36,7 +34,7 @@ inline std::optional WaveformFromEnum(ALenum type) } return std::nullopt; } -inline ALenum EnumFromWaveform(ChorusWaveform type) +constexpr ALenum EnumFromWaveform(ChorusWaveform type) { switch(type) { @@ -46,13 +44,41 @@ inline ALenum EnumFromWaveform(ChorusWaveform type) throw std::runtime_error{"Invalid chorus waveform: "+std::to_string(static_cast(type))}; } -void Chorus_setParami(EffectProps *props, ALenum param, int val) +constexpr EffectProps genDefaultChorusProps() noexcept +{ + ChorusProps props{}; + props.Waveform = WaveformFromEnum(AL_CHORUS_DEFAULT_WAVEFORM).value(); + props.Phase = AL_CHORUS_DEFAULT_PHASE; + props.Rate = AL_CHORUS_DEFAULT_RATE; + props.Depth = AL_CHORUS_DEFAULT_DEPTH; + props.Feedback = AL_CHORUS_DEFAULT_FEEDBACK; + props.Delay = AL_CHORUS_DEFAULT_DELAY; + return props; +} + +constexpr EffectProps genDefaultFlangerProps() noexcept +{ + ChorusProps props{}; + props.Waveform = WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM).value(); + props.Phase = AL_FLANGER_DEFAULT_PHASE; + props.Rate = AL_FLANGER_DEFAULT_RATE; + props.Depth = AL_FLANGER_DEFAULT_DEPTH; + props.Feedback = AL_FLANGER_DEFAULT_FEEDBACK; + props.Delay = AL_FLANGER_DEFAULT_DELAY; + return props; +} + +} // namespace + +const EffectProps ChorusEffectProps{genDefaultChorusProps()}; + +void ChorusEffectHandler::SetParami(ChorusProps &props, ALenum param, int val) { switch(param) { case AL_CHORUS_WAVEFORM: if(auto formopt = WaveformFromEnum(val)) - props->Chorus.Waveform = *formopt; + props.Waveform = *formopt; else throw effect_exception{AL_INVALID_VALUE, "Invalid chorus waveform: 0x%04x", val}; break; @@ -60,115 +86,89 @@ void Chorus_setParami(EffectProps *props, ALenum param, int val) case AL_CHORUS_PHASE: if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE)) throw effect_exception{AL_INVALID_VALUE, "Chorus phase out of range: %d", val}; - props->Chorus.Phase = val; + props.Phase = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param}; } } -void Chorus_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Chorus_setParami(props, param, vals[0]); } -void Chorus_setParamf(EffectProps *props, ALenum param, float val) +void ChorusEffectHandler::SetParamiv(ChorusProps &props, ALenum param, const int *vals) +{ SetParami(props, param, *vals); } +void ChorusEffectHandler::SetParamf(ChorusProps &props, ALenum param, float val) { switch(param) { case AL_CHORUS_RATE: if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE)) throw effect_exception{AL_INVALID_VALUE, "Chorus rate out of range: %f", val}; - props->Chorus.Rate = val; + props.Rate = val; break; case AL_CHORUS_DEPTH: if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH)) throw effect_exception{AL_INVALID_VALUE, "Chorus depth out of range: %f", val}; - props->Chorus.Depth = val; + props.Depth = val; break; case AL_CHORUS_FEEDBACK: if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK)) throw effect_exception{AL_INVALID_VALUE, "Chorus feedback out of range: %f", val}; - props->Chorus.Feedback = val; + props.Feedback = val; break; case AL_CHORUS_DELAY: if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY)) throw effect_exception{AL_INVALID_VALUE, "Chorus delay out of range: %f", val}; - props->Chorus.Delay = val; + props.Delay = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param}; } } -void Chorus_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Chorus_setParamf(props, param, vals[0]); } +void ChorusEffectHandler::SetParamfv(ChorusProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, *vals); } -void Chorus_getParami(const EffectProps *props, ALenum param, int *val) +void ChorusEffectHandler::GetParami(const ChorusProps &props, ALenum param, int *val) { switch(param) { - case AL_CHORUS_WAVEFORM: - *val = EnumFromWaveform(props->Chorus.Waveform); - break; - - case AL_CHORUS_PHASE: - *val = props->Chorus.Phase; - break; + case AL_CHORUS_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break; + case AL_CHORUS_PHASE: *val = props.Phase; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param}; } } -void Chorus_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Chorus_getParami(props, param, vals); } -void Chorus_getParamf(const EffectProps *props, ALenum param, float *val) +void ChorusEffectHandler::GetParamiv(const ChorusProps &props, ALenum param, int *vals) +{ GetParami(props, param, vals); } +void ChorusEffectHandler::GetParamf(const ChorusProps &props, ALenum param, float *val) { switch(param) { - case AL_CHORUS_RATE: - *val = props->Chorus.Rate; - break; - - case AL_CHORUS_DEPTH: - *val = props->Chorus.Depth; - break; - - case AL_CHORUS_FEEDBACK: - *val = props->Chorus.Feedback; - break; - - case AL_CHORUS_DELAY: - *val = props->Chorus.Delay; - break; + case AL_CHORUS_RATE: *val = props.Rate; break; + case AL_CHORUS_DEPTH: *val = props.Depth; break; + case AL_CHORUS_FEEDBACK: *val = props.Feedback; break; + case AL_CHORUS_DELAY: *val = props.Delay; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param}; } } -void Chorus_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Chorus_getParamf(props, param, vals); } +void ChorusEffectHandler::GetParamfv(const ChorusProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } -EffectProps genDefaultChorusProps() noexcept -{ - EffectProps props{}; - props.Chorus.Waveform = *WaveformFromEnum(AL_CHORUS_DEFAULT_WAVEFORM); - props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE; - props.Chorus.Rate = AL_CHORUS_DEFAULT_RATE; - props.Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH; - props.Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK; - props.Chorus.Delay = AL_CHORUS_DEFAULT_DELAY; - return props; -} +const EffectProps FlangerEffectProps{genDefaultFlangerProps()}; -void Flanger_setParami(EffectProps *props, ALenum param, int val) +void FlangerEffectHandler::SetParami(ChorusProps &props, ALenum param, int val) { switch(param) { case AL_FLANGER_WAVEFORM: if(auto formopt = WaveformFromEnum(val)) - props->Chorus.Waveform = *formopt; + props.Waveform = *formopt; else throw effect_exception{AL_INVALID_VALUE, "Invalid flanger waveform: 0x%04x", val}; break; @@ -176,123 +176,85 @@ void Flanger_setParami(EffectProps *props, ALenum param, int val) case AL_FLANGER_PHASE: if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE)) throw effect_exception{AL_INVALID_VALUE, "Flanger phase out of range: %d", val}; - props->Chorus.Phase = val; + props.Phase = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param}; } } -void Flanger_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Flanger_setParami(props, param, vals[0]); } -void Flanger_setParamf(EffectProps *props, ALenum param, float val) +void FlangerEffectHandler::SetParamiv(ChorusProps &props, ALenum param, const int *vals) +{ SetParami(props, param, *vals); } +void FlangerEffectHandler::SetParamf(ChorusProps &props, ALenum param, float val) { switch(param) { case AL_FLANGER_RATE: if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE)) throw effect_exception{AL_INVALID_VALUE, "Flanger rate out of range: %f", val}; - props->Chorus.Rate = val; + props.Rate = val; break; case AL_FLANGER_DEPTH: if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH)) throw effect_exception{AL_INVALID_VALUE, "Flanger depth out of range: %f", val}; - props->Chorus.Depth = val; + props.Depth = val; break; case AL_FLANGER_FEEDBACK: if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK)) throw effect_exception{AL_INVALID_VALUE, "Flanger feedback out of range: %f", val}; - props->Chorus.Feedback = val; + props.Feedback = val; break; case AL_FLANGER_DELAY: if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY)) throw effect_exception{AL_INVALID_VALUE, "Flanger delay out of range: %f", val}; - props->Chorus.Delay = val; + props.Delay = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param}; } } -void Flanger_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Flanger_setParamf(props, param, vals[0]); } +void FlangerEffectHandler::SetParamfv(ChorusProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, *vals); } -void Flanger_getParami(const EffectProps *props, ALenum param, int *val) +void FlangerEffectHandler::GetParami(const ChorusProps &props, ALenum param, int *val) { switch(param) { - case AL_FLANGER_WAVEFORM: - *val = EnumFromWaveform(props->Chorus.Waveform); - break; - - case AL_FLANGER_PHASE: - *val = props->Chorus.Phase; - break; + case AL_FLANGER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break; + case AL_FLANGER_PHASE: *val = props.Phase; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param}; } } -void Flanger_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Flanger_getParami(props, param, vals); } -void Flanger_getParamf(const EffectProps *props, ALenum param, float *val) +void FlangerEffectHandler::GetParamiv(const ChorusProps &props, ALenum param, int *vals) +{ GetParami(props, param, vals); } +void FlangerEffectHandler::GetParamf(const ChorusProps &props, ALenum param, float *val) { switch(param) { - case AL_FLANGER_RATE: - *val = props->Chorus.Rate; - break; - - case AL_FLANGER_DEPTH: - *val = props->Chorus.Depth; - break; - - case AL_FLANGER_FEEDBACK: - *val = props->Chorus.Feedback; - break; - - case AL_FLANGER_DELAY: - *val = props->Chorus.Delay; - break; + case AL_FLANGER_RATE: *val = props.Rate; break; + case AL_FLANGER_DEPTH: *val = props.Depth; break; + case AL_FLANGER_FEEDBACK: *val = props.Feedback; break; + case AL_FLANGER_DELAY: *val = props.Delay; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param}; } } -void Flanger_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Flanger_getParamf(props, param, vals); } +void FlangerEffectHandler::GetParamfv(const ChorusProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } -EffectProps genDefaultFlangerProps() noexcept -{ - EffectProps props{}; - props.Chorus.Waveform = *WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM); - props.Chorus.Phase = AL_FLANGER_DEFAULT_PHASE; - props.Chorus.Rate = AL_FLANGER_DEFAULT_RATE; - props.Chorus.Depth = AL_FLANGER_DEFAULT_DEPTH; - props.Chorus.Feedback = AL_FLANGER_DEFAULT_FEEDBACK; - props.Chorus.Delay = AL_FLANGER_DEFAULT_DELAY; - return props; -} - -} // namespace -DEFINE_ALEFFECT_VTABLE(Chorus); - -const EffectProps ChorusEffectProps{genDefaultChorusProps()}; - -DEFINE_ALEFFECT_VTABLE(Flanger); - -const EffectProps FlangerEffectProps{genDefaultFlangerProps()}; - - -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { struct EaxChorusTraits { - using Props = EAXCHORUSPROPERTIES; + using EaxProps = EAXCHORUSPROPERTIES; using Committer = EaxChorusCommitter; static constexpr auto efx_effect() { return AL_EFFECT_CHORUS; } @@ -357,7 +319,7 @@ struct EaxChorusTraits { }; // EaxChorusTraits struct EaxFlangerTraits { - using Props = EAXFLANGERPROPERTIES; + using EaxProps = EAXFLANGERPROPERTIES; using Committer = EaxFlangerCommitter; static constexpr auto efx_effect() { return AL_EFFECT_FLANGER; } @@ -424,6 +386,7 @@ struct EaxFlangerTraits { template struct ChorusFlangerEffect { using Traits = TTraits; + using EaxProps = typename Traits::EaxProps; using Committer = typename Traits::Committer; using Exception = typename Committer::Exception; @@ -494,7 +457,7 @@ struct ChorusFlangerEffect { }; // DelayValidator struct AllValidator { - void operator()(const typename Traits::Props& all) const + void operator()(const EaxProps& all) const { WaveformValidator{}(all.ulWaveform); PhaseValidator{}(all.lPhase); @@ -508,7 +471,7 @@ struct ChorusFlangerEffect { public: static void SetDefaults(EaxEffectProps &props) { - auto&& all = props.emplace(); + auto&& all = props.emplace(); all.ulWaveform = Traits::eax_default_waveform(); all.lPhase = Traits::eax_default_phase(); all.flRate = Traits::eax_default_rate(); @@ -518,102 +481,83 @@ struct ChorusFlangerEffect { } - static void Get(const EaxCall &call, const EaxEffectProps &props) + static void Get(const EaxCall &call, const EaxProps &all) { - auto&& all = std::get(props); switch(call.get_property_id()) { case Traits::eax_none_param_id(): break; - case Traits::eax_allparameters_param_id(): call.template set_value(all); break; - case Traits::eax_waveform_param_id(): call.template set_value(all.ulWaveform); break; - case Traits::eax_phase_param_id(): call.template set_value(all.lPhase); break; - case Traits::eax_rate_param_id(): call.template set_value(all.flRate); break; - case Traits::eax_depth_param_id(): call.template set_value(all.flDepth); break; - case Traits::eax_feedback_param_id(): call.template set_value(all.flFeedback); break; - case Traits::eax_delay_param_id(): call.template set_value(all.flDelay); break; - default: Committer::fail_unknown_property_id(); } } - static void Set(const EaxCall &call, EaxEffectProps &props) + static void Set(const EaxCall &call, EaxProps &all) { - auto&& all = std::get(props); switch(call.get_property_id()) { case Traits::eax_none_param_id(): break; - case Traits::eax_allparameters_param_id(): Committer::template defer(call, all); break; - case Traits::eax_waveform_param_id(): Committer::template defer(call, all.ulWaveform); break; - case Traits::eax_phase_param_id(): Committer::template defer(call, all.lPhase); break; - case Traits::eax_rate_param_id(): Committer::template defer(call, all.flRate); break; - case Traits::eax_depth_param_id(): Committer::template defer(call, all.flDepth); break; - case Traits::eax_feedback_param_id(): Committer::template defer(call, all.flFeedback); break; - case Traits::eax_delay_param_id(): Committer::template defer(call, all.flDelay); break; - default: Committer::fail_unknown_property_id(); } } - static bool Commit(const EaxEffectProps &props, EaxEffectProps &props_, EffectProps &al_props_) + static bool Commit(const EaxProps &props, EaxEffectProps &props_, ChorusProps &al_props_) { - if(props == props_) + if(auto *cur = std::get_if(&props_); cur && *cur == props) return false; props_ = props; - auto&& dst = std::get(props); - al_props_.Chorus.Waveform = Traits::eax_waveform(dst.ulWaveform); - al_props_.Chorus.Phase = static_cast(dst.lPhase); - al_props_.Chorus.Rate = dst.flRate; - al_props_.Chorus.Depth = dst.flDepth; - al_props_.Chorus.Feedback = dst.flFeedback; - al_props_.Chorus.Delay = dst.flDelay; + al_props_.Waveform = Traits::eax_waveform(props.ulWaveform); + al_props_.Phase = static_cast(props.lPhase); + al_props_.Rate = props.flRate; + al_props_.Depth = props.flDepth; + al_props_.Feedback = props.flFeedback; + al_props_.Delay = props.flDelay; return true; } @@ -638,29 +582,25 @@ template<> throw Exception{message}; } -template<> -bool ChorusCommitter::commit(const EaxEffectProps &props) +bool EaxChorusCommitter::commit(const EAXCHORUSPROPERTIES &props) { using Committer = ChorusFlangerEffect; - return Committer::Commit(props, mEaxProps, mAlProps); + return Committer::Commit(props, mEaxProps, mAlProps.emplace()); } -template<> -void ChorusCommitter::SetDefaults(EaxEffectProps &props) +void EaxChorusCommitter::SetDefaults(EaxEffectProps &props) { using Committer = ChorusFlangerEffect; Committer::SetDefaults(props); } -template<> -void ChorusCommitter::Get(const EaxCall &call, const EaxEffectProps &props) +void EaxChorusCommitter::Get(const EaxCall &call, const EAXCHORUSPROPERTIES &props) { using Committer = ChorusFlangerEffect; Committer::Get(call, props); } -template<> -void ChorusCommitter::Set(const EaxCall &call, EaxEffectProps &props) +void EaxChorusCommitter::Set(const EaxCall &call, EAXCHORUSPROPERTIES &props) { using Committer = ChorusFlangerEffect; Committer::Set(call, props); @@ -679,29 +619,25 @@ template<> throw Exception{message}; } -template<> -bool FlangerCommitter::commit(const EaxEffectProps &props) +bool EaxFlangerCommitter::commit(const EAXFLANGERPROPERTIES &props) { using Committer = ChorusFlangerEffect; - return Committer::Commit(props, mEaxProps, mAlProps); + return Committer::Commit(props, mEaxProps, mAlProps.emplace()); } -template<> -void FlangerCommitter::SetDefaults(EaxEffectProps &props) +void EaxFlangerCommitter::SetDefaults(EaxEffectProps &props) { using Committer = ChorusFlangerEffect; Committer::SetDefaults(props); } -template<> -void FlangerCommitter::Get(const EaxCall &call, const EaxEffectProps &props) +void EaxFlangerCommitter::Get(const EaxCall &call, const EAXFLANGERPROPERTIES &props) { using Committer = ChorusFlangerEffect; Committer::Get(call, props); } -template<> -void FlangerCommitter::Set(const EaxCall &call, EaxEffectProps &props) +void EaxFlangerCommitter::Set(const EaxCall &call, EAXFLANGERPROPERTIES &props) { using Committer = ChorusFlangerEffect; Committer::Set(call, props); diff --git a/3rdparty/openal/al/effects/compressor.cpp b/3rdparty/openal/al/effects/compressor.cpp index ca8af84face0..3579d82ff18d 100644 --- a/3rdparty/openal/al/effects/compressor.cpp +++ b/3rdparty/openal/al/effects/compressor.cpp @@ -7,8 +7,8 @@ #include "alc/effects/base.h" #include "effects.h" -#ifdef ALSOFT_EAX -#include "alnumeric.h" +#if ALSOFT_EAX +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -16,14 +16,25 @@ namespace { -void Compressor_setParami(EffectProps *props, ALenum param, int val) +constexpr EffectProps genDefaultProps() noexcept +{ + CompressorProps props{}; + props.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF; + return props; +} + +} // namespace + +const EffectProps CompressorEffectProps{genDefaultProps()}; + +void CompressorEffectHandler::SetParami(CompressorProps &props, ALenum param, int val) { switch(param) { case AL_COMPRESSOR_ONOFF: if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF)) throw effect_exception{AL_INVALID_VALUE, "Compressor state out of range"}; - props->Compressor.OnOff = (val != AL_FALSE); + props.OnOff = (val != AL_FALSE); break; default: @@ -31,53 +42,38 @@ void Compressor_setParami(EffectProps *props, ALenum param, int val) param}; } } -void Compressor_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Compressor_setParami(props, param, vals[0]); } -void Compressor_setParamf(EffectProps*, ALenum param, float) +void CompressorEffectHandler::SetParamiv(CompressorProps &props, ALenum param, const int *vals) +{ SetParami(props, param, *vals); } +void CompressorEffectHandler::SetParamf(CompressorProps&, ALenum param, float) { throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; } -void Compressor_setParamfv(EffectProps*, ALenum param, const float*) +void CompressorEffectHandler::SetParamfv(CompressorProps&, ALenum param, const float*) { throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param}; } -void Compressor_getParami(const EffectProps *props, ALenum param, int *val) +void CompressorEffectHandler::GetParami(const CompressorProps &props, ALenum param, int *val) { switch(param) { - case AL_COMPRESSOR_ONOFF: - *val = props->Compressor.OnOff; - break; - + case AL_COMPRESSOR_ONOFF: *val = props.OnOff; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x", param}; } } -void Compressor_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Compressor_getParami(props, param, vals); } -void Compressor_getParamf(const EffectProps*, ALenum param, float*) +void CompressorEffectHandler::GetParamiv(const CompressorProps &props, ALenum param, int *vals) +{ GetParami(props, param, vals); } +void CompressorEffectHandler::GetParamf(const CompressorProps&, ALenum param, float*) { throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; } -void Compressor_getParamfv(const EffectProps*, ALenum param, float*) +void CompressorEffectHandler::GetParamfv(const CompressorProps&, ALenum param, float*) { throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param}; } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Compressor.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF; - return props; -} - -} // namespace - -DEFINE_ALEFFECT_VTABLE(Compressor); -const EffectProps CompressorEffectProps{genDefaultProps()}; - -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using CompressorCommitter = EaxCommitter; @@ -115,28 +111,24 @@ template<> throw Exception{message}; } -template<> -bool CompressorCommitter::commit(const EaxEffectProps &props) +bool EaxCompressorCommitter::commit(const EAXAGCCOMPRESSORPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; + mAlProps = CompressorProps{props.ulOnOff != 0}; - mAlProps.Compressor.OnOff = (std::get(props).ulOnOff != 0); return true; } -template<> -void CompressorCommitter::SetDefaults(EaxEffectProps &props) +void EaxCompressorCommitter::SetDefaults(EaxEffectProps &props) { props = EAXAGCCOMPRESSORPROPERTIES{EAXAGCCOMPRESSOR_DEFAULTONOFF}; } -template<> -void CompressorCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxCompressorCommitter::Get(const EaxCall &call, const EAXAGCCOMPRESSORPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXAGCCOMPRESSOR_NONE: break; @@ -146,10 +138,8 @@ void CompressorCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) } } -template<> -void CompressorCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxCompressorCommitter::Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXAGCCOMPRESSOR_NONE: break; diff --git a/3rdparty/openal/al/effects/convolution.cpp b/3rdparty/openal/al/effects/convolution.cpp index 8e850fd3e20b..618d5aac580d 100644 --- a/3rdparty/openal/al/effects/convolution.cpp +++ b/3rdparty/openal/al/effects/convolution.cpp @@ -1,93 +1,117 @@ #include "config.h" +#include +#include +#include + #include "AL/al.h" -#include "alc/inprogext.h" -#include "alc/effects/base.h" +#include "alc/inprogext.h" +#include "alnumeric.h" +#include "alspan.h" +#include "core/effects/base.h" #include "effects.h" namespace { -void Convolution_setParami(EffectProps* /*props*/, ALenum param, int /*val*/) +constexpr EffectProps genDefaultProps() noexcept +{ + ConvolutionProps props{}; + props.OrientAt = {0.0f, 0.0f, -1.0f}; + props.OrientUp = {0.0f, 1.0f, 0.0f}; + return props; +} + +} // namespace + +const EffectProps ConvolutionEffectProps{genDefaultProps()}; + +void ConvolutionEffectHandler::SetParami(ConvolutionProps& /*props*/, ALenum param, int /*val*/) { switch(param) { default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", + throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect integer property 0x%04x", param}; } } -void Convolution_setParamiv(EffectProps *props, ALenum param, const int *vals) +void ConvolutionEffectHandler::SetParamiv(ConvolutionProps &props, ALenum param, const int *vals) { switch(param) { default: - Convolution_setParami(props, param, vals[0]); + SetParami(props, param, *vals); } } -void Convolution_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/) +void ConvolutionEffectHandler::SetParamf(ConvolutionProps& /*props*/, ALenum param, float /*val*/) { switch(param) { default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", + throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect float property 0x%04x", param}; } } -void Convolution_setParamfv(EffectProps *props, ALenum param, const float *vals) +void ConvolutionEffectHandler::SetParamfv(ConvolutionProps &props, ALenum param, const float *values) { + static constexpr auto finite_checker = [](float val) -> bool { return std::isfinite(val); }; + al::span vals; switch(param) { + case AL_CONVOLUTION_ORIENTATION_SOFT: + vals = {values, 6_uz}; + if(!std::all_of(vals.cbegin(), vals.cend(), finite_checker)) + throw effect_exception{AL_INVALID_VALUE, "Property 0x%04x value out of range", param}; + + std::copy_n(vals.cbegin(), props.OrientAt.size(), props.OrientAt.begin()); + std::copy_n(vals.cbegin()+3, props.OrientUp.size(), props.OrientUp.begin()); + break; + default: - Convolution_setParamf(props, param, vals[0]); + SetParamf(props, param, *values); } } -void Convolution_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/) +void ConvolutionEffectHandler::GetParami(const ConvolutionProps& /*props*/, ALenum param, int* /*val*/) { switch(param) { default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", + throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect integer property 0x%04x", param}; } } -void Convolution_getParamiv(const EffectProps *props, ALenum param, int *vals) +void ConvolutionEffectHandler::GetParamiv(const ConvolutionProps &props, ALenum param, int *vals) { switch(param) { default: - Convolution_getParami(props, param, vals); + GetParami(props, param, vals); } } -void Convolution_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/) +void ConvolutionEffectHandler::GetParamf(const ConvolutionProps& /*props*/, ALenum param, float* /*val*/) { switch(param) { default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", + throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect float property 0x%04x", param}; } } -void Convolution_getParamfv(const EffectProps *props, ALenum param, float *vals) +void ConvolutionEffectHandler::GetParamfv(const ConvolutionProps &props, ALenum param, float *values) { + al::span vals; switch(param) { + case AL_CONVOLUTION_ORIENTATION_SOFT: + vals = {values, 6_uz}; + std::copy(props.OrientAt.cbegin(), props.OrientAt.cend(), vals.begin()); + std::copy(props.OrientUp.cbegin(), props.OrientUp.cend(), vals.begin()+3); + break; + default: - Convolution_getParamf(props, param, vals); + GetParamf(props, param, values); } } - -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - return props; -} - -} // namespace - -DEFINE_ALEFFECT_VTABLE(Convolution); - -const EffectProps ConvolutionEffectProps{genDefaultProps()}; diff --git a/3rdparty/openal/al/effects/dedicated.cpp b/3rdparty/openal/al/effects/dedicated.cpp index db57003c2af9..f032a72ec3e3 100644 --- a/3rdparty/openal/al/effects/dedicated.cpp +++ b/3rdparty/openal/al/effects/dedicated.cpp @@ -12,61 +12,111 @@ namespace { -void Dedicated_setParami(EffectProps*, ALenum param, int) +constexpr EffectProps genDefaultDialogProps() noexcept +{ + DedicatedProps props{}; + props.Target = DedicatedProps::Dialog; + props.Gain = 1.0f; + return props; +} + +constexpr EffectProps genDefaultLfeProps() noexcept +{ + DedicatedProps props{}; + props.Target = DedicatedProps::Lfe; + props.Gain = 1.0f; + return props; +} + +} // namespace + +const EffectProps DedicatedDialogEffectProps{genDefaultDialogProps()}; + +void DedicatedDialogEffectHandler::SetParami(DedicatedProps&, ALenum param, int) { throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; } -void Dedicated_setParamiv(EffectProps*, ALenum param, const int*) +void DedicatedDialogEffectHandler::SetParamiv(DedicatedProps&, ALenum param, const int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param}; } -void Dedicated_setParamf(EffectProps *props, ALenum param, float val) +void DedicatedDialogEffectHandler::SetParamf(DedicatedProps &props, ALenum param, float val) { switch(param) { case AL_DEDICATED_GAIN: if(!(val >= 0.0f && std::isfinite(val))) throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"}; - props->Dedicated.Gain = val; + props.Gain = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param}; } } -void Dedicated_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Dedicated_setParamf(props, param, vals[0]); } +void DedicatedDialogEffectHandler::SetParamfv(DedicatedProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, *vals); } -void Dedicated_getParami(const EffectProps*, ALenum param, int*) +void DedicatedDialogEffectHandler::GetParami(const DedicatedProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; } -void Dedicated_getParamiv(const EffectProps*, ALenum param, int*) +void DedicatedDialogEffectHandler::GetParamiv(const DedicatedProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param}; } -void Dedicated_getParamf(const EffectProps *props, ALenum param, float *val) +void DedicatedDialogEffectHandler::GetParamf(const DedicatedProps &props, ALenum param, float *val) +{ + switch(param) + { + case AL_DEDICATED_GAIN: *val = props.Gain; break; + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param}; + } +} +void DedicatedDialogEffectHandler::GetParamfv(const DedicatedProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } + + +const EffectProps DedicatedLfeEffectProps{genDefaultLfeProps()}; + +void DedicatedLfeEffectHandler::SetParami(DedicatedProps&, ALenum param, int) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; } +void DedicatedLfeEffectHandler::SetParamiv(DedicatedProps&, ALenum param, const int*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", + param}; +} +void DedicatedLfeEffectHandler::SetParamf(DedicatedProps &props, ALenum param, float val) { switch(param) { case AL_DEDICATED_GAIN: - *val = props->Dedicated.Gain; + if(!(val >= 0.0f && std::isfinite(val))) + throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"}; + props.Gain = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param}; } } -void Dedicated_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Dedicated_getParamf(props, param, vals); } +void DedicatedLfeEffectHandler::SetParamfv(DedicatedProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, *vals); } -EffectProps genDefaultProps() noexcept +void DedicatedLfeEffectHandler::GetParami(const DedicatedProps&, ALenum param, int*) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; } +void DedicatedLfeEffectHandler::GetParamiv(const DedicatedProps&, ALenum param, int*) { - EffectProps props{}; - props.Dedicated.Gain = 1.0f; - return props; + throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", + param}; } - -} // namespace - -DEFINE_ALEFFECT_VTABLE(Dedicated); - -const EffectProps DedicatedEffectProps{genDefaultProps()}; +void DedicatedLfeEffectHandler::GetParamf(const DedicatedProps &props, ALenum param, float *val) +{ + switch(param) + { + case AL_DEDICATED_GAIN: *val = props.Gain; break; + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param}; + } +} +void DedicatedLfeEffectHandler::GetParamfv(const DedicatedProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } diff --git a/3rdparty/openal/al/effects/distortion.cpp b/3rdparty/openal/al/effects/distortion.cpp index e046d8e72490..bc285644bdf8 100644 --- a/3rdparty/openal/al/effects/distortion.cpp +++ b/3rdparty/openal/al/effects/distortion.cpp @@ -7,8 +7,9 @@ #include "alc/effects/base.h" #include "effects.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include "alnumeric.h" +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -16,110 +17,95 @@ namespace { -void Distortion_setParami(EffectProps*, ALenum param, int) +constexpr EffectProps genDefaultProps() noexcept +{ + DistortionProps props{}; + props.Edge = AL_DISTORTION_DEFAULT_EDGE; + props.Gain = AL_DISTORTION_DEFAULT_GAIN; + props.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF; + props.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER; + props.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH; + return props; +} + +} // namespace + +const EffectProps DistortionEffectProps{genDefaultProps()}; + +void DistortionEffectHandler::SetParami(DistortionProps&, ALenum param, int) { throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; } -void Distortion_setParamiv(EffectProps*, ALenum param, const int*) +void DistortionEffectHandler::SetParamiv(DistortionProps&, ALenum param, const int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param}; } -void Distortion_setParamf(EffectProps *props, ALenum param, float val) +void DistortionEffectHandler::SetParamf(DistortionProps &props, ALenum param, float val) { switch(param) { case AL_DISTORTION_EDGE: if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE)) throw effect_exception{AL_INVALID_VALUE, "Distortion edge out of range"}; - props->Distortion.Edge = val; + props.Edge = val; break; case AL_DISTORTION_GAIN: if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN)) throw effect_exception{AL_INVALID_VALUE, "Distortion gain out of range"}; - props->Distortion.Gain = val; + props.Gain = val; break; case AL_DISTORTION_LOWPASS_CUTOFF: if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF)) throw effect_exception{AL_INVALID_VALUE, "Distortion low-pass cutoff out of range"}; - props->Distortion.LowpassCutoff = val; + props.LowpassCutoff = val; break; case AL_DISTORTION_EQCENTER: if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER)) throw effect_exception{AL_INVALID_VALUE, "Distortion EQ center out of range"}; - props->Distortion.EQCenter = val; + props.EQCenter = val; break; case AL_DISTORTION_EQBANDWIDTH: if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH)) throw effect_exception{AL_INVALID_VALUE, "Distortion EQ bandwidth out of range"}; - props->Distortion.EQBandwidth = val; + props.EQBandwidth = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param}; } } -void Distortion_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Distortion_setParamf(props, param, vals[0]); } +void DistortionEffectHandler::SetParamfv(DistortionProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, *vals); } -void Distortion_getParami(const EffectProps*, ALenum param, int*) +void DistortionEffectHandler::GetParami(const DistortionProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; } -void Distortion_getParamiv(const EffectProps*, ALenum param, int*) +void DistortionEffectHandler::GetParamiv(const DistortionProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param}; } -void Distortion_getParamf(const EffectProps *props, ALenum param, float *val) +void DistortionEffectHandler::GetParamf(const DistortionProps &props, ALenum param, float *val) { switch(param) { - case AL_DISTORTION_EDGE: - *val = props->Distortion.Edge; - break; - - case AL_DISTORTION_GAIN: - *val = props->Distortion.Gain; - break; - - case AL_DISTORTION_LOWPASS_CUTOFF: - *val = props->Distortion.LowpassCutoff; - break; - - case AL_DISTORTION_EQCENTER: - *val = props->Distortion.EQCenter; - break; - - case AL_DISTORTION_EQBANDWIDTH: - *val = props->Distortion.EQBandwidth; - break; + case AL_DISTORTION_EDGE: *val = props.Edge; break; + case AL_DISTORTION_GAIN: *val = props.Gain; break; + case AL_DISTORTION_LOWPASS_CUTOFF: *val = props.LowpassCutoff; break; + case AL_DISTORTION_EQCENTER: *val = props.EQCenter; break; + case AL_DISTORTION_EQBANDWIDTH: *val = props.EQBandwidth; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param}; } } -void Distortion_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Distortion_getParamf(props, param, vals); } - -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE; - props.Distortion.Gain = AL_DISTORTION_DEFAULT_GAIN; - props.Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF; - props.Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER; - props.Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH; - return props; -} - -} // namespace - -DEFINE_ALEFFECT_VTABLE(Distortion); +void DistortionEffectHandler::GetParamfv(const DistortionProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } -const EffectProps DistortionEffectProps{genDefaultProps()}; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using DistortionCommitter = EaxCommitter; @@ -204,26 +190,26 @@ template<> throw Exception{message}; } -template<> -bool DistortionCommitter::commit(const EaxEffectProps &props) +bool EaxDistortionCommitter::commit(const EAXDISTORTIONPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; - - auto &eaxprops = std::get(props); - mAlProps.Distortion.Edge = eaxprops.flEdge; - mAlProps.Distortion.Gain = level_mb_to_gain(static_cast(eaxprops.lGain)); - mAlProps.Distortion.LowpassCutoff = eaxprops.flLowPassCutOff; - mAlProps.Distortion.EQCenter = eaxprops.flEQCenter; - mAlProps.Distortion.EQBandwidth = eaxprops.flEdge; + mAlProps = [&]{ + DistortionProps ret{}; + ret.Edge = props.flEdge; + ret.Gain = level_mb_to_gain(static_cast(props.lGain)); + ret.LowpassCutoff = props.flLowPassCutOff; + ret.EQCenter = props.flEQCenter; + ret.EQBandwidth = props.flEdge; + return ret; + }(); return true; } -template<> -void DistortionCommitter::SetDefaults(EaxEffectProps &props) +void EaxDistortionCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXDISTORTIONPROPERTIES defprops{[] { @@ -238,10 +224,8 @@ void DistortionCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void DistortionCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxDistortionCommitter::Get(const EaxCall &call, const EAXDISTORTIONPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXDISTORTION_NONE: break; @@ -255,10 +239,8 @@ void DistortionCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) } } -template<> -void DistortionCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxDistortionCommitter::Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXDISTORTION_NONE: break; diff --git a/3rdparty/openal/al/effects/echo.cpp b/3rdparty/openal/al/effects/echo.cpp index 48aacef34b0f..55f12537f638 100644 --- a/3rdparty/openal/al/effects/echo.cpp +++ b/3rdparty/openal/al/effects/echo.cpp @@ -7,8 +7,9 @@ #include "alc/effects/base.h" #include "effects.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include "alnumeric.h" +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -19,104 +20,89 @@ namespace { static_assert(EchoMaxDelay >= AL_ECHO_MAX_DELAY, "Echo max delay too short"); static_assert(EchoMaxLRDelay >= AL_ECHO_MAX_LRDELAY, "Echo max left-right delay too short"); -void Echo_setParami(EffectProps*, ALenum param, int) +constexpr EffectProps genDefaultProps() noexcept +{ + EchoProps props{}; + props.Delay = AL_ECHO_DEFAULT_DELAY; + props.LRDelay = AL_ECHO_DEFAULT_LRDELAY; + props.Damping = AL_ECHO_DEFAULT_DAMPING; + props.Feedback = AL_ECHO_DEFAULT_FEEDBACK; + props.Spread = AL_ECHO_DEFAULT_SPREAD; + return props; +} + +} // namespace + +const EffectProps EchoEffectProps{genDefaultProps()}; + +void EchoEffectHandler::SetParami(EchoProps&, ALenum param, int) { throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; } -void Echo_setParamiv(EffectProps*, ALenum param, const int*) +void EchoEffectHandler::SetParamiv(EchoProps&, ALenum param, const int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; } -void Echo_setParamf(EffectProps *props, ALenum param, float val) +void EchoEffectHandler::SetParamf(EchoProps &props, ALenum param, float val) { switch(param) { case AL_ECHO_DELAY: if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY)) throw effect_exception{AL_INVALID_VALUE, "Echo delay out of range"}; - props->Echo.Delay = val; + props.Delay = val; break; case AL_ECHO_LRDELAY: if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY)) throw effect_exception{AL_INVALID_VALUE, "Echo LR delay out of range"}; - props->Echo.LRDelay = val; + props.LRDelay = val; break; case AL_ECHO_DAMPING: if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING)) throw effect_exception{AL_INVALID_VALUE, "Echo damping out of range"}; - props->Echo.Damping = val; + props.Damping = val; break; case AL_ECHO_FEEDBACK: if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK)) throw effect_exception{AL_INVALID_VALUE, "Echo feedback out of range"}; - props->Echo.Feedback = val; + props.Feedback = val; break; case AL_ECHO_SPREAD: if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD)) throw effect_exception{AL_INVALID_VALUE, "Echo spread out of range"}; - props->Echo.Spread = val; + props.Spread = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param}; } } -void Echo_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Echo_setParamf(props, param, vals[0]); } +void EchoEffectHandler::SetParamfv(EchoProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, *vals); } -void Echo_getParami(const EffectProps*, ALenum param, int*) +void EchoEffectHandler::GetParami(const EchoProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; } -void Echo_getParamiv(const EffectProps*, ALenum param, int*) +void EchoEffectHandler::GetParamiv(const EchoProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; } -void Echo_getParamf(const EffectProps *props, ALenum param, float *val) +void EchoEffectHandler::GetParamf(const EchoProps &props, ALenum param, float *val) { switch(param) { - case AL_ECHO_DELAY: - *val = props->Echo.Delay; - break; - - case AL_ECHO_LRDELAY: - *val = props->Echo.LRDelay; - break; - - case AL_ECHO_DAMPING: - *val = props->Echo.Damping; - break; - - case AL_ECHO_FEEDBACK: - *val = props->Echo.Feedback; - break; - - case AL_ECHO_SPREAD: - *val = props->Echo.Spread; - break; + case AL_ECHO_DELAY: *val = props.Delay; break; + case AL_ECHO_LRDELAY: *val = props.LRDelay; break; + case AL_ECHO_DAMPING: *val = props.Damping; break; + case AL_ECHO_FEEDBACK: *val = props.Feedback; break; + case AL_ECHO_SPREAD: *val = props.Spread; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param}; } } -void Echo_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Echo_getParamf(props, param, vals); } - -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Echo.Delay = AL_ECHO_DEFAULT_DELAY; - props.Echo.LRDelay = AL_ECHO_DEFAULT_LRDELAY; - props.Echo.Damping = AL_ECHO_DEFAULT_DAMPING; - props.Echo.Feedback = AL_ECHO_DEFAULT_FEEDBACK; - props.Echo.Spread = AL_ECHO_DEFAULT_SPREAD; - return props; -} - -} // namespace - -DEFINE_ALEFFECT_VTABLE(Echo); +void EchoEffectHandler::GetParamfv(const EchoProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } -const EffectProps EchoEffectProps{genDefaultProps()}; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using EchoCommitter = EaxCommitter; @@ -201,26 +187,26 @@ template<> throw Exception{message}; } -template<> -bool EchoCommitter::commit(const EaxEffectProps &props) +bool EaxEchoCommitter::commit(const EAXECHOPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; - - auto &eaxprops = std::get(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; + mAlProps = [&]{ + EchoProps ret{}; + ret.Delay = props.flDelay; + ret.LRDelay = props.flLRDelay; + ret.Damping = props.flDamping; + ret.Feedback = props.flFeedback; + ret.Spread = props.flSpread; + return ret; + }(); return true; } -template<> -void EchoCommitter::SetDefaults(EaxEffectProps &props) +void EaxEchoCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXECHOPROPERTIES defprops{[] { @@ -235,10 +221,8 @@ void EchoCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void EchoCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxEchoCommitter::Get(const EaxCall &call, const EAXECHOPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXECHO_NONE: break; @@ -252,10 +236,8 @@ void EchoCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) } } -template<> -void EchoCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxEchoCommitter::Set(const EaxCall &call, EAXECHOPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXECHO_NONE: break; diff --git a/3rdparty/openal/al/effects/effects.cpp b/3rdparty/openal/al/effects/effects.cpp index 4a67b5ffeb45..ace4e70665e1 100644 --- a/3rdparty/openal/al/effects/effects.cpp +++ b/3rdparty/openal/al/effects/effects.cpp @@ -1,9 +1,3 @@ #include "config.h" -#ifdef ALSOFT_EAX - -#include -#include "AL/efx.h" #include "effects.h" - -#endif // ALSOFT_EAX diff --git a/3rdparty/openal/al/effects/effects.h b/3rdparty/openal/al/effects/effects.h index 9d57dd820e8a..3c4b8f93f472 100644 --- a/3rdparty/openal/al/effects/effects.h +++ b/3rdparty/openal/al/effects/effects.h @@ -1,52 +1,47 @@ #ifndef AL_EFFECTS_EFFECTS_H #define AL_EFFECTS_EFFECTS_H -#include "AL/al.h" - -#include "core/except.h" - -#ifdef ALSOFT_EAX -#include "al/eax/effect.h" -#endif // ALSOFT_EAX - -union EffectProps; - - -class effect_exception final : public al::base_exception { - ALenum mErrorCode; - -public: -#ifdef __USE_MINGW_ANSI_STDIO - [[gnu::format(gnu_printf, 3, 4)]] -#else - [[gnu::format(printf, 3, 4)]] -#endif - effect_exception(ALenum code, const char *msg, ...); - ~effect_exception() override; +#include - ALenum errorCode() const noexcept { return mErrorCode; } -}; - - -struct EffectVtable { - void (*const setParami)(EffectProps *props, ALenum param, int val); - void (*const setParamiv)(EffectProps *props, ALenum param, const int *vals); - void (*const setParamf)(EffectProps *props, ALenum param, float val); - void (*const setParamfv)(EffectProps *props, ALenum param, const float *vals); +#include "AL/al.h" - void (*const getParami)(const EffectProps *props, ALenum param, int *val); - void (*const getParamiv)(const EffectProps *props, ALenum param, int *vals); - void (*const getParamf)(const EffectProps *props, ALenum param, float *val); - void (*const getParamfv)(const EffectProps *props, ALenum param, float *vals); +#include "al/error.h" +#include "core/effects/base.h" + +#define DECL_HANDLER(N, T) \ +struct N { \ + using prop_type = T; \ + \ + static void SetParami(prop_type &props, ALenum param, int val); \ + static void SetParamiv(prop_type &props, ALenum param, const int *vals); \ + static void SetParamf(prop_type &props, ALenum param, float val); \ + static void SetParamfv(prop_type &props, ALenum param, const float *vals);\ + static void GetParami(const prop_type &props, ALenum param, int *val); \ + static void GetParamiv(const prop_type &props, ALenum param, int *vals); \ + static void GetParamf(const prop_type &props, ALenum param, float *val); \ + static void GetParamfv(const prop_type &props, ALenum param, float *vals);\ }; - -#define DEFINE_ALEFFECT_VTABLE(T) \ -const EffectVtable T##EffectVtable = { \ - T##_setParami, T##_setParamiv, \ - T##_setParamf, T##_setParamfv, \ - T##_getParami, T##_getParamiv, \ - T##_getParamf, T##_getParamfv, \ -} +DECL_HANDLER(NullEffectHandler, std::monostate) +DECL_HANDLER(ReverbEffectHandler, ReverbProps) +DECL_HANDLER(StdReverbEffectHandler, ReverbProps) +DECL_HANDLER(AutowahEffectHandler, AutowahProps) +DECL_HANDLER(ChorusEffectHandler, ChorusProps) +DECL_HANDLER(CompressorEffectHandler, CompressorProps) +DECL_HANDLER(DistortionEffectHandler, DistortionProps) +DECL_HANDLER(EchoEffectHandler, EchoProps) +DECL_HANDLER(EqualizerEffectHandler, EqualizerProps) +DECL_HANDLER(FlangerEffectHandler, ChorusProps) +DECL_HANDLER(FshifterEffectHandler, FshifterProps) +DECL_HANDLER(ModulatorEffectHandler, ModulatorProps) +DECL_HANDLER(PshifterEffectHandler, PshifterProps) +DECL_HANDLER(VmorpherEffectHandler, VmorpherProps) +DECL_HANDLER(DedicatedDialogEffectHandler, DedicatedProps) +DECL_HANDLER(DedicatedLfeEffectHandler, DedicatedProps) +DECL_HANDLER(ConvolutionEffectHandler, ConvolutionProps) +#undef DECL_HANDLER + + +using effect_exception = al::context_error; /* Default properties for the given effect types. */ @@ -64,25 +59,8 @@ extern const EffectProps FshifterEffectProps; extern const EffectProps ModulatorEffectProps; extern const EffectProps PshifterEffectProps; extern const EffectProps VmorpherEffectProps; -extern const EffectProps DedicatedEffectProps; +extern const EffectProps DedicatedDialogEffectProps; +extern const EffectProps DedicatedLfeEffectProps; extern const EffectProps ConvolutionEffectProps; -/* Vtables to get/set properties for the given effect types. */ -extern const EffectVtable NullEffectVtable; -extern const EffectVtable ReverbEffectVtable; -extern const EffectVtable StdReverbEffectVtable; -extern const EffectVtable AutowahEffectVtable; -extern const EffectVtable ChorusEffectVtable; -extern const EffectVtable CompressorEffectVtable; -extern const EffectVtable DistortionEffectVtable; -extern const EffectVtable EchoEffectVtable; -extern const EffectVtable EqualizerEffectVtable; -extern const EffectVtable FlangerEffectVtable; -extern const EffectVtable FshifterEffectVtable; -extern const EffectVtable ModulatorEffectVtable; -extern const EffectVtable PshifterEffectVtable; -extern const EffectVtable VmorpherEffectVtable; -extern const EffectVtable DedicatedEffectVtable; -extern const EffectVtable ConvolutionEffectVtable; - #endif /* AL_EFFECTS_EFFECTS_H */ diff --git a/3rdparty/openal/al/effects/equalizer.cpp b/3rdparty/openal/al/effects/equalizer.cpp index 76d5bdef07cc..15f5983069f5 100644 --- a/3rdparty/openal/al/effects/equalizer.cpp +++ b/3rdparty/openal/al/effects/equalizer.cpp @@ -7,8 +7,9 @@ #include "alc/effects/base.h" #include "effects.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include "alnumeric.h" +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -16,165 +17,135 @@ namespace { -void Equalizer_setParami(EffectProps*, ALenum param, int) +constexpr EffectProps genDefaultProps() noexcept +{ + EqualizerProps props{}; + props.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF; + props.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN; + props.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER; + props.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN; + props.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH; + props.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER; + props.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN; + props.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH; + props.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF; + props.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN; + return props; +} + +} // namespace + +const EffectProps EqualizerEffectProps{genDefaultProps()}; + +void EqualizerEffectHandler::SetParami(EqualizerProps&, ALenum param, int) { throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; } -void Equalizer_setParamiv(EffectProps*, ALenum param, const int*) +void EqualizerEffectHandler::SetParamiv(EqualizerProps&, ALenum param, const int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param}; } -void Equalizer_setParamf(EffectProps *props, ALenum param, float val) +void EqualizerEffectHandler::SetParamf(EqualizerProps &props, ALenum param, float val) { switch(param) { case AL_EQUALIZER_LOW_GAIN: if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN)) throw effect_exception{AL_INVALID_VALUE, "Equalizer low-band gain out of range"}; - props->Equalizer.LowGain = val; + props.LowGain = val; break; case AL_EQUALIZER_LOW_CUTOFF: if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF)) throw effect_exception{AL_INVALID_VALUE, "Equalizer low-band cutoff out of range"}; - props->Equalizer.LowCutoff = val; + props.LowCutoff = val; break; case AL_EQUALIZER_MID1_GAIN: if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN)) throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band gain out of range"}; - props->Equalizer.Mid1Gain = val; + props.Mid1Gain = val; break; case AL_EQUALIZER_MID1_CENTER: if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER)) throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band center out of range"}; - props->Equalizer.Mid1Center = val; + props.Mid1Center = val; break; case AL_EQUALIZER_MID1_WIDTH: if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH)) throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band width out of range"}; - props->Equalizer.Mid1Width = val; + props.Mid1Width = val; break; case AL_EQUALIZER_MID2_GAIN: if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN)) throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band gain out of range"}; - props->Equalizer.Mid2Gain = val; + props.Mid2Gain = val; break; case AL_EQUALIZER_MID2_CENTER: if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER)) throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band center out of range"}; - props->Equalizer.Mid2Center = val; + props.Mid2Center = val; break; case AL_EQUALIZER_MID2_WIDTH: if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH)) throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band width out of range"}; - props->Equalizer.Mid2Width = val; + props.Mid2Width = val; break; case AL_EQUALIZER_HIGH_GAIN: if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN)) throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band gain out of range"}; - props->Equalizer.HighGain = val; + props.HighGain = val; break; case AL_EQUALIZER_HIGH_CUTOFF: if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF)) throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band cutoff out of range"}; - props->Equalizer.HighCutoff = val; + props.HighCutoff = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param}; } } -void Equalizer_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Equalizer_setParamf(props, param, vals[0]); } +void EqualizerEffectHandler::SetParamfv(EqualizerProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, *vals); } -void Equalizer_getParami(const EffectProps*, ALenum param, int*) +void EqualizerEffectHandler::GetParami(const EqualizerProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; } -void Equalizer_getParamiv(const EffectProps*, ALenum param, int*) +void EqualizerEffectHandler::GetParamiv(const EqualizerProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param}; } -void Equalizer_getParamf(const EffectProps *props, ALenum param, float *val) +void EqualizerEffectHandler::GetParamf(const EqualizerProps &props, ALenum param, float *val) { switch(param) { - case AL_EQUALIZER_LOW_GAIN: - *val = props->Equalizer.LowGain; - break; - - case AL_EQUALIZER_LOW_CUTOFF: - *val = props->Equalizer.LowCutoff; - break; - - case AL_EQUALIZER_MID1_GAIN: - *val = props->Equalizer.Mid1Gain; - break; - - case AL_EQUALIZER_MID1_CENTER: - *val = props->Equalizer.Mid1Center; - break; - - case AL_EQUALIZER_MID1_WIDTH: - *val = props->Equalizer.Mid1Width; - break; - - case AL_EQUALIZER_MID2_GAIN: - *val = props->Equalizer.Mid2Gain; - break; - - case AL_EQUALIZER_MID2_CENTER: - *val = props->Equalizer.Mid2Center; - break; - - case AL_EQUALIZER_MID2_WIDTH: - *val = props->Equalizer.Mid2Width; - break; - - case AL_EQUALIZER_HIGH_GAIN: - *val = props->Equalizer.HighGain; - break; - - case AL_EQUALIZER_HIGH_CUTOFF: - *val = props->Equalizer.HighCutoff; - break; + case AL_EQUALIZER_LOW_GAIN: *val = props.LowGain; break; + case AL_EQUALIZER_LOW_CUTOFF: *val = props.LowCutoff; break; + case AL_EQUALIZER_MID1_GAIN: *val = props.Mid1Gain; break; + case AL_EQUALIZER_MID1_CENTER: *val = props.Mid1Center; break; + case AL_EQUALIZER_MID1_WIDTH: *val = props.Mid1Width; break; + case AL_EQUALIZER_MID2_GAIN: *val = props.Mid2Gain; break; + case AL_EQUALIZER_MID2_CENTER: *val = props.Mid2Center; break; + case AL_EQUALIZER_MID2_WIDTH: *val = props.Mid2Width; break; + case AL_EQUALIZER_HIGH_GAIN: *val = props.HighGain; break; + case AL_EQUALIZER_HIGH_CUTOFF: *val = props.HighCutoff; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param}; } } -void Equalizer_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Equalizer_getParamf(props, param, vals); } +void EqualizerEffectHandler::GetParamfv(const EqualizerProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Equalizer.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF; - props.Equalizer.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN; - props.Equalizer.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER; - props.Equalizer.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN; - props.Equalizer.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH; - props.Equalizer.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER; - props.Equalizer.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN; - props.Equalizer.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH; - props.Equalizer.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF; - props.Equalizer.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN; - return props; -} - -} // namespace - -DEFINE_ALEFFECT_VTABLE(Equalizer); - -const EffectProps EqualizerEffectProps{genDefaultProps()}; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using EqualizerCommitter = EaxCommitter; @@ -319,31 +290,31 @@ template<> throw Exception{message}; } -template<> -bool EqualizerCommitter::commit(const EaxEffectProps &props) +bool EaxEqualizerCommitter::commit(const EAXEQUALIZERPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; - - auto &eaxprops = std::get(props); - mAlProps.Equalizer.LowGain = level_mb_to_gain(static_cast(eaxprops.lLowGain)); - mAlProps.Equalizer.LowCutoff = eaxprops.flLowCutOff; - mAlProps.Equalizer.Mid1Gain = level_mb_to_gain(static_cast(eaxprops.lMid1Gain)); - mAlProps.Equalizer.Mid1Center = eaxprops.flMid1Center; - mAlProps.Equalizer.Mid1Width = eaxprops.flMid1Width; - mAlProps.Equalizer.Mid2Gain = level_mb_to_gain(static_cast(eaxprops.lMid2Gain)); - mAlProps.Equalizer.Mid2Center = eaxprops.flMid2Center; - mAlProps.Equalizer.Mid2Width = eaxprops.flMid2Width; - mAlProps.Equalizer.HighGain = level_mb_to_gain(static_cast(eaxprops.lHighGain)); - mAlProps.Equalizer.HighCutoff = eaxprops.flHighCutOff; + mAlProps = [&]{ + EqualizerProps ret{}; + ret.LowGain = level_mb_to_gain(static_cast(props.lLowGain)); + ret.LowCutoff = props.flLowCutOff; + ret.Mid1Gain = level_mb_to_gain(static_cast(props.lMid1Gain)); + ret.Mid1Center = props.flMid1Center; + ret.Mid1Width = props.flMid1Width; + ret.Mid2Gain = level_mb_to_gain(static_cast(props.lMid2Gain)); + ret.Mid2Center = props.flMid2Center; + ret.Mid2Width = props.flMid2Width; + ret.HighGain = level_mb_to_gain(static_cast(props.lHighGain)); + ret.HighCutoff = props.flHighCutOff; + return ret; + }(); return true; } -template<> -void EqualizerCommitter::SetDefaults(EaxEffectProps &props) +void EaxEqualizerCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXEQUALIZERPROPERTIES defprops{[] { @@ -363,10 +334,8 @@ void EqualizerCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void EqualizerCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxEqualizerCommitter::Get(const EaxCall &call, const EAXEQUALIZERPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXEQUALIZER_NONE: break; @@ -385,10 +354,8 @@ void EqualizerCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) } } -template<> -void EqualizerCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxEqualizerCommitter::Set(const EaxCall &call, EAXEQUALIZERPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXEQUALIZER_NONE: break; diff --git a/3rdparty/openal/al/effects/fshifter.cpp b/3rdparty/openal/al/effects/fshifter.cpp index 37c372c35f78..e42a633fd300 100644 --- a/3rdparty/openal/al/effects/fshifter.cpp +++ b/3rdparty/openal/al/effects/fshifter.cpp @@ -10,9 +10,10 @@ #include "alc/effects/base.h" #include "effects.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include -#include "alnumeric.h" + +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -20,7 +21,7 @@ namespace { -std::optional DirectionFromEmum(ALenum value) +constexpr std::optional DirectionFromEmum(ALenum value) noexcept { switch(value) { @@ -30,7 +31,7 @@ std::optional DirectionFromEmum(ALenum value) } return std::nullopt; } -ALenum EnumFromDirection(FShifterDirection dir) +constexpr ALenum EnumFromDirection(FShifterDirection dir) { switch(dir) { @@ -41,31 +42,26 @@ ALenum EnumFromDirection(FShifterDirection dir) throw std::runtime_error{"Invalid direction: "+std::to_string(static_cast(dir))}; } -void Fshifter_setParamf(EffectProps *props, ALenum param, float val) +constexpr EffectProps genDefaultProps() noexcept { - switch(param) - { - case AL_FREQUENCY_SHIFTER_FREQUENCY: - if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY)) - throw effect_exception{AL_INVALID_VALUE, "Frequency shifter frequency out of range"}; - props->Fshifter.Frequency = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", - param}; - } + FshifterProps props{}; + props.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY; + props.LeftDirection = DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION).value(); + props.RightDirection = DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION).value(); + return props; } -void Fshifter_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Fshifter_setParamf(props, param, vals[0]); } -void Fshifter_setParami(EffectProps *props, ALenum param, int val) +} // namespace + +const EffectProps FshifterEffectProps{genDefaultProps()}; + +void FshifterEffectHandler::SetParami(FshifterProps &props, ALenum param, int val) { switch(param) { case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION: if(auto diropt = DirectionFromEmum(val)) - props->Fshifter.LeftDirection = *diropt; + props.LeftDirection = *diropt; else throw effect_exception{AL_INVALID_VALUE, "Unsupported frequency shifter left direction: 0x%04x", val}; @@ -73,7 +69,7 @@ void Fshifter_setParami(EffectProps *props, ALenum param, int val) case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION: if(auto diropt = DirectionFromEmum(val)) - props->Fshifter.RightDirection = *diropt; + props.RightDirection = *diropt; else throw effect_exception{AL_INVALID_VALUE, "Unsupported frequency shifter right direction: 0x%04x", val}; @@ -84,33 +80,52 @@ void Fshifter_setParami(EffectProps *props, ALenum param, int val) "Invalid frequency shifter integer property 0x%04x", param}; } } -void Fshifter_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Fshifter_setParami(props, param, vals[0]); } +void FshifterEffectHandler::SetParamiv(FshifterProps &props, ALenum param, const int *vals) +{ SetParami(props, param, *vals); } -void Fshifter_getParami(const EffectProps *props, ALenum param, int *val) +void FshifterEffectHandler::SetParamf(FshifterProps &props, ALenum param, float val) +{ + switch(param) + { + case AL_FREQUENCY_SHIFTER_FREQUENCY: + if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY)) + throw effect_exception{AL_INVALID_VALUE, "Frequency shifter frequency out of range"}; + props.Frequency = val; + break; + + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", + param}; + } +} +void FshifterEffectHandler::SetParamfv(FshifterProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, *vals); } + +void FshifterEffectHandler::GetParami(const FshifterProps &props, ALenum param, int *val) { switch(param) { case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION: - *val = EnumFromDirection(props->Fshifter.LeftDirection); + *val = EnumFromDirection(props.LeftDirection); break; case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION: - *val = EnumFromDirection(props->Fshifter.RightDirection); + *val = EnumFromDirection(props.RightDirection); break; + default: throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter integer property 0x%04x", param}; } } -void Fshifter_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Fshifter_getParami(props, param, vals); } +void FshifterEffectHandler::GetParamiv(const FshifterProps &props, ALenum param, int *vals) +{ GetParami(props, param, vals); } -void Fshifter_getParamf(const EffectProps *props, ALenum param, float *val) +void FshifterEffectHandler::GetParamf(const FshifterProps &props, ALenum param, float *val) { switch(param) { case AL_FREQUENCY_SHIFTER_FREQUENCY: - *val = props->Fshifter.Frequency; + *val = props.Frequency; break; default: @@ -118,25 +133,11 @@ void Fshifter_getParamf(const EffectProps *props, ALenum param, float *val) param}; } } -void Fshifter_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Fshifter_getParamf(props, param, vals); } - -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Fshifter.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY; - props.Fshifter.LeftDirection = *DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION); - props.Fshifter.RightDirection = *DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION); - return props; -} - -} // namespace - -DEFINE_ALEFFECT_VTABLE(Fshifter); +void FshifterEffectHandler::GetParamfv(const FshifterProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } -const EffectProps FshifterEffectProps{genDefaultProps()}; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using FrequencyShifterCommitter = EaxCommitter; @@ -197,10 +198,9 @@ template<> throw Exception{message}; } -template<> -bool FrequencyShifterCommitter::commit(const EaxEffectProps &props) +bool EaxFrequencyShifterCommitter::commit(const EAXFREQUENCYSHIFTERPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; @@ -214,16 +214,18 @@ bool FrequencyShifterCommitter::commit(const EaxEffectProps &props) return FShifterDirection::Off; }; - auto &eaxprops = std::get(props); - mAlProps.Fshifter.Frequency = eaxprops.flFrequency; - mAlProps.Fshifter.LeftDirection = get_direction(eaxprops.ulLeftDirection); - mAlProps.Fshifter.RightDirection = get_direction(eaxprops.ulRightDirection); + mAlProps = [&]{ + FshifterProps ret{}; + ret.Frequency = props.flFrequency; + ret.LeftDirection = get_direction(props.ulLeftDirection); + ret.RightDirection = get_direction(props.ulRightDirection); + return ret; + }(); return true; } -template<> -void FrequencyShifterCommitter::SetDefaults(EaxEffectProps &props) +void EaxFrequencyShifterCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXFREQUENCYSHIFTERPROPERTIES defprops{[] { @@ -236,10 +238,8 @@ void FrequencyShifterCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void FrequencyShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxFrequencyShifterCommitter::Get(const EaxCall &call, const EAXFREQUENCYSHIFTERPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXFREQUENCYSHIFTER_NONE: break; @@ -251,10 +251,8 @@ void FrequencyShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &p } } -template<> -void FrequencyShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxFrequencyShifterCommitter::Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXFREQUENCYSHIFTER_NONE: break; diff --git a/3rdparty/openal/al/effects/modulator.cpp b/3rdparty/openal/al/effects/modulator.cpp index 366e7ef71154..fd178d07a994 100644 --- a/3rdparty/openal/al/effects/modulator.cpp +++ b/3rdparty/openal/al/effects/modulator.cpp @@ -10,9 +10,10 @@ #include "alc/effects/base.h" #include "effects.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include -#include "alnumeric.h" + +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -20,7 +21,7 @@ namespace { -std::optional WaveformFromEmum(ALenum value) +constexpr std::optional WaveformFromEmum(ALenum value) noexcept { switch(value) { @@ -30,7 +31,7 @@ std::optional WaveformFromEmum(ALenum value) } return std::nullopt; } -ALenum EnumFromWaveform(ModulatorWaveform type) +constexpr ALenum EnumFromWaveform(ModulatorWaveform type) { switch(type) { @@ -42,40 +43,31 @@ ALenum EnumFromWaveform(ModulatorWaveform type) std::to_string(static_cast(type))}; } -void Modulator_setParamf(EffectProps *props, ALenum param, float val) +constexpr EffectProps genDefaultProps() noexcept { - switch(param) - { - case AL_RING_MODULATOR_FREQUENCY: - if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY)) - throw effect_exception{AL_INVALID_VALUE, "Modulator frequency out of range: %f", val}; - props->Modulator.Frequency = val; - break; + ModulatorProps props{}; + props.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY; + props.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF; + props.Waveform = WaveformFromEmum(AL_RING_MODULATOR_DEFAULT_WAVEFORM).value(); + return props; +} - case AL_RING_MODULATOR_HIGHPASS_CUTOFF: - if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF)) - throw effect_exception{AL_INVALID_VALUE, "Modulator high-pass cutoff out of range: %f", val}; - props->Modulator.HighPassCutoff = val; - break; +} // namespace - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param}; - } -} -void Modulator_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Modulator_setParamf(props, param, vals[0]); } -void Modulator_setParami(EffectProps *props, ALenum param, int val) +const EffectProps ModulatorEffectProps{genDefaultProps()}; + +void ModulatorEffectHandler::SetParami(ModulatorProps &props, ALenum param, int val) { switch(param) { case AL_RING_MODULATOR_FREQUENCY: case AL_RING_MODULATOR_HIGHPASS_CUTOFF: - Modulator_setParamf(props, param, static_cast(val)); + SetParamf(props, param, static_cast(val)); break; case AL_RING_MODULATOR_WAVEFORM: if(auto formopt = WaveformFromEmum(val)) - props->Modulator.Waveform = *formopt; + props.Waveform = *formopt; else throw effect_exception{AL_INVALID_VALUE, "Invalid modulator waveform: 0x%04x", val}; break; @@ -85,64 +77,63 @@ void Modulator_setParami(EffectProps *props, ALenum param, int val) param}; } } -void Modulator_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Modulator_setParami(props, param, vals[0]); } +void ModulatorEffectHandler::SetParamiv(ModulatorProps &props, ALenum param, const int *vals) +{ SetParami(props, param, *vals); } -void Modulator_getParami(const EffectProps *props, ALenum param, int *val) +void ModulatorEffectHandler::SetParamf(ModulatorProps &props, ALenum param, float val) { switch(param) { case AL_RING_MODULATOR_FREQUENCY: - *val = static_cast(props->Modulator.Frequency); + if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY)) + throw effect_exception{AL_INVALID_VALUE, "Modulator frequency out of range: %f", val}; + props.Frequency = val; break; + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: - *val = static_cast(props->Modulator.HighPassCutoff); - break; - case AL_RING_MODULATOR_WAVEFORM: - *val = EnumFromWaveform(props->Modulator.Waveform); + if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF)) + throw effect_exception{AL_INVALID_VALUE, "Modulator high-pass cutoff out of range: %f", val}; + props.HighPassCutoff = val; break; + default: + throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param}; + } +} +void ModulatorEffectHandler::SetParamfv(ModulatorProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, *vals); } + +void ModulatorEffectHandler::GetParami(const ModulatorProps &props, ALenum param, int *val) +{ + switch(param) + { + case AL_RING_MODULATOR_FREQUENCY: *val = static_cast(props.Frequency); break; + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = static_cast(props.HighPassCutoff); break; + case AL_RING_MODULATOR_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break; + default: throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", param}; } } -void Modulator_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Modulator_getParami(props, param, vals); } -void Modulator_getParamf(const EffectProps *props, ALenum param, float *val) +void ModulatorEffectHandler::GetParamiv(const ModulatorProps &props, ALenum param, int *vals) +{ GetParami(props, param, vals); } +void ModulatorEffectHandler::GetParamf(const ModulatorProps &props, ALenum param, float *val) { switch(param) { - case AL_RING_MODULATOR_FREQUENCY: - *val = props->Modulator.Frequency; - break; - case AL_RING_MODULATOR_HIGHPASS_CUTOFF: - *val = props->Modulator.HighPassCutoff; - break; + case AL_RING_MODULATOR_FREQUENCY: *val = props.Frequency; break; + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = props.HighPassCutoff; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param}; } } -void Modulator_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Modulator_getParamf(props, param, vals); } +void ModulatorEffectHandler::GetParamfv(const ModulatorProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY; - props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF; - props.Modulator.Waveform = *WaveformFromEmum(AL_RING_MODULATOR_DEFAULT_WAVEFORM); - return props; -} - -} // namespace - -DEFINE_ALEFFECT_VTABLE(Modulator); - -const EffectProps ModulatorEffectProps{genDefaultProps()}; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using ModulatorCommitter = EaxCommitter; @@ -203,10 +194,9 @@ template<> throw Exception{message}; } -template<> -bool ModulatorCommitter::commit(const EaxEffectProps &props) +bool EaxModulatorCommitter::commit(const EAXRINGMODULATORPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; @@ -222,16 +212,18 @@ bool ModulatorCommitter::commit(const EaxEffectProps &props) return ModulatorWaveform::Sinusoid; }; - auto &eaxprops = std::get(props); - mAlProps.Modulator.Frequency = eaxprops.flFrequency; - mAlProps.Modulator.HighPassCutoff = eaxprops.flHighPassCutOff; - mAlProps.Modulator.Waveform = get_waveform(eaxprops.ulWaveform); + mAlProps = [&]{ + ModulatorProps ret{}; + ret.Frequency = props.flFrequency; + ret.HighPassCutoff = props.flHighPassCutOff; + ret.Waveform = get_waveform(props.ulWaveform); + return ret; + }(); return true; } -template<> -void ModulatorCommitter::SetDefaults(EaxEffectProps &props) +void EaxModulatorCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXRINGMODULATORPROPERTIES defprops{[] { @@ -244,10 +236,8 @@ void ModulatorCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void ModulatorCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxModulatorCommitter::Get(const EaxCall &call, const EAXRINGMODULATORPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXRINGMODULATOR_NONE: break; @@ -259,11 +249,9 @@ void ModulatorCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) } } -template<> -void ModulatorCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxModulatorCommitter::Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props) { - auto &props = std::get(props_); - switch (call.get_property_id()) + switch(call.get_property_id()) { case EAXRINGMODULATOR_NONE: break; case EAXRINGMODULATOR_ALLPARAMETERS: defer(call, props); break; diff --git a/3rdparty/openal/al/effects/null.cpp b/3rdparty/openal/al/effects/null.cpp index 1e8787e79381..728752373987 100644 --- a/3rdparty/openal/al/effects/null.cpp +++ b/3rdparty/openal/al/effects/null.cpp @@ -7,14 +7,24 @@ #include "alc/effects/base.h" #include "effects.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX +#include "al/eax/effect.h" #include "al/eax/exception.h" #endif // ALSOFT_EAX namespace { -void Null_setParami(EffectProps* /*props*/, ALenum param, int /*val*/) +constexpr EffectProps genDefaultProps() noexcept +{ + return std::monostate{}; +} + +} // namespace + +const EffectProps NullEffectProps{genDefaultProps()}; + +void NullEffectHandler::SetParami(std::monostate& /*props*/, ALenum param, int /*val*/) { switch(param) { @@ -23,15 +33,15 @@ void Null_setParami(EffectProps* /*props*/, ALenum param, int /*val*/) param}; } } -void Null_setParamiv(EffectProps *props, ALenum param, const int *vals) +void NullEffectHandler::SetParamiv(std::monostate &props, ALenum param, const int *vals) { switch(param) { default: - Null_setParami(props, param, vals[0]); + SetParami(props, param, *vals); } } -void Null_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/) +void NullEffectHandler::SetParamf(std::monostate& /*props*/, ALenum param, float /*val*/) { switch(param) { @@ -40,16 +50,16 @@ void Null_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/) param}; } } -void Null_setParamfv(EffectProps *props, ALenum param, const float *vals) +void NullEffectHandler::SetParamfv(std::monostate &props, ALenum param, const float *vals) { switch(param) { default: - Null_setParamf(props, param, vals[0]); + SetParamf(props, param, *vals); } } -void Null_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/) +void NullEffectHandler::GetParami(const std::monostate& /*props*/, ALenum param, int* /*val*/) { switch(param) { @@ -58,15 +68,15 @@ void Null_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/) param}; } } -void Null_getParamiv(const EffectProps *props, ALenum param, int *vals) +void NullEffectHandler::GetParamiv(const std::monostate &props, ALenum param, int *vals) { switch(param) { default: - Null_getParami(props, param, vals); + GetParami(props, param, vals); } } -void Null_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/) +void NullEffectHandler::GetParamf(const std::monostate& /*props*/, ALenum param, float* /*val*/) { switch(param) { @@ -75,29 +85,17 @@ void Null_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/) param}; } } -void Null_getParamfv(const EffectProps *props, ALenum param, float *vals) +void NullEffectHandler::GetParamfv(const std::monostate &props, ALenum param, float *vals) { switch(param) { default: - Null_getParamf(props, param, vals); + GetParamf(props, param, vals); } } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - return props; -} -} // namespace - -DEFINE_ALEFFECT_VTABLE(Null); - -const EffectProps NullEffectProps{genDefaultProps()}; - - -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using NullCommitter = EaxCommitter; @@ -117,29 +115,26 @@ template<> throw Exception{message}; } -template<> -bool NullCommitter::commit(const EaxEffectProps &props) +bool EaxNullCommitter::commit(const std::monostate &props) { - const bool ret{props != mEaxProps}; + const bool ret{std::holds_alternative(mEaxProps)}; mEaxProps = props; + mAlProps = std::monostate{}; return ret; } -template<> -void NullCommitter::SetDefaults(EaxEffectProps &props) +void EaxNullCommitter::SetDefaults(EaxEffectProps &props) { - props.emplace(); + props = std::monostate{}; } -template<> -void NullCommitter::Get(const EaxCall &call, const EaxEffectProps&) +void EaxNullCommitter::Get(const EaxCall &call, const std::monostate&) { if(call.get_property_id() != 0) fail_unknown_property_id(); } -template<> -void NullCommitter::Set(const EaxCall &call, EaxEffectProps&) +void EaxNullCommitter::Set(const EaxCall &call, std::monostate&) { if(call.get_property_id() != 0) fail_unknown_property_id(); diff --git a/3rdparty/openal/al/effects/pshifter.cpp b/3rdparty/openal/al/effects/pshifter.cpp index 10824016b7d3..9cc4ea1cdfb2 100644 --- a/3rdparty/openal/al/effects/pshifter.cpp +++ b/3rdparty/openal/al/effects/pshifter.cpp @@ -7,8 +7,8 @@ #include "alc/effects/base.h" #include "effects.h" -#ifdef ALSOFT_EAX -#include "alnumeric.h" +#if ALSOFT_EAX +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -16,28 +16,32 @@ namespace { -void Pshifter_setParamf(EffectProps*, ALenum param, float) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; } -void Pshifter_setParamfv(EffectProps*, ALenum param, const float*) +constexpr EffectProps genDefaultProps() noexcept { - throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x", - param}; + PshifterProps props{}; + props.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE; + props.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE; + return props; } -void Pshifter_setParami(EffectProps *props, ALenum param, int val) +} // namespace + +const EffectProps PshifterEffectProps{genDefaultProps()}; + +void PshifterEffectHandler::SetParami(PshifterProps &props, ALenum param, int val) { switch(param) { case AL_PITCH_SHIFTER_COARSE_TUNE: if(!(val >= AL_PITCH_SHIFTER_MIN_COARSE_TUNE && val <= AL_PITCH_SHIFTER_MAX_COARSE_TUNE)) throw effect_exception{AL_INVALID_VALUE, "Pitch shifter coarse tune out of range"}; - props->Pshifter.CoarseTune = val; + props.CoarseTune = val; break; case AL_PITCH_SHIFTER_FINE_TUNE: if(!(val >= AL_PITCH_SHIFTER_MIN_FINE_TUNE && val <= AL_PITCH_SHIFTER_MAX_FINE_TUNE)) throw effect_exception{AL_INVALID_VALUE, "Pitch shifter fine tune out of range"}; - props->Pshifter.FineTune = val; + props.FineTune = val; break; default: @@ -45,51 +49,42 @@ void Pshifter_setParami(EffectProps *props, ALenum param, int val) param}; } } -void Pshifter_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Pshifter_setParami(props, param, vals[0]); } +void PshifterEffectHandler::SetParamiv(PshifterProps &props, ALenum param, const int *vals) +{ SetParami(props, param, *vals); } -void Pshifter_getParami(const EffectProps *props, ALenum param, int *val) +void PshifterEffectHandler::SetParamf(PshifterProps&, ALenum param, float) +{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; } +void PshifterEffectHandler::SetParamfv(PshifterProps&, ALenum param, const float*) +{ + throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x", + param}; +} + +void PshifterEffectHandler::GetParami(const PshifterProps &props, ALenum param, int *val) { switch(param) { - case AL_PITCH_SHIFTER_COARSE_TUNE: - *val = props->Pshifter.CoarseTune; - break; - case AL_PITCH_SHIFTER_FINE_TUNE: - *val = props->Pshifter.FineTune; - break; + case AL_PITCH_SHIFTER_COARSE_TUNE: *val = props.CoarseTune; break; + case AL_PITCH_SHIFTER_FINE_TUNE: *val = props.FineTune; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", param}; } } -void Pshifter_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Pshifter_getParami(props, param, vals); } +void PshifterEffectHandler::GetParamiv(const PshifterProps &props, ALenum param, int *vals) +{ GetParami(props, param, vals); } -void Pshifter_getParamf(const EffectProps*, ALenum param, float*) +void PshifterEffectHandler::GetParamf(const PshifterProps&, ALenum param, float*) { throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; } -void Pshifter_getParamfv(const EffectProps*, ALenum param, float*) +void PshifterEffectHandler::GetParamfv(const PshifterProps&, ALenum param, float*) { throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x", param}; } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Pshifter.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE; - props.Pshifter.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE; - return props; -} - -} // namespace - -DEFINE_ALEFFECT_VTABLE(Pshifter); -const EffectProps PshifterEffectProps{genDefaultProps()}; - -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using PitchShifterCommitter = EaxCommitter; @@ -138,32 +133,30 @@ template<> throw Exception{message}; } -template<> -bool PitchShifterCommitter::commit(const EaxEffectProps &props) +bool EaxPitchShifterCommitter::commit(const EAXPITCHSHIFTERPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; - - auto &eaxprops = std::get(props); - mAlProps.Pshifter.CoarseTune = static_cast(eaxprops.lCoarseTune); - mAlProps.Pshifter.FineTune = static_cast(eaxprops.lFineTune); + mAlProps = [&]{ + PshifterProps ret{}; + ret.CoarseTune = static_cast(props.lCoarseTune); + ret.FineTune = static_cast(props.lFineTune); + return ret; + }(); return true; } -template<> -void PitchShifterCommitter::SetDefaults(EaxEffectProps &props) +void EaxPitchShifterCommitter::SetDefaults(EaxEffectProps &props) { props = EAXPITCHSHIFTERPROPERTIES{EAXPITCHSHIFTER_DEFAULTCOARSETUNE, EAXPITCHSHIFTER_DEFAULTFINETUNE}; } -template<> -void PitchShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxPitchShifterCommitter::Get(const EaxCall &call, const EAXPITCHSHIFTERPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXPITCHSHIFTER_NONE: break; @@ -174,10 +167,8 @@ void PitchShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props } } -template<> -void PitchShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxPitchShifterCommitter::Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXPITCHSHIFTER_NONE: break; diff --git a/3rdparty/openal/al/effects/reverb.cpp b/3rdparty/openal/al/effects/reverb.cpp index b037443fafdf..68d230f86ed3 100644 --- a/3rdparty/openal/al/effects/reverb.cpp +++ b/3rdparty/openal/al/effects/reverb.cpp @@ -1,18 +1,24 @@ #include "config.h" +#include +#include #include +#include #include "AL/al.h" #include "AL/efx.h" -#include "alc/effects/base.h" +#include "alnumeric.h" +#include "alspan.h" +#include "core/effects/base.h" #include "effects.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include -#include "alnumeric.h" -#include "AL/efx-presets.h" +#include "al/eax/api.h" +#include "al/eax/call.h" +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -20,14 +26,80 @@ namespace { -void Reverb_setParami(EffectProps *props, ALenum param, int val) +constexpr EffectProps genDefaultProps() noexcept +{ + ReverbProps props{}; + props.Density = AL_EAXREVERB_DEFAULT_DENSITY; + props.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION; + props.Gain = AL_EAXREVERB_DEFAULT_GAIN; + props.GainHF = AL_EAXREVERB_DEFAULT_GAINHF; + props.GainLF = AL_EAXREVERB_DEFAULT_GAINLF; + props.DecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME; + props.DecayHFRatio = AL_EAXREVERB_DEFAULT_DECAY_HFRATIO; + props.DecayLFRatio = AL_EAXREVERB_DEFAULT_DECAY_LFRATIO; + props.ReflectionsGain = AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN; + props.ReflectionsDelay = AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY; + props.ReflectionsPan[0] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; + props.ReflectionsPan[1] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; + props.ReflectionsPan[2] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; + props.LateReverbGain = AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN; + props.LateReverbDelay = AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY; + props.LateReverbPan[0] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; + props.LateReverbPan[1] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; + props.LateReverbPan[2] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; + props.EchoTime = AL_EAXREVERB_DEFAULT_ECHO_TIME; + props.EchoDepth = AL_EAXREVERB_DEFAULT_ECHO_DEPTH; + props.ModulationTime = AL_EAXREVERB_DEFAULT_MODULATION_TIME; + props.ModulationDepth = AL_EAXREVERB_DEFAULT_MODULATION_DEPTH; + props.AirAbsorptionGainHF = AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF; + props.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE; + props.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE; + props.RoomRolloffFactor = AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; + props.DecayHFLimit = AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT; + return props; +} + +constexpr EffectProps genDefaultStdProps() noexcept +{ + ReverbProps props{}; + props.Density = AL_REVERB_DEFAULT_DENSITY; + props.Diffusion = AL_REVERB_DEFAULT_DIFFUSION; + props.Gain = AL_REVERB_DEFAULT_GAIN; + props.GainHF = AL_REVERB_DEFAULT_GAINHF; + props.GainLF = 1.0f; + props.DecayTime = AL_REVERB_DEFAULT_DECAY_TIME; + props.DecayHFRatio = AL_REVERB_DEFAULT_DECAY_HFRATIO; + props.DecayLFRatio = 1.0f; + props.ReflectionsGain = AL_REVERB_DEFAULT_REFLECTIONS_GAIN; + props.ReflectionsDelay = AL_REVERB_DEFAULT_REFLECTIONS_DELAY; + props.ReflectionsPan = {0.0f, 0.0f, 0.0f}; + props.LateReverbGain = AL_REVERB_DEFAULT_LATE_REVERB_GAIN; + props.LateReverbDelay = AL_REVERB_DEFAULT_LATE_REVERB_DELAY; + props.LateReverbPan = {0.0f, 0.0f, 0.0f}; + props.EchoTime = 0.25f; + props.EchoDepth = 0.0f; + props.ModulationTime = 0.25f; + props.ModulationDepth = 0.0f; + props.AirAbsorptionGainHF = AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF; + props.HFReference = 5000.0f; + props.LFReference = 250.0f; + props.RoomRolloffFactor = AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; + props.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT; + return props; +} + +} // namespace + +const EffectProps ReverbEffectProps{genDefaultProps()}; + +void ReverbEffectHandler::SetParami(ReverbProps &props, ALenum param, int val) { switch(param) { case AL_EAXREVERB_DECAY_HFLIMIT: if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hflimit out of range"}; - props->Reverb.DecayHFLimit = val != AL_FALSE; + props.DecayHFLimit = val != AL_FALSE; break; default: @@ -35,535 +107,363 @@ void Reverb_setParami(EffectProps *props, ALenum param, int val) param}; } } -void Reverb_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Reverb_setParami(props, param, vals[0]); } -void Reverb_setParamf(EffectProps *props, ALenum param, float val) +void ReverbEffectHandler::SetParamiv(ReverbProps &props, ALenum param, const int *vals) +{ SetParami(props, param, *vals); } +void ReverbEffectHandler::SetParamf(ReverbProps &props, ALenum param, float val) { switch(param) { case AL_EAXREVERB_DENSITY: if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb density out of range"}; - props->Reverb.Density = val; + props.Density = val; break; case AL_EAXREVERB_DIFFUSION: if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb diffusion out of range"}; - props->Reverb.Diffusion = val; + props.Diffusion = val; break; case AL_EAXREVERB_GAIN: if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gain out of range"}; - props->Reverb.Gain = val; + props.Gain = val; break; case AL_EAXREVERB_GAINHF: if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gainhf out of range"}; - props->Reverb.GainHF = val; + props.GainHF = val; break; case AL_EAXREVERB_GAINLF: if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gainlf out of range"}; - props->Reverb.GainLF = val; + props.GainLF = val; break; case AL_EAXREVERB_DECAY_TIME: if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay time out of range"}; - props->Reverb.DecayTime = val; + props.DecayTime = val; break; case AL_EAXREVERB_DECAY_HFRATIO: if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hfratio out of range"}; - props->Reverb.DecayHFRatio = val; + props.DecayHFRatio = val; break; case AL_EAXREVERB_DECAY_LFRATIO: if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay lfratio out of range"}; - props->Reverb.DecayLFRatio = val; + props.DecayLFRatio = val; break; case AL_EAXREVERB_REFLECTIONS_GAIN: if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections gain out of range"}; - props->Reverb.ReflectionsGain = val; + props.ReflectionsGain = val; break; case AL_EAXREVERB_REFLECTIONS_DELAY: if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections delay out of range"}; - props->Reverb.ReflectionsDelay = val; + props.ReflectionsDelay = val; break; case AL_EAXREVERB_LATE_REVERB_GAIN: if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb gain out of range"}; - props->Reverb.LateReverbGain = val; + props.LateReverbGain = val; break; case AL_EAXREVERB_LATE_REVERB_DELAY: if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb delay out of range"}; - props->Reverb.LateReverbDelay = val; + props.LateReverbDelay = val; break; case AL_EAXREVERB_ECHO_TIME: if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb echo time out of range"}; - props->Reverb.EchoTime = val; + props.EchoTime = val; break; case AL_EAXREVERB_ECHO_DEPTH: if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb echo depth out of range"}; - props->Reverb.EchoDepth = val; + props.EchoDepth = val; break; case AL_EAXREVERB_MODULATION_TIME: if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb modulation time out of range"}; - props->Reverb.ModulationTime = val; + props.ModulationTime = val; break; case AL_EAXREVERB_MODULATION_DEPTH: if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb modulation depth out of range"}; - props->Reverb.ModulationDepth = val; + props.ModulationDepth = val; break; case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb air absorption gainhf out of range"}; - props->Reverb.AirAbsorptionGainHF = val; + props.AirAbsorptionGainHF = val; break; case AL_EAXREVERB_HFREFERENCE: if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb hfreference out of range"}; - props->Reverb.HFReference = val; + props.HFReference = val; break; case AL_EAXREVERB_LFREFERENCE: if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb lfreference out of range"}; - props->Reverb.LFReference = val; + props.LFReference = val; break; case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb room rolloff factor out of range"}; - props->Reverb.RoomRolloffFactor = val; + props.RoomRolloffFactor = val; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param}; } } -void Reverb_setParamfv(EffectProps *props, ALenum param, const float *vals) +void ReverbEffectHandler::SetParamfv(ReverbProps &props, ALenum param, const float *vals) { + static constexpr auto finite_checker = [](float f) -> bool { return std::isfinite(f); }; + al::span values; switch(param) { case AL_EAXREVERB_REFLECTIONS_PAN: - if(!(std::isfinite(vals[0]) && std::isfinite(vals[1]) && std::isfinite(vals[2]))) + values = {vals, 3_uz}; + if(!std::all_of(values.cbegin(), values.cend(), finite_checker)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections pan out of range"}; - props->Reverb.ReflectionsPan[0] = vals[0]; - props->Reverb.ReflectionsPan[1] = vals[1]; - props->Reverb.ReflectionsPan[2] = vals[2]; + std::copy(values.cbegin(), values.cend(), props.ReflectionsPan.begin()); break; case AL_EAXREVERB_LATE_REVERB_PAN: - if(!(std::isfinite(vals[0]) && std::isfinite(vals[1]) && std::isfinite(vals[2]))) + values = {vals, 3_uz}; + if(!std::all_of(values.cbegin(), values.cend(), finite_checker)) throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb pan out of range"}; - props->Reverb.LateReverbPan[0] = vals[0]; - props->Reverb.LateReverbPan[1] = vals[1]; - props->Reverb.LateReverbPan[2] = vals[2]; + std::copy(values.cbegin(), values.cend(), props.LateReverbPan.begin()); break; default: - Reverb_setParamf(props, param, vals[0]); + SetParamf(props, param, *vals); break; } } -void Reverb_getParami(const EffectProps *props, ALenum param, int *val) +void ReverbEffectHandler::GetParami(const ReverbProps &props, ALenum param, int *val) { switch(param) { - case AL_EAXREVERB_DECAY_HFLIMIT: - *val = props->Reverb.DecayHFLimit; - break; - + case AL_EAXREVERB_DECAY_HFLIMIT: *val = props.DecayHFLimit; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x", param}; } } -void Reverb_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Reverb_getParami(props, param, vals); } -void Reverb_getParamf(const EffectProps *props, ALenum param, float *val) +void ReverbEffectHandler::GetParamiv(const ReverbProps &props, ALenum param, int *vals) +{ GetParami(props, param, vals); } +void ReverbEffectHandler::GetParamf(const ReverbProps &props, ALenum param, float *val) { switch(param) { - case AL_EAXREVERB_DENSITY: - *val = props->Reverb.Density; - break; - - case AL_EAXREVERB_DIFFUSION: - *val = props->Reverb.Diffusion; - break; - - case AL_EAXREVERB_GAIN: - *val = props->Reverb.Gain; - break; - - case AL_EAXREVERB_GAINHF: - *val = props->Reverb.GainHF; - break; - - case AL_EAXREVERB_GAINLF: - *val = props->Reverb.GainLF; - break; - - case AL_EAXREVERB_DECAY_TIME: - *val = props->Reverb.DecayTime; - break; - - case AL_EAXREVERB_DECAY_HFRATIO: - *val = props->Reverb.DecayHFRatio; - break; - - case AL_EAXREVERB_DECAY_LFRATIO: - *val = props->Reverb.DecayLFRatio; - break; - - case AL_EAXREVERB_REFLECTIONS_GAIN: - *val = props->Reverb.ReflectionsGain; - break; - - case AL_EAXREVERB_REFLECTIONS_DELAY: - *val = props->Reverb.ReflectionsDelay; - break; - - case AL_EAXREVERB_LATE_REVERB_GAIN: - *val = props->Reverb.LateReverbGain; - break; - - case AL_EAXREVERB_LATE_REVERB_DELAY: - *val = props->Reverb.LateReverbDelay; - break; - - case AL_EAXREVERB_ECHO_TIME: - *val = props->Reverb.EchoTime; - break; - - case AL_EAXREVERB_ECHO_DEPTH: - *val = props->Reverb.EchoDepth; - break; - - case AL_EAXREVERB_MODULATION_TIME: - *val = props->Reverb.ModulationTime; - break; - - case AL_EAXREVERB_MODULATION_DEPTH: - *val = props->Reverb.ModulationDepth; - break; - - case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: - *val = props->Reverb.AirAbsorptionGainHF; - break; - - case AL_EAXREVERB_HFREFERENCE: - *val = props->Reverb.HFReference; - break; - - case AL_EAXREVERB_LFREFERENCE: - *val = props->Reverb.LFReference; - break; - - case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: - *val = props->Reverb.RoomRolloffFactor; - break; + case AL_EAXREVERB_DENSITY: *val = props.Density; break; + case AL_EAXREVERB_DIFFUSION: *val = props.Diffusion; break; + case AL_EAXREVERB_GAIN: *val = props.Gain; break; + case AL_EAXREVERB_GAINHF: *val = props.GainHF; break; + case AL_EAXREVERB_GAINLF: *val = props.GainLF; break; + case AL_EAXREVERB_DECAY_TIME: *val = props.DecayTime; break; + case AL_EAXREVERB_DECAY_HFRATIO: *val = props.DecayHFRatio; break; + case AL_EAXREVERB_DECAY_LFRATIO: *val = props.DecayLFRatio; break; + case AL_EAXREVERB_REFLECTIONS_GAIN: *val = props.ReflectionsGain; break; + case AL_EAXREVERB_REFLECTIONS_DELAY: *val = props.ReflectionsDelay; break; + case AL_EAXREVERB_LATE_REVERB_GAIN: *val = props.LateReverbGain; break; + case AL_EAXREVERB_LATE_REVERB_DELAY: *val = props.LateReverbDelay; break; + case AL_EAXREVERB_ECHO_TIME: *val = props.EchoTime; break; + case AL_EAXREVERB_ECHO_DEPTH: *val = props.EchoDepth; break; + case AL_EAXREVERB_MODULATION_TIME: *val = props.ModulationTime; break; + case AL_EAXREVERB_MODULATION_DEPTH: *val = props.ModulationDepth; break; + case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: *val = props.AirAbsorptionGainHF; break; + case AL_EAXREVERB_HFREFERENCE: *val = props.HFReference; break; + case AL_EAXREVERB_LFREFERENCE: *val = props.LFReference; break; + case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: *val = props.RoomRolloffFactor; break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param}; } } -void Reverb_getParamfv(const EffectProps *props, ALenum param, float *vals) +void ReverbEffectHandler::GetParamfv(const ReverbProps &props, ALenum param, float *vals) { + al::span values; switch(param) { case AL_EAXREVERB_REFLECTIONS_PAN: - vals[0] = props->Reverb.ReflectionsPan[0]; - vals[1] = props->Reverb.ReflectionsPan[1]; - vals[2] = props->Reverb.ReflectionsPan[2]; + values = {vals, 3_uz}; + std::copy(props.ReflectionsPan.cbegin(), props.ReflectionsPan.cend(), values.begin()); break; case AL_EAXREVERB_LATE_REVERB_PAN: - vals[0] = props->Reverb.LateReverbPan[0]; - vals[1] = props->Reverb.LateReverbPan[1]; - vals[2] = props->Reverb.LateReverbPan[2]; + values = {vals, 3_uz}; + std::copy(props.LateReverbPan.cbegin(), props.LateReverbPan.cend(), values.begin()); break; default: - Reverb_getParamf(props, param, vals); + GetParamf(props, param, vals); break; } } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Reverb.Density = AL_EAXREVERB_DEFAULT_DENSITY; - props.Reverb.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION; - props.Reverb.Gain = AL_EAXREVERB_DEFAULT_GAIN; - props.Reverb.GainHF = AL_EAXREVERB_DEFAULT_GAINHF; - props.Reverb.GainLF = AL_EAXREVERB_DEFAULT_GAINLF; - props.Reverb.DecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME; - props.Reverb.DecayHFRatio = AL_EAXREVERB_DEFAULT_DECAY_HFRATIO; - props.Reverb.DecayLFRatio = AL_EAXREVERB_DEFAULT_DECAY_LFRATIO; - props.Reverb.ReflectionsGain = AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN; - props.Reverb.ReflectionsDelay = AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY; - props.Reverb.ReflectionsPan[0] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; - props.Reverb.ReflectionsPan[1] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; - props.Reverb.ReflectionsPan[2] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; - props.Reverb.LateReverbGain = AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN; - props.Reverb.LateReverbDelay = AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY; - props.Reverb.LateReverbPan[0] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; - props.Reverb.LateReverbPan[1] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; - props.Reverb.LateReverbPan[2] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; - props.Reverb.EchoTime = AL_EAXREVERB_DEFAULT_ECHO_TIME; - props.Reverb.EchoDepth = AL_EAXREVERB_DEFAULT_ECHO_DEPTH; - props.Reverb.ModulationTime = AL_EAXREVERB_DEFAULT_MODULATION_TIME; - props.Reverb.ModulationDepth = AL_EAXREVERB_DEFAULT_MODULATION_DEPTH; - props.Reverb.AirAbsorptionGainHF = AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF; - props.Reverb.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE; - props.Reverb.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE; - props.Reverb.RoomRolloffFactor = AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; - props.Reverb.DecayHFLimit = AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT; - return props; -} +const EffectProps StdReverbEffectProps{genDefaultStdProps()}; -void StdReverb_setParami(EffectProps *props, ALenum param, int val) +void StdReverbEffectHandler::SetParami(ReverbProps &props, ALenum param, int val) { switch(param) { case AL_REVERB_DECAY_HFLIMIT: if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT)) - throw effect_exception{AL_INVALID_VALUE, "Reverb decay hflimit out of range"}; - props->Reverb.DecayHFLimit = val != AL_FALSE; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hflimit out of range"}; + props.DecayHFLimit = val != AL_FALSE; break; default: - throw effect_exception{AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param}; + throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x", + param}; } } -void StdReverb_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ StdReverb_setParami(props, param, vals[0]); } -void StdReverb_setParamf(EffectProps *props, ALenum param, float val) +void StdReverbEffectHandler::SetParamiv(ReverbProps &props, ALenum param, const int *vals) +{ SetParami(props, param, *vals); } +void StdReverbEffectHandler::SetParamf(ReverbProps &props, ALenum param, float val) { switch(param) { case AL_REVERB_DENSITY: if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY)) - throw effect_exception{AL_INVALID_VALUE, "Reverb density out of range"}; - props->Reverb.Density = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb density out of range"}; + props.Density = val; break; case AL_REVERB_DIFFUSION: if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION)) - throw effect_exception{AL_INVALID_VALUE, "Reverb diffusion out of range"}; - props->Reverb.Diffusion = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb diffusion out of range"}; + props.Diffusion = val; break; case AL_REVERB_GAIN: if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "Reverb gain out of range"}; - props->Reverb.Gain = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gain out of range"}; + props.Gain = val; break; case AL_REVERB_GAINHF: if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF)) - throw effect_exception{AL_INVALID_VALUE, "Reverb gainhf out of range"}; - props->Reverb.GainHF = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gainhf out of range"}; + props.GainHF = val; break; case AL_REVERB_DECAY_TIME: if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME)) - throw effect_exception{AL_INVALID_VALUE, "Reverb decay time out of range"}; - props->Reverb.DecayTime = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay time out of range"}; + props.DecayTime = val; break; case AL_REVERB_DECAY_HFRATIO: if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO)) - throw effect_exception{AL_INVALID_VALUE, "Reverb decay hfratio out of range"}; - props->Reverb.DecayHFRatio = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hfratio out of range"}; + props.DecayHFRatio = val; break; case AL_REVERB_REFLECTIONS_GAIN: if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "Reverb reflections gain out of range"}; - props->Reverb.ReflectionsGain = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections gain out of range"}; + props.ReflectionsGain = val; break; case AL_REVERB_REFLECTIONS_DELAY: if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY)) - throw effect_exception{AL_INVALID_VALUE, "Reverb reflections delay out of range"}; - props->Reverb.ReflectionsDelay = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections delay out of range"}; + props.ReflectionsDelay = val; break; case AL_REVERB_LATE_REVERB_GAIN: if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "Reverb late reverb gain out of range"}; - props->Reverb.LateReverbGain = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb gain out of range"}; + props.LateReverbGain = val; break; case AL_REVERB_LATE_REVERB_DELAY: if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY)) - throw effect_exception{AL_INVALID_VALUE, "Reverb late reverb delay out of range"}; - props->Reverb.LateReverbDelay = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb delay out of range"}; + props.LateReverbDelay = val; break; case AL_REVERB_AIR_ABSORPTION_GAINHF: if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF)) - throw effect_exception{AL_INVALID_VALUE, "Reverb air absorption gainhf out of range"}; - props->Reverb.AirAbsorptionGainHF = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb air absorption gainhf out of range"}; + props.AirAbsorptionGainHF = val; break; case AL_REVERB_ROOM_ROLLOFF_FACTOR: if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR)) - throw effect_exception{AL_INVALID_VALUE, "Reverb room rolloff factor out of range"}; - props->Reverb.RoomRolloffFactor = val; + throw effect_exception{AL_INVALID_VALUE, "EAX Reverb room rolloff factor out of range"}; + props.RoomRolloffFactor = val; break; default: - throw effect_exception{AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param}; + throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param}; } } -void StdReverb_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ StdReverb_setParamf(props, param, vals[0]); } +void StdReverbEffectHandler::SetParamfv(ReverbProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, *vals); } -void StdReverb_getParami(const EffectProps *props, ALenum param, int *val) +void StdReverbEffectHandler::GetParami(const ReverbProps &props, ALenum param, int *val) { switch(param) { - case AL_REVERB_DECAY_HFLIMIT: - *val = props->Reverb.DecayHFLimit; - break; - + case AL_REVERB_DECAY_HFLIMIT: *val = props.DecayHFLimit; break; default: - throw effect_exception{AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param}; + throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x", + param}; } } -void StdReverb_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ StdReverb_getParami(props, param, vals); } -void StdReverb_getParamf(const EffectProps *props, ALenum param, float *val) +void StdReverbEffectHandler::GetParamiv(const ReverbProps &props, ALenum param, int *vals) +{ GetParami(props, param, vals); } +void StdReverbEffectHandler::GetParamf(const ReverbProps &props, ALenum param, float *val) { switch(param) { - case AL_REVERB_DENSITY: - *val = props->Reverb.Density; - break; - - case AL_REVERB_DIFFUSION: - *val = props->Reverb.Diffusion; - break; - - case AL_REVERB_GAIN: - *val = props->Reverb.Gain; - break; - - case AL_REVERB_GAINHF: - *val = props->Reverb.GainHF; - break; - - case AL_REVERB_DECAY_TIME: - *val = props->Reverb.DecayTime; - break; - - case AL_REVERB_DECAY_HFRATIO: - *val = props->Reverb.DecayHFRatio; - break; - - case AL_REVERB_REFLECTIONS_GAIN: - *val = props->Reverb.ReflectionsGain; - break; - - case AL_REVERB_REFLECTIONS_DELAY: - *val = props->Reverb.ReflectionsDelay; - break; - - case AL_REVERB_LATE_REVERB_GAIN: - *val = props->Reverb.LateReverbGain; - break; - - case AL_REVERB_LATE_REVERB_DELAY: - *val = props->Reverb.LateReverbDelay; - break; - - case AL_REVERB_AIR_ABSORPTION_GAINHF: - *val = props->Reverb.AirAbsorptionGainHF; - break; - - case AL_REVERB_ROOM_ROLLOFF_FACTOR: - *val = props->Reverb.RoomRolloffFactor; - break; + case AL_REVERB_DENSITY: *val = props.Density; break; + case AL_REVERB_DIFFUSION: *val = props.Diffusion; break; + case AL_REVERB_GAIN: *val = props.Gain; break; + case AL_REVERB_GAINHF: *val = props.GainHF; break; + case AL_REVERB_DECAY_TIME: *val = props.DecayTime; break; + case AL_REVERB_DECAY_HFRATIO: *val = props.DecayHFRatio; break; + case AL_REVERB_REFLECTIONS_GAIN: *val = props.ReflectionsGain; break; + case AL_REVERB_REFLECTIONS_DELAY: *val = props.ReflectionsDelay; break; + case AL_REVERB_LATE_REVERB_GAIN: *val = props.LateReverbGain; break; + case AL_REVERB_LATE_REVERB_DELAY: *val = props.LateReverbDelay; break; + case AL_REVERB_AIR_ABSORPTION_GAINHF: *val = props.AirAbsorptionGainHF; break; + case AL_REVERB_ROOM_ROLLOFF_FACTOR: *val = props.RoomRolloffFactor; break; default: - throw effect_exception{AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param}; + throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param}; } } -void StdReverb_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ StdReverb_getParamf(props, param, vals); } - -EffectProps genDefaultStdProps() noexcept -{ - EffectProps props{}; - props.Reverb.Density = AL_REVERB_DEFAULT_DENSITY; - props.Reverb.Diffusion = AL_REVERB_DEFAULT_DIFFUSION; - props.Reverb.Gain = AL_REVERB_DEFAULT_GAIN; - props.Reverb.GainHF = AL_REVERB_DEFAULT_GAINHF; - props.Reverb.GainLF = 1.0f; - props.Reverb.DecayTime = AL_REVERB_DEFAULT_DECAY_TIME; - props.Reverb.DecayHFRatio = AL_REVERB_DEFAULT_DECAY_HFRATIO; - props.Reverb.DecayLFRatio = 1.0f; - props.Reverb.ReflectionsGain = AL_REVERB_DEFAULT_REFLECTIONS_GAIN; - props.Reverb.ReflectionsDelay = AL_REVERB_DEFAULT_REFLECTIONS_DELAY; - props.Reverb.ReflectionsPan[0] = 0.0f; - props.Reverb.ReflectionsPan[1] = 0.0f; - props.Reverb.ReflectionsPan[2] = 0.0f; - props.Reverb.LateReverbGain = AL_REVERB_DEFAULT_LATE_REVERB_GAIN; - props.Reverb.LateReverbDelay = AL_REVERB_DEFAULT_LATE_REVERB_DELAY; - props.Reverb.LateReverbPan[0] = 0.0f; - props.Reverb.LateReverbPan[1] = 0.0f; - props.Reverb.LateReverbPan[2] = 0.0f; - props.Reverb.EchoTime = 0.25f; - props.Reverb.EchoDepth = 0.0f; - props.Reverb.ModulationTime = 0.25f; - props.Reverb.ModulationDepth = 0.0f; - props.Reverb.AirAbsorptionGainHF = AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF; - props.Reverb.HFReference = 5000.0f; - props.Reverb.LFReference = 250.0f; - props.Reverb.RoomRolloffFactor = AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; - props.Reverb.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT; - return props; -} - -} // namespace +void StdReverbEffectHandler::GetParamfv(const ReverbProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } -DEFINE_ALEFFECT_VTABLE(Reverb); -const EffectProps ReverbEffectProps{genDefaultProps()}; - -DEFINE_ALEFFECT_VTABLE(StdReverb); - -const EffectProps StdReverbEffectProps{genDefaultStdProps()}; - -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { class EaxReverbEffectException : public EaxException @@ -1086,98 +986,87 @@ struct EaxReverbCommitter::Exception : public EaxReverbEffectException throw Exception{message}; } -void EaxReverbCommitter::translate(const EAX_REVERBPROPERTIES& src, EaxEffectProps& dst) noexcept +void EaxReverbCommitter::translate(const EAX_REVERBPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept { assert(src.environment <= EAX1REVERB_MAXENVIRONMENT); - auto&& eaxprops = dst.emplace(EAXREVERB_PRESETS[src.environment]); - eaxprops.flDecayTime = src.fDecayTime_sec; - eaxprops.flDecayHFRatio = src.fDamping; - eaxprops.lReverb = mini(static_cast(gain_to_level_mb(src.fVolume)), 0); + dst = EAXREVERB_PRESETS[src.environment]; + dst.flDecayTime = src.fDecayTime_sec; + dst.flDecayHFRatio = src.fDamping; + dst.lReverb = static_cast(std::min(gain_to_level_mb(src.fVolume), 0.0f)); } -void EaxReverbCommitter::translate(const EAX20LISTENERPROPERTIES& src, EaxEffectProps& dst) noexcept +void EaxReverbCommitter::translate(const EAX20LISTENERPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept { assert(src.dwEnvironment <= EAX1REVERB_MAXENVIRONMENT); - auto&& eaxprops = dst.emplace(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 = src; + dst = EAXREVERB_PRESETS[src.dwEnvironment]; + dst.ulEnvironment = src.dwEnvironment; + dst.flEnvironmentSize = src.flEnvironmentSize; + dst.flEnvironmentDiffusion = src.flEnvironmentDiffusion; + dst.lRoom = src.lRoom; + dst.lRoomHF = src.lRoomHF; + dst.flDecayTime = src.flDecayTime; + dst.flDecayHFRatio = src.flDecayHFRatio; + dst.lReflections = src.lReflections; + dst.flReflectionsDelay = src.flReflectionsDelay; + dst.lReverb = src.lReverb; + dst.flReverbDelay = src.flReverbDelay; + dst.flAirAbsorptionHF = src.flAirAbsorptionHF; + dst.flRoomRolloffFactor = src.flRoomRolloffFactor; + dst.ulFlags = src.dwFlags; } bool EaxReverbCommitter::commit(const EAX_REVERBPROPERTIES &props) { - EaxEffectProps dst{}; + EAXREVERBPROPERTIES dst{}; translate(props, dst); return commit(dst); } bool EaxReverbCommitter::commit(const EAX20LISTENERPROPERTIES &props) { - EaxEffectProps dst{}; + EAXREVERBPROPERTIES dst{}; translate(props, dst); return commit(dst); } bool EaxReverbCommitter::commit(const EAXREVERBPROPERTIES &props) { - EaxEffectProps dst{}; - translate(props, dst); - return commit(dst); -} - -bool EaxReverbCommitter::commit(const EaxEffectProps &props) -{ - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; - auto &eaxprops = std::get(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 = eaxprops.flEnvironmentDiffusion; - mAlProps.Reverb.Gain = level_mb_to_gain(static_cast(eaxprops.lRoom)); - mAlProps.Reverb.GainHF = level_mb_to_gain(static_cast(eaxprops.lRoomHF)); - mAlProps.Reverb.GainLF = level_mb_to_gain(static_cast(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(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(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); + const auto size = props.flEnvironmentSize; + const auto density = (size * size * size) / 16.0f; + mAlProps = [&]{ + ReverbProps ret{}; + ret.Density = std::min(density, AL_EAXREVERB_MAX_DENSITY); + ret.Diffusion = props.flEnvironmentDiffusion; + ret.Gain = level_mb_to_gain(static_cast(props.lRoom)); + ret.GainHF = level_mb_to_gain(static_cast(props.lRoomHF)); + ret.GainLF = level_mb_to_gain(static_cast(props.lRoomLF)); + ret.DecayTime = props.flDecayTime; + ret.DecayHFRatio = props.flDecayHFRatio; + ret.DecayLFRatio = props.flDecayLFRatio; + ret.ReflectionsGain = level_mb_to_gain(static_cast(props.lReflections)); + ret.ReflectionsDelay = props.flReflectionsDelay; + ret.ReflectionsPan = {props.vReflectionsPan.x, props.vReflectionsPan.y, + props.vReflectionsPan.z}; + ret.LateReverbGain = level_mb_to_gain(static_cast(props.lReverb)); + ret.LateReverbDelay = props.flReverbDelay; + ret.LateReverbPan = {props.vReverbPan.x, props.vReverbPan.y, props.vReverbPan.z}; + ret.EchoTime = props.flEchoTime; + ret.EchoDepth = props.flEchoDepth; + ret.ModulationTime = props.flModulationTime; + ret.ModulationDepth = props.flModulationDepth; + ret.AirAbsorptionGainHF = level_mb_to_gain(props.flAirAbsorptionHF); + ret.HFReference = props.flHFReference; + ret.LFReference = props.flLFReference; + ret.RoomRolloffFactor = props.flRoomRolloffFactor; + ret.DecayHFLimit = ((props.ulFlags & EAXREVERBFLAGS_DECAYHFLIMIT) != 0); + return ret; + }(); + return true; } @@ -1274,11 +1163,6 @@ void EaxReverbCommitter::Get(const EaxCall &call, const EAXREVERBPROPERTIES &pro } } -void EaxReverbCommitter::Get(const EaxCall &call, const EaxEffectProps &props) -{ - Get(call, std::get(props)); -} - void EaxReverbCommitter::Set(const EaxCall &call, EAX_REVERBPROPERTIES &props) { @@ -1297,71 +1181,23 @@ void EaxReverbCommitter::Set(const EaxCall &call, EAX20LISTENERPROPERTIES &props { switch(call.get_property_id()) { - case DSPROPERTY_EAX20LISTENER_NONE: - break; - - case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS: - defer(call, props); - break; - - case DSPROPERTY_EAX20LISTENER_ROOM: - defer(call, props.lRoom); - break; - - case DSPROPERTY_EAX20LISTENER_ROOMHF: - defer(call, props.lRoomHF); - break; - - case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR: - defer(call, props.flRoomRolloffFactor); - break; - - case DSPROPERTY_EAX20LISTENER_DECAYTIME: - defer(call, props.flDecayTime); - break; - - case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO: - defer(call, props.flDecayHFRatio); - break; - - case DSPROPERTY_EAX20LISTENER_REFLECTIONS: - defer(call, props.lReflections); - break; - - case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY: - defer(call, props.flReverbDelay); - break; - - case DSPROPERTY_EAX20LISTENER_REVERB: - defer(call, props.lReverb); - break; - - case DSPROPERTY_EAX20LISTENER_REVERBDELAY: - defer(call, props.flReverbDelay); - break; - - case DSPROPERTY_EAX20LISTENER_ENVIRONMENT: - defer(call, props, props.dwEnvironment); - break; - - case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE: - defer(call, props, props.flEnvironmentSize); - break; - - case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION: - defer(call, props.flEnvironmentDiffusion); - break; - - case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF: - defer(call, props.flAirAbsorptionHF); - break; - - case DSPROPERTY_EAX20LISTENER_FLAGS: - defer(call, props.dwFlags); - break; - - default: - fail_unknown_property_id(); + case DSPROPERTY_EAX20LISTENER_NONE: break; + case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS: defer(call, props); break; + case DSPROPERTY_EAX20LISTENER_ROOM: defer(call, props.lRoom); break; + case DSPROPERTY_EAX20LISTENER_ROOMHF: defer(call, props.lRoomHF); break; + case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR: defer(call, props.flRoomRolloffFactor); break; + case DSPROPERTY_EAX20LISTENER_DECAYTIME: defer(call, props.flDecayTime); break; + case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO: defer(call, props.flDecayHFRatio); break; + case DSPROPERTY_EAX20LISTENER_REFLECTIONS: defer(call, props.lReflections); break; + case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY: defer(call, props.flReverbDelay); break; + case DSPROPERTY_EAX20LISTENER_REVERB: defer(call, props.lReverb); break; + case DSPROPERTY_EAX20LISTENER_REVERBDELAY: defer(call, props.flReverbDelay); break; + case DSPROPERTY_EAX20LISTENER_ENVIRONMENT: defer(call, props, props.dwEnvironment); break; + case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE: defer(call, props, props.flEnvironmentSize); break; + case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION: defer(call, props.flEnvironmentDiffusion); break; + case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF: defer(call, props.flAirAbsorptionHF); break; + case DSPROPERTY_EAX20LISTENER_FLAGS: defer(call, props.dwFlags); break; + default: fail_unknown_property_id(); } } @@ -1369,117 +1205,34 @@ void EaxReverbCommitter::Set(const EaxCall &call, EAXREVERBPROPERTIES &props) { switch(call.get_property_id()) { - case EAXREVERB_NONE: - break; - - case EAXREVERB_ALLPARAMETERS: - defer(call, props); - break; - - case EAXREVERB_ENVIRONMENT: - defer(call, props, props.ulEnvironment); - break; - - case EAXREVERB_ENVIRONMENTSIZE: - defer(call, props, props.flEnvironmentSize); - break; - - case EAXREVERB_ENVIRONMENTDIFFUSION: - defer3(call, props, props.flEnvironmentDiffusion); - break; - - case EAXREVERB_ROOM: - defer3(call, props, props.lRoom); - break; - - case EAXREVERB_ROOMHF: - defer3(call, props, props.lRoomHF); - break; - - case EAXREVERB_ROOMLF: - defer3(call, props, props.lRoomLF); - break; - - case EAXREVERB_DECAYTIME: - defer3(call, props, props.flDecayTime); - break; - - case EAXREVERB_DECAYHFRATIO: - defer3(call, props, props.flDecayHFRatio); - break; - - case EAXREVERB_DECAYLFRATIO: - defer3(call, props, props.flDecayLFRatio); - break; - - case EAXREVERB_REFLECTIONS: - defer3(call, props, props.lReflections); - break; - - case EAXREVERB_REFLECTIONSDELAY: - defer3(call, props, props.flReflectionsDelay); - break; - - case EAXREVERB_REFLECTIONSPAN: - defer3(call, props, props.vReflectionsPan); - break; - - case EAXREVERB_REVERB: - defer3(call, props, props.lReverb); - break; - - case EAXREVERB_REVERBDELAY: - defer3(call, props, props.flReverbDelay); - break; - - case EAXREVERB_REVERBPAN: - defer3(call, props, props.vReverbPan); - break; - - case EAXREVERB_ECHOTIME: - defer3(call, props, props.flEchoTime); - break; - - case EAXREVERB_ECHODEPTH: - defer3(call, props, props.flEchoDepth); - break; - - case EAXREVERB_MODULATIONTIME: - defer3(call, props, props.flModulationTime); - break; - - case EAXREVERB_MODULATIONDEPTH: - defer3(call, props, props.flModulationDepth); - break; - - case EAXREVERB_AIRABSORPTIONHF: - defer3(call, props, props.flAirAbsorptionHF); - break; - - case EAXREVERB_HFREFERENCE: - defer3(call, props, props.flHFReference); - break; - - case EAXREVERB_LFREFERENCE: - defer3(call, props, props.flLFReference); - break; - - case EAXREVERB_ROOMROLLOFFFACTOR: - defer3(call, props, props.flRoomRolloffFactor); - break; - - case EAXREVERB_FLAGS: - defer3(call, props, props.ulFlags); - break; - - default: - fail_unknown_property_id(); + case EAXREVERB_NONE: break; + case EAXREVERB_ALLPARAMETERS: defer(call, props); break; + case EAXREVERB_ENVIRONMENT: defer(call, props, props.ulEnvironment); break; + case EAXREVERB_ENVIRONMENTSIZE: defer(call, props, props.flEnvironmentSize); break; + case EAXREVERB_ENVIRONMENTDIFFUSION: defer3(call, props, props.flEnvironmentDiffusion); break; + case EAXREVERB_ROOM: defer3(call, props, props.lRoom); break; + case EAXREVERB_ROOMHF: defer3(call, props, props.lRoomHF); break; + case EAXREVERB_ROOMLF: defer3(call, props, props.lRoomLF); break; + case EAXREVERB_DECAYTIME: defer3(call, props, props.flDecayTime); break; + case EAXREVERB_DECAYHFRATIO: defer3(call, props, props.flDecayHFRatio); break; + case EAXREVERB_DECAYLFRATIO: defer3(call, props, props.flDecayLFRatio); break; + case EAXREVERB_REFLECTIONS: defer3(call, props, props.lReflections); break; + case EAXREVERB_REFLECTIONSDELAY: defer3(call, props, props.flReflectionsDelay); break; + case EAXREVERB_REFLECTIONSPAN: defer3(call, props, props.vReflectionsPan); break; + case EAXREVERB_REVERB: defer3(call, props, props.lReverb); break; + case EAXREVERB_REVERBDELAY: defer3(call, props, props.flReverbDelay); break; + case EAXREVERB_REVERBPAN: defer3(call, props, props.vReverbPan); break; + case EAXREVERB_ECHOTIME: defer3(call, props, props.flEchoTime); break; + case EAXREVERB_ECHODEPTH: defer3(call, props, props.flEchoDepth); break; + case EAXREVERB_MODULATIONTIME: defer3(call, props, props.flModulationTime); break; + case EAXREVERB_MODULATIONDEPTH: defer3(call, props, props.flModulationDepth); break; + case EAXREVERB_AIRABSORPTIONHF: defer3(call, props, props.flAirAbsorptionHF); break; + case EAXREVERB_HFREFERENCE: defer3(call, props, props.flHFReference); break; + case EAXREVERB_LFREFERENCE: defer3(call, props, props.flLFReference); break; + case EAXREVERB_ROOMROLLOFFFACTOR: defer3(call, props, props.flRoomRolloffFactor); break; + case EAXREVERB_FLAGS: defer3(call, props, props.ulFlags); break; + default: fail_unknown_property_id(); } } -void EaxReverbCommitter::Set(const EaxCall &call, EaxEffectProps &props) -{ - Set(call, std::get(props)); -} - #endif // ALSOFT_EAX diff --git a/3rdparty/openal/al/effects/vmorpher.cpp b/3rdparty/openal/al/effects/vmorpher.cpp index f255122910ba..09bd73d9c4fb 100644 --- a/3rdparty/openal/al/effects/vmorpher.cpp +++ b/3rdparty/openal/al/effects/vmorpher.cpp @@ -7,12 +7,12 @@ #include "AL/al.h" #include "AL/efx.h" -#include "alc/effects/base.h" +#include "core/effects/base.h" #include "effects.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include -#include "alnumeric.h" +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -20,7 +20,7 @@ namespace { -std::optional PhenomeFromEnum(ALenum val) +constexpr std::optional PhenomeFromEnum(ALenum val) noexcept { #define HANDLE_PHENOME(x) case AL_VOCAL_MORPHER_PHONEME_ ## x: \ return VMorpherPhenome::x @@ -60,7 +60,7 @@ std::optional PhenomeFromEnum(ALenum val) return std::nullopt; #undef HANDLE_PHENOME } -ALenum EnumFromPhenome(VMorpherPhenome phenome) +constexpr ALenum EnumFromPhenome(VMorpherPhenome phenome) { #define HANDLE_PHENOME(x) case VMorpherPhenome::x: return AL_VOCAL_MORPHER_PHONEME_ ## x switch(phenome) @@ -100,7 +100,7 @@ ALenum EnumFromPhenome(VMorpherPhenome phenome) #undef HANDLE_PHENOME } -std::optional WaveformFromEmum(ALenum value) +constexpr std::optional WaveformFromEmum(ALenum value) noexcept { switch(value) { @@ -110,7 +110,7 @@ std::optional WaveformFromEmum(ALenum value) } return std::nullopt; } -ALenum EnumFromWaveform(VMorpherWaveform type) +constexpr ALenum EnumFromWaveform(VMorpherWaveform type) { switch(type) { @@ -122,13 +122,29 @@ ALenum EnumFromWaveform(VMorpherWaveform type) std::to_string(static_cast(type))}; } -void Vmorpher_setParami(EffectProps *props, ALenum param, int val) +constexpr EffectProps genDefaultProps() noexcept +{ + VmorpherProps props{}; + props.Rate = AL_VOCAL_MORPHER_DEFAULT_RATE; + props.PhonemeA = PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEA).value(); + props.PhonemeB = PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEB).value(); + props.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING; + props.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING; + props.Waveform = WaveformFromEmum(AL_VOCAL_MORPHER_DEFAULT_WAVEFORM).value(); + return props; +} + +} // namespace + +const EffectProps VmorpherEffectProps{genDefaultProps()}; + +void VmorpherEffectHandler::SetParami(VmorpherProps &props, ALenum param, int val) { switch(param) { case AL_VOCAL_MORPHER_PHONEMEA: if(auto phenomeopt = PhenomeFromEnum(val)) - props->Vmorpher.PhonemeA = *phenomeopt; + props.PhonemeA = *phenomeopt; else throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a out of range: 0x%04x", val}; break; @@ -136,12 +152,12 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val) case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING)) throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a coarse tuning out of range"}; - props->Vmorpher.PhonemeACoarseTuning = val; + props.PhonemeACoarseTuning = val; break; case AL_VOCAL_MORPHER_PHONEMEB: if(auto phenomeopt = PhenomeFromEnum(val)) - props->Vmorpher.PhonemeB = *phenomeopt; + props.PhonemeB = *phenomeopt; else throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b out of range: 0x%04x", val}; break; @@ -149,12 +165,12 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val) case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING)) throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b coarse tuning out of range"}; - props->Vmorpher.PhonemeBCoarseTuning = val; + props.PhonemeBCoarseTuning = val; break; case AL_VOCAL_MORPHER_WAVEFORM: if(auto formopt = WaveformFromEmum(val)) - props->Vmorpher.Waveform = *formopt; + props.Waveform = *formopt; else throw effect_exception{AL_INVALID_VALUE, "Vocal morpher waveform out of range: 0x%04x", val}; break; @@ -164,19 +180,19 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val) param}; } } -void Vmorpher_setParamiv(EffectProps*, ALenum param, const int*) +void VmorpherEffectHandler::SetParamiv(VmorpherProps&, ALenum param, const int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x", param}; } -void Vmorpher_setParamf(EffectProps *props, ALenum param, float val) +void VmorpherEffectHandler::SetParamf(VmorpherProps &props, ALenum param, float val) { switch(param) { case AL_VOCAL_MORPHER_RATE: if(!(val >= AL_VOCAL_MORPHER_MIN_RATE && val <= AL_VOCAL_MORPHER_MAX_RATE)) throw effect_exception{AL_INVALID_VALUE, "Vocal morpher rate out of range"}; - props->Vmorpher.Rate = val; + props.Rate = val; break; default: @@ -184,49 +200,35 @@ void Vmorpher_setParamf(EffectProps *props, ALenum param, float val) param}; } } -void Vmorpher_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Vmorpher_setParamf(props, param, vals[0]); } +void VmorpherEffectHandler::SetParamfv(VmorpherProps &props, ALenum param, const float *vals) +{ SetParamf(props, param, *vals); } -void Vmorpher_getParami(const EffectProps *props, ALenum param, int* val) +void VmorpherEffectHandler::GetParami(const VmorpherProps &props, ALenum param, int* val) { switch(param) { - case AL_VOCAL_MORPHER_PHONEMEA: - *val = EnumFromPhenome(props->Vmorpher.PhonemeA); - break; - - case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: - *val = props->Vmorpher.PhonemeACoarseTuning; - break; - - case AL_VOCAL_MORPHER_PHONEMEB: - *val = EnumFromPhenome(props->Vmorpher.PhonemeB); - break; - - case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: - *val = props->Vmorpher.PhonemeBCoarseTuning; - break; - - case AL_VOCAL_MORPHER_WAVEFORM: - *val = EnumFromWaveform(props->Vmorpher.Waveform); - break; + case AL_VOCAL_MORPHER_PHONEMEA: *val = EnumFromPhenome(props.PhonemeA); break; + case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: *val = props.PhonemeACoarseTuning; break; + case AL_VOCAL_MORPHER_PHONEMEB: *val = EnumFromPhenome(props.PhonemeB); break; + case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: *val = props.PhonemeBCoarseTuning; break; + case AL_VOCAL_MORPHER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break; default: throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x", param}; } } -void Vmorpher_getParamiv(const EffectProps*, ALenum param, int*) +void VmorpherEffectHandler::GetParamiv(const VmorpherProps&, ALenum param, int*) { throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x", param}; } -void Vmorpher_getParamf(const EffectProps *props, ALenum param, float *val) +void VmorpherEffectHandler::GetParamf(const VmorpherProps &props, ALenum param, float *val) { switch(param) { case AL_VOCAL_MORPHER_RATE: - *val = props->Vmorpher.Rate; + *val = props.Rate; break; default: @@ -234,28 +236,11 @@ void Vmorpher_getParamf(const EffectProps *props, ALenum param, float *val) param}; } } -void Vmorpher_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Vmorpher_getParamf(props, param, vals); } +void VmorpherEffectHandler::GetParamfv(const VmorpherProps &props, ALenum param, float *vals) +{ GetParamf(props, param, vals); } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Vmorpher.Rate = AL_VOCAL_MORPHER_DEFAULT_RATE; - props.Vmorpher.PhonemeA = *PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEA); - props.Vmorpher.PhonemeB = *PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEB); - props.Vmorpher.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING; - props.Vmorpher.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING; - props.Vmorpher.Waveform = *WaveformFromEmum(AL_VOCAL_MORPHER_DEFAULT_WAVEFORM); - return props; -} - -} // namespace -DEFINE_ALEFFECT_VTABLE(Vmorpher); - -const EffectProps VmorpherEffectProps{genDefaultProps()}; - -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using VocalMorpherCommitter = EaxCommitter; @@ -352,10 +337,9 @@ template<> throw Exception{message}; } -template<> -bool VocalMorpherCommitter::commit(const EaxEffectProps &props) +bool EaxVocalMorpherCommitter::commit(const EAXVOCALMORPHERPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; @@ -407,19 +391,21 @@ bool VocalMorpherCommitter::commit(const EaxEffectProps &props) return VMorpherWaveform::Sinusoid; }; - auto &eaxprops = std::get(props); - mAlProps.Vmorpher.PhonemeA = get_phoneme(eaxprops.ulPhonemeA); - mAlProps.Vmorpher.PhonemeACoarseTuning = static_cast(eaxprops.lPhonemeACoarseTuning); - mAlProps.Vmorpher.PhonemeB = get_phoneme(eaxprops.ulPhonemeB); - mAlProps.Vmorpher.PhonemeBCoarseTuning = static_cast(eaxprops.lPhonemeBCoarseTuning); - mAlProps.Vmorpher.Waveform = get_waveform(eaxprops.ulWaveform); - mAlProps.Vmorpher.Rate = eaxprops.flRate; + mAlProps = [&]{ + VmorpherProps ret{}; + ret.PhonemeA = get_phoneme(props.ulPhonemeA); + ret.PhonemeACoarseTuning = static_cast(props.lPhonemeACoarseTuning); + ret.PhonemeB = get_phoneme(props.ulPhonemeB); + ret.PhonemeBCoarseTuning = static_cast(props.lPhonemeBCoarseTuning); + ret.Waveform = get_waveform(props.ulWaveform); + ret.Rate = props.flRate; + return ret; + }(); return true; } -template<> -void VocalMorpherCommitter::SetDefaults(EaxEffectProps &props) +void EaxVocalMorpherCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXVOCALMORPHERPROPERTIES defprops{[] { @@ -435,87 +421,35 @@ void VocalMorpherCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void VocalMorpherCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxVocalMorpherCommitter::Get(const EaxCall &call, const EAXVOCALMORPHERPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { - case EAXVOCALMORPHER_NONE: - break; - - case EAXVOCALMORPHER_ALLPARAMETERS: - call.set_value(props); - break; - - case EAXVOCALMORPHER_PHONEMEA: - call.set_value(props.ulPhonemeA); - break; - - case EAXVOCALMORPHER_PHONEMEACOARSETUNING: - call.set_value(props.lPhonemeACoarseTuning); - break; - - case EAXVOCALMORPHER_PHONEMEB: - call.set_value(props.ulPhonemeB); - break; - - case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: - call.set_value(props.lPhonemeBCoarseTuning); - break; - - case EAXVOCALMORPHER_WAVEFORM: - call.set_value(props.ulWaveform); - break; - - case EAXVOCALMORPHER_RATE: - call.set_value(props.flRate); - break; - - default: - fail_unknown_property_id(); + case EAXVOCALMORPHER_NONE: break; + case EAXVOCALMORPHER_ALLPARAMETERS: call.set_value(props); break; + case EAXVOCALMORPHER_PHONEMEA: call.set_value(props.ulPhonemeA); break; + case EAXVOCALMORPHER_PHONEMEACOARSETUNING: call.set_value(props.lPhonemeACoarseTuning); break; + case EAXVOCALMORPHER_PHONEMEB: call.set_value(props.ulPhonemeB); break; + case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: call.set_value(props.lPhonemeBCoarseTuning); break; + case EAXVOCALMORPHER_WAVEFORM: call.set_value(props.ulWaveform); break; + case EAXVOCALMORPHER_RATE: call.set_value(props.flRate); break; + default: fail_unknown_property_id(); } } -template<> -void VocalMorpherCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxVocalMorpherCommitter::Set(const EaxCall &call, EAXVOCALMORPHERPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { - case EAXVOCALMORPHER_NONE: - break; - - case EAXVOCALMORPHER_ALLPARAMETERS: - defer(call, props); - break; - - case EAXVOCALMORPHER_PHONEMEA: - defer(call, props.ulPhonemeA); - break; - - case EAXVOCALMORPHER_PHONEMEACOARSETUNING: - defer(call, props.lPhonemeACoarseTuning); - break; - - case EAXVOCALMORPHER_PHONEMEB: - defer(call, props.ulPhonemeB); - break; - - case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: - defer(call, props.lPhonemeBCoarseTuning); - break; - - case EAXVOCALMORPHER_WAVEFORM: - defer(call, props.ulWaveform); - break; - - case EAXVOCALMORPHER_RATE: - defer(call, props.flRate); - break; - - default: - fail_unknown_property_id(); + case EAXVOCALMORPHER_NONE: break; + case EAXVOCALMORPHER_ALLPARAMETERS: defer(call, props); break; + case EAXVOCALMORPHER_PHONEMEA: defer(call, props.ulPhonemeA); break; + case EAXVOCALMORPHER_PHONEMEACOARSETUNING: defer(call, props.lPhonemeACoarseTuning); break; + case EAXVOCALMORPHER_PHONEMEB: defer(call, props.ulPhonemeB); break; + case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: defer(call, props.lPhonemeBCoarseTuning); break; + case EAXVOCALMORPHER_WAVEFORM: defer(call, props.ulWaveform); break; + case EAXVOCALMORPHER_RATE: defer(call, props.flRate); break; + default: fail_unknown_property_id(); } } diff --git a/3rdparty/openal/al/error.cpp b/3rdparty/openal/al/error.cpp index c23594770b18..f852d5ed4187 100644 --- a/3rdparty/openal/al/error.cpp +++ b/3rdparty/openal/al/error.cpp @@ -20,19 +20,22 @@ #include "config.h" +#include "error.h" + #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #endif -#include #include #include #include #include #include #include -#include +#include +#include +#include #include #include "AL/al.h" @@ -41,22 +44,30 @@ #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 "strutils.h" -bool TrapALError{false}; +namespace al { +context_error::context_error(ALenum code, const char *msg, ...) : mErrorCode{code} +{ + /* NOLINTBEGIN(*-array-to-pointer-decay) */ + std::va_list args; + va_start(args, msg); + setMessage(msg, args); + va_end(args); + /* NOLINTEND(*-array-to-pointer-decay) */ +} +context_error::~context_error() = default; +} /* namespace al */ void ALCcontext::setError(ALenum errorCode, const char *msg, ...) { auto message = std::vector(256); - va_list args, args2; + /* NOLINTBEGIN(*-array-to-pointer-decay) */ + std::va_list args, args2; va_start(args, msg); va_copy(args2, args); int msglen{std::vsnprintf(message.data(), message.size(), msg, args)}; @@ -67,6 +78,7 @@ void ALCcontext::setError(ALenum errorCode, const char *msg, ...) } va_end(args2); va_end(args); + /* NOLINTEND(*-array-to-pointer-decay) */ if(msglen >= 0) msg = message.data(); @@ -89,54 +101,55 @@ void ALCcontext::setError(ALenum errorCode, const char *msg, ...) #endif } - ALenum curerr{AL_NO_ERROR}; - mLastError.compare_exchange_strong(curerr, errorCode); + if(mLastThreadError.get() == AL_NO_ERROR) + mLastThreadError.set(errorCode); - debugMessage(DebugSource::API, DebugType::Error, 0, DebugSeverity::High, - {msg, static_cast(msglen)}); + debugMessage(DebugSource::API, DebugType::Error, static_cast(errorCode), + DebugSeverity::High, {msg, static_cast(msglen)}); } /* 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 +AL_API auto AL_APIENTRY alGetError() noexcept -> ALenum { - auto context = GetContextRef(); - if(!context) UNLIKELY + if(auto context = GetContextRef()) LIKELY + return alGetErrorDirect(context.get()); + + auto get_value = [](const char *envname, const char *optname) -> ALenum { - 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::max()) - return static_cast(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) + auto optstr = al::getenv(envname); + if(!optstr) + optstr = ConfigValueStr({}, "game_compat", optname); + if(optstr) { + char *end{}; + auto value = std::strtoul(optstr->c_str(), &end, 0); + if(end && *end == '\0' && value <= std::numeric_limits::max()) + return static_cast(value); + ERR("Invalid default error value: \"%s\"", optstr->c_str()); + } + return AL_INVALID_OPERATION; + }; + static const ALenum deferror{get_value("__ALSOFT_DEFAULT_ERROR", "default-error")}; + + WARN("Querying error state on null context (implicitly 0x%04x)\n", deferror); + if(TrapALError) + { #ifdef _WIN32 - if(IsDebuggerPresent()) - DebugBreak(); + if(IsDebuggerPresent()) + DebugBreak(); #elif defined(SIGTRAP) - raise(SIGTRAP); + raise(SIGTRAP); #endif - } - return deferror; } - return alGetErrorDirect(context.get()); + return deferror; } FORCE_ALIGN ALenum AL_APIENTRY alGetErrorDirect(ALCcontext *context) noexcept { - return context->mLastError.exchange(AL_NO_ERROR); + ALenum ret{context->mLastThreadError.get()}; + if(ret != AL_NO_ERROR) UNLIKELY + context->mLastThreadError.set(AL_NO_ERROR); + return ret; } diff --git a/3rdparty/openal/al/error.h b/3rdparty/openal/al/error.h new file mode 100644 index 000000000000..c50011a54143 --- /dev/null +++ b/3rdparty/openal/al/error.h @@ -0,0 +1,27 @@ +#ifndef AL_ERROR_H +#define AL_ERROR_H + +#include "AL/al.h" + +#include "core/except.h" + +namespace al { + +class context_error final : public al::base_exception { + ALenum mErrorCode{}; + +public: +#ifdef __MINGW32__ + [[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]] +#else + [[gnu::format(printf, 3, 4)]] +#endif + context_error(ALenum code, const char *msg, ...); + ~context_error() final; + + [[nodiscard]] auto errorCode() const noexcept -> ALenum { return mErrorCode; } +}; + +} /* namespace al */ + +#endif /* AL_ERROR_H */ diff --git a/3rdparty/openal/al/event.cpp b/3rdparty/openal/al/event.cpp index 8b76ceffde8f..7ceb949c6c22 100644 --- a/3rdparty/openal/al/event.cpp +++ b/3rdparty/openal/al/event.cpp @@ -3,32 +3,34 @@ #include "event.h" -#include #include -#include +#include #include #include #include #include #include #include -#include #include -#include +#include +#include #include "AL/al.h" #include "AL/alc.h" +#include "AL/alext.h" #include "alc/context.h" -#include "alc/effects/base.h" -#include "alc/inprogext.h" -#include "almalloc.h" +#include "alsem.h" +#include "alspan.h" +#include "alstring.h" #include "core/async_event.h" -#include "core/except.h" +#include "core/context.h" +#include "core/effects/base.h" #include "core/logging.h" -#include "core/voice_change.h" #include "debug.h" #include "direct_defs.h" +#include "error.h" +#include "intrusive_ptr.h" #include "opthelpers.h" #include "ringbuffer.h" @@ -47,27 +49,22 @@ int EventThread(ALCcontext *context) bool quitnow{false}; while(!quitnow) { - auto evt_data = ring->getReadVector().first; + auto evt_data = ring->getReadVector()[0]; if(evt_data.len == 0) { context->mEventSem.wait(); continue; } - std::lock_guard _{context->mEventCbLock}; - do { - auto *evt_ptr = std::launder(reinterpret_cast(evt_data.buf)); - evt_data.buf += sizeof(AsyncEvent); - evt_data.len -= 1; - - AsyncEvent event{std::move(*evt_ptr)}; - std::destroy_at(evt_ptr); - ring->readAdvance(1); - + auto eventlock = std::lock_guard{context->mEventCbLock}; + const auto enabledevts = context->mEnabledEvts.load(std::memory_order_acquire); + auto evt_span = al::span{std::launder(reinterpret_cast(evt_data.buf)), + evt_data.len}; + for(auto &event : evt_span) + { quitnow = std::holds_alternative(event); if(quitnow) UNLIKELY break; - auto enabledevts = context->mEnabledEvts.load(std::memory_order_acquire); auto proc_killthread = [](AsyncKillThread&) { }; auto proc_release = [](AsyncEffectReleaseEvent &evt) { @@ -102,7 +99,7 @@ int EventThread(ALCcontext *context) break; } context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.mId, state, - static_cast(msg.length()), msg.c_str(), context->mEventParam); + al::sizei(msg), msg.c_str(), context->mEventParam); }; auto proc_buffercomp = [context,enabledevts](AsyncBufferCompleteEvent &evt) { @@ -114,25 +111,23 @@ int EventThread(ALCcontext *context) if(evt.mCount == 1) msg += " buffer completed"; else msg += " buffers completed"; context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.mId, evt.mCount, - static_cast(msg.length()), msg.c_str(), context->mEventParam); + al::sizei(msg), msg.c_str(), context->mEventParam); }; auto proc_disconnect = [context,enabledevts](AsyncDisconnectEvent &evt) { - 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))) + return; - if(context->mEventCb - && enabledevts.test(al::to_underlying(AsyncEnableBits::Disconnected))) - context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0, - static_cast(message.length()), message.data(), - context->mEventParam); + context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0, al::sizei(evt.msg), + evt.msg.c_str(), context->mEventParam); }; std::visit(overloaded{proc_srcstate, proc_buffercomp, proc_release, proc_disconnect, proc_killthread}, event); - } while(evt_data.len != 0); + } + std::destroy(evt_span.begin(), evt_span.end()); + ring->readAdvance(evt_span.size()); } return 0; } @@ -167,12 +162,12 @@ void StartEventThrd(ALCcontext *ctx) void StopEventThrd(ALCcontext *ctx) { RingBuffer *ring{ctx->mAsyncEvents.get()}; - auto evt_data = ring->getWriteVector().first; + auto evt_data = ring->getWriteVector()[0]; if(evt_data.len == 0) { do { std::this_thread::yield(); - evt_data = ring->getWriteVector().first; + evt_data = ring->getWriteVector()[0]; } while(evt_data.len == 0); } std::ignore = InitAsyncEvent(evt_data.buf); @@ -183,20 +178,23 @@ void StopEventThrd(ALCcontext *ctx) ctx->mEventThread.join(); } -AL_API DECL_FUNCEXT3(void, alEventControl,SOFT, ALsizei, const ALenum*, ALboolean) +AL_API DECL_FUNCEXT3(void, alEventControl,SOFT, ALsizei,count, const ALenum*,types, ALboolean,enable) FORCE_ALIGN void AL_APIENTRY alEventControlDirectSOFT(ALCcontext *context, ALsizei count, const ALenum *types, ALboolean enable) noexcept -{ - 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"); +try { + if(count < 0) + throw al::context_error{AL_INVALID_VALUE, "Controlling %d events", count}; + if(count <= 0) UNLIKELY return; + + if(!types) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; ContextBase::AsyncEventBitset flags{}; for(ALenum evttype : al::span{types, static_cast(count)}) { auto etype = GetEventType(evttype); if(!etype) - return context->setError(AL_INVALID_ENUM, "Invalid event type 0x%04x", evttype); + throw al::context_error{AL_INVALID_ENUM, "Invalid event type 0x%04x", evttype}; flags.set(al::to_underlying(*etype)); } @@ -221,15 +219,18 @@ FORCE_ALIGN void AL_APIENTRY alEventControlDirectSOFT(ALCcontext *context, ALsiz /* Wait to ensure the event handler sees the changed flags before * returning. */ - std::lock_guard _{context->mEventCbLock}; + std::lock_guard eventlock{context->mEventCbLock}; } } +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); +} -AL_API DECL_FUNCEXT2(void, alEventCallback,SOFT, ALEVENTPROCSOFT, void*) +AL_API DECL_FUNCEXT2(void, alEventCallback,SOFT, ALEVENTPROCSOFT,callback, void*,userParam) FORCE_ALIGN void AL_APIENTRY alEventCallbackDirectSOFT(ALCcontext *context, ALEVENTPROCSOFT callback, void *userParam) noexcept { - std::lock_guard _{context->mEventCbLock}; + std::lock_guard eventlock{context->mEventCbLock}; context->mEventCb = callback; context->mEventParam = userParam; } diff --git a/3rdparty/openal/al/extension.cpp b/3rdparty/openal/al/extension.cpp index 6d1ac3275b61..061addc84625 100644 --- a/3rdparty/openal/al/extension.cpp +++ b/3rdparty/openal/al/extension.cpp @@ -20,21 +20,18 @@ #include "config.h" -#include -#include -#include +#include #include "AL/al.h" #include "AL/alc.h" #include "alc/context.h" #include "alstring.h" -#include "core/except.h" #include "direct_defs.h" #include "opthelpers.h" -AL_API DECL_FUNC1(ALboolean, alIsExtensionPresent, const ALchar*) +AL_API DECL_FUNC1(ALboolean, alIsExtensionPresent, const ALchar*,extName) FORCE_ALIGN ALboolean AL_APIENTRY alIsExtensionPresentDirect(ALCcontext *context, const ALchar *extName) noexcept { if(!extName) UNLIKELY @@ -43,10 +40,10 @@ FORCE_ALIGN ALboolean AL_APIENTRY alIsExtensionPresentDirect(ALCcontext *context return AL_FALSE; } - size_t len{strlen(extName)}; + const std::string_view tofind{extName}; for(std::string_view ext : context->mExtensions) { - if(len == ext.length() && al::strncasecmp(ext.data(), extName, len) == 0) + if(al::case_compare(ext, tofind) == 0) return AL_TRUE; } @@ -68,12 +65,12 @@ FORCE_ALIGN ALvoid* AL_APIENTRY alGetProcAddressDirect(ALCcontext*, const ALchar AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName) noexcept { - if(!enumName) return static_cast(0); + if(!enumName) return ALenum{0}; return alcGetEnumValue(nullptr, enumName); } FORCE_ALIGN ALenum AL_APIENTRY alGetEnumValueDirect(ALCcontext*, const ALchar *enumName) noexcept { - if(!enumName) return static_cast(0); + if(!enumName) return ALenum{0}; return alcGetEnumValue(nullptr, enumName); } diff --git a/3rdparty/openal/al/filter.cpp b/3rdparty/openal/al/filter.cpp index f0a078b7a6fa..4ef6b9583201 100644 --- a/3rdparty/openal/al/filter.cpp +++ b/3rdparty/openal/al/filter.cpp @@ -29,8 +29,8 @@ #include #include #include -#include #include +#include #include #include "AL/al.h" @@ -42,36 +42,16 @@ #include "alc/device.h" #include "almalloc.h" #include "alnumeric.h" -#include "core/except.h" +#include "alspan.h" #include "direct_defs.h" +#include "error.h" +#include "intrusive_ptr.h" #include "opthelpers.h" namespace { -class filter_exception final : public al::base_exception { - ALenum mErrorCode; - -public: -#ifdef __USE_MINGW_ANSI_STDIO - [[gnu::format(gnu_printf, 3, 4)]] -#else - [[gnu::format(printf, 3, 4)]] -#endif - filter_exception(ALenum code, const char *msg, ...); - ~filter_exception() override; - - ALenum errorCode() const noexcept { return mErrorCode; } -}; - -filter_exception::filter_exception(ALenum code, const char* msg, ...) : mErrorCode{code} -{ - std::va_list args; - va_start(args, msg); - setMessage(msg, args); - va_end(args); -} -filter_exception::~filter_exception() = default; +using SubListAllocator = al::allocator>; void InitFilterParams(ALfilter *filter, ALenum type) @@ -80,43 +60,43 @@ void InitFilterParams(ALfilter *filter, ALenum type) { filter->Gain = AL_LOWPASS_DEFAULT_GAIN; filter->GainHF = AL_LOWPASS_DEFAULT_GAINHF; - filter->HFReference = LOWPASSFREQREF; + filter->HFReference = LowPassFreqRef; filter->GainLF = 1.0f; - filter->LFReference = HIGHPASSFREQREF; + filter->LFReference = HighPassFreqRef; filter->mTypeVariant.emplace(); } else if(type == AL_FILTER_HIGHPASS) { filter->Gain = AL_HIGHPASS_DEFAULT_GAIN; filter->GainHF = 1.0f; - filter->HFReference = LOWPASSFREQREF; + filter->HFReference = LowPassFreqRef; filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF; - filter->LFReference = HIGHPASSFREQREF; + filter->LFReference = HighPassFreqRef; filter->mTypeVariant.emplace(); } else if(type == AL_FILTER_BANDPASS) { filter->Gain = AL_BANDPASS_DEFAULT_GAIN; filter->GainHF = AL_BANDPASS_DEFAULT_GAINHF; - filter->HFReference = LOWPASSFREQREF; + filter->HFReference = LowPassFreqRef; filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF; - filter->LFReference = HIGHPASSFREQREF; + filter->LFReference = HighPassFreqRef; filter->mTypeVariant.emplace(); } else { filter->Gain = 1.0f; filter->GainHF = 1.0f; - filter->HFReference = LOWPASSFREQREF; + filter->HFReference = LowPassFreqRef; filter->GainLF = 1.0f; - filter->LFReference = HIGHPASSFREQREF; + filter->LFReference = HighPassFreqRef; filter->mTypeVariant.emplace(); } filter->type = type; } -bool EnsureFilters(ALCdevice *device, size_t needed) -{ +auto EnsureFilters(al::Device *device, size_t needed) noexcept -> bool +try { 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(al::popcount(sublist.FreeMask)); })}; @@ -126,22 +106,20 @@ bool EnsureFilters(ALCdevice *device, size_t needed) 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(al_calloc(alignof(ALfilter), sizeof(ALfilter)*64)); - if(!sublist->Filters) UNLIKELY - { - device->FilterList.pop_back(); - return false; - } - count += 64; + FilterSubList sublist{}; + sublist.FreeMask = ~0_u64; + sublist.Filters = SubListAllocator{}.allocate(1); + device->FilterList.emplace_back(std::move(sublist)); + count += std::tuple_size_v; } return true; } +catch(...) { + return false; +} -ALfilter *AllocFilter(ALCdevice *device) +auto AllocFilter(al::Device *device) noexcept -> ALfilter* { auto sublist = std::find_if(device->FilterList.begin(), device->FilterList.end(), [](const FilterSubList &entry) noexcept -> bool @@ -150,7 +128,7 @@ ALfilter *AllocFilter(ALCdevice *device) auto slidx = static_cast(al::countr_zero(sublist->FreeMask)); ASSUME(slidx < 64); - ALfilter *filter{al::construct_at(sublist->Filters + slidx)}; + ALfilter *filter{al::construct_at(al::to_address(sublist->Filters->begin() + slidx))}; InitFilterParams(filter, AL_FILTER_NULL); /* Add 1 to avoid filter ID 0. */ @@ -161,7 +139,7 @@ ALfilter *AllocFilter(ALCdevice *device) return filter; } -void FreeFilter(ALCdevice *device, ALfilter *filter) +void FreeFilter(al::Device *device, ALfilter *filter) { device->mFilterNames.erase(filter->id); @@ -175,7 +153,7 @@ void FreeFilter(ALCdevice *device, ALfilter *filter) } -inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) +auto LookupFilter(al::Device *device, ALuint id) noexcept -> ALfilter* { const size_t lidx{(id-1) >> 6}; const ALuint slidx{(id-1) & 0x3f}; @@ -185,7 +163,7 @@ inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) FilterSubList &sublist = device->FilterList[lidx]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Filters + slidx; + return al::to_address(sublist.Filters->begin() + slidx); } } // namespace @@ -193,36 +171,36 @@ inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) /* Null filter parameter handlers */ template<> void FilterTable::setParami(ALfilter*, ALenum param, int) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } template<> void FilterTable::setParamiv(ALfilter*, ALenum param, const int*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } template<> void FilterTable::setParamf(ALfilter*, ALenum param, float) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } template<> void FilterTable::setParamfv(ALfilter*, ALenum param, const float*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } template<> void FilterTable::getParami(const ALfilter*, ALenum param, int*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } template<> void FilterTable::getParamiv(const ALfilter*, ALenum param, int*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } template<> void FilterTable::getParamf(const ALfilter*, ALenum param, float*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } template<> void FilterTable::getParamfv(const ALfilter*, ALenum param, float*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } /* Lowpass parameter handlers */ template<> void FilterTable::setParami(ALfilter*, ALenum param, int) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; } +{ throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; } template<> void FilterTable::setParamiv(ALfilter *filter, ALenum param, const int *values) -{ setParami(filter, param, values[0]); } +{ setParami(filter, param, *values); } template<> void FilterTable::setParamf(ALfilter *filter, ALenum param, float val) { @@ -230,26 +208,24 @@ void FilterTable::setParamf(ALfilter *filter, ALenum param, { case AL_LOWPASS_GAIN: if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN)) - throw filter_exception{AL_INVALID_VALUE, "Low-pass gain %f out of range", val}; + throw al::context_error{AL_INVALID_VALUE, "Low-pass gain %f out of range", val}; filter->Gain = val; - break; + return; case AL_LOWPASS_GAINHF: if(!(val >= AL_LOWPASS_MIN_GAINHF && val <= AL_LOWPASS_MAX_GAINHF)) - throw filter_exception{AL_INVALID_VALUE, "Low-pass gainhf %f out of range", val}; + throw al::context_error{AL_INVALID_VALUE, "Low-pass gainhf %f out of range", val}; filter->GainHF = val; - break; - - default: - throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param}; + return; } + throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param}; } template<> void FilterTable::setParamfv(ALfilter *filter, ALenum param, const float *vals) -{ setParamf(filter, param, vals[0]); } +{ setParamf(filter, param, *vals); } template<> void FilterTable::getParami(const ALfilter*, ALenum param, int*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; } +{ throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; } template<> void FilterTable::getParamiv(const ALfilter *filter, ALenum param, int *values) { getParami(filter, param, values); } @@ -258,17 +234,10 @@ void FilterTable::getParamf(const ALfilter *filter, ALenum p { switch(param) { - case AL_LOWPASS_GAIN: - *val = filter->Gain; - break; - - case AL_LOWPASS_GAINHF: - *val = filter->GainHF; - break; - - default: - throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param}; + case AL_LOWPASS_GAIN: *val = filter->Gain; return; + case AL_LOWPASS_GAINHF: *val = filter->GainHF; return; } + throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param}; } template<> void FilterTable::getParamfv(const ALfilter *filter, ALenum param, float *vals) @@ -277,10 +246,10 @@ void FilterTable::getParamfv(const ALfilter *filter, ALenum /* Highpass parameter handlers */ template<> void FilterTable::setParami(ALfilter*, ALenum param, int) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; } +{ throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; } template<> void FilterTable::setParamiv(ALfilter *filter, ALenum param, const int *values) -{ setParami(filter, param, values[0]); } +{ setParami(filter, param, *values); } template<> void FilterTable::setParamf(ALfilter *filter, ALenum param, float val) { @@ -288,26 +257,24 @@ void FilterTable::setParamf(ALfilter *filter, ALenum param, { case AL_HIGHPASS_GAIN: if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN)) - throw filter_exception{AL_INVALID_VALUE, "High-pass gain %f out of range", val}; + throw al::context_error{AL_INVALID_VALUE, "High-pass gain %f out of range", val}; filter->Gain = val; - break; + return; case AL_HIGHPASS_GAINLF: if(!(val >= AL_HIGHPASS_MIN_GAINLF && val <= AL_HIGHPASS_MAX_GAINLF)) - throw filter_exception{AL_INVALID_VALUE, "High-pass gainlf %f out of range", val}; + throw al::context_error{AL_INVALID_VALUE, "High-pass gainlf %f out of range", val}; filter->GainLF = val; - break; - - default: - throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param}; + return; } + throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param}; } template<> void FilterTable::setParamfv(ALfilter *filter, ALenum param, const float *vals) -{ setParamf(filter, param, vals[0]); } +{ setParamf(filter, param, *vals); } template<> void FilterTable::getParami(const ALfilter*, ALenum param, int*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; } +{ throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; } template<> void FilterTable::getParamiv(const ALfilter *filter, ALenum param, int *values) { getParami(filter, param, values); } @@ -316,17 +283,10 @@ void FilterTable::getParamf(const ALfilter *filter, ALenum { switch(param) { - case AL_HIGHPASS_GAIN: - *val = filter->Gain; - break; - - case AL_HIGHPASS_GAINLF: - *val = filter->GainLF; - break; - - default: - throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param}; + case AL_HIGHPASS_GAIN: *val = filter->Gain; return; + case AL_HIGHPASS_GAINLF: *val = filter->GainLF; return; } + throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param}; } template<> void FilterTable::getParamfv(const ALfilter *filter, ALenum param, float *vals) @@ -335,10 +295,10 @@ void FilterTable::getParamfv(const ALfilter *filter, ALenum /* Bandpass parameter handlers */ template<> void FilterTable::setParami(ALfilter*, ALenum param, int) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; } +{ throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; } template<> void FilterTable::setParamiv(ALfilter *filter, ALenum param, const int *values) -{ setParami(filter, param, values[0]); } +{ setParami(filter, param, *values); } template<> void FilterTable::setParamf(ALfilter *filter, ALenum param, float val) { @@ -346,32 +306,30 @@ void FilterTable::setParamf(ALfilter *filter, ALenum param, { case AL_BANDPASS_GAIN: if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN)) - throw filter_exception{AL_INVALID_VALUE, "Band-pass gain %f out of range", val}; + throw al::context_error{AL_INVALID_VALUE, "Band-pass gain %f out of range", val}; filter->Gain = val; - break; + return; case AL_BANDPASS_GAINHF: if(!(val >= AL_BANDPASS_MIN_GAINHF && val <= AL_BANDPASS_MAX_GAINHF)) - throw filter_exception{AL_INVALID_VALUE, "Band-pass gainhf %f out of range", val}; + throw al::context_error{AL_INVALID_VALUE, "Band-pass gainhf %f out of range", val}; filter->GainHF = val; - break; + return; case AL_BANDPASS_GAINLF: if(!(val >= AL_BANDPASS_MIN_GAINLF && val <= AL_BANDPASS_MAX_GAINLF)) - throw filter_exception{AL_INVALID_VALUE, "Band-pass gainlf %f out of range", val}; + throw al::context_error{AL_INVALID_VALUE, "Band-pass gainlf %f out of range", val}; filter->GainLF = val; - break; - - default: - throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param}; + return; } + throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param}; } template<> void FilterTable::setParamfv(ALfilter *filter, ALenum param, const float *vals) -{ setParamf(filter, param, vals[0]); } +{ setParamf(filter, param, *vals); } template<> void FilterTable::getParami(const ALfilter*, ALenum param, int*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; } +{ throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; } template<> void FilterTable::getParamiv(const ALfilter *filter, ALenum param, int *values) { getParami(filter, param, values); } @@ -380,232 +338,204 @@ void FilterTable::getParamf(const ALfilter *filter, ALenum { switch(param) { - case AL_BANDPASS_GAIN: - *val = filter->Gain; - break; - - case AL_BANDPASS_GAINHF: - *val = filter->GainHF; - break; - - case AL_BANDPASS_GAINLF: - *val = filter->GainLF; - break; - - default: - throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param}; + case AL_BANDPASS_GAIN: *val = filter->Gain; return; + case AL_BANDPASS_GAINHF: *val = filter->GainHF; return; + case AL_BANDPASS_GAINLF: *val = filter->GainLF; return; } + throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param}; } template<> void FilterTable::getParamfv(const ALfilter *filter, ALenum param, float *vals) { getParamf(filter, param, vals); } -AL_API DECL_FUNC2(void, alGenFilters, ALsizei, ALuint*) +AL_API DECL_FUNC2(void, alGenFilters, ALsizei,n, ALuint*,filters) FORCE_ALIGN void AL_APIENTRY alGenFiltersDirect(ALCcontext *context, ALsizei n, ALuint *filters) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Generating %d filters", n); +try { + if(n < 0) + throw al::context_error{AL_INVALID_VALUE, "Generating %d filters", n}; if(n <= 0) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; - if(!EnsureFilters(device, static_cast(n))) - { - context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d filter%s", n, (n==1)?"":"s"); - return; - } + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; - if(n == 1) LIKELY - { - /* Special handling for the easy and normal case. */ - ALfilter *filter{AllocFilter(device)}; - if(filter) filters[0] = filter->id; - } - else - { - /* Store the allocated buffer IDs in a separate local list, to avoid - * modifying the user storage in case of failure. - */ - std::vector ids; - ids.reserve(static_cast(n)); - do { - ALfilter *filter{AllocFilter(device)}; - ids.emplace_back(filter->id); - } while(--n); - std::copy(ids.begin(), ids.end(), filters); - } + const al::span fids{filters, static_cast(n)}; + if(!EnsureFilters(device, fids.size())) + throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d filter%s", n, + (n == 1) ? "" : "s"}; + + std::generate(fids.begin(), fids.end(), [device]{ return AllocFilter(device)->id; }); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC2(void, alDeleteFilters, ALsizei, const ALuint*) +AL_API DECL_FUNC2(void, alDeleteFilters, ALsizei,n, const ALuint*,filters) FORCE_ALIGN void AL_APIENTRY alDeleteFiltersDirect(ALCcontext *context, ALsizei n, const ALuint *filters) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Deleting %d filters", n); +try { + if(n < 0) + throw al::context_error{AL_INVALID_VALUE, "Deleting %d filters", n}; if(n <= 0) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; /* First try to find any filters that are invalid. */ auto validate_filter = [device](const ALuint fid) -> bool { return !fid || LookupFilter(device, fid) != nullptr; }; - const ALuint *filters_end = filters + n; - auto invflt = std::find_if_not(filters, filters_end, validate_filter); - if(invflt != filters_end) UNLIKELY - { - context->setError(AL_INVALID_NAME, "Invalid filter ID %u", *invflt); - return; - } + const al::span fids{filters, static_cast(n)}; + auto invflt = std::find_if_not(fids.begin(), fids.end(), validate_filter); + if(invflt != fids.end()) + throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", *invflt}; /* All good. Delete non-0 filter IDs. */ auto delete_filter = [device](const ALuint fid) -> void { - ALfilter *filter{fid ? LookupFilter(device, fid) : nullptr}; - if(filter) FreeFilter(device, filter); + if(ALfilter *filter{fid ? LookupFilter(device, fid) : nullptr}) + FreeFilter(device, filter); }; - std::for_each(filters, filters_end, delete_filter); + std::for_each(fids.begin(), fids.end(), delete_filter); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC1(ALboolean, alIsFilter, ALuint) +AL_API DECL_FUNC1(ALboolean, alIsFilter, ALuint,filter) FORCE_ALIGN ALboolean AL_APIENTRY alIsFilterDirect(ALCcontext *context, ALuint filter) noexcept { - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; if(!filter || LookupFilter(device, filter)) return AL_TRUE; return AL_FALSE; } -AL_API DECL_FUNC3(void, alFilteri, ALuint, ALenum, ALint) +AL_API DECL_FUNC3(void, alFilteri, ALuint,filter, ALenum,param, ALint,value) FORCE_ALIGN void AL_APIENTRY alFilteriDirect(ALCcontext *context, ALuint filter, ALenum param, ALint value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; +try { + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; ALfilter *alfilt{LookupFilter(device, filter)}; - if(!alfilt) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter); - else 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 + if(!alfilt) + throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter}; + + switch(param) { - /* 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()); + case AL_FILTER_TYPE: + if(!(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS + || value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS)) + throw al::context_error{AL_INVALID_VALUE, "Invalid filter type 0x%04x", value}; + InitFilterParams(alfilt, value); + return; } + + /* Call the appropriate handler */ + std::visit([alfilt,param,value](auto&& thunk){thunk.setParami(alfilt, param, value);}, + alfilt->mTypeVariant); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alFilteriv, ALuint, ALenum, const ALint*) +AL_API DECL_FUNC3(void, alFilteriv, ALuint,filter, ALenum,param, const ALint*,values) FORCE_ALIGN void AL_APIENTRY alFilterivDirect(ALCcontext *context, ALuint filter, ALenum param, const ALint *values) noexcept -{ +try { switch(param) { case AL_FILTER_TYPE: - alFilteriDirect(context, filter, param, values[0]); + alFilteriDirect(context, filter, param, *values); return; } - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; ALfilter *alfilt{LookupFilter(device, filter)}; - if(!alfilt) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter); - else try - { - /* Call the appropriate handler */ - 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()); - } + if(!alfilt) + throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter}; + + /* Call the appropriate handler */ + std::visit([alfilt,param,values](auto&& thunk){thunk.setParamiv(alfilt, param, values);}, + alfilt->mTypeVariant); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alFilterf, ALuint, ALenum, ALfloat) +AL_API DECL_FUNC3(void, alFilterf, ALuint,filter, ALenum,param, ALfloat,value) FORCE_ALIGN void AL_APIENTRY alFilterfDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; +try { + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; ALfilter *alfilt{LookupFilter(device, filter)}; - if(!alfilt) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter); - else try - { - /* Call the appropriate handler */ - 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()); - } + if(!alfilt) + throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter}; + + /* Call the appropriate handler */ + std::visit([alfilt,param,value](auto&& thunk){thunk.setParamf(alfilt, param, value);}, + alfilt->mTypeVariant); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alFilterfv, ALuint, ALenum, const ALfloat*) +AL_API DECL_FUNC3(void, alFilterfv, ALuint,filter, ALenum,param, const ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param, const ALfloat *values) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; +try { + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; ALfilter *alfilt{LookupFilter(device, filter)}; - if(!alfilt) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter); - else try - { - /* Call the appropriate handler */ - 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()); - } + if(!alfilt) + throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter}; + + /* Call the appropriate handler */ + std::visit([alfilt,param,values](auto&& thunk){thunk.setParamfv(alfilt, param, values);}, + alfilt->mTypeVariant); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alGetFilteri, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetFilteri, ALuint,filter, ALenum,param, ALint*,value) FORCE_ALIGN void AL_APIENTRY alGetFilteriDirect(ALCcontext *context, ALuint filter, ALenum param, ALint *value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; +try { + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; const ALfilter *alfilt{LookupFilter(device, filter)}; - if(!alfilt) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter); - else if(param == AL_FILTER_TYPE) - *value = alfilt->type; - else try + if(!alfilt) + throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter}; + + switch(param) { - /* 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()); + case AL_FILTER_TYPE: + *value = alfilt->type; + return; } + + /* Call the appropriate handler */ + std::visit([alfilt,param,value](auto&& thunk){thunk.getParami(alfilt, param, value);}, + alfilt->mTypeVariant); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alGetFilteriv, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetFilteriv, ALuint,filter, ALenum,param, ALint*,values) FORCE_ALIGN void AL_APIENTRY alGetFilterivDirect(ALCcontext *context, ALuint filter, ALenum param, ALint *values) noexcept -{ +try { switch(param) { case AL_FILTER_TYPE: @@ -613,74 +543,68 @@ FORCE_ALIGN void AL_APIENTRY alGetFilterivDirect(ALCcontext *context, ALuint fil return; } - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; const ALfilter *alfilt{LookupFilter(device, filter)}; - if(!alfilt) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter); - else try - { - /* Call the appropriate handler */ - 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()); - } + if(!alfilt) + throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter}; + + /* Call the appropriate handler */ + std::visit([alfilt,param,values](auto&& thunk){thunk.getParamiv(alfilt, param, values);}, + alfilt->mTypeVariant); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alGetFilterf, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetFilterf, ALuint,filter, ALenum,param, ALfloat*,value) FORCE_ALIGN void AL_APIENTRY alGetFilterfDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat *value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; +try { + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; const ALfilter *alfilt{LookupFilter(device, filter)}; if(!alfilt) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter); - else try - { - /* Call the appropriate handler */ - 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()); - } + throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter}; + + /* Call the appropriate handler */ + std::visit([alfilt,param,value](auto&& thunk){thunk.getParamf(alfilt, param, value);}, + alfilt->mTypeVariant); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alGetFilterfv, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetFilterfv, ALuint,filter, ALenum,param, ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alGetFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat *values) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; +try { + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; const ALfilter *alfilt{LookupFilter(device, filter)}; if(!alfilt) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter); - else try - { - /* Call the appropriate handler */ - 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()); - } + throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter}; + + /* Call the appropriate handler */ + std::visit([alfilt,param,values](auto&& thunk){thunk.getParamfv(alfilt, param, values);}, + alfilt->mTypeVariant); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } void ALfilter::SetName(ALCcontext *context, ALuint id, std::string_view name) { - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; auto filter = LookupFilter(device, id); - if(!filter) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid filter ID %u", id); + if(!filter) + throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", id}; device->mFilterNames.insert_or_assign(id, name); } @@ -695,10 +619,10 @@ FilterSubList::~FilterSubList() while(usemask) { const int idx{al::countr_zero(usemask)}; - std::destroy_at(Filters+idx); + std::destroy_at(al::to_address(Filters->begin() + idx)); usemask &= ~(1_u64 << idx); } FreeMask = ~usemask; - al_free(Filters); + SubListAllocator{}.deallocate(Filters, 1); Filters = nullptr; } diff --git a/3rdparty/openal/al/filter.h b/3rdparty/openal/al/filter.h index 505900d46597..d5b20de2bb32 100644 --- a/3rdparty/openal/al/filter.h +++ b/3rdparty/openal/al/filter.h @@ -1,18 +1,22 @@ #ifndef AL_FILTER_H #define AL_FILTER_H +#include +#include #include +#include #include #include "AL/al.h" #include "AL/alc.h" -#include "AL/alext.h" +#include "AL/efx.h" #include "almalloc.h" +#include "alnumeric.h" -#define LOWPASSFREQREF 5000.0f -#define HIGHPASSFREQREF 250.0f +inline constexpr float LowPassFreqRef{5000.0f}; +inline constexpr float HighPassFreqRef{250.0f}; template struct FilterTable { @@ -25,6 +29,10 @@ struct FilterTable { static void getParamiv(const struct ALfilter*, ALenum, int*); static void getParamf(const struct ALfilter*, ALenum, float*); static void getParamfv(const struct ALfilter*, ALenum, float*); + +private: + FilterTable() = default; + friend T; }; struct NullFilterTable : public FilterTable { }; @@ -38,9 +46,9 @@ struct ALfilter { float Gain{1.0f}; float GainHF{1.0f}; - float HFReference{LOWPASSFREQREF}; + float HFReference{LowPassFreqRef}; float GainLF{1.0f}; - float LFReference{HIGHPASSFREQREF}; + float LFReference{HighPassFreqRef}; using TableTypes = std::variant; @@ -51,7 +59,22 @@ struct ALfilter { static void SetName(ALCcontext *context, ALuint id, std::string_view name); - DISABLE_ALLOC() + DISABLE_ALLOC +}; + +struct FilterSubList { + uint64_t FreeMask{~0_u64}; + gsl::owner*> Filters{nullptr}; + + FilterSubList() noexcept = default; + FilterSubList(const FilterSubList&) = delete; + FilterSubList(FilterSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Filters{rhs.Filters} + { rhs.FreeMask = ~0_u64; rhs.Filters = nullptr; } + ~FilterSubList(); + + FilterSubList& operator=(const FilterSubList&) = delete; + FilterSubList& operator=(FilterSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(Filters, rhs.Filters); return *this; } }; #endif diff --git a/3rdparty/openal/al/listener.cpp b/3rdparty/openal/al/listener.cpp index ea2ebb3f7e81..cc9268d60f8e 100644 --- a/3rdparty/openal/al/listener.cpp +++ b/3rdparty/openal/al/listener.cpp @@ -22,6 +22,7 @@ #include "listener.h" +#include #include #include @@ -30,11 +31,10 @@ #include "AL/efx.h" #include "alc/context.h" -#include "almalloc.h" -#include "atomic.h" -#include "core/except.h" +#include "alnumeric.h" +#include "alspan.h" #include "direct_defs.h" -#include "opthelpers.h" +#include "error.h" namespace { @@ -53,7 +53,7 @@ inline void CommitAndUpdateProps(ALCcontext *context) { if(!context->mDeferUpdates) { -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(context->eaxNeedsCommit()) { context->mPropsDirty = true; @@ -69,122 +69,121 @@ inline void CommitAndUpdateProps(ALCcontext *context) } // namespace -AL_API DECL_FUNC2(void, alListenerf, ALenum, ALfloat) +AL_API DECL_FUNC2(void, alListenerf, ALenum,param, ALfloat,value) FORCE_ALIGN void AL_APIENTRY alListenerfDirect(ALCcontext *context, ALenum param, ALfloat value) noexcept -{ +try { ALlistener &listener = context->mListener; - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; switch(param) { case AL_GAIN: if(!(value >= 0.0f && std::isfinite(value))) - return context->setError(AL_INVALID_VALUE, "Listener gain out of range"); + throw al::context_error{AL_INVALID_VALUE, "Listener gain out of range"}; listener.Gain = value; UpdateProps(context); - break; + return; 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"); + throw al::context_error{AL_INVALID_VALUE, "Listener meters per unit out of range"}; listener.mMetersPerUnit = value; UpdateProps(context); - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid listener float property"); + return; } + throw al::context_error{AL_INVALID_ENUM, "Invalid listener float property 0x%x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC4(void, alListener3f, ALenum, ALfloat, ALfloat, ALfloat) +AL_API DECL_FUNC4(void, alListener3f, ALenum,param, ALfloat,value1, ALfloat,value2, ALfloat,value3) FORCE_ALIGN void AL_APIENTRY alListener3fDirect(ALCcontext *context, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) noexcept -{ +try { ALlistener &listener = context->mListener; - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; switch(param) { case AL_POSITION: if(!(std::isfinite(value1) && std::isfinite(value2) && std::isfinite(value3))) - return context->setError(AL_INVALID_VALUE, "Listener position out of range"); + throw al::context_error{AL_INVALID_VALUE, "Listener position out of range"}; listener.Position[0] = value1; listener.Position[1] = value2; listener.Position[2] = value3; CommitAndUpdateProps(context); - break; + return; case AL_VELOCITY: if(!(std::isfinite(value1) && std::isfinite(value2) && std::isfinite(value3))) - return context->setError(AL_INVALID_VALUE, "Listener velocity out of range"); + throw al::context_error{AL_INVALID_VALUE, "Listener velocity out of range"}; listener.Velocity[0] = value1; listener.Velocity[1] = value2; listener.Velocity[2] = value3; CommitAndUpdateProps(context); - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid listener 3-float property"); + return; } + throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-float property 0x%x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC2(void, alListenerfv, ALenum, const ALfloat*) +AL_API DECL_FUNC2(void, alListenerfv, ALenum,param, const ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alListenerfvDirect(ALCcontext *context, ALenum param, const ALfloat *values) noexcept -{ - if(!values) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); +try { + if(!values) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; switch(param) { case AL_GAIN: case AL_METERS_PER_UNIT: - alListenerfDirect(context, param, values[0]); + alListenerfDirect(context, param, *values); return; case AL_POSITION: case AL_VELOCITY: - alListener3fDirect(context, param, values[0], values[1], values[2]); + auto vals = al::span{values, 3_uz}; + alListener3fDirect(context, param, vals[0], vals[1], vals[2]); return; } ALlistener &listener = context->mListener; - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; switch(param) { case AL_ORIENTATION: - 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]))) + auto vals = al::span{values, 6_uz}; + if(!std::all_of(vals.cbegin(), vals.cend(), [](float f) { return std::isfinite(f); })) return context->setError(AL_INVALID_VALUE, "Listener orientation out of range"); /* AT then UP */ - listener.OrientAt[0] = values[0]; - listener.OrientAt[1] = values[1]; - listener.OrientAt[2] = values[2]; - listener.OrientUp[0] = values[3]; - listener.OrientUp[1] = values[4]; - listener.OrientUp[2] = values[5]; + std::copy_n(vals.cbegin(), 3, listener.OrientAt.begin()); + std::copy_n(vals.cbegin()+3, 3, listener.OrientUp.begin()); CommitAndUpdateProps(context); - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid listener float-vector property"); + return; } + throw al::context_error{AL_INVALID_ENUM, "Invalid listener float-vector property 0x%x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC2(void, alListeneri, ALenum, ALint) +AL_API DECL_FUNC2(void, alListeneri, ALenum,param, ALint,value) FORCE_ALIGN void AL_APIENTRY alListeneriDirect(ALCcontext *context, ALenum param, ALint /*value*/) noexcept -{ - std::lock_guard _{context->mPropLock}; - switch(param) - { - default: - context->setError(AL_INVALID_ENUM, "Invalid listener integer property"); - } +try { + std::lock_guard proplock{context->mPropLock}; + throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer property 0x%x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC4(void, alListener3i, ALenum, ALint, ALint, ALint) +AL_API DECL_FUNC4(void, alListener3i, ALenum,param, ALint,value1, ALint,value2, ALint,value3) FORCE_ALIGN void AL_APIENTRY alListener3iDirect(ALCcontext *context, ALenum param, ALint value1, ALint value2, ALint value3) noexcept -{ +try { switch(param) { case AL_POSITION: @@ -194,105 +193,105 @@ FORCE_ALIGN void AL_APIENTRY alListener3iDirect(ALCcontext *context, ALenum para return; } - std::lock_guard _{context->mPropLock}; - switch(param) - { - default: - context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property"); - } + std::lock_guard proplock{context->mPropLock}; + throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-integer property 0x%x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC2(void, alListeneriv, ALenum, const ALint*) +AL_API DECL_FUNC2(void, alListeneriv, ALenum,param, const ALint*,values) FORCE_ALIGN void AL_APIENTRY alListenerivDirect(ALCcontext *context, ALenum param, const ALint *values) noexcept -{ - if(!values) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); +try { + if(!values) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; + al::span vals; switch(param) { case AL_POSITION: case AL_VELOCITY: - alListener3fDirect(context, param, static_cast(values[0]), - static_cast(values[1]), static_cast(values[2])); + vals = {values, 3_uz}; + alListener3fDirect(context, param, static_cast(vals[0]), + static_cast(vals[1]), static_cast(vals[2])); return; case AL_ORIENTATION: - const ALfloat fvals[6]{ - static_cast(values[0]), - static_cast(values[1]), - static_cast(values[2]), - static_cast(values[3]), - static_cast(values[4]), - static_cast(values[5]), + vals = {values, 6_uz}; + const std::array fvals{static_cast(vals[0]), static_cast(vals[1]), + static_cast(vals[2]), static_cast(vals[3]), + static_cast(vals[4]), static_cast(vals[5]), }; - alListenerfvDirect(context, param, fvals); + alListenerfvDirect(context, param, fvals.data()); return; } - std::lock_guard _{context->mPropLock}; - switch(param) - { - default: - context->setError(AL_INVALID_ENUM, "Invalid listener integer-vector property"); - } + std::lock_guard proplock{context->mPropLock}; + throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer-vector property 0x%x", + param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC2(void, alGetListenerf, ALenum, ALfloat*) +AL_API DECL_FUNC2(void, alGetListenerf, ALenum,param, ALfloat*,value) FORCE_ALIGN void AL_APIENTRY alGetListenerfDirect(ALCcontext *context, ALenum param, ALfloat *value) noexcept -{ - ALlistener &listener = context->mListener; - std::lock_guard _{context->mPropLock}; +try { if(!value) - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) - { - case AL_GAIN: - *value = listener.Gain; - break; + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; - case AL_METERS_PER_UNIT: - *value = listener.mMetersPerUnit; - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid listener float property"); + ALlistener &listener = context->mListener; + std::lock_guard proplock{context->mPropLock}; + switch(param) + { + case AL_GAIN: *value = listener.Gain; return; + case AL_METERS_PER_UNIT: *value = listener.mMetersPerUnit; return; } + throw al::context_error{AL_INVALID_ENUM, "Invalid listener float property 0x%x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC4(void, alGetListener3f, ALenum, ALfloat*, ALfloat*, ALfloat*) +AL_API DECL_FUNC4(void, alGetListener3f, ALenum,param, ALfloat*,value1, ALfloat*,value2, ALfloat*,value3) FORCE_ALIGN void AL_APIENTRY alGetListener3fDirect(ALCcontext *context, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) noexcept -{ - ALlistener &listener = context->mListener; - std::lock_guard _{context->mPropLock}; +try { if(!value1 || !value2 || !value3) - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; + + ALlistener &listener = context->mListener; + std::lock_guard proplock{context->mPropLock}; + switch(param) { case AL_POSITION: *value1 = listener.Position[0]; *value2 = listener.Position[1]; *value3 = listener.Position[2]; - break; + return; case AL_VELOCITY: *value1 = listener.Velocity[0]; *value2 = listener.Velocity[1]; *value3 = listener.Velocity[2]; - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid listener 3-float property"); + return; } + throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-float property 0x%x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC2(void, alGetListenerfv, ALenum, ALfloat*) +AL_API DECL_FUNC2(void, alGetListenerfv, ALenum,param, ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alGetListenerfvDirect(ALCcontext *context, ALenum param, ALfloat *values) noexcept -{ +try { + if(!values) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; + switch(param) { case AL_GAIN: @@ -302,101 +301,101 @@ FORCE_ALIGN void AL_APIENTRY alGetListenerfvDirect(ALCcontext *context, ALenum p case AL_POSITION: case AL_VELOCITY: - alGetListener3fDirect(context, param, values+0, values+1, values+2); + auto vals = al::span{values, 3_uz}; + alGetListener3fDirect(context, param, &vals[0], &vals[1], &vals[2]); return; } ALlistener &listener = context->mListener; - std::lock_guard _{context->mPropLock}; - if(!values) - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) + std::lock_guard proplock{context->mPropLock}; + switch(param) { case AL_ORIENTATION: + al::span vals{values, 6_uz}; // AT then UP - values[0] = listener.OrientAt[0]; - values[1] = listener.OrientAt[1]; - values[2] = listener.OrientAt[2]; - values[3] = listener.OrientUp[0]; - values[4] = listener.OrientUp[1]; - values[5] = listener.OrientUp[2]; - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid listener float-vector property"); + std::copy_n(listener.OrientAt.cbegin(), 3, vals.begin()); + std::copy_n(listener.OrientUp.cbegin(), 3, vals.begin()+3); + return; } + throw al::context_error{AL_INVALID_ENUM, "Invalid listener float-vector property 0x%x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC2(void, alGetListeneri, ALenum, ALint*) +AL_API DECL_FUNC2(void, alGetListeneri, ALenum,param, ALint*,value) FORCE_ALIGN void AL_APIENTRY alGetListeneriDirect(ALCcontext *context, ALenum param, ALint *value) noexcept -{ - std::lock_guard _{context->mPropLock}; - if(!value) - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) - { - default: - context->setError(AL_INVALID_ENUM, "Invalid listener integer property"); - } +try { + if(!value) throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; + std::lock_guard proplock{context->mPropLock}; + throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer property 0x%x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC4(void, alGetListener3i, ALenum, ALint*, ALint*, ALint*) +AL_API DECL_FUNC4(void, alGetListener3i, ALenum,param, ALint*,value1, ALint*,value2, ALint*,value3) FORCE_ALIGN void AL_APIENTRY alGetListener3iDirect(ALCcontext *context, ALenum param, ALint *value1, ALint *value2, ALint *value3) noexcept -{ - ALlistener &listener = context->mListener; - std::lock_guard _{context->mPropLock}; +try { if(!value1 || !value2 || !value3) - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; + + ALlistener &listener = context->mListener; + std::lock_guard proplock{context->mPropLock}; + switch(param) { case AL_POSITION: *value1 = static_cast(listener.Position[0]); *value2 = static_cast(listener.Position[1]); *value3 = static_cast(listener.Position[2]); - break; + return; case AL_VELOCITY: *value1 = static_cast(listener.Velocity[0]); *value2 = static_cast(listener.Velocity[1]); *value3 = static_cast(listener.Velocity[2]); - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property"); + return; } + throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-integer property 0x%x", param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC2(void, alGetListeneriv, ALenum, ALint*) +AL_API DECL_FUNC2(void, alGetListeneriv, ALenum,param, ALint*,values) FORCE_ALIGN void AL_APIENTRY alGetListenerivDirect(ALCcontext *context, ALenum param, ALint *values) noexcept -{ +try { + if(!values) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; + switch(param) { case AL_POSITION: case AL_VELOCITY: - alGetListener3iDirect(context, param, values+0, values+1, values+2); + auto vals = al::span{values, 3_uz}; + alGetListener3iDirect(context, param, &vals[0], &vals[1], &vals[2]); return; } ALlistener &listener = context->mListener; - std::lock_guard _{context->mPropLock}; - if(!values) - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) + std::lock_guard proplock{context->mPropLock}; + + static constexpr auto f2i = [](const float val) noexcept { return static_cast(val); }; + switch(param) { case AL_ORIENTATION: + auto vals = al::span{values, 6_uz}; // AT then UP - values[0] = static_cast(listener.OrientAt[0]); - values[1] = static_cast(listener.OrientAt[1]); - values[2] = static_cast(listener.OrientAt[2]); - values[3] = static_cast(listener.OrientUp[0]); - values[4] = static_cast(listener.OrientUp[1]); - values[5] = static_cast(listener.OrientUp[2]); - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid listener integer-vector property"); + std::transform(listener.OrientAt.cbegin(), listener.OrientAt.cend(), vals.begin(), f2i); + std::transform(listener.OrientUp.cbegin(), listener.OrientUp.cend(), vals.begin()+3, f2i); + return; } + throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer-vector property 0x%x", + param}; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } diff --git a/3rdparty/openal/al/listener.h b/3rdparty/openal/al/listener.h index 8153287715e9..7a4ff530f72e 100644 --- a/3rdparty/openal/al/listener.h +++ b/3rdparty/openal/al/listener.h @@ -3,8 +3,6 @@ #include -#include "AL/al.h" -#include "AL/alc.h" #include "AL/efx.h" #include "almalloc.h" @@ -18,7 +16,7 @@ struct ALlistener { float Gain{1.0f}; float mMetersPerUnit{AL_DEFAULT_METERS_PER_UNIT}; - DISABLE_ALLOC() + DISABLE_ALLOC }; #endif diff --git a/3rdparty/openal/al/source.cpp b/3rdparty/openal/al/source.cpp index 2fbd1703aeec..81a809e69e20 100644 --- a/3rdparty/openal/al/source.cpp +++ b/3rdparty/openal/al/source.cpp @@ -25,22 +25,24 @@ #include #include #include +#include #include #include #include -#include #include #include -#include +#include #include #include #include #include -#include #include #include #include -#include +#include +#include +#include +#include #include #include @@ -50,7 +52,6 @@ #include "AL/efx.h" #include "albit.h" -#include "alc/alu.h" #include "alc/backends/base.h" #include "alc/context.h" #include "alc/device.h" @@ -61,30 +62,33 @@ #include "atomic.h" #include "auxeffectslot.h" #include "buffer.h" -#include "core/ambidefs.h" -#include "core/bformatdec.h" -#include "core/except.h" -#include "core/filters/nfc.h" -#include "core/filters/splitter.h" +#include "core/buffer_storage.h" #include "core/logging.h" +#include "core/mixer/defs.h" #include "core/voice_change.h" #include "direct_defs.h" -#include "event.h" +#include "error.h" #include "filter.h" +#include "flexarray.h" +#include "intrusive_ptr.h" #include "opthelpers.h" -#include "ringbuffer.h" -#ifdef ALSOFT_EAX -#include -#endif // ALSOFT_EAX - -bool sBufferSubDataCompat{false}; +#if ALSOFT_EAX +#include "eax/api.h" +#include "eax/call.h" +#include "eax/fx_slot_index.h" +#include "eax/utils.h" +#endif namespace { -using namespace std::placeholders; +using SubListAllocator = al::allocator>; using std::chrono::nanoseconds; using seconds_d = std::chrono::duration; +using source_store_array = std::array; +using source_store_vector = std::vector; +using source_store_variant = std::variant; + Voice *GetSourceVoice(ALsource *source, ALCcontext *context) { @@ -97,7 +101,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; } @@ -127,7 +131,7 @@ void UpdateSourceProps(const ALsource *source, Voice *voice, ALCcontext *context props->RefDistance = source->RefDistance; props->MaxDistance = source->MaxDistance; props->RolloffFactor = source->RolloffFactor -#ifdef ALSOFT_EAX +#if ALSOFT_EAX + source->RolloffFactor2 #endif ; @@ -155,6 +159,7 @@ void UpdateSourceProps(const ALsource *source, Voice *voice, ALCcontext *context props->Radius = source->Radius; props->EnhWidth = source->EnhWidth; + props->Panning = source->mPanningEnabled ? source->mPan : 0.0f; props->Direct.Gain = source->Direct.Gain; props->Direct.GainHF = source->Direct.GainHF; @@ -173,7 +178,7 @@ void UpdateSourceProps(const ALsource *source, Voice *voice, ALCcontext *context ret.LFReference = srcsend.LFReference; return ret; }; - std::transform(source->Send.cbegin(), source->Send.cend(), props->Send, copy_send); + std::transform(source->Send.cbegin(), source->Send.cend(), props->Send.begin(), copy_send); if(!props->Send[0].Slot && context->mDefaultSlot) props->Send[0].Slot = context->mDefaultSlot->mSlot; @@ -196,7 +201,7 @@ void UpdateSourceProps(const ALsource *source, Voice *voice, ALCcontext *context */ int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime) { - ALCdevice *device{context->mALDevice.get()}; + auto *device = context->mALDevice.get(); const VoiceBufferItem *Current{}; int64_t readPos{}; uint refcount{}; @@ -204,7 +209,7 @@ int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds do { refcount = device->waitForMix(); - *clocktime = GetDeviceClockTime(device); + *clocktime = device->getClockTime(); voice = GetSourceVoice(Source, context); if(voice) { @@ -214,7 +219,7 @@ int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds readPos += voice->mPositionFrac.load(std::memory_order_relaxed); } std::atomic_thread_fence(std::memory_order_acquire); - } while(refcount != device->MixCount.load(std::memory_order_relaxed)); + } while(refcount != device->mMixCount.load(std::memory_order_relaxed)); if(!voice) return 0; @@ -236,7 +241,7 @@ int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds */ double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime) { - ALCdevice *device{context->mALDevice.get()}; + auto *device = context->mALDevice.get(); const VoiceBufferItem *Current{}; int64_t readPos{}; uint refcount{}; @@ -244,7 +249,7 @@ double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *cl do { refcount = device->waitForMix(); - *clocktime = GetDeviceClockTime(device); + *clocktime = device->getClockTime(); voice = GetSourceVoice(Source, context); if(voice) { @@ -254,7 +259,7 @@ double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *cl readPos += voice->mPositionFrac.load(std::memory_order_relaxed); } std::atomic_thread_fence(std::memory_order_acquire); - } while(refcount != device->MixCount.load(std::memory_order_relaxed)); + } while(refcount != device->mMixCount.load(std::memory_order_relaxed)); if(!voice) return 0.0f; @@ -284,9 +289,9 @@ double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *cl * queue (not the start of the current buffer). */ template -T GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) +NOINLINE T GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) { - ALCdevice *device{context->mALDevice.get()}; + auto *device = context->mALDevice.get(); const VoiceBufferItem *Current{}; int64_t readPos{}; uint readPosFrac{}; @@ -304,7 +309,7 @@ T GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) readPosFrac = voice->mPositionFrac.load(std::memory_order_relaxed); } std::atomic_thread_fence(std::memory_order_acquire); - } while(refcount != device->MixCount.load(std::memory_order_relaxed)); + } while(refcount != device->mMixCount.load(std::memory_order_relaxed)); if(!voice) return T{0}; @@ -336,7 +341,7 @@ T GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) else { readPos /= BufferFmt->mSampleRate; - offset = static_cast(clampi64(readPos, std::numeric_limits::min(), + offset = static_cast(std::clamp(readPos, std::numeric_limits::min(), std::numeric_limits::max())); } break; @@ -345,7 +350,7 @@ T GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) if constexpr(std::is_floating_point_v) offset = static_cast(readPos) + static_cast(readPosFrac)/T{MixerFracOne}; else - offset = static_cast(clampi64(readPos, std::numeric_limits::min(), + offset = static_cast(std::clamp(readPos, std::numeric_limits::min(), std::numeric_limits::max())); break; @@ -377,7 +382,7 @@ T GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) * format (Bytes, Samples or Seconds). */ template -T GetSourceLength(const ALsource *source, ALenum name) +NOINLINE T GetSourceLength(const ALsource *source, ALenum name) { uint64_t length{0}; const ALbuffer *BufferFmt{nullptr}; @@ -397,14 +402,14 @@ T GetSourceLength(const ALsource *source, ALenum name) if constexpr(std::is_floating_point_v) return static_cast(length) / static_cast(BufferFmt->mSampleRate); else - return static_cast(minu64(length/BufferFmt->mSampleRate, + return static_cast(std::min(length/BufferFmt->mSampleRate, std::numeric_limits::max())); case AL_SAMPLE_LENGTH_SOFT: if constexpr(std::is_floating_point_v) return static_cast(length); else - return static_cast(minu64(length, std::numeric_limits::max())); + return static_cast(std::min(length, std::numeric_limits::max())); case AL_BYTE_LENGTH_SOFT: const ALuint BlockSamples{BufferFmt->mBlockAlign}; @@ -416,7 +421,7 @@ T GetSourceLength(const ALsource *source, ALenum name) return static_cast(length); else { - if(length > std::numeric_limits::max()) + if(length > uint64_t{std::numeric_limits::max()}) return RoundDown(std::numeric_limits::max(), static_cast(BlockSize)); return static_cast(length); } @@ -438,7 +443,7 @@ struct VoicePos { * using the given offset type and offset. If the offset is out of range, * returns an empty optional. */ -std::optional GetSampleOffset(al::deque &BufferList, +std::optional GetSampleOffset(std::deque &BufferList, ALenum OffsetType, double Offset) { /* Find the first valid Buffer in the Queue */ @@ -469,7 +474,7 @@ std::optional GetSampleOffset(al::deque &BufferList dblfrac += 1.0; } offset = static_cast(dbloff); - frac = static_cast(mind(dblfrac*MixerFracOne, MixerFracOne-1.0)); + frac = static_cast(std::min(dblfrac*MixerFracOne, MixerFracOne-1.0)); break; case AL_SAMPLE_OFFSET: @@ -480,7 +485,7 @@ std::optional GetSampleOffset(al::deque &BufferList dblfrac += 1.0; } offset = static_cast(dbloff); - frac = static_cast(mind(dblfrac*MixerFracOne, MixerFracOne-1.0)); + frac = static_cast(std::min(dblfrac*MixerFracOne, MixerFracOne-1.0)); break; case AL_BYTE_OFFSET: @@ -521,16 +526,19 @@ std::optional GetSampleOffset(al::deque &BufferList void InitVoice(Voice *voice, ALsource *source, ALbufferQueueItem *BufferList, ALCcontext *context, - ALCdevice *device) + al::Device *device) { voice->mLoopBuffer.store(source->Looping ? &source->mQueue.front() : nullptr, std::memory_order_relaxed); ALbuffer *buffer{BufferList->mBuffer}; voice->mFrequency = buffer->mSampleRate; - voice->mFmtChannels = - (buffer->mChannels == FmtStereo && source->mStereoMode == SourceStereo::Enhanced) ? - FmtSuperStereo : buffer->mChannels; + if(buffer->mChannels == FmtMono && source->mPanningEnabled) + voice->mFmtChannels = FmtMonoDup; + else if(buffer->mChannels == FmtStereo && source->mStereoMode == SourceStereo::Enhanced) + voice->mFmtChannels = FmtSuperStereo; + else + voice->mFmtChannels = buffer->mChannels; voice->mFmtType = buffer->mType; voice->mFrameStep = buffer->channelsFromFmt(); voice->mBytesPerBlock = buffer->blockSizeFromFmt(); @@ -569,7 +577,7 @@ VoiceChange *GetVoiceChanger(ALCcontext *ctx) void SendVoiceChanges(ALCcontext *ctx, VoiceChange *tail) { - ALCdevice *device{ctx->mALDevice.get()}; + auto *device = ctx->mALDevice.get(); VoiceChange *oldhead{ctx->mCurrentVoiceChange.load(std::memory_order_acquire)}; while(VoiceChange *next{oldhead->mNext.load(std::memory_order_relaxed)}) @@ -577,7 +585,7 @@ void SendVoiceChanges(ALCcontext *ctx, VoiceChange *tail) oldhead->mNext.store(tail, std::memory_order_release); const bool connected{device->Connected.load(std::memory_order_acquire)}; - device->waitForMix(); + std::ignore = device->waitForMix(); if(!connected) UNLIKELY { if(ctx->mStopVoicesOnDisconnect.load(std::memory_order_acquire)) @@ -598,8 +606,8 @@ void SendVoiceChanges(ALCcontext *ctx, VoiceChange *tail) } -bool SetVoiceOffset(Voice *oldvoice, const VoicePos &vpos, ALsource *source, ALCcontext *context, - ALCdevice *device) +auto SetVoiceOffset(Voice *oldvoice, const VoicePos &vpos, ALsource *source, ALCcontext *context, + al::Device *device) -> bool { /* First, get a free voice to start at the new offset. */ auto voicelist = context->getVoicesSpan(); @@ -683,7 +691,7 @@ bool SetVoiceOffset(Voice *oldvoice, const VoicePos &vpos, ALsource *source, ALC return true; /* Otherwise, wait for any current mix to finish and check one last time. */ - device->waitForMix(); + std::ignore = device->waitForMix(); if(newvoice->mPlayState.load(std::memory_order_acquire) != Voice::Pending) return true; /* The change-over failed because the old voice stopped before the new @@ -723,26 +731,26 @@ bool EnsureSources(ALCcontext *context, size_t needed) [](size_t cur, const SourceSubList &sublist) noexcept -> size_t { return cur + static_cast(al::popcount(sublist.FreeMask)); })}; - while(needed > count) - { - if(context->mSourceList.size() >= 1<<25) UNLIKELY - return false; - - context->mSourceList.emplace_back(); - auto sublist = context->mSourceList.end() - 1; - sublist->FreeMask = ~0_u64; - sublist->Sources = static_cast(al_calloc(alignof(ALsource), sizeof(ALsource)*64)); - if(!sublist->Sources) UNLIKELY + try { + while(needed > count) { - context->mSourceList.pop_back(); - return false; + if(context->mSourceList.size() >= 1<<25) UNLIKELY + return false; + + SourceSubList sublist{}; + sublist.FreeMask = ~0_u64; + sublist.Sources = SubListAllocator{}.allocate(1); + context->mSourceList.emplace_back(std::move(sublist)); + count += std::tuple_size_v; } - count += 64; + } + catch(...) { + return false; } return true; } -ALsource *AllocSource(ALCcontext *context) +ALsource *AllocSource(ALCcontext *context) noexcept { auto sublist = std::find_if(context->mSourceList.begin(), context->mSourceList.end(), [](const SourceSubList &entry) noexcept -> bool @@ -751,7 +759,10 @@ ALsource *AllocSource(ALCcontext *context) auto slidx = static_cast(al::countr_zero(sublist->FreeMask)); ASSUME(slidx < 64); - ALsource *source{al::construct_at(sublist->Sources + slidx)}; + ALsource *source{al::construct_at(al::to_address(sublist->Sources->begin() + slidx))}; +#if ALSOFT_EAX + source->eaxInitialize(context); +#endif // ALSOFT_EAX /* Add 1 to avoid source ID 0. */ source->id = ((lidx<<6) | slidx) + 1; @@ -799,10 +810,10 @@ inline ALsource *LookupSource(ALCcontext *context, ALuint id) noexcept SourceSubList &sublist{context->mSourceList[lidx]}; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Sources + slidx; + return al::to_address(sublist.Sources->begin() + slidx); } -auto LookupBuffer = [](ALCdevice *device, auto id) noexcept -> ALbuffer* +auto LookupBuffer = [](al::Device *device, auto id) noexcept -> ALbuffer* { const auto lidx{(id-1) >> 6}; const auto slidx{(id-1) & 0x3f}; @@ -812,10 +823,10 @@ auto LookupBuffer = [](ALCdevice *device, auto id) noexcept -> ALbuffer* BufferSubList &sublist = device->BufferList[static_cast(lidx)]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Buffers + static_cast(slidx); + return al::to_address(sublist.Buffers->begin() + static_cast(slidx)); }; -auto LookupFilter = [](ALCdevice *device, auto id) noexcept -> ALfilter* +auto LookupFilter = [](al::Device *device, auto id) noexcept -> ALfilter* { const auto lidx{(id-1) >> 6}; const auto slidx{(id-1) & 0x3f}; @@ -825,7 +836,7 @@ auto LookupFilter = [](ALCdevice *device, auto id) noexcept -> ALfilter* FilterSubList &sublist = device->FilterList[static_cast(lidx)]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Filters + static_cast(slidx); + return al::to_address(sublist.Filters->begin() + static_cast(slidx)); }; auto LookupEffectSlot = [](ALCcontext *context, auto id) noexcept -> ALeffectslot* @@ -838,7 +849,7 @@ auto LookupEffectSlot = [](ALCcontext *context, auto id) noexcept -> ALeffectslo EffectSlotSubList &sublist{context->mEffectSlotList[static_cast(lidx)]}; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.EffectSlots + static_cast(slidx); + return al::to_address(sublist.EffectSlots->begin() + static_cast(slidx)); }; @@ -1011,6 +1022,10 @@ enum SourceProp : ALenum { /* AL_SOFT_buffer_sub_data */ srcByteRWOffsetsSOFT = AL_BYTE_RW_OFFSETS_SOFT, srcSampleRWOffsetsSOFT = AL_SAMPLE_RW_OFFSETS_SOFT, + + /* AL_SOFT_source_panning */ + srcPanningEnabledSOFT = AL_PANNING_ENABLED_SOFT, + srcPanSOFT = AL_PAN_SOFT, }; @@ -1038,6 +1053,8 @@ constexpr ALuint IntValsByProp(ALenum prop) case AL_SOURCE_RESAMPLER_SOFT: case AL_SOURCE_SPATIALIZE_SOFT: case AL_STEREO_MODE_SOFT: + case AL_PANNING_ENABLED_SOFT: + case AL_PAN_SOFT: return 1; case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/ @@ -1115,6 +1132,8 @@ constexpr ALuint Int64ValsByProp(ALenum prop) case AL_SOURCE_RESAMPLER_SOFT: case AL_SOURCE_SPATIALIZE_SOFT: case AL_STEREO_MODE_SOFT: + case AL_PANNING_ENABLED_SOFT: + case AL_PAN_SOFT: return 1; case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/ @@ -1208,6 +1227,8 @@ constexpr ALuint FloatValsByProp(ALenum prop) case AL_SEC_LENGTH_SOFT: case AL_STEREO_MODE_SOFT: case AL_SUPER_STEREO_WIDTH_SOFT: + case AL_PANNING_ENABLED_SOFT: + case AL_PAN_SOFT: return 1; case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/ @@ -1281,6 +1302,8 @@ constexpr ALuint DoubleValsByProp(ALenum prop) case AL_SEC_LENGTH_SOFT: case AL_STEREO_MODE_SOFT: case AL_SUPER_STEREO_WIDTH_SOFT: + case AL_PANNING_ENABLED_SOFT: + case AL_PAN_SOFT: return 1; case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/ @@ -1315,18 +1338,6 @@ constexpr ALuint DoubleValsByProp(ALenum prop) } -struct check_exception : std::exception { -}; -struct check_size_exception final : check_exception { - const char *what() const noexcept override - { return "check_size_exception"; } -}; -struct check_value_exception final : check_exception { - const char *what() const noexcept override - { return "check_value_exception"; } -}; - - void UpdateSourceProps(ALsource *source, ALCcontext *context) { if(!context->mDeferUpdates) @@ -1339,7 +1350,7 @@ void UpdateSourceProps(ALsource *source, ALCcontext *context) } source->mPropsDirty = true; } -#ifdef ALSOFT_EAX +#if ALSOFT_EAX void CommitAndUpdateSourceProps(ALsource *source, ALCcontext *context) { if(!context->mDeferUpdates) @@ -1373,21 +1384,22 @@ struct PropType { static const char *Name() { return "float"; } }; template<> struct PropType { static const char *Name() { return "double"; } }; -template struct HexPrinter { - char mStr[sizeof(T)*2 + 3]{}; + std::array mStr{}; + + template HexPrinter(T value) { using ST = std::make_signed_t>; if constexpr(std::is_same_v) - std::snprintf(mStr, std::size(mStr), "0x%x", value); + std::snprintf(mStr.data(), mStr.size(), "0x%x", value); else if constexpr(std::is_same_v) - std::snprintf(mStr, std::size(mStr), "0x%lx", value); + std::snprintf(mStr.data(), mStr.size(), "0x%lx", value); else if constexpr(std::is_same_v) - std::snprintf(mStr, std::size(mStr), "0x%llx", value); + std::snprintf(mStr.data(), mStr.size(), "0x%llx", value); } - const char *c_str() const noexcept { return mStr; } + [[nodiscard]] auto c_str() const noexcept -> const char* { return mStr.data(); } }; @@ -1395,38 +1407,35 @@ struct HexPrinter { * Returns a pair of lambdas to check the following setter. * * 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. + * throwing a context error if it fails. * - * The second lambda tests the validity of the value check, setting the proper - * context error and throwing a check_value_exception if it failed. + * The second lambda tests the validity of the value check, throwing a context + * error if it failed. */ template -auto GetCheckers(ALCcontext *const Context, const SourceProp prop, const al::span values) +auto GetCheckers(const SourceProp prop, const al::span values) { return std::make_pair( [=](size_t expect) -> void { - if(values.size() == expect) LIKELY return; - Context->setError(AL_INVALID_ENUM, "Property 0x%04x expects %zu value(s), got %zu", - prop, expect, values.size()); - throw check_size_exception{}; + if(values.size() == expect) return; + throw al::context_error{AL_INVALID_ENUM, + "Property 0x%04x expects %zu value(s), got %zu", prop, expect, values.size()}; }, - [Context](bool passed) -> void + [](bool passed) -> void { - if(passed) LIKELY return; - Context->setError(AL_INVALID_VALUE, "Value out of range"); - throw check_value_exception{}; + if(passed) return; + throw al::context_error{AL_INVALID_VALUE, "Value out of range"}; } ); } template NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, - const al::span values) try + const al::span values) { - auto&& [CheckSize, CheckValue] = GetCheckers(Context, prop, values); - ALCdevice *device{Context->mALDevice.get()}; + auto [CheckSize, CheckValue] = GetCheckers(prop, values); + auto *device = Context->mALDevice.get(); switch(prop) { @@ -1437,8 +1446,8 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con if constexpr(std::is_integral_v) { /* Query only */ - return Context->setError(AL_INVALID_OPERATION, - "Setting read-only source property 0x%04x", prop); + throw al::context_error{AL_INVALID_OPERATION, + "Setting read-only source property 0x%04x", prop}; } break; @@ -1450,12 +1459,15 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con case AL_SAMPLE_OFFSET_CLOCK_SOFT: case AL_SEC_OFFSET_CLOCK_SOFT: /* Query only */ - return Context->setError(AL_INVALID_OPERATION, - "Setting read-only source property 0x%04x", prop); + throw al::context_error{AL_INVALID_OPERATION, "Setting read-only source property 0x%04x", + prop}; case AL_PITCH: CheckSize(1); - CheckValue(values[0] >= T{0}); + if constexpr(std::is_floating_point_v) + CheckValue(values[0] >= T{0} && std::isfinite(static_cast(values[0]))); + else + CheckValue(values[0] >= T{0}); Source->Pitch = static_cast(values[0]); return UpdateSourceProps(Source, Context); @@ -1476,42 +1488,60 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con case AL_GAIN: CheckSize(1); - CheckValue(values[0] >= T{0}); + if constexpr(std::is_floating_point_v) + CheckValue(values[0] >= T{0} && std::isfinite(static_cast(values[0]))); + else + CheckValue(values[0] >= T{0}); Source->Gain = static_cast(values[0]); return UpdateSourceProps(Source, Context); case AL_MAX_DISTANCE: CheckSize(1); - CheckValue(values[0] >= T{0}); + if constexpr(std::is_floating_point_v) + CheckValue(values[0] >= T{0} && std::isfinite(static_cast(values[0]))); + else + CheckValue(values[0] >= T{0}); Source->MaxDistance = static_cast(values[0]); return CommitAndUpdateSourceProps(Source, Context); case AL_ROLLOFF_FACTOR: CheckSize(1); - CheckValue(values[0] >= T{0}); + if constexpr(std::is_floating_point_v) + CheckValue(values[0] >= T{0} && std::isfinite(static_cast(values[0]))); + else + CheckValue(values[0] >= T{0}); Source->RolloffFactor = static_cast(values[0]); return CommitAndUpdateSourceProps(Source, Context); case AL_REFERENCE_DISTANCE: CheckSize(1); - CheckValue(values[0] >= T{0}); + if constexpr(std::is_floating_point_v) + CheckValue(values[0] >= T{0} && std::isfinite(static_cast(values[0]))); + else + CheckValue(values[0] >= T{0}); Source->RefDistance = static_cast(values[0]); return CommitAndUpdateSourceProps(Source, Context); case AL_MIN_GAIN: CheckSize(1); - CheckValue(values[0] >= T{0}); + if constexpr(std::is_floating_point_v) + CheckValue(values[0] >= T{0} && std::isfinite(static_cast(values[0]))); + else + CheckValue(values[0] >= T{0}); Source->MinGain = static_cast(values[0]); return UpdateSourceProps(Source, Context); case AL_MAX_GAIN: CheckSize(1); - CheckValue(values[0] >= T{0}); + if constexpr(std::is_floating_point_v) + CheckValue(values[0] >= T{0} && std::isfinite(static_cast(values[0]))); + else + CheckValue(values[0] >= T{0}); Source->MaxGain = static_cast(values[0]); return UpdateSourceProps(Source, Context); @@ -1581,7 +1611,7 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con * to ensure it isn't currently looping back or reaching the * end. */ - device->waitForMix(); + std::ignore = device->waitForMix(); } return; } @@ -1591,30 +1621,29 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con if constexpr(std::is_integral_v) { 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); - } - al::deque oldlist; + if(const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))}; + state == AL_PLAYING || state == AL_PAUSED) + throw al::context_error{AL_INVALID_OPERATION, + "Setting buffer on playing or paused source %u", Source->id}; + + std::deque oldlist; if(values[0]) { using UT = std::make_unsigned_t; - std::lock_guard _{device->BufferLock}; + std::lock_guard buflock{device->BufferLock}; ALbuffer *buffer{LookupBuffer(device, static_cast(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); + if(!buffer) + throw al::context_error{AL_INVALID_VALUE, "Invalid buffer ID %s", + std::to_string(values[0]).c_str()}; + if(buffer->MappedAccess && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) + throw al::context_error{AL_INVALID_OPERATION, + "Setting non-persistently mapped buffer %u", buffer->id}; + if(buffer->mCallback && buffer->ref.load(std::memory_order_relaxed) != 0) + throw al::context_error{AL_INVALID_OPERATION, + "Setting already-set callback buffer %u", buffer->id}; /* Add the selected buffer to a one-item queue */ - al::deque newlist; + std::deque newlist; newlist.emplace_back(); newlist.back().mCallback = buffer->mCallback; newlist.back().mUserData = buffer->mUserData; @@ -1622,7 +1651,7 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con newlist.back().mSampleLen = buffer->mSampleLen; newlist.back().mLoopStart = buffer->mLoopStart; newlist.back().mLoopEnd = buffer->mLoopEnd; - newlist.back().mSamples = buffer->mData.data(); + newlist.back().mSamples = buffer->mData; newlist.back().mBuffer = buffer; IncrementRef(buffer->ref); @@ -1659,7 +1688,7 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con if(Voice *voice{GetSourceVoice(Source, Context)}) { auto vpos = GetSampleOffset(Source->mQueue, prop, static_cast(values[0])); - if(!vpos) return Context->setError(AL_INVALID_VALUE, "Invalid offset"); + if(!vpos) throw al::context_error{AL_INVALID_VALUE, "Invalid offset"}; if(SetVoiceOffset(voice, *vpos, Source, Context, Context->mALDevice.get())) return; @@ -1674,8 +1703,8 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con if constexpr(std::is_integral_v) { /* Query only */ - return Context->setError(AL_INVALID_OPERATION, - "Setting read-only source property 0x%04x", prop); + throw al::context_error{AL_INVALID_OPERATION, + "Setting read-only source property 0x%04x", prop}; } } break; @@ -1686,8 +1715,8 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con if constexpr(std::is_integral_v) { /* Query only */ - return Context->setError(AL_INVALID_OPERATION, - "Setting read-only source property 0x%04x", prop); + throw al::context_error{AL_INVALID_OPERATION, + "Setting read-only source property 0x%04x", prop}; } break; } @@ -1707,6 +1736,25 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con Source->EnhWidth = static_cast(values[0]); return UpdateSourceProps(Source, Context); + case AL_PANNING_ENABLED_SOFT: + CheckSize(1); + if(const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))}; + state == AL_PLAYING || state == AL_PAUSED) + throw al::context_error{AL_INVALID_OPERATION, + "Modifying panning enabled on playing or paused source %u", Source->id}; + + CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE); + + Source->mPanningEnabled = values[0] != AL_FALSE; + return UpdateSourceProps(Source, Context); + + case AL_PAN_SOFT: + CheckSize(1); + CheckValue(values[0] >= T{-1} && values[0] <= T{1}); + + Source->mPan = static_cast(values[0]); + return UpdateSourceProps(Source, Context); + case AL_STEREO_ANGLES: CheckSize(2); if constexpr(std::is_floating_point_v) @@ -1780,11 +1828,11 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con const auto filterid = static_cast>(values[0]); if(values[0]) { - std::lock_guard _{device->FilterLock}; + std::lock_guard filterlock{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()); + if(!filter) + throw al::context_error{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; @@ -1795,9 +1843,9 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con { Source->Direct.Gain = 1.0f; Source->Direct.GainHF = 1.0f; - Source->Direct.HFReference = LOWPASSFREQREF; + Source->Direct.HFReference = LowPassFreqRef; Source->Direct.GainLF = 1.0f; - Source->Direct.LFReference = HIGHPASSFREQREF; + Source->Direct.LFReference = HighPassFreqRef; } return UpdateSourceProps(Source, Context); } @@ -1845,8 +1893,8 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con Source->DirectChannels = *mode; return UpdateSourceProps(Source, Context); } - return Context->setError(AL_INVALID_VALUE, "Invalid direct channels mode: %s\n", - HexPrinter{values[0]}.c_str()); + throw al::context_error{AL_INVALID_VALUE, "Invalid direct channels mode: %s\n", + HexPrinter{values[0]}.c_str()}; } break; @@ -1861,8 +1909,8 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con UpdateSourceProps(Source, Context); return; } - return Context->setError(AL_INVALID_VALUE, "Invalid distance model: %s\n", - HexPrinter{values[0]}.c_str()); + throw al::context_error{AL_INVALID_VALUE, "Invalid distance model: %s\n", + HexPrinter{values[0]}.c_str()}; } break; @@ -1886,8 +1934,8 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con Source->mSpatialize = *mode; return UpdateSourceProps(Source, Context); } - return Context->setError(AL_INVALID_VALUE, "Invalid source spatialize mode: %s\n", - HexPrinter{values[0]}.c_str()); + throw al::context_error{AL_INVALID_VALUE, "Invalid source spatialize mode: %s\n", + HexPrinter{values[0]}.c_str()}; } break; @@ -1895,19 +1943,18 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con if constexpr(std::is_integral_v) { 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(const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))}; + state == AL_PLAYING || state == AL_PAUSED) + throw al::context_error{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()); + throw al::context_error{AL_INVALID_VALUE, "Invalid stereo mode: %s\n", + HexPrinter{values[0]}.c_str()}; } break; @@ -1923,23 +1970,24 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con 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()); + slot = LookupEffectSlot(Context, slotid); + if(!slot) + throw al::context_error{AL_INVALID_VALUE, "Invalid effect ID %s", + std::to_string(slotid).c_str()}; } - if(sendidx >= device->NumAuxSends) UNLIKELY - return Context->setError(AL_INVALID_VALUE, "Invalid send %s", - std::to_string(sendidx).c_str()); + if(sendidx >= device->NumAuxSends) + throw al::context_error{AL_INVALID_VALUE, "Invalid send %s", + std::to_string(sendidx).c_str()}; auto &send = Source->Send[static_cast(sendidx)]; if(values[2]) { - std::lock_guard _{device->FilterLock}; + std::lock_guard filterlock{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()); + if(!filter) + throw al::context_error{AL_INVALID_VALUE, "Invalid filter ID %s", + std::to_string(filterid).c_str()}; send.Gain = filter->Gain; send.GainHF = filter->GainHF; @@ -1952,9 +2000,9 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con /* Disable filter */ send.Gain = 1.0f; send.GainHF = 1.0f; - send.HFReference = LOWPASSFREQREF; + send.HFReference = LowPassFreqRef; send.GainLF = 1.0f; - send.LFReference = HIGHPASSFREQREF; + send.LFReference = HighPassFreqRef; } /* We must force an update if the current auxiliary slot is valid @@ -1987,112 +2035,108 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con } ERR("Unexpected %s property: 0x%04x\n", PropType::Name(), prop); - Context->setError(AL_INVALID_ENUM, "Invalid source %s property 0x%04x", PropType::Name(), - prop); -} -catch(check_exception&) { + throw al::context_error{AL_INVALID_ENUM, "Invalid source %s property 0x%04x", + PropType::Name(), prop}; } template -auto GetSizeChecker(ALCcontext *const Context, const SourceProp prop, const al::span values) +auto GetSizeChecker(const SourceProp prop, const al::span values) { return [=](size_t expect) -> void { if(values.size() == expect) LIKELY return; - Context->setError(AL_INVALID_ENUM, "Property 0x%04x expects %zu value(s), got %zu", - prop, expect, values.size()); - throw check_size_exception{}; + throw al::context_error{AL_INVALID_ENUM, "Property 0x%04x expects %zu value(s), got %zu", + prop, expect, values.size()}; }; } template -[[nodiscard]] NOINLINE -bool GetProperty(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, - const al::span values) try +NOINLINE void GetProperty(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, + const al::span values) { using std::chrono::duration_cast; - auto CheckSize = GetSizeChecker(Context, prop, values); - ALCdevice *device{Context->mALDevice.get()}; + auto CheckSize = GetSizeChecker(prop, values); + auto *device = Context->mALDevice.get(); switch(prop) { case AL_GAIN: CheckSize(1); values[0] = static_cast(Source->Gain); - return true; + return; case AL_PITCH: CheckSize(1); values[0] = static_cast(Source->Pitch); - return true; + return; case AL_MAX_DISTANCE: CheckSize(1); values[0] = static_cast(Source->MaxDistance); - return true; + return; case AL_ROLLOFF_FACTOR: CheckSize(1); values[0] = static_cast(Source->RolloffFactor); - return true; + return; case AL_REFERENCE_DISTANCE: CheckSize(1); values[0] = static_cast(Source->RefDistance); - return true; + return; case AL_CONE_INNER_ANGLE: CheckSize(1); values[0] = static_cast(Source->InnerAngle); - return true; + return; case AL_CONE_OUTER_ANGLE: CheckSize(1); values[0] = static_cast(Source->OuterAngle); - return true; + return; case AL_MIN_GAIN: CheckSize(1); values[0] = static_cast(Source->MinGain); - return true; + return; case AL_MAX_GAIN: CheckSize(1); values[0] = static_cast(Source->MaxGain); - return true; + return; case AL_CONE_OUTER_GAIN: CheckSize(1); values[0] = static_cast(Source->OuterGain); - return true; + return; case AL_SEC_OFFSET: case AL_SAMPLE_OFFSET: case AL_BYTE_OFFSET: CheckSize(1); values[0] = GetSourceOffset(Source, prop, Context); - return true; + return; case AL_CONE_OUTER_GAINHF: CheckSize(1); values[0] = static_cast(Source->OuterGainHF); - return true; + return; case AL_AIR_ABSORPTION_FACTOR: CheckSize(1); values[0] = static_cast(Source->AirAbsorptionFactor); - return true; + return; case AL_ROOM_ROLLOFF_FACTOR: CheckSize(1); values[0] = static_cast(Source->RoomRolloffFactor); - return true; + return; case AL_DOPPLER_FACTOR: CheckSize(1); values[0] = static_cast(Source->DopplerFactor); - return true; + return; case AL_SAMPLE_RW_OFFSETS_SOFT: if constexpr(std::is_integral_v) @@ -2106,7 +2150,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source * buffer queue. */ values[1] = values[0]; - return true; + return; } } break; @@ -2118,7 +2162,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source CheckSize(1); values[0] = static_cast(Source->Radius); - return true; + return; } else { @@ -2131,7 +2175,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source * buffer queue. */ values[1] = values[0]; - return true; + return; } break; } @@ -2139,14 +2183,24 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source case AL_SUPER_STEREO_WIDTH_SOFT: CheckSize(1); values[0] = static_cast(Source->EnhWidth); - return true; + return; case AL_BYTE_LENGTH_SOFT: case AL_SAMPLE_LENGTH_SOFT: case AL_SEC_LENGTH_SOFT: CheckSize(1); values[0] = GetSourceLength(Source, prop); - return true; + return; + + case AL_PANNING_ENABLED_SOFT: + CheckSize(1); + values[0] = Source->mPanningEnabled; + return; + + case AL_PAN_SOFT: + CheckSize(1); + values[0] = static_cast(Source->mPan); + return; case AL_STEREO_ANGLES: if constexpr(std::is_floating_point_v) @@ -2154,7 +2208,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source CheckSize(2); values[0] = static_cast(Source->StereoPan[0]); values[1] = static_cast(Source->StereoPan[1]); - return true; + return; } break; @@ -2169,7 +2223,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source nanoseconds srcclock{}; values[0] = GetSourceSampleOffset(Source, Context, &srcclock); { - std::lock_guard _{device->StateLock}; + std::lock_guard statelock{device->StateLock}; clocktime = GetClockLatency(device, device->Backend.get()); } if(srcclock == clocktime.ClockTime) @@ -2183,7 +2237,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source const auto diff = std::min(clocktime.Latency, clocktime.ClockTime-srcclock); values[1] = nanoseconds{clocktime.Latency - diff}.count(); } - return true; + return; } break; @@ -2194,7 +2248,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source nanoseconds srcclock{}; values[0] = GetSourceSampleOffset(Source, Context, &srcclock); values[1] = srcclock.count(); - return true; + return; } break; @@ -2209,7 +2263,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source nanoseconds srcclock{}; values[0] = GetSourceSecOffset(Source, Context, &srcclock); { - std::lock_guard _{device->StateLock}; + std::lock_guard statelock{device->StateLock}; clocktime = GetClockLatency(device, device->Backend.get()); } if(srcclock == clocktime.ClockTime) @@ -2223,7 +2277,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source const auto diff = std::min(clocktime.Latency, clocktime.ClockTime-srcclock); values[1] = duration_cast(clocktime.Latency - diff).count(); } - return true; + return; } break; @@ -2234,7 +2288,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source nanoseconds srcclock{}; values[0] = GetSourceSecOffset(Source, Context, &srcclock); values[1] = duration_cast(srcclock).count(); - return true; + return; } break; @@ -2243,21 +2297,21 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source values[0] = static_cast(Source->Position[0]); values[1] = static_cast(Source->Position[1]); values[2] = static_cast(Source->Position[2]); - return true; + return; case AL_VELOCITY: CheckSize(3); values[0] = static_cast(Source->Velocity[0]); values[1] = static_cast(Source->Velocity[1]); values[2] = static_cast(Source->Velocity[2]); - return true; + return; case AL_DIRECTION: CheckSize(3); values[0] = static_cast(Source->Direction[0]); values[1] = static_cast(Source->Direction[1]); values[2] = static_cast(Source->Direction[2]); - return true; + return; case AL_ORIENTATION: CheckSize(6); @@ -2267,7 +2321,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source values[3] = static_cast(Source->OrientUp[0]); values[4] = static_cast(Source->OrientUp[1]); values[5] = static_cast(Source->OrientUp[2]); - return true; + return; case AL_SOURCE_RELATIVE: @@ -2275,7 +2329,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = Source->HeadRelative; - return true; + return; } break; @@ -2284,7 +2338,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = Source->Looping; - return true; + return; } break; @@ -2292,7 +2346,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source if constexpr(std::is_integral_v) { CheckSize(1); - ALbufferQueueItem *BufferList{}; + const ALbufferQueueItem *BufferList{}; /* HACK: This query should technically only return the buffer set * on a static source. However, some apps had used it to detect * when a streaming source changed buffers, so report the current @@ -2306,11 +2360,14 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source else if(Voice *voice{GetSourceVoice(Source, Context)}) { VoiceBufferItem *Current{voice->mCurrentBuffer.load(std::memory_order_relaxed)}; - BufferList = static_cast(Current); + const auto iter = std::find_if(Source->mQueue.cbegin(), Source->mQueue.cend(), + [Current](const ALbufferQueueItem &item) noexcept -> bool + { return &item == Current; }); + BufferList = (iter != Source->mQueue.cend()) ? al::to_address(iter) : nullptr; } ALbuffer *buffer{BufferList ? BufferList->mBuffer : nullptr}; values[0] = buffer ? static_cast(buffer->id) : T{0}; - return true; + return; } break; @@ -2319,7 +2376,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = GetSourceState(Source, GetSourceVoice(Source, Context)); - return true; + return; } break; @@ -2328,7 +2385,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = static_cast(Source->mQueue.size()); - return true; + return; } break; @@ -2360,7 +2417,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source } values[0] = played; } - return true; + return; } break; @@ -2369,7 +2426,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = Source->SourceType; - return true; + return; } break; @@ -2378,7 +2435,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = Source->DryGainHFAuto; - return true; + return; } break; @@ -2387,7 +2444,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = Source->WetGainAuto; - return true; + return; } break; @@ -2396,7 +2453,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = Source->WetGainHFAuto; - return true; + return; } break; @@ -2405,7 +2462,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = EnumFromDirectMode(Source->DirectChannels); - return true; + return; } break; @@ -2414,7 +2471,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = ALenumFromDistanceModel(Source->mDistanceModel); - return true; + return; } break; @@ -2423,7 +2480,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = static_cast(Source->mResampler); - return true; + return; } break; @@ -2432,7 +2489,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = EnumFromSpatializeMode(Source->mSpatialize); - return true; + return; } break; @@ -2441,7 +2498,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = EnumFromStereoMode(Source->mStereoMode); - return true; + return; } break; @@ -2451,19 +2508,15 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source } ERR("Unexpected %s query property: 0x%04x\n", PropType::Name(), prop); - Context->setError(AL_INVALID_ENUM, "Invalid source %s query property 0x%04x", - PropType::Name(), prop); - return false; -} -catch(check_exception&) { - return false; + throw al::context_error{AL_INVALID_ENUM, "Invalid source %s query property 0x%04x", + PropType::Name(), prop}; } void StartSources(ALCcontext *const context, const al::span srchandles, const nanoseconds start_time=nanoseconds::min()) { - ALCdevice *device{context->mALDevice.get()}; + auto *device = context->mALDevice.get(); /* If the device is disconnected, and voices stop on disconnect, go right * to stopped. */ @@ -2552,7 +2605,7 @@ void StartSources(ALCcontext *const context, const al::span srchandle cur->mSourceID = source->id; cur->mState = VChangeState::Play; source->state = AL_PLAYING; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(context->hasEax()) source->eaxCommit(); #endif // ALSOFT_EAX @@ -2572,7 +2625,7 @@ void StartSources(ALCcontext *const context, const al::span srchandle default: assert(voice == nullptr); cur->mOldVoice = nullptr; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(context->hasEax()) source->eaxCommit(); #endif // ALSOFT_EAX @@ -2631,592 +2684,640 @@ void StartSources(ALCcontext *const context, const al::span srchandle } // namespace -AL_API DECL_FUNC2(void, alGenSources, ALsizei, ALuint*) +AL_API DECL_FUNC2(void, alGenSources, ALsizei,n, ALuint*,sources) FORCE_ALIGN void AL_APIENTRY alGenSourcesDirect(ALCcontext *context, ALsizei n, ALuint *sources) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Generating %d sources", n); +try { + if(n < 0) + throw al::context_error{AL_INVALID_VALUE, "Generating %d sources", n}; if(n <= 0) UNLIKELY return; - std::unique_lock srclock{context->mSourceLock}; - ALCdevice *device{context->mALDevice.get()}; - if(static_cast(n) > device->SourcesMax-context->mNumSources) - { - context->setError(AL_OUT_OF_MEMORY, "Exceeding %u source limit (%u + %d)", - device->SourcesMax, context->mNumSources, n); - return; - } - if(!EnsureSources(context, static_cast(n))) - { - context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d source%s", n, (n==1)?"":"s"); - return; - } + auto srclock = std::unique_lock{context->mSourceLock}; + auto *device = context->mALDevice.get(); - if(n == 1) - { - ALsource *source{AllocSource(context)}; - sources[0] = source->id; + const al::span sids{sources, static_cast(n)}; + if(context->mNumSources > device->SourcesMax + || sids.size() > device->SourcesMax-context->mNumSources) + throw al::context_error{AL_OUT_OF_MEMORY, "Exceeding %u source limit (%u + %d)", + device->SourcesMax, context->mNumSources, n}; + if(!EnsureSources(context, sids.size())) + throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d source%s", n, + (n == 1) ? "" : "s"}; -#ifdef ALSOFT_EAX - source->eaxInitialize(context); -#endif // ALSOFT_EAX - } - else - { - std::vector ids; - ids.reserve(static_cast(n)); - do { - ALsource *source{AllocSource(context)}; - ids.emplace_back(source->id); - -#ifdef ALSOFT_EAX - source->eaxInitialize(context); -#endif // ALSOFT_EAX - } while(--n); - std::copy(ids.cbegin(), ids.cend(), sources); - } + std::generate(sids.begin(), sids.end(), [context]{ return AllocSource(context)->id; }); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC2(void, alDeleteSources, ALsizei, const ALuint*) +AL_API DECL_FUNC2(void, alDeleteSources, ALsizei,n, const ALuint*,sources) FORCE_ALIGN void AL_APIENTRY alDeleteSourcesDirect(ALCcontext *context, ALsizei n, const ALuint *sources) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Deleting %d sources", n); +try { + if(n < 0) + throw al::context_error{AL_INVALID_VALUE, "Deleting %d sources", n}; if(n <= 0) UNLIKELY return; - std::lock_guard _{context->mSourceLock}; + std::lock_guard srclock{context->mSourceLock}; /* Check that all Sources are valid */ 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); - if(invsrc != sources_end) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *invsrc); + const al::span sids{sources, static_cast(n)}; + auto invsrc = std::find_if_not(sids.begin(), sids.end(), validate_source); + if(invsrc != sids.end()) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", *invsrc}; /* All good. Delete source IDs. */ auto delete_source = [&context](const ALuint sid) -> void { - ALsource *src{LookupSource(context, sid)}; - if(src) FreeSource(context, src); + if(ALsource *src{LookupSource(context, sid)}) + FreeSource(context, src); }; - std::for_each(sources, sources_end, delete_source); + std::for_each(sids.begin(), sids.end(), delete_source); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC1(ALboolean, alIsSource, ALuint) +AL_API DECL_FUNC1(ALboolean, alIsSource, ALuint,source) FORCE_ALIGN ALboolean AL_APIENTRY alIsSourceDirect(ALCcontext *context, ALuint source) noexcept { - std::lock_guard _{context->mSourceLock}; + std::lock_guard srclock{context->mSourceLock}; if(LookupSource(context, source) != nullptr) return AL_TRUE; return AL_FALSE; } -AL_API DECL_FUNC3(void, alSourcef, ALuint, ALenum, ALfloat) +AL_API DECL_FUNC3(void, alSourcef, ALuint,source, ALenum,param, ALfloat,value) FORCE_ALIGN void AL_APIENTRY alSourcefDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat value) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; - ALsource *Source = LookupSource(context, source); - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; - SetProperty(Source, context, static_cast(param), al::span{&value, 1u}); + SetProperty(Source, context, static_cast(param), {&value, 1u}); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC5(void, alSource3f, ALuint, ALenum, ALfloat, ALfloat, ALfloat) +AL_API DECL_FUNC5(void, alSource3f, ALuint,source, ALenum,param, ALfloat,value1, ALfloat,value2, ALfloat,value3) FORCE_ALIGN void AL_APIENTRY alSource3fDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; - ALsource *Source = LookupSource(context, source); - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; - const float fvals[3]{ value1, value2, value3 }; - SetProperty(Source, context, static_cast(param), al::span{fvals}); + const std::array fvals{value1, value2, value3}; + SetProperty(Source, context, static_cast(param), fvals); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alSourcefv, ALuint, ALenum, const ALfloat*) +AL_API DECL_FUNC3(void, alSourcefv, ALuint,source, ALenum,param, const ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alSourcefvDirect(ALCcontext *context, ALuint source, ALenum param, const ALfloat *values) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; - 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"); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; + if(!values) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; const ALuint count{FloatValsByProp(param)}; SetProperty(Source, context, static_cast(param), al::span{values, count}); } +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); +} -AL_API DECL_FUNCEXT3(void, alSourced,SOFT, ALuint, ALenum, ALdouble) +AL_API DECL_FUNCEXT3(void, alSourced,SOFT, ALuint,source, ALenum,param, ALdouble,value) FORCE_ALIGN void AL_APIENTRY alSourcedDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble value) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; - ALsource *Source = LookupSource(context, source); - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; - SetProperty(Source, context, static_cast(param), al::span{&value, 1}); + SetProperty(Source, context, static_cast(param), {&value, 1}); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNCEXT5(void, alSource3d,SOFT, ALuint, ALenum, ALdouble, ALdouble, ALdouble) +AL_API DECL_FUNCEXT5(void, alSource3d,SOFT, ALuint,source, ALenum,param, ALdouble,value1, ALdouble,value2, ALdouble,value3) FORCE_ALIGN void AL_APIENTRY alSource3dDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; - ALsource *Source = LookupSource(context, source); - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; - const double dvals[3]{value1, value2, value3}; - SetProperty(Source, context, static_cast(param), al::span{dvals}); + const std::array dvals{value1, value2, value3}; + SetProperty(Source, context, static_cast(param), dvals); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNCEXT3(void, alSourcedv,SOFT, ALuint, ALenum, const ALdouble*) +AL_API DECL_FUNCEXT3(void, alSourcedv,SOFT, ALuint,source, ALenum,param, const ALdouble*,values) FORCE_ALIGN void AL_APIENTRY alSourcedvDirectSOFT(ALCcontext *context, ALuint source, ALenum param, const ALdouble *values) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; - 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"); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; + if(!values) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; const ALuint count{DoubleValsByProp(param)}; SetProperty(Source, context, static_cast(param), al::span{values, count}); } +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); +} -AL_API DECL_FUNC3(void, alSourcei, ALuint, ALenum, ALint) +AL_API DECL_FUNC3(void, alSourcei, ALuint,source, ALenum,param, ALint,value) FORCE_ALIGN void AL_APIENTRY alSourceiDirect(ALCcontext *context, ALuint source, ALenum param, ALint value) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; - ALsource *Source = LookupSource(context, source); - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; - SetProperty(Source, context, static_cast(param), al::span{&value, 1u}); + SetProperty(Source, context, static_cast(param), {&value, 1u}); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC5(void, alSource3i, ALuint, ALenum, ALint, ALint, ALint) +AL_API DECL_FUNC5(void, alSource3i, ALuint,buffer, ALenum,param, ALint,value1, ALint,value2, ALint,value3) FORCE_ALIGN void AL_APIENTRY alSource3iDirect(ALCcontext *context, ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; - ALsource *Source = LookupSource(context, source); - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; - const int ivals[3]{ value1, value2, value3 }; - SetProperty(Source, context, static_cast(param), al::span{ivals}); + const std::array ivals{value1, value2, value3}; + SetProperty(Source, context, static_cast(param), ivals); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alSourceiv, ALuint, ALenum, const ALint*) +AL_API DECL_FUNC3(void, alSourceiv, ALuint,source, ALenum,param, const ALint*,values) FORCE_ALIGN void AL_APIENTRY alSourceivDirect(ALCcontext *context, ALuint source, ALenum param, const ALint *values) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; - 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"); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; + if(!values) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; const ALuint count{IntValsByProp(param)}; SetProperty(Source, context, static_cast(param), al::span{values, count}); } +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); +} -AL_API DECL_FUNCEXT3(void, alSourcei64,SOFT, ALuint, ALenum, ALint64SOFT) +AL_API DECL_FUNCEXT3(void, alSourcei64,SOFT, ALuint,source, ALenum,param, ALint64SOFT,value) FORCE_ALIGN void AL_APIENTRY alSourcei64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT value) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; - SetProperty(Source, context, static_cast(param), al::span{&value, 1u}); + SetProperty(Source, context, static_cast(param), {&value, 1u}); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNCEXT5(void, alSource3i64,SOFT, ALuint, ALenum, ALint64SOFT, ALint64SOFT, ALint64SOFT) +AL_API DECL_FUNCEXT5(void, alSource3i64,SOFT, ALuint,source, ALenum,param, ALint64SOFT,value1, ALint64SOFT,value2, ALint64SOFT,value3) FORCE_ALIGN void AL_APIENTRY alSource3i64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; - const int64_t i64vals[3]{ value1, value2, value3 }; - SetProperty(Source, context, static_cast(param), al::span{i64vals}); + const std::array i64vals{value1, value2, value3}; + SetProperty(Source, context, static_cast(param), i64vals); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNCEXT3(void, alSourcei64v,SOFT, ALuint, ALenum, const ALint64SOFT*) +AL_API DECL_FUNCEXT3(void, alSourcei64v,SOFT, ALuint,source, ALenum,param, const ALint64SOFT*,values) FORCE_ALIGN void AL_APIENTRY alSourcei64vDirectSOFT(ALCcontext *context, ALuint source, ALenum param, const ALint64SOFT *values) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; 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"); + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; + if(!values) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; const ALuint count{Int64ValsByProp(param)}; SetProperty(Source, context, static_cast(param), al::span{values, count}); } +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); +} -AL_API DECL_FUNC3(void, alGetSourcef, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetSourcef, ALuint,source, ALenum,param, ALfloat*,value) FORCE_ALIGN void AL_APIENTRY alGetSourcefDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *value) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!value) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; + if(!value) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; - std::ignore = GetProperty(Source, context, static_cast(param), al::span{value, 1}); + GetProperty(Source, context, static_cast(param), al::span{value, 1u}); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC5(void, alGetSource3f, ALuint, ALenum, ALfloat*, ALfloat*, ALfloat*) +AL_API DECL_FUNC5(void, alGetSource3f, ALuint,source, ALenum,param, ALfloat*,value1, ALfloat*,value2, ALfloat*,value3) FORCE_ALIGN void AL_APIENTRY alGetSource3fDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!(value1 && value2 && value3)) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; + if(!(value1 && value2 && value3)) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; - float fvals[3]; - if(GetProperty(Source, context, static_cast(param), al::span{fvals})) - { - *value1 = fvals[0]; - *value2 = fvals[1]; - *value3 = fvals[2]; - } + std::array fvals{}; + GetProperty(Source, context, static_cast(param), fvals); + *value1 = fvals[0]; + *value2 = fvals[1]; + *value3 = fvals[2]; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alGetSourcefv, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetSourcefv, ALuint,source, ALenum,param, ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alGetSourcefvDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *values) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; 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"); + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; + if(!values) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; const ALuint count{FloatValsByProp(param)}; - std::ignore = GetProperty(Source, context, static_cast(param), - al::span{values, count}); + GetProperty(Source, context, static_cast(param), al::span{values, count}); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNCEXT3(void, alGetSourced,SOFT, ALuint, ALenum, ALdouble*) +AL_API DECL_FUNCEXT3(void, alGetSourced,SOFT, ALuint,source, ALenum,param, ALdouble*,value) FORCE_ALIGN void AL_APIENTRY alGetSourcedDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *value) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!value) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; + if(!value) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; - std::ignore = GetProperty(Source, context, static_cast(param), al::span{value, 1}); + GetProperty(Source, context, static_cast(param), al::span{value, 1u}); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNCEXT5(void, alGetSource3d,SOFT, ALuint, ALenum, ALdouble*, ALdouble*, ALdouble*) +AL_API DECL_FUNCEXT5(void, alGetSource3d,SOFT, ALuint,source, ALenum,param, ALdouble*,value1, ALdouble*,value2, ALdouble*,value3) FORCE_ALIGN void AL_APIENTRY alGetSource3dDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!(value1 && value2 && value3)) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; + if(!(value1 && value2 && value3)) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; - double dvals[3]; - if(GetProperty(Source, context, static_cast(param), al::span{dvals})) - { - *value1 = dvals[0]; - *value2 = dvals[1]; - *value3 = dvals[2]; - } + std::array dvals{}; + GetProperty(Source, context, static_cast(param), dvals); + *value1 = dvals[0]; + *value2 = dvals[1]; + *value3 = dvals[2]; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNCEXT3(void, alGetSourcedv,SOFT, ALuint, ALenum, ALdouble*) +AL_API DECL_FUNCEXT3(void, alGetSourcedv,SOFT, ALuint,source, ALenum,param, ALdouble*,values) FORCE_ALIGN void AL_APIENTRY alGetSourcedvDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *values) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; 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"); + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; + if(!values) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; const ALuint count{DoubleValsByProp(param)}; - std::ignore = GetProperty(Source, context, static_cast(param), - al::span{values, count}); + GetProperty(Source, context, static_cast(param), al::span{values, count}); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alGetSourcei, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetSourcei, ALuint,source, ALenum,param, ALint*,value) FORCE_ALIGN void AL_APIENTRY alGetSourceiDirect(ALCcontext *context, ALuint source, ALenum param, ALint *value) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!value) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; + if(!value) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; - std::ignore = GetProperty(Source, context, static_cast(param), al::span{value, 1}); + GetProperty(Source, context, static_cast(param), al::span{value, 1u}); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC5(void, alGetSource3i, ALuint, ALenum, ALint*, ALint*, ALint*) +AL_API DECL_FUNC5(void, alGetSource3i, ALuint,source, ALenum,param, ALint*,value1, ALint*,value2, ALint*,value3) FORCE_ALIGN void AL_APIENTRY alGetSource3iDirect(ALCcontext *context, ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!(value1 && value2 && value3)) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; + if(!(value1 && value2 && value3)) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; - int ivals[3]; - if(GetProperty(Source, context, static_cast(param), al::span{ivals})) - { - *value1 = ivals[0]; - *value2 = ivals[1]; - *value3 = ivals[2]; - } + std::array ivals{}; + GetProperty(Source, context, static_cast(param), ivals); + *value1 = ivals[0]; + *value2 = ivals[1]; + *value3 = ivals[2]; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC3(void, alGetSourceiv, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetSourceiv, ALuint,source, ALenum,param, ALint*,values) FORCE_ALIGN void AL_APIENTRY alGetSourceivDirect(ALCcontext *context, ALuint source, ALenum param, ALint *values) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; 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"); + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; + if(!values) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; const ALuint count{IntValsByProp(param)}; - std::ignore = GetProperty(Source, context, static_cast(param), - al::span{values, count}); + GetProperty(Source, context, static_cast(param), al::span{values, count}); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNCEXT3(void, alGetSourcei64,SOFT, ALuint, ALenum, ALint64SOFT*) +AL_API DECL_FUNCEXT3(void, alGetSourcei64,SOFT, ALuint,source, ALenum,param, ALint64SOFT*,value) FORCE_ALIGN void AL_APIENTRY alGetSourcei64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *value) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!value) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; + if(!value) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; - std::ignore = GetProperty(Source, context, static_cast(param), al::span{value, 1}); + GetProperty(Source, context, static_cast(param), al::span{value, 1u}); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNCEXT5(void, alGetSource3i64,SOFT, ALuint, ALenum, ALint64SOFT*, ALint64SOFT*, ALint64SOFT*) +AL_API DECL_FUNCEXT5(void, alGetSource3i64,SOFT, ALuint,source, ALenum,param, ALint64SOFT*,value1, ALint64SOFT*,value2, ALint64SOFT*,value3) FORCE_ALIGN void AL_APIENTRY alGetSource3i64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!(value1 && value2 && value3)) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; + if(!(value1 && value2 && value3)) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; - int64_t i64vals[3]; - if(GetProperty(Source, context, static_cast(param), al::span{i64vals})) - { - *value1 = i64vals[0]; - *value2 = i64vals[1]; - *value3 = i64vals[2]; - } + std::array i64vals{}; + GetProperty(Source, context, static_cast(param), i64vals); + *value1 = i64vals[0]; + *value2 = i64vals[1]; + *value3 = i64vals[2]; +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNCEXT3(void, alGetSourcei64v,SOFT, ALuint, ALenum, ALint64SOFT*) +AL_API DECL_FUNCEXT3(void, alGetSourcei64v,SOFT, ALuint,source, ALenum,param, ALint64SOFT*,values) FORCE_ALIGN void AL_APIENTRY alGetSourcei64vDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *values) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; 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"); + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; + if(!values) + throw al::context_error{AL_INVALID_VALUE, "NULL pointer"}; const ALuint count{Int64ValsByProp(param)}; - std::ignore = GetProperty(Source, context, static_cast(param), - al::span{values, count}); + GetProperty(Source, context, static_cast(param), al::span{values, count}); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC1(void, alSourcePlay, ALuint) +AL_API DECL_FUNC1(void, alSourcePlay, ALuint,source) FORCE_ALIGN void AL_APIENTRY alSourcePlayDirect(ALCcontext *context, ALuint source) noexcept -{ - std::lock_guard _{context->mSourceLock}; - ALsource *srchandle{LookupSource(context, source)}; - if(!srchandle) - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); +try { + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; - StartSources(context, {&srchandle, 1}); + StartSources(context, {&Source, 1}); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -FORCE_ALIGN DECL_FUNCEXT2(void, alSourcePlayAtTime,SOFT, ALuint, ALint64SOFT) +FORCE_ALIGN DECL_FUNCEXT2(void, alSourcePlayAtTime,SOFT, ALuint,source, ALint64SOFT,start_time) FORCE_ALIGN void AL_APIENTRY alSourcePlayAtTimeDirectSOFT(ALCcontext *context, ALuint source, ALint64SOFT start_time) noexcept -{ - if(start_time < 0) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Invalid time point %" PRId64, start_time); +try { + if(start_time < 0) + throw al::context_error{AL_INVALID_VALUE, "Invalid time point %" PRId64, start_time}; - std::lock_guard _{context->mSourceLock}; - ALsource *srchandle{LookupSource(context, source)}; - if(!srchandle) - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", source}; - StartSources(context, {&srchandle, 1}, nanoseconds{start_time}); + StartSources(context, {&Source, 1}, nanoseconds{start_time}); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } -AL_API DECL_FUNC2(void, alSourcePlayv, ALsizei, const ALuint*) +AL_API DECL_FUNC2(void, alSourcePlayv, ALsizei,n, const ALuint*,sources) FORCE_ALIGN void AL_APIENTRY alSourcePlayvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Playing %d sources", n); +try { + if(n < 0) + throw al::context_error{AL_INVALID_VALUE, "Playing %d sources", n}; if(n <= 0) UNLIKELY return; - std::vector extra_sources; - std::array source_storage; - al::span srchandles; - if(static_cast(n) <= source_storage.size()) LIKELY - srchandles = al::span{source_storage}.first(static_cast(n)); - else + al::span sids{sources, static_cast(n)}; + source_store_variant source_store; + const auto srchandles = [&source_store](size_t count) -> al::span { - extra_sources.resize(static_cast(n)); - srchandles = extra_sources; - } + if(count > std::tuple_size_v) + return al::span{source_store.emplace(count)}; + return al::span{source_store.emplace()}.first(count); + }(sids.size()); - std::lock_guard _{context->mSourceLock}; - for(auto &srchdl : srchandles) + std::lock_guard sourcelock{context->mSourceLock}; + auto lookup_src = [context](const ALuint sid) -> ALsource* { - srchdl = LookupSource(context, *sources); - if(!srchdl) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources); - ++sources; - } + if(ALsource *src{LookupSource(context, sid)}) + return src; + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", sid}; + }; + std::transform(sids.cbegin(), sids.cend(), srchandles.begin(), lookup_src); StartSources(context, srchandles); } +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); +} -FORCE_ALIGN DECL_FUNCEXT3(void, alSourcePlayAtTimev,SOFT, ALsizei, const ALuint*, ALint64SOFT) +FORCE_ALIGN DECL_FUNCEXT3(void, alSourcePlayAtTimev,SOFT, ALsizei,n, const ALuint*,sources, ALint64SOFT,start_time) FORCE_ALIGN void AL_APIENTRY alSourcePlayAtTimevDirectSOFT(ALCcontext *context, ALsizei n, const ALuint *sources, ALint64SOFT start_time) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Playing %d sources", n); +try { + if(n < 0) + throw al::context_error{AL_INVALID_VALUE, "Playing %d sources", n}; if(n <= 0) UNLIKELY return; - if(start_time < 0) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Invalid time point %" PRId64, start_time); + if(start_time < 0) + throw al::context_error{AL_INVALID_VALUE, "Invalid time point %" PRId64, start_time}; - std::vector extra_sources; - std::array source_storage; - al::span srchandles; - if(static_cast(n) <= source_storage.size()) LIKELY - srchandles = al::span{source_storage}.first(static_cast(n)); - else + al::span sids{sources, static_cast(n)}; + source_store_variant source_store; + const auto srchandles = [&source_store](size_t count) -> al::span { - extra_sources.resize(static_cast(n)); - srchandles = extra_sources; - } + if(count > std::tuple_size_v) + return al::span{source_store.emplace(count)}; + return al::span{source_store.emplace()}.first(count); + }(sids.size()); - std::lock_guard _{context->mSourceLock}; - for(auto &srchdl : srchandles) + std::lock_guard sourcelock{context->mSourceLock}; + auto lookup_src = [context](const ALuint sid) -> ALsource* { - srchdl = LookupSource(context, *sources); - if(!srchdl) - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources); - ++sources; - } + if(ALsource *src{LookupSource(context, sid)}) + return src; + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", sid}; + }; + std::transform(sids.cbegin(), sids.cend(), srchandles.begin(), lookup_src); StartSources(context, srchandles, nanoseconds{start_time}); } +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); +} -AL_API DECL_FUNC1(void, alSourcePause, ALuint) +AL_API DECL_FUNC1(void, alSourcePause, ALuint,source) FORCE_ALIGN void AL_APIENTRY alSourcePauseDirect(ALCcontext *context, ALuint source) noexcept { alSourcePausevDirect(context, 1, &source); } -AL_API DECL_FUNC2(void, alSourcePausev, ALsizei, const ALuint*) +AL_API DECL_FUNC2(void, alSourcePausev, ALsizei,n, const ALuint*,sources) FORCE_ALIGN void AL_APIENTRY alSourcePausevDirect(ALCcontext *context, ALsizei n, const ALuint *sources) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Pausing %d sources", n); +try { + if(n < 0) + throw al::context_error{AL_INVALID_VALUE, "Pausing %d sources", n}; if(n <= 0) UNLIKELY return; - std::vector extra_sources; - std::array source_storage; - al::span srchandles; - if(static_cast(n) <= source_storage.size()) LIKELY - srchandles = al::span{source_storage}.first(static_cast(n)); - else + al::span sids{sources, static_cast(n)}; + source_store_variant source_store; + const auto srchandles = [&source_store](size_t count) -> al::span { - extra_sources.resize(static_cast(n)); - srchandles = extra_sources; - } + if(count > std::tuple_size_v) + return al::span{source_store.emplace(count)}; + return al::span{source_store.emplace()}.first(count); + }(sids.size()); - std::lock_guard _{context->mSourceLock}; - for(auto &srchdl : srchandles) + std::lock_guard sourcelock{context->mSourceLock}; + auto lookup_src = [context](const ALuint sid) -> ALsource* { - srchdl = LookupSource(context, *sources); - if(!srchdl) - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources); - ++sources; - } + if(ALsource *src{LookupSource(context, sid)}) + return src; + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", sid}; + }; + std::transform(sids.cbegin(), sids.cend(), srchandles.begin(), lookup_src); /* Pausing has to be done in two steps. First, for each source that's * detected to be playing, chamge the voice (asynchronously) to @@ -3256,39 +3357,40 @@ FORCE_ALIGN void AL_APIENTRY alSourcePausevDirect(ALCcontext *context, ALsizei n } } } +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); +} -AL_API DECL_FUNC1(void, alSourceStop, ALuint) +AL_API DECL_FUNC1(void, alSourceStop, ALuint,source) FORCE_ALIGN void AL_APIENTRY alSourceStopDirect(ALCcontext *context, ALuint source) noexcept { alSourceStopvDirect(context, 1, &source); } -AL_API DECL_FUNC2(void, alSourceStopv, ALsizei, const ALuint*) +AL_API DECL_FUNC2(void, alSourceStopv, ALsizei,n, const ALuint*,sources) FORCE_ALIGN void AL_APIENTRY alSourceStopvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Stopping %d sources", n); +try { + if(n < 0) + throw al::context_error{AL_INVALID_VALUE, "Stopping %d sources", n}; if(n <= 0) UNLIKELY return; - std::vector extra_sources; - std::array source_storage; - al::span srchandles; - if(static_cast(n) <= source_storage.size()) LIKELY - srchandles = al::span{source_storage}.first(static_cast(n)); - else + al::span sids{sources, static_cast(n)}; + source_store_variant source_store; + const auto srchandles = [&source_store](size_t count) -> al::span { - extra_sources.resize(static_cast(n)); - srchandles = extra_sources; - } + if(count > std::tuple_size_v) + return al::span{source_store.emplace(count)}; + return al::span{source_store.emplace()}.first(count); + }(sids.size()); - std::lock_guard _{context->mSourceLock}; - for(auto &srchdl : srchandles) + std::lock_guard sourcelock{context->mSourceLock}; + auto lookup_src = [context](const ALuint sid) -> ALsource* { - srchdl = LookupSource(context, *sources); - if(!srchdl) - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources); - ++sources; - } + if(ALsource *src{LookupSource(context, sid)}) + return src; + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", sid}; + }; + std::transform(sids.cbegin(), sids.cend(), srchandles.begin(), lookup_src); VoiceChange *tail{}, *cur{}; for(ALsource *source : srchandles) @@ -3310,44 +3412,45 @@ FORCE_ALIGN void AL_APIENTRY alSourceStopvDirect(ALCcontext *context, ALsizei n, } source->Offset = 0.0; source->OffsetType = AL_NONE; - source->VoiceIdx = INVALID_VOICE_IDX; + source->VoiceIdx = InvalidVoiceIndex; } if(tail) LIKELY SendVoiceChanges(context, tail); } +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); +} -AL_API DECL_FUNC1(void, alSourceRewind, ALuint) +AL_API DECL_FUNC1(void, alSourceRewind, ALuint,source) FORCE_ALIGN void AL_APIENTRY alSourceRewindDirect(ALCcontext *context, ALuint source) noexcept { alSourceRewindvDirect(context, 1, &source); } -AL_API DECL_FUNC2(void, alSourceRewindv, ALsizei, const ALuint*) +AL_API DECL_FUNC2(void, alSourceRewindv, ALsizei,n, const ALuint*,sources) FORCE_ALIGN void AL_APIENTRY alSourceRewindvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Rewinding %d sources", n); +try { + if(n < 0) + throw al::context_error{AL_INVALID_VALUE, "Rewinding %d sources", n}; if(n <= 0) UNLIKELY return; - std::vector extra_sources; - std::array source_storage; - al::span srchandles; - if(static_cast(n) <= source_storage.size()) LIKELY - srchandles = al::span{source_storage}.first(static_cast(n)); - else + al::span sids{sources, static_cast(n)}; + source_store_variant source_store; + const auto srchandles = [&source_store](size_t count) -> al::span { - extra_sources.resize(static_cast(n)); - srchandles = extra_sources; - } + if(count > std::tuple_size_v) + return al::span{source_store.emplace(count)}; + return al::span{source_store.emplace()}.first(count); + }(sids.size()); - std::lock_guard _{context->mSourceLock}; - for(auto &srchdl : srchandles) + std::lock_guard sourcelock{context->mSourceLock}; + auto lookup_src = [context](const ALuint sid) -> ALsource* { - srchdl = LookupSource(context, *sources); - if(!srchdl) - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources); - ++sources; - } + if(ALsource *src{LookupSource(context, sid)}) + return src; + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", sid}; + }; + std::transform(sids.cbegin(), sids.cend(), srchandles.begin(), lookup_src); VoiceChange *tail{}, *cur{}; for(ALsource *source : srchandles) @@ -3371,32 +3474,35 @@ FORCE_ALIGN void AL_APIENTRY alSourceRewindvDirect(ALCcontext *context, ALsizei } source->Offset = 0.0; source->OffsetType = AL_NONE; - source->VoiceIdx = INVALID_VOICE_IDX; + source->VoiceIdx = InvalidVoiceIndex; } if(tail) LIKELY SendVoiceChanges(context, tail); } +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); +} -AL_API DECL_FUNC3(void, alSourceQueueBuffers, ALuint, ALsizei, const ALuint*) +AL_API DECL_FUNC3(void, alSourceQueueBuffers, ALuint,source, ALsizei,nb, const ALuint*,buffers) FORCE_ALIGN void AL_APIENTRY alSourceQueueBuffersDirect(ALCcontext *context, ALuint src, ALsizei nb, const ALuint *buffers) noexcept -{ - if(nb < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Queueing %d buffers", nb); +try { + if(nb < 0) + throw al::context_error{AL_INVALID_VALUE, "Queueing %d buffers", nb}; if(nb <= 0) UNLIKELY return; - std::lock_guard _{context->mSourceLock}; + std::lock_guard sourcelock{context->mSourceLock}; ALsource *source{LookupSource(context,src)}; - if(!source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", src); + if(!source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", src}; /* Can't queue on a Static Source */ - if(source->SourceType == AL_STATIC) UNLIKELY - return context->setError(AL_INVALID_OPERATION, "Queueing onto static source %u", src); + if(source->SourceType == AL_STATIC) + throw al::context_error{AL_INVALID_OPERATION, "Queueing onto static source %u", src}; /* Check for a valid Buffer, for its frequency and format */ - ALCdevice *device{context->mALDevice.get()}; + auto *device = context->mALDevice.get(); ALbuffer *BufferFmt{nullptr}; for(auto &item : source->mQueue) { @@ -3405,90 +3511,85 @@ FORCE_ALIGN void AL_APIENTRY alSourceQueueBuffersDirect(ALCcontext *context, ALu } std::unique_lock buflock{device->BufferLock}; + const auto bids = al::span{buffers, static_cast(nb)}; const size_t NewListStart{source->mQueue.size()}; - ALbufferQueueItem *BufferList{nullptr}; - for(ALsizei i{0};i < nb;i++) - { - bool fmt_mismatch{false}; - ALbuffer *buffer{nullptr}; - if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == nullptr) - { - context->setError(AL_INVALID_NAME, "Queueing invalid buffer ID %u", buffers[i]); - goto buffer_error; - } - if(buffer) + try { + ALbufferQueueItem *BufferList{nullptr}; + std::for_each(bids.cbegin(), bids.cend(), + [source,device,&BufferFmt,&BufferList](const ALuint bid) { - if(buffer->mSampleRate < 1) - { - context->setError(AL_INVALID_OPERATION, "Queueing buffer %u with no format", - buffer->id); - goto buffer_error; - } - if(buffer->mCallback) - { - context->setError(AL_INVALID_OPERATION, "Queueing callback buffer %u", buffer->id); - goto buffer_error; - } - if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) + ALbuffer *buffer{bid ? LookupBuffer(device, bid) : nullptr}; + if(bid && !buffer) + throw al::context_error{AL_INVALID_NAME, "Queueing invalid buffer ID %u", bid}; + + if(buffer) { - context->setError(AL_INVALID_OPERATION, - "Queueing non-persistently mapped buffer %u", buffer->id); - goto buffer_error; + if(buffer->mSampleRate < 1) + throw al::context_error{AL_INVALID_OPERATION, + "Queueing buffer %u with no format", buffer->id}; + + if(buffer->mCallback) + throw al::context_error{AL_INVALID_OPERATION, "Queueing callback buffer %u", + buffer->id}; + + if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) + throw al::context_error{AL_INVALID_OPERATION, + "Queueing non-persistently mapped buffer %u", buffer->id}; } - } - source->mQueue.emplace_back(); - if(!BufferList) - BufferList = &source->mQueue.back(); - else - { - auto &item = source->mQueue.back(); - BufferList->mNext.store(&item, std::memory_order_relaxed); - BufferList = &item; - } - if(!buffer) continue; - BufferList->mBlockAlign = buffer->mBlockAlign; - BufferList->mSampleLen = buffer->mSampleLen; - BufferList->mLoopEnd = buffer->mSampleLen; - BufferList->mSamples = buffer->mData.data(); - BufferList->mBuffer = buffer; - IncrementRef(buffer->ref); - - if(BufferFmt == nullptr) - BufferFmt = buffer; - else - { - fmt_mismatch |= BufferFmt->mSampleRate != buffer->mSampleRate; - fmt_mismatch |= BufferFmt->mChannels != buffer->mChannels; - fmt_mismatch |= BufferFmt->mType != buffer->mType; - if(BufferFmt->isBFormat()) + source->mQueue.emplace_back(); + if(!BufferList) + BufferList = &source->mQueue.back(); + else { - fmt_mismatch |= BufferFmt->mAmbiLayout != buffer->mAmbiLayout; - fmt_mismatch |= BufferFmt->mAmbiScaling != buffer->mAmbiScaling; + auto &item = source->mQueue.back(); + BufferList->mNext.store(&item, std::memory_order_relaxed); + BufferList = &item; } - fmt_mismatch |= BufferFmt->mAmbiOrder != buffer->mAmbiOrder; - } - if(fmt_mismatch) UNLIKELY - { - context->setError(AL_INVALID_OPERATION, "Queueing buffer with mismatched format\n" - " Expected: %uhz, %s, %s ; Got: %uhz, %s, %s\n", BufferFmt->mSampleRate, - NameFromFormat(BufferFmt->mType), NameFromFormat(BufferFmt->mChannels), - buffer->mSampleRate, NameFromFormat(buffer->mType), - NameFromFormat(buffer->mChannels)); - - buffer_error: - /* A buffer failed (invalid ID or format), so unlock and release - * each buffer we had. - */ - auto iter = source->mQueue.begin() + ptrdiff_t(NewListStart); - for(;iter != source->mQueue.end();++iter) + if(!buffer) return; + BufferList->mBlockAlign = buffer->mBlockAlign; + BufferList->mSampleLen = buffer->mSampleLen; + BufferList->mLoopEnd = buffer->mSampleLen; + BufferList->mSamples = buffer->mData; + BufferList->mBuffer = buffer; + IncrementRef(buffer->ref); + + bool fmt_mismatch{false}; + if(BufferFmt == nullptr) + BufferFmt = buffer; + else { - if(ALbuffer *buf{iter->mBuffer}) - DecrementRef(buf->ref); + fmt_mismatch |= BufferFmt->mSampleRate != buffer->mSampleRate; + fmt_mismatch |= BufferFmt->mChannels != buffer->mChannels; + fmt_mismatch |= BufferFmt->mType != buffer->mType; + if(BufferFmt->isBFormat()) + { + fmt_mismatch |= BufferFmt->mAmbiLayout != buffer->mAmbiLayout; + fmt_mismatch |= BufferFmt->mAmbiScaling != buffer->mAmbiScaling; + } + fmt_mismatch |= BufferFmt->mAmbiOrder != buffer->mAmbiOrder; } - source->mQueue.resize(NewListStart); - return; + if(fmt_mismatch) + throw al::context_error{AL_INVALID_OPERATION, + "Queueing buffer with mismatched format\n" + " Expected: %uhz, %s, %s ; Got: %uhz, %s, %s\n", BufferFmt->mSampleRate, + NameFromFormat(BufferFmt->mType), NameFromFormat(BufferFmt->mChannels), + buffer->mSampleRate, NameFromFormat(buffer->mType), + NameFromFormat(buffer->mChannels)}; + }); + } + catch(...) { + /* A buffer failed (invalid ID or format), or there was some other + * unexpected error, so unlock and release each buffer we had. + */ + auto iter = source->mQueue.begin() + ptrdiff_t(NewListStart); + for(;iter != source->mQueue.end();++iter) + { + if(ALbuffer *buf{iter->mBuffer}) + DecrementRef(buf->ref); } + source->mQueue.resize(NewListStart); + throw; } /* All buffers good. */ buflock.unlock(); @@ -3502,28 +3603,31 @@ FORCE_ALIGN void AL_APIENTRY alSourceQueueBuffersDirect(ALCcontext *context, ALu (iter-1)->mNext.store(al::to_address(iter), std::memory_order_release); } } +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); +} -AL_API DECL_FUNC3(void, alSourceUnqueueBuffers, ALuint, ALsizei, ALuint*) +AL_API DECL_FUNC3(void, alSourceUnqueueBuffers, ALuint,source, ALsizei,nb, ALuint*,buffers) FORCE_ALIGN void AL_APIENTRY alSourceUnqueueBuffersDirect(ALCcontext *context, ALuint src, ALsizei nb, ALuint *buffers) noexcept -{ - if(nb < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Unqueueing %d buffers", nb); +try { + if(nb < 0) + throw al::context_error{AL_INVALID_VALUE, "Unqueueing %d buffers", nb}; if(nb <= 0) UNLIKELY return; - std::lock_guard _{context->mSourceLock}; + std::lock_guard sourcelock{context->mSourceLock}; ALsource *source{LookupSource(context,src)}; - if(!source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", src); + if(!source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", src}; - if(source->SourceType != AL_STREAMING) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Unqueueing from a non-streaming source %u", - src); - if(source->Looping) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Unqueueing from looping source %u", src); + if(source->SourceType != AL_STREAMING) + throw al::context_error{AL_INVALID_VALUE, "Unqueueing from a non-streaming source %u",src}; + if(source->Looping) + throw al::context_error{AL_INVALID_VALUE, "Unqueueing from looping source %u", src}; /* Make sure enough buffers have been processed to unqueue. */ - uint processed{0u}; + const al::span bids{buffers, static_cast(nb)}; + size_t processed{0}; if(source->state != AL_INITIAL) LIKELY { VoiceBufferItem *Current{nullptr}; @@ -3536,21 +3640,25 @@ FORCE_ALIGN void AL_APIENTRY alSourceUnqueueBuffersDirect(ALCcontext *context, A ++processed; } } - if(processed < static_cast(nb)) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Unqueueing %d buffer%s (only %u processed)", - nb, (nb==1)?"":"s", processed); + if(processed < bids.size()) + throw al::context_error{AL_INVALID_VALUE, "Unqueueing %d buffer%s (only %zu processed)", + nb, (nb==1)?"":"s", processed}; - do { + std::generate(bids.begin(), bids.end(), [source]() noexcept -> ALuint + { auto &head = source->mQueue.front(); + ALuint bid{0}; if(ALbuffer *buffer{head.mBuffer}) { - *(buffers++) = buffer->id; + bid = buffer->id; DecrementRef(buffer->ref); } - else - *(buffers++) = 0; source->mQueue.pop_front(); - } while(--nb); + return bid; + }); +} +catch(al::context_error& e) { + context->setError(e.errorCode(), "%s", e.what()); } @@ -3563,21 +3671,21 @@ AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint, ALsizei, const ALu } -ALsource::ALsource() +ALsource::ALsource() noexcept { Direct.Gain = 1.0f; Direct.GainHF = 1.0f; - Direct.HFReference = LOWPASSFREQREF; + Direct.HFReference = LowPassFreqRef; Direct.GainLF = 1.0f; - Direct.LFReference = HIGHPASSFREQREF; + Direct.LFReference = HighPassFreqRef; for(auto &send : Send) { send.Slot = nullptr; send.Gain = 1.0f; send.GainHF = 1.0f; - send.HFReference = LOWPASSFREQREF; + send.HFReference = LowPassFreqRef; send.GainLF = 1.0f; - send.LFReference = HIGHPASSFREQREF; + send.LFReference = HighPassFreqRef; } } @@ -3596,13 +3704,13 @@ ALsource::~ALsource() void UpdateAllSourceProps(ALCcontext *context) { - std::lock_guard _{context->mSourceLock}; + std::lock_guard srclock{context->mSourceLock}; auto voicelist = context->getVoicesSpan(); ALuint vidx{0u}; for(Voice *voice : voicelist) { ALuint sid{voice->mSourceID.load(std::memory_order_acquire)}; - ALsource *source = sid ? LookupSource(context, sid) : nullptr; + ALsource *source{sid ? LookupSource(context, sid) : nullptr}; if(source && source->VoiceIdx == vidx) { if(std::exchange(source->mPropsDirty, false)) @@ -3614,11 +3722,11 @@ void UpdateAllSourceProps(ALCcontext *context) void ALsource::SetName(ALCcontext *context, ALuint id, std::string_view name) { - std::lock_guard _{context->mSourceLock}; + std::lock_guard srclock{context->mSourceLock}; auto source = LookupSource(context, id); - if(!source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", id); + if(!source) + throw al::context_error{AL_INVALID_NAME, "Invalid source ID %u", id}; context->mSourceNames.insert_or_assign(id, name); } @@ -3634,18 +3742,15 @@ SourceSubList::~SourceSubList() { const int idx{al::countr_zero(usemask)}; usemask &= ~(1_u64 << idx); - std::destroy_at(Sources+idx); + std::destroy_at(al::to_address(Sources->begin() + idx)); } FreeMask = ~usemask; - al_free(Sources); + SubListAllocator{}.deallocate(Sources, 1); Sources = nullptr; } -#ifdef ALSOFT_EAX -constexpr const ALsource::EaxFxSlotIds ALsource::eax4_fx_slot_ids; -constexpr const ALsource::EaxFxSlotIds ALsource::eax5_fx_slot_ids; - +#if ALSOFT_EAX void ALsource::eaxInitialize(ALCcontext *context) noexcept { assert(context != nullptr); @@ -3918,29 +4023,30 @@ void ALsource::eax4_translate(const Eax4Props& src, Eax5Props& dst) noexcept // Active FX slots. // - for (auto i = 0; i < EAX50_MAX_ACTIVE_FXSLOTS; ++i) { - auto& dst_id = dst.active_fx_slots.guidActiveFXSlots[i]; - - if (i < EAX40_MAX_ACTIVE_FXSLOTS) { - const auto& src_id = src.active_fx_slots.guidActiveFXSlots[i]; - - if (src_id == EAX_NULL_GUID) - dst_id = EAX_NULL_GUID; - else if (src_id == EAX_PrimaryFXSlotID) - dst_id = EAX_PrimaryFXSlotID; - else if (src_id == EAXPROPERTYID_EAX40_FXSlot0) - dst_id = EAXPROPERTYID_EAX50_FXSlot0; - else if (src_id == EAXPROPERTYID_EAX40_FXSlot1) - dst_id = EAXPROPERTYID_EAX50_FXSlot1; - else if (src_id == EAXPROPERTYID_EAX40_FXSlot2) - dst_id = EAXPROPERTYID_EAX50_FXSlot2; - else if (src_id == EAXPROPERTYID_EAX40_FXSlot3) - dst_id = EAXPROPERTYID_EAX50_FXSlot3; - else - assert(false && "Unknown active FX slot ID."); - } else - dst_id = EAX_NULL_GUID; - } + auto translate_slotid = [](const GUID &src_id) -> GUID + { + if(src_id == EAX_NULL_GUID) + return EAX_NULL_GUID; + if(src_id == EAX_PrimaryFXSlotID) + return EAX_PrimaryFXSlotID; + if(src_id == EAXPROPERTYID_EAX40_FXSlot0) + return EAXPROPERTYID_EAX50_FXSlot0; + if(src_id == EAXPROPERTYID_EAX40_FXSlot1) + return EAXPROPERTYID_EAX50_FXSlot1; + if(src_id == EAXPROPERTYID_EAX40_FXSlot2) + return EAXPROPERTYID_EAX50_FXSlot2; + if(src_id == EAXPROPERTYID_EAX40_FXSlot3) + return EAXPROPERTYID_EAX50_FXSlot3; + + UNLIKELY + ERR("Unexpected active FX slot ID\n"); + return EAX_NULL_GUID; + }; + const auto src_slots = al::span{src.active_fx_slots.guidActiveFXSlots}; + const auto dst_slots = al::span{dst.active_fx_slots.guidActiveFXSlots}; + auto dstiter = std::transform(src_slots.cbegin(), src_slots.cend(), dst_slots.begin(), + translate_slotid); + std::fill(dstiter, dst_slots.end(), EAX_NULL_GUID); // Speaker levels. // @@ -3961,62 +4067,55 @@ float ALsource::eax_calculate_dst_occlusion_mb( EaxAlLowPassParam ALsource::eax_create_direct_filter_param() const noexcept { - auto gain_mb = - static_cast(mEax.source.lDirect) + - (static_cast(mEax.source.lObstruction) * mEax.source.flObstructionLFRatio) + - eax_calculate_dst_occlusion_mb( - mEax.source.lOcclusion, - mEax.source.flOcclusionDirectRatio, - mEax.source.flOcclusionLFRatio); - - const auto has_source_occlusion = (mEax.source.lOcclusion != 0); + const auto &source = mEax.source; - auto gain_hf_mb = - static_cast(mEax.source.lDirectHF) + - static_cast(mEax.source.lObstruction); + auto gain_mb = static_cast(source.lObstruction) * source.flObstructionLFRatio; + auto gainhf_mb = static_cast(source.lObstruction); for(size_t i{0};i < EAX_MAX_FXSLOTS;++i) { if(!mEaxActiveFxSlots[i]) continue; - if(has_source_occlusion) + if(source.lOcclusion != 0) { 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_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_environmental_fx && is_primary) { - gain_mb += eax_calculate_dst_occlusion_mb( - mEax.source.lOcclusion, - mEax.source.flOcclusionDirectRatio, - mEax.source.flOcclusionLFRatio); + gain_mb += eax_calculate_dst_occlusion_mb(source.lOcclusion, + source.flOcclusionDirectRatio, source.flOcclusionLFRatio); - gain_hf_mb += static_cast(mEax.source.lOcclusion) * mEax.source.flOcclusionDirectRatio; + gainhf_mb += static_cast(source.lOcclusion) * source.flOcclusionDirectRatio; } } const auto& send = mEax.sends[i]; - if(send.lOcclusion != 0) { - gain_mb += eax_calculate_dst_occlusion_mb( - send.lOcclusion, - send.flOcclusionDirectRatio, + gain_mb += eax_calculate_dst_occlusion_mb(send.lOcclusion, send.flOcclusionDirectRatio, send.flOcclusionLFRatio); - gain_hf_mb += static_cast(send.lOcclusion) * send.flOcclusionDirectRatio; + gainhf_mb += static_cast(send.lOcclusion) * send.flOcclusionDirectRatio; } } - const auto al_low_pass_param = EaxAlLowPassParam{ - level_mb_to_gain(gain_mb), - minf(level_mb_to_gain(gain_hf_mb), 1.0f)}; + /* gainhf_mb is the absolute mBFS of the filter's high-frequency volume, + * and gain_mb is the absolute mBFS of the filter's low-frequency volume. + * Adjust the HF volume to be relative to the LF volume, to make the + * appropriate main and relative HF filter volumes. + * + * Also add the Direct and DirectHF properties to the filter, which are + * already the main and relative HF volumes. + */ + gainhf_mb -= gain_mb - static_cast(source.lDirectHF); + gain_mb += static_cast(source.lDirect); - return al_low_pass_param; + return EaxAlLowPassParam{level_mb_to_gain(gain_mb), + std::min(level_mb_to_gain(gainhf_mb), 1.0f)}; } EaxAlLowPassParam ALsource::eax_create_room_filter_param( @@ -4024,44 +4123,40 @@ EaxAlLowPassParam ALsource::eax_create_room_filter_param( const EAXSOURCEALLSENDPROPERTIES& send) const noexcept { 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); - - const auto gain_mb = - (static_cast(fx_slot_eax.lOcclusion) * fx_slot_eax.flOcclusionLFRatio) + - static_cast((is_environmental_fx ? mEax.source.lRoom : 0) + send.lSend) + - (is_listener_environment ? - eax_calculate_dst_occlusion_mb( - mEax.source.lOcclusion, - mEax.source.flOcclusionRoomRatio, - mEax.source.flOcclusionLFRatio) : - 0.0f) + - eax_calculate_dst_occlusion_mb( - send.lOcclusion, - send.flOcclusionRoomRatio, - send.flOcclusionLFRatio) + - (is_listener_environment ? - (static_cast(mEax.source.lExclusion) * mEax.source.flExclusionLFRatio) : - 0.0f) + - (static_cast(send.lExclusion) * send.flExclusionLFRatio); - - const auto gain_hf_mb = - static_cast(fx_slot_eax.lOcclusion) + - static_cast((is_environmental_fx ? mEax.source.lRoomHF : 0) + send.lSendHF) + - (is_listener_environment ? - ((static_cast(mEax.source.lOcclusion) * mEax.source.flOcclusionRoomRatio)) : - 0.0f) + - (static_cast(send.lOcclusion) * send.flOcclusionRoomRatio) + - (is_listener_environment ? - static_cast(mEax.source.lExclusion + send.lExclusion) : - 0.0f); - - const auto al_low_pass_param = EaxAlLowPassParam{ - level_mb_to_gain(gain_mb), - minf(level_mb_to_gain(gain_hf_mb), 1.0f)}; - - return al_low_pass_param; + const auto is_environmental_fx = bool{(fx_slot_eax.ulFlags & EAXFXSLOTFLAGS_ENVIRONMENT) != 0}; + const auto is_primary = bool{mEaxPrimaryFxSlotId.value_or(-1) == fx_slot.eax_get_index()}; + + auto gain_mb = (static_cast(fx_slot_eax.lOcclusion) * fx_slot_eax.flOcclusionLFRatio) + + eax_calculate_dst_occlusion_mb(send.lOcclusion, send.flOcclusionRoomRatio, + send.flOcclusionLFRatio) + + (static_cast(send.lExclusion) * send.flExclusionLFRatio); + + auto gainhf_mb = static_cast(fx_slot_eax.lOcclusion) + + (static_cast(send.lOcclusion) * send.flOcclusionRoomRatio); + + if(is_environmental_fx && is_primary) + { + const auto &source = mEax.source; + + gain_mb += eax_calculate_dst_occlusion_mb(source.lOcclusion, source.flOcclusionRoomRatio, + source.flOcclusionLFRatio); + gain_mb += static_cast(source.lExclusion) * source.flExclusionLFRatio; + + gainhf_mb += static_cast(source.lOcclusion) * source.flOcclusionRoomRatio; + gainhf_mb += static_cast(source.lExclusion + send.lExclusion); + } + + gainhf_mb -= gain_mb - static_cast(send.lSendHF); + gain_mb += static_cast(send.lSend); + if(is_environmental_fx) + { + const auto &source = mEax.source; + gain_mb += static_cast(source.lRoom); + gainhf_mb += static_cast(source.lRoomHF); + } + + return EaxAlLowPassParam{level_mb_to_gain(gain_mb), + std::min(level_mb_to_gain(gainhf_mb), 1.0f)}; } void ALsource::eax_update_direct_filter() @@ -4069,9 +4164,9 @@ void ALsource::eax_update_direct_filter() const auto& direct_param = eax_create_direct_filter_param(); Direct.Gain = direct_param.gain; Direct.GainHF = direct_param.gain_hf; - Direct.HFReference = LOWPASSFREQREF; + Direct.HFReference = LowPassFreqRef; Direct.GainLF = 1.0f; - Direct.LFReference = HIGHPASSFREQREF; + Direct.LFReference = HighPassFreqRef; mPropsDirty = true; } @@ -4360,7 +4455,7 @@ void ALsource::eax4_set(const EaxCall& call, Eax4Props& props) break; case EAXSOURCE_ACTIVEFXSLOTID: - eax4_defer_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots); + eax4_defer_active_fx_slot_id(call, al::span{props.active_fx_slots.guidActiveFXSlots}); break; default: @@ -4420,10 +4515,13 @@ void ALsource::eax5_set(const EaxCall& call, Eax5Props& props) case EAXSOURCE_ROLLOFFFACTOR: case EAXSOURCE_ROOMROLLOFFFACTOR: case EAXSOURCE_AIRABSORPTIONFACTOR: - case EAXSOURCE_FLAGS: eax3_set(call, props.source); break; + case EAXSOURCE_FLAGS: + eax_defer(call, props.source.ulFlags); + break; + case EAXSOURCE_SENDPARAMETERS: eax5_defer_sends(call, props.sends); break; @@ -4441,7 +4539,7 @@ void ALsource::eax5_set(const EaxCall& call, Eax5Props& props) break; case EAXSOURCE_ACTIVEFXSLOTID: - eax5_defer_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots); + eax5_defer_active_fx_slot_id(call, al::span{props.active_fx_slots.guidActiveFXSlots}); break; case EAXSOURCE_MACROFXFACTOR: @@ -4477,13 +4575,11 @@ void ALsource::eax_set(const EaxCall& call) mEaxVersion = eax_version; } -void ALsource::eax_get_active_fx_slot_id(const EaxCall& call, const GUID* ids, size_t max_count) +void ALsource::eax_get_active_fx_slot_id(const EaxCall& call, const al::span src_ids) { - assert(ids != nullptr); - assert(max_count == EAX40_MAX_ACTIVE_FXSLOTS || max_count == EAX50_MAX_ACTIVE_FXSLOTS); - const auto dst_ids = call.get_values(max_count); - const auto count = dst_ids.size(); - std::uninitialized_copy_n(ids, count, dst_ids.begin()); + assert(src_ids.size()==EAX40_MAX_ACTIVE_FXSLOTS || src_ids.size()==EAX50_MAX_ACTIVE_FXSLOTS); + const auto dst_ids = call.get_values(src_ids.size()); + std::uninitialized_copy_n(src_ids.begin(), dst_ids.size(), dst_ids.begin()); } void ALsource::eax1_get(const EaxCall& call, const Eax1Props& props) @@ -4731,7 +4827,7 @@ void ALsource::eax4_get(const EaxCall& call, const Eax4Props& props) break; case EAXSOURCE_ACTIVEFXSLOTID: - eax_get_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots, EAX40_MAX_ACTIVE_FXSLOTS); + eax_get_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots); break; default: @@ -4803,7 +4899,7 @@ void ALsource::eax5_get(const EaxCall& call, const Eax5Props& props) break; case EAXSOURCE_ACTIVEFXSLOTID: - eax_get_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots, EAX50_MAX_ACTIVE_FXSLOTS); + eax_get_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots); break; case EAXSOURCE_MACROFXFACTOR: @@ -4843,9 +4939,9 @@ void ALsource::eax_set_al_source_send(ALeffectslot *slot, size_t sendidx, const auto &send = Send[sendidx]; send.Gain = filter.gain; send.GainHF = filter.gain_hf; - send.HFReference = LOWPASSFREQREF; + send.HFReference = LowPassFreqRef; send.GainLF = 1.0f; - send.LFReference = HIGHPASSFREQREF; + send.LFReference = HighPassFreqRef; if(slot != nullptr) IncrementRef(slot->ref); diff --git a/3rdparty/openal/al/source.h b/3rdparty/openal/al/source.h index 2bdeb2a385b1..c69143b498b0 100644 --- a/3rdparty/openal/al/source.h +++ b/3rdparty/openal/al/source.h @@ -1,28 +1,29 @@ #ifndef AL_SOURCE_H #define AL_SOURCE_H +#include "config.h" + #include -#include #include +#include #include -#include #include #include +#include #include "AL/al.h" #include "AL/alc.h" +#include "AL/alext.h" -#include "alc/alu.h" -#include "alc/context.h" -#include "alc/inprogext.h" -#include "aldeque.h" #include "almalloc.h" +#include "alnumbers.h" #include "alnumeric.h" -#include "atomic.h" +#include "alspan.h" +#include "core/context.h" #include "core/voice.h" -#include "vector.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX +#include "eax/api.h" #include "eax/call.h" #include "eax/exception.h" #include "eax/fx_slot_index.h" @@ -31,27 +32,25 @@ struct ALbuffer; struct ALeffectslot; - +enum class Resampler : uint8_t; enum class SourceStereo : bool { Normal = AL_NORMAL_SOFT, Enhanced = AL_SUPER_STEREO_SOFT }; -#define DEFAULT_SENDS 2 +inline constexpr size_t DefaultSendCount{2}; -#define INVALID_VOICE_IDX static_cast(-1) +inline constexpr ALuint InvalidVoiceIndex{std::numeric_limits::max()}; -extern bool sBufferSubDataCompat; +inline bool sBufferSubDataCompat{false}; struct ALbufferQueueItem : public VoiceBufferItem { ALbuffer *mBuffer{nullptr}; - - DISABLE_ALLOC() }; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX class EaxSourceException : public EaxException { public: explicit EaxSourceException(const char* message) @@ -72,7 +71,7 @@ struct ALsource { float RefDistance{1.0f}; float MaxDistance{std::numeric_limits::max()}; float RolloffFactor{1.0f}; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX // For EAXSOURCE_ROLLOFFFACTOR, which is distinct from and added to // AL_ROLLOFF_FACTOR float RolloffFactor2{0.0f}; @@ -89,6 +88,7 @@ struct ALsource { DirectMode DirectChannels{DirectMode::Off}; SpatializeMode mSpatialize{SpatializeMode::Auto}; SourceStereo mStereoMode{SourceStereo::Normal}; + bool mPanningEnabled{false}; bool DryGainHFAuto{true}; bool WetGainAuto{true}; @@ -106,24 +106,27 @@ struct ALsource { float Radius{0.0f}; float EnhWidth{0.593f}; + float mPan{0.0f}; /** Direct filter and auxiliary send info. */ - struct { - float Gain; - float GainHF; - float HFReference; - float GainLF; - float LFReference; - } Direct; + struct DirectData { + float Gain{}; + float GainHF{}; + float HFReference{}; + float GainLF{}; + float LFReference{}; + }; + DirectData Direct; + struct SendData { - ALeffectslot *Slot; - float Gain; - float GainHF; - float HFReference; - float GainLF; - float LFReference; + ALeffectslot *Slot{}; + float Gain{}; + float GainHF{}; + float HFReference{}; + float GainLF{}; + float LFReference{}; }; - std::array Send; + std::array Send; /** * Last user-specified offset, and the offset type (bytes, samples, or @@ -139,20 +142,20 @@ struct ALsource { ALenum state{AL_INITIAL}; /** Source Buffer Queue head. */ - al::deque mQueue; + std::deque 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}; - ALsource(); + ALsource() noexcept; ~ALsource(); ALsource(const ALsource&) = delete; @@ -160,9 +163,9 @@ struct ALsource { static void SetName(ALCcontext *context, ALuint id, std::string_view name); - DISABLE_ALLOC() + DISABLE_ALLOC -#ifdef ALSOFT_EAX +#if ALSOFT_EAX public: void eaxInitialize(ALCcontext *context) noexcept; void eaxDispatch(const EaxCall& call); @@ -174,18 +177,18 @@ struct ALsource { private: using Exception = EaxSourceException; - static constexpr auto eax_max_speakers = 9; + static constexpr auto eax_max_speakers{9u}; - using EaxFxSlotIds = const GUID* [EAX_MAX_FXSLOTS]; + using EaxFxSlotIds = std::array; - static constexpr const EaxFxSlotIds eax4_fx_slot_ids = { + static constexpr const EaxFxSlotIds eax4_fx_slot_ids{ &EAXPROPERTYID_EAX40_FXSlot0, &EAXPROPERTYID_EAX40_FXSlot1, &EAXPROPERTYID_EAX40_FXSlot2, &EAXPROPERTYID_EAX40_FXSlot3, }; - static constexpr const EaxFxSlotIds eax5_fx_slot_ids = { + static constexpr const EaxFxSlotIds eax5_fx_slot_ids{ &EAXPROPERTYID_EAX50_FXSlot0, &EAXPROPERTYID_EAX50_FXSlot1, &EAXPROPERTYID_EAX50_FXSlot2, @@ -218,11 +221,6 @@ struct ALsource { Eax3Props source; EaxSends sends; EAX40ACTIVEFXSLOTS active_fx_slots; - - bool operator==(const Eax4Props& rhs) noexcept - { - return std::memcmp(this, &rhs, sizeof(Eax4Props)) == 0; - } }; struct Eax4State { @@ -235,11 +233,6 @@ struct ALsource { EaxSends sends; EAX50ACTIVEFXSLOTS active_fx_slots; EaxSpeakerLevels speaker_levels; - - bool operator==(const Eax5Props& rhs) noexcept - { - return std::memcmp(this, &rhs, sizeof(Eax5Props)) == 0; - } }; struct Eax5State { @@ -549,7 +542,24 @@ struct ALsource { struct Eax5SourceAllValidator { void operator()(const EAX50SOURCEPROPERTIES& props) const { - Eax3SourceAllValidator{}(static_cast(props)); + Eax2SourceDirectValidator{}(props.lDirect); + Eax2SourceDirectHfValidator{}(props.lDirectHF); + Eax2SourceRoomValidator{}(props.lRoom); + Eax2SourceRoomHfValidator{}(props.lRoomHF); + Eax2SourceObstructionValidator{}(props.lObstruction); + Eax2SourceObstructionLfRatioValidator{}(props.flObstructionLFRatio); + Eax2SourceOcclusionValidator{}(props.lOcclusion); + Eax2SourceOcclusionLfRatioValidator{}(props.flOcclusionLFRatio); + Eax2SourceOcclusionRoomRatioValidator{}(props.flOcclusionRoomRatio); + Eax3SourceOcclusionDirectRatioValidator{}(props.flOcclusionDirectRatio); + Eax3SourceExclusionValidator{}(props.lExclusion); + Eax3SourceExclusionLfRatioValidator{}(props.flExclusionLFRatio); + Eax2SourceOutsideVolumeHfValidator{}(props.lOutsideVolumeHF); + Eax3SourceDopplerFactorValidator{}(props.flDopplerFactor); + Eax3SourceRolloffFactorValidator{}(props.flRolloffFactor); + Eax2SourceRoomRolloffFactorValidator{}(props.flRoomRolloffFactor); + Eax2SourceAirAbsorptionFactorValidator{}(props.flAirAbsorptionFactor); + Eax5SourceFlagsValidator{}(props.ulFlags); Eax5SourceMacroFXFactorValidator{}(props.flMacroFXFactor); } }; @@ -812,39 +822,38 @@ struct ALsource { [[noreturn]] static void eax_fail_unknown_active_fx_slot_id(); [[noreturn]] static void eax_fail_unknown_receiving_fx_slot_id(); - void eax_set_sends_defaults(EaxSends& sends, const EaxFxSlotIds& ids) noexcept; - void eax1_set_defaults(Eax1Props& props) noexcept; + static void eax_set_sends_defaults(EaxSends& sends, const EaxFxSlotIds& ids) noexcept; + static void eax1_set_defaults(Eax1Props& props) noexcept; void eax1_set_defaults() noexcept; - void eax2_set_defaults(Eax2Props& props) noexcept; + static void eax2_set_defaults(Eax2Props& props) noexcept; void eax2_set_defaults() noexcept; - void eax3_set_defaults(Eax3Props& props) noexcept; + static void eax3_set_defaults(Eax3Props& props) noexcept; void eax3_set_defaults() noexcept; - void eax4_set_sends_defaults(EaxSends& sends) noexcept; - void eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS& slots) noexcept; + static void eax4_set_sends_defaults(EaxSends& sends) noexcept; + static void eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS& slots) noexcept; void eax4_set_defaults() noexcept; - void eax5_set_source_defaults(EAX50SOURCEPROPERTIES& props) noexcept; - void eax5_set_sends_defaults(EaxSends& sends) noexcept; - void eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS& slots) noexcept; - void eax5_set_speaker_levels_defaults(EaxSpeakerLevels& speaker_levels) noexcept; - void eax5_set_defaults(Eax5Props& props) noexcept; + static void eax5_set_source_defaults(EAX50SOURCEPROPERTIES& props) noexcept; + static void eax5_set_sends_defaults(EaxSends& sends) noexcept; + static void eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS& slots) noexcept; + static void eax5_set_speaker_levels_defaults(EaxSpeakerLevels& speaker_levels) noexcept; + static void eax5_set_defaults(Eax5Props& props) noexcept; void eax5_set_defaults() noexcept; void eax_set_defaults() noexcept; - void eax1_translate(const Eax1Props& src, Eax5Props& dst) noexcept; - void eax2_translate(const Eax2Props& src, Eax5Props& dst) noexcept; - void eax3_translate(const Eax3Props& src, Eax5Props& dst) noexcept; - void eax4_translate(const Eax4Props& src, Eax5Props& dst) noexcept; + static void eax1_translate(const Eax1Props& src, Eax5Props& dst) noexcept; + static void eax2_translate(const Eax2Props& src, Eax5Props& dst) noexcept; + static void eax3_translate(const Eax3Props& src, Eax5Props& dst) noexcept; + static void eax4_translate(const Eax4Props& src, Eax5Props& dst) noexcept; static float eax_calculate_dst_occlusion_mb( long src_occlusion_mb, float path_ratio, float lf_ratio) noexcept; - EaxAlLowPassParam eax_create_direct_filter_param() const noexcept; + [[nodiscard]] auto eax_create_direct_filter_param() const noexcept -> EaxAlLowPassParam; - EaxAlLowPassParam eax_create_room_filter_param( - const ALeffectslot& fx_slot, - const EAXSOURCEALLSENDPROPERTIES& send) const noexcept; + [[nodiscard]] auto eax_create_room_filter_param(const ALeffectslot& fx_slot, + const EAXSOURCEALLSENDPROPERTIES& send) const noexcept -> EaxAlLowPassParam; void eax_update_direct_filter(); void eax_update_room_filters(); @@ -897,16 +906,16 @@ struct ALsource { } } - void eax_get_active_fx_slot_id(const EaxCall& call, const GUID* ids, size_t max_count); - void eax1_get(const EaxCall& call, const Eax1Props& props); - void eax2_get(const EaxCall& call, const Eax2Props& props); - void eax3_get_obstruction(const EaxCall& call, const Eax3Props& props); - void eax3_get_occlusion(const EaxCall& call, const Eax3Props& props); - void eax3_get_exclusion(const EaxCall& call, const Eax3Props& props); - void eax3_get(const EaxCall& call, const Eax3Props& props); + static void eax_get_active_fx_slot_id(const EaxCall& call, const al::span src_ids); + static void eax1_get(const EaxCall& call, const Eax1Props& props); + static void eax2_get(const EaxCall& call, const Eax2Props& props); + static void eax3_get_obstruction(const EaxCall& call, const Eax3Props& props); + static void eax3_get_occlusion(const EaxCall& call, const Eax3Props& props); + static void eax3_get_exclusion(const EaxCall& call, const Eax3Props& props); + static void eax3_get(const EaxCall& call, const Eax3Props& props); void eax4_get(const EaxCall& call, const Eax4Props& props); - void eax5_get_all_2d(const EaxCall& call, const EAX50SOURCEPROPERTIES& props); - void eax5_get_speaker_levels(const EaxCall& call, const EaxSpeakerLevels& props); + static void eax5_get_all_2d(const EaxCall& call, const EAX50SOURCEPROPERTIES& props); + static void eax5_get_speaker_levels(const EaxCall& call, const EaxSpeakerLevels& props); void eax5_get(const EaxCall& call, const Eax5Props& props); void eax_get(const EaxCall& call); @@ -979,21 +988,21 @@ struct ALsource { } template - void eax_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount]) + void eax_defer_active_fx_slot_id(const EaxCall& call, const al::span dst_ids) { const auto src_ids = call.get_values(TIdCount); std::for_each(src_ids.cbegin(), src_ids.cend(), TValidator{}); - std::uninitialized_copy(src_ids.cbegin(), src_ids.cend(), dst_ids); + std::uninitialized_copy(src_ids.cbegin(), src_ids.cend(), dst_ids.begin()); } template - void eax4_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount]) + void eax4_defer_active_fx_slot_id(const EaxCall& call, const al::span dst_ids) { eax_defer_active_fx_slot_id(call, dst_ids); } template - void eax5_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount]) + void eax5_defer_active_fx_slot_id(const EaxCall& call, const al::span dst_ids) { eax_defer_active_fx_slot_id(call, dst_ids); } @@ -1025,12 +1034,12 @@ struct ALsource { void eax_set_efx_wet_gain_auto(); void eax_set_efx_wet_gain_hf_auto(); - void eax1_set(const EaxCall& call, Eax1Props& props); - void eax2_set(const EaxCall& call, Eax2Props& props); + static void eax1_set(const EaxCall& call, Eax1Props& props); + static void eax2_set(const EaxCall& call, Eax2Props& props); void eax3_set(const EaxCall& call, Eax3Props& props); void eax4_set(const EaxCall& call, Eax4Props& props); - void eax5_defer_all_2d(const EaxCall& call, EAX50SOURCEPROPERTIES& props); - void eax5_defer_speaker_levels(const EaxCall& call, EaxSpeakerLevels& props); + static void eax5_defer_all_2d(const EaxCall& call, EAX50SOURCEPROPERTIES& props); + static void eax5_defer_speaker_levels(const EaxCall& call, EaxSpeakerLevels& props); void eax5_set(const EaxCall& call, Eax5Props& props); void eax_set(const EaxCall& call); @@ -1044,4 +1053,19 @@ struct ALsource { void UpdateAllSourceProps(ALCcontext *context); +struct SourceSubList { + uint64_t FreeMask{~0_u64}; + gsl::owner*> Sources{nullptr}; + + SourceSubList() noexcept = default; + SourceSubList(const SourceSubList&) = delete; + SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources} + { rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; } + ~SourceSubList(); + + SourceSubList& operator=(const SourceSubList&) = delete; + SourceSubList& operator=(SourceSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; } +}; + #endif diff --git a/3rdparty/openal/al/state.cpp b/3rdparty/openal/al/state.cpp index 5131edd93a51..9bfaa8b3a722 100644 --- a/3rdparty/openal/al/state.cpp +++ b/3rdparty/openal/al/state.cpp @@ -22,27 +22,30 @@ #include "version.h" +#include #include #include -#include +#include #include #include #include #include +#include #include "AL/al.h" #include "AL/alc.h" #include "AL/alext.h" #include "al/debug.h" -#include "albit.h" +#include "al/listener.h" #include "alc/alu.h" #include "alc/context.h" +#include "alc/device.h" #include "alc/inprogext.h" #include "alnumeric.h" #include "atomic.h" #include "core/context.h" -#include "core/except.h" +#include "core/logging.h" #include "core/mixer/defs.h" #include "core/voice.h" #include "direct_defs.h" @@ -50,9 +53,7 @@ #include "opthelpers.h" #include "strutils.h" -#ifdef ALSOFT_EAX -#include "alc/device.h" - +#if ALSOFT_EAX #include "eax/globals.h" #include "eax/x_ram.h" #endif // ALSOFT_EAX @@ -60,19 +61,21 @@ namespace { -constexpr ALchar alVendor[] = "OpenAL Community"; -constexpr ALchar alVersion[] = "1.1 ALSOFT " ALSOFT_VERSION; -constexpr ALchar alRenderer[] = "OpenAL Soft"; +using ALvoidptr = ALvoid*; -// Error Messages -constexpr ALchar alNoError[] = "No Error"; -constexpr ALchar alErrInvalidName[] = "Invalid Name"; -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"; +[[nodiscard]] constexpr auto GetVendorString() noexcept { return "OpenAL Community"; } +[[nodiscard]] constexpr auto GetVersionString() noexcept { return "1.1 ALSOFT " ALSOFT_VERSION; } +[[nodiscard]] constexpr auto GetRendererString() noexcept { return "OpenAL Soft"; } + +/* Error Messages */ +[[nodiscard]] constexpr auto GetNoErrorString() noexcept { return "No Error"; } +[[nodiscard]] constexpr auto GetInvalidNameString() noexcept { return "Invalid Name"; } +[[nodiscard]] constexpr auto GetInvalidEnumString() noexcept { return "Invalid Enum"; } +[[nodiscard]] constexpr auto GetInvalidValueString() noexcept { return "Invalid Value"; } +[[nodiscard]] constexpr auto GetInvalidOperationString() noexcept { return "Invalid Operation"; } +[[nodiscard]] constexpr auto GetOutOfMemoryString() noexcept { return "Out of Memory"; } +[[nodiscard]] constexpr auto GetStackOverflowString() noexcept { return "Stack Overflow"; } +[[nodiscard]] constexpr auto GetStackUnderflowString() noexcept { return "Stack Underflow"; } /* Resampler strings */ template struct ResamplerName { }; @@ -80,8 +83,10 @@ template<> struct ResamplerName { static constexpr const ALchar *Get() noexcept { return "Nearest"; } }; template<> struct ResamplerName { static constexpr const ALchar *Get() noexcept { return "Linear"; } }; -template<> struct ResamplerName -{ static constexpr const ALchar *Get() noexcept { return "Cubic"; } }; +template<> struct ResamplerName +{ static constexpr const ALchar *Get() noexcept { return "Cubic Spline"; } }; +template<> struct ResamplerName +{ static constexpr const ALchar *Get() noexcept { return "4-point Gaussian"; } }; template<> struct ResamplerName { static constexpr const ALchar *Get() noexcept { return "11th order Sinc (fast)"; } }; template<> struct ResamplerName @@ -98,7 +103,8 @@ const ALchar *GetResamplerName(const Resampler rtype) { HANDLE_RESAMPLER(Resampler::Point); HANDLE_RESAMPLER(Resampler::Linear); - HANDLE_RESAMPLER(Resampler::Cubic); + HANDLE_RESAMPLER(Resampler::Spline); + HANDLE_RESAMPLER(Resampler::Gaussian); HANDLE_RESAMPLER(Resampler::FastBSinc12); HANDLE_RESAMPLER(Resampler::BSinc12); HANDLE_RESAMPLER(Resampler::FastBSinc24); @@ -109,7 +115,7 @@ const ALchar *GetResamplerName(const Resampler rtype) throw std::runtime_error{"Unexpected resampler index"}; } -std::optional DistanceModelFromALenum(ALenum model) +constexpr auto DistanceModelFromALenum(ALenum model) noexcept -> std::optional { switch(model) { @@ -123,7 +129,7 @@ std::optional DistanceModelFromALenum(ALenum model) } return std::nullopt; } -ALenum ALenumFromDistanceModel(DistanceModel model) +constexpr auto ALenumFromDistanceModel(DistanceModel model) -> ALenum { switch(model) { @@ -139,24 +145,24 @@ ALenum ALenumFromDistanceModel(DistanceModel model) } 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, + DopplerFactorProp = AL_DOPPLER_FACTOR, + DopplerVelocityProp = AL_DOPPLER_VELOCITY, + DistanceModelProp = AL_DISTANCE_MODEL, + SpeedOfSoundProp = AL_SPEED_OF_SOUND, + DeferredUpdatesProp = AL_DEFERRED_UPDATES_SOFT, + GainLimitProp = AL_GAIN_LIMIT_SOFT, + NumResamplersProp = AL_NUM_RESAMPLERS_SOFT, + DefaultResamplerProp = AL_DEFAULT_RESAMPLER_SOFT, + DebugLoggedMessagesProp = AL_DEBUG_LOGGED_MESSAGES_EXT, + DebugNextLoggedMessageLengthProp = AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT, + MaxDebugMessageLengthProp = AL_MAX_DEBUG_MESSAGE_LENGTH_EXT, + MaxDebugLoggedMessagesProp = AL_MAX_DEBUG_LOGGED_MESSAGES_EXT, + MaxDebugGroupDepthProp = AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT, + MaxLabelLengthProp = AL_MAX_LABEL_LENGTH_EXT, + ContextFlagsProp = AL_CONTEXT_FLAGS_EXT, +#if ALSOFT_EAX + EaxRamSizeProp = AL_EAX_RAM_SIZE, + EaxRamFreeProp = AL_EAX_RAM_FREE, #endif }; @@ -221,14 +227,14 @@ void GetValue(ALCcontext *context, ALenum pname, T *values) case AL_DEBUG_LOGGED_MESSAGES_EXT: { - std::lock_guard _{context->mDebugCbLock}; + std::lock_guard debuglock{context->mDebugCbLock}; *values = cast_value(context->mDebugLog.size()); return; } case AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT: { - std::lock_guard _{context->mDebugCbLock}; + std::lock_guard debuglock{context->mDebugCbLock}; *values = cast_value(context->mDebugLog.empty() ? 0_uz : (context->mDebugLog.front().mMessage.size()+1)); return; @@ -254,9 +260,8 @@ void GetValue(ALCcontext *context, ALenum pname, T *values) *values = cast_value(context->mContextFlags.to_ulong()); return; -#ifdef ALSOFT_EAX - -#define EAX_ERROR "[alGetInteger] EAX not enabled." +#if ALSOFT_EAX +#define EAX_ERROR "[alGetInteger] EAX not enabled" case AL_EAX_RAM_SIZE: if(eax_g_is_enabled) @@ -264,8 +269,8 @@ void GetValue(ALCcontext *context, ALenum pname, T *values) *values = cast_value(eax_x_ram_max_size); return; } - context->setError(AL_INVALID_ENUM, EAX_ERROR); - return; + ERR(EAX_ERROR "\n"); + break; case AL_EAX_RAM_FREE: if(eax_g_is_enabled) @@ -275,11 +280,10 @@ void GetValue(ALCcontext *context, ALenum pname, T *values) *values = cast_value(device->eax_x_ram_free_size); return; } - context->setError(AL_INVALID_ENUM, EAX_ERROR); - return; + ERR(EAX_ERROR "\n"); + break; #undef EAX_ERROR - #endif // ALSOFT_EAX } context->setError(AL_INVALID_ENUM, "Invalid context property 0x%04x", pname); @@ -299,7 +303,7 @@ inline void UpdateProps(ALCcontext *context) /* 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 +AL_API auto AL_APIENTRY alsoft_get_version() noexcept -> const ALchar* { static const auto spoof = al::getenv("ALSOFT_SPOOF_VERSION"); if(spoof) return spoof->c_str(); @@ -307,112 +311,98 @@ AL_API const ALchar* AL_APIENTRY alsoft_get_version(void) noexcept } -AL_API DECL_FUNC1(void, alEnable, ALenum) +AL_API DECL_FUNC1(void, alEnable, ALenum,capability) FORCE_ALIGN void AL_APIENTRY alEnableDirect(ALCcontext *context, ALenum capability) noexcept { switch(capability) { case AL_SOURCE_DISTANCE_MODEL: { - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; context->mSourceDistanceModel = true; UpdateProps(context); } - break; + return; case AL_DEBUG_OUTPUT_EXT: - context->mDebugEnabled = true; - break; + context->mDebugEnabled.store(true); + return; 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); + return; } + context->setError(AL_INVALID_VALUE, "Invalid enable property 0x%04x", capability); } -AL_API DECL_FUNC1(void, alDisable, ALenum) +AL_API DECL_FUNC1(void, alDisable, ALenum,capability) FORCE_ALIGN void AL_APIENTRY alDisableDirect(ALCcontext *context, ALenum capability) noexcept { switch(capability) { case AL_SOURCE_DISTANCE_MODEL: { - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; context->mSourceDistanceModel = false; UpdateProps(context); } - break; + return; case AL_DEBUG_OUTPUT_EXT: - context->mDebugEnabled = false; - break; + context->mDebugEnabled.store(false); + return; case AL_STOP_SOURCES_ON_DISCONNECT_SOFT: - context->mStopVoicesOnDisconnect = false; - break; - - default: - context->setError(AL_INVALID_VALUE, "Invalid disable property 0x%04x", capability); + context->mStopVoicesOnDisconnect.store(false); + return; } + context->setError(AL_INVALID_VALUE, "Invalid disable property 0x%04x", capability); } -AL_API DECL_FUNC1(ALboolean, alIsEnabled, ALenum) +AL_API DECL_FUNC1(ALboolean, alIsEnabled, ALenum,capability) FORCE_ALIGN ALboolean AL_APIENTRY alIsEnabledDirect(ALCcontext *context, ALenum capability) noexcept { - std::lock_guard _{context->mPropLock}; - ALboolean value{AL_FALSE}; + std::lock_guard proplock{context->mPropLock}; switch(capability) { - case AL_SOURCE_DISTANCE_MODEL: - value = context->mSourceDistanceModel ? AL_TRUE : AL_FALSE; - break; - - case AL_DEBUG_OUTPUT_EXT: - value = context->mDebugEnabled ? AL_TRUE : AL_FALSE; - break; - + case AL_SOURCE_DISTANCE_MODEL: return context->mSourceDistanceModel ? AL_TRUE : AL_FALSE; + case AL_DEBUG_OUTPUT_EXT: return context->mDebugEnabled ? AL_TRUE : AL_FALSE; 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 context->mStopVoicesOnDisconnect.load() ? AL_TRUE : AL_FALSE; } - - return value; + context->setError(AL_INVALID_VALUE, "Invalid is enabled property 0x%04x", capability); + return AL_FALSE; } #define DECL_GETFUNC(R, Name, Ext) \ -AL_API R AL_APIENTRY Name##Ext(ALenum pname) noexcept \ +auto AL_APIENTRY Name##Ext(ALenum pname) noexcept -> R \ { \ - R value{}; \ + auto value = R{}; \ 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 \ +FORCE_ALIGN auto AL_APIENTRY Name##Direct##Ext(ALCcontext *context, ALenum pname) noexcept -> R \ { \ - R value{}; \ + auto value = R{}; \ Name##vDirect##Ext(context, pname, &value); \ return value; \ } -DECL_GETFUNC(ALboolean, alGetBoolean,) -DECL_GETFUNC(ALdouble, alGetDouble,) -DECL_GETFUNC(ALfloat, alGetFloat,) -DECL_GETFUNC(ALint, alGetInteger,) +AL_API DECL_GETFUNC(ALboolean, alGetBoolean,) +AL_API DECL_GETFUNC(ALdouble, alGetDouble,) +AL_API DECL_GETFUNC(ALfloat, alGetFloat,) +AL_API DECL_GETFUNC(ALint, alGetInteger,) -DECL_GETFUNC(ALint64SOFT, alGetInteger64,SOFT) -DECL_GETFUNC(ALvoid*, alGetPointer,SOFT) +DECL_GETFUNC(ALvoidptr, alGetPointer,EXT) +AL_API DECL_GETFUNC(ALint64SOFT, alGetInteger64,SOFT) +AL_API DECL_GETFUNC(ALvoidptr, alGetPointer,SOFT) #undef DECL_GETFUNC -AL_API DECL_FUNC2(void, alGetBooleanv, ALenum, ALboolean*) +AL_API DECL_FUNC2(void, alGetBooleanv, ALenum,pname, ALboolean*,values) FORCE_ALIGN void AL_APIENTRY alGetBooleanvDirect(ALCcontext *context, ALenum pname, ALboolean *values) noexcept { if(!values) UNLIKELY @@ -420,7 +410,7 @@ FORCE_ALIGN void AL_APIENTRY alGetBooleanvDirect(ALCcontext *context, ALenum pna GetValue(context, pname, values); } -AL_API DECL_FUNC2(void, alGetDoublev, ALenum, ALdouble*) +AL_API DECL_FUNC2(void, alGetDoublev, ALenum,pname, ALdouble*,values) FORCE_ALIGN void AL_APIENTRY alGetDoublevDirect(ALCcontext *context, ALenum pname, ALdouble *values) noexcept { if(!values) UNLIKELY @@ -428,7 +418,7 @@ FORCE_ALIGN void AL_APIENTRY alGetDoublevDirect(ALCcontext *context, ALenum pnam GetValue(context, pname, values); } -AL_API DECL_FUNC2(void, alGetFloatv, ALenum, ALfloat*) +AL_API DECL_FUNC2(void, alGetFloatv, ALenum,pname, ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alGetFloatvDirect(ALCcontext *context, ALenum pname, ALfloat *values) noexcept { if(!values) UNLIKELY @@ -436,7 +426,7 @@ FORCE_ALIGN void AL_APIENTRY alGetFloatvDirect(ALCcontext *context, ALenum pname GetValue(context, pname, values); } -AL_API DECL_FUNC2(void, alGetIntegerv, ALenum, ALint*) +AL_API DECL_FUNC2(void, alGetIntegerv, ALenum,pname, ALint*,values) FORCE_ALIGN void AL_APIENTRY alGetIntegervDirect(ALCcontext *context, ALenum pname, ALint *values) noexcept { if(!values) UNLIKELY @@ -444,7 +434,7 @@ FORCE_ALIGN void AL_APIENTRY alGetIntegervDirect(ALCcontext *context, ALenum pna GetValue(context, pname, values); } -AL_API DECL_FUNCEXT2(void, alGetInteger64v,SOFT, ALenum, ALint64SOFT*) +AL_API DECL_FUNCEXT2(void, alGetInteger64v,SOFT, ALenum,pname, ALint64SOFT*,values) FORCE_ALIGN void AL_APIENTRY alGetInteger64vDirectSOFT(ALCcontext *context, ALenum pname, ALint64SOFT *values) noexcept { if(!values) UNLIKELY @@ -452,8 +442,12 @@ FORCE_ALIGN void AL_APIENTRY alGetInteger64vDirectSOFT(ALCcontext *context, ALen GetValue(context, pname, values); } -AL_API DECL_FUNCEXT2(void, alGetPointerv,SOFT, ALenum, ALvoid**) +AL_API DECL_FUNCEXT2(void, alGetPointerv,SOFT, ALenum,pname, ALvoid**,values) FORCE_ALIGN void AL_APIENTRY alGetPointervDirectSOFT(ALCcontext *context, ALenum pname, ALvoid **values) noexcept +{ return alGetPointervDirectEXT(context, pname, values); } + +FORCE_ALIGN DECL_FUNCEXT2(void, alGetPointerv,EXT, ALenum,pname, ALvoid**,values) +FORCE_ALIGN void AL_APIENTRY alGetPointervDirectEXT(ALCcontext *context, ALenum pname, ALvoid **values) noexcept { if(!values) UNLIKELY return context->setError(AL_INVALID_VALUE, "NULL pointer"); @@ -461,118 +455,87 @@ FORCE_ALIGN void AL_APIENTRY alGetPointervDirectSOFT(ALCcontext *context, ALenum switch(pname) { case AL_EVENT_CALLBACK_FUNCTION_SOFT: - *values = al::bit_cast(context->mEventCb); - break; + *values = reinterpret_cast(context->mEventCb); + return; case AL_EVENT_CALLBACK_USER_PARAM_SOFT: *values = context->mEventParam; - break; + return; case AL_DEBUG_CALLBACK_FUNCTION_EXT: - *values = al::bit_cast(context->mDebugCb); - break; + *values = reinterpret_cast(context->mDebugCb); + return; case AL_DEBUG_CALLBACK_USER_PARAM_EXT: *values = context->mDebugParam; - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid context pointer property 0x%04x", pname); + return; } + context->setError(AL_INVALID_ENUM, "Invalid context pointer property 0x%04x", pname); } -AL_API DECL_FUNC1(const ALchar*, alGetString, ALenum) +AL_API DECL_FUNC1(const ALchar*, alGetString, ALenum,pname) FORCE_ALIGN const ALchar* AL_APIENTRY alGetStringDirect(ALCcontext *context, ALenum pname) noexcept { - const ALchar *value{nullptr}; switch(pname) { case AL_VENDOR: - value = alVendor; - break; - + if(auto device = context->mALDevice.get(); !device->mVendorOverride.empty()) + return device->mVendorOverride.c_str(); + return GetVendorString(); case AL_VERSION: - value = alVersion; - break; - + if(auto device = context->mALDevice.get(); !device->mVersionOverride.empty()) + return device->mVersionOverride.c_str(); + return GetVersionString(); case AL_RENDERER: - value = alRenderer; - break; - - case AL_EXTENSIONS: - value = context->mExtensionsString.c_str(); - break; - - case AL_NO_ERROR: - value = alNoError; - break; - - case AL_INVALID_NAME: - value = alErrInvalidName; - break; - - case AL_INVALID_ENUM: - value = alErrInvalidEnum; - break; - - case AL_INVALID_VALUE: - value = alErrInvalidValue; - break; - - case AL_INVALID_OPERATION: - value = alErrInvalidOp; - break; - - case AL_OUT_OF_MEMORY: - 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); + if(auto device = context->mALDevice.get(); !device->mRendererOverride.empty()) + return device->mRendererOverride.c_str(); + return GetRendererString(); + case AL_EXTENSIONS: return context->mExtensionsString.c_str(); + case AL_NO_ERROR: return GetNoErrorString(); + case AL_INVALID_NAME: return GetInvalidNameString(); + case AL_INVALID_ENUM: return GetInvalidEnumString(); + case AL_INVALID_VALUE: return GetInvalidValueString(); + case AL_INVALID_OPERATION: return GetInvalidOperationString(); + case AL_OUT_OF_MEMORY: return GetOutOfMemoryString(); + case AL_STACK_OVERFLOW_EXT: return GetStackOverflowString(); + case AL_STACK_UNDERFLOW_EXT: return GetStackUnderflowString(); } - return value; + context->setError(AL_INVALID_VALUE, "Invalid string property 0x%04x", pname); + return nullptr; } -AL_API DECL_FUNC1(void, alDopplerFactor, ALfloat) +AL_API DECL_FUNC1(void, alDopplerFactor, ALfloat,value) FORCE_ALIGN void AL_APIENTRY alDopplerFactorDirect(ALCcontext *context, ALfloat value) noexcept { if(!(value >= 0.0f && std::isfinite(value))) context->setError(AL_INVALID_VALUE, "Doppler factor %f out of range", value); else { - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; context->mDopplerFactor = value; UpdateProps(context); } } -AL_API DECL_FUNC1(void, alSpeedOfSound, ALfloat) +AL_API DECL_FUNC1(void, alSpeedOfSound, ALfloat,value) FORCE_ALIGN void AL_APIENTRY alSpeedOfSoundDirect(ALCcontext *context, ALfloat value) noexcept { if(!(value > 0.0f && std::isfinite(value))) context->setError(AL_INVALID_VALUE, "Speed of sound %f out of range", value); else { - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; context->mSpeedOfSound = value; UpdateProps(context); } } -AL_API DECL_FUNC1(void, alDistanceModel, ALenum) +AL_API DECL_FUNC1(void, alDistanceModel, ALenum,value) FORCE_ALIGN void AL_APIENTRY alDistanceModelDirect(ALCcontext *context, ALenum value) noexcept { if(auto model = DistanceModelFromALenum(value)) { - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; context->mDistanceModel = *model; if(!context->mSourceDistanceModel) UpdateProps(context); @@ -585,35 +548,31 @@ FORCE_ALIGN void AL_APIENTRY alDistanceModelDirect(ALCcontext *context, ALenum v AL_API DECL_FUNCEXT(void, alDeferUpdates,SOFT) FORCE_ALIGN void AL_APIENTRY alDeferUpdatesDirectSOFT(ALCcontext *context) noexcept { - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; context->deferUpdates(); } AL_API DECL_FUNCEXT(void, alProcessUpdates,SOFT) FORCE_ALIGN void AL_APIENTRY alProcessUpdatesDirectSOFT(ALCcontext *context) noexcept { - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; context->processUpdates(); } -AL_API DECL_FUNCEXT2(const ALchar*, alGetStringi,SOFT, ALenum,ALsizei) +AL_API DECL_FUNCEXT2(const ALchar*, alGetStringi,SOFT, ALenum,pname, ALsizei,index) FORCE_ALIGN const ALchar* AL_APIENTRY alGetStringiDirectSOFT(ALCcontext *context, ALenum pname, ALsizei index) noexcept { - const ALchar *value{nullptr}; switch(pname) { case AL_RESAMPLER_NAME_SOFT: - if(index < 0 || index > static_cast(Resampler::Max)) - context->setError(AL_INVALID_VALUE, "Resampler name index %d out of range", index); - else - value = GetResamplerName(static_cast(index)); - break; - - default: - context->setError(AL_INVALID_VALUE, "Invalid string indexed property"); + if(index >= 0 && index <= static_cast(Resampler::Max)) + return GetResamplerName(static_cast(index)); + context->setError(AL_INVALID_VALUE, "Resampler name index %d out of range", index); + return nullptr; } - return value; + context->setError(AL_INVALID_VALUE, "Invalid string indexed property"); + return nullptr; } @@ -623,7 +582,7 @@ AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value) noexcept if(!context) UNLIKELY return; if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY - context->debugMessage(DebugSource::API, DebugType::DeprecatedBehavior, 0, + context->debugMessage(DebugSource::API, DebugType::DeprecatedBehavior, 1, DebugSeverity::Medium, "alDopplerVelocity is deprecated in AL 1.1, use alSpeedOfSound; " "alDopplerVelocity(x) -> alSpeedOfSound(343.3f * x)"); @@ -632,7 +591,7 @@ AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value) noexcept context->setError(AL_INVALID_VALUE, "Doppler velocity %f out of range", value); else { - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; context->mDopplerVelocity = value; UpdateProps(context.get()); } @@ -641,21 +600,21 @@ AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value) noexcept void UpdateContextProps(ALCcontext *context) { - /* Get an unused proprty container, or allocate a new one as needed. */ + /* Get an unused property container, or allocate a new one as needed. */ ContextProps *props{context->mFreeContextProps.load(std::memory_order_acquire)}; if(!props) - props = new ContextProps{}; - else { - ContextProps *next; - do { - next = props->next.load(std::memory_order_relaxed); - } while(context->mFreeContextProps.compare_exchange_weak(props, next, - std::memory_order_seq_cst, std::memory_order_acquire) == 0); + context->allocContextProps(); + props = context->mFreeContextProps.load(std::memory_order_acquire); } + ContextProps *next; + do { + next = props->next.load(std::memory_order_relaxed); + } while(context->mFreeContextProps.compare_exchange_weak(props, next, + std::memory_order_acq_rel, std::memory_order_acquire) == false); /* Copy in current property values. */ - ALlistener &listener = context->mListener; + const auto &listener = context->mListener; props->Position = listener.Position; props->Velocity = listener.Velocity; props->OrientAt = listener.OrientAt; @@ -667,6 +626,9 @@ void UpdateContextProps(ALCcontext *context) props->DopplerFactor = context->mDopplerFactor; props->DopplerVelocity = context->mDopplerVelocity; props->SpeedOfSound = context->mSpeedOfSound; +#if ALSOFT_EAX + props->DistanceFactor = context->eaxGetDistanceFactor(); +#endif props->SourceDistanceModel = context->mSourceDistanceModel; props->mDistanceModel = context->mDistanceModel; diff --git a/3rdparty/openal/alc/alc.cpp b/3rdparty/openal/alc/alc.cpp index baa4788f3d7e..26ce923384ff 100644 --- a/3rdparty/openal/alc/alc.cpp +++ b/3rdparty/openal/alc/alc.cpp @@ -19,6 +19,8 @@ */ #include "config.h" +#include "config_backends.h" +#include "config_simd.h" #include "version.h" @@ -39,10 +41,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -50,10 +52,10 @@ #include #include #include -#include #include #include -#include +#include +#include #include #include @@ -67,11 +69,12 @@ #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 "alconfig.h" #include "almalloc.h" +#include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" #include "alstring.h" @@ -85,13 +88,12 @@ #include "core/cpu_caps.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" -#include "core/except.h" +#include "core/filters/nfc.h" #include "core/helpers.h" #include "core/mastering.h" -#include "core/mixer/hrtfdefs.h" #include "core/fpu_ctrl.h" -#include "core/front_stablizer.h" #include "core/logging.h" #include "core/uhjfilter.h" #include "core/voice.h" @@ -99,6 +101,7 @@ #include "device.h" #include "effects/base.h" #include "export_list.h" +#include "flexarray.h" #include "inprogext.h" #include "intrusive_ptr.h" #include "opthelpers.h" @@ -107,59 +110,62 @@ #include "backends/base.h" #include "backends/null.h" #include "backends/loopback.h" -#ifdef HAVE_PIPEWIRE +#if HAVE_PIPEWIRE #include "backends/pipewire.h" #endif -#ifdef HAVE_JACK +#if HAVE_JACK #include "backends/jack.h" #endif -#ifdef HAVE_PULSEAUDIO +#if HAVE_PULSEAUDIO #include "backends/pulseaudio.h" #endif -#ifdef HAVE_ALSA +#if HAVE_ALSA #include "backends/alsa.h" #endif -#ifdef HAVE_WASAPI +#if HAVE_WASAPI #include "backends/wasapi.h" #endif -#ifdef HAVE_COREAUDIO +#if HAVE_COREAUDIO #include "backends/coreaudio.h" #endif -#ifdef HAVE_OPENSL +#if HAVE_OPENSL #include "backends/opensl.h" #endif -#ifdef HAVE_OBOE +#if HAVE_OBOE #include "backends/oboe.h" #endif -#ifdef HAVE_SOLARIS +#if HAVE_SOLARIS #include "backends/solaris.h" #endif -#ifdef HAVE_SNDIO +#if HAVE_SNDIO #include "backends/sndio.h" #endif -#ifdef HAVE_OSS +#if HAVE_OSS #include "backends/oss.h" #endif -#ifdef HAVE_DSOUND +#if HAVE_DSOUND #include "backends/dsound.h" #endif -#ifdef HAVE_WINMM +#if HAVE_WINMM #include "backends/winmm.h" #endif -#ifdef HAVE_PORTAUDIO +#if HAVE_PORTAUDIO #include "backends/portaudio.h" #endif -#ifdef HAVE_SDL2 +#if HAVE_SDL2 #include "backends/sdl2.h" #endif -#ifdef HAVE_WAVE +#if HAVE_OTHERIO +#include "backends/otherio.h" +#endif +#if HAVE_WAVE #include "backends/wave.h" #endif -#ifdef ALSOFT_EAX +#if ALSOFT_EAX +#include "al/eax/api.h" #include "al/eax/globals.h" -#include "al/eax/x_ram.h" -#endif // ALSOFT_EAX +#endif /************************************************ @@ -173,7 +179,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, - al::bit_cast(module), &module); + reinterpret_cast(module), &module); break; } return TRUE; @@ -182,7 +188,7 @@ BOOL APIENTRY DllMain(HINSTANCE module, DWORD reason, LPVOID /*reserved*/) namespace { -using namespace std::placeholders; +using namespace std::string_view_literals; using std::chrono::seconds; using std::chrono::nanoseconds; @@ -190,64 +196,76 @@ using voidp = void*; using float2 = std::array; +auto gProcessRunning = true; +struct ProcessWatcher { + ProcessWatcher() = default; + ProcessWatcher(const ProcessWatcher&) = delete; + ProcessWatcher& operator=(const ProcessWatcher&) = delete; + ~ProcessWatcher() { gProcessRunning = false; } +}; +ProcessWatcher gProcessWatcher; + /************************************************ * Backends ************************************************/ struct BackendInfo { const char *name; - BackendFactory& (*getFactory)(void); + BackendFactory& (*getFactory)(); }; -BackendInfo BackendList[] = { -#ifdef HAVE_PIPEWIRE - { "pipewire", PipeWireBackendFactory::getFactory }, +std::array BackendList{ +#if HAVE_PIPEWIRE + BackendInfo{"pipewire", PipeWireBackendFactory::getFactory}, #endif -#ifdef HAVE_PULSEAUDIO - { "pulse", PulseBackendFactory::getFactory }, +#if HAVE_PULSEAUDIO + BackendInfo{"pulse", PulseBackendFactory::getFactory}, #endif -#ifdef HAVE_WASAPI - { "wasapi", WasapiBackendFactory::getFactory }, +#if HAVE_WASAPI + BackendInfo{"wasapi", WasapiBackendFactory::getFactory}, +#endif +#if HAVE_COREAUDIO + BackendInfo{"core", CoreAudioBackendFactory::getFactory}, #endif -#ifdef HAVE_COREAUDIO - { "core", CoreAudioBackendFactory::getFactory }, +#if HAVE_OBOE + BackendInfo{"oboe", OboeBackendFactory::getFactory}, #endif -#ifdef HAVE_OBOE - { "oboe", OboeBackendFactory::getFactory }, +#if HAVE_OPENSL + BackendInfo{"opensl", OSLBackendFactory::getFactory}, #endif -#ifdef HAVE_OPENSL - { "opensl", OSLBackendFactory::getFactory }, +#if HAVE_ALSA + BackendInfo{"alsa", AlsaBackendFactory::getFactory}, #endif -#ifdef HAVE_ALSA - { "alsa", AlsaBackendFactory::getFactory }, +#if HAVE_SOLARIS + BackendInfo{"solaris", SolarisBackendFactory::getFactory}, #endif -#ifdef HAVE_SOLARIS - { "solaris", SolarisBackendFactory::getFactory }, +#if HAVE_SNDIO + BackendInfo{"sndio", SndIOBackendFactory::getFactory}, #endif -#ifdef HAVE_SNDIO - { "sndio", SndIOBackendFactory::getFactory }, +#if HAVE_OSS + BackendInfo{"oss", OSSBackendFactory::getFactory}, #endif -#ifdef HAVE_OSS - { "oss", OSSBackendFactory::getFactory }, +#if HAVE_DSOUND + BackendInfo{"dsound", DSoundBackendFactory::getFactory}, #endif -#ifdef HAVE_JACK - { "jack", JackBackendFactory::getFactory }, +#if HAVE_WINMM + BackendInfo{"winmm", WinMMBackendFactory::getFactory}, #endif -#ifdef HAVE_DSOUND - { "dsound", DSoundBackendFactory::getFactory }, +#if HAVE_PORTAUDIO + BackendInfo{"port", PortBackendFactory::getFactory}, #endif -#ifdef HAVE_WINMM - { "winmm", WinMMBackendFactory::getFactory }, +#if HAVE_SDL2 + BackendInfo{"sdl2", SDL2BackendFactory::getFactory}, #endif -#ifdef HAVE_PORTAUDIO - { "port", PortBackendFactory::getFactory }, +#if HAVE_JACK + BackendInfo{"jack", JackBackendFactory::getFactory}, #endif -#ifdef HAVE_SDL2 - { "sdl2", SDL2BackendFactory::getFactory }, +#if HAVE_OTHERIO + BackendInfo{"otherio", OtherIOBackendFactory::getFactory}, #endif - { "null", NullBackendFactory::getFactory }, -#ifdef HAVE_WAVE - { "wave", WaveBackendFactory::getFactory }, + BackendInfo{"null", NullBackendFactory::getFactory}, +#if HAVE_WAVE + BackendInfo{"wave", WaveBackendFactory::getFactory}, #endif }; @@ -255,21 +273,28 @@ BackendFactory *PlaybackFactory{}; BackendFactory *CaptureFactory{}; -constexpr ALCchar alcNoError[] = "No Error"; -constexpr ALCchar alcErrInvalidDevice[] = "Invalid Device"; -constexpr ALCchar alcErrInvalidContext[] = "Invalid Context"; -constexpr ALCchar alcErrInvalidEnum[] = "Invalid Enum"; -constexpr ALCchar alcErrInvalidValue[] = "Invalid Value"; -constexpr ALCchar alcErrOutOfMemory[] = "Out of Memory"; +[[nodiscard]] constexpr auto GetNoErrorString() noexcept { return "No Error"; } +[[nodiscard]] constexpr auto GetInvalidDeviceString() noexcept { return "Invalid Device"; } +[[nodiscard]] constexpr auto GetInvalidContextString() noexcept { return "Invalid Context"; } +[[nodiscard]] constexpr auto GetInvalidEnumString() noexcept { return "Invalid Enum"; } +[[nodiscard]] constexpr auto GetInvalidValueString() noexcept { return "Invalid Value"; } +[[nodiscard]] constexpr auto GetOutOfMemoryString() noexcept { return "Out of Memory"; } +[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "OpenAL Soft\0"; } + +#ifdef _WIN32 +[[nodiscard]] constexpr auto GetDevicePrefix() noexcept { return "OpenAL Soft on "sv; } +#else +[[nodiscard]] constexpr auto GetDevicePrefix() noexcept { return std::string_view{}; } +#endif /************************************************ * Global variables ************************************************/ /* Enumerated device names */ -constexpr ALCchar alcDefaultName[] = "OpenAL Soft\0"; - +std::vector alcAllDevicesArray; +std::vector alcCaptureDeviceArray; std::string alcAllDevicesList; std::string alcCaptureDeviceList; @@ -297,36 +322,41 @@ constexpr uint DitherRNGSeed{22222u}; /************************************************ * ALC information ************************************************/ -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_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 " - "ALC_SOFT_device_clock " - "ALC_SOFT_HRTF " - "ALC_SOFT_loopback " - "ALC_SOFT_loopback_bformat " - "ALC_SOFT_output_limiter " - "ALC_SOFT_output_mode " - "ALC_SOFT_pause_device " - "ALC_SOFT_reopen_device " - "ALC_SOFT_system_events"; +[[nodiscard]] constexpr auto GetNoDeviceExtList() noexcept -> const char* +{ + return "ALC_ENUMERATE_ALL_EXT " + "ALC_ENUMERATION_EXT " + "ALC_EXT_CAPTURE " + "ALC_EXT_direct_context " + "ALC_EXT_EFX " + "ALC_EXT_thread_local_context " + "ALC_SOFT_loopback " + "ALC_SOFT_loopback_bformat " + "ALC_SOFT_reopen_device " + "ALC_SOFT_system_events"; +} +[[nodiscard]] constexpr auto GetExtensionList() noexcept -> const char* +{ + return "ALC_ENUMERATE_ALL_EXT " + "ALC_ENUMERATION_EXT " + "ALC_EXT_CAPTURE " + "ALC_EXT_debug " + "ALC_EXT_DEDICATED " + "ALC_EXT_direct_context " + "ALC_EXT_disconnect " + "ALC_EXT_EFX " + "ALC_EXT_thread_local_context " + "ALC_SOFT_device_clock " + "ALC_SOFT_HRTF " + "ALC_SOFT_loopback " + "ALC_SOFT_loopback_bformat " + "ALC_SOFT_output_limiter " + "ALC_SOFT_output_mode " + "ALC_SOFT_pause_device " + "ALC_SOFT_reopen_device " + "ALC_SOFT_system_events"; +} + constexpr int alcMajorVersion{1}; constexpr int alcMinorVersion{1}; @@ -334,19 +364,19 @@ constexpr int alcEFXMajorVersion{1}; constexpr int alcEFXMinorVersion{0}; -using DeviceRef = al::intrusive_ptr; +using DeviceRef = al::intrusive_ptr; /************************************************ * Device lists ************************************************/ -std::vector DeviceList; +std::vector DeviceList; std::vector ContextList; std::recursive_mutex ListLock; -void alc_initconfig(void) +void alc_initconfig() { if(auto loglevel = al::getenv("ALSOFT_LOGLEVEL")) { @@ -401,7 +431,7 @@ void alc_initconfig(void) if(auto suspendmode = al::getenv("__ALSOFT_SUSPEND_CONTEXT")) { - if(al::strcasecmp(suspendmode->c_str(), "ignore") == 0) + if(al::case_compare(*suspendmode, "ignore"sv) == 0) { SuspendDefers = false; TRACE("Selected context suspend behavior, \"ignore\"\n"); @@ -411,51 +441,51 @@ void alc_initconfig(void) } int capfilter{0}; -#if defined(HAVE_SSE4_1) +#if HAVE_SSE4_1 capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1; -#elif defined(HAVE_SSE3) +#elif HAVE_SSE3 capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3; -#elif defined(HAVE_SSE2) +#elif HAVE_SSE2 capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2; -#elif defined(HAVE_SSE) +#elif HAVE_SSE capfilter |= CPU_CAP_SSE; #endif -#ifdef HAVE_NEON +#if HAVE_NEON capfilter |= CPU_CAP_NEON; #endif - if(auto cpuopt = ConfigValueStr(nullptr, nullptr, "disable-cpu-exts")) + if(auto cpuopt = ConfigValueStr({}, {}, "disable-cpu-exts"sv)) { - const char *str{cpuopt->c_str()}; - if(al::strcasecmp(str, "all") == 0) + std::string_view cpulist{*cpuopt}; + if(al::case_compare(cpulist, "all"sv) == 0) capfilter = 0; - else + else while(!cpulist.empty()) { - const char *next = str; - do { - str = next; - while(isspace(str[0])) - str++; - next = strchr(str, ','); - - if(!str[0] || str[0] == ',') - continue; - - size_t len{next ? static_cast(next-str) : strlen(str)}; - while(len > 0 && isspace(str[len-1])) - len--; - if(len == 3 && al::strncasecmp(str, "sse", len) == 0) - capfilter &= ~CPU_CAP_SSE; - else if(len == 4 && al::strncasecmp(str, "sse2", len) == 0) - capfilter &= ~CPU_CAP_SSE2; - else if(len == 4 && al::strncasecmp(str, "sse3", len) == 0) - capfilter &= ~CPU_CAP_SSE3; - else if(len == 6 && al::strncasecmp(str, "sse4.1", len) == 0) - capfilter &= ~CPU_CAP_SSE4_1; - else if(len == 4 && al::strncasecmp(str, "neon", len) == 0) - capfilter &= ~CPU_CAP_NEON; - else - WARN("Invalid CPU extension \"%s\"\n", str); - } while(next++); + auto nextpos = std::min(cpulist.find(','), cpulist.size()); + auto entry = cpulist.substr(0, nextpos); + + while(nextpos < cpulist.size() && cpulist[nextpos] == ',') + ++nextpos; + cpulist.remove_prefix(nextpos); + + while(!entry.empty() && std::isspace(entry.front())) + entry.remove_prefix(1); + while(!entry.empty() && std::isspace(entry.back())) + entry.remove_suffix(1); + if(entry.empty()) + continue; + + if(al::case_compare(entry, "sse"sv) == 0) + capfilter &= ~CPU_CAP_SSE; + else if(al::case_compare(entry, "sse2"sv) == 0) + capfilter &= ~CPU_CAP_SSE2; + else if(al::case_compare(entry, "sse3"sv) == 0) + capfilter &= ~CPU_CAP_SSE3; + else if(al::case_compare(entry, "sse4.1"sv) == 0) + capfilter &= ~CPU_CAP_SSE4_1; + else if(al::case_compare(entry, "neon"sv) == 0) + capfilter &= ~CPU_CAP_NEON; + else + WARN("Invalid CPU extension \"%.*s\"\n", al::sizei(entry), entry.data()); } } if(auto cpuopt = GetCPUInfo()) @@ -476,58 +506,56 @@ void alc_initconfig(void) CPUCapFlags = caps & capfilter; } - if(auto priopt = ConfigValueInt(nullptr, nullptr, "rt-prio")) + if(auto priopt = ConfigValueInt({}, {}, "rt-prio"sv)) RTPrioLevel = *priopt; - if(auto limopt = ConfigValueBool(nullptr, nullptr, "rt-time-limit")) + if(auto limopt = ConfigValueBool({}, {}, "rt-time-limit"sv)) AllowRTTimeLimit = *limopt; { CompatFlagBitset compatflags{}; - auto checkflag = [](const char *envname, const char *optname) -> bool + auto checkflag = [](const char *envname, const std::string_view optname) -> bool { if(auto optval = al::getenv(envname)) { - if(al::strcasecmp(optval->c_str(), "true") == 0 - || strtol(optval->c_str(), nullptr, 0) == 1) - return true; - return false; + return al::case_compare(*optval, "true"sv) == 0 + || strtol(optval->c_str(), nullptr, 0) == 1; } - return GetConfigValueBool(nullptr, "game_compat", optname, false); + return GetConfigValueBool({}, "game_compat", optname, false); }; - sBufferSubDataCompat = checkflag("__ALSOFT_ENABLE_SUB_DATA_EXT", "enable-sub-data-ext"); - compatflags.set(CompatFlags::ReverseX, checkflag("__ALSOFT_REVERSE_X", "reverse-x")); - compatflags.set(CompatFlags::ReverseY, checkflag("__ALSOFT_REVERSE_Y", "reverse-y")); - compatflags.set(CompatFlags::ReverseZ, checkflag("__ALSOFT_REVERSE_Z", "reverse-z")); + sBufferSubDataCompat = checkflag("__ALSOFT_ENABLE_SUB_DATA_EXT", "enable-sub-data-ext"sv); + compatflags.set(CompatFlags::ReverseX, checkflag("__ALSOFT_REVERSE_X", "reverse-x"sv)); + compatflags.set(CompatFlags::ReverseY, checkflag("__ALSOFT_REVERSE_Y", "reverse-y"sv)); + compatflags.set(CompatFlags::ReverseZ, checkflag("__ALSOFT_REVERSE_Z", "reverse-z"sv)); - aluInit(compatflags, ConfigValueFloat(nullptr, "game_compat", "nfc-scale").value_or(1.0f)); + aluInit(compatflags, ConfigValueFloat({}, "game_compat"sv, "nfc-scale"sv).value_or(1.0f)); } - Voice::InitMixer(ConfigValueStr(nullptr, nullptr, "resampler")); + Voice::InitMixer(ConfigValueStr({}, {}, "resampler"sv)); - if(auto uhjfiltopt = ConfigValueStr(nullptr, "uhj", "decode-filter")) + if(auto uhjfiltopt = ConfigValueStr({}, "uhj"sv, "decode-filter"sv)) { - if(al::strcasecmp(uhjfiltopt->c_str(), "fir256") == 0) + if(al::case_compare(*uhjfiltopt, "fir256"sv) == 0) UhjDecodeQuality = UhjQualityType::FIR256; - else if(al::strcasecmp(uhjfiltopt->c_str(), "fir512") == 0) + else if(al::case_compare(*uhjfiltopt, "fir512"sv) == 0) UhjDecodeQuality = UhjQualityType::FIR512; - else if(al::strcasecmp(uhjfiltopt->c_str(), "iir") == 0) + else if(al::case_compare(*uhjfiltopt, "iir"sv) == 0) UhjDecodeQuality = UhjQualityType::IIR; else WARN("Unsupported uhj/decode-filter: %s\n", uhjfiltopt->c_str()); } - if(auto uhjfiltopt = ConfigValueStr(nullptr, "uhj", "encode-filter")) + if(auto uhjfiltopt = ConfigValueStr({}, "uhj"sv, "encode-filter"sv)) { - if(al::strcasecmp(uhjfiltopt->c_str(), "fir256") == 0) + if(al::case_compare(*uhjfiltopt, "fir256"sv) == 0) UhjEncodeQuality = UhjQualityType::FIR256; - else if(al::strcasecmp(uhjfiltopt->c_str(), "fir512") == 0) + else if(al::case_compare(*uhjfiltopt, "fir512"sv) == 0) UhjEncodeQuality = UhjQualityType::FIR512; - else if(al::strcasecmp(uhjfiltopt->c_str(), "iir") == 0) + else if(al::case_compare(*uhjfiltopt, "iir"sv) == 0) UhjEncodeQuality = UhjQualityType::IIR; else WARN("Unsupported uhj/encode-filter: %s\n", uhjfiltopt->c_str()); } - auto traperr = al::getenv("ALSOFT_TRAP_ERROR"); - if(traperr && (al::strcasecmp(traperr->c_str(), "true") == 0 + if(auto traperr = al::getenv("ALSOFT_TRAP_ERROR"); traperr + && (al::case_compare(*traperr, "true"sv) == 0 || std::strtol(traperr->c_str(), nullptr, 0) == 1)) { TrapALError = true; @@ -537,66 +565,69 @@ void alc_initconfig(void) { traperr = al::getenv("ALSOFT_TRAP_AL_ERROR"); if(traperr) - TrapALError = al::strcasecmp(traperr->c_str(), "true") == 0 + TrapALError = al::case_compare(*traperr, "true"sv) == 0 || strtol(traperr->c_str(), nullptr, 0) == 1; else - TrapALError = !!GetConfigValueBool(nullptr, nullptr, "trap-al-error", false); + TrapALError = GetConfigValueBool({}, {}, "trap-al-error"sv, false); traperr = al::getenv("ALSOFT_TRAP_ALC_ERROR"); if(traperr) - TrapALCError = al::strcasecmp(traperr->c_str(), "true") == 0 + TrapALCError = al::case_compare(*traperr, "true"sv) == 0 || strtol(traperr->c_str(), nullptr, 0) == 1; else - TrapALCError = !!GetConfigValueBool(nullptr, nullptr, "trap-alc-error", false); + TrapALCError = GetConfigValueBool({}, {}, "trap-alc-error"sv, false); } - if(auto boostopt = ConfigValueFloat(nullptr, "reverb", "boost")) + if(auto boostopt = ConfigValueFloat({}, "reverb"sv, "boost"sv)) { - const float valf{std::isfinite(*boostopt) ? clampf(*boostopt, -24.0f, 24.0f) : 0.0f}; + const float valf{std::isfinite(*boostopt) ? std::clamp(*boostopt, -24.0f, 24.0f) : 0.0f}; ReverbBoost *= std::pow(10.0f, valf / 20.0f); } - auto BackendListEnd = std::end(BackendList); + auto BackendListEnd = BackendList.end(); auto devopt = al::getenv("ALSOFT_DRIVERS"); - if(devopt || (devopt=ConfigValueStr(nullptr, nullptr, "drivers"))) + if(!devopt) devopt = ConfigValueStr({}, {}, "drivers"sv); + if(devopt) { - auto backendlist_cur = std::begin(BackendList); + auto backendlist_cur = BackendList.begin(); bool endlist{true}; - const char *next{devopt->c_str()}; - do { - const char *devs{next}; - while(isspace(devs[0])) - devs++; - next = strchr(devs, ','); - - const bool delitem{devs[0] == '-'}; - if(devs[0] == '-') devs++; + std::string_view drvlist{*devopt}; + while(!drvlist.empty()) + { + auto nextpos = std::min(drvlist.find(','), drvlist.size()); + auto entry = drvlist.substr(0, nextpos); - if(!devs[0] || devs[0] == ',') + endlist = true; + if(nextpos < drvlist.size()) { endlist = false; - continue; + while(nextpos < drvlist.size() && drvlist[nextpos] == ',') + ++nextpos; } - endlist = true; + drvlist.remove_prefix(nextpos); + + while(!entry.empty() && std::isspace(entry.front())) + entry.remove_prefix(1); + const bool delitem{!entry.empty() && entry.front() == '-'}; + if(delitem) entry.remove_prefix(1); + + while(!entry.empty() && std::isspace(entry.back())) + entry.remove_suffix(1); + if(entry.empty()) + continue; - size_t len{next ? (static_cast(next-devs)) : strlen(devs)}; - while(len > 0 && isspace(devs[len-1])) --len; #ifdef HAVE_WASAPI /* HACK: For backwards compatibility, convert backend references of * mmdevapi to wasapi. This should eventually be removed. */ - if(len == 8 && strncmp(devs, "mmdevapi", len) == 0) - { - devs = "wasapi"; - len = 6; - } + if(entry == "mmdevapi"sv) + entry = "wasapi"sv; #endif - auto find_backend = [devs,len](const BackendInfo &backend) -> bool - { return len == strlen(backend.name) && strncmp(backend.name, devs, len) == 0; }; - auto this_backend = std::find_if(std::begin(BackendList), BackendListEnd, - find_backend); + auto find_backend = [entry](const BackendInfo &backend) -> bool + { return entry == backend.name; }; + auto this_backend = std::find_if(BackendList.begin(), BackendListEnd, find_backend); if(this_backend == BackendListEnd) continue; @@ -605,7 +636,7 @@ void alc_initconfig(void) BackendListEnd = std::move(this_backend+1, BackendListEnd, this_backend); else backendlist_cur = std::rotate(backendlist_cur, this_backend, this_backend+1); - } while(next++); + } if(endlist) BackendListEnd = backendlist_cur; @@ -635,7 +666,7 @@ void alc_initconfig(void) TRACE("Added \"%s\" for capture\n", backend.name); } }; - std::for_each(std::begin(BackendList), BackendListEnd, init_backend); + std::for_each(BackendList.begin(), BackendListEnd, init_backend); LoopbackBackendFactory::getFactory().init(); @@ -644,36 +675,32 @@ void alc_initconfig(void) if(!CaptureFactory) WARN("No capture backend available!\n"); - if(auto exclopt = ConfigValueStr(nullptr, nullptr, "excludefx")) + if(auto exclopt = ConfigValueStr({}, {}, "excludefx"sv)) { - const char *next{exclopt->c_str()}; - do { - const char *str{next}; - next = strchr(str, ','); - - if(!str[0] || next == str) - continue; + std::string_view exclude{*exclopt}; + while(!exclude.empty()) + { + const auto nextpos = exclude.find(','); + const auto entry = exclude.substr(0, nextpos); + exclude.remove_prefix((nextpos < exclude.size()) ? nextpos+1 : exclude.size()); - size_t len{next ? static_cast(next-str) : strlen(str)}; - for(const EffectList &effectitem : gEffectList) - { - if(len == strlen(effectitem.name) && - strncmp(effectitem.name, str, len) == 0) - DisabledEffects[effectitem.type] = true; - } - } while(next++); + std::for_each(gEffectList.cbegin(), gEffectList.cend(), + [entry](const EffectList &effectitem) noexcept + { + if(entry == std::data(effectitem.name)) + DisabledEffects.set(effectitem.type); + }); + } } InitEffect(&ALCcontext::sDefaultEffect); auto defrevopt = al::getenv("ALSOFT_DEFAULT_REVERB"); - if(defrevopt || (defrevopt=ConfigValueStr(nullptr, nullptr, "default-reverb"))) - LoadReverbPreset(defrevopt->c_str(), &ALCcontext::sDefaultEffect); + if(!defrevopt) defrevopt = ConfigValueStr({}, {}, "default-reverb"sv); + if(defrevopt) LoadReverbPreset(*defrevopt, &ALCcontext::sDefaultEffect); -#ifdef ALSOFT_EAX +#if ALSOFT_EAX { - static constexpr char eax_block_name[] = "eax"; - - if(const auto eax_enable_opt = ConfigValueBool(nullptr, eax_block_name, "enable")) + if(const auto eax_enable_opt = ConfigValueBool({}, "eax", "enable")) { eax_g_is_enabled = *eax_enable_opt; if(!eax_g_is_enabled) @@ -682,15 +709,15 @@ void alc_initconfig(void) else eax_g_is_enabled = true; - if((DisabledEffects[EAXREVERB_EFFECT] || DisabledEffects[CHORUS_EFFECT]) + if((DisabledEffects.test(EAXREVERB_EFFECT) || DisabledEffects.test(CHORUS_EFFECT)) && eax_g_is_enabled) { eax_g_is_enabled = false; TRACE("EAX disabled because %s disabled.\n", - (DisabledEffects[EAXREVERB_EFFECT] && DisabledEffects[CHORUS_EFFECT]) + (DisabledEffects.test(EAXREVERB_EFFECT) && DisabledEffects.test(CHORUS_EFFECT)) ? "EAXReverb and Chorus are" : - DisabledEffects[EAXREVERB_EFFECT] ? "EAXReverb is" : - DisabledEffects[CHORUS_EFFECT] ? "Chorus is" : ""); + DisabledEffects.test(EAXREVERB_EFFECT) ? "EAXReverb is" : + DisabledEffects.test(CHORUS_EFFECT) ? "Chorus is" : ""); } } #endif // ALSOFT_EAX @@ -706,63 +733,107 @@ void ProbeAllDevicesList() { InitConfig(); - std::lock_guard _{ListLock}; + std::lock_guard listlock{ListLock}; if(!PlaybackFactory) + { + decltype(alcAllDevicesArray){}.swap(alcAllDevicesArray); decltype(alcAllDevicesList){}.swap(alcAllDevicesList); + } else { - std::string names{PlaybackFactory->probe(BackendType::Playback)}; - if(names.empty()) names += '\0'; - names.swap(alcAllDevicesList); + alcAllDevicesArray = PlaybackFactory->enumerate(BackendType::Playback); + if(const auto prefix = GetDevicePrefix(); !prefix.empty()) + std::for_each(alcAllDevicesArray.begin(), alcAllDevicesArray.end(), + [prefix](std::string &name) { name.insert(0, prefix); }); + + decltype(alcAllDevicesList){}.swap(alcAllDevicesList); + if(alcAllDevicesArray.empty()) + alcAllDevicesList += '\0'; + else for(auto &devname : alcAllDevicesArray) + alcAllDevicesList.append(devname) += '\0'; } } void ProbeCaptureDeviceList() { InitConfig(); - std::lock_guard _{ListLock}; + std::lock_guard listlock{ListLock}; if(!CaptureFactory) + { + decltype(alcCaptureDeviceArray){}.swap(alcCaptureDeviceArray); decltype(alcCaptureDeviceList){}.swap(alcCaptureDeviceList); + } else { - std::string names{CaptureFactory->probe(BackendType::Capture)}; - if(names.empty()) names += '\0'; - names.swap(alcCaptureDeviceList); + alcCaptureDeviceArray = CaptureFactory->enumerate(BackendType::Capture); + if(const auto prefix = GetDevicePrefix(); !prefix.empty()) + std::for_each(alcCaptureDeviceArray.begin(), alcCaptureDeviceArray.end(), + [prefix](std::string &name) { name.insert(0, prefix); }); + + decltype(alcCaptureDeviceList){}.swap(alcCaptureDeviceList); + if(alcCaptureDeviceArray.empty()) + alcCaptureDeviceList += '\0'; + else for(auto &devname : alcCaptureDeviceArray) + alcCaptureDeviceList.append(devname) += '\0'; } } +al::span SpanFromAttributeList(const ALCint *attribs) noexcept +{ + al::span attrSpan; + if(attribs) + { + const ALCint *attrEnd{attribs}; + while(*attrEnd != 0) + attrEnd += 2; /* NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ + attrSpan = {attribs, attrEnd}; + } + return attrSpan; +} + struct DevFmtPair { DevFmtChannels chans; DevFmtType type; }; std::optional DecomposeDevFormat(ALenum format) { - static const struct { + struct FormatType { ALenum format; DevFmtChannels channels; DevFmtType type; - } list[] = { - { AL_FORMAT_MONO8, DevFmtMono, DevFmtUByte }, - { AL_FORMAT_MONO16, DevFmtMono, DevFmtShort }, - { AL_FORMAT_MONO_FLOAT32, DevFmtMono, DevFmtFloat }, - - { AL_FORMAT_STEREO8, DevFmtStereo, DevFmtUByte }, - { AL_FORMAT_STEREO16, DevFmtStereo, DevFmtShort }, - { AL_FORMAT_STEREO_FLOAT32, DevFmtStereo, DevFmtFloat }, - - { AL_FORMAT_QUAD8, DevFmtQuad, DevFmtUByte }, - { AL_FORMAT_QUAD16, DevFmtQuad, DevFmtShort }, - { AL_FORMAT_QUAD32, DevFmtQuad, DevFmtFloat }, - - { AL_FORMAT_51CHN8, DevFmtX51, DevFmtUByte }, - { AL_FORMAT_51CHN16, DevFmtX51, DevFmtShort }, - { AL_FORMAT_51CHN32, DevFmtX51, DevFmtFloat }, - - { AL_FORMAT_61CHN8, DevFmtX61, DevFmtUByte }, - { AL_FORMAT_61CHN16, DevFmtX61, DevFmtShort }, - { AL_FORMAT_61CHN32, DevFmtX61, DevFmtFloat }, - - { AL_FORMAT_71CHN8, DevFmtX71, DevFmtUByte }, - { AL_FORMAT_71CHN16, DevFmtX71, DevFmtShort }, - { AL_FORMAT_71CHN32, DevFmtX71, DevFmtFloat }, + }; + static constexpr std::array list{ + FormatType{AL_FORMAT_MONO8, DevFmtMono, DevFmtUByte}, + FormatType{AL_FORMAT_MONO16, DevFmtMono, DevFmtShort}, + FormatType{AL_FORMAT_MONO_I32, DevFmtMono, DevFmtInt}, + FormatType{AL_FORMAT_MONO_FLOAT32, DevFmtMono, DevFmtFloat}, + + FormatType{AL_FORMAT_STEREO8, DevFmtStereo, DevFmtUByte}, + FormatType{AL_FORMAT_STEREO16, DevFmtStereo, DevFmtShort}, + FormatType{AL_FORMAT_STEREO_I32, DevFmtStereo, DevFmtInt}, + FormatType{AL_FORMAT_STEREO_FLOAT32, DevFmtStereo, DevFmtFloat}, + + FormatType{AL_FORMAT_QUAD8, DevFmtQuad, DevFmtUByte}, + FormatType{AL_FORMAT_QUAD16, DevFmtQuad, DevFmtShort}, + FormatType{AL_FORMAT_QUAD32, DevFmtQuad, DevFmtFloat}, + FormatType{AL_FORMAT_QUAD_I32, DevFmtQuad, DevFmtInt}, + FormatType{AL_FORMAT_QUAD_FLOAT32, DevFmtQuad, DevFmtFloat}, + + FormatType{AL_FORMAT_51CHN8, DevFmtX51, DevFmtUByte}, + FormatType{AL_FORMAT_51CHN16, DevFmtX51, DevFmtShort}, + FormatType{AL_FORMAT_51CHN32, DevFmtX51, DevFmtFloat}, + FormatType{AL_FORMAT_51CHN_I32, DevFmtX51, DevFmtInt}, + FormatType{AL_FORMAT_51CHN_FLOAT32, DevFmtX51, DevFmtFloat}, + + FormatType{AL_FORMAT_61CHN8, DevFmtX61, DevFmtUByte}, + FormatType{AL_FORMAT_61CHN16, DevFmtX61, DevFmtShort}, + FormatType{AL_FORMAT_61CHN32, DevFmtX61, DevFmtFloat}, + FormatType{AL_FORMAT_61CHN_I32, DevFmtX61, DevFmtInt}, + FormatType{AL_FORMAT_61CHN_FLOAT32, DevFmtX61, DevFmtFloat}, + + FormatType{AL_FORMAT_71CHN8, DevFmtX71, DevFmtUByte}, + FormatType{AL_FORMAT_71CHN16, DevFmtX71, DevFmtShort}, + FormatType{AL_FORMAT_71CHN32, DevFmtX71, DevFmtFloat}, + FormatType{AL_FORMAT_71CHN_I32, DevFmtX71, DevFmtInt}, + FormatType{AL_FORMAT_71CHN_FLOAT32, DevFmtX71, DevFmtFloat}, }; for(const auto &item : list) @@ -832,6 +903,7 @@ ALCenum EnumFromDevFmt(DevFmtChannels channels) case DevFmtAmbi3D: return ALC_BFORMAT3D_SOFT; /* FIXME: Shouldn't happen. */ case DevFmtX714: + case DevFmtX7144: case DevFmtX3D71: break; } throw std::runtime_error{"Invalid DevFmtChannels: "+std::to_string(int(channels))}; @@ -880,82 +952,65 @@ 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 FrontStereoSplit{{ - {FrontLeft, 0.5f}, {FrontRight, 0.5f} -}}; -constexpr std::array FrontLeft9{{ - {FrontLeft, 1.0f/9.0f} -}}; -constexpr std::array FrontRight9{{ - {FrontRight, 1.0f/9.0f} -}}; -constexpr std::array BackMonoToFrontSplit{{ - {FrontLeft, 0.5f/9.0f}, {FrontRight, 0.5f/9.0f} -}}; -constexpr std::array LeftStereoSplit{{ - {FrontLeft, 0.5f}, {BackLeft, 0.5f} -}}; -constexpr std::array RightStereoSplit{{ - {FrontRight, 0.5f}, {BackRight, 0.5f} -}}; -constexpr std::array BackStereoSplit{{ - {BackLeft, 0.5f}, {BackRight, 0.5f} -}}; -constexpr std::array SideStereoSplit{{ - {SideLeft, 0.5f}, {SideRight, 0.5f} -}}; -constexpr std::array ToSideLeft{{ - {SideLeft, 1.0f} -}}; -constexpr std::array ToSideRight{{ - {SideRight, 1.0f} -}}; -constexpr std::array BackLeftSplit{{ - {SideLeft, 0.5f}, {BackCenter, 0.5f} -}}; -constexpr std::array BackRightSplit{{ - {SideRight, 0.5f}, {BackCenter, 0.5f} -}}; - -const std::array StereoDownmix{{ - { FrontCenter, FrontStereoSplit }, - { SideLeft, FrontLeft9 }, - { SideRight, FrontRight9 }, - { BackLeft, FrontLeft9 }, - { BackRight, FrontRight9 }, - { BackCenter, BackMonoToFrontSplit }, -}}; -const std::array QuadDownmix{{ - { FrontCenter, FrontStereoSplit }, - { SideLeft, LeftStereoSplit }, - { SideRight, RightStereoSplit }, - { BackCenter, BackStereoSplit }, -}}; -const std::array X51Downmix{{ - { BackLeft, ToSideLeft }, - { BackRight, ToSideRight }, - { BackCenter, SideStereoSplit }, -}}; -const std::array X61Downmix{{ - { BackLeft, BackLeftSplit }, - { BackRight, BackRightSplit }, -}}; -const std::array X71Downmix{{ - { BackCenter, BackStereoSplit }, -}}; - - -std::unique_ptr CreateDeviceLimiter(const ALCdevice *device, const float threshold) +constexpr float inv_sqrt2f{static_cast(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}, +}; + + +auto CreateDeviceLimiter(const al::Device *device, const float threshold) + -> std::unique_ptr { - static constexpr bool AutoKnee{true}; - static constexpr bool AutoAttack{true}; - static constexpr bool AutoRelease{true}; - static constexpr bool AutoPostGain{true}; - static constexpr bool AutoDeclip{true}; static constexpr float LookAheadTime{0.001f}; static constexpr float HoldTime{0.002f}; static constexpr float PreGainDb{0.0f}; @@ -965,9 +1020,12 @@ std::unique_ptr CreateDeviceLimiter(const ALCdevice *device, const f static constexpr float AttackTime{0.02f}; static constexpr float ReleaseTime{0.2f}; + const auto flags = Compressor::FlagBits{}.set(Compressor::AutoKnee).set(Compressor::AutoAttack) + .set(Compressor::AutoRelease).set(Compressor::AutoPostGain).set(Compressor::AutoDeclip); + return Compressor::Create(device->RealOut.Buffer.size(), static_cast(device->Frequency), - AutoKnee, AutoAttack, AutoRelease, AutoPostGain, AutoDeclip, LookAheadTime, HoldTime, - PreGainDb, PostGainDb, threshold, Ratio, KneeDb, AttackTime, ReleaseTime); + flags, LookAheadTime, HoldTime, PreGainDb, PostGainDb, threshold, Ratio, KneeDb, + AttackTime, ReleaseTime); } /** @@ -976,21 +1034,33 @@ std::unique_ptr CreateDeviceLimiter(const ALCdevice *device, const f * to jump forward or back. Must not be called while the device is running/ * mixing. */ -inline void UpdateClockBase(ALCdevice *device) +inline void UpdateClockBase(al::Device *device) { - IncrementRef(device->MixCount); - device->ClockBase += nanoseconds{seconds{device->SamplesDone}} / device->Frequency; - device->SamplesDone = 0; - IncrementRef(device->MixCount); + using std::chrono::duration_cast; + + const auto mixLock = device->getWriteMixLock(); + + auto clockBaseSec = device->mClockBaseSec.load(std::memory_order_relaxed); + auto clockBaseNSec = nanoseconds{device->mClockBaseNSec.load(std::memory_order_relaxed)}; + clockBaseNSec += nanoseconds{seconds{device->mSamplesDone.load(std::memory_order_relaxed)}} + / device->Frequency; + + clockBaseSec += duration_cast(clockBaseNSec); + clockBaseNSec %= seconds{1}; + + device->mClockBaseSec.store(clockBaseSec, std::memory_order_relaxed); + device->mClockBaseNSec.store(duration_cast(clockBaseNSec), + std::memory_order_relaxed); + device->mSamplesDone.store(0, std::memory_order_relaxed); } /** * Updates device parameters according to the attribute list (caller is * responsible for holding the list lock). */ -ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) +auto UpdateDeviceParams(al::Device *device, const al::span attrList) -> ALCenum { - if((!attrList || !attrList[0]) && device->Type == DeviceType::Loopback) + if(attrList.empty() && device->Type == DeviceType::Loopback) { WARN("Missing attributes for loopback device\n"); return ALC_INVALID_VALUE; @@ -1006,8 +1076,8 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) std::optional opttype; std::optional optlayout; std::optional optscale; - uint period_size{DEFAULT_UPDATE_SIZE}; - uint buffer_size{DEFAULT_UPDATE_SIZE * DEFAULT_NUM_UPDATES}; + uint period_size{DefaultUpdateSize}; + uint buffer_size{DefaultUpdateSize * DefaultNumUpdates}; int hrtf_id{-1}; uint aorder{0u}; @@ -1015,71 +1085,74 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) { /* Get default settings from the user configuration */ - if(auto freqopt = device->configValue(nullptr, "frequency")) + if(auto freqopt = device->configValue({}, "frequency")) { - optsrate = clampu(*freqopt, MIN_OUTPUT_RATE, MAX_OUTPUT_RATE); + optsrate = std::clamp(*freqopt, MinOutputRate, MaxOutputRate); - const double scale{static_cast(*optsrate) / DEFAULT_OUTPUT_RATE}; - period_size = static_cast(period_size*scale + 0.5); + const double scale{static_cast(*optsrate) / double{DefaultOutputRate}}; + period_size = static_cast(std::lround(period_size * scale)); } - if(auto persizeopt = device->configValue(nullptr, "period_size")) - period_size = clampu(*persizeopt, 64, 8192); - if(auto numperopt = device->configValue(nullptr, "periods")) - buffer_size = clampu(*numperopt, 2, 16) * period_size; + if(auto persizeopt = device->configValue({}, "period_size")) + period_size = std::clamp(*persizeopt, 64u, 8192u); + if(auto numperopt = device->configValue({}, "periods")) + buffer_size = std::clamp(*numperopt, 2u, 16u) * period_size; else - buffer_size = period_size * DEFAULT_NUM_UPDATES; + buffer_size = period_size * uint{DefaultNumUpdates}; - if(auto typeopt = device->configValue(nullptr, "sample-type")) + if(auto typeopt = device->configValue({}, "sample-type")) { - static constexpr struct TypeMap { - const char name[8]; + struct TypeMap { + std::string_view name; DevFmtType type; - } typelist[] = { - { "int8", DevFmtByte }, - { "uint8", DevFmtUByte }, - { "int16", DevFmtShort }, - { "uint16", DevFmtUShort }, - { "int32", DevFmtInt }, - { "uint32", DevFmtUInt }, - { "float32", DevFmtFloat }, + }; + constexpr std::array typelist{ + TypeMap{"int8"sv, DevFmtByte }, + TypeMap{"uint8"sv, DevFmtUByte }, + TypeMap{"int16"sv, DevFmtShort }, + TypeMap{"uint16"sv, DevFmtUShort}, + TypeMap{"int32"sv, DevFmtInt }, + TypeMap{"uint32"sv, DevFmtUInt }, + TypeMap{"float32"sv, DevFmtFloat }, }; const ALCchar *fmt{typeopt->c_str()}; - auto iter = std::find_if(std::begin(typelist), std::end(typelist), - [fmt](const TypeMap &entry) -> bool - { return al::strcasecmp(entry.name, fmt) == 0; }); - if(iter == std::end(typelist)) + auto iter = std::find_if(typelist.begin(), typelist.end(), + [svfmt=std::string_view{fmt}](const TypeMap &entry) -> bool + { return al::case_compare(entry.name, svfmt) == 0; }); + if(iter == typelist.end()) ERR("Unsupported sample-type: %s\n", fmt); else opttype = iter->type; } - if(auto chanopt = device->configValue(nullptr, "channels")) + if(auto chanopt = device->configValue({}, "channels")) { - static constexpr struct ChannelMap { - const char name[16]; + struct ChannelMap { + std::string_view name; DevFmtChannels chans; uint8_t order; - } chanlist[] = { - { "mono", DevFmtMono, 0 }, - { "stereo", DevFmtStereo, 0 }, - { "quad", DevFmtQuad, 0 }, - { "surround51", DevFmtX51, 0 }, - { "surround61", DevFmtX61, 0 }, - { "surround71", DevFmtX71, 0 }, - { "surround714", DevFmtX714, 0 }, - { "surround3d71", DevFmtX3D71, 0 }, - { "surround51rear", DevFmtX51, 0 }, - { "ambi1", DevFmtAmbi3D, 1 }, - { "ambi2", DevFmtAmbi3D, 2 }, - { "ambi3", DevFmtAmbi3D, 3 }, + }; + constexpr std::array chanlist{ + ChannelMap{"mono"sv, DevFmtMono, 0}, + ChannelMap{"stereo"sv, DevFmtStereo, 0}, + ChannelMap{"quad"sv, DevFmtQuad, 0}, + ChannelMap{"surround51"sv, DevFmtX51, 0}, + ChannelMap{"surround61"sv, DevFmtX61, 0}, + ChannelMap{"surround71"sv, DevFmtX71, 0}, + ChannelMap{"surround714"sv, DevFmtX714, 0}, + ChannelMap{"surround7144"sv, DevFmtX7144, 0}, + ChannelMap{"surround3d71"sv, DevFmtX3D71, 0}, + ChannelMap{"surround51rear"sv, DevFmtX51, 0}, + ChannelMap{"ambi1"sv, DevFmtAmbi3D, 1}, + ChannelMap{"ambi2"sv, DevFmtAmbi3D, 2}, + ChannelMap{"ambi3"sv, DevFmtAmbi3D, 3}, }; const ALCchar *fmt{chanopt->c_str()}; - auto iter = std::find_if(std::begin(chanlist), std::end(chanlist), - [fmt](const ChannelMap &entry) -> bool - { return al::strcasecmp(entry.name, fmt) == 0; }); - if(iter == std::end(chanlist)) + auto iter = std::find_if(chanlist.begin(), chanlist.end(), + [svfmt=std::string_view{fmt}](const ChannelMap &entry) -> bool + { return al::case_compare(entry.name, svfmt) == 0; }); + if(iter == chanlist.end()) ERR("Unsupported channels: %s\n", fmt); else { @@ -1087,82 +1160,80 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) aorder = iter->order; } } - if(auto ambiopt = device->configValue(nullptr, "ambi-format")) + if(auto ambiopt = device->configValue({}, "ambi-format"sv)) { - const ALCchar *fmt{ambiopt->c_str()}; - if(al::strcasecmp(fmt, "fuma") == 0) + if(al::case_compare(*ambiopt, "fuma"sv) == 0) { optlayout = DevAmbiLayout::FuMa; optscale = DevAmbiScaling::FuMa; } - else if(al::strcasecmp(fmt, "acn+fuma") == 0) + else if(al::case_compare(*ambiopt, "acn+fuma"sv) == 0) { optlayout = DevAmbiLayout::ACN; optscale = DevAmbiScaling::FuMa; } - else if(al::strcasecmp(fmt, "ambix") == 0 || al::strcasecmp(fmt, "acn+sn3d") == 0) + else if(al::case_compare(*ambiopt, "ambix"sv) == 0 + || al::case_compare(*ambiopt, "acn+sn3d"sv) == 0) { optlayout = DevAmbiLayout::ACN; optscale = DevAmbiScaling::SN3D; } - else if(al::strcasecmp(fmt, "acn+n3d") == 0) + else if(al::case_compare(*ambiopt, "acn+n3d"sv) == 0) { optlayout = DevAmbiLayout::ACN; optscale = DevAmbiScaling::N3D; } else - ERR("Unsupported ambi-format: %s\n", fmt); + ERR("Unsupported ambi-format: %s\n", ambiopt->c_str()); } - if(auto hrtfopt = device->configValue(nullptr, "hrtf")) + if(auto hrtfopt = device->configValue({}, "hrtf"sv)) { WARN("general/hrtf is deprecated, please use stereo-encoding instead\n"); - const char *hrtf{hrtfopt->c_str()}; - if(al::strcasecmp(hrtf, "true") == 0) + if(al::case_compare(*hrtfopt, "true"sv) == 0) stereomode = StereoEncoding::Hrtf; - else if(al::strcasecmp(hrtf, "false") == 0) + else if(al::case_compare(*hrtfopt, "false"sv) == 0) { if(!stereomode || *stereomode == StereoEncoding::Hrtf) stereomode = StereoEncoding::Default; } - else if(al::strcasecmp(hrtf, "auto") != 0) - ERR("Unexpected hrtf value: %s\n", hrtf); + else if(al::case_compare(*hrtfopt, "auto"sv) != 0) + ERR("Unexpected hrtf value: %s\n", hrtfopt->c_str()); } } - if(auto encopt = device->configValue(nullptr, "stereo-encoding")) + if(auto encopt = device->configValue({}, "stereo-encoding"sv)) { - const char *mode{encopt->c_str()}; - if(al::strcasecmp(mode, "basic") == 0 || al::strcasecmp(mode, "panpot") == 0) + if(al::case_compare(*encopt, "basic"sv) == 0 || al::case_compare(*encopt, "panpot"sv) == 0) stereomode = StereoEncoding::Basic; - else if(al::strcasecmp(mode, "uhj") == 0) + else if(al::case_compare(*encopt, "uhj") == 0) stereomode = StereoEncoding::Uhj; - else if(al::strcasecmp(mode, "hrtf") == 0) + else if(al::case_compare(*encopt, "hrtf") == 0) stereomode = StereoEncoding::Hrtf; else - ERR("Unexpected stereo-encoding: %s\n", mode); + ERR("Unexpected stereo-encoding: %s\n", encopt->c_str()); } // Check for app-specified attributes - if(attrList && attrList[0]) + if(!attrList.empty()) { ALenum outmode{ALC_ANY_SOFT}; std::optional opthrtf; int freqAttr{}; #define ATTRIBUTE(a) a: TRACE("%s = %d\n", #a, attrList[attrIdx + 1]); - size_t attrIdx{0}; - while(attrList[attrIdx]) +#define ATTRIBUTE_HEX(a) a: TRACE("%s = 0x%x\n", #a, attrList[attrIdx + 1]); + for(size_t attrIdx{0};attrIdx < attrList.size();attrIdx+=2) { switch(attrList[attrIdx]) { - case ATTRIBUTE(ALC_FORMAT_CHANNELS_SOFT) + case ATTRIBUTE_HEX(ALC_FORMAT_CHANNELS_SOFT) if(device->Type == DeviceType::Loopback) optchans = DevFmtChannelsFromEnum(attrList[attrIdx + 1]); break; - case ATTRIBUTE(ALC_FORMAT_TYPE_SOFT) + case ATTRIBUTE_HEX(ALC_FORMAT_TYPE_SOFT) if(device->Type == DeviceType::Loopback) opttype = DevFmtTypeFromEnum(attrList[attrIdx + 1]); break; @@ -1171,12 +1242,12 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) freqAttr = attrList[attrIdx + 1]; break; - case ATTRIBUTE(ALC_AMBISONIC_LAYOUT_SOFT) + case ATTRIBUTE_HEX(ALC_AMBISONIC_LAYOUT_SOFT) if(device->Type == DeviceType::Loopback) optlayout = DevAmbiLayoutFromEnum(attrList[attrIdx + 1]); break; - case ATTRIBUTE(ALC_AMBISONIC_SCALING_SOFT) + case ATTRIBUTE_HEX(ALC_AMBISONIC_SCALING_SOFT) if(device->Type == DeviceType::Loopback) optscale = DevAmbiScalingFromEnum(attrList[attrIdx + 1]); break; @@ -1198,8 +1269,8 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) case ATTRIBUTE(ALC_MAX_AUXILIARY_SENDS) numSends = static_cast(attrList[attrIdx + 1]); - if(numSends > INT_MAX) numSends = 0; - else numSends = minu(numSends, MAX_SENDS); + if(numSends > uint{std::numeric_limits::max()}) numSends = 0; + else numSends = std::min(numSends, uint{MaxSendCount}); break; case ATTRIBUTE(ALC_HRTF_SOFT) @@ -1224,25 +1295,27 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) optlimit = std::nullopt; break; - case ATTRIBUTE(ALC_OUTPUT_MODE_SOFT) + case ATTRIBUTE_HEX(ALC_OUTPUT_MODE_SOFT) outmode = attrList[attrIdx + 1]; break; + case ATTRIBUTE_HEX(ALC_CONTEXT_FLAGS_EXT) + break; + default: TRACE("0x%04X = %d (0x%x)\n", attrList[attrIdx], attrList[attrIdx + 1], attrList[attrIdx + 1]); break; } - - attrIdx += 2; } +#undef ATTRIBUTE_HEX #undef ATTRIBUTE if(device->Type == DeviceType::Loopback) { if(!optchans || !opttype) return ALC_INVALID_VALUE; - if(freqAttr < MIN_OUTPUT_RATE || freqAttr > MAX_OUTPUT_RATE) + if(freqAttr < int{MinOutputRate} || freqAttr > int{MaxOutputRate}) return ALC_INVALID_VALUE; if(*optchans == DevFmtAmbi3D) { @@ -1292,7 +1365,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) if(outmode != ALC_ANY_SOFT) { - using OutputMode = ALCdevice::OutputMode; + using OutputMode = al::Device::OutputMode; switch(OutputMode(outmode)) { case OutputMode::Any: break; @@ -1319,12 +1392,12 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) if(freqAttr) { - uint oldrate = optsrate.value_or(DEFAULT_OUTPUT_RATE); - freqAttr = clampi(freqAttr, MIN_OUTPUT_RATE, MAX_OUTPUT_RATE); + uint oldrate = optsrate.value_or(DefaultOutputRate); + freqAttr = std::clamp(freqAttr, MinOutputRate, MaxOutputRate); const double scale{static_cast(freqAttr) / oldrate}; - period_size = static_cast(period_size*scale + 0.5); - buffer_size = static_cast(buffer_size*scale + 0.5); + period_size = static_cast(std::lround(period_size * scale)); + buffer_size = static_cast(std::lround(buffer_size * scale)); optsrate = static_cast(freqAttr); } } @@ -1332,16 +1405,19 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) /* If a context is already running on the device, stop playback so the * device attributes can be updated. */ - if(device->Flags.test(DeviceRunning)) + if(device->mDeviceState == DeviceState::Playing) + { device->Backend->stop(); - device->Flags.reset(DeviceRunning); + device->mDeviceState = DeviceState::Unprepared; + } UpdateClockBase(device); } - if(device->Flags.test(DeviceRunning)) + if(device->mDeviceState == DeviceState::Playing) return ALC_NO_ERROR; + device->mDeviceState = DeviceState::Unprepared; device->AvgSpeakerDist = 0.0f; device->mNFCtrlFilter = NfcFilter{}; device->mUhjEncoder = nullptr; @@ -1395,14 +1471,14 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) device->mAmbiOrder = 0; device->BufferSize = buffer_size; device->UpdateSize = period_size; - device->Frequency = optsrate.value_or(DEFAULT_OUTPUT_RATE); + device->Frequency = optsrate.value_or(DefaultOutputRate); device->Flags.set(FrequencyRequest, optsrate.has_value()) .set(ChannelsRequest, optchans.has_value()) .set(SampleTypeRequest, opttype.has_value()); if(device->FmtChans == DevFmtAmbi3D) { - device->mAmbiOrder = clampu(aorder, 1, MaxAmbiOrder); + device->mAmbiOrder = std::clamp(aorder, 1u, uint{MaxAmbiOrder}); device->mAmbiLayout = optlayout.value_or(DevAmbiLayout::Default); device->mAmbiScale = optscale.value_or(DevAmbiScaling::Default); if(device->mAmbiOrder > 3 @@ -1410,11 +1486,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) || device->mAmbiScale == DevAmbiScaling::FuMa)) { ERR("FuMa is incompatible with %d%s order ambisonics (up to 3rd order only)\n", - device->mAmbiOrder, - (((device->mAmbiOrder%100)/10) == 1) ? "th" : - ((device->mAmbiOrder%10) == 1) ? "st" : - ((device->mAmbiOrder%10) == 2) ? "nd" : - ((device->mAmbiOrder%10) == 3) ? "rd" : "th"); + device->mAmbiOrder, GetCounterSuffix(device->mAmbiOrder)); device->mAmbiOrder = 3; } } @@ -1464,15 +1536,14 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) if(device->Type != DeviceType::Loopback) { - if(auto modeopt = device->configValue(nullptr, "stereo-mode")) + if(auto modeopt = device->configValue({}, "stereo-mode")) { - const char *mode{modeopt->c_str()}; - if(al::strcasecmp(mode, "headphones") == 0) + if(al::case_compare(*modeopt, "headphones"sv) == 0) device->Flags.set(DirectEar); - else if(al::strcasecmp(mode, "speakers") == 0) + else if(al::case_compare(*modeopt, "speakers"sv) == 0) device->Flags.reset(DirectEar); - else if(al::strcasecmp(mode, "auto") != 0) - ERR("Unexpected stereo-mode: %s\n", mode); + else if(al::case_compare(*modeopt, "auto"sv) != 0) + ERR("Unexpected stereo-mode: %s\n", modeopt->c_str()); } } @@ -1481,24 +1552,24 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) /* Calculate the max number of sources, and split them between the mono and * stereo count given the requested number of stereo sources. */ - if(auto srcsopt = device->configValue(nullptr, "sources")) + if(auto srcsopt = device->configValue({}, "sources"sv)) { if(*srcsopt <= 0) numMono = 256; - else numMono = maxu(*srcsopt, 16); + else numMono = std::max(*srcsopt, 16u); } else { - numMono = minu(numMono, INT_MAX-numStereo); - numMono = maxu(numMono+numStereo, 256); + numMono = std::min(numMono, std::numeric_limits::max()-numStereo); + numMono = std::max(numMono+numStereo, 256u); } - numStereo = minu(numStereo, numMono); + numStereo = std::min(numStereo, numMono); numMono -= numStereo; device->SourcesMax = numMono + numStereo; device->NumMonoSources = numMono; device->NumStereoSources = numStereo; - if(auto sendsopt = device->configValue(nullptr, "sends")) - numSends = minu(numSends, static_cast(clampi(*sendsopt, 0, MAX_SENDS))); + if(auto sendsopt = device->configValue({}, "sends"sv)) + numSends = std::min(numSends, std::clamp(*sendsopt, 0u, uint{MaxSendCount})); device->NumAuxSends = numSends; TRACE("Max sources: %d (%d + %d), effect slots: %d, sends: %d\n", @@ -1517,17 +1588,18 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) case DevFmtX61: device->RealOut.RemixMap = X61Downmix; break; case DevFmtX71: device->RealOut.RemixMap = X71Downmix; break; case DevFmtX714: device->RealOut.RemixMap = X71Downmix; break; + case DevFmtX7144: device->RealOut.RemixMap = X71Downmix; break; case DevFmtX3D71: device->RealOut.RemixMap = X51Downmix; break; case DevFmtAmbi3D: break; } - nanoseconds::rep sample_delay{0}; + size_t sample_delay{0}; if(auto *encoder{device->mUhjEncoder.get()}) sample_delay += encoder->getDelay(); - if(device->getConfigValueBool(nullptr, "dither", true)) + if(device->getConfigValueBool({}, "dither"sv, true)) { - int depth{device->configValue(nullptr, "dither-depth").value_or(0)}; + int depth{device->configValue({}, "dither-depth"sv).value_or(0)}; if(depth <= 0) { switch(device->FmtType) @@ -1549,7 +1621,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) if(depth > 0) { - depth = clampi(depth, 2, 24); + depth = std::clamp(depth, 2, 24); device->DitherDepth = std::pow(2.0f, static_cast(depth-1)); } } @@ -1560,7 +1632,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) device->DitherDepth); if(!optlimit) - optlimit = device->configValue(nullptr, "output-limiter"); + optlimit = device->configValue({}, "output-limiter"); /* If the gain limiter is unset, use the limiter for integer-based output * (where samples must be clamped), and don't for floating-point (which can @@ -1582,7 +1654,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) break; } } - if(optlimit.value_or(false) == false) + if(!optlimit.value_or(false)) TRACE("Output limiter disabled\n"); else { @@ -1614,84 +1686,100 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) } /* Convert the sample delay from samples to nanosamples to nanoseconds. */ + sample_delay = std::min(sample_delay, std::numeric_limits::max()); device->FixedLatency += nanoseconds{seconds{sample_delay}} / device->Frequency; TRACE("Fixed device latency: %" PRId64 "ns\n", int64_t{device->FixedLatency.count()}); FPUCtl mixer_mode{}; - for(ContextBase *ctxbase : *device->mContexts.load()) + auto reset_context = [device](ContextBase *ctxbase) { - auto *context = static_cast(ctxbase); + auto *context = dynamic_cast(ctxbase); + assert(context != nullptr); + if(!context) return; std::unique_lock proplock{context->mPropLock}; std::unique_lock slotlock{context->mEffectSlotLock}; /* Clear out unused effect slot clusters. */ - auto slot_cluster_not_in_use = [](ContextBase::EffectSlotCluster &cluster) + auto slot_cluster_not_in_use = [](ContextBase::EffectSlotCluster &clusterptr) -> bool { - for(size_t i{0};i < ContextBase::EffectSlotClusterSize;++i) - { - if(cluster[i].InUse) - return false; - } - return true; + return std::none_of(clusterptr->begin(), clusterptr->end(), + std::mem_fn(&EffectSlot::InUse)); }; - auto slotcluster_iter = std::remove_if(context->mEffectSlotClusters.begin(), + auto slotcluster_end = std::remove_if(context->mEffectSlotClusters.begin(), context->mEffectSlotClusters.end(), slot_cluster_not_in_use); - context->mEffectSlotClusters.erase(slotcluster_iter, context->mEffectSlotClusters.end()); + context->mEffectSlotClusters.erase(slotcluster_end, context->mEffectSlotClusters.end()); /* Free all wet buffers. Any in use will be reallocated with an updated * configuration in aluInitEffectPanning. */ - for(auto&& slots : context->mEffectSlotClusters) + auto clear_wetbuffers = [](ContextBase::EffectSlotCluster &clusterptr) { - for(size_t i{0};i < ContextBase::EffectSlotClusterSize;++i) + auto clear_buffer = [](EffectSlot &slot) { - slots[i].mWetBuffer.clear(); - slots[i].mWetBuffer.shrink_to_fit(); - slots[i].Wet.Buffer = {}; - } - } + slot.mWetBuffer.clear(); + slot.mWetBuffer.shrink_to_fit(); + slot.Wet.Buffer = {}; + }; + std::for_each(clusterptr->begin(), clusterptr->end(), clear_buffer); + }; + std::for_each(context->mEffectSlotClusters.begin(), context->mEffectSlotClusters.end(), + clear_wetbuffers); if(ALeffectslot *slot{context->mDefaultSlot.get()}) { - aluInitEffectPanning(slot->mSlot, context); + auto *slotbase = slot->mSlot; + aluInitEffectPanning(slotbase, context); + + if(auto *props = slotbase->Update.exchange(nullptr, std::memory_order_relaxed)) + AtomicReplaceHead(context->mFreeEffectSlotProps, props); EffectState *state{slot->Effect.State.get()}; state->mOutTarget = device->Dry.Buffer; state->deviceUpdate(device, slot->Buffer); - slot->updateProps(context); + slot->mPropsDirty = true; } if(EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_relaxed)}) - std::fill_n(curarray->end(), curarray->size(), nullptr); - for(auto &sublist : context->mEffectSlotList) + std::fill(curarray->begin()+ptrdiff_t(curarray->size()>>1), curarray->end(), nullptr); + auto reset_slots = [device,context](EffectSlotSubList &sublist) { uint64_t usemask{~sublist.FreeMask}; while(usemask) { - const int idx{al::countr_zero(usemask)}; - ALeffectslot *slot{sublist.EffectSlots + idx}; + const auto idx = static_cast(al::countr_zero(usemask)); + auto &slot = (*sublist.EffectSlots)[idx]; usemask &= ~(1_u64 << idx); - aluInitEffectPanning(slot->mSlot, context); + auto *slotbase = slot.mSlot; + aluInitEffectPanning(slotbase, context); - EffectState *state{slot->Effect.State.get()}; + if(auto *props = slotbase->Update.exchange(nullptr, std::memory_order_relaxed)) + AtomicReplaceHead(context->mFreeEffectSlotProps, props); + + EffectState *state{slot.Effect.State.get()}; state->mOutTarget = device->Dry.Buffer; - state->deviceUpdate(device, slot->Buffer); - slot->updateProps(context); + state->deviceUpdate(device, slot.Buffer); + slot.mPropsDirty = true; } - } + }; + std::for_each(context->mEffectSlotList.begin(), context->mEffectSlotList.end(), + reset_slots); + + /* Clear all effect slot props to let them get allocated again. */ + context->mEffectSlotPropClusters.clear(); + context->mFreeEffectSlotProps.store(nullptr, std::memory_order_relaxed); slotlock.unlock(); - const uint num_sends{device->NumAuxSends}; std::unique_lock srclock{context->mSourceLock}; - for(auto &sublist : context->mSourceList) + const uint num_sends{device->NumAuxSends}; + auto reset_sources = [num_sends](SourceSubList &sublist) { uint64_t usemask{~sublist.FreeMask}; while(usemask) { - const int idx{al::countr_zero(usemask)}; - ALsource *source{sublist.Sources + idx}; + const auto idx = static_cast(al::countr_zero(usemask)); + auto &source = (*sublist.Sources)[idx]; usemask &= ~(1_u64 << idx); auto clear_send = [](ALsource::SendData &send) -> void @@ -1701,30 +1789,31 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) send.Slot = nullptr; send.Gain = 1.0f; send.GainHF = 1.0f; - send.HFReference = LOWPASSFREQREF; + send.HFReference = LowPassFreqRef; send.GainLF = 1.0f; - send.LFReference = HIGHPASSFREQREF; + send.LFReference = HighPassFreqRef; }; - auto send_begin = source->Send.begin() + static_cast(num_sends); - std::for_each(send_begin, source->Send.end(), clear_send); + const auto sends = al::span{source.Send}.subspan(num_sends); + std::for_each(sends.begin(), sends.end(), clear_send); - source->mPropsDirty = true; + source.mPropsDirty = true; } - } + }; + std::for_each(context->mSourceList.begin(), context->mSourceList.end(), reset_sources); - auto voicelist = context->getVoicesSpan(); - for(Voice *voice : voicelist) + auto reset_voice = [device,num_sends,context](Voice *voice) { /* Clear extraneous property set sends. */ - std::fill(std::begin(voice->mProps.Send)+num_sends, std::end(voice->mProps.Send), - VoiceProps::SendData{}); + const auto sendparams = al::span{voice->mProps.Send}.subspan(num_sends); + std::fill(sendparams.begin(), sendparams.end(), VoiceProps::SendData{}); std::fill(voice->mSend.begin()+num_sends, voice->mSend.end(), Voice::TargetData{}); - for(auto &chandata : voice->mChans) + auto clear_wetparams = [num_sends](Voice::ChannelData &chandata) { - std::fill(chandata.mWetParams.begin()+num_sends, chandata.mWetParams.end(), - SendParams{}); - } + const auto wetparams = al::span{chandata.mWetParams}.subspan(num_sends); + std::fill(wetparams.begin(), wetparams.end(), SendParams{}); + }; + std::for_each(voice->mChans.begin(), voice->mChans.end(), clear_wetparams); if(VoicePropsItem *props{voice->mUpdate.exchange(nullptr, std::memory_order_relaxed)}) AtomicReplaceHead(context->mFreeVoiceProps, props); @@ -1734,10 +1823,13 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) voice->mPlayState.compare_exchange_strong(vstate, Voice::Stopped, std::memory_order_acquire, std::memory_order_acquire); if(voice->mSourceID.load(std::memory_order_relaxed) == 0u) - continue; + return; voice->prepare(device); - } + }; + const auto voicespan = context->getVoicesSpan(); + std::for_each(voicespan.begin(), voicespan.end(), reset_voice); + /* Clear all voice props to let them get allocated again. */ context->mVoicePropClusters.clear(); context->mFreeVoiceProps.store(nullptr, std::memory_order_relaxed); @@ -1745,16 +1837,20 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) context->mPropsDirty = false; UpdateContextProps(context); + UpdateAllEffectSlotProps(context); UpdateAllSourceProps(context); - } + }; + auto ctxspan = al::span{*device->mContexts.load()}; + std::for_each(ctxspan.begin(), ctxspan.end(), reset_context); mixer_mode.leave(); + device->mDeviceState = DeviceState::Configured; if(!device->Flags.test(DevicePaused)) { try { auto backend = device->Backend.get(); backend->start(); - device->Flags.set(DeviceRunning); + device->mDeviceState = DeviceState::Playing; } catch(al::backend_exception& e) { ERR("%s\n", e.what()); @@ -1773,24 +1869,25 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) * Updates device parameters as above, and also first clears the disconnected * status, if set. */ -bool ResetDeviceParams(ALCdevice *device, const int *attrList) +auto ResetDeviceParams(al::Device *device, const al::span attrList) -> bool { /* If the device was disconnected, reset it since we're opened anew. */ if(!device->Connected.load(std::memory_order_relaxed)) UNLIKELY { /* Make sure disconnection is finished before continuing on. */ - device->waitForMix(); + std::ignore = device->waitForMix(); for(ContextBase *ctxbase : *device->mContexts.load(std::memory_order_acquire)) { - auto *ctx = static_cast(ctxbase); - if(!ctx->mStopVoicesOnDisconnect.load(std::memory_order_acquire)) + auto *ctx = dynamic_cast(ctxbase); + assert(ctx != nullptr); + if(!ctx || !ctx->mStopVoicesOnDisconnect.load(std::memory_order_acquire)) continue; /* Clear any pending voice changes and reallocate voices to get a * clean restart. */ - std::lock_guard __{ctx->mSourceLock}; + std::lock_guard sourcelock{ctx->mSourceLock}; auto *vchg = ctx->mCurrentVoiceChange.load(std::memory_order_acquire); while(auto *next = vchg->mNext.load(std::memory_order_acquire)) vchg = next; @@ -1818,7 +1915,7 @@ bool ResetDeviceParams(ALCdevice *device, const int *attrList) /** Checks if the device handle is valid, and returns a new reference if so. */ DeviceRef VerifyDevice(ALCdevice *device) { - std::lock_guard _{ListLock}; + std::lock_guard listlock{ListLock}; auto iter = std::lower_bound(DeviceList.begin(), DeviceList.end(), device); if(iter != DeviceList.end() && *iter == device) { @@ -1834,7 +1931,7 @@ DeviceRef VerifyDevice(ALCdevice *device) */ ContextRef VerifyContext(ALCcontext *context) { - std::lock_guard _{ListLock}; + std::lock_guard listlock{ListLock}; auto iter = std::lower_bound(ContextList.begin(), ContextList.end(), context); if(iter != ContextList.end() && *iter == context) { @@ -1852,7 +1949,7 @@ FORCE_ALIGN void ALC_APIENTRY alsoft_set_log_callback(LPALSOFTLOGCALLBACK callba } /** Returns a new reference to the currently active context for this thread. */ -ContextRef GetContextRef(void) +ContextRef GetContextRef() noexcept { ALCcontext *context{ALCcontext::getThreadContext()}; if(context) @@ -1871,7 +1968,7 @@ ContextRef GetContextRef(void) return ContextRef{context}; } -void alcSetError(ALCdevice *device, ALCenum errorCode) +void alcSetError(al::Device *device, ALCenum errorCode) { WARN("Error generated on device %p, code 0x%04x\n", voidp{device}, errorCode); if(TrapALCError) @@ -1897,6 +1994,9 @@ void alcSetError(ALCdevice *device, ALCenum errorCode) ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device) noexcept { + if(!gProcessRunning) + return ALC_INVALID_DEVICE; + DeviceRef dev{VerifyDevice(device)}; if(dev) return dev->LastError.exchange(ALC_NO_ERROR); return LastNullDeviceError.exchange(ALC_NO_ERROR); @@ -1912,7 +2012,7 @@ ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context) noexcept return; } - if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY + if(ctx->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 " @@ -1921,7 +2021,7 @@ ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context) noexcept if(SuspendDefers) { - std::lock_guard _{ctx->mPropLock}; + std::lock_guard proplock{ctx->mPropLock}; ctx->deferUpdates(); } } @@ -1935,8 +2035,8 @@ ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context) noexcept return; } - if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY - ctx->debugMessage(DebugSource::API, DebugType::Portability, 0, DebugSeverity::Medium, + if(ctx->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY + ctx->debugMessage(DebugSource::API, DebugType::Portability, 1, 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 " @@ -1944,137 +2044,108 @@ ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context) noexcept if(SuspendDefers) { - std::lock_guard _{ctx->mPropLock}; + std::lock_guard proplock{ctx->mPropLock}; ctx->processUpdates(); } } -ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum param) noexcept +ALC_API auto ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum param) noexcept -> const ALCchar* { - const ALCchar *value{nullptr}; - switch(param) { - case ALC_NO_ERROR: - value = alcNoError; - break; - - case ALC_INVALID_ENUM: - value = alcErrInvalidEnum; - break; - - case ALC_INVALID_VALUE: - value = alcErrInvalidValue; - break; - - case ALC_INVALID_DEVICE: - value = alcErrInvalidDevice; - break; - - case ALC_INVALID_CONTEXT: - value = alcErrInvalidContext; - break; - - case ALC_OUT_OF_MEMORY: - value = alcErrOutOfMemory; - break; + case ALC_NO_ERROR: return GetNoErrorString(); + case ALC_INVALID_ENUM: return GetInvalidEnumString(); + case ALC_INVALID_VALUE: return GetInvalidValueString(); + case ALC_INVALID_DEVICE: return GetInvalidDeviceString(); + case ALC_INVALID_CONTEXT: return GetInvalidContextString(); + case ALC_OUT_OF_MEMORY: return GetOutOfMemoryString(); case ALC_DEVICE_SPECIFIER: - value = alcDefaultName; - break; + return GetDefaultName(); case ALC_ALL_DEVICES_SPECIFIER: if(DeviceRef dev{VerifyDevice(Device)}) { if(dev->Type == DeviceType::Capture) - alcSetError(dev.get(), ALC_INVALID_ENUM); - else if(dev->Type == DeviceType::Loopback) - value = alcDefaultName; - else { - std::lock_guard _{dev->StateLock}; - value = dev->DeviceName.c_str(); + alcSetError(dev.get(), ALC_INVALID_ENUM); + return nullptr; } + if(dev->Type == DeviceType::Loopback) + return GetDefaultName(); + + auto statelock = std::lock_guard{dev->StateLock}; + return dev->mDeviceName.c_str(); } - else - { - ProbeAllDevicesList(); - value = alcAllDevicesList.c_str(); - } - break; + ProbeAllDevicesList(); + return alcAllDevicesList.c_str(); case ALC_CAPTURE_DEVICE_SPECIFIER: if(DeviceRef dev{VerifyDevice(Device)}) { if(dev->Type != DeviceType::Capture) - alcSetError(dev.get(), ALC_INVALID_ENUM); - else { - std::lock_guard _{dev->StateLock}; - value = dev->DeviceName.c_str(); + alcSetError(dev.get(), ALC_INVALID_ENUM); + return nullptr; } + + auto statelock = std::lock_guard{dev->StateLock}; + return dev->mDeviceName.c_str(); } - else - { - ProbeCaptureDeviceList(); - value = alcCaptureDeviceList.c_str(); - } - break; + ProbeCaptureDeviceList(); + return alcCaptureDeviceList.c_str(); /* Default devices are always first in the list */ case ALC_DEFAULT_DEVICE_SPECIFIER: - value = alcDefaultName; - break; + return GetDefaultName(); case ALC_DEFAULT_ALL_DEVICES_SPECIFIER: if(alcAllDevicesList.empty()) ProbeAllDevicesList(); /* Copy first entry as default. */ - alcDefaultAllDevicesSpecifier = alcAllDevicesList.c_str(); - value = alcDefaultAllDevicesSpecifier.c_str(); - break; + if(alcAllDevicesArray.empty()) + return GetDefaultName(); + + alcDefaultAllDevicesSpecifier = alcAllDevicesArray.front(); + return alcDefaultAllDevicesSpecifier.c_str(); case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER: if(alcCaptureDeviceList.empty()) ProbeCaptureDeviceList(); /* Copy first entry as default. */ - alcCaptureDefaultDeviceSpecifier = alcCaptureDeviceList.c_str(); - value = alcCaptureDefaultDeviceSpecifier.c_str(); - break; + if(alcCaptureDeviceArray.empty()) + return GetDefaultName(); + + alcCaptureDefaultDeviceSpecifier = alcCaptureDeviceArray.front(); + return alcCaptureDefaultDeviceSpecifier.c_str(); case ALC_EXTENSIONS: if(VerifyDevice(Device)) - value = alcExtensionList; - else - value = alcNoDeviceExtList; - break; + return GetExtensionList(); + return GetNoDeviceExtList(); case ALC_HRTF_SPECIFIER_SOFT: if(DeviceRef dev{VerifyDevice(Device)}) { - std::lock_guard _{dev->StateLock}; - value = (dev->mHrtf ? dev->mHrtfName.c_str() : ""); + std::lock_guard statelock{dev->StateLock}; + return dev->mHrtf ? dev->mHrtfName.c_str() : ""; } - else - alcSetError(nullptr, ALC_INVALID_DEVICE); - break; + alcSetError(nullptr, ALC_INVALID_DEVICE); + return nullptr; default: alcSetError(VerifyDevice(Device).get(), ALC_INVALID_ENUM); - break; } - return value; + return nullptr; } - -static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span values) +namespace { +auto GetIntegerv(al::Device *device, ALCenum param, const al::span values) -> size_t { - size_t i; - if(values.empty()) { alcSetError(device, ALC_INVALID_VALUE); @@ -2099,7 +2170,7 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span values[0] = alcEFXMinorVersion; return 1; case ALC_MAX_AUXILIARY_SENDS: - values[0] = MAX_SENDS; + values[0] = MaxSendCount; return 1; case ALC_ATTRIBUTES_SIZE: @@ -2125,7 +2196,7 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span return 0; } - std::lock_guard _{device->StateLock}; + std::lock_guard statelock{device->StateLock}; if(device->Type == DeviceType::Capture) { static constexpr int MaxCaptureAttributes{9}; @@ -2135,11 +2206,9 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span values[0] = MaxCaptureAttributes; return 1; case ALC_ALL_ATTRIBUTES: - i = 0; - if(values.size() < MaxCaptureAttributes) - alcSetError(device, ALC_INVALID_VALUE); - else + if(values.size() >= MaxCaptureAttributes) { + size_t i{0}; values[i++] = ALC_MAJOR_VERSION; values[i++] = alcMajorVersion; values[i++] = ALC_MINOR_VERSION; @@ -2150,8 +2219,10 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span values[i++] = device->Connected.load(std::memory_order_relaxed); values[i++] = 0; assert(i == MaxCaptureAttributes); + return i; } - return i; + alcSetError(device, ALC_INVALID_VALUE); + return 0; case ALC_MAJOR_VERSION: values[0] = alcMajorVersion; @@ -2175,24 +2246,22 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span } /* render device */ - auto NumAttrsForDevice = [](ALCdevice *aldev) noexcept + auto NumAttrsForDevice = [device]() noexcept -> uint8_t { - if(aldev->Type == DeviceType::Loopback && aldev->FmtChans == DevFmtAmbi3D) + if(device->Type == DeviceType::Loopback && device->FmtChans == DevFmtAmbi3D) return 37; return 31; }; switch(param) { case ALC_ATTRIBUTES_SIZE: - values[0] = NumAttrsForDevice(device); + values[0] = NumAttrsForDevice(); return 1; case ALC_ALL_ATTRIBUTES: - i = 0; - if(values.size() < static_cast(NumAttrsForDevice(device))) - alcSetError(device, ALC_INVALID_VALUE); - else + if(values.size() >= NumAttrsForDevice()) { + size_t i{0}; values[i++] = ALC_MAJOR_VERSION; values[i++] = alcMajorVersion; values[i++] = ALC_MINOR_VERSION; @@ -2258,8 +2327,11 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span values[i++] = static_cast(device->getOutputMode1()); values[i++] = 0; + assert(i == NumAttrsForDevice()); + return i; } - return i; + alcSetError(device, ALC_INVALID_VALUE); + return 0; case ALC_MAJOR_VERSION: values[0] = alcMajorVersion; @@ -2370,8 +2442,8 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span case ALC_NUM_HRTF_SPECIFIERS_SOFT: device->enumerateHrtfs(); - values[0] = static_cast(minz(device->mHrtfList.size(), - std::numeric_limits::max())); + values[0] = static_cast(std::min(device->mHrtfList.size(), + size_t{std::numeric_limits::max()})); return 1; case ALC_OUTPUT_LIMITER_SOFT: @@ -2391,6 +2463,7 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span } return 0; } +} // namespace ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) noexcept { @@ -2409,113 +2482,118 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, alcSetError(dev.get(), ALC_INVALID_VALUE); return; } + const auto valuespan = al::span{values, static_cast(size)}; if(!dev || dev->Type == DeviceType::Capture) { - auto ivals = std::vector(static_cast(size)); + auto ivals = std::vector(valuespan.size()); if(size_t got{GetIntegerv(dev.get(), pname, ivals)}) - std::copy_n(ivals.begin(), got, values); + std::copy_n(ivals.cbegin(), got, valuespan.begin()); return; } /* render device */ - auto NumAttrsForDevice = [](ALCdevice *aldev) noexcept + auto NumAttrsForDevice = [](al::Device *aldev) noexcept -> size_t { if(aldev->Type == DeviceType::Loopback && aldev->FmtChans == DevFmtAmbi3D) return 41; return 35; }; - std::lock_guard _{dev->StateLock}; + std::lock_guard statelock{dev->StateLock}; switch(pname) { case ALC_ATTRIBUTES_SIZE: - *values = NumAttrsForDevice(dev.get()); + valuespan[0] = static_cast(NumAttrsForDevice(dev.get())); break; case ALC_ALL_ATTRIBUTES: - if(size < NumAttrsForDevice(dev.get())) + if(valuespan.size() < NumAttrsForDevice(dev.get())) alcSetError(dev.get(), ALC_INVALID_VALUE); else { size_t i{0}; - values[i++] = ALC_FREQUENCY; - values[i++] = dev->Frequency; + valuespan[i++] = ALC_FREQUENCY; + valuespan[i++] = dev->Frequency; if(dev->Type != DeviceType::Loopback) { - values[i++] = ALC_REFRESH; - values[i++] = dev->Frequency / dev->UpdateSize; + valuespan[i++] = ALC_REFRESH; + valuespan[i++] = dev->Frequency / dev->UpdateSize; - values[i++] = ALC_SYNC; - values[i++] = ALC_FALSE; + valuespan[i++] = ALC_SYNC; + valuespan[i++] = ALC_FALSE; } else { - values[i++] = ALC_FORMAT_CHANNELS_SOFT; - values[i++] = EnumFromDevFmt(dev->FmtChans); + valuespan[i++] = ALC_FORMAT_CHANNELS_SOFT; + valuespan[i++] = EnumFromDevFmt(dev->FmtChans); - values[i++] = ALC_FORMAT_TYPE_SOFT; - values[i++] = EnumFromDevFmt(dev->FmtType); + valuespan[i++] = ALC_FORMAT_TYPE_SOFT; + valuespan[i++] = EnumFromDevFmt(dev->FmtType); if(dev->FmtChans == DevFmtAmbi3D) { - values[i++] = ALC_AMBISONIC_LAYOUT_SOFT; - values[i++] = EnumFromDevAmbi(dev->mAmbiLayout); + valuespan[i++] = ALC_AMBISONIC_LAYOUT_SOFT; + valuespan[i++] = EnumFromDevAmbi(dev->mAmbiLayout); - values[i++] = ALC_AMBISONIC_SCALING_SOFT; - values[i++] = EnumFromDevAmbi(dev->mAmbiScale); + valuespan[i++] = ALC_AMBISONIC_SCALING_SOFT; + valuespan[i++] = EnumFromDevAmbi(dev->mAmbiScale); - values[i++] = ALC_AMBISONIC_ORDER_SOFT; - values[i++] = dev->mAmbiOrder; + valuespan[i++] = ALC_AMBISONIC_ORDER_SOFT; + valuespan[i++] = dev->mAmbiOrder; } } - values[i++] = ALC_MONO_SOURCES; - values[i++] = dev->NumMonoSources; + valuespan[i++] = ALC_MONO_SOURCES; + valuespan[i++] = dev->NumMonoSources; - values[i++] = ALC_STEREO_SOURCES; - values[i++] = dev->NumStereoSources; + valuespan[i++] = ALC_STEREO_SOURCES; + valuespan[i++] = dev->NumStereoSources; - values[i++] = ALC_MAX_AUXILIARY_SENDS; - values[i++] = dev->NumAuxSends; + valuespan[i++] = ALC_MAX_AUXILIARY_SENDS; + valuespan[i++] = dev->NumAuxSends; - values[i++] = ALC_HRTF_SOFT; - values[i++] = (dev->mHrtf ? ALC_TRUE : ALC_FALSE); + valuespan[i++] = ALC_HRTF_SOFT; + valuespan[i++] = (dev->mHrtf ? ALC_TRUE : ALC_FALSE); - values[i++] = ALC_HRTF_STATUS_SOFT; - values[i++] = dev->mHrtfStatus; + valuespan[i++] = ALC_HRTF_STATUS_SOFT; + valuespan[i++] = dev->mHrtfStatus; - values[i++] = ALC_OUTPUT_LIMITER_SOFT; - values[i++] = dev->Limiter ? ALC_TRUE : ALC_FALSE; + valuespan[i++] = ALC_OUTPUT_LIMITER_SOFT; + valuespan[i++] = dev->Limiter ? ALC_TRUE : ALC_FALSE; ClockLatency clock{GetClockLatency(dev.get(), dev->Backend.get())}; - values[i++] = ALC_DEVICE_CLOCK_SOFT; - values[i++] = clock.ClockTime.count(); + valuespan[i++] = ALC_DEVICE_CLOCK_SOFT; + valuespan[i++] = clock.ClockTime.count(); - values[i++] = ALC_DEVICE_LATENCY_SOFT; - values[i++] = clock.Latency.count(); + valuespan[i++] = ALC_DEVICE_LATENCY_SOFT; + valuespan[i++] = clock.Latency.count(); - values[i++] = ALC_OUTPUT_MODE_SOFT; - values[i++] = static_cast(device->getOutputMode1()); + valuespan[i++] = ALC_OUTPUT_MODE_SOFT; + valuespan[i++] = al::to_underlying(dev->getOutputMode1()); - values[i++] = 0; + valuespan[i++] = 0; } break; case ALC_DEVICE_CLOCK_SOFT: { uint samplecount, refcount; - nanoseconds basecount; + seconds clocksec; + nanoseconds clocknsec; do { refcount = dev->waitForMix(); - basecount = dev->ClockBase; - samplecount = dev->SamplesDone; - } while(refcount != ReadRef(dev->MixCount)); - basecount += nanoseconds{seconds{samplecount}} / dev->Frequency; - *values = basecount.count(); + samplecount = dev->mSamplesDone.load(std::memory_order_relaxed); + clocksec = dev->mClockBaseSec.load(std::memory_order_relaxed); + clocknsec = dev->mClockBaseNSec.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + } while(refcount != dev->mMixCount.load(std::memory_order_relaxed)); + + valuespan[0] = nanoseconds{clocksec + nanoseconds{clocknsec} + + nanoseconds{seconds{samplecount}}/dev->Frequency}.count(); } break; case ALC_DEVICE_LATENCY_SOFT: - *values = GetClockLatency(dev.get(), dev->Backend.get()).Latency.count(); + valuespan[0] = GetClockLatency(dev.get(), dev->Backend.get()).Latency.count(); break; case ALC_DEVICE_CLOCK_LATENCY_SOFT: @@ -2524,15 +2602,15 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, else { ClockLatency clock{GetClockLatency(dev.get(), dev->Backend.get())}; - values[0] = clock.ClockTime.count(); - values[1] = clock.Latency.count(); + valuespan[0] = clock.ClockTime.count(); + valuespan[1] = clock.Latency.count(); } break; default: - auto ivals = std::vector(static_cast(size)); + auto ivals = std::vector(valuespan.size()); if(size_t got{GetIntegerv(dev.get(), pname, ivals)}) - std::copy_n(ivals.begin(), got, values); + std::copy_n(ivals.cbegin(), got, valuespan.begin()); break; } } @@ -2542,23 +2620,22 @@ ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const A { DeviceRef dev{VerifyDevice(device)}; if(!extName) - alcSetError(dev.get(), ALC_INVALID_VALUE); - else { - size_t len = strlen(extName); - const char *ptr = (dev ? alcExtensionList : alcNoDeviceExtList); - while(ptr && *ptr) - { - if(al::strncasecmp(ptr, extName, len) == 0 && (ptr[len] == '\0' || isspace(ptr[len]))) - return ALC_TRUE; + alcSetError(dev.get(), ALC_INVALID_VALUE); + return ALC_FALSE; + } - if((ptr=strchr(ptr, ' ')) != nullptr) - { - do { - ++ptr; - } while(isspace(*ptr)); - } - } + const std::string_view tofind{extName}; + const auto extlist = dev ? std::string_view{GetExtensionList()} + : std::string_view{GetNoDeviceExtList()}; + auto matchpos = extlist.find(tofind); + while(matchpos != std::string_view::npos) + { + const auto endpos = matchpos + tofind.size(); + if((matchpos == 0 || std::isspace(extlist[matchpos-1])) + && (endpos == extlist.size() || std::isspace(extlist[endpos]))) + return ALC_TRUE; + matchpos = extlist.find(tofind, matchpos+1); } return ALC_FALSE; } @@ -2576,7 +2653,7 @@ ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar return nullptr; } -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(eax_g_is_enabled) { for(const auto &func : eaxFunctions) @@ -2604,7 +2681,7 @@ ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *e return 0; } -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(eax_g_is_enabled) { for(const auto &enm : eaxEnumerations) @@ -2643,7 +2720,8 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin dev->LastError.store(ALC_NO_ERROR); - ALCenum err{UpdateDeviceParams(dev.get(), attrList)}; + const auto attrSpan = SpanFromAttributeList(attrList); + ALCenum err{UpdateDeviceParams(dev.get(), attrSpan)}; if(err != ALC_NO_ERROR) { alcSetError(dev.get(), err); @@ -2651,28 +2729,16 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin } ContextFlagBitset ctxflags{0}; - if(attrList) + for(size_t i{0};i < attrSpan.size();i+=2) { - for(size_t i{0};attrList[i];i+=2) + if(attrSpan[i] == ALC_CONTEXT_FLAGS_EXT) { - if(attrList[i] == ALC_CONTEXT_FLAGS_EXT) - { - ctxflags = static_cast(attrList[i+1]); - break; - } + ctxflags = static_cast(attrSpan[i+1]); + break; } } - ContextRef context{[](auto&& ...args) -> ContextRef - { - try { - return ContextRef{new ALCcontext{std::forward(args)...}}; - } - catch(std::exception& e) { - ERR("Failed to create ALCcontext: %s\n", e.what()); - return ContextRef{}; - } - }(dev, ctxflags)}; + auto context = ContextRef{new(std::nothrow) ALCcontext{dev, ctxflags}}; if(!context) { alcSetError(dev.get(), ALC_OUT_OF_MEMORY); @@ -2680,14 +2746,14 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin } context->init(); - if(auto volopt = dev->configValue(nullptr, "volume-adjust")) + if(auto volopt = dev->configValue({}, "volume-adjust")) { const float valf{*volopt}; if(!std::isfinite(valf)) ERR("volume-adjust must be finite: %f\n", valf); else { - const float db{clampf(valf, -24.0f, 24.0f)}; + const float db{std::clamp(valf, -24.0f, 24.0f)}; if(db != valf) WARN("volume-adjust clamped: %f, range: +/-%f\n", valf, 24.0f); context->mGainBoost = std::pow(10.0f, db/20.0f); @@ -2701,9 +2767,8 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin /* Allocate a new context array, which holds 1 more than the current/ * old array. */ - auto *oldarray = device->mContexts.load(); - const size_t newcount{oldarray->size()+1}; - std::unique_ptr newarray{ContextArray::Create(newcount)}; + auto *oldarray = dev->mContexts.load(); + auto newarray = ContextArray::Create(oldarray->size() + 1); /* Copy the current/old context handles to the new array, appending the * new context. @@ -2714,19 +2779,16 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin /* Store the new context array in the device. Wait for any current mix * to finish before deleting the old array. */ - dev->mContexts.store(newarray.release()); - if(oldarray != &DeviceBase::sEmptyContextArray) - { - dev->waitForMix(); - delete oldarray; - } + auto prevarray = dev->mContexts.exchange(std::move(newarray)); + std::ignore = dev->waitForMix(); } statelock.unlock(); { - std::lock_guard _{ListLock}; + listlock.lock(); auto iter = std::lower_bound(ContextList.cbegin(), ContextList.cend(), context.get()); ContextList.emplace(iter, context.get()); + listlock.unlock(); } if(ALeffectslot *slot{context->mDefaultSlot.get()}) @@ -2745,6 +2807,9 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) noexcept { + if(!gProcessRunning) + return; + std::unique_lock listlock{ListLock}; auto iter = std::lower_bound(ContextList.begin(), ContextList.end(), context); if(iter == ContextList.end() || *iter != context) @@ -2760,18 +2825,13 @@ ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) noexcept ContextRef ctx{*iter}; ContextList.erase(iter); - ALCdevice *Device{ctx->mALDevice.get()}; - - std::lock_guard _{Device->StateLock}; - if(!ctx->deinit() && Device->Flags.test(DeviceRunning)) - { - Device->Backend->stop(); - Device->Flags.reset(DeviceRunning); - } + auto *Device = ctx->mALDevice.get(); + std::lock_guard statelock{Device->StateLock}; + ctx->deinit(); } -ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) noexcept +ALC_API auto ALC_APIENTRY alcGetCurrentContext() noexcept -> ALCcontext* { ALCcontext *Context{ALCcontext::getThreadContext()}; if(!Context) Context = ALCcontext::sGlobalContext.load(); @@ -2779,7 +2839,7 @@ ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) noexcept } /** Returns the currently active thread-local context. */ -ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) noexcept +ALC_API auto ALC_APIENTRY alcGetThreadContext() noexcept -> ALCcontext* { return ALCcontext::getThreadContext(); } ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) noexcept @@ -2804,7 +2864,7 @@ ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) noexc * the current context as its refcount is decremented. */ } - ContextRef{ALCcontext::sGlobalContext.exchange(ctx.release())}; + ctx = ContextRef{ALCcontext::sGlobalContext.exchange(ctx.release())}; ALCcontext::sGlobalContextLock.store(false, std::memory_order_release); /* Take ownership of the thread-local context reference (if any), clearing @@ -2861,45 +2921,59 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) noexcep return nullptr; } - if(deviceName) + std::string_view devname{deviceName ? deviceName : ""}; + if(!devname.empty()) { - TRACE("Opening playback device \"%s\"\n", deviceName); - if(!deviceName[0] || al::strcasecmp(deviceName, alcDefaultName) == 0 + TRACE("Opening playback device \"%.*s\"\n", al::sizei(devname), devname.data()); + if(al::case_compare(devname, GetDefaultName()) == 0 #ifdef _WIN32 /* Some old Windows apps hardcode these expecting OpenAL to use a * specific audio API, even when they're not enumerated. Creative's * router effectively ignores them too. */ - || al::strcasecmp(deviceName, "DirectSound3D") == 0 - || al::strcasecmp(deviceName, "DirectSound") == 0 - || al::strcasecmp(deviceName, "MMSYSTEM") == 0 + || al::case_compare(devname, "DirectSound3D"sv) == 0 + || al::case_compare(devname, "DirectSound"sv) == 0 + || al::case_compare(devname, "MMSYSTEM"sv) == 0 #endif /* Some old Linux apps hardcode configuration strings that were * supported by the OpenAL SI. We can't really do anything useful * with them, so just ignore. */ - || (deviceName[0] == '\'' && deviceName[1] == '(') - || al::strcasecmp(deviceName, "openal-soft") == 0) - deviceName = nullptr; + || al::starts_with(devname, "'("sv) + || al::case_compare(devname, "openal-soft"sv) == 0) + devname = {}; + else + { + const auto prefix = GetDevicePrefix(); + if(!prefix.empty() && devname.size() > prefix.size() + && al::starts_with(devname, prefix)) + devname = devname.substr(prefix.size()); + } } else TRACE("Opening default playback device\n"); const uint DefaultSends{ -#ifdef ALSOFT_EAX +#if ALSOFT_EAX eax_g_is_enabled ? uint{EAX_MAX_FXSLOTS} : #endif // ALSOFT_EAX - DEFAULT_SENDS + uint{DefaultSendCount} }; - DeviceRef device{new ALCdevice{DeviceType::Playback}}; + auto device = DeviceRef{new(std::nothrow) al::Device{DeviceType::Playback}}; + if(!device) + { + WARN("Failed to create playback device handle\n"); + alcSetError(nullptr, ALC_OUT_OF_MEMORY); + return nullptr; + } /* Set output format */ device->FmtChans = DevFmtChannelsDefault; device->FmtType = DevFmtTypeDefault; - device->Frequency = DEFAULT_OUTPUT_RATE; - device->UpdateSize = DEFAULT_UPDATE_SIZE; - device->BufferSize = DEFAULT_UPDATE_SIZE * DEFAULT_NUM_UPDATES; + device->Frequency = DefaultOutputRate; + device->UpdateSize = DefaultUpdateSize; + device->BufferSize = DefaultUpdateSize * DefaultNumUpdates; device->SourcesMax = 256; device->NumStereoSources = 1; @@ -2908,26 +2982,10 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) noexcep 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::max()) - throw al::backend_exception{al::backend_error::NoDevice, - "Device name too long (%zu >= %d)", devname.length(), - std::numeric_limits::max()}; - auto backend = PlaybackFactory->createBackend(device.get(), BackendType::Playback); - std::lock_guard _{ListLock}; + std::lock_guard listlock{ListLock}; backend->open(devname); + device->mDeviceName = std::string{GetDevicePrefix()}+backend->mDeviceName; device->Backend = std::move(backend); } catch(al::backend_exception &e) { @@ -2937,18 +2995,42 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) noexcep return nullptr; } + auto checkopt = [&device](const char *envname, const std::string_view optname) { - std::lock_guard _{ListLock}; + if(auto optval = al::getenv(envname)) return optval; + return device->configValue("game_compat", optname); + }; + if(auto overrideopt = checkopt("__ALSOFT_VENDOR_OVERRIDE", "vendor-override"sv)) + { + device->mVendorOverride = std::move(*overrideopt); + TRACE("Overriding vendor string: \"%s\"\n", device->mVendorOverride.c_str()); + } + if(auto overrideopt = checkopt("__ALSOFT_VERSION_OVERRIDE", "version-override"sv)) + { + device->mVersionOverride = std::move(*overrideopt); + TRACE("Overriding version string: \"%s\"\n", device->mVersionOverride.c_str()); + } + if(auto overrideopt = checkopt("__ALSOFT_RENDERER_OVERRIDE", "renderer-override"sv)) + { + device->mRendererOverride = std::move(*overrideopt); + TRACE("Overriding renderer string: \"%s\"\n", device->mRendererOverride.c_str()); + } + + { + std::lock_guard listlock{ListLock}; auto iter = std::lower_bound(DeviceList.cbegin(), DeviceList.cend(), device.get()); DeviceList.emplace(iter, device.get()); } - TRACE("Created device %p, \"%s\"\n", voidp{device.get()}, device->DeviceName.c_str()); + TRACE("Created device %p, \"%s\"\n", voidp{device.get()}, device->mDeviceName.c_str()); return device.release(); } ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) noexcept { + if(!gProcessRunning) + return ALC_FALSE; + std::unique_lock listlock{ListLock}; auto iter = std::lower_bound(DeviceList.begin(), DeviceList.end(), device); if(iter == DeviceList.end() || *iter != device) @@ -2975,7 +3057,7 @@ ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) noexcept auto ctxiter = std::lower_bound(ContextList.begin(), ContextList.end(), ctx); if(ctxiter != ContextList.end() && *ctxiter == ctx) { - orphanctxs.emplace_back(ContextRef{*ctxiter}); + orphanctxs.emplace_back(*ctxiter); ContextList.erase(ctxiter); } } @@ -2988,9 +3070,11 @@ ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) noexcept } orphanctxs.clear(); - if(dev->Flags.test(DeviceRunning)) + if(dev->mDeviceState == DeviceState::Playing) + { dev->Backend->stop(); - dev->Flags.reset(DeviceRunning); + dev->mDeviceState = DeviceState::Configured; + } return ALC_TRUE; } @@ -3015,17 +3099,31 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, return nullptr; } - if(deviceName) + std::string_view devname{deviceName ? deviceName : ""}; + if(!devname.empty()) { - TRACE("Opening capture device \"%s\"\n", deviceName); - if(!deviceName[0] || al::strcasecmp(deviceName, alcDefaultName) == 0 - || al::strcasecmp(deviceName, "openal-soft") == 0) - deviceName = nullptr; + TRACE("Opening capture device \"%.*s\"\n", al::sizei(devname), devname.data()); + if(al::case_compare(devname, GetDefaultName()) == 0 + || al::case_compare(devname, "openal-soft"sv) == 0) + devname = {}; + else + { + const auto prefix = GetDevicePrefix(); + if(!prefix.empty() && devname.size() > prefix.size() + && al::starts_with(devname, prefix)) + devname = devname.substr(prefix.size()); + } } else TRACE("Opening default capture device\n"); - DeviceRef device{new ALCdevice{DeviceType::Capture}}; + auto device = DeviceRef{new(std::nothrow) al::Device{DeviceType::Capture}}; + if(!device) + { + WARN("Failed to create capture device handle\n"); + alcSetError(nullptr, ALC_OUT_OF_MEMORY); + return nullptr; + } auto decompfmt = DecomposeDevFormat(format); if(!decompfmt) @@ -3049,15 +3147,10 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, device->BufferSize); try { - const std::string_view devname{deviceName ? deviceName : ""}; - if(devname.length() >= std::numeric_limits::max()) - throw al::backend_exception{al::backend_error::NoDevice, - "Device name too long (%zu >= %d)", devname.length(), - std::numeric_limits::max()}; - auto backend = CaptureFactory->createBackend(device.get(), BackendType::Capture); - std::lock_guard _{ListLock}; + std::lock_guard listlock{ListLock}; backend->open(devname); + device->mDeviceName = std::string{GetDevicePrefix()}+backend->mDeviceName; device->Backend = std::move(backend); } catch(al::backend_exception &e) { @@ -3068,17 +3161,21 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, } { - std::lock_guard _{ListLock}; + std::lock_guard listlock{ListLock}; auto iter = std::lower_bound(DeviceList.cbegin(), DeviceList.cend(), device.get()); DeviceList.emplace(iter, device.get()); } + device->mDeviceState = DeviceState::Configured; - TRACE("Created capture device %p, \"%s\"\n", voidp{device.get()}, device->DeviceName.c_str()); + TRACE("Created capture device %p, \"%s\"\n", voidp{device.get()}, device->mDeviceName.c_str()); return device.release(); } ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) noexcept { + if(!gProcessRunning) + return ALC_FALSE; + std::unique_lock listlock{ListLock}; auto iter = std::lower_bound(DeviceList.begin(), DeviceList.end(), device); if(iter == DeviceList.end() || *iter != device) @@ -3096,10 +3193,12 @@ ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) noexcep DeviceList.erase(iter); listlock.unlock(); - std::lock_guard _{dev->StateLock}; - if(dev->Flags.test(DeviceRunning)) + std::lock_guard statelock{dev->StateLock}; + if(dev->mDeviceState == DeviceState::Playing) + { dev->Backend->stop(); - dev->Flags.reset(DeviceRunning); + dev->mDeviceState = DeviceState::Configured; + } return ALC_TRUE; } @@ -3113,15 +3212,16 @@ ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) noexcept return; } - std::lock_guard _{dev->StateLock}; - if(!dev->Connected.load(std::memory_order_acquire)) + std::lock_guard statelock{dev->StateLock}; + if(!dev->Connected.load(std::memory_order_acquire) + || dev->mDeviceState < DeviceState::Configured) alcSetError(dev.get(), ALC_INVALID_DEVICE); - else if(!dev->Flags.test(DeviceRunning)) + else if(dev->mDeviceState != DeviceState::Playing) { try { auto backend = dev->Backend.get(); backend->start(); - dev->Flags.set(DeviceRunning); + dev->mDeviceState = DeviceState::Playing; } catch(al::backend_exception& e) { ERR("%s\n", e.what()); @@ -3138,10 +3238,12 @@ ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) noexcept alcSetError(dev.get(), ALC_INVALID_DEVICE); else { - std::lock_guard _{dev->StateLock}; - if(dev->Flags.test(DeviceRunning)) + std::lock_guard statelock{dev->StateLock}; + if(dev->mDeviceState == DeviceState::Playing) + { dev->Backend->stop(); - dev->Flags.reset(DeviceRunning); + dev->mDeviceState = DeviceState::Configured; + } } } @@ -3162,7 +3264,7 @@ ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, if(samples < 1) return; - std::lock_guard _{dev->StateLock}; + std::lock_guard statelock{dev->StateLock}; BackendBase *backend{dev->Backend.get()}; const auto usamples = static_cast(samples); @@ -3186,20 +3288,26 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN InitConfig(); /* Make sure the device name, if specified, is us. */ - if(deviceName && strcmp(deviceName, alcDefaultName) != 0) + if(deviceName && strcmp(deviceName, GetDefaultName()) != 0) { alcSetError(nullptr, ALC_INVALID_VALUE); return nullptr; } const uint DefaultSends{ -#ifdef ALSOFT_EAX +#if ALSOFT_EAX eax_g_is_enabled ? uint{EAX_MAX_FXSLOTS} : #endif // ALSOFT_EAX - DEFAULT_SENDS + uint{DefaultSendCount} }; - DeviceRef device{new ALCdevice{DeviceType::Loopback}}; + auto device = DeviceRef{new(std::nothrow) al::Device{DeviceType::Loopback}}; + if(!device) + { + WARN("Failed to create loopback device handle\n"); + alcSetError(nullptr, ALC_OUT_OF_MEMORY); + return nullptr; + } device->SourcesMax = 256; device->AuxiliaryEffectSlotMax = 64; @@ -3209,7 +3317,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN device->BufferSize = 0; device->UpdateSize = 0; - device->Frequency = DEFAULT_OUTPUT_RATE; + device->Frequency = DefaultOutputRate; device->FmtChans = DevFmtChannelsDefault; device->FmtType = DevFmtTypeDefault; @@ -3220,6 +3328,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN auto backend = LoopbackBackendFactory::getFactory().createBackend(device.get(), BackendType::Playback); backend->open("Loopback"); + device->mDeviceName = std::string{GetDevicePrefix()}+backend->mDeviceName; device->Backend = std::move(backend); } catch(al::backend_exception &e) { @@ -3230,7 +3339,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN } { - std::lock_guard _{ListLock}; + std::lock_guard listlock{ListLock}; auto iter = std::lower_bound(DeviceList.cbegin(), DeviceList.cend(), device.get()); DeviceList.emplace(iter, device.get()); } @@ -3252,7 +3361,7 @@ ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device else { if(DevFmtTypeFromEnum(type).has_value() && DevFmtChannelsFromEnum(channels).has_value() - && freq >= MIN_OUTPUT_RATE && freq <= MAX_OUTPUT_RATE) + && freq >= int{MinOutputRate} && freq <= int{MaxOutputRate}) return ALC_TRUE; } @@ -3271,12 +3380,13 @@ ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device #endif ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) noexcept { - if(!device || device->Type != DeviceType::Loopback) UNLIKELY - alcSetError(device, ALC_INVALID_DEVICE); + auto aldev = dynamic_cast(device); + if(!aldev || aldev->Type != DeviceType::Loopback) UNLIKELY + alcSetError(aldev, ALC_INVALID_DEVICE); else if(samples < 0 || (samples > 0 && buffer == nullptr)) UNLIKELY - alcSetError(device, ALC_INVALID_VALUE); + alcSetError(aldev, ALC_INVALID_VALUE); else - device->renderSamples(buffer, static_cast(samples), device->channelsFromFmt()); + aldev->renderSamples(buffer, static_cast(samples), aldev->channelsFromFmt()); } @@ -3292,10 +3402,12 @@ ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device) noexcept alcSetError(dev.get(), ALC_INVALID_DEVICE); else { - std::lock_guard _{dev->StateLock}; - if(dev->Flags.test(DeviceRunning)) + std::lock_guard statelock{dev->StateLock}; + if(dev->mDeviceState == DeviceState::Playing) + { dev->Backend->stop(); - dev->Flags.reset(DeviceRunning); + dev->mDeviceState = DeviceState::Configured; + } dev->Flags.set(DevicePaused); } } @@ -3310,9 +3422,21 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) noexcept return; } - std::lock_guard _{dev->StateLock}; + std::lock_guard statelock{dev->StateLock}; if(!dev->Flags.test(DevicePaused)) return; + if(dev->mDeviceState < DeviceState::Configured) + { + WARN("Cannot resume unconfigured device\n"); + alcSetError(dev.get(), ALC_INVALID_DEVICE); + return; + } + if(!dev->Connected.load()) + { + WARN("Cannot resume a disconnected device\n"); + alcSetError(dev.get(), ALC_INVALID_DEVICE); + return; + } dev->Flags.reset(DevicePaused); if(dev->mContexts.load()->empty()) return; @@ -3320,7 +3444,7 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) noexcept try { auto backend = dev->Backend.get(); backend->start(); - dev->Flags.set(DeviceRunning); + dev->mDeviceState = DeviceState::Playing; } catch(al::backend_exception& e) { ERR("%s\n", e.what()); @@ -3329,8 +3453,8 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) noexcept return; } TRACE("Post-resume: %s, %s, %uhz, %u / %u buffer\n", - DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType), - device->Frequency, device->UpdateSize, device->BufferSize); + DevFmtChannelsString(dev->FmtChans), DevFmtTypeString(dev->FmtType), + dev->Frequency, dev->UpdateSize, dev->BufferSize); } @@ -3371,17 +3495,19 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi alcSetError(dev.get(), ALC_INVALID_DEVICE); return ALC_FALSE; } - std::lock_guard _{dev->StateLock}; + std::lock_guard statelock{dev->StateLock}; listlock.unlock(); /* Force the backend to stop mixing first since we're resetting. Also reset * the connected state so lost devices can attempt recover. */ - if(dev->Flags.test(DeviceRunning)) + if(dev->mDeviceState == DeviceState::Playing) + { dev->Backend->stop(); - dev->Flags.reset(DeviceRunning); + dev->mDeviceState = DeviceState::Configured; + } - return ResetDeviceParams(dev.get(), attribs) ? ALC_TRUE : ALC_FALSE; + return ResetDeviceParams(dev.get(), SpanFromAttributeList(attribs)) ? ALC_TRUE : ALC_FALSE; } @@ -3393,12 +3519,6 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi FORCE_ALIGN ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, const ALCchar *deviceName, const ALCint *attribs) noexcept { - if(deviceName) - { - if(!deviceName[0] || al::strcasecmp(deviceName, alcDefaultName) == 0) - deviceName = nullptr; - } - std::unique_lock listlock{ListLock}; DeviceRef dev{VerifyDevice(device)}; if(!dev || dev->Type != DeviceType::Playback) @@ -3407,24 +3527,39 @@ FORCE_ALIGN ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, alcSetError(dev.get(), ALC_INVALID_DEVICE); return ALC_FALSE; } - std::lock_guard _{dev->StateLock}; + std::lock_guard statelock{dev->StateLock}; - /* Force the backend to stop mixing first since we're reopening. */ - if(dev->Flags.test(DeviceRunning)) + std::string_view devname{deviceName ? deviceName : ""}; + if(!devname.empty()) { - auto backend = dev->Backend.get(); - backend->stop(); - dev->Flags.reset(DeviceRunning); + if(devname.length() >= size_t{std::numeric_limits::max()}) + { + ERR("Device name too long (%zu >= %d)\n", devname.length(), + std::numeric_limits::max()); + alcSetError(dev.get(), ALC_INVALID_VALUE); + return ALC_FALSE; + } + if(al::case_compare(devname, GetDefaultName()) == 0) + devname = {}; + else + { + const auto prefix = GetDevicePrefix(); + if(!prefix.empty() && devname.size() > prefix.size() + && al::starts_with(devname, prefix)) + devname = devname.substr(prefix.size()); + } + } + + /* Force the backend device to stop first since we're opening another one. */ + const bool wasPlaying{dev->mDeviceState == DeviceState::Playing}; + if(wasPlaying) + { + dev->Backend->stop(); + dev->mDeviceState = DeviceState::Configured; } BackendPtr newbackend; try { - const std::string_view devname{deviceName ? deviceName : ""}; - if(devname.length() >= std::numeric_limits::max()) - throw al::backend_exception{al::backend_error::NoDevice, - "Device name too long (%zu >= %d)", devname.length(), - std::numeric_limits::max()}; - newbackend = PlaybackFactory->createBackend(dev.get(), BackendType::Playback); newbackend->open(devname); } @@ -3436,16 +3571,12 @@ FORCE_ALIGN ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, alcSetError(dev.get(), (e.errorCode() == al::backend_error::OutOfMemory) ? ALC_OUT_OF_MEMORY : ALC_INVALID_VALUE); - /* If the device is connected, not paused, and has contexts, ensure it - * continues playing. - */ - if(dev->Connected.load(std::memory_order_relaxed) && !dev->Flags.test(DevicePaused) - && !dev->mContexts.load(std::memory_order_relaxed)->empty()) + if(dev->Connected.load(std::memory_order_relaxed) && wasPlaying) { try { auto backend = dev->Backend.get(); backend->start(); - dev->Flags.set(DeviceRunning); + dev->mDeviceState = DeviceState::Playing; } catch(al::backend_exception &be) { ERR("%s\n", be.what()); @@ -3455,8 +3586,34 @@ FORCE_ALIGN ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, return ALC_FALSE; } listlock.unlock(); + dev->mDeviceName = std::string{GetDevicePrefix()}+newbackend->mDeviceName; dev->Backend = std::move(newbackend); - TRACE("Reopened device %p, \"%s\"\n", voidp{dev.get()}, dev->DeviceName.c_str()); + dev->mDeviceState = DeviceState::Unprepared; + TRACE("Reopened device %p, \"%s\"\n", voidp{dev.get()}, dev->mDeviceName.c_str()); + + std::string{}.swap(dev->mVendorOverride); + std::string{}.swap(dev->mVersionOverride); + std::string{}.swap(dev->mRendererOverride); + auto checkopt = [&dev](const char *envname, const std::string_view optname) + { + if(auto optval = al::getenv(envname)) return optval; + return dev->configValue("game_compat", optname); + }; + if(auto overrideopt = checkopt("__ALSOFT_VENDOR_OVERRIDE", "vendor-override"sv)) + { + dev->mVendorOverride = std::move(*overrideopt); + TRACE("Overriding vendor string: \"%s\"\n", dev->mVendorOverride.c_str()); + } + if(auto overrideopt = checkopt("__ALSOFT_VERSION_OVERRIDE", "version-override"sv)) + { + dev->mVersionOverride = std::move(*overrideopt); + TRACE("Overriding version string: \"%s\"\n", dev->mVersionOverride.c_str()); + } + if(auto overrideopt = checkopt("__ALSOFT_RENDERER_OVERRIDE", "renderer-override"sv)) + { + dev->mRendererOverride = std::move(*overrideopt); + TRACE("Overriding renderer string: \"%s\"\n", dev->mRendererOverride.c_str()); + } /* Always return true even if resetting fails. It shouldn't fail, but this * is primarily to avoid confusion by the app seeing the function return @@ -3469,6 +3626,38 @@ FORCE_ALIGN ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, * In this way, we essentially act as if the function succeeded, but * immediately disconnects following it. */ - ResetDeviceParams(dev.get(), attribs); + ResetDeviceParams(dev.get(), SpanFromAttributeList(attribs)); return ALC_TRUE; } + +/************************************************ + * 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_FALSE; + } + + auto supported = alc::EventSupport::NoSupport; + switch(deviceType) + { + case ALC_PLAYBACK_DEVICE_SOFT: + if(PlaybackFactory) + supported = PlaybackFactory->queryEventSupport(*etype, BackendType::Playback); + return al::to_underlying(supported); + + case ALC_CAPTURE_DEVICE_SOFT: + if(CaptureFactory) + supported = CaptureFactory->queryEventSupport(*etype, BackendType::Capture); + return al::to_underlying(supported); + } + WARN("Invalid device type: 0x%04x\n", deviceType); + alcSetError(nullptr, ALC_INVALID_ENUM); + return ALC_FALSE; +} diff --git a/3rdparty/openal/alc/alconfig.cpp b/3rdparty/openal/alc/alconfig.cpp index 097ba3a0ca1a..23153301f28e 100644 --- a/3rdparty/openal/alc/alconfig.cpp +++ b/3rdparty/openal/alc/alconfig.cpp @@ -22,9 +22,6 @@ #include "alconfig.h" -#include -#include -#include #ifdef _WIN32 #include #include @@ -34,18 +31,25 @@ #endif #include -#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include +#include -#include "alfstream.h" +#include "almalloc.h" #include "alstring.h" #include "core/helpers.h" #include "core/logging.h" #include "strutils.h" -#include "vector.h" -#if defined(ALSOFT_UWP) +#if ALSOFT_UWP #include // !!This is important!! #include #include @@ -55,6 +59,14 @@ using namespace winrt; namespace { +using namespace std::string_view_literals; + +#if defined(_WIN32) && !defined(_GAMING_XBOX) && !ALSOFT_UWP +struct CoTaskMemDeleter { + void operator()(void *mem) const { CoTaskMemFree(mem); } +}; +#endif + struct ConfigEntry { std::string key; std::string value; @@ -79,57 +91,48 @@ bool readline(std::istream &f, std::string &output) return std::getline(f, output) && !output.empty(); } -std::string expdup(const char *str) +std::string expdup(std::string_view str) { std::string output; - std::string envval; - while(*str != '\0') + while(!str.empty()) { - const char *addstr; - size_t addstrlen; - - if(str[0] != '$') + if(auto nextpos = str.find('$')) { - const char *next = std::strchr(str, '$'); - addstr = str; - addstrlen = next ? static_cast(next-str) : std::strlen(str); + output += str.substr(0, nextpos); + if(nextpos == std::string_view::npos) + break; - str += addstrlen; + str.remove_prefix(nextpos); } - else - { - str++; - if(*str == '$') - { - const char *next = std::strchr(str+1, '$'); - addstr = str; - addstrlen = next ? static_cast(next-str) : std::strlen(str); - str += addstrlen; - } - else - { - const bool hasbraces{(*str == '{')}; + str.remove_prefix(1); + if(str.empty()) + { + output += '$'; + break; + } + if(str.front() == '$') + { + output += '$'; + str.remove_prefix(1); + continue; + } - if(hasbraces) str++; - const char *envstart = str; - while(std::isalnum(*str) || *str == '_') - ++str; - if(hasbraces && *str != '}') - continue; - const std::string envname{envstart, str}; - if(hasbraces) str++; + const bool hasbraces{str.front() == '{'}; + if(hasbraces) str.remove_prefix(1); - envval = al::getenv(envname.c_str()).value_or(std::string{}); - addstr = envval.data(); - addstrlen = envval.length(); - } - } - if(addstrlen == 0) + size_t envend{0}; + while(envend < str.size() && (std::isalnum(str[envend]) || str[envend] == '_')) + ++envend; + if(hasbraces && (envend == str.size() || str[envend] != '}')) continue; + const std::string envname{str.substr(0, envend)}; + if(hasbraces) ++envend; + str.remove_prefix(envend); - output.append(addstr, addstrlen); + if(auto envval = al::getenv(envname.c_str())) + output += *envval; } return output; @@ -147,44 +150,43 @@ void LoadConfigFromFile(std::istream &f) if(buffer[0] == '[') { - auto line = const_cast(buffer.data()); - char *section = line+1; - char *endsection; - - endsection = std::strchr(section, ']'); - if(!endsection || section == endsection) + auto endpos = buffer.find(']', 1); + if(endpos == 1 || endpos == std::string::npos) { - ERR(" config parse error: bad line \"%s\"\n", line); + ERR(" config parse error: bad line \"%s\"\n", buffer.c_str()); continue; } - if(endsection[1] != 0) + if(buffer[endpos+1] != '\0') { - char *end = endsection+1; - while(std::isspace(*end)) - ++end; - if(*end != 0 && *end != '#') + size_t last{endpos+1}; + while(last < buffer.size() && std::isspace(buffer[last])) + ++last; + + if(last < buffer.size() && buffer[last] != '#') { - ERR(" config parse error: bad line \"%s\"\n", line); + ERR(" config parse error: bad line \"%s\"\n", buffer.c_str()); continue; } } - *endsection = 0; + + auto section = std::string_view{buffer}.substr(1, endpos-1); curSection.clear(); - if(al::strcasecmp(section, "general") != 0) + if(al::case_compare(section, "general"sv) != 0) { do { - char *nextp = std::strchr(section, '%'); - if(!nextp) + auto nextp = section.find('%'); + if(nextp == std::string_view::npos) { curSection += section; break; } - curSection.append(section, nextp); - section = nextp; + curSection += section.substr(0, nextp); + section.remove_prefix(nextp); - if(((section[1] >= '0' && section[1] <= '9') || + if(section.size() > 2 && + ((section[1] >= '0' && section[1] <= '9') || (section[1] >= 'a' && section[1] <= 'f') || (section[1] >= 'A' && section[1] <= 'F')) && ((section[2] >= '0' && section[2] <= '9') || @@ -205,19 +207,19 @@ void LoadConfigFromFile(std::istream &f) else if(section[2] >= 'A' && section[2] <= 'F') b |= (section[2]-'A'+0x0a); curSection += static_cast(b); - section += 3; + section.remove_prefix(3); } - else if(section[1] == '%') + else if(section.size() > 1 && section[1] == '%') { curSection += '%'; - section += 2; + section.remove_prefix(2); } else { curSection += '%'; - section += 1; + section.remove_prefix(1); } - } while(*section != 0); + } while(!section.empty()); } continue; @@ -235,16 +237,17 @@ void LoadConfigFromFile(std::istream &f) ERR(" config parse error: malformed option line: \"%s\"\n", buffer.c_str()); continue; } - auto keyend = sep++; - while(keyend > 0 && std::isspace(buffer[keyend-1])) - --keyend; - if(!keyend) + auto keypart = std::string_view{buffer}.substr(0, sep++); + while(!keypart.empty() && std::isspace(keypart.back())) + keypart.remove_suffix(1); + if(keypart.empty()) { ERR(" config parse error: malformed option line: \"%s\"\n", buffer.c_str()); continue; } - while(sep < buffer.size() && std::isspace(buffer[sep])) - sep++; + auto valpart = std::string_view{buffer}.substr(sep); + while(!valpart.empty() && std::isspace(valpart.front())) + valpart.remove_prefix(1); std::string fullKey; if(!curSection.empty()) @@ -252,20 +255,24 @@ void LoadConfigFromFile(std::istream &f) fullKey += curSection; fullKey += '/'; } - fullKey += buffer.substr(0u, keyend); + fullKey += keypart; - std::string value{(sep < buffer.size()) ? buffer.substr(sep) : std::string{}}; - if(value.size() > 1) + if(valpart.size() > size_t{std::numeric_limits::max()}) + { + ERR(" config parse error: value too long in line \"%s\"\n", buffer.c_str()); + continue; + } + if(valpart.size() > 1) { - if((value.front() == '"' && value.back() == '"') - || (value.front() == '\'' && value.back() == '\'')) + if((valpart.front() == '"' && valpart.back() == '"') + || (valpart.front() == '\'' && valpart.back() == '\'')) { - value.pop_back(); - value.erase(value.begin()); + valpart.remove_prefix(1); + valpart.remove_suffix(1); } } - TRACE(" found '%s' = '%s'\n", fullKey.c_str(), value.c_str()); + TRACE(" setting '%s' = '%.*s'\n", fullKey.c_str(), al::sizei(valpart), valpart.data()); /* Check if we already have this option set */ auto find_key = [&fullKey](const ConfigEntry &entry) -> bool @@ -273,61 +280,50 @@ void LoadConfigFromFile(std::istream &f) auto ent = std::find_if(ConfOpts.begin(), ConfOpts.end(), find_key); if(ent != ConfOpts.end()) { - if(!value.empty()) - ent->value = expdup(value.c_str()); + if(!valpart.empty()) + ent->value = expdup(valpart); else ConfOpts.erase(ent); } - else if(!value.empty()) - ConfOpts.emplace_back(ConfigEntry{std::move(fullKey), expdup(value.c_str())}); + else if(!valpart.empty()) + ConfOpts.emplace_back(ConfigEntry{std::move(fullKey), expdup(valpart)}); } ConfOpts.shrink_to_fit(); } -const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName) +auto GetConfigValue(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName) -> const std::string& { - if(!keyName) - return nullptr; + static const auto emptyString = std::string{}; + if(keyName.empty()) + return emptyString; std::string key; - if(blockName && al::strcasecmp(blockName, "general") != 0) + if(!blockName.empty() && al::case_compare(blockName, "general"sv) != 0) { key = blockName; - if(devName) - { - key += '/'; - key += devName; - } key += '/'; - key += keyName; } - else + if(!devName.empty()) { - if(devName) - { - key = devName; - key += '/'; - } - key += keyName; + key += devName; + key += '/'; } + key += keyName; auto iter = std::find_if(ConfOpts.cbegin(), ConfOpts.cend(), - [&key](const ConfigEntry &entry) -> bool - { return entry.key == key; }); + [&key](const ConfigEntry &entry) -> bool { return entry.key == key; }); if(iter != ConfOpts.cend()) { - TRACE("Found %s = \"%s\"\n", key.c_str(), iter->value.c_str()); + TRACE("Found option %s = \"%s\"\n", key.c_str(), iter->value.c_str()); if(!iter->value.empty()) - return iter->value.c_str(); - return nullptr; + return iter->value; + return emptyString; } - if(!devName) - { - TRACE("Key %s not found\n", key.c_str()); - return nullptr; - } - return GetConfigValue(nullptr, blockName, keyName); + if(devName.empty()) + return emptyString; + return GetConfigValue({}, blockName, keyName); } } // namespace @@ -336,42 +332,49 @@ const char *GetConfigValue(const char *devName, const char *blockName, const cha #ifdef _WIN32 void ReadALConfig() { + namespace fs = std::filesystem; + fs::path path; + #if !defined(_GAMING_XBOX) { -#if !defined(ALSOFT_UWP) - WCHAR buffer[MAX_PATH]; - if (!SHGetSpecialFolderPathW(nullptr, buffer, CSIDL_APPDATA, FALSE)) - return; +#if !ALSOFT_UWP + std::unique_ptr bufstore; + const HRESULT hr{SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DONT_UNEXPAND, + nullptr, al::out_ptr(bufstore))}; + if(SUCCEEDED(hr)) + { + const std::wstring_view buffer{bufstore.get()}; #else winrt::Windows::Storage::ApplicationDataContainer localSettings = winrt::Windows::Storage::ApplicationData::Current().LocalSettings(); - auto buffer = Windows::Storage::ApplicationData::Current().RoamingFolder().Path(); + auto bufstore = Windows::Storage::ApplicationData::Current().RoamingFolder().Path(); + std::wstring_view buffer{bufstore}; + { #endif - std::string filepath{wstr_to_utf8(buffer)}; - filepath += "\\alsoft.ini"; + path = fs::path{buffer}; + path /= L"alsoft.ini"; - TRACE("Loading config %s...\n", filepath.c_str()); - al::ifstream f{filepath}; - if(f.is_open()) - LoadConfigFromFile(f); + TRACE("Loading config %s...\n", + reinterpret_cast(path.u8string().c_str())); + if(std::ifstream f{path}; f.is_open()) + LoadConfigFromFile(f); + } } #endif - - std::string ppath{GetProcBinary().path}; - if(!ppath.empty()) + path = fs::u8path(GetProcBinary().path); + if(!path.empty()) { - ppath += "\\alsoft.ini"; - TRACE("Loading config %s...\n", ppath.c_str()); - al::ifstream f{ppath}; - if(f.is_open()) + path /= L"alsoft.ini"; + TRACE("Loading config %s...\n", reinterpret_cast(path.u8string().c_str())); + if(std::ifstream f{path}; f.is_open()) LoadConfigFromFile(f); } if(auto confpath = al::getenv(L"ALSOFT_CONF")) { - TRACE("Loading config %s...\n", wstr_to_utf8(confpath->c_str()).c_str()); - al::ifstream f{*confpath}; - if(f.is_open()) + path = *confpath; + TRACE("Loading config %s...\n", reinterpret_cast(path.u8string().c_str())); + if(std::ifstream f{path}; f.is_open()) LoadConfigFromFile(f); } } @@ -380,13 +383,12 @@ void ReadALConfig() void ReadALConfig() { - const char *str{"/etc/openal/alsoft.conf"}; + namespace fs = std::filesystem; + fs::path path{"/etc/openal/alsoft.conf"}; - TRACE("Loading config %s...\n", str); - al::ifstream f{str}; - if(f.is_open()) + TRACE("Loading config %s...\n", reinterpret_cast(path.u8string().c_str())); + if(std::ifstream f{path}; f.is_open()) LoadConfigFromFile(f); - f.close(); std::string confpaths{al::getenv("XDG_CONFIG_DIRS").value_or("/etc/xdg")}; /* Go through the list in reverse, since "the order of base directories @@ -394,48 +396,45 @@ void ReadALConfig() * important". Ergo, we need to load the settings from the later dirs * first so that the settings in the earlier dirs override them. */ - std::string fname; while(!confpaths.empty()) { - auto next = confpaths.find_last_of(':'); + auto next = confpaths.rfind(':'); if(next < confpaths.length()) { - fname = confpaths.substr(next+1); + path = fs::path{std::string_view{confpaths}.substr(next+1)}.lexically_normal(); confpaths.erase(next); } else { - fname = confpaths; + path = fs::path{confpaths}.lexically_normal(); confpaths.clear(); } - if(fname.empty() || fname.front() != '/') - WARN("Ignoring XDG config dir: %s\n", fname.c_str()); + if(!path.is_absolute()) + WARN("Ignoring XDG config dir: %s\n", + reinterpret_cast(path.u8string().c_str())); else { - if(fname.back() != '/') fname += "/alsoft.conf"; - else fname += "alsoft.conf"; + path /= "alsoft.conf"; - TRACE("Loading config %s...\n", fname.c_str()); - f = al::ifstream{fname}; - if(f.is_open()) + TRACE("Loading config %s...\n", + reinterpret_cast(path.u8string().c_str())); + if(std::ifstream f{path}; f.is_open()) LoadConfigFromFile(f); } - fname.clear(); } #ifdef __APPLE__ CFBundleRef mainBundle = CFBundleGetMainBundle(); if(mainBundle) { - unsigned char fileName[PATH_MAX]; - CFURLRef configURL; + CFURLRef configURL{CFBundleCopyResourceURL(mainBundle, CFSTR(".alsoftrc"), CFSTR(""), + nullptr)}; - if((configURL=CFBundleCopyResourceURL(mainBundle, CFSTR(".alsoftrc"), CFSTR(""), nullptr)) && - CFURLGetFileSystemRepresentation(configURL, true, fileName, sizeof(fileName))) + std::array fileName{}; + if(configURL && CFURLGetFileSystemRepresentation(configURL, true, fileName.data(), fileName.size())) { - f = al::ifstream{reinterpret_cast(fileName)}; - if(f.is_open()) + if(std::ifstream f{reinterpret_cast(fileName.data())}; f.is_open()) LoadConfigFromFile(f); } } @@ -443,102 +442,135 @@ void ReadALConfig() if(auto homedir = al::getenv("HOME")) { - fname = *homedir; - if(fname.back() != '/') fname += "/.alsoftrc"; - else fname += ".alsoftrc"; + path = *homedir; + path /= ".alsoftrc"; - TRACE("Loading config %s...\n", fname.c_str()); - f = al::ifstream{fname}; - if(f.is_open()) + TRACE("Loading config %s...\n", reinterpret_cast(path.u8string().c_str())); + if(std::ifstream f{path}; f.is_open()) LoadConfigFromFile(f); } if(auto configdir = al::getenv("XDG_CONFIG_HOME")) { - fname = *configdir; - if(fname.back() != '/') fname += "/alsoft.conf"; - else fname += "alsoft.conf"; + path = *configdir; + path /= "alsoft.conf"; } else { - fname.clear(); + path.clear(); if(auto homedir = al::getenv("HOME")) { - fname = *homedir; - if(fname.back() != '/') fname += "/.config/alsoft.conf"; - else fname += ".config/alsoft.conf"; + path = *homedir; + path /= ".config/alsoft.conf"; } } - if(!fname.empty()) + if(!path.empty()) { - TRACE("Loading config %s...\n", fname.c_str()); - f = al::ifstream{fname}; - if(f.is_open()) + TRACE("Loading config %s...\n", reinterpret_cast(path.u8string().c_str())); + if(std::ifstream f{path}; f.is_open()) LoadConfigFromFile(f); } - std::string ppath{GetProcBinary().path}; - if(!ppath.empty()) + path = GetProcBinary().path; + if(!path.empty()) { - if(ppath.back() != '/') ppath += "/alsoft.conf"; - else ppath += "alsoft.conf"; + path /= "alsoft.conf"; - TRACE("Loading config %s...\n", ppath.c_str()); - f = al::ifstream{ppath}; - if(f.is_open()) + TRACE("Loading config %s...\n", reinterpret_cast(path.u8string().c_str())); + if(std::ifstream f{path}; f.is_open()) LoadConfigFromFile(f); } if(auto confname = al::getenv("ALSOFT_CONF")) { TRACE("Loading config %s...\n", confname->c_str()); - f = al::ifstream{*confname}; - if(f.is_open()) + if(std::ifstream f{*confname}; f.is_open()) LoadConfigFromFile(f); } } #endif -std::optional ConfigValueStr(const char *devName, const char *blockName, const char *keyName) +auto ConfigValueStr(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName) -> std::optional { - if(const char *val{GetConfigValue(devName, blockName, keyName)}) + if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) return val; return std::nullopt; } -std::optional ConfigValueInt(const char *devName, const char *blockName, const char *keyName) +auto ConfigValueInt(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName) -> std::optional { - if(const char *val{GetConfigValue(devName, blockName, keyName)}) - return static_cast(std::strtol(val, nullptr, 0)); + if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try { + return static_cast(std::stol(val, nullptr, 0)); + } + catch(std::exception&) { + WARN("Option is not an int: %.*s = %s\n", al::sizei(keyName), keyName.data(), val.c_str()); + } + return std::nullopt; } -std::optional ConfigValueUInt(const char *devName, const char *blockName, const char *keyName) +auto ConfigValueUInt(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName) -> std::optional { - if(const char *val{GetConfigValue(devName, blockName, keyName)}) - return static_cast(std::strtoul(val, nullptr, 0)); + if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try { + return static_cast(std::stoul(val, nullptr, 0)); + } + catch(std::exception&) { + WARN("Option is not an unsigned int: %.*s = %s\n", al::sizei(keyName), keyName.data(), + val.c_str()); + } return std::nullopt; } -std::optional ConfigValueFloat(const char *devName, const char *blockName, const char *keyName) +auto ConfigValueFloat(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName) -> std::optional { - if(const char *val{GetConfigValue(devName, blockName, keyName)}) - return std::strtof(val, nullptr); + if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try { + return std::stof(val); + } + catch(std::exception&) { + WARN("Option is not a float: %.*s = %s\n", al::sizei(keyName), keyName.data(), + val.c_str()); + } return std::nullopt; } -std::optional ConfigValueBool(const char *devName, const char *blockName, const char *keyName) +auto ConfigValueBool(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName) -> std::optional { - 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; + if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try { + return al::case_compare(val, "on"sv) == 0 || al::case_compare(val, "yes"sv) == 0 + || al::case_compare(val, "true"sv) == 0 || std::stoll(val) != 0; + } + catch(std::out_of_range&) { + /* If out of range, the value is some non-0 (true) value and it doesn't + * matter that it's too big or small. + */ + return true; + } + catch(std::exception&) { + /* If stoll fails to convert for any other reason, it's some other word + * that's treated as false. + */ + return false; + } return std::nullopt; } -bool GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, bool def) +auto GetConfigValueBool(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName, bool def) -> bool { - 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); + if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try { + return al::case_compare(val, "on"sv) == 0 || al::case_compare(val, "yes"sv) == 0 + || al::case_compare(val, "true"sv) == 0 || std::stoll(val) != 0; + } + catch(std::out_of_range&) { + return true; + } + catch(std::exception&) { + return false; + } return def; } diff --git a/3rdparty/openal/alc/alconfig.h b/3rdparty/openal/alc/alconfig.h index 1eb44405bcae..e7daac28ef93 100644 --- a/3rdparty/openal/alc/alconfig.h +++ b/3rdparty/openal/alc/alconfig.h @@ -3,16 +3,23 @@ #include #include +#include void ReadALConfig(); -bool GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, bool def); +bool GetConfigValueBool(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName, bool def); -std::optional ConfigValueStr(const char *devName, const char *blockName, const char *keyName); -std::optional ConfigValueInt(const char *devName, const char *blockName, const char *keyName); -std::optional ConfigValueUInt(const char *devName, const char *blockName, const char *keyName); -std::optional ConfigValueFloat(const char *devName, const char *blockName, const char *keyName); -std::optional ConfigValueBool(const char *devName, const char *blockName, const char *keyName); +std::optional ConfigValueStr(const std::string_view devName, + const std::string_view blockName, const std::string_view keyName); +std::optional ConfigValueInt(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName); +std::optional ConfigValueUInt(const std::string_view devName, + const std::string_view blockName, const std::string_view keyName); +std::optional ConfigValueFloat(const std::string_view devName, + const std::string_view blockName, const std::string_view keyName); +std::optional ConfigValueBool(const std::string_view devName, + const std::string_view blockName, const std::string_view keyName); #endif /* ALCONFIG_H */ diff --git a/3rdparty/openal/alc/alu.cpp b/3rdparty/openal/alc/alu.cpp index 6eb4691edfa3..8fbb947de07f 100644 --- a/3rdparty/openal/alc/alu.cpp +++ b/3rdparty/openal/alc/alu.cpp @@ -19,6 +19,7 @@ */ #include "config.h" +#include "config_simd.h" #include "alu.h" @@ -26,23 +27,25 @@ #include #include #include -#include -#include +#include #include +#include +#include #include #include -#include #include #include #include -#include #include -#include +#include +#include #include +#include #include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" +#include "alsem.h" #include "alspan.h" #include "alstring.h" #include "atomic.h" @@ -70,6 +73,7 @@ #include "core/mixer/defs.h" #include "core/mixer/hrtfdefs.h" #include "core/resampler_limits.h" +#include "core/storage_formats.h" #include "core/uhjfilter.h" #include "core/voice.h" #include "core/voice_change.h" @@ -78,19 +82,18 @@ #include "ringbuffer.h" #include "strutils.h" #include "vecmat.h" -#include "vector.h" struct CTag; -#ifdef HAVE_SSE +#if HAVE_SSE struct SSETag; #endif -#ifdef HAVE_SSE2 +#if HAVE_SSE2 struct SSE2Tag; #endif -#ifdef HAVE_SSE4_1 +#if HAVE_SSE4_1 struct SSE4Tag; #endif -#ifdef HAVE_NEON +#if HAVE_NEON struct NEONTag; #endif struct PointTag; @@ -107,15 +110,14 @@ namespace { using uint = unsigned int; using namespace std::chrono; - -using namespace std::placeholders; +using namespace std::string_view_literals; float InitConeScale() { float ret{1.0f}; if(auto optval = al::getenv("__ALSOFT_HALF_ANGLE_CONES")) { - if(al::strcasecmp(optval->c_str(), "true") == 0 + if(al::case_compare(*optval, "true"sv) == 0 || strtol(optval->c_str(), nullptr, 0) == 1) ret *= 0.5f; } @@ -136,18 +138,19 @@ float NfcScale{1.0f}; using HrtfDirectMixerFunc = void(*)(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut, - const al::span InSamples, float2 *AccumSamples, float *TempBuf, - HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize); + const al::span InSamples, const al::span AccumSamples, + const al::span TempBuf, const al::span ChanState, + const size_t IrSize, const size_t SamplesToDo); HrtfDirectMixerFunc MixDirectHrtf{MixDirectHrtf_}; -inline HrtfDirectMixerFunc SelectHrtfMixer(void) +inline HrtfDirectMixerFunc SelectHrtfMixer() { -#ifdef HAVE_NEON +#if HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return MixDirectHrtf_; #endif -#ifdef HAVE_SSE +#if HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) return MixDirectHrtf_; #endif @@ -164,7 +167,7 @@ inline void BsincPrepare(const uint increment, BsincState *state, const BSincTab if(increment > MixerFracOne) { sf = MixerFracOne/static_cast(increment) - table->scaleBase; - sf = maxf(0.0f, BSincScaleCount*sf*table->scaleRange - 1.0f); + sf = std::max(0.0f, BSincScaleCount*sf*table->scaleRange - 1.0f); si = float2uint(sf); /* The interpolation factor is fit to this diagonally-symmetric curve * to reduce the transition ripple caused by interpolating different @@ -176,7 +179,7 @@ inline void BsincPrepare(const uint increment, BsincState *state, const BSincTab state->sf = sf; state->m = table->m[si]; state->l = (state->m/2) - 1; - state->filter = table->Tab + table->filterOffset[si]; + state->filter = table->Tab.subspan(table->filterOffset[si]); } inline ResamplerFunc SelectResampler(Resampler resampler, uint increment) @@ -186,25 +189,34 @@ inline ResamplerFunc SelectResampler(Resampler resampler, uint increment) case Resampler::Point: return Resample_; case Resampler::Linear: -#ifdef HAVE_NEON +#if HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return Resample_; #endif -#ifdef HAVE_SSE4_1 +#if HAVE_SSE4_1 if((CPUCapFlags&CPU_CAP_SSE4_1)) return Resample_; #endif -#ifdef HAVE_SSE2 +#if HAVE_SSE2 if((CPUCapFlags&CPU_CAP_SSE2)) return Resample_; #endif return Resample_; - case Resampler::Cubic: -#ifdef HAVE_NEON + case Resampler::Spline: + case Resampler::Gaussian: +#if HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return Resample_; #endif -#ifdef HAVE_SSE +#if HAVE_SSE4_1 + if((CPUCapFlags&CPU_CAP_SSE4_1)) + return Resample_; +#endif +#if HAVE_SSE2 + if((CPUCapFlags&CPU_CAP_SSE2)) + return Resample_; +#endif +#if HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) return Resample_; #endif @@ -213,11 +225,11 @@ inline ResamplerFunc SelectResampler(Resampler resampler, uint increment) case Resampler::BSinc24: if(increment > MixerFracOne) { -#ifdef HAVE_NEON +#if HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return Resample_; #endif -#ifdef HAVE_SSE +#if HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) return Resample_; #endif @@ -226,11 +238,11 @@ inline ResamplerFunc SelectResampler(Resampler resampler, uint increment) /* fall-through */ case Resampler::FastBSinc12: case Resampler::FastBSinc24: -#ifdef HAVE_NEON +#if HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return Resample_; #endif -#ifdef HAVE_SSE +#if HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) return Resample_; #endif @@ -249,7 +261,7 @@ void aluInit(CompatFlagBitset flags, const float nfcscale) YScale = flags.test(CompatFlags::ReverseY) ? -1.0f : 1.0f; ZScale = flags.test(CompatFlags::ReverseZ) ? -1.0f : 1.0f; - NfcScale = clampf(nfcscale, 0.0001f, 10000.0f); + NfcScale = std::clamp(nfcscale, 0.0001f, 10000.0f); } @@ -260,16 +272,19 @@ ResamplerFunc PrepareResampler(Resampler resampler, uint increment, InterpState case Resampler::Point: case Resampler::Linear: break; - case Resampler::Cubic: - state->cubic.filter = gCubicSpline.Tab.data(); + case Resampler::Spline: + state->emplace(al::span{gSplineFilter.mTable}); + break; + case Resampler::Gaussian: + state->emplace(al::span{gGaussianFilter.mTable}); break; case Resampler::FastBSinc12: case Resampler::BSinc12: - BsincPrepare(increment, &state->bsinc, &gBSinc12); + BsincPrepare(increment, &state->emplace(), &gBSinc12); break; case Resampler::FastBSinc24: case Resampler::BSinc24: - BsincPrepare(increment, &state->bsinc, &gBSinc24); + BsincPrepare(increment, &state->emplace(), &gBSinc24); break; } return SelectResampler(resampler, increment); @@ -279,34 +294,33 @@ 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); + mHrtfState->mTemp, mHrtfState->mChannels, mHrtfState->mIrSize, SamplesToDo); } void DeviceBase::ProcessAmbiDec(const size_t SamplesToDo) { - AmbiDecoder->process(RealOut.Buffer, Dry.Buffer.data(), SamplesToDo); + AmbiDecoder->process(RealOut.Buffer, Dry.Buffer, 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); + AmbiDecoder->processStablize(RealOut.Buffer, Dry.Buffer, lidx, ridx, cidx, 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(), @@ -316,15 +330,14 @@ void DeviceBase::ProcessUhj(const size_t SamplesToDo) void DeviceBase::ProcessBs2b(const size_t SamplesToDo) { /* First, decode the ambisonic mix to the "real" output. */ - AmbiDecoder->process(RealOut.Buffer, Dry.Buffer.data(), SamplesToDo); + AmbiDecoder->process(RealOut.Buffer, Dry.Buffer, 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(), - SamplesToDo); + Bs2b->cross_feed(RealOut.Buffer[lidx].data(), RealOut.Buffer[ridx].data(), SamplesToDo); } @@ -350,21 +363,22 @@ inline uint dither_rng(uint *seed) noexcept void UpsampleBFormatTransform( const al::span,MaxAmbiChannels> output, const al::span> upsampler, - const al::span,MaxAmbiChannels> rotator, size_t coeffs_order) + const al::span,MaxAmbiChannels> rotator, + size_t ambi_order) { - const size_t num_chans{AmbiChannelsFromOrder(coeffs_order)}; + const size_t num_chans{AmbiChannelsFromOrder(ambi_order)}; for(size_t i{0};i < upsampler.size();++i) output[i].fill(0.0f); for(size_t i{0};i < upsampler.size();++i) { for(size_t k{0};k < num_chans;++k) { - float *RESTRICT out{output[i].data()}; + const float a{upsampler[i][k]}; /* Write the full number of channels. The compiler will have an * easier time optimizing if it has a fixed length. */ - for(size_t j{0};j < MaxAmbiChannels;++j) - out[j] += upsampler[i][k] * rotator[k][j]; + std::transform(rotator[k].cbegin(), rotator[k].cend(), output[i].cbegin(), + output[i].begin(), [a](float rot, float dst) noexcept { return rot*a + dst; }); } } } @@ -423,11 +437,19 @@ bool CalcContextParams(ContextBase *ctx) ctx->mParams.Velocity = rot * vel; ctx->mParams.Gain = props->Gain * ctx->mGainBoost; - ctx->mParams.MetersPerUnit = props->MetersPerUnit; + ctx->mParams.MetersPerUnit = props->MetersPerUnit +#if ALSOFT_EAX + * props->DistanceFactor +#endif + ; ctx->mParams.AirAbsorptionGainHF = props->AirAbsorptionGainHF; ctx->mParams.DopplerFactor = props->DopplerFactor; - ctx->mParams.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity; + ctx->mParams.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity +#if ALSOFT_EAX + / props->DistanceFactor +#endif + ; ctx->mParams.SourceDistanceModel = props->SourceDistanceModel; ctx->mParams.mDistanceModel = props->mDistanceModel; @@ -451,23 +473,27 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBa slot->Target = props->Target; slot->EffectType = props->Type; slot->mEffectProps = props->Props; - if(props->Type == EffectSlotType::Reverb || props->Type == EffectSlotType::EAXReverb) - { - slot->RoomRolloff = props->Props.Reverb.RoomRolloffFactor; - slot->DecayTime = props->Props.Reverb.DecayTime; - slot->DecayLFRatio = props->Props.Reverb.DecayLFRatio; - slot->DecayHFRatio = props->Props.Reverb.DecayHFRatio; - slot->DecayHFLimit = props->Props.Reverb.DecayHFLimit; - slot->AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF; - } - else + + slot->RoomRolloff = 0.0f; + slot->DecayTime = 0.0f; + slot->DecayLFRatio = 0.0f; + slot->DecayHFRatio = 0.0f; + slot->DecayHFLimit = false; + slot->AirAbsorptionGainHF = 1.0f; + if(auto *reverbprops = std::get_if(&props->Props)) { - slot->RoomRolloff = 0.0f; - slot->DecayTime = 0.0f; - slot->DecayLFRatio = 0.0f; - slot->DecayHFRatio = 0.0f; - slot->DecayHFLimit = false; - slot->AirAbsorptionGainHF = 1.0f; + slot->RoomRolloff = reverbprops->RoomRolloffFactor; + slot->AirAbsorptionGainHF = reverbprops->AirAbsorptionGainHF; + /* If this effect slot's Auxiliary Send Auto is off, don't apply the + * automatic send adjustments based on source distance. + */ + if(slot->AuxSendAuto) + { + slot->DecayTime = reverbprops->DecayTime; + slot->DecayLFRatio = reverbprops->DecayLFRatio; + slot->DecayHFRatio = reverbprops->DecayHFRatio; + slot->DecayHFLimit = reverbprops->DecayHFLimit; + } } EffectState *state{props->State.release()}; @@ -482,9 +508,9 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBa /* Otherwise, if it would be deleted send it off with a release event. */ RingBuffer *ring{context->mAsyncEvents.get()}; auto evt_vec = ring->getWriteVector(); - if(evt_vec.first.len > 0) LIKELY + if(evt_vec[0].len > 0) LIKELY { - auto &evt = InitAsyncEvent(evt_vec.first.buf); + auto &evt = InitAsyncEvent(evt_vec[0].buf); evt.mEffectState = oldstate; ring->writeAdvance(1); } @@ -499,16 +525,15 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBa } } - AtomicReplaceHead(context->mFreeEffectslotProps, props); + AtomicReplaceHead(context->mFreeEffectSlotProps, props); - EffectTarget output; - if(EffectSlot *target{slot->Target}) - output = EffectTarget{&target->Wet, nullptr}; - else + const auto output = [slot,context]() -> EffectTarget { + if(EffectSlot *target{slot->Target}) + return EffectTarget{&target->Wet, nullptr}; DeviceBase *device{context->mDevice}; - output = EffectTarget{&device->Dry, &device->RealOut}; - } + return EffectTarget{&device->Dry, &device->RealOut}; + }(); state->update(context, slot, &slot->mEffectProps, output); return true; } @@ -603,19 +628,18 @@ inline std::array ScaleAzimuthFront3_2(std::array pos) * precomputed since they're constant. The second-order coefficients are * followed by the third-order coefficients, etc. */ -template -constexpr size_t CalcRotatorSize() -{ return (L*2 + 1)*(L*2 + 1) + CalcRotatorSize(); } - -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()> mCoeffs{}; + std::array mCoeffs{}; RotatorCoeffs() { @@ -627,17 +651,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((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(l*l - m*m)/denom); - coeffs->v = std::sqrt(static_cast(l+abs_m-1) * - static_cast(l+abs_m) / denom) * (1.0f+d) * (1.0f - 2.0f*d) * 0.5f; - coeffs->w = std::sqrt(static_cast(l-abs_m-1) * - static_cast(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((std::abs(n) == l) ? + (2*l) * (2*l - 1) : (l*l - n*n))}; + + if(m == 0) + { + coeffs->u = static_cast(std::sqrt(l * l / denom)); + coeffs->v = static_cast(std::sqrt((l-1) * l / denom) * -1.0); + coeffs->w = 0.0f; + } + else + { + const int abs_m{std::abs(m)}; + coeffs->u = static_cast(std::sqrt((l*l - m*m) / denom)); + coeffs->v = static_cast(std::sqrt((l+abs_m-1) * (l+abs_m) / denom) * + 0.5); + coeffs->w = static_cast(std::sqrt((l-abs_m-1) * (l-abs_m) / denom) * + -0.5); + } ++coeffs; } } @@ -656,27 +701,27 @@ void AmbiRotator(AmbiRotateMatrix &matrix, const int order) /* Don't do anything for < 2nd order. */ if(order < 2) return; - auto P = [](const int i, const int l, const int a, const int n, const size_t last_band, - const AmbiRotateMatrix &R) + static constexpr auto P = [](const int i, const int l, const int a, const int n, + const size_t last_band, const AmbiRotateMatrix &R) { - const float ri1{ R[ 1+2][static_cast(i+2)]}; - const float rim1{R[-1+2][static_cast(i+2)]}; - const float ri0{ R[ 0+2][static_cast(i+2)]}; + const float ri1{ R[ 1+2][static_cast(i+2_z)]}; + const float rim1{R[-1+2][static_cast(i+2_z)]}; + const float ri0{ R[ 0+2][static_cast(i+2_z)]}; const size_t y{last_band + static_cast(a+l-1)}; if(n == -l) - return ri1*R[last_band][y] + rim1*R[last_band + static_cast(l-1)*2][y]; + return ri1*R[last_band][y] + rim1*R[last_band + static_cast(l-1_z)*2][y]; if(n == l) - return ri1*R[last_band + static_cast(l-1)*2][y] - rim1*R[last_band][y]; - return ri0*R[last_band + static_cast(n+l-1)][y]; + return ri1*R[last_band + static_cast(l-1_z)*2][y] - rim1*R[last_band][y]; + return ri0*R[last_band + static_cast(l-1_z+n)][y]; }; - auto U = [P](const int l, const int m, const int n, const size_t last_band, + static constexpr auto U = [](const int l, const int m, const int n, const size_t last_band, const AmbiRotateMatrix &R) { return P(0, l, m, n, last_band, R); }; - auto V = [P](const int l, const int m, const int n, const size_t last_band, + static constexpr auto V = [](const int l, const int m, const int n, const size_t last_band, const AmbiRotateMatrix &R) { using namespace al::numbers; @@ -692,7 +737,7 @@ void AmbiRotator(AmbiRotateMatrix &matrix, const int order) const float p1{P(-1, l, -m-1, n, last_band, R)}; return d ? p1*sqrt2_v : (p0 + p1); }; - auto W = [P](const int l, const int m, const int n, const size_t last_band, + static constexpr auto W = [](const int l, const int m, const int n, const size_t last_band, const AmbiRotateMatrix &R) { assert(m != 0); @@ -721,12 +766,12 @@ 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; @@ -756,48 +801,54 @@ struct GainTriplet { float Base, HF, LF; }; void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, const float zpos, const float Distance, const float Spread, const GainTriplet &DryGain, - const al::span WetGain, EffectSlot *(&SendSlots)[MAX_SENDS], - const VoiceProps *props, const ContextParams &Context, DeviceBase *Device) + const al::span WetGain, + const al::span SendSlots, const VoiceProps *props, + const ContextParams &Context, DeviceBase *Device) { - static constexpr ChanPosMap MonoMap[1]{ - { FrontCenter, std::array{0.0f, 0.0f, -1.0f} } - }, RearMap[2]{ - { BackLeft, std::array{-sin30, 0.0f, cos30} }, - { BackRight, std::array{ sin30, 0.0f, cos30} }, - }, QuadMap[4]{ - { 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, 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, 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, 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} }, + static constexpr std::array MonoMap{ + ChanPosMap{FrontCenter, std::array{0.0f, 0.0f, -1.0f}} + }; + static constexpr std::array RearMap{ + ChanPosMap{BackLeft, std::array{-sin30, 0.0f, cos30}}, + ChanPosMap{BackRight, std::array{ sin30, 0.0f, cos30}}, + }; + static constexpr std::array QuadMap{ + ChanPosMap{FrontLeft, std::array{-sin45, 0.0f, -cos45}}, + ChanPosMap{FrontRight, std::array{ sin45, 0.0f, -cos45}}, + ChanPosMap{BackLeft, std::array{-sin45, 0.0f, cos45}}, + ChanPosMap{BackRight, std::array{ sin45, 0.0f, cos45}}, + }; + static constexpr std::array X51Map{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}}, + ChanPosMap{LFE, {}}, + ChanPosMap{SideLeft, std::array{-sin110, 0.0f, -cos110}}, + ChanPosMap{SideRight, std::array{ sin110, 0.0f, -cos110}}, + }; + static constexpr std::array X61Map{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}}, + ChanPosMap{LFE, {}}, + ChanPosMap{BackCenter, std::array{ 0.0f, 0.0f, 1.0f}}, + ChanPosMap{SideLeft, std::array{-1.0f, 0.0f, 0.0f}}, + ChanPosMap{SideRight, std::array{ 1.0f, 0.0f, 0.0f}}, + }; + static constexpr std::array X71Map{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}}, + ChanPosMap{LFE, {}}, + ChanPosMap{BackLeft, std::array{-sin30, 0.0f, cos30}}, + ChanPosMap{BackRight, std::array{ sin30, 0.0f, cos30}}, + ChanPosMap{SideLeft, std::array{ -1.0f, 0.0f, 0.0f}}, + ChanPosMap{SideRight, std::array{ 1.0f, 0.0f, 0.0f}}, }; - ChanPosMap StereoMap[2]{ - { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, - { FrontRight, std::array{ sin30, 0.0f, -cos30} }, + std::array StereoMap{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, }; const auto Frequency = static_cast(Device->Frequency); @@ -814,49 +865,84 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con [](SendParams ¶ms) -> void { params.Gains.Target.fill(0.0f); }); } - DirectMode DirectChannels{props->DirectChannels}; - const ChanPosMap *chans{nullptr}; - switch(voice->mFmtChannels) + const auto getChans = [props,&StereoMap](FmtChannels chanfmt) noexcept + -> std::pair> { - case FmtMono: - chans = MonoMap; - /* Mono buffers are never played direct. */ - DirectChannels = DirectMode::Off; - break; - - case FmtStereo: - if(DirectChannels == DirectMode::Off) + switch(chanfmt) { - for(size_t i{0};i < 2;++i) + case FmtMono: + /* Mono buffers are never played direct. */ + return {DirectMode::Off, al::span{MonoMap}}; + + case FmtStereo: + case FmtMonoDup: + if(props->DirectChannels == DirectMode::Off) { - /* 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); + 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); + } } + return {props->DirectChannels, al::span{StereoMap}}; + + case FmtRear: return {props->DirectChannels, al::span{RearMap}}; + case FmtQuad: return {props->DirectChannels, al::span{QuadMap}}; + case FmtX51: return {props->DirectChannels, al::span{X51Map}}; + case FmtX61: return {props->DirectChannels, al::span{X61Map}}; + case FmtX71: return {props->DirectChannels, al::span{X71Map}}; + + case FmtBFormat2D: + case FmtBFormat3D: + case FmtUHJ2: + case FmtUHJ3: + case FmtUHJ4: + case FmtSuperStereo: + return {DirectMode::Off, {}}; } - chans = StereoMap; - break; - - case FmtRear: chans = RearMap; break; - case FmtQuad: chans = QuadMap; break; - case FmtX51: chans = X51Map; break; - case FmtX61: chans = X61Map; break; - case FmtX71: chans = X71Map; break; - - case FmtBFormat2D: - case FmtBFormat3D: - case FmtUHJ2: - case FmtUHJ3: - case FmtUHJ4: - case FmtSuperStereo: - DirectChannels = DirectMode::Off; - break; - } + return {props->DirectChannels, {}}; + }; + const auto [DirectChannels,chans] = getChans(voice->mFmtChannels); voice->mFlags.reset(VoiceHasHrtf).reset(VoiceHasNfc); if(auto *decoder{voice->mDecoder.get()}) - decoder->mWidthControl = minf(props->EnhWidth, 0.7f); + decoder->mWidthControl = std::min(props->EnhWidth, 0.7f); + + const float lgain{std::min(1.0f-props->Panning, 1.0f)}; + const float rgain{std::min(1.0f+props->Panning, 1.0f)}; + const float mingain{std::min(lgain, rgain)}; + auto SelectChannelGain = [lgain,rgain,mingain](const Channel chan) noexcept + { + switch(chan) + { + case FrontLeft: return lgain; + case FrontRight: return rgain; + case FrontCenter: break; + case LFE: break; + case BackLeft: return lgain; + case BackRight: return rgain; + case BackCenter: break; + case SideLeft: return lgain; + case SideRight: return rgain; + case TopCenter: break; + case TopFrontLeft: return lgain; + case TopFrontCenter: break; + case TopFrontRight: return rgain; + case TopBackLeft: return lgain; + case TopBackCenter: break; + case TopBackRight: return rgain; + case BottomFrontLeft: return lgain; + case BottomFrontRight: return rgain; + case BottomBackLeft: return lgain; + case BottomBackRight: return rgain; + case Aux0: case Aux1: case Aux2: case Aux3: case Aux4: case Aux5: case Aux6: case Aux7: + case Aux8: case Aux9: case Aux10: case Aux11: case Aux12: case Aux13: case Aux14: + case Aux15: case MaxChannels: break; + } + return mingain; + }; if(IsAmbisonic(voice->mFmtChannels)) { @@ -878,7 +964,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con /* Clamp the distance for really close sources, to prevent * excessive bass. */ - const float mdist{maxf(Distance*NfcScale, Device->AvgSpeakerDist/4.0f)}; + const float mdist{std::max(Distance*NfcScale, Device->AvgSpeakerDist/4.0f)}; const float w0{SpeedOfSoundMetersPerSec / (mdist * Frequency)}; /* Only need to adjust the first channel of a B-Format source. */ @@ -908,12 +994,12 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con 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); } } @@ -995,9 +1081,9 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con /* Convert the rotation matrix for input ordering and scaling, and * whether input is 2D or 3D. */ - const uint8_t *index_map{Is2DAmbisonic(voice->mFmtChannels) ? - GetAmbi2DLayout(voice->mAmbiLayout).data() : - GetAmbiLayout(voice->mAmbiLayout).data()}; + const auto index_map = Is2DAmbisonic(voice->mFmtChannels) ? + GetAmbi2DLayout(voice->mAmbiLayout).subspan(0) : + GetAmbiLayout(voice->mAmbiLayout).subspan(0); /* Scale the panned W signal inversely to coverage (full coverage * means no panned signal), and according to the channel scaling. @@ -1014,16 +1100,17 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con * to the coverage amount) with the directional pan. For all * other channels, use just the (scaled) B-Format signal. */ - for(size_t x{0};x < MaxAmbiChannels;++x) - coeffs[x] += mixmatrix[acn][x] * scale; + std::transform(mixmatrix[acn].cbegin(), mixmatrix[acn].cend(), coeffs.begin(), + coeffs.begin(), [scale](const float in, const float coeff) noexcept + { return in*scale + coeff; }); - 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); } @@ -1040,13 +1127,13 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con for(size_t c{0};c < num_channels;c++) { - uint idx{Device->channelIdxByName(chans[c].channel)}; - if(idx != InvalidChannelIndex) - voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base; + const float pangain{SelectChannelGain(chans[c].channel)}; + if(uint idx{Device->channelIdxByName(chans[c].channel)}; idx != InvalidChannelIndex) + voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base * pangain; else if(DirectChannels == DirectMode::RemixMismatch) { - auto match_channel = [chans,c](const InputRemixMap &map) noexcept -> bool - { return chans[c].channel == map.channel; }; + auto match_channel = [channel=chans[c].channel](const InputRemixMap &map) noexcept + { return channel == map.channel; }; auto remap = std::find_if(Device->RealOut.RemixMap.cbegin(), Device->RealOut.RemixMap.cend(), match_channel); if(remap != Device->RealOut.RemixMap.cend()) @@ -1055,8 +1142,8 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con { idx = Device->channelIdxByName(target.channel); if(idx != InvalidChannelIndex) - voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base * - target.mix; + voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base * pangain + * target.mix; } } } @@ -1071,12 +1158,13 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con if(chans[c].channel == LFE) continue; + const float pangain{SelectChannelGain(chans[c].channel)}; 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 * pangain, voice->mChans[c].mWetParams[i].Gains.Target); } } @@ -1092,7 +1180,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con { if(voice->mFmtChannels == FmtMono) { - const float src_ev{std::asin(clampf(ypos, -1.0f, 1.0f))}; + const float src_ev{std::asin(std::clamp(ypos, -1.0f, 1.0f))}; const float src_az{std::atan2(xpos, -zpos)}; Device->mHrtf->getCoeffs(src_ev, src_az, Distance*NfcScale, Spread, @@ -1104,23 +1192,22 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con 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); } } else for(size_t c{0};c < num_channels;c++) { - using namespace al::numbers; - /* Skip LFE */ if(chans[c].channel == LFE) continue; + const float pangain{SelectChannelGain(chans[c].channel)}; /* Warp the channel position toward the source position as the * source spread decreases. With no spread, all channels are at * the source position, at full spread (pi*2), each channel is * left unchanged. */ - const float a{1.0f - (inv_pi_v/2.0f)*Spread}; + const float a{1.0f - (al::numbers::inv_pi_v/2.0f)*Spread}; std::array pos{ lerpf(chans[c].pos[0], xpos, a), lerpf(chans[c].pos[1], ypos, a), @@ -1133,19 +1220,19 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con pos[2] /= len; } - const float ev{std::asin(clampf(pos[1], -1.0f, 1.0f))}; + const float ev{std::asin(std::clamp(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; + voice->mChans[c].mDryParams.Hrtf.Target.Gain = DryGain.Base * pangain; 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 * pangain, voice->mChans[c].mWetParams[i].Gains.Target); } } @@ -1156,7 +1243,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con * where it can be 0 or full (non-mono sources are always full * spread here). */ - const float spread{Spread * (voice->mFmtChannels == FmtMono)}; + const float spread{Spread * float(voice->mFmtChannels == FmtMono)}; /* Local sources on HRTF play with each channel panned to its * relative location around the listener, providing "virtual @@ -1167,6 +1254,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con /* Skip LFE */ if(chans[c].channel == LFE) continue; + const float pangain{SelectChannelGain(chans[c].channel)}; /* Get the HRIR coefficients and delays for this channel * position. @@ -1177,7 +1265,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con Device->mHrtf->getCoeffs(ev, az, std::numeric_limits::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; + voice->mChans[c].mDryParams.Hrtf.Target.Gain = DryGain.Base * pangain; /* Normal panning for auxiliary sends. */ const auto coeffs = CalcDirectionCoeffs(chans[c].pos, spread); @@ -1185,7 +1273,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con 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 * pangain, voice->mChans[c].mWetParams[i].Gains.Target); } } @@ -1205,7 +1293,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con /* Clamp the distance for really close sources, to prevent * excessive bass. */ - const float mdist{maxf(Distance*NfcScale, Device->AvgSpeakerDist/4.0f)}; + const float mdist{std::max(Distance*NfcScale, Device->AvgSpeakerDist/4.0f)}; const float w0{SpeedOfSoundMetersPerSec / (mdist * Frequency)}; /* Adjust NFC filters. */ @@ -1226,63 +1314,60 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con }; 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); } } - else + else for(size_t c{0};c < num_channels;c++) { - using namespace al::numbers; + const auto pangain = SelectChannelGain(chans[c].channel); - for(size_t c{0};c < num_channels;c++) + /* Special-case LFE */ + if(chans[c].channel == LFE) { - /* Special-case LFE */ - if(chans[c].channel == LFE) + if(Device->Dry.Buffer.data() == Device->RealOut.Buffer.data()) { - if(Device->Dry.Buffer.data() == Device->RealOut.Buffer.data()) - { - const uint idx{Device->channelIdxByName(chans[c].channel)}; - if(idx != InvalidChannelIndex) - voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base; - } - continue; + const auto idx = uint{Device->channelIdxByName(chans[c].channel)}; + if(idx != InvalidChannelIndex) + voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base * pangain; } + continue; + } - /* Warp the channel position toward the source position as - * the spread decreases. With no spread, all channels are - * at the source position, at full spread (pi*2), each - * channel position is left unchanged. - */ - const float a{1.0f - (inv_pi_v/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; - } + /* Warp the channel position toward the source position as the + * spread decreases. With no spread, all channels are at the + * source position, at full spread (pi*2), each channel + * position is left unchanged. + */ + const auto a = 1.0f - (al::numbers::inv_pi_v/2.0f)*Spread; + auto pos = std::array{ + lerpf(chans[c].pos[0], xpos, a), + lerpf(chans[c].pos[1], ypos, a), + lerpf(chans[c].pos[2], zpos, a)}; + const auto 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) - pos = ScaleAzimuthFront3(pos); - const auto coeffs = CalcDirectionCoeffs(pos, 0.0f); + if(Device->mRenderMode == RenderMode::Pairwise) + pos = ScaleAzimuthFront3(pos); + const auto coeffs = CalcDirectionCoeffs(pos, 0.0f); - ComputePanGains(&Device->Dry, coeffs.data(), 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, - voice->mChans[c].mWetParams[i].Gains.Target); - } + ComputePanGains(&Device->Dry, coeffs, DryGain.Base * pangain, + voice->mChans[c].mDryParams.Gains.Target); + for(uint i{0};i < NumSends;i++) + { + if(const EffectSlot *Slot{SendSlots[i]}) + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base * pangain, + voice->mChans[c].mWetParams[i].Gains.Target); } } } @@ -1304,9 +1389,11 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con * where it can be 0 or full (non-mono sources are always full * spread here). */ - const float spread{Spread * (voice->mFmtChannels == FmtMono)}; + const float spread{Spread * float(voice->mFmtChannels == FmtMono)}; for(size_t c{0};c < num_channels;c++) { + const float pangain{SelectChannelGain(chans[c].channel)}; + /* Special-case LFE */ if(chans[c].channel == LFE) { @@ -1314,7 +1401,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con { const uint idx{Device->channelIdxByName(chans[c].channel)}; if(idx != InvalidChannelIndex) - voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base; + voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base * pangain; } continue; } @@ -1322,12 +1409,12 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con 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 * pangain, 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 * pangain, voice->mChans[c].mWetParams[i].Gains.Target); } } @@ -1376,7 +1463,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con void CalcNonAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBase *context) { DeviceBase *Device{context->mDevice}; - EffectSlot *SendSlots[MAX_SENDS]; + std::array SendSlots{}; voice->mDirect.Buffer = Device->Dry.Buffer; for(uint i{0};i < Device->NumAuxSends;i++) @@ -1397,19 +1484,20 @@ void CalcNonAttnSourceParams(Voice *voice, const VoiceProps *props, const Contex if(Pitch > float{MaxPitch}) voice->mStep = MaxPitch<mStep = maxu(fastf2u(Pitch * MixerFracOne), 1); + voice->mStep = std::max(fastf2u(Pitch * MixerFracOne), 1u); voice->mResampler = PrepareResampler(props->mResampler, voice->mStep, &voice->mResampleState); /* Calculate gains */ - GainTriplet DryGain; - DryGain.Base = minf(clampf(props->Gain, props->MinGain, props->MaxGain) * props->Direct.Gain * - context->mParams.Gain, GainMixMax); + GainTriplet DryGain{}; + DryGain.Base = std::min(std::clamp(props->Gain, props->MinGain, props->MaxGain) * + props->Direct.Gain * context->mParams.Gain, GainMixMax); DryGain.HF = props->Direct.GainHF; DryGain.LF = props->Direct.GainLF; - GainTriplet WetGain[MAX_SENDS]; + + std::array WetGain{}; for(uint i{0};i < Device->NumAuxSends;i++) { - WetGain[i].Base = minf(clampf(props->Gain, props->MinGain, props->MaxGain) * + WetGain[i].Base = std::min(std::clamp(props->Gain, props->MinGain, props->MaxGain) * props->Send[i].Gain * context->mParams.Gain, GainMixMax); WetGain[i].HF = props->Send[i].GainHF; WetGain[i].LF = props->Send[i].GainLF; @@ -1426,25 +1514,26 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa /* Set mixing buffers and get send parameters. */ voice->mDirect.Buffer = Device->Dry.Buffer; - EffectSlot *SendSlots[MAX_SENDS]; - uint UseDryAttnForRoom{0}; + std::array SendSlots{}; + std::array RoomRolloff{}; for(uint i{0};i < NumSends;i++) { SendSlots[i] = props->Send[i].Slot; if(!SendSlots[i] || SendSlots[i]->EffectType == EffectSlotType::None) + { SendSlots[i] = nullptr; - else if(!SendSlots[i]->AuxSendAuto) + voice->mSend[i].Buffer = {}; + } + else { - /* If the slot's auxiliary send auto is off, the data sent to the - * effect slot is the same as the dry path, sans filter effects. + /* NOTE: Contrary to the EFX docs, the effect's room rolloff factor + * applies to the selected distance model along with the source's + * room rolloff factor, not necessarily the inverse distance model. */ - UseDryAttnForRoom |= 1u<RoomRolloffFactor + SendSlots[i]->RoomRolloff; - if(!SendSlots[i]) - voice->mSend[i].Buffer = {}; - else voice->mSend[i].Buffer = SendSlots[i]->Wet.Buffer; + } } /* Transform source to listener space (convert to head relative) */ @@ -1471,62 +1560,77 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa /* Calculate distance attenuation */ float ClampedDist{Distance}; float DryGainBase{props->Gain}; - float WetGainBase{props->Gain}; + std::array WetGainBase{}; + WetGainBase.fill(props->Gain); + float DryAttnBase{1.0f}; switch(context->mParams.SourceDistanceModel ? props->mDistanceModel : context->mParams.mDistanceModel) { - case DistanceModel::InverseClamped: - if(props->MaxDistance < props->RefDistance) break; - ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance); - /*fall-through*/ - case DistanceModel::Inverse: - if(props->RefDistance > 0.0f) + case DistanceModel::InverseClamped: + if(props->MaxDistance < props->RefDistance) break; + ClampedDist = std::clamp(ClampedDist, props->RefDistance, props->MaxDistance); + /*fall-through*/ + case DistanceModel::Inverse: + if(props->RefDistance > 0.0f) + { + float dist{lerpf(props->RefDistance, ClampedDist, props->RolloffFactor)}; + if(dist > 0.0f) { - float dist{lerpf(props->RefDistance, ClampedDist, props->RolloffFactor)}; - if(dist > 0.0f) DryGainBase *= props->RefDistance / dist; - - dist = lerpf(props->RefDistance, ClampedDist, props->RoomRolloffFactor); - if(dist > 0.0f) WetGainBase *= props->RefDistance / dist; + DryAttnBase = props->RefDistance / dist; + DryGainBase *= DryAttnBase; } - break; - - case DistanceModel::LinearClamped: - if(props->MaxDistance < props->RefDistance) break; - ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance); - /*fall-through*/ - case DistanceModel::Linear: - if(props->MaxDistance != props->RefDistance) - { - float attn{(ClampedDist-props->RefDistance) / - (props->MaxDistance-props->RefDistance) * props->RolloffFactor}; - DryGainBase *= maxf(1.0f - attn, 0.0f); - attn = (ClampedDist-props->RefDistance) / - (props->MaxDistance-props->RefDistance) * props->RoomRolloffFactor; - WetGainBase *= maxf(1.0f - attn, 0.0f); + for(size_t i{0};i < NumSends;++i) + { + dist = lerpf(props->RefDistance, ClampedDist, RoomRolloff[i]); + if(dist > 0.0f) WetGainBase[i] *= props->RefDistance / dist; } - break; - - case DistanceModel::ExponentClamped: - if(props->MaxDistance < props->RefDistance) break; - ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance); - /*fall-through*/ - case DistanceModel::Exponent: - if(ClampedDist > 0.0f && props->RefDistance > 0.0f) + } + break; + + case DistanceModel::LinearClamped: + if(props->MaxDistance < props->RefDistance) break; + ClampedDist = std::clamp(ClampedDist, props->RefDistance, props->MaxDistance); + /*fall-through*/ + case DistanceModel::Linear: + if(props->MaxDistance != props->RefDistance) + { + float attn{(ClampedDist-props->RefDistance) / + (props->MaxDistance-props->RefDistance) * props->RolloffFactor}; + DryAttnBase = std::max(1.0f - attn, 0.0f); + DryGainBase *= DryAttnBase; + + for(size_t i{0};i < NumSends;++i) { - const float dist_ratio{ClampedDist/props->RefDistance}; - DryGainBase *= std::pow(dist_ratio, -props->RolloffFactor); - WetGainBase *= std::pow(dist_ratio, -props->RoomRolloffFactor); + attn = (ClampedDist-props->RefDistance) / + (props->MaxDistance-props->RefDistance) * RoomRolloff[i]; + WetGainBase[i] *= std::max(1.0f - attn, 0.0f); } - break; + } + break; - case DistanceModel::Disable: - break; + case DistanceModel::ExponentClamped: + if(props->MaxDistance < props->RefDistance) break; + ClampedDist = std::clamp(ClampedDist, props->RefDistance, props->MaxDistance); + /*fall-through*/ + case DistanceModel::Exponent: + if(ClampedDist > 0.0f && props->RefDistance > 0.0f) + { + const float dist_ratio{ClampedDist/props->RefDistance}; + DryAttnBase = std::pow(dist_ratio, -props->RolloffFactor); + DryGainBase *= DryAttnBase; + for(size_t i{0};i < NumSends;++i) + WetGainBase[i] *= std::pow(dist_ratio, -RoomRolloff[i]); + } + break; + + case DistanceModel::Disable: + break; } /* Calculate directional soundcones */ - float ConeHF{1.0f}, WetConeHF{1.0f}; + float ConeHF{1.0f}, WetCone{1.0f}, WetConeHF{1.0f}; if(directional && props->InnerAngle < 360.0f) { static constexpr float Rad2Deg{static_cast(180.0 / al::numbers::pi)}; @@ -1536,52 +1640,62 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa if(Angle >= props->OuterAngle) { ConeGain = props->OuterGain; - ConeHF = lerpf(1.0f, props->OuterGainHF, props->DryGainHFAuto); + if(props->DryGainHFAuto) + ConeHF = props->OuterGainHF; } else if(Angle >= props->InnerAngle) { const float scale{(Angle-props->InnerAngle) / (props->OuterAngle-props->InnerAngle)}; ConeGain = lerpf(1.0f, props->OuterGain, scale); - ConeHF = lerpf(1.0f, props->OuterGainHF, scale * props->DryGainHFAuto); + if(props->DryGainHFAuto) + ConeHF = lerpf(1.0f, props->OuterGainHF, scale); } DryGainBase *= ConeGain; - WetGainBase *= lerpf(1.0f, ConeGain, props->WetGainAuto); - - WetConeHF = lerpf(1.0f, ConeHF, props->WetGainHFAuto); + if(props->WetGainAuto) + WetCone = ConeGain; + if(props->WetGainHFAuto) + WetConeHF = ConeHF; } /* Apply gain and frequency filters */ - DryGainBase = clampf(DryGainBase, props->MinGain, props->MaxGain) * context->mParams.Gain; - WetGainBase = clampf(WetGainBase, props->MinGain, props->MaxGain) * context->mParams.Gain; - GainTriplet DryGain{}; - DryGain.Base = minf(DryGainBase * props->Direct.Gain, GainMixMax); + DryGainBase = std::clamp(DryGainBase, props->MinGain, props->MaxGain) * context->mParams.Gain; + DryGain.Base = std::min(DryGainBase * props->Direct.Gain, GainMixMax); DryGain.HF = ConeHF * props->Direct.GainHF; DryGain.LF = props->Direct.GainLF; - GainTriplet WetGain[MAX_SENDS]{}; + + std::array WetGain{}; for(uint i{0};i < NumSends;i++) { - /* If this effect slot's Auxiliary Send Auto is off, then use the dry - * path distance and cone attenuation, otherwise use the wet (room) - * path distance and cone attenuation. The send filter is used instead - * of the direct filter, regardless. - */ - const bool use_room{!(UseDryAttnForRoom&(1u<Send[i].Gain, GainMixMax); - WetGain[i].HF = (use_room ? WetConeHF : ConeHF) * props->Send[i].GainHF; + const auto gain = std::clamp(WetGainBase[i]*WetCone, props->MinGain, props->MaxGain) * + context->mParams.Gain; + WetGain[i].Base = std::min(gain * props->Send[i].Gain, GainMixMax); + WetGain[i].HF = WetConeHF * props->Send[i].GainHF; WetGain[i].LF = props->Send[i].GainLF; } /* Distance-based air absorption and initial send decay. */ if(Distance > props->RefDistance) LIKELY { - const float distance_base{(Distance-props->RefDistance) * props->RolloffFactor}; - const float distance_meters{distance_base * context->mParams.MetersPerUnit}; - const float dryabsorb{distance_meters * props->AirAbsorptionFactor}; - if(dryabsorb > std::numeric_limits::epsilon()) - DryGain.HF *= std::pow(context->mParams.AirAbsorptionGainHF, dryabsorb); + /* FIXME: In keeping with EAX, the base air absorption gain should be + * taken from the reverb property in the "primary fx slot" when it has + * a reverb effect and the environment flag set, and be applied to the + * direct path and all environment sends, rather than each path using + * the air absorption gain associated with the given slot's effect. At + * this point in the mixer, and even in EFX itself, there's no concept + * of a "primary fx slot" so it's unclear which effect slot should be + * checked. + * + * The HF reference is also intended to be handled the same way, but + * again, there's no concept of a "primary fx slot" here and no way to + * know which effect slot to look at for the reference frequency. + */ + const auto distance_units = float{(Distance-props->RefDistance) * props->RolloffFactor}; + const auto distance_meters = float{distance_units * context->mParams.MetersPerUnit}; + const auto absorb = float{distance_meters * props->AirAbsorptionFactor}; + if(absorb > std::numeric_limits::epsilon()) + DryGain.HF *= std::pow(context->mParams.AirAbsorptionGainHF, absorb); /* If the source's Auxiliary Send Filter Gain Auto is off, no extra * adjustment is applied to the send gains. @@ -1591,72 +1705,25 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa if(!SendSlots[i] || !(SendSlots[i]->DecayTime > 0.0f)) continue; - auto calc_attenuation = [](float distance, float refdist, float rolloff) noexcept - { - const float dist{lerpf(refdist, distance, rolloff)}; - if(dist > refdist) return refdist / dist; - return 1.0f; - }; - - /* The reverb effect's room rolloff factor always applies to an - * inverse distance rolloff model. - */ - WetGain[i].Base *= calc_attenuation(Distance, props->RefDistance, - SendSlots[i]->RoomRolloff); + if(SendSlots[i]->AirAbsorptionGainHF < 1.0f + && absorb > std::numeric_limits::epsilon()) + WetGain[i].HF *= std::pow(SendSlots[i]->AirAbsorptionGainHF, absorb); - if(distance_meters > std::numeric_limits::epsilon()) - WetGain[i].HF *= std::pow(SendSlots[i]->AirAbsorptionGainHF, distance_meters); - - /* If this effect slot's Auxiliary Send Auto is off, don't apply - * the automatic initial reverb decay (should the reverb's room - * rolloff still apply?). - */ - if(!SendSlots[i]->AuxSendAuto) - continue; - - GainTriplet DecayDistance; - /* Calculate the distances to where this effect's decay reaches - * -60dB. - */ - DecayDistance.Base = SendSlots[i]->DecayTime * SpeedOfSoundMetersPerSec; - DecayDistance.LF = DecayDistance.Base * SendSlots[i]->DecayLFRatio; - DecayDistance.HF = DecayDistance.Base * SendSlots[i]->DecayHFRatio; - if(SendSlots[i]->DecayHFLimit) - { - const float airAbsorption{SendSlots[i]->AirAbsorptionGainHF}; - if(airAbsorption < 1.0f) - { - /* Calculate the distance to where this effect's air - * absorption reaches -60dB, and limit the effect's HF - * decay distance (so it doesn't take any longer to decay - * than the air would allow). - */ - static constexpr float log10_decaygain{-3.0f/*std::log10(ReverbDecayGain)*/}; - const float absorb_dist{log10_decaygain / std::log10(airAbsorption)}; - DecayDistance.HF = minf(absorb_dist, DecayDistance.HF); - } - } - - const float baseAttn = calc_attenuation(Distance, props->RefDistance, - props->RolloffFactor); + const float DecayDistance{SendSlots[i]->DecayTime * SpeedOfSoundMetersPerSec}; /* Apply a decay-time transformation to the wet path, based on the * source distance. The initial decay of the reverb effect is * calculated and applied to the wet path. + * + * FIXME: This is very likely not correct. It more likely should + * work by calculating a rolloff dynamically based on the reverb + * parameters (and source distance?) and add it to the room rolloff + * with the reverb and source rolloff parameters. */ - const float fact{distance_base / DecayDistance.Base}; + const float baseAttn{DryAttnBase}; + const float fact{distance_meters / DecayDistance}; const float gain{std::pow(ReverbDecayGain, fact)*(1.0f-baseAttn) + baseAttn}; WetGain[i].Base *= gain; - - if(gain > 0.0f) - { - const float hffact{distance_base / DecayDistance.HF}; - const float gainhf{std::pow(ReverbDecayGain, hffact)*(1.0f-baseAttn) + baseAttn}; - WetGain[i].HF *= minf(gainhf/gain, 1.0f); - const float lffact{distance_base / DecayDistance.LF}; - const float gainlf{std::pow(ReverbDecayGain, lffact)*(1.0f-baseAttn) + baseAttn}; - WetGain[i].LF *= minf(gainlf/gain, 1.0f); - } } } @@ -1703,7 +1770,7 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa if(Pitch > float{MaxPitch}) voice->mStep = MaxPitch<mStep = maxu(fastf2u(Pitch * MixerFracOne), 1); + voice->mStep = std::max(fastf2u(Pitch * MixerFracOne), 1u); voice->mResampler = PrepareResampler(props->mResampler, voice->mStep, &voice->mResampleState); float spread{0.0f}; @@ -1723,7 +1790,7 @@ void CalcSourceParams(Voice *voice, ContextBase *context, bool force) if(props) { - voice->mProps = *props; + voice->mProps = static_cast(*props); AtomicReplaceHead(context->mFreeVoiceProps, props); } @@ -1742,9 +1809,9 @@ void SendSourceStateEvent(ContextBase *context, uint id, VChangeState state) { RingBuffer *ring{context->mAsyncEvents.get()}; auto evt_vec = ring->getWriteVector(); - if(evt_vec.first.len < 1) return; + if(evt_vec[0].len < 1) return; - auto &evt = InitAsyncEvent(evt_vec.first.buf); + auto &evt = InitAsyncEvent(evt_vec[0].buf); evt.mId = id; switch(state) { @@ -1863,8 +1930,8 @@ void ProcessVoiceChanges(ContextBase *ctx) ctx->mCurrentVoiceChange.store(cur, std::memory_order_release); } -void ProcessParamUpdates(ContextBase *ctx, const EffectSlotArray &slots, - const al::span voices) +void ProcessParamUpdates(ContextBase *ctx, const al::span slots, + const al::span sorted_slots, const al::span voices) { ProcessVoiceChanges(ctx); @@ -1872,9 +1939,9 @@ void ProcessParamUpdates(ContextBase *ctx, const EffectSlotArray &slots, if(!ctx->mHoldUpdates.load(std::memory_order_acquire)) LIKELY { bool force{CalcContextParams(ctx)}; - auto sorted_slots = const_cast(slots.data() + slots.size()); + auto sorted_slot_base = al::to_address(sorted_slots.begin()); for(EffectSlot *slot : slots) - force |= CalcEffectSlotParams(slot, sorted_slots, ctx); + force |= CalcEffectSlotParams(slot, sorted_slot_base, ctx); for(Voice *voice : voices) { @@ -1890,142 +1957,137 @@ void ProcessContexts(DeviceBase *device, const uint SamplesToDo) { ASSUME(SamplesToDo > 0); - const nanoseconds curtime{device->ClockBase + - nanoseconds{seconds{device->SamplesDone}}/device->Frequency}; + const auto curtime = device->getClockTime(); - for(ContextBase *ctx : *device->mContexts.load(std::memory_order_acquire)) + auto proc_context = [SamplesToDo,curtime](ContextBase *ctx) { - const EffectSlotArray &auxslots = *ctx->mActiveAuxSlots.load(std::memory_order_acquire); - const al::span voices{ctx->getVoicesSpanAcquired()}; + const auto auxslotspan = al::span{*ctx->mActiveAuxSlots.load(std::memory_order_acquire)}; + const auto auxslots = auxslotspan.first(auxslotspan.size()>>1); + const auto sorted_slots = auxslotspan.last(auxslotspan.size()>>1); + const auto voices = ctx->getVoicesSpanAcquired(); /* Process pending property updates for objects on the context. */ - ProcessParamUpdates(ctx, auxslots, voices); + ProcessParamUpdates(ctx, auxslots, sorted_slots, voices); /* Clear auxiliary effect slot mixing buffers. */ - for(EffectSlot *slot : auxslots) + auto clear_wetbuffers = [](EffectSlot *slot) { - for(auto &buffer : slot->Wet.Buffer) - buffer.fill(0.0f); - } + auto clear_buffer = [](const FloatBufferSpan buffer) + { std::fill(buffer.begin(), buffer.end(), 0.0f); }; + std::for_each(slot->Wet.Buffer.begin(), slot->Wet.Buffer.end(), clear_buffer); + }; + std::for_each(auxslots.begin(), auxslots.end(), clear_wetbuffers); /* Process voices that have a playing source. */ - for(Voice *voice : voices) + auto proc_voice = [ctx,curtime,SamplesToDo](Voice *voice) { const Voice::State vstate{voice->mPlayState.load(std::memory_order_acquire)}; if(vstate != Voice::Stopped && vstate != Voice::Pending) voice->mix(vstate, ctx, curtime, SamplesToDo); - } + }; + std::for_each(voices.begin(), voices.end(), proc_voice); /* Process effects. */ - if(const size_t num_slots{auxslots.size()}) + if(!auxslots.empty()) { - auto slots = auxslots.data(); - auto slots_end = slots + num_slots; - /* Sort the slots into extra storage, so that effect slots come - * before their effect slot target (or their targets' target). + * before their effect slot target (or their targets' target). Skip + * sorting if it has already been done. */ - const al::span sorted_slots{const_cast(slots_end), - num_slots}; - /* Skip sorting if it has already been done. */ if(!sorted_slots[0]) { - /* First, copy the slots to the sorted list, then partition the - * sorted list so that all slots without a target slot go to - * the end. + /* First, copy the slots to the sorted list and partition them, + * so that all slots without a target slot go to the end. */ - std::copy(slots, slots_end, sorted_slots.begin()); - auto split_point = std::partition(sorted_slots.begin(), sorted_slots.end(), - [](const EffectSlot *slot) noexcept -> bool - { return slot->Target != nullptr; }); + auto has_target = [](const EffectSlot *slot) noexcept -> bool + { return slot->Target != nullptr; }; + auto split_point = std::partition_copy(auxslots.rbegin(), auxslots.rend(), + sorted_slots.begin(), sorted_slots.rbegin(), has_target).first; /* There must be at least one slot without a slot target. */ assert(split_point != sorted_slots.end()); - /* Simple case: no more than 1 slot has a target slot. Either - * all slots go right to the output, or the remaining one must - * target an already-partitioned slot. + /* Starting from the back of the sorted list, continue + * partitioning the front of the list given each target until + * all targets are accounted for. This ensures all slots + * without a target go last, all slots directly targeting those + * last slots go second-to-last, all slots directly targeting + * those second-last slots go third-to-last, etc. */ - if(split_point - sorted_slots.begin() > 1) + auto next_target = sorted_slots.end(); + while(std::distance(sorted_slots.begin(), split_point) > 1) { - /* At least two slots target other slots. Starting from the - * back of the sorted list, continue partitioning the front - * of the list given each target until all targets are - * accounted for. This ensures all slots without a target - * go last, all slots directly targeting those last slots - * go second-to-last, all slots directly targeting those - * second-last slots go third-to-last, etc. + /* This shouldn't happen, but if there's unsorted slots + * left that don't target any sorted slots, they can't + * contribute to the output, so leave them. */ - auto next_target = sorted_slots.end(); - do { - /* This shouldn't happen, but if there's unsorted slots - * left that don't target any sorted slots, they can't - * contribute to the output, so leave them. - */ - if(next_target == split_point) UNLIKELY - break; - - --next_target; - split_point = std::partition(sorted_slots.begin(), split_point, - [next_target](const EffectSlot *slot) noexcept -> bool - { return slot->Target != *next_target; }); - } while(split_point - sorted_slots.begin() > 1); + if(next_target == split_point) UNLIKELY + break; + + --next_target; + auto not_next = [next_target](const EffectSlot *slot) noexcept -> bool + { return slot->Target != *next_target; }; + split_point = std::partition(sorted_slots.begin(), split_point, not_next); } } - for(const EffectSlot *slot : sorted_slots) + auto proc_slot = [SamplesToDo](const EffectSlot *slot) { EffectState *state{slot->mEffectState.get()}; state->process(SamplesToDo, slot->Wet.Buffer, state->mOutTarget); - } + }; + std::for_each(sorted_slots.begin(), sorted_slots.end(), proc_slot); } /* Signal the event handler if there are any events to read. */ - RingBuffer *ring{ctx->mAsyncEvents.get()}; - if(ring->readSpace() > 0) + if(RingBuffer *ring{ctx->mAsyncEvents.get()}; ring->readSpace() > 0) ctx->mEventSem.post(); - } + }; + const auto contexts = al::span{*device->mContexts.load(std::memory_order_acquire)}; + std::for_each(contexts.begin(), contexts.end(), proc_context); } void ApplyDistanceComp(const al::span Samples, const size_t SamplesToDo, - const DistanceComp::ChanData *distcomp) + const al::span chandata) { ASSUME(SamplesToDo > 0); + auto distcomp = chandata.begin(); for(auto &chanbuffer : Samples) { const float gain{distcomp->Gain}; - const size_t base{distcomp->Length}; - float *distbuf{al::assume_aligned<16>(distcomp->Buffer)}; + auto distbuf = al::span{al::assume_aligned<16>(distcomp->Buffer.data()), + distcomp->Buffer.size()}; ++distcomp; - if(base < 1) - continue; + const size_t base{distbuf.size()}; + if(base < 1) continue; - float *inout{al::assume_aligned<16>(chanbuffer.data())}; - auto inout_end = inout + SamplesToDo; + const auto inout = al::span{al::assume_aligned<16>(chanbuffer.data()), SamplesToDo}; if(SamplesToDo >= base) LIKELY { - auto delay_end = std::rotate(inout, inout_end - base, inout_end); - std::swap_ranges(inout, delay_end, distbuf); + auto delay_end = std::rotate(inout.begin(), inout.end()-ptrdiff_t(base), inout.end()); + std::swap_ranges(inout.begin(), delay_end, distbuf.begin()); } else { - auto delay_start = std::swap_ranges(inout, inout_end, distbuf); - std::rotate(distbuf, delay_start, distbuf + base); + auto delay_start = std::swap_ranges(inout.begin(), inout.end(), distbuf.begin()); + std::rotate(distbuf.begin(), delay_start, distbuf.begin()+ptrdiff_t(base)); } - std::transform(inout, inout_end, inout, [gain](float s) { return s * gain; }); + std::transform(inout.begin(), inout.end(), inout.begin(), + [gain](float s) { return s*gain; }); } } void ApplyDither(const al::span Samples, uint *dither_seed, const float quant_scale, const size_t SamplesToDo) { + static constexpr double invRNGRange{1.0 / std::numeric_limits::max()}; ASSUME(SamplesToDo > 0); /* Dithering. Generate whitenoise (uniform distribution of random values * between -1 and +1) and add it to the sample values, after scaling up to - * the desired quantization depth amd before rounding. + * the desired quantization depth and before rounding. */ const float invscale{1.0f / quant_scale}; uint seed{*dither_seed}; @@ -2034,7 +2096,7 @@ void ApplyDither(const al::span Samples, uint *dither_seed, float val{sample * quant_scale}; uint rng0{dither_rng(&seed)}; uint rng1{dither_rng(&seed)}; - val += static_cast(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX)); + val += static_cast(rng0*invRNGRange - rng1*invRNGRange); return fast_roundf(val) * invscale; }; for(FloatBufferLine &inout : Samples) @@ -2058,12 +2120,12 @@ template<> inline int32_t SampleConv(float val) noexcept * When scaling and clamping for a signed 32-bit integer, these following * values are the best a float can give. */ - return fastf2i(clampf(val*2147483648.0f, -2147483648.0f, 2147483520.0f)); + return fastf2i(std::clamp(val*2147483648.0f, -2147483648.0f, 2147483520.0f)); } template<> inline int16_t SampleConv(float val) noexcept -{ return static_cast(fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f))); } +{ return static_cast(fastf2i(std::clamp(val*32768.0f, -32768.0f, 32767.0f))); } template<> inline int8_t SampleConv(float val) noexcept -{ return static_cast(fastf2i(clampf(val*128.0f, -128.0f, 127.0f))); } +{ return static_cast(fastf2i(std::clamp(val*128.0f, -128.0f, 127.0f))); } /* Define unsigned output variations. */ template<> inline uint32_t SampleConv(float val) noexcept @@ -2073,34 +2135,48 @@ template<> inline uint16_t SampleConv(float val) noexcept template<> inline uint8_t SampleConv(float val) noexcept { return static_cast(SampleConv(val) + 128); } -template +template void Write(const al::span InBuffer, void *OutBuffer, const size_t Offset, const size_t SamplesToDo, const size_t FrameStep) { ASSUME(FrameStep > 0); ASSUME(SamplesToDo > 0); - DevFmtType_t *outbase{static_cast*>(OutBuffer) + Offset*FrameStep}; + const auto output = al::span{static_cast(OutBuffer), (Offset+SamplesToDo)*FrameStep} + .subspan(Offset*FrameStep); size_t c{0}; for(const FloatBufferLine &inbuf : InBuffer) { - DevFmtType_t *out{outbase++}; - auto conv_sample = [FrameStep,&out](const float s) noexcept -> void + auto out = output.begin(); + auto conv_sample = [FrameStep,c,&out](const float s) noexcept { - *out = SampleConv>(s); - out += FrameStep; + out[c] = SampleConv(s); + out += ptrdiff_t(FrameStep); }; - std::for_each(inbuf.begin(), inbuf.begin()+SamplesToDo, conv_sample); + std::for_each_n(inbuf.cbegin(), SamplesToDo, conv_sample); ++c; } if(const size_t extra{FrameStep - c}) { - const auto silence = SampleConv>(0.0f); + const auto silence = SampleConv(0.0f); for(size_t i{0};i < SamplesToDo;++i) - { - std::fill_n(outbase, extra, silence); - outbase += FrameStep; - } + std::fill_n(&output[i*FrameStep + c], extra, silence); + } +} + +template +void Write(const al::span InBuffer, al::span OutBuffers, + const size_t Offset, const size_t SamplesToDo) +{ + ASSUME(SamplesToDo > 0); + + auto srcbuf = InBuffer.cbegin(); + for(auto *dstbuf : OutBuffers) + { + const auto src = al::span{*srcbuf}.first(SamplesToDo); + const auto dst = al::span{static_cast(dstbuf), Offset+SamplesToDo}.subspan(Offset); + std::transform(src.cbegin(), src.end(), dst.begin(), SampleConv); + ++srcbuf; } } @@ -2108,28 +2184,28 @@ void Write(const al::span InBuffer, void *OutBuffer, cons uint DeviceBase::renderSamples(const uint numSamples) { - const uint samplesToDo{minu(numSamples, BufferLineSize)}; + const uint samplesToDo{std::min(numSamples, uint{BufferLineSize})}; /* Clear main mixing buffers. */ for(FloatBufferLine &buffer : MixBuffer) buffer.fill(0.0f); - /* Increment the mix count at the start (lsb should now be 1). */ - IncrementRef(MixCount); - - /* Process and mix each context's sources and effects. */ - ProcessContexts(this, samplesToDo); + { + const auto mixLock = getWriteMixLock(); - /* Increment the clock time. Every second's worth of samples is converted - * and added to clock base so that large sample counts don't overflow - * during conversion. This also guarantees a stable conversion. - */ - SamplesDone += samplesToDo; - ClockBase += std::chrono::seconds{SamplesDone / Frequency}; - SamplesDone %= Frequency; + /* Process and mix each context's sources and effects. */ + ProcessContexts(this, samplesToDo); - /* Increment the mix count at the end (lsb should now be 0). */ - IncrementRef(MixCount); + /* Every second's worth of samples is converted and added to clock base + * so that large sample counts don't overflow during conversion. This + * also guarantees a stable conversion. + */ + auto samplesDone = mSamplesDone.load(std::memory_order_relaxed) + samplesToDo; + auto clockBaseSec = mClockBaseSec.load(std::memory_order_relaxed) + + seconds32{samplesDone/Frequency}; + mSamplesDone.store(samplesDone%Frequency, std::memory_order_relaxed); + mClockBaseSec.store(clockBaseSec, std::memory_order_relaxed); + } /* Apply any needed post-process for finalizing the Dry mix to the RealOut * (Ambisonic decode, UHJ encode, etc). @@ -2137,11 +2213,11 @@ uint DeviceBase::renderSamples(const uint numSamples) postProcess(samplesToDo); /* Apply compression, limiting sample amplitude if needed or desired. */ - if(Limiter) Limiter->process(samplesToDo, RealOut.Buffer.data()); + if(Limiter) Limiter->process(samplesToDo, RealOut.Buffer); /* Apply delays and attenuation for mismatched speaker distances. */ if(ChannelDelays) - ApplyDistanceComp(RealOut.Buffer, samplesToDo, ChannelDelays->mChannels.data()); + ApplyDistanceComp(RealOut.Buffer, samplesToDo, ChannelDelays->mChannels); /* Apply dithering. The compressor should have left enough headroom for the * dither noise to not saturate. @@ -2152,7 +2228,7 @@ uint DeviceBase::renderSamples(const uint numSamples) return samplesToDo; } -void DeviceBase::renderSamples(const al::span outBuffers, const uint numSamples) +void DeviceBase::renderSamples(const al::span outBuffers, const uint numSamples) { FPUCtl mixer_mode{}; uint total{0}; @@ -2160,12 +2236,19 @@ void DeviceBase::renderSamples(const al::span outBuffers, const uint num { const uint samplesToDo{renderSamples(todo)}; - auto *srcbuf = RealOut.Buffer.data(); - for(auto *dstbuf : outBuffers) + switch(FmtType) { - std::copy_n(srcbuf->data(), samplesToDo, dstbuf + total); - ++srcbuf; +#define HANDLE_WRITE(T) case T: \ + Write>(RealOut.Buffer, outBuffers, total, samplesToDo); break; + HANDLE_WRITE(DevFmtByte) + HANDLE_WRITE(DevFmtUByte) + HANDLE_WRITE(DevFmtShort) + HANDLE_WRITE(DevFmtUShort) + HANDLE_WRITE(DevFmtInt) + HANDLE_WRITE(DevFmtUInt) + HANDLE_WRITE(DevFmtFloat) } +#undef HANDLE_WRITE total += samplesToDo; } @@ -2187,7 +2270,7 @@ void DeviceBase::renderSamples(void *outBuffer, const uint numSamples, const siz switch(FmtType) { #define HANDLE_WRITE(T) case T: \ - Write(RealOut.Buffer, outBuffer, total, samplesToDo, frameStep); break; + Write>(RealOut.Buffer, outBuffer, total, samplesToDo, frameStep); break; HANDLE_WRITE(DevFmtByte) HANDLE_WRITE(DevFmtUByte) HANDLE_WRITE(DevFmtShort) @@ -2205,24 +2288,35 @@ void DeviceBase::renderSamples(void *outBuffer, const uint numSamples, const siz void DeviceBase::handleDisconnect(const char *msg, ...) { - IncrementRef(MixCount); + const auto mixLock = getWriteMixLock(); + if(Connected.exchange(false, std::memory_order_acq_rel)) { AsyncEvent evt{std::in_place_type}; auto &disconnect = std::get(evt); - va_list args; + /* NOLINTBEGIN(*-array-to-pointer-decay) */ + va_list args, args2; va_start(args, msg); - int msglen{vsnprintf(disconnect.msg, sizeof(disconnect.msg), msg, args)}; + va_copy(args2, args); + if(int msglen{vsnprintf(nullptr, 0, msg, args)}; msglen > 0) + { + disconnect.msg.resize(static_cast(msglen)+1_uz); + vsnprintf(disconnect.msg.data(), disconnect.msg.size(), msg, args2); + } + else + disconnect.msg = ""; + va_end(args2); va_end(args); + /* NOLINTEND(*-array-to-pointer-decay) */ - if(msglen < 0 || static_cast(msglen) >= sizeof(disconnect.msg)) - disconnect.msg[sizeof(disconnect.msg)-1] = 0; + while(!disconnect.msg.empty() && disconnect.msg.back() == '\0') + disconnect.msg.pop_back(); for(ContextBase *ctx : *mContexts.load()) { RingBuffer *ring{ctx->mAsyncEvents.get()}; - auto evt_data = ring->getWriteVector().first; + auto evt_data = ring->getWriteVector()[0]; if(evt_data.len > 0) { al::construct_at(reinterpret_cast(evt_data.buf), evt); @@ -2230,7 +2324,7 @@ void DeviceBase::handleDisconnect(const char *msg, ...) ctx->mEventSem.post(); } - if(!ctx->mStopVoicesOnDisconnect) + if(!ctx->mStopVoicesOnDisconnect.load()) { ProcessVoiceChanges(ctx); continue; @@ -2247,5 +2341,4 @@ void DeviceBase::handleDisconnect(const char *msg, ...) std::for_each(voicelist.begin(), voicelist.end(), stop_voice); } } - IncrementRef(MixCount); } diff --git a/3rdparty/openal/alc/alu.h b/3rdparty/openal/alc/alu.h index 33453487b1f4..6da8d9174064 100644 --- a/3rdparty/openal/alc/alu.h +++ b/3rdparty/openal/alc/alu.h @@ -2,20 +2,23 @@ #define ALU_H #include +#include #include -#include struct ALCcontext; struct ALCdevice; struct EffectSlot; -enum class StereoEncoding : uint8_t; +enum class StereoEncoding : std::uint8_t; +namespace al { +struct Device; +} // namespace al constexpr float GainMixMax{1000.0f}; /* +60dB */ -enum CompatFlags : uint8_t { +enum CompatFlags : std::uint8_t { ReverseX, ReverseY, ReverseZ, @@ -31,7 +34,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, std::optional stereomode); +void aluInitRenderer(al::Device *device, int hrtf_id, std::optional stereomode); void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context); diff --git a/3rdparty/openal/alc/backends/alsa.cpp b/3rdparty/openal/alc/backends/alsa.cpp index 0d9ff30dff63..d735c6d5d223 100644 --- a/3rdparty/openal/alc/backends/alsa.cpp +++ b/3rdparty/openal/alc/backends/alsa.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #include "alc/alconfig.h" #include "almalloc.h" #include "alnumeric.h" +#include "alstring.h" #include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" @@ -53,7 +55,9 @@ namespace { -constexpr char alsaDevice[] = "ALSA Default"; +using namespace std::string_view_literals; + +[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "ALSA Default"sv; } #ifdef HAVE_DYNLOAD @@ -252,55 +256,93 @@ std::vector PlaybackDevices; std::vector CaptureDevices; -const char *prefix_name(snd_pcm_stream_t stream) +std::string_view prefix_name(snd_pcm_stream_t stream) noexcept { - assert(stream == SND_PCM_STREAM_PLAYBACK || stream == SND_PCM_STREAM_CAPTURE); - return (stream==SND_PCM_STREAM_PLAYBACK) ? "device-prefix" : "capture-prefix"; + if(stream == SND_PCM_STREAM_PLAYBACK) + return "device-prefix"sv; + return "capture-prefix"sv; } +struct SndCtlCardInfo { + snd_ctl_card_info_t *mInfo{}; + + SndCtlCardInfo() { snd_ctl_card_info_malloc(&mInfo); } + ~SndCtlCardInfo() { if(mInfo) snd_ctl_card_info_free(mInfo); } + SndCtlCardInfo(const SndCtlCardInfo&) = delete; + SndCtlCardInfo& operator=(const SndCtlCardInfo&) = delete; + + [[nodiscard]] + operator snd_ctl_card_info_t*() const noexcept { return mInfo; } +}; + +struct SndPcmInfo { + snd_pcm_info_t *mInfo{}; + + SndPcmInfo() { snd_pcm_info_malloc(&mInfo); } + ~SndPcmInfo() { if(mInfo) snd_pcm_info_free(mInfo); } + SndPcmInfo(const SndPcmInfo&) = delete; + SndPcmInfo& operator=(const SndPcmInfo&) = delete; + + [[nodiscard]] + operator snd_pcm_info_t*() const noexcept { return mInfo; } +}; + +struct SndCtl { + snd_ctl_t *mHandle{}; + + SndCtl() = default; + ~SndCtl() { if(mHandle) snd_ctl_close(mHandle); } + SndCtl(const SndCtl&) = delete; + SndCtl& operator=(const SndCtl&) = delete; + + [[nodiscard]] + auto open(const char *name, int mode) { return snd_ctl_open(&mHandle, name, mode); } + + [[nodiscard]] + operator snd_ctl_t*() const noexcept { return mHandle; } +}; + + std::vector probe_devices(snd_pcm_stream_t stream) { std::vector devlist; - snd_ctl_card_info_t *info; - snd_ctl_card_info_malloc(&info); - snd_pcm_info_t *pcminfo; - snd_pcm_info_malloc(&pcminfo); + SndCtlCardInfo info; + SndPcmInfo pcminfo; - auto defname = ConfigValueStr(nullptr, "alsa", - (stream == SND_PCM_STREAM_PLAYBACK) ? "device" : "capture"); - devlist.emplace_back(alsaDevice, defname ? defname->c_str() : "default"); + auto defname = ConfigValueStr({}, "alsa"sv, + (stream == SND_PCM_STREAM_PLAYBACK) ? "device"sv : "capture"sv); + devlist.emplace_back(GetDefaultName(), defname ? std::string_view{*defname} : "default"sv); - if(auto customdevs = ConfigValueStr(nullptr, "alsa", - (stream == SND_PCM_STREAM_PLAYBACK) ? "custom-devices" : "custom-captures")) + if(auto customdevs = ConfigValueStr({}, "alsa"sv, + (stream == SND_PCM_STREAM_PLAYBACK) ? "custom-devices"sv : "custom-captures"sv)) { - size_t nextpos{customdevs->find_first_not_of(';')}; - size_t curpos; - while((curpos=nextpos) < customdevs->length()) + size_t curpos{customdevs->find_first_not_of(';')}; + while(curpos < customdevs->length()) { - nextpos = customdevs->find_first_of(';', curpos+1); - - size_t seppos{customdevs->find_first_of('=', curpos)}; + size_t nextpos{customdevs->find(';', curpos+1)}; + const size_t seppos{customdevs->find('=', curpos)}; if(seppos == curpos || seppos >= nextpos) { - std::string spec{customdevs->substr(curpos, nextpos-curpos)}; + const std::string spec{customdevs->substr(curpos, nextpos-curpos)}; ERR("Invalid ALSA device specification \"%s\"\n", spec.c_str()); } else { - devlist.emplace_back(customdevs->substr(curpos, seppos-curpos), - customdevs->substr(seppos+1, nextpos-seppos-1)); - const auto &entry = devlist.back(); + const std::string_view strview{*customdevs}; + const auto &entry = devlist.emplace_back(strview.substr(curpos, seppos-curpos), + strview.substr(seppos+1, nextpos-seppos-1)); TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str()); } if(nextpos < customdevs->length()) nextpos = customdevs->find_first_not_of(';', nextpos+1); + curpos = nextpos; } } - const std::string main_prefix{ - ConfigValueStr(nullptr, "alsa", prefix_name(stream)).value_or("plughw:")}; + const std::string main_prefix{ConfigValueStr({}, "alsa"sv, prefix_name(stream)) + .value_or("plughw:")}; int card{-1}; int err{snd_card_next(&card)}; @@ -308,16 +350,17 @@ std::vector probe_devices(snd_pcm_stream_t stream) { std::string name{"hw:" + std::to_string(card)}; - snd_ctl_t *handle; - if((err=snd_ctl_open(&handle, name.c_str(), 0)) < 0) + SndCtl handle; + err = handle.open(name.c_str(), 0); + if(err < 0) { ERR("control open (hw:%d): %s\n", card, snd_strerror(err)); continue; } - if((err=snd_ctl_card_info(handle, info)) < 0) + err = snd_ctl_card_info(handle, info); + if(err < 0) { ERR("control hardware info (hw:%d): %s\n", card, snd_strerror(err)); - snd_ctl_close(handle); continue; } @@ -326,8 +369,7 @@ std::vector probe_devices(snd_pcm_stream_t stream) name = prefix_name(stream); name += '-'; name += cardid; - const std::string card_prefix{ - ConfigValueStr(nullptr, "alsa", name.c_str()).value_or(main_prefix)}; + const std::string card_prefix{ConfigValueStr({}, "alsa"sv, name).value_or(main_prefix)}; int dev{-1}; while(true) @@ -339,7 +381,8 @@ std::vector probe_devices(snd_pcm_stream_t stream) snd_pcm_info_set_device(pcminfo, static_cast(dev)); snd_pcm_info_set_subdevice(pcminfo, 0); snd_pcm_info_set_stream(pcminfo, stream); - if((err=snd_ctl_pcm_info(handle, pcminfo)) < 0) + err = snd_ctl_pcm_info(handle, pcminfo); + if(err < 0) { if(err != -ENOENT) ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err)); @@ -352,8 +395,8 @@ std::vector probe_devices(snd_pcm_stream_t stream) name += cardid; name += '-'; name += std::to_string(dev); - const std::string device_prefix{ - ConfigValueStr(nullptr, "alsa", name.c_str()).value_or(card_prefix)}; + const std::string device_prefix{ConfigValueStr({}, "alsa"sv, name) + .value_or(card_prefix)}; /* "CardName, PcmName (CARD=cardid,DEV=dev)" */ name = cardname; @@ -372,18 +415,13 @@ std::vector probe_devices(snd_pcm_stream_t stream) device += ",DEV="; device += std::to_string(dev); - devlist.emplace_back(std::move(name), std::move(device)); - const auto &entry = devlist.back(); + const auto &entry = devlist.emplace_back(std::move(name), std::move(device)); TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str()); } - snd_ctl_close(handle); } if(err < 0) ERR("snd_card_next failed: %s\n", snd_strerror(err)); - snd_pcm_info_free(pcminfo); - snd_ctl_card_info_free(info); - return devlist; } @@ -392,7 +430,6 @@ int verify_state(snd_pcm_t *handle) { snd_pcm_state_t state{snd_pcm_state(handle)}; - int err; switch(state) { case SND_PCM_STATE_OPEN: @@ -405,15 +442,27 @@ int verify_state(snd_pcm_t *handle) break; case SND_PCM_STATE_XRUN: - if((err=snd_pcm_recover(handle, -EPIPE, 1)) < 0) + if(int err{snd_pcm_recover(handle, -EPIPE, 1)}; err < 0) return err; break; case SND_PCM_STATE_SUSPENDED: - if((err=snd_pcm_recover(handle, -ESTRPIPE, 1)) < 0) + if(int err{snd_pcm_recover(handle, -ESTRPIPE, 1)}; err < 0) return err; break; + case SND_PCM_STATE_DISCONNECTED: return -ENODEV; + + /* ALSA headers have made this enum public, leaving us in a bind: use + * the enum despite being private and internal to the libasound, or + * ignore when an enum value isn't handled. We can't rely on it being + * declared either, since older headers don't have it and it could be + * removed in the future. We can't even really rely on its value, since + * being private/internal means it's subject to change, but this is the + * best we can do. + */ + case 1024 /*SND_PCM_STATE_PRIVATE1*/: + assert(state != 1024); } return state; @@ -443,8 +492,6 @@ struct AlsaPlayback final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(AlsaPlayback) }; AlsaPlayback::~AlsaPlayback() @@ -458,7 +505,7 @@ AlsaPlayback::~AlsaPlayback() int AlsaPlayback::mixerProc() { SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); const snd_pcm_uframes_t update_size{mDevice->UpdateSize}; const snd_pcm_uframes_t buffer_size{mDevice->BufferSize}; @@ -506,7 +553,7 @@ int AlsaPlayback::mixerProc() avail -= avail%update_size; // it is possible that contiguous areas are smaller, thus we use a loop - std::lock_guard _{mMutex}; + std::lock_guard dlock{mMutex}; while(avail > 0) { snd_pcm_uframes_t frames{avail}; @@ -520,6 +567,7 @@ int AlsaPlayback::mixerProc() break; } + /* NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ char *WritePtr{static_cast(areas->addr) + (offset * areas->step / 8)}; mDevice->renderSamples(WritePtr, static_cast(frames), mFrameStep); @@ -541,7 +589,7 @@ int AlsaPlayback::mixerProc() int AlsaPlayback::mixerNoMMapProc() { SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); const snd_pcm_uframes_t update_size{mDevice->UpdateSize}; const snd_pcm_uframes_t buffer_size{mDevice->BufferSize}; @@ -585,13 +633,13 @@ int AlsaPlayback::mixerNoMMapProc() continue; } - std::byte *WritePtr{mBuffer.data()}; + auto WritePtr = mBuffer.begin(); avail = snd_pcm_bytes_to_frames(mPcmHandle, static_cast(mBuffer.size())); - std::lock_guard _{mMutex}; - mDevice->renderSamples(WritePtr, static_cast(avail), mFrameStep); + std::lock_guard dlock{mMutex}; + mDevice->renderSamples(al::to_address(WritePtr), static_cast(avail), mFrameStep); while(avail > 0) { - snd_pcm_sframes_t ret{snd_pcm_writei(mPcmHandle, WritePtr, + snd_pcm_sframes_t ret{snd_pcm_writei(mPcmHandle, al::to_address(WritePtr), static_cast(avail))}; switch(ret) { @@ -638,13 +686,13 @@ void AlsaPlayback::open(std::string_view 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", static_cast(name.length()), name.data()}; + "Device name \"%.*s\" not found", al::sizei(name), name.data()}; driver = iter->device_name; } else { - name = alsaDevice; - if(auto driveropt = ConfigValueStr(nullptr, "alsa", "device")) + name = GetDefaultName(); + if(auto driveropt = ConfigValueStr({}, "alsa"sv, "device"sv)) driver = std::move(driveropt).value(); } TRACE("Opening device \"%s\"\n", driver.c_str()); @@ -661,7 +709,7 @@ void AlsaPlayback::open(std::string_view name) /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */ snd_config_update_free_global(); - mDevice->DeviceName = name; + mDeviceName = name; } bool AlsaPlayback::reset() @@ -692,15 +740,14 @@ bool AlsaPlayback::reset() break; } - bool allowmmap{!!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "mmap", true)}; + bool allowmmap{GetConfigValueBool(mDevice->mDeviceName, "alsa"sv, "mmap"sv, true)}; uint periodLen{static_cast(mDevice->UpdateSize * 1000000_u64 / mDevice->Frequency)}; uint bufferLen{static_cast(mDevice->BufferSize * 1000000_u64 / mDevice->Frequency)}; uint rate{mDevice->Frequency}; - int err{}; HwParamsPtr hp{CreateHwParams()}; #define CHECK(x) do { \ - if((err=(x)) < 0) \ + if(int err{x}; err < 0) \ throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \ snd_strerror(err)}; \ } while(0) @@ -715,17 +762,18 @@ bool AlsaPlayback::reset() /* test and set format (implicitly sets sample bits) */ if(snd_pcm_hw_params_test_format(mPcmHandle, hp.get(), format) < 0) { - static const struct { + struct FormatMap { snd_pcm_format_t format; DevFmtType fmttype; - } formatlist[] = { - { SND_PCM_FORMAT_FLOAT, DevFmtFloat }, - { SND_PCM_FORMAT_S32, DevFmtInt }, - { SND_PCM_FORMAT_U32, DevFmtUInt }, - { SND_PCM_FORMAT_S16, DevFmtShort }, - { SND_PCM_FORMAT_U16, DevFmtUShort }, - { SND_PCM_FORMAT_S8, DevFmtByte }, - { SND_PCM_FORMAT_U8, DevFmtUByte }, + }; + static constexpr std::array formatlist{ + FormatMap{SND_PCM_FORMAT_FLOAT, DevFmtFloat }, + FormatMap{SND_PCM_FORMAT_S32, DevFmtInt }, + FormatMap{SND_PCM_FORMAT_U32, DevFmtUInt }, + FormatMap{SND_PCM_FORMAT_S16, DevFmtShort }, + FormatMap{SND_PCM_FORMAT_U16, DevFmtUShort}, + FormatMap{SND_PCM_FORMAT_S8, DevFmtByte }, + FormatMap{SND_PCM_FORMAT_U8, DevFmtUByte }, }; for(const auto &fmt : formatlist) @@ -750,7 +798,7 @@ bool AlsaPlayback::reset() else mDevice->FmtChans = DevFmtStereo; } /* set rate (implicitly constrains period/buffer parameters) */ - if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "allow-resampler", false) + if(!GetConfigValueBool(mDevice->mDeviceName, "alsa", "allow-resampler", false) || !mDevice->Flags.test(FrequencyRequest)) { if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp.get(), 0) < 0) @@ -760,10 +808,10 @@ bool AlsaPlayback::reset() WARN("Failed to enable ALSA resampler\n"); CHECK(snd_pcm_hw_params_set_rate_near(mPcmHandle, hp.get(), &rate, nullptr)); /* set period time (implicitly constrains period/buffer parameters) */ - if((err=snd_pcm_hw_params_set_period_time_near(mPcmHandle, hp.get(), &periodLen, nullptr)) < 0) + if(int err{snd_pcm_hw_params_set_period_time_near(mPcmHandle, hp.get(), &periodLen, nullptr)}; err < 0) ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err)); /* set buffer time (implicitly sets buffer size/bytes/time and period size/bytes) */ - if((err=snd_pcm_hw_params_set_buffer_time_near(mPcmHandle, hp.get(), &bufferLen, nullptr)) < 0) + if(int err{snd_pcm_hw_params_set_buffer_time_near(mPcmHandle, hp.get(), &bufferLen, nullptr)}; err < 0) ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err)); /* install and prepare hardware configuration */ CHECK(snd_pcm_hw_params(mPcmHandle, hp.get())); @@ -798,11 +846,10 @@ bool AlsaPlayback::reset() void AlsaPlayback::start() { - int err{}; snd_pcm_access_t access{}; HwParamsPtr hp{CreateHwParams()}; #define CHECK(x) do { \ - if((err=(x)) < 0) \ + if(int err{x}; err < 0) \ throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \ snd_strerror(err)}; \ } while(0) @@ -849,10 +896,9 @@ void AlsaPlayback::stop() ClockLatency AlsaPlayback::getClockLatency() { - ClockLatency ret; - - std::lock_guard _{mMutex}; - ret.ClockTime = GetDeviceClockTime(mDevice); + std::lock_guard dlock{mMutex}; + ClockLatency ret{}; + ret.ClockTime = mDevice->getClockTime(); snd_pcm_sframes_t delay{}; int err{snd_pcm_delay(mPcmHandle, &delay)}; if(err < 0) @@ -886,8 +932,6 @@ struct AlsaCapture final : public BackendBase { RingBufferPtr mRing{nullptr}; snd_pcm_sframes_t mLastAvail{0}; - - DEF_NEWDEL(AlsaCapture) }; AlsaCapture::~AlsaCapture() @@ -910,19 +954,18 @@ void AlsaCapture::open(std::string_view 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", static_cast(name.length()), name.data()}; + "Device name \"%.*s\" not found", al::sizei(name), name.data()}; driver = iter->device_name; } else { - name = alsaDevice; - if(auto driveropt = ConfigValueStr(nullptr, "alsa", "capture")) + name = GetDefaultName(); + if(auto driveropt = ConfigValueStr({}, "alsa"sv, "capture"sv)) driver = std::move(driveropt).value(); } TRACE("Opening device \"%s\"\n", driver.c_str()); - int err{snd_pcm_open(&mPcmHandle, driver.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)}; - if(err < 0) + if(int err{snd_pcm_open(&mPcmHandle, driver.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)}; err < 0) throw al::backend_exception{al::backend_error::NoDevice, "Could not open ALSA device \"%s\"", driver.c_str()}; @@ -955,13 +998,15 @@ void AlsaCapture::open(std::string_view name) break; } - snd_pcm_uframes_t bufferSizeInFrames{maxu(mDevice->BufferSize, 100*mDevice->Frequency/1000)}; - snd_pcm_uframes_t periodSizeInFrames{minu(mDevice->BufferSize, 25*mDevice->Frequency/1000)}; + snd_pcm_uframes_t bufferSizeInFrames{std::max(mDevice->BufferSize, + 100u*mDevice->Frequency/1000u)}; + snd_pcm_uframes_t periodSizeInFrames{std::min(mDevice->BufferSize, + 25u*mDevice->Frequency/1000u)}; bool needring{false}; HwParamsPtr hp{CreateHwParams()}; #define CHECK(x) do { \ - if((err=(x)) < 0) \ + if(int err{x}; err < 0) \ throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \ snd_strerror(err)}; \ } while(0) @@ -993,19 +1038,17 @@ void AlsaCapture::open(std::string_view name) if(needring) mRing = RingBuffer::Create(mDevice->BufferSize, mDevice->frameSizeFromFmt(), false); - mDevice->DeviceName = name; + mDeviceName = name; } void AlsaCapture::start() { - int err{snd_pcm_prepare(mPcmHandle)}; - if(err < 0) + if(int err{snd_pcm_prepare(mPcmHandle)}; err < 0) throw al::backend_exception{al::backend_error::DeviceError, "snd_pcm_prepare failed: %s", snd_strerror(err)}; - err = snd_pcm_start(mPcmHandle); - if(err < 0) + if(int err{snd_pcm_start(mPcmHandle)}; err < 0) throw al::backend_exception{al::backend_error::DeviceError, "snd_pcm_start failed: %s", snd_strerror(err)}; @@ -1029,8 +1072,7 @@ void AlsaCapture::stop() captureSamples(temp.data(), avail); mBuffer = std::move(temp); } - int err{snd_pcm_drop(mPcmHandle)}; - if(err < 0) + if(int err{snd_pcm_drop(mPcmHandle)}; err < 0) ERR("drop failed: %s\n", snd_strerror(err)); mDoCapture = false; } @@ -1039,10 +1081,13 @@ void AlsaCapture::captureSamples(std::byte *buffer, uint samples) { if(mRing) { - mRing->read(buffer, samples); + std::ignore = mRing->read(buffer, samples); return; } + const auto outspan = al::span{buffer, + static_cast(snd_pcm_frames_to_bytes(mPcmHandle, samples))}; + auto outiter = outspan.begin(); mLastAvail -= samples; while(mDevice->Connected.load(std::memory_order_acquire) && samples > 0) { @@ -1055,20 +1100,21 @@ void AlsaCapture::captureSamples(std::byte *buffer, uint samples) if(static_cast(amt) > samples) amt = samples; amt = snd_pcm_frames_to_bytes(mPcmHandle, amt); - std::copy_n(mBuffer.begin(), amt, buffer); + std::copy_n(mBuffer.begin(), amt, outiter); mBuffer.erase(mBuffer.begin(), mBuffer.begin()+amt); amt = snd_pcm_bytes_to_frames(mPcmHandle, amt); } else if(mDoCapture) - amt = snd_pcm_readi(mPcmHandle, buffer, samples); + amt = snd_pcm_readi(mPcmHandle, al::to_address(outiter), samples); if(amt < 0) { ERR("read error: %s\n", snd_strerror(static_cast(amt))); if(amt == -EAGAIN) continue; - if((amt=snd_pcm_recover(mPcmHandle, static_cast(amt), 1)) >= 0) + amt = snd_pcm_recover(mPcmHandle, static_cast(amt), 1); + if(amt >= 0) { amt = snd_pcm_start(mPcmHandle); if(amt >= 0) @@ -1088,11 +1134,11 @@ void AlsaCapture::captureSamples(std::byte *buffer, uint samples) continue; } - buffer = buffer + amt; + outiter += amt; samples -= static_cast(amt); } if(samples > 0) - std::fill_n(buffer, snd_pcm_frames_to_bytes(mPcmHandle, samples), + std::fill_n(outiter, snd_pcm_frames_to_bytes(mPcmHandle, samples), std::byte((mDevice->FmtType == DevFmtUByte) ? 0x80 : 0)); } @@ -1105,7 +1151,8 @@ uint AlsaCapture::availableSamples() { ERR("avail update failed: %s\n", snd_strerror(static_cast(avail))); - if((avail=snd_pcm_recover(mPcmHandle, static_cast(avail), 1)) >= 0) + avail = snd_pcm_recover(mPcmHandle, static_cast(avail), 1); + if(avail >= 0) { if(mDoCapture) avail = snd_pcm_start(mPcmHandle); @@ -1122,26 +1169,27 @@ uint AlsaCapture::availableSamples() if(!mRing) { - if(avail < 0) avail = 0; + avail = std::max(avail, 0); avail += snd_pcm_bytes_to_frames(mPcmHandle, static_cast(mBuffer.size())); - if(avail > mLastAvail) mLastAvail = avail; + mLastAvail = std::max(mLastAvail, avail); return static_cast(mLastAvail); } while(avail > 0) { auto vec = mRing->getWriteVector(); - if(vec.first.len == 0) break; + if(vec[0].len == 0) break; - snd_pcm_sframes_t amt{std::min(static_cast(vec.first.len), avail)}; - amt = snd_pcm_readi(mPcmHandle, vec.first.buf, static_cast(amt)); + snd_pcm_sframes_t amt{std::min(static_cast(vec[0].len), avail)}; + amt = snd_pcm_readi(mPcmHandle, vec[0].buf, static_cast(amt)); if(amt < 0) { ERR("read error: %s\n", snd_strerror(static_cast(amt))); if(amt == -EAGAIN) continue; - if((amt=snd_pcm_recover(mPcmHandle, static_cast(amt), 1)) >= 0) + amt = snd_pcm_recover(mPcmHandle, static_cast(amt), 1); + if(amt >= 0) { if(mDoCapture) amt = snd_pcm_start(mPcmHandle); @@ -1168,9 +1216,8 @@ uint AlsaCapture::availableSamples() ClockLatency AlsaCapture::getClockLatency() { - ClockLatency ret; - - ret.ClockTime = GetDeviceClockTime(mDevice); + ClockLatency ret{}; + ret.ClockTime = mDevice->getClockTime(); snd_pcm_sframes_t delay{}; int err{snd_pcm_delay(mPcmHandle, &delay)}; if(err < 0) @@ -1189,13 +1236,9 @@ ClockLatency AlsaCapture::getClockLatency() bool AlsaBackendFactory::init() { - bool error{false}; - #ifdef HAVE_DYNLOAD if(!alsa_handle) { - std::string missing_funcs; - alsa_handle = LoadLib("libasound.so.2"); if(!alsa_handle) { @@ -1203,52 +1246,47 @@ bool AlsaBackendFactory::init() return false; } - error = false; + std::string missing_funcs; #define LOAD_FUNC(f) do { \ - p##f = al::bit_cast(GetSymbol(alsa_handle, #f)); \ - if(p##f == nullptr) { \ - error = true; \ - missing_funcs += "\n" #f; \ - } \ + p##f = reinterpret_cast(GetSymbol(alsa_handle, #f)); \ + if(p##f == nullptr) missing_funcs += "\n" #f; \ } while(0) ALSA_FUNCS(LOAD_FUNC); #undef LOAD_FUNC - if(error) + if(!missing_funcs.empty()) { WARN("Missing expected functions:%s\n", missing_funcs.c_str()); CloseLib(alsa_handle); alsa_handle = nullptr; + return false; } } #endif - return !error; + return true; } bool AlsaBackendFactory::querySupport(BackendType type) { return (type == BackendType::Playback || type == BackendType::Capture); } -std::string AlsaBackendFactory::probe(BackendType type) +auto AlsaBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; - + std::vector outnames; auto add_device = [&outnames](const DevMap &entry) -> void - { - /* +1 to also append the null char (to ensure a null-separated list and - * double-null terminated list). - */ - outnames.append(entry.name.c_str(), entry.name.length()+1); - }; + { outnames.emplace_back(entry.name); }; + switch(type) { case BackendType::Playback: PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK); + outnames.reserve(PlaybackDevices.size()); std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device); break; case BackendType::Capture: CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE); + outnames.reserve(CaptureDevices.size()); std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device); break; } diff --git a/3rdparty/openal/alc/backends/alsa.h b/3rdparty/openal/alc/backends/alsa.h index b256dcf5a996..1327db8a4e82 100644 --- a/3rdparty/openal/alc/backends/alsa.h +++ b/3rdparty/openal/alc/backends/alsa.h @@ -5,15 +5,15 @@ struct AlsaBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_ALSA_H */ diff --git a/3rdparty/openal/alc/backends/base.cpp b/3rdparty/openal/alc/backends/base.cpp index ab3ad028d1d4..4f90fef1f543 100644 --- a/3rdparty/openal/alc/backends/base.cpp +++ b/3rdparty/openal/alc/backends/base.cpp @@ -7,16 +7,6 @@ #include #include -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#include - -#include "albit.h" -#include "core/logging.h" -#endif - -#include "atomic.h" #include "core/devformat.h" @@ -24,10 +14,12 @@ namespace al { backend_exception::backend_exception(backend_error code, const char *msg, ...) : mErrorCode{code} { + /* NOLINTBEGIN(*-array-to-pointer-decay) */ std::va_list args; va_start(args, msg); setMessage(msg, args); va_end(args); + /* NOLINTEND(*-array-to-pointer-decay) */ } backend_exception::~backend_exception() = default; @@ -45,27 +37,26 @@ uint BackendBase::availableSamples() ClockLatency BackendBase::getClockLatency() { - ClockLatency ret; + ClockLatency ret{}; uint refcount; do { refcount = mDevice->waitForMix(); - ret.ClockTime = GetDeviceClockTime(mDevice); + ret.ClockTime = mDevice->getClockTime(); std::atomic_thread_fence(std::memory_order_acquire); - } while(refcount != ReadRef(mDevice->MixCount)); + } while(refcount != mDevice->mMixCount.load(std::memory_order_relaxed)); /* NOTE: The device will generally have about all but one periods filled at * any given time during playback. Without a more accurate measurement from * the output, this is an okay approximation. */ - ret.Latency = std::max(std::chrono::seconds{mDevice->BufferSize-mDevice->UpdateSize}, - std::chrono::seconds::zero()); + ret.Latency = std::chrono::seconds{mDevice->BufferSize - mDevice->UpdateSize}; ret.Latency /= mDevice->Frequency; return ret; } -void BackendBase::setDefaultWFXChannelOrder() +void BackendBase::setDefaultWFXChannelOrder() const { mDevice->RealOut.ChannelIndex.fill(InvalidChannelIndex); @@ -125,6 +116,24 @@ void BackendBase::setDefaultWFXChannelOrder() mDevice->RealOut.ChannelIndex[TopBackLeft] = 10; mDevice->RealOut.ChannelIndex[TopBackRight] = 11; break; + case DevFmtX7144: + mDevice->RealOut.ChannelIndex[FrontLeft] = 0; + mDevice->RealOut.ChannelIndex[FrontRight] = 1; + mDevice->RealOut.ChannelIndex[FrontCenter] = 2; + mDevice->RealOut.ChannelIndex[LFE] = 3; + mDevice->RealOut.ChannelIndex[BackLeft] = 4; + mDevice->RealOut.ChannelIndex[BackRight] = 5; + mDevice->RealOut.ChannelIndex[SideLeft] = 6; + mDevice->RealOut.ChannelIndex[SideRight] = 7; + mDevice->RealOut.ChannelIndex[TopFrontLeft] = 8; + mDevice->RealOut.ChannelIndex[TopFrontRight] = 9; + mDevice->RealOut.ChannelIndex[TopBackLeft] = 10; + mDevice->RealOut.ChannelIndex[TopBackRight] = 11; + mDevice->RealOut.ChannelIndex[BottomFrontLeft] = 12; + mDevice->RealOut.ChannelIndex[BottomFrontRight] = 13; + mDevice->RealOut.ChannelIndex[BottomBackLeft] = 14; + mDevice->RealOut.ChannelIndex[BottomBackRight] = 15; + break; case DevFmtX3D71: mDevice->RealOut.ChannelIndex[FrontLeft] = 0; mDevice->RealOut.ChannelIndex[FrontRight] = 1; @@ -140,7 +149,7 @@ void BackendBase::setDefaultWFXChannelOrder() } } -void BackendBase::setDefaultChannelOrder() +void BackendBase::setDefaultChannelOrder() const { mDevice->RealOut.ChannelIndex.fill(InvalidChannelIndex); @@ -178,6 +187,24 @@ void BackendBase::setDefaultChannelOrder() mDevice->RealOut.ChannelIndex[TopBackLeft] = 10; mDevice->RealOut.ChannelIndex[TopBackRight] = 11; break; + case DevFmtX7144: + mDevice->RealOut.ChannelIndex[FrontLeft] = 0; + mDevice->RealOut.ChannelIndex[FrontRight] = 1; + mDevice->RealOut.ChannelIndex[BackLeft] = 2; + mDevice->RealOut.ChannelIndex[BackRight] = 3; + mDevice->RealOut.ChannelIndex[FrontCenter] = 4; + mDevice->RealOut.ChannelIndex[LFE] = 5; + mDevice->RealOut.ChannelIndex[SideLeft] = 6; + mDevice->RealOut.ChannelIndex[SideRight] = 7; + mDevice->RealOut.ChannelIndex[TopFrontLeft] = 8; + mDevice->RealOut.ChannelIndex[TopFrontRight] = 9; + mDevice->RealOut.ChannelIndex[TopBackLeft] = 10; + mDevice->RealOut.ChannelIndex[TopBackRight] = 11; + mDevice->RealOut.ChannelIndex[BottomFrontLeft] = 12; + mDevice->RealOut.ChannelIndex[BottomFrontRight] = 13; + mDevice->RealOut.ChannelIndex[BottomBackLeft] = 14; + mDevice->RealOut.ChannelIndex[BottomBackRight] = 15; + break; case DevFmtX3D71: mDevice->RealOut.ChannelIndex[FrontLeft] = 0; mDevice->RealOut.ChannelIndex[FrontRight] = 1; diff --git a/3rdparty/openal/alc/backends/base.h b/3rdparty/openal/alc/backends/base.h index a4079fe42064..0e82bb6b7fab 100644 --- a/3rdparty/openal/alc/backends/base.h +++ b/3rdparty/openal/alc/backends/base.h @@ -8,9 +8,11 @@ #include #include #include +#include #include "core/device.h" #include "core/except.h" +#include "alc/events.h" using uint = unsigned int; @@ -33,15 +35,22 @@ struct BackendBase { virtual ClockLatency getClockLatency(); DeviceBase *const mDevice; + std::string mDeviceName; + BackendBase() = delete; + BackendBase(const BackendBase&) = delete; + BackendBase(BackendBase&&) = delete; BackendBase(DeviceBase *device) noexcept : mDevice{device} { } virtual ~BackendBase() = default; + void operator=(const BackendBase&) = delete; + void operator=(BackendBase&&) = delete; + protected: /** Sets the default channel order used by most non-WaveFormatEx-based APIs. */ - void setDefaultChannelOrder(); + void setDefaultChannelOrder() const; /** Sets the default channel order used by WaveFormatEx. */ - void setDefaultWFXChannelOrder(); + void setDefaultWFXChannelOrder() const; }; using BackendPtr = std::unique_ptr; @@ -51,18 +60,6 @@ enum class BackendType { }; -/* Helper to get the current clock time from the device's ClockBase, and - * SamplesDone converted from the sample rate. - */ -inline std::chrono::nanoseconds GetDeviceClockTime(DeviceBase *device) -{ - using std::chrono::seconds; - using std::chrono::nanoseconds; - - auto ns = nanoseconds{seconds{device->SamplesDone}} / device->Frequency; - return device->ClockBase + ns; -} - /* Helper to get the device latency from the backend, including any fixed * latency from post-processing. */ @@ -75,16 +72,24 @@ inline ClockLatency GetClockLatency(DeviceBase *device, BackendBase *backend) struct BackendFactory { - virtual bool init() = 0; + BackendFactory() = default; + BackendFactory(const BackendFactory&) = delete; + BackendFactory(BackendFactory&&) = delete; + virtual ~BackendFactory() = default; - virtual bool querySupport(BackendType type) = 0; + void operator=(const BackendFactory&) = delete; + void operator=(BackendFactory&&) = delete; - virtual std::string probe(BackendType type) = 0; + virtual auto init() -> bool = 0; - virtual BackendPtr createBackend(DeviceBase *device, BackendType type) = 0; + virtual auto querySupport(BackendType type) -> bool = 0; -protected: - virtual ~BackendFactory() = default; + virtual auto queryEventSupport(alc::EventType, BackendType) -> alc::EventSupport + { return alc::EventSupport::NoSupport; } + + virtual auto enumerate(BackendType type) -> std::vector = 0; + + virtual auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr = 0; }; namespace al { @@ -99,15 +104,15 @@ class backend_exception final : public base_exception { backend_error mErrorCode; public: -#ifdef __USE_MINGW_ANSI_STDIO - [[gnu::format(gnu_printf, 3, 4)]] +#ifdef __MINGW32__ + [[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]] #else [[gnu::format(printf, 3, 4)]] #endif backend_exception(backend_error code, const char *msg, ...); ~backend_exception() override; - backend_error errorCode() const noexcept { return mErrorCode; } + [[nodiscard]] auto errorCode() const noexcept -> backend_error { return mErrorCode; } }; } // namespace al diff --git a/3rdparty/openal/alc/backends/coreaudio.cpp b/3rdparty/openal/alc/backends/coreaudio.cpp index 1684545b9665..2e103d4562db 100644 --- a/3rdparty/openal/alc/backends/coreaudio.cpp +++ b/3rdparty/openal/alc/backends/coreaudio.cpp @@ -24,7 +24,9 @@ #include #include +#include #include +#include #include #include #include @@ -32,14 +34,13 @@ #include #include #include -#include #include "alnumeric.h" +#include "alstring.h" #include "core/converter.h" #include "core/device.h" #include "core/logging.h" #include "ringbuffer.h" -#include "alc/events.h" #include #include @@ -56,6 +57,36 @@ namespace { constexpr auto OutputElement = 0; constexpr auto InputElement = 1; +// These following arrays should always be defined in ascending AudioChannelLabel value order +constexpr std::array MonoChanMap { kAudioChannelLabel_Mono }; +constexpr std::array StereoChanMap { kAudioChannelLabel_Left, kAudioChannelLabel_Right}; +constexpr std::array QuadChanMap { + kAudioChannelLabel_Left, kAudioChannelLabel_Right, + kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround +}; +constexpr std::array X51ChanMap { + kAudioChannelLabel_Left, kAudioChannelLabel_Right, + kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen, + kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround +}; +constexpr std::array X51RearChanMap { + kAudioChannelLabel_Left, kAudioChannelLabel_Right, + kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen, + kAudioChannelLabel_RearSurroundRight, kAudioChannelLabel_RearSurroundLeft +}; +constexpr std::array X61ChanMap { + kAudioChannelLabel_Left, kAudioChannelLabel_Right, + kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen, + kAudioChannelLabel_CenterSurround, + kAudioChannelLabel_RearSurroundRight, kAudioChannelLabel_RearSurroundLeft +}; +constexpr std::array X71ChanMap { + kAudioChannelLabel_Left, kAudioChannelLabel_Right, + kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen, + kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround, + kAudioChannelLabel_LeftCenter, kAudioChannelLabel_RightCenter +}; + struct FourCCPrinter { char mString[sizeof(UInt32) + 1]{}; @@ -159,7 +190,7 @@ std::string GetDeviceName(AudioDeviceID devId) /* Clear extraneous nul chars that may have been written with the name * string, and return it. */ - while(!devname.back()) + while(!devname.empty() && !devname.back()) devname.pop_back(); return devname; } @@ -167,7 +198,7 @@ std::string GetDeviceName(AudioDeviceID devId) UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture) { UInt32 propSize{}; - auto err = GetDevPropertySize(devId, kAudioDevicePropertyStreamConfiguration, isCapture, 0, + auto err = GetDevPropertySize(devId, kAudioUnitProperty_AudioChannelLayout, isCapture, 0, &propSize); if(err) { @@ -176,11 +207,11 @@ UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture) return 0; } - auto buflist_data = std::make_unique(propSize); - auto *buflist = reinterpret_cast(buflist_data.get()); + auto channel_data = std::make_unique(propSize); + auto *channel_layout = reinterpret_cast(channel_data.get()); - err = GetDevProperty(devId, kAudioDevicePropertyStreamConfiguration, isCapture, 0, propSize, - buflist); + err = GetDevProperty(devId, kAudioUnitProperty_AudioChannelLayout, isCapture, 0, propSize, + channel_layout); if(err) { ERR("kAudioDevicePropertyStreamConfiguration query failed: '%s' (%u)\n", @@ -188,11 +219,7 @@ UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture) return 0; } - UInt32 numChannels{0}; - for(size_t i{0};i < buflist->mNumberBuffers;++i) - numChannels += buflist->mBuffers[i].mNumberChannels; - - return numChannels; + return channel_layout->mNumberChannelDescriptions; } @@ -277,7 +304,7 @@ struct DeviceHelper { DeviceHelper() { AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice, - kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain}; + kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster}; OSStatus status = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil); if (status != noErr) ERR("AudioObjectAddPropertyListener fail: %d", status); @@ -285,7 +312,7 @@ struct DeviceHelper { ~DeviceHelper() { AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice, - kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain}; + kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster}; OSStatus status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil); if (status != noErr) ERR("AudioObjectRemovePropertyListener fail: %d", status); @@ -338,8 +365,6 @@ struct CoreAudioPlayback final : public BackendBase { uint mFrameSize{0u}; AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD - - DEF_NEWDEL(CoreAudioPlayback) }; CoreAudioPlayback::~CoreAudioPlayback() @@ -379,7 +404,7 @@ void CoreAudioPlayback::open(std::string_view 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", static_cast(name.length()), name.data()}; + "Device name \"%.*s\" not found", al::sizei(name), name.data()}; audioDevice = devmatch->mId; } @@ -388,7 +413,7 @@ void CoreAudioPlayback::open(std::string_view name) name = ca_device; else if(name != ca_device) throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + al::sizei(name), name.data()}; #endif /* open the default output unit */ @@ -437,7 +462,7 @@ void CoreAudioPlayback::open(std::string_view name) #if CAN_ENUMERATE if(!name.empty()) - mDevice->DeviceName = name; + mDeviceName = name; else { UInt32 propSize{sizeof(audioDevice)}; @@ -446,8 +471,8 @@ void CoreAudioPlayback::open(std::string_view name) kAudioUnitScope_Global, OutputElement, &audioDevice, &propSize); std::string devname{GetDeviceName(audioDevice)}; - if(!devname.empty()) mDevice->DeviceName = std::move(devname); - else mDevice->DeviceName = "Unknown Device Name"; + if(!devname.empty()) mDeviceName = std::move(devname); + else mDeviceName = "Unknown Device Name"; } if(audioDevice != kAudioDeviceUnknown) @@ -465,7 +490,7 @@ void CoreAudioPlayback::open(std::string_view name) } #else - mDevice->DeviceName = name; + mDeviceName = name; #endif } @@ -507,6 +532,22 @@ bool CoreAudioPlayback::reset() mDevice->Frequency = static_cast(streamFormat.mSampleRate); } + struct ChannelMap { + DevFmtChannels fmt; + al::span map; + bool is_51rear; + }; + + static constexpr std::array chanmaps{{ + { DevFmtX71, X71ChanMap, false }, + { DevFmtX61, X61ChanMap, false }, + { DevFmtX51, X51ChanMap, false }, + { DevFmtX51, X51RearChanMap, true }, + { DevFmtQuad, QuadChanMap, false }, + { DevFmtStereo, StereoChanMap, false }, + { DevFmtMono, MonoChanMap, false } + }}; + /* FIXME: How to tell what channels are what in the output device, and how * to specify what we're giving? e.g. 6.0 vs 5.1 */ @@ -555,6 +596,42 @@ bool CoreAudioPlayback::reset() return false; } + if(!mDevice->Flags.test(ChannelsRequest)) + { + auto propSize = UInt32{}; + auto writable = Boolean{}; + + err = AudioUnitGetPropertyInfo(mAudioUnit, kAudioUnitProperty_AudioChannelLayout, + kAudioUnitScope_Output, OutputElement, &propSize, &writable); + if(err == noErr) + { + auto layout_data = std::make_unique(propSize); + auto *layout = reinterpret_cast(layout_data.get()); + + err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_AudioChannelLayout, + kAudioUnitScope_Output, OutputElement, layout, &propSize); + if(err == noErr) + { + auto descs = al::span{std::data(layout->mChannelDescriptions), + layout->mNumberChannelDescriptions}; + auto labels = std::vector(descs.size()); + + std::transform(descs.begin(), descs.end(), labels.begin(), + std::mem_fn(&AudioChannelDescription::mChannelLabel)); + sort(labels.begin(), labels.end()); + + auto chaniter = std::find_if(chanmaps.cbegin(), chanmaps.cend(), + [&labels](const ChannelMap &chanmap) -> bool + { + return std::includes(labels.begin(), labels.end(), chanmap.map.begin(), + chanmap.map.end()); + }); + if(chaniter != chanmaps.cend()) + mDevice->FmtChans = chaniter->fmt; + } + } + } + setDefaultWFXChannelOrder(); /* setup callback */ @@ -624,8 +701,6 @@ struct CoreAudioCapture final : public BackendBase { std::vector mCaptureData; RingBufferPtr mRing{nullptr}; - - DEF_NEWDEL(CoreAudioCapture) }; CoreAudioCapture::~CoreAudioCapture() @@ -641,7 +716,7 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags, AudioBufferList*) noexcept { union { - std::byte _[maxz(sizeof(AudioBufferList), offsetof(AudioBufferList, mBuffers[1]))]; + std::byte buf[std::max(sizeof(AudioBufferList), offsetof(AudioBufferList, mBuffers[1]))]; AudioBufferList list; } audiobuf{}; @@ -658,7 +733,7 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags, return err; } - mRing->write(mCaptureData.data(), inNumberFrames); + std::ignore = mRing->write(mCaptureData.data(), inNumberFrames); return noErr; } @@ -680,7 +755,7 @@ void CoreAudioCapture::open(std::string_view 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", static_cast(name.length()), name.data()}; + "Device name \"%.*s\" not found", al::sizei(name), name.data()}; audioDevice = devmatch->mId; } @@ -689,7 +764,7 @@ void CoreAudioCapture::open(std::string_view name) name = ca_device; else if(name != ca_device) throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + al::sizei(name), name.data()}; #endif AudioComponentDescription desc{}; @@ -826,6 +901,7 @@ void CoreAudioCapture::open(std::string_view name) case DevFmtX61: case DevFmtX71: case DevFmtX714: + case DevFmtX7144: case DevFmtX3D71: case DevFmtAmbi3D: throw al::backend_exception{al::backend_error::DeviceError, "%s not supported", @@ -860,8 +936,8 @@ void CoreAudioCapture::open(std::string_view name) * conversion ring buffer. Ensure at least 100ms for the total buffer. */ double srateScale{outputFormat.mSampleRate / mDevice->Frequency}; - auto FrameCount64 = maxu64(static_cast(std::ceil(mDevice->BufferSize*srateScale)), - static_cast(outputFormat.mSampleRate)/10); + auto FrameCount64 = std::max(static_cast(std::ceil(mDevice->BufferSize*srateScale)), + static_cast(outputFormat.mSampleRate)/10_u64); FrameCount64 += MaxResamplerPadding; if(FrameCount64 > std::numeric_limits::max()) throw al::backend_exception{al::backend_error::DeviceError, @@ -877,7 +953,7 @@ void CoreAudioCapture::open(std::string_view name) mCaptureData.resize(outputFrameCount * mFrameSize); - outputFrameCount = static_cast(maxu64(outputFrameCount, FrameCount64)); + outputFrameCount = static_cast(std::max(uint64_t{outputFrameCount}, FrameCount64)); mRing = RingBuffer::Create(outputFrameCount, mFrameSize, false); /* Set up sample converter if needed */ @@ -888,7 +964,7 @@ void CoreAudioCapture::open(std::string_view name) #if CAN_ENUMERATE if(!name.empty()) - mDevice->DeviceName = name; + mDeviceName = name; else { UInt32 propSize{sizeof(audioDevice)}; @@ -897,11 +973,11 @@ void CoreAudioCapture::open(std::string_view name) kAudioUnitScope_Global, InputElement, &audioDevice, &propSize); std::string devname{GetDeviceName(audioDevice)}; - if(!devname.empty()) mDevice->DeviceName = std::move(devname); - else mDevice->DeviceName = "Unknown Device Name"; + if(!devname.empty()) mDeviceName = std::move(devname); + else mDeviceName = "Unknown Device Name"; } #else - mDevice->DeviceName = name; + mDeviceName = name; #endif } @@ -925,21 +1001,21 @@ void CoreAudioCapture::captureSamples(std::byte *buffer, uint samples) { if(!mConverter) { - mRing->read(buffer, samples); + std::ignore = mRing->read(buffer, samples); return; } auto rec_vec = mRing->getReadVector(); - const void *src0{rec_vec.first.buf}; - auto src0len = static_cast(rec_vec.first.len); + const void *src0{rec_vec[0].buf}; + auto src0len = static_cast(rec_vec[0].len); uint got{mConverter->convert(&src0, &src0len, buffer, samples)}; - size_t total_read{rec_vec.first.len - src0len}; - if(got < samples && !src0len && rec_vec.second.len > 0) + size_t total_read{rec_vec[0].len - src0len}; + if(got < samples && !src0len && rec_vec[1].len > 0) { - const void *src1{rec_vec.second.buf}; - auto src1len = static_cast(rec_vec.second.len); + const void *src1{rec_vec[1].buf}; + auto src1len = static_cast(rec_vec[1].len); got += mConverter->convert(&src1, &src1len, buffer + got*mFrameSize, samples-got); - total_read += rec_vec.second.len - src1len; + total_read += rec_vec[1].len - src1len; } mRing->readAdvance(total_read); @@ -970,23 +1046,23 @@ bool CoreAudioBackendFactory::init() bool CoreAudioBackendFactory::querySupport(BackendType type) { return type == BackendType::Playback || type == BackendType::Capture; } -std::string CoreAudioBackendFactory::probe(BackendType type) +auto CoreAudioBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; + std::vector outnames; #if CAN_ENUMERATE auto append_name = [&outnames](const DeviceEntry &entry) -> void - { - /* Includes null char. */ - outnames.append(entry.mName.c_str(), entry.mName.length()+1); - }; + { outnames.emplace_back(entry.mName); }; + switch(type) { case BackendType::Playback: EnumerateDevices(PlaybackList, false); + outnames.reserve(PlaybackList.size()); std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name); break; case BackendType::Capture: EnumerateDevices(CaptureList, true); + outnames.reserve(CaptureList.size()); std::for_each(CaptureList.cbegin(), CaptureList.cend(), append_name); break; } @@ -997,8 +1073,7 @@ std::string CoreAudioBackendFactory::probe(BackendType type) { case BackendType::Playback: case BackendType::Capture: - /* Includes null char. */ - outnames.append(ca_device, sizeof(ca_device)); + outnames.emplace_back(ca_device); break; } #endif @@ -1013,3 +1088,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/3rdparty/openal/alc/backends/coreaudio.h b/3rdparty/openal/alc/backends/coreaudio.h index 1252edde381c..26c2aaf98ad4 100644 --- a/3rdparty/openal/alc/backends/coreaudio.h +++ b/3rdparty/openal/alc/backends/coreaudio.h @@ -5,15 +5,17 @@ struct CoreAudioBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto queryEventSupport(alc::EventType eventType, BackendType type) -> alc::EventSupport final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - static BackendFactory &getFactory(); + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; + + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_COREAUDIO_H */ diff --git a/3rdparty/openal/alc/backends/dsound.cpp b/3rdparty/openal/alc/backends/dsound.cpp index b5596f1c8051..5e5221162e73 100644 --- a/3rdparty/openal/alc/backends/dsound.cpp +++ b/3rdparty/openal/alc/backends/dsound.cpp @@ -35,18 +35,17 @@ #include #include #include +#include +#include #include #include #include -#include -#include #include #include #include -#include "albit.h" -#include "alnumeric.h" #include "alspan.h" +#include "alstring.h" #include "althrd_setname.h" #include "comptr.h" #include "core/device.h" @@ -91,9 +90,6 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0 namespace { -#define DEVNAME_HEAD "OpenAL Soft on " - - #ifdef HAVE_DYNLOAD void *ds_handle; HRESULT (WINAPI *pDirectSoundCreate)(const GUID *pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter); @@ -147,7 +143,7 @@ BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR*, voi return TRUE; auto& devices = *static_cast*>(data); - const std::string basename{DEVNAME_HEAD + wstr_to_utf8(desc)}; + const auto basename = wstr_to_utf8(desc); int count{1}; std::string newname{basename}; @@ -191,8 +187,6 @@ struct DSoundPlayback final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(DSoundPlayback) }; DSoundPlayback::~DSoundPlayback() @@ -211,7 +205,7 @@ DSoundPlayback::~DSoundPlayback() FORCE_ALIGN int DSoundPlayback::mixerProc() { SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); DSBCAPS DSBCaps{}; DSBCaps.dwSize = sizeof(DSBCaps); @@ -307,12 +301,10 @@ void DSoundPlayback::open(std::string_view name) 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}; @@ -334,8 +326,7 @@ void DSoundPlayback::open(std::string_view 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", static_cast(name.length()), - name.data()}; + "Device name \"%.*s\" not found", al::sizei(name), name.data()}; } guid = &iter->guid; } @@ -362,7 +353,7 @@ void DSoundPlayback::open(std::string_view name) mPrimaryBuffer = nullptr; mDS = std::move(ds); - mDevice->DeviceName = name; + mDeviceName = name; } bool DSoundPlayback::reset() @@ -428,49 +419,53 @@ bool DSoundPlayback::reset() case DevFmtX51: OutputType.dwChannelMask = isRear51 ? X5DOT1REAR : X5DOT1; break; case DevFmtX61: OutputType.dwChannelMask = X6DOT1; break; case DevFmtX71: OutputType.dwChannelMask = X7DOT1; break; + case DevFmtX7144: mDevice->FmtChans = DevFmtX714; + /* fall-through */ case DevFmtX714: OutputType.dwChannelMask = X7DOT1DOT4; break; case DevFmtX3D71: OutputType.dwChannelMask = X7DOT1; break; } -retry_open: - hr = S_OK; - OutputType.Format.wFormatTag = WAVE_FORMAT_PCM; - OutputType.Format.nChannels = static_cast(mDevice->channelsFromFmt()); - OutputType.Format.wBitsPerSample = static_cast(mDevice->bytesFromFmt() * 8); - OutputType.Format.nBlockAlign = static_cast(OutputType.Format.nChannels * - OutputType.Format.wBitsPerSample / 8); - OutputType.Format.nSamplesPerSec = mDevice->Frequency; - OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec * - OutputType.Format.nBlockAlign; - OutputType.Format.cbSize = 0; - - if(OutputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat) - { - OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; - OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); - if(mDevice->FmtType == DevFmtFloat) - OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - else - OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + do { + hr = S_OK; + OutputType.Format.wFormatTag = WAVE_FORMAT_PCM; + OutputType.Format.nChannels = static_cast(mDevice->channelsFromFmt()); + OutputType.Format.wBitsPerSample = static_cast(mDevice->bytesFromFmt() * 8); + OutputType.Format.nBlockAlign = static_cast(OutputType.Format.nChannels * + OutputType.Format.wBitsPerSample / 8); + OutputType.Format.nSamplesPerSec = mDevice->Frequency; + OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec * + OutputType.Format.nBlockAlign; + OutputType.Format.cbSize = 0; - mPrimaryBuffer = nullptr; - } - else - { - if(SUCCEEDED(hr) && !mPrimaryBuffer) + if(OutputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat) { - DSBUFFERDESC DSBDescription{}; - DSBDescription.dwSize = sizeof(DSBDescription); - DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER; - hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mPrimaryBuffer), nullptr); + OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */ + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); + if(mDevice->FmtType == DevFmtFloat) + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + else + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + + mPrimaryBuffer = nullptr; + } + else + { + if(SUCCEEDED(hr) && !mPrimaryBuffer) + { + DSBUFFERDESC DSBDescription{}; + DSBDescription.dwSize = sizeof(DSBDescription); + DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER; + hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mPrimaryBuffer), nullptr); + } + if(SUCCEEDED(hr)) + hr = mPrimaryBuffer->SetFormat(&OutputType.Format); } - if(SUCCEEDED(hr)) - hr = mPrimaryBuffer->SetFormat(&OutputType.Format); - } - if(SUCCEEDED(hr)) - { + if(FAILED(hr)) + break; + uint num_updates{mDevice->BufferSize / mDevice->UpdateSize}; if(num_updates > MAX_UPDATES) num_updates = MAX_UPDATES; @@ -484,12 +479,10 @@ bool DSoundPlayback::reset() DSBDescription.lpwfxFormat = &OutputType.Format; hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mBuffer), nullptr); - if(FAILED(hr) && mDevice->FmtType == DevFmtFloat) - { - mDevice->FmtType = DevFmtShort; - goto retry_open; - } - } + if(SUCCEEDED(hr) || mDevice->FmtType != DevFmtFloat) + break; + mDevice->FmtType = DevFmtShort; + } while(FAILED(hr)); if(SUCCEEDED(hr)) { @@ -499,7 +492,7 @@ bool DSoundPlayback::reset() uint num_updates{mDevice->BufferSize / mDevice->UpdateSize}; assert(num_updates <= MAX_UPDATES); - std::array nots; + std::array nots{}; for(uint i{0};i < num_updates;++i) { nots[i].dwOffset = i * mDevice->UpdateSize * OutputType.Format.nBlockAlign; @@ -562,8 +555,6 @@ struct DSoundCapture final : public BackendBase { DWORD mCursor{0u}; RingBufferPtr mRing; - - DEF_NEWDEL(DSoundCapture) }; DSoundCapture::~DSoundCapture() @@ -583,12 +574,10 @@ void DSoundCapture::open(std::string_view name) 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}; @@ -610,8 +599,7 @@ void DSoundCapture::open(std::string_view 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", static_cast(name.length()), - name.data()}; + "Device name \"%.*s\" not found", al::sizei(name), name.data()}; } guid = &iter->guid; } @@ -642,6 +630,7 @@ void DSoundCapture::open(std::string_view name) case DevFmtX61: InputType.dwChannelMask = X6DOT1; break; case DevFmtX71: InputType.dwChannelMask = X7DOT1; break; case DevFmtX714: InputType.dwChannelMask = X7DOT1DOT4; break; + case DevFmtX7144: case DevFmtX3D71: case DevFmtAmbi3D: WARN("%s capture not supported\n", DevFmtChannelsString(mDevice->FmtChans)); @@ -658,6 +647,7 @@ void DSoundCapture::open(std::string_view name) InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec * InputType.Format.nBlockAlign; InputType.Format.cbSize = 0; + /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */ InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample; if(mDevice->FmtType == DevFmtFloat) InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; @@ -670,8 +660,7 @@ void DSoundCapture::open(std::string_view name) InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); } - uint samples{mDevice->BufferSize}; - samples = maxu(samples, 100 * mDevice->Frequency / 1000); + const uint samples{std::max(mDevice->BufferSize, mDevice->Frequency/10u)}; DSCBUFFERDESC DSCBDescription{}; DSCBDescription.dwSize = sizeof(DSCBDescription); @@ -699,7 +688,7 @@ void DSoundCapture::open(std::string_view name) mBufferBytes = DSCBDescription.dwBufferBytes; setDefaultWFXChannelOrder(); - mDevice->DeviceName = name; + mDeviceName = name; } void DSoundCapture::start() @@ -721,7 +710,7 @@ void DSoundCapture::stop() } void DSoundCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } uint DSoundCapture::availableSamples() { @@ -744,9 +733,9 @@ uint DSoundCapture::availableSamples() } if(SUCCEEDED(hr)) { - mRing->write(ReadPtr1, ReadCnt1/FrameSize); + std::ignore = mRing->write(ReadPtr1, ReadCnt1/FrameSize); if(ReadPtr2 != nullptr && ReadCnt2 > 0) - mRing->write(ReadPtr2, ReadCnt2/FrameSize); + std::ignore = mRing->write(ReadPtr2, ReadCnt2/FrameSize); hr = mDSCbuffer->Unlock(ReadPtr1, ReadCnt1, ReadPtr2, ReadCnt2); mCursor = ReadCursor; } @@ -782,7 +771,7 @@ bool DSoundBackendFactory::init() } #define LOAD_FUNC(f) do { \ - p##f = al::bit_cast(GetSymbol(ds_handle, #f)); \ + p##f = reinterpret_cast(GetSymbol(ds_handle, #f)); \ if(!p##f) \ { \ CloseLib(ds_handle); \ @@ -803,40 +792,32 @@ bool DSoundBackendFactory::init() bool DSoundBackendFactory::querySupport(BackendType type) { return (type == BackendType::Playback || type == BackendType::Capture); } -std::string DSoundBackendFactory::probe(BackendType type) +auto DSoundBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; + std::vector outnames; auto add_device = [&outnames](const DevMap &entry) -> void - { - /* +1 to also append the null char (to ensure a null-separated list and - * double-null terminated list). - */ - outnames.append(entry.name.c_str(), entry.name.length()+1); - }; + { outnames.emplace_back(entry.name); }; /* Initialize COM to prevent name truncation */ - HRESULT hr; - HRESULT hrcom{CoInitialize(nullptr)}; + ComWrapper com{}; switch(type) { case BackendType::Playback: PlaybackDevices.clear(); - hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices); - if(FAILED(hr)) + if(HRESULT hr{DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices)}; FAILED(hr)) ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr); + outnames.reserve(PlaybackDevices.size()); std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device); break; case BackendType::Capture: CaptureDevices.clear(); - hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices); - if(FAILED(hr)) + if(HRESULT hr{DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices)};FAILED(hr)) ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr); + outnames.reserve(CaptureDevices.size()); std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device); break; } - if(SUCCEEDED(hrcom)) - CoUninitialize(); return outnames; } diff --git a/3rdparty/openal/alc/backends/dsound.h b/3rdparty/openal/alc/backends/dsound.h index 787f227a0a20..33adbf2971fe 100644 --- a/3rdparty/openal/alc/backends/dsound.h +++ b/3rdparty/openal/alc/backends/dsound.h @@ -5,15 +5,15 @@ struct DSoundBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_DSOUND_H */ diff --git a/3rdparty/openal/alc/backends/jack.cpp b/3rdparty/openal/alc/backends/jack.cpp index a0a5c4402f72..209f7b597db4 100644 --- a/3rdparty/openal/alc/backends/jack.cpp +++ b/3rdparty/openal/alc/backends/jack.cpp @@ -32,10 +32,10 @@ #include #include -#include "albit.h" #include "alc/alconfig.h" #include "alnumeric.h" #include "alsem.h" +#include "alstring.h" #include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" @@ -49,6 +49,8 @@ namespace { +using namespace std::string_view_literals; + #ifdef HAVE_DYNLOAD #define JACK_FUNCS(MAGIC) \ MAGIC(jack_client_open); \ @@ -102,20 +104,16 @@ decltype(jack_error_callback) * pjack_error_callback; #endif -constexpr char JackDefaultAudioType[] = JACK_DEFAULT_AUDIO_TYPE; - jack_options_t ClientOptions = JackNullOption; bool jack_load() { - bool error{false}; - #ifdef HAVE_DYNLOAD if(!jack_handle) { - std::string missing_funcs; - -#ifdef _WIN32 +#if defined(_WIN64) +#define JACKLIB "libjack64.dll" +#elif defined(_WIN32) #define JACKLIB "libjack.dll" #else #define JACKLIB "libjack.so.0" @@ -127,48 +125,54 @@ bool jack_load() return false; } - error = false; + std::string missing_funcs; #define LOAD_FUNC(f) do { \ - p##f = al::bit_cast(GetSymbol(jack_handle, #f)); \ - if(p##f == nullptr) { \ - error = true; \ - missing_funcs += "\n" #f; \ - } \ + p##f = reinterpret_cast(GetSymbol(jack_handle, #f)); \ + if(p##f == nullptr) missing_funcs += "\n" #f; \ } while(0) JACK_FUNCS(LOAD_FUNC); #undef LOAD_FUNC /* Optional symbols. These don't exist in all versions of JACK. */ -#define LOAD_SYM(f) p##f = al::bit_cast(GetSymbol(jack_handle, #f)) +#define LOAD_SYM(f) p##f = reinterpret_cast(GetSymbol(jack_handle, #f)) LOAD_SYM(jack_error_callback); #undef LOAD_SYM - if(error) + if(!missing_funcs.empty()) { WARN("Missing expected functions:%s\n", missing_funcs.c_str()); CloseLib(jack_handle); jack_handle = nullptr; + return false; } } #endif - return !error; + return true; } struct JackDeleter { void operator()(void *ptr) { jack_free(ptr); } }; -using JackPortsPtr = std::unique_ptr; +using JackPortsPtr = std::unique_ptr; /* NOLINT(*-avoid-c-arrays) */ struct DeviceEntry { std::string mName; std::string mPattern; + DeviceEntry() = default; + DeviceEntry(const DeviceEntry&) = default; + DeviceEntry(DeviceEntry&&) = default; template DeviceEntry(T&& name, U&& pattern) : mName{std::forward(name)}, mPattern{std::forward(pattern)} { } + ~DeviceEntry(); + + DeviceEntry& operator=(const DeviceEntry&) = default; + DeviceEntry& operator=(DeviceEntry&&) = default; }; +DeviceEntry::~DeviceEntry() = default; std::vector PlaybackList; @@ -177,26 +181,22 @@ void EnumerateDevices(jack_client_t *client, std::vector &list) { std::remove_reference_t{}.swap(list); - if(JackPortsPtr ports{jack_get_ports(client, nullptr, JackDefaultAudioType, JackPortIsInput)}) + if(JackPortsPtr ports{jack_get_ports(client, nullptr, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput)}) { for(size_t i{0};ports[i];++i) { - const char *sep{std::strchr(ports[i], ':')}; - if(!sep || ports[i] == sep) continue; + const std::string_view portname{ports[i]}; + const size_t seppos{portname.find(':')}; + if(seppos == 0 || seppos >= portname.size()) + continue; - const al::span portdev{ports[i], sep}; + const auto portdev = portname.substr(0, seppos); auto check_name = [portdev](const DeviceEntry &entry) -> bool - { - const size_t len{portdev.size()}; - return entry.mName.length() == len - && entry.mName.compare(0, len, portdev.data(), len) == 0; - }; + { return entry.mName == portdev; }; if(std::find_if(list.cbegin(), list.cend(), check_name) != list.cend()) continue; - std::string name{portdev.data(), portdev.size()}; - list.emplace_back(name, name+":"); - const auto &entry = list.back(); + const auto &entry = list.emplace_back(portdev, std::string{portdev}+":"); TRACE("Got device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str()); } /* There are ports but couldn't get device names from them. Add a @@ -205,11 +205,11 @@ void EnumerateDevices(jack_client_t *client, std::vector &list) if(ports[0] && list.empty()) { WARN("No device names found in available ports, adding a generic name.\n"); - list.emplace_back("JACK", ""); + list.emplace_back("JACK"sv, ""sv); } } - if(auto listopt = ConfigValueStr(nullptr, "jack", "custom-devices")) + if(auto listopt = ConfigValueStr({}, "jack", "custom-devices")) { for(size_t strpos{0};strpos < listopt->size();) { @@ -217,38 +217,32 @@ void EnumerateDevices(jack_client_t *client, std::vector &list) size_t seppos{listopt->find('=', strpos)}; if(seppos >= nextpos || seppos == strpos) { - const std::string entry{listopt->substr(strpos, nextpos-strpos)}; - ERR("Invalid device entry: \"%s\"\n", entry.c_str()); + const auto entry = std::string_view{*listopt}.substr(strpos, nextpos-strpos); + ERR("Invalid device entry: \"%.*s\"\n", al::sizei(entry), entry.data()); if(nextpos != std::string::npos) ++nextpos; strpos = nextpos; continue; } - const al::span name{listopt->data()+strpos, seppos-strpos}; - const al::span pattern{listopt->data()+(seppos+1), - std::min(nextpos, listopt->size())-(seppos+1)}; + const auto name = std::string_view{*listopt}.substr(strpos, seppos-strpos); + const auto pattern = std::string_view{*listopt}.substr(seppos+1, + std::min(nextpos, listopt->size())-(seppos+1)); /* Check if this custom pattern already exists in the list. */ auto check_pattern = [pattern](const DeviceEntry &entry) -> bool - { - const size_t len{pattern.size()}; - return entry.mPattern.length() == len - && entry.mPattern.compare(0, len, pattern.data(), len) == 0; - }; + { return entry.mPattern == pattern; }; auto itemmatch = std::find_if(list.begin(), list.end(), check_pattern); if(itemmatch != list.end()) { /* If so, replace the name with this custom one. */ - itemmatch->mName.assign(name.data(), name.size()); + itemmatch->mName = name; TRACE("Customized device name: %s = %s\n", itemmatch->mName.c_str(), itemmatch->mPattern.c_str()); } else { /* Otherwise, add a new device entry. */ - list.emplace_back(std::string{name.data(), name.size()}, - std::string{pattern.data(), pattern.size()}); - const auto &entry = list.back(); + const auto &entry = list.emplace_back(name, pattern); TRACE("Got custom device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str()); } @@ -307,7 +301,7 @@ struct JackPlayback final : public BackendBase { std::string mPortPattern; jack_client_t *mClient{nullptr}; - std::array mPort{}; + std::array mPort{}; std::mutex mMutex; @@ -318,8 +312,6 @@ struct JackPlayback final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(JackPlayback) }; JackPlayback::~JackPlayback() @@ -339,22 +331,22 @@ JackPlayback::~JackPlayback() int JackPlayback::processRt(jack_nframes_t numframes) noexcept { - std::array out; - size_t numchans{0}; + auto outptrs = std::array{}; + auto numchans = size_t{0}; for(auto port : mPort) { if(!port || numchans == mDevice->RealOut.Buffer.size()) break; - out[numchans++] = static_cast(jack_port_get_buffer(port, numframes)); + outptrs[numchans++] = jack_port_get_buffer(port, numframes); } + const auto dst = al::span{outptrs}.first(numchans); if(mPlaying.load(std::memory_order_acquire)) LIKELY - mDevice->renderSamples({out.data(), numchans}, static_cast(numframes)); + mDevice->renderSamples(dst, static_cast(numframes)); else { - auto clear_buf = [numframes](float *outbuf) -> void - { std::fill_n(outbuf, numframes, 0.0f); }; - std::for_each(out.begin(), out.begin()+numchans, clear_buf); + std::for_each(dst.begin(), dst.end(), [numframes](void *outbuf) -> void + { std::fill_n(static_cast(outbuf), numframes, 0.0f); }); } return 0; @@ -363,53 +355,46 @@ int JackPlayback::processRt(jack_nframes_t numframes) noexcept int JackPlayback::process(jack_nframes_t numframes) noexcept { - std::array out; + std::array,MaxOutputChannels> out; size_t numchans{0}; for(auto port : mPort) { if(!port) break; - out[numchans++] = static_cast(jack_port_get_buffer(port, numframes)); + out[numchans++] = {static_cast(jack_port_get_buffer(port, numframes)), numframes}; } - jack_nframes_t total{0}; + size_t total{0}; if(mPlaying.load(std::memory_order_acquire)) LIKELY { auto data = mRing->getReadVector(); - jack_nframes_t todo{minu(numframes, static_cast(data.first.len))}; - auto write_first = [&data,numchans,todo](float *outbuf) -> float* + const auto update_size = size_t{mDevice->UpdateSize}; + + const auto outlen = size_t{numframes / update_size}; + const auto len1 = size_t{std::min(data[0].len/update_size, outlen)}; + const auto len2 = size_t{std::min(data[1].len/update_size, outlen-len1)}; + + auto src = al::span{reinterpret_cast(data[0].buf), update_size*len1*numchans}; + for(size_t i{0};i < len1;++i) { - const float *RESTRICT in = reinterpret_cast(data.first.buf); - auto deinterlace_input = [&in,numchans]() noexcept -> float + for(size_t c{0};c < numchans;++c) { - float ret{*in}; - in += numchans; - return ret; - }; - std::generate_n(outbuf, todo, deinterlace_input); - data.first.buf += sizeof(float); - return outbuf + todo; - }; - std::transform(out.begin(), out.begin()+numchans, out.begin(), write_first); - total += todo; - - todo = minu(numframes-total, static_cast(data.second.len)); - if(todo > 0) + const auto iter = std::copy_n(src.begin(), update_size, out[c].begin()); + out[c] = {iter, out[c].end()}; + src = src.subspan(update_size); + } + total += update_size; + } + + src = al::span{reinterpret_cast(data[1].buf), update_size*len2*numchans}; + for(size_t i{0};i < len2;++i) { - auto write_second = [&data,numchans,todo](float *outbuf) -> float* + for(size_t c{0};c < numchans;++c) { - const float *RESTRICT in = reinterpret_cast(data.second.buf); - auto deinterlace_input = [&in,numchans]() noexcept -> float - { - float ret{*in}; - in += numchans; - return ret; - }; - std::generate_n(outbuf, todo, deinterlace_input); - data.second.buf += sizeof(float); - return outbuf + todo; - }; - std::transform(out.begin(), out.begin()+numchans, out.begin(), write_second); - total += todo; + const auto iter = std::copy_n(src.begin(), update_size, out[c].begin()); + out[c] = {iter, out[c].end()}; + src = src.subspan(update_size); + } + total += update_size; } mRing->readAdvance(total); @@ -418,8 +403,8 @@ int JackPlayback::process(jack_nframes_t numframes) noexcept if(numframes > total) { - const jack_nframes_t todo{numframes - total}; - auto clear_buf = [todo](float *outbuf) -> void { std::fill_n(outbuf, todo, 0.0f); }; + auto clear_buf = [](const al::span outbuf) -> void + { std::fill(outbuf.begin(), outbuf.end(), 0.0f); }; std::for_each(out.begin(), out.begin()+numchans, clear_buf); } @@ -429,31 +414,54 @@ int JackPlayback::process(jack_nframes_t numframes) noexcept int JackPlayback::mixerProc() { SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); - const size_t frame_step{mDevice->channelsFromFmt()}; + const auto update_size = uint{mDevice->UpdateSize}; + const auto num_channels = size_t{mDevice->channelsFromFmt()}; + auto outptrs = std::vector(num_channels); while(!mKillNow.load(std::memory_order_acquire) && mDevice->Connected.load(std::memory_order_acquire)) { - if(mRing->writeSpace() < mDevice->UpdateSize) + if(mRing->writeSpace() < update_size) { mSem.wait(); continue; } auto data = mRing->getWriteVector(); - size_t todo{data.first.len + data.second.len}; - todo -= todo%mDevice->UpdateSize; + const auto len1 = size_t{data[0].len / update_size}; + const auto len2 = size_t{data[1].len / update_size}; - const auto len1 = static_cast(minz(data.first.len, todo)); - const auto len2 = static_cast(minz(data.second.len, todo-len1)); - - std::lock_guard _{mMutex}; - mDevice->renderSamples(data.first.buf, len1, frame_step); + std::lock_guard dlock{mMutex}; + auto buffer = al::span{reinterpret_cast(data[0].buf), data[0].len*num_channels}; + auto bufiter = buffer.begin(); + for(size_t i{0};i < len1;++i) + { + std::generate_n(outptrs.begin(), outptrs.size(), [&bufiter,update_size] + { + auto ret = al::to_address(bufiter); + bufiter += ptrdiff_t(update_size); + return ret; + }); + mDevice->renderSamples(outptrs, update_size); + } if(len2 > 0) - mDevice->renderSamples(data.second.buf, len2, frame_step); - mRing->writeAdvance(todo); + { + buffer = al::span{reinterpret_cast(data[1].buf), data[1].len*num_channels}; + bufiter = buffer.begin(); + for(size_t i{0};i < len2;++i) + { + std::generate_n(outptrs.begin(), outptrs.size(), [&bufiter,update_size] + { + auto ret = al::to_address(bufiter); + bufiter += ptrdiff_t(update_size); + return ret; + }); + mDevice->renderSamples(outptrs, update_size); + } + } + mRing->writeAdvance((len1+len2) * update_size); } return 0; @@ -467,7 +475,7 @@ void JackPlayback::open(std::string_view name) const PathNamePair &binname = GetProcBinary(); const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()}; - jack_status_t status; + jack_status_t status{}; mClient = jack_client_open(client_name, ClientOptions, &status, nullptr); if(mClient == nullptr) throw al::backend_exception{al::backend_error::DeviceError, @@ -496,11 +504,11 @@ void JackPlayback::open(std::string_view 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", static_cast(name.length()), name.data()}; + "Device name \"%.*s\" not found", al::sizei(name), name.data()}; mPortPattern = iter->mPattern; } - mDevice->DeviceName = name; + mDeviceName = name; } bool JackPlayback::reset() @@ -510,7 +518,7 @@ 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); + mRTMixing = GetConfigValueBool(mDevice->mDeviceName, "jack", "rt-mix", true); jack_set_process_callback(mClient, mRTMixing ? &JackPlayback::processRtC : &JackPlayback::processC, this); @@ -528,9 +536,10 @@ bool JackPlayback::reset() } else { - const char *devname{mDevice->DeviceName.c_str()}; - uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)}; - bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize); + const auto devname = std::string_view{mDevice->mDeviceName}; + auto bufsize = ConfigValueUInt(devname, "jack", "buffer-size") + .value_or(mDevice->UpdateSize); + bufsize = std::max(NextPowerOf2(bufsize), mDevice->UpdateSize); mDevice->BufferSize = bufsize + mDevice->UpdateSize; } @@ -538,27 +547,27 @@ bool JackPlayback::reset() mDevice->FmtType = DevFmtFloat; int port_num{0}; - auto ports_end = mPort.begin() + mDevice->channelsFromFmt(); - auto bad_port = mPort.begin(); - while(bad_port != ports_end) + auto ports = al::span{mPort}.first(mDevice->channelsFromFmt()); + auto bad_port = ports.begin(); + while(bad_port != ports.end()) { std::string name{"channel_" + std::to_string(++port_num)}; - *bad_port = jack_port_register(mClient, name.c_str(), JackDefaultAudioType, + *bad_port = jack_port_register(mClient, name.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsTerminal, 0); if(!*bad_port) break; ++bad_port; } - if(bad_port != ports_end) + if(bad_port != ports.end()) { ERR("Failed to register enough JACK ports for %s output\n", DevFmtChannelsString(mDevice->FmtChans)); - if(bad_port == mPort.begin()) return false; + if(bad_port == ports.begin()) return false; - if(bad_port == mPort.begin()+1) + if(bad_port == ports.begin()+1) mDevice->FmtChans = DevFmtMono; else { - ports_end = mPort.begin()+2; + const auto ports_end = ports.begin()+2; while(bad_port != ports_end) { jack_port_unregister(mClient, *(--bad_port)); @@ -578,10 +587,10 @@ void JackPlayback::start() if(jack_activate(mClient)) throw al::backend_exception{al::backend_error::DeviceError, "Failed to activate client"}; - const char *devname{mDevice->DeviceName.c_str()}; + const auto devname = std::string_view{mDevice->mDeviceName}; if(ConfigValueBool(devname, "jack", "connect-ports").value_or(true)) { - JackPortsPtr pnames{jack_get_ports(mClient, mPortPattern.c_str(), JackDefaultAudioType, + JackPortsPtr pnames{jack_get_ports(mClient, mPortPattern.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput)}; if(!pnames) { @@ -616,7 +625,7 @@ void JackPlayback::start() else { uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)}; - bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize); + bufsize = std::max(NextPowerOf2(bufsize), mDevice->UpdateSize); mDevice->BufferSize = bufsize + mDevice->UpdateSize; mRing = RingBuffer::Create(bufsize, mDevice->frameSizeFromFmt(), true); @@ -654,10 +663,9 @@ void JackPlayback::stop() ClockLatency JackPlayback::getClockLatency() { - ClockLatency ret; - - std::lock_guard _{mMutex}; - ret.ClockTime = GetDeviceClockTime(mDevice); + std::lock_guard dlock{mMutex}; + ClockLatency ret{}; + ret.ClockTime = mDevice->getClockTime(); ret.Latency = std::chrono::seconds{mRing ? mRing->readSpace() : mDevice->UpdateSize}; ret.Latency /= mDevice->Frequency; @@ -677,7 +685,7 @@ bool JackBackendFactory::init() if(!jack_load()) return false; - if(!GetConfigValueBool(nullptr, "jack", "spawn-server", false)) + if(!GetConfigValueBool({}, "jack", "spawn-server", false)) ClientOptions = static_cast(ClientOptions | JackNoStartServer); const PathNamePair &binname = GetProcBinary(); @@ -685,7 +693,7 @@ bool JackBackendFactory::init() void (*old_error_cb)(const char*){&jack_error_callback ? jack_error_callback : nullptr}; jack_set_error_function(jack_msg_handler); - jack_status_t status; + jack_status_t status{}; jack_client_t *client{jack_client_open(client_name, ClientOptions, &status, nullptr)}; jack_set_error_function(old_error_cb); if(!client) @@ -703,18 +711,15 @@ bool JackBackendFactory::init() bool JackBackendFactory::querySupport(BackendType type) { return (type == BackendType::Playback); } -std::string JackBackendFactory::probe(BackendType type) +auto JackBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; + std::vector outnames; auto append_name = [&outnames](const DeviceEntry &entry) -> void - { - /* Includes null char. */ - outnames.append(entry.mName.c_str(), entry.mName.length()+1); - }; + { outnames.emplace_back(entry.mName); }; const PathNamePair &binname = GetProcBinary(); const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()}; - jack_status_t status; + jack_status_t status{}; switch(type) { case BackendType::Playback: @@ -725,6 +730,7 @@ std::string JackBackendFactory::probe(BackendType type) } else WARN("jack_client_open() failed, 0x%02x\n", status); + outnames.reserve(PlaybackList.size()); std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name); break; case BackendType::Capture: diff --git a/3rdparty/openal/alc/backends/jack.h b/3rdparty/openal/alc/backends/jack.h index b83f24dda16f..1e4c9f05b1f4 100644 --- a/3rdparty/openal/alc/backends/jack.h +++ b/3rdparty/openal/alc/backends/jack.h @@ -5,15 +5,15 @@ struct JackBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_JACK_H */ diff --git a/3rdparty/openal/alc/backends/loopback.cpp b/3rdparty/openal/alc/backends/loopback.cpp index 2972fc0145d1..00824f306339 100644 --- a/3rdparty/openal/alc/backends/loopback.cpp +++ b/3rdparty/openal/alc/backends/loopback.cpp @@ -34,14 +34,12 @@ struct LoopbackBackend final : public BackendBase { bool reset() override; void start() override; void stop() override; - - DEF_NEWDEL(LoopbackBackend) }; void LoopbackBackend::open(std::string_view name) { - mDevice->DeviceName = name; + mDeviceName = name; } bool LoopbackBackend::reset() @@ -65,8 +63,8 @@ bool LoopbackBackendFactory::init() bool LoopbackBackendFactory::querySupport(BackendType) { return true; } -std::string LoopbackBackendFactory::probe(BackendType) -{ return std::string{}; } +auto LoopbackBackendFactory::enumerate(BackendType) -> std::vector +{ return {}; } BackendPtr LoopbackBackendFactory::createBackend(DeviceBase *device, BackendType) { return BackendPtr{new LoopbackBackend{device}}; } diff --git a/3rdparty/openal/alc/backends/loopback.h b/3rdparty/openal/alc/backends/loopback.h index cb42b3c8e81b..876a052cb566 100644 --- a/3rdparty/openal/alc/backends/loopback.h +++ b/3rdparty/openal/alc/backends/loopback.h @@ -5,15 +5,15 @@ struct LoopbackBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_LOOPBACK_H */ diff --git a/3rdparty/openal/alc/backends/null.cpp b/3rdparty/openal/alc/backends/null.cpp index 3c68e4cec9a8..be04c58a36ee 100644 --- a/3rdparty/openal/alc/backends/null.cpp +++ b/3rdparty/openal/alc/backends/null.cpp @@ -30,8 +30,8 @@ #include #include +#include "alstring.h" #include "althrd_setname.h" -#include "almalloc.h" #include "core/device.h" #include "core/helpers.h" @@ -41,8 +41,9 @@ namespace { using std::chrono::seconds; using std::chrono::milliseconds; using std::chrono::nanoseconds; +using namespace std::string_view_literals; -constexpr char nullDevice[] = "No Output"; +[[nodiscard]] constexpr auto GetDeviceName() noexcept { return "No Output"sv; } struct NullBackend final : public BackendBase { @@ -57,8 +58,6 @@ struct NullBackend final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(NullBackend) }; int NullBackend::mixerProc() @@ -66,7 +65,7 @@ int NullBackend::mixerProc() const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2}; SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); int64_t done{0}; auto start = std::chrono::steady_clock::now(); @@ -108,12 +107,12 @@ int NullBackend::mixerProc() void NullBackend::open(std::string_view name) { if(name.empty()) - name = nullDevice; - else if(name != nullDevice) + name = GetDeviceName(); + else if(name != GetDeviceName()) throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + al::sizei(name), name.data()}; - mDevice->DeviceName = name; + mDeviceName = name; } bool NullBackend::reset() @@ -150,19 +149,17 @@ bool NullBackendFactory::init() bool NullBackendFactory::querySupport(BackendType type) { return (type == BackendType::Playback); } -std::string NullBackendFactory::probe(BackendType type) +auto NullBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; switch(type) { case BackendType::Playback: - /* Includes null char. */ - outnames.append(nullDevice, sizeof(nullDevice)); - break; + /* Include null char. */ + return std::vector{std::string{GetDeviceName()}}; case BackendType::Capture: break; } - return outnames; + return {}; } BackendPtr NullBackendFactory::createBackend(DeviceBase *device, BackendType type) diff --git a/3rdparty/openal/alc/backends/null.h b/3rdparty/openal/alc/backends/null.h index 7048cad6f09a..213842af7e51 100644 --- a/3rdparty/openal/alc/backends/null.h +++ b/3rdparty/openal/alc/backends/null.h @@ -5,15 +5,15 @@ struct NullBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_NULL_H */ diff --git a/3rdparty/openal/alc/backends/oboe.cpp b/3rdparty/openal/alc/backends/oboe.cpp index b7bab19ae4ea..6bd720cbf7d8 100644 --- a/3rdparty/openal/alc/backends/oboe.cpp +++ b/3rdparty/openal/alc/backends/oboe.cpp @@ -4,10 +4,11 @@ #include "oboe.h" #include +#include #include -#include #include "alnumeric.h" +#include "alstring.h" #include "core/device.h" #include "core/logging.h" #include "ringbuffer.h" @@ -17,7 +18,9 @@ namespace { -constexpr char device_name[] = "Oboe Default"; +using namespace std::string_view_literals; + +[[nodiscard]] constexpr auto GetDeviceName() noexcept { return "Oboe Default"sv; } struct OboePlayback final : public BackendBase, public oboe::AudioStreamCallback { @@ -48,21 +51,20 @@ oboe::DataCallbackResult OboePlayback::onAudioReady(oboe::AudioStream *oboeStrea return oboe::DataCallbackResult::Continue; } -void OboePlayback::onErrorAfterClose(oboe::AudioStream* audioStream, oboe::Result error) +void OboePlayback::onErrorAfterClose(oboe::AudioStream*, oboe::Result error) { - if (error == oboe::Result::ErrorDisconnected) { + 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(std::string_view name) { if(name.empty()) - name = device_name; - else if(name != device_name) + name = GetDeviceName(); + else if(name != GetDeviceName()) throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + al::sizei(name), name.data()}; /* Open a basic output stream, just to ensure it can work. */ oboe::ManagedStream stream; @@ -73,7 +75,7 @@ void OboePlayback::open(std::string_view name) throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s", oboe::convertToText(result)}; - mDevice->DeviceName = name; + mDeviceName = name; } bool OboePlayback::reset() @@ -81,6 +83,7 @@ bool OboePlayback::reset() oboe::AudioStreamBuilder builder; builder.setDirection(oboe::Direction::Output); builder.setPerformanceMode(oboe::PerformanceMode::LowLatency); + builder.setUsage(oboe::Usage::Game); /* Don't let Oboe convert. We should be able to handle anything it gives * back. */ @@ -144,7 +147,7 @@ bool OboePlayback::reset() if(result != oboe::Result::OK) throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s", oboe::convertToText(result)}; - mStream->setBufferSizeInFrames(mini(static_cast(mDevice->BufferSize), + mStream->setBufferSizeInFrames(std::min(static_cast(mDevice->BufferSize), mStream->getBufferCapacityInFrames())); TRACE("Got stream with properties:\n%s", oboe::convertToText(mStream.get())); @@ -173,6 +176,9 @@ bool OboePlayback::reset() mDevice->FmtType = DevFmtInt; break; case oboe::AudioFormat::I24: +#endif +#if OBOE_VERSION_MAJOR > 1 || (OBOE_VERSION_MAJOR == 1 && OBOE_VERSION_MINOR >= 8) + case oboe::AudioFormat::IEC61937: #endif case oboe::AudioFormat::Unspecified: case oboe::AudioFormat::Invalid: @@ -186,9 +192,9 @@ bool OboePlayback::reset() * FramesPerBurst may not necessarily be correct, but hopefully it can act as a minimum * update size. */ - mDevice->UpdateSize = maxu(mDevice->Frequency / 100, + mDevice->UpdateSize = std::max(mDevice->Frequency/100u, static_cast(mStream->getFramesPerBurst())); - mDevice->BufferSize = maxu(mDevice->UpdateSize * 2, + mDevice->BufferSize = std::max(mDevice->UpdateSize*2u, static_cast(mStream->getBufferSizeInFrames())); return true; @@ -230,7 +236,7 @@ struct OboeCapture final : public BackendBase, public oboe::AudioStreamCallback oboe::DataCallbackResult OboeCapture::onAudioReady(oboe::AudioStream*, void *audioData, int32_t numFrames) { - mRing->write(audioData, static_cast(numFrames)); + std::ignore = mRing->write(audioData, static_cast(numFrames)); return oboe::DataCallbackResult::Continue; } @@ -238,10 +244,10 @@ oboe::DataCallbackResult OboeCapture::onAudioReady(oboe::AudioStream*, void *aud void OboeCapture::open(std::string_view name) { if(name.empty()) - name = device_name; - else if(name != device_name) + name = GetDeviceName(); + else if(name != GetDeviceName()) throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + al::sizei(name), name.data()}; oboe::AudioStreamBuilder builder; builder.setDirection(oboe::Direction::Input) @@ -267,6 +273,7 @@ void OboeCapture::open(std::string_view name) case DevFmtX61: case DevFmtX71: case DevFmtX714: + case DevFmtX7144: case DevFmtX3D71: case DevFmtAmbi3D: throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported", @@ -305,10 +312,10 @@ void OboeCapture::open(std::string_view name) TRACE("Got stream with properties:\n%s", oboe::convertToText(mStream.get())); /* Ensure a minimum ringbuffer size of 100ms. */ - mRing = RingBuffer::Create(maxu(mDevice->BufferSize, mDevice->Frequency/10), + mRing = RingBuffer::Create(std::max(mDevice->BufferSize, mDevice->Frequency/10u), static_cast(mStream->getBytesPerFrame()), false); - mDevice->DeviceName = name; + mDeviceName = name; } void OboeCapture::start() @@ -330,7 +337,7 @@ uint OboeCapture::availableSamples() { return static_cast(mRing->readSpace()); } void OboeCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } } // namespace @@ -339,16 +346,15 @@ bool OboeBackendFactory::init() { return true; } bool OboeBackendFactory::querySupport(BackendType type) { return type == BackendType::Playback || type == BackendType::Capture; } -std::string OboeBackendFactory::probe(BackendType type) +auto OboeBackendFactory::enumerate(BackendType type) -> std::vector { switch(type) { case BackendType::Playback: case BackendType::Capture: - /* Includes null char. */ - return std::string{device_name, sizeof(device_name)}; + return std::vector{std::string{GetDeviceName()}}; } - return std::string{}; + return {}; } BackendPtr OboeBackendFactory::createBackend(DeviceBase *device, BackendType type) diff --git a/3rdparty/openal/alc/backends/oboe.h b/3rdparty/openal/alc/backends/oboe.h index a39c24454f7d..d277cfe7cf84 100644 --- a/3rdparty/openal/alc/backends/oboe.h +++ b/3rdparty/openal/alc/backends/oboe.h @@ -5,15 +5,15 @@ struct OboeBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_OBOE_H */ diff --git a/3rdparty/openal/alc/backends/opensl.cpp b/3rdparty/openal/alc/backends/opensl.cpp index 61e3c9a76792..605ee5499f6b 100644 --- a/3rdparty/openal/alc/backends/opensl.cpp +++ b/3rdparty/openal/alc/backends/opensl.cpp @@ -23,10 +23,10 @@ #include "opensl.h" -#include #include #include +#include #include #include #include @@ -36,6 +36,7 @@ #include "albit.h" #include "alnumeric.h" #include "alsem.h" +#include "alstring.h" #include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" @@ -50,15 +51,17 @@ namespace { +using namespace std::string_view_literals; + /* Helper macros */ #define EXTRACT_VCALL_ARGS(...) __VA_ARGS__)) #define VCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS #define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS -constexpr char opensl_device[] = "OpenSL"; - +[[nodiscard]] constexpr auto GetDeviceName() noexcept { return "OpenSL"sv; } +[[nodiscard]] constexpr SLuint32 GetChannelMask(DevFmtChannels chans) noexcept { switch(chans) @@ -82,6 +85,7 @@ constexpr SLuint32 GetChannelMask(DevFmtChannels chans) noexcept SL_SPEAKER_BACK_RIGHT | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT | SL_SPEAKER_TOP_FRONT_LEFT | SL_SPEAKER_TOP_FRONT_RIGHT | SL_SPEAKER_TOP_BACK_LEFT | SL_SPEAKER_TOP_BACK_RIGHT; + case DevFmtX7144: case DevFmtAmbi3D: break; } @@ -189,8 +193,6 @@ struct OpenSLPlayback final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(OpenSLPlayback) }; OpenSLPlayback::~OpenSLPlayback() @@ -229,7 +231,7 @@ void OpenSLPlayback::process(SLAndroidSimpleBufferQueueItf) noexcept int OpenSLPlayback::mixerProc() { SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); SLPlayItf player; SLAndroidSimpleBufferQueueItf bufferQueue; @@ -276,26 +278,26 @@ int OpenSLPlayback::mixerProc() std::unique_lock dlock{mMutex}; auto data = mRing->getWriteVector(); - mDevice->renderSamples(data.first.buf, - static_cast(data.first.len)*mDevice->UpdateSize, frame_step); - if(data.second.len > 0) - mDevice->renderSamples(data.second.buf, - static_cast(data.second.len)*mDevice->UpdateSize, frame_step); + mDevice->renderSamples(data[0].buf, + static_cast(data[0].len)*mDevice->UpdateSize, frame_step); + if(data[1].len > 0) + mDevice->renderSamples(data[1].buf, + static_cast(data[1].len)*mDevice->UpdateSize, frame_step); - size_t todo{data.first.len + data.second.len}; + const auto todo = size_t{data[0].len + data[1].len}; mRing->writeAdvance(todo); dlock.unlock(); for(size_t i{0};i < todo;i++) { - if(!data.first.len) + if(!data[0].len) { - data.first = data.second; - data.second.buf = nullptr; - data.second.len = 0; + data[0] = data[1]; + data[1].buf = nullptr; + data[1].len = 0; } - result = VCALL(bufferQueue,Enqueue)(data.first.buf, mDevice->UpdateSize*mFrameSize); + result = VCALL(bufferQueue,Enqueue)(data[0].buf, mDevice->UpdateSize*mFrameSize); PrintErr(result, "bufferQueue->Enqueue"); if(SL_RESULT_SUCCESS != result) { @@ -303,8 +305,8 @@ int OpenSLPlayback::mixerProc() break; } - data.first.len--; - data.first.buf += mDevice->UpdateSize*mFrameSize; + data[0].len--; + data[0].buf += mDevice->UpdateSize*mFrameSize; } } @@ -315,10 +317,10 @@ int OpenSLPlayback::mixerProc() void OpenSLPlayback::open(std::string_view name) { if(name.empty()) - name = opensl_device; - else if(name != opensl_device) + name = GetDeviceName(); + else if(name != GetDeviceName()) throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + al::sizei(name), name.data()}; /* There's only one device, so if it's already open, there's nothing to do. */ if(mEngineObj) return; @@ -362,7 +364,7 @@ void OpenSLPlayback::open(std::string_view name) "Failed to initialize OpenSL device: 0x%08x", result}; } - mDevice->DeviceName = name; + mDeviceName = name; } bool OpenSLPlayback::reset() @@ -375,74 +377,6 @@ bool OpenSLPlayback::reset() mRing = nullptr; -#if 0 - if(!mDevice->Flags.get()) - { - /* FIXME: Disabled until I figure out how to get the Context needed for - * the getSystemService call. - */ - JNIEnv *env = Android_GetJNIEnv(); - jobject jctx = Android_GetContext(); - - /* Get necessary stuff for using java.lang.Integer, - * android.content.Context, and android.media.AudioManager. - */ - jclass int_cls = JCALL(env,FindClass)("java/lang/Integer"); - jmethodID int_parseint = JCALL(env,GetStaticMethodID)(int_cls, - "parseInt", "(Ljava/lang/String;)I" - ); - TRACE("Integer: %p, parseInt: %p\n", int_cls, int_parseint); - - jclass ctx_cls = JCALL(env,FindClass)("android/content/Context"); - jfieldID ctx_audsvc = JCALL(env,GetStaticFieldID)(ctx_cls, - "AUDIO_SERVICE", "Ljava/lang/String;" - ); - jmethodID ctx_getSysSvc = JCALL(env,GetMethodID)(ctx_cls, - "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;" - ); - TRACE("Context: %p, AUDIO_SERVICE: %p, getSystemService: %p\n", - ctx_cls, ctx_audsvc, ctx_getSysSvc); - - jclass audmgr_cls = JCALL(env,FindClass)("android/media/AudioManager"); - jfieldID audmgr_prop_out_srate = JCALL(env,GetStaticFieldID)(audmgr_cls, - "PROPERTY_OUTPUT_SAMPLE_RATE", "Ljava/lang/String;" - ); - jmethodID audmgr_getproperty = JCALL(env,GetMethodID)(audmgr_cls, - "getProperty", "(Ljava/lang/String;)Ljava/lang/String;" - ); - TRACE("AudioManager: %p, PROPERTY_OUTPUT_SAMPLE_RATE: %p, getProperty: %p\n", - audmgr_cls, audmgr_prop_out_srate, audmgr_getproperty); - - const char *strchars; - jstring strobj; - - /* Now make the calls. */ - //AudioManager audMgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE); - strobj = JCALL(env,GetStaticObjectField)(ctx_cls, ctx_audsvc); - jobject audMgr = JCALL(env,CallObjectMethod)(jctx, ctx_getSysSvc, strobj); - strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr); - TRACE("Context.getSystemService(%s) = %p\n", strchars, audMgr); - JCALL(env,ReleaseStringUTFChars)(strobj, strchars); - - //String srateStr = audMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); - strobj = JCALL(env,GetStaticObjectField)(audmgr_cls, audmgr_prop_out_srate); - jstring srateStr = JCALL(env,CallObjectMethod)(audMgr, audmgr_getproperty, strobj); - strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr); - TRACE("audMgr.getProperty(%s) = %p\n", strchars, srateStr); - JCALL(env,ReleaseStringUTFChars)(strobj, strchars); - - //int sampleRate = Integer.parseInt(srateStr); - sampleRate = JCALL(env,CallStaticIntMethod)(int_cls, int_parseint, srateStr); - - strchars = JCALL(env,GetStringUTFChars)(srateStr, nullptr); - TRACE("Got system sample rate %uhz (%s)\n", sampleRate, strchars); - JCALL(env,ReleaseStringUTFChars)(srateStr, strchars); - - if(!sampleRate) sampleRate = device->Frequency; - else sampleRate = maxu(sampleRate, MIN_OUTPUT_RATE); - } -#endif - mDevice->FmtChans = DevFmtStereo; mDevice->FmtType = DevFmtShort; @@ -630,8 +564,8 @@ ClockLatency OpenSLPlayback::getClockLatency() { ClockLatency ret; - std::lock_guard _{mMutex}; - ret.ClockTime = GetDeviceClockTime(mDevice); + std::lock_guard dlock{mMutex}; + ret.ClockTime = mDevice->getClockTime(); ret.Latency = std::chrono::seconds{mRing->readSpace() * mDevice->UpdateSize}; ret.Latency /= mDevice->Frequency; @@ -662,8 +596,6 @@ struct OpenSLCapture final : public BackendBase { uint mSplOffset{0u}; uint mFrameSize{0}; - - DEF_NEWDEL(OpenSLCapture) }; OpenSLCapture::~OpenSLCapture() @@ -689,10 +621,10 @@ void OpenSLCapture::process(SLAndroidSimpleBufferQueueItf) noexcept void OpenSLCapture::open(std::string_view name) { if(name.empty()) - name = opensl_device; - else if(name != opensl_device) + name = GetDeviceName(); + else if(name != GetDeviceName()) throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + al::sizei(name), name.data()}; SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)}; PrintErr(result, "slCreateEngine"); @@ -710,10 +642,10 @@ void OpenSLCapture::open(std::string_view name) { mFrameSize = mDevice->frameSizeFromFmt(); /* Ensure the total length is at least 100ms */ - uint length{maxu(mDevice->BufferSize, mDevice->Frequency/10)}; + uint length{std::max(mDevice->BufferSize, mDevice->Frequency/10u)}; /* Ensure the per-chunk length is at least 10ms, and no more than 50ms. */ - uint update_len{clampu(mDevice->BufferSize/3, mDevice->Frequency/100, - mDevice->Frequency/100*5)}; + uint update_len{std::clamp(mDevice->BufferSize/3u, mDevice->Frequency/100u, + mDevice->Frequency/100u*5u)}; uint num_updates{(length+update_len-1) / update_len}; mRing = RingBuffer::Create(num_updates, update_len*mFrameSize, false); @@ -824,16 +756,16 @@ void OpenSLCapture::open(std::string_view name) 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); - std::fill_n(data.second.buf, data.second.len*chunk_size, silence); - for(size_t i{0u};i < data.first.len && SL_RESULT_SUCCESS == result;i++) + std::fill_n(data[0].buf, data[0].len*chunk_size, silence); + std::fill_n(data[1].buf, data[1].len*chunk_size, silence); + for(size_t i{0u};i < data[0].len && SL_RESULT_SUCCESS == result;i++) { - result = VCALL(bufferQueue,Enqueue)(data.first.buf + chunk_size*i, chunk_size); + result = VCALL(bufferQueue,Enqueue)(data[0].buf + chunk_size*i, chunk_size); PrintErr(result, "bufferQueue->Enqueue"); } - for(size_t i{0u};i < data.second.len && SL_RESULT_SUCCESS == result;i++) + for(size_t i{0u};i < data[1].len && SL_RESULT_SUCCESS == result;i++) { - result = VCALL(bufferQueue,Enqueue)(data.second.buf + chunk_size*i, chunk_size); + result = VCALL(bufferQueue,Enqueue)(data[1].buf + chunk_size*i, chunk_size); PrintErr(result, "bufferQueue->Enqueue"); } } @@ -853,7 +785,7 @@ void OpenSLCapture::open(std::string_view name) "Failed to initialize OpenSL device: 0x%08x", result}; } - mDevice->DeviceName = name; + mDeviceName = name; } void OpenSLCapture::start() @@ -897,8 +829,8 @@ void OpenSLCapture::captureSamples(std::byte *buffer, uint samples) auto rdata = mRing->getReadVector(); for(uint i{0};i < samples;) { - const uint rem{minu(samples - i, update_size - mSplOffset)}; - std::copy_n(rdata.first.buf + mSplOffset*size_t{mFrameSize}, rem*size_t{mFrameSize}, + const uint rem{std::min(samples - i, update_size - mSplOffset)}; + std::copy_n(rdata[0].buf + mSplOffset*size_t{mFrameSize}, rem*size_t{mFrameSize}, buffer + i*size_t{mFrameSize}); mSplOffset += rem; @@ -908,11 +840,11 @@ void OpenSLCapture::captureSamples(std::byte *buffer, uint samples) mSplOffset = 0; ++adv_count; - rdata.first.len -= 1; - if(!rdata.first.len) - rdata.first = rdata.second; + rdata[0].len -= 1; + if(!rdata[0].len) + rdata[0] = rdata[1]; else - rdata.first.buf += chunk_size; + rdata[0].buf += chunk_size; } i += rem; @@ -945,20 +877,20 @@ void OpenSLCapture::captureSamples(std::byte *buffer, uint samples) SLresult result{SL_RESULT_SUCCESS}; auto wdata = mRing->getWriteVector(); - if(adv_count > wdata.second.len) LIKELY + if(adv_count > wdata[1].len) LIKELY { - auto len1 = std::min(wdata.first.len, adv_count-wdata.second.len); - auto buf1 = wdata.first.buf + chunk_size*(wdata.first.len-len1); + auto len1 = std::min(wdata[0].len, adv_count-wdata[1].len); + auto buf1 = wdata[0].buf + chunk_size*(wdata[0].len-len1); for(size_t i{0u};i < len1 && SL_RESULT_SUCCESS == result;i++) { result = VCALL(bufferQueue,Enqueue)(buf1 + chunk_size*i, chunk_size); PrintErr(result, "bufferQueue->Enqueue"); } } - if(wdata.second.len > 0) + if(wdata[1].len > 0) { - auto len2 = std::min(wdata.second.len, adv_count); - auto buf2 = wdata.second.buf + chunk_size*(wdata.second.len-len2); + auto len2 = std::min(wdata[1].len, adv_count); + auto buf2 = wdata[1].buf + chunk_size*(wdata[1].len-len2); for(size_t i{0u};i < len2 && SL_RESULT_SUCCESS == result;i++) { result = VCALL(bufferQueue,Enqueue)(buf2 + chunk_size*i, chunk_size); @@ -977,18 +909,15 @@ bool OSLBackendFactory::init() { return true; } bool OSLBackendFactory::querySupport(BackendType type) { return (type == BackendType::Playback || type == BackendType::Capture); } -std::string OSLBackendFactory::probe(BackendType type) +auto OSLBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; switch(type) { case BackendType::Playback: case BackendType::Capture: - /* Includes null char. */ - outnames.append(opensl_device, sizeof(opensl_device)); - break; + return std::vector{std::string{GetDeviceName()}}; } - return outnames; + return {}; } BackendPtr OSLBackendFactory::createBackend(DeviceBase *device, BackendType type) diff --git a/3rdparty/openal/alc/backends/opensl.h b/3rdparty/openal/alc/backends/opensl.h index b81624476cc0..9f13dd71f9d0 100644 --- a/3rdparty/openal/alc/backends/opensl.h +++ b/3rdparty/openal/alc/backends/opensl.h @@ -5,15 +5,15 @@ struct OSLBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_OSL_H */ diff --git a/3rdparty/openal/alc/backends/oss.cpp b/3rdparty/openal/alc/backends/oss.cpp index 87d3ba35b9d2..7bdf8a7b4731 100644 --- a/3rdparty/openal/alc/backends/oss.cpp +++ b/3rdparty/openal/alc/backends/oss.cpp @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include #include #include @@ -43,6 +45,7 @@ #include "alc/alconfig.h" #include "almalloc.h" #include "alnumeric.h" +#include "alstring.h" #include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" @@ -79,13 +82,22 @@ namespace { -constexpr char DefaultName[] = "OSS Default"; -std::string DefaultPlayback{"/dev/dsp"}; -std::string DefaultCapture{"/dev/dsp"}; +using namespace std::string_literals; +using namespace std::string_view_literals; + +[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "OSS Default"sv; } + +std::string DefaultPlayback{"/dev/dsp"s}; +std::string DefaultCapture{"/dev/dsp"s}; struct DevMap { std::string name; std::string device_name; + + template + DevMap(T&& name_, U&& devname_) + : name{std::forward(name_)}, device_name{std::forward(devname_)} + { } }; std::vector PlaybackDevices; @@ -98,98 +110,120 @@ std::vector CaptureDevices; #define DSP_CAP_INPUT 0x00010000 void ALCossListPopulate(std::vector &devlist, int type) { - devlist.emplace_back(DevMap{DefaultName, (type==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback}); + devlist.emplace_back(GetDefaultName(), (type==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback); } #else -void ALCossListAppend(std::vector &list, al::span handle, al::span path) +class FileHandle { + int mFd{-1}; + +public: + FileHandle() = default; + FileHandle(const FileHandle&) = delete; + FileHandle& operator=(const FileHandle&) = delete; + ~FileHandle() { if(mFd != -1) ::close(mFd); } + + template + [[nodiscard]] auto open(const char *fname, Args&& ...args) -> bool + { + close(); + mFd = ::open(fname, std::forward(args)...); + return mFd != -1; + } + void close() + { + if(mFd != -1) + ::close(mFd); + mFd = -1; + } + + [[nodiscard]] + auto get() const noexcept -> int { return mFd; } +}; + +void ALCossListAppend(std::vector &list, std::string_view handle, std::string_view path) { #ifdef ALC_OSS_DEVNODE_TRUC for(size_t i{0};i < path.size();++i) { - if(path[i] == '.' && handle.size() + i >= path.size()) + if(path[i] == '.' && handle.size() >= path.size() - i) { const size_t hoffset{handle.size() + i - path.size()}; if(strncmp(path.data() + i, handle.data() + hoffset, path.size() - i) == 0) - handle = handle.first(hoffset); - path = path.first(i); + handle = handle.substr(0, hoffset); + path = path.substr(0, i); } } #endif if(handle.empty()) handle = path; - std::string basename{handle.data(), handle.size()}; - std::string devname{path.data(), path.size()}; - - auto match_devname = [&devname](const DevMap &entry) -> bool - { return entry.device_name == devname; }; + auto match_devname = [path](const DevMap &entry) -> bool + { return entry.device_name == path; }; if(std::find_if(list.cbegin(), list.cend(), match_devname) != list.cend()) return; - auto checkName = [&list](const std::string &name) -> bool + auto checkName = [&list](const std::string_view name) -> bool { - 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(); }; int count{1}; - std::string newname{basename}; + std::string newname{handle}; while(checkName(newname)) { - newname = basename; + newname = handle; newname += " #"; newname += std::to_string(++count); } - list.emplace_back(DevMap{std::move(newname), std::move(devname)}); - const DevMap &entry = list.back(); + const DevMap &entry = list.emplace_back(std::move(newname), path); TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str()); } void ALCossListPopulate(std::vector &devlist, int type_flag) { - int fd{open("/dev/mixer", O_RDONLY)}; - if(fd < 0) + oss_sysinfo si{}; + FileHandle file; + if(!file.open("/dev/mixer", O_RDONLY)) { - TRACE("Could not open /dev/mixer: %s\n", strerror(errno)); + TRACE("Could not open /dev/mixer: %s\n", std::generic_category().message(errno).c_str()); goto done; } - oss_sysinfo si; - if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1) + if(ioctl(file.get(), SNDCTL_SYSINFO, &si) == -1) { - TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno)); + TRACE("SNDCTL_SYSINFO failed: %s\n", std::generic_category().message(errno).c_str()); goto done; } for(int i{0};i < si.numaudios;i++) { - oss_audioinfo ai; + oss_audioinfo ai{}; ai.dev = i; - if(ioctl(fd, SNDCTL_AUDIOINFO, &ai) == -1) + if(ioctl(file.get(), SNDCTL_AUDIOINFO, &ai) == -1) { - ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i, strerror(errno)); + ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i, + std::generic_category().message(errno).c_str()); continue; } if(!(ai.caps&type_flag) || ai.devnode[0] == '\0') continue; - al::span handle; + std::string_view handle; if(ai.handle[0] != '\0') handle = {ai.handle, strnlen(ai.handle, sizeof(ai.handle))}; else handle = {ai.name, strnlen(ai.name, sizeof(ai.name))}; - al::span devnode{ai.devnode, strnlen(ai.devnode, sizeof(ai.devnode))}; + const std::string_view devnode{ai.devnode, strnlen(ai.devnode, sizeof(ai.devnode))}; ALCossListAppend(devlist, handle, devnode); } done: - if(fd >= 0) - close(fd); - fd = -1; + file.close(); const char *defdev{((type_flag==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback).c_str()}; auto iter = std::find_if(devlist.cbegin(), devlist.cend(), @@ -197,7 +231,7 @@ void ALCossListPopulate(std::vector &devlist, int type_flag) { return entry.device_name == defdev; } ); if(iter == devlist.cend()) - devlist.insert(devlist.begin(), DevMap{DefaultName, defdev}); + devlist.insert(devlist.begin(), DevMap{GetDefaultName(), defdev}); else { DevMap entry{std::move(*iter)}; @@ -238,8 +272,6 @@ struct OSSPlayback final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(OSSPlayback) }; OSSPlayback::~OSSPlayback() @@ -253,7 +285,7 @@ OSSPlayback::~OSSPlayback() int OSSPlayback::mixerProc() { SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); const size_t frame_step{mDevice->channelsFromFmt()}; const size_t frame_size{mDevice->frameSizeFromFmt()}; @@ -265,38 +297,38 @@ int OSSPlayback::mixerProc() pollitem.fd = mFd; pollitem.events = POLLOUT; - int pret{poll(&pollitem, 1, 1000)}; - if(pret < 0) + if(int pret{poll(&pollitem, 1, 1000)}; pret < 0) { if(errno == EINTR || errno == EAGAIN) continue; - ERR("poll failed: %s\n", strerror(errno)); - mDevice->handleDisconnect("Failed waiting for playback buffer: %s", strerror(errno)); + const auto errstr = std::generic_category().message(errno); + ERR("poll failed: %s\n", errstr.c_str()); + mDevice->handleDisconnect("Failed waiting for playback buffer: %s", errstr.c_str()); break; } - else if(pret == 0) + else if(pret == 0) /* NOLINT(*-else-after-return) 'pret' is local to the if/else blocks */ { WARN("poll timeout\n"); continue; } - std::byte *write_ptr{mMixData.data()}; - size_t to_write{mMixData.size()}; - mDevice->renderSamples(write_ptr, static_cast(to_write/frame_size), frame_step); - while(to_write > 0 && !mKillNow.load(std::memory_order_acquire)) + al::span write_buf{mMixData}; + mDevice->renderSamples(write_buf.data(), static_cast(write_buf.size()/frame_size), + frame_step); + while(!write_buf.empty() && !mKillNow.load(std::memory_order_acquire)) { - ssize_t wrote{write(mFd, write_ptr, to_write)}; + ssize_t wrote{write(mFd, write_buf.data(), write_buf.size())}; if(wrote < 0) { if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) continue; - ERR("write failed: %s\n", strerror(errno)); - mDevice->handleDisconnect("Failed writing playback samples: %s", strerror(errno)); + const auto errstr = std::generic_category().message(errno); + ERR("write failed: %s\n", errstr.c_str()); + mDevice->handleDisconnect("Failed writing playback samples: %s", errstr.c_str()); break; } - to_write -= static_cast(wrote); - write_ptr += wrote; + write_buf = write_buf.subspan(static_cast(wrote)); } } @@ -308,7 +340,7 @@ void OSSPlayback::open(std::string_view name) { const char *devname{DefaultPlayback.c_str()}; if(name.empty()) - name = DefaultName; + name = GetDefaultName(); else { if(PlaybackDevices.empty()) @@ -320,20 +352,20 @@ void OSSPlayback::open(std::string_view name) ); if(iter == PlaybackDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%.*s\" not found", static_cast(name.length()), name.data()}; + "Device name \"%.*s\" not found", al::sizei(name), name.data()}; devname = iter->device_name.c_str(); } int fd{::open(devname, O_WRONLY)}; if(fd == -1) throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s", devname, - strerror(errno)}; + std::generic_category().message(errno).c_str()}; if(mFd != -1) ::close(mFd); mFd = fd; - mDevice->DeviceName = name; + mDeviceName = name; } bool OSSPlayback::reset() @@ -363,15 +395,14 @@ bool OSSPlayback::reset() uint ossSpeed{mDevice->Frequency}; uint frameSize{numChannels * mDevice->bytesFromFmt()}; /* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */ - uint log2FragmentSize{maxu(log2i(mDevice->UpdateSize*frameSize), 4)}; + uint log2FragmentSize{std::max(log2i(mDevice->UpdateSize*frameSize), 4u)}; uint numFragmentsLogSize{(periods << 16) | log2FragmentSize}; audio_buf_info info{}; - const char *err; -#define CHECKERR(func) if((func) < 0) { \ - err = #func; \ - goto err; \ -} +#define CHECKERR(func) if((func) < 0) \ + throw al::backend_exception{al::backend_error::DeviceError, "%s failed: %s\n", #func, \ + std::generic_category().message(errno).c_str()}; + /* Don't fail if SETFRAGMENT fails. We can handle just about anything * that's reported back via GETOSPACE */ ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize); @@ -379,12 +410,6 @@ bool OSSPlayback::reset() CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels)); CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed)); CHECKERR(ioctl(mFd, SNDCTL_DSP_GETOSPACE, &info)); - if(0) - { - err: - ERR("%s failed: %s\n", err, strerror(errno)); - return false; - } #undef CHECKERR if(mDevice->channelsFromFmt() != numChannels) @@ -409,7 +434,7 @@ bool OSSPlayback::reset() setDefaultChannelOrder(); - mMixData.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt()); + mMixData.resize(size_t{mDevice->UpdateSize} * mDevice->frameSizeFromFmt()); return true; } @@ -433,7 +458,7 @@ void OSSPlayback::stop() mThread.join(); if(ioctl(mFd, SNDCTL_DSP_RESET) != 0) - ERR("Error resetting device: %s\n", strerror(errno)); + ERR("Error resetting device: %s\n", std::generic_category().message(errno).c_str()); } @@ -455,8 +480,6 @@ struct OSScapture final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(OSScapture) }; OSScapture::~OSScapture() @@ -470,7 +493,7 @@ OSScapture::~OSScapture() int OSScapture::recordProc() { SetRTPriority(); - althrd_setname(RECORD_THREAD_NAME); + althrd_setname(GetRecordThreadName()); const size_t frame_size{mDevice->frameSizeFromFmt()}; while(!mKillNow.load(std::memory_order_acquire)) @@ -479,29 +502,30 @@ int OSScapture::recordProc() pollitem.fd = mFd; pollitem.events = POLLIN; - int sret{poll(&pollitem, 1, 1000)}; - if(sret < 0) + if(int pret{poll(&pollitem, 1, 1000)}; pret < 0) { if(errno == EINTR || errno == EAGAIN) continue; - ERR("poll failed: %s\n", strerror(errno)); - mDevice->handleDisconnect("Failed to check capture samples: %s", strerror(errno)); + const auto errstr = std::generic_category().message(errno); + ERR("poll failed: %s\n", errstr.c_str()); + mDevice->handleDisconnect("Failed to check capture samples: %s", errstr.c_str()); break; } - else if(sret == 0) + else if(pret == 0) /* NOLINT(*-else-after-return) 'pret' is local to the if/else blocks */ { WARN("poll timeout\n"); continue; } auto vec = mRing->getWriteVector(); - if(vec.first.len > 0) + if(vec[0].len > 0) { - ssize_t amt{read(mFd, vec.first.buf, vec.first.len*frame_size)}; + ssize_t amt{read(mFd, vec[0].buf, vec[0].len*frame_size)}; if(amt < 0) { - ERR("read failed: %s\n", strerror(errno)); - mDevice->handleDisconnect("Failed reading capture samples: %s", strerror(errno)); + const auto errstr = std::generic_category().message(errno); + ERR("read failed: %s\n", errstr.c_str()); + mDevice->handleDisconnect("Failed reading capture samples: %s", errstr.c_str()); break; } mRing->writeAdvance(static_cast(amt)/frame_size); @@ -516,7 +540,7 @@ void OSScapture::open(std::string_view name) { const char *devname{DefaultCapture.c_str()}; if(name.empty()) - name = DefaultName; + name = GetDefaultName(); else { if(CaptureDevices.empty()) @@ -528,14 +552,14 @@ void OSScapture::open(std::string_view name) ); if(iter == CaptureDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%.*s\" not found", static_cast(name.length()), name.data()}; + "Device name \"%.*s\" not found", al::sizei(name), name.data()}; devname = iter->device_name.c_str(); } mFd = ::open(devname, O_RDONLY); if(mFd == -1) throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s", devname, - strerror(errno)}; + std::generic_category().message(errno).c_str()}; int ossFormat{}; switch(mDevice->FmtType) @@ -562,13 +586,13 @@ void OSScapture::open(std::string_view name) uint frameSize{numChannels * mDevice->bytesFromFmt()}; uint ossSpeed{mDevice->Frequency}; /* according to the OSS spec, 16 bytes are the minimum */ - uint log2FragmentSize{maxu(log2i(mDevice->BufferSize * frameSize / periods), 4)}; + uint log2FragmentSize{std::max(log2i(mDevice->BufferSize * frameSize / periods), 4u)}; uint numFragmentsLogSize{(periods << 16) | log2FragmentSize}; audio_buf_info info{}; #define CHECKERR(func) if((func) < 0) { \ throw al::backend_exception{al::backend_error::DeviceError, #func " failed: %s", \ - strerror(errno)}; \ + std::generic_category().message(errno).c_str()}; \ } CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize)); CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat)); @@ -591,7 +615,7 @@ void OSScapture::open(std::string_view name) mRing = RingBuffer::Create(mDevice->BufferSize, frameSize, false); - mDevice->DeviceName = name; + mDeviceName = name; } void OSScapture::start() @@ -613,11 +637,11 @@ void OSScapture::stop() mThread.join(); if(ioctl(mFd, SNDCTL_DSP_RESET) != 0) - ERR("Error resetting device: %s\n", strerror(errno)); + ERR("Error resetting device: %s\n", std::generic_category().message(errno).c_str()); } void OSScapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } uint OSScapture::availableSamples() { return static_cast(mRing->readSpace()); } @@ -633,9 +657,9 @@ BackendFactory &OSSBackendFactory::getFactory() bool OSSBackendFactory::init() { - if(auto devopt = ConfigValueStr(nullptr, "oss", "device")) + if(auto devopt = ConfigValueStr({}, "oss", "device")) DefaultPlayback = std::move(*devopt); - if(auto capopt = ConfigValueStr(nullptr, "oss", "capture")) + if(auto capopt = ConfigValueStr({}, "oss", "capture")) DefaultCapture = std::move(*capopt); return true; @@ -644,18 +668,13 @@ bool OSSBackendFactory::init() bool OSSBackendFactory::querySupport(BackendType type) { return (type == BackendType::Playback || type == BackendType::Capture); } -std::string OSSBackendFactory::probe(BackendType type) +auto OSSBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; - + std::vector outnames; auto add_device = [&outnames](const DevMap &entry) -> void { - struct stat buf; - if(stat(entry.device_name.c_str(), &buf) == 0) - { - /* Includes null char. */ - outnames.append(entry.name.c_str(), entry.name.length()+1); - } + if(struct stat buf{}; stat(entry.device_name.c_str(), &buf) == 0) + outnames.emplace_back(entry.name); }; switch(type) @@ -663,12 +682,14 @@ std::string OSSBackendFactory::probe(BackendType type) case BackendType::Playback: PlaybackDevices.clear(); ALCossListPopulate(PlaybackDevices, DSP_CAP_OUTPUT); + outnames.reserve(PlaybackDevices.size()); std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device); break; case BackendType::Capture: CaptureDevices.clear(); ALCossListPopulate(CaptureDevices, DSP_CAP_INPUT); + outnames.reserve(CaptureDevices.size()); std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device); break; } diff --git a/3rdparty/openal/alc/backends/oss.h b/3rdparty/openal/alc/backends/oss.h index 4f2c00b96990..b5faf96a5784 100644 --- a/3rdparty/openal/alc/backends/oss.h +++ b/3rdparty/openal/alc/backends/oss.h @@ -5,15 +5,15 @@ struct OSSBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_OSS_H */ diff --git a/3rdparty/openal/alc/backends/otherio.cpp b/3rdparty/openal/alc/backends/otherio.cpp new file mode 100644 index 000000000000..60055b10876f --- /dev/null +++ b/3rdparty/openal/alc/backends/otherio.cpp @@ -0,0 +1,700 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2024 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include "otherio.h" + +#define WIN32_LEAN_AND_MEAN +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#ifndef _WAVEFORMATEXTENSIBLE_ +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "albit.h" +#include "alstring.h" +#include "althrd_setname.h" +#include "comptr.h" +#include "core/converter.h" +#include "core/device.h" +#include "core/helpers.h" +#include "core/logging.h" +#include "strutils.h" + + +/* A custom C++ interface that should be capable of interoperating with ASIO + * drivers. + */ +enum class ORIOError : LONG { + Okay = 0, + Success = 0x3f4847a0, + NotPresent = -1000, + HWMalfunction, + InvalidParameter, + InvalidMode, + SPNotAdvancing, + NoClock, + NoMemory, +}; + +/* A 64-bit integer or double, which has the most significant 32-bit word first. */ +struct ORIO64Bit { + uint32_t hi; + uint32_t lo; + + template + auto as() const -> T = delete; +}; + +template<> [[nodiscard]] +auto ORIO64Bit::as() const -> uint64_t { return (uint64_t{hi}<<32) | lo; } +template<> [[nodiscard]] +auto ORIO64Bit::as() const -> int64_t { return static_cast(as()); } +template<> [[nodiscard]] +auto ORIO64Bit::as() const -> double { return al::bit_cast(as()); } + + +enum class ORIOSampleType : LONG { + Int16BE = 0, + Int24BE = 1, + Int32BE = 2, + Float32BE = 3, + Float64BE = 4, + Int32BE16 = 8, + Int32BE18 = 9, + Int32BE20 = 10, + Int32BE24 = 11, + + Int16LE = 16, + Int24LE = 17, + Int32LE = 18, + Float32LE = 19, + Float64LE = 20, + Int32LE16 = 24, + Int32LE18 = 25, + Int32LE20 = 26, + Int32LE24 = 27, + + DSDInt8LSB1 = 32, + DSDInt8MSB1 = 33, + + DSDInt8 = 40, +}; + +struct ORIOClockSource { + LONG mIndex; + LONG mAssocChannel; + LONG mAssocGroup; + LONG mIsCurrent; + std::array mName; +}; + +struct ORIOChannelInfo { + LONG mChannel; + LONG mIsInput; + LONG mIsActive; + LONG mGroup; + ORIOSampleType mSampleType; + std::array mName; +}; + +struct ORIOBufferInfo { + LONG mIsInput; + LONG mChannelNum; + std::array mBuffers; +}; + +struct ORIOTime { + struct TimeInfo { + double mSpeed; + ORIO64Bit mSystemTime; + ORIO64Bit mSamplePosition; + double mSampleRate; + ULONG mFlags; + std::array mReserved; + }; + struct TimeCode { + double mSpeed; + ORIO64Bit mTimeCodeSamples; + ULONG mFlags; + std::array mFuture; + }; + + std::array mReserved; + TimeInfo mTimeInfo; + TimeCode mTimeCode; +}; + +#ifdef _WIN64 +#define ORIO_CALLBACK CALLBACK +#else +#define ORIO_CALLBACK +#endif + +struct ORIOCallbacks { + void (ORIO_CALLBACK*BufferSwitch)(LONG bufferIndex, LONG directProcess) noexcept; + void (ORIO_CALLBACK*SampleRateDidChange)(double srate) noexcept; + auto (ORIO_CALLBACK*Message)(LONG selector, LONG value, void *message, double *opt) noexcept -> LONG; + auto (ORIO_CALLBACK*BufferSwitchTimeInfo)(ORIOTime *timeInfo, LONG bufferIndex, LONG directProcess) noexcept -> ORIOTime*; +}; + +/* COM interfaces don't include a virtual destructor in their pure-virtual + * classes, and we can't add one without breaking ABI. + */ +#ifdef __GNUC__ +_Pragma("GCC diagnostic push") +_Pragma("GCC diagnostic ignored \"-Wnon-virtual-dtor\"") +#endif +/* NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor) */ +struct ORIOiface : public IUnknown { + STDMETHOD_(LONG, Init)(void *sysHandle) = 0; + /* A fixed-length span should be passed exactly the same as one pointer. + * This ensures an appropriately-sized buffer for the driver. + */ + STDMETHOD_(void, GetDriverName)(al::span name) = 0; + STDMETHOD_(LONG, GetDriverVersion)() = 0; + STDMETHOD_(void, GetErrorMessage)(al::span message) = 0; + STDMETHOD_(ORIOError, Start)() = 0; + STDMETHOD_(ORIOError, Stop)() = 0; + STDMETHOD_(ORIOError, GetChannels)(LONG *numInput, LONG *numOutput) = 0; + STDMETHOD_(ORIOError, GetLatencies)(LONG *inputLatency, LONG *outputLatency) = 0; + STDMETHOD_(ORIOError, GetBufferSize)(LONG *minSize, LONG *maxSize, LONG *preferredSize, LONG *granularity) = 0; + STDMETHOD_(ORIOError, CanSampleRate)(double srate) = 0; + STDMETHOD_(ORIOError, GetSampleRate)(double *srate) = 0; + STDMETHOD_(ORIOError, SetSampleRate)(double srate) = 0; + STDMETHOD_(ORIOError, GetClockSources)(ORIOClockSource *clocks, LONG *numSources) = 0; + STDMETHOD_(ORIOError, SetClockSource)(LONG index) = 0; + STDMETHOD_(ORIOError, GetSamplePosition)(ORIO64Bit *splPos, ORIO64Bit *tstampNS) = 0; + STDMETHOD_(ORIOError, GetChannelInfo)(ORIOChannelInfo *info) = 0; + STDMETHOD_(ORIOError, CreateBuffers)(ORIOBufferInfo *infos, LONG numInfos, LONG bufferSize, ORIOCallbacks *callbacks) = 0; + STDMETHOD_(ORIOError, DisposeBuffers)() = 0; + STDMETHOD_(ORIOError, ControlPanel)() = 0; + STDMETHOD_(ORIOError, Future)(LONG selector, void *opt) = 0; + STDMETHOD_(ORIOError, OutputReady)() = 0; + + ORIOiface() = default; + ORIOiface(const ORIOiface&) = delete; + auto operator=(const ORIOiface&) -> ORIOiface& = delete; + ~ORIOiface() = delete; +}; +#ifdef __GNUC__ +_Pragma("GCC diagnostic pop") +#endif + +namespace { + +using namespace std::string_view_literals; +using std::chrono::nanoseconds; +using std::chrono::milliseconds; +using std::chrono::seconds; + + +struct DeviceEntry { + std::string mDrvName; + CLSID mDrvGuid{}; +}; + +std::vector gDeviceList; + + +struct KeyCloser { + void operator()(HKEY key) { RegCloseKey(key); } +}; +using KeyPtr = std::unique_ptr,KeyCloser>; + +[[nodiscard]] +auto PopulateDeviceList() -> HRESULT +{ + auto regbase = KeyPtr{}; + auto res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\ASIO", 0, KEY_READ, + al::out_ptr(regbase)); + if(res != ERROR_SUCCESS) + { + ERR("Error opening HKLM\\Software\\ASIO: %ld\n", res); + return E_NOINTERFACE; + } + + auto numkeys = DWORD{}; + auto maxkeylen = DWORD{}; + res = RegQueryInfoKeyW(regbase.get(), nullptr, nullptr, nullptr, &numkeys, &maxkeylen, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr); + if(res != ERROR_SUCCESS) + { + ERR("Error querying HKLM\\Software\\ASIO info: %ld\n", res); + return E_FAIL; + } + + /* maxkeylen is the max number of unicode characters a subkey is. A unicode + * character can occupy two WCHARs, so ensure there's enough space for them + * and the null char. + */ + auto keyname = std::vector(maxkeylen*2 + 1); + for(DWORD i{0};i < numkeys;++i) + { + auto namelen = static_cast(keyname.size()); + res = RegEnumKeyExW(regbase.get(), i, keyname.data(), &namelen, nullptr, nullptr, nullptr, + nullptr); + if(res != ERROR_SUCCESS) + { + ERR("Error querying HKLM\\Software\\ASIO subkey %lu: %ld\n", i, res); + continue; + } + if(namelen == 0) + { + ERR("HKLM\\Software\\ASIO subkey %lu is blank?\n", i); + continue; + } + auto subkeyname = wstr_to_utf8({keyname.data(), namelen}); + + auto subkey = KeyPtr{}; + res = RegOpenKeyExW(regbase.get(), keyname.data(), 0, KEY_READ, al::out_ptr(subkey)); + if(res != ERROR_SUCCESS) + { + ERR("Error opening HKLM\\Software\\ASIO\\%s: %ld\n", subkeyname.c_str(), res); + continue; + } + + auto idstr = std::array{}; + auto readsize = DWORD{idstr.size()*sizeof(WCHAR)}; + res = RegGetValueW(subkey.get(), L"", L"CLSID", RRF_RT_REG_SZ, nullptr, idstr.data(), + &readsize); + if(res != ERROR_SUCCESS) + { + ERR("Failed to read HKLM\\Software\\ASIO\\%s\\CLSID: %ld\n", subkeyname.c_str(), res); + continue; + } + idstr.back() = 0; + + auto guid = CLSID{}; + if(auto hr = CLSIDFromString(idstr.data(), &guid); FAILED(hr)) + { + ERR("Failed to parse CLSID \"%s\": 0x%08lx\n", wstr_to_utf8(idstr.data()).c_str(), hr); + continue; + } + + /* The CLSID is also used for the IID. */ + auto iface = ComPtr{}; + auto hr = CoCreateInstance(guid, nullptr, CLSCTX_INPROC_SERVER, guid, al::out_ptr(iface)); + if(SUCCEEDED(hr)) + { +#if !ALSOFT_UWP + if(!iface->Init(GetForegroundWindow())) +#else + if(!iface->Init(nullptr)) +#endif + { + ERR("Failed to initialize %s\n", subkeyname.c_str()); + continue; + } + auto drvname = std::array{}; + iface->GetDriverName(drvname); + auto drvver = iface->GetDriverVersion(); + + auto &entry = gDeviceList.emplace_back(); + entry.mDrvName = drvname.data(); + entry.mDrvGuid = guid; + + TRACE("Got %s v%ld, CLSID {%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n", + entry.mDrvName.c_str(), drvver, 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]); + } + else + ERR("Failed to create %s instance for CLSID {%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}: 0x%08lx\n", + subkeyname.c_str(), 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], hr); + } + + return S_OK; +} + + +enum class MsgType { + OpenDevice, + ResetDevice, + StartDevice, + StopDevice, + CloseDevice, + + QuitThread +}; + +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, to ensure COM objects are used + * on a thread where COM is initialized. + */ +struct OtherIOProxy { + OtherIOProxy() = default; + OtherIOProxy(const OtherIOProxy&) = delete; + OtherIOProxy(OtherIOProxy&&) = delete; + virtual ~OtherIOProxy() = default; + + void operator=(const OtherIOProxy&) = delete; + void operator=(OtherIOProxy&&) = delete; + + virtual HRESULT openProxy(std::string_view name) = 0; + virtual void closeProxy() = 0; + + virtual HRESULT resetProxy() = 0; + virtual HRESULT startProxy() = 0; + virtual void stopProxy() = 0; + + struct Msg { + MsgType mType; + OtherIOProxy *mProxy; + std::string_view mParam; + std::promise mPromise; + + explicit operator bool() const noexcept { return mType != MsgType::QuitThread; } + }; + static inline std::deque mMsgQueue; + static inline std::mutex mMsgQueueLock; + static inline std::condition_variable mMsgQueueCond; + + auto pushMessage(MsgType type, std::string_view param={}) -> std::future + { + auto promise = std::promise{}; + auto future = std::future{promise.get_future()}; + { + auto msglock = std::lock_guard{mMsgQueueLock}; + mMsgQueue.emplace_back(Msg{type, this, param, std::move(promise)}); + } + mMsgQueueCond.notify_one(); + return future; + } + + static auto popMessage() -> Msg + { + auto lock = std::unique_lock{mMsgQueueLock}; + mMsgQueueCond.wait(lock, []{return !mMsgQueue.empty();}); + auto msg = Msg{std::move(mMsgQueue.front())}; + mMsgQueue.pop_front(); + return msg; + } + + static void messageHandler(std::promise *promise); +}; + +void OtherIOProxy::messageHandler(std::promise *promise) +{ + TRACE("Starting COM message thread\n"); + + auto com = ComWrapper{COINIT_APARTMENTTHREADED}; + if(!com) + { + WARN("Failed to initialize COM: 0x%08lx\n", com.status()); + promise->set_value(com.status()); + return; + } + + auto hr = PopulateDeviceList(); + if(FAILED(hr)) + { + promise->set_value(hr); + return; + } + + promise->set_value(S_OK); + promise = nullptr; + + TRACE("Starting message loop\n"); + while(Msg msg{popMessage()}) + { + TRACE("Got message \"%s\" (0x%04x, this=%p, param=\"%.*s\")\n", + GetMessageTypeName(msg.mType), static_cast(msg.mType), + static_cast(msg.mProxy), al::sizei(msg.mParam), msg.mParam.data()); + + switch(msg.mType) + { + case MsgType::OpenDevice: + hr = msg.mProxy->openProxy(msg.mParam); + msg.mPromise.set_value(hr); + continue; + + case MsgType::ResetDevice: + hr = msg.mProxy->resetProxy(); + msg.mPromise.set_value(hr); + continue; + + case MsgType::StartDevice: + hr = msg.mProxy->startProxy(); + msg.mPromise.set_value(hr); + continue; + + case MsgType::StopDevice: + msg.mProxy->stopProxy(); + msg.mPromise.set_value(S_OK); + continue; + + case MsgType::CloseDevice: + msg.mProxy->closeProxy(); + msg.mPromise.set_value(S_OK); + continue; + + case MsgType::QuitThread: + break; + } + ERR("Unexpected message: %u\n", static_cast(msg.mType)); + msg.mPromise.set_value(E_FAIL); + } + TRACE("Message loop finished\n"); +} + + +struct OtherIOPlayback final : public BackendBase, OtherIOProxy { + OtherIOPlayback(DeviceBase *device) noexcept : BackendBase{device} { } + ~OtherIOPlayback() final; + + void mixerProc(); + + void open(std::string_view name) final; + auto openProxy(std::string_view name) -> HRESULT final; + void closeProxy() final; + auto reset() -> bool final; + auto resetProxy() -> HRESULT final; + void start() final; + auto startProxy() -> HRESULT final; + void stop() final; + void stopProxy() final; + + HRESULT mOpenStatus{E_FAIL}; + + std::atomic mKillNow{true}; + std::thread mThread; +}; + +OtherIOPlayback::~OtherIOPlayback() +{ + if(SUCCEEDED(mOpenStatus)) + pushMessage(MsgType::CloseDevice).wait(); +} + +void OtherIOPlayback::mixerProc() +{ + const auto restTime = milliseconds{mDevice->UpdateSize*1000/mDevice->Frequency / 2}; + + SetRTPriority(); + althrd_setname(GetMixerThreadName()); + + auto done = int64_t{0}; + auto start = std::chrono::steady_clock::now(); + while(!mKillNow.load(std::memory_order_acquire) + && mDevice->Connected.load(std::memory_order_acquire)) + { + auto now = std::chrono::steady_clock::now(); + + /* This converts from nanoseconds to nanosamples, then to samples. */ + auto avail = int64_t{std::chrono::duration_cast((now-start) * mDevice->Frequency).count()}; + if(avail-done < mDevice->UpdateSize) + { + std::this_thread::sleep_for(restTime); + continue; + } + while(avail-done >= mDevice->UpdateSize) + { + mDevice->renderSamples(nullptr, mDevice->UpdateSize, 0u); + done += mDevice->UpdateSize; + } + + if(done >= mDevice->Frequency) + { + auto s = seconds{done/mDevice->Frequency}; + start += s; + done -= mDevice->Frequency*s.count(); + } + } +} + + +void OtherIOPlayback::open(std::string_view name) +{ + if(name.empty() && !gDeviceList.empty()) + name = gDeviceList[0].mDrvName; + else + { + auto iter = std::find_if(gDeviceList.cbegin(), gDeviceList.cend(), + [name](const DeviceEntry &entry) { return entry.mDrvName == name; }); + if(iter == gDeviceList.cend()) + throw al::backend_exception{al::backend_error::NoDevice, + "Device name \"%.*s\" not found", al::sizei(name), name.data()}; + } + + mOpenStatus = pushMessage(MsgType::OpenDevice, name).get(); + if(FAILED(mOpenStatus)) + throw al::backend_exception{al::backend_error::DeviceError, "Failed to open \"%.*s\"", + al::sizei(name), name.data()}; + + mDeviceName = name; +} + +auto OtherIOPlayback::openProxy(std::string_view name [[maybe_unused]]) -> HRESULT +{ + return S_OK; +} + +void OtherIOPlayback::closeProxy() +{ +} + +auto OtherIOPlayback::reset() -> bool +{ + return SUCCEEDED(pushMessage(MsgType::ResetDevice).get()); +} + +auto OtherIOPlayback::resetProxy() -> HRESULT +{ + setDefaultWFXChannelOrder(); + return S_OK; +} + +void OtherIOPlayback::start() +{ + auto hr = pushMessage(MsgType::StartDevice).get(); + if(FAILED(hr)) + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to start playback: 0x%08lx", hr}; +} + +auto OtherIOPlayback::startProxy() -> HRESULT +{ + try { + mKillNow.store(false, std::memory_order_release); + mThread = std::thread{std::mem_fn(&OtherIOPlayback::mixerProc), this}; + return S_OK; + } + catch(std::exception& e) { + ERR("Failed to start mixing thread: %s", e.what()); + } + return E_FAIL; +} + +void OtherIOPlayback::stop() +{ + pushMessage(MsgType::StopDevice).wait(); +} + +void OtherIOPlayback::stopProxy() +{ + if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable()) + return; + mThread.join(); +} + +} // namespace + + +auto OtherIOBackendFactory::init() -> bool +{ + static HRESULT InitResult{E_FAIL}; + if(FAILED(InitResult)) try + { + auto promise = std::promise{}; + auto future = promise.get_future(); + + std::thread{&OtherIOProxy::messageHandler, &promise}.detach(); + InitResult = future.get(); + } + catch(...) { + } + + return SUCCEEDED(InitResult); +} + +auto OtherIOBackendFactory::querySupport(BackendType type) -> bool +{ return type == BackendType::Playback; } + +auto OtherIOBackendFactory::enumerate(BackendType type) -> std::vector +{ + std::vector outnames; + + switch(type) + { + case BackendType::Playback: + std::for_each(gDeviceList.cbegin(), gDeviceList.cend(), + [&outnames](const DeviceEntry &entry) { outnames.emplace_back(entry.mDrvName); }); + break; + + case BackendType::Capture: + break; + } + + return outnames; +} + +auto OtherIOBackendFactory::createBackend(DeviceBase *device, BackendType type) -> BackendPtr +{ + if(type == BackendType::Playback) + return BackendPtr{new OtherIOPlayback{device}}; + return nullptr; +} + +auto OtherIOBackendFactory::getFactory() -> BackendFactory& +{ + static auto factory = OtherIOBackendFactory{}; + return factory; +} + +auto OtherIOBackendFactory::queryEventSupport(alc::EventType, BackendType) -> alc::EventSupport +{ + return alc::EventSupport::NoSupport; +} diff --git a/3rdparty/openal/alc/backends/otherio.h b/3rdparty/openal/alc/backends/otherio.h new file mode 100644 index 000000000000..64cb436ea7ec --- /dev/null +++ b/3rdparty/openal/alc/backends/otherio.h @@ -0,0 +1,21 @@ +#ifndef BACKENDS_OTHERIO_H +#define BACKENDS_OTHERIO_H + +#include "base.h" + +struct OtherIOBackendFactory final : public BackendFactory { +public: + auto init() -> bool final; + + auto querySupport(BackendType type) -> bool final; + + auto queryEventSupport(alc::EventType eventType, BackendType type) -> alc::EventSupport final; + + auto enumerate(BackendType type) -> std::vector final; + + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; + + static auto getFactory() -> BackendFactory&; +}; + +#endif /* BACKENDS_OTHERIO_H */ diff --git a/3rdparty/openal/alc/backends/pipewire.cpp b/3rdparty/openal/alc/backends/pipewire.cpp index 1c4e2cc4933b..a14316c1a04f 100644 --- a/3rdparty/openal/alc/backends/pipewire.cpp +++ b/3rdparty/openal/alc/backends/pipewire.cpp @@ -23,26 +23,30 @@ #include "pipewire.h" #include +#include #include +#include +#include +#include #include +#include +#include #include #include #include #include -#include +#include #include #include #include -#include +#include #include -#include +#include #include -#include "albit.h" #include "alc/alconfig.h" -#include "alc/events.h" +#include "alc/backends/base.h" #include "almalloc.h" -#include "alnumeric.h" #include "alspan.h" #include "alstring.h" #include "core/devformat.h" @@ -73,10 +77,15 @@ _Pragma("GCC diagnostic ignored \"-Weverything\"") #include "spa/buffer/buffer.h" #include "spa/param/audio/format-utils.h" #include "spa/param/audio/raw.h" +#include "spa/param/format.h" #include "spa/param/param.h" #include "spa/pod/builder.h" #include "spa/utils/json.h" +/* NOLINTBEGIN : All kinds of unsafe C stuff here from PipeWire headers + * (function-like macros, C style casts in macros, etc), which we can't do + * anything about except wrap into inline functions. + */ namespace { /* Wrap some nasty macros here too... */ template @@ -109,32 +118,72 @@ template constexpr auto get_pod_body(const spa_pod *pod) noexcept { return al::span{static_cast(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 +/* NOLINTEND */ _Pragma("GCC diagnostic pop") namespace { +template [[nodiscard]] constexpr +auto as_const_ptr(T *ptr) noexcept -> std::add_const_t* { return ptr; } + +struct PodDynamicBuilder { +private: + std::vector 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=1024) : 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(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" #endif +using namespace std::string_view_literals; using std::chrono::seconds; using std::chrono::milliseconds; using std::chrono::nanoseconds; using uint = unsigned int; -constexpr char pwireDevice[] = "PipeWire Output"; -constexpr char pwireInput[] = "PipeWire Input"; - bool check_version(const char *version) { @@ -144,10 +193,8 @@ bool check_version(const char *version) */ int major{0}, minor{0}, revision{0}; int ret{sscanf(version, "%d.%d.%d", &major, &minor, &revision)}; - if(ret == 3 && (major > PW_MAJOR || (major == PW_MAJOR && minor > PW_MINOR) - || (major == PW_MAJOR && minor == PW_MINOR && revision >= PW_MICRO))) - return true; - return false; + return ret == 3 && (major > PW_MAJOR || (major == PW_MAJOR && minor > PW_MINOR) + || (major == PW_MAJOR && minor == PW_MINOR && revision >= PW_MICRO)); } #ifdef HAVE_DYNLOAD @@ -201,7 +248,7 @@ bool pwire_load() if(pwire_handle) return true; - static constexpr char pwire_library[] = "libpipewire-0.3.so.0"; + const char *pwire_library{"libpipewire-0.3.so.0"}; std::string missing_funcs; pwire_handle = LoadLib(pwire_library); @@ -212,7 +259,7 @@ bool pwire_load() } #define LOAD_FUNC(f) do { \ - p##f = al::bit_cast(GetSymbol(pwire_handle, #f)); \ + p##f = reinterpret_cast(GetSymbol(pwire_handle, #f)); \ if(p##f == nullptr) missing_funcs += "\n" #f; \ } while(0); PWIRE_FUNCS(LOAD_FUNC) @@ -296,7 +343,7 @@ using Pod_t = typename PodInfo::Type; template al::span> get_array_span(const spa_pod *pod) { - uint32_t nvals; + uint32_t nvals{}; if(void *v{spa_pod_get_array(pod, &nvals)}) { if(get_array_value_type(pod) == T) @@ -330,11 +377,11 @@ To as(From) noexcept = delete; * - pw_metadata */ template<> -pw_proxy* as(pw_registry *reg) noexcept { return al::bit_cast(reg); } +pw_proxy* as(pw_registry *reg) noexcept { return reinterpret_cast(reg); } template<> -pw_proxy* as(pw_node *node) noexcept { return al::bit_cast(node); } +pw_proxy* as(pw_node *node) noexcept { return reinterpret_cast(node); } template<> -pw_proxy* as(pw_metadata *mdata) noexcept { return al::bit_cast(mdata); } +pw_proxy* as(pw_metadata *mdata) noexcept { return reinterpret_cast(mdata); } struct PwContextDeleter { @@ -367,9 +414,10 @@ struct PwStreamDeleter { }; using PwStreamPtr = std::unique_ptr; -/* Enums for bitflags... again... *sigh* */ +/* NOLINTBEGIN(*EnumCastOutOfRange) Enums for bitflags... again... *sigh* */ constexpr pw_stream_flags operator|(pw_stream_flags lhs, pw_stream_flags rhs) noexcept { return static_cast(lhs | al::to_underlying(rhs)); } +/* NOLINTEND(*EnumCastOutOfRange) */ constexpr pw_stream_flags& operator|=(pw_stream_flags &lhs, pw_stream_flags rhs) noexcept { lhs = lhs | rhs; return lhs; } @@ -397,9 +445,11 @@ class ThreadMainloop { explicit operator bool() const noexcept { return mLoop != nullptr; } + [[nodiscard]] auto start() const { return pw_thread_loop_start(mLoop); } auto stop() const { return pw_thread_loop_stop(mLoop); } + [[nodiscard]] auto getLoop() const { return pw_thread_loop_get_loop(mLoop); } auto lock() const { return pw_thread_loop_lock(mLoop); } @@ -407,7 +457,7 @@ class ThreadMainloop { auto signal(bool wait) const { return pw_thread_loop_signal(mLoop, wait); } - auto newContext(pw_properties *props=nullptr, size_t user_data_size=0) + auto newContext(pw_properties *props=nullptr, size_t user_data_size=0) const { return PwContextPtr{pw_context_new(getLoop(), props, user_data_size)}; } static auto Create(const char *name, spa_dict *props=nullptr) @@ -443,8 +493,7 @@ struct NodeProxy { { pw_node_events ret{}; ret.version = PW_VERSION_NODE_EVENTS; - ret.info = [](void *object, const pw_node_info *info) noexcept - { static_cast(object)->infoCallback(info); }; + ret.info = infoCallback; ret.param = [](void *object, int seq, uint32_t id, uint32_t index, uint32_t next, const spa_pod *param) noexcept { static_cast(object)->paramCallback(seq, id, index, next, param); }; return ret; @@ -452,7 +501,7 @@ struct NodeProxy { uint32_t mId{}; - PwNodePtr mNode{}; + PwNodePtr mNode; spa_hook mListener{}; NodeProxy(uint32_t id, PwNodePtr node) @@ -464,16 +513,15 @@ struct NodeProxy { /* 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)); + std::array fmtids{{SPA_PARAM_EnumFormat, SPA_PARAM_Format}}; + ppw_node_subscribe_params(mNode.get(), fmtids.data(), fmtids.size()); } ~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; + static void infoCallback(void *object, const pw_node_info *info) noexcept; + void paramCallback(int seq, uint32_t id, uint32_t index, uint32_t next, const spa_pod *param) const noexcept; }; /* A metadata proxy object used to query the default sink and source. */ @@ -482,14 +530,13 @@ struct MetadataProxy { { 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(object)->propertyCallback(id, key, type, value); }; + ret.property = propertyCallback; return ret; } uint32_t mId{}; - PwMetadataPtr mMetadata{}; + PwMetadataPtr mMetadata; spa_hook mListener{}; MetadataProxy(uint32_t id, PwMetadataPtr mdata) @@ -501,7 +548,8 @@ struct MetadataProxy { ~MetadataProxy() { spa_hook_remove(&mListener); } - int propertyCallback(uint32_t id, const char *key, const char *type, const char *value) noexcept; + static auto propertyCallback(void *object, uint32_t id, const char *key, const char *type, + const char *value) noexcept -> int; }; @@ -509,10 +557,10 @@ struct MetadataProxy { * to objects being added to or removed from the registry. */ struct EventManager { - ThreadMainloop mLoop{}; - PwContextPtr mContext{}; - PwCorePtr mCore{}; - PwRegistryPtr mRegistry{}; + ThreadMainloop mLoop; + PwContextPtr mContext; + PwCorePtr mCore; + PwRegistryPtr mRegistry; spa_hook mRegistryListener{}; spa_hook mCoreListener{}; @@ -541,7 +589,8 @@ struct EventManager { 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 + [[nodiscard]] + auto initIsDone(std::memory_order m=std::memory_order_seq_cst) const noexcept -> bool { return mInitDone.load(m); } /** @@ -647,7 +696,7 @@ struct DeviceNode { void parsePositions(const spa_pod *value, bool force_update) noexcept; void parseChannelCount(const spa_pod *value, bool force_update) noexcept; - void callEvent(alc::EventType type, std::string_view message) + void callEvent(alc::EventType type, std::string_view message) const { /* Source nodes aren't recognized for playback, only Sink and Duplex * nodes are. All node types are recognized for capture. @@ -728,25 +777,32 @@ void DeviceNode::Remove(uint32_t id) } -const spa_audio_channel MonoMap[]{ +constexpr std::array MonoMap{ SPA_AUDIO_CHANNEL_MONO -}, StereoMap[] { +}; +constexpr std::array StereoMap{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR -}, QuadMap[]{ +}; +constexpr std::array QuadMap{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR -}, X51Map[]{ +}; +constexpr std::array X51Map{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR -}, X51RearMap[]{ +}; +constexpr std::array X51RearMap{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR -}, X61Map[]{ +}; +constexpr std::array X61Map{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_RC, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR -}, X71Map[]{ +}; +constexpr std::array X71Map{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR -}, X714Map[]{ +}; +constexpr std::array X714Map{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, SPA_AUDIO_CHANNEL_TFL, SPA_AUDIO_CHANNEL_TFR, SPA_AUDIO_CHANNEL_TRL, SPA_AUDIO_CHANNEL_TRR @@ -756,17 +812,15 @@ const spa_audio_channel MonoMap[]{ * Checks if every channel in 'map1' exists in 'map0' (that is, map0 is equal * to or a superset of map1). */ -template -bool MatchChannelMap(const al::span map0, const spa_audio_channel (&map1)[N]) +bool MatchChannelMap(const al::span map0, + const al::span map1) { - if(map0.size() < N) + if(map0.size() < map1.size()) return false; - for(const spa_audio_channel chid : map1) - { - if(std::find(map0.begin(), map0.end(), chid) == map0.end()) - return false; - } - return true; + + auto find_channel = [map0](const spa_audio_channel chid) -> bool + { return std::find(map0.begin(), map0.end(), chid) != map0.end(); }; + return std::all_of(map1.cbegin(), map1.cend(), find_channel); } void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexcept @@ -794,7 +848,8 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce /* [0] is the default, [1] is the min, and [2] is the max. */ TRACE(" sample rate: %d (range: %d -> %d)\n", srates[0], srates[1], srates[2]); if(!mSampleRate || force_update) - mSampleRate = static_cast(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE)); + mSampleRate = static_cast(std::clamp(srates[0], MinOutputRate, + MaxOutputRate)); return; } @@ -820,7 +875,7 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce */ for(const auto &rate : srates) { - if(rate >= MIN_OUTPUT_RATE && rate <= MAX_OUTPUT_RATE) + if(rate >= int{MinOutputRate} && rate <= int{MaxOutputRate}) { if(!mSampleRate || force_update) mSampleRate = static_cast(rate); @@ -841,7 +896,8 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce TRACE(" sample rate: %d\n", srates[0]); if(!mSampleRate || force_update) - mSampleRate = static_cast(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE)); + mSampleRate = static_cast(std::clamp(srates[0], MinOutputRate, + MaxOutputRate)); return; } @@ -919,15 +975,16 @@ void DeviceNode::parseChannelCount(const spa_pod *value, bool force_update) noex } -constexpr char MonitorPrefix[]{"Monitor of "}; -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/"}; +[[nodiscard]] constexpr auto GetMonitorPrefix() noexcept { return "Monitor of "sv; } +[[nodiscard]] constexpr auto GetMonitorSuffix() noexcept { return ".monitor"sv; } +[[nodiscard]] constexpr auto GetAudioSinkClassName() noexcept { return "Audio/Sink"sv; } +[[nodiscard]] constexpr auto GetAudioSourceClassName() noexcept { return "Audio/Source"sv; } +[[nodiscard]] constexpr auto GetAudioDuplexClassName() noexcept { return "Audio/Duplex"sv; } +[[nodiscard]] constexpr auto GetAudioSourceVirtualClassName() noexcept +{ return "Audio/Source/Virtual"sv; } -void NodeProxy::infoCallback(const pw_node_info *info) noexcept + +void NodeProxy::infoCallback(void*, 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. @@ -940,14 +997,15 @@ void NodeProxy::infoCallback(const pw_node_info *info) noexcept /* Can this actually change? */ const char *media_class{spa_dict_lookup(info->props, PW_KEY_MEDIA_CLASS)}; if(!media_class) UNLIKELY return; + const std::string_view className{media_class}; NodeType ntype{}; - if(al::strcasecmp(media_class, AudioSinkClass) == 0) + if(al::case_compare(className, GetAudioSinkClassName()) == 0) ntype = NodeType::Sink; - else if(al::strcasecmp(media_class, AudioSourceClass) == 0 - || al::strcasecmp(media_class, AudioSourceVirtualClass) == 0) + else if(al::case_compare(className, GetAudioSourceClassName()) == 0 + || al::case_compare(className, GetAudioSourceVirtualClassName()) == 0) ntype = NodeType::Source; - else if(al::strcasecmp(media_class, AudioDuplexClass) == 0) + else if(al::case_compare(className, GetAudioDuplexClassName()) == 0) ntype = NodeType::Duplex; else { @@ -990,12 +1048,17 @@ void NodeProxy::infoCallback(const pw_node_info *info) noexcept /* 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. + * the side of caution: send a DeviceRemoved event if it had a name + * that's being changed, and send a DeviceAdded event when the name + * differs or it didn't have one. + * + * The DeviceRemoved event needs to be called before the potentially + * new NodeType is set, so the removal event is called for the previous + * device type, while the DeviceAdded event needs to be called after. * - * This is overkill if the name or devname can't change. + * This is overkill if the node type, name, and devname can't change. */ + bool notifyAdd{false}; if(node.mName != name) { if(gEventHandler.initIsDone(std::memory_order_relaxed)) @@ -1005,27 +1068,32 @@ void NodeProxy::infoCallback(const pw_node_info *info) noexcept 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); + notifyAdd = true; } node.mName = std::move(name); } node.mDevName = devName ? devName : ""; node.mType = ntype; - node.mIsHeadphones = form_factor && (al::strcasecmp(form_factor, "headphones") == 0 - || al::strcasecmp(form_factor, "headset") == 0); + node.mIsHeadphones = form_factor && (al::case_compare(form_factor, "headphones"sv) == 0 + || al::case_compare(form_factor, "headset"sv) == 0); + if(notifyAdd) + { + const std::string msg{"Device added: "+node.mName}; + node.callEvent(alc::EventType::DeviceAdded, msg); + } } } -void NodeProxy::paramCallback(int, uint32_t id, uint32_t, uint32_t, const spa_pod *param) noexcept +void NodeProxy::paramCallback(int, uint32_t id, uint32_t, uint32_t, const spa_pod *param) const noexcept { 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"); + TRACE("Device ID %" PRIu64 " %s format%s:\n", node->mSerial, + (id == SPA_PARAM_EnumFormat) ? "available" : "current", + (id == SPA_PARAM_EnumFormat) ? "s" : ""); const bool force_update{id == SPA_PARAM_Format}; if(const spa_pod_prop *prop{spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_rate)}) @@ -1033,22 +1101,25 @@ void NodeProxy::paramCallback(int, uint32_t id, uint32_t, uint32_t, const spa_po if(const spa_pod_prop *prop{spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_position)}) node->parsePositions(&prop->value, force_update); - else if((prop=spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_channels)) != nullptr) - node->parseChannelCount(&prop->value, force_update); + else + { + prop = spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_channels); + if(prop) node->parseChannelCount(&prop->value, force_update); + } } } -int MetadataProxy::propertyCallback(uint32_t id, const char *key, const char *type, - const char *value) noexcept +auto MetadataProxy::propertyCallback(void*, uint32_t id, const char *key, const char *type, + const char *value) noexcept -> int { if(id != PW_ID_CORE) return 0; bool isCapture{}; - if(std::strcmp(key, "default.audio.sink") == 0) + if("default.audio.sink"sv == key) isCapture = false; - else if(std::strcmp(key, "default.audio.source") == 0) + else if("default.audio.source"sv == key) isCapture = true; else return 0; @@ -1060,15 +1131,15 @@ int MetadataProxy::propertyCallback(uint32_t id, const char *key, const char *ty else DefaultSourceDevice.clear(); return 0; } - if(std::strcmp(type, "Spa:String:JSON") != 0) + if("Spa:String:JSON"sv != type) { ERR("Unexpected %s property type: %s\n", key, type); return 0; } - spa_json it[2]{}; - spa_json_init(&it[0], value, strlen(value)); - if(spa_json_enter_object(&it[0], &it[1]) <= 0) + std::array it{}; + spa_json_init(it.data(), value, strlen(value)); + if(spa_json_enter_object(&std::get<0>(it), &std::get<1>(it)) <= 0) return 0; auto get_json_string = [](spa_json *iter) @@ -1079,57 +1150,51 @@ int MetadataProxy::propertyCallback(uint32_t id, const char *key, const char *ty int len{spa_json_next(iter, &val)}; if(len <= 0) return str; - str.emplace().resize(static_cast(len), '\0'); - if(spa_json_parse_string(val, len, &str->front()) <= 0) + str.emplace(static_cast(len), '\0'); + if(spa_json_parse_string(val, len, str->data()) <= 0) str.reset(); else while(!str->empty() && str->back() == '\0') str->pop_back(); return str; }; - while(auto propKey = get_json_string(&it[1])) + while(auto propKey = get_json_string(&std::get<1>(it))) { - if(*propKey == "name") + if("name"sv == *propKey) { - auto propValue = get_json_string(&it[1]); + auto propValue = get_json_string(&std::get<1>(it)); if(!propValue) break; TRACE("Got default %s device \"%s\"\n", isCapture ? "capture" : "playback", propValue->c_str()); - if(!isCapture) + if(!isCapture && DefaultSinkDevice != *propValue) { - if(DefaultSinkDevice != *propValue) + if(gEventHandler.mInitDone.load(std::memory_order_relaxed)) { - 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); + auto entry = DeviceNode::FindByDevName(*propValue); + const std::string message{"Default playback device changed: "+ + (entry ? entry->mName : std::string{})}; + alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback, + message); } + DefaultSinkDevice = std::move(*propValue); } - else + else if(isCapture && DefaultSourceDevice != *propValue) { - if(DefaultSourceDevice != *propValue) + if(gEventHandler.mInitDone.load(std::memory_order_relaxed)) { - 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); + auto entry = DeviceNode::FindByDevName(*propValue); + const std::string message{"Default capture device changed: "+ + (entry ? entry->mName : std::string{})}; + alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture, + message); } + DefaultSourceDevice = std::move(*propValue); } } else { const char *v{}; - if(spa_json_next(&it[1], &v) <= 0) + if(spa_json_next(&std::get<1>(it), &v) <= 0) break; } } @@ -1208,16 +1273,16 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t { const char *media_class{spa_dict_lookup(props, PW_KEY_MEDIA_CLASS)}; if(!media_class) return; + const std::string_view className{media_class}; /* Specifically, audio sinks and sources (and duplexes). */ - const bool isGood{al::strcasecmp(media_class, AudioSinkClass) == 0 - || al::strcasecmp(media_class, AudioSourceClass) == 0 - || al::strcasecmp(media_class, AudioSourceVirtualClass) == 0 - || al::strcasecmp(media_class, AudioDuplexClass) == 0}; + const bool isGood{al::case_compare(className, GetAudioSinkClassName()) == 0 + || al::case_compare(className, GetAudioSourceClassName()) == 0 + || al::case_compare(className, GetAudioSourceVirtualClassName()) == 0 + || al::case_compare(className, GetAudioDuplexClassName()) == 0}; if(!isGood) { - if(std::strstr(media_class, "/Video") == nullptr - && std::strncmp(media_class, StreamClass, sizeof(StreamClass)-1) != 0) + if(!al::contains(className, "/Video"sv) && !al::starts_with(className, "Stream/"sv)) TRACE("Ignoring node class %s\n", media_class); return; } @@ -1248,7 +1313,7 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t const char *data_class{spa_dict_lookup(props, PW_KEY_METADATA_NAME)}; if(!data_class) return; - if(std::strcmp(data_class, "default") != 0) + if("default"sv != data_class) { TRACE("Ignoring metadata \"%s\"\n", data_class); return; @@ -1337,6 +1402,7 @@ spa_audio_info_raw make_spa_info(DeviceBase *device, bool is51rear, use_f32p_e u case DevFmtX71: map = X71Map; break; case DevFmtX714: map = X714Map; break; case DevFmtX3D71: map = X71Map; break; + case DevFmtX7144: case DevFmtAmbi3D: info.flags |= SPA_AUDIO_FLAG_UNPOSITIONED; info.channels = device->channelsFromFmt(); @@ -1345,7 +1411,7 @@ spa_audio_info_raw make_spa_info(DeviceBase *device, bool is51rear, use_f32p_e u if(!map.empty()) { info.channels = static_cast(map.size()); - std::copy(map.begin(), map.end(), info.position); + std::copy(map.begin(), map.end(), std::begin(info.position)); } return info; @@ -1370,8 +1436,7 @@ class PipeWirePlayback final : public BackendBase { PwStreamPtr mStream; spa_hook mStreamListener{}; spa_io_rate_match *mRateMatch{}; - std::unique_ptr mChannelPtrs; - uint mNumChannels{}; + std::vector mChannelPtrs; static constexpr pw_stream_events CreateEvents() { @@ -1388,13 +1453,11 @@ class PipeWirePlayback final : public BackendBase { public: PipeWirePlayback(DeviceBase *device) noexcept : BackendBase{device} { } - ~PipeWirePlayback() + ~PipeWirePlayback() final { /* Stop the mainloop so the stream can be properly destroyed. */ if(mLoop) mLoop.stop(); } - - DEF_NEWDEL(PipeWirePlayback) }; @@ -1408,6 +1471,8 @@ void PipeWirePlayback::ioChangedCallback(uint32_t id, void *area, uint32_t size) case SPA_IO_RateMatch: if(size >= sizeof(spa_io_rate_match)) mRateMatch = static_cast(area); + else + mRateMatch = nullptr; break; } } @@ -1418,7 +1483,7 @@ void PipeWirePlayback::outputCallback() noexcept if(!pw_buf) UNLIKELY return; const al::span datas{pw_buf->buffer->datas, - minu(mNumChannels, pw_buf->buffer->n_datas)}; + std::min(mChannelPtrs.size(), size_t{pw_buf->buffer->n_datas})}; #if PW_CHECK_VERSION(0,3,49) /* In 0.3.49, pw_buffer::requested specifies the number of samples needed * by the resampler/graph for this audio update. @@ -1438,11 +1503,11 @@ void PipeWirePlayback::outputCallback() noexcept * buffer length in any one channel is smaller than we wanted (shouldn't * be, but just in case). */ - float **chanptr_end{mChannelPtrs.get()}; + auto chanptr_end = mChannelPtrs.begin(); for(const auto &data : datas) { - length = minu(length, data.maxsize/sizeof(float)); - *chanptr_end = static_cast(data.data); + length = std::min(length, data.maxsize/uint{sizeof(float)}); + *chanptr_end = data.data; ++chanptr_end; data.chunk->offset = 0; @@ -1450,7 +1515,7 @@ void PipeWirePlayback::outputCallback() noexcept data.chunk->size = length * sizeof(float); } - mDevice->renderSamples({mChannelPtrs.get(), chanptr_end}, length); + mDevice->renderSamples(mChannelPtrs, length); pw_buf->size = length; pw_stream_queue_buffer(mStream.get(), pw_buf); @@ -1466,7 +1531,7 @@ void PipeWirePlayback::open(std::string_view name) gEventHandler.waitForInit(); if(name.empty()) { - EventWatcherLockGuard _{gEventHandler}; + EventWatcherLockGuard evtlock{gEventHandler}; auto&& devlist = DeviceNode::GetList(); auto match = devlist.cend(); @@ -1491,15 +1556,15 @@ void PipeWirePlayback::open(std::string_view name) } else { - EventWatcherLockGuard _{gEventHandler}; + EventWatcherLockGuard evtlock{gEventHandler}; auto&& devlist = DeviceNode::GetList(); auto match_name = [name](const DeviceNode &n) -> bool - { return n.mType != NodeType::Source && n.mName == name; }; + { return n.mType != NodeType::Source && (n.mName == name || n.mDevName == 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", static_cast(name.length()), name.data()}; + "Device name \"%.*s\" not found", al::sizei(name), name.data()}; targetid = match->mSerial; devname = match->mName; @@ -1539,21 +1604,21 @@ void PipeWirePlayback::open(std::string_view name) mTargetId = targetid; if(!devname.empty()) - mDevice->DeviceName = std::move(devname); + mDeviceName = std::move(devname); else - mDevice->DeviceName = pwireDevice; + mDeviceName = "PipeWire Output"sv; } bool PipeWirePlayback::reset() { if(mStream) { - MainloopLockGuard _{mLoop}; + MainloopLockGuard looplock{mLoop}; mStream = nullptr; } mStreamListener = {}; mRateMatch = nullptr; - mTimeBase = GetDeviceClockTime(mDevice); + mTimeBase = mDevice->getClockTime(); /* If connecting to a specific device, update various device parameters to * match its format. @@ -1562,7 +1627,7 @@ bool PipeWirePlayback::reset() mDevice->Flags.reset(DirectEar); if(mTargetId != PwIdAny) { - EventWatcherLockGuard _{gEventHandler}; + EventWatcherLockGuard evtlock{gEventHandler}; auto&& devlist = DeviceNode::GetList(); auto match_id = [targetid=mTargetId](const DeviceNode &n) -> bool @@ -1574,11 +1639,12 @@ bool PipeWirePlayback::reset() { /* Scale the update size if the sample rate changes. */ const double scale{static_cast(match->mSampleRate) / mDevice->Frequency}; - const double numbufs{static_cast(mDevice->BufferSize)/mDevice->UpdateSize}; + const double updatesize{std::round(mDevice->UpdateSize * scale)}; + const double buffersize{std::round(mDevice->BufferSize * scale)}; + mDevice->Frequency = match->mSampleRate; - mDevice->UpdateSize = static_cast(clampd(mDevice->UpdateSize*scale + 0.5, - 64.0, 8192.0)); - mDevice->BufferSize = static_cast(numbufs*mDevice->UpdateSize + 0.5); + mDevice->UpdateSize = static_cast(std::clamp(updatesize, 64.0, 8192.0)); + mDevice->BufferSize = static_cast(std::max(buffersize, 128.0)); } if(!mDevice->Flags.test(ChannelsRequest) && match->mChannels != InvalidChannelConfig) mDevice->FmtChans = match->mChannels; @@ -1590,16 +1656,10 @@ bool PipeWirePlayback::reset() /* Force planar 32-bit float output for playback. This is what PipeWire * handles internally, and it's easier for us too. */ - spa_audio_info_raw info{make_spa_info(mDevice, is51rear, ForceF32Planar)}; + auto info = spa_audio_info_raw{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(pod_buffer_size); - spa_pod_builder b{make_pod_builder(pod_buffer.get(), pod_buffer_size)}; - - const spa_pod *params{spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info)}; + auto b = PodDynamicBuilder{}; + auto params = as_const_ptr(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"}; @@ -1608,7 +1668,7 @@ bool PipeWirePlayback::reset() * be useful? */ auto&& binary = GetProcBinary(); - const char *appname{binary.fname.length() ? binary.fname.c_str() : "OpenAL Soft"}; + const char *appname{!binary.fname.empty() ? binary.fname.c_str() : "OpenAL Soft"}; pw_properties *props{pw_properties_new(PW_KEY_NODE_NAME, appname, PW_KEY_NODE_DESCRIPTION, appname, PW_KEY_MEDIA_TYPE, "Audio", @@ -1640,7 +1700,7 @@ bool PipeWirePlayback::reset() pw_stream_flags flags{PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE | PW_STREAM_FLAG_MAP_BUFFERS}; - if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pipewire", "rt-mix", true)) + if(GetConfigValueBool(mDevice->mDeviceName, "pipewire", "rt-mix", false)) flags |= PW_STREAM_FLAG_RT_PROCESS; if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_OUTPUT, PwIdAny, flags, ¶ms, 1)}) throw al::backend_exception{al::backend_error::DeviceError, @@ -1665,8 +1725,7 @@ bool PipeWirePlayback::reset() */ plock.unlock(); - mNumChannels = mDevice->channelsFromFmt(); - mChannelPtrs = std::make_unique(mNumChannels); + mChannelPtrs.resize(mDevice->channelsFromFmt()); setDefaultWFXChannelOrder(); @@ -1724,7 +1783,7 @@ void PipeWirePlayback::start() mDevice->UpdateSize = updatesize; mDevice->BufferSize = static_cast(ptime.buffered + delay + - totalbuffers*updatesize); + uint64_t{totalbuffers}*updatesize); break; } #else @@ -1758,8 +1817,7 @@ void PipeWirePlayback::stop() { MainloopUniqueLock plock{mLoop}; if(int res{pw_stream_set_active(mStream.get(), false)}) - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to stop PipeWire stream (res: %d)", res}; + ERR("Failed to stop PipeWire stream (res: %d)\n", res); /* Wait for the stream to stop playing. */ plock.wait([stream=mStream.get()]() @@ -1778,7 +1836,7 @@ ClockLatency PipeWirePlayback::getClockLatency() pw_time ptime{}; if(mStream) { - MainloopLockGuard _{mLoop}; + MainloopLockGuard looplock{mLoop}; if(int res{pw_stream_get_time_n(mStream.get(), &ptime, sizeof(ptime))}) ERR("Failed to get PipeWire stream time (res: %d)\n", res); } @@ -1791,10 +1849,10 @@ ClockLatency PipeWirePlayback::getClockLatency() uint refcount; do { refcount = mDevice->waitForMix(); - mixtime = GetDeviceClockTime(mDevice); + mixtime = mDevice->getClockTime(); clock_gettime(CLOCK_MONOTONIC, &tspec); std::atomic_thread_fence(std::memory_order_acquire); - } while(refcount != ReadRef(mDevice->MixCount)); + } while(refcount != mDevice->mMixCount.load(std::memory_order_relaxed)); /* Convert the monotonic clock, stream ticks, and stream delay to * nanoseconds. @@ -1868,7 +1926,7 @@ class PipeWireCapture final : public BackendBase { PwStreamPtr mStream; spa_hook mStreamListener{}; - RingBufferPtr mRing{}; + RingBufferPtr mRing; static constexpr pw_stream_events CreateEvents() { @@ -1883,9 +1941,7 @@ class PipeWireCapture final : public BackendBase { public: PipeWireCapture(DeviceBase *device) noexcept : BackendBase{device} { } - ~PipeWireCapture() { if(mLoop) mLoop.stop(); } - - DEF_NEWDEL(PipeWireCapture) + ~PipeWireCapture() final { if(mLoop) mLoop.stop(); } }; @@ -1898,10 +1954,11 @@ void PipeWireCapture::inputCallback() noexcept if(!pw_buf) UNLIKELY return; spa_data *bufdata{pw_buf->buffer->datas}; - const uint offset{minu(bufdata->chunk->offset, bufdata->maxsize)}; - const uint size{minu(bufdata->chunk->size, bufdata->maxsize - offset)}; + const uint offset{bufdata->chunk->offset % bufdata->maxsize}; + const auto input = al::span{static_cast(bufdata->data), bufdata->maxsize} + .subspan(offset, std::min(bufdata->chunk->size, bufdata->maxsize - offset)); - mRing->write(static_cast(bufdata->data) + offset, size / mRing->getElemSize()); + std::ignore = mRing->write(input.data(), input.size() / mRing->getElemSize()); pw_stream_queue_buffer(mStream.get(), pw_buf); } @@ -1916,7 +1973,7 @@ void PipeWireCapture::open(std::string_view name) gEventHandler.waitForInit(); if(name.empty()) { - EventWatcherLockGuard _{gEventHandler}; + EventWatcherLockGuard evtlock{gEventHandler}; auto&& devlist = DeviceNode::GetList(); auto match = devlist.cend(); @@ -1942,30 +1999,39 @@ void PipeWireCapture::open(std::string_view name) targetid = match->mSerial; if(match->mType != NodeType::Sink) devname = match->mName; - else devname = MonitorPrefix+match->mName; + else devname = std::string{GetMonitorPrefix()}+match->mName; } else { - EventWatcherLockGuard _{gEventHandler}; + EventWatcherLockGuard evtlock{gEventHandler}; auto&& devlist = DeviceNode::GetList(); + const std::string_view prefix{GetMonitorPrefix()}; + const std::string_view suffix{GetMonitorSuffix()}; 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() && name.length() >= MonitorPrefixLen - && std::strncmp(name.data(), MonitorPrefix, MonitorPrefixLen) == 0) + if(match == devlist.cend() && al::starts_with(name, prefix)) { - const std::string_view sinkname{name.substr(MonitorPrefixLen)}; + const std::string_view sinkname{name.substr(prefix.length())}; 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); } + else if(match == devlist.cend() && al::ends_with(name, suffix)) + { + const std::string_view sinkname{name.substr(0, name.size()-suffix.size())}; + auto match_sinkname = [sinkname](const DeviceNode &n) -> bool + { return n.mType == NodeType::Sink && n.mDevName == 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", static_cast(name.length()), name.data()}; + "Device name \"%.*s\" not found", al::sizei(name), name.data()}; targetid = match->mSerial; - devname = name; + if(match->mType != NodeType::Sink) devname = match->mName; + else devname = std::string{GetMonitorPrefix()}+match->mName; } if(!mLoop) @@ -2002,15 +2068,15 @@ void PipeWireCapture::open(std::string_view name) mTargetId = targetid; if(!devname.empty()) - mDevice->DeviceName = std::move(devname); + mDeviceName = std::move(devname); else - mDevice->DeviceName = pwireInput; + mDeviceName = "PipeWire Input"sv; bool is51rear{false}; if(mTargetId != PwIdAny) { - EventWatcherLockGuard _{gEventHandler}; + EventWatcherLockGuard evtlock{gEventHandler}; auto&& devlist = DeviceNode::GetList(); auto match_id = [targetid=mTargetId](const DeviceNode &n) -> bool @@ -2019,19 +2085,16 @@ void PipeWireCapture::open(std::string_view name) if(match != devlist.cend()) is51rear = match->mIs51Rear; } - spa_audio_info_raw info{make_spa_info(mDevice, is51rear, UseDevType)}; - - constexpr uint32_t pod_buffer_size{1024}; - auto pod_buffer = std::make_unique(pod_buffer_size); - spa_pod_builder b{make_pod_builder(pod_buffer.get(), pod_buffer_size)}; + auto info = spa_audio_info_raw{make_spa_info(mDevice, is51rear, UseDevType)}; - const spa_pod *params[]{spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info)}; - if(!params[0]) + auto b = PodDynamicBuilder{}; + auto params = as_const_ptr(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"}; auto&& binary = GetProcBinary(); - const char *appname{binary.fname.length() ? binary.fname.c_str() : "OpenAL Soft"}; + const char *appname{!binary.fname.empty() ? binary.fname.c_str() : "OpenAL Soft"}; pw_properties *props{pw_properties_new( PW_KEY_NODE_NAME, appname, PW_KEY_NODE_DESCRIPTION, appname, @@ -2067,7 +2130,7 @@ void PipeWireCapture::open(std::string_view name) constexpr pw_stream_flags Flags{PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS}; - if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_INPUT, PwIdAny, Flags, params, 1)}) + if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_INPUT, PwIdAny, Flags, ¶ms, 1)}) throw al::backend_exception{al::backend_error::DeviceError, "Error connecting PipeWire stream (res: %d)", res}; @@ -2086,7 +2149,7 @@ void PipeWireCapture::open(std::string_view name) setDefaultWFXChannelOrder(); /* Ensure at least a 100ms capture buffer. */ - mRing = RingBuffer::Create(maxu(mDevice->Frequency/10, mDevice->BufferSize), + mRing = RingBuffer::Create(std::max(mDevice->Frequency/10u, mDevice->BufferSize), mDevice->frameSizeFromFmt(), false); } @@ -2113,8 +2176,7 @@ void PipeWireCapture::stop() { MainloopUniqueLock plock{mLoop}; if(int res{pw_stream_set_active(mStream.get(), false)}) - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to stop PipeWire stream (res: %d)", res}; + ERR("Failed to stop PipeWire stream (res: %d)\n", res); plock.wait([stream=mStream.get()]() { return pw_stream_get_state(stream, nullptr) != PW_STREAM_STATE_STREAMING; }); @@ -2124,7 +2186,7 @@ uint PipeWireCapture::availableSamples() { return static_cast(mRing->readSpace()); } void PipeWireCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } } // namespace @@ -2143,11 +2205,11 @@ bool PipeWireBackendFactory::init() } TRACE("Found PipeWire version \"%s\" (%s or newer)\n", version, pw_get_headers_version()); - pw_init(0, nullptr); + pw_init(nullptr, nullptr); if(!gEventHandler.init()) return false; - if(!GetConfigValueBool(nullptr, "pipewire", "assume-audio", false) + if(!GetConfigValueBool({}, "pipewire", "assume-audio", false) && !gEventHandler.waitForAudio()) { gEventHandler.kill(); @@ -2163,12 +2225,12 @@ bool PipeWireBackendFactory::init() bool PipeWireBackendFactory::querySupport(BackendType type) { return type == BackendType::Playback || type == BackendType::Capture; } -std::string PipeWireBackendFactory::probe(BackendType type) +auto PipeWireBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; + std::vector outnames; gEventHandler.waitForInit(); - EventWatcherLockGuard _{gEventHandler}; + EventWatcherLockGuard evtlock{gEventHandler}; auto&& devlist = DeviceNode::GetList(); auto match_defsink = [](const DeviceNode &n) -> bool @@ -2186,31 +2248,31 @@ std::string PipeWireBackendFactory::probe(BackendType type) case BackendType::Playback: defmatch = std::find_if(defmatch, devlist.cend(), match_defsink); if(defmatch != devlist.cend()) - { - /* Includes null char. */ - outnames.append(defmatch->mName.c_str(), defmatch->mName.length()+1); - } + outnames.emplace_back(defmatch->mName); for(auto iter = devlist.cbegin();iter != devlist.cend();++iter) { if(iter != defmatch && iter->mType != NodeType::Source) - outnames.append(iter->mName.c_str(), iter->mName.length()+1); + outnames.emplace_back(iter->mName); } break; case BackendType::Capture: + outnames.reserve(devlist.size()); defmatch = std::find_if(defmatch, devlist.cend(), match_defsource); if(defmatch != devlist.cend()) { if(defmatch->mType == NodeType::Sink) - outnames.append(MonitorPrefix); - outnames.append(defmatch->mName.c_str(), defmatch->mName.length()+1); + outnames.emplace_back(std::string{GetMonitorPrefix()}+defmatch->mName); + else + outnames.emplace_back(defmatch->mName); } for(auto iter = devlist.cbegin();iter != devlist.cend();++iter) { if(iter != defmatch) { if(iter->mType == NodeType::Sink) - outnames.append(MonitorPrefix); - outnames.append(iter->mName.c_str(), iter->mName.length()+1); + outnames.emplace_back(std::string{GetMonitorPrefix()}+iter->mName); + else + outnames.emplace_back(iter->mName); } } break; @@ -2219,6 +2281,7 @@ std::string PipeWireBackendFactory::probe(BackendType type) return outnames; } + BackendPtr PipeWireBackendFactory::createBackend(DeviceBase *device, BackendType type) { if(type == BackendType::Playback) @@ -2233,3 +2296,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/3rdparty/openal/alc/backends/pipewire.h b/3rdparty/openal/alc/backends/pipewire.h index 5f930239c607..567d18eddd0a 100644 --- a/3rdparty/openal/alc/backends/pipewire.h +++ b/3rdparty/openal/alc/backends/pipewire.h @@ -2,22 +2,26 @@ #define BACKENDS_PIPEWIRE_H #include +#include +#include "alc/events.h" #include "base.h" struct DeviceBase; struct PipeWireBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto queryEventSupport(alc::EventType eventType, BackendType type) -> alc::EventSupport final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - static BackendFactory &getFactory(); + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; + + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_PIPEWIRE_H */ diff --git a/3rdparty/openal/alc/backends/portaudio.cpp b/3rdparty/openal/alc/backends/portaudio.cpp index 979a54d616bc..67641fee2158 100644 --- a/3rdparty/openal/alc/backends/portaudio.cpp +++ b/3rdparty/openal/alc/backends/portaudio.cpp @@ -22,26 +22,23 @@ #include "portaudio.h" +#include #include #include #include -#include "albit.h" #include "alc/alconfig.h" -#include "alnumeric.h" +#include "alstring.h" #include "core/device.h" #include "core/logging.h" #include "dynload.h" #include "ringbuffer.h" -#include +#include /* NOLINT(*-duplicate-include) Not the same header. */ namespace { -constexpr char pa_device[] = "PortAudio Default"; - - #ifdef HAVE_DYNLOAD void *pa_handle; #define MAKE_FUNC(x) decltype(x) * p##x @@ -52,6 +49,8 @@ MAKE_FUNC(Pa_StartStream); MAKE_FUNC(Pa_StopStream); MAKE_FUNC(Pa_OpenStream); MAKE_FUNC(Pa_CloseStream); +MAKE_FUNC(Pa_GetDeviceCount); +MAKE_FUNC(Pa_GetDeviceInfo); MAKE_FUNC(Pa_GetDefaultOutputDevice); MAKE_FUNC(Pa_GetDefaultInputDevice); MAKE_FUNC(Pa_GetStreamInfo); @@ -65,12 +64,47 @@ MAKE_FUNC(Pa_GetStreamInfo); #define Pa_StopStream pPa_StopStream #define Pa_OpenStream pPa_OpenStream #define Pa_CloseStream pPa_CloseStream +#define Pa_GetDeviceCount pPa_GetDeviceCount +#define Pa_GetDeviceInfo pPa_GetDeviceInfo #define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice #define Pa_GetDefaultInputDevice pPa_GetDefaultInputDevice #define Pa_GetStreamInfo pPa_GetStreamInfo #endif #endif +struct DeviceEntry { + std::string mName; + uint mPlaybackChannels{}; + uint mCaptureChannels{}; +}; +std::vector DeviceNames; + +void EnumerateDevices() +{ + const auto devcount = Pa_GetDeviceCount(); + if(devcount < 0) + { + ERR("Error getting device count: %s\n", Pa_GetErrorText(devcount)); + return; + } + + std::vector(static_cast(devcount)).swap(DeviceNames); + PaDeviceIndex idx{0}; + for(auto &entry : DeviceNames) + { + if(auto info = Pa_GetDeviceInfo(idx); info && info->name) + { + entry.mName = info->name; + entry.mPlaybackChannels = static_cast(std::max(info->maxOutputChannels, 0)); + entry.mCaptureChannels = static_cast(std::max(info->maxInputChannels, 0)); + TRACE("Device %d \"%s\": %d playback, %d capture channels\n", idx, entry.mName.c_str(), + info->maxOutputChannels, info->maxInputChannels); + } + ++idx; + } +} + +struct StreamParamsExt : public PaStreamParameters { uint updateSize; }; struct PortPlayback final : public BackendBase { PortPlayback(DeviceBase *device) noexcept : BackendBase{device} { } @@ -78,13 +112,8 @@ struct PortPlayback final : public BackendBase { int writeCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) noexcept; - static int writeCallbackC(const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, - const PaStreamCallbackFlags statusFlags, void *userData) noexcept - { - return static_cast(userData)->writeCallback(inputBuffer, outputBuffer, - framesPerBuffer, timeInfo, statusFlags); - } + + void createStream(PaDeviceIndex deviceid); void open(std::string_view name) override; bool reset() override; @@ -92,10 +121,8 @@ struct PortPlayback final : public BackendBase { void stop() override; PaStream *mStream{nullptr}; - PaStreamParameters mParams{}; - uint mUpdateSize{0u}; - - DEF_NEWDEL(PortPlayback) + StreamParamsExt mParams{}; + PaDeviceIndex mDeviceIdx{-1}; }; PortPlayback::~PortPlayback() @@ -116,100 +143,134 @@ int PortPlayback::writeCallback(const void*, void *outputBuffer, unsigned long f } -void PortPlayback::open(std::string_view name) +void PortPlayback::createStream(PaDeviceIndex deviceid) { - if(name.empty()) - name = pa_device; - else if(name != pa_device) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; - - PaStreamParameters params{}; - auto devidopt = ConfigValueInt(nullptr, "port", "device"); - if(devidopt && *devidopt >= 0) params.device = *devidopt; - else params.device = Pa_GetDefaultOutputDevice(); + auto &devinfo = DeviceNames.at(static_cast(deviceid)); + + auto params = StreamParamsExt{}; + params.device = deviceid; params.suggestedLatency = mDevice->BufferSize / static_cast(mDevice->Frequency); params.hostApiSpecificStreamInfo = nullptr; - - params.channelCount = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2); - + params.channelCount = static_cast(std::min(devinfo.mPlaybackChannels, + mDevice->channelsFromFmt())); switch(mDevice->FmtType) { - case DevFmtByte: - params.sampleFormat = paInt8; - break; - case DevFmtUByte: - params.sampleFormat = paUInt8; - break; - case DevFmtUShort: - /* fall-through */ - case DevFmtShort: - params.sampleFormat = paInt16; - break; - case DevFmtUInt: - /* fall-through */ - case DevFmtInt: - params.sampleFormat = paInt32; - break; - case DevFmtFloat: - params.sampleFormat = paFloat32; - break; + case DevFmtByte: params.sampleFormat = paInt8; break; + case DevFmtUByte: params.sampleFormat = paUInt8; break; + case DevFmtUShort: [[fallthrough]]; + case DevFmtShort: params.sampleFormat = paInt16; break; + case DevFmtUInt: [[fallthrough]]; + case DevFmtInt: params.sampleFormat = paInt32; break; + case DevFmtFloat: params.sampleFormat = paFloat32; break; } + params.updateSize = mDevice->UpdateSize; -retry_open: - PaStream *stream{}; - PaError err{Pa_OpenStream(&stream, nullptr, ¶ms, mDevice->Frequency, mDevice->UpdateSize, - paNoFlag, &PortPlayback::writeCallbackC, this)}; - if(err != paNoError) + auto srate = uint{mDevice->Frequency}; + + static constexpr auto writeCallback = [](const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, + const PaStreamCallbackFlags statusFlags, void *userData) noexcept { - if(params.sampleFormat == paFloat32) - { + return static_cast(userData)->writeCallback(inputBuffer, outputBuffer, + framesPerBuffer, timeInfo, statusFlags); + }; + while(PaError err{Pa_OpenStream(&mStream, nullptr, ¶ms, srate, params.updateSize, paNoFlag, + writeCallback, this)}) + { + if(params.updateSize != DefaultUpdateSize) + params.updateSize = DefaultUpdateSize; + else if(srate != 48000u) + srate = (srate != 44100u) ? 44100u : 48000u; + else if(params.sampleFormat != paInt16) params.sampleFormat = paInt16; - goto retry_open; - } - throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s", - Pa_GetErrorText(err)}; + else if(params.channelCount != 2) + params.channelCount = 2; + else + throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s", + Pa_GetErrorText(err)}; } - Pa_CloseStream(mStream); - mStream = stream; mParams = params; - mUpdateSize = mDevice->UpdateSize; +} - mDevice->DeviceName = name; +void PortPlayback::open(std::string_view name) +{ + if(DeviceNames.empty()) + EnumerateDevices(); + + PaDeviceIndex deviceid{-1}; + if(name.empty()) + { + if(auto devidopt = ConfigValueInt({}, "port", "device")) + deviceid = *devidopt; + if(deviceid < 0 || static_cast(deviceid) >= DeviceNames.size()) + deviceid = Pa_GetDefaultOutputDevice(); + name = DeviceNames.at(static_cast(deviceid)).mName; + } + else + { + auto iter = std::find_if(DeviceNames.cbegin(), DeviceNames.cend(), + [name](const DeviceEntry &entry) + { return entry.mPlaybackChannels > 0 && name == entry.mName; }); + if(iter == DeviceNames.cend()) + throw al::backend_exception{al::backend_error::NoDevice, + "Device name \"%.*s\" not found", al::sizei(name), name.data()}; + deviceid = static_cast(std::distance(DeviceNames.cbegin(), iter)); + } + + createStream(deviceid); + mDeviceIdx = deviceid; + + mDeviceName = name; } bool PortPlayback::reset() { - const PaStreamInfo *streamInfo{Pa_GetStreamInfo(mStream)}; - mDevice->Frequency = static_cast(streamInfo->sampleRate); - mDevice->UpdateSize = mUpdateSize; - - if(mParams.sampleFormat == paInt8) - mDevice->FmtType = DevFmtByte; - else if(mParams.sampleFormat == paUInt8) - mDevice->FmtType = DevFmtUByte; - else if(mParams.sampleFormat == paInt16) - mDevice->FmtType = DevFmtShort; - else if(mParams.sampleFormat == paInt32) - mDevice->FmtType = DevFmtInt; - else if(mParams.sampleFormat == paFloat32) - mDevice->FmtType = DevFmtFloat; - else + if(mStream) { - ERR("Unexpected sample format: 0x%lx\n", mParams.sampleFormat); - return false; + auto err = Pa_CloseStream(mStream); + if(err != paNoError) + ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); + mStream = nullptr; } - if(mParams.channelCount >= 2) - mDevice->FmtChans = DevFmtStereo; - else if(mParams.channelCount == 1) - mDevice->FmtChans = DevFmtMono; - else + createStream(mDeviceIdx); + + switch(mParams.sampleFormat) { - ERR("Unexpected channel count: %u\n", mParams.channelCount); - return false; + case paFloat32: mDevice->FmtType = DevFmtFloat; break; + case paInt32: mDevice->FmtType = DevFmtInt; break; + case paInt16: mDevice->FmtType = DevFmtShort; break; + case paInt8: mDevice->FmtType = DevFmtByte; break; + case paUInt8: mDevice->FmtType = DevFmtUByte; break; + default: + ERR("Unexpected PortAudio sample format: %lu\n", mParams.sampleFormat); + throw al::backend_exception{al::backend_error::NoDevice, "Invalid sample format: %lu", + mParams.sampleFormat}; + } + + if(mParams.channelCount != static_cast(mDevice->channelsFromFmt())) + { + if(mParams.channelCount >= 2) + mDevice->FmtChans = DevFmtStereo; + else if(mParams.channelCount == 1) + mDevice->FmtChans = DevFmtMono; + mDevice->mAmbiOrder = 0; } + + const PaStreamInfo *streamInfo{Pa_GetStreamInfo(mStream)}; + mDevice->Frequency = static_cast(std::lround(streamInfo->sampleRate)); + mDevice->UpdateSize = mParams.updateSize; + mDevice->BufferSize = mDevice->UpdateSize * 2; + if(streamInfo->outputLatency > 0.0f) + { + const double sampleLatency{streamInfo->outputLatency * streamInfo->sampleRate}; + TRACE("Reported stream latency: %f sec (%f samples)\n", streamInfo->outputLatency, + sampleLatency); + mDevice->BufferSize = static_cast(std::clamp(sampleLatency, + double(mDevice->BufferSize), double{std::numeric_limits::max()})); + } + setDefaultChannelOrder(); return true; @@ -217,16 +278,14 @@ bool PortPlayback::reset() void PortPlayback::start() { - const PaError err{Pa_StartStream(mStream)}; - if(err == paNoError) + if(const PaError err{Pa_StartStream(mStream)}; err != paNoError) throw al::backend_exception{al::backend_error::DeviceError, "Failed to start playback: %s", Pa_GetErrorText(err)}; } void PortPlayback::stop() { - PaError err{Pa_StopStream(mStream)}; - if(err != paNoError) + if(PaError err{Pa_StopStream(mStream)}; err != paNoError) ERR("Error stopping stream: %s\n", Pa_GetErrorText(err)); } @@ -236,14 +295,7 @@ struct PortCapture final : public BackendBase { ~PortCapture() override; int readCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) noexcept; - static int readCallbackC(const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, - const PaStreamCallbackFlags statusFlags, void *userData) noexcept - { - return static_cast(userData)->readCallback(inputBuffer, outputBuffer, - framesPerBuffer, timeInfo, statusFlags); - } + const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) const noexcept; void open(std::string_view name) override; void start() override; @@ -252,11 +304,9 @@ struct PortCapture final : public BackendBase { uint availableSamples() override; PaStream *mStream{nullptr}; - PaStreamParameters mParams; + PaStreamParameters mParams{}; RingBufferPtr mRing{nullptr}; - - DEF_NEWDEL(PortCapture) }; PortCapture::~PortCapture() @@ -269,30 +319,44 @@ PortCapture::~PortCapture() int PortCapture::readCallback(const void *inputBuffer, void*, unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo*, const PaStreamCallbackFlags) noexcept + const PaStreamCallbackTimeInfo*, const PaStreamCallbackFlags) const noexcept { - mRing->write(inputBuffer, framesPerBuffer); + std::ignore = mRing->write(inputBuffer, framesPerBuffer); return 0; } void PortCapture::open(std::string_view name) { + if(DeviceNames.empty()) + EnumerateDevices(); + + int deviceid{}; if(name.empty()) - name = pa_device; - else if(name != pa_device) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + { + if(auto devidopt = ConfigValueInt({}, "port", "capture")) + deviceid = *devidopt; + if(deviceid < 0 || static_cast(deviceid) >= DeviceNames.size()) + deviceid = Pa_GetDefaultInputDevice(); + name = DeviceNames.at(static_cast(deviceid)).mName; + } + else + { + auto iter = std::find_if(DeviceNames.cbegin(), DeviceNames.cend(), + [name](const DeviceEntry &entry) + { return entry.mCaptureChannels > 0 && name == entry.mName; }); + if(iter == DeviceNames.cend()) + throw al::backend_exception{al::backend_error::NoDevice, + "Device name \"%.*s\" not found", al::sizei(name), name.data()}; + deviceid = static_cast(std::distance(DeviceNames.cbegin(), iter)); + } - uint samples{mDevice->BufferSize}; - samples = maxu(samples, 100 * mDevice->Frequency / 1000); - uint frame_size{mDevice->frameSizeFromFmt()}; + const uint samples{std::max(mDevice->BufferSize, mDevice->Frequency/10u)}; + const uint frame_size{mDevice->frameSizeFromFmt()}; mRing = RingBuffer::Create(samples, frame_size, false); - auto devidopt = ConfigValueInt(nullptr, "port", "capture"); - if(devidopt && *devidopt >= 0) mParams.device = *devidopt; - else mParams.device = Pa_GetDefaultOutputDevice(); + mParams.device = deviceid; mParams.suggestedLatency = 0.0f; mParams.hostApiSpecificStreamInfo = nullptr; @@ -320,28 +384,33 @@ void PortCapture::open(std::string_view name) } mParams.channelCount = static_cast(mDevice->channelsFromFmt()); + static constexpr auto readCallback = [](const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, + const PaStreamCallbackFlags statusFlags, void *userData) noexcept + { + return static_cast(userData)->readCallback(inputBuffer, outputBuffer, + framesPerBuffer, timeInfo, statusFlags); + }; PaError err{Pa_OpenStream(&mStream, &mParams, nullptr, mDevice->Frequency, - paFramesPerBufferUnspecified, paNoFlag, &PortCapture::readCallbackC, this)}; + paFramesPerBufferUnspecified, paNoFlag, readCallback, this)}; if(err != paNoError) throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s", Pa_GetErrorText(err)}; - mDevice->DeviceName = name; + mDeviceName = name; } void PortCapture::start() { - const PaError err{Pa_StartStream(mStream)}; - if(err != paNoError) + if(const PaError err{Pa_StartStream(mStream)}; err != paNoError) throw al::backend_exception{al::backend_error::DeviceError, "Failed to start recording: %s", Pa_GetErrorText(err)}; } void PortCapture::stop() { - PaError err{Pa_StopStream(mStream)}; - if(err != paNoError) + if(PaError err{Pa_StopStream(mStream)}; err != paNoError) ERR("Error stopping stream: %s\n", Pa_GetErrorText(err)); } @@ -350,15 +419,13 @@ uint PortCapture::availableSamples() { return static_cast(mRing->readSpace()); } void PortCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } } // namespace bool PortBackendFactory::init() { - PaError err; - #ifdef HAVE_DYNLOAD if(!pa_handle) { @@ -377,7 +444,7 @@ bool PortBackendFactory::init() return false; #define LOAD_FUNC(f) do { \ - p##f = al::bit_cast(GetSymbol(pa_handle, #f)); \ + p##f = reinterpret_cast(GetSymbol(pa_handle, #f)); \ if(p##f == nullptr) \ { \ CloseLib(pa_handle); \ @@ -392,12 +459,15 @@ bool PortBackendFactory::init() LOAD_FUNC(Pa_StopStream); LOAD_FUNC(Pa_OpenStream); LOAD_FUNC(Pa_CloseStream); + LOAD_FUNC(Pa_GetDeviceCount); + LOAD_FUNC(Pa_GetDeviceInfo); LOAD_FUNC(Pa_GetDefaultOutputDevice); LOAD_FUNC(Pa_GetDefaultInputDevice); LOAD_FUNC(Pa_GetStreamInfo); #undef LOAD_FUNC - if((err=Pa_Initialize()) != paNoError) + const PaError err{Pa_Initialize()}; + if(err != paNoError) { ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err)); CloseLib(pa_handle); @@ -406,7 +476,8 @@ bool PortBackendFactory::init() } } #else - if((err=Pa_Initialize()) != paNoError) + const PaError err{Pa_Initialize()}; + if(err != paNoError) { ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err)); return false; @@ -418,18 +489,52 @@ bool PortBackendFactory::init() bool PortBackendFactory::querySupport(BackendType type) { return (type == BackendType::Playback || type == BackendType::Capture); } -std::string PortBackendFactory::probe(BackendType type) +auto PortBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; + std::vector devices; + + EnumerateDevices(); + int defaultid{-1}; switch(type) { case BackendType::Playback: + defaultid = Pa_GetDefaultOutputDevice(); + if(auto devidopt = ConfigValueInt({}, "port", "device"); devidopt && *devidopt >= 0 + && static_cast(*devidopt) < DeviceNames.size()) + defaultid = *devidopt; + + for(size_t i{0};i < DeviceNames.size();++i) + { + if(DeviceNames[i].mPlaybackChannels > 0) + { + if(defaultid >= 0 && static_cast(defaultid) == i) + devices.emplace(devices.cbegin(), DeviceNames[i].mName); + else + devices.emplace_back(DeviceNames[i].mName); + } + } + break; + case BackendType::Capture: - /* Includes null char. */ - outnames.append(pa_device, sizeof(pa_device)); + defaultid = Pa_GetDefaultInputDevice(); + if(auto devidopt = ConfigValueInt({}, "port", "capture"); devidopt && *devidopt >= 0 + && static_cast(*devidopt) < DeviceNames.size()) + defaultid = *devidopt; + + for(size_t i{0};i < DeviceNames.size();++i) + { + if(DeviceNames[i].mCaptureChannels > 0) + { + if(defaultid >= 0 && static_cast(defaultid) == i) + devices.emplace(devices.cbegin(), DeviceNames[i].mName); + else + devices.emplace_back(DeviceNames[i].mName); + } + } break; } - return outnames; + + return devices; } BackendPtr PortBackendFactory::createBackend(DeviceBase *device, BackendType type) diff --git a/3rdparty/openal/alc/backends/portaudio.h b/3rdparty/openal/alc/backends/portaudio.h index c35ccff2d625..f5abe8e4ce0d 100644 --- a/3rdparty/openal/alc/backends/portaudio.h +++ b/3rdparty/openal/alc/backends/portaudio.h @@ -5,15 +5,15 @@ struct PortBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_PORTAUDIO_H */ diff --git a/3rdparty/openal/alc/backends/pulseaudio.cpp b/3rdparty/openal/alc/backends/pulseaudio.cpp index e2cea8a8e596..1cceea51181e 100644 --- a/3rdparty/openal/alc/backends/pulseaudio.cpp +++ b/3rdparty/openal/alc/backends/pulseaudio.cpp @@ -28,23 +28,24 @@ #include #include #include +#include +#include +#include #include +#include #include #include #include -#include -#include #include +#include #include #include #include -#include "albit.h" #include "alc/alconfig.h" -#include "alc/events.h" -#include "almalloc.h" -#include "alnumeric.h" #include "alspan.h" +#include "alstring.h" +#include "base.h" #include "core/devformat.h" #include "core/device.h" #include "core/logging.h" @@ -251,7 +252,7 @@ constexpr pa_channel_map MonoChanMap{ }; -/* *grumble* Don't use enums for bitflags. */ +/* NOLINTBEGIN(*EnumCastOutOfRange) *grumble* Don't use enums for bitflags. */ constexpr pa_stream_flags_t operator|(pa_stream_flags_t lhs, pa_stream_flags_t rhs) { return pa_stream_flags_t(lhs | al::to_underlying(rhs)); } constexpr pa_stream_flags_t& operator|=(pa_stream_flags_t &lhs, pa_stream_flags_t rhs) @@ -277,6 +278,7 @@ constexpr pa_context_flags_t& operator|=(pa_context_flags_t &lhs, pa_context_fla 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)); } +/* NOLINTEND(*EnumCastOutOfRange) */ struct DevMap { @@ -321,11 +323,12 @@ class PulseMainloop { explicit operator bool() const noexcept { return mLoop != nullptr; } + [[nodiscard]] auto start() const { return pa_threaded_mainloop_start(mLoop); } 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; } + [[nodiscard]] auto getApi() const { return pa_threaded_mainloop_get_api(mLoop); } + [[nodiscard]] auto getContext() const noexcept { return mContext; } auto lock() const { return pa_threaded_mainloop_lock(mLoop); } auto unlock() const { return pa_threaded_mainloop_unlock(mLoop); } @@ -335,14 +338,14 @@ class PulseMainloop { static auto Create() { return PulseMainloop{pa_threaded_mainloop_new()}; } - void streamSuccessCallback(pa_stream*, int) noexcept { signal(); } + void streamSuccessCallback(pa_stream*, int) const noexcept { signal(); } static void streamSuccessCallbackC(pa_stream *stream, int success, void *pdata) noexcept { static_cast(pdata)->streamSuccessCallback(stream, success); } void close(pa_stream *stream=nullptr); - void deviceSinkCallback(pa_context*, const pa_sink_info *info, int eol) noexcept + void deviceSinkCallback(pa_context*, const pa_sink_info *info, int eol) const noexcept { if(eol) { @@ -373,7 +376,7 @@ class PulseMainloop { TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str()); } - void deviceSourceCallback(pa_context*, const pa_source_info *info, int eol) noexcept + void deviceSourceCallback(pa_context*, const pa_source_info *info, int eol) const noexcept { if(eol) { @@ -420,7 +423,7 @@ struct MainloopUniqueLock : public std::unique_lock { auto wait(Predicate done_waiting) const -> void { while(!done_waiting()) wait(); } - void waitForOperation(pa_operation *op) + void waitForOperation(pa_operation *op) const { if(op) { @@ -489,7 +492,7 @@ PulseMainloop::~PulseMainloop() { if(mContext) { - MainloopUniqueLock _{*this}; + MainloopUniqueLock looplock{*this}; pa_context_disconnect(mContext); pa_context_unref(mContext); } @@ -510,21 +513,20 @@ void MainloopUniqueLock::connectContext() pa_context_set_state_callback(mutex()->mContext, [](pa_context *ctx, void *pdata) noexcept { return static_cast(pdata)->contextStateCallback(ctx); }, this); - int err; - if((err=pa_context_connect(mutex()->mContext, nullptr, pulse_ctx_flags, nullptr)) >= 0) + int err{pa_context_connect(mutex()->mContext, nullptr, pulse_ctx_flags, nullptr)}; + if(err >= 0) { - pa_context_state_t state; - while((state=pa_context_get_state(mutex()->mContext)) != PA_CONTEXT_READY) + wait([&err,this]() { + pa_context_state_t state{pa_context_get_state(mutex()->mContext)}; if(!PA_CONTEXT_IS_GOOD(state)) { err = pa_context_errno(mutex()->mContext); - if(err > 0) err = -err; - break; + if(err > 0) err = -err; + return true; } - - wait(); - } + return state == PA_CONTEXT_READY; + }); } pa_context_set_state_callback(mutex()->mContext, nullptr, nullptr); @@ -559,9 +561,9 @@ pa_stream *MainloopUniqueLock::connectStream(const char *device_name, pa_stream_ stream_id, pa_strerror(err)}; } - pa_stream_state_t state; - while((state=pa_stream_get_state(stream)) != PA_STREAM_READY) + wait([&err,stream,stream_id,this]() { + pa_stream_state_t state{pa_stream_get_state(stream)}; if(!PA_STREAM_IS_GOOD(state)) { err = pa_context_errno(mutex()->mContext); @@ -569,9 +571,9 @@ pa_stream *MainloopUniqueLock::connectStream(const char *device_name, pa_stream_ throw al::backend_exception{al::backend_error::DeviceError, "%s did not get ready (%s)", stream_id, pa_strerror(err)}; } + return state == PA_STREAM_READY; + }); - wait(); - } pa_stream_set_state_callback(stream, nullptr, nullptr); return stream; @@ -582,7 +584,7 @@ void PulseMainloop::close(pa_stream *stream) if(!stream) return; - MainloopUniqueLock _{*this}; + MainloopUniqueLock looplock{*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); @@ -597,6 +599,8 @@ void PulseMainloop::probePlaybackDevices() PlaybackDevices.clear(); try { MainloopUniqueLock plock{*this}; + plock.connectContext(); + auto sink_callback = [](pa_context *ctx, const pa_sink_info *info, int eol, void *pdata) noexcept { return static_cast(pdata)->deviceSinkCallback(ctx, info, eol); }; @@ -616,6 +620,8 @@ void PulseMainloop::probeCaptureDevices() CaptureDevices.clear(); try { MainloopUniqueLock plock{*this}; + plock.connectContext(); + auto src_callback = [](pa_context *ctx, const pa_source_info *info, int eol, void *pdata) noexcept { return static_cast(pdata)->deviceSourceCallback(ctx, info, eol); }; @@ -655,17 +661,15 @@ struct PulsePlayback final : public BackendBase { PulseMainloop mMainloop; - std::optional mDeviceName{std::nullopt}; + std::optional mDeviceId{std::nullopt}; bool mIs51Rear{false}; - pa_buffer_attr mAttr; - pa_sample_spec mSpec; + pa_buffer_attr mAttr{}; + pa_sample_spec mSpec{}; pa_stream *mStream{nullptr}; uint mFrameSize{0u}; - - DEF_NEWDEL(PulsePlayback) }; PulsePlayback::~PulsePlayback() @@ -706,7 +710,7 @@ void PulsePlayback::streamWriteCallback(pa_stream *stream, size_t nbytes) noexce free_func = pa_xfree; } else - buflen = minz(buflen, nbytes); + buflen = std::min(buflen, nbytes); nbytes -= buflen; mDevice->renderSamples(buf, static_cast(buflen/mFrameSize), mSpec.channels); @@ -754,9 +758,9 @@ void PulsePlayback::sinkInfoCallback(pa_context*, const pa_sink_info *info, int else { mIs51Rear = false; - char chanmap_str[PA_CHANNEL_MAP_SNPRINT_MAX]{}; - pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map); - WARN("Failed to find format for channel map:\n %s\n", chanmap_str); + std::array chanmap_str{}; + pa_channel_map_snprint(chanmap_str.data(), chanmap_str.size(), &info->channel_map); + WARN("Failed to find format for channel map:\n %s\n", chanmap_str.data()); } if(info->active_port) @@ -772,35 +776,39 @@ void PulsePlayback::sinkNameCallback(pa_context*, const pa_sink_info *info, int mMainloop.signal(); return; } - mDevice->DeviceName = info->description; + mDeviceName = info->description; } void PulsePlayback::streamMovedCallback(pa_stream *stream) noexcept { - mDeviceName = pa_stream_get_device_name(stream); - TRACE("Stream moved to %s\n", mDeviceName->c_str()); + mDeviceId = pa_stream_get_device_name(stream); + TRACE("Stream moved to %s\n", mDeviceId->c_str()); } void PulsePlayback::open(std::string_view name) { mMainloop = PulseMainloop::Create(); - mMainloop.start(); + if(mMainloop.start() != 0) + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to start device mainloop"}; const char *pulse_name{nullptr}; - const char *dev_name{nullptr}; + std::string_view display_name; if(!name.empty()) { if(PlaybackDevices.empty()) mMainloop.probePlaybackDevices(); - auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(), - [name](const DevMap &entry) -> bool { return entry.name == name; }); + auto match_name = [name](const DevMap &entry) -> bool + { return entry.name == name || entry.device_name == name; }; + auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(), match_name); if(iter == PlaybackDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%.*s\" not found", static_cast(name.length()), name.data()}; + "Device name \"%.*s\" not found", al::sizei(name), name.data()}; + pulse_name = iter->device_name.c_str(); - dev_name = iter->name.c_str(); + display_name = iter->name; } MainloopUniqueLock plock{mMainloop}; @@ -808,7 +816,7 @@ void PulsePlayback::open(std::string_view name) pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | PA_STREAM_FIX_CHANNELS}; - if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", true)) + if(!GetConfigValueBool({}, "pulse", "allow-moves", true)) flags |= PA_STREAM_DONT_MOVE; pa_sample_spec spec{}; @@ -825,13 +833,14 @@ void PulsePlayback::open(std::string_view name) mStream = plock.connectStream(pulse_name, flags, nullptr, &spec, nullptr, BackendType::Playback); - pa_stream_set_moved_callback(mStream, [](pa_stream *stream, void *pdata) noexcept - { return static_cast(pdata)->streamMovedCallback(stream); }, this); + constexpr auto move_callback = [](pa_stream *stream, void *pdata) noexcept + { return static_cast(pdata)->streamMovedCallback(stream); }; + pa_stream_set_moved_callback(mStream, move_callback, this); mFrameSize = static_cast(pa_frame_size(pa_stream_get_sample_spec(mStream))); - if(pulse_name) mDeviceName.emplace(pulse_name); - else mDeviceName.reset(); - if(!dev_name) + if(pulse_name) mDeviceId.emplace(pulse_name); + else mDeviceId.reset(); + if(display_name.empty()) { auto name_callback = [](pa_context *context, const pa_sink_info *info, int eol, void *pdata) noexcept { return static_cast(pdata)->sinkNameCallback(context, info, eol); }; @@ -840,13 +849,13 @@ void PulsePlayback::open(std::string_view name) plock.waitForOperation(op); } else - mDevice->DeviceName = dev_name; + mDeviceName = display_name; } bool PulsePlayback::reset() { MainloopUniqueLock plock{mMainloop}; - const auto deviceName = mDeviceName ? mDeviceName->c_str() : nullptr; + const auto deviceName = mDeviceId ? mDeviceId->c_str() : nullptr; if(mStream) { @@ -859,17 +868,17 @@ bool PulsePlayback::reset() mStream = nullptr; } - auto info_callback = [](pa_context *context, const pa_sink_info *info, int eol, void *pdata) noexcept + auto info_cb = [](pa_context *context, const pa_sink_info *info, int eol, void *pdata) noexcept { return static_cast(pdata)->sinkInfoCallback(context, info, eol); }; - pa_operation *op{pa_context_get_sink_info_by_name(mMainloop.getContext(), deviceName, - info_callback, this)}; + pa_operation *op{pa_context_get_sink_info_by_name(mMainloop.getContext(), deviceName, info_cb, + this)}; plock.waitForOperation(op); pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_EARLY_REQUESTS}; - if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", true)) + if(!GetConfigValueBool({}, "pulse", "allow-moves", true)) flags |= PA_STREAM_DONT_MOVE; - if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "adjust-latency", false)) + if(GetConfigValueBool(mDevice->mDeviceName, "pulse", "adjust-latency", false)) { /* ADJUST_LATENCY can't be specified with EARLY_REQUESTS, for some * reason. So if the user wants to adjust the overall device latency, @@ -878,7 +887,7 @@ bool PulsePlayback::reset() flags &= ~PA_STREAM_EARLY_REQUESTS; flags |= PA_STREAM_ADJUST_LATENCY; } - if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "fix-rate", false) + if(GetConfigValueBool(mDevice->mDeviceName, "pulse", "fix-rate", false) || !mDevice->Flags.test(FrequencyRequest)) flags |= PA_STREAM_FIX_RATE; @@ -907,6 +916,9 @@ bool PulsePlayback::reset() case DevFmtX3D71: chanmap = X71ChanMap; break; + case DevFmtX7144: + mDevice->FmtChans = DevFmtX714; + /*fall-through*/ case DevFmtX714: chanmap = X714ChanMap; break; @@ -952,10 +964,13 @@ bool PulsePlayback::reset() mStream = plock.connectStream(deviceName, flags, &mAttr, &mSpec, &chanmap, BackendType::Playback); - pa_stream_set_state_callback(mStream, [](pa_stream *stream, void *pdata) noexcept - { return static_cast(pdata)->streamStateCallback(stream); }, this); - pa_stream_set_moved_callback(mStream, [](pa_stream *stream, void *pdata) noexcept - { return static_cast(pdata)->streamMovedCallback(stream); }, this); + constexpr auto state_callback = [](pa_stream *stream, void *pdata) noexcept + { return static_cast(pdata)->streamStateCallback(stream); }; + pa_stream_set_state_callback(mStream, state_callback, this); + + constexpr auto move_callback = [](pa_stream *stream, void *pdata) noexcept + { return static_cast(pdata)->streamMovedCallback(stream); }; + pa_stream_set_moved_callback(mStream, move_callback, this); mSpec = *(pa_stream_get_sample_spec(mStream)); mFrameSize = static_cast(pa_frame_size(&mSpec)); @@ -966,15 +981,15 @@ bool PulsePlayback::reset() * accordingly. */ const auto scale = static_cast(mSpec.rate) / mDevice->Frequency; - const auto perlen = static_cast(clampd(scale*mDevice->UpdateSize + 0.5, 64.0, - 8192.0)); - const auto buflen = static_cast(clampd(scale*mDevice->BufferSize + 0.5, perlen*2, - std::numeric_limits::max()/mFrameSize)); + const auto perlen = std::clamp(std::round(scale*mDevice->UpdateSize), 64.0, 8192.0); + const auto bufmax = uint{std::numeric_limits::max()} / mFrameSize; + const auto buflen = std::clamp(std::round(scale*mDevice->BufferSize), perlen*2.0, + static_cast(bufmax)); mAttr.maxlength = ~0u; - mAttr.tlength = buflen * mFrameSize; + mAttr.tlength = static_cast(buflen) * mFrameSize; mAttr.prebuf = 0u; - mAttr.minreq = perlen * mFrameSize; + mAttr.minreq = static_cast(perlen) * mFrameSize; op = pa_stream_set_buffer_attr(mStream, &mAttr, &PulseMainloop::streamSuccessCallbackC, &mMainloop); @@ -983,7 +998,7 @@ bool PulsePlayback::reset() mDevice->Frequency = mSpec.rate; } - auto attr_callback = [](pa_stream *stream, void *pdata) noexcept + constexpr auto attr_callback = [](pa_stream *stream, void *pdata) noexcept { return static_cast(pdata)->bufferAttrCallback(stream); }; pa_stream_set_buffer_attr_callback(mStream, attr_callback, this); bufferAttrCallback(mStream); @@ -1008,8 +1023,9 @@ void PulsePlayback::start() pa_stream_write(mStream, buf, todo, pa_xfree, 0, PA_SEEK_RELATIVE); } - pa_stream_set_write_callback(mStream, [](pa_stream *stream, size_t nbytes, void *pdata)noexcept - { return static_cast(pdata)->streamWriteCallback(stream, nbytes); }, this); + constexpr auto stream_write = [](pa_stream *stream, size_t nbytes, void *pdata) noexcept + { return static_cast(pdata)->streamWriteCallback(stream, nbytes); }; + pa_stream_set_write_callback(mStream, stream_write, this); pa_operation *op{pa_stream_cork(mStream, 0, &PulseMainloop::streamSuccessCallbackC, &mMainloop)}; @@ -1029,13 +1045,13 @@ void PulsePlayback::stop() ClockLatency PulsePlayback::getClockLatency() { - ClockLatency ret; - pa_usec_t latency; - int neg, err; + ClockLatency ret{}; + pa_usec_t latency{}; + int neg{}, err{}; { MainloopUniqueLock plock{mMainloop}; - ret.ClockTime = GetDeviceClockTime(mDevice); + ret.ClockTime = mDevice->getClockTime(); err = pa_stream_get_latency(mStream, &latency, &neg); } @@ -1075,7 +1091,7 @@ struct PulseCapture final : public BackendBase { PulseMainloop mMainloop; - std::optional mDeviceName{std::nullopt}; + std::optional mDeviceId{std::nullopt}; al::span mCapBuffer; size_t mHoleLength{0}; @@ -1088,8 +1104,6 @@ struct PulseCapture final : public BackendBase { pa_sample_spec mSpec{}; pa_stream *mStream{nullptr}; - - DEF_NEWDEL(PulseCapture) }; PulseCapture::~PulseCapture() @@ -1113,13 +1127,13 @@ void PulseCapture::sourceNameCallback(pa_context*, const pa_source_info *info, i mMainloop.signal(); return; } - mDevice->DeviceName = info->description; + mDeviceName = info->description; } void PulseCapture::streamMovedCallback(pa_stream *stream) noexcept { - mDeviceName = pa_stream_get_device_name(stream); - TRACE("Stream moved to %s\n", mDeviceName->c_str()); + mDeviceId = pa_stream_get_device_name(stream); + TRACE("Stream moved to %s\n", mDeviceId->c_str()); } @@ -1128,7 +1142,9 @@ void PulseCapture::open(std::string_view name) if(!mMainloop) { mMainloop = PulseMainloop::Create(); - mMainloop.start(); + if(mMainloop.start() != 0) + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to start device mainloop"}; } const char *pulse_name{nullptr}; @@ -1137,13 +1153,15 @@ void PulseCapture::open(std::string_view name) if(CaptureDevices.empty()) mMainloop.probeCaptureDevices(); - auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(), - [name](const DevMap &entry) -> bool { return entry.name == name; }); + auto match_name = [name](const DevMap &entry) -> bool + { return entry.name == name || entry.device_name == name; }; + auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(), match_name); if(iter == CaptureDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%.*s\" not found", static_cast(name.length()), name.data()}; + "Device name \"%.*s\" not found", al::sizei(name), name.data()}; + pulse_name = iter->device_name.c_str(); - mDevice->DeviceName = iter->name; + mDeviceName = iter->name; } MainloopUniqueLock plock{mMainloop}; @@ -1152,27 +1170,14 @@ void PulseCapture::open(std::string_view name) pa_channel_map chanmap{}; switch(mDevice->FmtChans) { - case DevFmtMono: - chanmap = MonoChanMap; - break; - case DevFmtStereo: - chanmap = StereoChanMap; - break; - case DevFmtQuad: - chanmap = QuadChanMap; - break; - case DevFmtX51: - chanmap = X51ChanMap; - break; - case DevFmtX61: - chanmap = X61ChanMap; - break; - case DevFmtX71: - chanmap = X71ChanMap; - break; - case DevFmtX714: - chanmap = X714ChanMap; - break; + case DevFmtMono: chanmap = MonoChanMap; break; + case DevFmtStereo: chanmap = StereoChanMap; break; + case DevFmtQuad: chanmap = QuadChanMap; break; + case DevFmtX51: chanmap = X51ChanMap; break; + case DevFmtX61: chanmap = X61ChanMap; break; + case DevFmtX71: chanmap = X71ChanMap; break; + case DevFmtX714: chanmap = X714ChanMap; break; + case DevFmtX7144: case DevFmtX3D71: case DevFmtAmbi3D: throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported", @@ -1207,31 +1212,35 @@ void PulseCapture::open(std::string_view name) throw al::backend_exception{al::backend_error::DeviceError, "Invalid sample format"}; const auto frame_size = static_cast(pa_frame_size(&mSpec)); - const uint samples{maxu(mDevice->BufferSize, 100 * mDevice->Frequency / 1000)}; + const uint samples{std::max(mDevice->BufferSize, mDevice->Frequency*100u/1000u)}; mAttr.minreq = ~0u; mAttr.prebuf = ~0u; mAttr.maxlength = samples * frame_size; mAttr.tlength = ~0u; - mAttr.fragsize = minu(samples, 50*mDevice->Frequency/1000) * frame_size; + mAttr.fragsize = std::min(samples, mDevice->Frequency*50u/1000u) * frame_size; pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY}; - if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", true)) + if(!GetConfigValueBool({}, "pulse", "allow-moves", true)) flags |= PA_STREAM_DONT_MOVE; TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)"); mStream = plock.connectStream(pulse_name, flags, &mAttr, &mSpec, &chanmap, BackendType::Capture); - pa_stream_set_moved_callback(mStream, [](pa_stream *stream, void *pdata) noexcept - { return static_cast(pdata)->streamMovedCallback(stream); }, this); - pa_stream_set_state_callback(mStream, [](pa_stream *stream, void *pdata) noexcept - { return static_cast(pdata)->streamStateCallback(stream); }, this); + constexpr auto move_callback = [](pa_stream *stream, void *pdata) noexcept + { return static_cast(pdata)->streamMovedCallback(stream); }; + pa_stream_set_moved_callback(mStream, move_callback, this); - if(pulse_name) mDeviceName.emplace(pulse_name); - else mDeviceName.reset(); - if(mDevice->DeviceName.empty()) + constexpr auto state_callback = [](pa_stream *stream, void *pdata) noexcept + { return static_cast(pdata)->streamStateCallback(stream); }; + pa_stream_set_state_callback(mStream, state_callback, this); + + if(pulse_name) mDeviceId.emplace(pulse_name); + else mDeviceId.reset(); + if(mDeviceName.empty()) { - auto name_callback = [](pa_context *context, const pa_source_info *info, int eol, void *pdata) noexcept + constexpr auto name_callback = [](pa_context *context, const pa_source_info *info, int eol, + void *pdata) noexcept { return static_cast(pdata)->sourceNameCallback(context, info, eol); }; pa_operation *op{pa_context_get_source_info_by_name(mMainloop.getContext(), pa_stream_get_device_name(mStream), name_callback, this)}; @@ -1267,7 +1276,7 @@ void PulseCapture::captureSamples(std::byte *buffer, uint samples) { if(mHoleLength > 0) UNLIKELY { - const size_t rem{minz(dstbuf.size(), mHoleLength)}; + const size_t rem{std::min(dstbuf.size(), mHoleLength)}; std::fill_n(dstbuf.begin(), rem, mSilentVal); dstbuf = dstbuf.subspan(rem); mHoleLength -= rem; @@ -1276,7 +1285,7 @@ void PulseCapture::captureSamples(std::byte *buffer, uint samples) } if(!mCapBuffer.empty()) { - const size_t rem{minz(dstbuf.size(), mCapBuffer.size())}; + const size_t rem{std::min(dstbuf.size(), mCapBuffer.size())}; std::copy_n(mCapBuffer.begin(), rem, dstbuf.begin()); dstbuf = dstbuf.subspan(rem); mCapBuffer = mCapBuffer.subspan(rem); @@ -1301,8 +1310,8 @@ void PulseCapture::captureSamples(std::byte *buffer, uint samples) break; } - const void *capbuf; - size_t caplen; + const void *capbuf{}; + size_t caplen{}; if(pa_stream_peek(mStream, &capbuf, &caplen) < 0) UNLIKELY { mDevice->handleDisconnect("Failed retrieving capture samples: %s", @@ -1324,7 +1333,7 @@ void PulseCapture::captureSamples(std::byte *buffer, uint samples) uint PulseCapture::availableSamples() { - size_t readable{maxz(mCapBuffer.size(), mHoleLength)}; + size_t readable{std::max(mCapBuffer.size(), mHoleLength)}; if(mDevice->Connected.load(std::memory_order_acquire)) { @@ -1357,13 +1366,13 @@ uint PulseCapture::availableSamples() ClockLatency PulseCapture::getClockLatency() { - ClockLatency ret; - pa_usec_t latency; - int neg, err; + ClockLatency ret{}; + pa_usec_t latency{}; + int neg{}, err{}; { MainloopUniqueLock plock{mMainloop}; - ret.ClockTime = GetDeviceClockTime(mDevice); + ret.ClockTime = mDevice->getClockTime(); err = pa_stream_get_latency(mStream, &latency, &neg); } @@ -1388,9 +1397,6 @@ bool PulseBackendFactory::init() #ifdef HAVE_DYNLOAD if(!pulse_handle) { - bool ret{true}; - std::string missing_funcs; - #ifdef _WIN32 #define PALIB "libpulse-0.dll" #elif defined(__APPLE__) && defined(__MACH__) @@ -1405,17 +1411,15 @@ bool PulseBackendFactory::init() return false; } + std::string missing_funcs; #define LOAD_FUNC(x) do { \ - p##x = al::bit_cast(GetSymbol(pulse_handle, #x)); \ - if(!(p##x)) { \ - ret = false; \ - missing_funcs += "\n" #x; \ - } \ + p##x = reinterpret_cast(GetSymbol(pulse_handle, #x)); \ + if(!(p##x)) missing_funcs += "\n" #x; \ } while(0) PULSE_FUNCS(LOAD_FUNC) #undef LOAD_FUNC - if(!ret) + if(!missing_funcs.empty()) { WARN("Missing expected functions:%s\n", missing_funcs.c_str()); CloseLib(pulse_handle); @@ -1426,14 +1430,18 @@ bool PulseBackendFactory::init() #endif /* HAVE_DYNLOAD */ pulse_ctx_flags = PA_CONTEXT_NOFLAGS; - if(!GetConfigValueBool(nullptr, "pulse", "spawn-server", false)) + if(!GetConfigValueBool({}, "pulse", "spawn-server", false)) pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN; try { if(!gGlobalMainloop) { gGlobalMainloop = PulseMainloop::Create(); - gGlobalMainloop.start(); + if(gGlobalMainloop.start() != 0) + { + gGlobalMainloop = nullptr; + return false; + } } MainloopUniqueLock plock{gGlobalMainloop}; @@ -1449,27 +1457,24 @@ bool PulseBackendFactory::init() bool PulseBackendFactory::querySupport(BackendType type) { return type == BackendType::Playback || type == BackendType::Capture; } -std::string PulseBackendFactory::probe(BackendType type) +auto PulseBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; + std::vector outnames; auto add_device = [&outnames](const DevMap &entry) -> void - { - /* +1 to also append the null char (to ensure a null-separated list and - * double-null terminated list). - */ - outnames.append(entry.name.c_str(), entry.name.length()+1); - }; + { outnames.push_back(entry.name); }; switch(type) { case BackendType::Playback: gGlobalMainloop.probePlaybackDevices(); + outnames.reserve(PlaybackDevices.size()); std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device); break; case BackendType::Capture: gGlobalMainloop.probeCaptureDevices(); + outnames.reserve(CaptureDevices.size()); std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device); break; } @@ -1491,3 +1496,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/3rdparty/openal/alc/backends/pulseaudio.h b/3rdparty/openal/alc/backends/pulseaudio.h index 6690fe8a9f6d..c183d4794e28 100644 --- a/3rdparty/openal/alc/backends/pulseaudio.h +++ b/3rdparty/openal/alc/backends/pulseaudio.h @@ -1,19 +1,27 @@ #ifndef BACKENDS_PULSEAUDIO_H #define BACKENDS_PULSEAUDIO_H +#include +#include + +#include "alc/events.h" #include "base.h" +struct DeviceBase; + class PulseBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; + + auto querySupport(BackendType type) -> bool final; - bool querySupport(BackendType type) override; + auto queryEventSupport(alc::EventType eventType, BackendType type) -> alc::EventSupport final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_PULSEAUDIO_H */ diff --git a/3rdparty/openal/alc/backends/sdl2.cpp b/3rdparty/openal/alc/backends/sdl2.cpp index f5ed4316a87e..04131c4bb2fd 100644 --- a/3rdparty/openal/alc/backends/sdl2.cpp +++ b/3rdparty/openal/alc/backends/sdl2.cpp @@ -26,11 +26,10 @@ #include #include #include +#include -#include "almalloc.h" #include "alnumeric.h" #include "core/device.h" -#include "core/logging.h" _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wold-style-cast\"") @@ -40,13 +39,10 @@ _Pragma("GCC diagnostic pop") namespace { -#ifdef _WIN32 -#define DEVNAME_PREFIX "OpenAL Soft on " -#else -#define DEVNAME_PREFIX "" -#endif +using namespace std::string_view_literals; -constexpr char defaultDeviceName[] = DEVNAME_PREFIX "Default Device"; +[[nodiscard]] constexpr auto getDefaultDeviceName() noexcept -> std::string_view +{ return "Default Device"sv; } struct Sdl2Backend final : public BackendBase { Sdl2Backend(DeviceBase *device) noexcept : BackendBase{device} { } @@ -59,15 +55,9 @@ struct Sdl2Backend final : public BackendBase { void start() override; void stop() override; + std::string mSDLName; SDL_AudioDeviceID mDeviceID{0u}; uint mFrameSize{0}; - - uint mFrequency{0u}; - DevFmtChannels mFmtChans{}; - DevFmtType mFmtType{}; - uint mUpdateSize{0u}; - - DEF_NEWDEL(Sdl2Backend) }; Sdl2Backend::~Sdl2Backend() @@ -99,8 +89,9 @@ void Sdl2Backend::open(std::string_view name) case DevFmtInt: want.format = AUDIO_S32SYS; break; case DevFmtFloat: want.format = AUDIO_F32; break; } - want.channels = (mDevice->FmtChans == DevFmtMono) ? 1 : 2; - want.samples = static_cast(minu(mDevice->UpdateSize, 8192)); + want.channels = static_cast(std::min(mDevice->channelsFromFmt(), + std::numeric_limits::max())); + want.samples = static_cast(std::min(mDevice->UpdateSize, 8192u)); want.callback = [](void *ptr, Uint8 *stream, int len) noexcept { return static_cast(ptr)->audioCallback(stream, len); }; want.userdata = this; @@ -108,46 +99,23 @@ void Sdl2Backend::open(std::string_view name) /* Passing nullptr to SDL_OpenAudioDevice opens a default, which isn't * necessarily the first in the list. */ - SDL_AudioDeviceID devid; + const auto defaultDeviceName = getDefaultDeviceName(); if(name.empty() || name == defaultDeviceName) { name = defaultDeviceName; - devid = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE); + mSDLName.clear(); + mDeviceID = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have, + SDL_AUDIO_ALLOW_ANY_CHANGE); } else { - const size_t prefix_len = strlen(DEVNAME_PREFIX); - 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 - { - const std::string devname{name}; - devid = SDL_OpenAudioDevice(devname.c_str(), SDL_FALSE, &want, &have, - SDL_AUDIO_ALLOW_ANY_CHANGE); - } + mSDLName = name; + mDeviceID = SDL_OpenAudioDevice(mSDLName.c_str(), SDL_FALSE, &want, &have, + SDL_AUDIO_ALLOW_ANY_CHANGE); } - if(!devid) + if(!mDeviceID) throw al::backend_exception{al::backend_error::NoDevice, "%s", SDL_GetError()}; - DevFmtChannels devchans{}; - if(have.channels >= 2) - devchans = DevFmtStereo; - else if(have.channels == 1) - devchans = DevFmtMono; - else - { - SDL_CloseAudioDevice(devid); - throw al::backend_exception{al::backend_error::DeviceError, - "Unhandled SDL channel count: %d", int{have.channels}}; - } - DevFmtType devtype{}; switch(have.format) { @@ -158,32 +126,100 @@ void Sdl2Backend::open(std::string_view name) case AUDIO_S32SYS: devtype = DevFmtInt; break; case AUDIO_F32SYS: devtype = DevFmtFloat; break; default: - SDL_CloseAudioDevice(devid); throw al::backend_exception{al::backend_error::DeviceError, "Unhandled SDL format: 0x%04x", have.format}; } - if(mDeviceID) - SDL_CloseAudioDevice(mDeviceID); - mDeviceID = devid; - mFrameSize = BytesFromDevFmt(devtype) * have.channels; - mFrequency = static_cast(have.freq); - mFmtChans = devchans; - mFmtType = devtype; - mUpdateSize = have.samples; - mDevice->DeviceName = name; + mDeviceName = name; } bool Sdl2Backend::reset() { - mDevice->Frequency = mFrequency; - mDevice->FmtChans = mFmtChans; - mDevice->FmtType = mFmtType; - mDevice->UpdateSize = mUpdateSize; - mDevice->BufferSize = mUpdateSize * 2; /* SDL always (tries to) use two periods. */ + if(mDeviceID) + SDL_CloseAudioDevice(mDeviceID); + mDeviceID = 0; + + auto want = SDL_AudioSpec{}; + want.freq = static_cast(mDevice->Frequency); + switch(mDevice->FmtType) + { + case DevFmtUByte: want.format = AUDIO_U8; break; + case DevFmtByte: want.format = AUDIO_S8; break; + case DevFmtUShort: want.format = AUDIO_U16SYS; break; + case DevFmtShort: want.format = AUDIO_S16SYS; break; + case DevFmtUInt: [[fallthrough]]; + case DevFmtInt: want.format = AUDIO_S32SYS; break; + case DevFmtFloat: want.format = AUDIO_F32; break; + } + want.channels = static_cast(std::min(mDevice->channelsFromFmt(), + std::numeric_limits::max())); + want.samples = static_cast(std::min(mDevice->UpdateSize, 8192u)); + want.callback = [](void *ptr, Uint8 *stream, int len) noexcept + { return static_cast(ptr)->audioCallback(stream, len); }; + want.userdata = this; + + auto have = SDL_AudioSpec{}; + if(mSDLName.empty()) + { + mDeviceID = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have, + SDL_AUDIO_ALLOW_ANY_CHANGE); + } + else + { + mDeviceID = SDL_OpenAudioDevice(mSDLName.c_str(), SDL_FALSE, &want, &have, + SDL_AUDIO_ALLOW_ANY_CHANGE); + } + if(!mDeviceID) + throw al::backend_exception{al::backend_error::NoDevice, "%s", SDL_GetError()}; + + if(have.channels != mDevice->channelsFromFmt()) + { + /* SDL guarantees these layouts for the given channel count. */ + if(have.channels == 8) + mDevice->FmtChans = DevFmtX71; + else if(have.channels == 7) + mDevice->FmtChans = DevFmtX61; + else if(have.channels == 6) + mDevice->FmtChans = DevFmtX51; + else if(have.channels == 4) + mDevice->FmtChans = DevFmtQuad; + else if(have.channels >= 2) + mDevice->FmtChans = DevFmtStereo; + else if(have.channels == 1) + mDevice->FmtChans = DevFmtMono; + else + throw al::backend_exception{al::backend_error::DeviceError, + "Unhandled SDL channel count: %d", int{have.channels}}; + mDevice->mAmbiOrder = 0; + } + + switch(have.format) + { + case AUDIO_U8: mDevice->FmtType = DevFmtUByte; break; + case AUDIO_S8: mDevice->FmtType = DevFmtByte; break; + case AUDIO_U16SYS: mDevice->FmtType = DevFmtUShort; break; + case AUDIO_S16SYS: mDevice->FmtType = DevFmtShort; break; + case AUDIO_S32SYS: mDevice->FmtType = DevFmtInt; break; + case AUDIO_F32SYS: mDevice->FmtType = DevFmtFloat; break; + default: + throw al::backend_exception{al::backend_error::DeviceError, "Unhandled SDL format: 0x%04x", + have.format}; + } + + mFrameSize = BytesFromDevFmt(mDevice->FmtType) * have.channels; + + if(have.freq < int{MinOutputRate}) + throw al::backend_exception{al::backend_error::DeviceError, + "Unhandled SDL sample rate: %d", have.format}; + + mDevice->Frequency = static_cast(have.freq); + mDevice->UpdateSize = have.samples; + mDevice->BufferSize = std::max(have.size/mFrameSize, mDevice->UpdateSize*2u); + setDefaultWFXChannelOrder(); + return true; } @@ -207,23 +243,25 @@ bool SDL2BackendFactory::init() bool SDL2BackendFactory::querySupport(BackendType type) { return type == BackendType::Playback; } -std::string SDL2BackendFactory::probe(BackendType type) +auto SDL2BackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; + std::vector outnames; if(type != BackendType::Playback) return outnames; int num_devices{SDL_GetNumAudioDevices(SDL_FALSE)}; + if(num_devices <= 0) + return outnames; - /* Includes null char. */ - outnames.append(defaultDeviceName, sizeof(defaultDeviceName)); + outnames.reserve(static_cast(num_devices)+1_uz); + outnames.emplace_back(getDefaultDeviceName()); for(int i{0};i < num_devices;++i) { - std::string name{DEVNAME_PREFIX}; - name += SDL_GetAudioDeviceName(i, SDL_FALSE); - if(!name.empty()) - outnames.append(name.c_str(), name.length()+1); + if(const char *name = SDL_GetAudioDeviceName(i, SDL_FALSE)) + outnames.emplace_back(name); + else + outnames.emplace_back("Unknown Device Name #"+std::to_string(i)); } return outnames; } diff --git a/3rdparty/openal/alc/backends/sdl2.h b/3rdparty/openal/alc/backends/sdl2.h index 3bd8df8633e3..8c5829121607 100644 --- a/3rdparty/openal/alc/backends/sdl2.h +++ b/3rdparty/openal/alc/backends/sdl2.h @@ -5,15 +5,15 @@ struct SDL2BackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_SDL2_H */ diff --git a/3rdparty/openal/alc/backends/sndio.cpp b/3rdparty/openal/alc/backends/sndio.cpp index d54c337b2c89..5f7947031515 100644 --- a/3rdparty/openal/alc/backends/sndio.cpp +++ b/3rdparty/openal/alc/backends/sndio.cpp @@ -23,30 +23,34 @@ #include "sndio.h" #include +#include +#include +#include #include #include -#include -#include -#include +#include #include #include #include "alnumeric.h" +#include "alstring.h" #include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" #include "core/logging.h" #include "ringbuffer.h" -#include +#include /* NOLINT(*-duplicate-include) Not the same header. */ namespace { -static const char sndio_device[] = "SndIO Default"; +using namespace std::string_view_literals; + +[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "SndIO Default"sv; } struct SioPar : public sio_par { - SioPar() { sio_initpar(this); } + SioPar() : sio_par{} { sio_initpar(this); } void clear() { sio_initpar(this); } }; @@ -69,8 +73,6 @@ struct SndioPlayback final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(SndioPlayback) }; SndioPlayback::~SndioPlayback() @@ -86,7 +88,7 @@ int SndioPlayback::mixerProc() const size_t frameSize{frameStep * mDevice->bytesFromFmt()}; SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); while(!mKillNow.load(std::memory_order_acquire) && mDevice->Connected.load(std::memory_order_acquire)) @@ -115,10 +117,10 @@ int SndioPlayback::mixerProc() void SndioPlayback::open(std::string_view name) { if(name.empty()) - name = sndio_device; - else if(name != sndio_device) + name = GetDefaultName(); + else if(name != GetDefaultName()) throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + al::sizei(name), name.data()}; sio_hdl *sndHandle{sio_open(nullptr, SIO_PLAY, 0)}; if(!sndHandle) @@ -128,7 +130,7 @@ void SndioPlayback::open(std::string_view name) sio_close(mSndHandle); mSndHandle = sndHandle; - mDevice->DeviceName = name; + mDeviceName = name; } bool SndioPlayback::reset() @@ -136,72 +138,75 @@ bool SndioPlayback::reset() SioPar par; auto tryfmt = mDevice->FmtType; -retry_params: - switch(tryfmt) + while(true) { - case DevFmtByte: - par.bits = 8; - par.sig = 1; - break; - case DevFmtUByte: - par.bits = 8; - par.sig = 0; - break; - case DevFmtShort: - par.bits = 16; - par.sig = 1; - break; - case DevFmtUShort: - par.bits = 16; - par.sig = 0; - break; - case DevFmtFloat: - case DevFmtInt: - par.bits = 32; - par.sig = 1; - break; - case DevFmtUInt: - par.bits = 32; - par.sig = 0; - break; - } - par.bps = SIO_BPS(par.bits); - par.le = SIO_LE_NATIVE; - par.msb = 1; - - par.rate = mDevice->Frequency; - par.pchan = mDevice->channelsFromFmt(); - - par.round = mDevice->UpdateSize; - par.appbufsz = mDevice->BufferSize - mDevice->UpdateSize; - if(!par.appbufsz) par.appbufsz = mDevice->UpdateSize; + switch(tryfmt) + { + case DevFmtByte: + par.bits = 8; + par.sig = 1; + break; + case DevFmtUByte: + par.bits = 8; + par.sig = 0; + break; + case DevFmtShort: + par.bits = 16; + par.sig = 1; + break; + case DevFmtUShort: + par.bits = 16; + par.sig = 0; + break; + case DevFmtFloat: + case DevFmtInt: + par.bits = 32; + par.sig = 1; + break; + case DevFmtUInt: + par.bits = 32; + par.sig = 0; + break; + } + par.bps = SIO_BPS(par.bits); + par.le = SIO_LE_NATIVE; + par.msb = 1; + + par.rate = mDevice->Frequency; + par.pchan = mDevice->channelsFromFmt(); + + par.round = mDevice->UpdateSize; + par.appbufsz = mDevice->BufferSize - mDevice->UpdateSize; + if(!par.appbufsz) par.appbufsz = mDevice->UpdateSize; + + try { + if(!sio_setpar(mSndHandle, &par)) + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to set device parameters"}; + + par.clear(); + if(!sio_getpar(mSndHandle, &par)) + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to get device parameters"}; + + if(par.bps > 1 && par.le != SIO_LE_NATIVE) + throw al::backend_exception{al::backend_error::DeviceError, + "%s-endian samples not supported", par.le ? "Little" : "Big"}; + if(par.bits < par.bps*8 && !par.msb) + throw al::backend_exception{al::backend_error::DeviceError, + "MSB-padded samples not supported (%u of %u bits)", par.bits, par.bps*8}; + if(par.pchan < 1) + throw al::backend_exception{al::backend_error::DeviceError, + "No playback channels on device"}; - try { - if(!sio_setpar(mSndHandle, &par)) - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to set device parameters"}; - - par.clear(); - if(!sio_getpar(mSndHandle, &par)) - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to get device parameters"}; - - if(par.bps > 1 && par.le != SIO_LE_NATIVE) - throw al::backend_exception{al::backend_error::DeviceError, - "%s-endian samples not supported", par.le ? "Little" : "Big"}; - if(par.bits < par.bps*8 && !par.msb) - throw al::backend_exception{al::backend_error::DeviceError, - "MSB-padded samples not supported (%u of %u bits)", par.bits, par.bps*8}; - if(par.pchan < 1) - throw al::backend_exception{al::backend_error::DeviceError, - "No playback channels on device"}; - } - catch(al::backend_exception &e) { - if(tryfmt == DevFmtShort) - throw; - par.clear(); - tryfmt = DevFmtShort; - goto retry_params; + break; + } + catch(al::backend_exception &e) { + if(tryfmt == DevFmtShort) + throw; + par.clear(); + tryfmt = DevFmtShort; + } } if(par.bps == 1) @@ -229,7 +234,7 @@ bool SndioPlayback::reset() mDevice->UpdateSize = par.round; mDevice->BufferSize = par.bufsz + par.round; - mBuffer.resize(mDevice->UpdateSize * par.pchan*par.bps); + mBuffer.resize(size_t{mDevice->UpdateSize} * par.pchan*par.bps); if(par.sig == 1) std::fill(mBuffer.begin(), mBuffer.end(), std::byte{}); else if(par.bits == 8) @@ -292,8 +297,6 @@ struct SndioCapture final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(SndioCapture) }; SndioCapture::~SndioCapture() @@ -306,7 +309,7 @@ SndioCapture::~SndioCapture() int SndioCapture::recordProc() { SetRTPriority(); - althrd_setname(RECORD_THREAD_NAME); + althrd_setname(GetRecordThreadName()); const uint frameSize{mDevice->frameSizeFromFmt()}; @@ -317,29 +320,30 @@ int SndioCapture::recordProc() return 1; } - auto fds = std::make_unique(static_cast(nfds_pre)); + auto fds = std::vector(static_cast(nfds_pre)); while(!mKillNow.load(std::memory_order_acquire) && mDevice->Connected.load(std::memory_order_acquire)) { /* Wait until there's some samples to read. */ - const int nfds{sio_pollfd(mSndHandle, fds.get(), POLLIN)}; + const int nfds{sio_pollfd(mSndHandle, fds.data(), POLLIN)}; if(nfds <= 0) { mDevice->handleDisconnect("Failed to get polling fds: %d", nfds); break; } - int pollres{::poll(fds.get(), static_cast(nfds), 2000)}; + int pollres{::poll(fds.data(), fds.size(), 2000)}; if(pollres < 0) { if(errno == EINTR) continue; - mDevice->handleDisconnect("Poll error: %s", strerror(errno)); + mDevice->handleDisconnect("Poll error: %s", + std::generic_category().message(errno).c_str()); break; } if(pollres == 0) continue; - const int revents{sio_revents(mSndHandle, fds.get())}; + const int revents{sio_revents(mSndHandle, fds.data())}; if((revents&POLLHUP)) { mDevice->handleDisconnect("Got POLLHUP from poll events"); @@ -349,7 +353,7 @@ int SndioCapture::recordProc() continue; auto data = mRing->getWriteVector(); - al::span buffer{data.first.buf, data.first.len*frameSize}; + al::span buffer{data[0].buf, data[0].len*frameSize}; while(!buffer.empty()) { size_t got{sio_read(mSndHandle, buffer.data(), buffer.size())}; @@ -367,14 +371,14 @@ int SndioCapture::recordProc() if(buffer.empty()) { data = mRing->getWriteVector(); - buffer = {data.first.buf, data.first.len*frameSize}; + buffer = {data[0].buf, data[0].len*frameSize}; } } if(buffer.empty()) { /* Got samples to read, but no place to store it. Drop it. */ - static char junk[4096]; - sio_read(mSndHandle, junk, sizeof(junk) - (sizeof(junk)%frameSize)); + static std::array junk; + sio_read(mSndHandle, junk.data(), junk.size() - (junk.size()%frameSize)); } } @@ -385,10 +389,10 @@ int SndioCapture::recordProc() void SndioCapture::open(std::string_view name) { if(name.empty()) - name = sndio_device; - else if(name != sndio_device) + name = GetDefaultName(); + else if(name != GetDefaultName()) throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + al::sizei(name), name.data()}; mSndHandle = sio_open(nullptr, SIO_REC, true); if(mSndHandle == nullptr) @@ -431,8 +435,8 @@ void SndioCapture::open(std::string_view name) par.rchan = mDevice->channelsFromFmt(); par.rate = mDevice->Frequency; - par.appbufsz = maxu(mDevice->BufferSize, mDevice->Frequency/10); - par.round = minu(par.appbufsz/2, mDevice->Frequency/40); + par.appbufsz = std::max(mDevice->BufferSize, mDevice->Frequency/10u); + par.round = std::min(par.appbufsz/2u, mDevice->Frequency/40u); if(!sio_setpar(mSndHandle, &par) || !sio_getpar(mSndHandle, &par)) throw al::backend_exception{al::backend_error::DeviceError, @@ -461,13 +465,13 @@ void SndioCapture::open(std::string_view name) DevFmtTypeString(mDevice->FmtType), DevFmtChannelsString(mDevice->FmtChans), mDevice->Frequency, par.sig?'s':'u', par.bps*8, par.rchan, par.rate}; - mRing = RingBuffer::Create(mDevice->BufferSize, par.bps*par.rchan, false); + mRing = RingBuffer::Create(mDevice->BufferSize, size_t{par.bps}*par.rchan, false); mDevice->BufferSize = static_cast(mRing->writeSpace()); mDevice->UpdateSize = par.round; setDefaultChannelOrder(); - mDevice->DeviceName = name; + mDeviceName = name; } void SndioCapture::start() @@ -497,7 +501,7 @@ void SndioCapture::stop() } void SndioCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } uint SndioCapture::availableSamples() { return static_cast(mRing->readSpace()); } @@ -516,18 +520,15 @@ bool SndIOBackendFactory::init() bool SndIOBackendFactory::querySupport(BackendType type) { return (type == BackendType::Playback || type == BackendType::Capture); } -std::string SndIOBackendFactory::probe(BackendType type) +auto SndIOBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; switch(type) { case BackendType::Playback: case BackendType::Capture: - /* Includes null char. */ - outnames.append(sndio_device, sizeof(sndio_device)); - break; + return std::vector{std::string{GetDefaultName()}}; } - return outnames; + return {}; } BackendPtr SndIOBackendFactory::createBackend(DeviceBase *device, BackendType type) diff --git a/3rdparty/openal/alc/backends/sndio.h b/3rdparty/openal/alc/backends/sndio.h index d94331911790..a4496c92aabf 100644 --- a/3rdparty/openal/alc/backends/sndio.h +++ b/3rdparty/openal/alc/backends/sndio.h @@ -5,15 +5,15 @@ struct SndIOBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_SNDIO_H */ diff --git a/3rdparty/openal/alc/backends/solaris.cpp b/3rdparty/openal/alc/backends/solaris.cpp index 38f9db19b603..84821d35c6ba 100644 --- a/3rdparty/openal/alc/backends/solaris.cpp +++ b/3rdparty/openal/alc/backends/solaris.cpp @@ -41,6 +41,7 @@ #include #include "alc/alconfig.h" +#include "alstring.h" #include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" @@ -51,7 +52,9 @@ namespace { -constexpr char solaris_device[] = "Solaris Default"; +using namespace std::string_view_literals; + +[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "Solaris Default"sv; } std::string solaris_driver{"/dev/audio"}; @@ -74,8 +77,6 @@ struct SolarisBackend final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(SolarisBackend) }; SolarisBackend::~SolarisBackend() @@ -88,10 +89,10 @@ SolarisBackend::~SolarisBackend() int SolarisBackend::mixerProc() { SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); const size_t frame_step{mDevice->channelsFromFmt()}; - const uint frame_size{mDevice->frameSizeFromFmt()}; + const size_t frame_size{mDevice->frameSizeFromFmt()}; while(!mKillNow.load(std::memory_order_acquire) && mDevice->Connected.load(std::memory_order_acquire)) @@ -115,12 +116,12 @@ int SolarisBackend::mixerProc() continue; } - std::byte *write_ptr{mBuffer.data()}; - size_t to_write{mBuffer.size()}; - mDevice->renderSamples(write_ptr, static_cast(to_write/frame_size), frame_step); - while(to_write > 0 && !mKillNow.load(std::memory_order_acquire)) + al::span buffer{mBuffer}; + mDevice->renderSamples(buffer.data(), static_cast(buffer.size()/frame_size), + frame_step); + while(!buffer.empty() && !mKillNow.load(std::memory_order_acquire)) { - ssize_t wrote{write(mFd, write_ptr, to_write)}; + ssize_t wrote{write(mFd, buffer.data(), buffer.size())}; if(wrote < 0) { if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) @@ -130,8 +131,7 @@ int SolarisBackend::mixerProc() break; } - to_write -= static_cast(wrote); - write_ptr += wrote; + buffer = buffer.subspan(static_cast(wrote)); } } @@ -142,10 +142,10 @@ int SolarisBackend::mixerProc() void SolarisBackend::open(std::string_view name) { if(name.empty()) - name = solaris_device; - else if(name != solaris_device) + name = GetDefaultName(); + else if(name != GetDefaultName()) throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + al::sizei(name), name.data()}; int fd{::open(solaris_driver.c_str(), O_WRONLY)}; if(fd == -1) @@ -156,7 +156,7 @@ void SolarisBackend::open(std::string_view name) ::close(mFd); mFd = fd; - mDevice->DeviceName = name; + mDeviceName = name; } bool SolarisBackend::reset() @@ -267,7 +267,7 @@ BackendFactory &SolarisBackendFactory::getFactory() bool SolarisBackendFactory::init() { - if(auto devopt = ConfigValueStr(nullptr, "solaris", "device")) + if(auto devopt = ConfigValueStr({}, "solaris", "device")) solaris_driver = std::move(*devopt); return true; } @@ -275,23 +275,19 @@ bool SolarisBackendFactory::init() bool SolarisBackendFactory::querySupport(BackendType type) { return type == BackendType::Playback; } -std::string SolarisBackendFactory::probe(BackendType type) +auto SolarisBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; switch(type) { case BackendType::Playback: - { - struct stat buf; - if(stat(solaris_driver.c_str(), &buf) == 0) - outnames.append(solaris_device, sizeof(solaris_device)); - } - break; + if(struct stat buf{}; stat(solaris_driver.c_str(), &buf) == 0) + return std::vector{std::string{GetDefaultName()}}; + break; case BackendType::Capture: break; } - return outnames; + return {}; } BackendPtr SolarisBackendFactory::createBackend(DeviceBase *device, BackendType type) diff --git a/3rdparty/openal/alc/backends/solaris.h b/3rdparty/openal/alc/backends/solaris.h index 5da6ad3a95a2..8415c362b8fb 100644 --- a/3rdparty/openal/alc/backends/solaris.h +++ b/3rdparty/openal/alc/backends/solaris.h @@ -5,15 +5,15 @@ struct SolarisBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_SOLARIS_H */ diff --git a/3rdparty/openal/alc/backends/wasapi.cpp b/3rdparty/openal/alc/backends/wasapi.cpp index e26af7c9c60f..11cd998aba39 100644 --- a/3rdparty/openal/alc/backends/wasapi.cpp +++ b/3rdparty/openal/alc/backends/wasapi.cpp @@ -25,10 +25,11 @@ #define WIN32_LEAN_AND_MEAN #include -#include -#include +#include +#include #include +#include #include #include #include @@ -55,14 +56,15 @@ #include #include #include +#include #include #include #include "albit.h" #include "alc/alconfig.h" -#include "alc/events.h" #include "alnumeric.h" #include "alspan.h" +#include "alstring.h" #include "althrd_setname.h" #include "comptr.h" #include "core/converter.h" @@ -72,8 +74,7 @@ #include "ringbuffer.h" #include "strutils.h" -#if defined(ALSOFT_UWP) - +#if ALSOFT_UWP #include // !!This is important!! #include #include @@ -98,7 +99,7 @@ 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) +#if !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 ); @@ -106,14 +107,12 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x namespace { +using namespace std::string_view_literals; using std::chrono::nanoseconds; using std::chrono::milliseconds; using std::chrono::seconds; -using ReferenceTime = std::chrono::duration>; - -inline constexpr ReferenceTime operator "" _reftime(unsigned long long int n) noexcept -{ return ReferenceTime{static_cast(n)}; } +using ReferenceTime = std::chrono::duration>; #define MONO SPEAKER_FRONT_CENTER @@ -157,9 +156,6 @@ constexpr AudioObjectType ChannelMask_Quad{AudioObjectType_FrontLeft | AudioObje 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}; @@ -171,10 +167,13 @@ constexpr AudioObjectType ChannelMask_X714{AudioObjectType_FrontLeft | AudioObje | AudioObjectType_SideRight | AudioObjectType_BackLeft | AudioObjectType_BackRight | AudioObjectType_TopFrontLeft | AudioObjectType_TopFrontRight | AudioObjectType_TopBackLeft | AudioObjectType_TopBackRight}; - - -constexpr char DevNameHead[] = "OpenAL Soft on "; -constexpr size_t DevNameHeadLen{std::size(DevNameHead) - 1}; +constexpr AudioObjectType ChannelMask_X7144{AudioObjectType_FrontLeft | AudioObjectType_FrontRight + | AudioObjectType_FrontCenter | AudioObjectType_LowFrequency | AudioObjectType_SideLeft + | AudioObjectType_SideRight | AudioObjectType_BackLeft | AudioObjectType_BackRight + | AudioObjectType_TopFrontLeft | AudioObjectType_TopFrontRight | AudioObjectType_TopBackLeft + | AudioObjectType_TopBackRight | AudioObjectType_BottomFrontLeft + | AudioObjectType_BottomFrontRight | AudioObjectType_BottomBackLeft + | AudioObjectType_BottomBackRight}; template @@ -185,7 +184,7 @@ overloaded(Ts...) -> overloaded; template -auto as_unsigned(T value) noexcept +constexpr auto as_unsigned(T value) noexcept { using UT = std::make_unsigned_t; return static_cast(value); @@ -202,34 +201,66 @@ constexpr uint RefTime2Samples(const ReferenceTime &val, T srate) noexcept class GuidPrinter { - char mMsg[64]; + std::array mMsg{}; public: GuidPrinter(const GUID &guid) { - std::snprintf(mMsg, std::size(mMsg), "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", + std::snprintf(mMsg.data(), mMsg.size(), "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", DWORD{guid.Data1}, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); } - const char *c_str() const { return mMsg; } + [[nodiscard]] auto c_str() const -> const char* { return mMsg.data(); } }; struct PropVariant { - PROPVARIANT mProp; + PROPVARIANT mProp{}; public: PropVariant() { PropVariantInit(&mProp); } + PropVariant(const PropVariant &rhs) : PropVariant{} { PropVariantCopy(&mProp, &rhs.mProp); } ~PropVariant() { clear(); } + auto operator=(const PropVariant &rhs) -> PropVariant& + { + if(this != &rhs) + PropVariantCopy(&mProp, &rhs.mProp); + return *this; + } + void clear() { PropVariantClear(&mProp); } PROPVARIANT* get() noexcept { return &mProp; } - PROPVARIANT& operator*() noexcept { return mProp; } - const PROPVARIANT& operator*() const noexcept { return mProp; } + /* NOLINTBEGIN(cppcoreguidelines-pro-type-union-access) */ + [[nodiscard]] + auto type() const noexcept -> VARTYPE { return mProp.vt; } - PROPVARIANT* operator->() noexcept { return &mProp; } - const PROPVARIANT* operator->() const noexcept { return &mProp; } + template [[nodiscard]] + auto value() const -> T + { + if constexpr(std::is_same_v) + { + alassert(mProp.vt == VT_UI4 || mProp.vt == VT_UINT); + return mProp.uintVal; + } + else if constexpr(std::is_same_v || std::is_same_v + || std::is_same_v || std::is_same_v) + { + alassert(mProp.vt == VT_LPWSTR); + return mProp.pwszVal; + } + } + + void setBlob(const al::span data) + { + if constexpr(sizeof(size_t) > sizeof(ULONG)) + alassert(data.size() <= std::numeric_limits::max()); + mProp.vt = VT_BLOB; + mProp.blob.cbSize = static_cast(data.size()); + mProp.blob.pBlobData = data.data(); + } + /* NOLINTEND(cppcoreguidelines-pro-type-union-access) */ }; struct DevMap { @@ -248,9 +279,9 @@ struct DevMap { }; DevMap::~DevMap() = default; -bool checkName(const al::span list, const std::string &name) +bool checkName(const al::span 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(); } @@ -271,19 +302,26 @@ struct DeviceList { struct DeviceListLock : public std::unique_lock { using std::unique_lock::unique_lock; - auto& getPlaybackList() const noexcept { return mutex()->mPlayback; } - auto& getCaptureList() const noexcept { return mutex()->mCapture; } + [[nodiscard]] auto& getPlaybackList() const noexcept { return mutex()->mPlayback; } + [[nodiscard]] 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; } + [[nodiscard]] auto getPlaybackDefaultId() const noexcept -> std::wstring_view { return mutex()->mPlaybackDefaultId; } void setCaptureDefaultId(std::wstring_view devid) const { mutex()->mCaptureDefaultId = devid; } - std::wstring_view getCaptureDefaultId() const noexcept { return mutex()->mCaptureDefaultId; } + [[nodiscard]] auto getCaptureDefaultId() const noexcept -> std::wstring_view { return mutex()->mCaptureDefaultId; } }; DeviceList gDeviceList; -#if defined(ALSOFT_UWP) +#ifdef AVRTAPI +struct AvrtHandleCloser { + void operator()(HANDLE handle) { AvRevertMmThreadCharacteristics(handle); } +}; +using AvrtHandlePtr = std::unique_ptr,AvrtHandleCloser>; +#endif + +#if ALSOFT_UWP enum EDataFlow { eRender = 0, eCapture = (eRender + 1), @@ -292,7 +330,7 @@ enum EDataFlow { }; #endif -#if defined(ALSOFT_UWP) +#if ALSOFT_UWP using DeviceHandle = Windows::Devices::Enumeration::DeviceInformation; using EventRegistrationToken = winrt::event_token; #else @@ -300,54 +338,42 @@ using DeviceHandle = ComPtr; #endif -using NameGUIDPair = std::pair; -static NameGUIDPair GetDeviceNameAndGuid(const DeviceHandle &device) +struct NameGUIDPair { std::string mName; std::string mGuid; }; +auto GetDeviceNameAndGuid(const DeviceHandle &device) -> NameGUIDPair { - static constexpr char UnknownName[]{"Unknown Device Name"}; - static constexpr char UnknownGuid[]{"Unknown Device GUID"}; -#if !defined(ALSOFT_UWP) - std::string name, guid; + constexpr auto UnknownName = "Unknown Device Name"sv; + constexpr auto UnknownGuid = "Unknown Device GUID"sv; - ComPtr ps; - HRESULT hr = device->OpenPropertyStore(STGM_READ, al::out_ptr(ps)); +#if !ALSOFT_UWP + auto ps = ComPtr{}; + auto hr = device->OpenPropertyStore(STGM_READ, al::out_ptr(ps)); if(FAILED(hr)) { WARN("OpenPropertyStore failed: 0x%08lx\n", hr); - return std::make_pair(UnknownName, UnknownGuid); + return NameGUIDPair{std::string{UnknownName}, std::string{UnknownGuid}}; } - PropVariant pvprop; + auto ret = NameGUIDPair{}; + auto pvprop = PropVariant{}; hr = ps->GetValue(al::bit_cast(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); + else if(pvprop.type() == VT_LPWSTR) + ret.mName = wstr_to_utf8(pvprop.value()); else - { - WARN("Unexpected Device_FriendlyName PROPVARIANT type: 0x%04x\n", pvprop->vt); - name = UnknownName; - } + WARN("Unexpected Device_FriendlyName PROPVARIANT type: 0x%04x\n", pvprop.type()); pvprop.clear(); hr = ps->GetValue(al::bit_cast(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 if(pvprop.type() == VT_LPWSTR) + ret.mGuid = wstr_to_utf8(pvprop.value()); else - { - WARN("Unexpected AudioEndpoint_GUID PROPVARIANT type: 0x%04x\n", pvprop->vt); - guid = UnknownGuid; - } + WARN("Unexpected AudioEndpoint_GUID PROPVARIANT type: 0x%04x\n", pvprop.type()); #else - std::string name{wstr_to_utf8(device.Name())}; - std::string guid; + auto ret = NameGUIDPair{wstr_to_utf8(device.Name()), {}}; + // 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"}.")) @@ -355,18 +381,18 @@ static NameGUIDPair GetDeviceNameAndGuid(const DeviceHandle &device) devIdStart += 2; // L"}." if(auto devIdStartEnd = wcschr(devIdStart, L'#')) { - std::wstring wDevId{devIdStart, static_cast(devIdStartEnd - devIdStart)}; - guid = wstr_to_utf8(wDevId.c_str()); - std::transform(guid.begin(), guid.end(), guid.begin(), + ret.mGuid = wstr_to_utf8(std::wstring_view{devIdStart, + static_cast(devIdStartEnd - devIdStart)}); + std::transform(ret.mGuid.begin(), ret.mGuid.end(), ret.mGuid.begin(), [](char ch) { return static_cast(std::toupper(ch)); }); } } - if(name.empty()) name = UnknownName; - if(guid.empty()) guid = UnknownGuid; #endif - return std::make_pair(std::move(name), std::move(guid)); + if(ret.mName.empty()) ret.mName = UnknownName; + if(ret.mGuid.empty()) ret.mGuid = UnknownGuid; + return ret; } -#if !defined(ALSOFT_UWP) +#if !ALSOFT_UWP EndpointFormFactor GetDeviceFormfactor(IMMDevice *device) { ComPtr ps; @@ -382,53 +408,59 @@ EndpointFormFactor GetDeviceFormfactor(IMMDevice *device) hr = ps->GetValue(PKEY_AudioEndpoint_FormFactor, pvform.get()); if(FAILED(hr)) WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr); - else if(pvform->vt == VT_UI4) - formfactor = static_cast(pvform->ulVal); - else if(pvform->vt != VT_EMPTY) - WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform->vt); + else if(pvform.type() == VT_UI4) + formfactor = static_cast(pvform.value()); + else if(pvform.type() != VT_EMPTY) + WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform.type()); return formfactor; } #endif -#if defined(ALSOFT_UWP) +#if ALSOFT_UWP struct DeviceHelper final : public IActivateAudioInterfaceCompletionHandler #else struct DeviceHelper final : private IMMNotificationClient #endif { +#if ALSOFT_UWP DeviceHelper() { -#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) + static constexpr auto playback_cb = [](const IInspectable &sender [[maybe_unused]], + const DefaultAudioRenderDeviceChangedEventArgs &args) + { + if(args.Role() == AudioDeviceRole::Default) { - const std::string msg{ "Default playback device changed: " + + const auto msg = std::string{"Default playback device changed: " + wstr_to_utf8(args.Id())}; - alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback, - msg); + alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback, msg); } - }); + }; + mRenderDeviceChangedToken = MediaDevice::DefaultAudioRenderDeviceChanged(playback_cb); - mCaptureDeviceChangedToken = MediaDevice::DefaultAudioCaptureDeviceChanged([this](const IInspectable& /*sender*/, const DefaultAudioCaptureDeviceChangedEventArgs& args) { - if (args.Role() == AudioDeviceRole::Default) + static constexpr auto capture_cb = [](const IInspectable &sender [[maybe_unused]], + 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); + const auto msg = std::string{"Default capture device changed: " + + wstr_to_utf8(args.Id())}; + alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture, msg); } - }); -#endif + }; + mCaptureDeviceChangedToken = MediaDevice::DefaultAudioCaptureDeviceChanged(capture_cb); } +#else + DeviceHelper() = default; +#endif ~DeviceHelper() { -#if defined(ALSOFT_UWP) +#if ALSOFT_UWP MediaDevice::DefaultAudioRenderDeviceChanged(mRenderDeviceChangedToken); MediaDevice::DefaultAudioCaptureDeviceChanged(mCaptureDeviceChangedToken); @@ -442,6 +474,9 @@ struct DeviceHelper final : private IMMNotificationClient #endif } + template + auto as() noexcept -> T { return T{this}; } + /** -------------------------- IUnknown ----------------------------- */ std::atomic mRefCount{1}; STDMETHODIMP_(ULONG) AddRef() noexcept override { return mRefCount.fetch_add(1u) + 1u; } @@ -466,24 +501,24 @@ struct DeviceHelper final : private IMMNotificationClient // 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 ALSOFT_UWP if(IId == __uuidof(IActivateAudioInterfaceCompletionHandler)) { - *UnknownPtrPtr = static_cast(this); + *UnknownPtrPtr = as(); AddRef(); return S_OK; } #else if(IId == __uuidof(IMMNotificationClient)) { - *UnknownPtrPtr = static_cast(this); + *UnknownPtrPtr = as(); AddRef(); return S_OK; } #endif else if(IId == __uuidof(IAgileObject) || IId == __uuidof(IUnknown)) { - *UnknownPtrPtr = static_cast(this); + *UnknownPtrPtr = as(); AddRef(); return S_OK; } @@ -493,7 +528,7 @@ struct DeviceHelper final : private IMMNotificationClient return E_NOINTERFACE; } -#if defined(ALSOFT_UWP) +#if ALSOFT_UWP /** ----------------------- IActivateAudioInterfaceCompletionHandler ------------ */ HRESULT ActivateCompleted(IActivateAudioInterfaceAsyncOperation*) override { @@ -571,6 +606,7 @@ struct DeviceHelper final : private IMMNotificationClient return S_OK; } + /* NOLINTNEXTLINE(clazy-function-args-by-ref) */ STDMETHODIMP OnPropertyValueChanged(LPCWSTR /*pwstrDeviceId*/, const PROPERTYKEY /*key*/) noexcept override { return S_OK; } STDMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId) noexcept override @@ -599,7 +635,7 @@ struct DeviceHelper final : private IMMNotificationClient /** -------------------------- DeviceHelper ----------------------------- */ HRESULT init() { -#if !defined(ALSOFT_UWP) +#if !ALSOFT_UWP HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), al::out_ptr(mEnumerator))}; if(SUCCEEDED(hr)) @@ -614,7 +650,7 @@ struct DeviceHelper final : private IMMNotificationClient HRESULT openDevice(std::wstring_view devid, EDataFlow flow, DeviceHandle& device) { -#if !defined(ALSOFT_UWP) +#if !ALSOFT_UWP HRESULT hr{E_FAIL}; if(mEnumerator) { @@ -640,7 +676,7 @@ struct DeviceHelper final : private IMMNotificationClient #endif } -#if !defined(ALSOFT_UWP) +#if !ALSOFT_UWP static HRESULT activateAudioClient(_In_ DeviceHandle &device, REFIID iid, void **ppv) { return device->Activate(iid, CLSCTX_INPROC_SERVER, nullptr, ppv); } #else @@ -678,7 +714,7 @@ struct DeviceHelper final : private IMMNotificationClient std::wstring defaultId; std::vector{}.swap(list); -#if !defined(ALSOFT_UWP) +#if !ALSOFT_UWP ComPtr coll; HRESULT hr{mEnumerator->EnumAudioEndpoints(flowdir, DEVICE_STATE_ACTIVE, al::out_ptr(coll))}; @@ -722,12 +758,13 @@ struct DeviceHelper final : private IMMNotificationClient const auto deviceRole = Windows::Media::Devices::AudioDeviceRole::Default; auto DefaultAudioId = flowdir == eRender ? MediaDevice::GetDefaultAudioRenderId(deviceRole) : MediaDevice::GetDefaultAudioCaptureId(deviceRole); - if (DefaultAudioId.empty()) - return defaultId; - - auto deviceInfo = DeviceInformation::CreateFromIdAsync(DefaultAudioId, nullptr, DeviceInformationKind::DeviceInterface).get(); - if(!deviceInfo) - return defaultId; + if(!DefaultAudioId.empty()) + { + auto deviceInfo = DeviceInformation::CreateFromIdAsync(DefaultAudioId, nullptr, + DeviceInformationKind::DeviceInterface).get(); + if(deviceInfo) + defaultId = deviceInfo.Id().data(); + } // Get the string identifier of the audio renderer auto AudioSelector = flowdir == eRender ? MediaDevice::GetAudioRenderSelector() : MediaDevice::GetAudioCaptureSelector(); @@ -740,7 +777,7 @@ struct DeviceHelper final : private IMMNotificationClient auto deviceCount = DeviceInfoCollection.Size(); for(unsigned int i{0};i < deviceCount;++i) { - deviceInfo = DeviceInfoCollection.GetAt(i); + auto deviceInfo = DeviceInfoCollection.GetAt(i); if(deviceInfo) std::ignore = AddDevice(deviceInfo, deviceInfo.Id().data(), list); } @@ -764,14 +801,14 @@ struct DeviceHelper final : private IMMNotificationClient auto name_guid = GetDeviceNameAndGuid(device); int count{1}; - std::string newname{name_guid.first}; + std::string newname{name_guid.mName}; while(checkName(list, newname)) { - newname = name_guid.first; + newname = name_guid.mName; newname += " #"; newname += std::to_string(++count); } - list.emplace_back(std::move(newname), std::move(name_guid.second), devid); + list.emplace_back(std::move(newname), std::move(name_guid.mGuid), devid); const DevMap &newentry = list.back(); TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", newentry.name.c_str(), @@ -779,7 +816,7 @@ struct DeviceHelper final : private IMMNotificationClient return true; } -#if !defined(ALSOFT_UWP) +#if !ALSOFT_UWP static WCHAR *GetDeviceId(IMMDevice *device) { WCHAR *devid; @@ -816,6 +853,7 @@ bool MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in) { out->Format = *in; out->Format.cbSize = 0; + /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */ out->Samples.wValidBitsPerSample = out->Format.wBitsPerSample; if(out->Format.nChannels == 1) out->dwChannelMask = MONO; @@ -829,6 +867,7 @@ bool MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in) { out->Format = *in; out->Format.cbSize = 0; + /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */ out->Samples.wValidBitsPerSample = out->Format.wBitsPerSample; if(out->Format.nChannels == 1) out->dwChannelMask = MONO; @@ -853,6 +892,7 @@ void TraceFormat(const char *msg, const WAVEFORMATEX *format) { const WAVEFORMATEXTENSIBLE *fmtex{ CONTAINING_RECORD(format, const WAVEFORMATEXTENSIBLE, Format)}; + /* NOLINTBEGIN(cppcoreguidelines-pro-type-union-access) */ TRACE("%s:\n" " FormatTag = 0x%04x\n" " Channels = %d\n" @@ -868,6 +908,7 @@ void TraceFormat(const char *msg, const WAVEFORMATEX *format) fmtex->Format.nAvgBytesPerSec, fmtex->Format.nBlockAlign, fmtex->Format.wBitsPerSample, fmtex->Format.cbSize, fmtex->Samples.wReserved, fmtex->dwChannelMask, GuidPrinter{fmtex->SubFormat}.c_str()); + /* NOLINTEND(cppcoreguidelines-pro-type-union-access) */ } else TRACE("%s:\n" @@ -883,6 +924,56 @@ void TraceFormat(const char *msg, const WAVEFORMATEX *format) } +/* Duplicates the first sample of each sample frame to the second sample, at + * half volume. Essentially converting mono to stereo. + */ +template +void DuplicateSamples(al::span insamples, size_t step) +{ + auto samples = al::span{reinterpret_cast(insamples.data()), insamples.size()/sizeof(T)}; + if constexpr(std::is_floating_point_v) + { + for(size_t i{0};i < samples.size();i+=step) + { + const auto s = samples[i] * T{0.5}; + samples[i+1] = samples[i] = s; + } + } + else if constexpr(std::is_signed_v) + { + for(size_t i{0};i < samples.size();i+=step) + { + const auto s = samples[i] / 2; + samples[i+1] = samples[i] = T(s); + } + } + else + { + using ST = std::make_signed_t; + static constexpr auto SignBit = T{1u << (sizeof(T)*8 - 1)}; + for(size_t i{0};i < samples.size();i+=step) + { + const auto s = static_cast(samples[i]^SignBit) / 2; + samples[i+1] = samples[i] = T(s)^SignBit; + } + } +} + +void DuplicateSamples(al::span insamples, DevFmtType sampletype, size_t step) +{ + switch(sampletype) + { + case DevFmtByte: return DuplicateSamples(insamples, step); + case DevFmtUByte: return DuplicateSamples(insamples, step); + case DevFmtShort: return DuplicateSamples(insamples, step); + case DevFmtUShort: return DuplicateSamples(insamples, step); + case DevFmtInt: return DuplicateSamples(insamples, step); + case DevFmtUInt: return DuplicateSamples(insamples, step); + case DevFmtFloat: return DuplicateSamples(insamples, step); + } +} + + enum class MsgType { OpenDevice, ResetDevice, @@ -910,8 +1001,14 @@ constexpr const char *GetMessageTypeName(MsgType type) noexcept /* Proxy interface used by the message handler. */ struct WasapiProxy { + WasapiProxy() = default; + WasapiProxy(const WasapiProxy&) = delete; + WasapiProxy(WasapiProxy&&) = delete; virtual ~WasapiProxy() = default; + void operator=(const WasapiProxy&) = delete; + void operator=(WasapiProxy&&) = delete; + virtual HRESULT openProxy(std::string_view name) = 0; virtual void closeProxy() = 0; @@ -930,6 +1027,7 @@ struct WasapiProxy { static inline std::deque mMsgQueue; static inline std::mutex mMsgQueueLock; static inline std::condition_variable mMsgQueueCond; + static inline DWORD sAvIndex{}; static inline std::optional sDeviceHelper; @@ -938,7 +1036,7 @@ struct WasapiProxy { std::promise promise; std::future future{promise.get_future()}; { - std::lock_guard _{mMsgQueueLock}; + std::lock_guard msglock{mMsgQueueLock}; mMsgQueue.emplace_back(Msg{type, this, param, std::move(promise)}); } mMsgQueueCond.notify_one(); @@ -950,7 +1048,7 @@ struct WasapiProxy { std::promise promise; std::future future{promise.get_future()}; { - std::lock_guard _{mMsgQueueLock}; + std::lock_guard msglock{mMsgQueueLock}; mMsgQueue.emplace_back(Msg{type, nullptr, {}, std::move(promise)}); } mMsgQueueCond.notify_one(); @@ -973,19 +1071,27 @@ int WasapiProxy::messageHandler(std::promise *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; } - hr = sDeviceHelper.emplace().init(); + struct HelperResetter { + HelperResetter() = default; + HelperResetter(const HelperResetter&) = delete; + auto operator=(const HelperResetter&) -> HelperResetter& = delete; + ~HelperResetter() { sDeviceHelper.reset(); } + }; + HelperResetter scoped_watcher; + + HRESULT hr{sDeviceHelper.emplace().init()}; promise->set_value(hr); promise = nullptr; if(FAILED(hr)) - goto skip_loop; + return 0; { auto devlock = DeviceListLock{gDeviceList}; @@ -1000,8 +1106,7 @@ int WasapiProxy::messageHandler(std::promise *promise) { TRACE("Got message \"%s\" (0x%04x, this=%p, param=\"%.*s\")\n", GetMessageTypeName(msg.mType), static_cast(msg.mType), - static_cast(msg.mProxy), static_cast(msg.mParam.length()), - msg.mParam.data()); + static_cast(msg.mProxy), al::sizei(msg.mParam), msg.mParam.data()); switch(msg.mType) { @@ -1038,10 +1143,6 @@ int WasapiProxy::messageHandler(std::promise *promise) } TRACE("Message loop finished\n"); -skip_loop: - sDeviceHelper.reset(); - CoUninitialize(); - return 0; } @@ -1068,6 +1169,8 @@ struct WasapiPlayback final : public BackendBase, WasapiProxy { void prepareFormat(WAVEFORMATEXTENSIBLE &OutputType); void finalizeFormat(WAVEFORMATEXTENSIBLE &OutputType); + auto initSpatial() -> bool; + HRESULT mOpenStatus{E_FAIL}; DeviceHandle mMMDev{nullptr}; @@ -1080,13 +1183,14 @@ struct WasapiPlayback final : public BackendBase, WasapiProxy { ComPtr mRender{nullptr}; AudioObjectType mStaticMask{}; }; - std::variant mAudio; + std::variant mAudio{std::in_place_index_t<0>{}}; HANDLE mNotifyEvent{nullptr}; - UINT32 mOrigBufferSize{}, mOrigUpdateSize{}; - std::unique_ptr mResampleBuffer{}; + UINT32 mOutBufferSize{}, mOutUpdateSize{}; + std::vector mResampleBuffer{}; uint mBufferFilled{0}; SampleConverterPtr mResampler; + bool mMonoUpsample{false}; WAVEFORMATEXTENSIBLE mFormat{}; std::atomic mPadding{0u}; @@ -1095,8 +1199,6 @@ struct WasapiPlayback final : public BackendBase, WasapiProxy { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(WasapiPlayback) }; WasapiPlayback::~WasapiPlayback() @@ -1113,26 +1215,38 @@ 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(mAudio); SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); const uint frame_size{mFormat.Format.nChannels * mFormat.Format.wBitsPerSample / 8u}; - const uint update_size{mOrigUpdateSize}; - const UINT32 buffer_len{mOrigBufferSize}; + const uint update_size{mOutUpdateSize}; + const UINT32 buffer_len{mOutBufferSize}; + const void *resbufferptr{}; + +#ifdef AVRTAPI + /* TODO: "Audio" or "Pro Audio"? The suggestion is to use "Pro Audio" for + * device periods less than 10ms, and "Audio" for greater than or equal to + * 10ms. + */ + auto taskname = (update_size < mFormat.Format.nSamplesPerSec/100) ? L"Pro Audio" : L"Audio"; + auto avhandle = AvrtHandlePtr{AvSetMmThreadCharacteristicsW(taskname, &sAvIndex)}; +#endif + + mBufferFilled = 0; while(!mKillNow.load(std::memory_order_relaxed)) { UINT32 written; - hr = audio.mClient->GetCurrentPadding(&written); + HRESULT hr{audio.mClient->GetCurrentPadding(&written)}; if(FAILED(hr)) { ERR("Failed to get padding: 0x%08lx\n", hr); @@ -1156,37 +1270,39 @@ FORCE_ALIGN int WasapiPlayback::mixerProc() { if(mResampler) { - std::lock_guard _{mMutex}; + std::lock_guard dlock{mMutex}; + auto dst = al::span{buffer, size_t{len}*frame_size}; for(UINT32 done{0};done < len;) { if(mBufferFilled == 0) { - mDevice->renderSamples(mResampleBuffer.get(), mDevice->UpdateSize, + mDevice->renderSamples(mResampleBuffer.data(), mDevice->UpdateSize, mFormat.Format.nChannels); + resbufferptr = mResampleBuffer.data(); mBufferFilled = mDevice->UpdateSize; } - const void *src{mResampleBuffer.get()}; - uint srclen{mBufferFilled}; - uint got{mResampler->convert(&src, &srclen, buffer, len-done)}; - buffer += got*frame_size; + uint got{mResampler->convert(&resbufferptr, &mBufferFilled, dst.data(), + len-done)}; + dst = dst.subspan(size_t{got}*frame_size); done += got; mPadding.store(written + done, std::memory_order_relaxed); - if(srclen) - { - const char *bsrc{static_cast(src)}; - std::copy(bsrc, bsrc + srclen*frame_size, mResampleBuffer.get()); - } - mBufferFilled = srclen; } } else { - std::lock_guard _{mMutex}; + std::lock_guard dlock{mMutex}; mDevice->renderSamples(buffer, len, mFormat.Format.nChannels); mPadding.store(written + len, std::memory_order_relaxed); } + + if(mMonoUpsample) + { + DuplicateSamples(al::span{buffer, size_t{len}*frame_size}, mDevice->FmtType, + mFormat.Format.nChannels); + } + hr = audio.mRender->ReleaseBuffer(len, 0); } if(FAILED(hr)) @@ -1198,43 +1314,48 @@ FORCE_ALIGN int WasapiPlayback::mixerProc() } mPadding.store(0u, std::memory_order_release); - CoUninitialize(); return 0; } FORCE_ALIGN int WasapiPlayback::mixerSpatialProc() { - 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(mAudio); SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); std::vector> channels; - std::vector buffers; - std::vector resbuffers; + std::vector buffers; + std::vector resbuffers; std::vector tmpbuffers; +#ifdef AVRTAPI + auto taskname = (mOutUpdateSize < mFormat.Format.nSamplesPerSec/100) ? L"Pro Audio" : L"Audio"; + auto avhandle = AvrtHandlePtr{AvSetMmThreadCharacteristicsW(taskname, &sAvIndex)}; +#endif + /* 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(mDevice->BufferSize-mDevice->UpdateSize, std::memory_order_release); + mPadding.store(mOutBufferSize-mOutUpdateSize, 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); - hr = audio.mRender->Reset(); + HRESULT hr{audio.mRender->Reset()}; if(FAILED(hr)) { ERR("ISpatialAudioObjectRenderStream::Reset failed: 0x%08lx\n", hr); @@ -1244,7 +1365,7 @@ FORCE_ALIGN int WasapiPlayback::mixerSpatialProc() } UINT32 dynamicCount{}, framesToDo{}; - hr = audio.mRender->BeginUpdatingAudioObjects(&dynamicCount, &framesToDo); + HRESULT hr{audio.mRender->BeginUpdatingAudioObjects(&dynamicCount, &framesToDo)}; if(SUCCEEDED(hr)) { if(channels.empty()) UNLIKELY @@ -1265,9 +1386,12 @@ FORCE_ALIGN int WasapiPlayback::mixerSpatialProc() { tmpbuffers.resize(buffers.size()); resbuffers.resize(buffers.size()); + auto bufptr = mResampleBuffer.begin(); for(size_t i{0};i < tmpbuffers.size();++i) - resbuffers[i] = reinterpret_cast(mResampleBuffer.get()) + - mDevice->UpdateSize*i; + { + resbuffers[i] = al::to_address(bufptr); + bufptr += ptrdiff_t(mDevice->UpdateSize*sizeof(float)); + } } } @@ -1275,19 +1399,19 @@ FORCE_ALIGN int WasapiPlayback::mixerSpatialProc() * update, unfortunately. */ std::transform(channels.cbegin(), channels.cend(), buffers.begin(), - [](const ComPtr &obj) -> float* + [](const ComPtr &obj) -> void* { - BYTE *buffer{}; - UINT32 size{}; + auto buffer = LPBYTE{}; + auto size = UINT32{}; obj->GetBuffer(&buffer, &size); - return reinterpret_cast(buffer); + return buffer; }); if(!mResampler) mDevice->renderSamples(buffers, framesToDo); else { - std::lock_guard _{mMutex}; + std::lock_guard dlock{mMutex}; for(UINT32 pos{0};pos < framesToDo;) { if(mBufferFilled == 0) @@ -1298,9 +1422,9 @@ FORCE_ALIGN int WasapiPlayback::mixerSpatialProc() } const uint got{mResampler->convertPlanar(tmpbuffers.data(), &mBufferFilled, - reinterpret_cast(buffers.data()), framesToDo-pos)}; + buffers.data(), framesToDo-pos)}; for(auto &buf : buffers) - buf += got; + buf = static_cast(buf) + got; /* NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ pos += got; } } @@ -1313,7 +1437,6 @@ FORCE_ALIGN int WasapiPlayback::mixerSpatialProc() } mPadding.store(0u, std::memory_order_release); - CoUninitialize(); return 0; } @@ -1332,12 +1455,6 @@ void WasapiPlayback::open(std::string_view name) "Failed to create notify events"}; } - if(name.length() >= DevNameHeadLen - && std::strncmp(name.data(), DevNameHead, DevNameHeadLen) == 0) - { - name = name.substr(DevNameHeadLen); - } - mOpenStatus = pushMessage(MsgType::OpenDevice, name).get(); if(FAILED(mOpenStatus)) throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx", @@ -1364,8 +1481,7 @@ HRESULT WasapiPlayback::openProxy(std::string_view name) } if(iter == list.cend()) { - WARN("Failed to find device name matching \"%.*s\"\n", static_cast(name.length()), - name.data()); + WARN("Failed to find device name matching \"%.*s\"\n", al::sizei(name), name.data()); return E_FAIL; } devname = iter->name; @@ -1379,16 +1495,16 @@ HRESULT WasapiPlayback::openProxy(std::string_view name) return hr; } if(!devname.empty()) - mDevice->DeviceName = DevNameHead + std::move(devname); + mDeviceName = std::move(devname); else - mDevice->DeviceName = DevNameHead + GetDeviceNameAndGuid(mMMDev).first; + mDeviceName = GetDeviceNameAndGuid(mMMDev).mName; return S_OK; } void WasapiPlayback::closeProxy() { - mAudio.emplace(); + mAudio.emplace(); mMMDev = nullptr; } @@ -1467,6 +1583,9 @@ void WasapiPlayback::prepareFormat(WAVEFORMATEXTENSIBLE &OutputType) OutputType.Format.nChannels = 8; OutputType.dwChannelMask = X7DOT1; break; + case DevFmtX7144: + mDevice->FmtChans = DevFmtX714; + /*fall-through*/ case DevFmtX714: OutputType.Format.nChannels = 12; OutputType.dwChannelMask = X7DOT1DOT4; @@ -1479,7 +1598,6 @@ void WasapiPlayback::prepareFormat(WAVEFORMATEXTENSIBLE &OutputType) /* fall-through */ case DevFmtUByte: OutputType.Format.wBitsPerSample = 8; - OutputType.Samples.wValidBitsPerSample = 8; OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; break; case DevFmtUShort: @@ -1487,7 +1605,6 @@ void WasapiPlayback::prepareFormat(WAVEFORMATEXTENSIBLE &OutputType) /* fall-through */ case DevFmtShort: OutputType.Format.wBitsPerSample = 16; - OutputType.Samples.wValidBitsPerSample = 16; OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; break; case DevFmtUInt: @@ -1495,15 +1612,15 @@ void WasapiPlayback::prepareFormat(WAVEFORMATEXTENSIBLE &OutputType) /* fall-through */ case DevFmtInt: OutputType.Format.wBitsPerSample = 32; - OutputType.Samples.wValidBitsPerSample = 32; OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; break; case DevFmtFloat: OutputType.Format.wBitsPerSample = 32; - OutputType.Samples.wValidBitsPerSample = 32; OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; break; } + /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */ + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; OutputType.Format.nSamplesPerSec = mDevice->Frequency; OutputType.Format.nBlockAlign = static_cast(OutputType.Format.nChannels * @@ -1514,10 +1631,10 @@ void WasapiPlayback::prepareFormat(WAVEFORMATEXTENSIBLE &OutputType) void WasapiPlayback::finalizeFormat(WAVEFORMATEXTENSIBLE &OutputType) { - if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "wasapi", "allow-resampler", true)) - mDevice->Frequency = OutputType.Format.nSamplesPerSec; + if(!GetConfigValueBool(mDevice->mDeviceName, "wasapi", "allow-resampler", true)) + mDevice->Frequency = uint(OutputType.Format.nSamplesPerSec); else - mDevice->Frequency = minu(mDevice->Frequency, OutputType.Format.nSamplesPerSec); + mDevice->Frequency = std::min(mDevice->Frequency, uint(OutputType.Format.nSamplesPerSec)); const uint32_t chancount{OutputType.Format.nChannels}; const DWORD chanmask{OutputType.dwChannelMask}; @@ -1535,6 +1652,12 @@ void WasapiPlayback::finalizeFormat(WAVEFORMATEXTENSIBLE &OutputType) { case DevFmtMono: chansok = (chancount >= 1 && ((chanmask&MonoMask) == MONO || !chanmask)); + if(!chansok && chancount >= 2 && (chanmask&StereoMask) == STEREO) + { + /* Mono rendering with stereo+ output is handled specially. */ + chansok = true; + mMonoUpsample = true; + } break; case DevFmtStereo: chansok = (chancount >= 2 && ((chanmask&StereoMask) == STEREO || !chanmask)); @@ -1555,6 +1678,7 @@ void WasapiPlayback::finalizeFormat(WAVEFORMATEXTENSIBLE &OutputType) break; case DevFmtX714: chansok = (chancount >= 12 && ((chanmask&X714Mask) == X7DOT1DOT4 || !chanmask)); + case DevFmtX7144: case DevFmtAmbi3D: break; } @@ -1614,252 +1738,230 @@ void WasapiPlayback::finalizeFormat(WAVEFORMATEXTENSIBLE &OutputType) OutputType.Format.wBitsPerSample = 16; OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; } + /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */ OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; } -bool WasapiPlayback::reset() +auto WasapiPlayback::initSpatial() -> bool { - HRESULT hr{pushMessage(MsgType::ResetDevice).get()}; + auto &audio = mAudio.emplace(); + HRESULT hr{sDeviceHelper->activateAudioClient(mMMDev, __uuidof(ISpatialAudioClient), + al::out_ptr(audio.mClient))}; 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)) { - auto &audio = mAudio.emplace(); - HRESULT hr{sDeviceHelper->activateAudioClient(mMMDev, __uuidof(ISpatialAudioClient), - al::out_ptr(audio.mClient))}; - if(FAILED(hr)) - { - ERR("Failed to activate spatial audio client: 0x%08lx\n", hr); - goto no_spatial; - } + ERR("Failed to activate spatial audio client: 0x%08lx\n", hr); + return false; + } - ComPtr 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; - } + ComPtr fmtenum; + hr = audio.mClient->GetSupportedAudioObjectFormatEnumerator(al::out_ptr(fmtenum)); + if(FAILED(hr)) + { + ERR("Failed to get format enumerator: 0x%08lx\n", hr); + return false; + } - UINT32 fmtcount{}; - hr = fmtenum->GetCount(&fmtcount); - if(FAILED(hr) || fmtcount == 0) - { - ERR("Failed to get format count: 0x%08lx\n", hr); - goto no_spatial; - } + UINT32 fmtcount{}; + hr = fmtenum->GetCount(&fmtcount); + if(FAILED(hr) || fmtcount == 0) + { + ERR("Failed to get format count: 0x%08lx\n", hr); + return false; + } - 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); + WAVEFORMATEX *preferredFormat{}; + hr = fmtenum->GetFormat(0, &preferredFormat); + if(FAILED(hr)) + { + ERR("Failed to get preferred format: 0x%08lx\n", hr); + return false; + } + TraceFormat("Preferred mix format", preferredFormat); - UINT32 maxFrames{}; - hr = audio.mClient->GetMaxFrameCount(preferredFormat, &maxFrames); + 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 get max frames: 0x%08lx\n", hr); + ERR("Failed to format %u: 0x%08lx\n", i+1, hr); else - TRACE("Max sample frames: %u\n", maxFrames); - for(UINT32 i{1};i < fmtcount;++i) { - WAVEFORMATEX *otherFormat{}; - hr = fmtenum->GetFormat(i, &otherFormat); + TraceFormat("Other mix format", otherFormat); + UINT32 otherMaxFrames{}; + hr = audio.mClient->GetMaxFrameCount(otherFormat, &otherMaxFrames); if(FAILED(hr)) - ERR("Failed to format %u: 0x%08lx\n", i+1, hr); + ERR("Failed to get max frames: 0x%08lx\n", hr); else - { - 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); - } + TRACE("Max sample frames: %u\n", otherMaxFrames); } + } - WAVEFORMATEXTENSIBLE OutputType; - if(!MakeExtensible(&OutputType, preferredFormat)) - goto no_spatial; + WAVEFORMATEXTENSIBLE OutputType; + if(!MakeExtensible(&OutputType, preferredFormat)) + return false; - /* 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(OutputType.Format.nBlockAlign * 32 - / OutputType.Format.wBitsPerSample); - OutputType.Format.wBitsPerSample = 32; - } - OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; - OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + /* This seems to be the format of each "object", which should be mono. */ + if(!(OutputType.Format.nChannels == 1 + && (OutputType.dwChannelMask == MONO || !OutputType.dwChannelMask))) + ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, + OutputType.dwChannelMask); - /* Match the output rate if not requesting anything specific. */ - if(!mDevice->Flags.test(FrequencyRequest)) - mDevice->Frequency = OutputType.Format.nSamplesPerSec; + /* 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(OutputType.Format.nBlockAlign * 32 + / OutputType.Format.wBitsPerSample); + OutputType.Format.wBitsPerSample = 32; + } + /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */ + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - 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) - 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; - /* 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 + /* Match the output rate if not requesting anything specific. */ + if(!mDevice->Flags.test(FrequencyRequest)) + mDevice->Frequency = OutputType.Format.nSamplesPerSec; + + auto getTypeMask = [](DevFmtChannels chans) noexcept + { + switch(chans) { - const uint32_t chancount{OutputType.Format.nChannels}; - const DWORD chanmask{OutputType.dwChannelMask}; - isRear51 = (chancount == 6 && (chanmask&X51RearMask) == X5DOT1REAR); + case DevFmtMono: return ChannelMask_Mono; + case DevFmtStereo: return ChannelMask_Stereo; + case DevFmtQuad: return ChannelMask_Quad; + case DevFmtX51: return ChannelMask_X51; + case DevFmtX61: return ChannelMask_X61; + case DevFmtX3D71: [[fallthrough]]; + case DevFmtX71: return ChannelMask_X71; + case DevFmtX714: return ChannelMask_X714; + case DevFmtX7144: return ChannelMask_X7144; + case DevFmtAmbi3D: + break; } + return ChannelMask_Stereo; + }; - auto getTypeMask = [isRear51](DevFmtChannels chans) noexcept - { - switch(chans) - { - 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; - SpatialAudioObjectRenderStreamActivationParams streamParams{}; - streamParams.ObjectFormat = &OutputType.Format; - streamParams.StaticObjectTypeMask = getTypeMask(mDevice->FmtChans); - streamParams.Category = AudioCategory_Media; - streamParams.EventHandle = mNotifyEvent; + PropVariant paramProp{}; + paramProp.setBlob({reinterpret_cast(&streamParams), sizeof(streamParams)}); - PropVariant paramProp{}; - paramProp->vt = VT_BLOB; - paramProp->blob.cbSize = sizeof(streamParams); - paramProp->blob.pBlobData = reinterpret_cast(&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); + return false; + } - 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; - } + audio.mStaticMask = streamParams.StaticObjectTypeMask; + mFormat = OutputType; - 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->mDeviceName, "wasapi", "allow-resampler", true)) + mDevice->Frequency = uint(OutputType.Format.nSamplesPerSec); + else + mDevice->Frequency = std::min(mDevice->Frequency, + uint(OutputType.Format.nSamplesPerSec)); - 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}; + 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. + */ + mOutUpdateSize = mDevice->UpdateSize; + mOutBufferSize = mOutUpdateSize*2; + ReferenceTime per_time{ReferenceTime{seconds{mDevice->UpdateSize}} / mDevice->Frequency}; - ComPtr tmpClient; - hr = sDeviceHelper->activateAudioClient(mMMDev, __uuidof(IAudioClient), - al::out_ptr(tmpClient)); + ComPtr 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 + { + hr = tmpClient->GetDevicePeriod(&reinterpret_cast(per_time), nullptr); if(FAILED(hr)) - ERR("Failed to activate audio client: 0x%08lx\n", hr); + ERR("Failed to get device period: 0x%08lx\n", hr); else { - hr = tmpClient->GetDevicePeriod(&reinterpret_cast(per_time), nullptr); - if(FAILED(hr)) - ERR("Failed to get device period: 0x%08lx\n", hr); - else - { - mOrigUpdateSize = RefTime2Samples(per_time, mFormat.Format.nSamplesPerSec); - mOrigBufferSize = mOrigUpdateSize*2; - } + mOutUpdateSize = RefTime2Samples(per_time, mFormat.Format.nSamplesPerSec); + mOutBufferSize = mOutUpdateSize*2; } - tmpClient = nullptr; + } + tmpClient = nullptr; - mDevice->UpdateSize = RefTime2Samples(per_time, mDevice->Frequency); - mDevice->BufferSize = mDevice->UpdateSize*2; + mDevice->UpdateSize = RefTime2Samples(per_time, mDevice->Frequency); + mDevice->BufferSize = mDevice->UpdateSize*2; - mResampler = nullptr; - mResampleBuffer = nullptr; - mBufferFilled = 0; - if(mDevice->Frequency != mFormat.Format.nSamplesPerSec) - { - 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(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); - } + mResampler = nullptr; + mResampleBuffer.clear(); + mResampleBuffer.shrink_to_fit(); + mBufferFilled = 0; + if(mDevice->Frequency != mFormat.Format.nSamplesPerSec) + { + 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.resize(size_t{mDevice->UpdateSize} * channelCount * + mFormat.Format.wBitsPerSample / 8); - return S_OK; + TRACE("Created converter for %s/%s format, dst: %luhz (%u), src: %uhz (%u)\n", + DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType), + mFormat.Format.nSamplesPerSec, mOutUpdateSize, mDevice->Frequency, + mDevice->UpdateSize); + } + + return true; +} + +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->mDeviceName, "wasapi", "spatial-api", false)) + { + if(initSpatial()) + return S_OK; } -no_spatial: mDevice->Flags.reset(Virtualization); + mMonoUpsample = false; auto &audio = mAudio.emplace(); HRESULT hr{sDeviceHelper->activateAudioClient(mMMDev, __uuidof(IAudioClient), @@ -1921,7 +2023,7 @@ HRESULT WasapiPlayback::resetProxy() } mFormat = OutputType; -#if !defined(ALSOFT_UWP) +#if !ALSOFT_UWP const EndpointFormFactor formfactor{GetDeviceFormfactor(mMMDev.get())}; mDevice->Flags.set(DirectEar, (formfactor == Headphones || formfactor == Headset)); #else @@ -1955,32 +2057,41 @@ HRESULT WasapiPlayback::resetProxy() return hr; } + hr = audio.mClient->GetService(__uuidof(IAudioRenderClient), al::out_ptr(audio.mRender)); + if(FAILED(hr)) + { + ERR("Failed to get IAudioRenderClient: 0x%08lx\n", hr); + return hr; + } + /* Find the nearest multiple of the period size to the update size */ if(min_per < per_time) - min_per *= maxi64((per_time + min_per/2) / min_per, 1); + min_per *= std::max((per_time + min_per/2) / min_per, 1_i64); - mOrigBufferSize = buffer_len; - mOrigUpdateSize = minu(RefTime2Samples(min_per, mFormat.Format.nSamplesPerSec), buffer_len/2); + mOutBufferSize = buffer_len; + mOutUpdateSize = std::min(RefTime2Samples(min_per, mFormat.Format.nSamplesPerSec), + buffer_len/2u); mDevice->BufferSize = static_cast(uint64_t{buffer_len} * mDevice->Frequency / mFormat.Format.nSamplesPerSec); - mDevice->UpdateSize = minu(RefTime2Samples(min_per, mDevice->Frequency), - mDevice->BufferSize/2); + mDevice->UpdateSize = std::min(RefTime2Samples(min_per, mDevice->Frequency), + mDevice->BufferSize/2u); mResampler = nullptr; - mResampleBuffer = nullptr; + mResampleBuffer.clear(); + mResampleBuffer.shrink_to_fit(); mBufferFilled = 0; if(mDevice->Frequency != mFormat.Format.nSamplesPerSec) { mResampler = SampleConverter::Create(mDevice->FmtType, mDevice->FmtType, mFormat.Format.nChannels, mDevice->Frequency, mFormat.Format.nSamplesPerSec, Resampler::FastBSinc24); - mResampleBuffer = std::make_unique(size_t{mDevice->UpdateSize} * - mFormat.Format.nChannels * mFormat.Format.wBitsPerSample / 8); + mResampleBuffer.resize(size_t{mDevice->UpdateSize} * mFormat.Format.nChannels * + 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, + mFormat.Format.nSamplesPerSec, mOutUpdateSize, mDevice->Frequency, mDevice->UpdateSize); } @@ -2000,8 +2111,6 @@ HRESULT WasapiPlayback::startProxy() { ResetEvent(mNotifyEvent); - auto mstate_fallback = [](std::monostate) -> HRESULT - { return E_FAIL; }; auto start_plain = [&](PlainDevice &audio) -> HRESULT { HRESULT hr{audio.mClient->Start()}; @@ -2011,21 +2120,15 @@ HRESULT WasapiPlayback::startProxy() return 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; - } + try { + mKillNow.store(false, std::memory_order_release); + mThread = std::thread{std::mem_fn(&WasapiPlayback::mixerProc), this}; } - if(FAILED(hr)) + catch(...) { + ERR("Failed to start thread\n"); audio.mClient->Stop(); + hr = E_FAIL; + } return hr; }; auto start_spatial = [&](SpatialDevice &audio) -> HRESULT @@ -2054,7 +2157,7 @@ HRESULT WasapiPlayback::startProxy() return hr; }; - return std::visit(overloaded{mstate_fallback, start_plain, start_spatial}, mAudio); + return std::visit(overloaded{start_plain, start_spatial}, mAudio); } @@ -2069,28 +2172,22 @@ void WasapiPlayback::stopProxy() mKillNow.store(true, std::memory_order_release); mThread.join(); - auto mstate_fallback = [](std::monostate) -> void - { }; auto stop_plain = [](PlainDevice &audio) -> void - { - audio.mRender = nullptr; - audio.mClient->Stop(); - }; + { 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); + std::visit(overloaded{stop_plain, stop_spatial}, mAudio); } ClockLatency WasapiPlayback::getClockLatency() { - ClockLatency ret; - - std::lock_guard _{mMutex}; - ret.ClockTime = GetDeviceClockTime(mDevice); + std::lock_guard dlock{mMutex}; + ClockLatency ret{}; + ret.ClockTime = mDevice->getClockTime(); ret.Latency = seconds{mPadding.load(std::memory_order_relaxed)}; ret.Latency /= mFormat.Format.nSamplesPerSec; if(mResampler) @@ -2135,8 +2232,6 @@ struct WasapiCapture final : public BackendBase, WasapiProxy { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(WasapiCapture) }; WasapiCapture::~WasapiCapture() @@ -2153,21 +2248,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); + althrd_setname(GetRecordThreadName()); std::vector 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) @@ -2183,7 +2278,7 @@ FORCE_ALIGN int WasapiCapture::recordProc() { if(mChannelConv.is_active()) { - samples.resize(numsamples*2); + samples.resize(numsamples*2_uz); mChannelConv.convert(rdata, samples.data(), numsamples); rdata = reinterpret_cast(samples.data()); } @@ -2193,30 +2288,35 @@ FORCE_ALIGN int WasapiCapture::recordProc() size_t dstframes; if(mSampleConv) { + static constexpr auto lenlimit = size_t{std::numeric_limits::max()}; const void *srcdata{rdata}; uint srcframes{numsamples}; - dstframes = mSampleConv->convert(&srcdata, &srcframes, data.first.buf, - static_cast(minz(data.first.len, INT_MAX))); - if(srcframes > 0 && dstframes == data.first.len && data.second.len > 0) + dstframes = mSampleConv->convert(&srcdata, &srcframes, data[0].buf, + static_cast(std::min(data[0].len, lenlimit))); + if(srcframes > 0 && dstframes == data[0].len && data[1].len > 0) { /* If some source samples remain, all of the first dest * block was filled, and there's space in the second * dest block, do another run for the second block. */ - dstframes += mSampleConv->convert(&srcdata, &srcframes, data.second.buf, - static_cast(minz(data.second.len, INT_MAX))); + dstframes += mSampleConv->convert(&srcdata, &srcframes, data[1].buf, + static_cast(std::min(data[1].len, lenlimit))); } } else { const uint framesize{mDevice->frameSizeFromFmt()}; - size_t len1{minz(data.first.len, numsamples)}; - size_t len2{minz(data.second.len, numsamples-len1)}; + auto dst = al::span{rdata, size_t{numsamples}*framesize}; + size_t len1{std::min(data[0].len, size_t{numsamples})}; + size_t len2{std::min(data[1].len, numsamples-len1)}; - memcpy(data.first.buf, rdata, len1*framesize); + memcpy(data[0].buf, dst.data(), len1*framesize); if(len2 > 0) - memcpy(data.second.buf, rdata+len1*framesize, len2*framesize); + { + dst = dst.subspan(len1*framesize); + memcpy(data[1].buf, dst.data(), len2*framesize); + } dstframes = len1 + len2; } @@ -2238,7 +2338,6 @@ FORCE_ALIGN int WasapiCapture::recordProc() ERR("WaitForSingleObjectEx error: 0x%lx\n", res); } - CoUninitialize(); return 0; } @@ -2257,12 +2356,6 @@ void WasapiCapture::open(std::string_view name) "Failed to create notify events"}; } - if(name.length() >= DevNameHeadLen - && std::strncmp(name.data(), DevNameHead, DevNameHeadLen) == 0) - { - name = name.substr(DevNameHeadLen); - } - mOpenStatus = pushMessage(MsgType::OpenDevice, name).get(); if(FAILED(mOpenStatus)) throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx", @@ -2297,8 +2390,7 @@ HRESULT WasapiCapture::openProxy(std::string_view name) } if(iter == devlist.cend()) { - WARN("Failed to find device name matching \"%.*s\"\n", static_cast(name.length()), - name.data()); + WARN("Failed to find device name matching \"%.*s\"\n", al::sizei(name), name.data()); return E_FAIL; } devname = iter->name; @@ -2313,21 +2405,23 @@ HRESULT WasapiCapture::openProxy(std::string_view name) } mClient = nullptr; if(!devname.empty()) - mDevice->DeviceName = DevNameHead + std::move(devname); + mDeviceName = std::move(devname); else - mDevice->DeviceName = DevNameHead + GetDeviceNameAndGuid(mMMDev).first; + mDeviceName = GetDeviceNameAndGuid(mMMDev).mName; return S_OK; } void WasapiCapture::closeProxy() { + mCapture = nullptr; mClient = nullptr; mMMDev = nullptr; } HRESULT WasapiCapture::resetProxy() { + mCapture = nullptr; mClient = nullptr; HRESULT hr{sDeviceHelper->activateAudioClient(mMMDev, __uuidof(IAudioClient), @@ -2396,6 +2490,7 @@ HRESULT WasapiCapture::resetProxy() InputType.dwChannelMask = X7DOT1DOT4; break; + case DevFmtX7144: case DevFmtX3D71: case DevFmtAmbi3D: return E_FAIL; @@ -2423,6 +2518,7 @@ HRESULT WasapiCapture::resetProxy() InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; break; } + /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */ InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample; InputType.Format.nSamplesPerSec = mDevice->Frequency; @@ -2485,6 +2581,8 @@ HRESULT WasapiCapture::resetProxy() return (chancount == 8 && (chanmask == 0 || (chanmask&X71Mask) == X7DOT1)); case DevFmtX714: return (chancount == 12 && (chanmask == 0 || (chanmask&X714Mask) == X7DOT1DOT4)); + case DevFmtX7144: + return (chancount == 16 && chanmask == 0); case DevFmtAmbi3D: return (chanmask == 0 && chancount == device->channelsFromFmt()); } @@ -2583,6 +2681,13 @@ HRESULT WasapiCapture::resetProxy() return hr; } + hr = mClient->GetService(__uuidof(IAudioCaptureClient), al::out_ptr(mCapture)); + if(FAILED(hr)) + { + ERR("Failed to get IAudioCaptureClient: 0x%08lx\n", hr); + return hr; + } + UINT32 buffer_len{}; ReferenceTime min_per{}; hr = mClient->GetDevicePeriod(&reinterpret_cast(min_per), nullptr); @@ -2628,24 +2733,15 @@ HRESULT WasapiCapture::startProxy() return hr; } - hr = mClient->GetService(__uuidof(IAudioCaptureClient), al::out_ptr(mCapture)); - if(SUCCEEDED(hr)) - { - try { - mKillNow.store(false, std::memory_order_release); - mThread = std::thread{std::mem_fn(&WasapiCapture::recordProc), this}; - } - catch(...) { - mCapture = nullptr; - ERR("Failed to start thread\n"); - hr = E_FAIL; - } + try { + mKillNow.store(false, std::memory_order_release); + mThread = std::thread{std::mem_fn(&WasapiCapture::recordProc), this}; } - - if(FAILED(hr)) - { + catch(...) { + ERR("Failed to start thread\n"); mClient->Stop(); mClient->Reset(); + hr = E_FAIL; } return hr; @@ -2657,20 +2753,19 @@ void WasapiCapture::stop() void WasapiCapture::stopProxy() { - if(!mCapture || !mThread.joinable()) + if(!mThread.joinable()) return; mKillNow.store(true, std::memory_order_release); mThread.join(); - mCapture = nullptr; mClient->Stop(); mClient->Reset(); } void WasapiCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } uint WasapiCapture::availableSamples() { return static_cast(mRing->readSpace()); } @@ -2698,9 +2793,9 @@ bool WasapiBackendFactory::init() bool WasapiBackendFactory::querySupport(BackendType type) { return type == BackendType::Playback || type == BackendType::Capture; } -std::string WasapiBackendFactory::probe(BackendType type) +auto WasapiBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; + std::vector outnames; auto devlock = DeviceListLock{gDeviceList}; switch(type) @@ -2712,15 +2807,11 @@ std::string WasapiBackendFactory::probe(BackendType type) { 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); + outnames.emplace_back(entry.name); continue; } /* Default device goes first. */ - std::string name{DevNameHead + entry.name}; - outnames.insert(0, name.c_str(), name.length()+1); + outnames.emplace(outnames.cbegin(), entry.name); } } break; @@ -2732,11 +2823,10 @@ std::string WasapiBackendFactory::probe(BackendType type) { if(entry.devid != defaultId) { - outnames.append(DevNameHead).append(entry.name.c_str(), entry.name.length()+1); + outnames.emplace_back(entry.name); continue; } - std::string name{DevNameHead + entry.name}; - outnames.insert(0, name.c_str(), name.length()+1); + outnames.emplace(outnames.cbegin(), entry.name); } } break; @@ -2759,3 +2849,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 !ALSOFT_UWP + return alc::EventSupport::FullSupport; +#endif + + case alc::EventType::Count: + break; + } + return alc::EventSupport::NoSupport; +} diff --git a/3rdparty/openal/alc/backends/wasapi.h b/3rdparty/openal/alc/backends/wasapi.h index bb2671ee84a8..f3cb85413658 100644 --- a/3rdparty/openal/alc/backends/wasapi.h +++ b/3rdparty/openal/alc/backends/wasapi.h @@ -5,15 +5,17 @@ struct WasapiBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto queryEventSupport(alc::EventType eventType, BackendType type) -> alc::EventSupport final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - static BackendFactory &getFactory(); + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; + + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_WASAPI_H */ diff --git a/3rdparty/openal/alc/backends/wave.cpp b/3rdparty/openal/alc/backends/wave.cpp index 794d5cb87880..bf8e32bc380a 100644 --- a/3rdparty/openal/alc/backends/wave.cpp +++ b/3rdparty/openal/alc/backends/wave.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,7 @@ #include "alc/alconfig.h" #include "almalloc.h" #include "alnumeric.h" +#include "alstring.h" #include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" @@ -48,6 +50,7 @@ namespace { +using namespace std::string_view_literals; using std::chrono::seconds; using std::chrono::milliseconds; using std::chrono::nanoseconds; @@ -55,38 +58,43 @@ using std::chrono::nanoseconds; using ubyte = unsigned char; using ushort = unsigned short; -constexpr char waveDevice[] = "Wave File Writer"; +struct FileDeleter { + void operator()(gsl::owner f) { fclose(f); } +}; +using FilePtr = std::unique_ptr; + +[[nodiscard]] constexpr auto GetDeviceName() noexcept { return "Wave File Writer"sv; } -constexpr ubyte SUBTYPE_PCM[]{ +constexpr std::array SUBTYPE_PCM{{ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 -}; -constexpr ubyte SUBTYPE_FLOAT[]{ +}}; +constexpr std::array SUBTYPE_FLOAT{{ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 -}; +}}; -constexpr ubyte SUBTYPE_BFORMAT_PCM[]{ +constexpr std::array SUBTYPE_BFORMAT_PCM{{ 0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1, 0xca, 0x00, 0x00, 0x00 -}; +}}; -constexpr ubyte SUBTYPE_BFORMAT_FLOAT[]{ +constexpr std::array SUBTYPE_BFORMAT_FLOAT{{ 0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1, 0xca, 0x00, 0x00, 0x00 -}; +}}; void fwrite16le(ushort val, FILE *f) { - ubyte data[2]{ static_cast(val&0xff), static_cast((val>>8)&0xff) }; - fwrite(data, 1, 2, f); + std::array data{static_cast(val&0xff), static_cast((val>>8)&0xff)}; + fwrite(data.data(), 1, data.size(), f); } void fwrite32le(uint val, FILE *f) { - ubyte data[4]{ static_cast(val&0xff), static_cast((val>>8)&0xff), - static_cast((val>>16)&0xff), static_cast((val>>24)&0xff) }; - fwrite(data, 1, 4, f); + std::array data{static_cast(val&0xff), static_cast((val>>8)&0xff), + static_cast((val>>16)&0xff), static_cast((val>>24)&0xff)}; + fwrite(data.data(), 1, data.size(), f); } @@ -101,29 +109,22 @@ struct WaveBackend final : public BackendBase { void start() override; void stop() override; - FILE *mFile{nullptr}; + FilePtr mFile{nullptr}; long mDataStart{-1}; std::vector mBuffer; std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(WaveBackend) }; -WaveBackend::~WaveBackend() -{ - if(mFile) - fclose(mFile); - mFile = nullptr; -} +WaveBackend::~WaveBackend() = default; int WaveBackend::mixerProc() { const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2}; - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); const size_t frameStep{mDevice->channelsFromFmt()}; const size_t frameSize{mDevice->frameSizeFromFmt()}; @@ -169,8 +170,8 @@ int WaveBackend::mixerProc() } } - const size_t fs{fwrite(mBuffer.data(), frameSize, mDevice->UpdateSize, mFile)}; - if(fs < mDevice->UpdateSize || ferror(mFile)) + const size_t fs{fwrite(mBuffer.data(), frameSize, mDevice->UpdateSize, mFile.get())}; + if(fs < mDevice->UpdateSize || ferror(mFile.get())) { ERR("Error writing to file\n"); mDevice->handleDisconnect("Failed to write playback samples"); @@ -196,15 +197,15 @@ int WaveBackend::mixerProc() void WaveBackend::open(std::string_view name) { - auto fname = ConfigValueStr(nullptr, "wave", "file"); + auto fname = ConfigValueStr({}, "wave", "file"); if(!fname) throw al::backend_exception{al::backend_error::NoDevice, "No wave output filename"}; if(name.empty()) - name = waveDevice; - else if(name != waveDevice) + name = GetDeviceName(); + else if(name != GetDeviceName()) throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + al::sizei(name), name.data()}; /* There's only one "device", so if it's already open, we're done. */ if(mFile) return; @@ -212,28 +213,27 @@ void WaveBackend::open(std::string_view name) #ifdef _WIN32 { std::wstring wname{utf8_to_wstr(fname.value())}; - mFile = _wfopen(wname.c_str(), L"wb"); + mFile = FilePtr{_wfopen(wname.c_str(), L"wb")}; } #else - mFile = fopen(fname->c_str(), "wb"); + mFile = FilePtr{fopen(fname->c_str(), "wb")}; #endif if(!mFile) throw al::backend_exception{al::backend_error::DeviceError, "Could not open file '%s': %s", - fname->c_str(), strerror(errno)}; + fname->c_str(), std::generic_category().message(errno).c_str()}; - mDevice->DeviceName = name; + mDeviceName = name; } bool WaveBackend::reset() { uint channels{0}, bytes{0}, chanmask{0}; bool isbformat{false}; - size_t val; - fseek(mFile, 0, SEEK_SET); - clearerr(mFile); + fseek(mFile.get(), 0, SEEK_SET); + clearerr(mFile.get()); - if(GetConfigValueBool(nullptr, "wave", "bformat", false)) + if(GetConfigValueBool({}, "wave", "bformat", false)) { mDevice->FmtChans = DevFmtAmbi3D; mDevice->mAmbiOrder = 1; @@ -264,6 +264,9 @@ bool WaveBackend::reset() case DevFmtX51: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400; break; case DevFmtX61: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400; break; case DevFmtX71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break; + case DevFmtX7144: + mDevice->FmtChans = DevFmtX714; + [[fallthrough]]; case DevFmtX714: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400 | 0x1000 | 0x4000 | 0x8000 | 0x20000; @@ -272,7 +275,7 @@ bool WaveBackend::reset() case DevFmtX3D71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break; case DevFmtAmbi3D: /* .amb output requires FuMa */ - mDevice->mAmbiOrder = minu(mDevice->mAmbiOrder, 3); + mDevice->mAmbiOrder = std::min(mDevice->mAmbiOrder, 3u); mDevice->mAmbiLayout = DevAmbiLayout::FuMa; mDevice->mAmbiScale = DevAmbiScaling::FuMa; isbformat = true; @@ -282,49 +285,53 @@ bool WaveBackend::reset() bytes = mDevice->bytesFromFmt(); channels = mDevice->channelsFromFmt(); - rewind(mFile); + rewind(mFile.get()); + if(auto errcode = errno; errno != 0 && errno != ENOENT) + { + ERR("Failed to reset file offset: %s (%d)\n", + std::generic_category().message(errcode).c_str(), errcode); + } - fputs("RIFF", mFile); - fwrite32le(0xFFFFFFFF, mFile); // 'RIFF' header len; filled in at close + fputs("RIFF", mFile.get()); + fwrite32le(0xFFFFFFFF, mFile.get()); // 'RIFF' header len; filled in at stop - fputs("WAVE", mFile); + fputs("WAVE", mFile.get()); - fputs("fmt ", mFile); - fwrite32le(40, mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE + fputs("fmt ", mFile.get()); + fwrite32le(40, mFile.get()); // 'fmt ' header len; 40 bytes for EXTENSIBLE // 16-bit val, format type id (extensible: 0xFFFE) - fwrite16le(0xFFFE, mFile); + fwrite16le(0xFFFE, mFile.get()); // 16-bit val, channel count - fwrite16le(static_cast(channels), mFile); + fwrite16le(static_cast(channels), mFile.get()); // 32-bit val, frequency - fwrite32le(mDevice->Frequency, mFile); + fwrite32le(mDevice->Frequency, mFile.get()); // 32-bit val, bytes per second - fwrite32le(mDevice->Frequency * channels * bytes, mFile); + fwrite32le(mDevice->Frequency * channels * bytes, mFile.get()); // 16-bit val, frame size - fwrite16le(static_cast(channels * bytes), mFile); + fwrite16le(static_cast(channels * bytes), mFile.get()); // 16-bit val, bits per sample - fwrite16le(static_cast(bytes * 8), mFile); + fwrite16le(static_cast(bytes * 8), mFile.get()); // 16-bit val, extra byte count - fwrite16le(22, mFile); + fwrite16le(22, mFile.get()); // 16-bit val, valid bits per sample - fwrite16le(static_cast(bytes * 8), mFile); + fwrite16le(static_cast(bytes * 8), mFile.get()); // 32-bit val, channel mask - fwrite32le(chanmask, mFile); + fwrite32le(chanmask, mFile.get()); // 16 byte GUID, sub-type format - val = fwrite((mDevice->FmtType == DevFmtFloat) ? - (isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) : - (isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, mFile); - (void)val; + std::ignore = fwrite((mDevice->FmtType == DevFmtFloat) ? + (isbformat ? SUBTYPE_BFORMAT_FLOAT.data() : SUBTYPE_FLOAT.data()) : + (isbformat ? SUBTYPE_BFORMAT_PCM.data() : SUBTYPE_PCM.data()), 1, 16, mFile.get()); - fputs("data", mFile); - fwrite32le(0xFFFFFFFF, mFile); // 'data' header len; filled in at close + fputs("data", mFile.get()); + fwrite32le(0xFFFFFFFF, mFile.get()); // 'data' header len; filled in at stop - if(ferror(mFile)) + if(ferror(mFile.get())) { - ERR("Error writing header: %s\n", strerror(errno)); + ERR("Error writing header: %s\n", std::generic_category().message(errno).c_str()); return false; } - mDataStart = ftell(mFile); + mDataStart = ftell(mFile.get()); setDefaultWFXChannelOrder(); @@ -336,7 +343,7 @@ bool WaveBackend::reset() void WaveBackend::start() { - if(mDataStart > 0 && fseek(mFile, 0, SEEK_END) != 0) + if(mDataStart > 0 && fseek(mFile.get(), 0, SEEK_END) != 0) WARN("Failed to seek on output file\n"); try { mKillNow.store(false, std::memory_order_release); @@ -356,14 +363,14 @@ void WaveBackend::stop() if(mDataStart > 0) { - long size{ftell(mFile)}; + long size{ftell(mFile.get())}; if(size > 0) { long dataLen{size - mDataStart}; - if(fseek(mFile, 4, SEEK_SET) == 0) - fwrite32le(static_cast(size-8), mFile); // 'WAVE' header len - if(fseek(mFile, mDataStart-4, SEEK_SET) == 0) - fwrite32le(static_cast(dataLen), mFile); // 'data' header len + if(fseek(mFile.get(), 4, SEEK_SET) == 0) + fwrite32le(static_cast(size-8), mFile.get()); // 'WAVE' header len + if(fseek(mFile.get(), mDataStart-4, SEEK_SET) == 0) + fwrite32le(static_cast(dataLen), mFile.get()); // 'data' header len } } } @@ -377,19 +384,16 @@ bool WaveBackendFactory::init() bool WaveBackendFactory::querySupport(BackendType type) { return type == BackendType::Playback; } -std::string WaveBackendFactory::probe(BackendType type) +auto WaveBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; switch(type) { case BackendType::Playback: - /* Includes null char. */ - outnames.append(waveDevice, sizeof(waveDevice)); - break; + return std::vector{std::string{GetDeviceName()}}; case BackendType::Capture: break; } - return outnames; + return {}; } BackendPtr WaveBackendFactory::createBackend(DeviceBase *device, BackendType type) diff --git a/3rdparty/openal/alc/backends/wave.h b/3rdparty/openal/alc/backends/wave.h index e768d33617ca..85f4c76ff788 100644 --- a/3rdparty/openal/alc/backends/wave.h +++ b/3rdparty/openal/alc/backends/wave.h @@ -5,15 +5,15 @@ struct WaveBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_WAVE_H */ diff --git a/3rdparty/openal/alc/backends/winmm.cpp b/3rdparty/openal/alc/backends/winmm.cpp index f0fb0a1c2e0d..8545472c8da6 100644 --- a/3rdparty/openal/alc/backends/winmm.cpp +++ b/3rdparty/openal/alc/backends/winmm.cpp @@ -22,8 +22,8 @@ #include "winmm.h" -#include -#include +#include +#include #include #include @@ -38,14 +38,15 @@ #include #include -#include "alnumeric.h" #include "alsem.h" +#include "alstring.h" #include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" #include "core/logging.h" #include "ringbuffer.h" #include "strutils.h" +#include "vector.h" #ifndef WAVE_FORMAT_IEEE_FLOAT #define WAVE_FORMAT_IEEE_FLOAT 0x0003 @@ -53,16 +54,13 @@ namespace { -#define DEVNAME_HEAD "OpenAL Soft on " - - std::vector PlaybackDevices; std::vector CaptureDevices; bool checkName(const std::vector &list, const std::string &name) { return std::find(list.cbegin(), list.cend(), name) != list.cend(); } -void ProbePlaybackDevices(void) +void ProbePlaybackDevices() { PlaybackDevices.clear(); @@ -75,7 +73,7 @@ void ProbePlaybackDevices(void) WAVEOUTCAPSW WaveCaps{}; if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR) { - const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)}; + const auto basename = wstr_to_utf8(std::data(WaveCaps.szPname)); int count{1}; std::string newname{basename}; @@ -93,7 +91,7 @@ void ProbePlaybackDevices(void) } } -void ProbeCaptureDevices(void) +void ProbeCaptureDevices() { CaptureDevices.clear(); @@ -106,7 +104,7 @@ void ProbeCaptureDevices(void) WAVEINCAPSW WaveCaps{}; if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR) { - const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)}; + const auto basename = wstr_to_utf8(std::data(WaveCaps.szPname)); int count{1}; std::string newname{basename}; @@ -144,6 +142,7 @@ struct WinMMPlayback final : public BackendBase { al::semaphore mSem; uint mIdx{0u}; std::array mWaveBuffer{}; + al::vector mBuffer; HWAVEOUT mOutHdl{nullptr}; @@ -151,8 +150,6 @@ struct WinMMPlayback final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(WinMMPlayback) }; WinMMPlayback::~WinMMPlayback() @@ -160,9 +157,6 @@ WinMMPlayback::~WinMMPlayback() if(mOutHdl) waveOutClose(mOutHdl); mOutHdl = nullptr; - - al_free(mWaveBuffer[0].lpData); - std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{}); } /* WinMMPlayback::waveOutProc @@ -180,7 +174,7 @@ void CALLBACK WinMMPlayback::waveOutProc(HWAVEOUT, UINT msg, DWORD_PTR, DWORD_PT FORCE_ALIGN int WinMMPlayback::mixerProc() { SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); while(!mKillNow.load(std::memory_order_acquire) && mDevice->Connected.load(std::memory_order_acquire)) @@ -219,51 +213,47 @@ void WinMMPlayback::open(std::string_view name) PlaybackDevices.cbegin(); if(iter == PlaybackDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + al::sizei(name), name.data()}; auto DeviceID = static_cast(std::distance(PlaybackDevices.cbegin(), iter)); DevFmtType fmttype{mDevice->FmtType}; -retry_open: WAVEFORMATEX format{}; - if(fmttype == DevFmtFloat) - { - format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; - format.wBitsPerSample = 32; - } - else - { - format.wFormatTag = WAVE_FORMAT_PCM; - if(fmttype == DevFmtUByte || fmttype == DevFmtByte) - format.wBitsPerSample = 8; - else - format.wBitsPerSample = 16; - } - format.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2); - format.nBlockAlign = static_cast(format.wBitsPerSample * format.nChannels / 8); - format.nSamplesPerSec = mDevice->Frequency; - format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; - format.cbSize = 0; - - HWAVEOUT outHandle{}; - MMRESULT res{waveOutOpen(&outHandle, DeviceID, &format, - reinterpret_cast(&WinMMPlayback::waveOutProcC), - reinterpret_cast(this), CALLBACK_FUNCTION)}; - if(res != MMSYSERR_NOERROR) - { + do { + format = WAVEFORMATEX{}; if(fmttype == DevFmtFloat) { - fmttype = DevFmtShort; - goto retry_open; + format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; + format.wBitsPerSample = 32; } - throw al::backend_exception{al::backend_error::DeviceError, "waveOutOpen failed: %u", res}; - } + else + { + format.wFormatTag = WAVE_FORMAT_PCM; + if(fmttype == DevFmtUByte || fmttype == DevFmtByte) + format.wBitsPerSample = 8; + else + format.wBitsPerSample = 16; + } + format.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2); + format.nBlockAlign = static_cast(format.wBitsPerSample * format.nChannels / 8); + format.nSamplesPerSec = mDevice->Frequency; + format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; + format.cbSize = 0; + + MMRESULT res{waveOutOpen(&mOutHdl, DeviceID, &format, + reinterpret_cast(&WinMMPlayback::waveOutProcC), + reinterpret_cast(this), CALLBACK_FUNCTION)}; + if(res == MMSYSERR_NOERROR) break; + + if(fmttype != DevFmtFloat) + throw al::backend_exception{al::backend_error::DeviceError, "waveOutOpen failed: %u", + res}; + + fmttype = DevFmtShort; + } while(true); - if(mOutHdl) - waveOutClose(mOutHdl); - mOutHdl = outHandle; mFormat = format; - mDevice->DeviceName = PlaybackDevices[DeviceID]; + mDeviceName = PlaybackDevices[DeviceID]; } bool WinMMPlayback::reset() @@ -313,11 +303,11 @@ bool WinMMPlayback::reset() } setDefaultWFXChannelOrder(); - uint BufferSize{mDevice->UpdateSize * mFormat.nChannels * mDevice->bytesFromFmt()}; + const uint BufferSize{mDevice->UpdateSize * mFormat.nChannels * mDevice->bytesFromFmt()}; - al_free(mWaveBuffer[0].lpData); + decltype(mBuffer)(BufferSize*mWaveBuffer.size()).swap(mBuffer); mWaveBuffer[0] = WAVEHDR{}; - mWaveBuffer[0].lpData = static_cast(al_calloc(16, BufferSize * mWaveBuffer.size())); + mWaveBuffer[0].lpData = mBuffer.data(); mWaveBuffer[0].dwBufferLength = BufferSize; for(size_t i{1};i < mWaveBuffer.size();i++) { @@ -380,6 +370,7 @@ struct WinMMCapture final : public BackendBase { al::semaphore mSem; uint mIdx{0}; std::array mWaveBuffer{}; + al::vector mBuffer; HWAVEIN mInHdl{nullptr}; @@ -389,8 +380,6 @@ struct WinMMCapture final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(WinMMCapture) }; WinMMCapture::~WinMMCapture() @@ -399,9 +388,6 @@ WinMMCapture::~WinMMCapture() if(mInHdl) waveInClose(mInHdl); mInHdl = nullptr; - - al_free(mWaveBuffer[0].lpData); - std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{}); } /* WinMMCapture::waveInProc @@ -418,7 +404,7 @@ void CALLBACK WinMMCapture::waveInProc(HWAVEIN, UINT msg, DWORD_PTR, DWORD_PTR) int WinMMCapture::captureProc() { - althrd_setname(RECORD_THREAD_NAME); + althrd_setname(GetRecordThreadName()); while(!mKillNow.load(std::memory_order_acquire) && mDevice->Connected.load(std::memory_order_acquire)) @@ -435,7 +421,8 @@ int WinMMCapture::captureProc() WAVEHDR &waveHdr = mWaveBuffer[widx]; widx = (widx+1) % mWaveBuffer.size(); - mRing->write(waveHdr.lpData, waveHdr.dwBytesRecorded / mFormat.nBlockAlign); + std::ignore = mRing->write(waveHdr.lpData, + waveHdr.dwBytesRecorded / mFormat.nBlockAlign); mReadable.fetch_sub(1, std::memory_order_acq_rel); waveInAddBuffer(mInHdl, &waveHdr, sizeof(WAVEHDR)); } while(--todo); @@ -457,7 +444,7 @@ void WinMMCapture::open(std::string_view name) CaptureDevices.cbegin(); if(iter == CaptureDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + al::sizei(name), name.data()}; auto DeviceID = static_cast(std::distance(CaptureDevices.cbegin(), iter)); switch(mDevice->FmtChans) @@ -471,6 +458,7 @@ void WinMMCapture::open(std::string_view name) case DevFmtX61: case DevFmtX71: case DevFmtX714: + case DevFmtX7144: case DevFmtX3D71: case DevFmtAmbi3D: throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported", @@ -514,14 +502,14 @@ void WinMMCapture::open(std::string_view name) // Allocate circular memory buffer for the captured audio // Make sure circular buffer is at least 100ms in size - uint CapturedDataSize{mDevice->BufferSize}; - CapturedDataSize = static_cast(maxz(CapturedDataSize, BufferSize*mWaveBuffer.size())); + const auto CapturedDataSize = std::max(mDevice->BufferSize, + BufferSize*mWaveBuffer.size()); mRing = RingBuffer::Create(CapturedDataSize, mFormat.nBlockAlign, false); - al_free(mWaveBuffer[0].lpData); + decltype(mBuffer)(BufferSize*mWaveBuffer.size()).swap(mBuffer); mWaveBuffer[0] = WAVEHDR{}; - mWaveBuffer[0].lpData = static_cast(al_calloc(16, BufferSize * mWaveBuffer.size())); + mWaveBuffer[0].lpData = mBuffer.data(); mWaveBuffer[0].dwBufferLength = BufferSize; for(size_t i{1};i < mWaveBuffer.size();++i) { @@ -530,7 +518,7 @@ void WinMMCapture::open(std::string_view name) mWaveBuffer[i].dwBufferLength = mWaveBuffer[i-1].dwBufferLength; } - mDevice->DeviceName = CaptureDevices[DeviceID]; + mDeviceName = CaptureDevices[DeviceID]; } void WinMMCapture::start() @@ -573,7 +561,7 @@ void WinMMCapture::stop() } void WinMMCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } uint WinMMCapture::availableSamples() { return static_cast(mRing->readSpace()); } @@ -587,26 +575,23 @@ bool WinMMBackendFactory::init() bool WinMMBackendFactory::querySupport(BackendType type) { return type == BackendType::Playback || type == BackendType::Capture; } -std::string WinMMBackendFactory::probe(BackendType type) +auto WinMMBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; + std::vector outnames; auto add_device = [&outnames](const std::string &dname) -> void - { - /* +1 to also append the null char (to ensure a null-separated list and - * double-null terminated list). - */ - if(!dname.empty()) - outnames.append(dname.c_str(), dname.length()+1); - }; + { if(!dname.empty()) outnames.emplace_back(dname); }; + switch(type) { case BackendType::Playback: ProbePlaybackDevices(); + outnames.reserve(PlaybackDevices.size()); std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device); break; case BackendType::Capture: ProbeCaptureDevices(); + outnames.reserve(CaptureDevices.size()); std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device); break; } diff --git a/3rdparty/openal/alc/backends/winmm.h b/3rdparty/openal/alc/backends/winmm.h index 45a706aa341e..51ce432ca346 100644 --- a/3rdparty/openal/alc/backends/winmm.h +++ b/3rdparty/openal/alc/backends/winmm.h @@ -5,15 +5,15 @@ struct WinMMBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_WINMM_H */ diff --git a/3rdparty/openal/alc/context.cpp b/3rdparty/openal/alc/context.cpp index 8c930056fdaf..16da4e2a53e0 100644 --- a/3rdparty/openal/alc/context.cpp +++ b/3rdparty/openal/alc/context.cpp @@ -5,13 +5,13 @@ #include #include -#include +#include #include -#include +#include #include -#include -#include +#include #include +#include #include #include "AL/efx.h" @@ -24,75 +24,78 @@ #include "al/listener.h" #include "albit.h" #include "alc/alu.h" +#include "alc/backends/base.h" +#include "alnumeric.h" #include "alspan.h" +#include "atomic.h" #include "core/async_event.h" +#include "core/devformat.h" #include "core/device.h" #include "core/effectslot.h" #include "core/logging.h" -#include "core/voice.h" #include "core/voice_change.h" #include "device.h" +#include "flexarray.h" #include "ringbuffer.h" #include "vecmat.h" -#ifdef ALSOFT_EAX -#include -#include "alstring.h" +#if ALSOFT_EAX +#include "al/eax/call.h" #include "al/eax/globals.h" #endif // ALSOFT_EAX namespace { -using namespace std::placeholders; - +using namespace std::string_view_literals; using voidp = void*; /* Default context extensions */ std::vector getContextExtensions() noexcept { return std::vector{ - "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_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", + "AL_EXT_ALAW"sv, + "AL_EXT_BFORMAT"sv, + "AL_EXT_debug"sv, + "AL_EXT_direct_context"sv, + "AL_EXT_DOUBLE"sv, + "AL_EXT_EXPONENT_DISTANCE"sv, + "AL_EXT_FLOAT32"sv, + "AL_EXT_IMA4"sv, + "AL_EXT_LINEAR_DISTANCE"sv, + "AL_EXT_MCFORMATS"sv, + "AL_EXT_MULAW"sv, + "AL_EXT_MULAW_BFORMAT"sv, + "AL_EXT_MULAW_MCFORMATS"sv, + "AL_EXT_OFFSET"sv, + "AL_EXT_source_distance_model"sv, + "AL_EXT_SOURCE_RADIUS"sv, + "AL_EXT_STATIC_BUFFER"sv, + "AL_EXT_STEREO_ANGLES"sv, + "AL_LOKI_quadriphonic"sv, + "AL_SOFT_bformat_ex"sv, + "AL_SOFTX_bformat_hoa"sv, + "AL_SOFT_block_alignment"sv, + "AL_SOFT_buffer_length_query"sv, + "AL_SOFT_callback_buffer"sv, + "AL_SOFTX_convolution_effect"sv, + "AL_SOFT_deferred_updates"sv, + "AL_SOFT_direct_channels"sv, + "AL_SOFT_direct_channels_remix"sv, + "AL_SOFT_effect_target"sv, + "AL_SOFT_events"sv, + "AL_SOFT_gain_clamp_ex"sv, + "AL_SOFTX_hold_on_disconnect"sv, + "AL_SOFT_loop_points"sv, + "AL_SOFTX_map_buffer"sv, + "AL_SOFT_MSADPCM"sv, + "AL_SOFT_source_latency"sv, + "AL_SOFT_source_length"sv, + "AL_SOFTX_source_panning"sv, + "AL_SOFT_source_resampler"sv, + "AL_SOFT_source_spatialize"sv, + "AL_SOFT_source_start_delay"sv, + "AL_SOFT_UHJ"sv, + "AL_SOFT_UHJ_ex"sv, }; } @@ -116,11 +119,15 @@ thread_local ALCcontext::ThreadCtx ALCcontext::sThreadContext; ALeffect ALCcontext::sDefaultEffect; -ALCcontext::ALCcontext(al::intrusive_ptr device, ContextFlagBitset flags) +ALCcontext::ALCcontext(al::intrusive_ptr 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); + + /* Low-severity debug messages are disabled by default. */ + alDebugMessageControlDirectEXT(this, AL_DONT_CARE_EXT, AL_DONT_CARE_EXT, + AL_DEBUG_SEVERITY_LOW_EXT, 0, nullptr, AL_FALSE); } ALCcontext::~ALCcontext() @@ -135,7 +142,7 @@ ALCcontext::~ALCcontext() mSourceList.clear(); mNumSources = 0; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX eaxUninitialize(); #endif // ALSOFT_EAX @@ -157,16 +164,17 @@ void ALCcontext::init() aluInitEffectPanning(mDefaultSlot->mSlot, this); } - EffectSlotArray *auxslots; + std::unique_ptr auxslots; if(!mDefaultSlot) auxslots = EffectSlot::CreatePtrArray(0); else { - auxslots = EffectSlot::CreatePtrArray(1); + auxslots = EffectSlot::CreatePtrArray(2); (*auxslots)[0] = mDefaultSlot->mSlot; + (*auxslots)[1] = mDefaultSlot->mSlot; mDefaultSlot->mState = SlotState::Playing; } - mActiveAuxSlots.store(auxslots, std::memory_order_relaxed); + mActiveAuxSlots.store(std::move(auxslots), std::memory_order_relaxed); allocVoiceChanges(); { @@ -180,15 +188,15 @@ void ALCcontext::init() if(sBufferSubDataCompat) { - auto iter = std::find(mExtensions.begin(), mExtensions.end(), "AL_EXT_SOURCE_RADIUS"); + auto iter = std::find(mExtensions.begin(), mExtensions.end(), "AL_EXT_SOURCE_RADIUS"sv); 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"); + mExtensions.emplace_back("AL_SOFT_buffer_sub_data"sv); } -#ifdef ALSOFT_EAX +#if ALSOFT_EAX eax_initialize_extensions(); #endif // ALSOFT_EAX @@ -211,19 +219,31 @@ void ALCcontext::init() mExtensionsString = std::move(extensions); } +#if ALSOFT_EAX + eax_set_defaults(); +#endif + mParams.Position = alu::Vector{0.0f, 0.0f, 0.0f, 1.0f}; mParams.Matrix = alu::Matrix::Identity(); mParams.Velocity = alu::Vector{}; mParams.Gain = mListener.Gain; - mParams.MetersPerUnit = mListener.mMetersPerUnit; + mParams.MetersPerUnit = mListener.mMetersPerUnit +#if ALSOFT_EAX + * eaxGetDistanceFactor() +#endif + ; mParams.AirAbsorptionGainHF = mAirAbsorptionGainHF; mParams.DopplerFactor = mDopplerFactor; - mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity; + mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity +#if ALSOFT_EAX + / eaxGetDistanceFactor() +#endif + ; mParams.SourceDistanceModel = mSourceDistanceModel; mParams.mDistanceModel = mDistanceModel; - mAsyncEvents = RingBuffer::Create(511, sizeof(AsyncEvent), false); + mAsyncEvents = RingBuffer::Create(1024, sizeof(AsyncEvent), false); StartEventThrd(this); @@ -231,63 +251,58 @@ void ALCcontext::init() mActiveVoiceCount.store(64, std::memory_order_relaxed); } -bool ALCcontext::deinit() +void ALCcontext::deinit() { if(sLocalContext == this) { WARN("%p released while current on thread\n", voidp{this}); + auto _ = ContextRef{sLocalContext}; sThreadContext.set(nullptr); - dec_ref(); } - ALCcontext *origctx{this}; - if(sGlobalContext.compare_exchange_strong(origctx, nullptr)) + if(ALCcontext *origctx{this}; sGlobalContext.compare_exchange_strong(origctx, nullptr)) { + auto _ = ContextRef{origctx}; while(sGlobalContextLock.load()) { /* Wait to make sure another thread didn't get the context and is * trying to increment its refcount. */ } - dec_ref(); } - bool ret{}; + bool stopPlayback{}; /* First make sure this context exists in the device's list. */ - auto *oldarray = mDevice->mContexts.load(std::memory_order_acquire); - if(auto toremove = static_cast(std::count(oldarray->begin(), oldarray->end(), this))) + auto oldarray = al::span{*mDevice->mContexts.load(std::memory_order_acquire)}; + if(auto toremove = static_cast(std::count(oldarray.begin(), oldarray.end(), this))) { using ContextArray = al::FlexArray; - auto alloc_ctx_array = [](const size_t count) -> ContextArray* - { - if(count == 0) return &DeviceBase::sEmptyContextArray; - return ContextArray::Create(count).release(); - }; - auto *newarray = alloc_ctx_array(oldarray->size() - toremove); + const auto newsize = size_t{oldarray.size() - toremove}; + auto newarray = ContextArray::Create(newsize); /* Copy the current/old context handles to the new array, excluding the * given context. */ - std::copy_if(oldarray->begin(), oldarray->end(), newarray->begin(), + std::copy_if(oldarray.begin(), oldarray.end(), newarray->begin(), [this](ContextBase *ctx) { return ctx != this; }); /* Store the new context array in the device. Wait for any current mix * to finish before deleting the old array. */ - mDevice->mContexts.store(newarray); - if(oldarray != &DeviceBase::sEmptyContextArray) - { - mDevice->waitForMix(); - delete oldarray; - } + auto prevarray = mDevice->mContexts.exchange(std::move(newarray)); + std::ignore = mDevice->waitForMix(); - ret = !newarray->empty(); + stopPlayback = (newsize == 0); } else - ret = !oldarray->empty(); + stopPlayback = oldarray.empty(); StopEventThrd(this); - return ret; + if(stopPlayback && mALDevice->mDeviceState == DeviceState::Playing) + { + mALDevice->Backend->stop(); + mALDevice->mDeviceState = DeviceState::Configured; + } } void ALCcontext::applyAllUpdates() @@ -300,7 +315,7 @@ void ALCcontext::applyAllUpdates() /* busy-wait */ } -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(mEaxNeedsCommit) eaxCommit(); #endif @@ -317,7 +332,7 @@ void ALCcontext::applyAllUpdates() } -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { template @@ -328,10 +343,10 @@ void ForEachSource(ALCcontext *context, F func) uint64_t usemask{~sublist.FreeMask}; while(usemask) { - const int idx{al::countr_zero(usemask)}; + const auto idx = static_cast(al::countr_zero(usemask)); usemask &= ~(1_u64 << idx); - func(sublist.Sources[idx]); + func((*sublist.Sources)[idx]); } } } @@ -469,14 +484,14 @@ void ALCcontext::eax_initialize_extensions() if(!eax_g_is_enabled) return; - mExtensions.emplace(mExtensions.begin(), eax_x_ram_ext_name); + mExtensions.emplace(mExtensions.begin(), "EAX-RAM"sv); if(eaxIsCapable()) { - 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); + mExtensions.emplace(mExtensions.begin(), "EAX5.0"sv); + mExtensions.emplace(mExtensions.begin(), "EAX4.0"sv); + mExtensions.emplace(mExtensions.begin(), "EAX3.0"sv); + mExtensions.emplace(mExtensions.begin(), "EAX2.0"sv); + mExtensions.emplace(mExtensions.begin(), "EAX"sv); } } @@ -549,10 +564,11 @@ unsigned long ALCcontext::eax_detect_speaker_configuration() const case DevFmtX51: return SPEAKERS_5; case DevFmtX61: return SPEAKERS_6; case DevFmtX71: return SPEAKERS_7; - /* 7.1.4 is compatible with 7.1. This could instead be HEADPHONES to + /* 7.1.4(.4) is compatible with 7.1. This could instead be HEADPHONES to * suggest with-height surround sound (like HRTF). */ case DevFmtX714: return SPEAKERS_7; + case DevFmtX7144: return SPEAKERS_7; /* 3D7.1 is only compatible with 5.1. This could instead be HEADPHONES to * suggest full-sphere surround sound (like HRTF). */ @@ -576,7 +592,7 @@ void ALCcontext::eax_update_speaker_configuration() void ALCcontext::eax_set_last_error_defaults() noexcept { - mEaxLastError = EAX_OK; + mEaxLastError = EAXCONTEXT_DEFAULTLASTERROR; } void ALCcontext::eax_session_set_defaults() noexcept @@ -665,6 +681,7 @@ void ALCcontext::eax_get_misc(const EaxCall& call) break; case EAXCONTEXT_LASTERROR: call.set_value(mEaxLastError); + mEaxLastError = EAX_OK; break; case EAXCONTEXT_SPEAKERCONFIG: call.set_value(mEaxSpeakerConfig); @@ -747,10 +764,7 @@ void ALCcontext::eax_context_commit_primary_fx_slot_id() void ALCcontext::eax_context_commit_distance_factor() { - if(mListener.mMetersPerUnit == mEax.flDistanceFactor) - return; - - mListener.mMetersPerUnit = mEax.flDistanceFactor; + /* mEax.flDistanceFactor was changed, so the context props are dirty. */ mPropsDirty = true; } @@ -1013,56 +1027,48 @@ void ALCcontext::eaxCommit() } -FORCE_ALIGN ALenum AL_APIENTRY EAXSet(const GUID *a, ALuint b, ALuint c, ALvoid *d, ALuint e) noexcept +FORCE_ALIGN auto AL_APIENTRY EAXSet(const GUID *property_set_id, ALuint property_id, + ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum { auto context = GetContextRef(); if(!context) UNLIKELY return AL_INVALID_OPERATION; - return EAXSetDirect(context.get(), a, b, c, d, e); + return EAXSetDirect(context.get(), property_set_id, property_id, source_id, value, value_size); } -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 +FORCE_ALIGN auto AL_APIENTRY EAXSetDirect(ALCcontext *context, const GUID *property_set_id, + ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum try { std::lock_guard prop_lock{context->mPropLock}; - return context->eax_eax_set( - property_set_id, - property_id, - property_source_id, - property_value, - property_value_size); + return context->eax_eax_set(property_set_id, property_id, source_id, value, value_size); } catch(...) { - eax_log_exception(__func__); + context->eaxSetLastError(); + eax_log_exception(std::data(__func__)); return AL_INVALID_OPERATION; } -FORCE_ALIGN ALenum AL_APIENTRY EAXGet(const GUID *a, ALuint b, ALuint c, ALvoid *d, ALuint e) noexcept +FORCE_ALIGN auto AL_APIENTRY EAXGet(const GUID *property_set_id, ALuint property_id, + ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum { auto context = GetContextRef(); if(!context) UNLIKELY return AL_INVALID_OPERATION; - return EAXGetDirect(context.get(), a, b, c, d, e); + return EAXGetDirect(context.get(), property_set_id, property_id, source_id, value, value_size); } -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 +FORCE_ALIGN auto AL_APIENTRY EAXGetDirect(ALCcontext *context, const GUID *property_set_id, + ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum try { std::lock_guard prop_lock{context->mPropLock}; - return context->eax_eax_get( - property_set_id, - property_id, - property_source_id, - property_value, - property_value_size); + return context->eax_eax_get(property_set_id, property_id, source_id, value, value_size); } catch(...) { - eax_log_exception(__func__); + context->eaxSetLastError(); + eax_log_exception(std::data(__func__)); return AL_INVALID_OPERATION; } #endif // ALSOFT_EAX diff --git a/3rdparty/openal/alc/context.h b/3rdparty/openal/alc/context.h index 201c88730adc..fdd746c698fc 100644 --- a/3rdparty/openal/alc/context.h +++ b/3rdparty/openal/alc/context.h @@ -1,11 +1,14 @@ #ifndef ALC_CONTEXT_H #define ALC_CONTEXT_H +#include "config.h" + #include +#include +#include #include #include #include -#include #include #include #include @@ -17,29 +20,30 @@ #include "AL/alext.h" #include "al/listener.h" -#include "almalloc.h" -#include "alnumeric.h" -#include "atomic.h" +#include "althreads.h" #include "core/context.h" -#include "inprogext.h" #include "intrusive_ptr.h" +#include "opthelpers.h" -#ifdef ALSOFT_EAX -#include "al/eax/call.h" +#if ALSOFT_EAX +#include "al/eax/api.h" #include "al/eax/exception.h" #include "al/eax/fx_slot_index.h" #include "al/eax/fx_slots.h" #include "al/eax/utils.h" + +class EaxCall; #endif // ALSOFT_EAX struct ALeffect; struct ALeffectslot; -struct ALsource; struct DebugGroup; +struct EffectSlotSubList; +struct SourceSubList; -enum class DebugSource : uint8_t; -enum class DebugType : uint8_t; -enum class DebugSeverity : uint8_t; +enum class DebugSource : std::uint8_t; +enum class DebugType : std::uint8_t; +enum class DebugSeverity : std::uint8_t; using uint = unsigned int; @@ -68,47 +72,19 @@ struct DebugLogEntry { }; -struct SourceSubList { - uint64_t FreeMask{~0_u64}; - ALsource *Sources{nullptr}; /* 64 */ - - SourceSubList() noexcept = default; - SourceSubList(const SourceSubList&) = delete; - SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources} - { rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; } - ~SourceSubList(); - - SourceSubList& operator=(const SourceSubList&) = delete; - SourceSubList& operator=(SourceSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; } -}; - -struct EffectSlotSubList { - uint64_t FreeMask{~0_u64}; - ALeffectslot *EffectSlots{nullptr}; /* 64 */ - - EffectSlotSubList() noexcept = default; - EffectSlotSubList(const EffectSlotSubList&) = delete; - EffectSlotSubList(EffectSlotSubList&& rhs) noexcept - : FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots} - { rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; } - ~EffectSlotSubList(); - - EffectSlotSubList& operator=(const EffectSlotSubList&) = delete; - EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; } -}; - -struct ALCcontext : public al::intrusive_ref, ContextBase { - const al::intrusive_ptr mALDevice; +namespace al { +struct Device; +} // namespace al +struct ALCcontext final : public al::intrusive_ref, ContextBase { + const al::intrusive_ptr mALDevice; bool mPropsDirty{true}; bool mDeferUpdates{false}; std::mutex mPropLock; - std::atomic mLastError{AL_NO_ERROR}; + al::tss mLastThreadError{AL_NO_ERROR}; const ContextFlagBitset mContextFlags; std::atomic mDebugEnabled{false}; @@ -145,23 +121,23 @@ struct ALCcontext : public al::intrusive_ref, ContextBase { std::unique_ptr mDefaultSlot; std::vector mExtensions; - std::string mExtensionsString{}; + std::string mExtensionsString; std::unordered_map mSourceNames; std::unordered_map mEffectSlotNames; - ALCcontext(al::intrusive_ptr device, ContextFlagBitset flags); + ALCcontext(al::intrusive_ptr device, ContextFlagBitset flags); ALCcontext(const ALCcontext&) = delete; ALCcontext& operator=(const ALCcontext&) = delete; - ~ALCcontext(); + ~ALCcontext() final; void init(); /** * Removes the context from its device and removes it from being current on - * the running thread or globally. Returns true if other contexts still - * exist on the device. + * the running thread or globally. Stops device playback if this was the + * last context on its device. */ - bool deinit(); + void deinit(); /** * Defers/suspends updates for the given context's listener and sources. @@ -186,8 +162,8 @@ struct ALCcontext : public al::intrusive_ref, ContextBase { */ void applyAllUpdates(); -#ifdef __USE_MINGW_ANSI_STDIO - [[gnu::format(gnu_printf, 3, 4)]] +#ifdef __MINGW32__ + [[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]] #else [[gnu::format(printf, 3, 4)]] #endif @@ -218,8 +194,19 @@ struct ALCcontext : public al::intrusive_ref, ContextBase { */ class ThreadCtx { public: + ThreadCtx() = default; + ThreadCtx(const ThreadCtx&) = delete; + auto operator=(const ThreadCtx&) -> ThreadCtx& = delete; + ~ThreadCtx(); + /* NOLINTBEGIN(readability-convert-member-functions-to-static) + * This should be non-static to invoke construction of the thread-local + * sThreadContext, so that it's destructor gets run at thread exit to + * clear sLocalContext (which isn't a member variable to make read + * access efficient). + */ void set(ALCcontext *ctx) const noexcept { sLocalContext = ctx; } + /* NOLINTEND(readability-convert-member-functions-to-static) */ }; static thread_local ThreadCtx sThreadContext; @@ -230,10 +217,7 @@ struct ALCcontext : public al::intrusive_ref, ContextBase { /* Default effect that applies to sources that don't have an effect on send 0. */ static ALeffect sDefaultEffect; - DEF_NEWDEL(ALCcontext) - -#ifdef ALSOFT_EAX -public: +#if ALSOFT_EAX bool hasEax() const noexcept { return mEaxIsInitialized; } bool eaxIsCapable() const noexcept; @@ -255,7 +239,11 @@ struct ALCcontext : public al::intrusive_ref, ContextBase { void eaxSetLastError() noexcept; - EaxFxSlotIndex eaxGetPrimaryFxSlotIndex() const noexcept + [[nodiscard]] + auto eaxGetDistanceFactor() const noexcept -> float { return mEax.flDistanceFactor; } + + [[nodiscard]] + auto eaxGetPrimaryFxSlotIndex() const noexcept -> EaxFxSlotIndex { return mEaxPrimaryFxSlotIndex; } const ALeffectslot& eaxGetFxSlot(EaxFxSlotIndexValue fx_slot_index) const @@ -290,12 +278,11 @@ struct ALCcontext : public al::intrusive_ref, ContextBase { Eax5Props d; // Deferred. }; - class ContextException : public EaxException - { + class ContextException final : public EaxException { public: - explicit ContextException(const char* message) + explicit ContextException(const char *message) : EaxException{"EAX_CONTEXT", message} - {} + { } }; struct Eax4PrimaryFxSlotIdValidator { @@ -477,7 +464,7 @@ struct ALCcontext : public al::intrusive_ref, ContextBase { typename TMemberResult, typename TProps, typename TState> - void eax_defer(const EaxCall& call, TState& state, TMemberResult TProps::*member) noexcept + void eax_defer(const EaxCall& call, TState& state, TMemberResult TProps::*member) { const auto& src = call.get_value(); TValidator{}(src); @@ -560,28 +547,20 @@ struct ALCcontext : public al::intrusive_ref, ContextBase { using ContextRef = al::intrusive_ptr; -ContextRef GetContextRef(void); +ContextRef GetContextRef() noexcept; void UpdateContextProps(ALCcontext *context); -extern bool TrapALError; +inline bool TrapALError{false}; -#ifdef ALSOFT_EAX -ALenum AL_APIENTRY EAXSet( - const GUID* property_set_id, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_value, - ALuint property_value_size) noexcept; +#if ALSOFT_EAX +auto AL_APIENTRY EAXSet(const GUID *property_set_id, ALuint property_id, + ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum; -ALenum AL_APIENTRY EAXGet( - const GUID* property_set_id, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_value, - ALuint property_value_size) noexcept; +auto AL_APIENTRY EAXGet(const GUID *property_set_id, ALuint property_id, + ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum; #endif // ALSOFT_EAX #endif /* ALC_CONTEXT_H */ diff --git a/3rdparty/openal/alc/device.cpp b/3rdparty/openal/alc/device.cpp index 27aa6f36e349..64c339983798 100644 --- a/3rdparty/openal/alc/device.cpp +++ b/3rdparty/openal/alc/device.cpp @@ -3,19 +3,22 @@ #include "device.h" +#include +#include #include -#include +#include "al/buffer.h" +#include "al/effect.h" +#include "al/filter.h" #include "albit.h" -#include "alconfig.h" +#include "alnumeric.h" +#include "atomic.h" #include "backends/base.h" -#include "core/bformatdec.h" -#include "core/bs2b.h" -#include "core/front_stablizer.h" +#include "core/devformat.h" #include "core/hrtf.h" #include "core/logging.h" #include "core/mastering.h" -#include "core/uhjfilter.h" +#include "flexarray.h" namespace { @@ -24,11 +27,12 @@ using voidp = void*; } // namespace +namespace al { -ALCdevice::ALCdevice(DeviceType type) : DeviceBase{type} +Device::Device(DeviceType type) : DeviceBase{type} { } -ALCdevice::~ALCdevice() +Device::~Device() { TRACE("Freeing device %p\n", voidp{this}); @@ -53,10 +57,10 @@ ALCdevice::~ALCdevice() WARN("%zu Filter%s not deleted\n", count, (count==1)?"":"s"); } -void ALCdevice::enumerateHrtfs() +void Device::enumerateHrtfs() { - mHrtfList = EnumerateHrtf(configValue(nullptr, "hrtf-paths")); - if(auto defhrtfopt = configValue(nullptr, "default-hrtf")) + mHrtfList = EnumerateHrtf(configValue({}, "hrtf-paths")); + if(auto defhrtfopt = configValue({}, "default-hrtf")) { auto iter = std::find(mHrtfList.begin(), mHrtfList.end(), *defhrtfopt); if(iter == mHrtfList.end()) @@ -66,7 +70,7 @@ void ALCdevice::enumerateHrtfs() } } -auto ALCdevice::getOutputMode1() const noexcept -> OutputMode1 +auto Device::getOutputMode1() const noexcept -> OutputMode1 { if(mContexts.load(std::memory_order_relaxed)->empty()) return OutputMode1::Any; @@ -85,9 +89,12 @@ auto ALCdevice::getOutputMode1() const noexcept -> OutputMode1 case DevFmtX61: return OutputMode1::X61; case DevFmtX71: return OutputMode1::X71; case DevFmtX714: + case DevFmtX7144: case DevFmtX3D71: case DevFmtAmbi3D: break; } return OutputMode1::Any; } + +} // namespace al diff --git a/3rdparty/openal/alc/device.h b/3rdparty/openal/alc/device.h index 66f37a7ec534..586fe4b9f05a 100644 --- a/3rdparty/openal/alc/device.h +++ b/3rdparty/openal/alc/device.h @@ -1,85 +1,42 @@ #ifndef ALC_DEVICE_H #define ALC_DEVICE_H +#include "config.h" + #include #include #include #include -#include #include #include -#include +#include #include +#include "AL/al.h" #include "AL/alc.h" #include "AL/alext.h" #include "alconfig.h" -#include "almalloc.h" -#include "alnumeric.h" #include "core/device.h" -#include "inprogext.h" #include "intrusive_ptr.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include "al/eax/x_ram.h" #endif // ALSOFT_EAX -struct ALbuffer; -struct ALeffect; -struct ALfilter; struct BackendBase; +struct BufferSubList; +struct EffectSubList; +struct FilterSubList; using uint = unsigned int; -struct BufferSubList { - uint64_t FreeMask{~0_u64}; - ALbuffer *Buffers{nullptr}; /* 64 */ - - BufferSubList() noexcept = default; - BufferSubList(const BufferSubList&) = delete; - BufferSubList(BufferSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Buffers{rhs.Buffers} - { rhs.FreeMask = ~0_u64; rhs.Buffers = nullptr; } - ~BufferSubList(); - - BufferSubList& operator=(const BufferSubList&) = delete; - BufferSubList& operator=(BufferSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(Buffers, rhs.Buffers); return *this; } -}; - -struct EffectSubList { - uint64_t FreeMask{~0_u64}; - ALeffect *Effects{nullptr}; /* 64 */ - - EffectSubList() noexcept = default; - EffectSubList(const EffectSubList&) = delete; - EffectSubList(EffectSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Effects{rhs.Effects} - { rhs.FreeMask = ~0_u64; rhs.Effects = nullptr; } - ~EffectSubList(); - - EffectSubList& operator=(const EffectSubList&) = delete; - EffectSubList& operator=(EffectSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(Effects, rhs.Effects); return *this; } -}; - -struct FilterSubList { - uint64_t FreeMask{~0_u64}; - ALfilter *Filters{nullptr}; /* 64 */ +struct ALCdevice { virtual ~ALCdevice() = default; }; - FilterSubList() noexcept = default; - FilterSubList(const FilterSubList&) = delete; - FilterSubList(FilterSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Filters{rhs.Filters} - { rhs.FreeMask = ~0_u64; rhs.Filters = nullptr; } - ~FilterSubList(); - - FilterSubList& operator=(const FilterSubList&) = delete; - FilterSubList& operator=(FilterSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(Filters, rhs.Filters); return *this; } -}; +namespace al { - -struct ALCdevice : public al::intrusive_ref, DeviceBase { +struct Device final : public ALCdevice, al::intrusive_ref, DeviceBase { /* This lock protects the device state (format, update size, etc) from * being from being changed in multiple threads, or being accessed while * being changed. It's also used to serialize calls to the backend. @@ -129,7 +86,7 @@ struct ALCdevice : public al::intrusive_ref, DeviceBase { std::mutex FilterLock; std::vector FilterList; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX ALuint eax_x_ram_free_size{eax_x_ram_max_size}; #endif // ALSOFT_EAX @@ -138,37 +95,41 @@ struct ALCdevice : public al::intrusive_ref, DeviceBase { std::unordered_map mEffectNames; std::unordered_map mFilterNames; - ALCdevice(DeviceType type); - ~ALCdevice(); + std::string mVendorOverride; + std::string mVersionOverride; + std::string mRendererOverride; + + Device(DeviceType type); + ~Device() final; void enumerateHrtfs(); - bool getConfigValueBool(const char *block, const char *key, bool def) - { return GetConfigValueBool(DeviceName.c_str(), block, key, def); } + bool getConfigValueBool(const std::string_view block, const std::string_view key, bool def) + { return GetConfigValueBool(mDeviceName, block, key, def); } template - inline std::optional configValue(const char *block, const char *key) = delete; - - DEF_NEWDEL(ALCdevice) + auto configValue(const std::string_view block, const std::string_view key) -> std::optional = delete; }; -template<> -inline std::optional ALCdevice::configValue(const char *block, const char *key) -{ return ConfigValueStr(DeviceName.c_str(), block, key); } -template<> -inline std::optional ALCdevice::configValue(const char *block, const char *key) -{ return ConfigValueInt(DeviceName.c_str(), block, key); } -template<> -inline std::optional ALCdevice::configValue(const char *block, const char *key) -{ return ConfigValueUInt(DeviceName.c_str(), block, key); } -template<> -inline std::optional ALCdevice::configValue(const char *block, const char *key) -{ return ConfigValueFloat(DeviceName.c_str(), block, key); } -template<> -inline std::optional ALCdevice::configValue(const char *block, const char *key) -{ return ConfigValueBool(DeviceName.c_str(), block, key); } +template<> inline +auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional +{ return ConfigValueStr(mDeviceName, block, key); } +template<> inline +auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional +{ return ConfigValueInt(mDeviceName, block, key); } +template<> inline +auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional +{ return ConfigValueUInt(mDeviceName, block, key); } +template<> inline +auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional +{ return ConfigValueFloat(mDeviceName, block, key); } +template<> inline +auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional +{ return ConfigValueBool(mDeviceName, block, key); } + +} // namespace al /** Stores the latest ALC device error. */ -void alcSetError(ALCdevice *device, ALCenum errorCode); +void alcSetError(al::Device *device, ALCenum errorCode); #endif diff --git a/3rdparty/openal/alc/effects/autowah.cpp b/3rdparty/openal/alc/effects/autowah.cpp index 4f874ef29262..b208996e7378 100644 --- a/3rdparty/openal/alc/effects/autowah.cpp +++ b/3rdparty/openal/alc/effects/autowah.cpp @@ -22,24 +22,24 @@ #include #include +#include #include -#include -#include +#include #include "alc/effects/base.h" -#include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" #include "core/ambidefs.h" #include "core/bufferline.h" #include "core/context.h" -#include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/mixer.h" #include "intrusive_ptr.h" +struct BufferStorage; namespace { @@ -50,35 +50,37 @@ constexpr float QFactor{5.0f}; struct AutowahState final : public EffectState { /* Effect parameters */ - float mAttackRate; - float mReleaseRate; - float mResonanceGain; - float mPeakGain; - float mFreqMinNorm; - float mBandwidthNorm; - float mEnvDelay; + float mAttackRate{}; + float mReleaseRate{}; + float mResonanceGain{}; + float mPeakGain{}; + float mFreqMinNorm{}; + float mBandwidthNorm{}; + float mEnvDelay{}; /* Filter components derived from the envelope. */ - struct { - float cos_w0; - float alpha; - } mEnv[BufferLineSize]; + struct FilterParam { + float cos_w0{}; + float alpha{}; + }; + std::array mEnv; - struct { + struct ChannelData { uint mTargetChannel{InvalidChannelIndex}; - /* Effect filters' history. */ - struct { - float z1, z2; - } mFilter; + struct FilterHistory { + float z1{}, z2{}; + }; + FilterHistory mFilter; /* Effect gains for each output channel */ - float mCurrentGain; - float mTargetGain; - } mChans[MaxAmbiChannels]; + float mCurrentGain{}; + float mTargetGain{}; + }; + std::array mChans; /* Effects buffers */ - alignas(16) float mBufferOut[BufferLineSize]; + alignas(16) FloatBufferLine mBufferOut{}; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; @@ -86,8 +88,6 @@ struct AutowahState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(AutowahState) }; void AutowahState::deviceUpdate(const DeviceBase*, const BufferStorage*) @@ -118,18 +118,19 @@ void AutowahState::deviceUpdate(const DeviceBase*, const BufferStorage*) } void AutowahState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get(*props_); const DeviceBase *device{context->mDevice}; const auto frequency = static_cast(device->Frequency); - const float ReleaseTime{clampf(props->Autowah.ReleaseTime, 0.001f, 1.0f)}; + const float ReleaseTime{std::clamp(props.ReleaseTime, 0.001f, 1.0f)}; - mAttackRate = std::exp(-1.0f / (props->Autowah.AttackTime*frequency)); + mAttackRate = std::exp(-1.0f / (props.AttackTime*frequency)); mReleaseRate = std::exp(-1.0f / (ReleaseTime*frequency)); /* 0-20dB Resonance Peak gain */ - mResonanceGain = std::sqrt(std::log10(props->Autowah.Resonance)*10.0f / 3.0f); - mPeakGain = 1.0f - std::log10(props->Autowah.PeakGain / GainScale); + mResonanceGain = std::sqrt(std::log10(props.Resonance)*10.0f / 3.0f); + mPeakGain = 1.0f - std::log10(props.PeakGain / GainScale); mFreqMinNorm = MinFreq / frequency; mBandwidthNorm = (MaxFreq-MinFreq) / frequency; @@ -155,23 +156,22 @@ void AutowahState::process(const size_t samplesToDo, float env_delay{mEnvDelay}; for(size_t i{0u};i < samplesToDo;i++) { - float w0, sample, a; - /* Envelope follower described on the book: Audio Effects, Theory, * Implementation and Application. */ - sample = peak_gain * std::fabs(samplesIn[0][i]); - a = (sample > env_delay) ? attack_rate : release_rate; + const float sample{peak_gain * std::fabs(samplesIn[0][i])}; + const float a{(sample > env_delay) ? attack_rate : release_rate}; env_delay = lerpf(sample, env_delay, a); /* Calculate the cos and alpha components for this sample's filter. */ - w0 = minf((bandwidth*env_delay + freq_min), 0.46f) * (al::numbers::pi_v*2.0f); + const float w0{std::min(bandwidth*env_delay + freq_min, 0.46f) * + (al::numbers::pi_v*2.0f)}; mEnv[i].cos_w0 = std::cos(w0); mEnv[i].alpha = std::sin(w0)/(2.0f * QFactor); } mEnvDelay = env_delay; - auto chandata = std::begin(mChans); + auto chandata = mChans.begin(); for(const auto &insamples : samplesIn) { const size_t outidx{chandata->mTargetChannel}; @@ -194,18 +194,18 @@ void AutowahState::process(const size_t samplesToDo, { const float alpha{mEnv[i].alpha}; const float cos_w0{mEnv[i].cos_w0}; - float input, output; - float a[3], b[3]; - - b[0] = 1.0f + alpha*res_gain; - b[1] = -2.0f * cos_w0; - b[2] = 1.0f - alpha*res_gain; - a[0] = 1.0f + alpha/res_gain; - a[1] = -2.0f * cos_w0; - a[2] = 1.0f - alpha/res_gain; - - input = insamples[i]; - output = input*(b[0]/a[0]) + z1; + + const std::array b{ + 1.0f + alpha*res_gain, + -2.0f * cos_w0, + 1.0f - alpha*res_gain}; + const std::array a{ + 1.0f + alpha/res_gain, + -2.0f * cos_w0, + 1.0f - alpha/res_gain}; + + const float input{insamples[i]}; + const float output{input*(b[0]/a[0]) + z1}; z1 = input*(b[1]/a[0]) - output*(a[1]/a[0]) + z2; z2 = input*(b[2]/a[0]) - output*(a[2]/a[0]); mBufferOut[i] = output; @@ -214,8 +214,8 @@ void AutowahState::process(const size_t samplesToDo, chandata->mFilter.z2 = z2; /* Now, mix the processed sound data to the output. */ - MixSamples({mBufferOut, samplesToDo}, samplesOut[outidx].data(), chandata->mCurrentGain, - chandata->mTargetGain, samplesToDo); + MixSamples(al::span{mBufferOut}.first(samplesToDo), samplesOut[outidx], + chandata->mCurrentGain, chandata->mTargetGain, samplesToDo); ++chandata; } } diff --git a/3rdparty/openal/alc/effects/base.h b/3rdparty/openal/alc/effects/base.h index 95695857a4d7..a64880d26a54 100644 --- a/3rdparty/openal/alc/effects/base.h +++ b/3rdparty/openal/alc/effects/base.h @@ -4,23 +4,27 @@ #include "core/effects/base.h" -EffectStateFactory *NullStateFactory_getFactory(void); -EffectStateFactory *ReverbStateFactory_getFactory(void); -EffectStateFactory *StdReverbStateFactory_getFactory(void); -EffectStateFactory *AutowahStateFactory_getFactory(void); -EffectStateFactory *ChorusStateFactory_getFactory(void); -EffectStateFactory *CompressorStateFactory_getFactory(void); -EffectStateFactory *DistortionStateFactory_getFactory(void); -EffectStateFactory *EchoStateFactory_getFactory(void); -EffectStateFactory *EqualizerStateFactory_getFactory(void); -EffectStateFactory *FlangerStateFactory_getFactory(void); -EffectStateFactory *FshifterStateFactory_getFactory(void); -EffectStateFactory *ModulatorStateFactory_getFactory(void); -EffectStateFactory *PshifterStateFactory_getFactory(void); -EffectStateFactory* VmorpherStateFactory_getFactory(void); - -EffectStateFactory *DedicatedStateFactory_getFactory(void); - -EffectStateFactory *ConvolutionStateFactory_getFactory(void); +/* This is a user config option for modifying the overall output of the reverb + * effect. + */ +inline float ReverbBoost{1.0f}; + + +EffectStateFactory *NullStateFactory_getFactory(); +EffectStateFactory *ReverbStateFactory_getFactory(); +EffectStateFactory *ChorusStateFactory_getFactory(); +EffectStateFactory *AutowahStateFactory_getFactory(); +EffectStateFactory *CompressorStateFactory_getFactory(); +EffectStateFactory *DistortionStateFactory_getFactory(); +EffectStateFactory *EchoStateFactory_getFactory(); +EffectStateFactory *EqualizerStateFactory_getFactory(); +EffectStateFactory *FshifterStateFactory_getFactory(); +EffectStateFactory *ModulatorStateFactory_getFactory(); +EffectStateFactory *PshifterStateFactory_getFactory(); +EffectStateFactory* VmorpherStateFactory_getFactory(); + +EffectStateFactory *DedicatedStateFactory_getFactory(); + +EffectStateFactory *ConvolutionStateFactory_getFactory(); #endif /* EFFECTS_BASE_H */ diff --git a/3rdparty/openal/alc/effects/chorus.cpp b/3rdparty/openal/alc/effects/chorus.cpp index c84531d4411d..4e6d36c55dfe 100644 --- a/3rdparty/openal/alc/effects/chorus.cpp +++ b/3rdparty/openal/alc/effects/chorus.cpp @@ -22,20 +22,22 @@ #include #include -#include +#include #include -#include +#include +#include #include #include "alc/effects/base.h" -#include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" +#include "core/ambidefs.h" #include "core/bufferline.h" #include "core/context.h" -#include "core/devformat.h" +#include "core/cubic_tables.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/mixer.h" #include "core/mixer/defs.h" @@ -43,11 +45,19 @@ #include "intrusive_ptr.h" #include "opthelpers.h" +struct BufferStorage; namespace { using uint = unsigned int; +constexpr auto inv_sqrt2 = static_cast(1.0 / al::numbers::sqrt2); +constexpr auto lcoeffs_pw = CalcDirectionCoeffs(std::array{-1.0f, 0.0f, 0.0f}); +constexpr auto rcoeffs_pw = CalcDirectionCoeffs(std::array{ 1.0f, 0.0f, 0.0f}); +constexpr auto lcoeffs_nrml = CalcDirectionCoeffs(std::array{-inv_sqrt2, 0.0f, inv_sqrt2}); +constexpr auto rcoeffs_nrml = CalcDirectionCoeffs(std::array{ inv_sqrt2, 0.0f, inv_sqrt2}); + + struct ChorusState final : public EffectState { std::vector mDelayBuffer; uint mOffset{0}; @@ -58,16 +68,17 @@ struct ChorusState final : public EffectState { uint mLfoDisp{0}; /* Calculated delays to apply to the left and right outputs. */ - uint mModDelays[2][BufferLineSize]; + std::array,2> mModDelays{}; /* Temp storage for the modulated left and right outputs. */ - alignas(16) float mBuffer[2][BufferLineSize]; + alignas(16) std::array mBuffer{}; /* Gains for left and right outputs. */ - struct { - float Current[MaxAmbiChannels]{}; - float Target[MaxAmbiChannels]{}; - } mGains[2]; + struct OutGains { + std::array Current{}; + std::array Target{}; + }; + std::array mGains; /* effect parameters */ ChorusWaveform mWaveform{}; @@ -78,66 +89,70 @@ struct ChorusState final : public EffectState { void calcTriangleDelays(const size_t todo); void calcSinusoidDelays(const size_t todo); - void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; - void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props, - const EffectTarget target) override; - void process(const size_t samplesToDo, const al::span samplesIn, - const al::span samplesOut) override; + void deviceUpdate(const DeviceBase *device, const float MaxDelay); + void update(const ContextBase *context, const EffectSlot *slot, const ChorusWaveform waveform, + const float delay, const float depth, const float feedback, const float rate, + int phase, const EffectTarget target); - DEF_NEWDEL(ChorusState) + void deviceUpdate(const DeviceBase *device, const BufferStorage*) final; + void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props_, + const EffectTarget target) final; + void process(const size_t samplesToDo, const al::span samplesIn, + const al::span samplesOut) final; }; + void ChorusState::deviceUpdate(const DeviceBase *Device, const BufferStorage*) { - constexpr float max_delay{maxf(ChorusMaxDelay, FlangerMaxDelay)}; - + constexpr auto MaxDelay = std::max(ChorusMaxDelay, FlangerMaxDelay); const auto frequency = static_cast(Device->Frequency); - const size_t maxlen{NextPowerOf2(float2uint(max_delay*2.0f*frequency) + 1u)}; + const size_t maxlen{NextPowerOf2(float2uint(MaxDelay*2.0f*frequency) + 1u)}; if(maxlen != mDelayBuffer.size()) decltype(mDelayBuffer)(maxlen).swap(mDelayBuffer); std::fill(mDelayBuffer.begin(), mDelayBuffer.end(), 0.0f); for(auto &e : mGains) { - std::fill(std::begin(e.Current), std::end(e.Current), 0.0f); - std::fill(std::begin(e.Target), std::end(e.Target), 0.0f); + e.Current.fill(0.0f); + e.Target.fill(0.0f); } } -void ChorusState::update(const ContextBase *Context, const EffectSlot *Slot, - const EffectProps *props, const EffectTarget target) +void ChorusState::update(const ContextBase *context, const EffectSlot *slot, + const EffectProps *props_, const EffectTarget target) { - constexpr int mindelay{(MaxResamplerPadding>>1) << MixerFracBits}; + static constexpr int mindelay{MaxResamplerEdge << gCubicTable.sTableBits}; + auto &props = std::get(*props_); /* The LFO depth is scaled to be relative to the sample delay. Clamp the * delay and depth to allow enough padding for resampling. */ - const DeviceBase *device{Context->mDevice}; + const DeviceBase *device{context->mDevice}; const auto frequency = static_cast(device->Frequency); - mWaveform = props->Chorus.Waveform; + mWaveform = props.Waveform; - mDelay = maxi(float2int(props->Chorus.Delay*frequency*MixerFracOne + 0.5f), mindelay); - mDepth = minf(props->Chorus.Depth * static_cast(mDelay), + const auto stepscale = float{frequency * gCubicTable.sTableSteps}; + mDelay = std::max(float2int(std::round(props.Delay * stepscale)), mindelay); + mDepth = std::min(static_cast(mDelay) * props.Depth, static_cast(mDelay - mindelay)); - mFeedback = props->Chorus.Feedback; + mFeedback = props.Feedback; /* Gains for left and right sides */ - static constexpr auto inv_sqrt2 = static_cast(1.0 / al::numbers::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; + const bool ispairwise{device->mRenderMode == RenderMode::Pairwise}; + const auto lcoeffs = (!ispairwise) ? al::span{lcoeffs_nrml} : al::span{lcoeffs_pw}; + const auto rcoeffs = (!ispairwise) ? al::span{rcoeffs_nrml} : al::span{rcoeffs_pw}; + /* Attenuate the outputs by -3dB, since we duplicate a single mono input to + * separate left/right outputs. + */ + const auto gain = slot->Gain * (1.0f/al::numbers::sqrt2_v); 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, gain, mGains[0].Target); + ComputePanGains(target.Main, rcoeffs, gain, mGains[1].Target); - float rate{props->Chorus.Rate}; - if(!(rate > 0.0f)) + if(!(props.Rate > 0.0f)) { mLfoOffset = 0; mLfoRange = 1; @@ -149,7 +164,9 @@ void ChorusState::update(const ContextBase *Context, const EffectSlot *Slot, /* Calculate LFO coefficient (number of samples per cycle). Limit the * max range to avoid overflow when calculating the displacement. */ - uint lfo_range{float2uint(minf(frequency/rate + 0.5f, float{INT_MAX/360 - 180}))}; + static constexpr int range_limit{std::numeric_limits::max()/360 - 180}; + const auto range = std::round(frequency / props.Rate); + const uint lfo_range{float2uint(std::min(range, float{range_limit}))}; mLfoOffset = mLfoOffset * lfo_range / mLfoRange; mLfoRange = lfo_range; @@ -164,8 +181,8 @@ void ChorusState::update(const ContextBase *Context, const EffectSlot *Slot, } /* Calculate lfo phase displacement */ - int phase{props->Chorus.Phase}; - if(phase < 0) phase = 360 + phase; + auto phase = props.Phase; + if(phase < 0) phase += 360; mLfoDisp = (mLfoRange*static_cast(phase) + 180) / 360; } } @@ -178,9 +195,6 @@ void ChorusState::calcTriangleDelays(const size_t todo) const float depth{mDepth}; const int delay{mDelay}; - ASSUME(lfo_range > 0); - ASSUME(todo > 0); - auto gen_lfo = [lfo_scale,depth,delay](const uint offset) -> uint { const float offset_norm{static_cast(offset) * lfo_scale}; @@ -188,25 +202,24 @@ void ChorusState::calcTriangleDelays(const size_t todo) }; uint offset{mLfoOffset}; + ASSUME(lfo_range > offset); + auto ldelays = mModDelays[0].begin(); for(size_t i{0};i < todo;) { - size_t rem{minz(todo-i, lfo_range-offset)}; - do { - mModDelays[0][i++] = gen_lfo(offset++); - } while(--rem); - if(offset == lfo_range) - offset = 0; + const size_t rem{std::min(todo-i, size_t{lfo_range-offset})}; + ldelays = std::generate_n(ldelays, rem, [&offset,gen_lfo] { return gen_lfo(offset++); }); + if(offset == lfo_range) offset = 0; + i += rem; } offset = (mLfoOffset+mLfoDisp) % lfo_range; + auto rdelays = mModDelays[1].begin(); for(size_t i{0};i < todo;) { - size_t rem{minz(todo-i, lfo_range-offset)}; - do { - mModDelays[1][i++] = gen_lfo(offset++); - } while(--rem); - if(offset == lfo_range) - offset = 0; + const size_t rem{std::min(todo-i, size_t{lfo_range-offset})}; + rdelays = std::generate_n(rdelays, rem, [&offset,gen_lfo] { return gen_lfo(offset++); }); + if(offset == lfo_range) offset = 0; + i += rem; } mLfoOffset = static_cast(mLfoOffset+todo) % lfo_range; @@ -219,9 +232,6 @@ void ChorusState::calcSinusoidDelays(const size_t todo) const float depth{mDepth}; const int delay{mDelay}; - ASSUME(lfo_range > 0); - ASSUME(todo > 0); - auto gen_lfo = [lfo_scale,depth,delay](const uint offset) -> uint { const float offset_norm{static_cast(offset) * lfo_scale}; @@ -229,25 +239,24 @@ void ChorusState::calcSinusoidDelays(const size_t todo) }; uint offset{mLfoOffset}; + ASSUME(lfo_range > offset); + auto ldelays = mModDelays[0].begin(); for(size_t i{0};i < todo;) { - size_t rem{minz(todo-i, lfo_range-offset)}; - do { - mModDelays[0][i++] = gen_lfo(offset++); - } while(--rem); - if(offset == lfo_range) - offset = 0; + const size_t rem{std::min(todo-i, size_t{lfo_range-offset})}; + ldelays = std::generate_n(ldelays, rem, [&offset,gen_lfo] { return gen_lfo(offset++); }); + if(offset == lfo_range) offset = 0; + i += rem; } offset = (mLfoOffset+mLfoDisp) % lfo_range; + auto rdelays = mModDelays[1].begin(); for(size_t i{0};i < todo;) { - size_t rem{minz(todo-i, lfo_range-offset)}; - do { - mModDelays[1][i++] = gen_lfo(offset++); - } while(--rem); - if(offset == lfo_range) - offset = 0; + const size_t rem{std::min(todo-i, size_t{lfo_range-offset})}; + rdelays = std::generate_n(rdelays, rem, [&offset,gen_lfo] { return gen_lfo(offset++); }); + if(offset == lfo_range) offset = 0; + i += rem; } mLfoOffset = static_cast(mLfoOffset+todo) % lfo_range; @@ -255,10 +264,10 @@ void ChorusState::calcSinusoidDelays(const size_t todo) void ChorusState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) { - const size_t bufmask{mDelayBuffer.size()-1}; + const auto delaybuf = al::span{mDelayBuffer}; + const size_t bufmask{delaybuf.size()-1}; const float feedback{mFeedback}; const uint avgdelay{(static_cast(mDelay) + MixerFracHalf) >> MixerFracBits}; - float *RESTRICT delaybuf{mDelayBuffer.data()}; uint offset{mOffset}; if(mWaveform == ChorusWaveform::Sinusoid) @@ -266,35 +275,39 @@ void ChorusState::process(const size_t samplesToDo, const al::span(mBuffer[0])}; - float *RESTRICT rbuffer{al::assume_aligned<16>(mBuffer[1])}; + const auto ldelays = al::span{mModDelays[0]}; + const auto rdelays = al::span{mModDelays[1]}; + const auto lbuffer = al::span{mBuffer[0]}; + const auto rbuffer = al::span{mBuffer[1]}; for(size_t i{0u};i < samplesToDo;++i) { // Feed the buffer's input first (necessary for delays < 1). delaybuf[offset&bufmask] = samplesIn[0][i]; // Tap for the left output. - uint delay{offset - (ldelays[i]>>MixerFracBits)}; - float mu{static_cast(ldelays[i]&MixerFracMask) * (1.0f/MixerFracOne)}; - lbuffer[i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask], - delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask], mu); + size_t delay{offset - (ldelays[i] >> gCubicTable.sTableBits)}; + size_t phase{ldelays[i] & gCubicTable.sTableMask}; + lbuffer[i] = delaybuf[(delay+1) & bufmask]*gCubicTable.getCoeff0(phase) + + delaybuf[(delay ) & bufmask]*gCubicTable.getCoeff1(phase) + + delaybuf[(delay-1) & bufmask]*gCubicTable.getCoeff2(phase) + + delaybuf[(delay-2) & bufmask]*gCubicTable.getCoeff3(phase); // Tap for the right output. - delay = offset - (rdelays[i]>>MixerFracBits); - mu = static_cast(rdelays[i]&MixerFracMask) * (1.0f/MixerFracOne); - rbuffer[i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask], - delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask], mu); + delay = offset - (rdelays[i] >> gCubicTable.sTableBits); + phase = rdelays[i] & gCubicTable.sTableMask; + rbuffer[i] = delaybuf[(delay+1) & bufmask]*gCubicTable.getCoeff0(phase) + + delaybuf[(delay ) & bufmask]*gCubicTable.getCoeff1(phase) + + delaybuf[(delay-1) & bufmask]*gCubicTable.getCoeff2(phase) + + delaybuf[(delay-2) & bufmask]*gCubicTable.getCoeff3(phase); // Accumulate feedback from the average delay of the taps. delaybuf[offset&bufmask] += delaybuf[(offset-avgdelay) & bufmask] * feedback; ++offset; } - MixSamples({lbuffer, samplesToDo}, samplesOut, mGains[0].Current, mGains[0].Target, + MixSamples(lbuffer.first(samplesToDo), samplesOut, mGains[0].Current, mGains[0].Target, samplesToDo, 0); - MixSamples({rbuffer, samplesToDo}, samplesOut, mGains[1].Current, mGains[1].Target, + MixSamples(rbuffer.first(samplesToDo), samplesOut, mGains[1].Current, mGains[1].Target, samplesToDo, 0); mOffset = offset; @@ -306,15 +319,6 @@ struct ChorusStateFactory final : public EffectStateFactory { { return al::intrusive_ptr{new ChorusState{}}; } }; - -/* Flanger is basically a chorus with a really short delay. They can both use - * the same processing functions, so piggyback flanger on the chorus functions. - */ -struct FlangerStateFactory final : public EffectStateFactory { - al::intrusive_ptr create() override - { return al::intrusive_ptr{new ChorusState{}}; } -}; - } // namespace EffectStateFactory *ChorusStateFactory_getFactory() @@ -322,9 +326,3 @@ EffectStateFactory *ChorusStateFactory_getFactory() static ChorusStateFactory ChorusFactory{}; return &ChorusFactory; } - -EffectStateFactory *FlangerStateFactory_getFactory() -{ - static FlangerStateFactory FlangerFactory{}; - return &FlangerFactory; -} diff --git a/3rdparty/openal/alc/effects/compressor.cpp b/3rdparty/openal/alc/effects/compressor.cpp index 0a7ed67a78fb..3197119c7318 100644 --- a/3rdparty/openal/alc/effects/compressor.cpp +++ b/3rdparty/openal/alc/effects/compressor.cpp @@ -32,48 +32,49 @@ #include "config.h" +#include #include +#include #include -#include -#include +#include #include "alc/effects/base.h" -#include "almalloc.h" -#include "alnumeric.h" #include "alspan.h" #include "core/ambidefs.h" #include "core/bufferline.h" -#include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" -#include "core/mixer.h" #include "core/mixer/defs.h" #include "intrusive_ptr.h" +struct BufferStorage; struct ContextBase; namespace { -#define AMP_ENVELOPE_MIN 0.5f -#define AMP_ENVELOPE_MAX 2.0f +constexpr float AmpEnvelopeMin{0.5f}; +constexpr float AmpEnvelopeMax{2.0f}; -#define ATTACK_TIME 0.1f /* 100ms to rise from min to max */ -#define RELEASE_TIME 0.2f /* 200ms to drop from max to min */ +constexpr float AttackTime{0.1f}; /* 100ms to rise from min to max */ +constexpr float ReleaseTime{0.2f}; /* 200ms to drop from max to min */ struct CompressorState final : public EffectState { /* Effect gains for each channel */ - struct { + struct TargetGain { uint mTarget{InvalidChannelIndex}; float mGain{1.0f}; - } mChans[MaxAmbiChannels]; + }; + std::array mChans; /* Effect parameters */ bool mEnabled{true}; float mAttackMult{1.0f}; float mReleaseMult{1.0f}; float mEnvFollower{1.0f}; + alignas(16) FloatBufferLine mGains{}; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; @@ -81,8 +82,6 @@ struct CompressorState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(CompressorState) }; void CompressorState::deviceUpdate(const DeviceBase *device, const BufferStorage*) @@ -90,20 +89,20 @@ void CompressorState::deviceUpdate(const DeviceBase *device, const BufferStorage /* Number of samples to do a full attack and release (non-integer sample * counts are okay). */ - const float attackCount{static_cast(device->Frequency) * ATTACK_TIME}; - const float releaseCount{static_cast(device->Frequency) * RELEASE_TIME}; + const float attackCount{static_cast(device->Frequency) * AttackTime}; + const float releaseCount{static_cast(device->Frequency) * ReleaseTime}; /* Calculate per-sample multipliers to attack and release at the desired * rates. */ - mAttackMult = std::pow(AMP_ENVELOPE_MAX/AMP_ENVELOPE_MIN, 1.0f/attackCount); - mReleaseMult = std::pow(AMP_ENVELOPE_MIN/AMP_ENVELOPE_MAX, 1.0f/releaseCount); + mAttackMult = std::pow(AmpEnvelopeMax/AmpEnvelopeMin, 1.0f/attackCount); + mReleaseMult = std::pow(AmpEnvelopeMin/AmpEnvelopeMax, 1.0f/releaseCount); } void CompressorState::update(const ContextBase*, const EffectSlot *slot, const EffectProps *props, const EffectTarget target) { - mEnabled = props->Compressor.OnOff; + mEnabled = std::get(*props).OnOff; mOutTarget = target.Main->Buffer; auto set_channel = [this](size_t idx, uint outchan, float outgain) @@ -117,72 +116,62 @@ void CompressorState::update(const ContextBase*, const EffectSlot *slot, void CompressorState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) { - for(size_t base{0u};base < samplesToDo;) + /* Generate the per-sample gains from the signal envelope. */ + float env{mEnvFollower}; + if(mEnabled) { - float gains[256]; - const size_t td{minz(256, samplesToDo-base)}; - - /* Generate the per-sample gains from the signal envelope. */ - float env{mEnvFollower}; - if(mEnabled) + for(size_t i{0u};i < samplesToDo;++i) { - for(size_t i{0u};i < td;++i) - { - /* Clamp the absolute amplitude to the defined envelope limits, - * then attack or release the envelope to reach it. - */ - const float amplitude{clampf(std::fabs(samplesIn[0][base+i]), AMP_ENVELOPE_MIN, - AMP_ENVELOPE_MAX)}; - if(amplitude > env) - env = minf(env*mAttackMult, amplitude); - else if(amplitude < env) - env = maxf(env*mReleaseMult, amplitude); - - /* Apply the reciprocal of the envelope to normalize the volume - * (compress the dynamic range). - */ - gains[i] = 1.0f / env; - } + /* Clamp the absolute amplitude to the defined envelope limits, + * then attack or release the envelope to reach it. + */ + const float amplitude{std::clamp(std::fabs(samplesIn[0][i]), AmpEnvelopeMin, + AmpEnvelopeMax)}; + if(amplitude > env) + env = std::min(env*mAttackMult, amplitude); + else if(amplitude < env) + env = std::max(env*mReleaseMult, amplitude); + + /* Apply the reciprocal of the envelope to normalize the volume + * (compress the dynamic range). + */ + mGains[i] = 1.0f / env; } - else + } + else + { + /* Same as above, except the amplitude is forced to 1. This helps + * ensure smooth gain changes when the compressor is turned on and off. + */ + for(size_t i{0u};i < samplesToDo;++i) { - /* Same as above, except the amplitude is forced to 1. This helps - * ensure smooth gain changes when the compressor is turned on and - * off. - */ - for(size_t i{0u};i < td;++i) - { - const float amplitude{1.0f}; - if(amplitude > env) - env = minf(env*mAttackMult, amplitude); - else if(amplitude < env) - env = maxf(env*mReleaseMult, amplitude); + const float amplitude{1.0f}; + if(amplitude > env) + env = std::min(env*mAttackMult, amplitude); + else if(amplitude < env) + env = std::max(env*mReleaseMult, amplitude); - gains[i] = 1.0f / env; - } + mGains[i] = 1.0f / env; } - mEnvFollower = env; + } + mEnvFollower = env; - /* Now compress the signal amplitude to output. */ - auto chan = std::cbegin(mChans); - for(const auto &input : samplesIn) + /* Now compress the signal amplitude to output. */ + auto chan = mChans.cbegin(); + for(const auto &input : samplesIn) + { + const size_t outidx{chan->mTarget}; + if(outidx != InvalidChannelIndex) { - const size_t outidx{chan->mTarget}; - if(outidx != InvalidChannelIndex) + const auto dst = al::span{samplesOut[outidx]}; + const float gain{chan->mGain}; + if(!(std::fabs(gain) > GainSilenceThreshold)) { - const float *RESTRICT src{input.data() + base}; - float *RESTRICT dst{samplesOut[outidx].data() + base}; - const float gain{chan->mGain}; - if(!(std::fabs(gain) > GainSilenceThreshold)) - { - for(size_t i{0u};i < td;i++) - dst[i] += src[i] * gains[i] * gain; - } + for(size_t i{0u};i < samplesToDo;++i) + dst[i] += input[i] * mGains[i] * gain; } - ++chan; } - - base += td; + ++chan; } } diff --git a/3rdparty/openal/alc/effects/convolution.cpp b/3rdparty/openal/alc/effects/convolution.cpp index f46422d40d94..53e9e56a1cd1 100644 --- a/3rdparty/openal/alc/effects/convolution.cpp +++ b/3rdparty/openal/alc/effects/convolution.cpp @@ -1,19 +1,21 @@ #include "config.h" +#include "config_simd.h" #include #include +#include +#include #include #include +#include #include -#include #include -#include -#include +#include -#ifdef HAVE_SSE_INTRINSICS +#if HAVE_SSE_INTRINSICS #include -#elif defined(HAVE_NEON) +#elif HAVE_NEON #include #endif @@ -29,56 +31,85 @@ #include "core/context.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/filters/splitter.h" #include "core/fmt_traits.h" #include "core/mixer.h" +#include "core/uhjfilter.h" #include "intrusive_ptr.h" +#include "opthelpers.h" +#include "pffft.h" #include "polyphase_resampler.h" +#include "vecmat.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 std::byte *src, const size_t srcstep, FmtType srctype, - const size_t samples) noexcept +template +inline void LoadSampleArray(const al::span dst, const std::byte *src, + const std::size_t channel, const std::size_t srcstep) noexcept { -#define HANDLE_FMT(T) case T: al::LoadSampleArray(dst, src, srcstep, samples); break + using TypeTraits = al::FmtTypeTraits; + using SampleType = typename TypeTraits::Type; + const auto converter = TypeTraits{}; + assert(channel < srcstep); + + const auto srcspan = al::span{reinterpret_cast(src), dst.size()*srcstep}; + auto ssrc = srcspan.cbegin(); + std::generate(dst.begin(), dst.end(), [converter,channel,srcstep,&ssrc] + { + const auto ret = converter(ssrc[channel]); + ssrc += ptrdiff_t(srcstep); + return ret; + }); +} + +void LoadSamples(const al::span dst, const std::byte *src, const size_t channel, + const size_t srcstep, const FmtType srctype) noexcept +{ +#define HANDLE_FMT(T) case T: LoadSampleArray(dst, src, channel, srcstep); break switch(srctype) { HANDLE_FMT(FmtUByte); HANDLE_FMT(FmtShort); + HANDLE_FMT(FmtInt); HANDLE_FMT(FmtFloat); HANDLE_FMT(FmtDouble); HANDLE_FMT(FmtMulaw); @@ -86,7 +117,7 @@ void LoadSamples(float *RESTRICT dst, const std::byte *src, const size_t srcstep /* FIXME: Handle ADPCM decoding here. */ case FmtIMA4: case FmtMSADPCM: - std::fill_n(dst, samples, 0.0f); + std::fill(dst.begin(), dst.end(), 0.0f); break; } #undef HANDLE_FMT @@ -137,10 +168,11 @@ constexpr size_t ConvolveUpdateSize{256}; constexpr size_t ConvolveUpdateSamples{ConvolveUpdateSize / 2}; -void apply_fir(al::span dst, const float *RESTRICT src, const float *RESTRICT filter) +void apply_fir(al::span dst, const al::span input, const al::span filter) { -#ifdef HAVE_SSE_INTRINSICS - for(float &output : dst) + auto src = input.begin(); +#if HAVE_SSE_INTRINSICS + std::generate(dst.begin(), dst.end(), [&src,filter] { __m128 r4{_mm_setzero_ps()}; for(size_t j{0};j < ConvolveUpdateSamples;j+=4) @@ -150,39 +182,40 @@ void apply_fir(al::span dst, const float *RESTRICT src, const float *REST r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs)); } + ++src; + r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); - output = _mm_cvtss_f32(r4); + return _mm_cvtss_f32(r4); + }); - ++src; - } - -#elif defined(HAVE_NEON) +#elif HAVE_NEON - for(float &output : dst) + std::generate(dst.begin(), dst.end(), [&src,filter] { float32x4_t r4{vdupq_n_f32(0.0f)}; for(size_t j{0};j < ConvolveUpdateSamples;j+=4) r4 = vmlaq_f32(r4, vld1q_f32(&src[j]), vld1q_f32(&filter[j])); - r4 = vaddq_f32(r4, vrev64q_f32(r4)); - output = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); - ++src; - } + + r4 = vaddq_f32(r4, vrev64q_f32(r4)); + return vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); + }); #else - for(float &output : dst) + std::generate(dst.begin(), dst.end(), [&src,filter] { float ret{0.0f}; for(size_t j{0};j < ConvolveUpdateSamples;++j) ret += src[j] * filter[j]; - output = ret; ++src; - } + return ret; + }); #endif } + struct ConvolutionState final : public EffectState { FmtChannels mChannels{}; AmbiLayout mAmbiLayout{}; @@ -190,11 +223,13 @@ struct ConvolutionState final : public EffectState { uint mAmbiOrder{}; size_t mFifoPos{0}; - std::array mInput{}; + alignas(16) std::array mInput{}; al::vector,16> mFilter; al::vector,16> mOutput; - alignas(16) std::array mFftBuffer{}; + PFFFTSetup mFft; + alignas(16) std::array mFftBuffer{}; + alignas(16) std::array mFftWorkBuffer{}; size_t mCurrentSegment{0}; size_t mNumConvolveSegs{0}; @@ -202,13 +237,12 @@ struct ConvolutionState final : public EffectState { struct ChannelData { alignas(16) FloatBufferLine mBuffer{}; float mHfScale{}, mLfScale{}; - BandSplitter mFilter{}; - float Current[MAX_OUTPUT_CHANNELS]{}; - float Target[MAX_OUTPUT_CHANNELS]{}; + BandSplitter mFilter; + std::array Current{}; + std::array Target{}; }; - using ChannelDataArray = al::FlexArray; - std::unique_ptr mChans; - std::unique_ptr mComplexData; + std::vector mChans; + al::vector mComplexData; ConvolutionState() = default; @@ -224,24 +258,22 @@ struct ConvolutionState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(ConvolutionState) }; void ConvolutionState::NormalMix(const al::span samplesOut, const size_t samplesToDo) { - for(auto &chan : *mChans) - MixSamples({chan.mBuffer.data(), samplesToDo}, samplesOut, chan.Current, chan.Target, - samplesToDo, 0); + for(auto &chan : mChans) + MixSamples(al::span{chan.mBuffer}.first(samplesToDo), samplesOut, chan.Current, + chan.Target, samplesToDo, 0); } void ConvolutionState::UpsampleMix(const al::span samplesOut, const size_t samplesToDo) { - for(auto &chan : *mChans) + for(auto &chan : mChans) { - const al::span src{chan.mBuffer.data(), samplesToDo}; + const auto src = al::span{chan.mBuffer}.first(samplesToDo); chan.mFilter.processScale(src, chan.mHfScale, chan.mLfScale); MixSamples(src, samplesOut, chan.Current, chan.Target, samplesToDo, 0); } @@ -253,19 +285,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 = PFFFTSetup{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,14 +309,12 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag mChannels = buffer->mChannels; mAmbiLayout = IsUHJ(mChannels) ? AmbiLayout::FuMa : buffer->mAmbiLayout; mAmbiScaling = IsUHJ(mChannels) ? AmbiScaling::UHJ : buffer->mAmbiScaling; - mAmbiOrder = minu(buffer->mAmbiOrder, MaxConvolveAmbiOrder); + mAmbiOrder = std::min(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 @@ -295,7 +329,7 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag buffer->mSampleRate); const BandSplitter splitter{device->mXOverFreq / static_cast(device->Frequency)}; - for(auto &e : *mChans) + for(auto &e : mChans) e.mFilter = splitter; mFilter.resize(numChannels, {}); @@ -307,126 +341,150 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag * segment is allocated to simplify handling. */ mNumConvolveSegs = (resampledCount+(ConvolveUpdateSamples-1)) / ConvolveUpdateSamples; - mNumConvolveSegs = maxz(mNumConvolveSegs, 2) - 1; + mNumConvolveSegs = std::max(mNumConvolveSegs, 2_uz) - 1_uz; - const size_t complex_length{mNumConvolveSegs * m * (numChannels+1)}; - mComplexData = std::make_unique(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)}; - auto srcsamples = std::make_unique(srclinelength * numChannels); - std::fill_n(srcsamples.get(), srclinelength * numChannels, 0.0f); + auto srcsamples = std::vector(srclinelength * numChannels); + std::fill(srcsamples.begin(), srcsamples.end(), 0.0f); for(size_t c{0};c < numChannels && c < realChannels;++c) - LoadSamples(srcsamples.get() + srclinelength*c, buffer->mData.data() + bytesPerSample*c, - realChannels, buffer->mType, buffer->mSampleLen); + LoadSamples(al::span{srcsamples}.subspan(srclinelength*c, buffer->mSampleLen), + buffer->mData.data(), c, realChannels, buffer->mType); if(IsUHJ(mChannels)) { auto decoder = std::make_unique(); std::array samples{}; for(size_t c{0};c < numChannels;++c) - samples[c] = srcsamples.get() + srclinelength*c; + samples[c] = al::to_address(srcsamples.begin() + ptrdiff_t(srclinelength*c)); decoder->decode({samples.data(), numChannels}, buffer->mSampleLen, buffer->mSampleLen); } - auto ressamples = std::make_unique(buffer->mSampleLen + - (resampler ? resampledCount : 0)); - complex_f *filteriter = mComplexData.get() + mNumConvolveSegs*m; + auto ressamples = std::vector(buffer->mSampleLen + (resampler ? resampledCount : 0)); + auto ffttmp = al::vector(ConvolveUpdateSize); + auto fftbuffer = std::vector>(ConvolveUpdateSize); + + auto filteriter = mComplexData.begin() + ptrdiff_t(mNumConvolveSegs*ConvolveUpdateSize); for(size_t c{0};c < numChannels;++c) { + auto bufsamples = al::span{srcsamples}.subspan(srclinelength*c, buffer->mSampleLen); /* Resample to match the device. */ if(resampler) { - std::copy_n(srcsamples.get() + srclinelength*c, buffer->mSampleLen, - ressamples.get() + resampledCount); - resampler.process(buffer->mSampleLen, ressamples.get()+resampledCount, - resampledCount, ressamples.get()); + auto restmp = al::span{ressamples}.subspan(resampledCount, buffer->mSampleLen); + std::copy(bufsamples.cbegin(), bufsamples.cend(), restmp.begin()); + resampler.process(restmp, al::span{ressamples}.first(resampledCount)); } else - std::copy_n(srcsamples.get() + srclinelength*c, buffer->mSampleLen, ressamples.get()); + std::copy(bufsamples.cbegin(), bufsamples.cend(), ressamples.begin()); /* Store the first segment's samples in reverse in the time-domain, to * apply as a FIR filter. */ - const size_t first_size{minz(resampledCount, ConvolveUpdateSamples)}; - std::transform(ressamples.get(), ressamples.get()+first_size, mFilter[c].rbegin(), + const size_t first_size{std::min(size_t{resampledCount}, ConvolveUpdateSamples)}; + auto sampleseg = al::span{ressamples.cbegin(), first_size}; + std::transform(sampleseg.cbegin(), sampleseg.cend(), mFilter[c].rbegin(), [](const double d) noexcept -> float { return static_cast(d); }); - auto fftbuffer = std::vector>(ConvolveUpdateSize); size_t done{first_size}; for(size_t s{0};s < mNumConvolveSegs;++s) { - const size_t todo{minz(resampledCount-done, ConvolveUpdateSamples)}; + const size_t todo{std::min(resampledCount-done, ConvolveUpdateSamples)}; + sampleseg = al::span{ressamples}.subspan(done, todo); - auto iter = std::copy_n(&ressamples[done], todo, fftbuffer.begin()); + /* Apply a double-precision forward FFT for more precise frequency + * measurements. + */ + auto iter = std::copy(sampleseg.cbegin(), sampleseg.cend(), fftbuffer.begin()); done += todo; std::fill(iter, fftbuffer.end(), std::complex{}); - forward_fft(al::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(fftbuffer[i].real()) * fftscale; + ffttmp[i*2 + 1] = static_cast((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). + */ + mFft.zreorder(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 ChanPosMap MonoMap[1]{ - { FrontCenter, std::array{0.0f, 0.0f, -1.0f} } - }, StereoMap[2]{ - { FrontLeft, std::array{-sin45, 0.0f, -cos45} }, - { FrontRight, std::array{ sin45, 0.0f, -cos45} }, - }, RearMap[2]{ - { BackLeft, std::array{-sin45, 0.0f, cos45} }, - { BackRight, std::array{ sin45, 0.0f, cos45} }, - }, QuadMap[4]{ - { 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, 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, 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, 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} }, + static constexpr std::array MonoMap{ + ChanPosMap{FrontCenter, std::array{0.0f, 0.0f, -1.0f}} + }; + static constexpr std::array StereoMap{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + }; + static constexpr std::array RearMap{ + ChanPosMap{BackLeft, std::array{-sin30, 0.0f, cos30}}, + ChanPosMap{BackRight, std::array{ sin30, 0.0f, cos30}}, + }; + static constexpr std::array QuadMap{ + ChanPosMap{FrontLeft, std::array{-sin45, 0.0f, -cos45}}, + ChanPosMap{FrontRight, std::array{ sin45, 0.0f, -cos45}}, + ChanPosMap{BackLeft, std::array{-sin45, 0.0f, cos45}}, + ChanPosMap{BackRight, std::array{ sin45, 0.0f, cos45}}, + }; + static constexpr std::array X51Map{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}}, + ChanPosMap{LFE, {}}, + ChanPosMap{SideLeft, std::array{-sin110, 0.0f, -cos110}}, + ChanPosMap{SideRight, std::array{ sin110, 0.0f, -cos110}}, + }; + static constexpr std::array X61Map{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}}, + ChanPosMap{LFE, {}}, + ChanPosMap{BackCenter, std::array{ 0.0f, 0.0f, 1.0f} }, + ChanPosMap{SideLeft, std::array{-1.0f, 0.0f, 0.0f} }, + ChanPosMap{SideRight, std::array{ 1.0f, 0.0f, 0.0f} }, + }; + static constexpr std::array X71Map{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}}, + ChanPosMap{LFE, {}}, + ChanPosMap{BackLeft, std::array{-sin30, 0.0f, cos30}}, + ChanPosMap{BackRight, std::array{ sin30, 0.0f, cos30}}, + ChanPosMap{SideLeft, std::array{ -1.0f, 0.0f, 0.0f}}, + ChanPosMap{SideRight, std::array{ 1.0f, 0.0f, 0.0f}}, }; if(mNumConvolveSegs < 1) UNLIKELY return; + auto &props = std::get(*props_); mMix = &ConvolutionState::NormalMix; - for(auto &chan : *mChans) - std::fill(std::begin(chan.Target), std::end(chan.Target), 0.0f); + for(auto &chan : mChans) + std::fill(chan.Target.begin(), chan.Target.end(), 0.0f); const float gain{slot->Gain}; if(IsAmbisonic(mChannels)) { @@ -434,40 +492,58 @@ 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; + alu::Vector N{props.OrientAt[0], props.OrientAt[1], props.OrientAt[2], 0.0f}; + N.normalize(); + alu::Vector V{props.OrientUp[0], props.OrientUp[1], props.OrientUp[2], 0.0f}; + V.normalize(); + /* Build and normalize right-vector */ + alu::Vector U{N.cross_product(V)}; + U.normalize(); + + const std::array mixmatrix{ + std::array{1.0f, 0.0f, 0.0f, 0.0f}, + std::array{0.0f, U[0], -U[1], U[2]}, + std::array{0.0f, -V[0], V[1], -V[2]}, + std::array{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()}; + const auto index_map = Is2DAmbisonic(mChannels) ? + al::span{GetAmbi2DLayout(mAmbiLayout)}.subspan(0) : + al::span{GetAmbiLayout(mAmbiLayout)}.subspan(0); std::array 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]}; + + std::transform(mixmatrix[acn].cbegin(), mixmatrix[acn].cend(), coeffs.begin(), + [scale](const float in) noexcept -> float { return in * scale; }); + + ComputePanGains(target.Main, coeffs, gain, mChans[c].Target); } } else @@ -477,6 +553,7 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot switch(mChannels) { case FmtMono: chanmap = MonoMap; break; + case FmtMonoDup: chanmap = MonoMap; break; case FmtSuperStereo: case FmtStereo: chanmap = StereoMap; break; case FmtRear: chanmap = RearMap; break; @@ -496,7 +573,7 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot if(device->mRenderMode == RenderMode::Pairwise) { /* Scales the azimuth of the given vector by 3 if it's in front. - * Effectively scales +/-45 degrees to +/-90 degrees, leaving > +90 + * Effectively scales +/-30 degrees to +/-90 degrees, leaving > +90 * and < -90 alone. */ auto ScaleAzimuthFront = [](std::array pos) -> std::array @@ -511,20 +588,20 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot float x{pos[0] / len2d}; float z{-pos[2] / len2d}; - /* Z > cos(pi/4) = -45 < azimuth < 45 degrees. */ - if(z > cos45) + /* Z > cos(pi/6) = -30 < azimuth < 30 degrees. */ + if(z > cos30) { - /* Double the angle represented by x,z. */ - const float resx{2.0f*x * z}; - const float resz{z*z - x*x}; + /* 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] = resx * len2d; - pos[2] = -resz * len2d; + pos[0] = x * len2d; + pos[2] = -z * len2d; } else { - /* If azimuth >= 45 degrees, clamp to 90 degrees. */ + /* If azimuth >= 30 degrees, clamp to 90 degrees. */ pos[0] = std::copysign(len2d, pos[0]); pos[2] = 0.0f; } @@ -536,14 +613,14 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot { if(chanmap[i].channel == LFE) continue; const auto coeffs = CalcDirectionCoeffs(ScaleAzimuthFront(chanmap[i].pos), 0.0f); - ComputePanGains(target.Main, coeffs.data(), gain, (*mChans)[i].Target); + 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 = CalcDirectionCoeffs(chanmap[i].pos, 0.0f); - ComputePanGains(target.Main, coeffs.data(), gain, (*mChans)[i].Target); + ComputePanGains(target.Main, coeffs, gain, mChans[i].Target); } } } @@ -554,27 +631,26 @@ 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;) { - const size_t todo{minz(ConvolveUpdateSamples-mFifoPos, samplesToDo-base)}; + const size_t todo{std::min(ConvolveUpdateSamples-mFifoPos, samplesToDo-base)}; - std::copy_n(samplesIn[0].begin() + base, todo, - mInput.begin()+ConvolveUpdateSamples+mFifoPos); + std::copy_n(samplesIn[0].begin() + ptrdiff_t(base), todo, + mInput.begin()+ptrdiff_t(ConvolveUpdateSamples+mFifoPos)); /* 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; - apply_fir({buf_iter, todo}, mInput.data()+1 + mFifoPos, mFilter[c].data()); + auto outspan = al::span{mChans[c].mBuffer}.subspan(base, todo); + apply_fir(outspan, al::span{mInput}.subspan(1+mFifoPos), mFilter[c]); - auto fifo_iter = mOutput[c].begin() + mFifoPos; - std::transform(fifo_iter, fifo_iter+todo, buf_iter, buf_iter, std::plus<>{}); + auto fifospan = al::span{mOutput[c]}.subspan(mFifoPos, todo); + std::transform(fifospan.cbegin(), fifospan.cend(), outspan.cbegin(), outspan.begin(), + std::plus{}); } mFifoPos += todo; @@ -586,59 +662,51 @@ 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::span{mFftBuffer}); + mFft.transform(mInput.data(), &mComplexData[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) + auto filter = mComplexData.cbegin() + ptrdiff_t(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); + auto input = mComplexData.cbegin() + ptrdiff_t(curseg*ConvolveUpdateSize); for(size_t s{curseg};s < mNumConvolveSegs;++s) { - for(size_t i{0};i < m;++i,++input,++filter) - mFftBuffer[i] += *input * *filter; + mFft.zconvolve_accumulate(al::to_address(input), al::to_address(filter), + mFftBuffer.data()); + input += ConvolveUpdateSize; + filter += ConvolveUpdateSize; } - input = mComplexData.get(); + input = mComplexData.cbegin(); for(size_t s{0};s < curseg;++s) { - for(size_t i{0};i < m;++i,++input,++filter) - mFftBuffer[i] += *input * *filter; + mFft.zconvolve_accumulate(al::to_address(input), al::to_address(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::span{mFftBuffer}); - - /* The iFFT'd response is scaled up by the number of bins, so apply - * the inverse to normalize the output. - */ - for(size_t i{0};i < ConvolveUpdateSamples;++i) - mOutput[c][i] = - (mFftBuffer[i].real()+mOutput[c][ConvolveUpdateSamples+i]) * - (1.0f/float{ConvolveUpdateSize}); - for(size_t i{0};i < ConvolveUpdateSamples;++i) - mOutput[c][ConvolveUpdateSamples+i] = mFftBuffer[ConvolveUpdateSamples+i].real(); + mFft.transform(mFftBuffer.data(), mFftBuffer.data(), mFftWorkBuffer.data(), + PFFFT_BACKWARD); + + /* The filter was attenuated, so the response is already scaled. */ + std::transform(mFftBuffer.cbegin(), mFftBuffer.cbegin()+ConvolveUpdateSamples, + mOutput[c].cbegin()+ConvolveUpdateSamples, mOutput[c].begin(), std::plus{}); + std::copy(mFftBuffer.cbegin()+ConvolveUpdateSamples, mFftBuffer.cend(), + mOutput[c].begin()+ConvolveUpdateSamples); } /* Shift the input history. */ diff --git a/3rdparty/openal/alc/effects/dedicated.cpp b/3rdparty/openal/alc/effects/dedicated.cpp index e82b13d9b968..df1bd5ed0788 100644 --- a/3rdparty/openal/alc/effects/dedicated.cpp +++ b/3rdparty/openal/alc/effects/dedicated.cpp @@ -23,18 +23,19 @@ #include #include #include -#include +#include #include "alc/effects/base.h" -#include "almalloc.h" #include "alspan.h" #include "core/bufferline.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/mixer.h" #include "intrusive_ptr.h" +struct BufferStorage; struct ContextBase; @@ -47,45 +48,36 @@ struct DedicatedState final : public EffectState { * gains for all possible output channels and not just the main ambisonic * buffer. */ - float mCurrentGains[MAX_OUTPUT_CHANNELS]; - float mTargetGains[MAX_OUTPUT_CHANNELS]; + std::array mCurrentGains{}; + std::array mTargetGains{}; - void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; - void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props, - const EffectTarget target) override; + void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) final; + void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props_, + const EffectTarget target) final; void process(const size_t samplesToDo, const al::span samplesIn, - const al::span samplesOut) override; - - DEF_NEWDEL(DedicatedState) + const al::span samplesOut) final; }; void DedicatedState::deviceUpdate(const DeviceBase*, const BufferStorage*) { - std::fill(std::begin(mCurrentGains), std::end(mCurrentGains), 0.0f); + std::fill(mCurrentGains.begin(), mCurrentGains.end(), 0.0f); } void DedicatedState::update(const ContextBase*, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { - std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f); + std::fill(mTargetGains.begin(), mTargetGains.end(), 0.0f); - const float Gain{slot->Gain * props->Dedicated.Gain}; + auto &props = std::get(*props_); + const float Gain{slot->Gain * props.Gain}; - if(slot->EffectType == EffectSlotType::DedicatedLFE) - { - const uint idx{target.RealOut ? target.RealOut->ChannelIndex[LFE] : InvalidChannelIndex}; - if(idx != InvalidChannelIndex) - { - mOutTarget = target.RealOut->Buffer; - mTargetGains[idx] = Gain; - } - } - else if(slot->EffectType == EffectSlotType::DedicatedDialog) + if(props.Target == DedicatedProps::Dialog) { /* 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] + * plays from the front-center location. + */ + const size_t idx{target.RealOut ? target.RealOut->ChannelIndex[FrontCenter] : InvalidChannelIndex}; if(idx != InvalidChannelIndex) { @@ -97,14 +89,23 @@ void DedicatedState::update(const ContextBase*, const EffectSlot *slot, 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); + } + } + else if(props.Target == DedicatedProps::Lfe) + { + const size_t idx{target.RealOut ? target.RealOut->ChannelIndex[LFE] : InvalidChannelIndex}; + if(idx != InvalidChannelIndex) + { + mOutTarget = target.RealOut->Buffer; + mTargetGains[idx] = Gain; } } } void DedicatedState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) { - MixSamples({samplesIn[0].data(), samplesToDo}, samplesOut, mCurrentGains, mTargetGains, + MixSamples(al::span{samplesIn[0]}.first(samplesToDo), samplesOut, mCurrentGains, mTargetGains, samplesToDo, 0); } diff --git a/3rdparty/openal/alc/effects/distortion.cpp b/3rdparty/openal/alc/effects/distortion.cpp index 392d81c8101a..202e4fd792de 100644 --- a/3rdparty/openal/alc/effects/distortion.cpp +++ b/3rdparty/openal/alc/effects/distortion.cpp @@ -22,30 +22,32 @@ #include #include +#include #include -#include +#include #include "alc/effects/base.h" -#include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" +#include "core/ambidefs.h" #include "core/bufferline.h" #include "core/context.h" -#include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/filters/biquad.h" #include "core/mixer.h" #include "core/mixer/defs.h" #include "intrusive_ptr.h" +struct BufferStorage; namespace { struct DistortionState final : public EffectState { /* Effect gains for each channel */ - float mGain[MaxAmbiChannels]{}; + std::array mGain{}; /* Effect parameters */ BiquadFilter mLowpass; @@ -53,7 +55,7 @@ struct DistortionState final : public EffectState { float mAttenuation{}; float mEdgeCoeff{}; - alignas(16) float mBuffer[2][BufferLineSize]{}; + alignas(16) std::array mBuffer{}; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; @@ -61,8 +63,6 @@ struct DistortionState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(DistortionState) }; void DistortionState::deviceUpdate(const DeviceBase*, const BufferStorage*) @@ -72,16 +72,16 @@ void DistortionState::deviceUpdate(const DeviceBase*, const BufferStorage*) } void DistortionState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get(*props_); const DeviceBase *device{context->mDevice}; /* Store waveshaper edge settings. */ - const float edge{minf(std::sin(al::numbers::pi_v*0.5f * props->Distortion.Edge), - 0.99f)}; + const float edge{std::min(std::sin(al::numbers::pi_v*0.5f * props.Edge), 0.99f)}; mEdgeCoeff = 2.0f * edge / (1.0f-edge); - float cutoff{props->Distortion.LowpassCutoff}; + float cutoff{props.LowpassCutoff}; /* Bandwidth value is constant in octaves. */ float bandwidth{(cutoff / 2.0f) / (cutoff * 0.67f)}; /* Divide normalized frequency by the amount of oversampling done during @@ -90,15 +90,15 @@ void DistortionState::update(const ContextBase *context, const EffectSlot *slot, auto frequency = static_cast(device->Frequency); mLowpass.setParamsFromBandwidth(BiquadType::LowPass, cutoff/frequency/4.0f, 1.0f, bandwidth); - cutoff = props->Distortion.EQCenter; + cutoff = props.EQCenter; /* Convert bandwidth in Hz to octaves. */ - bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f); + bandwidth = props.EQBandwidth / (cutoff * 0.67f); mBandpass.setParamsFromBandwidth(BiquadType::BandPass, cutoff/frequency/4.0f, 1.0f, bandwidth); 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.Gain, mGain); } void DistortionState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) @@ -111,7 +111,7 @@ void DistortionState::process(const size_t samplesToDo, const al::span float { - smp = (1.0f + fc) * smp/(1.0f + fc*std::abs(smp)); - smp = (1.0f + fc) * smp/(1.0f + fc*std::abs(smp)) * -1.0f; - smp = (1.0f + fc) * smp/(1.0f + fc*std::abs(smp)); + smp = (1.0f + fc) * smp/(1.0f + fc*std::fabs(smp)); + smp = (1.0f + fc) * smp/(1.0f + fc*std::fabs(smp)) * -1.0f; + smp = (1.0f + fc) * smp/(1.0f + fc*std::fabs(smp)); return smp; }; - std::transform(std::begin(mBuffer[1]), std::begin(mBuffer[1])+todo, std::begin(mBuffer[0]), + std::transform(mBuffer[1].begin(), mBuffer[1].begin()+todo, mBuffer[0].begin(), proc_sample); /* Third step, do bandpass filtering of distorted signal. */ - mBandpass.process({mBuffer[0], todo}, mBuffer[1]); + mBandpass.process(al::span{mBuffer[0]}.first(todo), mBuffer[1]); todo >>= 2; - const float *outgains{mGain}; - for(FloatBufferLine &output : samplesOut) + auto outgains = mGain.cbegin(); + auto proc_bufline = [this,base,todo,&outgains](FloatBufferSpan output) { /* Fourth step, final, do attenuation and perform decimation, * storing only one sample out of four. */ const float gain{*(outgains++)}; if(!(std::fabs(gain) > GainSilenceThreshold)) - continue; - - for(size_t i{0u};i < todo;i++) - output[base+i] += gain * mBuffer[1][i*4]; - } + return; + + auto src = mBuffer[1].cbegin(); + const auto dst = al::span{output}.subspan(base, todo); + auto dec_sample = [gain,&src](float sample) noexcept -> float + { + sample += *src * gain; + src += 4; + return sample; + }; + std::transform(dst.begin(), dst.end(), dst.begin(), dec_sample); + }; + std::for_each(samplesOut.begin(), samplesOut.end(), proc_bufline); base += todo; } diff --git a/3rdparty/openal/alc/effects/echo.cpp b/3rdparty/openal/alc/effects/echo.cpp index 7824c24694c4..a303117d9d30 100644 --- a/3rdparty/openal/alc/effects/echo.cpp +++ b/3rdparty/openal/alc/effects/echo.cpp @@ -22,25 +22,26 @@ #include #include +#include #include -#include -#include +#include #include #include "alc/effects/base.h" -#include "almalloc.h" #include "alnumeric.h" #include "alspan.h" +#include "core/ambidefs.h" #include "core/bufferline.h" #include "core/context.h" -#include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/filters/biquad.h" #include "core/mixer.h" #include "intrusive_ptr.h" #include "opthelpers.h" +struct BufferStorage; namespace { @@ -53,29 +54,26 @@ struct EchoState final : public EffectState { // The echo is two tap. The delay is the number of samples from before the // current offset - struct { - size_t delay{0u}; - } mTap[2]; + std::array mDelayTap{}; size_t mOffset{0u}; /* The panning gains for the two taps */ - struct { - float Current[MaxAmbiChannels]{}; - float Target[MaxAmbiChannels]{}; - } mGains[2]; + struct OutGains { + std::array Current{}; + std::array Target{}; + }; + std::array mGains; BiquadFilter mFilter; float mFeedGain{0.0f}; - alignas(16) float mTempBuffer[2][BufferLineSize]; + alignas(16) std::array mTempBuffer{}; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props, const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(EchoState) }; void EchoState::deviceUpdate(const DeviceBase *Device, const BufferStorage*) @@ -92,56 +90,57 @@ void EchoState::deviceUpdate(const DeviceBase *Device, const BufferStorage*) std::fill(mSampleBuffer.begin(), mSampleBuffer.end(), 0.0f); for(auto &e : mGains) { - std::fill(std::begin(e.Current), std::end(e.Current), 0.0f); - std::fill(std::begin(e.Target), std::end(e.Target), 0.0f); + std::fill(e.Current.begin(), e.Current.end(), 0.0f); + std::fill(e.Target.begin(), e.Target.end(), 0.0f); } } void EchoState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get(*props_); const DeviceBase *device{context->mDevice}; const auto frequency = static_cast(device->Frequency); - mTap[0].delay = maxu(float2uint(props->Echo.Delay*frequency + 0.5f), 1); - mTap[1].delay = float2uint(props->Echo.LRDelay*frequency + 0.5f) + mTap[0].delay; + mDelayTap[0] = std::max(float2uint(std::round(props.Delay*frequency)), 1u); + mDelayTap[1] = float2uint(std::round(props.LRDelay*frequency)) + mDelayTap[0]; - const float gainhf{maxf(1.0f - props->Echo.Damping, 0.0625f)}; /* Limit -24dB */ + const float gainhf{std::max(1.0f - props.Damping, 0.0625f)}; /* Limit -24dB */ mFilter.setParamsFromSlope(BiquadType::HighShelf, LowpassFreqRef/frequency, gainhf, 1.0f); - mFeedGain = props->Echo.Feedback; + mFeedGain = props.Feedback; - /* Convert echo spread (where 0 = center, +/-1 = sides) to angle. */ - const float angle{std::asin(props->Echo.Spread)}; + /* Convert echo spread (where 0 = center, +/-1 = sides) to a 2D vector. */ + const float x{props.Spread}; /* +x = left */ + const float z{std::sqrt(1.0f - x*x)}; - const auto coeffs0 = CalcAngleCoeffs(-angle, 0.0f, 0.0f); - const auto coeffs1 = CalcAngleCoeffs( angle, 0.0f, 0.0f); + const auto coeffs0 = CalcAmbiCoeffs( x, 0.0f, z, 0.0f); + const auto coeffs1 = CalcAmbiCoeffs(-x, 0.0f, z, 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 samplesIn, const al::span samplesOut) { - const size_t mask{mSampleBuffer.size()-1}; - float *RESTRICT delaybuf{mSampleBuffer.data()}; + const auto delaybuf = al::span{mSampleBuffer}; + const size_t mask{delaybuf.size()-1}; size_t offset{mOffset}; - size_t tap1{offset - mTap[0].delay}; - size_t tap2{offset - mTap[1].delay}; - float z1, z2; + size_t tap1{offset - mDelayTap[0]}; + size_t tap2{offset - mDelayTap[1]}; ASSUME(samplesToDo > 0); const BiquadFilter filter{mFilter}; - std::tie(z1, z2) = mFilter.getComponents(); + auto [z1, z2] = mFilter.getComponents(); for(size_t i{0u};i < samplesToDo;) { offset &= mask; tap1 &= mask; tap2 &= mask; - size_t td{minz(mask+1 - maxz(offset, maxz(tap1, tap2)), samplesToDo-i)}; + size_t td{std::min(mask+1 - std::max(offset, std::max(tap1, tap2)), samplesToDo-i)}; do { /* Feed the delay buffer's input first. */ delaybuf[offset] = samplesIn[0][i]; @@ -161,8 +160,8 @@ void EchoState::process(const size_t samplesToDo, const al::span #include +#include #include #include -#include -#include +#include #include "alc/effects/base.h" -#include "almalloc.h" #include "alspan.h" #include "core/ambidefs.h" #include "core/bufferline.h" #include "core/context.h" -#include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/filters/biquad.h" #include "core/mixer.h" #include "intrusive_ptr.h" +struct BufferStorage; namespace { @@ -86,16 +86,17 @@ namespace { struct EqualizerState final : public EffectState { - struct { + struct OutParams { uint mTargetChannel{InvalidChannelIndex}; /* Effect parameters */ - BiquadFilter mFilter[4]; + std::array mFilter; /* Effect gains for each channel */ float mCurrentGain{}; float mTargetGain{}; - } mChans[MaxAmbiChannels]; + }; + std::array mChans; alignas(16) FloatBufferLine mSampleBuffer{}; @@ -105,8 +106,6 @@ struct EqualizerState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(EqualizerState) }; void EqualizerState::deviceUpdate(const DeviceBase*, const BufferStorage*) @@ -114,18 +113,17 @@ void EqualizerState::deviceUpdate(const DeviceBase*, const BufferStorage*) for(auto &e : mChans) { e.mTargetChannel = InvalidChannelIndex; - std::for_each(std::begin(e.mFilter), std::end(e.mFilter), - std::mem_fn(&BiquadFilter::clear)); + std::for_each(e.mFilter.begin(), e.mFilter.end(), std::mem_fn(&BiquadFilter::clear)); e.mCurrentGain = 0.0f; } } void EqualizerState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get(*props_); const DeviceBase *device{context->mDevice}; auto frequency = static_cast(device->Frequency); - float gain, f0norm; /* Calculate coefficients for the each type of filter. Note that the shelf * and peaking filters' gain is for the centerpoint of the transition band, @@ -133,22 +131,22 @@ void EqualizerState::update(const ContextBase *context, const EffectSlot *slot, * property gains need their dB halved (sqrt of linear gain) for the * shelf/peak to reach the provided gain. */ - gain = std::sqrt(props->Equalizer.LowGain); - f0norm = props->Equalizer.LowCutoff / frequency; + float gain{std::sqrt(props.LowGain)}; + float f0norm{props.LowCutoff / frequency}; mChans[0].mFilter[0].setParamsFromSlope(BiquadType::LowShelf, f0norm, gain, 0.75f); - gain = std::sqrt(props->Equalizer.Mid1Gain); - f0norm = props->Equalizer.Mid1Center / frequency; + gain = std::sqrt(props.Mid1Gain); + f0norm = props.Mid1Center / frequency; mChans[0].mFilter[1].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain, - props->Equalizer.Mid1Width); + props.Mid1Width); - gain = std::sqrt(props->Equalizer.Mid2Gain); - f0norm = props->Equalizer.Mid2Center / frequency; + gain = std::sqrt(props.Mid2Gain); + f0norm = props.Mid2Center / frequency; mChans[0].mFilter[2].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain, - props->Equalizer.Mid2Width); + props.Mid2Width); - gain = std::sqrt(props->Equalizer.HighGain); - f0norm = props->Equalizer.HighCutoff / frequency; + gain = std::sqrt(props.HighGain); + f0norm = props.HighCutoff / frequency; mChans[0].mFilter[3].setParamsFromSlope(BiquadType::HighShelf, f0norm, gain, 0.75f); /* Copy the filter coefficients for the other input channels. */ @@ -171,18 +169,17 @@ void EqualizerState::update(const ContextBase *context, const EffectSlot *slot, void EqualizerState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) { - const al::span buffer{mSampleBuffer.data(), samplesToDo}; - auto chan = std::begin(mChans); + const auto buffer = al::span{mSampleBuffer}.first(samplesToDo); + auto chan = mChans.begin(); for(const auto &input : samplesIn) { - const size_t outidx{chan->mTargetChannel}; - if(outidx != InvalidChannelIndex) + if(const size_t outidx{chan->mTargetChannel}; outidx != InvalidChannelIndex) { - const al::span inbuf{input.data(), samplesToDo}; - DualBiquad{chan->mFilter[0], chan->mFilter[1]}.process(inbuf, buffer.begin()); - DualBiquad{chan->mFilter[2], chan->mFilter[3]}.process(buffer, buffer.begin()); + const auto inbuf = al::span{input}.first(samplesToDo); + DualBiquad{chan->mFilter[0], chan->mFilter[1]}.process(inbuf, buffer); + DualBiquad{chan->mFilter[2], chan->mFilter[3]}.process(buffer, buffer); - MixSamples(buffer, samplesOut[outidx].data(), chan->mCurrentGain, chan->mTargetGain, + MixSamples(buffer, samplesOut[outidx], chan->mCurrentGain, chan->mTargetGain, samplesToDo); } ++chan; diff --git a/3rdparty/openal/alc/effects/fshifter.cpp b/3rdparty/openal/alc/effects/fshifter.cpp index ec0cc29fc339..7790a2435121 100644 --- a/3rdparty/openal/alc/effects/fshifter.cpp +++ b/3rdparty/openal/alc/effects/fshifter.cpp @@ -25,23 +25,25 @@ #include #include #include -#include +#include #include "alc/effects/base.h" #include "alcomplex.h" -#include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" +#include "core/ambidefs.h" #include "core/bufferline.h" #include "core/context.h" -#include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/mixer.h" #include "core/mixer/defs.h" #include "intrusive_ptr.h" +#include "opthelpers.h" +struct BufferStorage; namespace { @@ -57,7 +59,7 @@ constexpr size_t HilStep{HilSize / OversampleFactor}; /* Define a Hann window, used to filter the HIL input and output. */ struct Windower { - alignas(16) std::array mData; + alignas(16) std::array mData{}; Windower() { @@ -91,10 +93,11 @@ struct FshifterState final : public EffectState { alignas(16) FloatBufferLine mBufferOut{}; /* Effect gains for each output channel */ - struct { - float Current[MaxAmbiChannels]{}; - float Target[MaxAmbiChannels]{}; - } mGains[2]; + struct OutGains { + std::array Current{}; + std::array Target{}; + }; + std::array mGains; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; @@ -102,8 +105,6 @@ struct FshifterState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(FshifterState) }; void FshifterState::deviceUpdate(const DeviceBase*, const BufferStorage*) @@ -122,20 +123,21 @@ void FshifterState::deviceUpdate(const DeviceBase*, const BufferStorage*) for(auto &gain : mGains) { - std::fill(std::begin(gain.Current), std::end(gain.Current), 0.0f); - std::fill(std::begin(gain.Target), std::end(gain.Target), 0.0f); + gain.Current.fill(0.0f); + gain.Target.fill(0.0f); } } void FshifterState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get(*props_); const DeviceBase *device{context->mDevice}; - const float step{props->Fshifter.Frequency / static_cast(device->Frequency)}; - mPhaseStep[0] = mPhaseStep[1] = fastf2u(minf(step, 1.0f) * MixerFracOne); + const float step{props.Frequency / static_cast(device->Frequency)}; + mPhaseStep[0] = mPhaseStep[1] = fastf2u(std::min(step, 1.0f) * MixerFracOne); - switch(props->Fshifter.LeftDirection) + switch(props.LeftDirection) { case FShifterDirection::Down: mSign[0] = -1.0; @@ -149,7 +151,7 @@ void FshifterState::update(const ContextBase *context, const EffectSlot *slot, break; } - switch(props->Fshifter.RightDirection) + switch(props.RightDirection) { case FShifterDirection::Down: mSign[1] = -1.0; @@ -172,15 +174,15 @@ void FshifterState::update(const ContextBase *context, const EffectSlot *slot, 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 samplesIn, const al::span samplesOut) { for(size_t base{0u};base < samplesToDo;) { - size_t todo{minz(HilStep-mCount, samplesToDo-base)}; + size_t todo{std::min(HilStep-mCount, samplesToDo-base)}; /* Fill FIFO buffer with samples data */ const size_t pos{mPos}; @@ -218,25 +220,27 @@ void FshifterState::process(const size_t samplesToDo, const al::span(mBufferOut.data())}; for(size_t c{0};c < 2;++c) { + const double sign{mSign[c]}; const uint phase_step{mPhaseStep[c]}; uint phase_idx{mPhase[c]}; - for(size_t k{0};k < samplesToDo;++k) - { - const double phase{phase_idx * (al::numbers::pi*2.0 / MixerFracOne)}; - BufferOut[k] = static_cast(mOutdata[k].real()*std::cos(phase) + - mOutdata[k].imag()*std::sin(phase)*mSign[c]); - - phase_idx += phase_step; - phase_idx &= MixerFracMask; - } + std::transform(mOutdata.cbegin(), mOutdata.cbegin()+samplesToDo, mBufferOut.begin(), + [&phase_idx,phase_step,sign](const complex_d &in) -> float + { + const double phase{phase_idx * (al::numbers::pi*2.0 / MixerFracOne)}; + const auto out = static_cast(in.real()*std::cos(phase) + + in.imag()*std::sin(phase)*sign); + + phase_idx += phase_step; + phase_idx &= MixerFracMask; + return out; + }); mPhase[c] = phase_idx; /* Now, mix the processed sound data to the output. */ - MixSamples({BufferOut, samplesToDo}, samplesOut, mGains[c].Current, mGains[c].Target, - maxz(samplesToDo, 512), 0); + MixSamples(al::span{mBufferOut}.first(samplesToDo), samplesOut, mGains[c].Current, + mGains[c].Target, std::max(samplesToDo, 512_uz), 0); } } diff --git a/3rdparty/openal/alc/effects/modulator.cpp b/3rdparty/openal/alc/effects/modulator.cpp index f99ba19c03f2..e86a3c5d8014 100644 --- a/3rdparty/openal/alc/effects/modulator.cpp +++ b/3rdparty/openal/alc/effects/modulator.cpp @@ -22,65 +22,56 @@ #include #include +#include +#include #include -#include +#include +#include #include "alc/effects/base.h" -#include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" #include "core/ambidefs.h" #include "core/bufferline.h" #include "core/context.h" -#include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/filters/biquad.h" #include "core/mixer.h" #include "intrusive_ptr.h" +#include "opthelpers.h" +struct BufferStorage; namespace { using uint = unsigned int; -inline float Sin(uint index, float scale) -{ return std::sin(static_cast(index) * scale); } - -inline float Saw(uint index, float scale) -{ return static_cast(index)*scale - 1.0f; } - -inline float Square(uint index, float scale) -{ return (static_cast(index)*scale < 0.5f)*2.0f - 1.0f; } +struct SinFunc { + static auto Get(uint index, float scale) noexcept(noexcept(std::sin(0.0f))) -> float + { return std::sin(static_cast(index) * scale); } +}; -inline float One(uint, float) -{ return 1.0f; } +struct SawFunc { + static constexpr auto Get(uint index, float scale) noexcept -> float + { return static_cast(index)*scale - 1.0f; } +}; -struct ModulatorState final : public EffectState { - template - void Modulate(size_t todo) - { - const uint range{mRange}; - const float scale{mIndexScale}; - uint index{mIndex}; +struct SquareFunc { + static constexpr auto Get(uint index, float scale) noexcept -> float + { return float(static_cast(index)*scale < 0.5f)*2.0f - 1.0f; } +}; - ASSUME(range > 1); - ASSUME(todo > 0); +struct OneFunc { + static constexpr auto Get(uint, float) noexcept -> float + { return 1.0f; } +}; - 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; - } - void (ModulatorState::*mGenModSamples)(size_t){}; +struct ModulatorState final : public EffectState { + std::variant mSampleGen; uint mIndex{0}; uint mRange{1}; @@ -89,14 +80,15 @@ struct ModulatorState final : public EffectState { alignas(16) FloatBufferLine mModSamples{}; alignas(16) FloatBufferLine mBuffer{}; - struct { + struct OutParams { uint mTargetChannel{InvalidChannelIndex}; BiquadFilter mFilter; float mCurrentGain{}; float mTargetGain{}; - } mChans[MaxAmbiChannels]; + }; + std::array mChans; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; @@ -104,17 +96,8 @@ struct ModulatorState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(ModulatorState) }; -template<> -void ModulatorState::Modulate(size_t todo) -{ - std::fill_n(mModSamples.begin(), todo, 1.0f); - mIndex = 0; -} - void ModulatorState::deviceUpdate(const DeviceBase*, const BufferStorage*) { for(auto &e : mChans) @@ -126,8 +109,9 @@ void ModulatorState::deviceUpdate(const DeviceBase*, const BufferStorage*) } void ModulatorState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get(*props_); const DeviceBase *device{context->mDevice}; /* The effective frequency will be adjusted to have a whole number of @@ -137,10 +121,10 @@ void ModulatorState::update(const ContextBase *context, const EffectSlot *slot, * 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(device->Frequency)/props->Modulator.Frequency + 0.5f + const float samplesPerCycle{props.Frequency > 0.0f + ? static_cast(device->Frequency)/props.Frequency + 0.5f : 1.0f}; - const uint range{static_cast(clampf(samplesPerCycle, 1.0f, + const uint range{static_cast(std::clamp(samplesPerCycle, 1.0f, static_cast(device->Frequency)))}; mIndex = static_cast(uint64_t{mIndex} * range / mRange); mRange = range; @@ -148,19 +132,19 @@ void ModulatorState::update(const ContextBase *context, const EffectSlot *slot, if(mRange == 1) { mIndexScale = 0.0f; - mGenModSamples = &ModulatorState::Modulate; + mSampleGen.emplace(); } - else if(props->Modulator.Waveform == ModulatorWaveform::Sinusoid) + else if(props.Waveform == ModulatorWaveform::Sinusoid) { mIndexScale = al::numbers::pi_v*2.0f / static_cast(mRange); - mGenModSamples = &ModulatorState::Modulate; + mSampleGen.emplace(); } - else if(props->Modulator.Waveform == ModulatorWaveform::Sawtooth) + else if(props.Waveform == ModulatorWaveform::Sawtooth) { mIndexScale = 2.0f / static_cast(mRange-1); - mGenModSamples = &ModulatorState::Modulate; + mSampleGen.emplace(); } - else /*if(props->Modulator.Waveform == ModulatorWaveform::Square)*/ + else if(props.Waveform == ModulatorWaveform::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 @@ -168,11 +152,11 @@ void ModulatorState::update(const ContextBase *context, const EffectSlot *slot, */ mRange = (mRange+1) & ~1u; mIndexScale = 1.0f / static_cast(mRange-1); - mGenModSamples = &ModulatorState::Modulate; + mSampleGen.emplace(); } - float f0norm{props->Modulator.HighPassCutoff / static_cast(device->Frequency)}; - f0norm = clampf(f0norm, 1.0f/512.0f, 0.49f); + float f0norm{props.HighPassCutoff / static_cast(device->Frequency)}; + f0norm = std::clamp(f0norm, 1.0f/512.0f, 0.49f); /* Bandwidth value is constant in octaves. */ mChans[0].mFilter.setParamsFromBandwidth(BiquadType::HighPass, f0norm, 1.0f, 0.75f); for(size_t i{1u};i < slot->Wet.Buffer.size();++i) @@ -189,20 +173,39 @@ void ModulatorState::update(const ContextBase *context, const EffectSlot *slot, void ModulatorState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) { - (this->*mGenModSamples)(samplesToDo); + ASSUME(samplesToDo > 0); + + std::visit([this,samplesToDo](auto&& type) + { + const uint range{mRange}; + const float scale{mIndexScale}; + uint index{mIndex}; + + ASSUME(range > 1); + + for(size_t i{0};i < samplesToDo;) + { + size_t rem{std::min(samplesToDo-i, size_t{range-index})}; + do { + mModSamples[i++] = type.Get(index++, scale); + } while(--rem); + if(index == range) + index = 0; + } + mIndex = index; + }, mSampleGen); - auto chandata = std::begin(mChans); + auto chandata = mChans.begin(); for(const auto &input : samplesIn) { - const size_t outidx{chandata->mTargetChannel}; - if(outidx != InvalidChannelIndex) + if(const size_t outidx{chandata->mTargetChannel}; outidx != InvalidChannelIndex) { - chandata->mFilter.process({input.data(), samplesToDo}, mBuffer.data()); - for(size_t i{0u};i < samplesToDo;++i) - mBuffer[i] *= mModSamples[i]; + chandata->mFilter.process(al::span{input}.first(samplesToDo), mBuffer); + std::transform(mBuffer.cbegin(), mBuffer.cbegin()+samplesToDo, mModSamples.cbegin(), + mBuffer.begin(), std::multiplies<>{}); - MixSamples({mBuffer.data(), samplesToDo}, samplesOut[outidx].data(), - chandata->mCurrentGain, chandata->mTargetGain, minz(samplesToDo, 64)); + MixSamples(al::span{mBuffer}.first(samplesToDo), samplesOut[outidx], + chandata->mCurrentGain, chandata->mTargetGain, std::min(samplesToDo, 64_uz)); } ++chandata; } diff --git a/3rdparty/openal/alc/effects/null.cpp b/3rdparty/openal/alc/effects/null.cpp index 1f9ae67bc34a..217181a8e710 100644 --- a/3rdparty/openal/alc/effects/null.cpp +++ b/3rdparty/openal/alc/effects/null.cpp @@ -1,14 +1,15 @@ #include "config.h" -#include +#include -#include "almalloc.h" #include "alspan.h" #include "base.h" #include "core/bufferline.h" +#include "core/effects/base.h" #include "intrusive_ptr.h" +struct BufferStorage; struct ContextBase; struct DeviceBase; struct EffectSlot; @@ -25,8 +26,6 @@ struct NullState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(NullState) }; /* This constructs the effect state. It's called when the object is first diff --git a/3rdparty/openal/alc/effects/pshifter.cpp b/3rdparty/openal/alc/effects/pshifter.cpp index 2460cf782751..8b3c6d291cf5 100644 --- a/3rdparty/openal/alc/effects/pshifter.cpp +++ b/3rdparty/openal/alc/effects/pshifter.cpp @@ -25,22 +25,23 @@ #include #include #include -#include +#include #include "alc/effects/base.h" -#include "alcomplex.h" -#include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" +#include "core/ambidefs.h" #include "core/bufferline.h" -#include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/mixer.h" #include "core/mixer/defs.h" #include "intrusive_ptr.h" +#include "pffft.h" +struct BufferStorage; struct ContextBase; @@ -58,7 +59,7 @@ constexpr size_t StftStep{StftSize / OversampleFactor}; /* Define a Hann window, used to filter the STFT input and output. */ struct Windower { - alignas(16) std::array mData; + alignas(16) std::array mData{}; Windower() { @@ -82,27 +83,29 @@ struct FrequencyBin { struct PshifterState final : public EffectState { /* Effect parameters */ - size_t mCount; - size_t mPos; - uint mPitchShiftI; - float mPitchShift; + size_t mCount{}; + size_t mPos{}; + uint mPitchShiftI{}; + float mPitchShift{}; /* Effects buffers */ - std::array mFIFO; - std::array mLastPhase; - std::array mSumPhase; - std::array mOutputAccum; + std::array mFIFO{}; + std::array mLastPhase{}; + std::array mSumPhase{}; + std::array mOutputAccum{}; - std::array mFftBuffer; + PFFFTSetup mFft; + alignas(16) std::array mFftBuffer{}; + alignas(16) std::array mFftWorkBuffer{}; - std::array mAnalysisBuffer; - std::array mSynthesisBuffer; + std::array mAnalysisBuffer{}; + std::array mSynthesisBuffer{}; - alignas(16) FloatBufferLine mBufferOut; + alignas(16) FloatBufferLine mBufferOut{}; /* Effect gains for each output channel */ - float mCurrentGains[MaxAmbiChannels]; - float mTargetGains[MaxAmbiChannels]; + std::array mCurrentGains{}; + std::array mTargetGains{}; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; @@ -110,8 +113,6 @@ struct PshifterState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(PshifterState) }; void PshifterState::deviceUpdate(const DeviceBase*, const BufferStorage*) @@ -126,26 +127,31 @@ 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); + mCurrentGains.fill(0.0f); + mTargetGains.fill(0.0f); + + if(!mFft) + mFft = PFFFTSetup{StftSize, PFFFT_REAL}; } void PshifterState::update(const ContextBase*, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { - const int tune{props->Pshifter.CoarseTune*100 + props->Pshifter.FineTune}; + auto &props = std::get(*props_); + const int tune{props.CoarseTune*100 + props.FineTune}; const float pitch{std::pow(2.0f, static_cast(tune) / 1200.0f)}; - mPitchShiftI = clampu(fastf2u(pitch*MixerFracOne), MixerFracHalf, MixerFracOne*2); + mPitchShiftI = std::clamp(fastf2u(pitch*MixerFracOne), uint{MixerFracHalf}, + uint{MixerFracOne}*2u); mPitchShift = static_cast(mPitchShiftI) * float{1.0f/MixerFracOne}; 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, @@ -162,7 +168,7 @@ void PshifterState::process(const size_t samplesToDo, for(size_t base{0u};base < samplesToDo;) { - const size_t todo{minz(StftStep-mCount, samplesToDo-base)}; + const size_t todo{std::min(StftStep-mCount, samplesToDo-base)}; /* Retrieve the output samples from the FIFO and fill in the new input * samples. @@ -186,15 +192,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::span{mFftBuffer}); + mFft.transform_ordered(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. @@ -232,8 +242,8 @@ void PshifterState::process(const size_t samplesToDo, */ std::fill(mSynthesisBuffer.begin(), mSynthesisBuffer.end(), FrequencyBin{}); - constexpr size_t bin_limit{((StftHalfSize+1)<> MixerFracBits}; @@ -266,21 +276,29 @@ void PshifterState::process(const size_t samplesToDo, tmp -= static_cast(qpd + (qpd%2)); mSumPhase[k] = tmp * al::numbers::pi_v; - 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::span{mFftBuffer}); + mFft.transform_ordered(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); @@ -288,8 +306,8 @@ void PshifterState::process(const size_t samplesToDo, } /* Now, mix the processed sound data to the output. */ - MixSamples({mBufferOut.data(), samplesToDo}, samplesOut, mCurrentGains, mTargetGains, - maxz(samplesToDo, 512), 0); + MixSamples(al::span{mBufferOut}.first(samplesToDo), samplesOut, mCurrentGains, mTargetGains, + std::max(samplesToDo, 512_uz), 0); } diff --git a/3rdparty/openal/alc/effects/reverb.cpp b/3rdparty/openal/alc/effects/reverb.cpp index b00f638b27a7..75f76a159d3b 100644 --- a/3rdparty/openal/alc/effects/reverb.cpp +++ b/3rdparty/openal/alc/effects/reverb.cpp @@ -22,22 +22,25 @@ #include #include +#include +#include +#include #include #include -#include #include -#include +#include +#include #include "alc/effects/base.h" -#include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" #include "core/ambidefs.h" #include "core/bufferline.h" #include "core/context.h" -#include "core/devformat.h" +#include "core/cubic_tables.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/filters/biquad.h" #include "core/filters/splitter.h" @@ -45,13 +48,9 @@ #include "core/mixer/defs.h" #include "intrusive_ptr.h" #include "opthelpers.h" -#include "vecmat.h" #include "vector.h" -/* This is a user config option for modifying the overall output of the reverb - * effect. - */ -float ReverbBoost = 1.0f; +struct BufferStorage; namespace { @@ -65,41 +64,6 @@ constexpr float DefaultModulationTime{0.25f}; #define MOD_FRACMASK (MOD_FRACONE-1) -struct CubicFilter { - static constexpr size_t sTableBits{8}; - static constexpr size_t sTableSteps{1 << sTableBits}; - static constexpr size_t sTableMask{sTableSteps - 1}; - - float mFilter[sTableSteps*2 + 1]{}; - - constexpr CubicFilter() - { - /* This creates a lookup table for a cubic spline filter, with 256 - * steps between samples. Only half the coefficients are needed, since - * Coeff2 is just Coeff1 in reverse and Coeff3 is just Coeff0 in - * reverse. - */ - for(size_t i{0};i < sTableSteps;++i) - { - const double mu{static_cast(i) / double{sTableSteps}}; - const double mu2{mu*mu}, mu3{mu2*mu}; - const double a0{-0.5*mu3 + mu2 + -0.5*mu}; - const double a1{ 1.5*mu3 + -2.5*mu2 + 1.0f}; - mFilter[i] = static_cast(a1); - mFilter[sTableSteps+i] = static_cast(a0); - } - } - - constexpr float getCoeff0(size_t i) const noexcept { return mFilter[sTableSteps+i]; } - constexpr float getCoeff1(size_t i) const noexcept { return mFilter[i]; } - constexpr float getCoeff2(size_t i) const noexcept { return mFilter[sTableSteps-i]; } - constexpr float getCoeff3(size_t i) const noexcept { return mFilter[sTableSteps*2-i]; } -}; -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,35 +86,41 @@ 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 }, - { 0.5f, -0.5f, -0.5f, 0.5f }, - { 0.5f, 0.5f, -0.5f, -0.5f }, - { 0.5f, -0.5f, 0.5f, -0.5f } -}; +alignas(16) constexpr std::array,NUM_LINES> B2A{{ + /* W Y Z X */ + {{ 0.5f, 0.5f, 0.5f, 0.5f }}, /* A0 */ + {{ 0.5f, -0.5f, -0.5f, 0.5f }}, /* A1 */ + {{ 0.5f, 0.5f, -0.5f, -0.5f }}, /* A2 */ + {{ 0.5f, -0.5f, 0.5f, -0.5f }} /* A3 */ +}}; -/* 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,NUM_LINES> EarlyA2B{{ - {{ 0.5f, 0.5f, 0.5f, 0.5f }}, - {{ 0.5f, -0.5f, 0.5f, -0.5f }}, - {{ 0.5f, -0.5f, -0.5f, 0.5f }}, - {{ 0.5f, 0.5f, -0.5f, -0.5f }} + /* A0 A1 A2 A3 */ + {{ 0.5f, 0.5f, 0.5f, 0.5f }}, /* W */ + {{ 0.5f, -0.5f, 0.5f, -0.5f }}, /* Y */ + {{ 0.5f, -0.5f, -0.5f, 0.5f }}, /* Z */ + {{ 0.5f, 0.5f, -0.5f, -0.5f }} /* X */ }}; -/* 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(1.0/al::numbers::sqrt2); alignas(16) constexpr std::array,NUM_LINES> LateA2B{{ - {{ 0.5f, 0.5f, 0.5f, 0.5f }}, - {{ InvSqrt2, -InvSqrt2, 0.0f, 0.0f }}, - {{ 0.0f, 0.0f, InvSqrt2, -InvSqrt2 }}, - {{ 0.5f, 0.5f, -0.5f, -0.5f }} + /* A0 A1 A2 A3 */ + {{ 0.5f, 0.5f, 0.5f, 0.5f }}, /* W */ + {{ InvSqrt2, -InvSqrt2, 0.0f, 0.0f }}, /* Y */ + {{ 0.0f, 0.0f, -InvSqrt2, InvSqrt2 }}, /* Z */ + {{ 0.5f, 0.5f, -0.5f, -0.5f }} /* X */ }}; /* The all-pass and delay lines have a variable length dependent on the @@ -251,7 +221,7 @@ constexpr std::array EARLY_ALLPASS_LENGTHS{{ * Using an average dimension of 1m, we get: */ constexpr std::array EARLY_LINE_LENGTHS{{ - 5.9850400e-4f, 1.0913150e-3f, 1.5376658e-3f, 1.9419362e-3f + 0.0000000e+0f, 4.9281100e-4f, 9.3916180e-4f, 1.3434322e-3f }}; /* The late all-pass filter lengths are based on the late line lengths: @@ -288,21 +258,17 @@ struct DelayLineI { /* The delay lines use interleaved samples, with the lengths being powers * of 2 to allow the use of bit-masking instead of a modulus for wrapping. */ - size_t Mask{0u}; - union { - uintptr_t LineOffset{0u}; - std::array *Line; - }; + al::span mLine; /* Given the allocated sample buffer, this function updates each delay line * offset. */ - void realizeLineOffset(std::array *sampleBuffer) noexcept - { Line = sampleBuffer + LineOffset; } + void realizeLineOffset(al::span sampleBuffer) noexcept + { mLine = sampleBuffer; } /* Calculate the length of a delay line and store its mask and offset. */ - uint calcLineLength(const float length, const uintptr_t offset, const float frequency, - const uint extra) + static + auto calcLineLength(const float length, const float frequency, const uint extra) -> size_t { /* All line lengths are powers of 2, calculated from their lengths in * seconds, rounded up. @@ -310,23 +276,85 @@ struct DelayLineI { uint samples{float2uint(std::ceil(length*frequency))}; samples = NextPowerOf2(samples + extra); - /* All lines share a single sample buffer. */ - Mask = samples - 1; - LineOffset = offset; - /* Return the sample count for accumulation. */ - return samples; + return samples*NUM_LINES; + } +}; + +struct DelayLineU { + al::span mLine; + + void realizeLineOffset(al::span sampleBuffer) noexcept + { + assert(sampleBuffer.size() > 4 && !(sampleBuffer.size() & (sampleBuffer.size()-1))); + mLine = sampleBuffer; + } + + static + auto calcLineLength(const float length, const float frequency, const uint extra) -> size_t + { + uint samples{float2uint(std::ceil(length*frequency))}; + samples = NextPowerOf2(samples + extra); + + return samples*NUM_LINES; + } + + [[nodiscard]] + auto get(size_t chan) const noexcept + { + const size_t stride{mLine.size() / NUM_LINES}; + return mLine.subspan(chan*stride, stride); + } + + void write(size_t offset, const size_t c, al::span in) const noexcept + { + const size_t stride{mLine.size() / NUM_LINES}; + const auto output = mLine.subspan(c*stride); + while(!in.empty()) + { + offset &= stride-1; + const size_t td{std::min(stride - offset, in.size())}; + std::copy_n(in.begin(), td, output.begin() + ptrdiff_t(offset)); + offset += td; + in = in.subspan(td); + } } - void write(size_t offset, const size_t c, const float *RESTRICT in, const size_t count) const noexcept + /* 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 W, applying a 180-degree phase shift and + * moving each response to its spatially opposite location. + */ + void writeReflected(size_t offset, const al::span in, + const size_t count) const noexcept { - ASSUME(count > 0); + const size_t stride{mLine.size() / NUM_LINES}; for(size_t i{0u};i < count;) { - offset &= Mask; - size_t td{minz(Mask+1 - offset, count - i)}; + offset &= stride-1; + size_t td{std::min(stride - offset, count - i)}; do { - Line[offset++][c] = in[i++]; + const std::array src{in[0][i], in[1][i], in[2][i], in[3][i]}; + ++i; + + const std::array f{ + (src[0] - src[1] - src[2] - src[3]) * 0.5f, + (src[1] - src[0] - src[2] - src[3]) * 0.5f, + (src[2] - src[0] - src[1] - src[3]) * 0.5f, + (src[3] - src[0] - src[1] - src[2] ) * 0.5f + }; + mLine[0*stride + offset] = f[0]; + mLine[1*stride + offset] = f[1]; + mLine[2*stride + offset] = f[2]; + mLine[3*stride + offset] = f[3]; + ++offset; } while(--td); } } @@ -335,10 +363,19 @@ struct DelayLineI { struct VecAllpass { DelayLineI Delay; float Coeff{0.0f}; - size_t Offset[NUM_LINES]{}; + std::array Offset{}; void process(const al::span samples, size_t offset, - const float xCoeff, const float yCoeff, const size_t todo); + const float xCoeff, const float yCoeff, const size_t todo) const noexcept; +}; + +struct Allpass4 { + DelayLineU Delay; + float Coeff{0.0f}; + std::array Offset{}; + + void process(const al::span samples, const size_t offset, + const size_t todo) const noexcept; }; struct T60Filter { @@ -353,30 +390,37 @@ struct T60Filter { /* Applies the two T60 damping filter sections. */ void process(const al::span samples) - { DualBiquad{HFFilter, LFFilter}.process(samples, samples.data()); } + { DualBiquad{HFFilter, LFFilter}.process(samples, samples); } void clear() noexcept { HFFilter.clear(); LFFilter.clear(); } }; struct EarlyReflections { - /* A Gerzon vector all-pass filter is used to simulate initial diffusion. - * The spread from this filter also helps smooth out the reverb tail. - */ - VecAllpass VecAp; + Allpass4 VecAp; /* An echo line is used to complete the second half of the early * reflections. */ - DelayLineI Delay; - size_t Offset[NUM_LINES]{}; - float Coeff[NUM_LINES]{}; + DelayLineU Delay; + std::array Offset{}; + std::array Coeff{}; /* The gain for each output channel based on 3D panning. */ - float CurrentGains[NUM_LINES][MaxAmbiChannels]{}; - float TargetGains[NUM_LINES][MaxAmbiChannels]{}; + struct OutGains { + std::array Current{}; + std::array Target{}; + + void clear() { Current.fill(0.0f); Target.fill(0.0); } + }; + std::array Gains{}; void updateLines(const float density_mult, const float diffusion, const float decayTime, const float frequency); + + void clear() + { + std::for_each(Gains.begin(), Gains.end(), std::mem_fn(&OutGains::clear)); + } }; @@ -384,22 +428,29 @@ struct Modulation { /* The vibrato time is tracked with an index over a (MOD_FRACONE) * normalized range. */ - uint Index, Step; + uint Index{0u}, Step{1u}; /* The depth of frequency change, in samples. */ - float Depth; + float Depth{0.0f}; - float ModDelays[MAX_UPDATE_SAMPLES]; + std::array ModDelays{}; void updateModulator(float modTime, float modDepth, float frequency); - void calcDelays(size_t todo); + auto calcDelays(size_t todo) -> al::span; + + void clear() noexcept + { + Index = 0u; + Step = 1u; + Depth = 0.0f; + } }; struct LateReverb { /* A recursive delay line is used fill in the reverb tail. */ - DelayLineI Delay; - size_t Offset[NUM_LINES]{}; + DelayLineU Delay; + std::array Offset{}; /* Attenuation to compensate for the modal density and decay rate of the * late lines. @@ -407,7 +458,7 @@ struct LateReverb { float DensityGain{0.0f}; /* T60 decay filters are used to simulate absorption. */ - T60Filter T60[NUM_LINES]; + std::array T60; Modulation Mod; @@ -415,40 +466,49 @@ struct LateReverb { VecAllpass VecAp; /* The gain for each output channel based on 3D panning. */ - float CurrentGains[NUM_LINES][MaxAmbiChannels]{}; - float TargetGains[NUM_LINES][MaxAmbiChannels]{}; + struct OutGains { + std::array Current{}; + std::array Target{}; + + void clear() { Current.fill(0.0f); Target.fill(0.0); } + }; + std::array Gains{}; void updateLines(const float density_mult, const float diffusion, const float lfDecayTime, const float mfDecayTime, const float hfDecayTime, const float lf0norm, const float hf0norm, const float frequency); - void clear() noexcept + void clear() { - for(auto &filter : T60) - filter.clear(); + std::for_each(T60.begin(), T60.end(), std::mem_fn(&T60Filter::clear)); + Mod.clear(); + std::for_each(Gains.begin(), Gains.end(), std::mem_fn(&OutGains::clear)); } }; struct ReverbPipeline { /* Master effect filters */ - struct { + struct FilterPair { BiquadFilter Lp; BiquadFilter Hp; - } mFilter[NUM_LINES]; + void clear() noexcept { Lp.clear(); Hp.clear(); } + }; + std::array mFilter; - /* Core delay line (early reflections and late reverb tap from this). */ - DelayLineI mEarlyDelayIn; - DelayLineI mLateDelayIn; + /* Late reverb input delay line (early reflections feed this, and late + * reverb taps from it). + */ + DelayLineU mLateDelayIn; - /* Tap points for early reflection delay. */ - size_t mEarlyDelayTap[NUM_LINES][2]{}; - float mEarlyDelayCoeff[NUM_LINES]{}; + /* Tap points for early reflection input delay. */ + std::array,NUM_LINES> mEarlyDelayTap{}; + std::array,NUM_LINES> mEarlyDelayCoeff{}; /* Tap points for late reverb feed and delay. */ - size_t mLateDelayTap[NUM_LINES][2]{}; + std::array,NUM_LINES> mLateDelayTap{}; /* Coefficients for the all-pass and line scattering matrices. */ - float mMixX{0.0f}; + float mMixX{1.0f}; float mMixY{0.0f}; EarlyReflections mEarly; @@ -459,13 +519,13 @@ struct ReverbPipeline { size_t mFadeSampleCount{1}; - void updateDelayLine(const float earlyDelay, const float lateDelay, const float density_mult, - const float decayTime, const float frequency); + void updateDelayLine(const float gain, const float earlyDelay, const float lateDelay, + const float density_mult, const float decayTime, const float frequency); void update3DPanning(const al::span ReflectionsPan, const al::span LateReverbPan, const float earlyGain, const float lateGain, const bool doUpmix, const MixParams *mainMix); - void processEarly(size_t offset, const size_t samplesToDo, + void processEarly(const DelayLineU &main_delay, size_t offset, const size_t samplesToDo, const al::span tempSamples, const al::span outSamples); void processLate(size_t offset, const size_t samplesToDo, @@ -474,17 +534,15 @@ struct ReverbPipeline { void clear() noexcept { - for(auto &filter : mFilter) - { - filter.Lp.clear(); - filter.Hp.clear(); - } + std::for_each(mFilter.begin(), mFilter.end(), std::mem_fn(&FilterPair::clear)); + mEarlyDelayTap = {}; + mEarlyDelayCoeff = {}; + mLateDelayTap = {}; + mEarly.clear(); mLate.clear(); - for(auto &filters : mAmbiSplitter) - { - for(auto &filter : filters) - filter.clear(); - } + auto clear_filters = [](const al::span filters) + { std::for_each(filters.begin(), filters.end(), std::mem_fn(&BandSplitter::clear)); }; + std::for_each(mAmbiSplitter.begin(), mAmbiSplitter.end(), clear_filters); } }; @@ -492,9 +550,9 @@ struct ReverbState final : public EffectState { /* All delay lines are allocated as a single buffer to reduce memory * fragmentation and management code. */ - al::vector,16> mSampleBuffer; + al::vector mSampleBuffer; - struct { + struct Params { /* Calculated parameters which indicate if cross-fading is needed after * an update. */ @@ -507,7 +565,8 @@ struct ReverbState final : public EffectState { float ModulationDepth{0.0f}; float HFReference{5000.0f}; float LFReference{250.0f}; - } mParams; + }; + Params mParams; enum PipelineState : uint8_t { DeviceClear, @@ -517,18 +576,20 @@ struct ReverbState final : public EffectState { Normal, }; PipelineState mPipelineState{DeviceClear}; - uint8_t mCurrentPipeline{0}; + bool mCurrentPipeline{false}; + + /* Core delay line (early reflections tap from this). */ + DelayLineU mMainDelay; - ReverbPipeline mPipelines[2]; + std::array mPipelines; /* The current write offset for all delay lines. */ size_t mOffset{}; /* Temporary storage used when processing. */ - union { - alignas(16) FloatBufferLine mTempLine{}; - alignas(16) std::array mTempSamples; - }; + alignas(16) FloatBufferLine mTempLine{}; + alignas(16) std::array mTempSamples{}; + alignas(16) std::array mEarlySamples{}; alignas(16) std::array mLateSamples{}; @@ -538,48 +599,43 @@ struct ReverbState final : public EffectState { void MixOutPlain(ReverbPipeline &pipeline, const al::span samplesOut, - const size_t todo) + const size_t todo) const { - ASSUME(todo > 0); - /* When not upsampling, the panning gains convert to B-Format and pan * at the same time. */ - for(size_t c{0u};c < NUM_LINES;c++) + auto inBuffer = mEarlySamples.cbegin(); + for(auto &gains : pipeline.mEarly.Gains) { - const al::span tmpspan{mEarlySamples[c].data(), todo}; - MixSamples(tmpspan, samplesOut, pipeline.mEarly.CurrentGains[c], - pipeline.mEarly.TargetGains[c], todo, 0); + MixSamples(al::span{*inBuffer++}.first(todo), samplesOut, gains.Current, gains.Target, + todo, 0); } - for(size_t c{0u};c < NUM_LINES;c++) + inBuffer = mLateSamples.cbegin(); + for(auto &gains : pipeline.mLate.Gains) { - const al::span tmpspan{mLateSamples[c].data(), todo}; - MixSamples(tmpspan, samplesOut, pipeline.mLate.CurrentGains[c], - pipeline.mLate.TargetGains[c], todo, 0); + MixSamples(al::span{*inBuffer++}.first(todo), samplesOut, gains.Current, gains.Target, + todo, 0); } } void MixOutAmbiUp(ReverbPipeline &pipeline, const al::span samplesOut, const size_t todo) { - ASSUME(todo > 0); - auto DoMixRow = [](const al::span OutBuffer, const al::span Gains, - const float *InSamples, const size_t InStride) + const al::span InSamples) { + auto inBuffer = InSamples.cbegin(); std::fill(OutBuffer.begin(), OutBuffer.end(), 0.0f); for(const float gain : Gains) { - const float *RESTRICT input{al::assume_aligned<16>(InSamples)}; - InSamples += InStride; - - if(!(std::fabs(gain) > GainSilenceThreshold)) - continue; - - auto mix_sample = [gain](const float sample, const float in) noexcept -> float - { return sample + in*gain; }; - std::transform(OutBuffer.begin(), OutBuffer.end(), input, OutBuffer.begin(), - mix_sample); + if(std::fabs(gain) > GainSilenceThreshold) + { + auto mix_sample = [gain](const float sample, const float in) noexcept -> float + { return sample + in*gain; }; + std::transform(OutBuffer.begin(), OutBuffer.end(), inBuffer->cbegin(), + OutBuffer.begin(), mix_sample); + } + ++inBuffer; } }; @@ -587,29 +643,33 @@ struct ReverbState final : public EffectState { * so the proper HF scaling can be applied to each B-Format channel. * The panning gains then pan and upsample the B-Format channels. */ - const al::span tmpspan{al::assume_aligned<16>(mTempLine.data()), todo}; - for(size_t c{0u};c < NUM_LINES;c++) + const auto tmpspan = al::span{mTempLine}.first(todo); + auto hfscale = float{mOrderScales[0]}; + auto splitter = pipeline.mAmbiSplitter[0].begin(); + auto a2bcoeffs = EarlyA2B.cbegin(); + for(auto &gains : pipeline.mEarly.Gains) { - DoMixRow(tmpspan, EarlyA2B[c], mEarlySamples[0].data(), mEarlySamples[0].size()); + DoMixRow(tmpspan, *(a2bcoeffs++), mEarlySamples); /* Apply scaling to the B-Format's HF response to "upsample" it to * higher-order output. */ - const float hfscale{(c==0) ? mOrderScales[0] : mOrderScales[1]}; - pipeline.mAmbiSplitter[0][c].processHfScale(tmpspan, hfscale); + (splitter++)->processHfScale(tmpspan, hfscale); + hfscale = mOrderScales[1]; - MixSamples(tmpspan, samplesOut, pipeline.mEarly.CurrentGains[c], - pipeline.mEarly.TargetGains[c], todo, 0); + MixSamples(tmpspan, samplesOut, gains.Current, gains.Target, todo, 0); } - for(size_t c{0u};c < NUM_LINES;c++) + hfscale = mOrderScales[0]; + splitter = pipeline.mAmbiSplitter[1].begin(); + a2bcoeffs = LateA2B.cbegin(); + for(auto &gains : pipeline.mLate.Gains) { - DoMixRow(tmpspan, LateA2B[c], mLateSamples[0].data(), mLateSamples[0].size()); + DoMixRow(tmpspan, *(a2bcoeffs++), mLateSamples); - const float hfscale{(c==0) ? mOrderScales[0] : mOrderScales[1]}; - pipeline.mAmbiSplitter[1][c].processHfScale(tmpspan, hfscale); + (splitter++)->processHfScale(tmpspan, hfscale); + hfscale = mOrderScales[1]; - MixSamples(tmpspan, samplesOut, pipeline.mLate.CurrentGains[c], - pipeline.mLate.TargetGains[c], todo, 0); + MixSamples(tmpspan, samplesOut, gains.Current, gains.Target, todo, 0); } } @@ -628,8 +688,6 @@ struct ReverbState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(ReverbState) }; /************************************** @@ -637,18 +695,13 @@ struct ReverbState final : public EffectState { **************************************/ inline float CalcDelayLengthMult(float density) -{ return maxf(5.0f, std::cbrt(density*DENSITY_SCALE)); } +{ return std::max(5.0f, std::cbrt(density*DENSITY_SCALE)); } /* Calculates the delay line metrics and allocates the shared sample buffer * for all lines given the sample rate (frequency). */ void ReverbState::allocLines(const float frequency) { - /* All delay line lengths are calculated to accommodate the full range of - * lengths given their respective parameters. - */ - size_t totalSamples{0u}; - /* Multiplier for the maximum density value, i.e. density=1, which is * actually the least density... */ @@ -658,64 +711,83 @@ void ReverbState::allocLines(const float frequency) * time and depth coefficient, and halfed for the low-to-high frequency * swing. */ - constexpr float max_mod_delay{MaxModulationTime*MODULATION_DEPTH_COEFF / 2.0f}; + static constexpr float max_mod_delay{MaxModulationTime*MODULATION_DEPTH_COEFF / 2.0f}; + std::array linelengths{}; + size_t oidx{0}; + + size_t totalSamples{0u}; + /* The main delay length includes the maximum early reflection delay and + * the largest early tap width. It must also be extended by the update size + * (BufferLineSize) for block processing. + */ + float length{ReverbMaxReflectionsDelay + EARLY_TAP_LENGTHS.back()*multiplier}; + size_t count{mMainDelay.calcLineLength(length, frequency, BufferLineSize)}; + linelengths[oidx++] = count; + totalSamples += count; for(auto &pipeline : mPipelines) { - /* The main delay length includes the maximum early reflection delay, - * the largest early tap width, the maximum late reverb delay, and the - * largest late tap width. Finally, it must also be extended by the - * update size (BufferLineSize) for block processing. - */ - float length{ReverbMaxReflectionsDelay + EARLY_TAP_LENGTHS.back()*multiplier}; - totalSamples += pipeline.mEarlyDelayIn.calcLineLength(length, totalSamples, frequency, - BufferLineSize); - - constexpr float LateLineDiffAvg{(LATE_LINE_LENGTHS.back()-LATE_LINE_LENGTHS.front()) / + static constexpr float LateDiffAvg{(LATE_LINE_LENGTHS.back()-LATE_LINE_LENGTHS.front()) / float{NUM_LINES}}; - length = ReverbMaxLateReverbDelay + LateLineDiffAvg*multiplier; - totalSamples += pipeline.mLateDelayIn.calcLineLength(length, totalSamples, frequency, - BufferLineSize); + length = ReverbMaxLateReverbDelay + LateDiffAvg*multiplier; + count = pipeline.mLateDelayIn.calcLineLength(length, frequency, BufferLineSize); + linelengths[oidx++] = count; + totalSamples += count; /* The early vector all-pass line. */ length = EARLY_ALLPASS_LENGTHS.back() * multiplier; - totalSamples += pipeline.mEarly.VecAp.Delay.calcLineLength(length, totalSamples, frequency, - 0); + count = pipeline.mEarly.VecAp.Delay.calcLineLength(length, frequency, 0); + linelengths[oidx++] = count; + totalSamples += count; /* The early reflection line. */ length = EARLY_LINE_LENGTHS.back() * multiplier; - totalSamples += pipeline.mEarly.Delay.calcLineLength(length, totalSamples, frequency, - MAX_UPDATE_SAMPLES); + count = pipeline.mEarly.Delay.calcLineLength(length, frequency, MAX_UPDATE_SAMPLES); + linelengths[oidx++] = count; + totalSamples += count; /* The late vector all-pass line. */ length = LATE_ALLPASS_LENGTHS.back() * multiplier; - totalSamples += pipeline.mLate.VecAp.Delay.calcLineLength(length, totalSamples, frequency, - 0); + count = pipeline.mLate.VecAp.Delay.calcLineLength(length, frequency, 0); + linelengths[oidx++] = count; + totalSamples += count; /* The late delay lines are calculated from the largest maximum density * line length, and the maximum modulation delay. Four additional * samples are needed for resampling the modulator delay. */ length = LATE_LINE_LENGTHS.back()*multiplier + max_mod_delay; - totalSamples += pipeline.mLate.Delay.calcLineLength(length, totalSamples, frequency, 4); + count = pipeline.mLate.Delay.calcLineLength(length, frequency, 4); + linelengths[oidx++] = count; + totalSamples += count; } + assert(oidx == linelengths.size()); if(totalSamples != mSampleBuffer.size()) decltype(mSampleBuffer)(totalSamples).swap(mSampleBuffer); /* Clear the sample buffer. */ - std::fill(mSampleBuffer.begin(), mSampleBuffer.end(), decltype(mSampleBuffer)::value_type{}); + std::fill(mSampleBuffer.begin(), mSampleBuffer.end(), 0.0f); /* Update all delays to reflect the new sample buffer. */ + auto bufferspan = al::span{mSampleBuffer}; + oidx = 0; + mMainDelay.realizeLineOffset(bufferspan.first(linelengths[oidx])); + bufferspan = bufferspan.subspan(linelengths[oidx++]); for(auto &pipeline : mPipelines) { - pipeline.mEarlyDelayIn.realizeLineOffset(mSampleBuffer.data()); - pipeline.mLateDelayIn.realizeLineOffset(mSampleBuffer.data()); - pipeline.mEarly.VecAp.Delay.realizeLineOffset(mSampleBuffer.data()); - pipeline.mEarly.Delay.realizeLineOffset(mSampleBuffer.data()); - pipeline.mLate.VecAp.Delay.realizeLineOffset(mSampleBuffer.data()); - pipeline.mLate.Delay.realizeLineOffset(mSampleBuffer.data()); + pipeline.mLateDelayIn.realizeLineOffset(bufferspan.first(linelengths[oidx])); + bufferspan = bufferspan.subspan(linelengths[oidx++]); + pipeline.mEarly.VecAp.Delay.realizeLineOffset(bufferspan.first(linelengths[oidx])); + bufferspan = bufferspan.subspan(linelengths[oidx++]); + pipeline.mEarly.Delay.realizeLineOffset(bufferspan.first(linelengths[oidx])); + bufferspan = bufferspan.subspan(linelengths[oidx++]); + pipeline.mLate.VecAp.Delay.realizeLineOffset(bufferspan.first(linelengths[oidx])); + bufferspan = bufferspan.subspan(linelengths[oidx++]); + pipeline.mLate.Delay.realizeLineOffset(bufferspan.first(linelengths[oidx])); + bufferspan = bufferspan.subspan(linelengths[oidx++]); } + assert(oidx == linelengths.size()); } void ReverbState::deviceUpdate(const DeviceBase *device, const BufferStorage*) @@ -725,41 +797,7 @@ void ReverbState::deviceUpdate(const DeviceBase *device, const BufferStorage*) /* Allocate the delay lines. */ allocLines(frequency); - for(auto &pipeline : mPipelines) - { - /* Clear filters and gain coefficients since the delay lines were all just - * cleared (if not reallocated). - */ - for(auto &filter : pipeline.mFilter) - { - filter.Lp.clear(); - filter.Hp.clear(); - } - - std::fill(std::begin(pipeline.mEarlyDelayCoeff),std::end(pipeline.mEarlyDelayCoeff), 0.0f); - std::fill(std::begin(pipeline.mEarlyDelayCoeff),std::end(pipeline.mEarlyDelayCoeff), 0.0f); - - pipeline.mLate.DensityGain = 0.0f; - for(auto &t60 : pipeline.mLate.T60) - { - t60.MidGain = 0.0f; - t60.HFFilter.clear(); - t60.LFFilter.clear(); - } - - pipeline.mLate.Mod.Index = 0; - pipeline.mLate.Mod.Step = 1; - pipeline.mLate.Mod.Depth = 0.0f; - - for(auto &gains : pipeline.mEarly.CurrentGains) - std::fill(std::begin(gains), std::end(gains), 0.0f); - for(auto &gains : pipeline.mEarly.TargetGains) - std::fill(std::begin(gains), std::end(gains), 0.0f); - for(auto &gains : pipeline.mLate.CurrentGains) - std::fill(std::begin(gains), std::end(gains), 0.0f); - for(auto &gains : pipeline.mLate.TargetGains) - std::fill(std::begin(gains), std::end(gains), 0.0f); - } + std::for_each(mPipelines.begin(), mPipelines.end(), std::mem_fn(&ReverbPipeline::clear)); mPipelineState = DeviceClear; /* Reset offset base. */ @@ -775,14 +813,14 @@ void ReverbState::deviceUpdate(const DeviceBase *device, const BufferStorage*) mUpmixOutput = false; mOrderScales.fill(1.0f); } - mPipelines[0].mAmbiSplitter[0][0].init(device->mXOverFreq / frequency); - for(auto &pipeline : mPipelines) + + auto splitter = BandSplitter{device->mXOverFreq / frequency}; + auto set_splitters = [&splitter](ReverbPipeline &pipeline) { - std::fill(pipeline.mAmbiSplitter[0].begin(), pipeline.mAmbiSplitter[0].end(), - pipeline.mAmbiSplitter[0][0]); - std::fill(pipeline.mAmbiSplitter[1].begin(), pipeline.mAmbiSplitter[1].end(), - pipeline.mAmbiSplitter[0][0]); - } + std::fill(pipeline.mAmbiSplitter[0].begin(), pipeline.mAmbiSplitter[0].end(), splitter); + std::fill(pipeline.mAmbiSplitter[1].begin(), pipeline.mAmbiSplitter[1].end(), splitter); + }; + std::for_each(mPipelines.begin(), mPipelines.end(), set_splitters); } /************************************** @@ -853,7 +891,7 @@ float CalcLimitedHfRatio(const float hfRatio, const float airAbsorptionGainHF, CalcDecayLength(airAbsorptionGainHF, decayTime)}; /* Using the limit calculated above, apply the upper bound to the HF ratio. */ - return minf(limitRatio, hfRatio); + return std::min(limitRatio, hfRatio); } @@ -909,7 +947,7 @@ void Modulation::updateModulator(float modTime, float modDepth, float frequency) * appropriate step size to generate an LFO, which will vary the feedback * delay over time. */ - Step = maxu(fastf2u(MOD_FRACONE / (frequency * modTime)), 1); + Step = std::max(fastf2u(MOD_FRACONE / (frequency * modTime)), 1u); /* The modulation depth effects the amount of frequency change over the * range of the sinus. It needs to be scaled by the modulation time so that @@ -982,7 +1020,7 @@ void LateReverb::updateLines(const float density_mult, const float diffusion, * includes one sample of delay. Reduce by one to compensate. */ length = LATE_LINE_LENGTHS[i] * density_mult; - Offset[i] = maxu(float2uint(length*frequency + 0.5f), 1u) - 1u; + Offset[i] = std::max(float2uint(length*frequency + 0.5f), 1u) - 1u; /* Approximate the absorption that the vector all-pass would exhibit * given the current diffusion so we don't have to process a full T60 @@ -999,8 +1037,8 @@ void LateReverb::updateLines(const float density_mult, const float diffusion, /* Update the offsets for the main effect delay line. */ -void ReverbPipeline::updateDelayLine(const float earlyDelay, const float lateDelay, - const float density_mult, const float decayTime, const float frequency) +void ReverbPipeline::updateDelayLine(const float gain, const float earlyDelay, + const float lateDelay, const float density_mult, const float decayTime, const float frequency) { /* Early reflection taps are decorrelated by means of an average room * reflection approximation described above the definition of the taps. @@ -1016,8 +1054,12 @@ void ReverbPipeline::updateDelayLine(const float earlyDelay, const float lateDel { float length{EARLY_TAP_LENGTHS[i]*density_mult}; mEarlyDelayTap[i][1] = float2uint((earlyDelay+length) * frequency); - mEarlyDelayCoeff[i] = CalcDecayCoeff(length, decayTime); + mEarlyDelayCoeff[i][1] = CalcDecayCoeff(length, decayTime) * gain; + /* 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; mLateDelayTap[i][1] = float2uint(length * frequency); @@ -1038,14 +1080,14 @@ std::array,4> GetTransformFromVector(const al::span norm{{vec[0], vec[1], vec[2]}}; float mag{std::sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2])}; if(mag > 1.0f) { const float scale{al::numbers::sqrt3_v / mag}; - norm[0] = vec[0] * -scale; - norm[1] = vec[1] * scale; - norm[2] = vec[2] * scale; + norm[0] *= -scale; + norm[1] *= scale; + norm[2] *= scale; mag = 1.0f; } else @@ -1054,9 +1096,9 @@ std::array,4> GetTransformFromVector(const al::span; - norm[1] = vec[1] * al::numbers::sqrt3_v; - norm[2] = vec[2] * al::numbers::sqrt3_v; + norm[0] *= -al::numbers::sqrt3_v; + norm[1] *= al::numbers::sqrt3_v; + norm[2] *= al::numbers::sqrt3_v; } return std::array,4>{{ @@ -1075,45 +1117,41 @@ void ReverbPipeline::update3DPanning(const al::span ReflectionsPa /* Create matrices that transform a B-Format signal according to the * panning vectors. */ - const std::array,4> earlymat{GetTransformFromVector(ReflectionsPan)}; - const std::array,4> latemat{GetTransformFromVector(LateReverbPan)}; + const auto earlymat = GetTransformFromVector(ReflectionsPan); + const auto latemat = GetTransformFromVector(LateReverbPan); - if(doUpmix) + const auto get_coeffs = [&] { - /* When upsampling, combine the early and late transforms with the - * first-order upsample matrix. This results in panning gains that - * apply the panning transform to first-order B-Format, which is then - * upsampled. - */ - auto mult_matrix = [](const al::span,4> mtx1) + if(doUpmix) { - auto&& mtx2 = AmbiScale::FirstOrderUp; - std::array,NUM_LINES> res{}; - - for(size_t i{0};i < mtx1[0].size();++i) + /* When upsampling, combine the early and late transforms with the + * first-order upsample matrix. This results in panning gains that + * apply the panning transform to first-order B-Format, which is + * then upsampled. + */ + auto mult_matrix = [](const al::span,4> mtx1) { - float *RESTRICT dst{res[i].data()}; - for(size_t k{0};k < mtx1.size();++k) + std::array,NUM_LINES> res{}; + const auto mtx2 = al::span{AmbiScale::FirstOrderUp}; + + for(size_t i{0};i < mtx1[0].size();++i) { - const float *RESTRICT src{mtx2[k].data()}; - const float a{mtx1[k][i]}; - for(size_t j{0};j < mtx2[0].size();++j) - dst[j] += a * src[j]; + const al::span dst{res[i]}; + static_assert(dst.size() >= std::tuple_size_v); + for(size_t k{0};k < mtx1.size();++k) + { + const float a{mtx1[k][i]}; + std::transform(mtx2[k].begin(), mtx2[k].end(), dst.begin(), dst.begin(), + [a](const float in, const float out) noexcept -> float + { return a*in + out; }); + } } - } - return res; - }; - auto earlycoeffs = mult_matrix(earlymat); - auto latecoeffs = mult_matrix(latemat); + return res; + }; + return std::array{mult_matrix(earlymat), mult_matrix(latemat)}; + } - for(size_t i{0u};i < NUM_LINES;i++) - ComputePanGains(mainMix, earlycoeffs[i].data(), earlyGain, mEarly.TargetGains[i]); - for(size_t i{0u};i < NUM_LINES;i++) - ComputePanGains(mainMix, latecoeffs[i].data(), lateGain, mLate.TargetGains[i]); - } - else - { /* When not upsampling, combine the early and late A-to-B-Format * conversions with their respective transform. This results panning * gains that convert A-Format to B-Format, which is then panned. @@ -1125,158 +1163,155 @@ void ReverbPipeline::update3DPanning(const al::span ReflectionsPa for(size_t i{0};i < mtx1[0].size();++i) { - float *RESTRICT dst{res[i].data()}; + const al::span dst{res[i]}; + static_assert(dst.size() >= std::tuple_size_v); for(size_t k{0};k < mtx1.size();++k) { const float a{mtx1[k][i]}; - for(size_t j{0};j < mtx2.size();++j) - dst[j] += a * mtx2[j][k]; + std::transform(mtx2[k].begin(), mtx2[k].end(), dst.begin(), dst.begin(), + [a](const float in, const float out) noexcept -> float + { return a*in + out; }); } } return res; }; - auto earlycoeffs = mult_matrix(EarlyA2B, earlymat); - auto latecoeffs = mult_matrix(LateA2B, latemat); - - for(size_t i{0u};i < NUM_LINES;i++) - ComputePanGains(mainMix, earlycoeffs[i].data(), earlyGain, mEarly.TargetGains[i]); - for(size_t i{0u};i < NUM_LINES;i++) - ComputePanGains(mainMix, latecoeffs[i].data(), lateGain, mLate.TargetGains[i]); - } + return std::array{mult_matrix(EarlyA2B, earlymat), mult_matrix(LateA2B, latemat)}; + }; + const auto [earlycoeffs, latecoeffs] = get_coeffs(); + + auto earlygains = mEarly.Gains.begin(); + for(auto &coeffs : earlycoeffs) + ComputePanGains(mainMix, coeffs, earlyGain, (earlygains++)->Target); + auto lategains = mLate.Gains.begin(); + for(auto &coeffs : latecoeffs) + ComputePanGains(mainMix, coeffs, lateGain, (lategains++)->Target); } void ReverbState::update(const ContextBase *Context, const EffectSlot *Slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get(*props_); const DeviceBase *Device{Context->mDevice}; const auto frequency = static_cast(Device->Frequency); /* If the HF limit parameter is flagged, calculate an appropriate limit * based on the air absorption parameter. */ - float hfRatio{props->Reverb.DecayHFRatio}; - if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f) - hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF, - props->Reverb.DecayTime); + float hfRatio{props.DecayHFRatio}; + if(props.DecayHFLimit && props.AirAbsorptionGainHF < 1.0f) + hfRatio = CalcLimitedHfRatio(hfRatio, props.AirAbsorptionGainHF, props.DecayTime); /* Calculate the LF/HF decay times. */ constexpr float MinDecayTime{0.1f}, MaxDecayTime{20.0f}; - const float lfDecayTime{clampf(props->Reverb.DecayTime*props->Reverb.DecayLFRatio, - MinDecayTime, MaxDecayTime)}; - const float hfDecayTime{clampf(props->Reverb.DecayTime*hfRatio, MinDecayTime, MaxDecayTime)}; + const float lfDecayTime{std::clamp(props.DecayTime*props.DecayLFRatio, MinDecayTime, + MaxDecayTime)}; + const float hfDecayTime{std::clamp(props.DecayTime*hfRatio, MinDecayTime, MaxDecayTime)}; /* Determine if a full update is required. */ const bool fullUpdate{mPipelineState == DeviceClear || /* Density is essentially a master control for the feedback delays, so * changes the offsets of many delay lines. */ - mParams.Density != props->Reverb.Density || + mParams.Density != props.Density || /* Diffusion and decay times influences the decay rate (gain) of the * late reverb T60 filter. */ - mParams.Diffusion != props->Reverb.Diffusion || - mParams.DecayTime != props->Reverb.DecayTime || + mParams.Diffusion != props.Diffusion || + mParams.DecayTime != props.DecayTime || mParams.HFDecayTime != hfDecayTime || mParams.LFDecayTime != lfDecayTime || /* Modulation time and depth both require fading the modulation delay. */ - mParams.ModulationTime != props->Reverb.ModulationTime || - mParams.ModulationDepth != props->Reverb.ModulationDepth || + mParams.ModulationTime != props.ModulationTime || + mParams.ModulationDepth != props.ModulationDepth || /* HF/LF References control the weighting used to calculate the density * gain. */ - mParams.HFReference != props->Reverb.HFReference || - mParams.LFReference != props->Reverb.LFReference}; + mParams.HFReference != props.HFReference || + mParams.LFReference != props.LFReference}; if(fullUpdate) { - mParams.Density = props->Reverb.Density; - mParams.Diffusion = props->Reverb.Diffusion; - mParams.DecayTime = props->Reverb.DecayTime; + mParams.Density = props.Density; + mParams.Diffusion = props.Diffusion; + mParams.DecayTime = props.DecayTime; mParams.HFDecayTime = hfDecayTime; mParams.LFDecayTime = lfDecayTime; - mParams.ModulationTime = props->Reverb.ModulationTime; - mParams.ModulationDepth = props->Reverb.ModulationDepth; - mParams.HFReference = props->Reverb.HFReference; - mParams.LFReference = props->Reverb.LFReference; + mParams.ModulationTime = props.ModulationTime; + mParams.ModulationDepth = props.ModulationDepth; + mParams.HFReference = props.HFReference; + mParams.LFReference = props.LFReference; mPipelineState = (mPipelineState != DeviceClear) ? StartFade : Normal; - mCurrentPipeline ^= 1; + mCurrentPipeline = !mCurrentPipeline; + + auto &oldpipeline = mPipelines[!mCurrentPipeline]; + for(size_t j{0};j < NUM_LINES;++j) + oldpipeline.mEarlyDelayCoeff[j][1] = 0.0f; } auto &pipeline = mPipelines[mCurrentPipeline]; + /* The density-based room size (delay length) multiplier. */ + const float density_mult{CalcDelayLengthMult(props.Density)}; + + /* Update the main effect delay and associated taps. */ + pipeline.updateDelayLine(props.Gain, props.ReflectionsDelay, props.LateReverbDelay, + density_mult, props.DecayTime, frequency); + /* Update early and late 3D panning. */ mOutTarget = target.Main->Buffer; - const float gain{props->Reverb.Gain * Slot->Gain * ReverbBoost}; - pipeline.update3DPanning(props->Reverb.ReflectionsPan, props->Reverb.LateReverbPan, - props->Reverb.ReflectionsGain*gain, props->Reverb.LateReverbGain*gain, mUpmixOutput, - target.Main); + const float gain{Slot->Gain * ReverbBoost}; + pipeline.update3DPanning(props.ReflectionsPan, props.LateReverbPan, props.ReflectionsGain*gain, + props.LateReverbGain*gain, mUpmixOutput, target.Main); /* Calculate the master filters */ - float hf0norm{minf(props->Reverb.HFReference/frequency, 0.49f)}; - pipeline.mFilter[0].Lp.setParamsFromSlope(BiquadType::HighShelf, hf0norm, props->Reverb.GainHF, 1.0f); - float lf0norm{minf(props->Reverb.LFReference/frequency, 0.49f)}; - pipeline.mFilter[0].Hp.setParamsFromSlope(BiquadType::LowShelf, lf0norm, props->Reverb.GainLF, 1.0f); + float hf0norm{std::min(props.HFReference/frequency, 0.49f)}; + pipeline.mFilter[0].Lp.setParamsFromSlope(BiquadType::HighShelf, hf0norm, props.GainHF, 1.0f); + float lf0norm{std::min(props.LFReference/frequency, 0.49f)}; + pipeline.mFilter[0].Hp.setParamsFromSlope(BiquadType::LowShelf, lf0norm, props.GainLF, 1.0f); for(size_t i{1u};i < NUM_LINES;i++) { pipeline.mFilter[i].Lp.copyParamsFrom(pipeline.mFilter[0].Lp); pipeline.mFilter[i].Hp.copyParamsFrom(pipeline.mFilter[0].Hp); } - /* The density-based room size (delay length) multiplier. */ - const float density_mult{CalcDelayLengthMult(props->Reverb.Density)}; - - /* Update the main effect delay and associated taps. */ - pipeline.updateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay, - density_mult, props->Reverb.DecayTime, frequency); - if(fullUpdate) { /* Update the early lines. */ - pipeline.mEarly.updateLines(density_mult, props->Reverb.Diffusion, props->Reverb.DecayTime, - frequency); + pipeline.mEarly.updateLines(density_mult, props.Diffusion, props.DecayTime, frequency); /* Get the mixing matrix coefficients. */ - CalcMatrixCoeffs(props->Reverb.Diffusion, &pipeline.mMixX, &pipeline.mMixY); + CalcMatrixCoeffs(props.Diffusion, &pipeline.mMixX, &pipeline.mMixY); /* Update the modulator rate and depth. */ - pipeline.mLate.Mod.updateModulator(props->Reverb.ModulationTime, - props->Reverb.ModulationDepth, frequency); + pipeline.mLate.Mod.updateModulator(props.ModulationTime, props.ModulationDepth, frequency); /* Update the late lines. */ - pipeline.mLate.updateLines(density_mult, props->Reverb.Diffusion, lfDecayTime, - props->Reverb.DecayTime, hfDecayTime, lf0norm, hf0norm, frequency); + pipeline.mLate.updateLines(density_mult, props.Diffusion, lfDecayTime, props.DecayTime, + hfDecayTime, lf0norm, hf0norm, frequency); } /* 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 decayBase{props.ReflectionsGain * props.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}; + /* 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. + * + * Otherwise, if the late reverb already starts at -60dB or less, only + * include the time to get to the late reverb. + */ + const float diffTime{!(decayDiff < 1.0f) ? 0.0f + : (std::log10(decayDiff)*(20.0f / -60.0f) * props.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(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(minf(decaySamples, 100'000.0f)); - } + const float decaySamples{(props.ReflectionsDelay+props.LateReverbDelay+diffTime) + * frequency}; + /* Limit to 100,000 samples (a touch over 2 seconds at 48khz) to avoid + * excessive double-processing. + */ + pipeline.mFadeSampleCount = static_cast(std::min(decaySamples, 100'000.0f)); } @@ -1322,35 +1357,34 @@ void ReverbState::update(const ContextBase *Context, const EffectSlot *Slot, * Where D is a diagonal matrix (of x), and S is a triangular matrix (of y) * whose combination of signs are being iterated. */ -inline auto VectorPartialScatter(const std::array &RESTRICT in, - const float xCoeff, const float yCoeff) -> std::array +inline auto VectorPartialScatter(const std::array &in, const float xCoeff, + const float yCoeff) noexcept -> std::array { - return std::array{{ + return std::array{ xCoeff*in[0] + yCoeff*( in[1] + -in[2] + in[3]), xCoeff*in[1] + yCoeff*(-in[0] + in[2] + in[3]), xCoeff*in[2] + yCoeff*( in[0] + -in[1] + in[3]), xCoeff*in[3] + yCoeff*(-in[0] + -in[1] + -in[2] ) - }}; + }; } -/* Utilizes the above, but reverses the input channels. */ -void VectorScatterRevDelayIn(const DelayLineI delay, size_t offset, const float xCoeff, - const float yCoeff, const al::span in, const size_t count) +/* Utilizes the above, but also applies a line-based reflection on the input + * channels (swapping 0<->3 and 1<->2). + */ +void VectorScatterRev(const float xCoeff, const float yCoeff, + const al::span samples, const size_t count) noexcept { ASSUME(count > 0); - for(size_t i{0u};i < count;) + for(size_t i{0u};i < count;++i) { - offset &= delay.Mask; - size_t td{minz(delay.Mask+1 - offset, count-i)}; - do { - std::array f; - for(size_t j{0u};j < NUM_LINES;j++) - f[NUM_LINES-1-j] = in[j][i]; - ++i; + std::array src{samples[0][i], samples[1][i], samples[2][i], samples[3][i]}; - delay.Line[offset++] = VectorPartialScatter(f, xCoeff, yCoeff); - } while(--td); + src = VectorPartialScatter(std::array{src[3], src[2], src[1], src[0]}, xCoeff, yCoeff); + samples[0][i] = src[0]; + samples[1][i] = src[1]; + samples[2][i] = src[2]; + samples[3][i] = src[3]; } } @@ -1360,167 +1394,229 @@ 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 samples, size_t offset, - const float xCoeff, const float yCoeff, const size_t todo) +void VecAllpass::process(const al::span samples, size_t main_offset, + const float xCoeff, const float yCoeff, const size_t todo) const noexcept { - const DelayLineI delay{Delay}; + const auto linelen = size_t{Delay.mLine.size()/NUM_LINES}; const float feedCoeff{Coeff}; ASSUME(todo > 0); - size_t vap_offset[NUM_LINES]; - for(size_t j{0u};j < NUM_LINES;j++) - vap_offset[j] = offset - Offset[j]; for(size_t i{0u};i < todo;) { - for(size_t j{0u};j < NUM_LINES;j++) - vap_offset[j] &= delay.Mask; - offset &= delay.Mask; + std::array vap_offset{}; + std::transform(Offset.cbegin(), Offset.cend(), vap_offset.begin(), + [main_offset,mask=linelen-1](const size_t delay) noexcept -> size_t + { return (main_offset-delay) & mask; }); + main_offset &= linelen-1; - size_t maxoff{offset}; - for(size_t j{0u};j < NUM_LINES;j++) - maxoff = maxz(maxoff, vap_offset[j]); - size_t td{minz(delay.Mask+1 - maxoff, todo - i)}; + const auto maxoff = std::accumulate(vap_offset.cbegin(), vap_offset.cend(), main_offset, + [](const size_t offset, const size_t apoffset) { return std::max(offset, apoffset); }); + size_t td{std::min(linelen - maxoff, todo - i)}; + + auto delayIn = Delay.mLine.begin(); + auto delayOut = Delay.mLine.begin() + ptrdiff_t(main_offset*NUM_LINES); + main_offset += td; do { - std::array f; + std::array f{}; for(size_t j{0u};j < NUM_LINES;j++) { const float input{samples[j][i]}; - const float out{delay.Line[vap_offset[j]++][j] - feedCoeff*input}; + const float out{delayIn[vap_offset[j]*NUM_LINES + j] - feedCoeff*input}; f[j] = input + feedCoeff*out; samples[j][i] = out; } + delayIn += NUM_LINES; ++i; - delay.Line[offset++] = VectorPartialScatter(f, xCoeff, yCoeff); + f = VectorPartialScatter(f, xCoeff, yCoeff); + delayOut = std::copy_n(f.cbegin(), f.size(), delayOut); } while(--td); } } +/* This applies a more typical all-pass to each line, without the scattering + * matrix. + */ +void Allpass4::process(const al::span samples, const size_t offset, + const size_t todo) const noexcept +{ + const DelayLineU delay{Delay}; + const float feedCoeff{Coeff}; + + ASSUME(todo > 0); + + for(size_t j{0u};j < NUM_LINES;j++) + { + auto smpiter = samples[j].begin(); + const auto buffer = delay.get(j); + size_t dstoffset{offset}; + size_t vap_offset{offset - Offset[j]}; + for(size_t i{0u};i < todo;) + { + vap_offset &= buffer.size()-1; + dstoffset &= buffer.size()-1; + + const size_t maxoff{std::max(dstoffset, vap_offset)}; + const size_t td{std::min(buffer.size() - maxoff, todo - i)}; + + auto proc_sample = [buffer,feedCoeff,&vap_offset,&dstoffset](const float x) -> float + { + const float y{buffer[vap_offset++] - feedCoeff*x}; + buffer[dstoffset++] = x + feedCoeff*y; + return y; + }; + smpiter = std::transform(smpiter, smpiter+td, smpiter, proc_sample); + i += td; + } + } +} + + /* This generates early reflections. * * This is done by obtaining the primary reflections (those arriving from the * same direction as the source) from the main delay line. These are * attenuated and all-pass filtered (based on the diffusion parameter). * - * The early lines are then fed in reverse (according to the approximately - * opposite spatial location of the A-Format lines) to create the secondary + * 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, - const al::span tempSamples, +void ReverbPipeline::processEarly(const DelayLineU &main_delay, size_t offset, + const size_t samplesToDo, const al::span tempSamples, const al::span outSamples) { - const DelayLineI early_delay{mEarly.Delay}; - const DelayLineI in_delay{mEarlyDelayIn}; + const DelayLineU early_delay{mEarly.Delay}; + const DelayLineU in_delay{main_delay}; const float mixX{mMixX}; const float mixY{mMixY}; - ASSUME(samplesToDo > 0); + ASSUME(samplesToDo <= BufferLineSize); for(size_t base{0};base < samplesToDo;) { - const size_t todo{minz(samplesToDo-base, MAX_UPDATE_SAMPLES)}; + const size_t todo{std::min(samplesToDo-base, MAX_UPDATE_SAMPLES)}; /* First, load decorrelated samples from the main delay line as the * primary reflections. */ - const float fadeStep{1.0f / static_cast(todo)}; - for(size_t j{0u};j < NUM_LINES;j++) + const auto fadeStep = 1.0f / static_cast(todo); + for(size_t j{0_uz};j < NUM_LINES;j++) { - size_t early_delay_tap0{offset - mEarlyDelayTap[j][0]}; - size_t early_delay_tap1{offset - mEarlyDelayTap[j][1]}; - const float coeff{mEarlyDelayCoeff[j]}; - const float coeffStep{early_delay_tap0 != early_delay_tap1 ? coeff*fadeStep : 0.0f}; - float fadeCount{0.0f}; + const auto input = in_delay.get(j); + auto early_delay_tap0 = size_t{offset - mEarlyDelayTap[j][0]}; + auto early_delay_tap1 = size_t{offset - mEarlyDelayTap[j][1]}; + mEarlyDelayTap[j][0] = mEarlyDelayTap[j][1]; + const auto coeff0 = float{mEarlyDelayCoeff[j][0]}; + const auto coeff1 = float{mEarlyDelayCoeff[j][1]}; + mEarlyDelayCoeff[j][0] = mEarlyDelayCoeff[j][1]; + auto fadeCount = 0.0f; - for(size_t i{0u};i < todo;) + auto tmp = tempSamples[j].begin(); + for(size_t i{0_uz};i < todo;) { - early_delay_tap0 &= in_delay.Mask; - early_delay_tap1 &= in_delay.Mask; - const size_t max_tap{maxz(early_delay_tap0, early_delay_tap1)}; - size_t td{minz(in_delay.Mask+1 - max_tap, todo-i)}; - do { - const float fade0{coeff - coeffStep*fadeCount}; - const float fade1{coeffStep*fadeCount}; + early_delay_tap0 &= input.size()-1; + early_delay_tap1 &= input.size()-1; + const auto max_tap = size_t{std::max(early_delay_tap0, early_delay_tap1)}; + const auto td = size_t{std::min(input.size()-max_tap, todo-i)}; + const auto intap0 = input.subspan(early_delay_tap0, td); + const auto intap1 = input.subspan(early_delay_tap1, td); + + auto do_blend = [coeff0,coeff1,fadeStep,&fadeCount](const float in0, + const float in1) noexcept -> float + { + const auto ret = lerpf(in0*coeff0, in1*coeff1, fadeStep*fadeCount); fadeCount += 1.0f; - tempSamples[j][i++] = in_delay.Line[early_delay_tap0++][j]*fade0 + - in_delay.Line[early_delay_tap1++][j]*fade1; - } while(--td); + return ret; + }; + tmp = std::transform(intap0.begin(), intap0.end(), intap1.begin(), tmp, do_blend); + early_delay_tap0 += td; + early_delay_tap1 += td; + i += td; } - mEarlyDelayTap[j][0] = mEarlyDelayTap[j][1]; + /* Band-pass the incoming samples. */ + auto&& filter = DualBiquad{mFilter[j].Lp, mFilter[j].Hp}; + filter.process(al::span{tempSamples[j]}.first(todo), tempSamples[j]); } - /* Apply a vector all-pass, to help color the initial reflections based - * on the diffusion strength. - */ - mEarly.VecAp.process(tempSamples, offset, mixX, mixY, todo); + /* Apply an all-pass, to help color the initial reflections. */ + mEarly.VecAp.process(tempSamples, offset, todo); - /* 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); - for(size_t j{0u};j < NUM_LINES;j++) + /* Apply a delay and bounce to generate secondary reflections. */ + early_delay.writeReflected(offset, tempSamples, todo); + for(size_t j{0_uz};j < NUM_LINES;j++) { - size_t feedb_tap{offset - mEarly.Offset[j]}; - const float feedb_coeff{mEarly.Coeff[j]}; - float *RESTRICT out{al::assume_aligned<16>(outSamples[j].data() + base)}; + const auto input = early_delay.get(j); + auto feedb_tap = size_t{offset - mEarly.Offset[j]}; + const auto feedb_coeff = float{mEarly.Coeff[j]}; + auto out = outSamples[j].begin() + base; + auto tmp = tempSamples[j].begin(); - for(size_t i{0u};i < todo;) + for(size_t i{0_uz};i < todo;) { - 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]; - ++i; - } while(--td); + feedb_tap &= input.size()-1; + + const auto td = size_t{std::min(input.size() - feedb_tap, todo - i)}; + const auto delaySrc = input.subspan(feedb_tap, td); + + /* Combine the main input with the attenuated delayed echo for + * the early output. + */ + out = std::transform(delaySrc.begin(), delaySrc.end(), tmp, out, + [feedb_coeff](const float delayspl, const float mainspl) noexcept -> float + { return delayspl*feedb_coeff + mainspl; }); + + /* Move the (non-attenuated) delayed echo to the temp buffer + * for feeding the late reverb. + */ + tmp = std::copy_n(delaySrc.begin(), delaySrc.size(), tmp); + feedb_tap += td; + i += td; } } - /* Finally, write the result to the late delay line input for the late - * reverb stage to pick up at the appropriate time, applying a scatter - * and bounce to improve the initial diffusion in the late reverb. + /* Finally, apply a scatter and bounce to improve the initial diffusion + * in the late reverb, writing the result to the late delay line input. */ - VectorScatterRevDelayIn(mLateDelayIn, offset, mixX, mixY, tempSamples, todo); + VectorScatterRev(mixX, mixY, tempSamples, todo); + for(size_t j{0_uz};j < NUM_LINES;j++) + mLateDelayIn.write(offset, j, al::span{tempSamples[j]}.first(todo)); base += todo; offset += todo; } } -void Modulation::calcDelays(size_t todo) +auto Modulation::calcDelays(size_t todo) -> al::span { - uint idx{Index}; - const uint step{Step}; - const float depth{Depth}; - for(size_t i{0};i < todo;++i) + auto idx = Index; + const auto step = Step; + const auto depth = Depth * float{gCubicTable.sTableSteps}; + const auto delays = al::span{ModDelays}.first(todo); + std::generate(delays.begin(), delays.end(), [step,depth,&idx] { idx += step; - const float x{static_cast(idx&MOD_FRACMASK) * (1.0f/MOD_FRACONE)}; + const auto x = static_cast(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)) + const auto 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; - } + : ((16.0f * x * x) + (-8.0f * x) + (-16.0f * x) + 8.0f); + return float2uint((lfo+1.0f) * depth); + }); Index = idx; + return delays; } @@ -1539,88 +1635,105 @@ void ReverbPipeline::processLate(size_t offset, const size_t samplesToDo, const al::span tempSamples, const al::span outSamples) { - const DelayLineI late_delay{mLate.Delay}; - const DelayLineI in_delay{mLateDelayIn}; + const DelayLineU late_delay{mLate.Delay}; + const DelayLineU in_delay{mLateDelayIn}; const float mixX{mMixX}; const float mixY{mMixY}; - ASSUME(samplesToDo > 0); + ASSUME(samplesToDo <= BufferLineSize); for(size_t base{0};base < samplesToDo;) { - const size_t todo{minz(samplesToDo-base, minz(mLate.Offset[0], MAX_UPDATE_SAMPLES))}; + const size_t todo{std::min(std::min(mLate.Offset[0], MAX_UPDATE_SAMPLES), + samplesToDo-base)}; ASSUME(todo > 0); /* First, calculate the modulated delays for the late feedback. */ - mLate.Mod.calcDelays(todo); + const auto delays = mLate.Mod.calcDelays(todo); - /* Next, load decorrelated samples from the main and feedback delay - * lines. Filter the signal to apply its frequency-dependent decay. + /* Now load samples from the feedback delay lines. Filter the signal to + * apply its frequency-dependent decay. */ + for(size_t j{0_uz};j < NUM_LINES;++j) + { + const auto input = late_delay.get(j); + const auto midGain = mLate.T60[j].MidGain; + auto late_feedb_tap = size_t{offset - mLate.Offset[j]}; + + auto proc_sample = [input,midGain,&late_feedb_tap](const size_t idelay) -> float + { + /* Calculate the read sample offset and sub-sample offset + * between it and the next sample. + */ + const auto delay = late_feedb_tap - (idelay>>gCubicTable.sTableBits); + const auto delayoffset = size_t{idelay & gCubicTable.sTableMask}; + ++late_feedb_tap; + + /* Get the samples around the delayed offset, interpolated for + * output. + */ + const auto out0 = float{input[(delay ) & (input.size()-1)]}; + const auto out1 = float{input[(delay-1) & (input.size()-1)]}; + const auto out2 = float{input[(delay-2) & (input.size()-1)]}; + const auto out3 = float{input[(delay-3) & (input.size()-1)]}; + + const auto out = out0*gCubicTable.getCoeff0(delayoffset) + + out1*gCubicTable.getCoeff1(delayoffset) + + out2*gCubicTable.getCoeff2(delayoffset) + + out3*gCubicTable.getCoeff3(delayoffset); + return out * midGain; + }; + std::transform(delays.begin(), delays.end(), tempSamples[j].begin(), proc_sample); + + mLate.T60[j].process(al::span{tempSamples[j]}.first(todo)); + } + + /* Next load decorrelated samples from the main delay lines. */ const float fadeStep{1.0f / static_cast(todo)}; - for(size_t j{0u};j < NUM_LINES;j++) + for(size_t j{0_uz};j < NUM_LINES;++j) { - size_t late_delay_tap0{offset - mLateDelayTap[j][0]}; - size_t late_delay_tap1{offset - mLateDelayTap[j][1]}; - size_t late_feedb_tap{offset - mLate.Offset[j]}; - const float midGain{mLate.T60[j].MidGain}; - const float densityGain{mLate.DensityGain * midGain}; - const float densityStep{late_delay_tap0 != late_delay_tap1 ? - densityGain*fadeStep : 0.0f}; - float fadeCount{0.0f}; + const auto input = in_delay.get(j); + auto late_delay_tap0 = size_t{offset - mLateDelayTap[j][0]}; + auto late_delay_tap1 = size_t{offset - mLateDelayTap[j][1]}; + mLateDelayTap[j][0] = mLateDelayTap[j][1]; + const auto densityGain = mLate.DensityGain; + const auto densityStep = late_delay_tap0 != late_delay_tap1 + ? densityGain*fadeStep : 0.0f; + auto fadeCount = 0.0f; + auto samples = tempSamples[j].begin(); for(size_t i{0u};i < todo;) { - late_delay_tap0 &= in_delay.Mask; - late_delay_tap1 &= in_delay.Mask; - size_t td{minz(todo-i, in_delay.Mask+1 - maxz(late_delay_tap0, late_delay_tap1))}; - do { - /* Calculate the read offset and offset between it and the - * next sample. - */ - const float fdelay{mLate.Mod.ModDelays[i]}; - const size_t idelay{float2uint(fdelay * float{gCubicTable.sTableSteps})}; - const size_t delay{late_feedb_tap - (idelay>>gCubicTable.sTableBits)}; - const size_t delayoffset{idelay & gCubicTable.sTableMask}; - ++late_feedb_tap; - - /* Get the samples around by the delayed offset. */ - const float out0{late_delay.Line[(delay ) & late_delay.Mask][j]}; - const float out1{late_delay.Line[(delay-1) & late_delay.Mask][j]}; - const float out2{late_delay.Line[(delay-2) & late_delay.Mask][j]}; - const float out3{late_delay.Line[(delay-3) & late_delay.Mask][j]}; - - /* The output is obtained by interpolating the four samples - * that were acquired above, and combined with the main - * delay tap. - */ - const float out{out0*gCubicTable.getCoeff0(delayoffset) - + out1*gCubicTable.getCoeff1(delayoffset) - + out2*gCubicTable.getCoeff2(delayoffset) - + out3*gCubicTable.getCoeff3(delayoffset)}; - const float fade0{densityGain - densityStep*fadeCount}; - const float fade1{densityStep*fadeCount}; + late_delay_tap0 &= input.size()-1; + late_delay_tap1 &= input.size()-1; + const auto td = size_t{std::min(todo - i, + input.size() - std::max(late_delay_tap0, late_delay_tap1))}; + + auto proc_sample = [input,densityGain,densityStep,&late_delay_tap0, + &late_delay_tap1,&fadeCount](const float sample) noexcept -> float + { + const auto fade0 = float{densityGain - densityStep*fadeCount}; + const auto fade1 = float{densityStep*fadeCount}; fadeCount += 1.0f; - tempSamples[j][i] = out*midGain + - in_delay.Line[late_delay_tap0++][j]*fade0 + - in_delay.Line[late_delay_tap1++][j]*fade1; - ++i; - } while(--td); + return input[late_delay_tap0++]*fade0 + input[late_delay_tap1++]*fade1 + + sample; + }; + samples = std::transform(samples, samples+ptrdiff_t(td), samples, proc_sample); + i += td; } - mLateDelayTap[j][0] = mLateDelayTap[j][1]; - - mLate.T60[j].process({tempSamples[j].data(), todo}); } /* Apply a vector all-pass to improve micro-surface diffusion, and * write out the results for mixing. */ mLate.VecAp.process(tempSamples, offset, mixX, mixY, todo); - for(size_t j{0u};j < NUM_LINES;j++) + for(size_t j{0_uz};j < NUM_LINES;++j) std::copy_n(tempSamples[j].begin(), todo, outSamples[j].begin()+base); /* Finally, scatter and bounce the results to refeed the feedback buffer. */ - VectorScatterRevDelayIn(late_delay, offset, mixX, mixY, tempSamples, todo); + VectorScatterRev(mixX, mixY, tempSamples, todo); + for(size_t j{0_uz};j < NUM_LINES;++j) + late_delay.write(offset, j, al::span{tempSamples[j]}.first(todo)); base += todo; offset += todo; @@ -1631,110 +1744,34 @@ void ReverbState::process(const size_t samplesToDo, const al::span 0); + ASSUME(samplesToDo <= BufferLineSize); - auto &oldpipeline = mPipelines[mCurrentPipeline^1]; + auto &oldpipeline = mPipelines[!mCurrentPipeline]; auto &pipeline = mPipelines[mCurrentPipeline]; - if(mPipelineState >= Fading) + /* Convert B-Format to A-Format for processing. */ + const size_t numInput{std::min(samplesIn.size(), NUM_LINES)}; + const al::span tmpspan{al::assume_aligned<16>(mTempLine.data()), samplesToDo}; + for(size_t c{0u};c < NUM_LINES;++c) { - /* Convert B-Format to A-Format for processing. */ - const size_t numInput{minz(samplesIn.size(), NUM_LINES)}; - const al::span tmpspan{al::assume_aligned<16>(mTempLine.data()), samplesToDo}; - for(size_t c{0u};c < NUM_LINES;c++) + std::fill(tmpspan.begin(), tmpspan.end(), 0.0f); + for(size_t i{0};i < numInput;++i) { - std::fill(tmpspan.begin(), tmpspan.end(), 0.0f); - for(size_t i{0};i < numInput;++i) - { - const float gain{B2A[c][i]}; - const float *RESTRICT input{al::assume_aligned<16>(samplesIn[i].data())}; - - auto mix_sample = [gain](const float sample, const float in) noexcept -> float - { return sample + in*gain; }; - std::transform(tmpspan.begin(), tmpspan.end(), input, tmpspan.begin(), - mix_sample); - } + const float gain{B2A[c][i]}; - /* Band-pass the incoming samples and feed the initial delay line. */ - auto&& filter = DualBiquad{pipeline.mFilter[c].Lp, pipeline.mFilter[c].Hp}; - filter.process(tmpspan, tmpspan.data()); - pipeline.mEarlyDelayIn.write(offset, c, tmpspan.cbegin(), samplesToDo); + auto mix_sample = [gain](const float sample, const float in) noexcept -> float + { return sample + in*gain; }; + std::transform(tmpspan.begin(), tmpspan.end(), samplesIn[i].begin(), tmpspan.begin(), + mix_sample); } - if(mPipelineState == Fading) - { - /* Give the old pipeline silence if it's still fading out. */ - for(size_t c{0u};c < NUM_LINES;c++) - { - std::fill(tmpspan.begin(), tmpspan.end(), 0.0f); - auto&& filter = DualBiquad{oldpipeline.mFilter[c].Lp, oldpipeline.mFilter[c].Hp}; - filter.process(tmpspan, tmpspan.data()); - oldpipeline.mEarlyDelayIn.write(offset, c, tmpspan.cbegin(), samplesToDo); - } - } + mMainDelay.write(offset, c, tmpspan); } - else - { - /* At the start of a fade, fade in input for the current pipeline, and - * fade out input for the old pipeline. - */ - const size_t numInput{minz(samplesIn.size(), NUM_LINES)}; - const al::span tmpspan{al::assume_aligned<16>(mTempLine.data()), samplesToDo}; - const float fadeStep{1.0f / static_cast(samplesToDo)}; - - for(size_t c{0u};c < NUM_LINES;c++) - { - std::fill(tmpspan.begin(), tmpspan.end(), 0.0f); - for(size_t i{0};i < numInput;++i) - { - const float gain{B2A[c][i]}; - const float *RESTRICT input{al::assume_aligned<16>(samplesIn[i].data())}; - - auto mix_sample = [gain](const float sample, const float in) noexcept -> float - { return sample + in*gain; }; - std::transform(tmpspan.begin(), tmpspan.end(), input, tmpspan.begin(), - mix_sample); - } - float stepCount{0.0f}; - for(float &sample : tmpspan) - { - stepCount += 1.0f; - sample *= stepCount*fadeStep; - } - - auto&& filter = DualBiquad{pipeline.mFilter[c].Lp, pipeline.mFilter[c].Hp}; - filter.process(tmpspan, tmpspan.data()); - pipeline.mEarlyDelayIn.write(offset, c, tmpspan.cbegin(), samplesToDo); - } - for(size_t c{0u};c < NUM_LINES;c++) - { - std::fill(tmpspan.begin(), tmpspan.end(), 0.0f); - for(size_t i{0};i < numInput;++i) - { - const float gain{B2A[c][i]}; - const float *RESTRICT input{al::assume_aligned<16>(samplesIn[i].data())}; - auto mix_sample = [gain](const float sample, const float in) noexcept -> float - { return sample + in*gain; }; - std::transform(tmpspan.begin(), tmpspan.end(), input, tmpspan.begin(), - mix_sample); - } - float stepCount{0.0f}; - for(float &sample : tmpspan) - { - stepCount += 1.0f; - sample *= 1.0f - stepCount*fadeStep; - } - - auto&& filter = DualBiquad{oldpipeline.mFilter[c].Lp, oldpipeline.mFilter[c].Hp}; - filter.process(tmpspan, tmpspan.data()); - oldpipeline.mEarlyDelayIn.write(offset, c, tmpspan.cbegin(), samplesToDo); - } - mPipelineState = Fading; - } + mPipelineState = std::max(Fading, mPipelineState); /* Process reverb for these samples. and mix them to the output. */ - pipeline.processEarly(offset, samplesToDo, mTempSamples, mEarlySamples); + pipeline.processEarly(mMainDelay, offset, samplesToDo, mTempSamples, mEarlySamples); pipeline.processLate(offset, samplesToDo, mTempSamples, mLateSamples); mixOut(pipeline, samplesOut, samplesToDo); @@ -1743,9 +1780,9 @@ void ReverbState::process(const size_t samplesToDo, const al::span= oldpipeline.mFadeSampleCount) { - for(auto &gains : oldpipeline.mEarly.TargetGains) - std::fill(std::begin(gains), std::end(gains), 0.0f); - for(auto &gains : oldpipeline.mLate.TargetGains) - std::fill(std::begin(gains), std::end(gains), 0.0f); + for(auto &gains : oldpipeline.mEarly.Gains) + std::fill(gains.Target.begin(), gains.Target.end(), 0.0f); + for(auto &gains : oldpipeline.mLate.Gains) + std::fill(gains.Target.begin(), gains.Target.end(), 0.0f); oldpipeline.mFadeSampleCount = 0; mPipelineState = Cleanup; } @@ -1770,7 +1807,7 @@ void ReverbState::process(const size_t samplesToDo, const al::span{new ReverbState{}}; } }; -struct StdReverbStateFactory final : public EffectStateFactory { - al::intrusive_ptr create() override - { return al::intrusive_ptr{new ReverbState{}}; } -}; - } // namespace EffectStateFactory *ReverbStateFactory_getFactory() @@ -1797,9 +1829,3 @@ EffectStateFactory *ReverbStateFactory_getFactory() static ReverbStateFactory ReverbFactory{}; return &ReverbFactory; } - -EffectStateFactory *StdReverbStateFactory_getFactory() -{ - static StdReverbStateFactory ReverbFactory{}; - return &ReverbFactory; -} diff --git a/3rdparty/openal/alc/effects/vmorpher.cpp b/3rdparty/openal/alc/effects/vmorpher.cpp index 872c7add1698..07b2257e5b36 100644 --- a/3rdparty/openal/alc/effects/vmorpher.cpp +++ b/3rdparty/openal/alc/effects/vmorpher.cpp @@ -34,68 +34,69 @@ #include #include +#include #include #include -#include +#include #include "alc/effects/base.h" -#include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" #include "core/ambidefs.h" #include "core/bufferline.h" #include "core/context.h" -#include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/mixer.h" #include "intrusive_ptr.h" +struct BufferStorage; namespace { using uint = unsigned int; -#define MAX_UPDATE_SAMPLES 256 -#define NUM_FORMANTS 4 -#define NUM_FILTERS 2 -#define Q_FACTOR 5.0f - -#define VOWEL_A_INDEX 0 -#define VOWEL_B_INDEX 1 +constexpr size_t MaxUpdateSamples{256}; +constexpr size_t NumFormants{4}; +constexpr float RcpQFactor{1.0f / 5.0f}; +enum : size_t { + VowelAIndex, + VowelBIndex, + NumFilters +}; -#define WAVEFORM_FRACBITS 24 -#define WAVEFORM_FRACONE (1<*2.0f / WAVEFORM_FRACONE}; + constexpr float scale{al::numbers::pi_v*2.0f / float{WaveformFracOne}}; return std::sin(static_cast(index) * scale)*0.5f + 0.5f; } inline float Saw(uint index) -{ return static_cast(index) / float{WAVEFORM_FRACONE}; } +{ return static_cast(index) / float{WaveformFracOne}; } inline float Triangle(uint index) -{ return std::fabs(static_cast(index)*(2.0f/WAVEFORM_FRACONE) - 1.0f); } +{ return std::fabs(static_cast(index)*(2.0f/WaveformFracOne) - 1.0f); } inline float Half(uint) { return 0.5f; } template -void Oscillate(float *RESTRICT dst, uint index, const uint step, size_t todo) +void Oscillate(const al::span dst, uint index, const uint step) { - for(size_t i{0u};i < todo;i++) + std::generate(dst.begin(), dst.end(), [&index,step] { index += step; - index &= WAVEFORM_FRACMASK; - dst[i] = func(index); - } + index &= WaveformFracMask; + return func(index); + }); } -struct FormantFilter -{ +struct FormantFilter { float mCoeff{0.0f}; float mGain{1.0f}; float mS1{0.0f}; @@ -106,34 +107,38 @@ struct FormantFilter : mCoeff{std::tan(al::numbers::pi_v * f0norm)}, mGain{gain} { } - inline void process(const float *samplesIn, float *samplesOut, const size_t numInput) + void process(const float *samplesIn, float *samplesOut, const size_t numInput) noexcept { /* A state variable filter from a topology-preserving transform. * Based on a talk given by Ivan Cohen: https://www.youtube.com/watch?v=esjHXGPyrhg */ const float g{mCoeff}; const float gain{mGain}; - const float h{1.0f / (1.0f + (g/Q_FACTOR) + (g*g))}; + const float h{1.0f / (1.0f + (g*RcpQFactor) + (g*g))}; + const float coeff{RcpQFactor + g}; float s1{mS1}; float s2{mS2}; - for(size_t i{0u};i < numInput;i++) - { - const float H{(samplesIn[i] - (1.0f/Q_FACTOR + g)*s1 - s2)*h}; - const float B{g*H + s1}; - const float L{g*B + s2}; + const auto input = al::span{samplesIn, numInput}; + const auto output = al::span{samplesOut, numInput}; + std::transform(input.cbegin(), input.cend(), output.cbegin(), output.begin(), + [g,gain,h,coeff,&s1,&s2](const float in, const float out) noexcept -> float + { + const float H{(in - coeff*s1 - s2)*h}; + const float B{g*H + s1}; + const float L{g*B + s2}; - s1 = g*H + B; - s2 = g*B + L; + s1 = g*H + B; + s2 = g*B + L; - // Apply peak and accumulate samples. - samplesOut[i] += B * gain; - } + // Apply peak and accumulate samples. + return out + B*gain; + }); mS1 = s1; mS2 = s2; } - inline void clear() + void clear() noexcept { mS1 = 0.0f; mS2 = 0.0f; @@ -142,26 +147,27 @@ struct FormantFilter struct VmorpherState final : public EffectState { - struct { + struct OutParams { uint mTargetChannel{InvalidChannelIndex}; /* Effect parameters */ - FormantFilter mFormants[NUM_FILTERS][NUM_FORMANTS]; + std::array,NumFilters> mFormants; /* Effect gains for each channel */ float mCurrentGain{}; float mTargetGain{}; - } mChans[MaxAmbiChannels]; + }; + std::array mChans; - void (*mGetSamples)(float*RESTRICT, uint, const uint, size_t){}; + void (*mGetSamples)(const al::span dst, uint index, const uint step){}; uint mIndex{0}; uint mStep{1}; /* Effects buffers */ - alignas(16) float mSampleBufferA[MAX_UPDATE_SAMPLES]{}; - alignas(16) float mSampleBufferB[MAX_UPDATE_SAMPLES]{}; - alignas(16) float mLfo[MAX_UPDATE_SAMPLES]{}; + alignas(16) std::array mSampleBufferA{}; + alignas(16) std::array mSampleBufferB{}; + alignas(16) std::array mLfo{}; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props, @@ -169,14 +175,12 @@ struct VmorpherState final : public EffectState { void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - static std::array getFiltersByPhoneme(VMorpherPhenome phoneme, - float frequency, float pitch); - - DEF_NEWDEL(VmorpherState) + static std::array getFiltersByPhoneme(VMorpherPhenome phoneme, + float frequency, float pitch) noexcept; }; -std::array VmorpherState::getFiltersByPhoneme(VMorpherPhenome phoneme, - float frequency, float pitch) +std::array VmorpherState::getFiltersByPhoneme(VMorpherPhenome phoneme, + float frequency, float pitch) noexcept { /* Using soprano formant set of values to * better match mid-range frequency space. @@ -232,44 +236,43 @@ void VmorpherState::deviceUpdate(const DeviceBase*, const BufferStorage*) for(auto &e : mChans) { e.mTargetChannel = InvalidChannelIndex; - std::for_each(std::begin(e.mFormants[VOWEL_A_INDEX]), std::end(e.mFormants[VOWEL_A_INDEX]), + std::for_each(e.mFormants[VowelAIndex].begin(), e.mFormants[VowelAIndex].end(), std::mem_fn(&FormantFilter::clear)); - std::for_each(std::begin(e.mFormants[VOWEL_B_INDEX]), std::end(e.mFormants[VOWEL_B_INDEX]), + std::for_each(e.mFormants[VowelBIndex].begin(), e.mFormants[VowelBIndex].end(), std::mem_fn(&FormantFilter::clear)); e.mCurrentGain = 0.0f; } } void VmorpherState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get(*props_); const DeviceBase *device{context->mDevice}; const float frequency{static_cast(device->Frequency)}; - const float step{props->Vmorpher.Rate / frequency}; - mStep = fastf2u(clampf(step*WAVEFORM_FRACONE, 0.0f, float{WAVEFORM_FRACONE-1})); + const float step{props.Rate / frequency}; + mStep = fastf2u(std::clamp(step*WaveformFracOne, 0.0f, WaveformFracOne-1.0f)); if(mStep == 0) mGetSamples = Oscillate; - else if(props->Vmorpher.Waveform == VMorpherWaveform::Sinusoid) + else if(props.Waveform == VMorpherWaveform::Sinusoid) mGetSamples = Oscillate; - else if(props->Vmorpher.Waveform == VMorpherWaveform::Triangle) + else if(props.Waveform == VMorpherWaveform::Triangle) mGetSamples = Oscillate; - else /*if(props->Vmorpher.Waveform == VMorpherWaveform::Sawtooth)*/ + else /*if(props.Waveform == VMorpherWaveform::Sawtooth)*/ mGetSamples = Oscillate; - const float pitchA{std::pow(2.0f, - static_cast(props->Vmorpher.PhonemeACoarseTuning) / 12.0f)}; - const float pitchB{std::pow(2.0f, - static_cast(props->Vmorpher.PhonemeBCoarseTuning) / 12.0f)}; + const float pitchA{std::pow(2.0f, static_cast(props.PhonemeACoarseTuning) / 12.0f)}; + const float pitchB{std::pow(2.0f, static_cast(props.PhonemeBCoarseTuning) / 12.0f)}; - auto vowelA = getFiltersByPhoneme(props->Vmorpher.PhonemeA, frequency, pitchA); - auto vowelB = getFiltersByPhoneme(props->Vmorpher.PhonemeB, frequency, pitchB); + auto vowelA = getFiltersByPhoneme(props.PhonemeA, frequency, pitchA); + auto vowelB = getFiltersByPhoneme(props.PhonemeB, frequency, pitchB); /* Copy the filter coefficients to the input channels. */ for(size_t i{0u};i < slot->Wet.Buffer.size();++i) { - std::copy(vowelA.begin(), vowelA.end(), std::begin(mChans[i].mFormants[VOWEL_A_INDEX])); - std::copy(vowelB.begin(), vowelB.end(), std::begin(mChans[i].mFormants[VOWEL_B_INDEX])); + std::copy(vowelA.begin(), vowelA.end(), mChans[i].mFormants[VowelAIndex].begin()); + std::copy(vowelB.begin(), vowelB.end(), mChans[i].mFormants[VowelBIndex].begin()); } mOutTarget = target.Main->Buffer; @@ -283,18 +286,20 @@ void VmorpherState::update(const ContextBase *context, const EffectSlot *slot, void VmorpherState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) { + alignas(16) std::array blended{}; + /* Following the EFX specification for a conformant implementation which describes * the effect as a pair of 4-band formant filters blended together using an LFO. */ for(size_t base{0u};base < samplesToDo;) { - const size_t td{minz(MAX_UPDATE_SAMPLES, samplesToDo-base)}; + const size_t td{std::min(MaxUpdateSamples, samplesToDo-base)}; - mGetSamples(mLfo, mIndex, mStep, td); + mGetSamples(al::span{mLfo}.first(td), mIndex, mStep); mIndex += static_cast(mStep * td); - mIndex &= WAVEFORM_FRACMASK; + mIndex &= WaveformFracMask; - auto chandata = std::begin(mChans); + auto chandata = mChans.begin(); for(const auto &input : samplesIn) { const size_t outidx{chandata->mTargetChannel}; @@ -304,30 +309,29 @@ void VmorpherState::process(const size_t samplesToDo, const al::spanmFormants[VOWEL_A_INDEX]; - auto& vowelB = chandata->mFormants[VOWEL_B_INDEX]; + const auto vowelA = al::span{chandata->mFormants[VowelAIndex]}; + const auto vowelB = al::span{chandata->mFormants[VowelBIndex]}; /* Process first vowel. */ - std::fill_n(std::begin(mSampleBufferA), td, 0.0f); - vowelA[0].process(&input[base], mSampleBufferA, td); - vowelA[1].process(&input[base], mSampleBufferA, td); - vowelA[2].process(&input[base], mSampleBufferA, td); - vowelA[3].process(&input[base], mSampleBufferA, td); + std::fill_n(mSampleBufferA.begin(), td, 0.0f); + vowelA[0].process(&input[base], mSampleBufferA.data(), td); + vowelA[1].process(&input[base], mSampleBufferA.data(), td); + vowelA[2].process(&input[base], mSampleBufferA.data(), td); + vowelA[3].process(&input[base], mSampleBufferA.data(), td); /* Process second vowel. */ - std::fill_n(std::begin(mSampleBufferB), td, 0.0f); - vowelB[0].process(&input[base], mSampleBufferB, td); - vowelB[1].process(&input[base], mSampleBufferB, td); - vowelB[2].process(&input[base], mSampleBufferB, td); - vowelB[3].process(&input[base], mSampleBufferB, td); + std::fill_n(mSampleBufferB.begin(), td, 0.0f); + vowelB[0].process(&input[base], mSampleBufferB.data(), td); + vowelB[1].process(&input[base], mSampleBufferB.data(), td); + vowelB[2].process(&input[base], mSampleBufferB.data(), td); + vowelB[3].process(&input[base], mSampleBufferB.data(), td); - alignas(16) float blended[MAX_UPDATE_SAMPLES]; for(size_t i{0u};i < td;i++) blended[i] = lerpf(mSampleBufferA[i], mSampleBufferB[i], mLfo[i]); /* Now, mix the processed sound data to the output. */ - MixSamples({blended, td}, samplesOut[outidx].data()+base, chandata->mCurrentGain, - chandata->mTargetGain, samplesToDo-base); + MixSamples(al::span{blended}.first(td), al::span{samplesOut[outidx]}.subspan(base), + chandata->mCurrentGain, chandata->mTargetGain, samplesToDo-base); ++chandata; } diff --git a/3rdparty/openal/alc/events.cpp b/3rdparty/openal/alc/events.cpp index a80faf8aa075..1010a33840ad 100644 --- a/3rdparty/openal/alc/events.cpp +++ b/3rdparty/openal/alc/events.cpp @@ -3,8 +3,6 @@ #include "events.h" -#include - #include "alspan.h" #include "core/logging.h" #include "device.h" @@ -12,17 +10,6 @@ namespace { -std::optional 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; -} - ALCenum EnumFromEventType(const alc::EventType type) { switch(type) @@ -39,6 +26,17 @@ ALCenum EnumFromEventType(const alc::EventType type) namespace alc { +std::optional 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}; @@ -73,7 +71,7 @@ FORCE_ALIGN ALCboolean ALC_APIENTRY alcEventControlSOFT(ALCsizei count, const AL alc::EventBitSet eventSet{0}; for(ALCenum type : al::span{events, static_cast(count)}) { - auto etype = GetEventType(type); + auto etype = alc::GetEventType(type); if(!etype) { WARN("Invalid event type: 0x%04x\n", type); diff --git a/3rdparty/openal/alc/events.h b/3rdparty/openal/alc/events.h index 4acc505df64a..3f53ec76265b 100644 --- a/3rdparty/openal/alc/events.h +++ b/3rdparty/openal/alc/events.h @@ -6,6 +6,7 @@ #include #include +#include #include @@ -19,6 +20,13 @@ enum class EventType : uint8_t { Count }; +std::optional 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, diff --git a/3rdparty/openal/alc/export_list.h b/3rdparty/openal/alc/export_list.h index cefe7a099bce..b83f2c38a3fc 100644 --- a/3rdparty/openal/alc/export_list.h +++ b/3rdparty/openal/alc/export_list.h @@ -1,12 +1,14 @@ #ifndef ALC_EXPORT_LIST_H #define ALC_EXPORT_LIST_H +#include "config.h" + #include "AL/alc.h" #include "AL/al.h" #include "AL/alext.h" #include "inprogext.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include "context.h" #include "al/eax/x_ram.h" #endif @@ -16,7 +18,8 @@ struct FuncExport { const char *funcName; void *address; }; -#define DECL(x) { #x, reinterpret_cast(x) } +#define DECL(x) FuncExport{#x, reinterpret_cast(x)} +/* NOLINTNEXTLINE(*-avoid-c-arrays) Too large for std::array auto-deduction :( */ inline const FuncExport alcFunctions[]{ DECL(alcCreateContext), DECL(alcMakeContextCurrent), @@ -56,6 +59,7 @@ inline const FuncExport alcFunctions[]{ DECL(alcReopenDeviceSOFT), + DECL(alcEventIsSupportedSOFT), DECL(alcEventControlSOFT), DECL(alcEventCallbackSOFT), @@ -200,11 +204,6 @@ inline const FuncExport alcFunctions[]{ DECL(alGetBuffer3PtrSOFT), DECL(alGetBufferPtrvSOFT), - DECL(alAuxiliaryEffectSlotPlaySOFT), - DECL(alAuxiliaryEffectSlotPlayvSOFT), - DECL(alAuxiliaryEffectSlotStopSOFT), - DECL(alAuxiliaryEffectSlotStopvSOFT), - DECL(alSourcePlayAtTimeSOFT), DECL(alSourcePlayAtTimevSOFT), @@ -218,6 +217,10 @@ inline const FuncExport alcFunctions[]{ DECL(alPushDebugGroupEXT), DECL(alPopDebugGroupEXT), DECL(alGetDebugMessageLogEXT), + DECL(alObjectLabelEXT), + DECL(alGetObjectLabelEXT), + DECL(alGetPointerEXT), + DECL(alGetPointervEXT), /* Direct Context functions */ DECL(alcGetProcAddress2), @@ -368,15 +371,16 @@ inline const FuncExport alcFunctions[]{ DECL(alPushDebugGroupDirectEXT), DECL(alPopDebugGroupDirectEXT), DECL(alGetDebugMessageLogDirectEXT), - DECL(alObjectLabelEXT), DECL(alObjectLabelDirectEXT), - DECL(alGetObjectLabelEXT), DECL(alGetObjectLabelDirectEXT), + DECL(alGetPointerDirectEXT), + DECL(alGetPointervDirectEXT), /* Extra functions */ DECL(alsoft_set_log_callback), -#ifdef ALSOFT_EAX -}, eaxFunctions[]{ +}; +#if ALSOFT_EAX +inline const std::array eaxFunctions{ DECL(EAXGet), DECL(EAXSet), DECL(EAXGetBufferMode), @@ -386,15 +390,16 @@ inline const FuncExport alcFunctions[]{ DECL(EAXSetDirect), DECL(EAXGetBufferModeDirect), DECL(EAXSetBufferModeDirect), -#endif }; +#endif #undef DECL struct EnumExport { const char *enumName; int value; }; -#define DECL(x) { #x, (x) } +#define DECL(x) EnumExport{#x, (x)} +/* NOLINTNEXTLINE(*-avoid-c-arrays) Too large for std::array auto-deduction :( */ inline const EnumExport alcEnumerations[]{ DECL(ALC_INVALID), DECL(ALC_FALSE), @@ -598,6 +603,48 @@ inline const EnumExport alcEnumerations[]{ 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_BFORMAT2D_I32), + DECL(AL_FORMAT_BFORMAT3D_I32), + DECL(AL_FORMAT_UHJ2CHN_I32_SOFT), + DECL(AL_FORMAT_UHJ3CHN_I32_SOFT), + DECL(AL_FORMAT_UHJ4CHN_I32_SOFT), + + 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), @@ -820,32 +867,9 @@ inline const EnumExport alcEnumerations[]{ DECL(AL_UNPACK_AMBISONIC_ORDER_SOFT), - DECL(AL_EFFECT_CONVOLUTION_REVERB_SOFT), + DECL(AL_EFFECT_CONVOLUTION_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_DONT_CARE_EXT), DECL(AL_DEBUG_OUTPUT_EXT), DECL(AL_DEBUG_CALLBACK_FUNCTION_EXT), @@ -882,16 +906,20 @@ inline const EnumExport alcEnumerations[]{ DECL(AL_EFFECT_EXT), DECL(AL_AUXILIARY_EFFECT_SLOT_EXT), + DECL(AL_PANNING_ENABLED_SOFT), + DECL(AL_PAN_SOFT), + DECL(AL_STOP_SOURCES_ON_DISCONNECT_SOFT), -#ifdef ALSOFT_EAX -}, eaxEnumerations[]{ +}; +#if ALSOFT_EAX +inline const std::array 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 }; +#endif #undef DECL #endif /* ALC_EXPORT_LIST_H */ diff --git a/3rdparty/openal/alc/inprogext.h b/3rdparty/openal/alc/inprogext.h index 2fa425bbbcb6..f847b340d300 100644 --- a/3rdparty/openal/alc/inprogext.h +++ b/3rdparty/openal/alc/inprogext.h @@ -1,6 +1,7 @@ #ifndef INPROGEXT_H #define INPROGEXT_H +/* NOLINTBEGIN */ #include "AL/al.h" #include "AL/alc.h" #include "AL/alext.h" @@ -41,20 +42,11 @@ void AL_APIENTRY alFlushMappedBufferDirectSOFT(ALCcontext *context, ALuint buffe #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) 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_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 +#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 #endif #ifndef AL_SOFT_hold_on_disconnect @@ -63,338 +55,33 @@ AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint * #endif -#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; +#ifndef AL_EXT_32bit_formats +#define AL_EXT_32bit_formats +#define AL_FORMAT_MONO_I32 0x19DB +#define AL_FORMAT_STEREO_I32 0x19DC +#define AL_FORMAT_REAR_I32 0x19DD +#define AL_FORMAT_REAR_FLOAT32 0x19DE +#define AL_FORMAT_QUAD_I32 0x19DF +#define AL_FORMAT_QUAD_FLOAT32 0x19E0 +#define AL_FORMAT_51CHN_I32 0x19E1 +#define AL_FORMAT_51CHN_FLOAT32 0x19E2 +#define AL_FORMAT_61CHN_I32 0x19E3 +#define AL_FORMAT_61CHN_FLOAT32 0x19E4 +#define AL_FORMAT_71CHN_I32 0x19E5 +#define AL_FORMAT_71CHN_FLOAT32 0x19E6 + +#define AL_FORMAT_BFORMAT2D_I32 0x19E7 +#define AL_FORMAT_BFORMAT3D_I32 0x19E8 + +#define AL_FORMAT_UHJ2CHN_I32_SOFT 0x19E9 +#define AL_FORMAT_UHJ3CHN_I32_SOFT 0x19EA +#define AL_FORMAT_UHJ4CHN_I32_SOFT 0x19EB #endif + +#ifndef AL_SOFT_source_panning +#define AL_SOFT_source_panning +#define AL_PANNING_ENABLED_SOFT 0x19EC +#define AL_PAN_SOFT 0x19ED #endif /* Non-standard exports. Not part of any extension. */ @@ -407,13 +94,25 @@ void ALC_APIENTRY alsoft_set_log_callback(LPALSOFTLOGCALLBACK callback, void *us AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint src, ALsizei nb, const ALuint *buffers) noexcept; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid) noexcept; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids) noexcept; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid) noexcept; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids) noexcept; + 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; +/* Not included in the public headers or export list, as a precaution for apps + * that check these to determine the behavior of the multi-channel *32 formats. + */ +#define AL_FORMAT_MONO32 0x1202 +#define AL_FORMAT_STEREO32 0x1203 + #ifdef __cplusplus } /* extern "C" */ #endif +/* NOLINTEND */ #endif /* INPROGEXT_H */ diff --git a/3rdparty/openal/alc/panning.cpp b/3rdparty/openal/alc/panning.cpp index 871fef65e906..f144186cbd0f 100644 --- a/3rdparty/openal/alc/panning.cpp +++ b/3rdparty/openal/alc/panning.cpp @@ -22,29 +22,26 @@ #include #include +#include #include #include #include +#include #include -#include +#include #include -#include #include -#include #include #include #include +#include +#include #include -#include "AL/al.h" #include "AL/alc.h" #include "AL/alext.h" -#include "al/auxeffectslot.h" -#include "albit.h" -#include "alconfig.h" #include "alc/context.h" -#include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" @@ -53,23 +50,33 @@ #include "core/ambdec.h" #include "core/ambidefs.h" #include "core/bformatdec.h" +#include "core/bufferline.h" #include "core/bs2b.h" +#include "core/context.h" #include "core/devformat.h" +#include "core/device.h" +#include "core/effectslot.h" +#include "core/filters/nfc.h" +#include "core/filters/splitter.h" #include "core/front_stablizer.h" #include "core/hrtf.h" #include "core/logging.h" +#include "core/mixer/hrtfdefs.h" #include "core/uhjfilter.h" #include "device.h" +#include "flexarray.h" +#include "intrusive_ptr.h" #include "opthelpers.h" +#include "vector.h" namespace { -using namespace std::placeholders; +using namespace std::string_view_literals; using std::chrono::seconds; using std::chrono::nanoseconds; -inline const char *GetLabelFromChannel(Channel channel) +const char *GetLabelFromChannel(Channel channel) { switch(channel) { @@ -91,6 +98,11 @@ inline const char *GetLabelFromChannel(Channel channel) case TopBackCenter: return "top-back-center"; case TopBackRight: return "top-back-right"; + case BottomFrontLeft: return "bottom-front-left"; + case BottomFrontRight: return "bottom-front-right"; + case BottomBackLeft: return "bottom-back-left"; + case BottomBackRight: return "bottom-back-right"; + case Aux0: return "Aux0"; case Aux1: return "Aux1"; case Aux2: return "Aux2"; @@ -113,6 +125,27 @@ inline const char *GetLabelFromChannel(Channel channel) return "(unknown)"; } +auto GetLayoutName(DevAmbiLayout layout) noexcept -> const char* +{ + switch(layout) + { + case DevAmbiLayout::FuMa: return "FuMa"; + case DevAmbiLayout::ACN: return "ACN"; + } + return ""; +} + +auto GetScalingName(DevAmbiScaling scaling) noexcept -> const char* +{ + switch(scaling) + { + case DevAmbiScaling::FuMa: return "FuMa"; + case DevAmbiScaling::SN3D: return "SN3D"; + case DevAmbiScaling::N3D: return "N3D"; + } + return ""; +} + std::unique_ptr CreateStablizer(const size_t outchans, const uint srate) { @@ -128,7 +161,7 @@ std::unique_ptr CreateStablizer(const size_t outchans, const uin return stablizer; } -void AllocChannels(ALCdevice *device, const size_t main_chans, const size_t real_chans) +void AllocChannels(al::Device *device, const size_t main_chans, const size_t real_chans) { TRACE("Channel config, Main: %zu, Real: %zu\n", main_chans, real_chans); @@ -227,43 +260,48 @@ struct DecoderConfig { using DecoderView = DecoderConfig; -void InitNearFieldCtrl(ALCdevice *device, float ctrl_dist, uint order, bool is3d) +void InitNearFieldCtrl(al::Device *device, const float ctrl_dist, const uint order, + const bool is3d) { - static const uint chans_per_order2d[MaxAmbiOrder+1]{ 1, 2, 2, 2 }; - static const uint chans_per_order3d[MaxAmbiOrder+1]{ 1, 3, 5, 7 }; + static const std::array chans_per_order2d{{1, 2, 2, 2}}; + static const std::array chans_per_order3d{{1, 3, 5, 7}}; /* NFC is only used when AvgSpeakerDist is greater than 0. */ if(!device->getConfigValueBool("decoder", "nfc", false) || !(ctrl_dist > 0.0f)) return; - device->AvgSpeakerDist = clampf(ctrl_dist, 0.1f, 10.0f); + device->AvgSpeakerDist = std::clamp(ctrl_dist, 0.1f, 10.0f); TRACE("Using near-field reference distance: %.2f meters\n", device->AvgSpeakerDist); const float w1{SpeedOfSoundMetersPerSec / (device->AvgSpeakerDist * static_cast(device->Frequency))}; device->mNFCtrlFilter.init(w1); - auto iter = std::copy_n(is3d ? chans_per_order3d : chans_per_order2d, order+1u, - std::begin(device->NumChannelsPerOrder)); - std::fill(iter, std::end(device->NumChannelsPerOrder), 0u); + auto iter = std::copy_n(is3d ? chans_per_order3d.begin() : chans_per_order2d.begin(), order+1u, + device->NumChannelsPerOrder.begin()); + std::fill(iter, device->NumChannelsPerOrder.end(), 0u); } -void InitDistanceComp(ALCdevice *device, const al::span channels, - const al::span dists) +void InitDistanceComp(al::Device *device, const al::span channels, + const al::span dists) { - const float maxdist{std::accumulate(std::begin(dists), std::end(dists), 0.0f, maxf)}; + const float maxdist{std::accumulate(dists.begin(), dists.end(), 0.0f, + [](const float a, const float b) noexcept -> float { return std::max(a, b); })}; if(!device->getConfigValueBool("decoder", "distance-comp", true) || !(maxdist > 0.0f)) return; const auto distSampleScale = static_cast(device->Frequency) / SpeedOfSoundMetersPerSec; - std::vector ChanDelay; + + struct DistCoeffs { uint Length{}; float Gain{}; }; + std::vector ChanDelay; ChanDelay.reserve(device->RealOut.Buffer.size()); + size_t total{0u}; for(size_t chidx{0};chidx < channels.size();++chidx) { const Channel ch{channels[chidx]}; - const uint idx{device->RealOut.ChannelIndex[ch]}; + const size_t idx{device->RealOut.ChannelIndex[ch]}; if(idx == InvalidChannelIndex) continue; @@ -278,12 +316,12 @@ void InitDistanceComp(ALCdevice *device, const al::span channels, float delay{std::floor((maxdist - distance)*distSampleScale + 0.5f)}; if(delay > float{DistanceComp::MaxDelay-1}) { - ERR("Delay for channel %u (%s) exceeds buffer length (%f > %d)\n", idx, + ERR("Delay for channel %zu (%s) exceeds buffer length (%f > %d)\n", idx, GetLabelFromChannel(ch), delay, DistanceComp::MaxDelay-1); delay = float{DistanceComp::MaxDelay-1}; } - ChanDelay.resize(maxz(ChanDelay.size(), idx+1)); + ChanDelay.resize(std::max(ChanDelay.size(), idx+1_uz)); ChanDelay[idx].Length = static_cast(delay); ChanDelay[idx].Gain = distance / maxdist; TRACE("Channel %s distance comp: %u samples, %f gain\n", GetLabelFromChannel(ch), @@ -298,16 +336,17 @@ void InitDistanceComp(ALCdevice *device, const al::span channels, if(total > 0) { auto chandelays = DistanceComp::Create(total); + auto chanbuffer = chandelays->mSamples.begin(); - ChanDelay[0].Buffer = chandelays->mSamples.data(); - auto set_bufptr = [](const DistanceComp::ChanData &last, const DistanceComp::ChanData &cur) - -> DistanceComp::ChanData + auto set_bufptr = [&chanbuffer](const DistCoeffs &data) { - DistanceComp::ChanData ret{cur}; - ret.Buffer = last.Buffer + RoundUp(last.Length, 4); + DistanceComp::ChanData ret{}; + ret.Buffer = al::span{chanbuffer, data.Length}; + ret.Gain = data.Gain; + chanbuffer += ptrdiff_t(RoundUp(data.Length, 4)); return ret; }; - std::partial_sum(ChanDelay.begin(), ChanDelay.end(), chandelays->mChannels.begin(), + std::transform(ChanDelay.begin(), ChanDelay.end(), chandelays->mChannels.begin(), set_bufptr); device->ChannelDelays = std::move(chandelays); } @@ -328,8 +367,8 @@ constexpr auto GetAmbiLayout(DevAmbiLayout layouttype) noexcept } -DecoderView MakeDecoderView(ALCdevice *device, const AmbDecConf *conf, - DecoderConfig &decoder) +auto MakeDecoderView(al::Device *device, const AmbDecConf *conf, + DecoderConfig &decoder) -> DecoderView { DecoderView ret{}; @@ -346,23 +385,20 @@ DecoderView MakeDecoderView(ALCdevice *device, const AmbDecConf *conf, case AmbDecScale::FuMa: decoder.mScaling = DevAmbiScaling::FuMa; break; } - std::copy_n(std::begin(conf->HFOrderGain), - std::min(std::size(conf->HFOrderGain), std::size(decoder.mOrderGain)), - std::begin(decoder.mOrderGain)); - std::copy_n(std::begin(conf->LFOrderGain), - std::min(std::size(conf->LFOrderGain), std::size(decoder.mOrderGainLF)), - std::begin(decoder.mOrderGainLF)); + const auto hfordermin = std::min(conf->HFOrderGain.size(), decoder.mOrderGain.size()); + std::copy_n(conf->HFOrderGain.begin(), hfordermin, decoder.mOrderGain.begin()); + const auto lfordermin = std::min(conf->LFOrderGain.size(), decoder.mOrderGainLF.size()); + std::copy_n(conf->LFOrderGain.begin(), lfordermin, decoder.mOrderGainLF.begin()); 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 ? al::span{AmbiIndex::FromACN} + : al::span{AmbiIndex::FromACN2D}; const auto hfmatrix = conf->HFMatrix; const auto lfmatrix = conf->LFMatrix; uint chan_count{0}; - using const_speaker_span = al::span; - for(auto &speaker : const_speaker_span{conf->Speakers.get(), conf->NumSpeakers}) + for(auto &speaker : al::span{std::as_const(conf->Speakers)}) { /* NOTE: AmbDec does not define any standard speaker names, however * for this to work we have to by able to find the output channel @@ -381,36 +417,48 @@ DecoderView MakeDecoderView(ALCdevice *device, const AmbDecConf *conf, * RFT = Top front right * LBT = Top back left * RBT = Top back right + * LFB = Bottom front left + * RFB = Bottom front right + * LBB = Bottom back left + * RBB = Bottom back right * * Additionally, surround51 will acknowledge back speakers for side * channels, to avoid issues with an ambdec expecting 5.1 to use the * back channels. */ Channel ch{}; - if(speaker.Name == "LF") + if(speaker.Name == "LF"sv) ch = FrontLeft; - else if(speaker.Name == "RF") + else if(speaker.Name == "RF"sv) ch = FrontRight; - else if(speaker.Name == "CE") + else if(speaker.Name == "CE"sv) ch = FrontCenter; - else if(speaker.Name == "LS") + else if(speaker.Name == "LS"sv) ch = SideLeft; - else if(speaker.Name == "RS") + else if(speaker.Name == "RS"sv) ch = SideRight; - else if(speaker.Name == "LB") + else if(speaker.Name == "LB"sv) ch = (device->FmtChans == DevFmtX51) ? SideLeft : BackLeft; - else if(speaker.Name == "RB") + else if(speaker.Name == "RB"sv) ch = (device->FmtChans == DevFmtX51) ? SideRight : BackRight; - else if(speaker.Name == "CB") + else if(speaker.Name == "CB"sv) ch = BackCenter; - else if(speaker.Name == "LFT") + else if(speaker.Name == "LFT"sv) ch = TopFrontLeft; - else if(speaker.Name == "RFT") + else if(speaker.Name == "RFT"sv) ch = TopFrontRight; - else if(speaker.Name == "LBT") + else if(speaker.Name == "LBT"sv) ch = TopBackLeft; - else if(speaker.Name == "RBT") + else if(speaker.Name == "RBT"sv) ch = TopBackRight; + else if(speaker.Name == "LFB"sv) + ch = BottomFrontLeft; + else if(speaker.Name == "RFB"sv) + ch = BottomFrontRight; + else if(speaker.Name == "LBB"sv) + ch = BottomBackLeft; + else if(speaker.Name == "RBB"sv) + ch = BottomBackRight; else { int idx{}; @@ -446,13 +494,13 @@ DecoderView MakeDecoderView(ALCdevice *device, const AmbDecConf *conf, ret.mOrder = decoder.mOrder; ret.mIs3D = decoder.mIs3D; ret.mScaling = decoder.mScaling; - ret.mChannels = {decoder.mChannels.data(), chan_count}; + ret.mChannels = al::span{decoder.mChannels}.first(chan_count); ret.mOrderGain = decoder.mOrderGain; - ret.mCoeffs = {decoder.mCoeffs.data(), chan_count}; + ret.mCoeffs = al::span{decoder.mCoeffs}.first(chan_count); if(conf->FreqBands > 1) { ret.mOrderGainLF = decoder.mOrderGainLF; - ret.mCoeffsLF = {decoder.mCoeffsLF.data(), chan_count}; + ret.mCoeffsLF = al::span{decoder.mCoeffsLF}.first(chan_count); } } return ret; @@ -584,8 +632,46 @@ constexpr DecoderConfig X714Config{ {{8.80892603e-02f, -7.48948724e-02f, 9.08779842e-02f, -6.22480443e-02f}}, }} }; +constexpr DecoderConfig X7144Config{ + 1, true, {{BackLeft, SideLeft, FrontLeft, FrontRight, SideRight, BackRight, TopBackLeft, TopFrontLeft, TopFrontRight, TopBackRight, BottomBackLeft, BottomFrontLeft, BottomFrontRight, BottomBackRight}}, + DevAmbiScaling::N3D, + /*HF*/{{2.64575131e+0f, 1.52752523e+0f}}, + {{ + {{7.14285714e-02f, 5.09426708e-02f, 0.00000000e+00f, -8.82352941e-02f}}, + {{7.14285714e-02f, 1.01885342e-01f, 0.00000000e+00f, 0.00000000e+00f}}, + {{7.14285714e-02f, 5.09426708e-02f, 0.00000000e+00f, 8.82352941e-02f}}, + {{7.14285714e-02f, -5.09426708e-02f, 0.00000000e+00f, 8.82352941e-02f}}, + {{7.14285714e-02f, -1.01885342e-01f, 0.00000000e+00f, 0.00000000e+00f}}, + {{7.14285714e-02f, -5.09426708e-02f, 0.00000000e+00f, -8.82352941e-02f}}, + {{7.14285714e-02f, 5.88235294e-02f, 1.25000000e-01f, -5.88235294e-02f}}, + {{7.14285714e-02f, 5.88235294e-02f, 1.25000000e-01f, 5.88235294e-02f}}, + {{7.14285714e-02f, -5.88235294e-02f, 1.25000000e-01f, 5.88235294e-02f}}, + {{7.14285714e-02f, -5.88235294e-02f, 1.25000000e-01f, -5.88235294e-02f}}, + {{7.14285714e-02f, 5.88235294e-02f, -1.25000000e-01f, -5.88235294e-02f}}, + {{7.14285714e-02f, 5.88235294e-02f, -1.25000000e-01f, 5.88235294e-02f}}, + {{7.14285714e-02f, -5.88235294e-02f, -1.25000000e-01f, 5.88235294e-02f}}, + {{7.14285714e-02f, -5.88235294e-02f, -1.25000000e-01f, -5.88235294e-02f}}, + }}, + /*LF*/{{1.00000000e+0f, 1.00000000e+0f}}, + {{ + {{7.14285714e-02f, 5.09426708e-02f, 0.00000000e+00f, -8.82352941e-02f}}, + {{7.14285714e-02f, 1.01885342e-01f, 0.00000000e+00f, 0.00000000e+00f}}, + {{7.14285714e-02f, 5.09426708e-02f, 0.00000000e+00f, 8.82352941e-02f}}, + {{7.14285714e-02f, -5.09426708e-02f, 0.00000000e+00f, 8.82352941e-02f}}, + {{7.14285714e-02f, -1.01885342e-01f, 0.00000000e+00f, 0.00000000e+00f}}, + {{7.14285714e-02f, -5.09426708e-02f, 0.00000000e+00f, -8.82352941e-02f}}, + {{7.14285714e-02f, 5.88235294e-02f, 1.25000000e-01f, -5.88235294e-02f}}, + {{7.14285714e-02f, 5.88235294e-02f, 1.25000000e-01f, 5.88235294e-02f}}, + {{7.14285714e-02f, -5.88235294e-02f, 1.25000000e-01f, 5.88235294e-02f}}, + {{7.14285714e-02f, -5.88235294e-02f, 1.25000000e-01f, -5.88235294e-02f}}, + {{7.14285714e-02f, 5.88235294e-02f, -1.25000000e-01f, -5.88235294e-02f}}, + {{7.14285714e-02f, 5.88235294e-02f, -1.25000000e-01f, 5.88235294e-02f}}, + {{7.14285714e-02f, -5.88235294e-02f, -1.25000000e-01f, 5.88235294e-02f}}, + {{7.14285714e-02f, -5.88235294e-02f, -1.25000000e-01f, -5.88235294e-02f}}, + }} +}; -void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=false, +void InitPanning(al::Device *device, const bool hqdec=false, const bool stablize=false, DecoderView decoder={}) { if(!decoder) @@ -599,14 +685,15 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= case DevFmtX61: decoder = X61Config; break; case DevFmtX71: decoder = X71Config; break; case DevFmtX714: decoder = X714Config; break; + case DevFmtX7144: decoder = X7144Config; break; case DevFmtX3D71: decoder = X3D71Config; break; case DevFmtAmbi3D: - 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), + const auto acnmap = GetAmbiLayout(device->mAmbiLayout).first(count); + const auto n3dscale = GetAmbiScales(device->mAmbiScale); + + std::transform(acnmap.cbegin(), acnmap.cend(), device->Dry.AmbiMap.begin(), [n3dscale](const uint8_t &acn) noexcept -> BFChannelConfig { return BFChannelConfig{1.0f/n3dscale[acn], acn}; }); AllocChannels(device, count, 0); @@ -621,6 +708,9 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= avg_dist = *delayopt * SpeedOfSoundMetersPerSec; } + TRACE("%u%s order ambisonic output (%s layout, %s scaling)\n", device->mAmbiOrder, + GetCounterSuffix(device->mAmbiOrder), GetLayoutName(device->mAmbiLayout), + GetScalingName(device->mAmbiScale)); InitNearFieldCtrl(device, avg_dist, device->mAmbiOrder, true); return; } @@ -632,7 +722,7 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= std::vector chancoeffs, chancoeffslf; for(size_t i{0u};i < decoder.mChannels.size();++i) { - const uint idx{device->channelIdxByName(decoder.mChannels[i])}; + const size_t idx{device->channelIdxByName(decoder.mChannels[i])}; if(idx == InvalidChannelIndex) { ERR("Failed to find %s channel in device\n", @@ -640,10 +730,10 @@ 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 ? al::span{AmbiIndex::OrderFromChannel} + : al::span{AmbiIndex::OrderFrom2DChannel}; - chancoeffs.resize(maxz(chancoeffs.size(), idx+1u), ChannelDec{}); + chancoeffs.resize(std::max(chancoeffs.size(), idx+1_zu), ChannelDec{}); al::span src{decoder.mCoeffs[i]}; al::span dst{chancoeffs[idx]}; for(size_t ambichan{0};ambichan < ambicount;++ambichan) @@ -652,7 +742,7 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= if(!dual_band) continue; - chancoeffslf.resize(maxz(chancoeffslf.size(), idx+1u), ChannelDec{}); + chancoeffslf.resize(std::max(chancoeffslf.size(), idx+1_zu), ChannelDec{}); src = decoder.mCoeffsLF[i]; dst = chancoeffslf[idx]; for(size_t ambichan{0};ambichan < ambicount;++ambichan) @@ -663,10 +753,10 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= device->mAmbiOrder = decoder.mOrder; device->m2DMixing = !decoder.mIs3D; - const al::span acnmap{decoder.mIs3D ? AmbiIndex::FromACN.data() : - AmbiIndex::FromACN2D.data(), ambicount}; + const auto acnmap = decoder.mIs3D ? al::span{AmbiIndex::FromACN}.first(ambicount) + : al::span{AmbiIndex::FromACN2D}.first(ambicount); const auto coeffscale = GetAmbiScales(decoder.mScaling); - std::transform(acnmap.begin(), acnmap.end(), std::begin(device->Dry.AmbiMap), + std::transform(acnmap.begin(), acnmap.end(), device->Dry.AmbiMap.begin(), [coeffscale](const uint8_t &acn) noexcept { return BFChannelConfig{1.0f/coeffscale[acn], acn}; }); AllocChannels(device, ambicount, device->channelsFromFmt()); @@ -677,7 +767,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()) { @@ -706,122 +796,128 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= device->mXOverFreq/static_cast(device->Frequency), std::move(stablizer)); } -void InitHrtfPanning(ALCdevice *device) +void InitHrtfPanning(al::Device *device) { - constexpr float Deg180{al::numbers::pi_v}; - constexpr float Deg_90{Deg180 / 2.0f /* 90 degrees*/}; - constexpr float Deg_45{Deg_90 / 2.0f /* 45 degrees*/}; - constexpr float Deg135{Deg_45 * 3.0f /*135 degrees*/}; - constexpr float Deg_21{3.648638281e-01f /* 20~ 21 degrees*/}; - constexpr float Deg_32{5.535743589e-01f /* 31~ 32 degrees*/}; - constexpr float Deg_35{6.154797087e-01f /* 35~ 36 degrees*/}; - constexpr float Deg_58{1.017221968e+00f /* 58~ 59 degrees*/}; - constexpr float Deg_69{1.205932499e+00f /* 69~ 70 degrees*/}; - constexpr float Deg111{1.935660155e+00f /*110~111 degrees*/}; - constexpr float Deg122{2.124370686e+00f /*121~122 degrees*/}; - static const AngularPoint AmbiPoints1O[]{ - { EvRadians{ Deg_35}, AzRadians{-Deg_45} }, - { EvRadians{ Deg_35}, AzRadians{-Deg135} }, - { EvRadians{ Deg_35}, AzRadians{ Deg_45} }, - { EvRadians{ Deg_35}, AzRadians{ Deg135} }, - { EvRadians{-Deg_35}, AzRadians{-Deg_45} }, - { EvRadians{-Deg_35}, AzRadians{-Deg135} }, - { EvRadians{-Deg_35}, AzRadians{ Deg_45} }, - { EvRadians{-Deg_35}, AzRadians{ Deg135} }, - }, AmbiPoints2O[]{ - { EvRadians{-Deg_32}, AzRadians{ 0.0f} }, - { EvRadians{ 0.0f}, AzRadians{ Deg_58} }, - { EvRadians{ Deg_58}, AzRadians{ Deg_90} }, - { EvRadians{ Deg_32}, AzRadians{ 0.0f} }, - { EvRadians{ 0.0f}, AzRadians{ Deg122} }, - { EvRadians{-Deg_58}, AzRadians{-Deg_90} }, - { EvRadians{-Deg_32}, AzRadians{ Deg180} }, - { EvRadians{ 0.0f}, AzRadians{-Deg122} }, - { EvRadians{ Deg_58}, AzRadians{-Deg_90} }, - { EvRadians{ Deg_32}, AzRadians{ Deg180} }, - { EvRadians{ 0.0f}, AzRadians{-Deg_58} }, - { EvRadians{-Deg_58}, AzRadians{ Deg_90} }, - }, AmbiPoints3O[]{ - { EvRadians{ Deg_69}, AzRadians{-Deg_90} }, - { EvRadians{ Deg_69}, AzRadians{ Deg_90} }, - { EvRadians{-Deg_69}, AzRadians{-Deg_90} }, - { EvRadians{-Deg_69}, AzRadians{ Deg_90} }, - { EvRadians{ 0.0f}, AzRadians{-Deg_69} }, - { EvRadians{ 0.0f}, AzRadians{-Deg111} }, - { EvRadians{ 0.0f}, AzRadians{ Deg_69} }, - { EvRadians{ 0.0f}, AzRadians{ Deg111} }, - { EvRadians{ Deg_21}, AzRadians{ 0.0f} }, - { EvRadians{ Deg_21}, AzRadians{ Deg180} }, - { EvRadians{-Deg_21}, AzRadians{ 0.0f} }, - { EvRadians{-Deg_21}, AzRadians{ Deg180} }, - { EvRadians{ Deg_35}, AzRadians{-Deg_45} }, - { EvRadians{ Deg_35}, AzRadians{-Deg135} }, - { EvRadians{ Deg_35}, AzRadians{ Deg_45} }, - { EvRadians{ Deg_35}, AzRadians{ Deg135} }, - { EvRadians{-Deg_35}, AzRadians{-Deg_45} }, - { EvRadians{-Deg_35}, AzRadians{-Deg135} }, - { EvRadians{-Deg_35}, AzRadians{ Deg_45} }, - { EvRadians{-Deg_35}, AzRadians{ Deg135} }, + static constexpr float Deg180{al::numbers::pi_v}; + static constexpr float Deg_90{Deg180 / 2.0f /* 90 degrees*/}; + static constexpr float Deg_45{Deg_90 / 2.0f /* 45 degrees*/}; + static constexpr float Deg135{Deg_45 * 3.0f /*135 degrees*/}; + static constexpr float Deg_21{3.648638281e-01f /* 20~ 21 degrees*/}; + static constexpr float Deg_32{5.535743589e-01f /* 31~ 32 degrees*/}; + static constexpr float Deg_35{6.154797087e-01f /* 35~ 36 degrees*/}; + static constexpr float Deg_58{1.017221968e+00f /* 58~ 59 degrees*/}; + static constexpr float Deg_69{1.205932499e+00f /* 69~ 70 degrees*/}; + static constexpr float Deg111{1.935660155e+00f /*110~111 degrees*/}; + static constexpr float Deg122{2.124370686e+00f /*121~122 degrees*/}; + static constexpr std::array AmbiPoints1O{ + AngularPoint{EvRadians{ Deg_35}, AzRadians{-Deg_45}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{-Deg135}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{ Deg_45}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{ Deg135}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{-Deg_45}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{-Deg135}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{ Deg_45}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{ Deg135}}, }; - static const float AmbiMatrix1O[][MaxAmbiChannels]{ - { 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 }, - }, AmbiMatrix2O[][MaxAmbiChannels]{ - { 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, }, - }, AmbiMatrix3O[][MaxAmbiChannels]{ - { 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, }, + static constexpr std::array AmbiPoints2O{ + AngularPoint{EvRadians{-Deg_32}, AzRadians{ 0.0f}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{ Deg_58}}, + AngularPoint{EvRadians{ Deg_58}, AzRadians{ Deg_90}}, + AngularPoint{EvRadians{ Deg_32}, AzRadians{ 0.0f}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{ Deg122}}, + AngularPoint{EvRadians{-Deg_58}, AzRadians{-Deg_90}}, + AngularPoint{EvRadians{-Deg_32}, AzRadians{ Deg180}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{-Deg122}}, + AngularPoint{EvRadians{ Deg_58}, AzRadians{-Deg_90}}, + AngularPoint{EvRadians{ Deg_32}, AzRadians{ Deg180}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{-Deg_58}}, + AngularPoint{EvRadians{-Deg_58}, AzRadians{ Deg_90}}, }; - static const float AmbiOrderHFGain1O[MaxAmbiOrder+1]{ + static constexpr std::array AmbiPoints3O{ + AngularPoint{EvRadians{ Deg_69}, AzRadians{-Deg_90}}, + AngularPoint{EvRadians{ Deg_69}, AzRadians{ Deg_90}}, + AngularPoint{EvRadians{-Deg_69}, AzRadians{-Deg_90}}, + AngularPoint{EvRadians{-Deg_69}, AzRadians{ Deg_90}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{-Deg_69}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{-Deg111}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{ Deg_69}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{ Deg111}}, + AngularPoint{EvRadians{ Deg_21}, AzRadians{ 0.0f}}, + AngularPoint{EvRadians{ Deg_21}, AzRadians{ Deg180}}, + AngularPoint{EvRadians{-Deg_21}, AzRadians{ 0.0f}}, + AngularPoint{EvRadians{-Deg_21}, AzRadians{ Deg180}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{-Deg_45}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{-Deg135}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{ Deg_45}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{ Deg135}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{-Deg_45}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{-Deg135}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{ Deg_45}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{ Deg135}}, + }; + static constexpr std::array AmbiMatrix1O{ + ChannelCoeffs{1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f}, + ChannelCoeffs{1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f}, + ChannelCoeffs{1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f}, + ChannelCoeffs{1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f}, + ChannelCoeffs{1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f}, + ChannelCoeffs{1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f}, + ChannelCoeffs{1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f}, + ChannelCoeffs{1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f}, + }; + static constexpr std::array AmbiMatrix2O{ + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + }; + static constexpr std::array AmbiMatrix3O{ + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + ChannelCoeffs{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}, + }; + static constexpr std::array AmbiOrderHFGain1O{ /*ENRGY*/ 2.000000000e+00f, 1.154700538e+00f - }, AmbiOrderHFGain2O[MaxAmbiOrder+1]{ + }; + static constexpr std::array AmbiOrderHFGain2O{ /*ENRGY*/ 1.825741858e+00f, 1.414213562e+00f, 7.302967433e-01f /*AMP 1.000000000e+00f, 7.745966692e-01f, 4.000000000e-01f*/ /*RMS 9.128709292e-01f, 7.071067812e-01f, 3.651483717e-01f*/ - }, AmbiOrderHFGain3O[MaxAmbiOrder+1]{ + }; + static constexpr std::array AmbiOrderHFGain3O{ /*ENRGY 1.865086714e+00f, 1.606093894e+00f, 1.142055301e+00f, 5.683795528e-01f*/ /*AMP*/ 1.000000000e+00f, 8.611363116e-01f, 6.123336207e-01f, 3.047469850e-01f /*RMS 8.340921354e-01f, 7.182670250e-01f, 5.107426573e-01f, 2.541870634e-01f*/ }; - 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"); + static_assert(AmbiPoints1O.size() == AmbiMatrix1O.size(), "First-Order Ambisonic HRTF mismatch"); + static_assert(AmbiPoints2O.size() == AmbiMatrix2O.size(), "Second-Order Ambisonic HRTF mismatch"); + static_assert(AmbiPoints3O.size() == AmbiMatrix3O.size(), "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 @@ -841,32 +937,32 @@ void InitHrtfPanning(ALCdevice *device) */ device->mRenderMode = RenderMode::Hrtf; uint ambi_order{1}; - if(auto modeopt = device->configValue(nullptr, "hrtf-mode")) + if(auto modeopt = device->configValue({}, "hrtf-mode")) { struct HrtfModeEntry { - char name[8]; + std::string_view name; RenderMode mode; uint order; }; - static const HrtfModeEntry hrtf_modes[]{ - { "full", RenderMode::Hrtf, 1 }, - { "ambi1", RenderMode::Normal, 1 }, - { "ambi2", RenderMode::Normal, 2 }, - { "ambi3", RenderMode::Normal, 3 }, + constexpr std::array hrtf_modes{ + HrtfModeEntry{"full"sv, RenderMode::Hrtf, 1}, + HrtfModeEntry{"ambi1"sv, RenderMode::Normal, 1}, + HrtfModeEntry{"ambi2"sv, RenderMode::Normal, 2}, + HrtfModeEntry{"ambi3"sv, RenderMode::Normal, 3}, }; - const char *mode{modeopt->c_str()}; - if(al::strcasecmp(mode, "basic") == 0) + std::string_view mode{*modeopt}; + if(al::case_compare(mode, "basic"sv) == 0) { - ERR("HRTF mode \"%s\" deprecated, substituting \"%s\"\n", mode, "ambi2"); + ERR("HRTF mode \"%s\" deprecated, substituting \"%s\"\n", modeopt->c_str(), "ambi2"); mode = "ambi2"; } auto match_entry = [mode](const HrtfModeEntry &entry) -> bool - { return al::strcasecmp(mode, entry.name) == 0; }; - auto iter = std::find_if(std::begin(hrtf_modes), std::end(hrtf_modes), match_entry); - if(iter == std::end(hrtf_modes)) - ERR("Unexpected hrtf-mode: %s\n", mode); + { return al::case_compare(mode, entry.name) == 0; }; + auto iter = std::find_if(hrtf_modes.begin(), hrtf_modes.end(), match_entry); + if(iter == hrtf_modes.end()) + ERR("Unexpected hrtf-mode: %s\n", modeopt->c_str()); else { device->mRenderMode = iter->mode; @@ -874,17 +970,13 @@ void InitHrtfPanning(ALCdevice *device) } } TRACE("%u%s order %sHRTF rendering enabled, using \"%s\"\n", ambi_order, - (((ambi_order%100)/10) == 1) ? "th" : - ((ambi_order%10) == 1) ? "st" : - ((ambi_order%10) == 2) ? "nd" : - ((ambi_order%10) == 3) ? "rd" : "th", - (device->mRenderMode == RenderMode::Hrtf) ? "+ Full " : "", + GetCounterSuffix(ambi_order), (device->mRenderMode == RenderMode::Hrtf) ? "+ Full " : "", device->mHrtfName.c_str()); bool perHrirMin{false}; - al::span AmbiPoints{AmbiPoints1O}; - const float (*AmbiMatrix)[MaxAmbiChannels]{AmbiMatrix1O}; - al::span AmbiOrderHFGain{AmbiOrderHFGain1O}; + auto AmbiPoints = al::span{AmbiPoints1O}.subspan(0); + auto AmbiMatrix = al::span{AmbiMatrix1O}.subspan(0); + auto AmbiOrderHFGain = al::span{AmbiOrderHFGain1O}; if(ambi_order >= 3) { perHrirMin = true; @@ -902,10 +994,9 @@ void InitHrtfPanning(ALCdevice *device) device->m2DMixing = false; const size_t count{AmbiChannelsFromOrder(ambi_order)}; - std::transform(AmbiIndex::FromACN.begin(), AmbiIndex::FromACN.begin()+count, - std::begin(device->Dry.AmbiMap), - [](const uint8_t &index) noexcept { return BFChannelConfig{1.0f, index}; } - ); + const auto acnmap = al::span{AmbiIndex::FromACN}.first(count); + std::transform(acnmap.begin(), acnmap.end(), device->Dry.AmbiMap.begin(), + [](const uint8_t &index) noexcept { return BFChannelConfig{1.0f, index}; }); AllocChannels(device, count, device->channelsFromFmt()); HrtfStore *Hrtf{device->mHrtf.get()}; @@ -917,16 +1008,16 @@ void InitHrtfPanning(ALCdevice *device) InitNearFieldCtrl(device, Hrtf->mFields[0].distance, ambi_order, true); } -void InitUhjPanning(ALCdevice *device) +void InitUhjPanning(al::Device *device) { /* UHJ is always 2D first-order. */ - constexpr size_t count{Ambi2DChannelsFromOrder(1)}; + static constexpr size_t count{Ambi2DChannelsFromOrder(1)}; device->mAmbiOrder = 1; device->m2DMixing = true; - auto acnmap_begin = AmbiIndex::FromFuMa2D.begin(); - std::transform(acnmap_begin, acnmap_begin + count, std::begin(device->Dry.AmbiMap), + const auto acnmap = al::span{AmbiIndex::FromFuMa2D}.first(); + std::transform(acnmap.cbegin(), acnmap.cend(), device->Dry.AmbiMap.begin(), [](const uint8_t &acn) noexcept -> BFChannelConfig { return BFChannelConfig{1.0f/AmbiScale::FromUHJ[acn], acn}; }); AllocChannels(device, count, device->channelsFromFmt()); @@ -934,7 +1025,7 @@ void InitUhjPanning(ALCdevice *device) } // namespace -void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional stereomode) +void aluInitRenderer(al::Device *device, int hrtf_id, std::optional stereomode) { /* Hold the HRTF the device last used, in case it's used again. */ HrtfStorePtr old_hrtf{std::move(device->mHrtf)}; @@ -961,6 +1052,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional> decoder_store; + std::unique_ptr> decoder_store; DecoderView decoder{}; - float speakerdists[MAX_OUTPUT_CHANNELS]{}; + std::array speakerdists{}; auto load_config = [device,&decoder_store,&decoder,&speakerdists](const char *config) { AmbDecConf conf{}; @@ -981,13 +1073,13 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optionalc_str()); return false; } - else if(conf.NumSpeakers > MAX_OUTPUT_CHANNELS) + if(conf.Speakers.size() > MaxOutputChannels) { - ERR("Unsupported decoder speaker count %zu (max %d)\n", conf.NumSpeakers, - MAX_OUTPUT_CHANNELS); + ERR("Unsupported decoder speaker count %zu (max %zu)\n", conf.Speakers.size(), + MaxOutputChannels); return false; } - else if(conf.ChanMask > Ambi3OrderMask) + if(conf.ChanMask > Ambi3OrderMask) { ERR("Unsupported decoder channel mask 0x%04x (max 0x%x)\n", conf.ChanMask, Ambi3OrderMask); @@ -996,12 +1088,15 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optionalFmtChans), conf.Description.c_str()); - device->mXOverFreq = clampf(conf.XOverFreq, 100.0f, 1000.0f); + device->mXOverFreq = std::clamp(conf.XOverFreq, 100.0f, 1000.0f); - decoder_store = std::make_unique>(); + decoder_store = std::make_unique>(); decoder = MakeDecoderView(device, &conf, *decoder_store); - for(size_t i{0};i < decoder.mChannels.size();++i) - speakerdists[i] = conf.Speakers[i].Distance; + + const auto confspeakers = al::span{std::as_const(conf.Speakers)} + .first(decoder.mChannels.size()); + std::transform(confspeakers.cbegin(), confspeakers.cend(), speakerdists.begin(), + std::mem_fn(&AmbDecConf::SpeakerConf::Distance)); return true; }; bool usingCustom{false}; @@ -1019,8 +1114,8 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optionalRealOut.ChannelIndex[FrontCenter] != InvalidChannelIndex && device->RealOut.ChannelIndex[FrontLeft] != InvalidChannelIndex && device->RealOut.ChannelIndex[FrontRight] != InvalidChannelIndex - && device->getConfigValueBool(nullptr, "front-stablizer", false) != 0}; - const bool hqdec{device->getConfigValueBool("decoder", "hq-mode", true) != 0}; + && device->getConfigValueBool({}, "front-stablizer", false)}; + const bool hqdec{device->getConfigValueBool("decoder", "hq-mode", true)}; InitPanning(device, hqdec, stablize, decoder); if(decoder) { @@ -1043,8 +1138,8 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optionalAmbiDecoder.get()}) { - device->PostProcess = ambidec->hasStablizer() ? &ALCdevice::ProcessAmbiDecStablized - : &ALCdevice::ProcessAmbiDec; + device->PostProcess = ambidec->hasStablizer() ? &al::Device::ProcessAmbiDecStablized + : &al::Device::ProcessAmbiDec; } return; } @@ -1061,7 +1156,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional= 0 && static_cast(hrtf_id) < device->mHrtfList.size()) { - const std::string &hrtfname = device->mHrtfList[static_cast(hrtf_id)]; + const std::string_view hrtfname{device->mHrtfList[static_cast(hrtf_id)]}; if(HrtfStorePtr hrtf{GetLoadedHrtf(hrtfname, device->Frequency)}) { device->mHrtf = std::move(hrtf); @@ -1071,7 +1166,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optionalmHrtf) { - for(const auto &hrtfname : device->mHrtfList) + for(const std::string_view hrtfname : device->mHrtfList) { if(HrtfStorePtr hrtf{GetLoadedHrtf(hrtfname, device->Frequency)}) { @@ -1088,14 +1183,14 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optionalmHrtf.get()}; device->mIrSize = hrtf->mIrSize; - if(auto hrtfsizeopt = device->configValue(nullptr, "hrtf-size")) + if(auto hrtfsizeopt = device->configValue({}, "hrtf-size")) { if(*hrtfsizeopt > 0 && *hrtfsizeopt < device->mIrSize) - device->mIrSize = maxu(*hrtfsizeopt, MinIrLength); + device->mIrSize = std::max(*hrtfsizeopt, MinIrLength); } InitHrtfPanning(device); - device->PostProcess = &ALCdevice::ProcessHrtf; + device->PostProcess = &al::Device::ProcessHrtf; device->mHrtfStatus = ALC_HRTF_ENABLED_SOFT; return; } @@ -1104,39 +1199,43 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optionalmUhjEncoder = std::make_unique(); + ftype = "IIR"sv; break; case UhjQualityType::FIR256: device->mUhjEncoder = std::make_unique>(); + ftype = "FIR-256"sv; break; case UhjQualityType::FIR512: device->mUhjEncoder = std::make_unique>(); + ftype = "FIR-512"sv; break; } assert(device->mUhjEncoder != nullptr); - TRACE("UHJ enabled\n"); + TRACE("UHJ enabled (%.*s encoder)\n", al::sizei(ftype), ftype.data()); InitUhjPanning(device); - device->PostProcess = &ALCdevice::ProcessUhj; + device->PostProcess = &al::Device::ProcessUhj; return; } device->mRenderMode = RenderMode::Pairwise; if(device->Type != DeviceType::Loopback) { - if(auto cflevopt = device->configValue(nullptr, "cf_level")) + if(auto cflevopt = device->configValue({}, "cf_level")) { if(*cflevopt > 0 && *cflevopt <= 6) { - device->Bs2b = std::make_unique(); - bs2b_set_params(device->Bs2b.get(), *cflevopt, - static_cast(device->Frequency)); + auto bs2b = std::make_unique(); + bs2b->set_params(*cflevopt, static_cast(device->Frequency)); + device->Bs2b = std::move(bs2b); TRACE("BS2B enabled\n"); InitPanning(device); - device->PostProcess = &ALCdevice::ProcessBs2b; + device->PostProcess = &al::Device::ProcessBs2b; return; } } @@ -1144,7 +1243,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optionalPostProcess = &ALCdevice::ProcessAmbiDec; + device->PostProcess = &al::Device::ProcessAmbiDec; } @@ -1155,10 +1254,9 @@ void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context) slot->mWetBuffer.resize(count); - 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}; }); + const auto acnmap = al::span{AmbiIndex::FromACN}.first(count); + const auto iter = std::transform(acnmap.cbegin(), acnmap.cend(), slot->Wet.AmbiMap.begin(), + [](const uint8_t &acn) noexcept -> BFChannelConfig { return BFChannelConfig{1.0f, acn}; }); std::fill(iter, slot->Wet.AmbiMap.end(), BFChannelConfig{}); slot->Wet.Buffer = slot->mWetBuffer; } diff --git a/3rdparty/openal/cmake/FindJACK.cmake b/3rdparty/openal/cmake/FindJACK.cmake index b72fe3f9a91e..c85d0643f392 100644 --- a/3rdparty/openal/cmake/FindJACK.cmake +++ b/3rdparty/openal/cmake/FindJACK.cmake @@ -43,7 +43,7 @@ find_path(JACK_INCLUDE_DIR NAMES jack/jack.h DOC "The JACK include directory" ) -find_library(JACK_LIBRARY NAMES jack +find_library(JACK_LIBRARY NAMES jack jack64 DOC "The JACK library" ) diff --git a/3rdparty/openal/cmake/FindSndIO.cmake b/3rdparty/openal/cmake/FindSndIO.cmake new file mode 100644 index 000000000000..eb4a2587be6b --- /dev/null +++ b/3rdparty/openal/cmake/FindSndIO.cmake @@ -0,0 +1,31 @@ +# - Find SndIO includes and libraries +# +# SNDIO_FOUND - True if SNDIO_INCLUDE_DIR & SNDIO_LIBRARY are found +# SNDIO_LIBRARIES - Set when SNDIO_LIBRARY is found +# SNDIO_INCLUDE_DIRS - Set when SNDIO_INCLUDE_DIR is found +# +# SNDIO_INCLUDE_DIR - where to find sndio.h, etc. +# SNDIO_LIBRARY - the sndio library +# + +find_path(SNDIO_INCLUDE_DIR + NAMES sndio.h + DOC "The SndIO include directory" +) + +find_library(SNDIO_LIBRARY + NAMES sndio + DOC "The SndIO library" +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(SndIO + REQUIRED_VARS SNDIO_LIBRARY SNDIO_INCLUDE_DIR +) + +if(SNDIO_FOUND) + set(SNDIO_LIBRARIES ${SNDIO_LIBRARY}) + set(SNDIO_INCLUDE_DIRS ${SNDIO_INCLUDE_DIR}) +endif() + +mark_as_advanced(SNDIO_INCLUDE_DIR SNDIO_LIBRARY) diff --git a/3rdparty/openal/cmake/FindSoundIO.cmake b/3rdparty/openal/cmake/FindSoundIO.cmake deleted file mode 100644 index 10450254d213..000000000000 --- a/3rdparty/openal/cmake/FindSoundIO.cmake +++ /dev/null @@ -1,32 +0,0 @@ -# - Find SoundIO (sndio) includes and libraries -# -# SOUNDIO_FOUND - True if SOUNDIO_INCLUDE_DIR & SOUNDIO_LIBRARY are -# found -# SOUNDIO_LIBRARIES - Set when SOUNDIO_LIBRARY is found -# SOUNDIO_INCLUDE_DIRS - Set when SOUNDIO_INCLUDE_DIR is found -# -# SOUNDIO_INCLUDE_DIR - where to find sndio.h, etc. -# SOUNDIO_LIBRARY - the sndio library -# - -find_path(SOUNDIO_INCLUDE_DIR - NAMES sndio.h - DOC "The SoundIO include directory" -) - -find_library(SOUNDIO_LIBRARY - NAMES sndio - DOC "The SoundIO library" -) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(SoundIO - REQUIRED_VARS SOUNDIO_LIBRARY SOUNDIO_INCLUDE_DIR -) - -if(SOUNDIO_FOUND) - set(SOUNDIO_LIBRARIES ${SOUNDIO_LIBRARY}) - set(SOUNDIO_INCLUDE_DIRS ${SOUNDIO_INCLUDE_DIR}) -endif() - -mark_as_advanced(SOUNDIO_INCLUDE_DIR SOUNDIO_LIBRARY) diff --git a/3rdparty/openal/common/alassert.cpp b/3rdparty/openal/common/alassert.cpp new file mode 100644 index 000000000000..aa44841e428b --- /dev/null +++ b/3rdparty/openal/common/alassert.cpp @@ -0,0 +1,44 @@ + +#include "alassert.h" + +#include +#include + +namespace { + +[[noreturn]] +void throw_error(const std::string &message) +{ + throw std::runtime_error{message}; +} + +} /* namespace */ + +namespace al { + +[[noreturn]] +void do_assert(const char *message, int linenum, const char *filename, const char *funcname) noexcept +{ + /* Throwing an exception that tries to leave a noexcept function will + * hopefully cause the system to provide info about the caught exception in + * an error dialog. At least on Linux, this results in the process printing + * + * terminate called after throwing an instance of 'std::runtime_error' + * what(): + * + * before terminating from a SIGABRT. Hopefully Windows and Mac will do the + * appropriate things with the message to alert the user about an abnormal + * termination. + */ + auto errstr = std::string{filename}; + errstr += ':'; + errstr += std::to_string(linenum); + errstr += ": "; + errstr += funcname; + errstr += ": "; + errstr += message; + + throw_error(errstr); +} + +} /* namespace al */ diff --git a/3rdparty/openal/common/alassert.h b/3rdparty/openal/common/alassert.h new file mode 100644 index 000000000000..3797e96d5bd1 --- /dev/null +++ b/3rdparty/openal/common/alassert.h @@ -0,0 +1,24 @@ +#ifndef AL_ASSERT_H +#define AL_ASSERT_H + +#include + +#include "opthelpers.h" + +namespace al { + +[[noreturn]] +void do_assert(const char *message, int linenum, const char *filename, const char *funcname) noexcept; + +} /* namespace al */ + +/* A custom assert macro that is not compiled out for Release/NDEBUG builds, + * making it an appropriate replacement for assert() checks that must not be + * ignored. + */ +#define alassert(cond) do { \ + if(!(cond)) UNLIKELY \ + al::do_assert("Assertion '" #cond "' failed", __LINE__, __FILE__, std::data(__func__)); \ +} while(0) + +#endif /* AL_ASSERT_H */ diff --git a/3rdparty/openal/common/albit.h b/3rdparty/openal/common/albit.h index 962eb0aae458..b5c10d718066 100644 --- a/3rdparty/openal/common/albit.h +++ b/3rdparty/openal/common/albit.h @@ -1,7 +1,11 @@ #ifndef AL_BIT_H #define AL_BIT_H +#include +#include +#ifndef __GNUC__ #include +#endif #include #include #include @@ -17,9 +21,19 @@ std::enable_if_t, To> bit_cast(const From &src) noexcept { - std::aligned_storage_t dst; - std::memcpy(&dst, &src, sizeof(To)); - return *std::launder(reinterpret_cast(&dst)); + alignas(To) std::array dst; + std::memcpy(dst.data(), &src, sizeof(To)); + return *std::launder(reinterpret_cast(dst.data())); +} + +template +std::enable_if_t, +T> byteswap(T value) noexcept +{ + static_assert(std::has_unique_object_representations_v); + auto bytes = al::bit_cast>(value); + std::reverse(bytes.begin(), bytes.end()); + return al::bit_cast(bytes); } #ifdef __BYTE_ORDER__ diff --git a/3rdparty/openal/common/alcomplex.cpp b/3rdparty/openal/common/alcomplex.cpp index dad62d0a1910..ae8bc41cef4b 100644 --- a/3rdparty/openal/common/alcomplex.cpp +++ b/3rdparty/openal/common/alcomplex.cpp @@ -4,10 +4,11 @@ #include "alcomplex.h" #include +#include #include -#include #include #include +#include #include #include "albit.h" @@ -19,32 +20,33 @@ namespace { using ushort = unsigned short; -using ushort2 = std::pair; +using ushort2 = std::array; +using complex_d = std::complex; -constexpr size_t BitReverseCounter(size_t log2_size) noexcept +constexpr std::size_t BitReverseCounter(std::size_t log2_size) noexcept { /* Some magic math that calculates the number of swaps needed for a * sequence of bit-reversed indices when index < reversed_index. */ - return (1u<<(log2_size-1)) - (1u<<((log2_size-1u)/2u)); + return (1_zu<<(log2_size-1)) - (1_zu<<((log2_size-1_zu)/2_zu)); } -template +template struct BitReverser { static_assert(N <= sizeof(ushort)*8, "Too many bits for the bit-reversal table."); - ushort2 mData[BitReverseCounter(N)]{}; + std::array mData{}; constexpr BitReverser() { - const size_t fftsize{1u << N}; - size_t ret_i{0}; + const std::size_t fftsize{1u << N}; + std::size_t ret_i{0}; /* Bit-reversal permutation applied to a sequence of fftsize items. */ - for(size_t idx{1u};idx < fftsize-1;++idx) + for(std::size_t idx{1u};idx < fftsize-1;++idx) { - size_t revidx{idx}; + std::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); @@ -54,8 +56,8 @@ struct BitReverser { if(idx < revidx) { - mData[ret_i].first = static_cast(idx); - mData[ret_i].second = static_cast(revidx); + mData[ret_i][0] = static_cast(idx); + mData[ret_i][1] = static_cast(revidx); ++ret_i; } } @@ -109,43 +111,41 @@ constexpr std::array,gBitReverses.size()-1> gArgAngle{{ } // namespace -template -std::enable_if_t::value> -complex_fft(const al::span> buffer, const al::type_identity_t sign) +void complex_fft(const al::span> buffer, const double sign) { - const size_t fftsize{buffer.size()}; + const std::size_t fftsize{buffer.size()}; /* Get the number of bits used for indexing. Simplifies bit-reversal and * the main loop count. */ - const size_t log2_size{static_cast(al::countr_zero(fftsize))}; + const std::size_t log2_size{static_cast(al::countr_zero(fftsize))}; if(log2_size < gBitReverses.size()) LIKELY { for(auto &rev : gBitReverses[log2_size]) - std::swap(buffer[rev.first], buffer[rev.second]); + std::swap(buffer[rev[0]], buffer[rev[1]]); /* Iterative form of Danielson-Lanczos lemma */ - for(size_t i{0};i < log2_size;++i) + for(std::size_t i{0};i < log2_size;++i) { - const size_t step2{1_uz << i}; - const size_t step{2_uz << i}; + const std::size_t step2{1_uz << i}; + const std::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) + for(std::size_t k{0};k < fftsize;k+=step) { - std::complex temp{buffer[k+step2]}; + const complex_d temp{buffer[k+step2]}; buffer[k+step2] = buffer[k] - temp; buffer[k] += temp; } - const std::complex w{gArgAngle[i].real(), gArgAngle[i].imag()*sign}; - std::complex u{w}; - for(size_t j{1};j < step2;j++) + const complex_d w{gArgAngle[i].real(), gArgAngle[i].imag()*sign}; + complex_d u{w}; + for(std::size_t j{1};j < step2;j++) { - for(size_t k{j};k < fftsize;k+=step) + for(std::size_t k{j};k < fftsize;k+=step) { - std::complex temp{buffer[k+step2] * u}; + const complex_d temp{buffer[k+step2] * u}; buffer[k+step2] = buffer[k] - temp; buffer[k] += temp; } @@ -155,9 +155,11 @@ complex_fft(const al::span> buffer, const al::type_identity_t } else { - for(size_t idx{1u};idx < fftsize-1;++idx) + assert(log2_size < 32); + + for(std::size_t idx{1u};idx < fftsize-1;++idx) { - size_t revidx{idx}; + std::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); @@ -169,26 +171,26 @@ complex_fft(const al::span> buffer, const al::type_identity_t std::swap(buffer[idx], buffer[revidx]); } - const Real pi{al::numbers::pi_v * sign}; - for(size_t i{0};i < log2_size;++i) + const double pi{al::numbers::pi * sign}; + for(std::size_t i{0};i < log2_size;++i) { - const size_t step2{1_uz << i}; - const size_t step{2_uz << i}; - for(size_t k{0};k < fftsize;k+=step) + const std::size_t step2{1_uz << i}; + const std::size_t step{2_uz << i}; + for(std::size_t k{0};k < fftsize;k+=step) { - std::complex temp{buffer[k+step2]}; + const complex_d temp{buffer[k+step2]}; buffer[k+step2] = buffer[k] - temp; buffer[k] += temp; } - const Real arg{pi / static_cast(step2)}; - const std::complex w{std::polar(Real{1}, arg)}; - std::complex u{w}; - for(size_t j{1};j < step2;j++) + const double arg{pi / static_cast(step2)}; + const complex_d w{std::polar(1.0, arg)}; + complex_d u{w}; + for(std::size_t j{1};j < step2;j++) { - for(size_t k{j};k < fftsize;k+=step) + for(std::size_t k{j};k < fftsize;k+=step) { - std::complex temp{buffer[k+step2] * u}; + const complex_d temp{buffer[k+step2] * u}; buffer[k+step2] = buffer[k] - temp; buffer[k] += temp; } @@ -200,13 +202,11 @@ complex_fft(const al::span> buffer, const al::type_identity_t void complex_hilbert(const al::span> buffer) { - using namespace std::placeholders; - inverse_fft(buffer); const double inverse_size = 1.0/static_cast(buffer.size()); auto bufiter = buffer.begin(); - const auto halfiter = bufiter + (buffer.size()>>1); + const auto halfiter = bufiter + ptrdiff_t(buffer.size()>>1); *bufiter *= inverse_size; ++bufiter; bufiter = std::transform(bufiter, halfiter, bufiter, @@ -217,7 +217,3 @@ void complex_hilbert(const al::span> buffer) forward_fft(buffer); } - - -template void complex_fft<>(const al::span> buffer, const float sign); -template void complex_fft<>(const al::span> buffer, const double sign); diff --git a/3rdparty/openal/common/alcomplex.h b/3rdparty/openal/common/alcomplex.h index 042a323266b5..9f12cef32cd2 100644 --- a/3rdparty/openal/common/alcomplex.h +++ b/3rdparty/openal/common/alcomplex.h @@ -2,7 +2,6 @@ #define ALCOMPLEX_H #include -#include #include "alspan.h" @@ -11,25 +10,21 @@ * 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 -std::enable_if_t::value> -complex_fft(const al::span> buffer, const al::type_identity_t sign); +void complex_fft(const al::span> 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 -void forward_fft(const al::span buffer) -{ complex_fft(al::span{buffer}, -1); } +inline void forward_fft(const al::span> buffer) +{ complex_fft(buffer, -1.0); } /** * Calculate the time-domain signal of the frequency-domain response in the * provided buffer, which MUST BE power of two. */ -template -void inverse_fft(const al::span buffer) -{ complex_fft(al::span{buffer}, 1); } +inline void inverse_fft(const al::span> buffer) +{ complex_fft(buffer, +1.0); } /** * Calculate the complex helical sequence (discrete-time analytical signal) of diff --git a/3rdparty/openal/common/aldeque.h b/3rdparty/openal/common/aldeque.h deleted file mode 100644 index 3f99bf007469..000000000000 --- a/3rdparty/openal/common/aldeque.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef ALDEQUE_H -#define ALDEQUE_H - -#include - -#include "almalloc.h" - - -namespace al { - -template -using deque = std::deque>; - -} // namespace al - -#endif /* ALDEQUE_H */ diff --git a/3rdparty/openal/common/alfstream.cpp b/3rdparty/openal/common/alfstream.cpp deleted file mode 100644 index 8991ce0352a0..000000000000 --- a/3rdparty/openal/common/alfstream.cpp +++ /dev/null @@ -1,26 +0,0 @@ - -#include "config.h" - -#include "alfstream.h" - -#include "strutils.h" - -#ifdef _WIN32 - -namespace al { - -ifstream::ifstream(const char *filename, std::ios_base::openmode mode) - : std::ifstream{utf8_to_wstr(filename).c_str(), mode} -{ } - -void ifstream::open(const char *filename, std::ios_base::openmode mode) -{ - std::wstring wstr{utf8_to_wstr(filename)}; - std::ifstream::open(wstr.c_str(), mode); -} - -ifstream::~ifstream() = default; - -} // namespace al - -#endif diff --git a/3rdparty/openal/common/alfstream.h b/3rdparty/openal/common/alfstream.h deleted file mode 100644 index 62b2e12c7568..000000000000 --- a/3rdparty/openal/common/alfstream.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef AL_FSTREAM_H -#define AL_FSTREAM_H - -#ifdef _WIN32 - -#include -#include - - -namespace al { - -// Inherit from std::ifstream to accept UTF-8 filenames -class ifstream final : public std::ifstream { -public: - explicit ifstream(const char *filename, std::ios_base::openmode mode=std::ios_base::in); - explicit ifstream(const std::string &filename, std::ios_base::openmode mode=std::ios_base::in) - : ifstream{filename.c_str(), mode} { } - - explicit ifstream(const wchar_t *filename, std::ios_base::openmode mode=std::ios_base::in) - : std::ifstream{filename, mode} { } - explicit ifstream(const std::wstring &filename, std::ios_base::openmode mode=std::ios_base::in) - : ifstream{filename.c_str(), mode} { } - - void open(const char *filename, std::ios_base::openmode mode=std::ios_base::in); - void open(const std::string &filename, std::ios_base::openmode mode=std::ios_base::in) - { open(filename.c_str(), mode); } - - ~ifstream() override; -}; - -} // namespace al - -#else /* _WIN32 */ - -#include - -namespace al { - -using ifstream = std::ifstream; - -} // namespace al - -#endif /* _WIN32 */ - -#endif /* AL_FSTREAM_H */ diff --git a/3rdparty/openal/common/almalloc.cpp b/3rdparty/openal/common/almalloc.cpp deleted file mode 100644 index ad1dc6be082e..000000000000 --- a/3rdparty/openal/common/almalloc.cpp +++ /dev/null @@ -1,61 +0,0 @@ - -#include "config.h" - -#include "almalloc.h" - -#include -#include -#include -#include -#include -#ifdef HAVE_MALLOC_H -#include -#endif - - -void *al_malloc(size_t alignment, size_t size) -{ - assert((alignment & (alignment-1)) == 0); - alignment = std::max(alignment, alignof(std::max_align_t)); - -#if defined(HAVE_POSIX_MEMALIGN) - void *ret{}; - if(posix_memalign(&ret, alignment, size) == 0) - return ret; - return nullptr; -#elif defined(HAVE__ALIGNED_MALLOC) - return _aligned_malloc(size, alignment); -#else - size_t total_size{size + alignment-1 + sizeof(void*)}; - void *base{std::malloc(total_size)}; - if(base != nullptr) - { - void *aligned_ptr{static_cast(base) + sizeof(void*)}; - total_size -= sizeof(void*); - - std::align(alignment, size, aligned_ptr, total_size); - *(static_cast(aligned_ptr)-1) = base; - base = aligned_ptr; - } - return base; -#endif -} - -void *al_calloc(size_t alignment, size_t size) -{ - void *ret{al_malloc(alignment, size)}; - if(ret) std::memset(ret, 0, size); - return ret; -} - -void al_free(void *ptr) noexcept -{ -#if defined(HAVE_POSIX_MEMALIGN) - std::free(ptr); -#elif defined(HAVE__ALIGNED_MALLOC) - _aligned_free(ptr); -#else - if(ptr != nullptr) - std::free(*(static_cast(ptr) - 1)); -#endif -} diff --git a/3rdparty/openal/common/almalloc.h b/3rdparty/openal/common/almalloc.h index 873473cacafe..e3a7bb5217a8 100644 --- a/3rdparty/openal/common/almalloc.h +++ b/3rdparty/openal/common/almalloc.h @@ -3,49 +3,24 @@ #include #include -#include #include -#include #include #include #include +#include -#include "pragmadefs.h" - -void al_free(void *ptr) noexcept; -[[gnu::alloc_align(1), gnu::alloc_size(2), gnu::malloc]] -void *al_malloc(size_t alignment, size_t size); -[[gnu::alloc_align(1), gnu::alloc_size(2), gnu::malloc]] -void *al_calloc(size_t alignment, size_t size); +namespace gsl { +template using owner = T; +} -#define DISABLE_ALLOC() \ +#define DISABLE_ALLOC \ void *operator new(size_t) = delete; \ void *operator new[](size_t) = delete; \ void operator delete(void*) noexcept = delete; \ void operator delete[](void*) noexcept = delete; -#define DEF_NEWDEL(T) \ - void *operator new(size_t size) \ - { \ - static_assert(&operator new == &T::operator new, \ - "Incorrect container type specified"); \ - if(void *ret{al_malloc(alignof(T), size)}) \ - return ret; \ - throw std::bad_alloc(); \ - } \ - void *operator new[](size_t size) { return operator new(size); } \ - void operator delete(void *block) noexcept { al_free(block); } \ - void operator delete[](void *block) noexcept { operator delete(block); } - -#define DEF_PLACE_NEWDEL() \ - void *operator new(size_t /*size*/, void *ptr) noexcept { return ptr; } \ - void *operator new[](size_t /*size*/, void *ptr) noexcept { return ptr; } \ - void operator delete(void *block, void*) noexcept { al_free(block); } \ - void operator delete(void *block) noexcept { al_free(block); } \ - void operator delete[](void *block, void*) noexcept { al_free(block); } \ - void operator delete[](void *block) noexcept { al_free(block); } enum FamCount : size_t { }; @@ -58,56 +33,64 @@ enum FamCount : size_t { }; sizeof(T)); \ } \ \ - void *operator new(size_t /*size*/, FamCount count) \ + gsl::owner operator new(size_t /*size*/, FamCount count) \ { \ - if(void *ret{al_malloc(alignof(T), T::Sizeof(count))}) \ - return ret; \ - throw std::bad_alloc(); \ + const auto alignment = std::align_val_t{alignof(T)}; \ + return ::operator new[](T::Sizeof(count), alignment); \ } \ + void operator delete(gsl::owner block, FamCount) noexcept \ + { ::operator delete[](block, std::align_val_t{alignof(T)}); } \ + void operator delete(gsl::owner block) noexcept \ + { ::operator delete[](block, std::align_val_t{alignof(T)}); } \ void *operator new[](size_t /*size*/) = delete; \ - void operator delete(void *block, FamCount) { al_free(block); } \ - void operator delete(void *block) noexcept { al_free(block); } \ void operator delete[](void* /*block*/) = delete; namespace al { -template +template struct allocator { - static constexpr std::size_t alignment{std::max(Align, alignof(T))}; - - using value_type = T; - using reference = T&; - using const_reference = const T&; - using pointer = T*; - using const_pointer = const T*; + static constexpr auto Alignment = std::max(AlignV, alignof(T)); + static constexpr auto AlignVal = std::align_val_t{Alignment}; + + using value_type = std::remove_cv_t>; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using is_always_equal = std::true_type; - template + template = true> struct rebind { - using other = allocator; + using other = allocator; }; constexpr explicit allocator() noexcept = default; template - constexpr explicit allocator(const allocator&) noexcept { } + constexpr explicit allocator(const allocator&) noexcept + { static_assert(Alignment == allocator::Alignment); } - T *allocate(std::size_t n) + gsl::owner allocate(std::size_t n) { if(n > std::numeric_limits::max()/sizeof(T)) throw std::bad_alloc(); - if(auto p = al_malloc(alignment, n*sizeof(T))) return static_cast(p); - throw std::bad_alloc(); + return static_cast>(::operator new[](n*sizeof(T), AlignVal)); } - void deallocate(T *p, std::size_t) noexcept { al_free(p); } + void deallocate(gsl::owner p, std::size_t) noexcept + { ::operator delete[](gsl::owner{p}, AlignVal); } }; template -constexpr bool operator==(const allocator&, const allocator&) noexcept { return true; } +constexpr bool operator==(const allocator&, const allocator&) noexcept +{ return allocator::Alignment == allocator::Alignment; } template -constexpr bool operator!=(const allocator&, const allocator&) noexcept { return false; } +constexpr bool operator!=(const allocator&, const allocator&) noexcept +{ return allocator::Alignment != allocator::Alignment; } +#ifdef __cpp_lib_to_address +using std::to_address; +#else template constexpr T *to_address(T *p) noexcept { @@ -120,128 +103,52 @@ constexpr auto to_address(const T &p) noexcept { return ::al::to_address(p.operator->()); } - +#endif template constexpr T* construct_at(T *ptr, Args&& ...args) - noexcept(std::is_nothrow_constructible::value) -{ return ::new(static_cast(ptr)) T{std::forward(args)...}; } - - -/* Storage for flexible array data. This is trivially destructible if type T is - * trivially destructible. - */ -template::value> -struct FlexArrayStorage { - const size_t mSize; - union { - char mDummy; - alignas(alignment) T mArray[1]; - }; - - static constexpr size_t Sizeof(size_t count, size_t base=0u) noexcept - { - const size_t len{sizeof(T)*count}; - return std::max(offsetof(FlexArrayStorage,mArray)+len, sizeof(FlexArrayStorage)) + base; - } + noexcept(std::is_nothrow_constructible_v) +{ + /* NOLINTBEGIN(cppcoreguidelines-owning-memory) construct_at doesn't + * necessarily handle the address from an owner, while placement new + * expects to. + */ + return ::new(static_cast(ptr)) T{std::forward(args)...}; + /* NOLINTEND(cppcoreguidelines-owning-memory) */ +} - FlexArrayStorage(size_t size) : mSize{size} - { std::uninitialized_default_construct_n(mArray, mSize); } - ~FlexArrayStorage() = default; - FlexArrayStorage(const FlexArrayStorage&) = delete; - FlexArrayStorage& operator=(const FlexArrayStorage&) = delete; -}; +template +class out_ptr_t { + static_assert(!std::is_same_v); -template -struct FlexArrayStorage { - const size_t mSize; - union { - char mDummy; - alignas(alignment) T mArray[1]; - }; + SP &mRes; + std::variant mPtr{}; - static constexpr size_t Sizeof(size_t count, size_t base) noexcept +public: + out_ptr_t(SP &res) : mRes{res} { } + ~out_ptr_t() { - const size_t len{sizeof(T)*count}; - return std::max(offsetof(FlexArrayStorage,mArray)+len, sizeof(FlexArrayStorage)) + base; + auto set_res = [this](auto &ptr) + { mRes.reset(static_cast(ptr)); }; + std::visit(set_res, mPtr); } + out_ptr_t(const out_ptr_t&) = delete; + out_ptr_t& operator=(const out_ptr_t&) = delete; - FlexArrayStorage(size_t size) : mSize{size} - { std::uninitialized_default_construct_n(mArray, mSize); } - ~FlexArrayStorage() { std::destroy_n(mArray, mSize); } + operator PT*() noexcept + { return &std::get(mPtr); } - FlexArrayStorage(const FlexArrayStorage&) = delete; - FlexArrayStorage& operator=(const FlexArrayStorage&) = delete; + operator void**() noexcept + { return &mPtr.template emplace(); } }; -/* A flexible array type. Used either standalone or at the end of a parent - * struct, with placement new, to have a run-time-sized array that's embedded - * with its size. - */ -template -struct FlexArray { - using element_type = T; - using value_type = std::remove_cv_t; - using index_type = size_t; - using difference_type = ptrdiff_t; - - using pointer = T*; - using const_pointer = const T*; - using reference = T&; - using const_reference = const T&; - - using iterator = pointer; - using const_iterator = const_pointer; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - - using Storage_t_ = FlexArrayStorage; - - Storage_t_ mStore; - - static constexpr index_type Sizeof(index_type count, index_type base=0u) noexcept - { return Storage_t_::Sizeof(count, base); } - static std::unique_ptr Create(index_type count) - { - void *ptr{al_calloc(alignof(FlexArray), Sizeof(count))}; - return std::unique_ptr{al::construct_at(static_cast(ptr), count)}; - } - - FlexArray(index_type size) : mStore{size} { } - ~FlexArray() = default; - - index_type size() const noexcept { return mStore.mSize; } - bool empty() const noexcept { return mStore.mSize == 0; } - - pointer data() noexcept { return mStore.mArray; } - const_pointer data() const noexcept { return mStore.mArray; } - - reference operator[](index_type i) noexcept { return mStore.mArray[i]; } - const_reference operator[](index_type i) const noexcept { return mStore.mArray[i]; } - - reference front() noexcept { return mStore.mArray[0]; } - const_reference front() const noexcept { return mStore.mArray[0]; } - - reference back() noexcept { return mStore.mArray[mStore.mSize-1]; } - const_reference back() const noexcept { return mStore.mArray[mStore.mSize-1]; } - - iterator begin() noexcept { return mStore.mArray; } - const_iterator begin() const noexcept { return mStore.mArray; } - const_iterator cbegin() const noexcept { return mStore.mArray; } - iterator end() noexcept { return mStore.mArray + mStore.mSize; } - const_iterator end() const noexcept { return mStore.mArray + mStore.mSize; } - const_iterator cend() const noexcept { return mStore.mArray + mStore.mSize; } - - reverse_iterator rbegin() noexcept { return end(); } - const_reverse_iterator rbegin() const noexcept { return end(); } - const_reverse_iterator crbegin() const noexcept { return cend(); } - reverse_iterator rend() noexcept { return begin(); } - const_reverse_iterator rend() const noexcept { return begin(); } - const_reverse_iterator crend() const noexcept { return cbegin(); } - - DEF_PLACE_NEWDEL() -}; +template +auto out_ptr(SP &res) +{ + using ptype = typename SP::element_type*; + return out_ptr_t{res}; +} } // namespace al diff --git a/3rdparty/openal/common/alnumbers.h b/3rdparty/openal/common/alnumbers.h index e92d7b871ee5..6d201ad4f50a 100644 --- a/3rdparty/openal/common/alnumbers.h +++ b/3rdparty/openal/common/alnumbers.h @@ -1,11 +1,9 @@ #ifndef COMMON_ALNUMBERS_H #define COMMON_ALNUMBERS_H -#include +#include -namespace al { - -namespace numbers { +namespace al::numbers { namespace detail_ { template @@ -29,8 +27,6 @@ inline constexpr auto inv_pi = inv_pi_v; inline constexpr auto sqrt2 = sqrt2_v; inline constexpr auto sqrt3 = sqrt3_v; -} // namespace numbers - -} // namespace al +} // namespace al::numbers #endif /* COMMON_ALNUMBERS_H */ diff --git a/3rdparty/openal/common/alnumeric.h b/3rdparty/openal/common/alnumeric.h index 759dbc126f0d..2eb097b516f4 100644 --- a/3rdparty/openal/common/alnumeric.h +++ b/3rdparty/openal/common/alnumeric.h @@ -1,15 +1,19 @@ #ifndef AL_NUMERIC_H #define AL_NUMERIC_H +#include "config_simd.h" + #include +#include #include #include #include +#include #include #ifdef HAVE_INTRIN_H #include #endif -#ifdef HAVE_SSE_INTRINSICS +#if HAVE_SSE_INTRINSICS #include #endif @@ -18,78 +22,31 @@ #include "opthelpers.h" -constexpr auto operator "" _i64(unsigned long long n) noexcept { return static_cast(n); } -constexpr auto operator "" _u64(unsigned long long n) noexcept { return static_cast(n); } +constexpr auto operator "" _i64(unsigned long long n) noexcept { return static_cast(n); } +constexpr auto operator "" _u64(unsigned long long n) noexcept { return static_cast(n); } constexpr auto operator "" _z(unsigned long long n) noexcept -{ return static_cast>(n); } -constexpr auto operator "" _uz(unsigned long long n) noexcept { return static_cast(n); } -constexpr auto operator "" _zu(unsigned long long n) noexcept { return static_cast(n); } - - -constexpr inline float minf(float a, float b) noexcept -{ return ((a > b) ? b : a); } -constexpr inline float maxf(float a, float b) noexcept -{ return ((a > b) ? a : b); } -constexpr inline float clampf(float val, float min, float max) noexcept -{ return minf(max, maxf(min, val)); } - -constexpr inline double mind(double a, double b) noexcept -{ return ((a > b) ? b : a); } -constexpr inline double maxd(double a, double b) noexcept -{ return ((a > b) ? a : b); } -constexpr inline double clampd(double val, double min, double max) noexcept -{ return mind(max, maxd(min, val)); } - -constexpr inline unsigned int minu(unsigned int a, unsigned int b) noexcept -{ return ((a > b) ? b : a); } -constexpr inline unsigned int maxu(unsigned int a, unsigned int b) noexcept -{ return ((a > b) ? a : b); } -constexpr inline unsigned int clampu(unsigned int val, unsigned int min, unsigned int max) noexcept -{ return minu(max, maxu(min, val)); } - -constexpr inline int mini(int a, int b) noexcept -{ return ((a > b) ? b : a); } -constexpr inline int maxi(int a, int b) noexcept -{ return ((a > b) ? a : b); } -constexpr inline int clampi(int val, int min, int max) noexcept -{ return mini(max, maxi(min, val)); } - -constexpr inline int64_t mini64(int64_t a, int64_t b) noexcept -{ return ((a > b) ? b : a); } -constexpr inline int64_t maxi64(int64_t a, int64_t b) noexcept -{ return ((a > b) ? a : b); } -constexpr inline int64_t clampi64(int64_t val, int64_t min, int64_t max) noexcept -{ return mini64(max, maxi64(min, val)); } - -constexpr inline uint64_t minu64(uint64_t a, uint64_t b) noexcept -{ return ((a > b) ? b : a); } -constexpr inline uint64_t maxu64(uint64_t a, uint64_t b) noexcept -{ return ((a > b) ? a : b); } -constexpr inline uint64_t clampu64(uint64_t val, uint64_t min, uint64_t max) noexcept -{ return minu64(max, maxu64(min, val)); } - -constexpr inline size_t minz(size_t a, size_t b) noexcept -{ return ((a > b) ? b : a); } -constexpr inline size_t maxz(size_t a, size_t b) noexcept -{ return ((a > b) ? a : b); } -constexpr inline size_t clampz(size_t val, size_t min, size_t max) noexcept -{ return minz(max, maxz(min, val)); } - - -constexpr inline float lerpf(float val1, float val2, float mu) noexcept -{ return val1 + (val2-val1)*mu; } -constexpr inline float cubic(float val1, float val2, float val3, float val4, float mu) noexcept +{ return static_cast>(n); } +constexpr auto operator "" _uz(unsigned long long n) noexcept { return static_cast(n); } +constexpr auto operator "" _zu(unsigned long long n) noexcept { return static_cast(n); } + + +constexpr auto GetCounterSuffix(size_t count) noexcept -> const char* { - const float mu2{mu*mu}, mu3{mu2*mu}; - const float a0{-0.5f*mu3 + mu2 + -0.5f*mu}; - const float a1{ 1.5f*mu3 + -2.5f*mu2 + 1.0f}; - const float a2{-1.5f*mu3 + 2.0f*mu2 + 0.5f*mu}; - const float a3{ 0.5f*mu3 + -0.5f*mu2}; - return val1*a0 + val2*a1 + val3*a2 + val4*a3; + auto &suffix = (((count%100)/10) == 1) ? "th" : + ((count%10) == 1) ? "st" : + ((count%10) == 2) ? "nd" : + ((count%10) == 3) ? "rd" : "th"; + return std::data(suffix); } +constexpr auto lerpf(float val1, float val2, float mu) noexcept -> float +{ return val1 + (val2-val1)*mu; } +constexpr auto lerpd(double val1, double val2, double mu) noexcept -> double +{ 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 { @@ -129,24 +86,21 @@ constexpr T RoundUp(T value, al::type_identity_t r) noexcept */ inline int fastf2i(float f) noexcept { -#if defined(HAVE_SSE_INTRINSICS) +#if HAVE_SSE_INTRINSICS return _mm_cvt_ss2si(_mm_set_ss(f)); -#elif defined(_MSC_VER) && defined(_M_IX86_FP) +#elif defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP == 0 int i; __asm fld f __asm fistp i return i; -#elif (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) +#elif (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) \ + && !defined(__SSE_MATH__) int i; -#ifdef __SSE_MATH__ - __asm__("cvtss2si %1, %0" : "=r"(i) : "x"(f)); -#else __asm__ __volatile__("fistpl %0" : "=m"(i) : "t"(f) : "st"); -#endif return i; #else @@ -160,7 +114,7 @@ inline unsigned int fastf2u(float f) noexcept /** Converts float-to-int using standard behavior (truncation). */ inline int float2int(float f) noexcept { -#if defined(HAVE_SSE_INTRINSICS) +#if HAVE_SSE_INTRINSICS return _mm_cvtt_ss2si(_mm_set_ss(f)); #elif (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP == 0) \ @@ -191,7 +145,7 @@ inline unsigned int float2uint(float f) noexcept /** Converts double-to-int using standard behavior (truncation). */ inline int double2int(double d) noexcept { -#if defined(HAVE_SSE_INTRINSICS) +#if HAVE_SSE_INTRINSICS return _mm_cvttsd_si32(_mm_set_sd(d)); #elif (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP < 2) \ @@ -242,7 +196,7 @@ inline float fast_roundf(float f) noexcept /* Integral limit, where sub-integral precision is not available for * floats. */ - static constexpr float ilim[2]{ + static constexpr std::array ilim{ 8388608.0f /* 0x1.0p+23 */, -8388608.0f /* -0x1.0p+23 */ }; @@ -266,11 +220,15 @@ inline float fast_roundf(float f) noexcept * optimize this out because of non-associative rules on floating-point * math (as long as you don't use -fassociative-math, * -funsafe-math-optimizations, -ffast-math, or -Ofast, in which case this - * may break). + * may break without __builtin_assoc_barrier support). */ +#if HAS_BUILTIN(__builtin_assoc_barrier) + return __builtin_assoc_barrier(f + ilim[sign]) - ilim[sign]; +#else f += ilim[sign]; return f - ilim[sign]; #endif +#endif } @@ -285,9 +243,9 @@ inline float level_mb_to_gain(float x) // Converts gain to level (mB). inline float gain_to_level_mb(float x) { - if (x <= 0.0f) + if(x <= 1e-05f) return -10'000.0f; - return maxf(std::log10(x) * 2'000.0f, -10'000.0f); + return std::max(std::log10(x) * 2'000.0f, -10'000.0f); } #endif /* AL_NUMERIC_H */ diff --git a/3rdparty/openal/common/alsem.cpp b/3rdparty/openal/common/alsem.cpp index 6a92b35cf252..2eeed5929d84 100644 --- a/3rdparty/openal/common/alsem.cpp +++ b/3rdparty/openal/common/alsem.cpp @@ -24,8 +24,6 @@ #include -#include "opthelpers.h" - #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN @@ -39,7 +37,8 @@ semaphore::semaphore(unsigned int initial) { if(initial > static_cast(std::numeric_limits::max())) throw std::system_error(std::make_error_code(std::errc::value_too_large)); - mSem = CreateSemaphore(nullptr, initial, std::numeric_limits::max(), nullptr); + mSem = CreateSemaphoreW(nullptr, static_cast(initial), std::numeric_limits::max(), + nullptr); if(mSem == nullptr) throw std::system_error(std::make_error_code(std::errc::resource_unavailable_try_again)); } diff --git a/3rdparty/openal/common/alsem.h b/3rdparty/openal/common/alsem.h index 9f72d1c6510d..90b393190265 100644 --- a/3rdparty/openal/common/alsem.h +++ b/3rdparty/openal/common/alsem.h @@ -24,7 +24,7 @@ class semaphore { #else using native_type = sem_t; #endif - native_type mSem; + native_type mSem{}; public: semaphore(unsigned int initial=0); diff --git a/3rdparty/openal/common/alspan.h b/3rdparty/openal/common/alspan.h index d5e42324b8c6..4563289e11ce 100644 --- a/3rdparty/openal/common/alspan.h +++ b/3rdparty/openal/common/alspan.h @@ -1,157 +1,252 @@ #ifndef AL_SPAN_H #define AL_SPAN_H -#include +#include #include -#include #include +#include +#include #include +#include +#include "alassert.h" #include "almalloc.h" #include "altraits.h" namespace al { -constexpr size_t dynamic_extent{static_cast(-1)}; +/* This is here primarily to help ensure proper behavior for span's iterators, + * being an actual object with member functions instead of a raw pointer (which + * has requirements like + and - working with ptrdiff_t). This also helps + * silence clang-tidy's pointer arithmetic warnings for span and FlexArray + * iterators. It otherwise behaves like a plain pointer and should optimize + * accordingly. + * + * Shouldn't be needed once we use std::span in C++20. + */ +template +class ptr_wrapper { + static_assert(std::is_pointer_v); + T mPointer{}; + +public: + using value_type = std::remove_pointer_t; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::random_access_iterator_tag; + + explicit constexpr ptr_wrapper(T ptr) : mPointer{ptr} { } + + /* NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ + constexpr auto operator++() noexcept -> ptr_wrapper& { ++mPointer; return *this; } + constexpr auto operator--() noexcept -> ptr_wrapper& { --mPointer; return *this; } + constexpr auto operator++(int) noexcept -> ptr_wrapper + { + auto temp = *this; + ++*this; + return temp; + } + constexpr auto operator--(int) noexcept -> ptr_wrapper + { + auto temp = *this; + --*this; + return temp; + } + + constexpr + auto operator+=(std::ptrdiff_t n) noexcept -> ptr_wrapper& { mPointer += n; return *this; } + constexpr + auto operator-=(std::ptrdiff_t n) noexcept -> ptr_wrapper& { mPointer -= n; return *this; } + + [[nodiscard]] constexpr auto operator*() const noexcept -> value_type& { return *mPointer; } + [[nodiscard]] constexpr auto operator->() const noexcept -> value_type* { return mPointer; } + [[nodiscard]] constexpr + auto operator[](std::size_t idx) const noexcept -> value_type& {return mPointer[idx];} + + [[nodiscard]] friend constexpr + auto operator+(const ptr_wrapper &lhs, std::ptrdiff_t n) noexcept -> ptr_wrapper + { return ptr_wrapper{lhs.mPointer + n}; } + [[nodiscard]] friend constexpr + auto operator+(std::ptrdiff_t n, const ptr_wrapper &rhs) noexcept -> ptr_wrapper + { return ptr_wrapper{n + rhs.mPointer}; } + [[nodiscard]] friend constexpr + auto operator-(const ptr_wrapper &lhs, std::ptrdiff_t n) noexcept -> ptr_wrapper + { return ptr_wrapper{lhs.mPointer - n}; } + + [[nodiscard]] friend constexpr + auto operator-(const ptr_wrapper &lhs, const ptr_wrapper &rhs)noexcept->std::ptrdiff_t + { return lhs.mPointer - rhs.mPointer; } + + [[nodiscard]] friend constexpr + auto operator==(const ptr_wrapper &lhs, const ptr_wrapper &rhs) noexcept -> bool + { return lhs.mPointer == rhs.mPointer; } + [[nodiscard]] friend constexpr + auto operator!=(const ptr_wrapper &lhs, const ptr_wrapper &rhs) noexcept -> bool + { return lhs.mPointer != rhs.mPointer; } + [[nodiscard]] friend constexpr + auto operator<=(const ptr_wrapper &lhs, const ptr_wrapper &rhs) noexcept -> bool + { return lhs.mPointer <= rhs.mPointer; } + [[nodiscard]] friend constexpr + auto operator>=(const ptr_wrapper &lhs, const ptr_wrapper &rhs) noexcept -> bool + { return lhs.mPointer >= rhs.mPointer; } + [[nodiscard]] friend constexpr + auto operator<(const ptr_wrapper &lhs, const ptr_wrapper &rhs) noexcept -> bool + { return lhs.mPointer < rhs.mPointer; } + [[nodiscard]] friend constexpr + auto operator>(const ptr_wrapper &lhs, const ptr_wrapper &rhs) noexcept -> bool + { return lhs.mPointer > rhs.mPointer; } + /* NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ +}; + -template +inline constexpr std::size_t dynamic_extent{static_cast(-1)}; + +template class span; namespace detail_ { template struct is_span_ : std::false_type { }; - template + template struct is_span_> : std::true_type { }; template - constexpr bool is_span_v = is_span_>::value; + inline constexpr bool is_span_v = is_span_>::value; template struct is_std_array_ : std::false_type { }; - template + template struct is_std_array_> : std::true_type { }; template - constexpr bool is_std_array_v = is_std_array_>::value; + inline constexpr bool is_std_array_v = is_std_array_>::value; template - constexpr bool has_size_and_data = false; + inline constexpr bool has_size_and_data = false; template - constexpr bool has_size_and_data())),decltype(std::data(std::declval()))>> = true; template - constexpr bool is_valid_container_type = !is_span_v && !is_std_array_v + inline constexpr bool is_valid_container_type = !is_span_v && !is_std_array_v && !std::is_array::value && has_size_and_data; template - constexpr bool is_array_compatible = std::is_convertible::value; + inline constexpr bool is_array_compatible = std::is_convertible::value; /* NOLINT(*-avoid-c-arrays) */ template - constexpr bool is_valid_container = is_valid_container_type + inline constexpr bool is_valid_container = is_valid_container_type && is_array_compatible()))>,T>; } // namespace detail_ #define REQUIRES(...) std::enable_if_t<(__VA_ARGS__),bool> = true -template +template class span { public: using element_type = T; using value_type = std::remove_cv_t; - using index_type = size_t; - using difference_type = ptrdiff_t; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; using pointer = T*; using const_pointer = const T*; using reference = T&; using const_reference = const T&; - using iterator = pointer; - using const_iterator = const_pointer; + using iterator = ptr_wrapper; + using const_iterator = ptr_wrapper; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; - static constexpr size_t extent{E}; + static constexpr std::size_t extent{E}; template constexpr span() noexcept { } template - constexpr explicit span(U iter, index_type) : mData{::al::to_address(iter)} { } - template::value)> - constexpr explicit span(U first, V) : mData{::al::to_address(first)} - {} - - constexpr span(type_identity_t (&arr)[E]) noexcept - : span{std::data(arr), std::size(arr)} - { } - constexpr span(std::array &arr) noexcept - : span{std::data(arr), std::size(arr)} - { } - template::value)> - constexpr span(const std::array &arr) noexcept - : span{std::data(arr), std::size(arr)} - { } + constexpr explicit span(U iter, size_type size_) : mData{::al::to_address(iter)} + { alassert(size_ == extent); } + template::value)> + constexpr explicit span(U first, V last) : mData{::al::to_address(first)} + { alassert(static_cast(last-first) == extent); } + + template + constexpr span(type_identity_t (&arr)[N]) noexcept /* NOLINT(*-avoid-c-arrays) */ + : mData{std::data(arr)} + { static_assert(N == extent); } + template + constexpr span(std::array &arr) noexcept : mData{std::data(arr)} + { static_assert(N == extent); } + template::value)> + constexpr span(const std::array &arr) noexcept : mData{std::data(arr)} + { static_assert(N == extent); } template)> constexpr explicit span(U&& cont) : span{std::data(cont), std::size(cont)} { } - template::value + template::value && detail_::is_array_compatible && N == dynamic_extent)> - constexpr explicit span(const span &span_) noexcept - : span{std::data(span_), std::size(span_)} - { } - template::value + constexpr explicit span(const span &span_) noexcept : mData{std::data(span_)} + { alassert(std::size(span_) == extent); } + template::value && detail_::is_array_compatible && N == extent)> - constexpr span(const span &span_) noexcept : span{std::data(span_), std::size(span_)} { } + constexpr span(const span &span_) noexcept : mData{std::data(span_)} { } constexpr span(const span&) noexcept = default; constexpr span& operator=(const span &rhs) noexcept = default; - constexpr reference front() const { return *mData; } - constexpr reference back() const { return *(mData+E-1); } - constexpr reference operator[](index_type idx) const { return mData[idx]; } - constexpr pointer data() const noexcept { return mData; } - - constexpr index_type size() const noexcept { return E; } - constexpr index_type size_bytes() const noexcept { return E * sizeof(value_type); } - constexpr bool empty() const noexcept { return E == 0; } - - constexpr iterator begin() const noexcept { return mData; } - constexpr iterator end() const noexcept { return mData+E; } - constexpr const_iterator cbegin() const noexcept { return mData; } - constexpr const_iterator cend() const noexcept { return mData+E; } - - constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } - constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } - constexpr const_reverse_iterator crbegin() const noexcept - { return const_reverse_iterator{cend()}; } - constexpr const_reverse_iterator crend() const noexcept - { return const_reverse_iterator{cbegin()}; } - - template - constexpr span first() const + [[nodiscard]] constexpr auto front() const -> reference { return mData[0]; } + [[nodiscard]] constexpr auto back() const -> reference { return mData[E-1]; } + [[nodiscard]] constexpr auto operator[](size_type idx) const -> reference { return mData[idx]; } + [[nodiscard]] constexpr auto data() const noexcept -> pointer { return mData; } + + [[nodiscard]] constexpr auto size() const noexcept -> size_type { return E; } + [[nodiscard]] constexpr auto size_bytes() const noexcept -> size_type { return E * sizeof(value_type); } + [[nodiscard]] constexpr auto empty() const noexcept -> bool { return E == 0; } + + [[nodiscard]] constexpr auto begin() const noexcept -> iterator { return iterator{mData}; } + [[nodiscard]] constexpr auto end() const noexcept -> iterator { return iterator{mData+E}; } + [[nodiscard]] constexpr + auto cbegin() const noexcept -> const_iterator { return const_iterator{mData}; } + [[nodiscard]] constexpr + auto cend() const noexcept -> const_iterator { return const_iterator{mData+E}; } + + [[nodiscard]] constexpr + auto rbegin() const noexcept -> reverse_iterator { return reverse_iterator{end()}; } + [[nodiscard]] constexpr + auto rend() const noexcept -> reverse_iterator { return reverse_iterator{begin()}; } + [[nodiscard]] constexpr + auto crbegin() const noexcept -> const_reverse_iterator { return cend(); } + [[nodiscard]] constexpr + auto crend() const noexcept -> const_reverse_iterator { return cbegin(); } + + template + [[nodiscard]] constexpr auto first() const noexcept -> span { static_assert(E >= C, "New size exceeds original capacity"); return span{mData, C}; } - template - constexpr span last() const + template + [[nodiscard]] constexpr auto last() const noexcept -> span { static_assert(E >= C, "New size exceeds original capacity"); return span{mData+(E-C), C}; } - template - constexpr auto subspan() const -> std::enable_if_t> + template + [[nodiscard]] constexpr + auto subspan() const noexcept -> std::enable_if_t> { static_assert(E >= O, "Offset exceeds extent"); static_assert(E-O >= C, "New size exceeds original capacity"); return span{mData+O, C}; } - template - constexpr auto subspan() const -> std::enable_if_t> + template + [[nodiscard]] constexpr + auto subspan() const noexcept -> std::enable_if_t> { static_assert(E >= O, "Offset exceeds extent"); return span{mData+O, E-O}; @@ -161,10 +256,13 @@ class span { * defining the specialization. As a result, these methods need to be * defined later. */ - constexpr span first(size_t count) const; - constexpr span last(size_t count) const; - constexpr span subspan(size_t offset, - size_t count=dynamic_extent) const; + [[nodiscard]] constexpr + auto first(std::size_t count) const noexcept -> span; + [[nodiscard]] constexpr + auto last(std::size_t count) const noexcept -> span; + [[nodiscard]] constexpr + auto subspan(std::size_t offset, std::size_t count=dynamic_extent) const noexcept + -> span; private: pointer mData{nullptr}; @@ -175,7 +273,7 @@ class span { public: using element_type = T; using value_type = std::remove_cv_t; - using index_type = size_t; + using size_type = std::size_t; using difference_type = ptrdiff_t; using pointer = T*; @@ -183,121 +281,160 @@ class span { using reference = T&; using const_reference = const T&; - using iterator = pointer; - using const_iterator = const_pointer; + using iterator = ptr_wrapper; + using const_iterator = ptr_wrapper; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; - static constexpr size_t extent{dynamic_extent}; + static constexpr std::size_t extent{dynamic_extent}; constexpr span() noexcept = default; template - constexpr span(U iter, index_type count) : mData{::al::to_address(iter)}, mDataEnd{::al::to_address(iter) + count} + constexpr span(U iter, size_type count) : mData{::al::to_address(iter)}, mDataLength{count} { } - template::value)> - constexpr span(U first, V last) : span{::al::to_address(first), static_cast(last - first)} + template::value)> + constexpr span(U first, V last) + : span{::al::to_address(first), static_cast(last-first)} { } - template - constexpr span(type_identity_t (&arr)[N]) noexcept - : span{std::data(arr), std::size(arr)} + template + constexpr span(type_identity_t (&arr)[N]) noexcept /* NOLINT(*-avoid-c-arrays) */ + : mData{std::data(arr)}, mDataLength{std::size(arr)} { } - template + template constexpr span(std::array &arr) noexcept - : span{std::data(arr), std::size(arr)} + : mData{std::data(arr)}, mDataLength{std::size(arr)} { } - template::value)> + template::value)> constexpr span(const std::array &arr) noexcept - : span{std::data(arr), std::size(arr)} + : mData{std::data(arr)}, mDataLength{std::size(arr)} { } template)> constexpr span(U&& cont) : span{std::data(cont), std::size(cont)} { } - template::value || extent != N) - && detail_::is_array_compatible)> + template + && (!std::is_same::value || extent != N))> constexpr span(const span &span_) noexcept : span{std::data(span_), std::size(span_)} { } constexpr span(const span&) noexcept = default; constexpr span& operator=(const span &rhs) noexcept = default; - constexpr reference front() const { return *mData; } - constexpr reference back() const { return *(mDataEnd-1); } - constexpr reference operator[](index_type idx) const { return mData[idx]; } - constexpr pointer data() const noexcept { return mData; } - - constexpr index_type size() const noexcept { return static_cast(mDataEnd-mData); } - constexpr index_type size_bytes() const noexcept - { return static_cast(mDataEnd-mData) * sizeof(value_type); } - constexpr bool empty() const noexcept { return mData == mDataEnd; } - - constexpr iterator begin() const noexcept { return mData; } - constexpr iterator end() const noexcept { return mDataEnd; } - constexpr const_iterator cbegin() const noexcept { return mData; } - constexpr const_iterator cend() const noexcept { return mDataEnd; } - - constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } - constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } - constexpr const_reverse_iterator crbegin() const noexcept - { return const_reverse_iterator{cend()}; } - constexpr const_reverse_iterator crend() const noexcept - { return const_reverse_iterator{cbegin()}; } - - template - constexpr span first() const - { return span{mData, C}; } + [[nodiscard]] constexpr auto front() const -> reference { return mData[0]; } + [[nodiscard]] constexpr auto back() const -> reference { return mData[mDataLength-1]; } + [[nodiscard]] constexpr auto operator[](size_type idx) const -> reference {return mData[idx];} + [[nodiscard]] constexpr auto data() const noexcept -> pointer { return mData; } + + [[nodiscard]] constexpr auto size() const noexcept -> size_type { return mDataLength; } + [[nodiscard]] constexpr + auto size_bytes() const noexcept -> size_type { return mDataLength * sizeof(value_type); } + [[nodiscard]] constexpr auto empty() const noexcept -> bool { return mDataLength == 0; } + + [[nodiscard]] constexpr auto begin() const noexcept -> iterator { return iterator{mData}; } + [[nodiscard]] constexpr + auto end() const noexcept -> iterator { return iterator{mData+mDataLength}; } + [[nodiscard]] constexpr + auto cbegin() const noexcept -> const_iterator { return const_iterator{mData}; } + [[nodiscard]] constexpr + auto cend() const noexcept -> const_iterator { return const_iterator{mData+mDataLength}; } + + [[nodiscard]] constexpr + auto rbegin() const noexcept -> reverse_iterator { return reverse_iterator{end()}; } + [[nodiscard]] constexpr + auto rend() const noexcept -> reverse_iterator { return reverse_iterator{begin()}; } + [[nodiscard]] constexpr + auto crbegin() const noexcept -> const_reverse_iterator { return cend(); } + [[nodiscard]] constexpr + auto crend() const noexcept -> const_reverse_iterator { return cbegin(); } + + template + [[nodiscard]] constexpr auto first() const noexcept -> span + { + assert(C <= mDataLength); + return span{mData, C}; + } - constexpr span first(size_t count) const - { return (count >= size()) ? *this : span{mData, mData+count}; } + [[nodiscard]] constexpr auto first(std::size_t count) const noexcept -> span + { + assert(count <= mDataLength); + return span{mData, count}; + } - template - constexpr span last() const - { return span{mDataEnd-C, C}; } + template + [[nodiscard]] constexpr auto last() const noexcept -> span + { + assert(C <= mDataLength); + return span{mData+mDataLength-C, C}; + } - constexpr span last(size_t count) const - { return (count >= size()) ? *this : span{mDataEnd-count, mDataEnd}; } + [[nodiscard]] constexpr auto last(std::size_t count) const noexcept -> span + { + assert(count <= mDataLength); + return span{mData+mDataLength-count, count}; + } - template - constexpr auto subspan() const -> std::enable_if_t> - { return span{mData+O, C}; } + template + [[nodiscard]] constexpr + auto subspan() const noexcept -> std::enable_if_t> + { + assert(O <= mDataLength); + assert(C <= mDataLength-O); + return span{mData+O, C}; + } - template - constexpr auto subspan() const -> std::enable_if_t> - { return span{mData+O, mDataEnd}; } + template + [[nodiscard]] constexpr + auto subspan() const noexcept -> std::enable_if_t> + { + assert(O <= mDataLength); + return span{mData+O, mDataLength-O}; + } - constexpr span subspan(size_t offset, size_t count=dynamic_extent) const + [[nodiscard]] constexpr + auto subspan(std::size_t offset, std::size_t count=dynamic_extent) const noexcept -> span { - return (offset > size()) ? span{} : - (count >= size()-offset) ? span{mData+offset, mDataEnd} : - span{mData+offset, mData+offset+count}; + assert(offset <= mDataLength); + if(count != dynamic_extent) + { + assert(count <= mDataLength-offset); + return span{mData+offset, count}; + } + return span{mData+offset, mDataLength-offset}; } private: pointer mData{nullptr}; - pointer mDataEnd{nullptr}; + size_type mDataLength{0}; }; -template -constexpr inline auto span::first(size_t count) const -> span +template +[[nodiscard]] constexpr +auto span::first(std::size_t count) const noexcept -> span { - return (count >= size()) ? span{mData, extent} : - span{mData, count}; + assert(count <= size()); + return span{mData, count}; } -template -constexpr inline auto span::last(size_t count) const -> span +template +[[nodiscard]] constexpr +auto span::last(std::size_t count) const noexcept -> span { - return (count >= size()) ? span{mData, extent} : - span{mData+extent-count, count}; + assert(count <= size()); + return span{mData+size()-count, count}; } -template -constexpr inline auto span::subspan(size_t offset, size_t count) const +template +[[nodiscard]] constexpr +auto span::subspan(std::size_t offset, std::size_t count) const noexcept -> span { - return (offset > size()) ? span{} : - (count >= size()-offset) ? span{mData+offset, mData+extent} : - span{mData+offset, mData+offset+count}; + assert(offset <= size()); + if(count != dynamic_extent) + { + assert(count <= size()-offset); + return span{mData+offset, count}; + } + return span{mData+offset, size()-offset}; } @@ -305,7 +442,7 @@ template span(T, EndOrSize) -> span())>>; template -span(T (&)[N]) -> span; +span(T (&)[N]) -> span; /* NOLINT(*-avoid-c-arrays) */ template span(std::array&) -> span; @@ -314,7 +451,7 @@ template span(const std::array&) -> span; template)> -span(C&&) -> span()))>>; +span(C&&) -> span()))>>; #undef REQUIRES diff --git a/3rdparty/openal/common/alstring.cpp b/3rdparty/openal/common/alstring.cpp index 4a84be1db24a..d609823dfc48 100644 --- a/3rdparty/openal/common/alstring.cpp +++ b/3rdparty/openal/common/alstring.cpp @@ -3,42 +3,51 @@ #include "alstring.h" +#include #include -#include +#include +#include -namespace { +namespace al { -int to_upper(const char ch) +int case_compare(const std::string_view str0, const std::string_view str1) noexcept { - using char8_traits = std::char_traits; - return std::toupper(char8_traits::to_int_type(ch)); -} - -} // namespace + using Traits = std::string_view::traits_type; -namespace al { + auto ch0 = str0.cbegin(); + auto ch1 = str1.cbegin(); + auto ch1end = ch1 + std::min(str0.size(), str1.size()); + while(ch1 != ch1end) + { + const int u0{std::toupper(Traits::to_int_type(*ch0))}; + const int u1{std::toupper(Traits::to_int_type(*ch1))}; + if(const int diff{u0-u1}) return diff; + ++ch0; ++ch1; + } -int strcasecmp(const char *str0, const char *str1) noexcept -{ - do { - const int diff{to_upper(*str0) - to_upper(*str1)}; - if(diff < 0) return -1; - if(diff > 0) return 1; - } while(*(str0++) && *(str1++)); + if(str0.size() < str1.size()) return -1; + if(str0.size() > str1.size()) return 1; return 0; } -int strncasecmp(const char *str0, const char *str1, std::size_t len) noexcept +int case_compare(const std::wstring_view str0, const std::wstring_view str1) noexcept { - if(len > 0) + using Traits = std::wstring_view::traits_type; + + auto ch0 = str0.cbegin(); + auto ch1 = str1.cbegin(); + auto ch1end = ch1 + std::min(str0.size(), str1.size()); + while(ch1 != ch1end) { - do { - const int diff{to_upper(*str0) - to_upper(*str1)}; - if(diff < 0) return -1; - if(diff > 0) return 1; - } while(--len && *(str0++) && *(str1++)); + const auto u0 = std::towupper(Traits::to_int_type(*ch0)); + const auto u1 = std::towupper(Traits::to_int_type(*ch1)); + if(const auto diff = static_cast(u0-u1)) return diff; + ++ch0; ++ch1; } + + if(str0.size() < str1.size()) return -1; + if(str0.size() > str1.size()) return 1; return 0; } diff --git a/3rdparty/openal/common/alstring.h b/3rdparty/openal/common/alstring.h index 6c5004ee1abe..c03936136724 100644 --- a/3rdparty/openal/common/alstring.h +++ b/3rdparty/openal/common/alstring.h @@ -1,17 +1,58 @@ #ifndef AL_STRING_H #define AL_STRING_H +#include #include -#include +#include +#include +#include namespace al { -/* These would be better served by using a string_view-like span/view with - * case-insensitive char traits. +template +[[nodiscard]] constexpr +auto sizei(const std::basic_string_view str) noexcept -> int +{ return static_cast(std::min(str.size(), std::numeric_limits::max())); } + +template +[[nodiscard]] constexpr +auto sizei(const std::basic_string &str) noexcept -> int +{ return static_cast(std::min(str.size(), std::numeric_limits::max())); } + + +[[nodiscard]] +constexpr bool contains(const std::string_view str0, const std::string_view str1) noexcept +{ return str0.find(str1) != std::string_view::npos; } + +[[nodiscard]] +constexpr bool starts_with(const std::string_view str0, const std::string_view str1) noexcept +{ return str0.substr(0, std::min(str0.size(), str1.size())) == str1; } + +[[nodiscard]] +constexpr bool ends_with(const std::string_view str0, const std::string_view str1) noexcept +{ return str0.substr(str0.size() - std::min(str0.size(), str1.size())) == str1; } + +[[nodiscard]] +int case_compare(const std::string_view str0, const std::string_view str1) noexcept; + +[[nodiscard]] +int case_compare(const std::wstring_view str0, const std::wstring_view str1) noexcept; + +/* C++20 changes path::u8string() to return a string using a new/distinct + * char8_t type for UTF-8 strings. However, support for this with standard + * string functions is totally inadequate, and we already hold UTF-8 with plain + * char strings. So this function is used to reinterpret a char8_t string as a + * char string_view. */ -int strcasecmp(const char *str0, const char *str1) noexcept; -int strncasecmp(const char *str0, const char *str1, std::size_t len) noexcept; +#if defined(__cpp_lib_char8_t) && __cpp_lib_char8_t >= 201907L +inline auto u8_as_char(const std::u8string_view str) -> std::string_view +#else +inline auto u8_as_char(const std::string_view str) -> std::string_view +#endif +{ + return std::string_view{reinterpret_cast(str.data()), str.size()}; +} } // namespace al diff --git a/3rdparty/openal/common/althrd_setname.cpp b/3rdparty/openal/common/althrd_setname.cpp index 22d330923f4f..b92b05d1c91f 100644 --- a/3rdparty/openal/common/althrd_setname.cpp +++ b/3rdparty/openal/common/althrd_setname.cpp @@ -14,13 +14,14 @@ void althrd_setname(const char *name [[maybe_unused]]) #define MS_VC_EXCEPTION 0x406D1388 #pragma pack(push,8) - struct { + struct InfoStruct { 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) + InfoStruct info{}; info.dwType = 0x1000; info.szName = name; info.dwThreadID = ~DWORD{0}; @@ -60,7 +61,7 @@ using setname_t4 = int(*)(pthread_t, const char*, void*); { func(pthread_self(), name); } [[maybe_unused]] void setname_caller(setname_t4 func, const char *name) -{ func(pthread_self(), "%s", static_cast(const_cast(name))); } +{ func(pthread_self(), "%s", const_cast(name)); /* NOLINT(*-const-cast) */ } } // namespace diff --git a/3rdparty/openal/common/althreads.h b/3rdparty/openal/common/althreads.h new file mode 100644 index 000000000000..d84917067130 --- /dev/null +++ b/3rdparty/openal/common/althreads.h @@ -0,0 +1,143 @@ +#ifndef AL_THREADS_H +#define AL_THREADS_H + +#include +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include + +#elif defined(__APPLE__) + +#include + +#else + +#include +#endif + +#include "albit.h" + +namespace al { + +template +class tss { + static_assert(sizeof(T) <= sizeof(void*)); + static_assert(std::is_trivially_destructible_v && std::is_trivially_copy_constructible_v); + + [[nodiscard]] + static auto to_ptr(const T &value) noexcept -> void* + { + if constexpr(std::is_pointer_v) + { + if constexpr(std::is_const_v>) + return const_cast(static_cast(value)); /* NOLINT(*-const-cast) */ + else + return static_cast(value); + } + else if constexpr(sizeof(T) == sizeof(void*)) + return al::bit_cast(value); + else if constexpr(std::is_integral_v) + return al::bit_cast(static_cast(value)); + } + + [[nodiscard]] + static auto from_ptr(void *ptr) noexcept -> T + { + if constexpr(std::is_pointer_v) + return static_cast(ptr); + else if constexpr(sizeof(T) == sizeof(void*)) + return al::bit_cast(ptr); + else if constexpr(std::is_integral_v) + return static_cast(al::bit_cast(ptr)); + } + +#ifdef _WIN32 + DWORD mTss{TLS_OUT_OF_INDEXES}; + +public: + tss() : mTss{TlsAlloc()} + { + if(mTss == TLS_OUT_OF_INDEXES) + throw std::runtime_error{"al::tss::tss()"}; + } + explicit tss(const T &init) : tss{} + { + if(TlsSetValue(mTss, to_ptr(init)) == FALSE) + throw std::runtime_error{"al::tss::tss(T)"}; + } + ~tss() { TlsFree(mTss); } + + void set(const T &value) const + { + if(TlsSetValue(mTss, to_ptr(value)) == FALSE) + throw std::runtime_error{"al::tss::set(T)"}; + } + + [[nodiscard]] + auto get() const noexcept -> T { return from_ptr(TlsGetValue(mTss)); } + +#elif defined(__APPLE__) + + pthread_key_t mTss{}; + +public: + tss() + { + if(int res{pthread_key_create(&mTss, nullptr)}; res != 0) + throw std::runtime_error{"al::tss::tss()"}; + } + explicit tss(const T &init) : tss{} + { + if(int res{pthread_setspecific(mTss, to_ptr(init))}; res != 0) + throw std::runtime_error{"al::tss::tss(T)"}; + } + ~tss() { pthread_key_delete(mTss); } + + void set(const T &value) const + { + if(int res{pthread_setspecific(mTss, to_ptr(value))}; res != 0) + throw std::runtime_error{"al::tss::set(T)"}; + } + + [[nodiscard]] + auto get() const noexcept -> T { return from_ptr(pthread_getspecific(mTss)); } + +#else + + tss_t mTss{}; + +public: + tss() + { + if(int res{tss_create(&mTss, nullptr)}; res != thrd_success) + throw std::runtime_error{"al::tss::tss()"}; + } + explicit tss(const T &init) : tss{} + { + if(int res{tss_set(mTss, to_ptr(init))}; res != thrd_success) + throw std::runtime_error{"al::tss::tss(T)"}; + } + ~tss() { tss_delete(mTss); } + + void set(const T &value) const + { + if(int res{tss_set(mTss, to_ptr(value))}; res != thrd_success) + throw std::runtime_error{"al::tss::set(T)"}; + } + + [[nodiscard]] + auto get() const noexcept -> T { return from_ptr(tss_get(mTss)); } +#endif /* _WIN32 */ + + tss(const tss&) = delete; + tss(tss&&) = delete; + void operator=(const tss&) = delete; + void operator=(tss&&) = delete; +}; + +} // namespace al + +#endif /* AL_THREADS_H */ diff --git a/3rdparty/openal/common/atomic.h b/3rdparty/openal/common/atomic.h index 5e9b04c6975f..7075b15892f8 100644 --- a/3rdparty/openal/common/atomic.h +++ b/3rdparty/openal/common/atomic.h @@ -2,17 +2,17 @@ #define AL_ATOMIC_H #include +#include +#include +#include "almalloc.h" -using RefCount = std::atomic; - -inline void InitRef(RefCount &ref, unsigned int value) -{ ref.store(value, std::memory_order_relaxed); } -inline unsigned int ReadRef(RefCount &ref) -{ return ref.load(std::memory_order_acquire); } -inline unsigned int IncrementRef(RefCount &ref) +template +auto IncrementRef(std::atomic &ref) noexcept { return ref.fetch_add(1u, std::memory_order_acq_rel)+1u; } -inline unsigned int DecrementRef(RefCount &ref) + +template +auto DecrementRef(std::atomic &ref) noexcept { return ref.fetch_sub(1u, std::memory_order_acq_rel)-1u; } @@ -30,4 +30,75 @@ inline void AtomicReplaceHead(std::atomic &head, T newhead) std::memory_order_acq_rel, std::memory_order_acquire)); } +namespace al { + +template> +class atomic_unique_ptr { + std::atomic> mPointer{}; + + using unique_ptr_t = std::unique_ptr; + +public: + atomic_unique_ptr() = default; + atomic_unique_ptr(const atomic_unique_ptr&) = delete; + explicit atomic_unique_ptr(std::nullptr_t) noexcept { } + explicit atomic_unique_ptr(gsl::owner ptr) noexcept : mPointer{ptr} { } + explicit atomic_unique_ptr(unique_ptr_t&& rhs) noexcept : mPointer{rhs.release()} { } + ~atomic_unique_ptr() + { + if(auto ptr = mPointer.exchange(nullptr, std::memory_order_relaxed)) + D{}(ptr); + } + + auto operator=(const atomic_unique_ptr&) -> atomic_unique_ptr& = delete; + auto operator=(std::nullptr_t) noexcept -> atomic_unique_ptr& + { + if(auto ptr = mPointer.exchange(nullptr)) + D{}(ptr); + return *this; + } + auto operator=(unique_ptr_t&& rhs) noexcept -> atomic_unique_ptr& + { + if(auto ptr = mPointer.exchange(rhs.release())) + D{}(ptr); + return *this; + } + + [[nodiscard]] + auto load(std::memory_order m=std::memory_order_seq_cst) const noexcept -> T* + { return mPointer.load(m); } + void store(std::nullptr_t, std::memory_order m=std::memory_order_seq_cst) noexcept + { + if(auto oldptr = mPointer.exchange(nullptr, m)) + D{}(oldptr); + } + void store(gsl::owner ptr, std::memory_order m=std::memory_order_seq_cst) noexcept + { + if(auto oldptr = mPointer.exchange(ptr, m)) + D{}(oldptr); + } + void store(unique_ptr_t&& ptr, std::memory_order m=std::memory_order_seq_cst) noexcept + { + if(auto oldptr = mPointer.exchange(ptr.release(), m)) + D{}(oldptr); + } + + [[nodiscard]] + auto exchange(std::nullptr_t, std::memory_order m=std::memory_order_seq_cst) noexcept -> unique_ptr_t + { return unique_ptr_t{mPointer.exchange(nullptr, m)}; } + [[nodiscard]] + auto exchange(gsl::owner ptr, std::memory_order m=std::memory_order_seq_cst) noexcept -> unique_ptr_t + { return unique_ptr_t{mPointer.exchange(ptr, m)}; } + [[nodiscard]] + auto exchange(std::unique_ptr&& ptr, std::memory_order m=std::memory_order_seq_cst) noexcept -> unique_ptr_t + { return unique_ptr_t{mPointer.exchange(ptr.release(), m)}; } + + [[nodiscard]] + auto is_lock_free() const noexcept -> bool { return mPointer.is_lock_free(); } + + static constexpr auto is_always_lock_free = std::atomic>::is_always_lock_free; +}; + +} // namespace al + #endif /* AL_ATOMIC_H */ diff --git a/3rdparty/openal/common/comptr.h b/3rdparty/openal/common/comptr.h index 5a733ea2e824..cd0e2e3eed5a 100644 --- a/3rdparty/openal/common/comptr.h +++ b/3rdparty/openal/common/comptr.h @@ -1,13 +1,53 @@ #ifndef COMMON_COMPTR_H #define COMMON_COMPTR_H +#ifdef _WIN32 #include #include #include #include #include -template +#define WIN32_LEAN_AND_MEAN +#include +#include + +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; + + [[nodiscard]] + HRESULT status() const noexcept { return mStatus; } + explicit operator bool() const noexcept { return SUCCEEDED(status()); } + + void uninit() + { + if(SUCCEEDED(mStatus)) + CoUninitialize(); + mStatus = E_FAIL; + } +}; + + +template /* NOLINTNEXTLINE(clazy-rule-of-three) False positive */ struct ComPtr { using element_type = T; @@ -22,6 +62,7 @@ struct ComPtr { explicit ComPtr(T *ptr) noexcept : mPtr{ptr} { } ~ComPtr() { if(mPtr) mPtr->Release(); } + /* NOLINTNEXTLINE(bugprone-unhandled-self-assignment) Yes it is. */ ComPtr& operator=(const ComPtr &rhs) noexcept(RefIsNoexcept) { if constexpr(RefIsNoexcept) @@ -69,43 +110,6 @@ struct ComPtr { private: T *mPtr{nullptr}; }; - - -namespace al { - -template -class out_ptr_t { - static_assert(!std::is_same_v); - - SP &mRes; - std::variant mPtr{}; - -public: - out_ptr_t(SP &res) : mRes{res} { } - ~out_ptr_t() - { - auto set_res = [this](auto &ptr) - { mRes.reset(static_cast(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(mPtr); } - - operator void**() noexcept - { return &mPtr.template emplace(); } -}; - -template -auto out_ptr(SP &res) -{ - using ptype = typename SP::element_type*; - return out_ptr_t{res}; -} - -} // namespace al +#endif /* _WIN32 */ #endif diff --git a/3rdparty/openal/common/dynload.cpp b/3rdparty/openal/common/dynload.cpp index 86c36e003a72..333a9435ee07 100644 --- a/3rdparty/openal/common/dynload.cpp +++ b/3rdparty/openal/common/dynload.cpp @@ -3,13 +3,12 @@ #include "dynload.h" -#include "albit.h" -#include "strutils.h" - #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include +#include "strutils.h" + void *LoadLib(const char *name) { std::wstring wname{utf8_to_wstr(name)}; @@ -18,7 +17,7 @@ void *LoadLib(const char *name) void CloseLib(void *handle) { FreeLibrary(static_cast(handle)); } void *GetSymbol(void *handle, const char *name) -{ return al::bit_cast(GetProcAddress(static_cast(handle), name)); } +{ return reinterpret_cast(GetProcAddress(static_cast(handle), name)); } #elif defined(HAVE_DLFCN_H) diff --git a/3rdparty/openal/common/flexarray.h b/3rdparty/openal/common/flexarray.h new file mode 100644 index 000000000000..afd6eaeee078 --- /dev/null +++ b/3rdparty/openal/common/flexarray.h @@ -0,0 +1,139 @@ +#ifndef AL_FLEXARRAY_H +#define AL_FLEXARRAY_H + +#include +#include +#include +#include +#include +#include + +#include "almalloc.h" +#include "alspan.h" + +namespace al { + +/* Storage for flexible array data. This is trivially destructible if type T is + * trivially destructible. + */ +template::value> +struct alignas(alignment) FlexArrayStorage : al::span { + /* NOLINTBEGIN(bugprone-sizeof-expression) clang-tidy warns about the + * sizeof(T) being suspicious when T is a pointer type, which it will be + * for flexible arrays of pointers. + */ + static constexpr size_t Sizeof(size_t count, size_t base=0u) noexcept + { return sizeof(FlexArrayStorage) + sizeof(T)*count + base; } + /* NOLINTEND(bugprone-sizeof-expression) */ + + /* NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) Flexible + * arrays store their payloads after the end of the object, which must be + * the last in the whole parent chain. + */ + FlexArrayStorage(size_t size) noexcept(std::is_nothrow_constructible_v) + : al::span{::new(static_cast(this+1)) T[size], size} + { } + /* NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ + ~FlexArrayStorage() = default; + + FlexArrayStorage(const FlexArrayStorage&) = delete; + FlexArrayStorage& operator=(const FlexArrayStorage&) = delete; +}; + +template +struct alignas(alignment) FlexArrayStorage : al::span { + static constexpr size_t Sizeof(size_t count, size_t base=0u) noexcept + { return sizeof(FlexArrayStorage) + sizeof(T)*count + base; } + + /* NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ + FlexArrayStorage(size_t size) noexcept(std::is_nothrow_constructible_v) + : al::span{::new(static_cast(this+1)) T[size], size} + { } + /* NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ + ~FlexArrayStorage() { std::destroy(this->begin(), this->end()); } + + FlexArrayStorage(const FlexArrayStorage&) = delete; + FlexArrayStorage& operator=(const FlexArrayStorage&) = delete; +}; + +/* A flexible array type. Used either standalone or at the end of a parent + * struct, to have a run-time-sized array that's embedded with its size. Should + * be used delicately, ensuring there's no additional data after the FlexArray + * member. + */ +template +struct FlexArray { + using element_type = T; + using value_type = std::remove_cv_t; + using index_type = size_t; + using difference_type = ptrdiff_t; + + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + + static constexpr std::size_t StorageAlign{std::max(alignof(T), Align)}; + using Storage_t_ = FlexArrayStorage), StorageAlign)>; + + using iterator = typename Storage_t_::iterator; + using const_iterator = typename Storage_t_::const_iterator; + using reverse_iterator = typename Storage_t_::reverse_iterator; + using const_reverse_iterator = typename Storage_t_::const_reverse_iterator; + + const Storage_t_ mStore; + + static constexpr index_type Sizeof(index_type count, index_type base=0u) noexcept + { return Storage_t_::Sizeof(count, base); } + static std::unique_ptr Create(index_type count) + { return std::unique_ptr{new(FamCount{count}) FlexArray{count}}; } + + FlexArray(index_type size) noexcept(std::is_nothrow_constructible_v) + : mStore{size} + { } + ~FlexArray() = default; + + [[nodiscard]] auto size() const noexcept -> index_type { return mStore.size(); } + [[nodiscard]] auto empty() const noexcept -> bool { return mStore.empty(); } + + [[nodiscard]] auto data() noexcept -> pointer { return mStore.data(); } + [[nodiscard]] auto data() const noexcept -> const_pointer { return mStore.data(); } + + [[nodiscard]] auto operator[](index_type i) noexcept -> reference { return mStore[i]; } + [[nodiscard]] auto operator[](index_type i) const noexcept -> const_reference { return mStore[i]; } + + [[nodiscard]] auto front() noexcept -> reference { return mStore.front(); } + [[nodiscard]] auto front() const noexcept -> const_reference { return mStore.front(); } + + [[nodiscard]] auto back() noexcept -> reference { return mStore.back(); } + [[nodiscard]] auto back() const noexcept -> const_reference { return mStore.back(); } + + [[nodiscard]] auto begin() noexcept -> iterator { return mStore.begin(); } + [[nodiscard]] auto begin() const noexcept -> const_iterator { return mStore.cbegin(); } + [[nodiscard]] auto cbegin() const noexcept -> const_iterator { return mStore.cbegin(); } + [[nodiscard]] auto end() noexcept -> iterator { return mStore.end(); } + [[nodiscard]] auto end() const noexcept -> const_iterator { return mStore.cend(); } + [[nodiscard]] auto cend() const noexcept -> const_iterator { return mStore.cend(); } + + [[nodiscard]] auto rbegin() noexcept -> reverse_iterator { return mStore.rbegin(); } + [[nodiscard]] auto rbegin() const noexcept -> const_reverse_iterator { return mStore.crbegin(); } + [[nodiscard]] auto crbegin() const noexcept -> const_reverse_iterator { return mStore.crbegin(); } + [[nodiscard]] auto rend() noexcept -> reverse_iterator { return mStore.rend(); } + [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator { return mStore.crend(); } + [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator { return mStore.crend(); } + + gsl::owner operator new(size_t, FamCount count) + { return ::operator new[](Sizeof(count), std::align_val_t{alignof(FlexArray)}); } + void operator delete(gsl::owner block, FamCount) noexcept + { ::operator delete[](block, std::align_val_t{alignof(FlexArray)}); } + void operator delete(gsl::owner block) noexcept + { ::operator delete[](block, std::align_val_t{alignof(FlexArray)}); } + + void *operator new(size_t size) = delete; + void *operator new[](size_t size) = delete; + void operator delete[](void *block) = delete; +}; + +} // namespace al + +#endif /* AL_FLEXARRAY_H */ diff --git a/3rdparty/openal/common/intrusive_ptr.h b/3rdparty/openal/common/intrusive_ptr.h index 27075347998e..e86b3ce6a89a 100644 --- a/3rdparty/openal/common/intrusive_ptr.h +++ b/3rdparty/openal/common/intrusive_ptr.h @@ -1,6 +1,8 @@ #ifndef INTRUSIVE_PTR_H #define INTRUSIVE_PTR_H +#include +#include #include #include "atomic.h" @@ -11,7 +13,10 @@ namespace al { template class intrusive_ref { - RefCount mRef{1u}; + std::atomic mRef{1u}; + +protected: + ~intrusive_ref() = default; public: unsigned int add_ref() noexcept { return IncrementRef(mRef); } @@ -46,7 +51,7 @@ class intrusive_ref { }; -template +template /* NOLINTNEXTLINE(clazy-rule-of-three) False positive */ class intrusive_ptr { T *mPtr{nullptr}; @@ -60,6 +65,9 @@ class intrusive_ptr { explicit intrusive_ptr(T *ptr) noexcept : mPtr{ptr} { } ~intrusive_ptr() { if(mPtr) mPtr->dec_ref(); } + /* NOLINTBEGIN(bugprone-unhandled-self-assignment) + * Self-assignment is handled properly here. + */ intrusive_ptr& operator=(const intrusive_ptr &rhs) noexcept { static_assert(noexcept(std::declval()->dec_ref()), "dec_ref must be noexcept"); @@ -69,6 +77,7 @@ class intrusive_ptr { mPtr = rhs.mPtr; return *this; } + /* NOLINTEND(bugprone-unhandled-self-assignment) */ intrusive_ptr& operator=(intrusive_ptr&& rhs) noexcept { if(&rhs != this) LIKELY @@ -81,9 +90,9 @@ class intrusive_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; } + [[nodiscard]] auto operator*() const noexcept -> T& { return *mPtr; } + [[nodiscard]] auto operator->() const noexcept -> T* { return mPtr; } + [[nodiscard]] auto get() const noexcept -> T* { return mPtr; } void reset(T *ptr=nullptr) noexcept { @@ -98,23 +107,6 @@ class intrusive_ptr { void swap(intrusive_ptr&& rhs) noexcept { std::swap(mPtr, rhs.mPtr); } }; -#define AL_DECL_OP(op) \ -template \ -inline bool operator op(const intrusive_ptr &lhs, const T *rhs) noexcept \ -{ return lhs.get() op rhs; } \ -template \ -inline bool operator op(const T *lhs, const intrusive_ptr &rhs) noexcept \ -{ return lhs op rhs.get(); } - -AL_DECL_OP(==) -AL_DECL_OP(!=) -AL_DECL_OP(<=) -AL_DECL_OP(>=) -AL_DECL_OP(<) -AL_DECL_OP(>) - -#undef AL_DECL_OP - } // namespace al #endif /* INTRUSIVE_PTR_H */ diff --git a/3rdparty/openal/common/opthelpers.h b/3rdparty/openal/common/opthelpers.h index dc43ccdb5301..fff881545156 100644 --- a/3rdparty/openal/common/opthelpers.h +++ b/3rdparty/openal/common/opthelpers.h @@ -28,6 +28,24 @@ #define NOINLINE #endif +#if defined(__MINGW32__) && defined(__i386__) +/* 32-bit MinGW targets have a bug where __STDCPP_DEFAULT_NEW_ALIGNMENT__ + * reports 16, despite the default operator new calling standard malloc which + * only guarantees 8-byte alignment. As a result, structs that need and specify + * 16-byte alignment only get 8-byte alignment. Explicitly specifying 32-byte + * alignment forces the over-aligned operator new to be called, giving the + * correct (if larger than necessary) alignment. + * + * Technically this bug affects 32-bit GCC more generally, but typically only + * with fairly old glibc versions as newer versions do guarantee the 16-byte + * alignment as specified. MinGW is reliant on msvcrt.dll's malloc however, + * which can't be updated to give that guarantee. + */ +#define SIMDALIGN alignas(32) +#else +#define SIMDALIGN +#endif + /* Unlike the likely attribute, ASSUME requires the condition to be true or * else it invokes undefined behavior. It's essentially an assert without * actually checking the condition at run-time, allowing for stronger @@ -42,7 +60,7 @@ #elif HAS_BUILTIN(__builtin_unreachable) #define ASSUME(x) do { if(x) break; __builtin_unreachable(); } while(0) #else -#define ASSUME(x) ((void)0) +#define ASSUME(x) (static_cast(0)) #endif /* This shouldn't be needed since unknown attributes are ignored, but older diff --git a/3rdparty/openal/common/pffft.cpp b/3rdparty/openal/common/pffft.cpp new file mode 100644 index 000000000000..36e965428aa5 --- /dev/null +++ b/3rdparty/openal/common/pffft.cpp @@ -0,0 +1,2308 @@ +//$ nobt + +/* Copyright (c) 2013 Julien Pommier ( pommier@modartt.com ) + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "albit.h" +#include "almalloc.h" +#include "alnumbers.h" +#include "alnumeric.h" +#include "alspan.h" +#include "opthelpers.h" + + +using uint = unsigned int; + +namespace { + +#if defined(__GNUC__) || defined(_MSC_VER) +#define RESTRICT __restrict +#else +#define RESTRICT +#endif + +/* 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__)) \ + && (defined(__VEC__) || defined(__ALTIVEC__)) +#include +using v4sf = vector float; +constexpr uint SimdSize{4}; +force_inline v4sf vzero() noexcept { return (vector float)vec_splat_u8(0); } +force_inline v4sf vmul(v4sf a, v4sf b) noexcept { return vec_madd(a, b, vzero()); } +force_inline v4sf vadd(v4sf a, v4sf b) noexcept { return vec_add(a, b); } +force_inline v4sf vmadd(v4sf a, v4sf b, v4sf c) noexcept { return vec_madd(a, b, c); } +force_inline v4sf vsub(v4sf a, v4sf b) noexcept { return vec_sub(a, b); } +force_inline v4sf ld_ps1(float a) noexcept { return vec_splats(a); } + +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 vals{{a, b, c, d}}; + return vec_ld(0, vals.data()); +} +force_inline v4sf vinsert0(v4sf v, float a) noexcept { return vec_insert(a, v, 0); } +force_inline float vextract0(v4sf v) noexcept { return vec_extract(v, 0); } + +force_inline void interleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + out1 = vec_mergeh(in1, in2); + out2 = vec_mergel(in1, in2); +} +force_inline void uninterleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + out1 = 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}); +} + +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); +} + +force_inline v4sf vswaphl(v4sf a, v4sf b) noexcept +{ return 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 +using v4sf = __m128; +/* 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. + */ +constexpr uint SimdSize{4}; +force_inline v4sf vzero() noexcept { return _mm_setzero_ps(); } +force_inline v4sf vmul(v4sf a, v4sf b) noexcept { return _mm_mul_ps(a, b); } +force_inline v4sf vadd(v4sf a, v4sf b) noexcept { return _mm_add_ps(a, b); } +force_inline v4sf vmadd(v4sf a, v4sf b, v4sf c) noexcept { return _mm_add_ps(_mm_mul_ps(a,b), c); } +force_inline v4sf vsub(v4sf a, v4sf b) noexcept { return _mm_sub_ps(a, b); } +force_inline v4sf ld_ps1(float a) noexcept { return _mm_set1_ps(a); } + +force_inline v4sf vset4(float a, float b, float c, float d) noexcept +{ return _mm_setr_ps(a, b, c, d); } +force_inline v4sf vinsert0(const v4sf v, const float a) noexcept +{ return _mm_move_ss(v, _mm_set_ss(a)); } +force_inline float vextract0(v4sf v) noexcept +{ return _mm_cvtss_f32(v); } + +force_inline void interleave2(const v4sf in1, const v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + out1 = _mm_unpacklo_ps(in1, in2); + out2 = _mm_unpackhi_ps(in1, in2); +} +force_inline void uninterleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + out1 = _mm_shuffle_ps(in1, in2, _MM_SHUFFLE(2,0,2,0)); + out2 = _mm_shuffle_ps(in1, in2, _MM_SHUFFLE(3,1,3,1)); +} + +force_inline void vtranspose4(v4sf &x0, v4sf &x1, v4sf &x2, v4sf &x3) noexcept +{ _MM_TRANSPOSE4_PS(x0, x1, x2, x3); } + +force_inline v4sf vswaphl(v4sf a, v4sf b) noexcept +{ return _mm_shuffle_ps(b, a, _MM_SHUFFLE(3,2,1,0)); } + +/* + * ARM NEON support macros + */ +#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(__arm64) || defined(_M_ARM64) + +#include +using v4sf = float32x4_t; +constexpr uint SimdSize{4}; +force_inline v4sf vzero() noexcept { return vdupq_n_f32(0.0f); } +force_inline v4sf vmul(v4sf a, v4sf b) noexcept { return vmulq_f32(a, b); } +force_inline v4sf vadd(v4sf a, v4sf b) noexcept { return vaddq_f32(a, b); } +force_inline v4sf vmadd(v4sf a, v4sf b, v4sf c) noexcept { return vmlaq_f32(c, a, b); } +force_inline v4sf vsub(v4sf a, v4sf b) noexcept { return vsubq_f32(a, b); } +force_inline v4sf ld_ps1(float a) noexcept { return vdupq_n_f32(a); } + +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; +} +force_inline v4sf vinsert0(v4sf v, float a) noexcept +{ return vsetq_lane_f32(a, v, 0); } +force_inline float vextract0(v4sf v) noexcept +{ return 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]; +} + +force_inline v4sf vswaphl(v4sf a, v4sf b) noexcept +{ return 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; +constexpr uint SimdSize{4}; +force_inline constexpr v4sf vzero() noexcept { return v4sf{0.0f, 0.0f, 0.0f, 0.0f}; } +force_inline constexpr v4sf vmul(v4sf a, v4sf b) noexcept { return a * b; } +force_inline constexpr v4sf vadd(v4sf a, v4sf b) noexcept { return a + b; } +force_inline constexpr v4sf vmadd(v4sf a, v4sf b, v4sf c) noexcept { return a*b + c; } +force_inline constexpr v4sf vsub(v4sf a, v4sf b) noexcept { return a - b; } +force_inline constexpr v4sf ld_ps1(float a) noexcept { return v4sf{a, a, a, a}; } + +force_inline constexpr v4sf vset4(float a, float b, float c, float d) noexcept +{ return v4sf{a, b, c, d}; } +force_inline constexpr v4sf vinsert0(v4sf v, float a) noexcept +{ return v4sf{a, v[1], v[2], v[3]}; } +force_inline float vextract0(v4sf v) noexcept +{ return 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 +{ + out1 = unpacklo(in1, in2); + out2 = unpackhi(in1, in2); +} +force_inline void uninterleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + out1 = v4sf{in1[0], in1[2], in2[0], in2[2]}; + out2 = v4sf{in1[1], in1[3], in2[1], in2[3]}; +} + +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]}; } + +#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 +using v4sf = float; +constexpr uint SimdSize{1}; +force_inline constexpr v4sf vmul(v4sf a, v4sf b) noexcept { return a * b; } +force_inline constexpr v4sf vadd(v4sf a, v4sf b) noexcept { return a + b; } +force_inline constexpr v4sf vmadd(v4sf a, v4sf b, v4sf c) noexcept { return a*b + c; } +force_inline constexpr v4sf vsub(v4sf a, v4sf b) noexcept { return a - b; } +force_inline constexpr v4sf ld_ps1(float a) noexcept { return a; } + +#else + +[[maybe_unused]] inline +auto valigned(const float *ptr) noexcept -> bool +{ + static constexpr uintptr_t alignmask{SimdSize*sizeof(float) - 1}; + return (reinterpret_cast(ptr) & alignmask) == 0; +} +#endif + +// 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) + +inline void assertv4(const al::span v_f [[maybe_unused]], + const float f0 [[maybe_unused]], const float f1 [[maybe_unused]], + const float f2 [[maybe_unused]], const float f3 [[maybe_unused]]) +{ assert(v_f[0] == f0 && v_f[1] == f1 && v_f[2] == f2 && v_f[3] == f3); } + +template +constexpr auto make_float_array(std::integer_sequence) +{ return std::array{static_cast(N)...}; } + +/* detect bugs with the vector support macros */ +[[maybe_unused]] void validate_pffft_simd() +{ + using float4 = std::array; + static constexpr auto f = make_float_array(std::make_index_sequence<16>{}); + + v4sf a0_v{vset4(f[ 0], f[ 1], f[ 2], f[ 3])}; + v4sf a1_v{vset4(f[ 4], f[ 5], f[ 6], f[ 7])}; + v4sf a2_v{vset4(f[ 8], f[ 9], f[10], f[11])}; + v4sf a3_v{vset4(f[12], f[13], f[14], f[15])}; + v4sf u_v{}; + + auto t_v = vzero(); + auto t_f = al::bit_cast(t_v); + printf("VZERO=[%2g %2g %2g %2g]\n", t_f[0], t_f[1], t_f[2], t_f[3]); + assertv4(t_f, 0, 0, 0, 0); + + t_v = vadd(a1_v, a2_v); + t_f = al::bit_cast(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_f, 12, 14, 16, 18); + + t_v = vmul(a1_v, a2_v); + t_f = al::bit_cast(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_f, 32, 45, 60, 77); + + t_v = vmadd(a1_v, a2_v, a0_v); + t_f = al::bit_cast(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_f, 32, 46, 62, 80); + + interleave2(a1_v, a2_v, t_v, u_v); + t_f = al::bit_cast(t_v); + auto u_f = al::bit_cast(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_f, 4, 8, 5, 9); + assertv4(u_f, 6, 10, 7, 11); + + uninterleave2(a1_v, a2_v, t_v, u_v); + t_f = al::bit_cast(t_v); + u_f = al::bit_cast(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_f, 4, 6, 8, 10); + assertv4(u_f, 5, 7, 9, 11); + + t_v = ld_ps1(f[15]); + t_f = al::bit_cast(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_f, 15, 15, 15, 15); + + t_v = vswaphl(a1_v, a2_v); + t_f = al::bit_cast(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_f, 8, 9, 6, 7); + + vtranspose4(a0_v, a1_v, a2_v, a3_v); + auto a0_f = al::bit_cast(a0_v); + auto a1_f = al::bit_cast(a1_v); + auto a2_f = al::bit_cast(a2_v); + auto a3_f = al::bit_cast(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_f, 0, 4, 8, 12); + assertv4(a1_f, 1, 5, 9, 13); + assertv4(a2_f, 2, 6, 10, 14); + assertv4(a3_f, 3, 7, 11, 15); +} +#endif //!PFFFT_SIMD_DISABLE + +/* SSE and co like 16-bytes aligned pointers */ +/* with a 64-byte alignment, we are even aligned on L2 cache lines... */ +constexpr auto V4sfAlignment = size_t(64); +constexpr auto V4sfAlignVal = std::align_val_t(V4sfAlignment); + +/* NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + * FIXME: Converting this from raw pointers to spans or something will probably + * need significant work to maintain performance, given non-sequential range- + * checked accesses and lack of 'restrict' to indicate non-aliased memory. At + * least, some tests should be done to check the impact of using range-checked + * spans here before blindly switching. + */ +/* + 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 *const 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 *const wa1, 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}; + const auto wa2 = wa1 + 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 *const wa1, 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 + { + const auto wa2 = wa1 + ido; + const auto wa3 = wa2 + ido; + 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 *const wa1, 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)}; + + auto cc_ref = [&cc,ido](size_t a_1, size_t a_2) noexcept -> auto& + { return cc[(a_2-1)*ido + a_1 + 1]; }; + auto ch_ref = [&ch,ido,l1](size_t a_1, size_t a_3) noexcept -> auto& + { return ch[(a_3-1)*l1*ido + a_1 + 1]; }; + + assert(ido > 2); + + const auto wa2 = wa1 + ido; + const auto wa3 = wa2 + ido; + const auto wa4 = wa3 + ido; + 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; + } + } +} + +NOINLINE void radf2_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, + v4sf *RESTRICT ch, const float *const 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 *const 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 *const wa1) +{ + 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; + + const auto wa2 = wa1 + ido; + 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 *const wa1) +{ + 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 auto wa2 = wa1 + ido; + 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 *const wa1) +{ + 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) + { + const auto wa2 = wa1 + ido; + const auto wa3 = wa2 + ido; + + 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 * -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 *const wa1) +{ + 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) + { + const auto wa2 = wa1 + ido; + const auto wa3 = wa2 + ido; + + 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 *const wa1) +{ + 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)}; + + auto cc_ref = [&cc,l1,ido](size_t a_1, size_t a_2, size_t a_3) noexcept -> auto& + { return cc[(a_3*l1 + a_2)*ido + a_1]; }; + auto ch_ref = [&ch,ido](size_t a_1, size_t a_2, size_t a_3) noexcept -> auto& + { return ch[(a_3*5 + a_2)*ido + a_1]; }; + + /* Parameter adjustments */ + ch -= 1 + ido * 6; + cc -= 1 + ido * (1 + l1); + + const auto wa2 = wa1 + ido; + const auto wa3 = wa2 + ido; + const auto wa4 = wa3 + ido; + + /* 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); + } + } +} /* radf5 */ + +void radb5_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, v4sf *RESTRICT ch, + const float *const wa1) +{ + 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)}; + + auto cc_ref = [&cc,ido](size_t a_1, size_t a_2, size_t a_3) noexcept -> auto& + { return cc[(a_3*5 + a_2)*ido + a_1]; }; + auto ch_ref = [&ch,ido,l1](size_t a_1, size_t a_2, size_t a_3) noexcept -> auto& + { return ch[(a_3*l1 + a_2)*ido + a_1]; }; + + /* Parameter adjustments */ + ch -= 1 + ido*(1 + l1); + cc -= 1 + ido*6; + + const auto wa2 = wa1 + ido; + const auto wa3 = wa2 + ido; + const auto wa4 = wa3 + ido; + + /* 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; + } + } +} /* radb5 */ + +NOINLINE v4sf *rfftf1_ps(const size_t n, const v4sf *input_readonly, v4sf *work1, v4sf *work2, + const float *wa, const al::span 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}; + size_t k1{1}; + while(true) + { + const size_t kh{nf - k1}; + const size_t ip{ifac[kh + 2]}; + const size_t l1{l2 / ip}; + const size_t ido{n / l2}; + iw -= (ip - 1)*ido; + switch(ip) + { + case 5: + radf5_ps(ido, l1, in, out, &wa[iw]); + break; + case 4: + radf4_ps(ido, l1, in, out, &wa[iw]); + break; + case 3: + radf3_ps(ido, l1, in, out, &wa[iw]); + break; + case 2: + radf2_ps(ido, l1, in, out, &wa[iw]); + break; + default: + assert(0); + } + if(++k1 > nf) + return out; + + l2 = l1; + if(out == work2) + { + out = work1; + in = work2; + } + else + { + out = work2; + in = work1; + } + } +} /* rfftf1 */ + +NOINLINE v4sf *rfftb1_ps(const size_t n, const v4sf *input_readonly, v4sf *work1, v4sf *work2, + const float *wa, const al::span 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}; + size_t k1{1}; + while(true) + { + const size_t ip{ifac[k1 + 1]}; + const size_t l2{ip*l1}; + const size_t ido{n / l2}; + switch(ip) + { + case 5: + radb5_ps(ido, l1, in, out, &wa[iw]); + break; + case 4: + radb4_ps(ido, l1, in, out, &wa[iw]); + break; + case 3: + radb3_ps(ido, l1, in, out, &wa[iw]); + break; + case 2: + radb2_ps(ido, l1, in, out, &wa[iw]); + break; + default: + assert(0); + } + if(++k1 > nf) + return out; + + l1 = l2; + iw += (ip - 1)*ido; + + if(out == work2) + { + out = work1; + in = work2; + } + else + { + out = work2; + in = work1; + } + } +} + +v4sf *cfftf1_ps(const size_t n, const v4sf *input_readonly, v4sf *work1, v4sf *work2, + const float *wa, const al::span 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}; + size_t k1{2}; + while(true) + { + 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: + passf5_ps(idot, l1, in, out, &wa[iw], fsign); + break; + case 4: + passf4_ps(idot, l1, in, out, &wa[iw], fsign); + break; + case 3: + passf3_ps(idot, l1, in, out, &wa[iw], fsign); + break; + case 2: + passf2_ps(idot, l1, in, out, &wa[iw], fsign); + break; + default: + assert(0); + } + if(++k1 > nf+1) + return out; + + l1 = l2; + iw += (ip - 1)*idot; + if(out == work2) + { + out = work1; + in = work2; + } + else + { + out = work2; + in = work1; + } + } +} + + +uint decompose(const uint n, const al::span ifac, const al::span 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 ifac) +{ + static constexpr std::array ntryh{4u,2u,3u,5u}; + + 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(ld)*argh}; + double fi{0.0}; + for(size_t ii{2};ii < ido;ii += 2) + { + fi += 1.0; + wa[i++] = static_cast(std::cos(fi*argld)); + wa[i++] = static_cast(std::sin(fi*argld)); + } + is += ido; + } + l1 = l2; + } +} /* rffti1 */ + +void cffti1_ps(const uint n, float *wa, const al::span ifac) +{ + static constexpr std::array ntryh{5u,3u,4u,2u}; + + 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(ld)*argh}; + double fi{0.0}; + for(size_t ii{3};ii < idot;ii += 2) + { + fi += 1.0; + wa[++i] = static_cast(std::cos(fi*argld)); + wa[++i] = static_cast(std::sin(fi*argld)); + } + if(ip > 5) + { + wa[i1-1] = wa[i-1]; + wa[i1] = wa[i]; + } + } + l1 = l2; + } +} /* cffti1 */ + +} // namespace + +/* NOLINTNEXTLINE(clang-analyzer-optin.performance.Padding) */ +struct PFFFT_Setup { + uint N{}; + uint Ncvec{}; /* nb of complex simd vectors (N/4 if PFFFT_COMPLEX, N/8 if PFFFT_REAL) */ + std::array ifac{}; + pffft_transform_t transform{}; + + float *twiddle{}; /* N/4 elements */ + al::span e; /* N/4*3 elements */ + + alignas(V4sfAlignment) std::byte end; +}; + +PFFFTSetupPtr 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*SimdSize*SimdSize)) == 0); + else + assert((N%(SimdSize*SimdSize)) == 0); + + const uint Ncvec{(transform == PFFFT_REAL ? N/2 : N) / SimdSize}; + + const size_t storelen{std::max(offsetof(PFFFT_Setup, end) + 2_zu*Ncvec*sizeof(v4sf), + sizeof(PFFFT_Setup))}; + auto storage = static_cast>(::operator new[](storelen, V4sfAlignVal)); + al::span extrastore{&storage[offsetof(PFFFT_Setup, end)], 2_zu*Ncvec*sizeof(v4sf)}; + + PFFFTSetupPtr s{::new(storage) PFFFT_Setup{}}; + s->N = N; + s->transform = transform; + s->Ncvec = Ncvec; + + const size_t ecount{2_zu*Ncvec*(SimdSize-1)/SimdSize}; + s->e = {std::launder(reinterpret_cast(extrastore.data())), ecount}; + s->twiddle = std::launder(reinterpret_cast(&extrastore[ecount*sizeof(v4sf)])); + + if constexpr(SimdSize > 1) + { + auto e = std::vector(s->e.size()*SimdSize, 0.0f); + for(size_t k{0};k < s->Ncvec;++k) + { + const size_t i{k / SimdSize}; + const size_t j{k % SimdSize}; + for(size_t m{0};m < SimdSize-1;++m) + { + const double A{-2.0*al::numbers::pi*static_cast((m+1)*k) / N}; + e[((i*3 + m)*2 + 0)*SimdSize + j] = static_cast(std::cos(A)); + e[((i*3 + m)*2 + 1)*SimdSize + j] = static_cast(std::sin(A)); + } + } + std::memcpy(s->e.data(), e.data(), e.size()*sizeof(float)); + } + if(transform == PFFFT_REAL) + rffti1_ps(N/SimdSize, s->twiddle, s->ifac); + else + cffti1_ps(N/SimdSize, 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/SimdSize) + s = nullptr; + + return s; +} + + +void pffft_destroy_setup(gsl::owner s) noexcept +{ + std::destroy_at(s); + ::operator delete[](gsl::owner{s}, V4sfAlignVal); +} + +#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/SimdSize}; // 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/SimdSize}; // 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/2.0f}; + + assert(in != out); + const size_t dk{Ncvec/SimdSize}; // 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>(in[0]); + const auto ci = al::bit_cast>(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}; + + assert(in != out); + const size_t dk{Ncvec/SimdSize}; // 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 Xr{}, Xi{}; + for(size_t k{0};k < SimdSize;++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}; + + std::array buff{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.data()); + } + 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.data()); + } + if(ordered) + pffft_zreorder(setup, reinterpret_cast(buff[!ib]), + reinterpret_cast(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(vinput), + reinterpret_cast(buff[ib]), PFFFT_BACKWARD); + vinput = buff[ib]; + ib = !ib; + } + if(setup->transform == PFFFT_REAL) + { + pffft_real_preprocess(Ncvec, vinput, buff[ib], setup->e.data()); + 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.data()); + 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(in)}; + v4sf *RESTRICT vout{reinterpret_cast(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/SimdSize/2); + reversed_copy(dk, vin+6, 8, vout + N/SimdSize); + } + 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/SimdSize/4, vout + N/SimdSize - 6, -8); + unreversed_copy(dk, vin + 3_uz*N/SimdSize/4, vout + N/SimdSize - 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(a)}; + const v4sf *RESTRICT vb{reinterpret_cast(b)}; + v4sf *RESTRICT vab{reinterpret_cast(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(a)}; + const v4sf *RESTRICT vb{reinterpret_cast(b)}; + v4sf *RESTRICT vab{reinterpret_cast(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(al::assume_aligned<16>(input)), + reinterpret_cast(al::assume_aligned<16>(output)), + reinterpret_cast(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(al::assume_aligned<16>(input)), + reinterpret_cast(al::assume_aligned<16>(output)), + reinterpret_cast(al::assume_aligned<16>(work)), direction, true); +} + +#else // defined(PFFFT_SIMD_DISABLE) + +// standard routine using scalar floats, without SIMD stuff. + +namespace { + +void pffft_transform_internal(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 + +void pffft_zreorder(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) */ +/* NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ diff --git a/3rdparty/openal/common/pffft.h b/3rdparty/openal/common/pffft.h new file mode 100644 index 000000000000..ac7e2940ac86 --- /dev/null +++ b/3rdparty/openal/common/pffft.h @@ -0,0 +1,212 @@ +/* Copyright (c) 2013 Julien Pommier ( pommier@modartt.com ) + + 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 +#include + +#include "almalloc.h" + + +/* opaque struct holding internal stuff (precomputed twiddle factors) this + * struct can be shared by many threads as it contains only read-only data. + */ +struct PFFFT_Setup; + +/* direction of the transform */ +enum pffft_direction_t { PFFFT_FORWARD, PFFFT_BACKWARD }; + +/* type of transform */ +enum pffft_transform_t { PFFFT_REAL, PFFFT_COMPLEX }; + +void pffft_destroy_setup(gsl::owner setup) noexcept; +struct PFFFTSetupDeleter { + void operator()(gsl::owner setup) const noexcept { pffft_destroy_setup(setup); } +}; +using PFFFTSetupPtr = std::unique_ptr; + +/** + * 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. + */ +PFFFTSetupPtr pffft_new_setup(unsigned int N, pffft_transform_t transform); + +/** + * 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); + + +struct PFFFTSetup { + PFFFTSetupPtr mSetup; + + PFFFTSetup() = default; + PFFFTSetup(const PFFFTSetup&) = delete; + PFFFTSetup(PFFFTSetup&& rhs) noexcept = default; + explicit PFFFTSetup(std::nullptr_t) noexcept { } + explicit PFFFTSetup(unsigned int n, pffft_transform_t transform) + : mSetup{pffft_new_setup(n, transform)} + { } + ~PFFFTSetup() = default; + + PFFFTSetup& operator=(const PFFFTSetup&) = delete; + PFFFTSetup& operator=(PFFFTSetup&& rhs) noexcept = default; + + [[nodiscard]] explicit operator bool() const noexcept { return mSetup != nullptr; } + + void transform(const float *input, float *output, float *work, pffft_direction_t direction) const + { pffft_transform(mSetup.get(), input, output, work, direction); } + + void transform_ordered(const float *input, float *output, float *work, + pffft_direction_t direction) const + { pffft_transform_ordered(mSetup.get(), input, output, work, direction); } + + void zreorder(const float *input, float *output, pffft_direction_t direction) const + { pffft_zreorder(mSetup.get(), input, output, direction); } + + void zconvolve_scale_accumulate(const float *dft_a, const float *dft_b, float *dft_ab, + float scaling) const + { pffft_zconvolve_scale_accumulate(mSetup.get(), dft_a, dft_b, dft_ab, scaling); } + + void zconvolve_accumulate(const float *dft_a, const float *dft_b, float *dft_ab) const + { pffft_zconvolve_accumulate(mSetup.get(), dft_a, dft_b, dft_ab); } +}; + +#endif // PFFFT_H diff --git a/3rdparty/openal/common/phase_shifter.h b/3rdparty/openal/common/phase_shifter.h index 061e91769813..5ff867400121 100644 --- a/3rdparty/openal/common/phase_shifter.h +++ b/3rdparty/openal/common/phase_shifter.h @@ -1,96 +1,60 @@ #ifndef PHASE_SHIFTER_H #define PHASE_SHIFTER_H -#ifdef HAVE_SSE_INTRINSICS +#include "config_simd.h" + +#if HAVE_SSE_INTRINSICS #include -#elif defined(HAVE_NEON) +#elif HAVE_NEON #include #endif #include -#include +#include +#include -#include "alcomplex.h" +#include "alnumbers.h" #include "alspan.h" +#include "opthelpers.h" /* 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. */ -template -struct PhaseShifterT { +template +struct SIMDALIGN PhaseShifterT { static_assert(FilterSize >= 16, "FilterSize needs to be at least 16"); static_assert((FilterSize&(FilterSize-1)) == 0, "FilterSize needs to be power-of-two"); alignas(16) std::array mCoeffs{}; - /* Some notes on this filter construction. - * - * A wide-band phase-shift filter needs a delay to maintain linearity. A - * dirac impulse in the center of a time-domain buffer represents a filter - * passing all frequencies through as-is with a pure delay. Converting that - * to the frequency domain, adjusting the phase of each frequency bin by - * +90 degrees, then converting back to the time domain, results in a FIR - * filter that applies a +90 degree wide-band phase-shift. - * - * A particularly notable aspect of the time-domain filter response is that - * every other coefficient is 0. This allows doubling the effective size of - * the filter, by storing only the non-0 coefficients and double-stepping - * over the input to apply it. - * - * Additionally, the resulting filter is independent of the sample rate. - * The same filter can be applied regardless of the device's sample rate - * and achieve the same effect. - */ PhaseShifterT() { - using complex_d = std::complex; - constexpr size_t fft_size{FilterSize}; - constexpr size_t half_size{fft_size / 2}; - - auto fftBuffer = std::make_unique(fft_size); - std::fill_n(fftBuffer.get(), fft_size, complex_d{}); - fftBuffer[half_size] = 1.0; - - forward_fft(al::span{fftBuffer.get(), fft_size}); - for(size_t i{0};i < half_size+1;++i) - fftBuffer[i] = complex_d{-fftBuffer[i].imag(), fftBuffer[i].real()}; - 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}); - - auto fftiter = fftBuffer.get() + half_size + (FilterSize/2 - 1); - for(float &coeff : mCoeffs) + /* Every other coefficient is 0, so we only need to calculate and store + * the non-0 terms and double-step over the input to apply it. The + * calculated coefficients are in reverse to make applying in the time- + * domain more efficient. + */ + for(std::size_t i{0};i < FilterSize/2;++i) { - coeff = static_cast(fftiter->real() / double{fft_size}); - fftiter -= 2; + const int k{static_cast(i*2 + 1) - int{FilterSize/2}}; + + /* Calculate the Blackman window value for this coefficient. */ + const double w{2.0*al::numbers::pi * static_cast(i*2 + 1) + / double{FilterSize}}; + const double window{0.3635819 - 0.4891775*std::cos(w) + 0.1365995*std::cos(2.0*w) + - 0.0106411*std::cos(3.0*w)}; + + const double pk{al::numbers::pi * static_cast(k)}; + mCoeffs[i] = static_cast(window * (1.0-std::cos(pk)) / pk); } } - void process(al::span dst, const float *RESTRICT src) const; + void process(const al::span dst, const al::span 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; - } +#if HAVE_NEON static auto unpacklo(float32x4_t a, float32x4_t b) { float32x2x2_t result{vzip_f32(vget_low_f32(a), vget_low_f32(b))}; @@ -109,105 +73,141 @@ struct PhaseShifterT { ret = vsetq_lane_f32(d, ret, 3); return ret; } + static void vtranspose4(float32x4_t &x0, float32x4_t &x1, float32x4_t &x2, float32x4_t &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]; + } #endif }; -template -inline void PhaseShifterT::process(al::span dst, const float *RESTRICT src) const +template +NOINLINE inline +void PhaseShifterT::process(const al::span dst, const al::span src) const { -#ifdef HAVE_SSE_INTRINSICS - if(size_t todo{dst.size()>>1}) + auto in = src.begin(); +#if HAVE_SSE_INTRINSICS + if(const std::size_t todo{dst.size()>>2}) { - auto *out = reinterpret_cast<__m64*>(dst.data()); - do { - __m128 r04{_mm_setzero_ps()}; - __m128 r14{_mm_setzero_ps()}; - for(size_t j{0};j < mCoeffs.size();j+=4) + auto out = al::span{reinterpret_cast<__m128*>(dst.data()), todo}; + std::generate(out.begin(), out.end(), [&in,this] + { + __m128 r0{_mm_setzero_ps()}; + __m128 r1{_mm_setzero_ps()}; + __m128 r2{_mm_setzero_ps()}; + __m128 r3{_mm_setzero_ps()}; + for(std::size_t j{0};j < mCoeffs.size();j+=4) { const __m128 coeffs{_mm_load_ps(&mCoeffs[j])}; - const __m128 s0{_mm_loadu_ps(&src[j*2])}; - const __m128 s1{_mm_loadu_ps(&src[j*2 + 4])}; + const __m128 s0{_mm_loadu_ps(&in[j*2])}; + const __m128 s1{_mm_loadu_ps(&in[j*2 + 4])}; + const __m128 s2{_mm_movehl_ps(_mm_movelh_ps(s1, s1), s0)}; + const __m128 s3{_mm_loadh_pi(_mm_movehl_ps(s1, s1), + reinterpret_cast(&in[j*2 + 8]))}; __m128 s{_mm_shuffle_ps(s0, s1, _MM_SHUFFLE(2, 0, 2, 0))}; - r04 = _mm_add_ps(r04, _mm_mul_ps(s, coeffs)); + r0 = _mm_add_ps(r0, _mm_mul_ps(s, coeffs)); s = _mm_shuffle_ps(s0, s1, _MM_SHUFFLE(3, 1, 3, 1)); - r14 = _mm_add_ps(r14, _mm_mul_ps(s, coeffs)); - } - src += 2; + r1 = _mm_add_ps(r1, _mm_mul_ps(s, coeffs)); - __m128 r4{_mm_add_ps(_mm_unpackhi_ps(r04, r14), _mm_unpacklo_ps(r04, r14))}; - r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); + s = _mm_shuffle_ps(s2, s3, _MM_SHUFFLE(2, 0, 2, 0)); + r2 = _mm_add_ps(r2, _mm_mul_ps(s, coeffs)); + + s = _mm_shuffle_ps(s2, s3, _MM_SHUFFLE(3, 1, 3, 1)); + r3 = _mm_add_ps(r3, _mm_mul_ps(s, coeffs)); + } + in += 4; - _mm_storel_pi(out, r4); - ++out; - } while(--todo); + _MM_TRANSPOSE4_PS(r0, r1, r2, r3); + return _mm_add_ps(_mm_add_ps(r0, r1), _mm_add_ps(r2, r3)); + }); } - if((dst.size()&1)) + if(const std::size_t todo{dst.size()&3}) { - __m128 r4{_mm_setzero_ps()}; - for(size_t j{0};j < mCoeffs.size();j+=4) + auto out = dst.last(todo); + std::generate(out.begin(), out.end(), [&in,this] { - const __m128 coeffs{_mm_load_ps(&mCoeffs[j])}; - const __m128 s{_mm_setr_ps(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])}; - r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs)); - } - r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); - r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); - - dst.back() = _mm_cvtss_f32(r4); + __m128 r4{_mm_setzero_ps()}; + for(std::size_t j{0};j < mCoeffs.size();j+=4) + { + const __m128 coeffs{_mm_load_ps(&mCoeffs[j])}; + const __m128 s{_mm_setr_ps(in[j*2], in[j*2 + 2], in[j*2 + 4], in[j*2 + 6])}; + r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs)); + } + ++in; + r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); + r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); + return _mm_cvtss_f32(r4); + }); } -#elif defined(HAVE_NEON) +#elif HAVE_NEON - size_t pos{0}; - if(size_t todo{dst.size()>>1}) + if(const std::size_t todo{dst.size()>>2}) { - do { - float32x4_t r04{vdupq_n_f32(0.0f)}; - float32x4_t r14{vdupq_n_f32(0.0f)}; - for(size_t j{0};j < mCoeffs.size();j+=4) + auto out = al::span{reinterpret_cast(dst.data()), todo}; + std::generate(out.begin(), out.end(), [&in,this] + { + float32x4_t r0{vdupq_n_f32(0.0f)}; + float32x4_t r1{vdupq_n_f32(0.0f)}; + float32x4_t r2{vdupq_n_f32(0.0f)}; + float32x4_t r3{vdupq_n_f32(0.0f)}; + for(std::size_t j{0};j < mCoeffs.size();j+=4) { 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])}; - - r04 = vmlaq_f32(r04, shuffle_2020(s0, s1), coeffs); - r14 = vmlaq_f32(r14, shuffle_3131(s0, s1), coeffs); + const float32x4_t s0{vld1q_f32(&in[j*2])}; + const float32x4_t s1{vld1q_f32(&in[j*2 + 4])}; + const float32x4_t s2{vcombine_f32(vget_high_f32(s0), vget_low_f32(s1))}; + const float32x4_t s3{vcombine_f32(vget_high_f32(s1), vld1_f32(&in[j*2 + 8]))}; + const float32x4x2_t values0{vuzpq_f32(s0, s1)}; + const float32x4x2_t values1{vuzpq_f32(s2, s3)}; + + r0 = vmlaq_f32(r0, values0.val[0], coeffs); + r1 = vmlaq_f32(r1, values0.val[1], coeffs); + r2 = vmlaq_f32(r2, values1.val[0], coeffs); + r3 = vmlaq_f32(r3, values1.val[1], coeffs); } - src += 2; + in += 4; - float32x4_t r4{vaddq_f32(unpackhi(r04, r14), unpacklo(r04, r14))}; - float32x2_t r2{vadd_f32(vget_low_f32(r4), vget_high_f32(r4))}; - - vst1_f32(&dst[pos], r2); - pos += 2; - } while(--todo); + vtranspose4(r0, r1, r2, r3); + return vaddq_f32(vaddq_f32(r0, r1), vaddq_f32(r2, r3)); + }); } - if((dst.size()&1)) + if(const std::size_t todo{dst.size()&3}) { - float32x4_t r4{vdupq_n_f32(0.0f)}; - for(size_t j{0};j < mCoeffs.size();j+=4) + auto out = dst.last(todo); + std::generate(out.begin(), out.end(), [&in,this] { - const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])}; - const float32x4_t s{load4(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])}; - r4 = vmlaq_f32(r4, s, coeffs); - } - r4 = vaddq_f32(r4, vrev64q_f32(r4)); - dst[pos] = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); + float32x4_t r4{vdupq_n_f32(0.0f)}; + for(std::size_t j{0};j < mCoeffs.size();j+=4) + { + const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])}; + const float32x4_t s{load4(in[j*2], in[j*2 + 2], in[j*2 + 4], in[j*2 + 6])}; + r4 = vmlaq_f32(r4, s, coeffs); + } + ++in; + r4 = vaddq_f32(r4, vrev64q_f32(r4)); + return vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); + }); } #else - for(float &output : dst) + std::generate(dst.begin(), dst.end(), [&in,this] { float ret{0.0f}; - for(size_t j{0};j < mCoeffs.size();++j) - ret += src[j*2] * mCoeffs[j]; - - output = ret; - ++src; - } + for(std::size_t j{0};j < mCoeffs.size();++j) + ret += in[j*2] * mCoeffs[j]; + ++in; + return ret; + }); #endif } diff --git a/3rdparty/openal/common/polyphase_resampler.cpp b/3rdparty/openal/common/polyphase_resampler.cpp index 14f7e40d877a..d74ecdff6910 100644 --- a/3rdparty/openal/common/polyphase_resampler.cpp +++ b/3rdparty/openal/common/polyphase_resampler.cpp @@ -3,45 +3,46 @@ #include #include +#include +#include +#include #include "alnumbers.h" #include "opthelpers.h" +using uint = unsigned int; + namespace { 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); -} /* 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 +constexpr auto cyl_bessel_i(T nu, U x) -> U { - // 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 +51,19 @@ constexpr double BesselI_0(const double x) term *= y * y; sum += term; } while(sum != last_sum); - return sum; + return static_cast(sum); +} + +/* 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 +80,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 +125,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(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 +138,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; @@ -145,78 +146,70 @@ void PPhaseResampler::init(const uint srcRate, const uint dstRate) * ends before the nyquist (0.5). Both are scaled by the downsampling * factor. */ - double cutoff, width; - if(mP > mQ) - { - cutoff = 0.475 / mP; - width = 0.05 / mP; - } - else - { - cutoff = 0.475 / mQ; - width = 0.05 / mQ; - } + const auto [cutoff, width] = (mP > mQ) ? std::make_tuple(0.475 / mP, 0.05 / mP) + : std::make_tuple(0.475 / mQ, 0.05 / mQ); + // A rejection of -180 dB is used for the stop band. Round up when // 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 // polyphase filter implementation. -void PPhaseResampler::process(const uint inN, const double *in, const uint outN, double *out) +void PPhaseResampler::process(const al::span in, const al::span out) { - if(outN == 0) UNLIKELY + if(out.empty()) UNLIKELY return; // Handle in-place operation. std::vector workspace; - double *work{out}; - if(work == in) UNLIKELY + al::span work{out}; + if(work.data() == in.data()) UNLIKELY { - workspace.resize(outN); - work = workspace.data(); + workspace.resize(out.size()); + work = workspace; } // Resample the input. const uint p{mP}, q{mQ}, m{mM}, l{mL}; - const double *f{mF.data()}; - for(uint i{0};i < outN;i++) + const al::span f{mF}; + for(uint i{0};i < out.size();i++) { // Input starts at l to compensate for the filter delay. This will // drop any build-up from the first half of the filter. - size_t j_f{(l + q*i) % p}; - size_t j_s{(l + q*i) / p}; + std::size_t j_f{(l + q*i) % p}; + std::size_t j_s{(l + q*i) / p}; - // Only take input when 0 <= j_s < inN. + // Only take input when 0 <= j_s < in.size(). double r{0.0}; if(j_f < m) LIKELY { - size_t filt_len{(m-j_f+p-1) / p}; - if(j_s+1 > inN) LIKELY + std::size_t filt_len{(m-j_f+p-1) / p}; + if(j_s+1 > in.size()) LIKELY { - size_t skip{std::min(j_s+1 - inN, filt_len)}; + std::size_t skip{std::min(j_s+1 - in.size(), filt_len)}; j_f += p*skip; j_s -= skip; filt_len -= skip; } - if(size_t todo{std::min(j_s+1, filt_len)}) LIKELY + std::size_t todo{std::min(j_s+1, filt_len)}; + while(todo) { - do { - r += f[j_f] * in[j_s]; - j_f += p; - --j_s; - } while(--todo); + r += f[j_f] * in[j_s]; + j_f += p; --j_s; + --todo; } } work[i] = r; } // Clean up after in-place operation. - if(work != out) - std::copy_n(work, outN, out); + if(work.data() != out.data()) + std::copy(work.cbegin(), work.cend(), out.begin()); } diff --git a/3rdparty/openal/common/polyphase_resampler.h b/3rdparty/openal/common/polyphase_resampler.h index 557485bb2f61..0795c80d897e 100644 --- a/3rdparty/openal/common/polyphase_resampler.h +++ b/3rdparty/openal/common/polyphase_resampler.h @@ -3,6 +3,8 @@ #include +#include "alspan.h" + using uint = unsigned int; @@ -35,12 +37,12 @@ using uint = unsigned int; struct PPhaseResampler { void init(const uint srcRate, const uint dstRate); - void process(const uint inN, const double *in, const uint outN, double *out); + void process(const al::span in, const al::span out); explicit operator bool() const noexcept { return !mF.empty(); } private: - uint mP, mQ, mM, mL; + uint mP{}, mQ{}, mM{}, mL{}; std::vector mF; }; diff --git a/3rdparty/openal/common/ringbuffer.cpp b/3rdparty/openal/common/ringbuffer.cpp index af1f36695e93..23c48806a6cd 100644 --- a/3rdparty/openal/common/ringbuffer.cpp +++ b/3rdparty/openal/common/ringbuffer.cpp @@ -23,201 +23,151 @@ #include "ringbuffer.h" #include -#include +#include +#include +#include #include -#include "almalloc.h" +#include "alnumeric.h" +#include "alspan.h" -RingBufferPtr RingBuffer::Create(std::size_t sz, std::size_t elem_sz, int limit_writes) +auto RingBuffer::Create(std::size_t sz, std::size_t elem_sz, bool limit_writes) -> RingBufferPtr { std::size_t power_of_two{0u}; if(sz > 0) { - power_of_two = sz; + power_of_two = sz - 1; power_of_two |= power_of_two>>1; power_of_two |= power_of_two>>2; power_of_two |= power_of_two>>4; power_of_two |= power_of_two>>8; power_of_two |= power_of_two>>16; - if constexpr(SIZE_MAX > UINT_MAX) + if constexpr(sizeof(size_t) > sizeof(uint32_t)) power_of_two |= power_of_two>>32; } ++power_of_two; - if(power_of_two <= sz || power_of_two > std::numeric_limits::max()/elem_sz) + if(power_of_two < sz || power_of_two > std::numeric_limits::max()>>1 + || power_of_two > std::numeric_limits::max()/elem_sz) throw std::overflow_error{"Ring buffer size overflow"}; 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; - rb->mElemSize = elem_sz; + RingBufferPtr rb{new(FamCount(bufbytes)) RingBuffer{limit_writes ? sz : power_of_two, + power_of_two-1, elem_sz, bufbytes}}; return rb; } void RingBuffer::reset() noexcept { - mWritePtr.store(0, std::memory_order_relaxed); - mReadPtr.store(0, std::memory_order_relaxed); + mWriteCount.store(0, std::memory_order_relaxed); + mReadCount.store(0, std::memory_order_relaxed); std::fill_n(mBuffer.begin(), (mSizeMask+1)*mElemSize, std::byte{}); } -std::size_t RingBuffer::read(void *dest, std::size_t cnt) noexcept +auto RingBuffer::read(void *dest, std::size_t count) noexcept -> std::size_t { - const std::size_t free_cnt{readSpace()}; - if(free_cnt == 0) return 0; + const std::size_t w{mWriteCount.load(std::memory_order_acquire)}; + const std::size_t r{mReadCount.load(std::memory_order_relaxed)}; + const std::size_t readable{w - r}; + if(readable == 0) return 0; - const std::size_t to_read{std::min(cnt, free_cnt)}; - std::size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask}; + const std::size_t to_read{std::min(count, readable)}; + const std::size_t read_idx{r & mSizeMask}; - std::size_t n1, n2; - const std::size_t cnt2{read_ptr + to_read}; - if(cnt2 > mSizeMask+1) - { - n1 = mSizeMask+1 - read_ptr; - n2 = cnt2 & mSizeMask; - } - else - { - n1 = to_read; - n2 = 0; - } + const std::size_t rdend{read_idx + to_read}; + const auto [n1, n2] = (rdend <= mSizeMask+1) ? std::array{to_read, 0_uz} + : std::array{mSizeMask+1 - read_idx, rdend&mSizeMask}; - auto outiter = std::copy_n(mBuffer.begin() + read_ptr*mElemSize, n1*mElemSize, - static_cast(dest)); - read_ptr += n1; + auto dstbytes = al::span{static_cast(dest), count*mElemSize}; + auto outiter = std::copy_n(mBuffer.begin() + ptrdiff_t(read_idx*mElemSize), n1*mElemSize, + dstbytes.begin()); if(n2 > 0) - { std::copy_n(mBuffer.begin(), n2*mElemSize, outiter); - read_ptr += n2; - } - mReadPtr.store(read_ptr, std::memory_order_release); + mReadCount.store(r+n1+n2, std::memory_order_release); return to_read; } -std::size_t RingBuffer::peek(void *dest, std::size_t cnt) const noexcept +auto RingBuffer::peek(void *dest, std::size_t count) const noexcept -> std::size_t { - const std::size_t free_cnt{readSpace()}; - if(free_cnt == 0) return 0; + const std::size_t w{mWriteCount.load(std::memory_order_acquire)}; + const std::size_t r{mReadCount.load(std::memory_order_relaxed)}; + const std::size_t readable{w - r}; + if(readable == 0) return 0; - const std::size_t to_read{std::min(cnt, free_cnt)}; - std::size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask}; + const std::size_t to_read{std::min(count, readable)}; + const std::size_t read_idx{r & mSizeMask}; - std::size_t n1, n2; - const std::size_t cnt2{read_ptr + to_read}; - if(cnt2 > mSizeMask+1) - { - n1 = mSizeMask+1 - read_ptr; - n2 = cnt2 & mSizeMask; - } - else - { - n1 = to_read; - n2 = 0; - } + const std::size_t rdend{read_idx + to_read}; + const auto [n1, n2] = (rdend <= mSizeMask+1) ? std::array{to_read, 0_uz} + : std::array{mSizeMask+1 - read_idx, rdend&mSizeMask}; - auto outiter = std::copy_n(mBuffer.begin() + read_ptr*mElemSize, n1*mElemSize, - static_cast(dest)); + auto dstbytes = al::span{static_cast(dest), count*mElemSize}; + auto outiter = std::copy_n(mBuffer.begin() + ptrdiff_t(read_idx*mElemSize), n1*mElemSize, + dstbytes.begin()); if(n2 > 0) std::copy_n(mBuffer.begin(), n2*mElemSize, outiter); return to_read; } -std::size_t RingBuffer::write(const void *src, std::size_t cnt) noexcept +auto RingBuffer::write(const void *src, std::size_t count) noexcept -> std::size_t { - const std::size_t free_cnt{writeSpace()}; - if(free_cnt == 0) return 0; + const std::size_t w{mWriteCount.load(std::memory_order_relaxed)}; + const std::size_t r{mReadCount.load(std::memory_order_acquire)}; + const std::size_t writable{mWriteSize - (w - r)}; + if(writable == 0) return 0; - const std::size_t to_write{std::min(cnt, free_cnt)}; - std::size_t write_ptr{mWritePtr.load(std::memory_order_relaxed) & mSizeMask}; + const std::size_t to_write{std::min(count, writable)}; + const std::size_t write_idx{w & mSizeMask}; - std::size_t n1, n2; - const std::size_t cnt2{write_ptr + to_write}; - if(cnt2 > mSizeMask+1) - { - n1 = mSizeMask+1 - write_ptr; - n2 = cnt2 & mSizeMask; - } - else - { - n1 = to_write; - n2 = 0; - } + const std::size_t wrend{write_idx + to_write}; + const auto [n1, n2] = (wrend <= mSizeMask+1) ? std::array{to_write, 0_uz} + : std::array{mSizeMask+1 - write_idx, wrend&mSizeMask}; - auto srcbytes = static_cast(src); - std::copy_n(srcbytes, n1*mElemSize, mBuffer.begin() + write_ptr*mElemSize); - write_ptr += n1; + auto srcbytes = al::span{static_cast(src), count*mElemSize}; + std::copy_n(srcbytes.cbegin(), n1*mElemSize, mBuffer.begin() + ptrdiff_t(write_idx*mElemSize)); if(n2 > 0) - { - std::copy_n(srcbytes + n1*mElemSize, n2*mElemSize, mBuffer.begin()); - write_ptr += n2; - } - mWritePtr.store(write_ptr, std::memory_order_release); + std::copy_n(srcbytes.cbegin() + ptrdiff_t(n1*mElemSize), n2*mElemSize, mBuffer.begin()); + mWriteCount.store(w+n1+n2, std::memory_order_release); return to_write; } -auto RingBuffer::getReadVector() const noexcept -> DataPair +auto RingBuffer::getReadVector() noexcept -> DataPair { - DataPair ret; - - 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 std::size_t free_cnt{(w-r) & mSizeMask}; + const std::size_t w{mWriteCount.load(std::memory_order_acquire)}; + const std::size_t r{mReadCount.load(std::memory_order_relaxed)}; + const std::size_t readable{w - r}; + const std::size_t read_idx{r & mSizeMask}; - const std::size_t cnt2{r + free_cnt}; - if(cnt2 > mSizeMask+1) + const std::size_t rdend{read_idx + readable}; + if(rdend > 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(mBuffer.data() + r*mElemSize); - ret.first.len = mSizeMask+1 - r; - ret.second.buf = const_cast(mBuffer.data()); - ret.second.len = cnt2 & mSizeMask; - } - else - { - /* Single part vector: just the rest of the buffer */ - ret.first.buf = const_cast(mBuffer.data() + r*mElemSize); - ret.first.len = free_cnt; - ret.second.buf = nullptr; - ret.second.len = 0; + * plus some from the start of the buffer. + */ + return DataPair{{{mBuffer.data() + read_idx*mElemSize, mSizeMask+1 - read_idx}, + {mBuffer.data(), rdend&mSizeMask}}}; } - - return ret; + return DataPair{{{mBuffer.data() + read_idx*mElemSize, readable}, {}}}; } -auto RingBuffer::getWriteVector() const noexcept -> DataPair +auto RingBuffer::getWriteVector() noexcept -> DataPair { - DataPair ret; - - 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 std::size_t free_cnt{(r-w-1) & mSizeMask}; + const std::size_t w{mWriteCount.load(std::memory_order_relaxed)}; + const std::size_t r{mReadCount.load(std::memory_order_acquire)}; + const std::size_t writable{mWriteSize - (w - r)}; + const std::size_t write_idx{w & mSizeMask}; - const std::size_t cnt2{w + free_cnt}; - if(cnt2 > mSizeMask+1) + const std::size_t wrend{write_idx + writable}; + if(wrend > 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(mBuffer.data() + w*mElemSize); - ret.first.len = mSizeMask+1 - w; - ret.second.buf = const_cast(mBuffer.data()); - ret.second.len = cnt2 & mSizeMask; + * plus some from the start of the buffer. + */ + return DataPair{{{mBuffer.data() + write_idx*mElemSize, mSizeMask+1 - write_idx}, + {mBuffer.data(), wrend&mSizeMask}}}; } - else - { - ret.first.buf = const_cast(mBuffer.data() + w*mElemSize); - ret.first.len = free_cnt; - ret.second.buf = nullptr; - ret.second.len = 0; - } - - return ret; + return DataPair{{{mBuffer.data() + write_idx*mElemSize, writable}, {}}}; } diff --git a/3rdparty/openal/common/ringbuffer.h b/3rdparty/openal/common/ringbuffer.h index 8c65c3af9ac8..59b0a0ac7d86 100644 --- a/3rdparty/openal/common/ringbuffer.h +++ b/3rdparty/openal/common/ringbuffer.h @@ -2,26 +2,35 @@ #define RINGBUFFER_H #include +#include #include #include #include #include "almalloc.h" +#include "flexarray.h" /* NOTE: This lockless ringbuffer implementation is copied from JACK, extended * to include an element size. Consequently, parameters and return values for a - * size or count is in 'elements', not bytes. Additionally, it only supports + * size or count are in 'elements', not bytes. Additionally, it only supports * single-consumer/single-provider operation. */ struct RingBuffer { private: - std::atomic mWritePtr{0u}; - std::atomic mReadPtr{0u}; - std::size_t mWriteSize{0u}; - std::size_t mSizeMask{0u}; - std::size_t mElemSize{0u}; +#if defined(__cpp_lib_hardware_interference_size) && !defined(_LIBCPP_VERSION) + static constexpr std::size_t sCacheAlignment{std::hardware_destructive_interference_size}; +#else + /* Assume a 64-byte cache line, the most common/likely value. */ + static constexpr std::size_t sCacheAlignment{64}; +#endif + alignas(sCacheAlignment) std::atomic mWriteCount{0u}; + alignas(sCacheAlignment) std::atomic mReadCount{0u}; + + alignas(sCacheAlignment) const std::size_t mWriteSize; + const std::size_t mSizeMask; + const std::size_t mElemSize; al::FlexArray mBuffer; @@ -30,82 +39,95 @@ struct RingBuffer { std::byte *buf; std::size_t len; }; - using DataPair = std::pair; + using DataPair = std::array; - - RingBuffer(const std::size_t count) : mBuffer{count} { } + RingBuffer(const std::size_t writesize, const std::size_t mask, const std::size_t elemsize, + const std::size_t numbytes) + : mWriteSize{writesize}, mSizeMask{mask}, mElemSize{elemsize}, mBuffer{numbytes} + { } /** Reset the read and write pointers to zero. This is not thread safe. */ - void reset() noexcept; - - /** - * The non-copying data reader. Returns two ringbuffer data pointers that - * hold the current readable data. If the readable data is in one segment - * the second segment has zero length. - */ - DataPair getReadVector() const noexcept; - /** - * The non-copying data writer. Returns two ringbuffer data pointers that - * hold the current writeable data. If the writeable data is in one segment - * the second segment has zero length. - */ - DataPair getWriteVector() const noexcept; + auto reset() noexcept -> void; /** * 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. */ - std::size_t readSpace() const noexcept + [[nodiscard]] auto readSpace() const noexcept -> std::size_t { - const size_t w{mWritePtr.load(std::memory_order_acquire)}; - const size_t r{mReadPtr.load(std::memory_order_acquire)}; - return (w-r) & mSizeMask; + const std::size_t w{mWriteCount.load(std::memory_order_acquire)}; + const std::size_t r{mReadCount.load(std::memory_order_acquire)}; + /* mWriteCount is never more than mWriteSize greater than mReadCount. */ + return w - r; } /** - * The copying data reader. Copy at most `cnt' elements into `dest'. + * The copying data reader. Copy at most `count' elements into `dest'. * Returns the actual number of elements copied. */ - std::size_t read(void *dest, std::size_t cnt) noexcept; + [[nodiscard]] auto read(void *dest, std::size_t count) noexcept -> std::size_t; /** - * The copying data reader w/o read pointer advance. Copy at most `cnt' + * The copying data reader w/o read pointer advance. Copy at most `count' * elements into `dest'. Returns the actual number of elements copied. */ - std::size_t peek(void *dest, std::size_t cnt) const noexcept; - /** Advance the read pointer `cnt' places. */ - void readAdvance(std::size_t cnt) noexcept - { mReadPtr.fetch_add(cnt, std::memory_order_acq_rel); } - + [[nodiscard]] auto peek(void *dest, std::size_t count) const noexcept -> std::size_t; /** - * 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. + * The non-copying data reader. Returns two ringbuffer data pointers that + * hold the current readable data. If the readable data is in one segment + * the second segment has zero length. */ - std::size_t writeSpace() const noexcept + [[nodiscard]] auto getReadVector() noexcept -> DataPair; + /** Advance the read pointer `count' places. */ + auto readAdvance(std::size_t count) noexcept -> void { - const size_t w{mWritePtr.load(std::memory_order_acquire)}; - const size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask}; - return (r-w-1) & mSizeMask; + const std::size_t w{mWriteCount.load(std::memory_order_acquire)}; + const std::size_t r{mReadCount.load(std::memory_order_relaxed)}; + [[maybe_unused]] const std::size_t readable{w - r}; + assert(readable >= count); + mReadCount.store(r+count, std::memory_order_release); } + /** - * The copying data writer. Copy at most `cnt' elements from `src'. Returns + * Return the number of elements available for writing. This is the total + * number of writable elements excluding what's readable (already written). + */ + [[nodiscard]] auto writeSpace() const noexcept -> std::size_t + { return mWriteSize - readSpace(); } + + /** + * The copying data writer. Copy at most `count' elements from `src'. Returns * the actual number of elements copied. */ - std::size_t write(const void *src, std::size_t cnt) noexcept; - /** Advance the write pointer `cnt' places. */ - void writeAdvance(std::size_t cnt) noexcept - { mWritePtr.fetch_add(cnt, std::memory_order_acq_rel); } + [[nodiscard]] auto write(const void *src, std::size_t count) noexcept -> std::size_t; + + /** + * The non-copying data writer. Returns two ringbuffer data pointers that + * hold the current writeable data. If the writeable data is in one segment + * the second segment has zero length. + */ + [[nodiscard]] auto getWriteVector() noexcept -> DataPair; + /** Advance the write pointer `count' places. */ + auto writeAdvance(std::size_t count) noexcept -> void + { + const std::size_t w{mWriteCount.load(std::memory_order_relaxed)}; + const std::size_t r{mReadCount.load(std::memory_order_acquire)}; + [[maybe_unused]] const std::size_t writable{mWriteSize - (w - r)}; + assert(writable >= count); + mWriteCount.store(w+count, std::memory_order_release); + } - std::size_t getElemSize() const noexcept { return mElemSize; } + [[nodiscard]] auto getElemSize() const noexcept -> std::size_t { return mElemSize; } /** * Create a new ringbuffer to hold at least `sz' elements of `elem_sz' - * bytes. The number of elements is rounded up to the next power of two - * (even if it is already a power of two, to ensure the requested amount - * can be written). + * bytes. The number of elements is rounded up to a power of two. If + * `limit_writes' is true, the writable space will be limited to `sz' + * elements regardless of the rounded size. */ - static std::unique_ptr Create(std::size_t sz, std::size_t elem_sz, int limit_writes); + [[nodiscard]] static + auto Create(std::size_t sz, std::size_t elem_sz, bool limit_writes) -> std::unique_ptr; DEF_FAM_NEWDEL(RingBuffer, mBuffer) }; diff --git a/3rdparty/openal/common/strutils.cpp b/3rdparty/openal/common/strutils.cpp index f7868e2e147b..4bcd41194121 100644 --- a/3rdparty/openal/common/strutils.cpp +++ b/3rdparty/openal/common/strutils.cpp @@ -5,21 +5,22 @@ #include - #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include +#include "alstring.h" + std::string wstr_to_utf8(std::wstring_view wstr) { std::string ret; - int len{WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast(wstr.length()), nullptr, - 0, nullptr, nullptr)}; + const int len{WideCharToMultiByte(CP_UTF8, 0, wstr.data(), al::sizei(wstr), nullptr, 0, + nullptr, nullptr)}; if(len > 0) { - ret.resize(len); - WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast(wstr.length()), &ret[0], len, + ret.resize(static_cast(len)); + WideCharToMultiByte(CP_UTF8, 0, wstr.data(), al::sizei(wstr), ret.data(), len, nullptr, nullptr); } @@ -30,12 +31,11 @@ std::wstring utf8_to_wstr(std::string_view str) { std::wstring ret; - int len{MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.length()), nullptr, - 0)}; + const int len{MultiByteToWideChar(CP_UTF8, 0, str.data(), al::sizei(str), nullptr, 0)}; if(len > 0) { - ret.resize(len); - MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.length()), &ret[0], len); + ret.resize(static_cast(len)); + MultiByteToWideChar(CP_UTF8, 0, str.data(), al::sizei(str), ret.data(), len); } return ret; @@ -51,7 +51,7 @@ std::optional getenv(const char *envname) #else const char *str{std::getenv(envname)}; #endif - if(str && str[0] != '\0') + if(str && *str != '\0') return str; return std::nullopt; } @@ -60,7 +60,7 @@ std::optional getenv(const char *envname) std::optional getenv(const WCHAR *envname) { const WCHAR *str{_wgetenv(envname)}; - if(str && str[0] != L'\0') + if(str && *str != L'\0') return str; return std::nullopt; } diff --git a/3rdparty/openal/common/strutils.h b/3rdparty/openal/common/strutils.h index 7eee0c1d1269..3644847c2bca 100644 --- a/3rdparty/openal/common/strutils.h +++ b/3rdparty/openal/common/strutils.h @@ -5,8 +5,8 @@ #include #ifdef _WIN32 +#include #include -#include std::string wstr_to_utf8(std::wstring_view wstr); std::wstring utf8_to_wstr(std::string_view str); diff --git a/3rdparty/openal/common/vecmat.h b/3rdparty/openal/common/vecmat.h index a45f262f6ded..7ac6afe32701 100644 --- a/3rdparty/openal/common/vecmat.h +++ b/3rdparty/openal/common/vecmat.h @@ -1,6 +1,7 @@ #ifndef COMMON_VECMAT_H #define COMMON_VECMAT_H +#include #include #include #include @@ -11,22 +12,24 @@ namespace alu { -template -class VectorR { - static_assert(std::is_floating_point::value, "Must use floating-point types"); - alignas(16) T mVals[4]; +class Vector { + alignas(16) std::array mVals{}; public: - constexpr VectorR() noexcept = default; - constexpr VectorR(const VectorR&) noexcept = default; - constexpr explicit VectorR(T a, T b, T c, T d) noexcept : mVals{a, b, c, d} { } + constexpr Vector() noexcept = default; + constexpr Vector(const Vector&) noexcept = default; + constexpr Vector(Vector&&) noexcept = default; + constexpr explicit Vector(float a, float b, float c, float d) noexcept : mVals{{a,b,c,d}} { } - constexpr VectorR& operator=(const VectorR&) noexcept = default; + constexpr auto operator=(const Vector&) noexcept -> Vector& = default; + constexpr auto operator=(Vector&&) noexcept -> Vector& = default; - constexpr T& operator[](size_t idx) noexcept { return mVals[idx]; } - constexpr const T& operator[](size_t idx) const noexcept { return mVals[idx]; } + [[nodiscard]] constexpr + auto operator[](std::size_t idx) noexcept -> float& { return mVals[idx]; } + [[nodiscard]] constexpr + auto operator[](std::size_t idx) const noexcept -> const float& { return mVals[idx]; } - constexpr VectorR& operator+=(const VectorR &rhs) noexcept + constexpr auto operator+=(const Vector &rhs) noexcept -> Vector& { mVals[0] += rhs.mVals[0]; mVals[1] += rhs.mVals[1]; @@ -35,85 +38,84 @@ class VectorR { return *this; } - constexpr VectorR operator-(const VectorR &rhs) const noexcept + [[nodiscard]] constexpr + auto operator-(const Vector &rhs) const noexcept -> Vector { - return VectorR{mVals[0] - rhs.mVals[0], mVals[1] - rhs.mVals[1], + return Vector{mVals[0] - rhs.mVals[0], mVals[1] - rhs.mVals[1], mVals[2] - rhs.mVals[2], mVals[3] - rhs.mVals[3]}; } - constexpr T normalize(T limit = std::numeric_limits::epsilon()) + constexpr auto normalize() -> float { - limit = std::max(limit, std::numeric_limits::epsilon()); - const T length_sqr{mVals[0]*mVals[0] + mVals[1]*mVals[1] + mVals[2]*mVals[2]}; - if(length_sqr > limit*limit) + const auto length_sqr = float{mVals[0]*mVals[0] + mVals[1]*mVals[1] + mVals[2]*mVals[2]}; + if(length_sqr > std::numeric_limits::epsilon()) { - const T length{std::sqrt(length_sqr)}; - T inv_length{T{1}/length}; + const auto length = std::sqrt(length_sqr); + auto inv_length = float{1.0f / length}; mVals[0] *= inv_length; mVals[1] *= inv_length; mVals[2] *= inv_length; return length; } - mVals[0] = mVals[1] = mVals[2] = T{0}; - return T{0}; + mVals[0] = mVals[1] = mVals[2] = 0.0f; + return 0.0f; } - constexpr VectorR cross_product(const alu::VectorR &rhs) const noexcept + [[nodiscard]] constexpr auto cross_product(const Vector &rhs) const noexcept -> Vector { - return VectorR{ + return Vector{ mVals[1]*rhs.mVals[2] - mVals[2]*rhs.mVals[1], mVals[2]*rhs.mVals[0] - mVals[0]*rhs.mVals[2], mVals[0]*rhs.mVals[1] - mVals[1]*rhs.mVals[0], - T{0}}; + 0.0f}; } - constexpr T dot_product(const alu::VectorR &rhs) const noexcept + [[nodiscard]] constexpr auto dot_product(const Vector &rhs) const noexcept -> float { return mVals[0]*rhs.mVals[0] + mVals[1]*rhs.mVals[1] + mVals[2]*rhs.mVals[2]; } }; -using Vector = VectorR; -template -class MatrixR { - static_assert(std::is_floating_point::value, "Must use floating-point types"); - alignas(16) T mVals[16]; +class Matrix { + alignas(16) std::array mVals{}; public: - constexpr MatrixR() noexcept = default; - constexpr MatrixR(const MatrixR&) noexcept = default; - constexpr explicit MatrixR( - T aa, T ab, T ac, T ad, - T ba, T bb, T bc, T bd, - T ca, T cb, T cc, T cd, - T da, T db, T dc, T dd) noexcept - : mVals{aa,ab,ac,ad, ba,bb,bc,bd, ca,cb,cc,cd, da,db,dc,dd} + constexpr Matrix() noexcept = default; + constexpr Matrix(const Matrix&) noexcept = default; + constexpr Matrix(Matrix&&) noexcept = default; + constexpr explicit Matrix( + float aa, float ab, float ac, float ad, + float ba, float bb, float bc, float bd, + float ca, float cb, float cc, float cd, + float da, float db, float dc, float dd) noexcept + : mVals{{aa,ab,ac,ad, ba,bb,bc,bd, ca,cb,cc,cd, da,db,dc,dd}} { } - constexpr MatrixR& operator=(const MatrixR&) noexcept = default; + constexpr auto operator=(const Matrix&) noexcept -> Matrix& = default; + constexpr auto operator=(Matrix&&) noexcept -> Matrix& = default; - constexpr auto operator[](size_t idx) noexcept { return al::span{&mVals[idx*4], 4}; } - constexpr auto operator[](size_t idx) const noexcept - { return al::span{&mVals[idx*4], 4}; } + [[nodiscard]] constexpr auto operator[](std::size_t idx) noexcept + { return al::span{&mVals[idx*4], 4}; } + [[nodiscard]] constexpr auto operator[](std::size_t idx) const noexcept + { return al::span{&mVals[idx*4], 4}; } - static constexpr MatrixR Identity() noexcept + [[nodiscard]] static constexpr auto Identity() noexcept -> Matrix { - return MatrixR{ - T{1}, T{0}, T{0}, T{0}, - T{0}, T{1}, T{0}, T{0}, - T{0}, T{0}, T{1}, T{0}, - T{0}, T{0}, T{0}, T{1}}; + return Matrix{ + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + } + + [[nodiscard]] friend constexpr + auto operator*(const Matrix &mtx, const Vector &vec) noexcept -> Vector + { + return Vector{ + vec[0]*mtx[0][0] + vec[1]*mtx[1][0] + vec[2]*mtx[2][0] + vec[3]*mtx[3][0], + vec[0]*mtx[0][1] + vec[1]*mtx[1][1] + vec[2]*mtx[2][1] + vec[3]*mtx[3][1], + vec[0]*mtx[0][2] + vec[1]*mtx[1][2] + vec[2]*mtx[2][2] + vec[3]*mtx[3][2], + vec[0]*mtx[0][3] + vec[1]*mtx[1][3] + vec[2]*mtx[2][3] + vec[3]*mtx[3][3]}; } }; -using Matrix = MatrixR; - -template -constexpr VectorR operator*(const MatrixR &mtx, const VectorR &vec) noexcept -{ - return VectorR{ - vec[0]*mtx[0][0] + vec[1]*mtx[1][0] + vec[2]*mtx[2][0] + vec[3]*mtx[3][0], - vec[0]*mtx[0][1] + vec[1]*mtx[1][1] + vec[2]*mtx[2][1] + vec[3]*mtx[3][1], - vec[0]*mtx[0][2] + vec[1]*mtx[1][2] + vec[2]*mtx[2][2] + vec[3]*mtx[3][2], - vec[0]*mtx[0][3] + vec[1]*mtx[1][3] + vec[2]*mtx[2][3] + vec[3]*mtx[3][3]}; -} } // namespace alu diff --git a/3rdparty/openal/common/vector.h b/3rdparty/openal/common/vector.h index 1b69d6a77fea..364735c67da4 100644 --- a/3rdparty/openal/common/vector.h +++ b/3rdparty/openal/common/vector.h @@ -1,13 +1,14 @@ #ifndef AL_VECTOR_H #define AL_VECTOR_H +#include #include #include "almalloc.h" namespace al { -template +template using vector = std::vector>; } // namespace al diff --git a/3rdparty/openal/config.h.in b/3rdparty/openal/config.h.in index 20df5b467079..fb61fcf284ca 100644 --- a/3rdparty/openal/config.h.in +++ b/3rdparty/openal/config.h.in @@ -1,93 +1,18 @@ /* Define the alignment attribute for externally callable functions. */ #define FORCE_ALIGN @ALSOFT_FORCE_ALIGN@ -/* Define if deprecated EAX extensions are enabled */ -#cmakedefine ALSOFT_EAX - /* Define if HRTF data is embedded in the library */ #cmakedefine ALSOFT_EMBED_HRTF_DATA -/* Define if we have the posix_memalign function */ -#cmakedefine HAVE_POSIX_MEMALIGN - -/* Define if we have the _aligned_malloc function */ -#cmakedefine HAVE__ALIGNED_MALLOC - /* Define if we have the proc_pidpath function */ #cmakedefine HAVE_PROC_PIDPATH -/* Define if we have the getopt function */ -#cmakedefine HAVE_GETOPT - -/* Define if we have DBus/RTKit */ -#cmakedefine HAVE_RTKIT - -/* Define if we have SSE CPU extensions */ -#cmakedefine HAVE_SSE -#cmakedefine HAVE_SSE2 -#cmakedefine HAVE_SSE3 -#cmakedefine HAVE_SSE4_1 - -/* Define if we have ARM Neon CPU extensions */ -#cmakedefine HAVE_NEON - -/* Define if we have the ALSA backend */ -#cmakedefine HAVE_ALSA - -/* Define if we have the OSS backend */ -#cmakedefine HAVE_OSS - -/* Define if we have the PipeWire backend */ -#cmakedefine HAVE_PIPEWIRE - -/* Define if we have the Solaris backend */ -#cmakedefine HAVE_SOLARIS - -/* Define if we have the SndIO backend */ -#cmakedefine HAVE_SNDIO - -/* Define if we have the WASAPI backend */ -#cmakedefine HAVE_WASAPI - -/* Define if we have the DSound backend */ -#cmakedefine HAVE_DSOUND - -/* Define if we have the Windows Multimedia backend */ -#cmakedefine HAVE_WINMM - -/* Define if we have the PortAudio backend */ -#cmakedefine HAVE_PORTAUDIO - -/* Define if we have the PulseAudio backend */ -#cmakedefine HAVE_PULSEAUDIO - -/* Define if we have the JACK backend */ -#cmakedefine HAVE_JACK - -/* Define if we have the CoreAudio backend */ -#cmakedefine HAVE_COREAUDIO - -/* Define if we have the OpenSL backend */ -#cmakedefine HAVE_OPENSL - -/* Define if we have the Oboe backend */ -#cmakedefine HAVE_OBOE - -/* Define if we have the Wave Writer backend */ -#cmakedefine HAVE_WAVE - -/* Define if we have the SDL2 backend */ -#cmakedefine HAVE_SDL2 - /* Define if we have dlfcn.h */ #cmakedefine HAVE_DLFCN_H /* Define if we have pthread_np.h */ #cmakedefine HAVE_PTHREAD_NP_H -/* Define if we have malloc.h */ -#cmakedefine HAVE_MALLOC_H - /* Define if we have cpuid.h */ #cmakedefine HAVE_CPUID_H @@ -97,18 +22,12 @@ /* Define if we have guiddef.h */ #cmakedefine HAVE_GUIDDEF_H -/* Define if we have initguid.h */ -#cmakedefine HAVE_INITGUID_H - /* Define if we have GCC's __get_cpuid() */ #cmakedefine HAVE_GCC_GET_CPUID /* Define if we have the __cpuid() intrinsic */ #cmakedefine HAVE_CPUID_INTRINSIC -/* Define if we have SSE intrinsics */ -#cmakedefine HAVE_SSE_INTRINSICS - /* Define if we have pthread_setschedparam() */ #cmakedefine HAVE_PTHREAD_SETSCHEDPARAM @@ -121,5 +40,11 @@ /* Define the installation data directory */ #cmakedefine ALSOFT_INSTALL_DATADIR "@ALSOFT_INSTALL_DATADIR@" -/* Define whether build alsoft for winuwp */ -#cmakedefine ALSOFT_UWP +/* Define to 1 if we have DBus/RTKit, else 0 */ +#cmakedefine01 HAVE_RTKIT + +/* Define to 1 if building for winuwp, else 0 */ +#cmakedefine01 ALSOFT_UWP + +/* Define to 1 if building with legacy EAX API support, else 0 */ +#cmakedefine01 ALSOFT_EAX diff --git a/3rdparty/openal/config_backends.h.in b/3rdparty/openal/config_backends.h.in new file mode 100644 index 000000000000..7a2645c49b33 --- /dev/null +++ b/3rdparty/openal/config_backends.h.in @@ -0,0 +1,35 @@ +/* Define to 1 if the given backend is enabled, else 0 */ + +#cmakedefine01 HAVE_ALSA + +#cmakedefine01 HAVE_OSS + +#cmakedefine01 HAVE_PIPEWIRE + +#cmakedefine01 HAVE_SOLARIS + +#cmakedefine01 HAVE_SNDIO + +#cmakedefine01 HAVE_WASAPI + +#cmakedefine01 HAVE_DSOUND + +#cmakedefine01 HAVE_WINMM + +#cmakedefine01 HAVE_PORTAUDIO + +#cmakedefine01 HAVE_PULSEAUDIO + +#cmakedefine01 HAVE_JACK + +#cmakedefine01 HAVE_COREAUDIO + +#cmakedefine01 HAVE_OPENSL + +#cmakedefine01 HAVE_OBOE + +#cmakedefine01 HAVE_OTHERIO + +#cmakedefine01 HAVE_WAVE + +#cmakedefine01 HAVE_SDL2 diff --git a/3rdparty/openal/config_simd.h.in b/3rdparty/openal/config_simd.h.in new file mode 100644 index 000000000000..3d284cc7bf1b --- /dev/null +++ b/3rdparty/openal/config_simd.h.in @@ -0,0 +1,10 @@ +/* Define to 1 if we have SSE CPU extensions, else 0 */ +#cmakedefine01 HAVE_SSE +#cmakedefine01 HAVE_SSE2 +#cmakedefine01 HAVE_SSE3 +#cmakedefine01 HAVE_SSE4_1 + +#cmakedefine01 HAVE_SSE_INTRINSICS + +/* Define to 1 if we have ARM Neon CPU extensions, else 0 */ +#cmakedefine01 HAVE_NEON diff --git a/3rdparty/openal/core/ambdec.cpp b/3rdparty/openal/core/ambdec.cpp index f98e1098f012..1fce504f33ae 100644 --- a/3rdparty/openal/core/ambdec.cpp +++ b/3rdparty/openal/core/ambdec.cpp @@ -8,12 +8,13 @@ #include #include #include +#include +#include #include #include #include #include "albit.h" -#include "alfstream.h" #include "alspan.h" #include "opthelpers.h" @@ -42,8 +43,8 @@ enum class ReaderScope { HFMatrix, }; -#ifdef __USE_MINGW_ANSI_STDIO -[[gnu::format(gnu_printf,2,3)]] +#ifdef __MINGW32__ +[[gnu::format(__MINGW_PRINTF_FORMAT,2,3)]] #else [[gnu::format(printf,2,3)]] #endif @@ -53,10 +54,10 @@ std::optional make_error(size_t linenum, const char *fmt, ...) auto &str = ret.emplace(); str.resize(256); - int printed{std::snprintf(const_cast(str.data()), str.length(), "Line %zu: ", linenum)}; - if(printed < 0) printed = 0; - auto plen = std::min(static_cast(printed), str.length()); + const auto printed = std::snprintf(str.data(), str.length(), "Line %zu: ", linenum); + const auto plen = std::min(static_cast(std::max(printed, 0)), str.length()); + /* NOLINTBEGIN(*-array-to-pointer-decay) */ std::va_list args, args2; va_start(args, fmt); va_copy(args2, args); @@ -68,6 +69,7 @@ std::optional make_error(size_t linenum, const char *fmt, ...) } va_end(args2); va_end(args); + /* NOLINTEND(*-array-to-pointer-decay) */ return ret; } @@ -79,7 +81,7 @@ AmbDecConf::~AmbDecConf() = default; std::optional AmbDecConf::load(const char *fname) noexcept { - al::ifstream f{fname}; + std::ifstream f{std::filesystem::u8path(fname)}; if(!f.is_open()) return std::string("Failed to open file \"")+fname+"\""; @@ -111,7 +113,7 @@ std::optional AmbDecConf::load(const char *fname) noexcept { if(command == "add_spkr") { - if(speaker_pos == NumSpeakers) + if(speaker_pos == Speakers.size()) return make_error(linenum, "Too many speakers specified"); AmbDecConf::SpeakerConf &spkr = Speakers[speaker_pos++]; @@ -127,7 +129,7 @@ std::optional AmbDecConf::load(const char *fname) noexcept else if(scope == ReaderScope::LFMatrix || scope == ReaderScope::HFMatrix) { auto &gains = (scope == ReaderScope::LFMatrix) ? LFOrderGain : HFOrderGain; - auto *matrix = (scope == ReaderScope::LFMatrix) ? LFMatrix : HFMatrix; + auto matrix = (scope == ReaderScope::LFMatrix) ? LFMatrix : HFMatrix; auto &pos = (scope == ReaderScope::LFMatrix) ? lfmatrix_pos : hfmatrix_pos; if(command == "order_gain") @@ -145,7 +147,7 @@ std::optional AmbDecConf::load(const char *fname) noexcept } else if(command == "add_row") { - if(pos == NumSpeakers) + if(pos == Speakers.size()) return make_error(linenum, "Too many matrix rows specified"); unsigned int mask{ChanMask}; @@ -205,12 +207,13 @@ std::optional AmbDecConf::load(const char *fname) noexcept } else if(command == "/dec/speakers") { - if(NumSpeakers) + if(!Speakers.empty()) return make_error(linenum, "Duplicate speakers"); - istr >> NumSpeakers; - if(!NumSpeakers) - return make_error(linenum, "Invalid speakers: %zu", NumSpeakers); - Speakers = std::make_unique(NumSpeakers); + size_t numspeakers{}; + istr >> numspeakers; + if(!numspeakers) + return make_error(linenum, "Invalid speakers: %zu", numspeakers); + Speakers.resize(numspeakers); } else if(command == "/dec/coeff_scale") { @@ -243,22 +246,22 @@ std::optional AmbDecConf::load(const char *fname) noexcept } else if(command == "/speakers/{") { - if(!NumSpeakers) + if(Speakers.empty()) return make_error(linenum, "Speakers defined without a count"); scope = ReaderScope::Speakers; } else if(command == "/lfmatrix/{" || command == "/hfmatrix/{" || command == "/matrix/{") { - if(!NumSpeakers) + if(Speakers.empty()) return make_error(linenum, "Matrix defined without a speaker count"); if(!ChanMask) return make_error(linenum, "Matrix defined without a channel mask"); - if(!Matrix) + if(Matrix.empty()) { - Matrix = std::make_unique(NumSpeakers * FreqBands); - LFMatrix = Matrix.get(); - HFMatrix = LFMatrix + NumSpeakers*(FreqBands-1); + Matrix.resize(Speakers.size() * FreqBands); + LFMatrix = al::span{Matrix}.first(Speakers.size()); + HFMatrix = al::span{Matrix}.subspan(Speakers.size()*(FreqBands-1)); } if(FreqBands == 1) @@ -285,8 +288,8 @@ std::optional AmbDecConf::load(const char *fname) noexcept if(!is_at_end(buffer, endpos)) return make_error(linenum, "Extra junk on end: %s", buffer.substr(endpos).c_str()); - if(speaker_pos < NumSpeakers || hfmatrix_pos < NumSpeakers - || (FreqBands == 2 && lfmatrix_pos < NumSpeakers)) + if(speaker_pos < Speakers.size() || hfmatrix_pos < Speakers.size() + || (FreqBands == 2 && lfmatrix_pos < Speakers.size())) return make_error(linenum, "Incomplete decoder definition"); if(CoeffScale == AmbDecScale::Unset) return make_error(linenum, "No coefficient scaling defined"); diff --git a/3rdparty/openal/core/ambdec.h b/3rdparty/openal/core/ambdec.h index 19f68697114c..587a30c666ce 100644 --- a/3rdparty/openal/core/ambdec.h +++ b/3rdparty/openal/core/ambdec.h @@ -5,7 +5,9 @@ #include #include #include +#include +#include "alspan.h" #include "core/ambidefs.h" /* Helpers to read .ambdec configuration files. */ @@ -34,18 +36,17 @@ struct AmbDecConf { float Elevation{0.0f}; std::string Connection; }; - size_t NumSpeakers{0}; - std::unique_ptr Speakers; + std::vector Speakers; using CoeffArray = std::array; - std::unique_ptr Matrix; + std::vector Matrix; /* Unused when FreqBands == 1 */ - float LFOrderGain[MaxAmbiOrder+1]{}; - CoeffArray *LFMatrix; + std::array LFOrderGain{}; + al::span LFMatrix; - float HFOrderGain[MaxAmbiOrder+1]{}; - CoeffArray *HFMatrix; + std::array HFOrderGain{}; + al::span HFMatrix; ~AmbDecConf(); diff --git a/3rdparty/openal/core/ambidefs.cpp b/3rdparty/openal/core/ambidefs.cpp index c1bb5c81e8b6..4a72e34f3808 100644 --- a/3rdparty/openal/core/ambidefs.cpp +++ b/3rdparty/openal/core/ambidefs.cpp @@ -10,7 +10,6 @@ namespace { using AmbiChannelFloatArray = std::array; -constexpr auto inv_sqrt2f = static_cast(1.0/al::numbers::sqrt2); constexpr auto inv_sqrt3f = static_cast(1.0/al::numbers::sqrt3); @@ -21,25 +20,25 @@ constexpr auto inv_sqrt3f = static_cast(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,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,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 +48,17 @@ constexpr std::array,MaxAmbiOrder+1> HFScales2D * signal. While not perfect, this should accurately encode a lower-order * signal into a higher-order signal. */ -constexpr std::array,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 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 +67,29 @@ 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), -}}; +}; 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,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 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), -}}; +constexpr std::array FirstOrder2DDecoder{ + std::array{1.666666667e-01f, -9.622504486e-02f, 0.0f, 1.666666667e-01f}, + std::array{1.666666667e-01f, -1.924500897e-01f, 0.0f, 0.000000000e+00f}, + std::array{1.666666667e-01f, -9.622504486e-02f, 0.0f, -1.666666667e-01f}, + std::array{1.666666667e-01f, 9.622504486e-02f, 0.0f, -1.666666667e-01f}, + std::array{1.666666667e-01f, 1.924500897e-01f, 0.0f, 0.000000000e+00f}, + std::array{1.666666667e-01f, 9.622504486e-02f, 0.0f, 1.666666667e-01f}, +}; +constexpr std::array FirstOrder2DEncoder{ + 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(FirstOrder2DDecoder.size() == FirstOrder2DEncoder.size(), "First-order 2D mismatch"); @@ -94,21 +97,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,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 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 +124,29 @@ 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), -}}; +}; 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,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 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 +155,29 @@ static_assert(SecondOrder2DDecoder.size() == SecondOrder2DEncoder.size(), * matrix, just using a more dense speaker array suitable for third-order * content. */ -constexpr std::array,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 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 +198,24 @@ constexpr std::array 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,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 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 +224,7 @@ constexpr std::array 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 +233,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,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 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,7 +256,7 @@ constexpr std::array 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"); @@ -279,13 +282,13 @@ constexpr auto CalcAmbiUpsampler(const std::array,M> &decode } // namespace -const std::array AmbiScale::FirstOrderUp{CalcAmbiUpsampler(FirstOrderDecoder, FirstOrderEncoder)}; -const std::array AmbiScale::FirstOrder2DUp{CalcAmbiUpsampler(FirstOrder2DDecoder, FirstOrder2DEncoder)}; -const std::array AmbiScale::SecondOrderUp{CalcAmbiUpsampler(SecondOrderDecoder, SecondOrderEncoder)}; -const std::array AmbiScale::SecondOrder2DUp{CalcAmbiUpsampler(SecondOrder2DDecoder, SecondOrder2DEncoder)}; -const std::array AmbiScale::ThirdOrderUp{CalcAmbiUpsampler(ThirdOrderDecoder, ThirdOrderEncoder)}; -const std::array AmbiScale::ThirdOrder2DUp{CalcAmbiUpsampler(ThirdOrder2DDecoder, ThirdOrder2DEncoder)}; -const std::array AmbiScale::FourthOrder2DUp{CalcAmbiUpsampler(FourthOrder2DDecoder, FourthOrder2DEncoder)}; +const std::array,4> AmbiScale::FirstOrderUp{CalcAmbiUpsampler(FirstOrderDecoder, FirstOrderEncoder)}; +const std::array,4> AmbiScale::FirstOrder2DUp{CalcAmbiUpsampler(FirstOrder2DDecoder, FirstOrder2DEncoder)}; +const std::array,9> AmbiScale::SecondOrderUp{CalcAmbiUpsampler(SecondOrderDecoder, SecondOrderEncoder)}; +const std::array,9> AmbiScale::SecondOrder2DUp{CalcAmbiUpsampler(SecondOrder2DDecoder, SecondOrder2DEncoder)}; +const std::array,16> AmbiScale::ThirdOrderUp{CalcAmbiUpsampler(ThirdOrderDecoder, ThirdOrderEncoder)}; +const std::array,16> AmbiScale::ThirdOrder2DUp{CalcAmbiUpsampler(ThirdOrder2DDecoder, ThirdOrder2DEncoder)}; +const std::array,25> AmbiScale::FourthOrder2DUp{CalcAmbiUpsampler(FourthOrder2DDecoder, FourthOrder2DEncoder)}; std::array AmbiScale::GetHFOrderScales(const uint src_order, diff --git a/3rdparty/openal/core/ambidefs.h b/3rdparty/openal/core/ambidefs.h index bea1a312079b..2e7774a957ba 100644 --- a/3rdparty/openal/core/ambidefs.h +++ b/3rdparty/openal/core/ambidefs.h @@ -2,8 +2,8 @@ #define CORE_AMBIDEFS_H #include -#include -#include +#include +#include #include "alnumbers.h" @@ -14,10 +14,10 @@ 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. */ -inline constexpr uint8_t MaxAmbiOrder{3}; -constexpr inline size_t AmbiChannelsFromOrder(size_t order) noexcept +constexpr auto AmbiChannelsFromOrder(std::size_t order) noexcept -> std::size_t { return (order+1) * (order+1); } -inline constexpr size_t MaxAmbiChannels{AmbiChannelsFromOrder(MaxAmbiOrder)}; +inline constexpr auto MaxAmbiOrder = std::uint8_t{3}; +inline constexpr auto MaxAmbiChannels = size_t{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 @@ -39,20 +39,20 @@ inline constexpr uint AmbiPeriphonicMask{0xfe7ce4}; * representation. This is 2 per each order above zero-order, plus 1 for zero- * order. Or simply, o*2 + 1. */ -constexpr inline size_t Ambi2DChannelsFromOrder(size_t order) noexcept +constexpr auto Ambi2DChannelsFromOrder(std::size_t order) noexcept -> std::size_t { return order*2 + 1; } -inline constexpr size_t MaxAmbi2DChannels{Ambi2DChannelsFromOrder(MaxAmbiOrder)}; +inline constexpr auto 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 inline constexpr std::array FromN3D{{ + static constexpr auto FromN3D = std::array{ 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 FromSN3D{{ + }; + static constexpr auto FromSN3D = std::array{ 1.000000000f, /* ACN 0, sqrt(1) */ 1.732050808f, /* ACN 1, sqrt(3) */ 1.732050808f, /* ACN 2, sqrt(3) */ @@ -69,8 +69,8 @@ struct AmbiScale { 2.645751311f, /* ACN 13, sqrt(7) */ 2.645751311f, /* ACN 14, sqrt(7) */ 2.645751311f, /* ACN 15, sqrt(7) */ - }}; - static inline constexpr std::array FromFuMa{{ + }; + static constexpr auto FromFuMa = std::array{ 1.414213562f, /* ACN 0 (W), sqrt(2) */ 1.732050808f, /* ACN 1 (Y), sqrt(3) */ 1.732050808f, /* ACN 2 (Z), sqrt(3) */ @@ -87,15 +87,15 @@ struct AmbiScale { 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 FromUHJ{{ + }; + static constexpr auto FromUHJ = std::array{ 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 GetHFOrderScales(const uint src_order, @@ -111,7 +111,7 @@ struct AmbiScale { }; struct AmbiIndex { - static inline constexpr std::array FromFuMa{{ + static constexpr auto FromFuMa = std::array{ 0, /* W */ 3, /* X */ 1, /* Y */ @@ -128,8 +128,8 @@ struct AmbiIndex { 10, /* O */ 15, /* P */ 9, /* Q */ - }}; - static inline constexpr std::array FromFuMa2D{{ + }; + static constexpr auto FromFuMa2D = std::array{ 0, /* W */ 3, /* X */ 1, /* Y */ @@ -137,23 +137,23 @@ struct AmbiIndex { 4, /* V */ 15, /* P */ 9, /* Q */ - }}; + }; - static inline constexpr std::array FromACN{{ + static constexpr auto FromACN = std::array{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 - }}; - static inline constexpr std::array FromACN2D{{ + }; + static constexpr auto FromACN2D = std::array{ 0, 1,3, 4,8, 9,15 - }}; + }; - static inline constexpr std::array OrderFromChannel{{ + static constexpr auto OrderFromChannel = std::array{ 0, 1,1,1, 2,2,2,2,2, 3,3,3,3,3,3,3, - }}; - static inline constexpr std::array OrderFrom2DChannel{{ + }; + static constexpr auto OrderFrom2DChannel = std::array{ 0, 1,1, 2,2, 3,3, - }}; + }; }; diff --git a/3rdparty/openal/core/async_event.h b/3rdparty/openal/core/async_event.h index f1ca0c7b6251..f865fd8a809b 100644 --- a/3rdparty/openal/core/async_event.h +++ b/3rdparty/openal/core/async_event.h @@ -1,7 +1,9 @@ #ifndef CORE_EVENT_H #define CORE_EVENT_H -#include +#include +#include +#include #include #include "almalloc.h" @@ -11,7 +13,7 @@ struct EffectState; using uint = unsigned int; -enum class AsyncEnableBits : uint8_t { +enum class AsyncEnableBits : std::uint8_t { SourceState, BufferCompleted, Disconnected, @@ -19,7 +21,7 @@ enum class AsyncEnableBits : uint8_t { }; -enum class AsyncSrcState : uint8_t { +enum class AsyncSrcState : std::uint8_t { Reset, Stop, Play, @@ -39,7 +41,7 @@ struct AsyncBufferCompleteEvent { }; struct AsyncDisconnectEvent { - char msg[244]; + std::string msg; }; struct AsyncEffectReleaseEvent { diff --git a/3rdparty/openal/core/bformatdec.cpp b/3rdparty/openal/core/bformatdec.cpp index a308e1856967..1e46e9508ced 100644 --- a/3rdparty/openal/core/bformatdec.cpp +++ b/3rdparty/openal/core/bformatdec.cpp @@ -6,11 +6,13 @@ #include #include #include +#include #include -#include "almalloc.h" #include "alnumbers.h" +#include "bufferline.h" #include "filters/splitter.h" +#include "flexarray.h" #include "front_stablizer.h" #include "mixer.h" #include "opthelpers.h" @@ -36,9 +38,8 @@ BFormatDec::BFormatDec(const size_t inchans, const al::span co auto &decoder = mChannelDec.emplace>(inchans); for(size_t j{0};j < decoder.size();++j) { - float *outcoeffs{decoder[j].mGains}; - for(const ChannelDec &incoeffs : coeffs) - *(outcoeffs++) = incoeffs[j]; + std::transform(coeffs.cbegin(), coeffs.cend(), decoder[j].mGains.begin(), + [j](const ChannelDec &incoeffs) { return incoeffs[j]; }); } } else @@ -50,45 +51,40 @@ BFormatDec::BFormatDec(const size_t inchans, const al::span co for(size_t j{0};j < decoder.size();++j) { - float *outcoeffs{decoder[j].mGains[sHFBand]}; - for(const ChannelDec &incoeffs : coeffs) - *(outcoeffs++) = incoeffs[j]; + std::transform(coeffs.cbegin(), coeffs.cend(), decoder[j].mGains[sHFBand].begin(), + [j](const ChannelDec &incoeffs) { return incoeffs[j]; }); - outcoeffs = decoder[j].mGains[sLFBand]; - for(const ChannelDec &incoeffs : coeffslf) - *(outcoeffs++) = incoeffs[j]; + std::transform(coeffslf.cbegin(), coeffslf.cend(), decoder[j].mGains[sLFBand].begin(), + [j](const ChannelDec &incoeffs) { return incoeffs[j]; }); } } } void BFormatDec::process(const al::span OutBuffer, - const FloatBufferLine *InSamples, const size_t SamplesToDo) + const al::span InSamples, const size_t SamplesToDo) { ASSUME(SamplesToDo > 0); auto decode_dualband = [=](std::vector &decoder) { - auto *input = InSamples; - const al::span hfSamples{mSamples[sHFBand].data(), SamplesToDo}; - const al::span lfSamples{mSamples[sLFBand].data(), SamplesToDo}; + auto input = InSamples.cbegin(); + const auto hfSamples = al::span{mSamples[sHFBand]}.first(SamplesToDo); + const auto lfSamples = al::span{mSamples[sLFBand]}.first(SamplesToDo); for(auto &chandec : decoder) { - chandec.mXOver.process({input->data(), SamplesToDo}, hfSamples.data(), - lfSamples.data()); + chandec.mXOver.process(al::span{*input++}.first(SamplesToDo), hfSamples, lfSamples); MixSamples(hfSamples, OutBuffer, chandec.mGains[sHFBand], chandec.mGains[sHFBand],0,0); MixSamples(lfSamples, OutBuffer, chandec.mGains[sLFBand], chandec.mGains[sLFBand],0,0); - ++input; } }; auto decode_singleband = [=](std::vector &decoder) { - auto *input = InSamples; + auto input = InSamples.cbegin(); for(auto &chandec : decoder) { - MixSamples({input->data(), SamplesToDo}, OutBuffer, chandec.mGains, chandec.mGains, - 0, 0); - ++input; + MixSamples(al::span{*input++}.first(SamplesToDo), OutBuffer, chandec.mGains, + chandec.mGains, 0, 0); } }; @@ -96,38 +92,36 @@ void BFormatDec::process(const al::span OutBuffer, } void BFormatDec::processStablize(const al::span OutBuffer, - const FloatBufferLine *InSamples, const size_t lidx, const size_t ridx, const size_t cidx, - const size_t SamplesToDo) + const al::span InSamples, const size_t lidx, const size_t ridx, + const size_t cidx, const size_t SamplesToDo) { ASSUME(SamplesToDo > 0); /* Move the existing direct L/R signal out so it doesn't get processed by * the stablizer. */ - float *RESTRICT mid{al::assume_aligned<16>(mStablizer->MidDirect.data())}; - float *RESTRICT side{al::assume_aligned<16>(mStablizer->Side.data())}; - for(size_t i{0};i < SamplesToDo;++i) - { - mid[i] = OutBuffer[lidx][i] + OutBuffer[ridx][i]; - side[i] = OutBuffer[lidx][i] - OutBuffer[ridx][i]; - } - std::fill_n(OutBuffer[lidx].begin(), SamplesToDo, 0.0f); - std::fill_n(OutBuffer[ridx].begin(), SamplesToDo, 0.0f); + const auto leftout = al::span{OutBuffer[lidx]}.first(SamplesToDo); + const auto rightout = al::span{OutBuffer[ridx]}.first(SamplesToDo); + const auto mid = al::span{mStablizer->MidDirect}.first(SamplesToDo); + const auto side = al::span{mStablizer->Side}.first(SamplesToDo); + std::transform(leftout.cbegin(), leftout.cend(), rightout.cbegin(), mid.begin(),std::plus{}); + std::transform(leftout.cbegin(), leftout.cend(), rightout.cbegin(), side.begin(),std::minus{}); + std::fill_n(leftout.begin(), leftout.size(), 0.0f); + std::fill_n(rightout.begin(), rightout.size(), 0.0f); /* Decode the B-Format input to OutBuffer. */ process(OutBuffer, InSamples, SamplesToDo); /* Include the decoded side signal with the direct side signal. */ for(size_t i{0};i < SamplesToDo;++i) - side[i] += OutBuffer[lidx][i] - OutBuffer[ridx][i]; + side[i] += leftout[i] - rightout[i]; /* Get the decoded mid signal and band-split it. */ - std::transform(OutBuffer[lidx].cbegin(), OutBuffer[lidx].cbegin()+SamplesToDo, - OutBuffer[ridx].cbegin(), mStablizer->Temp.begin(), - [](const float l, const float r) noexcept { return l + r; }); + const auto tmpsamples = al::span{mStablizer->Temp}.first(SamplesToDo); + std::transform(leftout.cbegin(), leftout.cend(), rightout.cbegin(), tmpsamples.begin(), + std::plus{}); - mStablizer->MidFilter.process({mStablizer->Temp.data(), SamplesToDo}, mStablizer->MidHF.data(), - mStablizer->MidLF.data()); + mStablizer->MidFilter.process(tmpsamples, mStablizer->MidHF, mStablizer->MidLF); /* Apply an all-pass to all channels to match the band-splitter's phase * shift. This is to keep the phase synchronized between the existing @@ -140,9 +134,9 @@ void BFormatDec::processStablize(const al::span OutBuffer, * and substitute the direct mid signal and direct+decoded side signal. */ if(i == lidx) - mStablizer->ChannelFilters[i].processAllPass({mid, SamplesToDo}); + mStablizer->ChannelFilters[i].processAllPass(mid); else if(i == ridx) - mStablizer->ChannelFilters[i].processAllPass({side, SamplesToDo}); + mStablizer->ChannelFilters[i].processAllPass(side); else mStablizer->ChannelFilters[i].processAllPass({OutBuffer[i].data(), SamplesToDo}); } @@ -156,6 +150,7 @@ void BFormatDec::processStablize(const al::span OutBuffer, const float cos_hf{std::cos(1.0f/4.0f * (al::numbers::pi_v*0.5f))}; const float sin_lf{std::sin(1.0f/3.0f * (al::numbers::pi_v*0.5f))}; const float sin_hf{std::sin(1.0f/4.0f * (al::numbers::pi_v*0.5f))}; + const auto centerout = al::span{OutBuffer[cidx]}.first(SamplesToDo); for(size_t i{0};i < SamplesToDo;i++) { /* Add the direct mid signal to the processed mid signal so it can be @@ -168,9 +163,9 @@ void BFormatDec::processStablize(const al::span OutBuffer, /* The generated center channel signal adds to the existing signal, * while the modified left and right channels replace. */ - OutBuffer[lidx][i] = (m + s) * 0.5f; - OutBuffer[ridx][i] = (m - s) * 0.5f; - OutBuffer[cidx][i] += c * 0.5f; + leftout[i] = (m + s) * 0.5f; + rightout[i] = (m - s) * 0.5f; + centerout[i] += c * 0.5f; } } diff --git a/3rdparty/openal/core/bformatdec.h b/3rdparty/openal/core/bformatdec.h index 3bb7f544899b..b86f771da15c 100644 --- a/3rdparty/openal/core/bformatdec.h +++ b/3rdparty/openal/core/bformatdec.h @@ -7,33 +7,32 @@ #include #include -#include "almalloc.h" #include "alspan.h" #include "ambidefs.h" #include "bufferline.h" #include "devformat.h" #include "filters/splitter.h" - -struct FrontStablizer; +#include "front_stablizer.h" +#include "opthelpers.h" using ChannelDec = std::array; -class BFormatDec { +class SIMDALIGN BFormatDec { static constexpr size_t sHFBand{0}; static constexpr size_t sLFBand{1}; static constexpr size_t sNumBands{2}; struct ChannelDecoderSingle { - float mGains[MAX_OUTPUT_CHANNELS]; + std::array mGains{}; }; struct ChannelDecoderDual { BandSplitter mXOver; - float mGains[sNumBands][MAX_OUTPUT_CHANNELS]; + std::array,sNumBands> mGains{}; }; - alignas(16) std::array mSamples; + alignas(16) std::array mSamples{}; const std::unique_ptr mStablizer; @@ -44,22 +43,20 @@ class BFormatDec { const al::span coeffslf, const float xover_f0norm, std::unique_ptr stablizer); - bool hasStablizer() const noexcept { return mStablizer != nullptr; } + [[nodiscard]] auto hasStablizer() const noexcept -> bool { return mStablizer != nullptr; } /* Decodes the ambisonic input to the given output channels. */ - void process(const al::span OutBuffer, const FloatBufferLine *InSamples, - const size_t SamplesToDo); + void process(const al::span OutBuffer, + const al::span InSamples, const size_t SamplesToDo); /* Decodes the ambisonic input to the given output channels with stablization. */ void processStablize(const al::span OutBuffer, - const FloatBufferLine *InSamples, const size_t lidx, const size_t ridx, const size_t cidx, - const size_t SamplesToDo); + const al::span InSamples, const size_t lidx, const size_t ridx, + const size_t cidx, const size_t SamplesToDo); static std::unique_ptr Create(const size_t inchans, const al::span coeffs, const al::span coeffslf, const float xover_f0norm, std::unique_ptr stablizer); - - DEF_NEWDEL(BFormatDec) }; #endif /* CORE_BFORMATDEC_H */ diff --git a/3rdparty/openal/core/bs2b.cpp b/3rdparty/openal/core/bs2b.cpp index 303bf9bd9300..6e292b147d8b 100644 --- a/3rdparty/openal/core/bs2b.cpp +++ b/3rdparty/openal/core/bs2b.cpp @@ -26,72 +26,75 @@ #include #include #include +#include #include "alnumbers.h" +#include "alspan.h" #include "bs2b.h" +namespace { /* Set up all data. */ -static void init(struct bs2b *bs2b) +void init(Bs2b::bs2b *bs2b) { float Fc_lo, Fc_hi; float G_lo, G_hi; - float x, g; switch(bs2b->level) { - case BS2B_LOW_CLEVEL: /* Low crossfeed level */ + case Bs2b::LowCLevel: /* Low crossfeed level */ Fc_lo = 360.0f; Fc_hi = 501.0f; G_lo = 0.398107170553497f; G_hi = 0.205671765275719f; break; - case BS2B_MIDDLE_CLEVEL: /* Middle crossfeed level */ + case Bs2b::MiddleCLevel: /* Middle crossfeed level */ Fc_lo = 500.0f; Fc_hi = 711.0f; G_lo = 0.459726988530872f; G_hi = 0.228208484414988f; break; - case BS2B_HIGH_CLEVEL: /* High crossfeed level (virtual speakers are closer to itself) */ + case Bs2b::HighCLevel: /* High crossfeed level (virtual speakers are closer to itself) */ Fc_lo = 700.0f; Fc_hi = 1021.0f; G_lo = 0.530884444230988f; G_hi = 0.250105790667544f; break; - case BS2B_LOW_ECLEVEL: /* Low easy crossfeed level */ + case Bs2b::LowECLevel: /* Low easy crossfeed level */ Fc_lo = 360.0f; Fc_hi = 494.0f; G_lo = 0.316227766016838f; G_hi = 0.168236228897329f; break; - case BS2B_MIDDLE_ECLEVEL: /* Middle easy crossfeed level */ + case Bs2b::MiddleECLevel: /* Middle easy crossfeed level */ Fc_lo = 500.0f; Fc_hi = 689.0f; G_lo = 0.354813389233575f; G_hi = 0.187169483835901f; break; - default: /* High easy crossfeed level */ - bs2b->level = BS2B_HIGH_ECLEVEL; + case Bs2b::HighECLevel: /* High easy crossfeed level */ + default: + bs2b->level = Bs2b::HighECLevel; Fc_lo = 700.0f; Fc_hi = 975.0f; G_lo = 0.398107170553497f; G_hi = 0.205671765275719f; break; - } /* switch */ + } - g = 1.0f / (1.0f - G_hi + G_lo); + float g{1.0f / (1.0f - G_hi + G_lo)}; /* $fc = $Fc / $s; * $d = 1 / 2 / pi / $fc; * $x = exp(-1 / $d); */ - x = std::exp(-al::numbers::pi_v*2.0f*Fc_lo/static_cast(bs2b->srate)); + float x{ std::exp(-al::numbers::pi_v*2.0f*Fc_lo/static_cast(bs2b->srate))}; bs2b->b1_lo = x; bs2b->a0_lo = G_lo * (1.0f - x) * g; @@ -99,85 +102,88 @@ static void init(struct bs2b *bs2b) bs2b->b1_hi = x; bs2b->a0_hi = (1.0f - G_hi * (1.0f - x)) * g; bs2b->a1_hi = -x * g; -} /* init */ +} +} // namespace /* Exported functions. * See descriptions in "bs2b.h" */ +namespace Bs2b { -void bs2b_set_params(struct bs2b *bs2b, int level, int srate) -{ - if(srate <= 0) srate = 1; - - bs2b->level = level; - bs2b->srate = srate; - init(bs2b); -} /* bs2b_set_params */ - -int bs2b_get_level(struct bs2b *bs2b) +void bs2b::set_params(int level_, int srate_) { - return bs2b->level; -} /* bs2b_get_level */ + if(srate_ < 1) + throw std::runtime_error{"BS2B srate < 1"}; -int bs2b_get_srate(struct bs2b *bs2b) -{ - return bs2b->srate; -} /* bs2b_get_srate */ + level = level_; + srate = srate_; + init(this); +} -void bs2b_clear(struct bs2b *bs2b) +void bs2b::clear() { - std::fill(std::begin(bs2b->history), std::end(bs2b->history), bs2b::t_last_sample{}); -} /* bs2b_clear */ + history.fill(bs2b::t_last_sample{}); +} -void bs2b_cross_feed(struct bs2b *bs2b, float *Left, float *Right, size_t SamplesToDo) +void bs2b::cross_feed(float *Left, float *Right, size_t SamplesToDo) { - const float a0_lo{bs2b->a0_lo}; - const float b1_lo{bs2b->b1_lo}; - const float a0_hi{bs2b->a0_hi}; - const float a1_hi{bs2b->a1_hi}; - const float b1_hi{bs2b->b1_hi}; - float lsamples[128][2]; - float rsamples[128][2]; - - for(size_t base{0};base < SamplesToDo;) + const float a0lo{a0_lo}; + const float b1lo{b1_lo}; + const float a0hi{a0_hi}; + const float a1hi{a1_hi}; + const float b1hi{b1_hi}; + std::array,128> samples{}; + al::span lsamples{Left, SamplesToDo}; + al::span rsamples{Right, SamplesToDo}; + + while(!lsamples.empty()) { - const size_t todo{std::min(128, SamplesToDo-base)}; + const size_t todo{std::min(samples.size(), lsamples.size())}; /* Process left input */ - float z_lo{bs2b->history[0].lo}; - float z_hi{bs2b->history[0].hi}; - for(size_t i{0};i < todo;i++) - { - lsamples[i][0] = a0_lo*Left[i] + z_lo; - z_lo = b1_lo*lsamples[i][0]; - - lsamples[i][1] = a0_hi*Left[i] + z_hi; - z_hi = a1_hi*Left[i] + b1_hi*lsamples[i][1]; - } - bs2b->history[0].lo = z_lo; - bs2b->history[0].hi = z_hi; + float z_lo{history[0].lo}; + float z_hi{history[0].hi}; + std::transform(lsamples.cbegin(), lsamples.cbegin()+ptrdiff_t(todo), samples.begin(), + [a0hi,a1hi,b1hi,a0lo,b1lo,&z_lo,&z_hi](const float x) -> std::array + { + float y0{a0hi*x + z_hi}; + z_hi = a1hi*x + b1hi*y0; + + float y1{a0lo*x + z_lo}; + z_lo = b1lo*y1; + + return {y0, y1}; + }); + history[0].lo = z_lo; + history[0].hi = z_hi; /* Process right input */ - z_lo = bs2b->history[1].lo; - z_hi = bs2b->history[1].hi; - for(size_t i{0};i < todo;i++) - { - rsamples[i][0] = a0_lo*Right[i] + z_lo; - z_lo = b1_lo*rsamples[i][0]; - - rsamples[i][1] = a0_hi*Right[i] + z_hi; - z_hi = a1_hi*Right[i] + b1_hi*rsamples[i][1]; - } - bs2b->history[1].lo = z_lo; - bs2b->history[1].hi = z_hi; - - /* Crossfeed */ - for(size_t i{0};i < todo;i++) - *(Left++) = lsamples[i][1] + rsamples[i][0]; - for(size_t i{0};i < todo;i++) - *(Right++) = rsamples[i][1] + lsamples[i][0]; - - base += todo; + z_lo = history[1].lo; + z_hi = history[1].hi; + std::transform(rsamples.cbegin(), rsamples.cbegin()+ptrdiff_t(todo), samples.begin(), + samples.begin(), + [a0hi,a1hi,b1hi,a0lo,b1lo,&z_lo,&z_hi](const float x, const std::array out) -> std::array + { + float y0{a0lo*x + z_lo}; + z_lo = b1lo*y0; + + float y1{a0hi*x + z_hi}; + z_hi = a1hi*x + b1hi*y1; + + return {out[0]+y0, out[1]+y1}; + }); + history[1].lo = z_lo; + history[1].hi = z_hi; + + auto iter = std::transform(samples.cbegin(), samples.cbegin()+todo, lsamples.begin(), + [](const std::array &in) { return in[0]; }); + lsamples = {iter, lsamples.end()}; + + iter = std::transform(samples.cbegin(), samples.cbegin()+todo, rsamples.begin(), + [](const std::array &in) { return in[1]; }); + rsamples = {iter, rsamples.end()}; } -} /* bs2b_cross_feed */ +} + +} // namespace Bs2b diff --git a/3rdparty/openal/core/bs2b.h b/3rdparty/openal/core/bs2b.h index 4d0b9dd8e14e..32c77e1010e3 100644 --- a/3rdparty/openal/core/bs2b.h +++ b/3rdparty/openal/core/bs2b.h @@ -24,66 +24,66 @@ #ifndef CORE_BS2B_H #define CORE_BS2B_H -#include "almalloc.h" +#include +#include -/* Number of crossfeed levels */ -#define BS2B_CLEVELS 3 +namespace Bs2b { -/* Normal crossfeed levels */ -#define BS2B_HIGH_CLEVEL 3 -#define BS2B_MIDDLE_CLEVEL 2 -#define BS2B_LOW_CLEVEL 1 +enum { + /* Normal crossfeed levels */ + LowCLevel = 1, + MiddleCLevel = 2, + HighCLevel = 3, -/* Easy crossfeed levels */ -#define BS2B_HIGH_ECLEVEL BS2B_HIGH_CLEVEL + BS2B_CLEVELS -#define BS2B_MIDDLE_ECLEVEL BS2B_MIDDLE_CLEVEL + BS2B_CLEVELS -#define BS2B_LOW_ECLEVEL BS2B_LOW_CLEVEL + BS2B_CLEVELS + /* Easy crossfeed levels */ + LowECLevel = 4, + MiddleECLevel = 5, + HighECLevel = 6, -/* Default crossfeed levels */ -#define BS2B_DEFAULT_CLEVEL BS2B_HIGH_ECLEVEL -/* Default sample rate (Hz) */ -#define BS2B_DEFAULT_SRATE 44100 + DefaultCLevel = HighECLevel +}; struct bs2b { - int level; /* Crossfeed level */ - int srate; /* Sample rate (Hz) */ + int level{}; /* Crossfeed level */ + int srate{}; /* Sample rate (Hz) */ /* Lowpass IIR filter coefficients */ - float a0_lo; - float b1_lo; + float a0_lo{}; + float b1_lo{}; /* Highboost IIR filter coefficients */ - float a0_hi; - float a1_hi; - float b1_hi; + float a0_hi{}; + float a1_hi{}; + float b1_hi{}; /* Buffer of filter history * [0] - first channel, [1] - second channel */ struct t_last_sample { - float lo; - float hi; - } history[2]; - - DEF_NEWDEL(bs2b) -}; + float lo{}; + float hi{}; + }; + std::array history{}; + + /* Clear buffers and set new coefficients with new crossfeed level and + * sample rate values. + * level - crossfeed level of *Level enum values. + * srate - sample rate by Hz. + */ + void set_params(int level, int srate); -/* Clear buffers and set new coefficients with new crossfeed level and sample - * rate values. - * level - crossfeed level of *LEVEL values. - * srate - sample rate by Hz. - */ -void bs2b_set_params(bs2b *bs2b, int level, int srate); + /* Return current crossfeed level value */ + [[nodiscard]] auto get_level() const noexcept -> int { return level; } -/* Return current crossfeed level value */ -int bs2b_get_level(bs2b *bs2b); + /* Return current sample rate value */ + [[nodiscard]] auto get_srate() const noexcept -> int { return srate; } -/* Return current sample rate value */ -int bs2b_get_srate(bs2b *bs2b); + /* Clear buffer */ + void clear(); -/* Clear buffer */ -void bs2b_clear(bs2b *bs2b); + void cross_feed(float *Left, float *Right, std::size_t SamplesToDo); +}; -void bs2b_cross_feed(bs2b *bs2b, float *Left, float *Right, size_t SamplesToDo); +} // namespace Bs2b #endif /* CORE_BS2B_H */ diff --git a/3rdparty/openal/core/bsinc_tables.cpp b/3rdparty/openal/core/bsinc_tables.cpp index 1b3f57841a2f..9d6b5d8b77e6 100644 --- a/3rdparty/openal/core/bsinc_tables.cpp +++ b/3rdparty/openal/core/bsinc_tables.cpp @@ -5,12 +5,16 @@ #include #include #include +#include #include -#include -#include +#include +#include #include "alnumbers.h" +#include "alnumeric.h" +#include "alspan.h" #include "bsinc_defs.h" +#include "opthelpers.h" #include "resampler_limits.h" @@ -19,37 +23,32 @@ namespace { using uint = unsigned int; -/* 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::epsilon()}; - if(!(x > epsilon || x < -epsilon)) - return 1.0; - return std::sin(al::numbers::pi*x) / (al::numbers::pi*x); -} - /* 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 +constexpr auto cyl_bessel_i(T nu, U x) -> U { + 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; @@ -57,8 +56,20 @@ constexpr double BesselI_0(const double x) noexcept term *= y * y; sum += term; } while(sum != last_sum); + return static_cast(sum); +} - 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::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 @@ -79,7 +90,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. @@ -98,7 +109,7 @@ constexpr double CalcKaiserBeta(const double rejection) { if(rejection > 50.0) return 0.1102 * (rejection-8.7); - else if(rejection >= 21.0) + if(rejection >= 21.0) return (0.5842 * std::pow(rejection-21.0, 0.4)) + (0.07886 * (rejection-21.0)); return 0.0; } @@ -108,24 +119,18 @@ struct BSincHeader { double width{}; double beta{}; double scaleBase{}; - double scaleRange{}; - double besseli_0_beta{}; - uint a[BSincScaleCount]{}; + std::array a{}; uint total_size{}; constexpr BSincHeader(uint Rejection, uint Order) noexcept + : width{CalcKaiserWidth(Rejection, Order)}, beta{CalcKaiserBeta(Rejection)} + , scaleBase{width / 2.0} { - 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(num_points / 2.0 / scale), num_points)}; const uint m{2 * a_}; @@ -144,16 +149,18 @@ constexpr BSincHeader bsinc24_hdr{60, 23}; template -struct BSincFilterArray { +struct SIMDALIGN BSincFilterArray { alignas(16) std::array mTable{}; BSincFilterArray() { - constexpr uint BSincPointsMax{(hdr.a[0]*2 + 3) & ~3u}; + static constexpr uint BSincPointsMax{(hdr.a[0]*2u + 3u) & ~3u}; static_assert(BSincPointsMax <= MaxResamplerPadding, "MaxResamplerPadding is too small"); - using filter_type = double[BSincPhaseCount+1][BSincPointsMax]; - auto filter = std::make_unique(BSincScaleCount); + using filter_type = std::array,BSincPhaseCount>; + auto filter = std::vector(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. @@ -162,22 +169,19 @@ 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(hdr.a[si]); const double l{a - 1.0/BSincPhaseCount}; - /* Do one extra phase index so that the phase delta has a proper - * target for its last index. - */ - for(uint pi{0};pi <= BSincPhaseCount;++pi) + for(uint pi{0};pi < BSincPhaseCount;++pi) { const double phase{std::floor(l) + (pi/double{BSincPhaseCount})}; 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); } } @@ -200,50 +204,78 @@ struct BSincFilterArray { /* Linear interpolation between phases is simplified by pre- * calculating the delta (b - a) in: x = a + f (b - a) */ - for(size_t i{0};i < m;++i) + if(pi < BSincPhaseCount-1) { - const double phDelta{filter[si][pi+1][o+i] - filter[si][pi][o+i]}; - mTable[idx++] = static_cast(phDelta); + for(size_t i{0};i < m;++i) + { + const double phDelta{filter[si][pi+1][o+i] - filter[si][pi][o+i]}; + mTable[idx++] = static_cast(phDelta); + } } - } - /* Calculate and write out each phase index's filter quality scale - * deltas. The last scale index doesn't have any scale or scale- - * phase deltas. - */ - if(si == BSincScaleCount-1) - { - for(size_t i{0};i < BSincPhaseCount*m*2;++i) - mTable[idx++] = 0.0f; - } - else for(size_t pi{0};pi < BSincPhaseCount;++pi) - { - /* Linear interpolation between scales is also simplified. - * - * Given a difference in the number of points between scales, - * the destination points will be 0, thus: x = a + f (-a) - */ - for(size_t i{0};i < m;++i) + else { - const double scDelta{filter[si+1][pi][o+i] - filter[si][pi][o+i]}; - mTable[idx++] = static_cast(scDelta); + /* The delta target for the last phase index is the first + * phase index with the coefficients offset by one. The + * first delta targets 0, as it represents a coefficient + * for a sample that won't be part of the filter. + */ + mTable[idx++] = static_cast(0.0 - filter[si][pi][o]); + for(size_t i{1};i < m;++i) + { + const double phDelta{filter[si][0][o+i-1] - filter[si][pi][o+i]}; + mTable[idx++] = static_cast(phDelta); + } } + } - /* This last simplification is done to complete the bilinear - * equation for the combination of phase and scale. - */ - for(size_t i{0};i < m;++i) + /* Now write out each phase index's scale and phase+scale deltas, + * to complete the bilinear equation for the combination of phase + * and scale. + */ + if(si < BSincScaleCount-1) + { + for(size_t pi{0};pi < BSincPhaseCount;++pi) { - const double spDelta{(filter[si+1][pi+1][o+i] - filter[si+1][pi][o+i]) - - (filter[si][pi+1][o+i] - filter[si][pi][o+i])}; - mTable[idx++] = static_cast(spDelta); + for(size_t i{0};i < m;++i) + { + const double scDelta{filter[si+1][pi][o+i] - filter[si][pi][o+i]}; + mTable[idx++] = static_cast(scDelta); + } + + if(pi < BSincPhaseCount-1) + { + for(size_t i{0};i < m;++i) + { + const double spDelta{(filter[si+1][pi+1][o+i]-filter[si+1][pi][o+i]) - + (filter[si][pi+1][o+i]-filter[si][pi][o+i])}; + mTable[idx++] = static_cast(spDelta); + } + } + else + { + mTable[idx++] = static_cast((0.0 - filter[si+1][pi][o]) - + (0.0 - filter[si][pi][o])); + for(size_t i{1};i < m;++i) + { + const double spDelta{(filter[si+1][0][o+i-1] - filter[si+1][pi][o+i]) - + (filter[si][0][o+i-1] - filter[si][pi][o+i])}; + mTable[idx++] = static_cast(spDelta); + } + } } } + else + { + /* The last scale index doesn't have scale-related deltas. */ + for(size_t i{0};i < BSincPhaseCount*m*2;++i) + mTable[idx++] = 0.0f; + } } assert(idx == hdr.total_size); } - constexpr const BSincHeader &getHeader() const noexcept { return hdr; } - constexpr const float *getTable() const noexcept { return &mTable.front(); } + [[nodiscard]] constexpr auto getHeader() const noexcept -> const BSincHeader& { return hdr; } + [[nodiscard]] constexpr auto getTable() const noexcept { return al::span{mTable}; } }; const BSincFilterArray bsinc12_filter{}; @@ -255,7 +287,7 @@ constexpr BSincTable GenerateBSincTable(const T &filter) BSincTable ret{}; const BSincHeader &hdr = filter.getHeader(); ret.scaleBase = static_cast(hdr.scaleBase); - ret.scaleRange = static_cast(1.0 / hdr.scaleRange); + ret.scaleRange = static_cast(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/3rdparty/openal/core/bsinc_tables.h b/3rdparty/openal/core/bsinc_tables.h index aca4b274420a..eab894ce96d6 100644 --- a/3rdparty/openal/core/bsinc_tables.h +++ b/3rdparty/openal/core/bsinc_tables.h @@ -1,14 +1,17 @@ #ifndef CORE_BSINC_TABLES_H #define CORE_BSINC_TABLES_H +#include + +#include "alspan.h" #include "bsinc_defs.h" struct BSincTable { float scaleBase, scaleRange; - unsigned int m[BSincScaleCount]; - unsigned int filterOffset[BSincScaleCount]; - const float *Tab; + std::array m; + std::array filterOffset; + al::span Tab; }; extern const BSincTable gBSinc12; diff --git a/3rdparty/openal/core/buffer_storage.cpp b/3rdparty/openal/core/buffer_storage.cpp index 98ca2c1b3fe5..780e388adeb7 100644 --- a/3rdparty/openal/core/buffer_storage.cpp +++ b/3rdparty/openal/core/buffer_storage.cpp @@ -3,79 +3,3 @@ #include "buffer_storage.h" -#include - - -const char *NameFromFormat(FmtType type) noexcept -{ - switch(type) - { - case FmtUByte: return "UInt8"; - case FmtShort: return "Int16"; - case FmtFloat: return "Float"; - case FmtDouble: return "Double"; - case FmtMulaw: return "muLaw"; - case FmtAlaw: return "aLaw"; - case FmtIMA4: return "IMA4 ADPCM"; - case FmtMSADPCM: return "MS ADPCM"; - } - return ""; -} - -const char *NameFromFormat(FmtChannels channels) noexcept -{ - switch(channels) - { - case FmtMono: return "Mono"; - case FmtStereo: return "Stereo"; - case FmtRear: return "Rear"; - case FmtQuad: return "Quadraphonic"; - case FmtX51: return "Surround 5.1"; - case FmtX61: return "Surround 6.1"; - case FmtX71: return "Surround 7.1"; - case FmtBFormat2D: return "B-Format 2D"; - case FmtBFormat3D: return "B-Format 3D"; - case FmtUHJ2: return "UHJ2"; - case FmtUHJ3: return "UHJ3"; - case FmtUHJ4: return "UHJ4"; - case FmtSuperStereo: return "Super Stereo"; - } - return ""; -} - -uint BytesFromFmt(FmtType type) noexcept -{ - switch(type) - { - case FmtUByte: return sizeof(uint8_t); - case FmtShort: return sizeof(int16_t); - case FmtFloat: return sizeof(float); - case FmtDouble: return sizeof(double); - case FmtMulaw: return sizeof(uint8_t); - case FmtAlaw: return sizeof(uint8_t); - case FmtIMA4: break; - case FmtMSADPCM: break; - } - return 0; -} - -uint ChannelsFromFmt(FmtChannels chans, uint ambiorder) noexcept -{ - switch(chans) - { - case FmtMono: return 1; - case FmtStereo: return 2; - case FmtRear: return 2; - case FmtQuad: return 4; - case FmtX51: return 6; - case FmtX61: return 7; - case FmtX71: return 8; - case FmtBFormat2D: return (ambiorder*2) + 1; - case FmtBFormat3D: return (ambiorder+1) * (ambiorder+1); - case FmtUHJ2: return 2; - case FmtUHJ3: return 3; - case FmtUHJ4: return 4; - case FmtSuperStereo: return 2; - } - return 0; -} diff --git a/3rdparty/openal/core/buffer_storage.h b/3rdparty/openal/core/buffer_storage.h index d8ab0b670d02..ab097cf12fad 100644 --- a/3rdparty/openal/core/buffer_storage.h +++ b/3rdparty/openal/core/buffer_storage.h @@ -7,56 +7,11 @@ #include "alnumeric.h" #include "alspan.h" #include "ambidefs.h" +#include "storage_formats.h" using uint = unsigned int; -/* Storable formats */ -enum FmtType : unsigned char { - FmtUByte, - FmtShort, - FmtFloat, - FmtDouble, - FmtMulaw, - FmtAlaw, - FmtIMA4, - FmtMSADPCM, -}; -enum FmtChannels : unsigned char { - FmtMono, - FmtStereo, - FmtRear, - FmtQuad, - FmtX51, /* (WFX order) */ - FmtX61, /* (WFX order) */ - FmtX71, /* (WFX order) */ - FmtBFormat2D, - FmtBFormat3D, - FmtUHJ2, /* 2-channel UHJ, aka "BHJ", stereo-compatible */ - FmtUHJ3, /* 3-channel UHJ, aka "THJ" */ - FmtUHJ4, /* 4-channel UHJ, aka "PHJ" */ - FmtSuperStereo, /* Stereo processed with Super Stereo. */ -}; - -enum class AmbiLayout : unsigned char { - FuMa, - ACN, -}; -enum class AmbiScaling : unsigned char { - FuMa, - SN3D, - N3D, - UHJ, -}; - -const char *NameFromFormat(FmtType type) noexcept; -const char *NameFromFormat(FmtChannels channels) noexcept; - -uint BytesFromFmt(FmtType type) noexcept; -uint ChannelsFromFmt(FmtChannels chans, uint ambiorder) noexcept; -inline uint FrameSizeFromFmt(FmtChannels chans, FmtType type, uint ambiorder) noexcept -{ return ChannelsFromFmt(chans, ambiorder) * BytesFromFmt(type); } - constexpr bool IsBFormat(FmtChannels chans) noexcept { return chans == FmtBFormat2D || chans == FmtBFormat3D; } @@ -97,19 +52,20 @@ struct BufferStorage { AmbiScaling mAmbiScaling{AmbiScaling::FuMa}; uint mAmbiOrder{0u}; - inline uint bytesFromFmt() const noexcept { return BytesFromFmt(mType); } - inline uint channelsFromFmt() const noexcept + [[nodiscard]] auto bytesFromFmt() const noexcept -> uint { return BytesFromFmt(mType); } + [[nodiscard]] auto channelsFromFmt() const noexcept -> uint { return ChannelsFromFmt(mChannels, mAmbiOrder); } - inline uint frameSizeFromFmt() const noexcept { return channelsFromFmt() * bytesFromFmt(); } + [[nodiscard]] auto frameSizeFromFmt() const noexcept -> uint + { return channelsFromFmt() * bytesFromFmt(); } - inline uint blockSizeFromFmt() const noexcept + [[nodiscard]] auto blockSizeFromFmt() const noexcept -> uint { if(mType == FmtIMA4) return ((mBlockAlign-1)/2 + 4) * channelsFromFmt(); if(mType == FmtMSADPCM) return ((mBlockAlign-2)/2 + 7) * channelsFromFmt(); return frameSizeFromFmt(); }; - inline bool isBFormat() const noexcept { return IsBFormat(mChannels); } + [[nodiscard]] auto isBFormat() const noexcept -> bool { return IsBFormat(mChannels); } }; #endif /* CORE_BUFFER_STORAGE_H */ diff --git a/3rdparty/openal/core/bufferline.h b/3rdparty/openal/core/bufferline.h index 8b445f3ff58a..309fb778f841 100644 --- a/3rdparty/openal/core/bufferline.h +++ b/3rdparty/openal/core/bufferline.h @@ -9,7 +9,7 @@ * more memory and are harder on cache, while smaller values may need more * iterations for mixing. */ -constexpr int BufferLineSize{1024}; +inline constexpr size_t BufferLineSize{1024}; using FloatBufferLine = std::array; using FloatBufferSpan = al::span; diff --git a/3rdparty/openal/core/context.cpp b/3rdparty/openal/core/context.cpp index 2ebbc7b13ed8..ca4ccf845e41 100644 --- a/3rdparty/openal/core/context.cpp +++ b/3rdparty/openal/core/context.cpp @@ -2,6 +2,7 @@ #include "config.h" #include +#include #include #include #include @@ -26,55 +27,19 @@ ContextBase::ContextBase(DeviceBase *device) : mDevice{device} ContextBase::~ContextBase() { - size_t count{0}; - ContextProps *cprops{mParams.ContextUpdate.exchange(nullptr, std::memory_order_relaxed)}; - if(cprops) - { - ++count; - delete cprops; - } - cprops = mFreeContextProps.exchange(nullptr, std::memory_order_acquire); - while(cprops) - { - std::unique_ptr old{cprops}; - cprops = old->next.load(std::memory_order_relaxed); - ++count; - } - TRACE("Freed %zu context property object%s\n", count, (count==1)?"":"s"); - - count = 0; - EffectSlotProps *eprops{mFreeEffectslotProps.exchange(nullptr, std::memory_order_acquire)}; - while(eprops) - { - std::unique_ptr old{eprops}; - eprops = old->next.load(std::memory_order_relaxed); - ++count; - } - TRACE("Freed %zu AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s"); - - if(EffectSlotArray *curarray{mActiveAuxSlots.exchange(nullptr, std::memory_order_relaxed)}) - { - std::destroy_n(curarray->end(), curarray->size()); - delete curarray; - } - - delete mVoices.exchange(nullptr, std::memory_order_relaxed); + mActiveAuxSlots.store(nullptr, std::memory_order_relaxed); + mVoices.store(nullptr, std::memory_order_relaxed); if(mAsyncEvents) { - count = 0; - auto evt_vec = mAsyncEvents->getReadVector(); - if(evt_vec.first.len > 0) + size_t count{0}; + for(auto &evt : mAsyncEvents->getReadVector()) { - std::destroy_n(std::launder(reinterpret_cast(evt_vec.first.buf)), - evt_vec.first.len); - count += evt_vec.first.len; - } - if(evt_vec.second.len > 0) - { - std::destroy_n(std::launder(reinterpret_cast(evt_vec.second.buf)), - evt_vec.second.len); - count += evt_vec.second.len; + if(evt.len > 0) + { + std::destroy_n(std::launder(reinterpret_cast(evt.buf)), evt.len); + count += evt.len; + } } if(count > 0) TRACE("Destructed %zu orphaned event%s\n", count, (count==1)?"":"s"); @@ -85,85 +50,131 @@ ContextBase::~ContextBase() void ContextBase::allocVoiceChanges() { - constexpr size_t clustersize{128}; + static constexpr size_t clustersize{std::tuple_size_v}; + + VoiceChangeCluster clusterptr{std::make_unique()}; + const auto cluster = al::span{*clusterptr}; - VoiceChangeCluster cluster{std::make_unique(clustersize)}; for(size_t i{1};i < clustersize;++i) cluster[i-1].mNext.store(std::addressof(cluster[i]), std::memory_order_relaxed); cluster[clustersize-1].mNext.store(mVoiceChangeTail, std::memory_order_relaxed); - mVoiceChangeClusters.emplace_back(std::move(cluster)); - mVoiceChangeTail = mVoiceChangeClusters.back().get(); + mVoiceChangeClusters.emplace_back(std::move(clusterptr)); + mVoiceChangeTail = mVoiceChangeClusters.back()->data(); } void ContextBase::allocVoiceProps() { - constexpr size_t clustersize{32}; + static constexpr size_t clustersize{std::tuple_size_v}; TRACE("Increasing allocated voice properties to %zu\n", (mVoicePropClusters.size()+1) * clustersize); - VoicePropsCluster cluster{std::make_unique(clustersize)}; + auto clusterptr = std::make_unique(); + auto cluster = al::span{*clusterptr}; for(size_t i{1};i < clustersize;++i) cluster[i-1].next.store(std::addressof(cluster[i]), std::memory_order_relaxed); - mVoicePropClusters.emplace_back(std::move(cluster)); + mVoicePropClusters.emplace_back(std::move(clusterptr)); VoicePropsItem *oldhead{mFreeVoiceProps.load(std::memory_order_acquire)}; do { - mVoicePropClusters.back()[clustersize-1].next.store(oldhead, std::memory_order_relaxed); - } while(mFreeVoiceProps.compare_exchange_weak(oldhead, mVoicePropClusters.back().get(), + mVoicePropClusters.back()->back().next.store(oldhead, std::memory_order_relaxed); + } while(mFreeVoiceProps.compare_exchange_weak(oldhead, mVoicePropClusters.back()->data(), std::memory_order_acq_rel, std::memory_order_acquire) == false); } void ContextBase::allocVoices(size_t addcount) { - constexpr size_t clustersize{32}; + static constexpr size_t clustersize{std::tuple_size_v}; /* Convert element count to cluster count. */ addcount = (addcount+(clustersize-1)) / clustersize; + if(!addcount) + { + if(!mVoiceClusters.empty()) + return; + ++addcount; + } + if(addcount >= std::numeric_limits::max()/clustersize - mVoiceClusters.size()) throw std::runtime_error{"Allocating too many voices"}; const size_t totalcount{(mVoiceClusters.size()+addcount) * clustersize}; TRACE("Increasing allocated voices to %zu\n", totalcount); - auto newarray = VoiceArray::Create(totalcount); while(addcount) { - mVoiceClusters.emplace_back(std::make_unique(clustersize)); + mVoiceClusters.emplace_back(std::make_unique()); --addcount; } + auto newarray = VoiceArray::Create(totalcount); auto voice_iter = newarray->begin(); for(VoiceCluster &cluster : mVoiceClusters) - { - for(size_t i{0};i < clustersize;++i) - *(voice_iter++) = &cluster[i]; - } + voice_iter = std::transform(cluster->begin(), cluster->end(), voice_iter, + [](Voice &voice) noexcept -> Voice* { return &voice; }); - if(auto *oldvoices = mVoices.exchange(newarray.release(), std::memory_order_acq_rel)) - { - mDevice->waitForMix(); - delete oldvoices; - } + if(auto oldvoices = mVoices.exchange(std::move(newarray), std::memory_order_acq_rel)) + std::ignore = mDevice->waitForMix(); } +void ContextBase::allocEffectSlotProps() +{ + static constexpr size_t clustersize{std::tuple_size_v}; + + TRACE("Increasing allocated effect slot properties to %zu\n", + (mEffectSlotPropClusters.size()+1) * clustersize); + + auto clusterptr = std::make_unique(); + auto cluster = al::span{*clusterptr}; + for(size_t i{1};i < clustersize;++i) + cluster[i-1].next.store(std::addressof(cluster[i]), std::memory_order_relaxed); + auto *newcluster = mEffectSlotPropClusters.emplace_back(std::move(clusterptr)).get(); + + EffectSlotProps *oldhead{mFreeEffectSlotProps.load(std::memory_order_acquire)}; + do { + newcluster->back().next.store(oldhead, std::memory_order_relaxed); + } while(mFreeEffectSlotProps.compare_exchange_weak(oldhead, newcluster->data(), + std::memory_order_acq_rel, std::memory_order_acquire) == false); +} + EffectSlot *ContextBase::getEffectSlot() { - for(auto& cluster : mEffectSlotClusters) + for(auto& clusterptr : mEffectSlotClusters) { - for(size_t i{0};i < EffectSlotClusterSize;++i) - { - if(!cluster[i].InUse) - return &cluster[i]; - } + const auto cluster = al::span{*clusterptr}; + auto iter = std::find_if_not(cluster.begin(), cluster.end(), + std::mem_fn(&EffectSlot::InUse)); + if(iter != cluster.end()) return al::to_address(iter); } - if(1 >= std::numeric_limits::max()/EffectSlotClusterSize - mEffectSlotClusters.size()) + auto clusterptr = std::make_unique(); + if(1 >= std::numeric_limits::max()/clusterptr->size() - mEffectSlotClusters.size()) throw std::runtime_error{"Allocating too many effect slots"}; - const size_t totalcount{(mEffectSlotClusters.size()+1) * EffectSlotClusterSize}; + const size_t totalcount{(mEffectSlotClusters.size()+1) * clusterptr->size()}; TRACE("Increasing allocated effect slots to %zu\n", totalcount); - mEffectSlotClusters.emplace_back(std::make_unique(EffectSlotClusterSize)); - return getEffectSlot(); + mEffectSlotClusters.emplace_back(std::move(clusterptr)); + return mEffectSlotClusters.back()->data(); +} + + +void ContextBase::allocContextProps() +{ + static constexpr size_t clustersize{std::tuple_size_v}; + + TRACE("Increasing allocated context properties to %zu\n", + (mContextPropClusters.size()+1) * clustersize); + + auto clusterptr = std::make_unique(); + auto cluster = al::span{*clusterptr}; + for(size_t i{1};i < clustersize;++i) + cluster[i-1].next.store(std::addressof(cluster[i]), std::memory_order_relaxed); + auto *newcluster = mContextPropClusters.emplace_back(std::move(clusterptr)).get(); + + ContextProps *oldhead{mFreeContextProps.load(std::memory_order_acquire)}; + do { + newcluster->back().next.store(oldhead, std::memory_order_relaxed); + } while(mFreeContextProps.compare_exchange_weak(oldhead, newcluster->data(), + std::memory_order_acq_rel, std::memory_order_acquire) == false); } diff --git a/3rdparty/openal/core/context.h b/3rdparty/openal/core/context.h index ccb7dd3bfbec..fc47a4963bf2 100644 --- a/3rdparty/openal/core/context.h +++ b/3rdparty/openal/core/context.h @@ -1,6 +1,8 @@ #ifndef CORE_CONTEXT_H #define CORE_CONTEXT_H +#include "config.h" + #include #include #include @@ -9,11 +11,11 @@ #include #include -#include "almalloc.h" #include "alsem.h" #include "alspan.h" #include "async_event.h" #include "atomic.h" +#include "flexarray.h" #include "opthelpers.h" #include "vecmat.h" @@ -26,9 +28,9 @@ struct VoiceChange; struct VoicePropsItem; -constexpr float SpeedOfSoundMetersPerSec{343.3f}; +inline constexpr float SpeedOfSoundMetersPerSec{343.3f}; -constexpr float AirAbsorbGainHF{0.99426f}; /* -0.05dB */ +inline constexpr float AirAbsorbGainHF{0.99426f}; /* -0.05dB */ enum class DistanceModel : unsigned char { Disable, @@ -52,21 +54,22 @@ struct ContextProps { float DopplerFactor; float DopplerVelocity; float SpeedOfSound; +#if ALSOFT_EAX + float DistanceFactor; +#endif bool SourceDistanceModel; DistanceModel mDistanceModel; std::atomic next; - - DEF_NEWDEL(ContextProps) }; struct ContextParams { /* Pointer to the most recent property values that are awaiting an update. */ std::atomic ContextUpdate{nullptr}; - alu::Vector Position{}; + alu::Vector Position; alu::Matrix Matrix{alu::Matrix::Identity()}; - alu::Vector Velocity{}; + alu::Vector Velocity; float Gain{1.0f}; float MetersPerUnit{1.0f}; @@ -85,7 +88,7 @@ struct ContextBase { /* Counter for the pre-mixing updates, in 31.1 fixed point (lowest bit * indicates if updates are currently happening). */ - RefCount mUpdateCount{0u}; + std::atomic mUpdateCount{0u}; std::atomic mHoldUpdates{false}; std::atomic mStopVoicesOnDisconnect{true}; @@ -96,7 +99,7 @@ struct ContextBase { */ std::atomic mFreeContextProps{nullptr}; std::atomic mFreeVoiceProps{nullptr}; - std::atomic mFreeEffectslotProps{nullptr}; + std::atomic mFreeEffectSlotProps{nullptr}; /* The voice change tail is the beginning of the "free" elements, up to and * *excluding* the current. If tail==current, there's no free elements and @@ -108,21 +111,22 @@ struct ContextBase { void allocVoiceChanges(); void allocVoiceProps(); - + void allocEffectSlotProps(); + void allocContextProps(); ContextParams mParams; using VoiceArray = al::FlexArray; - std::atomic mVoices{}; + al::atomic_unique_ptr mVoices; std::atomic mActiveVoiceCount{}; void allocVoices(size_t addcount); - al::span getVoicesSpan() const noexcept + [[nodiscard]] auto getVoicesSpan() const noexcept -> al::span { return {mVoices.load(std::memory_order_relaxed)->data(), mActiveVoiceCount.load(std::memory_order_relaxed)}; } - al::span getVoicesSpanAcquired() const noexcept + [[nodiscard]] auto getVoicesSpanAcquired() const noexcept -> al::span { return {mVoices.load(std::memory_order_acquire)->data(), mActiveVoiceCount.load(std::memory_order_acquire)}; @@ -130,7 +134,11 @@ struct ContextBase { using EffectSlotArray = al::FlexArray; - std::atomic mActiveAuxSlots{nullptr}; + /* This array is split in half. The front half is the list of activated + * effect slots as set by the app, and the back half is the same list but + * sorted to ensure later effect slots are fed by earlier ones. + */ + al::atomic_unique_ptr mActiveAuxSlots; std::thread mEventThread; al::semaphore mEventSem; @@ -143,27 +151,35 @@ struct ContextBase { * However, to avoid allocating each object individually, they're allocated * in clusters that are stored in a vector for easy automatic cleanup. */ - using VoiceChangeCluster = std::unique_ptr; + using VoiceChangeCluster = std::unique_ptr>; std::vector mVoiceChangeClusters; - using VoiceCluster = std::unique_ptr; + using VoiceCluster = std::unique_ptr>; std::vector mVoiceClusters; - using VoicePropsCluster = std::unique_ptr; + using VoicePropsCluster = std::unique_ptr>; std::vector mVoicePropClusters; - static constexpr size_t EffectSlotClusterSize{4}; EffectSlot *getEffectSlot(); - using EffectSlotCluster = std::unique_ptr; + using EffectSlotCluster = std::unique_ptr>; std::vector mEffectSlotClusters; + using EffectSlotPropsCluster = std::unique_ptr>; + std::vector mEffectSlotPropClusters; + + /* This could be greater than 2, but there should be no way there can be + * more than two context property updates in use simultaneously. + */ + using ContextPropsCluster = std::unique_ptr>; + std::vector mContextPropClusters; + ContextBase(DeviceBase *device); ContextBase(const ContextBase&) = delete; ContextBase& operator=(const ContextBase&) = delete; - ~ContextBase(); + virtual ~ContextBase(); }; #endif /* CORE_CONTEXT_H */ diff --git a/3rdparty/openal/core/converter.cpp b/3rdparty/openal/core/converter.cpp index dea31bd57f6b..97fffc161b82 100644 --- a/3rdparty/openal/core/converter.cpp +++ b/3rdparty/openal/core/converter.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include "albit.h" #include "alnumeric.h" @@ -24,43 +24,46 @@ static_assert((BufferLineSize-1)/MaxPitch > 0, "MaxPitch is too large for Buffer static_assert((INT_MAX>>MixerFracBits)/MaxPitch > BufferLineSize, "MaxPitch and/or BufferLineSize are too large for MixerFracBits!"); -/* Base template left undefined. Should be marked =delete, but Clang 3.8.1 - * chokes on that given the inline specializations. - */ template -inline float LoadSample(DevFmtType_t val) noexcept; +constexpr float LoadSample(DevFmtType_t val) noexcept = delete; -template<> inline float LoadSample(DevFmtType_t val) noexcept -{ return val * (1.0f/128.0f); } -template<> inline float LoadSample(DevFmtType_t val) noexcept -{ return val * (1.0f/32768.0f); } -template<> inline float LoadSample(DevFmtType_t val) noexcept +template<> constexpr float LoadSample(DevFmtType_t val) noexcept +{ return float(val) * (1.0f/128.0f); } +template<> constexpr float LoadSample(DevFmtType_t val) noexcept +{ return float(val) * (1.0f/32768.0f); } +template<> constexpr float LoadSample(DevFmtType_t val) noexcept { return static_cast(val) * (1.0f/2147483648.0f); } -template<> inline float LoadSample(DevFmtType_t val) noexcept +template<> constexpr float LoadSample(DevFmtType_t val) noexcept { return val; } -template<> inline float LoadSample(DevFmtType_t val) noexcept +template<> constexpr float LoadSample(DevFmtType_t val) noexcept { return LoadSample(static_cast(val - 128)); } -template<> inline float LoadSample(DevFmtType_t val) noexcept +template<> constexpr float LoadSample(DevFmtType_t val) noexcept { return LoadSample(static_cast(val - 32768)); } -template<> inline float LoadSample(DevFmtType_t val) noexcept +template<> constexpr float LoadSample(DevFmtType_t val) noexcept { return LoadSample(static_cast(val - 2147483648u)); } template -inline void LoadSampleArray(float *RESTRICT dst, const void *src, const size_t srcstep, - const size_t samples) noexcept +inline void LoadSampleArray(const al::span dst, const void *src, const size_t channel, + const size_t srcstep) noexcept { - const DevFmtType_t *ssrc = static_cast*>(src); - for(size_t i{0u};i < samples;i++) - dst[i] = LoadSample(ssrc[i*srcstep]); + assert(channel < srcstep); + const auto srcspan = al::span{static_cast*>(src), dst.size()*srcstep}; + auto ssrc = srcspan.cbegin(); + std::generate(dst.begin(), dst.end(), [&ssrc,channel,srcstep] + { + const float ret{LoadSample(ssrc[channel])}; + ssrc += ptrdiff_t(srcstep); + return ret; + }); } -void LoadSamples(float *dst, const void *src, const size_t srcstep, const DevFmtType srctype, - const size_t samples) noexcept +void LoadSamples(const al::span dst, const void *src, const size_t channel, + const size_t srcstep, const DevFmtType srctype) noexcept { #define HANDLE_FMT(T) \ - case T: LoadSampleArray(dst, src, srcstep, samples); break + case T: LoadSampleArray(dst, src, channel, srcstep); break switch(srctype) { HANDLE_FMT(DevFmtByte); @@ -81,11 +84,11 @@ inline DevFmtType_t StoreSample(float) noexcept; template<> inline float StoreSample(float val) noexcept { return val; } template<> inline int32_t StoreSample(float val) noexcept -{ return fastf2i(clampf(val*2147483648.0f, -2147483648.0f, 2147483520.0f)); } +{ return fastf2i(std::clamp(val*2147483648.0f, -2147483648.0f, 2147483520.0f)); } template<> inline int16_t StoreSample(float val) noexcept -{ return static_cast(fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f))); } +{ return static_cast(fastf2i(std::clamp(val*32768.0f, -32768.0f, 32767.0f))); } template<> inline int8_t StoreSample(float val) noexcept -{ return static_cast(fastf2i(clampf(val*128.0f, -128.0f, 127.0f))); } +{ return static_cast(fastf2i(std::clamp(val*128.0f, -128.0f, 127.0f))); } /* Define unsigned output variations. */ template<> inline uint32_t StoreSample(float val) noexcept @@ -96,20 +99,25 @@ template<> inline uint8_t StoreSample(float val) noexcept { return static_cast(StoreSample(val) + 128); } template -inline void StoreSampleArray(void *dst, const float *RESTRICT src, const size_t dststep, - const size_t samples) noexcept +inline void StoreSampleArray(void *dst, const al::span src, const size_t channel, + const size_t dststep) noexcept { - DevFmtType_t *sdst = static_cast*>(dst); - for(size_t i{0u};i < samples;i++) - sdst[i*dststep] = StoreSample(src[i]); + assert(channel < dststep); + const auto dstspan = al::span{static_cast*>(dst), src.size()*dststep}; + auto sdst = dstspan.begin(); + std::for_each(src.cbegin(), src.cend(), [&sdst,channel,dststep](const float in) + { + sdst[channel] = StoreSample(in); + sdst += ptrdiff_t(dststep); + }); } -void StoreSamples(void *dst, const float *src, const size_t dststep, const DevFmtType dsttype, - const size_t samples) noexcept +void StoreSamples(void *dst, const al::span src, const size_t channel, + const size_t dststep, const DevFmtType dsttype) noexcept { #define HANDLE_FMT(T) \ - case T: StoreSampleArray(dst, src, dststep, samples); break + case T: StoreSampleArray(dst, src, channel, dststep); break switch(dsttype) { HANDLE_FMT(DevFmtByte); @@ -125,30 +133,35 @@ void StoreSamples(void *dst, const float *src, const size_t dststep, const DevFm template -void Mono2Stereo(float *RESTRICT dst, const void *src, const size_t frames) noexcept +void Mono2Stereo(const al::span dst, const void *src) noexcept { - const DevFmtType_t *ssrc = static_cast*>(src); - for(size_t i{0u};i < frames;i++) - dst[i*2 + 1] = dst[i*2 + 0] = LoadSample(ssrc[i]) * 0.707106781187f; + const auto srcspan = al::span{static_cast*>(src), dst.size()>>1}; + auto sdst = dst.begin(); + std::for_each(srcspan.cbegin(), srcspan.cend(), [&sdst](const auto in) + { sdst = std::fill_n(sdst, 2, LoadSample(in)*0.707106781187f); }); } template -void Multi2Mono(uint chanmask, const size_t step, const float scale, float *RESTRICT dst, - const void *src, const size_t frames) noexcept +void Multi2Mono(uint chanmask, const size_t step, const float scale, const al::span dst, + const void *src) noexcept { - const DevFmtType_t *ssrc = static_cast*>(src); - std::fill_n(dst, frames, 0.0f); + const auto srcspan = al::span{static_cast*>(src), step*dst.size()}; + std::fill_n(dst.begin(), dst.size(), 0.0f); for(size_t c{0};chanmask;++c) { if((chanmask&1)) LIKELY { - for(size_t i{0u};i < frames;i++) - dst[i] += LoadSample(ssrc[i*step + c]); + auto ssrc = srcspan.cbegin(); + std::for_each(dst.begin(), dst.end(), [&ssrc,step,c](float &sample) + { + const float s{LoadSample(ssrc[c])}; + ssrc += ptrdiff_t(step); + sample += s; + }); } chanmask >>= 1; } - for(size_t i{0u};i < frames;i++) - dst[i] *= scale; + std::for_each(dst.begin(), dst.end(), [scale](float &sample) noexcept { sample *= scale; }); } } // namespace @@ -156,10 +169,11 @@ void Multi2Mono(uint chanmask, const size_t step, const float scale, float *REST SampleConverterPtr SampleConverter::Create(DevFmtType srcType, DevFmtType dstType, size_t numchans, uint srcRate, uint dstRate, Resampler resampler) { + SampleConverterPtr converter; if(numchans < 1 || srcRate < 1 || dstRate < 1) - return nullptr; + return converter; - SampleConverterPtr converter{new(FamCount(numchans)) SampleConverter{numchans}}; + converter = SampleConverterPtr{new(FamCount(numchans)) SampleConverter{numchans}}; converter->mSrcType = srcType; converter->mDstType = dstType; converter->mSrcTypeSize = BytesFromDevFmt(srcType); @@ -168,19 +182,19 @@ SampleConverterPtr SampleConverter::Create(DevFmtType srcType, DevFmtType dstTyp converter->mSrcPrepCount = MaxResamplerPadding; converter->mFracOffset = 0; for(auto &chan : converter->mChan) - { - const al::span buffer{chan.PrevSamples}; - std::fill(buffer.begin(), buffer.end(), 0.0f); - } + chan.PrevSamples.fill(0.0f); /* Have to set the mixer FPU mode since that's what the resampler code expects. */ FPUCtl mixer_mode{}; - auto step = static_cast( - mind(srcRate*double{MixerFracOne}/dstRate + 0.5, MaxPitch*MixerFracOne)); - converter->mIncrement = maxu(step, 1); + const auto step = std::min(std::round(srcRate*double{MixerFracOne}/dstRate), + MaxPitch*double{MixerFracOne}); + converter->mIncrement = std::max(static_cast(step), 1u); if(converter->mIncrement == MixerFracOne) - converter->mResample = [](const InterpState*, const float *RESTRICT src, uint, const uint, - const al::span dst) { std::copy_n(src, dst.size(), dst.begin()); }; + { + converter->mResample = [](const InterpState*, const al::span src, uint, + const uint, const al::span dst) + { std::copy_n(src.begin()+MaxResamplerEdge, dst.size(), dst.begin()); }; + } else converter->mResample = PrepareResampler(resampler, converter->mIncrement, &converter->mState); @@ -210,24 +224,25 @@ uint SampleConverter::availableOut(uint srcframes) const DataSize64 -= mFracOffset; /* If we have a full prep, we can generate at least one sample. */ - return static_cast(clampu64((DataSize64 + mIncrement-1)/mIncrement, 1, - std::numeric_limits::max())); + return static_cast(std::clamp((DataSize64 + mIncrement-1)/mIncrement, 1_u64, + uint64_t{std::numeric_limits::max()})); } uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint dstframes) { - const uint SrcFrameSize{static_cast(mChan.size()) * mSrcTypeSize}; - const uint DstFrameSize{static_cast(mChan.size()) * mDstTypeSize}; + const size_t SrcFrameSize{mChan.size() * mSrcTypeSize}; + const size_t DstFrameSize{mChan.size() * mDstTypeSize}; const uint increment{mIncrement}; - auto SamplesIn = static_cast(*src); uint NumSrcSamples{*srcframes}; + auto SamplesIn = al::span{static_cast(*src), NumSrcSamples*SrcFrameSize}; + auto SamplesOut = al::span{static_cast(dst), dstframes*DstFrameSize}; FPUCtl mixer_mode{}; uint pos{0}; while(pos < dstframes && NumSrcSamples > 0) { const uint prepcount{mSrcPrepCount}; - const uint readable{minu(NumSrcSamples, BufferLineSize - prepcount)}; + const uint readable{std::min(NumSrcSamples, uint{BufferLineSize} - prepcount)}; if(prepcount < MaxResamplerPadding && MaxResamplerPadding-prepcount >= readable) { @@ -235,16 +250,16 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint * what we're given for later. */ for(size_t chan{0u};chan < mChan.size();chan++) - LoadSamples(&mChan[chan].PrevSamples[prepcount], SamplesIn + mSrcTypeSize*chan, - mChan.size(), mSrcType, readable); + LoadSamples(al::span{mChan[chan].PrevSamples}.subspan(prepcount, readable), + SamplesIn.data(), chan, mChan.size(), mSrcType); mSrcPrepCount = prepcount + readable; NumSrcSamples = 0; break; } - float *RESTRICT SrcData{mSrcSamples}; - float *RESTRICT DstData{mDstSamples}; + const auto SrcData = al::span{mSrcSamples}; + const auto DstData = al::span{mDstSamples}; uint DataPosFrac{mFracOffset}; uint64_t DataSize64{prepcount}; DataSize64 += readable; @@ -253,39 +268,36 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint DataSize64 -= DataPosFrac; /* If we have a full prep, we can generate at least one sample. */ - auto DstSize = static_cast( - clampu64((DataSize64 + increment-1)/increment, 1, BufferLineSize)); - DstSize = minu(DstSize, dstframes-pos); + auto DstSize = static_cast(std::clamp((DataSize64 + increment-1)/increment, 1_u64, + uint64_t{BufferLineSize})); + DstSize = std::min(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)}; + const uint nextprep{std::min(prepcount+readable-SrcDataEnd, MaxResamplerPadding)}; for(size_t chan{0u};chan < mChan.size();chan++) { - const std::byte *SrcSamples{SamplesIn + mSrcTypeSize*chan}; - std::byte *DstSamples = static_cast(dst) + mDstTypeSize*chan; - /* Load the previous samples into the source data first, then the * new samples from the input buffer. */ - std::copy_n(mChan[chan].PrevSamples, prepcount, SrcData); - LoadSamples(SrcData + prepcount, SrcSamples, mChan.size(), mSrcType, readable); + std::copy_n(mChan[chan].PrevSamples.cbegin(), prepcount, SrcData.begin()); + LoadSamples(SrcData.subspan(prepcount, readable), SamplesIn.data(), chan, mChan.size(), + mSrcType); /* 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); + auto previter = std::copy_n(SrcData.begin()+ptrdiff_t(SrcDataEnd), nextprep, + mChan[chan].PrevSamples.begin()); + std::fill(previter, mChan[chan].PrevSamples.end(), 0.0f); /* Now resample, and store the result in the output buffer. */ - mResample(&mState, SrcData+MaxResamplerEdge, DataPosFrac, increment, - {DstData, DstSize}); + mResample(&mState, SrcData, DataPosFrac, increment, DstData.first(DstSize)); - StoreSamples(DstSamples, DstData, mChan.size(), mDstType, DstSize); + StoreSamples(SamplesOut.data(), DstData.first(DstSize), chan, mChan.size(), mDstType); } /* Update the number of prep samples still available, as well as the @@ -295,22 +307,24 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint 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)}; - SamplesIn += SrcFrameSize*srcread; + const uint srcread{std::min(NumSrcSamples, SrcDataEnd + mSrcPrepCount - prepcount)}; + SamplesIn = SamplesIn.subspan(SrcFrameSize*srcread); NumSrcSamples -= srcread; - dst = static_cast(dst) + DstFrameSize*DstSize; + SamplesOut = SamplesOut.subspan(DstFrameSize*DstSize); pos += DstSize; } - *src = SamplesIn; + *src = SamplesIn.data(); *srcframes = NumSrcSamples; return pos; } -uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void **dst, uint dstframes) +uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void *const*dst, uint dstframes) { + const auto srcs = al::span{src, mChan.size()}; + const auto dsts = al::span{dst, mChan.size()}; const uint increment{mIncrement}; uint NumSrcSamples{*srcframes}; @@ -319,7 +333,7 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void **ds while(pos < dstframes && NumSrcSamples > 0) { const uint prepcount{mSrcPrepCount}; - const uint readable{minu(NumSrcSamples, BufferLineSize - prepcount)}; + const uint readable{std::min(NumSrcSamples, uint{BufferLineSize} - prepcount)}; if(prepcount < MaxResamplerPadding && MaxResamplerPadding-prepcount >= readable) { @@ -328,9 +342,11 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void **ds */ for(size_t chan{0u};chan < mChan.size();chan++) { - LoadSamples(&mChan[chan].PrevSamples[prepcount], - static_cast(src[chan]), 1, mSrcType, readable); - src[chan] = static_cast(src[chan]) + mSrcTypeSize*readable; + auto samples = al::span{static_cast(srcs[chan]), + NumSrcSamples*size_t{mSrcTypeSize}}; + LoadSamples(al::span{mChan[chan].PrevSamples}.subspan(prepcount, readable), + samples.data(), 0, 1, mSrcType); + srcs[chan] = samples.subspan(size_t{mSrcTypeSize}*readable).data(); } mSrcPrepCount = prepcount + readable; @@ -338,8 +354,8 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void **ds break; } - float *RESTRICT SrcData{mSrcSamples}; - float *RESTRICT DstData{mDstSamples}; + const auto SrcData = al::span{mSrcSamples}; + const auto DstData = al::span{mDstSamples}; uint DataPosFrac{mFracOffset}; uint64_t DataSize64{prepcount}; DataSize64 += readable; @@ -348,37 +364,37 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void **ds DataSize64 -= DataPosFrac; /* If we have a full prep, we can generate at least one sample. */ - auto DstSize = static_cast( - clampu64((DataSize64 + increment-1)/increment, 1, BufferLineSize)); - DstSize = minu(DstSize, dstframes-pos); + auto DstSize = static_cast(std::clamp((DataSize64 + increment-1)/increment, 1_u64, + uint64_t{BufferLineSize})); + DstSize = std::min(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)}; + const uint nextprep{std::min(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); + auto srciter = std::copy_n(mChan[chan].PrevSamples.cbegin(),prepcount,SrcData.begin()); + LoadSamples({srciter, readable}, srcs[chan], 0, 1, mSrcType); /* 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); + auto previter = std::copy_n(SrcData.begin()+ptrdiff_t(SrcDataEnd), nextprep, + mChan[chan].PrevSamples.begin()); + std::fill(previter, mChan[chan].PrevSamples.end(), 0.0f); /* Now resample, and store the result in the output buffer. */ - mResample(&mState, SrcData+MaxResamplerEdge, DataPosFrac, increment, - {DstData, DstSize}); + mResample(&mState, SrcData, DataPosFrac, increment, DstData.first(DstSize)); - std::byte *DstSamples = static_cast(dst[chan]) + pos*mDstTypeSize; - StoreSamples(DstSamples, DstData, 1, mDstType, DstSize); + auto DstSamples = al::span{static_cast(dsts[chan]), + size_t{mDstTypeSize}*dstframes}.subspan(pos*size_t{mDstTypeSize}); + StoreSamples(DstSamples.data(), DstData.first(DstSize), 0, 1, mDstType); } /* Update the number of prep samples still available, as well as the @@ -388,9 +404,13 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void **ds 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(src[chan]) + mSrcTypeSize*srcread; + const uint srcread{std::min(NumSrcSamples, SrcDataEnd + mSrcPrepCount - prepcount)}; + std::for_each(srcs.begin(), srcs.end(), [this,NumSrcSamples,srcread](const void *&srcref) + { + auto srcspan = al::span{static_cast(srcref), + size_t{mSrcTypeSize}*NumSrcSamples}; + srcref = srcspan.subspan(size_t{mSrcTypeSize}*srcread).data(); + }); NumSrcSamples -= srcread; pos += DstSize; @@ -409,7 +429,7 @@ void ChannelConverter::convert(const void *src, float *dst, uint frames) const const float scale{std::sqrt(1.0f / static_cast(al::popcount(mChanMask)))}; switch(mSrcType) { -#define HANDLE_FMT(T) case T: Multi2Mono(mChanMask, mSrcStep, scale, dst, src, frames); break +#define HANDLE_FMT(T) case T: Multi2Mono(mChanMask, mSrcStep, scale, {dst, frames}, src); break HANDLE_FMT(DevFmtByte); HANDLE_FMT(DevFmtUByte); HANDLE_FMT(DevFmtShort); @@ -424,7 +444,7 @@ void ChannelConverter::convert(const void *src, float *dst, uint frames) const { switch(mSrcType) { -#define HANDLE_FMT(T) case T: Mono2Stereo(dst, src, frames); break +#define HANDLE_FMT(T) case T: Mono2Stereo({dst, frames*2_uz}, src); break HANDLE_FMT(DevFmtByte); HANDLE_FMT(DevFmtUByte); HANDLE_FMT(DevFmtShort); diff --git a/3rdparty/openal/core/converter.h b/3rdparty/openal/core/converter.h index d811b46b42fe..1c513f552678 100644 --- a/3rdparty/openal/core/converter.h +++ b/3rdparty/openal/core/converter.h @@ -7,7 +7,9 @@ #include "almalloc.h" #include "devformat.h" +#include "flexarray.h" #include "mixer/defs.h" +#include "resampler_limits.h" using uint = unsigned int; @@ -22,25 +24,25 @@ struct SampleConverter { uint mFracOffset{}; uint mIncrement{}; - InterpState mState{}; + InterpState mState; ResamplerFunc mResample{}; - alignas(16) float mSrcSamples[BufferLineSize]{}; - alignas(16) float mDstSamples[BufferLineSize]{}; + alignas(16) FloatBufferLine mSrcSamples{}; + alignas(16) FloatBufferLine mDstSamples{}; struct ChanSamples { - alignas(16) float PrevSamples[MaxResamplerPadding]; + alignas(16) std::array PrevSamples; }; al::FlexArray mChan; 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 **dst, uint dstframes); - uint availableOut(uint srcframes) const; + [[nodiscard]] auto convert(const void **src, uint *srcframes, void *dst, uint dstframes) -> uint; + [[nodiscard]] auto convertPlanar(const void **src, uint *srcframes, void *const*dst, uint dstframes) -> uint; + [[nodiscard]] auto availableOut(uint srcframes) const -> uint; using SampleOffset = std::chrono::duration>; - SampleOffset currentInputDelay() const noexcept + [[nodiscard]] auto currentInputDelay() const noexcept -> SampleOffset { const int64_t prep{int64_t{mSrcPrepCount} - MaxResamplerEdge}; return SampleOffset{(prep< bool { return mChanMask != 0; } void convert(const void *src, float *dst, uint frames) const; }; diff --git a/3rdparty/openal/core/cpu_caps.cpp b/3rdparty/openal/core/cpu_caps.cpp index 1a064cf466a0..ddc318162add 100644 --- a/3rdparty/openal/core/cpu_caps.cpp +++ b/3rdparty/openal/core/cpu_caps.cpp @@ -1,5 +1,6 @@ #include "config.h" +#include "config_simd.h" #include "cpu_caps.h" @@ -111,22 +112,22 @@ std::optional GetCPUInfo() #else /* Assume support for whatever's supported if we can't check for it */ -#if defined(HAVE_SSE4_1) +#if HAVE_SSE4_1 #warning "Assuming SSE 4.1 run-time support!" ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1; -#elif defined(HAVE_SSE3) +#elif HAVE_SSE3 #warning "Assuming SSE 3 run-time support!" ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3; -#elif defined(HAVE_SSE2) +#elif HAVE_SSE2 #warning "Assuming SSE 2 run-time support!" ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2; -#elif defined(HAVE_SSE) +#elif HAVE_SSE #warning "Assuming SSE run-time support!" ret.mCaps |= CPU_CAP_SSE; #endif #endif /* CAN_GET_CPUID */ -#ifdef HAVE_NEON +#if HAVE_NEON #ifdef __ARM_NEON ret.mCaps |= CPU_CAP_NEON; #elif defined(_WIN32) && (defined(_M_ARM) || defined(_M_ARM64)) diff --git a/3rdparty/openal/core/cubic_defs.h b/3rdparty/openal/core/cubic_defs.h index 33751c9743c6..a382081640f9 100644 --- a/3rdparty/openal/core/cubic_defs.h +++ b/3rdparty/openal/core/cubic_defs.h @@ -1,13 +1,15 @@ #ifndef CORE_CUBIC_DEFS_H #define CORE_CUBIC_DEFS_H +#include + /* The number of distinct phase intervals within the cubic filter tables. */ constexpr unsigned int CubicPhaseBits{5}; constexpr unsigned int CubicPhaseCount{1 << CubicPhaseBits}; struct CubicCoefficients { - float mCoeffs[4]; - float mDeltas[4]; + alignas(16) std::array mCoeffs; + alignas(16) std::array mDeltas; }; #endif /* CORE_CUBIC_DEFS_H */ diff --git a/3rdparty/openal/core/cubic_tables.cpp b/3rdparty/openal/core/cubic_tables.cpp index 5e7aafad6386..cf469f9c0ebf 100644 --- a/3rdparty/openal/core/cubic_tables.cpp +++ b/3rdparty/openal/core/cubic_tables.cpp @@ -2,50 +2,120 @@ #include "cubic_tables.h" #include -#include +#include +#include +#include "alnumbers.h" +#include "alnumeric.h" #include "cubic_defs.h" - +/* These gaussian filter tables are inspired by the gaussian-like filter found + * in the SNES. This is based on the public domain code developed by Near, with + * the help of Ryphecha and nocash, from the nesdev.org forums. + * + * + * + * Additional changes were made here, the most obvious being that is has full + * floating-point precision instead of 11-bit fixed-point, but also an offset + * adjustment for the coefficients to better preserve phase. + */ namespace { -struct SplineFilterArray { - alignas(16) std::array mTable{}; +[[nodiscard]] +auto GetCoeff(double idx) noexcept -> double +{ + const double k{0.5 + idx}; + if(k > 512.0) return 0.0; + const double s{ std::sin(al::numbers::pi*1.280/1024 * k)}; + const double t{(std::cos(al::numbers::pi*2.000/1023 * k) - 1.0) * 0.50}; + const double u{(std::cos(al::numbers::pi*4.000/1023 * k) - 1.0) * 0.08}; + return s * (t + u + 1.0) / k; +} + +} // namespace - constexpr SplineFilterArray() +GaussianTable::GaussianTable() +{ + static constexpr double IndexScale{512.0 / double{CubicPhaseCount*2}}; + /* Fill in the main coefficients. */ + for(std::size_t pi{0};pi < CubicPhaseCount;++pi) { - /* Fill in the main coefficients. */ - for(size_t pi{0};pi < CubicPhaseCount;++pi) - { - const double mu{static_cast(pi) / CubicPhaseCount}; - const double mu2{mu*mu}, mu3{mu2*mu}; - mTable[pi].mCoeffs[0] = static_cast(-0.5*mu3 + mu2 + -0.5*mu); - mTable[pi].mCoeffs[1] = static_cast( 1.5*mu3 + -2.5*mu2 + 1.0); - mTable[pi].mCoeffs[2] = static_cast(-1.5*mu3 + 2.0*mu2 + 0.5*mu); - mTable[pi].mCoeffs[3] = static_cast( 0.5*mu3 + -0.5*mu2); - } - - /* Fill in the coefficient deltas. */ - for(size_t pi{0};pi < CubicPhaseCount-1;++pi) - { - mTable[pi].mDeltas[0] = mTable[pi+1].mCoeffs[0] - mTable[pi].mCoeffs[0]; - mTable[pi].mDeltas[1] = mTable[pi+1].mCoeffs[1] - mTable[pi].mCoeffs[1]; - mTable[pi].mDeltas[2] = mTable[pi+1].mCoeffs[2] - mTable[pi].mCoeffs[2]; - mTable[pi].mDeltas[3] = mTable[pi+1].mCoeffs[3] - mTable[pi].mCoeffs[3]; - } - - const size_t pi{CubicPhaseCount - 1}; - mTable[pi].mDeltas[0] = -mTable[pi].mCoeffs[0]; - mTable[pi].mDeltas[1] = -mTable[pi].mCoeffs[1]; - mTable[pi].mDeltas[2] = 1.0f - mTable[pi].mCoeffs[2]; - mTable[pi].mDeltas[3] = -mTable[pi].mCoeffs[3]; + const double coeff0{GetCoeff(static_cast(CubicPhaseCount + pi)*IndexScale)}; + const double coeff1{GetCoeff(static_cast(pi)*IndexScale)}; + const double coeff2{GetCoeff(static_cast(CubicPhaseCount - pi)*IndexScale)}; + const double coeff3{GetCoeff(static_cast(CubicPhaseCount*2_uz-pi)*IndexScale)}; + + const double scale{1.0 / (coeff0 + coeff1 + coeff2 + coeff3)}; + mTable[pi].mCoeffs[0] = static_cast(coeff0 * scale); + mTable[pi].mCoeffs[1] = static_cast(coeff1 * scale); + mTable[pi].mCoeffs[2] = static_cast(coeff2 * scale); + mTable[pi].mCoeffs[3] = static_cast(coeff3 * scale); } - constexpr auto& getTable() const noexcept { return mTable; } -}; + /* Fill in the coefficient deltas. */ + for(std::size_t pi{0};pi < CubicPhaseCount-1;++pi) + { + mTable[pi].mDeltas[0] = mTable[pi+1].mCoeffs[0] - mTable[pi].mCoeffs[0]; + mTable[pi].mDeltas[1] = mTable[pi+1].mCoeffs[1] - mTable[pi].mCoeffs[1]; + mTable[pi].mDeltas[2] = mTable[pi+1].mCoeffs[2] - mTable[pi].mCoeffs[2]; + mTable[pi].mDeltas[3] = mTable[pi+1].mCoeffs[3] - mTable[pi].mCoeffs[3]; + } -constexpr SplineFilterArray SplineFilter{}; + const std::size_t pi{CubicPhaseCount - 1}; + mTable[pi].mDeltas[0] = 0.0f - mTable[pi].mCoeffs[0]; + mTable[pi].mDeltas[1] = mTable[0].mCoeffs[0] - mTable[pi].mCoeffs[1]; + mTable[pi].mDeltas[2] = mTable[0].mCoeffs[1] - mTable[pi].mCoeffs[2]; + mTable[pi].mDeltas[3] = mTable[0].mCoeffs[2] - mTable[pi].mCoeffs[3]; +} -} // namespace +SplineTable::SplineTable() +{ + /* This filter table is based on a Catmull-Rom spline. It retains more of + * the original high-frequency content, at the cost of increased harmonics. + */ + for(std::size_t pi{0};pi < CubicPhaseCount;++pi) + { + const double mu{static_cast(pi) / double{CubicPhaseCount}}; + const double mu2{mu*mu}, mu3{mu2*mu}; + mTable[pi].mCoeffs[0] = static_cast(-0.5*mu3 + mu2 + -0.5*mu); + mTable[pi].mCoeffs[1] = static_cast( 1.5*mu3 + -2.5*mu2 + 1.0); + mTable[pi].mCoeffs[2] = static_cast(-1.5*mu3 + 2.0*mu2 + 0.5*mu); + mTable[pi].mCoeffs[3] = static_cast( 0.5*mu3 + -0.5*mu2); + } -const CubicTable gCubicSpline{SplineFilter.getTable()}; + for(std::size_t pi{0};pi < CubicPhaseCount-1;++pi) + { + mTable[pi].mDeltas[0] = mTable[pi+1].mCoeffs[0] - mTable[pi].mCoeffs[0]; + mTable[pi].mDeltas[1] = mTable[pi+1].mCoeffs[1] - mTable[pi].mCoeffs[1]; + mTable[pi].mDeltas[2] = mTable[pi+1].mCoeffs[2] - mTable[pi].mCoeffs[2]; + mTable[pi].mDeltas[3] = mTable[pi+1].mCoeffs[3] - mTable[pi].mCoeffs[3]; + } + + const std::size_t pi{CubicPhaseCount - 1}; + mTable[pi].mDeltas[0] = 0.0f - mTable[pi].mCoeffs[0]; + mTable[pi].mDeltas[1] = mTable[0].mCoeffs[0] - mTable[pi].mCoeffs[1]; + mTable[pi].mDeltas[2] = mTable[0].mCoeffs[1] - mTable[pi].mCoeffs[2]; + mTable[pi].mDeltas[3] = mTable[0].mCoeffs[2] - mTable[pi].mCoeffs[3]; +} + + +CubicFilter::CubicFilter() +{ + static constexpr double IndexScale{512.0 / double{sTableSteps*2}}; + /* Only half the coefficients need to be iterated here, since Coeff2 and + * Coeff3 are just Coeff1 and Coeff0 in reverse respectively. + */ + for(size_t i{0};i < sTableSteps/2 + 1;++i) + { + const double coeff0{GetCoeff(static_cast(sTableSteps + i)*IndexScale)}; + const double coeff1{GetCoeff(static_cast(i)*IndexScale)}; + const double coeff2{GetCoeff(static_cast(sTableSteps - i)*IndexScale)}; + const double coeff3{GetCoeff(static_cast(sTableSteps*2_uz - i)*IndexScale)}; + + const double scale{1.0 / (coeff0 + coeff1 + coeff2 + coeff3)}; + mFilter[sTableSteps + i] = static_cast(coeff0 * scale); + mFilter[i] = static_cast(coeff1 * scale); + mFilter[sTableSteps - i] = static_cast(coeff2 * scale); + mFilter[sTableSteps*2 - i] = static_cast(coeff3 * scale); + } +} diff --git a/3rdparty/openal/core/cubic_tables.h b/3rdparty/openal/core/cubic_tables.h index 88097ae2f153..b32764451737 100644 --- a/3rdparty/openal/core/cubic_tables.h +++ b/3rdparty/openal/core/cubic_tables.h @@ -1,17 +1,42 @@ #ifndef CORE_CUBIC_TABLES_H #define CORE_CUBIC_TABLES_H -#include "alspan.h" +#include +#include + #include "cubic_defs.h" +#include "opthelpers.h" -struct CubicTable { - al::span Tab; +struct SIMDALIGN CubicTable { + std::array mTable{}; }; -/* A Catmull-Rom spline. The spline passes through the center two samples, - * ensuring no discontinuity while moving through a series of samples. - */ -extern const CubicTable gCubicSpline; +struct GaussianTable : CubicTable { GaussianTable(); }; +inline const GaussianTable gGaussianFilter; + +struct SplineTable : CubicTable { SplineTable(); }; +inline const SplineTable gSplineFilter; + + +struct CubicFilter { + static constexpr std::size_t sTableBits{8}; + static constexpr std::size_t sTableSteps{1 << sTableBits}; + static constexpr std::size_t sTableMask{sTableSteps - 1}; + + std::array mFilter{}; + + CubicFilter(); + + [[nodiscard]] constexpr + auto getCoeff0(std::size_t i) const noexcept -> float { return mFilter[sTableSteps+i]; } + [[nodiscard]] constexpr + auto getCoeff1(std::size_t i) const noexcept -> float { return mFilter[i]; } + [[nodiscard]] constexpr + auto getCoeff2(std::size_t i) const noexcept -> float { return mFilter[sTableSteps-i]; } + [[nodiscard]] constexpr + auto getCoeff3(std::size_t i) const noexcept -> float { return mFilter[sTableSteps*2-i]; } +}; +inline const CubicFilter gCubicTable; #endif /* CORE_CUBIC_TABLES_H */ diff --git a/3rdparty/openal/core/dbus_wrap.cpp b/3rdparty/openal/core/dbus_wrap.cpp index 4841956686b6..05d9fc0638a8 100644 --- a/3rdparty/openal/core/dbus_wrap.cpp +++ b/3rdparty/openal/core/dbus_wrap.cpp @@ -8,16 +8,22 @@ #include #include -#include "albit.h" #include "logging.h" void PrepareDBus() { - static constexpr char libname[] = "libdbus-1.so.3"; + const char *libname{"libdbus-1.so.3"}; + + dbus_handle = LoadLib(libname); + if(!dbus_handle) + { + WARN("Failed to load %s\n", libname); + return; + } auto load_func = [](auto &f, const char *name) -> void - { f = al::bit_cast>(GetSymbol(dbus_handle, name)); }; + { f = reinterpret_cast>(GetSymbol(dbus_handle, name)); }; #define LOAD_FUNC(x) do { \ load_func(p##x, #x); \ if(!p##x) \ @@ -29,14 +35,8 @@ void PrepareDBus() } \ } while(0); - dbus_handle = LoadLib(libname); - if(!dbus_handle) - { - WARN("Failed to load %s\n", libname); - return; - } + DBUS_FUNCTIONS(LOAD_FUNC) -DBUS_FUNCTIONS(LOAD_FUNC) #undef LOAD_FUNC } #endif diff --git a/3rdparty/openal/core/dbus_wrap.h b/3rdparty/openal/core/dbus_wrap.h index 65f08942409e..afc27d8429f8 100644 --- a/3rdparty/openal/core/dbus_wrap.h +++ b/3rdparty/openal/core/dbus_wrap.h @@ -70,9 +70,16 @@ namespace dbus { struct Error { Error() { dbus_error_init(&mError); } + Error(const Error&) = delete; + Error(Error&&) = delete; ~Error() { dbus_error_free(&mError); } + + void operator=(const Error&) = delete; + void operator=(Error&&) = delete; + DBusError* operator->() { return &mError; } DBusError &get() { return mError; } + private: DBusError mError{}; }; diff --git a/3rdparty/openal/core/devformat.cpp b/3rdparty/openal/core/devformat.cpp index acdabc4f1f43..57d1d8ea7623 100644 --- a/3rdparty/openal/core/devformat.cpp +++ b/3rdparty/openal/core/devformat.cpp @@ -29,6 +29,7 @@ uint ChannelsFromDevFmt(DevFmtChannels chans, uint ambiorder) noexcept case DevFmtX61: return 7; case DevFmtX71: return 8; case DevFmtX714: return 12; + case DevFmtX7144: return 16; case DevFmtX3D71: return 8; case DevFmtAmbi3D: return (ambiorder+1) * (ambiorder+1); } @@ -60,6 +61,7 @@ const char *DevFmtChannelsString(DevFmtChannels chans) noexcept case DevFmtX61: return "6.1 Surround"; case DevFmtX71: return "7.1 Surround"; case DevFmtX714: return "7.1.4 Surround"; + case DevFmtX7144: return "7.1.4.4 Surround"; case DevFmtX3D71: return "3D7.1 Surround"; case DevFmtAmbi3D: return "Ambisonic 3D"; } diff --git a/3rdparty/openal/core/devformat.h b/3rdparty/openal/core/devformat.h index 485826a397e7..ca59df5e55f1 100644 --- a/3rdparty/openal/core/devformat.h +++ b/3rdparty/openal/core/devformat.h @@ -2,6 +2,7 @@ #define CORE_DEVFORMAT_H #include +#include using uint = unsigned int; @@ -25,6 +26,11 @@ enum Channel : unsigned char { TopBackCenter, TopBackRight, + BottomFrontLeft, + BottomFrontRight, + BottomBackLeft, + BottomBackRight, + Aux0, Aux1, Aux2, @@ -66,12 +72,13 @@ enum DevFmtChannels : unsigned char { DevFmtX61, DevFmtX71, DevFmtX714, + DevFmtX7144, DevFmtX3D71, DevFmtAmbi3D, DevFmtChannelsDefault = DevFmtStereo }; -#define MAX_OUTPUT_CHANNELS 16 +inline constexpr std::size_t MaxOutputChannels{16}; /* DevFmtType traits, providing the type, etc given a DevFmtType. */ template diff --git a/3rdparty/openal/core/device.cpp b/3rdparty/openal/core/device.cpp index 2766c5e4ea7e..4c2dc2a38864 100644 --- a/3rdparty/openal/core/device.cpp +++ b/3rdparty/openal/core/device.cpp @@ -9,15 +9,9 @@ #include "mastering.h" -al::FlexArray DeviceBase::sEmptyContextArray{0u}; - - -DeviceBase::DeviceBase(DeviceType type) : Type{type}, mContexts{&sEmptyContextArray} +DeviceBase::DeviceBase(DeviceType type) + : Type{type}, mContexts{al::FlexArray::Create(0)} { } -DeviceBase::~DeviceBase() -{ - auto *oldarray = mContexts.exchange(nullptr, std::memory_order_relaxed); - if(oldarray != &sEmptyContextArray) delete oldarray; -} +DeviceBase::~DeviceBase() = default; diff --git a/3rdparty/openal/core/device.h b/3rdparty/openal/core/device.h index b088e130ef04..1c96293e5237 100644 --- a/3rdparty/openal/core/device.h +++ b/3rdparty/openal/core/device.h @@ -5,9 +5,9 @@ #include #include #include +#include +#include #include -#include -#include #include #include "almalloc.h" @@ -17,6 +17,7 @@ #include "bufferline.h" #include "devformat.h" #include "filters/nfc.h" +#include "flexarray.h" #include "intrusive_ptr.h" #include "mixer/hrtfdefs.h" #include "opthelpers.h" @@ -25,8 +26,10 @@ #include "vector.h" class BFormatDec; +namespace Bs2b { struct bs2b; -struct Compressor; +} // namespace Bs2b +class Compressor; struct ContextBase; struct DirectHrtfState; struct HrtfStore; @@ -34,28 +37,28 @@ struct HrtfStore; using uint = unsigned int; -#define MIN_OUTPUT_RATE 8000 -#define MAX_OUTPUT_RATE 192000 -#define DEFAULT_OUTPUT_RATE 48000 +inline constexpr std::size_t MinOutputRate{8000}; +inline constexpr std::size_t MaxOutputRate{192000}; +inline constexpr std::size_t DefaultOutputRate{48000}; -#define DEFAULT_UPDATE_SIZE 960 /* 20ms */ -#define DEFAULT_NUM_UPDATES 3 +inline constexpr std::size_t DefaultUpdateSize{960}; /* 20ms */ +inline constexpr std::size_t DefaultNumUpdates{3}; -enum class DeviceType : uint8_t { +enum class DeviceType : std::uint8_t { Playback, Capture, Loopback }; -enum class RenderMode : uint8_t { +enum class RenderMode : std::uint8_t { Normal, Pairwise, Hrtf }; -enum class StereoEncoding : uint8_t { +enum class StereoEncoding : std::uint8_t { Basic, Uhj, Hrtf, @@ -77,24 +80,23 @@ struct DistanceComp { static constexpr uint MaxDelay{1024}; struct ChanData { + al::span Buffer; /* Valid size is [0...MaxDelay). */ float Gain{1.0f}; - uint Length{0u}; /* Valid range is [0...MaxDelay). */ - float *Buffer{nullptr}; }; - std::array mChannels; + std::array mChannels; al::FlexArray mSamples; - DistanceComp(size_t count) : mSamples{count} { } + DistanceComp(std::size_t count) : mSamples{count} { } - static std::unique_ptr Create(size_t numsamples) + static std::unique_ptr Create(std::size_t numsamples) { return std::unique_ptr{new(FamCount(numsamples)) DistanceComp{numsamples}}; } DEF_FAM_NEWDEL(DistanceComp, mSamples) }; -constexpr uint InvalidChannelIndex{~0u}; +constexpr auto InvalidChannelIndex = static_cast(~0u); struct BFChannelConfig { float Scale; @@ -112,24 +114,24 @@ 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 void setAmbiMixParams(const MixParams &inmix, const float gainbase, F func) const { - const size_t numIn{inmix.Buffer.size()}; - const size_t numOut{Buffer.size()}; - for(size_t i{0};i < numIn;++i) + const std::size_t numIn{inmix.Buffer.size()}; + const std::size_t numOut{Buffer.size()}; + for(std::size_t i{0};i < numIn;++i) { - auto idx = InvalidChannelIndex; - auto gain = 0.0f; + std::uint8_t idx{InvalidChannelIndex}; + float gain{0.0f}; - for(size_t j{0};j < numOut;++j) + for(std::size_t j{0};j < numOut;++j) { if(AmbiMap[j].Index == inmix.AmbiMap[i].Index) { - idx = static_cast(j); + idx = static_cast(j); gain = AmbiMap[j].Scale * gainbase; break; } @@ -141,7 +143,7 @@ struct MixParams { struct RealMixParams { al::span RemixMap; - std::array ChannelIndex{}; + std::array ChannelIndex{}; al::span Buffer; }; @@ -158,8 +160,6 @@ enum { // Specifies if the DSP is paused at user request DevicePaused, - // Specifies if the device is currently running - DeviceRunning, // Specifies if the output plays directly on/in ears (headphones, headset, // ear buds, etc). @@ -173,15 +173,18 @@ enum { DeviceFlagsCount }; -struct DeviceBase { - /* To avoid extraneous allocations, a 0-sized FlexArray is - * defined globally as a sharable object. - */ - static al::FlexArray sEmptyContextArray; +enum class DeviceState : std::uint8_t { + Unprepared, + Configured, + Playing +}; +struct SIMDALIGN DeviceBase { std::atomic Connected{true}; const DeviceType Type{}; + std::string mDeviceName; + uint Frequency{}; uint UpdateSize{}; uint BufferSize{}; @@ -198,10 +201,9 @@ struct DeviceBase { DevAmbiLayout mAmbiLayout{DevAmbiLayout::Default}; DevAmbiScaling mAmbiScale{DevAmbiScaling::Default}; - std::string DeviceName; - // Device flags - std::bitset Flags{}; + std::bitset Flags; + DeviceState mDeviceState{DeviceState::Unprepared}; uint NumAuxSends{}; @@ -218,35 +220,36 @@ struct DeviceBase { */ NfcFilter mNFCtrlFilter{}; - uint SamplesDone{0u}; - std::chrono::nanoseconds ClockBase{0}; + using seconds32 = std::chrono::duration; + using nanoseconds32 = std::chrono::duration; + + std::atomic mSamplesDone{0u}; + /* Split the clock to avoid a 64-bit atomic for certain 32-bit targets. */ + std::atomic mClockBaseSec{seconds32{}}; + std::atomic mClockBaseNSec{nanoseconds32{}}; std::chrono::nanoseconds FixedLatency{0}; AmbiRotateMatrix mAmbiRotateMatrix{}; AmbiRotateMatrix mAmbiRotateMatrix2{}; /* Temp storage used for mixer processing. */ - static constexpr size_t MixerLineSize{BufferLineSize + DecoderBase::sMaxPadding}; - static constexpr size_t MixerChannelsMax{16}; - using MixerBufferLine = std::array; - alignas(16) std::array mSampleData; - alignas(16) std::array mResampleData; - - alignas(16) float FilteredData[BufferLineSize]; - union { - alignas(16) float HrtfSourceData[BufferLineSize + HrtfHistoryLength]; - alignas(16) float NfcSampleData[BufferLineSize]; - }; + static constexpr std::size_t MixerLineSize{BufferLineSize + DecoderBase::sMaxPadding}; + static constexpr std::size_t MixerChannelsMax{16}; + alignas(16) std::array mSampleData{}; + alignas(16) std::array mResampleData{}; + + alignas(16) std::array FilteredData{}; + alignas(16) std::array ExtraSampleData{}; /* Persistent storage for HRTF mixing. */ - alignas(16) float2 HrtfAccumData[BufferLineSize + HrirLength]; + alignas(16) std::array HrtfAccumData{}; /* Mixing buffer used by the Dry mix and Real output. */ al::vector MixBuffer; /* The "dry" path corresponds to the main output. */ MixParams Dry; - uint NumChannelsPerOrder[MaxAmbiOrder+1]{}; + std::array NumChannelsPerOrder{}; /* "Real" output, which will be written to the device buffer. May alias the * dry buffer. @@ -265,7 +268,7 @@ struct DeviceBase { std::unique_ptr AmbiDecoder; /* Stereo-to-binaural filter */ - std::unique_ptr Bs2b; + std::unique_ptr Bs2b; using PostProc = void(DeviceBase::*)(const size_t SamplesToDo); PostProc PostProcess{nullptr}; @@ -284,44 +287,75 @@ struct DeviceBase { * the end, so the bottom bit indicates if the device is currently mixing * and the upper bits indicates how many mixes have been done. */ - RefCount MixCount{0u}; + std::atomic mMixCount{0u}; // Contexts created on this device - std::atomic*> mContexts{nullptr}; + al::atomic_unique_ptr> mContexts; - DeviceBase(DeviceType type); - DeviceBase(const DeviceBase&) = delete; - DeviceBase& operator=(const DeviceBase&) = delete; - ~DeviceBase(); + [[nodiscard]] auto bytesFromFmt() const noexcept -> uint { return BytesFromDevFmt(FmtType); } + [[nodiscard]] auto channelsFromFmt() const noexcept -> uint { return ChannelsFromDevFmt(FmtChans, mAmbiOrder); } + [[nodiscard]] auto frameSizeFromFmt() const noexcept -> uint { return bytesFromFmt() * channelsFromFmt(); } - uint bytesFromFmt() const noexcept { return BytesFromDevFmt(FmtType); } - uint channelsFromFmt() const noexcept { return ChannelsFromDevFmt(FmtChans, mAmbiOrder); } - uint frameSizeFromFmt() const noexcept { return bytesFromFmt() * channelsFromFmt(); } + struct MixLock { + DeviceBase *const self; + const uint mEndVal; - uint waitForMix() const noexcept + MixLock(DeviceBase *device, const uint endval) noexcept : self{device}, mEndVal{endval} { } + MixLock(const MixLock&) = delete; + void operator=(const MixLock&) = delete; + /* Update the mix count when the lock goes out of scope to "release" it + * (lsb should be 0). + */ + ~MixLock() { self->mMixCount.store(mEndVal, std::memory_order_release); } + }; + auto getWriteMixLock() noexcept -> MixLock { - uint refcount; - while((refcount=MixCount.load(std::memory_order_acquire))&1) { - } + /* Increment the mix count at the start of mixing and writing clock + * info (lsb should be 1). + */ + const auto oldCount = mMixCount.fetch_add(1u, std::memory_order_acq_rel); + return MixLock{this, oldCount+2}; + } + + /** Waits for the mixer to not be mixing or updating the clock. */ + [[nodiscard]] auto waitForMix() const noexcept -> uint + { + uint refcount{mMixCount.load(std::memory_order_acquire)}; + while((refcount&1)) refcount = mMixCount.load(std::memory_order_acquire); return refcount; } - void ProcessHrtf(const size_t SamplesToDo); - void ProcessAmbiDec(const size_t SamplesToDo); - void ProcessAmbiDecStablized(const size_t SamplesToDo); - void ProcessUhj(const size_t SamplesToDo); - void ProcessBs2b(const size_t SamplesToDo); + /** + * Helper to get the current clock time from the device's ClockBase, and + * SamplesDone converted from the sample rate. Should only be called while + * watching the MixCount. + */ + [[nodiscard]] auto getClockTime() const noexcept -> std::chrono::nanoseconds + { + using std::chrono::seconds; + using std::chrono::nanoseconds; + + auto ns = nanoseconds{seconds{mSamplesDone.load(std::memory_order_relaxed)}} / Frequency; + return nanoseconds{mClockBaseNSec.load(std::memory_order_relaxed)} + + mClockBaseSec.load(std::memory_order_relaxed) + ns; + } + + void ProcessHrtf(const std::size_t SamplesToDo); + void ProcessAmbiDec(const std::size_t SamplesToDo); + void ProcessAmbiDecStablized(const std::size_t SamplesToDo); + void ProcessUhj(const std::size_t SamplesToDo); + void ProcessBs2b(const std::size_t SamplesToDo); - inline void postProcess(const size_t SamplesToDo) + void postProcess(const std::size_t SamplesToDo) { if(PostProcess) LIKELY (this->*PostProcess)(SamplesToDo); } - void renderSamples(const al::span outBuffers, const uint numSamples); - void renderSamples(void *outBuffer, const uint numSamples, const size_t frameStep); + void renderSamples(const al::span outBuffers, const uint numSamples); + void renderSamples(void *outBuffer, const uint numSamples, const std::size_t frameStep); /* Caller must lock the device state, and the mixer must not be running. */ -#ifdef __USE_MINGW_ANSI_STDIO - [[gnu::format(gnu_printf,2,3)]] +#ifdef __MINGW32__ + [[gnu::format(__MINGW_PRINTF_FORMAT,2,3)]] #else [[gnu::format(printf,2,3)]] #endif @@ -329,21 +363,29 @@ 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 + [[nodiscard]] auto channelIdxByName(Channel chan) const noexcept -> std::uint8_t { return RealOut.ChannelIndex[chan]; } - DISABLE_ALLOC() - private: uint renderSamples(const uint numSamples); + +protected: + DeviceBase(DeviceType type); + ~DeviceBase(); + +public: + DeviceBase(const DeviceBase&) = delete; + DeviceBase& operator=(const DeviceBase&) = delete; }; /* Must be less than 15 characters (16 including terminating null) for * compatibility with pthread_setname_np limitations. */ -#define MIXER_THREAD_NAME "alsoft-mixer" +[[nodiscard]] constexpr +auto GetMixerThreadName() noexcept -> const char* { return "alsoft-mixer"; } -#define RECORD_THREAD_NAME "alsoft-record" +[[nodiscard]] constexpr +auto GetRecordThreadName() noexcept -> const char* { return "alsoft-record"; } #endif /* CORE_DEVICE_H */ diff --git a/3rdparty/openal/core/effects/base.h b/3rdparty/openal/core/effects/base.h index b02d33b7b1ae..879f83900549 100644 --- a/3rdparty/openal/core/effects/base.h +++ b/3rdparty/openal/core/effects/base.h @@ -1,13 +1,14 @@ #ifndef CORE_EFFECTS_BASE_H #define CORE_EFFECTS_BASE_H -#include +#include +#include +#include -#include "almalloc.h" #include "alspan.h" -#include "atomic.h" #include "core/bufferline.h" #include "intrusive_ptr.h" +#include "opthelpers.h" struct BufferStorage; struct ContextBase; @@ -18,21 +19,21 @@ struct RealMixParams; /** Target gain for the reverb decay feedback reaching the decay time. */ -constexpr float ReverbDecayGain{0.001f}; /* -60 dB */ +inline constexpr float ReverbDecayGain{0.001f}; /* -60 dB */ -constexpr float ReverbMaxReflectionsDelay{0.3f}; -constexpr float ReverbMaxLateReverbDelay{0.1f}; +inline constexpr float ReverbMaxReflectionsDelay{0.3f}; +inline constexpr float ReverbMaxLateReverbDelay{0.1f}; enum class ChorusWaveform { Sinusoid, Triangle }; -constexpr float ChorusMaxDelay{0.016f}; -constexpr float FlangerMaxDelay{0.004f}; +inline constexpr float ChorusMaxDelay{0.016f}; +inline constexpr float FlangerMaxDelay{0.004f}; -constexpr float EchoMaxDelay{0.207f}; -constexpr float EchoMaxLRDelay{0.404f}; +inline constexpr float EchoMaxDelay{0.207f}; +inline constexpr float EchoMaxLRDelay{0.404f}; enum class FShifterDirection { Down, @@ -58,122 +59,142 @@ enum class VMorpherWaveform { Sawtooth }; -union EffectProps { - struct { - float Density; - float Diffusion; - float Gain; - float GainHF; - float GainLF; - float DecayTime; - float DecayHFRatio; - float DecayLFRatio; - float ReflectionsGain; - float ReflectionsDelay; - float ReflectionsPan[3]; - float LateReverbGain; - float LateReverbDelay; - float LateReverbPan[3]; - float EchoTime; - float EchoDepth; - float ModulationTime; - float ModulationDepth; - float AirAbsorptionGainHF; - float HFReference; - float LFReference; - float RoomRolloffFactor; - bool DecayHFLimit; - } Reverb; - - struct { - float AttackTime; - float ReleaseTime; - float Resonance; - float PeakGain; - } Autowah; - - struct { - ChorusWaveform Waveform; - int Phase; - float Rate; - float Depth; - float Feedback; - float Delay; - } Chorus; /* Also Flanger */ - - struct { - bool OnOff; - } Compressor; - - struct { - float Edge; - float Gain; - float LowpassCutoff; - float EQCenter; - float EQBandwidth; - } Distortion; - - struct { - float Delay; - float LRDelay; - - float Damping; - float Feedback; - - float Spread; - } Echo; - - struct { - float LowCutoff; - float LowGain; - float Mid1Center; - float Mid1Gain; - float Mid1Width; - float Mid2Center; - float Mid2Gain; - float Mid2Width; - float HighCutoff; - float HighGain; - } Equalizer; - - struct { - float Frequency; - FShifterDirection LeftDirection; - FShifterDirection RightDirection; - } Fshifter; - - struct { - float Frequency; - float HighPassCutoff; - ModulatorWaveform Waveform; - } Modulator; - - struct { - int CoarseTune; - int FineTune; - } Pshifter; - - struct { - float Rate; - VMorpherPhenome PhonemeA; - VMorpherPhenome PhonemeB; - int PhonemeACoarseTuning; - int PhonemeBCoarseTuning; - VMorpherWaveform Waveform; - } Vmorpher; - - struct { - float Gain; - } Dedicated; +struct ReverbProps { + float Density; + float Diffusion; + float Gain; + float GainHF; + float GainLF; + float DecayTime; + float DecayHFRatio; + float DecayLFRatio; + float ReflectionsGain; + float ReflectionsDelay; + std::array ReflectionsPan; + float LateReverbGain; + float LateReverbDelay; + std::array LateReverbPan; + float EchoTime; + float EchoDepth; + float ModulationTime; + float ModulationDepth; + float AirAbsorptionGainHF; + float HFReference; + float LFReference; + float RoomRolloffFactor; + bool DecayHFLimit; }; +struct AutowahProps { + float AttackTime; + float ReleaseTime; + float Resonance; + float PeakGain; +}; + +struct ChorusProps { + ChorusWaveform Waveform; + int Phase; + float Rate; + float Depth; + float Feedback; + float Delay; +}; + +struct CompressorProps { + bool OnOff; +}; + +struct DistortionProps { + float Edge; + float Gain; + float LowpassCutoff; + float EQCenter; + float EQBandwidth; +}; + +struct EchoProps { + float Delay; + float LRDelay; + + float Damping; + float Feedback; + + float Spread; +}; + +struct EqualizerProps { + float LowCutoff; + float LowGain; + float Mid1Center; + float Mid1Gain; + float Mid1Width; + float Mid2Center; + float Mid2Gain; + float Mid2Width; + float HighCutoff; + float HighGain; +}; + +struct FshifterProps { + float Frequency; + FShifterDirection LeftDirection; + FShifterDirection RightDirection; +}; + +struct ModulatorProps { + float Frequency; + float HighPassCutoff; + ModulatorWaveform Waveform; +}; + +struct PshifterProps { + int CoarseTune; + int FineTune; +}; + +struct VmorpherProps { + float Rate; + VMorpherPhenome PhonemeA; + VMorpherPhenome PhonemeB; + int PhonemeACoarseTuning; + int PhonemeBCoarseTuning; + VMorpherWaveform Waveform; +}; + +struct DedicatedProps { + enum TargetType : bool { Dialog, Lfe }; + TargetType Target; + float Gain; +}; + +struct ConvolutionProps { + std::array OrientAt; + std::array OrientUp; +}; + +using EffectProps = std::variant; + struct EffectTarget { MixParams *Main; RealMixParams *RealOut; }; -struct EffectState : public al::intrusive_ref { +struct SIMDALIGN EffectState : public al::intrusive_ref { al::span mOutTarget; @@ -188,8 +209,14 @@ struct EffectState : public al::intrusive_ref { struct EffectStateFactory { + EffectStateFactory() = default; + EffectStateFactory(const EffectStateFactory&) = delete; + EffectStateFactory(EffectStateFactory&&) = delete; virtual ~EffectStateFactory() = default; + void operator=(const EffectStateFactory&) = delete; + void operator=(EffectStateFactory&&) = delete; + virtual al::intrusive_ptr create() = 0; }; diff --git a/3rdparty/openal/core/effectslot.cpp b/3rdparty/openal/core/effectslot.cpp index db8aa078c223..d07c79a546a0 100644 --- a/3rdparty/openal/core/effectslot.cpp +++ b/3rdparty/openal/core/effectslot.cpp @@ -3,17 +3,13 @@ #include "effectslot.h" -#include +#include #include "almalloc.h" #include "context.h" -EffectSlotArray *EffectSlot::CreatePtrArray(size_t count) noexcept +std::unique_ptr EffectSlot::CreatePtrArray(size_t count) { - /* Allocate space for twice as many pointers, so the mixer has scratch - * space to store a sorted list during mixing. - */ - void *ptr{al_calloc(alignof(EffectSlotArray), EffectSlotArray::Sizeof(count*2))}; - return al::construct_at(static_cast(ptr), count); + return std::unique_ptr{new(FamCount{count}) EffectSlotArray(count)}; } diff --git a/3rdparty/openal/core/effectslot.h b/3rdparty/openal/core/effectslot.h index 2624ae5fd306..77e156efec1f 100644 --- a/3rdparty/openal/core/effectslot.h +++ b/3rdparty/openal/core/effectslot.h @@ -2,10 +2,11 @@ #define CORE_EFFECTSLOT_H #include +#include -#include "almalloc.h" #include "device.h" #include "effects/base.h" +#include "flexarray.h" #include "intrusive_ptr.h" struct EffectSlot; @@ -18,20 +19,18 @@ enum class EffectSlotType : unsigned char { None, Reverb, Chorus, + Autowah, + Compressor, + Convolution, + Dedicated, Distortion, Echo, + Equalizer, Flanger, FrequencyShifter, - VocalMorpher, PitchShifter, RingModulator, - Autowah, - Compressor, - Equalizer, - EAXReverb, - DedicatedLFE, - DedicatedDialog, - Convolution + VocalMorpher, }; struct EffectSlotProps { @@ -45,8 +44,6 @@ struct EffectSlotProps { al::intrusive_ptr State; std::atomic next; - - DEF_NEWDEL(EffectSlotProps) }; @@ -67,7 +64,7 @@ struct EffectSlot { EffectSlot *Target{nullptr}; EffectSlotType EffectType{EffectSlotType::None}; - EffectProps mEffectProps{}; + EffectProps mEffectProps; al::intrusive_ptr mEffectState; float RoomRolloff{0.0f}; /* Added to the source's room rolloff, not multiplied. */ @@ -81,9 +78,7 @@ struct EffectSlot { al::vector mWetBuffer; - static EffectSlotArray *CreatePtrArray(size_t count) noexcept; - - DEF_NEWDEL(EffectSlot) + static std::unique_ptr CreatePtrArray(size_t count); }; #endif /* CORE_EFFECTSLOT_H */ diff --git a/3rdparty/openal/core/except.cpp b/3rdparty/openal/core/except.cpp index 45fd4eb5f7a0..f338e9aeb2df 100644 --- a/3rdparty/openal/core/except.cpp +++ b/3rdparty/openal/core/except.cpp @@ -13,18 +13,20 @@ namespace al { base_exception::~base_exception() = default; -void base_exception::setMessage(const char* msg, std::va_list args) +void base_exception::setMessage(const char *msg, std::va_list args) { + /* NOLINTBEGIN(*-array-to-pointer-decay) */ std::va_list args2; va_copy(args2, args); int msglen{std::vsnprintf(nullptr, 0, msg, args)}; if(msglen > 0) LIKELY { mMessage.resize(static_cast(msglen)+1); - std::vsnprintf(const_cast(mMessage.data()), mMessage.length(), msg, args2); + std::vsnprintf(mMessage.data(), mMessage.length(), msg, args2); mMessage.pop_back(); } va_end(args2); + /* NOLINTEND(*-array-to-pointer-decay) */ } } // namespace al diff --git a/3rdparty/openal/core/except.h b/3rdparty/openal/core/except.h index 0e28e9dfede6..1b3d634fc58f 100644 --- a/3rdparty/openal/core/except.h +++ b/3rdparty/openal/core/except.h @@ -13,19 +13,20 @@ class base_exception : public std::exception { std::string mMessage; protected: + auto setMessage(const char *msg, std::va_list args) -> void; + +public: base_exception() = default; - virtual ~base_exception(); + base_exception(const base_exception&) = default; + base_exception(base_exception&&) = default; + ~base_exception() override; - void setMessage(const char *msg, std::va_list args); + auto operator=(const base_exception&) -> base_exception& = default; + auto operator=(base_exception&&) -> base_exception& = default; -public: - const char *what() const noexcept override { return mMessage.c_str(); } + [[nodiscard]] auto what() const noexcept -> const char* override { return mMessage.c_str(); } }; } // namespace al -#define START_API_FUNC try - -#define END_API_FUNC catch(...) { std::terminate(); } - #endif /* CORE_EXCEPT_H */ diff --git a/3rdparty/openal/core/filters/biquad.cpp b/3rdparty/openal/core/filters/biquad.cpp index a0a62eb8bde4..c1faf5e9fe05 100644 --- a/3rdparty/openal/core/filters/biquad.cpp +++ b/3rdparty/openal/core/filters/biquad.cpp @@ -3,6 +3,7 @@ #include "biquad.h" +#include #include #include #include @@ -27,8 +28,8 @@ void BiquadFilterR::setParams(BiquadType type, Real f0norm, Real gain, Rea const Real alpha{sin_w0/2.0f * rcpQ}; Real sqrtgain_alpha_2; - Real a[3]{ 1.0f, 0.0f, 0.0f }; - Real b[3]{ 1.0f, 0.0f, 0.0f }; + std::array a{{1.0f, 0.0f, 0.0f}}; + std::array b{{1.0f, 0.0f, 0.0f}}; /* Calculate filter coefficients depending on filter type */ switch(type) @@ -94,7 +95,7 @@ void BiquadFilterR::setParams(BiquadType type, Real f0norm, Real gain, Rea } template -void BiquadFilterR::process(const al::span src, Real *dst) +void BiquadFilterR::process(const al::span src, const al::span dst) { const Real b0{mB0}; const Real b1{mB1}; @@ -119,7 +120,7 @@ void BiquadFilterR::process(const al::span src, Real *dst) z2 = input*b2 - output*a2; return output; }; - std::transform(src.cbegin(), src.cend(), dst, proc_sample); + std::transform(src.cbegin(), src.cend(), dst.begin(), proc_sample); mZ1 = z1; mZ2 = z2; @@ -127,7 +128,7 @@ void BiquadFilterR::process(const al::span src, Real *dst) template void BiquadFilterR::dualProcess(BiquadFilterR &other, const al::span src, - Real *dst) + const al::span dst) { const Real b00{mB0}; const Real b01{mB1}; @@ -156,7 +157,7 @@ void BiquadFilterR::dualProcess(BiquadFilterR &other, const al::span src, Real *dst); + void process(const al::span src, const al::span dst); /** Processes this filter and the other at the same time. */ - void dualProcess(BiquadFilterR &other, const al::span src, Real *dst); + void dualProcess(BiquadFilterR &other, const al::span src, + const al::span dst); /* Rather hacky. It's just here to support "manual" processing. */ - std::pair getComponents() const noexcept { return {mZ1, mZ2}; } + [[nodiscard]] auto getComponents() const noexcept -> std::array { return {{mZ1,mZ2}}; } void setComponents(Real z1, Real z2) noexcept { mZ1 = z1; mZ2 = z2; } - Real processOne(const Real in, Real &z1, Real &z2) const noexcept + [[nodiscard]] auto processOne(const Real in, Real &z1, Real &z2) const noexcept -> Real { const Real out{in*mB0 + z1}; z1 = in*mB1 - out*mA1 + z2; @@ -134,7 +135,7 @@ template struct DualBiquadR { BiquadFilterR &f0, &f1; - void process(const al::span src, Real *dst) + void process(const al::span src, const al::span dst) { f0.dualProcess(f1, src, dst); } }; diff --git a/3rdparty/openal/core/filters/nfc.cpp b/3rdparty/openal/core/filters/nfc.cpp index aa64c6130728..c3c313db4fed 100644 --- a/3rdparty/openal/core/filters/nfc.cpp +++ b/3rdparty/openal/core/filters/nfc.cpp @@ -48,24 +48,22 @@ namespace { -constexpr float B[5][4] = { - { 0.0f }, - { 1.0f }, - { 3.0f, 3.0f }, - { 3.6778f, 6.4595f, 2.3222f }, - { 4.2076f, 11.4877f, 5.7924f, 9.1401f } +constexpr std::array B{ + std::array{ 0.0f, 0.0f, 0.0f, 0.0f}, + std::array{ 1.0f, 0.0f, 0.0f, 0.0f}, + std::array{ 3.0f, 3.0f, 0.0f, 0.0f}, + std::array{3.6778f, 6.4595f, 2.3222f, 0.0f}, + std::array{4.2076f, 11.4877f, 5.7924f, 9.1401f} }; NfcFilter1 NfcFilterCreate1(const float w0, const float w1) noexcept { NfcFilter1 nfc{}; - float b_00, g_0; - float r; /* Calculate bass-cut coefficients. */ - r = 0.5f * w1; - b_00 = B[1][0] * r; - g_0 = 1.0f + b_00; + float r{0.5f * w1}; + float b_00{B[1][0] * r}; + float g_0{1.0f + b_00}; nfc.base_gain = 1.0f / g_0; nfc.a1 = 2.0f * b_00 / g_0; @@ -95,14 +93,12 @@ void NfcFilterAdjust1(NfcFilter1 *nfc, const float w0) noexcept NfcFilter2 NfcFilterCreate2(const float w0, const float w1) noexcept { NfcFilter2 nfc{}; - float b_10, b_11, g_1; - float r; /* Calculate bass-cut coefficients. */ - r = 0.5f * w1; - b_10 = B[2][0] * r; - b_11 = B[2][1] * r * r; - g_1 = 1.0f + b_10 + b_11; + float r{0.5f * w1}; + float b_10{B[2][0] * r}; + float b_11{B[2][1] * r * r}; + float g_1{1.0f + b_10 + b_11}; nfc.base_gain = 1.0f / g_1; nfc.a1 = (2.0f*b_10 + 4.0f*b_11) / g_1; @@ -137,17 +133,14 @@ void NfcFilterAdjust2(NfcFilter2 *nfc, const float w0) noexcept NfcFilter3 NfcFilterCreate3(const float w0, const float w1) noexcept { NfcFilter3 nfc{}; - float b_10, b_11, g_1; - float b_00, g_0; - float r; /* Calculate bass-cut coefficients. */ - r = 0.5f * w1; - b_10 = B[3][0] * r; - b_11 = B[3][1] * r * r; - b_00 = B[3][2] * r; - g_1 = 1.0f + b_10 + b_11; - g_0 = 1.0f + b_00; + float r{0.5f * w1}; + float b_10{B[3][0] * r}; + float b_11{B[3][1] * r * r}; + float b_00{B[3][2] * r}; + float g_1{1.0f + b_10 + b_11}; + float g_0{1.0f + b_00}; nfc.base_gain = 1.0f / (g_1 * g_0); nfc.a1 = (2.0f*b_10 + 4.0f*b_11) / g_1; @@ -189,18 +182,15 @@ void NfcFilterAdjust3(NfcFilter3 *nfc, const float w0) noexcept NfcFilter4 NfcFilterCreate4(const float w0, const float w1) noexcept { NfcFilter4 nfc{}; - float b_10, b_11, g_1; - float b_00, b_01, g_0; - float r; /* Calculate bass-cut coefficients. */ - r = 0.5f * w1; - b_10 = B[4][0] * r; - b_11 = B[4][1] * r * r; - b_00 = B[4][2] * r; - b_01 = B[4][3] * r * r; - g_1 = 1.0f + b_10 + b_11; - g_0 = 1.0f + b_00 + b_01; + float r{0.5f * w1}; + float b_10{B[4][0] * r}; + float b_11{B[4][1] * r * r}; + float b_00{B[4][2] * r}; + float b_01{B[4][3] * r * r}; + float g_1{1.0f + b_10 + b_11}; + float g_0{1.0f + b_00 + b_01}; nfc.base_gain = 1.0f / (g_1 * g_0); nfc.a1 = (2.0f*b_10 + 4.0f*b_11) / g_1; @@ -262,7 +252,7 @@ void NfcFilter::adjust(const float w0) noexcept } -void NfcFilter::process1(const al::span src, float *RESTRICT dst) +void NfcFilter::process1(const al::span src, const al::span dst) { const float gain{first.gain}; const float b1{first.b1}; @@ -275,11 +265,11 @@ void NfcFilter::process1(const al::span src, float *RESTRICT dst) z1 += y; return out; }; - std::transform(src.cbegin(), src.cend(), dst, proc_sample); + std::transform(src.cbegin(), src.cend(), dst.begin(), proc_sample); first.z[0] = z1; } -void NfcFilter::process2(const al::span src, float *RESTRICT dst) +void NfcFilter::process2(const al::span src, const al::span dst) { const float gain{second.gain}; const float b1{second.b1}; @@ -296,12 +286,12 @@ void NfcFilter::process2(const al::span src, float *RESTRICT dst) z1 += y; return out; }; - std::transform(src.cbegin(), src.cend(), dst, proc_sample); + std::transform(src.cbegin(), src.cend(), dst.begin(), proc_sample); second.z[0] = z1; second.z[1] = z2; } -void NfcFilter::process3(const al::span src, float *RESTRICT dst) +void NfcFilter::process3(const al::span src, const al::span dst) { const float gain{third.gain}; const float b1{third.b1}; @@ -325,13 +315,13 @@ void NfcFilter::process3(const al::span src, float *RESTRICT dst) z3 += y; return out; }; - std::transform(src.cbegin(), src.cend(), dst, proc_sample); + std::transform(src.cbegin(), src.cend(), dst.begin(), proc_sample); third.z[0] = z1; third.z[1] = z2; third.z[2] = z3; } -void NfcFilter::process4(const al::span src, float *RESTRICT dst) +void NfcFilter::process4(const al::span src, const al::span dst) { const float gain{fourth.gain}; const float b1{fourth.b1}; @@ -359,7 +349,7 @@ void NfcFilter::process4(const al::span src, float *RESTRICT dst) z3 += y; return out; }; - std::transform(src.cbegin(), src.cend(), dst, proc_sample); + std::transform(src.cbegin(), src.cend(), dst.begin(), proc_sample); fourth.z[0] = z1; fourth.z[1] = z2; fourth.z[2] = z3; diff --git a/3rdparty/openal/core/filters/nfc.h b/3rdparty/openal/core/filters/nfc.h index 4b8e68b5dc0f..00ab4dd76540 100644 --- a/3rdparty/openal/core/filters/nfc.h +++ b/3rdparty/openal/core/filters/nfc.h @@ -1,30 +1,31 @@ #ifndef CORE_FILTERS_NFC_H #define CORE_FILTERS_NFC_H +#include #include #include "alspan.h" struct NfcFilter1 { - float base_gain, gain; - float b1, a1; - float z[1]; + float base_gain{1.0f}, gain{1.0f}; + float b1{}, a1{}; + std::array z{}; }; struct NfcFilter2 { - float base_gain, gain; - float b1, b2, a1, a2; - float z[2]; + float base_gain{1.0f}, gain{1.0f}; + float b1{}, b2{}, a1{}, a2{}; + std::array z{}; }; struct NfcFilter3 { - float base_gain, gain; - float b1, b2, b3, a1, a2, a3; - float z[3]; + float base_gain{1.0f}, gain{1.0f}; + float b1{}, b2{}, b3{}, a1{}, a2{}, a3{}; + std::array z{}; }; struct NfcFilter4 { - float base_gain, gain; - float b1, b2, b3, b4, a1, a2, a3, a4; - float z[4]; + float base_gain{1.0f}, gain{1.0f}; + float b1{}, b2{}, b3{}, b4{}, a1{}, a2{}, a3{}, a4{}; + std::array z{}; }; class NfcFilter { @@ -48,16 +49,16 @@ class NfcFilter { void adjust(const float w0) noexcept; /* Near-field control filter for first-order ambisonic channels (1-3). */ - void process1(const al::span src, float *RESTRICT dst); + void process1(const al::span src, const al::span dst); /* Near-field control filter for second-order ambisonic channels (4-8). */ - void process2(const al::span src, float *RESTRICT dst); + void process2(const al::span src, const al::span dst); /* Near-field control filter for third-order ambisonic channels (9-15). */ - void process3(const al::span src, float *RESTRICT dst); + void process3(const al::span src, const al::span dst); /* Near-field control filter for fourth-order ambisonic channels (16-24). */ - void process4(const al::span src, float *RESTRICT dst); + void process4(const al::span src, const al::span dst); }; #endif /* CORE_FILTERS_NFC_H */ diff --git a/3rdparty/openal/core/filters/splitter.cpp b/3rdparty/openal/core/filters/splitter.cpp index 983ba36f15dd..fbb6b2b7ef00 100644 --- a/3rdparty/openal/core/filters/splitter.cpp +++ b/3rdparty/openal/core/filters/splitter.cpp @@ -4,6 +4,7 @@ #include "splitter.h" #include +#include #include #include @@ -27,14 +28,17 @@ void BandSplitterR::init(Real f0norm) } template -void BandSplitterR::process(const al::span input, Real *hpout, Real *lpout) +void BandSplitterR::process(const al::span input, const al::span hpout, + const al::span lpout) { const Real ap_coeff{mCoeff}; const Real lp_coeff{mCoeff*0.5f + 0.5f}; Real lp_z1{mLpZ1}; Real lp_z2{mLpZ2}; Real ap_z1{mApZ1}; - auto proc_sample = [ap_coeff,lp_coeff,&lp_z1,&lp_z2,&ap_z1,&lpout](const Real in) noexcept -> Real + assert(lpout.size() <= input.size()); + auto lpiter = lpout.begin(); + auto proc_sample = [ap_coeff,lp_coeff,&lp_z1,&lp_z2,&ap_z1,&lpiter](const Real in) noexcept -> Real { /* Low-pass sample processing. */ Real d{(in - lp_z1) * lp_coeff}; @@ -45,7 +49,7 @@ void BandSplitterR::process(const al::span input, Real *hpout, lp_y = lp_z2 + d; lp_z2 = lp_y + d; - *(lpout++) = lp_y; + *(lpiter++) = lp_y; /* All-pass sample processing. */ Real ap_y{in*ap_coeff + ap_z1}; @@ -54,15 +58,15 @@ void BandSplitterR::process(const al::span input, Real *hpout, /* High-pass generated from removing low-passed output. */ return ap_y - lp_y; }; - std::transform(input.cbegin(), input.cend(), hpout, proc_sample); + std::transform(input.cbegin(), input.cend(), hpout.begin(), proc_sample); mLpZ1 = lp_z1; mLpZ2 = lp_z2; mApZ1 = ap_z1; } template -void BandSplitterR::processHfScale(const al::span input, Real *RESTRICT output, - const Real hfscale) +void BandSplitterR::processHfScale(const al::span input, + const al::span output, const Real hfscale) { const Real ap_coeff{mCoeff}; const Real lp_coeff{mCoeff*0.5f + 0.5f}; @@ -89,7 +93,7 @@ void BandSplitterR::processHfScale(const al::span input, Real */ return (ap_y-lp_y)*hfscale + lp_y; }; - std::transform(input.begin(), input.end(), output, proc_sample); + std::transform(input.cbegin(), input.cend(), output.begin(), proc_sample); mLpZ1 = lp_z1; mLpZ2 = lp_z2; mApZ1 = ap_z1; diff --git a/3rdparty/openal/core/filters/splitter.h b/3rdparty/openal/core/filters/splitter.h index e853eb385b8d..e3658c19afa1 100644 --- a/3rdparty/openal/core/filters/splitter.h +++ b/3rdparty/openal/core/filters/splitter.h @@ -22,9 +22,11 @@ class BandSplitterR { void init(Real f0norm); void clear() noexcept { mLpZ1 = mLpZ2 = mApZ1 = 0.0f; } - void process(const al::span input, Real *hpout, Real *lpout); + void process(const al::span input, const al::span hpout, + const al::span lpout); - void processHfScale(const al::span input, Real *output, const Real hfscale); + void processHfScale(const al::span input, const al::span output, + const Real hfscale); void processHfScale(const al::span samples, const Real hfscale); void processScale(const al::span samples, const Real hfscale, const Real lfscale); diff --git a/3rdparty/openal/core/fmt_traits.cpp b/3rdparty/openal/core/fmt_traits.cpp index 054d87669f0f..9d79287db507 100644 --- a/3rdparty/openal/core/fmt_traits.cpp +++ b/3rdparty/openal/core/fmt_traits.cpp @@ -6,7 +6,7 @@ namespace al { -const int16_t muLawDecompressionTable[256] = { +const std::array muLawDecompressionTable{{ -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, @@ -39,9 +39,9 @@ const int16_t muLawDecompressionTable[256] = { 244, 228, 212, 196, 180, 164, 148, 132, 120, 112, 104, 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0 -}; +}}; -const int16_t aLawDecompressionTable[256] = { +const std::array aLawDecompressionTable{{ -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, @@ -74,6 +74,6 @@ const int16_t aLawDecompressionTable[256] = { 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, 688, 656, 752, 720, 560, 528, 624, 592, 944, 912, 1008, 976, 816, 784, 880, 848 -}; +}}; } // namespace al diff --git a/3rdparty/openal/core/fmt_traits.h b/3rdparty/openal/core/fmt_traits.h index 1879c81b1187..e87ea57cedec 100644 --- a/3rdparty/openal/core/fmt_traits.h +++ b/3rdparty/openal/core/fmt_traits.h @@ -1,16 +1,16 @@ #ifndef CORE_FMT_TRAITS_H #define CORE_FMT_TRAITS_H -#include -#include +#include +#include -#include "buffer_storage.h" +#include "storage_formats.h" namespace al { -extern const int16_t muLawDecompressionTable[256]; -extern const int16_t aLawDecompressionTable[256]; +extern const std::array muLawDecompressionTable; +extern const std::array aLawDecompressionTable; template @@ -18,62 +18,52 @@ struct FmtTypeTraits { }; template<> struct FmtTypeTraits { - using Type = uint8_t; + using Type = std::uint8_t; - template - static constexpr OutT to(const Type val) noexcept { return val*OutT{1.0/128.0} - OutT{1.0}; } + constexpr float operator()(const Type val) const noexcept + { return float(val)*(1.0f/128.0f) - 1.0f; } }; template<> struct FmtTypeTraits { - using Type = int16_t; + using Type = std::int16_t; - template - static constexpr OutT to(const Type val) noexcept { return val*OutT{1.0/32768.0}; } + constexpr float operator()(const Type val) const noexcept + { return float(val) * (1.0f/32768.0f); } +}; +template<> +struct FmtTypeTraits { + using Type = std::int32_t; + + constexpr float operator()(const Type val) const noexcept + { return static_cast(val)*(1.0f/2147483648.0f); } }; template<> struct FmtTypeTraits { using Type = float; - template - static constexpr OutT to(const Type val) noexcept { return val; } + constexpr float operator()(const Type val) const noexcept { return val; } }; template<> struct FmtTypeTraits { using Type = double; - template - static constexpr OutT to(const Type val) noexcept { return static_cast(val); } + constexpr float operator()(const Type val) const noexcept { return static_cast(val); } }; template<> struct FmtTypeTraits { - using Type = uint8_t; + using Type = std::uint8_t; - template - static constexpr OutT to(const Type val) noexcept - { return muLawDecompressionTable[val] * OutT{1.0/32768.0}; } + constexpr float operator()(const Type val) const noexcept + { return float(muLawDecompressionTable[val]) * (1.0f/32768.0f); } }; template<> struct FmtTypeTraits { - using Type = uint8_t; + using Type = std::uint8_t; - template - static constexpr OutT to(const Type val) noexcept - { return aLawDecompressionTable[val] * OutT{1.0/32768.0}; } + constexpr float operator()(const Type val) const noexcept + { return float(aLawDecompressionTable[val]) * (1.0f/32768.0f); } }; - -template -inline void LoadSampleArray(DstT *RESTRICT dst, const std::byte *src, const std::size_t srcstep, - const std::size_t samples) noexcept -{ - using TypeTraits = FmtTypeTraits; - using SampleType = typename TypeTraits::Type; - - const SampleType *RESTRICT ssrc{reinterpret_cast(src)}; - for(size_t i{0u};i < samples;i++) - dst[i] = TypeTraits::template to(ssrc[i*srcstep]); -} - } // namespace al #endif /* CORE_FMT_TRAITS_H */ diff --git a/3rdparty/openal/core/fpu_ctrl.cpp b/3rdparty/openal/core/fpu_ctrl.cpp index 0cf0d6e72d10..7a4f147fa30d 100644 --- a/3rdparty/openal/core/fpu_ctrl.cpp +++ b/3rdparty/openal/core/fpu_ctrl.cpp @@ -1,61 +1,90 @@ #include "config.h" +#include "config_simd.h" #include "fpu_ctrl.h" #ifdef HAVE_INTRIN_H #include #endif -#ifdef HAVE_SSE_INTRINSICS +#if HAVE_SSE_INTRINSICS #include -#ifndef _MM_DENORMALS_ZERO_MASK +#elif HAVE_SSE +#include +#endif + +#if 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 +#if !HAVE_SSE_INTRINSICS && HAVE_SSE #include "cpu_caps.h" +#endif +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}; +#if HAVE_SSE_INTRINSICS + *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 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 - - this->in_mode = true; } -void FPUCtl::leave() noexcept +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +[[gnu::target("sse")]] +#endif +[[maybe_unused]] +void reset_fpu(unsigned int state [[maybe_unused]]) { - if(!this->in_mode) return; +#if HAVE_SSE_INTRINSICS || HAVE_SSE + _mm_setcsr(state); +#endif +} -#if defined(HAVE_SSE_INTRINSICS) - _mm_setcsr(this->sse_state); +} // namespace -#elif defined(__GNUC__) && defined(HAVE_SSE) +unsigned int FPUCtl::Set() noexcept +{ + unsigned int state{}; +#if HAVE_SSE_INTRINSICS + disable_denormals(&state); +#elif HAVE_SSE + if((CPUCapFlags&CPU_CAP_SSE)) + disable_denormals(&state); +#endif + return state; +} + +void FPUCtl::Reset(unsigned int state [[maybe_unused]]) noexcept +{ +#if HAVE_SSE_INTRINSICS + reset_fpu(state); +#elif HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) - __asm__ __volatile__("ldmxcsr %0" : : "m" (*&this->sse_state)); + reset_fpu(state); #endif - this->in_mode = false; } diff --git a/3rdparty/openal/core/fpu_ctrl.h b/3rdparty/openal/core/fpu_ctrl.h index 9554313ae047..d4f75ec3146c 100644 --- a/3rdparty/openal/core/fpu_ctrl.h +++ b/3rdparty/openal/core/fpu_ctrl.h @@ -2,20 +2,31 @@ #define CORE_FPU_CTRL_H class FPUCtl { -#if defined(HAVE_SSE_INTRINSICS) || (defined(__GNUC__) && defined(HAVE_SSE)) unsigned int sse_state{}; -#endif bool in_mode{}; + static unsigned int Set() noexcept; + static void Reset(unsigned int state) noexcept; + public: - FPUCtl() noexcept { enter(); in_mode = true; } - ~FPUCtl() { if(in_mode) leave(); } + FPUCtl() noexcept : sse_state{Set()}, in_mode{true} { } + ~FPUCtl() { if(in_mode) Reset(sse_state); } FPUCtl(const FPUCtl&) = delete; FPUCtl& operator=(const FPUCtl&) = delete; - void enter() noexcept; - void leave() noexcept; + void enter() noexcept + { + if(!in_mode) + sse_state = Set(); + in_mode = true; + } + void leave() noexcept + { + if(in_mode) + Reset(sse_state); + in_mode = false; + } }; #endif /* CORE_FPU_CTRL_H */ diff --git a/3rdparty/openal/core/front_stablizer.h b/3rdparty/openal/core/front_stablizer.h index 6825111a7c6a..8eeb6d7476ab 100644 --- a/3rdparty/openal/core/front_stablizer.h +++ b/3rdparty/openal/core/front_stablizer.h @@ -7,6 +7,7 @@ #include "almalloc.h" #include "bufferline.h" #include "filters/splitter.h" +#include "flexarray.h" struct FrontStablizer { diff --git a/3rdparty/openal/core/helpers.cpp b/3rdparty/openal/core/helpers.cpp index b353da2e1012..8464d53ef370 100644 --- a/3rdparty/openal/core/helpers.cpp +++ b/3rdparty/openal/core/helpers.cpp @@ -11,11 +11,15 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include "almalloc.h" #include "alnumeric.h" #include "alspan.h" #include "alstring.h" @@ -23,12 +27,50 @@ #include "strutils.h" -/* Mixing thread priority level */ -int RTPrioLevel{1}; +namespace { + +using namespace std::string_view_literals; + +std::mutex gSearchLock; + +void DirectorySearch(const std::filesystem::path &path, const std::string_view ext, + std::vector *const results) +{ + namespace fs = std::filesystem; -/* Allow reducing the process's RTTime limit for RTKit. */ -bool AllowRTTimeLimit{true}; + const auto base = results->size(); + + try { + auto fpath = path.lexically_normal(); + if(!fs::exists(fpath)) + return; + + TRACE("Searching %s for *%.*s\n", reinterpret_cast(fpath.u8string().c_str()), + al::sizei(ext), ext.data()); + for(auto&& dirent : fs::directory_iterator{fpath}) + { + auto&& entrypath = dirent.path(); + if(!entrypath.has_extension()) + continue; + + if(fs::status(entrypath).type() != fs::file_type::regular) + continue; + const auto u8ext = entrypath.extension().u8string(); + if(al::case_compare(al::u8_as_char(u8ext), ext) == 0) + results->emplace_back(al::u8_as_char(entrypath.u8string())); + } + } + catch(std::exception& e) { + ERR("Exception enumerating files: %s\n", e.what()); + } + + const auto newlist = al::span{*results}.subspan(base); + std::sort(newlist.begin(), newlist.end()); + for(const auto &name : newlist) + TRACE(" got %s\n", name.c_str()); +} +} // namespace #ifdef _WIN32 @@ -37,149 +79,116 @@ bool AllowRTTimeLimit{true}; const PathNamePair &GetProcBinary() { - static std::optional procbin; - if(procbin) return *procbin; -#if !defined(ALSOFT_UWP) - auto fullpath = std::vector(256); - DWORD len{GetModuleFileNameW(nullptr, fullpath.data(), static_cast(fullpath.size()))}; - while(len == fullpath.size()) - { - fullpath.resize(fullpath.size() << 1); - len = GetModuleFileNameW(nullptr, fullpath.data(), static_cast(fullpath.size())); - } - if(len == 0) + auto get_procbin = [] { - ERR("Failed to get process name: error %lu\n", GetLastError()); - procbin.emplace(); - return *procbin; - } +#if !ALSOFT_UWP + DWORD pathlen{256}; + auto fullpath = std::wstring(pathlen, L'\0'); + DWORD len{GetModuleFileNameW(nullptr, fullpath.data(), pathlen)}; + while(len == fullpath.size()) + { + pathlen <<= 1; + if(pathlen == 0) + { + /* pathlen overflow (more than 4 billion characters??) */ + len = 0; + break; + } + fullpath.resize(pathlen); + len = GetModuleFileNameW(nullptr, fullpath.data(), pathlen); + } + if(len == 0) + { + ERR("Failed to get process name: error %lu\n", GetLastError()); + return PathNamePair{}; + } - fullpath.resize(len); - if(fullpath.back() != 0) - fullpath.push_back(0); + fullpath.resize(len); #else - auto exePath = __wargv[0]; - if (!exePath) - { - ERR("Failed to get process name: error %lu\n", GetLastError()); - procbin.emplace(); - return *procbin; - } - std::vector fullpath{exePath, exePath + wcslen(exePath) + 1}; + const WCHAR *exePath{__wargv[0]}; + if(!exePath) + { + ERR("Failed to get process name: __wargv[0] == nullptr\n"); + return PathNamePair{}; + } + std::wstring fullpath{exePath}; #endif - std::replace(fullpath.begin(), fullpath.end(), '/', '\\'); - auto sep = std::find(fullpath.rbegin()+1, fullpath.rend(), '\\'); - if(sep != fullpath.rend()) - { - *sep = 0; - procbin.emplace(wstr_to_utf8(fullpath.data()), wstr_to_utf8(al::to_address(sep.base()))); - } - else - procbin.emplace(std::string{}, wstr_to_utf8(fullpath.data())); + std::replace(fullpath.begin(), fullpath.end(), L'/', L'\\'); - TRACE("Got binary: %s, %s\n", procbin->path.c_str(), procbin->fname.c_str()); - return *procbin; + PathNamePair res{}; + if(auto seppos = fullpath.rfind(L'\\'); seppos < fullpath.size()) + { + res.path = wstr_to_utf8(std::wstring_view{fullpath}.substr(0, seppos)); + res.fname = wstr_to_utf8(std::wstring_view{fullpath}.substr(seppos+1)); + } + else + res.fname = wstr_to_utf8(fullpath); + + TRACE("Got binary: %s, %s\n", res.path.c_str(), res.fname.c_str()); + return res; + }; + static const PathNamePair procbin{get_procbin()}; + return procbin; } namespace { -void DirectorySearch(const char *path, const char *ext, std::vector *const results) -{ - std::string pathstr{path}; - pathstr += "\\*"; - pathstr += ext; - TRACE("Searching %s\n", pathstr.c_str()); +#if !ALSOFT_UWP && !defined(_GAMING_XBOX) +struct CoTaskMemDeleter { + void operator()(void *mem) const { CoTaskMemFree(mem); } +}; +#endif - std::wstring wpath{utf8_to_wstr(pathstr.c_str())}; - WIN32_FIND_DATAW fdata; - HANDLE hdl{FindFirstFileExW(wpath.c_str(), FindExInfoStandard, &fdata, FindExSearchNameMatch, NULL, 0)}; - if(hdl == INVALID_HANDLE_VALUE) return; +} // namespace - const auto base = results->size(); +auto SearchDataFiles(const std::string_view ext) -> std::vector +{ + auto srchlock = std::lock_guard{gSearchLock}; - do { - results->emplace_back(); - std::string &str = results->back(); - str = path; - str += '\\'; - str += wstr_to_utf8(fdata.cFileName); - } while(FindNextFileW(hdl, &fdata)); - FindClose(hdl); - const al::span newlist{results->data()+base, results->size()-base}; - std::sort(newlist.begin(), newlist.end()); - for(const auto &name : newlist) - TRACE(" got %s\n", name.c_str()); -} + /* Search the app-local directory. */ + auto results = std::vector{}; + if(auto localpath = al::getenv(L"ALSOFT_LOCAL_PATH")) + DirectorySearch(*localpath, ext, &results); + else if(auto curpath = std::filesystem::current_path(); !curpath.empty()) + DirectorySearch(curpath, ext, &results); -} // namespace + return results; +} -std::vector SearchDataFiles(const char *ext, const char *subdir) +auto SearchDataFiles(const std::string_view ext, const std::string_view subdir) + -> std::vector { - auto is_slash = [](int c) noexcept { return (c == '\\' || c == '/'); }; - - static std::mutex search_lock; - std::lock_guard _{search_lock}; + std::lock_guard srchlock{gSearchLock}; /* If the path is absolute, use it directly. */ std::vector results; - if(std::isalpha(subdir[0]) && subdir[1] == ':' && is_slash(subdir[2])) - { - std::string path{subdir}; - std::replace(path.begin(), path.end(), '/', '\\'); - DirectorySearch(path.c_str(), ext, &results); - return results; - } - if(subdir[0] == '\\' && subdir[1] == '\\' && subdir[2] == '?' && subdir[3] == '\\') + auto path = std::filesystem::u8path(subdir); + if(path.is_absolute()) { - DirectorySearch(subdir, ext, &results); + DirectorySearch(path, ext, &results); return results; } - std::string path; - - /* Search the app-local directory. */ - if(auto localpath = al::getenv(L"ALSOFT_LOCAL_PATH")) - { - path = wstr_to_utf8(localpath->c_str()); - if(is_slash(path.back())) - path.pop_back(); - } - else if(WCHAR *cwdbuf{_wgetcwd(nullptr, 0)}) - { - path = wstr_to_utf8(cwdbuf); - if(is_slash(path.back())) - path.pop_back(); - free(cwdbuf); - } - else - path = "."; - std::replace(path.begin(), path.end(), '/', '\\'); - DirectorySearch(path.c_str(), ext, &results); - -#if !defined(ALSOFT_UWP) && !defined(_GAMING_XBOX) +#if !ALSOFT_UWP && !defined(_GAMING_XBOX) /* Search the local and global data dirs. */ - for(auto id : std::array{CSIDL_APPDATA, CSIDL_COMMON_APPDATA}) + for(const auto &folderid : std::array{FOLDERID_RoamingAppData, FOLDERID_ProgramData}) { - WCHAR buffer[MAX_PATH]; - if(SHGetSpecialFolderPathW(nullptr, buffer, id, FALSE) == FALSE) + std::unique_ptr buffer; + const HRESULT hr{SHGetKnownFolderPath(folderid, KF_FLAG_DONT_UNEXPAND, nullptr, + al::out_ptr(buffer))}; + if(FAILED(hr) || !buffer || !*buffer) continue; - path = wstr_to_utf8(buffer); - if(!is_slash(path.back())) - path += '\\'; - path += subdir; - std::replace(path.begin(), path.end(), '/', '\\'); - - DirectorySearch(path.c_str(), ext, &results); + DirectorySearch(std::filesystem::path{buffer.get()}/path, ext, &results); } #endif return results; } -void SetRTPriority(void) +void SetRTPriority() { -#if !defined(ALSOFT_UWP) +#if !ALSOFT_UWP if(RTPrioLevel > 0) { if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) @@ -206,7 +215,7 @@ void SetRTPriority(void) #include #include #endif -#ifdef HAVE_RTKIT +#if HAVE_RTKIT #include #include "dbus_wrap.h" @@ -218,184 +227,121 @@ void SetRTPriority(void) const PathNamePair &GetProcBinary() { - static std::optional procbin; - if(procbin) return *procbin; - - std::vector pathname; -#ifdef __FreeBSD__ - size_t pathlen; - int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; - if(sysctl(mib, 4, nullptr, &pathlen, nullptr, 0) == -1) - WARN("Failed to sysctl kern.proc.pathname: %s\n", strerror(errno)); - else + auto get_procbin = [] { - pathname.resize(pathlen + 1); - sysctl(mib, 4, pathname.data(), &pathlen, nullptr, 0); - pathname.resize(pathlen); - } + std::string pathname; +#ifdef __FreeBSD__ + size_t pathlen{}; + std::array mib{{CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}}; + if(sysctl(mib.data(), mib.size(), nullptr, &pathlen, nullptr, 0) == -1) + WARN("Failed to sysctl kern.proc.pathname: %s\n", + std::generic_category().message(errno).c_str()); + else + { + auto procpath = std::vector(pathlen+1, '\0'); + sysctl(mib.data(), mib.size(), procpath.data(), &pathlen, nullptr, 0); + pathname = procpath.data(); + } #endif #ifdef HAVE_PROC_PIDPATH - if(pathname.empty()) - { - char procpath[PROC_PIDPATHINFO_MAXSIZE]{}; - const pid_t pid{getpid()}; - if(proc_pidpath(pid, procpath, sizeof(procpath)) < 1) - ERR("proc_pidpath(%d, ...) failed: %s\n", pid, strerror(errno)); - else - pathname.insert(pathname.end(), procpath, procpath+strlen(procpath)); - } + if(pathname.empty()) + { + std::array procpath{}; + const pid_t pid{getpid()}; + if(proc_pidpath(pid, procpath.data(), procpath.size()) < 1) + ERR("proc_pidpath(%d, ...) failed: %s\n", pid, + std::generic_category().message(errno).c_str()); + else + pathname = procpath.data(); + } #endif #ifdef __HAIKU__ - if(pathname.empty()) - { - char procpath[PATH_MAX]; - if(find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, procpath, sizeof(procpath)) == B_OK) - pathname.insert(pathname.end(), procpath, procpath+strlen(procpath)); - } + if(pathname.empty()) + { + std::array procpath{}; + if(find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, procpath.data(), procpath.size()) == B_OK) + pathname = procpath.data(); + } #endif #ifndef __SWITCH__ - if(pathname.empty()) - { - const char *SelfLinkNames[]{ - "/proc/self/exe", - "/proc/self/file", - "/proc/curproc/exe", - "/proc/curproc/file" - }; - - pathname.resize(256); - - const char *selfname{}; - ssize_t len{}; - for(const char *name : SelfLinkNames) + if(pathname.empty()) { - selfname = name; - len = readlink(selfname, pathname.data(), pathname.size()); - if(len >= 0 || errno != ENOENT) break; + const std::array SelfLinkNames{ + "/proc/self/exe"sv, + "/proc/self/file"sv, + "/proc/curproc/exe"sv, + "/proc/curproc/file"sv, + }; + + for(const std::string_view name : SelfLinkNames) + { + try { + if(!std::filesystem::exists(name)) + continue; + if(auto path = std::filesystem::read_symlink(name); !path.empty()) + { + pathname = al::u8_as_char(path.u8string()); + break; + } + } + catch(std::exception& e) { + WARN("Exception getting symlink %.*s: %s\n", al::sizei(name), name.data(), + e.what()); + } + } } +#endif - while(len > 0 && static_cast(len) == pathname.size()) - { - pathname.resize(pathname.size() << 1); - len = readlink(selfname, pathname.data(), pathname.size()); - } - if(len <= 0) + PathNamePair res{}; + if(auto seppos = pathname.rfind('/'); seppos < pathname.size()) { - WARN("Failed to readlink %s: %s\n", selfname, strerror(errno)); - len = 0; + res.path = std::string_view{pathname}.substr(0, seppos); + res.fname = std::string_view{pathname}.substr(seppos+1); } + else + res.fname = pathname; - pathname.resize(static_cast(len)); - } -#endif - while(!pathname.empty() && pathname.back() == 0) - pathname.pop_back(); - - auto sep = std::find(pathname.crbegin(), pathname.crend(), '/'); - if(sep != pathname.crend()) - procbin.emplace(std::string(pathname.cbegin(), sep.base()-1), - std::string(sep.base(), pathname.cend())); - else - procbin.emplace(std::string{}, std::string(pathname.cbegin(), pathname.cend())); - - TRACE("Got binary: \"%s\", \"%s\"\n", procbin->path.c_str(), procbin->fname.c_str()); - return *procbin; + TRACE("Got binary: \"%s\", \"%s\"\n", res.path.c_str(), res.fname.c_str()); + return res; + }; + static const PathNamePair procbin{get_procbin()}; + return procbin; } -namespace { - -void DirectorySearch(const char *path, const char *ext, std::vector *const results) +auto SearchDataFiles(const std::string_view ext) -> std::vector { - TRACE("Searching %s for *%s\n", path, ext); - DIR *dir{opendir(path)}; - if(!dir) return; - - const auto base = results->size(); - const size_t extlen{strlen(ext)}; - - while(struct dirent *dirent{readdir(dir)}) - { - if(strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0) - continue; + auto srchlock = std::lock_guard{gSearchLock}; - const size_t len{strlen(dirent->d_name)}; - if(len <= extlen) continue; - if(al::strcasecmp(dirent->d_name+len-extlen, ext) != 0) - continue; - - results->emplace_back(); - std::string &str = results->back(); - str = path; - if(str.back() != '/') - str.push_back('/'); - str += dirent->d_name; - } - closedir(dir); + /* Search the app-local directory. */ + auto results = std::vector{}; + if(auto localpath = al::getenv("ALSOFT_LOCAL_PATH")) + DirectorySearch(*localpath, ext, &results); + else if(auto curpath = std::filesystem::current_path(); !curpath.empty()) + DirectorySearch(curpath, ext, &results); - const al::span newlist{results->data()+base, results->size()-base}; - std::sort(newlist.begin(), newlist.end()); - for(const auto &name : newlist) - TRACE(" got %s\n", name.c_str()); + return results; } -} // namespace - -std::vector SearchDataFiles(const char *ext, const char *subdir) +auto SearchDataFiles(const std::string_view ext, const std::string_view subdir) + -> std::vector { - static std::mutex search_lock; - std::lock_guard _{search_lock}; + std::lock_guard srchlock{gSearchLock}; std::vector results; - if(subdir[0] == '/') + auto path = std::filesystem::u8path(subdir); + if(path.is_absolute()) { - DirectorySearch(subdir, ext, &results); + DirectorySearch(path, ext, &results); return results; } - /* Search the app-local directory. */ - if(auto localpath = al::getenv("ALSOFT_LOCAL_PATH")) - DirectorySearch(localpath->c_str(), ext, &results); - else - { - std::vector cwdbuf(256); - while(!getcwd(cwdbuf.data(), cwdbuf.size())) - { - if(errno != ERANGE) - { - cwdbuf.clear(); - break; - } - cwdbuf.resize(cwdbuf.size() << 1); - } - if(cwdbuf.empty()) - DirectorySearch(".", ext, &results); - else - { - DirectorySearch(cwdbuf.data(), ext, &results); - cwdbuf.clear(); - } - } - - // Search local data dir + /* Search local data dir */ if(auto datapath = al::getenv("XDG_DATA_HOME")) - { - std::string &path = *datapath; - if(path.back() != '/') - path += '/'; - path += subdir; - DirectorySearch(path.c_str(), ext, &results); - } + DirectorySearch(std::filesystem::path{*datapath}/path, ext, &results); else if(auto homepath = al::getenv("HOME")) - { - std::string &path = *homepath; - if(path.back() == '/') - path.pop_back(); - path += "/.local/share/"; - path += subdir; - DirectorySearch(path.c_str(), ext, &results); - } + DirectorySearch(std::filesystem::path{*homepath}/".local/share"/path, ext, &results); - // Search global data dirs + /* Search global data dirs */ std::string datadirs{al::getenv("XDG_DATA_DIRS").value_or("/usr/local/share/:/usr/share/")}; size_t curpos{0u}; @@ -403,30 +349,19 @@ std::vector SearchDataFiles(const char *ext, const char *subdir) { size_t nextpos{datadirs.find(':', curpos)}; - std::string path{(nextpos != std::string::npos) ? - datadirs.substr(curpos, nextpos++ - curpos) : datadirs.substr(curpos)}; + std::string_view pathname{(nextpos != std::string::npos) + ? std::string_view{datadirs}.substr(curpos, nextpos++ - curpos) + : std::string_view{datadirs}.substr(curpos)}; curpos = nextpos; - if(path.empty()) continue; - if(path.back() != '/') - path += '/'; - path += subdir; - - DirectorySearch(path.c_str(), ext, &results); + if(!pathname.empty()) + DirectorySearch(std::filesystem::path{pathname}/path, ext, &results); } #ifdef ALSOFT_INSTALL_DATADIR - // Search the installation data directory - { - std::string path{ALSOFT_INSTALL_DATADIR}; - if(!path.empty()) - { - if(path.back() != '/') - path += '/'; - path += subdir; - DirectorySearch(path.c_str(), ext, &results); - } - } + /* Search the installation data directory */ + if(auto instpath = std::filesystem::path{ALSOFT_INSTALL_DATADIR}; !instpath.empty()) + DirectorySearch(instpath/path, ext, &results); #endif return results; @@ -447,7 +382,7 @@ bool SetRTPriorityPthread(int prio [[maybe_unused]]) rtmax = (rtmax-rtmin)/2 + rtmin; struct sched_param param{}; - param.sched_priority = clampi(prio, rtmin, rtmax); + param.sched_priority = std::clamp(prio, rtmin, rtmax); #ifdef SCHED_RESET_ON_FORK err = pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, ¶m); if(err == EINVAL) @@ -455,13 +390,14 @@ bool SetRTPriorityPthread(int prio [[maybe_unused]]) err = pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); if(err == 0) return true; #endif - WARN("pthread_setschedparam failed: %s (%d)\n", std::strerror(err), err); + WARN("pthread_setschedparam failed: %s (%d)\n", std::generic_category().message(err).c_str(), + err); return false; } bool SetRTPriorityRTKit(int prio [[maybe_unused]]) { -#ifdef HAVE_RTKIT +#if HAVE_RTKIT if(!HasDBus()) { WARN("D-Bus not available\n"); @@ -483,7 +419,7 @@ bool SetRTPriorityRTKit(int prio [[maybe_unused]]) if(err == -ENOENT) { err = std::abs(err); - ERR("Could not query RTKit: %s (%d)\n", std::strerror(err), err); + ERR("Could not query RTKit: %s (%d)\n", std::generic_category().message(err).c_str(), err); return false; } int rtmax{rtkit_get_max_realtime_priority(conn.get())}; @@ -519,19 +455,20 @@ bool SetRTPriorityRTKit(int prio [[maybe_unused]]) err = limit_rttime(conn.get()); if(err != 0) WARN("Failed to set RLIMIT_RTTIME for RTKit: %s (%d)\n", - std::strerror(err), err); + std::generic_category().message(err).c_str(), err); } /* Limit the maximum real-time priority to half. */ rtmax = (rtmax+1)/2; - prio = clampi(prio, 1, rtmax); + prio = std::clamp(prio, 1, rtmax); TRACE("Making real-time with priority %d (max: %d)\n", prio, rtmax); err = rtkit_make_realtime(conn.get(), 0, prio); if(err == 0) return true; err = std::abs(err); - WARN("Failed to set real-time priority: %s (%d)\n", std::strerror(err), err); + WARN("Failed to set real-time priority: %s (%d)\n", + std::generic_category().message(err).c_str(), err); } /* Don't try to set the niceness for non-Linux systems. Standard POSIX has * niceness as a per-process attribute, while the intent here is for the @@ -546,7 +483,8 @@ bool SetRTPriorityRTKit(int prio [[maybe_unused]]) if(err == 0) return true; err = std::abs(err); - WARN("Failed to set high priority: %s (%d)\n", std::strerror(err), err); + WARN("Failed to set high priority: %s (%d)\n", + std::generic_category().message(err).c_str(), err); } #endif /* __linux__ */ diff --git a/3rdparty/openal/core/helpers.h b/3rdparty/openal/core/helpers.h index df51c1161de4..96987a2e8743 100644 --- a/3rdparty/openal/core/helpers.h +++ b/3rdparty/openal/core/helpers.h @@ -1,26 +1,26 @@ #ifndef CORE_HELPERS_H #define CORE_HELPERS_H -#include #include +#include #include struct PathNamePair { std::string path, fname; - - PathNamePair() = default; - template - PathNamePair(T&& path_, U&& fname_) - : path{std::forward(path_)}, fname{std::forward(fname_)} - { } }; -const PathNamePair &GetProcBinary(void); +const PathNamePair &GetProcBinary(); + +/* Mixing thread priority level */ +inline int RTPrioLevel{1}; + +/* Allow reducing the process's RTTime limit for RTKit. */ +inline bool AllowRTTimeLimit{true}; -extern int RTPrioLevel; -extern bool AllowRTTimeLimit; -void SetRTPriority(void); +void SetRTPriority(); -std::vector SearchDataFiles(const char *match, const char *subdir); +auto SearchDataFiles(const std::string_view ext) -> std::vector; +auto SearchDataFiles(const std::string_view ext, const std::string_view subdir) + -> std::vector; #endif /* CORE_HELPERS_H */ diff --git a/3rdparty/openal/core/hrtf.cpp b/3rdparty/openal/core/hrtf.cpp index 9a13a0045056..9833b125bcb1 100644 --- a/3rdparty/openal/core/hrtf.cpp +++ b/3rdparty/openal/core/hrtf.cpp @@ -12,22 +12,24 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include #include "albit.h" -#include "alfstream.h" #include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" +#include "alstring.h" #include "ambidefs.h" #include "filters/splitter.h" #include "helpers.h" @@ -39,10 +41,16 @@ namespace { +using namespace std::string_view_literals; + struct HrtfEntry { std::string mDispName; std::string mFilename; + template + HrtfEntry(T&& dispname, U&& fname) + : mDispName{std::forward(dispname)}, mFilename{std::forward(fname)} + { } /* GCC warns when it tries to inline this. */ ~HrtfEntry(); }; @@ -50,11 +58,12 @@ HrtfEntry::~HrtfEntry() = default; struct LoadedHrtf { std::string mFilename; + uint mSampleRate{}; std::unique_ptr mEntry; template - LoadedHrtf(T&& name, U&& entry) - : mFilename{std::forward(name)}, mEntry{std::forward(entry)} + LoadedHrtf(T&& name, uint srate, U&& entry) + : mFilename{std::forward(name)}, mSampleRate{srate}, mEntry{std::forward(entry)} { } LoadedHrtf(LoadedHrtf&&) = default; /* GCC warns when it tries to inline this. */ @@ -86,12 +95,19 @@ constexpr uint HrirDelayFracBits{2}; constexpr uint HrirDelayFracOne{1 << HrirDelayFracBits}; constexpr uint HrirDelayFracHalf{HrirDelayFracOne >> 1}; +/* The sample rate is stored as a 24-bit integer, so 16MHz is the largest + * supported. + */ +constexpr uint MaxSampleRate{0xff'ff'ff}; + static_assert(MaxHrirDelay*HrirDelayFracOne < 256, "MAX_HRIR_DELAY or DELAY_FRAC too large"); -constexpr char magicMarker00[8]{'M','i','n','P','H','R','0','0'}; -constexpr char magicMarker01[8]{'M','i','n','P','H','R','0','1'}; -constexpr char magicMarker02[8]{'M','i','n','P','H','R','0','2'}; -constexpr char magicMarker03[8]{'M','i','n','P','H','R','0','3'}; + +[[nodiscard]] constexpr auto GetMarker00Name() noexcept { return "MinPHR00"sv; } +[[nodiscard]] constexpr auto GetMarker01Name() noexcept { return "MinPHR01"sv; } +[[nodiscard]] constexpr auto GetMarker02Name() noexcept { return "MinPHR02"sv; } +[[nodiscard]] constexpr auto GetMarker03Name() noexcept { return "MinPHR03"sv; } + /* First value for pass-through coefficients (remaining are 0), used for omni- * directional sounds. */ @@ -104,6 +120,11 @@ std::mutex EnumeratedHrtfLock; std::vector EnumeratedHrtfs; +/* NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + * To access a memory buffer through the std::istream interface, a custom + * std::streambuf implementation is needed that has to do pointer manipulation + * for seeking. With C++23, we may be able to use std::spanstream instead. + */ class databuf final : public std::streambuf { int_type underflow() override { return traits_type::eof(); } @@ -113,34 +134,32 @@ class databuf final : public std::streambuf { if((mode&std::ios_base::out) || !(mode&std::ios_base::in)) return traits_type::eof(); - char_type *cur; switch(whence) { - case std::ios_base::beg: - if(offset < 0 || offset > egptr()-eback()) - return traits_type::eof(); - cur = eback() + offset; - break; - - case std::ios_base::cur: - if((offset >= 0 && offset > egptr()-gptr()) || - (offset < 0 && -offset > gptr()-eback())) - return traits_type::eof(); - cur = gptr() + offset; - break; + case std::ios_base::beg: + if(offset < 0 || offset > egptr()-eback()) + return traits_type::eof(); + setg(eback(), eback()+offset, egptr()); + break; - case std::ios_base::end: - if(offset > 0 || -offset > egptr()-eback()) - return traits_type::eof(); - cur = egptr() + offset; - break; + case std::ios_base::cur: + if((offset >= 0 && offset > egptr()-gptr()) || + (offset < 0 && -offset > gptr()-eback())) + return traits_type::eof(); + setg(eback(), gptr()+offset, egptr()); + break; - default: + case std::ios_base::end: + if(offset > 0 || -offset > egptr()-eback()) return traits_type::eof(); + setg(eback(), egptr()+offset, egptr()); + break; + + default: + return traits_type::eof(); } - setg(eback(), cur, egptr()); - return cur - eback(); + return gptr() - eback(); } pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override @@ -152,24 +171,23 @@ class databuf final : public std::streambuf { if(pos < 0 || pos > egptr()-eback()) return traits_type::eof(); - setg(eback(), eback() + static_cast(pos), egptr()); + setg(eback(), eback()+static_cast(pos), egptr()); return pos; } public: - databuf(const char_type *start_, const char_type *end_) noexcept + databuf(const al::span data) noexcept { - setg(const_cast(start_), const_cast(start_), - const_cast(end_)); + setg(data.data(), data.data(), al::to_address(data.end())); } }; +/* NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ class idstream final : public std::istream { databuf mStreamBuf; public: - idstream(const char *start_, const char *end_) - : std::istream{nullptr}, mStreamBuf{start_, end_} + idstream(const al::span data) : std::istream{nullptr}, mStreamBuf{data} { init(&mStreamBuf); } }; @@ -184,7 +202,7 @@ IdxBlend CalcEvIndex(uint evcount, float ev) al::numbers::inv_pi_v; uint idx{float2uint(ev)}; - return IdxBlend{minu(idx, evcount-1), ev-static_cast(idx)}; + return IdxBlend{std::min(idx, evcount-1u), ev-static_cast(idx)}; } /* Calculate the azimuth index given the polar azimuth in radians. This will @@ -206,7 +224,7 @@ IdxBlend CalcAzIndex(uint azcount, float az) * and azimuth in radians. The coefficients are normalized. */ void HrtfStore::getCoeffs(float elevation, float azimuth, float distance, float spread, - HrirArray &coeffs, const al::span delays) + const HrirSpan coeffs, const al::span delays) const { const float dirfact{1.0f - (al::numbers::inv_pi_v/2.0f * spread)}; @@ -222,7 +240,7 @@ void HrtfStore::getCoeffs(float elevation, float azimuth, float distance, float /* Calculate the elevation indices. */ const auto elev0 = CalcEvIndex(field->evCount, elevation); - const size_t elev1_idx{minu(elev0.idx+1, field->evCount-1)}; + const size_t elev1_idx{std::min(elev0.idx+1u, field->evCount-1u)}; const size_t ir0offset{mElev[ebase + elev0.idx].irOffset}; const size_t ir1offset{mElev[ebase + elev1_idx].irOffset}; @@ -231,43 +249,43 @@ void HrtfStore::getCoeffs(float elevation, float azimuth, float distance, float const auto az1 = CalcAzIndex(mElev[ebase + elev1_idx].azCount, azimuth); /* Calculate the HRIR indices to blend. */ - const size_t idx[4]{ + const std::array idx{{ ir0offset + az0.idx, ir0offset + ((az0.idx+1) % mElev[ebase + elev0.idx].azCount), ir1offset + az1.idx, ir1offset + ((az1.idx+1) % mElev[ebase + elev1_idx].azCount) - }; + }}; /* Calculate bilinear blending weights, attenuated according to the * directional panning factor. */ - const float blend[4]{ + const std::array blend{{ (1.0f-elev0.blend) * (1.0f-az0.blend) * dirfact, (1.0f-elev0.blend) * ( az0.blend) * dirfact, ( elev0.blend) * (1.0f-az1.blend) * dirfact, ( elev0.blend) * ( az1.blend) * dirfact - }; + }}; /* Calculate the blended HRIR delays. */ - float d{mDelays[idx[0]][0]*blend[0] + mDelays[idx[1]][0]*blend[1] + mDelays[idx[2]][0]*blend[2] - + mDelays[idx[3]][0]*blend[3]}; + float d{float(mDelays[idx[0]][0])*blend[0] + float(mDelays[idx[1]][0])*blend[1] + + float(mDelays[idx[2]][0])*blend[2] + float(mDelays[idx[3]][0])*blend[3]}; delays[0] = fastf2u(d * float{1.0f/HrirDelayFracOne}); - d = mDelays[idx[0]][1]*blend[0] + mDelays[idx[1]][1]*blend[1] + mDelays[idx[2]][1]*blend[2] - + mDelays[idx[3]][1]*blend[3]; + d = float(mDelays[idx[0]][1])*blend[0] + float(mDelays[idx[1]][1])*blend[1] + + float(mDelays[idx[2]][1])*blend[2] + float(mDelays[idx[3]][1])*blend[3]; delays[1] = fastf2u(d * float{1.0f/HrirDelayFracOne}); /* Calculate the blended HRIR coefficients. */ - float *coeffout{al::assume_aligned<16>(coeffs[0].data())}; - coeffout[0] = PassthruCoeff * (1.0f-dirfact); - coeffout[1] = PassthruCoeff * (1.0f-dirfact); - std::fill_n(coeffout+2, size_t{HrirLength-1}*2, 0.0f); + auto coeffout = coeffs.begin(); + coeffout[0][0] = PassthruCoeff * (1.0f-dirfact); + coeffout[0][1] = PassthruCoeff * (1.0f-dirfact); + std::fill_n(coeffout+1, size_t{HrirLength-1}, std::array{0.0f, 0.0f}); for(size_t c{0};c < 4;c++) { - const float *srccoeffs{al::assume_aligned<16>(mCoeffs[idx[c]][0].data())}; const float mult{blend[c]}; - auto blend_coeffs = [mult](const float src, const float coeff) noexcept -> float - { return src*mult + coeff; }; - std::transform(srccoeffs, srccoeffs + HrirLength*2, coeffout, coeffout, blend_coeffs); + auto blend_coeffs = [mult](const float2 &src, const float2 &coeff) noexcept -> float2 + { return float2{{src[0]*mult + coeff[0], src[1]*mult + coeff[1]}}; }; + std::transform(mCoeffs[idx[c]].cbegin(), mCoeffs[idx[c]].cend(), coeffout, coeffout, + blend_coeffs); } } @@ -276,7 +294,8 @@ std::unique_ptr DirectHrtfState::Create(size_t num_chans) { return std::unique_ptr{new(FamCount(num_chans)) DirectHrtfState{num_chans}}; } void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool perHrirMin, - const al::span AmbiPoints, const float (*AmbiMatrix)[MaxAmbiChannels], + const al::span AmbiPoints, + const al::span> AmbiMatrix, const float XOverFreq, const al::span AmbiOrderHFGain) { using double2 = std::array; @@ -287,7 +306,8 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool const double xover_norm{double{XOverFreq} / Hrtf->mSampleRate}; mChannels[0].mSplitter.init(static_cast(xover_norm)); - for(size_t i{0};i < mChannels.size();++i) + mChannels[0].mHfScale = AmbiOrderHFGain[0]; + for(size_t i{1};i < mChannels.size();++i) { const size_t order{AmbiIndex::OrderFromChannel[i]}; mChannels[i].mSplitter = mChannels[0].mSplitter; @@ -300,14 +320,14 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool { auto &field = Hrtf->mFields[0]; const auto elev0 = CalcEvIndex(field.evCount, pt.Elev.value); - const size_t elev1_idx{minu(elev0.idx+1, field.evCount-1)}; + const size_t elev1_idx{std::min(elev0.idx+1u, field.evCount-1u)}; const size_t ir0offset{Hrtf->mElev[elev0.idx].irOffset}; const size_t ir1offset{Hrtf->mElev[elev1_idx].irOffset}; const auto az0 = CalcAzIndex(Hrtf->mElev[elev0.idx].azCount, pt.Azim.value); const auto az1 = CalcAzIndex(Hrtf->mElev[elev1_idx].azCount, pt.Azim.value); - const size_t idx[4]{ + const std::array idx{ ir0offset + az0.idx, ir0offset + ((az0.idx+1) % Hrtf->mElev[elev0.idx].azCount), ir1offset + az1.idx, @@ -319,8 +339,8 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool ImpulseResponse res{Hrtf->mCoeffs[irOffset], Hrtf->mDelays[irOffset][0], Hrtf->mDelays[irOffset][1]}; - min_delay = minu(min_delay, minu(res.ldelay, res.rdelay)); - max_delay = maxu(max_delay, maxu(res.ldelay, res.rdelay)); + min_delay = std::min(min_delay, std::min(res.ldelay, res.rdelay)); + max_delay = std::max(max_delay, std::max(res.ldelay, res.rdelay)); return res; }; @@ -333,38 +353,42 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool auto tmpres = std::vector>(mChannels.size()); max_delay = 0; - for(size_t c{0u};c < AmbiPoints.size();++c) + auto matrixline = AmbiMatrix.cbegin(); + for(auto &impulse : impres) { - const ConstHrirSpan hrir{impres[c].hrir}; - const uint base_delay{perHrirMin ? minu(impres[c].ldelay, impres[c].rdelay) : min_delay}; - const uint ldelay{hrir_delay_round(impres[c].ldelay - base_delay)}; - const uint rdelay{hrir_delay_round(impres[c].rdelay - base_delay)}; - max_delay = maxu(max_delay, maxu(impres[c].ldelay, impres[c].rdelay) - base_delay); - - for(size_t i{0u};i < mChannels.size();++i) + const ConstHrirSpan hrir{impulse.hrir}; + const uint base_delay{perHrirMin ? std::min(impulse.ldelay, impulse.rdelay) : min_delay}; + const uint ldelay{hrir_delay_round(impulse.ldelay - base_delay)}; + const uint rdelay{hrir_delay_round(impulse.rdelay - base_delay)}; + max_delay = std::max(max_delay, std::max(impulse.ldelay, impulse.rdelay) - base_delay); + + auto gains = matrixline->cbegin(); + ++matrixline; + for(auto &result : tmpres) { - const double mult{AmbiMatrix[c][i]}; - const size_t numirs{HrirLength - maxz(ldelay, rdelay)}; + const double mult{*(gains++)}; + const size_t numirs{HrirLength - std::max(ldelay, rdelay)}; size_t lidx{ldelay}, ridx{rdelay}; for(size_t j{0};j < numirs;++j) { - tmpres[i][lidx++][0] += hrir[j][0] * mult; - tmpres[i][ridx++][1] += hrir[j][1] * mult; + result[lidx++][0] += hrir[j][0] * mult; + result[ridx++][1] += hrir[j][1] * mult; } } } impres.clear(); - for(size_t i{0u};i < mChannels.size();++i) + auto output = mChannels.begin(); + for(auto &result : tmpres) { - auto copy_arr = [](const double2 &in) noexcept -> float2 + auto cast_array2 = [](const double2 &in) noexcept -> float2 { return float2{{static_cast(in[0]), static_cast(in[1])}}; }; - std::transform(tmpres[i].cbegin(), tmpres[i].cend(), mChannels[i].mCoeffs.begin(), - copy_arr); + std::transform(result.cbegin(), result.cend(), output->mCoeffs.begin(), cast_array2); + ++output; } tmpres.clear(); - const uint max_length{minu(hrir_delay_round(max_delay) + irSize, HrirLength)}; + const uint max_length{std::min(hrir_delay_round(max_delay) + irSize, HrirLength)}; TRACE("New max delay: %.2f, FIR length: %u\n", max_delay/double{HrirDelayFracOne}, max_length); mIrSize = max_length; @@ -376,8 +400,15 @@ namespace { std::unique_ptr CreateHrtfStore(uint rate, uint8_t irSize, const al::span fields, const al::span elevs, const HrirArray *coeffs, - const ubyte2 *delays, const char *filename) + const ubyte2 *delays) { + static_assert(alignof(HrtfStore::Field) <= alignof(HrtfStore)); + static_assert(alignof(HrtfStore::Elevation) <= alignof(HrtfStore)); + static_assert(16 <= alignof(HrtfStore)); + + if(rate > MaxSampleRate) + throw std::runtime_error{"Sample rate is too large (max: "+std::to_string(MaxSampleRate)+"hz)"}; + const size_t irCount{size_t{elevs.back().azCount} + elevs.back().irOffset}; size_t total{sizeof(HrtfStore)}; total = RoundUp(total, alignof(HrtfStore::Field)); /* Align for field infos */ @@ -388,56 +419,54 @@ std::unique_ptr CreateHrtfStore(uint rate, uint8_t irSize, total += sizeof(std::declval().mCoeffs[0])*irCount; total += sizeof(std::declval().mDelays[0])*irCount; - std::unique_ptr Hrtf{}; - if(void *ptr{al_calloc(16, total)}) - { - Hrtf.reset(al::construct_at(static_cast(ptr))); - InitRef(Hrtf->mRef, 1u); - Hrtf->mSampleRate = rate & 0xff'ff'ff; - Hrtf->mIrSize = irSize; - - /* Set up pointers to storage following the main HRTF struct. */ - char *base = reinterpret_cast(Hrtf.get()); - size_t offset{sizeof(HrtfStore)}; - - offset = RoundUp(offset, alignof(HrtfStore::Field)); /* Align for field infos */ - auto field_ = reinterpret_cast(base + offset); - offset += sizeof(field_[0])*fields.size(); - - offset = RoundUp(offset, alignof(HrtfStore::Elevation)); /* Align for elevation infos */ - auto elev_ = reinterpret_cast(base + offset); - offset += sizeof(elev_[0])*elevs.size(); - - offset = RoundUp(offset, 16); /* Align for coefficients using SIMD */ - auto coeffs_ = reinterpret_cast(base + offset); - offset += sizeof(coeffs_[0])*irCount; - - auto delays_ = reinterpret_cast(base + offset); - offset += sizeof(delays_[0])*irCount; - - if(offset != total) - throw std::runtime_error{"HrtfStore allocation size mismatch"}; - - /* Copy input data to storage. */ - std::uninitialized_copy(fields.cbegin(), fields.cend(), field_); - std::uninitialized_copy(elevs.cbegin(), elevs.cend(), elev_); - std::uninitialized_copy_n(coeffs, irCount, coeffs_); - std::uninitialized_copy_n(delays, irCount, delays_); - - /* Finally, assign the storage pointers. */ - Hrtf->mFields = {field_, fields.size()}; - Hrtf->mElev = elev_; - Hrtf->mCoeffs = coeffs_; - Hrtf->mDelays = delays_; - } - else - ERR("Out of memory allocating storage for %s.\n", filename); + static constexpr auto AlignVal = std::align_val_t{alignof(HrtfStore)}; + std::unique_ptr Hrtf{::new(::operator new[](total, AlignVal)) HrtfStore{}}; + Hrtf->mRef.store(1u, std::memory_order_relaxed); + Hrtf->mSampleRate = rate & 0xff'ff'ff; + Hrtf->mIrSize = irSize; + + /* Set up pointers to storage following the main HRTF struct. */ + auto storage = al::span{reinterpret_cast(Hrtf.get()), total}; + auto base = storage.begin(); + ptrdiff_t offset{sizeof(HrtfStore)}; + + offset = RoundUp(offset, alignof(HrtfStore::Field)); /* Align for field infos */ + auto field_ = al::span{reinterpret_cast(al::to_address(base + offset)), + fields.size()}; + offset += ptrdiff_t(sizeof(field_[0])*fields.size()); + + offset = RoundUp(offset, alignof(HrtfStore::Elevation)); /* Align for elevation infos */ + auto elev_ = al::span{reinterpret_cast(al::to_address(base + offset)), + elevs.size()}; + offset += ptrdiff_t(sizeof(elev_[0])*elevs.size()); + + offset = RoundUp(offset, 16); /* Align for coefficients using SIMD */ + auto coeffs_ = al::span{reinterpret_cast(al::to_address(base + offset)), irCount}; + offset += ptrdiff_t(sizeof(coeffs_[0])*irCount); + + auto delays_ = al::span{reinterpret_cast(al::to_address(base + offset)), irCount}; + offset += ptrdiff_t(sizeof(delays_[0])*irCount); + + if(size_t(offset) != total) + throw std::runtime_error{"HrtfStore allocation size mismatch"}; + + /* Copy input data to storage. */ + std::uninitialized_copy(fields.cbegin(), fields.cend(), field_.begin()); + std::uninitialized_copy(elevs.cbegin(), elevs.cend(), elev_.begin()); + std::uninitialized_copy_n(coeffs, irCount, coeffs_.begin()); + std::uninitialized_copy_n(delays, irCount, delays_.begin()); + + /* Finally, assign the storage pointers. */ + Hrtf->mFields = field_; + Hrtf->mElev = elev_; + Hrtf->mCoeffs = coeffs_; + Hrtf->mDelays = delays_; return Hrtf; } -void MirrorLeftHrirs(const al::span elevs, HrirArray *coeffs, - ubyte2 *delays) +void MirrorLeftHrirs(const al::span elevs, al::span coeffs, + al::span delays) { for(const auto &elev : elevs) { @@ -477,11 +506,11 @@ T> readle(std::istream &data) static_assert((num_bits&7) == 0, "num_bits must be a multiple of 8"); static_assert(num_bits <= sizeof(T)*8, "num_bits is too large for the type"); - T ret{}; - if(!data.read(reinterpret_cast(&ret), num_bits/8)) + alignas(T) std::array ret{}; + if(!data.read(ret.data(), num_bits/8)) return static_cast(EOF); - return fixsign(ret); + return fixsign(al::bit_cast(ret)); } template @@ -491,13 +520,12 @@ T> readle(std::istream &data) static_assert((num_bits&7) == 0, "num_bits must be a multiple of 8"); static_assert(num_bits <= sizeof(T)*8, "num_bits is too large for the type"); - T ret{}; - std::byte b[sizeof(T)]{}; - if(!data.read(reinterpret_cast(b), num_bits/8)) + alignas(T) std::array ret{}; + if(!data.read(ret.data(), num_bits/8)) return static_cast(EOF); - std::reverse_copy(std::begin(b), std::end(b), reinterpret_cast(&ret)); + std::reverse(ret.begin(), ret.end()); - return fixsign(ret); + return fixsign(al::bit_cast(ret)); } template<> @@ -505,17 +533,14 @@ inline uint8_t readle(std::istream &data) { return static_cast(data.get()); } -std::unique_ptr LoadHrtf00(std::istream &data, const char *filename) +std::unique_ptr LoadHrtf00(std::istream &data) { uint rate{readle(data)}; ushort irCount{readle(data)}; ushort irSize{readle(data)}; ubyte evCount{readle(data)}; if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; if(irSize < MinIrLength || irSize > HrirLength) { @@ -533,10 +558,8 @@ std::unique_ptr LoadHrtf00(std::istream &data, const char *filename) for(auto &elev : elevs) elev.irOffset = readle(data); if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; + for(size_t i{1};i < evCount;i++) { if(elevs[i].irOffset <= elevs[i-1].irOffset) @@ -575,16 +598,14 @@ std::unique_ptr LoadHrtf00(std::istream &data, const char *filename) auto delays = std::vector(irCount); for(auto &hrir : coeffs) { - for(auto &val : al::span{hrir.data(), irSize}) - val[0] = readle(data) / 32768.0f; + for(auto &val : al::span{hrir}.first(irSize)) + val[0] = float(readle(data)) / 32768.0f; } for(auto &val : delays) val[0] = readle(data); if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; + for(size_t i{0};i < irCount;i++) { if(delays[i][0] > MaxHrirDelay) @@ -596,23 +617,20 @@ std::unique_ptr LoadHrtf00(std::istream &data, const char *filename) } /* Mirror the left ear responses to the right ear. */ - MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data()); + MirrorLeftHrirs(elevs, coeffs, delays); - const HrtfStore::Field field[1]{{0.0f, evCount}}; - return CreateHrtfStore(rate, static_cast(irSize), field, {elevs.data(), elevs.size()}, - coeffs.data(), delays.data(), filename); + const std::array field{HrtfStore::Field{0.0f, evCount}}; + return CreateHrtfStore(rate, static_cast(irSize), field, elevs, coeffs.data(), + delays.data()); } -std::unique_ptr LoadHrtf01(std::istream &data, const char *filename) +std::unique_ptr LoadHrtf01(std::istream &data) { uint rate{readle(data)}; uint8_t irSize{readle(data)}; ubyte evCount{readle(data)}; if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; if(irSize < MinIrLength || irSize > HrirLength) { @@ -630,10 +648,8 @@ std::unique_ptr LoadHrtf01(std::istream &data, const char *filename) for(auto &elev : elevs) elev.azCount = readle(data); if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; + for(size_t i{0};i < evCount;++i) { if(elevs[i].azCount < MinAzCount || elevs[i].azCount > MaxAzCount) @@ -653,16 +669,14 @@ std::unique_ptr LoadHrtf01(std::istream &data, const char *filename) auto delays = std::vector(irCount); for(auto &hrir : coeffs) { - for(auto &val : al::span{hrir.data(), irSize}) - val[0] = readle(data) / 32768.0f; + for(auto &val : al::span{hrir}.first(irSize)) + val[0] = float(readle(data)) / 32768.0f; } for(auto &val : delays) val[0] = readle(data); if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; + for(size_t i{0};i < irCount;i++) { if(delays[i][0] > MaxHrirDelay) @@ -674,19 +688,18 @@ std::unique_ptr LoadHrtf01(std::istream &data, const char *filename) } /* Mirror the left ear responses to the right ear. */ - MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data()); + MirrorLeftHrirs(elevs, coeffs, delays); - const HrtfStore::Field field[1]{{0.0f, evCount}}; - return CreateHrtfStore(rate, irSize, field, {elevs.data(), elevs.size()}, coeffs.data(), - delays.data(), filename); + const std::array field{HrtfStore::Field{0.0f, evCount}}; + return CreateHrtfStore(rate, irSize, field, elevs, coeffs.data(), delays.data()); } -std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) +std::unique_ptr LoadHrtf02(std::istream &data) { - constexpr ubyte SampleType_S16{0}; - constexpr ubyte SampleType_S24{1}; - constexpr ubyte ChanType_LeftOnly{0}; - constexpr ubyte ChanType_LeftRight{1}; + static constexpr ubyte SampleType_S16{0}; + static constexpr ubyte SampleType_S24{1}; + static constexpr ubyte ChanType_LeftOnly{0}; + static constexpr ubyte ChanType_LeftRight{1}; uint rate{readle(data)}; ubyte sampleType{readle(data)}; @@ -694,10 +707,7 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) uint8_t irSize{readle(data)}; ubyte fdCount{readle(data)}; if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; if(sampleType > SampleType_S24) { @@ -729,10 +739,7 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) const ushort distance{readle(data)}; const ubyte evCount{readle(data)}; if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; if(distance < MinFdDistance || distance > MaxFdDistance) { @@ -747,7 +754,7 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) return nullptr; } - fields[f].distance = distance / 1000.0f; + fields[f].distance = float(distance) / 1000.0f; fields[f].evCount = evCount; if(f > 0 && fields[f].distance <= fields[f-1].distance) { @@ -758,13 +765,10 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) const size_t ebase{elevs.size()}; elevs.resize(ebase + evCount); - for(auto &elev : al::span(elevs.data()+ebase, evCount)) + for(auto &elev : al::span{elevs}.subspan(ebase, evCount)) elev.azCount = readle(data); if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; for(size_t e{0};e < evCount;e++) { @@ -795,25 +799,23 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) { for(auto &hrir : coeffs) { - for(auto &val : al::span{hrir.data(), irSize}) - val[0] = readle(data) / 32768.0f; + for(auto &val : al::span{hrir}.first(irSize)) + val[0] = float(readle(data)) / 32768.0f; } } else if(sampleType == SampleType_S24) { for(auto &hrir : coeffs) { - for(auto &val : al::span{hrir.data(), irSize}) + for(auto &val : al::span{hrir}.first(irSize)) val[0] = static_cast(readle(data)) / 8388608.0f; } } for(auto &val : delays) val[0] = readle(data); if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; + for(size_t i{0};i < irTotal;++i) { if(delays[i][0] > MaxHrirDelay) @@ -825,7 +827,7 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) } /* Mirror the left ear responses to the right ear. */ - MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data()); + MirrorLeftHrirs(elevs, coeffs, delays); } else if(channelType == ChanType_LeftRight) { @@ -833,10 +835,10 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) { for(auto &hrir : coeffs) { - for(auto &val : al::span{hrir.data(), irSize}) + for(auto &val : al::span{hrir}.first(irSize)) { - val[0] = readle(data) / 32768.0f; - val[1] = readle(data) / 32768.0f; + val[0] = float(readle(data)) / 32768.0f; + val[1] = float(readle(data)) / 32768.0f; } } } @@ -844,7 +846,7 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) { for(auto &hrir : coeffs) { - for(auto &val : al::span{hrir.data(), irSize}) + for(auto &val : al::span{hrir}.first(irSize)) { val[0] = static_cast(readle(data)) / 8388608.0f; val[1] = static_cast(readle(data)) / 8388608.0f; @@ -857,10 +859,7 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) val[1] = readle(data); } if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; for(size_t i{0};i < irTotal;++i) { @@ -893,16 +892,16 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) * count. Reverse the order of the groups, keeping the relative order * of per-group azimuth counts. */ - auto elevs__end = elevs_.end(); - auto copy_azs = [&elevs,&elevs__end](const ptrdiff_t ebase, const HrtfStore::Field &field) + auto elevs_end = elevs_.end(); + auto copy_azs = [&elevs,&elevs_end](const ptrdiff_t ebase, const HrtfStore::Field &field) -> ptrdiff_t { auto elevs_src = elevs.begin()+ebase; - elevs__end = std::copy_backward(elevs_src, elevs_src+field.evCount, elevs__end); + elevs_end = std::copy_backward(elevs_src, elevs_src+field.evCount, elevs_end); return ebase + field.evCount; }; - (void)std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_azs); - assert(elevs_.begin() == elevs__end); + std::ignore = std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_azs); + assert(elevs_.begin() == elevs_end); /* Reestablish the IR offset for each elevation index, given the new * ordering of elevations. @@ -922,12 +921,13 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) auto copy_irs = [&elevs,&coeffs,&delays,&coeffs_end,&delays_end]( const ptrdiff_t ebase, const HrtfStore::Field &field) -> ptrdiff_t { - auto accum_az = [](int count, const HrtfStore::Elevation &elev) noexcept -> int + auto accum_az = [](const ptrdiff_t count, const HrtfStore::Elevation &elev) noexcept + -> ptrdiff_t { return count + elev.azCount; }; - const auto elevs_mid = elevs.cbegin() + ebase; - const auto elevs_end = elevs_mid + field.evCount; - const int abase{std::accumulate(elevs.cbegin(), elevs_mid, 0, accum_az)}; - const int num_azs{std::accumulate(elevs_mid, elevs_end, 0, accum_az)}; + const auto elev_mid = elevs.cbegin() + ebase; + const auto abase = std::accumulate(elevs.cbegin(), elev_mid, ptrdiff_t{0}, accum_az); + const auto num_azs = std::accumulate(elev_mid, elev_mid + field.evCount, ptrdiff_t{0}, + accum_az); coeffs_end = std::copy_backward(coeffs.cbegin() + abase, coeffs.cbegin() + (abase+num_azs), coeffs_end); @@ -936,7 +936,7 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) return ebase + field.evCount; }; - (void)std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_irs); + std::ignore = std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_irs); assert(coeffs_.begin() == coeffs_end); assert(delays_.begin() == delays_end); @@ -946,24 +946,20 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) delays = std::move(delays_); } - return CreateHrtfStore(rate, irSize, {fields.data(), fields.size()}, - {elevs.data(), elevs.size()}, coeffs.data(), delays.data(), filename); + return CreateHrtfStore(rate, irSize, fields, elevs, coeffs.data(), delays.data()); } -std::unique_ptr LoadHrtf03(std::istream &data, const char *filename) +std::unique_ptr LoadHrtf03(std::istream &data) { - constexpr ubyte ChanType_LeftOnly{0}; - constexpr ubyte ChanType_LeftRight{1}; + static constexpr ubyte ChanType_LeftOnly{0}; + static constexpr ubyte ChanType_LeftRight{1}; uint rate{readle(data)}; ubyte channelType{readle(data)}; uint8_t irSize{readle(data)}; ubyte fdCount{readle(data)}; if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; if(channelType > ChanType_LeftRight) { @@ -990,10 +986,7 @@ std::unique_ptr LoadHrtf03(std::istream &data, const char *filename) const ushort distance{readle(data)}; const ubyte evCount{readle(data)}; if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; if(distance < MinFdDistance || distance > MaxFdDistance) { @@ -1008,7 +1001,7 @@ std::unique_ptr LoadHrtf03(std::istream &data, const char *filename) return nullptr; } - fields[f].distance = distance / 1000.0f; + fields[f].distance = float(distance) / 1000.0f; fields[f].evCount = evCount; if(f > 0 && fields[f].distance > fields[f-1].distance) { @@ -1019,13 +1012,10 @@ std::unique_ptr LoadHrtf03(std::istream &data, const char *filename) const size_t ebase{elevs.size()}; elevs.resize(ebase + evCount); - for(auto &elev : al::span(elevs.data()+ebase, evCount)) + for(auto &elev : al::span{elevs}.subspan(ebase, evCount)) elev.azCount = readle(data); if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; for(size_t e{0};e < evCount;e++) { @@ -1054,16 +1044,14 @@ std::unique_ptr LoadHrtf03(std::istream &data, const char *filename) { for(auto &hrir : coeffs) { - for(auto &val : al::span{hrir.data(), irSize}) + for(auto &val : al::span{hrir}.first(irSize)) val[0] = static_cast(readle(data)) / 8388608.0f; } for(auto &val : delays) val[0] = readle(data); if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; + for(size_t i{0};i < irTotal;++i) { if(delays[i][0] > MaxHrirDelay< LoadHrtf03(std::istream &data, const char *filename) } /* Mirror the left ear responses to the right ear. */ - MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data()); + MirrorLeftHrirs(elevs, coeffs, delays); } else if(channelType == ChanType_LeftRight) { for(auto &hrir : coeffs) { - for(auto &val : al::span{hrir.data(), irSize}) + for(auto &val : al::span{hrir}.first(irSize)) { val[0] = static_cast(readle(data)) / 8388608.0f; val[1] = static_cast(readle(data)) / 8388608.0f; @@ -1093,10 +1081,7 @@ std::unique_ptr LoadHrtf03(std::istream &data, const char *filename) val[1] = readle(data); } if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; for(size_t i{0};i < irTotal;++i) { @@ -1115,39 +1100,38 @@ std::unique_ptr LoadHrtf03(std::istream &data, const char *filename) } } - return CreateHrtfStore(rate, irSize, {fields.data(), fields.size()}, - {elevs.data(), elevs.size()}, coeffs.data(), delays.data(), filename); + return CreateHrtfStore(rate, irSize, fields, elevs, coeffs.data(), delays.data()); } -bool checkName(const std::string &name) +bool checkName(const std::string_view name) { - auto match_name = [&name](const HrtfEntry &entry) -> bool { return name == entry.mDispName; }; + auto match_name = [name](const HrtfEntry &entry) -> bool { return name == entry.mDispName; }; auto &enum_names = EnumeratedHrtfs; return std::find_if(enum_names.cbegin(), enum_names.cend(), match_name) != enum_names.cend(); } -void AddFileEntry(const std::string &filename) +void AddFileEntry(const std::string_view filename) { /* Check if this file has already been enumerated. */ auto enum_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(), - [&filename](const HrtfEntry &entry) -> bool + [filename](const HrtfEntry &entry) -> bool { return entry.mFilename == filename; }); if(enum_iter != EnumeratedHrtfs.cend()) { - TRACE("Skipping duplicate file entry %s\n", filename.c_str()); + TRACE("Skipping duplicate file entry %.*s\n", al::sizei(filename), filename.data()); return; } /* TODO: Get a human-readable name from the HRTF data (possibly coming in a * format update). */ - size_t namepos{filename.find_last_of('/')+1}; - if(!namepos) namepos = filename.find_last_of('\\')+1; + size_t namepos{filename.rfind('/')+1}; + if(!namepos) namepos = filename.rfind('\\')+1; - size_t extpos{filename.find_last_of('.')}; + size_t extpos{filename.rfind('.')}; if(extpos <= namepos) extpos = std::string::npos; - const std::string basename{(extpos == std::string::npos) ? + const std::string_view basename{(extpos == std::string::npos) ? filename.substr(namepos) : filename.substr(namepos, extpos-namepos)}; std::string newname{basename}; int count{1}; @@ -1157,8 +1141,7 @@ void AddFileEntry(const std::string &filename) newname += " #"; newname += std::to_string(++count); } - EnumeratedHrtfs.emplace_back(HrtfEntry{newname, filename}); - const HrtfEntry &entry = EnumeratedHrtfs.back(); + const HrtfEntry &entry = EnumeratedHrtfs.emplace_back(newname, filename); TRACE("Adding file entry \"%s\"\n", entry.mFilename.c_str()); } @@ -1166,9 +1149,10 @@ void AddFileEntry(const std::string &filename) /* Unfortunate that we have to duplicate AddFileEntry to take a memory buffer * for input instead of opening the given filename. */ -void AddBuiltInEntry(const std::string &dispname, uint residx) +void AddBuiltInEntry(const std::string_view dispname, uint residx) { - const std::string filename{'!'+std::to_string(residx)+'_'+dispname}; + std::string filename{'!'+std::to_string(residx)+'_'}; + filename += dispname; auto enum_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(), [&filename](const HrtfEntry &entry) -> bool @@ -1190,8 +1174,7 @@ void AddBuiltInEntry(const std::string &dispname, uint residx) newname += " #"; newname += std::to_string(++count); } - EnumeratedHrtfs.emplace_back(HrtfEntry{newname, filename}); - const HrtfEntry &entry = EnumeratedHrtfs.back(); + const HrtfEntry &entry = EnumeratedHrtfs.emplace_back(std::move(newname), std::move(filename)); TRACE("Adding built-in entry \"%s\"\n", entry.mFilename.c_str()); } @@ -1206,6 +1189,7 @@ al::span GetResource(int /*name*/) #else +/* NOLINTNEXTLINE(*-avoid-c-arrays) */ constexpr unsigned char hrtf_default[]{ #include "default_hrtf.txt" }; @@ -1223,47 +1207,46 @@ al::span GetResource(int name) std::vector EnumerateHrtf(std::optional pathopt) { - std::lock_guard _{EnumeratedHrtfLock}; + std::lock_guard enumlock{EnumeratedHrtfLock}; EnumeratedHrtfs.clear(); + for(const auto &fname : SearchDataFiles(".mhr"sv)) + AddFileEntry(fname); + bool usedefaults{true}; if(pathopt) { - const char *pathlist{pathopt->c_str()}; - while(pathlist && *pathlist) + std::string_view pathlist{*pathopt}; + while(!pathlist.empty()) { - const char *next, *end; - - while(isspace(*pathlist) || *pathlist == ',') - pathlist++; - if(*pathlist == '\0') - continue; + while(!pathlist.empty() && (std::isspace(pathlist.front()) || pathlist.front() == ',')) + pathlist.remove_prefix(1); + if(pathlist.empty()) + break; - next = strchr(pathlist, ','); - if(next) - end = next++; + auto endpos = std::min(pathlist.find(','), pathlist.size()); + auto entry = pathlist.substr(0, endpos); + if(endpos < pathlist.size()) + pathlist.remove_prefix(++endpos); else { - end = pathlist + strlen(pathlist); + pathlist.remove_prefix(endpos); usedefaults = false; } - while(end != pathlist && isspace(*(end-1))) - --end; - if(end != pathlist) + while(!entry.empty() && std::isspace(entry.back())) + entry.remove_suffix(1); + if(!entry.empty()) { - const std::string pname{pathlist, end}; - for(const auto &fname : SearchDataFiles(".mhr", pname.c_str())) + for(const auto &fname : SearchDataFiles(".mhr"sv, entry)) AddFileEntry(fname); } - - pathlist = next; } } if(usedefaults) { - for(const auto &fname : SearchDataFiles(".mhr", "openal/hrtf")) + for(const auto &fname : SearchDataFiles(".mhr"sv, "openal/hrtf"sv)) AddFileEntry(fname); if(!GetResource(IDR_DEFAULT_HRTF_MHR).empty()) @@ -1278,28 +1261,35 @@ std::vector EnumerateHrtf(std::optional pathopt) return list; } -HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate) -{ - std::lock_guard _{EnumeratedHrtfLock}; +HrtfStorePtr GetLoadedHrtf(const std::string_view name, const uint devrate) +try { + if(devrate > MaxSampleRate) + { + WARN("Device sample rate too large for HRTF (%uhz > %uhz)\n", devrate, MaxSampleRate); + return nullptr; + } + std::lock_guard enumlock{EnumeratedHrtfLock}; auto entry_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(), - [&name](const HrtfEntry &entry) -> bool { return entry.mDispName == name; }); + [name](const HrtfEntry &entry) -> bool { return entry.mDispName == name; }); if(entry_iter == EnumeratedHrtfs.cend()) return nullptr; const std::string &fname = entry_iter->mFilename; - std::lock_guard __{LoadedHrtfLock}; - auto hrtf_lt_fname = [](LoadedHrtf &hrtf, const std::string &filename) -> bool - { return hrtf.mFilename < filename; }; + std::lock_guard loadlock{LoadedHrtfLock}; + auto hrtf_lt_fname = [devrate](LoadedHrtf &hrtf, const std::string_view filename) -> bool + { + return hrtf.mSampleRate < devrate + || (hrtf.mSampleRate == devrate && hrtf.mFilename < filename); + }; auto handle = std::lower_bound(LoadedHrtfs.begin(), LoadedHrtfs.end(), fname, hrtf_lt_fname); - while(handle != LoadedHrtfs.end() && handle->mFilename == fname) + if(handle != LoadedHrtfs.end() && handle->mSampleRate == devrate && handle->mFilename == fname) { - HrtfStore *hrtf{handle->mEntry.get()}; - if(hrtf && hrtf->mSampleRate == devrate) + if(HrtfStore *hrtf{handle->mEntry.get()}) { + assert(hrtf->mSampleRate == devrate); hrtf->add_ref(); return HrtfStorePtr{hrtf}; } - ++handle; } std::unique_ptr stream; @@ -1311,15 +1301,17 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate) al::span res{GetResource(residx)}; if(res.empty()) { - ERR("Could not get resource %u, %s\n", residx, name.c_str()); + ERR("Could not get resource %u, %.*s\n", residx, al::sizei(name), name.data()); return nullptr; } - stream = std::make_unique(res.begin(), res.end()); + /* NOLINTNEXTLINE(*-const-cast) */ + stream = std::make_unique(al::span{const_cast(res.data()), res.size()}); } else { TRACE("Loading %s...\n", fname.c_str()); - auto fstr = std::make_unique(fname.c_str(), std::ios::binary); + auto fstr = std::make_unique(std::filesystem::u8path(fname), + std::ios::binary); if(!fstr->is_open()) { ERR("Could not open %s\n", fname.c_str()); @@ -1329,43 +1321,41 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate) } std::unique_ptr hrtf; - char magic[sizeof(magicMarker03)]; - stream->read(magic, sizeof(magic)); - if(stream->gcount() < static_cast(sizeof(magicMarker03))) - ERR("%s data is too short (%zu bytes)\n", name.c_str(), stream->gcount()); - else if(memcmp(magic, magicMarker03, sizeof(magicMarker03)) == 0) + std::array magic{}; + stream->read(magic.data(), magic.size()); + if(stream->gcount() < static_cast(GetMarker03Name().size())) + ERR("%.*s data is too short (%zu bytes)\n", al::sizei(name),name.data(), stream->gcount()); + else if(GetMarker03Name() == std::string_view{magic.data(), magic.size()}) { TRACE("Detected data set format v3\n"); - hrtf = LoadHrtf03(*stream, name.c_str()); + hrtf = LoadHrtf03(*stream); } - else if(memcmp(magic, magicMarker02, sizeof(magicMarker02)) == 0) + else if(GetMarker02Name() == std::string_view{magic.data(), magic.size()}) { TRACE("Detected data set format v2\n"); - hrtf = LoadHrtf02(*stream, name.c_str()); + hrtf = LoadHrtf02(*stream); } - else if(memcmp(magic, magicMarker01, sizeof(magicMarker01)) == 0) + else if(GetMarker01Name() == std::string_view{magic.data(), magic.size()}) { TRACE("Detected data set format v1\n"); - hrtf = LoadHrtf01(*stream, name.c_str()); + hrtf = LoadHrtf01(*stream); } - else if(memcmp(magic, magicMarker00, sizeof(magicMarker00)) == 0) + else if(GetMarker00Name() == std::string_view{magic.data(), magic.size()}) { TRACE("Detected data set format v0\n"); - hrtf = LoadHrtf00(*stream, name.c_str()); + hrtf = LoadHrtf00(*stream); } else - ERR("Invalid header in %s: \"%.8s\"\n", name.c_str(), magic); + ERR("Invalid header in %.*s: \"%.8s\"\n", al::sizei(name), name.data(), magic.data()); stream.reset(); if(!hrtf) - { - ERR("Failed to load %s\n", name.c_str()); return nullptr; - } if(hrtf->mSampleRate != devrate) { - TRACE("Resampling HRTF %s (%uhz -> %uhz)\n", name.c_str(), hrtf->mSampleRate, devrate); + TRACE("Resampling HRTF %.*s (%uhz -> %uhz)\n", al::sizei(name), name.data(), + 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(), 0_uz, @@ -1375,17 +1365,18 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate) const size_t irCount{size_t{hrtf->mElev[lastEv].irOffset} + hrtf->mElev[lastEv].azCount}; /* Resample all the IRs. */ - std::array,2> inout; + std::array,2> inout{}; PPhaseResampler rs; rs.init(hrtf->mSampleRate, devrate); for(size_t i{0};i < irCount;++i) { - HrirArray &coeffs = const_cast(hrtf->mCoeffs[i]); + /* NOLINTNEXTLINE(*-const-cast) */ + auto coeffs = al::span{const_cast(hrtf->mCoeffs[i])}; for(size_t j{0};j < 2;++j) { std::transform(coeffs.cbegin(), coeffs.cend(), inout[0].begin(), [j](const float2 &in) noexcept -> double { return in[j]; }); - rs.process(HrirLength, inout[0].data(), HrirLength, inout[1].data()); + rs.process(inout[0], inout[1]); for(size_t k{0};k < HrirLength;++k) coeffs[k][j] = static_cast(inout[1][k]); } @@ -1400,9 +1391,9 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate) { for(size_t j{0};j < 2;++j) { - const float new_delay{std::round(hrtf->mDelays[i][j] * rate_scale) / + const float new_delay{std::round(float(hrtf->mDelays[i][j]) * rate_scale) / float{HrirDelayFracOne}}; - max_delay = maxf(max_delay, new_delay); + max_delay = std::max(max_delay, new_delay); new_delays[i][j] = new_delay; } } @@ -1420,25 +1411,31 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate) for(size_t i{0};i < irCount;++i) { - ubyte2 &delays = const_cast(hrtf->mDelays[i]); - for(size_t j{0};j < 2;++j) - delays[j] = static_cast(float2int(new_delays[i][j]*delay_scale + 0.5f)); + /* NOLINTNEXTLINE(*-const-cast) */ + auto delays = al::span{const_cast(hrtf->mDelays[i])}; + std::transform(new_delays[i].cbegin(), new_delays[i].cend(), delays.begin(), + [delay_scale](const float delay) + { return static_cast(float2int(delay*delay_scale + 0.5f)); }); } /* Scale the IR size for the new sample rate and update the stored * sample rate. */ const float newIrSize{std::round(static_cast(hrtf->mIrSize) * rate_scale)}; - hrtf->mIrSize = static_cast(minf(HrirLength, newIrSize)); + hrtf->mIrSize = static_cast(std::min(float{HrirLength}, newIrSize)); hrtf->mSampleRate = devrate & 0xff'ff'ff; } - TRACE("Loaded HRTF %s for sample rate %uhz, %u-sample filter\n", name.c_str(), - hrtf->mSampleRate, hrtf->mIrSize); - handle = LoadedHrtfs.emplace(handle, fname, std::move(hrtf)); + handle = LoadedHrtfs.emplace(handle, fname, devrate, std::move(hrtf)); + TRACE("Loaded HRTF %.*s for sample rate %uhz, %u-sample filter\n", al::sizei(name),name.data(), + handle->mEntry->mSampleRate, handle->mEntry->mIrSize); return HrtfStorePtr{handle->mEntry.get()}; } +catch(std::exception& e) { + ERR("Failed to load %.*s: %s\n", al::sizei(name), name.data(), e.what()); + return nullptr; +} void HrtfStore::add_ref() @@ -1453,15 +1450,15 @@ void HrtfStore::dec_ref() TRACE("HrtfStore %p decreasing refcount to %u\n", decltype(std::declval()){this}, ref); if(ref == 0) { - std::lock_guard _{LoadedHrtfLock}; + std::lock_guard loadlock{LoadedHrtfLock}; /* Go through and remove all unused HRTFs. */ auto remove_unused = [](LoadedHrtf &hrtf) -> bool { HrtfStore *entry{hrtf.mEntry.get()}; - if(entry && ReadRef(entry->mRef) == 0) + if(entry && entry->mRef.load() == 0) { - TRACE("Unloading unused HRTF %s\n", hrtf.mFilename.data()); + TRACE("Unloading unused HRTF %s\n", hrtf.mFilename.c_str()); hrtf.mEntry = nullptr; return true; } diff --git a/3rdparty/openal/core/hrtf.h b/3rdparty/openal/core/hrtf.h index 5e6e09a8c3bd..91c9244387be 100644 --- a/3rdparty/openal/core/hrtf.h +++ b/3rdparty/openal/core/hrtf.h @@ -6,19 +6,20 @@ #include #include #include +#include #include #include "almalloc.h" #include "alspan.h" -#include "atomic.h" #include "ambidefs.h" #include "bufferline.h" -#include "mixer/hrtfdefs.h" +#include "flexarray.h" #include "intrusive_ptr.h" +#include "mixer/hrtfdefs.h" -struct HrtfStore { - RefCount mRef; +struct alignas(16) HrtfStore { + std::atomic mRef; uint mSampleRate : 24; uint mIrSize : 8; @@ -36,17 +37,24 @@ struct HrtfStore { ushort azCount; ushort irOffset; }; - Elevation *mElev; - const HrirArray *mCoeffs; - const ubyte2 *mDelays; + al::span mElev; + al::span mCoeffs; + al::span mDelays; - void getCoeffs(float elevation, float azimuth, float distance, float spread, HrirArray &coeffs, - const al::span delays); + void getCoeffs(float elevation, float azimuth, float distance, float spread, + const HrirSpan coeffs, const al::span delays) const; void add_ref(); void dec_ref(); - DEF_PLACE_NEWDEL() + void *operator new(size_t) = delete; + void *operator new[](size_t) = delete; + void operator delete[](void*) noexcept = delete; + + void operator delete(gsl::owner block, void*) noexcept + { ::operator delete[](block, std::align_val_t{alignof(HrtfStore)}); } + void operator delete(gsl::owner block) noexcept + { ::operator delete[](block, std::align_val_t{alignof(HrtfStore)}); } }; using HrtfStorePtr = al::intrusive_ptr; @@ -60,7 +68,7 @@ struct AngularPoint { struct DirectHrtfState { - std::array mTemp; + std::array mTemp{}; /* HRTF filter state for dry buffer content */ uint mIrSize{0}; @@ -74,7 +82,8 @@ struct DirectHrtfState { * are ordered and scaled according to the matrix input. */ void build(const HrtfStore *Hrtf, const uint irSize, const bool perHrirMin, - const al::span AmbiPoints, const float (*AmbiMatrix)[MaxAmbiChannels], + const al::span AmbiPoints, + const al::span> AmbiMatrix, const float XOverFreq, const al::span AmbiOrderHFGain); static std::unique_ptr Create(size_t num_chans); @@ -84,6 +93,6 @@ struct DirectHrtfState { std::vector EnumerateHrtf(std::optional pathopt); -HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate); +HrtfStorePtr GetLoadedHrtf(const std::string_view name, const uint devrate); #endif /* CORE_HRTF_H */ diff --git a/3rdparty/openal/core/logging.cpp b/3rdparty/openal/core/logging.cpp index 56ad0a0dba45..c0ff45c0241d 100644 --- a/3rdparty/openal/core/logging.cpp +++ b/3rdparty/openal/core/logging.cpp @@ -3,6 +3,7 @@ #include "logging.h" +#include #include #include #include @@ -13,6 +14,7 @@ #include #include "alspan.h" +#include "opthelpers.h" #include "strutils.h" @@ -46,7 +48,7 @@ LogState gLogState{LogState::FirstRun}; LogCallbackFunc gLogCallback{}; void *gLogCallbackPtr{}; -constexpr std::optional GetLevelCode(LogLevel level) +constexpr auto GetLevelCode(LogLevel level) noexcept -> std::optional { switch(level) { @@ -75,8 +77,8 @@ void al_set_log_callback(LogCallbackFunc callback, void *userptr) } } -void al_print(LogLevel level, const char *fmt, ...) -{ +void al_print(LogLevel level, const char *fmt, ...) noexcept +try { /* 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. @@ -97,6 +99,7 @@ void al_print(LogLevel level, const char *fmt, ...) auto prefend1 = std::copy_n(prefix.begin(), prefix.size(), stcmsg.begin()); al::span msg{prefend1, stcmsg.end()}; + /* NOLINTBEGIN(*-array-to-pointer-decay) */ std::va_list args, args2; va_start(args, fmt); va_copy(args2, args); @@ -119,6 +122,7 @@ void al_print(LogLevel level, const char *fmt, ...) msg = {msg.data(), std::strlen(msg.data())}; va_end(args2); va_end(args); + /* NOLINTEND(*-array-to-pointer-decay) */ if(gLogLevel >= level) { @@ -167,3 +171,6 @@ void al_print(LogLevel level, const char *fmt, ...) } } } +catch(...) { + /* Swallow any exceptions */ +} diff --git a/3rdparty/openal/core/logging.h b/3rdparty/openal/core/logging.h index 06b7cddea316..527e795403ff 100644 --- a/3rdparty/openal/core/logging.h +++ b/3rdparty/openal/core/logging.h @@ -1,9 +1,7 @@ #ifndef CORE_LOGGING_H #define CORE_LOGGING_H -#include - -#include "opthelpers.h" +#include enum class LogLevel { @@ -22,12 +20,12 @@ using LogCallbackFunc = void(*)(void *userptr, char level, const char *message, void al_set_log_callback(LogCallbackFunc callback, void *userptr); -#ifdef __USE_MINGW_ANSI_STDIO -[[gnu::format(gnu_printf,2,3)]] +#ifdef __MINGW32__ +[[gnu::format(__MINGW_PRINTF_FORMAT,2,3)]] #else [[gnu::format(printf,2,3)]] #endif -void al_print(LogLevel level, const char *fmt, ...); +void al_print(LogLevel level, const char *fmt, ...) noexcept; #define TRACE(...) al_print(LogLevel::Trace, __VA_ARGS__) diff --git a/3rdparty/openal/core/mastering.cpp b/3rdparty/openal/core/mastering.cpp index 4445719b4216..4da708ab86cd 100644 --- a/3rdparty/openal/core/mastering.cpp +++ b/3rdparty/openal/core/mastering.cpp @@ -11,7 +11,6 @@ #include #include -#include "almalloc.h" #include "alnumeric.h" #include "alspan.h" #include "opthelpers.h" @@ -20,9 +19,9 @@ /* These structures assume BufferLineSize is a power of 2. */ static_assert((BufferLineSize & (BufferLineSize-1)) == 0, "BufferLineSize is not a power of 2"); -struct SlidingHold { - alignas(16) float mValues[BufferLineSize]; - uint mExpiries[BufferLineSize]; +struct SIMDALIGN SlidingHold { + alignas(16) FloatBufferLine mValues; + std::array mExpiries; uint mLowerIndex; uint mUpperIndex; uint mLength; @@ -31,7 +30,9 @@ struct SlidingHold { namespace { -using namespace std::placeholders; +template +constexpr auto assume_aligned_span(const al::span s) noexcept -> al::span +{ return al::span{al::assume_aligned(s.data()), s.size()}; } /* This sliding hold follows the input level with an instant attack and a * fixed duration hold before an instant release to the next highest level. @@ -44,8 +45,8 @@ float UpdateSlidingHold(SlidingHold *Hold, const uint i, const float in) { static constexpr uint mask{BufferLineSize - 1}; const uint length{Hold->mLength}; - float (&values)[BufferLineSize] = Hold->mValues; - uint (&expiries)[BufferLineSize] = Hold->mExpiries; + const al::span values{Hold->mValues}; + const al::span expiries{Hold->mExpiries}; uint lowerIndex{Hold->mLowerIndex}; uint upperIndex{Hold->mUpperIndex}; @@ -60,14 +61,16 @@ float UpdateSlidingHold(SlidingHold *Hold, const uint i, const float in) } else { - do { + auto findLowerIndex = [&lowerIndex,in,values]() noexcept -> bool + { do { if(!(in >= values[lowerIndex])) - goto found_place; + return true; } while(lowerIndex--); + return false; + }; + while(!findLowerIndex()) lowerIndex = mask; - } while(true); - found_place: lowerIndex = (lowerIndex + 1) & mask; values[lowerIndex] = in; @@ -82,38 +85,42 @@ float UpdateSlidingHold(SlidingHold *Hold, const uint i, const float in) void ShiftSlidingHold(SlidingHold *Hold, const uint n) { - auto exp_begin = std::begin(Hold->mExpiries) + Hold->mUpperIndex; - auto exp_last = std::begin(Hold->mExpiries) + Hold->mLowerIndex; - if(exp_last-exp_begin < 0) + auto exp_upper = Hold->mExpiries.begin() + Hold->mUpperIndex; + if(Hold->mLowerIndex < Hold->mUpperIndex) { - std::transform(exp_begin, std::end(Hold->mExpiries), exp_begin, - [n](uint e){ return e - n; }); - exp_begin = std::begin(Hold->mExpiries); + std::transform(exp_upper, Hold->mExpiries.end(), exp_upper, + [n](const uint e) noexcept { return e - n; }); + exp_upper = Hold->mExpiries.begin(); } - std::transform(exp_begin, exp_last+1, exp_begin, [n](uint e){ return e - n; }); + const auto exp_lower = Hold->mExpiries.begin() + Hold->mLowerIndex; + std::transform(exp_upper, exp_lower+1, exp_upper, + [n](const uint e) noexcept { return e - n; }); } +} // namespace /* Multichannel compression is linked via the absolute maximum of all * channels. */ -void LinkChannels(Compressor *Comp, const uint SamplesToDo, const FloatBufferLine *OutBuffer) +void Compressor::linkChannels(const uint SamplesToDo, + const al::span OutBuffer) { - const size_t numChans{Comp->mNumChans}; - ASSUME(SamplesToDo > 0); - ASSUME(numChans > 0); + ASSUME(SamplesToDo <= BufferLineSize); - auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; - std::fill(side_begin, side_begin+SamplesToDo, 0.0f); + const auto sideChain = al::span{mSideChain}.subspan(mLookAhead, SamplesToDo); + std::fill_n(sideChain.begin(), sideChain.size(), 0.0f); - auto fill_max = [SamplesToDo,side_begin](const FloatBufferLine &input) -> void + auto fill_max = [sideChain](const FloatBufferLine &input) -> void { - const float *RESTRICT buffer{al::assume_aligned<16>(input.data())}; - auto max_abs = std::bind(maxf, _1, std::bind(static_cast(std::fabs), _2)); - std::transform(side_begin, side_begin+SamplesToDo, buffer, side_begin, max_abs); + const auto buffer = assume_aligned_span<16>(al::span{input}); + auto max_abs = [](const float s0, const float s1) noexcept -> float + { return std::max(s0, std::fabs(s1)); }; + std::transform(sideChain.begin(), sideChain.end(), buffer.begin(), sideChain.begin(), + max_abs); }; - std::for_each(OutBuffer, OutBuffer+numChans, fill_max); + for(const FloatBufferLine &input : OutBuffer) + fill_max(input); } /* This calculates the squared crest factor of the control signal for the @@ -121,60 +128,63 @@ void LinkChannels(Compressor *Comp, const uint SamplesToDo, const FloatBufferLin * it uses an instantaneous squared peak detector and a squared RMS detector * both with 200ms release times. */ -void CrestDetector(Compressor *Comp, const uint SamplesToDo) +void Compressor::crestDetector(const uint SamplesToDo) { - const float a_crest{Comp->mCrestCoeff}; - float y2_peak{Comp->mLastPeakSq}; - float y2_rms{Comp->mLastRmsSq}; + const float a_crest{mCrestCoeff}; + float y2_peak{mLastPeakSq}; + float y2_rms{mLastRmsSq}; ASSUME(SamplesToDo > 0); + ASSUME(SamplesToDo <= BufferLineSize); auto calc_crest = [&y2_rms,&y2_peak,a_crest](const float x_abs) noexcept -> float { - const float x2{clampf(x_abs * x_abs, 0.000001f, 1000000.0f)}; + const float x2{std::clamp(x_abs*x_abs, 0.000001f, 1000000.0f)}; - y2_peak = maxf(x2, lerpf(x2, y2_peak, a_crest)); + y2_peak = std::max(x2, lerpf(x2, y2_peak, a_crest)); y2_rms = lerpf(x2, y2_rms, a_crest); return y2_peak / y2_rms; }; - auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; - std::transform(side_begin, side_begin+SamplesToDo, std::begin(Comp->mCrestFactor), calc_crest); + const auto sideChain = al::span{mSideChain}.subspan(mLookAhead, SamplesToDo); + std::transform(sideChain.cbegin(), sideChain.cend(), mCrestFactor.begin(), calc_crest); - Comp->mLastPeakSq = y2_peak; - Comp->mLastRmsSq = y2_rms; + mLastPeakSq = y2_peak; + mLastRmsSq = y2_rms; } /* The side-chain starts with a simple peak detector (based on the absolute * value of the incoming signal) and performs most of its operations in the * log domain. */ -void PeakDetector(Compressor *Comp, const uint SamplesToDo) +void Compressor::peakDetector(const uint SamplesToDo) { ASSUME(SamplesToDo > 0); + ASSUME(SamplesToDo <= BufferLineSize); - /* Clamp the minimum amplitude to near-zero and convert to logarithm. */ - auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; - std::transform(side_begin, side_begin+SamplesToDo, side_begin, - [](float s) { return std::log(maxf(0.000001f, s)); }); + /* Clamp the minimum amplitude to near-zero and convert to logarithmic. */ + const auto sideChain = al::span{mSideChain}.subspan(mLookAhead, SamplesToDo); + std::transform(sideChain.cbegin(), sideChain.cend(), sideChain.begin(), + [](float s) { return std::log(std::max(0.000001f, s)); }); } /* An optional hold can be used to extend the peak detector so it can more * solidly detect fast transients. This is best used when operating as a * limiter. */ -void PeakHoldDetector(Compressor *Comp, const uint SamplesToDo) +void Compressor::peakHoldDetector(const uint SamplesToDo) { ASSUME(SamplesToDo > 0); + ASSUME(SamplesToDo <= BufferLineSize); - SlidingHold *hold{Comp->mHold}; + SlidingHold *hold{mHold.get()}; uint i{0}; auto detect_peak = [&i,hold](const float x_abs) -> float { - const float x_G{std::log(maxf(0.000001f, x_abs))}; + const float x_G{std::log(std::max(0.000001f, x_abs))}; return UpdateSlidingHold(hold, i++, x_G); }; - auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; - std::transform(side_begin, side_begin+SamplesToDo, side_begin, detect_peak); + auto sideChain = al::span{mSideChain}.subspan(mLookAhead, SamplesToDo); + std::transform(sideChain.cbegin(), sideChain.cend(), sideChain.begin(), detect_peak); ShiftSlidingHold(hold, SamplesToDo); } @@ -184,46 +194,46 @@ void PeakHoldDetector(Compressor *Comp, const uint SamplesToDo) * to knee width, attack/release times, make-up/post gain, and clipping * reduction. */ -void GainCompressor(Compressor *Comp, const uint SamplesToDo) +void Compressor::gainCompressor(const uint SamplesToDo) { - const bool autoKnee{Comp->mAuto.Knee}; - const bool autoAttack{Comp->mAuto.Attack}; - const bool autoRelease{Comp->mAuto.Release}; - const bool autoPostGain{Comp->mAuto.PostGain}; - const bool autoDeclip{Comp->mAuto.Declip}; - const uint lookAhead{Comp->mLookAhead}; - const float threshold{Comp->mThreshold}; - const float slope{Comp->mSlope}; - const float attack{Comp->mAttack}; - const float release{Comp->mRelease}; - const float c_est{Comp->mGainEstimate}; - const float a_adp{Comp->mAdaptCoeff}; - const float *crestFactor{Comp->mCrestFactor}; - float postGain{Comp->mPostGain}; - float knee{Comp->mKnee}; + const bool autoKnee{mAuto.Knee}; + const bool autoAttack{mAuto.Attack}; + const bool autoRelease{mAuto.Release}; + const bool autoPostGain{mAuto.PostGain}; + const bool autoDeclip{mAuto.Declip}; + const float threshold{mThreshold}; + const float slope{mSlope}; + const float attack{mAttack}; + const float release{mRelease}; + const float c_est{mGainEstimate}; + const float a_adp{mAdaptCoeff}; + auto lookAhead = mSideChain.cbegin() + mLookAhead; + auto crestFactor = mCrestFactor.cbegin(); + float postGain{mPostGain}; + float knee{mKnee}; float t_att{attack}; float t_rel{release - attack}; float a_att{std::exp(-1.0f / t_att)}; float a_rel{std::exp(-1.0f / t_rel)}; - float y_1{Comp->mLastRelease}; - float y_L{Comp->mLastAttack}; - float c_dev{Comp->mLastGainDev}; + float y_1{mLastRelease}; + float y_L{mLastAttack}; + float c_dev{mLastGainDev}; ASSUME(SamplesToDo > 0); - for(float &sideChain : al::span{Comp->mSideChain, SamplesToDo}) + auto process = [&](const float input) -> float { if(autoKnee) - knee = maxf(0.0f, 2.5f * (c_dev + c_est)); + knee = std::max(0.0f, 2.5f * (c_dev + c_est)); const float knee_h{0.5f * knee}; /* This is the gain computer. It applies a static compression curve * to the control signal. */ - const float x_over{std::addressof(sideChain)[lookAhead] - threshold}; + const float x_over{*(lookAhead++) - threshold}; const float y_G{ (x_over <= -knee_h) ? 0.0f : - (std::fabs(x_over) < knee_h) ? (x_over + knee_h) * (x_over + knee_h) / (2.0f * knee) : + (std::fabs(x_over) < knee_h) ? (x_over+knee_h) * (x_over+knee_h) / (2.0f * knee) : x_over}; const float y2_crest{*(crestFactor++)}; @@ -243,7 +253,7 @@ void GainCompressor(Compressor *Comp, const uint SamplesToDo) * above to compensate for the chained operating mode. */ const float x_L{-slope * y_G}; - y_1 = maxf(x_L, lerpf(x_L, y_1, a_rel)); + y_1 = std::max(x_L, lerpf(x_L, y_1, a_rel)); y_L = lerpf(y_1, y_L, a_att); /* Knee width and make-up gain automation make use of a smoothed @@ -262,17 +272,19 @@ void GainCompressor(Compressor *Comp, const uint SamplesToDo) * same output level. */ if(autoDeclip) - c_dev = maxf(c_dev, sideChain - y_L - threshold - c_est); + c_dev = std::max(c_dev, input - y_L - threshold - c_est); postGain = -(c_dev + c_est); } - sideChain = std::exp(postGain - y_L); - } + return std::exp(postGain - y_L); + }; + auto sideChain = al::span{mSideChain}.first(SamplesToDo); + std::transform(sideChain.begin(), sideChain.end(), sideChain.begin(), process); - Comp->mLastRelease = y_1; - Comp->mLastAttack = y_L; - Comp->mLastGainDev = c_dev; + mLastRelease = y_1; + mLastAttack = y_L; + mLastGainDev = c_dev; } /* Combined with the hold time, a look-ahead delay can improve handling of @@ -280,75 +292,60 @@ void GainCompressor(Compressor *Comp, const uint SamplesToDo) * reaching the offending impulse. This is best used when operating as a * limiter. */ -void SignalDelay(Compressor *Comp, const uint SamplesToDo, FloatBufferLine *OutBuffer) +void Compressor::signalDelay(const uint SamplesToDo, const al::span OutBuffer) { - const size_t numChans{Comp->mNumChans}; - const uint lookAhead{Comp->mLookAhead}; + const auto lookAhead = mLookAhead; ASSUME(SamplesToDo > 0); - ASSUME(numChans > 0); + ASSUME(SamplesToDo <= BufferLineSize); ASSUME(lookAhead > 0); + ASSUME(lookAhead < BufferLineSize); - for(size_t c{0};c < numChans;c++) + auto delays = mDelay.begin(); + for(auto &buffer : OutBuffer) { - float *inout{al::assume_aligned<16>(OutBuffer[c].data())}; - float *delaybuf{al::assume_aligned<16>(Comp->mDelay[c].data())}; + const auto inout = al::span{buffer}.first(SamplesToDo); + const auto delaybuf = al::span{*(delays++)}.first(lookAhead); - auto inout_end = inout + SamplesToDo; - if(SamplesToDo >= lookAhead) LIKELY + if(SamplesToDo >= delaybuf.size()) LIKELY { - auto delay_end = std::rotate(inout, inout_end - lookAhead, inout_end); - std::swap_ranges(inout, delay_end, delaybuf); + const auto inout_start = inout.end() - ptrdiff_t(delaybuf.size()); + const auto delay_end = std::rotate(inout.begin(), inout_start, inout.end()); + std::swap_ranges(inout.begin(), delay_end, delaybuf.begin()); } else { - auto delay_start = std::swap_ranges(inout, inout_end, delaybuf); - std::rotate(delaybuf, delay_start, delaybuf + lookAhead); + auto delay_start = std::swap_ranges(inout.begin(), inout.end(), delaybuf.begin()); + std::rotate(delaybuf.begin(), delay_start, delaybuf.end()); } } } -} // namespace - std::unique_ptr Compressor::Create(const size_t NumChans, const float SampleRate, - const bool AutoKnee, const bool AutoAttack, const bool AutoRelease, const bool AutoPostGain, - const bool AutoDeclip, const float LookAheadTime, const float HoldTime, const float PreGainDb, - const float PostGainDb, const float ThresholdDb, const float Ratio, const float KneeDb, - const float AttackTime, const float ReleaseTime) + const FlagBits autoflags, const float LookAheadTime, const float HoldTime, + const float PreGainDb, const float PostGainDb, const float ThresholdDb, const float Ratio, + const float KneeDb, const float AttackTime, const float ReleaseTime) { - const auto lookAhead = static_cast( - clampf(std::round(LookAheadTime*SampleRate), 0.0f, BufferLineSize-1)); - const auto hold = static_cast( - clampf(std::round(HoldTime*SampleRate), 0.0f, BufferLineSize-1)); - - size_t size{sizeof(Compressor)}; - if(lookAhead > 0) - { - size += sizeof(*Compressor::mDelay) * NumChans; - /* The sliding hold implementation doesn't handle a length of 1. A 1- - * sample hold is useless anyway, it would only ever give back what was - * just given to it. - */ - if(hold > 1) - size += sizeof(*Compressor::mHold); - } - - auto Comp = CompressorPtr{al::construct_at(static_cast(al_calloc(16, size)))}; - Comp->mNumChans = NumChans; - Comp->mAuto.Knee = AutoKnee; - Comp->mAuto.Attack = AutoAttack; - Comp->mAuto.Release = AutoRelease; - Comp->mAuto.PostGain = AutoPostGain; - Comp->mAuto.Declip = AutoPostGain && AutoDeclip; + const auto lookAhead = static_cast(std::clamp(std::round(LookAheadTime*SampleRate), 0.0f, + BufferLineSize-1.0f)); + const auto hold = static_cast(std::clamp(std::round(HoldTime*SampleRate), 0.0f, + BufferLineSize-1.0f)); + + auto Comp = CompressorPtr{new Compressor{}}; + Comp->mAuto.Knee = autoflags.test(AutoKnee); + Comp->mAuto.Attack = autoflags.test(AutoAttack); + Comp->mAuto.Release = autoflags.test(AutoRelease); + Comp->mAuto.PostGain = autoflags.test(AutoPostGain); + Comp->mAuto.Declip = autoflags.test(AutoPostGain) && autoflags.test(AutoDeclip); Comp->mLookAhead = lookAhead; Comp->mPreGain = std::pow(10.0f, PreGainDb / 20.0f); - Comp->mPostGain = PostGainDb * std::log(10.0f) / 20.0f; - Comp->mThreshold = ThresholdDb * std::log(10.0f) / 20.0f; - Comp->mSlope = 1.0f / maxf(1.0f, Ratio) - 1.0f; - Comp->mKnee = maxf(0.0f, KneeDb * std::log(10.0f) / 20.0f); - Comp->mAttack = maxf(1.0f, AttackTime * SampleRate); - Comp->mRelease = maxf(1.0f, ReleaseTime * SampleRate); + Comp->mPostGain = std::log(10.0f)/20.0f * PostGainDb; + Comp->mThreshold = std::log(10.0f)/20.0f * ThresholdDb; + Comp->mSlope = 1.0f / std::max(1.0f, Ratio) - 1.0f; + Comp->mKnee = std::max(0.0f, std::log(10.0f)/20.0f * KneeDb); + Comp->mAttack = std::max(1.0f, AttackTime * SampleRate); + Comp->mRelease = std::max(1.0f, ReleaseTime * SampleRate); /* Knee width automation actually treats the compressor as a limiter. By * varying the knee width, it can effectively be seen as applying @@ -359,17 +356,18 @@ std::unique_ptr Compressor::Create(const size_t NumChans, const floa if(lookAhead > 0) { + /* The sliding hold implementation doesn't handle a length of 1. A 1- + * sample hold is useless anyway, it would only ever give back what was + * just given to it. + */ if(hold > 1) { - Comp->mHold = al::construct_at(reinterpret_cast(Comp.get() + 1)); + Comp->mHold = std::make_unique(); Comp->mHold->mValues[0] = -std::numeric_limits::infinity(); Comp->mHold->mExpiries[0] = hold; Comp->mHold->mLength = hold; - Comp->mDelay = reinterpret_cast(Comp->mHold + 1); } - else - Comp->mDelay = reinterpret_cast(Comp.get() + 1); - std::uninitialized_fill_n(Comp->mDelay, NumChans, FloatBufferLine{}); + Comp->mDelay.resize(NumChans, FloatBufferLine{}); } Comp->mCrestCoeff = std::exp(-1.0f / (0.200f * SampleRate)); // 200ms @@ -379,61 +377,51 @@ std::unique_ptr Compressor::Create(const size_t NumChans, const floa return Comp; } -Compressor::~Compressor() -{ - if(mHold) - std::destroy_at(mHold); - mHold = nullptr; - if(mDelay) - std::destroy_n(mDelay, mNumChans); - mDelay = nullptr; -} +Compressor::~Compressor() = default; -void Compressor::process(const uint SamplesToDo, FloatBufferLine *OutBuffer) +void Compressor::process(const uint SamplesToDo, const al::span InOut) { - const size_t numChans{mNumChans}; - ASSUME(SamplesToDo > 0); - ASSUME(numChans > 0); + ASSUME(SamplesToDo <= BufferLineSize); const float preGain{mPreGain}; if(preGain != 1.0f) { auto apply_gain = [SamplesToDo,preGain](FloatBufferLine &input) noexcept -> void { - float *buffer{al::assume_aligned<16>(input.data())}; - std::transform(buffer, buffer+SamplesToDo, buffer, - [preGain](float s) { return s * preGain; }); + const auto buffer = assume_aligned_span<16>(al::span{input}.first(SamplesToDo)); + std::transform(buffer.cbegin(), buffer.cend(), buffer.begin(), + [preGain](const float s) noexcept { return s * preGain; }); }; - std::for_each(OutBuffer, OutBuffer+numChans, apply_gain); + std::for_each(InOut.begin(), InOut.end(), apply_gain); } - LinkChannels(this, SamplesToDo, OutBuffer); + linkChannels(SamplesToDo, InOut); if(mAuto.Attack || mAuto.Release) - CrestDetector(this, SamplesToDo); + crestDetector(SamplesToDo); if(mHold) - PeakHoldDetector(this, SamplesToDo); + peakHoldDetector(SamplesToDo); else - PeakDetector(this, SamplesToDo); + peakDetector(SamplesToDo); - GainCompressor(this, SamplesToDo); + gainCompressor(SamplesToDo); - if(mDelay) - SignalDelay(this, SamplesToDo, OutBuffer); + if(!mDelay.empty()) + signalDelay(SamplesToDo, InOut); - const float (&sideChain)[BufferLineSize*2] = mSideChain; - auto apply_comp = [SamplesToDo,&sideChain](FloatBufferLine &input) noexcept -> void + const auto gains = assume_aligned_span<16>(al::span{mSideChain}.first(SamplesToDo)); + auto apply_comp = [gains](const FloatBufferSpan inout) noexcept -> void { - float *buffer{al::assume_aligned<16>(input.data())}; - const float *gains{al::assume_aligned<16>(&sideChain[0])}; - std::transform(gains, gains+SamplesToDo, buffer, buffer, - [](float g, float s) { return g * s; }); + const auto buffer = assume_aligned_span<16>(inout); + std::transform(gains.cbegin(), gains.cend(), buffer.cbegin(), buffer.begin(), + std::multiplies{}); }; - std::for_each(OutBuffer, OutBuffer+numChans, apply_comp); + for(const FloatBufferSpan inout : InOut) + apply_comp(inout); - auto side_begin = std::begin(mSideChain) + SamplesToDo; - std::copy(side_begin, side_begin+mLookAhead, std::begin(mSideChain)); + const auto delayedGains = al::span{mSideChain}.subspan(SamplesToDo, mLookAhead); + std::copy(delayedGains.begin(), delayedGains.end(), mSideChain.begin()); } diff --git a/3rdparty/openal/core/mastering.h b/3rdparty/openal/core/mastering.h index 1a36937ca484..32f581dbc486 100644 --- a/3rdparty/openal/core/mastering.h +++ b/3rdparty/openal/core/mastering.h @@ -1,10 +1,15 @@ #ifndef CORE_MASTERING_H #define CORE_MASTERING_H +#include +#include #include -#include "almalloc.h" +#include "alnumeric.h" +#include "alspan.h" #include "bufferline.h" +#include "opthelpers.h" +#include "vector.h" struct SlidingHold; @@ -21,16 +26,15 @@ using uint = unsigned int; * * http://c4dm.eecs.qmul.ac.uk/audioengineering/compressors/ */ -struct Compressor { - size_t mNumChans{0u}; - - struct { +class SIMDALIGN Compressor { + struct AutoFlags { bool Knee : 1; bool Attack : 1; bool Release : 1; bool PostGain : 1; bool Declip : 1; - } mAuto{}; + }; + AutoFlags mAuto{}; uint mLookAhead{0}; @@ -44,11 +48,11 @@ struct Compressor { float mAttack{0.0f}; float mRelease{0.0f}; - alignas(16) float mSideChain[2*BufferLineSize]{}; - alignas(16) float mCrestFactor[BufferLineSize]{}; + alignas(16) std::array mSideChain{}; + alignas(16) std::array mCrestFactor{}; - SlidingHold *mHold{nullptr}; - FloatBufferLine *mDelay{nullptr}; + std::unique_ptr mHold; + al::vector mDelay; float mCrestCoeff{0.0f}; float mGainEstimate{0.0f}; @@ -60,12 +64,24 @@ struct Compressor { float mLastAttack{0.0f}; float mLastGainDev{0.0f}; + Compressor() = default; - ~Compressor(); - void process(const uint SamplesToDo, FloatBufferLine *OutBuffer); - int getLookAhead() const noexcept { return static_cast(mLookAhead); } + void linkChannels(const uint SamplesToDo, const al::span OutBuffer); + void crestDetector(const uint SamplesToDo); + void peakDetector(const uint SamplesToDo); + void peakHoldDetector(const uint SamplesToDo); + void gainCompressor(const uint SamplesToDo); + void signalDelay(const uint SamplesToDo, const al::span OutBuffer); - DEF_PLACE_NEWDEL() +public: + enum { + AutoKnee, AutoAttack, AutoRelease, AutoPostGain, AutoDeclip, FlagsCount + }; + using FlagBits = std::bitset; + + ~Compressor(); + void process(const uint SamplesToDo, al::span InOut); + [[nodiscard]] auto getLookAhead() const noexcept -> uint { return mLookAhead; } /** * The compressor is initialized with the following settings: @@ -94,11 +110,9 @@ struct Compressor { * automating release time. */ static std::unique_ptr Create(const size_t NumChans, const float SampleRate, - const bool AutoKnee, const bool AutoAttack, const bool AutoRelease, - const bool AutoPostGain, const bool AutoDeclip, const float LookAheadTime, - const float HoldTime, const float PreGainDb, const float PostGainDb, - const float ThresholdDb, const float Ratio, const float KneeDb, const float AttackTime, - const float ReleaseTime); + const FlagBits autoflags, const float LookAheadTime, const float HoldTime, + const float PreGainDb, const float PostGainDb, const float ThresholdDb, const float Ratio, + const float KneeDb, const float AttackTime, const float ReleaseTime); }; using CompressorPtr = std::unique_ptr; diff --git a/3rdparty/openal/core/mixer.cpp b/3rdparty/openal/core/mixer.cpp index 066c57bd765e..bba7ae2061f9 100644 --- a/3rdparty/openal/core/mixer.cpp +++ b/3rdparty/openal/core/mixer.cpp @@ -3,10 +3,12 @@ #include "mixer.h" +#include #include +#include #include "alnumbers.h" -#include "devformat.h" +#include "core/ambidefs.h" #include "device.h" #include "mixer/defs.h" @@ -82,14 +84,13 @@ std::array 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 gains) +void ComputePanGains(const MixParams *mix, const al::span coeffs, + const float ingain, const al::span gains) { - auto ambimap = mix->AmbiMap.cbegin(); + auto ambimap = al::span{std::as_const(mix->AmbiMap)}.first(mix->Buffer.size()); - auto iter = std::transform(ambimap, ambimap+mix->Buffer.size(), gains.begin(), + auto iter = std::transform(ambimap.begin(), ambimap.end(), 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/3rdparty/openal/core/mixer.h b/3rdparty/openal/core/mixer.h index a9c1f931ca9f..b5f1b9aa1c88 100644 --- a/3rdparty/openal/core/mixer.h +++ b/3rdparty/openal/core/mixer.h @@ -3,34 +3,32 @@ #include #include -#include -#include +#include #include "alspan.h" #include "ambidefs.h" #include "bufferline.h" -#include "devformat.h" struct MixParams; /* Mixer functions that handle one input and multiple output channels. */ using MixerOutFunc = void(*)(const al::span InSamples, - const al::span OutBuffer, float *CurrentGains, const float *TargetGains, - const size_t Counter, const size_t OutPos); + const al::span OutBuffer, const al::span CurrentGains, + const al::span TargetGains, const std::size_t Counter, const std::size_t OutPos); extern MixerOutFunc MixSamplesOut; inline void MixSamples(const al::span InSamples, - const al::span OutBuffer, float *CurrentGains, const float *TargetGains, - const size_t Counter, const size_t OutPos) + const al::span OutBuffer, const al::span CurrentGains, + const al::span TargetGains, const std::size_t Counter, const std::size_t OutPos) { MixSamplesOut(InSamples, OutBuffer, CurrentGains, TargetGains, Counter, OutPos); } /* Mixer functions that handle one input and one output channel. */ -using MixerOneFunc = void(*)(const al::span InSamples, float *OutBuffer, - float &CurrentGain, const float TargetGain, const size_t Counter); +using MixerOneFunc = void(*)(const al::span InSamples,const al::span OutBuffer, + float &CurrentGain, const float TargetGain, const std::size_t Counter); extern MixerOneFunc MixSamplesOne; -inline void MixSamples(const al::span InSamples, float *OutBuffer, float &CurrentGain, - const float TargetGain, const size_t Counter) +inline void MixSamples(const al::span InSamples, const al::span OutBuffer, + float &CurrentGain, const float TargetGain, const std::size_t Counter) { MixSamplesOne(InSamples, OutBuffer, CurrentGain, TargetGain, Counter); } @@ -103,7 +101,7 @@ inline std::array 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 gains); +void ComputePanGains(const MixParams *mix, const al::span coeffs, + const float ingain, const al::span gains); #endif /* CORE_MIXER_H */ diff --git a/3rdparty/openal/core/mixer/defs.h b/3rdparty/openal/core/mixer/defs.h index 48daca9b95c2..f19217c80906 100644 --- a/3rdparty/openal/core/mixer/defs.h +++ b/3rdparty/openal/core/mixer/defs.h @@ -2,13 +2,15 @@ #define CORE_MIXER_DEFS_H #include -#include +#include +#include +#include +#include #include "alspan.h" #include "core/bufferline.h" -#include "core/resampler_limits.h" +#include "core/cubic_defs.h" -struct CubicCoefficients; struct HrtfChannelState; struct HrtfFilter; struct MixHrtfFilter; @@ -17,18 +19,19 @@ using uint = unsigned int; using float2 = std::array; -constexpr int MixerFracBits{16}; -constexpr int MixerFracOne{1 << MixerFracBits}; -constexpr int MixerFracMask{MixerFracOne - 1}; -constexpr int MixerFracHalf{MixerFracOne >> 1}; +inline constexpr int MixerFracBits{16}; +inline constexpr int MixerFracOne{1 << MixerFracBits}; +inline constexpr int MixerFracMask{MixerFracOne - 1}; +inline constexpr int MixerFracHalf{MixerFracOne >> 1}; -constexpr float GainSilenceThreshold{0.00001f}; /* -100dB */ +inline constexpr float GainSilenceThreshold{0.00001f}; /* -100dB */ -enum class Resampler : uint8_t { +enum class Resampler : std::uint8_t { Point, Linear, - Cubic, + Spline, + Gaussian, FastBSinc12, BSinc12, FastBSinc24, @@ -49,56 +52,59 @@ struct BsincState { * delta coefficients. Starting at phase index 0, each subsequent phase * index follows contiguously. */ - const float *filter; + al::span filter; }; struct CubicState { /* Filter coefficients, and coefficient deltas. Starting at phase index 0, * each subsequent phase index follows contiguously. */ - const CubicCoefficients *filter; + al::span filter; + CubicState(al::span f) : filter{f} { } }; -union InterpState { - CubicState cubic; - BsincState bsinc; -}; +using InterpState = std::variant; -using ResamplerFunc = void(*)(const InterpState *state, const float *RESTRICT src, uint frac, +using ResamplerFunc = void(*)(const InterpState *state, const al::span src, uint frac, const uint increment, const al::span dst); ResamplerFunc PrepareResampler(Resampler resampler, uint increment, InterpState *state); template -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, +void Resample_(const InterpState *state, const al::span src, uint frac, const uint increment, const al::span dst); template void Mix_(const al::span InSamples, const al::span OutBuffer, - float *CurrentGains, const float *TargetGains, const size_t Counter, const size_t OutPos); + const al::span CurrentGains, const al::span TargetGains, + const size_t Counter, const size_t OutPos); template -void Mix_(const al::span InSamples, float *OutBuffer, float &CurrentGain, - const float TargetGain, const size_t Counter); +void Mix_(const al::span InSamples, const al::span OutBuffer, + float &CurrentGain, const float TargetGain, const size_t Counter); template -void MixHrtf_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const MixHrtfFilter *hrtfparams, const size_t BufferSize); +void MixHrtf_(const al::span InSamples, const al::span AccumSamples, + const uint IrSize, const MixHrtfFilter *hrtfparams, const size_t SamplesToDo); template -void MixHrtfBlend_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const HrtfFilter *oldparams, const MixHrtfFilter *newparams, const size_t BufferSize); +void MixHrtfBlend_(const al::span InSamples, const al::span AccumSamples, + const uint IrSize, const HrtfFilter *oldparams, const MixHrtfFilter *newparams, + const size_t SamplesToDo); template void MixDirectHrtf_(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut, - const al::span InSamples, float2 *AccumSamples, - float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize); + const al::span InSamples, const al::span AccumSamples, + const al::span TempBuf, const al::span ChanState, + const size_t IrSize, const size_t SamplesToDo); /* Vectorized resampler helpers */ template -inline void InitPosArrays(uint frac, uint increment, uint (&frac_arr)[N], uint (&pos_arr)[N]) +constexpr void InitPosArrays(uint pos, uint frac, const uint increment, + const al::span frac_arr, const al::span pos_arr) { - pos_arr[0] = 0; + static_assert(pos_arr.size() == frac_arr.size()); + pos_arr[0] = pos; frac_arr[0] = frac; - for(size_t i{1};i < N;i++) + for(size_t i{1};i < pos_arr.size();++i) { const uint frac_tmp{frac_arr[i-1] + increment}; pos_arr[i] = pos_arr[i-1] + (frac_tmp>>MixerFracBits); diff --git a/3rdparty/openal/core/mixer/hrtfbase.h b/3rdparty/openal/core/mixer/hrtfbase.h index 36f88e4967ce..703bfab9d649 100644 --- a/3rdparty/openal/core/mixer/hrtfbase.h +++ b/3rdparty/openal/core/mixer/hrtfbase.h @@ -4,21 +4,23 @@ #include #include -#include "almalloc.h" +#include "defs.h" #include "hrtfdefs.h" #include "opthelpers.h" using uint = unsigned int; -using ApplyCoeffsT = void(&)(float2 *RESTRICT Values, const size_t irSize, +using ApplyCoeffsT = void(const al::span Values, const size_t irSize, const ConstHrirSpan Coeffs, const float left, const float right); template -inline void MixHrtfBase(const float *InSamples, float2 *RESTRICT AccumSamples, const size_t IrSize, - const MixHrtfFilter *hrtfparams, const size_t BufferSize) +inline void MixHrtfBase(const al::span InSamples, const al::span AccumSamples, + const size_t IrSize, const MixHrtfFilter *hrtfparams, const size_t SamplesToDo) { - ASSUME(BufferSize > 0); + ASSUME(SamplesToDo > 0); + ASSUME(SamplesToDo <= BufferLineSize); + ASSUME(IrSize <= HrirLength); const ConstHrirSpan Coeffs{hrtfparams->Coeffs}; const float gainstep{hrtfparams->GainStep}; @@ -27,26 +29,28 @@ inline void MixHrtfBase(const float *InSamples, float2 *RESTRICT AccumSamples, c size_t ldelay{HrtfHistoryLength - hrtfparams->Delay[0]}; size_t rdelay{HrtfHistoryLength - hrtfparams->Delay[1]}; float stepcount{0.0f}; - for(size_t i{0u};i < BufferSize;++i) + for(size_t i{0u};i < SamplesToDo;++i) { const float g{gain + gainstep*stepcount}; const float left{InSamples[ldelay++] * g}; const float right{InSamples[rdelay++] * g}; - ApplyCoeffs(AccumSamples+i, IrSize, Coeffs, left, right); + ApplyCoeffs(AccumSamples.subspan(i), IrSize, Coeffs, left, right); stepcount += 1.0f; } } template -inline void MixHrtfBlendBase(const float *InSamples, float2 *RESTRICT AccumSamples, - const size_t IrSize, const HrtfFilter *oldparams, const MixHrtfFilter *newparams, - const size_t BufferSize) +inline void MixHrtfBlendBase(const al::span InSamples, + const al::span AccumSamples, const size_t IrSize, const HrtfFilter *oldparams, + const MixHrtfFilter *newparams, const size_t SamplesToDo) { - ASSUME(BufferSize > 0); + ASSUME(SamplesToDo > 0); + ASSUME(SamplesToDo <= BufferLineSize); + ASSUME(IrSize <= HrirLength); const ConstHrirSpan OldCoeffs{oldparams->Coeffs}; - const float oldGainStep{oldparams->Gain / static_cast(BufferSize)}; + const float oldGainStep{oldparams->Gain / static_cast(SamplesToDo)}; const ConstHrirSpan NewCoeffs{newparams->Coeffs}; const float newGainStep{newparams->GainStep}; @@ -54,29 +58,29 @@ inline void MixHrtfBlendBase(const float *InSamples, float2 *RESTRICT AccumSampl { size_t ldelay{HrtfHistoryLength - oldparams->Delay[0]}; size_t rdelay{HrtfHistoryLength - oldparams->Delay[1]}; - auto stepcount = static_cast(BufferSize); - for(size_t i{0u};i < BufferSize;++i) + auto stepcount = static_cast(SamplesToDo); + for(size_t i{0u};i < SamplesToDo;++i) { const float g{oldGainStep*stepcount}; const float left{InSamples[ldelay++] * g}; const float right{InSamples[rdelay++] * g}; - ApplyCoeffs(AccumSamples+i, IrSize, OldCoeffs, left, right); + ApplyCoeffs(AccumSamples.subspan(i), IrSize, OldCoeffs, left, right); stepcount -= 1.0f; } } - if(newGainStep*static_cast(BufferSize) > GainSilenceThreshold) LIKELY + if(newGainStep*static_cast(SamplesToDo) > GainSilenceThreshold) LIKELY { size_t ldelay{HrtfHistoryLength+1 - newparams->Delay[0]}; size_t rdelay{HrtfHistoryLength+1 - newparams->Delay[1]}; float stepcount{1.0f}; - for(size_t i{1u};i < BufferSize;++i) + for(size_t i{1u};i < SamplesToDo;++i) { const float g{newGainStep*stepcount}; const float left{InSamples[ldelay++] * g}; const float right{InSamples[rdelay++] * g}; - ApplyCoeffs(AccumSamples+i, IrSize, NewCoeffs, left, right); + ApplyCoeffs(AccumSamples.subspan(i), IrSize, NewCoeffs, left, right); stepcount += 1.0f; } @@ -85,45 +89,52 @@ inline void MixHrtfBlendBase(const float *InSamples, float2 *RESTRICT AccumSampl template inline void MixDirectHrtfBase(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut, - const al::span InSamples, float2 *RESTRICT AccumSamples, - float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize) + const al::span InSamples, const al::span AccumSamples, + const al::span TempBuf, const al::span ChannelState, + const size_t IrSize, const size_t SamplesToDo) { - ASSUME(BufferSize > 0); + ASSUME(SamplesToDo > 0); + ASSUME(SamplesToDo <= BufferLineSize); + ASSUME(IrSize <= HrirLength); + assert(ChannelState.size() == InSamples.size()); + auto ChanState = ChannelState.begin(); for(const FloatBufferLine &input : InSamples) { /* For dual-band processing, the signal needs extra scaling applied to * the high frequency response. The band-splitter applies this scaling * with a consistent phase shift regardless of the scale amount. */ - ChanState->mSplitter.processHfScale({input.data(), BufferSize}, TempBuf, + ChanState->mSplitter.processHfScale(al::span{input}.first(SamplesToDo), TempBuf, ChanState->mHfScale); /* Now apply the HRIR coefficients to this channel. */ - const float *RESTRICT tempbuf{al::assume_aligned<16>(TempBuf)}; const ConstHrirSpan Coeffs{ChanState->mCoeffs}; - for(size_t i{0u};i < BufferSize;++i) + for(size_t i{0u};i < SamplesToDo;++i) { - const float insample{tempbuf[i]}; - ApplyCoeffs(AccumSamples+i, IrSize, Coeffs, insample, insample); + const float insample{TempBuf[i]}; + ApplyCoeffs(AccumSamples.subspan(i), IrSize, Coeffs, insample, insample); } ++ChanState; } /* Add the HRTF signal to the existing "direct" signal. */ - float *RESTRICT left{al::assume_aligned<16>(LeftOut.data())}; - float *RESTRICT right{al::assume_aligned<16>(RightOut.data())}; - for(size_t i{0u};i < BufferSize;++i) - left[i] += AccumSamples[i][0]; - for(size_t i{0u};i < BufferSize;++i) - right[i] += AccumSamples[i][1]; + const auto left = al::span{al::assume_aligned<16>(LeftOut.data()), SamplesToDo}; + std::transform(left.cbegin(), left.cend(), AccumSamples.cbegin(), left.begin(), + [](const float sample, const float2 &accum) noexcept -> float + { return sample + accum[0]; }); + const auto right = al::span{al::assume_aligned<16>(RightOut.data()), SamplesToDo}; + std::transform(right.cbegin(), right.cend(), AccumSamples.cbegin(), right.begin(), + [](const float sample, const float2 &accum) noexcept -> float + { return sample + accum[1]; }); /* Copy the new in-progress accumulation values to the front and clear the * following samples for the next mix. */ - auto accum_iter = std::copy_n(AccumSamples+BufferSize, HrirLength, AccumSamples); - std::fill_n(accum_iter, BufferSize, float2{}); + const auto accum_inprog = AccumSamples.subspan(SamplesToDo, HrirLength); + auto accum_iter = std::copy(accum_inprog.cbegin(), accum_inprog.cend(), AccumSamples.begin()); + std::fill_n(accum_iter, SamplesToDo, float2{}); } #endif /* CORE_MIXER_HRTFBASE_H */ diff --git a/3rdparty/openal/core/mixer/mixer_c.cpp b/3rdparty/openal/core/mixer/mixer_c.cpp index 28a92ef7e637..e14454d26d84 100644 --- a/3rdparty/openal/core/mixer/mixer_c.cpp +++ b/3rdparty/openal/core/mixer/mixer_c.cpp @@ -1,14 +1,21 @@ #include "config.h" -#include -#include +#include +#include +#include #include +#include #include "alnumeric.h" +#include "alspan.h" #include "core/bsinc_defs.h" +#include "core/bufferline.h" #include "core/cubic_defs.h" +#include "core/mixer/hrtfdefs.h" +#include "core/resampler_limits.h" #include "defs.h" #include "hrtfbase.h" +#include "opthelpers.h" struct CTag; struct PointTag; @@ -28,191 +35,246 @@ constexpr uint CubicPhaseDiffBits{MixerFracBits - CubicPhaseBits}; constexpr uint CubicPhaseDiffOne{1 << CubicPhaseDiffBits}; constexpr uint CubicPhaseDiffMask{CubicPhaseDiffOne - 1u}; -inline float do_point(const InterpState&, const float *RESTRICT vals, const uint) -{ return vals[0]; } -inline float do_lerp(const InterpState&, const float *RESTRICT vals, const uint frac) -{ return lerpf(vals[0], vals[1], static_cast(frac)*(1.0f/MixerFracOne)); } -inline float do_cubic(const InterpState &istate, const float *RESTRICT vals, const uint frac) +using SamplerNST = float(const al::span, const size_t, const uint) noexcept; + +template +using SamplerT = float(const T&,const al::span,const size_t,const uint) noexcept; + +[[nodiscard]] constexpr +auto do_point(const al::span vals, const size_t pos, const uint) noexcept -> float +{ return vals[pos]; } +[[nodiscard]] constexpr +auto do_lerp(const al::span vals, const size_t pos, const uint frac) noexcept -> float +{ return lerpf(vals[pos+0], vals[pos+1], static_cast(frac)*(1.0f/MixerFracOne)); } +[[nodiscard]] constexpr +auto do_cubic(const CubicState &istate, const al::span vals, const size_t pos, + const uint frac) noexcept -> float { /* Calculate the phase index and factor. */ - const uint pi{frac >> CubicPhaseDiffBits}; + const uint pi{frac >> CubicPhaseDiffBits}; ASSUME(pi < CubicPhaseCount); const float pf{static_cast(frac&CubicPhaseDiffMask) * (1.0f/CubicPhaseDiffOne)}; - const float *RESTRICT fil{al::assume_aligned<16>(istate.cubic.filter[pi].mCoeffs)}; - const float *RESTRICT phd{al::assume_aligned<16>(istate.cubic.filter[pi].mDeltas)}; + const auto fil = al::span{istate.filter[pi].mCoeffs}; + const auto phd = al::span{istate.filter[pi].mDeltas}; /* Apply the phase interpolated filter. */ - return (fil[0] + pf*phd[0])*vals[0] + (fil[1] + pf*phd[1])*vals[1] - + (fil[2] + pf*phd[2])*vals[2] + (fil[3] + pf*phd[3])*vals[3]; + return (fil[0] + pf*phd[0])*vals[pos+0] + (fil[1] + pf*phd[1])*vals[pos+1] + + (fil[2] + pf*phd[2])*vals[pos+2] + (fil[3] + pf*phd[3])*vals[pos+3]; } -inline float do_bsinc(const InterpState &istate, const float *RESTRICT vals, const uint frac) +[[nodiscard]] constexpr +auto do_fastbsinc(const BsincState &bsinc, const al::span vals, const size_t pos, + const uint frac) noexcept -> float { - const size_t m{istate.bsinc.m}; + const size_t m{bsinc.m}; ASSUME(m > 0); + ASSUME(m <= MaxResamplerPadding); /* Calculate the phase index and factor. */ - const uint pi{frac >> BsincPhaseDiffBits}; + const uint pi{frac >> BsincPhaseDiffBits}; ASSUME(pi < BSincPhaseCount); const float pf{static_cast(frac&BsincPhaseDiffMask) * (1.0f/BsincPhaseDiffOne)}; - const float *RESTRICT fil{istate.bsinc.filter + m*pi*2}; - const float *RESTRICT phd{fil + m}; - const float *RESTRICT scd{fil + BSincPhaseCount*2*m}; - const float *RESTRICT spd{scd + m}; + const auto fil = bsinc.filter.subspan(2_uz*pi*m); + const auto phd = fil.subspan(m); - /* Apply the scale and phase interpolated filter. */ + /* Apply the phase interpolated filter. */ float r{0.0f}; - for(size_t j_f{0};j_f < m;j_f++) - r += (fil[j_f] + istate.bsinc.sf*scd[j_f] + pf*(phd[j_f] + istate.bsinc.sf*spd[j_f])) * vals[j_f]; + for(size_t j_f{0};j_f < m;++j_f) + r += (fil[j_f] + pf*phd[j_f]) * vals[pos+j_f]; return r; } -inline float do_fastbsinc(const InterpState &istate, const float *RESTRICT vals, const uint frac) +[[nodiscard]] constexpr +auto do_bsinc(const BsincState &bsinc, const al::span vals, const size_t pos, + const uint frac) noexcept -> float { - const size_t m{istate.bsinc.m}; + const size_t m{bsinc.m}; ASSUME(m > 0); + ASSUME(m <= MaxResamplerPadding); /* Calculate the phase index and factor. */ - const uint pi{frac >> BsincPhaseDiffBits}; + const uint pi{frac >> BsincPhaseDiffBits}; ASSUME(pi < BSincPhaseCount); const float pf{static_cast(frac&BsincPhaseDiffMask) * (1.0f/BsincPhaseDiffOne)}; - const float *RESTRICT fil{istate.bsinc.filter + m*pi*2}; - const float *RESTRICT phd{fil + m}; + const auto fil = bsinc.filter.subspan(2_uz*pi*m); + const auto phd = fil.subspan(m); + const auto scd = fil.subspan(BSincPhaseCount*2_uz*m); + const auto spd = scd.subspan(m); - /* Apply the phase interpolated filter. */ + /* Apply the scale and phase interpolated filter. */ float r{0.0f}; - for(size_t j_f{0};j_f < m;j_f++) - r += (fil[j_f] + pf*phd[j_f]) * vals[j_f]; + for(size_t j_f{0};j_f < m;++j_f) + r += (fil[j_f] + bsinc.sf*scd[j_f] + pf*(phd[j_f] + bsinc.sf*spd[j_f])) * vals[pos+j_f]; return r; } -using SamplerT = float(&)(const InterpState&, const float*RESTRICT, const uint); -template -void DoResample(const InterpState *state, const float *RESTRICT src, uint frac, - const uint increment, const al::span dst) +template +void DoResample(const al::span src, uint frac, const uint increment, + const al::span dst) { - const InterpState istate{*state}; ASSUME(frac < MixerFracOne); - for(float &out : dst) + size_t pos{0}; + std::generate(dst.begin(), dst.end(), [&pos,&frac,src,increment]() -> float { - out = Sampler(istate, src, frac); + const float output{Sampler(src, pos, frac)}; + frac += increment; + pos += frac>>MixerFracBits; + frac &= MixerFracMask; + return output; + }); +} +template Sampler> +void DoResample(const U istate, const al::span src, uint frac, const uint increment, + const al::span dst) +{ + ASSUME(frac < MixerFracOne); + size_t pos{0}; + std::generate(dst.begin(), dst.end(), [istate,src,&pos,&frac,increment]() -> float + { + const float output{Sampler(istate, src, pos, frac)}; frac += increment; - src += frac>>MixerFracBits; + pos += frac>>MixerFracBits; frac &= MixerFracMask; - } + return output; + }); } -inline void ApplyCoeffs(float2 *RESTRICT Values, const size_t IrSize, const ConstHrirSpan Coeffs, - const float left, const float right) +inline void ApplyCoeffs(const al::span Values, const size_t IrSize, + const ConstHrirSpan Coeffs, const float left, const float right) noexcept { ASSUME(IrSize >= MinIrLength); - for(size_t c{0};c < IrSize;++c) - { - Values[c][0] += Coeffs[c][0] * left; - Values[c][1] += Coeffs[c][1] * right; - } + ASSUME(IrSize <= HrirLength); + + auto mix_impulse = [left,right](const float2 &value, const float2 &coeff) noexcept -> float2 + { return float2{{value[0] + coeff[0]*left, value[1] + coeff[1]*right}}; }; + std::transform(Values.cbegin(), Values.cbegin()+ptrdiff_t(IrSize), Coeffs.cbegin(), + Values.begin(), mix_impulse); } -force_inline void MixLine(const al::span InSamples, float *RESTRICT dst, - float &CurrentGain, const float TargetGain, const float delta, const size_t min_len, +force_inline void MixLine(al::span InSamples, const al::span dst, + float &CurrentGain, const float TargetGain, const float delta, const size_t fade_len, size_t Counter) { - float gain{CurrentGain}; - const float step{(TargetGain-gain) * delta}; + const float step{(TargetGain-CurrentGain) * delta}; - size_t pos{0}; - if(!(std::abs(step) > std::numeric_limits::epsilon())) - gain = TargetGain; - else + auto output = dst.begin(); + if(std::abs(step) > std::numeric_limits::epsilon()) { + auto input = InSamples.first(fade_len); + InSamples = InSamples.subspan(fade_len); + + const float gain{CurrentGain}; float step_count{0.0f}; - for(;pos != min_len;++pos) + output = std::transform(input.begin(), input.end(), output, output, + [gain,step,&step_count](const float in, float out) noexcept -> float + { + out += in * (gain + step*step_count); + step_count += 1.0f; + return out; + }); + + if(fade_len < Counter) { - dst[pos] += InSamples[pos] * (gain + step*step_count); - step_count += 1.0f; + CurrentGain = gain + step*step_count; + return; } - if(pos == Counter) - gain = TargetGain; - else - gain += step*step_count; } - CurrentGain = gain; + CurrentGain = TargetGain; - if(!(std::abs(gain) > GainSilenceThreshold)) + if(!(std::abs(TargetGain) > GainSilenceThreshold)) return; - for(;pos != InSamples.size();++pos) - dst[pos] += InSamples[pos] * gain; + + std::transform(InSamples.begin(), InSamples.end(), output, output, + [TargetGain](const float in, const float out) noexcept -> float + { return out + in*TargetGain; }); } } // namespace template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, +void Resample_(const InterpState*, const al::span src, uint frac, const uint increment, const al::span dst) -{ DoResample(state, src, frac, increment, dst); } +{ DoResample(src.subspan(MaxResamplerEdge), frac, increment, dst); } template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, +void Resample_(const InterpState*, const al::span src, uint frac, const uint increment, const al::span dst) -{ DoResample(state, src, frac, increment, dst); } +{ DoResample(src.subspan(MaxResamplerEdge), frac, increment, dst); } template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, +void Resample_(const InterpState *state, const al::span src, uint frac, const uint increment, const al::span dst) -{ DoResample(state, src-1, frac, increment, dst); } +{ + DoResample(std::get(*state), src.subspan(MaxResamplerEdge-1), + frac, increment, dst); +} template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, - const uint increment, const al::span dst) -{ DoResample(state, src-state->bsinc.l, frac, increment, dst); } +void Resample_(const InterpState *state, const al::span src, + uint frac, const uint increment, const al::span dst) +{ + const auto istate = std::get(*state); + ASSUME(istate.l <= MaxResamplerEdge); + DoResample(istate, src.subspan(MaxResamplerEdge-istate.l), frac, + increment, dst); +} template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, +void Resample_(const InterpState *state, const al::span src, uint frac, const uint increment, const al::span dst) -{ DoResample(state, src-state->bsinc.l, frac, increment, dst); } +{ + const auto istate = std::get(*state); + ASSUME(istate.l <= MaxResamplerEdge); + DoResample(istate, src.subspan(MaxResamplerEdge-istate.l), frac, + increment, dst); +} template<> -void MixHrtf_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const MixHrtfFilter *hrtfparams, const size_t BufferSize) -{ MixHrtfBase(InSamples, AccumSamples, IrSize, hrtfparams, BufferSize); } +void MixHrtf_(const al::span InSamples, const al::span AccumSamples, + const uint IrSize, const MixHrtfFilter *hrtfparams, const size_t SamplesToDo) +{ MixHrtfBase(InSamples, AccumSamples, IrSize, hrtfparams, SamplesToDo); } template<> -void MixHrtfBlend_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const HrtfFilter *oldparams, const MixHrtfFilter *newparams, const size_t BufferSize) +void MixHrtfBlend_(const al::span InSamples,const al::span AccumSamples, + const uint IrSize, const HrtfFilter *oldparams, const MixHrtfFilter *newparams, + const size_t SamplesToDo) { MixHrtfBlendBase(InSamples, AccumSamples, IrSize, oldparams, newparams, - BufferSize); + SamplesToDo); } template<> void MixDirectHrtf_(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut, - const al::span InSamples, float2 *AccumSamples, - float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize) + const al::span InSamples, const al::span AccumSamples, + const al::span TempBuf, const al::span ChanState, + const size_t IrSize, const size_t SamplesToDo) { MixDirectHrtfBase(LeftOut, RightOut, InSamples, AccumSamples, TempBuf, ChanState, - IrSize, BufferSize); + IrSize, SamplesToDo); } template<> void Mix_(const al::span InSamples, const al::span OutBuffer, - float *CurrentGains, const float *TargetGains, const size_t Counter, const size_t OutPos) + const al::span CurrentGains, const al::span TargetGains, + const size_t Counter, const size_t OutPos) { const float delta{(Counter > 0) ? 1.0f / static_cast(Counter) : 0.0f}; - const auto min_len = minz(Counter, InSamples.size()); + const auto fade_len = std::min(Counter, InSamples.size()); + auto curgains = CurrentGains.begin(); + auto targetgains = TargetGains.cbegin(); for(FloatBufferLine &output : OutBuffer) - MixLine(InSamples, al::assume_aligned<16>(output.data()+OutPos), *CurrentGains++, - *TargetGains++, delta, min_len, Counter); + MixLine(InSamples, al::span{output}.subspan(OutPos), *curgains++, *targetgains++, delta, + fade_len, Counter); } template<> -void Mix_(const al::span InSamples, float *OutBuffer, float &CurrentGain, - const float TargetGain, const size_t Counter) +void Mix_(const al::span InSamples, const al::span OutBuffer, + float &CurrentGain, const float TargetGain, const size_t Counter) { const float delta{(Counter > 0) ? 1.0f / static_cast(Counter) : 0.0f}; - const auto min_len = minz(Counter, InSamples.size()); + const auto fade_len = std::min(Counter, InSamples.size()); - MixLine(InSamples, al::assume_aligned<16>(OutBuffer), CurrentGain, - TargetGain, delta, min_len, Counter); + MixLine(InSamples, OutBuffer, CurrentGain, TargetGain, delta, fade_len, Counter); } diff --git a/3rdparty/openal/core/mixer/mixer_neon.cpp b/3rdparty/openal/core/mixer/mixer_neon.cpp index ead775af1ebf..600c014b7ab9 100644 --- a/3rdparty/openal/core/mixer/mixer_neon.cpp +++ b/3rdparty/openal/core/mixer/mixer_neon.cpp @@ -2,15 +2,24 @@ #include -#include +#include +#include +#include #include +#include #include "alnumeric.h" +#include "alspan.h" #include "core/bsinc_defs.h" +#include "core/bufferline.h" #include "core/cubic_defs.h" +#include "core/mixer/hrtfdefs.h" +#include "core/resampler_limits.h" #include "defs.h" #include "hrtfbase.h" +#include "opthelpers.h" +struct CTag; struct NEONTag; struct LerpTag; struct CubicTag; @@ -22,6 +31,8 @@ struct FastBSincTag; #pragma GCC target("fpu=neon") #endif +using uint = unsigned int; + namespace { constexpr uint BSincPhaseDiffBits{MixerFracBits - BSincPhaseBits}; @@ -32,6 +43,19 @@ constexpr uint CubicPhaseDiffBits{MixerFracBits - CubicPhaseBits}; constexpr uint CubicPhaseDiffOne{1 << CubicPhaseDiffBits}; constexpr uint CubicPhaseDiffMask{CubicPhaseDiffOne - 1u}; +force_inline +void vtranspose4(float32x4_t &x0, float32x4_t &x1, float32x4_t &x2, float32x4_t &x3) noexcept +{ + 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]; +} + inline float32x4_t set_f4(float l0, float l1, float l2, float l3) { float32x4_t ret{vmovq_n_f32(l0)}; @@ -41,60 +65,62 @@ inline float32x4_t set_f4(float l0, float l1, float l2, float l3) return ret; } -inline void ApplyCoeffs(float2 *RESTRICT Values, const size_t IrSize, const ConstHrirSpan Coeffs, - const float left, const float right) +inline void ApplyCoeffs(const al::span Values, const size_t IrSize, + const ConstHrirSpan Coeffs, const float left, const float right) { - float32x4_t leftright4; - { - float32x2_t leftright2{vmov_n_f32(left)}; - leftright2 = vset_lane_f32(right, leftright2, 1); - leftright4 = vcombine_f32(leftright2, leftright2); - } - ASSUME(IrSize >= MinIrLength); + ASSUME(IrSize <= HrirLength); + + auto dup_samples = [left,right]() -> float32x4_t + { + float32x2_t leftright2{vset_lane_f32(right, vmov_n_f32(left), 1)}; + return vcombine_f32(leftright2, leftright2); + }; + const auto leftright4 = dup_samples(); + + /* Using a loop here instead of std::transform since some builds seem to + * have an issue with accessing an array/span of float32x4_t. + */ for(size_t c{0};c < IrSize;c += 2) { - float32x4_t vals = vld1q_f32(&Values[c][0]); - float32x4_t coefs = vld1q_f32(&Coeffs[c][0]); - - vals = vmlaq_f32(vals, coefs, leftright4); - + auto vals = vld1q_f32(&Values[c][0]); + vals = vmlaq_f32(vals, vld1q_f32(&Coeffs[c][0]), leftright4); vst1q_f32(&Values[c][0], vals); } } -force_inline void MixLine(const al::span InSamples, float *RESTRICT dst, - float &CurrentGain, const float TargetGain, const float delta, const size_t min_len, - const size_t aligned_len, size_t Counter) +force_inline void MixLine(const al::span InSamples, const al::span dst, + float &CurrentGain, const float TargetGain, const float delta, const size_t fade_len, + const size_t realign_len, size_t Counter) { - float gain{CurrentGain}; - const float step{(TargetGain-gain) * delta}; + const auto step = float{(TargetGain-CurrentGain) * delta}; - size_t pos{0}; - if(!(std::abs(step) > std::numeric_limits::epsilon())) - gain = TargetGain; - else + auto pos = size_t{0}; + if(std::abs(step) > std::numeric_limits::epsilon()) { - float step_count{0.0f}; + const auto gain = float{CurrentGain}; + auto step_count = float{0.0f}; /* Mix with applying gain steps in aligned multiples of 4. */ - if(size_t todo{min_len >> 2}) + if(const size_t todo{fade_len >> 2}) { - const float32x4_t four4{vdupq_n_f32(4.0f)}; - const float32x4_t step4{vdupq_n_f32(step)}; - const float32x4_t gain4{vdupq_n_f32(gain)}; - float32x4_t step_count4{vdupq_n_f32(0.0f)}; - step_count4 = vsetq_lane_f32(1.0f, step_count4, 1); - step_count4 = vsetq_lane_f32(2.0f, step_count4, 2); - step_count4 = vsetq_lane_f32(3.0f, step_count4, 3); + const auto four4 = vdupq_n_f32(4.0f); + const auto step4 = vdupq_n_f32(step); + const auto gain4 = vdupq_n_f32(gain); + auto step_count4 = set_f4(0.0f, 1.0f, 2.0f, 3.0f); + + const auto in4 = al::span{reinterpret_cast(InSamples.data()), + InSamples.size()/4}.first(todo); + const auto out4 = al::span{reinterpret_cast(dst.data()), dst.size()/4}; + std::transform(in4.begin(), in4.end(), out4.begin(), out4.begin(), + [gain4,step4,four4,&step_count4](const float32x4_t val4, float32x4_t dry4) + { + /* dry += val * (gain + step*step_count) */ + dry4 = vmlaq_f32(dry4, val4, vmlaq_f32(gain4, step4, step_count4)); + step_count4 = vaddq_f32(step_count4, four4); + return dry4; + }); + pos += in4.size()*4; - do { - const float32x4_t val4 = vld1q_f32(&InSamples[pos]); - float32x4_t dry4 = vld1q_f32(&dst[pos]); - dry4 = vmlaq_f32(dry4, val4, vmlaq_f32(gain4, step4, step_count4)); - step_count4 = vaddq_f32(step_count4, four4); - vst1q_f32(&dst[pos], dry4); - pos += 4; - } while(--todo); /* NOTE: step_count4 now represents the next four counts after the * last four mixed samples, so the lowest element represents the * next step count to apply. @@ -102,152 +128,242 @@ force_inline void MixLine(const al::span InSamples, float *RESTRICT step_count = vgetq_lane_f32(step_count4, 0); } /* Mix with applying left over gain steps that aren't aligned multiples of 4. */ - for(size_t leftover{min_len&3};leftover;++pos,--leftover) + if(const size_t leftover{fade_len&3}) + { + const auto in = InSamples.subspan(pos, leftover); + const auto out = dst.subspan(pos); + + std::transform(in.begin(), in.end(), out.begin(), out.begin(), + [gain,step,&step_count](const float val, float dry) noexcept -> float + { + dry += val * (gain + step*step_count); + step_count += 1.0f; + return dry; + }); + pos += leftover; + } + if(pos < Counter) { - dst[pos] += InSamples[pos] * (gain + step*step_count); - step_count += 1.0f; + CurrentGain = gain + step*step_count; + return; } - if(pos == Counter) - gain = TargetGain; - else - gain += step*step_count; /* Mix until pos is aligned with 4 or the mix is done. */ - for(size_t leftover{aligned_len&3};leftover;++pos,--leftover) - dst[pos] += InSamples[pos] * gain; + if(const size_t leftover{realign_len&3}) + { + const auto in = InSamples.subspan(pos, leftover); + const auto out = dst.subspan(pos); + + std::transform(in.begin(), in.end(), out.begin(), out.begin(), + [TargetGain](const float val, const float dry) noexcept -> float + { return dry + val*TargetGain; }); + pos += leftover; + } } - CurrentGain = gain; + CurrentGain = TargetGain; - if(!(std::abs(gain) > GainSilenceThreshold)) + if(!(std::abs(TargetGain) > GainSilenceThreshold)) return; - if(size_t todo{(InSamples.size()-pos) >> 2}) + if(const size_t todo{(InSamples.size()-pos) >> 2}) { - const float32x4_t gain4 = vdupq_n_f32(gain); - do { - const float32x4_t val4 = vld1q_f32(&InSamples[pos]); - float32x4_t dry4 = vld1q_f32(&dst[pos]); - dry4 = vmlaq_f32(dry4, val4, gain4); - vst1q_f32(&dst[pos], dry4); - pos += 4; - } while(--todo); + const auto in4 = al::span{reinterpret_cast(InSamples.data()), + InSamples.size()/4}.last(todo); + const auto out = dst.subspan(pos); + const auto out4 = al::span{reinterpret_cast(out.data()), out.size()/4}; + + const auto gain4 = vdupq_n_f32(TargetGain); + std::transform(in4.begin(), in4.end(), out4.begin(), out4.begin(), + [gain4](const float32x4_t val4, const float32x4_t dry4) -> float32x4_t + { return vmlaq_f32(dry4, val4, gain4); }); + pos += in4.size()*4; + } + if(const size_t leftover{(InSamples.size()-pos)&3}) + { + const auto in = InSamples.last(leftover); + const auto out = dst.subspan(pos); + + std::transform(in.begin(), in.end(), out.begin(), out.begin(), + [TargetGain](const float val, const float dry) noexcept -> float + { return dry + val*TargetGain; }); } - for(size_t leftover{(InSamples.size()-pos)&3};leftover;++pos,--leftover) - dst[pos] += InSamples[pos] * gain; } } // namespace template<> -void Resample_(const InterpState*, const float *RESTRICT src, uint frac, +void Resample_(const InterpState*, const al::span src, uint frac, const uint increment, const al::span dst) { ASSUME(frac < MixerFracOne); - const int32x4_t increment4 = vdupq_n_s32(static_cast(increment*4)); + const uint32x4_t increment4 = vdupq_n_u32(increment*4u); const float32x4_t fracOne4 = vdupq_n_f32(1.0f/MixerFracOne); - const int32x4_t fracMask4 = vdupq_n_s32(MixerFracMask); - alignas(16) uint pos_[4], frac_[4]; - int32x4_t pos4, frac4; + const uint32x4_t fracMask4 = vdupq_n_u32(MixerFracMask); - InitPosArrays(frac, increment, frac_, pos_); - frac4 = vld1q_s32(reinterpret_cast(frac_)); - pos4 = vld1q_s32(reinterpret_cast(pos_)); + alignas(16) std::array pos_{}, frac_{}; + InitPosArrays(MaxResamplerEdge, frac, increment, al::span{frac_}, al::span{pos_}); + uint32x4_t frac4 = vld1q_u32(frac_.data()); + uint32x4_t pos4 = vld1q_u32(pos_.data()); - auto dst_iter = dst.begin(); - for(size_t todo{dst.size()>>2};todo;--todo) + auto vecout = al::span{reinterpret_cast(dst.data()), dst.size()/4}; + std::generate(vecout.begin(), vecout.end(), [=,&pos4,&frac4]() -> float32x4_t { - const int pos0{vgetq_lane_s32(pos4, 0)}; - const int pos1{vgetq_lane_s32(pos4, 1)}; - const int pos2{vgetq_lane_s32(pos4, 2)}; - const int pos3{vgetq_lane_s32(pos4, 3)}; + const uint pos0{vgetq_lane_u32(pos4, 0)}; + const uint pos1{vgetq_lane_u32(pos4, 1)}; + const uint pos2{vgetq_lane_u32(pos4, 2)}; + const uint pos3{vgetq_lane_u32(pos4, 3)}; + ASSUME(pos0 <= pos1); ASSUME(pos1 <= pos2); ASSUME(pos2 <= pos3); const float32x4_t val1{set_f4(src[pos0], src[pos1], src[pos2], src[pos3])}; - const float32x4_t val2{set_f4(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1])}; + const float32x4_t val2{set_f4(src[pos0+1_uz], src[pos1+1_uz], src[pos2+1_uz], src[pos3+1_uz])}; /* val1 + (val2-val1)*mu */ const float32x4_t r0{vsubq_f32(val2, val1)}; - const float32x4_t mu{vmulq_f32(vcvtq_f32_s32(frac4), fracOne4)}; + const float32x4_t mu{vmulq_f32(vcvtq_f32_u32(frac4), fracOne4)}; const float32x4_t out{vmlaq_f32(val1, mu, r0)}; - vst1q_f32(dst_iter, out); - dst_iter += 4; - - frac4 = vaddq_s32(frac4, increment4); - pos4 = vaddq_s32(pos4, vshrq_n_s32(frac4, MixerFracBits)); - frac4 = vandq_s32(frac4, fracMask4); - } + frac4 = vaddq_u32(frac4, increment4); + pos4 = vaddq_u32(pos4, vshrq_n_u32(frac4, MixerFracBits)); + frac4 = vandq_u32(frac4, fracMask4); + return out; + }); if(size_t todo{dst.size()&3}) { - src += static_cast(vgetq_lane_s32(pos4, 0)); - frac = static_cast(vgetq_lane_s32(frac4, 0)); + auto pos = size_t{vgetq_lane_u32(pos4, 0)}; + frac = vgetq_lane_u32(frac4, 0); - do { - *(dst_iter++) = lerpf(src[0], src[1], static_cast(frac) * (1.0f/MixerFracOne)); + const auto out = dst.last(todo); + std::generate(out.begin(), out.end(), [&pos,&frac,src,increment] + { + const float output{lerpf(src[pos+0], src[pos+1], + static_cast(frac) * (1.0f/MixerFracOne))}; frac += increment; - src += frac>>MixerFracBits; + pos += frac>>MixerFracBits; frac &= MixerFracMask; - } while(--todo); + return output; + }); } } template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, - const uint increment, const al::span dst) +void Resample_(const InterpState *state, const al::span src, + uint frac, const uint increment, const al::span dst) { ASSUME(frac < MixerFracOne); - const CubicCoefficients *RESTRICT filter = al::assume_aligned<16>(state->cubic.filter); + const auto filter = std::get(*state).filter; - src -= 1; - for(float &out_sample : dst) + const uint32x4_t increment4{vdupq_n_u32(increment*4u)}; + const uint32x4_t fracMask4{vdupq_n_u32(MixerFracMask)}; + const float32x4_t fracDiffOne4{vdupq_n_f32(1.0f/CubicPhaseDiffOne)}; + const uint32x4_t fracDiffMask4{vdupq_n_u32(CubicPhaseDiffMask)}; + + alignas(16) std::array pos_{}, frac_{}; + InitPosArrays(MaxResamplerEdge-1, frac, increment, al::span{frac_}, al::span{pos_}); + uint32x4_t frac4{vld1q_u32(frac_.data())}; + uint32x4_t pos4{vld1q_u32(pos_.data())}; + + auto vecout = al::span{reinterpret_cast(dst.data()), dst.size()/4}; + std::generate(vecout.begin(), vecout.end(), [=,&pos4,&frac4] + { + const uint pos0{vgetq_lane_u32(pos4, 0)}; + const uint pos1{vgetq_lane_u32(pos4, 1)}; + const uint pos2{vgetq_lane_u32(pos4, 2)}; + const uint pos3{vgetq_lane_u32(pos4, 3)}; + ASSUME(pos0 <= pos1); ASSUME(pos1 <= pos2); ASSUME(pos2 <= pos3); + const float32x4_t val0{vld1q_f32(&src[pos0])}; + const float32x4_t val1{vld1q_f32(&src[pos1])}; + const float32x4_t val2{vld1q_f32(&src[pos2])}; + const float32x4_t val3{vld1q_f32(&src[pos3])}; + + const uint32x4_t pi4{vshrq_n_u32(frac4, CubicPhaseDiffBits)}; + const uint pi0{vgetq_lane_u32(pi4, 0)}; ASSUME(pi0 < CubicPhaseCount); + const uint pi1{vgetq_lane_u32(pi4, 1)}; ASSUME(pi1 < CubicPhaseCount); + const uint pi2{vgetq_lane_u32(pi4, 2)}; ASSUME(pi2 < CubicPhaseCount); + const uint pi3{vgetq_lane_u32(pi4, 3)}; ASSUME(pi3 < CubicPhaseCount); + + const float32x4_t pf4{vmulq_f32(vcvtq_f32_u32(vandq_u32(frac4, fracDiffMask4)), + fracDiffOne4)}; + + float32x4_t r0{vmulq_f32(val0, + vmlaq_f32(vld1q_f32(filter[pi0].mCoeffs.data()), vdupq_lane_f32(vget_low_f32(pf4), 0), + vld1q_f32(filter[pi0].mDeltas.data())))}; + float32x4_t r1{vmulq_f32(val1, + vmlaq_f32(vld1q_f32(filter[pi1].mCoeffs.data()), vdupq_lane_f32(vget_low_f32(pf4), 1), + vld1q_f32(filter[pi1].mDeltas.data())))}; + float32x4_t r2{vmulq_f32(val2, + vmlaq_f32(vld1q_f32(filter[pi2].mCoeffs.data()), vdupq_lane_f32(vget_high_f32(pf4), 0), + vld1q_f32(filter[pi2].mDeltas.data())))}; + float32x4_t r3{vmulq_f32(val3, + vmlaq_f32(vld1q_f32(filter[pi3].mCoeffs.data()), vdupq_lane_f32(vget_high_f32(pf4), 1), + vld1q_f32(filter[pi3].mDeltas.data())))}; + + vtranspose4(r0, r1, r2, r3); + r0 = vaddq_f32(vaddq_f32(r0, r1), vaddq_f32(r2, r3)); + + frac4 = vaddq_u32(frac4, increment4); + pos4 = vaddq_u32(pos4, vshrq_n_u32(frac4, MixerFracBits)); + frac4 = vandq_u32(frac4, fracMask4); + return r0; + }); + + if(const size_t todo{dst.size()&3}) { - const uint pi{frac >> CubicPhaseDiffBits}; - const float pf{static_cast(frac&CubicPhaseDiffMask) * (1.0f/CubicPhaseDiffOne)}; - const float32x4_t pf4{vdupq_n_f32(pf)}; + auto pos = size_t{vgetq_lane_u32(pos4, 0)}; + frac = vgetq_lane_u32(frac4, 0); - /* Apply the phase interpolated filter. */ + auto out = dst.last(todo); + std::generate(out.begin(), out.end(), [&pos,&frac,src,increment,filter] + { + const uint pi{frac >> CubicPhaseDiffBits}; ASSUME(pi < CubicPhaseCount); + const float pf{static_cast(frac&CubicPhaseDiffMask) * (1.0f/CubicPhaseDiffOne)}; + const float32x4_t pf4{vdupq_n_f32(pf)}; - /* f = fil + pf*phd */ - const float32x4_t f4 = vmlaq_f32(vld1q_f32(filter[pi].mCoeffs), pf4, - vld1q_f32(filter[pi].mDeltas)); - /* r = f*src */ - float32x4_t r4{vmulq_f32(f4, vld1q_f32(src))}; + const float32x4_t f4{vmlaq_f32(vld1q_f32(filter[pi].mCoeffs.data()), pf4, + vld1q_f32(filter[pi].mDeltas.data()))}; + float32x4_t r4{vmulq_f32(f4, vld1q_f32(&src[pos]))}; - r4 = vaddq_f32(r4, vrev64q_f32(r4)); - out_sample = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); + r4 = vaddq_f32(r4, vrev64q_f32(r4)); + const float output{vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0)}; - frac += increment; - src += frac>>MixerFracBits; - frac &= MixerFracMask; + frac += increment; + pos += frac>>MixerFracBits; + frac &= MixerFracMask; + return output; + }); } } template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, - const uint increment, const al::span dst) +void Resample_(const InterpState *state, const al::span src, + uint frac, const uint increment, const al::span dst) { - const float *const filter{state->bsinc.filter}; - const float32x4_t sf4{vdupq_n_f32(state->bsinc.sf)}; - const size_t m{state->bsinc.m}; + const auto &bsinc = std::get(*state); + const auto sf4 = vdupq_n_f32(bsinc.sf); + const auto m = size_t{bsinc.m}; ASSUME(m > 0); + ASSUME(m <= MaxResamplerPadding); ASSUME(frac < MixerFracOne); - src -= state->bsinc.l; - for(float &out_sample : dst) + const auto filter = bsinc.filter.first(4_uz*BSincPhaseCount*m); + + ASSUME(bsinc.l <= MaxResamplerEdge); + auto pos = size_t{MaxResamplerEdge-bsinc.l}; + std::generate(dst.begin(), dst.end(), [&pos,&frac,src,increment,sf4,m,filter]() -> float { // Calculate the phase index and factor. - const uint pi{frac >> BSincPhaseDiffBits}; + const uint pi{frac >> BSincPhaseDiffBits}; ASSUME(pi < BSincPhaseCount); const float pf{static_cast(frac&BSincPhaseDiffMask) * (1.0f/BSincPhaseDiffOne)}; // Apply the scale and phase interpolated filter. float32x4_t r4{vdupq_n_f32(0.0f)}; { const float32x4_t pf4{vdupq_n_f32(pf)}; - const float *RESTRICT fil{filter + m*pi*2}; - const float *RESTRICT phd{fil + m}; - const float *RESTRICT scd{fil + BSincPhaseCount*2*m}; - const float *RESTRICT spd{scd + m}; + const auto fil = filter.subspan(2_uz*pi*m); + const auto phd = fil.subspan(m); + const auto scd = fil.subspan(2_uz*BSincPhaseCount*m); + const auto spd = scd.subspan(m); size_t td{m >> 2}; size_t j{0u}; @@ -257,41 +373,46 @@ void Resample_(const InterpState *state, const float *RESTRICT vmlaq_f32(vld1q_f32(&fil[j]), sf4, vld1q_f32(&scd[j])), pf4, vmlaq_f32(vld1q_f32(&phd[j]), sf4, vld1q_f32(&spd[j]))); /* r += f*src */ - r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j])); + r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[pos+j])); j += 4; } while(--td); } r4 = vaddq_f32(r4, vrev64q_f32(r4)); - out_sample = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); + const float output{vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0)}; frac += increment; - src += frac>>MixerFracBits; + pos += frac>>MixerFracBits; frac &= MixerFracMask; - } + return output; + }); } template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, - const uint increment, const al::span dst) +void Resample_(const InterpState *state, const al::span src, + uint frac, const uint increment, const al::span dst) { - const float *const filter{state->bsinc.filter}; - const size_t m{state->bsinc.m}; + const auto &bsinc = std::get(*state); + const auto m = size_t{bsinc.m}; ASSUME(m > 0); + ASSUME(m <= MaxResamplerPadding); ASSUME(frac < MixerFracOne); - src -= state->bsinc.l; - for(float &out_sample : dst) + const auto filter = bsinc.filter.first(2_uz*BSincPhaseCount*m); + + ASSUME(bsinc.l <= MaxResamplerEdge); + auto pos = size_t{MaxResamplerEdge-bsinc.l}; + std::generate(dst.begin(), dst.end(), [&pos,&frac,src,increment,m,filter]() -> float { // Calculate the phase index and factor. - const uint pi{frac >> BSincPhaseDiffBits}; + const uint pi{frac >> BSincPhaseDiffBits}; ASSUME(pi < BSincPhaseCount); const float pf{static_cast(frac&BSincPhaseDiffMask) * (1.0f/BSincPhaseDiffOne)}; // Apply the phase interpolated filter. float32x4_t r4{vdupq_n_f32(0.0f)}; { const float32x4_t pf4{vdupq_n_f32(pf)}; - const float *RESTRICT fil{filter + m*pi*2}; - const float *RESTRICT phd{fil + m}; + const auto fil = filter.subspan(2_uz*pi*m); + const auto phd = fil.subspan(m); size_t td{m >> 2}; size_t j{0u}; @@ -299,64 +420,75 @@ void Resample_(const InterpState *state, const float *REST /* f = fil + pf*phd */ const float32x4_t f4 = vmlaq_f32(vld1q_f32(&fil[j]), pf4, vld1q_f32(&phd[j])); /* r += f*src */ - r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j])); + r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[pos+j])); j += 4; } while(--td); } r4 = vaddq_f32(r4, vrev64q_f32(r4)); - out_sample = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); + const float output{vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0)}; frac += increment; - src += frac>>MixerFracBits; + pos += frac>>MixerFracBits; frac &= MixerFracMask; - } + return output; + }); } template<> -void MixHrtf_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const MixHrtfFilter *hrtfparams, const size_t BufferSize) -{ MixHrtfBase(InSamples, AccumSamples, IrSize, hrtfparams, BufferSize); } +void MixHrtf_(const al::span InSamples, const al::span AccumSamples, + const uint IrSize, const MixHrtfFilter *hrtfparams, const size_t SamplesToDo) +{ MixHrtfBase(InSamples, AccumSamples, IrSize, hrtfparams, SamplesToDo); } template<> -void MixHrtfBlend_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const HrtfFilter *oldparams, const MixHrtfFilter *newparams, const size_t BufferSize) +void MixHrtfBlend_(const al::span InSamples, + const al::span AccumSamples, const uint IrSize, const HrtfFilter *oldparams, + const MixHrtfFilter *newparams, const size_t SamplesToDo) { MixHrtfBlendBase(InSamples, AccumSamples, IrSize, oldparams, newparams, - BufferSize); + SamplesToDo); } template<> void MixDirectHrtf_(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut, - const al::span InSamples, float2 *AccumSamples, - float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize) + const al::span InSamples, const al::span AccumSamples, + const al::span TempBuf, const al::span ChanState, + const size_t IrSize, const size_t SamplesToDo) { MixDirectHrtfBase(LeftOut, RightOut, InSamples, AccumSamples, TempBuf, ChanState, - IrSize, BufferSize); + IrSize, SamplesToDo); } template<> -void Mix_(const al::span InSamples, const al::span OutBuffer, - float *CurrentGains, const float *TargetGains, const size_t Counter, const size_t OutPos) +void Mix_(const al::span InSamples,const al::span OutBuffer, + const al::span CurrentGains, const al::span TargetGains, + const size_t Counter, const size_t OutPos) { + if((OutPos&3) != 0) UNLIKELY + return Mix_(InSamples, OutBuffer, CurrentGains, TargetGains, Counter, OutPos); + const float delta{(Counter > 0) ? 1.0f / static_cast(Counter) : 0.0f}; - const auto min_len = minz(Counter, InSamples.size()); - const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len; + const auto fade_len = std::min(Counter, InSamples.size()); + const auto realign_len = std::min((fade_len+3_uz) & ~3_uz, InSamples.size()) - fade_len; + auto curgains = CurrentGains.begin(); + auto targetgains = TargetGains.cbegin(); for(FloatBufferLine &output : OutBuffer) - MixLine(InSamples, al::assume_aligned<16>(output.data()+OutPos), *CurrentGains++, - *TargetGains++, delta, min_len, aligned_len, Counter); + MixLine(InSamples, al::span{output}.subspan(OutPos), *curgains++, *targetgains++, delta, + fade_len, realign_len, Counter); } template<> -void Mix_(const al::span InSamples, float *OutBuffer, float &CurrentGain, - const float TargetGain, const size_t Counter) +void Mix_(const al::span InSamples, const al::span OutBuffer, + float &CurrentGain, const float TargetGain, const size_t Counter) { + if((reinterpret_cast(OutBuffer.data())&15) != 0) UNLIKELY + return Mix_(InSamples, OutBuffer, CurrentGain, TargetGain, Counter); + const float delta{(Counter > 0) ? 1.0f / static_cast(Counter) : 0.0f}; - const auto min_len = minz(Counter, InSamples.size()); - const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len; + const auto fade_len = std::min(Counter, InSamples.size()); + const auto realign_len = std::min((fade_len+3_uz) & ~3_uz, InSamples.size()) - fade_len; - MixLine(InSamples, al::assume_aligned<16>(OutBuffer), CurrentGain, TargetGain, delta, min_len, - aligned_len, Counter); + MixLine(InSamples, OutBuffer, CurrentGain, TargetGain, delta, fade_len, realign_len, Counter); } diff --git a/3rdparty/openal/core/mixer/mixer_sse.cpp b/3rdparty/openal/core/mixer/mixer_sse.cpp index 70f77c14cf91..097cd9333087 100644 --- a/3rdparty/openal/core/mixer/mixer_sse.cpp +++ b/3rdparty/openal/core/mixer/mixer_sse.cpp @@ -1,16 +1,27 @@ #include "config.h" +#include #include -#include +#include +#include +#include +#include #include +#include #include "alnumeric.h" +#include "alspan.h" #include "core/bsinc_defs.h" +#include "core/bufferline.h" #include "core/cubic_defs.h" +#include "core/mixer/hrtfdefs.h" +#include "core/resampler_limits.h" #include "defs.h" #include "hrtfbase.h" +#include "opthelpers.h" +struct CTag; struct SSETag; struct CubicTag; struct BSincTag; @@ -31,42 +42,48 @@ constexpr uint CubicPhaseDiffBits{MixerFracBits - CubicPhaseBits}; constexpr uint CubicPhaseDiffOne{1 << CubicPhaseDiffBits}; constexpr uint CubicPhaseDiffMask{CubicPhaseDiffOne - 1u}; -#define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z)) +force_inline __m128 vmadd(const __m128 x, const __m128 y, const __m128 z) noexcept +{ return _mm_add_ps(x, _mm_mul_ps(y, z)); } -inline void ApplyCoeffs(float2 *RESTRICT Values, const size_t IrSize, const ConstHrirSpan Coeffs, - const float left, const float right) +inline void ApplyCoeffs(const al::span Values, const size_t IrSize, + const ConstHrirSpan Coeffs, const float left, const float right) { - const __m128 lrlr{_mm_setr_ps(left, right, left, right)}; - ASSUME(IrSize >= MinIrLength); + ASSUME(IrSize <= HrirLength); + const auto lrlr = _mm_setr_ps(left, right, left, right); + /* Round up the IR size to a multiple of 2 for SIMD (2 IRs for 2 channels + * is 4 floats), to avoid cutting the last sample for odd IR counts. The + * underlying HRIR is a fixed-size multiple of 2, any extra samples are + * either 0 (silence) or more IR samples that get applied for "free". + */ + const auto count4 = size_t{(IrSize+1) >> 1}; + /* This isn't technically correct to test alignment, but it's true for * systems that support SSE, which is the only one that needs to know the * alignment of Values (which alternates between 8- and 16-byte aligned). */ - if(!(reinterpret_cast(Values)&15)) + if(!(reinterpret_cast(Values.data())&15)) { - for(size_t i{0};i < IrSize;i += 2) - { - const __m128 coeffs{_mm_load_ps(Coeffs[i].data())}; - __m128 vals{_mm_load_ps(Values[i].data())}; - vals = MLA4(vals, lrlr, coeffs); - _mm_store_ps(Values[i].data(), vals); - } + const auto vals4 = al::span{reinterpret_cast<__m128*>(Values[0].data()), count4}; + const auto coeffs4 = al::span{reinterpret_cast(Coeffs[0].data()), count4}; + + std::transform(vals4.cbegin(), vals4.cend(), coeffs4.cbegin(), vals4.begin(), + [lrlr](const __m128 &val, const __m128 &coeff) -> __m128 + { return vmadd(val, coeff, lrlr); }); } else { - __m128 imp0, imp1; - __m128 coeffs{_mm_load_ps(Coeffs[0].data())}; - __m128 vals{_mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<__m64*>(Values[0].data()))}; - imp0 = _mm_mul_ps(lrlr, coeffs); + auto coeffs = _mm_load_ps(Coeffs[0].data()); + auto vals = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<__m64*>(Values[0].data())); + auto imp0 = _mm_mul_ps(lrlr, coeffs); vals = _mm_add_ps(imp0, vals); _mm_storel_pi(reinterpret_cast<__m64*>(Values[0].data()), vals); - size_t td{((IrSize+1)>>1) - 1}; + size_t td{count4 - 1}; size_t i{1}; do { coeffs = _mm_load_ps(Coeffs[i+1].data()); vals = _mm_load_ps(Values[i].data()); - imp1 = _mm_mul_ps(lrlr, coeffs); + const auto imp1 = _mm_mul_ps(lrlr, coeffs); imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2)); vals = _mm_add_ps(imp0, vals); _mm_store_ps(Values[i].data(), vals); @@ -80,37 +97,38 @@ inline void ApplyCoeffs(float2 *RESTRICT Values, const size_t IrSize, const Cons } } -force_inline void MixLine(const al::span InSamples, float *RESTRICT dst, - float &CurrentGain, const float TargetGain, const float delta, const size_t min_len, - const size_t aligned_len, size_t Counter) +force_inline void MixLine(const al::span InSamples, const al::span dst, + float &CurrentGain, const float TargetGain, const float delta, const size_t fade_len, + const size_t realign_len, size_t Counter) { - float gain{CurrentGain}; - const float step{(TargetGain-gain) * delta}; + const auto step = float{(TargetGain-CurrentGain) * delta}; size_t pos{0}; - if(!(std::abs(step) > std::numeric_limits::epsilon())) - gain = TargetGain; - else + if(std::abs(step) > std::numeric_limits::epsilon()) { - float step_count{0.0f}; + const auto gain = CurrentGain; + auto step_count = 0.0f; /* Mix with applying gain steps in aligned multiples of 4. */ - if(size_t todo{min_len >> 2}) + if(const size_t todo{fade_len >> 2}) { - const __m128 four4{_mm_set1_ps(4.0f)}; - const __m128 step4{_mm_set1_ps(step)}; - const __m128 gain4{_mm_set1_ps(gain)}; - __m128 step_count4{_mm_setr_ps(0.0f, 1.0f, 2.0f, 3.0f)}; - do { - const __m128 val4{_mm_load_ps(&InSamples[pos])}; - __m128 dry4{_mm_load_ps(&dst[pos])}; - - /* dry += val * (gain + step*step_count) */ - dry4 = MLA4(dry4, val4, MLA4(gain4, step4, step_count4)); + const auto four4 = _mm_set1_ps(4.0f); + const auto step4 = _mm_set1_ps(step); + const auto gain4 = _mm_set1_ps(gain); + auto step_count4 = _mm_setr_ps(0.0f, 1.0f, 2.0f, 3.0f); + + const auto in4 = al::span{reinterpret_cast(InSamples.data()), + InSamples.size()/4}.first(todo); + const auto out4 = al::span{reinterpret_cast<__m128*>(dst.data()), dst.size()/4}; + std::transform(in4.begin(), in4.end(), out4.begin(), out4.begin(), + [gain4,step4,four4,&step_count4](const __m128 val4, __m128 dry4) -> __m128 + { + /* dry += val * (gain + step*step_count) */ + dry4 = vmadd(dry4, val4, vmadd(gain4, step4, step_count4)); + step_count4 = _mm_add_ps(step_count4, four4); + return dry4; + }); + pos += in4.size()*4; - _mm_store_ps(&dst[pos], dry4); - step_count4 = _mm_add_ps(step_count4, four4); - pos += 4; - } while(--todo); /* NOTE: step_count4 now represents the next four counts after the * last four mixed samples, so the lowest element represents the * next step count to apply. @@ -118,210 +136,258 @@ force_inline void MixLine(const al::span InSamples, float *RESTRICT step_count = _mm_cvtss_f32(step_count4); } /* Mix with applying left over gain steps that aren't aligned multiples of 4. */ - for(size_t leftover{min_len&3};leftover;++pos,--leftover) + if(const size_t leftover{fade_len&3}) { - dst[pos] += InSamples[pos] * (gain + step*step_count); - step_count += 1.0f; + const auto in = InSamples.subspan(pos, leftover); + const auto out = dst.subspan(pos); + + std::transform(in.begin(), in.end(), out.begin(), out.begin(), + [gain,step,&step_count](const float val, float dry) noexcept -> float + { + dry += val * (gain + step*step_count); + step_count += 1.0f; + return dry; + }); + pos += leftover; + } + if(pos < Counter) + { + CurrentGain = gain + step*step_count; + return; } - if(pos == Counter) - gain = TargetGain; - else - gain += step*step_count; /* Mix until pos is aligned with 4 or the mix is done. */ - for(size_t leftover{aligned_len&3};leftover;++pos,--leftover) - dst[pos] += InSamples[pos] * gain; + if(const size_t leftover{realign_len&3}) + { + const auto in = InSamples.subspan(pos, leftover); + const auto out = dst.subspan(pos); + + std::transform(in.begin(), in.end(), out.begin(), out.begin(), + [TargetGain](const float val, const float dry) noexcept -> float + { return dry + val*TargetGain; }); + pos += leftover; + } } - CurrentGain = gain; + CurrentGain = TargetGain; - if(!(std::abs(gain) > GainSilenceThreshold)) + if(!(std::abs(TargetGain) > GainSilenceThreshold)) return; if(size_t todo{(InSamples.size()-pos) >> 2}) { - const __m128 gain4{_mm_set1_ps(gain)}; - do { - const __m128 val4{_mm_load_ps(&InSamples[pos])}; - __m128 dry4{_mm_load_ps(&dst[pos])}; - dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4)); - _mm_store_ps(&dst[pos], dry4); - pos += 4; - } while(--todo); + const auto in4 = al::span{reinterpret_cast(InSamples.data()), + InSamples.size()/4}.last(todo); + const auto out = dst.subspan(pos); + const auto out4 = al::span{reinterpret_cast<__m128*>(out.data()), out.size()/4}; + + const auto gain4 = _mm_set1_ps(TargetGain); + std::transform(in4.begin(), in4.end(), out4.begin(), out4.begin(), + [gain4](const __m128 val4, const __m128 dry4) -> __m128 + { return vmadd(dry4, val4, gain4); }); + pos += in4.size()*4; + } + if(const size_t leftover{(InSamples.size()-pos)&3}) + { + const auto in = InSamples.last(leftover); + const auto out = dst.subspan(pos); + + std::transform(in.begin(), in.end(), out.begin(), out.begin(), + [TargetGain](const float val, const float dry) noexcept -> float + { return dry + val*TargetGain; }); } - for(size_t leftover{(InSamples.size()-pos)&3};leftover;++pos,--leftover) - dst[pos] += InSamples[pos] * gain; } } // namespace template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, - const uint increment, const al::span dst) +void Resample_(const InterpState *state, const al::span src, + uint frac, const uint increment, const al::span dst) { ASSUME(frac < MixerFracOne); - const CubicCoefficients *RESTRICT filter = al::assume_aligned<16>(state->cubic.filter); + const auto filter = std::get(*state).filter; - src -= 1; - for(float &out_sample : dst) + size_t pos{MaxResamplerEdge-1}; + std::generate(dst.begin(), dst.end(), [&pos,&frac,src,increment,filter]() -> float { - const uint pi{frac >> CubicPhaseDiffBits}; + const uint pi{frac >> CubicPhaseDiffBits}; ASSUME(pi < CubicPhaseCount); const float pf{static_cast(frac&CubicPhaseDiffMask) * (1.0f/CubicPhaseDiffOne)}; const __m128 pf4{_mm_set1_ps(pf)}; /* Apply the phase interpolated filter. */ /* f = fil + pf*phd */ - const __m128 f4 = MLA4(_mm_load_ps(filter[pi].mCoeffs), pf4, - _mm_load_ps(filter[pi].mDeltas)); + const __m128 f4 = vmadd(_mm_load_ps(filter[pi].mCoeffs.data()), pf4, + _mm_load_ps(filter[pi].mDeltas.data())); /* r = f*src */ - __m128 r4{_mm_mul_ps(f4, _mm_loadu_ps(src))}; + __m128 r4{_mm_mul_ps(f4, _mm_loadu_ps(&src[pos]))}; r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); - out_sample = _mm_cvtss_f32(r4); + const float output{_mm_cvtss_f32(r4)}; frac += increment; - src += frac>>MixerFracBits; + pos += frac>>MixerFracBits; frac &= MixerFracMask; - } + return output; + }); } template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, - const uint increment, const al::span dst) +void Resample_(const InterpState *state, const al::span src, + uint frac, const uint increment, const al::span dst) { - const float *const filter{state->bsinc.filter}; - const __m128 sf4{_mm_set1_ps(state->bsinc.sf)}; - const size_t m{state->bsinc.m}; + const auto &bsinc = std::get(*state); + const auto sf4 = _mm_set1_ps(bsinc.sf); + const auto m = size_t{bsinc.m}; ASSUME(m > 0); + ASSUME(m <= MaxResamplerPadding); ASSUME(frac < MixerFracOne); - src -= state->bsinc.l; - for(float &out_sample : dst) + const auto filter = bsinc.filter.first(4_uz*BSincPhaseCount*m); + + ASSUME(bsinc.l <= MaxResamplerEdge); + auto pos = size_t{MaxResamplerEdge-bsinc.l}; + std::generate(dst.begin(), dst.end(), [&pos,&frac,src,increment,sf4,m,filter]() -> float { // Calculate the phase index and factor. - const uint pi{frac >> BSincPhaseDiffBits}; + const size_t pi{frac >> BSincPhaseDiffBits}; ASSUME(pi < BSincPhaseCount); const float pf{static_cast(frac&BSincPhaseDiffMask) * (1.0f/BSincPhaseDiffOne)}; // Apply the scale and phase interpolated filter. - __m128 r4{_mm_setzero_ps()}; + auto r4 = _mm_setzero_ps(); { - const __m128 pf4{_mm_set1_ps(pf)}; - const float *RESTRICT fil{filter + m*pi*2}; - const float *RESTRICT phd{fil + m}; - const float *RESTRICT scd{fil + BSincPhaseCount*2*m}; - const float *RESTRICT spd{scd + m}; - size_t td{m >> 2}; - size_t j{0u}; + const auto pf4 = _mm_set1_ps(pf); + const auto fil = filter.subspan(2_uz*pi*m); + const auto phd = fil.subspan(m); + const auto scd = fil.subspan(2_uz*BSincPhaseCount*m); + const auto spd = scd.subspan(m); + auto td = size_t{m >> 2}; + auto j = size_t{0}; do { /* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */ - const __m128 f4 = MLA4( - MLA4(_mm_load_ps(&fil[j]), sf4, _mm_load_ps(&scd[j])), - pf4, MLA4(_mm_load_ps(&phd[j]), sf4, _mm_load_ps(&spd[j]))); + const __m128 f4 = vmadd( + vmadd(_mm_load_ps(&fil[j]), sf4, _mm_load_ps(&scd[j])), + pf4, vmadd(_mm_load_ps(&phd[j]), sf4, _mm_load_ps(&spd[j]))); /* r += f*src */ - r4 = MLA4(r4, f4, _mm_loadu_ps(&src[j])); + r4 = vmadd(r4, f4, _mm_loadu_ps(&src[pos+j])); j += 4; } while(--td); } r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); - out_sample = _mm_cvtss_f32(r4); + const auto output = _mm_cvtss_f32(r4); frac += increment; - src += frac>>MixerFracBits; + pos += frac>>MixerFracBits; frac &= MixerFracMask; - } + return output; + }); } template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, - const uint increment, const al::span dst) +void Resample_(const InterpState *state, const al::span src, + uint frac, const uint increment, const al::span dst) { - const float *const filter{state->bsinc.filter}; - const size_t m{state->bsinc.m}; + const auto &bsinc = std::get(*state); + const auto m = size_t{bsinc.m}; ASSUME(m > 0); + ASSUME(m <= MaxResamplerPadding); ASSUME(frac < MixerFracOne); - src -= state->bsinc.l; - for(float &out_sample : dst) + const auto filter = bsinc.filter.first(2_uz*m*BSincPhaseCount); + + ASSUME(bsinc.l <= MaxResamplerEdge); + size_t pos{MaxResamplerEdge-bsinc.l}; + std::generate(dst.begin(), dst.end(), [&pos,&frac,src,increment,filter,m]() -> float { // Calculate the phase index and factor. - const uint pi{frac >> BSincPhaseDiffBits}; + const size_t pi{frac >> BSincPhaseDiffBits}; ASSUME(pi < BSincPhaseCount); const float pf{static_cast(frac&BSincPhaseDiffMask) * (1.0f/BSincPhaseDiffOne)}; // Apply the phase interpolated filter. - __m128 r4{_mm_setzero_ps()}; + auto r4 = _mm_setzero_ps(); { - const __m128 pf4{_mm_set1_ps(pf)}; - const float *RESTRICT fil{filter + m*pi*2}; - const float *RESTRICT phd{fil + m}; - size_t td{m >> 2}; - size_t j{0u}; + const auto pf4 = _mm_set1_ps(pf); + const auto fil = filter.subspan(2_uz*m*pi); + const auto phd = fil.subspan(m); + auto td = size_t{m >> 2}; + auto j = size_t{0}; do { /* f = fil + pf*phd */ - const __m128 f4 = MLA4(_mm_load_ps(&fil[j]), pf4, _mm_load_ps(&phd[j])); + const auto f4 = vmadd(_mm_load_ps(&fil[j]), pf4, _mm_load_ps(&phd[j])); /* r += f*src */ - r4 = MLA4(r4, f4, _mm_loadu_ps(&src[j])); + r4 = vmadd(r4, f4, _mm_loadu_ps(&src[pos+j])); j += 4; } while(--td); } r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); - out_sample = _mm_cvtss_f32(r4); + const auto output = _mm_cvtss_f32(r4); frac += increment; - src += frac>>MixerFracBits; + pos += frac>>MixerFracBits; frac &= MixerFracMask; - } + return output; + }); } template<> -void MixHrtf_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const MixHrtfFilter *hrtfparams, const size_t BufferSize) -{ MixHrtfBase(InSamples, AccumSamples, IrSize, hrtfparams, BufferSize); } +void MixHrtf_(const al::span InSamples, const al::span AccumSamples, + const uint IrSize, const MixHrtfFilter *hrtfparams, const size_t SamplesToDo) +{ MixHrtfBase(InSamples, AccumSamples, IrSize, hrtfparams, SamplesToDo); } template<> -void MixHrtfBlend_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const HrtfFilter *oldparams, const MixHrtfFilter *newparams, const size_t BufferSize) +void MixHrtfBlend_(const al::span InSamples, + const al::span AccumSamples, const uint IrSize, const HrtfFilter *oldparams, + const MixHrtfFilter *newparams, const size_t SamplesToDo) { MixHrtfBlendBase(InSamples, AccumSamples, IrSize, oldparams, newparams, - BufferSize); + SamplesToDo); } template<> void MixDirectHrtf_(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut, - const al::span InSamples, float2 *AccumSamples, - float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize) + const al::span InSamples, const al::span AccumSamples, + const al::span TempBuf, const al::span ChanState, + const size_t IrSize, const size_t SamplesToDo) { MixDirectHrtfBase(LeftOut, RightOut, InSamples, AccumSamples, TempBuf, ChanState, - IrSize, BufferSize); + IrSize, SamplesToDo); } template<> void Mix_(const al::span InSamples, const al::span OutBuffer, - float *CurrentGains, const float *TargetGains, const size_t Counter, const size_t OutPos) + const al::span CurrentGains, const al::span TargetGains, + const size_t Counter, const size_t OutPos) { + if((OutPos&3) != 0) UNLIKELY + return Mix_(InSamples, OutBuffer, CurrentGains, TargetGains, Counter, OutPos); + const float delta{(Counter > 0) ? 1.0f / static_cast(Counter) : 0.0f}; - const auto min_len = minz(Counter, InSamples.size()); - const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len; + const auto fade_len = std::min(Counter, InSamples.size()); + const auto realign_len = std::min((fade_len+3_uz) & ~3_uz, InSamples.size()) - fade_len; + auto curgains = CurrentGains.begin(); + auto targetgains = TargetGains.cbegin(); for(FloatBufferLine &output : OutBuffer) - MixLine(InSamples, al::assume_aligned<16>(output.data()+OutPos), *CurrentGains++, - *TargetGains++, delta, min_len, aligned_len, Counter); + MixLine(InSamples, al::span{output}.subspan(OutPos), *curgains++, *targetgains++, delta, + fade_len, realign_len, Counter); } template<> -void Mix_(const al::span InSamples, float *OutBuffer, float &CurrentGain, - const float TargetGain, const size_t Counter) +void Mix_(const al::span InSamples, const al::span OutBuffer, + float &CurrentGain, const float TargetGain, const size_t Counter) { + if((reinterpret_cast(OutBuffer.data())&15) != 0) UNLIKELY + return Mix_(InSamples, OutBuffer, CurrentGain, TargetGain, Counter); + const float delta{(Counter > 0) ? 1.0f / static_cast(Counter) : 0.0f}; - const auto min_len = minz(Counter, InSamples.size()); - const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len; + const auto fade_len = std::min(Counter, InSamples.size()); + const auto realign_len = std::min((fade_len+3_uz) & ~3_uz, InSamples.size()) - fade_len; - MixLine(InSamples, al::assume_aligned<16>(OutBuffer), CurrentGain, TargetGain, delta, min_len, - aligned_len, Counter); + MixLine(InSamples, OutBuffer, CurrentGain, TargetGain, delta, fade_len, realign_len, Counter); } diff --git a/3rdparty/openal/core/mixer/mixer_sse2.cpp b/3rdparty/openal/core/mixer/mixer_sse2.cpp index edaaf7a1a1fe..c79d50cabecf 100644 --- a/3rdparty/openal/core/mixer/mixer_sse2.cpp +++ b/3rdparty/openal/core/mixer/mixer_sse2.cpp @@ -23,19 +23,42 @@ #include #include +#include +#include +#include +#include + #include "alnumeric.h" +#include "alspan.h" +#include "core/cubic_defs.h" +#include "core/resampler_limits.h" #include "defs.h" +#include "opthelpers.h" struct SSE2Tag; struct LerpTag; +struct CubicTag; #if defined(__GNUC__) && !defined(__clang__) && !defined(__SSE2__) #pragma GCC target("sse2") #endif +using uint = unsigned int; + +namespace { + +constexpr uint CubicPhaseDiffBits{MixerFracBits - CubicPhaseBits}; +constexpr uint CubicPhaseDiffOne{1 << CubicPhaseDiffBits}; +constexpr uint CubicPhaseDiffMask{CubicPhaseDiffOne - 1u}; + +force_inline __m128 vmadd(const __m128 x, const __m128 y, const __m128 z) noexcept +{ return _mm_add_ps(x, _mm_mul_ps(y, z)); } + +} // namespace + template<> -void Resample_(const InterpState*, const float *RESTRICT src, uint frac, +void Resample_(const InterpState*, const al::span src, uint frac, const uint increment, const al::span dst) { ASSUME(frac < MixerFracOne); @@ -44,47 +67,148 @@ void Resample_(const InterpState*, const float *RESTRICT src, u const __m128 fracOne4{_mm_set1_ps(1.0f/MixerFracOne)}; const __m128i fracMask4{_mm_set1_epi32(MixerFracMask)}; - alignas(16) uint pos_[4], frac_[4]; - InitPosArrays(frac, increment, frac_, pos_); + std::array pos_{}, frac_{}; + InitPosArrays(MaxResamplerEdge, frac, increment, al::span{frac_}, al::span{pos_}); __m128i frac4{_mm_setr_epi32(static_cast(frac_[0]), static_cast(frac_[1]), static_cast(frac_[2]), static_cast(frac_[3]))}; __m128i pos4{_mm_setr_epi32(static_cast(pos_[0]), static_cast(pos_[1]), static_cast(pos_[2]), static_cast(pos_[3]))}; - auto dst_iter = dst.begin(); - for(size_t todo{dst.size()>>2};todo;--todo) + auto vecout = al::span{reinterpret_cast<__m128*>(dst.data()), dst.size()/4}; + std::generate(vecout.begin(), vecout.end(), [=,&pos4,&frac4]() -> __m128 { - const int pos0{_mm_cvtsi128_si32(pos4)}; - const int pos1{_mm_cvtsi128_si32(_mm_srli_si128(pos4, 4))}; - const int pos2{_mm_cvtsi128_si32(_mm_srli_si128(pos4, 8))}; - const int pos3{_mm_cvtsi128_si32(_mm_srli_si128(pos4, 12))}; - const __m128 val1{_mm_setr_ps(src[pos0 ], src[pos1 ], src[pos2 ], src[pos3 ])}; - const __m128 val2{_mm_setr_ps(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1])}; + const auto pos0 = static_cast(_mm_cvtsi128_si32(pos4)); + const auto pos1 = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(pos4, 4))); + const auto pos2 = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(pos4, 8))); + const auto pos3 = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(pos4, 12))); + ASSUME(pos0 <= pos1); ASSUME(pos1 <= pos2); ASSUME(pos2 <= pos3); + const __m128 val1{_mm_setr_ps(src[pos0], src[pos1], src[pos2], src[pos3])}; + const __m128 val2{_mm_setr_ps(src[pos0+1_uz], src[pos1+1_uz], src[pos2+1_uz], src[pos3+1_uz])}; /* val1 + (val2-val1)*mu */ const __m128 r0{_mm_sub_ps(val2, val1)}; const __m128 mu{_mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4)}; const __m128 out{_mm_add_ps(val1, _mm_mul_ps(mu, r0))}; - _mm_store_ps(dst_iter, out); - dst_iter += 4; - frac4 = _mm_add_epi32(frac4, increment4); pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, MixerFracBits)); frac4 = _mm_and_si128(frac4, fracMask4); - } + return out; + }); if(size_t todo{dst.size()&3}) { - src += static_cast(_mm_cvtsi128_si32(pos4)); + auto pos = size_t{static_cast(_mm_cvtsi128_si32(pos4))}; + frac = static_cast(_mm_cvtsi128_si32(frac4)); + + const auto out = dst.last(todo); + std::generate(out.begin(), out.end(), [&pos,&frac,src,increment]() + { + const float smp{lerpf(src[pos+0], src[pos+1], + static_cast(frac) * (1.0f/MixerFracOne))}; + + frac += increment; + pos += frac>>MixerFracBits; + frac &= MixerFracMask; + return smp; + }); + } +} + +template<> +void Resample_(const InterpState *state, const al::span src, + uint frac, const uint increment, const al::span dst) +{ + ASSUME(frac < MixerFracOne); + + const auto filter = std::get(*state).filter; + + const __m128i increment4{_mm_set1_epi32(static_cast(increment*4))}; + const __m128i fracMask4{_mm_set1_epi32(MixerFracMask)}; + const __m128 fracDiffOne4{_mm_set1_ps(1.0f/CubicPhaseDiffOne)}; + const __m128i fracDiffMask4{_mm_set1_epi32(CubicPhaseDiffMask)}; + + std::array pos_{}, frac_{}; + InitPosArrays(MaxResamplerEdge-1, frac, increment, al::span{frac_}, al::span{pos_}); + __m128i frac4{_mm_setr_epi32(static_cast(frac_[0]), static_cast(frac_[1]), + static_cast(frac_[2]), static_cast(frac_[3]))}; + __m128i pos4{_mm_setr_epi32(static_cast(pos_[0]), static_cast(pos_[1]), + static_cast(pos_[2]), static_cast(pos_[3]))}; + + auto vecout = al::span{reinterpret_cast<__m128*>(dst.data()), dst.size()/4}; + std::generate(vecout.begin(), vecout.end(), [=,&pos4,&frac4] + { + const auto pos0 = static_cast(_mm_cvtsi128_si32(pos4)); + const auto pos1 = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(pos4, 4))); + const auto pos2 = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(pos4, 8))); + const auto pos3 = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(pos4, 12))); + ASSUME(pos0 <= pos1); ASSUME(pos1 <= pos2); ASSUME(pos2 <= pos3); + const __m128 val0{_mm_loadu_ps(&src[pos0])}; + const __m128 val1{_mm_loadu_ps(&src[pos1])}; + const __m128 val2{_mm_loadu_ps(&src[pos2])}; + const __m128 val3{_mm_loadu_ps(&src[pos3])}; + + const __m128i pi4{_mm_srli_epi32(frac4, CubicPhaseDiffBits)}; + const auto pi0 = static_cast(_mm_cvtsi128_si32(pi4)); + const auto pi1 = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(pi4, 4))); + const auto pi2 = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(pi4, 8))); + const auto pi3 = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(pi4, 12))); + ASSUME(pi0 < CubicPhaseCount); ASSUME(pi1 < CubicPhaseCount); + ASSUME(pi2 < CubicPhaseCount); ASSUME(pi3 < CubicPhaseCount); + + const __m128 pf4{_mm_mul_ps(_mm_cvtepi32_ps(_mm_and_si128(frac4, fracDiffMask4)), + fracDiffOne4)}; + + __m128 r0{_mm_mul_ps(val0, + vmadd(_mm_load_ps(filter[pi0].mCoeffs.data()), + _mm_shuffle_ps(pf4, pf4, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_load_ps(filter[pi0].mDeltas.data())))}; + __m128 r1{_mm_mul_ps(val1, + vmadd(_mm_load_ps(filter[pi1].mCoeffs.data()), + _mm_shuffle_ps(pf4, pf4, _MM_SHUFFLE(1, 1, 1, 1)), + _mm_load_ps(filter[pi1].mDeltas.data())))}; + __m128 r2{_mm_mul_ps(val2, + vmadd(_mm_load_ps(filter[pi2].mCoeffs.data()), + _mm_shuffle_ps(pf4, pf4, _MM_SHUFFLE(2, 2, 2, 2)), + _mm_load_ps(filter[pi2].mDeltas.data())))}; + __m128 r3{_mm_mul_ps(val3, + vmadd(_mm_load_ps(filter[pi3].mCoeffs.data()), + _mm_shuffle_ps(pf4, pf4, _MM_SHUFFLE(3, 3, 3, 3)), + _mm_load_ps(filter[pi3].mDeltas.data())))}; + + _MM_TRANSPOSE4_PS(r0, r1, r2, r3); + r0 = _mm_add_ps(_mm_add_ps(r0, r1), _mm_add_ps(r2, r3)); + + frac4 = _mm_add_epi32(frac4, increment4); + pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, MixerFracBits)); + frac4 = _mm_and_si128(frac4, fracMask4); + return r0; + }); + + if(const size_t todo{dst.size()&3}) + { + auto pos = size_t{static_cast(_mm_cvtsi128_si32(pos4))}; frac = static_cast(_mm_cvtsi128_si32(frac4)); - do { - *(dst_iter++) = lerpf(src[0], src[1], static_cast(frac) * (1.0f/MixerFracOne)); + auto out = dst.last(todo); + std::generate(out.begin(), out.end(), [&pos,&frac,src,increment,filter] + { + const uint pi{frac >> CubicPhaseDiffBits}; ASSUME(pi < CubicPhaseCount); + const float pf{static_cast(frac&CubicPhaseDiffMask) * (1.0f/CubicPhaseDiffOne)}; + const __m128 pf4{_mm_set1_ps(pf)}; + + const __m128 f4 = vmadd(_mm_load_ps(filter[pi].mCoeffs.data()), pf4, + _mm_load_ps(filter[pi].mDeltas.data())); + __m128 r4{_mm_mul_ps(f4, _mm_loadu_ps(&src[pos]))}; + + r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); + r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); + const float output{_mm_cvtss_f32(r4)}; frac += increment; - src += frac>>MixerFracBits; + pos += frac>>MixerFracBits; frac &= MixerFracMask; - } while(--todo); + return output; + }); } } diff --git a/3rdparty/openal/core/mixer/mixer_sse41.cpp b/3rdparty/openal/core/mixer/mixer_sse41.cpp index 8ccd9fd3a862..345330454413 100644 --- a/3rdparty/openal/core/mixer/mixer_sse41.cpp +++ b/3rdparty/openal/core/mixer/mixer_sse41.cpp @@ -24,19 +24,42 @@ #include #include +#include +#include +#include +#include + #include "alnumeric.h" +#include "alspan.h" +#include "core/cubic_defs.h" +#include "core/resampler_limits.h" #include "defs.h" +#include "opthelpers.h" struct SSE4Tag; struct LerpTag; +struct CubicTag; #if defined(__GNUC__) && !defined(__clang__) && !defined(__SSE4_1__) #pragma GCC target("sse4.1") #endif +using uint = unsigned int; + +namespace { + +constexpr uint CubicPhaseDiffBits{MixerFracBits - CubicPhaseBits}; +constexpr uint CubicPhaseDiffOne{1 << CubicPhaseDiffBits}; +constexpr uint CubicPhaseDiffMask{CubicPhaseDiffOne - 1u}; + +force_inline __m128 vmadd(const __m128 x, const __m128 y, const __m128 z) noexcept +{ return _mm_add_ps(x, _mm_mul_ps(y, z)); } + +} // namespace + template<> -void Resample_(const InterpState*, const float *RESTRICT src, uint frac, +void Resample_(const InterpState*, const al::span src, uint frac, const uint increment, const al::span dst) { ASSUME(frac < MixerFracOne); @@ -45,35 +68,34 @@ void Resample_(const InterpState*, const float *RESTRICT src, u const __m128 fracOne4{_mm_set1_ps(1.0f/MixerFracOne)}; const __m128i fracMask4{_mm_set1_epi32(MixerFracMask)}; - alignas(16) uint pos_[4], frac_[4]; - InitPosArrays(frac, increment, frac_, pos_); + std::array pos_{}, frac_{}; + InitPosArrays(MaxResamplerEdge, frac, increment, al::span{frac_}, al::span{pos_}); __m128i frac4{_mm_setr_epi32(static_cast(frac_[0]), static_cast(frac_[1]), static_cast(frac_[2]), static_cast(frac_[3]))}; __m128i pos4{_mm_setr_epi32(static_cast(pos_[0]), static_cast(pos_[1]), static_cast(pos_[2]), static_cast(pos_[3]))}; - auto dst_iter = dst.begin(); - for(size_t todo{dst.size()>>2};todo;--todo) + auto vecout = al::span{reinterpret_cast<__m128*>(dst.data()), dst.size()/4}; + std::generate(vecout.begin(), vecout.end(), [=,&pos4,&frac4] { - const int pos0{_mm_extract_epi32(pos4, 0)}; - const int pos1{_mm_extract_epi32(pos4, 1)}; - const int pos2{_mm_extract_epi32(pos4, 2)}; - const int pos3{_mm_extract_epi32(pos4, 3)}; - const __m128 val1{_mm_setr_ps(src[pos0 ], src[pos1 ], src[pos2 ], src[pos3 ])}; - const __m128 val2{_mm_setr_ps(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1])}; + const auto pos0 = static_cast(_mm_extract_epi32(pos4, 0)); + const auto pos1 = static_cast(_mm_extract_epi32(pos4, 1)); + const auto pos2 = static_cast(_mm_extract_epi32(pos4, 2)); + const auto pos3 = static_cast(_mm_extract_epi32(pos4, 3)); + ASSUME(pos0 <= pos1); ASSUME(pos1 <= pos2); ASSUME(pos2 <= pos3); + const __m128 val1{_mm_setr_ps(src[pos0], src[pos1], src[pos2], src[pos3])}; + const __m128 val2{_mm_setr_ps(src[pos0+1_uz], src[pos1+1_uz], src[pos2+1_uz], src[pos3+1_uz])}; /* val1 + (val2-val1)*mu */ const __m128 r0{_mm_sub_ps(val2, val1)}; const __m128 mu{_mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4)}; const __m128 out{_mm_add_ps(val1, _mm_mul_ps(mu, r0))}; - _mm_store_ps(dst_iter, out); - dst_iter += 4; - frac4 = _mm_add_epi32(frac4, increment4); pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, MixerFracBits)); frac4 = _mm_and_si128(frac4, fracMask4); - } + return out; + }); if(size_t todo{dst.size()&3}) { @@ -81,15 +103,117 @@ void Resample_(const InterpState*, const float *RESTRICT src, u * four samples, so the lowest element is the next position to * resample. */ - src += static_cast(_mm_cvtsi128_si32(pos4)); + auto pos = size_t{static_cast(_mm_cvtsi128_si32(pos4))}; + frac = static_cast(_mm_cvtsi128_si32(frac4)); + + auto out = dst.last(todo); + std::generate(out.begin(), out.end(), [&pos,&frac,src,increment] + { + const float smp{lerpf(src[pos+0], src[pos+1], + static_cast(frac) * (1.0f/MixerFracOne))}; + + frac += increment; + pos += frac>>MixerFracBits; + frac &= MixerFracMask; + return smp; + }); + } +} + +template<> +void Resample_(const InterpState *state, const al::span src, + uint frac, const uint increment, const al::span dst) +{ + ASSUME(frac < MixerFracOne); + + const auto filter = std::get(*state).filter; + + const __m128i increment4{_mm_set1_epi32(static_cast(increment*4))}; + const __m128i fracMask4{_mm_set1_epi32(MixerFracMask)}; + const __m128 fracDiffOne4{_mm_set1_ps(1.0f/CubicPhaseDiffOne)}; + const __m128i fracDiffMask4{_mm_set1_epi32(CubicPhaseDiffMask)}; + + std::array pos_{}, frac_{}; + InitPosArrays(MaxResamplerEdge-1, frac, increment, al::span{frac_}, al::span{pos_}); + __m128i frac4{_mm_setr_epi32(static_cast(frac_[0]), static_cast(frac_[1]), + static_cast(frac_[2]), static_cast(frac_[3]))}; + __m128i pos4{_mm_setr_epi32(static_cast(pos_[0]), static_cast(pos_[1]), + static_cast(pos_[2]), static_cast(pos_[3]))}; + + auto vecout = al::span{reinterpret_cast<__m128*>(dst.data()), dst.size()/4}; + std::generate(vecout.begin(), vecout.end(), [=,&pos4,&frac4] + { + const auto pos0 = static_cast(_mm_extract_epi32(pos4, 0)); + const auto pos1 = static_cast(_mm_extract_epi32(pos4, 1)); + const auto pos2 = static_cast(_mm_extract_epi32(pos4, 2)); + const auto pos3 = static_cast(_mm_extract_epi32(pos4, 3)); + ASSUME(pos0 <= pos1); ASSUME(pos1 <= pos2); ASSUME(pos2 <= pos3); + const __m128 val0{_mm_loadu_ps(&src[pos0])}; + const __m128 val1{_mm_loadu_ps(&src[pos1])}; + const __m128 val2{_mm_loadu_ps(&src[pos2])}; + const __m128 val3{_mm_loadu_ps(&src[pos3])}; + + const __m128i pi4{_mm_srli_epi32(frac4, CubicPhaseDiffBits)}; + const auto pi0 = static_cast(_mm_extract_epi32(pi4, 0)); + const auto pi1 = static_cast(_mm_extract_epi32(pi4, 1)); + const auto pi2 = static_cast(_mm_extract_epi32(pi4, 2)); + const auto pi3 = static_cast(_mm_extract_epi32(pi4, 3)); + ASSUME(pi0 < CubicPhaseCount); ASSUME(pi1 < CubicPhaseCount); + ASSUME(pi2 < CubicPhaseCount); ASSUME(pi3 < CubicPhaseCount); + + const __m128 pf4{_mm_mul_ps(_mm_cvtepi32_ps(_mm_and_si128(frac4, fracDiffMask4)), + fracDiffOne4)}; + + __m128 r0{_mm_mul_ps(val0, + vmadd(_mm_load_ps(filter[pi0].mCoeffs.data()), + _mm_shuffle_ps(pf4, pf4, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_load_ps(filter[pi0].mDeltas.data())))}; + __m128 r1{_mm_mul_ps(val1, + vmadd(_mm_load_ps(filter[pi1].mCoeffs.data()), + _mm_shuffle_ps(pf4, pf4, _MM_SHUFFLE(1, 1, 1, 1)), + _mm_load_ps(filter[pi1].mDeltas.data())))}; + __m128 r2{_mm_mul_ps(val2, + vmadd(_mm_load_ps(filter[pi2].mCoeffs.data()), + _mm_shuffle_ps(pf4, pf4, _MM_SHUFFLE(2, 2, 2, 2)), + _mm_load_ps(filter[pi2].mDeltas.data())))}; + __m128 r3{_mm_mul_ps(val3, + vmadd(_mm_load_ps(filter[pi3].mCoeffs.data()), + _mm_shuffle_ps(pf4, pf4, _MM_SHUFFLE(3, 3, 3, 3)), + _mm_load_ps(filter[pi3].mDeltas.data())))}; + + _MM_TRANSPOSE4_PS(r0, r1, r2, r3); + r0 = _mm_add_ps(_mm_add_ps(r0, r1), _mm_add_ps(r2, r3)); + + frac4 = _mm_add_epi32(frac4, increment4); + pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, MixerFracBits)); + frac4 = _mm_and_si128(frac4, fracMask4); + return r0; + }); + + if(const size_t todo{dst.size()&3}) + { + auto pos = size_t{static_cast(_mm_cvtsi128_si32(pos4))}; frac = static_cast(_mm_cvtsi128_si32(frac4)); - do { - *(dst_iter++) = lerpf(src[0], src[1], static_cast(frac) * (1.0f/MixerFracOne)); + auto out = dst.last(todo); + std::generate(out.begin(), out.end(), [&pos,&frac,src,increment,filter] + { + const uint pi{frac >> CubicPhaseDiffBits}; ASSUME(pi < CubicPhaseCount); + const float pf{static_cast(frac&CubicPhaseDiffMask) * (1.0f/CubicPhaseDiffOne)}; + const __m128 pf4{_mm_set1_ps(pf)}; + + const __m128 f4 = vmadd(_mm_load_ps(filter[pi].mCoeffs.data()), pf4, + _mm_load_ps(filter[pi].mDeltas.data())); + __m128 r4{_mm_mul_ps(f4, _mm_loadu_ps(&src[pos]))}; + + r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); + r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); + const float output{_mm_cvtss_f32(r4)}; frac += increment; - src += frac>>MixerFracBits; + pos += frac>>MixerFracBits; frac &= MixerFracMask; - } while(--todo); + return output; + }); } } diff --git a/3rdparty/openal/core/resampler_limits.h b/3rdparty/openal/core/resampler_limits.h index 9d4cefdae251..a32807e8e0e1 100644 --- a/3rdparty/openal/core/resampler_limits.h +++ b/3rdparty/openal/core/resampler_limits.h @@ -5,8 +5,8 @@ * Note that the padding is symmetric (half at the beginning and half at the * end)! */ -constexpr int MaxResamplerPadding{48}; +constexpr unsigned int MaxResamplerPadding{48}; -constexpr int MaxResamplerEdge{MaxResamplerPadding >> 1}; +constexpr unsigned int MaxResamplerEdge{MaxResamplerPadding >> 1}; #endif /* CORE_RESAMPLER_LIMITS_H */ diff --git a/3rdparty/openal/core/rtkit.cpp b/3rdparty/openal/core/rtkit.cpp index ff944ebf2b4f..70b5e12bbe0f 100644 --- a/3rdparty/openal/core/rtkit.cpp +++ b/3rdparty/openal/core/rtkit.cpp @@ -30,14 +30,14 @@ #include "rtkit.h" -#include +#include #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include -#include +#include #include #include #ifdef __linux__ @@ -153,29 +153,29 @@ int rtkit_get_int_property(DBusConnection *connection, const char *propname, lon } // namespace -int rtkit_get_max_realtime_priority(DBusConnection *connection) +int rtkit_get_max_realtime_priority(DBusConnection *system_bus) { long long retval{}; - int err{rtkit_get_int_property(connection, "MaxRealtimePriority", &retval)}; + int err{rtkit_get_int_property(system_bus, "MaxRealtimePriority", &retval)}; return err < 0 ? err : static_cast(retval); } -int rtkit_get_min_nice_level(DBusConnection *connection, int *min_nice_level) +int rtkit_get_min_nice_level(DBusConnection *system_bus, int *min_nice_level) { long long retval{}; - int err{rtkit_get_int_property(connection, "MinNiceLevel", &retval)}; + int err{rtkit_get_int_property(system_bus, "MinNiceLevel", &retval)}; if(err >= 0) *min_nice_level = static_cast(retval); return err; } -long long rtkit_get_rttime_usec_max(DBusConnection *connection) +long long rtkit_get_rttime_usec_max(DBusConnection *system_bus) { long long retval{}; - int err{rtkit_get_int_property(connection, "RTTimeUSecMax", &retval)}; + int err{rtkit_get_int_property(system_bus, "RTTimeUSecMax", &retval)}; return err < 0 ? err : retval; } -int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) +int rtkit_make_realtime(DBusConnection *system_bus, pid_t thread, int priority) { if(thread == 0) thread = _gettid(); @@ -195,7 +195,7 @@ int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) if(!ready) return -ENOMEM; dbus::Error error; - dbus::MessagePtr r{dbus_connection_send_with_reply_and_block(connection, m.get(), -1, + dbus::MessagePtr r{dbus_connection_send_with_reply_and_block(system_bus, m.get(), -1, &error.get())}; if(!r) return translate_error(error->name); @@ -205,7 +205,7 @@ int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) return 0; } -int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level) +int rtkit_make_high_priority(DBusConnection *system_bus, pid_t thread, int nice_level) { if(thread == 0) thread = _gettid(); @@ -225,7 +225,7 @@ int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_ if(!ready) return -ENOMEM; dbus::Error error; - dbus::MessagePtr r{dbus_connection_send_with_reply_and_block(connection, m.get(), -1, + dbus::MessagePtr r{dbus_connection_send_with_reply_and_block(system_bus, m.get(), -1, &error.get())}; if(!r) return translate_error(error->name); diff --git a/3rdparty/openal/core/storage_formats.cpp b/3rdparty/openal/core/storage_formats.cpp new file mode 100644 index 000000000000..51f64644c554 --- /dev/null +++ b/3rdparty/openal/core/storage_formats.cpp @@ -0,0 +1,85 @@ + +#include "config.h" + +#include "storage_formats.h" + +#include + + +const char *NameFromFormat(FmtType type) noexcept +{ + switch(type) + { + case FmtUByte: return "UInt8"; + case FmtShort: return "Int16"; + case FmtInt: return "Int32"; + case FmtFloat: return "Float"; + case FmtDouble: return "Double"; + case FmtMulaw: return "muLaw"; + case FmtAlaw: return "aLaw"; + case FmtIMA4: return "IMA4 ADPCM"; + case FmtMSADPCM: return "MS ADPCM"; + } + return ""; +} + +const char *NameFromFormat(FmtChannels channels) noexcept +{ + switch(channels) + { + case FmtMono: return "Mono"; + case FmtStereo: return "Stereo"; + case FmtRear: return "Rear"; + case FmtQuad: return "Quadraphonic"; + case FmtX51: return "Surround 5.1"; + case FmtX61: return "Surround 6.1"; + case FmtX71: return "Surround 7.1"; + case FmtBFormat2D: return "B-Format 2D"; + case FmtBFormat3D: return "B-Format 3D"; + case FmtUHJ2: return "UHJ2"; + case FmtUHJ3: return "UHJ3"; + case FmtUHJ4: return "UHJ4"; + case FmtSuperStereo: return "Super Stereo"; + case FmtMonoDup: return "Mono (dup)"; + } + return ""; +} + +uint BytesFromFmt(FmtType type) noexcept +{ + switch(type) + { + case FmtUByte: return sizeof(std::uint8_t); + case FmtShort: return sizeof(std::int16_t); + case FmtInt: return sizeof(std::int32_t); + case FmtFloat: return sizeof(float); + case FmtDouble: return sizeof(double); + case FmtMulaw: return sizeof(std::uint8_t); + case FmtAlaw: return sizeof(std::uint8_t); + case FmtIMA4: break; + case FmtMSADPCM: break; + } + return 0; +} + +uint ChannelsFromFmt(FmtChannels chans, uint ambiorder) noexcept +{ + switch(chans) + { + case FmtMono: return 1; + case FmtStereo: return 2; + case FmtRear: return 2; + case FmtQuad: return 4; + case FmtX51: return 6; + case FmtX61: return 7; + case FmtX71: return 8; + case FmtBFormat2D: return (ambiorder*2) + 1; + case FmtBFormat3D: return (ambiorder+1) * (ambiorder+1); + case FmtUHJ2: return 2; + case FmtUHJ3: return 3; + case FmtUHJ4: return 4; + case FmtSuperStereo: return 2; + case FmtMonoDup: return 1; + } + return 0; +} diff --git a/3rdparty/openal/core/storage_formats.h b/3rdparty/openal/core/storage_formats.h new file mode 100644 index 000000000000..acced258a59f --- /dev/null +++ b/3rdparty/openal/core/storage_formats.h @@ -0,0 +1,54 @@ +#ifndef CORE_STORAGE_FORMATS_H +#define CORE_STORAGE_FORMATS_H + +using uint = unsigned int; + +/* Storable formats */ +enum FmtType : unsigned char { + FmtUByte, + FmtShort, + FmtInt, + FmtFloat, + FmtDouble, + FmtMulaw, + FmtAlaw, + FmtIMA4, + FmtMSADPCM, +}; +enum FmtChannels : unsigned char { + FmtMono, + FmtStereo, + FmtRear, + FmtQuad, + FmtX51, /* (WFX order) */ + FmtX61, /* (WFX order) */ + FmtX71, /* (WFX order) */ + FmtBFormat2D, + FmtBFormat3D, + FmtUHJ2, /* 2-channel UHJ, aka "BHJ", stereo-compatible */ + FmtUHJ3, /* 3-channel UHJ, aka "THJ" */ + FmtUHJ4, /* 4-channel UHJ, aka "PHJ" */ + FmtSuperStereo, /* Stereo processed with Super Stereo. */ + FmtMonoDup, /* Mono duplicated for left/right separation */ +}; + +enum class AmbiLayout : unsigned char { + FuMa, + ACN, +}; +enum class AmbiScaling : unsigned char { + FuMa, + SN3D, + N3D, + UHJ, +}; + +const char *NameFromFormat(FmtType type) noexcept; +const char *NameFromFormat(FmtChannels channels) noexcept; + +uint BytesFromFmt(FmtType type) noexcept; +uint ChannelsFromFmt(FmtChannels chans, uint ambiorder) noexcept; +inline uint FrameSizeFromFmt(FmtChannels chans, FmtType type, uint ambiorder) noexcept +{ return ChannelsFromFmt(chans, ambiorder) * BytesFromFmt(type); } + +#endif /* CORE_STORAGE_FORMATS_H */ diff --git a/3rdparty/openal/core/uhjfilter.cpp b/3rdparty/openal/core/uhjfilter.cpp index 4e4e99a54800..868f63920e2e 100644 --- a/3rdparty/openal/core/uhjfilter.cpp +++ b/3rdparty/openal/core/uhjfilter.cpp @@ -4,29 +4,117 @@ #include "uhjfilter.h" #include -#include +#include +#include +#include +#include #include "alcomplex.h" -#include "alnumeric.h" +#include "almalloc.h" +#include "alnumbers.h" +#include "core/bufferline.h" #include "opthelpers.h" +#include "pffft.h" #include "phase_shifter.h" +#include "vector.h" -UhjQualityType UhjDecodeQuality{UhjQualityType::Default}; -UhjQualityType UhjEncodeQuality{UhjQualityType::Default}; +namespace { +template +constexpr auto assume_aligned_span(const al::span s) noexcept -> al::span +{ return al::span{al::assume_aligned(s.data()), s.size()}; } -namespace { +/* 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 +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); + + PFFFTSetup mFft; + alignas(16) std::array mFilterData; + + SegmentedFilter() : mFft{sFftLength, PFFFT_REAL} + { + static constexpr size_t fft_size{N}; + + /* To set up the filter, we first need to generate the desired + * response (not reversed). + */ + auto tmpBuffer = std::vector(fft_size, 0.0); + for(std::size_t i{0};i < fft_size/2;++i) + { + const int k{int{fft_size/2} - static_cast(i*2 + 1)}; + + const double w{2.0*al::numbers::pi * static_cast(i*2 + 1) + / double{fft_size}}; + const double window{0.3635819 - 0.4891775*std::cos(w) + 0.1365995*std::cos(2.0*w) + - 0.0106411*std::cos(3.0*w)}; + + const double pk{al::numbers::pi * static_cast(k)}; + tmpBuffer[i*2 + 1] = window * (1.0-std::cos(pk)) / pk; + } + + /* The segments of the filter are converted back to the frequency + * domain, each on their own (0 stuffed). + */ + using complex_d = std::complex; + auto fftBuffer = std::vector(sFftLength); + auto fftTmp = al::vector(sFftLength); + auto filter = mFilterData.begin(); + for(size_t s{0};s < sNumSegments;++s) + { + const auto tmpspan = al::span{tmpBuffer}.subspan(sSampleLength*s, sSampleLength); + auto iter = std::copy_n(tmpspan.cbegin(), tmpspan.size(), fftBuffer.begin()); + std::fill(iter, fftBuffer.end(), complex_d{}); + forward_fft(fftBuffer); + + /* 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(fftBuffer[i].real()) / float{sFftLength}; + fftTmp[i*2 + 1] = static_cast((i == 0) ? fftBuffer[sSampleLength].real() + : fftBuffer[i].imag()) / float{sFftLength}; + } + mFft.zreorder(fftTmp.data(), al::to_address(filter), PFFFT_BACKWARD); + filter += sFftLength; + } + } +}; -const PhaseShifterT PShiftLq{}; -const PhaseShifterT PShiftHq{}; +template +const SegmentedFilter gSegmentedFilter; template -struct GetPhaseShifter; -template<> -struct GetPhaseShifter { static auto& Get() noexcept { return PShiftLq; } }; -template<> -struct GetPhaseShifter { static auto& Get() noexcept { return PShiftHq; } }; +const PhaseShifterT PShifter; /* Filter coefficients for the 'base' all-pass IIR, which applies a frequency- @@ -45,8 +133,21 @@ constexpr std::array Filter2Coeff{{ } // namespace +void UhjAllPassFilter::processOne(const al::span 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 coeffs, - const al::span src, const bool updateState, float *RESTRICT dst) + const al::span src, const bool updateState, const al::span dst) { auto state = mState; @@ -61,7 +162,7 @@ void UhjAllPassFilter::process(const al::span coeffs, } return x; }; - std::transform(src.begin(), src.end(), dst, proc_sample); + std::transform(src.begin(), src.end(), dst.begin(), proc_sample); if(updateState) LIKELY mState = state; } @@ -87,68 +188,138 @@ template void UhjEncoder::encode(float *LeftOut, float *RightOut, const al::span InSamples, const size_t SamplesToDo) { - const auto &PShift = GetPhaseShifter::Get(); + static constexpr auto &Filter = gSegmentedFilter; + static_assert(sFftLength == Filter.sFftLength); + static_assert(sSegmentSize == Filter.sSampleLength); + static_assert(sNumSegments == Filter.sNumSegments); ASSUME(SamplesToDo > 0); + ASSUME(SamplesToDo <= BufferLineSize); - const float *RESTRICT winput{al::assume_aligned<16>(InSamples[0])}; - const float *RESTRICT xinput{al::assume_aligned<16>(InSamples[1])}; - const float *RESTRICT yinput{al::assume_aligned<16>(InSamples[2])}; + const auto winput = al::span{al::assume_aligned<16>(InSamples[0]), SamplesToDo}; + const auto xinput = al::span{al::assume_aligned<16>(InSamples[1]), SamplesToDo}; + const auto yinput = al::span{al::assume_aligned<16>(InSamples[2]), SamplesToDo}; - std::copy_n(winput, SamplesToDo, mW.begin()+sFilterDelay); - std::copy_n(xinput, SamplesToDo, mX.begin()+sFilterDelay); - std::copy_n(yinput, SamplesToDo, mY.begin()+sFilterDelay); + std::copy_n(winput.begin(), SamplesToDo, mW.begin()+sFilterDelay); + std::copy_n(xinput.begin(), SamplesToDo, mX.begin()+sFilterDelay); + std::copy_n(yinput.begin(), SamplesToDo, mY.begin()+sFilterDelay); /* S = 0.9396926*W + 0.1855740*X */ - for(size_t i{0};i < SamplesToDo;++i) - mS[i] = 0.9396926f*mW[i] + 0.1855740f*mX[i]; + std::transform(mW.begin(), mW.begin()+SamplesToDo, mX.begin(), mS.begin(), + [](const float w, const float x) noexcept { return 0.9396926f*w + 0.1855740f*x; }); /* 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()); + auto dstore = mD.begin(); + size_t curseg{mCurrentSegment}; + for(size_t base{0};base < SamplesToDo;) + { + const size_t todo{std::min(sSegmentSize-mFifoPos, SamplesToDo-base)}; + auto wseg = winput.subspan(base, todo); + auto xseg = xinput.subspan(base, todo); + auto wxio = al::span{mWXInOut}.subspan(mFifoPos, todo); + + /* Copy out the samples that were previously processed by the FFT. */ + dstore = std::copy_n(wxio.begin(), todo, dstore); + + /* Transform the non-delayed input and store in the front half of the + * filter input. + */ + std::transform(wseg.begin(), wseg.end(), xseg.begin(), wxio.begin(), + [](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. + */ + auto input = mWXHistory.begin() + curseg*sFftLength; + std::copy_n(mWXInOut.begin(), sSegmentSize, input); + std::fill_n(input+sSegmentSize, sSegmentSize, 0.0f); + + Filter.mFft.transform(al::to_address(input), al::to_address(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); + auto filter = Filter.mFilterData.begin(); + for(size_t s{curseg};s < sNumSegments;++s) + { + Filter.mFft.zconvolve_accumulate(al::to_address(input), al::to_address(filter), + mFftBuffer.data()); + input += sFftLength; + filter += sFftLength; + } + input = mWXHistory.begin(); + for(size_t s{0};s < curseg;++s) + { + Filter.mFft.zconvolve_accumulate(al::to_address(input), al::to_address(filter), + mFftBuffer.data()); + input += sFftLength; + filter += sFftLength; + } + + /* Convert back to samples, writing to the output and storing the extra + * for next time. + */ + Filter.mFft.transform(mFftBuffer.data(), mFftBuffer.data(), mWorkData.data(), + PFFFT_BACKWARD); + + std::transform(mFftBuffer.begin(), mFftBuffer.begin()+sSegmentSize, + mWXInOut.begin()+sSegmentSize, mWXInOut.begin(), std::plus{}); + std::copy_n(mFftBuffer.begin()+sSegmentSize, sSegmentSize, mWXInOut.begin()+sSegmentSize); + + /* 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) - mD[i] = mD[i] + 0.6554516f*mY[i]; + std::transform(mD.begin(), mD.begin()+SamplesToDo, mY.begin(), mD.begin(), + [](const float jwx, const float y) noexcept { return jwx + 0.6554516f*y; }); /* Copy the future samples to the front for next time. */ 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(); + auto delayBuffer = mDirectDelay.begin(); for(float *buffer : {LeftOut, RightOut}) { - float *distbuf{al::assume_aligned<16>(delayBuffer->data())}; + const auto distbuf = assume_aligned_span<16>(al::span{*delayBuffer}); ++delayBuffer; - float *inout{al::assume_aligned<16>(buffer)}; - auto inout_end = inout + SamplesToDo; - if(SamplesToDo >= sFilterDelay) LIKELY + const auto inout = al::span{al::assume_aligned<16>(buffer), SamplesToDo}; + if(SamplesToDo >= sFilterDelay) { - auto delay_end = std::rotate(inout, inout_end - sFilterDelay, inout_end); - std::swap_ranges(inout, delay_end, distbuf); + auto delay_end = std::rotate(inout.begin(), inout.end() - sFilterDelay, inout.end()); + std::swap_ranges(inout.begin(), delay_end, distbuf.begin()); } else { - auto delay_start = std::swap_ranges(inout, inout_end, distbuf); - std::rotate(distbuf, delay_start, distbuf + sFilterDelay); + auto delay_start = std::swap_ranges(inout.begin(), inout.end(), distbuf.begin()); + std::rotate(distbuf.begin(), delay_start, distbuf.begin() + sFilterDelay); } } /* Combine the direct signal with the produced output. */ /* Left = (S + D)/2.0 */ - float *RESTRICT left{al::assume_aligned<16>(LeftOut)}; - for(size_t i{0};i < SamplesToDo;i++) + const auto left = al::span{al::assume_aligned<16>(LeftOut), SamplesToDo}; + for(size_t i{0};i < SamplesToDo;++i) left[i] += (mS[i] + mD[i]) * 0.5f; + /* Right = (S - D)/2.0 */ - float *RESTRICT right{al::assume_aligned<16>(RightOut)}; - for(size_t i{0};i < SamplesToDo;i++) + const auto right = al::span{al::assume_aligned<16>(RightOut), SamplesToDo}; + for(size_t i{0};i < SamplesToDo;++i) right[i] += (mS[i] - mD[i]) * 0.5f; } @@ -171,47 +342,49 @@ void UhjEncoderIIR::encode(float *LeftOut, float *RightOut, const al::span InSamples, const size_t SamplesToDo) { ASSUME(SamplesToDo > 0); + ASSUME(SamplesToDo <= BufferLineSize); - const float *RESTRICT winput{al::assume_aligned<16>(InSamples[0])}; - const float *RESTRICT xinput{al::assume_aligned<16>(InSamples[1])}; - const float *RESTRICT yinput{al::assume_aligned<16>(InSamples[2])}; + const auto winput = al::span{al::assume_aligned<16>(InSamples[0]), SamplesToDo}; + const auto xinput = al::span{al::assume_aligned<16>(InSamples[1]), SamplesToDo}; + const auto yinput = al::span{al::assume_aligned<16>(InSamples[2]), SamplesToDo}; /* S = 0.9396926*W + 0.1855740*X */ - std::transform(winput, winput+SamplesToDo, xinput, mTemp.begin(), + std::transform(winput.begin(), winput.end(), xinput.begin(), mTemp.begin(), [](const float w, const float x) noexcept { return 0.9396926f*w + 0.1855740f*x; }); - mFilter1WX.process(Filter1Coeff, {mTemp.data(), SamplesToDo}, true, mS.data()+1); + mFilter1WX.process(Filter1Coeff, al::span{mTemp}.first(SamplesToDo), true, + al::span{mS}.subspan(1)); mS[0] = mDelayWX; mDelayWX = mS[SamplesToDo]; /* Precompute j(-0.3420201*W + 0.5098604*X) and store in mWX. */ - std::transform(winput, winput+SamplesToDo, xinput, mTemp.begin(), + std::transform(winput.begin(), winput.end(), xinput.begin(), mTemp.begin(), [](const float w, const float x) noexcept { return -0.3420201f*w + 0.5098604f*x; }); - mFilter2WX.process(Filter2Coeff, {mTemp.data(), SamplesToDo}, true, mWX.data()); + mFilter2WX.process(Filter2Coeff, al::span{mTemp}.first(SamplesToDo), true, mWX); /* Apply filter1 to Y and store in mD. */ - mFilter1Y.process(Filter1Coeff, {yinput, SamplesToDo}, SamplesToDo, mD.data()+1); + mFilter1Y.process(Filter1Coeff, yinput, true, al::span{mD}.subspan(1)); mD[0] = mDelayY; mDelayY = mD[SamplesToDo]; /* D = j(-0.3420201*W + 0.5098604*X) + 0.6554516*Y */ - for(size_t i{0};i < SamplesToDo;++i) - mD[i] = mWX[i] + 0.6554516f*mD[i]; + std::transform(mWX.begin(), mWX.begin()+SamplesToDo, mD.begin(), mD.begin(), + [](const float jwx, const float y) noexcept { return jwx + 0.6554516f*y; }); /* Apply the base filter to the existing output to align with the processed * signal. */ - mFilter1Direct[0].process(Filter1Coeff, {LeftOut, SamplesToDo}, true, mTemp.data()+1); + const auto left = al::span{al::assume_aligned<16>(LeftOut), SamplesToDo}; + mFilter1Direct[0].process(Filter1Coeff, left, true, al::span{mTemp}.subspan(1)); mTemp[0] = mDirectDelay[0]; mDirectDelay[0] = mTemp[SamplesToDo]; /* Left = (S + D)/2.0 */ - float *RESTRICT left{al::assume_aligned<16>(LeftOut)}; - for(size_t i{0};i < SamplesToDo;i++) + for(size_t i{0};i < SamplesToDo;++i) left[i] = (mS[i] + mD[i])*0.5f + mTemp[i]; - mFilter1Direct[1].process(Filter1Coeff, {RightOut, SamplesToDo}, true, mTemp.data()+1); + const auto right = al::span{al::assume_aligned<16>(RightOut), SamplesToDo}; + mFilter1Direct[1].process(Filter1Coeff, right, true, al::span{mTemp}.subspan(1)); mTemp[0] = mDirectDelay[1]; mDirectDelay[1] = mTemp[SamplesToDo]; /* Right = (S - D)/2.0 */ - float *RESTRICT right{al::assume_aligned<16>(RightOut)}; - for(size_t i{0};i < SamplesToDo;i++) + for(size_t i{0};i < SamplesToDo;++i) right[i] = (mS[i] - mD[i])*0.5f + mTemp[i]; } @@ -235,31 +408,29 @@ void UhjDecoder::decode(const al::span samples, const size_t samplesT { static_assert(sInputPadding <= sMaxPadding, "Filter padding is too large"); - const auto &PShift = GetPhaseShifter::Get(); + constexpr auto &PShift = PShifter; ASSUME(samplesToDo > 0); + ASSUME(samplesToDo <= BufferLineSize); { - const float *RESTRICT left{al::assume_aligned<16>(samples[0])}; - const float *RESTRICT right{al::assume_aligned<16>(samples[1])}; - const float *RESTRICT t{al::assume_aligned<16>(samples[2])}; + const auto left = al::span{al::assume_aligned<16>(samples[0]), samplesToDo+sInputPadding}; + const auto right = al::span{al::assume_aligned<16>(samples[1]), samplesToDo+sInputPadding}; + const auto t = al::span{al::assume_aligned<16>(samples[2]), samplesToDo+sInputPadding}; /* S = Left + Right */ - for(size_t i{0};i < samplesToDo+sInputPadding;++i) - mS[i] = left[i] + right[i]; + std::transform(left.begin(), left.end(), right.begin(), mS.begin(), std::plus{}); /* D = Left - Right */ - for(size_t i{0};i < samplesToDo+sInputPadding;++i) - mD[i] = left[i] - right[i]; + std::transform(left.begin(), left.end(), right.begin(), mD.begin(), std::minus{}); /* T */ - for(size_t i{0};i < samplesToDo+sInputPadding;++i) - mT[i] = t[i]; + std::copy(t.begin(), t.end(), mT.begin()); } - float *RESTRICT woutput{al::assume_aligned<16>(samples[0])}; - float *RESTRICT xoutput{al::assume_aligned<16>(samples[1])}; - float *RESTRICT youtput{al::assume_aligned<16>(samples[2])}; + const auto woutput = al::span{al::assume_aligned<16>(samples[0]), samplesToDo}; + const auto xoutput = al::span{al::assume_aligned<16>(samples[1]), samplesToDo}; + const auto youtput = al::span{al::assume_aligned<16>(samples[2]), samplesToDo}; /* Precompute j(0.828331*D + 0.767820*T) and store in xoutput. */ auto tmpiter = std::copy(mDTHistory.cbegin(), mDTHistory.cend(), mTemp.begin()); @@ -267,21 +438,22 @@ void UhjDecoder::decode(const al::span samples, const size_t samplesT [](const float d, const float t) noexcept { return 0.828331f*d + 0.767820f*t; }); if(updateState) LIKELY std::copy_n(mTemp.cbegin()+samplesToDo, mDTHistory.size(), mDTHistory.begin()); - PShift.process({xoutput, samplesToDo}, mTemp.data()); + PShift.process(xoutput, mTemp); /* W = 0.981532*S + 0.197484*j(0.828331*D + 0.767820*T) */ - for(size_t i{0};i < samplesToDo;++i) - woutput[i] = 0.981532f*mS[i] + 0.197484f*xoutput[i]; + std::transform(mS.begin(), mS.begin()+samplesToDo, xoutput.begin(), woutput.begin(), + [](const float s, const float jdt) noexcept { return 0.981532f*s + 0.197484f*jdt; }); + /* X = 0.418496*S - j(0.828331*D + 0.767820*T) */ - for(size_t i{0};i < samplesToDo;++i) - xoutput[i] = 0.418496f*mS[i] - xoutput[i]; + std::transform(mS.begin(), mS.begin()+samplesToDo, xoutput.begin(), xoutput.begin(), + [](const float s, const float jdt) noexcept { return 0.418496f*s - jdt; }); /* Precompute j*S and store in youtput. */ tmpiter = std::copy(mSHistory.cbegin(), mSHistory.cend(), mTemp.begin()); std::copy_n(mS.cbegin(), samplesToDo+sInputPadding, tmpiter); if(updateState) LIKELY std::copy_n(mTemp.cbegin()+samplesToDo, mSHistory.size(), mSHistory.begin()); - PShift.process({youtput, samplesToDo}, mTemp.data()); + PShift.process(youtput, mTemp); /* Y = 0.795968*D - 0.676392*T + j(0.186633*S) */ for(size_t i{0};i < samplesToDo;++i) @@ -289,10 +461,10 @@ void UhjDecoder::decode(const al::span samples, const size_t samplesT if(samples.size() > 3) { - float *RESTRICT zoutput{al::assume_aligned<16>(samples[3])}; + const auto zoutput = al::span{al::assume_aligned<16>(samples[3]), samplesToDo}; /* Z = 1.023332*Q */ - for(size_t i{0};i < samplesToDo;++i) - zoutput[i] = 1.023332f*zoutput[i]; + std::transform(zoutput.begin(), zoutput.end(), zoutput.begin(), + [](const float q) noexcept { return 1.023332f*q; }); } } @@ -302,70 +474,67 @@ void UhjDecoderIIR::decode(const al::span samples, const size_t samplesT static_assert(sInputPadding <= sMaxPadding, "Filter padding is too large"); ASSUME(samplesToDo > 0); + ASSUME(samplesToDo <= BufferLineSize); { - const float *RESTRICT left{al::assume_aligned<16>(samples[0])}; - const float *RESTRICT right{al::assume_aligned<16>(samples[1])}; + const auto left = al::span{al::assume_aligned<16>(samples[0]), samplesToDo+sInputPadding}; + const auto right = al::span{al::assume_aligned<16>(samples[1]), samplesToDo+sInputPadding}; /* S = Left + Right */ - for(size_t i{0};i < samplesToDo;++i) - mS[i] = left[i] + right[i]; + std::transform(left.begin(), left.end(), right.begin(), mS.begin(), std::plus{}); /* D = Left - Right */ - for(size_t i{0};i < samplesToDo;++i) - mD[i] = left[i] - right[i]; + std::transform(left.begin(), left.end(), right.begin(), mD.begin(), std::minus{}); } - float *RESTRICT woutput{al::assume_aligned<16>(samples[0])}; - float *RESTRICT xoutput{al::assume_aligned<16>(samples[1])}; - float *RESTRICT youtput{al::assume_aligned<16>(samples[2])}; + const auto woutput = al::span{al::assume_aligned<16>(samples[0]), samplesToDo}; + const auto xoutput = al::span{al::assume_aligned<16>(samples[1]), samplesToDo}; + const auto youtput = al::span{al::assume_aligned<16>(samples[2]), samplesToDo+sInputPadding}; /* 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.begin(), + 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, al::span{mTemp}.subspan(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, al::span{mS}.first(samplesToDo), updateState, mTemp); /* W = 0.981532*S + 0.197484*j(0.828331*D + 0.767820*T) */ - for(size_t i{0};i < samplesToDo;++i) - woutput[i] = 0.981532f*mTemp[i] + 0.197484f*xoutput[i]; + std::transform(mTemp.begin(), mTemp.begin()+samplesToDo, xoutput.begin(), woutput.begin(), + [](const float s, const float jdt) noexcept { return 0.981532f*s + 0.197484f*jdt; }); /* X = 0.418496*S - j(0.828331*D + 0.767820*T) */ - for(size_t i{0};i < samplesToDo;++i) - xoutput[i] = 0.418496f*mTemp[i] - xoutput[i]; + std::transform(mTemp.begin(), mTemp.begin()+samplesToDo, xoutput.begin(), xoutput.begin(), + [](const float s, const float jdt) noexcept { return 0.418496f*s - jdt; }); /* Apply filter1 to (0.795968*D - 0.676392*T) and store in mTemp. */ - std::transform(mD.cbegin(), mD.cbegin()+samplesToDo, youtput, youtput, + std::transform(mD.cbegin(), mD.cbegin()+samplesToDo, youtput.begin(), youtput.begin(), [](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.first(samplesToDo), updateState, mTemp); /* 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, al::span{mS}.subspan(1, samplesToDo), updateState, youtput); /* Y = 0.795968*D - 0.676392*T + j(0.186633*S) */ - for(size_t i{0};i < samplesToDo;++i) - youtput[i] = mTemp[i] + 0.186633f*youtput[i]; - + std::transform(mTemp.begin(), mTemp.begin()+samplesToDo, youtput.begin(), youtput.begin(), + [](const float dt, const float js) noexcept { return dt + 0.186633f*js; }); if(samples.size() > 3) { - float *RESTRICT zoutput{al::assume_aligned<16>(samples[3])}; + const auto zoutput = al::span{al::assume_aligned<16>(samples[3]), samplesToDo}; /* 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, updateState, mTemp); /* Z = 1.023332*Q */ - for(size_t i{0};i < samplesToDo;++i) - zoutput[i] = 1.023332f*mTemp[i]; + std::transform(mTemp.begin(), mTemp.end(), zoutput.begin(), + [](const float q) noexcept { return 1.023332f*q; }); } + + mFirstRun = false; } @@ -374,9 +543,9 @@ void UhjDecoderIIR::decode(const al::span samples, const size_t samplesT * S = Left + Right * D = Left - Right * - * W = 0.6098637*S - 0.6896511*j*w*D - * X = 0.8624776*S + 0.7626955*j*w*D - * Y = 1.6822415*w*D - 0.2156194*j*S + * W = 0.6098637*S + 0.6896511*j*w*D + * X = 0.8624776*S - 0.7626955*j*w*D + * Y = 1.6822415*w*D + 0.2156194*j*S * * where j is a +90 degree phase shift. w is a variable control for the * resulting stereo width, with the range 0 <= w <= 0.7. @@ -387,16 +556,16 @@ void UhjStereoDecoder::decode(const al::span samples, const size_t sa { static_assert(sInputPadding <= sMaxPadding, "Filter padding is too large"); - const auto &PShift = GetPhaseShifter::Get(); + constexpr auto &PShift = PShifter; ASSUME(samplesToDo > 0); + ASSUME(samplesToDo <= BufferLineSize); { - const float *RESTRICT left{al::assume_aligned<16>(samples[0])}; - const float *RESTRICT right{al::assume_aligned<16>(samples[1])}; + const auto left = al::span{al::assume_aligned<16>(samples[0]), samplesToDo+sInputPadding}; + const auto right = al::span{al::assume_aligned<16>(samples[1]), samplesToDo+sInputPadding}; - for(size_t i{0};i < samplesToDo+sInputPadding;++i) - mS[i] = left[i] + right[i]; + std::transform(left.begin(), left.end(), right.begin(), mS.begin(), std::plus{}); /* Pre-apply the width factor to the difference signal D. Smoothly * interpolate when it changes. @@ -405,53 +574,60 @@ void UhjStereoDecoder::decode(const al::span samples, const size_t sa const float wcurrent{(mCurrentWidth < 0.0f) ? wtarget : mCurrentWidth}; if(wtarget == wcurrent || !updateState) { - for(size_t i{0};i < samplesToDo+sInputPadding;++i) - mD[i] = (left[i] - right[i]) * wcurrent; + std::transform(left.begin(), left.end(), right.begin(), mD.begin(), + [wcurrent](const float l, const float r) noexcept { return (l-r) * wcurrent; }); mCurrentWidth = wcurrent; } else { const float wstep{(wtarget - wcurrent) / static_cast(samplesToDo)}; float fi{0.0f}; - for(size_t i{0};i < samplesToDo;++i) - { - 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; + + const auto lfade = left.first(samplesToDo); + auto dstore = std::transform(lfade.begin(), lfade.begin(), right.begin(), mD.begin(), + [wcurrent,wstep,&fi](const float l, const float r) noexcept + { + const float ret{(l-r) * (wcurrent + wstep*fi)}; + fi += 1.0f; + return ret; + }); + + const auto lend = left.subspan(samplesToDo); + const auto rend = right.subspan(samplesToDo); + std::transform(lend.begin(), lend.end(), rend.begin(), dstore, + [wtarget](const float l, const float r) noexcept { return (l-r) * wtarget; }); mCurrentWidth = wtarget; } } - float *RESTRICT woutput{al::assume_aligned<16>(samples[0])}; - float *RESTRICT xoutput{al::assume_aligned<16>(samples[1])}; - float *RESTRICT youtput{al::assume_aligned<16>(samples[2])}; + const auto woutput = al::span{al::assume_aligned<16>(samples[0]), samplesToDo}; + const auto xoutput = al::span{al::assume_aligned<16>(samples[1]), samplesToDo}; + const auto youtput = al::span{al::assume_aligned<16>(samples[2]), samplesToDo}; /* Precompute j*D and store in xoutput. */ auto tmpiter = std::copy(mDTHistory.cbegin(), mDTHistory.cend(), mTemp.begin()); std::copy_n(mD.cbegin(), samplesToDo+sInputPadding, tmpiter); if(updateState) LIKELY std::copy_n(mTemp.cbegin()+samplesToDo, mDTHistory.size(), mDTHistory.begin()); - PShift.process({xoutput, samplesToDo}, mTemp.data()); + PShift.process(xoutput, mTemp); - /* W = 0.6098637*S - 0.6896511*j*w*D */ - for(size_t i{0};i < samplesToDo;++i) - woutput[i] = 0.6098637f*mS[i] - 0.6896511f*xoutput[i]; - /* X = 0.8624776*S + 0.7626955*j*w*D */ - for(size_t i{0};i < samplesToDo;++i) - xoutput[i] = 0.8624776f*mS[i] + 0.7626955f*xoutput[i]; + /* W = 0.6098637*S + 0.6896511*j*w*D */ + std::transform(mS.begin(), mS.begin()+samplesToDo, xoutput.begin(), woutput.begin(), + [](const float s, const float jd) noexcept { return 0.6098637f*s + 0.6896511f*jd; }); + /* X = 0.8624776*S - 0.7626955*j*w*D */ + std::transform(mS.begin(), mS.begin()+samplesToDo, xoutput.begin(), xoutput.begin(), + [](const float s, const float jd) noexcept { return 0.8624776f*s - 0.7626955f*jd; }); /* Precompute j*S and store in youtput. */ tmpiter = std::copy(mSHistory.cbegin(), mSHistory.cend(), mTemp.begin()); std::copy_n(mS.cbegin(), samplesToDo+sInputPadding, tmpiter); if(updateState) LIKELY std::copy_n(mTemp.cbegin()+samplesToDo, mSHistory.size(), mSHistory.begin()); - PShift.process({youtput, samplesToDo}, mTemp.data()); + PShift.process(youtput, mTemp); - /* Y = 1.6822415*w*D - 0.2156194*j*S */ - for(size_t i{0};i < samplesToDo;++i) - youtput[i] = 1.6822415f*mD[i] - 0.2156194f*youtput[i]; + /* Y = 1.6822415*w*D + 0.2156194*j*S */ + std::transform(mD.begin(), mD.begin()+samplesToDo, youtput.begin(), youtput.begin(), + [](const float d, const float js) noexcept { return 1.6822415f*d + 0.2156194f*js; }); } void UhjStereoDecoderIIR::decode(const al::span samples, const size_t samplesToDo, @@ -460,13 +636,13 @@ void UhjStereoDecoderIIR::decode(const al::span samples, const size_t sa static_assert(sInputPadding <= sMaxPadding, "Filter padding is too large"); ASSUME(samplesToDo > 0); + ASSUME(samplesToDo <= BufferLineSize); { - const float *RESTRICT left{al::assume_aligned<16>(samples[0])}; - const float *RESTRICT right{al::assume_aligned<16>(samples[1])}; + const auto left = al::span{al::assume_aligned<16>(samples[0]), samplesToDo+sInputPadding}; + const auto right = al::span{al::assume_aligned<16>(samples[1]), samplesToDo+sInputPadding}; - for(size_t i{0};i < samplesToDo;++i) - mS[i] = left[i] + right[i]; + std::transform(left.begin(), left.end(), right.begin(), mS.begin(), std::plus{}); /* Pre-apply the width factor to the difference signal D. Smoothly * interpolate when it changes. @@ -475,53 +651,63 @@ void UhjStereoDecoderIIR::decode(const al::span 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) - mD[i] = (left[i] - right[i]) * wcurrent; + std::transform(left.begin(), left.end(), right.begin(), mD.begin(), + [wcurrent](const float l, const float r) noexcept + { return (l-r) * wcurrent; }); mCurrentWidth = wcurrent; } else { const float wstep{(wtarget - wcurrent) / static_cast(samplesToDo)}; float fi{0.0f}; - for(size_t i{0};i < samplesToDo;++i) - { - mD[i] = (left[i] - right[i]) * (wcurrent + wstep*fi); - fi += 1.0f; - } + + const auto lfade = left.first(samplesToDo); + auto dstore = std::transform(lfade.begin(), lfade.begin(), right.begin(), mD.begin(), + [wcurrent,wstep,&fi](const float l, const float r) noexcept + { + const float ret{(l-r) * (wcurrent + wstep*fi)}; + fi += 1.0f; + return ret; + }); + + const auto lend = left.subspan(samplesToDo); + const auto rend = right.subspan(samplesToDo); + std::transform(lend.begin(), lend.end(), rend.begin(), dstore, + [wtarget](const float l, const float r) noexcept { return (l-r) * wtarget; }); mCurrentWidth = wtarget; } } - float *RESTRICT woutput{al::assume_aligned<16>(samples[0])}; - float *RESTRICT xoutput{al::assume_aligned<16>(samples[1])}; - float *RESTRICT youtput{al::assume_aligned<16>(samples[2])}; + const auto woutput = al::span{al::assume_aligned<16>(samples[0]), samplesToDo}; + const auto xoutput = al::span{al::assume_aligned<16>(samples[1]), samplesToDo}; + const auto youtput = al::span{al::assume_aligned<16>(samples[2]), samplesToDo}; /* 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, al::span{mS}.first(samplesToDo), updateState, mTemp); /* 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, al::span{mD}.subspan(1, samplesToDo), updateState, xoutput); - /* W = 0.6098637*S - 0.6896511*j*w*D */ - for(size_t i{0};i < samplesToDo;++i) - woutput[i] = 0.6098637f*mTemp[i] - 0.6896511f*xoutput[i]; - /* X = 0.8624776*S + 0.7626955*j*w*D */ - for(size_t i{0};i < samplesToDo;++i) - xoutput[i] = 0.8624776f*mTemp[i] + 0.7626955f*xoutput[i]; + /* W = 0.6098637*S + 0.6896511*j*w*D */ + std::transform(mTemp.begin(), mTemp.begin()+samplesToDo, xoutput.begin(), woutput.begin(), + [](const float s, const float jd) noexcept { return 0.6098637f*s + 0.6896511f*jd; }); + /* X = 0.8624776*S - 0.7626955*j*w*D */ + std::transform(mTemp.begin(), mTemp.begin()+samplesToDo, xoutput.begin(), xoutput.begin(), + [](const float s, const float jd) noexcept { return 0.8624776f*s - 0.7626955f*jd; }); /* 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, al::span{mS}.subspan(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, al::span{mD}.first(samplesToDo), updateState, mTemp); - /* 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]; + /* Y = 1.6822415*w*D + 0.2156194*j*S */ + std::transform(mTemp.begin(), mTemp.begin()+samplesToDo, youtput.begin(), youtput.begin(), + [](const float d, const float js) noexcept { return 1.6822415f*d + 0.2156194f*js; }); + + mFirstRun = false; } diff --git a/3rdparty/openal/core/uhjfilter.h b/3rdparty/openal/core/uhjfilter.h index df30809444b7..264a1e1aff73 100644 --- a/3rdparty/openal/core/uhjfilter.h +++ b/3rdparty/openal/core/uhjfilter.h @@ -2,42 +2,51 @@ #define CORE_UHJFILTER_H #include +#include +#include -#include "almalloc.h" #include "alspan.h" #include "bufferline.h" +#include "opthelpers.h" -static constexpr size_t UhjLength256{256}; -static constexpr size_t UhjLength512{512}; +inline constexpr std::size_t UhjLength256{256}; +inline constexpr std::size_t UhjLength512{512}; -enum class UhjQualityType : uint8_t { +enum class UhjQualityType : std::uint8_t { IIR = 0, FIR256, FIR512, Default = IIR }; -extern UhjQualityType UhjDecodeQuality; -extern UhjQualityType UhjEncodeQuality; +inline UhjQualityType UhjDecodeQuality{UhjQualityType::Default}; +inline UhjQualityType UhjEncodeQuality{UhjQualityType::Default}; struct UhjAllPassFilter { struct AllPassState { /* Last two delayed components for direct form II. */ - float z[2]; + std::array z{}; }; std::array mState; + void processOne(const al::span coeffs, float x); void process(const al::span coeffs, const al::span src, - const bool update, float *RESTRICT dst); + const bool update, const al::span dst); }; -struct UhjEncoderBase { +struct SIMDALIGN UhjEncoderBase { + UhjEncoderBase() = default; + UhjEncoderBase(const UhjEncoderBase&) = delete; + UhjEncoderBase(UhjEncoderBase&&) = delete; virtual ~UhjEncoderBase() = default; - virtual size_t getDelay() noexcept = 0; + void operator=(const UhjEncoderBase&) = delete; + void operator=(UhjEncoderBase&&) = delete; + + virtual std::size_t getDelay() noexcept = 0; /** * Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input @@ -45,12 +54,15 @@ struct UhjEncoderBase { * with an additional +3dB boost). */ virtual void encode(float *LeftOut, float *RightOut, - const al::span InSamples, const size_t SamplesToDo) = 0; + const al::span InSamples, const std::size_t SamplesToDo) = 0; }; -template +template struct UhjEncoder final : public UhjEncoderBase { - static constexpr size_t sFilterDelay{N/2}; + static constexpr std::size_t sFftLength{256}; + static constexpr std::size_t sSegmentSize{sFftLength/2}; + static constexpr std::size_t sNumSegments{N/sSegmentSize}; + static constexpr std::size_t sFilterDelay{N/2 + sSegmentSize}; /* Delays and processing storage for the input signal. */ alignas(16) std::array mW{}; @@ -60,15 +72,16 @@ struct UhjEncoder final : public UhjEncoderBase { alignas(16) std::array mS{}; alignas(16) std::array 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 mWX{}; + /* History and temp storage for the convolution filter. */ + std::size_t mFifoPos{}, mCurrentSegment{}; + alignas(16) std::array mWXInOut{}; + alignas(16) std::array mFftBuffer{}; + alignas(16) std::array mWorkData{}; + alignas(16) std::array mWXHistory{}; alignas(16) std::array,2> mDirectDelay{}; - size_t getDelay() noexcept override { return sFilterDelay; } + std::size_t getDelay() noexcept override { return sFilterDelay; } /** * Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input @@ -76,13 +89,11 @@ struct UhjEncoder final : public UhjEncoderBase { * with an additional +3dB boost). */ void encode(float *LeftOut, float *RightOut, const al::span InSamples, - const size_t SamplesToDo) override; - - DEF_NEWDEL(UhjEncoder) + const std::size_t SamplesToDo) final; }; struct UhjEncoderIIR final : public UhjEncoderBase { - static constexpr size_t sFilterDelay{1}; + static constexpr std::size_t sFilterDelay{1}; /* Processing storage for the input signal. */ alignas(16) std::array mS{}; @@ -98,7 +109,7 @@ struct UhjEncoderIIR final : public UhjEncoderBase { std::array mFilter1Direct; std::array mDirectDelay{}; - size_t getDelay() noexcept override { return sFilterDelay; } + std::size_t getDelay() noexcept override { return sFilterDelay; } /** * Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input @@ -106,22 +117,26 @@ struct UhjEncoderIIR final : public UhjEncoderBase { * with an additional +3dB boost). */ void encode(float *LeftOut, float *RightOut, const al::span InSamples, - const size_t SamplesToDo) override; - - DEF_NEWDEL(UhjEncoderIIR) + const std::size_t SamplesToDo) final; }; -struct DecoderBase { - static constexpr size_t sMaxPadding{256}; +struct SIMDALIGN DecoderBase { + static constexpr std::size_t sMaxPadding{256}; /* For 2-channel UHJ, shelf filters should use these LF responses. */ static constexpr float sWLFScale{0.661f}; static constexpr float sXYLFScale{1.293f}; + DecoderBase() = default; + DecoderBase(const DecoderBase&) = delete; + DecoderBase(DecoderBase&&) = delete; virtual ~DecoderBase() = default; - virtual void decode(const al::span samples, const size_t samplesToDo, + void operator=(const DecoderBase&) = delete; + void operator=(DecoderBase&&) = delete; + + virtual void decode(const al::span samples, const std::size_t samplesToDo, const bool updateState) = 0; /** @@ -131,10 +146,10 @@ struct DecoderBase { float mWidthControl{0.593f}; }; -template +template struct UhjDecoder final : public DecoderBase { /* The number of extra sample frames needed for input. */ - static constexpr size_t sInputPadding{N/2}; + static constexpr std::size_t sInputPadding{N/2}; alignas(16) std::array mS{}; alignas(16) std::array mD{}; @@ -153,24 +168,23 @@ struct UhjDecoder final : public DecoderBase { * reconstructed from 2-channel UHJ should not be run through a normal * B-Format decoder, as it needs different shelf filters. */ - void decode(const al::span samples, const size_t samplesToDo, - const bool updateState) override; - - DEF_NEWDEL(UhjDecoder) + void decode(const al::span samples, const std::size_t samplesToDo, + const bool updateState) final; }; 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 std::size_t sInputPadding{1}; - alignas(16) std::array mS{}; - alignas(16) std::array mD{}; - alignas(16) std::array mTemp{}; - float mDelayS{}, mDelayDT{}, mDelayQ{}; + bool mFirstRun{true}; + alignas(16) std::array mS{}; + alignas(16) std::array mD{}; + alignas(16) std::array mTemp{}; UhjAllPassFilter mFilter1S; UhjAllPassFilter mFilter2DT; @@ -178,15 +192,13 @@ struct UhjDecoderIIR final : public DecoderBase { UhjAllPassFilter mFilter2S; UhjAllPassFilter mFilter1Q; - void decode(const al::span samples, const size_t samplesToDo, - const bool updateState) override; - - DEF_NEWDEL(UhjDecoderIIR) + void decode(const al::span samples, const std::size_t samplesToDo, + const bool updateState) final; }; -template +template struct UhjStereoDecoder final : public DecoderBase { - static constexpr size_t sInputPadding{N/2}; + static constexpr std::size_t sInputPadding{N/2}; float mCurrentWidth{-1.0f}; @@ -204,31 +216,27 @@ struct UhjStereoDecoder final : public DecoderBase { * should contain 3 channels, the first two being the left and right stereo * channels, and the third left empty. */ - void decode(const al::span samples, const size_t samplesToDo, - const bool updateState) override; - - DEF_NEWDEL(UhjStereoDecoder) + void decode(const al::span samples, const std::size_t samplesToDo, + const bool updateState) final; }; struct UhjStereoDecoderIIR final : public DecoderBase { - static constexpr size_t sInputPadding{0}; + static constexpr std::size_t sInputPadding{1}; + bool mFirstRun{true}; float mCurrentWidth{-1.0f}; - alignas(16) std::array mS{}; - alignas(16) std::array mD{}; - alignas(16) std::array mTemp{}; - float mDelayS{}, mDelayD{}; + alignas(16) std::array mS{}; + alignas(16) std::array mD{}; + alignas(16) std::array mTemp{}; UhjAllPassFilter mFilter1S; UhjAllPassFilter mFilter2D; UhjAllPassFilter mFilter1D; UhjAllPassFilter mFilter2S; - void decode(const al::span samples, const size_t samplesToDo, - const bool updateState) override; - - DEF_NEWDEL(UhjStereoDecoderIIR) + void decode(const al::span samples, const std::size_t samplesToDo, + const bool updateState) final; }; #endif /* CORE_UHJFILTER_H */ diff --git a/3rdparty/openal/core/uiddefs.cpp b/3rdparty/openal/core/uiddefs.cpp index 9471bba5cfbe..e52a9ae33c88 100644 --- a/3rdparty/openal/core/uiddefs.cpp +++ b/3rdparty/openal/core/uiddefs.cpp @@ -1,17 +1,13 @@ #include "config.h" - +#include "config_backends.h" #ifndef AL_NO_UID_DEFS -#if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H) +#if defined(HAVE_GUIDDEF_H) #define INITGUID #include -#ifdef HAVE_GUIDDEF_H #include -#else -#include -#endif DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71); DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71); @@ -20,7 +16,7 @@ DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf,0x08, 0x0 DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e); -#if defined(HAVE_WASAPI) && !defined(ALSOFT_UWP) +#if HAVE_WASAPI && !ALSOFT_UWP #include #include #include diff --git a/3rdparty/openal/core/voice.cpp b/3rdparty/openal/core/voice.cpp index b8acc7a66ef8..aa2417362a82 100644 --- a/3rdparty/openal/core/voice.cpp +++ b/3rdparty/openal/core/voice.cpp @@ -1,5 +1,6 @@ #include "config.h" +#include "config_simd.h" #include "voice.h" @@ -9,11 +10,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include @@ -42,45 +43,44 @@ #include "voice_change.h" struct CTag; -#ifdef HAVE_SSE +#if HAVE_SSE struct SSETag; #endif -#ifdef HAVE_NEON +#if HAVE_NEON struct NEONTag; #endif -static_assert(!(sizeof(DeviceBase::MixerBufferLine)&15), - "DeviceBase::MixerBufferLine must be a multiple of 16 bytes"); +static_assert(!(DeviceBase::MixerLineSize&3), "MixerLineSize must be a multiple of 4"); static_assert(!(MaxResamplerEdge&3), "MaxResamplerEdge is not a multiple of 4"); static_assert((BufferLineSize-1)/MaxPitch > 0, "MaxPitch is too large for BufferLineSize!"); static_assert((INT_MAX>>MixerFracBits)/MaxPitch > BufferLineSize, "MaxPitch and/or BufferLineSize are too large for MixerFracBits!"); -Resampler ResamplerDefault{Resampler::Cubic}; - namespace { using uint = unsigned int; using namespace std::chrono; +using namespace std::string_view_literals; -using HrtfMixerFunc = void(*)(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const MixHrtfFilter *hrtfparams, const size_t BufferSize); -using HrtfMixerBlendFunc = void(*)(const float *InSamples, float2 *AccumSamples, - const uint IrSize, const HrtfFilter *oldparams, const MixHrtfFilter *newparams, - const size_t BufferSize); +using HrtfMixerFunc = void(*)(const al::span InSamples, + const al::span AccumSamples, const uint IrSize, const MixHrtfFilter *hrtfparams, + const size_t SamplesToDo); +using HrtfMixerBlendFunc = void(*)(const al::span InSamples, + const al::span AccumSamples, const uint IrSize, const HrtfFilter *oldparams, + const MixHrtfFilter *newparams, const size_t SamplesToDo); HrtfMixerFunc MixHrtfSamples{MixHrtf_}; HrtfMixerBlendFunc MixHrtfBlendSamples{MixHrtfBlend_}; inline MixerOutFunc SelectMixer() { -#ifdef HAVE_NEON +#if HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return Mix_; #endif -#ifdef HAVE_SSE +#if HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) return Mix_; #endif @@ -89,11 +89,11 @@ inline MixerOutFunc SelectMixer() inline MixerOneFunc SelectMixerOne() { -#ifdef HAVE_NEON +#if HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return Mix_; #endif -#ifdef HAVE_SSE +#if HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) return Mix_; #endif @@ -102,11 +102,11 @@ inline MixerOneFunc SelectMixerOne() inline HrtfMixerFunc SelectHrtfMixer() { -#ifdef HAVE_NEON +#if HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return MixHrtf_; #endif -#ifdef HAVE_SSE +#if HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) return MixHrtf_; #endif @@ -115,11 +115,11 @@ inline HrtfMixerFunc SelectHrtfMixer() inline HrtfMixerBlendFunc SelectHrtfBlendMixer() { -#ifdef HAVE_NEON +#if HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return MixHrtfBlend_; #endif -#ifdef HAVE_SSE +#if HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) return MixHrtfBlend_; #endif @@ -128,42 +128,50 @@ inline HrtfMixerBlendFunc SelectHrtfBlendMixer() } // namespace -void Voice::InitMixer(std::optional resampler) +void Voice::InitMixer(std::optional resopt) { - if(resampler) + if(resopt) { struct ResamplerEntry { - const char name[16]; + const std::string_view name; const Resampler resampler; }; - constexpr ResamplerEntry ResamplerList[]{ - { "none", Resampler::Point }, - { "point", Resampler::Point }, - { "linear", Resampler::Linear }, - { "cubic", Resampler::Cubic }, - { "bsinc12", Resampler::BSinc12 }, - { "fast_bsinc12", Resampler::FastBSinc12 }, - { "bsinc24", Resampler::BSinc24 }, - { "fast_bsinc24", Resampler::FastBSinc24 }, + constexpr std::array ResamplerList{ + ResamplerEntry{"none"sv, Resampler::Point}, + ResamplerEntry{"point"sv, Resampler::Point}, + ResamplerEntry{"linear"sv, Resampler::Linear}, + ResamplerEntry{"spline"sv, Resampler::Spline}, + ResamplerEntry{"gaussian"sv, Resampler::Gaussian}, + ResamplerEntry{"bsinc12"sv, Resampler::BSinc12}, + ResamplerEntry{"fast_bsinc12"sv, Resampler::FastBSinc12}, + ResamplerEntry{"bsinc24"sv, Resampler::BSinc24}, + ResamplerEntry{"fast_bsinc24"sv, Resampler::FastBSinc24}, }; - const char *str{resampler->c_str()}; - if(al::strcasecmp(str, "bsinc") == 0) + std::string_view resampler{*resopt}; + + if (al::case_compare(resampler, "cubic"sv) == 0) + { + WARN("Resampler option \"%s\" is deprecated, using spline\n", resopt->c_str()); + resampler = "spline"sv; + } + else if(al::case_compare(resampler, "sinc4"sv) == 0 + || al::case_compare(resampler, "sinc8"sv) == 0) { - WARN("Resampler option \"%s\" is deprecated, using bsinc12\n", str); - str = "bsinc12"; + WARN("Resampler option \"%s\" is deprecated, using gaussian\n", resopt->c_str()); + resampler = "gaussian"sv; } - else if(al::strcasecmp(str, "sinc4") == 0 || al::strcasecmp(str, "sinc8") == 0) + else if(al::case_compare(resampler, "bsinc"sv) == 0) { - WARN("Resampler option \"%s\" is deprecated, using cubic\n", str); - str = "cubic"; + WARN("Resampler option \"%s\" is deprecated, using bsinc12\n", resopt->c_str()); + resampler = "bsinc12"sv; } - auto iter = std::find_if(std::begin(ResamplerList), std::end(ResamplerList), - [str](const ResamplerEntry &entry) -> bool - { return al::strcasecmp(str, entry.name) == 0; }); - if(iter == std::end(ResamplerList)) - ERR("Invalid resampler: %s\n", str); + auto iter = std::find_if(ResamplerList.begin(), ResamplerList.end(), + [resampler](const ResamplerEntry &entry) -> bool + { return al::case_compare(resampler, entry.name) == 0; }); + if(iter == ResamplerList.end()) + ERR("Invalid resampler: %s\n", resopt->c_str()); else ResamplerDefault = iter->resampler; } @@ -178,7 +186,7 @@ void Voice::InitMixer(std::optional resampler) namespace { /* IMA ADPCM Stepsize table */ -constexpr int IMAStep_size[89] = { +constexpr std::array IMAStep_size{{ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, @@ -188,35 +196,35 @@ constexpr int IMAStep_size[89] = { 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493,10442, 11487,12635,13899,15289,16818,18500,20350,22358,24633,27086,29794, 32767 -}; +}}; /* IMA4 ADPCM Codeword decode table */ -constexpr int IMA4Codeword[16] = { +constexpr std::array IMA4Codeword{{ 1, 3, 5, 7, 9, 11, 13, 15, -1,-3,-5,-7,-9,-11,-13,-15, -}; +}}; /* IMA4 ADPCM Step index adjust decode table */ -constexpr int IMA4Index_adjust[16] = { +constexpr std::arrayIMA4Index_adjust{{ -1,-1,-1,-1, 2, 4, 6, 8, -1,-1,-1,-1, 2, 4, 6, 8 -}; +}}; /* MSADPCM Adaption table */ -constexpr int MSADPCMAdaption[16] = { +constexpr std::array MSADPCMAdaption{{ 230, 230, 230, 230, 307, 409, 512, 614, 768, 614, 512, 409, 307, 230, 230, 230 -}; +}}; /* MSADPCM Adaption Coefficient tables */ -constexpr int MSADPCMAdaptionCoeff[7][2] = { - { 256, 0 }, - { 512, -256 }, - { 0, 0 }, - { 192, 64 }, - { 240, 0 }, - { 460, -208 }, - { 392, -232 } +constexpr std::array MSADPCMAdaptionCoeff{ + std::array{256, 0}, + std::array{512, -256}, + std::array{ 0, 0}, + std::array{192, 64}, + std::array{240, 0}, + std::array{460, -208}, + std::array{392, -232} }; @@ -224,9 +232,9 @@ void SendSourceStoppedEvent(ContextBase *context, uint id) { RingBuffer *ring{context->mAsyncEvents.get()}; auto evt_vec = ring->getWriteVector(); - if(evt_vec.first.len < 1) return; + if(evt_vec[0].len < 1) return; - auto &evt = InitAsyncEvent(evt_vec.first.buf); + auto &evt = InitAsyncEvent(evt_vec[0].buf); evt.mId = id; evt.mState = AsyncSrcState::Stop; @@ -234,8 +242,8 @@ void SendSourceStoppedEvent(ContextBase *context, uint id) } -const float *DoFilters(BiquadFilter &lpfilter, BiquadFilter &hpfilter, float *dst, - const al::span src, int type) +al::span DoFilters(BiquadFilter &lpfilter, BiquadFilter &hpfilter, + const al::span dst, const al::span src, int type) { switch(type) { @@ -247,71 +255,89 @@ const float *DoFilters(BiquadFilter &lpfilter, BiquadFilter &hpfilter, float *ds case AF_LowPass: lpfilter.process(src, dst); hpfilter.clear(); - return dst; + return dst.first(src.size()); case AF_HighPass: lpfilter.clear(); hpfilter.process(src, dst); - return dst; + return dst.first(src.size()); case AF_BandPass: DualBiquad{lpfilter, hpfilter}.process(src, dst); - return dst; + return dst.first(src.size()); } - return src.data(); + return src; } template -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 +inline void LoadSamples(const al::span dstSamples, const al::span srcData, + const size_t srcChan, const size_t srcOffset, const size_t srcStep, + const size_t samplesPerBlock [[maybe_unused]]) noexcept { - constexpr size_t sampleSize{sizeof(typename al::FmtTypeTraits::Type)}; - auto s = src + (srcOffset*srcStep + srcChan)*sampleSize; - - al::LoadSampleArray(dstSamples, s, srcStep, samplesToLoad); + using TypeTraits = al::FmtTypeTraits; + using SampleType = typename TypeTraits::Type; + static constexpr size_t sampleSize{sizeof(SampleType)}; + assert(srcChan < srcStep); + auto converter = TypeTraits{}; + + al::span src{reinterpret_cast(srcData.data()), + srcData.size()/sampleSize}; + auto ssrc = src.cbegin() + ptrdiff_t(srcOffset*srcStep); + std::generate(dstSamples.begin(), dstSamples.end(), [&ssrc,srcChan,srcStep,converter] + { + auto ret = converter(ssrc[srcChan]); + ssrc += ptrdiff_t(srcStep); + return ret; + }); } template<> -inline void LoadSamples(float *RESTRICT dstSamples, const std::byte *src, +inline void LoadSamples(al::span dstSamples, al::span src, const size_t srcChan, const size_t srcOffset, const size_t srcStep, - const size_t samplesPerBlock, const size_t samplesToLoad) noexcept + const size_t samplesPerBlock) noexcept { + static constexpr int MaxStepIndex{static_cast(IMAStep_size.size()) - 1}; + + assert(srcStep > 0 || srcStep <= 2); + assert(srcChan < srcStep); + assert(samplesPerBlock > 1); const size_t blockBytes{((samplesPerBlock-1)/2 + 4)*srcStep}; /* Skip to the ADPCM block containing the srcOffset sample. */ - src += srcOffset/samplesPerBlock*blockBytes; + src = src.subspan(srcOffset/samplesPerBlock*blockBytes); /* Calculate how many samples need to be skipped in the block. */ size_t skip{srcOffset % samplesPerBlock}; /* NOTE: This could probably be optimized better. */ - size_t wrote{0}; - do { - static constexpr int MaxStepIndex{static_cast(std::size(IMAStep_size)) - 1}; + auto dst = dstSamples.begin(); + while(dst != dstSamples.end()) + { /* 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{int(src[srcChan*4]) | (int(src[srcChan*4 + 1]) << 8)}; + int sample{int(src[srcChan*4 + 0]) | (int(src[srcChan*4 + 1]) << 8)}; int index{int(src[srcChan*4 + 2]) | (int(src[srcChan*4 + 3]) << 8)}; + auto nibbleData = src.subspan((srcStep+srcChan)*4); + src = src.subspan(blockBytes); sample = (sample^0x8000) - 32768; - index = clampi((index^0x8000) - 32768, 0, MaxStepIndex); + index = std::clamp((index^0x8000) - 32768, 0, MaxStepIndex); if(skip == 0) { - dstSamples[wrote++] = static_cast(sample) / 32768.0f; - if(wrote == samplesToLoad) return; + *dst = static_cast(sample) / 32768.0f; + if(++dst == dstSamples.end()) return; } else --skip; - auto decode_sample = [&sample,&index](const uint nibble) + auto decode_sample = [&sample,&index](const uint8_t nibble) { - sample += IMA4Codeword[nibble] * IMAStep_size[index] / 8; - sample = clampi(sample, -32768, 32767); + sample += IMA4Codeword[nibble] * IMAStep_size[static_cast(index)] / 8; + sample = std::clamp(sample, -32768, 32767); index += IMA4Index_adjust[nibble]; - index = clampi(index, 0, MaxStepIndex); + index = std::clamp(index, 0, MaxStepIndex); return sample; }; @@ -324,7 +350,7 @@ inline void LoadSamples(float *RESTRICT dstSamples, const std::byte *sr * always be less than the block size). They need to be decoded despite * being ignored for proper state on the remaining samples. */ - const std::byte *nibbleData{src + (srcStep+srcChan)*4}; + static constexpr auto NibbleMask = std::byte{0xf}; size_t nibbleOffset{0}; const size_t startOffset{skip + 1}; for(;skip;--skip) @@ -334,61 +360,59 @@ inline void LoadSamples(float *RESTRICT dstSamples, const std::byte *sr const size_t byteOffset{wordOffset*srcStep + ((nibbleOffset>>1)&3u)}; ++nibbleOffset; - std::ignore = decode_sample(uint(nibbleData[byteOffset]>>byteShift) & 15u); + const auto nval = (nibbleData[byteOffset]>>byteShift) & NibbleMask; + std::ignore = decode_sample(al::to_underlying(nval)); } /* Second, decode the rest of the block and write to the output, until * the end of the block or the end of output. */ - const size_t todo{minz(samplesPerBlock-startOffset, samplesToLoad-wrote)}; - for(size_t i{0};i < todo;++i) + const size_t todo{std::min(samplesPerBlock-startOffset, size_t(dstSamples.end()-dst))}; + dst = std::generate_n(dst, todo, [&] { const size_t byteShift{(nibbleOffset&1) * 4}; const size_t wordOffset{(nibbleOffset>>1) & ~3_uz}; const size_t byteOffset{wordOffset*srcStep + ((nibbleOffset>>1)&3u)}; ++nibbleOffset; - const int result{decode_sample(uint(nibbleData[byteOffset]>>byteShift) & 15u)}; - dstSamples[wrote++] = static_cast(result) / 32768.0f; - } - if(wrote == samplesToLoad) - return; - - src += blockBytes; - } while(true); + const auto nval = (nibbleData[byteOffset]>>byteShift) & NibbleMask; + return static_cast(decode_sample(al::to_underlying(nval))) / 32768.0f; + }); + } } template<> -inline void LoadSamples(float *RESTRICT dstSamples, const std::byte *src, +inline void LoadSamples(al::span dstSamples, al::span src, const size_t srcChan, const size_t srcOffset, const size_t srcStep, - const size_t samplesPerBlock, const size_t samplesToLoad) noexcept + const size_t samplesPerBlock) noexcept { + assert(srcStep > 0 || srcStep <= 2); + assert(srcChan < srcStep); + assert(samplesPerBlock > 2); const size_t blockBytes{((samplesPerBlock-2)/2 + 7)*srcStep}; - src += srcOffset/samplesPerBlock*blockBytes; + src = src.subspan(srcOffset/samplesPerBlock*blockBytes); size_t skip{srcOffset % samplesPerBlock}; - size_t wrote{0}; - do { + auto dst = dstSamples.begin(); + while(dst != dstSamples.end()) + { /* Each MS ADPCM block starts with an 8-bit block predictor, used to * dictate how the two sample history values are mixed with the decoded * sample, and an initial signed 16-bit delta value which scales the * nibble sample value. This is followed by the two initial 16-bit * sample history values. */ - const std::byte *input{src}; - const uint8_t blockpred{std::min(uint8_t(input[srcChan]), uint8_t{6})}; - input += srcStep; - int delta{int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1]) << 8)}; - input += srcStep*2; - - int sampleHistory[2]{}; - sampleHistory[0] = int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1])<<8); - input += srcStep*2; - sampleHistory[1] = int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1])<<8); - input += srcStep*2; - - const al::span coeffs{MSADPCMAdaptionCoeff[blockpred]}; + const uint8_t blockpred{std::min(uint8_t(src[srcChan]), uint8_t{6})}; + int delta{int(src[srcStep + 2*srcChan + 0]) | (int(src[srcStep + 2*srcChan + 1]) << 8)}; + + auto sampleHistory = std::array{ + int(src[3*srcStep + 2*srcChan + 0]) | (int(src[3*srcStep + 2*srcChan + 1])<<8), + int(src[5*srcStep + 2*srcChan + 0]) | (int(src[5*srcStep + 2*srcChan + 1])<<8)}; + const auto input = src.subspan(7*srcStep); + src = src.subspan(blockBytes); + + const auto coeffs = al::span{MSADPCMAdaptionCoeff[blockpred]}; delta = (delta^0x8000) - 32768; sampleHistory[0] = (sampleHistory[0]^0x8000) - 32768; sampleHistory[1] = (sampleHistory[1]^0x8000) - 32768; @@ -398,31 +422,31 @@ inline void LoadSamples(float *RESTRICT dstSamples, const std::byte */ if(skip == 0) { - dstSamples[wrote++] = static_cast(sampleHistory[1]) / 32768.0f; - if(wrote == samplesToLoad) return; - dstSamples[wrote++] = static_cast(sampleHistory[0]) / 32768.0f; - if(wrote == samplesToLoad) return; + *dst = static_cast(sampleHistory[1]) / 32768.0f; + if(++dst == dstSamples.end()) return; + *dst = static_cast(sampleHistory[0]) / 32768.0f; + if(++dst == dstSamples.end()) return; } else if(skip == 1) { --skip; - dstSamples[wrote++] = static_cast(sampleHistory[0]) / 32768.0f; - if(wrote == samplesToLoad) return; + *dst = static_cast(sampleHistory[0]) / 32768.0f; + if(++dst == dstSamples.end()) return; } else skip -= 2; - auto decode_sample = [&sampleHistory,&delta,coeffs](const int nibble) + auto decode_sample = [&sampleHistory,&delta,coeffs](const uint8_t nibble) { int pred{(sampleHistory[0]*coeffs[0] + sampleHistory[1]*coeffs[1]) / 256}; pred += ((nibble^0x08) - 0x08) * delta; - pred = clampi(pred, -32768, 32767); + pred = std::clamp(pred, -32768, 32767); sampleHistory[1] = sampleHistory[0]; sampleHistory[0] = pred; delta = (MSADPCMAdaption[nibble] * delta) / 256; - delta = maxi(16, delta); + delta = std::max(16, delta); return pred; }; @@ -430,6 +454,7 @@ inline void LoadSamples(float *RESTRICT dstSamples, const std::byte /* The rest of the block is a series of nibbles, interleaved per- * channel. First, skip samples. */ + static constexpr auto NibbleMask = std::byte{0xf}; const size_t startOffset{skip + 2}; size_t nibbleOffset{srcChan}; for(;skip;--skip) @@ -438,42 +463,40 @@ inline void LoadSamples(float *RESTRICT dstSamples, const std::byte const size_t byteShift{((nibbleOffset&1)^1) * 4}; nibbleOffset += srcStep; - std::ignore = decode_sample(int(input[byteOffset]>>byteShift) & 15); + const auto nval = (input[byteOffset]>>byteShift) & NibbleMask; + std::ignore = decode_sample(al::to_underlying(nval)); } /* Now decode the rest of the block, until the end of the block or the * dst buffer is filled. */ - const size_t todo{minz(samplesPerBlock-startOffset, samplesToLoad-wrote)}; - for(size_t j{0};j < todo;++j) + const size_t todo{std::min(samplesPerBlock-startOffset, size_t(dstSamples.end()-dst))}; + dst = std::generate_n(dst, todo, [&] { const size_t byteOffset{nibbleOffset>>1}; const size_t byteShift{((nibbleOffset&1)^1) * 4}; nibbleOffset += srcStep; - const int sample{decode_sample(int(input[byteOffset]>>byteShift) & 15)}; - dstSamples[wrote++] = static_cast(sample) / 32768.0f; - } - if(wrote == samplesToLoad) - return; - - src += blockBytes; - } while(true); + const auto nval = (input[byteOffset]>>byteShift) & NibbleMask; + return static_cast(decode_sample(al::to_underlying(nval))) / 32768.0f; + }); + } } -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 +void LoadSamples(const al::span dstSamples, const al::span src, + const size_t srcChan, const size_t srcOffset, const FmtType srcType, const size_t srcStep, + const size_t samplesPerBlock) noexcept { #define HANDLE_FMT(T) case T: \ LoadSamples(dstSamples, src, srcChan, srcOffset, srcStep, \ - samplesPerBlock, samplesToLoad); \ + samplesPerBlock); \ break switch(srcType) { HANDLE_FMT(FmtUByte); HANDLE_FMT(FmtShort); + HANDLE_FMT(FmtInt); HANDLE_FMT(FmtFloat); HANDLE_FMT(FmtDouble); HANDLE_FMT(FmtMulaw); @@ -486,26 +509,24 @@ void LoadSamples(float *dstSamples, const std::byte *src, const size_t srcChan, void LoadBufferStatic(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, const size_t dataPosInt, const FmtType sampleType, const size_t srcChannel, - const size_t srcStep, size_t samplesLoaded, const size_t samplesToLoad, - float *voiceSamples) + const size_t srcStep, al::span voiceSamples) { if(!bufferLoopItem) { + float lastSample{0.0f}; /* Load what's left to play from the buffer */ if(buffer->mSampleLen > dataPosInt) LIKELY { const size_t buffer_remaining{buffer->mSampleLen - dataPosInt}; - const size_t remaining{minz(samplesToLoad-samplesLoaded, buffer_remaining)}; - LoadSamples(voiceSamples+samplesLoaded, buffer->mSamples, srcChannel, dataPosInt, - sampleType, srcStep, buffer->mBlockAlign, remaining); - samplesLoaded += remaining; + const size_t remaining{std::min(voiceSamples.size(), buffer_remaining)}; + LoadSamples(voiceSamples.first(remaining), buffer->mSamples, srcChannel, dataPosInt, + sampleType, srcStep, buffer->mBlockAlign); + lastSample = voiceSamples[remaining-1]; + voiceSamples = voiceSamples.subspan(remaining); } - if(const size_t toFill{samplesToLoad - samplesLoaded}) - { - auto srcsamples = voiceSamples + samplesLoaded; - std::fill_n(srcsamples, toFill, *(srcsamples-1)); - } + if(const size_t toFill{voiceSamples.size()}) + std::fill_n(voiceSamples.begin(), toFill, lastSample); } else { @@ -517,49 +538,47 @@ void LoadBufferStatic(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, : (((dataPosInt-loopStart)%(loopEnd-loopStart)) + loopStart)}; /* Load what's left of this loop iteration */ - const size_t remaining{minz(samplesToLoad-samplesLoaded, loopEnd-dataPosInt)}; - LoadSamples(voiceSamples+samplesLoaded, buffer->mSamples, srcChannel, intPos, sampleType, - srcStep, buffer->mBlockAlign, remaining); - samplesLoaded += remaining; + const size_t remaining{std::min(voiceSamples.size(), loopEnd-dataPosInt)}; + LoadSamples(voiceSamples.first(remaining), buffer->mSamples, srcChannel, intPos, + sampleType, srcStep, buffer->mBlockAlign); + voiceSamples = voiceSamples.subspan(remaining); /* Load repeats of the loop to fill the buffer. */ const size_t loopSize{loopEnd - loopStart}; - while(const size_t toFill{minz(samplesToLoad - samplesLoaded, loopSize)}) + while(const size_t toFill{std::min(voiceSamples.size(), loopSize)}) { - LoadSamples(voiceSamples+samplesLoaded, buffer->mSamples, srcChannel, loopStart, - sampleType, srcStep, buffer->mBlockAlign, toFill); - samplesLoaded += toFill; + LoadSamples(voiceSamples.first(toFill), buffer->mSamples, srcChannel, loopStart, + sampleType, srcStep, buffer->mBlockAlign); + voiceSamples = voiceSamples.subspan(toFill); } } } void LoadBufferCallback(VoiceBufferItem *buffer, const size_t dataPosInt, const size_t numCallbackSamples, const FmtType sampleType, const size_t srcChannel, - const size_t srcStep, size_t samplesLoaded, const size_t samplesToLoad, float *voiceSamples) + const size_t srcStep, al::span voiceSamples) { - /* Load what's left to play from the buffer */ + float lastSample{0.0f}; if(numCallbackSamples > dataPosInt) LIKELY { - const size_t remaining{minz(samplesToLoad-samplesLoaded, numCallbackSamples-dataPosInt)}; - LoadSamples(voiceSamples+samplesLoaded, buffer->mSamples, srcChannel, dataPosInt, - sampleType, srcStep, buffer->mBlockAlign, remaining); - samplesLoaded += remaining; + const size_t remaining{std::min(voiceSamples.size(), numCallbackSamples-dataPosInt)}; + LoadSamples(voiceSamples.first(remaining), buffer->mSamples, srcChannel, dataPosInt, + sampleType, srcStep, buffer->mBlockAlign); + lastSample = voiceSamples[remaining-1]; + voiceSamples = voiceSamples.subspan(remaining); } - if(const size_t toFill{samplesToLoad - samplesLoaded}) - { - auto srcsamples = voiceSamples + samplesLoaded; - std::fill_n(srcsamples, toFill, *(srcsamples-1)); - } + if(const size_t toFill{voiceSamples.size()}) + std::fill_n(voiceSamples.begin(), toFill, lastSample); } void LoadBufferQueue(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, size_t dataPosInt, const FmtType sampleType, const size_t srcChannel, - const size_t srcStep, size_t samplesLoaded, const size_t samplesToLoad, - float *voiceSamples) + const size_t srcStep, al::span voiceSamples) { + float lastSample{0.0f}; /* Crawl the buffer queue to fill in the temp buffer */ - while(buffer && samplesLoaded != samplesToLoad) + while(buffer && !voiceSamples.empty()) { if(dataPosInt >= buffer->mSampleLen) { @@ -569,48 +588,47 @@ void LoadBufferQueue(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, continue; } - const size_t remaining{minz(samplesToLoad-samplesLoaded, buffer->mSampleLen-dataPosInt)}; - LoadSamples(voiceSamples+samplesLoaded, buffer->mSamples, srcChannel, dataPosInt, - sampleType, srcStep, buffer->mBlockAlign, remaining); + const size_t remaining{std::min(voiceSamples.size(), buffer->mSampleLen-dataPosInt)}; + LoadSamples(voiceSamples.first(remaining), buffer->mSamples, srcChannel, dataPosInt, + sampleType, srcStep, buffer->mBlockAlign); - samplesLoaded += remaining; - if(samplesLoaded == samplesToLoad) + lastSample = voiceSamples[remaining-1]; + voiceSamples = voiceSamples.subspan(remaining); + if(voiceSamples.empty()) break; dataPosInt = 0; buffer = buffer->mNext.load(std::memory_order_acquire); if(!buffer) buffer = bufferLoopItem; } - if(const size_t toFill{samplesToLoad - samplesLoaded}) - { - auto srcsamples = voiceSamples + samplesLoaded; - std::fill_n(srcsamples, toFill, *(srcsamples-1)); - } + if(const size_t toFill{voiceSamples.size()}) + std::fill_n(voiceSamples.begin(), toFill, lastSample); } -void DoHrtfMix(const float *samples, const uint DstBufferSize, DirectParams &parms, - const float TargetGain, const uint Counter, uint OutPos, const bool IsPlaying, - DeviceBase *Device) +void DoHrtfMix(const al::span samples, DirectParams &parms, const float TargetGain, + const size_t Counter, size_t OutPos, const bool IsPlaying, DeviceBase *Device) { const uint IrSize{Device->mIrSize}; - auto &HrtfSamples = Device->HrtfSourceData; - auto &AccumSamples = Device->HrtfAccumData; + const auto HrtfSamples = al::span{Device->ExtraSampleData}; + const auto AccumSamples = al::span{Device->HrtfAccumData}; /* Copy the HRTF history and new input samples into a temp buffer. */ auto src_iter = std::copy(parms.Hrtf.History.begin(), parms.Hrtf.History.end(), - std::begin(HrtfSamples)); - std::copy_n(samples, DstBufferSize, src_iter); + HrtfSamples.begin()); + std::copy_n(samples.begin(), samples.size(), src_iter); /* Copy the last used samples back into the history buffer for later. */ if(IsPlaying) LIKELY - std::copy_n(std::begin(HrtfSamples) + DstBufferSize, parms.Hrtf.History.size(), - parms.Hrtf.History.begin()); + { + const auto endsamples = HrtfSamples.subspan(samples.size(), parms.Hrtf.History.size()); + std::copy_n(endsamples.cbegin(), endsamples.size(), parms.Hrtf.History.begin()); + } /* If fading and this is the first mixing pass, fade between the IRs. */ - uint fademix{0u}; + size_t fademix{0}; if(Counter && OutPos == 0) { - fademix = minu(DstBufferSize, Counter); + fademix = std::min(samples.size(), Counter); float gain{TargetGain}; @@ -629,8 +647,8 @@ void DoHrtfMix(const float *samples, const uint DstBufferSize, DirectParams &par parms.Hrtf.Target.Coeffs, parms.Hrtf.Target.Delay, 0.0f, gain / static_cast(fademix)}; - MixHrtfBlendSamples(HrtfSamples, AccumSamples+OutPos, IrSize, &parms.Hrtf.Old, &hrtfparams, - fademix); + MixHrtfBlendSamples(HrtfSamples, AccumSamples.subspan(OutPos), IrSize, &parms.Hrtf.Old, + &hrtfparams, fademix); /* Update the old parameters with the result. */ parms.Hrtf.Old = parms.Hrtf.Target; @@ -638,15 +656,15 @@ void DoHrtfMix(const float *samples, const uint DstBufferSize, DirectParams &par OutPos += fademix; } - if(fademix < DstBufferSize) + if(fademix < samples.size()) { - const uint todo{DstBufferSize - fademix}; + const size_t todo{samples.size() - fademix}; float gain{TargetGain}; /* Interpolate the target gain if the gain fading lasts longer than * this mix. */ - if(Counter > DstBufferSize) + if(Counter > samples.size()) { const float a{static_cast(todo) / static_cast(Counter-fademix)}; gain = lerpf(parms.Hrtf.Old.Gain, TargetGain, a); @@ -657,37 +675,40 @@ void DoHrtfMix(const float *samples, const uint DstBufferSize, DirectParams &par parms.Hrtf.Target.Delay, parms.Hrtf.Old.Gain, (gain - parms.Hrtf.Old.Gain) / static_cast(todo)}; - MixHrtfSamples(HrtfSamples+fademix, AccumSamples+OutPos, IrSize, &hrtfparams, todo); + MixHrtfSamples(HrtfSamples.subspan(fademix), AccumSamples.subspan(OutPos), IrSize, + &hrtfparams, todo); /* Store the now-current gain for next time. */ parms.Hrtf.Old.Gain = gain; } } -void DoNfcMix(const al::span samples, FloatBufferLine *OutBuffer, DirectParams &parms, - const float *TargetGains, const uint Counter, const uint OutPos, DeviceBase *Device) +void DoNfcMix(const al::span samples, al::span OutBuffer, + DirectParams &parms, const al::span OutGains, + const uint Counter, const uint OutPos, DeviceBase *Device) { - using FilterProc = void (NfcFilter::*)(const al::span, float*); - static constexpr FilterProc NfcProcess[MaxAmbiOrder+1]{ - nullptr, &NfcFilter::process1, &NfcFilter::process2, &NfcFilter::process3}; + using FilterProc = void (NfcFilter::*)(const al::span, const al::span); + static constexpr std::array NfcProcess{{ + nullptr, &NfcFilter::process1, &NfcFilter::process2, &NfcFilter::process3}}; - float *CurrentGains{parms.Gains.Current.data()}; - MixSamples(samples, {OutBuffer, 1u}, CurrentGains, TargetGains, Counter, OutPos); - ++OutBuffer; - ++CurrentGains; - ++TargetGains; + MixSamples(samples, al::span{OutBuffer[0]}.subspan(OutPos), parms.Gains.Current[0], + OutGains[0], Counter); + OutBuffer = OutBuffer.subspan(1); + auto CurrentGains = al::span{parms.Gains.Current}.subspan(1); + auto TargetGains = OutGains.subspan(1); - const al::span nfcsamples{Device->NfcSampleData, samples.size()}; + const auto nfcsamples = al::span{Device->ExtraSampleData}.first(samples.size()); size_t order{1}; while(const size_t chancount{Device->NumChannelsPerOrder[order]}) { - (parms.NFCtrlFilter.*NfcProcess[order])(samples, nfcsamples.data()); - MixSamples(nfcsamples, {OutBuffer, chancount}, CurrentGains, TargetGains, Counter, OutPos); - OutBuffer += chancount; - CurrentGains += chancount; - TargetGains += chancount; + (parms.NFCtrlFilter.*NfcProcess[order])(samples, nfcsamples); + MixSamples(nfcsamples, OutBuffer.first(chancount), CurrentGains, TargetGains, Counter, + OutPos); if(++order == MaxAmbiOrder+1) break; + OutBuffer = OutBuffer.subspan(chancount); + CurrentGains = CurrentGains.subspan(chancount); + TargetGains = TargetGains.subspan(chancount); } } @@ -696,7 +717,7 @@ void DoNfcMix(const al::span samples, FloatBufferLine *OutBuffer, D void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds deviceTime, const uint SamplesToDo) { - static constexpr std::array SilentTarget{}; + static constexpr std::array SilentTarget{}; ASSUME(SamplesToDo > 0); @@ -750,17 +771,9 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi /* Get the number of samples ahead of the current time that output * should start at. Skip this update if it's beyond the output sample * count. - * - * Round the start position to a multiple of 4, which some mixers want. - * This makes the start time accurate to 4 samples. This could be made - * sample-accurate by forcing non-SIMD functions on the first run. */ - seconds::rep sampleOffset{duration_cast(diff * Device->Frequency).count()}; - sampleOffset = (sampleOffset+2) & ~seconds::rep{3}; - if(sampleOffset >= SamplesToDo) - return; - - OutPos = static_cast(sampleOffset); + OutPos = static_cast(round(diff * Device->Frequency).count()); + if(OutPos >= SamplesToDo) return; } /* Calculate the number of samples to mix, and the number of (resampled) @@ -772,34 +785,34 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi /* Get a span of pointers to hold the floating point, deinterlaced, * resampled buffer data to be mixed. */ - std::array SamplePointers; - const al::span MixingSamples{SamplePointers.data(), mChans.size()}; - auto get_bufferline = [](DeviceBase::MixerBufferLine &bufline) noexcept -> float* - { return bufline.data(); }; - std::transform(Device->mSampleData.end() - mChans.size(), Device->mSampleData.end(), - MixingSamples.begin(), get_bufferline); - - /* If there's a matching sample step and no phase offset, use a simple copy - * for resampling. - */ - const ResamplerFunc Resample{(increment == MixerFracOne && DataPosFrac == 0) - ? ResamplerFunc{[](const InterpState*, const float *RESTRICT src, uint, const uint, - const al::span dst) { std::copy_n(src, dst.size(), dst.begin()); }} - : mResampler}; + auto SamplePointers = std::array{}; + const auto MixingSamples = al::span{SamplePointers}.first(mChans.size()); + { + const uint channelStep{(samplesToLoad+3u)&~3u}; + auto base = Device->mSampleData.end() - MixingSamples.size()*channelStep; + std::generate(MixingSamples.begin(), MixingSamples.end(), [&base,channelStep] + { + const auto ret = base; + base += channelStep; + return al::to_address(ret); + }); + } /* UHJ2 and SuperStereo only have 2 buffer channels, but 3 mixing channels - * (3rd channel is generated from decoding). + * (3rd channel is generated from decoding). MonoDup only has 1 buffer + * channel, but 2 mixing channels (2nd channel is just duplicated). */ - const size_t realChannels{(mFmtChannels == FmtUHJ2 || mFmtChannels == FmtSuperStereo) ? 2u + const size_t realChannels{(mFmtChannels == FmtMonoDup) ? 1u + : (mFmtChannels == FmtUHJ2 || mFmtChannels == FmtSuperStereo) ? 2u : MixingSamples.size()}; for(size_t chan{0};chan < realChannels;++chan) { - using ResBufType = decltype(DeviceBase::mResampleData); - static constexpr uint srcSizeMax{static_cast(ResBufType{}.size()-MaxResamplerEdge)}; + static constexpr uint ResBufSize{std::tuple_size_v}; + static constexpr uint srcSizeMax{ResBufSize - MaxResamplerEdge}; const al::span prevSamples{mPrevSamples[chan]}; - const auto resampleBuffer = std::copy(prevSamples.cbegin(), prevSamples.cend(), - Device->mResampleData.begin()) - MaxResamplerEdge; + std::copy(prevSamples.cbegin(), prevSamples.cend(), Device->mResampleData.begin()); + const auto resampleBuffer = al::span{Device->mResampleData}.subspan(); int intPos{DataPosInt}; uint fracPos{DataPosFrac}; @@ -831,7 +844,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi dataSize64 += ext + MaxResamplerEdge; if(dataSize64 <= srcSizeMax) - return std::make_pair(dstBufferSize, static_cast(dataSize64)); + return std::array{dstBufferSize, static_cast(dataSize64)}; /* If the source size got saturated, we can't fill the desired * dst size. Figure out how many dst samples we can fill. @@ -846,88 +859,103 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi */ dstBufferSize = static_cast(dataSize64) & ~3u; } - return std::make_pair(dstBufferSize, srcSizeMax); + return std::array{dstBufferSize, srcSizeMax}; }; - const auto bufferSizes = calc_buffer_sizes(samplesToLoad - samplesLoaded); - const auto dstBufferSize = bufferSizes.first; - const auto srcBufferSize = bufferSizes.second; + const auto [dstBufferSize, srcBufferSize] = calc_buffer_sizes( + samplesToLoad - samplesLoaded); + + size_t srcSampleDelay{0}; + if(intPos < 0) UNLIKELY + { + /* If the current position is negative, there's that many + * silent samples to load before using the buffer. + */ + srcSampleDelay = static_cast(-intPos); + if(srcSampleDelay >= srcBufferSize) + { + /* If the number of silent source samples exceeds the + * number to load, the output will be silent. + */ + std::fill_n(MixingSamples[chan]+samplesLoaded, dstBufferSize, 0.0f); + std::fill_n(resampleBuffer.begin(), srcBufferSize, 0.0f); + goto skip_resample; + } + + std::fill_n(resampleBuffer.begin(), srcSampleDelay, 0.0f); + } /* Load the necessary samples from the given buffer(s). */ - if(!BufferListItem) + if(!BufferListItem) UNLIKELY { - const uint avail{minu(srcBufferSize, MaxResamplerEdge)}; - const uint tofill{maxu(srcBufferSize, MaxResamplerEdge)}; + const uint avail{std::min(srcBufferSize, MaxResamplerEdge)}; + const uint tofill{std::max(srcBufferSize, MaxResamplerEdge)}; + const auto srcbuf = resampleBuffer.first(tofill); /* When loading from a voice that ended prematurely, only take * the samples that get closest to 0 amplitude. This helps * certain sounds fade out better. */ - auto abs_lt = [](const float lhs, const float rhs) noexcept -> bool - { return std::abs(lhs) < std::abs(rhs); }; - auto srciter = std::min_element(resampleBuffer, resampleBuffer+avail, abs_lt); + auto srciter = std::min_element(srcbuf.begin(), srcbuf.begin()+ptrdiff_t(avail), + [](const float l, const float r) { return std::abs(l) < std::abs(r); }); - std::fill(srciter+1, resampleBuffer+tofill, *srciter); + std::fill(srciter+1, srcbuf.end(), *srciter); } - else + else if(mFlags.test(VoiceIsStatic)) { - size_t srcSampleDelay{0}; - if(intPos < 0) UNLIKELY - { - /* If the current position is negative, there's that many - * silent samples to load before using the buffer. - */ - srcSampleDelay = static_cast(-intPos); - if(srcSampleDelay >= srcBufferSize) - { - /* If the number of silent source samples exceeds the - * number to load, the output will be silent. - */ - std::fill_n(MixingSamples[chan]+samplesLoaded, dstBufferSize, 0.0f); - std::fill_n(resampleBuffer, srcBufferSize, 0.0f); - goto skip_resample; - } - - std::fill_n(resampleBuffer, srcSampleDelay, 0.0f); - } - const uint uintPos{static_cast(maxi(intPos, 0))}; - - if(mFlags.test(VoiceIsStatic)) - LoadBufferStatic(BufferListItem, BufferLoopItem, uintPos, mFmtType, chan, - mFrameStep, srcSampleDelay, srcBufferSize, al::to_address(resampleBuffer)); - else if(mFlags.test(VoiceIsCallback)) + const auto uintPos = static_cast(std::max(intPos, 0)); + const auto bufferSamples = resampleBuffer.subspan(srcSampleDelay, + srcBufferSize-srcSampleDelay); + LoadBufferStatic(BufferListItem, BufferLoopItem, uintPos, mFmtType, chan, + mFrameStep, bufferSamples); + } + else if(mFlags.test(VoiceIsCallback)) + { + const auto uintPos = static_cast(std::max(intPos, 0)); + const uint callbackBase{mCallbackBlockBase * mSamplesPerBlock}; + const size_t bufferOffset{uintPos - callbackBase}; + const size_t needSamples{bufferOffset + srcBufferSize - srcSampleDelay}; + const size_t needBlocks{(needSamples + mSamplesPerBlock-1) / mSamplesPerBlock}; + if(!mFlags.test(VoiceCallbackStopped) && needBlocks > mNumCallbackBlocks) { - const uint callbackBase{mCallbackBlockBase * mSamplesPerBlock}; - const size_t bufferOffset{uintPos - callbackBase}; - const size_t needSamples{bufferOffset + srcBufferSize - srcSampleDelay}; - const size_t needBlocks{(needSamples + mSamplesPerBlock-1) / mSamplesPerBlock}; - if(!mFlags.test(VoiceCallbackStopped) && needBlocks > mNumCallbackBlocks) + const size_t byteOffset{mNumCallbackBlocks*size_t{mBytesPerBlock}}; + const size_t needBytes{(needBlocks-mNumCallbackBlocks)*size_t{mBytesPerBlock}}; + + const int gotBytes{BufferListItem->mCallback(BufferListItem->mUserData, + &BufferListItem->mSamples[byteOffset], static_cast(needBytes))}; + if(gotBytes < 0) + mFlags.set(VoiceCallbackStopped); + else if(static_cast(gotBytes) < needBytes) { - const size_t byteOffset{mNumCallbackBlocks*mBytesPerBlock}; - const size_t needBytes{(needBlocks-mNumCallbackBlocks)*mBytesPerBlock}; - - const int gotBytes{BufferListItem->mCallback(BufferListItem->mUserData, - &BufferListItem->mSamples[byteOffset], static_cast(needBytes))}; - if(gotBytes < 0) - mFlags.set(VoiceCallbackStopped); - else if(static_cast(gotBytes) < needBytes) - { - mFlags.set(VoiceCallbackStopped); - mNumCallbackBlocks += static_cast(gotBytes) / mBytesPerBlock; - } - else - mNumCallbackBlocks = static_cast(needBlocks); + mFlags.set(VoiceCallbackStopped); + mNumCallbackBlocks += static_cast(gotBytes) / mBytesPerBlock; } - const size_t numSamples{uint{mNumCallbackBlocks} * mSamplesPerBlock}; - LoadBufferCallback(BufferListItem, bufferOffset, numSamples, mFmtType, chan, - mFrameStep, srcSampleDelay, srcBufferSize, al::to_address(resampleBuffer)); + else + mNumCallbackBlocks = static_cast(needBlocks); } - else - LoadBufferQueue(BufferListItem, BufferLoopItem, uintPos, mFmtType, chan, - mFrameStep, srcSampleDelay, srcBufferSize, al::to_address(resampleBuffer)); + const size_t numSamples{size_t{mNumCallbackBlocks} * mSamplesPerBlock}; + const auto bufferSamples = resampleBuffer.subspan(srcSampleDelay, + srcBufferSize-srcSampleDelay); + LoadBufferCallback(BufferListItem, bufferOffset, numSamples, mFmtType, chan, + mFrameStep, bufferSamples); + } + else + { + const auto uintPos = static_cast(std::max(intPos, 0)); + const auto bufferSamples = resampleBuffer.subspan(srcSampleDelay, + srcBufferSize-srcSampleDelay); + LoadBufferQueue(BufferListItem, BufferLoopItem, uintPos, mFmtType, chan, + mFrameStep, bufferSamples); } - Resample(&mResampleState, al::to_address(resampleBuffer), fracPos, increment, - {MixingSamples[chan]+samplesLoaded, dstBufferSize}); + /* If there's a matching sample step and no phase offset, use a + * simple copy for resampling. + */ + if(increment == MixerFracOne && fracPos == 0) + std::copy_n(resampleBuffer.cbegin(), dstBufferSize, + MixingSamples[chan]+samplesLoaded); + else + mResampler(&mResampleState, Device->mResampleData, fracPos, increment, + {MixingSamples[chan]+samplesLoaded, dstBufferSize}); /* Store the last source samples used for next time. */ if(vstate == Playing) LIKELY @@ -940,7 +968,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi { const size_t dstOffset{samplesToMix - samplesLoaded}; const size_t srcOffset{(dstOffset*increment + fracPos) >> MixerFracBits}; - std::copy_n(resampleBuffer-MaxResamplerEdge+srcOffset, prevSamples.size(), + std::copy_n(Device->mResampleData.cbegin()+srcOffset, prevSamples.size(), prevSamples.begin()); } } @@ -952,18 +980,26 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi fracPos += dstBufferSize*increment; const uint srcOffset{fracPos >> MixerFracBits}; fracPos &= MixerFracMask; - intPos += srcOffset; + intPos += static_cast(srcOffset); /* If more samples need to be loaded, copy the back of the * resampleBuffer to the front to reuse it. prevSamples isn't * reliable since it's only updated for the end of the mix. */ - std::copy(resampleBuffer-MaxResamplerEdge+srcOffset, - resampleBuffer+MaxResamplerEdge+srcOffset, resampleBuffer-MaxResamplerEdge); + std::copy_n(Device->mResampleData.cbegin()+srcOffset, MaxResamplerPadding, + Device->mResampleData.begin()); } } } - for(auto &samples : MixingSamples.subspan(realChannels)) + if(mFmtChannels == FmtMonoDup) + { + /* NOTE: a mono source shouldn't have a decoder or the VoiceIsAmbisonic + * flag, so aliasing instead of copying to the second channel shouldn't + * be a problem. + */ + MixingSamples[1] = MixingSamples[0]; + } + else for(auto &samples : MixingSamples.subspan(realChannels)) std::fill_n(samples, samplesToLoad, 0.0f); if(mDecoder) @@ -980,7 +1016,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi } } - const uint Counter{mFlags.test(VoiceIsFading) ? minu(samplesToMix, 64u) : 0u}; + const uint Counter{mFlags.test(VoiceIsFading) ? std::min(samplesToMix, 64u) : 0u}; if(!Counter) { /* No fading, just overwrite the old/current params. */ @@ -1011,25 +1047,24 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi const al::span FilterBuf{Device->FilteredData}; { DirectParams &parms = chandata.mDryParams; - const float *samples{DoFilters(parms.LowPass, parms.HighPass, FilterBuf.data(), - {*voiceSamples, samplesToMix}, mDirect.FilterType)}; + const auto samples = DoFilters(parms.LowPass, parms.HighPass, FilterBuf, + {*voiceSamples, samplesToMix}, mDirect.FilterType); if(mFlags.test(VoiceHasHrtf)) { - const float TargetGain{parms.Hrtf.Target.Gain * (vstate == Playing)}; - DoHrtfMix(samples, samplesToMix, parms, TargetGain, Counter, OutPos, - (vstate == Playing), Device); + const float TargetGain{parms.Hrtf.Target.Gain * float(vstate == Playing)}; + DoHrtfMix(samples, parms, TargetGain, Counter, OutPos, (vstate == Playing), + Device); } else { - const float *TargetGains{(vstate == Playing) ? parms.Gains.Target.data() - : SilentTarget.data()}; + const auto TargetGains = (vstate == Playing) ? al::span{parms.Gains.Target} + : al::span{SilentTarget}; if(mFlags.test(VoiceHasNfc)) - DoNfcMix({samples, samplesToMix}, mDirect.Buffer.data(), parms, - TargetGains, Counter, OutPos, Device); + DoNfcMix(samples, mDirect.Buffer, parms, TargetGains, Counter, OutPos, Device); else - MixSamples({samples, samplesToMix}, mDirect.Buffer, - parms.Gains.Current.data(), TargetGains, Counter, OutPos); + MixSamples(samples, mDirect.Buffer, parms.Gains.Current, TargetGains, Counter, + OutPos); } } @@ -1039,13 +1074,13 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi continue; SendParams &parms = chandata.mWetParams[send]; - const float *samples{DoFilters(parms.LowPass, parms.HighPass, FilterBuf.data(), - {*voiceSamples, samplesToMix}, mSend[send].FilterType)}; + const auto samples = DoFilters(parms.LowPass, parms.HighPass, FilterBuf, + {*voiceSamples, samplesToMix}, mSend[send].FilterType); - const float *TargetGains{(vstate == Playing) ? parms.Gains.Target.data() - : SilentTarget.data()}; - MixSamples({samples, samplesToMix}, mSend[send].Buffer, - parms.Gains.Current.data(), TargetGains, Counter, OutPos); + const auto TargetGains = (vstate == Playing) ? al::span{parms.Gains.Target} + : al::span{SilentTarget}; + MixSamples(samples, mSend[send].Buffer, parms.Gains.Current, TargetGains, Counter, + OutPos); } ++voiceSamples; @@ -1062,12 +1097,11 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi /* Update voice positions and buffers as needed. */ DataPosFrac += increment*samplesToMix; - const uint SrcSamplesDone{DataPosFrac>>MixerFracBits}; - DataPosInt += SrcSamplesDone; + DataPosInt += static_cast(DataPosFrac>>MixerFracBits); DataPosFrac &= MixerFracMask; uint buffers_done{0u}; - if(BufferListItem && DataPosInt >= 0) LIKELY + if(BufferListItem && DataPosInt > 0) LIKELY { if(mFlags.test(VoiceIsStatic)) { @@ -1098,10 +1132,11 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi const uint blocksDone{currentBlock - mCallbackBlockBase}; if(blocksDone < mNumCallbackBlocks) { - const size_t byteOffset{blocksDone*mBytesPerBlock}; - const size_t byteEnd{mNumCallbackBlocks*mBytesPerBlock}; - std::byte *data{BufferListItem->mSamples}; - std::copy(data+byteOffset, data+byteEnd, data); + const size_t byteOffset{blocksDone*size_t{mBytesPerBlock}}; + const size_t byteEnd{mNumCallbackBlocks*size_t{mBytesPerBlock}}; + const al::span data{BufferListItem->mSamples}; + std::copy(data.cbegin()+ptrdiff_t(byteOffset), data.cbegin()+ptrdiff_t(byteEnd), + data.begin()); mNumCallbackBlocks -= blocksDone; mCallbackBlockBase += blocksDone; } @@ -1119,7 +1154,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi if(BufferListItem->mSampleLen > static_cast(DataPosInt)) break; - DataPosInt -= BufferListItem->mSampleLen; + DataPosInt -= static_cast(BufferListItem->mSampleLen); ++buffers_done; BufferListItem = BufferListItem->mNext.load(std::memory_order_relaxed); @@ -1148,9 +1183,9 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi { RingBuffer *ring{Context->mAsyncEvents.get()}; auto evt_vec = ring->getWriteVector(); - if(evt_vec.first.len > 0) + if(evt_vec[0].len > 0) { - auto &evt = InitAsyncEvent(evt_vec.first.buf); + auto &evt = InitAsyncEvent(evt_vec[0].buf); evt.mId = SourceID; evt.mCount = buffers_done; ring->writeAdvance(1); @@ -1173,22 +1208,23 @@ void Voice::prepare(DeviceBase *device) /* Even if storing really high order ambisonics, we only mix channels for * orders up to the device order. The rest are simply dropped. */ - uint num_channels{(mFmtChannels == FmtUHJ2 || mFmtChannels == FmtSuperStereo) ? 3 : - ChannelsFromFmt(mFmtChannels, minu(mAmbiOrder, device->mAmbiOrder))}; - if(num_channels > device->mSampleData.size()) UNLIKELY + uint num_channels{(mFmtChannels == FmtMonoDup) ? 2 + : (mFmtChannels == FmtUHJ2 || mFmtChannels == FmtSuperStereo) ? 3 + : ChannelsFromFmt(mFmtChannels, std::min(mAmbiOrder, device->mAmbiOrder))}; + if(num_channels > device->MixerChannelsMax) UNLIKELY { - ERR("Unexpected channel count: %u (limit: %zu, %d:%d)\n", num_channels, - device->mSampleData.size(), mFmtChannels, mAmbiOrder); - num_channels = static_cast(device->mSampleData.size()); + ERR("Unexpected channel count: %u (limit: %zu, %s : %d)\n", num_channels, + device->MixerChannelsMax, NameFromFormat(mFmtChannels), mAmbiOrder); + num_channels = device->MixerChannelsMax; } if(mChans.capacity() > 2 && num_channels < mChans.capacity()) { decltype(mChans){}.swap(mChans); decltype(mPrevSamples){}.swap(mPrevSamples); } - mChans.reserve(maxu(2, num_channels)); + mChans.reserve(std::max(2u, num_channels)); mChans.resize(num_channels); - mPrevSamples.reserve(maxu(2, num_channels)); + mPrevSamples.reserve(std::max(2u, num_channels)); mPrevSamples.resize(num_channels); mDecoder = nullptr; @@ -1272,8 +1308,10 @@ void Voice::prepare(DeviceBase *device) */ else if(mAmbiOrder && device->mAmbiOrder > mAmbiOrder) { - const uint8_t *OrderFromChan{Is2DAmbisonic(mFmtChannels) ? - AmbiIndex::OrderFrom2DChannel.data() : AmbiIndex::OrderFromChannel.data()}; + auto OrdersSpan = Is2DAmbisonic(mFmtChannels) + ? al::span{AmbiIndex::OrderFrom2DChannel} + : al::span{AmbiIndex::OrderFromChannel}; + auto OrderFromChan = OrdersSpan.cbegin(); const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder, device->m2DMixing); diff --git a/3rdparty/openal/core/voice.h b/3rdparty/openal/core/voice.h index a599eda8f8ec..9dbffdde36e2 100644 --- a/3rdparty/openal/core/voice.h +++ b/3rdparty/openal/core/voice.h @@ -10,7 +10,6 @@ #include #include -#include "almalloc.h" #include "alspan.h" #include "bufferline.h" #include "buffer_storage.h" @@ -20,6 +19,7 @@ #include "filters/splitter.h" #include "mixer/defs.h" #include "mixer/hrtfdefs.h" +#include "opthelpers.h" #include "resampler_limits.h" #include "uhjfilter.h" #include "vector.h" @@ -32,7 +32,7 @@ enum class DistanceModel : unsigned char; using uint = unsigned int; -#define MAX_SENDS 6 +inline constexpr size_t MaxSendCount{6}; enum class SpatializeMode : unsigned char { @@ -48,7 +48,7 @@ enum class DirectMode : unsigned char { }; -constexpr uint MaxPitch{10}; +inline constexpr uint MaxPitch{10}; enum { @@ -65,26 +65,29 @@ struct DirectParams { NfcFilter NFCtrlFilter; - struct { - HrtfFilter Old; - HrtfFilter Target; - alignas(16) std::array History; - } Hrtf; + struct HrtfParams { + HrtfFilter Old{}; + HrtfFilter Target{}; + alignas(16) std::array History{}; + }; + HrtfParams Hrtf; - struct { - std::array Current; - std::array Target; - } Gains; + struct GainParams { + std::array Current{}; + std::array Target{}; + }; + GainParams Gains; }; struct SendParams { BiquadFilter LowPass; BiquadFilter HighPass; - struct { - std::array Current; - std::array Target; - } Gains; + struct GainParams { + std::array Current{}; + std::array Target{}; + }; + GainParams Gains; }; @@ -99,7 +102,10 @@ struct VoiceBufferItem { uint mLoopStart{0u}; uint mLoopEnd{0u}; - std::byte *mSamples{nullptr}; + al::span mSamples; + +protected: + ~VoiceBufferItem() = default; }; @@ -138,15 +144,18 @@ struct VoiceProps { float Radius; float EnhWidth; + float Panning; /** Direct filter and auxiliary send info. */ - struct { + struct DirectData { float Gain; float GainHF; float HFReference; float GainLF; float LFReference; - } Direct; + }; + DirectData Direct; + struct SendData { EffectSlot *Slot; float Gain; @@ -154,13 +163,12 @@ struct VoiceProps { float HFReference; float GainLF; float LFReference; - } Send[MAX_SENDS]; + }; + std::array Send; }; struct VoicePropsItem : public VoiceProps { std::atomic next{nullptr}; - - DEF_NEWDEL(VoicePropsItem) }; enum : uint { @@ -175,7 +183,7 @@ enum : uint { VoiceFlagCount }; -struct Voice { +struct SIMDALIGN Voice { enum State { Stopped, Playing, @@ -185,7 +193,7 @@ struct Voice { std::atomic mUpdate{nullptr}; - VoiceProps mProps; + VoiceProps mProps{}; std::atomic mSourceID{0u}; std::atomic mPlayState{Stopped}; @@ -195,30 +203,30 @@ struct Voice { * Source offset in samples, relative to the currently playing buffer, NOT * the whole queue. */ - std::atomic mPosition; + std::atomic mPosition{}; /** Fractional (fixed-point) offset to the next sample. */ - std::atomic mPositionFrac; + std::atomic mPositionFrac{}; /* Current buffer queue item being played. */ - std::atomic mCurrentBuffer; + std::atomic mCurrentBuffer{}; /* Buffer queue item to loop to at end of queue (will be NULL for non- * looping voices). */ - std::atomic mLoopBuffer; + std::atomic mLoopBuffer{}; std::chrono::nanoseconds mStartTime{}; /* Properties for the attached buffer(s). */ - FmtChannels mFmtChannels; - FmtType mFmtType; - uint mFrequency; - uint mFrameStep; /**< In steps of the sample type size. */ - uint mBytesPerBlock; /**< Or for PCM formats, BytesPerFrame. */ - uint mSamplesPerBlock; /**< Always 1 for PCM formats. */ - AmbiLayout mAmbiLayout; - AmbiScaling mAmbiScaling; - uint mAmbiOrder; + FmtChannels mFmtChannels{}; + FmtType mFmtType{}; + uint mFrequency{}; + uint mFrameStep{}; /**< In steps of the sample type size. */ + uint mBytesPerBlock{}; /**< Or for PCM formats, BytesPerFrame. */ + uint mSamplesPerBlock{}; /**< Always 1 for PCM formats. */ + AmbiLayout mAmbiLayout{}; + AmbiScaling mAmbiScaling{}; + uint mAmbiOrder{}; std::unique_ptr mDecoder; uint mDecoderPadding{}; @@ -226,20 +234,20 @@ struct Voice { /** Current target parameters used for mixing. */ uint mStep{0}; - ResamplerFunc mResampler; + ResamplerFunc mResampler{}; InterpState mResampleState; - std::bitset mFlags{}; + std::bitset mFlags; uint mNumCallbackBlocks{0}; uint mCallbackBlockBase{0}; struct TargetData { - int FilterType; + int FilterType{}; al::span Buffer; }; TargetData mDirect; - std::array mSend; + std::array mSend; /* The first MaxResamplerPadding/2 elements are the sample history from the * previous mix, with an additional MaxResamplerPadding/2 elements that are @@ -250,11 +258,11 @@ struct Voice { al::vector mPrevSamples{2}; struct ChannelData { - float mAmbiHFScale, mAmbiLFScale; + float mAmbiHFScale{}, mAmbiLFScale{}; BandSplitter mAmbiSplitter; DirectParams mDryParams; - std::array mWetParams; + std::array mWetParams; }; al::vector mChans{2}; @@ -269,11 +277,9 @@ struct Voice { void prepare(DeviceBase *device); - static void InitMixer(std::optional resampler); - - DEF_NEWDEL(Voice) + static void InitMixer(std::optional resopt); }; -extern Resampler ResamplerDefault; +inline Resampler ResamplerDefault{Resampler::Gaussian}; #endif /* CORE_VOICE_H */ diff --git a/3rdparty/openal/core/voice_change.h b/3rdparty/openal/core/voice_change.h index ddc6186f5da2..e97c48f33110 100644 --- a/3rdparty/openal/core/voice_change.h +++ b/3rdparty/openal/core/voice_change.h @@ -3,8 +3,6 @@ #include -#include "almalloc.h" - struct Voice; using uint = unsigned int; @@ -24,8 +22,6 @@ struct VoiceChange { VChangeState mState{}; std::atomic mNext{nullptr}; - - DEF_NEWDEL(VoiceChange) }; #endif /* VOICE_CHANGE_H */ diff --git a/3rdparty/openal/include/AL/al.h b/3rdparty/openal/include/AL/al.h index 87274184b2db..a4e3ad5159f1 100644 --- a/3rdparty/openal/include/AL/al.h +++ b/3rdparty/openal/include/AL/al.h @@ -1,12 +1,23 @@ #ifndef AL_AL_H #define AL_AL_H +/* NOLINTBEGIN */ #ifdef __cplusplus extern "C" { +#ifdef _MSVC_LANG +#define AL_CPLUSPLUS _MSVC_LANG +#else +#define AL_CPLUSPLUS __cplusplus +#endif + #ifndef AL_DISABLE_NOEXCEPT +#if AL_CPLUSPLUS >= 201103L #define AL_API_NOEXCEPT noexcept -#if __cplusplus >= 201703L +#else +#define AL_API_NOEXCEPT +#endif +#if AL_CPLUSPLUS >= 201703L #define AL_API_NOEXCEPT17 noexcept #else #define AL_API_NOEXCEPT17 @@ -18,6 +29,8 @@ extern "C" { #define AL_API_NOEXCEPT17 #endif +#undef AL_CPLUSPLUS + #else /* __cplusplus */ #define AL_API_NOEXCEPT @@ -689,5 +702,6 @@ typedef void (AL_APIENTRY *LPALDISTANCEMODEL)(ALenum distanceModel) AL_ #ifdef __cplusplus } /* extern "C" */ #endif +/* NOLINTEND */ #endif /* AL_AL_H */ diff --git a/3rdparty/openal/include/AL/alc.h b/3rdparty/openal/include/AL/alc.h index 73dcf08f043d..d048ca04dbd0 100644 --- a/3rdparty/openal/include/AL/alc.h +++ b/3rdparty/openal/include/AL/alc.h @@ -1,12 +1,23 @@ #ifndef AL_ALC_H #define AL_ALC_H +/* NOLINTBEGIN */ #ifdef __cplusplus extern "C" { +#ifdef _MSVC_LANG +#define ALC_CPLUSPLUS _MSVC_LANG +#else +#define ALC_CPLUSPLUS __cplusplus +#endif + #ifndef AL_DISABLE_NOEXCEPT +#if ALC_CPLUSPLUS >= 201103L #define ALC_API_NOEXCEPT noexcept -#if __cplusplus >= 201703L +#else +#define ALC_API_NOEXCEPT +#endif +#if ALC_CPLUSPLUS >= 201703L #define ALC_API_NOEXCEPT17 noexcept #else #define ALC_API_NOEXCEPT17 @@ -18,6 +29,8 @@ extern "C" { #define ALC_API_NOEXCEPT17 #endif +#undef ALC_CPLUSPLUS + #else /* __cplusplus */ #define ALC_API_NOEXCEPT @@ -289,5 +302,6 @@ typedef void (ALC_APIENTRY *LPALCCAPTURESAMPLES)(ALCdevice *device, AL #ifdef __cplusplus } /* extern "C" */ #endif +/* NOLINTEND */ #endif /* AL_ALC_H */ diff --git a/3rdparty/openal/include/AL/alext.h b/3rdparty/openal/include/AL/alext.h index b99d6aacb83b..3c479e0bfbbb 100644 --- a/3rdparty/openal/include/AL/alext.h +++ b/3rdparty/openal/include/AL/alext.h @@ -1,6 +1,7 @@ #ifndef AL_ALEXT_H #define AL_ALEXT_H +/* NOLINTBEGIN */ #include /* Define int64 and uint64 types */ #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \ @@ -25,6 +26,8 @@ typedef uint64_t _alsoft_uint64_t; extern "C" { #endif +struct _GUID; + #ifndef AL_LOKI_IMA_ADPCM_format #define AL_LOKI_IMA_ADPCM_format 1 #define AL_FORMAT_IMA_ADPCM_MONO16_EXT 0x10000 @@ -701,15 +704,19 @@ 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; +typedef void* (AL_APIENTRY*LPALGETPOINTEREXT)(ALenum pname) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETPOINTERVEXT)(ALenum pname, void **values) 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 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; +void* AL_APIENTRY alGetPointerEXT(ALenum pname) AL_API_NOEXCEPT; +void AL_APIENTRY alGetPointervEXT(ALenum pname, void **values) AL_API_NOEXCEPT; #endif #endif @@ -720,18 +727,361 @@ void AL_APIENTRY alGetObjectLabelEXT(ALenum identifier, ALuint name, ALsizei buf #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 +#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 *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 *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 *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; +typedef void* (AL_APIENTRY*LPALGETPOINTERDIRECTEXT)(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETPOINTERVDIRECTEXT)(ALCcontext *context, ALenum pname, void **values) 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 source_id, ALvoid *value, ALuint value_size) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPEAXGETDIRECT)(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint source_id, ALvoid *value, ALuint 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* ALC_APIENTRY alcGetProcAddress2(ALCdevice *device, const ALCchar *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 alGetPointerDirectEXT(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT; +void AL_APIENTRY alGetPointervDirectEXT(ALCcontext *context, ALenum pname, void **values) 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 source_id, ALvoid *value, ALuint value_size) AL_API_NOEXCEPT; +ALenum AL_APIENTRY EAXGetDirect(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint source_id, ALvoid *value, ALuint 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 + #ifdef __cplusplus } #endif +/* NOLINTEND */ #endif diff --git a/3rdparty/openal/include/AL/efx-presets.h b/3rdparty/openal/include/AL/efx-presets.h index 8539fd51789f..acd5bf398522 100644 --- a/3rdparty/openal/include/AL/efx-presets.h +++ b/3rdparty/openal/include/AL/efx-presets.h @@ -2,6 +2,7 @@ #ifndef EFX_PRESETS_H #define EFX_PRESETS_H +/* NOLINTBEGIN */ #ifndef EFXEAXREVERBPROPERTIES_DEFINED #define EFXEAXREVERBPROPERTIES_DEFINED @@ -399,4 +400,5 @@ typedef struct { #define EFX_REVERB_PRESET_SMALLWATERROOM \ { 1.0000f, 0.7000f, 0.3162f, 0.4477f, 1.0000f, 1.5100f, 1.2500f, 1.1400f, 0.8913f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1790f, 0.1500f, 0.8950f, 0.1900f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } +/* NOLINTEND */ #endif /* EFX_PRESETS_H */ diff --git a/3rdparty/openal/include/AL/efx.h b/3rdparty/openal/include/AL/efx.h index f24222c353db..1e93bf2222b4 100644 --- a/3rdparty/openal/include/AL/efx.h +++ b/3rdparty/openal/include/AL/efx.h @@ -1,6 +1,7 @@ #ifndef AL_EFX_H #define AL_EFX_H +/* NOLINTBEGIN */ #include #include "alc.h" @@ -758,5 +759,6 @@ AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum par #ifdef __cplusplus } /* extern "C" */ #endif +/* NOLINTEND */ #endif /* AL_EFX_H */