Kaynağa Gözat

Merge pull request #1292 from Azaezel/alpha41/openALUpgrade

open al upgrade
Brian Roberts 1 yıl önce
ebeveyn
işleme
7db28feb67
100 değiştirilmiş dosya ile 9802 ekleme ve 9331 silme
  1. 80 36
      Engine/lib/CMakeLists.txt
  2. 41 5
      Engine/lib/openal-soft/.github/workflows/ci.yml
  3. 1 1
      Engine/lib/openal-soft/.github/workflows/makemhr.yml
  4. 1 0
      Engine/lib/openal-soft/.gitignore
  5. 201 166
      Engine/lib/openal-soft/CMakeLists.txt
  6. 38 0
      Engine/lib/openal-soft/LICENSE-pffft
  7. 1 1
      Engine/lib/openal-soft/OpenALConfig.cmake.in
  8. 27 0
      Engine/lib/openal-soft/README.md
  9. 366 403
      Engine/lib/openal-soft/al/auxeffectslot.cpp
  10. 59 41
      Engine/lib/openal-soft/al/auxeffectslot.h
  11. 410 428
      Engine/lib/openal-soft/al/buffer.cpp
  12. 27 7
      Engine/lib/openal-soft/al/buffer.h
  13. 618 0
      Engine/lib/openal-soft/al/debug.cpp
  14. 70 0
      Engine/lib/openal-soft/al/debug.h
  15. 127 0
      Engine/lib/openal-soft/al/direct_defs.h
  16. 44 15
      Engine/lib/openal-soft/al/eax/api.h
  17. 1 2
      Engine/lib/openal-soft/al/eax/call.cpp
  18. 17 17
      Engine/lib/openal-soft/al/eax/call.h
  19. 182 143
      Engine/lib/openal-soft/al/eax/effect.h
  20. 10 37
      Engine/lib/openal-soft/al/eax/exception.cpp
  21. 10 5
      Engine/lib/openal-soft/al/eax/exception.h
  22. 3 4
      Engine/lib/openal-soft/al/eax/fx_slot_index.h
  23. 4 10
      Engine/lib/openal-soft/al/eax/fx_slots.h
  24. 0 21
      Engine/lib/openal-soft/al/eax/globals.cpp
  25. 2 18
      Engine/lib/openal-soft/al/eax/globals.h
  26. 4 4
      Engine/lib/openal-soft/al/eax/utils.cpp
  27. 6 6
      Engine/lib/openal-soft/al/eax/utils.h
  28. 2 10
      Engine/lib/openal-soft/al/eax/x_ram.h
  29. 336 367
      Engine/lib/openal-soft/al/effect.cpp
  30. 40 12
      Engine/lib/openal-soft/al/effect.h
  31. 72 84
      Engine/lib/openal-soft/al/effects/autowah.cpp
  32. 108 186
      Engine/lib/openal-soft/al/effects/chorus.cpp
  33. 35 44
      Engine/lib/openal-soft/al/effects/compressor.cpp
  34. 54 30
      Engine/lib/openal-soft/al/effects/convolution.cpp
  35. 72 22
      Engine/lib/openal-soft/al/effects/dedicated.cpp
  36. 73 87
      Engine/lib/openal-soft/al/effects/distortion.cpp
  37. 73 87
      Engine/lib/openal-soft/al/effects/echo.cpp
  38. 0 6
      Engine/lib/openal-soft/al/effects/effects.cpp
  39. 40 62
      Engine/lib/openal-soft/al/effects/effects.h
  40. 108 142
      Engine/lib/openal-soft/al/effects/equalizer.cpp
  41. 77 74
      Engine/lib/openal-soft/al/effects/fshifter.cpp
  42. 80 87
      Engine/lib/openal-soft/al/effects/modulator.cpp
  43. 29 35
      Engine/lib/openal-soft/al/effects/null.cpp
  44. 49 57
      Engine/lib/openal-soft/al/effects/pshifter.cpp
  45. 267 391
      Engine/lib/openal-soft/al/effects/reverb.cpp
  46. 92 156
      Engine/lib/openal-soft/al/effects/vmorpher.cpp
  47. 76 25
      Engine/lib/openal-soft/al/error.cpp
  48. 27 0
      Engine/lib/openal-soft/al/error.h
  49. 114 89
      Engine/lib/openal-soft/al/event.cpp
  50. 24 28
      Engine/lib/openal-soft/al/extension.cpp
  51. 394 486
      Engine/lib/openal-soft/al/filter.cpp
  52. 50 26
      Engine/lib/openal-soft/al/filter.h
  53. 203 245
      Engine/lib/openal-soft/al/listener.cpp
  54. 1 3
      Engine/lib/openal-soft/al/listener.h
  55. 969 1211
      Engine/lib/openal-soft/al/source.cpp
  56. 110 83
      Engine/lib/openal-soft/al/source.h
  57. 330 666
      Engine/lib/openal-soft/al/state.cpp
  58. 115 776
      Engine/lib/openal-soft/alc/alc.cpp
  59. 202 193
      Engine/lib/openal-soft/alc/alconfig.cpp
  60. 14 7
      Engine/lib/openal-soft/alc/alconfig.h
  61. 373 252
      Engine/lib/openal-soft/alc/alu.cpp
  62. 5 5
      Engine/lib/openal-soft/alc/alu.h
  63. 181 143
      Engine/lib/openal-soft/alc/backends/alsa.cpp
  64. 5 5
      Engine/lib/openal-soft/alc/backends/alsa.h
  65. 45 19
      Engine/lib/openal-soft/alc/backends/base.cpp
  66. 31 26
      Engine/lib/openal-soft/alc/backends/base.h
  67. 182 88
      Engine/lib/openal-soft/alc/backends/coreaudio.cpp
  68. 7 5
      Engine/lib/openal-soft/alc/backends/coreaudio.h
  69. 92 107
      Engine/lib/openal-soft/alc/backends/dsound.cpp
  70. 5 5
      Engine/lib/openal-soft/alc/backends/dsound.h
  71. 151 143
      Engine/lib/openal-soft/alc/backends/jack.cpp
  72. 5 5
      Engine/lib/openal-soft/alc/backends/jack.h
  73. 4 6
      Engine/lib/openal-soft/alc/backends/loopback.cpp
  74. 5 5
      Engine/lib/openal-soft/alc/backends/loopback.h
  75. 17 19
      Engine/lib/openal-soft/alc/backends/null.cpp
  76. 5 5
      Engine/lib/openal-soft/alc/backends/null.h
  77. 45 32
      Engine/lib/openal-soft/alc/backends/oboe.cpp
  78. 5 5
      Engine/lib/openal-soft/alc/backends/oboe.h
  79. 45 114
      Engine/lib/openal-soft/alc/backends/opensl.cpp
  80. 5 5
      Engine/lib/openal-soft/alc/backends/opensl.h
  81. 132 115
      Engine/lib/openal-soft/alc/backends/oss.cpp
  82. 5 5
      Engine/lib/openal-soft/alc/backends/oss.h
  83. 386 223
      Engine/lib/openal-soft/alc/backends/pipewire.cpp
  84. 9 5
      Engine/lib/openal-soft/alc/backends/pipewire.h
  85. 185 91
      Engine/lib/openal-soft/alc/backends/portaudio.cpp
  86. 5 5
      Engine/lib/openal-soft/alc/backends/portaudio.h
  87. 254 226
      Engine/lib/openal-soft/alc/backends/pulseaudio.cpp
  88. 13 5
      Engine/lib/openal-soft/alc/backends/pulseaudio.h
  89. 41 23
      Engine/lib/openal-soft/alc/backends/sdl2.cpp
  90. 5 5
      Engine/lib/openal-soft/alc/backends/sdl2.h
  91. 120 119
      Engine/lib/openal-soft/alc/backends/sndio.cpp
  92. 5 5
      Engine/lib/openal-soft/alc/backends/sndio.h
  93. 29 34
      Engine/lib/openal-soft/alc/backends/solaris.cpp
  94. 5 5
      Engine/lib/openal-soft/alc/backends/solaris.h
  95. 632 136
      Engine/lib/openal-soft/alc/backends/wasapi.cpp
  96. 7 5
      Engine/lib/openal-soft/alc/backends/wasapi.h
  97. 83 85
      Engine/lib/openal-soft/alc/backends/wave.cpp
  98. 5 5
      Engine/lib/openal-soft/alc/backends/wave.h
  99. 77 88
      Engine/lib/openal-soft/alc/backends/winmm.cpp
  100. 5 5
      Engine/lib/openal-soft/alc/backends/winmm.h

+ 80 - 36
Engine/lib/CMakeLists.txt

@@ -108,7 +108,7 @@ mark_as_advanced(SDL_WAYLAND)
 mark_as_advanced(SDL_WERROR)
 mark_as_advanced(SDL_X11)
 mark_as_advanced(SDL_XINPUT)
-
+mark_as_advanced(SDL2_DIR)
 add_subdirectory(sdl ${TORQUE_LIB_TARG_DIRECTORY}/sdl2 EXCLUDE_FROM_ALL)
 
 add_subdirectory(nativeFileDialogs ${TORQUE_LIB_TARG_DIRECTORY}/nfd EXCLUDE_FROM_ALL)
@@ -199,64 +199,59 @@ add_subdirectory(glad ${TORQUE_LIB_TARG_DIRECTORY}/glad EXCLUDE_FROM_ALL)
 if(TORQUE_SFX_OPENAL)
     advanced_option(TORQUE_OGGVORBIS "Enable OGG Vorbis" ON)
     advanced_option(ALSOFT_EAX "Enable legacy EAX extensions" ${WIN32})
-    advanced_option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" ON)
-    advanced_option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" ON)
+    advanced_option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" OFF)
+    advanced_option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" OFF)
     advanced_option(ALSOFT_UPDATE_BUILD_VERSION "Update git build version info" ON)
-    mark_as_advanced(ALSOFT_REQUIRE_OBOE)
-    mark_as_advanced(ALSOFT_REQUIRE_PIPEWIRE)
-    mark_as_advanced(ALSOFT_REQUIRE_RTKIT)
-    mark_as_advanced(ALSOFT_REQUIRE_SSE3)
-    #Hide some unnecessary fields as advanced
-    mark_as_advanced(ALSOFT_INSTALL_AMBDEC_PRESETS)
-    mark_as_advanced(ALSOFT_BACKEND_DSOUND)
-    mark_as_advanced(ALSOFT_BACKEND_MMDEVAPI)
+    mark_as_advanced(ALSOFT_BACKEND_COREAUDIO)
+    mark_as_advanced(ALSOFT_BACKEND_DSOUND)    
+    mark_as_advanced(ALSOFT_BACKEND_JACK)
+    mark_as_advanced(ALSOFT_BACKEND_OBOE)
+    mark_as_advanced(ALSOFT_BACKEND_OPENSL)
+    mark_as_advanced(ALSOFT_BACKEND_PIPEWIRE)
+    mark_as_advanced(ALSOFT_BACKEND_PORTAUDIO)
+    mark_as_advanced(ALSOFT_BACKEND_PULSEAUDIO)
+    mark_as_advanced(ALSOFT_BACKEND_SDL2)
+    mark_as_advanced(ALSOFT_BACKEND_WASAPI)
     mark_as_advanced(ALSOFT_BACKEND_WAVE)
     mark_as_advanced(ALSOFT_BACKEND_WINMM)
-    mark_as_advanced(ALSOFT_INSTALL_CONFIG)
+    mark_as_advanced(ALSOFT_BUILD_ROUTER)
+    mark_as_advanced(ALSOFT_CPUEXT_NEON)
     mark_as_advanced(ALSOFT_CPUEXT_SSE)
     mark_as_advanced(ALSOFT_CPUEXT_SSE2)
     mark_as_advanced(ALSOFT_CPUEXT_SSE3)
     mark_as_advanced(ALSOFT_CPUEXT_SSE4_1)
     mark_as_advanced(ALSOFT_DLOPEN)
-    mark_as_advanced(ALSOFT_EMBED_HRTF_DATA)
-    mark_as_advanced(ALSOFT_EXAMPLES)
-    mark_as_advanced(ALSOFT_INSTALL_HRTF_DATA)
+    mark_as_advanced(ALSOFT_EAX)
+    advanced_option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library footprint)" OFF)
+    advanced_option(ALSOFT_EXAMPLES  "Build example programs"  OFF)
     mark_as_advanced(ALSOFT_INSTALL)
+    advanced_option(ALSOFT_INSTALL_CONFIG "Install alsoft.conf sample configuration file" OFF)
+    advanced_option(ALSOFT_INSTALL_HRTF_DATA "Install HRTF data files" OFF)
+    advanced_option(ALSOFT_INSTALL_AMBDEC_PRESETS "Install AmbDec preset files" OFF)
+    advanced_option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" OFF)
+    advanced_option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" OFF)
+    mark_as_advanced(ALSOFT_UPDATE_BUILD_VERSION)
     mark_as_advanced(ALSOFT_NO_CONFIG_UTIL)
-    mark_as_advanced(ALSOFT_NO_UID_DEFS)
-    mark_as_advanced(ALSOFT_REQUIRE_ALSA)
     mark_as_advanced(ALSOFT_REQUIRE_COREAUDIO)
     mark_as_advanced(ALSOFT_REQUIRE_DSOUND)
     mark_as_advanced(ALSOFT_REQUIRE_JACK)
-    mark_as_advanced(ALSOFT_REQUIRE_MMDEVAPI)
     mark_as_advanced(ALSOFT_REQUIRE_NEON)
+    mark_as_advanced(ALSOFT_REQUIRE_OBOE)
     mark_as_advanced(ALSOFT_REQUIRE_OPENSL)
-    mark_as_advanced(ALSOFT_REQUIRE_OSS)
+    mark_as_advanced(ALSOFT_REQUIRE_PIPEWIRE)
     mark_as_advanced(ALSOFT_REQUIRE_PORTAUDIO)
     mark_as_advanced(ALSOFT_REQUIRE_PULSEAUDIO)
-    mark_as_advanced(ALSOFT_REQUIRE_QSA)
-    mark_as_advanced(ALSOFT_REQUIRE_SNDIO)
-    mark_as_advanced(ALSOFT_REQUIRE_SOLARIS)
+    mark_as_advanced(ALSOFT_REQUIRE_SDL2)
     mark_as_advanced(ALSOFT_REQUIRE_SSE)
     mark_as_advanced(ALSOFT_REQUIRE_SSE2)
+    mark_as_advanced(ALSOFT_REQUIRE_SSE3)
     mark_as_advanced(ALSOFT_REQUIRE_SSE4_1)
+    mark_as_advanced(ALSOFT_REQUIRE_WASAPI)
     mark_as_advanced(ALSOFT_REQUIRE_WINMM)
+    mark_as_advanced(ALSOFT_SEARCH_INSTALL_DATADIR)
     mark_as_advanced(ALSOFT_TESTS)
     mark_as_advanced(ALSOFT_UTILS)
     mark_as_advanced(ALSOFT_WERROR)
-    mark_as_advanced(COREAUDIO_FRAMEWORK)
-    mark_as_advanced(CMAKE_DEBUG_POSTFIX)
-    mark_as_advanced(FORCE_STATIC_VCRT)
-    mark_as_advanced(ALSOFT_BACKEND_WASAPI)
-    mark_as_advanced(ALSOFT_BUILD_ROUTER)
-    mark_as_advanced(ALSOFT_REQUIRE_SDL2)
-    mark_as_advanced(ALSOFT_REQUIRE_WASAPI)
-    mark_as_advanced(ALSOFT_BACKEND_COREAUDIO)
-    mark_as_advanced(ALSOFT_OSX_FRAMEWORK)
-    mark_as_advanced(ALSOFT_STATIC_LIBGCC)
-    mark_as_advanced(ALSOFT_STATIC_STDCXX)
-    mark_as_advanced(AUDIOTOOLBOX_LIBRARY)
-    mark_as_advanced(AUDIOUNIT_INCLUDE_DIR)
 endif()
 
 advanced_option(INSTALL_DOCS "" OFF)
@@ -273,9 +268,33 @@ add_subdirectory(flac ${TORQUE_LIB_TARG_DIRECTORY}/flac EXCLUDE_FROM_ALL)
 advanced_option(INSTALL_DOCS "" OFF)
 advanced_option(OPUS_INSTALL_PKG_CONFIG_MODULE "" OFF)
 advanced_option(OPUS_INSTALL_CMAKE_CONFIG_MODULE "" OFF)
+mark_as_advanced(OPUS_ASSERTIONS)
+mark_as_advanced(OPUS_BUILD_PROGRAMS)
+mark_as_advanced(OPUS_BUILD_SHARED_LIBRARY)
+mark_as_advanced(OPUS_BUILD_TESTING)
+mark_as_advanced(OPUS_CHECK_ASM)
+mark_as_advanced(OPUS_CUSTOM_MODES)
+mark_as_advanced(OPUS_DISABLE_INTRINSICS)
+mark_as_advanced(OPUS_ENABLE_FLOAT_API)
+mark_as_advanced(OPUS_FIXED_POINT)
+mark_as_advanced(OPUS_FLOAT_APPROX)
+mark_as_advanced(OPUS_FUZZING)
+mark_as_advanced(OPUS_HARDENING)
+mark_as_advanced(OPUS_STACK_PROTECTOR)
+mark_as_advanced(OPUS_USE_ALLOCA)
+mark_as_advanced(OPUS_X86_MAY_HAVE_AVX)
+mark_as_advanced(OPUS_X86_MAY_HAVE_SSE)
+mark_as_advanced(OPUS_X86_MAY_HAVE_SSE2)
+mark_as_advanced(OPUS_X86_MAY_HAVE_SSE4_1)
+mark_as_advanced(OPUS_X86_PRESUME_AVX)
+mark_as_advanced(OPUS_X86_PRESUME_SSE)
+mark_as_advanced(OPUS_X86_PRESUME_SSE2)
+mark_as_advanced(OPUS_X86_PRESUME_SSE4_1)
 add_subdirectory(opus ${TORQUE_LIB_TARG_DIRECTORY}/opus EXCLUDE_FROM_ALL)
 add_subdirectory(libtheora ${TORQUE_LIB_TARG_DIRECTORY}/libtheora EXCLUDE_FROM_ALL)
 
+mark_as_advanced(PULSEAUDIO_INCLUDE_DIR)
+mark_as_advanced(PULSEAUDIO_LIBRARY)
 advanced_option(BUILD_PROGRAMS "" OFF)
 advanced_option(BUILD_EXAMPLES "" OFF)
 advanced_option(ENABLE_CPACK "" OFF)
@@ -288,4 +307,29 @@ add_subdirectory(libsndfile ${TORQUE_LIB_TARG_DIRECTORY}/libsndfile EXCLUDE_FROM
 if(TORQUE_TESTING)
     add_subdirectory(gtest ${TORQUE_LIB_TARG_DIRECTORY}/gtest EXCLUDE_FROM_ALL)
 endif()
-#endif()
+#endif()
+
+#misc randoms
+mark_as_advanced(WINDRES)
+mark_as_advanced(AUDIOUNIT_INCLUDE_DIR)
+mark_as_advanced(CCACHE_BINARY)
+mark_as_advanced(COREAUDIO_FRAMEWORK)
+mark_as_advanced(FORCE_STATIC_VCRT)
+mark_as_advanced(HAVE_GIT)
+mark_as_advanced(LIBRARY_SUFFIX)
+mark_as_advanced(USE_STATIC_CRT)
+
+mark_as_advanced(BUILD_CXXLIBS)
+mark_as_advanced(BUILD_DOCS)
+mark_as_advanced(BUILD_REGTEST)
+mark_as_advanced(BUILD_UTILS)
+
+mark_as_advanced(ENABLE_64_BIT_WORDS)
+mark_as_advanced(ENABLE_BOW_DOCS)
+mark_as_advanced(ENABLE_EXTERNAL_LIBS)
+
+mark_as_advanced(WITH_ASM)
+mark_as_advanced(WITH_AVX)
+mark_as_advanced(WITH_FORTIFY_SOURCE)
+mark_as_advanced(WITH_OGG)
+mark_as_advanced(WITH_STACK_PROTECTOR)

+ 41 - 5
Engine/lib/openal-soft/.github/workflows/ci.yml

@@ -1,6 +1,6 @@
 name: CI
 
-on: [push]
+on: [push, pull_request]
 
 jobs:
   build:
@@ -14,6 +14,7 @@ jobs:
             name: "Win32-Release",
             os: windows-latest,
             cmake_opts: "-A Win32 \
+              -DALSOFT_TESTS=ON \
               -DALSOFT_BUILD_ROUTER=ON \
               -DALSOFT_REQUIRE_WINMM=ON \
               -DALSOFT_REQUIRE_DSOUND=ON \
@@ -24,6 +25,7 @@ jobs:
             name: "Win32-Debug",
             os: windows-latest,
             cmake_opts: "-A Win32 \
+              -DALSOFT_TESTS=ON \
               -DALSOFT_BUILD_ROUTER=ON \
               -DALSOFT_REQUIRE_WINMM=ON \
               -DALSOFT_REQUIRE_DSOUND=ON \
@@ -34,6 +36,7 @@ jobs:
             name: "Win64-Release",
             os: windows-latest,
             cmake_opts: "-A x64 \
+              -DALSOFT_TESTS=ON \
               -DALSOFT_BUILD_ROUTER=ON \
               -DALSOFT_REQUIRE_WINMM=ON \
               -DALSOFT_REQUIRE_DSOUND=ON \
@@ -44,16 +47,42 @@ jobs:
             name: "Win64-Debug",
             os: windows-latest,
             cmake_opts: "-A x64 \
+              -DALSOFT_TESTS=ON \
               -DALSOFT_BUILD_ROUTER=ON \
               -DALSOFT_REQUIRE_WINMM=ON \
               -DALSOFT_REQUIRE_DSOUND=ON \
               -DALSOFT_REQUIRE_WASAPI=ON",
             build_type: "Debug"
           }
+        - {
+            name: "Win64-UWP",
+            os: windows-latest,
+            cmake_opts: "-A x64 \
+              -DALSOFT_TESTS=OFF \
+              -DCMAKE_SYSTEM_NAME=WindowsStore \
+              \"-DCMAKE_SYSTEM_VERSION=10.0\" \
+              -DALSOFT_BUILD_ROUTER=ON \
+              -DALSOFT_REQUIRE_WASAPI=ON",
+            build_type: "Release"
+          }
         - {
             name: "macOS-Release",
             os: macos-latest,
-            cmake_opts: "-DALSOFT_REQUIRE_COREAUDIO=ON",
+            cmake_opts: "-DALSOFT_REQUIRE_COREAUDIO=ON \
+              -DALSOFT_TESTS=ON",
+            build_type: "Release"
+          }
+        - {
+            name: "iOS-Release",
+            os: macos-latest,
+            cmake_opts: "-GXcode \
+            -DCMAKE_SYSTEM_NAME=iOS \
+            -DALSOFT_REQUIRE_COREAUDIO=ON \
+            -DALSOFT_UTILS=OFF \
+            -DALSOFT_EXAMPLES=OFF \
+            -DALSOFT_TESTS=OFF \
+            -DALSOFT_INSTALL=OFF \
+            \"-DCMAKE_OSX_ARCHITECTURES=arm64\"",
             build_type: "Release"
           }
         - {
@@ -65,7 +94,8 @@ jobs:
               -DALSOFT_REQUIRE_PORTAUDIO=ON \
               -DALSOFT_REQUIRE_PULSEAUDIO=ON \
               -DALSOFT_REQUIRE_JACK=ON \
-              -DALSOFT_REQUIRE_PIPEWIRE=ON",
+              -DALSOFT_REQUIRE_PIPEWIRE=ON \
+              -DALSOFT_TESTS=ON",
             deps_cmdline: "sudo apt update && sudo apt-get install -qq \
               libpulse-dev \
               portaudio19-dev \
@@ -78,7 +108,7 @@ jobs:
           }
 
     steps:
-    - uses: actions/checkout@v1
+    - uses: actions/checkout@v3
 
     - name: Install Dependencies
       shell: bash
@@ -97,6 +127,12 @@ jobs:
       run: |
         cmake --build build --config ${{matrix.config.build_type}}
 
+    - name: Test
+      shell: bash
+      run: |
+        cd build
+        ctest
+
     - name: Create Archive
       if: ${{ matrix.config.os == 'windows-latest' }}
       shell: bash
@@ -109,7 +145,7 @@ jobs:
 
     - name: Upload Archive
       # Upload package as an artifact of this workflow.
-      uses: actions/[email protected].1
+      uses: actions/[email protected].2
       if: ${{ matrix.config.os == 'windows-latest' }}
       with:
         name: soft_oal-${{matrix.config.name}}

+ 1 - 1
Engine/lib/openal-soft/.github/workflows/makemhr.yml

@@ -55,7 +55,7 @@ jobs:
         copy "libmysofa/windows/third-party/zlib-1.2.11/bin/zlib.dll" "Artifacts/zlib.dll"
 
     - name: Upload makemhr artifact
-      uses: actions/[email protected].1
+      uses: actions/[email protected].2
       with:
         name: makemhr
         path: "Artifacts/"

+ 1 - 0
Engine/lib/openal-soft/.gitignore

@@ -1,6 +1,7 @@
 build*/
 winbuild
 win64build
+.vs/
 
 ## kdevelop
 *.kdev4

+ 201 - 166
Engine/lib/openal-soft/CMakeLists.txt

@@ -1,6 +1,7 @@
 # CMake build file list for OpenAL
 
-cmake_minimum_required(VERSION 3.0.2)
+cmake_minimum_required(VERSION 3.13)
+enable_testing()
 
 if(APPLE)
     # The workaround for try_compile failing with code signing
@@ -28,11 +29,10 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
                 FORCE)
         endif()
     endif()
+elseif(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
+    set(ALSOFT_UWP TRUE)
 endif()
 
-set(CMAKE_C_VISIBILITY_PRESET hidden)
-set(CMAKE_CXX_VISIBILITY_PRESET hidden)
-
 if(COMMAND CMAKE_POLICY)
     cmake_policy(SET CMP0003 NEW)
     cmake_policy(SET CMP0005 NEW)
@@ -76,8 +76,8 @@ if(NOT CMAKE_DEBUG_POSTFIX)
 endif()
 
 set(DEFAULT_TARGET_PROPS
-    # Require C++14.
-    CXX_STANDARD 14
+    # Require C++17.
+    CXX_STANDARD 17
     CXX_STANDARD_REQUIRED TRUE
     # Prefer C11, but support C99 and earlier when possible.
     C_STANDARD 11)
@@ -109,6 +109,7 @@ option(ALSOFT_UTILS "Build utility programs"  ON)
 option(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" OFF)
 
 option(ALSOFT_EXAMPLES  "Build example programs"  ON)
+option(ALSOFT_TESTS "Build test programs"  OFF)
 
 option(ALSOFT_INSTALL "Install main library" ON)
 option(ALSOFT_INSTALL_CONFIG "Install alsoft.conf sample configuration file" ON)
@@ -148,6 +149,8 @@ set(CPP_DEFS ) # C pre-processor, not C++
 set(INC_PATHS )
 set(C_FLAGS )
 set(LINKER_FLAGS )
+set(LINKER_FLAGS_DEBUG )
+set(LINKER_FLAGS_RELEASE )
 set(EXTRA_LIBS )
 
 if(WIN32)
@@ -165,7 +168,7 @@ elseif(APPLE)
 endif()
 
 
-# QNX's gcc do not uses /usr/include and /usr/lib pathes by default
+# QNX's gcc do not uses /usr/include and /usr/lib paths by default
 if("${CMAKE_C_PLATFORM_ID}" STREQUAL "QNX")
     set(INC_PATHS ${INC_PATHS} /usr/include)
     set(LINKER_FLAGS ${LINKER_FLAGS} -L/usr/lib)
@@ -186,29 +189,18 @@ set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0)
 set(EXPORT_DECL "")
 
 
-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=")
+# Some systems erroneously require the __STDC_FORMAT_MACROS macro to be defined
+# to get the fixed-width integer type formatter macros.
+check_cxx_source_compiles("#include <cinttypes>
+#include <cstdio>
+int main()
+{
+    int64_t i64{};
+    std::printf(\"%\" PRId64, i64);
+}"
+HAVE_STDC_FORMAT_MACROS)
+if(NOT HAVE_STDC_FORMAT_MACROS)
+    set(CPP_DEFS ${CPP_DEFS} __STDC_FORMAT_MACROS)
 endif()
 
 # Some systems may need libatomic for atomic functions to work
@@ -278,6 +270,11 @@ else()
         endif()
     endif()
 
+    check_cxx_compiler_flag(-Wno-interference-size HAVE_WNO_INTERFERENCE_SIZE)
+    if(HAVE_WNO_INTERFERENCE_SIZE)
+        set(C_FLAGS ${C_FLAGS} $<$<COMPILE_LANGUAGE:CXX>:-Wno-interference-size>)
+    endif()
+
     if(ALSOFT_WERROR)
         set(C_FLAGS ${C_FLAGS} -Werror)
     endif()
@@ -312,7 +309,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)
@@ -320,14 +317,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)
@@ -335,38 +332,38 @@ 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()
 
 # Set visibility/export options if available
-if(WIN32)
-    if(NOT LIBTYPE STREQUAL "STATIC")
+if(NOT LIBTYPE STREQUAL "STATIC")
+    if(WIN32)
         set(EXPORT_DECL "__declspec(dllexport)")
-    endif()
-else()
-    set(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
-    # Yes GCC, really don't accept visibility modes you don't support
-    set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Wattributes -Werror")
-
-    check_c_source_compiles("int foo() __attribute__((visibility(\"protected\")));
-                             int main() {return 0;}" HAVE_GCC_PROTECTED_VISIBILITY)
-    if(HAVE_GCC_PROTECTED_VISIBILITY)
-        if(NOT LIBTYPE STREQUAL "STATIC")
-            set(EXPORT_DECL "__attribute__((visibility(\"protected\")))")
-        endif()
     else()
-        check_c_source_compiles("int foo() __attribute__((visibility(\"default\")));
-                                 int main() {return 0;}" HAVE_GCC_DEFAULT_VISIBILITY)
-        if(HAVE_GCC_DEFAULT_VISIBILITY)
-            if(NOT LIBTYPE STREQUAL "STATIC")
+        set(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
+        # Yes GCC, really don't accept visibility modes you don't support
+        set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Wattributes -Werror")
+
+        check_c_source_compiles("int foo() __attribute__((visibility(\"protected\")));
+                                int main() {return 0;}" HAVE_GCC_PROTECTED_VISIBILITY)
+        if(HAVE_GCC_PROTECTED_VISIBILITY)
+            set(EXPORT_DECL "__attribute__((visibility(\"protected\")))")
+        else()
+            check_c_source_compiles("int foo() __attribute__((visibility(\"default\")));
+                                    int main() {return 0;}" HAVE_GCC_DEFAULT_VISIBILITY)
+            if(HAVE_GCC_DEFAULT_VISIBILITY)
                 set(EXPORT_DECL "__attribute__((visibility(\"default\")))")
             endif()
         endif()
-    endif()
+        if(HAVE_GCC_PROTECTED_VISIBILITY OR HAVE_GCC_DEFAULT_VISIBILITY)
+            set(CMAKE_C_VISIBILITY_PRESET hidden)
+            set(CMAKE_CXX_VISIBILITY_PRESET hidden)
+        endif()
 
-    set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}")
+        set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}")
+    endif()
 endif()
 
 
@@ -403,7 +400,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)
@@ -448,10 +445,11 @@ 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()
 
 
+set(ALSOFT_FORCE_ALIGN )
 set(SSE_FLAGS )
 set(FPMATH_SET "0")
 if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2)
@@ -476,6 +474,7 @@ if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2)
             # OSs don't guarantee this on 32-bit, so externally-callable
             # functions need to ensure an aligned stack.
             set(EXPORT_DECL "${EXPORT_DECL}__attribute__((force_align_arg_pointer))")
+            set(ALSOFT_FORCE_ALIGN "__attribute__((force_align_arg_pointer))")
         endif()
     endif()
 endif()
@@ -493,13 +492,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 )
@@ -517,7 +512,7 @@ if(HAVE_LIBRT)
     set(RT_LIB rt)
 endif()
 
-# Check for the dlopen API (for dynamicly loading backend libs)
+# Check for the dlopen API (for dynamically loading backend libs)
 if(ALSOFT_DLOPEN)
     check_include_file(dlfcn.h HAVE_DLFCN_H)
     check_library_exists(dl dlopen "" HAVE_LIBDL)
@@ -546,8 +541,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)
@@ -582,34 +575,36 @@ 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/albyte.h
     common/alcomplex.cpp
     common/alcomplex.h
-    common/aldeque.h
-    common/alfstream.cpp
-    common/alfstream.h
-    common/almalloc.cpp
     common/almalloc.h
     common/alnumbers.h
     common/alnumeric.h
-    common/aloptional.h
+    common/alsem.cpp
+    common/alsem.h
     common/alspan.h
     common/alstring.cpp
     common/alstring.h
+    common/althrd_setname.cpp
+    common/althrd_setname.h
+    common/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
@@ -618,8 +613,6 @@ set(COMMON_OBJS
     common/ringbuffer.h
     common/strutils.cpp
     common/strutils.h
-    common/threads.cpp
-    common/threads.h
     common/vecmat.h
     common/vector.h)
 
@@ -680,6 +673,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
@@ -726,7 +721,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
@@ -742,6 +737,9 @@ set(OPENAL_OBJS
     al/auxeffectslot.h
     al/buffer.cpp
     al/buffer.h
+    al/debug.cpp
+    al/debug.h
+    al/direct_defs.h
     al/effect.cpp
     al/effect.h
     al/effects/autowah.cpp
@@ -761,6 +759,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
@@ -798,6 +797,9 @@ set(ALC_OBJS
     alc/effects/pshifter.cpp
     alc/effects/reverb.cpp
     alc/effects/vmorpher.cpp
+    alc/events.cpp
+    alc/events.h
+    alc/export_list.h
     alc/inprogext.h
     alc/panning.cpp)
 
@@ -815,7 +817,6 @@ if(ALSOFT_EAX)
         al/eax/fx_slot_index.h
         al/eax/fx_slots.cpp
         al/eax/fx_slots.h
-        al/eax/globals.cpp
         al/eax/globals.h
         al/eax/utils.cpp
         al/eax/utils.h
@@ -899,7 +900,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
@@ -916,7 +917,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)
@@ -963,66 +964,72 @@ 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
 if(WIN32)
-    # Check MMSystem backend
-    option(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON)
-    option(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF)
-    if(ALSOFT_BACKEND_WINMM)
-        set(HAVE_WINMM 1)
-        set(BACKENDS "${BACKENDS} WinMM,")
-        set(ALC_OBJS ${ALC_OBJS} alc/backends/winmm.cpp alc/backends/winmm.h)
-        # There doesn't seem to be good way to search for winmm.lib for MSVC.
-        # find_library doesn't find it without being told to look in a specific
-        # place in the WindowsSDK, but it links anyway. If there ends up being
-        # Windows targets without this, another means to detect it is needed.
-        set(EXTRA_LIBS winmm ${EXTRA_LIBS})
-    endif()
-
-    # Check DSound backend
-    option(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON)
-    option(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF)
-    if(ALSOFT_BACKEND_DSOUND)
-        check_include_file(dsound.h HAVE_DSOUND_H)
-        if(DXSDK_DIR)
-            find_path(DSOUND_INCLUDE_DIR NAMES "dsound.h"
-                PATHS "${DXSDK_DIR}" PATH_SUFFIXES include
-                DOC "The DirectSound include directory")
+    if (NOT ALSOFT_UWP)
+        # Check MMSystem backend
+        option(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON)
+        option(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF)
+        if(ALSOFT_BACKEND_WINMM)
+            set(HAVE_WINMM 1)
+            set(BACKENDS "${BACKENDS} WinMM,")
+            set(ALC_OBJS ${ALC_OBJS} alc/backends/winmm.cpp alc/backends/winmm.h)
+            # There doesn't seem to be good way to search for winmm.lib for MSVC.
+            # find_library doesn't find it without being told to look in a specific
+            # place in the WindowsSDK, but it links anyway. If there ends up being
+            # Windows targets without this, another means to detect it is needed.
+            set(EXTRA_LIBS winmm ${EXTRA_LIBS})
         endif()
-        if(HAVE_DSOUND_H OR DSOUND_INCLUDE_DIR)
-            set(HAVE_DSOUND 1)
-            set(BACKENDS "${BACKENDS} DirectSound,")
-            set(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h)
 
-            if(NOT HAVE_DSOUND_H)
-                set(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIR})
+        # Check DSound backend
+        option(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON)
+        option(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF)
+        if(ALSOFT_BACKEND_DSOUND)
+            check_include_file(dsound.h HAVE_DSOUND_H)
+            if(DXSDK_DIR)
+                find_path(DSOUND_INCLUDE_DIR NAMES "dsound.h"
+                    PATHS "${DXSDK_DIR}" PATH_SUFFIXES include
+                    DOC "The DirectSound include directory")
+            endif()
+            if(HAVE_DSOUND_H OR DSOUND_INCLUDE_DIR)
+                set(HAVE_DSOUND 1)
+                set(BACKENDS "${BACKENDS} DirectSound,")
+                set(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h)
+
+                if(NOT HAVE_DSOUND_H)
+                    set(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIR})
+                endif()
             endif()
         endif()
     endif()
@@ -1040,13 +1047,13 @@ if(WIN32)
     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()
 
 # Check JACK backend
@@ -1063,7 +1070,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
@@ -1098,7 +1105,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
@@ -1130,7 +1137,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
@@ -1142,11 +1149,12 @@ if(ALSOFT_BACKEND_OPENSL)
         set(HAVE_OPENSL 1)
         set(ALC_OBJS  ${ALC_OBJS} alc/backends/opensl.cpp alc/backends/opensl.h)
         set(BACKENDS  "${BACKENDS} OpenSL,")
-        set(EXTRA_LIBS "OpenSL::OpenSLES" ${EXTRA_LIBS})
+        set(EXTRA_LIBS ${OPENSL_LIBRARIES} ${EXTRA_LIBS})
+        set(INC_PATHS ${INC_PATHS} ${OPENSL_INCLUDE_DIRS})
     endif()
 endif()
 if(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL)
-    message(FATAL_ERROR "Failed to enabled required OpenSL backend")
+    message(FATAL_ERROR "Failed to enable required OpenSL backend")
 endif()
 
 # Check PortAudio backend
@@ -1163,7 +1171,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
@@ -1181,7 +1189,7 @@ if(ALSOFT_BACKEND_SDL2)
     endif()
 endif()
 if(ALSOFT_REQUIRE_SDL2 AND NOT SDL2_FOUND)
-    message(FATAL_ERROR "Failed to enabled required SDL2 backend")
+    message(FATAL_ERROR "Failed to enable required SDL2 backend")
 endif()
 
 # Optionally enable the Wave Writer backend
@@ -1309,11 +1317,12 @@ configure_file(
     @ONLY)
 
 
-add_library(common STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS})
-target_include_directories(common PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/include)
-target_compile_definitions(common PRIVATE ${CPP_DEFS})
-target_compile_options(common PRIVATE ${C_FLAGS})
-set_target_properties(common PROPERTIES ${DEFAULT_TARGET_PROPS} POSITION_INDEPENDENT_CODE TRUE)
+add_library(alcommon STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS})
+target_include_directories(alcommon PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/include
+    PUBLIC ${OpenAL_SOURCE_DIR}/common)
+target_compile_definitions(alcommon PRIVATE ${CPP_DEFS})
+target_compile_options(alcommon PRIVATE ${C_FLAGS})
+set_target_properties(alcommon PROPERTIES ${DEFAULT_TARGET_PROPS} POSITION_INDEPENDENT_CODE TRUE)
 
 
 unset(HAS_ROUTER)
@@ -1348,7 +1357,7 @@ else()
             PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}"
             "AL_API=${EXPORT_DECL}" ${CPP_DEFS})
         target_compile_options(OpenAL PRIVATE ${C_FLAGS})
-        target_link_libraries(OpenAL PRIVATE common ${LINKER_FLAGS})
+        target_link_libraries(OpenAL PRIVATE alcommon ${LINKER_FLAGS})
         target_include_directories(OpenAL
           PUBLIC
             $<BUILD_INTERFACE:${OpenAL_SOURCE_DIR}/include>
@@ -1379,13 +1388,31 @@ else()
     if(WIN32)
         set_target_properties(${IMPL_TARGET} PROPERTIES PREFIX "")
     endif()
-    target_link_libraries(${IMPL_TARGET} PRIVATE common ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB})
+    target_link_libraries(${IMPL_TARGET} PRIVATE alcommon ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB})
+
+    if(ALSOFT_UWP)
+        set(ALSOFT_CPPWINRT_VERSION "2.0.230706.1" CACHE STRING "The soft-oal default cppwinrt version")
+        
+        find_program(NUGET_EXE NAMES nuget)
+        if(NOT NUGET_EXE)
+            message("NUGET.EXE not found.")
+            message(FATAL_ERROR "Please install this executable, and run CMake again.")
+        endif()
+
+        exec_program(${NUGET_EXE}
+            ARGS install "Microsoft.Windows.CppWinRT" -Version ${ALSOFT_CPPWINRT_VERSION} -ExcludeVersion -OutputDirectory "\"${CMAKE_BINARY_DIR}/packages\"")
+
+        set_target_properties(${IMPL_TARGET} PROPERTIES
+            VS_PROJECT_IMPORT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.props
+        )
+        target_link_libraries(${IMPL_TARGET} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.targets)
+    endif()
 
     if(NOT WIN32 AND NOT APPLE)
         # FIXME: This doesn't put a dependency on the version script. Changing
         # the version script will not cause a relink as it should.
-        set_property(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY
-            LINK_FLAGS " -Wl,--version-script=${OpenAL_SOURCE_DIR}/libopenal.version")
+        target_link_options(${IMPL_TARGET} PRIVATE
+            "-Wl,--version-script=${OpenAL_SOURCE_DIR}/libopenal.version")
     endif()
 
     if(APPLE AND ALSOFT_OSX_FRAMEWORK)
@@ -1443,8 +1470,8 @@ 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}"
-    ${CPP_DEFS})
+    PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES $<$<BOOL:${ALSOFT_EAX}>:ALSOFT_EAX>
+    "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}" ${CPP_DEFS})
 target_compile_options(${IMPL_TARGET} PRIVATE ${C_FLAGS})
 
 if(TARGET build_version)
@@ -1462,8 +1489,7 @@ if(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC"
             message(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation")
         endif()
     else()
-        set_property(TARGET OpenAL APPEND_STRING PROPERTY
-            LINK_FLAGS " -Wl,--output-def,OpenAL32.def")
+        target_link_options(OpenAL PRIVATE "-Wl,--output-def,${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
@@ -1494,7 +1520,10 @@ if(FPMATH_SET)
     message(STATUS "Building with SSE${FPMATH_SET} codegen")
     message(STATUS "")
 endif()
-
+if(ALSOFT_UWP)
+    message(STATUS "Building with UWP support")
+    message(STATUS "")
+endif()
 if(ALSOFT_EAX)
     message(STATUS "Building with legacy EAX extension support")
     message(STATUS "")
@@ -1580,7 +1609,7 @@ if(ALSOFT_UTILS)
         target_include_directories(uhjdecoder
             PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
         target_compile_options(uhjdecoder PRIVATE ${C_FLAGS})
-        target_link_libraries(uhjdecoder PUBLIC common
+        target_link_libraries(uhjdecoder PUBLIC alcommon
             PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG})
         set_target_properties(uhjdecoder PROPERTIES ${DEFAULT_TARGET_PROPS})
 
@@ -1589,7 +1618,7 @@ if(ALSOFT_UTILS)
         target_include_directories(uhjencoder
             PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
         target_compile_options(uhjencoder PRIVATE ${C_FLAGS})
-        target_link_libraries(uhjencoder PUBLIC common
+        target_link_libraries(uhjencoder PUBLIC alcommon
             PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG})
         set_target_properties(uhjencoder PROPERTIES ${DEFAULT_TARGET_PROPS})
     endif()
@@ -1602,7 +1631,7 @@ if(ALSOFT_UTILS)
         target_compile_definitions(sofa-support PRIVATE ${CPP_DEFS})
         target_include_directories(sofa-support PUBLIC ${OpenAL_SOURCE_DIR}/common)
         target_compile_options(sofa-support PRIVATE ${C_FLAGS})
-        target_link_libraries(sofa-support PUBLIC common MySOFA::MySOFA PRIVATE ${LINKER_FLAGS})
+        target_link_libraries(sofa-support PUBLIC alcommon MySOFA::MySOFA PRIVATE ${LINKER_FLAGS})
         set_target_properties(sofa-support PROPERTIES ${DEFAULT_TARGET_PROPS})
 
         set(MAKEMHR_SRCS
@@ -1612,9 +1641,6 @@ 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
@@ -1644,22 +1670,22 @@ endif()
 
 
 # Add a static library with common functions used by multiple example targets
-add_library(ex-common STATIC EXCLUDE_FROM_ALL
+add_library(al-excommon STATIC EXCLUDE_FROM_ALL
     examples/common/alhelpers.c
     examples/common/alhelpers.h)
-target_compile_definitions(ex-common PUBLIC ${CPP_DEFS})
-target_include_directories(ex-common PUBLIC ${OpenAL_SOURCE_DIR}/common)
-target_compile_options(ex-common PUBLIC ${C_FLAGS})
-target_link_libraries(ex-common PUBLIC OpenAL PRIVATE ${RT_LIB})
-set_target_properties(ex-common PROPERTIES ${DEFAULT_TARGET_PROPS})
+target_compile_definitions(al-excommon PUBLIC ${CPP_DEFS})
+target_include_directories(al-excommon PUBLIC ${OpenAL_SOURCE_DIR}/common)
+target_compile_options(al-excommon PUBLIC ${C_FLAGS})
+target_link_libraries(al-excommon PUBLIC OpenAL PRIVATE ${RT_LIB})
+set_target_properties(al-excommon PROPERTIES ${DEFAULT_TARGET_PROPS})
 
 if(ALSOFT_EXAMPLES)
     add_executable(altonegen examples/altonegen.c)
-    target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} ex-common ${UNICODE_FLAG})
+    target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} al-excommon ${UNICODE_FLAG})
     set_target_properties(altonegen PROPERTIES ${DEFAULT_TARGET_PROPS})
 
     add_executable(alrecord examples/alrecord.c)
-    target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} ex-common ${UNICODE_FLAG})
+    target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} al-excommon ${UNICODE_FLAG})
     set_target_properties(alrecord PROPERTIES ${DEFAULT_TARGET_PROPS})
 
     if(ALSOFT_INSTALL_EXAMPLES)
@@ -1670,48 +1696,53 @@ if(ALSOFT_EXAMPLES)
 
     if(SNDFILE_FOUND)
         add_executable(alplay examples/alplay.c)
-        target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
+        target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
             ${UNICODE_FLAG})
         set_target_properties(alplay PROPERTIES ${DEFAULT_TARGET_PROPS})
 
         add_executable(alstream examples/alstream.c)
-        target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
+        target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
             ${UNICODE_FLAG})
         set_target_properties(alstream PROPERTIES ${DEFAULT_TARGET_PROPS})
 
         add_executable(alreverb examples/alreverb.c)
-        target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
+        target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
             ${UNICODE_FLAG})
         set_target_properties(alreverb PROPERTIES ${DEFAULT_TARGET_PROPS})
 
         add_executable(almultireverb examples/almultireverb.c)
         target_link_libraries(almultireverb
-            PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${MATH_LIB} ${UNICODE_FLAG})
+            PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${MATH_LIB} ${UNICODE_FLAG})
         set_target_properties(almultireverb PROPERTIES ${DEFAULT_TARGET_PROPS})
 
         add_executable(allatency examples/allatency.c)
-        target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
+        target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
             ${UNICODE_FLAG})
         set_target_properties(allatency PROPERTIES ${DEFAULT_TARGET_PROPS})
 
         add_executable(alhrtf examples/alhrtf.c)
         target_link_libraries(alhrtf
-            PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${MATH_LIB} ${UNICODE_FLAG})
+            PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${MATH_LIB} ${UNICODE_FLAG})
         set_target_properties(alhrtf PROPERTIES ${DEFAULT_TARGET_PROPS})
 
         add_executable(alstreamcb examples/alstreamcb.cpp)
-        target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
+        target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
             ${UNICODE_FLAG})
         set_target_properties(alstreamcb PROPERTIES ${DEFAULT_TARGET_PROPS})
 
-        add_executable(alconvolve examples/alconvolve.c)
-        target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} common SndFile::SndFile ex-common
+        add_executable(aldirect examples/aldirect.cpp)
+        target_link_libraries(aldirect PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
             ${UNICODE_FLAG})
+        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})
         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")
@@ -1720,7 +1751,7 @@ if(ALSOFT_EXAMPLES)
     if(SDL2_FOUND)
         add_executable(alloopback examples/alloopback.c)
         target_link_libraries(alloopback
-            PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ex-common ${MATH_LIB})
+            PRIVATE ${LINKER_FLAGS} SDL2::SDL2 al-excommon ${MATH_LIB})
         set_target_properties(alloopback PROPERTIES ${DEFAULT_TARGET_PROPS})
 
         if(ALSOFT_INSTALL_EXAMPLES)
@@ -1757,7 +1788,7 @@ if(ALSOFT_EXAMPLES)
             add_executable(alffplay examples/alffplay.cpp)
             target_include_directories(alffplay PRIVATE ${FFMPEG_INCLUDE_DIRS})
             target_link_libraries(alffplay
-                PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ${FFMPEG_LIBRARIES} ex-common)
+                PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ${FFMPEG_LIBRARIES} al-excommon)
             set_target_properties(alffplay PROPERTIES ${DEFAULT_TARGET_PROPS})
 
             if(ALSOFT_INSTALL_EXAMPLES)
@@ -1769,6 +1800,10 @@ if(ALSOFT_EXAMPLES)
     message(STATUS "")
 endif()
 
+if (ALSOFT_TESTS)
+add_subdirectory(tests)
+endif()
+
 if(EXTRA_INSTALLS)
     install(TARGETS ${EXTRA_INSTALLS}
         RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}

+ 38 - 0
Engine/lib/openal-soft/LICENSE-pffft

@@ -0,0 +1,38 @@
+A modified PFFFT is included, with the following license.
+
+Copyright (c) 2023  Christopher Robinson
+
+Copyright (c) 2013  Julien Pommier ( [email protected] )
+
+Copyright (c) 2004 the University Corporation for Atmospheric
+Research ("UCAR"). All rights reserved. Developed by NCAR's
+Computational and Information Systems Laboratory, UCAR,
+www.cisl.ucar.edu.
+
+Redistribution and use of the Software in source and binary forms,
+with or without modification, is permitted provided that the
+following conditions are met:
+
+- Neither the names of NCAR's Computational and Information Systems
+Laboratory, the University Corporation for Atmospheric Research,
+nor the names of its sponsors or contributors may be used to
+endorse or promote products derived from this Software without
+specific prior written permission.
+
+- Redistributions of source code must retain the above copyright
+notices, this list of conditions, and the disclaimer below.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions, and the disclaimer below in the
+documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+SOFTWARE.

+ 1 - 1
Engine/lib/openal-soft/OpenALConfig.cmake.in

@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.1...3.18)
 
 include("${CMAKE_CURRENT_LIST_DIR}/OpenALTargets.cmake")
 

+ 27 - 0
Engine/lib/openal-soft/README.md

@@ -64,6 +64,33 @@ as application-agnostic behavior of the library. See alsoftrc.sample for
 available settings.
 
 
+Language Bindings
+-----------------
+
+As a C API, OpenAL Soft can be used directly by any language that can use
+functions with C linkage. For languages that can't directly use C-style
+headers, bindings may be developed to allow code written in that language to
+call into the library. Some bindings for some languages are listed here.
+
+C# Bindings:
+* [OpenTK](https://opentk.net/) includes low-level C# bindings for the OpenAL
+API, including some extensions. It also includes utility libraries for math and
+linear algebra, which can be useful for 3D calculations.
+
+Java Bindings:
+* [JOAL](https://jogamp.org/joal/www/), part of the JogAmp project, includes
+Java bindings for the OpenAL API, usable with OpenAL Soft. It also includes a
+higher level Sound3D Toolkit API and utility functions to make easier use of
+OpenAL features and capabilities.
+
+Python Bindings:
+* [PyOpenAL](https://pypi.org/project/PyOpenAL/). Also includes methods to play
+wave files and, with PyOgg, also Vorbis, Opus, and FLAC.
+
+Other bindings for these and other languages also exist. This list will grow as
+more bindings are found.
+
+
 Acknowledgements
 ----------------
 

Dosya farkı çok büyük olduğundan ihmal edildi
+ 366 - 403
Engine/lib/openal-soft/al/auxeffectslot.cpp


+ 59 - 41
Engine/lib/openal-soft/al/auxeffectslot.h

@@ -1,23 +1,24 @@
 #ifndef AL_AUXEFFECTSLOT_H
 #define AL_AUXEFFECTSLOT_H
 
+#include <array>
 #include <atomic>
-#include <cstddef>
+#include <cstdint>
+#include <string_view>
+#include <utility>
 
 #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
 #include <memory>
+#include "eax/api.h"
 #include "eax/call.h"
 #include "eax/effect.h"
 #include "eax/exception.h"
@@ -26,8 +27,6 @@
 #endif // ALSOFT_EAX
 
 struct ALbuffer;
-struct ALeffect;
-struct WetBuffer;
 
 #ifdef ALSOFT_EAX
 class EaxFxSlotException : public EaxException {
@@ -38,30 +37,30 @@ public:
 };
 #endif // ALSOFT_EAX
 
-enum class SlotState : ALenum {
-    Initial = AL_INITIAL,
-    Playing = AL_PLAYING,
-    Stopped = AL_STOPPED,
+enum class SlotState : bool {
+    Initial, Playing,
 };
 
 struct ALeffectslot {
+    ALuint EffectId{};
     float Gain{1.0f};
     bool  AuxSendAuto{true};
     ALeffectslot *Target{nullptr};
     ALbuffer *Buffer{nullptr};
 
-    struct {
+    struct EffectData {
         EffectSlotType Type{EffectSlotType::None};
         EffectProps Props{};
 
         al::intrusive_ptr<EffectState> State;
-    } Effect;
+    };
+    EffectData Effect;
 
     bool mPropsDirty{true};
 
     SlotState mState{SlotState::Initial};
 
-    RefCount ref{0u};
+    std::atomic<ALuint> ref{0u};
 
     EffectSlot *mSlot{nullptr};
 
@@ -73,23 +72,23 @@ struct ALeffectslot {
     ALeffectslot& operator=(const ALeffectslot&) = delete;
     ~ALeffectslot();
 
-    ALenum initEffect(ALenum effectType, const EffectProps &effectProps, ALCcontext *context);
-    void updateProps(ALCcontext *context);
+    ALenum initEffect(ALuint effectId, ALenum effectType, const EffectProps &effectProps,
+        ALCcontext *context);
+    void updateProps(ALCcontext *context) const;
 
-    /* This can be new'd for the context's default effect slot. */
-    DEF_NEWDEL(ALeffectslot)
+    static void SetName(ALCcontext *context, ALuint id, std::string_view name);
 
 
 #ifdef 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();
@@ -193,6 +192,17 @@ private:
         }
     };
 
+    struct Eax5FlagsValidator {
+        void operator()(unsigned long ulFlags) const
+        {
+            EaxRangeValidator{}(
+                "Flags",
+                ulFlags,
+                0UL,
+                ~EAX50FXSLOTFLAGS_RESERVED);
+        }
+    };
+
     struct Eax5OcclusionValidator {
         void operator()(long lOcclusion) const
         {
@@ -215,21 +225,13 @@ private:
         }
     };
 
-    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<const EAX40FXSLOTPROPERTIES&>(all));
+            Eax4GuidLoadEffectValidator{}(all.guidLoadEffect);
+            Eax4VolumeValidator{}(all.lVolume);
+            Eax4LockValidator{}(all.lLock);
+            Eax5FlagsValidator{}(all.ulFlags);
             Eax5OcclusionValidator{}(all.lOcclusion);
             Eax5OcclusionLfRatioValidator{}(all.flOcclusionLFRatio);
         }
@@ -277,14 +279,14 @@ private:
         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;
@@ -293,8 +295,8 @@ private:
     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);
@@ -307,7 +309,7 @@ private:
     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);
@@ -365,4 +367,20 @@ 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<std::array<ALeffectslot,64>*> 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

Dosya farkı çok büyük olduğundan ihmal edildi
+ 410 - 428
Engine/lib/openal-soft/al/buffer.cpp


+ 27 - 7
Engine/lib/openal-soft/al/buffer.h

@@ -1,20 +1,23 @@
 #ifndef AL_BUFFER_H
 #define AL_BUFFER_H
 
+#include <array>
 #include <atomic>
+#include <cstddef>
+#include <cstdint>
+#include <string_view>
+#include <utility>
 
 #include "AL/al.h"
+#include "AL/alc.h"
 
-#include "albyte.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"
-
 enum class EaxStorage : uint8_t {
     Automatic,
     Accessible,
@@ -26,7 +29,7 @@ enum class EaxStorage : uint8_t {
 struct ALbuffer : public BufferStorage {
     ALbitfieldSOFT Access{0u};
 
-    al::vector<al::byte,16> mDataStorage;
+    al::vector<std::byte,16> mDataStorage;
 
     ALuint OriginalSize{0};
 
@@ -42,12 +45,14 @@ 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<ALuint> ref{0u};
 
     /* Self ID */
     ALuint id{0};
 
-    DISABLE_ALLOC()
+    static void SetName(ALCcontext *context, ALuint id, std::string_view name);
+
+    DISABLE_ALLOC
 
 #ifdef ALSOFT_EAX
     EaxStorage eax_x_ram_mode{EaxStorage::Automatic};
@@ -55,4 +60,19 @@ struct ALbuffer : public BufferStorage {
 #endif // ALSOFT_EAX
 };
 
+struct BufferSubList {
+    uint64_t FreeMask{~0_u64};
+    gsl::owner<std::array<ALbuffer,64>*> 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

+ 618 - 0
Engine/lib/openal-soft/al/debug.cpp

@@ -0,0 +1,618 @@
+#include "config.h"
+
+#include "debug.h"
+
+#include <algorithm>
+#include <array>
+#include <atomic>
+#include <cstring>
+#include <deque>
+#include <mutex>
+#include <optional>
+#include <stdexcept>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+#include <utility>
+
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "AL/alext.h"
+
+#include "alc/context.h"
+#include "alc/device.h"
+#include "alc/inprogext.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");
+
+template<typename T, T ...Vals>
+constexpr auto make_array_sequence(std::integer_sequence<T, Vals...>)
+{ return std::array<T,sizeof...(Vals)>{Vals...}; }
+
+template<typename T, size_t N>
+constexpr auto make_array_sequence()
+{ return make_array_sequence(std::make_integer_sequence<T,N>{}); }
+
+
+constexpr auto GetDebugSource(ALenum source) noexcept -> std::optional<DebugSource>
+{
+    switch(source)
+    {
+    case AL_DEBUG_SOURCE_API_EXT: return DebugSource::API;
+    case AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT: return DebugSource::System;
+    case AL_DEBUG_SOURCE_THIRD_PARTY_EXT: return DebugSource::ThirdParty;
+    case AL_DEBUG_SOURCE_APPLICATION_EXT: return DebugSource::Application;
+    case AL_DEBUG_SOURCE_OTHER_EXT: return DebugSource::Other;
+    }
+    return std::nullopt;
+}
+
+constexpr auto GetDebugType(ALenum type) noexcept -> std::optional<DebugType>
+{
+    switch(type)
+    {
+    case AL_DEBUG_TYPE_ERROR_EXT: return DebugType::Error;
+    case AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT: return DebugType::DeprecatedBehavior;
+    case AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT: return DebugType::UndefinedBehavior;
+    case AL_DEBUG_TYPE_PORTABILITY_EXT: return DebugType::Portability;
+    case AL_DEBUG_TYPE_PERFORMANCE_EXT: return DebugType::Performance;
+    case AL_DEBUG_TYPE_MARKER_EXT: return DebugType::Marker;
+    case AL_DEBUG_TYPE_PUSH_GROUP_EXT: return DebugType::PushGroup;
+    case AL_DEBUG_TYPE_POP_GROUP_EXT: return DebugType::PopGroup;
+    case AL_DEBUG_TYPE_OTHER_EXT: return DebugType::Other;
+    }
+    return std::nullopt;
+}
+
+constexpr auto GetDebugSeverity(ALenum severity) noexcept -> std::optional<DebugSeverity>
+{
+    switch(severity)
+    {
+    case AL_DEBUG_SEVERITY_HIGH_EXT: return DebugSeverity::High;
+    case AL_DEBUG_SEVERITY_MEDIUM_EXT: return DebugSeverity::Medium;
+    case AL_DEBUG_SEVERITY_LOW_EXT: return DebugSeverity::Low;
+    case AL_DEBUG_SEVERITY_NOTIFICATION_EXT: return DebugSeverity::Notification;
+    }
+    return std::nullopt;
+}
+
+
+constexpr auto GetDebugSourceEnum(DebugSource source) -> ALenum
+{
+    switch(source)
+    {
+    case DebugSource::API: return AL_DEBUG_SOURCE_API_EXT;
+    case DebugSource::System: return AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT;
+    case DebugSource::ThirdParty: return AL_DEBUG_SOURCE_THIRD_PARTY_EXT;
+    case DebugSource::Application: return AL_DEBUG_SOURCE_APPLICATION_EXT;
+    case DebugSource::Other: return AL_DEBUG_SOURCE_OTHER_EXT;
+    }
+    throw std::runtime_error{"Unexpected debug source value "+std::to_string(al::to_underlying(source))};
+}
+
+constexpr auto GetDebugTypeEnum(DebugType type) -> ALenum
+{
+    switch(type)
+    {
+    case DebugType::Error: return AL_DEBUG_TYPE_ERROR_EXT;
+    case DebugType::DeprecatedBehavior: return AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT;
+    case DebugType::UndefinedBehavior: return AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT;
+    case DebugType::Portability: return AL_DEBUG_TYPE_PORTABILITY_EXT;
+    case DebugType::Performance: return AL_DEBUG_TYPE_PERFORMANCE_EXT;
+    case DebugType::Marker: return AL_DEBUG_TYPE_MARKER_EXT;
+    case DebugType::PushGroup: return AL_DEBUG_TYPE_PUSH_GROUP_EXT;
+    case DebugType::PopGroup: return AL_DEBUG_TYPE_POP_GROUP_EXT;
+    case DebugType::Other: return AL_DEBUG_TYPE_OTHER_EXT;
+    }
+    throw std::runtime_error{"Unexpected debug type value "+std::to_string(al::to_underlying(type))};
+}
+
+constexpr auto GetDebugSeverityEnum(DebugSeverity severity) -> ALenum
+{
+    switch(severity)
+    {
+    case DebugSeverity::High: return AL_DEBUG_SEVERITY_HIGH_EXT;
+    case DebugSeverity::Medium: return AL_DEBUG_SEVERITY_MEDIUM_EXT;
+    case DebugSeverity::Low: return AL_DEBUG_SEVERITY_LOW_EXT;
+    case DebugSeverity::Notification: return AL_DEBUG_SEVERITY_NOTIFICATION_EXT;
+    }
+    throw std::runtime_error{"Unexpected debug severity value "+std::to_string(al::to_underlying(severity))};
+}
+
+
+constexpr auto GetDebugSourceName(DebugSource source) noexcept -> const char*
+{
+    switch(source)
+    {
+    case DebugSource::API: return "API";
+    case DebugSource::System: return "Audio System";
+    case DebugSource::ThirdParty: return "Third Party";
+    case DebugSource::Application: return "Application";
+    case DebugSource::Other: return "Other";
+    }
+    return "<invalid source>";
+}
+
+constexpr auto GetDebugTypeName(DebugType type) noexcept -> const char*
+{
+    switch(type)
+    {
+    case DebugType::Error: return "Error";
+    case DebugType::DeprecatedBehavior: return "Deprecated Behavior";
+    case DebugType::UndefinedBehavior: return "Undefined Behavior";
+    case DebugType::Portability: return "Portability";
+    case DebugType::Performance: return "Performance";
+    case DebugType::Marker: return "Marker";
+    case DebugType::PushGroup: return "Push Group";
+    case DebugType::PopGroup: return "Pop Group";
+    case DebugType::Other: return "Other";
+    }
+    return "<invalid type>";
+}
+
+constexpr auto GetDebugSeverityName(DebugSeverity severity) noexcept -> const char*
+{
+    switch(severity)
+    {
+    case DebugSeverity::High: return "High";
+    case DebugSeverity::Medium: return "Medium";
+    case DebugSeverity::Low: return "Low";
+    case DebugSeverity::Notification: return "Notification";
+    }
+    return "<invalid severity>";
+}
+
+} // namespace
+
+
+void ALCcontext::sendDebugMessage(std::unique_lock<std::mutex> &debuglock, DebugSource source,
+    DebugType type, ALuint id, DebugSeverity severity, std::string_view message)
+{
+    if(!mDebugEnabled.load(std::memory_order_relaxed)) UNLIKELY
+        return;
+
+    if(message.length() >= MaxDebugMessageLength) UNLIKELY
+    {
+        ERR("Debug message too long (%zu >= %d):\n-> %.*s\n", message.length(),
+            MaxDebugMessageLength, al::sizei(message), message.data());
+        return;
+    }
+
+    DebugGroup &debug = mDebugGroups.back();
+
+    const uint64_t idfilter{(1_u64 << (DebugSourceBase+al::to_underlying(source)))
+        | (1_u64 << (DebugTypeBase+al::to_underlying(type)))
+        | (uint64_t{id} << 32)};
+    auto iditer = std::lower_bound(debug.mIdFilters.cbegin(), debug.mIdFilters.cend(), idfilter);
+    if(iditer != debug.mIdFilters.cend() && *iditer == idfilter)
+        return;
+
+    const uint filter{(1u << (DebugSourceBase+al::to_underlying(source)))
+        | (1u << (DebugTypeBase+al::to_underlying(type)))
+        | (1u << (DebugSeverityBase+al::to_underlying(severity)))};
+    auto iter = std::lower_bound(debug.mFilters.cbegin(), debug.mFilters.cend(), filter);
+    if(iter != debug.mFilters.cend() && *iter == filter)
+        return;
+
+    if(mDebugCb)
+    {
+        auto callback = mDebugCb;
+        auto param = mDebugParam;
+        debuglock.unlock();
+        callback(GetDebugSourceEnum(source), GetDebugTypeEnum(type), id,
+            GetDebugSeverityEnum(severity), static_cast<ALsizei>(message.length()), message.data(),
+            param);
+    }
+    else
+    {
+        if(mDebugLog.size() < MaxDebugLoggedMessages)
+            mDebugLog.emplace_back(source, type, id, severity, message);
+        else UNLIKELY
+            ERR("Debug message log overflow. Lost message:\n"
+                "  Source: %s\n"
+                "  Type: %s\n"
+                "  ID: %u\n"
+                "  Severity: %s\n"
+                "  Message: \"%.*s\"\n",
+                GetDebugSourceName(source), GetDebugTypeName(type), id,
+                GetDebugSeverityName(severity), al::sizei(message), message.data());
+    }
+}
+
+
+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<std::mutex> debuglock{context->mDebugCbLock};
+    context->mDebugCb = callback;
+    context->mDebugParam = userParam;
+}
+
+
+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)
+        throw al::context_error{AL_INVALID_VALUE, "Null message pointer"};
+
+    auto msgview = (length < 0) ? std::string_view{message}
+        : std::string_view{message, static_cast<uint>(length)};
+    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)
+        throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
+    if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
+        throw al::context_error{AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source};
+
+    auto dtype = GetDebugType(type);
+    if(!dtype)
+        throw al::context_error{AL_INVALID_ENUM, "Invalid debug type 0x%04x", type};
+
+    auto dseverity = GetDebugSeverity(severity);
+    if(!dseverity)
+        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,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)
+            throw al::context_error{AL_INVALID_VALUE, "IDs is null with non-0 count"};
+        if(source == AL_DONT_CARE_EXT)
+            throw al::context_error{AL_INVALID_OPERATION,
+                "Debug source cannot be AL_DONT_CARE_EXT with IDs"};
+        if(type == AL_DONT_CARE_EXT)
+            throw al::context_error{AL_INVALID_OPERATION,
+                "Debug type cannot be AL_DONT_CARE_EXT with IDs"};
+        if(severity != AL_DONT_CARE_EXT)
+            throw al::context_error{AL_INVALID_OPERATION,
+                "Debug severity must be AL_DONT_CARE_EXT with IDs"};
+    }
+
+    if(enable != AL_TRUE && enable != AL_FALSE)
+        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<uint8_t,ElemCount>();
+
+    auto srcIndices = al::span{Values}.subspan(DebugSourceBase,DebugSourceCount);
+    if(source != AL_DONT_CARE_EXT)
+    {
+        auto dsource = GetDebugSource(source);
+        if(!dsource)
+            throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
+        srcIndices = srcIndices.subspan(al::to_underlying(*dsource), 1);
+    }
+
+    auto typeIndices = al::span{Values}.subspan(DebugTypeBase,DebugTypeCount);
+    if(type != AL_DONT_CARE_EXT)
+    {
+        auto dtype = GetDebugType(type);
+        if(!dtype)
+            throw al::context_error{AL_INVALID_ENUM, "Invalid debug type 0x%04x", type};
+        typeIndices = typeIndices.subspan(al::to_underlying(*dtype), 1);
+    }
+
+    auto svrIndices = al::span{Values}.subspan(DebugSeverityBase,DebugSeverityCount);
+    if(severity != AL_DONT_CARE_EXT)
+    {
+        auto dseverity = GetDebugSeverity(severity);
+        if(!dseverity)
+            throw al::context_error{AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity};
+        svrIndices = svrIndices.subspan(al::to_underlying(*dseverity), 1);
+    }
+
+    std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
+    DebugGroup &debug = context->mDebugGroups.back();
+    if(count > 0)
+    {
+        const uint filterbase{(1u<<srcIndices[0]) | (1u<<typeIndices[0])};
+
+        for(const uint id : al::span{ids, static_cast<uint>(count)})
+        {
+            const uint64_t filter{filterbase | (uint64_t{id} << 32)};
+
+            auto iter = std::lower_bound(debug.mIdFilters.cbegin(), debug.mIdFilters.cend(),
+                filter);
+            if(!enable && (iter == debug.mIdFilters.cend() || *iter != filter))
+                debug.mIdFilters.insert(iter, filter);
+            else if(enable && iter != debug.mIdFilters.cend() && *iter == filter)
+                debug.mIdFilters.erase(iter);
+        }
+    }
+    else
+    {
+        auto apply_filter = [enable,&debug](const uint filter)
+        {
+            auto iter = std::lower_bound(debug.mFilters.cbegin(), debug.mFilters.cend(), filter);
+            if(!enable && (iter == debug.mFilters.cend() || *iter != filter))
+                debug.mFilters.insert(iter, filter);
+            else if(enable && iter != debug.mFilters.cend() && *iter == filter)
+                debug.mFilters.erase(iter);
+        };
+        auto apply_severity = [apply_filter,svrIndices](const uint filter)
+        {
+            std::for_each(svrIndices.cbegin(), svrIndices.cend(),
+                [apply_filter,filter](const uint idx){ apply_filter(filter | (1<<idx)); });
+        };
+        auto apply_type = [apply_severity,typeIndices](const uint filter)
+        {
+            std::for_each(typeIndices.cbegin(), typeIndices.cend(),
+                [apply_severity,filter](const uint idx){ apply_severity(filter | (1<<idx)); });
+        };
+        std::for_each(srcIndices.cbegin(), srcIndices.cend(),
+            [apply_type](const uint idx){ apply_type(1<<idx); });
+    }
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
+}
+
+
+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)
+            throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%zu >= %d)", newlen,
+                MaxDebugMessageLength};
+        length = static_cast<ALsizei>(newlen);
+    }
+    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)
+        throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
+    if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
+        throw al::context_error{AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source};
+
+    std::unique_lock<std::mutex> debuglock{context->mDebugCbLock};
+    if(context->mDebugGroups.size() >= MaxDebugGroupDepth)
+        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<uint>(length)});
+    auto &oldback = *(context->mDebugGroups.end()-2);
+    auto &newback = context->mDebugGroups.back();
+
+    newback.mFilters = oldback.mFilters;
+    newback.mIdFilters = oldback.mIdFilters;
+
+    if(context->mContextFlags.test(ContextFlags::DebugBit))
+        context->sendDebugMessage(debuglock, newback.mSource, DebugType::PushGroup, newback.mId,
+            DebugSeverity::Notification, newback.mMessage);
+}
+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<std::mutex> debuglock{context->mDebugCbLock};
+    if(context->mDebugGroups.size() <= 1)
+        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;
+    const auto id = debug.mId;
+    std::string message{std::move(debug.mMessage)};
+
+    context->mDebugGroups.pop_back();
+    if(context->mContextFlags.test(ContextFlags::DebugBit))
+        context->sendDebugMessage(debuglock, source, DebugType::PopGroup, id,
+            DebugSeverity::Notification, message);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
+}
+
+
+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
+try {
+    if(logBufSize < 0)
+        throw al::context_error{AL_INVALID_VALUE, "Negative debug log buffer size"};
+
+    auto sourcesOut = al::span{sources, sources ? count : 0u};
+    auto typesOut = al::span{types, types ? count : 0u};
+    auto idsOut = al::span{ids, ids ? count : 0u};
+    auto severitiesOut = al::span{severities, severities ? count : 0u};
+    auto lengthsOut = al::span{lengths, lengths ? count : 0u};
+    auto logOut = al::span{logBuf, logBuf ? static_cast<ALuint>(logBufSize) : 0u};
+
+    std::lock_guard<std::mutex> debuglock{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(logOut.data() != nullptr)
+        {
+            if(logOut.size() < tocopy)
+                return i;
+            auto oiter = std::copy(entry.mMessage.cbegin(), entry.mMessage.cend(), logOut.begin());
+            *oiter = '\0';
+            logOut = {oiter+1, logOut.end()};
+        }
+
+        if(!sourcesOut.empty())
+        {
+            sourcesOut.front() = GetDebugSourceEnum(entry.mSource);
+            sourcesOut = sourcesOut.subspan<1>();
+        }
+        if(!typesOut.empty())
+        {
+            typesOut.front() = GetDebugTypeEnum(entry.mType);
+            typesOut = typesOut.subspan<1>();
+        }
+        if(!idsOut.empty())
+        {
+            idsOut.front() = entry.mId;
+            idsOut = idsOut.subspan<1>();
+        }
+        if(!severitiesOut.empty())
+        {
+            severitiesOut.front() = GetDebugSeverityEnum(entry.mSeverity);
+            severitiesOut = severitiesOut.subspan<1>();
+        }
+        if(!lengthsOut.empty())
+        {
+            lengthsOut.front() = static_cast<ALsizei>(tocopy);
+            lengthsOut = lengthsOut.subspan<1>();
+        }
+
+        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,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
+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<uint>(length)};
+    if(objname.size() >= MaxObjectLabelLength)
+        throw al::context_error{AL_INVALID_VALUE, "Object label length too long (%zu >= %d)",
+            objname.size(), MaxObjectLabelLength};
+
+    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,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
+try {
+    if(bufSize < 0)
+        throw al::context_error{AL_INVALID_VALUE, "Negative 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"};
+
+    const auto labelOut = al::span{label, label ? static_cast<ALuint>(bufSize) : 0u};
+    auto copy_name = [name,length,labelOut](std::unordered_map<ALuint,std::string> &names)
+    {
+        std::string_view objname;
+
+        auto iter = names.find(name);
+        if(iter != names.end())
+            objname = iter->second;
+
+        if(labelOut.empty())
+            *length = static_cast<ALsizei>(objname.size());
+        else
+        {
+            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<ALsizei>(tocopy);
+        }
+    };
+
+    if(identifier == AL_SOURCE_EXT)
+    {
+        std::lock_guard srclock{context->mSourceLock};
+        copy_name(context->mSourceNames);
+    }
+    else if(identifier == AL_BUFFER)
+    {
+        ALCdevice *device{context->mALDevice.get()};
+        std::lock_guard buflock{device->BufferLock};
+        copy_name(device->mBufferNames);
+    }
+    else if(identifier == AL_FILTER_EXT)
+    {
+        ALCdevice *device{context->mALDevice.get()};
+        std::lock_guard filterlock{device->FilterLock};
+        copy_name(device->mFilterNames);
+    }
+    else if(identifier == AL_EFFECT_EXT)
+    {
+        ALCdevice *device{context->mALDevice.get()};
+        std::lock_guard effectlock{device->EffectLock};
+        copy_name(device->mEffectNames);
+    }
+    else if(identifier == AL_AUXILIARY_EFFECT_SLOT_EXT)
+    {
+        std::lock_guard slotlock{context->mEffectSlotLock};
+        copy_name(context->mEffectSlotNames);
+    }
+    else
+        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());
+}

+ 70 - 0
Engine/lib/openal-soft/al/debug.h

@@ -0,0 +1,70 @@
+#ifndef AL_DEBUG_H
+#define AL_DEBUG_H
+
+#include <cstdint>
+#include <string>
+#include <utility>
+#include <vector>
+
+using uint = unsigned int;
+
+
+/* Somewhat arbitrary. Avoid letting it get out of control if the app enables
+ * logging but never reads it.
+ */
+inline constexpr 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 : std::uint8_t {
+    API = 0,
+    System,
+    ThirdParty,
+    Application,
+    Other,
+};
+inline constexpr uint DebugSourceCount{5};
+
+inline constexpr uint DebugTypeBase{DebugSourceBase + DebugSourceCount};
+enum class DebugType : std::uint8_t {
+    Error = 0,
+    DeprecatedBehavior,
+    UndefinedBehavior,
+    Portability,
+    Performance,
+    Marker,
+    PushGroup,
+    PopGroup,
+    Other,
+};
+inline constexpr uint DebugTypeCount{9};
+
+inline constexpr uint DebugSeverityBase{DebugTypeBase + DebugTypeCount};
+enum class DebugSeverity : std::uint8_t {
+    High = 0,
+    Medium,
+    Low,
+    Notification,
+};
+inline constexpr uint DebugSeverityCount{4};
+
+struct DebugGroup {
+    const uint mId;
+    const DebugSource mSource;
+    std::string mMessage;
+    std::vector<uint> mFilters;
+    std::vector<std::uint64_t> mIdFilters;
+
+    template<typename T>
+    DebugGroup(DebugSource source, uint id, T&& message)
+        : mId{id}, mSource{source}, mMessage{std::forward<T>(message)}
+    { }
+    DebugGroup(const DebugGroup&) = default;
+    DebugGroup(DebugGroup&&) = default;
+    ~DebugGroup();
+};
+
+#endif /* AL_DEBUG_H */

+ 127 - 0
Engine/lib/openal-soft/al/direct_defs.h

@@ -0,0 +1,127 @@
+#ifndef AL_DIRECT_DEFS_H
+#define AL_DIRECT_DEFS_H
+
+namespace detail_ {
+
+template<typename T>
+constexpr T DefaultVal() noexcept { return T{}; }
+
+template<>
+constexpr void DefaultVal() noexcept { }
+
+} // namespace detail_
+
+#define DECL_FUNC(R, Name)                                                    \
+auto AL_APIENTRY Name() noexcept -> R                                         \
+{                                                                             \
+    auto context = GetContextRef();                                           \
+    if(!context) UNLIKELY return detail_::DefaultVal<R>();                    \
+    return Name##Direct(context.get());                                       \
+}
+
+#define DECL_FUNC1(R, Name, T1,n1)                                            \
+auto AL_APIENTRY Name(T1 n1) noexcept -> R                                    \
+{                                                                             \
+    auto context = GetContextRef();                                           \
+    if(!context) UNLIKELY return detail_::DefaultVal<R>();                    \
+    return Name##Direct(context.get(), n1);                                   \
+}
+
+#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<R>();                    \
+    return Name##Direct(context.get(), n1, n2);                               \
+}
+
+#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<R>();                    \
+    return Name##Direct(context.get(), n1, n2, n3);                           \
+}
+
+#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<R>();                    \
+    return Name##Direct(context.get(), n1, n2, n3, n4);                       \
+}
+
+#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<R>();                    \
+    return Name##Direct(context.get(), n1, n2, n3, n4, n5);                   \
+}
+
+
+#define DECL_FUNCEXT(R, Name,Ext)                                             \
+auto AL_APIENTRY Name##Ext() noexcept -> R                                    \
+{                                                                             \
+    auto context = GetContextRef();                                           \
+    if(!context) UNLIKELY return detail_::DefaultVal<R>();                    \
+    return Name##Direct##Ext(context.get());                                  \
+}
+
+#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<R>();                    \
+    return Name##Direct##Ext(context.get(), n1);                              \
+}
+
+#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<R>();                    \
+    return Name##Direct##Ext(context.get(), n1, n2);                          \
+}
+
+#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<R>();                    \
+    return Name##Direct##Ext(context.get(), n1, n2, n3);                      \
+}
+
+#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<R>();                    \
+    return Name##Direct##Ext(context.get(), n1, n2, n3, n4);                  \
+}
+
+#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<R>();                    \
+    return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5);              \
+}
+
+#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<R>();                    \
+    return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5, n6);          \
+}
+
+#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<R>();                    \
+    return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5, n6, n7, n8);  \
+}
+
+#endif /* AL_DIRECT_DEFS_H */

+ 44 - 15
Engine/lib/openal-soft/al/eax/api.h

@@ -10,34 +10,39 @@
 //
 
 
+#include <array>
 #include <cfloat>
 #include <cstdint>
 #include <cstring>
-
-#include <array>
+#include <tuple>
+#ifdef _WIN32
+#include <guiddef.h>
+#endif
 
 #include "AL/al.h"
 
 
-#ifndef GUID_DEFINED
-#define GUID_DEFINED
-typedef struct _GUID {
+#ifndef _WIN32
+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<std::uint8_t,8> Data4;
+};
 
-#ifndef _SYS_GUID_OPERATOR_EQ_
-#define _SYS_GUID_OPERATOR_EQ_
 inline bool operator==(const GUID& lhs, const GUID& rhs) noexcept
 { return std::memcmp(&lhs, &rhs, sizeof(GUID)) == 0; }
 
 inline bool operator!=(const GUID& lhs, const GUID& rhs) noexcept
 { return !(lhs == rhs); }
-#endif  // _SYS_GUID_OPERATOR_EQ_
-#endif // GUID_DEFINED
+#endif // _WIN32
 
+#define DECL_EQOP(T, ...) \
+[[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;
 
@@ -276,11 +281,15 @@ struct EAXVECTOR {
     float x;
     float y;
     float z;
+    [[nodiscard]]
+    auto get_members() const noexcept { return std::forward_as_tuple(x, y, z); }
 }; // EAXVECTOR
 
+[[nodiscard]]
 inline bool operator==(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept
-{ return std::memcmp(&lhs, &rhs, sizeof(EAXVECTOR)) == 0; }
+{ return lhs.get_members() == rhs.get_members(); }
 
+[[nodiscard]]
 inline bool operator!=(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept
 { return !(lhs == rhs); }
 
@@ -361,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;
@@ -613,7 +623,7 @@ struct EAX30SOURCEPROPERTIES {
     float flOcclusionLFRatio; // occlusion low-frequency level re. main control
     float flOcclusionRoomRatio; // relative occlusion control for room effect
     float flOcclusionDirectRatio; // relative occlusion control for direct path
-    long lExclusion; // main exlusion control (attenuation at high frequencies)
+    long lExclusion; // main exclusion control (attenuation at high frequencies)
     float flExclusionLFRatio; // exclusion low-frequency level re. main control
     long lOutsideVolumeHF; // outside sound cone level at high frequencies
     float flDopplerFactor; // like DS3D flDopplerFactor but per source
@@ -653,11 +663,11 @@ struct EAXSPEAKERLEVELPROPERTIES {
 }; // EAXSPEAKERLEVELPROPERTIES
 
 struct EAX40ACTIVEFXSLOTS {
-    GUID guidActiveFXSlots[EAX40_MAX_ACTIVE_FXSLOTS];
+    std::array<GUID,EAX40_MAX_ACTIVE_FXSLOTS> guidActiveFXSlots;
 }; // EAX40ACTIVEFXSLOTS
 
 struct EAX50ACTIVEFXSLOTS {
-    GUID guidActiveFXSlots[EAX50_MAX_ACTIVE_FXSLOTS];
+    std::array<GUID,EAX50_MAX_ACTIVE_FXSLOTS> guidActiveFXSlots;
 }; // EAX50ACTIVEFXSLOTS
 
 // Use this structure for EAXSOURCE_OBSTRUCTIONPARAMETERS property.
@@ -836,6 +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, 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
 
 
@@ -965,6 +980,7 @@ enum EAXAGCCOMPRESSOR_PROPERTY : unsigned int {
 
 struct EAXAGCCOMPRESSORPROPERTIES {
     unsigned long ulOnOff; // Switch Compressor on or off
+    DECL_EQOP(EAXAGCCOMPRESSORPROPERTIES, ulOnOff)
 }; // EAXAGCCOMPRESSORPROPERTIES
 
 
@@ -991,6 +1007,7 @@ struct EAXAUTOWAHPROPERTIES {
     float flReleaseTime; // Release time (seconds)
     long lResonance; // Resonance (mB)
     long lPeakLevel; // Peak level (mB)
+    DECL_EQOP(EAXAUTOWAHPROPERTIES, flAttackTime, flReleaseTime, lResonance, lPeakLevel)
 }; // EAXAUTOWAHPROPERTIES
 
 
@@ -1038,6 +1055,7 @@ struct EAXCHORUSPROPERTIES {
     float flDepth; // Depth (0 to 1)
     float flFeedback; // Feedback (-1 to 1)
     float flDelay; // Delay (seconds)
+    DECL_EQOP(EAXCHORUSPROPERTIES, ulWaveform, lPhase, flRate, flDepth, flFeedback, flDelay)
 }; // EAXCHORUSPROPERTIES
 
 
@@ -1086,6 +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, flEdge, lGain, flLowPassCutOff, flEQCenter, flEQBandwidth)
 }; // EAXDISTORTIONPROPERTIES
 
 
@@ -1130,6 +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, flDelay, flLRDelay, flDamping, flFeedback, flSpread)
 }; // EAXECHOPROPERTIES
 
 
@@ -1184,6 +1204,8 @@ struct EAXEQUALIZERPROPERTIES {
     float flMid2Width; // (octaves)
     long lHighGain; // (mB)
     float flHighCutOff; // (Hz)
+    DECL_EQOP(EAXEQUALIZERPROPERTIES, lLowGain, flLowCutOff, lMid1Gain, flMid1Center, flMid1Width,
+        lMid2Gain, flMid2Center, flMid2Width, lHighGain, flHighCutOff)
 }; // EAXEQUALIZERPROPERTIES
 
 
@@ -1255,6 +1277,7 @@ struct EAXFLANGERPROPERTIES {
     float flDepth; // Depth (0 to 1)
     float flFeedback; // Feedback (0 to 1)
     float flDelay; // Delay (seconds)
+    DECL_EQOP(EAXFLANGERPROPERTIES, ulWaveform, lPhase, flRate, flDepth, flFeedback, flDelay)
 }; // EAXFLANGERPROPERTIES
 
 
@@ -1305,6 +1328,7 @@ struct EAXFREQUENCYSHIFTERPROPERTIES {
     float flFrequency; // (Hz)
     unsigned long ulLeftDirection; // see enum above
     unsigned long ulRightDirection; // see enum above
+    DECL_EQOP(EAXFREQUENCYSHIFTERPROPERTIES, flFrequency, ulLeftDirection, ulRightDirection)
 }; // EAXFREQUENCYSHIFTERPROPERTIES
 
 
@@ -1383,6 +1407,8 @@ struct EAXVOCALMORPHERPROPERTIES {
     long lPhonemeBCoarseTuning; // (semitones)
     unsigned long ulWaveform; // Waveform selector - see enum above
     float flRate; // (Hz)
+    DECL_EQOP(EAXVOCALMORPHERPROPERTIES, ulPhonemeA, lPhonemeACoarseTuning, ulPhonemeB,
+        lPhonemeBCoarseTuning, ulWaveform, flRate)
 }; // EAXVOCALMORPHERPROPERTIES
 
 
@@ -1425,6 +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, lCoarseTune, lFineTune)
 }; // EAXPITCHSHIFTERPROPERTIES
 
 
@@ -1460,6 +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, flFrequency, flHighPassCutOff, ulWaveform)
 }; // EAXRINGMODULATORPROPERTIES
 
 
@@ -1490,4 +1518,5 @@ using LPEAXGET = ALenum(AL_APIENTRY*)(
     ALvoid* property_buffer,
     ALuint property_size);
 
+#undef DECL_EQOP
 #endif // !EAX_API_INCLUDED

+ 1 - 2
Engine/lib/openal-soft/al/eax/call.cpp

@@ -22,8 +22,7 @@ EaxCall::EaxCall(
     ALuint property_source_id,
     ALvoid* property_buffer,
     ALuint property_size)
-    : mCallType{type}, mVersion{0}, mPropertySetId{EaxCallPropertySetId::none}
-    , mIsDeferred{(property_id & deferred_flag) != 0}
+    : mCallType{type}, mIsDeferred{(property_id & deferred_flag) != 0}
     , mPropertyId{property_id & ~deferred_flag}, mPropertySourceId{property_source_id}
     , mPropertyBuffer{property_buffer}, mPropertyBufferSize{property_size}
 {

+ 17 - 17
Engine/lib/openal-soft/al/eax/call.h

@@ -31,16 +31,16 @@ public:
         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<typename TException, typename TValue>
-    TValue& get_value() const
+    [[nodiscard]] auto get_value() const -> TValue&
     {
         if(mPropertyBufferSize < sizeof(TValue))
             fail_too_small();
@@ -49,32 +49,32 @@ public:
     }
 
     template<typename TValue>
-    al::span<TValue> get_values(size_t max_count) const
+    [[nodiscard]] auto get_values(size_t max_count) const -> al::span<TValue>
     {
         if(max_count == 0 || mPropertyBufferSize < sizeof(TValue))
             fail_too_small();
 
-        const auto count = minz(mPropertyBufferSize / sizeof(TValue), max_count);
-        return al::as_span(static_cast<TValue*>(mPropertyBuffer), count);
+        const auto count = std::min(mPropertyBufferSize/sizeof(TValue), max_count);
+        return {static_cast<TValue*>(mPropertyBuffer), count};
     }
 
     template<typename TValue>
-    al::span<TValue> get_values() const
+    [[nodiscard]] auto get_values() const -> al::span<TValue>
     {
-        return get_values<TValue>(~size_t{});
+        return get_values<TValue>(~0_uz);
     }
 
     template<typename TException, typename TValue>
-    void set_value(const TValue& value) const
+    auto set_value(const TValue& value) const -> void
     {
         get_value<TException, TValue>() = value;
     }
 
 private:
     const EaxCallType mCallType;
-    int mVersion;
-    EaxFxSlotIndex mFxSlotIndex;
-    EaxCallPropertySetId mPropertySetId;
+    int mVersion{};
+    EaxFxSlotIndex mFxSlotIndex{};
+    EaxCallPropertySetId mPropertySetId{EaxCallPropertySetId::none};
     bool mIsDeferred;
 
     const ALuint mPropertyId;

+ 182 - 143
Engine/lib/openal-soft/al/eax/effect.h

@@ -4,60 +4,56 @@
 
 #include <cassert>
 #include <memory>
+#include <variant>
 
 #include "alnumeric.h"
 #include "AL/al.h"
+#include "AL/alext.h"
 #include "core/effects/base.h"
 #include "call.h"
 
-struct EaxEffectErrorMessages
-{
+struct EaxEffectErrorMessages {
     static constexpr auto unknown_property_id() noexcept { return "Unknown property id."; }
     static constexpr auto unknown_version() noexcept { return "Unknown version."; }
 }; // EaxEffectErrorMessages
 
-/* TODO: Use std::variant (C++17). */
-enum class EaxEffectType {
-    None, Reverb, Chorus, Autowah, Compressor, Distortion, Echo, Equalizer, Flanger,
-    FrequencyShifter, Modulator, PitchShifter, VocalMorpher
-};
-struct EaxEffectProps {
-    EaxEffectType mType;
-    union {
-        EAXREVERBPROPERTIES mReverb;
-        EAXCHORUSPROPERTIES mChorus;
-        EAXAUTOWAHPROPERTIES mAutowah;
-        EAXAGCCOMPRESSORPROPERTIES mCompressor;
-        EAXDISTORTIONPROPERTIES mDistortion;
-        EAXECHOPROPERTIES mEcho;
-        EAXEQUALIZERPROPERTIES mEqualizer;
-        EAXFLANGERPROPERTIES mFlanger;
-        EAXFREQUENCYSHIFTERPROPERTIES mFrequencyShifter;
-        EAXRINGMODULATORPROPERTIES mModulator;
-        EAXPITCHSHIFTERPROPERTIES mPitchShifter;
-        EAXVOCALMORPHERPROPERTIES mVocalMorpher;
-    };
-};
+using EaxEffectProps = std::variant<std::monostate,
+    EAXREVERBPROPERTIES,
+    EAXCHORUSPROPERTIES,
+    EAXAUTOWAHPROPERTIES,
+    EAXAGCCOMPRESSORPROPERTIES,
+    EAXDISTORTIONPROPERTIES,
+    EAXECHOPROPERTIES,
+    EAXEQUALIZERPROPERTIES,
+    EAXFLANGERPROPERTIES,
+    EAXFREQUENCYSHIFTERPROPERTIES,
+    EAXRINGMODULATORPROPERTIES,
+    EAXPITCHSHIFTERPROPERTIES,
+    EAXVOCALMORPHERPROPERTIES>;
+
+template<typename... Ts>
+struct overloaded : Ts... { using Ts::operator()...; };
+
+template<typename... Ts>
+overloaded(Ts...) -> overloaded<Ts...>;
 
 constexpr ALenum EnumFromEaxEffectType(const EaxEffectProps &props)
 {
-    switch(props.mType)
-    {
-    case EaxEffectType::None: break;
-    case EaxEffectType::Reverb: return AL_EFFECT_EAXREVERB;
-    case EaxEffectType::Chorus: return AL_EFFECT_CHORUS;
-    case EaxEffectType::Autowah: return AL_EFFECT_AUTOWAH;
-    case EaxEffectType::Compressor: return AL_EFFECT_COMPRESSOR;
-    case EaxEffectType::Distortion: return AL_EFFECT_DISTORTION;
-    case EaxEffectType::Echo: return AL_EFFECT_ECHO;
-    case EaxEffectType::Equalizer: return AL_EFFECT_EQUALIZER;
-    case EaxEffectType::Flanger: return AL_EFFECT_FLANGER;
-    case EaxEffectType::FrequencyShifter: return AL_EFFECT_FREQUENCY_SHIFTER;
-    case EaxEffectType::Modulator: return AL_EFFECT_RING_MODULATOR;
-    case EaxEffectType::PitchShifter: return AL_EFFECT_PITCH_SHIFTER;
-    case EaxEffectType::VocalMorpher: return AL_EFFECT_VOCAL_MORPHER;
-    }
-    return AL_EFFECT_NULL;
+    return std::visit(overloaded{
+        [](const std::monostate&) noexcept { return AL_EFFECT_NULL; },
+        [](const EAXREVERBPROPERTIES&) noexcept { return AL_EFFECT_EAXREVERB; },
+        [](const EAXCHORUSPROPERTIES&) noexcept { return AL_EFFECT_CHORUS; },
+        [](const EAXAUTOWAHPROPERTIES&) noexcept { return AL_EFFECT_AUTOWAH; },
+        [](const EAXAGCCOMPRESSORPROPERTIES&) noexcept { return AL_EFFECT_COMPRESSOR; },
+        [](const EAXDISTORTIONPROPERTIES&) noexcept { return AL_EFFECT_DISTORTION; },
+        [](const EAXECHOPROPERTIES&) noexcept { return AL_EFFECT_ECHO; },
+        [](const EAXEQUALIZERPROPERTIES&) noexcept { return AL_EFFECT_EQUALIZER; },
+        [](const EAXFLANGERPROPERTIES&) noexcept { return AL_EFFECT_FLANGER; },
+        [](const EAXFREQUENCYSHIFTERPROPERTIES&) noexcept { return AL_EFFECT_FREQUENCY_SHIFTER; },
+        [](const EAXRINGMODULATORPROPERTIES&) noexcept { return AL_EFFECT_RING_MODULATOR; },
+        [](const EAXPITCHSHIFTERPROPERTIES&) noexcept { return AL_EFFECT_PITCH_SHIFTER; },
+        [](const EAXVOCALMORPHERPROPERTIES&) noexcept { return AL_EFFECT_VOCAL_MORPHER; }
+    }, props);
 }
 
 struct EaxReverbCommitter {
@@ -105,7 +101,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);
@@ -115,16 +110,13 @@ 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<typename T>
@@ -149,51 +141,137 @@ struct EaxCommitter {
     [[noreturn]] static void fail(const char *message);
     [[noreturn]] static void fail_unknown_property_id()
     { fail(EaxEffectErrorMessages::unknown_property_id()); }
-
-    bool commit(const EaxEffectProps &props);
-
-    static void SetDefaults(EaxEffectProps &props);
-    static void Get(const EaxCall &call, const EaxEffectProps &props);
-    static void Set(const EaxCall &call, EaxEffectProps &props);
 };
 
 struct EaxAutowahCommitter : public EaxCommitter<EaxAutowahCommitter> {
     using EaxCommitter<EaxAutowahCommitter>::EaxCommitter;
+
+    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<EaxChorusCommitter> {
     using EaxCommitter<EaxChorusCommitter>::EaxCommitter;
+
+    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<EaxCompressorCommitter> {
     using EaxCommitter<EaxCompressorCommitter>::EaxCommitter;
+
+    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<EaxDistortionCommitter> {
     using EaxCommitter<EaxDistortionCommitter>::EaxCommitter;
+
+    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<EaxEchoCommitter> {
     using EaxCommitter<EaxEchoCommitter>::EaxCommitter;
+
+    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<EaxEqualizerCommitter> {
     using EaxCommitter<EaxEqualizerCommitter>::EaxCommitter;
+
+    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<EaxFlangerCommitter> {
     using EaxCommitter<EaxFlangerCommitter>::EaxCommitter;
+
+    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<EaxFrequencyShifterCommitter> {
     using EaxCommitter<EaxFrequencyShifterCommitter>::EaxCommitter;
+
+    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<EaxModulatorCommitter> {
     using EaxCommitter<EaxModulatorCommitter>::EaxCommitter;
+
+    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<EaxPitchShifterCommitter> {
     using EaxCommitter<EaxPitchShifterCommitter>::EaxCommitter;
+
+    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<EaxVocalMorpherCommitter> {
     using EaxCommitter<EaxVocalMorpherCommitter>::EaxCommitter;
+
+    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<EaxNullCommitter> {
     using EaxCommitter<EaxNullCommitter>::EaxCommitter;
+
+    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<typename T>
+struct CommitterFromProps { };
+
+template<> struct CommitterFromProps<std::monostate> { using type = EaxNullCommitter; };
+template<> struct CommitterFromProps<EAXREVERBPROPERTIES> { using type = EaxReverbCommitter; };
+template<> struct CommitterFromProps<EAXCHORUSPROPERTIES> { using type = EaxChorusCommitter; };
+template<> struct CommitterFromProps<EAXAGCCOMPRESSORPROPERTIES> { using type = EaxCompressorCommitter; };
+template<> struct CommitterFromProps<EAXAUTOWAHPROPERTIES> { using type = EaxAutowahCommitter; };
+template<> struct CommitterFromProps<EAXDISTORTIONPROPERTIES> { using type = EaxDistortionCommitter; };
+template<> struct CommitterFromProps<EAXECHOPROPERTIES> { using type = EaxEchoCommitter; };
+template<> struct CommitterFromProps<EAXEQUALIZERPROPERTIES> { using type = EaxEqualizerCommitter; };
+template<> struct CommitterFromProps<EAXFLANGERPROPERTIES> { using type = EaxFlangerCommitter; };
+template<> struct CommitterFromProps<EAXFREQUENCYSHIFTERPROPERTIES> { using type = EaxFrequencyShifterCommitter; };
+template<> struct CommitterFromProps<EAXRINGMODULATORPROPERTIES> { using type = EaxModulatorCommitter; };
+template<> struct CommitterFromProps<EAXPITCHSHIFTERPROPERTIES> { using type = EaxPitchShifterCommitter; };
+template<> struct CommitterFromProps<EAXVOCALMORPHERPROPERTIES> { using type = EaxVocalMorpherCommitter; };
+
+template<typename T>
+using CommitterFor = typename CommitterFromProps<std::remove_cv_t<std::remove_reference_t<T>>>::type;
+
 
 class EaxEffect {
 public:
@@ -238,51 +316,39 @@ public:
     State4 state5_{};
 
 
-    template<typename T, typename ...Args>
-    void call_set_defaults(Args&& ...args)
-    { return T::SetDefaults(std::forward<Args>(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<EaxReverbCommitter>(props);
-        if(altype == AL_EFFECT_CHORUS)
-            return call_set_defaults<EaxChorusCommitter>(props);
-        if(altype == AL_EFFECT_AUTOWAH)
-            return call_set_defaults<EaxAutowahCommitter>(props);
-        if(altype == AL_EFFECT_COMPRESSOR)
-            return call_set_defaults<EaxCompressorCommitter>(props);
-        if(altype == AL_EFFECT_DISTORTION)
-            return call_set_defaults<EaxDistortionCommitter>(props);
-        if(altype == AL_EFFECT_ECHO)
-            return call_set_defaults<EaxEchoCommitter>(props);
-        if(altype == AL_EFFECT_EQUALIZER)
-            return call_set_defaults<EaxEqualizerCommitter>(props);
-        if(altype == AL_EFFECT_FLANGER)
-            return call_set_defaults<EaxFlangerCommitter>(props);
-        if(altype == AL_EFFECT_FREQUENCY_SHIFTER)
-            return call_set_defaults<EaxFrequencyShifterCommitter>(props);
-        if(altype == AL_EFFECT_RING_MODULATOR)
-            return call_set_defaults<EaxModulatorCommitter>(props);
-        if(altype == AL_EFFECT_PITCH_SHIFTER)
-            return call_set_defaults<EaxPitchShifterCommitter>(props);
-        if(altype == AL_EFFECT_VOCAL_MORPHER)
-            return call_set_defaults<EaxVocalMorpherCommitter>(props);
-        return call_set_defaults<EaxNullCommitter>(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<typename T>
     void init()
     {
-        call_set_defaults<EaxReverbCommitter>(state1_.d);
+        EaxReverbCommitter::SetDefaults(state1_.d);
         state1_.i = state1_.d;
-        call_set_defaults<EaxReverbCommitter>(state2_.d);
+        EaxReverbCommitter::SetDefaults(state2_.d);
         state2_.i = state2_.d;
-        call_set_defaults<EaxReverbCommitter>(state3_.d);
+        EaxReverbCommitter::SetDefaults(state3_.d);
         state3_.i = state3_.d;
-        call_set_defaults<T>(state4_.d);
+        T::SetDefaults(state4_.d);
         state4_.i = state4_.d;
-        call_set_defaults<T>(state5_.d);
+        T::SetDefaults(state5_.d);
         state5_.i = state5_.d;
     }
 
@@ -290,9 +356,9 @@ public:
     {
         switch(eax_version)
         {
-        case 1: call_set_defaults<EaxReverbCommitter>(state1_.d); break;
-        case 2: call_set_defaults<EaxReverbCommitter>(state2_.d); break;
-        case 3: call_set_defaults<EaxReverbCommitter>(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;
         }
@@ -300,47 +366,20 @@ public:
     }
 
 
-#define EAXCALL(T, Callable, ...)                                             \
-    if(T == EaxEffectType::Reverb)                                            \
-        return Callable<EaxReverbCommitter>(__VA_ARGS__);                     \
-    if(T == EaxEffectType::Chorus)                                            \
-        return Callable<EaxChorusCommitter>(__VA_ARGS__);                     \
-    if(T == EaxEffectType::Autowah)                                           \
-        return Callable<EaxAutowahCommitter>(__VA_ARGS__);                    \
-    if(T == EaxEffectType::Compressor)                                        \
-        return Callable<EaxCompressorCommitter>(__VA_ARGS__);                 \
-    if(T == EaxEffectType::Distortion)                                        \
-        return Callable<EaxDistortionCommitter>(__VA_ARGS__);                 \
-    if(T == EaxEffectType::Echo)                                              \
-        return Callable<EaxEchoCommitter>(__VA_ARGS__);                       \
-    if(T == EaxEffectType::Equalizer)                                         \
-        return Callable<EaxEqualizerCommitter>(__VA_ARGS__);                  \
-    if(T == EaxEffectType::Flanger)                                           \
-        return Callable<EaxFlangerCommitter>(__VA_ARGS__);                    \
-    if(T == EaxEffectType::FrequencyShifter)                                  \
-        return Callable<EaxFrequencyShifterCommitter>(__VA_ARGS__);           \
-    if(T == EaxEffectType::Modulator)                                         \
-        return Callable<EaxModulatorCommitter>(__VA_ARGS__);                  \
-    if(T == EaxEffectType::PitchShifter)                                      \
-        return Callable<EaxPitchShifterCommitter>(__VA_ARGS__);               \
-    if(T == EaxEffectType::VocalMorpher)                                      \
-        return Callable<EaxVocalMorpherCommitter>(__VA_ARGS__);               \
-    return Callable<EaxNullCommitter>(__VA_ARGS__)
-
-    template<typename T, typename ...Args>
-    static void call_set(Args&& ...args)
-    { return T::Set(std::forward<Args>(args)...); }
-
     static void call_set(const EaxCall &call, EaxEffectProps &props)
-    { EAXCALL(props.mType, call_set, call, props); }
+    {
+        return std::visit([&](auto &arg)
+        { return CommitterFor<decltype(arg)>::Set(call, arg); },
+        props);
+    }
 
     void set(const EaxCall &call)
     {
         switch(call.get_version())
         {
-        case 1: call_set<EaxReverbCommitter>(call, state1_.d); break;
-        case 2: call_set<EaxReverbCommitter>(call, state2_.d); break;
-        case 3: call_set<EaxReverbCommitter>(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;
         }
@@ -348,32 +387,32 @@ public:
     }
 
 
-    template<typename T, typename ...Args>
-    static void call_get(Args&& ...args)
-    { return T::Get(std::forward<Args>(args)...); }
-
     static void call_get(const EaxCall &call, const EaxEffectProps &props)
-    { EAXCALL(props.mType, call_get, call, props); }
+    {
+        return std::visit([&](auto &arg)
+        { return CommitterFor<decltype(arg)>::Get(call, arg); },
+        props);
+    }
 
-    void get(const EaxCall &call)
+    void get(const EaxCall &call) const
     {
         switch(call.get_version())
         {
-        case 1: call_get<EaxReverbCommitter>(call, state1_.d); break;
-        case 2: call_get<EaxReverbCommitter>(call, state2_.d); break;
-        case 3: call_get<EaxReverbCommitter>(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<typename T, typename ...Args>
-    bool call_commit(Args&& ...args)
-    { return T{props_, al_effect_props_}.commit(std::forward<Args>(args)...); }
-
     bool call_commit(const EaxEffectProps &props)
-    { EAXCALL(props.mType, call_commit, props); }
+    {
+        return std::visit([&](auto &arg)
+        { return CommitterFor<decltype(arg)>{props_, al_effect_props_}.commit(arg); },
+        props);
+    }
 
     bool commit(int eax_version)
     {
@@ -388,15 +427,15 @@ public:
         {
         case 1:
             state1_.i = state1_.d;
-            ret |= call_commit<EaxReverbCommitter>(state1_.d);
+            ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state1_.d);
             break;
         case 2:
             state2_.i = state2_.d;
-            ret |= call_commit<EaxReverbCommitter>(state2_.d);
+            ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state2_.d);
             break;
         case 3:
             state3_.i = state3_.d;
-            ret |= call_commit<EaxReverbCommitter>(state3_.d);
+            ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state3_.d);
             break;
         case 4:
             state4_.i = state4_.d;

+ 10 - 37
Engine/lib/openal-soft/al/eax/exception.cpp

@@ -6,54 +6,27 @@
 #include <string>
 
 
-EaxException::EaxException(const char *context, const char *message)
+EaxException::EaxException(std::string_view context, std::string_view message)
     : std::runtime_error{make_message(context, message)}
 {
 }
 EaxException::~EaxException() = default;
 
 
-std::string EaxException::make_message(const char *context, const char *message)
+std::string EaxException::make_message(std::string_view context, std::string_view message)
 {
-    const auto context_size = (context ? std::string::traits_type::length(context) : 0);
-    const auto has_contex = (context_size > 0);
-
-    const auto message_size = (message ? std::string::traits_type::length(message) : 0);
-    const auto has_message = (message_size > 0);
-
-    if (!has_contex && !has_message)
-    {
-        return std::string{};
-    }
-
-    static constexpr char left_prefix[] = "[";
-    const auto left_prefix_size = std::string::traits_type::length(left_prefix);
-
-    static constexpr char right_prefix[] = "] ";
-    const auto right_prefix_size = std::string::traits_type::length(right_prefix);
-
-    const auto what_size =
-        (
-            has_contex ?
-            left_prefix_size + context_size + right_prefix_size :
-            0) +
-        message_size +
-        1;
-
     auto what = std::string{};
-    what.reserve(what_size);
-
-    if (has_contex)
-    {
-        what.append(left_prefix, left_prefix_size);
-        what.append(context, context_size);
-        what.append(right_prefix, right_prefix_size);
-    }
+    if(context.empty() && message.empty())
+        return what;
 
-    if (has_message)
+    what.reserve((!context.empty() ? context.size() + 3 : 0) + message.length() + 1);
+    if(!context.empty())
     {
-        what.append(message, message_size);
+        what += "[";
+        what += context;
+        what += "] ";
     }
+    what += message;
 
     return what;
 }

+ 10 - 5
Engine/lib/openal-soft/al/eax/exception.h

@@ -1,18 +1,23 @@
 #ifndef EAX_EXCEPTION_INCLUDED
 #define EAX_EXCEPTION_INCLUDED
 
-
 #include <stdexcept>
 #include <string>
+#include <string_view>
 
 
 class EaxException : public std::runtime_error {
-    static std::string make_message(const char *context, const char *message);
+    static std::string make_message(std::string_view context, std::string_view message);
 
 public:
-    EaxException(const char *context, const char *message);
+    EaxException() = 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 */

+ 3 - 4
Engine/lib/openal-soft/al/eax/fx_slot_index.h

@@ -3,17 +3,16 @@
 
 
 #include <cstddef>
+#include <optional>
 
-#include "aloptional.h"
 #include "api.h"
 
 
 using EaxFxSlotIndexValue = std::size_t;
 
-class EaxFxSlotIndex : public al::optional<EaxFxSlotIndexValue>
-{
+class EaxFxSlotIndex : public std::optional<EaxFxSlotIndexValue> {
 public:
-    using al::optional<EaxFxSlotIndexValue>::optional;
+    using std::optional<EaxFxSlotIndexValue>::optional;
 
     EaxFxSlotIndex& operator=(const EaxFxSlotIndexValue &value) { set(value); return *this; }
     EaxFxSlotIndex& operator=(const GUID &guid) { set(guid); return *this; }

+ 4 - 10
Engine/lib/openal-soft/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 @@ public:
     }
 
 
-    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<EaxAlEffectSlotUPtr, EAX_MAX_FXSLOTS>;
@@ -39,8 +34,7 @@ private:
 
 
     [[noreturn]]
-    static void fail(
-        const char* message);
+    static void fail(const char* message);
 
     void initialize_fx_slots(ALCcontext& al_context);
 }; // EaxFxSlots

+ 0 - 21
Engine/lib/openal-soft/al/eax/globals.cpp

@@ -1,21 +0,0 @@
-#include "config.h"
-
-#include "globals.h"
-
-
-bool eax_g_is_enabled = true;
-
-
-const char eax1_ext_name[] = "EAX";
-const char eax2_ext_name[] = "EAX2.0";
-const char eax3_ext_name[] = "EAX3.0";
-const char eax4_ext_name[] = "EAX4.0";
-const char eax5_ext_name[] = "EAX5.0";
-
-const char eax_x_ram_ext_name[] = "EAX-RAM";
-
-const char eax_eax_set_func_name[] = "EAXSet";
-const char eax_eax_get_func_name[] = "EAXGet";
-
-const char eax_eax_set_buffer_mode_func_name[] = "EAXSetBufferMode";
-const char eax_eax_get_buffer_mode_func_name[] = "EAXGetBufferMode";

+ 2 - 18
Engine/lib/openal-soft/al/eax/globals.h

@@ -1,22 +1,6 @@
 #ifndef EAX_GLOBALS_INCLUDED
 #define EAX_GLOBALS_INCLUDED
 
+inline bool eax_g_is_enabled{true};
 
-extern bool eax_g_is_enabled;
-
-
-extern const char eax1_ext_name[];
-extern const char eax2_ext_name[];
-extern const char eax3_ext_name[];
-extern const char eax4_ext_name[];
-extern const char eax5_ext_name[];
-
-extern const char eax_x_ram_ext_name[];
-
-extern const char eax_eax_set_func_name[];
-extern const char eax_eax_get_func_name[];
-
-extern const char eax_eax_set_buffer_mode_func_name[];
-extern const char eax_eax_get_buffer_mode_func_name[];
-
-#endif // !EAX_GLOBALS_INCLUDED
+#endif /* EAX_GLOBALS_INCLUDED */

+ 4 - 4
Engine/lib/openal-soft/al/eax/utils.cpp

@@ -5,10 +5,11 @@
 #include <cassert>
 #include <exception>
 
+#include "alstring.h"
 #include "core/logging.h"
 
 
-void eax_log_exception(const char *message) noexcept
+void eax_log_exception(std::string_view message) noexcept
 {
     const auto exception_ptr = std::current_exception();
     assert(exception_ptr);
@@ -17,10 +18,9 @@ void eax_log_exception(const char *message) noexcept
         std::rethrow_exception(exception_ptr);
     }
     catch(const std::exception& ex) {
-        const auto ex_message = ex.what();
-        ERR("%s %s\n", message ? message : "", ex_message);
+        ERR("%.*s %s\n", al::sizei(message), message.data(), ex.what());
     }
     catch(...) {
-        ERR("%s %s\n", message ? message : "", "Generic exception.");
+        ERR("%.*s %s\n", al::sizei(message), message.data(), "Generic exception.");
     }
 }

+ 6 - 6
Engine/lib/openal-soft/al/eax/utils.h

@@ -4,8 +4,11 @@
 #include <algorithm>
 #include <cstdint>
 #include <string>
+#include <string_view>
 #include <type_traits>
 
+#include "opthelpers.h"
+
 using EaxDirtyFlags = unsigned int;
 
 struct EaxAlLowPassParam {
@@ -13,16 +16,13 @@ struct EaxAlLowPassParam {
     float gain_hf;
 };
 
-void eax_log_exception(const char *message) noexcept;
+void eax_log_exception(std::string_view message) noexcept;
 
 template<typename TException, typename TValue>
-void eax_validate_range(
-    const char* value_name,
-    const TValue& value,
-    const TValue& min_value,
+void eax_validate_range(std::string_view value_name, const TValue& value, const TValue& min_value,
     const TValue& max_value)
 {
-    if (value >= min_value && value <= max_value)
+    if(value >= min_value && value <= max_value) LIKELY
         return;
 
     const auto message =

+ 2 - 10
Engine/lib/openal-soft/al/eax/x_ram.h

@@ -24,15 +24,7 @@ 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);
-
-ALenum AL_APIENTRY EAXGetBufferMode(
-    ALuint buffer,
-    ALint* pReserved);
-
+ALboolean AL_APIENTRY EAXSetBufferMode(ALsizei n, const ALuint *buffers, ALint value) noexcept;
+ALenum AL_APIENTRY EAXGetBufferMode(ALuint buffer, ALint *pReserved) noexcept;
 
 #endif // !EAX_X_RAM_INCLUDED

+ 336 - 367
Engine/lib/openal-soft/al/effect.cpp

@@ -28,9 +28,13 @@
 #include <iterator>
 #include <memory>
 #include <mutex>
-#include <new>
 #include <numeric>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
 #include <utility>
+#include <variant>
+#include <vector>
 
 #include "AL/al.h"
 #include "AL/alc.h"
@@ -38,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"
-#include "vector.h"
 
-#ifdef ALSOFT_EAX
-#include <cassert>
 
-#include "eax/exception.h"
-#endif // ALSOFT_EAX
-
-const EffectList gEffectList[16]{
+const std::array<EffectList,16> gEffectList{{
     { "eaxreverb",   EAXREVERB_EFFECT,   AL_EFFECT_EAXREVERB },
     { "reverb",      REVERB_EFFECT,      AL_EFFECT_REVERB },
     { "autowah",     AUTOWAH_EFFECT,     AL_EFFECT_AUTOWAH },
@@ -73,95 +74,74 @@ 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 },
-};
-
-bool DisabledEffects[MAX_EFFECTS];
-
+    { "convolution", CONVOLUTION_EFFECT, AL_EFFECT_CONVOLUTION_SOFT },
+}};
 
-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); }
-
-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); }
-
+using SubListAllocator = al::allocator<std::array<ALeffect,64>>;
 
-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<NullEffectHandler>(); break;
+    case AL_EFFECT_EAXREVERB: effect->PropsVariant.emplace<ReverbEffectHandler>(); break;
+    case AL_EFFECT_REVERB: effect->PropsVariant.emplace<StdReverbEffectHandler>(); break;
+    case AL_EFFECT_AUTOWAH: effect->PropsVariant.emplace<AutowahEffectHandler>(); break;
+    case AL_EFFECT_CHORUS: effect->PropsVariant.emplace<ChorusEffectHandler>(); break;
+    case AL_EFFECT_COMPRESSOR: effect->PropsVariant.emplace<CompressorEffectHandler>(); break;
+    case AL_EFFECT_DISTORTION: effect->PropsVariant.emplace<DistortionEffectHandler>(); break;
+    case AL_EFFECT_ECHO: effect->PropsVariant.emplace<EchoEffectHandler>(); break;
+    case AL_EFFECT_EQUALIZER: effect->PropsVariant.emplace<EqualizerEffectHandler>(); break;
+    case AL_EFFECT_FLANGER: effect->PropsVariant.emplace<ChorusEffectHandler>(); break;
+    case AL_EFFECT_FREQUENCY_SHIFTER: effect->PropsVariant.emplace<FshifterEffectHandler>(); break;
+    case AL_EFFECT_RING_MODULATOR: effect->PropsVariant.emplace<ModulatorEffectHandler>(); break;
+    case AL_EFFECT_PITCH_SHIFTER: effect->PropsVariant.emplace<PshifterEffectHandler>(); break;
+    case AL_EFFECT_VOCAL_MORPHER: effect->PropsVariant.emplace<VmorpherEffectHandler>(); break;
+    case AL_EFFECT_DEDICATED_DIALOGUE:
+        effect->PropsVariant.emplace<DedicatedDialogEffectHandler>();
+        break;
+    case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT:
+        effect->PropsVariant.emplace<DedicatedLfeEffectHandler>();
+        break;
+    case AL_EFFECT_CONVOLUTION_SOFT:
+        effect->PropsVariant.emplace<ConvolutionEffectHandler>();
+        break;
     }
+    effect->Props = GetDefaultProps(type);
     effect->type = type;
 }
 
-bool EnsureEffects(ALCdevice *device, size_t needed)
-{
-    size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), size_t{0},
+auto EnsureEffects(ALCdevice *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<ALuint>(al::popcount(sublist.FreeMask)); })};
 
@@ -170,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<ALeffect*>(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<SubListAllocator::value_type>;
     }
     return true;
 }
+catch(...) {
+    return false;
+}
 
-ALeffect *AllocEffect(ALCdevice *device)
+ALeffect *AllocEffect(ALCdevice *device) noexcept
 {
     auto sublist = std::find_if(device->EffectList.begin(), device->EffectList.end(),
         [](const EffectSubList &entry) noexcept -> bool
@@ -193,7 +171,7 @@ ALeffect *AllocEffect(ALCdevice *device)
     auto slidx = static_cast<ALuint>(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. */
@@ -206,16 +184,18 @@ ALeffect *AllocEffect(ALCdevice *device)
 
 void FreeEffect(ALCdevice *device, ALeffect *effect)
 {
+    device->mEffectNames.erase(effect->id);
+
     const ALuint id{effect->id - 1};
     const size_t lidx{id >> 6};
     const ALuint slidx{id & 0x3f};
 
-    al::destroy_at(effect);
+    std::destroy_at(effect);
 
     device->EffectList[lidx].FreeMask |= 1_u64 << slidx;
 }
 
-inline ALeffect *LookupEffect(ALCdevice *device, ALuint id)
+inline auto LookupEffect(ALCdevice *device, ALuint id) noexcept -> ALeffect*
 {
     const size_t lidx{(id-1) >> 6};
     const ALuint slidx{(id-1) & 0x3f};
@@ -225,320 +205,294 @@ 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 void AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    if(n < 0) UNLIKELY
-        context->setError(AL_INVALID_VALUE, "Generating %d effects", n);
+AL_API DECL_FUNC2(void, alGenEffects, ALsizei,n, ALuint*,effects)
+FORCE_ALIGN void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) noexcept
+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<std::mutex> _{device->EffectLock};
-    if(!EnsureEffects(device, static_cast<ALuint>(n)))
-    {
-        context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d effect%s", n, (n==1)?"":"s");
-        return;
-    }
+    std::lock_guard<std::mutex> effectlock{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.
-         */
-        al::vector<ALuint> ids;
-        ids.reserve(static_cast<ALuint>(n));
-        do {
-            ALeffect *effect{AllocEffect(device)};
-            ids.emplace_back(effect->id);
-        } while(--n);
-        std::copy(ids.cbegin(), ids.cend(), effects);
-    }
-}
-END_API_FUNC
+    const al::span eids{effects, static_cast<ALuint>(n)};
+    if(!EnsureEffects(device, eids.size()))
+        throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d effect%s", n,
+            (n == 1) ? "" : "s"};
 
-AL_API void AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+    std::generate(eids.begin(), eids.end(), [device]{ return AllocEffect(device)->id; });
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
+}
 
-    if(n < 0) UNLIKELY
-        context->setError(AL_INVALID_VALUE, "Deleting %d effects", n);
+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
+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<std::mutex> _{device->EffectLock};
+    std::lock_guard<std::mutex> effectlock{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<ALuint>(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());
 }
-END_API_FUNC
 
-AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect)
-START_API_FUNC
+AL_API DECL_FUNC1(ALboolean, alIsEffect, ALuint,effect)
+FORCE_ALIGN ALboolean AL_APIENTRY alIsEffectDirect(ALCcontext *context, ALuint effect) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(context) LIKELY
-    {
-        ALCdevice *device{context->mALDevice.get()};
-        std::lock_guard<std::mutex> _{device->EffectLock};
-        if(!effect || LookupEffect(device, effect))
-            return AL_TRUE;
-    }
+    ALCdevice *device{context->mALDevice.get()};
+    std::lock_guard<std::mutex> effectlock{device->EffectLock};
+    if(!effect || LookupEffect(device, effect))
+        return AL_TRUE;
     return AL_FALSE;
 }
-END_API_FUNC
-
-AL_API void AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
 
+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
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->EffectLock};
+    std::lock_guard<std::mutex> effectlock{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<std::remove_reference_t<decltype(arg)>>;
+        using PropType = typename Type::prop_type;
+        return arg.SetParami(std::get<PropType>(aleffect->Props), param, value);
+    }, aleffect->PropsVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *values)
-START_API_FUNC
-{
+AL_API DECL_FUNC3(void, alEffectiv, ALuint,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:
-        alEffecti(effect, param, values[0]);
+        alEffectiDirect(context, effect, param, *values);
         return;
     }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->EffectLock};
+    std::lock_guard<std::mutex> effectlock{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<std::remove_reference_t<decltype(arg)>>;
+        using PropType = typename Type::prop_type;
+        return arg.SetParamiv(std::get<PropType>(aleffect->Props), param, values);
+    }, aleffect->PropsVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
-
-AL_API void AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
 
+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
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->EffectLock};
+    std::lock_guard<std::mutex> effectlock{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<std::remove_reference_t<decltype(arg)>>;
+        using PropType = typename Type::prop_type;
+        return arg.SetParamf(std::get<PropType>(aleffect->Props), param, value);
+    }, aleffect->PropsVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
-
-AL_API void AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *values)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
 
+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
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->EffectLock};
+    std::lock_guard<std::mutex> effectlock{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<std::remove_reference_t<decltype(arg)>>;
+        using PropType = typename Type::prop_type;
+        return arg.SetParamfv(std::get<PropType>(aleffect->Props), param, values);
+    }, aleffect->PropsVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
-
-AL_API void AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
 
+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
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->EffectLock};
+    std::lock_guard<std::mutex> effectlock{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<std::remove_reference_t<decltype(arg)>>;
+        using PropType = typename Type::prop_type;
+        return arg.GetParami(std::get<PropType>(aleffect->Props), param, value);
+    }, aleffect->PropsVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *values)
-START_API_FUNC
-{
+AL_API DECL_FUNC3(void, alGetEffectiv, ALuint,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:
-        alGetEffecti(effect, param, values);
+        alGetEffectiDirect(context, effect, param, values);
         return;
     }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->EffectLock};
+    std::lock_guard<std::mutex> effectlock{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<std::remove_reference_t<decltype(arg)>>;
+        using PropType = typename Type::prop_type;
+        return arg.GetParamiv(std::get<PropType>(aleffect->Props), param, values);
+    }, aleffect->PropsVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
-
-AL_API void AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
 
+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
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->EffectLock};
+    std::lock_guard<std::mutex> effectlock{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<std::remove_reference_t<decltype(arg)>>;
+        using PropType = typename Type::prop_type;
+        return arg.GetParamf(std::get<PropType>(aleffect->Props), param, value);
+    }, aleffect->PropsVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
-
-AL_API void AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *values)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
 
+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
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->EffectLock};
+    std::lock_guard<std::mutex> effectlock{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<std::remove_reference_t<decltype(arg)>>;
+        using PropType = typename Type::prop_type;
+        return arg.GetParamfv(std::get<PropType>(aleffect->Props), param, values);
+    }, aleffect->PropsVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
 
 void InitEffect(ALeffect *effect)
@@ -546,26 +500,43 @@ void InitEffect(ALeffect *effect)
     InitEffectParams(effect, AL_EFFECT_NULL);
 }
 
+void ALeffect::SetName(ALCcontext* context, ALuint id, std::string_view name)
+{
+    ALCdevice *device{context->mALDevice.get()};
+    std::lock_guard<std::mutex> effectlock{device->EffectLock};
+
+    auto effect = LookupEffect(device, id);
+    if(!effect)
+        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", id};
+
+    device->mEffectNames.insert_or_assign(id, name);
+}
+
+
 EffectSubList::~EffectSubList()
 {
+    if(!Effects)
+        return;
+
     uint64_t usemask{~FreeMask};
     while(usemask)
     {
         const int idx{al::countr_zero(usemask)};
-        al::destroy_at(Effects+idx);
+        std::destroy_at(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),
@@ -695,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<ReverbProps>(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
@@ -757,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);
 }

+ 40 - 12
Engine/lib/openal-soft/al/effect.h

@@ -1,11 +1,20 @@
 #ifndef AL_EFFECT_H
 #define AL_EFFECT_H
 
+#include <array>
+#include <bitset>
+#include <cstdint>
+#include <string_view>
+#include <utility>
+
 #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 {
@@ -27,36 +36,55 @@ enum {
 
     MAX_EFFECTS
 };
-extern bool DisabledEffects[MAX_EFFECTS];
-
-extern float ReverbBoost;
+inline std::bitset<MAX_EFFECTS> 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<EffectList,16> gEffectList;
 
+using EffectHandlerVariant = std::variant<NullEffectHandler,ReverbEffectHandler,
+    StdReverbEffectHandler,AutowahEffectHandler,ChorusEffectHandler,CompressorEffectHandler,
+    DistortionEffectHandler,EchoEffectHandler,EqualizerEffectHandler,FlangerEffectHandler,
+    FshifterEffectHandler,ModulatorEffectHandler,PshifterEffectHandler,VmorpherEffectHandler,
+    DedicatedDialogEffectHandler,DedicatedLfeEffectHandler,ConvolutionEffectHandler>;
 
 struct ALeffect {
     // Effect type (AL_EFFECT_NULL, ...)
     ALenum type{AL_EFFECT_NULL};
 
+    EffectHandlerVariant PropsVariant;
     EffectProps Props{};
 
-    const EffectVtable *vtab{nullptr};
-
     /* Self ID */
     ALuint id{0u};
 
-    DISABLE_ALLOC()
+    static void SetName(ALCcontext *context, ALuint id, std::string_view name);
+
+    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<std::array<ALeffect,64>*> 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

+ 72 - 84
Engine/lib/openal-soft/al/effects/autowah.cpp

@@ -13,6 +13,7 @@
 
 #ifdef ALSOFT_EAX
 #include "alnumeric.h"
+#include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #endif // ALSOFT_EAX
@@ -20,100 +21,87 @@
 
 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;
-}
-
-} // namespace
-
-DEFINE_ALEFFECT_VTABLE(Autowah);
-
-const EffectProps AutowahEffectProps{genDefaultProps()};
+void AutowahEffectHandler::GetParamfv(const AutowahProps &props, ALenum param, float *vals)
+{ GetParamf(props, param, vals); }
 
 #ifdef ALSOFT_EAX
 namespace {
@@ -189,62 +177,62 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool AutowahCommitter::commit(const EaxEffectProps &props)
+bool EaxAutowahCommitter::commit(const EAXAUTOWAHPROPERTIES &props)
 {
-    if(props.mType == mEaxProps.mType
-        && mEaxProps.mAutowah.flAttackTime == props.mAutowah.flAttackTime
-        && mEaxProps.mAutowah.flReleaseTime == props.mAutowah.flReleaseTime
-        && mEaxProps.mAutowah.lResonance == props.mAutowah.lResonance
-        && mEaxProps.mAutowah.lPeakLevel == props.mAutowah.lPeakLevel)
+    if(auto *cur = std::get_if<EAXAUTOWAHPROPERTIES>(&mEaxProps); cur && *cur == props)
         return false;
 
     mEaxProps = props;
-
-    mAlProps.Autowah.AttackTime = props.mAutowah.flAttackTime;
-    mAlProps.Autowah.ReleaseTime = props.mAutowah.flReleaseTime;
-    mAlProps.Autowah.Resonance = level_mb_to_gain(static_cast<float>(props.mAutowah.lResonance));
-    mAlProps.Autowah.PeakGain = level_mb_to_gain(static_cast<float>(props.mAutowah.lPeakLevel));
+    mAlProps = [&]{
+        AutowahProps ret{};
+        ret.AttackTime = props.flAttackTime;
+        ret.ReleaseTime = props.flReleaseTime;
+        ret.Resonance = level_mb_to_gain(static_cast<float>(props.lResonance));
+        ret.PeakGain = level_mb_to_gain(static_cast<float>(props.lPeakLevel));
+        return ret;
+    }();
 
     return true;
 }
 
-template<>
-void AutowahCommitter::SetDefaults(EaxEffectProps &props)
+void EaxAutowahCommitter::SetDefaults(EaxEffectProps &props)
 {
-    props.mType = EaxEffectType::Autowah;
-    props.mAutowah.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME;
-    props.mAutowah.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME;
-    props.mAutowah.lResonance = EAXAUTOWAH_DEFAULTRESONANCE;
-    props.mAutowah.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL;
+    static constexpr EAXAUTOWAHPROPERTIES defprops{[]
+    {
+        EAXAUTOWAHPROPERTIES ret{};
+        ret.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME;
+        ret.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME;
+        ret.lResonance = EAXAUTOWAH_DEFAULTRESONANCE;
+        ret.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL;
+        return ret;
+    }()};
+    props = defprops;
 }
 
-template<>
-void AutowahCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxAutowahCommitter::Get(const EaxCall &call, const EAXAUTOWAHPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXAUTOWAH_NONE: break;
-    case EAXAUTOWAH_ALLPARAMETERS: call.set_value<Exception>(props.mAutowah); break;
-    case EAXAUTOWAH_ATTACKTIME: call.set_value<Exception>(props.mAutowah.flAttackTime); break;
-    case EAXAUTOWAH_RELEASETIME: call.set_value<Exception>(props.mAutowah.flReleaseTime); break;
-    case EAXAUTOWAH_RESONANCE: call.set_value<Exception>(props.mAutowah.lResonance); break;
-    case EAXAUTOWAH_PEAKLEVEL: call.set_value<Exception>(props.mAutowah.lPeakLevel); break;
+    case EAXAUTOWAH_ALLPARAMETERS: call.set_value<Exception>(props); break;
+    case EAXAUTOWAH_ATTACKTIME: call.set_value<Exception>(props.flAttackTime); break;
+    case EAXAUTOWAH_RELEASETIME: call.set_value<Exception>(props.flReleaseTime); break;
+    case EAXAUTOWAH_RESONANCE: call.set_value<Exception>(props.lResonance); break;
+    case EAXAUTOWAH_PEAKLEVEL: call.set_value<Exception>(props.lPeakLevel); break;
     default: fail_unknown_property_id();
     }
 }
 
-template<>
-void AutowahCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxAutowahCommitter::Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXAUTOWAH_NONE: break;
-    case EAXAUTOWAH_ALLPARAMETERS: defer<AllValidator>(call, props.mAutowah); break;
-    case EAXAUTOWAH_ATTACKTIME: defer<AttackTimeValidator>(call, props.mAutowah.flAttackTime); break;
-    case EAXAUTOWAH_RELEASETIME: defer<ReleaseTimeValidator>(call, props.mAutowah.flReleaseTime); break;
-    case EAXAUTOWAH_RESONANCE: defer<ResonanceValidator>(call, props.mAutowah.lResonance); break;
-    case EAXAUTOWAH_PEAKLEVEL: defer<PeakLevelValidator>(call, props.mAutowah.lPeakLevel); break;
+    case EAXAUTOWAH_ALLPARAMETERS: defer<AllValidator>(call, props); break;
+    case EAXAUTOWAH_ATTACKTIME: defer<AttackTimeValidator>(call, props.flAttackTime); break;
+    case EAXAUTOWAH_RELEASETIME: defer<ReleaseTimeValidator>(call, props.flReleaseTime); break;
+    case EAXAUTOWAH_RESONANCE: defer<ResonanceValidator>(call, props.lResonance); break;
+    case EAXAUTOWAH_PEAKLEVEL: defer<PeakLevelValidator>(call, props.lPeakLevel); break;
     default: fail_unknown_property_id();
     }
 }

+ 108 - 186
Engine/lib/openal-soft/al/effects/chorus.cpp

@@ -1,19 +1,17 @@
 
 #include "config.h"
 
+#include <optional>
 #include <stdexcept>
 
 #include "AL/al.h"
 #include "AL/efx.h"
 
-#include "alc/effects/base.h"
-#include "aloptional.h"
-#include "core/logging.h"
 #include "effects.h"
 
 #ifdef ALSOFT_EAX
 #include <cassert>
-#include "alnumeric.h"
+#include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #endif // ALSOFT_EAX
@@ -27,16 +25,16 @@ static_assert(FlangerMaxDelay >= AL_FLANGER_MAX_DELAY, "Flanger max delay too sm
 static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
 static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
 
-inline al::optional<ChorusWaveform> WaveformFromEnum(ALenum type)
+constexpr std::optional<ChorusWaveform> WaveformFromEnum(ALenum type) noexcept
 {
     switch(type)
     {
     case AL_CHORUS_WAVEFORM_SINUSOID: return ChorusWaveform::Sinusoid;
     case AL_CHORUS_WAVEFORM_TRIANGLE: return ChorusWaveform::Triangle;
     }
-    return al::nullopt;
+    return std::nullopt;
 }
-inline ALenum EnumFromWaveform(ChorusWaveform type)
+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<int>(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,127 +176,87 @@ 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); }
-
-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()};
+void FlangerEffectHandler::GetParamfv(const ChorusProps &props, ALenum param, float *vals)
+{ GetParamf(props, param, vals); }
 
 
 #ifdef ALSOFT_EAX
 namespace {
 
 struct EaxChorusTraits {
-    using Props = EAXCHORUSPROPERTIES;
+    using EaxProps = EAXCHORUSPROPERTIES;
     using Committer = EaxChorusCommitter;
-    static constexpr auto Field = &EaxEffectProps::mChorus;
 
-    static constexpr auto eax_effect_type() { return EaxEffectType::Chorus; }
     static constexpr auto efx_effect() { return AL_EFFECT_CHORUS; }
 
     static constexpr auto eax_none_param_id() { return EAXCHORUS_NONE; }
@@ -359,11 +319,9 @@ struct EaxChorusTraits {
 }; // EaxChorusTraits
 
 struct EaxFlangerTraits {
-    using Props = EAXFLANGERPROPERTIES;
+    using EaxProps = EAXFLANGERPROPERTIES;
     using Committer = EaxFlangerCommitter;
-    static constexpr auto Field = &EaxEffectProps::mFlanger;
 
-    static constexpr auto eax_effect_type() { return EaxEffectType::Flanger; }
     static constexpr auto efx_effect() { return AL_EFFECT_FLANGER; }
 
     static constexpr auto eax_none_param_id() { return EAXFLANGER_NONE; }
@@ -428,11 +386,10 @@ struct EaxFlangerTraits {
 template<typename TTraits>
 struct ChorusFlangerEffect {
     using Traits = TTraits;
+    using EaxProps = typename Traits::EaxProps;
     using Committer = typename Traits::Committer;
     using Exception = typename Committer::Exception;
 
-    static constexpr auto Field = Traits::Field;
-
     struct WaveformValidator {
         void operator()(unsigned long ulWaveform) const
         {
@@ -500,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);
@@ -514,8 +471,7 @@ struct ChorusFlangerEffect {
 public:
     static void SetDefaults(EaxEffectProps &props)
     {
-        auto&& all = props.*Field;
-        props.mType = Traits::eax_effect_type();
+        auto&& all = props.emplace<EaxProps>();
         all.ulWaveform = Traits::eax_default_waveform();
         all.lPhase = Traits::eax_default_phase();
         all.flRate = Traits::eax_default_rate();
@@ -525,109 +481,83 @@ public:
     }
 
 
-    static void Get(const EaxCall &call, const EaxEffectProps &props)
+    static void Get(const EaxCall &call, const EaxProps &all)
     {
-        auto&& all = props.*Field;
         switch(call.get_property_id())
         {
         case Traits::eax_none_param_id():
             break;
-
         case Traits::eax_allparameters_param_id():
             call.template set_value<Exception>(all);
             break;
-
         case Traits::eax_waveform_param_id():
             call.template set_value<Exception>(all.ulWaveform);
             break;
-
         case Traits::eax_phase_param_id():
             call.template set_value<Exception>(all.lPhase);
             break;
-
         case Traits::eax_rate_param_id():
             call.template set_value<Exception>(all.flRate);
             break;
-
         case Traits::eax_depth_param_id():
             call.template set_value<Exception>(all.flDepth);
             break;
-
         case Traits::eax_feedback_param_id():
             call.template set_value<Exception>(all.flFeedback);
             break;
-
         case Traits::eax_delay_param_id():
             call.template set_value<Exception>(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 = props.*Field;
         switch(call.get_property_id())
         {
         case Traits::eax_none_param_id():
             break;
-
         case Traits::eax_allparameters_param_id():
             Committer::template defer<AllValidator>(call, all);
             break;
-
         case Traits::eax_waveform_param_id():
             Committer::template defer<WaveformValidator>(call, all.ulWaveform);
             break;
-
         case Traits::eax_phase_param_id():
             Committer::template defer<PhaseValidator>(call, all.lPhase);
             break;
-
         case Traits::eax_rate_param_id():
             Committer::template defer<RateValidator>(call, all.flRate);
             break;
-
         case Traits::eax_depth_param_id():
             Committer::template defer<DepthValidator>(call, all.flDepth);
             break;
-
         case Traits::eax_feedback_param_id():
             Committer::template defer<FeedbackValidator>(call, all.flFeedback);
             break;
-
         case Traits::eax_delay_param_id():
             Committer::template defer<DelayValidator>(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.mType == props_.mType)
-        {
-            auto&& src = props_.*Field;
-            auto&& dst = props.*Field;
-            if(dst.ulWaveform == src.ulWaveform && dst.lPhase == src.lPhase
-                && dst.flRate == src.flRate && dst.flDepth == src.flDepth
-                && dst.flFeedback == src.flFeedback && dst.flDelay == src.flDelay)
-                return false;
-        }
+        if(auto *cur = std::get_if<EaxProps>(&props_); cur && *cur == props)
+            return false;
 
         props_ = props;
-        auto&& dst = props.*Field;
 
-        al_props_.Chorus.Waveform = Traits::eax_waveform(dst.ulWaveform);
-        al_props_.Chorus.Phase = static_cast<int>(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<int>(props.lPhase);
+        al_props_.Rate = props.flRate;
+        al_props_.Depth = props.flDepth;
+        al_props_.Feedback = props.flFeedback;
+        al_props_.Delay = props.flDelay;
 
         return true;
     }
@@ -652,29 +582,25 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool ChorusCommitter::commit(const EaxEffectProps &props)
+bool EaxChorusCommitter::commit(const EAXCHORUSPROPERTIES &props)
 {
     using Committer = ChorusFlangerEffect<EaxChorusTraits>;
-    return Committer::Commit(props, mEaxProps, mAlProps);
+    return Committer::Commit(props, mEaxProps, mAlProps.emplace<ChorusProps>());
 }
 
-template<>
-void ChorusCommitter::SetDefaults(EaxEffectProps &props)
+void EaxChorusCommitter::SetDefaults(EaxEffectProps &props)
 {
     using Committer = ChorusFlangerEffect<EaxChorusTraits>;
     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<EaxChorusTraits>;
     Committer::Get(call, props);
 }
 
-template<>
-void ChorusCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxChorusCommitter::Set(const EaxCall &call, EAXCHORUSPROPERTIES &props)
 {
     using Committer = ChorusFlangerEffect<EaxChorusTraits>;
     Committer::Set(call, props);
@@ -693,29 +619,25 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool FlangerCommitter::commit(const EaxEffectProps &props)
+bool EaxFlangerCommitter::commit(const EAXFLANGERPROPERTIES &props)
 {
     using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
-    return Committer::Commit(props, mEaxProps, mAlProps);
+    return Committer::Commit(props, mEaxProps, mAlProps.emplace<ChorusProps>());
 }
 
-template<>
-void FlangerCommitter::SetDefaults(EaxEffectProps &props)
+void EaxFlangerCommitter::SetDefaults(EaxEffectProps &props)
 {
     using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
     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<EaxFlangerTraits>;
     Committer::Get(call, props);
 }
 
-template<>
-void FlangerCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxFlangerCommitter::Set(const EaxCall &call, EAXFLANGERPROPERTIES &props)
 {
     using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
     Committer::Set(call, props);

+ 35 - 44
Engine/lib/openal-soft/al/effects/compressor.cpp

@@ -9,6 +9,7 @@
 
 #ifdef ALSOFT_EAX
 #include "alnumeric.h"
+#include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #endif // ALSOFT_EAX
@@ -16,14 +17,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,51 +43,36 @@ 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
 namespace {
@@ -115,46 +112,40 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool CompressorCommitter::commit(const EaxEffectProps &props)
+bool EaxCompressorCommitter::commit(const EAXAGCCOMPRESSORPROPERTIES &props)
 {
-    if(props.mType == mEaxProps.mType
-        && props.mCompressor.ulOnOff == mEaxProps.mCompressor.ulOnOff)
+    if(auto *cur = std::get_if<EAXAGCCOMPRESSORPROPERTIES>(&mEaxProps); cur && *cur == props)
         return false;
 
     mEaxProps = props;
+    mAlProps = CompressorProps{props.ulOnOff != 0};
 
-    mAlProps.Compressor.OnOff = (props.mCompressor.ulOnOff != 0);
     return true;
 }
 
-template<>
-void CompressorCommitter::SetDefaults(EaxEffectProps &props)
+void EaxCompressorCommitter::SetDefaults(EaxEffectProps &props)
 {
-    props.mType = EaxEffectType::Compressor;
-    props.mCompressor.ulOnOff = EAXAGCCOMPRESSOR_DEFAULTONOFF;
+    props = EAXAGCCOMPRESSORPROPERTIES{EAXAGCCOMPRESSOR_DEFAULTONOFF};
 }
 
-template<>
-void CompressorCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxCompressorCommitter::Get(const EaxCall &call, const EAXAGCCOMPRESSORPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXAGCCOMPRESSOR_NONE: break;
-    case EAXAGCCOMPRESSOR_ALLPARAMETERS: call.set_value<Exception>(props.mCompressor); break;
-    case EAXAGCCOMPRESSOR_ONOFF: call.set_value<Exception>(props.mCompressor.ulOnOff); break;
+    case EAXAGCCOMPRESSOR_ALLPARAMETERS: call.set_value<Exception>(props); break;
+    case EAXAGCCOMPRESSOR_ONOFF: call.set_value<Exception>(props.ulOnOff); break;
     default: fail_unknown_property_id();
     }
 }
 
-template<>
-void CompressorCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxCompressorCommitter::Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXAGCCOMPRESSOR_NONE: break;
-    case EAXAGCCOMPRESSOR_ALLPARAMETERS: defer<AllValidator>(call, props.mCompressor); break;
-    case EAXAGCCOMPRESSOR_ONOFF: defer<OnOffValidator>(call, props.mCompressor.ulOnOff); break;
+    case EAXAGCCOMPRESSOR_ALLPARAMETERS: defer<AllValidator>(call, props); break;
+    case EAXAGCCOMPRESSOR_ONOFF: defer<OnOffValidator>(call, props.ulOnOff); break;
     default: fail_unknown_property_id();
     }
 }

+ 54 - 30
Engine/lib/openal-soft/al/effects/convolution.cpp

@@ -1,93 +1,117 @@
 
 #include "config.h"
 
+#include <algorithm>
+#include <array>
+#include <cmath>
+
 #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<const float> 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<float> 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()};

+ 72 - 22
Engine/lib/openal-soft/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); }

+ 73 - 87
Engine/lib/openal-soft/al/effects/distortion.cpp

@@ -9,6 +9,7 @@
 
 #ifdef ALSOFT_EAX
 #include "alnumeric.h"
+#include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #endif // ALSOFT_EAX
@@ -16,108 +17,93 @@
 
 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
 namespace {
@@ -204,66 +190,66 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool DistortionCommitter::commit(const EaxEffectProps &props)
+bool EaxDistortionCommitter::commit(const EAXDISTORTIONPROPERTIES &props)
 {
-    if(props.mType == mEaxProps.mType && mEaxProps.mDistortion.flEdge == props.mDistortion.flEdge
-        && mEaxProps.mDistortion.lGain == props.mDistortion.lGain
-        && mEaxProps.mDistortion.flLowPassCutOff == props.mDistortion.flLowPassCutOff
-        && mEaxProps.mDistortion.flEQCenter == props.mDistortion.flEQCenter
-        && mEaxProps.mDistortion.flEQBandwidth == props.mDistortion.flEQBandwidth)
+    if(auto *cur = std::get_if<EAXDISTORTIONPROPERTIES>(&mEaxProps); cur && *cur == props)
         return false;
 
     mEaxProps = props;
-
-    mAlProps.Distortion.Edge = props.mDistortion.flEdge;
-    mAlProps.Distortion.Gain = level_mb_to_gain(static_cast<float>(props.mDistortion.lGain));
-    mAlProps.Distortion.LowpassCutoff = props.mDistortion.flLowPassCutOff;
-    mAlProps.Distortion.EQCenter = props.mDistortion.flEQCenter;
-    mAlProps.Distortion.EQBandwidth = props.mDistortion.flEdge;
+    mAlProps = [&]{
+        DistortionProps ret{};
+        ret.Edge = props.flEdge;
+        ret.Gain = level_mb_to_gain(static_cast<float>(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)
 {
-    props.mType = EaxEffectType::Distortion;
-    props.mDistortion.flEdge = EAXDISTORTION_DEFAULTEDGE;
-    props.mDistortion.lGain = EAXDISTORTION_DEFAULTGAIN;
-    props.mDistortion.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF;
-    props.mDistortion.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER;
-    props.mDistortion.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH;
+    static constexpr EAXDISTORTIONPROPERTIES defprops{[]
+    {
+        EAXDISTORTIONPROPERTIES ret{};
+        ret.flEdge = EAXDISTORTION_DEFAULTEDGE;
+        ret.lGain = EAXDISTORTION_DEFAULTGAIN;
+        ret.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF;
+        ret.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER;
+        ret.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH;
+        return ret;
+    }()};
+    props = defprops;
 }
 
-template<>
-void DistortionCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxDistortionCommitter::Get(const EaxCall &call, const EAXDISTORTIONPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXDISTORTION_NONE: break;
-    case EAXDISTORTION_ALLPARAMETERS: call.set_value<Exception>(props.mDistortion); break;
-    case EAXDISTORTION_EDGE: call.set_value<Exception>(props.mDistortion.flEdge); break;
-    case EAXDISTORTION_GAIN: call.set_value<Exception>(props.mDistortion.lGain); break;
-    case EAXDISTORTION_LOWPASSCUTOFF: call.set_value<Exception>(props.mDistortion.flLowPassCutOff); break;
-    case EAXDISTORTION_EQCENTER: call.set_value<Exception>(props.mDistortion.flEQCenter); break;
-    case EAXDISTORTION_EQBANDWIDTH: call.set_value<Exception>(props.mDistortion.flEQBandwidth); break;
+    case EAXDISTORTION_ALLPARAMETERS: call.set_value<Exception>(props); break;
+    case EAXDISTORTION_EDGE: call.set_value<Exception>(props.flEdge); break;
+    case EAXDISTORTION_GAIN: call.set_value<Exception>(props.lGain); break;
+    case EAXDISTORTION_LOWPASSCUTOFF: call.set_value<Exception>(props.flLowPassCutOff); break;
+    case EAXDISTORTION_EQCENTER: call.set_value<Exception>(props.flEQCenter); break;
+    case EAXDISTORTION_EQBANDWIDTH: call.set_value<Exception>(props.flEQBandwidth); break;
     default: fail_unknown_property_id();
     }
 }
 
-template<>
-void DistortionCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxDistortionCommitter::Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXDISTORTION_NONE: break;
-    case EAXDISTORTION_ALLPARAMETERS: defer<AllValidator>(call, props.mDistortion); break;
-    case EAXDISTORTION_EDGE: defer<EdgeValidator>(call, props.mDistortion.flEdge); break;
-    case EAXDISTORTION_GAIN: defer<GainValidator>(call, props.mDistortion.lGain); break;
-    case EAXDISTORTION_LOWPASSCUTOFF: defer<LowPassCutOffValidator>(call, props.mDistortion.flLowPassCutOff); break;
-    case EAXDISTORTION_EQCENTER: defer<EqCenterValidator>(call, props.mDistortion.flEQCenter); break;
-    case EAXDISTORTION_EQBANDWIDTH: defer<EqBandwidthValidator>(call, props.mDistortion.flEQBandwidth); break;
+    case EAXDISTORTION_ALLPARAMETERS: defer<AllValidator>(call, props); break;
+    case EAXDISTORTION_EDGE: defer<EdgeValidator>(call, props.flEdge); break;
+    case EAXDISTORTION_GAIN: defer<GainValidator>(call, props.lGain); break;
+    case EAXDISTORTION_LOWPASSCUTOFF: defer<LowPassCutOffValidator>(call, props.flLowPassCutOff); break;
+    case EAXDISTORTION_EQCENTER: defer<EqCenterValidator>(call, props.flEQCenter); break;
+    case EAXDISTORTION_EQBANDWIDTH: defer<EqBandwidthValidator>(call, props.flEQBandwidth); break;
     default: fail_unknown_property_id();
     }
 }

+ 73 - 87
Engine/lib/openal-soft/al/effects/echo.cpp

@@ -9,6 +9,7 @@
 
 #ifdef ALSOFT_EAX
 #include "alnumeric.h"
+#include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #endif // ALSOFT_EAX
@@ -19,102 +20,87 @@ 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
 namespace {
@@ -201,66 +187,66 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool EchoCommitter::commit(const EaxEffectProps &props)
+bool EaxEchoCommitter::commit(const EAXECHOPROPERTIES &props)
 {
-    if(props.mType == mEaxProps.mType && mEaxProps.mEcho.flDelay == props.mEcho.flDelay
-        && mEaxProps.mEcho.flLRDelay == props.mEcho.flLRDelay
-        && mEaxProps.mEcho.flDamping == props.mEcho.flDamping
-        && mEaxProps.mEcho.flFeedback == props.mEcho.flFeedback
-        && mEaxProps.mEcho.flSpread == props.mEcho.flSpread)
+    if(auto *cur = std::get_if<EAXECHOPROPERTIES>(&mEaxProps); cur && *cur == props)
         return false;
 
     mEaxProps = props;
-
-    mAlProps.Echo.Delay = props.mEcho.flDelay;
-    mAlProps.Echo.LRDelay = props.mEcho.flLRDelay;
-    mAlProps.Echo.Damping = props.mEcho.flDamping;
-    mAlProps.Echo.Feedback = props.mEcho.flFeedback;
-    mAlProps.Echo.Spread = props.mEcho.flSpread;
+    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)
 {
-    props.mType = EaxEffectType::Echo;
-    props.mEcho.flDelay = EAXECHO_DEFAULTDELAY;
-    props.mEcho.flLRDelay = EAXECHO_DEFAULTLRDELAY;
-    props.mEcho.flDamping = EAXECHO_DEFAULTDAMPING;
-    props.mEcho.flFeedback = EAXECHO_DEFAULTFEEDBACK;
-    props.mEcho.flSpread = EAXECHO_DEFAULTSPREAD;
+    static constexpr EAXECHOPROPERTIES defprops{[]
+    {
+        EAXECHOPROPERTIES ret{};
+        ret.flDelay = EAXECHO_DEFAULTDELAY;
+        ret.flLRDelay = EAXECHO_DEFAULTLRDELAY;
+        ret.flDamping = EAXECHO_DEFAULTDAMPING;
+        ret.flFeedback = EAXECHO_DEFAULTFEEDBACK;
+        ret.flSpread = EAXECHO_DEFAULTSPREAD;
+        return ret;
+    }()};
+    props = defprops;
 }
 
-template<>
-void EchoCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxEchoCommitter::Get(const EaxCall &call, const EAXECHOPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXECHO_NONE: break;
-    case EAXECHO_ALLPARAMETERS: call.set_value<Exception>(props.mEcho); break;
-    case EAXECHO_DELAY: call.set_value<Exception>(props.mEcho.flDelay); break;
-    case EAXECHO_LRDELAY: call.set_value<Exception>(props.mEcho.flLRDelay); break;
-    case EAXECHO_DAMPING: call.set_value<Exception>(props.mEcho.flDamping); break;
-    case EAXECHO_FEEDBACK: call.set_value<Exception>(props.mEcho.flFeedback); break;
-    case EAXECHO_SPREAD: call.set_value<Exception>(props.mEcho.flSpread); break;
+    case EAXECHO_ALLPARAMETERS: call.set_value<Exception>(props); break;
+    case EAXECHO_DELAY: call.set_value<Exception>(props.flDelay); break;
+    case EAXECHO_LRDELAY: call.set_value<Exception>(props.flLRDelay); break;
+    case EAXECHO_DAMPING: call.set_value<Exception>(props.flDamping); break;
+    case EAXECHO_FEEDBACK: call.set_value<Exception>(props.flFeedback); break;
+    case EAXECHO_SPREAD: call.set_value<Exception>(props.flSpread); break;
     default: fail_unknown_property_id();
     }
 }
 
-template<>
-void EchoCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxEchoCommitter::Set(const EaxCall &call, EAXECHOPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXECHO_NONE: break;
-    case EAXECHO_ALLPARAMETERS: defer<AllValidator>(call, props.mEcho); break;
-    case EAXECHO_DELAY: defer<DelayValidator>(call, props.mEcho.flDelay); break;
-    case EAXECHO_LRDELAY: defer<LrDelayValidator>(call, props.mEcho.flLRDelay); break;
-    case EAXECHO_DAMPING: defer<DampingValidator>(call, props.mEcho.flDamping); break;
-    case EAXECHO_FEEDBACK: defer<FeedbackValidator>(call, props.mEcho.flFeedback); break;
-    case EAXECHO_SPREAD: defer<SpreadValidator>(call, props.mEcho.flSpread); break;
+    case EAXECHO_ALLPARAMETERS: defer<AllValidator>(call, props); break;
+    case EAXECHO_DELAY: defer<DelayValidator>(call, props.flDelay); break;
+    case EAXECHO_LRDELAY: defer<LrDelayValidator>(call, props.flLRDelay); break;
+    case EAXECHO_DAMPING: defer<DampingValidator>(call, props.flDamping); break;
+    case EAXECHO_FEEDBACK: defer<FeedbackValidator>(call, props.flFeedback); break;
+    case EAXECHO_SPREAD: defer<SpreadValidator>(call, props.flSpread); break;
     default: fail_unknown_property_id();
     }
 }

+ 0 - 6
Engine/lib/openal-soft/al/effects/effects.cpp

@@ -1,9 +1,3 @@
 #include "config.h"
 
-#ifdef ALSOFT_EAX
-
-#include <cassert>
-#include "AL/efx.h"
 #include "effects.h"
-
-#endif // ALSOFT_EAX

+ 40 - 62
Engine/lib/openal-soft/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 <variant>
 
-    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 */

+ 108 - 142
Engine/lib/openal-soft/al/effects/equalizer.cpp

@@ -9,6 +9,7 @@
 
 #ifdef ALSOFT_EAX
 #include "alnumeric.h"
+#include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #endif // ALSOFT_EAX
@@ -16,163 +17,133 @@
 
 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
 namespace {
@@ -319,91 +290,86 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool EqualizerCommitter::commit(const EaxEffectProps &props)
+bool EaxEqualizerCommitter::commit(const EAXEQUALIZERPROPERTIES &props)
 {
-    if(props.mType == mEaxProps.mType && mEaxProps.mEqualizer.lLowGain == props.mEqualizer.lLowGain
-        && mEaxProps.mEqualizer.flLowCutOff == props.mEqualizer.flLowCutOff
-        && mEaxProps.mEqualizer.lMid1Gain == props.mEqualizer.lMid1Gain
-        && mEaxProps.mEqualizer.flMid1Center == props.mEqualizer.flMid1Center
-        && mEaxProps.mEqualizer.flMid1Width == props.mEqualizer.flMid1Width
-        && mEaxProps.mEqualizer.lMid2Gain == props.mEqualizer.lMid2Gain
-        && mEaxProps.mEqualizer.flMid2Center == props.mEqualizer.flMid2Center
-        && mEaxProps.mEqualizer.flMid2Width == props.mEqualizer.flMid2Width
-        && mEaxProps.mEqualizer.lHighGain == props.mEqualizer.lHighGain
-        && mEaxProps.mEqualizer.flHighCutOff == props.mEqualizer.flHighCutOff)
+    if(auto *cur = std::get_if<EAXEQUALIZERPROPERTIES>(&mEaxProps); cur && *cur == props)
         return false;
 
     mEaxProps = props;
-
-    mAlProps.Equalizer.LowGain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lLowGain));
-    mAlProps.Equalizer.LowCutoff = props.mEqualizer.flLowCutOff;
-    mAlProps.Equalizer.Mid1Gain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lMid1Gain));
-    mAlProps.Equalizer.Mid1Center = props.mEqualizer.flMid1Center;
-    mAlProps.Equalizer.Mid1Width = props.mEqualizer.flMid1Width;
-    mAlProps.Equalizer.Mid2Gain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lMid2Gain));
-    mAlProps.Equalizer.Mid2Center = props.mEqualizer.flMid2Center;
-    mAlProps.Equalizer.Mid2Width = props.mEqualizer.flMid2Width;
-    mAlProps.Equalizer.HighGain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lHighGain));
-    mAlProps.Equalizer.HighCutoff = props.mEqualizer.flHighCutOff;
+    mAlProps = [&]{
+        EqualizerProps ret{};
+        ret.LowGain = level_mb_to_gain(static_cast<float>(props.lLowGain));
+        ret.LowCutoff = props.flLowCutOff;
+        ret.Mid1Gain = level_mb_to_gain(static_cast<float>(props.lMid1Gain));
+        ret.Mid1Center = props.flMid1Center;
+        ret.Mid1Width = props.flMid1Width;
+        ret.Mid2Gain = level_mb_to_gain(static_cast<float>(props.lMid2Gain));
+        ret.Mid2Center = props.flMid2Center;
+        ret.Mid2Width = props.flMid2Width;
+        ret.HighGain = level_mb_to_gain(static_cast<float>(props.lHighGain));
+        ret.HighCutoff = props.flHighCutOff;
+        return ret;
+    }();
 
     return true;
 }
 
-template<>
-void EqualizerCommitter::SetDefaults(EaxEffectProps &props)
+void EaxEqualizerCommitter::SetDefaults(EaxEffectProps &props)
 {
-    props.mType = EaxEffectType::Equalizer;
-    props.mEqualizer.lLowGain = EAXEQUALIZER_DEFAULTLOWGAIN;
-    props.mEqualizer.flLowCutOff = EAXEQUALIZER_DEFAULTLOWCUTOFF;
-    props.mEqualizer.lMid1Gain = EAXEQUALIZER_DEFAULTMID1GAIN;
-    props.mEqualizer.flMid1Center = EAXEQUALIZER_DEFAULTMID1CENTER;
-    props.mEqualizer.flMid1Width = EAXEQUALIZER_DEFAULTMID1WIDTH;
-    props.mEqualizer.lMid2Gain = EAXEQUALIZER_DEFAULTMID2GAIN;
-    props.mEqualizer.flMid2Center = EAXEQUALIZER_DEFAULTMID2CENTER;
-    props.mEqualizer.flMid2Width = EAXEQUALIZER_DEFAULTMID2WIDTH;
-    props.mEqualizer.lHighGain = EAXEQUALIZER_DEFAULTHIGHGAIN;
-    props.mEqualizer.flHighCutOff = EAXEQUALIZER_DEFAULTHIGHCUTOFF;
+    static constexpr EAXEQUALIZERPROPERTIES defprops{[]
+    {
+        EAXEQUALIZERPROPERTIES ret{};
+        ret.lLowGain = EAXEQUALIZER_DEFAULTLOWGAIN;
+        ret.flLowCutOff = EAXEQUALIZER_DEFAULTLOWCUTOFF;
+        ret.lMid1Gain = EAXEQUALIZER_DEFAULTMID1GAIN;
+        ret.flMid1Center = EAXEQUALIZER_DEFAULTMID1CENTER;
+        ret.flMid1Width = EAXEQUALIZER_DEFAULTMID1WIDTH;
+        ret.lMid2Gain = EAXEQUALIZER_DEFAULTMID2GAIN;
+        ret.flMid2Center = EAXEQUALIZER_DEFAULTMID2CENTER;
+        ret.flMid2Width = EAXEQUALIZER_DEFAULTMID2WIDTH;
+        ret.lHighGain = EAXEQUALIZER_DEFAULTHIGHGAIN;
+        ret.flHighCutOff = EAXEQUALIZER_DEFAULTHIGHCUTOFF;
+        return ret;
+    }()};
+    props = defprops;
 }
 
-template<>
-void EqualizerCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxEqualizerCommitter::Get(const EaxCall &call, const EAXEQUALIZERPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXEQUALIZER_NONE: break;
-    case EAXEQUALIZER_ALLPARAMETERS: call.set_value<Exception>(props.mEqualizer); break;
-    case EAXEQUALIZER_LOWGAIN: call.set_value<Exception>(props.mEqualizer.lLowGain); break;
-    case EAXEQUALIZER_LOWCUTOFF: call.set_value<Exception>(props.mEqualizer.flLowCutOff); break;
-    case EAXEQUALIZER_MID1GAIN: call.set_value<Exception>(props.mEqualizer.lMid1Gain); break;
-    case EAXEQUALIZER_MID1CENTER: call.set_value<Exception>(props.mEqualizer.flMid1Center); break;
-    case EAXEQUALIZER_MID1WIDTH: call.set_value<Exception>(props.mEqualizer.flMid1Width); break;
-    case EAXEQUALIZER_MID2GAIN: call.set_value<Exception>(props.mEqualizer.lMid2Gain); break;
-    case EAXEQUALIZER_MID2CENTER: call.set_value<Exception>(props.mEqualizer.flMid2Center); break;
-    case EAXEQUALIZER_MID2WIDTH: call.set_value<Exception>(props.mEqualizer.flMid2Width); break;
-    case EAXEQUALIZER_HIGHGAIN: call.set_value<Exception>(props.mEqualizer.lHighGain); break;
-    case EAXEQUALIZER_HIGHCUTOFF: call.set_value<Exception>(props.mEqualizer.flHighCutOff); break;
+    case EAXEQUALIZER_ALLPARAMETERS: call.set_value<Exception>(props); break;
+    case EAXEQUALIZER_LOWGAIN: call.set_value<Exception>(props.lLowGain); break;
+    case EAXEQUALIZER_LOWCUTOFF: call.set_value<Exception>(props.flLowCutOff); break;
+    case EAXEQUALIZER_MID1GAIN: call.set_value<Exception>(props.lMid1Gain); break;
+    case EAXEQUALIZER_MID1CENTER: call.set_value<Exception>(props.flMid1Center); break;
+    case EAXEQUALIZER_MID1WIDTH: call.set_value<Exception>(props.flMid1Width); break;
+    case EAXEQUALIZER_MID2GAIN: call.set_value<Exception>(props.lMid2Gain); break;
+    case EAXEQUALIZER_MID2CENTER: call.set_value<Exception>(props.flMid2Center); break;
+    case EAXEQUALIZER_MID2WIDTH: call.set_value<Exception>(props.flMid2Width); break;
+    case EAXEQUALIZER_HIGHGAIN: call.set_value<Exception>(props.lHighGain); break;
+    case EAXEQUALIZER_HIGHCUTOFF: call.set_value<Exception>(props.flHighCutOff); break;
     default: fail_unknown_property_id();
     }
 }
 
-template<>
-void EqualizerCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxEqualizerCommitter::Set(const EaxCall &call, EAXEQUALIZERPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXEQUALIZER_NONE: break;
-    case EAXEQUALIZER_ALLPARAMETERS: defer<AllValidator>(call, props.mEqualizer); break;
-    case EAXEQUALIZER_LOWGAIN: defer<LowGainValidator>(call, props.mEqualizer.lLowGain); break;
-    case EAXEQUALIZER_LOWCUTOFF: defer<LowCutOffValidator>(call, props.mEqualizer.flLowCutOff); break;
-    case EAXEQUALIZER_MID1GAIN: defer<Mid1GainValidator>(call, props.mEqualizer.lMid1Gain); break;
-    case EAXEQUALIZER_MID1CENTER: defer<Mid1CenterValidator>(call, props.mEqualizer.flMid1Center); break;
-    case EAXEQUALIZER_MID1WIDTH: defer<Mid1WidthValidator>(call, props.mEqualizer.flMid1Width); break;
-    case EAXEQUALIZER_MID2GAIN: defer<Mid2GainValidator>(call, props.mEqualizer.lMid2Gain); break;
-    case EAXEQUALIZER_MID2CENTER: defer<Mid2CenterValidator>(call, props.mEqualizer.flMid2Center); break;
-    case EAXEQUALIZER_MID2WIDTH: defer<Mid2WidthValidator>(call, props.mEqualizer.flMid2Width); break;
-    case EAXEQUALIZER_HIGHGAIN: defer<HighGainValidator>(call, props.mEqualizer.lHighGain); break;
-    case EAXEQUALIZER_HIGHCUTOFF: defer<HighCutOffValidator>(call, props.mEqualizer.flHighCutOff); break;
+    case EAXEQUALIZER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
+    case EAXEQUALIZER_LOWGAIN: defer<LowGainValidator>(call, props.lLowGain); break;
+    case EAXEQUALIZER_LOWCUTOFF: defer<LowCutOffValidator>(call, props.flLowCutOff); break;
+    case EAXEQUALIZER_MID1GAIN: defer<Mid1GainValidator>(call, props.lMid1Gain); break;
+    case EAXEQUALIZER_MID1CENTER: defer<Mid1CenterValidator>(call, props.flMid1Center); break;
+    case EAXEQUALIZER_MID1WIDTH: defer<Mid1WidthValidator>(call, props.flMid1Width); break;
+    case EAXEQUALIZER_MID2GAIN: defer<Mid2GainValidator>(call, props.lMid2Gain); break;
+    case EAXEQUALIZER_MID2CENTER: defer<Mid2CenterValidator>(call, props.flMid2Center); break;
+    case EAXEQUALIZER_MID2WIDTH: defer<Mid2WidthValidator>(call, props.flMid2Width); break;
+    case EAXEQUALIZER_HIGHGAIN: defer<HighGainValidator>(call, props.lHighGain); break;
+    case EAXEQUALIZER_HIGHCUTOFF: defer<HighCutOffValidator>(call, props.flHighCutOff); break;
     default: fail_unknown_property_id();
     }
 }

+ 77 - 74
Engine/lib/openal-soft/al/effects/fshifter.cpp

@@ -1,18 +1,19 @@
 
 #include "config.h"
 
+#include <optional>
 #include <stdexcept>
 
 #include "AL/al.h"
 #include "AL/efx.h"
 
 #include "alc/effects/base.h"
-#include "aloptional.h"
 #include "effects.h"
 
 #ifdef ALSOFT_EAX
 #include <cassert>
 #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 {
 
-al::optional<FShifterDirection> DirectionFromEmum(ALenum value)
+constexpr std::optional<FShifterDirection> DirectionFromEmum(ALenum value) noexcept
 {
     switch(value)
     {
@@ -28,9 +29,9 @@ al::optional<FShifterDirection> DirectionFromEmum(ALenum value)
     case AL_FREQUENCY_SHIFTER_DIRECTION_UP: return FShifterDirection::Up;
     case AL_FREQUENCY_SHIFTER_DIRECTION_OFF: return FShifterDirection::Off;
     }
-    return al::nullopt;
+    return std::nullopt;
 }
-ALenum EnumFromDirection(FShifterDirection dir)
+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<int>(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 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 Fshifter_getParami(const EffectProps *props, ALenum param, int *val)
+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,23 +133,9 @@ 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
 namespace {
@@ -197,13 +198,9 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool FrequencyShifterCommitter::commit(const EaxEffectProps &props)
+bool EaxFrequencyShifterCommitter::commit(const EAXFREQUENCYSHIFTERPROPERTIES &props)
 {
-    if(props.mType == mEaxProps.mType
-        && mEaxProps.mFrequencyShifter.flFrequency == props.mFrequencyShifter.flFrequency
-        && mEaxProps.mFrequencyShifter.ulLeftDirection == props.mFrequencyShifter.ulLeftDirection
-        && mEaxProps.mFrequencyShifter.ulRightDirection == props.mFrequencyShifter.ulRightDirection)
+    if(auto *cur = std::get_if<EAXFREQUENCYSHIFTERPROPERTIES>(&mEaxProps); cur && *cur == props)
         return false;
 
     mEaxProps = props;
@@ -217,46 +214,52 @@ bool FrequencyShifterCommitter::commit(const EaxEffectProps &props)
         return FShifterDirection::Off;
     };
 
-    mAlProps.Fshifter.Frequency = props.mFrequencyShifter.flFrequency;
-    mAlProps.Fshifter.LeftDirection = get_direction(props.mFrequencyShifter.ulLeftDirection);
-    mAlProps.Fshifter.RightDirection = get_direction(props.mFrequencyShifter.ulRightDirection);
+    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)
 {
-    props.mType = EaxEffectType::FrequencyShifter;
-    props.mFrequencyShifter.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY;
-    props.mFrequencyShifter.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION;
-    props.mFrequencyShifter.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION;
+    static constexpr EAXFREQUENCYSHIFTERPROPERTIES defprops{[]
+    {
+        EAXFREQUENCYSHIFTERPROPERTIES ret{};
+        ret.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY;
+        ret.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION;
+        ret.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION;
+        return ret;
+    }()};
+    props = defprops;
 }
 
-template<>
-void FrequencyShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxFrequencyShifterCommitter::Get(const EaxCall &call, const EAXFREQUENCYSHIFTERPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXFREQUENCYSHIFTER_NONE: break;
-    case EAXFREQUENCYSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props.mFrequencyShifter); break;
-    case EAXFREQUENCYSHIFTER_FREQUENCY: call.set_value<Exception>(props.mFrequencyShifter.flFrequency); break;
-    case EAXFREQUENCYSHIFTER_LEFTDIRECTION: call.set_value<Exception>(props.mFrequencyShifter.ulLeftDirection); break;
-    case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: call.set_value<Exception>(props.mFrequencyShifter.ulRightDirection); break;
+    case EAXFREQUENCYSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props); break;
+    case EAXFREQUENCYSHIFTER_FREQUENCY: call.set_value<Exception>(props.flFrequency); break;
+    case EAXFREQUENCYSHIFTER_LEFTDIRECTION: call.set_value<Exception>(props.ulLeftDirection); break;
+    case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: call.set_value<Exception>(props.ulRightDirection); break;
     default: fail_unknown_property_id();
     }
 }
 
-template<>
-void FrequencyShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxFrequencyShifterCommitter::Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXFREQUENCYSHIFTER_NONE: break;
-    case EAXFREQUENCYSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props.mFrequencyShifter); break;
-    case EAXFREQUENCYSHIFTER_FREQUENCY: defer<FrequencyValidator>(call, props.mFrequencyShifter.flFrequency); break;
-    case EAXFREQUENCYSHIFTER_LEFTDIRECTION: defer<LeftDirectionValidator>(call, props.mFrequencyShifter.ulLeftDirection); break;
-    case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: defer<RightDirectionValidator>(call, props.mFrequencyShifter.ulRightDirection); break;
+    case EAXFREQUENCYSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
+    case EAXFREQUENCYSHIFTER_FREQUENCY: defer<FrequencyValidator>(call, props.flFrequency); break;
+    case EAXFREQUENCYSHIFTER_LEFTDIRECTION: defer<LeftDirectionValidator>(call, props.ulLeftDirection); break;
+    case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: defer<RightDirectionValidator>(call, props.ulRightDirection); break;
     default: fail_unknown_property_id();
     }
 }

+ 80 - 87
Engine/lib/openal-soft/al/effects/modulator.cpp

@@ -1,18 +1,19 @@
 
 #include "config.h"
 
+#include <optional>
 #include <stdexcept>
 
 #include "AL/al.h"
 #include "AL/efx.h"
 
 #include "alc/effects/base.h"
-#include "aloptional.h"
 #include "effects.h"
 
 #ifdef ALSOFT_EAX
 #include <cassert>
 #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 {
 
-al::optional<ModulatorWaveform> WaveformFromEmum(ALenum value)
+constexpr std::optional<ModulatorWaveform> WaveformFromEmum(ALenum value) noexcept
 {
     switch(value)
     {
@@ -28,9 +29,9 @@ al::optional<ModulatorWaveform> WaveformFromEmum(ALenum value)
     case AL_RING_MODULATOR_SAWTOOTH: return ModulatorWaveform::Sawtooth;
     case AL_RING_MODULATOR_SQUARE: return ModulatorWaveform::Square;
     }
-    return al::nullopt;
+    return std::nullopt;
 }
-ALenum EnumFromWaveform(ModulatorWaveform type)
+constexpr ALenum EnumFromWaveform(ModulatorWaveform type)
 {
     switch(type)
     {
@@ -42,40 +43,31 @@ ALenum EnumFromWaveform(ModulatorWaveform type)
         std::to_string(static_cast<int>(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<float>(val));
+        SetParamf(props, param, static_cast<float>(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,62 +77,61 @@ 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<int>(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<int>(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<int>(props.Frequency); break;
+    case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = static_cast<int>(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); }
-
-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;
-}
+void ModulatorEffectHandler::GetParamfv(const ModulatorProps &props, ALenum param, float *vals)
+{ GetParamf(props, param, vals); }
 
-} // namespace
-
-DEFINE_ALEFFECT_VTABLE(Modulator);
-
-const EffectProps ModulatorEffectProps{genDefaultProps()};
 
 #ifdef ALSOFT_EAX
 namespace {
@@ -203,13 +194,9 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool ModulatorCommitter::commit(const EaxEffectProps &props)
+bool EaxModulatorCommitter::commit(const EAXRINGMODULATORPROPERTIES &props)
 {
-    if(props.mType == mEaxProps.mType
-        && mEaxProps.mModulator.flFrequency == props.mModulator.flFrequency
-        && mEaxProps.mModulator.flHighPassCutOff == props.mModulator.flHighPassCutOff
-        && mEaxProps.mModulator.ulWaveform == props.mModulator.ulWaveform)
+    if(auto *cur = std::get_if<EAXRINGMODULATORPROPERTIES>(&mEaxProps); cur && *cur == props)
         return false;
 
     mEaxProps = props;
@@ -225,46 +212,52 @@ bool ModulatorCommitter::commit(const EaxEffectProps &props)
         return ModulatorWaveform::Sinusoid;
     };
 
-    mAlProps.Modulator.Frequency = props.mModulator.flFrequency;
-    mAlProps.Modulator.HighPassCutoff = props.mModulator.flHighPassCutOff;
-    mAlProps.Modulator.Waveform = get_waveform(props.mModulator.ulWaveform);
+    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)
 {
-    props.mType = EaxEffectType::Modulator;
-    props.mModulator.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY;
-    props.mModulator.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF;
-    props.mModulator.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM;
+    static constexpr EAXRINGMODULATORPROPERTIES defprops{[]
+    {
+        EAXRINGMODULATORPROPERTIES ret{};
+        ret.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY;
+        ret.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF;
+        ret.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM;
+        return ret;
+    }()};
+    props = defprops;
 }
 
-template<>
-void ModulatorCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxModulatorCommitter::Get(const EaxCall &call, const EAXRINGMODULATORPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXRINGMODULATOR_NONE: break;
-    case EAXRINGMODULATOR_ALLPARAMETERS: call.set_value<Exception>(props.mModulator); break;
-    case EAXRINGMODULATOR_FREQUENCY: call.set_value<Exception>(props.mModulator.flFrequency); break;
-    case EAXRINGMODULATOR_HIGHPASSCUTOFF: call.set_value<Exception>(props.mModulator.flHighPassCutOff); break;
-    case EAXRINGMODULATOR_WAVEFORM: call.set_value<Exception>(props.mModulator.ulWaveform); break;
+    case EAXRINGMODULATOR_ALLPARAMETERS: call.set_value<Exception>(props); break;
+    case EAXRINGMODULATOR_FREQUENCY: call.set_value<Exception>(props.flFrequency); break;
+    case EAXRINGMODULATOR_HIGHPASSCUTOFF: call.set_value<Exception>(props.flHighPassCutOff); break;
+    case EAXRINGMODULATOR_WAVEFORM: call.set_value<Exception>(props.ulWaveform); break;
     default: fail_unknown_property_id();
     }
 }
 
-template<>
-void ModulatorCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxModulatorCommitter::Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props)
 {
-    switch (call.get_property_id())
+    switch(call.get_property_id())
     {
     case EAXRINGMODULATOR_NONE: break;
-    case EAXRINGMODULATOR_ALLPARAMETERS: defer<AllValidator>(call, props.mModulator); break;
-    case EAXRINGMODULATOR_FREQUENCY: defer<FrequencyValidator>(call, props.mModulator.flFrequency); break;
-    case EAXRINGMODULATOR_HIGHPASSCUTOFF: defer<HighPassCutOffValidator>(call, props.mModulator.flHighPassCutOff); break;
-    case EAXRINGMODULATOR_WAVEFORM: defer<WaveformValidator>(call, props.mModulator.ulWaveform); break;
+    case EAXRINGMODULATOR_ALLPARAMETERS: defer<AllValidator>(call, props); break;
+    case EAXRINGMODULATOR_FREQUENCY: defer<FrequencyValidator>(call, props.flFrequency); break;
+    case EAXRINGMODULATOR_HIGHPASSCUTOFF: defer<HighPassCutOffValidator>(call, props.flHighPassCutOff); break;
+    case EAXRINGMODULATOR_WAVEFORM: defer<WaveformValidator>(call, props.ulWaveform); break;
     default: fail_unknown_property_id();
     }
 }

+ 29 - 35
Engine/lib/openal-soft/al/effects/null.cpp

@@ -8,13 +8,23 @@
 #include "effects.h"
 
 #ifdef 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,27 +85,15 @@ 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
 namespace {
@@ -117,30 +115,26 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool NullCommitter::commit(const EaxEffectProps &props)
+bool EaxNullCommitter::commit(const std::monostate &props)
 {
-    const bool ret{props.mType != mEaxProps.mType};
+    const bool ret{std::holds_alternative<std::monostate>(mEaxProps)};
     mEaxProps = props;
+    mAlProps = std::monostate{};
     return ret;
 }
 
-template<>
-void NullCommitter::SetDefaults(EaxEffectProps &props)
+void EaxNullCommitter::SetDefaults(EaxEffectProps &props)
 {
-    props = EaxEffectProps{};
-    props.mType = EaxEffectType::None;
+    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();

+ 49 - 57
Engine/lib/openal-soft/al/effects/pshifter.cpp

@@ -9,6 +9,7 @@
 
 #ifdef ALSOFT_EAX
 #include "alnumeric.h"
+#include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #endif // ALSOFT_EAX
@@ -16,28 +17,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,49 +50,40 @@ 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
 namespace {
@@ -138,52 +134,48 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool PitchShifterCommitter::commit(const EaxEffectProps &props)
+bool EaxPitchShifterCommitter::commit(const EAXPITCHSHIFTERPROPERTIES &props)
 {
-    if(props.mType == mEaxProps.mType
-        && mEaxProps.mPitchShifter.lCoarseTune == props.mPitchShifter.lCoarseTune
-        && mEaxProps.mPitchShifter.lFineTune == props.mPitchShifter.lFineTune)
+    if(auto *cur = std::get_if<EAXPITCHSHIFTERPROPERTIES>(&mEaxProps); cur && *cur == props)
         return false;
 
     mEaxProps = props;
-
-    mAlProps.Pshifter.CoarseTune = static_cast<int>(mEaxProps.mPitchShifter.lCoarseTune);
-    mAlProps.Pshifter.FineTune = static_cast<int>(mEaxProps.mPitchShifter.lFineTune);
+    mAlProps = [&]{
+        PshifterProps ret{};
+        ret.CoarseTune = static_cast<int>(props.lCoarseTune);
+        ret.FineTune = static_cast<int>(props.lFineTune);
+        return ret;
+    }();
 
     return true;
 }
 
-template<>
-void PitchShifterCommitter::SetDefaults(EaxEffectProps &props)
+void EaxPitchShifterCommitter::SetDefaults(EaxEffectProps &props)
 {
-    props.mType = EaxEffectType::PitchShifter;
-    props.mPitchShifter.lCoarseTune = EAXPITCHSHIFTER_DEFAULTCOARSETUNE;
-    props.mPitchShifter.lFineTune = EAXPITCHSHIFTER_DEFAULTFINETUNE;
+    props = EAXPITCHSHIFTERPROPERTIES{EAXPITCHSHIFTER_DEFAULTCOARSETUNE,
+        EAXPITCHSHIFTER_DEFAULTFINETUNE};
 }
 
-template<>
-void PitchShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxPitchShifterCommitter::Get(const EaxCall &call, const EAXPITCHSHIFTERPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXPITCHSHIFTER_NONE: break;
-    case EAXPITCHSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props.mPitchShifter); break;
-    case EAXPITCHSHIFTER_COARSETUNE: call.set_value<Exception>(props.mPitchShifter.lCoarseTune); break;
-    case EAXPITCHSHIFTER_FINETUNE: call.set_value<Exception>(props.mPitchShifter.lFineTune); break;
+    case EAXPITCHSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props); break;
+    case EAXPITCHSHIFTER_COARSETUNE: call.set_value<Exception>(props.lCoarseTune); break;
+    case EAXPITCHSHIFTER_FINETUNE: call.set_value<Exception>(props.lFineTune); break;
     default: fail_unknown_property_id();
     }
 }
 
-template<>
-void PitchShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxPitchShifterCommitter::Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXPITCHSHIFTER_NONE: break;
-    case EAXPITCHSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props.mPitchShifter); break;
-    case EAXPITCHSHIFTER_COARSETUNE: defer<CoarseTuneValidator>(call, props.mPitchShifter.lCoarseTune); break;
-    case EAXPITCHSHIFTER_FINETUNE: defer<FineTuneValidator>(call, props.mPitchShifter.lFineTune); break;
+    case EAXPITCHSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
+    case EAXPITCHSHIFTER_COARSETUNE: defer<CoarseTuneValidator>(call, props.lCoarseTune); break;
+    case EAXPITCHSHIFTER_FINETUNE: defer<FineTuneValidator>(call, props.lFineTune); break;
     default: fail_unknown_property_id();
     }
 }

Dosya farkı çok büyük olduğundan ihmal edildi
+ 267 - 391
Engine/lib/openal-soft/al/effects/reverb.cpp


+ 92 - 156
Engine/lib/openal-soft/al/effects/vmorpher.cpp

@@ -1,18 +1,18 @@
 
 #include "config.h"
 
+#include <optional>
 #include <stdexcept>
 
 #include "AL/al.h"
 #include "AL/efx.h"
 
-#include "alc/effects/base.h"
-#include "aloptional.h"
+#include "core/effects/base.h"
 #include "effects.h"
 
 #ifdef ALSOFT_EAX
 #include <cassert>
-#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 {
 
-al::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val)
+constexpr std::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val) noexcept
 {
 #define HANDLE_PHENOME(x) case AL_VOCAL_MORPHER_PHONEME_ ## x:                \
     return VMorpherPhenome::x
@@ -57,10 +57,10 @@ al::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val)
     HANDLE_PHENOME(V);
     HANDLE_PHENOME(Z);
     }
-    return al::nullopt;
+    return std::nullopt;
 #undef HANDLE_PHENOME
 }
-ALenum EnumFromPhenome(VMorpherPhenome phenome)
+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
 }
 
-al::optional<VMorpherWaveform> WaveformFromEmum(ALenum value)
+constexpr std::optional<VMorpherWaveform> WaveformFromEmum(ALenum value) noexcept
 {
     switch(value)
     {
@@ -108,9 +108,9 @@ al::optional<VMorpherWaveform> WaveformFromEmum(ALenum value)
     case AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE: return VMorpherWaveform::Triangle;
     case AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH: return VMorpherWaveform::Sawtooth;
     }
-    return al::nullopt;
+    return std::nullopt;
 }
-ALenum EnumFromWaveform(VMorpherWaveform type)
+constexpr ALenum EnumFromWaveform(VMorpherWaveform type)
 {
     switch(type)
     {
@@ -122,13 +122,29 @@ ALenum EnumFromWaveform(VMorpherWaveform type)
         std::to_string(static_cast<int>(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,26 +236,9 @@ 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
 namespace {
@@ -352,16 +337,9 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool VocalMorpherCommitter::commit(const EaxEffectProps &props)
+bool EaxVocalMorpherCommitter::commit(const EAXVOCALMORPHERPROPERTIES &props)
 {
-    if(props.mType == mEaxProps.mType
-        && mEaxProps.mVocalMorpher.ulPhonemeA == props.mVocalMorpher.ulPhonemeA
-        && mEaxProps.mVocalMorpher.lPhonemeACoarseTuning == props.mVocalMorpher.lPhonemeACoarseTuning
-        && mEaxProps.mVocalMorpher.ulPhonemeB == props.mVocalMorpher.ulPhonemeB
-        && mEaxProps.mVocalMorpher.lPhonemeBCoarseTuning == props.mVocalMorpher.lPhonemeBCoarseTuning
-        && mEaxProps.mVocalMorpher.ulWaveform == props.mVocalMorpher.ulWaveform
-        && mEaxProps.mVocalMorpher.flRate == props.mVocalMorpher.flRate)
+    if(auto *cur = std::get_if<EAXVOCALMORPHERPROPERTIES>(&mEaxProps); cur && *cur == props)
         return false;
 
     mEaxProps = props;
@@ -413,107 +391,65 @@ bool VocalMorpherCommitter::commit(const EaxEffectProps &props)
         return VMorpherWaveform::Sinusoid;
     };
 
-    mAlProps.Vmorpher.PhonemeA = get_phoneme(props.mVocalMorpher.ulPhonemeA);
-    mAlProps.Vmorpher.PhonemeACoarseTuning = static_cast<int>(props.mVocalMorpher.lPhonemeACoarseTuning);
-    mAlProps.Vmorpher.PhonemeB = get_phoneme(props.mVocalMorpher.ulPhonemeB);
-    mAlProps.Vmorpher.PhonemeBCoarseTuning = static_cast<int>(props.mVocalMorpher.lPhonemeBCoarseTuning);
-    mAlProps.Vmorpher.Waveform = get_waveform(props.mVocalMorpher.ulWaveform);
-    mAlProps.Vmorpher.Rate = props.mVocalMorpher.flRate;
+    mAlProps = [&]{
+        VmorpherProps ret{};
+        ret.PhonemeA = get_phoneme(props.ulPhonemeA);
+        ret.PhonemeACoarseTuning = static_cast<int>(props.lPhonemeACoarseTuning);
+        ret.PhonemeB = get_phoneme(props.ulPhonemeB);
+        ret.PhonemeBCoarseTuning = static_cast<int>(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)
 {
-    props.mType = EaxEffectType::VocalMorpher;
-    props.mVocalMorpher.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA;
-    props.mVocalMorpher.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING;
-    props.mVocalMorpher.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB;
-    props.mVocalMorpher.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING;
-    props.mVocalMorpher.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM;
-    props.mVocalMorpher.flRate = EAXVOCALMORPHER_DEFAULTRATE;
+    static constexpr EAXVOCALMORPHERPROPERTIES defprops{[]
+    {
+        EAXVOCALMORPHERPROPERTIES ret{};
+        ret.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA;
+        ret.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING;
+        ret.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB;
+        ret.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING;
+        ret.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM;
+        ret.flRate = EAXVOCALMORPHER_DEFAULTRATE;
+        return ret;
+    }()};
+    props = defprops;
 }
 
-template<>
-void VocalMorpherCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxVocalMorpherCommitter::Get(const EaxCall &call, const EAXVOCALMORPHERPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
-    case EAXVOCALMORPHER_NONE:
-        break;
-
-    case EAXVOCALMORPHER_ALLPARAMETERS:
-        call.set_value<Exception>(props.mVocalMorpher);
-        break;
-
-    case EAXVOCALMORPHER_PHONEMEA:
-        call.set_value<Exception>(props.mVocalMorpher.ulPhonemeA);
-        break;
-
-    case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
-        call.set_value<Exception>(props.mVocalMorpher.lPhonemeACoarseTuning);
-        break;
-
-    case EAXVOCALMORPHER_PHONEMEB:
-        call.set_value<Exception>(props.mVocalMorpher.ulPhonemeB);
-        break;
-
-    case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
-        call.set_value<Exception>(props.mVocalMorpher.lPhonemeBCoarseTuning);
-        break;
-
-    case EAXVOCALMORPHER_WAVEFORM:
-        call.set_value<Exception>(props.mVocalMorpher.ulWaveform);
-        break;
-
-    case EAXVOCALMORPHER_RATE:
-        call.set_value<Exception>(props.mVocalMorpher.flRate);
-        break;
-
-    default:
-        fail_unknown_property_id();
+    case EAXVOCALMORPHER_NONE: break;
+    case EAXVOCALMORPHER_ALLPARAMETERS: call.set_value<Exception>(props); break;
+    case EAXVOCALMORPHER_PHONEMEA: call.set_value<Exception>(props.ulPhonemeA); break;
+    case EAXVOCALMORPHER_PHONEMEACOARSETUNING: call.set_value<Exception>(props.lPhonemeACoarseTuning); break;
+    case EAXVOCALMORPHER_PHONEMEB: call.set_value<Exception>(props.ulPhonemeB); break;
+    case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: call.set_value<Exception>(props.lPhonemeBCoarseTuning); break;
+    case EAXVOCALMORPHER_WAVEFORM: call.set_value<Exception>(props.ulWaveform); break;
+    case EAXVOCALMORPHER_RATE: call.set_value<Exception>(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)
 {
     switch(call.get_property_id())
     {
-    case EAXVOCALMORPHER_NONE:
-        break;
-
-    case EAXVOCALMORPHER_ALLPARAMETERS:
-        defer<AllValidator>(call, props.mVocalMorpher);
-        break;
-
-    case EAXVOCALMORPHER_PHONEMEA:
-        defer<PhonemeAValidator>(call, props.mVocalMorpher.ulPhonemeA);
-        break;
-
-    case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
-        defer<PhonemeACoarseTuningValidator>(call, props.mVocalMorpher.lPhonemeACoarseTuning);
-        break;
-
-    case EAXVOCALMORPHER_PHONEMEB:
-        defer<PhonemeBValidator>(call, props.mVocalMorpher.ulPhonemeB);
-        break;
-
-    case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
-        defer<PhonemeBCoarseTuningValidator>(call, props.mVocalMorpher.lPhonemeBCoarseTuning);
-        break;
-
-    case EAXVOCALMORPHER_WAVEFORM:
-        defer<WaveformValidator>(call, props.mVocalMorpher.ulWaveform);
-        break;
-
-    case EAXVOCALMORPHER_RATE:
-        defer<RateValidator>(call, props.mVocalMorpher.flRate);
-        break;
-
-    default:
-        fail_unknown_property_id();
+    case EAXVOCALMORPHER_NONE: break;
+    case EAXVOCALMORPHER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
+    case EAXVOCALMORPHER_PHONEMEA: defer<PhonemeAValidator>(call, props.ulPhonemeA); break;
+    case EAXVOCALMORPHER_PHONEMEACOARSETUNING: defer<PhonemeACoarseTuningValidator>(call, props.lPhonemeACoarseTuning); break;
+    case EAXVOCALMORPHER_PHONEMEB: defer<PhonemeBValidator>(call, props.ulPhonemeB); break;
+    case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: defer<PhonemeBCoarseTuningValidator>(call, props.lPhonemeBCoarseTuning); break;
+    case EAXVOCALMORPHER_WAVEFORM: defer<WaveformValidator>(call, props.ulWaveform); break;
+    case EAXVOCALMORPHER_RATE: defer<RateValidator>(call, props.flRate); break;
+    default: fail_unknown_property_id();
     }
 }
 

+ 76 - 25
Engine/lib/openal-soft/al/error.cpp

@@ -20,6 +20,8 @@
 
 #include "config.h"
 
+#include "error.h"
+
 #ifdef _WIN32
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
@@ -29,27 +31,45 @@
 #include <csignal>
 #include <cstdarg>
 #include <cstdio>
+#include <cstdlib>
 #include <cstring>
-#include <mutex>
+#include <limits>
+#include <optional>
+#include <string>
+#include <utility>
+#include <vector>
 
 #include "AL/al.h"
 #include "AL/alc.h"
 
+#include "al/debug.h"
+#include "alc/alconfig.h"
 #include "alc/context.h"
-#include "almalloc.h"
-#include "core/except.h"
+#include "alc/inprogext.h"
 #include "core/logging.h"
 #include "opthelpers.h"
-#include "vector.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 = al::vector<char>(256);
+    auto message = std::vector<char>(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)};
@@ -60,9 +80,15 @@ 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();
-    else msg = "<internal error constructing message>";
+    if(msglen >= 0)
+        msg = message.data();
+    else
+    {
+        msg = "<internal error constructing message>";
+        msglen = static_cast<int>(strlen(msg));
+    }
 
     WARN("Error generated on context %p, code 0x%04x, \"%s\"\n",
         decltype(std::declval<void*>()){this}, errorCode, msg);
@@ -77,30 +103,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<uint>(msglen)});
 }
 
-AL_API ALenum AL_APIENTRY alGetError(void)
-START_API_FUNC
+/* Special-case alGetError since it (potentially) raises a debug signal and
+ * returns a non-default value for a null context.
+ */
+AL_API auto AL_APIENTRY alGetError() noexcept -> ALenum
 {
-    ContextRef 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 constexpr ALenum deferror{AL_INVALID_OPERATION};
-        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<ALenum>::max())
+                return static_cast<ALenum>(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 deferror;
+}
 
-    return context->mLastError.exchange(AL_NO_ERROR);
+FORCE_ALIGN ALenum AL_APIENTRY alGetErrorDirect(ALCcontext *context) noexcept
+{
+    ALenum ret{context->mLastThreadError.get()};
+    if(ret != AL_NO_ERROR) UNLIKELY
+        context->mLastThreadError.set(AL_NO_ERROR);
+    return ret;
 }
-END_API_FUNC

+ 27 - 0
Engine/lib/openal-soft/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 */

+ 114 - 89
Engine/lib/openal-soft/al/event.cpp

@@ -3,35 +3,49 @@
 
 #include "event.h"
 
-#include <algorithm>
+#include <array>
 #include <atomic>
-#include <cstring>
+#include <bitset>
 #include <exception>
 #include <memory>
 #include <mutex>
 #include <new>
+#include <optional>
 #include <string>
+#include <string_view>
 #include <thread>
+#include <tuple>
 #include <utility>
+#include <variant>
 
 #include "AL/al.h"
 #include "AL/alc.h"
+#include "AL/alext.h"
 
-#include "albyte.h"
 #include "alc/context.h"
-#include "alc/effects/base.h"
-#include "alc/inprogext.h"
-#include "almalloc.h"
+#include "alsem.h"
+#include "alspan.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"
-#include "threads.h"
 
 
-static int EventThread(ALCcontext *context)
+namespace {
+
+template<typename... Ts>
+struct overloaded : Ts... { using Ts::operator()...; };
+
+template<typename... Ts>
+overloaded(Ts...) -> overloaded<Ts...>;
+
+int EventThread(ALCcontext *context)
 {
     RingBuffer *ring{context->mAsyncEvents.get()};
     bool quitnow{false};
@@ -44,76 +58,98 @@ static int EventThread(ALCcontext *context)
             continue;
         }
 
-        std::lock_guard<std::mutex> _{context->mEventCbLock};
-        do {
-            auto *evt_ptr = reinterpret_cast<AsyncEvent*>(evt_data.buf);
-            evt_data.buf += sizeof(AsyncEvent);
-            evt_data.len -= 1;
-
-            AsyncEvent evt{*evt_ptr};
-            al::destroy_at(evt_ptr);
-            ring->readAdvance(1);
-
-            quitnow = evt.EnumType == AsyncEvent::KillThread;
+        std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
+        auto evt_span = al::span{std::launder(reinterpret_cast<AsyncEvent*>(evt_data.buf)),
+            evt_data.len};
+        for(auto &event : evt_span)
+        {
+            quitnow = std::holds_alternative<AsyncKillThread>(event);
             if(quitnow) UNLIKELY break;
 
-            if(evt.EnumType == AsyncEvent::ReleaseEffectState)
-            {
-                al::intrusive_ptr<EffectState>{evt.u.mEffectState};
-                continue;
-            }
-
             auto enabledevts = context->mEnabledEvts.load(std::memory_order_acquire);
-            if(!context->mEventCb || !enabledevts.test(evt.EnumType))
-                continue;
-
-            if(evt.EnumType == AsyncEvent::SourceStateChange)
+            auto proc_killthread = [](AsyncKillThread&) { };
+            auto proc_release = [](AsyncEffectReleaseEvent &evt)
+            {
+                al::intrusive_ptr<EffectState>{evt.mEffectState};
+            };
+            auto proc_srcstate = [context,enabledevts](AsyncSourceStateEvent &evt)
             {
+                if(!context->mEventCb
+                    || !enabledevts.test(al::to_underlying(AsyncEnableBits::SourceState)))
+                    return;
+
                 ALuint state{};
-                std::string msg{"Source ID " + std::to_string(evt.u.srcstate.id)};
+                std::string msg{"Source ID " + std::to_string(evt.mId)};
                 msg += " state has changed to ";
-                switch(evt.u.srcstate.state)
+                switch(evt.mState)
                 {
-                case AsyncEvent::SrcState::Reset:
+                case AsyncSrcState::Reset:
                     msg += "AL_INITIAL";
                     state = AL_INITIAL;
                     break;
-                case AsyncEvent::SrcState::Stop:
+                case AsyncSrcState::Stop:
                     msg += "AL_STOPPED";
                     state = AL_STOPPED;
                     break;
-                case AsyncEvent::SrcState::Play:
+                case AsyncSrcState::Play:
                     msg += "AL_PLAYING";
                     state = AL_PLAYING;
                     break;
-                case AsyncEvent::SrcState::Pause:
+                case AsyncSrcState::Pause:
                     msg += "AL_PAUSED";
                     state = AL_PAUSED;
                     break;
                 }
-                context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.u.srcstate.id,
-                    state, static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
-            }
-            else if(evt.EnumType == AsyncEvent::BufferCompleted)
+                context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.mId, state,
+                    static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
+            };
+            auto proc_buffercomp = [context,enabledevts](AsyncBufferCompleteEvent &evt)
             {
-                std::string msg{std::to_string(evt.u.bufcomp.count)};
-                if(evt.u.bufcomp.count == 1) msg += " buffer completed";
+                if(!context->mEventCb
+                    || !enabledevts.test(al::to_underlying(AsyncEnableBits::BufferCompleted)))
+                    return;
+
+                std::string msg{std::to_string(evt.mCount)};
+                if(evt.mCount == 1) msg += " buffer completed";
                 else msg += " buffers completed";
-                context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.u.bufcomp.id,
-                    evt.u.bufcomp.count, static_cast<ALsizei>(msg.length()), msg.c_str(),
-                    context->mEventParam);
-            }
-            else if(evt.EnumType == AsyncEvent::Disconnected)
+                context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.mId, evt.mCount,
+                    static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
+            };
+            auto proc_disconnect = [context,enabledevts](AsyncDisconnectEvent &evt)
             {
-                context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0,
-                    static_cast<ALsizei>(strlen(evt.u.disconnect.msg)), evt.u.disconnect.msg,
-                    context->mEventParam);
-            }
-        } while(evt_data.len != 0);
+                context->debugMessage(DebugSource::System, DebugType::Error, 0,
+                    DebugSeverity::High, evt.msg);
+
+                if(context->mEventCb
+                    && enabledevts.test(al::to_underlying(AsyncEnableBits::Disconnected)))
+                    context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0,
+                        static_cast<ALsizei>(evt.msg.length()), evt.msg.c_str(),
+                        context->mEventParam);
+            };
+
+            std::visit(overloaded{proc_srcstate, proc_buffercomp, proc_release, proc_disconnect,
+                proc_killthread}, event);
+        }
+        std::destroy(evt_span.begin(), evt_span.end());
+        ring->readAdvance(evt_span.size());
     }
     return 0;
 }
 
+constexpr std::optional<AsyncEnableBits> GetEventType(ALenum etype) noexcept
+{
+    switch(etype)
+    {
+    case AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT: return AsyncEnableBits::BufferCompleted;
+    case AL_EVENT_TYPE_DISCONNECTED_SOFT: return AsyncEnableBits::Disconnected;
+    case AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT: return AsyncEnableBits::SourceState;
+    }
+    return std::nullopt;
+}
+
+} // namespace
+
+
 void StartEventThrd(ALCcontext *ctx)
 {
     try {
@@ -138,7 +174,7 @@ void StopEventThrd(ALCcontext *ctx)
             evt_data = ring->getWriteVector().first;
         } while(evt_data.len == 0);
     }
-    al::construct_at(reinterpret_cast<AsyncEvent*>(evt_data.buf), AsyncEvent::KillThread);
+    std::ignore = InitAsyncEvent<AsyncKillThread>(evt_data.buf);
     ring->writeAdvance(1);
 
     ctx->mEventSem.post();
@@ -146,34 +182,25 @@ void StopEventThrd(ALCcontext *ctx)
         ctx->mEventThread.join();
 }
 
-AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+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
+try {
+    if(count < 0)
+        throw al::context_error{AL_INVALID_VALUE, "Controlling %d events", count};
+    if(count <= 0) UNLIKELY return;
 
-    if(count < 0) context->setError(AL_INVALID_VALUE, "Controlling %d events", count);
-    if(count <= 0) return;
-    if(!types) return context->setError(AL_INVALID_VALUE, "NULL pointer");
+    if(!types)
+        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
 
     ContextBase::AsyncEventBitset flags{};
-    const ALenum *types_end = types+count;
-    auto bad_type = std::find_if_not(types, types_end,
-        [&flags](ALenum type) noexcept -> bool
-        {
-            if(type == AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT)
-                flags.set(AsyncEvent::BufferCompleted);
-            else if(type == AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT)
-                flags.set(AsyncEvent::SourceStateChange);
-            else if(type == AL_EVENT_TYPE_DISCONNECTED_SOFT)
-                flags.set(AsyncEvent::Disconnected);
-            else
-                return false;
-            return true;
-        }
-    );
-    if(bad_type != types_end)
-        return context->setError(AL_INVALID_ENUM, "Invalid event type 0x%04x", *bad_type);
+    for(ALenum evttype : al::span{types, static_cast<uint>(count)})
+    {
+        auto etype = GetEventType(evttype);
+        if(!etype)
+            throw al::context_error{AL_INVALID_ENUM, "Invalid event type 0x%04x", evttype};
+        flags.set(al::to_underlying(*etype));
+    }
 
     if(enable)
     {
@@ -196,20 +223,18 @@ START_API_FUNC
         /* Wait to ensure the event handler sees the changed flags before
          * returning.
          */
-        std::lock_guard<std::mutex> _{context->mEventCbLock};
+        std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
     }
 }
-END_API_FUNC
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
+}
 
-AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam)
-START_API_FUNC
+AL_API DECL_FUNCEXT2(void, alEventCallback,SOFT, ALEVENTPROCSOFT,callback, void*,userParam)
+FORCE_ALIGN void AL_APIENTRY alEventCallbackDirectSOFT(ALCcontext *context,
+    ALEVENTPROCSOFT callback, void *userParam) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    std::lock_guard<std::mutex> __{context->mEventCbLock};
+    std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
     context->mEventCb = callback;
     context->mEventParam = userParam;
 }
-END_API_FUNC

+ 24 - 28
Engine/lib/openal-soft/al/extension.cpp

@@ -20,63 +20,59 @@
 
 #include "config.h"
 
-#include <cctype>
-#include <cstdlib>
-#include <cstring>
+#include <string_view>
+#include <vector>
 
 #include "AL/al.h"
 #include "AL/alc.h"
 
 #include "alc/context.h"
+#include "alc/inprogext.h"
 #include "alstring.h"
-#include "core/except.h"
+#include "direct_defs.h"
 #include "opthelpers.h"
 
 
-AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extName)
-START_API_FUNC
+AL_API DECL_FUNC1(ALboolean, alIsExtensionPresent, const ALchar*,extName)
+FORCE_ALIGN ALboolean AL_APIENTRY alIsExtensionPresentDirect(ALCcontext *context, const ALchar *extName) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return AL_FALSE;
-
     if(!extName) UNLIKELY
     {
         context->setError(AL_INVALID_VALUE, "NULL pointer");
         return AL_FALSE;
     }
 
-    size_t len{strlen(extName)};
-    const char *ptr{context->mExtensionList};
-    while(ptr && *ptr)
+    const std::string_view tofind{extName};
+    for(std::string_view ext : context->mExtensions)
     {
-        if(al::strncasecmp(ptr, extName, len) == 0 && (ptr[len] == '\0' || isspace(ptr[len])))
+        if(al::case_compare(ext, tofind) == 0)
             return AL_TRUE;
-
-        if((ptr=strchr(ptr, ' ')) != nullptr)
-        {
-            do {
-                ++ptr;
-            } while(isspace(*ptr));
-        }
     }
 
     return AL_FALSE;
 }
-END_API_FUNC
 
 
-AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName)
-START_API_FUNC
+AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName) noexcept
 {
     if(!funcName) return nullptr;
     return alcGetProcAddress(nullptr, funcName);
 }
-END_API_FUNC
 
-AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName)
-START_API_FUNC
+FORCE_ALIGN ALvoid* AL_APIENTRY alGetProcAddressDirect(ALCcontext*, const ALchar *funcName) noexcept
+{
+    if(!funcName) return nullptr;
+    return alcGetProcAddress(nullptr, funcName);
+}
+
+AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName) noexcept
+{
+    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<ALenum>(0);
+    if(!enumName) return ALenum{0};
     return alcGetEnumValue(nullptr, enumName);
 }
-END_API_FUNC

+ 394 - 486
Engine/lib/openal-soft/al/filter.cpp

@@ -29,8 +29,10 @@
 #include <iterator>
 #include <memory>
 #include <mutex>
-#include <new>
 #include <numeric>
+#include <string>
+#include <unordered_map>
+#include <vector>
 
 #include "AL/al.h"
 #include "AL/alc.h"
@@ -39,252 +41,19 @@
 #include "albit.h"
 #include "alc/context.h"
 #include "alc/device.h"
+#include "alc/inprogext.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"
-#include "vector.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;
-
-
-#define DEFINE_ALFILTER_VTABLE(T)                                  \
-const ALfilter::Vtable T##_vtable = {                              \
-    T##_setParami, T##_setParamiv, T##_setParamf, T##_setParamfv,  \
-    T##_getParami, T##_getParamiv, T##_getParamf, T##_getParamfv,  \
-}
-
-void ALlowpass_setParami(ALfilter*, ALenum param, int)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
-void ALlowpass_setParamiv(ALfilter*, ALenum param, const int*)
-{
-    throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer-vector property 0x%04x",
-        param};
-}
-void ALlowpass_setParamf(ALfilter *filter, ALenum param, float val)
-{
-    switch(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};
-        filter->Gain = val;
-        break;
-
-    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};
-        filter->GainHF = val;
-        break;
-
-    default:
-        throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param};
-    }
-}
-void ALlowpass_setParamfv(ALfilter *filter, ALenum param, const float *vals)
-{ ALlowpass_setParamf(filter, param, vals[0]); }
-
-void ALlowpass_getParami(const ALfilter*, ALenum param, int*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
-void ALlowpass_getParamiv(const ALfilter*, ALenum param, int*)
-{
-    throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer-vector property 0x%04x",
-        param};
-}
-void ALlowpass_getParamf(const ALfilter *filter, ALenum param, float *val)
-{
-    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};
-    }
-}
-void ALlowpass_getParamfv(const ALfilter *filter, ALenum param, float *vals)
-{ ALlowpass_getParamf(filter, param, vals); }
-
-DEFINE_ALFILTER_VTABLE(ALlowpass);
-
-
-void ALhighpass_setParami(ALfilter*, ALenum param, int)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
-void ALhighpass_setParamiv(ALfilter*, ALenum param, const int*)
-{
-    throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer-vector property 0x%04x",
-        param};
-}
-void ALhighpass_setParamf(ALfilter *filter, ALenum param, float val)
-{
-    switch(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};
-        filter->Gain = val;
-        break;
-
-    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};
-        filter->GainLF = val;
-        break;
-
-    default:
-        throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param};
-    }
-}
-void ALhighpass_setParamfv(ALfilter *filter, ALenum param, const float *vals)
-{ ALhighpass_setParamf(filter, param, vals[0]); }
-
-void ALhighpass_getParami(const ALfilter*, ALenum param, int*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
-void ALhighpass_getParamiv(const ALfilter*, ALenum param, int*)
-{
-    throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer-vector property 0x%04x",
-        param};
-}
-void ALhighpass_getParamf(const ALfilter *filter, ALenum param, float *val)
-{
-    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};
-    }
-}
-void ALhighpass_getParamfv(const ALfilter *filter, ALenum param, float *vals)
-{ ALhighpass_getParamf(filter, param, vals); }
-
-DEFINE_ALFILTER_VTABLE(ALhighpass);
-
-
-void ALbandpass_setParami(ALfilter*, ALenum param, int)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
-void ALbandpass_setParamiv(ALfilter*, ALenum param, const int*)
-{
-    throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer-vector property 0x%04x",
-        param};
-}
-void ALbandpass_setParamf(ALfilter *filter, ALenum param, float val)
-{
-    switch(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};
-        filter->Gain = val;
-        break;
-
-    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};
-        filter->GainHF = val;
-        break;
-
-    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};
-        filter->GainLF = val;
-        break;
-
-    default:
-        throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param};
-    }
-}
-void ALbandpass_setParamfv(ALfilter *filter, ALenum param, const float *vals)
-{ ALbandpass_setParamf(filter, param, vals[0]); }
-
-void ALbandpass_getParami(const ALfilter*, ALenum param, int*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
-void ALbandpass_getParamiv(const ALfilter*, ALenum param, int*)
-{
-    throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer-vector property 0x%04x",
-        param};
-}
-void ALbandpass_getParamf(const ALfilter *filter, ALenum param, float *val)
-{
-    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};
-    }
-}
-void ALbandpass_getParamfv(const ALfilter *filter, ALenum param, float *vals)
-{ ALbandpass_getParamf(filter, param, vals); }
-
-DEFINE_ALFILTER_VTABLE(ALbandpass);
-
-
-void ALnullfilter_setParami(ALfilter*, ALenum param, int)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-void ALnullfilter_setParamiv(ALfilter*, ALenum param, const int*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-void ALnullfilter_setParamf(ALfilter*, ALenum param, float)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-void ALnullfilter_setParamfv(ALfilter*, ALenum param, const float*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-
-void ALnullfilter_getParami(const ALfilter*, ALenum param, int*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-void ALnullfilter_getParamiv(const ALfilter*, ALenum param, int*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-void ALnullfilter_getParamf(const ALfilter*, ALenum param, float*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-void ALnullfilter_getParamfv(const ALfilter*, ALenum param, float*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-
-DEFINE_ALFILTER_VTABLE(ALnullfilter);
+using SubListAllocator = al::allocator<std::array<ALfilter,64>>;
 
 
 void InitFilterParams(ALfilter *filter, ALenum type)
@@ -293,44 +62,44 @@ 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->vtab = &ALlowpass_vtable;
+        filter->LFReference = HighPassFreqRef;
+        filter->mTypeVariant.emplace<LowpassFilterTable>();
     }
     else if(type == AL_FILTER_HIGHPASS)
     {
         filter->Gain = AL_HIGHPASS_DEFAULT_GAIN;
         filter->GainHF = 1.0f;
-        filter->HFReference = LOWPASSFREQREF;
+        filter->HFReference = LowPassFreqRef;
         filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF;
-        filter->LFReference = HIGHPASSFREQREF;
-        filter->vtab = &ALhighpass_vtable;
+        filter->LFReference = HighPassFreqRef;
+        filter->mTypeVariant.emplace<HighpassFilterTable>();
     }
     else if(type == AL_FILTER_BANDPASS)
     {
         filter->Gain = AL_BANDPASS_DEFAULT_GAIN;
         filter->GainHF = AL_BANDPASS_DEFAULT_GAINHF;
-        filter->HFReference = LOWPASSFREQREF;
+        filter->HFReference = LowPassFreqRef;
         filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF;
-        filter->LFReference = HIGHPASSFREQREF;
-        filter->vtab = &ALbandpass_vtable;
+        filter->LFReference = HighPassFreqRef;
+        filter->mTypeVariant.emplace<BandpassFilterTable>();
     }
     else
     {
         filter->Gain = 1.0f;
         filter->GainHF = 1.0f;
-        filter->HFReference = LOWPASSFREQREF;
+        filter->HFReference = LowPassFreqRef;
         filter->GainLF = 1.0f;
-        filter->LFReference = HIGHPASSFREQREF;
-        filter->vtab = &ALnullfilter_vtable;
+        filter->LFReference = HighPassFreqRef;
+        filter->mTypeVariant.emplace<NullFilterTable>();
     }
     filter->type = type;
 }
 
-bool EnsureFilters(ALCdevice *device, size_t needed)
-{
-    size_t count{std::accumulate(device->FilterList.cbegin(), device->FilterList.cend(), size_t{0},
+auto EnsureFilters(ALCdevice *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<ALuint>(al::popcount(sublist.FreeMask)); })};
 
@@ -339,22 +108,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<ALfilter*>(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<SubListAllocator::value_type>;
     }
     return true;
 }
+catch(...) {
+    return false;
+}
 
 
-ALfilter *AllocFilter(ALCdevice *device)
+ALfilter *AllocFilter(ALCdevice *device) noexcept
 {
     auto sublist = std::find_if(device->FilterList.begin(), device->FilterList.end(),
         [](const FilterSubList &entry) noexcept -> bool
@@ -363,7 +130,7 @@ ALfilter *AllocFilter(ALCdevice *device)
     auto slidx = static_cast<ALuint>(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. */
@@ -376,17 +143,19 @@ ALfilter *AllocFilter(ALCdevice *device)
 
 void FreeFilter(ALCdevice *device, ALfilter *filter)
 {
+    device->mFilterNames.erase(filter->id);
+
     const ALuint id{filter->id - 1};
     const size_t lidx{id >> 6};
     const ALuint slidx{id & 0x3f};
 
-    al::destroy_at(filter);
+    std::destroy_at(filter);
 
     device->FilterList[lidx].FreeMask |= 1_u64 << slidx;
 }
 
 
-inline ALfilter *LookupFilter(ALCdevice *device, ALuint id)
+auto LookupFilter(ALCdevice *device, ALuint id) noexcept -> ALfilter*
 {
     const size_t lidx{(id-1) >> 6};
     const ALuint slidx{(id-1) & 0x3f};
@@ -396,327 +165,466 @@ 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
 
-AL_API void AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters)
-START_API_FUNC
+/* Null filter parameter handlers */
+template<>
+void FilterTable<NullFilterTable>::setParami(ALfilter*, ALenum param, int)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::setParamiv(ALfilter*, ALenum param, const int*)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::setParamf(ALfilter*, ALenum param, float)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::setParamfv(ALfilter*, ALenum param, const float*)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::getParami(const ALfilter*, ALenum param, int*)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::getParamiv(const ALfilter*, ALenum param, int*)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::getParamf(const ALfilter*, ALenum param, float*)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::getParamfv(const ALfilter*, ALenum param, float*)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+
+/* Lowpass parameter handlers */
+template<>
+void FilterTable<LowpassFilterTable>::setParami(ALfilter*, ALenum param, int)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
+template<>
+void FilterTable<LowpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values)
+{ setParami(filter, param, *values); }
+template<>
+void FilterTable<LowpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val)
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    if(n < 0) UNLIKELY
-        context->setError(AL_INVALID_VALUE, "Generating %d filters", n);
-    if(n <= 0) UNLIKELY return;
-
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->FilterLock};
-    if(!EnsureFilters(device, static_cast<ALuint>(n)))
+    switch(param)
     {
-        context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d filter%s", n, (n==1)?"":"s");
+    case AL_LOWPASS_GAIN:
+        if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN))
+            throw al::context_error{AL_INVALID_VALUE, "Low-pass gain %f out of range", val};
+        filter->Gain = val;
         return;
-    }
 
-    if(n == 1) LIKELY
+    case AL_LOWPASS_GAINHF:
+        if(!(val >= AL_LOWPASS_MIN_GAINHF && val <= AL_LOWPASS_MAX_GAINHF))
+            throw al::context_error{AL_INVALID_VALUE, "Low-pass gainhf %f out of range", val};
+        filter->GainHF = val;
+        return;
+    }
+    throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param};
+}
+template<>
+void FilterTable<LowpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals)
+{ setParamf(filter, param, *vals); }
+template<>
+void FilterTable<LowpassFilterTable>::getParami(const ALfilter*, ALenum param, int*)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
+template<>
+void FilterTable<LowpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values)
+{ getParami(filter, param, values); }
+template<>
+void FilterTable<LowpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val)
+{
+    switch(param)
     {
-        /* Special handling for the easy and normal case. */
-        ALfilter *filter{AllocFilter(device)};
-        if(filter) filters[0] = filter->id;
+    case AL_LOWPASS_GAIN: *val = filter->Gain; return;
+    case AL_LOWPASS_GAINHF: *val = filter->GainHF; return;
     }
-    else
+    throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param};
+}
+template<>
+void FilterTable<LowpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals)
+{ getParamf(filter, param, vals); }
+
+/* Highpass parameter handlers */
+template<>
+void FilterTable<HighpassFilterTable>::setParami(ALfilter*, ALenum param, int)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
+template<>
+void FilterTable<HighpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values)
+{ setParami(filter, param, *values); }
+template<>
+void FilterTable<HighpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val)
+{
+    switch(param)
+    {
+    case AL_HIGHPASS_GAIN:
+        if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN))
+            throw al::context_error{AL_INVALID_VALUE, "High-pass gain %f out of range", val};
+        filter->Gain = val;
+        return;
+
+    case AL_HIGHPASS_GAINLF:
+        if(!(val >= AL_HIGHPASS_MIN_GAINLF && val <= AL_HIGHPASS_MAX_GAINLF))
+            throw al::context_error{AL_INVALID_VALUE, "High-pass gainlf %f out of range", val};
+        filter->GainLF = val;
+        return;
+    }
+    throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param};
+}
+template<>
+void FilterTable<HighpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals)
+{ setParamf(filter, param, *vals); }
+template<>
+void FilterTable<HighpassFilterTable>::getParami(const ALfilter*, ALenum param, int*)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
+template<>
+void FilterTable<HighpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values)
+{ getParami(filter, param, values); }
+template<>
+void FilterTable<HighpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val)
+{
+    switch(param)
     {
-        /* Store the allocated buffer IDs in a separate local list, to avoid
-         * modifying the user storage in case of failure.
-         */
-        al::vector<ALuint> ids;
-        ids.reserve(static_cast<ALuint>(n));
-        do {
-            ALfilter *filter{AllocFilter(device)};
-            ids.emplace_back(filter->id);
-        } while(--n);
-        std::copy(ids.begin(), ids.end(), filters);
+    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};
 }
-END_API_FUNC
+template<>
+void FilterTable<HighpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals)
+{ getParamf(filter, param, vals); }
+
+/* Bandpass parameter handlers */
+template<>
+void FilterTable<BandpassFilterTable>::setParami(ALfilter*, ALenum param, int)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
+template<>
+void FilterTable<BandpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values)
+{ setParami(filter, param, *values); }
+template<>
+void FilterTable<BandpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val)
+{
+    switch(param)
+    {
+    case AL_BANDPASS_GAIN:
+        if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN))
+            throw al::context_error{AL_INVALID_VALUE, "Band-pass gain %f out of range", val};
+        filter->Gain = val;
+        return;
 
-AL_API void AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters)
-START_API_FUNC
+    case AL_BANDPASS_GAINHF:
+        if(!(val >= AL_BANDPASS_MIN_GAINHF && val <= AL_BANDPASS_MAX_GAINHF))
+            throw al::context_error{AL_INVALID_VALUE, "Band-pass gainhf %f out of range", val};
+        filter->GainHF = val;
+        return;
+
+    case AL_BANDPASS_GAINLF:
+        if(!(val >= AL_BANDPASS_MIN_GAINLF && val <= AL_BANDPASS_MAX_GAINLF))
+            throw al::context_error{AL_INVALID_VALUE, "Band-pass gainlf %f out of range", val};
+        filter->GainLF = val;
+        return;
+    }
+    throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param};
+}
+template<>
+void FilterTable<BandpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals)
+{ setParamf(filter, param, *vals); }
+template<>
+void FilterTable<BandpassFilterTable>::getParami(const ALfilter*, ALenum param, int*)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
+template<>
+void FilterTable<BandpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values)
+{ getParami(filter, param, values); }
+template<>
+void FilterTable<BandpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val)
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+    switch(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<BandpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals)
+{ getParamf(filter, param, vals); }
+
+
+AL_API DECL_FUNC2(void, alGenFilters, ALsizei,n, ALuint*,filters)
+FORCE_ALIGN void AL_APIENTRY alGenFiltersDirect(ALCcontext *context, ALsizei n, ALuint *filters) noexcept
+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<std::mutex> filterlock{device->FilterLock};
+
+    const al::span fids{filters, static_cast<ALuint>(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());
+}
 
-    if(n < 0) UNLIKELY
-        context->setError(AL_INVALID_VALUE, "Deleting %d filters", n);
+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
+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<std::mutex> _{device->FilterLock};
+    std::lock_guard<std::mutex> filterlock{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<ALuint>(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());
 }
-END_API_FUNC
 
-AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter)
-START_API_FUNC
+AL_API DECL_FUNC1(ALboolean, alIsFilter, ALuint,filter)
+FORCE_ALIGN ALboolean AL_APIENTRY alIsFilterDirect(ALCcontext *context, ALuint filter) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(context) LIKELY
-    {
-        ALCdevice *device{context->mALDevice.get()};
-        std::lock_guard<std::mutex> _{device->FilterLock};
-        if(!filter || LookupFilter(device, filter))
-            return AL_TRUE;
-    }
+    ALCdevice *device{context->mALDevice.get()};
+    std::lock_guard<std::mutex> filterlock{device->FilterLock};
+    if(!filter || LookupFilter(device, filter))
+        return AL_TRUE;
     return AL_FALSE;
 }
-END_API_FUNC
-
 
-AL_API void AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
 
+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
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->FilterLock};
+    std::lock_guard<std::mutex> filterlock{device->FilterLock};
 
     ALfilter *alfilt{LookupFilter(device, filter)};
-    if(!alfilt) UNLIKELY
-        context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
-    else
+    if(!alfilt)
+        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
+
+    switch(param)
     {
-        if(param == AL_FILTER_TYPE)
-        {
-            if(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS
-                || value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS)
-                InitFilterParams(alfilt, value);
-            else
-                context->setError(AL_INVALID_VALUE, "Invalid filter type 0x%04x", value);
-        }
-        else try
-        {
-            /* Call the appropriate handler */
-            alfilt->setParami(param, value);
-        }
-        catch(filter_exception &e) {
-            context->setError(e.errorCode(), "%s", e.what());
-        }
+    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());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *values)
-START_API_FUNC
-{
+AL_API DECL_FUNC3(void, alFilteriv, ALuint,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:
-        alFilteri(filter, param, values[0]);
+        alFilteriDirect(context, filter, param, *values);
         return;
     }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->FilterLock};
+    std::lock_guard<std::mutex> filterlock{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 */
-        alfilt->setParamiv(param, values);
-    }
-    catch(filter_exception &e) {
-        context->setError(e.errorCode(), "%s", e.what());
-    }
-}
-END_API_FUNC
+    if(!alfilt)
+        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
 
-AL_API void AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+    /* 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,filter, ALenum,param, ALfloat,value)
+FORCE_ALIGN void AL_APIENTRY alFilterfDirect(ALCcontext *context, ALuint filter, ALenum param,
+    ALfloat value) noexcept
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->FilterLock};
+    std::lock_guard<std::mutex> filterlock{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 */
-        alfilt->setParamf(param, value);
-    }
-    catch(filter_exception &e) {
-        context->setError(e.errorCode(), "%s", e.what());
-    }
-}
-END_API_FUNC
+    if(!alfilt)
+        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
 
-AL_API void AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *values)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+    /* 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,filter, ALenum,param, const ALfloat*,values)
+FORCE_ALIGN void AL_APIENTRY alFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param,
+    const ALfloat *values) noexcept
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->FilterLock};
+    std::lock_guard<std::mutex> filterlock{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 */
-        alfilt->setParamfv(param, values);
-    }
-    catch(filter_exception &e) {
-        context->setError(e.errorCode(), "%s", e.what());
-    }
-}
-END_API_FUNC
+    if(!alfilt)
+        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
 
-AL_API void AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+    /* 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,filter, ALenum,param, ALint*,value)
+FORCE_ALIGN void AL_APIENTRY alGetFilteriDirect(ALCcontext *context, ALuint filter, ALenum param,
+    ALint *value) noexcept
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->FilterLock};
+    std::lock_guard<std::mutex> filterlock{device->FilterLock};
 
     const ALfilter *alfilt{LookupFilter(device, filter)};
-    if(!alfilt) UNLIKELY
-        context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
-    else
+    if(!alfilt)
+        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
+
+    switch(param)
     {
-        if(param == AL_FILTER_TYPE)
-            *value = alfilt->type;
-        else try
-        {
-            /* Call the appropriate handler */
-            alfilt->getParami(param, value);
-        }
-        catch(filter_exception &e) {
-            context->setError(e.errorCode(), "%s", e.what());
-        }
+    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());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *values)
-START_API_FUNC
-{
+AL_API DECL_FUNC3(void, alGetFilteriv, ALuint,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:
-        alGetFilteri(filter, param, values);
+        alGetFilteriDirect(context, filter, param, values);
         return;
     }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+    ALCdevice *device{context->mALDevice.get()};
+    std::lock_guard<std::mutex> filterlock{device->FilterLock};
+
+    const ALfilter *alfilt{LookupFilter(device, filter)};
+    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,filter, ALenum,param, ALfloat*,value)
+FORCE_ALIGN void AL_APIENTRY alGetFilterfDirect(ALCcontext *context, ALuint filter, ALenum param,
+    ALfloat *value) noexcept
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->FilterLock};
+    std::lock_guard<std::mutex> filterlock{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 */
-        alfilt->getParamiv(param, values);
-    }
-    catch(filter_exception &e) {
-        context->setError(e.errorCode(), "%s", e.what());
-    }
-}
-END_API_FUNC
+        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
 
-AL_API void AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+    /* 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,filter, ALenum,param, ALfloat*,values)
+FORCE_ALIGN void AL_APIENTRY alGetFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param,
+    ALfloat *values) noexcept
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->FilterLock};
+    std::lock_guard<std::mutex> filterlock{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 */
-        alfilt->getParamf(param, value);
-    }
-    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());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *values)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
 
+void ALfilter::SetName(ALCcontext *context, ALuint id, std::string_view name)
+{
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->FilterLock};
+    std::lock_guard<std::mutex> filterlock{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 */
-        alfilt->getParamfv(param, values);
-    }
-    catch(filter_exception &e) {
-        context->setError(e.errorCode(), "%s", e.what());
-    }
+    auto filter = LookupFilter(device, id);
+    if(!filter)
+        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", id};
+
+    device->mFilterNames.insert_or_assign(id, name);
 }
-END_API_FUNC
 
 
 FilterSubList::~FilterSubList()
 {
+    if(!Filters)
+        return;
+
     uint64_t usemask{~FreeMask};
     while(usemask)
     {
         const int idx{al::countr_zero(usemask)};
-        al::destroy_at(Filters+idx);
+        std::destroy_at(al::to_address(Filters->begin() + idx));
         usemask &= ~(1_u64 << idx);
     }
     FreeMask = ~usemask;
-    al_free(Filters);
+    SubListAllocator{}.deallocate(Filters, 1);
     Filters = nullptr;
 }

+ 50 - 26
Engine/lib/openal-soft/al/filter.h

@@ -1,15 +1,40 @@
 #ifndef AL_FILTER_H
 #define AL_FILTER_H
 
+#include <array>
+#include <cstdint>
+#include <string_view>
+#include <utility>
+#include <variant>
 
 #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<typename T>
+struct FilterTable {
+    static void setParami(struct ALfilter*, ALenum, int);
+    static void setParamiv(struct ALfilter*, ALenum, const int*);
+    static void setParamf(struct ALfilter*, ALenum, float);
+    static void setParamfv(struct ALfilter*, ALenum, const float*);
+
+    static void getParami(const struct ALfilter*, ALenum, int*);
+    static void getParamiv(const struct ALfilter*, ALenum, int*);
+    static void getParamf(const struct ALfilter*, ALenum, float*);
+    static void getParamfv(const struct ALfilter*, ALenum, float*);
+};
+
+struct NullFilterTable : public FilterTable<NullFilterTable> { };
+struct LowpassFilterTable : public FilterTable<LowpassFilterTable> { };
+struct HighpassFilterTable : public FilterTable<HighpassFilterTable> { };
+struct BandpassFilterTable : public FilterTable<BandpassFilterTable> { };
 
 
 struct ALfilter {
@@ -17,36 +42,35 @@ 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};
 
-    struct Vtable {
-        void (*const setParami )(ALfilter *filter, ALenum param, int val);
-        void (*const setParamiv)(ALfilter *filter, ALenum param, const int *vals);
-        void (*const setParamf )(ALfilter *filter, ALenum param, float val);
-        void (*const setParamfv)(ALfilter *filter, ALenum param, const float *vals);
-
-        void (*const getParami )(const ALfilter *filter, ALenum param, int *val);
-        void (*const getParamiv)(const ALfilter *filter, ALenum param, int *vals);
-        void (*const getParamf )(const ALfilter *filter, ALenum param, float *val);
-        void (*const getParamfv)(const ALfilter *filter, ALenum param, float *vals);
-    };
-    const Vtable *vtab{nullptr};
+    using TableTypes = std::variant<NullFilterTable,LowpassFilterTable,HighpassFilterTable,
+        BandpassFilterTable>;
+    TableTypes mTypeVariant;
 
     /* Self ID */
     ALuint id{0};
 
-    void setParami(ALenum param, int value) { vtab->setParami(this, param, value); }
-    void setParamiv(ALenum param, const int *values) { vtab->setParamiv(this, param, values); }
-    void setParamf(ALenum param, float value) { vtab->setParamf(this, param, value); }
-    void setParamfv(ALenum param, const float *values) { vtab->setParamfv(this, param, values); }
-    void getParami(ALenum param, int *value) const { vtab->getParami(this, param, value); }
-    void getParamiv(ALenum param, int *values) const { vtab->getParamiv(this, param, values); }
-    void getParamf(ALenum param, float *value) const { vtab->getParamf(this, param, value); }
-    void getParamfv(ALenum param, float *values) const { vtab->getParamfv(this, param, values); }
+    static void SetName(ALCcontext *context, ALuint id, std::string_view name);
+
+    DISABLE_ALLOC
+};
+
+struct FilterSubList {
+    uint64_t FreeMask{~0_u64};
+    gsl::owner<std::array<ALfilter,64>*> 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();
 
-    DISABLE_ALLOC()
+    FilterSubList& operator=(const FilterSubList&) = delete;
+    FilterSubList& operator=(FilterSubList&& rhs) noexcept
+    { std::swap(FreeMask, rhs.FreeMask); std::swap(Filters, rhs.Filters); return *this; }
 };
 
 #endif

+ 203 - 245
Engine/lib/openal-soft/al/listener.cpp

@@ -22,6 +22,7 @@
 
 #include "listener.h"
 
+#include <algorithm>
 #include <cmath>
 #include <mutex>
 
@@ -30,9 +31,10 @@
 #include "AL/efx.h"
 
 #include "alc/context.h"
-#include "almalloc.h"
-#include "atomic.h"
-#include "core/except.h"
+#include "alc/inprogext.h"
+#include "alspan.h"
+#include "direct_defs.h"
+#include "error.h"
 #include "opthelpers.h"
 
 
@@ -68,377 +70,333 @@ inline void CommitAndUpdateProps(ALCcontext *context)
 
 } // namespace
 
-AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
+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<std::mutex> _{context->mPropLock};
+    std::lock_guard<std::mutex> 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.get());
-        break;
+        UpdateProps(context);
+        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.get());
-        break;
-
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener float property");
+        UpdateProps(context);
+        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());
 }
-END_API_FUNC
-
-AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
 
+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<std::mutex> _{context->mPropLock};
+    std::lock_guard<std::mutex> 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.get());
-        break;
+        CommitAndUpdateProps(context);
+        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.get());
-        break;
-
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener 3-float property");
+        CommitAndUpdateProps(context);
+        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());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
-START_API_FUNC
-{
-    if(values)
+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
+try {
+    if(!values)
+        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
+
+    switch(param)
     {
-        switch(param)
-        {
-        case AL_GAIN:
-        case AL_METERS_PER_UNIT:
-            alListenerf(param, values[0]);
-            return;
+    case AL_GAIN:
+    case AL_METERS_PER_UNIT:
+        alListenerfDirect(context, param, *values);
+        return;
 
-        case AL_POSITION:
-        case AL_VELOCITY:
-            alListener3f(param, values[0], values[1], values[2]);
-            return;
-        }
+    case AL_POSITION:
+    case AL_VELOCITY:
+        auto vals = al::span<const float,3>{values, 3_uz};
+        alListener3fDirect(context, param, vals[0], vals[1], vals[2]);
+        return;
     }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    if(!values) UNLIKELY
-        return context->setError(AL_INVALID_VALUE, "NULL pointer");
-
     ALlistener &listener = context->mListener;
-    std::lock_guard<std::mutex> _{context->mPropLock};
+    std::lock_guard<std::mutex> 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<const float,6>{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];
-        CommitAndUpdateProps(context.get());
-        break;
-
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener float-vector property");
+        std::copy_n(vals.cbegin(), 3, listener.OrientAt.begin());
+        std::copy_n(vals.cbegin()+3, 3, listener.OrientUp.begin());
+        CommitAndUpdateProps(context);
+        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());
 }
-END_API_FUNC
 
 
-AL_API void AL_APIENTRY alListeneri(ALenum param, ALint /*value*/)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    switch(param)
-    {
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener integer property");
-    }
+AL_API DECL_FUNC2(void, alListeneri, ALenum,param, ALint,value)
+FORCE_ALIGN void AL_APIENTRY alListeneriDirect(ALCcontext *context, ALenum param, ALint /*value*/) noexcept
+try {
+    std::lock_guard<std::mutex> 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());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3)
-START_API_FUNC
-{
+AL_API DECL_FUNC4(void, alListener3i, ALenum,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:
     case AL_VELOCITY:
-        alListener3f(param, static_cast<ALfloat>(value1), static_cast<ALfloat>(value2),
-            static_cast<ALfloat>(value3));
+        alListener3fDirect(context, param, static_cast<ALfloat>(value1),
+            static_cast<ALfloat>(value2), static_cast<ALfloat>(value3));
         return;
     }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    switch(param)
-    {
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property");
-    }
+    std::lock_guard<std::mutex> 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());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values)
-START_API_FUNC
-{
-    if(values)
+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
+try {
+    if(!values)
+        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
+
+    al::span<const ALint> vals;
+    switch(param)
     {
-        ALfloat fvals[6];
-        switch(param)
-        {
-        case AL_POSITION:
-        case AL_VELOCITY:
-            alListener3f(param, static_cast<ALfloat>(values[0]), static_cast<ALfloat>(values[1]),
-                static_cast<ALfloat>(values[2]));
-            return;
+    case AL_POSITION:
+    case AL_VELOCITY:
+        vals = {values, 3_uz};
+        alListener3fDirect(context, param, static_cast<ALfloat>(vals[0]),
+            static_cast<ALfloat>(vals[1]), static_cast<ALfloat>(vals[2]));
+        return;
 
-        case AL_ORIENTATION:
-            fvals[0] = static_cast<ALfloat>(values[0]);
-            fvals[1] = static_cast<ALfloat>(values[1]);
-            fvals[2] = static_cast<ALfloat>(values[2]);
-            fvals[3] = static_cast<ALfloat>(values[3]);
-            fvals[4] = static_cast<ALfloat>(values[4]);
-            fvals[5] = static_cast<ALfloat>(values[5]);
-            alListenerfv(param, fvals);
-            return;
-        }
+    case AL_ORIENTATION:
+        vals = {values, 6_uz};
+        const std::array fvals{static_cast<ALfloat>(vals[0]), static_cast<ALfloat>(vals[1]),
+            static_cast<ALfloat>(vals[2]), static_cast<ALfloat>(vals[3]),
+            static_cast<ALfloat>(vals[4]), static_cast<ALfloat>(vals[5]),
+        };
+        alListenerfvDirect(context, param, fvals.data());
+        return;
     }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    if(!values) UNLIKELY
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(param)
-    {
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener integer-vector property");
-    }
+    std::lock_guard<std::mutex> 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());
 }
-END_API_FUNC
 
 
-AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+AL_API DECL_FUNC2(void, alGetListenerf, ALenum,param, ALfloat*,value)
+FORCE_ALIGN void AL_APIENTRY alGetListenerfDirect(ALCcontext *context, ALenum param,
+    ALfloat *value) noexcept
+try {
+    if(!value)
+        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
 
     ALlistener &listener = context->mListener;
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    if(!value)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(param)
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
+    switch(param)
     {
-    case AL_GAIN:
-        *value = listener.Gain;
-        break;
-
-    case AL_METERS_PER_UNIT:
-        *value = listener.mMetersPerUnit;
-        break;
-
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener float property");
+    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());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+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
+try {
+    if(!value1 || !value2 || !value3)
+        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
 
     ALlistener &listener = context->mListener;
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    if(!value1 || !value2 || !value3)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(param)
+    std::lock_guard<std::mutex> 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());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
-START_API_FUNC
-{
+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:
     case AL_METERS_PER_UNIT:
-        alGetListenerf(param, values);
+        alGetListenerfDirect(context, param, values);
         return;
 
     case AL_POSITION:
     case AL_VELOCITY:
-        alGetListener3f(param, values+0, values+1, values+2);
+        auto vals = al::span<ALfloat,3>{values, 3_uz};
+        alGetListener3fDirect(context, param, &vals[0], &vals[1], &vals[2]);
         return;
     }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
     ALlistener &listener = context->mListener;
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    if(!values)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(param)
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
+    switch(param)
     {
     case AL_ORIENTATION:
+        al::span<ALfloat,6> 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());
 }
-END_API_FUNC
 
 
-AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    if(!value)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(param)
-    {
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener integer property");
-    }
+AL_API DECL_FUNC2(void, alGetListeneri, ALenum,param, ALint*,value)
+FORCE_ALIGN void AL_APIENTRY alGetListeneriDirect(ALCcontext *context, ALenum param, ALint *value) noexcept
+try {
+    if(!value) throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
+    std::lock_guard<std::mutex> 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());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+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
+try {
+    if(!value1 || !value2 || !value3)
+        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
 
     ALlistener &listener = context->mListener;
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    if(!value1 || !value2 || !value3)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(param)
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
+    switch(param)
     {
     case AL_POSITION:
         *value1 = static_cast<ALint>(listener.Position[0]);
         *value2 = static_cast<ALint>(listener.Position[1]);
         *value3 = static_cast<ALint>(listener.Position[2]);
-        break;
+        return;
 
     case AL_VELOCITY:
         *value1 = static_cast<ALint>(listener.Velocity[0]);
         *value2 = static_cast<ALint>(listener.Velocity[1]);
         *value3 = static_cast<ALint>(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());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
-START_API_FUNC
-{
+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:
-        alGetListener3i(param, values+0, values+1, values+2);
+        auto vals = al::span<ALint,3>{values, 3_uz};
+        alGetListener3iDirect(context, param, &vals[0], &vals[1], &vals[2]);
         return;
     }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
     ALlistener &listener = context->mListener;
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    if(!values)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(param)
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
+
+    static constexpr auto f2i = [](const float val) noexcept { return static_cast<ALint>(val); };
+    switch(param)
     {
     case AL_ORIENTATION:
+        auto vals = al::span<ALint,6>{values, 6_uz};
         // AT then UP
-        values[0] = static_cast<ALint>(listener.OrientAt[0]);
-        values[1] = static_cast<ALint>(listener.OrientAt[1]);
-        values[2] = static_cast<ALint>(listener.OrientAt[2]);
-        values[3] = static_cast<ALint>(listener.OrientUp[0]);
-        values[4] = static_cast<ALint>(listener.OrientUp[1]);
-        values[5] = static_cast<ALint>(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());
 }
-END_API_FUNC

+ 1 - 3
Engine/lib/openal-soft/al/listener.h

@@ -3,8 +3,6 @@
 
 #include <array>
 
-#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

Dosya farkı çok büyük olduğundan ihmal edildi
+ 969 - 1211
Engine/lib/openal-soft/al/source.cpp


+ 110 - 83
Engine/lib/openal-soft/al/source.h

@@ -2,26 +2,26 @@
 #define AL_SOURCE_H
 
 #include <array>
-#include <atomic>
 #include <cstddef>
-#include <iterator>
-#include <limits>
+#include <cstdint>
 #include <deque>
+#include <limits>
+#include <string_view>
+#include <utility>
 
 #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
+#include "eax/api.h"
 #include "eax/call.h"
 #include "eax/exception.h"
 #include "eax/fx_slot_index.h"
@@ -30,23 +30,23 @@
 
 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<ALuint>(-1)
+inline constexpr ALuint InvalidVoiceIndex{std::numeric_limits<ALuint>::max()};
 
-extern bool sBufferSubDataCompat;
+inline bool sBufferSubDataCompat{false};
 
 struct ALbufferQueueItem : public VoiceBufferItem {
     ALbuffer *mBuffer{nullptr};
 
-    DISABLE_ALLOC()
+    DISABLE_ALLOC
 };
 
 
@@ -88,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};
@@ -105,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<SendData,MAX_SENDS> Send;
+    std::array<SendData,MaxSendCount> Send;
 
     /**
      * Last user-specified offset, and the offset type (bytes, samples, or
@@ -138,26 +142,28 @@ struct ALsource {
     ALenum state{AL_INITIAL};
 
     /** Source Buffer Queue head. */
-    al::deque<ALbufferQueueItem> mQueue;
+    std::deque<ALbufferQueueItem> mQueue;
 
     bool mPropsDirty{true};
 
     /* Index into the context's Voices array. Lazily updated, only checked and
      * reset when looking up the voice.
      */
-    ALuint VoiceIdx{INVALID_VOICE_IDX};
+    ALuint VoiceIdx{InvalidVoiceIndex};
 
     /** Self ID */
     ALuint id{0};
 
 
-    ALsource();
+    ALsource() noexcept;
     ~ALsource();
 
     ALsource(const ALsource&) = delete;
     ALsource& operator=(const ALsource&) = delete;
 
-    DISABLE_ALLOC()
+    static void SetName(ALCcontext *context, ALuint id, std::string_view name);
+
+    DISABLE_ALLOC
 
 #ifdef ALSOFT_EAX
 public:
@@ -171,18 +177,18 @@ public:
 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<const GUID*,EAX_MAX_FXSLOTS>;
 
-    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,
@@ -215,11 +221,6 @@ private:
         Eax3Props source;
         EaxSends sends;
         EAX40ACTIVEFXSLOTS active_fx_slots;
-
-        bool operator==(const Eax4Props& rhs) noexcept
-        {
-            return std::memcmp(this, &rhs, sizeof(Eax4Props)) == 0;
-        }
     };
 
     struct Eax4State {
@@ -232,11 +233,6 @@ private:
         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 {
@@ -546,7 +542,24 @@ private:
     struct Eax5SourceAllValidator {
         void operator()(const EAX50SOURCEPROPERTIES& props) const
         {
-            Eax3SourceAllValidator{}(static_cast<const Eax3Props&>(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);
         }
     };
@@ -809,39 +822,38 @@ private:
     [[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();
@@ -894,16 +906,16 @@ private:
         }
     }
 
-    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<const GUID> 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);
 
@@ -976,21 +988,21 @@ private:
     }
 
     template<typename TValidator, size_t TIdCount>
-    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<GUID,TIdCount> dst_ids)
     {
         const auto src_ids = call.get_values<const GUID>(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<size_t TIdCount>
-    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<GUID,TIdCount> dst_ids)
     {
         eax_defer_active_fx_slot_id<Eax4ActiveFxSlotIdValidator>(call, dst_ids);
     }
 
     template<size_t TIdCount>
-    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<GUID,TIdCount> dst_ids)
     {
         eax_defer_active_fx_slot_id<Eax5ActiveFxSlotIdValidator>(call, dst_ids);
     }
@@ -1022,12 +1034,12 @@ private:
     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);
 
@@ -1041,4 +1053,19 @@ private:
 
 void UpdateAllSourceProps(ALCcontext *context);
 
+struct SourceSubList {
+    uint64_t FreeMask{~0_u64};
+    gsl::owner<std::array<ALsource,64>*> 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

+ 330 - 666
Engine/lib/openal-soft/al/state.cpp

@@ -22,26 +22,32 @@
 
 #include "version.h"
 
+#include <array>
 #include <atomic>
 #include <cmath>
+#include <deque>
 #include <mutex>
+#include <optional>
 #include <stdexcept>
 #include <string>
+#include <utility>
 
 #include "AL/al.h"
 #include "AL/alc.h"
 #include "AL/alext.h"
 
+#include "al/debug.h"
+#include "al/listener.h"
 #include "alc/alu.h"
 #include "alc/context.h"
 #include "alc/inprogext.h"
 #include "alnumeric.h"
-#include "aloptional.h"
 #include "atomic.h"
 #include "core/context.h"
-#include "core/except.h"
+#include "core/logging.h"
 #include "core/mixer/defs.h"
 #include "core/voice.h"
+#include "direct_defs.h"
 #include "intrusive_ptr.h"
 #include "opthelpers.h"
 #include "strutils.h"
@@ -56,17 +62,19 @@
 
 namespace {
 
-constexpr ALchar alVendor[] = "OpenAL Community";
-constexpr ALchar alVersion[] = "1.1 ALSOFT " ALSOFT_VERSION;
-constexpr ALchar alRenderer[] = "OpenAL Soft";
+[[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
-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";
+/* 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<Resampler rtype> struct ResamplerName { };
@@ -74,8 +82,10 @@ template<> struct ResamplerName<Resampler::Point>
 { static constexpr const ALchar *Get() noexcept { return "Nearest"; } };
 template<> struct ResamplerName<Resampler::Linear>
 { static constexpr const ALchar *Get() noexcept { return "Linear"; } };
-template<> struct ResamplerName<Resampler::Cubic>
-{ static constexpr const ALchar *Get() noexcept { return "Cubic"; } };
+template<> struct ResamplerName<Resampler::Spline>
+{ static constexpr const ALchar *Get() noexcept { return "Cubic Spline"; } };
+template<> struct ResamplerName<Resampler::Gaussian>
+{ static constexpr const ALchar *Get() noexcept { return "4-point Gaussian"; } };
 template<> struct ResamplerName<Resampler::FastBSinc12>
 { static constexpr const ALchar *Get() noexcept { return "11th order Sinc (fast)"; } };
 template<> struct ResamplerName<Resampler::BSinc12>
@@ -92,7 +102,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);
@@ -103,7 +114,7 @@ const ALchar *GetResamplerName(const Resampler rtype)
     throw std::runtime_error{"Unexpected resampler index"};
 }
 
-al::optional<DistanceModel> DistanceModelFromALenum(ALenum model)
+constexpr auto DistanceModelFromALenum(ALenum model) noexcept -> std::optional<DistanceModel>
 {
     switch(model)
     {
@@ -115,9 +126,9 @@ al::optional<DistanceModel> DistanceModelFromALenum(ALenum model)
     case AL_EXPONENT_DISTANCE: return DistanceModel::Exponent;
     case AL_EXPONENT_DISTANCE_CLAMPED: return DistanceModel::ExponentClamped;
     }
-    return al::nullopt;
+    return std::nullopt;
 }
-ALenum ALenumFromDistanceModel(DistanceModel model)
+constexpr auto ALenumFromDistanceModel(DistanceModel model) -> ALenum
 {
     switch(model)
     {
@@ -132,810 +143,463 @@ ALenum ALenumFromDistanceModel(DistanceModel model)
     throw std::runtime_error{"Unexpected distance model "+std::to_string(static_cast<int>(model))};
 }
 
-} // namespace
-
-/* WARNING: Non-standard export! Not part of any extension, or exposed in the
- * alcFunctions list.
- */
-AL_API const ALchar* AL_APIENTRY alsoft_get_version(void)
-START_API_FUNC
-{
-    static const auto spoof = al::getenv("ALSOFT_SPOOF_VERSION");
-    if(spoof) return spoof->c_str();
-    return ALSOFT_VERSION;
-}
-END_API_FUNC
-
-#define DO_UPDATEPROPS() do {                                                 \
-    if(!context->mDeferUpdates)                                               \
-        UpdateContextProps(context.get());                                    \
-    else                                                                      \
-        context->mPropsDirty = true;                                          \
-} while(0)
-
-
-AL_API void AL_APIENTRY alEnable(ALenum capability)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    switch(capability)
-    {
-    case AL_SOURCE_DISTANCE_MODEL:
-        {
-            std::lock_guard<std::mutex> _{context->mPropLock};
-            context->mSourceDistanceModel = true;
-            DO_UPDATEPROPS();
-        }
-        break;
-
-    case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
-        context->setError(AL_INVALID_OPERATION, "Re-enabling AL_STOP_SOURCES_ON_DISCONNECT_SOFT not yet supported");
-        break;
-
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid enable property 0x%04x", capability);
-    }
-}
-END_API_FUNC
-
-AL_API void AL_APIENTRY alDisable(ALenum capability)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    switch(capability)
-    {
-    case AL_SOURCE_DISTANCE_MODEL:
-        {
-            std::lock_guard<std::mutex> _{context->mPropLock};
-            context->mSourceDistanceModel = false;
-            DO_UPDATEPROPS();
-        }
-        break;
-
-    case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
-        context->mStopVoicesOnDisconnect = false;
-        break;
-
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid disable property 0x%04x", capability);
-    }
-}
-END_API_FUNC
-
-AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return AL_FALSE;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    ALboolean value{AL_FALSE};
-    switch(capability)
-    {
-    case AL_SOURCE_DISTANCE_MODEL:
-        value = context->mSourceDistanceModel ? AL_TRUE : AL_FALSE;
-        break;
-
-    case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
-        value = context->mStopVoicesOnDisconnect ? AL_TRUE : AL_FALSE;
-        break;
-
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid is enabled property 0x%04x", capability);
-    }
-
-    return value;
-}
-END_API_FUNC
-
-AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum pname)
-START_API_FUNC
+enum PropertyValue : ALenum {
+    DopplerFactor = AL_DOPPLER_FACTOR,
+    DopplerVelocity = AL_DOPPLER_VELOCITY,
+    DistanceModel = AL_DISTANCE_MODEL,
+    SpeedOfSound = AL_SPEED_OF_SOUND,
+    DeferredUpdates = AL_DEFERRED_UPDATES_SOFT,
+    GainLimit = AL_GAIN_LIMIT_SOFT,
+    NumResamplers = AL_NUM_RESAMPLERS_SOFT,
+    DefaultResampler = AL_DEFAULT_RESAMPLER_SOFT,
+    DebugLoggedMessages = AL_DEBUG_LOGGED_MESSAGES_EXT,
+    DebugNextLoggedMessageLength = AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT,
+    MaxDebugMessageLength = AL_MAX_DEBUG_MESSAGE_LENGTH_EXT,
+    MaxDebugLoggedMessages = AL_MAX_DEBUG_LOGGED_MESSAGES_EXT,
+    MaxDebugGroupDepth = AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT,
+    MaxLabelLength = AL_MAX_LABEL_LENGTH_EXT,
+    ContextFlags = AL_CONTEXT_FLAGS_EXT,
+#ifdef ALSOFT_EAX
+    EaxRamSize = AL_EAX_RAM_SIZE,
+    EaxRamFree = AL_EAX_RAM_FREE,
+#endif
+};
+
+template<typename T>
+struct PropertyCastType {
+    template<typename U>
+    constexpr auto operator()(U&& value) const noexcept
+    { return static_cast<T>(std::forward<U>(value)); }
+};
+/* Special-case ALboolean to be an actual bool instead of a char type. */
+template<>
+struct PropertyCastType<ALboolean> {
+    template<typename U>
+    constexpr ALboolean operator()(U&& value) const noexcept
+    { return static_cast<bool>(std::forward<U>(value)) ? AL_TRUE : AL_FALSE; }
+};
+
+
+template<typename T>
+void GetValue(ALCcontext *context, ALenum pname, T *values)
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return AL_FALSE;
+    auto cast_value = PropertyCastType<T>{};
 
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    ALboolean value{AL_FALSE};
-    switch(pname)
+    switch(static_cast<PropertyValue>(pname))
     {
     case AL_DOPPLER_FACTOR:
-        if(context->mDopplerFactor != 0.0f)
-            value = AL_TRUE;
-        break;
+        *values = cast_value(context->mDopplerFactor);
+        return;
 
     case AL_DOPPLER_VELOCITY:
-        if(context->mDopplerVelocity != 0.0f)
-            value = AL_TRUE;
-        break;
-
-    case AL_DISTANCE_MODEL:
-        if(context->mDistanceModel == DistanceModel::Default)
-            value = AL_TRUE;
-        break;
+        if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY
+            context->debugMessage(DebugSource::API, DebugType::DeprecatedBehavior, 0,
+                DebugSeverity::Medium,
+                "AL_DOPPLER_VELOCITY is deprecated in AL 1.1, use AL_SPEED_OF_SOUND; "
+                "AL_DOPPLER_VELOCITY -> AL_SPEED_OF_SOUND / 343.3f");
+        *values = cast_value(context->mDopplerVelocity);
+        return;
 
     case AL_SPEED_OF_SOUND:
-        if(context->mSpeedOfSound != 0.0f)
-            value = AL_TRUE;
-        break;
-
-    case AL_DEFERRED_UPDATES_SOFT:
-        if(context->mDeferUpdates)
-            value = AL_TRUE;
-        break;
+        *values = cast_value(context->mSpeedOfSound);
+        return;
 
     case AL_GAIN_LIMIT_SOFT:
-        if(GainMixMax/context->mGainBoost != 0.0f)
-            value = AL_TRUE;
-        break;
-
-    case AL_NUM_RESAMPLERS_SOFT:
-        /* Always non-0. */
-        value = AL_TRUE;
-        break;
-
-    case AL_DEFAULT_RESAMPLER_SOFT:
-        value = static_cast<int>(ResamplerDefault) ? AL_TRUE : AL_FALSE;
-        break;
-
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid boolean property 0x%04x", pname);
-    }
-
-    return value;
-}
-END_API_FUNC
-
-AL_API ALdouble AL_APIENTRY alGetDouble(ALenum pname)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return 0.0;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    ALdouble value{0.0};
-    switch(pname)
-    {
-    case AL_DOPPLER_FACTOR:
-        value = context->mDopplerFactor;
-        break;
-
-    case AL_DOPPLER_VELOCITY:
-        value = context->mDopplerVelocity;
-        break;
-
-    case AL_DISTANCE_MODEL:
-        value = static_cast<ALdouble>(ALenumFromDistanceModel(context->mDistanceModel));
-        break;
-
-    case AL_SPEED_OF_SOUND:
-        value = context->mSpeedOfSound;
-        break;
+        *values = cast_value(GainMixMax / context->mGainBoost);
+        return;
 
     case AL_DEFERRED_UPDATES_SOFT:
-        if(context->mDeferUpdates)
-            value = static_cast<ALdouble>(AL_TRUE);
-        break;
-
-    case AL_GAIN_LIMIT_SOFT:
-        value = ALdouble{GainMixMax}/context->mGainBoost;
-        break;
-
-    case AL_NUM_RESAMPLERS_SOFT:
-        value = static_cast<ALdouble>(Resampler::Max) + 1.0;
-        break;
-
-    case AL_DEFAULT_RESAMPLER_SOFT:
-        value = static_cast<ALdouble>(ResamplerDefault);
-        break;
-
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid double property 0x%04x", pname);
-    }
-
-    return value;
-}
-END_API_FUNC
-
-AL_API ALfloat AL_APIENTRY alGetFloat(ALenum pname)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return 0.0f;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    ALfloat value{0.0f};
-    switch(pname)
-    {
-    case AL_DOPPLER_FACTOR:
-        value = context->mDopplerFactor;
-        break;
-
-    case AL_DOPPLER_VELOCITY:
-        value = context->mDopplerVelocity;
-        break;
+        *values = cast_value(context->mDeferUpdates ? AL_TRUE : AL_FALSE);
+        return;
 
     case AL_DISTANCE_MODEL:
-        value = static_cast<ALfloat>(ALenumFromDistanceModel(context->mDistanceModel));
-        break;
-
-    case AL_SPEED_OF_SOUND:
-        value = context->mSpeedOfSound;
-        break;
-
-    case AL_DEFERRED_UPDATES_SOFT:
-        if(context->mDeferUpdates)
-            value = static_cast<ALfloat>(AL_TRUE);
-        break;
-
-    case AL_GAIN_LIMIT_SOFT:
-        value = GainMixMax/context->mGainBoost;
-        break;
+        *values = cast_value(ALenumFromDistanceModel(context->mDistanceModel));
+        return;
 
     case AL_NUM_RESAMPLERS_SOFT:
-        value = static_cast<ALfloat>(Resampler::Max) + 1.0f;
-        break;
+        *values = cast_value(al::to_underlying(Resampler::Max) + 1);
+        return;
 
     case AL_DEFAULT_RESAMPLER_SOFT:
-        value = static_cast<ALfloat>(ResamplerDefault);
-        break;
+        *values = cast_value(al::to_underlying(ResamplerDefault));
+        return;
 
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid float property 0x%04x", pname);
+    case AL_DEBUG_LOGGED_MESSAGES_EXT:
+    {
+        std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
+        *values = cast_value(context->mDebugLog.size());
+        return;
     }
 
-    return value;
-}
-END_API_FUNC
-
-AL_API ALint AL_APIENTRY alGetInteger(ALenum pname)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return 0;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    ALint value{0};
-    switch(pname)
+    case AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT:
     {
-    case AL_DOPPLER_FACTOR:
-        value = static_cast<ALint>(context->mDopplerFactor);
-        break;
-
-    case AL_DOPPLER_VELOCITY:
-        value = static_cast<ALint>(context->mDopplerVelocity);
-        break;
-
-    case AL_DISTANCE_MODEL:
-        value = ALenumFromDistanceModel(context->mDistanceModel);
-        break;
+        std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
+        *values = cast_value(context->mDebugLog.empty() ? 0_uz
+            : (context->mDebugLog.front().mMessage.size()+1));
+        return;
+    }
 
-    case AL_SPEED_OF_SOUND:
-        value = static_cast<ALint>(context->mSpeedOfSound);
-        break;
+    case AL_MAX_DEBUG_MESSAGE_LENGTH_EXT:
+        *values = cast_value(MaxDebugMessageLength);
+        return;
 
-    case AL_DEFERRED_UPDATES_SOFT:
-        if(context->mDeferUpdates)
-            value = AL_TRUE;
-        break;
+    case AL_MAX_DEBUG_LOGGED_MESSAGES_EXT:
+        *values = cast_value(MaxDebugLoggedMessages);
+        return;
 
-    case AL_GAIN_LIMIT_SOFT:
-        value = static_cast<ALint>(GainMixMax/context->mGainBoost);
-        break;
+    case AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT:
+        *values = cast_value(MaxDebugGroupDepth);
+        return;
 
-    case AL_NUM_RESAMPLERS_SOFT:
-        value = static_cast<int>(Resampler::Max) + 1;
-        break;
+    case AL_MAX_LABEL_LENGTH_EXT:
+        *values = cast_value(MaxObjectLabelLength);
+        return;
 
-    case AL_DEFAULT_RESAMPLER_SOFT:
-        value = static_cast<int>(ResamplerDefault);
-        break;
+    case AL_CONTEXT_FLAGS_EXT:
+        *values = cast_value(context->mContextFlags.to_ulong());
+        return;
 
 #ifdef ALSOFT_EAX
-
-#define EAX_ERROR "[alGetInteger] EAX not enabled."
+#define EAX_ERROR "[alGetInteger] EAX not enabled"
 
     case AL_EAX_RAM_SIZE:
-        if (eax_g_is_enabled)
-        {
-            value = eax_x_ram_max_size;
-        }
-        else
+        if(eax_g_is_enabled)
         {
-            context->setError(AL_INVALID_VALUE, EAX_ERROR);
+            *values = cast_value(eax_x_ram_max_size);
+            return;
         }
-
+        ERR(EAX_ERROR "\n");
         break;
 
     case AL_EAX_RAM_FREE:
-        if (eax_g_is_enabled)
+        if(eax_g_is_enabled)
         {
             auto device = context->mALDevice.get();
             std::lock_guard<std::mutex> device_lock{device->BufferLock};
-
-            value = static_cast<ALint>(device->eax_x_ram_free_size);
+            *values = cast_value(device->eax_x_ram_free_size);
+            return;
         }
-        else
-        {
-            context->setError(AL_INVALID_VALUE, EAX_ERROR);
-        }
-
+        ERR(EAX_ERROR "\n");
         break;
 
 #undef EAX_ERROR
-
 #endif // ALSOFT_EAX
-
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid integer property 0x%04x", pname);
     }
-
-    return value;
+    context->setError(AL_INVALID_ENUM, "Invalid context property 0x%04x", pname);
 }
-END_API_FUNC
 
-AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return 0_i64;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    ALint64SOFT value{0};
-    switch(pname)
-    {
-    case AL_DOPPLER_FACTOR:
-        value = static_cast<ALint64SOFT>(context->mDopplerFactor);
-        break;
-
-    case AL_DOPPLER_VELOCITY:
-        value = static_cast<ALint64SOFT>(context->mDopplerVelocity);
-        break;
 
-    case AL_DISTANCE_MODEL:
-        value = ALenumFromDistanceModel(context->mDistanceModel);
-        break;
-
-    case AL_SPEED_OF_SOUND:
-        value = static_cast<ALint64SOFT>(context->mSpeedOfSound);
-        break;
-
-    case AL_DEFERRED_UPDATES_SOFT:
-        if(context->mDeferUpdates)
-            value = AL_TRUE;
-        break;
-
-    case AL_GAIN_LIMIT_SOFT:
-        value = static_cast<ALint64SOFT>(GainMixMax/context->mGainBoost);
-        break;
-
-    case AL_NUM_RESAMPLERS_SOFT:
-        value = static_cast<ALint64SOFT>(Resampler::Max) + 1;
-        break;
-
-    case AL_DEFAULT_RESAMPLER_SOFT:
-        value = static_cast<ALint64SOFT>(ResamplerDefault);
-        break;
-
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid integer64 property 0x%04x", pname);
-    }
-
-    return value;
-}
-END_API_FUNC
-
-AL_API ALvoid* AL_APIENTRY alGetPointerSOFT(ALenum pname)
-START_API_FUNC
+inline void UpdateProps(ALCcontext *context)
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return nullptr;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    void *value{nullptr};
-    switch(pname)
-    {
-    case AL_EVENT_CALLBACK_FUNCTION_SOFT:
-        value = reinterpret_cast<void*>(context->mEventCb);
-        break;
-
-    case AL_EVENT_CALLBACK_USER_PARAM_SOFT:
-        value = context->mEventParam;
-        break;
+    if(!context->mDeferUpdates)
+        UpdateContextProps(context);
+    else
+        context->mPropsDirty = true;
+}
 
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid pointer property 0x%04x", pname);
-    }
+} // namespace
 
-    return value;
+/* WARNING: Non-standard export! Not part of any extension, or exposed in the
+ * alcFunctions list.
+ */
+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();
+    return ALSOFT_VERSION;
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetBooleanv(ALenum pname, ALboolean *values)
-START_API_FUNC
+
+AL_API DECL_FUNC1(void, alEnable, ALenum,capability)
+FORCE_ALIGN void AL_APIENTRY alEnableDirect(ALCcontext *context, ALenum capability) noexcept
 {
-    if(values)
+    switch(capability)
     {
-        switch(pname)
+    case AL_SOURCE_DISTANCE_MODEL:
         {
-            case AL_DOPPLER_FACTOR:
-            case AL_DOPPLER_VELOCITY:
-            case AL_DISTANCE_MODEL:
-            case AL_SPEED_OF_SOUND:
-            case AL_DEFERRED_UPDATES_SOFT:
-            case AL_GAIN_LIMIT_SOFT:
-            case AL_NUM_RESAMPLERS_SOFT:
-            case AL_DEFAULT_RESAMPLER_SOFT:
-                values[0] = alGetBoolean(pname);
-                return;
+            std::lock_guard<std::mutex> proplock{context->mPropLock};
+            context->mSourceDistanceModel = true;
+            UpdateProps(context);
         }
-    }
+        return;
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+    case AL_DEBUG_OUTPUT_EXT:
+        context->mDebugEnabled.store(true);
+        return;
 
-    if(!values)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(pname)
-    {
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid boolean-vector property 0x%04x", pname);
+    case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
+        context->setError(AL_INVALID_OPERATION, "Re-enabling AL_STOP_SOURCES_ON_DISCONNECT_SOFT not yet supported");
+        return;
     }
+    context->setError(AL_INVALID_VALUE, "Invalid enable property 0x%04x", capability);
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetDoublev(ALenum pname, ALdouble *values)
-START_API_FUNC
+AL_API DECL_FUNC1(void, alDisable, ALenum,capability)
+FORCE_ALIGN void AL_APIENTRY alDisableDirect(ALCcontext *context, ALenum capability) noexcept
 {
-    if(values)
+    switch(capability)
     {
-        switch(pname)
+    case AL_SOURCE_DISTANCE_MODEL:
         {
-            case AL_DOPPLER_FACTOR:
-            case AL_DOPPLER_VELOCITY:
-            case AL_DISTANCE_MODEL:
-            case AL_SPEED_OF_SOUND:
-            case AL_DEFERRED_UPDATES_SOFT:
-            case AL_GAIN_LIMIT_SOFT:
-            case AL_NUM_RESAMPLERS_SOFT:
-            case AL_DEFAULT_RESAMPLER_SOFT:
-                values[0] = alGetDouble(pname);
-                return;
+            std::lock_guard<std::mutex> proplock{context->mPropLock};
+            context->mSourceDistanceModel = false;
+            UpdateProps(context);
         }
-    }
+        return;
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+    case AL_DEBUG_OUTPUT_EXT:
+        context->mDebugEnabled.store(false);
+        return;
 
-    if(!values)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(pname)
-    {
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid double-vector property 0x%04x", pname);
+    case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
+        context->mStopVoicesOnDisconnect.store(false);
+        return;
     }
+    context->setError(AL_INVALID_VALUE, "Invalid disable property 0x%04x", capability);
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetFloatv(ALenum pname, ALfloat *values)
-START_API_FUNC
+AL_API DECL_FUNC1(ALboolean, alIsEnabled, ALenum,capability)
+FORCE_ALIGN ALboolean AL_APIENTRY alIsEnabledDirect(ALCcontext *context, ALenum capability) noexcept
 {
-    if(values)
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
+    switch(capability)
     {
-        switch(pname)
-        {
-            case AL_DOPPLER_FACTOR:
-            case AL_DOPPLER_VELOCITY:
-            case AL_DISTANCE_MODEL:
-            case AL_SPEED_OF_SOUND:
-            case AL_DEFERRED_UPDATES_SOFT:
-            case AL_GAIN_LIMIT_SOFT:
-            case AL_NUM_RESAMPLERS_SOFT:
-            case AL_DEFAULT_RESAMPLER_SOFT:
-                values[0] = alGetFloat(pname);
-                return;
-        }
+    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:
+        return context->mStopVoicesOnDisconnect.load() ? AL_TRUE : AL_FALSE;
     }
+    context->setError(AL_INVALID_VALUE, "Invalid is enabled property 0x%04x", capability);
+    return AL_FALSE;
+}
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    if(!values)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(pname)
-    {
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid float-vector property 0x%04x", pname);
-    }
+#define DECL_GETFUNC(R, Name, Ext)                                            \
+AL_API auto AL_APIENTRY Name##Ext(ALenum pname) noexcept -> R                 \
+{                                                                             \
+    R value{};                                                                \
+    auto context = GetContextRef();                                           \
+    if(!context) UNLIKELY return value;                                       \
+    Name##vDirect##Ext(GetContextRef().get(), pname, &value);                 \
+    return value;                                                             \
+}                                                                             \
+FORCE_ALIGN auto AL_APIENTRY Name##Direct##Ext(ALCcontext *context, ALenum pname) noexcept -> R \
+{                                                                             \
+    R value{};                                                                \
+    Name##vDirect##Ext(context, pname, &value);                               \
+    return value;                                                             \
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetIntegerv(ALenum pname, ALint *values)
-START_API_FUNC
-{
-    if(values)
-    {
-        switch(pname)
-        {
-            case AL_DOPPLER_FACTOR:
-            case AL_DOPPLER_VELOCITY:
-            case AL_DISTANCE_MODEL:
-            case AL_SPEED_OF_SOUND:
-            case AL_DEFERRED_UPDATES_SOFT:
-            case AL_GAIN_LIMIT_SOFT:
-            case AL_NUM_RESAMPLERS_SOFT:
-            case AL_DEFAULT_RESAMPLER_SOFT:
-                values[0] = alGetInteger(pname);
-                return;
-        }
-    }
+DECL_GETFUNC(ALboolean, alGetBoolean,)
+DECL_GETFUNC(ALdouble, alGetDouble,)
+DECL_GETFUNC(ALfloat, alGetFloat,)
+DECL_GETFUNC(ALint, alGetInteger,)
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+DECL_GETFUNC(ALint64SOFT, alGetInteger64,SOFT)
+DECL_GETFUNC(ALvoid*, alGetPointer,SOFT)
 
-    if(!values)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(pname)
-    {
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid integer-vector property 0x%04x", pname);
-    }
-}
-END_API_FUNC
+#undef DECL_GETFUNC
 
-AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values)
-START_API_FUNC
-{
-    if(values)
-    {
-        switch(pname)
-        {
-            case AL_DOPPLER_FACTOR:
-            case AL_DOPPLER_VELOCITY:
-            case AL_DISTANCE_MODEL:
-            case AL_SPEED_OF_SOUND:
-            case AL_DEFERRED_UPDATES_SOFT:
-            case AL_GAIN_LIMIT_SOFT:
-            case AL_NUM_RESAMPLERS_SOFT:
-            case AL_DEFAULT_RESAMPLER_SOFT:
-                values[0] = alGetInteger64SOFT(pname);
-                return;
-        }
-    }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+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
+        return context->setError(AL_INVALID_VALUE, "NULL pointer");
+    GetValue(context, pname, values);
+}
 
-    if(!values)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(pname)
-    {
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid integer64-vector property 0x%04x", pname);
-    }
+AL_API DECL_FUNC2(void, alGetDoublev, ALenum,pname, ALdouble*,values)
+FORCE_ALIGN void AL_APIENTRY alGetDoublevDirect(ALCcontext *context, ALenum pname, ALdouble *values) noexcept
+{
+    if(!values) UNLIKELY
+        return context->setError(AL_INVALID_VALUE, "NULL pointer");
+    GetValue(context, pname, values);
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, ALvoid **values)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alGetFloatv, ALenum,pname, ALfloat*,values)
+FORCE_ALIGN void AL_APIENTRY alGetFloatvDirect(ALCcontext *context, ALenum pname, ALfloat *values) noexcept
 {
-    if(values)
-    {
-        switch(pname)
-        {
-            case AL_EVENT_CALLBACK_FUNCTION_SOFT:
-            case AL_EVENT_CALLBACK_USER_PARAM_SOFT:
-                values[0] = alGetPointerSOFT(pname);
-                return;
-        }
-    }
+    if(!values) UNLIKELY
+        return context->setError(AL_INVALID_VALUE, "NULL pointer");
+    GetValue(context, pname, values);
+}
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+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
+        return context->setError(AL_INVALID_VALUE, "NULL pointer");
+    GetValue(context, pname, values);
+}
 
-    if(!values)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(pname)
-    {
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid pointer-vector property 0x%04x", pname);
-    }
+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
+        return context->setError(AL_INVALID_VALUE, "NULL pointer");
+    GetValue(context, pname, values);
 }
-END_API_FUNC
 
-AL_API const ALchar* AL_APIENTRY alGetString(ALenum pname)
-START_API_FUNC
+AL_API DECL_FUNCEXT2(void, alGetPointerv,SOFT, ALenum,pname, ALvoid**,values)
+FORCE_ALIGN void AL_APIENTRY alGetPointervDirectSOFT(ALCcontext *context, ALenum pname, ALvoid **values) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return nullptr;
+    if(!values) UNLIKELY
+        return context->setError(AL_INVALID_VALUE, "NULL pointer");
 
-    const ALchar *value{nullptr};
     switch(pname)
     {
-    case AL_VENDOR:
-        value = alVendor;
-        break;
-
-    case AL_VERSION:
-        value = alVersion;
-        break;
-
-    case AL_RENDERER:
-        value = alRenderer;
-        break;
-
-    case AL_EXTENSIONS:
-        value = context->mExtensionList;
-        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_EVENT_CALLBACK_FUNCTION_SOFT:
+        *values = reinterpret_cast<void*>(context->mEventCb);
+        return;
 
-    case AL_INVALID_OPERATION:
-        value = alErrInvalidOp;
-        break;
+    case AL_EVENT_CALLBACK_USER_PARAM_SOFT:
+        *values = context->mEventParam;
+        return;
 
-    case AL_OUT_OF_MEMORY:
-        value = alErrOutOfMemory;
-        break;
+    case AL_DEBUG_CALLBACK_FUNCTION_EXT:
+        *values = reinterpret_cast<void*>(context->mDebugCb);
+        return;
 
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid string property 0x%04x", pname);
+    case AL_DEBUG_CALLBACK_USER_PARAM_EXT:
+        *values = context->mDebugParam;
+        return;
     }
-    return value;
+    context->setError(AL_INVALID_ENUM, "Invalid context pointer property 0x%04x", pname);
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alDopplerFactor(ALfloat value)
-START_API_FUNC
+AL_API DECL_FUNC1(const ALchar*, alGetString, ALenum,pname)
+FORCE_ALIGN const ALchar* AL_APIENTRY alGetStringDirect(ALCcontext *context, ALenum pname) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    if(!(value >= 0.0f && std::isfinite(value)))
-        context->setError(AL_INVALID_VALUE, "Doppler factor %f out of range", value);
-    else
+    switch(pname)
     {
-        std::lock_guard<std::mutex> _{context->mPropLock};
-        context->mDopplerFactor = value;
-        DO_UPDATEPROPS();
-    }
+    case AL_VENDOR: return GetVendorString();
+    case AL_VERSION: return GetVersionString();
+    case AL_RENDERER: 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();
+    }
+    context->setError(AL_INVALID_VALUE, "Invalid string property 0x%04x", pname);
+    return nullptr;
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value)
-START_API_FUNC
+AL_API DECL_FUNC1(void, alDopplerFactor, ALfloat,value)
+FORCE_ALIGN void AL_APIENTRY alDopplerFactorDirect(ALCcontext *context, ALfloat value) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
     if(!(value >= 0.0f && std::isfinite(value)))
-        context->setError(AL_INVALID_VALUE, "Doppler velocity %f out of range", value);
+        context->setError(AL_INVALID_VALUE, "Doppler factor %f out of range", value);
     else
     {
-        std::lock_guard<std::mutex> _{context->mPropLock};
-        context->mDopplerVelocity = value;
-        DO_UPDATEPROPS();
+        std::lock_guard<std::mutex> proplock{context->mPropLock};
+        context->mDopplerFactor = value;
+        UpdateProps(context);
     }
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value)
-START_API_FUNC
+AL_API DECL_FUNC1(void, alSpeedOfSound, ALfloat,value)
+FORCE_ALIGN void AL_APIENTRY alSpeedOfSoundDirect(ALCcontext *context, ALfloat value) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
     if(!(value > 0.0f && std::isfinite(value)))
         context->setError(AL_INVALID_VALUE, "Speed of sound %f out of range", value);
     else
     {
-        std::lock_guard<std::mutex> _{context->mPropLock};
+        std::lock_guard<std::mutex> proplock{context->mPropLock};
         context->mSpeedOfSound = value;
-        DO_UPDATEPROPS();
+        UpdateProps(context);
     }
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alDistanceModel(ALenum value)
-START_API_FUNC
+AL_API DECL_FUNC1(void, alDistanceModel, ALenum,value)
+FORCE_ALIGN void AL_APIENTRY alDistanceModelDirect(ALCcontext *context, ALenum value) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
     if(auto model = DistanceModelFromALenum(value))
     {
-        std::lock_guard<std::mutex> _{context->mPropLock};
+        std::lock_guard<std::mutex> proplock{context->mPropLock};
         context->mDistanceModel = *model;
         if(!context->mSourceDistanceModel)
-            DO_UPDATEPROPS();
+            UpdateProps(context);
     }
     else
         context->setError(AL_INVALID_VALUE, "Distance model 0x%04x out of range", value);
 }
-END_API_FUNC
 
 
-AL_API void AL_APIENTRY alDeferUpdatesSOFT(void)
-START_API_FUNC
+AL_API DECL_FUNCEXT(void, alDeferUpdates,SOFT)
+FORCE_ALIGN void AL_APIENTRY alDeferUpdatesDirectSOFT(ALCcontext *context) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
     context->deferUpdates();
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alProcessUpdatesSOFT(void)
-START_API_FUNC
+AL_API DECL_FUNCEXT(void, alProcessUpdates,SOFT)
+FORCE_ALIGN void AL_APIENTRY alProcessUpdatesDirectSOFT(ALCcontext *context) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
     context->processUpdates();
 }
-END_API_FUNC
 
 
-AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index)
-START_API_FUNC
+AL_API DECL_FUNCEXT2(const ALchar*, alGetStringi,SOFT, ALenum,pname, ALsizei,index)
+FORCE_ALIGN const ALchar* AL_APIENTRY alGetStringiDirectSOFT(ALCcontext *context, ALenum pname, ALsizei index) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return nullptr;
-
-    const ALchar *value{nullptr};
     switch(pname)
     {
     case AL_RESAMPLER_NAME_SOFT:
-        if(index < 0 || index > static_cast<ALint>(Resampler::Max))
-            context->setError(AL_INVALID_VALUE, "Resampler name index %d out of range", index);
-        else
-            value = GetResamplerName(static_cast<Resampler>(index));
-        break;
+        if(index >= 0 && index <= static_cast<ALint>(Resampler::Max))
+            return GetResamplerName(static_cast<Resampler>(index));
+        context->setError(AL_INVALID_VALUE, "Resampler name index %d out of range", index);
+        return nullptr;
+    }
+    context->setError(AL_INVALID_VALUE, "Invalid string indexed property");
+    return nullptr;
+}
+
 
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid string indexed property");
+AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value) noexcept
+{
+    ContextRef context{GetContextRef()};
+    if(!context) UNLIKELY return;
+
+    if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY
+        context->debugMessage(DebugSource::API, DebugType::DeprecatedBehavior, 0,
+            DebugSeverity::Medium,
+            "alDopplerVelocity is deprecated in AL 1.1, use alSpeedOfSound; "
+            "alDopplerVelocity(x) -> alSpeedOfSound(343.3f * x)");
+
+    if(!(value >= 0.0f && std::isfinite(value)))
+        context->setError(AL_INVALID_VALUE, "Doppler velocity %f out of range", value);
+    else
+    {
+        std::lock_guard<std::mutex> proplock{context->mPropLock};
+        context->mDopplerVelocity = value;
+        UpdateProps(context.get());
     }
-    return value;
 }
-END_API_FUNC
 
 
 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;

Dosya farkı çok büyük olduğundan ihmal edildi
+ 115 - 776
Engine/lib/openal-soft/alc/alc.cpp


+ 202 - 193
Engine/lib/openal-soft/alc/alconfig.cpp

@@ -22,9 +22,6 @@
 
 #include "alconfig.h"
 
-#include <cstdlib>
-#include <cctype>
-#include <cstring>
 #ifdef _WIN32
 #include <windows.h>
 #include <shlobj.h>
@@ -34,25 +31,47 @@
 #endif
 
 #include <algorithm>
-#include <cstdio>
+#include <array>
+#include <cctype>
+#include <cstdlib>
+#include <filesystem>
+#include <fstream>
+#include <istream>
+#include <limits>
 #include <string>
+#include <string_view>
 #include <utility>
+#include <vector>
 
-#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)
+#include <winrt/Windows.Media.Core.h> // !!This is important!!
+#include <winrt/Windows.Storage.h>
+#include <winrt/Windows.Foundation.h>
+#include <winrt/Windows.Foundation.Collections.h>
+using namespace winrt;
+#endif
 
 namespace {
 
+using namespace std::string_view_literals;
+
+#if defined(_WIN32) && !defined(_GAMING_XBOX) && !defined(ALSOFT_UWP)
+struct CoTaskMemDeleter {
+    void operator()(void *mem) const { CoTaskMemFree(mem); }
+};
+#endif
+
 struct ConfigEntry {
     std::string key;
     std::string value;
 };
-al::vector<ConfigEntry> ConfOpts;
+std::vector<ConfigEntry> ConfOpts;
 
 
 std::string &lstrip(std::string &line)
@@ -72,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<size_t>(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<size_t>(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;
@@ -140,44 +150,43 @@ void LoadConfigFromFile(std::istream &f)
 
         if(buffer[0] == '[')
         {
-            auto line = const_cast<char*>(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') ||
@@ -198,19 +207,19 @@ void LoadConfigFromFile(std::istream &f)
                         else if(section[2] >= 'A' && section[2] <= 'F')
                             b |= (section[2]-'A'+0x0a);
                         curSection += static_cast<char>(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;
@@ -228,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())
@@ -245,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<int>::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
@@ -266,61 +280,49 @@ 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)
+const char *GetConfigValue(const std::string_view devName, const std::string_view blockName,
+    const std::string_view keyName)
 {
-    if(!keyName)
+    if(keyName.empty())
         return nullptr;
 
     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;
     }
 
-    if(!devName)
-    {
-        TRACE("Key %s not found\n", key.c_str());
+    if(devName.empty())
         return nullptr;
-    }
-    return GetConfigValue(nullptr, blockName, keyName);
+    return GetConfigValue({}, blockName, keyName);
 }
 
 } // namespace
@@ -329,33 +331,48 @@ const char *GetConfigValue(const char *devName, const char *blockName, const cha
 #ifdef _WIN32
 void ReadALConfig()
 {
-    WCHAR buffer[MAX_PATH];
-    if(SHGetSpecialFolderPathW(nullptr, buffer, CSIDL_APPDATA, FALSE) != FALSE)
+    namespace fs = std::filesystem;
+    fs::path path;
+
+#if !defined(_GAMING_XBOX)
     {
-        std::string filepath{wstr_to_utf8(buffer)};
-        filepath += "\\alsoft.ini";
+#if !defined(ALSOFT_UWP)
+        std::unique_ptr<WCHAR,CoTaskMemDeleter> 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 bufstore = Windows::Storage::ApplicationData::Current().RoamingFolder().Path();
+        std::wstring_view buffer{bufstore};
+        {
+#endif
+            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", 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 /= "alsoft.ini";
+        TRACE("Loading config %s...\n", 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", path.u8string().c_str());
+        if(std::ifstream f{path}; f.is_open())
             LoadConfigFromFile(f);
     }
 }
@@ -364,13 +381,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", 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
@@ -378,48 +394,43 @@ 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", 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", 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<unsigned char,PATH_MAX> fileName{};
+        if(configURL && CFURLGetFileSystemRepresentation(configURL, true, fileName.data(), fileName.size()))
         {
-            f = al::ifstream{reinterpret_cast<char*>(fileName)};
-            if(f.is_open())
+            if(std::ifstream f{reinterpret_cast<char*>(fileName.data())}; f.is_open())
                 LoadConfigFromFile(f);
         }
     }
@@ -427,102 +438,100 @@ 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", 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", 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", 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
 
-al::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName)
+std::optional<std::string> ConfigValueStr(const std::string_view devName,
+    const std::string_view blockName, const std::string_view keyName)
 {
     if(const char *val{GetConfigValue(devName, blockName, keyName)})
         return val;
-    return al::nullopt;
+    return std::nullopt;
 }
 
-al::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName)
+std::optional<int> ConfigValueInt(const std::string_view devName, const std::string_view blockName,
+    const std::string_view keyName)
 {
     if(const char *val{GetConfigValue(devName, blockName, keyName)})
         return static_cast<int>(std::strtol(val, nullptr, 0));
-    return al::nullopt;
+    return std::nullopt;
 }
 
-al::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName)
+std::optional<unsigned int> ConfigValueUInt(const std::string_view devName,
+    const std::string_view blockName, const std::string_view keyName)
 {
     if(const char *val{GetConfigValue(devName, blockName, keyName)})
         return static_cast<unsigned int>(std::strtoul(val, nullptr, 0));
-    return al::nullopt;
+    return std::nullopt;
 }
 
-al::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName)
+std::optional<float> ConfigValueFloat(const std::string_view devName,
+    const std::string_view blockName, const std::string_view keyName)
 {
     if(const char *val{GetConfigValue(devName, blockName, keyName)})
         return std::strtof(val, nullptr);
-    return al::nullopt;
+    return std::nullopt;
 }
 
-al::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName)
+std::optional<bool> ConfigValueBool(const std::string_view devName,
+    const std::string_view blockName, const std::string_view keyName)
 {
     if(const char *val{GetConfigValue(devName, blockName, keyName)})
         return al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
-            || al::strcasecmp(val, "true")==0 || atoi(val) != 0;
-    return al::nullopt;
+            || al::strcasecmp(val, "true") == 0 || atoi(val) != 0;
+    return std::nullopt;
 }
 
-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)
 {
     if(const char *val{GetConfigValue(devName, blockName, keyName)})
-        return (al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
-            || al::strcasecmp(val, "true") == 0 || atoi(val) != 0);
+        return al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
+            || al::strcasecmp(val, "true") == 0 || atoi(val) != 0;
     return def;
 }

+ 14 - 7
Engine/lib/openal-soft/alc/alconfig.h

@@ -1,18 +1,25 @@
 #ifndef ALCONFIG_H
 #define ALCONFIG_H
 
+#include <optional>
 #include <string>
+#include <string_view>
 
-#include "aloptional.h"
 
 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);
 
-al::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName);
-al::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName);
-al::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName);
-al::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName);
-al::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName);
+std::optional<std::string> ConfigValueStr(const std::string_view devName,
+    const std::string_view blockName, const std::string_view keyName);
+std::optional<int> ConfigValueInt(const std::string_view devName, const std::string_view blockName,
+    const std::string_view keyName);
+std::optional<unsigned int> ConfigValueUInt(const std::string_view devName,
+    const std::string_view blockName, const std::string_view keyName);
+std::optional<float> ConfigValueFloat(const std::string_view devName,
+    const std::string_view blockName, const std::string_view keyName);
+std::optional<bool> ConfigValueBool(const std::string_view devName,
+    const std::string_view blockName, const std::string_view keyName);
 
 #endif /* ALCONFIG_H */

Dosya farkı çok büyük olduğundan ihmal edildi
+ 373 - 252
Engine/lib/openal-soft/alc/alu.cpp


+ 5 - 5
Engine/lib/openal-soft/alc/alu.h

@@ -2,20 +2,20 @@
 #define ALU_H
 
 #include <bitset>
-
-#include "aloptional.h"
+#include <cstdint>
+#include <optional>
 
 struct ALCcontext;
 struct ALCdevice;
 struct EffectSlot;
 
-enum class StereoEncoding : unsigned char;
+enum class StereoEncoding : std::uint8_t;
 
 
 constexpr float GainMixMax{1000.0f}; /* +60dB */
 
 
-enum CompatFlags : uint8_t {
+enum CompatFlags : std::uint8_t {
     ReverseX,
     ReverseY,
     ReverseZ,
@@ -31,7 +31,7 @@ void aluInit(CompatFlagBitset flags, const float nfcscale);
  * Set up the appropriate panning method and mixing method given the device
  * properties.
  */
-void aluInitRenderer(ALCdevice *device, int hrtf_id, al::optional<StereoEncoding> stereomode);
+void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncoding> stereomode);
 
 void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context);
 

+ 181 - 143
Engine/lib/openal-soft/alc/backends/alsa.cpp

@@ -31,29 +31,33 @@
 #include <exception>
 #include <functional>
 #include <memory>
+#include <mutex>
 #include <string>
+#include <string_view>
 #include <thread>
 #include <utility>
+#include <vector>
 
-#include "albyte.h"
+#include "albit.h"
 #include "alc/alconfig.h"
 #include "almalloc.h"
 #include "alnumeric.h"
-#include "aloptional.h"
+#include "alstring.h"
+#include "althrd_setname.h"
 #include "core/device.h"
 #include "core/helpers.h"
 #include "core/logging.h"
 #include "dynload.h"
 #include "ringbuffer.h"
-#include "threads.h"
-#include "vector.h"
 
 #include <alsa/asoundlib.h>
 
 
 namespace {
 
-constexpr char alsaDevice[] = "ALSA Default";
+using namespace std::string_view_literals;
+
+[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "ALSA Default"sv; }
 
 
 #ifdef HAVE_DYNLOAD
@@ -248,59 +252,97 @@ struct DevMap {
     { }
 };
 
-al::vector<DevMap> PlaybackDevices;
-al::vector<DevMap> CaptureDevices;
+std::vector<DevMap> PlaybackDevices;
+std::vector<DevMap> CaptureDevices;
 
 
-const char *prefix_name(snd_pcm_stream_t stream)
+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;
 }
 
-al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
+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<DevMap> probe_devices(snd_pcm_stream_t stream)
 {
-    al::vector<DevMap> devlist;
+    std::vector<DevMap> devlist;
 
-    snd_ctl_card_info_t *info;
-    snd_ctl_card_info_malloc(&info);
-    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 @@ al::vector<DevMap> 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 @@ al::vector<DevMap> 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 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
             snd_pcm_info_set_device(pcminfo, static_cast<uint>(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 @@ al::vector<DevMap> 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 @@ al::vector<DevMap> 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;
@@ -427,7 +476,7 @@ struct AlsaPlayback final : public BackendBase {
     int mixerProc();
     int mixerNoMMapProc();
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     bool reset() override;
     void start() override;
     void stop() override;
@@ -439,12 +488,10 @@ struct AlsaPlayback final : public BackendBase {
     std::mutex mMutex;
 
     uint mFrameStep{};
-    al::vector<al::byte> mBuffer;
+    std::vector<std::byte> mBuffer;
 
     std::atomic<bool> mKillNow{true};
     std::thread mThread;
-
-    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<std::mutex> _{mMutex};
+        std::lock_guard<std::mutex> 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<char*>(areas->addr) + (offset * areas->step / 8)};
             mDevice->renderSamples(WritePtr, static_cast<uint>(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;
         }
 
-        al::byte *WritePtr{mBuffer.data()};
+        auto WritePtr = mBuffer.begin();
         avail = snd_pcm_bytes_to_frames(mPcmHandle, static_cast<ssize_t>(mBuffer.size()));
-        std::lock_guard<std::mutex> _{mMutex};
-        mDevice->renderSamples(WritePtr, static_cast<uint>(avail), mFrameStep);
+        std::lock_guard<std::mutex> dlock{mMutex};
+        mDevice->renderSamples(al::to_address(WritePtr), static_cast<uint>(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<snd_pcm_uframes_t>(avail))};
             switch(ret)
             {
@@ -626,10 +674,10 @@ int AlsaPlayback::mixerNoMMapProc()
 }
 
 
-void AlsaPlayback::open(const char *name)
+void AlsaPlayback::open(std::string_view name)
 {
     std::string driver{"default"};
-    if(name)
+    if(!name.empty())
     {
         if(PlaybackDevices.empty())
             PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
@@ -638,13 +686,13 @@ void AlsaPlayback::open(const char *name)
             [name](const DevMap &entry) -> bool { return entry.name == name; });
         if(iter == PlaybackDevices.cend())
             throw al::backend_exception{al::backend_error::NoDevice,
-                "Device name \"%s\" not found", name};
+                "Device name \"%.*s\" not found", 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());
@@ -692,15 +740,14 @@ bool AlsaPlayback::reset()
         break;
     }
 
-    bool allowmmap{!!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "mmap", true)};
+    bool allowmmap{GetConfigValueBool(mDevice->DeviceName, "alsa"sv, "mmap"sv, true)};
     uint periodLen{static_cast<uint>(mDevice->UpdateSize * 1000000_u64 / mDevice->Frequency)};
     uint bufferLen{static_cast<uint>(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->DeviceName, "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<std::mutex> _{mMutex};
-    ret.ClockTime = GetDeviceClockTime(mDevice);
+    std::lock_guard<std::mutex> dlock{mMutex};
+    ClockLatency ret{};
+    ret.ClockTime = mDevice->getClockTime();
     snd_pcm_sframes_t delay{};
     int err{snd_pcm_delay(mPcmHandle, &delay)};
     if(err < 0)
@@ -871,23 +917,21 @@ struct AlsaCapture final : public BackendBase {
     AlsaCapture(DeviceBase *device) noexcept : BackendBase{device} { }
     ~AlsaCapture() override;
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     void start() override;
     void stop() override;
-    void captureSamples(al::byte *buffer, uint samples) override;
+    void captureSamples(std::byte *buffer, uint samples) override;
     uint availableSamples() override;
     ClockLatency getClockLatency() override;
 
     snd_pcm_t *mPcmHandle{nullptr};
 
-    al::vector<al::byte> mBuffer;
+    std::vector<std::byte> mBuffer;
 
     bool mDoCapture{false};
     RingBufferPtr mRing{nullptr};
 
     snd_pcm_sframes_t mLastAvail{0};
-
-    DEF_NEWDEL(AlsaCapture)
 };
 
 AlsaCapture::~AlsaCapture()
@@ -898,10 +942,10 @@ AlsaCapture::~AlsaCapture()
 }
 
 
-void AlsaCapture::open(const char *name)
+void AlsaCapture::open(std::string_view name)
 {
     std::string driver{"default"};
-    if(name)
+    if(!name.empty())
     {
         if(CaptureDevices.empty())
             CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
@@ -910,19 +954,18 @@ void AlsaCapture::open(const char *name)
             [name](const DevMap &entry) -> bool { return entry.name == name; });
         if(iter == CaptureDevices.cend())
             throw al::backend_exception{al::backend_error::NoDevice,
-                "Device name \"%s\" not found", name};
+                "Device name \"%.*s\" not found", 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(const char *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)
@@ -999,13 +1044,11 @@ void AlsaCapture::open(const char *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)};
 
@@ -1024,25 +1067,27 @@ void AlsaCapture::stop()
         /* The ring buffer implicitly captures when checking availability.
          * Direct access needs to explicitly capture it into temp storage.
          */
-        auto temp = al::vector<al::byte>(
+        auto temp = std::vector<std::byte>(
             static_cast<size_t>(snd_pcm_frames_to_bytes(mPcmHandle, avail)));
         captureSamples(temp.data(), avail);
         mBuffer = std::move(temp);
     }
-    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;
 }
 
-void AlsaCapture::captureSamples(al::byte *buffer, uint samples)
+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<size_t>(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(al::byte *buffer, uint samples)
             if(static_cast<snd_pcm_uframes_t>(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<int>(amt)));
 
             if(amt == -EAGAIN)
                 continue;
-            if((amt=snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1)) >= 0)
+            amt = snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1);
+            if(amt >= 0)
             {
                 amt = snd_pcm_start(mPcmHandle);
                 if(amt >= 0)
@@ -1088,12 +1134,12 @@ void AlsaCapture::captureSamples(al::byte *buffer, uint samples)
             continue;
         }
 
-        buffer = buffer + amt;
+        outiter += amt;
         samples -= static_cast<uint>(amt);
     }
     if(samples > 0)
-        std::fill_n(buffer, snd_pcm_frames_to_bytes(mPcmHandle, samples),
-            al::byte((mDevice->FmtType == DevFmtUByte) ? 0x80 : 0));
+        std::fill_n(outiter, snd_pcm_frames_to_bytes(mPcmHandle, samples),
+            std::byte((mDevice->FmtType == DevFmtUByte) ? 0x80 : 0));
 }
 
 uint AlsaCapture::availableSamples()
@@ -1105,7 +1151,8 @@ uint AlsaCapture::availableSamples()
     {
         ERR("avail update failed: %s\n", snd_strerror(static_cast<int>(avail)));
 
-        if((avail=snd_pcm_recover(mPcmHandle, static_cast<int>(avail), 1)) >= 0)
+        avail = snd_pcm_recover(mPcmHandle, static_cast<int>(avail), 1);
+        if(avail >= 0)
         {
             if(mDoCapture)
                 avail = snd_pcm_start(mPcmHandle);
@@ -1141,7 +1188,8 @@ uint AlsaCapture::availableSamples()
 
             if(amt == -EAGAIN)
                 continue;
-            if((amt=snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1)) >= 0)
+            amt = snd_pcm_recover(mPcmHandle, static_cast<int>(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 = reinterpret_cast<decltype(p##f)>(GetSymbol(alsa_handle, #f));      \
-    if(p##f == nullptr) {                                                     \
-        error = true;                                                         \
-        missing_funcs += "\n" #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>
 {
-    std::string outnames;
-
+    std::vector<std::string> outnames;
     auto add_device = [&outnames](const DevMap &entry) -> void
-    {
-        /* +1 to also append the null char (to ensure a null-separated list and
-         * double-null terminated list).
-         */
-        outnames.append(entry.name.c_str(), entry.name.length()+1);
-    };
+    { 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;
     }

+ 5 - 5
Engine/lib/openal-soft/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<std::string> 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 */

+ 45 - 19
Engine/lib/openal-soft/alc/backends/base.cpp

@@ -7,17 +7,6 @@
 #include <array>
 #include <atomic>
 
-#ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <mmreg.h>
-
-#include "albit.h"
-#include "core/logging.h"
-#include "aloptional.h"
-#endif
-
-#include "atomic.h"
 #include "core/devformat.h"
 
 
@@ -25,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;
 
@@ -38,7 +29,7 @@ backend_exception::~backend_exception() = default;
 bool BackendBase::reset()
 { throw al::backend_exception{al::backend_error::DeviceError, "Invalid BackendBase call"}; }
 
-void BackendBase::captureSamples(al::byte*, uint)
+void BackendBase::captureSamples(std::byte*, uint)
 { }
 
 uint BackendBase::availableSamples()
@@ -46,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);
 
@@ -126,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;
@@ -141,7 +149,7 @@ void BackendBase::setDefaultWFXChannelOrder()
     }
 }
 
-void BackendBase::setDefaultChannelOrder()
+void BackendBase::setDefaultChannelOrder() const
 {
     mDevice->RealOut.ChannelIndex.fill(InvalidChannelIndex);
 
@@ -179,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;

+ 31 - 26
Engine/lib/openal-soft/alc/backends/base.h

@@ -3,13 +3,16 @@
 
 #include <chrono>
 #include <cstdarg>
+#include <cstddef>
 #include <memory>
 #include <ratio>
 #include <string>
+#include <string_view>
+#include <vector>
 
-#include "albyte.h"
 #include "core/device.h"
 #include "core/except.h"
+#include "alc/events.h"
 
 
 using uint = unsigned int;
@@ -20,27 +23,33 @@ struct ClockLatency {
 };
 
 struct BackendBase {
-    virtual void open(const char *name) = 0;
+    virtual void open(std::string_view name) = 0;
 
     virtual bool reset();
     virtual void start() = 0;
     virtual void stop() = 0;
 
-    virtual void captureSamples(al::byte *buffer, uint samples);
+    virtual void captureSamples(std::byte *buffer, uint samples);
     virtual uint availableSamples();
 
     virtual ClockLatency getClockLatency();
 
     DeviceBase *const mDevice;
 
+    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<BackendBase>;
 
@@ -50,18 +59,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.
  */
@@ -74,16 +71,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<std::string> = 0;
+
+    virtual auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr = 0;
 };
 
 namespace al {
@@ -98,15 +103,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

+ 182 - 88
Engine/lib/openal-soft/alc/backends/coreaudio.cpp

@@ -22,18 +22,20 @@
 
 #include "coreaudio.h"
 
-#include <inttypes.h>
+#include <cinttypes>
+#include <cmath>
+#include <memory>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string>
 #include <string.h>
 #include <unistd.h>
-
-#include <cmath>
-#include <memory>
-#include <string>
+#include <vector>
+#include <optional>
 
 #include "alnumeric.h"
+#include "alstring.h"
 #include "core/converter.h"
 #include "core/device.h"
 #include "core/logging.h"
@@ -42,18 +44,40 @@
 #include <AudioUnit/AudioUnit.h>
 #include <AudioToolbox/AudioToolbox.h>
 
-
-namespace {
-
 #if TARGET_OS_IOS || TARGET_OS_TV
 #define CAN_ENUMERATE 0
 #else
+#include <IOKit/audio/IOAudioTypes.h>
 #define CAN_ENUMERATE 1
 #endif
 
+namespace {
+
 constexpr auto OutputElement = 0;
 constexpr auto InputElement = 1;
 
+struct FourCCPrinter {
+    char mString[sizeof(UInt32) + 1]{};
+
+    constexpr FourCCPrinter(UInt32 code) noexcept
+    {
+        for(size_t i{0};i < sizeof(UInt32);++i)
+        {
+            const auto ch = static_cast<char>(code & 0xff);
+            /* If this breaks early it'll leave the first byte null, to get
+             * read as a 0-length string.
+             */
+            if(ch <= 0x1f || ch >= 0x7f)
+                break;
+            mString[sizeof(UInt32)-1-i] = ch;
+            code >>= 8;
+        }
+    }
+    constexpr FourCCPrinter(int code) noexcept : FourCCPrinter{static_cast<UInt32>(code)} { }
+
+    constexpr const char *c_str() const noexcept { return mString; }
+};
+
 #if CAN_ENUMERATE
 struct DeviceEntry {
     AudioDeviceID mId;
@@ -147,7 +171,8 @@ UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture)
         &propSize);
     if(err)
     {
-        ERR("kAudioDevicePropertyStreamConfiguration size query failed: %u\n", err);
+        ERR("kAudioDevicePropertyStreamConfiguration size query failed: '%s' (%u)\n",
+            FourCCPrinter{err}.c_str(), err);
         return 0;
     }
 
@@ -158,7 +183,8 @@ UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture)
         buflist);
     if(err)
     {
-        ERR("kAudioDevicePropertyStreamConfiguration query failed: %u\n", err);
+        ERR("kAudioDevicePropertyStreamConfiguration query failed: '%s' (%u)\n",
+            FourCCPrinter{err}.c_str(), err);
         return 0;
     }
 
@@ -182,7 +208,7 @@ void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture)
     auto devIds = std::vector<AudioDeviceID>(propSize/sizeof(AudioDeviceID), kAudioDeviceUnknown);
     if(auto err = GetHwProperty(kAudioHardwarePropertyDevices, propSize, devIds.data()))
     {
-        ERR("Failed to get device list: %u\n", err);
+        ERR("Failed to get device list: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
         return;
     }
 
@@ -247,6 +273,48 @@ void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture)
     newdevs.swap(list);
 }
 
+struct DeviceHelper {
+    DeviceHelper()
+    {
+        AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice,
+            kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain};
+        OSStatus status = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil);
+        if (status != noErr)
+            ERR("AudioObjectAddPropertyListener fail: %d", status);
+    }
+    ~DeviceHelper()
+    {
+        AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice,
+            kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain};
+        OSStatus status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil);
+        if (status != noErr)
+            ERR("AudioObjectRemovePropertyListener fail: %d", status);
+    }
+
+    static OSStatus DeviceListenerProc(AudioObjectID /*inObjectID*/, UInt32 inNumberAddresses,
+        const AudioObjectPropertyAddress *inAddresses, void* /*inClientData*/)
+    {
+        for(UInt32 i = 0; i < inNumberAddresses; ++i)
+        {
+            switch(inAddresses[i].mSelector)
+            {
+            case kAudioHardwarePropertyDefaultOutputDevice:
+            case kAudioHardwarePropertyDefaultSystemOutputDevice:
+                alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback,
+                    "Default playback device changed: "+std::to_string(inAddresses[i].mSelector));
+                break;
+            case kAudioHardwarePropertyDefaultInputDevice:
+                alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture,
+                    "Default capture device changed: "+std::to_string(inAddresses[i].mSelector));
+                break;
+            }
+        }
+        return noErr;
+    }
+};
+
+static std::optional<DeviceHelper> sDeviceHelper;
+
 #else
 
 static constexpr char ca_device[] = "CoreAudio Default";
@@ -260,15 +328,8 @@ struct CoreAudioPlayback final : public BackendBase {
     OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags,
         const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
         AudioBufferList *ioData) noexcept;
-    static OSStatus MixerProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
-        const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
-        AudioBufferList *ioData) noexcept
-    {
-        return static_cast<CoreAudioPlayback*>(inRefCon)->MixerProc(ioActionFlags, inTimeStamp,
-            inBusNumber, inNumberFrames, ioData);
-    }
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     bool reset() override;
     void start() override;
     void stop() override;
@@ -277,8 +338,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()
@@ -301,11 +360,11 @@ OSStatus CoreAudioPlayback::MixerProc(AudioUnitRenderActionFlags*, const AudioTi
 }
 
 
-void CoreAudioPlayback::open(const char *name)
+void CoreAudioPlayback::open(std::string_view name)
 {
 #if CAN_ENUMERATE
     AudioDeviceID audioDevice{kAudioDeviceUnknown};
-    if(!name)
+    if(name.empty())
         GetHwProperty(kAudioHardwarePropertyDefaultOutputDevice, sizeof(audioDevice),
             &audioDevice);
     else
@@ -318,16 +377,16 @@ void CoreAudioPlayback::open(const char *name)
         auto devmatch = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), find_name);
         if(devmatch == PlaybackList.cend())
             throw al::backend_exception{al::backend_error::NoDevice,
-                "Device name \"%s\" not found", name};
+                "Device name \"%.*s\" not found", al::sizei(name), name.data()};
 
         audioDevice = devmatch->mId;
     }
 #else
-    if(!name)
+    if(name.empty())
         name = ca_device;
-    else if(strcmp(name, ca_device) != 0)
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
-            name};
+    else if(name != ca_device)
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+            al::sizei(name), name.data()};
 #endif
 
     /* open the default output unit */
@@ -351,7 +410,7 @@ void CoreAudioPlayback::open(const char *name)
     OSStatus err{AudioComponentInstanceNew(comp, &audioUnit)};
     if(err != noErr)
         throw al::backend_exception{al::backend_error::NoDevice,
-            "Could not create component instance: %u", err};
+            "Could not create component instance: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
 
 #if CAN_ENUMERATE
     if(audioDevice != kAudioDeviceUnknown)
@@ -362,7 +421,7 @@ void CoreAudioPlayback::open(const char *name)
     err = AudioUnitInitialize(audioUnit);
     if(err != noErr)
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Could not initialize audio unit: %u", err};
+            "Could not initialize audio unit: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
 
     /* WARNING: I don't know if "valid" audio unit values are guaranteed to be
      * non-0. If not, this logic is broken.
@@ -375,7 +434,7 @@ void CoreAudioPlayback::open(const char *name)
     mAudioUnit = audioUnit;
 
 #if CAN_ENUMERATE
-    if(name)
+    if(!name.empty())
         mDevice->DeviceName = name;
     else
     {
@@ -388,6 +447,21 @@ void CoreAudioPlayback::open(const char *name)
         if(!devname.empty()) mDevice->DeviceName = std::move(devname);
         else mDevice->DeviceName = "Unknown Device Name";
     }
+
+    if(audioDevice != kAudioDeviceUnknown)
+    {
+        UInt32 type{};
+        err = GetDevProperty(audioDevice, kAudioDevicePropertyDataSource, false,
+            kAudioObjectPropertyElementMaster, sizeof(type), &type);
+        if(err != noErr)
+            ERR("Failed to get audio device type: %u\n", err);
+        else
+        {
+            TRACE("Got device type '%s'\n", FourCCPrinter{type}.c_str());
+            mDevice->Flags.set(DirectEar, (type == kIOAudioOutputPortSubTypeHeadphones));
+        }
+    }
+
 #else
     mDevice->DeviceName = name;
 #endif
@@ -397,7 +471,7 @@ bool CoreAudioPlayback::reset()
 {
     OSStatus err{AudioUnitUninitialize(mAudioUnit)};
     if(err != noErr)
-        ERR("-- AudioUnitUninitialize failed.\n");
+        ERR("AudioUnitUninitialize failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
 
     /* retrieve default output unit's properties (output side) */
     AudioStreamBasicDescription streamFormat{};
@@ -406,7 +480,8 @@ bool CoreAudioPlayback::reset()
         OutputElement, &streamFormat, &size);
     if(err != noErr || size != sizeof(streamFormat))
     {
-        ERR("AudioUnitGetProperty failed\n");
+        ERR("AudioUnitGetProperty(StreamFormat) failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(),
+            err);
         return false;
     }
 
@@ -473,7 +548,8 @@ bool CoreAudioPlayback::reset()
         OutputElement, &streamFormat, sizeof(streamFormat));
     if(err != noErr)
     {
-        ERR("AudioUnitSetProperty failed\n");
+        ERR("AudioUnitSetProperty(StreamFormat) failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(),
+            err);
         return false;
     }
 
@@ -482,14 +558,16 @@ bool CoreAudioPlayback::reset()
     /* setup callback */
     mFrameSize = mDevice->frameSizeFromFmt();
     AURenderCallbackStruct input{};
-    input.inputProc = CoreAudioPlayback::MixerProcC;
+    input.inputProc = [](void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) noexcept
+    { return static_cast<CoreAudioPlayback*>(inRefCon)->MixerProc(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); };
     input.inputProcRefCon = this;
 
     err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_SetRenderCallback,
         kAudioUnitScope_Input, OutputElement, &input, sizeof(AURenderCallbackStruct));
     if(err != noErr)
     {
-        ERR("AudioUnitSetProperty failed\n");
+        ERR("AudioUnitSetProperty(SetRenderCallback) failed: '%s' (%u)\n",
+            FourCCPrinter{err}.c_str(), err);
         return false;
     }
 
@@ -497,7 +575,7 @@ bool CoreAudioPlayback::reset()
     err = AudioUnitInitialize(mAudioUnit);
     if(err != noErr)
     {
-        ERR("AudioUnitInitialize failed\n");
+        ERR("AudioUnitInitialize failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
         return false;
     }
 
@@ -509,14 +587,14 @@ void CoreAudioPlayback::start()
     const OSStatus err{AudioOutputUnitStart(mAudioUnit)};
     if(err != noErr)
         throw al::backend_exception{al::backend_error::DeviceError,
-            "AudioOutputUnitStart failed: %d", err};
+            "AudioOutputUnitStart failed: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
 }
 
 void CoreAudioPlayback::stop()
 {
     OSStatus err{AudioOutputUnitStop(mAudioUnit)};
     if(err != noErr)
-        ERR("AudioOutputUnitStop failed\n");
+        ERR("AudioOutputUnitStop failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
 }
 
 
@@ -527,18 +605,11 @@ struct CoreAudioCapture final : public BackendBase {
     OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
         const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
         UInt32 inNumberFrames, AudioBufferList *ioData) noexcept;
-    static OSStatus RecordProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
-        const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
-        AudioBufferList *ioData) noexcept
-    {
-        return static_cast<CoreAudioCapture*>(inRefCon)->RecordProc(ioActionFlags, inTimeStamp,
-            inBusNumber, inNumberFrames, ioData);
-    }
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     void start() override;
     void stop() override;
-    void captureSamples(al::byte *buffer, uint samples) override;
+    void captureSamples(std::byte *buffer, uint samples) override;
     uint availableSamples() override;
 
     AudioUnit mAudioUnit{0};
@@ -548,11 +619,9 @@ struct CoreAudioCapture final : public BackendBase {
 
     SampleConverterPtr mConverter;
 
-    al::vector<char> mCaptureData;
+    std::vector<char> mCaptureData;
 
     RingBufferPtr mRing{nullptr};
-
-    DEF_NEWDEL(CoreAudioCapture)
 };
 
 CoreAudioCapture::~CoreAudioCapture()
@@ -568,7 +637,7 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
     AudioBufferList*) noexcept
 {
     union {
-        al::byte _[maxz(sizeof(AudioBufferList), offsetof(AudioBufferList, mBuffers[1]))];
+        std::byte buf[std::max(sizeof(AudioBufferList), offsetof(AudioBufferList, mBuffers[1]))];
         AudioBufferList list;
     } audiobuf{};
 
@@ -581,20 +650,20 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
         inNumberFrames, &audiobuf.list)};
     if(err != noErr)
     {
-        ERR("AudioUnitRender capture error: %d\n", err);
+        ERR("AudioUnitRender capture error: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
         return err;
     }
 
-    mRing->write(mCaptureData.data(), inNumberFrames);
+    std::ignore = mRing->write(mCaptureData.data(), inNumberFrames);
     return noErr;
 }
 
 
-void CoreAudioCapture::open(const char *name)
+void CoreAudioCapture::open(std::string_view name)
 {
 #if CAN_ENUMERATE
     AudioDeviceID audioDevice{kAudioDeviceUnknown};
-    if(!name)
+    if(name.empty())
         GetHwProperty(kAudioHardwarePropertyDefaultInputDevice, sizeof(audioDevice),
             &audioDevice);
     else
@@ -607,16 +676,16 @@ void CoreAudioCapture::open(const char *name)
         auto devmatch = std::find_if(CaptureList.cbegin(), CaptureList.cend(), find_name);
         if(devmatch == CaptureList.cend())
             throw al::backend_exception{al::backend_error::NoDevice,
-                "Device name \"%s\" not found", name};
+                "Device name \"%.*s\" not found", al::sizei(name), name.data()};
 
         audioDevice = devmatch->mId;
     }
 #else
-    if(!name)
+    if(name.empty())
         name = ca_device;
-    else if(strcmp(name, ca_device) != 0)
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
-            name};
+    else if(name != ca_device)
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+            al::sizei(name), name.data()};
 #endif
 
     AudioComponentDescription desc{};
@@ -640,7 +709,7 @@ void CoreAudioCapture::open(const char *name)
     OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)};
     if(err != noErr)
         throw al::backend_exception{al::backend_error::NoDevice,
-            "Could not create component instance: %u", err};
+            "Could not create component instance: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
 
     // Turn off AudioUnit output
     UInt32 enableIO{0};
@@ -648,7 +717,8 @@ void CoreAudioCapture::open(const char *name)
         kAudioUnitScope_Output, OutputElement, &enableIO, sizeof(enableIO));
     if(err != noErr)
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Could not disable audio unit output property: %u", err};
+            "Could not disable audio unit output property: '%s' (%u)", FourCCPrinter{err}.c_str(),
+            err};
 
     // Turn on AudioUnit input
     enableIO = 1;
@@ -656,7 +726,8 @@ void CoreAudioCapture::open(const char *name)
         kAudioUnitScope_Input, InputElement, &enableIO, sizeof(enableIO));
     if(err != noErr)
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Could not enable audio unit input property: %u", err};
+            "Could not enable audio unit input property: '%s' (%u)", FourCCPrinter{err}.c_str(),
+            err};
 
 #if CAN_ENUMERATE
     if(audioDevice != kAudioDeviceUnknown)
@@ -666,14 +737,15 @@ void CoreAudioCapture::open(const char *name)
 
     // set capture callback
     AURenderCallbackStruct input{};
-    input.inputProc = CoreAudioCapture::RecordProcC;
+    input.inputProc = [](void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) noexcept
+    { return static_cast<CoreAudioCapture*>(inRefCon)->RecordProc(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); };
     input.inputProcRefCon = this;
 
     err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_SetInputCallback,
         kAudioUnitScope_Global, InputElement, &input, sizeof(AURenderCallbackStruct));
     if(err != noErr)
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Could not set capture callback: %u", err};
+            "Could not set capture callback: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
 
     // Disable buffer allocation for capture
     UInt32 flag{0};
@@ -681,13 +753,14 @@ void CoreAudioCapture::open(const char *name)
         kAudioUnitScope_Output, InputElement, &flag, sizeof(flag));
     if(err != noErr)
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Could not disable buffer allocation property: %u", err};
+            "Could not disable buffer allocation property: '%s' (%u)", FourCCPrinter{err}.c_str(),
+            err};
 
     // Initialize the device
     err = AudioUnitInitialize(mAudioUnit);
     if(err != noErr)
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Could not initialize audio unit: %u", err};
+            "Could not initialize audio unit: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
 
     // Get the hardware format
     AudioStreamBasicDescription hardwareFormat{};
@@ -696,7 +769,7 @@ void CoreAudioCapture::open(const char *name)
         InputElement, &hardwareFormat, &propertySize);
     if(err != noErr || propertySize != sizeof(hardwareFormat))
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Could not get input format: %u", err};
+            "Could not get input format: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
 
     // Set up the requested format description
     AudioStreamBasicDescription requestedFormat{};
@@ -749,6 +822,7 @@ void CoreAudioCapture::open(const char *name)
     case DevFmtX61:
     case DevFmtX71:
     case DevFmtX714:
+    case DevFmtX7144:
     case DevFmtX3D71:
     case DevFmtAmbi3D:
         throw al::backend_exception{al::backend_error::DeviceError, "%s not supported",
@@ -777,14 +851,14 @@ void CoreAudioCapture::open(const char *name)
         InputElement, &outputFormat, sizeof(outputFormat));
     if(err != noErr)
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Could not set input format: %u", err};
+            "Could not set input format: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
 
     /* Calculate the minimum AudioUnit output format frame count for the pre-
      * conversion ring buffer. Ensure at least 100ms for the total buffer.
      */
     double srateScale{outputFormat.mSampleRate / mDevice->Frequency};
-    auto FrameCount64 = maxu64(static_cast<uint64_t>(std::ceil(mDevice->BufferSize*srateScale)),
-        static_cast<UInt32>(outputFormat.mSampleRate)/10);
+    auto FrameCount64 = std::max(static_cast<uint64_t>(std::ceil(mDevice->BufferSize*srateScale)),
+        static_cast<UInt32>(outputFormat.mSampleRate)/10_u64);
     FrameCount64 += MaxResamplerPadding;
     if(FrameCount64 > std::numeric_limits<int32_t>::max())
         throw al::backend_exception{al::backend_error::DeviceError,
@@ -796,11 +870,11 @@ void CoreAudioCapture::open(const char *name)
         kAudioUnitScope_Global, OutputElement, &outputFrameCount, &propertySize);
     if(err != noErr || propertySize != sizeof(outputFrameCount))
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Could not get input frame count: %u", err};
+            "Could not get input frame count: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
 
     mCaptureData.resize(outputFrameCount * mFrameSize);
 
-    outputFrameCount = static_cast<UInt32>(maxu64(outputFrameCount, FrameCount64));
+    outputFrameCount = static_cast<UInt32>(std::max(uint64_t{outputFrameCount}, FrameCount64));
     mRing = RingBuffer::Create(outputFrameCount, mFrameSize, false);
 
     /* Set up sample converter if needed */
@@ -810,7 +884,7 @@ void CoreAudioCapture::open(const char *name)
             mDevice->Frequency, Resampler::FastBSinc24);
 
 #if CAN_ENUMERATE
-    if(name)
+    if(!name.empty())
         mDevice->DeviceName = name;
     else
     {
@@ -834,21 +908,21 @@ void CoreAudioCapture::start()
     OSStatus err{AudioOutputUnitStart(mAudioUnit)};
     if(err != noErr)
         throw al::backend_exception{al::backend_error::DeviceError,
-            "AudioOutputUnitStart failed: %d", err};
+            "AudioOutputUnitStart failed: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
 }
 
 void CoreAudioCapture::stop()
 {
     OSStatus err{AudioOutputUnitStop(mAudioUnit)};
     if(err != noErr)
-        ERR("AudioOutputUnitStop failed\n");
+        ERR("AudioOutputUnitStop failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
 }
 
-void CoreAudioCapture::captureSamples(al::byte *buffer, uint samples)
+void CoreAudioCapture::captureSamples(std::byte *buffer, uint samples)
 {
     if(!mConverter)
     {
-        mRing->read(buffer, samples);
+        std::ignore = mRing->read(buffer, samples);
         return;
     }
 
@@ -882,28 +956,34 @@ BackendFactory &CoreAudioBackendFactory::getFactory()
     return factory;
 }
 
-bool CoreAudioBackendFactory::init() { return true; }
+bool CoreAudioBackendFactory::init() 
+{ 
+#if CAN_ENUMERATE
+    sDeviceHelper.emplace();
+#endif
+    return true; 
+}
 
 bool CoreAudioBackendFactory::querySupport(BackendType type)
 { return type == BackendType::Playback || type == BackendType::Capture; }
 
-std::string CoreAudioBackendFactory::probe(BackendType type)
+auto CoreAudioBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
 {
-    std::string outnames;
+    std::vector<std::string> 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;
     }
@@ -914,8 +994,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
@@ -930,3 +1009,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;
+}

+ 7 - 5
Engine/lib/openal-soft/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<std::string> final;
 
-    static BackendFactory &getFactory();
+    auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
+
+    static auto getFactory() -> BackendFactory&;
 };
 
 #endif /* BACKENDS_COREAUDIO_H */

+ 92 - 107
Engine/lib/openal-soft/alc/backends/dsound.cpp

@@ -25,10 +25,6 @@
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <memory.h>
-
 #include <cguid.h>
 #include <mmreg.h>
 #ifndef _WAVEFORMATEXTENSIBLE_
@@ -36,15 +32,21 @@
 #include <ksmedia.h>
 #endif
 
+#include <algorithm>
 #include <atomic>
 #include <cassert>
-#include <thread>
+#include <cstdio>
+#include <cstdlib>
+#include <functional>
+#include <memory.h>
+#include <mutex>
 #include <string>
+#include <thread>
 #include <vector>
-#include <algorithm>
-#include <functional>
 
-#include "alnumeric.h"
+#include "alspan.h"
+#include "alstring.h"
+#include "althrd_setname.h"
 #include "comptr.h"
 #include "core/device.h"
 #include "core/helpers.h"
@@ -52,7 +54,6 @@
 #include "dynload.h"
 #include "ringbuffer.h"
 #include "strutils.h"
-#include "threads.h"
 
 /* MinGW-w64 needs this for some unknown reason now. */
 using LPCWAVEFORMATEX = const WAVEFORMATEX*;
@@ -129,10 +130,10 @@ struct DevMap {
     { }
 };
 
-al::vector<DevMap> PlaybackDevices;
-al::vector<DevMap> CaptureDevices;
+std::vector<DevMap> PlaybackDevices;
+std::vector<DevMap> CaptureDevices;
 
-bool checkName(const al::vector<DevMap> &list, const std::string &name)
+bool checkName(const al::span<DevMap> list, const std::string &name)
 {
     auto match_name = [&name](const DevMap &entry) -> bool
     { return entry.name == name; };
@@ -144,7 +145,7 @@ BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR*, voi
     if(!guid)
         return TRUE;
 
-    auto& devices = *static_cast<al::vector<DevMap>*>(data);
+    auto& devices = *static_cast<std::vector<DevMap>*>(data);
     const std::string basename{DEVNAME_HEAD + wstr_to_utf8(desc)};
 
     int count{1};
@@ -176,7 +177,7 @@ struct DSoundPlayback final : public BackendBase {
 
     int mixerProc();
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     bool reset() override;
     void start() override;
     void stop() override;
@@ -189,8 +190,6 @@ struct DSoundPlayback final : public BackendBase {
 
     std::atomic<bool> mKillNow{true};
     std::thread mThread;
-
-    DEF_NEWDEL(DSoundPlayback)
 };
 
 DSoundPlayback::~DSoundPlayback()
@@ -209,7 +208,7 @@ DSoundPlayback::~DSoundPlayback()
 FORCE_ALIGN int DSoundPlayback::mixerProc()
 {
     SetRTPriority();
-    althrd_setname(MIXER_THREAD_NAME);
+    althrd_setname(GetMixerThreadName());
 
     DSBCAPS DSBCaps{};
     DSBCaps.dwSize = sizeof(DSBCaps);
@@ -299,24 +298,22 @@ FORCE_ALIGN int DSoundPlayback::mixerProc()
     return 0;
 }
 
-void DSoundPlayback::open(const char *name)
+void DSoundPlayback::open(std::string_view name)
 {
     HRESULT hr;
     if(PlaybackDevices.empty())
     {
         /* Initialize COM to prevent name truncation */
-        HRESULT hrcom{CoInitialize(nullptr)};
+        ComWrapper com{};
         hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
         if(FAILED(hr))
             ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
-        if(SUCCEEDED(hrcom))
-            CoUninitialize();
     }
 
     const GUID *guid{nullptr};
-    if(!name && !PlaybackDevices.empty())
+    if(name.empty() && !PlaybackDevices.empty())
     {
-        name = PlaybackDevices[0].name.c_str();
+        name = PlaybackDevices[0].name;
         guid = &PlaybackDevices[0].guid;
     }
     else
@@ -332,7 +329,7 @@ void DSoundPlayback::open(const char *name)
                     [&id](const DevMap &entry) -> bool { return entry.guid == id; });
             if(iter == PlaybackDevices.cend())
                 throw al::backend_exception{al::backend_error::NoDevice,
-                    "Device name \"%s\" not found", name};
+                    "Device name \"%.*s\" not found", al::sizei(name), name.data()};
         }
         guid = &iter->guid;
     }
@@ -347,7 +344,7 @@ void DSoundPlayback::open(const char *name)
     //DirectSound Init code
     ComPtr<IDirectSound> ds;
     if(SUCCEEDED(hr))
-        hr = DirectSoundCreate(guid, ds.getPtr(), nullptr);
+        hr = DirectSoundCreate(guid, al::out_ptr(ds), nullptr);
     if(SUCCEEDED(hr))
         hr = ds->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
     if(FAILED(hr))
@@ -425,49 +422,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<WORD>(mDevice->channelsFromFmt());
-    OutputType.Format.wBitsPerSample = static_cast<WORD>(mDevice->bytesFromFmt() * 8);
-    OutputType.Format.nBlockAlign = static_cast<WORD>(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<WORD>(mDevice->channelsFromFmt());
+        OutputType.Format.wBitsPerSample = static_cast<WORD>(mDevice->bytesFromFmt() * 8);
+        OutputType.Format.nBlockAlign = static_cast<WORD>(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, mPrimaryBuffer.getPtr(), 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;
@@ -480,26 +481,21 @@ retry_open:
         DSBDescription.dwBufferBytes = mDevice->BufferSize * OutputType.Format.nBlockAlign;
         DSBDescription.lpwfxFormat = &OutputType.Format;
 
-        hr = mDS->CreateSoundBuffer(&DSBDescription, mBuffer.getPtr(), nullptr);
-        if(FAILED(hr) && mDevice->FmtType == DevFmtFloat)
-        {
-            mDevice->FmtType = DevFmtShort;
-            goto retry_open;
-        }
-    }
+        hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mBuffer), nullptr);
+        if(SUCCEEDED(hr) || mDevice->FmtType != DevFmtFloat)
+            break;
+        mDevice->FmtType = DevFmtShort;
+    } while(FAILED(hr));
 
     if(SUCCEEDED(hr))
     {
-        void *ptr;
-        hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, &ptr);
+        hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, al::out_ptr(mNotifies));
         if(SUCCEEDED(hr))
         {
-            mNotifies = ComPtr<IDirectSoundNotify>{static_cast<IDirectSoundNotify*>(ptr)};
-
             uint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
             assert(num_updates <= MAX_UPDATES);
 
-            std::array<DSBPOSITIONNOTIFY,MAX_UPDATES> nots;
+            std::array<DSBPOSITIONNOTIFY,MAX_UPDATES> nots{};
             for(uint i{0};i < num_updates;++i)
             {
                 nots[i].dwOffset = i * mDevice->UpdateSize * OutputType.Format.nBlockAlign;
@@ -550,10 +546,10 @@ struct DSoundCapture final : public BackendBase {
     DSoundCapture(DeviceBase *device) noexcept : BackendBase{device} { }
     ~DSoundCapture() override;
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     void start() override;
     void stop() override;
-    void captureSamples(al::byte *buffer, uint samples) override;
+    void captureSamples(std::byte *buffer, uint samples) override;
     uint availableSamples() override;
 
     ComPtr<IDirectSoundCapture> mDSC;
@@ -562,8 +558,6 @@ struct DSoundCapture final : public BackendBase {
     DWORD mCursor{0u};
 
     RingBufferPtr mRing;
-
-    DEF_NEWDEL(DSoundCapture)
 };
 
 DSoundCapture::~DSoundCapture()
@@ -577,24 +571,22 @@ DSoundCapture::~DSoundCapture()
 }
 
 
-void DSoundCapture::open(const char *name)
+void DSoundCapture::open(std::string_view name)
 {
     HRESULT hr;
     if(CaptureDevices.empty())
     {
         /* Initialize COM to prevent name truncation */
-        HRESULT hrcom{CoInitialize(nullptr)};
+        ComWrapper com{};
         hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
         if(FAILED(hr))
             ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
-        if(SUCCEEDED(hrcom))
-            CoUninitialize();
     }
 
     const GUID *guid{nullptr};
-    if(!name && !CaptureDevices.empty())
+    if(name.empty() && !CaptureDevices.empty())
     {
-        name = CaptureDevices[0].name.c_str();
+        name = CaptureDevices[0].name;
         guid = &CaptureDevices[0].guid;
     }
     else
@@ -610,7 +602,7 @@ void DSoundCapture::open(const char *name)
                     [&id](const DevMap &entry) -> bool { return entry.guid == id; });
             if(iter == CaptureDevices.cend())
                 throw al::backend_exception{al::backend_error::NoDevice,
-                    "Device name \"%s\" not found", name};
+                    "Device name \"%.*s\" not found", al::sizei(name), name.data()};
         }
         guid = &iter->guid;
     }
@@ -641,6 +633,7 @@ void DSoundCapture::open(const char *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));
@@ -657,6 +650,7 @@ void DSoundCapture::open(const char *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;
@@ -669,8 +663,7 @@ void DSoundCapture::open(const char *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);
@@ -679,9 +672,9 @@ void DSoundCapture::open(const char *name)
     DSCBDescription.lpwfxFormat = &InputType.Format;
 
     //DirectSoundCapture Init code
-    hr = DirectSoundCaptureCreate(guid, mDSC.getPtr(), nullptr);
+    hr = DirectSoundCaptureCreate(guid, al::out_ptr(mDSC), nullptr);
     if(SUCCEEDED(hr))
-        mDSC->CreateCaptureBuffer(&DSCBDescription, mDSCbuffer.getPtr(), nullptr);
+        mDSC->CreateCaptureBuffer(&DSCBDescription, al::out_ptr(mDSCbuffer), nullptr);
     if(SUCCEEDED(hr))
          mRing = RingBuffer::Create(mDevice->BufferSize, InputType.Format.nBlockAlign, false);
 
@@ -719,8 +712,8 @@ void DSoundCapture::stop()
     }
 }
 
-void DSoundCapture::captureSamples(al::byte *buffer, uint samples)
-{ mRing->read(buffer, samples); }
+void DSoundCapture::captureSamples(std::byte *buffer, uint samples)
+{ std::ignore = mRing->read(buffer, samples); }
 
 uint DSoundCapture::availableSamples()
 {
@@ -743,9 +736,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;
     }
@@ -802,40 +795,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>
 {
-    std::string outnames;
+    std::vector<std::string> outnames;
     auto add_device = [&outnames](const DevMap &entry) -> void
-    {
-        /* +1 to also append the null char (to ensure a null-separated list and
-         * double-null terminated list).
-         */
-        outnames.append(entry.name.c_str(), entry.name.length()+1);
-    };
+    { 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;
 }

+ 5 - 5
Engine/lib/openal-soft/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<std::string> 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 */

+ 151 - 143
Engine/lib/openal-soft/alc/backends/jack.cpp

@@ -22,23 +22,26 @@
 
 #include "jack.h"
 
+#include <array>
 #include <cstdlib>
 #include <cstdio>
 #include <cstring>
 #include <memory.h>
-
-#include <array>
+#include <mutex>
 #include <thread>
 #include <functional>
+#include <vector>
 
 #include "alc/alconfig.h"
 #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 "dynload.h"
 #include "ringbuffer.h"
-#include "threads.h"
 
 #include <jack/jack.h>
 #include <jack/ringbuffer.h>
@@ -46,6 +49,8 @@
 
 namespace {
 
+using namespace std::string_view_literals;
+
 #ifdef HAVE_DYNLOAD
 #define JACK_FUNCS(MAGIC)          \
     MAGIC(jack_client_open);       \
@@ -99,19 +104,13 @@ 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
 #define JACKLIB "libjack.dll"
 #else
@@ -124,13 +123,10 @@ bool jack_load()
             return false;
         }
 
-        error = false;
+        std::string missing_funcs;
 #define LOAD_FUNC(f) do {                                                     \
     p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f));      \
-    if(p##f == nullptr) {                                                     \
-        error = true;                                                         \
-        missing_funcs += "\n" #f;                                             \
-    }                                                                         \
+    if(p##f == nullptr) missing_funcs += "\n" #f;                             \
 } while(0)
         JACK_FUNCS(LOAD_FUNC);
 #undef LOAD_FUNC
@@ -139,61 +135,66 @@ bool jack_load()
         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<const char*[],JackDeleter>;
+using JackPortsPtr = std::unique_ptr<const char*[],JackDeleter>; /* NOLINT(*-avoid-c-arrays) */
 
 struct DeviceEntry {
     std::string mName;
     std::string mPattern;
 
+    DeviceEntry() = default;
+    DeviceEntry(const DeviceEntry&) = default;
+    DeviceEntry(DeviceEntry&&) = default;
     template<typename T, typename U>
     DeviceEntry(T&& name, U&& pattern)
         : mName{std::forward<T>(name)}, mPattern{std::forward<U>(pattern)}
     { }
+    ~DeviceEntry();
+
+    DeviceEntry& operator=(const DeviceEntry&) = default;
+    DeviceEntry& operator=(DeviceEntry&&) = default;
 };
+DeviceEntry::~DeviceEntry() = default;
 
-al::vector<DeviceEntry> PlaybackList;
+std::vector<DeviceEntry> PlaybackList;
 
 
-void EnumerateDevices(jack_client_t *client, al::vector<DeviceEntry> &list)
+void EnumerateDevices(jack_client_t *client, std::vector<DeviceEntry> &list)
 {
     std::remove_reference_t<decltype(list)>{}.swap(list);
 
-    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<const char> 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
@@ -202,11 +203,11 @@ void EnumerateDevices(jack_client_t *client, al::vector<DeviceEntry> &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();)
         {
@@ -214,38 +215,32 @@ void EnumerateDevices(jack_client_t *client, al::vector<DeviceEntry> &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<const char> name{listopt->data()+strpos, seppos-strpos};
-            const al::span<const char> 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());
             }
 
@@ -295,7 +290,7 @@ struct JackPlayback final : public BackendBase {
 
     int mixerProc();
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     bool reset() override;
     void start() override;
     void stop() override;
@@ -304,7 +299,7 @@ struct JackPlayback final : public BackendBase {
     std::string mPortPattern;
 
     jack_client_t *mClient{nullptr};
-    std::array<jack_port_t*,MAX_OUTPUT_CHANNELS> mPort{};
+    std::array<jack_port_t*,MaxOutputChannels> mPort{};
 
     std::mutex mMutex;
 
@@ -315,8 +310,6 @@ struct JackPlayback final : public BackendBase {
 
     std::atomic<bool> mKillNow{true};
     std::thread mThread;
-
-    DEF_NEWDEL(JackPlayback)
 };
 
 JackPlayback::~JackPlayback()
@@ -336,22 +329,22 @@ JackPlayback::~JackPlayback()
 
 int JackPlayback::processRt(jack_nframes_t numframes) noexcept
 {
-    std::array<jack_default_audio_sample_t*,MAX_OUTPUT_CHANNELS> out;
-    size_t numchans{0};
+    auto outptrs = std::array<jack_default_audio_sample_t*,MaxOutputChannels>{};
+    auto numchans = size_t{0};
     for(auto port : mPort)
     {
         if(!port || numchans == mDevice->RealOut.Buffer.size())
             break;
-        out[numchans++] = static_cast<float*>(jack_port_get_buffer(port, numframes));
+        outptrs[numchans++] = static_cast<float*>(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<uint>(numframes));
+        mDevice->renderSamples(dst, static_cast<uint>(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](float *outbuf) -> void
+        { std::fill_n(outbuf, numframes, 0.0f); });
     }
 
     return 0;
@@ -360,53 +353,46 @@ int JackPlayback::processRt(jack_nframes_t numframes) noexcept
 
 int JackPlayback::process(jack_nframes_t numframes) noexcept
 {
-    std::array<jack_default_audio_sample_t*,MAX_OUTPUT_CHANNELS> out;
+    std::array<al::span<float>,MaxOutputChannels> out;
     size_t numchans{0};
     for(auto port : mPort)
     {
         if(!port) break;
-        out[numchans++] = static_cast<float*>(jack_port_get_buffer(port, numframes));
+        out[numchans++] = {static_cast<float*>(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<uint>(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.first.len/update_size, outlen)};
+        const auto len2 = size_t{std::min(data.second.len/update_size, outlen-len1)};
+
+        auto src = al::span{reinterpret_cast<float*>(data.first.buf), update_size*len1*numchans};
+        for(size_t i{0};i < len1;++i)
         {
-            const float *RESTRICT in = reinterpret_cast<float*>(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<uint>(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<float*>(data.second.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<float*>(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);
@@ -415,8 +401,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<float> outbuf) -> void
+        { std::fill(outbuf.begin(), outbuf.end(), 0.0f); };
         std::for_each(out.begin(), out.begin()+numchans, clear_buf);
     }
 
@@ -426,45 +412,70 @@ 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<float*>(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 = static_cast<uint>(minz(data.first.len, todo));
-        const auto len2 = static_cast<uint>(minz(data.second.len, todo-len1));
-
-        std::lock_guard<std::mutex> _{mMutex};
-        mDevice->renderSamples(data.first.buf, len1, frame_step);
+        const auto len1 = size_t{data.first.len / update_size};
+        const auto len2 = size_t{data.second.len / update_size};
+
+        std::lock_guard<std::mutex> dlock{mMutex};
+        auto buffer = al::span{reinterpret_cast<float*>(data.first.buf),
+            data.first.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<float*>(data.second.buf),
+                data.second.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;
 }
 
 
-void JackPlayback::open(const char *name)
+void JackPlayback::open(std::string_view name)
 {
     if(!mClient)
     {
         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,
@@ -481,9 +492,9 @@ void JackPlayback::open(const char *name)
     if(PlaybackList.empty())
         EnumerateDevices(mClient, PlaybackList);
 
-    if(!name && !PlaybackList.empty())
+    if(name.empty() && !PlaybackList.empty())
     {
-        name = PlaybackList[0].mName.c_str();
+        name = PlaybackList[0].mName;
         mPortPattern = PlaybackList[0].mPattern;
     }
     else
@@ -493,14 +504,10 @@ void JackPlayback::open(const char *name)
         auto iter = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), check_name);
         if(iter == PlaybackList.cend())
             throw al::backend_exception{al::backend_error::NoDevice,
-                "Device name \"%s\" not found", name?name:""};
+                "Device name \"%.*s\" not found", al::sizei(name), name.data()};
         mPortPattern = iter->mPattern;
     }
 
-    mRTMixing = GetConfigValueBool(name, "jack", "rt-mix", true);
-    jack_set_process_callback(mClient,
-        mRTMixing ? &JackPlayback::processRtC : &JackPlayback::processC, this);
-
     mDevice->DeviceName = name;
 }
 
@@ -511,6 +518,10 @@ bool JackPlayback::reset()
     std::for_each(mPort.begin(), mPort.end(), unregister_port);
     mPort.fill(nullptr);
 
+    mRTMixing = GetConfigValueBool(mDevice->DeviceName, "jack", "rt-mix", true);
+    jack_set_process_callback(mClient,
+        mRTMixing ? &JackPlayback::processRtC : &JackPlayback::processC, this);
+
     /* Ignore the requested buffer metrics and just keep one JACK-sized buffer
      * ready for when requested.
      */
@@ -525,9 +536,9 @@ bool JackPlayback::reset()
     }
     else
     {
-        const char *devname{mDevice->DeviceName.c_str()};
+        const std::string_view devname{mDevice->DeviceName};
         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;
     }
 
@@ -535,27 +546,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));
@@ -575,10 +586,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 std::string_view devname{mDevice->DeviceName};
     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)
         {
@@ -586,7 +597,7 @@ void JackPlayback::start()
             throw al::backend_exception{al::backend_error::DeviceError, "No playback ports found"};
         }
 
-        for(size_t i{0};i < al::size(mPort) && mPort[i];++i)
+        for(size_t i{0};i < std::size(mPort) && mPort[i];++i)
         {
             if(!pnames[i])
             {
@@ -613,7 +624,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);
@@ -651,10 +662,9 @@ void JackPlayback::stop()
 
 ClockLatency JackPlayback::getClockLatency()
 {
-    ClockLatency ret;
-
-    std::lock_guard<std::mutex> _{mMutex};
-    ret.ClockTime = GetDeviceClockTime(mDevice);
+    std::lock_guard<std::mutex> dlock{mMutex};
+    ClockLatency ret{};
+    ret.ClockTime = mDevice->getClockTime();
     ret.Latency  = std::chrono::seconds{mRing ? mRing->readSpace() : mDevice->UpdateSize};
     ret.Latency /= mDevice->Frequency;
 
@@ -674,7 +684,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<jack_options_t>(ClientOptions | JackNoStartServer);
 
     const PathNamePair &binname = GetProcBinary();
@@ -682,7 +692,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)
@@ -700,18 +710,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>
 {
-    std::string outnames;
+    std::vector<std::string> 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:
@@ -722,6 +729,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:

+ 5 - 5
Engine/lib/openal-soft/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<std::string> 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 */

+ 4 - 6
Engine/lib/openal-soft/alc/backends/loopback.cpp

@@ -30,16 +30,14 @@ namespace {
 struct LoopbackBackend final : public BackendBase {
     LoopbackBackend(DeviceBase *device) noexcept : BackendBase{device} { }
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     bool reset() override;
     void start() override;
     void stop() override;
-
-    DEF_NEWDEL(LoopbackBackend)
 };
 
 
-void LoopbackBackend::open(const char *name)
+void LoopbackBackend::open(std::string_view name)
 {
     mDevice->DeviceName = name;
 }
@@ -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<std::string>
+{ return {}; }
 
 BackendPtr LoopbackBackendFactory::createBackend(DeviceBase *device, BackendType)
 { return BackendPtr{new LoopbackBackend{device}}; }

+ 5 - 5
Engine/lib/openal-soft/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<std::string> 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 */

+ 17 - 19
Engine/lib/openal-soft/alc/backends/null.cpp

@@ -30,10 +30,11 @@
 #include <functional>
 #include <thread>
 
-#include "core/device.h"
 #include "almalloc.h"
+#include "alstring.h"
+#include "althrd_setname.h"
+#include "core/device.h"
 #include "core/helpers.h"
-#include "threads.h"
 
 
 namespace {
@@ -41,8 +42,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 {
@@ -50,15 +52,13 @@ struct NullBackend final : public BackendBase {
 
     int mixerProc();
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     bool reset() override;
     void start() override;
     void stop() override;
 
     std::atomic<bool> mKillNow{true};
     std::thread mThread;
-
-    DEF_NEWDEL(NullBackend)
 };
 
 int NullBackend::mixerProc()
@@ -66,7 +66,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();
@@ -105,13 +105,13 @@ int NullBackend::mixerProc()
 }
 
 
-void NullBackend::open(const char *name)
+void NullBackend::open(std::string_view name)
 {
-    if(!name)
-        name = nullDevice;
-    else if(strcmp(name, nullDevice) != 0)
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
-            name};
+    if(name.empty())
+        name = GetDeviceName();
+    else if(name != GetDeviceName())
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+            al::sizei(name), name.data()};
 
     mDevice->DeviceName = name;
 }
@@ -150,19 +150,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>
 {
-    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)

+ 5 - 5
Engine/lib/openal-soft/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<std::string> 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 */

+ 45 - 32
Engine/lib/openal-soft/alc/backends/oboe.cpp

@@ -4,10 +4,11 @@
 #include "oboe.h"
 
 #include <cassert>
+#include <cstdint>
 #include <cstring>
-#include <stdint.h>
 
 #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 {
@@ -28,7 +31,9 @@ struct OboePlayback final : public BackendBase, public oboe::AudioStreamCallback
     oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData,
         int32_t numFrames) override;
 
-    void open(const char *name) override;
+    void onErrorAfterClose(oboe::AudioStream* /* audioStream */, oboe::Result /* error */) override;
+
+    void open(std::string_view name) override;
     bool reset() override;
     void start() override;
     void stop() override;
@@ -46,14 +51,20 @@ oboe::DataCallbackResult OboePlayback::onAudioReady(oboe::AudioStream *oboeStrea
     return oboe::DataCallbackResult::Continue;
 }
 
+void OboePlayback::onErrorAfterClose(oboe::AudioStream*, oboe::Result error)
+{
+    if(error == oboe::Result::ErrorDisconnected)
+        mDevice->handleDisconnect("Oboe AudioStream was disconnected: %s", oboe::convertToText(error));
+    TRACE("Error was %s", oboe::convertToText(error));
+}
 
-void OboePlayback::open(const char *name)
+void OboePlayback::open(std::string_view name)
 {
-    if(!name)
-        name = device_name;
-    else if(std::strcmp(name, device_name) != 0)
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
-            name};
+    if(name.empty())
+        name = GetDeviceName();
+    else if(name != GetDeviceName())
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+            al::sizei(name), name.data()};
 
     /* Open a basic output stream, just to ensure it can work. */
     oboe::ManagedStream stream;
@@ -72,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.
      */
@@ -135,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<int32_t>(mDevice->BufferSize),
+    mStream->setBufferSizeInFrames(std::min(static_cast<int32_t>(mDevice->BufferSize),
         mStream->getBufferCapacityInFrames()));
     TRACE("Got stream with properties:\n%s", oboe::convertToText(mStream.get()));
 
@@ -164,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:
@@ -177,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<uint32_t>(mStream->getFramesPerBurst()));
-    mDevice->BufferSize = maxu(mDevice->UpdateSize * 2,
+    mDevice->BufferSize = std::max(mDevice->UpdateSize*2u,
         static_cast<uint32_t>(mStream->getBufferSizeInFrames()));
 
     return true;
@@ -197,8 +212,7 @@ void OboePlayback::stop()
 {
     oboe::Result result{mStream->stop()};
     if(result != oboe::Result::OK)
-        throw al::backend_exception{al::backend_error::DeviceError, "Failed to stop stream: %s",
-            oboe::convertToText(result)};
+        ERR("Failed to stop stream: %s\n", oboe::convertToText(result));
 }
 
 
@@ -212,28 +226,28 @@ struct OboeCapture final : public BackendBase, public oboe::AudioStreamCallback
     oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData,
         int32_t numFrames) override;
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     void start() override;
     void stop() override;
-    void captureSamples(al::byte *buffer, uint samples) override;
+    void captureSamples(std::byte *buffer, uint samples) override;
     uint availableSamples() override;
 };
 
 oboe::DataCallbackResult OboeCapture::onAudioReady(oboe::AudioStream*, void *audioData,
     int32_t numFrames)
 {
-    mRing->write(audioData, static_cast<uint32_t>(numFrames));
+    std::ignore = mRing->write(audioData, static_cast<uint32_t>(numFrames));
     return oboe::DataCallbackResult::Continue;
 }
 
 
-void OboeCapture::open(const char *name)
+void OboeCapture::open(std::string_view name)
 {
-    if(!name)
-        name = device_name;
-    else if(std::strcmp(name, device_name) != 0)
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
-            name};
+    if(name.empty())
+        name = GetDeviceName();
+    else if(name != GetDeviceName())
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+            al::sizei(name), name.data()};
 
     oboe::AudioStreamBuilder builder;
     builder.setDirection(oboe::Direction::Input)
@@ -259,6 +273,7 @@ void OboeCapture::open(const char *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",
@@ -297,7 +312,7 @@ void OboeCapture::open(const char *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<uint32_t>(mStream->getBytesPerFrame()), false);
 
     mDevice->DeviceName = name;
@@ -315,15 +330,14 @@ void OboeCapture::stop()
 {
     const oboe::Result result{mStream->stop()};
     if(result != oboe::Result::OK)
-        throw al::backend_exception{al::backend_error::DeviceError, "Failed to stop stream: %s",
-            oboe::convertToText(result)};
+        ERR("Failed to stop stream: %s\n", oboe::convertToText(result));
 }
 
 uint OboeCapture::availableSamples()
 { return static_cast<uint>(mRing->readSpace()); }
 
-void OboeCapture::captureSamples(al::byte *buffer, uint samples)
-{ mRing->read(buffer, samples); }
+void OboeCapture::captureSamples(std::byte *buffer, uint samples)
+{ std::ignore = mRing->read(buffer, samples); }
 
 } // namespace
 
@@ -332,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<std::string>
 {
     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)

+ 5 - 5
Engine/lib/openal-soft/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<std::string> 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 */

+ 45 - 114
Engine/lib/openal-soft/alc/backends/opensl.cpp

@@ -23,23 +23,26 @@
 
 #include "opensl.h"
 
-#include <stdlib.h>
 #include <jni.h>
 
-#include <new>
 #include <array>
+#include <cstdlib>
 #include <cstring>
+#include <mutex>
+#include <new>
 #include <thread>
 #include <functional>
 
 #include "albit.h"
 #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 "opthelpers.h"
 #include "ringbuffer.h"
-#include "threads.h"
 
 #include <SLES/OpenSLES.h>
 #include <SLES/OpenSLES_Android.h>
@@ -48,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)
@@ -80,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;
     }
@@ -159,12 +165,10 @@ struct OpenSLPlayback final : public BackendBase {
     ~OpenSLPlayback() override;
 
     void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
-    static void processC(SLAndroidSimpleBufferQueueItf bq, void *context) noexcept
-    { static_cast<OpenSLPlayback*>(context)->process(bq); }
 
     int mixerProc();
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     bool reset() override;
     void start() override;
     void stop() override;
@@ -189,8 +193,6 @@ struct OpenSLPlayback final : public BackendBase {
 
     std::atomic<bool> 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;
@@ -312,13 +314,13 @@ int OpenSLPlayback::mixerProc()
 }
 
 
-void OpenSLPlayback::open(const char *name)
+void OpenSLPlayback::open(std::string_view name)
 {
-    if(!name)
-        name = opensl_device;
-    else if(strcmp(name, opensl_device) != 0)
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
-            name};
+    if(name.empty())
+        name = GetDeviceName();
+    else if(name != GetDeviceName())
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+            al::sizei(name), name.data()};
 
     /* There's only one device, so if it's already open, there's nothing to do. */
     if(mEngineObj) return;
@@ -375,74 +377,6 @@ bool OpenSLPlayback::reset()
 
     mRing = nullptr;
 
-#if 0
-    if(!mDevice->Flags.get<FrequencyRequest>())
-    {
-        /* FIXME: Disabled until I figure out how to get the Context needed for
-         * the getSystemService call.
-         */
-        JNIEnv *env = Android_GetJNIEnv();
-        jobject jctx = Android_GetContext();
-
-        /* Get necessary stuff for using java.lang.Integer,
-         * android.content.Context, and android.media.AudioManager.
-         */
-        jclass int_cls = JCALL(env,FindClass)("java/lang/Integer");
-        jmethodID int_parseint = JCALL(env,GetStaticMethodID)(int_cls,
-            "parseInt", "(Ljava/lang/String;)I"
-        );
-        TRACE("Integer: %p, parseInt: %p\n", int_cls, int_parseint);
-
-        jclass ctx_cls = JCALL(env,FindClass)("android/content/Context");
-        jfieldID ctx_audsvc = JCALL(env,GetStaticFieldID)(ctx_cls,
-            "AUDIO_SERVICE", "Ljava/lang/String;"
-        );
-        jmethodID ctx_getSysSvc = JCALL(env,GetMethodID)(ctx_cls,
-            "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"
-        );
-        TRACE("Context: %p, AUDIO_SERVICE: %p, getSystemService: %p\n",
-              ctx_cls, ctx_audsvc, ctx_getSysSvc);
-
-        jclass audmgr_cls = JCALL(env,FindClass)("android/media/AudioManager");
-        jfieldID audmgr_prop_out_srate = JCALL(env,GetStaticFieldID)(audmgr_cls,
-            "PROPERTY_OUTPUT_SAMPLE_RATE", "Ljava/lang/String;"
-        );
-        jmethodID audmgr_getproperty = JCALL(env,GetMethodID)(audmgr_cls,
-            "getProperty", "(Ljava/lang/String;)Ljava/lang/String;"
-        );
-        TRACE("AudioManager: %p, PROPERTY_OUTPUT_SAMPLE_RATE: %p, getProperty: %p\n",
-              audmgr_cls, audmgr_prop_out_srate, audmgr_getproperty);
-
-        const char *strchars;
-        jstring strobj;
-
-        /* Now make the calls. */
-        //AudioManager audMgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
-        strobj = JCALL(env,GetStaticObjectField)(ctx_cls, ctx_audsvc);
-        jobject audMgr = JCALL(env,CallObjectMethod)(jctx, ctx_getSysSvc, strobj);
-        strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr);
-        TRACE("Context.getSystemService(%s) = %p\n", strchars, audMgr);
-        JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
-
-        //String srateStr = audMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
-        strobj = JCALL(env,GetStaticObjectField)(audmgr_cls, audmgr_prop_out_srate);
-        jstring srateStr = JCALL(env,CallObjectMethod)(audMgr, audmgr_getproperty, strobj);
-        strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr);
-        TRACE("audMgr.getProperty(%s) = %p\n", strchars, srateStr);
-        JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
-
-        //int sampleRate = Integer.parseInt(srateStr);
-        sampleRate = JCALL(env,CallStaticIntMethod)(int_cls, int_parseint, srateStr);
-
-        strchars = JCALL(env,GetStringUTFChars)(srateStr, nullptr);
-        TRACE("Got system sample rate %uhz (%s)\n", sampleRate, strchars);
-        JCALL(env,ReleaseStringUTFChars)(srateStr, strchars);
-
-        if(!sampleRate) sampleRate = device->Frequency;
-        else sampleRate = maxu(sampleRate, MIN_OUTPUT_RATE);
-    }
-#endif
-
     mDevice->FmtChans = DevFmtStereo;
     mDevice->FmtType = DevFmtShort;
 
@@ -564,7 +498,9 @@ void OpenSLPlayback::start()
     PrintErr(result, "bufferQueue->GetInterface");
     if(SL_RESULT_SUCCESS == result)
     {
-        result = VCALL(bufferQueue,RegisterCallback)(&OpenSLPlayback::processC, this);
+        result = VCALL(bufferQueue,RegisterCallback)(
+            [](SLAndroidSimpleBufferQueueItf bq, void *context) noexcept
+            { static_cast<OpenSLPlayback*>(context)->process(bq); }, this);
         PrintErr(result, "bufferQueue->RegisterCallback");
     }
     if(SL_RESULT_SUCCESS != result)
@@ -628,8 +564,8 @@ ClockLatency OpenSLPlayback::getClockLatency()
 {
     ClockLatency ret;
 
-    std::lock_guard<std::mutex> _{mMutex};
-    ret.ClockTime = GetDeviceClockTime(mDevice);
+    std::lock_guard<std::mutex> dlock{mMutex};
+    ret.ClockTime = mDevice->getClockTime();
     ret.Latency  = std::chrono::seconds{mRing->readSpace() * mDevice->UpdateSize};
     ret.Latency /= mDevice->Frequency;
 
@@ -642,13 +578,11 @@ struct OpenSLCapture final : public BackendBase {
     ~OpenSLCapture() override;
 
     void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
-    static void processC(SLAndroidSimpleBufferQueueItf bq, void *context) noexcept
-    { static_cast<OpenSLCapture*>(context)->process(bq); }
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     void start() override;
     void stop() override;
-    void captureSamples(al::byte *buffer, uint samples) override;
+    void captureSamples(std::byte *buffer, uint samples) override;
     uint availableSamples() override;
 
     /* engine interfaces */
@@ -662,8 +596,6 @@ struct OpenSLCapture final : public BackendBase {
     uint mSplOffset{0u};
 
     uint mFrameSize{0};
-
-    DEF_NEWDEL(OpenSLCapture)
 };
 
 OpenSLCapture::~OpenSLCapture()
@@ -686,13 +618,13 @@ void OpenSLCapture::process(SLAndroidSimpleBufferQueueItf) noexcept
 }
 
 
-void OpenSLCapture::open(const char* name)
+void OpenSLCapture::open(std::string_view name)
 {
-    if(!name)
-        name = opensl_device;
-    else if(strcmp(name, opensl_device) != 0)
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
-            name};
+    if(name.empty())
+        name = GetDeviceName();
+    else if(name != GetDeviceName())
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+            al::sizei(name), name.data()};
 
     SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)};
     PrintErr(result, "slCreateEngine");
@@ -710,10 +642,10 @@ void OpenSLCapture::open(const char* 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);
@@ -813,13 +745,15 @@ void OpenSLCapture::open(const char* name)
     }
     if(SL_RESULT_SUCCESS == result)
     {
-        result = VCALL(bufferQueue,RegisterCallback)(&OpenSLCapture::processC, this);
+        result = VCALL(bufferQueue,RegisterCallback)(
+            [](SLAndroidSimpleBufferQueueItf bq, void *context) noexcept
+            { static_cast<OpenSLCapture*>(context)->process(bq); }, this);
         PrintErr(result, "bufferQueue->RegisterCallback");
     }
     if(SL_RESULT_SUCCESS == result)
     {
         const uint chunk_size{mDevice->UpdateSize * mFrameSize};
-        const auto silence = (mDevice->FmtType == DevFmtUByte) ? al::byte{0x80} : al::byte{0};
+        const auto silence = (mDevice->FmtType == DevFmtUByte) ? std::byte{0x80} : std::byte{0};
 
         auto data = mRing->getWriteVector();
         std::fill_n(data.first.buf, data.first.len*chunk_size, silence);
@@ -883,7 +817,7 @@ void OpenSLCapture::stop()
     }
 }
 
-void OpenSLCapture::captureSamples(al::byte *buffer, uint samples)
+void OpenSLCapture::captureSamples(std::byte *buffer, uint samples)
 {
     const uint update_size{mDevice->UpdateSize};
     const uint chunk_size{update_size * mFrameSize};
@@ -895,7 +829,7 @@ void OpenSLCapture::captureSamples(al::byte *buffer, uint samples)
     auto rdata = mRing->getReadVector();
     for(uint i{0};i < samples;)
     {
-        const uint rem{minu(samples - i, update_size - mSplOffset)};
+        const uint rem{std::min(samples - i, update_size - mSplOffset)};
         std::copy_n(rdata.first.buf + mSplOffset*size_t{mFrameSize}, rem*size_t{mFrameSize},
             buffer + i*size_t{mFrameSize});
 
@@ -932,7 +866,7 @@ void OpenSLCapture::captureSamples(al::byte *buffer, uint samples)
         return;
 
     /* For each buffer chunk that was fully read, queue another writable buffer
-     * chunk to keep the OpenSL queue full. This is rather convulated, as a
+     * chunk to keep the OpenSL queue full. This is rather convoluted, as a
      * result of the ring buffer holding more elements than are writable at a
      * given time. The end of the write vector increments when the read pointer
      * advances, which will "expose" a previously unwritable element. So for
@@ -975,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>
 {
-    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)

+ 5 - 5
Engine/lib/openal-soft/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<std::string> 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 */

+ 132 - 115
Engine/lib/openal-soft/alc/backends/oss.cpp

@@ -31,27 +31,26 @@
 #include <algorithm>
 #include <atomic>
 #include <cerrno>
-#include <cstdio>
 #include <cstring>
 #include <exception>
 #include <functional>
 #include <memory>
-#include <new>
 #include <string>
+#include <string_view>
+#include <system_error>
 #include <thread>
 #include <utility>
+#include <vector>
 
-#include "albyte.h"
 #include "alc/alconfig.h"
 #include "almalloc.h"
 #include "alnumeric.h"
-#include "aloptional.h"
+#include "alstring.h"
+#include "althrd_setname.h"
 #include "core/device.h"
 #include "core/helpers.h"
 #include "core/logging.h"
 #include "ringbuffer.h"
-#include "threads.h"
-#include "vector.h"
 
 #include <sys/soundcard.h>
 
@@ -83,117 +82,148 @@
 
 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<typename T, typename U>
+    DevMap(T&& name_, U&& devname_)
+        : name{std::forward<T>(name_)}, device_name{std::forward<U>(devname_)}
+    { }
 };
 
-al::vector<DevMap> PlaybackDevices;
-al::vector<DevMap> CaptureDevices;
+std::vector<DevMap> PlaybackDevices;
+std::vector<DevMap> CaptureDevices;
 
 
 #ifdef ALC_OSS_COMPAT
 
 #define DSP_CAP_OUTPUT 0x00020000
 #define DSP_CAP_INPUT 0x00010000
-void ALCossListPopulate(al::vector<DevMap> &devlist, int type)
+void ALCossListPopulate(std::vector<DevMap> &devlist, int type)
 {
-    devlist.emplace_back(DevMap{DefaultName, (type==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback});
+    devlist.emplace_back(GetDefaultName(), (type==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback);
 }
 
 #else
 
-void ALCossListAppend(al::vector<DevMap> &list, al::span<const char> handle, al::span<const char> 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<typename ...Args>
+    [[nodiscard]] auto open(const char *fname, Args&& ...args) -> bool
+    {
+        close();
+        mFd = ::open(fname, std::forward<Args>(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<DevMap> &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(al::vector<DevMap> &devlist, int type_flag)
+void ALCossListPopulate(std::vector<DevMap> &devlist, int type_flag)
 {
-    int fd{open("/dev/mixer", O_RDONLY)};
-    if(fd < 0)
+    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<const char> 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<const char> 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(),
@@ -201,7 +231,7 @@ done:
         { 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)};
@@ -231,19 +261,17 @@ struct OSSPlayback final : public BackendBase {
 
     int mixerProc();
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     bool reset() override;
     void start() override;
     void stop() override;
 
     int mFd{-1};
 
-    al::vector<al::byte> mMixData;
+    std::vector<std::byte> mMixData;
 
     std::atomic<bool> mKillNow{true};
     std::thread mThread;
-
-    DEF_NEWDEL(OSSPlayback)
 };
 
 OSSPlayback::~OSSPlayback()
@@ -257,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()};
@@ -269,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;
         }
 
-        al::byte *write_ptr{mMixData.data()};
-        size_t to_write{mMixData.size()};
-        mDevice->renderSamples(write_ptr, static_cast<uint>(to_write/frame_size), frame_step);
-        while(to_write > 0 && !mKillNow.load(std::memory_order_acquire))
+        al::span write_buf{mMixData};
+        mDevice->renderSamples(write_buf.data(), static_cast<uint>(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<size_t>(wrote);
-            write_ptr += wrote;
+            write_buf = write_buf.subspan(static_cast<size_t>(wrote));
         }
     }
 
@@ -308,11 +336,11 @@ int OSSPlayback::mixerProc()
 }
 
 
-void OSSPlayback::open(const char *name)
+void OSSPlayback::open(std::string_view name)
 {
     const char *devname{DefaultPlayback.c_str()};
-    if(!name)
-        name = DefaultName;
+    if(name.empty())
+        name = GetDefaultName();
     else
     {
         if(PlaybackDevices.empty())
@@ -324,14 +352,14 @@ void OSSPlayback::open(const char *name)
         );
         if(iter == PlaybackDevices.cend())
             throw al::backend_exception{al::backend_error::NoDevice,
-                "Device name \"%s\" not found", name};
+                "Device name \"%.*s\" not found", 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);
@@ -367,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);
@@ -383,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)
@@ -413,7 +434,7 @@ bool OSSPlayback::reset()
 
     setDefaultChannelOrder();
 
-    mMixData.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
+    mMixData.resize(size_t{mDevice->UpdateSize} * mDevice->frameSizeFromFmt());
 
     return true;
 }
@@ -437,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());
 }
 
 
@@ -447,10 +468,10 @@ struct OSScapture final : public BackendBase {
 
     int recordProc();
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     void start() override;
     void stop() override;
-    void captureSamples(al::byte *buffer, uint samples) override;
+    void captureSamples(std::byte *buffer, uint samples) override;
     uint availableSamples() override;
 
     int mFd{-1};
@@ -459,8 +480,6 @@ struct OSScapture final : public BackendBase {
 
     std::atomic<bool> mKillNow{true};
     std::thread mThread;
-
-    DEF_NEWDEL(OSScapture)
 };
 
 OSScapture::~OSScapture()
@@ -474,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))
@@ -483,16 +502,16 @@ 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;
@@ -504,8 +523,9 @@ int OSScapture::recordProc()
             ssize_t amt{read(mFd, vec.first.buf, vec.first.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<size_t>(amt)/frame_size);
@@ -516,11 +536,11 @@ int OSScapture::recordProc()
 }
 
 
-void OSScapture::open(const char *name)
+void OSScapture::open(std::string_view name)
 {
     const char *devname{DefaultCapture.c_str()};
-    if(!name)
-        name = DefaultName;
+    if(name.empty())
+        name = GetDefaultName();
     else
     {
         if(CaptureDevices.empty())
@@ -532,14 +552,14 @@ void OSScapture::open(const char *name)
         );
         if(iter == CaptureDevices.cend())
             throw al::backend_exception{al::backend_error::NoDevice,
-                "Device name \"%s\" not found", name};
+                "Device name \"%.*s\" not found", 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)
@@ -566,13 +586,13 @@ void OSScapture::open(const char *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));
@@ -617,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(al::byte *buffer, uint samples)
-{ mRing->read(buffer, samples); }
+void OSScapture::captureSamples(std::byte *buffer, uint samples)
+{ std::ignore = mRing->read(buffer, samples); }
 
 uint OSScapture::availableSamples()
 { return static_cast<uint>(mRing->readSpace()); }
@@ -637,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;
@@ -648,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>
 {
-    std::string outnames;
-
+    std::vector<std::string> 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)
@@ -667,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;
     }

+ 5 - 5
Engine/lib/openal-soft/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<std::string> 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 */

Dosya farkı çok büyük olduğundan ihmal edildi
+ 386 - 223
Engine/lib/openal-soft/alc/backends/pipewire.cpp


+ 9 - 5
Engine/lib/openal-soft/alc/backends/pipewire.h

@@ -2,22 +2,26 @@
 #define BACKENDS_PIPEWIRE_H
 
 #include <string>
+#include <vector>
 
+#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<std::string> final;
 
-    static BackendFactory &getFactory();
+    auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
+
+    static auto getFactory() -> BackendFactory&;
 };
 
 #endif /* BACKENDS_PIPEWIRE_H */

+ 185 - 91
Engine/lib/openal-soft/alc/backends/portaudio.cpp

@@ -26,21 +26,20 @@
 #include <cstdlib>
 #include <cstring>
 
+#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 <portaudio.h>
+#include <portaudio.h> /* 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
@@ -51,6 +50,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);
@@ -64,12 +65,49 @@ 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;
+    bool mIsPlayback{};
+    bool mIsCapture{};
+};
+std::vector<DeviceEntry> DeviceNames;
+
+void EnumerateDevices()
+{
+    const auto devcount = Pa_GetDeviceCount();
+    if(devcount < 0)
+    {
+        ERR("Error getting device count: %s\n", Pa_GetErrorText(devcount));
+        return;
+    }
+
+    std::vector<DeviceEntry>(static_cast<uint>(devcount)).swap(DeviceNames);
+    PaDeviceIndex idx{0};
+    for(auto &entry : DeviceNames)
+    {
+        if(auto info = Pa_GetDeviceInfo(idx); info && info->name)
+        {
+#ifdef _WIN32
+            entry.mName = "OpenAL Soft on "+std::string{info->name};
+#else
+            entry.mName = info->name;
+#endif
+            entry.mIsPlayback = (info->maxOutputChannels > 0);
+            entry.mIsCapture = (info->maxInputChannels > 0);
+            TRACE("Device %d \"%s\": %d playback, %d capture channels\n", idx, entry.mName.c_str(),
+                info->maxOutputChannels, info->maxInputChannels);
+        }
+        ++idx;
+    }
+}
 
 struct PortPlayback final : public BackendBase {
     PortPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
@@ -77,24 +115,17 @@ 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<PortPlayback*>(userData)->writeCallback(inputBuffer, outputBuffer,
-            framesPerBuffer, timeInfo, statusFlags);
-    }
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     bool reset() override;
     void start() override;
     void stop() override;
 
     PaStream *mStream{nullptr};
     PaStreamParameters mParams{};
+    DevFmtChannels mChannelConfig{};
+    uint mAmbiOrder{};
     uint mUpdateSize{0u};
-
-    DEF_NEWDEL(PortPlayback)
 };
 
 PortPlayback::~PortPlayback()
@@ -115,22 +146,38 @@ int PortPlayback::writeCallback(const void*, void *outputBuffer, unsigned long f
 }
 
 
-void PortPlayback::open(const char *name)
+void PortPlayback::open(std::string_view name)
 {
-    if(!name)
-        name = pa_device;
-    else if(strcmp(name, pa_device) != 0)
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
-            name};
+    if(DeviceNames.empty())
+        EnumerateDevices();
+
+    int deviceid{-1};
+    if(name.empty())
+    {
+        if(auto devidopt = ConfigValueInt({}, "port", "device"))
+            deviceid = *devidopt;
+        if(deviceid < 0 || static_cast<uint>(deviceid) >= DeviceNames.size())
+            deviceid = Pa_GetDefaultOutputDevice();
+        name = DeviceNames.at(static_cast<uint>(deviceid)).mName;
+    }
+    else
+    {
+        auto iter = std::find_if(DeviceNames.cbegin(), DeviceNames.cend(),
+            [name](const DeviceEntry &entry) { return entry.mIsPlayback && 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<int>(std::distance(DeviceNames.cbegin(), iter));
+    }
 
     PaStreamParameters params{};
-    auto devidopt = ConfigValueInt(nullptr, "port", "device");
-    if(devidopt && *devidopt >= 0) params.device = *devidopt;
-    else params.device = Pa_GetDefaultOutputDevice();
+    params.device = deviceid;
     params.suggestedLatency = mDevice->BufferSize / static_cast<double>(mDevice->Frequency);
     params.hostApiSpecificStreamInfo = nullptr;
 
-    params.channelCount = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
+    mChannelConfig = mDevice->FmtChans;
+    mAmbiOrder = mDevice->mAmbiOrder;
+    params.channelCount = static_cast<int>(mDevice->channelsFromFmt());
 
     switch(mDevice->FmtType)
     {
@@ -155,19 +202,21 @@ void PortPlayback::open(const char *name)
         break;
     }
 
-retry_open:
+    static constexpr auto writeCallback = [](const void *inputBuffer, void *outputBuffer,
+        unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
+        const PaStreamCallbackFlags statusFlags, void *userData) noexcept
+    {
+        return static_cast<PortPlayback*>(userData)->writeCallback(inputBuffer, outputBuffer,
+            framesPerBuffer, timeInfo, statusFlags);
+    };
     PaStream *stream{};
-    PaError err{Pa_OpenStream(&stream, nullptr, &params, mDevice->Frequency, mDevice->UpdateSize,
-        paNoFlag, &PortPlayback::writeCallbackC, this)};
-    if(err != paNoError)
+    while(PaError err{Pa_OpenStream(&stream, nullptr, &params, mDevice->Frequency,
+        mDevice->UpdateSize, paNoFlag, writeCallback, this)})
     {
-        if(params.sampleFormat == paFloat32)
-        {
-            params.sampleFormat = paInt16;
-            goto retry_open;
-        }
-        throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s",
-            Pa_GetErrorText(err)};
+        if(params.sampleFormat != paFloat32)
+            throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s",
+                Pa_GetErrorText(err)};
+        params.sampleFormat = paInt16;
     }
 
     Pa_CloseStream(mStream);
@@ -182,7 +231,18 @@ bool PortPlayback::reset()
 {
     const PaStreamInfo *streamInfo{Pa_GetStreamInfo(mStream)};
     mDevice->Frequency = static_cast<uint>(streamInfo->sampleRate);
+    mDevice->FmtChans = mChannelConfig;
+    mDevice->mAmbiOrder = mAmbiOrder;
     mDevice->UpdateSize = mUpdateSize;
+    mDevice->BufferSize = mUpdateSize * 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<uint>(std::clamp(sampleLatency,
+            double(mDevice->BufferSize), double{std::numeric_limits<int>::max()}));
+    }
 
     if(mParams.sampleFormat == paInt8)
         mDevice->FmtType = DevFmtByte;
@@ -200,15 +260,6 @@ bool PortPlayback::reset()
         return false;
     }
 
-    if(mParams.channelCount >= 2)
-        mDevice->FmtChans = DevFmtStereo;
-    else if(mParams.channelCount == 1)
-        mDevice->FmtChans = DevFmtMono;
-    else
-    {
-        ERR("Unexpected channel count: %u\n", mParams.channelCount);
-        return false;
-    }
     setDefaultChannelOrder();
 
     return true;
@@ -216,16 +267,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));
 }
 
@@ -235,27 +284,18 @@ 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<PortCapture*>(userData)->readCallback(inputBuffer, outputBuffer,
-            framesPerBuffer, timeInfo, statusFlags);
-    }
+        const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) const noexcept;
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     void start() override;
     void stop() override;
-    void captureSamples(al::byte *buffer, uint samples) override;
+    void captureSamples(std::byte *buffer, uint samples) override;
     uint availableSamples() override;
 
     PaStream *mStream{nullptr};
-    PaStreamParameters mParams;
+    PaStreamParameters mParams{};
 
     RingBufferPtr mRing{nullptr};
-
-    DEF_NEWDEL(PortCapture)
 };
 
 PortCapture::~PortCapture()
@@ -268,30 +308,43 @@ 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(const char *name)
+void PortCapture::open(std::string_view name)
 {
-    if(!name)
-        name = pa_device;
-    else if(strcmp(name, pa_device) != 0)
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
-            name};
+    if(DeviceNames.empty())
+        EnumerateDevices();
 
-    uint samples{mDevice->BufferSize};
-    samples = maxu(samples, 100 * mDevice->Frequency / 1000);
-    uint frame_size{mDevice->frameSizeFromFmt()};
+    int deviceid{};
+    if(name.empty())
+    {
+        if(auto devidopt = ConfigValueInt({}, "port", "capture"))
+            deviceid = *devidopt;
+        if(deviceid < 0 || static_cast<uint>(deviceid) >= DeviceNames.size())
+            deviceid = Pa_GetDefaultInputDevice();
+        name = DeviceNames.at(static_cast<uint>(deviceid)).mName;
+    }
+    else
+    {
+        auto iter = std::find_if(DeviceNames.cbegin(), DeviceNames.cend(),
+            [name](const DeviceEntry &entry) { return entry.mIsCapture && 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<int>(std::distance(DeviceNames.cbegin(), iter));
+    }
+
+    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;
 
@@ -319,8 +372,15 @@ void PortCapture::open(const char *name)
     }
     mParams.channelCount = static_cast<int>(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<PortCapture*>(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)};
@@ -331,16 +391,14 @@ void PortCapture::open(const char *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));
 }
 
@@ -348,16 +406,14 @@ void PortCapture::stop()
 uint PortCapture::availableSamples()
 { return static_cast<uint>(mRing->readSpace()); }
 
-void PortCapture::captureSamples(al::byte *buffer, uint samples)
-{ mRing->read(buffer, samples); }
+void PortCapture::captureSamples(std::byte *buffer, uint samples)
+{ std::ignore = mRing->read(buffer, samples); }
 
 } // namespace
 
 
 bool PortBackendFactory::init()
 {
-    PaError err;
-
 #ifdef HAVE_DYNLOAD
     if(!pa_handle)
     {
@@ -391,12 +447,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);
@@ -405,7 +464,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;
@@ -417,18 +477,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>
 {
-    std::string outnames;
+    std::vector<std::string> devices;
+
+    EnumerateDevices();
+    int defaultid{-1};
     switch(type)
     {
     case BackendType::Playback:
+        defaultid = Pa_GetDefaultOutputDevice();
+        if(auto devidopt = ConfigValueInt({}, "port", "device"); devidopt && *devidopt >= 0
+            && static_cast<uint>(*devidopt) < DeviceNames.size())
+            defaultid = *devidopt;
+
+        for(size_t i{0};i < DeviceNames.size();++i)
+        {
+            if(DeviceNames[i].mIsPlayback)
+            {
+                if(defaultid >= 0 && static_cast<uint>(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<uint>(*devidopt) < DeviceNames.size())
+            defaultid = *devidopt;
+
+        for(size_t i{0};i < DeviceNames.size();++i)
+        {
+            if(DeviceNames[i].mIsCapture)
+            {
+                if(defaultid >= 0 && static_cast<uint>(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)

+ 5 - 5
Engine/lib/openal-soft/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<std::string> 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 */

Dosya farkı çok büyük olduğundan ihmal edildi
+ 254 - 226
Engine/lib/openal-soft/alc/backends/pulseaudio.cpp


+ 13 - 5
Engine/lib/openal-soft/alc/backends/pulseaudio.h

@@ -1,19 +1,27 @@
 #ifndef BACKENDS_PULSEAUDIO_H
 #define BACKENDS_PULSEAUDIO_H
 
+#include <string>
+#include <vector>
+
+#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<std::string> 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 */

+ 41 - 23
Engine/lib/openal-soft/alc/backends/sdl2.cpp

@@ -26,6 +26,7 @@
 #include <cstdlib>
 #include <cstring>
 #include <string>
+#include <string_view>
 
 #include "almalloc.h"
 #include "alnumeric.h"
@@ -46,17 +47,17 @@ namespace {
 #define DEVNAME_PREFIX ""
 #endif
 
-constexpr char defaultDeviceName[] = DEVNAME_PREFIX "Default Device";
+constexpr auto getDevicePrefix() noexcept -> std::string_view { return DEVNAME_PREFIX; }
+constexpr auto getDefaultDeviceName() noexcept -> std::string_view
+{ return DEVNAME_PREFIX "Default Device"; }
 
 struct Sdl2Backend final : public BackendBase {
     Sdl2Backend(DeviceBase *device) noexcept : BackendBase{device} { }
     ~Sdl2Backend() override;
 
     void audioCallback(Uint8 *stream, int len) noexcept;
-    static void audioCallbackC(void *ptr, Uint8 *stream, int len) noexcept
-    { static_cast<Sdl2Backend*>(ptr)->audioCallback(stream, len); }
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     bool reset() override;
     void start() override;
     void stop() override;
@@ -68,8 +69,6 @@ struct Sdl2Backend final : public BackendBase {
     DevFmtChannels mFmtChans{};
     DevFmtType     mFmtType{};
     uint mUpdateSize{0u};
-
-    DEF_NEWDEL(Sdl2Backend)
 };
 
 Sdl2Backend::~Sdl2Backend()
@@ -86,7 +85,7 @@ void Sdl2Backend::audioCallback(Uint8 *stream, int len) noexcept
     mDevice->renderSamples(stream, ulen / mFrameSize, mDevice->channelsFromFmt());
 }
 
-void Sdl2Backend::open(const char *name)
+void Sdl2Backend::open(std::string_view name)
 {
     SDL_AudioSpec want{}, have{};
 
@@ -102,24 +101,39 @@ void Sdl2Backend::open(const char *name)
     case DevFmtFloat: want.format = AUDIO_F32; break;
     }
     want.channels = (mDevice->FmtChans == DevFmtMono) ? 1 : 2;
-    want.samples = static_cast<Uint16>(minu(mDevice->UpdateSize, 8192));
-    want.callback = &Sdl2Backend::audioCallbackC;
+    want.samples = static_cast<Uint16>(std::min(mDevice->UpdateSize, 8192u));
+    want.callback = [](void *ptr, Uint8 *stream, int len) noexcept
+    { return static_cast<Sdl2Backend*>(ptr)->audioCallback(stream, len); };
     want.userdata = this;
 
     /* Passing nullptr to SDL_OpenAudioDevice opens a default, which isn't
      * necessarily the first in the list.
      */
+    const auto defaultDeviceName = getDefaultDeviceName();
     SDL_AudioDeviceID devid;
-    if(!name || strcmp(name, defaultDeviceName) == 0)
+    if(name.empty() || name == defaultDeviceName)
+    {
+        name = defaultDeviceName;
         devid = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE);
+    }
     else
     {
-        const size_t prefix_len = strlen(DEVNAME_PREFIX);
-        if(strncmp(name, DEVNAME_PREFIX, prefix_len) == 0)
-            devid = SDL_OpenAudioDevice(name+prefix_len, SDL_FALSE, &want, &have,
+        const auto namePrefix = getDevicePrefix();
+        if(name.size() >= namePrefix.size() && name.substr(0, namePrefix.size()) == namePrefix)
+        {
+            /* Copy the string_view to a string to ensure it's null terminated
+             * for this call.
+             */
+            const std::string devname{name.substr(namePrefix.size())};
+            devid = SDL_OpenAudioDevice(devname.c_str(), SDL_FALSE, &want, &have,
                 SDL_AUDIO_ALLOW_ANY_CHANGE);
+        }
         else
-            devid = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE);
+        {
+            const std::string devname{name};
+            devid = SDL_OpenAudioDevice(devname.c_str(), SDL_FALSE, &want, &have,
+                SDL_AUDIO_ALLOW_ANY_CHANGE);
+        }
     }
     if(!devid)
         throw al::backend_exception{al::backend_error::NoDevice, "%s", SDL_GetError()};
@@ -161,7 +175,7 @@ void Sdl2Backend::open(const char *name)
     mFmtType = devtype;
     mUpdateSize = have.samples;
 
-    mDevice->DeviceName = name ? name : defaultDeviceName;
+    mDevice->DeviceName = name;
 }
 
 bool Sdl2Backend::reset()
@@ -195,23 +209,27 @@ 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>
 {
-    std::string outnames;
+    std::vector<std::string> 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<unsigned int>(num_devices));
+    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);
+        std::string outname{getDevicePrefix()};
+        if(const char *name = SDL_GetAudioDeviceName(i, SDL_FALSE))
+            outname += name;
+        else
+            outname += "Unknown Device Name #"+std::to_string(i);
+        outnames.emplace_back(std::move(outname));
     }
     return outnames;
 }

+ 5 - 5
Engine/lib/openal-soft/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<std::string> 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 */

+ 120 - 119
Engine/lib/openal-soft/alc/backends/sndio.cpp

@@ -22,31 +22,35 @@
 
 #include "sndio.h"
 
+#include <cinttypes>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
 #include <functional>
-#include <inttypes.h>
 #include <poll.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <system_error>
 #include <thread>
+#include <vector>
 
 #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 "threads.h"
-#include "vector.h"
 
-#include <sndio.h>
+#include <sndio.h> /* 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); }
 };
@@ -57,7 +61,7 @@ struct SndioPlayback final : public BackendBase {
 
     int mixerProc();
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     bool reset() override;
     void start() override;
     void stop() override;
@@ -65,12 +69,10 @@ struct SndioPlayback final : public BackendBase {
     sio_hdl *mSndHandle{nullptr};
     uint mFrameStep{};
 
-    al::vector<al::byte> mBuffer;
+    std::vector<std::byte> mBuffer;
 
     std::atomic<bool> mKillNow{true};
     std::thread mThread;
-
-    DEF_NEWDEL(SndioPlayback)
 };
 
 SndioPlayback::~SndioPlayback()
@@ -86,12 +88,12 @@ 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))
     {
-        al::span<al::byte> buffer{mBuffer};
+        al::span<std::byte> buffer{mBuffer};
 
         mDevice->renderSamples(buffer.data(), static_cast<uint>(buffer.size() / frameSize),
             frameStep);
@@ -112,13 +114,13 @@ int SndioPlayback::mixerProc()
 }
 
 
-void SndioPlayback::open(const char *name)
+void SndioPlayback::open(std::string_view name)
 {
-    if(!name)
-        name = sndio_device;
-    else if(strcmp(name, sndio_device) != 0)
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
-            name};
+    if(name.empty())
+        name = GetDefaultName();
+    else if(name != GetDefaultName())
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+            al::sizei(name), name.data()};
 
     sio_hdl *sndHandle{sio_open(nullptr, SIO_PLAY, 0)};
     if(!sndHandle)
@@ -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,11 +234,11 @@ retry_params:
     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(), al::byte{});
+        std::fill(mBuffer.begin(), mBuffer.end(), std::byte{});
     else if(par.bits == 8)
-        std::fill_n(mBuffer.data(), mBuffer.size(), al::byte(0x80));
+        std::fill_n(mBuffer.data(), mBuffer.size(), std::byte(0x80));
     else if(par.bits == 16)
         std::fill_n(reinterpret_cast<uint16_t*>(mBuffer.data()), mBuffer.size()/2, 0x8000);
     else if(par.bits == 32)
@@ -280,10 +285,10 @@ struct SndioCapture final : public BackendBase {
 
     int recordProc();
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     void start() override;
     void stop() override;
-    void captureSamples(al::byte *buffer, uint samples) override;
+    void captureSamples(std::byte *buffer, uint samples) override;
     uint availableSamples() override;
 
     sio_hdl *mSndHandle{nullptr};
@@ -292,8 +297,6 @@ struct SndioCapture final : public BackendBase {
 
     std::atomic<bool> 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<pollfd[]>(static_cast<uint>(nfds_pre));
+    auto fds = std::vector<pollfd>(static_cast<uint>(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<uint>(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<al::byte> buffer{data.first.buf, data.first.len*frameSize};
+        al::span<std::byte> buffer{data.first.buf, data.first.len*frameSize};
         while(!buffer.empty())
         {
             size_t got{sio_read(mSndHandle, buffer.data(), buffer.size())};
@@ -373,8 +377,8 @@ int SndioCapture::recordProc()
         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<char,4096> junk;
+            sio_read(mSndHandle, junk.data(), junk.size() - (junk.size()%frameSize));
         }
     }
 
@@ -382,13 +386,13 @@ int SndioCapture::recordProc()
 }
 
 
-void SndioCapture::open(const char *name)
+void SndioCapture::open(std::string_view name)
 {
-    if(!name)
-        name = sndio_device;
-    else if(strcmp(name, sndio_device) != 0)
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
-            name};
+    if(name.empty())
+        name = GetDefaultName();
+    else if(name != GetDefaultName())
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+            al::sizei(name), name.data()};
 
     mSndHandle = sio_open(nullptr, SIO_REC, true);
     if(mSndHandle == nullptr)
@@ -431,12 +435,12 @@ void SndioCapture::open(const char *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,
-            "Failed to set device praameters"};
+            "Failed to set device parameters"};
 
     if(par.bps > 1 && par.le != SIO_LE_NATIVE)
         throw al::backend_exception{al::backend_error::DeviceError,
@@ -461,7 +465,7 @@ void SndioCapture::open(const char *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<uint>(mRing->writeSpace());
     mDevice->UpdateSize = par.round;
 
@@ -496,8 +500,8 @@ void SndioCapture::stop()
         ERR("Error stopping device\n");
 }
 
-void SndioCapture::captureSamples(al::byte *buffer, uint samples)
-{ mRing->read(buffer, samples); }
+void SndioCapture::captureSamples(std::byte *buffer, uint samples)
+{ std::ignore = mRing->read(buffer, samples); }
 
 uint SndioCapture::availableSamples()
 { return static_cast<uint>(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>
 {
-    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)

+ 5 - 5
Engine/lib/openal-soft/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<std::string> 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 */

+ 29 - 34
Engine/lib/openal-soft/alc/backends/solaris.cpp

@@ -35,24 +35,26 @@
 #include <poll.h>
 #include <math.h>
 #include <string.h>
+#include <vector>
 
 #include <thread>
 #include <functional>
 
-#include "albyte.h"
 #include "alc/alconfig.h"
+#include "alstring.h"
+#include "althrd_setname.h"
 #include "core/device.h"
 #include "core/helpers.h"
 #include "core/logging.h"
-#include "threads.h"
-#include "vector.h"
 
 #include <sys/audioio.h>
 
 
 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"};
 
@@ -63,7 +65,7 @@ struct SolarisBackend final : public BackendBase {
 
     int mixerProc();
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     bool reset() override;
     void start() override;
     void stop() override;
@@ -71,12 +73,10 @@ struct SolarisBackend final : public BackendBase {
     int mFd{-1};
 
     uint mFrameStep{};
-    al::vector<al::byte> mBuffer;
+    std::vector<std::byte> mBuffer;
 
     std::atomic<bool> mKillNow{true};
     std::thread mThread;
-
-    DEF_NEWDEL(SolarisBackend)
 };
 
 SolarisBackend::~SolarisBackend()
@@ -89,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))
@@ -116,12 +116,12 @@ int SolarisBackend::mixerProc()
             continue;
         }
 
-        al::byte *write_ptr{mBuffer.data()};
-        size_t to_write{mBuffer.size()};
-        mDevice->renderSamples(write_ptr, static_cast<uint>(to_write/frame_size), frame_step);
-        while(to_write > 0 && !mKillNow.load(std::memory_order_acquire))
+        al::span<std::byte> buffer{mBuffer};
+        mDevice->renderSamples(buffer.data(), static_cast<uint>(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)
@@ -131,8 +131,7 @@ int SolarisBackend::mixerProc()
                 break;
             }
 
-            to_write -= static_cast<size_t>(wrote);
-            write_ptr += wrote;
+            buffer = buffer.subspan(static_cast<size_t>(wrote));
         }
     }
 
@@ -140,13 +139,13 @@ int SolarisBackend::mixerProc()
 }
 
 
-void SolarisBackend::open(const char *name)
+void SolarisBackend::open(std::string_view name)
 {
-    if(!name)
-        name = solaris_device;
-    else if(strcmp(name, solaris_device) != 0)
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
-            name};
+    if(name.empty())
+        name = GetDefaultName();
+    else if(name != GetDefaultName())
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+            al::sizei(name), name.data()};
 
     int fd{::open(solaris_driver.c_str(), O_WRONLY)};
     if(fd == -1)
@@ -231,7 +230,7 @@ bool SolarisBackend::reset()
     setDefaultChannelOrder();
 
     mBuffer.resize(mDevice->UpdateSize * size_t{frame_size});
-    std::fill(mBuffer.begin(), mBuffer.end(), al::byte{});
+    std::fill(mBuffer.begin(), mBuffer.end(), std::byte{});
 
     return true;
 }
@@ -268,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;
 }
@@ -276,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>
 {
-    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)

+ 5 - 5
Engine/lib/openal-soft/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<std::string> 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 */

Dosya farkı çok büyük olduğundan ihmal edildi
+ 632 - 136
Engine/lib/openal-soft/alc/backends/wasapi.cpp


+ 7 - 5
Engine/lib/openal-soft/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<std::string> final;
 
-    static BackendFactory &getFactory();
+    auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
+
+    static auto getFactory() -> BackendFactory&;
 };
 
 #endif /* BACKENDS_WASAPI_H */

+ 83 - 85
Engine/lib/openal-soft/alc/backends/wave.cpp

@@ -31,24 +31,26 @@
 #include <cstring>
 #include <exception>
 #include <functional>
+#include <system_error>
 #include <thread>
+#include <vector>
 
 #include "albit.h"
-#include "albyte.h"
 #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"
 #include "core/logging.h"
 #include "opthelpers.h"
 #include "strutils.h"
-#include "threads.h"
-#include "vector.h"
 
 
 namespace {
 
+using namespace std::string_view_literals;
 using std::chrono::seconds;
 using std::chrono::milliseconds;
 using std::chrono::nanoseconds;
@@ -56,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<FILE*> f) { fclose(f); }
+};
+using FilePtr = std::unique_ptr<FILE,FileDeleter>;
+
+[[nodiscard]] constexpr auto GetDeviceName() noexcept { return "Wave File Writer"sv; }
 
-constexpr ubyte SUBTYPE_PCM[]{
+constexpr std::array<ubyte,16> 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<ubyte,16> 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<ubyte,16> 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<ubyte,16> 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<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff) };
-    fwrite(data, 1, 2, f);
+    std::array data{static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff)};
+    fwrite(data.data(), 1, data.size(), f);
 }
 
 void fwrite32le(uint val, FILE *f)
 {
-    ubyte data[4]{ static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff),
-        static_cast<ubyte>((val>>16)&0xff), static_cast<ubyte>((val>>24)&0xff) };
-    fwrite(data, 1, 4, f);
+    std::array data{static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff),
+        static_cast<ubyte>((val>>16)&0xff), static_cast<ubyte>((val>>24)&0xff)};
+    fwrite(data.data(), 1, data.size(), f);
 }
 
 
@@ -97,34 +104,27 @@ struct WaveBackend final : public BackendBase {
 
     int mixerProc();
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     bool reset() override;
     void start() override;
     void stop() override;
 
-    FILE *mFile{nullptr};
+    FilePtr mFile{nullptr};
     long mDataStart{-1};
 
-    al::vector<al::byte> mBuffer;
+    std::vector<std::byte> mBuffer;
 
     std::atomic<bool> 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()};
@@ -155,13 +155,13 @@ int WaveBackend::mixerProc()
 
                 if(bytesize == 2)
                 {
-                    const size_t len{mBuffer.size() & ~size_t{1}};
+                    const size_t len{mBuffer.size() & ~1_uz};
                     for(size_t i{0};i < len;i+=2)
                         std::swap(mBuffer[i], mBuffer[i+1]);
                 }
                 else if(bytesize == 4)
                 {
-                    const size_t len{mBuffer.size() & ~size_t{3}};
+                    const size_t len{mBuffer.size() & ~3_uz};
                     for(size_t i{0};i < len;i+=4)
                     {
                         std::swap(mBuffer[i  ], mBuffer[i+3]);
@@ -170,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");
@@ -195,32 +195,32 @@ int WaveBackend::mixerProc()
     return 0;
 }
 
-void WaveBackend::open(const char *name)
+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)
-        name = waveDevice;
-    else if(strcmp(name, waveDevice) != 0)
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
-            name};
+    if(name.empty())
+        name = GetDeviceName();
+    else if(name != GetDeviceName())
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+            al::sizei(name), name.data()};
 
     /* There's only one "device", so if it's already open, we're done. */
     if(mFile) return;
 
 #ifdef _WIN32
     {
-        std::wstring wname{utf8_to_wstr(fname->c_str())};
-        mFile = _wfopen(wname.c_str(), L"wb");
+        std::wstring wname{utf8_to_wstr(fname.value())};
+        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;
 }
@@ -229,12 +229,11 @@ 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;
@@ -265,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;
@@ -273,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;
@@ -283,49 +285,48 @@ bool WaveBackend::reset()
     bytes = mDevice->bytesFromFmt();
     channels = mDevice->channelsFromFmt();
 
-    rewind(mFile);
+    rewind(mFile.get());
 
-    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 close
 
-    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<ushort>(channels), mFile);
+    fwrite16le(static_cast<ushort>(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<ushort>(channels * bytes), mFile);
+    fwrite16le(static_cast<ushort>(channels * bytes), mFile.get());
     // 16-bit val, bits per sample
-    fwrite16le(static_cast<ushort>(bytes * 8), mFile);
+    fwrite16le(static_cast<ushort>(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<ushort>(bytes * 8), mFile);
+    fwrite16le(static_cast<ushort>(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 close
 
-    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();
 
@@ -337,7 +338,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);
@@ -357,14 +358,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<uint>(size-8), mFile); // 'WAVE' header len
-            if(fseek(mFile, mDataStart-4, SEEK_SET) == 0)
-                fwrite32le(static_cast<uint>(dataLen), mFile); // 'data' header len
+            if(fseek(mFile.get(), 4, SEEK_SET) == 0)
+                fwrite32le(static_cast<uint>(size-8), mFile.get()); // 'WAVE' header len
+            if(fseek(mFile.get(), mDataStart-4, SEEK_SET) == 0)
+                fwrite32le(static_cast<uint>(dataLen), mFile.get()); // 'data' header len
         }
     }
 }
@@ -378,19 +379,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>
 {
-    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)

+ 5 - 5
Engine/lib/openal-soft/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<std::string> 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 */

+ 77 - 88
Engine/lib/openal-soft/alc/backends/winmm.cpp

@@ -22,8 +22,8 @@
 
 #include "winmm.h"
 
-#include <stdlib.h>
-#include <stdio.h>
+#include <cstdlib>
+#include <cstdio>
 #include <memory.h>
 
 #include <windows.h>
@@ -38,13 +38,15 @@
 #include <algorithm>
 #include <functional>
 
-#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 "threads.h"
+#include "vector.h"
 
 #ifndef WAVE_FORMAT_IEEE_FLOAT
 #define WAVE_FORMAT_IEEE_FLOAT  0x0003
@@ -55,13 +57,13 @@ namespace {
 #define DEVNAME_HEAD "OpenAL Soft on "
 
 
-al::vector<std::string> PlaybackDevices;
-al::vector<std::string> CaptureDevices;
+std::vector<std::string> PlaybackDevices;
+std::vector<std::string> CaptureDevices;
 
-bool checkName(const al::vector<std::string> &list, const std::string &name)
+bool checkName(const std::vector<std::string> &list, const std::string &name)
 { return std::find(list.cbegin(), list.cend(), name) != list.cend(); }
 
-void ProbePlaybackDevices(void)
+void ProbePlaybackDevices()
 {
     PlaybackDevices.clear();
 
@@ -74,7 +76,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 std::string basename{DEVNAME_HEAD + wstr_to_utf8(std::data(WaveCaps.szPname))};
 
             int count{1};
             std::string newname{basename};
@@ -92,7 +94,7 @@ void ProbePlaybackDevices(void)
     }
 }
 
-void ProbeCaptureDevices(void)
+void ProbeCaptureDevices()
 {
     CaptureDevices.clear();
 
@@ -105,7 +107,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 std::string basename{DEVNAME_HEAD + wstr_to_utf8(std::data(WaveCaps.szPname))};
 
             int count{1};
             std::string newname{basename};
@@ -134,7 +136,7 @@ struct WinMMPlayback final : public BackendBase {
 
     int mixerProc();
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     bool reset() override;
     void start() override;
     void stop() override;
@@ -143,6 +145,7 @@ struct WinMMPlayback final : public BackendBase {
     al::semaphore mSem;
     uint mIdx{0u};
     std::array<WAVEHDR,4> mWaveBuffer{};
+    al::vector<char,16> mBuffer;
 
     HWAVEOUT mOutHdl{nullptr};
 
@@ -150,8 +153,6 @@ struct WinMMPlayback final : public BackendBase {
 
     std::atomic<bool> mKillNow{true};
     std::thread mThread;
-
-    DEF_NEWDEL(WinMMPlayback)
 };
 
 WinMMPlayback::~WinMMPlayback()
@@ -159,14 +160,11 @@ WinMMPlayback::~WinMMPlayback()
     if(mOutHdl)
         waveOutClose(mOutHdl);
     mOutHdl = nullptr;
-
-    al_free(mWaveBuffer[0].lpData);
-    std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{});
 }
 
 /* WinMMPlayback::waveOutProc
  *
- * Posts a message to 'WinMMPlayback::mixerProc' everytime a WaveOut Buffer is
+ * Posts a message to 'WinMMPlayback::mixerProc' every time a WaveOut Buffer is
  * completed and returns to the application (for more data)
  */
 void CALLBACK WinMMPlayback::waveOutProc(HWAVEOUT, UINT msg, DWORD_PTR, DWORD_PTR) noexcept
@@ -179,7 +177,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))
@@ -207,59 +205,55 @@ FORCE_ALIGN int WinMMPlayback::mixerProc()
 }
 
 
-void WinMMPlayback::open(const char *name)
+void WinMMPlayback::open(std::string_view name)
 {
     if(PlaybackDevices.empty())
         ProbePlaybackDevices();
 
     // Find the Device ID matching the deviceName if valid
-    auto iter = name ?
+    auto iter = !name.empty() ?
         std::find(PlaybackDevices.cbegin(), PlaybackDevices.cend(), name) :
         PlaybackDevices.cbegin();
     if(iter == PlaybackDevices.cend())
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
-            name};
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+            al::sizei(name), name.data()};
     auto DeviceID = static_cast<UINT>(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<WORD>(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<DWORD_PTR>(&WinMMPlayback::waveOutProcC),
-        reinterpret_cast<DWORD_PTR>(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<WORD>(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<DWORD_PTR>(&WinMMPlayback::waveOutProcC),
+            reinterpret_cast<DWORD_PTR>(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];
@@ -312,11 +306,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<char*>(al_calloc(16, BufferSize * mWaveBuffer.size()));
+    mWaveBuffer[0].lpData = mBuffer.data();
     mWaveBuffer[0].dwBufferLength = BufferSize;
     for(size_t i{1};i < mWaveBuffer.size();i++)
     {
@@ -369,16 +363,17 @@ struct WinMMCapture final : public BackendBase {
 
     int captureProc();
 
-    void open(const char *name) override;
+    void open(std::string_view name) override;
     void start() override;
     void stop() override;
-    void captureSamples(al::byte *buffer, uint samples) override;
+    void captureSamples(std::byte *buffer, uint samples) override;
     uint availableSamples() override;
 
     std::atomic<uint> mReadable{0u};
     al::semaphore mSem;
     uint mIdx{0};
     std::array<WAVEHDR,4> mWaveBuffer{};
+    al::vector<char,16> mBuffer;
 
     HWAVEIN mInHdl{nullptr};
 
@@ -388,8 +383,6 @@ struct WinMMCapture final : public BackendBase {
 
     std::atomic<bool> mKillNow{true};
     std::thread mThread;
-
-    DEF_NEWDEL(WinMMCapture)
 };
 
 WinMMCapture::~WinMMCapture()
@@ -398,14 +391,11 @@ WinMMCapture::~WinMMCapture()
     if(mInHdl)
         waveInClose(mInHdl);
     mInHdl = nullptr;
-
-    al_free(mWaveBuffer[0].lpData);
-    std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{});
 }
 
 /* WinMMCapture::waveInProc
  *
- * Posts a message to 'WinMMCapture::captureProc' everytime a WaveIn Buffer is
+ * Posts a message to 'WinMMCapture::captureProc' every time a WaveIn Buffer is
  * completed and returns to the application (with more data).
  */
 void CALLBACK WinMMCapture::waveInProc(HWAVEIN, UINT msg, DWORD_PTR, DWORD_PTR) noexcept
@@ -417,7 +407,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))
@@ -434,7 +424,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);
@@ -445,18 +436,18 @@ int WinMMCapture::captureProc()
 }
 
 
-void WinMMCapture::open(const char *name)
+void WinMMCapture::open(std::string_view name)
 {
     if(CaptureDevices.empty())
         ProbeCaptureDevices();
 
     // Find the Device ID matching the deviceName if valid
-    auto iter = name ?
+    auto iter = !name.empty() ?
         std::find(CaptureDevices.cbegin(), CaptureDevices.cend(), name) :
         CaptureDevices.cbegin();
     if(iter == CaptureDevices.cend())
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
-            name};
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+            al::sizei(name), name.data()};
     auto DeviceID = static_cast<UINT>(std::distance(CaptureDevices.cbegin(), iter));
 
     switch(mDevice->FmtChans)
@@ -470,6 +461,7 @@ void WinMMCapture::open(const char *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",
@@ -513,14 +505,14 @@ void WinMMCapture::open(const char *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<uint>(maxz(CapturedDataSize, BufferSize*mWaveBuffer.size()));
+    const auto CapturedDataSize = std::max<size_t>(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<char*>(al_calloc(16, BufferSize * mWaveBuffer.size()));
+    mWaveBuffer[0].lpData = mBuffer.data();
     mWaveBuffer[0].dwBufferLength = BufferSize;
     for(size_t i{1};i < mWaveBuffer.size();++i)
     {
@@ -571,8 +563,8 @@ void WinMMCapture::stop()
     mIdx = 0;
 }
 
-void WinMMCapture::captureSamples(al::byte *buffer, uint samples)
-{ mRing->read(buffer, samples); }
+void WinMMCapture::captureSamples(std::byte *buffer, uint samples)
+{ std::ignore = mRing->read(buffer, samples); }
 
 uint WinMMCapture::availableSamples()
 { return static_cast<uint>(mRing->readSpace()); }
@@ -586,26 +578,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>
 {
-    std::string outnames;
+    std::vector<std::string> outnames;
     auto add_device = [&outnames](const std::string &dname) -> void
-    {
-        /* +1 to also append the null char (to ensure a null-separated list and
-         * double-null terminated list).
-         */
-        if(!dname.empty())
-            outnames.append(dname.c_str(), dname.length()+1);
-    };
+    { 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;
     }

+ 5 - 5
Engine/lib/openal-soft/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<std::string> 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 */

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor