Parcourir la source

update OpenAL-Soft to 1.24.3.

Sasha Szpakowski il y a 7 mois
Parent
commit
5e4f3241ac
100 fichiers modifiés avec 7929 ajouts et 5572 suppressions
  1. 1 1
      CMakeLists.txt
  2. 180 0
      libs/openal-soft/.clang-tidy
  3. 98 8
      libs/openal-soft/.github/workflows/ci.yml
  4. 26 17
      libs/openal-soft/.github/workflows/utils.yml
  5. 284 191
      libs/openal-soft/CMakeLists.txt
  6. 134 0
      libs/openal-soft/ChangeLog
  7. 11 0
      libs/openal-soft/README.md
  8. 237 268
      libs/openal-soft/al/auxeffectslot.cpp
  9. 72 66
      libs/openal-soft/al/auxeffectslot.h
  10. 266 220
      libs/openal-soft/al/buffer.cpp
  11. 4 2
      libs/openal-soft/al/buffer.h
  12. 159 145
      libs/openal-soft/al/debug.cpp
  13. 49 47
      libs/openal-soft/al/eax/api.h
  14. 30 24
      libs/openal-soft/al/eax/call.cpp
  15. 35 20
      libs/openal-soft/al/eax/effect.h
  16. 7 2
      libs/openal-soft/al/eax/exception.h
  17. 1 4
      libs/openal-soft/al/eax/fx_slots.h
  18. 2 3
      libs/openal-soft/al/eax/utils.cpp
  19. 3 69
      libs/openal-soft/al/eax/utils.h
  20. 0 5
      libs/openal-soft/al/eax/x_ram.h
  21. 150 133
      libs/openal-soft/al/effect.cpp
  22. 10 2
      libs/openal-soft/al/effect.h
  23. 35 44
      libs/openal-soft/al/effects/autowah.cpp
  24. 106 95
      libs/openal-soft/al/effects/chorus.cpp
  25. 28 38
      libs/openal-soft/al/effects/compressor.cpp
  26. 27 68
      libs/openal-soft/al/effects/convolution.cpp
  27. 52 59
      libs/openal-soft/al/effects/dedicated.cpp
  28. 41 45
      libs/openal-soft/al/effects/distortion.cpp
  29. 40 40
      libs/openal-soft/al/effects/echo.cpp
  30. 49 58
      libs/openal-soft/al/effects/effects.h
  31. 54 60
      libs/openal-soft/al/effects/equalizer.cpp
  32. 41 46
      libs/openal-soft/al/effects/fshifter.cpp
  33. 46 45
      libs/openal-soft/al/effects/modulator.cpp
  34. 24 55
      libs/openal-soft/al/effects/null.cpp
  35. 30 38
      libs/openal-soft/al/effects/pshifter.cpp
  36. 192 181
      libs/openal-soft/al/effects/reverb.cpp
  37. 54 61
      libs/openal-soft/al/effects/vmorpher.cpp
  38. 25 53
      libs/openal-soft/al/error.cpp
  39. 0 27
      libs/openal-soft/al/error.h
  40. 31 29
      libs/openal-soft/al/event.cpp
  41. 0 2
      libs/openal-soft/al/extension.cpp
  42. 185 158
      libs/openal-soft/al/filter.cpp
  43. 15 9
      libs/openal-soft/al/filter.h
  44. 90 55
      libs/openal-soft/al/listener.cpp
  45. 228 212
      libs/openal-soft/al/source.cpp
  46. 51 37
      libs/openal-soft/al/source.h
  47. 86 53
      libs/openal-soft/al/state.cpp
  48. 260 166
      libs/openal-soft/alc/alc.cpp
  49. 93 61
      libs/openal-soft/alc/alconfig.cpp
  50. 273 249
      libs/openal-soft/alc/alu.cpp
  51. 7 4
      libs/openal-soft/alc/alu.h
  52. 116 126
      libs/openal-soft/alc/backends/alsa.cpp
  53. 5 5
      libs/openal-soft/alc/backends/alsa.h
  54. 41 23
      libs/openal-soft/alc/backends/base.cpp
  55. 33 14
      libs/openal-soft/alc/backends/base.h
  56. 171 100
      libs/openal-soft/alc/backends/coreaudio.cpp
  57. 6 6
      libs/openal-soft/alc/backends/coreaudio.h
  58. 116 130
      libs/openal-soft/alc/backends/dsound.cpp
  59. 5 5
      libs/openal-soft/alc/backends/dsound.h
  60. 132 107
      libs/openal-soft/alc/backends/jack.cpp
  61. 5 5
      libs/openal-soft/alc/backends/jack.h
  62. 4 4
      libs/openal-soft/alc/backends/loopback.cpp
  63. 5 5
      libs/openal-soft/alc/backends/loopback.h
  64. 19 21
      libs/openal-soft/alc/backends/null.cpp
  65. 5 5
      libs/openal-soft/alc/backends/null.h
  66. 35 34
      libs/openal-soft/alc/backends/oboe.cpp
  67. 5 5
      libs/openal-soft/alc/backends/oboe.h
  68. 131 72
      libs/openal-soft/alc/backends/opensl.cpp
  69. 5 5
      libs/openal-soft/alc/backends/opensl.h
  70. 72 79
      libs/openal-soft/alc/backends/oss.cpp
  71. 5 5
      libs/openal-soft/alc/backends/oss.h
  72. 700 0
      libs/openal-soft/alc/backends/otherio.cpp
  73. 21 0
      libs/openal-soft/alc/backends/otherio.h
  74. 163 155
      libs/openal-soft/alc/backends/pipewire.cpp
  75. 8 6
      libs/openal-soft/alc/backends/pipewire.h
  76. 229 116
      libs/openal-soft/alc/backends/portaudio.cpp
  77. 0 19
      libs/openal-soft/alc/backends/portaudio.h
  78. 19 0
      libs/openal-soft/alc/backends/portaudio.hpp
  79. 291 223
      libs/openal-soft/alc/backends/pulseaudio.cpp
  80. 12 6
      libs/openal-soft/alc/backends/pulseaudio.h
  81. 110 77
      libs/openal-soft/alc/backends/sdl2.cpp
  82. 5 5
      libs/openal-soft/alc/backends/sdl2.h
  83. 393 0
      libs/openal-soft/alc/backends/sdl3.cpp
  84. 19 0
      libs/openal-soft/alc/backends/sdl3.h
  85. 51 57
      libs/openal-soft/alc/backends/sndio.cpp
  86. 0 19
      libs/openal-soft/alc/backends/sndio.h
  87. 19 0
      libs/openal-soft/alc/backends/sndio.hpp
  88. 26 26
      libs/openal-soft/alc/backends/solaris.cpp
  89. 5 5
      libs/openal-soft/alc/backends/solaris.h
  90. 634 475
      libs/openal-soft/alc/backends/wasapi.cpp
  91. 6 6
      libs/openal-soft/alc/backends/wasapi.h
  92. 50 45
      libs/openal-soft/alc/backends/wave.cpp
  93. 5 5
      libs/openal-soft/alc/backends/wave.h
  94. 75 93
      libs/openal-soft/alc/backends/winmm.cpp
  95. 5 5
      libs/openal-soft/alc/backends/winmm.h
  96. 123 105
      libs/openal-soft/alc/context.cpp
  97. 65 45
      libs/openal-soft/alc/context.h
  98. 18 14
      libs/openal-soft/alc/device.cpp
  99. 37 32
      libs/openal-soft/alc/device.h
  100. 2 2
      libs/openal-soft/alc/effects/autowah.cpp

+ 1 - 1
CMakeLists.txt

@@ -228,7 +228,7 @@ set(MEGA_LIBTHEORA_VER "1.1.1")
 set(MEGA_FREETYPE_VER "2.13.2")
 set(MEGA_FREETYPE_VER "2.13.2")
 set(MEGA_SDL2_VER "2.28.5")
 set(MEGA_SDL2_VER "2.28.5")
 set(MEGA_SDL3_VER "3.2.10")
 set(MEGA_SDL3_VER "3.2.10")
-set(MEGA_OPENAL_VER "1.23.1-bc7cb17")
+set(MEGA_OPENAL_VER "1.24.3")
 set(MEGA_MODPLUG_VER "0.8.8.4")
 set(MEGA_MODPLUG_VER "0.8.8.4")
 
 
 set(SKIP_INSTALL_ALL TRUE)
 set(SKIP_INSTALL_ALL TRUE)

+ 180 - 0
libs/openal-soft/.clang-tidy

@@ -0,0 +1,180 @@
+---
+  Checks: '-*,
+                    bugprone-argument-comment,
+                    bugprone-assert-side-effect,
+                    bugprone-assignment-in-if-condition,
+                    bugprone-bad-signal-to-kill-thread,
+                    bugprone-bool-pointer-implicit-conversion,
+                    bugprone-casting-through-void,
+                    bugprone-chained-comparison,
+                    bugprone-compare-pointer-to-member-virtual-function,
+                    bugprone-copy-constructor-init,
+                    bugprone-crtp-constructor-accessibility,
+                    bugprone-dangling-handle,
+                    bugprone-dynamic-static-initializers,
+                    bugprone-fold-init-type,
+                    bugprone-forward-declaration-namespace,
+                    bugprone-forwarding-reference-overload,
+                    bugprone-implicit-widening-of-multiplication-result,
+                    bugprone-inaccurate-erase,
+                    bugprone-incorrect-*,
+                    bugprone-infinite-loop,
+                    bugprone-integer-division,
+                    bugprone-lambda-function-name,
+                    bugprone-macro-repeated-side-effects,
+                    bugprone-misplaced-*,
+                    bugprone-move-forwarding-reference,
+                    bugprone-multi-level-implicit-pointer-conversion,
+                    bugprone-multiple-*,
+                    bugprone-narrowing-conversions,
+                    bugprone-no-escape,
+                    bugprone-non-zero-enum-to-bool-conversion,
+                    bugprone-not-null-terminated-result,
+                    bugprone-optional-value-conversion,
+                    bugprone-parent-virtual-call,
+                    bugprone-pointer-arithmetic-on-polymorphic-object,
+                    bugprone-posix-return,
+                    bugprone-redundant-branch-condition,
+                    bugprone-reserved-identifier,
+                    bugprone-return-const-ref-from-parameter,
+                    bugprone-shared-ptr-array-mismatch,
+                    bugprone-signal-handler,
+                    bugprone-signed-char-misuse,
+                    bugprone-sizeof-*,
+                    bugprone-spuriously-wake-up-functions,
+                    bugprone-standalone-empty,
+                    bugprone-string-*,
+                    bugprone-stringview-nullptr,
+                    bugprone-suspicious-*,
+                    bugprone-swapped-arguments,
+                    bugprone-terminating-continue,
+                    bugprone-throw-keyword-missing,
+                    bugprone-too-small-loop-variable,
+                    bugprone-undefined-memory-manipulation,
+                    bugprone-undelegated-constructor,
+                    bugprone-unhandled-*,
+                    bugprone-unique-ptr-array-mismatch,
+                    bugprone-unsafe-functions,
+                    bugprone-unused-*,
+                    bugprone-use-after-move,
+                    bugprone-virtual-near-miss,
+                    cert-dcl50-cpp,
+                    cert-dcl58-cpp,
+                    cert-env33-c,
+                    cert-err34-c,
+                    cert-err52-cpp,
+                    cert-err60-cpp,
+                    cert-flp30-c,
+                    cert-mem57-cpp,
+                    clang-analyzer-apiModeling.*,
+                    clang-analyzer-core.*,
+                    clang-analyzer-cplusplus.*,
+                    clang-analyzer-deadcode.DeadStores,
+                    clang-analyzer-fuchsia.HandleChecker,
+                    clang-analyzer-nullability.*,
+                    clang-analyzer-optin.*,
+                    clang-analyzer-osx.*,
+                    clang-analyzer-security.FloatLoopCounter,
+                    clang-analyzer-security.PutenvStackArray,
+                    clang-analyzer-security.SetgidSetuidOrder,
+                    clang-analyzer-security.cert.env.InvalidPtr,
+                    clang-analyzer-security.insecureAPI.SecuritySyntaxChecker,
+                    clang-analyzer-security.insecureAPI.UncheckedReturn,
+                    clang-analyzer-security.insecureAPI.bcmp,
+                    clang-analyzer-security.insecureAPI.bcopy,
+                    clang-analyzer-security.insecureAPI.bzero,
+                    clang-analyzer-security.insecureAPI.decodeValueOfObjCType,
+                    clang-analyzer-security.insecureAPI.getpw,
+                    clang-analyzer-security.insecureAPI.gets,
+                    clang-analyzer-security.insecureAPI.mkstemp,
+                    clang-analyzer-security.insecureAPI.mktemp,
+                    clang-analyzer-security.insecureAPI.rand,
+                    clang-analyzer-security.insecureAPI.strcpy,
+                    clang-analyzer-security.insecureAPI.vfork,
+                    clang-analyzer-unix.*,
+                    clang-analyzer-valist.*,
+                    clang-analyzer-webkit.*,
+                    concurrency-thread-canceltype-asynchronous,
+                    cppcoreguidelines-avoid-capturing-lambda-coroutines,
+                    cppcoreguidelines-avoid-c-arrays,
+                    cppcoreguidelines-avoid-goto,
+                    cppcoreguidelines-avoid-reference-coroutine-parameters,
+                    cppcoreguidelines-c-copy-assignment-signature,
+                    cppcoreguidelines-explicit-virtual-functions,
+                    cppcoreguidelines-interfaces-global-init,
+                    cppcoreguidelines-narrowing-conversions,
+                    cppcoreguidelines-no-malloc,
+                    cppcoreguidelines-no-suspend-with-lock,
+                    cppcoreguidelines-owning-memory,
+                    cppcoreguidelines-prefer-member-initializer,
+                    cppcoreguidelines-pro-bounds-array-to-pointer-decay,
+                    cppcoreguidelines-pro-bounds-pointer-arithmetic,
+                    cppcoreguidelines-pro-type-const-cast,
+                    cppcoreguidelines-pro-type-cstyle-cast,
+                    cppcoreguidelines-pro-type-member-init,
+                    cppcoreguidelines-pro-type-static-cast-downcast,
+                    cppcoreguidelines-pro-type-union-access,
+                    cppcoreguidelines-pro-type-vararg,
+                    cppcoreguidelines-slicing,
+                    cppcoreguidelines-virtual-class-destructor,
+                    google-build-explicit-make-pair,
+                    google-default-arguments,
+                    google-explicit-constructor,
+                    hicpp-exception-baseclass,
+                    misc-confusable-identifiers,
+                    misc-coroutine-hostile-raii,
+                    misc-misleading-*,
+                    misc-non-copyable-objects,
+                    misc-throw-by-value-catch-by-reference,
+                    misc-uniqueptr-reset-release,
+                    modernize-avoid-*,
+                    modernize-concat-nested-namespaces,
+                    modernize-deprecated-*,
+                    modernize-loop-convert,
+                    modernize-macro-to-enum,
+                    modernize-make-*,
+                    modernize-pass-by-value,
+                    modernize-raw-string-literal,
+                    modernize-redundant-void-arg,
+                    modernize-replace-*,
+                    modernize-return-braced-init-list,
+                    modernize-shrink-to-fit,
+                    modernize-unary-static-assert,
+                    modernize-use-auto,
+                    modernize-use-bool-literals,
+                    modernize-use-default-member-init,
+                    modernize-use-emplace,
+                    modernize-use-equals-*,
+                    modernize-use-nodiscard,
+                    modernize-use-noexcept,
+                    modernize-use-nullptr,
+                    modernize-use-override,
+                    modernize-use-transparent-functors,
+                    modernize-use-uncaught-exceptions,
+                    modernize-use-using,
+                    performance-faster-string-find,
+                    performance-for-range-copy,
+                    performance-inefficient-*,
+                    performance-move-constructor-init,
+                    performance-noexcept-destructor,
+                    performance-noexcept-swap,
+                    performance-unnecessary-copy-initialization,
+                    portability-restrict-system-includes,
+                    portability-std-allocator-const,
+                    readability-const-return-type,
+                    readability-container-contains,
+                    readability-container-size-empty,
+                    readability-convert-member-functions-to-static,
+                    readability-delete-null-pointer,
+                    readability-duplicate-include,
+                    readability-else-after-return,
+                    readability-inconsistent-declaration-parameter-name,
+                    readability-make-member-function-const,
+                    readability-misleading-indentation,
+                    readability-misplaced-array-index,
+                    readability-redundant-*,
+                    readability-simplify-subscript-expr,
+                    readability-static-definition-in-anonymous-namespace,
+                    readability-string-compare,
+                    readability-uniqueptr-delete-release,
+                    readability-use-*'

+ 98 - 8
libs/openal-soft/.github/workflows/ci.yml

@@ -106,9 +106,35 @@ jobs:
               libdbus-1-dev",
               libdbus-1-dev",
             build_type: "Release"
             build_type: "Release"
           }
           }
-
+        - {
+            name: "Android_armeabi-v7a-Release",
+            os: ubuntu-latest,
+            cmake_opts: "-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake \
+            -DALSOFT_EMBED_HRTF_DATA=TRUE \
+            -DALSOFT_REQUIRE_OPENSL=ON",
+            build_type: "Release"
+          }
+        - {
+            name: "Android_arm64-v8a-Release",
+            os: ubuntu-latest,
+            cmake_opts: "-DANDRIOD_ABI=arm64-v8a \
+            -DANDROID_PLATFORM=25 \
+            -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake \
+            -DALSOFT_EMBED_HRTF_DATA=TRUE \
+            -DALSOFT_REQUIRE_OPENSL=ON",
+            build_type: "Release"
+          }
     steps:
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
+      with:
+        fetch-depth: '0'
+
+    - name: Get current commit tag, short hash, count and date
+      run: |
+        echo "CommitTag=$(git describe --tags --abbrev=0 --match *.*.*)" >> $env:GITHUB_ENV
+        echo "CommitHashShort=$(git rev-parse --short=8 HEAD)" >> $env:GITHUB_ENV
+        echo "CommitCount=$(git rev-list --count $env:GITHUB_REF_NAME)" >> $env:GITHUB_ENV
+        echo "CommitDate=$(git show -s --date=iso-local --format=%cd)" >> $env:GITHUB_ENV
 
 
     - name: Install Dependencies
     - name: Install Dependencies
       shell: bash
       shell: bash
@@ -133,8 +159,8 @@ jobs:
         cd build
         cd build
         ctest
         ctest
 
 
-    - name: Create Archive
-      if: ${{ matrix.config.os == 'windows-latest' }}
+    - name: Set up Windows artifacts
+      if: ${{ contains(matrix.config.name, 'Win') }}
       shell: bash
       shell: bash
       run: |
       run: |
         cd build
         cd build
@@ -143,10 +169,74 @@ jobs:
         cp ${{matrix.config.build_type}}/soft_oal.dll archive
         cp ${{matrix.config.build_type}}/soft_oal.dll archive
         cp ${{matrix.config.build_type}}/OpenAL32.dll archive/router
         cp ${{matrix.config.build_type}}/OpenAL32.dll archive/router
 
 
-    - name: Upload Archive
-      # Upload package as an artifact of this workflow.
-      uses: actions/[email protected]
-      if: ${{ matrix.config.os == 'windows-latest' }}
+    - name: Set up Android artifacts
+      if: ${{ contains(matrix.config.name, 'Android') }}
+      shell: bash
+      run: |
+        cd build
+        mkdir archive
+        cp ${{github.workspace}}/build/libopenal.so archive/
+
+    - name: Upload build as a workflow artifact
+      uses: actions/upload-artifact@v4
+      if: ${{ contains(matrix.config.name, 'Win') || contains(matrix.config.name, 'Android') }}
       with:
       with:
         name: soft_oal-${{matrix.config.name}}
         name: soft_oal-${{matrix.config.name}}
         path: build/archive
         path: build/archive
+
+    outputs:
+      CommitTag: ${{env.CommitTag}}
+      CommitHashShort: ${{env.CommitHashShort}}
+      CommitCount: ${{env.CommitCount}}
+      CommitDate: ${{env.CommitDate}}
+
+  release:
+    if: github.event_name != 'pull_request'
+    needs: build
+    runs-on: ubuntu-latest
+    steps:
+
+    - name: Download build artifacts
+      uses: actions/[email protected]
+      with:
+        path: "build"
+        pattern: "*-Win??-Release"
+        github-token: "${{secrets.GITHUB_TOKEN}}"
+
+    - name: Set up build folders
+      run: |
+        mkdir -p build/release/OpenALSoft/Documentation
+        mkdir -p build/release/OpenALSoft/Win32
+        mkdir -p build/release/OpenALSoft/Win64
+        echo "${{github.repository}}" >>                                                                              "build/release/OpenALSoft/Documentation/Version.txt"
+        echo "v${{needs.build.outputs.CommitTag}}-${{needs.build.outputs.CommitHashShort}} ${{github.ref_name}}" >>   "build/release/OpenALSoft/Documentation/Version.txt"
+        echo "Commit #${{needs.build.outputs.CommitCount}}" >>                                                        "build/release/OpenALSoft/Documentation/Version.txt"
+        echo "${{needs.build.outputs.CommitDate}}" >>                                                                 "build/release/OpenALSoft/Documentation/Version.txt"
+        curl https://raw.githubusercontent.com/${{github.repository}}/${{github.ref_name}}/README.md               -o "build/release/OpenALSoft/Documentation/ReadMe.txt"
+        curl https://raw.githubusercontent.com/${{github.repository}}/${{github.ref_name}}/ChangeLog               -o "build/release/OpenALSoft/Documentation/ChangeLog.txt"
+        curl https://raw.githubusercontent.com/${{github.repository}}/${{github.ref_name}}/COPYING                 -o "build/release/OpenALSoft/Documentation/License.txt"
+        curl https://raw.githubusercontent.com/${{github.repository}}/${{github.ref_name}}/BSD-3Clause             -o "build/release/OpenALSoft/Documentation/License_BSD-3Clause.txt"
+        curl https://raw.githubusercontent.com/${{github.repository}}/${{github.ref_name}}/LICENSE-pffft           -o "build/release/OpenALSoft/Documentation/License_PFFFT.txt"
+        curl https://raw.githubusercontent.com/${{github.repository}}/${{github.ref_name}}/alsoftrc.sample         -o "build/release/OpenALSoft/Win32/alsoft.ini"
+        curl https://raw.githubusercontent.com/${{github.repository}}/${{github.ref_name}}/alsoftrc.sample         -o "build/release/OpenALSoft/Win64/alsoft.ini"
+        cp "build/soft_oal-Win32-Release/soft_oal.dll"                                                                "build/release/OpenALSoft/Win32/OpenAL32.dll"
+        cp "build/soft_oal-Win64-Release/soft_oal.dll"                                                                "build/release/OpenALSoft/Win64/OpenAL32.dll"
+        cp -r "build/release/OpenALSoft"                                                                              "build/release/OpenALSoft+HRTF"
+        cp "build/release/OpenALSoft+HRTF/Win32/alsoft.ini"                                                           "build/release/OpenALSoft+HRTF/Documentation/alsoft.ini"
+        curl https://raw.githubusercontent.com/${{github.repository}}/${{github.ref_name}}/configs/HRTF/alsoft.ini -o "build/release/OpenALSoft+HRTF/Win32/alsoft.ini"
+        cp "build/release/OpenALSoft+HRTF/Win32/alsoft.ini"                                                           "build/release/OpenALSoft+HRTF/Win64/alsoft.ini"
+
+    - name: Compress artifacts
+      run: |
+        cd build/release
+        7z a OpenALSoft.zip      ./OpenALSoft/*
+        7z a OpenALSoft+HRTF.zip ./OpenALSoft+HRTF/*
+
+    - name: GitHub pre-release
+      uses: "Sweeistaken/[email protected]"
+      with:
+        repo_token: "${{secrets.GITHUB_TOKEN}}"
+        automatic_release_tag: "latest"
+        prerelease: true
+        title: "OpenAL Soft v${{needs.build.outputs.CommitTag}}-${{needs.build.outputs.CommitHashShort}}"
+        files: "build/release/*"

+ 26 - 17
libs/openal-soft/.github/workflows/makemhr.yml → libs/openal-soft/.github/workflows/utils.yml

@@ -1,31 +1,35 @@
-name: makemhr
+name: utils
 
 
 on:
 on:
   push:
   push:
     paths:
     paths:
-      - 'utils/makemhr/**'
-      - '.github/workflows/makemhr.yml'
+      - 'utils/**'
+      - 'examples/**'
+      - '.github/workflows/utils.yml'
 
 
   workflow_dispatch:
   workflow_dispatch:
 
 
 env:
 env:
   BUILD_TYPE: Release
   BUILD_TYPE: Release
+  Branch: ${{github.ref_name}}
 
 
 jobs:
 jobs:
   Win64:
   Win64:
     runs-on: windows-latest
     runs-on: windows-latest
 
 
     steps:
     steps:
-    - uses: actions/checkout@v3
+    - name: Clone repo and submodules
+      run: git clone https://github.com/${{github.repository}}.git . --branch ${{env.Branch}}
 
 
-    - name: Get current date
-      run: echo "CurrentDate=$(date +'%Y-%m-%d')" >> $env:GITHUB_ENV
-
-    - name: Get commit hash
-      run: echo "CommitHash=$(git rev-parse --short=7 HEAD)" >> $env:GITHUB_ENV
+    - name: Get current date, commit hash and count
+      run: |
+        echo "CommitDate=$(git show -s --date=format:'%Y-%m-%d' --format=%cd)" >> $env:GITHUB_ENV
+        echo "CommitHashShort=$(git rev-parse --short=7 HEAD)" >> $env:GITHUB_ENV
+        echo "CommitCount=$(git rev-list --count ${{env.Branch}} --)" >> $env:GITHUB_ENV
 
 
     - name: Clone libmysofa
     - name: Clone libmysofa
-      run: git clone --depth 1 --branch v1.3.1 https://github.com/hoene/libmysofa.git libmysofa
+      run: |
+        git clone https://github.com/hoene/libmysofa.git --branch v1.3.3
 
 
     - name: Add MSBuild to PATH
     - name: Add MSBuild to PATH
       uses: microsoft/[email protected]
       uses: microsoft/[email protected]
@@ -52,25 +56,30 @@ jobs:
     - name: Collect artifacts
     - name: Collect artifacts
       run: |
       run: |
         copy "build/Release/makemhr.exe" "Artifacts/makemhr.exe"
         copy "build/Release/makemhr.exe" "Artifacts/makemhr.exe"
+        copy "build/Release/sofa-info.exe" "Artifacts/sofa-info.exe"
+        copy "build/Release/alrecord.exe" "Artifacts/alrecord.exe"
+        copy "build/Release/altonegen.exe" "Artifacts/altonegen.exe"
+        copy "build/Release/openal-info.exe" "Artifacts/openal-info.exe"
+        copy "build/Release/allafplay.exe" "Artifacts/allafplay.exe"
         copy "libmysofa/windows/third-party/zlib-1.2.11/bin/zlib.dll" "Artifacts/zlib.dll"
         copy "libmysofa/windows/third-party/zlib-1.2.11/bin/zlib.dll" "Artifacts/zlib.dll"
 
 
-    - name: Upload makemhr artifact
-      uses: actions/[email protected]
+    - name: Upload artifacts
+      uses: actions/upload-artifact@v4
       with:
       with:
-        name: makemhr
+        name: ${{env.CommitDate}}_utils-r${{env.CommitCount}}@${{env.CommitHashShort}}
         path: "Artifacts/"
         path: "Artifacts/"
 
 
     - name: Compress artifacts
     - name: Compress artifacts
       uses: papeloto/action-zip@v1
       uses: papeloto/action-zip@v1
       with:
       with:
         files: Artifacts/
         files: Artifacts/
-        dest: "Release/makemhr.zip"
+        dest: "Release/utils.zip"
 
 
     - name: GitHub pre-release
     - name: GitHub pre-release
       uses: "marvinpinto/action-automatic-releases@latest"
       uses: "marvinpinto/action-automatic-releases@latest"
       with:
       with:
         repo_token: "${{secrets.GITHUB_TOKEN}}"
         repo_token: "${{secrets.GITHUB_TOKEN}}"
-        automatic_release_tag: "makemhr"
+        automatic_release_tag: "utils"
         prerelease: true
         prerelease: true
-        title: "[${{env.CurrentDate}}] makemhr-${{env.CommitHash}}"
-        files: "Release/makemhr.zip"
+        title: "[${{env.CommitDate}}] utils-r${{env.CommitCount}}@${{env.CommitHashShort}}"
+        files: "Release/utils.zip"

+ 284 - 191
libs/openal-soft/CMakeLists.txt

@@ -75,12 +75,12 @@ if(NOT CMAKE_DEBUG_POSTFIX)
         FORCE)
         FORCE)
 endif()
 endif()
 
 
-set(DEFAULT_TARGET_PROPS
+set(ALSOFT_STD_VERSION_PROPS
     # Require C++17.
     # Require C++17.
     CXX_STANDARD 17
     CXX_STANDARD 17
     CXX_STANDARD_REQUIRED TRUE
     CXX_STANDARD_REQUIRED TRUE
-    # Prefer C11, but support C99 and earlier when possible.
-    C_STANDARD 11)
+    # Prefer C17, but support earlier when necessary.
+    C_STANDARD 17)
 
 
 set(CMAKE_MODULE_PATH "${OpenAL_SOURCE_DIR}/cmake")
 set(CMAKE_MODULE_PATH "${OpenAL_SOURCE_DIR}/cmake")
 
 
@@ -93,13 +93,13 @@ include(CheckCCompilerFlag)
 include(CheckCXXCompilerFlag)
 include(CheckCXXCompilerFlag)
 include(CheckCSourceCompiles)
 include(CheckCSourceCompiles)
 include(CheckCXXSourceCompiles)
 include(CheckCXXSourceCompiles)
+include(CheckLinkerFlag)
 include(CheckStructHasMember)
 include(CheckStructHasMember)
 include(CMakePackageConfigHelpers)
 include(CMakePackageConfigHelpers)
 include(GNUInstallDirs)
 include(GNUInstallDirs)
 
 
 find_package(PkgConfig)
 find_package(PkgConfig)
-find_package(SDL2 QUIET)
-
+find_package(SDL3 QUIET)
 
 
 option(ALSOFT_DLOPEN  "Check for the dlopen API for loading optional libs"  ON)
 option(ALSOFT_DLOPEN  "Check for the dlopen API for loading optional libs"  ON)
 
 
@@ -144,6 +144,23 @@ if(DEFINED ALSOFT_AMBDEC_PRESETS)
     message(WARNING "ALSOFT_AMBDEC_PRESETS is deprecated. Use ALSOFT_INSTALL_AMBDEC_PRESETS instead")
     message(WARNING "ALSOFT_AMBDEC_PRESETS is deprecated. Use ALSOFT_INSTALL_AMBDEC_PRESETS instead")
 endif()
 endif()
 
 
+if(MSVC)
+    option(FORCE_STATIC_VCRT "Force /MT for static VC runtimes" OFF)
+    if(FORCE_STATIC_VCRT)
+        foreach(flag_var
+                CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
+                CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO
+                CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
+                CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
+            if(${flag_var} MATCHES "/MD")
+                string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
+            endif()
+        endforeach(flag_var)
+    endif()
+endif()
+
+add_subdirectory(fmt-11.1.1 EXCLUDE_FROM_ALL)
+
 
 
 set(CPP_DEFS ) # C pre-processor, not C++
 set(CPP_DEFS ) # C pre-processor, not C++
 set(INC_PATHS )
 set(INC_PATHS )
@@ -163,6 +180,21 @@ if(WIN32)
     if(MINGW)
     if(MINGW)
         option(ALSOFT_BUILD_IMPORT_LIB "Build an import .lib using dlltool (requires sed)" ON)
         option(ALSOFT_BUILD_IMPORT_LIB "Build an import .lib using dlltool (requires sed)" ON)
     endif()
     endif()
+
+    if(NOT ALSOFT_UWP)
+        # Some systems may need NTDDI_VERSION defined to NTDDI_VISTA or later
+        check_c_source_compiles("#define INITGUID
+            #include <shlobj.h>
+            #include <knownfolders.h>
+            int main()
+            {
+                SHGetKnownFolderPath(&FOLDERID_RoamingAppData, KF_FLAG_DONT_UNEXPAND, NULL, NULL);
+                return 0;
+            }" HAVE_SHGETKNOWNFOLDERPATH_NO_NTDDI)
+        if(NOT HAVE_SHGETKNOWNFOLDERPATH_NO_NTDDI)
+            set(CPP_DEFS ${CPP_DEFS} NTDDI_VERSION=NTDDI_VISTA)
+        endif()
+    endif()
 elseif(APPLE)
 elseif(APPLE)
     option(ALSOFT_OSX_FRAMEWORK "Build as macOS framework" OFF)
     option(ALSOFT_OSX_FRAMEWORK "Build as macOS framework" OFF)
 endif()
 endif()
@@ -181,8 +213,8 @@ if(NOT LIBTYPE)
 endif()
 endif()
 
 
 set(LIB_MAJOR_VERSION "1")
 set(LIB_MAJOR_VERSION "1")
-set(LIB_MINOR_VERSION "23")
-set(LIB_REVISION "1")
+set(LIB_MINOR_VERSION "24")
+set(LIB_REVISION "3")
 set(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}")
 set(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}")
 set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0)
 set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0)
 
 
@@ -203,31 +235,6 @@ if(NOT HAVE_STDC_FORMAT_MACROS)
     set(CPP_DEFS ${CPP_DEFS} __STDC_FORMAT_MACROS)
     set(CPP_DEFS ${CPP_DEFS} __STDC_FORMAT_MACROS)
 endif()
 endif()
 
 
-if(NOT WIN32)
-    # Check if _POSIX_C_SOURCE and _XOPEN_SOURCE needs to be set for POSIX functions
-    check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_DEFAULT)
-    if(NOT HAVE_POSIX_MEMALIGN_DEFAULT)
-        set(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
-        set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600")
-        check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_POSIX)
-        if(NOT HAVE_POSIX_MEMALIGN_POSIX)
-            set(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS})
-        else()
-            set(CPP_DEFS ${CPP_DEFS} _POSIX_C_SOURCE=200112L _XOPEN_SOURCE=600)
-        endif()
-    endif()
-    unset(OLD_REQUIRED_FLAGS)
-endif()
-
-# C99 has restrict, but C++ does not, so we can only utilize __restrict.
-check_cxx_source_compiles("int *__restrict foo;
-int main() { return 0; }" HAVE___RESTRICT)
-if(HAVE___RESTRICT)
-    set(CPP_DEFS ${CPP_DEFS} RESTRICT=__restrict)
-else()
-    set(CPP_DEFS ${CPP_DEFS} "RESTRICT=")
-endif()
-
 # Some systems may need libatomic for atomic functions to work
 # Some systems may need libatomic for atomic functions to work
 set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
 set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
 set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES} atomic)
 set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES} atomic)
@@ -252,12 +259,18 @@ if(ANDROID)
 endif()
 endif()
 
 
 if(MSVC)
 if(MSVC)
-    set(CPP_DEFS ${CPP_DEFS} _CRT_SECURE_NO_WARNINGS)
+    # NOTE: _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR is temporary. When building on
+    # VS 2022 17.10 or newer, but using an older runtime, mutexes can crash
+    # when locked. Ideally the runtime should be updated on the system, but
+    # until the update becomes more widespread, this helps avoid some pain
+    # points.
+    set(CPP_DEFS ${CPP_DEFS} _CRT_SECURE_NO_WARNINGS _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR)
     check_cxx_compiler_flag(/permissive- HAVE_PERMISSIVE_SWITCH)
     check_cxx_compiler_flag(/permissive- HAVE_PERMISSIVE_SWITCH)
     if(HAVE_PERMISSIVE_SWITCH)
     if(HAVE_PERMISSIVE_SWITCH)
         set(C_FLAGS ${C_FLAGS} $<$<COMPILE_LANGUAGE:CXX>:/permissive->)
         set(C_FLAGS ${C_FLAGS} $<$<COMPILE_LANGUAGE:CXX>:/permissive->)
     endif()
     endif()
-    set(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4127 /wd4268 /wd4324 /wd5030 /wd5051)
+    set(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4127 /wd4268 /wd4324 /wd5030 /wd5051
+        $<$<COMPILE_LANGUAGE:CXX>:/EHsc> /utf-8)
 
 
     if(NOT DXSDK_DIR)
     if(NOT DXSDK_DIR)
         string(REGEX REPLACE "\\\\" "/" DXSDK_DIR "$ENV{DXSDK_DIR}")
         string(REGEX REPLACE "\\\\" "/" DXSDK_DIR "$ENV{DXSDK_DIR}")
@@ -267,19 +280,6 @@ if(MSVC)
     if(DXSDK_DIR)
     if(DXSDK_DIR)
         message(STATUS "Using DirectX SDK directory: ${DXSDK_DIR}")
         message(STATUS "Using DirectX SDK directory: ${DXSDK_DIR}")
     endif()
     endif()
-
-    option(FORCE_STATIC_VCRT "Force /MT for static VC runtimes" OFF)
-    if(FORCE_STATIC_VCRT)
-        foreach(flag_var
-                CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
-                CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO
-                CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
-                CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
-            if(${flag_var} MATCHES "/MD")
-                string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
-            endif()
-        endforeach(flag_var)
-    endif()
 else()
 else()
     set(C_FLAGS ${C_FLAGS} -Winline -Wunused -Wall -Wextra -Wshadow -Wconversion -Wcast-align
     set(C_FLAGS ${C_FLAGS} -Winline -Wunused -Wall -Wextra -Wshadow -Wconversion -Wcast-align
         -Wpedantic
         -Wpedantic
@@ -295,17 +295,25 @@ else()
         endif()
         endif()
     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)
     if(ALSOFT_WERROR)
         set(C_FLAGS ${C_FLAGS} -Werror)
         set(C_FLAGS ${C_FLAGS} -Werror)
+    else()
+        set(C_FLAGS ${C_FLAGS} -Werror=undef)
     endif()
     endif()
 
 
-    # We want RelWithDebInfo to actually include debug stuff (define _DEBUG
-    # instead of NDEBUG)
-    foreach(flag_var  CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO)
-        if(${flag_var} MATCHES "-DNDEBUG")
-            string(REGEX REPLACE "-DNDEBUG" "-D_DEBUG" ${flag_var} "${${flag_var}}")
-        endif()
-    endforeach()
+    # NOTE: This essentially provides the equivalent of the C++26 feature to
+    # initialize all local variables with a non-0 bit pattern. Until C++26 is
+    # adopted, the [[gnu::uninitialized]] attribute will avoid the auto-
+    # initialization where necessary.
+    check_c_compiler_flag(-ftrivial-auto-var-init=pattern HAVE_FTRIVIAL_AUTO_VAR_INIT)
+    if(HAVE_FTRIVIAL_AUTO_VAR_INIT)
+        set(C_FLAGS ${C_FLAGS} -ftrivial-auto-var-init=pattern)
+    endif()
 
 
     check_c_compiler_flag(-fno-math-errno HAVE_FNO_MATH_ERRNO)
     check_c_compiler_flag(-fno-math-errno HAVE_FNO_MATH_ERRNO)
     if(HAVE_FNO_MATH_ERRNO)
     if(HAVE_FNO_MATH_ERRNO)
@@ -512,13 +520,9 @@ if(HAVE_SSE2)
 endif()
 endif()
 
 
 
 
-check_include_file(malloc.h HAVE_MALLOC_H)
 check_include_file(cpuid.h HAVE_CPUID_H)
 check_include_file(cpuid.h HAVE_CPUID_H)
 check_include_file(intrin.h HAVE_INTRIN_H)
 check_include_file(intrin.h HAVE_INTRIN_H)
 check_include_file(guiddef.h HAVE_GUIDDEF_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
 # Some systems need libm for some math functions to work
 set(MATH_LIB )
 set(MATH_LIB )
@@ -599,12 +603,12 @@ if(NOT WIN32)
     endif()
     endif()
 endif()
 endif()
 
 
-check_symbol_exists(getopt unistd.h HAVE_GETOPT)
-
 
 
 # Common sources used by both the OpenAL implementation library, the OpenAL
 # Common sources used by both the OpenAL implementation library, the OpenAL
 # router, and certain tools and examples.
 # router, and certain tools and examples.
 set(COMMON_OBJS
 set(COMMON_OBJS
+    common/alassert.cpp
+    common/alassert.h
     common/albit.h
     common/albit.h
     common/alcomplex.cpp
     common/alcomplex.cpp
     common/alcomplex.h
     common/alcomplex.h
@@ -624,6 +628,8 @@ set(COMMON_OBJS
     common/comptr.h
     common/comptr.h
     common/dynload.cpp
     common/dynload.cpp
     common/dynload.h
     common/dynload.h
+    common/filesystem.cpp
+    common/filesystem.h
     common/flexarray.h
     common/flexarray.h
     common/intrusive_ptr.h
     common/intrusive_ptr.h
     common/opthelpers.h
     common/opthelpers.h
@@ -681,7 +687,6 @@ set(CORE_OBJS
     core/filters/nfc.h
     core/filters/nfc.h
     core/filters/splitter.cpp
     core/filters/splitter.cpp
     core/filters/splitter.h
     core/filters/splitter.h
-    core/fmt_traits.cpp
     core/fmt_traits.h
     core/fmt_traits.h
     core/fpu_ctrl.cpp
     core/fpu_ctrl.cpp
     core/fpu_ctrl.h
     core/fpu_ctrl.h
@@ -731,17 +736,17 @@ if(NOT WIN32)
             else()
             else()
                 set(EXTRA_LIBS ${EXTRA_LIBS} ${DBus1_LIBRARIES})
                 set(EXTRA_LIBS ${EXTRA_LIBS} ${DBus1_LIBRARIES})
             endif()
             endif()
+        else()
+            set(MISSING_VARS "")
+            if(NOT DBus1_INCLUDE_DIRS)
+                set(MISSING_VARS "${MISSING_VARS} DBus1_INCLUDE_DIRS")
+            endif()
+            if(NOT DBus1_LIBRARIES)
+                set(MISSING_VARS "${MISSING_VARS} DBus1_LIBRARIES")
+            endif()
+            message(STATUS "Could NOT find DBus1 (missing:${MISSING_VARS})")
+            unset(MISSING_VARS)
         endif()
         endif()
-    else()
-        set(MISSING_VARS "")
-        if(NOT DBus1_INCLUDE_DIRS)
-            set(MISSING_VARS "${MISSING_VARS} DBus1_INCLUDE_DIRS")
-        endif()
-        if(NOT DBus1_LIBRARIES)
-            set(MISSING_VARS "${MISSING_VARS} DBus1_LIBRARIES")
-        endif()
-        message(STATUS "Could NOT find DBus1 (missing:${MISSING_VARS})")
-        unset(MISSING_VARS)
     endif()
     endif()
 endif()
 endif()
 if(ALSOFT_REQUIRE_RTKIT AND NOT HAVE_RTKIT)
 if(ALSOFT_REQUIRE_RTKIT AND NOT HAVE_RTKIT)
@@ -783,7 +788,6 @@ set(OPENAL_OBJS
     al/effects/reverb.cpp
     al/effects/reverb.cpp
     al/effects/vmorpher.cpp
     al/effects/vmorpher.cpp
     al/error.cpp
     al/error.cpp
-    al/error.h
     al/event.cpp
     al/event.cpp
     al/event.h
     al/event.h
     al/extension.cpp
     al/extension.cpp
@@ -885,8 +889,10 @@ set(HAVE_PULSEAUDIO 0)
 set(HAVE_COREAUDIO  0)
 set(HAVE_COREAUDIO  0)
 set(HAVE_OPENSL     0)
 set(HAVE_OPENSL     0)
 set(HAVE_OBOE       0)
 set(HAVE_OBOE       0)
+set(HAVE_OTHERIO    0)
 set(HAVE_WAVE       0)
 set(HAVE_WAVE       0)
 set(HAVE_SDL2       0)
 set(HAVE_SDL2       0)
+set(HAVE_SDL3       0)
 
 
 if(WIN32 OR HAVE_DLFCN_H)
 if(WIN32 OR HAVE_DLFCN_H)
     set(IS_LINKED "")
     set(IS_LINKED "")
@@ -988,17 +994,21 @@ if(NOT WIN32)
         endif()
         endif()
     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)
     option(ALSOFT_REQUIRE_SNDIO "Require SndIO backend" OFF)
     if(ALSOFT_BACKEND_SNDIO)
     if(ALSOFT_BACKEND_SNDIO)
-        find_package(SoundIO)
-        if(SOUNDIO_FOUND)
+        find_package(SndIO)
+        if(SNDIO_FOUND)
             set(HAVE_SNDIO 1)
             set(HAVE_SNDIO 1)
             set(BACKENDS  "${BACKENDS} SndIO (linked),")
             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(ALC_OBJS  ${ALC_OBJS} alc/backends/sndio.cpp alc/backends/sndio.hpp)
+            set(EXTRA_LIBS ${SNDIO_LIBRARIES} ${EXTRA_LIBS})
+            set(INC_PATHS ${INC_PATHS} ${SNDIO_INCLUDE_DIRS})
         endif()
         endif()
     endif()
     endif()
 endif()
 endif()
@@ -1063,8 +1073,20 @@ if(WIN32)
             set(HAVE_WASAPI 1)
             set(HAVE_WASAPI 1)
             set(BACKENDS  "${BACKENDS} WASAPI,")
             set(BACKENDS  "${BACKENDS} WASAPI,")
             set(ALC_OBJS  ${ALC_OBJS} alc/backends/wasapi.cpp alc/backends/wasapi.h)
             set(ALC_OBJS  ${ALC_OBJS} alc/backends/wasapi.cpp alc/backends/wasapi.h)
+
+            if(NOT ALSOFT_UWP)
+                set(EXTRA_LIBS avrt ${EXTRA_LIBS})
+            endif()
         endif()
         endif()
     endif()
     endif()
+
+    option(ALSOFT_BACKEND_OTHERIO "Enable OtherIO backend" OFF)
+    option(ALSOFT_REQUIRE_OTHERIO "Require OtherIO backend" OFF)
+    if(ALSOFT_BACKEND_OTHERIO)
+        set(HAVE_OTHERIO 1)
+        set(BACKENDS  "${BACKENDS} OtherIO,")
+        set(ALC_OBJS  ${ALC_OBJS} alc/backends/otherio.cpp alc/backends/otherio.h)
+    endif()
 endif()
 endif()
 if(ALSOFT_REQUIRE_WINMM AND NOT HAVE_WINMM)
 if(ALSOFT_REQUIRE_WINMM AND NOT HAVE_WINMM)
     message(FATAL_ERROR "Failed to enable required WinMM backend")
     message(FATAL_ERROR "Failed to enable required WinMM backend")
@@ -1075,6 +1097,9 @@ endif()
 if(ALSOFT_REQUIRE_WASAPI AND NOT HAVE_WASAPI)
 if(ALSOFT_REQUIRE_WASAPI AND NOT HAVE_WASAPI)
     message(FATAL_ERROR "Failed to enable required WASAPI backend")
     message(FATAL_ERROR "Failed to enable required WASAPI backend")
 endif()
 endif()
+if(ALSOFT_REQUIRE_OTHERIO AND NOT HAVE_OTHERIO)
+    message(FATAL_ERROR "Failed to enable required OtherIO backend")
+endif()
 
 
 # Check JACK backend
 # Check JACK backend
 option(ALSOFT_BACKEND_JACK "Enable JACK backend" ON)
 option(ALSOFT_BACKEND_JACK "Enable JACK backend" ON)
@@ -1136,7 +1161,7 @@ if(ALSOFT_BACKEND_OBOE)
     if(ANDROID)
     if(ANDROID)
         set(OBOE_SOURCE "" CACHE STRING "Source directory for Oboe.")
         set(OBOE_SOURCE "" CACHE STRING "Source directory for Oboe.")
         if(OBOE_SOURCE)
         if(OBOE_SOURCE)
-            add_subdirectory(${OBOE_SOURCE} ./oboe)
+            add_subdirectory(${OBOE_SOURCE} ./oboe EXCLUDE_FROM_ALL)
             set(OBOE_TARGET oboe)
             set(OBOE_TARGET oboe)
         else()
         else()
             find_package(oboe CONFIG)
             find_package(oboe CONFIG)
@@ -1154,9 +1179,6 @@ if(ALSOFT_BACKEND_OBOE)
         set(ALC_OBJS  ${ALC_OBJS} alc/backends/oboe.cpp alc/backends/oboe.h)
         set(ALC_OBJS  ${ALC_OBJS} alc/backends/oboe.cpp alc/backends/oboe.h)
         set(BACKENDS  "${BACKENDS} Oboe,")
         set(BACKENDS  "${BACKENDS} Oboe,")
         set(EXTRA_LIBS ${OBOE_TARGET} ${EXTRA_LIBS})
         set(EXTRA_LIBS ${OBOE_TARGET} ${EXTRA_LIBS})
-        if(MEGA)
-            set(ALC_OBJS  ${ALC_OBJS} opensl_latency.cpp)
-        endif()
     endif()
     endif()
 endif()
 endif()
 if(ALSOFT_REQUIRE_OBOE AND NOT HAVE_OBOE)
 if(ALSOFT_REQUIRE_OBOE AND NOT HAVE_OBOE)
@@ -1171,8 +1193,8 @@ if(ALSOFT_BACKEND_OPENSL)
     if(OPENSL_FOUND)
     if(OPENSL_FOUND)
         set(HAVE_OPENSL 1)
         set(HAVE_OPENSL 1)
         set(ALC_OBJS  ${ALC_OBJS} alc/backends/opensl.cpp alc/backends/opensl.h)
         set(ALC_OBJS  ${ALC_OBJS} alc/backends/opensl.cpp alc/backends/opensl.h)
-        set(BACKENDS  "${BACKENDS} OpenSL,")
-        set(EXTRA_LIBS ${OPENSL_LIBRARIES} ${EXTRA_LIBS})
+        set(BACKENDS  "${BACKENDS} OpenSL${IS_LINKED},")
+        add_backend_libs(${OPENSL_LIBRARIES})
         set(INC_PATHS ${INC_PATHS} ${OPENSL_INCLUDE_DIRS})
         set(INC_PATHS ${INC_PATHS} ${OPENSL_INCLUDE_DIRS})
     endif()
     endif()
 endif()
 endif()
@@ -1188,7 +1210,7 @@ if(ALSOFT_BACKEND_PORTAUDIO)
     if(PORTAUDIO_FOUND)
     if(PORTAUDIO_FOUND)
         set(HAVE_PORTAUDIO 1)
         set(HAVE_PORTAUDIO 1)
         set(BACKENDS  "${BACKENDS} PortAudio${IS_LINKED},")
         set(BACKENDS  "${BACKENDS} PortAudio${IS_LINKED},")
-        set(ALC_OBJS  ${ALC_OBJS} alc/backends/portaudio.cpp alc/backends/portaudio.h)
+        set(ALC_OBJS  ${ALC_OBJS} alc/backends/portaudio.cpp alc/backends/portaudio.hpp)
         add_backend_libs(${PORTAUDIO_LIBRARIES})
         add_backend_libs(${PORTAUDIO_LIBRARIES})
         set(INC_PATHS ${INC_PATHS} ${PORTAUDIO_INCLUDE_DIRS})
         set(INC_PATHS ${INC_PATHS} ${PORTAUDIO_INCLUDE_DIRS})
     endif()
     endif()
@@ -1197,11 +1219,29 @@ if(ALSOFT_REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO)
     message(FATAL_ERROR "Failed to enable required PortAudio backend")
     message(FATAL_ERROR "Failed to enable required PortAudio backend")
 endif()
 endif()
 
 
-# Check for SDL2 backend
-# Off by default, since it adds a runtime dependency
+# Check for SDL2 or SDL3 backend
+# Off by default, since it adds a runtime dependency. Additionally, both SDL2
+# and SDL3 can't be enabled simultaneously.
+option(ALSOFT_BACKEND_SDL3 "Enable SDL3 backend" OFF)
+option(ALSOFT_REQUIRE_SDL3 "Require SDL3 backend" OFF)
+if(ALSOFT_BACKEND_SDL3)
+    if(SDL3_FOUND)
+        set(HAVE_SDL3 1)
+        set(ALC_OBJS  ${ALC_OBJS} alc/backends/sdl3.cpp alc/backends/sdl3.h)
+        set(BACKENDS  "${BACKENDS} SDL3,")
+        set(EXTRA_LIBS ${EXTRA_LIBS} SDL3::SDL3)
+    else()
+        message(STATUS "Could NOT find SDL3")
+    endif()
+endif()
+if(ALSOFT_REQUIRE_SDL3 AND NOT HAVE_SDL3)
+    message(FATAL_ERROR "Failed to enable required SDL3 backend")
+endif()
+
 option(ALSOFT_BACKEND_SDL2 "Enable SDL2 backend" OFF)
 option(ALSOFT_BACKEND_SDL2 "Enable SDL2 backend" OFF)
 option(ALSOFT_REQUIRE_SDL2 "Require SDL2 backend" OFF)
 option(ALSOFT_REQUIRE_SDL2 "Require SDL2 backend" OFF)
-if(ALSOFT_BACKEND_SDL2)
+if(ALSOFT_BACKEND_SDL2 AND NOT HAVE_SDL3)
+    find_package(SDL2)
     if(SDL2_FOUND)
     if(SDL2_FOUND)
         set(HAVE_SDL2 1)
         set(HAVE_SDL2 1)
         set(ALC_OBJS  ${ALC_OBJS} alc/backends/sdl2.cpp alc/backends/sdl2.h)
         set(ALC_OBJS  ${ALC_OBJS} alc/backends/sdl2.cpp alc/backends/sdl2.h)
@@ -1211,7 +1251,7 @@ if(ALSOFT_BACKEND_SDL2)
         message(STATUS "Could NOT find SDL2")
         message(STATUS "Could NOT find SDL2")
     endif()
     endif()
 endif()
 endif()
-if(ALSOFT_REQUIRE_SDL2 AND NOT SDL2_FOUND)
+if(ALSOFT_REQUIRE_SDL2 AND NOT HAVE_SDL2)
     message(FATAL_ERROR "Failed to enable required SDL2 backend")
     message(FATAL_ERROR "Failed to enable required SDL2 backend")
 endif()
 endif()
 
 
@@ -1253,7 +1293,7 @@ if(ALSOFT_UPDATE_BUILD_VERSION AND GIT_FOUND AND EXISTS "${OpenAL_SOURCE_DIR}/.g
         VERBATIM
         VERBATIM
     )
     )
 
 
-    add_custom_target(build_version DEPENDS "${OpenAL_BINARY_DIR}/version_witness.txt")
+    add_custom_target(alsoft.build_version DEPENDS "${OpenAL_BINARY_DIR}/version_witness.txt")
 else()
 else()
     set(GIT_BRANCH "UNKNOWN")
     set(GIT_BRANCH "UNKNOWN")
     set(GIT_COMMIT_HASH "unknown")
     set(GIT_COMMIT_HASH "unknown")
@@ -1282,6 +1322,15 @@ if(ALSOFT_EMBED_HRTF_DATA)
     make_hrtf_header("Default HRTF.mhr" "default_hrtf")
     make_hrtf_header("Default HRTF.mhr" "default_hrtf")
 endif()
 endif()
 
 
+# Set a 16KB page size for Android
+if(ANDROID)
+    set(CPP_DEFS ${CPP_DEFS} __BIONIC_NO_PAGE_SIZE_MACRO)
+    check_linker_flag(C "-Wl,-z,max-page-size=16384" HAS_MAX_PAGE_SIZE_16384)
+    if(HAS_MAX_PAGE_SIZE_16384)
+        set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,-z,max-page-size=16384")
+    endif()
+endif()
+
 
 
 if(ALSOFT_UTILS)
 if(ALSOFT_UTILS)
     find_package(MySOFA)
     find_package(MySOFA)
@@ -1294,9 +1343,9 @@ if(ALSOFT_UTILS)
 endif()
 endif()
 if(ALSOFT_UTILS OR ALSOFT_EXAMPLES)
 if(ALSOFT_UTILS OR ALSOFT_EXAMPLES)
     find_package(SndFile)
     find_package(SndFile)
-    if(SDL2_FOUND)
-        find_package(FFmpeg COMPONENTS AVFORMAT AVCODEC AVUTIL SWSCALE SWRESAMPLE)
-    endif()
+endif()
+if(ALSOFT_EXAMPLES AND SDL3_FOUND)
+    find_package(FFmpeg COMPONENTS AVFORMAT AVCODEC AVUTIL SWSCALE SWRESAMPLE)
 endif()
 endif()
 
 
 if(NOT WIN32)
 if(NOT WIN32)
@@ -1318,12 +1367,14 @@ if(LIBTYPE STREQUAL "STATIC")
     set(PKG_CONFIG_CFLAGS -DAL_LIBTYPE_STATIC)
     set(PKG_CONFIG_CFLAGS -DAL_LIBTYPE_STATIC)
     foreach(FLAG  ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB})
     foreach(FLAG  ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB})
         # If this is already a linker flag, or is a full path+file, add it
         # If this is already a linker flag, or is a full path+file, add it
-        # as-is. If it's an SDL2 target, add the link flag for it. Otherwise,
-        # it's a name intended to be dressed as -lname.
+        # as-is. If it's an SDL2 or SDL3 target, add the link flag for it.
+        # Otherwise, it's a name intended to be dressed as -lname.
         if(FLAG MATCHES "^-.*" OR EXISTS "${FLAG}")
         if(FLAG MATCHES "^-.*" OR EXISTS "${FLAG}")
             set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} ${FLAG}")
             set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} ${FLAG}")
         elseif(FLAG MATCHES "^SDL2::SDL2")
         elseif(FLAG MATCHES "^SDL2::SDL2")
             set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} -lSDL2")
             set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} -lSDL2")
+        elseif(FLAG MATCHES "^SDL3::SDL3")
+            set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} -lSDL3")
         else()
         else()
             set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} -l${FLAG}")
             set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} -l${FLAG}")
         endif()
         endif()
@@ -1334,28 +1385,53 @@ endif()
 configure_file(
 configure_file(
     "${OpenAL_SOURCE_DIR}/config.h.in"
     "${OpenAL_SOURCE_DIR}/config.h.in"
     "${OpenAL_BINARY_DIR}/config.h")
     "${OpenAL_BINARY_DIR}/config.h")
+configure_file(
+    "${OpenAL_SOURCE_DIR}/config_backends.h.in"
+    "${OpenAL_BINARY_DIR}/config_backends.h")
+configure_file(
+    "${OpenAL_SOURCE_DIR}/config_simd.h.in"
+    "${OpenAL_BINARY_DIR}/config_simd.h")
 configure_file(
 configure_file(
     "${OpenAL_SOURCE_DIR}/openal.pc.in"
     "${OpenAL_SOURCE_DIR}/openal.pc.in"
     "${OpenAL_BINARY_DIR}/openal.pc"
     "${OpenAL_BINARY_DIR}/openal.pc"
     @ONLY)
     @ONLY)
 
 
 
 
-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)
+add_library(alsoft.common STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS})
+target_include_directories(alsoft.common PRIVATE ${OpenAL_SOURCE_DIR}/include
+    PUBLIC ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
+target_compile_definitions(alsoft.common PRIVATE ${CPP_DEFS})
+target_compile_options(alsoft.common PRIVATE ${C_FLAGS})
+target_link_libraries(alsoft.common PRIVATE alsoft::fmt)
+set_target_properties(alsoft.common PROPERTIES ${ALSOFT_STD_VERSION_PROPS}
+    POSITION_INDEPENDENT_CODE TRUE)
 
 
 
 
 unset(HAS_ROUTER)
 unset(HAS_ROUTER)
 set(IMPL_TARGET OpenAL) # Either OpenAL or soft_oal.
 set(IMPL_TARGET OpenAL) # Either OpenAL or soft_oal.
 
 
+
+set(NEED_ANALYZE_SOURCE_FILES "")
+foreach(obj ${CORE_OBJS} ${OPENAL_OBJS} ${ALC_OBJS} ${COMMON_OBJS})
+    IF (NOT ${obj} MATCHES "${CMAKE_BINARY_DIR}/default_hrtf.txt")
+        list(APPEND NEED_ANALYZE_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/${obj}")
+    endif()
+endforeach()
+IF (ALSOFT_UTILS)
+    list(APPEND NEED_ANALYZE_SOURCE_FILES "${CMAKE_SOURCE_DIR}/utils/openal-info.c")
+endif()
+SET(CLANG_TIDY_EXECUTABLE "clang-tidy")
+if(DEFINED ENV{CLANG_TIDY_EXECUTABLE})
+    SET(CLANG_TIDY_EXECUTABLE $ENV{CLANG_TIDY_EXECUTABLE})
+endif()
+add_custom_target(clang-tidy-check ${CLANG_TIDY_EXECUTABLE} -format-style=file -p ${CMAKE_BINARY_DIR}/compile_commands.json ${NEED_ANALYZE_SOURCE_FILES} DEPENDS ${NEED_ANALYZE_SOURCE_FILES})
+
 # Build main library
 # Build main library
 if(LIBTYPE STREQUAL "STATIC")
 if(LIBTYPE STREQUAL "STATIC")
     add_library(${IMPL_TARGET} STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS} ${CORE_OBJS})
     add_library(${IMPL_TARGET} STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS} ${CORE_OBJS})
     target_compile_definitions(${IMPL_TARGET} PUBLIC AL_LIBTYPE_STATIC)
     target_compile_definitions(${IMPL_TARGET} PUBLIC AL_LIBTYPE_STATIC)
-    target_link_libraries(${IMPL_TARGET} PRIVATE ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB})
+    target_link_libraries(${IMPL_TARGET} PRIVATE ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}
+        $<BUILD_LOCAL_INTERFACE:alsoft::fmt>)
 
 
     if(WIN32)
     if(WIN32)
         # This option is for static linking OpenAL Soft into another project
         # This option is for static linking OpenAL Soft into another project
@@ -1380,7 +1456,7 @@ else()
             PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}"
             PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}"
             "AL_API=${EXPORT_DECL}" ${CPP_DEFS})
             "AL_API=${EXPORT_DECL}" ${CPP_DEFS})
         target_compile_options(OpenAL PRIVATE ${C_FLAGS})
         target_compile_options(OpenAL PRIVATE ${C_FLAGS})
-        target_link_libraries(OpenAL PRIVATE alcommon ${LINKER_FLAGS})
+        target_link_libraries(OpenAL PRIVATE alsoft.common ${LINKER_FLAGS} alsoft::fmt)
         target_include_directories(OpenAL
         target_include_directories(OpenAL
           PUBLIC
           PUBLIC
             $<BUILD_INTERFACE:${OpenAL_SOURCE_DIR}/include>
             $<BUILD_INTERFACE:${OpenAL_SOURCE_DIR}/include>
@@ -1389,10 +1465,10 @@ else()
             ${OpenAL_SOURCE_DIR}/common
             ${OpenAL_SOURCE_DIR}/common
             ${OpenAL_BINARY_DIR}
             ${OpenAL_BINARY_DIR}
         )
         )
-        set_target_properties(OpenAL PROPERTIES ${DEFAULT_TARGET_PROPS} PREFIX ""
+        set_target_properties(OpenAL PROPERTIES ${ALSOFT_STD_VERSION_PROPS} PREFIX ""
             OUTPUT_NAME ${LIBNAME})
             OUTPUT_NAME ${LIBNAME})
-        if(TARGET build_version)
-            add_dependencies(OpenAL build_version)
+        if(TARGET alsoft.build_version)
+            add_dependencies(OpenAL alsoft.build_version)
         endif()
         endif()
         set(HAS_ROUTER 1)
         set(HAS_ROUTER 1)
 
 
@@ -1411,24 +1487,30 @@ else()
     if(WIN32)
     if(WIN32)
         set_target_properties(${IMPL_TARGET} PROPERTIES PREFIX "")
         set_target_properties(${IMPL_TARGET} PROPERTIES PREFIX "")
     endif()
     endif()
-    target_link_libraries(${IMPL_TARGET} PRIVATE alcommon ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB})
+    target_link_libraries(${IMPL_TARGET} PRIVATE alsoft.common ${LINKER_FLAGS} ${EXTRA_LIBS}
+        ${MATH_LIB} alsoft::fmt)
 
 
     if(ALSOFT_UWP)
     if(ALSOFT_UWP)
-        set(ALSOFT_CPPWINRT_VERSION "2.0.230706.1" CACHE STRING "The soft-oal default cppwinrt version")
-        
-        find_program(NUGET_EXE NAMES nuget)
-        if(NOT NUGET_EXE)
-            message("NUGET.EXE not found.")
-            message(FATAL_ERROR "Please install this executable, and run CMake again.")
-        endif()
+        find_package(cppwinrt CONFIG)
+        if (TARGET Microsoft::CppWinRT)
+            target_link_libraries(${IMPL_TARGET} PRIVATE Microsoft::CppWinRT)
+        else()
+            set(ALSOFT_CPPWINRT_VERSION "2.0.230706.1" CACHE STRING "The soft-oal default cppwinrt version")
+
+            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\"")
+            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)
+            set_target_properties(${IMPL_TARGET} PROPERTIES
+                VS_PROJECT_IMPORT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.props
+            )
+            target_link_libraries(${IMPL_TARGET} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.targets)
+        endif()
     endif()
     endif()
 
 
     if(NOT WIN32 AND NOT APPLE)
     if(NOT WIN32 AND NOT APPLE)
@@ -1473,8 +1555,6 @@ else()
     endif()
     endif()
 endif()
 endif()
 
 
-set(OPENAL_LIB_NAME ${IMPL_TARGET} PARENT_SCOPE)
-
 target_include_directories(${IMPL_TARGET}
 target_include_directories(${IMPL_TARGET}
   PUBLIC
   PUBLIC
     $<BUILD_INTERFACE:${OpenAL_SOURCE_DIR}/include>
     $<BUILD_INTERFACE:${OpenAL_SOURCE_DIR}/include>
@@ -1489,18 +1569,18 @@ target_include_directories(${IMPL_TARGET}
     ${OpenAL_SOURCE_DIR}/common
     ${OpenAL_SOURCE_DIR}/common
 )
 )
 
 
-set_target_properties(${IMPL_TARGET} PROPERTIES ${DEFAULT_TARGET_PROPS}
+set_target_properties(${IMPL_TARGET} PROPERTIES ${ALSOFT_STD_VERSION_PROPS}
     OUTPUT_NAME ${LIBNAME}
     OUTPUT_NAME ${LIBNAME}
     VERSION ${LIB_VERSION}
     VERSION ${LIB_VERSION}
     SOVERSION ${LIB_MAJOR_VERSION}
     SOVERSION ${LIB_MAJOR_VERSION}
 )
 )
 target_compile_definitions(${IMPL_TARGET}
 target_compile_definitions(${IMPL_TARGET}
-    PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}"
+    PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES  "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}"
     ${CPP_DEFS})
     ${CPP_DEFS})
 target_compile_options(${IMPL_TARGET} PRIVATE ${C_FLAGS})
 target_compile_options(${IMPL_TARGET} PRIVATE ${C_FLAGS})
 
 
-if(TARGET build_version)
-    add_dependencies(${IMPL_TARGET} build_version)
+if(TARGET alsoft.build_version)
+    add_dependencies(${IMPL_TARGET} alsoft.build_version)
 endif()
 endif()
 
 
 if(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC")
 if(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC")
@@ -1514,7 +1594,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")
             message(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation")
         endif()
         endif()
     else()
     else()
-        target_link_options(OpenAL PRIVATE "-Wl,--output-def,OpenAL32.def")
+        target_link_options(OpenAL PRIVATE "-Wl,--output-def,${PROJECT_BINARY_DIR}/OpenAL32.def")
         add_custom_command(TARGET OpenAL POST_BUILD
         add_custom_command(TARGET OpenAL POST_BUILD
             COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def
             COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def
             COMMAND "${CMAKE_DLLTOOL}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll
             COMMAND "${CMAKE_DLLTOOL}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll
@@ -1623,7 +1703,7 @@ if(ALSOFT_UTILS)
     target_include_directories(openal-info PRIVATE ${OpenAL_SOURCE_DIR}/common)
     target_include_directories(openal-info PRIVATE ${OpenAL_SOURCE_DIR}/common)
     target_compile_options(openal-info PRIVATE ${C_FLAGS})
     target_compile_options(openal-info PRIVATE ${C_FLAGS})
     target_link_libraries(openal-info PRIVATE ${LINKER_FLAGS} OpenAL ${UNICODE_FLAG})
     target_link_libraries(openal-info PRIVATE ${LINKER_FLAGS} OpenAL ${UNICODE_FLAG})
-    set_target_properties(openal-info PROPERTIES ${DEFAULT_TARGET_PROPS})
+    set_target_properties(openal-info PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
     if(ALSOFT_INSTALL_EXAMPLES)
     if(ALSOFT_INSTALL_EXAMPLES)
         set(EXTRA_INSTALLS ${EXTRA_INSTALLS} openal-info)
         set(EXTRA_INSTALLS ${EXTRA_INSTALLS} openal-info)
     endif()
     endif()
@@ -1634,30 +1714,31 @@ if(ALSOFT_UTILS)
         target_include_directories(uhjdecoder
         target_include_directories(uhjdecoder
             PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
             PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
         target_compile_options(uhjdecoder PRIVATE ${C_FLAGS})
         target_compile_options(uhjdecoder PRIVATE ${C_FLAGS})
-        target_link_libraries(uhjdecoder PUBLIC alcommon
-            PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG})
-        set_target_properties(uhjdecoder PROPERTIES ${DEFAULT_TARGET_PROPS})
+        target_link_libraries(uhjdecoder PUBLIC alsoft.common
+            PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG} alsoft::fmt)
+        set_target_properties(uhjdecoder PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
 
 
         add_executable(uhjencoder utils/uhjencoder.cpp)
         add_executable(uhjencoder utils/uhjencoder.cpp)
         target_compile_definitions(uhjencoder PRIVATE ${CPP_DEFS})
         target_compile_definitions(uhjencoder PRIVATE ${CPP_DEFS})
         target_include_directories(uhjencoder
         target_include_directories(uhjencoder
             PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
             PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
         target_compile_options(uhjencoder PRIVATE ${C_FLAGS})
         target_compile_options(uhjencoder PRIVATE ${C_FLAGS})
-        target_link_libraries(uhjencoder PUBLIC alcommon
-            PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG})
-        set_target_properties(uhjencoder PROPERTIES ${DEFAULT_TARGET_PROPS})
+        target_link_libraries(uhjencoder PUBLIC alsoft.common
+            PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG} alsoft::fmt)
+        set_target_properties(uhjencoder PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
     endif()
     endif()
 
 
     if(MYSOFA_FOUND)
     if(MYSOFA_FOUND)
         set(SOFA_SUPPORT_SRCS
         set(SOFA_SUPPORT_SRCS
             utils/sofa-support.cpp
             utils/sofa-support.cpp
             utils/sofa-support.h)
             utils/sofa-support.h)
-        add_library(sofa-support STATIC EXCLUDE_FROM_ALL ${SOFA_SUPPORT_SRCS})
-        target_compile_definitions(sofa-support PRIVATE ${CPP_DEFS})
-        target_include_directories(sofa-support PUBLIC ${OpenAL_SOURCE_DIR}/common)
-        target_compile_options(sofa-support PRIVATE ${C_FLAGS})
-        target_link_libraries(sofa-support PUBLIC alcommon MySOFA::MySOFA PRIVATE ${LINKER_FLAGS})
-        set_target_properties(sofa-support PROPERTIES ${DEFAULT_TARGET_PROPS})
+        add_library(alsoft.sofa-support STATIC EXCLUDE_FROM_ALL ${SOFA_SUPPORT_SRCS})
+        target_compile_definitions(alsoft.sofa-support PRIVATE ${CPP_DEFS})
+        target_include_directories(alsoft.sofa-support PUBLIC ${OpenAL_SOURCE_DIR}/common)
+        target_compile_options(alsoft.sofa-support PRIVATE ${C_FLAGS})
+        target_link_libraries(alsoft.sofa-support PUBLIC alsoft.common MySOFA::MySOFA
+            PRIVATE ${LINKER_FLAGS} alsoft::fmt)
+        set_target_properties(alsoft.sofa-support PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
 
 
         set(MAKEMHR_SRCS
         set(MAKEMHR_SRCS
             utils/makemhr/loaddef.cpp
             utils/makemhr/loaddef.cpp
@@ -1666,16 +1747,14 @@ if(ALSOFT_UTILS)
             utils/makemhr/loadsofa.h
             utils/makemhr/loadsofa.h
             utils/makemhr/makemhr.cpp
             utils/makemhr/makemhr.cpp
             utils/makemhr/makemhr.h)
             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})
         add_executable(makemhr ${MAKEMHR_SRCS})
         target_compile_definitions(makemhr PRIVATE ${CPP_DEFS})
         target_compile_definitions(makemhr PRIVATE ${CPP_DEFS})
         target_include_directories(makemhr
         target_include_directories(makemhr
             PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/utils)
             PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/utils)
         target_compile_options(makemhr PRIVATE ${C_FLAGS})
         target_compile_options(makemhr PRIVATE ${C_FLAGS})
-        target_link_libraries(makemhr PRIVATE ${LINKER_FLAGS} sofa-support ${UNICODE_FLAG})
-        set_target_properties(makemhr PROPERTIES ${DEFAULT_TARGET_PROPS})
+        target_link_libraries(makemhr PRIVATE ${LINKER_FLAGS} alsoft.sofa-support ${UNICODE_FLAG}
+            alsoft::fmt)
+        set_target_properties(makemhr PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
         if(ALSOFT_INSTALL_EXAMPLES)
         if(ALSOFT_INSTALL_EXAMPLES)
             set(EXTRA_INSTALLS ${EXTRA_INSTALLS} makemhr)
             set(EXTRA_INSTALLS ${EXTRA_INSTALLS} makemhr)
         endif()
         endif()
@@ -1685,8 +1764,9 @@ if(ALSOFT_UTILS)
         target_compile_definitions(sofa-info PRIVATE ${CPP_DEFS})
         target_compile_definitions(sofa-info PRIVATE ${CPP_DEFS})
         target_include_directories(sofa-info PRIVATE ${OpenAL_SOURCE_DIR}/utils)
         target_include_directories(sofa-info PRIVATE ${OpenAL_SOURCE_DIR}/utils)
         target_compile_options(sofa-info PRIVATE ${C_FLAGS})
         target_compile_options(sofa-info PRIVATE ${C_FLAGS})
-        target_link_libraries(sofa-info PRIVATE ${LINKER_FLAGS} sofa-support ${UNICODE_FLAG})
-        set_target_properties(sofa-info PROPERTIES ${DEFAULT_TARGET_PROPS})
+        target_link_libraries(sofa-info PRIVATE ${LINKER_FLAGS} alsoft.sofa-support
+            ${UNICODE_FLAG} alsoft::fmt)
+        set_target_properties(sofa-info PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
     endif()
     endif()
     message(STATUS "Building utility programs")
     message(STATUS "Building utility programs")
 
 
@@ -1698,91 +1778,108 @@ endif()
 
 
 
 
 # Add a static library with common functions used by multiple example targets
 # Add a static library with common functions used by multiple example targets
-add_library(al-excommon STATIC EXCLUDE_FROM_ALL
+add_library(alsoft.excommon STATIC EXCLUDE_FROM_ALL
     examples/common/alhelpers.c
     examples/common/alhelpers.c
     examples/common/alhelpers.h)
     examples/common/alhelpers.h)
-target_compile_definitions(al-excommon PUBLIC ${CPP_DEFS})
-target_include_directories(al-excommon PUBLIC ${OpenAL_SOURCE_DIR}/common)
-target_compile_options(al-excommon PUBLIC ${C_FLAGS})
-target_link_libraries(al-excommon PUBLIC OpenAL PRIVATE ${RT_LIB})
-set_target_properties(al-excommon PROPERTIES ${DEFAULT_TARGET_PROPS})
+target_compile_definitions(alsoft.excommon PUBLIC ${CPP_DEFS})
+target_include_directories(alsoft.excommon PUBLIC ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
+target_compile_options(alsoft.excommon PUBLIC ${C_FLAGS})
+target_link_libraries(alsoft.excommon PUBLIC OpenAL PRIVATE ${RT_LIB})
+set_target_properties(alsoft.excommon PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
 
 
 if(ALSOFT_EXAMPLES)
 if(ALSOFT_EXAMPLES)
     add_executable(altonegen examples/altonegen.c)
     add_executable(altonegen examples/altonegen.c)
-    target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} al-excommon ${UNICODE_FLAG})
-    set_target_properties(altonegen PROPERTIES ${DEFAULT_TARGET_PROPS})
+    target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} alsoft.excommon
+        ${UNICODE_FLAG})
+    set_target_properties(altonegen PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
 
 
     add_executable(alrecord examples/alrecord.c)
     add_executable(alrecord examples/alrecord.c)
-    target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} al-excommon ${UNICODE_FLAG})
-    set_target_properties(alrecord PROPERTIES ${DEFAULT_TARGET_PROPS})
+    target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} alsoft.excommon ${UNICODE_FLAG})
+    set_target_properties(alrecord PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
+
+    add_executable(aldebug examples/aldebug.cpp)
+    target_link_libraries(aldebug PRIVATE ${LINKER_FLAGS} alsoft.excommon ${UNICODE_FLAG}
+        alsoft::fmt)
+    set_target_properties(aldebug PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
+
+    add_executable(allafplay examples/allafplay.cpp)
+    target_link_libraries(allafplay PRIVATE ${LINKER_FLAGS} alsoft.common alsoft.excommon
+        ${UNICODE_FLAG} alsoft::fmt)
+    set_target_properties(allafplay PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
 
 
     if(ALSOFT_INSTALL_EXAMPLES)
     if(ALSOFT_INSTALL_EXAMPLES)
-        set(EXTRA_INSTALLS ${EXTRA_INSTALLS} altonegen alrecord)
+        set(EXTRA_INSTALLS ${EXTRA_INSTALLS} altonegen alrecord aldebug allafplay)
     endif()
     endif()
 
 
     message(STATUS "Building example programs")
     message(STATUS "Building example programs")
 
 
     if(SNDFILE_FOUND)
     if(SNDFILE_FOUND)
         add_executable(alplay examples/alplay.c)
         add_executable(alplay examples/alplay.c)
-        target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
+        target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon
             ${UNICODE_FLAG})
             ${UNICODE_FLAG})
-        set_target_properties(alplay PROPERTIES ${DEFAULT_TARGET_PROPS})
+        set_target_properties(alplay PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
 
 
         add_executable(alstream examples/alstream.c)
         add_executable(alstream examples/alstream.c)
-        target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
+        target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon
             ${UNICODE_FLAG})
             ${UNICODE_FLAG})
-        set_target_properties(alstream PROPERTIES ${DEFAULT_TARGET_PROPS})
+        set_target_properties(alstream PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
 
 
         add_executable(alreverb examples/alreverb.c)
         add_executable(alreverb examples/alreverb.c)
-        target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
+        target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon
             ${UNICODE_FLAG})
             ${UNICODE_FLAG})
-        set_target_properties(alreverb PROPERTIES ${DEFAULT_TARGET_PROPS})
+        set_target_properties(alreverb PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
 
 
         add_executable(almultireverb examples/almultireverb.c)
         add_executable(almultireverb examples/almultireverb.c)
         target_link_libraries(almultireverb
         target_link_libraries(almultireverb
-            PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${MATH_LIB} ${UNICODE_FLAG})
-        set_target_properties(almultireverb PROPERTIES ${DEFAULT_TARGET_PROPS})
+            PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon ${MATH_LIB} ${UNICODE_FLAG})
+        set_target_properties(almultireverb PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
 
 
         add_executable(allatency examples/allatency.c)
         add_executable(allatency examples/allatency.c)
-        target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
+        target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon
             ${UNICODE_FLAG})
             ${UNICODE_FLAG})
-        set_target_properties(allatency PROPERTIES ${DEFAULT_TARGET_PROPS})
+        set_target_properties(allatency PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
 
 
         add_executable(alhrtf examples/alhrtf.c)
         add_executable(alhrtf examples/alhrtf.c)
         target_link_libraries(alhrtf
         target_link_libraries(alhrtf
-            PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${MATH_LIB} ${UNICODE_FLAG})
-        set_target_properties(alhrtf PROPERTIES ${DEFAULT_TARGET_PROPS})
+            PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon ${MATH_LIB} ${UNICODE_FLAG})
+        set_target_properties(alhrtf PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
 
 
         add_executable(alstreamcb examples/alstreamcb.cpp)
         add_executable(alstreamcb examples/alstreamcb.cpp)
-        target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
-            ${UNICODE_FLAG})
-        set_target_properties(alstreamcb PROPERTIES ${DEFAULT_TARGET_PROPS})
+        target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon
+            ${UNICODE_FLAG} alsoft::fmt)
+        set_target_properties(alstreamcb PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
+
+        add_executable(aldirect examples/aldirect.cpp)
+        target_link_libraries(aldirect PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon
+            ${UNICODE_FLAG} alsoft::fmt)
+        set_target_properties(aldirect PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
 
 
         add_executable(alconvolve examples/alconvolve.c)
         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})
+        target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} alsoft.common SndFile::SndFile
+            alsoft.excommon ${UNICODE_FLAG})
+        set_target_properties(alconvolve PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
 
 
         if(ALSOFT_INSTALL_EXAMPLES)
         if(ALSOFT_INSTALL_EXAMPLES)
             set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alplay alstream alreverb almultireverb allatency
             set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alplay alstream alreverb almultireverb allatency
-                alhrtf)
+                alhrtf aldirect)
         endif()
         endif()
 
 
         message(STATUS "Building SndFile example programs")
         message(STATUS "Building SndFile example programs")
     endif()
     endif()
 
 
-    if(SDL2_FOUND)
+    # Can't safely use SDL3 and SDL2 together
+    if(SDL3_FOUND AND NOT HAVE_SDL2)
+        message(STATUS "Building SDL3 example programs")
+
         add_executable(alloopback examples/alloopback.c)
         add_executable(alloopback examples/alloopback.c)
         target_link_libraries(alloopback
         target_link_libraries(alloopback
-            PRIVATE ${LINKER_FLAGS} SDL2::SDL2 al-excommon ${MATH_LIB})
-        set_target_properties(alloopback PROPERTIES ${DEFAULT_TARGET_PROPS})
+            PRIVATE ${LINKER_FLAGS} SDL3::SDL3 alsoft.excommon ${MATH_LIB})
+        set_target_properties(alloopback PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
 
 
         if(ALSOFT_INSTALL_EXAMPLES)
         if(ALSOFT_INSTALL_EXAMPLES)
             set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alloopback)
             set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alloopback)
         endif()
         endif()
 
 
-        message(STATUS "Building SDL example programs")
-
         set(FFVER_OK FALSE)
         set(FFVER_OK FALSE)
         if(FFMPEG_FOUND)
         if(FFMPEG_FOUND)
             set(FFVER_OK TRUE)
             set(FFVER_OK TRUE)
@@ -1811,19 +1908,19 @@ if(ALSOFT_EXAMPLES)
             add_executable(alffplay examples/alffplay.cpp)
             add_executable(alffplay examples/alffplay.cpp)
             target_include_directories(alffplay PRIVATE ${FFMPEG_INCLUDE_DIRS})
             target_include_directories(alffplay PRIVATE ${FFMPEG_INCLUDE_DIRS})
             target_link_libraries(alffplay
             target_link_libraries(alffplay
-                PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ${FFMPEG_LIBRARIES} al-excommon)
-            set_target_properties(alffplay PROPERTIES ${DEFAULT_TARGET_PROPS})
+                PRIVATE ${LINKER_FLAGS} SDL3::SDL3 ${FFMPEG_LIBRARIES} alsoft.excommon alsoft::fmt)
+            set_target_properties(alffplay PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
 
 
             if(ALSOFT_INSTALL_EXAMPLES)
             if(ALSOFT_INSTALL_EXAMPLES)
                 set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alffplay)
                 set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alffplay)
             endif()
             endif()
-            message(STATUS "Building SDL+FFmpeg example programs")
+            message(STATUS "Building SDL3+FFmpeg example programs")
         endif()
         endif()
     endif()
     endif()
     message(STATUS "")
     message(STATUS "")
 endif()
 endif()
 
 
-if (ALSOFT_TESTS)
+if(ALSOFT_TESTS)
 add_subdirectory(tests)
 add_subdirectory(tests)
 endif()
 endif()
 
 
@@ -1834,7 +1931,3 @@ if(EXTRA_INSTALLS)
         LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
         LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
         ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
         ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
 endif()
 endif()
-
-if(MEGA)
-    install(TARGETS ${IMPL_TARGET} RUNTIME DESTINATION . LIBRARY DESTINATION .)
-endif()

+ 134 - 0
libs/openal-soft/ChangeLog

@@ -1,3 +1,137 @@
+openal-soft-1.24.3:
+
+    Fixed using as a static library when linked into another project that uses
+    fmtlib.
+
+    Fixed building with static VC runtimes.
+
+    Fixed building with Windows headers that default to older targets.
+
+    Fixed building on 32-bit targets that use 32-bit file offsets.
+
+    Fixed handling WASAPI enumerated device changes.
+
+    Fixed a crash with UWP builds when __wargv is null.
+
+    Fixed using AL_FORMAT_BFORMAT3D_I32.
+
+    Improved the bsinc resamplers' cutoff frequencies.
+
+    Slightly reduced the aliasing noise in the cubic spline resampler.
+
+    Added new bsinc48 and fast_bsinc48 resampler options.
+
+    Added support for 16KB page sizes on Android.
+
+    Added support for using NFC filters with UHJ output.
+
+openal-soft-1.24.2:
+
+    Implemented the AL_SOFT_bformat_hoa extension.
+
+    Implemented default device change events for the PulseAudio backend.
+
+    Implemented an option for WASAPI exclusive mode playback.
+
+    Fixed reverb being too quiet for sounds from different directions.
+
+    Fixed compiling with certain versions of Clang.
+
+    Fixed compiling for some older macOS versions.
+
+    Fixed building alffplay on systems without pkg-config.
+
+    Improved output format detection for CoreAudio.
+
+    Changed the default resampler back to Cubic Spline.
+
+    Added an SDL3 playback backend. Disabled by default to avoid a runtime
+    dependency and for compatibility; a single process can't safely use SDL2
+    and SDL3 together on some OSs, so enable with care.
+
+    Converted examples from SDL2 to SDL3.
+
+    Integrated fmtlib into the main library and router for logging and string
+    formatting.
+
+openal-soft-1.24.1:
+
+    Fixed compilation on PowerPC.
+
+    Fixed compilation on some targets that lack lock-free 64-bit atomics.
+
+    Fixed a crash when parsing certain option values.
+
+    Fixed applying noexcept in the public headers with MSVC.
+
+    Fixed building for UWP with vcpkg.
+
+    Improved compatibility when compiling as C++20 or later.
+
+    Integrated fmtlib for some examples and utilities.
+
+openal-soft-1.24.0:
+
+    Updated library codebase to C++17.
+
+    Implemented the ALC_SOFT_system_events extension.
+
+    Implemented the AL_EXT_debug extension.
+
+    Implemented the AL_EXT_direct_context extension.
+
+    Implemented speaker configuration and headphones detection on CoreAudio.
+
+    Fixed a potential crash with some extension functions on 32-bit Windows.
+
+    Fixed a crash that can occur when stopping playback with the Oboe backend.
+
+    Fixed calculating the reverb room rolloff.
+
+    Fixed EAX occlusion, obstruction, and exclusion low-pass filter strength.
+
+    Fixed EAX distance factor calculations.
+
+    Fixed querying AL_EFFECTSLOT_EFFECT on auxiliary effect slots.
+
+    Fixed compilation on some macOS systems that lack libdispatch.
+
+    Fixed compilation as a subproject with MinGW.
+
+    Changed the context error state to be thread-local. This is technically out
+    of spec, but necessary to avoid race conditions with multi-threaded use.
+
+    Split the cubic resampler into 4-point spline and gaussian variants. The
+    latter prioritizing the suppression of aliasing distortion and harmonics,
+    the former not reducing high frequencies as much.
+
+    Improved timing precision of starting delayed sources.
+
+    Improved ring modulator quality.
+
+    Improved performance of convolution reverb.
+
+    Improved WASAPI device enumeration performance.
+
+    Added UWP support.
+
+    Added 'noexcept' to functions and function types when compiled as C++. As a
+    C API, OpenAL can't be expected to throw C++ exceptions, nor can it handle
+    them if they leave a callback.
+
+    Added an experimental config option for using WASAPI spatial audio output.
+
+    Added enumeration support to the PortAudio backend.
+
+    Added compatibility options to override the AL_VENDOR, AL_VERSION, and
+    AL_RENDERER strings.
+
+    Added an example to play LAF files.
+
+    Disabled real-time mixing by default for PipeWire playback.
+
+    Disabled the SndIO backend by default on non-BSD targets.
+
 openal-soft-1.23.1:
 openal-soft-1.23.1:
 
 
     Implemented the AL_SOFT_UHJ_ex extension.
     Implemented the AL_SOFT_UHJ_ex extension.

+ 11 - 0
libs/openal-soft/README.md

@@ -78,15 +78,26 @@ API, including some extensions. It also includes utility libraries for math and
 linear algebra, which can be useful for 3D calculations.
 linear algebra, which can be useful for 3D calculations.
 
 
 Java Bindings:
 Java Bindings:
+* [LWJGL](https://github.com/LWJGL/lwjgl3), the Lightweight Java Game Library,
+includes Java bindings for the OpenAL API, usable with OpenAL Soft.
 * [JOAL](https://jogamp.org/joal/www/), part of the JogAmp project, includes
 * [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
 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
 higher level Sound3D Toolkit API and utility functions to make easier use of
 OpenAL features and capabilities.
 OpenAL features and capabilities.
 
 
+Kotlin Bindings:
+* [Multiplatform OpenAL](https://git.karmakrafts.dev/kk/multiplatform-openal), developed for the Kleaver project,
+includes Kotlin/Native bindings for the OpenAL API, based on OpenAL Soft with support
+for Windows, Linux, macOS, iOS and Android.
+
 Python Bindings:
 Python Bindings:
 * [PyOpenAL](https://pypi.org/project/PyOpenAL/). Also includes methods to play
 * [PyOpenAL](https://pypi.org/project/PyOpenAL/). Also includes methods to play
 wave files and, with PyOgg, also Vorbis, Opus, and FLAC.
 wave files and, with PyOgg, also Vorbis, Opus, and FLAC.
 
 
+FreePascal/Lazarus Bindings:
+* [ALSound](https://github.com/Lulu04/ALSound). Also includes a higher level
+API and libsndfile support to simplify loading and playing sounds.
+
 Other bindings for these and other languages also exist. This list will grow as
 Other bindings for these and other languages also exist. This list will grow as
 more bindings are found.
 more bindings are found.
 
 

Fichier diff supprimé car celui-ci est trop grand
+ 237 - 268
libs/openal-soft/al/auxeffectslot.cpp


+ 72 - 66
libs/openal-soft/al/auxeffectslot.h

@@ -1,8 +1,11 @@
 #ifndef AL_AUXEFFECTSLOT_H
 #ifndef AL_AUXEFFECTSLOT_H
 #define AL_AUXEFFECTSLOT_H
 #define AL_AUXEFFECTSLOT_H
 
 
+#include "config.h"
+
 #include <array>
 #include <array>
 #include <atomic>
 #include <atomic>
+#include <bitset>
 #include <cstdint>
 #include <cstdint>
 #include <string_view>
 #include <string_view>
 #include <utility>
 #include <utility>
@@ -16,7 +19,7 @@
 #include "core/effectslot.h"
 #include "core/effectslot.h"
 #include "intrusive_ptr.h"
 #include "intrusive_ptr.h"
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 #include <memory>
 #include <memory>
 #include "eax/api.h"
 #include "eax/api.h"
 #include "eax/call.h"
 #include "eax/call.h"
@@ -28,7 +31,7 @@
 
 
 struct ALbuffer;
 struct ALbuffer;
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 class EaxFxSlotException : public EaxException {
 class EaxFxSlotException : public EaxException {
 public:
 public:
 	explicit EaxFxSlotException(const char* message)
 	explicit EaxFxSlotException(const char* message)
@@ -37,10 +40,8 @@ public:
 };
 };
 #endif // ALSOFT_EAX
 #endif // ALSOFT_EAX
 
 
-enum class SlotState : ALenum {
-    Initial = AL_INITIAL,
-    Playing = AL_PLAYING,
-    Stopped = AL_STOPPED,
+enum class SlotState : bool {
+    Initial, Playing,
 };
 };
 
 
 struct ALeffectslot {
 struct ALeffectslot {
@@ -52,7 +53,7 @@ struct ALeffectslot {
 
 
     struct EffectData {
     struct EffectData {
         EffectSlotType Type{EffectSlotType::None};
         EffectSlotType Type{EffectSlotType::None};
-        EffectProps Props{};
+        EffectProps Props;
 
 
         al::intrusive_ptr<EffectState> State;
         al::intrusive_ptr<EffectState> State;
     };
     };
@@ -69,7 +70,7 @@ struct ALeffectslot {
     /* Self ID */
     /* Self ID */
     ALuint id{};
     ALuint id{};
 
 
-    ALeffectslot(ALCcontext *context);
+    explicit ALeffectslot(ALCcontext *context);
     ALeffectslot(const ALeffectslot&) = delete;
     ALeffectslot(const ALeffectslot&) = delete;
     ALeffectslot& operator=(const ALeffectslot&) = delete;
     ALeffectslot& operator=(const ALeffectslot&) = delete;
     ~ALeffectslot();
     ~ALeffectslot();
@@ -81,13 +82,14 @@ struct ALeffectslot {
     static void SetName(ALCcontext *context, ALuint id, std::string_view name);
     static void SetName(ALCcontext *context, ALuint id, std::string_view name);
 
 
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 public:
 public:
     void eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index);
     void eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index);
 
 
-    [[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_; }
+    [[nodiscard]]
+    auto eax_get_index() const noexcept -> EaxFxSlotIndexValue { return mEaxFXSlotIndex; }
+    [[nodiscard]]
+    auto eax_get_eax_fx_slot() const noexcept -> const EAX50FXSLOTPROPERTIES& { return mEax; }
 
 
     // Returns `true` if all sources should be updated, or `false` otherwise.
     // Returns `true` if all sources should be updated, or `false` otherwise.
     [[nodiscard]] auto eax_dispatch(const EaxCall& call) -> bool
     [[nodiscard]] auto eax_dispatch(const EaxCall& call) -> bool
@@ -96,25 +98,24 @@ public:
     void eax_commit();
     void eax_commit();
 
 
 private:
 private:
-    static constexpr auto eax_load_effect_dirty_bit = EaxDirtyFlags{1} << 0;
-    static constexpr auto eax_volume_dirty_bit = EaxDirtyFlags{1} << 1;
-    static constexpr auto eax_lock_dirty_bit = EaxDirtyFlags{1} << 2;
-    static constexpr auto eax_flags_dirty_bit = EaxDirtyFlags{1} << 3;
-    static constexpr auto eax_occlusion_dirty_bit = EaxDirtyFlags{1} << 4;
-    static constexpr auto eax_occlusion_lf_ratio_dirty_bit = EaxDirtyFlags{1} << 5;
+    enum {
+        eax_load_effect_dirty_bit,
+        eax_volume_dirty_bit,
+        eax_lock_dirty_bit,
+        eax_flags_dirty_bit,
+        eax_occlusion_dirty_bit,
+        eax_occlusion_lf_ratio_dirty_bit,
+        eax_dirty_bit_count
+    };
 
 
     using Exception = EaxFxSlotException;
     using Exception = EaxFxSlotException;
 
 
-    using Eax4Props = EAX40FXSLOTPROPERTIES;
-
     struct Eax4State {
     struct Eax4State {
-        Eax4Props i; // Immediate.
+        EAX40FXSLOTPROPERTIES i; // Immediate.
     };
     };
 
 
-    using Eax5Props = EAX50FXSLOTPROPERTIES;
-
     struct Eax5State {
     struct Eax5State {
-        Eax5Props i; // Immediate.
+        EAX50FXSLOTPROPERTIES i; // Immediate.
     };
     };
 
 
     struct EaxRangeValidator {
     struct EaxRangeValidator {
@@ -194,6 +195,17 @@ private:
         }
         }
     };
     };
 
 
+    struct Eax5FlagsValidator {
+        void operator()(unsigned long ulFlags) const
+        {
+            EaxRangeValidator{}(
+                "Flags",
+                ulFlags,
+                0UL,
+                ~EAX50FXSLOTFLAGS_RESERVED);
+        }
+    };
+
     struct Eax5OcclusionValidator {
     struct Eax5OcclusionValidator {
         void operator()(long lOcclusion) const
         void operator()(long lOcclusion) const
         {
         {
@@ -216,35 +228,27 @@ private:
         }
         }
     };
     };
 
 
-    struct Eax5FlagsValidator {
-        void operator()(unsigned long ulFlags) const
-        {
-            EaxRangeValidator{}(
-                "Flags",
-                ulFlags,
-                0UL,
-                ~EAX50FXSLOTFLAGS_RESERVED);
-        }
-    };
-
     struct Eax5AllValidator {
     struct Eax5AllValidator {
         void operator()(const EAX50FXSLOTPROPERTIES& all) const
         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);
             Eax5OcclusionValidator{}(all.lOcclusion);
             Eax5OcclusionLfRatioValidator{}(all.flOcclusionLFRatio);
             Eax5OcclusionLfRatioValidator{}(all.flOcclusionLFRatio);
         }
         }
     };
     };
 
 
-    ALCcontext* eax_al_context_{};
-    EaxFxSlotIndexValue eax_fx_slot_index_{};
-    int eax_version_{}; // Current EAX version.
-    EaxDirtyFlags eax_df_{}; // Dirty flags for the current EAX version.
-    EaxEffectUPtr eax_effect_{};
-    Eax5State eax123_{}; // EAX1/EAX2/EAX3 state.
-    Eax4State eax4_{}; // EAX4 state.
-    Eax5State eax5_{}; // EAX5 state.
-    Eax5Props eax_{}; // Current EAX state.
+    ALCcontext* mEaxALContext{};
+    EaxFxSlotIndexValue mEaxFXSlotIndex{};
+    int mEaxVersion{}; // Current EAX version.
+    std::bitset<eax_dirty_bit_count> mEaxDf; // Dirty flags for the current EAX version.
+    EaxEffectUPtr mEaxEffect;
+    Eax5State mEax123{}; // EAX1/EAX2/EAX3 state.
+    Eax4State mEax4{}; // EAX4 state.
+    Eax5State mEax5{}; // EAX5 state.
+    EAX50FXSLOTPROPERTIES mEax{}; // Current EAX state.
 
 
     [[noreturn]] static void eax_fail(const char* message);
     [[noreturn]] static void eax_fail(const char* message);
     [[noreturn]] static void eax_fail_unknown_effect_id();
     [[noreturn]] static void eax_fail_unknown_effect_id();
@@ -255,12 +259,14 @@ private:
     // validates it,
     // validates it,
     // sets a dirty flag only if the new value differs form the old one,
     // sets a dirty flag only if the new value differs form the old one,
     // and assigns the new value.
     // and assigns the new value.
-    template<typename TValidator, EaxDirtyFlags TDirtyBit, typename TProperties>
-    static void eax_fx_slot_set(const EaxCall& call, TProperties& dst, EaxDirtyFlags& dirty_flags)
+    template<typename TValidator, size_t DirtyBit, typename TProperties>
+    static void eax_fx_slot_set(const EaxCall& call, TProperties& dst,
+        std::bitset<eax_dirty_bit_count>& dirty_flags)
     {
     {
         const auto& src = call.get_value<Exception, const TProperties>();
         const auto& src = call.get_value<Exception, const TProperties>();
         TValidator{}(src);
         TValidator{}(src);
-        dirty_flags |= (dst != src ? TDirtyBit : EaxDirtyFlags{});
+        if(dst != src)
+            dirty_flags.set(DirtyBit);
         dst = src;
         dst = src;
     }
     }
 
 
@@ -268,18 +274,18 @@ private:
     // validates it,
     // validates it,
     // sets a dirty flag without comparing the values,
     // sets a dirty flag without comparing the values,
     // and assigns the new value.
     // and assigns the new value.
-    template<typename TValidator, EaxDirtyFlags TDirtyBit, typename TProperties>
+    template<typename TValidator, size_t DirtyBit, typename TProperties>
     static void eax_fx_slot_set_dirty(const EaxCall& call, TProperties& dst,
     static void eax_fx_slot_set_dirty(const EaxCall& call, TProperties& dst,
-        EaxDirtyFlags& dirty_flags)
+        std::bitset<eax_dirty_bit_count>& dirty_flags)
     {
     {
         const auto& src = call.get_value<Exception, const TProperties>();
         const auto& src = call.get_value<Exception, const TProperties>();
         TValidator{}(src);
         TValidator{}(src);
-        dirty_flags |= TDirtyBit;
+        dirty_flags.set(DirtyBit);
         dst = src;
         dst = src;
     }
     }
 
 
     [[nodiscard]] constexpr auto eax4_fx_slot_is_legacy() const noexcept -> bool
     [[nodiscard]] constexpr auto eax4_fx_slot_is_legacy() const noexcept -> bool
-    { return eax_fx_slot_index_ < 2; }
+    { return mEaxFXSlotIndex < 2; }
 
 
     void eax4_fx_slot_ensure_unlocked() const;
     void eax4_fx_slot_ensure_unlocked() const;
 
 
@@ -287,15 +293,15 @@ private:
     [[nodiscard]] auto eax_get_eax_default_effect_guid() const noexcept -> const GUID&;
     [[nodiscard]] auto eax_get_eax_default_effect_guid() const noexcept -> const GUID&;
     [[nodiscard]] auto eax_get_eax_default_lock() const noexcept -> long;
     [[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;
-    void eax4_fx_slot_set_current_defaults(const Eax4Props& props) noexcept;
-    void eax5_fx_slot_set_current_defaults(const Eax5Props& props) noexcept;
+    void eax4_fx_slot_set_defaults(EAX40FXSLOTPROPERTIES& props) noexcept;
+    void eax5_fx_slot_set_defaults(EAX50FXSLOTPROPERTIES& props) noexcept;
+    void eax4_fx_slot_set_current_defaults(const EAX40FXSLOTPROPERTIES& props) noexcept;
+    void eax5_fx_slot_set_current_defaults(const EAX50FXSLOTPROPERTIES& props) noexcept;
     void eax_fx_slot_set_current_defaults();
     void eax_fx_slot_set_current_defaults();
     void eax_fx_slot_set_defaults();
     void eax_fx_slot_set_defaults();
 
 
-    static void eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props);
-    static void eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props);
+    static void eax4_fx_slot_get(const EaxCall& call, const EAX40FXSLOTPROPERTIES& props);
+    static void eax5_fx_slot_get(const EaxCall& call, const EAX50FXSLOTPROPERTIES& props);
     void eax_fx_slot_get(const EaxCall& call) const;
     void eax_fx_slot_get(const EaxCall& call) const;
     // Returns `true` if all sources should be updated, or `false` otherwise.
     // Returns `true` if all sources should be updated, or `false` otherwise.
     bool eax_get(const EaxCall& call);
     bool eax_get(const EaxCall& call);
@@ -320,25 +326,25 @@ private:
     bool eax_set(const EaxCall& call);
     bool eax_set(const EaxCall& call);
 
 
     template<
     template<
-        EaxDirtyFlags TDirtyBit,
+        size_t DirtyBit,
         typename TMemberResult,
         typename TMemberResult,
         typename TProps,
         typename TProps,
         typename TState>
         typename TState>
-    void eax_fx_slot_commit_property(TState& state, EaxDirtyFlags& dst_df,
+    void eax_fx_slot_commit_property(TState& state, std::bitset<eax_dirty_bit_count>& dst_df,
         TMemberResult TProps::*member) noexcept
         TMemberResult TProps::*member) noexcept
     {
     {
         auto& src_i = state.i;
         auto& src_i = state.i;
-        auto& dst_i = eax_;
+        auto& dst_i = mEax;
 
 
-        if((eax_df_ & TDirtyBit) != EaxDirtyFlags{})
+        if(mEaxDf.test(DirtyBit))
         {
         {
-            dst_df |= TDirtyBit;
+            dst_df.set(DirtyBit);
             dst_i.*member = src_i.*member;
             dst_i.*member = src_i.*member;
         }
         }
     }
     }
 
 
-    void eax4_fx_slot_commit(EaxDirtyFlags& dst_df);
-    void eax5_fx_slot_commit(Eax5State& state, EaxDirtyFlags& dst_df);
+    void eax4_fx_slot_commit(std::bitset<eax_dirty_bit_count>& dst_df);
+    void eax5_fx_slot_commit(Eax5State& state, std::bitset<eax_dirty_bit_count>& dst_df);
 
 
     // `alAuxiliaryEffectSloti(effect_slot, AL_EFFECTSLOT_EFFECT, effect)`
     // `alAuxiliaryEffectSloti(effect_slot, AL_EFFECTSLOT_EFFECT, effect)`
     void eax_set_efx_slot_effect(EaxEffect &effect);
     void eax_set_efx_slot_effect(EaxEffect &effect);
@@ -359,7 +365,7 @@ public:
 
 
 void UpdateAllEffectSlotProps(ALCcontext *context);
 void UpdateAllEffectSlotProps(ALCcontext *context);
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 using EaxAlEffectSlotUPtr = std::unique_ptr<ALeffectslot, ALeffectslot::EaxDeleter>;
 using EaxAlEffectSlotUPtr = std::unique_ptr<ALeffectslot, ALeffectslot::EaxDeleter>;
 
 
 EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context);
 EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context);

Fichier diff supprimé car celui-ci est trop grand
+ 266 - 220
libs/openal-soft/al/buffer.cpp


+ 4 - 2
libs/openal-soft/al/buffer.h

@@ -1,6 +1,8 @@
 #ifndef AL_BUFFER_H
 #ifndef AL_BUFFER_H
 #define AL_BUFFER_H
 #define AL_BUFFER_H
 
 
+#include "config.h"
+
 #include <array>
 #include <array>
 #include <atomic>
 #include <atomic>
 #include <cstddef>
 #include <cstddef>
@@ -17,7 +19,7 @@
 #include "core/buffer_storage.h"
 #include "core/buffer_storage.h"
 #include "vector.h"
 #include "vector.h"
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 enum class EaxStorage : uint8_t {
 enum class EaxStorage : uint8_t {
     Automatic,
     Automatic,
     Accessible,
     Accessible,
@@ -54,7 +56,7 @@ struct ALbuffer : public BufferStorage {
 
 
     DISABLE_ALLOC
     DISABLE_ALLOC
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
     EaxStorage eax_x_ram_mode{EaxStorage::Automatic};
     EaxStorage eax_x_ram_mode{EaxStorage::Automatic};
     bool eax_x_ram_is_hardware{};
     bool eax_x_ram_is_hardware{};
 #endif // ALSOFT_EAX
 #endif // ALSOFT_EAX

+ 159 - 145
libs/openal-soft/al/debug.cpp

@@ -21,18 +21,17 @@
 
 
 #include "alc/context.h"
 #include "alc/context.h"
 #include "alc/device.h"
 #include "alc/device.h"
-#include "alc/inprogext.h"
 #include "alnumeric.h"
 #include "alnumeric.h"
 #include "alspan.h"
 #include "alspan.h"
-#include "alstring.h"
 #include "auxeffectslot.h"
 #include "auxeffectslot.h"
 #include "buffer.h"
 #include "buffer.h"
+#include "core/except.h"
 #include "core/logging.h"
 #include "core/logging.h"
 #include "core/voice.h"
 #include "core/voice.h"
 #include "direct_defs.h"
 #include "direct_defs.h"
 #include "effect.h"
 #include "effect.h"
-#include "error.h"
 #include "filter.h"
 #include "filter.h"
+#include "fmt/core.h"
 #include "intrusive_ptr.h"
 #include "intrusive_ptr.h"
 #include "opthelpers.h"
 #include "opthelpers.h"
 #include "source.h"
 #include "source.h"
@@ -45,6 +44,8 @@ DebugGroup::~DebugGroup() = default;
 
 
 namespace {
 namespace {
 
 
+using namespace std::string_view_literals;
+
 static_assert(DebugSeverityBase+DebugSeverityCount <= 32, "Too many debug bits");
 static_assert(DebugSeverityBase+DebugSeverityCount <= 32, "Too many debug bits");
 
 
 template<typename T, T ...Vals>
 template<typename T, T ...Vals>
@@ -109,7 +110,8 @@ constexpr auto GetDebugSourceEnum(DebugSource source) -> ALenum
     case DebugSource::Application: return AL_DEBUG_SOURCE_APPLICATION_EXT;
     case DebugSource::Application: return AL_DEBUG_SOURCE_APPLICATION_EXT;
     case DebugSource::Other: return AL_DEBUG_SOURCE_OTHER_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))};
+    throw std::runtime_error{fmt::format("Unexpected debug source value: {}",
+        int{al::to_underlying(source)})};
 }
 }
 
 
 constexpr auto GetDebugTypeEnum(DebugType type) -> ALenum
 constexpr auto GetDebugTypeEnum(DebugType type) -> ALenum
@@ -126,7 +128,8 @@ constexpr auto GetDebugTypeEnum(DebugType type) -> ALenum
     case DebugType::PopGroup: return AL_DEBUG_TYPE_POP_GROUP_EXT;
     case DebugType::PopGroup: return AL_DEBUG_TYPE_POP_GROUP_EXT;
     case DebugType::Other: return AL_DEBUG_TYPE_OTHER_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))};
+    throw std::runtime_error{fmt::format("Unexpected debug type value: {}",
+        int{al::to_underlying(type)})};
 }
 }
 
 
 constexpr auto GetDebugSeverityEnum(DebugSeverity severity) -> ALenum
 constexpr auto GetDebugSeverityEnum(DebugSeverity severity) -> ALenum
@@ -138,50 +141,51 @@ constexpr auto GetDebugSeverityEnum(DebugSeverity severity) -> ALenum
     case DebugSeverity::Low: return AL_DEBUG_SEVERITY_LOW_EXT;
     case DebugSeverity::Low: return AL_DEBUG_SEVERITY_LOW_EXT;
     case DebugSeverity::Notification: return AL_DEBUG_SEVERITY_NOTIFICATION_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))};
+    throw std::runtime_error{fmt::format("Unexpected debug severity value: {}",
+        int{al::to_underlying(severity)})};
 }
 }
 
 
 
 
-constexpr auto GetDebugSourceName(DebugSource source) noexcept -> const char*
+constexpr auto GetDebugSourceName(DebugSource source) noexcept -> std::string_view
 {
 {
     switch(source)
     switch(source)
     {
     {
-    case DebugSource::API: return "API";
-    case DebugSource::System: return "Audio System";
-    case DebugSource::ThirdParty: return "Third Party";
-    case DebugSource::Application: return "Application";
-    case DebugSource::Other: return "Other";
+    case DebugSource::API: return "API"sv;
+    case DebugSource::System: return "Audio System"sv;
+    case DebugSource::ThirdParty: return "Third Party"sv;
+    case DebugSource::Application: return "Application"sv;
+    case DebugSource::Other: return "Other"sv;
     }
     }
-    return "<invalid source>";
+    return "<invalid source>"sv;
 }
 }
 
 
-constexpr auto GetDebugTypeName(DebugType type) noexcept -> const char*
+constexpr auto GetDebugTypeName(DebugType type) noexcept -> std::string_view
 {
 {
     switch(type)
     switch(type)
     {
     {
-    case DebugType::Error: return "Error";
-    case DebugType::DeprecatedBehavior: return "Deprecated Behavior";
-    case DebugType::UndefinedBehavior: return "Undefined Behavior";
-    case DebugType::Portability: return "Portability";
-    case DebugType::Performance: return "Performance";
-    case DebugType::Marker: return "Marker";
-    case DebugType::PushGroup: return "Push Group";
-    case DebugType::PopGroup: return "Pop Group";
-    case DebugType::Other: return "Other";
+    case DebugType::Error: return "Error"sv;
+    case DebugType::DeprecatedBehavior: return "Deprecated Behavior"sv;
+    case DebugType::UndefinedBehavior: return "Undefined Behavior"sv;
+    case DebugType::Portability: return "Portability"sv;
+    case DebugType::Performance: return "Performance"sv;
+    case DebugType::Marker: return "Marker"sv;
+    case DebugType::PushGroup: return "Push Group"sv;
+    case DebugType::PopGroup: return "Pop Group"sv;
+    case DebugType::Other: return "Other"sv;
     }
     }
-    return "<invalid type>";
+    return "<invalid type>"sv;
 }
 }
 
 
-constexpr auto GetDebugSeverityName(DebugSeverity severity) noexcept -> const char*
+constexpr auto GetDebugSeverityName(DebugSeverity severity) noexcept -> std::string_view
 {
 {
     switch(severity)
     switch(severity)
     {
     {
-    case DebugSeverity::High: return "High";
-    case DebugSeverity::Medium: return "Medium";
-    case DebugSeverity::Low: return "Low";
-    case DebugSeverity::Notification: return "Notification";
+    case DebugSeverity::High: return "High"sv;
+    case DebugSeverity::Medium: return "Medium"sv;
+    case DebugSeverity::Low: return "Low"sv;
+    case DebugSeverity::Notification: return "Notification"sv;
     }
     }
-    return "<invalid severity>";
+    return "<invalid severity>"sv;
 }
 }
 
 
 } // namespace
 } // namespace
@@ -195,8 +199,8 @@ void ALCcontext::sendDebugMessage(std::unique_lock<std::mutex> &debuglock, Debug
 
 
     if(message.length() >= MaxDebugMessageLength) UNLIKELY
     if(message.length() >= MaxDebugMessageLength) UNLIKELY
     {
     {
-        ERR("Debug message too long (%zu >= %d):\n-> %.*s\n", message.length(),
-            MaxDebugMessageLength, al::sizei(message), message.data());
+        ERR("Debug message too long ({} >= {}):\n-> {}", message.length(),
+            MaxDebugMessageLength, message);
         return;
         return;
     }
     }
 
 
@@ -231,13 +235,13 @@ void ALCcontext::sendDebugMessage(std::unique_lock<std::mutex> &debuglock, Debug
             mDebugLog.emplace_back(source, type, id, severity, message);
             mDebugLog.emplace_back(source, type, id, severity, message);
         else UNLIKELY
         else UNLIKELY
             ERR("Debug message log overflow. Lost message:\n"
             ERR("Debug message log overflow. Lost message:\n"
-                "  Source: %s\n"
-                "  Type: %s\n"
-                "  ID: %u\n"
-                "  Severity: %s\n"
-                "  Message: \"%.*s\"\n",
+                "  Source: {}\n"
+                "  Type: {}\n"
+                "  ID: {}\n"
+                "  Severity: {}\n"
+                "  Message: \"{}\"",
                 GetDebugSourceName(source), GetDebugTypeName(type), id,
                 GetDebugSourceName(source), GetDebugTypeName(type), id,
-                GetDebugSeverityName(severity), al::sizei(message), message.data());
+                GetDebugSeverityName(severity), message);
     }
     }
 }
 }
 
 
@@ -260,32 +264,36 @@ try {
         return;
         return;
 
 
     if(!message)
     if(!message)
-        throw al::context_error{AL_INVALID_VALUE, "Null message pointer"};
+        context->throw_error(AL_INVALID_VALUE, "Null message pointer");
 
 
     auto msgview = (length < 0) ? std::string_view{message}
     auto msgview = (length < 0) ? std::string_view{message}
         : std::string_view{message, static_cast<uint>(length)};
         : std::string_view{message, static_cast<uint>(length)};
     if(msgview.size() >= MaxDebugMessageLength)
     if(msgview.size() >= MaxDebugMessageLength)
-        throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%zu >= %d)",
-            msgview.size(), MaxDebugMessageLength};
+        context->throw_error(AL_INVALID_VALUE, "Debug message too long ({} >= {})", msgview.size(),
+            MaxDebugMessageLength);
 
 
     auto dsource = GetDebugSource(source);
     auto dsource = GetDebugSource(source);
     if(!dsource)
     if(!dsource)
-        throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
+        context->throw_error(AL_INVALID_ENUM, "Invalid debug source {:#04x}", as_unsigned(source));
     if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
     if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
-        throw al::context_error{AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source};
+        context->throw_error(AL_INVALID_ENUM, "Debug source {:#04x} not allowed",
+            as_unsigned(source));
 
 
     auto dtype = GetDebugType(type);
     auto dtype = GetDebugType(type);
     if(!dtype)
     if(!dtype)
-        throw al::context_error{AL_INVALID_ENUM, "Invalid debug type 0x%04x", type};
+        context->throw_error(AL_INVALID_ENUM, "Invalid debug type {:#04x}", as_unsigned(type));
 
 
     auto dseverity = GetDebugSeverity(severity);
     auto dseverity = GetDebugSeverity(severity);
     if(!dseverity)
     if(!dseverity)
-        throw al::context_error{AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity};
+        context->throw_error(AL_INVALID_ENUM, "Invalid debug severity {:#04x}",
+            as_unsigned(severity));
 
 
     context->debugMessage(*dsource, *dtype, id, *dseverity, msgview);
     context->debugMessage(*dsource, *dtype, id, *dseverity, msgview);
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 
 
@@ -296,20 +304,20 @@ try {
     if(count > 0)
     if(count > 0)
     {
     {
         if(!ids)
         if(!ids)
-            throw al::context_error{AL_INVALID_VALUE, "IDs is null with non-0 count"};
+            context->throw_error(AL_INVALID_VALUE, "IDs is null with non-0 count");
         if(source == AL_DONT_CARE_EXT)
         if(source == AL_DONT_CARE_EXT)
-            throw al::context_error{AL_INVALID_OPERATION,
-                "Debug source cannot be AL_DONT_CARE_EXT with IDs"};
+            context->throw_error(AL_INVALID_OPERATION,
+                "Debug source cannot be AL_DONT_CARE_EXT with IDs");
         if(type == AL_DONT_CARE_EXT)
         if(type == AL_DONT_CARE_EXT)
-            throw al::context_error{AL_INVALID_OPERATION,
-                "Debug type cannot be AL_DONT_CARE_EXT with IDs"};
+            context->throw_error(AL_INVALID_OPERATION,
+                "Debug type cannot be AL_DONT_CARE_EXT with IDs");
         if(severity != AL_DONT_CARE_EXT)
         if(severity != AL_DONT_CARE_EXT)
-            throw al::context_error{AL_INVALID_OPERATION,
-                "Debug severity must be AL_DONT_CARE_EXT with IDs"};
+            context->throw_error(AL_INVALID_OPERATION,
+                "Debug severity must be AL_DONT_CARE_EXT with IDs");
     }
     }
 
 
     if(enable != AL_TRUE && enable != AL_FALSE)
     if(enable != AL_TRUE && enable != AL_FALSE)
-        throw al::context_error{AL_INVALID_ENUM, "Invalid debug enable %d", enable};
+        context->throw_error(AL_INVALID_ENUM, "Invalid debug enable {}", enable);
 
 
     static constexpr size_t ElemCount{DebugSourceCount + DebugTypeCount + DebugSeverityCount};
     static constexpr size_t ElemCount{DebugSourceCount + DebugTypeCount + DebugSeverityCount};
     static constexpr auto Values = make_array_sequence<uint8_t,ElemCount>();
     static constexpr auto Values = make_array_sequence<uint8_t,ElemCount>();
@@ -319,7 +327,8 @@ try {
     {
     {
         auto dsource = GetDebugSource(source);
         auto dsource = GetDebugSource(source);
         if(!dsource)
         if(!dsource)
-            throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
+            context->throw_error(AL_INVALID_ENUM, "Invalid debug source {:#04x}",
+                as_unsigned(source));
         srcIndices = srcIndices.subspan(al::to_underlying(*dsource), 1);
         srcIndices = srcIndices.subspan(al::to_underlying(*dsource), 1);
     }
     }
 
 
@@ -328,7 +337,7 @@ try {
     {
     {
         auto dtype = GetDebugType(type);
         auto dtype = GetDebugType(type);
         if(!dtype)
         if(!dtype)
-            throw al::context_error{AL_INVALID_ENUM, "Invalid debug type 0x%04x", type};
+            context->throw_error(AL_INVALID_ENUM, "Invalid debug type {:#04x}", as_unsigned(type));
         typeIndices = typeIndices.subspan(al::to_underlying(*dtype), 1);
         typeIndices = typeIndices.subspan(al::to_underlying(*dtype), 1);
     }
     }
 
 
@@ -337,7 +346,8 @@ try {
     {
     {
         auto dseverity = GetDebugSeverity(severity);
         auto dseverity = GetDebugSeverity(severity);
         if(!dseverity)
         if(!dseverity)
-            throw al::context_error{AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity};
+            context->throw_error(AL_INVALID_ENUM, "Invalid debug severity {:#04x}",
+                as_unsigned(severity));
         svrIndices = svrIndices.subspan(al::to_underlying(*dseverity), 1);
         svrIndices = svrIndices.subspan(al::to_underlying(*dseverity), 1);
     }
     }
 
 
@@ -383,8 +393,10 @@ try {
             [apply_type](const uint idx){ apply_type(1<<idx); });
             [apply_type](const uint idx){ apply_type(1<<idx); });
     }
     }
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 
 
@@ -396,23 +408,24 @@ try {
     {
     {
         size_t newlen{std::strlen(message)};
         size_t newlen{std::strlen(message)};
         if(newlen >= MaxDebugMessageLength)
         if(newlen >= MaxDebugMessageLength)
-            throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%zu >= %d)", newlen,
-                MaxDebugMessageLength};
+            context->throw_error(AL_INVALID_VALUE, "Debug message too long ({} >= {})", newlen,
+                MaxDebugMessageLength);
         length = static_cast<ALsizei>(newlen);
         length = static_cast<ALsizei>(newlen);
     }
     }
     else if(length >= MaxDebugMessageLength)
     else if(length >= MaxDebugMessageLength)
-        throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%d >= %d)", length,
-            MaxDebugMessageLength};
+        context->throw_error(AL_INVALID_VALUE, "Debug message too long ({} >= {})", length,
+            MaxDebugMessageLength);
 
 
     auto dsource = GetDebugSource(source);
     auto dsource = GetDebugSource(source);
     if(!dsource)
     if(!dsource)
-        throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
+        context->throw_error(AL_INVALID_ENUM, "Invalid debug source {:#04x}", as_unsigned(source));
     if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
     if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
-        throw al::context_error{AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source};
+        context->throw_error(AL_INVALID_ENUM, "Debug source {:#04x} not allowed",
+            as_unsigned(source));
 
 
     std::unique_lock<std::mutex> debuglock{context->mDebugCbLock};
     std::unique_lock<std::mutex> debuglock{context->mDebugCbLock};
     if(context->mDebugGroups.size() >= MaxDebugGroupDepth)
     if(context->mDebugGroups.size() >= MaxDebugGroupDepth)
-        throw al::context_error{AL_STACK_OVERFLOW_EXT, "Pushing too many debug groups"};
+        context->throw_error(AL_STACK_OVERFLOW_EXT, "Pushing too many debug groups");
 
 
     context->mDebugGroups.emplace_back(*dsource, id,
     context->mDebugGroups.emplace_back(*dsource, id,
         std::string_view{message, static_cast<uint>(length)});
         std::string_view{message, static_cast<uint>(length)});
@@ -426,8 +439,10 @@ try {
         context->sendDebugMessage(debuglock, newback.mSource, DebugType::PushGroup, newback.mId,
         context->sendDebugMessage(debuglock, newback.mSource, DebugType::PushGroup, newback.mId,
             DebugSeverity::Notification, newback.mMessage);
             DebugSeverity::Notification, newback.mMessage);
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 FORCE_ALIGN DECL_FUNCEXT(void, alPopDebugGroup,EXT)
 FORCE_ALIGN DECL_FUNCEXT(void, alPopDebugGroup,EXT)
@@ -435,8 +450,7 @@ FORCE_ALIGN void AL_APIENTRY alPopDebugGroupDirectEXT(ALCcontext *context) noexc
 try {
 try {
     std::unique_lock<std::mutex> debuglock{context->mDebugCbLock};
     std::unique_lock<std::mutex> debuglock{context->mDebugCbLock};
     if(context->mDebugGroups.size() <= 1)
     if(context->mDebugGroups.size() <= 1)
-        throw al::context_error{AL_STACK_UNDERFLOW_EXT,
-            "Attempting to pop the default debug group"};
+        context->throw_error(AL_STACK_UNDERFLOW_EXT, "Attempting to pop the default debug group");
 
 
     DebugGroup &debug = context->mDebugGroups.back();
     DebugGroup &debug = context->mDebugGroups.back();
     const auto source = debug.mSource;
     const auto source = debug.mSource;
@@ -448,8 +462,10 @@ try {
         context->sendDebugMessage(debuglock, source, DebugType::PopGroup, id,
         context->sendDebugMessage(debuglock, source, DebugType::PopGroup, id,
             DebugSeverity::Notification, message);
             DebugSeverity::Notification, message);
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 
 
@@ -458,66 +474,60 @@ FORCE_ALIGN ALuint AL_APIENTRY alGetDebugMessageLogDirectEXT(ALCcontext *context
     ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities,
     ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities,
     ALsizei *lengths, ALchar *logBuf) noexcept
     ALsizei *lengths, ALchar *logBuf) noexcept
 try {
 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};
+    if(logBuf && logBufSize < 0)
+        context->throw_error(AL_INVALID_VALUE, "Negative debug log buffer size");
+
+    const auto sourcesSpan = al::span{sources, sources ? count : 0u};
+    const auto typesSpan = al::span{types, types ? count : 0u};
+    const auto idsSpan = al::span{ids, ids ? count : 0u};
+    const auto severitiesSpan = al::span{severities, severities ? count : 0u};
+    const auto lengthsSpan = al::span{lengths, lengths ? count : 0u};
+    const auto logSpan = al::span{logBuf, logBuf ? static_cast<ALuint>(logBufSize) : 0u};
+
+    auto sourceiter = sourcesSpan.begin();
+    auto typeiter = typesSpan.begin();
+    auto iditer = idsSpan.begin();
+    auto severityiter = severitiesSpan.begin();
+    auto lengthiter = lengthsSpan.begin();
+    auto logiter = logSpan.begin();
+
+    auto debuglock = std::lock_guard{context->mDebugCbLock};
     for(ALuint i{0};i < count;++i)
     for(ALuint i{0};i < count;++i)
     {
     {
         if(context->mDebugLog.empty())
         if(context->mDebugLog.empty())
             return i;
             return i;
 
 
         auto &entry = context->mDebugLog.front();
         auto &entry = context->mDebugLog.front();
-        const size_t tocopy{entry.mMessage.size() + 1};
-        if(logOut.data() != nullptr)
+        const auto tocopy = size_t{entry.mMessage.size() + 1};
+        if(al::to_address(logiter) != nullptr)
         {
         {
-            if(logOut.size() < tocopy)
+            if(static_cast<size_t>(std::distance(logiter, logSpan.end())) < tocopy)
                 return i;
                 return i;
-            auto oiter = std::copy(entry.mMessage.cbegin(), entry.mMessage.cend(), logOut.begin());
-            *oiter = '\0';
-            logOut = {oiter+1, logOut.end()};
+            logiter = std::copy(entry.mMessage.cbegin(), entry.mMessage.cend(), logiter);
+            *(logiter++) = '\0';
         }
         }
 
 
-        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>();
-        }
+        if(al::to_address(sourceiter) != nullptr)
+            *(sourceiter++) = GetDebugSourceEnum(entry.mSource);
+        if(al::to_address(typeiter) != nullptr)
+            *(typeiter++) = GetDebugTypeEnum(entry.mType);
+        if(al::to_address(iditer) != nullptr)
+            *(iditer++) = entry.mId;
+        if(al::to_address(severityiter) != nullptr)
+            *(severityiter++) = GetDebugSeverityEnum(entry.mSeverity);
+        if(al::to_address(lengthiter) != nullptr)
+            *(lengthiter++) = static_cast<ALsizei>(tocopy);
 
 
         context->mDebugLog.pop_front();
         context->mDebugLog.pop_front();
     }
     }
 
 
     return count;
     return count;
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+    return 0;
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
     return 0;
     return 0;
 }
 }
 
 
@@ -526,29 +536,30 @@ FORCE_ALIGN void AL_APIENTRY alObjectLabelDirectEXT(ALCcontext *context, ALenum
     ALuint name, ALsizei length, const ALchar *label) noexcept
     ALuint name, ALsizei length, const ALchar *label) noexcept
 try {
 try {
     if(!label && length != 0)
     if(!label && length != 0)
-        throw al::context_error{AL_INVALID_VALUE, "Null label pointer"};
+        context->throw_error(AL_INVALID_VALUE, "Null label pointer");
 
 
     auto objname = (length < 0) ? std::string_view{label}
     auto objname = (length < 0) ? std::string_view{label}
         : std::string_view{label, static_cast<uint>(length)};
         : std::string_view{label, static_cast<uint>(length)};
     if(objname.size() >= MaxObjectLabelLength)
     if(objname.size() >= MaxObjectLabelLength)
-        throw al::context_error{AL_INVALID_VALUE, "Object label length too long (%zu >= %d)",
-            objname.size(), MaxObjectLabelLength};
+        context->throw_error(AL_INVALID_VALUE, "Object label length too long ({} >= {})",
+            objname.size(), MaxObjectLabelLength);
 
 
-    if(identifier == AL_SOURCE_EXT)
-        return ALsource::SetName(context, name, objname);
-    if(identifier == AL_BUFFER)
-        return ALbuffer::SetName(context, name, objname);
-    if(identifier == AL_FILTER_EXT)
-        return ALfilter::SetName(context, name, objname);
-    if(identifier == AL_EFFECT_EXT)
-        return ALeffect::SetName(context, name, objname);
-    if(identifier == AL_AUXILIARY_EFFECT_SLOT_EXT)
-        return ALeffectslot::SetName(context, name, objname);
+    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};
+    context->throw_error(AL_INVALID_ENUM, "Invalid name identifier {:#04x}",
+        as_unsigned(identifier));
+}
+catch(al::base_exception&) {
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 FORCE_ALIGN DECL_FUNCEXT5(void, alGetObjectLabel,EXT, ALenum,identifier, ALuint,name, ALsizei,bufSize, ALsizei*,length, ALchar*,label)
 FORCE_ALIGN DECL_FUNCEXT5(void, alGetObjectLabel,EXT, ALenum,identifier, ALuint,name, ALsizei,bufSize, ALsizei*,length, ALchar*,label)
@@ -556,12 +567,12 @@ FORCE_ALIGN void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALen
     ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) noexcept
     ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) noexcept
 try {
 try {
     if(bufSize < 0)
     if(bufSize < 0)
-        throw al::context_error{AL_INVALID_VALUE, "Negative label bufSize"};
+        context->throw_error(AL_INVALID_VALUE, "Negative label bufSize");
 
 
     if(!label && !length)
     if(!label && !length)
-        throw al::context_error{AL_INVALID_VALUE, "Null length and label"};
+        context->throw_error(AL_INVALID_VALUE, "Null length and label");
     if(label && bufSize == 0)
     if(label && bufSize == 0)
-        throw al::context_error{AL_INVALID_VALUE, "Zero label bufSize"};
+        context->throw_error(AL_INVALID_VALUE, "Zero label bufSize");
 
 
     const auto labelOut = al::span{label, label ? static_cast<ALuint>(bufSize) : 0u};
     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)
     auto copy_name = [name,length,labelOut](std::unordered_map<ALuint,std::string> &names)
@@ -591,20 +602,20 @@ try {
     }
     }
     else if(identifier == AL_BUFFER)
     else if(identifier == AL_BUFFER)
     {
     {
-        ALCdevice *device{context->mALDevice.get()};
-        std::lock_guard buflock{device->BufferLock};
+        auto *device = context->mALDevice.get();
+        auto buflock = std::lock_guard{device->BufferLock};
         copy_name(device->mBufferNames);
         copy_name(device->mBufferNames);
     }
     }
     else if(identifier == AL_FILTER_EXT)
     else if(identifier == AL_FILTER_EXT)
     {
     {
-        ALCdevice *device{context->mALDevice.get()};
-        std::lock_guard filterlock{device->FilterLock};
+        auto *device = context->mALDevice.get();
+        auto buflock = std::lock_guard{device->FilterLock};
         copy_name(device->mFilterNames);
         copy_name(device->mFilterNames);
     }
     }
     else if(identifier == AL_EFFECT_EXT)
     else if(identifier == AL_EFFECT_EXT)
     {
     {
-        ALCdevice *device{context->mALDevice.get()};
-        std::lock_guard effectlock{device->EffectLock};
+        auto *device = context->mALDevice.get();
+        auto buflock = std::lock_guard{device->EffectLock};
         copy_name(device->mEffectNames);
         copy_name(device->mEffectNames);
     }
     }
     else if(identifier == AL_AUXILIARY_EFFECT_SLOT_EXT)
     else if(identifier == AL_AUXILIARY_EFFECT_SLOT_EXT)
@@ -613,8 +624,11 @@ try {
         copy_name(context->mEffectSlotNames);
         copy_name(context->mEffectSlotNames);
     }
     }
     else
     else
-        throw al::context_error{AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier};
+        context->throw_error(AL_INVALID_ENUM, "Invalid name identifier {:#04x}",
+            as_unsigned(identifier));
+}
+catch(al::base_exception&) {
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }

+ 49 - 47
libs/openal-soft/al/eax/api.h

@@ -21,6 +21,8 @@
 
 
 #include "AL/al.h"
 #include "AL/al.h"
 
 
+#include "opthelpers.h"
+
 
 
 #ifndef _WIN32
 #ifndef _WIN32
 using GUID = struct _GUID { /* NOLINT(*-reserved-identifier) */
 using GUID = struct _GUID { /* NOLINT(*-reserved-identifier) */
@@ -37,14 +39,16 @@ inline bool operator!=(const GUID& lhs, const GUID& rhs) noexcept
 { return !(lhs == rhs); }
 { return !(lhs == rhs); }
 #endif // _WIN32
 #endif // _WIN32
 
 
+/* TODO: This seems to create very inefficient comparisons. C++20 should allow
+ * creating default comparison operators, avoiding the need for this.
+ */
 #define DECL_EQOP(T, ...) \
 #define DECL_EQOP(T, ...) \
 [[nodiscard]] auto get_members() const noexcept { return std::forward_as_tuple(__VA_ARGS__); } \
 [[nodiscard]] auto get_members() const noexcept { return std::forward_as_tuple(__VA_ARGS__); } \
 [[nodiscard]] friend bool operator==(const T &lhs, const T &rhs) noexcept \
 [[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);  }
+{ 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;
+DECL_HIDDEN extern const GUID DSPROPSETID_EAX_ReverbProperties;
 
 
 enum DSPROPERTY_EAX_REVERBPROPERTY : unsigned int {
 enum DSPROPERTY_EAX_REVERBPROPERTY : unsigned int {
     DSPROPERTY_EAX_ALL,
     DSPROPERTY_EAX_ALL,
@@ -62,7 +66,7 @@ struct EAX_REVERBPROPERTIES {
 }; // EAX_REVERBPROPERTIES
 }; // EAX_REVERBPROPERTIES
 
 
 
 
-extern const GUID DSPROPSETID_EAXBUFFER_ReverbProperties;
+DECL_HIDDEN extern const GUID DSPROPSETID_EAXBUFFER_ReverbProperties;
 
 
 enum DSPROPERTY_EAXBUFFER_REVERBPROPERTY : unsigned int {
 enum DSPROPERTY_EAXBUFFER_REVERBPROPERTY : unsigned int {
     DSPROPERTY_EAXBUFFER_ALL,
     DSPROPERTY_EAXBUFFER_ALL,
@@ -78,7 +82,7 @@ constexpr auto EAX_BUFFER_MAXREVERBMIX = 1.0F;
 constexpr auto EAX_REVERBMIX_USEDISTANCE = -1.0F;
 constexpr auto EAX_REVERBMIX_USEDISTANCE = -1.0F;
 
 
 
 
-extern const GUID DSPROPSETID_EAX20_ListenerProperties;
+DECL_HIDDEN extern const GUID DSPROPSETID_EAX20_ListenerProperties;
 
 
 enum DSPROPERTY_EAX20_LISTENERPROPERTY : unsigned int {
 enum DSPROPERTY_EAX20_LISTENERPROPERTY : unsigned int {
     DSPROPERTY_EAX20LISTENER_NONE,
     DSPROPERTY_EAX20LISTENER_NONE,
@@ -216,7 +220,7 @@ constexpr auto EAX2LISTENER_DEFAULTFLAGS =
     EAX2LISTENERFLAGS_DECAYHFLIMIT;
     EAX2LISTENERFLAGS_DECAYHFLIMIT;
 
 
 
 
-extern const GUID DSPROPSETID_EAX20_BufferProperties;
+DECL_HIDDEN extern const GUID DSPROPSETID_EAX20_BufferProperties;
 
 
 enum DSPROPERTY_EAX20_BUFFERPROPERTY : unsigned int {
 enum DSPROPERTY_EAX20_BUFFERPROPERTY : unsigned int {
     DSPROPERTY_EAX20BUFFER_NONE,
     DSPROPERTY_EAX20BUFFER_NONE,
@@ -252,9 +256,9 @@ struct EAX20BUFFERPROPERTIES {
     unsigned long dwFlags; // modifies the behavior of properties
     unsigned long dwFlags; // modifies the behavior of properties
 }; // EAX20BUFFERPROPERTIES
 }; // EAX20BUFFERPROPERTIES
 
 
-extern const GUID DSPROPSETID_EAX30_ListenerProperties;
+DECL_HIDDEN extern const GUID DSPROPSETID_EAX30_ListenerProperties;
 
 
-extern const GUID DSPROPSETID_EAX30_BufferProperties;
+DECL_HIDDEN extern const GUID DSPROPSETID_EAX30_BufferProperties;
 
 
 
 
 constexpr auto EAX_MAX_FXSLOTS = 4;
 constexpr auto EAX_MAX_FXSLOTS = 4;
@@ -272,31 +276,29 @@ constexpr auto EAXERR_INCOMPATIBLE_SOURCE_TYPE = -5L;
 constexpr auto EAXERR_INCOMPATIBLE_EAX_VERSION = -6L;
 constexpr auto EAXERR_INCOMPATIBLE_EAX_VERSION = -6L;
 
 
 
 
-extern const GUID EAX_NULL_GUID;
+DECL_HIDDEN extern const GUID EAX_NULL_GUID;
 
 
-extern const GUID EAX_PrimaryFXSlotID;
+DECL_HIDDEN extern const GUID EAX_PrimaryFXSlotID;
 
 
 
 
 struct EAXVECTOR {
 struct EAXVECTOR {
     float x;
     float x;
     float y;
     float y;
     float z;
     float z;
-    [[nodiscard]]
-    auto get_members() const noexcept { return std::forward_as_tuple(x, y, z); }
-}; // EAXVECTOR
+};
 
 
 [[nodiscard]]
 [[nodiscard]]
 inline bool operator==(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept
 inline bool operator==(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept
-{ return lhs.get_members() == rhs.get_members(); }
+{ return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z; }
 
 
 [[nodiscard]]
 [[nodiscard]]
 inline bool operator!=(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept
 inline bool operator!=(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept
 { return !(lhs == rhs); }
 { return !(lhs == rhs); }
 
 
 
 
-extern const GUID EAXPROPERTYID_EAX40_Context;
+DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX40_Context;
 
 
-extern const GUID EAXPROPERTYID_EAX50_Context;
+DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX50_Context;
 
 
 // EAX50
 // EAX50
 constexpr auto HEADPHONES = 0UL;
 constexpr auto HEADPHONES = 0UL;
@@ -372,17 +374,17 @@ constexpr auto EAXCONTEXT_DEFAULTMACROFXFACTOR = 0.0F;
 
 
 constexpr auto EAXCONTEXT_DEFAULTLASTERROR = EAX_OK;
 constexpr auto EAXCONTEXT_DEFAULTLASTERROR = EAX_OK;
 
 
-extern const GUID EAXPROPERTYID_EAX40_FXSlot0;
-extern const GUID EAXPROPERTYID_EAX50_FXSlot0;
-extern const GUID EAXPROPERTYID_EAX40_FXSlot1;
-extern const GUID EAXPROPERTYID_EAX50_FXSlot1;
-extern const GUID EAXPROPERTYID_EAX40_FXSlot2;
-extern const GUID EAXPROPERTYID_EAX50_FXSlot2;
-extern const GUID EAXPROPERTYID_EAX40_FXSlot3;
-extern const GUID EAXPROPERTYID_EAX50_FXSlot3;
+DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX40_FXSlot0;
+DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX50_FXSlot0;
+DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX40_FXSlot1;
+DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX50_FXSlot1;
+DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX40_FXSlot2;
+DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX50_FXSlot2;
+DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX40_FXSlot3;
+DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX50_FXSlot3;
 
 
-extern const GUID EAX40CONTEXT_DEFAULTPRIMARYFXSLOTID;
-extern const GUID EAX50CONTEXT_DEFAULTPRIMARYFXSLOTID;
+DECL_HIDDEN extern const GUID EAX40CONTEXT_DEFAULTPRIMARYFXSLOTID;
+DECL_HIDDEN extern const GUID EAX50CONTEXT_DEFAULTPRIMARYFXSLOTID;
 
 
 enum EAXFXSLOT_PROPERTY : unsigned int {
 enum EAXFXSLOT_PROPERTY : unsigned int {
     EAXFXSLOT_PARAMETER = 0,
     EAXFXSLOT_PARAMETER = 0,
@@ -445,8 +447,8 @@ struct EAX50FXSLOTPROPERTIES : public EAX40FXSLOTPROPERTIES {
     float flOcclusionLFRatio;
     float flOcclusionLFRatio;
 }; // EAX50FXSLOTPROPERTIES
 }; // EAX50FXSLOTPROPERTIES
 
 
-extern const GUID EAXPROPERTYID_EAX40_Source;
-extern const GUID EAXPROPERTYID_EAX50_Source;
+DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX40_Source;
+DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX50_Source;
 
 
 // Source object properties
 // Source object properties
 enum EAXSOURCE_PROPERTY : unsigned int {
 enum EAXSOURCE_PROPERTY : unsigned int {
@@ -713,16 +715,16 @@ struct EAXSOURCEEXCLUSIONSENDPROPERTIES {
     float flExclusionLFRatio;
     float flExclusionLFRatio;
 }; // EAXSOURCEEXCLUSIONSENDPROPERTIES
 }; // EAXSOURCEEXCLUSIONSENDPROPERTIES
 
 
-extern const EAX40ACTIVEFXSLOTS EAX40SOURCE_DEFAULTACTIVEFXSLOTID;
+DECL_HIDDEN extern const EAX40ACTIVEFXSLOTS EAX40SOURCE_DEFAULTACTIVEFXSLOTID;
 
 
-extern const EAX50ACTIVEFXSLOTS EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID;
+DECL_HIDDEN extern const EAX50ACTIVEFXSLOTS EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID;
 
 
-extern const EAX50ACTIVEFXSLOTS EAX50SOURCE_2DDEFAULTACTIVEFXSLOTID;
+DECL_HIDDEN extern const EAX50ACTIVEFXSLOTS EAX50SOURCE_2DDEFAULTACTIVEFXSLOTID;
 
 
 
 
 // EAX Reverb Effect
 // EAX Reverb Effect
 
 
-extern const GUID EAX_REVERB_EFFECT;
+DECL_HIDDEN extern const GUID EAX_REVERB_EFFECT;
 
 
 // Reverb effect properties
 // Reverb effect properties
 enum EAXREVERB_PROPERTY : unsigned int {
 enum EAXREVERB_PROPERTY : unsigned int {
@@ -959,18 +961,18 @@ constexpr auto EAXREVERB_DEFAULTFLAGS =
 
 
 
 
 using Eax1ReverbPresets = std::array<EAX_REVERBPROPERTIES, EAX1_ENVIRONMENT_COUNT>;
 using Eax1ReverbPresets = std::array<EAX_REVERBPROPERTIES, EAX1_ENVIRONMENT_COUNT>;
-extern const Eax1ReverbPresets EAX1REVERB_PRESETS;
+DECL_HIDDEN extern const Eax1ReverbPresets EAX1REVERB_PRESETS;
 
 
 using Eax2ReverbPresets = std::array<EAX20LISTENERPROPERTIES, EAX2_ENVIRONMENT_COUNT>;
 using Eax2ReverbPresets = std::array<EAX20LISTENERPROPERTIES, EAX2_ENVIRONMENT_COUNT>;
-extern const Eax2ReverbPresets EAX2REVERB_PRESETS;
+DECL_HIDDEN extern const Eax2ReverbPresets EAX2REVERB_PRESETS;
 
 
 using EaxReverbPresets = std::array<EAXREVERBPROPERTIES, EAX1_ENVIRONMENT_COUNT>;
 using EaxReverbPresets = std::array<EAXREVERBPROPERTIES, EAX1_ENVIRONMENT_COUNT>;
-extern const EaxReverbPresets EAXREVERB_PRESETS;
+DECL_HIDDEN extern const EaxReverbPresets EAXREVERB_PRESETS;
 
 
 
 
 // AGC Compressor Effect
 // AGC Compressor Effect
 
 
-extern const GUID EAX_AGCCOMPRESSOR_EFFECT;
+DECL_HIDDEN extern const GUID EAX_AGCCOMPRESSOR_EFFECT;
 
 
 enum EAXAGCCOMPRESSOR_PROPERTY : unsigned int {
 enum EAXAGCCOMPRESSOR_PROPERTY : unsigned int {
     EAXAGCCOMPRESSOR_NONE,
     EAXAGCCOMPRESSOR_NONE,
@@ -991,7 +993,7 @@ constexpr auto EAXAGCCOMPRESSOR_DEFAULTONOFF = EAXAGCCOMPRESSOR_MAXONOFF;
 
 
 // Autowah Effect
 // Autowah Effect
 
 
-extern const GUID EAX_AUTOWAH_EFFECT;
+DECL_HIDDEN extern const GUID EAX_AUTOWAH_EFFECT;
 
 
 enum EAXAUTOWAH_PROPERTY : unsigned int {
 enum EAXAUTOWAH_PROPERTY : unsigned int {
     EAXAUTOWAH_NONE,
     EAXAUTOWAH_NONE,
@@ -1030,7 +1032,7 @@ constexpr auto EAXAUTOWAH_DEFAULTPEAKLEVEL = 2100L;
 
 
 // Chorus Effect
 // Chorus Effect
 
 
-extern const GUID EAX_CHORUS_EFFECT;
+DECL_HIDDEN extern const GUID EAX_CHORUS_EFFECT;
 
 
 enum EAXCHORUS_PROPERTY : unsigned int {
 enum EAXCHORUS_PROPERTY : unsigned int {
     EAXCHORUS_NONE,
     EAXCHORUS_NONE,
@@ -1086,7 +1088,7 @@ constexpr auto EAXCHORUS_DEFAULTDELAY = 0.016F;
 
 
 // Distortion Effect
 // Distortion Effect
 
 
-extern const GUID EAX_DISTORTION_EFFECT;
+DECL_HIDDEN extern const GUID EAX_DISTORTION_EFFECT;
 
 
 enum EAXDISTORTION_PROPERTY : unsigned int {
 enum EAXDISTORTION_PROPERTY : unsigned int {
     EAXDISTORTION_NONE,
     EAXDISTORTION_NONE,
@@ -1131,7 +1133,7 @@ constexpr auto EAXDISTORTION_DEFAULTEQBANDWIDTH = 3600.0F;
 
 
 // Echo Effect
 // Echo Effect
 
 
-extern const GUID EAX_ECHO_EFFECT;
+DECL_HIDDEN extern const GUID EAX_ECHO_EFFECT;
 
 
 enum EAXECHO_PROPERTY : unsigned int {
 enum EAXECHO_PROPERTY : unsigned int {
     EAXECHO_NONE,
     EAXECHO_NONE,
@@ -1176,7 +1178,7 @@ constexpr auto EAXECHO_DEFAULTSPREAD = -1.0F;
 
 
 // Equalizer Effect
 // Equalizer Effect
 
 
-extern const GUID EAX_EQUALIZER_EFFECT;
+DECL_HIDDEN extern const GUID EAX_EQUALIZER_EFFECT;
 
 
 enum EAXEQUALIZER_PROPERTY : unsigned int {
 enum EAXEQUALIZER_PROPERTY : unsigned int {
     EAXEQUALIZER_NONE,
     EAXEQUALIZER_NONE,
@@ -1252,7 +1254,7 @@ constexpr auto EAXEQUALIZER_DEFAULTHIGHCUTOFF = 6000.0F;
 
 
 // Flanger Effect
 // Flanger Effect
 
 
-extern const GUID EAX_FLANGER_EFFECT;
+DECL_HIDDEN extern const GUID EAX_FLANGER_EFFECT;
 
 
 enum EAXFLANGER_PROPERTY : unsigned int {
 enum EAXFLANGER_PROPERTY : unsigned int {
     EAXFLANGER_NONE,
     EAXFLANGER_NONE,
@@ -1308,7 +1310,7 @@ constexpr auto EAXFLANGER_DEFAULTDELAY = 0.002F;
 
 
 // Frequency Shifter Effect
 // Frequency Shifter Effect
 
 
-extern const GUID EAX_FREQUENCYSHIFTER_EFFECT;
+DECL_HIDDEN extern const GUID EAX_FREQUENCYSHIFTER_EFFECT;
 
 
 enum EAXFREQUENCYSHIFTER_PROPERTY : unsigned int {
 enum EAXFREQUENCYSHIFTER_PROPERTY : unsigned int {
     EAXFREQUENCYSHIFTER_NONE,
     EAXFREQUENCYSHIFTER_NONE,
@@ -1347,7 +1349,7 @@ constexpr auto EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION = EAXFREQUENCYSHIFTER_M
 
 
 // Vocal Morpher Effect
 // Vocal Morpher Effect
 
 
-extern const GUID EAX_VOCALMORPHER_EFFECT;
+DECL_HIDDEN extern const GUID EAX_VOCALMORPHER_EFFECT;
 
 
 enum EAXVOCALMORPHER_PROPERTY : unsigned int {
 enum EAXVOCALMORPHER_PROPERTY : unsigned int {
     EAXVOCALMORPHER_NONE,
     EAXVOCALMORPHER_NONE,
@@ -1439,7 +1441,7 @@ constexpr auto EAXVOCALMORPHER_DEFAULTRATE = 1.41F;
 
 
 // Pitch Shifter Effect
 // Pitch Shifter Effect
 
 
-extern const GUID EAX_PITCHSHIFTER_EFFECT;
+DECL_HIDDEN extern const GUID EAX_PITCHSHIFTER_EFFECT;
 
 
 enum EAXPITCHSHIFTER_PROPERTY : unsigned int {
 enum EAXPITCHSHIFTER_PROPERTY : unsigned int {
     EAXPITCHSHIFTER_NONE,
     EAXPITCHSHIFTER_NONE,
@@ -1466,7 +1468,7 @@ constexpr auto EAXPITCHSHIFTER_DEFAULTFINETUNE = 0L;
 
 
 // Ring Modulator Effect
 // Ring Modulator Effect
 
 
-extern const GUID EAX_RINGMODULATOR_EFFECT;
+DECL_HIDDEN extern const GUID EAX_RINGMODULATOR_EFFECT;
 
 
 enum EAXRINGMODULATOR_PROPERTY : unsigned int {
 enum EAXRINGMODULATOR_PROPERTY : unsigned int {
     EAXRINGMODULATOR_NONE,
     EAXRINGMODULATOR_NONE,

+ 30 - 24
libs/openal-soft/al/eax/call.cpp

@@ -15,13 +15,8 @@ public:
 
 
 } // namespace
 } // namespace
 
 
-EaxCall::EaxCall(
-    EaxCallType type,
-    const GUID& property_set_guid,
-    ALuint property_id,
-    ALuint property_source_id,
-    ALvoid* property_buffer,
-    ALuint property_size)
+EaxCall::EaxCall(EaxCallType type, const GUID &property_set_guid, ALuint property_id,
+    ALuint property_source_id, ALvoid *property_buffer, ALuint property_size)
     : mCallType{type}, mIsDeferred{(property_id & deferred_flag) != 0}
     : mCallType{type}, mIsDeferred{(property_id & deferred_flag) != 0}
     , mPropertyId{property_id & ~deferred_flag}, mPropertySourceId{property_source_id}
     , mPropertyId{property_id & ~deferred_flag}, mPropertySourceId{property_source_id}
     , mPropertyBuffer{property_buffer}, mPropertyBufferSize{property_size}
     , mPropertyBuffer{property_buffer}, mPropertyBufferSize{property_size}
@@ -145,23 +140,34 @@ EaxCall::EaxCall(
         fail("Unsupported property set id.");
         fail("Unsupported property set id.");
     }
     }
 
 
-    switch(mPropertyId)
-    {
-    case EAXCONTEXT_LASTERROR:
-    case EAXCONTEXT_SPEAKERCONFIG:
-    case EAXCONTEXT_EAXSESSION:
-    case EAXFXSLOT_NONE:
-    case EAXFXSLOT_ALLPARAMETERS:
-    case EAXFXSLOT_LOADEFFECT:
-    case EAXFXSLOT_VOLUME:
-    case EAXFXSLOT_LOCK:
-    case EAXFXSLOT_FLAGS:
-    case EAXFXSLOT_OCCLUSION:
-    case EAXFXSLOT_OCCLUSIONLFRATIO:
-        // EAX allow to set "defer" flag on immediate-only properties.
-        // If we don't clear our flag then "applyAllUpdates" in EAX context won't be called.
-        mIsDeferred = false;
-        break;
+    if(mPropertySetId == EaxCallPropertySetId::context)
+    {
+        switch(mPropertyId)
+        {
+        case EAXCONTEXT_LASTERROR:
+        case EAXCONTEXT_SPEAKERCONFIG:
+        case EAXCONTEXT_EAXSESSION:
+            // EAX allow to set "defer" flag on immediate-only properties.
+            // If we don't clear our flag then "applyAllUpdates" in EAX context won't be called.
+            mIsDeferred = false;
+            break;
+        }
+    }
+    else if(mPropertySetId == EaxCallPropertySetId::fx_slot)
+    {
+        switch(mPropertyId)
+        {
+        case EAXFXSLOT_NONE:
+        case EAXFXSLOT_ALLPARAMETERS:
+        case EAXFXSLOT_LOADEFFECT:
+        case EAXFXSLOT_VOLUME:
+        case EAXFXSLOT_LOCK:
+        case EAXFXSLOT_FLAGS:
+        case EAXFXSLOT_OCCLUSION:
+        case EAXFXSLOT_OCCLUSIONLFRATIO:
+            mIsDeferred = false;
+            break;
+        }
     }
     }
 
 
     if(!mIsDeferred)
     if(!mIsDeferred)

+ 35 - 20
libs/openal-soft/al/eax/effect.h

@@ -1,17 +1,17 @@
 #ifndef EAX_EFFECT_INCLUDED
 #ifndef EAX_EFFECT_INCLUDED
 #define EAX_EFFECT_INCLUDED
 #define EAX_EFFECT_INCLUDED
 
 
-
 #include <cassert>
 #include <cassert>
 #include <memory>
 #include <memory>
 #include <variant>
 #include <variant>
 
 
-#include "alnumeric.h"
 #include "AL/al.h"
 #include "AL/al.h"
 #include "AL/alext.h"
 #include "AL/alext.h"
 #include "core/effects/base.h"
 #include "core/effects/base.h"
 #include "call.h"
 #include "call.h"
 
 
+inline bool EaxTraceCommits{false};
+
 struct EaxEffectErrorMessages {
 struct EaxEffectErrorMessages {
     static constexpr auto unknown_property_id() noexcept { return "Unknown property id."; }
     static constexpr auto unknown_property_id() noexcept { return "Unknown property id."; }
     static constexpr auto unknown_version() noexcept { return "Unknown version."; }
     static constexpr auto unknown_version() noexcept { return "Unknown version."; }
@@ -123,10 +123,6 @@ template<typename T>
 struct EaxCommitter {
 struct EaxCommitter {
     struct Exception;
     struct Exception;
 
 
-    EaxCommitter(EaxEffectProps &eaxprops, EffectProps &alprops)
-        : mEaxProps{eaxprops}, mAlProps{alprops}
-    { }
-
     EaxEffectProps &mEaxProps;
     EaxEffectProps &mEaxProps;
     EffectProps &mAlProps;
     EffectProps &mAlProps;
 
 
@@ -141,10 +137,18 @@ struct EaxCommitter {
     [[noreturn]] static void fail(const char *message);
     [[noreturn]] static void fail(const char *message);
     [[noreturn]] static void fail_unknown_property_id()
     [[noreturn]] static void fail_unknown_property_id()
     { fail(EaxEffectErrorMessages::unknown_property_id()); }
     { fail(EaxEffectErrorMessages::unknown_property_id()); }
+
+private:
+    EaxCommitter(EaxEffectProps &eaxprops, EffectProps &alprops)
+        : mEaxProps{eaxprops}, mAlProps{alprops}
+    { }
+
+    friend T;
 };
 };
 
 
 struct EaxAutowahCommitter : public EaxCommitter<EaxAutowahCommitter> {
 struct EaxAutowahCommitter : public EaxCommitter<EaxAutowahCommitter> {
-    using EaxCommitter<EaxAutowahCommitter>::EaxCommitter;
+    template<typename ...Args>
+    explicit EaxAutowahCommitter(Args&& ...args) : EaxCommitter{std::forward<Args>(args)...} { }
 
 
     bool commit(const EAXAUTOWAHPROPERTIES &props);
     bool commit(const EAXAUTOWAHPROPERTIES &props);
 
 
@@ -153,7 +157,8 @@ struct EaxAutowahCommitter : public EaxCommitter<EaxAutowahCommitter> {
     static void Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props);
     static void Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props);
 };
 };
 struct EaxChorusCommitter : public EaxCommitter<EaxChorusCommitter> {
 struct EaxChorusCommitter : public EaxCommitter<EaxChorusCommitter> {
-    using EaxCommitter<EaxChorusCommitter>::EaxCommitter;
+    template<typename ...Args>
+    explicit EaxChorusCommitter(Args&& ...args) : EaxCommitter{std::forward<Args>(args)...} { }
 
 
     bool commit(const EAXCHORUSPROPERTIES &props);
     bool commit(const EAXCHORUSPROPERTIES &props);
 
 
@@ -162,7 +167,8 @@ struct EaxChorusCommitter : public EaxCommitter<EaxChorusCommitter> {
     static void Set(const EaxCall &call, EAXCHORUSPROPERTIES &props);
     static void Set(const EaxCall &call, EAXCHORUSPROPERTIES &props);
 };
 };
 struct EaxCompressorCommitter : public EaxCommitter<EaxCompressorCommitter> {
 struct EaxCompressorCommitter : public EaxCommitter<EaxCompressorCommitter> {
-    using EaxCommitter<EaxCompressorCommitter>::EaxCommitter;
+    template<typename ...Args>
+    explicit EaxCompressorCommitter(Args&& ...args) : EaxCommitter{std::forward<Args>(args)...} { }
 
 
     bool commit(const EAXAGCCOMPRESSORPROPERTIES &props);
     bool commit(const EAXAGCCOMPRESSORPROPERTIES &props);
 
 
@@ -171,7 +177,8 @@ struct EaxCompressorCommitter : public EaxCommitter<EaxCompressorCommitter> {
     static void Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props);
     static void Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props);
 };
 };
 struct EaxDistortionCommitter : public EaxCommitter<EaxDistortionCommitter> {
 struct EaxDistortionCommitter : public EaxCommitter<EaxDistortionCommitter> {
-    using EaxCommitter<EaxDistortionCommitter>::EaxCommitter;
+    template<typename ...Args>
+    explicit EaxDistortionCommitter(Args&& ...args) : EaxCommitter{std::forward<Args>(args)...} { }
 
 
     bool commit(const EAXDISTORTIONPROPERTIES &props);
     bool commit(const EAXDISTORTIONPROPERTIES &props);
 
 
@@ -180,7 +187,8 @@ struct EaxDistortionCommitter : public EaxCommitter<EaxDistortionCommitter> {
     static void Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props);
     static void Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props);
 };
 };
 struct EaxEchoCommitter : public EaxCommitter<EaxEchoCommitter> {
 struct EaxEchoCommitter : public EaxCommitter<EaxEchoCommitter> {
-    using EaxCommitter<EaxEchoCommitter>::EaxCommitter;
+    template<typename ...Args>
+    explicit EaxEchoCommitter(Args&& ...args) : EaxCommitter{std::forward<Args>(args)...} { }
 
 
     bool commit(const EAXECHOPROPERTIES &props);
     bool commit(const EAXECHOPROPERTIES &props);
 
 
@@ -189,7 +197,8 @@ struct EaxEchoCommitter : public EaxCommitter<EaxEchoCommitter> {
     static void Set(const EaxCall &call, EAXECHOPROPERTIES &props);
     static void Set(const EaxCall &call, EAXECHOPROPERTIES &props);
 };
 };
 struct EaxEqualizerCommitter : public EaxCommitter<EaxEqualizerCommitter> {
 struct EaxEqualizerCommitter : public EaxCommitter<EaxEqualizerCommitter> {
-    using EaxCommitter<EaxEqualizerCommitter>::EaxCommitter;
+    template<typename ...Args>
+    explicit EaxEqualizerCommitter(Args&& ...args) : EaxCommitter{std::forward<Args>(args)...} { }
 
 
     bool commit(const EAXEQUALIZERPROPERTIES &props);
     bool commit(const EAXEQUALIZERPROPERTIES &props);
 
 
@@ -198,7 +207,8 @@ struct EaxEqualizerCommitter : public EaxCommitter<EaxEqualizerCommitter> {
     static void Set(const EaxCall &call, EAXEQUALIZERPROPERTIES &props);
     static void Set(const EaxCall &call, EAXEQUALIZERPROPERTIES &props);
 };
 };
 struct EaxFlangerCommitter : public EaxCommitter<EaxFlangerCommitter> {
 struct EaxFlangerCommitter : public EaxCommitter<EaxFlangerCommitter> {
-    using EaxCommitter<EaxFlangerCommitter>::EaxCommitter;
+    template<typename ...Args>
+    explicit EaxFlangerCommitter(Args&& ...args) : EaxCommitter{std::forward<Args>(args)...} { }
 
 
     bool commit(const EAXFLANGERPROPERTIES &props);
     bool commit(const EAXFLANGERPROPERTIES &props);
 
 
@@ -207,7 +217,8 @@ struct EaxFlangerCommitter : public EaxCommitter<EaxFlangerCommitter> {
     static void Set(const EaxCall &call, EAXFLANGERPROPERTIES &props);
     static void Set(const EaxCall &call, EAXFLANGERPROPERTIES &props);
 };
 };
 struct EaxFrequencyShifterCommitter : public EaxCommitter<EaxFrequencyShifterCommitter> {
 struct EaxFrequencyShifterCommitter : public EaxCommitter<EaxFrequencyShifterCommitter> {
-    using EaxCommitter<EaxFrequencyShifterCommitter>::EaxCommitter;
+    template<typename ...Args>
+    explicit EaxFrequencyShifterCommitter(Args&& ...args) : EaxCommitter{std::forward<Args>(args)...} { }
 
 
     bool commit(const EAXFREQUENCYSHIFTERPROPERTIES &props);
     bool commit(const EAXFREQUENCYSHIFTERPROPERTIES &props);
 
 
@@ -216,7 +227,8 @@ struct EaxFrequencyShifterCommitter : public EaxCommitter<EaxFrequencyShifterCom
     static void Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props);
     static void Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props);
 };
 };
 struct EaxModulatorCommitter : public EaxCommitter<EaxModulatorCommitter> {
 struct EaxModulatorCommitter : public EaxCommitter<EaxModulatorCommitter> {
-    using EaxCommitter<EaxModulatorCommitter>::EaxCommitter;
+    template<typename ...Args>
+    explicit EaxModulatorCommitter(Args&& ...args) : EaxCommitter{std::forward<Args>(args)...} { }
 
 
     bool commit(const EAXRINGMODULATORPROPERTIES &props);
     bool commit(const EAXRINGMODULATORPROPERTIES &props);
 
 
@@ -225,7 +237,8 @@ struct EaxModulatorCommitter : public EaxCommitter<EaxModulatorCommitter> {
     static void Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props);
     static void Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props);
 };
 };
 struct EaxPitchShifterCommitter : public EaxCommitter<EaxPitchShifterCommitter> {
 struct EaxPitchShifterCommitter : public EaxCommitter<EaxPitchShifterCommitter> {
-    using EaxCommitter<EaxPitchShifterCommitter>::EaxCommitter;
+    template<typename ...Args>
+    explicit EaxPitchShifterCommitter(Args&& ...args) : EaxCommitter{std::forward<Args>(args)...} { }
 
 
     bool commit(const EAXPITCHSHIFTERPROPERTIES &props);
     bool commit(const EAXPITCHSHIFTERPROPERTIES &props);
 
 
@@ -234,7 +247,8 @@ struct EaxPitchShifterCommitter : public EaxCommitter<EaxPitchShifterCommitter>
     static void Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props);
     static void Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props);
 };
 };
 struct EaxVocalMorpherCommitter : public EaxCommitter<EaxVocalMorpherCommitter> {
 struct EaxVocalMorpherCommitter : public EaxCommitter<EaxVocalMorpherCommitter> {
-    using EaxCommitter<EaxVocalMorpherCommitter>::EaxCommitter;
+    template<typename ...Args>
+    explicit EaxVocalMorpherCommitter(Args&& ...args) : EaxCommitter{std::forward<Args>(args)...} { }
 
 
     bool commit(const EAXVOCALMORPHERPROPERTIES &props);
     bool commit(const EAXVOCALMORPHERPROPERTIES &props);
 
 
@@ -243,7 +257,8 @@ struct EaxVocalMorpherCommitter : public EaxCommitter<EaxVocalMorpherCommitter>
     static void Set(const EaxCall &call, EAXVOCALMORPHERPROPERTIES &props);
     static void Set(const EaxCall &call, EAXVOCALMORPHERPROPERTIES &props);
 };
 };
 struct EaxNullCommitter : public EaxCommitter<EaxNullCommitter> {
 struct EaxNullCommitter : public EaxCommitter<EaxNullCommitter> {
-    using EaxCommitter<EaxNullCommitter>::EaxCommitter;
+    template<typename ...Args>
+    explicit EaxNullCommitter(Args&& ...args) : EaxCommitter{std::forward<Args>(args)...} { }
 
 
     bool commit(const std::monostate &props);
     bool commit(const std::monostate &props);
 
 
@@ -279,7 +294,7 @@ public:
     ~EaxEffect() = default;
     ~EaxEffect() = default;
 
 
     ALenum al_effect_type_{AL_EFFECT_NULL};
     ALenum al_effect_type_{AL_EFFECT_NULL};
-    EffectProps al_effect_props_{};
+    EffectProps al_effect_props_;
 
 
     using Props1 = EAX_REVERBPROPERTIES;
     using Props1 = EAX_REVERBPROPERTIES;
     using Props2 = EAX20LISTENERPROPERTIES;
     using Props2 = EAX20LISTENERPROPERTIES;
@@ -308,7 +323,7 @@ public:
 
 
     int version_{};
     int version_{};
     bool changed_{};
     bool changed_{};
-    Props4 props_{};
+    Props4 props_;
     State1 state1_{};
     State1 state1_{};
     State2 state2_{};
     State2 state2_{};
     State3 state3_{};
     State3 state3_{};

+ 7 - 2
libs/openal-soft/al/eax/exception.h

@@ -10,9 +10,14 @@ class EaxException : public std::runtime_error {
     static std::string make_message(std::string_view context, std::string_view message);
     static std::string make_message(std::string_view context, std::string_view message);
 
 
 public:
 public:
+    EaxException() = delete;
+    EaxException(const EaxException&) = default;
+    EaxException(EaxException&&) = default;
     EaxException(std::string_view context, std::string_view message);
     EaxException(std::string_view context, std::string_view message);
     ~EaxException() override;
     ~EaxException() override;
-}; // EaxException
 
 
+    auto operator=(const EaxException&) -> EaxException& = default;
+    auto operator=(EaxException&&) -> EaxException& = default;
+};
 
 
-#endif // !EAX_EXCEPTION_INCLUDED
+#endif /* EAX_EXCEPTION_INCLUDED */

+ 1 - 4
libs/openal-soft/al/eax/fx_slots.h

@@ -6,13 +6,10 @@
 
 
 #include "al/auxeffectslot.h"
 #include "al/auxeffectslot.h"
 
 
-#include "api.h"
-#include "call.h"
 #include "fx_slot_index.h"
 #include "fx_slot_index.h"
 
 
 
 
-class EaxFxSlots
-{
+class EaxFxSlots {
 public:
 public:
     void initialize(ALCcontext& al_context);
     void initialize(ALCcontext& al_context);
 
 

+ 2 - 3
libs/openal-soft/al/eax/utils.cpp

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

+ 3 - 69
libs/openal-soft/al/eax/utils.h

@@ -1,15 +1,11 @@
 #ifndef EAX_UTILS_INCLUDED
 #ifndef EAX_UTILS_INCLUDED
 #define EAX_UTILS_INCLUDED
 #define EAX_UTILS_INCLUDED
 
 
-#include <algorithm>
-#include <cstdint>
-#include <string>
 #include <string_view>
 #include <string_view>
-#include <type_traits>
 
 
+#include "fmt/core.h"
 #include "opthelpers.h"
 #include "opthelpers.h"
 
 
-using EaxDirtyFlags = unsigned int;
 
 
 struct EaxAlLowPassParam {
 struct EaxAlLowPassParam {
     float gain;
     float gain;
@@ -25,71 +21,9 @@ void eax_validate_range(std::string_view value_name, const TValue& value, const
     if(value >= min_value && value <= max_value) LIKELY
     if(value >= min_value && value <= max_value) LIKELY
         return;
         return;
 
 
-    const auto message =
-        std::string{value_name} +
-        " out of range (value: " +
-        std::to_string(value) + "; min: " +
-        std::to_string(min_value) + "; max: " +
-        std::to_string(max_value) + ").";
-
+    const auto message = fmt::format("{} out of range (value: {}; min: {}; max: {}).", value_name,
+        value, min_value, max_value);
     throw TException{message.c_str()};
     throw TException{message.c_str()};
 }
 }
 
 
-namespace detail {
-
-template<typename T>
-struct EaxIsBitFieldStruct {
-private:
-    using yes = std::true_type;
-    using no = std::false_type;
-
-    template<typename U>
-    static auto test(int) -> decltype(std::declval<typename U::EaxIsBitFieldStruct>(), yes{});
-
-    template<typename>
-    static no test(...);
-
-public:
-    static constexpr auto value = std::is_same<decltype(test<T>(0)), yes>::value;
-};
-
-template<typename T, typename TValue>
-inline bool eax_bit_fields_are_equal(const T& lhs, const T& rhs) noexcept
-{
-    static_assert(sizeof(T) == sizeof(TValue), "Invalid type size.");
-    return reinterpret_cast<const TValue&>(lhs) == reinterpret_cast<const TValue&>(rhs);
-}
-
-} // namespace detail
-
-template<
-    typename T,
-    std::enable_if_t<detail::EaxIsBitFieldStruct<T>::value, int> = 0
->
-inline bool operator==(const T& lhs, const T& rhs) noexcept
-{
-    using Value = std::conditional_t<
-        sizeof(T) == 1,
-        std::uint8_t,
-        std::conditional_t<
-            sizeof(T) == 2,
-            std::uint16_t,
-            std::conditional_t<
-                sizeof(T) == 4,
-                std::uint32_t,
-                void>>>;
-
-    static_assert(!std::is_same<Value, void>::value, "Unsupported type.");
-    return detail::eax_bit_fields_are_equal<T, Value>(lhs, rhs);
-}
-
-template<
-    typename T,
-    std::enable_if_t<detail::EaxIsBitFieldStruct<T>::value, int> = 0
->
-inline bool operator!=(const T& lhs, const T& rhs) noexcept
-{
-    return !(lhs == rhs);
-}
-
 #endif // !EAX_UTILS_INCLUDED
 #endif // !EAX_UTILS_INCLUDED

+ 0 - 5
libs/openal-soft/al/eax/x_ram.h

@@ -24,12 +24,7 @@ constexpr auto AL_STORAGE_AUTOMATIC_NAME = "AL_STORAGE_AUTOMATIC";
 constexpr auto AL_STORAGE_HARDWARE_NAME = "AL_STORAGE_HARDWARE";
 constexpr auto AL_STORAGE_HARDWARE_NAME = "AL_STORAGE_HARDWARE";
 constexpr auto AL_STORAGE_ACCESSIBLE_NAME = "AL_STORAGE_ACCESSIBLE";
 constexpr auto AL_STORAGE_ACCESSIBLE_NAME = "AL_STORAGE_ACCESSIBLE";
 
 
-/* NOLINTBEGIN(readability-inconsistent-declaration-parameter-name)
- * These functions are defined using macros to forward them in a generic way to
- * implementation functions, which gives the parameters generic names.
- */
 ALboolean AL_APIENTRY EAXSetBufferMode(ALsizei n, const ALuint *buffers, ALint value) noexcept;
 ALboolean AL_APIENTRY EAXSetBufferMode(ALsizei n, const ALuint *buffers, ALint value) noexcept;
 ALenum AL_APIENTRY EAXGetBufferMode(ALuint buffer, ALint *pReserved) noexcept;
 ALenum AL_APIENTRY EAXGetBufferMode(ALuint buffer, ALint *pReserved) noexcept;
-/* NOLINTEND(readability-inconsistent-declaration-parameter-name) */
 
 
 #endif // !EAX_X_RAM_INCLUDED
 #endif // !EAX_X_RAM_INCLUDED

+ 150 - 133
libs/openal-soft/al/effect.cpp

@@ -51,9 +51,9 @@
 #include "alnumeric.h"
 #include "alnumeric.h"
 #include "alspan.h"
 #include "alspan.h"
 #include "alstring.h"
 #include "alstring.h"
+#include "core/except.h"
 #include "core/logging.h"
 #include "core/logging.h"
 #include "direct_defs.h"
 #include "direct_defs.h"
-#include "error.h"
 #include "intrusive_ptr.h"
 #include "intrusive_ptr.h"
 #include "opthelpers.h"
 #include "opthelpers.h"
 
 
@@ -109,11 +109,38 @@ constexpr auto GetDefaultProps(ALenum type) noexcept -> const EffectProps&
 
 
 void InitEffectParams(ALeffect *effect, ALenum type) noexcept
 void InitEffectParams(ALeffect *effect, ALenum type) noexcept
 {
 {
+    switch(type)
+    {
+    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->Props = GetDefaultProps(type);
     effect->type = type;
     effect->type = type;
 }
 }
 
 
-auto EnsureEffects(ALCdevice *device, size_t needed) noexcept -> bool
+[[nodiscard]]
+auto EnsureEffects(al::Device *device, size_t needed) noexcept -> bool
 try {
 try {
     size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), 0_uz,
     size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), 0_uz,
         [](size_t cur, const EffectSubList &sublist) noexcept -> size_t
         [](size_t cur, const EffectSubList &sublist) noexcept -> size_t
@@ -136,7 +163,8 @@ catch(...) {
     return false;
     return false;
 }
 }
 
 
-ALeffect *AllocEffect(ALCdevice *device) noexcept
+[[nodiscard]]
+auto AllocEffect(al::Device *device) noexcept -> ALeffect*
 {
 {
     auto sublist = std::find_if(device->EffectList.begin(), device->EffectList.end(),
     auto sublist = std::find_if(device->EffectList.begin(), device->EffectList.end(),
         [](const EffectSubList &entry) noexcept -> bool
         [](const EffectSubList &entry) noexcept -> bool
@@ -156,7 +184,7 @@ ALeffect *AllocEffect(ALCdevice *device) noexcept
     return effect;
     return effect;
 }
 }
 
 
-void FreeEffect(ALCdevice *device, ALeffect *effect)
+void FreeEffect(al::Device *device, ALeffect *effect)
 {
 {
     device->mEffectNames.erase(effect->id);
     device->mEffectNames.erase(effect->id);
 
 
@@ -169,7 +197,8 @@ void FreeEffect(ALCdevice *device, ALeffect *effect)
     device->EffectList[lidx].FreeMask |= 1_u64 << slidx;
     device->EffectList[lidx].FreeMask |= 1_u64 << slidx;
 }
 }
 
 
-inline auto LookupEffect(ALCdevice *device, ALuint id) noexcept -> ALeffect*
+[[nodiscard]] inline
+auto LookupEffect(al::Device *device, ALuint id) noexcept -> ALeffect*
 {
 {
     const size_t lidx{(id-1) >> 6};
     const size_t lidx{(id-1) >> 6};
     const ALuint slidx{(id-1) & 0x3f};
     const ALuint slidx{(id-1) & 0x3f};
@@ -188,21 +217,23 @@ AL_API DECL_FUNC2(void, alGenEffects, ALsizei,n, ALuint*,effects)
 FORCE_ALIGN void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) noexcept
 FORCE_ALIGN void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) noexcept
 try {
 try {
     if(n < 0)
     if(n < 0)
-        throw al::context_error{AL_INVALID_VALUE, "Generating %d effects", n};
+        context->throw_error(AL_INVALID_VALUE, "Generating {} effects", n);
     if(n <= 0) UNLIKELY return;
     if(n <= 0) UNLIKELY return;
 
 
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> effectlock{device->EffectLock};
+    auto *device = context->mALDevice.get();
+    auto effectlock = std::lock_guard{device->EffectLock};
 
 
     const al::span eids{effects, static_cast<ALuint>(n)};
     const al::span eids{effects, static_cast<ALuint>(n)};
     if(!EnsureEffects(device, eids.size()))
     if(!EnsureEffects(device, eids.size()))
-        throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d effect%s", n,
-            (n == 1) ? "" : "s"};
+        context->throw_error(AL_OUT_OF_MEMORY, "Failed to allocate {} effect{}", n,
+            (n==1) ? "" : "s");
 
 
     std::generate(eids.begin(), eids.end(), [device]{ return AllocEffect(device)->id; });
     std::generate(eids.begin(), eids.end(), [device]{ return AllocEffect(device)->id; });
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC2(void, alDeleteEffects, ALsizei,n, const ALuint*,effects)
 AL_API DECL_FUNC2(void, alDeleteEffects, ALsizei,n, const ALuint*,effects)
@@ -210,11 +241,11 @@ FORCE_ALIGN void AL_APIENTRY alDeleteEffectsDirect(ALCcontext *context, ALsizei
     const ALuint *effects) noexcept
     const ALuint *effects) noexcept
 try {
 try {
     if(n < 0)
     if(n < 0)
-        throw al::context_error{AL_INVALID_VALUE, "Deleting %d effects", n};
+        context->throw_error(AL_INVALID_VALUE, "Deleting {} effects", n);
     if(n <= 0) UNLIKELY return;
     if(n <= 0) UNLIKELY return;
 
 
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> effectlock{device->EffectLock};
+    auto *device = context->mALDevice.get();
+    auto effectlock = std::lock_guard{device->EffectLock};
 
 
     /* First try to find any effects that are invalid. */
     /* First try to find any effects that are invalid. */
     auto validate_effect = [device](const ALuint eid) -> bool
     auto validate_effect = [device](const ALuint eid) -> bool
@@ -223,7 +254,7 @@ try {
     const al::span eids{effects, static_cast<ALuint>(n)};
     const al::span eids{effects, static_cast<ALuint>(n)};
     auto inveffect = std::find_if_not(eids.begin(), eids.end(), validate_effect);
     auto inveffect = std::find_if_not(eids.begin(), eids.end(), validate_effect);
     if(inveffect != eids.end())
     if(inveffect != eids.end())
-        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", *inveffect};
+        context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", *inveffect);
 
 
     /* All good. Delete non-0 effect IDs. */
     /* All good. Delete non-0 effect IDs. */
     auto delete_effect = [device](ALuint eid) -> void
     auto delete_effect = [device](ALuint eid) -> void
@@ -233,15 +264,17 @@ try {
     };
     };
     std::for_each(eids.begin(), eids.end(), delete_effect);
     std::for_each(eids.begin(), eids.end(), delete_effect);
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC1(ALboolean, alIsEffect, ALuint,effect)
 AL_API DECL_FUNC1(ALboolean, alIsEffect, ALuint,effect)
 FORCE_ALIGN ALboolean AL_APIENTRY alIsEffectDirect(ALCcontext *context, ALuint effect) noexcept
 FORCE_ALIGN ALboolean AL_APIENTRY alIsEffectDirect(ALCcontext *context, ALuint effect) noexcept
 {
 {
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> effectlock{device->EffectLock};
+    auto *device = context->mALDevice.get();
+    auto effectlock = std::lock_guard{device->EffectLock};
     if(!effect || LookupEffect(device, effect))
     if(!effect || LookupEffect(device, effect))
         return AL_TRUE;
         return AL_TRUE;
     return AL_FALSE;
     return AL_FALSE;
@@ -251,12 +284,12 @@ AL_API DECL_FUNC3(void, alEffecti, ALuint,effect, ALenum,param, ALint,value)
 FORCE_ALIGN void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
 FORCE_ALIGN void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
     ALint value) noexcept
     ALint value) noexcept
 try {
 try {
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> effectlock{device->EffectLock};
+    auto *device = context->mALDevice.get();
+    auto effectlock = std::lock_guard{device->EffectLock};
 
 
     ALeffect *aleffect{LookupEffect(device, effect)};
     ALeffect *aleffect{LookupEffect(device, effect)};
     if(!aleffect)
     if(!aleffect)
-        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
+        context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect);
 
 
     switch(param)
     switch(param)
     {
     {
@@ -266,8 +299,8 @@ try {
             auto check_effect = [value](const EffectList &item) -> bool
             auto check_effect = [value](const EffectList &item) -> bool
             { return value == item.val && !DisabledEffects.test(item.type); };
             { return value == item.val && !DisabledEffects.test(item.type); };
             if(!std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect))
             if(!std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect))
-                throw al::context_error{AL_INVALID_VALUE, "Effect type 0x%04x not supported",
-                    value};
+                context->throw_error(AL_INVALID_VALUE, "Effect type {:#04x} not supported",
+                    as_unsigned(value));
         }
         }
 
 
         InitEffectParams(aleffect, value);
         InitEffectParams(aleffect, value);
@@ -275,19 +308,17 @@ try {
     }
     }
 
 
     /* Call the appropriate handler */
     /* Call the appropriate handler */
-    std::visit([aleffect,param,value](auto &arg)
+    std::visit([context,aleffect,param,value](auto &arg)
     {
     {
         using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
         using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
-        if constexpr(std::is_same_v<Type,ReverbProps>)
-        {
-            if(aleffect->type == AL_EFFECT_REVERB)
-                return EffectHandler::StdReverbSetParami(arg, param, value);
-        }
-        return EffectHandler::SetParami(arg, param, value);
-    }, aleffect->Props);
+        using PropType = typename Type::prop_type;
+        return arg.SetParami(context, std::get<PropType>(aleffect->Props), param, value);
+    }, aleffect->PropsVariant);
+}
+catch(al::base_exception&) {
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC3(void, alEffectiv, ALuint,effect, ALenum,param, const ALint*,values)
 AL_API DECL_FUNC3(void, alEffectiv, ALuint,effect, ALenum,param, const ALint*,values)
@@ -301,93 +332,87 @@ try {
         return;
         return;
     }
     }
 
 
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> effectlock{device->EffectLock};
+    auto *device = context->mALDevice.get();
+    auto effectlock = std::lock_guard{device->EffectLock};
 
 
     ALeffect *aleffect{LookupEffect(device, effect)};
     ALeffect *aleffect{LookupEffect(device, effect)};
     if(!aleffect)
     if(!aleffect)
-        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
+        context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect);
 
 
     /* Call the appropriate handler */
     /* Call the appropriate handler */
-    std::visit([aleffect,param,values](auto &arg)
+    std::visit([context,aleffect,param,values](auto &arg)
     {
     {
         using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
         using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
-        if constexpr(std::is_same_v<Type,ReverbProps>)
-        {
-            if(aleffect->type == AL_EFFECT_REVERB)
-                return EffectHandler::StdReverbSetParamiv(arg, param, values);
-        }
-        return EffectHandler::SetParamiv(arg, param, values);
-    }, aleffect->Props);
+        using PropType = typename Type::prop_type;
+        return arg.SetParamiv(context, std::get<PropType>(aleffect->Props), param, values);
+    }, aleffect->PropsVariant);
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC3(void, alEffectf, ALuint,effect, ALenum,param, ALfloat,value)
 AL_API DECL_FUNC3(void, alEffectf, ALuint,effect, ALenum,param, ALfloat,value)
 FORCE_ALIGN void AL_APIENTRY alEffectfDirect(ALCcontext *context, ALuint effect, ALenum param,
 FORCE_ALIGN void AL_APIENTRY alEffectfDirect(ALCcontext *context, ALuint effect, ALenum param,
     ALfloat value) noexcept
     ALfloat value) noexcept
 try {
 try {
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> effectlock{device->EffectLock};
+    auto *device = context->mALDevice.get();
+    auto effectlock = std::lock_guard{device->EffectLock};
 
 
     ALeffect *aleffect{LookupEffect(device, effect)};
     ALeffect *aleffect{LookupEffect(device, effect)};
-    if(!aleffect) UNLIKELY
-        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
+    if(!aleffect)
+        context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect);
 
 
     /* Call the appropriate handler */
     /* Call the appropriate handler */
-    std::visit([aleffect,param,value](auto &arg)
+    std::visit([context,aleffect,param,value](auto &arg)
     {
     {
         using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
         using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
-        if constexpr(std::is_same_v<Type,ReverbProps>)
-        {
-            if(aleffect->type == AL_EFFECT_REVERB)
-                return EffectHandler::StdReverbSetParamf(arg, param, value);
-        }
-        return EffectHandler::SetParamf(arg, param, value);
-    }, aleffect->Props);
+        using PropType = typename Type::prop_type;
+        return arg.SetParamf(context, std::get<PropType>(aleffect->Props), param, value);
+    }, aleffect->PropsVariant);
+}
+catch(al::base_exception&) {
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC3(void, alEffectfv, ALuint,effect, ALenum,param, const ALfloat*,values)
 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,
 FORCE_ALIGN void AL_APIENTRY alEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param,
     const ALfloat *values) noexcept
     const ALfloat *values) noexcept
 try {
 try {
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> effectlock{device->EffectLock};
+    auto *device = context->mALDevice.get();
+    auto effectlock = std::lock_guard{device->EffectLock};
 
 
     ALeffect *aleffect{LookupEffect(device, effect)};
     ALeffect *aleffect{LookupEffect(device, effect)};
     if(!aleffect)
     if(!aleffect)
-        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
+        context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect);
 
 
     /* Call the appropriate handler */
     /* Call the appropriate handler */
-    std::visit([aleffect,param,values](auto &arg)
+    std::visit([context,aleffect,param,values](auto &arg)
     {
     {
         using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
         using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
-        if constexpr(std::is_same_v<Type,ReverbProps>)
-        {
-            if(aleffect->type == AL_EFFECT_REVERB)
-                return EffectHandler::StdReverbSetParamfv(arg, param, values);
-        }
-        return EffectHandler::SetParamfv(arg, param, values);
-    }, aleffect->Props);
+        using PropType = typename Type::prop_type;
+        return arg.SetParamfv(context, std::get<PropType>(aleffect->Props), param, values);
+    }, aleffect->PropsVariant);
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC3(void, alGetEffecti, ALuint,effect, ALenum,param, ALint*,value)
 AL_API DECL_FUNC3(void, alGetEffecti, ALuint,effect, ALenum,param, ALint*,value)
 FORCE_ALIGN void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
 FORCE_ALIGN void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
     ALint *value) noexcept
     ALint *value) noexcept
 try {
 try {
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> effectlock{device->EffectLock};
+    auto *device = context->mALDevice.get();
+    auto effectlock = std::lock_guard{device->EffectLock};
 
 
     const ALeffect *aleffect{LookupEffect(device, effect)};
     const ALeffect *aleffect{LookupEffect(device, effect)};
     if(!aleffect)
     if(!aleffect)
-        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
+        context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect);
 
 
     switch(param)
     switch(param)
     {
     {
@@ -397,19 +422,17 @@ try {
     }
     }
 
 
     /* Call the appropriate handler */
     /* Call the appropriate handler */
-    std::visit([aleffect,param,value](auto &arg)
+    std::visit([context,aleffect,param,value](auto &arg)
     {
     {
         using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
         using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
-        if constexpr(std::is_same_v<Type,ReverbProps>)
-        {
-            if(aleffect->type == AL_EFFECT_REVERB)
-                return EffectHandler::StdReverbGetParami(arg, param, value);
-        }
-        return EffectHandler::GetParami(arg, param, value);
-    }, aleffect->Props);
+        using PropType = typename Type::prop_type;
+        return arg.GetParami(context, std::get<PropType>(aleffect->Props), param, value);
+    }, aleffect->PropsVariant);
+}
+catch(al::base_exception&) {
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC3(void, alGetEffectiv, ALuint,effect, ALenum,param, ALint*,values)
 AL_API DECL_FUNC3(void, alGetEffectiv, ALuint,effect, ALenum,param, ALint*,values)
@@ -423,81 +446,75 @@ try {
         return;
         return;
     }
     }
 
 
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> effectlock{device->EffectLock};
+    auto *device = context->mALDevice.get();
+    auto effectlock = std::lock_guard{device->EffectLock};
 
 
     const ALeffect *aleffect{LookupEffect(device, effect)};
     const ALeffect *aleffect{LookupEffect(device, effect)};
     if(!aleffect)
     if(!aleffect)
-        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
+        context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect);
 
 
     /* Call the appropriate handler */
     /* Call the appropriate handler */
-    std::visit([aleffect,param,values](auto &arg)
+    std::visit([context,aleffect,param,values](auto &arg)
     {
     {
         using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
         using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
-        if constexpr(std::is_same_v<Type,ReverbProps>)
-        {
-            if(aleffect->type == AL_EFFECT_REVERB)
-                return EffectHandler::StdReverbGetParamiv(arg, param, values);
-        }
-        return EffectHandler::GetParamiv(arg, param, values);
-    }, aleffect->Props);
+        using PropType = typename Type::prop_type;
+        return arg.GetParamiv(context, std::get<PropType>(aleffect->Props), param, values);
+    }, aleffect->PropsVariant);
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC3(void, alGetEffectf, ALuint,effect, ALenum,param, ALfloat*,value)
 AL_API DECL_FUNC3(void, alGetEffectf, ALuint,effect, ALenum,param, ALfloat*,value)
 FORCE_ALIGN void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effect, ALenum param,
 FORCE_ALIGN void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effect, ALenum param,
     ALfloat *value) noexcept
     ALfloat *value) noexcept
 try {
 try {
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> effectlock{device->EffectLock};
+    auto *device = context->mALDevice.get();
+    auto effectlock = std::lock_guard{device->EffectLock};
 
 
     const ALeffect *aleffect{LookupEffect(device, effect)};
     const ALeffect *aleffect{LookupEffect(device, effect)};
     if(!aleffect)
     if(!aleffect)
-        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
+        context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect);
 
 
     /* Call the appropriate handler */
     /* Call the appropriate handler */
-    std::visit([aleffect,param,value](auto &arg)
+    std::visit([context,aleffect,param,value](auto &arg)
     {
     {
         using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
         using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
-        if constexpr(std::is_same_v<Type,ReverbProps>)
-        {
-            if(aleffect->type == AL_EFFECT_REVERB)
-                return EffectHandler::StdReverbGetParamf(arg, param, value);
-        }
-        return EffectHandler::GetParamf(arg, param, value);
-    }, aleffect->Props);
+        using PropType = typename Type::prop_type;
+        return arg.GetParamf(context, std::get<PropType>(aleffect->Props), param, value);
+    }, aleffect->PropsVariant);
+}
+catch(al::base_exception&) {
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC3(void, alGetEffectfv, ALuint,effect, ALenum,param, ALfloat*,values)
 AL_API DECL_FUNC3(void, alGetEffectfv, ALuint,effect, ALenum,param, ALfloat*,values)
 FORCE_ALIGN void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param,
 FORCE_ALIGN void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param,
     ALfloat *values) noexcept
     ALfloat *values) noexcept
 try {
 try {
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> effectlock{device->EffectLock};
+    auto *device = context->mALDevice.get();
+    auto effectlock = std::lock_guard{device->EffectLock};
 
 
     const ALeffect *aleffect{LookupEffect(device, effect)};
     const ALeffect *aleffect{LookupEffect(device, effect)};
     if(!aleffect)
     if(!aleffect)
-        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
+        context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect);
 
 
     /* Call the appropriate handler */
     /* Call the appropriate handler */
-    std::visit([aleffect,param,values](auto &arg)
+    std::visit([context,aleffect,param,values](auto &arg)
     {
     {
         using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
         using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
-        if constexpr(std::is_same_v<Type,ReverbProps>)
-        {
-            if(aleffect->type == AL_EFFECT_REVERB)
-                return EffectHandler::StdReverbGetParamfv(arg, param, values);
-        }
-        return EffectHandler::GetParamfv(arg, param, values);
-    }, aleffect->Props);
+        using PropType = typename Type::prop_type;
+        return arg.GetParamfv(context, std::get<PropType>(aleffect->Props), param, values);
+    }, aleffect->PropsVariant);
+}
+catch(al::base_exception&) {
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 
 
@@ -508,12 +525,12 @@ void InitEffect(ALeffect *effect)
 
 
 void ALeffect::SetName(ALCcontext* context, ALuint id, std::string_view name)
 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 *device = context->mALDevice.get();
+    auto effectlock = std::lock_guard{device->EffectLock};
 
 
     auto effect = LookupEffect(device, id);
     auto effect = LookupEffect(device, id);
     if(!effect)
     if(!effect)
-        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", id};
+        context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", id);
 
 
     device->mEffectNames.insert_or_assign(id, name);
     device->mEffectNames.insert_or_assign(id, name);
 }
 }
@@ -679,7 +696,7 @@ void LoadReverbPreset(const std::string_view name, ALeffect *effect)
     if(al::case_compare(name, "NONE"sv) == 0)
     if(al::case_compare(name, "NONE"sv) == 0)
     {
     {
         InitEffectParams(effect, AL_EFFECT_NULL);
         InitEffectParams(effect, AL_EFFECT_NULL);
-        TRACE("Loading reverb '%s'\n", "NONE");
+        TRACE("Loading reverb '{}'", "NONE");
         return;
         return;
     }
     }
 
 
@@ -694,7 +711,7 @@ void LoadReverbPreset(const std::string_view name, ALeffect *effect)
         if(al::case_compare(name, std::data(reverbitem.name)) != 0)
         if(al::case_compare(name, std::data(reverbitem.name)) != 0)
             continue;
             continue;
 
 
-        TRACE("Loading reverb '%s'\n", std::data(reverbitem.name));
+        TRACE("Loading reverb '{}'", std::data(reverbitem.name));
         const auto &props = reverbitem.props;
         const auto &props = reverbitem.props;
         auto &dst = std::get<ReverbProps>(effect->Props);
         auto &dst = std::get<ReverbProps>(effect->Props);
         dst.Density   = props.flDensity;
         dst.Density   = props.flDensity;
@@ -727,7 +744,7 @@ void LoadReverbPreset(const std::string_view name, ALeffect *effect)
         return;
         return;
     }
     }
 
 
-    WARN("Reverb preset '%.*s' not found\n", al::sizei(name), name.data());
+    WARN("Reverb preset '{}' not found", name);
 }
 }
 
 
 bool IsValidEffectType(ALenum type) noexcept
 bool IsValidEffectType(ALenum type) noexcept

+ 10 - 2
libs/openal-soft/al/effect.h

@@ -6,6 +6,7 @@
 #include <cstdint>
 #include <cstdint>
 #include <string_view>
 #include <string_view>
 #include <utility>
 #include <utility>
+#include <variant>
 
 
 #include "AL/al.h"
 #include "AL/al.h"
 #include "AL/alc.h"
 #include "AL/alc.h"
@@ -14,6 +15,7 @@
 #include "almalloc.h"
 #include "almalloc.h"
 #include "alnumeric.h"
 #include "alnumeric.h"
 #include "core/effects/base.h"
 #include "core/effects/base.h"
+#include "effects/effects.h"
 
 
 
 
 enum {
 enum {
@@ -42,14 +44,20 @@ struct EffectList {
     ALuint type;
     ALuint type;
     ALenum val;
     ALenum val;
 };
 };
-extern const std::array<EffectList,16> gEffectList;
+DECL_HIDDEN 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 {
 struct ALeffect {
     // Effect type (AL_EFFECT_NULL, ...)
     // Effect type (AL_EFFECT_NULL, ...)
     ALenum type{AL_EFFECT_NULL};
     ALenum type{AL_EFFECT_NULL};
 
 
-    EffectProps Props{};
+    EffectHandlerVariant PropsVariant;
+    EffectProps Props;
 
 
     /* Self ID */
     /* Self ID */
     ALuint id{0u};
     ALuint id{0u};

+ 35 - 44
libs/openal-soft/al/effects/autowah.cpp

@@ -4,15 +4,13 @@
 #include <cmath>
 #include <cmath>
 #include <cstdlib>
 #include <cstdlib>
 
 
-#include <algorithm>
-
 #include "AL/efx.h"
 #include "AL/efx.h"
 
 
-#include "alc/effects/base.h"
+#include "alc/context.h"
+#include "alnumeric.h"
 #include "effects.h"
 #include "effects.h"
 
 
-#ifdef ALSOFT_EAX
-#include "alnumeric.h"
+#if ALSOFT_EAX
 #include "al/eax/effect.h"
 #include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #include "al/eax/utils.h"
@@ -35,75 +33,68 @@ constexpr EffectProps genDefaultProps() noexcept
 
 
 const EffectProps AutowahEffectProps{genDefaultProps()};
 const EffectProps AutowahEffectProps{genDefaultProps()};
 
 
-void EffectHandler::SetParami(AutowahProps&, ALenum param, int)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
-void EffectHandler::SetParamiv(AutowahProps&, ALenum param, const int*)
-{
-    throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
-        param};
-}
+void AutowahEffectHandler::SetParami(ALCcontext *context, AutowahProps&, ALenum param, int)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid autowah integer property {:#04x}", as_unsigned(param)); }
+void AutowahEffectHandler::SetParamiv(ALCcontext *context, AutowahProps&, ALenum param, const int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid autowah integer vector property {:#04x}", as_unsigned(param)); }
 
 
-void EffectHandler::SetParamf(AutowahProps &props, ALenum param, float val)
+void AutowahEffectHandler::SetParamf(ALCcontext *context, AutowahProps &props, ALenum param, float val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_AUTOWAH_ATTACK_TIME:
     case AL_AUTOWAH_ATTACK_TIME:
         if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE, "Autowah attack time out of range");
         props.AttackTime = val;
         props.AttackTime = val;
-        break;
+        return;
 
 
     case AL_AUTOWAH_RELEASE_TIME:
     case AL_AUTOWAH_RELEASE_TIME:
         if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE, "Autowah release time out of range");
         props.ReleaseTime = val;
         props.ReleaseTime = val;
-        break;
+        return;
 
 
     case AL_AUTOWAH_RESONANCE:
     case AL_AUTOWAH_RESONANCE:
         if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
         if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
-            throw effect_exception{AL_INVALID_VALUE, "Autowah resonance out of range"};
+            context->throw_error(AL_INVALID_VALUE, "Autowah resonance out of range");
         props.Resonance = val;
         props.Resonance = val;
-        break;
+        return;
 
 
     case AL_AUTOWAH_PEAK_GAIN:
     case AL_AUTOWAH_PEAK_GAIN:
         if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE, "Autowah peak gain out of range");
         props.PeakGain = val;
         props.PeakGain = val;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
+        return;
     }
     }
-}
-void EffectHandler::SetParamfv(AutowahProps &props,  ALenum param, const float *vals)
-{ SetParamf(props, param, *vals); }
 
 
-void EffectHandler::GetParami(const AutowahProps&, ALenum param, int*)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
-void EffectHandler::GetParamiv(const AutowahProps&, ALenum param, int*)
-{
-    throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
-        param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid autowah float property {:#04x}",
+        as_unsigned(param));
 }
 }
+void AutowahEffectHandler::SetParamfv(ALCcontext *context, AutowahProps &props,  ALenum param, const float *vals)
+{ SetParamf(context, props, param, *vals); }
 
 
-void EffectHandler::GetParamf(const AutowahProps &props, ALenum param, float *val)
+void AutowahEffectHandler::GetParami(ALCcontext *context, const AutowahProps&, ALenum param, int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid autowah integer property {:#04x}", as_unsigned(param)); }
+void AutowahEffectHandler::GetParamiv(ALCcontext *context, const AutowahProps&, ALenum param, int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid autowah integer vector property {:#04x}", as_unsigned(param)); }
+
+void AutowahEffectHandler::GetParamf(ALCcontext *context, const AutowahProps &props, ALenum param, float *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
-    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};
+    case AL_AUTOWAH_ATTACK_TIME: *val = props.AttackTime; return;
+    case AL_AUTOWAH_RELEASE_TIME: *val = props.ReleaseTime; return;
+    case AL_AUTOWAH_RESONANCE: *val = props.Resonance; return;
+    case AL_AUTOWAH_PEAK_GAIN: *val = props.PeakGain; return;
     }
     }
 
 
+    context->throw_error(AL_INVALID_ENUM, "Invalid autowah float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamfv(const AutowahProps &props, ALenum param, float *vals)
-{ GetParamf(props, param, vals); }
+void AutowahEffectHandler::GetParamfv(ALCcontext *context, const AutowahProps &props, ALenum param, float *vals)
+{ GetParamf(context, props, param, vals); }
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 namespace {
 namespace {
 
 
 using AutowahCommitter = EaxCommitter<EaxAutowahCommitter>;
 using AutowahCommitter = EaxCommitter<EaxAutowahCommitter>;

+ 106 - 95
libs/openal-soft/al/effects/chorus.cpp

@@ -7,13 +7,13 @@
 #include "AL/al.h"
 #include "AL/al.h"
 #include "AL/efx.h"
 #include "AL/efx.h"
 
 
-#include "alc/effects/base.h"
+#include "alc/context.h"
+#include "alnumeric.h"
 #include "core/logging.h"
 #include "core/logging.h"
 #include "effects.h"
 #include "effects.h"
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 #include <cassert>
 #include <cassert>
-#include "alnumeric.h"
 #include "al/eax/effect.h"
 #include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #include "al/eax/utils.h"
@@ -44,7 +44,8 @@ constexpr ALenum EnumFromWaveform(ChorusWaveform type)
     case ChorusWaveform::Sinusoid: return AL_CHORUS_WAVEFORM_SINUSOID;
     case ChorusWaveform::Sinusoid: return AL_CHORUS_WAVEFORM_SINUSOID;
     case ChorusWaveform::Triangle: return AL_CHORUS_WAVEFORM_TRIANGLE;
     case ChorusWaveform::Triangle: return AL_CHORUS_WAVEFORM_TRIANGLE;
     }
     }
-    throw std::runtime_error{"Invalid chorus waveform: "+std::to_string(static_cast<int>(type))};
+    throw std::runtime_error{fmt::format("Invalid chorus waveform: {}",
+        int{al::to_underlying(type)})};
 }
 }
 
 
 constexpr EffectProps genDefaultChorusProps() noexcept
 constexpr EffectProps genDefaultChorusProps() noexcept
@@ -61,7 +62,7 @@ constexpr EffectProps genDefaultChorusProps() noexcept
 
 
 constexpr EffectProps genDefaultFlangerProps() noexcept
 constexpr EffectProps genDefaultFlangerProps() noexcept
 {
 {
-    FlangerProps props{};
+    ChorusProps props{};
     props.Waveform = WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM).value();
     props.Waveform = WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM).value();
     props.Phase = AL_FLANGER_DEFAULT_PHASE;
     props.Phase = AL_FLANGER_DEFAULT_PHASE;
     props.Rate = AL_FLANGER_DEFAULT_RATE;
     props.Rate = AL_FLANGER_DEFAULT_RATE;
@@ -75,7 +76,7 @@ constexpr EffectProps genDefaultFlangerProps() noexcept
 
 
 const EffectProps ChorusEffectProps{genDefaultChorusProps()};
 const EffectProps ChorusEffectProps{genDefaultChorusProps()};
 
 
-void EffectHandler::SetParami(ChorusProps &props, ALenum param, int val)
+void ChorusEffectHandler::SetParami(ALCcontext *context, ChorusProps &props, ALenum param, int val)
 {
 {
     switch(param)
     switch(param)
     {
     {
@@ -83,89 +84,90 @@ void EffectHandler::SetParami(ChorusProps &props, ALenum param, int val)
         if(auto formopt = WaveformFromEnum(val))
         if(auto formopt = WaveformFromEnum(val))
             props.Waveform = *formopt;
             props.Waveform = *formopt;
         else
         else
-            throw effect_exception{AL_INVALID_VALUE, "Invalid chorus waveform: 0x%04x", val};
-        break;
+            context->throw_error(AL_INVALID_VALUE, "Invalid chorus waveform: {:#04x}",
+                as_unsigned(val));
+        return;
 
 
     case AL_CHORUS_PHASE:
     case AL_CHORUS_PHASE:
         if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_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};
+            context->throw_error(AL_INVALID_VALUE, "Chorus phase out of range: {}", val);
         props.Phase = val;
         props.Phase = val;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
+        return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid chorus integer property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::SetParamiv(ChorusProps &props, ALenum param, const int *vals)
-{ SetParami(props, param, *vals); }
-void EffectHandler::SetParamf(ChorusProps &props, ALenum param, float val)
+void ChorusEffectHandler::SetParamiv(ALCcontext *context, ChorusProps &props, ALenum param, const int *vals)
+{ SetParami(context, props, param, *vals); }
+void ChorusEffectHandler::SetParamf(ALCcontext *context, ChorusProps &props, ALenum param, float val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_CHORUS_RATE:
     case AL_CHORUS_RATE:
         if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_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};
+            context->throw_error(AL_INVALID_VALUE, "Chorus rate out of range: {:f}", val);
         props.Rate = val;
         props.Rate = val;
-        break;
+        return;
 
 
     case AL_CHORUS_DEPTH:
     case AL_CHORUS_DEPTH:
         if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_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};
+            context->throw_error(AL_INVALID_VALUE, "Chorus depth out of range: {:f}", val);
         props.Depth = val;
         props.Depth = val;
-        break;
+        return;
 
 
     case AL_CHORUS_FEEDBACK:
     case AL_CHORUS_FEEDBACK:
         if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_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};
+            context->throw_error(AL_INVALID_VALUE, "Chorus feedback out of range: {:f}", val);
         props.Feedback = val;
         props.Feedback = val;
-        break;
+        return;
 
 
     case AL_CHORUS_DELAY:
     case AL_CHORUS_DELAY:
         if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_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};
+            context->throw_error(AL_INVALID_VALUE, "Chorus delay out of range: {:f}", val);
         props.Delay = val;
         props.Delay = val;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
+        return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid chorus float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::SetParamfv(ChorusProps &props, ALenum param, const float *vals)
-{ SetParamf(props, param, *vals); }
+void ChorusEffectHandler::SetParamfv(ALCcontext *context, ChorusProps &props, ALenum param, const float *vals)
+{ SetParamf(context, props, param, *vals); }
 
 
-void EffectHandler::GetParami(const ChorusProps &props, ALenum param, int *val)
+void ChorusEffectHandler::GetParami(ALCcontext *context, const ChorusProps &props, ALenum param, int *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
-    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};
+    case AL_CHORUS_WAVEFORM: *val = EnumFromWaveform(props.Waveform); return;
+    case AL_CHORUS_PHASE: *val = props.Phase; return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid chorus integer property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamiv(const ChorusProps &props, ALenum param, int *vals)
-{ GetParami(props, param, vals); }
-void EffectHandler::GetParamf(const ChorusProps &props, ALenum param, float *val)
+void ChorusEffectHandler::GetParamiv(ALCcontext *context, const ChorusProps &props, ALenum param, int *vals)
+{ GetParami(context, props, param, vals); }
+void ChorusEffectHandler::GetParamf(ALCcontext *context, const ChorusProps &props, ALenum param, float *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
-    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};
+    case AL_CHORUS_RATE: *val = props.Rate; return;
+    case AL_CHORUS_DEPTH: *val = props.Depth; return;
+    case AL_CHORUS_FEEDBACK: *val = props.Feedback; return;
+    case AL_CHORUS_DELAY: *val = props.Delay; return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid chorus float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamfv(const ChorusProps &props, ALenum param, float *vals)
-{ GetParamf(props, param, vals); }
+void ChorusEffectHandler::GetParamfv(ALCcontext *context, const ChorusProps &props, ALenum param, float *vals)
+{ GetParamf(context, props, param, vals); }
 
 
 
 
 const EffectProps FlangerEffectProps{genDefaultFlangerProps()};
 const EffectProps FlangerEffectProps{genDefaultFlangerProps()};
 
 
-void EffectHandler::SetParami(FlangerProps &props, ALenum param, int val)
+void FlangerEffectHandler::SetParami(ALCcontext *context, ChorusProps &props, ALenum param, int val)
 {
 {
     switch(param)
     switch(param)
     {
     {
@@ -173,93 +175,93 @@ void EffectHandler::SetParami(FlangerProps &props, ALenum param, int val)
         if(auto formopt = WaveformFromEnum(val))
         if(auto formopt = WaveformFromEnum(val))
             props.Waveform = *formopt;
             props.Waveform = *formopt;
         else
         else
-            throw effect_exception{AL_INVALID_VALUE, "Invalid flanger waveform: 0x%04x", val};
-        break;
+            context->throw_error(AL_INVALID_VALUE, "Invalid flanger waveform: {:#04x}",
+                as_unsigned(val));
+        return;
 
 
     case AL_FLANGER_PHASE:
     case AL_FLANGER_PHASE:
         if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_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};
+            context->throw_error(AL_INVALID_VALUE, "Flanger phase out of range: {}", val);
         props.Phase = val;
         props.Phase = val;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
+        return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid flanger integer property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::SetParamiv(FlangerProps &props, ALenum param, const int *vals)
-{ SetParami(props, param, *vals); }
-void EffectHandler::SetParamf(FlangerProps &props, ALenum param, float val)
+void FlangerEffectHandler::SetParamiv(ALCcontext *context, ChorusProps &props, ALenum param, const int *vals)
+{ SetParami(context, props, param, *vals); }
+void FlangerEffectHandler::SetParamf(ALCcontext *context, ChorusProps &props, ALenum param, float val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_FLANGER_RATE:
     case AL_FLANGER_RATE:
         if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_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};
+            context->throw_error(AL_INVALID_VALUE, "Flanger rate out of range: {:f}", val);
         props.Rate = val;
         props.Rate = val;
-        break;
+        return;
 
 
     case AL_FLANGER_DEPTH:
     case AL_FLANGER_DEPTH:
         if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_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};
+            context->throw_error(AL_INVALID_VALUE, "Flanger depth out of range: {:f}", val);
         props.Depth = val;
         props.Depth = val;
-        break;
+        return;
 
 
     case AL_FLANGER_FEEDBACK:
     case AL_FLANGER_FEEDBACK:
         if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_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};
+            context->throw_error(AL_INVALID_VALUE, "Flanger feedback out of range: {:f}", val);
         props.Feedback = val;
         props.Feedback = val;
-        break;
+        return;
 
 
     case AL_FLANGER_DELAY:
     case AL_FLANGER_DELAY:
         if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_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};
+            context->throw_error(AL_INVALID_VALUE, "Flanger delay out of range: {:f}", val);
         props.Delay = val;
         props.Delay = val;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
+        return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid flanger float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::SetParamfv(FlangerProps &props, ALenum param, const float *vals)
-{ SetParamf(props, param, *vals); }
+void FlangerEffectHandler::SetParamfv(ALCcontext *context, ChorusProps &props, ALenum param, const float *vals)
+{ SetParamf(context, props, param, *vals); }
 
 
-void EffectHandler::GetParami(const FlangerProps &props, ALenum param, int *val)
+void FlangerEffectHandler::GetParami(ALCcontext *context, const ChorusProps &props, ALenum param, int *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
-    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};
+    case AL_FLANGER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); return;
+    case AL_FLANGER_PHASE: *val = props.Phase; return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid flanger integer property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamiv(const FlangerProps &props, ALenum param, int *vals)
-{ GetParami(props, param, vals); }
-void EffectHandler::GetParamf(const FlangerProps &props, ALenum param, float *val)
+void FlangerEffectHandler::GetParamiv(ALCcontext *context, const ChorusProps &props, ALenum param, int *vals)
+{ GetParami(context, props, param, vals); }
+void FlangerEffectHandler::GetParamf(ALCcontext *context, const ChorusProps &props, ALenum param, float *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
-    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};
+    case AL_FLANGER_RATE: *val = props.Rate; return;
+    case AL_FLANGER_DEPTH: *val = props.Depth; return;
+    case AL_FLANGER_FEEDBACK: *val = props.Feedback; return;
+    case AL_FLANGER_DELAY: *val = props.Delay; return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid flanger float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamfv(const FlangerProps &props, ALenum param, float *vals)
-{ GetParamf(props, param, vals); }
+void FlangerEffectHandler::GetParamfv(ALCcontext *context, const ChorusProps &props, ALenum param, float *vals)
+{ GetParamf(context, props, param, vals); }
 
 
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 namespace {
 namespace {
 
 
 struct EaxChorusTraits {
 struct EaxChorusTraits {
     using EaxProps = EAXCHORUSPROPERTIES;
     using EaxProps = EAXCHORUSPROPERTIES;
     using Committer = EaxChorusCommitter;
     using Committer = EaxChorusCommitter;
-    using AlProps = ChorusProps;
 
 
     static constexpr auto efx_effect() { return AL_EFFECT_CHORUS; }
     static constexpr auto efx_effect() { return AL_EFFECT_CHORUS; }
 
 
@@ -325,7 +327,6 @@ struct EaxChorusTraits {
 struct EaxFlangerTraits {
 struct EaxFlangerTraits {
     using EaxProps = EAXFLANGERPROPERTIES;
     using EaxProps = EAXFLANGERPROPERTIES;
     using Committer = EaxFlangerCommitter;
     using Committer = EaxFlangerCommitter;
-    using AlProps = FlangerProps;
 
 
     static constexpr auto efx_effect() { return AL_EFFECT_FLANGER; }
     static constexpr auto efx_effect() { return AL_EFFECT_FLANGER; }
 
 
@@ -393,7 +394,6 @@ struct ChorusFlangerEffect {
     using Traits = TTraits;
     using Traits = TTraits;
     using EaxProps = typename Traits::EaxProps;
     using EaxProps = typename Traits::EaxProps;
     using Committer = typename Traits::Committer;
     using Committer = typename Traits::Committer;
-    using AlProps = typename Traits::AlProps;
     using Exception = typename Committer::Exception;
     using Exception = typename Committer::Exception;
 
 
     struct WaveformValidator {
     struct WaveformValidator {
@@ -551,7 +551,7 @@ public:
         }
         }
     }
     }
 
 
-    static bool Commit(const EaxProps &props, EaxEffectProps &props_, AlProps &al_props_)
+    static bool Commit(const EaxProps &props, EaxEffectProps &props_, ChorusProps &al_props_)
     {
     {
         if(auto *cur = std::get_if<EaxProps>(&props_); cur && *cur == props)
         if(auto *cur = std::get_if<EaxProps>(&props_); cur && *cur == props)
             return false;
             return false;
@@ -564,6 +564,17 @@ public:
         al_props_.Depth = props.flDepth;
         al_props_.Depth = props.flDepth;
         al_props_.Feedback = props.flFeedback;
         al_props_.Feedback = props.flFeedback;
         al_props_.Delay = props.flDelay;
         al_props_.Delay = props.flDelay;
+        if(EaxTraceCommits) UNLIKELY
+        {
+            TRACE("Chorus/flanger commit:\n"
+                "  Waveform: {}\n"
+                "  Phase: {}\n"
+                "  Rate: {:f}\n"
+                "  Depth: {:f}\n"
+                "  Feedback: {:f}\n"
+                "  Delay: {:f}", al::to_underlying(al_props_.Waveform), al_props_.Phase,
+                al_props_.Rate, al_props_.Depth, al_props_.Feedback, al_props_.Delay);
+        }
 
 
         return true;
         return true;
     }
     }
@@ -628,7 +639,7 @@ template<>
 bool EaxFlangerCommitter::commit(const EAXFLANGERPROPERTIES &props)
 bool EaxFlangerCommitter::commit(const EAXFLANGERPROPERTIES &props)
 {
 {
     using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
     using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
-    return Committer::Commit(props, mEaxProps, mAlProps.emplace<FlangerProps>());
+    return Committer::Commit(props, mEaxProps, mAlProps.emplace<ChorusProps>());
 }
 }
 
 
 void EaxFlangerCommitter::SetDefaults(EaxEffectProps &props)
 void EaxFlangerCommitter::SetDefaults(EaxEffectProps &props)

+ 28 - 38
libs/openal-soft/al/effects/compressor.cpp

@@ -4,11 +4,11 @@
 #include "AL/al.h"
 #include "AL/al.h"
 #include "AL/efx.h"
 #include "AL/efx.h"
 
 
-#include "alc/effects/base.h"
+#include "alc/context.h"
+#include "alnumeric.h"
 #include "effects.h"
 #include "effects.h"
 
 
-#ifdef ALSOFT_EAX
-#include "alnumeric.h"
+#if ALSOFT_EAX
 #include "al/eax/effect.h"
 #include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #include "al/eax/utils.h"
@@ -28,56 +28,46 @@ constexpr EffectProps genDefaultProps() noexcept
 
 
 const EffectProps CompressorEffectProps{genDefaultProps()};
 const EffectProps CompressorEffectProps{genDefaultProps()};
 
 
-void EffectHandler::SetParami(CompressorProps &props, ALenum param, int val)
+void CompressorEffectHandler::SetParami(ALCcontext *context, CompressorProps &props, ALenum param, int val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_COMPRESSOR_ONOFF:
     case AL_COMPRESSOR_ONOFF:
         if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
         if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
-            throw effect_exception{AL_INVALID_VALUE, "Compressor state out of range"};
+            context->throw_error(AL_INVALID_VALUE, "Compressor state out of range");
         props.OnOff = (val != AL_FALSE);
         props.OnOff = (val != AL_FALSE);
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
-            param};
+        return;
     }
     }
-}
-void EffectHandler::SetParamiv(CompressorProps &props, ALenum param, const int *vals)
-{ SetParami(props, param, *vals); }
-void EffectHandler::SetParamf(CompressorProps&, ALenum param, float)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
-void EffectHandler::SetParamfv(CompressorProps&, ALenum param, const float*)
-{
-    throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x",
-        param};
-}
 
 
-void EffectHandler::GetParami(const CompressorProps &props, ALenum param, int *val)
+    context->throw_error(AL_INVALID_ENUM, "Invalid compressor integer property {:#04x}",
+        as_unsigned(param));
+}
+void CompressorEffectHandler::SetParamiv(ALCcontext *context, CompressorProps &props, ALenum param, const int *vals)
+{ SetParami(context, props, param, *vals); }
+void CompressorEffectHandler::SetParamf(ALCcontext *context, CompressorProps&, ALenum param, float)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid compressor float property {:#04x}", as_unsigned(param)); }
+void CompressorEffectHandler::SetParamfv(ALCcontext *context, CompressorProps&, ALenum param, const float*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid compressor float-vector property {:#04x}", as_unsigned(param)); }
+
+void CompressorEffectHandler::GetParami(ALCcontext *context, const CompressorProps &props, ALenum param, int *val)
 { 
 { 
     switch(param)
     switch(param)
     {
     {
-    case AL_COMPRESSOR_ONOFF:
-        *val = props.OnOff;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
-            param};
+    case AL_COMPRESSOR_ONOFF: *val = props.OnOff; return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid compressor integer property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamiv(const CompressorProps &props, ALenum param, int *vals)
-{ GetParami(props, param, vals); }
-void EffectHandler::GetParamf(const CompressorProps&, ALenum param, float*)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
-void EffectHandler::GetParamfv(const CompressorProps&, ALenum param, float*)
-{
-    throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x",
-        param};
-}
+void CompressorEffectHandler::GetParamiv(ALCcontext *context, const CompressorProps &props, ALenum param, int *vals)
+{ GetParami(context, props, param, vals); }
+void CompressorEffectHandler::GetParamf(ALCcontext *context, const CompressorProps&, ALenum param, float*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid compressor float property {:#04x}", as_unsigned(param)); }
+void CompressorEffectHandler::GetParamfv(ALCcontext *context, const CompressorProps&, ALenum param, float*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid compressor float-vector property {:#04x}", as_unsigned(param)); }
 
 
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 namespace {
 namespace {
 
 
 using CompressorCommitter = EaxCommitter<EaxCompressorCommitter>;
 using CompressorCommitter = EaxCommitter<EaxCompressorCommitter>;

+ 27 - 68
libs/openal-soft/al/effects/convolution.cpp

@@ -7,10 +7,10 @@
 
 
 #include "AL/al.h"
 #include "AL/al.h"
 
 
+#include "alc/context.h"
 #include "alc/inprogext.h"
 #include "alc/inprogext.h"
 #include "alnumeric.h"
 #include "alnumeric.h"
 #include "alspan.h"
 #include "alspan.h"
-#include "core/effects/base.h"
 #include "effects.h"
 #include "effects.h"
 
 
 
 
@@ -28,90 +28,49 @@ constexpr EffectProps genDefaultProps() noexcept
 
 
 const EffectProps ConvolutionEffectProps{genDefaultProps()};
 const EffectProps ConvolutionEffectProps{genDefaultProps()};
 
 
-void EffectHandler::SetParami(ConvolutionProps& /*props*/, ALenum param, int /*val*/)
-{
-    switch(param)
-    {
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect integer property 0x%04x",
-            param};
-    }
-}
-void EffectHandler::SetParamiv(ConvolutionProps &props, ALenum param, const int *vals)
-{
-    switch(param)
-    {
-    default:
-        SetParami(props, param, *vals);
-    }
-}
-void EffectHandler::SetParamf(ConvolutionProps& /*props*/, ALenum param, float /*val*/)
-{
-    switch(param)
-    {
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect float property 0x%04x",
-            param};
-    }
-}
-void EffectHandler::SetParamfv(ConvolutionProps &props, ALenum param, const float *values)
+void ConvolutionEffectHandler::SetParami(ALCcontext *context, ConvolutionProps& /*props*/, ALenum param, int /*val*/)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid convolution effect integer property {:#04x}", as_unsigned(param)); }
+void ConvolutionEffectHandler::SetParamiv(ALCcontext *context, ConvolutionProps &props, ALenum param, const int *vals)
+{ SetParami(context, props, param, *vals); }
+
+void ConvolutionEffectHandler::SetParamf(ALCcontext *context, ConvolutionProps& /*props*/, ALenum param, float /*val*/)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid convolution effect float property {:#04x}", as_unsigned(param)); }
+void ConvolutionEffectHandler::SetParamfv(ALCcontext *context, ConvolutionProps &props, ALenum param, const float *values)
 {
 {
     static constexpr auto finite_checker = [](float val) -> bool { return std::isfinite(val); };
     static constexpr auto finite_checker = [](float val) -> bool { return std::isfinite(val); };
-    al::span<const float> vals;
+
     switch(param)
     switch(param)
     {
     {
     case AL_CONVOLUTION_ORIENTATION_SOFT:
     case AL_CONVOLUTION_ORIENTATION_SOFT:
-        vals = {values, 6_uz};
+        auto vals = al::span{values, 6_uz};
         if(!std::all_of(vals.cbegin(), vals.cend(), finite_checker))
         if(!std::all_of(vals.cbegin(), vals.cend(), finite_checker))
-            throw effect_exception{AL_INVALID_VALUE, "Property 0x%04x value out of range", param};
+            context->throw_error(AL_INVALID_VALUE, "Convolution orientation out of range", param);
 
 
         std::copy_n(vals.cbegin(), props.OrientAt.size(), props.OrientAt.begin());
         std::copy_n(vals.cbegin(), props.OrientAt.size(), props.OrientAt.begin());
         std::copy_n(vals.cbegin()+3, props.OrientUp.size(), props.OrientUp.begin());
         std::copy_n(vals.cbegin()+3, props.OrientUp.size(), props.OrientUp.begin());
-        break;
-
-    default:
-        SetParamf(props, param, *values);
+        return;
     }
     }
-}
 
 
-void EffectHandler::GetParami(const ConvolutionProps& /*props*/, ALenum param, int* /*val*/)
-{
-    switch(param)
-    {
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect integer property 0x%04x",
-            param};
-    }
-}
-void EffectHandler::GetParamiv(const ConvolutionProps &props, ALenum param, int *vals)
-{
-    switch(param)
-    {
-    default:
-        GetParami(props, param, vals);
-    }
+    SetParamf(context, props, param, *values);
 }
 }
-void EffectHandler::GetParamf(const ConvolutionProps& /*props*/, ALenum param, float* /*val*/)
-{
-    switch(param)
-    {
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect float property 0x%04x",
-            param};
-    }
-}
-void EffectHandler::GetParamfv(const ConvolutionProps &props, ALenum param, float *values)
+
+void ConvolutionEffectHandler::GetParami(ALCcontext *context, const ConvolutionProps& /*props*/, ALenum param, int* /*val*/)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid convolution effect integer property {:#04x}", as_unsigned(param)); }
+void ConvolutionEffectHandler::GetParamiv(ALCcontext *context, const ConvolutionProps &props, ALenum param, int *vals)
+{ GetParami(context, props, param, vals); }
+
+void ConvolutionEffectHandler::GetParamf(ALCcontext *context, const ConvolutionProps& /*props*/, ALenum param, float* /*val*/)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid convolution effect float property {:#04x}", as_unsigned(param)); }
+void ConvolutionEffectHandler::GetParamfv(ALCcontext *context, const ConvolutionProps &props, ALenum param, float *values)
 {
 {
-    al::span<float> vals;
     switch(param)
     switch(param)
     {
     {
     case AL_CONVOLUTION_ORIENTATION_SOFT:
     case AL_CONVOLUTION_ORIENTATION_SOFT:
-        vals = {values, 6_uz};
+        auto vals = al::span{values, 6_uz};
         std::copy(props.OrientAt.cbegin(), props.OrientAt.cend(), vals.begin());
         std::copy(props.OrientAt.cbegin(), props.OrientAt.cend(), vals.begin());
         std::copy(props.OrientUp.cbegin(), props.OrientUp.cend(), vals.begin()+3);
         std::copy(props.OrientUp.cbegin(), props.OrientUp.cend(), vals.begin()+3);
-        break;
-
-    default:
-        GetParamf(props, param, values);
+        return;
     }
     }
+
+    GetParamf(context, props, param, values);
 }
 }

+ 52 - 59
libs/openal-soft/al/effects/dedicated.cpp

@@ -6,7 +6,8 @@
 #include "AL/al.h"
 #include "AL/al.h"
 #include "AL/alext.h"
 #include "AL/alext.h"
 
 
-#include "alc/effects/base.h"
+#include "alc/context.h"
+#include "alnumeric.h"
 #include "effects.h"
 #include "effects.h"
 
 
 
 
@@ -14,14 +15,16 @@ namespace {
 
 
 constexpr EffectProps genDefaultDialogProps() noexcept
 constexpr EffectProps genDefaultDialogProps() noexcept
 {
 {
-    DedicatedDialogProps props{};
+    DedicatedProps props{};
+    props.Target = DedicatedProps::Dialog;
     props.Gain = 1.0f;
     props.Gain = 1.0f;
     return props;
     return props;
 }
 }
 
 
 constexpr EffectProps genDefaultLfeProps() noexcept
 constexpr EffectProps genDefaultLfeProps() noexcept
 {
 {
-    DedicatedLfeProps props{};
+    DedicatedProps props{};
+    props.Target = DedicatedProps::Lfe;
     props.Gain = 1.0f;
     props.Gain = 1.0f;
     return props;
     return props;
 }
 }
@@ -30,91 +33,81 @@ constexpr EffectProps genDefaultLfeProps() noexcept
 
 
 const EffectProps DedicatedDialogEffectProps{genDefaultDialogProps()};
 const EffectProps DedicatedDialogEffectProps{genDefaultDialogProps()};
 
 
-void EffectHandler::SetParami(DedicatedDialogProps&, ALenum param, int)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
-void EffectHandler::SetParamiv(DedicatedDialogProps&, ALenum param, const int*)
-{
-    throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
-        param};
-}
-void EffectHandler::SetParamf(DedicatedDialogProps &props, ALenum param, float val)
+void DedicatedDialogEffectHandler::SetParami(ALCcontext *context, DedicatedProps&, ALenum param, int)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid dedicated integer property {:#04x}", as_unsigned(param)); }
+void DedicatedDialogEffectHandler::SetParamiv(ALCcontext *context, DedicatedProps&, ALenum param, const int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid dedicated integer-vector property {:#04x}", as_unsigned(param)); }
+void DedicatedDialogEffectHandler::SetParamf(ALCcontext *context, DedicatedProps &props, ALenum param, float val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_DEDICATED_GAIN:
     case AL_DEDICATED_GAIN:
         if(!(val >= 0.0f && std::isfinite(val)))
         if(!(val >= 0.0f && std::isfinite(val)))
-            throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"};
+            context->throw_error(AL_INVALID_VALUE, "Dedicated gain out of range");
         props.Gain = val;
         props.Gain = val;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
+        return;
     }
     }
-}
-void EffectHandler::SetParamfv(DedicatedDialogProps &props, ALenum param, const float *vals)
-{ SetParamf(props, param, *vals); }
 
 
-void EffectHandler::GetParami(const DedicatedDialogProps&, ALenum param, int*)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
-void EffectHandler::GetParamiv(const DedicatedDialogProps&, ALenum param, int*)
-{
-    throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
-        param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid dedicated float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamf(const DedicatedDialogProps &props, ALenum param, float *val)
+void DedicatedDialogEffectHandler::SetParamfv(ALCcontext *context, DedicatedProps &props, ALenum param, const float *vals)
+{ SetParamf(context, props, param, *vals); }
+
+void DedicatedDialogEffectHandler::GetParami(ALCcontext *context, const DedicatedProps&, ALenum param, int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid dedicated integer property {:#04x}", as_unsigned(param)); }
+void DedicatedDialogEffectHandler::GetParamiv(ALCcontext *context, const DedicatedProps&, ALenum param, int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid dedicated integer-vector property {:#04x}", as_unsigned(param)); }
+void DedicatedDialogEffectHandler::GetParamf(ALCcontext *context, const DedicatedProps &props, ALenum param, float *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
-    case AL_DEDICATED_GAIN: *val = props.Gain; break;
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
+    case AL_DEDICATED_GAIN: *val = props.Gain; return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid dedicated float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamfv(const DedicatedDialogProps &props, ALenum param, float *vals)
-{ GetParamf(props, param, vals); }
+void DedicatedDialogEffectHandler::GetParamfv(ALCcontext *context, const DedicatedProps &props, ALenum param, float *vals)
+{ GetParamf(context, props, param, vals); }
 
 
 
 
 const EffectProps DedicatedLfeEffectProps{genDefaultLfeProps()};
 const EffectProps DedicatedLfeEffectProps{genDefaultLfeProps()};
 
 
-void EffectHandler::SetParami(DedicatedLfeProps&, ALenum param, int)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
-void EffectHandler::SetParamiv(DedicatedLfeProps&, ALenum param, const int*)
-{
-    throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
-        param};
-}
-void EffectHandler::SetParamf(DedicatedLfeProps &props, ALenum param, float val)
+void DedicatedLfeEffectHandler::SetParami(ALCcontext *context, DedicatedProps&, ALenum param, int)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid dedicated integer property {:#04x}", as_unsigned(param)); }
+void DedicatedLfeEffectHandler::SetParamiv(ALCcontext *context, DedicatedProps&, ALenum param, const int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid dedicated integer-vector property {:#04x}", as_unsigned(param)); }
+void DedicatedLfeEffectHandler::SetParamf(ALCcontext *context, DedicatedProps &props, ALenum param, float val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_DEDICATED_GAIN:
     case AL_DEDICATED_GAIN:
         if(!(val >= 0.0f && std::isfinite(val)))
         if(!(val >= 0.0f && std::isfinite(val)))
-            throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"};
+            context->throw_error(AL_INVALID_VALUE, "Dedicated gain out of range");
         props.Gain = val;
         props.Gain = val;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
+        return;
     }
     }
-}
-void EffectHandler::SetParamfv(DedicatedLfeProps &props, ALenum param, const float *vals)
-{ SetParamf(props, param, *vals); }
 
 
-void EffectHandler::GetParami(const DedicatedLfeProps&, ALenum param, int*)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
-void EffectHandler::GetParamiv(const DedicatedLfeProps&, ALenum param, int*)
-{
-    throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
-        param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid dedicated float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamf(const DedicatedLfeProps &props, ALenum param, float *val)
+void DedicatedLfeEffectHandler::SetParamfv(ALCcontext *context, DedicatedProps &props, ALenum param, const float *vals)
+{ SetParamf(context, props, param, *vals); }
+
+void DedicatedLfeEffectHandler::GetParami(ALCcontext *context, const DedicatedProps&, ALenum param, int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid dedicated integer property {:#04x}", as_unsigned(param)); }
+void DedicatedLfeEffectHandler::GetParamiv(ALCcontext *context, const DedicatedProps&, ALenum param, int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid dedicated integer-vector property {:#04x}", as_unsigned(param)); }
+void DedicatedLfeEffectHandler::GetParamf(ALCcontext *context, const DedicatedProps &props, ALenum param, float *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
-    case AL_DEDICATED_GAIN: *val = props.Gain; break;
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
+    case AL_DEDICATED_GAIN: *val = props.Gain; return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid dedicated float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamfv(const DedicatedLfeProps &props, ALenum param, float *vals)
-{ GetParamf(props, param, vals); }
+void DedicatedLfeEffectHandler::GetParamfv(ALCcontext *context, const DedicatedProps &props, ALenum param, float *vals)
+{ GetParamf(context, props, param, vals); }

+ 41 - 45
libs/openal-soft/al/effects/distortion.cpp

@@ -4,11 +4,11 @@
 #include "AL/al.h"
 #include "AL/al.h"
 #include "AL/efx.h"
 #include "AL/efx.h"
 
 
-#include "alc/effects/base.h"
+#include "alc/context.h"
+#include "alnumeric.h"
 #include "effects.h"
 #include "effects.h"
 
 
-#ifdef ALSOFT_EAX
-#include "alnumeric.h"
+#if ALSOFT_EAX
 #include "al/eax/effect.h"
 #include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #include "al/eax/utils.h"
@@ -32,80 +32,76 @@ constexpr EffectProps genDefaultProps() noexcept
 
 
 const EffectProps DistortionEffectProps{genDefaultProps()};
 const EffectProps DistortionEffectProps{genDefaultProps()};
 
 
-void EffectHandler::SetParami(DistortionProps&, ALenum param, int)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
-void EffectHandler::SetParamiv(DistortionProps&, ALenum param, const int*)
-{
-    throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
-        param};
-}
-void EffectHandler::SetParamf(DistortionProps &props, ALenum param, float val)
+void DistortionEffectHandler::SetParami(ALCcontext *context, DistortionProps&, ALenum param, int)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid distortion integer property {:#04x}", as_unsigned(param)); }
+void DistortionEffectHandler::SetParamiv(ALCcontext *context, DistortionProps&, ALenum param, const int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid distortion integer-vector property {:#04x}", as_unsigned(param)); }
+
+void DistortionEffectHandler::SetParamf(ALCcontext *context, DistortionProps &props, ALenum param, float val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_DISTORTION_EDGE:
     case AL_DISTORTION_EDGE:
         if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
         if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
-            throw effect_exception{AL_INVALID_VALUE, "Distortion edge out of range"};
+            context->throw_error(AL_INVALID_VALUE, "Distortion edge out of range");
         props.Edge = val;
         props.Edge = val;
-        break;
+        return;
 
 
     case AL_DISTORTION_GAIN:
     case AL_DISTORTION_GAIN:
         if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
         if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
-            throw effect_exception{AL_INVALID_VALUE, "Distortion gain out of range"};
+            context->throw_error(AL_INVALID_VALUE, "Distortion gain out of range");
         props.Gain = val;
         props.Gain = val;
-        break;
+        return;
 
 
     case AL_DISTORTION_LOWPASS_CUTOFF:
     case AL_DISTORTION_LOWPASS_CUTOFF:
         if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE, "Distortion low-pass cutoff out of range");
         props.LowpassCutoff = val;
         props.LowpassCutoff = val;
-        break;
+        return;
 
 
     case AL_DISTORTION_EQCENTER:
     case AL_DISTORTION_EQCENTER:
         if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
         if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
-            throw effect_exception{AL_INVALID_VALUE, "Distortion EQ center out of range"};
+            context->throw_error(AL_INVALID_VALUE, "Distortion EQ center out of range");
         props.EQCenter = val;
         props.EQCenter = val;
-        break;
+        return;
 
 
     case AL_DISTORTION_EQBANDWIDTH:
     case AL_DISTORTION_EQBANDWIDTH:
         if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
         if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
-            throw effect_exception{AL_INVALID_VALUE, "Distortion EQ bandwidth out of range"};
+            context->throw_error(AL_INVALID_VALUE, "Distortion EQ bandwidth out of range");
         props.EQBandwidth = val;
         props.EQBandwidth = val;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param};
+        return;
     }
     }
-}
-void EffectHandler::SetParamfv(DistortionProps &props, ALenum param, const float *vals)
-{ SetParamf(props, param, *vals); }
 
 
-void EffectHandler::GetParami(const DistortionProps&, ALenum param, int*)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
-void EffectHandler::GetParamiv(const DistortionProps&, ALenum param, int*)
-{
-    throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
-        param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid distortion float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamf(const DistortionProps &props, ALenum param, float *val)
+void DistortionEffectHandler::SetParamfv(ALCcontext *context, DistortionProps &props, ALenum param, const float *vals)
+{ SetParamf(context, props, param, *vals); }
+
+void DistortionEffectHandler::GetParami(ALCcontext *context, const DistortionProps&, ALenum param, int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid distortion integer property {:#04x}", as_unsigned(param)); }
+void DistortionEffectHandler::GetParamiv(ALCcontext *context, const DistortionProps&, ALenum param, int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid distortion integer-vector property {:#04x}", as_unsigned(param)); }
+
+void DistortionEffectHandler::GetParamf(ALCcontext *context, const DistortionProps &props, ALenum param, float *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
-    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};
+    case AL_DISTORTION_EDGE: *val = props.Edge; return;
+    case AL_DISTORTION_GAIN: *val = props.Gain; return;
+    case AL_DISTORTION_LOWPASS_CUTOFF: *val = props.LowpassCutoff; return;
+    case AL_DISTORTION_EQCENTER: *val = props.EQCenter; return;
+    case AL_DISTORTION_EQBANDWIDTH: *val = props.EQBandwidth; return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid distortion float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamfv(const DistortionProps &props, ALenum param, float *vals)
-{ GetParamf(props, param, vals); }
+void DistortionEffectHandler::GetParamfv(ALCcontext *context, const DistortionProps &props, ALenum param, float *vals)
+{ GetParamf(context, props, param, vals); }
 
 
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 namespace {
 namespace {
 
 
 using DistortionCommitter = EaxCommitter<EaxDistortionCommitter>;
 using DistortionCommitter = EaxCommitter<EaxDistortionCommitter>;

+ 40 - 40
libs/openal-soft/al/effects/echo.cpp

@@ -4,11 +4,11 @@
 #include "AL/al.h"
 #include "AL/al.h"
 #include "AL/efx.h"
 #include "AL/efx.h"
 
 
-#include "alc/effects/base.h"
+#include "alc/context.h"
+#include "alnumeric.h"
 #include "effects.h"
 #include "effects.h"
 
 
-#ifdef ALSOFT_EAX
-#include "alnumeric.h"
+#if ALSOFT_EAX
 #include "al/eax/effect.h"
 #include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #include "al/eax/utils.h"
@@ -35,74 +35,74 @@ constexpr EffectProps genDefaultProps() noexcept
 
 
 const EffectProps EchoEffectProps{genDefaultProps()};
 const EffectProps EchoEffectProps{genDefaultProps()};
 
 
-void EffectHandler::SetParami(EchoProps&, ALenum param, int)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
-void EffectHandler::SetParamiv(EchoProps&, ALenum param, const int*)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
-void EffectHandler::SetParamf(EchoProps &props, ALenum param, float val)
+void EchoEffectHandler::SetParami(ALCcontext *context, EchoProps&, ALenum param, int)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid echo integer property {:#04x}", as_unsigned(param)); }
+void EchoEffectHandler::SetParamiv(ALCcontext *context, EchoProps&, ALenum param, const int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid echo integer-vector property {:#04x}", as_unsigned(param)); }
+void EchoEffectHandler::SetParamf(ALCcontext *context, EchoProps &props, ALenum param, float val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_ECHO_DELAY:
     case AL_ECHO_DELAY:
         if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
         if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
-            throw effect_exception{AL_INVALID_VALUE, "Echo delay out of range"};
+            context->throw_error(AL_INVALID_VALUE, "Echo delay out of range");
         props.Delay = val;
         props.Delay = val;
-        break;
+        return;
 
 
     case AL_ECHO_LRDELAY:
     case AL_ECHO_LRDELAY:
         if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY))
         if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY))
-            throw effect_exception{AL_INVALID_VALUE, "Echo LR delay out of range"};
+            context->throw_error(AL_INVALID_VALUE, "Echo LR delay out of range");
         props.LRDelay = val;
         props.LRDelay = val;
-        break;
+        return;
 
 
     case AL_ECHO_DAMPING:
     case AL_ECHO_DAMPING:
         if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING))
         if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING))
-            throw effect_exception{AL_INVALID_VALUE, "Echo damping out of range"};
+            context->throw_error(AL_INVALID_VALUE, "Echo damping out of range");
         props.Damping = val;
         props.Damping = val;
-        break;
+        return;
 
 
     case AL_ECHO_FEEDBACK:
     case AL_ECHO_FEEDBACK:
         if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK))
         if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK))
-            throw effect_exception{AL_INVALID_VALUE, "Echo feedback out of range"};
+            context->throw_error(AL_INVALID_VALUE, "Echo feedback out of range");
         props.Feedback = val;
         props.Feedback = val;
-        break;
+        return;
 
 
     case AL_ECHO_SPREAD:
     case AL_ECHO_SPREAD:
         if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD))
         if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD))
-            throw effect_exception{AL_INVALID_VALUE, "Echo spread out of range"};
+            context->throw_error(AL_INVALID_VALUE, "Echo spread out of range");
         props.Spread = val;
         props.Spread = val;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param};
+        return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid echo float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::SetParamfv(EchoProps &props, ALenum param, const float *vals)
-{ SetParamf(props, param, *vals); }
-
-void EffectHandler::GetParami(const EchoProps&, ALenum param, int*)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
-void EffectHandler::GetParamiv(const EchoProps&, ALenum param, int*)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
-void EffectHandler::GetParamf(const EchoProps &props, ALenum param, float *val)
+void EchoEffectHandler::SetParamfv(ALCcontext *context, EchoProps &props, ALenum param, const float *vals)
+{ SetParamf(context, props, param, *vals); }
+
+void EchoEffectHandler::GetParami(ALCcontext *context, const EchoProps&, ALenum param, int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid echo integer property {:#04x}", as_unsigned(param)); }
+void EchoEffectHandler::GetParamiv(ALCcontext *context, const EchoProps&, ALenum param, int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid echo integer-vector property {:#04x}", as_unsigned(param)); }
+void EchoEffectHandler::GetParamf(ALCcontext *context, const EchoProps &props, ALenum param, float *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
-    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};
+    case AL_ECHO_DELAY: *val = props.Delay; return;
+    case AL_ECHO_LRDELAY: *val = props.LRDelay; return;
+    case AL_ECHO_DAMPING: *val = props.Damping; return;
+    case AL_ECHO_FEEDBACK: *val = props.Feedback; return;
+    case AL_ECHO_SPREAD: *val = props.Spread; return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid echo float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamfv(const EchoProps &props, ALenum param, float *vals)
-{ GetParamf(props, param, vals); }
+void EchoEffectHandler::GetParamfv(ALCcontext *context, const EchoProps &props, ALenum param, float *vals)
+{ GetParamf(context, props, param, vals); }
 
 
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 namespace {
 namespace {
 
 
 using EchoCommitter = EaxCommitter<EaxEchoCommitter>;
 using EchoCommitter = EaxCommitter<EaxEchoCommitter>;

+ 49 - 58
libs/openal-soft/al/effects/effects.h

@@ -3,71 +3,62 @@
 
 
 #include <variant>
 #include <variant>
 
 
+#include "AL/alc.h"
 #include "AL/al.h"
 #include "AL/al.h"
 
 
-#include "al/error.h"
 #include "core/effects/base.h"
 #include "core/effects/base.h"
+#include "opthelpers.h"
 
 
-
-struct EffectHandler {
-#define DECL_HANDLER(T)                                                       \
-    static void SetParami(T &props, ALenum param, int val);                   \
-    static void SetParamiv(T &props, ALenum param, const int *vals);          \
-    static void SetParamf(T &props, ALenum param, float val);                 \
-    static void SetParamfv(T &props, ALenum param, const float *vals);        \
-    static void GetParami(const T &props, ALenum param, int *val);            \
-    static void GetParamiv(const T &props, ALenum param, int *vals);          \
-    static void GetParamf(const T &props, ALenum param, float *val);          \
-    static void GetParamfv(const T &props, ALenum param, float *vals);
-
-    DECL_HANDLER(std::monostate)
-    DECL_HANDLER(ReverbProps)
-    DECL_HANDLER(ChorusProps)
-    DECL_HANDLER(AutowahProps)
-    DECL_HANDLER(CompressorProps)
-    DECL_HANDLER(ConvolutionProps)
-    DECL_HANDLER(DedicatedDialogProps)
-    DECL_HANDLER(DedicatedLfeProps)
-    DECL_HANDLER(DistortionProps)
-    DECL_HANDLER(EchoProps)
-    DECL_HANDLER(EqualizerProps)
-    DECL_HANDLER(FlangerProps)
-    DECL_HANDLER(FshifterProps)
-    DECL_HANDLER(ModulatorProps)
-    DECL_HANDLER(PshifterProps)
-    DECL_HANDLER(VmorpherProps)
-#undef DECL_HANDLER
-
-    static void StdReverbSetParami(ReverbProps &props, ALenum param, int val);
-    static void StdReverbSetParamiv(ReverbProps &props, ALenum param, const int *vals);
-    static void StdReverbSetParamf(ReverbProps &props, ALenum param, float val);
-    static void StdReverbSetParamfv(ReverbProps &props, ALenum param, const float *vals);
-    static void StdReverbGetParami(const ReverbProps &props, ALenum param, int *val);
-    static void StdReverbGetParamiv(const ReverbProps &props, ALenum param, int *vals);
-    static void StdReverbGetParamf(const ReverbProps &props, ALenum param, float *val);
-    static void StdReverbGetParamfv(const ReverbProps &props, ALenum param, float *vals);
+#define DECL_HANDLER(N, T)                                                    \
+struct N {                                                                    \
+    using prop_type = T;                                                      \
+                                                                              \
+    static void SetParami(ALCcontext *context, prop_type &props, ALenum param, int val);           \
+    static void SetParamiv(ALCcontext *context, prop_type &props, ALenum param, const int *vals);  \
+    static void SetParamf(ALCcontext *context, prop_type &props, ALenum param, float val);         \
+    static void SetParamfv(ALCcontext *context, prop_type &props, ALenum param, const float *vals);\
+    static void GetParami(ALCcontext *context, const prop_type &props, ALenum param, int *val);    \
+    static void GetParamiv(ALCcontext *context, const prop_type &props, ALenum param, int *vals);  \
+    static void GetParamf(ALCcontext *context, const prop_type &props, ALenum param, float *val);  \
+    static void GetParamfv(ALCcontext *context, const prop_type &props, ALenum param, float *vals);\
 };
 };
-
-using effect_exception = al::context_error;
+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
 
 
 
 
 /* Default properties for the given effect types. */
 /* Default properties for the given effect types. */
-extern const EffectProps NullEffectProps;
-extern const EffectProps ReverbEffectProps;
-extern const EffectProps StdReverbEffectProps;
-extern const EffectProps AutowahEffectProps;
-extern const EffectProps ChorusEffectProps;
-extern const EffectProps CompressorEffectProps;
-extern const EffectProps DistortionEffectProps;
-extern const EffectProps EchoEffectProps;
-extern const EffectProps EqualizerEffectProps;
-extern const EffectProps FlangerEffectProps;
-extern const EffectProps FshifterEffectProps;
-extern const EffectProps ModulatorEffectProps;
-extern const EffectProps PshifterEffectProps;
-extern const EffectProps VmorpherEffectProps;
-extern const EffectProps DedicatedDialogEffectProps;
-extern const EffectProps DedicatedLfeEffectProps;
-extern const EffectProps ConvolutionEffectProps;
+DECL_HIDDEN extern const EffectProps NullEffectProps;
+DECL_HIDDEN extern const EffectProps ReverbEffectProps;
+DECL_HIDDEN extern const EffectProps StdReverbEffectProps;
+DECL_HIDDEN extern const EffectProps AutowahEffectProps;
+DECL_HIDDEN extern const EffectProps ChorusEffectProps;
+DECL_HIDDEN extern const EffectProps CompressorEffectProps;
+DECL_HIDDEN extern const EffectProps DistortionEffectProps;
+DECL_HIDDEN extern const EffectProps EchoEffectProps;
+DECL_HIDDEN extern const EffectProps EqualizerEffectProps;
+DECL_HIDDEN extern const EffectProps FlangerEffectProps;
+DECL_HIDDEN extern const EffectProps FshifterEffectProps;
+DECL_HIDDEN extern const EffectProps ModulatorEffectProps;
+DECL_HIDDEN extern const EffectProps PshifterEffectProps;
+DECL_HIDDEN extern const EffectProps VmorpherEffectProps;
+DECL_HIDDEN extern const EffectProps DedicatedDialogEffectProps;
+DECL_HIDDEN extern const EffectProps DedicatedLfeEffectProps;
+DECL_HIDDEN extern const EffectProps ConvolutionEffectProps;
 
 
 #endif /* AL_EFFECTS_EFFECTS_H */
 #endif /* AL_EFFECTS_EFFECTS_H */

+ 54 - 60
libs/openal-soft/al/effects/equalizer.cpp

@@ -4,11 +4,11 @@
 #include "AL/al.h"
 #include "AL/al.h"
 #include "AL/efx.h"
 #include "AL/efx.h"
 
 
-#include "alc/effects/base.h"
+#include "alc/context.h"
+#include "alnumeric.h"
 #include "effects.h"
 #include "effects.h"
 
 
-#ifdef ALSOFT_EAX
-#include "alnumeric.h"
+#if ALSOFT_EAX
 #include "al/eax/effect.h"
 #include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #include "al/eax/utils.h"
@@ -37,115 +37,109 @@ constexpr EffectProps genDefaultProps() noexcept
 
 
 const EffectProps EqualizerEffectProps{genDefaultProps()};
 const EffectProps EqualizerEffectProps{genDefaultProps()};
 
 
-void EffectHandler::SetParami(EqualizerProps&, ALenum param, int)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; }
-void EffectHandler::SetParamiv(EqualizerProps&, ALenum param, const int*)
-{
-    throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x",
-        param};
-}
-void EffectHandler::SetParamf(EqualizerProps &props, ALenum param, float val)
+void EqualizerEffectHandler::SetParami(ALCcontext *context, EqualizerProps&, ALenum param, int)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid equalizer integer property {:#04x}", as_unsigned(param)); }
+void EqualizerEffectHandler::SetParamiv(ALCcontext *context, EqualizerProps&, ALenum param, const int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid equalizer integer-vector property {:#04x}", as_unsigned(param)); }
+void EqualizerEffectHandler::SetParamf(ALCcontext *context, EqualizerProps &props, ALenum param, float val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_EQUALIZER_LOW_GAIN:
     case AL_EQUALIZER_LOW_GAIN:
         if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE, "Equalizer low-band gain out of range");
         props.LowGain = val;
         props.LowGain = val;
-        break;
+        return;
 
 
     case AL_EQUALIZER_LOW_CUTOFF:
     case AL_EQUALIZER_LOW_CUTOFF:
         if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE, "Equalizer low-band cutoff out of range");
         props.LowCutoff = val;
         props.LowCutoff = val;
-        break;
+        return;
 
 
     case AL_EQUALIZER_MID1_GAIN:
     case AL_EQUALIZER_MID1_GAIN:
         if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE, "Equalizer mid1-band gain out of range");
         props.Mid1Gain = val;
         props.Mid1Gain = val;
-        break;
+        return;
 
 
     case AL_EQUALIZER_MID1_CENTER:
     case AL_EQUALIZER_MID1_CENTER:
         if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE, "Equalizer mid1-band center out of range");
         props.Mid1Center = val;
         props.Mid1Center = val;
-        break;
+        return;
 
 
     case AL_EQUALIZER_MID1_WIDTH:
     case AL_EQUALIZER_MID1_WIDTH:
         if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE, "Equalizer mid1-band width out of range");
         props.Mid1Width = val;
         props.Mid1Width = val;
-        break;
+        return;
 
 
     case AL_EQUALIZER_MID2_GAIN:
     case AL_EQUALIZER_MID2_GAIN:
         if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE, "Equalizer mid2-band gain out of range");
         props.Mid2Gain = val;
         props.Mid2Gain = val;
-        break;
+        return;
 
 
     case AL_EQUALIZER_MID2_CENTER:
     case AL_EQUALIZER_MID2_CENTER:
         if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE, "Equalizer mid2-band center out of range");
         props.Mid2Center = val;
         props.Mid2Center = val;
-        break;
+        return;
 
 
     case AL_EQUALIZER_MID2_WIDTH:
     case AL_EQUALIZER_MID2_WIDTH:
         if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE, "Equalizer mid2-band width out of range");
         props.Mid2Width = val;
         props.Mid2Width = val;
-        break;
+        return;
 
 
     case AL_EQUALIZER_HIGH_GAIN:
     case AL_EQUALIZER_HIGH_GAIN:
         if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE, "Equalizer high-band gain out of range");
         props.HighGain = val;
         props.HighGain = val;
-        break;
+        return;
 
 
     case AL_EQUALIZER_HIGH_CUTOFF:
     case AL_EQUALIZER_HIGH_CUTOFF:
         if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE, "Equalizer high-band cutoff out of range");
         props.HighCutoff = val;
         props.HighCutoff = val;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param};
+        return;
     }
     }
-}
-void EffectHandler::SetParamfv(EqualizerProps &props, ALenum param, const float *vals)
-{ SetParamf(props, param, *vals); }
 
 
-void EffectHandler::GetParami(const EqualizerProps&, ALenum param, int*)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; }
-void EffectHandler::GetParamiv(const EqualizerProps&, ALenum param, int*)
-{
-    throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x",
-        param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid equalizer float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamf(const EqualizerProps &props, ALenum param, float *val)
+void EqualizerEffectHandler::SetParamfv(ALCcontext *context, EqualizerProps &props, ALenum param, const float *vals)
+{ SetParamf(context, props, param, *vals); }
+
+void EqualizerEffectHandler::GetParami(ALCcontext *context, const EqualizerProps&, ALenum param, int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid equalizer integer property {:#04x}", as_unsigned(param)); }
+void EqualizerEffectHandler::GetParamiv(ALCcontext *context, const EqualizerProps&, ALenum param, int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid equalizer integer-vector property {:#04x}", as_unsigned(param)); }
+void EqualizerEffectHandler::GetParamf(ALCcontext *context, const EqualizerProps &props, ALenum param, float *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
-    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};
+    case AL_EQUALIZER_LOW_GAIN: *val = props.LowGain; return;
+    case AL_EQUALIZER_LOW_CUTOFF: *val = props.LowCutoff; return;
+    case AL_EQUALIZER_MID1_GAIN: *val = props.Mid1Gain; return;
+    case AL_EQUALIZER_MID1_CENTER: *val = props.Mid1Center; return;
+    case AL_EQUALIZER_MID1_WIDTH: *val = props.Mid1Width; return;
+    case AL_EQUALIZER_MID2_GAIN: *val = props.Mid2Gain; return;
+    case AL_EQUALIZER_MID2_CENTER: *val = props.Mid2Center; return;
+    case AL_EQUALIZER_MID2_WIDTH: *val = props.Mid2Width; return;
+    case AL_EQUALIZER_HIGH_GAIN: *val = props.HighGain; return;
+    case AL_EQUALIZER_HIGH_CUTOFF: *val = props.HighCutoff; return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid equalizer float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamfv(const EqualizerProps &props, ALenum param, float *vals)
-{ GetParamf(props, param, vals); }
+void EqualizerEffectHandler::GetParamfv(ALCcontext *context, const EqualizerProps &props, ALenum param, float *vals)
+{ GetParamf(context, props, param, vals); }
 
 
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 namespace {
 namespace {
 
 
 using EqualizerCommitter = EaxCommitter<EaxEqualizerCommitter>;
 using EqualizerCommitter = EaxCommitter<EaxEqualizerCommitter>;

+ 41 - 46
libs/openal-soft/al/effects/fshifter.cpp

@@ -7,12 +7,13 @@
 #include "AL/al.h"
 #include "AL/al.h"
 #include "AL/efx.h"
 #include "AL/efx.h"
 
 
-#include "alc/effects/base.h"
+#include "alc/context.h"
+#include "alnumeric.h"
 #include "effects.h"
 #include "effects.h"
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 #include <cassert>
 #include <cassert>
-#include "alnumeric.h"
+
 #include "al/eax/effect.h"
 #include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #include "al/eax/utils.h"
@@ -39,7 +40,7 @@ constexpr ALenum EnumFromDirection(FShifterDirection dir)
     case FShifterDirection::Up: return AL_FREQUENCY_SHIFTER_DIRECTION_UP;
     case FShifterDirection::Up: return AL_FREQUENCY_SHIFTER_DIRECTION_UP;
     case FShifterDirection::Off: return AL_FREQUENCY_SHIFTER_DIRECTION_OFF;
     case FShifterDirection::Off: return AL_FREQUENCY_SHIFTER_DIRECTION_OFF;
     }
     }
-    throw std::runtime_error{"Invalid direction: "+std::to_string(static_cast<int>(dir))};
+    throw std::runtime_error{fmt::format("Invalid direction: {}", int{al::to_underlying(dir)})};
 }
 }
 
 
 constexpr EffectProps genDefaultProps() noexcept
 constexpr EffectProps genDefaultProps() noexcept
@@ -55,7 +56,7 @@ constexpr EffectProps genDefaultProps() noexcept
 
 
 const EffectProps FshifterEffectProps{genDefaultProps()};
 const EffectProps FshifterEffectProps{genDefaultProps()};
 
 
-void EffectHandler::SetParami(FshifterProps &props, ALenum param, int val)
+void FshifterEffectHandler::SetParami(ALCcontext *context, FshifterProps &props, ALenum param, int val)
 {
 {
     switch(param)
     switch(param)
     {
     {
@@ -63,81 +64,75 @@ void EffectHandler::SetParami(FshifterProps &props, ALenum param, int val)
         if(auto diropt = DirectionFromEmum(val))
         if(auto diropt = DirectionFromEmum(val))
             props.LeftDirection = *diropt;
             props.LeftDirection = *diropt;
         else
         else
-            throw effect_exception{AL_INVALID_VALUE,
-                "Unsupported frequency shifter left direction: 0x%04x", val};
-        break;
+            context->throw_error(AL_INVALID_VALUE,
+                "Unsupported frequency shifter left direction: {:#04x}", as_unsigned(val));
+        return;
 
 
     case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
     case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
         if(auto diropt = DirectionFromEmum(val))
         if(auto diropt = DirectionFromEmum(val))
             props.RightDirection = *diropt;
             props.RightDirection = *diropt;
         else
         else
-            throw effect_exception{AL_INVALID_VALUE,
-                "Unsupported frequency shifter right direction: 0x%04x", val};
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM,
-            "Invalid frequency shifter integer property 0x%04x", param};
+            context->throw_error(AL_INVALID_VALUE,
+                "Unsupported frequency shifter right direction: {:#04x}", as_unsigned(val));
+        return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid frequency shifter integer property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::SetParamiv(FshifterProps &props, ALenum param, const int *vals)
-{ SetParami(props, param, *vals); }
+void FshifterEffectHandler::SetParamiv(ALCcontext *context, FshifterProps &props, ALenum param, const int *vals)
+{ SetParami(context, props, param, *vals); }
 
 
-void EffectHandler::SetParamf(FshifterProps &props, ALenum param, float val)
+void FshifterEffectHandler::SetParamf(ALCcontext *context, FshifterProps &props, ALenum param, float val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_FREQUENCY_SHIFTER_FREQUENCY:
     case AL_FREQUENCY_SHIFTER_FREQUENCY:
         if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE, "Frequency shifter frequency out of range");
         props.Frequency = val;
         props.Frequency = val;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
-            param};
+        return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid frequency shifter float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::SetParamfv(FshifterProps &props, ALenum param, const float *vals)
-{ SetParamf(props, param, *vals); }
+void FshifterEffectHandler::SetParamfv(ALCcontext *context, FshifterProps &props, ALenum param, const float *vals)
+{ SetParamf(context, props, param, *vals); }
 
 
-void EffectHandler::GetParami(const FshifterProps &props, ALenum param, int *val)
+void FshifterEffectHandler::GetParami(ALCcontext *context, const FshifterProps &props, ALenum param, int *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
     case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
         *val = EnumFromDirection(props.LeftDirection);
         *val = EnumFromDirection(props.LeftDirection);
-        break;
+        return;
     case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
     case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
         *val = EnumFromDirection(props.RightDirection);
         *val = EnumFromDirection(props.RightDirection);
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM,
-            "Invalid frequency shifter integer property 0x%04x", param};
+        return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid frequency shifter integer property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamiv(const FshifterProps &props, ALenum param, int *vals)
-{ GetParami(props, param, vals); }
+void FshifterEffectHandler::GetParamiv(ALCcontext *context, const FshifterProps &props, ALenum param, int *vals)
+{ GetParami(context, props, param, vals); }
 
 
-void EffectHandler::GetParamf(const FshifterProps &props, ALenum param, float *val)
+void FshifterEffectHandler::GetParamf(ALCcontext *context, const FshifterProps &props, ALenum param, float *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
-    case AL_FREQUENCY_SHIFTER_FREQUENCY:
-        *val = props.Frequency;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
-            param};
+    case AL_FREQUENCY_SHIFTER_FREQUENCY: *val = props.Frequency; return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid frequency shifter float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamfv(const FshifterProps &props, ALenum param, float *vals)
-{ GetParamf(props, param, vals); }
+void FshifterEffectHandler::GetParamfv(ALCcontext *context, const FshifterProps &props, ALenum param, float *vals)
+{ GetParamf(context, props, param, vals); }
 
 
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 namespace {
 namespace {
 
 
 using FrequencyShifterCommitter = EaxCommitter<EaxFrequencyShifterCommitter>;
 using FrequencyShifterCommitter = EaxCommitter<EaxFrequencyShifterCommitter>;

+ 46 - 45
libs/openal-soft/al/effects/modulator.cpp

@@ -7,12 +7,13 @@
 #include "AL/al.h"
 #include "AL/al.h"
 #include "AL/efx.h"
 #include "AL/efx.h"
 
 
-#include "alc/effects/base.h"
+#include "alc/context.h"
+#include "alnumeric.h"
 #include "effects.h"
 #include "effects.h"
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 #include <cassert>
 #include <cassert>
-#include "alnumeric.h"
+
 #include "al/eax/effect.h"
 #include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #include "al/eax/utils.h"
@@ -39,8 +40,8 @@ constexpr ALenum EnumFromWaveform(ModulatorWaveform type)
     case ModulatorWaveform::Sawtooth: return AL_RING_MODULATOR_SAWTOOTH;
     case ModulatorWaveform::Sawtooth: return AL_RING_MODULATOR_SAWTOOTH;
     case ModulatorWaveform::Square: return AL_RING_MODULATOR_SQUARE;
     case ModulatorWaveform::Square: return AL_RING_MODULATOR_SQUARE;
     }
     }
-    throw std::runtime_error{"Invalid modulator waveform: " +
-        std::to_string(static_cast<int>(type))};
+    throw std::runtime_error{fmt::format("Invalid modulator waveform: {}",
+        int{al::to_underlying(type)})};
 }
 }
 
 
 constexpr EffectProps genDefaultProps() noexcept
 constexpr EffectProps genDefaultProps() noexcept
@@ -56,84 +57,84 @@ constexpr EffectProps genDefaultProps() noexcept
 
 
 const EffectProps ModulatorEffectProps{genDefaultProps()};
 const EffectProps ModulatorEffectProps{genDefaultProps()};
 
 
-void EffectHandler::SetParami(ModulatorProps &props, ALenum param, int val)
+void ModulatorEffectHandler::SetParami(ALCcontext *context, ModulatorProps &props, ALenum param, int val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_RING_MODULATOR_FREQUENCY:
     case AL_RING_MODULATOR_FREQUENCY:
     case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
     case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
-        SetParamf(props, param, static_cast<float>(val));
-        break;
+        SetParamf(context, props, param, static_cast<float>(val));
+        return;
 
 
     case AL_RING_MODULATOR_WAVEFORM:
     case AL_RING_MODULATOR_WAVEFORM:
         if(auto formopt = WaveformFromEmum(val))
         if(auto formopt = WaveformFromEmum(val))
             props.Waveform = *formopt;
             props.Waveform = *formopt;
         else
         else
-            throw effect_exception{AL_INVALID_VALUE, "Invalid modulator waveform: 0x%04x", val};
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x",
-            param};
+            context->throw_error(AL_INVALID_VALUE, "Invalid modulator waveform: {:#04x}",
+                as_unsigned(val));
+        return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid modulator integer property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::SetParamiv(ModulatorProps &props, ALenum param, const int *vals)
-{ SetParami(props, param, *vals); }
+void ModulatorEffectHandler::SetParamiv(ALCcontext *context, ModulatorProps &props, ALenum param, const int *vals)
+{ SetParami(context, props, param, *vals); }
 
 
-void EffectHandler::SetParamf(ModulatorProps &props, ALenum param, float val)
+void ModulatorEffectHandler::SetParamf(ALCcontext *context, ModulatorProps &props, ALenum param, float val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_RING_MODULATOR_FREQUENCY:
     case AL_RING_MODULATOR_FREQUENCY:
         if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_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};
+            context->throw_error(AL_INVALID_VALUE, "Modulator frequency out of range: {:f}", val);
         props.Frequency = val;
         props.Frequency = val;
-        break;
+        return;
 
 
     case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
     case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
         if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_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};
+            context->throw_error(AL_INVALID_VALUE, "Modulator high-pass cutoff out of range: {:f}",
+                val);
         props.HighPassCutoff = val;
         props.HighPassCutoff = val;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
+        return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid modulator float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::SetParamfv(ModulatorProps &props, ALenum param, const float *vals)
-{ SetParamf(props, param, *vals); }
+void ModulatorEffectHandler::SetParamfv(ALCcontext *context, ModulatorProps &props, ALenum param, const float *vals)
+{ SetParamf(context, props, param, *vals); }
 
 
-void EffectHandler::GetParami(const ModulatorProps &props, ALenum param, int *val)
+void ModulatorEffectHandler::GetParami(ALCcontext *context, const ModulatorProps &props, ALenum param, int *val)
 {
 {
     switch(param)
     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};
+    case AL_RING_MODULATOR_FREQUENCY: *val = static_cast<int>(props.Frequency); return;
+    case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = static_cast<int>(props.HighPassCutoff); return;
+    case AL_RING_MODULATOR_WAVEFORM: *val = EnumFromWaveform(props.Waveform); return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid modulator integer property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamiv(const ModulatorProps &props, ALenum param, int *vals)
-{ GetParami(props, param, vals); }
-void EffectHandler::GetParamf(const ModulatorProps &props, ALenum param, float *val)
+void ModulatorEffectHandler::GetParamiv(ALCcontext *context, const ModulatorProps &props, ALenum param, int *vals)
+{ GetParami(context, props, param, vals); }
+void ModulatorEffectHandler::GetParamf(ALCcontext *context, const ModulatorProps &props, ALenum param, float *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
-    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};
+    case AL_RING_MODULATOR_FREQUENCY: *val = props.Frequency; return;
+    case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = props.HighPassCutoff; return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid modulator float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamfv(const ModulatorProps &props, ALenum param, float *vals)
-{ GetParamf(props, param, vals); }
+void ModulatorEffectHandler::GetParamfv(ALCcontext *context, const ModulatorProps &props, ALenum param, float *vals)
+{ GetParamf(context, props, param, vals); }
 
 
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 namespace {
 namespace {
 
 
 using ModulatorCommitter = EaxCommitter<EaxModulatorCommitter>;
 using ModulatorCommitter = EaxCommitter<EaxModulatorCommitter>;

+ 24 - 55
libs/openal-soft/al/effects/null.cpp

@@ -4,10 +4,11 @@
 #include "AL/al.h"
 #include "AL/al.h"
 #include "AL/efx.h"
 #include "AL/efx.h"
 
 
-#include "alc/effects/base.h"
+#include "alc/context.h"
+#include "alnumeric.h"
 #include "effects.h"
 #include "effects.h"
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 #include "al/eax/effect.h"
 #include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/exception.h"
 #endif // ALSOFT_EAX
 #endif // ALSOFT_EAX
@@ -24,78 +25,46 @@ constexpr EffectProps genDefaultProps() noexcept
 
 
 const EffectProps NullEffectProps{genDefaultProps()};
 const EffectProps NullEffectProps{genDefaultProps()};
 
 
-void EffectHandler::SetParami(std::monostate& /*props*/, ALenum param, int /*val*/)
+void NullEffectHandler::SetParami(ALCcontext *context, std::monostate& /*props*/, ALenum param, int /*val*/)
 {
 {
-    switch(param)
-    {
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
-            param};
-    }
+    context->throw_error(AL_INVALID_ENUM, "Invalid null effect integer property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::SetParamiv(std::monostate &props, ALenum param, const int *vals)
+void NullEffectHandler::SetParamiv(ALCcontext *context, std::monostate &props, ALenum param, const int *vals)
 {
 {
-    switch(param)
-    {
-    default:
-        SetParami(props, param, *vals);
-    }
+    SetParami(context, props, param, *vals);
 }
 }
-void EffectHandler::SetParamf(std::monostate& /*props*/, ALenum param, float /*val*/)
+void NullEffectHandler::SetParamf(ALCcontext *context, std::monostate& /*props*/, ALenum param, float /*val*/)
 {
 {
-    switch(param)
-    {
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
-            param};
-    }
+    context->throw_error(AL_INVALID_ENUM, "Invalid null effect float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::SetParamfv(std::monostate &props, ALenum param, const float *vals)
+void NullEffectHandler::SetParamfv(ALCcontext *context, std::monostate &props, ALenum param, const float *vals)
 {
 {
-    switch(param)
-    {
-    default:
-        SetParamf(props, param, *vals);
-    }
+    SetParamf(context, props, param, *vals);
 }
 }
 
 
-void EffectHandler::GetParami(const std::monostate& /*props*/, ALenum param, int* /*val*/)
+void NullEffectHandler::GetParami(ALCcontext *context, const std::monostate& /*props*/, ALenum param, int* /*val*/)
 {
 {
-    switch(param)
-    {
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
-            param};
-    }
+    context->throw_error(AL_INVALID_ENUM, "Invalid null effect integer property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamiv(const std::monostate &props, ALenum param, int *vals)
+void NullEffectHandler::GetParamiv(ALCcontext *context, const std::monostate &props, ALenum param, int *vals)
 {
 {
-    switch(param)
-    {
-    default:
-        GetParami(props, param, vals);
-    }
+    GetParami(context, props, param, vals);
 }
 }
-void EffectHandler::GetParamf(const std::monostate& /*props*/, ALenum param, float* /*val*/)
+void NullEffectHandler::GetParamf(ALCcontext *context, const std::monostate& /*props*/, ALenum param, float* /*val*/)
 {
 {
-    switch(param)
-    {
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
-            param};
-    }
+    context->throw_error(AL_INVALID_ENUM, "Invalid null effect float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamfv(const std::monostate &props, ALenum param, float *vals)
+void NullEffectHandler::GetParamfv(ALCcontext *context, const std::monostate &props, ALenum param, float *vals)
 {
 {
-    switch(param)
-    {
-    default:
-        GetParamf(props, param, vals);
-    }
+    GetParamf(context, props, param, vals);
 }
 }
 
 
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 namespace {
 namespace {
 
 
 using NullCommitter = EaxCommitter<EaxNullCommitter>;
 using NullCommitter = EaxCommitter<EaxNullCommitter>;

+ 30 - 38
libs/openal-soft/al/effects/pshifter.cpp

@@ -4,11 +4,11 @@
 #include "AL/al.h"
 #include "AL/al.h"
 #include "AL/efx.h"
 #include "AL/efx.h"
 
 
-#include "alc/effects/base.h"
+#include "alc/context.h"
+#include "alnumeric.h"
 #include "effects.h"
 #include "effects.h"
 
 
-#ifdef ALSOFT_EAX
-#include "alnumeric.h"
+#if ALSOFT_EAX
 #include "al/eax/effect.h"
 #include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #include "al/eax/utils.h"
@@ -29,63 +29,55 @@ constexpr EffectProps genDefaultProps() noexcept
 
 
 const EffectProps PshifterEffectProps{genDefaultProps()};
 const EffectProps PshifterEffectProps{genDefaultProps()};
 
 
-void EffectHandler::SetParami(PshifterProps &props, ALenum param, int val)
+void PshifterEffectHandler::SetParami(ALCcontext *context, PshifterProps &props, ALenum param, int val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_PITCH_SHIFTER_COARSE_TUNE:
     case AL_PITCH_SHIFTER_COARSE_TUNE:
         if(!(val >= AL_PITCH_SHIFTER_MIN_COARSE_TUNE && val <= AL_PITCH_SHIFTER_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE, "Pitch shifter coarse tune out of range");
         props.CoarseTune = val;
         props.CoarseTune = val;
-        break;
+        return;
 
 
     case AL_PITCH_SHIFTER_FINE_TUNE:
     case AL_PITCH_SHIFTER_FINE_TUNE:
         if(!(val >= AL_PITCH_SHIFTER_MIN_FINE_TUNE && val <= AL_PITCH_SHIFTER_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE, "Pitch shifter fine tune out of range");
         props.FineTune = val;
         props.FineTune = val;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x",
-            param};
+        return;
     }
     }
-}
-void EffectHandler::SetParamiv(PshifterProps &props, ALenum param, const int *vals)
-{ SetParami(props, param, *vals); }
 
 
-void EffectHandler::SetParamf(PshifterProps&, ALenum param, float)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
-void EffectHandler::SetParamfv(PshifterProps&, ALenum param, const float*)
-{
-    throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x",
-        param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid pitch shifter integer property {:#04x}",
+        as_unsigned(param));
 }
 }
+void PshifterEffectHandler::SetParamiv(ALCcontext *context, PshifterProps &props, ALenum param, const int *vals)
+{ SetParami(context, props, param, *vals); }
 
 
-void EffectHandler::GetParami(const PshifterProps &props, ALenum param, int *val)
+void PshifterEffectHandler::SetParamf(ALCcontext *context, PshifterProps&, ALenum param, float)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid pitch shifter float property {:#04x}", as_unsigned(param)); }
+void PshifterEffectHandler::SetParamfv(ALCcontext *context, PshifterProps &props, ALenum param, const float *vals)
+{ SetParamf(context, props, param, *vals); }
+
+void PshifterEffectHandler::GetParami(ALCcontext *context, const PshifterProps &props, ALenum param, int *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
-    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};
+    case AL_PITCH_SHIFTER_COARSE_TUNE: *val = props.CoarseTune; return;
+    case AL_PITCH_SHIFTER_FINE_TUNE: *val = props.FineTune; return;
     }
     }
-}
-void EffectHandler::GetParamiv(const PshifterProps &props, ALenum param, int *vals)
-{ GetParami(props, param, vals); }
 
 
-void EffectHandler::GetParamf(const PshifterProps&, ALenum param, float*)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
-void EffectHandler::GetParamfv(const PshifterProps&, ALenum param, float*)
-{
-    throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x",
-        param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid pitch shifter integer property {:#04x}",
+        as_unsigned(param));
 }
 }
+void PshifterEffectHandler::GetParamiv(ALCcontext *context, const PshifterProps &props, ALenum param, int *vals)
+{ GetParami(context, props, param, vals); }
+
+void PshifterEffectHandler::GetParamf(ALCcontext *context, const PshifterProps&, ALenum param, float*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid pitch shifter float property {:#04x}", as_unsigned(param)); }
+void PshifterEffectHandler::GetParamfv(ALCcontext *context, const PshifterProps &props, ALenum param, float *vals)
+{ GetParamf(context, props, param, vals); }
 
 
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 namespace {
 namespace {
 
 
 using PitchShifterCommitter = EaxCommitter<EaxPitchShifterCommitter>;
 using PitchShifterCommitter = EaxCommitter<EaxPitchShifterCommitter>;

+ 192 - 181
libs/openal-soft/al/effects/reverb.cpp

@@ -9,13 +9,17 @@
 #include "AL/al.h"
 #include "AL/al.h"
 #include "AL/efx.h"
 #include "AL/efx.h"
 
 
+#include "alc/context.h"
 #include "alnumeric.h"
 #include "alnumeric.h"
 #include "alspan.h"
 #include "alspan.h"
-#include "core/effects/base.h"
+#include "core/logging.h"
 #include "effects.h"
 #include "effects.h"
+#include "fmt/ranges.h"
+#include "opthelpers.h"
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 #include <cassert>
 #include <cassert>
+
 #include "al/eax/api.h"
 #include "al/eax/api.h"
 #include "al/eax/call.h"
 #include "al/eax/call.h"
 #include "al/eax/effect.h"
 #include "al/eax/effect.h"
@@ -92,152 +96,149 @@ constexpr EffectProps genDefaultStdProps() noexcept
 
 
 const EffectProps ReverbEffectProps{genDefaultProps()};
 const EffectProps ReverbEffectProps{genDefaultProps()};
 
 
-void EffectHandler::SetParami(ReverbProps &props, ALenum param, int val)
+void ReverbEffectHandler::SetParami(ALCcontext *context, ReverbProps &props, ALenum param, int val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_EAXREVERB_DECAY_HFLIMIT:
     case AL_EAXREVERB_DECAY_HFLIMIT:
         if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
         if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hflimit out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb decay hflimit out of range");
         props.DecayHFLimit = val != AL_FALSE;
         props.DecayHFLimit = val != AL_FALSE;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
-            param};
+        return;
     }
     }
+    context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb integer property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::SetParamiv(ReverbProps &props, ALenum param, const int *vals)
-{ SetParami(props, param, *vals); }
-void EffectHandler::SetParamf(ReverbProps &props, ALenum param, float val)
+void ReverbEffectHandler::SetParamiv(ALCcontext *context, ReverbProps &props, ALenum param, const int *vals)
+{ SetParami(context, props, param, *vals); }
+void ReverbEffectHandler::SetParamf(ALCcontext *context, ReverbProps &props, ALenum param, float val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_EAXREVERB_DENSITY:
     case AL_EAXREVERB_DENSITY:
         if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
         if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb density out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb density out of range");
         props.Density = val;
         props.Density = val;
-        break;
+        return;
 
 
     case AL_EAXREVERB_DIFFUSION:
     case AL_EAXREVERB_DIFFUSION:
         if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
         if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb diffusion out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb diffusion out of range");
         props.Diffusion = val;
         props.Diffusion = val;
-        break;
+        return;
 
 
     case AL_EAXREVERB_GAIN:
     case AL_EAXREVERB_GAIN:
         if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
         if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gain out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb gain out of range");
         props.Gain = val;
         props.Gain = val;
-        break;
+        return;
 
 
     case AL_EAXREVERB_GAINHF:
     case AL_EAXREVERB_GAINHF:
         if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
         if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gainhf out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb gainhf out of range");
         props.GainHF = val;
         props.GainHF = val;
-        break;
+        return;
 
 
     case AL_EAXREVERB_GAINLF:
     case AL_EAXREVERB_GAINLF:
         if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
         if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gainlf out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb gainlf out of range");
         props.GainLF = val;
         props.GainLF = val;
-        break;
+        return;
 
 
     case AL_EAXREVERB_DECAY_TIME:
     case AL_EAXREVERB_DECAY_TIME:
         if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
         if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay time out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb decay time out of range");
         props.DecayTime = val;
         props.DecayTime = val;
-        break;
+        return;
 
 
     case AL_EAXREVERB_DECAY_HFRATIO:
     case AL_EAXREVERB_DECAY_HFRATIO:
         if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
         if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hfratio out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb decay hfratio out of range");
         props.DecayHFRatio = val;
         props.DecayHFRatio = val;
-        break;
+        return;
 
 
     case AL_EAXREVERB_DECAY_LFRATIO:
     case AL_EAXREVERB_DECAY_LFRATIO:
         if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
         if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay lfratio out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb decay lfratio out of range");
         props.DecayLFRatio = val;
         props.DecayLFRatio = val;
-        break;
+        return;
 
 
     case AL_EAXREVERB_REFLECTIONS_GAIN:
     case AL_EAXREVERB_REFLECTIONS_GAIN:
         if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
         if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections gain out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb reflections gain out of range");
         props.ReflectionsGain = val;
         props.ReflectionsGain = val;
-        break;
+        return;
 
 
     case AL_EAXREVERB_REFLECTIONS_DELAY:
     case AL_EAXREVERB_REFLECTIONS_DELAY:
         if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
         if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections delay out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb reflections delay out of range");
         props.ReflectionsDelay = val;
         props.ReflectionsDelay = val;
-        break;
+        return;
 
 
     case AL_EAXREVERB_LATE_REVERB_GAIN:
     case AL_EAXREVERB_LATE_REVERB_GAIN:
         if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
         if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb gain out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb late reverb gain out of range");
         props.LateReverbGain = val;
         props.LateReverbGain = val;
-        break;
+        return;
 
 
     case AL_EAXREVERB_LATE_REVERB_DELAY:
     case AL_EAXREVERB_LATE_REVERB_DELAY:
         if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
         if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb delay out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb late reverb delay out of range");
         props.LateReverbDelay = val;
         props.LateReverbDelay = val;
-        break;
+        return;
 
 
     case AL_EAXREVERB_ECHO_TIME:
     case AL_EAXREVERB_ECHO_TIME:
         if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
         if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb echo time out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb echo time out of range");
         props.EchoTime = val;
         props.EchoTime = val;
-        break;
+        return;
 
 
     case AL_EAXREVERB_ECHO_DEPTH:
     case AL_EAXREVERB_ECHO_DEPTH:
         if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
         if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb echo depth out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb echo depth out of range");
         props.EchoDepth = val;
         props.EchoDepth = val;
-        break;
+        return;
 
 
     case AL_EAXREVERB_MODULATION_TIME:
     case AL_EAXREVERB_MODULATION_TIME:
         if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
         if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb modulation time out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb modulation time out of range");
         props.ModulationTime = val;
         props.ModulationTime = val;
-        break;
+        return;
 
 
     case AL_EAXREVERB_MODULATION_DEPTH:
     case AL_EAXREVERB_MODULATION_DEPTH:
         if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
         if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb modulation depth out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb modulation depth out of range");
         props.ModulationDepth = val;
         props.ModulationDepth = val;
-        break;
+        return;
 
 
     case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
     case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
         if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
         if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb air absorption gainhf out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb air absorption gainhf out of range");
         props.AirAbsorptionGainHF = val;
         props.AirAbsorptionGainHF = val;
-        break;
+        return;
 
 
     case AL_EAXREVERB_HFREFERENCE:
     case AL_EAXREVERB_HFREFERENCE:
         if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
         if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb hfreference out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb hfreference out of range");
         props.HFReference = val;
         props.HFReference = val;
-        break;
+        return;
 
 
     case AL_EAXREVERB_LFREFERENCE:
     case AL_EAXREVERB_LFREFERENCE:
         if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
         if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb lfreference out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb lfreference out of range");
         props.LFReference = val;
         props.LFReference = val;
-        break;
+        return;
 
 
     case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
     case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
         if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
         if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb room rolloff factor out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb room rolloff factor out of range");
         props.RoomRolloffFactor = val;
         props.RoomRolloffFactor = val;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param};
+        return;
     }
     }
+    context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::SetParamfv(ReverbProps &props, ALenum param, const float *vals)
+void ReverbEffectHandler::SetParamfv(ALCcontext *context, ReverbProps &props, ALenum param, const float *vals)
 {
 {
     static constexpr auto finite_checker = [](float f) -> bool { return std::isfinite(f); };
     static constexpr auto finite_checker = [](float f) -> bool { return std::isfinite(f); };
     al::span<const float> values;
     al::span<const float> values;
@@ -246,64 +247,60 @@ void EffectHandler::SetParamfv(ReverbProps &props, ALenum param, const float *va
     case AL_EAXREVERB_REFLECTIONS_PAN:
     case AL_EAXREVERB_REFLECTIONS_PAN:
         values = {vals, 3_uz};
         values = {vals, 3_uz};
         if(!std::all_of(values.cbegin(), values.cend(), finite_checker))
         if(!std::all_of(values.cbegin(), values.cend(), finite_checker))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections pan out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb reflections pan out of range");
         std::copy(values.cbegin(), values.cend(), props.ReflectionsPan.begin());
         std::copy(values.cbegin(), values.cend(), props.ReflectionsPan.begin());
-        break;
+        return;
     case AL_EAXREVERB_LATE_REVERB_PAN:
     case AL_EAXREVERB_LATE_REVERB_PAN:
         values = {vals, 3_uz};
         values = {vals, 3_uz};
         if(!std::all_of(values.cbegin(), values.cend(), finite_checker))
         if(!std::all_of(values.cbegin(), values.cend(), finite_checker))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb pan out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb late reverb pan out of range");
         std::copy(values.cbegin(), values.cend(), props.LateReverbPan.begin());
         std::copy(values.cbegin(), values.cend(), props.LateReverbPan.begin());
-        break;
-
-    default:
-        SetParamf(props, param, *vals);
-        break;
+        return;
     }
     }
+    SetParamf(context, props, param, *vals);
 }
 }
 
 
-void EffectHandler::GetParami(const ReverbProps &props, ALenum param, int *val)
+void ReverbEffectHandler::GetParami(ALCcontext *context, const ReverbProps &props, ALenum param, int *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
-    case AL_EAXREVERB_DECAY_HFLIMIT: *val = props.DecayHFLimit; break;
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
-            param};
+    case AL_EAXREVERB_DECAY_HFLIMIT: *val = props.DecayHFLimit; return;
     }
     }
+    context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb integer property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamiv(const ReverbProps &props, ALenum param, int *vals)
-{ GetParami(props, param, vals); }
-void EffectHandler::GetParamf(const ReverbProps &props, ALenum param, float *val)
+void ReverbEffectHandler::GetParamiv(ALCcontext *context, const ReverbProps &props, ALenum param, int *vals)
+{ GetParami(context, props, param, vals); }
+void ReverbEffectHandler::GetParamf(ALCcontext *context, const ReverbProps &props, ALenum param, float *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
-    case AL_EAXREVERB_DENSITY: *val = props.Density; break;
-    case AL_EAXREVERB_DIFFUSION: *val = props.Diffusion; break;
-    case AL_EAXREVERB_GAIN: *val = props.Gain; break;
-    case AL_EAXREVERB_GAINHF: *val = props.GainHF; break;
-    case AL_EAXREVERB_GAINLF: *val = props.GainLF; break;
-    case AL_EAXREVERB_DECAY_TIME: *val = props.DecayTime; break;
-    case AL_EAXREVERB_DECAY_HFRATIO: *val = props.DecayHFRatio; break;
-    case AL_EAXREVERB_DECAY_LFRATIO: *val = props.DecayLFRatio; break;
-    case AL_EAXREVERB_REFLECTIONS_GAIN: *val = props.ReflectionsGain; break;
-    case AL_EAXREVERB_REFLECTIONS_DELAY: *val = props.ReflectionsDelay; break;
-    case AL_EAXREVERB_LATE_REVERB_GAIN: *val = props.LateReverbGain; break;
-    case AL_EAXREVERB_LATE_REVERB_DELAY: *val = props.LateReverbDelay; break;
-    case AL_EAXREVERB_ECHO_TIME: *val = props.EchoTime; break;
-    case AL_EAXREVERB_ECHO_DEPTH: *val = props.EchoDepth; break;
-    case AL_EAXREVERB_MODULATION_TIME: *val = props.ModulationTime; break;
-    case AL_EAXREVERB_MODULATION_DEPTH: *val = props.ModulationDepth; break;
-    case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: *val = props.AirAbsorptionGainHF; break;
-    case AL_EAXREVERB_HFREFERENCE: *val = props.HFReference; break;
-    case AL_EAXREVERB_LFREFERENCE: *val = props.LFReference; break;
-    case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: *val = props.RoomRolloffFactor; break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param};
+    case AL_EAXREVERB_DENSITY: *val = props.Density; return;
+    case AL_EAXREVERB_DIFFUSION: *val = props.Diffusion; return;
+    case AL_EAXREVERB_GAIN: *val = props.Gain; return;
+    case AL_EAXREVERB_GAINHF: *val = props.GainHF; return;
+    case AL_EAXREVERB_GAINLF: *val = props.GainLF; return;
+    case AL_EAXREVERB_DECAY_TIME: *val = props.DecayTime; return;
+    case AL_EAXREVERB_DECAY_HFRATIO: *val = props.DecayHFRatio; return;
+    case AL_EAXREVERB_DECAY_LFRATIO: *val = props.DecayLFRatio; return;
+    case AL_EAXREVERB_REFLECTIONS_GAIN: *val = props.ReflectionsGain; return;
+    case AL_EAXREVERB_REFLECTIONS_DELAY: *val = props.ReflectionsDelay; return;
+    case AL_EAXREVERB_LATE_REVERB_GAIN: *val = props.LateReverbGain; return;
+    case AL_EAXREVERB_LATE_REVERB_DELAY: *val = props.LateReverbDelay; return;
+    case AL_EAXREVERB_ECHO_TIME: *val = props.EchoTime; return;
+    case AL_EAXREVERB_ECHO_DEPTH: *val = props.EchoDepth; return;
+    case AL_EAXREVERB_MODULATION_TIME: *val = props.ModulationTime; return;
+    case AL_EAXREVERB_MODULATION_DEPTH: *val = props.ModulationDepth; return;
+    case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: *val = props.AirAbsorptionGainHF; return;
+    case AL_EAXREVERB_HFREFERENCE: *val = props.HFReference; return;
+    case AL_EAXREVERB_LFREFERENCE: *val = props.LFReference; return;
+    case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: *val = props.RoomRolloffFactor; return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamfv(const ReverbProps &props, ALenum param, float *vals)
+void ReverbEffectHandler::GetParamfv(ALCcontext *context, const ReverbProps &props, ALenum param, float *vals)
 {
 {
     al::span<float> values;
     al::span<float> values;
     switch(param)
     switch(param)
@@ -311,173 +308,155 @@ void EffectHandler::GetParamfv(const ReverbProps &props, ALenum param, float *va
     case AL_EAXREVERB_REFLECTIONS_PAN:
     case AL_EAXREVERB_REFLECTIONS_PAN:
         values = {vals, 3_uz};
         values = {vals, 3_uz};
         std::copy(props.ReflectionsPan.cbegin(), props.ReflectionsPan.cend(), values.begin());
         std::copy(props.ReflectionsPan.cbegin(), props.ReflectionsPan.cend(), values.begin());
-        break;
+        return;
     case AL_EAXREVERB_LATE_REVERB_PAN:
     case AL_EAXREVERB_LATE_REVERB_PAN:
         values = {vals, 3_uz};
         values = {vals, 3_uz};
         std::copy(props.LateReverbPan.cbegin(), props.LateReverbPan.cend(), values.begin());
         std::copy(props.LateReverbPan.cbegin(), props.LateReverbPan.cend(), values.begin());
-        break;
-
-    default:
-        GetParamf(props, param, vals);
-        break;
+        return;
     }
     }
+
+    GetParamf(context, props, param, vals);
 }
 }
 
 
 
 
 const EffectProps StdReverbEffectProps{genDefaultStdProps()};
 const EffectProps StdReverbEffectProps{genDefaultStdProps()};
 
 
-void EffectHandler::StdReverbSetParami(ReverbProps &props, ALenum param, int val)
+void StdReverbEffectHandler::SetParami(ALCcontext *context, ReverbProps &props, ALenum param, int val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_REVERB_DECAY_HFLIMIT:
     case AL_REVERB_DECAY_HFLIMIT:
         if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
         if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hflimit out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb decay hflimit out of range");
         props.DecayHFLimit = val != AL_FALSE;
         props.DecayHFLimit = val != AL_FALSE;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
-            param};
+        return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb integer property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::StdReverbSetParamiv(ReverbProps &props, ALenum param, const int *vals)
-{ StdReverbSetParami(props, param, *vals); }
-void EffectHandler::StdReverbSetParamf(ReverbProps &props, ALenum param, float val)
+void StdReverbEffectHandler::SetParamiv(ALCcontext *context, ReverbProps &props, ALenum param, const int *vals)
+{ SetParami(context, props, param, *vals); }
+void StdReverbEffectHandler::SetParamf(ALCcontext *context, ReverbProps &props, ALenum param, float val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_REVERB_DENSITY:
     case AL_REVERB_DENSITY:
         if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
         if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb density out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb density out of range");
         props.Density = val;
         props.Density = val;
-        break;
+        return;
 
 
     case AL_REVERB_DIFFUSION:
     case AL_REVERB_DIFFUSION:
         if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
         if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb diffusion out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb diffusion out of range");
         props.Diffusion = val;
         props.Diffusion = val;
-        break;
+        return;
 
 
     case AL_REVERB_GAIN:
     case AL_REVERB_GAIN:
         if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
         if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gain out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb gain out of range");
         props.Gain = val;
         props.Gain = val;
-        break;
+        return;
 
 
     case AL_REVERB_GAINHF:
     case AL_REVERB_GAINHF:
         if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
         if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gainhf out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb gainhf out of range");
         props.GainHF = val;
         props.GainHF = val;
-        break;
+        return;
 
 
     case AL_REVERB_DECAY_TIME:
     case AL_REVERB_DECAY_TIME:
         if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
         if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay time out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb decay time out of range");
         props.DecayTime = val;
         props.DecayTime = val;
-        break;
+        return;
 
 
     case AL_REVERB_DECAY_HFRATIO:
     case AL_REVERB_DECAY_HFRATIO:
         if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
         if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hfratio out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb decay hfratio out of range");
         props.DecayHFRatio = val;
         props.DecayHFRatio = val;
-        break;
+        return;
 
 
     case AL_REVERB_REFLECTIONS_GAIN:
     case AL_REVERB_REFLECTIONS_GAIN:
         if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
         if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections gain out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb reflections gain out of range");
         props.ReflectionsGain = val;
         props.ReflectionsGain = val;
-        break;
+        return;
 
 
     case AL_REVERB_REFLECTIONS_DELAY:
     case AL_REVERB_REFLECTIONS_DELAY:
         if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
         if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections delay out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb reflections delay out of range");
         props.ReflectionsDelay = val;
         props.ReflectionsDelay = val;
-        break;
+        return;
 
 
     case AL_REVERB_LATE_REVERB_GAIN:
     case AL_REVERB_LATE_REVERB_GAIN:
         if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
         if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb gain out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb late reverb gain out of range");
         props.LateReverbGain = val;
         props.LateReverbGain = val;
-        break;
+        return;
 
 
     case AL_REVERB_LATE_REVERB_DELAY:
     case AL_REVERB_LATE_REVERB_DELAY:
         if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
         if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb delay out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb late reverb delay out of range");
         props.LateReverbDelay = val;
         props.LateReverbDelay = val;
-        break;
+        return;
 
 
     case AL_REVERB_AIR_ABSORPTION_GAINHF:
     case AL_REVERB_AIR_ABSORPTION_GAINHF:
         if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
         if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb air absorption gainhf out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb air absorption gainhf out of range");
         props.AirAbsorptionGainHF = val;
         props.AirAbsorptionGainHF = val;
-        break;
+        return;
 
 
     case AL_REVERB_ROOM_ROLLOFF_FACTOR:
     case AL_REVERB_ROOM_ROLLOFF_FACTOR:
         if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
         if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
-            throw effect_exception{AL_INVALID_VALUE, "EAX Reverb room rolloff factor out of range"};
+            context->throw_error(AL_INVALID_VALUE, "EAX Reverb room rolloff factor out of range");
         props.RoomRolloffFactor = val;
         props.RoomRolloffFactor = val;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param};
-    }
-}
-void EffectHandler::StdReverbSetParamfv(ReverbProps &props, ALenum param, const float *vals)
-{
-    switch(param)
-    {
-    default:
-        StdReverbSetParamf(props, param, *vals);
-        break;
+        return;
     }
     }
-}
 
 
-void EffectHandler::StdReverbGetParami(const ReverbProps &props, ALenum param, int *val)
-{
-    switch(param)
-    {
-    case AL_REVERB_DECAY_HFLIMIT: *val = props.DecayHFLimit; break;
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
-            param};
-    }
+    context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::StdReverbGetParamiv(const ReverbProps &props, ALenum param, int *vals)
-{ StdReverbGetParami(props, param, vals); }
-void EffectHandler::StdReverbGetParamf(const ReverbProps &props, ALenum param, float *val)
+void StdReverbEffectHandler::SetParamfv(ALCcontext *context, ReverbProps &props, ALenum param, const float *vals)
+{ SetParamf(context, props, param, *vals); }
+
+void StdReverbEffectHandler::GetParami(ALCcontext *context, const ReverbProps &props, ALenum param, int *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
-    case AL_REVERB_DENSITY: *val = props.Density; break;
-    case AL_REVERB_DIFFUSION: *val = props.Diffusion; break;
-    case AL_REVERB_GAIN: *val = props.Gain; break;
-    case AL_REVERB_GAINHF: *val = props.GainHF; break;
-    case AL_REVERB_DECAY_TIME: *val = props.DecayTime; break;
-    case AL_REVERB_DECAY_HFRATIO: *val = props.DecayHFRatio; break;
-    case AL_REVERB_REFLECTIONS_GAIN: *val = props.ReflectionsGain; break;
-    case AL_REVERB_REFLECTIONS_DELAY: *val = props.ReflectionsDelay; break;
-    case AL_REVERB_LATE_REVERB_GAIN: *val = props.LateReverbGain; break;
-    case AL_REVERB_LATE_REVERB_DELAY: *val = props.LateReverbDelay; break;
-    case AL_REVERB_AIR_ABSORPTION_GAINHF: *val = props.AirAbsorptionGainHF; break;
-    case AL_REVERB_ROOM_ROLLOFF_FACTOR: *val = props.RoomRolloffFactor; break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param};
+    case AL_REVERB_DECAY_HFLIMIT: *val = props.DecayHFLimit; return;
     }
     }
+    context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb integer property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::StdReverbGetParamfv(const ReverbProps &props, ALenum param, float *vals)
+void StdReverbEffectHandler::GetParamiv(ALCcontext *context, const ReverbProps &props, ALenum param, int *vals)
+{ GetParami(context, props, param, vals); }
+void StdReverbEffectHandler::GetParamf(ALCcontext *context, const ReverbProps &props, ALenum param, float *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
-    default:
-        StdReverbGetParamf(props, param, vals);
-        break;
+    case AL_REVERB_DENSITY: *val = props.Density; return;
+    case AL_REVERB_DIFFUSION: *val = props.Diffusion; return;
+    case AL_REVERB_GAIN: *val = props.Gain; return;
+    case AL_REVERB_GAINHF: *val = props.GainHF; return;
+    case AL_REVERB_DECAY_TIME: *val = props.DecayTime; return;
+    case AL_REVERB_DECAY_HFRATIO: *val = props.DecayHFRatio; return;
+    case AL_REVERB_REFLECTIONS_GAIN: *val = props.ReflectionsGain; return;
+    case AL_REVERB_REFLECTIONS_DELAY: *val = props.ReflectionsDelay; return;
+    case AL_REVERB_LATE_REVERB_GAIN: *val = props.LateReverbGain; return;
+    case AL_REVERB_LATE_REVERB_DELAY: *val = props.LateReverbDelay; return;
+    case AL_REVERB_AIR_ABSORPTION_GAINHF: *val = props.AirAbsorptionGainHF; return;
+    case AL_REVERB_ROOM_ROLLOFF_FACTOR: *val = props.RoomRolloffFactor; return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb float property {:#04x}",
+        as_unsigned(param));
 }
 }
+void StdReverbEffectHandler::GetParamfv(ALCcontext *context, const ReverbProps &props, ALenum param, float *vals)
+{ GetParamf(context, props, param, vals); }
 
 
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 namespace {
 namespace {
 
 
 class EaxReverbEffectException : public EaxException
 class EaxReverbEffectException : public EaxException
@@ -1078,6 +1057,38 @@ bool EaxReverbCommitter::commit(const EAXREVERBPROPERTIES &props)
         ret.LFReference = props.flLFReference;
         ret.LFReference = props.flLFReference;
         ret.RoomRolloffFactor = props.flRoomRolloffFactor;
         ret.RoomRolloffFactor = props.flRoomRolloffFactor;
         ret.DecayHFLimit = ((props.ulFlags & EAXREVERBFLAGS_DECAYHFLIMIT) != 0);
         ret.DecayHFLimit = ((props.ulFlags & EAXREVERBFLAGS_DECAYHFLIMIT) != 0);
+        if(EaxTraceCommits) UNLIKELY
+        {
+            TRACE("Reverb commit:\n"
+                "  Density: {:f}\n"
+                "  Diffusion: {:f}\n"
+                "  Gain: {:f}\n"
+                "  GainHF: {:f}\n"
+                "  GainLF: {:f}\n"
+                "  DecayTime: {:f}\n"
+                "  DecayHFRatio: {:f}\n"
+                "  DecayLFRatio: {:f}\n"
+                "  ReflectionsGain: {:f}\n"
+                "  ReflectionsDelay: {:f}\n"
+                "  ReflectionsPan: {}\n"
+                "  LateReverbGain: {:f}\n"
+                "  LateReverbDelay: {:f}\n"
+                "  LateRevernPan: {}\n"
+                "  EchoTime: {:f}\n"
+                "  EchoDepth: {:f}\n"
+                "  ModulationTime: {:f}\n"
+                "  ModulationDepth: {:f}\n"
+                "  AirAbsorptionGainHF: {:f}\n"
+                "  HFReference: {:f}\n"
+                "  LFReference: {:f}\n"
+                "  RoomRolloffFactor: {:f}\n"
+                "  DecayHFLimit: {}", ret.Density, ret.Diffusion, ret.Gain, ret.GainHF, ret.GainLF,
+                ret.DecayTime, ret.DecayHFRatio, ret.DecayLFRatio, ret.ReflectionsGain,
+                ret.ReflectionsDelay, ret.ReflectionsPan, ret.LateReverbGain, ret.LateReverbDelay,
+                ret.LateReverbPan, ret.EchoTime, ret.EchoDepth, ret.ModulationTime,
+                ret.ModulationDepth, ret.AirAbsorptionGainHF, ret.HFReference, ret.LFReference,
+                ret.RoomRolloffFactor, ret.DecayHFLimit ? "true" : "false");
+        }
         return ret;
         return ret;
     }();
     }();
 
 

+ 54 - 61
libs/openal-soft/al/effects/vmorpher.cpp

@@ -7,12 +7,12 @@
 #include "AL/al.h"
 #include "AL/al.h"
 #include "AL/efx.h"
 #include "AL/efx.h"
 
 
-#include "alc/effects/base.h"
+#include "alc/context.h"
+#include "alnumeric.h"
 #include "effects.h"
 #include "effects.h"
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 #include <cassert>
 #include <cassert>
-#include "alnumeric.h"
 #include "al/eax/effect.h"
 #include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #include "al/eax/utils.h"
@@ -97,7 +97,7 @@ constexpr ALenum EnumFromPhenome(VMorpherPhenome phenome)
     HANDLE_PHENOME(V);
     HANDLE_PHENOME(V);
     HANDLE_PHENOME(Z);
     HANDLE_PHENOME(Z);
     }
     }
-    throw std::runtime_error{"Invalid phenome: "+std::to_string(static_cast<int>(phenome))};
+    throw std::runtime_error{fmt::format("Invalid phenome: {}", int{al::to_underlying(phenome)})};
 #undef HANDLE_PHENOME
 #undef HANDLE_PHENOME
 }
 }
 
 
@@ -119,8 +119,8 @@ constexpr ALenum EnumFromWaveform(VMorpherWaveform type)
     case VMorpherWaveform::Triangle: return AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE;
     case VMorpherWaveform::Triangle: return AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE;
     case VMorpherWaveform::Sawtooth: return AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH;
     case VMorpherWaveform::Sawtooth: return AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH;
     }
     }
-    throw std::runtime_error{"Invalid vocal morpher waveform: " +
-        std::to_string(static_cast<int>(type))};
+    throw std::runtime_error{fmt::format("Invalid vocal morpher waveform: {}",
+        int{al::to_underlying(type)})};
 }
 }
 
 
 constexpr EffectProps genDefaultProps() noexcept
 constexpr EffectProps genDefaultProps() noexcept
@@ -139,7 +139,7 @@ constexpr EffectProps genDefaultProps() noexcept
 
 
 const EffectProps VmorpherEffectProps{genDefaultProps()};
 const EffectProps VmorpherEffectProps{genDefaultProps()};
 
 
-void EffectHandler::SetParami(VmorpherProps &props, ALenum param, int val)
+void VmorpherEffectHandler::SetParami(ALCcontext *context, VmorpherProps &props, ALenum param, int val)
 {
 {
     switch(param)
     switch(param)
     {
     {
@@ -147,101 +147,94 @@ void EffectHandler::SetParami(VmorpherProps &props, ALenum param, int val)
         if(auto phenomeopt = PhenomeFromEnum(val))
         if(auto phenomeopt = PhenomeFromEnum(val))
             props.PhonemeA = *phenomeopt;
             props.PhonemeA = *phenomeopt;
         else
         else
-            throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a out of range: 0x%04x", val};
-        break;
+            context->throw_error(AL_INVALID_VALUE,
+                "Vocal morpher phoneme-a out of range: {:#04x}", as_unsigned(val));
+        return;
 
 
     case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING:
     case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING:
         if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE,
+                "Vocal morpher phoneme-a coarse tuning out of range");
         props.PhonemeACoarseTuning = val;
         props.PhonemeACoarseTuning = val;
-        break;
+        return;
 
 
     case AL_VOCAL_MORPHER_PHONEMEB:
     case AL_VOCAL_MORPHER_PHONEMEB:
         if(auto phenomeopt = PhenomeFromEnum(val))
         if(auto phenomeopt = PhenomeFromEnum(val))
             props.PhonemeB = *phenomeopt;
             props.PhonemeB = *phenomeopt;
         else
         else
-            throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b out of range: 0x%04x", val};
-        break;
+            context->throw_error(AL_INVALID_VALUE,
+                "Vocal morpher phoneme-b out of range: {:#04x}", as_unsigned(val));
+        return;
 
 
     case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING:
     case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING:
         if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE,
+                "Vocal morpher phoneme-b coarse tuning out of range");
         props.PhonemeBCoarseTuning = val;
         props.PhonemeBCoarseTuning = val;
-        break;
+        return;
 
 
     case AL_VOCAL_MORPHER_WAVEFORM:
     case AL_VOCAL_MORPHER_WAVEFORM:
         if(auto formopt = WaveformFromEmum(val))
         if(auto formopt = WaveformFromEmum(val))
             props.Waveform = *formopt;
             props.Waveform = *formopt;
         else
         else
-            throw effect_exception{AL_INVALID_VALUE, "Vocal morpher waveform out of range: 0x%04x", val};
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x",
-            param};
+            context->throw_error(AL_INVALID_VALUE, "Vocal morpher waveform out of range: {:#04x}",
+                as_unsigned(val));
+        return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid vocal morpher integer property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::SetParamiv(VmorpherProps&, ALenum param, const int*)
-{
-    throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x",
-        param};
-}
-void EffectHandler::SetParamf(VmorpherProps &props, ALenum param, float val)
+void VmorpherEffectHandler::SetParamiv(ALCcontext *context, VmorpherProps &props, ALenum param, const int *vals)
+{ SetParami(context, props, param, *vals); }
+void VmorpherEffectHandler::SetParamf(ALCcontext *context, VmorpherProps &props, ALenum param, float val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_VOCAL_MORPHER_RATE:
     case AL_VOCAL_MORPHER_RATE:
         if(!(val >= AL_VOCAL_MORPHER_MIN_RATE && val <= AL_VOCAL_MORPHER_MAX_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"};
+            context->throw_error(AL_INVALID_VALUE, "Vocal morpher rate out of range");
         props.Rate = val;
         props.Rate = val;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher float property 0x%04x",
-            param};
+        return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid vocal morpher float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::SetParamfv(VmorpherProps &props, ALenum param, const float *vals)
-{ SetParamf(props, param, *vals); }
+void VmorpherEffectHandler::SetParamfv(ALCcontext *context, VmorpherProps &props, ALenum param, const float *vals)
+{ SetParamf(context, props, param, *vals); }
 
 
-void EffectHandler::GetParami(const VmorpherProps &props, ALenum param, int* val)
+void VmorpherEffectHandler::GetParami(ALCcontext *context, const VmorpherProps &props, ALenum param, int* val)
 {
 {
     switch(param)
     switch(param)
     {
     {
-    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};
+    case AL_VOCAL_MORPHER_PHONEMEA: *val = EnumFromPhenome(props.PhonemeA); return;
+    case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: *val = props.PhonemeACoarseTuning; return;
+    case AL_VOCAL_MORPHER_PHONEMEB: *val = EnumFromPhenome(props.PhonemeB); return;
+    case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: *val = props.PhonemeBCoarseTuning; return;
+    case AL_VOCAL_MORPHER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid vocal morpher integer property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamiv(const VmorpherProps&, ALenum param, int*)
-{
-    throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x",
-        param};
-}
-void EffectHandler::GetParamf(const VmorpherProps &props, ALenum param, float *val)
+void VmorpherEffectHandler::GetParamiv(ALCcontext *context, const VmorpherProps &props, ALenum param, int *vals)
+{ GetParami(context, props, param, vals); }
+void VmorpherEffectHandler::GetParamf(ALCcontext *context, const VmorpherProps &props, ALenum param, float *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
-    case AL_VOCAL_MORPHER_RATE:
-        *val = props.Rate;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher float property 0x%04x",
-            param};
+    case AL_VOCAL_MORPHER_RATE: *val = props.Rate; return;
     }
     }
+
+    context->throw_error(AL_INVALID_ENUM, "Invalid vocal morpher float property {:#04x}",
+        as_unsigned(param));
 }
 }
-void EffectHandler::GetParamfv(const VmorpherProps &props, ALenum param, float *vals)
-{ GetParamf(props, param, vals); }
+void VmorpherEffectHandler::GetParamfv(ALCcontext *context, const VmorpherProps &props, ALenum param, float *vals)
+{ GetParamf(context, props, param, vals); }
 
 
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 namespace {
 namespace {
 
 
 using VocalMorpherCommitter = EaxCommitter<EaxVocalMorpherCommitter>;
 using VocalMorpherCommitter = EaxCommitter<EaxVocalMorpherCommitter>;

+ 25 - 53
libs/openal-soft/al/error.cpp

@@ -20,24 +20,19 @@
 
 
 #include "config.h"
 #include "config.h"
 
 
-#include "error.h"
-
 #ifdef _WIN32
 #ifdef _WIN32
 #define WIN32_LEAN_AND_MEAN
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <windows.h>
 #endif
 #endif
 
 
-#include <atomic>
 #include <csignal>
 #include <csignal>
 #include <cstdarg>
 #include <cstdarg>
 #include <cstdio>
 #include <cstdio>
 #include <cstdlib>
 #include <cstdlib>
 #include <cstring>
 #include <cstring>
-#include <limits>
 #include <optional>
 #include <optional>
 #include <string>
 #include <string>
 #include <utility>
 #include <utility>
-#include <vector>
 
 
 #include "AL/al.h"
 #include "AL/al.h"
 #include "AL/alc.h"
 #include "AL/alc.h"
@@ -45,53 +40,19 @@
 #include "al/debug.h"
 #include "al/debug.h"
 #include "alc/alconfig.h"
 #include "alc/alconfig.h"
 #include "alc/context.h"
 #include "alc/context.h"
-#include "alc/inprogext.h"
+#include "alnumeric.h"
+#include "core/except.h"
 #include "core/logging.h"
 #include "core/logging.h"
 #include "opthelpers.h"
 #include "opthelpers.h"
 #include "strutils.h"
 #include "strutils.h"
 
 
 
 
-namespace al {
-context_error::context_error(ALenum code, const char *msg, ...) : mErrorCode{code}
+void ALCcontext::setErrorImpl(ALenum errorCode, const fmt::string_view fmt, fmt::format_args args)
 {
 {
-    /* 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 */
+    const auto msg = fmt::vformat(fmt, std::move(args));
 
 
-void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
-{
-    auto message = std::vector<char>(256);
-
-    /* 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)};
-    if(msglen >= 0 && static_cast<size_t>(msglen) >= message.size())
-    {
-        message.resize(static_cast<size_t>(msglen) + 1u);
-        msglen = std::vsnprintf(message.data(), message.size(), msg, args2);
-    }
-    va_end(args2);
-    va_end(args);
-    /* NOLINTEND(*-array-to-pointer-decay) */
-
-    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);
+    WARN("Error generated on context {}, code {:#04x}, \"{}\"",
+        decltype(std::declval<void*>()){this}, as_unsigned(errorCode), msg);
     if(TrapALError)
     if(TrapALError)
     {
     {
 #ifdef _WIN32
 #ifdef _WIN32
@@ -106,10 +67,18 @@ void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
     if(mLastThreadError.get() == AL_NO_ERROR)
     if(mLastThreadError.get() == AL_NO_ERROR)
         mLastThreadError.set(errorCode);
         mLastThreadError.set(errorCode);
 
 
-    debugMessage(DebugSource::API, DebugType::Error, 0, DebugSeverity::High,
-        {msg, static_cast<uint>(msglen)});
+    debugMessage(DebugSource::API, DebugType::Error, static_cast<ALuint>(errorCode),
+        DebugSeverity::High, msg);
+}
+
+void ALCcontext::throw_error_impl(ALenum errorCode, const fmt::string_view fmt,
+    fmt::format_args args)
+{
+    setErrorImpl(errorCode, fmt, std::move(args));
+    throw al::base_exception{};
 }
 }
 
 
+
 /* Special-case alGetError since it (potentially) raises a debug signal and
 /* Special-case alGetError since it (potentially) raises a debug signal and
  * returns a non-default value for a null context.
  * returns a non-default value for a null context.
  */
  */
@@ -125,17 +94,20 @@ AL_API auto AL_APIENTRY alGetError() noexcept -> ALenum
             optstr = ConfigValueStr({}, "game_compat", optname);
             optstr = ConfigValueStr({}, "game_compat", optname);
         if(optstr)
         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());
+            try {
+                auto idx = 0_uz;
+                auto value = std::stoi(*optstr, &idx, 0);
+                if(idx >= optstr->size() || std::isspace(optstr->at(idx)))
+                    return static_cast<ALenum>(value);
+            } catch(...) {
+            }
+            ERR("Invalid default error value: \"{}\"", *optstr);
         }
         }
         return AL_INVALID_OPERATION;
         return AL_INVALID_OPERATION;
     };
     };
     static const ALenum deferror{get_value("__ALSOFT_DEFAULT_ERROR", "default-error")};
     static const ALenum deferror{get_value("__ALSOFT_DEFAULT_ERROR", "default-error")};
 
 
-    WARN("Querying error state on null context (implicitly 0x%04x)\n", deferror);
+    WARN("Querying error state on null context (implicitly {:#04x})", as_unsigned(deferror));
     if(TrapALError)
     if(TrapALError)
     {
     {
 #ifdef _WIN32
 #ifdef _WIN32

+ 0 - 27
libs/openal-soft/al/error.h

@@ -1,27 +0,0 @@
-#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 */

+ 31 - 29
libs/openal-soft/al/event.cpp

@@ -3,7 +3,6 @@
 
 
 #include "event.h"
 #include "event.h"
 
 
-#include <array>
 #include <atomic>
 #include <atomic>
 #include <bitset>
 #include <bitset>
 #include <exception>
 #include <exception>
@@ -12,10 +11,8 @@
 #include <new>
 #include <new>
 #include <optional>
 #include <optional>
 #include <string>
 #include <string>
-#include <string_view>
 #include <thread>
 #include <thread>
 #include <tuple>
 #include <tuple>
-#include <utility>
 #include <variant>
 #include <variant>
 
 
 #include "AL/al.h"
 #include "AL/al.h"
@@ -23,16 +20,17 @@
 #include "AL/alext.h"
 #include "AL/alext.h"
 
 
 #include "alc/context.h"
 #include "alc/context.h"
-#include "alc/inprogext.h"
+#include "alnumeric.h"
 #include "alsem.h"
 #include "alsem.h"
 #include "alspan.h"
 #include "alspan.h"
+#include "alstring.h"
 #include "core/async_event.h"
 #include "core/async_event.h"
 #include "core/context.h"
 #include "core/context.h"
 #include "core/effects/base.h"
 #include "core/effects/base.h"
+#include "core/except.h"
 #include "core/logging.h"
 #include "core/logging.h"
 #include "debug.h"
 #include "debug.h"
 #include "direct_defs.h"
 #include "direct_defs.h"
-#include "error.h"
 #include "intrusive_ptr.h"
 #include "intrusive_ptr.h"
 #include "opthelpers.h"
 #include "opthelpers.h"
 #include "ringbuffer.h"
 #include "ringbuffer.h"
@@ -52,14 +50,15 @@ int EventThread(ALCcontext *context)
     bool quitnow{false};
     bool quitnow{false};
     while(!quitnow)
     while(!quitnow)
     {
     {
-        auto evt_data = ring->getReadVector().first;
+        auto evt_data = ring->getReadVector()[0];
         if(evt_data.len == 0)
         if(evt_data.len == 0)
         {
         {
             context->mEventSem.wait();
             context->mEventSem.wait();
             continue;
             continue;
         }
         }
 
 
-        std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
+        auto eventlock = std::lock_guard{context->mEventCbLock};
+        const auto enabledevts = context->mEnabledEvts.load(std::memory_order_acquire);
         auto evt_span = al::span{std::launder(reinterpret_cast<AsyncEvent*>(evt_data.buf)),
         auto evt_span = al::span{std::launder(reinterpret_cast<AsyncEvent*>(evt_data.buf)),
             evt_data.len};
             evt_data.len};
         for(auto &event : evt_span)
         for(auto &event : evt_span)
@@ -67,7 +66,6 @@ int EventThread(ALCcontext *context)
             quitnow = std::holds_alternative<AsyncKillThread>(event);
             quitnow = std::holds_alternative<AsyncKillThread>(event);
             if(quitnow) UNLIKELY break;
             if(quitnow) UNLIKELY break;
 
 
-            auto enabledevts = context->mEnabledEvts.load(std::memory_order_acquire);
             auto proc_killthread = [](AsyncKillThread&) { };
             auto proc_killthread = [](AsyncKillThread&) { };
             auto proc_release = [](AsyncEffectReleaseEvent &evt)
             auto proc_release = [](AsyncEffectReleaseEvent &evt)
             {
             {
@@ -102,7 +100,7 @@ int EventThread(ALCcontext *context)
                     break;
                     break;
                 }
                 }
                 context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.mId, state,
                 context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.mId, state,
-                    static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
+                    al::sizei(msg), msg.c_str(), context->mEventParam);
             };
             };
             auto proc_buffercomp = [context,enabledevts](AsyncBufferCompleteEvent &evt)
             auto proc_buffercomp = [context,enabledevts](AsyncBufferCompleteEvent &evt)
             {
             {
@@ -114,20 +112,16 @@ int EventThread(ALCcontext *context)
                 if(evt.mCount == 1) msg += " buffer completed";
                 if(evt.mCount == 1) msg += " buffer completed";
                 else msg += " buffers completed";
                 else msg += " buffers completed";
                 context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.mId, evt.mCount,
                 context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.mId, evt.mCount,
-                    static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
+                    al::sizei(msg), msg.c_str(), context->mEventParam);
             };
             };
             auto proc_disconnect = [context,enabledevts](AsyncDisconnectEvent &evt)
             auto proc_disconnect = [context,enabledevts](AsyncDisconnectEvent &evt)
             {
             {
-                const std::string_view message{evt.msg.data()};
-
-                context->debugMessage(DebugSource::System, DebugType::Error, 0,
-                    DebugSeverity::High, message);
+                if(!context->mEventCb
+                    || !enabledevts.test(al::to_underlying(AsyncEnableBits::Disconnected)))
+                    return;
 
 
-                if(context->mEventCb
-                    && enabledevts.test(al::to_underlying(AsyncEnableBits::Disconnected)))
-                    context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0,
-                        static_cast<ALsizei>(message.length()), message.data(),
-                        context->mEventParam);
+                context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0, al::sizei(evt.msg),
+                    evt.msg.c_str(), context->mEventParam);
             };
             };
 
 
             std::visit(overloaded{proc_srcstate, proc_buffercomp, proc_release, proc_disconnect,
             std::visit(overloaded{proc_srcstate, proc_buffercomp, proc_release, proc_disconnect,
@@ -159,22 +153,22 @@ void StartEventThrd(ALCcontext *ctx)
         ctx->mEventThread = std::thread{EventThread, ctx};
         ctx->mEventThread = std::thread{EventThread, ctx};
     }
     }
     catch(std::exception& e) {
     catch(std::exception& e) {
-        ERR("Failed to start event thread: %s\n", e.what());
+        ERR("Failed to start event thread: {}", e.what());
     }
     }
     catch(...) {
     catch(...) {
-        ERR("Failed to start event thread! Expect problems.\n");
+        ERR("Failed to start event thread! Expect problems.");
     }
     }
 }
 }
 
 
 void StopEventThrd(ALCcontext *ctx)
 void StopEventThrd(ALCcontext *ctx)
 {
 {
     RingBuffer *ring{ctx->mAsyncEvents.get()};
     RingBuffer *ring{ctx->mAsyncEvents.get()};
-    auto evt_data = ring->getWriteVector().first;
+    auto evt_data = ring->getWriteVector()[0];
     if(evt_data.len == 0)
     if(evt_data.len == 0)
     {
     {
         do {
         do {
             std::this_thread::yield();
             std::this_thread::yield();
-            evt_data = ring->getWriteVector().first;
+            evt_data = ring->getWriteVector()[0];
         } while(evt_data.len == 0);
         } while(evt_data.len == 0);
     }
     }
     std::ignore = InitAsyncEvent<AsyncKillThread>(evt_data.buf);
     std::ignore = InitAsyncEvent<AsyncKillThread>(evt_data.buf);
@@ -190,18 +184,19 @@ FORCE_ALIGN void AL_APIENTRY alEventControlDirectSOFT(ALCcontext *context, ALsiz
     const ALenum *types, ALboolean enable) noexcept
     const ALenum *types, ALboolean enable) noexcept
 try {
 try {
     if(count < 0)
     if(count < 0)
-        throw al::context_error{AL_INVALID_VALUE, "Controlling %d events", count};
+        context->throw_error(AL_INVALID_VALUE, "Controlling {} events", count);
     if(count <= 0) UNLIKELY return;
     if(count <= 0) UNLIKELY return;
 
 
     if(!types)
     if(!types)
-        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
+        context->throw_error(AL_INVALID_VALUE, "NULL pointer");
 
 
     ContextBase::AsyncEventBitset flags{};
     ContextBase::AsyncEventBitset flags{};
     for(ALenum evttype : al::span{types, static_cast<uint>(count)})
     for(ALenum evttype : al::span{types, static_cast<uint>(count)})
     {
     {
         auto etype = GetEventType(evttype);
         auto etype = GetEventType(evttype);
         if(!etype)
         if(!etype)
-            throw al::context_error{AL_INVALID_ENUM, "Invalid event type 0x%04x", evttype};
+            context->throw_error(AL_INVALID_ENUM, "Invalid event type {:#04x}",
+                as_unsigned(evttype));
         flags.set(al::to_underlying(*etype));
         flags.set(al::to_underlying(*etype));
     }
     }
 
 
@@ -229,15 +224,22 @@ try {
         std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
         std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
     }
     }
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNCEXT2(void, alEventCallback,SOFT, ALEVENTPROCSOFT,callback, void*,userParam)
 AL_API DECL_FUNCEXT2(void, alEventCallback,SOFT, ALEVENTPROCSOFT,callback, void*,userParam)
 FORCE_ALIGN void AL_APIENTRY alEventCallbackDirectSOFT(ALCcontext *context,
 FORCE_ALIGN void AL_APIENTRY alEventCallbackDirectSOFT(ALCcontext *context,
     ALEVENTPROCSOFT callback, void *userParam) noexcept
     ALEVENTPROCSOFT callback, void *userParam) noexcept
-{
+try {
     std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
     std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
     context->mEventCb = callback;
     context->mEventCb = callback;
     context->mEventParam = userParam;
     context->mEventParam = userParam;
 }
 }
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
+}

+ 0 - 2
libs/openal-soft/al/extension.cpp

@@ -21,13 +21,11 @@
 #include "config.h"
 #include "config.h"
 
 
 #include <string_view>
 #include <string_view>
-#include <vector>
 
 
 #include "AL/al.h"
 #include "AL/al.h"
 #include "AL/alc.h"
 #include "AL/alc.h"
 
 
 #include "alc/context.h"
 #include "alc/context.h"
-#include "alc/inprogext.h"
 #include "alstring.h"
 #include "alstring.h"
 #include "direct_defs.h"
 #include "direct_defs.h"
 #include "opthelpers.h"
 #include "opthelpers.h"

+ 185 - 158
libs/openal-soft/al/filter.cpp

@@ -30,7 +30,6 @@
 #include <memory>
 #include <memory>
 #include <mutex>
 #include <mutex>
 #include <numeric>
 #include <numeric>
-#include <string>
 #include <unordered_map>
 #include <unordered_map>
 #include <vector>
 #include <vector>
 
 
@@ -41,12 +40,12 @@
 #include "albit.h"
 #include "albit.h"
 #include "alc/context.h"
 #include "alc/context.h"
 #include "alc/device.h"
 #include "alc/device.h"
-#include "alc/inprogext.h"
 #include "almalloc.h"
 #include "almalloc.h"
 #include "alnumeric.h"
 #include "alnumeric.h"
 #include "alspan.h"
 #include "alspan.h"
+#include "core/except.h"
+#include "core/logging.h"
 #include "direct_defs.h"
 #include "direct_defs.h"
-#include "error.h"
 #include "intrusive_ptr.h"
 #include "intrusive_ptr.h"
 #include "opthelpers.h"
 #include "opthelpers.h"
 
 
@@ -97,7 +96,8 @@ void InitFilterParams(ALfilter *filter, ALenum type)
     filter->type = type;
     filter->type = type;
 }
 }
 
 
-auto EnsureFilters(ALCdevice *device, size_t needed) noexcept -> bool
+[[nodiscard]]
+auto EnsureFilters(al::Device *device, size_t needed) noexcept -> bool
 try {
 try {
     size_t count{std::accumulate(device->FilterList.cbegin(), device->FilterList.cend(), 0_uz,
     size_t count{std::accumulate(device->FilterList.cbegin(), device->FilterList.cend(), 0_uz,
         [](size_t cur, const FilterSubList &sublist) noexcept -> size_t
         [](size_t cur, const FilterSubList &sublist) noexcept -> size_t
@@ -121,7 +121,8 @@ catch(...) {
 }
 }
 
 
 
 
-ALfilter *AllocFilter(ALCdevice *device) noexcept
+[[nodiscard]]
+auto AllocFilter(al::Device *device) noexcept -> ALfilter*
 {
 {
     auto sublist = std::find_if(device->FilterList.begin(), device->FilterList.end(),
     auto sublist = std::find_if(device->FilterList.begin(), device->FilterList.end(),
         [](const FilterSubList &entry) noexcept -> bool
         [](const FilterSubList &entry) noexcept -> bool
@@ -141,7 +142,7 @@ ALfilter *AllocFilter(ALCdevice *device) noexcept
     return filter;
     return filter;
 }
 }
 
 
-void FreeFilter(ALCdevice *device, ALfilter *filter)
+void FreeFilter(al::Device *device, ALfilter *filter)
 {
 {
     device->mFilterNames.erase(filter->id);
     device->mFilterNames.erase(filter->id);
 
 
@@ -155,7 +156,8 @@ void FreeFilter(ALCdevice *device, ALfilter *filter)
 }
 }
 
 
 
 
-inline auto LookupFilter(ALCdevice *device, ALuint id) noexcept -> ALfilter*
+[[nodiscard]]
+auto LookupFilter(al::Device *device, ALuint id) noexcept -> ALfilter*
 {
 {
     const size_t lidx{(id-1) >> 6};
     const size_t lidx{(id-1) >> 6};
     const ALuint slidx{(id-1) & 0x3f};
     const ALuint slidx{(id-1) & 0x3f};
@@ -172,171 +174,176 @@ inline auto LookupFilter(ALCdevice *device, ALuint id) noexcept -> ALfilter*
 
 
 /* Null filter parameter handlers */
 /* Null filter parameter handlers */
 template<>
 template<>
-void FilterTable<NullFilterTable>::setParami(ALfilter*, ALenum param, int)
-{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+void FilterTable<NullFilterTable>::setParami(ALCcontext *context, ALfilter*, ALenum param, int)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid null filter property {:#04x}", as_unsigned(param)); }
 template<>
 template<>
-void FilterTable<NullFilterTable>::setParamiv(ALfilter*, ALenum param, const int*)
-{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+void FilterTable<NullFilterTable>::setParamiv(ALCcontext *context, ALfilter*, ALenum param, const int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid null filter property {:#04x}", as_unsigned(param)); }
 template<>
 template<>
-void FilterTable<NullFilterTable>::setParamf(ALfilter*, ALenum param, float)
-{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+void FilterTable<NullFilterTable>::setParamf(ALCcontext *context, ALfilter*, ALenum param, float)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid null filter property {:#04x}", as_unsigned(param)); }
 template<>
 template<>
-void FilterTable<NullFilterTable>::setParamfv(ALfilter*, ALenum param, const float*)
-{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+void FilterTable<NullFilterTable>::setParamfv(ALCcontext *context, ALfilter*, ALenum param, const float*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid null filter property {:#04x}", as_unsigned(param)); }
 template<>
 template<>
-void FilterTable<NullFilterTable>::getParami(const ALfilter*, ALenum param, int*)
-{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+void FilterTable<NullFilterTable>::getParami(ALCcontext *context, const ALfilter*, ALenum param, int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid null filter property {:#04x}", as_unsigned(param)); }
 template<>
 template<>
-void FilterTable<NullFilterTable>::getParamiv(const ALfilter*, ALenum param, int*)
-{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+void FilterTable<NullFilterTable>::getParamiv(ALCcontext *context, const ALfilter*, ALenum param, int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid null filter property {:#04x}", as_unsigned(param)); }
 template<>
 template<>
-void FilterTable<NullFilterTable>::getParamf(const ALfilter*, ALenum param, float*)
-{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+void FilterTable<NullFilterTable>::getParamf(ALCcontext *context, const ALfilter*, ALenum param, float*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid null filter property {:#04x}", as_unsigned(param)); }
 template<>
 template<>
-void FilterTable<NullFilterTable>::getParamfv(const ALfilter*, ALenum param, float*)
-{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+void FilterTable<NullFilterTable>::getParamfv(ALCcontext *context, const ALfilter*, ALenum param, float*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid null filter property {:#04x}", as_unsigned(param)); }
 
 
 /* Lowpass parameter handlers */
 /* Lowpass parameter handlers */
 template<>
 template<>
-void FilterTable<LowpassFilterTable>::setParami(ALfilter*, ALenum param, int)
-{ throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
+void FilterTable<LowpassFilterTable>::setParami(ALCcontext *context, ALfilter*, ALenum param, int)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid low-pass integer property {:#04x}", as_unsigned(param)); }
 template<>
 template<>
-void FilterTable<LowpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values)
-{ setParami(filter, param, *values); }
+void FilterTable<LowpassFilterTable>::setParamiv(ALCcontext *context, ALfilter *filter, ALenum param, const int *values)
+{ setParami(context, filter, param, *values); }
 template<>
 template<>
-void FilterTable<LowpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val)
+void FilterTable<LowpassFilterTable>::setParamf(ALCcontext *context, ALfilter *filter, ALenum param, float val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_LOWPASS_GAIN:
     case AL_LOWPASS_GAIN:
         if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_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};
+            context->throw_error(AL_INVALID_VALUE, "Low-pass gain {:f} out of range", val);
         filter->Gain = val;
         filter->Gain = val;
         return;
         return;
 
 
     case AL_LOWPASS_GAINHF:
     case AL_LOWPASS_GAINHF:
         if(!(val >= AL_LOWPASS_MIN_GAINHF && val <= AL_LOWPASS_MAX_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};
+            context->throw_error(AL_INVALID_VALUE, "Low-pass gainhf {:f} out of range", val);
         filter->GainHF = val;
         filter->GainHF = val;
         return;
         return;
     }
     }
-    throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid low-pass float property {:#04x}",
+        as_unsigned(param));
 }
 }
 template<>
 template<>
-void FilterTable<LowpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals)
-{ setParamf(filter, param, *vals); }
+void FilterTable<LowpassFilterTable>::setParamfv(ALCcontext *context, ALfilter *filter, ALenum param, const float *vals)
+{ setParamf(context, filter, param, *vals); }
 template<>
 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}; }
+void FilterTable<LowpassFilterTable>::getParami(ALCcontext *context, const ALfilter*, ALenum param, int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid low-pass integer property {:#04x}", as_unsigned(param)); }
 template<>
 template<>
-void FilterTable<LowpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values)
-{ getParami(filter, param, values); }
+void FilterTable<LowpassFilterTable>::getParamiv(ALCcontext *context, const ALfilter *filter, ALenum param, int *values)
+{ getParami(context, filter, param, values); }
 template<>
 template<>
-void FilterTable<LowpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val)
+void FilterTable<LowpassFilterTable>::getParamf(ALCcontext *context, const ALfilter *filter, ALenum param, float *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_LOWPASS_GAIN: *val = filter->Gain; return;
     case AL_LOWPASS_GAIN: *val = filter->Gain; return;
     case AL_LOWPASS_GAINHF: *val = filter->GainHF; return;
     case AL_LOWPASS_GAINHF: *val = filter->GainHF; return;
     }
     }
-    throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid low-pass float property {:#04x}",
+        as_unsigned(param));
 }
 }
 template<>
 template<>
-void FilterTable<LowpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals)
-{ getParamf(filter, param, vals); }
+void FilterTable<LowpassFilterTable>::getParamfv(ALCcontext *context, const ALfilter *filter, ALenum param, float *vals)
+{ getParamf(context, filter, param, vals); }
 
 
 /* Highpass parameter handlers */
 /* Highpass parameter handlers */
 template<>
 template<>
-void FilterTable<HighpassFilterTable>::setParami(ALfilter*, ALenum param, int)
-{ throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
+void FilterTable<HighpassFilterTable>::setParami(ALCcontext *context, ALfilter*, ALenum param, int)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid high-pass integer property {:#04x}", as_unsigned(param)); }
 template<>
 template<>
-void FilterTable<HighpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values)
-{ setParami(filter, param, *values); }
+void FilterTable<HighpassFilterTable>::setParamiv(ALCcontext *context, ALfilter *filter, ALenum param, const int *values)
+{ setParami(context, filter, param, *values); }
 template<>
 template<>
-void FilterTable<HighpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val)
+void FilterTable<HighpassFilterTable>::setParamf(ALCcontext *context, ALfilter *filter, ALenum param, float val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_HIGHPASS_GAIN:
     case AL_HIGHPASS_GAIN:
         if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_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};
+            context->throw_error(AL_INVALID_VALUE, "High-pass gain {:f} out of range", val);
         filter->Gain = val;
         filter->Gain = val;
         return;
         return;
 
 
     case AL_HIGHPASS_GAINLF:
     case AL_HIGHPASS_GAINLF:
         if(!(val >= AL_HIGHPASS_MIN_GAINLF && val <= AL_HIGHPASS_MAX_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};
+            context->throw_error(AL_INVALID_VALUE, "High-pass gainlf {:f} out of range", val);
         filter->GainLF = val;
         filter->GainLF = val;
         return;
         return;
     }
     }
-    throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid high-pass float property {:#04x}",
+        as_unsigned(param));
 }
 }
 template<>
 template<>
-void FilterTable<HighpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals)
-{ setParamf(filter, param, *vals); }
+void FilterTable<HighpassFilterTable>::setParamfv(ALCcontext *context, ALfilter *filter, ALenum param, const float *vals)
+{ setParamf(context, filter, param, *vals); }
 template<>
 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}; }
+void FilterTable<HighpassFilterTable>::getParami(ALCcontext *context, const ALfilter*, ALenum param, int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid high-pass integer property {:#04x}", as_unsigned(param)); }
 template<>
 template<>
-void FilterTable<HighpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values)
-{ getParami(filter, param, values); }
+void FilterTable<HighpassFilterTable>::getParamiv(ALCcontext *context, const ALfilter *filter, ALenum param, int *values)
+{ getParami(context, filter, param, values); }
 template<>
 template<>
-void FilterTable<HighpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val)
+void FilterTable<HighpassFilterTable>::getParamf(ALCcontext *context, const ALfilter *filter, ALenum param, float *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_HIGHPASS_GAIN: *val = filter->Gain; return;
     case AL_HIGHPASS_GAIN: *val = filter->Gain; return;
     case AL_HIGHPASS_GAINLF: *val = filter->GainLF; return;
     case AL_HIGHPASS_GAINLF: *val = filter->GainLF; return;
     }
     }
-    throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid high-pass float property {:#04x}",
+        as_unsigned(param));
 }
 }
 template<>
 template<>
-void FilterTable<HighpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals)
-{ getParamf(filter, param, vals); }
+void FilterTable<HighpassFilterTable>::getParamfv(ALCcontext *context, const ALfilter *filter, ALenum param, float *vals)
+{ getParamf(context, filter, param, vals); }
 
 
 /* Bandpass parameter handlers */
 /* Bandpass parameter handlers */
 template<>
 template<>
-void FilterTable<BandpassFilterTable>::setParami(ALfilter*, ALenum param, int)
-{ throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
+void FilterTable<BandpassFilterTable>::setParami(ALCcontext *context, ALfilter*, ALenum param, int)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid band-pass integer property {:#04x}", as_unsigned(param)); }
 template<>
 template<>
-void FilterTable<BandpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values)
-{ setParami(filter, param, *values); }
+void FilterTable<BandpassFilterTable>::setParamiv(ALCcontext *context, ALfilter *filter, ALenum param, const int *values)
+{ setParami(context, filter, param, *values); }
 template<>
 template<>
-void FilterTable<BandpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val)
+void FilterTable<BandpassFilterTable>::setParamf(ALCcontext *context, ALfilter *filter, ALenum param, float val)
 {
 {
     switch(param)
     switch(param)
     {
     {
     case AL_BANDPASS_GAIN:
     case AL_BANDPASS_GAIN:
         if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_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};
+            context->throw_error(AL_INVALID_VALUE, "Band-pass gain {:f} out of range", val);
         filter->Gain = val;
         filter->Gain = val;
         return;
         return;
 
 
     case AL_BANDPASS_GAINHF:
     case AL_BANDPASS_GAINHF:
         if(!(val >= AL_BANDPASS_MIN_GAINHF && val <= AL_BANDPASS_MAX_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};
+            context->throw_error(AL_INVALID_VALUE, "Band-pass gainhf {:f} out of range", val);
         filter->GainHF = val;
         filter->GainHF = val;
         return;
         return;
 
 
     case AL_BANDPASS_GAINLF:
     case AL_BANDPASS_GAINLF:
         if(!(val >= AL_BANDPASS_MIN_GAINLF && val <= AL_BANDPASS_MAX_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};
+            context->throw_error(AL_INVALID_VALUE, "Band-pass gainlf {:f} out of range", val);
         filter->GainLF = val;
         filter->GainLF = val;
         return;
         return;
     }
     }
-    throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid band-pass float property {:#04x}",
+        as_unsigned(param));
 }
 }
 template<>
 template<>
-void FilterTable<BandpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals)
-{ setParamf(filter, param, *vals); }
+void FilterTable<BandpassFilterTable>::setParamfv(ALCcontext *context, ALfilter *filter, ALenum param, const float *vals)
+{ setParamf(context, filter, param, *vals); }
 template<>
 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}; }
+void FilterTable<BandpassFilterTable>::getParami(ALCcontext *context, const ALfilter*, ALenum param, int*)
+{ context->throw_error(AL_INVALID_ENUM, "Invalid band-pass integer property {:#04x}", as_unsigned(param)); }
 template<>
 template<>
-void FilterTable<BandpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values)
-{ getParami(filter, param, values); }
+void FilterTable<BandpassFilterTable>::getParamiv(ALCcontext *context, const ALfilter *filter, ALenum param, int *values)
+{ getParami(context, filter, param, values); }
 template<>
 template<>
-void FilterTable<BandpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val)
+void FilterTable<BandpassFilterTable>::getParamf(ALCcontext *context, const ALfilter *filter, ALenum param, float *val)
 {
 {
     switch(param)
     switch(param)
     {
     {
@@ -344,32 +351,35 @@ void FilterTable<BandpassFilterTable>::getParamf(const ALfilter *filter, ALenum
     case AL_BANDPASS_GAINHF: *val = filter->GainHF; return;
     case AL_BANDPASS_GAINHF: *val = filter->GainHF; return;
     case AL_BANDPASS_GAINLF: *val = filter->GainLF; return;
     case AL_BANDPASS_GAINLF: *val = filter->GainLF; return;
     }
     }
-    throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid band-pass float property {:#04x}",
+        as_unsigned(param));
 }
 }
 template<>
 template<>
-void FilterTable<BandpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals)
-{ getParamf(filter, param, vals); }
+void FilterTable<BandpassFilterTable>::getParamfv(ALCcontext *context, const ALfilter *filter, ALenum param, float *vals)
+{ getParamf(context, filter, param, vals); }
 
 
 
 
 AL_API DECL_FUNC2(void, alGenFilters, ALsizei,n, ALuint*,filters)
 AL_API DECL_FUNC2(void, alGenFilters, ALsizei,n, ALuint*,filters)
 FORCE_ALIGN void AL_APIENTRY alGenFiltersDirect(ALCcontext *context, ALsizei n, ALuint *filters) noexcept
 FORCE_ALIGN void AL_APIENTRY alGenFiltersDirect(ALCcontext *context, ALsizei n, ALuint *filters) noexcept
 try {
 try {
     if(n < 0)
     if(n < 0)
-        throw al::context_error{AL_INVALID_VALUE, "Generating %d filters", n};
+        context->throw_error(AL_INVALID_VALUE, "Generating {} filters", n);
     if(n <= 0) UNLIKELY return;
     if(n <= 0) UNLIKELY return;
 
 
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> filterlock{device->FilterLock};
+    auto *device = context->mALDevice.get();
+    auto filterlock = std::lock_guard{device->FilterLock};
 
 
     const al::span fids{filters, static_cast<ALuint>(n)};
     const al::span fids{filters, static_cast<ALuint>(n)};
     if(!EnsureFilters(device, fids.size()))
     if(!EnsureFilters(device, fids.size()))
-        throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d filter%s", n,
-            (n == 1) ? "" : "s"};
+        context->throw_error(AL_OUT_OF_MEMORY, "Failed to allocate {} filter{}", n,
+            (n==1) ? "" : "s");
 
 
     std::generate(fids.begin(), fids.end(), [device]{ return AllocFilter(device)->id; });
     std::generate(fids.begin(), fids.end(), [device]{ return AllocFilter(device)->id; });
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC2(void, alDeleteFilters, ALsizei,n, const ALuint*,filters)
 AL_API DECL_FUNC2(void, alDeleteFilters, ALsizei,n, const ALuint*,filters)
@@ -377,11 +387,11 @@ FORCE_ALIGN void AL_APIENTRY alDeleteFiltersDirect(ALCcontext *context, ALsizei
     const ALuint *filters) noexcept
     const ALuint *filters) noexcept
 try {
 try {
     if(n < 0)
     if(n < 0)
-        throw al::context_error{AL_INVALID_VALUE, "Deleting %d filters", n};
+        context->throw_error(AL_INVALID_VALUE, "Deleting {} filters", n);
     if(n <= 0) UNLIKELY return;
     if(n <= 0) UNLIKELY return;
 
 
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> filterlock{device->FilterLock};
+    auto *device = context->mALDevice.get();
+    auto filterlock = std::lock_guard{device->FilterLock};
 
 
     /* First try to find any filters that are invalid. */
     /* First try to find any filters that are invalid. */
     auto validate_filter = [device](const ALuint fid) -> bool
     auto validate_filter = [device](const ALuint fid) -> bool
@@ -390,7 +400,7 @@ try {
     const al::span fids{filters, static_cast<ALuint>(n)};
     const al::span fids{filters, static_cast<ALuint>(n)};
     auto invflt = std::find_if_not(fids.begin(), fids.end(), validate_filter);
     auto invflt = std::find_if_not(fids.begin(), fids.end(), validate_filter);
     if(invflt != fids.end())
     if(invflt != fids.end())
-        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", *invflt};
+        context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", *invflt);
 
 
     /* All good. Delete non-0 filter IDs. */
     /* All good. Delete non-0 filter IDs. */
     auto delete_filter = [device](const ALuint fid) -> void
     auto delete_filter = [device](const ALuint fid) -> void
@@ -400,15 +410,17 @@ try {
     };
     };
     std::for_each(fids.begin(), fids.end(), delete_filter);
     std::for_each(fids.begin(), fids.end(), delete_filter);
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC1(ALboolean, alIsFilter, ALuint,filter)
 AL_API DECL_FUNC1(ALboolean, alIsFilter, ALuint,filter)
 FORCE_ALIGN ALboolean AL_APIENTRY alIsFilterDirect(ALCcontext *context, ALuint filter) noexcept
 FORCE_ALIGN ALboolean AL_APIENTRY alIsFilterDirect(ALCcontext *context, ALuint filter) noexcept
 {
 {
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> filterlock{device->FilterLock};
+    auto *device = context->mALDevice.get();
+    auto filterlock = std::lock_guard{device->FilterLock};
     if(!filter || LookupFilter(device, filter))
     if(!filter || LookupFilter(device, filter))
         return AL_TRUE;
         return AL_TRUE;
     return AL_FALSE;
     return AL_FALSE;
@@ -419,29 +431,32 @@ AL_API DECL_FUNC3(void, alFilteri, ALuint,filter, ALenum,param, ALint,value)
 FORCE_ALIGN void AL_APIENTRY alFilteriDirect(ALCcontext *context, ALuint filter, ALenum param,
 FORCE_ALIGN void AL_APIENTRY alFilteriDirect(ALCcontext *context, ALuint filter, ALenum param,
     ALint value) noexcept
     ALint value) noexcept
 try {
 try {
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> filterlock{device->FilterLock};
+    auto *device = context->mALDevice.get();
+    auto filterlock = std::lock_guard{device->FilterLock};
 
 
     ALfilter *alfilt{LookupFilter(device, filter)};
     ALfilter *alfilt{LookupFilter(device, filter)};
     if(!alfilt)
     if(!alfilt)
-        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
+        context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", filter);
 
 
     switch(param)
     switch(param)
     {
     {
     case AL_FILTER_TYPE:
     case AL_FILTER_TYPE:
         if(!(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS
         if(!(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS
             || value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS))
             || value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS))
-            throw al::context_error{AL_INVALID_VALUE, "Invalid filter type 0x%04x", value};
+            context->throw_error(AL_INVALID_VALUE, "Invalid filter type {:#04x}",
+                as_unsigned(value));
         InitFilterParams(alfilt, value);
         InitFilterParams(alfilt, value);
         return;
         return;
     }
     }
 
 
     /* Call the appropriate handler */
     /* Call the appropriate handler */
-    std::visit([alfilt,param,value](auto&& thunk){thunk.setParami(alfilt, param, value);},
-        alfilt->mTypeVariant);
+    std::visit([context,alfilt,param,value](auto&& thunk)
+        { thunk.setParami(context, alfilt, param, value); }, alfilt->mTypeVariant);
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC3(void, alFilteriv, ALuint,filter, ALenum,param, const ALint*,values)
 AL_API DECL_FUNC3(void, alFilteriv, ALuint,filter, ALenum,param, const ALint*,values)
@@ -455,83 +470,89 @@ try {
         return;
         return;
     }
     }
 
 
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> filterlock{device->FilterLock};
+    auto *device = context->mALDevice.get();
+    auto filterlock = std::lock_guard{device->FilterLock};
 
 
     ALfilter *alfilt{LookupFilter(device, filter)};
     ALfilter *alfilt{LookupFilter(device, filter)};
     if(!alfilt)
     if(!alfilt)
-        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
+        context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", filter);
 
 
     /* Call the appropriate handler */
     /* Call the appropriate handler */
-    std::visit([alfilt,param,values](auto&& thunk){thunk.setParamiv(alfilt, param, values);},
-        alfilt->mTypeVariant);
+    std::visit([context,alfilt,param,values](auto&& thunk)
+        { thunk.setParamiv(context, alfilt, param, values); }, alfilt->mTypeVariant);
+}
+catch(al::base_exception&) {
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC3(void, alFilterf, ALuint,filter, ALenum,param, ALfloat,value)
 AL_API DECL_FUNC3(void, alFilterf, ALuint,filter, ALenum,param, ALfloat,value)
 FORCE_ALIGN void AL_APIENTRY alFilterfDirect(ALCcontext *context, ALuint filter, ALenum param,
 FORCE_ALIGN void AL_APIENTRY alFilterfDirect(ALCcontext *context, ALuint filter, ALenum param,
     ALfloat value) noexcept
     ALfloat value) noexcept
 try {
 try {
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> filterlock{device->FilterLock};
+    auto *device = context->mALDevice.get();
+    auto filterlock = std::lock_guard{device->FilterLock};
 
 
     ALfilter *alfilt{LookupFilter(device, filter)};
     ALfilter *alfilt{LookupFilter(device, filter)};
     if(!alfilt)
     if(!alfilt)
-        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
+        context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", filter);
 
 
     /* Call the appropriate handler */
     /* Call the appropriate handler */
-    std::visit([alfilt,param,value](auto&& thunk){thunk.setParamf(alfilt, param, value);},
-        alfilt->mTypeVariant);
+    std::visit([context,alfilt,param,value](auto&& thunk)
+        { thunk.setParamf(context, alfilt, param, value); }, alfilt->mTypeVariant);
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC3(void, alFilterfv, ALuint,filter, ALenum,param, const ALfloat*,values)
 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,
 FORCE_ALIGN void AL_APIENTRY alFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param,
     const ALfloat *values) noexcept
     const ALfloat *values) noexcept
 try {
 try {
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> filterlock{device->FilterLock};
+    auto *device = context->mALDevice.get();
+    auto filterlock = std::lock_guard{device->FilterLock};
 
 
     ALfilter *alfilt{LookupFilter(device, filter)};
     ALfilter *alfilt{LookupFilter(device, filter)};
     if(!alfilt)
     if(!alfilt)
-        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
+        context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", filter);
 
 
     /* Call the appropriate handler */
     /* Call the appropriate handler */
-    std::visit([alfilt,param,values](auto&& thunk){thunk.setParamfv(alfilt, param, values);},
-        alfilt->mTypeVariant);
+    std::visit([context,alfilt,param,values](auto&& thunk)
+        { thunk.setParamfv(context, alfilt, param, values); }, alfilt->mTypeVariant);
+}
+catch(al::base_exception&) {
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC3(void, alGetFilteri, ALuint,filter, ALenum,param, ALint*,value)
 AL_API DECL_FUNC3(void, alGetFilteri, ALuint,filter, ALenum,param, ALint*,value)
 FORCE_ALIGN void AL_APIENTRY alGetFilteriDirect(ALCcontext *context, ALuint filter, ALenum param,
 FORCE_ALIGN void AL_APIENTRY alGetFilteriDirect(ALCcontext *context, ALuint filter, ALenum param,
     ALint *value) noexcept
     ALint *value) noexcept
 try {
 try {
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> filterlock{device->FilterLock};
+    auto *device = context->mALDevice.get();
+    auto filterlock = std::lock_guard{device->FilterLock};
 
 
     const ALfilter *alfilt{LookupFilter(device, filter)};
     const ALfilter *alfilt{LookupFilter(device, filter)};
     if(!alfilt)
     if(!alfilt)
-        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
+        context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", filter);
 
 
     switch(param)
     switch(param)
     {
     {
-    case AL_FILTER_TYPE:
-        *value = alfilt->type;
-        return;
+    case AL_FILTER_TYPE: *value = alfilt->type; return;
     }
     }
 
 
     /* Call the appropriate handler */
     /* Call the appropriate handler */
-    std::visit([alfilt,param,value](auto&& thunk){thunk.getParami(alfilt, param, value);},
-        alfilt->mTypeVariant);
+    std::visit([context,alfilt,param,value](auto&& thunk)
+        { thunk.getParami(context, alfilt, param, value); }, alfilt->mTypeVariant);
+}
+catch(al::base_exception&) {
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC3(void, alGetFilteriv, ALuint,filter, ALenum,param, ALint*,values)
 AL_API DECL_FUNC3(void, alGetFilteriv, ALuint,filter, ALenum,param, ALint*,values)
@@ -545,68 +566,74 @@ try {
         return;
         return;
     }
     }
 
 
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> filterlock{device->FilterLock};
+    auto *device = context->mALDevice.get();
+    auto filterlock = std::lock_guard{device->FilterLock};
 
 
     const ALfilter *alfilt{LookupFilter(device, filter)};
     const ALfilter *alfilt{LookupFilter(device, filter)};
     if(!alfilt)
     if(!alfilt)
-        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
+        context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", filter);
 
 
     /* Call the appropriate handler */
     /* Call the appropriate handler */
-    std::visit([alfilt,param,values](auto&& thunk){thunk.getParamiv(alfilt, param, values);},
-        alfilt->mTypeVariant);
+    std::visit([context,alfilt,param,values](auto&& thunk)
+        { thunk.getParamiv(context, alfilt, param, values); }, alfilt->mTypeVariant);
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC3(void, alGetFilterf, ALuint,filter, ALenum,param, ALfloat*,value)
 AL_API DECL_FUNC3(void, alGetFilterf, ALuint,filter, ALenum,param, ALfloat*,value)
 FORCE_ALIGN void AL_APIENTRY alGetFilterfDirect(ALCcontext *context, ALuint filter, ALenum param,
 FORCE_ALIGN void AL_APIENTRY alGetFilterfDirect(ALCcontext *context, ALuint filter, ALenum param,
     ALfloat *value) noexcept
     ALfloat *value) noexcept
 try {
 try {
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> filterlock{device->FilterLock};
+    auto *device = context->mALDevice.get();
+    auto filterlock = std::lock_guard{device->FilterLock};
 
 
     const ALfilter *alfilt{LookupFilter(device, filter)};
     const ALfilter *alfilt{LookupFilter(device, filter)};
-    if(!alfilt) UNLIKELY
-        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
+    if(!alfilt)
+        context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", filter);
 
 
     /* Call the appropriate handler */
     /* Call the appropriate handler */
-    std::visit([alfilt,param,value](auto&& thunk){thunk.getParamf(alfilt, param, value);},
-        alfilt->mTypeVariant);
+    std::visit([context,alfilt,param,value](auto&& thunk)
+        { thunk.getParamf(context, alfilt, param, value); }, alfilt->mTypeVariant);
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC3(void, alGetFilterfv, ALuint,filter, ALenum,param, ALfloat*,values)
 AL_API DECL_FUNC3(void, alGetFilterfv, ALuint,filter, ALenum,param, ALfloat*,values)
 FORCE_ALIGN void AL_APIENTRY alGetFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param,
 FORCE_ALIGN void AL_APIENTRY alGetFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param,
     ALfloat *values) noexcept
     ALfloat *values) noexcept
 try {
 try {
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> filterlock{device->FilterLock};
+    auto *device = context->mALDevice.get();
+    auto filterlock = std::lock_guard{device->FilterLock};
 
 
     const ALfilter *alfilt{LookupFilter(device, filter)};
     const ALfilter *alfilt{LookupFilter(device, filter)};
-    if(!alfilt) UNLIKELY
-        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
+    if(!alfilt)
+        context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", filter);
 
 
     /* Call the appropriate handler */
     /* Call the appropriate handler */
-    std::visit([alfilt,param,values](auto&& thunk){thunk.getParamfv(alfilt, param, values);},
-        alfilt->mTypeVariant);
+    std::visit([context,alfilt,param,values](auto&& thunk)
+        { thunk.getParamfv(context, alfilt, param, values); }, alfilt->mTypeVariant);
+}
+catch(al::base_exception&) {
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 
 
 void ALfilter::SetName(ALCcontext *context, ALuint id, std::string_view name)
 void ALfilter::SetName(ALCcontext *context, ALuint id, std::string_view name)
 {
 {
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> filterlock{device->FilterLock};
+    auto *device = context->mALDevice.get();
+    auto filterlock = std::lock_guard{device->FilterLock};
 
 
     auto filter = LookupFilter(device, id);
     auto filter = LookupFilter(device, id);
     if(!filter)
     if(!filter)
-        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", id};
+        context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", id);
 
 
     device->mFilterNames.insert_or_assign(id, name);
     device->mFilterNames.insert_or_assign(id, name);
 }
 }

+ 15 - 9
libs/openal-soft/al/filter.h

@@ -14,21 +14,27 @@
 #include "almalloc.h"
 #include "almalloc.h"
 #include "alnumeric.h"
 #include "alnumeric.h"
 
 
+struct ALfilter;
+
 
 
 inline constexpr float LowPassFreqRef{5000.0f};
 inline constexpr float LowPassFreqRef{5000.0f};
 inline constexpr float HighPassFreqRef{250.0f};
 inline constexpr float HighPassFreqRef{250.0f};
 
 
 template<typename T>
 template<typename T>
 struct FilterTable {
 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*);
+    static void setParami(ALCcontext*, ALfilter*, ALenum, int);
+    static void setParamiv(ALCcontext*, ALfilter*, ALenum, const int*);
+    static void setParamf(ALCcontext*, ALfilter*, ALenum, float);
+    static void setParamfv(ALCcontext*, ALfilter*, ALenum, const float*);
+
+    static void getParami(ALCcontext*, const ALfilter*, ALenum, int*);
+    static void getParamiv(ALCcontext*, const ALfilter*, ALenum, int*);
+    static void getParamf(ALCcontext*, const ALfilter*, ALenum, float*);
+    static void getParamfv(ALCcontext*, const ALfilter*, ALenum, float*);
+
+private:
+    FilterTable() = default;
+    friend T;
 };
 };
 
 
 struct NullFilterTable : public FilterTable<NullFilterTable> { };
 struct NullFilterTable : public FilterTable<NullFilterTable> { };

+ 90 - 55
libs/openal-soft/al/listener.cpp

@@ -31,11 +31,11 @@
 #include "AL/efx.h"
 #include "AL/efx.h"
 
 
 #include "alc/context.h"
 #include "alc/context.h"
-#include "alc/inprogext.h"
+#include "alnumeric.h"
 #include "alspan.h"
 #include "alspan.h"
+#include "core/except.h"
+#include "core/logging.h"
 #include "direct_defs.h"
 #include "direct_defs.h"
-#include "error.h"
-#include "opthelpers.h"
 
 
 
 
 namespace {
 namespace {
@@ -54,7 +54,7 @@ inline void CommitAndUpdateProps(ALCcontext *context)
 {
 {
     if(!context->mDeferUpdates)
     if(!context->mDeferUpdates)
     {
     {
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
         if(context->eaxNeedsCommit())
         if(context->eaxNeedsCommit())
         {
         {
             context->mPropsDirty = true;
             context->mPropsDirty = true;
@@ -79,22 +79,26 @@ try {
     {
     {
     case AL_GAIN:
     case AL_GAIN:
         if(!(value >= 0.0f && std::isfinite(value)))
         if(!(value >= 0.0f && std::isfinite(value)))
-            throw al::context_error{AL_INVALID_VALUE, "Listener gain out of range"};
+            context->throw_error(AL_INVALID_VALUE, "Listener gain {:f} out of range", value);
         listener.Gain = value;
         listener.Gain = value;
         UpdateProps(context);
         UpdateProps(context);
         return;
         return;
 
 
     case AL_METERS_PER_UNIT:
     case AL_METERS_PER_UNIT:
         if(!(value >= AL_MIN_METERS_PER_UNIT && value <= AL_MAX_METERS_PER_UNIT))
         if(!(value >= AL_MIN_METERS_PER_UNIT && value <= AL_MAX_METERS_PER_UNIT))
-            throw al::context_error{AL_INVALID_VALUE, "Listener meters per unit out of range"};
+            context->throw_error(AL_INVALID_VALUE, "Listener meters per unit {:f} out of range",
+                value);
         listener.mMetersPerUnit = value;
         listener.mMetersPerUnit = value;
         UpdateProps(context);
         UpdateProps(context);
         return;
         return;
     }
     }
-    throw al::context_error{AL_INVALID_ENUM, "Invalid listener float property 0x%x", param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid listener float property {:#04x}",
+        as_unsigned(param));
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC4(void, alListener3f, ALenum,param, ALfloat,value1, ALfloat,value2, ALfloat,value3)
 AL_API DECL_FUNC4(void, alListener3f, ALenum,param, ALfloat,value1, ALfloat,value2, ALfloat,value3)
@@ -107,7 +111,7 @@ try {
     {
     {
     case AL_POSITION:
     case AL_POSITION:
         if(!(std::isfinite(value1) && std::isfinite(value2) && std::isfinite(value3)))
         if(!(std::isfinite(value1) && std::isfinite(value2) && std::isfinite(value3)))
-            throw al::context_error{AL_INVALID_VALUE, "Listener position out of range"};
+            context->throw_error(AL_INVALID_VALUE, "Listener position out of range");
         listener.Position[0] = value1;
         listener.Position[0] = value1;
         listener.Position[1] = value2;
         listener.Position[1] = value2;
         listener.Position[2] = value3;
         listener.Position[2] = value3;
@@ -116,17 +120,20 @@ try {
 
 
     case AL_VELOCITY:
     case AL_VELOCITY:
         if(!(std::isfinite(value1) && std::isfinite(value2) && std::isfinite(value3)))
         if(!(std::isfinite(value1) && std::isfinite(value2) && std::isfinite(value3)))
-            throw al::context_error{AL_INVALID_VALUE, "Listener velocity out of range"};
+            context->throw_error(AL_INVALID_VALUE, "Listener velocity out of range");
         listener.Velocity[0] = value1;
         listener.Velocity[0] = value1;
         listener.Velocity[1] = value2;
         listener.Velocity[1] = value2;
         listener.Velocity[2] = value3;
         listener.Velocity[2] = value3;
         CommitAndUpdateProps(context);
         CommitAndUpdateProps(context);
         return;
         return;
     }
     }
-    throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-float property 0x%x", param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid listener 3-float property {:#04x}",
+        as_unsigned(param));
+}
+catch(al::base_exception&) {
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC2(void, alListenerfv, ALenum,param, const ALfloat*,values)
 AL_API DECL_FUNC2(void, alListenerfv, ALenum,param, const ALfloat*,values)
@@ -134,7 +141,7 @@ FORCE_ALIGN void AL_APIENTRY alListenerfvDirect(ALCcontext *context, ALenum para
     const ALfloat *values) noexcept
     const ALfloat *values) noexcept
 try {
 try {
     if(!values)
     if(!values)
-        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
+        context->throw_error(AL_INVALID_VALUE, "NULL pointer");
 
 
     switch(param)
     switch(param)
     {
     {
@@ -157,17 +164,20 @@ try {
     case AL_ORIENTATION:
     case AL_ORIENTATION:
         auto vals = al::span<const float,6>{values, 6_uz};
         auto vals = al::span<const float,6>{values, 6_uz};
         if(!std::all_of(vals.cbegin(), vals.cend(), [](float f) { return std::isfinite(f); }))
         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");
+            context->throw_error(AL_INVALID_VALUE, "Listener orientation out of range");
         /* AT then UP */
         /* AT then UP */
         std::copy_n(vals.cbegin(), 3, listener.OrientAt.begin());
         std::copy_n(vals.cbegin(), 3, listener.OrientAt.begin());
         std::copy_n(vals.cbegin()+3, 3, listener.OrientUp.begin());
         std::copy_n(vals.cbegin()+3, 3, listener.OrientUp.begin());
         CommitAndUpdateProps(context);
         CommitAndUpdateProps(context);
         return;
         return;
     }
     }
-    throw al::context_error{AL_INVALID_ENUM, "Invalid listener float-vector property 0x%x", param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid listener float-vector property {:#04x}",
+        as_unsigned(param));
+}
+catch(al::base_exception&) {
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 
 
@@ -175,10 +185,13 @@ AL_API DECL_FUNC2(void, alListeneri, ALenum,param, ALint,value)
 FORCE_ALIGN void AL_APIENTRY alListeneriDirect(ALCcontext *context, ALenum param, ALint /*value*/) noexcept
 FORCE_ALIGN void AL_APIENTRY alListeneriDirect(ALCcontext *context, ALenum param, ALint /*value*/) noexcept
 try {
 try {
     std::lock_guard<std::mutex> proplock{context->mPropLock};
     std::lock_guard<std::mutex> proplock{context->mPropLock};
-    throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer property 0x%x", param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid listener integer property {:#04x}",
+        as_unsigned(param));
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC4(void, alListener3i, ALenum,param, ALint,value1, ALint,value2, ALint,value3)
 AL_API DECL_FUNC4(void, alListener3i, ALenum,param, ALint,value1, ALint,value2, ALint,value3)
@@ -195,10 +208,13 @@ try {
     }
     }
 
 
     std::lock_guard<std::mutex> proplock{context->mPropLock};
     std::lock_guard<std::mutex> proplock{context->mPropLock};
-    throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-integer property 0x%x", param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid listener 3-integer property {:#04x}",
+        as_unsigned(param));
+}
+catch(al::base_exception&) {
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC2(void, alListeneriv, ALenum,param, const ALint*,values)
 AL_API DECL_FUNC2(void, alListeneriv, ALenum,param, const ALint*,values)
@@ -206,7 +222,7 @@ FORCE_ALIGN void AL_APIENTRY alListenerivDirect(ALCcontext *context, ALenum para
     const ALint *values) noexcept
     const ALint *values) noexcept
 try {
 try {
     if(!values)
     if(!values)
-        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
+        context->throw_error(AL_INVALID_VALUE, "NULL pointer");
 
 
     al::span<const ALint> vals;
     al::span<const ALint> vals;
     switch(param)
     switch(param)
@@ -229,11 +245,13 @@ try {
     }
     }
 
 
     std::lock_guard<std::mutex> proplock{context->mPropLock};
     std::lock_guard<std::mutex> proplock{context->mPropLock};
-    throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer-vector property 0x%x",
-        param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid listener integer-vector property {:#04x}",
+        as_unsigned(param));
+}
+catch(al::base_exception&) {
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 
 
@@ -242,7 +260,7 @@ FORCE_ALIGN void AL_APIENTRY alGetListenerfDirect(ALCcontext *context, ALenum pa
     ALfloat *value) noexcept
     ALfloat *value) noexcept
 try {
 try {
     if(!value)
     if(!value)
-        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
+        context->throw_error(AL_INVALID_VALUE, "NULL pointer");
 
 
     ALlistener &listener = context->mListener;
     ALlistener &listener = context->mListener;
     std::lock_guard<std::mutex> proplock{context->mPropLock};
     std::lock_guard<std::mutex> proplock{context->mPropLock};
@@ -251,10 +269,13 @@ try {
     case AL_GAIN: *value = listener.Gain; return;
     case AL_GAIN: *value = listener.Gain; return;
     case AL_METERS_PER_UNIT: *value = listener.mMetersPerUnit; return;
     case AL_METERS_PER_UNIT: *value = listener.mMetersPerUnit; return;
     }
     }
-    throw al::context_error{AL_INVALID_ENUM, "Invalid listener float property 0x%x", param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid listener float property {:#04x}",
+        as_unsigned(param));
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC4(void, alGetListener3f, ALenum,param, ALfloat*,value1, ALfloat*,value2, ALfloat*,value3)
 AL_API DECL_FUNC4(void, alGetListener3f, ALenum,param, ALfloat*,value1, ALfloat*,value2, ALfloat*,value3)
@@ -262,7 +283,7 @@ FORCE_ALIGN void AL_APIENTRY alGetListener3fDirect(ALCcontext *context, ALenum p
     ALfloat *value1, ALfloat *value2, ALfloat *value3) noexcept
     ALfloat *value1, ALfloat *value2, ALfloat *value3) noexcept
 try {
 try {
     if(!value1 || !value2 || !value3)
     if(!value1 || !value2 || !value3)
-        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
+        context->throw_error(AL_INVALID_VALUE, "NULL pointer");
 
 
     ALlistener &listener = context->mListener;
     ALlistener &listener = context->mListener;
     std::lock_guard<std::mutex> proplock{context->mPropLock};
     std::lock_guard<std::mutex> proplock{context->mPropLock};
@@ -280,10 +301,13 @@ try {
         *value3 = listener.Velocity[2];
         *value3 = listener.Velocity[2];
         return;
         return;
     }
     }
-    throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-float property 0x%x", param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid listener 3-float property {:#04x}",
+        as_unsigned(param));
+}
+catch(al::base_exception&) {
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC2(void, alGetListenerfv, ALenum,param, ALfloat*,values)
 AL_API DECL_FUNC2(void, alGetListenerfv, ALenum,param, ALfloat*,values)
@@ -291,7 +315,7 @@ FORCE_ALIGN void AL_APIENTRY alGetListenerfvDirect(ALCcontext *context, ALenum p
     ALfloat *values) noexcept
     ALfloat *values) noexcept
 try {
 try {
     if(!values)
     if(!values)
-        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
+        context->throw_error(AL_INVALID_VALUE, "NULL pointer");
 
 
     switch(param)
     switch(param)
     {
     {
@@ -318,22 +342,28 @@ try {
         std::copy_n(listener.OrientUp.cbegin(), 3, vals.begin()+3);
         std::copy_n(listener.OrientUp.cbegin(), 3, vals.begin()+3);
         return;
         return;
     }
     }
-    throw al::context_error{AL_INVALID_ENUM, "Invalid listener float-vector property 0x%x", param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid listener float-vector property {:#04x}",
+        as_unsigned(param));
+}
+catch(al::base_exception&) {
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 
 
 AL_API DECL_FUNC2(void, alGetListeneri, ALenum,param, ALint*,value)
 AL_API DECL_FUNC2(void, alGetListeneri, ALenum,param, ALint*,value)
 FORCE_ALIGN void AL_APIENTRY alGetListeneriDirect(ALCcontext *context, ALenum param, ALint *value) noexcept
 FORCE_ALIGN void AL_APIENTRY alGetListeneriDirect(ALCcontext *context, ALenum param, ALint *value) noexcept
 try {
 try {
-    if(!value) throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
+    if(!value) context->throw_error(AL_INVALID_VALUE, "NULL pointer");
     std::lock_guard<std::mutex> proplock{context->mPropLock};
     std::lock_guard<std::mutex> proplock{context->mPropLock};
-    throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer property 0x%x", param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid listener integer property {:#04x}",
+        as_unsigned(param));
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(al::base_exception&) {
+}
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC4(void, alGetListener3i, ALenum,param, ALint*,value1, ALint*,value2, ALint*,value3)
 AL_API DECL_FUNC4(void, alGetListener3i, ALenum,param, ALint*,value1, ALint*,value2, ALint*,value3)
@@ -341,7 +371,7 @@ FORCE_ALIGN void AL_APIENTRY alGetListener3iDirect(ALCcontext *context, ALenum p
     ALint *value1, ALint *value2, ALint *value3) noexcept
     ALint *value1, ALint *value2, ALint *value3) noexcept
 try {
 try {
     if(!value1 || !value2 || !value3)
     if(!value1 || !value2 || !value3)
-        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
+        context->throw_error(AL_INVALID_VALUE, "NULL pointer");
 
 
     ALlistener &listener = context->mListener;
     ALlistener &listener = context->mListener;
     std::lock_guard<std::mutex> proplock{context->mPropLock};
     std::lock_guard<std::mutex> proplock{context->mPropLock};
@@ -359,10 +389,13 @@ try {
         *value3 = static_cast<ALint>(listener.Velocity[2]);
         *value3 = static_cast<ALint>(listener.Velocity[2]);
         return;
         return;
     }
     }
-    throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-integer property 0x%x", param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid listener 3-integer property {:#04x}",
+        as_unsigned(param));
+}
+catch(al::base_exception&) {
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }
 
 
 AL_API DECL_FUNC2(void, alGetListeneriv, ALenum,param, ALint*,values)
 AL_API DECL_FUNC2(void, alGetListeneriv, ALenum,param, ALint*,values)
@@ -370,7 +403,7 @@ FORCE_ALIGN void AL_APIENTRY alGetListenerivDirect(ALCcontext *context, ALenum p
     ALint *values) noexcept
     ALint *values) noexcept
 try {
 try {
     if(!values)
     if(!values)
-        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
+        context->throw_error(AL_INVALID_VALUE, "NULL pointer");
 
 
     switch(param)
     switch(param)
     {
     {
@@ -394,9 +427,11 @@ try {
         std::transform(listener.OrientUp.cbegin(), listener.OrientUp.cend(), vals.begin()+3, f2i);
         std::transform(listener.OrientUp.cbegin(), listener.OrientUp.cend(), vals.begin()+3, f2i);
         return;
         return;
     }
     }
-    throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer-vector property 0x%x",
-        param};
+    context->throw_error(AL_INVALID_ENUM, "Invalid listener integer-vector property {:#04x}",
+        as_unsigned(param));
+}
+catch(al::base_exception&) {
 }
 }
-catch(al::context_error& e) {
-    context->setError(e.errorCode(), "%s", e.what());
+catch(std::exception &e) {
+    ERR("Caught exception: {}", e.what());
 }
 }

Fichier diff supprimé car celui-ci est trop grand
+ 228 - 212
libs/openal-soft/al/source.cpp


+ 51 - 37
libs/openal-soft/al/source.h

@@ -1,6 +1,8 @@
 #ifndef AL_SOURCE_H
 #ifndef AL_SOURCE_H
 #define AL_SOURCE_H
 #define AL_SOURCE_H
 
 
+#include "config.h"
+
 #include <array>
 #include <array>
 #include <cstddef>
 #include <cstddef>
 #include <cstdint>
 #include <cstdint>
@@ -20,7 +22,7 @@
 #include "core/context.h"
 #include "core/context.h"
 #include "core/voice.h"
 #include "core/voice.h"
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 #include "eax/api.h"
 #include "eax/api.h"
 #include "eax/call.h"
 #include "eax/call.h"
 #include "eax/exception.h"
 #include "eax/exception.h"
@@ -45,12 +47,10 @@ inline bool sBufferSubDataCompat{false};
 
 
 struct ALbufferQueueItem : public VoiceBufferItem {
 struct ALbufferQueueItem : public VoiceBufferItem {
     ALbuffer *mBuffer{nullptr};
     ALbuffer *mBuffer{nullptr};
-
-    DISABLE_ALLOC
 };
 };
 
 
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 class EaxSourceException : public EaxException {
 class EaxSourceException : public EaxException {
 public:
 public:
     explicit EaxSourceException(const char* message)
     explicit EaxSourceException(const char* message)
@@ -71,7 +71,7 @@ struct ALsource {
     float RefDistance{1.0f};
     float RefDistance{1.0f};
     float MaxDistance{std::numeric_limits<float>::max()};
     float MaxDistance{std::numeric_limits<float>::max()};
     float RolloffFactor{1.0f};
     float RolloffFactor{1.0f};
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
     // For EAXSOURCE_ROLLOFFFACTOR, which is distinct from and added to
     // For EAXSOURCE_ROLLOFFFACTOR, which is distinct from and added to
     // AL_ROLLOFF_FACTOR
     // AL_ROLLOFF_FACTOR
     float RolloffFactor2{0.0f};
     float RolloffFactor2{0.0f};
@@ -165,10 +165,10 @@ struct ALsource {
 
 
     DISABLE_ALLOC
     DISABLE_ALLOC
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 public:
 public:
     void eaxInitialize(ALCcontext *context) noexcept;
     void eaxInitialize(ALCcontext *context) noexcept;
-    void eaxDispatch(const EaxCall& call);
+    void eaxDispatch(const EaxCall& call) { call.is_get() ? eax_get(call) : eax_set(call); }
     void eaxCommit();
     void eaxCommit();
     void eaxMarkAsChanged() noexcept { mEaxChanged = true; }
     void eaxMarkAsChanged() noexcept { mEaxChanged = true; }
 
 
@@ -199,26 +199,23 @@ private:
     using EaxSpeakerLevels = std::array<EAXSPEAKERLEVELPROPERTIES, eax_max_speakers>;
     using EaxSpeakerLevels = std::array<EAXSPEAKERLEVELPROPERTIES, eax_max_speakers>;
     using EaxSends = std::array<EAXSOURCEALLSENDPROPERTIES, EAX_MAX_FXSLOTS>;
     using EaxSends = std::array<EAXSOURCEALLSENDPROPERTIES, EAX_MAX_FXSLOTS>;
 
 
-    using Eax1Props = EAXBUFFER_REVERBPROPERTIES;
     struct Eax1State {
     struct Eax1State {
-        Eax1Props i; // Immediate.
-        Eax1Props d; // Deferred.
+        EAXBUFFER_REVERBPROPERTIES i; // Immediate.
+        EAXBUFFER_REVERBPROPERTIES d; // Deferred.
     };
     };
 
 
-    using Eax2Props = EAX20BUFFERPROPERTIES;
     struct Eax2State {
     struct Eax2State {
-        Eax2Props i; // Immediate.
-        Eax2Props d; // Deferred.
+        EAX20BUFFERPROPERTIES i; // Immediate.
+        EAX20BUFFERPROPERTIES d; // Deferred.
     };
     };
 
 
-    using Eax3Props = EAX30SOURCEPROPERTIES;
     struct Eax3State {
     struct Eax3State {
-        Eax3Props i; // Immediate.
-        Eax3Props d; // Deferred.
+        EAX30SOURCEPROPERTIES i; // Immediate.
+        EAX30SOURCEPROPERTIES d; // Deferred.
     };
     };
 
 
     struct Eax4Props {
     struct Eax4Props {
-        Eax3Props source;
+        EAX30SOURCEPROPERTIES source;
         EaxSends sends;
         EaxSends sends;
         EAX40ACTIVEFXSLOTS active_fx_slots;
         EAX40ACTIVEFXSLOTS active_fx_slots;
     };
     };
@@ -490,14 +487,14 @@ private:
     };
     };
 
 
     struct Eax1SourceAllValidator {
     struct Eax1SourceAllValidator {
-        void operator()(const Eax1Props& props) const
+        void operator()(const EAXBUFFER_REVERBPROPERTIES& props) const
         {
         {
             Eax1SourceReverbMixValidator{}(props.fMix);
             Eax1SourceReverbMixValidator{}(props.fMix);
         }
         }
     };
     };
 
 
     struct Eax2SourceAllValidator {
     struct Eax2SourceAllValidator {
-        void operator()(const Eax2Props& props) const
+        void operator()(const EAX20BUFFERPROPERTIES& props) const
         {
         {
             Eax2SourceDirectValidator{}(props.lDirect);
             Eax2SourceDirectValidator{}(props.lDirect);
             Eax2SourceDirectHfValidator{}(props.lDirectHF);
             Eax2SourceDirectHfValidator{}(props.lDirectHF);
@@ -516,7 +513,7 @@ private:
     };
     };
 
 
     struct Eax3SourceAllValidator {
     struct Eax3SourceAllValidator {
-        void operator()(const Eax3Props& props) const
+        void operator()(const EAX30SOURCEPROPERTIES& props) const
         {
         {
             Eax2SourceDirectValidator{}(props.lDirect);
             Eax2SourceDirectValidator{}(props.lDirect);
             Eax2SourceDirectHfValidator{}(props.lDirectHF);
             Eax2SourceDirectHfValidator{}(props.lDirectHF);
@@ -542,7 +539,24 @@ private:
     struct Eax5SourceAllValidator {
     struct Eax5SourceAllValidator {
         void operator()(const EAX50SOURCEPROPERTIES& props) const
         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);
             Eax5SourceMacroFXFactorValidator{}(props.flMacroFXFactor);
         }
         }
     };
     };
@@ -806,11 +820,11 @@ private:
     [[noreturn]] static void eax_fail_unknown_receiving_fx_slot_id();
     [[noreturn]] static void eax_fail_unknown_receiving_fx_slot_id();
 
 
     static void eax_set_sends_defaults(EaxSends& sends, const EaxFxSlotIds& ids) noexcept;
     static void eax_set_sends_defaults(EaxSends& sends, const EaxFxSlotIds& ids) noexcept;
-    static void eax1_set_defaults(Eax1Props& props) noexcept;
+    static void eax1_set_defaults(EAXBUFFER_REVERBPROPERTIES& props) noexcept;
     void eax1_set_defaults() noexcept;
     void eax1_set_defaults() noexcept;
-    static void eax2_set_defaults(Eax2Props& props) noexcept;
+    static void eax2_set_defaults(EAX20BUFFERPROPERTIES& props) noexcept;
     void eax2_set_defaults() noexcept;
     void eax2_set_defaults() noexcept;
-    static void eax3_set_defaults(Eax3Props& props) noexcept;
+    static void eax3_set_defaults(EAX30SOURCEPROPERTIES& props) noexcept;
     void eax3_set_defaults() noexcept;
     void eax3_set_defaults() noexcept;
     static void eax4_set_sends_defaults(EaxSends& sends) noexcept;
     static void eax4_set_sends_defaults(EaxSends& sends) noexcept;
     static void eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS& slots) noexcept;
     static void eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS& slots) noexcept;
@@ -823,9 +837,9 @@ private:
     void eax5_set_defaults() noexcept;
     void eax5_set_defaults() noexcept;
     void eax_set_defaults() noexcept;
     void eax_set_defaults() 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 eax1_translate(const EAXBUFFER_REVERBPROPERTIES& src, Eax5Props& dst) noexcept;
+    static void eax2_translate(const EAX20BUFFERPROPERTIES& src, Eax5Props& dst) noexcept;
+    static void eax3_translate(const EAX30SOURCEPROPERTIES& src, Eax5Props& dst) noexcept;
     static void eax4_translate(const Eax4Props& src, Eax5Props& dst) noexcept;
     static void eax4_translate(const Eax4Props& src, Eax5Props& dst) noexcept;
 
 
     static float eax_calculate_dst_occlusion_mb(
     static float eax_calculate_dst_occlusion_mb(
@@ -889,13 +903,13 @@ private:
         }
         }
     }
     }
 
 
-    static void eax_get_active_fx_slot_id(const EaxCall& call, const GUID* ids, size_t max_count);
-    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);
+    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 EAXBUFFER_REVERBPROPERTIES& props);
+    static void eax2_get(const EaxCall& call, const EAX20BUFFERPROPERTIES& props);
+    static void eax3_get_obstruction(const EaxCall& call, const EAX30SOURCEPROPERTIES& props);
+    static void eax3_get_occlusion(const EaxCall& call, const EAX30SOURCEPROPERTIES& props);
+    static void eax3_get_exclusion(const EaxCall& call, const EAX30SOURCEPROPERTIES& props);
+    static void eax3_get(const EaxCall& call, const EAX30SOURCEPROPERTIES& props);
     void eax4_get(const EaxCall& call, const Eax4Props& props);
     void eax4_get(const EaxCall& call, const Eax4Props& props);
     static void eax5_get_all_2d(const EaxCall& call, const EAX50SOURCEPROPERTIES& 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);
     static void eax5_get_speaker_levels(const EaxCall& call, const EaxSpeakerLevels& props);
@@ -1017,9 +1031,9 @@ private:
     void eax_set_efx_wet_gain_auto();
     void eax_set_efx_wet_gain_auto();
     void eax_set_efx_wet_gain_hf_auto();
     void eax_set_efx_wet_gain_hf_auto();
 
 
-    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);
+    static void eax1_set(const EaxCall& call, EAXBUFFER_REVERBPROPERTIES& props);
+    static void eax2_set(const EaxCall& call, EAX20BUFFERPROPERTIES& props);
+    void eax3_set(const EaxCall& call, EAX30SOURCEPROPERTIES& props);
     void eax4_set(const EaxCall& call, Eax4Props& props);
     void eax4_set(const EaxCall& call, Eax4Props& props);
     static void eax5_defer_all_2d(const EaxCall& call, EAX50SOURCEPROPERTIES& props);
     static void eax5_defer_all_2d(const EaxCall& call, EAX50SOURCEPROPERTIES& props);
     static void eax5_defer_speaker_levels(const EaxCall& call, EaxSpeakerLevels& props);
     static void eax5_defer_speaker_levels(const EaxCall& call, EaxSpeakerLevels& props);

+ 86 - 53
libs/openal-soft/al/state.cpp

@@ -40,6 +40,7 @@
 #include "al/listener.h"
 #include "al/listener.h"
 #include "alc/alu.h"
 #include "alc/alu.h"
 #include "alc/context.h"
 #include "alc/context.h"
+#include "alc/device.h"
 #include "alc/inprogext.h"
 #include "alc/inprogext.h"
 #include "alnumeric.h"
 #include "alnumeric.h"
 #include "atomic.h"
 #include "atomic.h"
@@ -52,9 +53,7 @@
 #include "opthelpers.h"
 #include "opthelpers.h"
 #include "strutils.h"
 #include "strutils.h"
 
 
-#ifdef ALSOFT_EAX
-#include "alc/device.h"
-
+#if ALSOFT_EAX
 #include "eax/globals.h"
 #include "eax/globals.h"
 #include "eax/x_ram.h"
 #include "eax/x_ram.h"
 #endif // ALSOFT_EAX
 #endif // ALSOFT_EAX
@@ -62,6 +61,8 @@
 
 
 namespace {
 namespace {
 
 
+using ALvoidptr = ALvoid*;
+
 [[nodiscard]] constexpr auto GetVendorString() noexcept { return "OpenAL Community"; }
 [[nodiscard]] constexpr auto GetVendorString() noexcept { return "OpenAL Community"; }
 [[nodiscard]] constexpr auto GetVersionString() noexcept { return "1.1 ALSOFT " ALSOFT_VERSION; }
 [[nodiscard]] constexpr auto GetVersionString() noexcept { return "1.1 ALSOFT " ALSOFT_VERSION; }
 [[nodiscard]] constexpr auto GetRendererString() noexcept { return "OpenAL Soft"; }
 [[nodiscard]] constexpr auto GetRendererString() noexcept { return "OpenAL Soft"; }
@@ -82,8 +83,10 @@ template<> struct ResamplerName<Resampler::Point>
 { static constexpr const ALchar *Get() noexcept { return "Nearest"; } };
 { static constexpr const ALchar *Get() noexcept { return "Nearest"; } };
 template<> struct ResamplerName<Resampler::Linear>
 template<> struct ResamplerName<Resampler::Linear>
 { static constexpr const ALchar *Get() noexcept { return "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>
 template<> struct ResamplerName<Resampler::FastBSinc12>
 { static constexpr const ALchar *Get() noexcept { return "11th order Sinc (fast)"; } };
 { static constexpr const ALchar *Get() noexcept { return "11th order Sinc (fast)"; } };
 template<> struct ResamplerName<Resampler::BSinc12>
 template<> struct ResamplerName<Resampler::BSinc12>
@@ -92,6 +95,10 @@ template<> struct ResamplerName<Resampler::FastBSinc24>
 { static constexpr const ALchar *Get() noexcept { return "23rd order Sinc (fast)"; } };
 { static constexpr const ALchar *Get() noexcept { return "23rd order Sinc (fast)"; } };
 template<> struct ResamplerName<Resampler::BSinc24>
 template<> struct ResamplerName<Resampler::BSinc24>
 { static constexpr const ALchar *Get() noexcept { return "23rd order Sinc"; } };
 { static constexpr const ALchar *Get() noexcept { return "23rd order Sinc"; } };
+template<> struct ResamplerName<Resampler::FastBSinc48>
+{ static constexpr const ALchar *Get() noexcept { return "47th order Sinc (fast)"; } };
+template<> struct ResamplerName<Resampler::BSinc48>
+{ static constexpr const ALchar *Get() noexcept { return "47th order Sinc"; } };
 
 
 const ALchar *GetResamplerName(const Resampler rtype)
 const ALchar *GetResamplerName(const Resampler rtype)
 {
 {
@@ -100,11 +107,14 @@ const ALchar *GetResamplerName(const Resampler rtype)
     {
     {
     HANDLE_RESAMPLER(Resampler::Point);
     HANDLE_RESAMPLER(Resampler::Point);
     HANDLE_RESAMPLER(Resampler::Linear);
     HANDLE_RESAMPLER(Resampler::Linear);
-    HANDLE_RESAMPLER(Resampler::Cubic);
+    HANDLE_RESAMPLER(Resampler::Spline);
+    HANDLE_RESAMPLER(Resampler::Gaussian);
     HANDLE_RESAMPLER(Resampler::FastBSinc12);
     HANDLE_RESAMPLER(Resampler::FastBSinc12);
     HANDLE_RESAMPLER(Resampler::BSinc12);
     HANDLE_RESAMPLER(Resampler::BSinc12);
     HANDLE_RESAMPLER(Resampler::FastBSinc24);
     HANDLE_RESAMPLER(Resampler::FastBSinc24);
     HANDLE_RESAMPLER(Resampler::BSinc24);
     HANDLE_RESAMPLER(Resampler::BSinc24);
+    HANDLE_RESAMPLER(Resampler::FastBSinc48);
+    HANDLE_RESAMPLER(Resampler::BSinc48);
     }
     }
 #undef HANDLE_RESAMPLER
 #undef HANDLE_RESAMPLER
     /* Should never get here. */
     /* Should never get here. */
@@ -141,24 +151,24 @@ constexpr auto ALenumFromDistanceModel(DistanceModel model) -> ALenum
 }
 }
 
 
 enum PropertyValue : ALenum {
 enum PropertyValue : ALenum {
-    DopplerFactor = AL_DOPPLER_FACTOR,
-    DopplerVelocity = AL_DOPPLER_VELOCITY,
-    DistanceModel = AL_DISTANCE_MODEL,
-    SpeedOfSound = AL_SPEED_OF_SOUND,
-    DeferredUpdates = AL_DEFERRED_UPDATES_SOFT,
-    GainLimit = AL_GAIN_LIMIT_SOFT,
-    NumResamplers = AL_NUM_RESAMPLERS_SOFT,
-    DefaultResampler = AL_DEFAULT_RESAMPLER_SOFT,
-    DebugLoggedMessages = AL_DEBUG_LOGGED_MESSAGES_EXT,
-    DebugNextLoggedMessageLength = AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT,
-    MaxDebugMessageLength = AL_MAX_DEBUG_MESSAGE_LENGTH_EXT,
-    MaxDebugLoggedMessages = AL_MAX_DEBUG_LOGGED_MESSAGES_EXT,
-    MaxDebugGroupDepth = AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT,
-    MaxLabelLength = AL_MAX_LABEL_LENGTH_EXT,
-    ContextFlags = AL_CONTEXT_FLAGS_EXT,
-#ifdef ALSOFT_EAX
-    EaxRamSize = AL_EAX_RAM_SIZE,
-    EaxRamFree = AL_EAX_RAM_FREE,
+    DopplerFactorProp = AL_DOPPLER_FACTOR,
+    DopplerVelocityProp = AL_DOPPLER_VELOCITY,
+    DistanceModelProp = AL_DISTANCE_MODEL,
+    SpeedOfSoundProp = AL_SPEED_OF_SOUND,
+    DeferredUpdatesProp = AL_DEFERRED_UPDATES_SOFT,
+    GainLimitProp = AL_GAIN_LIMIT_SOFT,
+    NumResamplersProp = AL_NUM_RESAMPLERS_SOFT,
+    DefaultResamplerProp = AL_DEFAULT_RESAMPLER_SOFT,
+    DebugLoggedMessagesProp = AL_DEBUG_LOGGED_MESSAGES_EXT,
+    DebugNextLoggedMessageLengthProp = AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT,
+    MaxDebugMessageLengthProp = AL_MAX_DEBUG_MESSAGE_LENGTH_EXT,
+    MaxDebugLoggedMessagesProp = AL_MAX_DEBUG_LOGGED_MESSAGES_EXT,
+    MaxDebugGroupDepthProp = AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT,
+    MaxLabelLengthProp = AL_MAX_LABEL_LENGTH_EXT,
+    ContextFlagsProp = AL_CONTEXT_FLAGS_EXT,
+#if ALSOFT_EAX
+    EaxRamSizeProp = AL_EAX_RAM_SIZE,
+    EaxRamFreeProp = AL_EAX_RAM_FREE,
 #endif
 #endif
 };
 };
 
 
@@ -256,7 +266,7 @@ void GetValue(ALCcontext *context, ALenum pname, T *values)
         *values = cast_value(context->mContextFlags.to_ulong());
         *values = cast_value(context->mContextFlags.to_ulong());
         return;
         return;
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 #define EAX_ERROR "[alGetInteger] EAX not enabled"
 #define EAX_ERROR "[alGetInteger] EAX not enabled"
 
 
     case AL_EAX_RAM_SIZE:
     case AL_EAX_RAM_SIZE:
@@ -265,7 +275,7 @@ void GetValue(ALCcontext *context, ALenum pname, T *values)
             *values = cast_value(eax_x_ram_max_size);
             *values = cast_value(eax_x_ram_max_size);
             return;
             return;
         }
         }
-        ERR(EAX_ERROR "\n");
+        ERR(EAX_ERROR);
         break;
         break;
 
 
     case AL_EAX_RAM_FREE:
     case AL_EAX_RAM_FREE:
@@ -276,13 +286,13 @@ void GetValue(ALCcontext *context, ALenum pname, T *values)
             *values = cast_value(device->eax_x_ram_free_size);
             *values = cast_value(device->eax_x_ram_free_size);
             return;
             return;
         }
         }
-        ERR(EAX_ERROR "\n");
+        ERR(EAX_ERROR);
         break;
         break;
 
 
 #undef EAX_ERROR
 #undef EAX_ERROR
 #endif // ALSOFT_EAX
 #endif // ALSOFT_EAX
     }
     }
-    context->setError(AL_INVALID_ENUM, "Invalid context property 0x%04x", pname);
+    context->setError(AL_INVALID_ENUM, "Invalid context property {:#04x}", as_unsigned(pname));
 }
 }
 
 
 
 
@@ -328,7 +338,8 @@ FORCE_ALIGN void AL_APIENTRY alEnableDirect(ALCcontext *context, ALenum capabili
         context->setError(AL_INVALID_OPERATION, "Re-enabling AL_STOP_SOURCES_ON_DISCONNECT_SOFT not yet supported");
         context->setError(AL_INVALID_OPERATION, "Re-enabling AL_STOP_SOURCES_ON_DISCONNECT_SOFT not yet supported");
         return;
         return;
     }
     }
-    context->setError(AL_INVALID_VALUE, "Invalid enable property 0x%04x", capability);
+    context->setError(AL_INVALID_VALUE, "Invalid enable property {:#04x}",
+        as_unsigned(capability));
 }
 }
 
 
 AL_API DECL_FUNC1(void, alDisable, ALenum,capability)
 AL_API DECL_FUNC1(void, alDisable, ALenum,capability)
@@ -352,7 +363,8 @@ FORCE_ALIGN void AL_APIENTRY alDisableDirect(ALCcontext *context, ALenum capabil
         context->mStopVoicesOnDisconnect.store(false);
         context->mStopVoicesOnDisconnect.store(false);
         return;
         return;
     }
     }
-    context->setError(AL_INVALID_VALUE, "Invalid disable property 0x%04x", capability);
+    context->setError(AL_INVALID_VALUE, "Invalid disable property {:#04x}",
+        as_unsigned(capability));
 }
 }
 
 
 AL_API DECL_FUNC1(ALboolean, alIsEnabled, ALenum,capability)
 AL_API DECL_FUNC1(ALboolean, alIsEnabled, ALenum,capability)
@@ -366,14 +378,15 @@ FORCE_ALIGN ALboolean AL_APIENTRY alIsEnabledDirect(ALCcontext *context, ALenum
     case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
     case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
         return context->mStopVoicesOnDisconnect.load() ? AL_TRUE : AL_FALSE;
         return context->mStopVoicesOnDisconnect.load() ? AL_TRUE : AL_FALSE;
     }
     }
-    context->setError(AL_INVALID_VALUE, "Invalid is enabled property 0x%04x", capability);
+    context->setError(AL_INVALID_VALUE, "Invalid is enabled property {:#04x}",
+        as_unsigned(capability));
     return AL_FALSE;
     return AL_FALSE;
 }
 }
 
 
 #define DECL_GETFUNC(R, Name, Ext)                                            \
 #define DECL_GETFUNC(R, Name, Ext)                                            \
-AL_API auto AL_APIENTRY Name##Ext(ALenum pname) noexcept -> R                 \
+auto AL_APIENTRY Name##Ext(ALenum pname) noexcept -> R                        \
 {                                                                             \
 {                                                                             \
-    R value{};                                                                \
+    auto value = R{};                                                         \
     auto context = GetContextRef();                                           \
     auto context = GetContextRef();                                           \
     if(!context) UNLIKELY return value;                                       \
     if(!context) UNLIKELY return value;                                       \
     Name##vDirect##Ext(GetContextRef().get(), pname, &value);                 \
     Name##vDirect##Ext(GetContextRef().get(), pname, &value);                 \
@@ -381,18 +394,19 @@ AL_API auto AL_APIENTRY Name##Ext(ALenum pname) noexcept -> R                 \
 }                                                                             \
 }                                                                             \
 FORCE_ALIGN auto AL_APIENTRY Name##Direct##Ext(ALCcontext *context, ALenum pname) noexcept -> R \
 FORCE_ALIGN auto AL_APIENTRY Name##Direct##Ext(ALCcontext *context, ALenum pname) noexcept -> R \
 {                                                                             \
 {                                                                             \
-    R value{};                                                                \
+    auto value = R{};                                                         \
     Name##vDirect##Ext(context, pname, &value);                               \
     Name##vDirect##Ext(context, pname, &value);                               \
     return value;                                                             \
     return value;                                                             \
 }
 }
 
 
-DECL_GETFUNC(ALboolean, alGetBoolean,)
-DECL_GETFUNC(ALdouble, alGetDouble,)
-DECL_GETFUNC(ALfloat, alGetFloat,)
-DECL_GETFUNC(ALint, alGetInteger,)
+AL_API DECL_GETFUNC(ALboolean, alGetBoolean,)
+AL_API DECL_GETFUNC(ALdouble, alGetDouble,)
+AL_API DECL_GETFUNC(ALfloat, alGetFloat,)
+AL_API DECL_GETFUNC(ALint, alGetInteger,)
 
 
-DECL_GETFUNC(ALint64SOFT, alGetInteger64,SOFT)
-DECL_GETFUNC(ALvoid*, alGetPointer,SOFT)
+DECL_GETFUNC(ALvoidptr, alGetPointer,EXT)
+AL_API DECL_GETFUNC(ALint64SOFT, alGetInteger64,SOFT)
+AL_API DECL_GETFUNC(ALvoidptr, alGetPointer,SOFT)
 
 
 #undef DECL_GETFUNC
 #undef DECL_GETFUNC
 
 
@@ -439,6 +453,10 @@ FORCE_ALIGN void AL_APIENTRY alGetInteger64vDirectSOFT(ALCcontext *context, ALen
 
 
 AL_API DECL_FUNCEXT2(void, alGetPointerv,SOFT, ALenum,pname, ALvoid**,values)
 AL_API DECL_FUNCEXT2(void, alGetPointerv,SOFT, ALenum,pname, ALvoid**,values)
 FORCE_ALIGN void AL_APIENTRY alGetPointervDirectSOFT(ALCcontext *context, ALenum pname, ALvoid **values) noexcept
 FORCE_ALIGN void AL_APIENTRY alGetPointervDirectSOFT(ALCcontext *context, ALenum pname, ALvoid **values) noexcept
+{ return alGetPointervDirectEXT(context, pname, values); }
+
+FORCE_ALIGN DECL_FUNCEXT2(void, alGetPointerv,EXT, ALenum,pname, ALvoid**,values)
+FORCE_ALIGN void AL_APIENTRY alGetPointervDirectEXT(ALCcontext *context, ALenum pname, ALvoid **values) noexcept
 {
 {
     if(!values) UNLIKELY
     if(!values) UNLIKELY
         return context->setError(AL_INVALID_VALUE, "NULL pointer");
         return context->setError(AL_INVALID_VALUE, "NULL pointer");
@@ -461,7 +479,8 @@ FORCE_ALIGN void AL_APIENTRY alGetPointervDirectSOFT(ALCcontext *context, ALenum
         *values = context->mDebugParam;
         *values = context->mDebugParam;
         return;
         return;
     }
     }
-    context->setError(AL_INVALID_ENUM, "Invalid context pointer property 0x%04x", pname);
+    context->setError(AL_INVALID_ENUM, "Invalid context pointer property {:#04x}",
+        as_unsigned(pname));
 }
 }
 
 
 AL_API DECL_FUNC1(const ALchar*, alGetString, ALenum,pname)
 AL_API DECL_FUNC1(const ALchar*, alGetString, ALenum,pname)
@@ -469,9 +488,18 @@ FORCE_ALIGN const ALchar* AL_APIENTRY alGetStringDirect(ALCcontext *context, ALe
 {
 {
     switch(pname)
     switch(pname)
     {
     {
-    case AL_VENDOR: return GetVendorString();
-    case AL_VERSION: return GetVersionString();
-    case AL_RENDERER: return GetRendererString();
+    case AL_VENDOR:
+        if(auto device = context->mALDevice.get(); !device->mVendorOverride.empty())
+            return device->mVendorOverride.c_str();
+        return GetVendorString();
+    case AL_VERSION:
+        if(auto device = context->mALDevice.get(); !device->mVersionOverride.empty())
+            return device->mVersionOverride.c_str();
+        return GetVersionString();
+    case AL_RENDERER:
+        if(auto device = context->mALDevice.get(); !device->mRendererOverride.empty())
+            return device->mRendererOverride.c_str();
+        return GetRendererString();
     case AL_EXTENSIONS: return context->mExtensionsString.c_str();
     case AL_EXTENSIONS: return context->mExtensionsString.c_str();
     case AL_NO_ERROR: return GetNoErrorString();
     case AL_NO_ERROR: return GetNoErrorString();
     case AL_INVALID_NAME: return GetInvalidNameString();
     case AL_INVALID_NAME: return GetInvalidNameString();
@@ -482,7 +510,7 @@ FORCE_ALIGN const ALchar* AL_APIENTRY alGetStringDirect(ALCcontext *context, ALe
     case AL_STACK_OVERFLOW_EXT: return GetStackOverflowString();
     case AL_STACK_OVERFLOW_EXT: return GetStackOverflowString();
     case AL_STACK_UNDERFLOW_EXT: return GetStackUnderflowString();
     case AL_STACK_UNDERFLOW_EXT: return GetStackUnderflowString();
     }
     }
-    context->setError(AL_INVALID_VALUE, "Invalid string property 0x%04x", pname);
+    context->setError(AL_INVALID_VALUE, "Invalid string property {:#04x}", as_unsigned(pname));
     return nullptr;
     return nullptr;
 }
 }
 
 
@@ -490,7 +518,7 @@ AL_API DECL_FUNC1(void, alDopplerFactor, ALfloat,value)
 FORCE_ALIGN void AL_APIENTRY alDopplerFactorDirect(ALCcontext *context, ALfloat value) noexcept
 FORCE_ALIGN void AL_APIENTRY alDopplerFactorDirect(ALCcontext *context, ALfloat value) noexcept
 {
 {
     if(!(value >= 0.0f && std::isfinite(value)))
     if(!(value >= 0.0f && std::isfinite(value)))
-        context->setError(AL_INVALID_VALUE, "Doppler factor %f out of range", value);
+        context->setError(AL_INVALID_VALUE, "Doppler factor {:f} out of range", value);
     else
     else
     {
     {
         std::lock_guard<std::mutex> proplock{context->mPropLock};
         std::lock_guard<std::mutex> proplock{context->mPropLock};
@@ -503,7 +531,7 @@ AL_API DECL_FUNC1(void, alSpeedOfSound, ALfloat,value)
 FORCE_ALIGN void AL_APIENTRY alSpeedOfSoundDirect(ALCcontext *context, ALfloat value) noexcept
 FORCE_ALIGN void AL_APIENTRY alSpeedOfSoundDirect(ALCcontext *context, ALfloat value) noexcept
 {
 {
     if(!(value > 0.0f && std::isfinite(value)))
     if(!(value > 0.0f && std::isfinite(value)))
-        context->setError(AL_INVALID_VALUE, "Speed of sound %f out of range", value);
+        context->setError(AL_INVALID_VALUE, "Speed of sound {:f} out of range", value);
     else
     else
     {
     {
         std::lock_guard<std::mutex> proplock{context->mPropLock};
         std::lock_guard<std::mutex> proplock{context->mPropLock};
@@ -523,7 +551,8 @@ FORCE_ALIGN void AL_APIENTRY alDistanceModelDirect(ALCcontext *context, ALenum v
             UpdateProps(context);
             UpdateProps(context);
     }
     }
     else
     else
-        context->setError(AL_INVALID_VALUE, "Distance model 0x%04x out of range", value);
+        context->setError(AL_INVALID_VALUE, "Distance model {:#04x} out of range",
+            as_unsigned(value));
 }
 }
 
 
 
 
@@ -548,12 +577,13 @@ FORCE_ALIGN const ALchar* AL_APIENTRY alGetStringiDirectSOFT(ALCcontext *context
     switch(pname)
     switch(pname)
     {
     {
     case AL_RESAMPLER_NAME_SOFT:
     case AL_RESAMPLER_NAME_SOFT:
-        if(index >= 0 && index <= static_cast<ALint>(Resampler::Max))
+        if(index >= 0 && index <= al::to_underlying(Resampler::Max))
             return GetResamplerName(static_cast<Resampler>(index));
             return GetResamplerName(static_cast<Resampler>(index));
-        context->setError(AL_INVALID_VALUE, "Resampler name index %d out of range", index);
+        context->setError(AL_INVALID_VALUE, "Resampler name index {} out of range", index);
         return nullptr;
         return nullptr;
     }
     }
-    context->setError(AL_INVALID_VALUE, "Invalid string indexed property");
+    context->setError(AL_INVALID_VALUE, "Invalid string indexed property {:#04x}",
+        as_unsigned(pname));
     return nullptr;
     return nullptr;
 }
 }
 
 
@@ -564,13 +594,13 @@ AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value) noexcept
     if(!context) UNLIKELY return;
     if(!context) UNLIKELY return;
 
 
     if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY
     if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY
-        context->debugMessage(DebugSource::API, DebugType::DeprecatedBehavior, 0,
+        context->debugMessage(DebugSource::API, DebugType::DeprecatedBehavior, 1,
             DebugSeverity::Medium,
             DebugSeverity::Medium,
             "alDopplerVelocity is deprecated in AL 1.1, use alSpeedOfSound; "
             "alDopplerVelocity is deprecated in AL 1.1, use alSpeedOfSound; "
             "alDopplerVelocity(x) -> alSpeedOfSound(343.3f * x)");
             "alDopplerVelocity(x) -> alSpeedOfSound(343.3f * x)");
 
 
     if(!(value >= 0.0f && std::isfinite(value)))
     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 velocity {:f} out of range", value);
     else
     else
     {
     {
         std::lock_guard<std::mutex> proplock{context->mPropLock};
         std::lock_guard<std::mutex> proplock{context->mPropLock};
@@ -608,6 +638,9 @@ void UpdateContextProps(ALCcontext *context)
     props->DopplerFactor = context->mDopplerFactor;
     props->DopplerFactor = context->mDopplerFactor;
     props->DopplerVelocity = context->mDopplerVelocity;
     props->DopplerVelocity = context->mDopplerVelocity;
     props->SpeedOfSound = context->mSpeedOfSound;
     props->SpeedOfSound = context->mSpeedOfSound;
+#if ALSOFT_EAX
+    props->DistanceFactor = context->eaxGetDistanceFactor();
+#endif
 
 
     props->SourceDistanceModel = context->mSourceDistanceModel;
     props->SourceDistanceModel = context->mSourceDistanceModel;
     props->mDistanceModel = context->mDistanceModel;
     props->mDistanceModel = context->mDistanceModel;

Fichier diff supprimé car celui-ci est trop grand
+ 260 - 166
libs/openal-soft/alc/alc.cpp


+ 93 - 61
libs/openal-soft/alc/alconfig.cpp

@@ -34,7 +34,6 @@
 #include <array>
 #include <array>
 #include <cctype>
 #include <cctype>
 #include <cstdlib>
 #include <cstdlib>
-#include <filesystem>
 #include <fstream>
 #include <fstream>
 #include <istream>
 #include <istream>
 #include <limits>
 #include <limits>
@@ -47,9 +46,10 @@
 #include "alstring.h"
 #include "alstring.h"
 #include "core/helpers.h"
 #include "core/helpers.h"
 #include "core/logging.h"
 #include "core/logging.h"
+#include "filesystem.h"
 #include "strutils.h"
 #include "strutils.h"
 
 
-#if defined(ALSOFT_UWP)
+#if ALSOFT_UWP
 #include <winrt/Windows.Media.Core.h> // !!This is important!!
 #include <winrt/Windows.Media.Core.h> // !!This is important!!
 #include <winrt/Windows.Storage.h>
 #include <winrt/Windows.Storage.h>
 #include <winrt/Windows.Foundation.h>
 #include <winrt/Windows.Foundation.h>
@@ -61,7 +61,7 @@ namespace {
 
 
 using namespace std::string_view_literals;
 using namespace std::string_view_literals;
 
 
-#if defined(_WIN32) && !defined(_GAMING_XBOX) && !defined(ALSOFT_UWP)
+#if defined(_WIN32) && !defined(_GAMING_XBOX) && !ALSOFT_UWP
 struct CoTaskMemDeleter {
 struct CoTaskMemDeleter {
     void operator()(void *mem) const { CoTaskMemFree(mem); }
     void operator()(void *mem) const { CoTaskMemFree(mem); }
 };
 };
@@ -153,7 +153,7 @@ void LoadConfigFromFile(std::istream &f)
             auto endpos = buffer.find(']', 1);
             auto endpos = buffer.find(']', 1);
             if(endpos == 1 || endpos == std::string::npos)
             if(endpos == 1 || endpos == std::string::npos)
             {
             {
-                ERR(" config parse error: bad line \"%s\"\n", buffer.c_str());
+                ERR(" config parse error: bad line \"{}\"", buffer);
                 continue;
                 continue;
             }
             }
             if(buffer[endpos+1] != '\0')
             if(buffer[endpos+1] != '\0')
@@ -164,7 +164,7 @@ void LoadConfigFromFile(std::istream &f)
 
 
                 if(last < buffer.size() && buffer[last] != '#')
                 if(last < buffer.size() && buffer[last] != '#')
                 {
                 {
-                    ERR(" config parse error: bad line \"%s\"\n", buffer.c_str());
+                    ERR(" config parse error: bad line \"{}\"", buffer);
                     continue;
                     continue;
                 }
                 }
             }
             }
@@ -234,7 +234,7 @@ void LoadConfigFromFile(std::istream &f)
         auto sep = buffer.find('=');
         auto sep = buffer.find('=');
         if(sep == std::string::npos)
         if(sep == std::string::npos)
         {
         {
-            ERR(" config parse error: malformed option line: \"%s\"\n", buffer.c_str());
+            ERR(" config parse error: malformed option line: \"{}\"", buffer);
             continue;
             continue;
         }
         }
         auto keypart = std::string_view{buffer}.substr(0, sep++);
         auto keypart = std::string_view{buffer}.substr(0, sep++);
@@ -242,7 +242,7 @@ void LoadConfigFromFile(std::istream &f)
             keypart.remove_suffix(1);
             keypart.remove_suffix(1);
         if(keypart.empty())
         if(keypart.empty())
         {
         {
-            ERR(" config parse error: malformed option line: \"%s\"\n", buffer.c_str());
+            ERR(" config parse error: malformed option line: \"{}\"", buffer);
             continue;
             continue;
         }
         }
         auto valpart = std::string_view{buffer}.substr(sep);
         auto valpart = std::string_view{buffer}.substr(sep);
@@ -257,9 +257,9 @@ void LoadConfigFromFile(std::istream &f)
         }
         }
         fullKey += keypart;
         fullKey += keypart;
 
 
-        if(valpart.size() > std::numeric_limits<int>::max())
+        if(valpart.size() > size_t{std::numeric_limits<int>::max()})
         {
         {
-            ERR(" config parse error: value too long in line \"%s\"\n", buffer.c_str());
+            ERR(" config parse error: value too long in line \"{}\"", buffer);
             continue;
             continue;
         }
         }
         if(valpart.size() > 1)
         if(valpart.size() > 1)
@@ -272,7 +272,7 @@ void LoadConfigFromFile(std::istream &f)
             }
             }
         }
         }
 
 
-        TRACE(" setting '%s' = '%.*s'\n", fullKey.c_str(), al::sizei(valpart), valpart.data());
+        TRACE(" setting '{}' = '{}'", fullKey, valpart);
 
 
         /* Check if we already have this option set */
         /* Check if we already have this option set */
         auto find_key = [&fullKey](const ConfigEntry &entry) -> bool
         auto find_key = [&fullKey](const ConfigEntry &entry) -> bool
@@ -291,11 +291,12 @@ void LoadConfigFromFile(std::istream &f)
     ConfOpts.shrink_to_fit();
     ConfOpts.shrink_to_fit();
 }
 }
 
 
-const char *GetConfigValue(const std::string_view devName, const std::string_view blockName,
-    const std::string_view keyName)
+auto GetConfigValue(const std::string_view devName, const std::string_view blockName,
+    const std::string_view keyName) -> const std::string&
 {
 {
+    static const auto emptyString = std::string{};
     if(keyName.empty())
     if(keyName.empty())
-        return nullptr;
+        return emptyString;
 
 
     std::string key;
     std::string key;
     if(!blockName.empty() && al::case_compare(blockName, "general"sv) != 0)
     if(!blockName.empty() && al::case_compare(blockName, "general"sv) != 0)
@@ -314,14 +315,14 @@ const char *GetConfigValue(const std::string_view devName, const std::string_vie
         [&key](const ConfigEntry &entry) -> bool { return entry.key == key; });
         [&key](const ConfigEntry &entry) -> bool { return entry.key == key; });
     if(iter != ConfOpts.cend())
     if(iter != ConfOpts.cend())
     {
     {
-        TRACE("Found option %s = \"%s\"\n", key.c_str(), iter->value.c_str());
+        TRACE("Found option {} = \"{}\"", key, iter->value);
         if(!iter->value.empty())
         if(!iter->value.empty())
-            return iter->value.c_str();
-        return nullptr;
+            return iter->value;
+        return emptyString;
     }
     }
 
 
     if(devName.empty())
     if(devName.empty())
-        return nullptr;
+        return emptyString;
     return GetConfigValue({}, blockName, keyName);
     return GetConfigValue({}, blockName, keyName);
 }
 }
 
 
@@ -331,12 +332,11 @@ const char *GetConfigValue(const std::string_view devName, const std::string_vie
 #ifdef _WIN32
 #ifdef _WIN32
 void ReadALConfig()
 void ReadALConfig()
 {
 {
-    namespace fs = std::filesystem;
     fs::path path;
     fs::path path;
 
 
 #if !defined(_GAMING_XBOX)
 #if !defined(_GAMING_XBOX)
     {
     {
-#if !defined(ALSOFT_UWP)
+#if !ALSOFT_UWP
         std::unique_ptr<WCHAR,CoTaskMemDeleter> bufstore;
         std::unique_ptr<WCHAR,CoTaskMemDeleter> bufstore;
         const HRESULT hr{SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DONT_UNEXPAND,
         const HRESULT hr{SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DONT_UNEXPAND,
             nullptr, al::out_ptr(bufstore))};
             nullptr, al::out_ptr(bufstore))};
@@ -352,8 +352,8 @@ void ReadALConfig()
             path = fs::path{buffer};
             path = fs::path{buffer};
             path /= L"alsoft.ini";
             path /= L"alsoft.ini";
 
 
-            TRACE("Loading config %s...\n", path.u8string().c_str());
-            if(std::ifstream f{path}; f.is_open())
+            TRACE("Loading config {}...", al::u8_as_char(path.u8string()));
+            if(fs::ifstream f{path}; f.is_open())
                 LoadConfigFromFile(f);
                 LoadConfigFromFile(f);
         }
         }
     }
     }
@@ -362,17 +362,17 @@ void ReadALConfig()
     path = fs::u8path(GetProcBinary().path);
     path = fs::u8path(GetProcBinary().path);
     if(!path.empty())
     if(!path.empty())
     {
     {
-        path /= "alsoft.ini";
-        TRACE("Loading config %s...\n", path.u8string().c_str());
-        if(std::ifstream f{path}; f.is_open())
+        path /= L"alsoft.ini";
+        TRACE("Loading config {}...", al::u8_as_char(path.u8string()));
+        if(fs::ifstream f{path}; f.is_open())
             LoadConfigFromFile(f);
             LoadConfigFromFile(f);
     }
     }
 
 
     if(auto confpath = al::getenv(L"ALSOFT_CONF"))
     if(auto confpath = al::getenv(L"ALSOFT_CONF"))
     {
     {
         path = *confpath;
         path = *confpath;
-        TRACE("Loading config %s...\n", path.u8string().c_str());
-        if(std::ifstream f{path}; f.is_open())
+        TRACE("Loading config {}...", al::u8_as_char(path.u8string()));
+        if(fs::ifstream f{path}; f.is_open())
             LoadConfigFromFile(f);
             LoadConfigFromFile(f);
     }
     }
 }
 }
@@ -381,11 +381,10 @@ void ReadALConfig()
 
 
 void ReadALConfig()
 void ReadALConfig()
 {
 {
-    namespace fs = std::filesystem;
     fs::path path{"/etc/openal/alsoft.conf"};
     fs::path path{"/etc/openal/alsoft.conf"};
 
 
-    TRACE("Loading config %s...\n", path.u8string().c_str());
-    if(std::ifstream f{path}; f.is_open())
+    TRACE("Loading config {}...", al::u8_as_char(path.u8string()));
+    if(fs::ifstream f{path}; f.is_open())
         LoadConfigFromFile(f);
         LoadConfigFromFile(f);
 
 
     std::string confpaths{al::getenv("XDG_CONFIG_DIRS").value_or("/etc/xdg")};
     std::string confpaths{al::getenv("XDG_CONFIG_DIRS").value_or("/etc/xdg")};
@@ -409,13 +408,13 @@ void ReadALConfig()
         }
         }
 
 
         if(!path.is_absolute())
         if(!path.is_absolute())
-            WARN("Ignoring XDG config dir: %s\n", path.u8string().c_str());
+            WARN("Ignoring XDG config dir: {}", al::u8_as_char(path.u8string()));
         else
         else
         {
         {
             path /= "alsoft.conf";
             path /= "alsoft.conf";
 
 
-            TRACE("Loading config %s...\n", path.u8string().c_str());
-            if(std::ifstream f{path}; f.is_open())
+            TRACE("Loading config {}...", al::u8_as_char(path.u8string()));
+            if(fs::ifstream f{path}; f.is_open())
                 LoadConfigFromFile(f);
                 LoadConfigFromFile(f);
         }
         }
     }
     }
@@ -441,7 +440,7 @@ void ReadALConfig()
         path = *homedir;
         path = *homedir;
         path /= ".alsoftrc";
         path /= ".alsoftrc";
 
 
-        TRACE("Loading config %s...\n", path.u8string().c_str());
+        TRACE("Loading config {}...", al::u8_as_char(path.u8string()));
         if(std::ifstream f{path}; f.is_open())
         if(std::ifstream f{path}; f.is_open())
             LoadConfigFromFile(f);
             LoadConfigFromFile(f);
     }
     }
@@ -462,7 +461,7 @@ void ReadALConfig()
     }
     }
     if(!path.empty())
     if(!path.empty())
     {
     {
-        TRACE("Loading config %s...\n", path.u8string().c_str());
+        TRACE("Loading config {}...", al::u8_as_char(path.u8string()));
         if(std::ifstream f{path}; f.is_open())
         if(std::ifstream f{path}; f.is_open())
             LoadConfigFromFile(f);
             LoadConfigFromFile(f);
     }
     }
@@ -472,66 +471,99 @@ void ReadALConfig()
     {
     {
         path /= "alsoft.conf";
         path /= "alsoft.conf";
 
 
-        TRACE("Loading config %s...\n", path.u8string().c_str());
+        TRACE("Loading config {}...", al::u8_as_char(path.u8string()));
         if(std::ifstream f{path}; f.is_open())
         if(std::ifstream f{path}; f.is_open())
             LoadConfigFromFile(f);
             LoadConfigFromFile(f);
     }
     }
 
 
     if(auto confname = al::getenv("ALSOFT_CONF"))
     if(auto confname = al::getenv("ALSOFT_CONF"))
     {
     {
-        TRACE("Loading config %s...\n", confname->c_str());
+        TRACE("Loading config {}...", *confname);
         if(std::ifstream f{*confname}; f.is_open())
         if(std::ifstream f{*confname}; f.is_open())
             LoadConfigFromFile(f);
             LoadConfigFromFile(f);
     }
     }
 }
 }
 #endif
 #endif
 
 
-std::optional<std::string> ConfigValueStr(const std::string_view devName,
-    const std::string_view blockName, const std::string_view keyName)
+auto ConfigValueStr(const std::string_view devName, const std::string_view blockName,
+    const std::string_view keyName) -> std::optional<std::string>
 {
 {
-    if(const char *val{GetConfigValue(devName, blockName, keyName)})
+    if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty())
         return val;
         return val;
     return std::nullopt;
     return std::nullopt;
 }
 }
 
 
-std::optional<int> ConfigValueInt(const std::string_view devName, const std::string_view blockName,
-    const std::string_view keyName)
+auto ConfigValueInt(const std::string_view devName, const std::string_view blockName,
+    const std::string_view keyName) -> std::optional<int>
 {
 {
-    if(const char *val{GetConfigValue(devName, blockName, keyName)})
-        return static_cast<int>(std::strtol(val, nullptr, 0));
+    if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try {
+        return static_cast<int>(std::stol(val, nullptr, 0));
+    }
+    catch(std::exception&) {
+        WARN("Option is not an int: {} = {}", keyName, val);
+    }
+
     return std::nullopt;
     return std::nullopt;
 }
 }
 
 
-std::optional<unsigned int> ConfigValueUInt(const std::string_view devName,
-    const std::string_view blockName, const std::string_view keyName)
+auto ConfigValueUInt(const std::string_view devName, const std::string_view blockName,
+    const std::string_view keyName) -> std::optional<unsigned int>
 {
 {
-    if(const char *val{GetConfigValue(devName, blockName, keyName)})
-        return static_cast<unsigned int>(std::strtoul(val, nullptr, 0));
+    if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try {
+        return static_cast<unsigned int>(std::stoul(val, nullptr, 0));
+    }
+    catch(std::exception&) {
+        WARN("Option is not an unsigned int: {} = {}", keyName, val);
+    }
     return std::nullopt;
     return std::nullopt;
 }
 }
 
 
-std::optional<float> ConfigValueFloat(const std::string_view devName,
-    const std::string_view blockName, const std::string_view keyName)
+auto ConfigValueFloat(const std::string_view devName, const std::string_view blockName,
+    const std::string_view keyName) -> std::optional<float>
 {
 {
-    if(const char *val{GetConfigValue(devName, blockName, keyName)})
-        return std::strtof(val, nullptr);
+    if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try {
+        return std::stof(val);
+    }
+    catch(std::exception&) {
+        WARN("Option is not a float: {} = {}", keyName, val);
+    }
     return std::nullopt;
     return std::nullopt;
 }
 }
 
 
-std::optional<bool> ConfigValueBool(const std::string_view devName,
-    const std::string_view blockName, const std::string_view keyName)
+auto ConfigValueBool(const std::string_view devName, const std::string_view blockName,
+    const std::string_view keyName) -> std::optional<bool>
 {
 {
-    if(const char *val{GetConfigValue(devName, blockName, keyName)})
-        return al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
-            || al::strcasecmp(val, "true") == 0 || atoi(val) != 0;
+    if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try {
+        return al::case_compare(val, "on"sv) == 0 || al::case_compare(val, "yes"sv) == 0
+            || al::case_compare(val, "true"sv) == 0 || std::stoll(val) != 0;
+    }
+    catch(std::out_of_range&) {
+        /* If out of range, the value is some non-0 (true) value and it doesn't
+         * matter that it's too big or small.
+         */
+        return true;
+    }
+    catch(std::exception&) {
+        /* If stoll fails to convert for any other reason, it's some other word
+         * that's treated as false.
+         */
+        return false;
+    }
     return std::nullopt;
     return std::nullopt;
 }
 }
 
 
-bool GetConfigValueBool(const std::string_view devName, const std::string_view blockName,
-    const std::string_view keyName, bool def)
+auto GetConfigValueBool(const std::string_view devName, const std::string_view blockName,
+    const std::string_view keyName, bool def) -> bool
 {
 {
-    if(const char *val{GetConfigValue(devName, blockName, keyName)})
-        return al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
-            || al::strcasecmp(val, "true") == 0 || atoi(val) != 0;
+    if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try {
+        return al::case_compare(val, "on"sv) == 0 || al::case_compare(val, "yes"sv) == 0
+            || al::case_compare(val, "true"sv) == 0 || std::stoll(val) != 0;
+    }
+    catch(std::out_of_range&) {
+        return true;
+    }
+    catch(std::exception&) {
+        return false;
+    }
     return def;
     return def;
 }
 }

+ 273 - 249
libs/openal-soft/alc/alu.cpp

@@ -19,6 +19,7 @@
  */
  */
 
 
 #include "config.h"
 #include "config.h"
+#include "config_simd.h"
 
 
 #include "alu.h"
 #include "alu.h"
 
 
@@ -26,23 +27,25 @@
 #include <array>
 #include <array>
 #include <atomic>
 #include <atomic>
 #include <cassert>
 #include <cassert>
-#include <chrono>
-#include <climits>
+#include <cmath>
 #include <cstdarg>
 #include <cstdarg>
+#include <cstddef>
 #include <cstdint>
 #include <cstdint>
 #include <cstdio>
 #include <cstdio>
 #include <cstdlib>
 #include <cstdlib>
-#include <functional>
 #include <iterator>
 #include <iterator>
 #include <limits>
 #include <limits>
 #include <memory>
 #include <memory>
-#include <new>
 #include <optional>
 #include <optional>
+#include <string>
+#include <string_view>
 #include <utility>
 #include <utility>
+#include <variant>
 
 
 #include "almalloc.h"
 #include "almalloc.h"
 #include "alnumbers.h"
 #include "alnumbers.h"
 #include "alnumeric.h"
 #include "alnumeric.h"
+#include "alsem.h"
 #include "alspan.h"
 #include "alspan.h"
 #include "alstring.h"
 #include "alstring.h"
 #include "atomic.h"
 #include "atomic.h"
@@ -70,6 +73,7 @@
 #include "core/mixer/defs.h"
 #include "core/mixer/defs.h"
 #include "core/mixer/hrtfdefs.h"
 #include "core/mixer/hrtfdefs.h"
 #include "core/resampler_limits.h"
 #include "core/resampler_limits.h"
+#include "core/storage_formats.h"
 #include "core/uhjfilter.h"
 #include "core/uhjfilter.h"
 #include "core/voice.h"
 #include "core/voice.h"
 #include "core/voice_change.h"
 #include "core/voice_change.h"
@@ -78,19 +82,18 @@
 #include "ringbuffer.h"
 #include "ringbuffer.h"
 #include "strutils.h"
 #include "strutils.h"
 #include "vecmat.h"
 #include "vecmat.h"
-#include "vector.h"
 
 
 struct CTag;
 struct CTag;
-#ifdef HAVE_SSE
+#if HAVE_SSE
 struct SSETag;
 struct SSETag;
 #endif
 #endif
-#ifdef HAVE_SSE2
+#if HAVE_SSE2
 struct SSE2Tag;
 struct SSE2Tag;
 #endif
 #endif
-#ifdef HAVE_SSE4_1
+#if HAVE_SSE4_1
 struct SSE4Tag;
 struct SSE4Tag;
 #endif
 #endif
-#ifdef HAVE_NEON
+#if HAVE_NEON
 struct NEONTag;
 struct NEONTag;
 #endif
 #endif
 struct PointTag;
 struct PointTag;
@@ -135,19 +138,19 @@ float NfcScale{1.0f};
 
 
 
 
 using HrtfDirectMixerFunc = void(*)(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut,
 using HrtfDirectMixerFunc = void(*)(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut,
-    const al::span<const FloatBufferLine> InSamples, float2 *AccumSamples,
-    const al::span<float,BufferLineSize> TempBuf, HrtfChannelState *ChanState, const size_t IrSize,
-    const size_t BufferSize);
+    const al::span<const FloatBufferLine> InSamples, const al::span<float2> AccumSamples,
+    const al::span<float,BufferLineSize> TempBuf, const al::span<HrtfChannelState> ChanState,
+    const size_t IrSize, const size_t SamplesToDo);
 
 
 HrtfDirectMixerFunc MixDirectHrtf{MixDirectHrtf_<CTag>};
 HrtfDirectMixerFunc MixDirectHrtf{MixDirectHrtf_<CTag>};
 
 
 inline HrtfDirectMixerFunc SelectHrtfMixer()
 inline HrtfDirectMixerFunc SelectHrtfMixer()
 {
 {
-#ifdef HAVE_NEON
+#if HAVE_NEON
     if((CPUCapFlags&CPU_CAP_NEON))
     if((CPUCapFlags&CPU_CAP_NEON))
         return MixDirectHrtf_<NEONTag>;
         return MixDirectHrtf_<NEONTag>;
 #endif
 #endif
-#ifdef HAVE_SSE
+#if HAVE_SSE
     if((CPUCapFlags&CPU_CAP_SSE))
     if((CPUCapFlags&CPU_CAP_SSE))
         return MixDirectHrtf_<SSETag>;
         return MixDirectHrtf_<SSETag>;
 #endif
 #endif
@@ -176,7 +179,7 @@ inline void BsincPrepare(const uint increment, BsincState *state, const BSincTab
     state->sf = sf;
     state->sf = sf;
     state->m = table->m[si];
     state->m = table->m[si];
     state->l = (state->m/2) - 1;
     state->l = (state->m/2) - 1;
-    state->filter = table->Tab + table->filterOffset[si];
+    state->filter = table->Tab.subspan(table->filterOffset[si]);
 }
 }
 
 
 inline ResamplerFunc SelectResampler(Resampler resampler, uint increment)
 inline ResamplerFunc SelectResampler(Resampler resampler, uint increment)
@@ -186,59 +189,62 @@ inline ResamplerFunc SelectResampler(Resampler resampler, uint increment)
     case Resampler::Point:
     case Resampler::Point:
         return Resample_<PointTag,CTag>;
         return Resample_<PointTag,CTag>;
     case Resampler::Linear:
     case Resampler::Linear:
-#ifdef HAVE_NEON
+#if HAVE_NEON
         if((CPUCapFlags&CPU_CAP_NEON))
         if((CPUCapFlags&CPU_CAP_NEON))
             return Resample_<LerpTag,NEONTag>;
             return Resample_<LerpTag,NEONTag>;
 #endif
 #endif
-#ifdef HAVE_SSE4_1
+#if HAVE_SSE4_1
         if((CPUCapFlags&CPU_CAP_SSE4_1))
         if((CPUCapFlags&CPU_CAP_SSE4_1))
             return Resample_<LerpTag,SSE4Tag>;
             return Resample_<LerpTag,SSE4Tag>;
 #endif
 #endif
-#ifdef HAVE_SSE2
+#if HAVE_SSE2
         if((CPUCapFlags&CPU_CAP_SSE2))
         if((CPUCapFlags&CPU_CAP_SSE2))
             return Resample_<LerpTag,SSE2Tag>;
             return Resample_<LerpTag,SSE2Tag>;
 #endif
 #endif
         return Resample_<LerpTag,CTag>;
         return Resample_<LerpTag,CTag>;
-    case Resampler::Cubic:
-#ifdef HAVE_NEON
+    case Resampler::Spline:
+    case Resampler::Gaussian:
+#if HAVE_NEON
         if((CPUCapFlags&CPU_CAP_NEON))
         if((CPUCapFlags&CPU_CAP_NEON))
             return Resample_<CubicTag,NEONTag>;
             return Resample_<CubicTag,NEONTag>;
 #endif
 #endif
-#ifdef HAVE_SSE4_1
+#if HAVE_SSE4_1
         if((CPUCapFlags&CPU_CAP_SSE4_1))
         if((CPUCapFlags&CPU_CAP_SSE4_1))
             return Resample_<CubicTag,SSE4Tag>;
             return Resample_<CubicTag,SSE4Tag>;
 #endif
 #endif
-#ifdef HAVE_SSE2
+#if HAVE_SSE2
         if((CPUCapFlags&CPU_CAP_SSE2))
         if((CPUCapFlags&CPU_CAP_SSE2))
             return Resample_<CubicTag,SSE2Tag>;
             return Resample_<CubicTag,SSE2Tag>;
 #endif
 #endif
-#ifdef HAVE_SSE
+#if HAVE_SSE
         if((CPUCapFlags&CPU_CAP_SSE))
         if((CPUCapFlags&CPU_CAP_SSE))
             return Resample_<CubicTag,SSETag>;
             return Resample_<CubicTag,SSETag>;
 #endif
 #endif
         return Resample_<CubicTag,CTag>;
         return Resample_<CubicTag,CTag>;
     case Resampler::BSinc12:
     case Resampler::BSinc12:
     case Resampler::BSinc24:
     case Resampler::BSinc24:
+    case Resampler::BSinc48:
         if(increment > MixerFracOne)
         if(increment > MixerFracOne)
         {
         {
-#ifdef HAVE_NEON
+#if HAVE_NEON
             if((CPUCapFlags&CPU_CAP_NEON))
             if((CPUCapFlags&CPU_CAP_NEON))
                 return Resample_<BSincTag,NEONTag>;
                 return Resample_<BSincTag,NEONTag>;
 #endif
 #endif
-#ifdef HAVE_SSE
+#if HAVE_SSE
             if((CPUCapFlags&CPU_CAP_SSE))
             if((CPUCapFlags&CPU_CAP_SSE))
                 return Resample_<BSincTag,SSETag>;
                 return Resample_<BSincTag,SSETag>;
 #endif
 #endif
             return Resample_<BSincTag,CTag>;
             return Resample_<BSincTag,CTag>;
         }
         }
-        /* fall-through */
+        [[fallthrough]];
     case Resampler::FastBSinc12:
     case Resampler::FastBSinc12:
     case Resampler::FastBSinc24:
     case Resampler::FastBSinc24:
-#ifdef HAVE_NEON
+    case Resampler::FastBSinc48:
+#if HAVE_NEON
         if((CPUCapFlags&CPU_CAP_NEON))
         if((CPUCapFlags&CPU_CAP_NEON))
             return Resample_<FastBSincTag,NEONTag>;
             return Resample_<FastBSincTag,NEONTag>;
 #endif
 #endif
-#ifdef HAVE_SSE
+#if HAVE_SSE
         if((CPUCapFlags&CPU_CAP_SSE))
         if((CPUCapFlags&CPU_CAP_SSE))
             return Resample_<FastBSincTag,SSETag>;
             return Resample_<FastBSincTag,SSETag>;
 #endif
 #endif
@@ -268,7 +274,10 @@ ResamplerFunc PrepareResampler(Resampler resampler, uint increment, InterpState
     case Resampler::Point:
     case Resampler::Point:
     case Resampler::Linear:
     case Resampler::Linear:
         break;
         break;
-    case Resampler::Cubic:
+    case Resampler::Spline:
+        state->emplace<CubicState>(al::span{gSplineFilter.mTable});
+        break;
+    case Resampler::Gaussian:
         state->emplace<CubicState>(al::span{gGaussianFilter.mTable});
         state->emplace<CubicState>(al::span{gGaussianFilter.mTable});
         break;
         break;
     case Resampler::FastBSinc12:
     case Resampler::FastBSinc12:
@@ -279,6 +288,10 @@ ResamplerFunc PrepareResampler(Resampler resampler, uint increment, InterpState
     case Resampler::BSinc24:
     case Resampler::BSinc24:
         BsincPrepare(increment, &state->emplace<BsincState>(), &gBSinc24);
         BsincPrepare(increment, &state->emplace<BsincState>(), &gBSinc24);
         break;
         break;
+    case Resampler::FastBSinc48:
+    case Resampler::BSinc48:
+        BsincPrepare(increment, &state->emplace<BsincState>(), &gBSinc48);
+        break;
     }
     }
     return SelectResampler(resampler, increment);
     return SelectResampler(resampler, increment);
 }
 }
@@ -290,8 +303,8 @@ void DeviceBase::ProcessHrtf(const size_t SamplesToDo)
     const size_t lidx{RealOut.ChannelIndex[FrontLeft]};
     const size_t lidx{RealOut.ChannelIndex[FrontLeft]};
     const size_t ridx{RealOut.ChannelIndex[FrontRight]};
     const size_t ridx{RealOut.ChannelIndex[FrontRight]};
 
 
-    MixDirectHrtf(RealOut.Buffer[lidx], RealOut.Buffer[ridx], Dry.Buffer, HrtfAccumData.data(),
-        mHrtfState->mTemp, mHrtfState->mChannels.data(), mHrtfState->mIrSize, SamplesToDo);
+    MixDirectHrtf(RealOut.Buffer[lidx], RealOut.Buffer[ridx], Dry.Buffer, HrtfAccumData,
+        mHrtfState->mTemp, mHrtfState->mChannels, mHrtfState->mIrSize, SamplesToDo);
 }
 }
 
 
 void DeviceBase::ProcessAmbiDec(const size_t SamplesToDo)
 void DeviceBase::ProcessAmbiDec(const size_t SamplesToDo)
@@ -330,7 +343,8 @@ void DeviceBase::ProcessBs2b(const size_t SamplesToDo)
     const size_t ridx{RealOut.ChannelIndex[FrontRight]};
     const size_t ridx{RealOut.ChannelIndex[FrontRight]};
 
 
     /* Now apply the BS2B binaural/crossfeed filter. */
     /* Now apply the BS2B binaural/crossfeed filter. */
-    Bs2b->cross_feed(RealOut.Buffer[lidx].data(), RealOut.Buffer[ridx].data(), SamplesToDo);
+    Bs2b->cross_feed(al::span{RealOut.Buffer[lidx]}.first(SamplesToDo),
+        al::span{RealOut.Buffer[ridx]}.first(SamplesToDo));
 }
 }
 
 
 
 
@@ -430,11 +444,19 @@ bool CalcContextParams(ContextBase *ctx)
     ctx->mParams.Velocity = rot * vel;
     ctx->mParams.Velocity = rot * vel;
 
 
     ctx->mParams.Gain = props->Gain * ctx->mGainBoost;
     ctx->mParams.Gain = props->Gain * ctx->mGainBoost;
-    ctx->mParams.MetersPerUnit = props->MetersPerUnit;
+    ctx->mParams.MetersPerUnit = props->MetersPerUnit
+#if ALSOFT_EAX
+        * props->DistanceFactor
+#endif
+        ;
     ctx->mParams.AirAbsorptionGainHF = props->AirAbsorptionGainHF;
     ctx->mParams.AirAbsorptionGainHF = props->AirAbsorptionGainHF;
 
 
     ctx->mParams.DopplerFactor = props->DopplerFactor;
     ctx->mParams.DopplerFactor = props->DopplerFactor;
-    ctx->mParams.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
+    ctx->mParams.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity
+#if ALSOFT_EAX
+        / props->DistanceFactor
+#endif
+        ;
 
 
     ctx->mParams.SourceDistanceModel = props->SourceDistanceModel;
     ctx->mParams.SourceDistanceModel = props->SourceDistanceModel;
     ctx->mParams.mDistanceModel = props->mDistanceModel;
     ctx->mParams.mDistanceModel = props->mDistanceModel;
@@ -458,23 +480,27 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBa
     slot->Target = props->Target;
     slot->Target = props->Target;
     slot->EffectType = props->Type;
     slot->EffectType = props->Type;
     slot->mEffectProps = props->Props;
     slot->mEffectProps = props->Props;
+
+    slot->RoomRolloff = 0.0f;
+    slot->DecayTime = 0.0f;
+    slot->DecayLFRatio = 0.0f;
+    slot->DecayHFRatio = 0.0f;
+    slot->DecayHFLimit = false;
+    slot->AirAbsorptionGainHF = 1.0f;
     if(auto *reverbprops = std::get_if<ReverbProps>(&props->Props))
     if(auto *reverbprops = std::get_if<ReverbProps>(&props->Props))
     {
     {
         slot->RoomRolloff = reverbprops->RoomRolloffFactor;
         slot->RoomRolloff = reverbprops->RoomRolloffFactor;
-        slot->DecayTime = reverbprops->DecayTime;
-        slot->DecayLFRatio = reverbprops->DecayLFRatio;
-        slot->DecayHFRatio = reverbprops->DecayHFRatio;
-        slot->DecayHFLimit = reverbprops->DecayHFLimit;
         slot->AirAbsorptionGainHF = reverbprops->AirAbsorptionGainHF;
         slot->AirAbsorptionGainHF = reverbprops->AirAbsorptionGainHF;
-    }
-    else
-    {
-        slot->RoomRolloff = 0.0f;
-        slot->DecayTime = 0.0f;
-        slot->DecayLFRatio = 0.0f;
-        slot->DecayHFRatio = 0.0f;
-        slot->DecayHFLimit = false;
-        slot->AirAbsorptionGainHF = 1.0f;
+        /* If this effect slot's Auxiliary Send Auto is off, don't apply the
+         * automatic send adjustments based on source distance.
+         */
+        if(slot->AuxSendAuto)
+        {
+            slot->DecayTime = reverbprops->DecayTime;
+            slot->DecayLFRatio = reverbprops->DecayLFRatio;
+            slot->DecayHFRatio = reverbprops->DecayHFRatio;
+            slot->DecayHFLimit = reverbprops->DecayHFLimit;
+        }
     }
     }
 
 
     EffectState *state{props->State.release()};
     EffectState *state{props->State.release()};
@@ -489,9 +515,9 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBa
         /* Otherwise, if it would be deleted send it off with a release event. */
         /* Otherwise, if it would be deleted send it off with a release event. */
         RingBuffer *ring{context->mAsyncEvents.get()};
         RingBuffer *ring{context->mAsyncEvents.get()};
         auto evt_vec = ring->getWriteVector();
         auto evt_vec = ring->getWriteVector();
-        if(evt_vec.first.len > 0) LIKELY
+        if(evt_vec[0].len > 0) LIKELY
         {
         {
-            auto &evt = InitAsyncEvent<AsyncEffectReleaseEvent>(evt_vec.first.buf);
+            auto &evt = InitAsyncEvent<AsyncEffectReleaseEvent>(evt_vec[0].buf);
             evt.mEffectState = oldstate;
             evt.mEffectState = oldstate;
             ring->writeAdvance(1);
             ring->writeAdvance(1);
         }
         }
@@ -508,14 +534,13 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBa
 
 
     AtomicReplaceHead(context->mFreeEffectSlotProps, props);
     AtomicReplaceHead(context->mFreeEffectSlotProps, props);
 
 
-    EffectTarget output;
-    if(EffectSlot *target{slot->Target})
-        output = EffectTarget{&target->Wet, nullptr};
-    else
+    const auto output = [slot,context]() -> EffectTarget
     {
     {
+        if(EffectSlot *target{slot->Target})
+            return EffectTarget{&target->Wet, nullptr};
         DeviceBase *device{context->mDevice};
         DeviceBase *device{context->mDevice};
-        output = EffectTarget{&device->Dry, &device->RealOut};
-    }
+        return EffectTarget{&device->Dry, &device->RealOut};
+    }();
     state->update(context, slot, &slot->mEffectProps, output);
     state->update(context, slot, &slot->mEffectProps, output);
     return true;
     return true;
 }
 }
@@ -683,8 +708,8 @@ void AmbiRotator(AmbiRotateMatrix &matrix, const int order)
     /* Don't do anything for < 2nd order. */
     /* Don't do anything for < 2nd order. */
     if(order < 2) return;
     if(order < 2) return;
 
 
-    auto P = [](const int i, const int l, const int a, const int n, const size_t last_band,
-        const AmbiRotateMatrix &R)
+    static constexpr auto P = [](const int i, const int l, const int a, const int n,
+        const size_t last_band, const AmbiRotateMatrix &R)
     {
     {
         const float ri1{ R[ 1+2][static_cast<size_t>(i+2_z)]};
         const float ri1{ R[ 1+2][static_cast<size_t>(i+2_z)]};
         const float rim1{R[-1+2][static_cast<size_t>(i+2_z)]};
         const float rim1{R[-1+2][static_cast<size_t>(i+2_z)]};
@@ -698,12 +723,12 @@ void AmbiRotator(AmbiRotateMatrix &matrix, const int order)
         return ri0*R[last_band + static_cast<size_t>(l-1_z+n)][y];
         return ri0*R[last_band + static_cast<size_t>(l-1_z+n)][y];
     };
     };
 
 
-    auto U = [P](const int l, const int m, const int n, const size_t last_band,
+    static constexpr auto U = [](const int l, const int m, const int n, const size_t last_band,
         const AmbiRotateMatrix &R)
         const AmbiRotateMatrix &R)
     {
     {
         return P(0, l, m, n, last_band, R);
         return P(0, l, m, n, last_band, R);
     };
     };
-    auto V = [P](const int l, const int m, const int n, const size_t last_band,
+    static constexpr auto V = [](const int l, const int m, const int n, const size_t last_band,
         const AmbiRotateMatrix &R)
         const AmbiRotateMatrix &R)
     {
     {
         using namespace al::numbers;
         using namespace al::numbers;
@@ -719,7 +744,7 @@ void AmbiRotator(AmbiRotateMatrix &matrix, const int order)
         const float p1{P(-1, l, -m-1, n, last_band, R)};
         const float p1{P(-1, l, -m-1, n, last_band, R)};
         return d ? p1*sqrt2_v<float> : (p0 + p1);
         return d ? p1*sqrt2_v<float> : (p0 + p1);
     };
     };
-    auto W = [P](const int l, const int m, const int n, const size_t last_band,
+    static constexpr auto W = [](const int l, const int m, const int n, const size_t last_band,
         const AmbiRotateMatrix &R)
         const AmbiRotateMatrix &R)
     {
     {
         assert(m != 0);
         assert(m != 0);
@@ -833,7 +858,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
         ChanPosMap{FrontRight,  std::array{ sin30, 0.0f, -cos30}},
         ChanPosMap{FrontRight,  std::array{ sin30, 0.0f, -cos30}},
     };
     };
 
 
-    const auto Frequency = static_cast<float>(Device->Frequency);
+    const auto Frequency = static_cast<float>(Device->mSampleRate);
     const uint NumSends{Device->NumAuxSends};
     const uint NumSends{Device->NumAuxSends};
 
 
     const size_t num_channels{voice->mChans.size()};
     const size_t num_channels{voice->mChans.size()};
@@ -915,6 +940,10 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
         case TopBackLeft: return lgain;
         case TopBackLeft: return lgain;
         case TopBackCenter: break;
         case TopBackCenter: break;
         case TopBackRight: return rgain;
         case TopBackRight: return rgain;
+        case BottomFrontLeft: return lgain;
+        case BottomFrontRight: return rgain;
+        case BottomBackLeft: return lgain;
+        case BottomBackRight: return rgain;
         case Aux0: case Aux1: case Aux2: case Aux3: case Aux4: case Aux5: case Aux6: case Aux7:
         case Aux0: case Aux1: case Aux2: case Aux3: case Aux4: case Aux5: case Aux6: case Aux7:
         case Aux8: case Aux9: case Aux10: case Aux11: case Aux12: case Aux13: case Aux14:
         case Aux8: case Aux9: case Aux10: case Aux11: case Aux12: case Aux13: case Aux14:
         case Aux15: case MaxChannels: break;
         case Aux15: case MaxChannels: break;
@@ -1176,8 +1205,6 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
             }
             }
             else for(size_t c{0};c < num_channels;c++)
             else for(size_t c{0};c < num_channels;c++)
             {
             {
-                using namespace al::numbers;
-
                 /* Skip LFE */
                 /* Skip LFE */
                 if(chans[c].channel == LFE) continue;
                 if(chans[c].channel == LFE) continue;
                 const float pangain{SelectChannelGain(chans[c].channel)};
                 const float pangain{SelectChannelGain(chans[c].channel)};
@@ -1187,7 +1214,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
                  * the source position, at full spread (pi*2), each channel is
                  * the source position, at full spread (pi*2), each channel is
                  * left unchanged.
                  * left unchanged.
                  */
                  */
-                const float a{1.0f - (inv_pi_v<float>/2.0f)*Spread};
+                const float a{1.0f - (al::numbers::inv_pi_v<float>/2.0f)*Spread};
                 std::array pos{
                 std::array pos{
                     lerpf(chans[c].pos[0], xpos, a),
                     lerpf(chans[c].pos[0], xpos, a),
                     lerpf(chans[c].pos[1], ypos, a),
                     lerpf(chans[c].pos[1], ypos, a),
@@ -1303,57 +1330,51 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
                             voice->mChans[0].mWetParams[i].Gains.Target);
                             voice->mChans[0].mWetParams[i].Gains.Target);
                 }
                 }
             }
             }
-            else
+            else for(size_t c{0};c < num_channels;c++)
             {
             {
-                using namespace al::numbers;
+                const auto pangain = SelectChannelGain(chans[c].channel);
 
 
-                for(size_t c{0};c < num_channels;c++)
+                /* Special-case LFE */
+                if(chans[c].channel == LFE)
                 {
                 {
-                    const float pangain{SelectChannelGain(chans[c].channel)};
-
-                    /* Special-case LFE */
-                    if(chans[c].channel == LFE)
+                    if(Device->Dry.Buffer.data() == Device->RealOut.Buffer.data())
                     {
                     {
-                        if(Device->Dry.Buffer.data() == Device->RealOut.Buffer.data())
-                        {
-                            const uint idx{Device->channelIdxByName(chans[c].channel)};
-                            if(idx != InvalidChannelIndex)
-                                voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base
-                                    * pangain;
-                        }
-                        continue;
+                        const auto idx = uint{Device->channelIdxByName(chans[c].channel)};
+                        if(idx != InvalidChannelIndex)
+                            voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base * pangain;
                     }
                     }
+                    continue;
+                }
 
 
-                    /* Warp the channel position toward the source position as
-                     * the spread decreases. With no spread, all channels are
-                     * at the source position, at full spread (pi*2), each
-                     * channel position is left unchanged.
-                     */
-                    const float a{1.0f - (inv_pi_v<float>/2.0f)*Spread};
-                    std::array pos{
-                        lerpf(chans[c].pos[0], xpos, a),
-                        lerpf(chans[c].pos[1], ypos, a),
-                        lerpf(chans[c].pos[2], zpos, a)};
-                    const float len{std::sqrt(pos[0]*pos[0] + pos[1]*pos[1] + pos[2]*pos[2])};
-                    if(len < 1.0f)
-                    {
-                        pos[0] /= len;
-                        pos[1] /= len;
-                        pos[2] /= len;
-                    }
+                /* Warp the channel position toward the source position as the
+                 * spread decreases. With no spread, all channels are at the
+                 * source position, at full spread (pi*2), each channel
+                 * position is left unchanged.
+                 */
+                const auto a = 1.0f - (al::numbers::inv_pi_v<float>/2.0f)*Spread;
+                auto pos = std::array{
+                    lerpf(chans[c].pos[0], xpos, a),
+                    lerpf(chans[c].pos[1], ypos, a),
+                    lerpf(chans[c].pos[2], zpos, a)};
+                const auto len = std::sqrt(pos[0]*pos[0] + pos[1]*pos[1] + pos[2]*pos[2]);
+                if(len < 1.0f)
+                {
+                    pos[0] /= len;
+                    pos[1] /= len;
+                    pos[2] /= len;
+                }
 
 
-                    if(Device->mRenderMode == RenderMode::Pairwise)
-                        pos = ScaleAzimuthFront3(pos);
-                    const auto coeffs = CalcDirectionCoeffs(pos, 0.0f);
+                if(Device->mRenderMode == RenderMode::Pairwise)
+                    pos = ScaleAzimuthFront3(pos);
+                const auto coeffs = CalcDirectionCoeffs(pos, 0.0f);
 
 
-                    ComputePanGains(&Device->Dry, coeffs, DryGain.Base * pangain,
-                        voice->mChans[c].mDryParams.Gains.Target);
-                    for(uint i{0};i < NumSends;i++)
-                    {
-                        if(const EffectSlot *Slot{SendSlots[i]})
-                            ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base * pangain,
-                                voice->mChans[c].mWetParams[i].Gains.Target);
-                    }
+                ComputePanGains(&Device->Dry, coeffs, DryGain.Base * pangain,
+                    voice->mChans[c].mDryParams.Gains.Target);
+                for(uint i{0};i < NumSends;i++)
+                {
+                    if(const EffectSlot *Slot{SendSlots[i]})
+                        ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base * pangain,
+                            voice->mChans[c].mWetParams[i].Gains.Target);
                 }
                 }
             }
             }
         }
         }
@@ -1449,7 +1470,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
 void CalcNonAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBase *context)
 void CalcNonAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBase *context)
 {
 {
     DeviceBase *Device{context->mDevice};
     DeviceBase *Device{context->mDevice};
-    std::array<EffectSlot*,MaxSendCount> SendSlots;
+    std::array<EffectSlot*,MaxSendCount> SendSlots{};
 
 
     voice->mDirect.Buffer = Device->Dry.Buffer;
     voice->mDirect.Buffer = Device->Dry.Buffer;
     for(uint i{0};i < Device->NumAuxSends;i++)
     for(uint i{0};i < Device->NumAuxSends;i++)
@@ -1466,7 +1487,7 @@ void CalcNonAttnSourceParams(Voice *voice, const VoiceProps *props, const Contex
 
 
     /* Calculate the stepping value */
     /* Calculate the stepping value */
     const auto Pitch = static_cast<float>(voice->mFrequency) /
     const auto Pitch = static_cast<float>(voice->mFrequency) /
-        static_cast<float>(Device->Frequency) * props->Pitch;
+        static_cast<float>(Device->mSampleRate) * props->Pitch;
     if(Pitch > float{MaxPitch})
     if(Pitch > float{MaxPitch})
         voice->mStep = MaxPitch<<MixerFracBits;
         voice->mStep = MaxPitch<<MixerFracBits;
     else
     else
@@ -1474,13 +1495,13 @@ void CalcNonAttnSourceParams(Voice *voice, const VoiceProps *props, const Contex
     voice->mResampler = PrepareResampler(props->mResampler, voice->mStep, &voice->mResampleState);
     voice->mResampler = PrepareResampler(props->mResampler, voice->mStep, &voice->mResampleState);
 
 
     /* Calculate gains */
     /* Calculate gains */
-    GainTriplet DryGain;
-    DryGain.Base  = std::min(std::clamp(props->Gain, props->MinGain, props->MaxGain) *
+    GainTriplet DryGain{};
+    DryGain.Base = std::min(std::clamp(props->Gain, props->MinGain, props->MaxGain) *
         props->Direct.Gain * context->mParams.Gain, GainMixMax);
         props->Direct.Gain * context->mParams.Gain, GainMixMax);
     DryGain.HF = props->Direct.GainHF;
     DryGain.HF = props->Direct.GainHF;
     DryGain.LF = props->Direct.GainLF;
     DryGain.LF = props->Direct.GainLF;
 
 
-    std::array<GainTriplet,MaxSendCount> WetGain;
+    std::array<GainTriplet,MaxSendCount> WetGain{};
     for(uint i{0};i < Device->NumAuxSends;i++)
     for(uint i{0};i < Device->NumAuxSends;i++)
     {
     {
         WetGain[i].Base = std::min(std::clamp(props->Gain, props->MinGain, props->MaxGain) *
         WetGain[i].Base = std::min(std::clamp(props->Gain, props->MinGain, props->MaxGain) *
@@ -1502,33 +1523,24 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa
     voice->mDirect.Buffer = Device->Dry.Buffer;
     voice->mDirect.Buffer = Device->Dry.Buffer;
     std::array<EffectSlot*,MaxSendCount> SendSlots{};
     std::array<EffectSlot*,MaxSendCount> SendSlots{};
     std::array<float,MaxSendCount> RoomRolloff{};
     std::array<float,MaxSendCount> RoomRolloff{};
-    std::bitset<MaxSendCount> UseDryAttnForRoom{0};
     for(uint i{0};i < NumSends;i++)
     for(uint i{0};i < NumSends;i++)
     {
     {
         SendSlots[i] = props->Send[i].Slot;
         SendSlots[i] = props->Send[i].Slot;
         if(!SendSlots[i] || SendSlots[i]->EffectType == EffectSlotType::None)
         if(!SendSlots[i] || SendSlots[i]->EffectType == EffectSlotType::None)
+        {
             SendSlots[i] = nullptr;
             SendSlots[i] = nullptr;
-        else if(SendSlots[i]->AuxSendAuto)
+            voice->mSend[i].Buffer = {};
+        }
+        else
         {
         {
             /* NOTE: Contrary to the EFX docs, the effect's room rolloff factor
             /* NOTE: Contrary to the EFX docs, the effect's room rolloff factor
              * applies to the selected distance model along with the source's
              * applies to the selected distance model along with the source's
              * room rolloff factor, not necessarily the inverse distance model.
              * room rolloff factor, not necessarily the inverse distance model.
-             *
-             * Generic Software also applies these rolloff factors regardless
-             * of any setting. It doesn't seem to use the effect slot's send
-             * auto for anything, though as far as I understand, it's supposed
-             * to control whether the send gets the same gain/gainhf as the
-             * direct path (excluding the filter).
              */
              */
             RoomRolloff[i] = props->RoomRolloffFactor + SendSlots[i]->RoomRolloff;
             RoomRolloff[i] = props->RoomRolloffFactor + SendSlots[i]->RoomRolloff;
-        }
-        else
-            UseDryAttnForRoom.set(i);
 
 
-        if(!SendSlots[i])
-            voice->mSend[i].Buffer = {};
-        else
             voice->mSend[i].Buffer = SendSlots[i]->Wet.Buffer;
             voice->mSend[i].Buffer = SendSlots[i]->Wet.Buffer;
+        }
     }
     }
 
 
     /* Transform source to listener space (convert to head relative) */
     /* Transform source to listener space (convert to head relative) */
@@ -1663,28 +1675,34 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa
     std::array<GainTriplet,MaxSendCount> WetGain{};
     std::array<GainTriplet,MaxSendCount> WetGain{};
     for(uint i{0};i < NumSends;i++)
     for(uint i{0};i < NumSends;i++)
     {
     {
-        WetGainBase[i] = std::clamp(WetGainBase[i]*WetCone, props->MinGain, props->MaxGain) *
+        const auto gain = std::clamp(WetGainBase[i]*WetCone, props->MinGain, props->MaxGain) *
             context->mParams.Gain;
             context->mParams.Gain;
-        /* If this effect slot's Auxiliary Send Auto is off, then use the dry
-         * path distance and cone attenuation, otherwise use the wet (room)
-         * path distance and cone attenuation. The send filter is used instead
-         * of the direct filter, regardless.
-         */
-        const bool use_room{!UseDryAttnForRoom.test(i)};
-        const float gain{use_room ? WetGainBase[i] : DryGainBase};
         WetGain[i].Base = std::min(gain * props->Send[i].Gain, GainMixMax);
         WetGain[i].Base = std::min(gain * props->Send[i].Gain, GainMixMax);
-        WetGain[i].HF = (use_room ? WetConeHF : ConeHF) * props->Send[i].GainHF;
+        WetGain[i].HF = WetConeHF * props->Send[i].GainHF;
         WetGain[i].LF = props->Send[i].GainLF;
         WetGain[i].LF = props->Send[i].GainLF;
     }
     }
 
 
     /* Distance-based air absorption and initial send decay. */
     /* Distance-based air absorption and initial send decay. */
     if(Distance > props->RefDistance) LIKELY
     if(Distance > props->RefDistance) LIKELY
     {
     {
-        const float distance_base{(Distance-props->RefDistance) * props->RolloffFactor};
-        const float distance_meters{distance_base * context->mParams.MetersPerUnit};
-        const float dryabsorb{distance_meters * props->AirAbsorptionFactor};
-        if(dryabsorb > std::numeric_limits<float>::epsilon())
-            DryGain.HF *= std::pow(context->mParams.AirAbsorptionGainHF, dryabsorb);
+        /* FIXME: In keeping with EAX, the base air absorption gain should be
+         * taken from the reverb property in the "primary fx slot" when it has
+         * a reverb effect and the environment flag set, and be applied to the
+         * direct path and all environment sends, rather than each path using
+         * the air absorption gain associated with the given slot's effect. At
+         * this point in the mixer, and even in EFX itself, there's no concept
+         * of a "primary fx slot" so it's unclear which effect slot should be
+         * checked.
+         *
+         * The HF reference is also intended to be handled the same way, but
+         * again, there's no concept of a "primary fx slot" here and no way to
+         * know which effect slot to look at for the reference frequency.
+         */
+        const auto distance_units = float{(Distance-props->RefDistance) * props->RolloffFactor};
+        const auto distance_meters = float{distance_units * context->mParams.MetersPerUnit};
+        const auto absorb = float{distance_meters * props->AirAbsorptionFactor};
+        if(absorb > std::numeric_limits<float>::epsilon())
+            DryGain.HF *= std::pow(context->mParams.AirAbsorptionGainHF, absorb);
 
 
         /* If the source's Auxiliary Send Filter Gain Auto is off, no extra
         /* If the source's Auxiliary Send Filter Gain Auto is off, no extra
          * adjustment is applied to the send gains.
          * adjustment is applied to the send gains.
@@ -1694,18 +1712,9 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa
             if(!SendSlots[i] || !(SendSlots[i]->DecayTime > 0.0f))
             if(!SendSlots[i] || !(SendSlots[i]->DecayTime > 0.0f))
                 continue;
                 continue;
 
 
-            if(distance_meters > std::numeric_limits<float>::epsilon())
-                WetGain[i].HF *= std::pow(SendSlots[i]->AirAbsorptionGainHF, distance_meters);
-
-            /* If this effect slot's Auxiliary Send Auto is off, don't apply
-             * the automatic initial reverb decay.
-             *
-             * NOTE: Generic Software applies the initial decay regardless of
-             * this setting. It doesn't seem to use it for anything, only the
-             * source's send filter gain auto flag affects this.
-             */
-            if(!SendSlots[i]->AuxSendAuto)
-                continue;
+            if(SendSlots[i]->AirAbsorptionGainHF < 1.0f
+                && absorb > std::numeric_limits<float>::epsilon())
+                WetGain[i].HF *= std::pow(SendSlots[i]->AirAbsorptionGainHF, absorb);
 
 
             const float DecayDistance{SendSlots[i]->DecayTime * SpeedOfSoundMetersPerSec};
             const float DecayDistance{SendSlots[i]->DecayTime * SpeedOfSoundMetersPerSec};
 
 
@@ -1719,7 +1728,7 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa
              * with the reverb and source rolloff parameters.
              * with the reverb and source rolloff parameters.
              */
              */
             const float baseAttn{DryAttnBase};
             const float baseAttn{DryAttnBase};
-            const float fact{distance_base / DecayDistance};
+            const float fact{distance_meters / DecayDistance};
             const float gain{std::pow(ReverbDecayGain, fact)*(1.0f-baseAttn) + baseAttn};
             const float gain{std::pow(ReverbDecayGain, fact)*(1.0f-baseAttn) + baseAttn};
             WetGain[i].Base *= gain;
             WetGain[i].Base *= gain;
         }
         }
@@ -1764,7 +1773,7 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa
     /* Adjust pitch based on the buffer and output frequencies, and calculate
     /* Adjust pitch based on the buffer and output frequencies, and calculate
      * fixed-point stepping value.
      * fixed-point stepping value.
      */
      */
-    Pitch *= static_cast<float>(voice->mFrequency) / static_cast<float>(Device->Frequency);
+    Pitch *= static_cast<float>(voice->mFrequency) / static_cast<float>(Device->mSampleRate);
     if(Pitch > float{MaxPitch})
     if(Pitch > float{MaxPitch})
         voice->mStep = MaxPitch<<MixerFracBits;
         voice->mStep = MaxPitch<<MixerFracBits;
     else
     else
@@ -1807,9 +1816,9 @@ void SendSourceStateEvent(ContextBase *context, uint id, VChangeState state)
 {
 {
     RingBuffer *ring{context->mAsyncEvents.get()};
     RingBuffer *ring{context->mAsyncEvents.get()};
     auto evt_vec = ring->getWriteVector();
     auto evt_vec = ring->getWriteVector();
-    if(evt_vec.first.len < 1) return;
+    if(evt_vec[0].len < 1) return;
 
 
-    auto &evt = InitAsyncEvent<AsyncSourceStateEvent>(evt_vec.first.buf);
+    auto &evt = InitAsyncEvent<AsyncSourceStateEvent>(evt_vec[0].buf);
     evt.mId = id;
     evt.mId = id;
     switch(state)
     switch(state)
     {
     {
@@ -1929,7 +1938,7 @@ void ProcessVoiceChanges(ContextBase *ctx)
 }
 }
 
 
 void ProcessParamUpdates(ContextBase *ctx, const al::span<EffectSlot*> slots,
 void ProcessParamUpdates(ContextBase *ctx, const al::span<EffectSlot*> slots,
-    const al::span<Voice*> voices)
+    const al::span<EffectSlot*> sorted_slots, const al::span<Voice*> voices)
 {
 {
     ProcessVoiceChanges(ctx);
     ProcessVoiceChanges(ctx);
 
 
@@ -1937,9 +1946,9 @@ void ProcessParamUpdates(ContextBase *ctx, const al::span<EffectSlot*> slots,
     if(!ctx->mHoldUpdates.load(std::memory_order_acquire)) LIKELY
     if(!ctx->mHoldUpdates.load(std::memory_order_acquire)) LIKELY
     {
     {
         bool force{CalcContextParams(ctx)};
         bool force{CalcContextParams(ctx)};
-        auto sorted_slots = al::to_address(slots.end());
+        auto sorted_slot_base = al::to_address(sorted_slots.begin());
         for(EffectSlot *slot : slots)
         for(EffectSlot *slot : slots)
-            force |= CalcEffectSlotParams(slot, sorted_slots, ctx);
+            force |= CalcEffectSlotParams(slot, sorted_slot_base, ctx);
 
 
         for(Voice *voice : voices)
         for(Voice *voice : voices)
         {
         {
@@ -1955,97 +1964,93 @@ void ProcessContexts(DeviceBase *device, const uint SamplesToDo)
 {
 {
     ASSUME(SamplesToDo > 0);
     ASSUME(SamplesToDo > 0);
 
 
-    const nanoseconds curtime{device->mClockBase.load(std::memory_order_relaxed) +
-        nanoseconds{seconds{device->mSamplesDone.load(std::memory_order_relaxed)}}/
-        device->Frequency};
+    const auto curtime = device->getClockTime();
 
 
-    for(ContextBase *ctx : *device->mContexts.load(std::memory_order_acquire))
+    auto proc_context = [SamplesToDo,curtime](ContextBase *ctx)
     {
     {
-        auto auxslots = al::span{*ctx->mActiveAuxSlots.load(std::memory_order_acquire)};
-        const al::span<Voice*> voices{ctx->getVoicesSpanAcquired()};
+        const auto auxslotspan = al::span{*ctx->mActiveAuxSlots.load(std::memory_order_acquire)};
+        const auto auxslots = auxslotspan.first(auxslotspan.size()>>1);
+        const auto sorted_slots = auxslotspan.last(auxslotspan.size()>>1);
+        const auto voices = ctx->getVoicesSpanAcquired();
 
 
         /* Process pending property updates for objects on the context. */
         /* Process pending property updates for objects on the context. */
-        ProcessParamUpdates(ctx, auxslots, voices);
+        ProcessParamUpdates(ctx, auxslots, sorted_slots, voices);
 
 
         /* Clear auxiliary effect slot mixing buffers. */
         /* Clear auxiliary effect slot mixing buffers. */
-        for(EffectSlot *slot : auxslots)
+        auto clear_wetbuffers = [](EffectSlot *slot)
         {
         {
-            for(auto &buffer : slot->Wet.Buffer)
-                buffer.fill(0.0f);
-        }
+            auto clear_buffer = [](const FloatBufferSpan buffer)
+            { std::fill(buffer.begin(), buffer.end(), 0.0f); };
+            std::for_each(slot->Wet.Buffer.begin(), slot->Wet.Buffer.end(), clear_buffer);
+        };
+        std::for_each(auxslots.begin(), auxslots.end(), clear_wetbuffers);
 
 
         /* Process voices that have a playing source. */
         /* Process voices that have a playing source. */
-        for(Voice *voice : voices)
+        auto proc_voice = [ctx,curtime,SamplesToDo](Voice *voice)
         {
         {
             const Voice::State vstate{voice->mPlayState.load(std::memory_order_acquire)};
             const Voice::State vstate{voice->mPlayState.load(std::memory_order_acquire)};
             if(vstate != Voice::Stopped && vstate != Voice::Pending)
             if(vstate != Voice::Stopped && vstate != Voice::Pending)
                 voice->mix(vstate, ctx, curtime, SamplesToDo);
                 voice->mix(vstate, ctx, curtime, SamplesToDo);
-        }
+        };
+        std::for_each(voices.begin(), voices.end(), proc_voice);
 
 
         /* Process effects. */
         /* Process effects. */
         if(!auxslots.empty())
         if(!auxslots.empty())
         {
         {
             /* Sort the slots into extra storage, so that effect slots come
             /* Sort the slots into extra storage, so that effect slots come
-             * before their effect slot target (or their targets' target).
+             * before their effect slot target (or their targets' target). Skip
+             * sorting if it has already been done.
              */
              */
-            const al::span sorted_slots{al::to_address(auxslots.end()), auxslots.size()};
-            /* Skip sorting if it has already been done. */
             if(!sorted_slots[0])
             if(!sorted_slots[0])
             {
             {
-                /* First, copy the slots to the sorted list, then partition the
-                 * sorted list so that all slots without a target slot go to
-                 * the end.
+                /* First, copy the slots to the sorted list and partition them,
+                 * so that all slots without a target slot go to the end.
                  */
                  */
-                std::copy(auxslots.begin(), auxslots.end(), sorted_slots.begin());
-                auto split_point = std::partition(sorted_slots.begin(), sorted_slots.end(),
-                    [](const EffectSlot *slot) noexcept -> bool
-                    { return slot->Target != nullptr; });
+                auto has_target = [](const EffectSlot *slot) noexcept -> bool
+                { return slot->Target != nullptr; };
+                auto split_point = std::partition_copy(auxslots.rbegin(), auxslots.rend(),
+                    sorted_slots.begin(), sorted_slots.rbegin(), has_target).first;
                 /* There must be at least one slot without a slot target. */
                 /* There must be at least one slot without a slot target. */
                 assert(split_point != sorted_slots.end());
                 assert(split_point != sorted_slots.end());
 
 
-                /* Simple case: no more than 1 slot has a target slot. Either
-                 * all slots go right to the output, or the remaining one must
-                 * target an already-partitioned slot.
+                /* Starting from the back of the sorted list, continue
+                 * partitioning the front of the list given each target until
+                 * all targets are accounted for. This ensures all slots
+                 * without a target go last, all slots directly targeting those
+                 * last slots go second-to-last, all slots directly targeting
+                 * those second-last slots go third-to-last, etc.
                  */
                  */
-                if(split_point - sorted_slots.begin() > 1)
+                auto next_target = sorted_slots.end();
+                while(std::distance(sorted_slots.begin(), split_point) > 1)
                 {
                 {
-                    /* At least two slots target other slots. Starting from the
-                     * back of the sorted list, continue partitioning the front
-                     * of the list given each target until all targets are
-                     * accounted for. This ensures all slots without a target
-                     * go last, all slots directly targeting those last slots
-                     * go second-to-last, all slots directly targeting those
-                     * second-last slots go third-to-last, etc.
+                    /* This shouldn't happen, but if there's unsorted slots
+                     * left that don't target any sorted slots, they can't
+                     * contribute to the output, so leave them.
                      */
                      */
-                    auto next_target = sorted_slots.end();
-                    do {
-                        /* This shouldn't happen, but if there's unsorted slots
-                         * left that don't target any sorted slots, they can't
-                         * contribute to the output, so leave them.
-                         */
-                        if(next_target == split_point) UNLIKELY
-                            break;
-
-                        --next_target;
-                        split_point = std::partition(sorted_slots.begin(), split_point,
-                            [next_target](const EffectSlot *slot) noexcept -> bool
-                            { return slot->Target != *next_target; });
-                    } while(split_point - sorted_slots.begin() > 1);
+                    if(next_target == split_point) UNLIKELY
+                        break;
+
+                    --next_target;
+                    auto not_next = [next_target](const EffectSlot *slot) noexcept -> bool
+                    { return slot->Target != *next_target; };
+                    split_point = std::partition(sorted_slots.begin(), split_point, not_next);
                 }
                 }
             }
             }
 
 
-            for(const EffectSlot *slot : sorted_slots)
+            auto proc_slot = [SamplesToDo](const EffectSlot *slot)
             {
             {
                 EffectState *state{slot->mEffectState.get()};
                 EffectState *state{slot->mEffectState.get()};
                 state->process(SamplesToDo, slot->Wet.Buffer, state->mOutTarget);
                 state->process(SamplesToDo, slot->Wet.Buffer, state->mOutTarget);
-            }
+            };
+            std::for_each(sorted_slots.begin(), sorted_slots.end(), proc_slot);
         }
         }
 
 
         /* Signal the event handler if there are any events to read. */
         /* Signal the event handler if there are any events to read. */
-        RingBuffer *ring{ctx->mAsyncEvents.get()};
-        if(ring->readSpace() > 0)
+        if(RingBuffer *ring{ctx->mAsyncEvents.get()}; ring->readSpace() > 0)
             ctx->mEventSem.post();
             ctx->mEventSem.post();
-    }
+    };
+    const auto contexts = al::span{*device->mContexts.load(std::memory_order_acquire)};
+    std::for_each(contexts.begin(), contexts.end(), proc_context);
 }
 }
 
 
 
 
@@ -2144,25 +2149,47 @@ void Write(const al::span<const FloatBufferLine> InBuffer, void *OutBuffer, cons
     ASSUME(FrameStep > 0);
     ASSUME(FrameStep > 0);
     ASSUME(SamplesToDo > 0);
     ASSUME(SamplesToDo > 0);
 
 
-    const auto output = al::span{static_cast<T*>(OutBuffer), (Offset+SamplesToDo)*FrameStep}
-        .subspan(Offset*FrameStep);
-    size_t c{0};
-    for(const FloatBufferLine &inbuf : InBuffer)
+    /* Some Clang versions don't like calling subspan on an rvalue here. */
+    const auto output_ = al::span{static_cast<T*>(OutBuffer), (Offset+SamplesToDo)*FrameStep};
+    const auto output = output_.subspan(Offset*FrameStep);
+
+    /* If there's extra channels in the interleaved output buffer to skip,
+     * clear the whole output buffer. This is simpler to ensure the extra
+     * channels are silent than trying to clear just the extra channels.
+     */
+    if(FrameStep > InBuffer.size())
+        std::fill(output.begin(), output.end(), SampleConv<T>(0.0f));
+
+    auto outbase = output.begin();
+    for(const auto &srcbuf : InBuffer)
     {
     {
-        auto out = output.begin();
-        auto conv_sample = [FrameStep,c,&out](const float s) noexcept
+        const auto src = al::span{srcbuf}.first(SamplesToDo);
+        auto out = outbase++;
+
+        *out = SampleConv<T>(src.front());
+        std::for_each(src.begin()+1, src.end(), [FrameStep,&out](const float s) noexcept
         {
         {
-            out[c] = SampleConv<T>(s);
             out += ptrdiff_t(FrameStep);
             out += ptrdiff_t(FrameStep);
-        };
-        std::for_each_n(inbuf.cbegin(), SamplesToDo, conv_sample);
-        ++c;
+            *out = SampleConv<T>(s);
+        });
     }
     }
-    if(const size_t extra{FrameStep - c})
+}
+
+template<typename T>
+void Write(const al::span<const FloatBufferLine> InBuffer, al::span<void*> OutBuffers,
+    const size_t Offset, const size_t SamplesToDo)
+{
+    ASSUME(SamplesToDo > 0);
+
+    auto srcbuf = InBuffer.cbegin();
+    for(auto *dstbuf : OutBuffers)
     {
     {
-        const auto silence = SampleConv<T>(0.0f);
-        for(size_t i{0};i < SamplesToDo;++i)
-            std::fill_n(&output[i*FrameStep + c], extra, silence);
+        const auto src = al::span{*srcbuf}.first(SamplesToDo);
+        /* Some Clang versions don't like calling subspan on an rvalue here. */
+        const auto dst_ = al::span{static_cast<T*>(dstbuf), Offset+SamplesToDo};
+        const auto dst = dst_.subspan(Offset);
+        std::transform(src.begin(), src.end(), dst.begin(), SampleConv<T>);
+        ++srcbuf;
     }
     }
 }
 }
 
 
@@ -2187,10 +2214,10 @@ uint DeviceBase::renderSamples(const uint numSamples)
          * also guarantees a stable conversion.
          * also guarantees a stable conversion.
          */
          */
         auto samplesDone = mSamplesDone.load(std::memory_order_relaxed) + samplesToDo;
         auto samplesDone = mSamplesDone.load(std::memory_order_relaxed) + samplesToDo;
-        auto clockBase = mClockBase.load(std::memory_order_relaxed) +
-            std::chrono::seconds{samplesDone/Frequency};
-        mSamplesDone.store(samplesDone%Frequency, std::memory_order_relaxed);
-        mClockBase.store(clockBase, std::memory_order_relaxed);
+        auto clockBaseSec = mClockBaseSec.load(std::memory_order_relaxed) +
+            seconds32{samplesDone/mSampleRate};
+        mSamplesDone.store(samplesDone%mSampleRate, std::memory_order_relaxed);
+        mClockBaseSec.store(clockBaseSec, std::memory_order_relaxed);
     }
     }
 
 
     /* Apply any needed post-process for finalizing the Dry mix to the RealOut
     /* Apply any needed post-process for finalizing the Dry mix to the RealOut
@@ -2199,7 +2226,7 @@ uint DeviceBase::renderSamples(const uint numSamples)
     postProcess(samplesToDo);
     postProcess(samplesToDo);
 
 
     /* Apply compression, limiting sample amplitude if needed or desired. */
     /* Apply compression, limiting sample amplitude if needed or desired. */
-    if(Limiter) Limiter->process(samplesToDo, RealOut.Buffer.data());
+    if(Limiter) Limiter->process(samplesToDo, RealOut.Buffer);
 
 
     /* Apply delays and attenuation for mismatched speaker distances. */
     /* Apply delays and attenuation for mismatched speaker distances. */
     if(ChannelDelays)
     if(ChannelDelays)
@@ -2214,7 +2241,7 @@ uint DeviceBase::renderSamples(const uint numSamples)
     return samplesToDo;
     return samplesToDo;
 }
 }
 
 
-void DeviceBase::renderSamples(const al::span<float*> outBuffers, const uint numSamples)
+void DeviceBase::renderSamples(const al::span<void*> outBuffers, const uint numSamples)
 {
 {
     FPUCtl mixer_mode{};
     FPUCtl mixer_mode{};
     uint total{0};
     uint total{0};
@@ -2222,13 +2249,19 @@ void DeviceBase::renderSamples(const al::span<float*> outBuffers, const uint num
     {
     {
         const uint samplesToDo{renderSamples(todo)};
         const uint samplesToDo{renderSamples(todo)};
 
 
-        auto srcbuf = RealOut.Buffer.cbegin();
-        for(auto *dstbuf : outBuffers)
+        switch(FmtType)
         {
         {
-            const auto dst = al::span{dstbuf, numSamples}.subspan(total);
-            std::copy_n(srcbuf->cbegin(), samplesToDo, dst.begin());
-            ++srcbuf;
+#define HANDLE_WRITE(T) case T:                                               \
+    Write<DevFmtType_t<T>>(RealOut.Buffer, outBuffers, total, samplesToDo); break;
+        HANDLE_WRITE(DevFmtByte)
+        HANDLE_WRITE(DevFmtUByte)
+        HANDLE_WRITE(DevFmtShort)
+        HANDLE_WRITE(DevFmtUShort)
+        HANDLE_WRITE(DevFmtInt)
+        HANDLE_WRITE(DevFmtUInt)
+        HANDLE_WRITE(DevFmtFloat)
         }
         }
+#undef HANDLE_WRITE
 
 
         total += samplesToDo;
         total += samplesToDo;
     }
     }
@@ -2266,7 +2299,7 @@ void DeviceBase::renderSamples(void *outBuffer, const uint numSamples, const siz
     }
     }
 }
 }
 
 
-void DeviceBase::handleDisconnect(const char *msg, ...)
+void DeviceBase::doDisconnect(std::string msg)
 {
 {
     const auto mixLock = getWriteMixLock();
     const auto mixLock = getWriteMixLock();
 
 
@@ -2274,21 +2307,12 @@ void DeviceBase::handleDisconnect(const char *msg, ...)
     {
     {
         AsyncEvent evt{std::in_place_type<AsyncDisconnectEvent>};
         AsyncEvent evt{std::in_place_type<AsyncDisconnectEvent>};
         auto &disconnect = std::get<AsyncDisconnectEvent>(evt);
         auto &disconnect = std::get<AsyncDisconnectEvent>(evt);
-
-        /* NOLINTBEGIN(*-array-to-pointer-decay) */
-        va_list args;
-        va_start(args, msg);
-        int msglen{vsnprintf(disconnect.msg.data(), disconnect.msg.size(), msg, args)};
-        va_end(args);
-        /* NOLINTEND(*-array-to-pointer-decay) */
-
-        if(msglen < 0 || static_cast<size_t>(msglen) >= disconnect.msg.size())
-            disconnect.msg[sizeof(disconnect.msg)-1] = 0;
+        disconnect.msg = std::move(msg);
 
 
         for(ContextBase *ctx : *mContexts.load())
         for(ContextBase *ctx : *mContexts.load())
         {
         {
             RingBuffer *ring{ctx->mAsyncEvents.get()};
             RingBuffer *ring{ctx->mAsyncEvents.get()};
-            auto evt_data = ring->getWriteVector().first;
+            auto evt_data = ring->getWriteVector()[0];
             if(evt_data.len > 0)
             if(evt_data.len > 0)
             {
             {
                 al::construct_at(reinterpret_cast<AsyncEvent*>(evt_data.buf), evt);
                 al::construct_at(reinterpret_cast<AsyncEvent*>(evt_data.buf), evt);

+ 7 - 4
libs/openal-soft/alc/alu.h

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

+ 116 - 126
libs/openal-soft/alc/backends/alsa.cpp

@@ -29,7 +29,6 @@
 #include <chrono>
 #include <chrono>
 #include <cstring>
 #include <cstring>
 #include <exception>
 #include <exception>
-#include <functional>
 #include <memory>
 #include <memory>
 #include <mutex>
 #include <mutex>
 #include <string>
 #include <string>
@@ -38,16 +37,15 @@
 #include <utility>
 #include <utility>
 #include <vector>
 #include <vector>
 
 
-#include "albit.h"
 #include "alc/alconfig.h"
 #include "alc/alconfig.h"
 #include "almalloc.h"
 #include "almalloc.h"
 #include "alnumeric.h"
 #include "alnumeric.h"
-#include "alstring.h"
 #include "althrd_setname.h"
 #include "althrd_setname.h"
 #include "core/device.h"
 #include "core/device.h"
 #include "core/helpers.h"
 #include "core/helpers.h"
 #include "core/logging.h"
 #include "core/logging.h"
 #include "dynload.h"
 #include "dynload.h"
+#include "fmt/core.h"
 #include "ringbuffer.h"
 #include "ringbuffer.h"
 
 
 #include <alsa/asoundlib.h>
 #include <alsa/asoundlib.h>
@@ -60,7 +58,7 @@ using namespace std::string_view_literals;
 [[nodiscard]] constexpr auto GetDefaultName() noexcept { return "ALSA Default"sv; }
 [[nodiscard]] constexpr auto GetDefaultName() noexcept { return "ALSA Default"sv; }
 
 
 
 
-#ifdef HAVE_DYNLOAD
+#if HAVE_DYNLOAD
 #define ALSA_FUNCS(MAGIC)                                                     \
 #define ALSA_FUNCS(MAGIC)                                                     \
     MAGIC(snd_strerror);                                                      \
     MAGIC(snd_strerror);                                                      \
     MAGIC(snd_pcm_open);                                                      \
     MAGIC(snd_pcm_open);                                                      \
@@ -272,7 +270,7 @@ struct SndCtlCardInfo {
     SndCtlCardInfo& operator=(const SndCtlCardInfo&) = delete;
     SndCtlCardInfo& operator=(const SndCtlCardInfo&) = delete;
 
 
     [[nodiscard]]
     [[nodiscard]]
-    operator snd_ctl_card_info_t*() const noexcept { return mInfo; }
+    operator snd_ctl_card_info_t*() const noexcept { return mInfo; } /* NOLINT(google-explicit-constructor) */
 };
 };
 
 
 struct SndPcmInfo {
 struct SndPcmInfo {
@@ -284,7 +282,7 @@ struct SndPcmInfo {
     SndPcmInfo& operator=(const SndPcmInfo&) = delete;
     SndPcmInfo& operator=(const SndPcmInfo&) = delete;
 
 
     [[nodiscard]]
     [[nodiscard]]
-    operator snd_pcm_info_t*() const noexcept { return mInfo; }
+    operator snd_pcm_info_t*() const noexcept { return mInfo; } /* NOLINT(google-explicit-constructor) */
 };
 };
 
 
 struct SndCtl {
 struct SndCtl {
@@ -299,7 +297,7 @@ struct SndCtl {
     auto open(const char *name, int mode) { return snd_ctl_open(&mHandle, name, mode); }
     auto open(const char *name, int mode) { return snd_ctl_open(&mHandle, name, mode); }
 
 
     [[nodiscard]]
     [[nodiscard]]
-    operator snd_ctl_t*() const noexcept { return mHandle; }
+    operator snd_ctl_t*() const noexcept { return mHandle; } /* NOLINT(google-explicit-constructor) */
 };
 };
 
 
 
 
@@ -324,15 +322,15 @@ std::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
             const size_t seppos{customdevs->find('=', curpos)};
             const size_t seppos{customdevs->find('=', curpos)};
             if(seppos == curpos || seppos >= nextpos)
             if(seppos == curpos || seppos >= nextpos)
             {
             {
-                const std::string spec{customdevs->substr(curpos, nextpos-curpos)};
-                ERR("Invalid ALSA device specification \"%s\"\n", spec.c_str());
+                const auto spec = std::string_view{*customdevs}.substr(curpos, nextpos-curpos);
+                ERR("Invalid ALSA device specification \"{}\"", spec);
             }
             }
             else
             else
             {
             {
                 const std::string_view strview{*customdevs};
                 const std::string_view strview{*customdevs};
                 const auto &entry = devlist.emplace_back(strview.substr(curpos, seppos-curpos),
                 const auto &entry = devlist.emplace_back(strview.substr(curpos, seppos-curpos),
                     strview.substr(seppos+1, nextpos-seppos-1));
                     strview.substr(seppos+1, nextpos-seppos-1));
-                TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
+                TRACE("Got device \"{}\", \"{}\"", entry.name, entry.device_name);
             }
             }
 
 
             if(nextpos < customdevs->length())
             if(nextpos < customdevs->length())
@@ -354,13 +352,13 @@ std::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
         err = handle.open(name.c_str(), 0);
         err = handle.open(name.c_str(), 0);
         if(err < 0)
         if(err < 0)
         {
         {
-            ERR("control open (hw:%d): %s\n", card, snd_strerror(err));
+            ERR("control open (hw:{}): {}", card, snd_strerror(err));
             continue;
             continue;
         }
         }
         err = snd_ctl_card_info(handle, info);
         err = snd_ctl_card_info(handle, info);
         if(err < 0)
         if(err < 0)
         {
         {
-            ERR("control hardware info (hw:%d): %s\n", card, snd_strerror(err));
+            ERR("control hardware info (hw:{}): {}", card, snd_strerror(err));
             continue;
             continue;
         }
         }
 
 
@@ -375,7 +373,7 @@ std::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
         while(true)
         while(true)
         {
         {
             if(snd_ctl_pcm_next_device(handle, &dev) < 0)
             if(snd_ctl_pcm_next_device(handle, &dev) < 0)
-                ERR("snd_ctl_pcm_next_device failed\n");
+                ERR("snd_ctl_pcm_next_device failed");
             if(dev < 0) break;
             if(dev < 0) break;
 
 
             snd_pcm_info_set_device(pcminfo, static_cast<uint>(dev));
             snd_pcm_info_set_device(pcminfo, static_cast<uint>(dev));
@@ -385,42 +383,28 @@ std::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
             if(err < 0)
             if(err < 0)
             {
             {
                 if(err != -ENOENT)
                 if(err != -ENOENT)
-                    ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err));
+                    ERR("control digital audio info (hw:{}): {}", card, snd_strerror(err));
                 continue;
                 continue;
             }
             }
 
 
             /* "prefix-cardid-dev" */
             /* "prefix-cardid-dev" */
-            name = prefix_name(stream);
-            name += '-';
-            name += cardid;
-            name += '-';
-            name += std::to_string(dev);
-            const std::string device_prefix{ConfigValueStr({}, "alsa"sv, name)
+            name = fmt::format("{}-{}-{}", prefix_name(stream), cardid, dev);
+            const auto device_prefix = std::string{ConfigValueStr({}, "alsa"sv, name)
                 .value_or(card_prefix)};
                 .value_or(card_prefix)};
 
 
             /* "CardName, PcmName (CARD=cardid,DEV=dev)" */
             /* "CardName, PcmName (CARD=cardid,DEV=dev)" */
-            name = cardname;
-            name += ", ";
-            name += snd_pcm_info_get_name(pcminfo);
-            name += " (CARD=";
-            name += cardid;
-            name += ",DEV=";
-            name += std::to_string(dev);
-            name += ')';
+            name = fmt::format("{}, {} (CARD={},DEV={})", cardname, snd_pcm_info_get_name(pcminfo),
+                cardid, dev);
 
 
             /* "devprefixCARD=cardid,DEV=dev" */
             /* "devprefixCARD=cardid,DEV=dev" */
-            std::string device{device_prefix};
-            device += "CARD=";
-            device += cardid;
-            device += ",DEV=";
-            device += std::to_string(dev);
+            auto device = fmt::format("{}CARD={},DEV={}", device_prefix, cardid, dev);
             
             
             const auto &entry = devlist.emplace_back(std::move(name), std::move(device));
             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());
+            TRACE("Got device \"{}\", \"{}\"", entry.name, entry.device_name);
         }
         }
     }
     }
     if(err < 0)
     if(err < 0)
-        ERR("snd_card_next failed: %s\n", snd_strerror(err));
+        ERR("snd_card_next failed: {}", snd_strerror(err));
 
 
     return devlist;
     return devlist;
 }
 }
@@ -452,6 +436,17 @@ int verify_state(snd_pcm_t *handle)
 
 
         case SND_PCM_STATE_DISCONNECTED:
         case SND_PCM_STATE_DISCONNECTED:
             return -ENODEV;
             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;
     return state;
@@ -459,7 +454,7 @@ int verify_state(snd_pcm_t *handle)
 
 
 
 
 struct AlsaPlayback final : public BackendBase {
 struct AlsaPlayback final : public BackendBase {
-    AlsaPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit AlsaPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
     ~AlsaPlayback() override;
     ~AlsaPlayback() override;
 
 
     int mixerProc();
     int mixerProc();
@@ -496,29 +491,29 @@ int AlsaPlayback::mixerProc()
     SetRTPriority();
     SetRTPriority();
     althrd_setname(GetMixerThreadName());
     althrd_setname(GetMixerThreadName());
 
 
-    const snd_pcm_uframes_t update_size{mDevice->UpdateSize};
-    const snd_pcm_uframes_t buffer_size{mDevice->BufferSize};
+    const snd_pcm_uframes_t update_size{mDevice->mUpdateSize};
+    const snd_pcm_uframes_t buffer_size{mDevice->mBufferSize};
     while(!mKillNow.load(std::memory_order_acquire))
     while(!mKillNow.load(std::memory_order_acquire))
     {
     {
         int state{verify_state(mPcmHandle)};
         int state{verify_state(mPcmHandle)};
         if(state < 0)
         if(state < 0)
         {
         {
-            ERR("Invalid state detected: %s\n", snd_strerror(state));
-            mDevice->handleDisconnect("Bad state: %s", snd_strerror(state));
+            ERR("Invalid state detected: {}", snd_strerror(state));
+            mDevice->handleDisconnect("Bad state: {}", snd_strerror(state));
             break;
             break;
         }
         }
 
 
         snd_pcm_sframes_t avails{snd_pcm_avail_update(mPcmHandle)};
         snd_pcm_sframes_t avails{snd_pcm_avail_update(mPcmHandle)};
         if(avails < 0)
         if(avails < 0)
         {
         {
-            ERR("available update failed: %s\n", snd_strerror(static_cast<int>(avails)));
+            ERR("available update failed: {}", snd_strerror(static_cast<int>(avails)));
             continue;
             continue;
         }
         }
         snd_pcm_uframes_t avail{static_cast<snd_pcm_uframes_t>(avails)};
         snd_pcm_uframes_t avail{static_cast<snd_pcm_uframes_t>(avails)};
 
 
         if(avail > buffer_size)
         if(avail > buffer_size)
         {
         {
-            WARN("available samples exceeds the buffer size\n");
+            WARN("available samples exceeds the buffer size");
             snd_pcm_reset(mPcmHandle);
             snd_pcm_reset(mPcmHandle);
             continue;
             continue;
         }
         }
@@ -531,12 +526,12 @@ int AlsaPlayback::mixerProc()
                 int err{snd_pcm_start(mPcmHandle)};
                 int err{snd_pcm_start(mPcmHandle)};
                 if(err < 0)
                 if(err < 0)
                 {
                 {
-                    ERR("start failed: %s\n", snd_strerror(err));
+                    ERR("start failed: {}", snd_strerror(err));
                     continue;
                     continue;
                 }
                 }
             }
             }
             if(snd_pcm_wait(mPcmHandle, 1000) == 0)
             if(snd_pcm_wait(mPcmHandle, 1000) == 0)
-                ERR("Wait timeout... buffer size too low?\n");
+                ERR("Wait timeout... buffer size too low?");
             continue;
             continue;
         }
         }
         avail -= avail%update_size;
         avail -= avail%update_size;
@@ -552,7 +547,7 @@ int AlsaPlayback::mixerProc()
             int err{snd_pcm_mmap_begin(mPcmHandle, &areas, &offset, &frames)};
             int err{snd_pcm_mmap_begin(mPcmHandle, &areas, &offset, &frames)};
             if(err < 0)
             if(err < 0)
             {
             {
-                ERR("mmap begin error: %s\n", snd_strerror(err));
+                ERR("mmap begin error: {}", snd_strerror(err));
                 break;
                 break;
             }
             }
 
 
@@ -563,7 +558,7 @@ int AlsaPlayback::mixerProc()
             snd_pcm_sframes_t commitres{snd_pcm_mmap_commit(mPcmHandle, offset, frames)};
             snd_pcm_sframes_t commitres{snd_pcm_mmap_commit(mPcmHandle, offset, frames)};
             if(commitres < 0 || static_cast<snd_pcm_uframes_t>(commitres) != frames)
             if(commitres < 0 || static_cast<snd_pcm_uframes_t>(commitres) != frames)
             {
             {
-                ERR("mmap commit error: %s\n",
+                ERR("mmap commit error: {}",
                     snd_strerror(commitres >= 0 ? -EPIPE : static_cast<int>(commitres)));
                     snd_strerror(commitres >= 0 ? -EPIPE : static_cast<int>(commitres)));
                 break;
                 break;
             }
             }
@@ -580,28 +575,28 @@ int AlsaPlayback::mixerNoMMapProc()
     SetRTPriority();
     SetRTPriority();
     althrd_setname(GetMixerThreadName());
     althrd_setname(GetMixerThreadName());
 
 
-    const snd_pcm_uframes_t update_size{mDevice->UpdateSize};
-    const snd_pcm_uframes_t buffer_size{mDevice->BufferSize};
+    const snd_pcm_uframes_t update_size{mDevice->mUpdateSize};
+    const snd_pcm_uframes_t buffer_size{mDevice->mBufferSize};
     while(!mKillNow.load(std::memory_order_acquire))
     while(!mKillNow.load(std::memory_order_acquire))
     {
     {
         int state{verify_state(mPcmHandle)};
         int state{verify_state(mPcmHandle)};
         if(state < 0)
         if(state < 0)
         {
         {
-            ERR("Invalid state detected: %s\n", snd_strerror(state));
-            mDevice->handleDisconnect("Bad state: %s", snd_strerror(state));
+            ERR("Invalid state detected: {}", snd_strerror(state));
+            mDevice->handleDisconnect("Bad state: {}", snd_strerror(state));
             break;
             break;
         }
         }
 
 
         snd_pcm_sframes_t avail{snd_pcm_avail_update(mPcmHandle)};
         snd_pcm_sframes_t avail{snd_pcm_avail_update(mPcmHandle)};
         if(avail < 0)
         if(avail < 0)
         {
         {
-            ERR("available update failed: %s\n", snd_strerror(static_cast<int>(avail)));
+            ERR("available update failed: {}", snd_strerror(static_cast<int>(avail)));
             continue;
             continue;
         }
         }
 
 
         if(static_cast<snd_pcm_uframes_t>(avail) > buffer_size)
         if(static_cast<snd_pcm_uframes_t>(avail) > buffer_size)
         {
         {
-            WARN("available samples exceeds the buffer size\n");
+            WARN("available samples exceeds the buffer size");
             snd_pcm_reset(mPcmHandle);
             snd_pcm_reset(mPcmHandle);
             continue;
             continue;
         }
         }
@@ -613,12 +608,12 @@ int AlsaPlayback::mixerNoMMapProc()
                 int err{snd_pcm_start(mPcmHandle)};
                 int err{snd_pcm_start(mPcmHandle)};
                 if(err < 0)
                 if(err < 0)
                 {
                 {
-                    ERR("start failed: %s\n", snd_strerror(err));
+                    ERR("start failed: {}", snd_strerror(err));
                     continue;
                     continue;
                 }
                 }
             }
             }
             if(snd_pcm_wait(mPcmHandle, 1000) == 0)
             if(snd_pcm_wait(mPcmHandle, 1000) == 0)
-                ERR("Wait timeout... buffer size too low?\n");
+                ERR("Wait timeout... buffer size too low?");
             continue;
             continue;
         }
         }
 
 
@@ -675,7 +670,7 @@ void AlsaPlayback::open(std::string_view name)
             [name](const DevMap &entry) -> bool { return entry.name == name; });
             [name](const DevMap &entry) -> bool { return entry.name == name; });
         if(iter == PlaybackDevices.cend())
         if(iter == PlaybackDevices.cend())
             throw al::backend_exception{al::backend_error::NoDevice,
             throw al::backend_exception{al::backend_error::NoDevice,
-                "Device name \"%.*s\" not found", al::sizei(name), name.data()};
+                "Device name \"{}\" not found", name};
         driver = iter->device_name;
         driver = iter->device_name;
     }
     }
     else
     else
@@ -684,13 +679,13 @@ void AlsaPlayback::open(std::string_view name)
         if(auto driveropt = ConfigValueStr({}, "alsa"sv, "device"sv))
         if(auto driveropt = ConfigValueStr({}, "alsa"sv, "device"sv))
             driver = std::move(driveropt).value();
             driver = std::move(driveropt).value();
     }
     }
-    TRACE("Opening device \"%s\"\n", driver.c_str());
+    TRACE("Opening device \"{}\"", driver);
 
 
     snd_pcm_t *pcmHandle{};
     snd_pcm_t *pcmHandle{};
     int err{snd_pcm_open(&pcmHandle, driver.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)};
     int err{snd_pcm_open(&pcmHandle, driver.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)};
     if(err < 0)
     if(err < 0)
         throw al::backend_exception{al::backend_error::NoDevice,
         throw al::backend_exception{al::backend_error::NoDevice,
-            "Could not open ALSA device \"%s\"", driver.c_str()};
+            "Could not open ALSA device \"{}\"", driver};
     if(mPcmHandle)
     if(mPcmHandle)
         snd_pcm_close(mPcmHandle);
         snd_pcm_close(mPcmHandle);
     mPcmHandle = pcmHandle;
     mPcmHandle = pcmHandle;
@@ -698,7 +693,7 @@ void AlsaPlayback::open(std::string_view name)
     /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
     /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
     snd_config_update_free_global();
     snd_config_update_free_global();
 
 
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 }
 }
 
 
 bool AlsaPlayback::reset()
 bool AlsaPlayback::reset()
@@ -729,15 +724,15 @@ bool AlsaPlayback::reset()
         break;
         break;
     }
     }
 
 
-    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};
+    bool allowmmap{GetConfigValueBool(mDevice->mDeviceName, "alsa"sv, "mmap"sv, true)};
+    uint periodLen{static_cast<uint>(mDevice->mUpdateSize * 1000000_u64 / mDevice->mSampleRate)};
+    uint bufferLen{static_cast<uint>(mDevice->mBufferSize * 1000000_u64 / mDevice->mSampleRate)};
+    uint rate{mDevice->mSampleRate};
 
 
     HwParamsPtr hp{CreateHwParams()};
     HwParamsPtr hp{CreateHwParams()};
 #define CHECK(x) do {                                                         \
 #define CHECK(x) do {                                                         \
     if(int err{x}; err < 0)                                                   \
     if(int err{x}; err < 0)                                                   \
-        throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \
+        throw al::backend_exception{al::backend_error::DeviceError, #x " failed: {}", \
             snd_strerror(err)};                                               \
             snd_strerror(err)};                                               \
 } while(0)
 } while(0)
     CHECK(snd_pcm_hw_params_any(mPcmHandle, hp.get()));
     CHECK(snd_pcm_hw_params_any(mPcmHandle, hp.get()));
@@ -787,21 +782,21 @@ bool AlsaPlayback::reset()
         else mDevice->FmtChans = DevFmtStereo;
         else mDevice->FmtChans = DevFmtStereo;
     }
     }
     /* set rate (implicitly constrains period/buffer parameters) */
     /* set rate (implicitly constrains period/buffer parameters) */
-    if(!GetConfigValueBool(mDevice->DeviceName, "alsa", "allow-resampler", false)
+    if(!GetConfigValueBool(mDevice->mDeviceName, "alsa", "allow-resampler", false)
         || !mDevice->Flags.test(FrequencyRequest))
         || !mDevice->Flags.test(FrequencyRequest))
     {
     {
         if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp.get(), 0) < 0)
         if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp.get(), 0) < 0)
-            WARN("Failed to disable ALSA resampler\n");
+            WARN("Failed to disable ALSA resampler");
     }
     }
     else if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp.get(), 1) < 0)
     else if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp.get(), 1) < 0)
-        WARN("Failed to enable ALSA resampler\n");
+        WARN("Failed to enable ALSA resampler");
     CHECK(snd_pcm_hw_params_set_rate_near(mPcmHandle, hp.get(), &rate, nullptr));
     CHECK(snd_pcm_hw_params_set_rate_near(mPcmHandle, hp.get(), &rate, nullptr));
     /* set period time (implicitly constrains period/buffer parameters) */
     /* set period time (implicitly constrains period/buffer parameters) */
     if(int err{snd_pcm_hw_params_set_period_time_near(mPcmHandle, hp.get(), &periodLen, nullptr)}; err < 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));
+        ERR("snd_pcm_hw_params_set_period_time_near failed: {}", snd_strerror(err));
     /* set buffer time (implicitly sets buffer size/bytes/time and period size/bytes) */
     /* set buffer time (implicitly sets buffer size/bytes/time and period size/bytes) */
     if(int err{snd_pcm_hw_params_set_buffer_time_near(mPcmHandle, hp.get(), &bufferLen, nullptr)}; err < 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));
+        ERR("snd_pcm_hw_params_set_buffer_time_near failed: {}", snd_strerror(err));
     /* install and prepare hardware configuration */
     /* install and prepare hardware configuration */
     CHECK(snd_pcm_hw_params(mPcmHandle, hp.get()));
     CHECK(snd_pcm_hw_params(mPcmHandle, hp.get()));
 
 
@@ -824,9 +819,9 @@ bool AlsaPlayback::reset()
 #undef CHECK
 #undef CHECK
     sp = nullptr;
     sp = nullptr;
 
 
-    mDevice->BufferSize = static_cast<uint>(bufferSizeInFrames);
-    mDevice->UpdateSize = static_cast<uint>(periodSizeInFrames);
-    mDevice->Frequency = rate;
+    mDevice->mBufferSize = static_cast<uint>(bufferSizeInFrames);
+    mDevice->mUpdateSize = static_cast<uint>(periodSizeInFrames);
+    mDevice->mSampleRate = rate;
 
 
     setDefaultChannelOrder();
     setDefaultChannelOrder();
 
 
@@ -839,7 +834,7 @@ void AlsaPlayback::start()
     HwParamsPtr hp{CreateHwParams()};
     HwParamsPtr hp{CreateHwParams()};
 #define CHECK(x) do {                                                         \
 #define CHECK(x) do {                                                         \
     if(int err{x}; err < 0)                                                   \
     if(int err{x}; err < 0)                                                   \
-        throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \
+        throw al::backend_exception{al::backend_error::DeviceError, #x " failed: {}", \
             snd_strerror(err)};                                               \
             snd_strerror(err)};                                               \
 } while(0)
 } while(0)
     CHECK(snd_pcm_hw_params_current(mPcmHandle, hp.get()));
     CHECK(snd_pcm_hw_params_current(mPcmHandle, hp.get()));
@@ -850,7 +845,7 @@ void AlsaPlayback::start()
     int (AlsaPlayback::*thread_func)(){};
     int (AlsaPlayback::*thread_func)(){};
     if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
     if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
     {
     {
-        auto datalen = snd_pcm_frames_to_bytes(mPcmHandle, mDevice->UpdateSize);
+        auto datalen = snd_pcm_frames_to_bytes(mPcmHandle, mDevice->mUpdateSize);
         mBuffer.resize(static_cast<size_t>(datalen));
         mBuffer.resize(static_cast<size_t>(datalen));
         thread_func = &AlsaPlayback::mixerNoMMapProc;
         thread_func = &AlsaPlayback::mixerNoMMapProc;
     }
     }
@@ -863,11 +858,11 @@ void AlsaPlayback::start()
 
 
     try {
     try {
         mKillNow.store(false, std::memory_order_release);
         mKillNow.store(false, std::memory_order_release);
-        mThread = std::thread{std::mem_fn(thread_func), this};
+        mThread = std::thread{thread_func, this};
     }
     }
     catch(std::exception& e) {
     catch(std::exception& e) {
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to start mixing thread: %s", e.what()};
+            "Failed to start mixing thread: {}", e.what()};
     }
     }
 }
 }
 
 
@@ -880,31 +875,30 @@ void AlsaPlayback::stop()
     mBuffer.clear();
     mBuffer.clear();
     int err{snd_pcm_drop(mPcmHandle)};
     int err{snd_pcm_drop(mPcmHandle)};
     if(err < 0)
     if(err < 0)
-        ERR("snd_pcm_drop failed: %s\n", snd_strerror(err));
+        ERR("snd_pcm_drop failed: {}", snd_strerror(err));
 }
 }
 
 
 ClockLatency AlsaPlayback::getClockLatency()
 ClockLatency AlsaPlayback::getClockLatency()
 {
 {
-    ClockLatency ret;
-
     std::lock_guard<std::mutex> dlock{mMutex};
     std::lock_guard<std::mutex> dlock{mMutex};
+    ClockLatency ret{};
     ret.ClockTime = mDevice->getClockTime();
     ret.ClockTime = mDevice->getClockTime();
     snd_pcm_sframes_t delay{};
     snd_pcm_sframes_t delay{};
     int err{snd_pcm_delay(mPcmHandle, &delay)};
     int err{snd_pcm_delay(mPcmHandle, &delay)};
     if(err < 0)
     if(err < 0)
     {
     {
-        ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
+        ERR("Failed to get pcm delay: {}", snd_strerror(err));
         delay = 0;
         delay = 0;
     }
     }
     ret.Latency  = std::chrono::seconds{std::max<snd_pcm_sframes_t>(0, delay)};
     ret.Latency  = std::chrono::seconds{std::max<snd_pcm_sframes_t>(0, delay)};
-    ret.Latency /= mDevice->Frequency;
+    ret.Latency /= mDevice->mSampleRate;
 
 
     return ret;
     return ret;
 }
 }
 
 
 
 
 struct AlsaCapture final : public BackendBase {
 struct AlsaCapture final : public BackendBase {
-    AlsaCapture(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit AlsaCapture(DeviceBase *device) noexcept : BackendBase{device} { }
     ~AlsaCapture() override;
     ~AlsaCapture() override;
 
 
     void open(std::string_view name) override;
     void open(std::string_view name) override;
@@ -944,7 +938,7 @@ void AlsaCapture::open(std::string_view name)
             [name](const DevMap &entry) -> bool { return entry.name == name; });
             [name](const DevMap &entry) -> bool { return entry.name == name; });
         if(iter == CaptureDevices.cend())
         if(iter == CaptureDevices.cend())
             throw al::backend_exception{al::backend_error::NoDevice,
             throw al::backend_exception{al::backend_error::NoDevice,
-                "Device name \"%.*s\" not found", al::sizei(name), name.data()};
+                "Device name \"{}\" not found", name};
         driver = iter->device_name;
         driver = iter->device_name;
     }
     }
     else
     else
@@ -954,10 +948,10 @@ void AlsaCapture::open(std::string_view name)
             driver = std::move(driveropt).value();
             driver = std::move(driveropt).value();
     }
     }
 
 
-    TRACE("Opening device \"%s\"\n", driver.c_str());
+    TRACE("Opening device \"{}\"", driver);
     if(int err{snd_pcm_open(&mPcmHandle, driver.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)}; 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,
         throw al::backend_exception{al::backend_error::NoDevice,
-            "Could not open ALSA device \"%s\"", driver.c_str()};
+            "Could not open ALSA device \"{}\"", driver};
 
 
     /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
     /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
     snd_config_update_free_global();
     snd_config_update_free_global();
@@ -988,16 +982,16 @@ void AlsaCapture::open(std::string_view name)
         break;
         break;
     }
     }
 
 
-    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)};
+    snd_pcm_uframes_t bufferSizeInFrames{std::max(mDevice->mBufferSize,
+        100u*mDevice->mSampleRate/1000u)};
+    snd_pcm_uframes_t periodSizeInFrames{std::min(mDevice->mBufferSize,
+        25u*mDevice->mSampleRate/1000u)};
 
 
     bool needring{false};
     bool needring{false};
     HwParamsPtr hp{CreateHwParams()};
     HwParamsPtr hp{CreateHwParams()};
 #define CHECK(x) do {                                                         \
 #define CHECK(x) do {                                                         \
     if(int err{x}; err < 0)                                                   \
     if(int err{x}; err < 0)                                                   \
-        throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \
+        throw al::backend_exception{al::backend_error::DeviceError, #x " failed: {}", \
             snd_strerror(err)};                                               \
             snd_strerror(err)};                                               \
 } while(0)
 } while(0)
     CHECK(snd_pcm_hw_params_any(mPcmHandle, hp.get()));
     CHECK(snd_pcm_hw_params_any(mPcmHandle, hp.get()));
@@ -1008,11 +1002,11 @@ void AlsaCapture::open(std::string_view name)
     /* set channels (implicitly sets frame bits) */
     /* set channels (implicitly sets frame bits) */
     CHECK(snd_pcm_hw_params_set_channels(mPcmHandle, hp.get(), mDevice->channelsFromFmt()));
     CHECK(snd_pcm_hw_params_set_channels(mPcmHandle, hp.get(), mDevice->channelsFromFmt()));
     /* set rate (implicitly constrains period/buffer parameters) */
     /* set rate (implicitly constrains period/buffer parameters) */
-    CHECK(snd_pcm_hw_params_set_rate(mPcmHandle, hp.get(), mDevice->Frequency, 0));
+    CHECK(snd_pcm_hw_params_set_rate(mPcmHandle, hp.get(), mDevice->mSampleRate, 0));
     /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
     /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
     if(snd_pcm_hw_params_set_buffer_size_min(mPcmHandle, hp.get(), &bufferSizeInFrames) < 0)
     if(snd_pcm_hw_params_set_buffer_size_min(mPcmHandle, hp.get(), &bufferSizeInFrames) < 0)
     {
     {
-        TRACE("Buffer too large, using intermediate ring buffer\n");
+        TRACE("Buffer too large, using intermediate ring buffer");
         needring = true;
         needring = true;
         CHECK(snd_pcm_hw_params_set_buffer_size_near(mPcmHandle, hp.get(), &bufferSizeInFrames));
         CHECK(snd_pcm_hw_params_set_buffer_size_near(mPcmHandle, hp.get(), &bufferSizeInFrames));
     }
     }
@@ -1026,20 +1020,20 @@ void AlsaCapture::open(std::string_view name)
     hp = nullptr;
     hp = nullptr;
 
 
     if(needring)
     if(needring)
-        mRing = RingBuffer::Create(mDevice->BufferSize, mDevice->frameSizeFromFmt(), false);
+        mRing = RingBuffer::Create(mDevice->mBufferSize, mDevice->frameSizeFromFmt(), false);
 
 
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 }
 }
 
 
 
 
 void AlsaCapture::start()
 void AlsaCapture::start()
 {
 {
     if(int err{snd_pcm_prepare(mPcmHandle)}; err < 0)
     if(int err{snd_pcm_prepare(mPcmHandle)}; err < 0)
-        throw al::backend_exception{al::backend_error::DeviceError, "snd_pcm_prepare failed: %s",
+        throw al::backend_exception{al::backend_error::DeviceError, "snd_pcm_prepare failed: {}",
             snd_strerror(err)};
             snd_strerror(err)};
 
 
     if(int err{snd_pcm_start(mPcmHandle)}; err < 0)
     if(int err{snd_pcm_start(mPcmHandle)}; err < 0)
-        throw al::backend_exception{al::backend_error::DeviceError, "snd_pcm_start failed: %s",
+        throw al::backend_exception{al::backend_error::DeviceError, "snd_pcm_start failed: {}",
             snd_strerror(err)};
             snd_strerror(err)};
 
 
     mDoCapture = true;
     mDoCapture = true;
@@ -1063,7 +1057,7 @@ void AlsaCapture::stop()
         mBuffer = std::move(temp);
         mBuffer = std::move(temp);
     }
     }
     if(int err{snd_pcm_drop(mPcmHandle)}; err < 0)
     if(int err{snd_pcm_drop(mPcmHandle)}; err < 0)
-        ERR("drop failed: %s\n", snd_strerror(err));
+        ERR("snd_pcm_drop failed: {}", snd_strerror(err));
     mDoCapture = false;
     mDoCapture = false;
 }
 }
 
 
@@ -1099,7 +1093,7 @@ void AlsaCapture::captureSamples(std::byte *buffer, uint samples)
             amt = snd_pcm_readi(mPcmHandle, al::to_address(outiter), samples);
             amt = snd_pcm_readi(mPcmHandle, al::to_address(outiter), samples);
         if(amt < 0)
         if(amt < 0)
         {
         {
-            ERR("read error: %s\n", snd_strerror(static_cast<int>(amt)));
+            ERR("read error: {}", snd_strerror(static_cast<int>(amt)));
 
 
             if(amt == -EAGAIN)
             if(amt == -EAGAIN)
                 continue;
                 continue;
@@ -1113,8 +1107,8 @@ void AlsaCapture::captureSamples(std::byte *buffer, uint samples)
             if(amt < 0)
             if(amt < 0)
             {
             {
                 const char *err{snd_strerror(static_cast<int>(amt))};
                 const char *err{snd_strerror(static_cast<int>(amt))};
-                ERR("restore error: %s\n", err);
-                mDevice->handleDisconnect("Capture recovery failure: %s", err);
+                ERR("restore error: {}", err);
+                mDevice->handleDisconnect("Capture recovery failure: {}", err);
                 break;
                 break;
             }
             }
             /* If the amount available is less than what's asked, we lost it
             /* If the amount available is less than what's asked, we lost it
@@ -1139,7 +1133,7 @@ uint AlsaCapture::availableSamples()
         avail = snd_pcm_avail_update(mPcmHandle);
         avail = snd_pcm_avail_update(mPcmHandle);
     if(avail < 0)
     if(avail < 0)
     {
     {
-        ERR("avail update failed: %s\n", snd_strerror(static_cast<int>(avail)));
+        ERR("snd_pcm_avail_update failed: {}", snd_strerror(static_cast<int>(avail)));
 
 
         avail = snd_pcm_recover(mPcmHandle, static_cast<int>(avail), 1);
         avail = snd_pcm_recover(mPcmHandle, static_cast<int>(avail), 1);
         if(avail >= 0)
         if(avail >= 0)
@@ -1152,29 +1146,29 @@ uint AlsaCapture::availableSamples()
         if(avail < 0)
         if(avail < 0)
         {
         {
             const char *err{snd_strerror(static_cast<int>(avail))};
             const char *err{snd_strerror(static_cast<int>(avail))};
-            ERR("restore error: %s\n", err);
-            mDevice->handleDisconnect("Capture recovery failure: %s", err);
+            ERR("restore error: {}", err);
+            mDevice->handleDisconnect("Capture recovery failure: {}", err);
         }
         }
     }
     }
 
 
     if(!mRing)
     if(!mRing)
     {
     {
-        if(avail < 0) avail = 0;
+        avail = std::max<snd_pcm_sframes_t>(avail, 0);
         avail += snd_pcm_bytes_to_frames(mPcmHandle, static_cast<ssize_t>(mBuffer.size()));
         avail += snd_pcm_bytes_to_frames(mPcmHandle, static_cast<ssize_t>(mBuffer.size()));
-        if(avail > mLastAvail) mLastAvail = avail;
+        mLastAvail = std::max(mLastAvail, avail);
         return static_cast<uint>(mLastAvail);
         return static_cast<uint>(mLastAvail);
     }
     }
 
 
     while(avail > 0)
     while(avail > 0)
     {
     {
         auto vec = mRing->getWriteVector();
         auto vec = mRing->getWriteVector();
-        if(vec.first.len == 0) break;
+        if(vec[0].len == 0) break;
 
 
-        snd_pcm_sframes_t amt{std::min(static_cast<snd_pcm_sframes_t>(vec.first.len), avail)};
-        amt = snd_pcm_readi(mPcmHandle, vec.first.buf, static_cast<snd_pcm_uframes_t>(amt));
+        snd_pcm_sframes_t amt{std::min(static_cast<snd_pcm_sframes_t>(vec[0].len), avail)};
+        amt = snd_pcm_readi(mPcmHandle, vec[0].buf, static_cast<snd_pcm_uframes_t>(amt));
         if(amt < 0)
         if(amt < 0)
         {
         {
-            ERR("read error: %s\n", snd_strerror(static_cast<int>(amt)));
+            ERR("read error: {}", snd_strerror(static_cast<int>(amt)));
 
 
             if(amt == -EAGAIN)
             if(amt == -EAGAIN)
                 continue;
                 continue;
@@ -1189,8 +1183,8 @@ uint AlsaCapture::availableSamples()
             if(amt < 0)
             if(amt < 0)
             {
             {
                 const char *err{snd_strerror(static_cast<int>(amt))};
                 const char *err{snd_strerror(static_cast<int>(amt))};
-                ERR("restore error: %s\n", err);
-                mDevice->handleDisconnect("Capture recovery failure: %s", err);
+                ERR("restore error: {}", err);
+                mDevice->handleDisconnect("Capture recovery failure: {}", err);
                 break;
                 break;
             }
             }
             avail = amt;
             avail = amt;
@@ -1206,18 +1200,17 @@ uint AlsaCapture::availableSamples()
 
 
 ClockLatency AlsaCapture::getClockLatency()
 ClockLatency AlsaCapture::getClockLatency()
 {
 {
-    ClockLatency ret;
-
+    ClockLatency ret{};
     ret.ClockTime = mDevice->getClockTime();
     ret.ClockTime = mDevice->getClockTime();
     snd_pcm_sframes_t delay{};
     snd_pcm_sframes_t delay{};
     int err{snd_pcm_delay(mPcmHandle, &delay)};
     int err{snd_pcm_delay(mPcmHandle, &delay)};
     if(err < 0)
     if(err < 0)
     {
     {
-        ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
+        ERR("Failed to get pcm delay: {}", snd_strerror(err));
         delay = 0;
         delay = 0;
     }
     }
     ret.Latency  = std::chrono::seconds{std::max<snd_pcm_sframes_t>(0, delay)};
     ret.Latency  = std::chrono::seconds{std::max<snd_pcm_sframes_t>(0, delay)};
-    ret.Latency /= mDevice->Frequency;
+    ret.Latency /= mDevice->mSampleRate;
 
 
     return ret;
     return ret;
 }
 }
@@ -1227,13 +1220,13 @@ ClockLatency AlsaCapture::getClockLatency()
 
 
 bool AlsaBackendFactory::init()
 bool AlsaBackendFactory::init()
 {
 {
-#ifdef HAVE_DYNLOAD
+#if HAVE_DYNLOAD
     if(!alsa_handle)
     if(!alsa_handle)
     {
     {
         alsa_handle = LoadLib("libasound.so.2");
         alsa_handle = LoadLib("libasound.so.2");
         if(!alsa_handle)
         if(!alsa_handle)
         {
         {
-            WARN("Failed to load %s\n", "libasound.so.2");
+            WARN("Failed to load {}", "libasound.so.2");
             return false;
             return false;
         }
         }
 
 
@@ -1247,7 +1240,7 @@ bool AlsaBackendFactory::init()
 
 
         if(!missing_funcs.empty())
         if(!missing_funcs.empty())
         {
         {
-            WARN("Missing expected functions:%s\n", missing_funcs.c_str());
+            WARN("Missing expected functions:{}", missing_funcs);
             CloseLib(alsa_handle);
             CloseLib(alsa_handle);
             alsa_handle = nullptr;
             alsa_handle = nullptr;
             return false;
             return false;
@@ -1261,26 +1254,23 @@ bool AlsaBackendFactory::init()
 bool AlsaBackendFactory::querySupport(BackendType type)
 bool AlsaBackendFactory::querySupport(BackendType type)
 { return (type == BackendType::Playback || type == BackendType::Capture); }
 { 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
     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)
     switch(type)
     {
     {
     case BackendType::Playback:
     case BackendType::Playback:
         PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
         PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
+        outnames.reserve(PlaybackDevices.size());
         std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
         std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
         break;
         break;
 
 
     case BackendType::Capture:
     case BackendType::Capture:
         CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
         CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
+        outnames.reserve(CaptureDevices.size());
         std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
         std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
         break;
         break;
     }
     }

+ 5 - 5
libs/openal-soft/alc/backends/alsa.h

@@ -5,15 +5,15 @@
 
 
 struct AlsaBackendFactory final : public BackendFactory {
 struct AlsaBackendFactory final : public BackendFactory {
 public:
 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 */
 #endif /* BACKENDS_ALSA_H */

+ 41 - 23
libs/openal-soft/alc/backends/base.cpp

@@ -3,36 +3,18 @@
 
 
 #include "base.h"
 #include "base.h"
 
 
-#include <algorithm>
 #include <array>
 #include <array>
 #include <atomic>
 #include <atomic>
+#include <utility>
 
 
-#ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <mmreg.h>
-
-#include "albit.h"
-#include "core/logging.h"
-#endif
-
-#include "atomic.h"
 #include "core/devformat.h"
 #include "core/devformat.h"
 
 
 
 
 namespace al {
 namespace al {
+auto backend_exception::make_string(fmt::string_view fmt, fmt::format_args args) -> std::string
+{ return fmt::vformat(fmt, std::move(args)); }
 
 
-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;
 backend_exception::~backend_exception() = default;
-
 } // namespace al
 } // namespace al
 
 
 
 
@@ -60,8 +42,8 @@ ClockLatency BackendBase::getClockLatency()
      * any given time during playback. Without a more accurate measurement from
      * any given time during playback. Without a more accurate measurement from
      * the output, this is an okay approximation.
      * the output, this is an okay approximation.
      */
      */
-    ret.Latency = std::chrono::seconds{mDevice->BufferSize - mDevice->UpdateSize};
-    ret.Latency /= mDevice->Frequency;
+    ret.Latency = std::chrono::seconds{mDevice->mBufferSize - mDevice->mUpdateSize};
+    ret.Latency /= mDevice->mSampleRate;
 
 
     return ret;
     return ret;
 }
 }
@@ -126,6 +108,24 @@ void BackendBase::setDefaultWFXChannelOrder() const
         mDevice->RealOut.ChannelIndex[TopBackLeft]   = 10;
         mDevice->RealOut.ChannelIndex[TopBackLeft]   = 10;
         mDevice->RealOut.ChannelIndex[TopBackRight]  = 11;
         mDevice->RealOut.ChannelIndex[TopBackRight]  = 11;
         break;
         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:
     case DevFmtX3D71:
         mDevice->RealOut.ChannelIndex[FrontLeft]   = 0;
         mDevice->RealOut.ChannelIndex[FrontLeft]   = 0;
         mDevice->RealOut.ChannelIndex[FrontRight]  = 1;
         mDevice->RealOut.ChannelIndex[FrontRight]  = 1;
@@ -179,6 +179,24 @@ void BackendBase::setDefaultChannelOrder() const
         mDevice->RealOut.ChannelIndex[TopBackLeft]   = 10;
         mDevice->RealOut.ChannelIndex[TopBackLeft]   = 10;
         mDevice->RealOut.ChannelIndex[TopBackRight]  = 11;
         mDevice->RealOut.ChannelIndex[TopBackRight]  = 11;
         break;
         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:
     case DevFmtX3D71:
         mDevice->RealOut.ChannelIndex[FrontLeft]   = 0;
         mDevice->RealOut.ChannelIndex[FrontLeft]   = 0;
         mDevice->RealOut.ChannelIndex[FrontRight]  = 1;
         mDevice->RealOut.ChannelIndex[FrontRight]  = 1;

+ 33 - 14
libs/openal-soft/alc/backends/base.h

@@ -5,13 +5,14 @@
 #include <cstdarg>
 #include <cstdarg>
 #include <cstddef>
 #include <cstddef>
 #include <memory>
 #include <memory>
-#include <ratio>
 #include <string>
 #include <string>
 #include <string_view>
 #include <string_view>
+#include <vector>
 
 
+#include "alc/events.h"
 #include "core/device.h"
 #include "core/device.h"
 #include "core/except.h"
 #include "core/except.h"
-#include "alc/events.h"
+#include "fmt/core.h"
 
 
 
 
 using uint = unsigned int;
 using uint = unsigned int;
@@ -34,10 +35,17 @@ struct BackendBase {
     virtual ClockLatency getClockLatency();
     virtual ClockLatency getClockLatency();
 
 
     DeviceBase *const mDevice;
     DeviceBase *const mDevice;
+    std::string mDeviceName;
 
 
-    BackendBase(DeviceBase *device) noexcept : mDevice{device} { }
+    BackendBase() = delete;
+    BackendBase(const BackendBase&) = delete;
+    BackendBase(BackendBase&&) = delete;
+    explicit BackendBase(DeviceBase *device) noexcept : mDevice{device} { }
     virtual ~BackendBase() = default;
     virtual ~BackendBase() = default;
 
 
+    void operator=(const BackendBase&) = delete;
+    void operator=(BackendBase&&) = delete;
+
 protected:
 protected:
     /** Sets the default channel order used by most non-WaveFormatEx-based APIs. */
     /** Sets the default channel order used by most non-WaveFormatEx-based APIs. */
     void setDefaultChannelOrder() const;
     void setDefaultChannelOrder() const;
@@ -64,18 +72,24 @@ inline ClockLatency GetClockLatency(DeviceBase *device, BackendBase *backend)
 
 
 
 
 struct BackendFactory {
 struct BackendFactory {
+    BackendFactory() = default;
+    BackendFactory(const BackendFactory&) = delete;
+    BackendFactory(BackendFactory&&) = delete;
     virtual ~BackendFactory() = default;
     virtual ~BackendFactory() = default;
 
 
-    virtual bool init() = 0;
+    void operator=(const BackendFactory&) = delete;
+    void operator=(BackendFactory&&) = delete;
 
 
-    virtual bool querySupport(BackendType type) = 0;
+    virtual auto init() -> bool = 0;
 
 
-    virtual alc::EventSupport queryEventSupport(alc::EventType, BackendType)
+    virtual auto querySupport(BackendType type) -> bool = 0;
+
+    virtual auto queryEventSupport(alc::EventType, BackendType) -> alc::EventSupport
     { return alc::EventSupport::NoSupport; }
     { return alc::EventSupport::NoSupport; }
 
 
-    virtual std::string probe(BackendType type) = 0;
+    virtual auto enumerate(BackendType type) -> std::vector<std::string> = 0;
 
 
-    virtual BackendPtr createBackend(DeviceBase *device, BackendType type) = 0;
+    virtual auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr = 0;
 };
 };
 
 
 namespace al {
 namespace al {
@@ -89,15 +103,20 @@ enum class backend_error {
 class backend_exception final : public base_exception {
 class backend_exception final : public base_exception {
     backend_error mErrorCode;
     backend_error mErrorCode;
 
 
+    static auto make_string(fmt::string_view fmt, fmt::format_args args) -> std::string;
+
 public:
 public:
-#ifdef __MINGW32__
-    [[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]]
-#else
-    [[gnu::format(printf, 3, 4)]]
-#endif
-    backend_exception(backend_error code, const char *msg, ...);
+    template<typename ...Args>
+    backend_exception(backend_error code, fmt::format_string<Args...> fmt, Args&& ...args)
+        : base_exception{make_string(fmt, fmt::make_format_args(args...))}, mErrorCode{code}
+    { }
+    backend_exception(const backend_exception&) = default;
+    backend_exception(backend_exception&&) = default;
     ~backend_exception() override;
     ~backend_exception() override;
 
 
+    backend_exception& operator=(const backend_exception&) = default;
+    backend_exception& operator=(backend_exception&&) = default;
+
     [[nodiscard]] auto errorCode() const noexcept -> backend_error { return mErrorCode; }
     [[nodiscard]] auto errorCode() const noexcept -> backend_error { return mErrorCode; }
 };
 };
 
 

+ 171 - 100
libs/openal-soft/alc/backends/coreaudio.cpp

@@ -24,7 +24,9 @@
 
 
 #include <cinttypes>
 #include <cinttypes>
 #include <cmath>
 #include <cmath>
+#include <functional>
 #include <memory>
 #include <memory>
+#include <optional>
 #include <stdint.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdlib.h>
@@ -32,13 +34,13 @@
 #include <string.h>
 #include <string.h>
 #include <unistd.h>
 #include <unistd.h>
 #include <vector>
 #include <vector>
-#include <optional>
 
 
 #include "alnumeric.h"
 #include "alnumeric.h"
 #include "alstring.h"
 #include "alstring.h"
 #include "core/converter.h"
 #include "core/converter.h"
 #include "core/device.h"
 #include "core/device.h"
 #include "core/logging.h"
 #include "core/logging.h"
+#include "fmt/core.h"
 #include "ringbuffer.h"
 #include "ringbuffer.h"
 
 
 #include <AudioUnit/AudioUnit.h>
 #include <AudioUnit/AudioUnit.h>
@@ -56,10 +58,40 @@ namespace {
 constexpr auto OutputElement = 0;
 constexpr auto OutputElement = 0;
 constexpr auto InputElement = 1;
 constexpr auto InputElement = 1;
 
 
+// These following arrays should always be defined in ascending AudioChannelLabel value order
+constexpr std::array<AudioChannelLabel, 1> MonoChanMap { kAudioChannelLabel_Mono };
+constexpr std::array<AudioChannelLabel, 2> StereoChanMap { kAudioChannelLabel_Left, kAudioChannelLabel_Right};
+constexpr std::array<AudioChannelLabel, 4> QuadChanMap {
+        kAudioChannelLabel_Left, kAudioChannelLabel_Right,
+        kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround
+};
+constexpr std::array<AudioChannelLabel, 6> X51ChanMap {
+        kAudioChannelLabel_Left, kAudioChannelLabel_Right,
+        kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen,
+        kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround
+};
+constexpr std::array<AudioChannelLabel, 6> X51RearChanMap {
+        kAudioChannelLabel_Left, kAudioChannelLabel_Right,
+        kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen,
+        kAudioChannelLabel_RearSurroundRight, kAudioChannelLabel_RearSurroundLeft
+};
+constexpr std::array<AudioChannelLabel, 7> X61ChanMap {
+        kAudioChannelLabel_Left, kAudioChannelLabel_Right,
+        kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen,
+        kAudioChannelLabel_CenterSurround,
+        kAudioChannelLabel_RearSurroundRight, kAudioChannelLabel_RearSurroundLeft
+};
+constexpr std::array<AudioChannelLabel, 8> X71ChanMap {
+        kAudioChannelLabel_Left, kAudioChannelLabel_Right,
+        kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen,
+        kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround,
+        kAudioChannelLabel_LeftCenter, kAudioChannelLabel_RightCenter
+};
+
 struct FourCCPrinter {
 struct FourCCPrinter {
     char mString[sizeof(UInt32) + 1]{};
     char mString[sizeof(UInt32) + 1]{};
 
 
-    constexpr FourCCPrinter(UInt32 code) noexcept
+    explicit constexpr FourCCPrinter(UInt32 code) noexcept
     {
     {
         for(size_t i{0};i < sizeof(UInt32);++i)
         for(size_t i{0};i < sizeof(UInt32);++i)
         {
         {
@@ -73,7 +105,7 @@ struct FourCCPrinter {
             code >>= 8;
             code >>= 8;
         }
         }
     }
     }
-    constexpr FourCCPrinter(int code) noexcept : FourCCPrinter{static_cast<UInt32>(code)} { }
+    explicit constexpr FourCCPrinter(OSStatus code) noexcept : FourCCPrinter{static_cast<UInt32>(code)} { }
 
 
     constexpr const char *c_str() const noexcept { return mString; }
     constexpr const char *c_str() const noexcept { return mString; }
 };
 };
@@ -159,19 +191,19 @@ std::string GetDeviceName(AudioDeviceID devId)
     /* Clear extraneous nul chars that may have been written with the name
     /* Clear extraneous nul chars that may have been written with the name
      * string, and return it.
      * string, and return it.
      */
      */
-    while(!devname.back())
+    while(!devname.empty() && !devname.back())
         devname.pop_back();
         devname.pop_back();
     return devname;
     return devname;
 }
 }
 
 
-UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture)
+auto GetDeviceChannelCount(AudioDeviceID devId, bool isCapture) -> UInt32
 {
 {
-    UInt32 propSize{};
+    auto propSize = UInt32{};
     auto err = GetDevPropertySize(devId, kAudioDevicePropertyStreamConfiguration, isCapture, 0,
     auto err = GetDevPropertySize(devId, kAudioDevicePropertyStreamConfiguration, isCapture, 0,
         &propSize);
         &propSize);
     if(err)
     if(err)
     {
     {
-        ERR("kAudioDevicePropertyStreamConfiguration size query failed: '%s' (%u)\n",
+        ERR("kAudioDevicePropertyStreamConfiguration size query failed: '{}' ({})",
             FourCCPrinter{err}.c_str(), err);
             FourCCPrinter{err}.c_str(), err);
         return 0;
         return 0;
     }
     }
@@ -183,15 +215,14 @@ UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture)
         buflist);
         buflist);
     if(err)
     if(err)
     {
     {
-        ERR("kAudioDevicePropertyStreamConfiguration query failed: '%s' (%u)\n",
+        ERR("kAudioDevicePropertyStreamConfiguration query failed: '{}' ({})",
             FourCCPrinter{err}.c_str(), err);
             FourCCPrinter{err}.c_str(), err);
         return 0;
         return 0;
     }
     }
 
 
-    UInt32 numChannels{0};
+    auto numChannels = UInt32{0};
     for(size_t i{0};i < buflist->mNumberBuffers;++i)
     for(size_t i{0};i < buflist->mNumberBuffers;++i)
         numChannels += buflist->mBuffers[i].mNumberChannels;
         numChannels += buflist->mBuffers[i].mNumberChannels;
-
     return numChannels;
     return numChannels;
 }
 }
 
 
@@ -201,14 +232,14 @@ void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture)
     UInt32 propSize{};
     UInt32 propSize{};
     if(auto err = GetHwPropertySize(kAudioHardwarePropertyDevices, &propSize))
     if(auto err = GetHwPropertySize(kAudioHardwarePropertyDevices, &propSize))
     {
     {
-        ERR("Failed to get device list size: %u\n", err);
+        ERR("Failed to get device list size: {}", err);
         return;
         return;
     }
     }
 
 
     auto devIds = std::vector<AudioDeviceID>(propSize/sizeof(AudioDeviceID), kAudioDeviceUnknown);
     auto devIds = std::vector<AudioDeviceID>(propSize/sizeof(AudioDeviceID), kAudioDeviceUnknown);
     if(auto err = GetHwProperty(kAudioHardwarePropertyDevices, propSize, devIds.data()))
     if(auto err = GetHwProperty(kAudioHardwarePropertyDevices, propSize, devIds.data()))
     {
     {
-        ERR("Failed to get device list: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
+        ERR("Failed to get device list: '{}' ({})", FourCCPrinter{err}.c_str(), err);
         return;
         return;
     }
     }
 
 
@@ -223,7 +254,7 @@ void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture)
     {
     {
         newdevs.emplace_back(DeviceEntry{defaultId, GetDeviceName(defaultId)});
         newdevs.emplace_back(DeviceEntry{defaultId, GetDeviceName(defaultId)});
         const auto &entry = newdevs.back();
         const auto &entry = newdevs.back();
-        TRACE("Got device: %s = ID %u\n", entry.mName.c_str(), entry.mId);
+        TRACE("Got device: {} = ID {}", entry.mName, entry.mId);
     }
     }
     for(const AudioDeviceID devId : devIds)
     for(const AudioDeviceID devId : devIds)
     {
     {
@@ -240,7 +271,7 @@ void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture)
         {
         {
             newdevs.emplace_back(DeviceEntry{devId, GetDeviceName(devId)});
             newdevs.emplace_back(DeviceEntry{devId, GetDeviceName(devId)});
             const auto &entry = newdevs.back();
             const auto &entry = newdevs.back();
-            TRACE("Got device: %s = ID %u\n", entry.mName.c_str(), entry.mId);
+            TRACE("Got device: {} = ID {}", entry.mName, entry.mId);
         }
         }
     }
     }
 
 
@@ -255,14 +286,12 @@ void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture)
             { return entry.mName == curitem->mName; };
             { return entry.mName == curitem->mName; };
             if(std::find_if(newdevs.begin(), curitem, check_match) != curitem)
             if(std::find_if(newdevs.begin(), curitem, check_match) != curitem)
             {
             {
-                std::string name{curitem->mName};
-                size_t count{1};
+                auto name = std::string{curitem->mName};
+                auto count = 1_uz;
                 auto check_name = [&name](const DeviceEntry &entry) -> bool
                 auto check_name = [&name](const DeviceEntry &entry) -> bool
                 { return entry.mName == name; };
                 { return entry.mName == name; };
                 do {
                 do {
-                    name = curitem->mName;
-                    name += " #";
-                    name += std::to_string(++count);
+                    name = fmt::format("{} #{}", curitem->mName, ++count);
                 } while(std::find_if(newdevs.begin(), curitem, check_name) != curitem);
                 } while(std::find_if(newdevs.begin(), curitem, check_name) != curitem);
                 curitem->mName = std::move(name);
                 curitem->mName = std::move(name);
             }
             }
@@ -277,18 +306,18 @@ struct DeviceHelper {
     DeviceHelper()
     DeviceHelper()
     {
     {
         AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice,
         AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice,
-            kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain};
+            kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster};
         OSStatus status = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil);
         OSStatus status = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil);
         if (status != noErr)
         if (status != noErr)
-            ERR("AudioObjectAddPropertyListener fail: %d", status);
+            ERR("AudioObjectAddPropertyListener fail: {}", status);
     }
     }
     ~DeviceHelper()
     ~DeviceHelper()
     {
     {
         AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice,
         AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice,
-            kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain};
+            kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster};
         OSStatus status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil);
         OSStatus status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil);
         if (status != noErr)
         if (status != noErr)
-            ERR("AudioObjectRemovePropertyListener fail: %d", status);
+            ERR("AudioObjectRemovePropertyListener fail: {}", status);
     }
     }
 
 
     static OSStatus DeviceListenerProc(AudioObjectID /*inObjectID*/, UInt32 inNumberAddresses,
     static OSStatus DeviceListenerProc(AudioObjectID /*inObjectID*/, UInt32 inNumberAddresses,
@@ -322,7 +351,7 @@ static constexpr char ca_device[] = "CoreAudio Default";
 
 
 
 
 struct CoreAudioPlayback final : public BackendBase {
 struct CoreAudioPlayback final : public BackendBase {
-    CoreAudioPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit CoreAudioPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
     ~CoreAudioPlayback() override;
     ~CoreAudioPlayback() override;
 
 
     OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags,
     OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags,
@@ -377,7 +406,7 @@ void CoreAudioPlayback::open(std::string_view name)
         auto devmatch = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), find_name);
         auto devmatch = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), find_name);
         if(devmatch == PlaybackList.cend())
         if(devmatch == PlaybackList.cend())
             throw al::backend_exception{al::backend_error::NoDevice,
             throw al::backend_exception{al::backend_error::NoDevice,
-                "Device name \"%.*s\" not found", al::sizei(name), name.data()};
+                "Device name \"{}\" not found", name};
 
 
         audioDevice = devmatch->mId;
         audioDevice = devmatch->mId;
     }
     }
@@ -385,8 +414,8 @@ void CoreAudioPlayback::open(std::string_view name)
     if(name.empty())
     if(name.empty())
         name = ca_device;
         name = ca_device;
     else if(name != ca_device)
     else if(name != ca_device)
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
-            al::sizei(name), name.data()};
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
+            name};
 #endif
 #endif
 
 
     /* open the default output unit */
     /* open the default output unit */
@@ -410,7 +439,7 @@ void CoreAudioPlayback::open(std::string_view name)
     OSStatus err{AudioComponentInstanceNew(comp, &audioUnit)};
     OSStatus err{AudioComponentInstanceNew(comp, &audioUnit)};
     if(err != noErr)
     if(err != noErr)
         throw al::backend_exception{al::backend_error::NoDevice,
         throw al::backend_exception{al::backend_error::NoDevice,
-            "Could not create component instance: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
+            "Could not create component instance: '{}' ({})", FourCCPrinter{err}.c_str(), err};
 
 
 #if CAN_ENUMERATE
 #if CAN_ENUMERATE
     if(audioDevice != kAudioDeviceUnknown)
     if(audioDevice != kAudioDeviceUnknown)
@@ -421,7 +450,7 @@ void CoreAudioPlayback::open(std::string_view name)
     err = AudioUnitInitialize(audioUnit);
     err = AudioUnitInitialize(audioUnit);
     if(err != noErr)
     if(err != noErr)
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Could not initialize audio unit: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
+            "Could not initialize audio unit: '{}' ({})", FourCCPrinter{err}.c_str(), err};
 
 
     /* WARNING: I don't know if "valid" audio unit values are guaranteed to be
     /* WARNING: I don't know if "valid" audio unit values are guaranteed to be
      * non-0. If not, this logic is broken.
      * non-0. If not, this logic is broken.
@@ -435,7 +464,7 @@ void CoreAudioPlayback::open(std::string_view name)
 
 
 #if CAN_ENUMERATE
 #if CAN_ENUMERATE
     if(!name.empty())
     if(!name.empty())
-        mDevice->DeviceName = name;
+        mDeviceName = name;
     else
     else
     {
     {
         UInt32 propSize{sizeof(audioDevice)};
         UInt32 propSize{sizeof(audioDevice)};
@@ -444,8 +473,8 @@ void CoreAudioPlayback::open(std::string_view name)
             kAudioUnitScope_Global, OutputElement, &audioDevice, &propSize);
             kAudioUnitScope_Global, OutputElement, &audioDevice, &propSize);
 
 
         std::string devname{GetDeviceName(audioDevice)};
         std::string devname{GetDeviceName(audioDevice)};
-        if(!devname.empty()) mDevice->DeviceName = std::move(devname);
-        else mDevice->DeviceName = "Unknown Device Name";
+        if(!devname.empty()) mDeviceName = std::move(devname);
+        else mDeviceName = "Unknown Device Name";
     }
     }
 
 
     if(audioDevice != kAudioDeviceUnknown)
     if(audioDevice != kAudioDeviceUnknown)
@@ -454,16 +483,16 @@ void CoreAudioPlayback::open(std::string_view name)
         err = GetDevProperty(audioDevice, kAudioDevicePropertyDataSource, false,
         err = GetDevProperty(audioDevice, kAudioDevicePropertyDataSource, false,
             kAudioObjectPropertyElementMaster, sizeof(type), &type);
             kAudioObjectPropertyElementMaster, sizeof(type), &type);
         if(err != noErr)
         if(err != noErr)
-            ERR("Failed to get audio device type: %u\n", err);
+            WARN("Failed to get audio device type: '{}' ({})", FourCCPrinter{err}.c_str(), err);
         else
         else
         {
         {
-            TRACE("Got device type '%s'\n", FourCCPrinter{type}.c_str());
+            TRACE("Got device type '{}'", FourCCPrinter{type}.c_str());
             mDevice->Flags.set(DirectEar, (type == kIOAudioOutputPortSubTypeHeadphones));
             mDevice->Flags.set(DirectEar, (type == kIOAudioOutputPortSubTypeHeadphones));
         }
         }
     }
     }
 
 
 #else
 #else
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 #endif
 #endif
 }
 }
 
 
@@ -471,7 +500,7 @@ bool CoreAudioPlayback::reset()
 {
 {
     OSStatus err{AudioUnitUninitialize(mAudioUnit)};
     OSStatus err{AudioUnitUninitialize(mAudioUnit)};
     if(err != noErr)
     if(err != noErr)
-        ERR("AudioUnitUninitialize failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
+        ERR("AudioUnitUninitialize failed: '{}' ({})", FourCCPrinter{err}.c_str(), err);
 
 
     /* retrieve default output unit's properties (output side) */
     /* retrieve default output unit's properties (output side) */
     AudioStreamBasicDescription streamFormat{};
     AudioStreamBasicDescription streamFormat{};
@@ -480,33 +509,75 @@ bool CoreAudioPlayback::reset()
         OutputElement, &streamFormat, &size);
         OutputElement, &streamFormat, &size);
     if(err != noErr || size != sizeof(streamFormat))
     if(err != noErr || size != sizeof(streamFormat))
     {
     {
-        ERR("AudioUnitGetProperty(StreamFormat) failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(),
+        ERR("AudioUnitGetProperty(StreamFormat) failed: '{}' ({})", FourCCPrinter{err}.c_str(),
             err);
             err);
         return false;
         return false;
     }
     }
 
 
-#if 0
-    TRACE("Output streamFormat of default output unit -\n");
-    TRACE("  streamFormat.mFramesPerPacket = %d\n", streamFormat.mFramesPerPacket);
-    TRACE("  streamFormat.mChannelsPerFrame = %d\n", streamFormat.mChannelsPerFrame);
-    TRACE("  streamFormat.mBitsPerChannel = %d\n", streamFormat.mBitsPerChannel);
-    TRACE("  streamFormat.mBytesPerPacket = %d\n", streamFormat.mBytesPerPacket);
-    TRACE("  streamFormat.mBytesPerFrame = %d\n", streamFormat.mBytesPerFrame);
-    TRACE("  streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate);
-#endif
-
     /* Use the sample rate from the output unit's current parameters, but reset
     /* Use the sample rate from the output unit's current parameters, but reset
      * everything else.
      * everything else.
      */
      */
-    if(mDevice->Frequency != streamFormat.mSampleRate)
+    if(mDevice->mSampleRate != streamFormat.mSampleRate)
+    {
+        mDevice->mBufferSize = static_cast<uint>(mDevice->mBufferSize*streamFormat.mSampleRate/
+            mDevice->mSampleRate + 0.5);
+        mDevice->mSampleRate = static_cast<uint>(streamFormat.mSampleRate);
+    }
+
+    struct ChannelMap {
+        DevFmtChannels fmt;
+        al::span<const AudioChannelLabel> map;
+        bool is_51rear;
+    };
+
+    static constexpr std::array<ChannelMap,7> chanmaps{{
+        { DevFmtX71, X71ChanMap, false },
+        { DevFmtX61, X61ChanMap, false },
+        { DevFmtX51, X51ChanMap, false },
+        { DevFmtX51, X51RearChanMap, true },
+        { DevFmtQuad, QuadChanMap, false },
+        { DevFmtStereo, StereoChanMap, false },
+        { DevFmtMono, MonoChanMap, false }
+    }};
+
+    if(!mDevice->Flags.test(ChannelsRequest))
     {
     {
-        mDevice->BufferSize = static_cast<uint>(mDevice->BufferSize*streamFormat.mSampleRate/
-            mDevice->Frequency + 0.5);
-        mDevice->Frequency = static_cast<uint>(streamFormat.mSampleRate);
+        auto propSize = UInt32{};
+        auto writable = Boolean{};
+
+        err = AudioUnitGetPropertyInfo(mAudioUnit, kAudioUnitProperty_AudioChannelLayout,
+            kAudioUnitScope_Output, OutputElement, &propSize, &writable);
+        if(err == noErr)
+        {
+            auto layout_data = std::make_unique<char[]>(propSize);
+            auto *layout = reinterpret_cast<AudioChannelLayout*>(layout_data.get());
+
+            err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_AudioChannelLayout,
+                kAudioUnitScope_Output, OutputElement, layout, &propSize);
+            if(err == noErr)
+            {
+                auto descs = al::span{std::data(layout->mChannelDescriptions),
+                    layout->mNumberChannelDescriptions};
+                auto labels = std::vector<AudioChannelLayoutTag>(descs.size());
+
+                std::transform(descs.begin(), descs.end(), labels.begin(),
+                    std::mem_fn(&AudioChannelDescription::mChannelLabel));
+                sort(labels.begin(), labels.end());
+
+                auto check_labels = [&labels](const ChannelMap &chanmap) -> bool
+                {
+                    return std::includes(labels.begin(), labels.end(), chanmap.map.begin(),
+                        chanmap.map.end());
+                };
+                auto chaniter = std::find_if(chanmaps.cbegin(), chanmaps.cend(), check_labels);
+                if(chaniter != chanmaps.cend())
+                    mDevice->FmtChans = chaniter->fmt;
+            }
+        }
     }
     }
 
 
-    /* FIXME: How to tell what channels are what in the output device, and how
-     * to specify what we're giving? e.g. 6.0 vs 5.1
+    /* TODO: Also set kAudioUnitProperty_AudioChannelLayout according to the AL
+     * device's channel configuration.
      */
      */
     streamFormat.mChannelsPerFrame = mDevice->channelsFromFmt();
     streamFormat.mChannelsPerFrame = mDevice->channelsFromFmt();
 
 
@@ -548,7 +619,7 @@ bool CoreAudioPlayback::reset()
         OutputElement, &streamFormat, sizeof(streamFormat));
         OutputElement, &streamFormat, sizeof(streamFormat));
     if(err != noErr)
     if(err != noErr)
     {
     {
-        ERR("AudioUnitSetProperty(StreamFormat) failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(),
+        ERR("AudioUnitSetProperty(StreamFormat) failed: '{}' ({})", FourCCPrinter{err}.c_str(),
             err);
             err);
         return false;
         return false;
     }
     }
@@ -566,7 +637,7 @@ bool CoreAudioPlayback::reset()
         kAudioUnitScope_Input, OutputElement, &input, sizeof(AURenderCallbackStruct));
         kAudioUnitScope_Input, OutputElement, &input, sizeof(AURenderCallbackStruct));
     if(err != noErr)
     if(err != noErr)
     {
     {
-        ERR("AudioUnitSetProperty(SetRenderCallback) failed: '%s' (%u)\n",
+        ERR("AudioUnitSetProperty(SetRenderCallback) failed: '{}' ({})",
             FourCCPrinter{err}.c_str(), err);
             FourCCPrinter{err}.c_str(), err);
         return false;
         return false;
     }
     }
@@ -575,7 +646,7 @@ bool CoreAudioPlayback::reset()
     err = AudioUnitInitialize(mAudioUnit);
     err = AudioUnitInitialize(mAudioUnit);
     if(err != noErr)
     if(err != noErr)
     {
     {
-        ERR("AudioUnitInitialize failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
+        ERR("AudioUnitInitialize failed: '{}' ({})", FourCCPrinter{err}.c_str(), err);
         return false;
         return false;
     }
     }
 
 
@@ -587,19 +658,19 @@ void CoreAudioPlayback::start()
     const OSStatus err{AudioOutputUnitStart(mAudioUnit)};
     const OSStatus err{AudioOutputUnitStart(mAudioUnit)};
     if(err != noErr)
     if(err != noErr)
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "AudioOutputUnitStart failed: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
+            "AudioOutputUnitStart failed: '{}' ({})", FourCCPrinter{err}.c_str(), err};
 }
 }
 
 
 void CoreAudioPlayback::stop()
 void CoreAudioPlayback::stop()
 {
 {
     OSStatus err{AudioOutputUnitStop(mAudioUnit)};
     OSStatus err{AudioOutputUnitStop(mAudioUnit)};
     if(err != noErr)
     if(err != noErr)
-        ERR("AudioOutputUnitStop failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
+        ERR("AudioOutputUnitStop failed: '{}' ({})", FourCCPrinter{err}.c_str(), err);
 }
 }
 
 
 
 
 struct CoreAudioCapture final : public BackendBase {
 struct CoreAudioCapture final : public BackendBase {
-    CoreAudioCapture(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit CoreAudioCapture(DeviceBase *device) noexcept : BackendBase{device} { }
     ~CoreAudioCapture() override;
     ~CoreAudioCapture() override;
 
 
     OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
     OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
@@ -650,7 +721,7 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
         inNumberFrames, &audiobuf.list)};
         inNumberFrames, &audiobuf.list)};
     if(err != noErr)
     if(err != noErr)
     {
     {
-        ERR("AudioUnitRender capture error: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
+        ERR("AudioUnitRender capture error: '{}' ({})", FourCCPrinter{err}.c_str(), err);
         return err;
         return err;
     }
     }
 
 
@@ -676,7 +747,7 @@ void CoreAudioCapture::open(std::string_view name)
         auto devmatch = std::find_if(CaptureList.cbegin(), CaptureList.cend(), find_name);
         auto devmatch = std::find_if(CaptureList.cbegin(), CaptureList.cend(), find_name);
         if(devmatch == CaptureList.cend())
         if(devmatch == CaptureList.cend())
             throw al::backend_exception{al::backend_error::NoDevice,
             throw al::backend_exception{al::backend_error::NoDevice,
-                "Device name \"%.*s\" not found", al::sizei(name), name.data()};
+                "Device name \"{}\" not found", name};
 
 
         audioDevice = devmatch->mId;
         audioDevice = devmatch->mId;
     }
     }
@@ -684,8 +755,8 @@ void CoreAudioCapture::open(std::string_view name)
     if(name.empty())
     if(name.empty())
         name = ca_device;
         name = ca_device;
     else if(name != ca_device)
     else if(name != ca_device)
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
-            al::sizei(name), name.data()};
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
+            name};
 #endif
 #endif
 
 
     AudioComponentDescription desc{};
     AudioComponentDescription desc{};
@@ -709,7 +780,7 @@ void CoreAudioCapture::open(std::string_view name)
     OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)};
     OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)};
     if(err != noErr)
     if(err != noErr)
         throw al::backend_exception{al::backend_error::NoDevice,
         throw al::backend_exception{al::backend_error::NoDevice,
-            "Could not create component instance: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
+            "Could not create component instance: '{}' ({})", FourCCPrinter{err}.c_str(), err};
 
 
     // Turn off AudioUnit output
     // Turn off AudioUnit output
     UInt32 enableIO{0};
     UInt32 enableIO{0};
@@ -717,7 +788,7 @@ void CoreAudioCapture::open(std::string_view name)
         kAudioUnitScope_Output, OutputElement, &enableIO, sizeof(enableIO));
         kAudioUnitScope_Output, OutputElement, &enableIO, sizeof(enableIO));
     if(err != noErr)
     if(err != noErr)
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Could not disable audio unit output property: '%s' (%u)", FourCCPrinter{err}.c_str(),
+            "Could not disable audio unit output property: '{}' ({})", FourCCPrinter{err}.c_str(),
             err};
             err};
 
 
     // Turn on AudioUnit input
     // Turn on AudioUnit input
@@ -726,7 +797,7 @@ void CoreAudioCapture::open(std::string_view name)
         kAudioUnitScope_Input, InputElement, &enableIO, sizeof(enableIO));
         kAudioUnitScope_Input, InputElement, &enableIO, sizeof(enableIO));
     if(err != noErr)
     if(err != noErr)
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Could not enable audio unit input property: '%s' (%u)", FourCCPrinter{err}.c_str(),
+            "Could not enable audio unit input property: '{}' ({})", FourCCPrinter{err}.c_str(),
             err};
             err};
 
 
 #if CAN_ENUMERATE
 #if CAN_ENUMERATE
@@ -745,7 +816,7 @@ void CoreAudioCapture::open(std::string_view name)
         kAudioUnitScope_Global, InputElement, &input, sizeof(AURenderCallbackStruct));
         kAudioUnitScope_Global, InputElement, &input, sizeof(AURenderCallbackStruct));
     if(err != noErr)
     if(err != noErr)
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Could not set capture callback: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
+            "Could not set capture callback: '{}' ({})", FourCCPrinter{err}.c_str(), err};
 
 
     // Disable buffer allocation for capture
     // Disable buffer allocation for capture
     UInt32 flag{0};
     UInt32 flag{0};
@@ -753,14 +824,14 @@ void CoreAudioCapture::open(std::string_view name)
         kAudioUnitScope_Output, InputElement, &flag, sizeof(flag));
         kAudioUnitScope_Output, InputElement, &flag, sizeof(flag));
     if(err != noErr)
     if(err != noErr)
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Could not disable buffer allocation property: '%s' (%u)", FourCCPrinter{err}.c_str(),
+            "Could not disable buffer allocation property: '{}' ({})", FourCCPrinter{err}.c_str(),
             err};
             err};
 
 
     // Initialize the device
     // Initialize the device
     err = AudioUnitInitialize(mAudioUnit);
     err = AudioUnitInitialize(mAudioUnit);
     if(err != noErr)
     if(err != noErr)
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Could not initialize audio unit: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
+            "Could not initialize audio unit: '{}' ({})", FourCCPrinter{err}.c_str(), err};
 
 
     // Get the hardware format
     // Get the hardware format
     AudioStreamBasicDescription hardwareFormat{};
     AudioStreamBasicDescription hardwareFormat{};
@@ -769,7 +840,7 @@ void CoreAudioCapture::open(std::string_view name)
         InputElement, &hardwareFormat, &propertySize);
         InputElement, &hardwareFormat, &propertySize);
     if(err != noErr || propertySize != sizeof(hardwareFormat))
     if(err != noErr || propertySize != sizeof(hardwareFormat))
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Could not get input format: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
+            "Could not get input format: '{}' ({})", FourCCPrinter{err}.c_str(), err};
 
 
     // Set up the requested format description
     // Set up the requested format description
     AudioStreamBasicDescription requestedFormat{};
     AudioStreamBasicDescription requestedFormat{};
@@ -822,15 +893,16 @@ void CoreAudioCapture::open(std::string_view name)
     case DevFmtX61:
     case DevFmtX61:
     case DevFmtX71:
     case DevFmtX71:
     case DevFmtX714:
     case DevFmtX714:
+    case DevFmtX7144:
     case DevFmtX3D71:
     case DevFmtX3D71:
     case DevFmtAmbi3D:
     case DevFmtAmbi3D:
-        throw al::backend_exception{al::backend_error::DeviceError, "%s not supported",
+        throw al::backend_exception{al::backend_error::DeviceError, "{} not supported",
             DevFmtChannelsString(mDevice->FmtChans)};
             DevFmtChannelsString(mDevice->FmtChans)};
     }
     }
 
 
     requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8;
     requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8;
     requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame;
     requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame;
-    requestedFormat.mSampleRate = mDevice->Frequency;
+    requestedFormat.mSampleRate = mDevice->mSampleRate;
     requestedFormat.mFormatID = kAudioFormatLinearPCM;
     requestedFormat.mFormatID = kAudioFormatLinearPCM;
     requestedFormat.mReserved = 0;
     requestedFormat.mReserved = 0;
     requestedFormat.mFramesPerPacket = 1;
     requestedFormat.mFramesPerPacket = 1;
@@ -850,18 +922,18 @@ void CoreAudioCapture::open(std::string_view name)
         InputElement, &outputFormat, sizeof(outputFormat));
         InputElement, &outputFormat, sizeof(outputFormat));
     if(err != noErr)
     if(err != noErr)
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Could not set input format: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
+            "Could not set input format: '{}' ({})", FourCCPrinter{err}.c_str(), err};
 
 
     /* Calculate the minimum AudioUnit output format frame count for the pre-
     /* Calculate the minimum AudioUnit output format frame count for the pre-
      * conversion ring buffer. Ensure at least 100ms for the total buffer.
      * conversion ring buffer. Ensure at least 100ms for the total buffer.
      */
      */
-    double srateScale{outputFormat.mSampleRate / mDevice->Frequency};
-    auto FrameCount64 = std::max(static_cast<uint64_t>(std::ceil(mDevice->BufferSize*srateScale)),
+    double srateScale{outputFormat.mSampleRate / mDevice->mSampleRate};
+    auto FrameCount64 = std::max(static_cast<uint64_t>(std::ceil(mDevice->mBufferSize*srateScale)),
         static_cast<UInt32>(outputFormat.mSampleRate)/10_u64);
         static_cast<UInt32>(outputFormat.mSampleRate)/10_u64);
     FrameCount64 += MaxResamplerPadding;
     FrameCount64 += MaxResamplerPadding;
     if(FrameCount64 > std::numeric_limits<int32_t>::max())
     if(FrameCount64 > std::numeric_limits<int32_t>::max())
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Calculated frame count is too large: %" PRIu64, FrameCount64};
+            "Calculated frame count is too large: {}", FrameCount64};
 
 
     UInt32 outputFrameCount{};
     UInt32 outputFrameCount{};
     propertySize = sizeof(outputFrameCount);
     propertySize = sizeof(outputFrameCount);
@@ -869,7 +941,7 @@ void CoreAudioCapture::open(std::string_view name)
         kAudioUnitScope_Global, OutputElement, &outputFrameCount, &propertySize);
         kAudioUnitScope_Global, OutputElement, &outputFrameCount, &propertySize);
     if(err != noErr || propertySize != sizeof(outputFrameCount))
     if(err != noErr || propertySize != sizeof(outputFrameCount))
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Could not get input frame count: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
+            "Could not get input frame count: '{}' ({})", FourCCPrinter{err}.c_str(), err};
 
 
     mCaptureData.resize(outputFrameCount * mFrameSize);
     mCaptureData.resize(outputFrameCount * mFrameSize);
 
 
@@ -877,14 +949,14 @@ void CoreAudioCapture::open(std::string_view name)
     mRing = RingBuffer::Create(outputFrameCount, mFrameSize, false);
     mRing = RingBuffer::Create(outputFrameCount, mFrameSize, false);
 
 
     /* Set up sample converter if needed */
     /* Set up sample converter if needed */
-    if(outputFormat.mSampleRate != mDevice->Frequency)
+    if(outputFormat.mSampleRate != mDevice->mSampleRate)
         mConverter = SampleConverter::Create(mDevice->FmtType, mDevice->FmtType,
         mConverter = SampleConverter::Create(mDevice->FmtType, mDevice->FmtType,
             mFormat.mChannelsPerFrame, static_cast<uint>(hardwareFormat.mSampleRate),
             mFormat.mChannelsPerFrame, static_cast<uint>(hardwareFormat.mSampleRate),
-            mDevice->Frequency, Resampler::FastBSinc24);
+            mDevice->mSampleRate, Resampler::FastBSinc24);
 
 
 #if CAN_ENUMERATE
 #if CAN_ENUMERATE
     if(!name.empty())
     if(!name.empty())
-        mDevice->DeviceName = name;
+        mDeviceName = name;
     else
     else
     {
     {
         UInt32 propSize{sizeof(audioDevice)};
         UInt32 propSize{sizeof(audioDevice)};
@@ -893,11 +965,11 @@ void CoreAudioCapture::open(std::string_view name)
             kAudioUnitScope_Global, InputElement, &audioDevice, &propSize);
             kAudioUnitScope_Global, InputElement, &audioDevice, &propSize);
 
 
         std::string devname{GetDeviceName(audioDevice)};
         std::string devname{GetDeviceName(audioDevice)};
-        if(!devname.empty()) mDevice->DeviceName = std::move(devname);
-        else mDevice->DeviceName = "Unknown Device Name";
+        if(!devname.empty()) mDeviceName = std::move(devname);
+        else mDeviceName = "Unknown Device Name";
     }
     }
 #else
 #else
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 #endif
 #endif
 }
 }
 
 
@@ -907,14 +979,14 @@ void CoreAudioCapture::start()
     OSStatus err{AudioOutputUnitStart(mAudioUnit)};
     OSStatus err{AudioOutputUnitStart(mAudioUnit)};
     if(err != noErr)
     if(err != noErr)
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "AudioOutputUnitStart failed: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
+            "AudioOutputUnitStart failed: '{}' ({})", FourCCPrinter{err}.c_str(), err};
 }
 }
 
 
 void CoreAudioCapture::stop()
 void CoreAudioCapture::stop()
 {
 {
     OSStatus err{AudioOutputUnitStop(mAudioUnit)};
     OSStatus err{AudioOutputUnitStop(mAudioUnit)};
     if(err != noErr)
     if(err != noErr)
-        ERR("AudioOutputUnitStop failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
+        ERR("AudioOutputUnitStop failed: '{}' ({})", FourCCPrinter{err}.c_str(), err);
 }
 }
 
 
 void CoreAudioCapture::captureSamples(std::byte *buffer, uint samples)
 void CoreAudioCapture::captureSamples(std::byte *buffer, uint samples)
@@ -926,16 +998,16 @@ void CoreAudioCapture::captureSamples(std::byte *buffer, uint samples)
     }
     }
 
 
     auto rec_vec = mRing->getReadVector();
     auto rec_vec = mRing->getReadVector();
-    const void *src0{rec_vec.first.buf};
-    auto src0len = static_cast<uint>(rec_vec.first.len);
+    const void *src0{rec_vec[0].buf};
+    auto src0len = static_cast<uint>(rec_vec[0].len);
     uint got{mConverter->convert(&src0, &src0len, buffer, samples)};
     uint got{mConverter->convert(&src0, &src0len, buffer, samples)};
-    size_t total_read{rec_vec.first.len - src0len};
-    if(got < samples && !src0len && rec_vec.second.len > 0)
+    size_t total_read{rec_vec[0].len - src0len};
+    if(got < samples && !src0len && rec_vec[1].len > 0)
     {
     {
-        const void *src1{rec_vec.second.buf};
-        auto src1len = static_cast<uint>(rec_vec.second.len);
+        const void *src1{rec_vec[1].buf};
+        auto src1len = static_cast<uint>(rec_vec[1].len);
         got += mConverter->convert(&src1, &src1len, buffer + got*mFrameSize, samples-got);
         got += mConverter->convert(&src1, &src1len, buffer + got*mFrameSize, samples-got);
-        total_read += rec_vec.second.len - src1len;
+        total_read += rec_vec[1].len - src1len;
     }
     }
 
 
     mRing->readAdvance(total_read);
     mRing->readAdvance(total_read);
@@ -966,23 +1038,23 @@ bool CoreAudioBackendFactory::init()
 bool CoreAudioBackendFactory::querySupport(BackendType type)
 bool CoreAudioBackendFactory::querySupport(BackendType type)
 { return type == BackendType::Playback || type == BackendType::Capture; }
 { 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
 #if CAN_ENUMERATE
     auto append_name = [&outnames](const DeviceEntry &entry) -> void
     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)
     switch(type)
     {
     {
     case BackendType::Playback:
     case BackendType::Playback:
         EnumerateDevices(PlaybackList, false);
         EnumerateDevices(PlaybackList, false);
+        outnames.reserve(PlaybackList.size());
         std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
         std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
         break;
         break;
     case BackendType::Capture:
     case BackendType::Capture:
         EnumerateDevices(CaptureList, true);
         EnumerateDevices(CaptureList, true);
+        outnames.reserve(CaptureList.size());
         std::for_each(CaptureList.cbegin(), CaptureList.cend(), append_name);
         std::for_each(CaptureList.cbegin(), CaptureList.cend(), append_name);
         break;
         break;
     }
     }
@@ -993,8 +1065,7 @@ std::string CoreAudioBackendFactory::probe(BackendType type)
     {
     {
     case BackendType::Playback:
     case BackendType::Playback:
     case BackendType::Capture:
     case BackendType::Capture:
-        /* Includes null char. */
-        outnames.append(ca_device, sizeof(ca_device));
+        outnames.emplace_back(ca_device);
         break;
         break;
     }
     }
 #endif
 #endif

+ 6 - 6
libs/openal-soft/alc/backends/coreaudio.h

@@ -5,17 +5,17 @@
 
 
 struct CoreAudioBackendFactory final : public BackendFactory {
 struct CoreAudioBackendFactory final : public BackendFactory {
 public:
 public:
-    bool init() override;
+    auto init() -> bool final;
 
 
-    bool querySupport(BackendType type) override;
+    auto querySupport(BackendType type) -> bool final;
 
 
-    alc::EventSupport queryEventSupport(alc::EventType eventType, 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_COREAUDIO_H */
 #endif /* BACKENDS_COREAUDIO_H */

+ 116 - 130
libs/openal-soft/alc/backends/dsound.cpp

@@ -37,23 +37,20 @@
 #include <cassert>
 #include <cassert>
 #include <cstdio>
 #include <cstdio>
 #include <cstdlib>
 #include <cstdlib>
-#include <functional>
 #include <memory.h>
 #include <memory.h>
-#include <mutex>
 #include <string>
 #include <string>
 #include <thread>
 #include <thread>
 #include <vector>
 #include <vector>
 
 
-#include "albit.h"
 #include "alnumeric.h"
 #include "alnumeric.h"
 #include "alspan.h"
 #include "alspan.h"
-#include "alstring.h"
 #include "althrd_setname.h"
 #include "althrd_setname.h"
 #include "comptr.h"
 #include "comptr.h"
 #include "core/device.h"
 #include "core/device.h"
 #include "core/helpers.h"
 #include "core/helpers.h"
 #include "core/logging.h"
 #include "core/logging.h"
 #include "dynload.h"
 #include "dynload.h"
+#include "fmt/core.h"
 #include "ringbuffer.h"
 #include "ringbuffer.h"
 #include "strutils.h"
 #include "strutils.h"
 
 
@@ -92,10 +89,7 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0
 
 
 namespace {
 namespace {
 
 
-#define DEVNAME_HEAD "OpenAL Soft on "
-
-
-#ifdef HAVE_DYNLOAD
+#if HAVE_DYNLOAD
 void *ds_handle;
 void *ds_handle;
 HRESULT (WINAPI *pDirectSoundCreate)(const GUID *pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter);
 HRESULT (WINAPI *pDirectSoundCreate)(const GUID *pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter);
 HRESULT (WINAPI *pDirectSoundEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
 HRESULT (WINAPI *pDirectSoundEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
@@ -148,24 +142,19 @@ BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR*, voi
         return TRUE;
         return TRUE;
 
 
     auto& devices = *static_cast<std::vector<DevMap>*>(data);
     auto& devices = *static_cast<std::vector<DevMap>*>(data);
-    const std::string basename{DEVNAME_HEAD + wstr_to_utf8(desc)};
+    const auto basename = wstr_to_utf8(desc);
 
 
-    int count{1};
-    std::string newname{basename};
+    auto count = 1;
+    auto newname = basename;
     while(checkName(devices, newname))
     while(checkName(devices, newname))
-    {
-        newname = basename;
-        newname += " #";
-        newname += std::to_string(++count);
-    }
-    devices.emplace_back(std::move(newname), *guid);
-    const DevMap &newentry = devices.back();
+        newname = fmt::format("{} #{}", basename, ++count);
+    const DevMap &newentry = devices.emplace_back(std::move(newname), *guid);
 
 
     OLECHAR *guidstr{nullptr};
     OLECHAR *guidstr{nullptr};
     HRESULT hr{StringFromCLSID(*guid, &guidstr)};
     HRESULT hr{StringFromCLSID(*guid, &guidstr)};
     if(SUCCEEDED(hr))
     if(SUCCEEDED(hr))
     {
     {
-        TRACE("Got device \"%s\", GUID \"%ls\"\n", newentry.name.c_str(), guidstr);
+        TRACE("Got device \"{}\", GUID \"{}\"", newentry.name, wstr_to_utf8(guidstr));
         CoTaskMemFree(guidstr);
         CoTaskMemFree(guidstr);
     }
     }
 
 
@@ -174,7 +163,7 @@ BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR*, voi
 
 
 
 
 struct DSoundPlayback final : public BackendBase {
 struct DSoundPlayback final : public BackendBase {
-    DSoundPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit DSoundPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
     ~DSoundPlayback() override;
     ~DSoundPlayback() override;
 
 
     int mixerProc();
     int mixerProc();
@@ -217,14 +206,15 @@ FORCE_ALIGN int DSoundPlayback::mixerProc()
     HRESULT err{mBuffer->GetCaps(&DSBCaps)};
     HRESULT err{mBuffer->GetCaps(&DSBCaps)};
     if(FAILED(err))
     if(FAILED(err))
     {
     {
-        ERR("Failed to get buffer caps: 0x%lx\n", err);
-        mDevice->handleDisconnect("Failure retrieving playback buffer info: 0x%lx", err);
+        ERR("Failed to get buffer caps: {:#x}", as_unsigned(err));
+        mDevice->handleDisconnect("Failure retrieving playback buffer info: {:#x}",
+            as_unsigned(err));
         return 1;
         return 1;
     }
     }
 
 
     const size_t FrameStep{mDevice->channelsFromFmt()};
     const size_t FrameStep{mDevice->channelsFromFmt()};
     uint FrameSize{mDevice->frameSizeFromFmt()};
     uint FrameSize{mDevice->frameSizeFromFmt()};
-    DWORD FragSize{mDevice->UpdateSize * FrameSize};
+    DWORD FragSize{mDevice->mUpdateSize * FrameSize};
 
 
     bool Playing{false};
     bool Playing{false};
     DWORD LastCursor{0u};
     DWORD LastCursor{0u};
@@ -244,8 +234,9 @@ FORCE_ALIGN int DSoundPlayback::mixerProc()
                 err = mBuffer->Play(0, 0, DSBPLAY_LOOPING);
                 err = mBuffer->Play(0, 0, DSBPLAY_LOOPING);
                 if(FAILED(err))
                 if(FAILED(err))
                 {
                 {
-                    ERR("Failed to play buffer: 0x%lx\n", err);
-                    mDevice->handleDisconnect("Failure starting playback: 0x%lx", err);
+                    ERR("Failed to play buffer: {:#x}", as_unsigned(err));
+                    mDevice->handleDisconnect("Failure starting playback: {:#x}",
+                        as_unsigned(err));
                     return 1;
                     return 1;
                 }
                 }
                 Playing = true;
                 Playing = true;
@@ -253,7 +244,7 @@ FORCE_ALIGN int DSoundPlayback::mixerProc()
 
 
             avail = WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE);
             avail = WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE);
             if(avail != WAIT_OBJECT_0)
             if(avail != WAIT_OBJECT_0)
-                ERR("WaitForSingleObjectEx error: 0x%lx\n", avail);
+                ERR("WaitForSingleObjectEx error: {:#x}", avail);
             continue;
             continue;
         }
         }
         avail -= avail%FragSize;
         avail -= avail%FragSize;
@@ -266,7 +257,7 @@ FORCE_ALIGN int DSoundPlayback::mixerProc()
         // If the buffer is lost, restore it and lock
         // If the buffer is lost, restore it and lock
         if(err == DSERR_BUFFERLOST)
         if(err == DSERR_BUFFERLOST)
         {
         {
-            WARN("Buffer lost, restoring...\n");
+            WARN("Buffer lost, restoring...");
             err = mBuffer->Restore();
             err = mBuffer->Restore();
             if(SUCCEEDED(err))
             if(SUCCEEDED(err))
             {
             {
@@ -276,22 +267,19 @@ FORCE_ALIGN int DSoundPlayback::mixerProc()
                                     &WritePtr2, &WriteCnt2, 0);
                                     &WritePtr2, &WriteCnt2, 0);
             }
             }
         }
         }
-
-        if(SUCCEEDED(err))
+        if(FAILED(err))
         {
         {
-            mDevice->renderSamples(WritePtr1, WriteCnt1/FrameSize, FrameStep);
-            if(WriteCnt2 > 0)
-                mDevice->renderSamples(WritePtr2, WriteCnt2/FrameSize, FrameStep);
-
-            mBuffer->Unlock(WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
-        }
-        else
-        {
-            ERR("Buffer lock error: %#lx\n", err);
-            mDevice->handleDisconnect("Failed to lock output buffer: 0x%lx", err);
+            ERR("Buffer lock error: {:#x}", as_unsigned(err));
+            mDevice->handleDisconnect("Failed to lock output buffer: {:#x}", as_unsigned(err));
             return 1;
             return 1;
         }
         }
 
 
+        mDevice->renderSamples(WritePtr1, WriteCnt1/FrameSize, FrameStep);
+        if(WriteCnt2 > 0)
+            mDevice->renderSamples(WritePtr2, WriteCnt2/FrameSize, FrameStep);
+
+        mBuffer->Unlock(WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
+
         // Update old write cursor location
         // Update old write cursor location
         LastCursor += WriteCnt1+WriteCnt2;
         LastCursor += WriteCnt1+WriteCnt2;
         LastCursor %= DSBCaps.dwBufferBytes;
         LastCursor %= DSBCaps.dwBufferBytes;
@@ -309,7 +297,7 @@ void DSoundPlayback::open(std::string_view name)
         ComWrapper com{};
         ComWrapper com{};
         hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
         hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
         if(FAILED(hr))
         if(FAILED(hr))
-            ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
+            ERR("Error enumerating DirectSound devices: {:#x}", as_unsigned(hr));
     }
     }
 
 
     const GUID *guid{nullptr};
     const GUID *guid{nullptr};
@@ -331,7 +319,7 @@ void DSoundPlayback::open(std::string_view name)
                     [&id](const DevMap &entry) -> bool { return entry.guid == id; });
                     [&id](const DevMap &entry) -> bool { return entry.guid == id; });
             if(iter == PlaybackDevices.cend())
             if(iter == PlaybackDevices.cend())
                 throw al::backend_exception{al::backend_error::NoDevice,
                 throw al::backend_exception{al::backend_error::NoDevice,
-                    "Device name \"%.*s\" not found", al::sizei(name), name.data()};
+                    "Device name \"{}\" not found", name};
         }
         }
         guid = &iter->guid;
         guid = &iter->guid;
     }
     }
@@ -350,15 +338,15 @@ void DSoundPlayback::open(std::string_view name)
     if(SUCCEEDED(hr))
     if(SUCCEEDED(hr))
         hr = ds->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
         hr = ds->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
     if(FAILED(hr))
     if(FAILED(hr))
-        throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx",
-            hr};
+        throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: {:#x}",
+            as_unsigned(hr)};
 
 
     mNotifies = nullptr;
     mNotifies = nullptr;
     mBuffer = nullptr;
     mBuffer = nullptr;
     mPrimaryBuffer = nullptr;
     mPrimaryBuffer = nullptr;
     mDS = std::move(ds);
     mDS = std::move(ds);
 
 
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 }
 }
 
 
 bool DSoundPlayback::reset()
 bool DSoundPlayback::reset()
@@ -393,7 +381,7 @@ bool DSoundPlayback::reset()
     HRESULT hr{mDS->GetSpeakerConfig(&speakers)};
     HRESULT hr{mDS->GetSpeakerConfig(&speakers)};
     if(FAILED(hr))
     if(FAILED(hr))
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to get speaker config: 0x%08lx", hr};
+            "Failed to get speaker config: {:#x}", as_unsigned(hr)};
 
 
     speakers = DSSPEAKER_CONFIG(speakers);
     speakers = DSSPEAKER_CONFIG(speakers);
     if(!mDevice->Flags.test(ChannelsRequest))
     if(!mDevice->Flags.test(ChannelsRequest))
@@ -409,7 +397,7 @@ bool DSoundPlayback::reset()
         else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND)
         else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND)
             mDevice->FmtChans = DevFmtX71;
             mDevice->FmtChans = DevFmtX71;
         else
         else
-            ERR("Unknown system speaker config: 0x%lx\n", speakers);
+            ERR("Unknown system speaker config: {:#x}", speakers);
     }
     }
     mDevice->Flags.set(DirectEar, (speakers == DSSPEAKER_HEADPHONE));
     mDevice->Flags.set(DirectEar, (speakers == DSSPEAKER_HEADPHONE));
     const bool isRear51{speakers == DSSPEAKER_5POINT1_BACK};
     const bool isRear51{speakers == DSSPEAKER_5POINT1_BACK};
@@ -424,81 +412,83 @@ bool DSoundPlayback::reset()
     case DevFmtX51: OutputType.dwChannelMask = isRear51 ? X5DOT1REAR : X5DOT1; break;
     case DevFmtX51: OutputType.dwChannelMask = isRear51 ? X5DOT1REAR : X5DOT1; break;
     case DevFmtX61: OutputType.dwChannelMask = X6DOT1; break;
     case DevFmtX61: OutputType.dwChannelMask = X6DOT1; break;
     case DevFmtX71: OutputType.dwChannelMask = X7DOT1; break;
     case DevFmtX71: OutputType.dwChannelMask = X7DOT1; break;
+    case DevFmtX7144: mDevice->FmtChans = DevFmtX714;
+        /* fall-through */
     case DevFmtX714: OutputType.dwChannelMask = X7DOT1DOT4; break;
     case DevFmtX714: OutputType.dwChannelMask = X7DOT1DOT4; break;
     case DevFmtX3D71: OutputType.dwChannelMask = X7DOT1; 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->mSampleRate;
+        OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
+            OutputType.Format.nBlockAlign;
+        OutputType.Format.cbSize = 0;
 
 
-        mPrimaryBuffer = nullptr;
-    }
-    else
-    {
-        if(SUCCEEDED(hr) && !mPrimaryBuffer)
+        if(OutputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat)
         {
         {
-            DSBUFFERDESC DSBDescription{};
-            DSBDescription.dwSize = sizeof(DSBDescription);
-            DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
-            hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mPrimaryBuffer), nullptr);
+            OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+            /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */
+            OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
+            OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
+            if(mDevice->FmtType == DevFmtFloat)
+                OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+            else
+                OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+
+            mPrimaryBuffer = nullptr;
+        }
+        else
+        {
+            if(SUCCEEDED(hr) && !mPrimaryBuffer)
+            {
+                DSBUFFERDESC DSBDescription{};
+                DSBDescription.dwSize = sizeof(DSBDescription);
+                DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
+                hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mPrimaryBuffer), nullptr);
+            }
+            if(SUCCEEDED(hr))
+                hr = mPrimaryBuffer->SetFormat(&OutputType.Format);
         }
         }
-        if(SUCCEEDED(hr))
-            hr = mPrimaryBuffer->SetFormat(&OutputType.Format);
-    }
 
 
-    if(SUCCEEDED(hr))
-    {
-        uint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
+        if(FAILED(hr))
+            break;
+
+        uint num_updates{mDevice->mBufferSize / mDevice->mUpdateSize};
         if(num_updates > MAX_UPDATES)
         if(num_updates > MAX_UPDATES)
             num_updates = MAX_UPDATES;
             num_updates = MAX_UPDATES;
-        mDevice->BufferSize = mDevice->UpdateSize * num_updates;
+        mDevice->mBufferSize = mDevice->mUpdateSize * num_updates;
 
 
         DSBUFFERDESC DSBDescription{};
         DSBUFFERDESC DSBDescription{};
         DSBDescription.dwSize = sizeof(DSBDescription);
         DSBDescription.dwSize = sizeof(DSBDescription);
         DSBDescription.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2
         DSBDescription.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2
             | DSBCAPS_GLOBALFOCUS;
             | DSBCAPS_GLOBALFOCUS;
-        DSBDescription.dwBufferBytes = mDevice->BufferSize * OutputType.Format.nBlockAlign;
+        DSBDescription.dwBufferBytes = mDevice->mBufferSize * OutputType.Format.nBlockAlign;
         DSBDescription.lpwfxFormat = &OutputType.Format;
         DSBDescription.lpwfxFormat = &OutputType.Format;
 
 
         hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mBuffer), nullptr);
         hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mBuffer), nullptr);
-        if(FAILED(hr) && mDevice->FmtType == DevFmtFloat)
-        {
-            mDevice->FmtType = DevFmtShort;
-            goto retry_open;
-        }
-    }
+        if(SUCCEEDED(hr) || mDevice->FmtType != DevFmtFloat)
+            break;
+        mDevice->FmtType = DevFmtShort;
+    } while(FAILED(hr));
 
 
     if(SUCCEEDED(hr))
     if(SUCCEEDED(hr))
     {
     {
         hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, al::out_ptr(mNotifies));
         hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, al::out_ptr(mNotifies));
         if(SUCCEEDED(hr))
         if(SUCCEEDED(hr))
         {
         {
-            uint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
+            uint num_updates{mDevice->mBufferSize / mDevice->mUpdateSize};
             assert(num_updates <= MAX_UPDATES);
             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)
             for(uint i{0};i < num_updates;++i)
             {
             {
-                nots[i].dwOffset = i * mDevice->UpdateSize * OutputType.Format.nBlockAlign;
+                nots[i].dwOffset = i * mDevice->mUpdateSize * OutputType.Format.nBlockAlign;
                 nots[i].hEventNotify = mNotifyEvent;
                 nots[i].hEventNotify = mNotifyEvent;
             }
             }
             if(mNotifies->SetNotificationPositions(num_updates, nots.data()) != DS_OK)
             if(mNotifies->SetNotificationPositions(num_updates, nots.data()) != DS_OK)
@@ -524,11 +514,11 @@ void DSoundPlayback::start()
 {
 {
     try {
     try {
         mKillNow.store(false, std::memory_order_release);
         mKillNow.store(false, std::memory_order_release);
-        mThread = std::thread{std::mem_fn(&DSoundPlayback::mixerProc), this};
+        mThread = std::thread{&DSoundPlayback::mixerProc, this};
     }
     }
     catch(std::exception& e) {
     catch(std::exception& e) {
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to start mixing thread: %s", e.what()};
+            "Failed to start mixing thread: {}", e.what()};
     }
     }
 }
 }
 
 
@@ -543,7 +533,7 @@ void DSoundPlayback::stop()
 
 
 
 
 struct DSoundCapture final : public BackendBase {
 struct DSoundCapture final : public BackendBase {
-    DSoundCapture(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit DSoundCapture(DeviceBase *device) noexcept : BackendBase{device} { }
     ~DSoundCapture() override;
     ~DSoundCapture() override;
 
 
     void open(std::string_view name) override;
     void open(std::string_view name) override;
@@ -580,7 +570,7 @@ void DSoundCapture::open(std::string_view name)
         ComWrapper com{};
         ComWrapper com{};
         hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
         hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
         if(FAILED(hr))
         if(FAILED(hr))
-            ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
+            ERR("Error enumerating DirectSound devices: {:#x}", as_unsigned(hr));
     }
     }
 
 
     const GUID *guid{nullptr};
     const GUID *guid{nullptr};
@@ -602,7 +592,7 @@ void DSoundCapture::open(std::string_view name)
                     [&id](const DevMap &entry) -> bool { return entry.guid == id; });
                     [&id](const DevMap &entry) -> bool { return entry.guid == id; });
             if(iter == CaptureDevices.cend())
             if(iter == CaptureDevices.cend())
                 throw al::backend_exception{al::backend_error::NoDevice,
                 throw al::backend_exception{al::backend_error::NoDevice,
-                    "Device name \"%.*s\" not found", al::sizei(name), name.data()};
+                    "Device name \"{}\" not found", name};
         }
         }
         guid = &iter->guid;
         guid = &iter->guid;
     }
     }
@@ -612,9 +602,9 @@ void DSoundCapture::open(std::string_view name)
     case DevFmtByte:
     case DevFmtByte:
     case DevFmtUShort:
     case DevFmtUShort:
     case DevFmtUInt:
     case DevFmtUInt:
-        WARN("%s capture samples not supported\n", DevFmtTypeString(mDevice->FmtType));
+        WARN("{} capture samples not supported", DevFmtTypeString(mDevice->FmtType));
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "%s capture samples not supported", DevFmtTypeString(mDevice->FmtType)};
+            "{} capture samples not supported", DevFmtTypeString(mDevice->FmtType)};
 
 
     case DevFmtUByte:
     case DevFmtUByte:
     case DevFmtShort:
     case DevFmtShort:
@@ -633,10 +623,11 @@ void DSoundCapture::open(std::string_view name)
     case DevFmtX61: InputType.dwChannelMask = X6DOT1; break;
     case DevFmtX61: InputType.dwChannelMask = X6DOT1; break;
     case DevFmtX71: InputType.dwChannelMask = X7DOT1; break;
     case DevFmtX71: InputType.dwChannelMask = X7DOT1; break;
     case DevFmtX714: InputType.dwChannelMask = X7DOT1DOT4; break;
     case DevFmtX714: InputType.dwChannelMask = X7DOT1DOT4; break;
+    case DevFmtX7144:
     case DevFmtX3D71:
     case DevFmtX3D71:
     case DevFmtAmbi3D:
     case DevFmtAmbi3D:
-        WARN("%s capture not supported\n", DevFmtChannelsString(mDevice->FmtChans));
-        throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported",
+        WARN("{} capture not supported", DevFmtChannelsString(mDevice->FmtChans));
+        throw al::backend_exception{al::backend_error::DeviceError, "{} capture not supported",
             DevFmtChannelsString(mDevice->FmtChans)};
             DevFmtChannelsString(mDevice->FmtChans)};
     }
     }
 
 
@@ -645,10 +636,11 @@ void DSoundCapture::open(std::string_view name)
     InputType.Format.wBitsPerSample = static_cast<WORD>(mDevice->bytesFromFmt() * 8);
     InputType.Format.wBitsPerSample = static_cast<WORD>(mDevice->bytesFromFmt() * 8);
     InputType.Format.nBlockAlign = static_cast<WORD>(InputType.Format.nChannels *
     InputType.Format.nBlockAlign = static_cast<WORD>(InputType.Format.nChannels *
         InputType.Format.wBitsPerSample / 8);
         InputType.Format.wBitsPerSample / 8);
-    InputType.Format.nSamplesPerSec = mDevice->Frequency;
+    InputType.Format.nSamplesPerSec = mDevice->mSampleRate;
     InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec *
     InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec *
         InputType.Format.nBlockAlign;
         InputType.Format.nBlockAlign;
     InputType.Format.cbSize = 0;
     InputType.Format.cbSize = 0;
+    /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */
     InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
     InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
     if(mDevice->FmtType == DevFmtFloat)
     if(mDevice->FmtType == DevFmtFloat)
         InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
         InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
@@ -661,7 +653,7 @@ void DSoundCapture::open(std::string_view name)
         InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
         InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
     }
     }
 
 
-    const uint samples{std::max(mDevice->BufferSize, mDevice->Frequency/10u)};
+    const uint samples{std::max(mDevice->mBufferSize, mDevice->mSampleRate/10u)};
 
 
     DSCBUFFERDESC DSCBDescription{};
     DSCBUFFERDESC DSCBDescription{};
     DSCBDescription.dwSize = sizeof(DSCBDescription);
     DSCBDescription.dwSize = sizeof(DSCBDescription);
@@ -674,7 +666,7 @@ void DSoundCapture::open(std::string_view name)
     if(SUCCEEDED(hr))
     if(SUCCEEDED(hr))
         mDSC->CreateCaptureBuffer(&DSCBDescription, al::out_ptr(mDSCbuffer), nullptr);
         mDSC->CreateCaptureBuffer(&DSCBDescription, al::out_ptr(mDSCbuffer), nullptr);
     if(SUCCEEDED(hr))
     if(SUCCEEDED(hr))
-         mRing = RingBuffer::Create(mDevice->BufferSize, InputType.Format.nBlockAlign, false);
+         mRing = RingBuffer::Create(mDevice->mBufferSize, InputType.Format.nBlockAlign, false);
 
 
     if(FAILED(hr))
     if(FAILED(hr))
     {
     {
@@ -682,14 +674,14 @@ void DSoundCapture::open(std::string_view name)
         mDSCbuffer = nullptr;
         mDSCbuffer = nullptr;
         mDSC = nullptr;
         mDSC = nullptr;
 
 
-        throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx",
-            hr};
+        throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: {:#x}",
+            as_unsigned(hr)};
     }
     }
 
 
     mBufferBytes = DSCBDescription.dwBufferBytes;
     mBufferBytes = DSCBDescription.dwBufferBytes;
     setDefaultWFXChannelOrder();
     setDefaultWFXChannelOrder();
 
 
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 }
 }
 
 
 void DSoundCapture::start()
 void DSoundCapture::start()
@@ -697,7 +689,7 @@ void DSoundCapture::start()
     const HRESULT hr{mDSCbuffer->Start(DSCBSTART_LOOPING)};
     const HRESULT hr{mDSCbuffer->Start(DSCBSTART_LOOPING)};
     if(FAILED(hr))
     if(FAILED(hr))
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failure starting capture: 0x%lx", hr};
+            "Failure starting capture: {:#x}", as_unsigned(hr)};
 }
 }
 
 
 void DSoundCapture::stop()
 void DSoundCapture::stop()
@@ -705,8 +697,8 @@ void DSoundCapture::stop()
     HRESULT hr{mDSCbuffer->Stop()};
     HRESULT hr{mDSCbuffer->Stop()};
     if(FAILED(hr))
     if(FAILED(hr))
     {
     {
-        ERR("stop failed: 0x%08lx\n", hr);
-        mDevice->handleDisconnect("Failure stopping capture: 0x%lx", hr);
+        ERR("stop failed: {:#x}", as_unsigned(hr));
+        mDevice->handleDisconnect("Failure stopping capture: {:#x}", as_unsigned(hr));
     }
     }
 }
 }
 
 
@@ -743,8 +735,8 @@ uint DSoundCapture::availableSamples()
 
 
     if(FAILED(hr))
     if(FAILED(hr))
     {
     {
-        ERR("update failed: 0x%08lx\n", hr);
-        mDevice->handleDisconnect("Failure retrieving capture data: 0x%lx", hr);
+        ERR("update failed: {:#x}", as_unsigned(hr));
+        mDevice->handleDisconnect("Failure retrieving capture data: {:#x}", as_unsigned(hr));
     }
     }
 
 
     return static_cast<uint>(mRing->readSpace());
     return static_cast<uint>(mRing->readSpace());
@@ -761,13 +753,13 @@ BackendFactory &DSoundBackendFactory::getFactory()
 
 
 bool DSoundBackendFactory::init()
 bool DSoundBackendFactory::init()
 {
 {
-#ifdef HAVE_DYNLOAD
+#if HAVE_DYNLOAD
     if(!ds_handle)
     if(!ds_handle)
     {
     {
         ds_handle = LoadLib("dsound.dll");
         ds_handle = LoadLib("dsound.dll");
         if(!ds_handle)
         if(!ds_handle)
         {
         {
-            ERR("Failed to load dsound.dll\n");
+            ERR("Failed to load dsound.dll");
             return false;
             return false;
         }
         }
 
 
@@ -793,35 +785,29 @@ bool DSoundBackendFactory::init()
 bool DSoundBackendFactory::querySupport(BackendType type)
 bool DSoundBackendFactory::querySupport(BackendType type)
 { return (type == BackendType::Playback || type == BackendType::Capture); }
 { 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
     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 */
     /* Initialize COM to prevent name truncation */
     ComWrapper com{};
     ComWrapper com{};
-    HRESULT hr;
     switch(type)
     switch(type)
     {
     {
     case BackendType::Playback:
     case BackendType::Playback:
         PlaybackDevices.clear();
         PlaybackDevices.clear();
-        hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
-        if(FAILED(hr))
-            ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr);
+        if(HRESULT hr{DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices)}; FAILED(hr))
+            ERR("Error enumerating DirectSound playback devices: {:#x}", as_unsigned(hr));
+        outnames.reserve(PlaybackDevices.size());
         std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
         std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
         break;
         break;
 
 
     case BackendType::Capture:
     case BackendType::Capture:
         CaptureDevices.clear();
         CaptureDevices.clear();
-        hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
-        if(FAILED(hr))
-            ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr);
+        if(HRESULT hr{DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices)};FAILED(hr))
+            ERR("Error enumerating DirectSound capture devices: {:#x}", as_unsigned(hr));
+        outnames.reserve(CaptureDevices.size());
         std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
         std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
         break;
         break;
     }
     }

+ 5 - 5
libs/openal-soft/alc/backends/dsound.h

@@ -5,15 +5,15 @@
 
 
 struct DSoundBackendFactory final : public BackendFactory {
 struct DSoundBackendFactory final : public BackendFactory {
 public:
 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 */
 #endif /* BACKENDS_DSOUND_H */

+ 132 - 107
libs/openal-soft/alc/backends/jack.cpp

@@ -29,19 +29,17 @@
 #include <memory.h>
 #include <memory.h>
 #include <mutex>
 #include <mutex>
 #include <thread>
 #include <thread>
-#include <functional>
 #include <vector>
 #include <vector>
 
 
-#include "albit.h"
 #include "alc/alconfig.h"
 #include "alc/alconfig.h"
 #include "alnumeric.h"
 #include "alnumeric.h"
 #include "alsem.h"
 #include "alsem.h"
-#include "alstring.h"
 #include "althrd_setname.h"
 #include "althrd_setname.h"
 #include "core/device.h"
 #include "core/device.h"
 #include "core/helpers.h"
 #include "core/helpers.h"
 #include "core/logging.h"
 #include "core/logging.h"
 #include "dynload.h"
 #include "dynload.h"
+#include "fmt/format.h"
 #include "ringbuffer.h"
 #include "ringbuffer.h"
 
 
 #include <jack/jack.h>
 #include <jack/jack.h>
@@ -52,7 +50,7 @@ namespace {
 
 
 using namespace std::string_view_literals;
 using namespace std::string_view_literals;
 
 
-#ifdef HAVE_DYNLOAD
+#if HAVE_DYNLOAD
 #define JACK_FUNCS(MAGIC)          \
 #define JACK_FUNCS(MAGIC)          \
     MAGIC(jack_client_open);       \
     MAGIC(jack_client_open);       \
     MAGIC(jack_client_close);      \
     MAGIC(jack_client_close);      \
@@ -109,10 +107,12 @@ jack_options_t ClientOptions = JackNullOption;
 
 
 bool jack_load()
 bool jack_load()
 {
 {
-#ifdef HAVE_DYNLOAD
+#if HAVE_DYNLOAD
     if(!jack_handle)
     if(!jack_handle)
     {
     {
-#ifdef _WIN32
+#if defined(_WIN64)
+#define JACKLIB "libjack64.dll"
+#elif defined(_WIN32)
 #define JACKLIB "libjack.dll"
 #define JACKLIB "libjack.dll"
 #else
 #else
 #define JACKLIB "libjack.so.0"
 #define JACKLIB "libjack.so.0"
@@ -120,7 +120,7 @@ bool jack_load()
         jack_handle = LoadLib(JACKLIB);
         jack_handle = LoadLib(JACKLIB);
         if(!jack_handle)
         if(!jack_handle)
         {
         {
-            WARN("Failed to load %s\n", JACKLIB);
+            WARN("Failed to load {}", JACKLIB);
             return false;
             return false;
         }
         }
 
 
@@ -138,7 +138,7 @@ bool jack_load()
 
 
         if(!missing_funcs.empty())
         if(!missing_funcs.empty())
         {
         {
-            WARN("Missing expected functions:%s\n", missing_funcs.c_str());
+            WARN("Missing expected functions:{}", missing_funcs);
             CloseLib(jack_handle);
             CloseLib(jack_handle);
             jack_handle = nullptr;
             jack_handle = nullptr;
             return false;
             return false;
@@ -159,11 +159,19 @@ struct DeviceEntry {
     std::string mName;
     std::string mName;
     std::string mPattern;
     std::string mPattern;
 
 
+    DeviceEntry() = default;
+    DeviceEntry(const DeviceEntry&) = default;
+    DeviceEntry(DeviceEntry&&) = default;
     template<typename T, typename U>
     template<typename T, typename U>
     DeviceEntry(T&& name, U&& pattern)
     DeviceEntry(T&& name, U&& pattern)
         : mName{std::forward<T>(name)}, mPattern{std::forward<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;
 
 
 std::vector<DeviceEntry> PlaybackList;
 std::vector<DeviceEntry> PlaybackList;
 
 
@@ -181,21 +189,21 @@ void EnumerateDevices(jack_client_t *client, std::vector<DeviceEntry> &list)
             if(seppos == 0 || seppos >= portname.size())
             if(seppos == 0 || seppos >= portname.size())
                 continue;
                 continue;
 
 
-            const std::string_view portdev{ports[i], seppos};
+            const auto portdev = portname.substr(0, seppos);
             auto check_name = [portdev](const DeviceEntry &entry) -> bool
             auto check_name = [portdev](const DeviceEntry &entry) -> bool
             { return entry.mName == portdev; };
             { return entry.mName == portdev; };
             if(std::find_if(list.cbegin(), list.cend(), check_name) != list.cend())
             if(std::find_if(list.cbegin(), list.cend(), check_name) != list.cend())
                 continue;
                 continue;
 
 
-            const auto &entry = list.emplace_back(portdev, std::string{portdev}+":");
-            TRACE("Got device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str());
+            const auto &entry = list.emplace_back(portdev, fmt::format("{}:", portdev));
+            TRACE("Got device: {} = {}", entry.mName, entry.mPattern);
         }
         }
         /* There are ports but couldn't get device names from them. Add a
         /* There are ports but couldn't get device names from them. Add a
          * generic entry.
          * generic entry.
          */
          */
         if(ports[0] && list.empty())
         if(ports[0] && list.empty())
         {
         {
-            WARN("No device names found in available ports, adding a generic name.\n");
+            WARN("No device names found in available ports, adding a generic name.");
             list.emplace_back("JACK"sv, ""sv);
             list.emplace_back("JACK"sv, ""sv);
         }
         }
     }
     }
@@ -208,8 +216,8 @@ void EnumerateDevices(jack_client_t *client, std::vector<DeviceEntry> &list)
             size_t seppos{listopt->find('=', strpos)};
             size_t seppos{listopt->find('=', strpos)};
             if(seppos >= nextpos || seppos == 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: \"{}\"", entry);
                 if(nextpos != std::string::npos) ++nextpos;
                 if(nextpos != std::string::npos) ++nextpos;
                 strpos = nextpos;
                 strpos = nextpos;
                 continue;
                 continue;
@@ -227,14 +235,13 @@ void EnumerateDevices(jack_client_t *client, std::vector<DeviceEntry> &list)
             {
             {
                 /* If so, replace the name with this custom one. */
                 /* If so, replace the name with this custom one. */
                 itemmatch->mName = name;
                 itemmatch->mName = name;
-                TRACE("Customized device name: %s = %s\n", itemmatch->mName.c_str(),
-                    itemmatch->mPattern.c_str());
+                TRACE("Customized device name: {} = {}", itemmatch->mName, itemmatch->mPattern);
             }
             }
             else
             else
             {
             {
                 /* Otherwise, add a new device entry. */
                 /* Otherwise, add a new device entry. */
                 const auto &entry = list.emplace_back(name, pattern);
                 const auto &entry = list.emplace_back(name, pattern);
-                TRACE("Got custom device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str());
+                TRACE("Got custom device: {} = {}", entry.mName, entry.mPattern);
             }
             }
 
 
             if(nextpos != std::string::npos) ++nextpos;
             if(nextpos != std::string::npos) ++nextpos;
@@ -270,7 +277,7 @@ void EnumerateDevices(jack_client_t *client, std::vector<DeviceEntry> &list)
 
 
 
 
 struct JackPlayback final : public BackendBase {
 struct JackPlayback final : public BackendBase {
-    JackPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit JackPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
     ~JackPlayback() override;
     ~JackPlayback() override;
 
 
     int processRt(jack_nframes_t numframes) noexcept;
     int processRt(jack_nframes_t numframes) noexcept;
@@ -322,22 +329,22 @@ JackPlayback::~JackPlayback()
 
 
 int JackPlayback::processRt(jack_nframes_t numframes) noexcept
 int JackPlayback::processRt(jack_nframes_t numframes) noexcept
 {
 {
-    std::array<jack_default_audio_sample_t*,MaxOutputChannels> out;
-    size_t numchans{0};
+    auto outptrs = std::array<void*,MaxOutputChannels>{};
+    auto numchans = size_t{0};
     for(auto port : mPort)
     for(auto port : mPort)
     {
     {
         if(!port || numchans == mDevice->RealOut.Buffer.size())
         if(!port || numchans == mDevice->RealOut.Buffer.size())
             break;
             break;
-        out[numchans++] = static_cast<float*>(jack_port_get_buffer(port, numframes));
+        outptrs[numchans++] = jack_port_get_buffer(port, numframes);
     }
     }
 
 
+    const auto dst = al::span{outptrs}.first(numchans);
     if(mPlaying.load(std::memory_order_acquire)) LIKELY
     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
     else
     {
     {
-        auto clear_buf = [numframes](float *outbuf) -> void
-        { std::fill_n(outbuf, numframes, 0.0f); };
-        std::for_each(out.begin(), out.begin()+numchans, clear_buf);
+        std::for_each(dst.begin(), dst.end(), [numframes](void *outbuf) -> void
+        { std::fill_n(static_cast<float*>(outbuf), numframes, 0.0f); });
     }
     }
 
 
     return 0;
     return 0;
@@ -354,43 +361,38 @@ int JackPlayback::process(jack_nframes_t numframes) noexcept
         out[numchans++] = {static_cast<float*>(jack_port_get_buffer(port, numframes)), 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
     if(mPlaying.load(std::memory_order_acquire)) LIKELY
     {
     {
         auto data = mRing->getReadVector();
         auto data = mRing->getReadVector();
-        jack_nframes_t todo{std::min(numframes, static_cast<jack_nframes_t>(data.first.len))};
-        auto firstin = al::span{reinterpret_cast<const float*>(data.first.buf), todo};
-        for(size_t c{0};c < numchans;++c)
+        const auto update_size = size_t{mDevice->mUpdateSize};
+
+        const auto outlen = size_t{numframes / update_size};
+        const auto len1 = size_t{std::min(data[0].len/update_size, outlen)};
+        const auto len2 = size_t{std::min(data[1].len/update_size, outlen-len1)};
+
+        auto src = al::span{reinterpret_cast<float*>(data[0].buf), update_size*len1*numchans};
+        for(size_t i{0};i < len1;++i)
         {
         {
-            auto in = firstin.cbegin();
-            auto deinterlace_input = [&in,c,numchans]() noexcept -> float
+            for(size_t c{0};c < numchans;++c)
             {
             {
-                const float ret{in[c]};
-                in += ptrdiff_t(numchans);
-                return ret;
-            };
-            std::generate_n(out[c].begin(), todo, deinterlace_input);
-            out[c] = out[c].subspan(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;
         }
         }
-        total += todo;
 
 
-        todo = std::min(numframes-total, static_cast<jack_nframes_t>(data.second.len));
-        if(todo > 0)
+        src = al::span{reinterpret_cast<float*>(data[1].buf), update_size*len2*numchans};
+        for(size_t i{0};i < len2;++i)
         {
         {
-            auto secondin = al::span{reinterpret_cast<const float*>(data.second.buf), todo};
             for(size_t c{0};c < numchans;++c)
             for(size_t c{0};c < numchans;++c)
             {
             {
-                auto in = secondin.cbegin();
-                auto deinterlace_input = [&in,c,numchans]() noexcept -> float
-                {
-                    float ret{in[c]};
-                    in += ptrdiff_t(numchans);
-                    return ret;
-                };
-                std::generate_n(out[c].begin(), todo, deinterlace_input);
-                out[c] = out[c].subspan(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 += todo;
+            total += update_size;
         }
         }
 
 
         mRing->readAdvance(total);
         mRing->readAdvance(total);
@@ -412,29 +414,52 @@ int JackPlayback::mixerProc()
     SetRTPriority();
     SetRTPriority();
     althrd_setname(GetMixerThreadName());
     althrd_setname(GetMixerThreadName());
 
 
-    const size_t frame_step{mDevice->channelsFromFmt()};
+    const auto update_size = uint{mDevice->mUpdateSize};
+    const auto num_channels = size_t{mDevice->channelsFromFmt()};
+    auto outptrs = std::vector<void*>(num_channels);
 
 
     while(!mKillNow.load(std::memory_order_acquire)
     while(!mKillNow.load(std::memory_order_acquire)
         && mDevice->Connected.load(std::memory_order_acquire))
         && mDevice->Connected.load(std::memory_order_acquire))
     {
     {
-        if(mRing->writeSpace() < mDevice->UpdateSize)
+        if(mRing->writeSpace() < update_size)
         {
         {
             mSem.wait();
             mSem.wait();
             continue;
             continue;
         }
         }
 
 
         auto data = mRing->getWriteVector();
         auto data = mRing->getWriteVector();
-        size_t todo{data.first.len + data.second.len};
-        todo -= todo%mDevice->UpdateSize;
-
-        const auto len1 = static_cast<uint>(std::min(data.first.len, todo));
-        const auto len2 = static_cast<uint>(std::min(data.second.len, todo-len1));
+        const auto len1 = size_t{data[0].len / update_size};
+        const auto len2 = size_t{data[1].len / update_size};
 
 
         std::lock_guard<std::mutex> dlock{mMutex};
         std::lock_guard<std::mutex> dlock{mMutex};
-        mDevice->renderSamples(data.first.buf, len1, frame_step);
+        auto buffer = al::span{reinterpret_cast<float*>(data[0].buf), data[0].len*num_channels};
+        auto bufiter = buffer.begin();
+        for(size_t i{0};i < len1;++i)
+        {
+            std::generate_n(outptrs.begin(), outptrs.size(), [&bufiter,update_size]
+            {
+                auto ret = al::to_address(bufiter);
+                bufiter += ptrdiff_t(update_size);
+                return ret;
+            });
+            mDevice->renderSamples(outptrs, update_size);
+        }
         if(len2 > 0)
         if(len2 > 0)
-            mDevice->renderSamples(data.second.buf, len2, frame_step);
-        mRing->writeAdvance(todo);
+        {
+            buffer = al::span{reinterpret_cast<float*>(data[1].buf), data[1].len*num_channels};
+            bufiter = buffer.begin();
+            for(size_t i{0};i < len2;++i)
+            {
+                std::generate_n(outptrs.begin(), outptrs.size(), [&bufiter,update_size]
+                {
+                    auto ret = al::to_address(bufiter);
+                    bufiter += ptrdiff_t(update_size);
+                    return ret;
+                });
+                mDevice->renderSamples(outptrs, update_size);
+            }
+        }
+        mRing->writeAdvance((len1+len2) * update_size);
     }
     }
 
 
     return 0;
     return 0;
@@ -452,13 +477,14 @@ void JackPlayback::open(std::string_view name)
         mClient = jack_client_open(client_name, ClientOptions, &status, nullptr);
         mClient = jack_client_open(client_name, ClientOptions, &status, nullptr);
         if(mClient == nullptr)
         if(mClient == nullptr)
             throw al::backend_exception{al::backend_error::DeviceError,
             throw al::backend_exception{al::backend_error::DeviceError,
-                "Failed to open client connection: 0x%02x", status};
+                "Failed to open client connection: {:#02x}",
+                as_unsigned(al::to_underlying(status))};
         if((status&JackServerStarted))
         if((status&JackServerStarted))
-            TRACE("JACK server started\n");
+            TRACE("JACK server started");
         if((status&JackNameNotUnique))
         if((status&JackNameNotUnique))
         {
         {
             client_name = jack_get_client_name(mClient);
             client_name = jack_get_client_name(mClient);
-            TRACE("Client name not unique, got '%s' instead\n", client_name);
+            TRACE("Client name not unique, got '{}' instead", client_name);
         }
         }
     }
     }
 
 
@@ -477,11 +503,11 @@ void JackPlayback::open(std::string_view name)
         auto iter = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), check_name);
         auto iter = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), check_name);
         if(iter == PlaybackList.cend())
         if(iter == PlaybackList.cend())
             throw al::backend_exception{al::backend_error::NoDevice,
             throw al::backend_exception{al::backend_error::NoDevice,
-                "Device name \"%.*s\" not found", al::sizei(name), name.data()};
+                "Device name \"{}\" not found", name};
         mPortPattern = iter->mPattern;
         mPortPattern = iter->mPattern;
     }
     }
 
 
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 }
 }
 
 
 bool JackPlayback::reset()
 bool JackPlayback::reset()
@@ -491,37 +517,38 @@ bool JackPlayback::reset()
     std::for_each(mPort.begin(), mPort.end(), unregister_port);
     std::for_each(mPort.begin(), mPort.end(), unregister_port);
     mPort.fill(nullptr);
     mPort.fill(nullptr);
 
 
-    mRTMixing = GetConfigValueBool(mDevice->DeviceName, "jack", "rt-mix", true);
+    mRTMixing = GetConfigValueBool(mDevice->mDeviceName, "jack", "rt-mix", true);
     jack_set_process_callback(mClient,
     jack_set_process_callback(mClient,
         mRTMixing ? &JackPlayback::processRtC : &JackPlayback::processC, this);
         mRTMixing ? &JackPlayback::processRtC : &JackPlayback::processC, this);
 
 
     /* Ignore the requested buffer metrics and just keep one JACK-sized buffer
     /* Ignore the requested buffer metrics and just keep one JACK-sized buffer
      * ready for when requested.
      * ready for when requested.
      */
      */
-    mDevice->Frequency = jack_get_sample_rate(mClient);
-    mDevice->UpdateSize = jack_get_buffer_size(mClient);
+    mDevice->mSampleRate = jack_get_sample_rate(mClient);
+    mDevice->mUpdateSize = jack_get_buffer_size(mClient);
     if(mRTMixing)
     if(mRTMixing)
     {
     {
         /* Assume only two periods when directly mixing. Should try to query
         /* Assume only two periods when directly mixing. Should try to query
          * the total port latency when connected.
          * the total port latency when connected.
          */
          */
-        mDevice->BufferSize = mDevice->UpdateSize * 2;
+        mDevice->mBufferSize = mDevice->mUpdateSize * 2;
     }
     }
     else
     else
     {
     {
-        const std::string_view devname{mDevice->DeviceName};
-        uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)};
-        bufsize = std::max(NextPowerOf2(bufsize), mDevice->UpdateSize);
-        mDevice->BufferSize = bufsize + mDevice->UpdateSize;
+        const auto devname = std::string_view{mDevice->mDeviceName};
+        auto bufsize = ConfigValueUInt(devname, "jack", "buffer-size")
+            .value_or(mDevice->mUpdateSize);
+        bufsize = std::max(NextPowerOf2(bufsize), mDevice->mUpdateSize);
+        mDevice->mBufferSize = bufsize + mDevice->mUpdateSize;
     }
     }
 
 
     /* Force 32-bit float output. */
     /* Force 32-bit float output. */
     mDevice->FmtType = DevFmtFloat;
     mDevice->FmtType = DevFmtFloat;
 
 
     int port_num{0};
     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)};
         std::string name{"channel_" + std::to_string(++port_num)};
         *bad_port = jack_port_register(mClient, name.c_str(), JACK_DEFAULT_AUDIO_TYPE,
         *bad_port = jack_port_register(mClient, name.c_str(), JACK_DEFAULT_AUDIO_TYPE,
@@ -529,17 +556,17 @@ bool JackPlayback::reset()
         if(!*bad_port) break;
         if(!*bad_port) break;
         ++bad_port;
         ++bad_port;
     }
     }
-    if(bad_port != ports_end)
+    if(bad_port != ports.end())
     {
     {
-        ERR("Failed to register enough JACK ports for %s output\n",
+        ERR("Failed to register enough JACK ports for {} output",
             DevFmtChannelsString(mDevice->FmtChans));
             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;
             mDevice->FmtChans = DevFmtMono;
         else
         else
         {
         {
-            ports_end = mPort.begin()+2;
+            const auto ports_end = ports.begin()+2;
             while(bad_port != ports_end)
             while(bad_port != ports_end)
             {
             {
                 jack_port_unregister(mClient, *(--bad_port));
                 jack_port_unregister(mClient, *(--bad_port));
@@ -559,7 +586,7 @@ void JackPlayback::start()
     if(jack_activate(mClient))
     if(jack_activate(mClient))
         throw al::backend_exception{al::backend_error::DeviceError, "Failed to activate client"};
         throw al::backend_exception{al::backend_error::DeviceError, "Failed to activate client"};
 
 
-    const std::string_view devname{mDevice->DeviceName};
+    const auto devname = std::string_view{mDevice->mDeviceName};
     if(ConfigValueBool(devname, "jack", "connect-ports").value_or(true))
     if(ConfigValueBool(devname, "jack", "connect-ports").value_or(true))
     {
     {
         JackPortsPtr pnames{jack_get_ports(mClient, mPortPattern.c_str(), JACK_DEFAULT_AUDIO_TYPE,
         JackPortsPtr pnames{jack_get_ports(mClient, mPortPattern.c_str(), JACK_DEFAULT_AUDIO_TYPE,
@@ -574,11 +601,11 @@ void JackPlayback::start()
         {
         {
             if(!pnames[i])
             if(!pnames[i])
             {
             {
-                ERR("No physical playback port for \"%s\"\n", jack_port_name(mPort[i]));
+                ERR("No physical playback port for \"{}\"", jack_port_name(mPort[i]));
                 break;
                 break;
             }
             }
             if(jack_connect(mClient, jack_port_name(mPort[i]), pnames[i]))
             if(jack_connect(mClient, jack_port_name(mPort[i]), pnames[i]))
-                ERR("Failed to connect output port \"%s\" to \"%s\"\n", jack_port_name(mPort[i]),
+                ERR("Failed to connect output port \"{}\" to \"{}\"", jack_port_name(mPort[i]),
                     pnames[i]);
                     pnames[i]);
         }
         }
     }
     }
@@ -587,31 +614,32 @@ void JackPlayback::start()
      * (it won't change again after jack_activate), then allocate the ring
      * (it won't change again after jack_activate), then allocate the ring
      * buffer with the appropriate size.
      * buffer with the appropriate size.
      */
      */
-    mDevice->Frequency = jack_get_sample_rate(mClient);
-    mDevice->UpdateSize = jack_get_buffer_size(mClient);
-    mDevice->BufferSize = mDevice->UpdateSize * 2;
+    mDevice->mSampleRate = jack_get_sample_rate(mClient);
+    mDevice->mUpdateSize = jack_get_buffer_size(mClient);
+    mDevice->mBufferSize = mDevice->mUpdateSize * 2;
 
 
     mRing = nullptr;
     mRing = nullptr;
     if(mRTMixing)
     if(mRTMixing)
         mPlaying.store(true, std::memory_order_release);
         mPlaying.store(true, std::memory_order_release);
     else
     else
     {
     {
-        uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)};
-        bufsize = std::max(NextPowerOf2(bufsize), mDevice->UpdateSize);
-        mDevice->BufferSize = bufsize + mDevice->UpdateSize;
+        uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size")
+            .value_or(mDevice->mUpdateSize)};
+        bufsize = std::max(NextPowerOf2(bufsize), mDevice->mUpdateSize);
+        mDevice->mBufferSize = bufsize + mDevice->mUpdateSize;
 
 
         mRing = RingBuffer::Create(bufsize, mDevice->frameSizeFromFmt(), true);
         mRing = RingBuffer::Create(bufsize, mDevice->frameSizeFromFmt(), true);
 
 
         try {
         try {
             mPlaying.store(true, std::memory_order_release);
             mPlaying.store(true, std::memory_order_release);
             mKillNow.store(false, std::memory_order_release);
             mKillNow.store(false, std::memory_order_release);
-            mThread = std::thread{std::mem_fn(&JackPlayback::mixerProc), this};
+            mThread = std::thread{&JackPlayback::mixerProc, this};
         }
         }
         catch(std::exception& e) {
         catch(std::exception& e) {
             jack_deactivate(mClient);
             jack_deactivate(mClient);
             mPlaying.store(false, std::memory_order_release);
             mPlaying.store(false, std::memory_order_release);
             throw al::backend_exception{al::backend_error::DeviceError,
             throw al::backend_exception{al::backend_error::DeviceError,
-                "Failed to start mixing thread: %s", e.what()};
+                "Failed to start mixing thread: {}", e.what()};
         }
         }
     }
     }
 }
 }
@@ -635,12 +663,11 @@ void JackPlayback::stop()
 
 
 ClockLatency JackPlayback::getClockLatency()
 ClockLatency JackPlayback::getClockLatency()
 {
 {
-    ClockLatency ret;
-
     std::lock_guard<std::mutex> dlock{mMutex};
     std::lock_guard<std::mutex> dlock{mMutex};
+    ClockLatency ret{};
     ret.ClockTime = mDevice->getClockTime();
     ret.ClockTime = mDevice->getClockTime();
-    ret.Latency  = std::chrono::seconds{mRing ? mRing->readSpace() : mDevice->UpdateSize};
-    ret.Latency /= mDevice->Frequency;
+    ret.Latency  = std::chrono::seconds{mRing ? mRing->readSpace() : mDevice->mUpdateSize};
+    ret.Latency /= mDevice->mSampleRate;
 
 
     return ret;
     return ret;
 }
 }
@@ -648,7 +675,7 @@ ClockLatency JackPlayback::getClockLatency()
 
 
 void jack_msg_handler(const char *message)
 void jack_msg_handler(const char *message)
 {
 {
-    WARN("%s\n", message);
+    WARN("{}", message);
 }
 }
 
 
 } // namespace
 } // namespace
@@ -671,9 +698,9 @@ bool JackBackendFactory::init()
     jack_set_error_function(old_error_cb);
     jack_set_error_function(old_error_cb);
     if(!client)
     if(!client)
     {
     {
-        WARN("jack_client_open() failed, 0x%02x\n", status);
+        WARN("jack_client_open() failed, {:#02x}", as_unsigned(al::to_underlying(status)));
         if((status&JackServerFailed) && !(ClientOptions&JackNoStartServer))
         if((status&JackServerFailed) && !(ClientOptions&JackNoStartServer))
-            ERR("Unable to connect to JACK server\n");
+            ERR("Unable to connect to JACK server");
         return false;
         return false;
     }
     }
 
 
@@ -684,14 +711,11 @@ bool JackBackendFactory::init()
 bool JackBackendFactory::querySupport(BackendType type)
 bool JackBackendFactory::querySupport(BackendType type)
 { return (type == BackendType::Playback); }
 { 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
     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 PathNamePair &binname = GetProcBinary();
     const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()};
     const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()};
@@ -705,7 +729,8 @@ std::string JackBackendFactory::probe(BackendType type)
             jack_client_close(client);
             jack_client_close(client);
         }
         }
         else
         else
-            WARN("jack_client_open() failed, 0x%02x\n", status);
+            WARN("jack_client_open() failed, {:#02x}", as_unsigned(al::to_underlying(status)));
+        outnames.reserve(PlaybackList.size());
         std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
         std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
         break;
         break;
     case BackendType::Capture:
     case BackendType::Capture:

+ 5 - 5
libs/openal-soft/alc/backends/jack.h

@@ -5,15 +5,15 @@
 
 
 struct JackBackendFactory final : public BackendFactory {
 struct JackBackendFactory final : public BackendFactory {
 public:
 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 */
 #endif /* BACKENDS_JACK_H */

+ 4 - 4
libs/openal-soft/alc/backends/loopback.cpp

@@ -28,7 +28,7 @@
 namespace {
 namespace {
 
 
 struct LoopbackBackend final : public BackendBase {
 struct LoopbackBackend final : public BackendBase {
-    LoopbackBackend(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit LoopbackBackend(DeviceBase *device) noexcept : BackendBase{device} { }
 
 
     void open(std::string_view name) override;
     void open(std::string_view name) override;
     bool reset() override;
     bool reset() override;
@@ -39,7 +39,7 @@ struct LoopbackBackend final : public BackendBase {
 
 
 void LoopbackBackend::open(std::string_view name)
 void LoopbackBackend::open(std::string_view name)
 {
 {
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 }
 }
 
 
 bool LoopbackBackend::reset()
 bool LoopbackBackend::reset()
@@ -63,8 +63,8 @@ bool LoopbackBackendFactory::init()
 bool LoopbackBackendFactory::querySupport(BackendType)
 bool LoopbackBackendFactory::querySupport(BackendType)
 { return true; }
 { 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)
 BackendPtr LoopbackBackendFactory::createBackend(DeviceBase *device, BackendType)
 { return BackendPtr{new LoopbackBackend{device}}; }
 { return BackendPtr{new LoopbackBackend{device}}; }

+ 5 - 5
libs/openal-soft/alc/backends/loopback.h

@@ -5,15 +5,15 @@
 
 
 struct LoopbackBackendFactory final : public BackendFactory {
 struct LoopbackBackendFactory final : public BackendFactory {
 public:
 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 */
 #endif /* BACKENDS_LOOPBACK_H */

+ 19 - 21
libs/openal-soft/alc/backends/null.cpp

@@ -27,11 +27,8 @@
 #include <chrono>
 #include <chrono>
 #include <cstdint>
 #include <cstdint>
 #include <cstring>
 #include <cstring>
-#include <functional>
 #include <thread>
 #include <thread>
 
 
-#include "almalloc.h"
-#include "alstring.h"
 #include "althrd_setname.h"
 #include "althrd_setname.h"
 #include "core/device.h"
 #include "core/device.h"
 #include "core/helpers.h"
 #include "core/helpers.h"
@@ -48,7 +45,7 @@ using namespace std::string_view_literals;
 
 
 
 
 struct NullBackend final : public BackendBase {
 struct NullBackend final : public BackendBase {
-    NullBackend(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit NullBackend(DeviceBase *device) noexcept : BackendBase{device} { }
 
 
     int mixerProc();
     int mixerProc();
 
 
@@ -63,7 +60,7 @@ struct NullBackend final : public BackendBase {
 
 
 int NullBackend::mixerProc()
 int NullBackend::mixerProc()
 {
 {
-    const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2};
+    const milliseconds restTime{mDevice->mUpdateSize*1000/mDevice->mSampleRate / 2};
 
 
     SetRTPriority();
     SetRTPriority();
     althrd_setname(GetMixerThreadName());
     althrd_setname(GetMixerThreadName());
@@ -76,16 +73,17 @@ int NullBackend::mixerProc()
         auto now = std::chrono::steady_clock::now();
         auto now = std::chrono::steady_clock::now();
 
 
         /* This converts from nanoseconds to nanosamples, then to samples. */
         /* This converts from nanoseconds to nanosamples, then to samples. */
-        int64_t avail{std::chrono::duration_cast<seconds>((now-start) * mDevice->Frequency).count()};
-        if(avail-done < mDevice->UpdateSize)
+        const auto avail = int64_t{std::chrono::duration_cast<seconds>((now-start)
+            * mDevice->mSampleRate).count()};
+        if(avail-done < mDevice->mUpdateSize)
         {
         {
             std::this_thread::sleep_for(restTime);
             std::this_thread::sleep_for(restTime);
             continue;
             continue;
         }
         }
-        while(avail-done >= mDevice->UpdateSize)
+        while(avail-done >= mDevice->mUpdateSize)
         {
         {
-            mDevice->renderSamples(nullptr, mDevice->UpdateSize, 0u);
-            done += mDevice->UpdateSize;
+            mDevice->renderSamples(nullptr, mDevice->mUpdateSize, 0u);
+            done += mDevice->mUpdateSize;
         }
         }
 
 
         /* For every completed second, increment the start time and reduce the
         /* For every completed second, increment the start time and reduce the
@@ -93,11 +91,11 @@ int NullBackend::mixerProc()
          * and current time from growing too large, while maintaining the
          * and current time from growing too large, while maintaining the
          * correct number of samples to render.
          * correct number of samples to render.
          */
          */
-        if(done >= mDevice->Frequency)
+        if(done >= mDevice->mSampleRate)
         {
         {
-            seconds s{done/mDevice->Frequency};
+            seconds s{done/mDevice->mSampleRate};
             start += s;
             start += s;
-            done -= mDevice->Frequency*s.count();
+            done -= mDevice->mSampleRate*s.count();
         }
         }
     }
     }
 
 
@@ -110,10 +108,10 @@ void NullBackend::open(std::string_view name)
     if(name.empty())
     if(name.empty())
         name = GetDeviceName();
         name = GetDeviceName();
     else if(name != GetDeviceName())
     else if(name != GetDeviceName())
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
-            al::sizei(name), name.data()};
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
+            name};
 
 
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 }
 }
 
 
 bool NullBackend::reset()
 bool NullBackend::reset()
@@ -126,11 +124,11 @@ void NullBackend::start()
 {
 {
     try {
     try {
         mKillNow.store(false, std::memory_order_release);
         mKillNow.store(false, std::memory_order_release);
-        mThread = std::thread{std::mem_fn(&NullBackend::mixerProc), this};
+        mThread = std::thread{&NullBackend::mixerProc, this};
     }
     }
     catch(std::exception& e) {
     catch(std::exception& e) {
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to start mixing thread: %s", e.what()};
+            "Failed to start mixing thread: {}", e.what()};
     }
     }
 }
 }
 
 
@@ -150,17 +148,17 @@ bool NullBackendFactory::init()
 bool NullBackendFactory::querySupport(BackendType type)
 bool NullBackendFactory::querySupport(BackendType type)
 { return (type == BackendType::Playback); }
 { return (type == BackendType::Playback); }
 
 
-std::string NullBackendFactory::probe(BackendType type)
+auto NullBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
 {
 {
     switch(type)
     switch(type)
     {
     {
     case BackendType::Playback:
     case BackendType::Playback:
         /* Include null char. */
         /* Include null char. */
-        return std::string{GetDeviceName()} + '\0';
+        return std::vector{std::string{GetDeviceName()}};
     case BackendType::Capture:
     case BackendType::Capture:
         break;
         break;
     }
     }
-    return std::string{};
+    return {};
 }
 }
 
 
 BackendPtr NullBackendFactory::createBackend(DeviceBase *device, BackendType type)
 BackendPtr NullBackendFactory::createBackend(DeviceBase *device, BackendType type)

+ 5 - 5
libs/openal-soft/alc/backends/null.h

@@ -5,15 +5,15 @@
 
 
 struct NullBackendFactory final : public BackendFactory {
 struct NullBackendFactory final : public BackendFactory {
 public:
 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 */
 #endif /* BACKENDS_NULL_H */

+ 35 - 34
libs/openal-soft/alc/backends/oboe.cpp

@@ -24,7 +24,7 @@ using namespace std::string_view_literals;
 
 
 
 
 struct OboePlayback final : public BackendBase, public oboe::AudioStreamCallback {
 struct OboePlayback final : public BackendBase, public oboe::AudioStreamCallback {
-    OboePlayback(DeviceBase *device) : BackendBase{device} { }
+    explicit OboePlayback(DeviceBase *device) : BackendBase{device} { }
 
 
     oboe::ManagedStream mStream;
     oboe::ManagedStream mStream;
 
 
@@ -54,8 +54,9 @@ oboe::DataCallbackResult OboePlayback::onAudioReady(oboe::AudioStream *oboeStrea
 void OboePlayback::onErrorAfterClose(oboe::AudioStream*, oboe::Result error)
 void OboePlayback::onErrorAfterClose(oboe::AudioStream*, oboe::Result error)
 {
 {
     if(error == oboe::Result::ErrorDisconnected)
     if(error == oboe::Result::ErrorDisconnected)
-        mDevice->handleDisconnect("Oboe AudioStream was disconnected: %s", oboe::convertToText(error));
-    TRACE("Error was %s", oboe::convertToText(error));
+        mDevice->handleDisconnect("Oboe AudioStream was disconnected: {}",
+            oboe::convertToText(error));
+    TRACE("Error was {}", oboe::convertToText(error));
 }
 }
 
 
 void OboePlayback::open(std::string_view name)
 void OboePlayback::open(std::string_view name)
@@ -63,8 +64,8 @@ void OboePlayback::open(std::string_view name)
     if(name.empty())
     if(name.empty())
         name = GetDeviceName();
         name = GetDeviceName();
     else if(name != GetDeviceName())
     else if(name != GetDeviceName())
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
-            al::sizei(name), name.data()};
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
+            name};
 
 
     /* Open a basic output stream, just to ensure it can work. */
     /* Open a basic output stream, just to ensure it can work. */
     oboe::ManagedStream stream;
     oboe::ManagedStream stream;
@@ -72,10 +73,10 @@ void OboePlayback::open(std::string_view name)
         ->setPerformanceMode(oboe::PerformanceMode::LowLatency)
         ->setPerformanceMode(oboe::PerformanceMode::LowLatency)
         ->openManagedStream(stream)};
         ->openManagedStream(stream)};
     if(result != oboe::Result::OK)
     if(result != oboe::Result::OK)
-        throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s",
+        throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: {}",
             oboe::convertToText(result)};
             oboe::convertToText(result)};
 
 
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 }
 }
 
 
 bool OboePlayback::reset()
 bool OboePlayback::reset()
@@ -95,7 +96,7 @@ bool OboePlayback::reset()
     if(mDevice->Flags.test(FrequencyRequest))
     if(mDevice->Flags.test(FrequencyRequest))
     {
     {
         builder.setSampleRateConversionQuality(oboe::SampleRateConversionQuality::High);
         builder.setSampleRateConversionQuality(oboe::SampleRateConversionQuality::High);
-        builder.setSampleRate(static_cast<int32_t>(mDevice->Frequency));
+        builder.setSampleRate(static_cast<int32_t>(mDevice->mSampleRate));
     }
     }
     if(mDevice->Flags.test(ChannelsRequest))
     if(mDevice->Flags.test(ChannelsRequest))
     {
     {
@@ -145,11 +146,11 @@ bool OboePlayback::reset()
         result = builder.openManagedStream(mStream);
         result = builder.openManagedStream(mStream);
     }
     }
     if(result != oboe::Result::OK)
     if(result != oboe::Result::OK)
-        throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s",
+        throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: {}",
             oboe::convertToText(result)};
             oboe::convertToText(result)};
-    mStream->setBufferSizeInFrames(std::min(static_cast<int32_t>(mDevice->BufferSize),
+    mStream->setBufferSizeInFrames(std::min(static_cast<int32_t>(mDevice->mBufferSize),
         mStream->getBufferCapacityInFrames()));
         mStream->getBufferCapacityInFrames()));
-    TRACE("Got stream with properties:\n%s", oboe::convertToText(mStream.get()));
+    TRACE("Got stream with properties:\n{}", oboe::convertToText(mStream.get()));
 
 
     if(static_cast<uint>(mStream->getChannelCount()) != mDevice->channelsFromFmt())
     if(static_cast<uint>(mStream->getChannelCount()) != mDevice->channelsFromFmt())
     {
     {
@@ -159,7 +160,7 @@ bool OboePlayback::reset()
             mDevice->FmtChans = DevFmtMono;
             mDevice->FmtChans = DevFmtMono;
         else
         else
             throw al::backend_exception{al::backend_error::DeviceError,
             throw al::backend_exception{al::backend_error::DeviceError,
-                "Got unhandled channel count: %d", mStream->getChannelCount()};
+                "Got unhandled channel count: {}", mStream->getChannelCount()};
     }
     }
     setDefaultWFXChannelOrder();
     setDefaultWFXChannelOrder();
 
 
@@ -183,18 +184,18 @@ bool OboePlayback::reset()
     case oboe::AudioFormat::Unspecified:
     case oboe::AudioFormat::Unspecified:
     case oboe::AudioFormat::Invalid:
     case oboe::AudioFormat::Invalid:
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Got unhandled sample type: %s", oboe::convertToText(mStream->getFormat())};
+            "Got unhandled sample type: {}", oboe::convertToText(mStream->getFormat())};
     }
     }
-    mDevice->Frequency = static_cast<uint32_t>(mStream->getSampleRate());
+    mDevice->mSampleRate = static_cast<uint32_t>(mStream->getSampleRate());
 
 
     /* Ensure the period size is no less than 10ms. It's possible for FramesPerCallback to be 0
     /* Ensure the period size is no less than 10ms. It's possible for FramesPerCallback to be 0
      * indicating variable updates, but OpenAL should have a reasonable minimum update size set.
      * indicating variable updates, but OpenAL should have a reasonable minimum update size set.
      * FramesPerBurst may not necessarily be correct, but hopefully it can act as a minimum
      * FramesPerBurst may not necessarily be correct, but hopefully it can act as a minimum
      * update size.
      * update size.
      */
      */
-    mDevice->UpdateSize = std::max(mDevice->Frequency/100u,
+    mDevice->mUpdateSize = std::max(mDevice->mSampleRate/100u,
         static_cast<uint32_t>(mStream->getFramesPerBurst()));
         static_cast<uint32_t>(mStream->getFramesPerBurst()));
-    mDevice->BufferSize = std::max(mDevice->UpdateSize*2u,
+    mDevice->mBufferSize = std::max(mDevice->mUpdateSize*2u,
         static_cast<uint32_t>(mStream->getBufferSizeInFrames()));
         static_cast<uint32_t>(mStream->getBufferSizeInFrames()));
 
 
     return true;
     return true;
@@ -204,7 +205,7 @@ void OboePlayback::start()
 {
 {
     const oboe::Result result{mStream->start()};
     const oboe::Result result{mStream->start()};
     if(result != oboe::Result::OK)
     if(result != oboe::Result::OK)
-        throw al::backend_exception{al::backend_error::DeviceError, "Failed to start stream: %s",
+        throw al::backend_exception{al::backend_error::DeviceError, "Failed to start stream: {}",
             oboe::convertToText(result)};
             oboe::convertToText(result)};
 }
 }
 
 
@@ -212,12 +213,12 @@ void OboePlayback::stop()
 {
 {
     oboe::Result result{mStream->stop()};
     oboe::Result result{mStream->stop()};
     if(result != oboe::Result::OK)
     if(result != oboe::Result::OK)
-        ERR("Failed to stop stream: %s\n", oboe::convertToText(result));
+        ERR("Failed to stop stream: {}", oboe::convertToText(result));
 }
 }
 
 
 
 
 struct OboeCapture final : public BackendBase, public oboe::AudioStreamCallback {
 struct OboeCapture final : public BackendBase, public oboe::AudioStreamCallback {
-    OboeCapture(DeviceBase *device) : BackendBase{device} { }
+    explicit OboeCapture(DeviceBase *device) : BackendBase{device} { }
 
 
     oboe::ManagedStream mStream;
     oboe::ManagedStream mStream;
 
 
@@ -246,8 +247,8 @@ void OboeCapture::open(std::string_view name)
     if(name.empty())
     if(name.empty())
         name = GetDeviceName();
         name = GetDeviceName();
     else if(name != GetDeviceName())
     else if(name != GetDeviceName())
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
-            al::sizei(name), name.data()};
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
+            name};
 
 
     oboe::AudioStreamBuilder builder;
     oboe::AudioStreamBuilder builder;
     builder.setDirection(oboe::Direction::Input)
     builder.setDirection(oboe::Direction::Input)
@@ -255,7 +256,7 @@ void OboeCapture::open(std::string_view name)
         ->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::High)
         ->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::High)
         ->setChannelConversionAllowed(true)
         ->setChannelConversionAllowed(true)
         ->setFormatConversionAllowed(true)
         ->setFormatConversionAllowed(true)
-        ->setSampleRate(static_cast<int32_t>(mDevice->Frequency))
+        ->setSampleRate(static_cast<int32_t>(mDevice->mSampleRate))
         ->setCallback(this);
         ->setCallback(this);
     /* Only use mono or stereo at user request. There's no telling what
     /* Only use mono or stereo at user request. There's no telling what
      * other counts may be inferred as.
      * other counts may be inferred as.
@@ -273,9 +274,10 @@ void OboeCapture::open(std::string_view name)
     case DevFmtX61:
     case DevFmtX61:
     case DevFmtX71:
     case DevFmtX71:
     case DevFmtX714:
     case DevFmtX714:
+    case DevFmtX7144:
     case DevFmtX3D71:
     case DevFmtX3D71:
     case DevFmtAmbi3D:
     case DevFmtAmbi3D:
-        throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported",
+        throw al::backend_exception{al::backend_error::DeviceError, "{} capture not supported",
             DevFmtChannelsString(mDevice->FmtChans)};
             DevFmtChannelsString(mDevice->FmtChans)};
     }
     }
 
 
@@ -300,28 +302,28 @@ void OboeCapture::open(std::string_view name)
     case DevFmtUShort:
     case DevFmtUShort:
     case DevFmtUInt:
     case DevFmtUInt:
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "%s capture samples not supported", DevFmtTypeString(mDevice->FmtType)};
+            "{} capture samples not supported", DevFmtTypeString(mDevice->FmtType)};
     }
     }
 
 
     oboe::Result result{builder.openManagedStream(mStream)};
     oboe::Result result{builder.openManagedStream(mStream)};
     if(result != oboe::Result::OK)
     if(result != oboe::Result::OK)
-        throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s",
+        throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: {}",
             oboe::convertToText(result)};
             oboe::convertToText(result)};
 
 
-    TRACE("Got stream with properties:\n%s", oboe::convertToText(mStream.get()));
+    TRACE("Got stream with properties:\n{}", oboe::convertToText(mStream.get()));
 
 
     /* Ensure a minimum ringbuffer size of 100ms. */
     /* Ensure a minimum ringbuffer size of 100ms. */
-    mRing = RingBuffer::Create(std::max(mDevice->BufferSize, mDevice->Frequency/10u),
+    mRing = RingBuffer::Create(std::max(mDevice->mBufferSize, mDevice->mSampleRate/10u),
         static_cast<uint32_t>(mStream->getBytesPerFrame()), false);
         static_cast<uint32_t>(mStream->getBytesPerFrame()), false);
 
 
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 }
 }
 
 
 void OboeCapture::start()
 void OboeCapture::start()
 {
 {
     const oboe::Result result{mStream->start()};
     const oboe::Result result{mStream->start()};
     if(result != oboe::Result::OK)
     if(result != oboe::Result::OK)
-        throw al::backend_exception{al::backend_error::DeviceError, "Failed to start stream: %s",
+        throw al::backend_exception{al::backend_error::DeviceError, "Failed to start stream: {}",
             oboe::convertToText(result)};
             oboe::convertToText(result)};
 }
 }
 
 
@@ -329,7 +331,7 @@ void OboeCapture::stop()
 {
 {
     const oboe::Result result{mStream->stop()};
     const oboe::Result result{mStream->stop()};
     if(result != oboe::Result::OK)
     if(result != oboe::Result::OK)
-        ERR("Failed to stop stream: %s\n", oboe::convertToText(result));
+        ERR("Failed to stop stream: {}", oboe::convertToText(result));
 }
 }
 
 
 uint OboeCapture::availableSamples()
 uint OboeCapture::availableSamples()
@@ -345,16 +347,15 @@ bool OboeBackendFactory::init() { return true; }
 bool OboeBackendFactory::querySupport(BackendType type)
 bool OboeBackendFactory::querySupport(BackendType type)
 { return type == BackendType::Playback || type == BackendType::Capture; }
 { return type == BackendType::Playback || type == BackendType::Capture; }
 
 
-std::string OboeBackendFactory::probe(BackendType type)
+auto OboeBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
 {
 {
     switch(type)
     switch(type)
     {
     {
     case BackendType::Playback:
     case BackendType::Playback:
     case BackendType::Capture:
     case BackendType::Capture:
-        /* Include null char. */
-        return std::string{GetDeviceName()} + '\0';
+        return std::vector{std::string{GetDeviceName()}};
     }
     }
-    return std::string{};
+    return {};
 }
 }
 
 
 BackendPtr OboeBackendFactory::createBackend(DeviceBase *device, BackendType type)
 BackendPtr OboeBackendFactory::createBackend(DeviceBase *device, BackendType type)

+ 5 - 5
libs/openal-soft/alc/backends/oboe.h

@@ -5,15 +5,15 @@
 
 
 struct OboeBackendFactory final : public BackendFactory {
 struct OboeBackendFactory final : public BackendFactory {
 public:
 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 */
 #endif /* BACKENDS_OBOE_H */

+ 131 - 72
libs/openal-soft/alc/backends/opensl.cpp

@@ -41,6 +41,7 @@
 #include "core/device.h"
 #include "core/device.h"
 #include "core/helpers.h"
 #include "core/helpers.h"
 #include "core/logging.h"
 #include "core/logging.h"
+#include "dynload.h"
 #include "opthelpers.h"
 #include "opthelpers.h"
 #include "ringbuffer.h"
 #include "ringbuffer.h"
 
 
@@ -53,6 +54,32 @@ namespace {
 
 
 using namespace std::string_view_literals;
 using namespace std::string_view_literals;
 
 
+
+#if HAVE_DYNLOAD
+#define SLES_SYMBOLS(MAGIC)                 \
+    MAGIC(slCreateEngine);                  \
+    MAGIC(SL_IID_ANDROIDCONFIGURATION);     \
+    MAGIC(SL_IID_ANDROIDSIMPLEBUFFERQUEUE); \
+    MAGIC(SL_IID_ENGINE);                   \
+    MAGIC(SL_IID_PLAY);                     \
+    MAGIC(SL_IID_RECORD);
+
+void *sles_handle;
+#define MAKE_SYMBOL(f) decltype(f) * p##f
+SLES_SYMBOLS(MAKE_SYMBOL)
+#undef MAKE_SYMBOL
+
+#ifndef IN_IDE_PARSER
+#define slCreateEngine (*pslCreateEngine)
+#define SL_IID_ANDROIDCONFIGURATION (*pSL_IID_ANDROIDCONFIGURATION)
+#define SL_IID_ANDROIDSIMPLEBUFFERQUEUE (*pSL_IID_ANDROIDSIMPLEBUFFERQUEUE)
+#define SL_IID_ENGINE (*pSL_IID_ENGINE)
+#define SL_IID_PLAY (*pSL_IID_PLAY)
+#define SL_IID_RECORD (*pSL_IID_RECORD)
+#endif
+#endif
+
+
 /* Helper macros */
 /* Helper macros */
 #define EXTRACT_VCALL_ARGS(...)  __VA_ARGS__))
 #define EXTRACT_VCALL_ARGS(...)  __VA_ARGS__))
 #define VCALL(obj, func)  ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
 #define VCALL(obj, func)  ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
@@ -85,6 +112,7 @@ constexpr SLuint32 GetChannelMask(DevFmtChannels chans) noexcept
         SL_SPEAKER_BACK_RIGHT | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT |
         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_FRONT_LEFT | SL_SPEAKER_TOP_FRONT_RIGHT | SL_SPEAKER_TOP_BACK_LEFT |
         SL_SPEAKER_TOP_BACK_RIGHT;
         SL_SPEAKER_TOP_BACK_RIGHT;
+    case DevFmtX7144:
     case DevFmtAmbi3D:
     case DevFmtAmbi3D:
         break;
         break;
     }
     }
@@ -155,12 +183,12 @@ constexpr const char *res_str(SLresult result) noexcept
 inline void PrintErr(SLresult res, const char *str)
 inline void PrintErr(SLresult res, const char *str)
 {
 {
     if(res != SL_RESULT_SUCCESS) UNLIKELY
     if(res != SL_RESULT_SUCCESS) UNLIKELY
-        ERR("%s: %s\n", str, res_str(res));
+        ERR("{}: {}", str, res_str(res));
 }
 }
 
 
 
 
 struct OpenSLPlayback final : public BackendBase {
 struct OpenSLPlayback final : public BackendBase {
-    OpenSLPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit OpenSLPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
     ~OpenSLPlayback() override;
     ~OpenSLPlayback() override;
 
 
     void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
     void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
@@ -246,7 +274,7 @@ int OpenSLPlayback::mixerProc()
     const size_t frame_step{mDevice->channelsFromFmt()};
     const size_t frame_step{mDevice->channelsFromFmt()};
 
 
     if(SL_RESULT_SUCCESS != result)
     if(SL_RESULT_SUCCESS != result)
-        mDevice->handleDisconnect("Failed to get playback buffer: 0x%08x", result);
+        mDevice->handleDisconnect("Failed to get playback buffer: {:#08x}", result);
 
 
     while(SL_RESULT_SUCCESS == result && !mKillNow.load(std::memory_order_acquire)
     while(SL_RESULT_SUCCESS == result && !mKillNow.load(std::memory_order_acquire)
         && mDevice->Connected.load(std::memory_order_acquire))
         && mDevice->Connected.load(std::memory_order_acquire))
@@ -264,7 +292,7 @@ int OpenSLPlayback::mixerProc()
             }
             }
             if(SL_RESULT_SUCCESS != result)
             if(SL_RESULT_SUCCESS != result)
             {
             {
-                mDevice->handleDisconnect("Failed to start playback: 0x%08x", result);
+                mDevice->handleDisconnect("Failed to start playback: {:#08x}", result);
                 break;
                 break;
             }
             }
 
 
@@ -277,35 +305,35 @@ int OpenSLPlayback::mixerProc()
 
 
         std::unique_lock<std::mutex> dlock{mMutex};
         std::unique_lock<std::mutex> dlock{mMutex};
         auto data = mRing->getWriteVector();
         auto data = mRing->getWriteVector();
-        mDevice->renderSamples(data.first.buf,
-            static_cast<uint>(data.first.len)*mDevice->UpdateSize, frame_step);
-        if(data.second.len > 0)
-            mDevice->renderSamples(data.second.buf,
-                static_cast<uint>(data.second.len)*mDevice->UpdateSize, frame_step);
+        mDevice->renderSamples(data[0].buf,
+            static_cast<uint>(data[0].len)*mDevice->mUpdateSize, frame_step);
+        if(data[1].len > 0)
+            mDevice->renderSamples(data[1].buf,
+                static_cast<uint>(data[1].len)*mDevice->mUpdateSize, frame_step);
 
 
-        size_t todo{data.first.len + data.second.len};
+        const auto todo = size_t{data[0].len + data[1].len};
         mRing->writeAdvance(todo);
         mRing->writeAdvance(todo);
         dlock.unlock();
         dlock.unlock();
 
 
         for(size_t i{0};i < todo;i++)
         for(size_t i{0};i < todo;i++)
         {
         {
-            if(!data.first.len)
+            if(!data[0].len)
             {
             {
-                data.first = data.second;
-                data.second.buf = nullptr;
-                data.second.len = 0;
+                data[0] = data[1];
+                data[1].buf = nullptr;
+                data[1].len = 0;
             }
             }
 
 
-            result = VCALL(bufferQueue,Enqueue)(data.first.buf, mDevice->UpdateSize*mFrameSize);
+            result = VCALL(bufferQueue,Enqueue)(data[0].buf, mDevice->mUpdateSize*mFrameSize);
             PrintErr(result, "bufferQueue->Enqueue");
             PrintErr(result, "bufferQueue->Enqueue");
             if(SL_RESULT_SUCCESS != result)
             if(SL_RESULT_SUCCESS != result)
             {
             {
-                mDevice->handleDisconnect("Failed to queue audio: 0x%08x", result);
+                mDevice->handleDisconnect("Failed to queue audio: {:#08x}", result);
                 break;
                 break;
             }
             }
 
 
-            data.first.len--;
-            data.first.buf += mDevice->UpdateSize*mFrameSize;
+            data[0].len--;
+            data[0].buf += mDevice->mUpdateSize*mFrameSize;
         }
         }
     }
     }
 
 
@@ -318,8 +346,8 @@ void OpenSLPlayback::open(std::string_view name)
     if(name.empty())
     if(name.empty())
         name = GetDeviceName();
         name = GetDeviceName();
     else if(name != GetDeviceName())
     else if(name != GetDeviceName())
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
-            al::sizei(name), name.data()};
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
+            name};
 
 
     /* There's only one device, so if it's already open, there's nothing to do. */
     /* There's only one device, so if it's already open, there's nothing to do. */
     if(mEngineObj) return;
     if(mEngineObj) return;
@@ -360,10 +388,10 @@ void OpenSLPlayback::open(std::string_view name)
         mEngine = nullptr;
         mEngine = nullptr;
 
 
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to initialize OpenSL device: 0x%08x", result};
+            "Failed to initialize OpenSL device: {:#08x}", result};
     }
     }
 
 
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 }
 }
 
 
 bool OpenSLPlayback::reset()
 bool OpenSLPlayback::reset()
@@ -396,14 +424,14 @@ bool OpenSLPlayback::reset()
 
 
     SLDataLocator_AndroidSimpleBufferQueue loc_bufq{};
     SLDataLocator_AndroidSimpleBufferQueue loc_bufq{};
     loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
     loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
-    loc_bufq.numBuffers = mDevice->BufferSize / mDevice->UpdateSize;
+    loc_bufq.numBuffers = mDevice->mBufferSize / mDevice->mUpdateSize;
 
 
     SLDataSource audioSrc{};
     SLDataSource audioSrc{};
 #ifdef SL_ANDROID_DATAFORMAT_PCM_EX
 #ifdef SL_ANDROID_DATAFORMAT_PCM_EX
     SLAndroidDataFormat_PCM_EX format_pcm_ex{};
     SLAndroidDataFormat_PCM_EX format_pcm_ex{};
     format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
     format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
     format_pcm_ex.numChannels = mDevice->channelsFromFmt();
     format_pcm_ex.numChannels = mDevice->channelsFromFmt();
-    format_pcm_ex.sampleRate = mDevice->Frequency * 1000;
+    format_pcm_ex.sampleRate = mDevice->mSampleRate * 1000;
     format_pcm_ex.bitsPerSample = mDevice->bytesFromFmt() * 8;
     format_pcm_ex.bitsPerSample = mDevice->bytesFromFmt() * 8;
     format_pcm_ex.containerSize = format_pcm_ex.bitsPerSample;
     format_pcm_ex.containerSize = format_pcm_ex.bitsPerSample;
     format_pcm_ex.channelMask = GetChannelMask(mDevice->FmtChans);
     format_pcm_ex.channelMask = GetChannelMask(mDevice->FmtChans);
@@ -434,7 +462,7 @@ bool OpenSLPlayback::reset()
         SLDataFormat_PCM format_pcm{};
         SLDataFormat_PCM format_pcm{};
         format_pcm.formatType = SL_DATAFORMAT_PCM;
         format_pcm.formatType = SL_DATAFORMAT_PCM;
         format_pcm.numChannels = mDevice->channelsFromFmt();
         format_pcm.numChannels = mDevice->channelsFromFmt();
-        format_pcm.samplesPerSec = mDevice->Frequency * 1000;
+        format_pcm.samplesPerSec = mDevice->mSampleRate * 1000;
         format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
         format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
         format_pcm.containerSize = format_pcm.bitsPerSample;
         format_pcm.containerSize = format_pcm.bitsPerSample;
         format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
         format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
@@ -471,8 +499,8 @@ bool OpenSLPlayback::reset()
     }
     }
     if(SL_RESULT_SUCCESS == result)
     if(SL_RESULT_SUCCESS == result)
     {
     {
-        const uint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
-        mRing = RingBuffer::Create(num_updates, mFrameSize*mDevice->UpdateSize, true);
+        const uint num_updates{mDevice->mBufferSize / mDevice->mUpdateSize};
+        mRing = RingBuffer::Create(num_updates, mFrameSize*mDevice->mUpdateSize, true);
     }
     }
 
 
     if(SL_RESULT_SUCCESS != result)
     if(SL_RESULT_SUCCESS != result)
@@ -504,15 +532,15 @@ void OpenSLPlayback::start()
     }
     }
     if(SL_RESULT_SUCCESS != result)
     if(SL_RESULT_SUCCESS != result)
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to register callback: 0x%08x", result};
+            "Failed to register callback: {:#08x}", result};
 
 
     try {
     try {
         mKillNow.store(false, std::memory_order_release);
         mKillNow.store(false, std::memory_order_release);
-        mThread = std::thread(std::mem_fn(&OpenSLPlayback::mixerProc), this);
+        mThread = std::thread(&OpenSLPlayback::mixerProc, this);
     }
     }
     catch(std::exception& e) {
     catch(std::exception& e) {
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to start mixing thread: %s", e.what()};
+            "Failed to start mixing thread: {}", e.what()};
     }
     }
 }
 }
 
 
@@ -565,15 +593,15 @@ ClockLatency OpenSLPlayback::getClockLatency()
 
 
     std::lock_guard<std::mutex> dlock{mMutex};
     std::lock_guard<std::mutex> dlock{mMutex};
     ret.ClockTime = mDevice->getClockTime();
     ret.ClockTime = mDevice->getClockTime();
-    ret.Latency  = std::chrono::seconds{mRing->readSpace() * mDevice->UpdateSize};
-    ret.Latency /= mDevice->Frequency;
+    ret.Latency  = std::chrono::seconds{mRing->readSpace() * mDevice->mUpdateSize};
+    ret.Latency /= mDevice->mSampleRate;
 
 
     return ret;
     return ret;
 }
 }
 
 
 
 
 struct OpenSLCapture final : public BackendBase {
 struct OpenSLCapture final : public BackendBase {
-    OpenSLCapture(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit OpenSLCapture(DeviceBase *device) noexcept : BackendBase{device} { }
     ~OpenSLCapture() override;
     ~OpenSLCapture() override;
 
 
     void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
     void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
@@ -622,8 +650,8 @@ void OpenSLCapture::open(std::string_view name)
     if(name.empty())
     if(name.empty())
         name = GetDeviceName();
         name = GetDeviceName();
     else if(name != GetDeviceName())
     else if(name != GetDeviceName())
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
-            al::sizei(name), name.data()};
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
+            name};
 
 
     SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)};
     SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)};
     PrintErr(result, "slCreateEngine");
     PrintErr(result, "slCreateEngine");
@@ -641,16 +669,16 @@ void OpenSLCapture::open(std::string_view name)
     {
     {
         mFrameSize = mDevice->frameSizeFromFmt();
         mFrameSize = mDevice->frameSizeFromFmt();
         /* Ensure the total length is at least 100ms */
         /* Ensure the total length is at least 100ms */
-        uint length{std::max(mDevice->BufferSize, mDevice->Frequency/10u)};
+        uint length{std::max(mDevice->mBufferSize, mDevice->mSampleRate/10u)};
         /* Ensure the per-chunk length is at least 10ms, and no more than 50ms. */
         /* Ensure the per-chunk length is at least 10ms, and no more than 50ms. */
-        uint update_len{std::clamp(mDevice->BufferSize/3u, mDevice->Frequency/100u,
-            mDevice->Frequency/100u*5u)};
+        uint update_len{std::clamp(mDevice->mBufferSize/3u, mDevice->mSampleRate/100u,
+            mDevice->mSampleRate/100u*5u)};
         uint num_updates{(length+update_len-1) / update_len};
         uint num_updates{(length+update_len-1) / update_len};
 
 
         mRing = RingBuffer::Create(num_updates, update_len*mFrameSize, false);
         mRing = RingBuffer::Create(num_updates, update_len*mFrameSize, false);
 
 
-        mDevice->UpdateSize = update_len;
-        mDevice->BufferSize = static_cast<uint>(mRing->writeSpace() * update_len);
+        mDevice->mUpdateSize = update_len;
+        mDevice->mBufferSize = static_cast<uint>(mRing->writeSpace() * update_len);
     }
     }
     if(SL_RESULT_SUCCESS == result)
     if(SL_RESULT_SUCCESS == result)
     {
     {
@@ -669,14 +697,14 @@ void OpenSLCapture::open(std::string_view name)
 
 
         SLDataLocator_AndroidSimpleBufferQueue loc_bq{};
         SLDataLocator_AndroidSimpleBufferQueue loc_bq{};
         loc_bq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
         loc_bq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
-        loc_bq.numBuffers = mDevice->BufferSize / mDevice->UpdateSize;
+        loc_bq.numBuffers = mDevice->mBufferSize / mDevice->mUpdateSize;
 
 
         SLDataSink audioSnk{};
         SLDataSink audioSnk{};
 #ifdef SL_ANDROID_DATAFORMAT_PCM_EX
 #ifdef SL_ANDROID_DATAFORMAT_PCM_EX
         SLAndroidDataFormat_PCM_EX format_pcm_ex{};
         SLAndroidDataFormat_PCM_EX format_pcm_ex{};
         format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
         format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
         format_pcm_ex.numChannels = mDevice->channelsFromFmt();
         format_pcm_ex.numChannels = mDevice->channelsFromFmt();
-        format_pcm_ex.sampleRate = mDevice->Frequency * 1000;
+        format_pcm_ex.sampleRate = mDevice->mSampleRate * 1000;
         format_pcm_ex.bitsPerSample = mDevice->bytesFromFmt() * 8;
         format_pcm_ex.bitsPerSample = mDevice->bytesFromFmt() * 8;
         format_pcm_ex.containerSize = format_pcm_ex.bitsPerSample;
         format_pcm_ex.containerSize = format_pcm_ex.bitsPerSample;
         format_pcm_ex.channelMask = GetChannelMask(mDevice->FmtChans);
         format_pcm_ex.channelMask = GetChannelMask(mDevice->FmtChans);
@@ -699,7 +727,7 @@ void OpenSLCapture::open(std::string_view name)
                 SLDataFormat_PCM format_pcm{};
                 SLDataFormat_PCM format_pcm{};
                 format_pcm.formatType = SL_DATAFORMAT_PCM;
                 format_pcm.formatType = SL_DATAFORMAT_PCM;
                 format_pcm.numChannels = mDevice->channelsFromFmt();
                 format_pcm.numChannels = mDevice->channelsFromFmt();
-                format_pcm.samplesPerSec = mDevice->Frequency * 1000;
+                format_pcm.samplesPerSec = mDevice->mSampleRate * 1000;
                 format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
                 format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
                 format_pcm.containerSize = format_pcm.bitsPerSample;
                 format_pcm.containerSize = format_pcm.bitsPerSample;
                 format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
                 format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
@@ -751,20 +779,20 @@ void OpenSLCapture::open(std::string_view name)
     }
     }
     if(SL_RESULT_SUCCESS == result)
     if(SL_RESULT_SUCCESS == result)
     {
     {
-        const uint chunk_size{mDevice->UpdateSize * mFrameSize};
+        const uint chunk_size{mDevice->mUpdateSize * mFrameSize};
         const auto silence = (mDevice->FmtType == DevFmtUByte) ? std::byte{0x80} : std::byte{0};
         const auto silence = (mDevice->FmtType == DevFmtUByte) ? std::byte{0x80} : std::byte{0};
 
 
         auto data = mRing->getWriteVector();
         auto data = mRing->getWriteVector();
-        std::fill_n(data.first.buf, data.first.len*chunk_size, silence);
-        std::fill_n(data.second.buf, data.second.len*chunk_size, silence);
-        for(size_t i{0u};i < data.first.len && SL_RESULT_SUCCESS == result;i++)
+        std::fill_n(data[0].buf, data[0].len*chunk_size, silence);
+        std::fill_n(data[1].buf, data[1].len*chunk_size, silence);
+        for(size_t i{0u};i < data[0].len && SL_RESULT_SUCCESS == result;i++)
         {
         {
-            result = VCALL(bufferQueue,Enqueue)(data.first.buf + chunk_size*i, chunk_size);
+            result = VCALL(bufferQueue,Enqueue)(data[0].buf + chunk_size*i, chunk_size);
             PrintErr(result, "bufferQueue->Enqueue");
             PrintErr(result, "bufferQueue->Enqueue");
         }
         }
-        for(size_t i{0u};i < data.second.len && SL_RESULT_SUCCESS == result;i++)
+        for(size_t i{0u};i < data[1].len && SL_RESULT_SUCCESS == result;i++)
         {
         {
-            result = VCALL(bufferQueue,Enqueue)(data.second.buf + chunk_size*i, chunk_size);
+            result = VCALL(bufferQueue,Enqueue)(data[1].buf + chunk_size*i, chunk_size);
             PrintErr(result, "bufferQueue->Enqueue");
             PrintErr(result, "bufferQueue->Enqueue");
         }
         }
     }
     }
@@ -781,10 +809,10 @@ void OpenSLCapture::open(std::string_view name)
         mEngine = nullptr;
         mEngine = nullptr;
 
 
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to initialize OpenSL device: 0x%08x", result};
+            "Failed to initialize OpenSL device: {:#08x}", result};
     }
     }
 
 
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 }
 }
 
 
 void OpenSLCapture::start()
 void OpenSLCapture::start()
@@ -800,7 +828,7 @@ void OpenSLCapture::start()
     }
     }
     if(SL_RESULT_SUCCESS != result)
     if(SL_RESULT_SUCCESS != result)
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to start capture: 0x%08x", result};
+            "Failed to start capture: {:#08x}", result};
 }
 }
 
 
 void OpenSLCapture::stop()
 void OpenSLCapture::stop()
@@ -818,7 +846,7 @@ void OpenSLCapture::stop()
 
 
 void OpenSLCapture::captureSamples(std::byte *buffer, uint samples)
 void OpenSLCapture::captureSamples(std::byte *buffer, uint samples)
 {
 {
-    const uint update_size{mDevice->UpdateSize};
+    const uint update_size{mDevice->mUpdateSize};
     const uint chunk_size{update_size * mFrameSize};
     const uint chunk_size{update_size * mFrameSize};
 
 
     /* Read the desired samples from the ring buffer then advance its read
     /* Read the desired samples from the ring buffer then advance its read
@@ -829,7 +857,7 @@ void OpenSLCapture::captureSamples(std::byte *buffer, uint samples)
     for(uint i{0};i < samples;)
     for(uint i{0};i < samples;)
     {
     {
         const uint rem{std::min(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},
+        std::copy_n(rdata[0].buf + mSplOffset*size_t{mFrameSize}, rem*size_t{mFrameSize},
             buffer + i*size_t{mFrameSize});
             buffer + i*size_t{mFrameSize});
 
 
         mSplOffset += rem;
         mSplOffset += rem;
@@ -839,11 +867,11 @@ void OpenSLCapture::captureSamples(std::byte *buffer, uint samples)
             mSplOffset = 0;
             mSplOffset = 0;
 
 
             ++adv_count;
             ++adv_count;
-            rdata.first.len -= 1;
-            if(!rdata.first.len)
-                rdata.first = rdata.second;
+            rdata[0].len -= 1;
+            if(!rdata[0].len)
+                rdata[0] = rdata[1];
             else
             else
-                rdata.first.buf += chunk_size;
+                rdata[0].buf += chunk_size;
         }
         }
 
 
         i += rem;
         i += rem;
@@ -857,7 +885,7 @@ void OpenSLCapture::captureSamples(std::byte *buffer, uint samples)
         PrintErr(result, "recordObj->GetInterface");
         PrintErr(result, "recordObj->GetInterface");
         if(SL_RESULT_SUCCESS != result) UNLIKELY
         if(SL_RESULT_SUCCESS != result) UNLIKELY
         {
         {
-            mDevice->handleDisconnect("Failed to get capture buffer queue: 0x%08x", result);
+            mDevice->handleDisconnect("Failed to get capture buffer queue: {:#08x}", result);
             bufferQueue = nullptr;
             bufferQueue = nullptr;
         }
         }
     }
     }
@@ -876,20 +904,20 @@ void OpenSLCapture::captureSamples(std::byte *buffer, uint samples)
 
 
     SLresult result{SL_RESULT_SUCCESS};
     SLresult result{SL_RESULT_SUCCESS};
     auto wdata = mRing->getWriteVector();
     auto wdata = mRing->getWriteVector();
-    if(adv_count > wdata.second.len) LIKELY
+    if(adv_count > wdata[1].len) LIKELY
     {
     {
-        auto len1 = std::min(wdata.first.len, adv_count-wdata.second.len);
-        auto buf1 = wdata.first.buf + chunk_size*(wdata.first.len-len1);
+        auto len1 = std::min(wdata[0].len, adv_count-wdata[1].len);
+        auto buf1 = wdata[0].buf + chunk_size*(wdata[0].len-len1);
         for(size_t i{0u};i < len1 && SL_RESULT_SUCCESS == result;i++)
         for(size_t i{0u};i < len1 && SL_RESULT_SUCCESS == result;i++)
         {
         {
             result = VCALL(bufferQueue,Enqueue)(buf1 + chunk_size*i, chunk_size);
             result = VCALL(bufferQueue,Enqueue)(buf1 + chunk_size*i, chunk_size);
             PrintErr(result, "bufferQueue->Enqueue");
             PrintErr(result, "bufferQueue->Enqueue");
         }
         }
     }
     }
-    if(wdata.second.len > 0)
+    if(wdata[1].len > 0)
     {
     {
-        auto len2 = std::min(wdata.second.len, adv_count);
-        auto buf2 = wdata.second.buf + chunk_size*(wdata.second.len-len2);
+        auto len2 = std::min(wdata[1].len, adv_count);
+        auto buf2 = wdata[1].buf + chunk_size*(wdata[1].len-len2);
         for(size_t i{0u};i < len2 && SL_RESULT_SUCCESS == result;i++)
         for(size_t i{0u};i < len2 && SL_RESULT_SUCCESS == result;i++)
         {
         {
             result = VCALL(bufferQueue,Enqueue)(buf2 + chunk_size*i, chunk_size);
             result = VCALL(bufferQueue,Enqueue)(buf2 + chunk_size*i, chunk_size);
@@ -899,25 +927,56 @@ void OpenSLCapture::captureSamples(std::byte *buffer, uint samples)
 }
 }
 
 
 uint OpenSLCapture::availableSamples()
 uint OpenSLCapture::availableSamples()
-{ return static_cast<uint>(mRing->readSpace()*mDevice->UpdateSize - mSplOffset); }
+{ return static_cast<uint>(mRing->readSpace()*mDevice->mUpdateSize - mSplOffset); }
 
 
 } // namespace
 } // namespace
 
 
-bool OSLBackendFactory::init() { return true; }
+bool OSLBackendFactory::init()
+{
+#if HAVE_DYNLOAD
+    if(!sles_handle)
+    {
+#define SLES_LIBNAME "libOpenSLES.so"
+        sles_handle = LoadLib(SLES_LIBNAME);
+        if(!sles_handle)
+        {
+            WARN("Failed to load {}", SLES_LIBNAME);
+            return false;
+        }
+
+        std::string missing_syms;
+#define LOAD_SYMBOL(f) do {                                                   \
+    p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(sles_handle, #f));      \
+    if(p##f == nullptr) missing_syms += "\n" #f;                              \
+} while(0)
+        SLES_SYMBOLS(LOAD_SYMBOL);
+#undef LOAD_SYMBOL
+
+        if(!missing_syms.empty())
+        {
+            WARN("Missing expected symbols:{}", missing_syms);
+            CloseLib(sles_handle);
+            sles_handle = nullptr;
+            return false;
+        }
+    }
+#endif
+
+    return true;
+}
 
 
 bool OSLBackendFactory::querySupport(BackendType type)
 bool OSLBackendFactory::querySupport(BackendType type)
 { return (type == BackendType::Playback || type == BackendType::Capture); }
 { return (type == BackendType::Playback || type == BackendType::Capture); }
 
 
-std::string OSLBackendFactory::probe(BackendType type)
+auto OSLBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
 {
 {
     switch(type)
     switch(type)
     {
     {
     case BackendType::Playback:
     case BackendType::Playback:
     case BackendType::Capture:
     case BackendType::Capture:
-        /* Include null char. */
-        return std::string{GetDeviceName()} + '\0';
+        return std::vector{std::string{GetDeviceName()}};
     }
     }
-    return std::string{};
+    return {};
 }
 }
 
 
 BackendPtr OSLBackendFactory::createBackend(DeviceBase *device, BackendType type)
 BackendPtr OSLBackendFactory::createBackend(DeviceBase *device, BackendType type)

+ 5 - 5
libs/openal-soft/alc/backends/opensl.h

@@ -5,15 +5,15 @@
 
 
 struct OSLBackendFactory final : public BackendFactory {
 struct OSLBackendFactory final : public BackendFactory {
 public:
 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 */
 #endif /* BACKENDS_OSL_H */

+ 72 - 79
libs/openal-soft/alc/backends/oss.cpp

@@ -33,7 +33,6 @@
 #include <cerrno>
 #include <cerrno>
 #include <cstring>
 #include <cstring>
 #include <exception>
 #include <exception>
-#include <functional>
 #include <memory>
 #include <memory>
 #include <string>
 #include <string>
 #include <string_view>
 #include <string_view>
@@ -43,13 +42,12 @@
 #include <vector>
 #include <vector>
 
 
 #include "alc/alconfig.h"
 #include "alc/alconfig.h"
-#include "almalloc.h"
 #include "alnumeric.h"
 #include "alnumeric.h"
-#include "alstring.h"
 #include "althrd_setname.h"
 #include "althrd_setname.h"
 #include "core/device.h"
 #include "core/device.h"
 #include "core/helpers.h"
 #include "core/helpers.h"
 #include "core/logging.h"
 #include "core/logging.h"
+#include "fmt/core.h"
 #include "ringbuffer.h"
 #include "ringbuffer.h"
 
 
 #include <sys/soundcard.h>
 #include <sys/soundcard.h>
@@ -169,18 +167,13 @@ void ALCossListAppend(std::vector<DevMap> &list, std::string_view handle, std::s
         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();
         return std::find_if(list.cbegin(), list.cend(), match_name) != list.cend();
     };
     };
-    int count{1};
-    std::string newname{handle};
+    auto count = 1;
+    auto newname = std::string{handle};
     while(checkName(newname))
     while(checkName(newname))
-    {
-        newname = handle;
-        newname += " #";
-        newname += std::to_string(++count);
-    }
-
-    const DevMap &entry = list.emplace_back(std::move(newname), path);
+        newname = fmt::format("{} #{}", handle, ++count);
 
 
-    TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
+    const auto &entry = list.emplace_back(std::move(newname), path);
+    TRACE("Got device \"{}\", \"{}\"", entry.name, entry.device_name);
 }
 }
 
 
 void ALCossListPopulate(std::vector<DevMap> &devlist, int type_flag)
 void ALCossListPopulate(std::vector<DevMap> &devlist, int type_flag)
@@ -189,13 +182,13 @@ void ALCossListPopulate(std::vector<DevMap> &devlist, int type_flag)
     FileHandle file;
     FileHandle file;
     if(!file.open("/dev/mixer", O_RDONLY))
     if(!file.open("/dev/mixer", O_RDONLY))
     {
     {
-        TRACE("Could not open /dev/mixer: %s\n", std::generic_category().message(errno).c_str());
+        TRACE("Could not open /dev/mixer: {}", std::generic_category().message(errno));
         goto done;
         goto done;
     }
     }
 
 
     if(ioctl(file.get(), SNDCTL_SYSINFO, &si) == -1)
     if(ioctl(file.get(), SNDCTL_SYSINFO, &si) == -1)
     {
     {
-        TRACE("SNDCTL_SYSINFO failed: %s\n", std::generic_category().message(errno).c_str());
+        TRACE("SNDCTL_SYSINFO failed: {}", std::generic_category().message(errno));
         goto done;
         goto done;
     }
     }
 
 
@@ -205,8 +198,7 @@ void ALCossListPopulate(std::vector<DevMap> &devlist, int type_flag)
         ai.dev = i;
         ai.dev = i;
         if(ioctl(file.get(), SNDCTL_AUDIOINFO, &ai) == -1)
         if(ioctl(file.get(), SNDCTL_AUDIOINFO, &ai) == -1)
         {
         {
-            ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i,
-                std::generic_category().message(errno).c_str());
+            ERR("SNDCTL_AUDIOINFO ({}) failed: {}", i, std::generic_category().message(errno));
             continue;
             continue;
         }
         }
         if(!(ai.caps&type_flag) || ai.devnode[0] == '\0')
         if(!(ai.caps&type_flag) || ai.devnode[0] == '\0')
@@ -256,7 +248,7 @@ uint log2i(uint x)
 
 
 
 
 struct OSSPlayback final : public BackendBase {
 struct OSSPlayback final : public BackendBase {
-    OSSPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit OSSPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
     ~OSSPlayback() override;
     ~OSSPlayback() override;
 
 
     int mixerProc();
     int mixerProc();
@@ -302,13 +294,13 @@ int OSSPlayback::mixerProc()
             if(errno == EINTR || errno == EAGAIN)
             if(errno == EINTR || errno == EAGAIN)
                 continue;
                 continue;
             const auto errstr = std::generic_category().message(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());
+            ERR("poll failed: {}", errstr);
+            mDevice->handleDisconnect("Failed waiting for playback buffer: {}", errstr);
             break;
             break;
         }
         }
         else if(pret == 0) /* NOLINT(*-else-after-return) 'pret' is local to the if/else blocks */
         else if(pret == 0) /* NOLINT(*-else-after-return) 'pret' is local to the if/else blocks */
         {
         {
-            WARN("poll timeout\n");
+            WARN("poll timeout");
             continue;
             continue;
         }
         }
 
 
@@ -323,8 +315,8 @@ int OSSPlayback::mixerProc()
                 if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
                 if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
                     continue;
                     continue;
                 const auto errstr = std::generic_category().message(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());
+                ERR("write failed: {}", errstr);
+                mDevice->handleDisconnect("Failed writing playback samples: {}", errstr);
                 break;
                 break;
             }
             }
 
 
@@ -352,20 +344,20 @@ void OSSPlayback::open(std::string_view name)
         );
         );
         if(iter == PlaybackDevices.cend())
         if(iter == PlaybackDevices.cend())
             throw al::backend_exception{al::backend_error::NoDevice,
             throw al::backend_exception{al::backend_error::NoDevice,
-                "Device name \"%.*s\" not found", al::sizei(name), name.data()};
+                "Device name \"{}\" not found", name};
         devname = iter->device_name.c_str();
         devname = iter->device_name.c_str();
     }
     }
 
 
-    int fd{::open(devname, O_WRONLY)};
+    const auto fd = ::open(devname, O_WRONLY); /* NOLINT(cppcoreguidelines-pro-type-vararg) */
     if(fd == -1)
     if(fd == -1)
-        throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s", devname,
-            std::generic_category().message(errno).c_str()};
+        throw al::backend_exception{al::backend_error::NoDevice, "Could not open {}: {}", devname,
+            std::generic_category().message(errno)};
 
 
     if(mFd != -1)
     if(mFd != -1)
         ::close(mFd);
         ::close(mFd);
     mFd = fd;
     mFd = fd;
 
 
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 }
 }
 
 
 bool OSSPlayback::reset()
 bool OSSPlayback::reset()
@@ -390,31 +382,33 @@ bool OSSPlayback::reset()
             break;
             break;
     }
     }
 
 
-    uint periods{mDevice->BufferSize / mDevice->UpdateSize};
+    uint periods{mDevice->mBufferSize / mDevice->mUpdateSize};
     uint numChannels{mDevice->channelsFromFmt()};
     uint numChannels{mDevice->channelsFromFmt()};
-    uint ossSpeed{mDevice->Frequency};
+    uint ossSpeed{mDevice->mSampleRate};
     uint frameSize{numChannels * mDevice->bytesFromFmt()};
     uint frameSize{numChannels * mDevice->bytesFromFmt()};
     /* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */
     /* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */
-    uint log2FragmentSize{std::max(log2i(mDevice->UpdateSize*frameSize), 4u)};
+    uint log2FragmentSize{std::max(log2i(mDevice->mUpdateSize*frameSize), 4u)};
     uint numFragmentsLogSize{(periods << 16) | log2FragmentSize};
     uint numFragmentsLogSize{(periods << 16) | log2FragmentSize};
 
 
     audio_buf_info info{};
     audio_buf_info info{};
 #define CHECKERR(func) if((func) < 0)                                         \
 #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()};
+    throw al::backend_exception{al::backend_error::DeviceError, #func " failed: {}", \
+        std::generic_category().message(errno)};
 
 
     /* Don't fail if SETFRAGMENT fails. We can handle just about anything
     /* Don't fail if SETFRAGMENT fails. We can handle just about anything
      * that's reported back via GETOSPACE */
      * that's reported back via GETOSPACE */
+    /* NOLINTBEGIN(cppcoreguidelines-pro-type-vararg) */
     ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
     ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
     CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat));
     CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat));
     CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels));
     CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels));
     CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed));
     CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed));
     CHECKERR(ioctl(mFd, SNDCTL_DSP_GETOSPACE, &info));
     CHECKERR(ioctl(mFd, SNDCTL_DSP_GETOSPACE, &info));
+    /* NOLINTEND(cppcoreguidelines-pro-type-vararg) */
 #undef CHECKERR
 #undef CHECKERR
 
 
     if(mDevice->channelsFromFmt() != numChannels)
     if(mDevice->channelsFromFmt() != numChannels)
     {
     {
-        ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(mDevice->FmtChans),
+        ERR("Failed to set {}, got {} channels instead", DevFmtChannelsString(mDevice->FmtChans),
             numChannels);
             numChannels);
         return false;
         return false;
     }
     }
@@ -423,18 +417,18 @@ bool OSSPlayback::reset()
          (ossFormat == AFMT_U8 && mDevice->FmtType == DevFmtUByte) ||
          (ossFormat == AFMT_U8 && mDevice->FmtType == DevFmtUByte) ||
          (ossFormat == AFMT_S16_NE && mDevice->FmtType == DevFmtShort)))
          (ossFormat == AFMT_S16_NE && mDevice->FmtType == DevFmtShort)))
     {
     {
-        ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(mDevice->FmtType),
-            ossFormat);
+        ERR("Failed to set {} samples, got OSS format {:#x}", DevFmtTypeString(mDevice->FmtType),
+            as_unsigned(ossFormat));
         return false;
         return false;
     }
     }
 
 
-    mDevice->Frequency = ossSpeed;
-    mDevice->UpdateSize = static_cast<uint>(info.fragsize) / frameSize;
-    mDevice->BufferSize = static_cast<uint>(info.fragments) * mDevice->UpdateSize;
+    mDevice->mSampleRate = ossSpeed;
+    mDevice->mUpdateSize = static_cast<uint>(info.fragsize) / frameSize;
+    mDevice->mBufferSize = static_cast<uint>(info.fragments) * mDevice->mUpdateSize;
 
 
     setDefaultChannelOrder();
     setDefaultChannelOrder();
 
 
-    mMixData.resize(size_t{mDevice->UpdateSize} * mDevice->frameSizeFromFmt());
+    mMixData.resize(size_t{mDevice->mUpdateSize} * mDevice->frameSizeFromFmt());
 
 
     return true;
     return true;
 }
 }
@@ -443,11 +437,11 @@ void OSSPlayback::start()
 {
 {
     try {
     try {
         mKillNow.store(false, std::memory_order_release);
         mKillNow.store(false, std::memory_order_release);
-        mThread = std::thread{std::mem_fn(&OSSPlayback::mixerProc), this};
+        mThread = std::thread{&OSSPlayback::mixerProc, this};
     }
     }
     catch(std::exception& e) {
     catch(std::exception& e) {
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to start mixing thread: %s", e.what()};
+            "Failed to start mixing thread: {}", e.what()};
     }
     }
 }
 }
 
 
@@ -457,13 +451,13 @@ void OSSPlayback::stop()
         return;
         return;
     mThread.join();
     mThread.join();
 
 
-    if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
-        ERR("Error resetting device: %s\n", std::generic_category().message(errno).c_str());
+    if(ioctl(mFd, SNDCTL_DSP_RESET) != 0) /* NOLINT(cppcoreguidelines-pro-type-vararg) */
+        ERR("Error resetting device: {}", std::generic_category().message(errno));
 }
 }
 
 
 
 
 struct OSScapture final : public BackendBase {
 struct OSScapture final : public BackendBase {
-    OSScapture(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit OSScapture(DeviceBase *device) noexcept : BackendBase{device} { }
     ~OSScapture() override;
     ~OSScapture() override;
 
 
     int recordProc();
     int recordProc();
@@ -507,25 +501,25 @@ int OSScapture::recordProc()
             if(errno == EINTR || errno == EAGAIN)
             if(errno == EINTR || errno == EAGAIN)
                 continue;
                 continue;
             const auto errstr = std::generic_category().message(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());
+            ERR("poll failed: {}", errstr);
+            mDevice->handleDisconnect("Failed to check capture samples: {}", errstr);
             break;
             break;
         }
         }
         else if(pret == 0) /* NOLINT(*-else-after-return) 'pret' is local to the if/else blocks */
         else if(pret == 0) /* NOLINT(*-else-after-return) 'pret' is local to the if/else blocks */
         {
         {
-            WARN("poll timeout\n");
+            WARN("poll timeout");
             continue;
             continue;
         }
         }
 
 
         auto vec = mRing->getWriteVector();
         auto vec = mRing->getWriteVector();
-        if(vec.first.len > 0)
+        if(vec[0].len > 0)
         {
         {
-            ssize_t amt{read(mFd, vec.first.buf, vec.first.len*frame_size)};
+            ssize_t amt{read(mFd, vec[0].buf, vec[0].len*frame_size)};
             if(amt < 0)
             if(amt < 0)
             {
             {
                 const auto errstr = std::generic_category().message(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());
+                ERR("read failed: {}", errstr);
+                mDevice->handleDisconnect("Failed reading capture samples: {}", errstr);
                 break;
                 break;
             }
             }
             mRing->writeAdvance(static_cast<size_t>(amt)/frame_size);
             mRing->writeAdvance(static_cast<size_t>(amt)/frame_size);
@@ -552,14 +546,14 @@ void OSScapture::open(std::string_view name)
         );
         );
         if(iter == CaptureDevices.cend())
         if(iter == CaptureDevices.cend())
             throw al::backend_exception{al::backend_error::NoDevice,
             throw al::backend_exception{al::backend_error::NoDevice,
-                "Device name \"%.*s\" not found", al::sizei(name), name.data()};
+                "Device name \"{}\" not found", name};
         devname = iter->device_name.c_str();
         devname = iter->device_name.c_str();
     }
     }
 
 
-    mFd = ::open(devname, O_RDONLY);
+    mFd = ::open(devname, O_RDONLY); /* NOLINT(cppcoreguidelines-pro-type-vararg) */
     if(mFd == -1)
     if(mFd == -1)
-        throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s", devname,
-            std::generic_category().message(errno).c_str()};
+        throw al::backend_exception{al::backend_error::NoDevice, "Could not open {}: {}", devname,
+            std::generic_category().message(errno)};
 
 
     int ossFormat{};
     int ossFormat{};
     switch(mDevice->FmtType)
     switch(mDevice->FmtType)
@@ -578,55 +572,57 @@ void OSScapture::open(std::string_view name)
     case DevFmtUInt:
     case DevFmtUInt:
     case DevFmtFloat:
     case DevFmtFloat:
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "%s capture samples not supported", DevFmtTypeString(mDevice->FmtType)};
+            "{} capture samples not supported", DevFmtTypeString(mDevice->FmtType)};
     }
     }
 
 
     uint periods{4};
     uint periods{4};
     uint numChannels{mDevice->channelsFromFmt()};
     uint numChannels{mDevice->channelsFromFmt()};
     uint frameSize{numChannels * mDevice->bytesFromFmt()};
     uint frameSize{numChannels * mDevice->bytesFromFmt()};
-    uint ossSpeed{mDevice->Frequency};
+    uint ossSpeed{mDevice->mSampleRate};
     /* according to the OSS spec, 16 bytes are the minimum */
     /* according to the OSS spec, 16 bytes are the minimum */
-    uint log2FragmentSize{std::max(log2i(mDevice->BufferSize * frameSize / periods), 4u)};
+    uint log2FragmentSize{std::max(log2i(mDevice->mBufferSize * frameSize / periods), 4u)};
     uint numFragmentsLogSize{(periods << 16) | log2FragmentSize};
     uint numFragmentsLogSize{(periods << 16) | log2FragmentSize};
 
 
     audio_buf_info info{};
     audio_buf_info info{};
 #define CHECKERR(func) if((func) < 0) {                                       \
 #define CHECKERR(func) if((func) < 0) {                                       \
-    throw al::backend_exception{al::backend_error::DeviceError, #func " failed: %s", \
-        std::generic_category().message(errno).c_str()};                      \
+    throw al::backend_exception{al::backend_error::DeviceError, #func " failed: {}", \
+        std::generic_category().message(errno)};                              \
 }
 }
+    /* NOLINTBEGIN(cppcoreguidelines-pro-type-vararg) */
     CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
     CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
     CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat));
     CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat));
     CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels));
     CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels));
     CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed));
     CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed));
     CHECKERR(ioctl(mFd, SNDCTL_DSP_GETISPACE, &info));
     CHECKERR(ioctl(mFd, SNDCTL_DSP_GETISPACE, &info));
+    /* NOLINTEND(cppcoreguidelines-pro-type-vararg) */
 #undef CHECKERR
 #undef CHECKERR
 
 
     if(mDevice->channelsFromFmt() != numChannels)
     if(mDevice->channelsFromFmt() != numChannels)
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to set %s, got %d channels instead", DevFmtChannelsString(mDevice->FmtChans),
+            "Failed to set {}, got {} channels instead", DevFmtChannelsString(mDevice->FmtChans),
             numChannels};
             numChannels};
 
 
     if(!((ossFormat == AFMT_S8 && mDevice->FmtType == DevFmtByte)
     if(!((ossFormat == AFMT_S8 && mDevice->FmtType == DevFmtByte)
         || (ossFormat == AFMT_U8 && mDevice->FmtType == DevFmtUByte)
         || (ossFormat == AFMT_U8 && mDevice->FmtType == DevFmtUByte)
         || (ossFormat == AFMT_S16_NE && mDevice->FmtType == DevFmtShort)))
         || (ossFormat == AFMT_S16_NE && mDevice->FmtType == DevFmtShort)))
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to set %s samples, got OSS format %#x", DevFmtTypeString(mDevice->FmtType),
-            ossFormat};
+            "Failed to set {} samples, got OSS format {:#x}", DevFmtTypeString(mDevice->FmtType),
+            as_unsigned(ossFormat)};
 
 
-    mRing = RingBuffer::Create(mDevice->BufferSize, frameSize, false);
+    mRing = RingBuffer::Create(mDevice->mBufferSize, frameSize, false);
 
 
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 }
 }
 
 
 void OSScapture::start()
 void OSScapture::start()
 {
 {
     try {
     try {
         mKillNow.store(false, std::memory_order_release);
         mKillNow.store(false, std::memory_order_release);
-        mThread = std::thread{std::mem_fn(&OSScapture::recordProc), this};
+        mThread = std::thread{&OSScapture::recordProc, this};
     }
     }
     catch(std::exception& e) {
     catch(std::exception& e) {
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to start recording thread: %s", e.what()};
+            "Failed to start recording thread: {}", e.what()};
     }
     }
 }
 }
 
 
@@ -636,8 +632,8 @@ void OSScapture::stop()
         return;
         return;
     mThread.join();
     mThread.join();
 
 
-    if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
-        ERR("Error resetting device: %s\n", std::generic_category().message(errno).c_str());
+    if(ioctl(mFd, SNDCTL_DSP_RESET) != 0) /* NOLINT(cppcoreguidelines-pro-type-vararg) */
+        ERR("Error resetting device: {}", std::generic_category().message(errno));
 }
 }
 
 
 void OSScapture::captureSamples(std::byte *buffer, uint samples)
 void OSScapture::captureSamples(std::byte *buffer, uint samples)
@@ -668,18 +664,13 @@ bool OSSBackendFactory::init()
 bool OSSBackendFactory::querySupport(BackendType type)
 bool OSSBackendFactory::querySupport(BackendType type)
 { return (type == BackendType::Playback || type == BackendType::Capture); }
 { 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
     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)
     switch(type)
@@ -687,12 +678,14 @@ std::string OSSBackendFactory::probe(BackendType type)
     case BackendType::Playback:
     case BackendType::Playback:
         PlaybackDevices.clear();
         PlaybackDevices.clear();
         ALCossListPopulate(PlaybackDevices, DSP_CAP_OUTPUT);
         ALCossListPopulate(PlaybackDevices, DSP_CAP_OUTPUT);
+        outnames.reserve(PlaybackDevices.size());
         std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
         std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
         break;
         break;
 
 
     case BackendType::Capture:
     case BackendType::Capture:
         CaptureDevices.clear();
         CaptureDevices.clear();
         ALCossListPopulate(CaptureDevices, DSP_CAP_INPUT);
         ALCossListPopulate(CaptureDevices, DSP_CAP_INPUT);
+        outnames.reserve(CaptureDevices.size());
         std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
         std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
         break;
         break;
     }
     }

+ 5 - 5
libs/openal-soft/alc/backends/oss.h

@@ -5,15 +5,15 @@
 
 
 struct OSSBackendFactory final : public BackendFactory {
 struct OSSBackendFactory final : public BackendFactory {
 public:
 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 */
 #endif /* BACKENDS_OSS_H */

+ 700 - 0
libs/openal-soft/alc/backends/otherio.cpp

@@ -0,0 +1,700 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2024 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include "otherio.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winreg.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <memory.h>
+
+#include <wtypes.h>
+#include <cguid.h>
+#include <devpropdef.h>
+#include <mmreg.h>
+#include <propsys.h>
+#include <propkey.h>
+#include <devpkey.h>
+#ifndef _WAVEFORMATEXTENSIBLE_
+#include <ks.h>
+#include <ksmedia.h>
+#endif
+
+#include <algorithm>
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <cstring>
+#include <deque>
+#include <future>
+#include <mutex>
+#include <string>
+#include <string_view>
+#include <thread>
+#include <vector>
+
+#include "albit.h"
+#include "alnumeric.h"
+#include "althrd_setname.h"
+#include "comptr.h"
+#include "core/converter.h"
+#include "core/device.h"
+#include "core/helpers.h"
+#include "core/logging.h"
+#include "strutils.h"
+
+
+/* A custom C++ interface that should be capable of interoperating with ASIO
+ * drivers.
+ */
+enum class ORIOError : LONG {
+    Okay = 0,
+    Success = 0x3f4847a0,
+    NotPresent = -1000,
+    HWMalfunction,
+    InvalidParameter,
+    InvalidMode,
+    SPNotAdvancing,
+    NoClock,
+    NoMemory,
+};
+
+/* A 64-bit integer or double, which has the most significant 32-bit word first. */
+struct ORIO64Bit {
+    uint32_t hi;
+    uint32_t lo;
+
+    template<typename T>
+    auto as() const -> T = delete;
+};
+
+template<> [[nodiscard]]
+auto ORIO64Bit::as() const -> uint64_t { return (uint64_t{hi}<<32) | lo; }
+template<> [[nodiscard]]
+auto ORIO64Bit::as() const -> int64_t { return static_cast<int64_t>(as<uint64_t>()); }
+template<> [[nodiscard]]
+auto ORIO64Bit::as() const -> double { return al::bit_cast<double>(as<uint64_t>()); }
+
+
+enum class ORIOSampleType : LONG {
+    Int16BE = 0,
+    Int24BE = 1,
+    Int32BE = 2,
+    Float32BE = 3,
+    Float64BE = 4,
+    Int32BE16 = 8,
+    Int32BE18 = 9,
+    Int32BE20 = 10,
+    Int32BE24 = 11,
+
+    Int16LE = 16,
+    Int24LE = 17,
+    Int32LE = 18,
+    Float32LE = 19,
+    Float64LE = 20,
+    Int32LE16 = 24,
+    Int32LE18 = 25,
+    Int32LE20 = 26,
+    Int32LE24 = 27,
+
+    DSDInt8LSB1 = 32,
+    DSDInt8MSB1 = 33,
+
+    DSDInt8 = 40,
+};
+
+struct ORIOClockSource {
+    LONG mIndex;
+    LONG mAssocChannel;
+    LONG mAssocGroup;
+    LONG mIsCurrent;
+    std::array<char,32> mName;
+};
+
+struct ORIOChannelInfo {
+    LONG mChannel;
+    LONG mIsInput;
+    LONG mIsActive;
+    LONG mGroup;
+    ORIOSampleType mSampleType;
+    std::array<char,32> mName;
+};
+
+struct ORIOBufferInfo {
+    LONG mIsInput;
+    LONG mChannelNum;
+    std::array<void*,2> mBuffers;
+};
+
+struct ORIOTime {
+    struct TimeInfo {
+        double mSpeed;
+        ORIO64Bit mSystemTime;
+        ORIO64Bit mSamplePosition;
+        double mSampleRate;
+        ULONG mFlags;
+        std::array<char,12> mReserved;
+    };
+    struct TimeCode {
+        double mSpeed;
+        ORIO64Bit mTimeCodeSamples;
+        ULONG mFlags;
+        std::array<char,64> mFuture;
+    };
+
+    std::array<LONG,4> mReserved;
+    TimeInfo mTimeInfo;
+    TimeCode mTimeCode;
+};
+
+#ifdef _WIN64
+#define ORIO_CALLBACK CALLBACK
+#else
+#define ORIO_CALLBACK
+#endif
+
+struct ORIOCallbacks {
+    void (ORIO_CALLBACK*BufferSwitch)(LONG bufferIndex, LONG directProcess) noexcept;
+    void (ORIO_CALLBACK*SampleRateDidChange)(double srate) noexcept;
+    auto (ORIO_CALLBACK*Message)(LONG selector, LONG value, void *message, double *opt) noexcept -> LONG;
+    auto (ORIO_CALLBACK*BufferSwitchTimeInfo)(ORIOTime *timeInfo, LONG bufferIndex, LONG directProcess) noexcept -> ORIOTime*;
+};
+
+/* COM interfaces don't include a virtual destructor in their pure-virtual
+ * classes, and we can't add one without breaking ABI.
+ */
+#ifdef __GNUC__
+_Pragma("GCC diagnostic push")
+_Pragma("GCC diagnostic ignored \"-Wnon-virtual-dtor\"")
+#endif
+/* NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor) */
+struct ORIOiface : public IUnknown {
+    STDMETHOD_(LONG, Init)(void *sysHandle) = 0;
+    /* A fixed-length span should be passed exactly the same as one pointer.
+     * This ensures an appropriately-sized buffer for the driver.
+     */
+    STDMETHOD_(void, GetDriverName)(al::span<char,32> name) = 0;
+    STDMETHOD_(LONG, GetDriverVersion)() = 0;
+    STDMETHOD_(void, GetErrorMessage)(al::span<char,124> message) = 0;
+    STDMETHOD_(ORIOError, Start)() = 0;
+    STDMETHOD_(ORIOError, Stop)() = 0;
+    STDMETHOD_(ORIOError, GetChannels)(LONG *numInput, LONG *numOutput) = 0;
+    STDMETHOD_(ORIOError, GetLatencies)(LONG *inputLatency, LONG *outputLatency) = 0;
+    STDMETHOD_(ORIOError, GetBufferSize)(LONG *minSize, LONG *maxSize, LONG *preferredSize, LONG *granularity) = 0;
+    STDMETHOD_(ORIOError, CanSampleRate)(double srate) = 0;
+    STDMETHOD_(ORIOError, GetSampleRate)(double *srate) = 0;
+    STDMETHOD_(ORIOError, SetSampleRate)(double srate) = 0;
+    STDMETHOD_(ORIOError, GetClockSources)(ORIOClockSource *clocks, LONG *numSources) = 0;
+    STDMETHOD_(ORIOError, SetClockSource)(LONG index) = 0;
+    STDMETHOD_(ORIOError, GetSamplePosition)(ORIO64Bit *splPos, ORIO64Bit *tstampNS) = 0;
+    STDMETHOD_(ORIOError, GetChannelInfo)(ORIOChannelInfo *info) = 0;
+    STDMETHOD_(ORIOError, CreateBuffers)(ORIOBufferInfo *infos, LONG numInfos, LONG bufferSize, ORIOCallbacks *callbacks) = 0;
+    STDMETHOD_(ORIOError, DisposeBuffers)() = 0;
+    STDMETHOD_(ORIOError, ControlPanel)() = 0;
+    STDMETHOD_(ORIOError, Future)(LONG selector, void *opt) = 0;
+    STDMETHOD_(ORIOError, OutputReady)() = 0;
+
+    ORIOiface() = default;
+    ORIOiface(const ORIOiface&) = delete;
+    auto operator=(const ORIOiface&) -> ORIOiface& = delete;
+    ~ORIOiface() = delete;
+};
+#ifdef __GNUC__
+_Pragma("GCC diagnostic pop")
+#endif
+
+namespace {
+
+using namespace std::string_view_literals;
+using std::chrono::nanoseconds;
+using std::chrono::milliseconds;
+using std::chrono::seconds;
+
+
+struct DeviceEntry {
+    std::string mDrvName;
+    CLSID mDrvGuid{};
+};
+
+std::vector<DeviceEntry> gDeviceList;
+
+
+struct KeyCloser {
+    void operator()(HKEY key) { RegCloseKey(key); }
+};
+using KeyPtr = std::unique_ptr<std::remove_pointer_t<HKEY>,KeyCloser>;
+
+[[nodiscard]]
+auto PopulateDeviceList() -> HRESULT
+{
+    auto regbase = KeyPtr{};
+    auto res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\ASIO", 0, KEY_READ,
+        al::out_ptr(regbase));
+    if(res != ERROR_SUCCESS)
+    {
+        ERR("Error opening HKLM\\Software\\ASIO: {}", res);
+        return E_NOINTERFACE;
+    }
+
+    auto numkeys = DWORD{};
+    auto maxkeylen = DWORD{};
+    res = RegQueryInfoKeyW(regbase.get(), nullptr, nullptr, nullptr, &numkeys, &maxkeylen, nullptr,
+        nullptr, nullptr, nullptr, nullptr, nullptr);
+    if(res != ERROR_SUCCESS)
+    {
+        ERR("Error querying HKLM\\Software\\ASIO info: {}", res);
+        return E_FAIL;
+    }
+
+    /* maxkeylen is the max number of unicode characters a subkey is. A unicode
+     * character can occupy two WCHARs, so ensure there's enough space for them
+     * and the null char.
+     */
+    auto keyname = std::vector<WCHAR>(maxkeylen*2 + 1);
+    for(DWORD i{0};i < numkeys;++i)
+    {
+        auto namelen = static_cast<DWORD>(keyname.size());
+        res = RegEnumKeyExW(regbase.get(), i, keyname.data(), &namelen, nullptr, nullptr, nullptr,
+            nullptr);
+        if(res != ERROR_SUCCESS)
+        {
+            ERR("Error querying HKLM\\Software\\ASIO subkey {}: {}", i, res);
+            continue;
+        }
+        if(namelen == 0)
+        {
+            ERR("HKLM\\Software\\ASIO subkey {} is blank?", i);
+            continue;
+        }
+        auto subkeyname = wstr_to_utf8({keyname.data(), namelen});
+
+        auto subkey = KeyPtr{};
+        res = RegOpenKeyExW(regbase.get(), keyname.data(), 0, KEY_READ, al::out_ptr(subkey));
+        if(res != ERROR_SUCCESS)
+        {
+            ERR("Error opening HKLM\\Software\\ASIO\\{}: {}", subkeyname, res);
+            continue;
+        }
+
+        auto idstr = std::array<WCHAR,48>{};
+        auto readsize = DWORD{idstr.size()*sizeof(WCHAR)};
+        res = RegGetValueW(subkey.get(), L"", L"CLSID", RRF_RT_REG_SZ, nullptr, idstr.data(),
+            &readsize);
+        if(res != ERROR_SUCCESS)
+        {
+            ERR("Failed to read HKLM\\Software\\ASIO\\{}\\CLSID: {}", subkeyname, res);
+            continue;
+        }
+        idstr.back() = 0;
+
+        auto guid = CLSID{};
+        if(auto hr = CLSIDFromString(idstr.data(), &guid); FAILED(hr))
+        {
+            ERR("Failed to parse CLSID \"{}\": {:#x}", wstr_to_utf8(idstr.data()),
+                as_unsigned(hr));
+            continue;
+        }
+
+        /* The CLSID is also used for the IID. */
+        auto iface = ComPtr<ORIOiface>{};
+        auto hr = CoCreateInstance(guid, nullptr, CLSCTX_INPROC_SERVER, guid, al::out_ptr(iface));
+        if(SUCCEEDED(hr))
+        {
+#if !ALSOFT_UWP
+            if(!iface->Init(GetForegroundWindow()))
+#else
+            if(!iface->Init(nullptr))
+#endif
+            {
+                ERR("Failed to initialize {}", subkeyname);
+                continue;
+            }
+            auto drvname = std::array<char,32>{};
+            iface->GetDriverName(drvname);
+            auto drvver = iface->GetDriverVersion();
+
+            auto &entry = gDeviceList.emplace_back();
+            entry.mDrvName = drvname.data();
+            entry.mDrvGuid = guid;
+
+            TRACE("Got {} v{}, CLSID {{{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}}}",
+                entry.mDrvName, drvver, guid.Data1, guid.Data2, guid.Data3, guid.Data4[0],
+                guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5],
+                guid.Data4[6], guid.Data4[7]);
+        }
+        else
+            ERR("Failed to create {} instance for CLSID {{{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}}}: {:#x}",
+                subkeyname.c_str(), guid.Data1, guid.Data2, guid.Data3, guid.Data4[0],
+                guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5],
+                guid.Data4[6], guid.Data4[7], as_unsigned(hr));
+    }
+
+    return S_OK;
+}
+
+
+enum class MsgType {
+    OpenDevice,
+    ResetDevice,
+    StartDevice,
+    StopDevice,
+    CloseDevice,
+
+    QuitThread
+};
+
+constexpr const char *GetMessageTypeName(MsgType type) noexcept
+{
+    switch(type)
+    {
+    case MsgType::OpenDevice: return "Open Device";
+    case MsgType::ResetDevice: return "Reset Device";
+    case MsgType::StartDevice: return "Start Device";
+    case MsgType::StopDevice: return "Stop Device";
+    case MsgType::CloseDevice: return "Close Device";
+    case MsgType::QuitThread: break;
+    }
+    return "";
+}
+
+
+/* Proxy interface used by the message handler, to ensure COM objects are used
+ * on a thread where COM is initialized.
+ */
+struct OtherIOProxy {
+    OtherIOProxy() = default;
+    OtherIOProxy(const OtherIOProxy&) = delete;
+    OtherIOProxy(OtherIOProxy&&) = delete;
+    virtual ~OtherIOProxy() = default;
+
+    void operator=(const OtherIOProxy&) = delete;
+    void operator=(OtherIOProxy&&) = delete;
+
+    virtual HRESULT openProxy(std::string_view name) = 0;
+    virtual void closeProxy() = 0;
+
+    virtual HRESULT resetProxy() = 0;
+    virtual HRESULT startProxy() = 0;
+    virtual void stopProxy() = 0;
+
+    struct Msg {
+        MsgType mType;
+        OtherIOProxy *mProxy;
+        std::string_view mParam;
+        std::promise<HRESULT> mPromise;
+
+        explicit operator bool() const noexcept { return mType != MsgType::QuitThread; }
+    };
+    static inline std::deque<Msg> mMsgQueue;
+    static inline std::mutex mMsgQueueLock;
+    static inline std::condition_variable mMsgQueueCond;
+
+    auto pushMessage(MsgType type, std::string_view param={}) -> std::future<HRESULT>
+    {
+        auto promise = std::promise<HRESULT>{};
+        auto future = std::future<HRESULT>{promise.get_future()};
+        {
+            auto msglock = std::lock_guard{mMsgQueueLock};
+            mMsgQueue.emplace_back(Msg{type, this, param, std::move(promise)});
+        }
+        mMsgQueueCond.notify_one();
+        return future;
+    }
+
+    static auto popMessage() -> Msg
+    {
+        auto lock = std::unique_lock{mMsgQueueLock};
+        mMsgQueueCond.wait(lock, []{return !mMsgQueue.empty();});
+        auto msg = Msg{std::move(mMsgQueue.front())};
+        mMsgQueue.pop_front();
+        return msg;
+    }
+
+    static void messageHandler(std::promise<HRESULT> *promise);
+};
+
+void OtherIOProxy::messageHandler(std::promise<HRESULT> *promise)
+{
+    TRACE("Starting COM message thread");
+
+    auto com = ComWrapper{COINIT_APARTMENTTHREADED};
+    if(!com)
+    {
+        WARN("Failed to initialize COM: {:#x}", as_unsigned(com.status()));
+        promise->set_value(com.status());
+        return;
+    }
+
+    auto hr = PopulateDeviceList();
+    if(FAILED(hr))
+    {
+        promise->set_value(hr);
+        return;
+    }
+
+    promise->set_value(S_OK);
+    promise = nullptr;
+
+    TRACE("Starting message loop");
+    while(Msg msg{popMessage()})
+    {
+        TRACE("Got message \"{}\" ({:#04x}, this={}, param=\"{}\")",
+            GetMessageTypeName(msg.mType), static_cast<uint>(msg.mType),
+            static_cast<void*>(msg.mProxy), msg.mParam);
+
+        switch(msg.mType)
+        {
+        case MsgType::OpenDevice:
+            hr = msg.mProxy->openProxy(msg.mParam);
+            msg.mPromise.set_value(hr);
+            continue;
+
+        case MsgType::ResetDevice:
+            hr = msg.mProxy->resetProxy();
+            msg.mPromise.set_value(hr);
+            continue;
+
+        case MsgType::StartDevice:
+            hr = msg.mProxy->startProxy();
+            msg.mPromise.set_value(hr);
+            continue;
+
+        case MsgType::StopDevice:
+            msg.mProxy->stopProxy();
+            msg.mPromise.set_value(S_OK);
+            continue;
+
+        case MsgType::CloseDevice:
+            msg.mProxy->closeProxy();
+            msg.mPromise.set_value(S_OK);
+            continue;
+
+        case MsgType::QuitThread:
+            break;
+        }
+        ERR("Unexpected message: {}", int{al::to_underlying(msg.mType)});
+        msg.mPromise.set_value(E_FAIL);
+    }
+    TRACE("Message loop finished");
+}
+
+
+struct OtherIOPlayback final : public BackendBase, OtherIOProxy {
+    explicit OtherIOPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
+    ~OtherIOPlayback() final;
+
+    void mixerProc();
+
+    void open(std::string_view name) final;
+    auto openProxy(std::string_view name) -> HRESULT final;
+    void closeProxy() final;
+    auto reset() -> bool final;
+    auto resetProxy() -> HRESULT final;
+    void start() final;
+    auto startProxy() -> HRESULT final;
+    void stop() final;
+    void stopProxy() final;
+
+    HRESULT mOpenStatus{E_FAIL};
+
+    std::atomic<bool> mKillNow{true};
+    std::thread mThread;
+};
+
+OtherIOPlayback::~OtherIOPlayback()
+{
+    if(SUCCEEDED(mOpenStatus))
+        pushMessage(MsgType::CloseDevice).wait();
+}
+
+void OtherIOPlayback::mixerProc()
+{
+    const auto restTime = milliseconds{mDevice->mUpdateSize*1000/mDevice->mSampleRate / 2};
+
+    SetRTPriority();
+    althrd_setname(GetMixerThreadName());
+
+    auto done = int64_t{0};
+    auto start = std::chrono::steady_clock::now();
+    while(!mKillNow.load(std::memory_order_acquire)
+        && mDevice->Connected.load(std::memory_order_acquire))
+    {
+        auto now = std::chrono::steady_clock::now();
+
+        /* This converts from nanoseconds to nanosamples, then to samples. */
+        const auto avail = int64_t{std::chrono::duration_cast<seconds>((now-start)
+            * mDevice->mSampleRate).count()};
+        if(avail-done < mDevice->mUpdateSize)
+        {
+            std::this_thread::sleep_for(restTime);
+            continue;
+        }
+        while(avail-done >= mDevice->mUpdateSize)
+        {
+            mDevice->renderSamples(nullptr, mDevice->mUpdateSize, 0u);
+            done += mDevice->mUpdateSize;
+        }
+
+        if(done >= mDevice->mSampleRate)
+        {
+            auto s = seconds{done/mDevice->mSampleRate};
+            start += s;
+            done -= mDevice->mSampleRate*s.count();
+        }
+    }
+}
+
+
+void OtherIOPlayback::open(std::string_view name)
+{
+    if(name.empty() && !gDeviceList.empty())
+        name = gDeviceList[0].mDrvName;
+    else
+    {
+        auto iter = std::find_if(gDeviceList.cbegin(), gDeviceList.cend(),
+            [name](const DeviceEntry &entry) { return entry.mDrvName == name; });
+        if(iter == gDeviceList.cend())
+            throw al::backend_exception{al::backend_error::NoDevice,
+                "Device name \"{}\" not found", name};
+    }
+
+    mOpenStatus = pushMessage(MsgType::OpenDevice, name).get();
+    if(FAILED(mOpenStatus))
+        throw al::backend_exception{al::backend_error::DeviceError, "Failed to open \"{}\"", name};
+
+    mDeviceName = name;
+}
+
+auto OtherIOPlayback::openProxy(std::string_view name [[maybe_unused]]) -> HRESULT
+{
+    return S_OK;
+}
+
+void OtherIOPlayback::closeProxy()
+{
+}
+
+auto OtherIOPlayback::reset() -> bool
+{
+    return SUCCEEDED(pushMessage(MsgType::ResetDevice).get());
+}
+
+auto OtherIOPlayback::resetProxy() -> HRESULT
+{
+    setDefaultWFXChannelOrder();
+    return S_OK;
+}
+
+void OtherIOPlayback::start()
+{
+    auto hr = pushMessage(MsgType::StartDevice).get();
+    if(FAILED(hr))
+        throw al::backend_exception{al::backend_error::DeviceError,
+            "Failed to start playback: {:#x}", as_unsigned(hr)};
+}
+
+auto OtherIOPlayback::startProxy() -> HRESULT
+{
+    try {
+        mKillNow.store(false, std::memory_order_release);
+        mThread = std::thread{&OtherIOPlayback::mixerProc, this};
+        return S_OK;
+    }
+    catch(std::exception& e) {
+        ERR("Failed to start mixing thread: {}", e.what());
+    }
+    return E_FAIL;
+}
+
+void OtherIOPlayback::stop()
+{
+    pushMessage(MsgType::StopDevice).wait();
+}
+
+void OtherIOPlayback::stopProxy()
+{
+    if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
+        return;
+    mThread.join();
+}
+
+} // namespace
+
+
+auto OtherIOBackendFactory::init() -> bool
+{
+    static HRESULT InitResult{E_FAIL};
+    if(FAILED(InitResult)) try
+    {
+        auto promise = std::promise<HRESULT>{};
+        auto future = promise.get_future();
+
+        std::thread{&OtherIOProxy::messageHandler, &promise}.detach();
+        InitResult = future.get();
+    }
+    catch(...) {
+    }
+
+    return SUCCEEDED(InitResult);
+}
+
+auto OtherIOBackendFactory::querySupport(BackendType type) -> bool
+{ return type == BackendType::Playback; }
+
+auto OtherIOBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
+{
+    std::vector<std::string> outnames;
+
+    switch(type)
+    {
+    case BackendType::Playback:
+        std::for_each(gDeviceList.cbegin(), gDeviceList.cend(),
+            [&outnames](const DeviceEntry &entry) { outnames.emplace_back(entry.mDrvName); });
+        break;
+
+    case BackendType::Capture:
+        break;
+    }
+
+    return outnames;
+}
+
+auto OtherIOBackendFactory::createBackend(DeviceBase *device, BackendType type) -> BackendPtr
+{
+    if(type == BackendType::Playback)
+        return BackendPtr{new OtherIOPlayback{device}};
+    return nullptr;
+}
+
+auto OtherIOBackendFactory::getFactory() -> BackendFactory&
+{
+    static auto factory = OtherIOBackendFactory{};
+    return factory;
+}
+
+auto OtherIOBackendFactory::queryEventSupport(alc::EventType, BackendType) -> alc::EventSupport
+{
+    return alc::EventSupport::NoSupport;
+}

+ 21 - 0
libs/openal-soft/alc/backends/otherio.h

@@ -0,0 +1,21 @@
+#ifndef BACKENDS_OTHERIO_H
+#define BACKENDS_OTHERIO_H
+
+#include "base.h"
+
+struct OtherIOBackendFactory final : public BackendFactory {
+public:
+    auto init() -> bool final;
+
+    auto querySupport(BackendType type) -> bool final;
+
+    auto queryEventSupport(alc::EventType eventType, BackendType type) -> alc::EventSupport final;
+
+    auto enumerate(BackendType type) -> std::vector<std::string> final;
+
+    auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
+
+    static auto getFactory() -> BackendFactory&;
+};
+
+#endif /* BACKENDS_OTHERIO_H */

Fichier diff supprimé car celui-ci est trop grand
+ 163 - 155
libs/openal-soft/alc/backends/pipewire.cpp


+ 8 - 6
libs/openal-soft/alc/backends/pipewire.h

@@ -2,24 +2,26 @@
 #define BACKENDS_PIPEWIRE_H
 #define BACKENDS_PIPEWIRE_H
 
 
 #include <string>
 #include <string>
+#include <vector>
 
 
+#include "alc/events.h"
 #include "base.h"
 #include "base.h"
 
 
 struct DeviceBase;
 struct DeviceBase;
 
 
 struct PipeWireBackendFactory final : public BackendFactory {
 struct PipeWireBackendFactory final : public BackendFactory {
 public:
 public:
-    bool init() override;
+    auto init() -> bool final;
 
 
-    bool querySupport(BackendType type) override;
+    auto querySupport(BackendType type) -> bool final;
 
 
-    alc::EventSupport queryEventSupport(alc::EventType eventType, 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_PIPEWIRE_H */
 #endif /* BACKENDS_PIPEWIRE_H */

+ 229 - 116
libs/openal-soft/alc/backends/portaudio.cpp

@@ -20,32 +20,25 @@
 
 
 #include "config.h"
 #include "config.h"
 
 
-#include "portaudio.h"
+#include "portaudio.hpp"
 
 
+#include <cmath>
 #include <cstdio>
 #include <cstdio>
 #include <cstdlib>
 #include <cstdlib>
 #include <cstring>
 #include <cstring>
 
 
-#include "albit.h"
 #include "alc/alconfig.h"
 #include "alc/alconfig.h"
-#include "alnumeric.h"
-#include "alstring.h"
 #include "core/device.h"
 #include "core/device.h"
 #include "core/logging.h"
 #include "core/logging.h"
 #include "dynload.h"
 #include "dynload.h"
 #include "ringbuffer.h"
 #include "ringbuffer.h"
 
 
-#include <portaudio.h> /* NOLINT(*-duplicate-include) Not the same header. */
+#include <portaudio.h>
 
 
 
 
 namespace {
 namespace {
 
 
-using namespace std::string_view_literals;
-
-[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "PortAudio Default"sv; }
-
-
-#ifdef HAVE_DYNLOAD
+#if HAVE_DYNLOAD
 void *pa_handle;
 void *pa_handle;
 #define MAKE_FUNC(x) decltype(x) * p##x
 #define MAKE_FUNC(x) decltype(x) * p##x
 MAKE_FUNC(Pa_Initialize);
 MAKE_FUNC(Pa_Initialize);
@@ -55,6 +48,8 @@ MAKE_FUNC(Pa_StartStream);
 MAKE_FUNC(Pa_StopStream);
 MAKE_FUNC(Pa_StopStream);
 MAKE_FUNC(Pa_OpenStream);
 MAKE_FUNC(Pa_OpenStream);
 MAKE_FUNC(Pa_CloseStream);
 MAKE_FUNC(Pa_CloseStream);
+MAKE_FUNC(Pa_GetDeviceCount);
+MAKE_FUNC(Pa_GetDeviceInfo);
 MAKE_FUNC(Pa_GetDefaultOutputDevice);
 MAKE_FUNC(Pa_GetDefaultOutputDevice);
 MAKE_FUNC(Pa_GetDefaultInputDevice);
 MAKE_FUNC(Pa_GetDefaultInputDevice);
 MAKE_FUNC(Pa_GetStreamInfo);
 MAKE_FUNC(Pa_GetStreamInfo);
@@ -68,35 +63,72 @@ MAKE_FUNC(Pa_GetStreamInfo);
 #define Pa_StopStream                  pPa_StopStream
 #define Pa_StopStream                  pPa_StopStream
 #define Pa_OpenStream                  pPa_OpenStream
 #define Pa_OpenStream                  pPa_OpenStream
 #define Pa_CloseStream                 pPa_CloseStream
 #define Pa_CloseStream                 pPa_CloseStream
+#define Pa_GetDeviceCount              pPa_GetDeviceCount
+#define Pa_GetDeviceInfo               pPa_GetDeviceInfo
 #define Pa_GetDefaultOutputDevice      pPa_GetDefaultOutputDevice
 #define Pa_GetDefaultOutputDevice      pPa_GetDefaultOutputDevice
 #define Pa_GetDefaultInputDevice       pPa_GetDefaultInputDevice
 #define Pa_GetDefaultInputDevice       pPa_GetDefaultInputDevice
 #define Pa_GetStreamInfo               pPa_GetStreamInfo
 #define Pa_GetStreamInfo               pPa_GetStreamInfo
 #endif
 #endif
 #endif
 #endif
 
 
+struct DeviceEntry {
+    std::string mName;
+    uint mPlaybackChannels{};
+    uint mCaptureChannels{};
+};
+std::vector<DeviceEntry> DeviceNames;
+
+void EnumerateDevices()
+{
+    const auto devcount = Pa_GetDeviceCount();
+    if(devcount < 0)
+    {
+        ERR("Error getting device count: {}", 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)
+        {
+            entry.mName = info->name;
+            entry.mPlaybackChannels = static_cast<uint>(std::max(info->maxOutputChannels, 0));
+            entry.mCaptureChannels = static_cast<uint>(std::max(info->maxInputChannels, 0));
+            TRACE("Device {} \"{}\": {} playback, {} capture channels", idx, entry.mName,
+                info->maxOutputChannels, info->maxInputChannels);
+        }
+        ++idx;
+    }
+}
+
+struct StreamParamsExt : public PaStreamParameters { uint updateSize; };
 
 
 struct PortPlayback final : public BackendBase {
 struct PortPlayback final : public BackendBase {
-    PortPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit PortPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
     ~PortPlayback() override;
     ~PortPlayback() override;
 
 
     int writeCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
     int writeCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
         const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) noexcept;
         const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) noexcept;
 
 
+    void createStream(PaDeviceIndex deviceid);
+
     void open(std::string_view name) override;
     void open(std::string_view name) override;
     bool reset() override;
     bool reset() override;
     void start() override;
     void start() override;
     void stop() override;
     void stop() override;
 
 
     PaStream *mStream{nullptr};
     PaStream *mStream{nullptr};
-    PaStreamParameters mParams{};
-    uint mUpdateSize{0u};
+    StreamParamsExt mParams{};
+    PaDeviceIndex mDeviceIdx{-1};
 };
 };
 
 
 PortPlayback::~PortPlayback()
 PortPlayback::~PortPlayback()
 {
 {
     PaError err{mStream ? Pa_CloseStream(mStream) : paNoError};
     PaError err{mStream ? Pa_CloseStream(mStream) : paNoError};
     if(err != paNoError)
     if(err != paNoError)
-        ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
+        ERR("Error closing stream: {}", Pa_GetErrorText(err));
     mStream = nullptr;
     mStream = nullptr;
 }
 }
 
 
@@ -110,45 +142,29 @@ int PortPlayback::writeCallback(const void*, void *outputBuffer, unsigned long f
 }
 }
 
 
 
 
-void PortPlayback::open(std::string_view name)
+void PortPlayback::createStream(PaDeviceIndex deviceid)
 {
 {
-    if(name.empty())
-        name = GetDefaultName();
-    else if(name != GetDefaultName())
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
-            al::sizei(name), name.data()};
-
-    PaStreamParameters params{};
-    auto devidopt = ConfigValueInt({}, "port", "device");
-    if(devidopt && *devidopt >= 0) params.device = *devidopt;
-    else params.device = Pa_GetDefaultOutputDevice();
-    params.suggestedLatency = mDevice->BufferSize / static_cast<double>(mDevice->Frequency);
-    params.hostApiSpecificStreamInfo = nullptr;
-
-    params.channelCount = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
+    auto &devinfo = DeviceNames.at(static_cast<uint>(deviceid));
 
 
+    auto params = StreamParamsExt{};
+    params.device = deviceid;
+    params.suggestedLatency = mDevice->mBufferSize / static_cast<double>(mDevice->mSampleRate);
+    params.hostApiSpecificStreamInfo = nullptr;
+    params.channelCount = static_cast<int>(std::min(devinfo.mPlaybackChannels,
+        mDevice->channelsFromFmt()));
     switch(mDevice->FmtType)
     switch(mDevice->FmtType)
     {
     {
-    case DevFmtByte:
-        params.sampleFormat = paInt8;
-        break;
-    case DevFmtUByte:
-        params.sampleFormat = paUInt8;
-        break;
-    case DevFmtUShort:
-        /* fall-through */
-    case DevFmtShort:
-        params.sampleFormat = paInt16;
-        break;
-    case DevFmtUInt:
-        /* fall-through */
-    case DevFmtInt:
-        params.sampleFormat = paInt32;
-        break;
-    case DevFmtFloat:
-        params.sampleFormat = paFloat32;
-        break;
+    case DevFmtByte: params.sampleFormat = paInt8; break;
+    case DevFmtUByte: params.sampleFormat = paUInt8; break;
+    case DevFmtUShort: [[fallthrough]];
+    case DevFmtShort: params.sampleFormat = paInt16; break;
+    case DevFmtUInt: [[fallthrough]];
+    case DevFmtInt: params.sampleFormat = paInt32; break;
+    case DevFmtFloat: params.sampleFormat = paFloat32; break;
     }
     }
+    params.updateSize = mDevice->mUpdateSize;
+
+    auto srate = uint{mDevice->mSampleRate};
 
 
     static constexpr auto writeCallback = [](const void *inputBuffer, void *outputBuffer,
     static constexpr auto writeCallback = [](const void *inputBuffer, void *outputBuffer,
         unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
         unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
@@ -157,55 +173,103 @@ void PortPlayback::open(std::string_view name)
         return static_cast<PortPlayback*>(userData)->writeCallback(inputBuffer, outputBuffer,
         return static_cast<PortPlayback*>(userData)->writeCallback(inputBuffer, outputBuffer,
             framesPerBuffer, timeInfo, statusFlags);
             framesPerBuffer, timeInfo, statusFlags);
     };
     };
-    PaStream *stream{};
-    while(PaError err{Pa_OpenStream(&stream, nullptr, &params, mDevice->Frequency,
-        mDevice->UpdateSize, paNoFlag, writeCallback, this)})
+    while(PaError err{Pa_OpenStream(&mStream, nullptr, &params, srate, params.updateSize, paNoFlag,
+        writeCallback, this)})
     {
     {
-        if(params.sampleFormat != paFloat32)
-            throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s",
+        if(params.updateSize != DefaultUpdateSize)
+            params.updateSize = DefaultUpdateSize;
+        else if(srate != 48000u)
+            srate = (srate != 44100u) ? 44100u : 48000u;
+        else if(params.sampleFormat != paInt16)
+            params.sampleFormat = paInt16;
+        else if(params.channelCount != 2)
+            params.channelCount = 2;
+        else
+            throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: {}",
                 Pa_GetErrorText(err)};
                 Pa_GetErrorText(err)};
-        params.sampleFormat = paInt16;
     }
     }
 
 
-    Pa_CloseStream(mStream);
-    mStream = stream;
     mParams = params;
     mParams = params;
-    mUpdateSize = mDevice->UpdateSize;
+}
+
+void PortPlayback::open(std::string_view name)
+{
+    if(DeviceNames.empty())
+        EnumerateDevices();
 
 
-    mDevice->DeviceName = name;
+    PaDeviceIndex 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.mPlaybackChannels > 0 && name == entry.mName; });
+        if(iter == DeviceNames.cend())
+            throw al::backend_exception{al::backend_error::NoDevice,
+                "Device name \"{}\" not found", name};
+        deviceid = static_cast<int>(std::distance(DeviceNames.cbegin(), iter));
+    }
+
+    createStream(deviceid);
+    mDeviceIdx = deviceid;
+
+    mDeviceName = name;
 }
 }
 
 
 bool PortPlayback::reset()
 bool PortPlayback::reset()
 {
 {
-    const PaStreamInfo *streamInfo{Pa_GetStreamInfo(mStream)};
-    mDevice->Frequency = static_cast<uint>(streamInfo->sampleRate);
-    mDevice->UpdateSize = mUpdateSize;
-
-    if(mParams.sampleFormat == paInt8)
-        mDevice->FmtType = DevFmtByte;
-    else if(mParams.sampleFormat == paUInt8)
-        mDevice->FmtType = DevFmtUByte;
-    else if(mParams.sampleFormat == paInt16)
-        mDevice->FmtType = DevFmtShort;
-    else if(mParams.sampleFormat == paInt32)
-        mDevice->FmtType = DevFmtInt;
-    else if(mParams.sampleFormat == paFloat32)
-        mDevice->FmtType = DevFmtFloat;
-    else
+    if(mStream)
     {
     {
-        ERR("Unexpected sample format: 0x%lx\n", mParams.sampleFormat);
-        return false;
+        auto err = Pa_CloseStream(mStream);
+        if(err != paNoError)
+            ERR("Error closing stream: {}", Pa_GetErrorText(err));
+        mStream = nullptr;
     }
     }
 
 
-    if(mParams.channelCount >= 2)
-        mDevice->FmtChans = DevFmtStereo;
-    else if(mParams.channelCount == 1)
-        mDevice->FmtChans = DevFmtMono;
-    else
+    createStream(mDeviceIdx);
+
+    switch(mParams.sampleFormat)
     {
     {
-        ERR("Unexpected channel count: %u\n", mParams.channelCount);
-        return false;
+    case paFloat32: mDevice->FmtType = DevFmtFloat; break;
+    case paInt32: mDevice->FmtType = DevFmtInt; break;
+    case paInt16: mDevice->FmtType = DevFmtShort; break;
+    case paInt8: mDevice->FmtType = DevFmtByte; break;
+    case paUInt8: mDevice->FmtType = DevFmtUByte; break;
+    default:
+        ERR("Unexpected PortAudio sample format: {}", mParams.sampleFormat);
+        throw al::backend_exception{al::backend_error::NoDevice, "Invalid sample format: {}",
+            mParams.sampleFormat};
+    }
+
+    if(mParams.channelCount != static_cast<int>(mDevice->channelsFromFmt()))
+    {
+        if(mParams.channelCount >= 2)
+            mDevice->FmtChans = DevFmtStereo;
+        else if(mParams.channelCount == 1)
+            mDevice->FmtChans = DevFmtMono;
+        mDevice->mAmbiOrder = 0;
+    }
+
+    const PaStreamInfo *streamInfo{Pa_GetStreamInfo(mStream)};
+    mDevice->mSampleRate = static_cast<uint>(std::lround(streamInfo->sampleRate));
+    mDevice->mUpdateSize = mParams.updateSize;
+    mDevice->mBufferSize = mDevice->mUpdateSize * 2;
+    if(streamInfo->outputLatency > 0.0f)
+    {
+        const double sampleLatency{streamInfo->outputLatency * streamInfo->sampleRate};
+        TRACE("Reported stream latency: {:f} sec ({:f} samples)", streamInfo->outputLatency,
+            sampleLatency);
+        mDevice->mBufferSize = static_cast<uint>(std::clamp(sampleLatency,
+            double(mDevice->mBufferSize), double{std::numeric_limits<int>::max()}));
     }
     }
+
     setDefaultChannelOrder();
     setDefaultChannelOrder();
 
 
     return true;
     return true;
@@ -213,22 +277,20 @@ bool PortPlayback::reset()
 
 
 void PortPlayback::start()
 void PortPlayback::start()
 {
 {
-    const PaError err{Pa_StartStream(mStream)};
-    if(err != paNoError)
-        throw al::backend_exception{al::backend_error::DeviceError, "Failed to start playback: %s",
+    if(const PaError err{Pa_StartStream(mStream)}; err != paNoError)
+        throw al::backend_exception{al::backend_error::DeviceError, "Failed to start playback: {}",
             Pa_GetErrorText(err)};
             Pa_GetErrorText(err)};
 }
 }
 
 
 void PortPlayback::stop()
 void PortPlayback::stop()
 {
 {
-    PaError err{Pa_StopStream(mStream)};
-    if(err != paNoError)
-        ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
+    if(PaError err{Pa_StopStream(mStream)}; err != paNoError)
+        ERR("Error stopping stream: {}", Pa_GetErrorText(err));
 }
 }
 
 
 
 
 struct PortCapture final : public BackendBase {
 struct PortCapture final : public BackendBase {
-    PortCapture(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit PortCapture(DeviceBase *device) noexcept : BackendBase{device} { }
     ~PortCapture() override;
     ~PortCapture() override;
 
 
     int readCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
     int readCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
@@ -250,7 +312,7 @@ PortCapture::~PortCapture()
 {
 {
     PaError err{mStream ? Pa_CloseStream(mStream) : paNoError};
     PaError err{mStream ? Pa_CloseStream(mStream) : paNoError};
     if(err != paNoError)
     if(err != paNoError)
-        ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
+        ERR("Error closing stream: {}", Pa_GetErrorText(err));
     mStream = nullptr;
     mStream = nullptr;
 }
 }
 
 
@@ -265,20 +327,35 @@ int PortCapture::readCallback(const void *inputBuffer, void*, unsigned long fram
 
 
 void PortCapture::open(std::string_view name)
 void PortCapture::open(std::string_view name)
 {
 {
+    if(DeviceNames.empty())
+        EnumerateDevices();
+
+    int deviceid{};
     if(name.empty())
     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()};
+    {
+        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.mCaptureChannels > 0 && name == entry.mName; });
+        if(iter == DeviceNames.cend())
+            throw al::backend_exception{al::backend_error::NoDevice,
+                "Device name \"{}\" not found", name};
+        deviceid = static_cast<int>(std::distance(DeviceNames.cbegin(), iter));
+    }
 
 
-    const uint samples{std::max(mDevice->BufferSize, mDevice->Frequency/10u)};
+    const uint samples{std::max(mDevice->mBufferSize, mDevice->mSampleRate/10u)};
     const uint frame_size{mDevice->frameSizeFromFmt()};
     const uint frame_size{mDevice->frameSizeFromFmt()};
 
 
     mRing = RingBuffer::Create(samples, frame_size, false);
     mRing = RingBuffer::Create(samples, frame_size, false);
 
 
-    auto devidopt = ConfigValueInt({}, "port", "capture");
-    if(devidopt && *devidopt >= 0) mParams.device = *devidopt;
-    else mParams.device = Pa_GetDefaultOutputDevice();
+    mParams.device = deviceid;
     mParams.suggestedLatency = 0.0f;
     mParams.suggestedLatency = 0.0f;
     mParams.hostApiSpecificStreamInfo = nullptr;
     mParams.hostApiSpecificStreamInfo = nullptr;
 
 
@@ -301,7 +378,7 @@ void PortCapture::open(std::string_view name)
         break;
         break;
     case DevFmtUInt:
     case DevFmtUInt:
     case DevFmtUShort:
     case DevFmtUShort:
-        throw al::backend_exception{al::backend_error::DeviceError, "%s samples not supported",
+        throw al::backend_exception{al::backend_error::DeviceError, "{} samples not supported",
             DevFmtTypeString(mDevice->FmtType)};
             DevFmtTypeString(mDevice->FmtType)};
     }
     }
     mParams.channelCount = static_cast<int>(mDevice->channelsFromFmt());
     mParams.channelCount = static_cast<int>(mDevice->channelsFromFmt());
@@ -313,29 +390,27 @@ void PortCapture::open(std::string_view name)
         return static_cast<PortCapture*>(userData)->readCallback(inputBuffer, outputBuffer,
         return static_cast<PortCapture*>(userData)->readCallback(inputBuffer, outputBuffer,
             framesPerBuffer, timeInfo, statusFlags);
             framesPerBuffer, timeInfo, statusFlags);
     };
     };
-    PaError err{Pa_OpenStream(&mStream, &mParams, nullptr, mDevice->Frequency,
+    PaError err{Pa_OpenStream(&mStream, &mParams, nullptr, mDevice->mSampleRate,
         paFramesPerBufferUnspecified, paNoFlag, readCallback, this)};
         paFramesPerBufferUnspecified, paNoFlag, readCallback, this)};
     if(err != paNoError)
     if(err != paNoError)
-        throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s",
+        throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: {}",
             Pa_GetErrorText(err)};
             Pa_GetErrorText(err)};
 
 
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 }
 }
 
 
 
 
 void PortCapture::start()
 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,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to start recording: %s", Pa_GetErrorText(err)};
+            "Failed to start recording: {}", Pa_GetErrorText(err)};
 }
 }
 
 
 void PortCapture::stop()
 void PortCapture::stop()
 {
 {
-    PaError err{Pa_StopStream(mStream)};
-    if(err != paNoError)
-        ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
+    if(PaError err{Pa_StopStream(mStream)}; err != paNoError)
+        ERR("Error stopping stream: {}", Pa_GetErrorText(err));
 }
 }
 
 
 
 
@@ -350,7 +425,7 @@ void PortCapture::captureSamples(std::byte *buffer, uint samples)
 
 
 bool PortBackendFactory::init()
 bool PortBackendFactory::init()
 {
 {
-#ifdef HAVE_DYNLOAD
+#if HAVE_DYNLOAD
     if(!pa_handle)
     if(!pa_handle)
     {
     {
 #ifdef _WIN32
 #ifdef _WIN32
@@ -383,6 +458,8 @@ bool PortBackendFactory::init()
         LOAD_FUNC(Pa_StopStream);
         LOAD_FUNC(Pa_StopStream);
         LOAD_FUNC(Pa_OpenStream);
         LOAD_FUNC(Pa_OpenStream);
         LOAD_FUNC(Pa_CloseStream);
         LOAD_FUNC(Pa_CloseStream);
+        LOAD_FUNC(Pa_GetDeviceCount);
+        LOAD_FUNC(Pa_GetDeviceInfo);
         LOAD_FUNC(Pa_GetDefaultOutputDevice);
         LOAD_FUNC(Pa_GetDefaultOutputDevice);
         LOAD_FUNC(Pa_GetDefaultInputDevice);
         LOAD_FUNC(Pa_GetDefaultInputDevice);
         LOAD_FUNC(Pa_GetStreamInfo);
         LOAD_FUNC(Pa_GetStreamInfo);
@@ -391,7 +468,7 @@ bool PortBackendFactory::init()
         const PaError err{Pa_Initialize()};
         const PaError err{Pa_Initialize()};
         if(err != paNoError)
         if(err != paNoError)
         {
         {
-            ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
+            ERR("Pa_Initialize() returned an error: {}", Pa_GetErrorText(err));
             CloseLib(pa_handle);
             CloseLib(pa_handle);
             pa_handle = nullptr;
             pa_handle = nullptr;
             return false;
             return false;
@@ -401,7 +478,7 @@ bool PortBackendFactory::init()
     const PaError err{Pa_Initialize()};
     const PaError err{Pa_Initialize()};
     if(err != paNoError)
     if(err != paNoError)
     {
     {
-        ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
+        ERR("Pa_Initialize() returned an error: {}", Pa_GetErrorText(err));
         return false;
         return false;
     }
     }
 #endif
 #endif
@@ -411,16 +488,52 @@ bool PortBackendFactory::init()
 bool PortBackendFactory::querySupport(BackendType type)
 bool PortBackendFactory::querySupport(BackendType type)
 { return (type == BackendType::Playback || type == BackendType::Capture); }
 { return (type == BackendType::Playback || type == BackendType::Capture); }
 
 
-std::string PortBackendFactory::probe(BackendType type)
+auto PortBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
 {
 {
+    std::vector<std::string> devices;
+
+    EnumerateDevices();
+    int defaultid{-1};
     switch(type)
     switch(type)
     {
     {
     case BackendType::Playback:
     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].mPlaybackChannels > 0)
+            {
+                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:
     case BackendType::Capture:
-        /* Include null char. */
-        return std::string{GetDefaultName()} + '\0';
+        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].mCaptureChannels > 0)
+            {
+                if(defaultid >= 0 && static_cast<uint>(defaultid) == i)
+                    devices.emplace(devices.cbegin(), DeviceNames[i].mName);
+                else
+                    devices.emplace_back(DeviceNames[i].mName);
+            }
+        }
+        break;
     }
     }
-    return std::string{};
+
+    return devices;
 }
 }
 
 
 BackendPtr PortBackendFactory::createBackend(DeviceBase *device, BackendType type)
 BackendPtr PortBackendFactory::createBackend(DeviceBase *device, BackendType type)

+ 0 - 19
libs/openal-soft/alc/backends/portaudio.h

@@ -1,19 +0,0 @@
-#ifndef BACKENDS_PORTAUDIO_H
-#define BACKENDS_PORTAUDIO_H
-
-#include "base.h"
-
-struct PortBackendFactory final : public BackendFactory {
-public:
-    bool init() override;
-
-    bool querySupport(BackendType type) override;
-
-    std::string probe(BackendType type) override;
-
-    BackendPtr createBackend(DeviceBase *device, BackendType type) override;
-
-    static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_PORTAUDIO_H */

+ 19 - 0
libs/openal-soft/alc/backends/portaudio.hpp

@@ -0,0 +1,19 @@
+#ifndef BACKENDS_PORTAUDIO_HPP
+#define BACKENDS_PORTAUDIO_HPP
+
+#include "base.h"
+
+struct PortBackendFactory final : public BackendFactory {
+public:
+    auto init() -> bool final;
+
+    auto querySupport(BackendType type) -> bool final;
+
+    auto enumerate(BackendType type) -> std::vector<std::string> final;
+
+    auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
+
+    static auto getFactory() -> BackendFactory&;
+};
+
+#endif /* BACKENDS_PORTAUDIO_HPP */

Fichier diff supprimé car celui-ci est trop grand
+ 291 - 223
libs/openal-soft/alc/backends/pulseaudio.cpp


+ 12 - 6
libs/openal-soft/alc/backends/pulseaudio.h

@@ -1,21 +1,27 @@
 #ifndef BACKENDS_PULSEAUDIO_H
 #ifndef BACKENDS_PULSEAUDIO_H
 #define BACKENDS_PULSEAUDIO_H
 #define BACKENDS_PULSEAUDIO_H
 
 
+#include <string>
+#include <vector>
+
+#include "alc/events.h"
 #include "base.h"
 #include "base.h"
 
 
+struct DeviceBase;
+
 class PulseBackendFactory final : public BackendFactory {
 class PulseBackendFactory final : public BackendFactory {
 public:
 public:
-    bool init() override;
+    auto init() -> bool final;
 
 
-    bool querySupport(BackendType type) override;
+    auto querySupport(BackendType type) -> bool final;
 
 
-    alc::EventSupport queryEventSupport(alc::EventType eventType, 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 */
 #endif /* BACKENDS_PULSEAUDIO_H */

+ 110 - 77
libs/openal-soft/alc/backends/sdl2.cpp

@@ -28,10 +28,8 @@
 #include <string>
 #include <string>
 #include <string_view>
 #include <string_view>
 
 
-#include "almalloc.h"
 #include "alnumeric.h"
 #include "alnumeric.h"
 #include "core/device.h"
 #include "core/device.h"
-#include "core/logging.h"
 
 
 _Pragma("GCC diagnostic push")
 _Pragma("GCC diagnostic push")
 _Pragma("GCC diagnostic ignored \"-Wold-style-cast\"")
 _Pragma("GCC diagnostic ignored \"-Wold-style-cast\"")
@@ -41,18 +39,13 @@ _Pragma("GCC diagnostic pop")
 
 
 namespace {
 namespace {
 
 
-#ifdef _WIN32
-#define DEVNAME_PREFIX "OpenAL Soft on "
-#else
-#define DEVNAME_PREFIX ""
-#endif
+using namespace std::string_view_literals;
 
 
-constexpr auto getDevicePrefix() noexcept -> std::string_view { return DEVNAME_PREFIX; }
-constexpr auto getDefaultDeviceName() noexcept -> std::string_view
-{ return DEVNAME_PREFIX "Default Device"; }
+[[nodiscard]] constexpr auto getDefaultDeviceName() noexcept -> std::string_view
+{ return "Default Device"sv; }
 
 
 struct Sdl2Backend final : public BackendBase {
 struct Sdl2Backend final : public BackendBase {
-    Sdl2Backend(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit Sdl2Backend(DeviceBase *device) noexcept : BackendBase{device} { }
     ~Sdl2Backend() override;
     ~Sdl2Backend() override;
 
 
     void audioCallback(Uint8 *stream, int len) noexcept;
     void audioCallback(Uint8 *stream, int len) noexcept;
@@ -62,13 +55,9 @@ struct Sdl2Backend final : public BackendBase {
     void start() override;
     void start() override;
     void stop() override;
     void stop() override;
 
 
+    std::string mSDLName;
     SDL_AudioDeviceID mDeviceID{0u};
     SDL_AudioDeviceID mDeviceID{0u};
     uint mFrameSize{0};
     uint mFrameSize{0};
-
-    uint mFrequency{0u};
-    DevFmtChannels mFmtChans{};
-    DevFmtType     mFmtType{};
-    uint mUpdateSize{0u};
 };
 };
 
 
 Sdl2Backend::~Sdl2Backend()
 Sdl2Backend::~Sdl2Backend()
@@ -89,7 +78,7 @@ void Sdl2Backend::open(std::string_view name)
 {
 {
     SDL_AudioSpec want{}, have{};
     SDL_AudioSpec want{}, have{};
 
 
-    want.freq = static_cast<int>(mDevice->Frequency);
+    want.freq = static_cast<int>(mDevice->mSampleRate);
     switch(mDevice->FmtType)
     switch(mDevice->FmtType)
     {
     {
     case DevFmtUByte: want.format = AUDIO_U8; break;
     case DevFmtUByte: want.format = AUDIO_U8; break;
@@ -100,8 +89,9 @@ void Sdl2Backend::open(std::string_view name)
     case DevFmtInt: want.format = AUDIO_S32SYS; break;
     case DevFmtInt: want.format = AUDIO_S32SYS; break;
     case DevFmtFloat: want.format = AUDIO_F32; break;
     case DevFmtFloat: want.format = AUDIO_F32; break;
     }
     }
-    want.channels = (mDevice->FmtChans == DevFmtMono) ? 1 : 2;
-    want.samples = static_cast<Uint16>(std::min(mDevice->UpdateSize, 8192u));
+    want.channels = static_cast<Uint8>(std::min<uint>(mDevice->channelsFromFmt(),
+        std::numeric_limits<Uint8>::max()));
+    want.samples = static_cast<Uint16>(std::min(mDevice->mUpdateSize, 8192u));
     want.callback = [](void *ptr, Uint8 *stream, int len) noexcept
     want.callback = [](void *ptr, Uint8 *stream, int len) noexcept
     { return static_cast<Sdl2Backend*>(ptr)->audioCallback(stream, len); };
     { return static_cast<Sdl2Backend*>(ptr)->audioCallback(stream, len); };
     want.userdata = this;
     want.userdata = this;
@@ -110,45 +100,21 @@ void Sdl2Backend::open(std::string_view name)
      * necessarily the first in the list.
      * necessarily the first in the list.
      */
      */
     const auto defaultDeviceName = getDefaultDeviceName();
     const auto defaultDeviceName = getDefaultDeviceName();
-    SDL_AudioDeviceID devid;
     if(name.empty() || name == defaultDeviceName)
     if(name.empty() || name == defaultDeviceName)
     {
     {
         name = defaultDeviceName;
         name = defaultDeviceName;
-        devid = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE);
+        mSDLName.clear();
+        mDeviceID = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have,
+            SDL_AUDIO_ALLOW_ANY_CHANGE);
     }
     }
     else
     else
     {
     {
-        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
-        {
-            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()};
-
-    DevFmtChannels devchans{};
-    if(have.channels >= 2)
-        devchans = DevFmtStereo;
-    else if(have.channels == 1)
-        devchans = DevFmtMono;
-    else
-    {
-        SDL_CloseAudioDevice(devid);
-        throw al::backend_exception{al::backend_error::DeviceError,
-            "Unhandled SDL channel count: %d", int{have.channels}};
+        mSDLName = name;
+        mDeviceID = SDL_OpenAudioDevice(mSDLName.c_str(), SDL_FALSE, &want, &have,
+            SDL_AUDIO_ALLOW_ANY_CHANGE);
     }
     }
+    if(!mDeviceID)
+        throw al::backend_exception{al::backend_error::NoDevice, "{}", SDL_GetError()};
 
 
     DevFmtType devtype{};
     DevFmtType devtype{};
     switch(have.format)
     switch(have.format)
@@ -160,32 +126,100 @@ void Sdl2Backend::open(std::string_view name)
     case AUDIO_S32SYS: devtype = DevFmtInt;    break;
     case AUDIO_S32SYS: devtype = DevFmtInt;    break;
     case AUDIO_F32SYS: devtype = DevFmtFloat;  break;
     case AUDIO_F32SYS: devtype = DevFmtFloat;  break;
     default:
     default:
-        SDL_CloseAudioDevice(devid);
-        throw al::backend_exception{al::backend_error::DeviceError, "Unhandled SDL format: 0x%04x",
-            have.format};
+        throw al::backend_exception{al::backend_error::DeviceError,
+            "Unhandled SDL format: {:#04x}", have.format};
     }
     }
 
 
-    if(mDeviceID)
-        SDL_CloseAudioDevice(mDeviceID);
-    mDeviceID = devid;
-
     mFrameSize = BytesFromDevFmt(devtype) * have.channels;
     mFrameSize = BytesFromDevFmt(devtype) * have.channels;
-    mFrequency = static_cast<uint>(have.freq);
-    mFmtChans = devchans;
-    mFmtType = devtype;
-    mUpdateSize = have.samples;
 
 
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 }
 }
 
 
 bool Sdl2Backend::reset()
 bool Sdl2Backend::reset()
 {
 {
-    mDevice->Frequency = mFrequency;
-    mDevice->FmtChans = mFmtChans;
-    mDevice->FmtType = mFmtType;
-    mDevice->UpdateSize = mUpdateSize;
-    mDevice->BufferSize = mUpdateSize * 2; /* SDL always (tries to) use two periods. */
+    if(mDeviceID)
+        SDL_CloseAudioDevice(mDeviceID);
+    mDeviceID = 0;
+
+    auto want = SDL_AudioSpec{};
+    want.freq = static_cast<int>(mDevice->mSampleRate);
+    switch(mDevice->FmtType)
+    {
+    case DevFmtUByte: want.format = AUDIO_U8; break;
+    case DevFmtByte: want.format = AUDIO_S8; break;
+    case DevFmtUShort: want.format = AUDIO_U16SYS; break;
+    case DevFmtShort: want.format = AUDIO_S16SYS; break;
+    case DevFmtUInt: [[fallthrough]];
+    case DevFmtInt: want.format = AUDIO_S32SYS; break;
+    case DevFmtFloat: want.format = AUDIO_F32; break;
+    }
+    want.channels = static_cast<Uint8>(std::min<uint>(mDevice->channelsFromFmt(),
+        std::numeric_limits<Uint8>::max()));
+    want.samples = static_cast<Uint16>(std::min(mDevice->mUpdateSize, 8192u));
+    want.callback = [](void *ptr, Uint8 *stream, int len) noexcept
+    { return static_cast<Sdl2Backend*>(ptr)->audioCallback(stream, len); };
+    want.userdata = this;
+
+    auto have = SDL_AudioSpec{};
+    if(mSDLName.empty())
+    {
+        mDeviceID = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have,
+            SDL_AUDIO_ALLOW_ANY_CHANGE);
+    }
+    else
+    {
+        mDeviceID = SDL_OpenAudioDevice(mSDLName.c_str(), SDL_FALSE, &want, &have,
+            SDL_AUDIO_ALLOW_ANY_CHANGE);
+    }
+    if(!mDeviceID)
+        throw al::backend_exception{al::backend_error::NoDevice, "{}", SDL_GetError()};
+
+    if(have.channels != mDevice->channelsFromFmt())
+    {
+        /* SDL guarantees these layouts for the given channel count. */
+        if(have.channels == 8)
+            mDevice->FmtChans = DevFmtX71;
+        else if(have.channels == 7)
+            mDevice->FmtChans = DevFmtX61;
+        else if(have.channels == 6)
+            mDevice->FmtChans = DevFmtX51;
+        else if(have.channels == 4)
+            mDevice->FmtChans = DevFmtQuad;
+        else if(have.channels >= 2)
+            mDevice->FmtChans = DevFmtStereo;
+        else if(have.channels == 1)
+            mDevice->FmtChans = DevFmtMono;
+        else
+            throw al::backend_exception{al::backend_error::DeviceError,
+                "Unhandled SDL channel count: {}", int{have.channels}};
+        mDevice->mAmbiOrder = 0;
+    }
+
+    switch(have.format)
+    {
+    case AUDIO_U8:     mDevice->FmtType = DevFmtUByte;  break;
+    case AUDIO_S8:     mDevice->FmtType = DevFmtByte;   break;
+    case AUDIO_U16SYS: mDevice->FmtType = DevFmtUShort; break;
+    case AUDIO_S16SYS: mDevice->FmtType = DevFmtShort;  break;
+    case AUDIO_S32SYS: mDevice->FmtType = DevFmtInt;    break;
+    case AUDIO_F32SYS: mDevice->FmtType = DevFmtFloat;  break;
+    default:
+        throw al::backend_exception{al::backend_error::DeviceError,
+            "Unhandled SDL format: {:#04x}", have.format};
+    }
+
+    mFrameSize = BytesFromDevFmt(mDevice->FmtType) * have.channels;
+
+    if(have.freq < int{MinOutputRate})
+        throw al::backend_exception{al::backend_error::DeviceError,
+            "Unhandled SDL sample rate: {}", have.freq};
+
+    mDevice->mSampleRate = static_cast<uint>(have.freq);
+    mDevice->mUpdateSize = have.samples;
+    mDevice->mBufferSize = std::max(have.size/mFrameSize, mDevice->mUpdateSize*2u);
+
     setDefaultWFXChannelOrder();
     setDefaultWFXChannelOrder();
+
     return true;
     return true;
 }
 }
 
 
@@ -209,26 +243,25 @@ bool SDL2BackendFactory::init()
 bool SDL2BackendFactory::querySupport(BackendType type)
 bool SDL2BackendFactory::querySupport(BackendType type)
 { return type == BackendType::Playback; }
 { 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)
     if(type != BackendType::Playback)
         return outnames;
         return outnames;
 
 
     int num_devices{SDL_GetNumAudioDevices(SDL_FALSE)};
     int num_devices{SDL_GetNumAudioDevices(SDL_FALSE)};
+    if(num_devices <= 0)
+        return outnames;
 
 
-    /* Includes null char. */
-    outnames += getDefaultDeviceName();
-    outnames += '\0';
+    outnames.reserve(static_cast<unsigned int>(num_devices)+1_uz);
+    outnames.emplace_back(getDefaultDeviceName());
     for(int i{0};i < num_devices;++i)
     for(int i{0};i < num_devices;++i)
     {
     {
-        outnames += getDevicePrefix();
         if(const char *name = SDL_GetAudioDeviceName(i, SDL_FALSE))
         if(const char *name = SDL_GetAudioDeviceName(i, SDL_FALSE))
-            outnames += name;
+            outnames.emplace_back(name);
         else
         else
-            outnames += "Unknown Device Name #"+std::to_string(i);
-        outnames += '\0';
+            outnames.emplace_back("Unknown Device Name #"+std::to_string(i));
     }
     }
     return outnames;
     return outnames;
 }
 }

+ 5 - 5
libs/openal-soft/alc/backends/sdl2.h

@@ -5,15 +5,15 @@
 
 
 struct SDL2BackendFactory final : public BackendFactory {
 struct SDL2BackendFactory final : public BackendFactory {
 public:
 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 */
 #endif /* BACKENDS_SDL2_H */

+ 393 - 0
libs/openal-soft/alc/backends/sdl3.cpp

@@ -0,0 +1,393 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2024 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include "sdl3.h"
+
+#include <cassert>
+#include <cstdlib>
+#include <cstring>
+#include <functional>
+#include <string>
+#include <string_view>
+
+#include "almalloc.h"
+#include "core/device.h"
+#include "core/logging.h"
+
+_Pragma("GCC diagnostic push")
+_Pragma("GCC diagnostic ignored \"-Wold-style-cast\"")
+#include "SDL3/SDL_audio.h"
+#include "SDL3/SDL_init.h"
+#include "SDL3/SDL_stdinc.h"
+_Pragma("GCC diagnostic pop")
+
+
+namespace {
+
+using namespace std::string_view_literals;
+
+_Pragma("GCC diagnostic push")
+_Pragma("GCC diagnostic ignored \"-Wold-style-cast\"")
+constexpr auto DefaultPlaybackDeviceID = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
+_Pragma("GCC diagnostic pop")
+
+
+template<typename T>
+struct SdlDeleter {
+    /* NOLINTNEXTLINE(cppcoreguidelines-no-malloc) */
+    void operator()(gsl::owner<T*> ptr) const { SDL_free(ptr); }
+};
+template<typename T>
+using unique_sdl_ptr = std::unique_ptr<T,SdlDeleter<T>>;
+
+
+struct DeviceEntry {
+    std::string mName;
+    SDL_AudioDeviceID mPhysDeviceID{};
+};
+
+std::vector<DeviceEntry> gPlaybackDevices;
+
+void EnumeratePlaybackDevices()
+{
+    auto numdevs = int{};
+    auto devicelist = unique_sdl_ptr<SDL_AudioDeviceID>{SDL_GetAudioPlaybackDevices(&numdevs)};
+    if(!devicelist || numdevs < 0)
+    {
+        ERR("Failed to get playback devices: {}", SDL_GetError());
+        return;
+    }
+
+    auto devids = al::span{devicelist.get(), static_cast<uint>(numdevs)};
+    auto newlist = std::vector<DeviceEntry>{};
+
+    newlist.reserve(devids.size());
+    std::transform(devids.begin(), devids.end(), std::back_inserter(newlist),
+        [](SDL_AudioDeviceID id)
+        {
+            auto *name = SDL_GetAudioDeviceName(id);
+            if(!name) return DeviceEntry{};
+            TRACE("Got device \"{}\", ID {}", name, id);
+            return DeviceEntry{name, id};
+        });
+
+    gPlaybackDevices.swap(newlist);
+}
+
+[[nodiscard]] constexpr auto getDefaultDeviceName() noexcept -> std::string_view
+{ return "Default Device"sv; }
+
+
+struct Sdl3Backend final : public BackendBase {
+    explicit Sdl3Backend(DeviceBase *device) noexcept : BackendBase{device} { }
+    ~Sdl3Backend() final;
+
+    void audioCallback(SDL_AudioStream *stream, int additional_amount, int total_amount) noexcept;
+
+    void open(std::string_view name) final;
+    auto reset() -> bool final;
+    void start() final;
+    void stop() final;
+
+    SDL_AudioDeviceID mDeviceID{0};
+    SDL_AudioStream *mStream{nullptr};
+    uint mNumChannels{0};
+    uint mFrameSize{0};
+    std::vector<std::byte> mBuffer;
+};
+
+Sdl3Backend::~Sdl3Backend()
+{
+    if(mStream)
+        SDL_DestroyAudioStream(mStream);
+    mStream = nullptr;
+}
+
+void Sdl3Backend::audioCallback(SDL_AudioStream *stream, int additional_amount, int total_amount)
+    noexcept
+{
+    if(additional_amount < 0)
+        additional_amount = total_amount;
+    if(additional_amount <= 0)
+        return;
+
+    const auto ulen = static_cast<unsigned int>(additional_amount);
+    assert((ulen % mFrameSize) == 0);
+
+    if(ulen > mBuffer.size())
+    {
+        mBuffer.resize(ulen);
+        std::fill(mBuffer.begin(), mBuffer.end(), (mDevice->FmtType == DevFmtUByte)
+            ? std::byte{0x80} : std::byte{});
+    }
+
+    mDevice->renderSamples(mBuffer.data(), ulen / mFrameSize, mNumChannels);
+    SDL_PutAudioStreamData(stream, mBuffer.data(), additional_amount);
+}
+
+void Sdl3Backend::open(std::string_view name)
+{
+    const auto defaultDeviceName = getDefaultDeviceName();
+    if(name.empty() || name == defaultDeviceName)
+    {
+        name = defaultDeviceName;
+        mDeviceID = DefaultPlaybackDeviceID;
+    }
+    else
+    {
+        if(gPlaybackDevices.empty())
+            EnumeratePlaybackDevices();
+
+        const auto iter = std::find_if(gPlaybackDevices.cbegin(), gPlaybackDevices.cend(),
+            [name](const DeviceEntry &entry) { return name == entry.mName; });
+        if(iter == gPlaybackDevices.cend())
+            throw al::backend_exception{al::backend_error::NoDevice, "No device named {}", name};
+
+        mDeviceID = iter->mPhysDeviceID;
+    }
+
+    mStream = SDL_OpenAudioDeviceStream(mDeviceID, nullptr, nullptr, nullptr);
+    if(!mStream)
+        throw al::backend_exception{al::backend_error::NoDevice, "{}", SDL_GetError()};
+
+    auto have = SDL_AudioSpec{};
+    auto update_size = int{};
+    if(SDL_GetAudioDeviceFormat(SDL_GetAudioStreamDevice(mStream), &have, &update_size))
+    {
+        auto devtype = mDevice->FmtType;
+        switch(have.format)
+        {
+        case SDL_AUDIO_U8:  devtype = DevFmtUByte; break;
+        case SDL_AUDIO_S8:  devtype = DevFmtByte;  break;
+        case SDL_AUDIO_S16: devtype = DevFmtShort; break;
+        case SDL_AUDIO_S32: devtype = DevFmtInt;   break;
+        case SDL_AUDIO_F32: devtype = DevFmtFloat; break;
+        default: break;
+        }
+        mDevice->FmtType = devtype;
+
+        if(have.freq >= int{MinOutputRate} && have.freq <= int{MaxOutputRate})
+            mDevice->mSampleRate = static_cast<uint>(have.freq);
+
+        /* SDL guarantees these layouts for the given channel count. */
+        if(have.channels == 8)
+            mDevice->FmtChans = DevFmtX71;
+        else if(have.channels == 7)
+            mDevice->FmtChans = DevFmtX61;
+        else if(have.channels == 6)
+            mDevice->FmtChans = DevFmtX51;
+        else if(have.channels == 4)
+            mDevice->FmtChans = DevFmtQuad;
+        else if(have.channels >= 2)
+            mDevice->FmtChans = DevFmtStereo;
+        else if(have.channels == 1)
+            mDevice->FmtChans = DevFmtMono;
+        mDevice->mAmbiOrder = 0;
+
+        mNumChannels = static_cast<uint>(have.channels);
+        mFrameSize = mDevice->bytesFromFmt() * mNumChannels;
+
+        if(update_size >= 64)
+        {
+            /* We have to assume the total buffer size is just twice the update
+             * size. SDL doesn't tell us the full end-to-end buffer latency.
+             */
+            mDevice->mUpdateSize = static_cast<uint>(update_size);
+            mDevice->mBufferSize = mDevice->mUpdateSize*2u;
+        }
+        else
+            ERR("Invalid update size from SDL stream: {}", update_size);
+    }
+    else
+        ERR("Failed to get format from SDL stream: {}", SDL_GetError());
+
+    mDeviceName = name;
+}
+
+auto Sdl3Backend::reset() -> bool
+{
+    static constexpr auto callback = [](void *ptr, SDL_AudioStream *stream, int additional_amount,
+        int total_amount) noexcept
+    {
+        return static_cast<Sdl3Backend*>(ptr)->audioCallback(stream, additional_amount,
+            total_amount);
+    };
+
+    if(mStream)
+        SDL_DestroyAudioStream(mStream);
+    mStream = nullptr;
+
+    mBuffer.clear();
+    mBuffer.shrink_to_fit();
+
+    auto want = SDL_AudioSpec{};
+    if(!SDL_GetAudioDeviceFormat(mDeviceID, &want, nullptr))
+        ERR("Failed to get device format: {}", SDL_GetError());
+
+    if(mDevice->Flags.test(FrequencyRequest) || want.freq < int{MinOutputRate})
+        want.freq = static_cast<int>(mDevice->mSampleRate);
+    if(mDevice->Flags.test(SampleTypeRequest)
+        || !(want.format == SDL_AUDIO_U8 || want.format == SDL_AUDIO_S8
+             || want.format == SDL_AUDIO_S16 || want.format == SDL_AUDIO_S32
+             || want.format == SDL_AUDIO_F32))
+    {
+        switch(mDevice->FmtType)
+        {
+        case DevFmtUByte:  want.format = SDL_AUDIO_U8;  break;
+        case DevFmtByte:   want.format = SDL_AUDIO_S8;  break;
+        case DevFmtUShort: [[fallthrough]];
+        case DevFmtShort:  want.format = SDL_AUDIO_S16; break;
+        case DevFmtUInt:   [[fallthrough]];
+        case DevFmtInt:    want.format = SDL_AUDIO_S32; break;
+        case DevFmtFloat:  want.format = SDL_AUDIO_F32; break;
+        }
+    }
+    if(mDevice->Flags.test(ChannelsRequest) || want.channels < 1)
+        want.channels = static_cast<int>(std::min<uint>(mDevice->channelsFromFmt(),
+            std::numeric_limits<int>::max()));
+
+    mStream = SDL_OpenAudioDeviceStream(mDeviceID, &want, callback, this);
+    if(!mStream)
+    {
+        /* If creating the stream failed, try again without a specific format. */
+        mStream = SDL_OpenAudioDeviceStream(mDeviceID, nullptr, callback, this);
+        if(!mStream)
+            throw al::backend_exception{al::backend_error::DeviceError,
+                "Failed to recreate stream: {}", SDL_GetError()};
+    }
+
+    auto update_size = int{};
+    auto have = SDL_AudioSpec{};
+    SDL_GetAudioDeviceFormat(SDL_GetAudioStreamDevice(mStream), &have, &update_size);
+
+    have = SDL_AudioSpec{};
+    if(!SDL_GetAudioStreamFormat(mStream, &have, nullptr))
+        throw al::backend_exception{al::backend_error::DeviceError,
+            "Failed to get stream format: {}", SDL_GetError()};
+
+    if(!mDevice->Flags.test(ChannelsRequest)
+        || (static_cast<uint>(have.channels) != mDevice->channelsFromFmt()
+            && !(mDevice->FmtChans == DevFmtStereo && have.channels >= 2)))
+    {
+        /* SDL guarantees these layouts for the given channel count. */
+        if(have.channels == 8)
+            mDevice->FmtChans = DevFmtX71;
+        else if(have.channels == 7)
+            mDevice->FmtChans = DevFmtX61;
+        else if(have.channels == 6)
+            mDevice->FmtChans = DevFmtX51;
+        else if(have.channels == 4)
+            mDevice->FmtChans = DevFmtQuad;
+        else if(have.channels >= 2)
+            mDevice->FmtChans = DevFmtStereo;
+        else if(have.channels == 1)
+            mDevice->FmtChans = DevFmtMono;
+        else
+            throw al::backend_exception{al::backend_error::DeviceError,
+                "Unhandled SDL channel count: {}", have.channels};
+        mDevice->mAmbiOrder = 0;
+    }
+    mNumChannels = static_cast<uint>(have.channels);
+
+    switch(have.format)
+    {
+    case SDL_AUDIO_U8:  mDevice->FmtType = DevFmtUByte; break;
+    case SDL_AUDIO_S8:  mDevice->FmtType = DevFmtByte;  break;
+    case SDL_AUDIO_S16: mDevice->FmtType = DevFmtShort; break;
+    case SDL_AUDIO_S32: mDevice->FmtType = DevFmtInt;   break;
+    case SDL_AUDIO_F32: mDevice->FmtType = DevFmtFloat; break;
+    default:
+        throw al::backend_exception{al::backend_error::DeviceError,
+            "Unhandled SDL format: {:#04x}", al::to_underlying(have.format)};
+    }
+
+    mFrameSize = mDevice->bytesFromFmt() * mNumChannels;
+
+    if(have.freq < int{MinOutputRate})
+        throw al::backend_exception{al::backend_error::DeviceError,
+            "Unhandled SDL sample rate: {}", have.freq};
+    mDevice->mSampleRate = static_cast<uint>(have.freq);
+
+    if(update_size >= 64)
+    {
+        mDevice->mUpdateSize = static_cast<uint>(update_size);
+        mDevice->mBufferSize = mDevice->mUpdateSize*2u;
+
+        mBuffer.resize(size_t{mDevice->mUpdateSize} * mFrameSize);
+        std::fill(mBuffer.begin(), mBuffer.end(), (mDevice->FmtType == DevFmtUByte)
+            ? std::byte{0x80} : std::byte{});
+    }
+    else
+        ERR("Invalid update size from SDL stream: {}", update_size);
+
+    setDefaultWFXChannelOrder();
+
+    return true;
+}
+
+void Sdl3Backend::start()
+{ SDL_ResumeAudioStreamDevice(mStream); }
+
+void Sdl3Backend::stop()
+{ SDL_PauseAudioStreamDevice(mStream); }
+
+} // namespace
+
+auto SDL3BackendFactory::getFactory() -> BackendFactory&
+{
+    static SDL3BackendFactory factory{};
+    return factory;
+}
+
+auto SDL3BackendFactory::init() -> bool
+{
+    if(!SDL_InitSubSystem(SDL_INIT_AUDIO))
+        return false;
+    TRACE("Current SDL3 audio driver: \"{}\"", SDL_GetCurrentAudioDriver());
+    return true;
+}
+
+auto SDL3BackendFactory::querySupport(BackendType type) -> bool
+{ return type == BackendType::Playback; }
+
+auto SDL3BackendFactory::enumerate(BackendType type) -> std::vector<std::string>
+{
+    auto outnames = std::vector<std::string>{};
+
+    if(type != BackendType::Playback)
+        return outnames;
+
+    EnumeratePlaybackDevices();
+    outnames.reserve(gPlaybackDevices.size()+1);
+    outnames.emplace_back(getDefaultDeviceName());
+    std::transform(gPlaybackDevices.begin(), gPlaybackDevices.end(), std::back_inserter(outnames),
+        std::mem_fn(&DeviceEntry::mName));
+
+    return outnames;
+}
+
+auto SDL3BackendFactory::createBackend(DeviceBase *device, BackendType type) -> BackendPtr
+{
+    if(type == BackendType::Playback)
+        return BackendPtr{new Sdl3Backend{device}};
+    return nullptr;
+}

+ 19 - 0
libs/openal-soft/alc/backends/sdl3.h

@@ -0,0 +1,19 @@
+#ifndef BACKENDS_SDL3_H
+#define BACKENDS_SDL3_H
+
+#include "base.h"
+
+struct SDL3BackendFactory final : public BackendFactory {
+public:
+    auto init() -> bool final;
+
+    auto querySupport(BackendType type) -> bool final;
+
+    auto enumerate(BackendType type) -> std::vector<std::string> final;
+
+    auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
+
+    static auto getFactory() -> BackendFactory&;
+};
+
+#endif /* BACKENDS_SDL3_H */

+ 51 - 57
libs/openal-soft/alc/backends/sndio.cpp

@@ -20,27 +20,23 @@
 
 
 #include "config.h"
 #include "config.h"
 
 
-#include "sndio.h"
+#include "sndio.hpp"
 
 
-#include <cinttypes>
 #include <cstdio>
 #include <cstdio>
 #include <cstdlib>
 #include <cstdlib>
 #include <cstring>
 #include <cstring>
-#include <functional>
 #include <poll.h>
 #include <poll.h>
 #include <system_error>
 #include <system_error>
 #include <thread>
 #include <thread>
 #include <vector>
 #include <vector>
 
 
-#include "alnumeric.h"
-#include "alstring.h"
 #include "althrd_setname.h"
 #include "althrd_setname.h"
 #include "core/device.h"
 #include "core/device.h"
 #include "core/helpers.h"
 #include "core/helpers.h"
 #include "core/logging.h"
 #include "core/logging.h"
 #include "ringbuffer.h"
 #include "ringbuffer.h"
 
 
-#include <sndio.h> /* NOLINT(*-duplicate-include) Not the same header. */
+#include <sndio.h>
 
 
 
 
 namespace {
 namespace {
@@ -56,7 +52,7 @@ struct SioPar : public sio_par {
 };
 };
 
 
 struct SndioPlayback final : public BackendBase {
 struct SndioPlayback final : public BackendBase {
-    SndioPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit SndioPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
     ~SndioPlayback() override;
     ~SndioPlayback() override;
 
 
     int mixerProc();
     int mixerProc();
@@ -102,7 +98,7 @@ int SndioPlayback::mixerProc()
             size_t wrote{sio_write(mSndHandle, buffer.data(), buffer.size())};
             size_t wrote{sio_write(mSndHandle, buffer.data(), buffer.size())};
             if(wrote > buffer.size() || wrote == 0)
             if(wrote > buffer.size() || wrote == 0)
             {
             {
-                ERR("sio_write failed: 0x%" PRIx64 "\n", wrote);
+                ERR("sio_write failed: {:#x}", wrote);
                 mDevice->handleDisconnect("Failed to write playback samples");
                 mDevice->handleDisconnect("Failed to write playback samples");
                 break;
                 break;
             }
             }
@@ -119,8 +115,8 @@ void SndioPlayback::open(std::string_view name)
     if(name.empty())
     if(name.empty())
         name = GetDefaultName();
         name = GetDefaultName();
     else if(name != GetDefaultName())
     else if(name != GetDefaultName())
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
-            al::sizei(name), name.data()};
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
+            name};
 
 
     sio_hdl *sndHandle{sio_open(nullptr, SIO_PLAY, 0)};
     sio_hdl *sndHandle{sio_open(nullptr, SIO_PLAY, 0)};
     if(!sndHandle)
     if(!sndHandle)
@@ -130,7 +126,7 @@ void SndioPlayback::open(std::string_view name)
         sio_close(mSndHandle);
         sio_close(mSndHandle);
     mSndHandle = sndHandle;
     mSndHandle = sndHandle;
 
 
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 }
 }
 
 
 bool SndioPlayback::reset()
 bool SndioPlayback::reset()
@@ -172,12 +168,12 @@ bool SndioPlayback::reset()
         par.le = SIO_LE_NATIVE;
         par.le = SIO_LE_NATIVE;
         par.msb = 1;
         par.msb = 1;
 
 
-        par.rate = mDevice->Frequency;
+        par.rate = mDevice->mSampleRate;
         par.pchan = mDevice->channelsFromFmt();
         par.pchan = mDevice->channelsFromFmt();
 
 
-        par.round = mDevice->UpdateSize;
-        par.appbufsz = mDevice->BufferSize - mDevice->UpdateSize;
-        if(!par.appbufsz) par.appbufsz = mDevice->UpdateSize;
+        par.round = mDevice->mUpdateSize;
+        par.appbufsz = mDevice->mBufferSize - mDevice->mUpdateSize;
+        if(!par.appbufsz) par.appbufsz = mDevice->mUpdateSize;
 
 
         try {
         try {
             if(!sio_setpar(mSndHandle, &par))
             if(!sio_setpar(mSndHandle, &par))
@@ -191,10 +187,10 @@ bool SndioPlayback::reset()
 
 
             if(par.bps > 1 && par.le != SIO_LE_NATIVE)
             if(par.bps > 1 && par.le != SIO_LE_NATIVE)
                 throw al::backend_exception{al::backend_error::DeviceError,
                 throw al::backend_exception{al::backend_error::DeviceError,
-                    "%s-endian samples not supported", par.le ? "Little" : "Big"};
+                    "{}-endian samples not supported", par.le ? "Little" : "Big"};
             if(par.bits < par.bps*8 && !par.msb)
             if(par.bits < par.bps*8 && !par.msb)
                 throw al::backend_exception{al::backend_error::DeviceError,
                 throw al::backend_exception{al::backend_error::DeviceError,
-                    "MSB-padded samples not supported (%u of %u bits)", par.bits, par.bps*8};
+                    "MSB-padded samples not supported ({} of {} bits)", par.bits, par.bps*8};
             if(par.pchan < 1)
             if(par.pchan < 1)
                 throw al::backend_exception{al::backend_error::DeviceError,
                 throw al::backend_exception{al::backend_error::DeviceError,
                     "No playback channels on device"};
                     "No playback channels on device"};
@@ -217,24 +213,24 @@ bool SndioPlayback::reset()
         mDevice->FmtType = (par.sig==1) ? DevFmtInt : DevFmtUInt;
         mDevice->FmtType = (par.sig==1) ? DevFmtInt : DevFmtUInt;
     else
     else
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Unhandled sample format: %s %u-bit", (par.sig?"signed":"unsigned"), par.bps*8};
+            "Unhandled sample format: {} {}-bit", (par.sig?"signed":"unsigned"), par.bps*8};
 
 
     mFrameStep = par.pchan;
     mFrameStep = par.pchan;
     if(par.pchan != mDevice->channelsFromFmt())
     if(par.pchan != mDevice->channelsFromFmt())
     {
     {
-        WARN("Got %u channel%s for %s\n", par.pchan, (par.pchan==1)?"":"s",
+        WARN("Got {} channel{} for {}", par.pchan, (par.pchan==1)?"":"s",
             DevFmtChannelsString(mDevice->FmtChans));
             DevFmtChannelsString(mDevice->FmtChans));
         if(par.pchan < 2) mDevice->FmtChans = DevFmtMono;
         if(par.pchan < 2) mDevice->FmtChans = DevFmtMono;
         else mDevice->FmtChans = DevFmtStereo;
         else mDevice->FmtChans = DevFmtStereo;
     }
     }
-    mDevice->Frequency = par.rate;
+    mDevice->mSampleRate = par.rate;
 
 
     setDefaultChannelOrder();
     setDefaultChannelOrder();
 
 
-    mDevice->UpdateSize = par.round;
-    mDevice->BufferSize = par.bufsz + par.round;
+    mDevice->mUpdateSize = par.round;
+    mDevice->mBufferSize = par.bufsz + par.round;
 
 
-    mBuffer.resize(size_t{mDevice->UpdateSize} * par.pchan*par.bps);
+    mBuffer.resize(size_t{mDevice->mUpdateSize} * par.pchan*par.bps);
     if(par.sig == 1)
     if(par.sig == 1)
         std::fill(mBuffer.begin(), mBuffer.end(), std::byte{});
         std::fill(mBuffer.begin(), mBuffer.end(), std::byte{});
     else if(par.bits == 8)
     else if(par.bits == 8)
@@ -254,12 +250,12 @@ void SndioPlayback::start()
 
 
     try {
     try {
         mKillNow.store(false, std::memory_order_release);
         mKillNow.store(false, std::memory_order_release);
-        mThread = std::thread{std::mem_fn(&SndioPlayback::mixerProc), this};
+        mThread = std::thread{&SndioPlayback::mixerProc, this};
     }
     }
     catch(std::exception& e) {
     catch(std::exception& e) {
         sio_stop(mSndHandle);
         sio_stop(mSndHandle);
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to start mixing thread: %s", e.what()};
+            "Failed to start mixing thread: {}", e.what()};
     }
     }
 }
 }
 
 
@@ -270,7 +266,7 @@ void SndioPlayback::stop()
     mThread.join();
     mThread.join();
 
 
     if(!sio_stop(mSndHandle))
     if(!sio_stop(mSndHandle))
-        ERR("Error stopping device\n");
+        ERR("Error stopping device");
 }
 }
 
 
 
 
@@ -280,7 +276,7 @@ void SndioPlayback::stop()
  * capture buffer sizes apps may request.
  * capture buffer sizes apps may request.
  */
  */
 struct SndioCapture final : public BackendBase {
 struct SndioCapture final : public BackendBase {
-    SndioCapture(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit SndioCapture(DeviceBase *device) noexcept : BackendBase{device} { }
     ~SndioCapture() override;
     ~SndioCapture() override;
 
 
     int recordProc();
     int recordProc();
@@ -316,7 +312,7 @@ int SndioCapture::recordProc()
     int nfds_pre{sio_nfds(mSndHandle)};
     int nfds_pre{sio_nfds(mSndHandle)};
     if(nfds_pre <= 0)
     if(nfds_pre <= 0)
     {
     {
-        mDevice->handleDisconnect("Incorrect return value from sio_nfds(): %d", nfds_pre);
+        mDevice->handleDisconnect("Incorrect return value from sio_nfds(): {}", nfds_pre);
         return 1;
         return 1;
     }
     }
 
 
@@ -329,15 +325,14 @@ int SndioCapture::recordProc()
         const int nfds{sio_pollfd(mSndHandle, fds.data(), POLLIN)};
         const int nfds{sio_pollfd(mSndHandle, fds.data(), POLLIN)};
         if(nfds <= 0)
         if(nfds <= 0)
         {
         {
-            mDevice->handleDisconnect("Failed to get polling fds: %d", nfds);
+            mDevice->handleDisconnect("Failed to get polling fds: {}", nfds);
             break;
             break;
         }
         }
         int pollres{::poll(fds.data(), fds.size(), 2000)};
         int pollres{::poll(fds.data(), fds.size(), 2000)};
         if(pollres < 0)
         if(pollres < 0)
         {
         {
             if(errno == EINTR) continue;
             if(errno == EINTR) continue;
-            mDevice->handleDisconnect("Poll error: %s",
-                std::generic_category().message(errno).c_str());
+            mDevice->handleDisconnect("Poll error: {}", std::generic_category().message(errno));
             break;
             break;
         }
         }
         if(pollres == 0)
         if(pollres == 0)
@@ -353,7 +348,7 @@ int SndioCapture::recordProc()
             continue;
             continue;
 
 
         auto data = mRing->getWriteVector();
         auto data = mRing->getWriteVector();
-        al::span<std::byte> buffer{data.first.buf, data.first.len*frameSize};
+        al::span<std::byte> buffer{data[0].buf, data[0].len*frameSize};
         while(!buffer.empty())
         while(!buffer.empty())
         {
         {
             size_t got{sio_read(mSndHandle, buffer.data(), buffer.size())};
             size_t got{sio_read(mSndHandle, buffer.data(), buffer.size())};
@@ -361,8 +356,8 @@ int SndioCapture::recordProc()
                 break;
                 break;
             if(got > buffer.size())
             if(got > buffer.size())
             {
             {
-                ERR("sio_read failed: 0x%" PRIx64 "\n", got);
-                mDevice->handleDisconnect("sio_read failed: 0x%" PRIx64, got);
+                ERR("sio_read failed: {:#x}", got);
+                mDevice->handleDisconnect("sio_read failed: {:#x}", got);
                 break;
                 break;
             }
             }
 
 
@@ -371,7 +366,7 @@ int SndioCapture::recordProc()
             if(buffer.empty())
             if(buffer.empty())
             {
             {
                 data = mRing->getWriteVector();
                 data = mRing->getWriteVector();
-                buffer = {data.first.buf, data.first.len*frameSize};
+                buffer = {data[0].buf, data[0].len*frameSize};
             }
             }
         }
         }
         if(buffer.empty())
         if(buffer.empty())
@@ -391,8 +386,8 @@ void SndioCapture::open(std::string_view name)
     if(name.empty())
     if(name.empty())
         name = GetDefaultName();
         name = GetDefaultName();
     else if(name != GetDefaultName())
     else if(name != GetDefaultName())
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
-            al::sizei(name), name.data()};
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
+            name};
 
 
     mSndHandle = sio_open(nullptr, SIO_REC, true);
     mSndHandle = sio_open(nullptr, SIO_REC, true);
     if(mSndHandle == nullptr)
     if(mSndHandle == nullptr)
@@ -427,16 +422,16 @@ void SndioCapture::open(std::string_view name)
         break;
         break;
     case DevFmtFloat:
     case DevFmtFloat:
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "%s capture samples not supported", DevFmtTypeString(mDevice->FmtType)};
+            "{} capture samples not supported", DevFmtTypeString(mDevice->FmtType)};
     }
     }
     par.bps = SIO_BPS(par.bits);
     par.bps = SIO_BPS(par.bits);
     par.le = SIO_LE_NATIVE;
     par.le = SIO_LE_NATIVE;
     par.msb = 1;
     par.msb = 1;
     par.rchan = mDevice->channelsFromFmt();
     par.rchan = mDevice->channelsFromFmt();
-    par.rate = mDevice->Frequency;
+    par.rate = mDevice->mSampleRate;
 
 
-    par.appbufsz = std::max(mDevice->BufferSize, mDevice->Frequency/10u);
-    par.round = std::min(par.appbufsz/2u, mDevice->Frequency/40u);
+    par.appbufsz = std::max(mDevice->mBufferSize, mDevice->mSampleRate/10u);
+    par.round = std::min(par.appbufsz/2u, mDevice->mSampleRate/40u);
 
 
     if(!sio_setpar(mSndHandle, &par) || !sio_getpar(mSndHandle, &par))
     if(!sio_setpar(mSndHandle, &par) || !sio_getpar(mSndHandle, &par))
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
@@ -444,10 +439,10 @@ void SndioCapture::open(std::string_view name)
 
 
     if(par.bps > 1 && par.le != SIO_LE_NATIVE)
     if(par.bps > 1 && par.le != SIO_LE_NATIVE)
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "%s-endian samples not supported", par.le ? "Little" : "Big"};
+            "{}-endian samples not supported", par.le ? "Little" : "Big"};
     if(par.bits < par.bps*8 && !par.msb)
     if(par.bits < par.bps*8 && !par.msb)
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Padded samples not supported (got %u of %u bits)", par.bits, par.bps*8};
+            "Padded samples not supported (got {} of {} bits)", par.bits, par.bps*8};
 
 
     auto match_fmt = [](DevFmtType fmttype, const sio_par &p) -> bool
     auto match_fmt = [](DevFmtType fmttype, const sio_par &p) -> bool
     {
     {
@@ -459,19 +454,19 @@ void SndioCapture::open(std::string_view name)
             || (fmttype == DevFmtUInt && p.bps == 4 && p.sig == 0);
             || (fmttype == DevFmtUInt && p.bps == 4 && p.sig == 0);
     };
     };
     if(!match_fmt(mDevice->FmtType, par) || mDevice->channelsFromFmt() != par.rchan
     if(!match_fmt(mDevice->FmtType, par) || mDevice->channelsFromFmt() != par.rchan
-        || mDevice->Frequency != par.rate)
+        || mDevice->mSampleRate != par.rate)
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to set format %s %s %uhz, got %c%u %u-channel %uhz instead",
+            "Failed to set format {} {} {}hz, got {}{} {}-channel {}hz instead",
             DevFmtTypeString(mDevice->FmtType), DevFmtChannelsString(mDevice->FmtChans),
             DevFmtTypeString(mDevice->FmtType), DevFmtChannelsString(mDevice->FmtChans),
-            mDevice->Frequency, par.sig?'s':'u', par.bps*8, par.rchan, par.rate};
+            mDevice->mSampleRate, par.sig?'s':'u', par.bps*8, par.rchan, par.rate};
 
 
-    mRing = RingBuffer::Create(mDevice->BufferSize, size_t{par.bps}*par.rchan, false);
-    mDevice->BufferSize = static_cast<uint>(mRing->writeSpace());
-    mDevice->UpdateSize = par.round;
+    mRing = RingBuffer::Create(mDevice->mBufferSize, size_t{par.bps}*par.rchan, false);
+    mDevice->mBufferSize = static_cast<uint>(mRing->writeSpace());
+    mDevice->mUpdateSize = par.round;
 
 
     setDefaultChannelOrder();
     setDefaultChannelOrder();
 
 
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 }
 }
 
 
 void SndioCapture::start()
 void SndioCapture::start()
@@ -481,12 +476,12 @@ void SndioCapture::start()
 
 
     try {
     try {
         mKillNow.store(false, std::memory_order_release);
         mKillNow.store(false, std::memory_order_release);
-        mThread = std::thread{std::mem_fn(&SndioCapture::recordProc), this};
+        mThread = std::thread{&SndioCapture::recordProc, this};
     }
     }
     catch(std::exception& e) {
     catch(std::exception& e) {
         sio_stop(mSndHandle);
         sio_stop(mSndHandle);
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to start capture thread: %s", e.what()};
+            "Failed to start capture thread: {}", e.what()};
     }
     }
 }
 }
 
 
@@ -497,7 +492,7 @@ void SndioCapture::stop()
     mThread.join();
     mThread.join();
 
 
     if(!sio_stop(mSndHandle))
     if(!sio_stop(mSndHandle))
-        ERR("Error stopping device\n");
+        ERR("Error stopping device");
 }
 }
 
 
 void SndioCapture::captureSamples(std::byte *buffer, uint samples)
 void SndioCapture::captureSamples(std::byte *buffer, uint samples)
@@ -520,16 +515,15 @@ bool SndIOBackendFactory::init()
 bool SndIOBackendFactory::querySupport(BackendType type)
 bool SndIOBackendFactory::querySupport(BackendType type)
 { return (type == BackendType::Playback || type == BackendType::Capture); }
 { return (type == BackendType::Playback || type == BackendType::Capture); }
 
 
-std::string SndIOBackendFactory::probe(BackendType type)
+auto SndIOBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
 {
 {
     switch(type)
     switch(type)
     {
     {
     case BackendType::Playback:
     case BackendType::Playback:
     case BackendType::Capture:
     case BackendType::Capture:
-        /* Include null char. */
-        return std::string{GetDefaultName()} + '\0';
+        return std::vector{std::string{GetDefaultName()}};
     }
     }
-    return std::string{};
+    return {};
 }
 }
 
 
 BackendPtr SndIOBackendFactory::createBackend(DeviceBase *device, BackendType type)
 BackendPtr SndIOBackendFactory::createBackend(DeviceBase *device, BackendType type)

+ 0 - 19
libs/openal-soft/alc/backends/sndio.h

@@ -1,19 +0,0 @@
-#ifndef BACKENDS_SNDIO_H
-#define BACKENDS_SNDIO_H
-
-#include "base.h"
-
-struct SndIOBackendFactory final : public BackendFactory {
-public:
-    bool init() override;
-
-    bool querySupport(BackendType type) override;
-
-    std::string probe(BackendType type) override;
-
-    BackendPtr createBackend(DeviceBase *device, BackendType type) override;
-
-    static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_SNDIO_H */

+ 19 - 0
libs/openal-soft/alc/backends/sndio.hpp

@@ -0,0 +1,19 @@
+#ifndef BACKENDS_SNDIO_HPP
+#define BACKENDS_SNDIO_HPP
+
+#include "base.h"
+
+struct SndIOBackendFactory final : public BackendFactory {
+public:
+    auto init() -> bool final;
+
+    auto querySupport(BackendType type) -> bool final;
+
+    auto enumerate(BackendType type) -> std::vector<std::string> final;
+
+    auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
+
+    static auto getFactory() -> BackendFactory&;
+};
+
+#endif /* BACKENDS_SNDIO_HPP */

+ 26 - 26
libs/openal-soft/alc/backends/solaris.cpp

@@ -60,7 +60,7 @@ std::string solaris_driver{"/dev/audio"};
 
 
 
 
 struct SolarisBackend final : public BackendBase {
 struct SolarisBackend final : public BackendBase {
-    SolarisBackend(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit SolarisBackend(DeviceBase *device) noexcept : BackendBase{device} { }
     ~SolarisBackend() override;
     ~SolarisBackend() override;
 
 
     int mixerProc();
     int mixerProc();
@@ -106,13 +106,13 @@ int SolarisBackend::mixerProc()
         {
         {
             if(errno == EINTR || errno == EAGAIN)
             if(errno == EINTR || errno == EAGAIN)
                 continue;
                 continue;
-            ERR("poll failed: %s\n", strerror(errno));
-            mDevice->handleDisconnect("Failed to wait for playback buffer: %s", strerror(errno));
+            ERR("poll failed: {}", strerror(errno));
+            mDevice->handleDisconnect("Failed to wait for playback buffer: {}", strerror(errno));
             break;
             break;
         }
         }
         else if(pret == 0)
         else if(pret == 0)
         {
         {
-            WARN("poll timeout\n");
+            WARN("poll timeout");
             continue;
             continue;
         }
         }
 
 
@@ -126,8 +126,8 @@ int SolarisBackend::mixerProc()
             {
             {
                 if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
                 if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
                     continue;
                     continue;
-                ERR("write failed: %s\n", strerror(errno));
-                mDevice->handleDisconnect("Failed to write playback samples: %s", strerror(errno));
+                ERR("write failed: {}", strerror(errno));
+                mDevice->handleDisconnect("Failed to write playback samples: {}", strerror(errno));
                 break;
                 break;
             }
             }
 
 
@@ -144,19 +144,19 @@ void SolarisBackend::open(std::string_view name)
     if(name.empty())
     if(name.empty())
         name = GetDefaultName();
         name = GetDefaultName();
     else if(name != GetDefaultName())
     else if(name != GetDefaultName())
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
-            al::sizei(name), name.data()};
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
+            name};
 
 
     int fd{::open(solaris_driver.c_str(), O_WRONLY)};
     int fd{::open(solaris_driver.c_str(), O_WRONLY)};
     if(fd == -1)
     if(fd == -1)
-        throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s",
-            solaris_driver.c_str(), strerror(errno)};
+        throw al::backend_exception{al::backend_error::NoDevice, "Could not open {}: {}",
+            solaris_driver, strerror(errno)};
 
 
     if(mFd != -1)
     if(mFd != -1)
         ::close(mFd);
         ::close(mFd);
     mFd = fd;
     mFd = fd;
 
 
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 }
 }
 
 
 bool SolarisBackend::reset()
 bool SolarisBackend::reset()
@@ -164,7 +164,7 @@ bool SolarisBackend::reset()
     audio_info_t info;
     audio_info_t info;
     AUDIO_INITINFO(&info);
     AUDIO_INITINFO(&info);
 
 
-    info.play.sample_rate = mDevice->Frequency;
+    info.play.sample_rate = mDevice->mSampleRate;
     info.play.channels = mDevice->channelsFromFmt();
     info.play.channels = mDevice->channelsFromFmt();
     switch(mDevice->FmtType)
     switch(mDevice->FmtType)
     {
     {
@@ -187,11 +187,11 @@ bool SolarisBackend::reset()
         info.play.encoding = AUDIO_ENCODING_LINEAR;
         info.play.encoding = AUDIO_ENCODING_LINEAR;
         break;
         break;
     }
     }
-    info.play.buffer_size = mDevice->BufferSize * mDevice->frameSizeFromFmt();
+    info.play.buffer_size = mDevice->mBufferSize * mDevice->frameSizeFromFmt();
 
 
     if(ioctl(mFd, AUDIO_SETINFO, &info) < 0)
     if(ioctl(mFd, AUDIO_SETINFO, &info) < 0)
     {
     {
-        ERR("ioctl failed: %s\n", strerror(errno));
+        ERR("ioctl failed: {}", strerror(errno));
         return false;
         return false;
     }
     }
 
 
@@ -203,7 +203,7 @@ bool SolarisBackend::reset()
             mDevice->FmtChans = DevFmtMono;
             mDevice->FmtChans = DevFmtMono;
         else
         else
             throw al::backend_exception{al::backend_error::DeviceError,
             throw al::backend_exception{al::backend_error::DeviceError,
-                "Got %u device channels", info.play.channels};
+                "Got {} device channels", info.play.channels};
     }
     }
 
 
     if(info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8)
     if(info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8)
@@ -216,20 +216,20 @@ bool SolarisBackend::reset()
         mDevice->FmtType = DevFmtInt;
         mDevice->FmtType = DevFmtInt;
     else
     else
     {
     {
-        ERR("Got unhandled sample type: %d (0x%x)\n", info.play.precision, info.play.encoding);
+        ERR("Got unhandled sample type: {} ({:#x})", info.play.precision, info.play.encoding);
         return false;
         return false;
     }
     }
 
 
     uint frame_size{mDevice->bytesFromFmt() * info.play.channels};
     uint frame_size{mDevice->bytesFromFmt() * info.play.channels};
     mFrameStep = info.play.channels;
     mFrameStep = info.play.channels;
-    mDevice->Frequency = info.play.sample_rate;
-    mDevice->BufferSize = info.play.buffer_size / frame_size;
+    mDevice->mSampleRate = info.play.sample_rate;
+    mDevice->mBufferSize = info.play.buffer_size / frame_size;
     /* How to get the actual period size/count? */
     /* How to get the actual period size/count? */
-    mDevice->UpdateSize = mDevice->BufferSize / 2;
+    mDevice->mUpdateSize = mDevice->mBufferSize / 2;
 
 
     setDefaultChannelOrder();
     setDefaultChannelOrder();
 
 
-    mBuffer.resize(mDevice->UpdateSize * size_t{frame_size});
+    mBuffer.resize(mDevice->mUpdateSize * size_t{frame_size});
     std::fill(mBuffer.begin(), mBuffer.end(), std::byte{});
     std::fill(mBuffer.begin(), mBuffer.end(), std::byte{});
 
 
     return true;
     return true;
@@ -239,11 +239,11 @@ void SolarisBackend::start()
 {
 {
     try {
     try {
         mKillNow.store(false, std::memory_order_release);
         mKillNow.store(false, std::memory_order_release);
-        mThread = std::thread{std::mem_fn(&SolarisBackend::mixerProc), this};
+        mThread = std::thread{&SolarisBackend::mixerProc, this};
     }
     }
     catch(std::exception& e) {
     catch(std::exception& e) {
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to start mixing thread: %s", e.what()};
+            "Failed to start mixing thread: {}", e.what()};
     }
     }
 }
 }
 
 
@@ -254,7 +254,7 @@ void SolarisBackend::stop()
     mThread.join();
     mThread.join();
 
 
     if(ioctl(mFd, AUDIO_DRAIN) < 0)
     if(ioctl(mFd, AUDIO_DRAIN) < 0)
-        ERR("Error draining device: %s\n", strerror(errno));
+        ERR("Error draining device: {}", strerror(errno));
 }
 }
 
 
 } // namespace
 } // namespace
@@ -275,19 +275,19 @@ bool SolarisBackendFactory::init()
 bool SolarisBackendFactory::querySupport(BackendType type)
 bool SolarisBackendFactory::querySupport(BackendType type)
 { return type == BackendType::Playback; }
 { return type == BackendType::Playback; }
 
 
-std::string SolarisBackendFactory::probe(BackendType type)
+auto SolarisBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
 {
 {
     switch(type)
     switch(type)
     {
     {
     case BackendType::Playback:
     case BackendType::Playback:
         if(struct stat buf{}; stat(solaris_driver.c_str(), &buf) == 0)
         if(struct stat buf{}; stat(solaris_driver.c_str(), &buf) == 0)
-            return std::string{GetDefaultName()} + '\0';
+            return std::vector{std::string{GetDefaultName()}};
         break;
         break;
 
 
     case BackendType::Capture:
     case BackendType::Capture:
         break;
         break;
     }
     }
-    return std::string{};
+    return {};
 }
 }
 
 
 BackendPtr SolarisBackendFactory::createBackend(DeviceBase *device, BackendType type)
 BackendPtr SolarisBackendFactory::createBackend(DeviceBase *device, BackendType type)

+ 5 - 5
libs/openal-soft/alc/backends/solaris.h

@@ -5,15 +5,15 @@
 
 
 struct SolarisBackendFactory final : public BackendFactory {
 struct SolarisBackendFactory final : public BackendFactory {
 public:
 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 */
 #endif /* BACKENDS_SOLARIS_H */

Fichier diff supprimé car celui-ci est trop grand
+ 634 - 475
libs/openal-soft/alc/backends/wasapi.cpp


+ 6 - 6
libs/openal-soft/alc/backends/wasapi.h

@@ -5,17 +5,17 @@
 
 
 struct WasapiBackendFactory final : public BackendFactory {
 struct WasapiBackendFactory final : public BackendFactory {
 public:
 public:
-    bool init() override;
+    auto init() -> bool final;
 
 
-    bool querySupport(BackendType type) override;
+    auto querySupport(BackendType type) -> bool final;
 
 
-    alc::EventSupport queryEventSupport(alc::EventType eventType, 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_WASAPI_H */
 #endif /* BACKENDS_WASAPI_H */

+ 50 - 45
libs/openal-soft/alc/backends/wave.cpp

@@ -30,7 +30,6 @@
 #include <cstdio>
 #include <cstdio>
 #include <cstring>
 #include <cstring>
 #include <exception>
 #include <exception>
-#include <functional>
 #include <system_error>
 #include <system_error>
 #include <thread>
 #include <thread>
 #include <vector>
 #include <vector>
@@ -39,12 +38,9 @@
 #include "alc/alconfig.h"
 #include "alc/alconfig.h"
 #include "almalloc.h"
 #include "almalloc.h"
 #include "alnumeric.h"
 #include "alnumeric.h"
-#include "alstring.h"
 #include "althrd_setname.h"
 #include "althrd_setname.h"
 #include "core/device.h"
 #include "core/device.h"
-#include "core/helpers.h"
 #include "core/logging.h"
 #include "core/logging.h"
-#include "opthelpers.h"
 #include "strutils.h"
 #include "strutils.h"
 
 
 
 
@@ -99,7 +95,7 @@ void fwrite32le(uint val, FILE *f)
 
 
 
 
 struct WaveBackend final : public BackendBase {
 struct WaveBackend final : public BackendBase {
-    WaveBackend(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit WaveBackend(DeviceBase *device) noexcept : BackendBase{device} { }
     ~WaveBackend() override;
     ~WaveBackend() override;
 
 
     int mixerProc();
     int mixerProc();
@@ -122,7 +118,7 @@ WaveBackend::~WaveBackend() = default;
 
 
 int WaveBackend::mixerProc()
 int WaveBackend::mixerProc()
 {
 {
-    const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2};
+    const milliseconds restTime{mDevice->mUpdateSize*1000/mDevice->mSampleRate / 2};
 
 
     althrd_setname(GetMixerThreadName());
     althrd_setname(GetMixerThreadName());
 
 
@@ -137,17 +133,17 @@ int WaveBackend::mixerProc()
         auto now = std::chrono::steady_clock::now();
         auto now = std::chrono::steady_clock::now();
 
 
         /* This converts from nanoseconds to nanosamples, then to samples. */
         /* This converts from nanoseconds to nanosamples, then to samples. */
-        int64_t avail{std::chrono::duration_cast<seconds>((now-start) *
-            mDevice->Frequency).count()};
-        if(avail-done < mDevice->UpdateSize)
+        const auto avail = int64_t{std::chrono::duration_cast<seconds>((now-start) *
+            mDevice->mSampleRate).count()};
+        if(avail-done < mDevice->mUpdateSize)
         {
         {
             std::this_thread::sleep_for(restTime);
             std::this_thread::sleep_for(restTime);
             continue;
             continue;
         }
         }
-        while(avail-done >= mDevice->UpdateSize)
+        while(avail-done >= mDevice->mUpdateSize)
         {
         {
-            mDevice->renderSamples(mBuffer.data(), mDevice->UpdateSize, frameStep);
-            done += mDevice->UpdateSize;
+            mDevice->renderSamples(mBuffer.data(), mDevice->mUpdateSize, frameStep);
+            done += mDevice->mUpdateSize;
 
 
             if(al::endian::native != al::endian::little)
             if(al::endian::native != al::endian::little)
             {
             {
@@ -170,10 +166,10 @@ int WaveBackend::mixerProc()
                 }
                 }
             }
             }
 
 
-            const size_t fs{fwrite(mBuffer.data(), frameSize, mDevice->UpdateSize, mFile.get())};
-            if(fs < mDevice->UpdateSize || ferror(mFile.get()))
+            const size_t fs{fwrite(mBuffer.data(), frameSize, mDevice->mUpdateSize, mFile.get())};
+            if(fs < mDevice->mUpdateSize || ferror(mFile.get()))
             {
             {
-                ERR("Error writing to file\n");
+                ERR("Error writing to file");
                 mDevice->handleDisconnect("Failed to write playback samples");
                 mDevice->handleDisconnect("Failed to write playback samples");
                 break;
                 break;
             }
             }
@@ -184,10 +180,10 @@ int WaveBackend::mixerProc()
          * and current time from growing too large, while maintaining the
          * and current time from growing too large, while maintaining the
          * correct number of samples to render.
          * correct number of samples to render.
          */
          */
-        if(done >= mDevice->Frequency)
+        if(done >= mDevice->mSampleRate)
         {
         {
-            seconds s{done/mDevice->Frequency};
-            done %= mDevice->Frequency;
+            seconds s{done/mDevice->mSampleRate};
+            done %= mDevice->mSampleRate;
             start += s;
             start += s;
         }
         }
     }
     }
@@ -204,8 +200,8 @@ void WaveBackend::open(std::string_view name)
     if(name.empty())
     if(name.empty())
         name = GetDeviceName();
         name = GetDeviceName();
     else if(name != GetDeviceName())
     else if(name != GetDeviceName())
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
-            al::sizei(name), name.data()};
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
+            name};
 
 
     /* There's only one "device", so if it's already open, we're done. */
     /* There's only one "device", so if it's already open, we're done. */
     if(mFile) return;
     if(mFile) return;
@@ -219,20 +215,14 @@ void WaveBackend::open(std::string_view name)
     mFile = FilePtr{fopen(fname->c_str(), "wb")};
     mFile = FilePtr{fopen(fname->c_str(), "wb")};
 #endif
 #endif
     if(!mFile)
     if(!mFile)
-        throw al::backend_exception{al::backend_error::DeviceError, "Could not open file '%s': %s",
-            fname->c_str(), std::generic_category().message(errno).c_str()};
+        throw al::backend_exception{al::backend_error::DeviceError, "Could not open file '{}': {}",
+            *fname, std::generic_category().message(errno)};
 
 
-    mDevice->DeviceName = name;
+    mDeviceName = name;
 }
 }
 
 
 bool WaveBackend::reset()
 bool WaveBackend::reset()
 {
 {
-    uint channels{0}, bytes{0}, chanmask{0};
-    bool isbformat{false};
-
-    fseek(mFile.get(), 0, SEEK_SET);
-    clearerr(mFile.get());
-
     if(GetConfigValueBool({}, "wave", "bformat", false))
     if(GetConfigValueBool({}, "wave", "bformat", false))
     {
     {
         mDevice->FmtChans = DevFmtAmbi3D;
         mDevice->FmtChans = DevFmtAmbi3D;
@@ -256,6 +246,8 @@ bool WaveBackend::reset()
     case DevFmtFloat:
     case DevFmtFloat:
         break;
         break;
     }
     }
+    auto chanmask = 0u;
+    auto isbformat = false;
     switch(mDevice->FmtChans)
     switch(mDevice->FmtChans)
     {
     {
     case DevFmtMono:   chanmask = 0x04; break;
     case DevFmtMono:   chanmask = 0x04; break;
@@ -264,6 +256,9 @@ bool WaveBackend::reset()
     case DevFmtX51: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400; break;
     case DevFmtX51: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400; break;
     case DevFmtX61: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 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 DevFmtX71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
+    case DevFmtX7144:
+        mDevice->FmtChans = DevFmtX714;
+        [[fallthrough]];
     case DevFmtX714:
     case DevFmtX714:
         chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400 | 0x1000 | 0x4000
         chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400 | 0x1000 | 0x4000
             | 0x8000 | 0x20000;
             | 0x8000 | 0x20000;
@@ -279,13 +274,24 @@ bool WaveBackend::reset()
         chanmask = 0;
         chanmask = 0;
         break;
         break;
     }
     }
-    bytes = mDevice->bytesFromFmt();
-    channels = mDevice->channelsFromFmt();
+    const auto bytes = mDevice->bytesFromFmt();
+    const auto channels = mDevice->channelsFromFmt();
 
 
-    rewind(mFile.get());
+    if(fseek(mFile.get(), 0, SEEK_CUR) != 0)
+    {
+        /* ESPIPE means the underlying file isn't seekable, which is fine for
+         * piped output.
+         */
+        if(auto errcode = errno; errcode != ESPIPE)
+        {
+            ERR("Failed to reset file offset: {} ({})", std::generic_category().message(errcode),
+                errcode);
+        }
+    }
+    clearerr(mFile.get());
 
 
     fputs("RIFF", mFile.get());
     fputs("RIFF", mFile.get());
-    fwrite32le(0xFFFFFFFF, mFile.get()); // 'RIFF' header len; filled in at close
+    fwrite32le(0xFFFFFFFF, mFile.get()); // 'RIFF' header len; filled in at stop
 
 
     fputs("WAVE", mFile.get());
     fputs("WAVE", mFile.get());
 
 
@@ -297,9 +303,9 @@ bool WaveBackend::reset()
     // 16-bit val, channel count
     // 16-bit val, channel count
     fwrite16le(static_cast<ushort>(channels), mFile.get());
     fwrite16le(static_cast<ushort>(channels), mFile.get());
     // 32-bit val, frequency
     // 32-bit val, frequency
-    fwrite32le(mDevice->Frequency, mFile.get());
+    fwrite32le(mDevice->mSampleRate, mFile.get());
     // 32-bit val, bytes per second
     // 32-bit val, bytes per second
-    fwrite32le(mDevice->Frequency * channels * bytes, mFile.get());
+    fwrite32le(mDevice->mSampleRate * channels * bytes, mFile.get());
     // 16-bit val, frame size
     // 16-bit val, frame size
     fwrite16le(static_cast<ushort>(channels * bytes), mFile.get());
     fwrite16le(static_cast<ushort>(channels * bytes), mFile.get());
     // 16-bit val, bits per sample
     // 16-bit val, bits per sample
@@ -316,18 +322,18 @@ bool WaveBackend::reset()
         (isbformat ? SUBTYPE_BFORMAT_PCM.data() : SUBTYPE_PCM.data()), 1, 16, mFile.get());
         (isbformat ? SUBTYPE_BFORMAT_PCM.data() : SUBTYPE_PCM.data()), 1, 16, mFile.get());
 
 
     fputs("data", mFile.get());
     fputs("data", mFile.get());
-    fwrite32le(0xFFFFFFFF, mFile.get()); // 'data' header len; filled in at close
+    fwrite32le(0xFFFFFFFF, mFile.get()); // 'data' header len; filled in at stop
 
 
     if(ferror(mFile.get()))
     if(ferror(mFile.get()))
     {
     {
-        ERR("Error writing header: %s\n", std::generic_category().message(errno).c_str());
+        ERR("Error writing header: {}", std::generic_category().message(errno));
         return false;
         return false;
     }
     }
     mDataStart = ftell(mFile.get());
     mDataStart = ftell(mFile.get());
 
 
     setDefaultWFXChannelOrder();
     setDefaultWFXChannelOrder();
 
 
-    const uint bufsize{mDevice->frameSizeFromFmt() * mDevice->UpdateSize};
+    const uint bufsize{mDevice->frameSizeFromFmt() * mDevice->mUpdateSize};
     mBuffer.resize(bufsize);
     mBuffer.resize(bufsize);
 
 
     return true;
     return true;
@@ -336,14 +342,14 @@ bool WaveBackend::reset()
 void WaveBackend::start()
 void WaveBackend::start()
 {
 {
     if(mDataStart > 0 && fseek(mFile.get(), 0, SEEK_END) != 0)
     if(mDataStart > 0 && fseek(mFile.get(), 0, SEEK_END) != 0)
-        WARN("Failed to seek on output file\n");
+        WARN("Failed to seek on output file");
     try {
     try {
         mKillNow.store(false, std::memory_order_release);
         mKillNow.store(false, std::memory_order_release);
-        mThread = std::thread{std::mem_fn(&WaveBackend::mixerProc), this};
+        mThread = std::thread{&WaveBackend::mixerProc, this};
     }
     }
     catch(std::exception& e) {
     catch(std::exception& e) {
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to start mixing thread: %s", e.what()};
+            "Failed to start mixing thread: {}", e.what()};
     }
     }
 }
 }
 
 
@@ -376,17 +382,16 @@ bool WaveBackendFactory::init()
 bool WaveBackendFactory::querySupport(BackendType type)
 bool WaveBackendFactory::querySupport(BackendType type)
 { return type == BackendType::Playback; }
 { return type == BackendType::Playback; }
 
 
-std::string WaveBackendFactory::probe(BackendType type)
+auto WaveBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
 {
 {
     switch(type)
     switch(type)
     {
     {
     case BackendType::Playback:
     case BackendType::Playback:
-        /* Include null char. */
-        return std::string{GetDeviceName()} + '\0';
+        return std::vector{std::string{GetDeviceName()}};
     case BackendType::Capture:
     case BackendType::Capture:
         break;
         break;
     }
     }
-    return std::string{};
+    return {};
 }
 }
 
 
 BackendPtr WaveBackendFactory::createBackend(DeviceBase *device, BackendType type)
 BackendPtr WaveBackendFactory::createBackend(DeviceBase *device, BackendType type)

+ 5 - 5
libs/openal-soft/alc/backends/wave.h

@@ -5,15 +5,15 @@
 
 
 struct WaveBackendFactory final : public BackendFactory {
 struct WaveBackendFactory final : public BackendFactory {
 public:
 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 */
 #endif /* BACKENDS_WAVE_H */

+ 75 - 93
libs/openal-soft/alc/backends/winmm.cpp

@@ -36,15 +36,14 @@
 #include <vector>
 #include <vector>
 #include <string>
 #include <string>
 #include <algorithm>
 #include <algorithm>
-#include <functional>
 
 
 #include "alnumeric.h"
 #include "alnumeric.h"
 #include "alsem.h"
 #include "alsem.h"
-#include "alstring.h"
 #include "althrd_setname.h"
 #include "althrd_setname.h"
 #include "core/device.h"
 #include "core/device.h"
 #include "core/helpers.h"
 #include "core/helpers.h"
 #include "core/logging.h"
 #include "core/logging.h"
+#include "fmt/core.h"
 #include "ringbuffer.h"
 #include "ringbuffer.h"
 #include "strutils.h"
 #include "strutils.h"
 #include "vector.h"
 #include "vector.h"
@@ -55,9 +54,6 @@
 
 
 namespace {
 namespace {
 
 
-#define DEVNAME_HEAD "OpenAL Soft on "
-
-
 std::vector<std::string> PlaybackDevices;
 std::vector<std::string> PlaybackDevices;
 std::vector<std::string> CaptureDevices;
 std::vector<std::string> CaptureDevices;
 
 
@@ -77,19 +73,15 @@ void ProbePlaybackDevices()
         WAVEOUTCAPSW WaveCaps{};
         WAVEOUTCAPSW WaveCaps{};
         if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
         if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
         {
         {
-            const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)};
+            const auto basename = wstr_to_utf8(std::data(WaveCaps.szPname));
 
 
-            int count{1};
-            std::string newname{basename};
+            auto count = 1;
+            auto newname = basename;
             while(checkName(PlaybackDevices, newname))
             while(checkName(PlaybackDevices, newname))
-            {
-                newname = basename;
-                newname += " #";
-                newname += std::to_string(++count);
-            }
+                newname = fmt::format("{} #{}", basename, ++count);
             dname = std::move(newname);
             dname = std::move(newname);
 
 
-            TRACE("Got device \"%s\", ID %u\n", dname.c_str(), i);
+            TRACE("Got device \"{}\", ID {}", dname, i);
         }
         }
         PlaybackDevices.emplace_back(std::move(dname));
         PlaybackDevices.emplace_back(std::move(dname));
     }
     }
@@ -108,19 +100,15 @@ void ProbeCaptureDevices()
         WAVEINCAPSW WaveCaps{};
         WAVEINCAPSW WaveCaps{};
         if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
         if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
         {
         {
-            const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)};
+            const auto basename = wstr_to_utf8(std::data(WaveCaps.szPname));
 
 
-            int count{1};
-            std::string newname{basename};
+            auto count = 1;
+            auto newname = basename;
             while(checkName(CaptureDevices, newname))
             while(checkName(CaptureDevices, newname))
-            {
-                newname = basename;
-                newname += " #";
-                newname += std::to_string(++count);
-            }
+                newname = fmt::format("{} #{}", basename, ++count);
             dname = std::move(newname);
             dname = std::move(newname);
 
 
-            TRACE("Got device \"%s\", ID %u\n", dname.c_str(), i);
+            TRACE("Got device \"{}\", ID {}", dname, i);
         }
         }
         CaptureDevices.emplace_back(std::move(dname));
         CaptureDevices.emplace_back(std::move(dname));
     }
     }
@@ -128,7 +116,7 @@ void ProbeCaptureDevices()
 
 
 
 
 struct WinMMPlayback final : public BackendBase {
 struct WinMMPlayback final : public BackendBase {
-    WinMMPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit WinMMPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
     ~WinMMPlayback() override;
     ~WinMMPlayback() override;
 
 
     void CALLBACK waveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR param1, DWORD_PTR param2) noexcept;
     void CALLBACK waveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR param1, DWORD_PTR param2) noexcept;
@@ -195,7 +183,7 @@ FORCE_ALIGN int WinMMPlayback::mixerProc()
             WAVEHDR &waveHdr = mWaveBuffer[widx];
             WAVEHDR &waveHdr = mWaveBuffer[widx];
             if(++widx == mWaveBuffer.size()) widx = 0;
             if(++widx == mWaveBuffer.size()) widx = 0;
 
 
-            mDevice->renderSamples(waveHdr.lpData, mDevice->UpdateSize, mFormat.nChannels);
+            mDevice->renderSamples(waveHdr.lpData, mDevice->mUpdateSize, mFormat.nChannels);
             mWritable.fetch_sub(1, std::memory_order_acq_rel);
             mWritable.fetch_sub(1, std::memory_order_acq_rel);
             waveOutWrite(mOutHdl, &waveHdr, sizeof(WAVEHDR));
             waveOutWrite(mOutHdl, &waveHdr, sizeof(WAVEHDR));
         } while(--todo);
         } while(--todo);
@@ -216,61 +204,57 @@ void WinMMPlayback::open(std::string_view name)
         std::find(PlaybackDevices.cbegin(), PlaybackDevices.cend(), name) :
         std::find(PlaybackDevices.cbegin(), PlaybackDevices.cend(), name) :
         PlaybackDevices.cbegin();
         PlaybackDevices.cbegin();
     if(iter == PlaybackDevices.cend())
     if(iter == PlaybackDevices.cend())
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
-            al::sizei(name), name.data()};
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
+            name};
     auto DeviceID = static_cast<UINT>(std::distance(PlaybackDevices.cbegin(), iter));
     auto DeviceID = static_cast<UINT>(std::distance(PlaybackDevices.cbegin(), iter));
 
 
     DevFmtType fmttype{mDevice->FmtType};
     DevFmtType fmttype{mDevice->FmtType};
-retry_open:
     WAVEFORMATEX format{};
     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)
         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->mSampleRate;
+        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: {}",
+                res};
+
+        fmttype = DevFmtShort;
+    } while(true);
 
 
-    if(mOutHdl)
-        waveOutClose(mOutHdl);
-    mOutHdl = outHandle;
     mFormat = format;
     mFormat = format;
 
 
-    mDevice->DeviceName = PlaybackDevices[DeviceID];
+    mDeviceName = PlaybackDevices[DeviceID];
 }
 }
 
 
 bool WinMMPlayback::reset()
 bool WinMMPlayback::reset()
 {
 {
-    mDevice->BufferSize = static_cast<uint>(uint64_t{mDevice->BufferSize} *
-        mFormat.nSamplesPerSec / mDevice->Frequency);
-    mDevice->BufferSize = (mDevice->BufferSize+3) & ~0x3u;
-    mDevice->UpdateSize = mDevice->BufferSize / 4;
-    mDevice->Frequency = mFormat.nSamplesPerSec;
+    mDevice->mBufferSize = static_cast<uint>(uint64_t{mDevice->mBufferSize} *
+        mFormat.nSamplesPerSec / mDevice->mSampleRate);
+    mDevice->mBufferSize = (mDevice->mBufferSize+3) & ~0x3u;
+    mDevice->mUpdateSize = mDevice->mBufferSize / 4;
+    mDevice->mSampleRate = mFormat.nSamplesPerSec;
 
 
     if(mFormat.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
     if(mFormat.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
     {
     {
@@ -278,7 +262,7 @@ bool WinMMPlayback::reset()
             mDevice->FmtType = DevFmtFloat;
             mDevice->FmtType = DevFmtFloat;
         else
         else
         {
         {
-            ERR("Unhandled IEEE float sample depth: %d\n", mFormat.wBitsPerSample);
+            ERR("Unhandled IEEE float sample depth: {}", mFormat.wBitsPerSample);
             return false;
             return false;
         }
         }
     }
     }
@@ -290,13 +274,13 @@ bool WinMMPlayback::reset()
             mDevice->FmtType = DevFmtUByte;
             mDevice->FmtType = DevFmtUByte;
         else
         else
         {
         {
-            ERR("Unhandled PCM sample depth: %d\n", mFormat.wBitsPerSample);
+            ERR("Unhandled PCM sample depth: {}", mFormat.wBitsPerSample);
             return false;
             return false;
         }
         }
     }
     }
     else
     else
     {
     {
-        ERR("Unhandled format tag: 0x%04x\n", mFormat.wFormatTag);
+        ERR("Unhandled format tag: {:#04x}", as_unsigned(mFormat.wFormatTag));
         return false;
         return false;
     }
     }
 
 
@@ -306,12 +290,12 @@ bool WinMMPlayback::reset()
         mDevice->FmtChans = DevFmtMono;
         mDevice->FmtChans = DevFmtMono;
     else
     else
     {
     {
-        ERR("Unhandled channel count: %d\n", mFormat.nChannels);
+        ERR("Unhandled channel count: {}", mFormat.nChannels);
         return false;
         return false;
     }
     }
     setDefaultWFXChannelOrder();
     setDefaultWFXChannelOrder();
 
 
-    const uint BufferSize{mDevice->UpdateSize * mFormat.nChannels * mDevice->bytesFromFmt()};
+    const uint BufferSize{mDevice->mUpdateSize * mFormat.nChannels * mDevice->bytesFromFmt()};
 
 
     decltype(mBuffer)(BufferSize*mWaveBuffer.size()).swap(mBuffer);
     decltype(mBuffer)(BufferSize*mWaveBuffer.size()).swap(mBuffer);
     mWaveBuffer[0] = WAVEHDR{};
     mWaveBuffer[0] = WAVEHDR{};
@@ -336,11 +320,11 @@ void WinMMPlayback::start()
         mWritable.store(static_cast<uint>(mWaveBuffer.size()), std::memory_order_release);
         mWritable.store(static_cast<uint>(mWaveBuffer.size()), std::memory_order_release);
 
 
         mKillNow.store(false, std::memory_order_release);
         mKillNow.store(false, std::memory_order_release);
-        mThread = std::thread{std::mem_fn(&WinMMPlayback::mixerProc), this};
+        mThread = std::thread{&WinMMPlayback::mixerProc, this};
     }
     }
     catch(std::exception& e) {
     catch(std::exception& e) {
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to start mixing thread: %s", e.what()};
+            "Failed to start mixing thread: {}", e.what()};
     }
     }
 }
 }
 
 
@@ -359,7 +343,7 @@ void WinMMPlayback::stop()
 
 
 
 
 struct WinMMCapture final : public BackendBase {
 struct WinMMCapture final : public BackendBase {
-    WinMMCapture(DeviceBase *device) noexcept : BackendBase{device} { }
+    explicit WinMMCapture(DeviceBase *device) noexcept : BackendBase{device} { }
     ~WinMMCapture() override;
     ~WinMMCapture() override;
 
 
     void CALLBACK waveInProc(HWAVEIN device, UINT msg, DWORD_PTR param1, DWORD_PTR param2) noexcept;
     void CALLBACK waveInProc(HWAVEIN device, UINT msg, DWORD_PTR param1, DWORD_PTR param2) noexcept;
@@ -451,8 +435,8 @@ void WinMMCapture::open(std::string_view name)
         std::find(CaptureDevices.cbegin(), CaptureDevices.cend(), name) :
         std::find(CaptureDevices.cbegin(), CaptureDevices.cend(), name) :
         CaptureDevices.cbegin();
         CaptureDevices.cbegin();
     if(iter == CaptureDevices.cend())
     if(iter == CaptureDevices.cend())
-        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
-            al::sizei(name), name.data()};
+        throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
+            name};
     auto DeviceID = static_cast<UINT>(std::distance(CaptureDevices.cbegin(), iter));
     auto DeviceID = static_cast<UINT>(std::distance(CaptureDevices.cbegin(), iter));
 
 
     switch(mDevice->FmtChans)
     switch(mDevice->FmtChans)
@@ -466,9 +450,10 @@ void WinMMCapture::open(std::string_view name)
     case DevFmtX61:
     case DevFmtX61:
     case DevFmtX71:
     case DevFmtX71:
     case DevFmtX714:
     case DevFmtX714:
+    case DevFmtX7144:
     case DevFmtX3D71:
     case DevFmtX3D71:
     case DevFmtAmbi3D:
     case DevFmtAmbi3D:
-        throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported",
+        throw al::backend_exception{al::backend_error::DeviceError, "{} capture not supported",
             DevFmtChannelsString(mDevice->FmtChans)};
             DevFmtChannelsString(mDevice->FmtChans)};
     }
     }
 
 
@@ -483,7 +468,7 @@ void WinMMCapture::open(std::string_view name)
     case DevFmtByte:
     case DevFmtByte:
     case DevFmtUShort:
     case DevFmtUShort:
     case DevFmtUInt:
     case DevFmtUInt:
-        throw al::backend_exception{al::backend_error::DeviceError, "%s samples not supported",
+        throw al::backend_exception{al::backend_error::DeviceError, "{} samples not supported",
             DevFmtTypeString(mDevice->FmtType)};
             DevFmtTypeString(mDevice->FmtType)};
     }
     }
 
 
@@ -493,7 +478,7 @@ void WinMMCapture::open(std::string_view name)
     mFormat.nChannels = static_cast<WORD>(mDevice->channelsFromFmt());
     mFormat.nChannels = static_cast<WORD>(mDevice->channelsFromFmt());
     mFormat.wBitsPerSample = static_cast<WORD>(mDevice->bytesFromFmt() * 8);
     mFormat.wBitsPerSample = static_cast<WORD>(mDevice->bytesFromFmt() * 8);
     mFormat.nBlockAlign = static_cast<WORD>(mFormat.wBitsPerSample * mFormat.nChannels / 8);
     mFormat.nBlockAlign = static_cast<WORD>(mFormat.wBitsPerSample * mFormat.nChannels / 8);
-    mFormat.nSamplesPerSec = mDevice->Frequency;
+    mFormat.nSamplesPerSec = mDevice->mSampleRate;
     mFormat.nAvgBytesPerSec = mFormat.nSamplesPerSec * mFormat.nBlockAlign;
     mFormat.nAvgBytesPerSec = mFormat.nSamplesPerSec * mFormat.nBlockAlign;
     mFormat.cbSize = 0;
     mFormat.cbSize = 0;
 
 
@@ -501,7 +486,7 @@ void WinMMCapture::open(std::string_view name)
         reinterpret_cast<DWORD_PTR>(&WinMMCapture::waveInProcC),
         reinterpret_cast<DWORD_PTR>(&WinMMCapture::waveInProcC),
         reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)};
         reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)};
     if(res != MMSYSERR_NOERROR)
     if(res != MMSYSERR_NOERROR)
-        throw al::backend_exception{al::backend_error::DeviceError, "waveInOpen failed: %u", res};
+        throw al::backend_exception{al::backend_error::DeviceError, "waveInOpen failed: {}", res};
 
 
     // Ensure each buffer is 50ms each
     // Ensure each buffer is 50ms each
     DWORD BufferSize{mFormat.nAvgBytesPerSec / 20u};
     DWORD BufferSize{mFormat.nAvgBytesPerSec / 20u};
@@ -509,7 +494,7 @@ void WinMMCapture::open(std::string_view name)
 
 
     // Allocate circular memory buffer for the captured audio
     // Allocate circular memory buffer for the captured audio
     // Make sure circular buffer is at least 100ms in size
     // Make sure circular buffer is at least 100ms in size
-    const auto CapturedDataSize = std::max<size_t>(mDevice->BufferSize,
+    const auto CapturedDataSize = std::max<size_t>(mDevice->mBufferSize,
         BufferSize*mWaveBuffer.size());
         BufferSize*mWaveBuffer.size());
 
 
     mRing = RingBuffer::Create(CapturedDataSize, mFormat.nBlockAlign, false);
     mRing = RingBuffer::Create(CapturedDataSize, mFormat.nBlockAlign, false);
@@ -525,7 +510,7 @@ void WinMMCapture::open(std::string_view name)
         mWaveBuffer[i].dwBufferLength = mWaveBuffer[i-1].dwBufferLength;
         mWaveBuffer[i].dwBufferLength = mWaveBuffer[i-1].dwBufferLength;
     }
     }
 
 
-    mDevice->DeviceName = CaptureDevices[DeviceID];
+    mDeviceName = CaptureDevices[DeviceID];
 }
 }
 
 
 void WinMMCapture::start()
 void WinMMCapture::start()
@@ -538,13 +523,13 @@ void WinMMCapture::start()
         }
         }
 
 
         mKillNow.store(false, std::memory_order_release);
         mKillNow.store(false, std::memory_order_release);
-        mThread = std::thread{std::mem_fn(&WinMMCapture::captureProc), this};
+        mThread = std::thread{&WinMMCapture::captureProc, this};
 
 
         waveInStart(mInHdl);
         waveInStart(mInHdl);
     }
     }
     catch(std::exception& e) {
     catch(std::exception& e) {
         throw al::backend_exception{al::backend_error::DeviceError,
         throw al::backend_exception{al::backend_error::DeviceError,
-            "Failed to start recording thread: %s", e.what()};
+            "Failed to start recording thread: {}", e.what()};
     }
     }
 }
 }
 
 
@@ -582,26 +567,23 @@ bool WinMMBackendFactory::init()
 bool WinMMBackendFactory::querySupport(BackendType type)
 bool WinMMBackendFactory::querySupport(BackendType type)
 { return type == BackendType::Playback || type == BackendType::Capture; }
 { 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
     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)
     switch(type)
     {
     {
     case BackendType::Playback:
     case BackendType::Playback:
         ProbePlaybackDevices();
         ProbePlaybackDevices();
+        outnames.reserve(PlaybackDevices.size());
         std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
         std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
         break;
         break;
 
 
     case BackendType::Capture:
     case BackendType::Capture:
         ProbeCaptureDevices();
         ProbeCaptureDevices();
+        outnames.reserve(CaptureDevices.size());
         std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
         std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
         break;
         break;
     }
     }

+ 5 - 5
libs/openal-soft/alc/backends/winmm.h

@@ -5,15 +5,15 @@
 
 
 struct WinMMBackendFactory final : public BackendFactory {
 struct WinMMBackendFactory final : public BackendFactory {
 public:
 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 */
 #endif /* BACKENDS_WINMM_H */

+ 123 - 105
libs/openal-soft/alc/context.cpp

@@ -6,12 +6,12 @@
 #include <algorithm>
 #include <algorithm>
 #include <array>
 #include <array>
 #include <cstddef>
 #include <cstddef>
-#include <cstring>
 #include <functional>
 #include <functional>
-#include <limits>
+#include <iterator>
 #include <numeric>
 #include <numeric>
-#include <stdexcept>
+#include <optional>
 #include <string_view>
 #include <string_view>
+#include <tuple>
 #include <utility>
 #include <utility>
 
 
 #include "AL/efx.h"
 #include "AL/efx.h"
@@ -25,20 +25,22 @@
 #include "albit.h"
 #include "albit.h"
 #include "alc/alu.h"
 #include "alc/alu.h"
 #include "alc/backends/base.h"
 #include "alc/backends/base.h"
+#include "alnumeric.h"
 #include "alspan.h"
 #include "alspan.h"
+#include "atomic.h"
 #include "core/async_event.h"
 #include "core/async_event.h"
+#include "core/devformat.h"
 #include "core/device.h"
 #include "core/device.h"
 #include "core/effectslot.h"
 #include "core/effectslot.h"
 #include "core/logging.h"
 #include "core/logging.h"
-#include "core/voice.h"
 #include "core/voice_change.h"
 #include "core/voice_change.h"
 #include "device.h"
 #include "device.h"
 #include "flexarray.h"
 #include "flexarray.h"
 #include "ringbuffer.h"
 #include "ringbuffer.h"
 #include "vecmat.h"
 #include "vecmat.h"
 
 
-#ifdef ALSOFT_EAX
-#include "alstring.h"
+#if ALSOFT_EAX
+#include "al/eax/call.h"
 #include "al/eax/globals.h"
 #include "al/eax/globals.h"
 #endif // ALSOFT_EAX
 #endif // ALSOFT_EAX
 
 
@@ -71,7 +73,7 @@ std::vector<std::string_view> getContextExtensions() noexcept
         "AL_EXT_STEREO_ANGLES"sv,
         "AL_EXT_STEREO_ANGLES"sv,
         "AL_LOKI_quadriphonic"sv,
         "AL_LOKI_quadriphonic"sv,
         "AL_SOFT_bformat_ex"sv,
         "AL_SOFT_bformat_ex"sv,
-        "AL_SOFTX_bformat_hoa"sv,
+        "AL_SOFT_bformat_hoa"sv,
         "AL_SOFT_block_alignment"sv,
         "AL_SOFT_block_alignment"sv,
         "AL_SOFT_buffer_length_query"sv,
         "AL_SOFT_buffer_length_query"sv,
         "AL_SOFT_callback_buffer"sv,
         "AL_SOFT_callback_buffer"sv,
@@ -108,7 +110,7 @@ ALCcontext::ThreadCtx::~ThreadCtx()
     if(ALCcontext *ctx{std::exchange(ALCcontext::sLocalContext, nullptr)})
     if(ALCcontext *ctx{std::exchange(ALCcontext::sLocalContext, nullptr)})
     {
     {
         const bool result{ctx->releaseIfNoDelete()};
         const bool result{ctx->releaseIfNoDelete()};
-        ERR("Context %p current for thread being destroyed%s!\n", voidp{ctx},
+        ERR("Context {} current for thread being destroyed{}!", voidp{ctx},
             result ? "" : ", leak detected");
             result ? "" : ", leak detected");
     }
     }
 }
 }
@@ -117,26 +119,30 @@ thread_local ALCcontext::ThreadCtx ALCcontext::sThreadContext;
 ALeffect ALCcontext::sDefaultEffect;
 ALeffect ALCcontext::sDefaultEffect;
 
 
 
 
-ALCcontext::ALCcontext(al::intrusive_ptr<ALCdevice> device, ContextFlagBitset flags)
+ALCcontext::ALCcontext(al::intrusive_ptr<al::Device> device, ContextFlagBitset flags)
     : ContextBase{device.get()}, mALDevice{std::move(device)}, mContextFlags{flags}
     : ContextBase{device.get()}, mALDevice{std::move(device)}, mContextFlags{flags}
 {
 {
     mDebugGroups.emplace_back(DebugSource::Other, 0, std::string{});
     mDebugGroups.emplace_back(DebugSource::Other, 0, std::string{});
     mDebugEnabled.store(mContextFlags.test(ContextFlags::DebugBit), std::memory_order_relaxed);
     mDebugEnabled.store(mContextFlags.test(ContextFlags::DebugBit), std::memory_order_relaxed);
+
+    /* Low-severity debug messages are disabled by default. */
+    alDebugMessageControlDirectEXT(this, AL_DONT_CARE_EXT, AL_DONT_CARE_EXT,
+        AL_DEBUG_SEVERITY_LOW_EXT, 0, nullptr, AL_FALSE);
 }
 }
 
 
 ALCcontext::~ALCcontext()
 ALCcontext::~ALCcontext()
 {
 {
-    TRACE("Freeing context %p\n", voidp{this});
+    TRACE("Freeing context {}", voidp{this});
 
 
     size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), 0_uz,
     size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), 0_uz,
         [](size_t cur, const SourceSubList &sublist) noexcept -> size_t
         [](size_t cur, const SourceSubList &sublist) noexcept -> size_t
         { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })};
         { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })};
     if(count > 0)
     if(count > 0)
-        WARN("%zu Source%s not deleted\n", count, (count==1)?"":"s");
+        WARN("{} Source{} not deleted", count, (count==1)?"":"s");
     mSourceList.clear();
     mSourceList.clear();
     mNumSources = 0;
     mNumSources = 0;
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
     eaxUninitialize();
     eaxUninitialize();
 #endif // ALSOFT_EAX
 #endif // ALSOFT_EAX
 
 
@@ -145,7 +151,7 @@ ALCcontext::~ALCcontext()
         [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
         [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
         { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
         { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
     if(count > 0)
     if(count > 0)
-        WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count, (count==1)?"":"s");
+        WARN("{} AuxiliaryEffectSlot{} not deleted", count, (count==1)?"":"s");
     mEffectSlotList.clear();
     mEffectSlotList.clear();
     mNumEffectSlots = 0;
     mNumEffectSlots = 0;
 }
 }
@@ -163,9 +169,9 @@ void ALCcontext::init()
         auxslots = EffectSlot::CreatePtrArray(0);
         auxslots = EffectSlot::CreatePtrArray(0);
     else
     else
     {
     {
-        auxslots = EffectSlot::CreatePtrArray(1);
+        auxslots = EffectSlot::CreatePtrArray(2);
         (*auxslots)[0] = mDefaultSlot->mSlot;
         (*auxslots)[0] = mDefaultSlot->mSlot;
-        std::uninitialized_fill_n(al::to_address(auxslots->end()), 1_uz, nullptr);
+        (*auxslots)[1] = mDefaultSlot->mSlot;
         mDefaultSlot->mState = SlotState::Playing;
         mDefaultSlot->mState = SlotState::Playing;
     }
     }
     mActiveAuxSlots.store(std::move(auxslots), std::memory_order_relaxed);
     mActiveAuxSlots.store(std::move(auxslots), std::memory_order_relaxed);
@@ -182,15 +188,17 @@ void ALCcontext::init()
 
 
     if(sBufferSubDataCompat)
     if(sBufferSubDataCompat)
     {
     {
-        auto iter = std::find(mExtensions.begin(), mExtensions.end(), "AL_EXT_SOURCE_RADIUS");
+        auto iter = std::find(mExtensions.begin(), mExtensions.end(), "AL_EXT_SOURCE_RADIUS"sv);
         if(iter != mExtensions.end()) mExtensions.erase(iter);
         if(iter != mExtensions.end()) mExtensions.erase(iter);
-        /* TODO: Would be nice to sort this alphabetically. Needs case-
-         * insensitive searching.
+
+        /* Insert the AL_SOFT_buffer_sub_data extension string between
+         * AL_SOFT_buffer_length_query and AL_SOFT_callback_buffer.
          */
          */
-        mExtensions.emplace_back("AL_SOFT_buffer_sub_data");
+        iter = std::find(mExtensions.begin(), mExtensions.end(), "AL_SOFT_callback_buffer"sv);
+        mExtensions.emplace(iter, "AL_SOFT_buffer_sub_data"sv);
     }
     }
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
     eax_initialize_extensions();
     eax_initialize_extensions();
 #endif // ALSOFT_EAX
 #endif // ALSOFT_EAX
 
 
@@ -213,19 +221,31 @@ void ALCcontext::init()
         mExtensionsString = std::move(extensions);
         mExtensionsString = std::move(extensions);
     }
     }
 
 
+#if ALSOFT_EAX
+    eax_set_defaults();
+#endif
+
     mParams.Position = alu::Vector{0.0f, 0.0f, 0.0f, 1.0f};
     mParams.Position = alu::Vector{0.0f, 0.0f, 0.0f, 1.0f};
     mParams.Matrix = alu::Matrix::Identity();
     mParams.Matrix = alu::Matrix::Identity();
     mParams.Velocity = alu::Vector{};
     mParams.Velocity = alu::Vector{};
     mParams.Gain = mListener.Gain;
     mParams.Gain = mListener.Gain;
-    mParams.MetersPerUnit = mListener.mMetersPerUnit;
+    mParams.MetersPerUnit = mListener.mMetersPerUnit
+#if ALSOFT_EAX
+        * eaxGetDistanceFactor()
+#endif
+        ;
     mParams.AirAbsorptionGainHF = mAirAbsorptionGainHF;
     mParams.AirAbsorptionGainHF = mAirAbsorptionGainHF;
     mParams.DopplerFactor = mDopplerFactor;
     mParams.DopplerFactor = mDopplerFactor;
-    mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity;
+    mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity
+#if ALSOFT_EAX
+        / eaxGetDistanceFactor()
+#endif
+        ;
     mParams.SourceDistanceModel = mSourceDistanceModel;
     mParams.SourceDistanceModel = mSourceDistanceModel;
     mParams.mDistanceModel = mDistanceModel;
     mParams.mDistanceModel = mDistanceModel;
 
 
 
 
-    mAsyncEvents = RingBuffer::Create(511, sizeof(AsyncEvent), false);
+    mAsyncEvents = RingBuffer::Create(1024, sizeof(AsyncEvent), false);
     StartEventThrd(this);
     StartEventThrd(this);
 
 
 
 
@@ -237,35 +257,34 @@ void ALCcontext::deinit()
 {
 {
     if(sLocalContext == this)
     if(sLocalContext == this)
     {
     {
-        WARN("%p released while current on thread\n", voidp{this});
+        WARN("{} released while current on thread", voidp{this});
+        auto _ = ContextRef{sLocalContext};
         sThreadContext.set(nullptr);
         sThreadContext.set(nullptr);
-        dec_ref();
     }
     }
 
 
-    ALCcontext *origctx{this};
-    if(sGlobalContext.compare_exchange_strong(origctx, nullptr))
+    if(ALCcontext *origctx{this}; sGlobalContext.compare_exchange_strong(origctx, nullptr))
     {
     {
+        auto _ = ContextRef{origctx};
         while(sGlobalContextLock.load()) {
         while(sGlobalContextLock.load()) {
             /* Wait to make sure another thread didn't get the context and is
             /* Wait to make sure another thread didn't get the context and is
              * trying to increment its refcount.
              * trying to increment its refcount.
              */
              */
         }
         }
-        dec_ref();
     }
     }
 
 
     bool stopPlayback{};
     bool stopPlayback{};
     /* First make sure this context exists in the device's list. */
     /* First make sure this context exists in the device's list. */
-    auto *oldarray = mDevice->mContexts.load(std::memory_order_acquire);
-    if(auto toremove = static_cast<size_t>(std::count(oldarray->begin(), oldarray->end(), this)))
+    auto oldarray = al::span{*mDevice->mContexts.load(std::memory_order_acquire)};
+    if(auto toremove = static_cast<size_t>(std::count(oldarray.begin(), oldarray.end(), this)))
     {
     {
         using ContextArray = al::FlexArray<ContextBase*>;
         using ContextArray = al::FlexArray<ContextBase*>;
-        const size_t newsize{oldarray->size() - toremove};
+        const auto newsize = size_t{oldarray.size() - toremove};
         auto newarray = ContextArray::Create(newsize);
         auto newarray = ContextArray::Create(newsize);
 
 
         /* Copy the current/old context handles to the new array, excluding the
         /* Copy the current/old context handles to the new array, excluding the
          * given context.
          * given context.
          */
          */
-        std::copy_if(oldarray->begin(), oldarray->end(), newarray->begin(),
+        std::copy_if(oldarray.begin(), oldarray.end(), newarray->begin(),
             [this](ContextBase *ctx) { return ctx != this; });
             [this](ContextBase *ctx) { return ctx != this; });
 
 
         /* Store the new context array in the device. Wait for any current mix
         /* Store the new context array in the device. Wait for any current mix
@@ -277,7 +296,7 @@ void ALCcontext::deinit()
         stopPlayback = (newsize == 0);
         stopPlayback = (newsize == 0);
     }
     }
     else
     else
-        stopPlayback = oldarray->empty();
+        stopPlayback = oldarray.empty();
 
 
     StopEventThrd(this);
     StopEventThrd(this);
 
 
@@ -298,7 +317,7 @@ void ALCcontext::applyAllUpdates()
         /* busy-wait */
         /* busy-wait */
     }
     }
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
     if(mEaxNeedsCommit)
     if(mEaxNeedsCommit)
         eaxCommit();
         eaxCommit();
 #endif
 #endif
@@ -315,7 +334,7 @@ void ALCcontext::applyAllUpdates()
 }
 }
 
 
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 namespace {
 namespace {
 
 
 template<typename F>
 template<typename F>
@@ -493,7 +512,7 @@ void ALCcontext::eax_initialize()
 
 
     eax_ensure_compatibility();
     eax_ensure_compatibility();
     eax_set_defaults();
     eax_set_defaults();
-    eax_context_commit_air_absorbtion_hf();
+    eax_context_commit_air_absorption_hf();
     eax_update_speaker_configuration();
     eax_update_speaker_configuration();
     eax_initialize_fx_slots();
     eax_initialize_fx_slots();
 
 
@@ -547,10 +566,11 @@ unsigned long ALCcontext::eax_detect_speaker_configuration() const
     case DevFmtX51: return SPEAKERS_5;
     case DevFmtX51: return SPEAKERS_5;
     case DevFmtX61: return SPEAKERS_6;
     case DevFmtX61: return SPEAKERS_6;
     case DevFmtX71: return SPEAKERS_7;
     case DevFmtX71: return SPEAKERS_7;
-    /* 7.1.4 is compatible with 7.1. This could instead be HEADPHONES to
+    /* 7.1.4(.4) is compatible with 7.1. This could instead be HEADPHONES to
      * suggest with-height surround sound (like HRTF).
      * suggest with-height surround sound (like HRTF).
      */
      */
     case DevFmtX714: return SPEAKERS_7;
     case DevFmtX714: return SPEAKERS_7;
+    case DevFmtX7144: return SPEAKERS_7;
     /* 3D7.1 is only compatible with 5.1. This could instead be HEADPHONES to
     /* 3D7.1 is only compatible with 5.1. This could instead be HEADPHONES to
      * suggest full-sphere surround sound (like HRTF).
      * suggest full-sphere surround sound (like HRTF).
      */
      */
@@ -561,7 +581,8 @@ unsigned long ALCcontext::eax_detect_speaker_configuration() const
      */
      */
     case DevFmtAmbi3D: return SPEAKERS_7;
     case DevFmtAmbi3D: return SPEAKERS_7;
     }
     }
-    ERR(EAX_PREFIX "Unexpected device channel format 0x%x.\n", mDevice->FmtChans);
+    ERR(EAX_PREFIX "Unexpected device channel format {:#x}.",
+        uint{al::to_underlying(mDevice->FmtChans)});
     return HEADPHONES;
     return HEADPHONES;
 
 
 #undef EAX_PREFIX
 #undef EAX_PREFIX
@@ -619,7 +640,7 @@ void ALCcontext::eax_context_set_defaults()
     eax5_context_set_defaults(mEax5);
     eax5_context_set_defaults(mEax5);
     mEax = mEax5.i;
     mEax = mEax5.i;
     mEaxVersion = 5;
     mEaxVersion = 5;
-    mEaxDf = EaxDirtyFlags{};
+    mEaxDf.reset();
 }
 }
 
 
 void ALCcontext::eax_set_defaults()
 void ALCcontext::eax_set_defaults()
@@ -746,14 +767,11 @@ void ALCcontext::eax_context_commit_primary_fx_slot_id()
 
 
 void ALCcontext::eax_context_commit_distance_factor()
 void ALCcontext::eax_context_commit_distance_factor()
 {
 {
-    if(mListener.mMetersPerUnit == mEax.flDistanceFactor)
-        return;
-
-    mListener.mMetersPerUnit = mEax.flDistanceFactor;
+    /* mEax.flDistanceFactor was changed, so the context props are dirty. */
     mPropsDirty = true;
     mPropsDirty = true;
 }
 }
 
 
-void ALCcontext::eax_context_commit_air_absorbtion_hf()
+void ALCcontext::eax_context_commit_air_absorption_hf()
 {
 {
     const auto new_value = level_mb_to_gain(mEax.flAirAbsorptionHF);
     const auto new_value = level_mb_to_gain(mEax.flAirAbsorptionHF);
 
 
@@ -814,16 +832,16 @@ void ALCcontext::eax4_defer_all(const EaxCall& call, Eax4State& state)
     dst_d = src;
     dst_d = src;
 
 
     if(dst_i.guidPrimaryFXSlotID != dst_d.guidPrimaryFXSlotID)
     if(dst_i.guidPrimaryFXSlotID != dst_d.guidPrimaryFXSlotID)
-        mEaxDf |= eax_primary_fx_slot_id_dirty_bit;
+        mEaxDf.set(eax_primary_fx_slot_id_dirty_bit);
 
 
     if(dst_i.flDistanceFactor != dst_d.flDistanceFactor)
     if(dst_i.flDistanceFactor != dst_d.flDistanceFactor)
-        mEaxDf |= eax_distance_factor_dirty_bit;
+        mEaxDf.set(eax_distance_factor_dirty_bit);
 
 
     if(dst_i.flAirAbsorptionHF != dst_d.flAirAbsorptionHF)
     if(dst_i.flAirAbsorptionHF != dst_d.flAirAbsorptionHF)
-        mEaxDf |= eax_air_absorption_hf_dirty_bit;
+        mEaxDf.set(eax_air_absorption_hf_dirty_bit);
 
 
     if(dst_i.flHFReference != dst_d.flHFReference)
     if(dst_i.flHFReference != dst_d.flHFReference)
-        mEaxDf |= eax_hf_reference_dirty_bit;
+        mEaxDf.set(eax_hf_reference_dirty_bit);
 }
 }
 
 
 void ALCcontext::eax4_defer(const EaxCall& call, Eax4State& state)
 void ALCcontext::eax4_defer(const EaxCall& call, Eax4State& state)
@@ -834,20 +852,20 @@ void ALCcontext::eax4_defer(const EaxCall& call, Eax4State& state)
         eax4_defer_all(call, state);
         eax4_defer_all(call, state);
         break;
         break;
     case EAXCONTEXT_PRIMARYFXSLOTID:
     case EAXCONTEXT_PRIMARYFXSLOTID:
-        eax_defer<Eax4PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(
-            call, state, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
+        eax_defer<Eax4PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(call, state,
+            &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
         break;
         break;
     case EAXCONTEXT_DISTANCEFACTOR:
     case EAXCONTEXT_DISTANCEFACTOR:
-        eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
-            call, state, &EAX40CONTEXTPROPERTIES::flDistanceFactor);
+        eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(call, state,
+            &EAX40CONTEXTPROPERTIES::flDistanceFactor);
         break;
         break;
     case EAXCONTEXT_AIRABSORPTIONHF:
     case EAXCONTEXT_AIRABSORPTIONHF:
-        eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
-            call, state, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
+        eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(call, state,
+            &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
         break;
         break;
     case EAXCONTEXT_HFREFERENCE:
     case EAXCONTEXT_HFREFERENCE:
-        eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
-            call, state, &EAX40CONTEXTPROPERTIES::flHFReference);
+        eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(call, state,
+            &EAX40CONTEXTPROPERTIES::flHFReference);
         break;
         break;
     default:
     default:
         eax_set_misc(call);
         eax_set_misc(call);
@@ -864,19 +882,19 @@ void ALCcontext::eax5_defer_all(const EaxCall& call, Eax5State& state)
     dst_d = src;
     dst_d = src;
 
 
     if(dst_i.guidPrimaryFXSlotID != dst_d.guidPrimaryFXSlotID)
     if(dst_i.guidPrimaryFXSlotID != dst_d.guidPrimaryFXSlotID)
-        mEaxDf |= eax_primary_fx_slot_id_dirty_bit;
+        mEaxDf.set(eax_primary_fx_slot_id_dirty_bit);
 
 
     if(dst_i.flDistanceFactor != dst_d.flDistanceFactor)
     if(dst_i.flDistanceFactor != dst_d.flDistanceFactor)
-        mEaxDf |= eax_distance_factor_dirty_bit;
+        mEaxDf.set(eax_distance_factor_dirty_bit);
 
 
     if(dst_i.flAirAbsorptionHF != dst_d.flAirAbsorptionHF)
     if(dst_i.flAirAbsorptionHF != dst_d.flAirAbsorptionHF)
-        mEaxDf |= eax_air_absorption_hf_dirty_bit;
+        mEaxDf.set(eax_air_absorption_hf_dirty_bit);
 
 
     if(dst_i.flHFReference != dst_d.flHFReference)
     if(dst_i.flHFReference != dst_d.flHFReference)
-        mEaxDf |= eax_hf_reference_dirty_bit;
+        mEaxDf.set(eax_hf_reference_dirty_bit);
 
 
     if(dst_i.flMacroFXFactor != dst_d.flMacroFXFactor)
     if(dst_i.flMacroFXFactor != dst_d.flMacroFXFactor)
-        mEaxDf |= eax_macro_fx_factor_dirty_bit;
+        mEaxDf.set(eax_macro_fx_factor_dirty_bit);
 }
 }
 
 
 void ALCcontext::eax5_defer(const EaxCall& call, Eax5State& state)
 void ALCcontext::eax5_defer(const EaxCall& call, Eax5State& state)
@@ -887,24 +905,24 @@ void ALCcontext::eax5_defer(const EaxCall& call, Eax5State& state)
         eax5_defer_all(call, state);
         eax5_defer_all(call, state);
         break;
         break;
     case EAXCONTEXT_PRIMARYFXSLOTID:
     case EAXCONTEXT_PRIMARYFXSLOTID:
-        eax_defer<Eax5PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(
-            call, state, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
+        eax_defer<Eax5PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(call, state,
+            &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
         break;
         break;
     case EAXCONTEXT_DISTANCEFACTOR:
     case EAXCONTEXT_DISTANCEFACTOR:
-        eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
-            call, state, &EAX50CONTEXTPROPERTIES::flDistanceFactor);
+        eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(call, state,
+            &EAX50CONTEXTPROPERTIES::flDistanceFactor);
         break;
         break;
     case EAXCONTEXT_AIRABSORPTIONHF:
     case EAXCONTEXT_AIRABSORPTIONHF:
-        eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
-            call, state, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
+        eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(call, state,
+            &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
         break;
         break;
     case EAXCONTEXT_HFREFERENCE:
     case EAXCONTEXT_HFREFERENCE:
-        eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
-            call, state, &EAX50CONTEXTPROPERTIES::flHFReference);
+        eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(call, state,
+            &EAX50CONTEXTPROPERTIES::flHFReference);
         break;
         break;
     case EAXCONTEXT_MACROFXFACTOR:
     case EAXCONTEXT_MACROFXFACTOR:
-        eax_defer<Eax5MacroFxFactorValidator, eax_macro_fx_factor_dirty_bit>(
-            call, state, &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
+        eax_defer<Eax5MacroFxFactorValidator, eax_macro_fx_factor_dirty_bit>(call, state,
+            &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
         break;
         break;
     default:
     default:
         eax_set_misc(call);
         eax_set_misc(call);
@@ -922,49 +940,49 @@ void ALCcontext::eax_set(const EaxCall& call)
     default: eax_fail_unknown_version();
     default: eax_fail_unknown_version();
     }
     }
     if(version != mEaxVersion)
     if(version != mEaxVersion)
-        mEaxDf = ~EaxDirtyFlags();
+        mEaxDf.set();
     mEaxVersion = version;
     mEaxVersion = version;
 }
 }
 
 
-void ALCcontext::eax4_context_commit(Eax4State& state, EaxDirtyFlags& dst_df)
+void ALCcontext::eax4_context_commit(Eax4State& state, std::bitset<eax_dirty_bit_count>& dst_df)
 {
 {
-    if(mEaxDf == EaxDirtyFlags{})
+    if(mEaxDf.none())
         return;
         return;
 
 
-    eax_context_commit_property<eax_primary_fx_slot_id_dirty_bit>(
-        state, dst_df, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
-    eax_context_commit_property<eax_distance_factor_dirty_bit>(
-        state, dst_df, &EAX40CONTEXTPROPERTIES::flDistanceFactor);
-    eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(
-        state, dst_df, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
-    eax_context_commit_property<eax_hf_reference_dirty_bit>(
-        state, dst_df, &EAX40CONTEXTPROPERTIES::flHFReference);
+    eax_context_commit_property<eax_primary_fx_slot_id_dirty_bit>(state, dst_df,
+        &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
+    eax_context_commit_property<eax_distance_factor_dirty_bit>(state, dst_df,
+        &EAX40CONTEXTPROPERTIES::flDistanceFactor);
+    eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(state, dst_df,
+        &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
+    eax_context_commit_property<eax_hf_reference_dirty_bit>(state, dst_df,
+        &EAX40CONTEXTPROPERTIES::flHFReference);
 
 
-    mEaxDf = EaxDirtyFlags{};
+    mEaxDf.reset();
 }
 }
 
 
-void ALCcontext::eax5_context_commit(Eax5State& state, EaxDirtyFlags& dst_df)
+void ALCcontext::eax5_context_commit(Eax5State& state, std::bitset<eax_dirty_bit_count>& dst_df)
 {
 {
-    if(mEaxDf == EaxDirtyFlags{})
+    if(mEaxDf.none())
         return;
         return;
 
 
-    eax_context_commit_property<eax_primary_fx_slot_id_dirty_bit>(
-        state, dst_df, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
-    eax_context_commit_property<eax_distance_factor_dirty_bit>(
-        state, dst_df, &EAX50CONTEXTPROPERTIES::flDistanceFactor);
-    eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(
-        state, dst_df, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
-    eax_context_commit_property<eax_hf_reference_dirty_bit>(
-        state, dst_df, &EAX50CONTEXTPROPERTIES::flHFReference);
-    eax_context_commit_property<eax_macro_fx_factor_dirty_bit>(
-        state, dst_df, &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
+    eax_context_commit_property<eax_primary_fx_slot_id_dirty_bit>(state, dst_df,
+        &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
+    eax_context_commit_property<eax_distance_factor_dirty_bit>(state, dst_df,
+        &EAX50CONTEXTPROPERTIES::flDistanceFactor);
+    eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(state, dst_df,
+        &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
+    eax_context_commit_property<eax_hf_reference_dirty_bit>(state, dst_df,
+        &EAX50CONTEXTPROPERTIES::flHFReference);
+    eax_context_commit_property<eax_macro_fx_factor_dirty_bit>(state, dst_df,
+        &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
 
 
-    mEaxDf = EaxDirtyFlags{};
+    mEaxDf.reset();
 }
 }
 
 
 void ALCcontext::eax_context_commit()
 void ALCcontext::eax_context_commit()
 {
 {
-    auto dst_df = EaxDirtyFlags{};
+    auto dst_df = std::bitset<eax_dirty_bit_count>{};
 
 
     switch(mEaxVersion)
     switch(mEaxVersion)
     {
     {
@@ -981,25 +999,25 @@ void ALCcontext::eax_context_commit()
         break;
         break;
     }
     }
 
 
-    if(dst_df == EaxDirtyFlags{})
+    if(dst_df.none())
         return;
         return;
 
 
-    if((dst_df & eax_primary_fx_slot_id_dirty_bit) != EaxDirtyFlags{})
+    if(dst_df.test(eax_primary_fx_slot_id_dirty_bit))
         eax_context_commit_primary_fx_slot_id();
         eax_context_commit_primary_fx_slot_id();
 
 
-    if((dst_df & eax_distance_factor_dirty_bit) != EaxDirtyFlags{})
+    if(dst_df.test(eax_distance_factor_dirty_bit))
         eax_context_commit_distance_factor();
         eax_context_commit_distance_factor();
 
 
-    if((dst_df & eax_air_absorption_hf_dirty_bit) != EaxDirtyFlags{})
-        eax_context_commit_air_absorbtion_hf();
+    if(dst_df.test(eax_air_absorption_hf_dirty_bit))
+        eax_context_commit_air_absorption_hf();
 
 
-    if((dst_df & eax_hf_reference_dirty_bit) != EaxDirtyFlags{})
+    if(dst_df.test(eax_hf_reference_dirty_bit))
         eax_context_commit_hf_reference();
         eax_context_commit_hf_reference();
 
 
-    if((dst_df & eax_macro_fx_factor_dirty_bit) != EaxDirtyFlags{})
+    if(dst_df.test(eax_macro_fx_factor_dirty_bit))
         eax_context_commit_macro_fx_factor();
         eax_context_commit_macro_fx_factor();
 
 
-    if((dst_df & eax_primary_fx_slot_id_dirty_bit) != EaxDirtyFlags{})
+    if(dst_df.test(eax_primary_fx_slot_id_dirty_bit))
         eax_update_sources();
         eax_update_sources();
 }
 }
 
 

+ 65 - 45
libs/openal-soft/alc/context.h

@@ -1,12 +1,14 @@
 #ifndef ALC_CONTEXT_H
 #ifndef ALC_CONTEXT_H
 #define ALC_CONTEXT_H
 #define ALC_CONTEXT_H
 
 
-#include <array>
+#include "config.h"
+
 #include <atomic>
 #include <atomic>
+#include <bitset>
+#include <cstdint>
 #include <deque>
 #include <deque>
 #include <memory>
 #include <memory>
 #include <mutex>
 #include <mutex>
-#include <stdint.h>
 #include <string>
 #include <string>
 #include <string_view>
 #include <string_view>
 #include <unordered_map>
 #include <unordered_map>
@@ -18,32 +20,31 @@
 #include "AL/alext.h"
 #include "AL/alext.h"
 
 
 #include "al/listener.h"
 #include "al/listener.h"
-#include "almalloc.h"
-#include "alnumeric.h"
 #include "althreads.h"
 #include "althreads.h"
-#include "atomic.h"
 #include "core/context.h"
 #include "core/context.h"
-#include "inprogext.h"
+#include "fmt/core.h"
 #include "intrusive_ptr.h"
 #include "intrusive_ptr.h"
+#include "opthelpers.h"
 
 
-#ifdef ALSOFT_EAX
-#include "al/eax/call.h"
+#if ALSOFT_EAX
+#include "al/eax/api.h"
 #include "al/eax/exception.h"
 #include "al/eax/exception.h"
 #include "al/eax/fx_slot_index.h"
 #include "al/eax/fx_slot_index.h"
 #include "al/eax/fx_slots.h"
 #include "al/eax/fx_slots.h"
 #include "al/eax/utils.h"
 #include "al/eax/utils.h"
+
+class EaxCall;
 #endif // ALSOFT_EAX
 #endif // ALSOFT_EAX
 
 
 struct ALeffect;
 struct ALeffect;
 struct ALeffectslot;
 struct ALeffectslot;
-struct ALsource;
 struct DebugGroup;
 struct DebugGroup;
 struct EffectSlotSubList;
 struct EffectSlotSubList;
 struct SourceSubList;
 struct SourceSubList;
 
 
-enum class DebugSource : uint8_t;
-enum class DebugType : uint8_t;
-enum class DebugSeverity : uint8_t;
+enum class DebugSource : std::uint8_t;
+enum class DebugType : std::uint8_t;
+enum class DebugSeverity : std::uint8_t;
 
 
 using uint = unsigned int;
 using uint = unsigned int;
 
 
@@ -72,9 +73,12 @@ struct DebugLogEntry {
 };
 };
 
 
 
 
-struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase {
-    const al::intrusive_ptr<ALCdevice> mALDevice;
+namespace al {
+struct Device;
+} // namespace al
 
 
+struct ALCcontext final : public al::intrusive_ref<ALCcontext>, ContextBase {
+    const al::intrusive_ptr<al::Device> mALDevice;
 
 
     bool mPropsDirty{true};
     bool mPropsDirty{true};
     bool mDeferUpdates{false};
     bool mDeferUpdates{false};
@@ -118,15 +122,15 @@ struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase {
     std::unique_ptr<ALeffectslot> mDefaultSlot;
     std::unique_ptr<ALeffectslot> mDefaultSlot;
 
 
     std::vector<std::string_view> mExtensions;
     std::vector<std::string_view> mExtensions;
-    std::string mExtensionsString{};
+    std::string mExtensionsString;
 
 
     std::unordered_map<ALuint,std::string> mSourceNames;
     std::unordered_map<ALuint,std::string> mSourceNames;
     std::unordered_map<ALuint,std::string> mEffectSlotNames;
     std::unordered_map<ALuint,std::string> mEffectSlotNames;
 
 
-    ALCcontext(al::intrusive_ptr<ALCdevice> device, ContextFlagBitset flags);
+    ALCcontext(al::intrusive_ptr<al::Device> device, ContextFlagBitset flags);
     ALCcontext(const ALCcontext&) = delete;
     ALCcontext(const ALCcontext&) = delete;
     ALCcontext& operator=(const ALCcontext&) = delete;
     ALCcontext& operator=(const ALCcontext&) = delete;
-    ~ALCcontext();
+    ~ALCcontext() final;
 
 
     void init();
     void init();
     /**
     /**
@@ -159,12 +163,18 @@ struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase {
      */
      */
     void applyAllUpdates();
     void applyAllUpdates();
 
 
-#ifdef __MINGW32__
-    [[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]]
-#else
-    [[gnu::format(printf, 3, 4)]]
-#endif
-    void setError(ALenum errorCode, const char *msg, ...);
+    void setErrorImpl(ALenum errorCode, const fmt::string_view fmt, fmt::format_args args);
+
+    template<typename ...Args>
+    void setError(ALenum errorCode, fmt::format_string<Args...> msg, Args&& ...args)
+    { setErrorImpl(errorCode, msg, fmt::make_format_args(args...)); }
+
+    [[noreturn]]
+    void throw_error_impl(ALenum errorCode, const fmt::string_view fmt, fmt::format_args args);
+
+    template<typename ...Args> [[noreturn]]
+    void throw_error(ALenum errorCode, fmt::format_string<Args...> fmt, Args&&... args)
+    { throw_error_impl(errorCode, fmt, fmt::make_format_args(args...)); }
 
 
     void sendDebugMessage(std::unique_lock<std::mutex> &debuglock, DebugSource source,
     void sendDebugMessage(std::unique_lock<std::mutex> &debuglock, DebugSource source,
         DebugType type, ALuint id, DebugSeverity severity, std::string_view message);
         DebugType type, ALuint id, DebugSeverity severity, std::string_view message);
@@ -191,6 +201,10 @@ private:
      */
      */
     class ThreadCtx {
     class ThreadCtx {
     public:
     public:
+        ThreadCtx() = default;
+        ThreadCtx(const ThreadCtx&) = delete;
+        auto operator=(const ThreadCtx&) -> ThreadCtx& = delete;
+
         ~ThreadCtx();
         ~ThreadCtx();
         /* NOLINTBEGIN(readability-convert-member-functions-to-static)
         /* NOLINTBEGIN(readability-convert-member-functions-to-static)
          * This should be non-static to invoke construction of the thread-local
          * This should be non-static to invoke construction of the thread-local
@@ -210,7 +224,7 @@ public:
     /* Default effect that applies to sources that don't have an effect on send 0. */
     /* Default effect that applies to sources that don't have an effect on send 0. */
     static ALeffect sDefaultEffect;
     static ALeffect sDefaultEffect;
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
     bool hasEax() const noexcept { return mEaxIsInitialized; }
     bool hasEax() const noexcept { return mEaxIsInitialized; }
     bool eaxIsCapable() const noexcept;
     bool eaxIsCapable() const noexcept;
 
 
@@ -232,7 +246,11 @@ public:
 
 
     void eaxSetLastError() noexcept;
     void eaxSetLastError() noexcept;
 
 
-    EaxFxSlotIndex eaxGetPrimaryFxSlotIndex() const noexcept
+    [[nodiscard]]
+    auto eaxGetDistanceFactor() const noexcept -> float { return mEax.flDistanceFactor; }
+
+    [[nodiscard]]
+    auto eaxGetPrimaryFxSlotIndex() const noexcept -> EaxFxSlotIndex
     { return mEaxPrimaryFxSlotIndex; }
     { return mEaxPrimaryFxSlotIndex; }
 
 
     const ALeffectslot& eaxGetFxSlot(EaxFxSlotIndexValue fx_slot_index) const
     const ALeffectslot& eaxGetFxSlot(EaxFxSlotIndexValue fx_slot_index) const
@@ -247,11 +265,14 @@ public:
     { mEaxFxSlots.commit(); }
     { mEaxFxSlots.commit(); }
 
 
 private:
 private:
-    static constexpr auto eax_primary_fx_slot_id_dirty_bit = EaxDirtyFlags{1} << 0;
-    static constexpr auto eax_distance_factor_dirty_bit = EaxDirtyFlags{1} << 1;
-    static constexpr auto eax_air_absorption_hf_dirty_bit = EaxDirtyFlags{1} << 2;
-    static constexpr auto eax_hf_reference_dirty_bit = EaxDirtyFlags{1} << 3;
-    static constexpr auto eax_macro_fx_factor_dirty_bit = EaxDirtyFlags{1} << 4;
+    enum {
+        eax_primary_fx_slot_id_dirty_bit,
+        eax_distance_factor_dirty_bit,
+        eax_air_absorption_hf_dirty_bit,
+        eax_hf_reference_dirty_bit,
+        eax_macro_fx_factor_dirty_bit,
+        eax_dirty_bit_count
+    };
 
 
     using Eax4Props = EAX40CONTEXTPROPERTIES;
     using Eax4Props = EAX40CONTEXTPROPERTIES;
 
 
@@ -267,12 +288,11 @@ private:
         Eax5Props d; // Deferred.
         Eax5Props d; // Deferred.
     };
     };
 
 
-    class ContextException : public EaxException
-    {
+    class ContextException final : public EaxException {
     public:
     public:
-        explicit ContextException(const char* message)
+        explicit ContextException(const char *message)
             : EaxException{"EAX_CONTEXT", message}
             : EaxException{"EAX_CONTEXT", message}
-        {}
+        { }
     };
     };
 
 
     struct Eax4PrimaryFxSlotIdValidator {
     struct Eax4PrimaryFxSlotIdValidator {
@@ -420,7 +440,7 @@ private:
 
 
     int mEaxVersion{}; // Current EAX version.
     int mEaxVersion{}; // Current EAX version.
     bool mEaxNeedsCommit{};
     bool mEaxNeedsCommit{};
-    EaxDirtyFlags mEaxDf{}; // Dirty flags for the current EAX version.
+    std::bitset<eax_dirty_bit_count> mEaxDf; // Dirty flags for the current EAX version.
     Eax5State mEax123{}; // EAX1/EAX2/EAX3 state.
     Eax5State mEax123{}; // EAX1/EAX2/EAX3 state.
     Eax4State mEax4{}; // EAX4 state.
     Eax4State mEax4{}; // EAX4 state.
     Eax5State mEax5{}; // EAX5 state.
     Eax5State mEax5{}; // EAX5 state.
@@ -450,7 +470,7 @@ private:
     // updates a dirty flag.
     // updates a dirty flag.
     template<
     template<
         typename TValidator,
         typename TValidator,
-        EaxDirtyFlags TDirtyBit,
+        size_t DirtyBit,
         typename TMemberResult,
         typename TMemberResult,
         typename TProps,
         typename TProps,
         typename TState>
         typename TState>
@@ -463,20 +483,20 @@ private:
         dst_d = src;
         dst_d = src;
 
 
         if(dst_i != dst_d)
         if(dst_i != dst_d)
-            mEaxDf |= TDirtyBit;
+            mEaxDf.set(DirtyBit);
     }
     }
 
 
     template<
     template<
-        EaxDirtyFlags TDirtyBit,
+        size_t DirtyBit,
         typename TMemberResult,
         typename TMemberResult,
         typename TProps,
         typename TProps,
         typename TState>
         typename TState>
-    void eax_context_commit_property(TState& state, EaxDirtyFlags& dst_df,
+    void eax_context_commit_property(TState& state, std::bitset<eax_dirty_bit_count>& dst_df,
         TMemberResult TProps::*member) noexcept
         TMemberResult TProps::*member) noexcept
     {
     {
-        if((mEaxDf & TDirtyBit) != EaxDirtyFlags{})
+        if(mEaxDf.test(DirtyBit))
         {
         {
-            dst_df |= TDirtyBit;
+            dst_df.set(DirtyBit);
             const auto& src_d = state.d.*member;
             const auto& src_d = state.d.*member;
             state.i.*member = src_d;
             state.i.*member = src_d;
             mEax.*member = src_d;
             mEax.*member = src_d;
@@ -514,7 +534,7 @@ private:
 
 
     void eax_context_commit_primary_fx_slot_id();
     void eax_context_commit_primary_fx_slot_id();
     void eax_context_commit_distance_factor();
     void eax_context_commit_distance_factor();
-    void eax_context_commit_air_absorbtion_hf();
+    void eax_context_commit_air_absorption_hf();
     void eax_context_commit_hf_reference();
     void eax_context_commit_hf_reference();
     void eax_context_commit_macro_fx_factor();
     void eax_context_commit_macro_fx_factor();
 
 
@@ -529,8 +549,8 @@ private:
     void eax5_defer(const EaxCall& call, Eax5State& state);
     void eax5_defer(const EaxCall& call, Eax5State& state);
     void eax_set(const EaxCall& call);
     void eax_set(const EaxCall& call);
 
 
-    void eax4_context_commit(Eax4State& state, EaxDirtyFlags& dst_df);
-    void eax5_context_commit(Eax5State& state, EaxDirtyFlags& dst_df);
+    void eax4_context_commit(Eax4State& state, std::bitset<eax_dirty_bit_count>& dst_df);
+    void eax5_context_commit(Eax5State& state, std::bitset<eax_dirty_bit_count>& dst_df);
     void eax_context_commit();
     void eax_context_commit();
 #endif // ALSOFT_EAX
 #endif // ALSOFT_EAX
 };
 };
@@ -545,7 +565,7 @@ void UpdateContextProps(ALCcontext *context);
 inline bool TrapALError{false};
 inline bool TrapALError{false};
 
 
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 auto AL_APIENTRY EAXSet(const GUID *property_set_id, ALuint property_id,
 auto AL_APIENTRY EAXSet(const GUID *property_set_id, ALuint property_id,
     ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum;
     ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum;
 
 

+ 18 - 14
libs/openal-soft/alc/device.cpp

@@ -3,6 +3,7 @@
 
 
 #include "device.h"
 #include "device.h"
 
 
+#include <algorithm>
 #include <cstddef>
 #include <cstddef>
 #include <numeric>
 #include <numeric>
 
 
@@ -10,15 +11,14 @@
 #include "al/effect.h"
 #include "al/effect.h"
 #include "al/filter.h"
 #include "al/filter.h"
 #include "albit.h"
 #include "albit.h"
-#include "alconfig.h"
+#include "alnumeric.h"
+#include "atomic.h"
 #include "backends/base.h"
 #include "backends/base.h"
-#include "core/bformatdec.h"
-#include "core/bs2b.h"
-#include "core/front_stablizer.h"
+#include "core/devformat.h"
 #include "core/hrtf.h"
 #include "core/hrtf.h"
 #include "core/logging.h"
 #include "core/logging.h"
 #include "core/mastering.h"
 #include "core/mastering.h"
-#include "core/uhjfilter.h"
+#include "flexarray.h"
 
 
 
 
 namespace {
 namespace {
@@ -27,13 +27,14 @@ using voidp = void*;
 
 
 } // namespace
 } // namespace
 
 
+namespace al {
 
 
-ALCdevice::ALCdevice(DeviceType type) : DeviceBase{type}
+Device::Device(DeviceType type) : DeviceBase{type}
 { }
 { }
 
 
-ALCdevice::~ALCdevice()
+Device::~Device()
 {
 {
-    TRACE("Freeing device %p\n", voidp{this});
+    TRACE("Freeing device {}", voidp{this});
 
 
     Backend = nullptr;
     Backend = nullptr;
 
 
@@ -41,35 +42,35 @@ ALCdevice::~ALCdevice()
         [](size_t cur, const BufferSubList &sublist) noexcept -> size_t
         [](size_t cur, const BufferSubList &sublist) noexcept -> size_t
         { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })};
         { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })};
     if(count > 0)
     if(count > 0)
-        WARN("%zu Buffer%s not deleted\n", count, (count==1)?"":"s");
+        WARN("{} Buffer{} not deleted", count, (count==1)?"":"s");
 
 
     count = std::accumulate(EffectList.cbegin(), EffectList.cend(), 0_uz,
     count = std::accumulate(EffectList.cbegin(), EffectList.cend(), 0_uz,
         [](size_t cur, const EffectSubList &sublist) noexcept -> size_t
         [](size_t cur, const EffectSubList &sublist) noexcept -> size_t
         { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
         { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
     if(count > 0)
     if(count > 0)
-        WARN("%zu Effect%s not deleted\n", count, (count==1)?"":"s");
+        WARN("{} Effect{} not deleted", count, (count==1)?"":"s");
 
 
     count = std::accumulate(FilterList.cbegin(), FilterList.cend(), 0_uz,
     count = std::accumulate(FilterList.cbegin(), FilterList.cend(), 0_uz,
         [](size_t cur, const FilterSubList &sublist) noexcept -> size_t
         [](size_t cur, const FilterSubList &sublist) noexcept -> size_t
         { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
         { return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
     if(count > 0)
     if(count > 0)
-        WARN("%zu Filter%s not deleted\n", count, (count==1)?"":"s");
+        WARN("{} Filter{} not deleted", count, (count==1)?"":"s");
 }
 }
 
 
-void ALCdevice::enumerateHrtfs()
+void Device::enumerateHrtfs()
 {
 {
     mHrtfList = EnumerateHrtf(configValue<std::string>({}, "hrtf-paths"));
     mHrtfList = EnumerateHrtf(configValue<std::string>({}, "hrtf-paths"));
     if(auto defhrtfopt = configValue<std::string>({}, "default-hrtf"))
     if(auto defhrtfopt = configValue<std::string>({}, "default-hrtf"))
     {
     {
         auto iter = std::find(mHrtfList.begin(), mHrtfList.end(), *defhrtfopt);
         auto iter = std::find(mHrtfList.begin(), mHrtfList.end(), *defhrtfopt);
         if(iter == mHrtfList.end())
         if(iter == mHrtfList.end())
-            WARN("Failed to find default HRTF \"%s\"\n", defhrtfopt->c_str());
+            WARN("Failed to find default HRTF \"{}\"", *defhrtfopt);
         else if(iter != mHrtfList.begin())
         else if(iter != mHrtfList.begin())
             std::rotate(mHrtfList.begin(), iter, iter+1);
             std::rotate(mHrtfList.begin(), iter, iter+1);
     }
     }
 }
 }
 
 
-auto ALCdevice::getOutputMode1() const noexcept -> OutputMode1
+auto Device::getOutputMode1() const noexcept -> OutputMode1
 {
 {
     if(mContexts.load(std::memory_order_relaxed)->empty())
     if(mContexts.load(std::memory_order_relaxed)->empty())
         return OutputMode1::Any;
         return OutputMode1::Any;
@@ -88,9 +89,12 @@ auto ALCdevice::getOutputMode1() const noexcept -> OutputMode1
     case DevFmtX61: return OutputMode1::X61;
     case DevFmtX61: return OutputMode1::X61;
     case DevFmtX71: return OutputMode1::X71;
     case DevFmtX71: return OutputMode1::X71;
     case DevFmtX714:
     case DevFmtX714:
+    case DevFmtX7144:
     case DevFmtX3D71:
     case DevFmtX3D71:
     case DevFmtAmbi3D:
     case DevFmtAmbi3D:
         break;
         break;
     }
     }
     return OutputMode1::Any;
     return OutputMode1::Any;
 }
 }
+
+} // namespace al

+ 37 - 32
libs/openal-soft/alc/device.h

@@ -1,34 +1,29 @@
 #ifndef ALC_DEVICE_H
 #ifndef ALC_DEVICE_H
 #define ALC_DEVICE_H
 #define ALC_DEVICE_H
 
 
-#include <array>
+#include "config.h"
+
 #include <atomic>
 #include <atomic>
 #include <memory>
 #include <memory>
 #include <mutex>
 #include <mutex>
 #include <optional>
 #include <optional>
-#include <stdint.h>
 #include <string>
 #include <string>
 #include <unordered_map>
 #include <unordered_map>
-#include <utility>
+#include <string_view>
 #include <vector>
 #include <vector>
 
 
+#include "AL/al.h"
 #include "AL/alc.h"
 #include "AL/alc.h"
 #include "AL/alext.h"
 #include "AL/alext.h"
 
 
 #include "alconfig.h"
 #include "alconfig.h"
-#include "almalloc.h"
-#include "alnumeric.h"
 #include "core/device.h"
 #include "core/device.h"
-#include "inprogext.h"
 #include "intrusive_ptr.h"
 #include "intrusive_ptr.h"
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
 #include "al/eax/x_ram.h"
 #include "al/eax/x_ram.h"
 #endif // ALSOFT_EAX
 #endif // ALSOFT_EAX
 
 
-struct ALbuffer;
-struct ALeffect;
-struct ALfilter;
 struct BackendBase;
 struct BackendBase;
 struct BufferSubList;
 struct BufferSubList;
 struct EffectSubList;
 struct EffectSubList;
@@ -37,7 +32,11 @@ struct FilterSubList;
 using uint = unsigned int;
 using uint = unsigned int;
 
 
 
 
-struct ALCdevice : public al::intrusive_ref<ALCdevice>, DeviceBase {
+struct ALCdevice { virtual ~ALCdevice() = default; };
+
+namespace al {
+
+struct Device final : public ALCdevice, al::intrusive_ref<al::Device>, DeviceBase {
     /* This lock protects the device state (format, update size, etc) from
     /* This lock protects the device state (format, update size, etc) from
      * being from being changed in multiple threads, or being accessed while
      * being from being changed in multiple threads, or being accessed while
      * being changed. It's also used to serialize calls to the backend.
      * being changed. It's also used to serialize calls to the backend.
@@ -87,7 +86,7 @@ struct ALCdevice : public al::intrusive_ref<ALCdevice>, DeviceBase {
     std::mutex FilterLock;
     std::mutex FilterLock;
     std::vector<FilterSubList> FilterList;
     std::vector<FilterSubList> FilterList;
 
 
-#ifdef ALSOFT_EAX
+#if ALSOFT_EAX
     ALuint eax_x_ram_free_size{eax_x_ram_max_size};
     ALuint eax_x_ram_free_size{eax_x_ram_max_size};
 #endif // ALSOFT_EAX
 #endif // ALSOFT_EAX
 
 
@@ -96,35 +95,41 @@ struct ALCdevice : public al::intrusive_ref<ALCdevice>, DeviceBase {
     std::unordered_map<ALuint,std::string> mEffectNames;
     std::unordered_map<ALuint,std::string> mEffectNames;
     std::unordered_map<ALuint,std::string> mFilterNames;
     std::unordered_map<ALuint,std::string> mFilterNames;
 
 
-    ALCdevice(DeviceType type);
-    ~ALCdevice();
+    std::string mVendorOverride;
+    std::string mVersionOverride;
+    std::string mRendererOverride;
+
+    explicit Device(DeviceType type);
+    ~Device() final;
 
 
     void enumerateHrtfs();
     void enumerateHrtfs();
 
 
     bool getConfigValueBool(const std::string_view block, const std::string_view key, bool def)
     bool getConfigValueBool(const std::string_view block, const std::string_view key, bool def)
-    { return GetConfigValueBool(DeviceName, block, key, def); }
+    { return GetConfigValueBool(mDeviceName, block, key, def); }
 
 
     template<typename T>
     template<typename T>
-    inline std::optional<T> configValue(const std::string_view block, const std::string_view key) = delete;
+    auto configValue(const std::string_view block, const std::string_view key) -> std::optional<T> = delete;
 };
 };
 
 
-template<>
-inline std::optional<std::string> ALCdevice::configValue(const std::string_view block, const std::string_view key)
-{ return ConfigValueStr(DeviceName, block, key); }
-template<>
-inline std::optional<int> ALCdevice::configValue(const std::string_view block, const std::string_view key)
-{ return ConfigValueInt(DeviceName, block, key); }
-template<>
-inline std::optional<uint> ALCdevice::configValue(const std::string_view block, const std::string_view key)
-{ return ConfigValueUInt(DeviceName, block, key); }
-template<>
-inline std::optional<float> ALCdevice::configValue(const std::string_view block, const std::string_view key)
-{ return ConfigValueFloat(DeviceName, block, key); }
-template<>
-inline std::optional<bool> ALCdevice::configValue(const std::string_view block, const std::string_view key)
-{ return ConfigValueBool(DeviceName, block, key); }
+template<> inline
+auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional<std::string>
+{ return ConfigValueStr(mDeviceName, block, key); }
+template<> inline
+auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional<int>
+{ return ConfigValueInt(mDeviceName, block, key); }
+template<> inline
+auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional<uint>
+{ return ConfigValueUInt(mDeviceName, block, key); }
+template<> inline
+auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional<float>
+{ return ConfigValueFloat(mDeviceName, block, key); }
+template<> inline
+auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional<bool>
+{ return ConfigValueBool(mDeviceName, block, key); }
+
+} // namespace al
 
 
 /** Stores the latest ALC device error. */
 /** Stores the latest ALC device error. */
-void alcSetError(ALCdevice *device, ALCenum errorCode);
+void alcSetError(al::Device *device, ALCenum errorCode);
 
 
 #endif
 #endif

+ 2 - 2
libs/openal-soft/alc/effects/autowah.cpp

@@ -122,7 +122,7 @@ void AutowahState::update(const ContextBase *context, const EffectSlot *slot,
 {
 {
     auto &props = std::get<AutowahProps>(*props_);
     auto &props = std::get<AutowahProps>(*props_);
     const DeviceBase *device{context->mDevice};
     const DeviceBase *device{context->mDevice};
-    const auto frequency = static_cast<float>(device->Frequency);
+    const auto frequency = static_cast<float>(device->mSampleRate);
 
 
     const float ReleaseTime{std::clamp(props.ReleaseTime, 0.001f, 1.0f)};
     const float ReleaseTime{std::clamp(props.ReleaseTime, 0.001f, 1.0f)};
 
 
@@ -214,7 +214,7 @@ void AutowahState::process(const size_t samplesToDo,
         chandata->mFilter.z2 = z2;
         chandata->mFilter.z2 = z2;
 
 
         /* Now, mix the processed sound data to the output. */
         /* Now, mix the processed sound data to the output. */
-        MixSamples({mBufferOut.data(), samplesToDo}, samplesOut[outidx].data(),
+        MixSamples(al::span{mBufferOut}.first(samplesToDo), samplesOut[outidx],
             chandata->mCurrentGain, chandata->mTargetGain, samplesToDo);
             chandata->mCurrentGain, chandata->mTargetGain, samplesToDo);
         ++chandata;
         ++chandata;
     }
     }

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff