mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-15 16:44:36 +00:00
update openal-soft to 1.24.3
keeping the alt 87514151c4 (diff-73a8dc1ce58605f6c5ea53548454c3bae516ec5132a29c9d7ff7edf9730c75be)
This commit is contained in:
parent
12db0500e8
commit
ba32094b7b
276 changed files with 49304 additions and 8712 deletions
180
Engine/lib/openal-soft/.clang-tidy
Normal file
180
Engine/lib/openal-soft/.clang-tidy
Normal file
|
|
@ -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-*'
|
||||||
106
Engine/lib/openal-soft/.github/workflows/ci.yml
vendored
106
Engine/lib/openal-soft/.github/workflows/ci.yml
vendored
|
|
@ -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
|
- name: Set up Windows artifacts
|
||||||
if: ${{ matrix.config.os == 'windows-latest' }}
|
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
|
- name: Set up Android artifacts
|
||||||
# Upload package as an artifact of this workflow.
|
if: ${{ contains(matrix.config.name, 'Android') }}
|
||||||
uses: actions/upload-artifact@v3.1.2
|
shell: bash
|
||||||
if: ${{ matrix.config.os == 'windows-latest' }}
|
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/download-artifact@v4.1.7
|
||||||
|
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/sweelease@v1.1"
|
||||||
|
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/*"
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,35 @@
|
||||||
name: makemhr
|
name: utils
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- 'utils/makemhr/**'
|
- 'utils/**'
|
||||||
- '.github/workflows/makemhr.yml'
|
- '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
|
- name: Get current date, commit hash and count
|
||||||
run: echo "CurrentDate=$(date +'%Y-%m-%d')" >> $env:GITHUB_ENV
|
run: |
|
||||||
|
echo "CommitDate=$(git show -s --date=format:'%Y-%m-%d' --format=%cd)" >> $env:GITHUB_ENV
|
||||||
- name: Get commit hash
|
echo "CommitHashShort=$(git rev-parse --short=7 HEAD)" >> $env:GITHUB_ENV
|
||||||
run: echo "CommitHash=$(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/setup-msbuild@v1.1.3
|
uses: microsoft/setup-msbuild@v1.1.3
|
||||||
|
|
@ -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
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v3.1.2
|
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}}"
|
title: "[${{env.CommitDate}}] utils-r${{env.CommitCount}}@${{env.CommitHashShort}}"
|
||||||
files: "Release/makemhr.zip"
|
files: "Release/utils.zip"
|
||||||
|
|
@ -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.
|
# Prefer C17, but support earlier when necessary.
|
||||||
C_STANDARD 11)
|
C_STANDARD 17)
|
||||||
|
|
||||||
list (APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
list (APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_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_MINOR_VERSION "24")
|
||||||
set(LIB_REVISION "1")
|
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)
|
||||||
|
|
||||||
|
|
@ -227,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}")
|
||||||
|
|
@ -242,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
|
||||||
|
|
@ -277,15 +302,18 @@ else()
|
||||||
|
|
||||||
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
|
# NOTE: This essentially provides the equivalent of the C++26 feature to
|
||||||
# instead of NDEBUG)
|
# initialize all local variables with a non-0 bit pattern. Until C++26 is
|
||||||
foreach(flag_var CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO)
|
# adopted, the [[gnu::uninitialized]] attribute will avoid the auto-
|
||||||
if(${flag_var} MATCHES "-DNDEBUG")
|
# initialization where necessary.
|
||||||
string(REGEX REPLACE "-DNDEBUG" "-D_DEBUG" ${flag_var} "${${flag_var}}")
|
check_c_compiler_flag(-ftrivial-auto-var-init=pattern HAVE_FTRIVIAL_AUTO_VAR_INIT)
|
||||||
endif()
|
if(HAVE_FTRIVIAL_AUTO_VAR_INIT)
|
||||||
endforeach()
|
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)
|
||||||
|
|
@ -600,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
|
||||||
|
|
@ -657,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
|
||||||
|
|
@ -707,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)
|
||||||
|
|
@ -759,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
|
||||||
|
|
@ -861,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 "")
|
||||||
|
|
@ -976,7 +1006,7 @@ if(NOT WIN32)
|
||||||
if(SNDIO_FOUND)
|
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(ALC_OBJS ${ALC_OBJS} alc/backends/sndio.cpp alc/backends/sndio.hpp)
|
||||||
set(EXTRA_LIBS ${SNDIO_LIBRARIES} ${EXTRA_LIBS})
|
set(EXTRA_LIBS ${SNDIO_LIBRARIES} ${EXTRA_LIBS})
|
||||||
set(INC_PATHS ${INC_PATHS} ${SNDIO_INCLUDE_DIRS})
|
set(INC_PATHS ${INC_PATHS} ${SNDIO_INCLUDE_DIRS})
|
||||||
endif()
|
endif()
|
||||||
|
|
@ -1043,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")
|
||||||
|
|
@ -1055,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)
|
||||||
|
|
@ -1116,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)
|
||||||
|
|
@ -1148,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(BACKENDS "${BACKENDS} OpenSL${IS_LINKED},")
|
||||||
set(EXTRA_LIBS ${OPENSL_LIBRARIES} ${EXTRA_LIBS})
|
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()
|
||||||
|
|
@ -1165,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()
|
||||||
|
|
@ -1174,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
|
# Check for SDL2 or SDL3 backend
|
||||||
# Off by default, since it adds a runtime dependency
|
# 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)
|
||||||
|
|
@ -1188,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()
|
||||||
|
|
||||||
|
|
@ -1230,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")
|
||||||
|
|
@ -1259,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)
|
||||||
|
|
@ -1271,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)
|
endif()
|
||||||
find_package(FFmpeg COMPONENTS AVFORMAT AVCODEC AVUTIL SWSCALE SWRESAMPLE)
|
if(ALSOFT_EXAMPLES AND SDL3_FOUND)
|
||||||
endif()
|
find_package(FFmpeg COMPONENTS AVFORMAT AVCODEC AVUTIL SWSCALE SWRESAMPLE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT WIN32)
|
if(NOT WIN32)
|
||||||
|
|
@ -1295,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,
|
# as-is. If it's an SDL2 or SDL3 target, add the link flag for it.
|
||||||
# it's a name intended to be dressed as -lname.
|
# 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()
|
||||||
|
|
@ -1311,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})
|
add_library(alsoft.common STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS})
|
||||||
target_include_directories(alcommon PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/include
|
target_include_directories(alsoft.common PRIVATE ${OpenAL_SOURCE_DIR}/include
|
||||||
PUBLIC ${OpenAL_SOURCE_DIR}/common)
|
PUBLIC ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
|
||||||
target_compile_definitions(alcommon PRIVATE ${CPP_DEFS})
|
target_compile_definitions(alsoft.common PRIVATE ${CPP_DEFS})
|
||||||
target_compile_options(alcommon PRIVATE ${C_FLAGS})
|
target_compile_options(alsoft.common PRIVATE ${C_FLAGS})
|
||||||
set_target_properties(alcommon PROPERTIES ${DEFAULT_TARGET_PROPS} POSITION_INDEPENDENT_CODE TRUE)
|
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
|
||||||
|
|
@ -1357,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>
|
||||||
|
|
@ -1366,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)
|
if(TARGET alsoft.build_version)
|
||||||
add_dependencies(OpenAL build_version)
|
add_dependencies(OpenAL alsoft.build_version)
|
||||||
endif()
|
endif()
|
||||||
set(HAS_ROUTER 1)
|
set(HAS_ROUTER 1)
|
||||||
|
|
||||||
|
|
@ -1388,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_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)
|
find_program(NUGET_EXE NAMES nuget)
|
||||||
if(NOT NUGET_EXE)
|
if(NOT NUGET_EXE)
|
||||||
message("NUGET.EXE not found.")
|
message("NUGET.EXE not found.")
|
||||||
message(FATAL_ERROR "Please install this executable, and run CMake again.")
|
message(FATAL_ERROR "Please install this executable, and run CMake again.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
exec_program(${NUGET_EXE}
|
||||||
|
ARGS install "Microsoft.Windows.CppWinRT" -Version ${ALSOFT_CPPWINRT_VERSION} -ExcludeVersion -OutputDirectory "\"${CMAKE_BINARY_DIR}/packages\"")
|
||||||
|
|
||||||
|
set_target_properties(${IMPL_TARGET} PROPERTIES
|
||||||
|
VS_PROJECT_IMPORT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.props
|
||||||
|
)
|
||||||
|
target_link_libraries(${IMPL_TARGET} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.targets)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
exec_program(${NUGET_EXE}
|
|
||||||
ARGS install "Microsoft.Windows.CppWinRT" -Version ${ALSOFT_CPPWINRT_VERSION} -ExcludeVersion -OutputDirectory "\"${CMAKE_BINARY_DIR}/packages\"")
|
|
||||||
|
|
||||||
set_target_properties(${IMPL_TARGET} PROPERTIES
|
|
||||||
VS_PROJECT_IMPORT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.props
|
|
||||||
)
|
|
||||||
target_link_libraries(${IMPL_TARGET} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.targets)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT WIN32 AND NOT APPLE)
|
if(NOT WIN32 AND NOT APPLE)
|
||||||
|
|
@ -1464,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 $<$<BOOL:${ALSOFT_EAX}>:ALSOFT_EAX>
|
PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}"
|
||||||
"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)
|
if(TARGET alsoft.build_version)
|
||||||
add_dependencies(${IMPL_TARGET} 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")
|
||||||
|
|
@ -1598,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()
|
||||||
|
|
@ -1609,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
|
target_link_libraries(uhjdecoder PUBLIC alsoft.common
|
||||||
PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG})
|
PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG} alsoft::fmt)
|
||||||
set_target_properties(uhjdecoder PROPERTIES ${DEFAULT_TARGET_PROPS})
|
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
|
target_link_libraries(uhjencoder PUBLIC alsoft.common
|
||||||
PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG})
|
PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG} alsoft::fmt)
|
||||||
set_target_properties(uhjencoder PROPERTIES ${DEFAULT_TARGET_PROPS})
|
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})
|
add_library(alsoft.sofa-support STATIC EXCLUDE_FROM_ALL ${SOFA_SUPPORT_SRCS})
|
||||||
target_compile_definitions(sofa-support PRIVATE ${CPP_DEFS})
|
target_compile_definitions(alsoft.sofa-support PRIVATE ${CPP_DEFS})
|
||||||
target_include_directories(sofa-support PUBLIC ${OpenAL_SOURCE_DIR}/common)
|
target_include_directories(alsoft.sofa-support PUBLIC ${OpenAL_SOURCE_DIR}/common)
|
||||||
target_compile_options(sofa-support PRIVATE ${C_FLAGS})
|
target_compile_options(alsoft.sofa-support PRIVATE ${C_FLAGS})
|
||||||
target_link_libraries(sofa-support PUBLIC alcommon MySOFA::MySOFA PRIVATE ${LINKER_FLAGS})
|
target_link_libraries(alsoft.sofa-support PUBLIC alsoft.common MySOFA::MySOFA
|
||||||
set_target_properties(sofa-support PROPERTIES ${DEFAULT_TARGET_PROPS})
|
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
|
||||||
|
|
@ -1646,8 +1752,9 @@ if(ALSOFT_UTILS)
|
||||||
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})
|
target_link_libraries(makemhr PRIVATE ${LINKER_FLAGS} alsoft.sofa-support ${UNICODE_FLAG}
|
||||||
set_target_properties(makemhr PROPERTIES ${DEFAULT_TARGET_PROPS})
|
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()
|
||||||
|
|
@ -1657,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})
|
target_link_libraries(sofa-info PRIVATE ${LINKER_FLAGS} alsoft.sofa-support
|
||||||
set_target_properties(sofa-info PROPERTIES ${DEFAULT_TARGET_PROPS})
|
${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")
|
||||||
|
|
||||||
|
|
@ -1670,75 +1778,86 @@ 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_compile_definitions(alsoft.excommon PUBLIC ${CPP_DEFS})
|
||||||
target_include_directories(al-excommon PUBLIC ${OpenAL_SOURCE_DIR}/common)
|
target_include_directories(alsoft.excommon PUBLIC ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
|
||||||
target_compile_options(al-excommon PUBLIC ${C_FLAGS})
|
target_compile_options(alsoft.excommon PUBLIC ${C_FLAGS})
|
||||||
target_link_libraries(al-excommon PUBLIC OpenAL PRIVATE ${RT_LIB})
|
target_link_libraries(alsoft.excommon PUBLIC OpenAL PRIVATE ${RT_LIB})
|
||||||
set_target_properties(al-excommon PROPERTIES ${DEFAULT_TARGET_PROPS})
|
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})
|
target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} alsoft.excommon
|
||||||
set_target_properties(altonegen PROPERTIES ${DEFAULT_TARGET_PROPS})
|
${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})
|
target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} alsoft.excommon ${UNICODE_FLAG})
|
||||||
set_target_properties(alrecord PROPERTIES ${DEFAULT_TARGET_PROPS})
|
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})
|
PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon ${MATH_LIB} ${UNICODE_FLAG})
|
||||||
set_target_properties(almultireverb PROPERTIES ${DEFAULT_TARGET_PROPS})
|
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})
|
PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon ${MATH_LIB} ${UNICODE_FLAG})
|
||||||
set_target_properties(alhrtf PROPERTIES ${DEFAULT_TARGET_PROPS})
|
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
|
target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon
|
||||||
${UNICODE_FLAG})
|
${UNICODE_FLAG} alsoft::fmt)
|
||||||
set_target_properties(alstreamcb PROPERTIES ${DEFAULT_TARGET_PROPS})
|
set_target_properties(alstreamcb PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
|
||||||
|
|
||||||
add_executable(aldirect examples/aldirect.cpp)
|
add_executable(aldirect examples/aldirect.cpp)
|
||||||
target_link_libraries(aldirect PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
|
target_link_libraries(aldirect PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon
|
||||||
${UNICODE_FLAG})
|
${UNICODE_FLAG} alsoft::fmt)
|
||||||
set_target_properties(aldirect PROPERTIES ${DEFAULT_TARGET_PROPS})
|
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
|
target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} alsoft.common SndFile::SndFile
|
||||||
al-excommon ${UNICODE_FLAG})
|
alsoft.excommon ${UNICODE_FLAG})
|
||||||
set_target_properties(alconvolve PROPERTIES ${DEFAULT_TARGET_PROPS})
|
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
|
||||||
|
|
@ -1748,18 +1867,19 @@ if(ALSOFT_EXAMPLES)
|
||||||
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})
|
PRIVATE ${LINKER_FLAGS} SDL3::SDL3 alsoft.excommon ${MATH_LIB})
|
||||||
set_target_properties(alloopback PROPERTIES ${DEFAULT_TARGET_PROPS})
|
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)
|
||||||
|
|
@ -1788,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)
|
PRIVATE ${LINKER_FLAGS} SDL3::SDL3 ${FFMPEG_LIBRARIES} alsoft.excommon alsoft::fmt)
|
||||||
set_target_properties(alffplay PROPERTIES ${DEFAULT_TARGET_PROPS})
|
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()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
@ -54,27 +53,27 @@
|
||||||
#include "buffer.h"
|
#include "buffer.h"
|
||||||
#include "core/buffer_storage.h"
|
#include "core/buffer_storage.h"
|
||||||
#include "core/device.h"
|
#include "core/device.h"
|
||||||
|
#include "core/except.h"
|
||||||
#include "core/fpu_ctrl.h"
|
#include "core/fpu_ctrl.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "direct_defs.h"
|
#include "direct_defs.h"
|
||||||
#include "effect.h"
|
#include "effect.h"
|
||||||
#include "error.h"
|
|
||||||
#include "flexarray.h"
|
#include "flexarray.h"
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.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/effect.h"
|
#include "eax/effect.h"
|
||||||
#include "eax/fx_slot_index.h"
|
#include "eax/fx_slot_index.h"
|
||||||
#include "eax/utils.h"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using SubListAllocator = al::allocator<std::array<ALeffectslot,64>>;
|
using SubListAllocator = al::allocator<std::array<ALeffectslot,64>>;
|
||||||
|
|
||||||
EffectStateFactory *getFactoryByType(EffectSlotType type)
|
[[nodiscard]]
|
||||||
|
auto getFactoryByType(EffectSlotType type) -> EffectStateFactory*
|
||||||
{
|
{
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
|
|
@ -98,6 +97,7 @@ EffectStateFactory *getFactoryByType(EffectSlotType type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
auto LookupEffectSlot(ALCcontext *context, ALuint id) noexcept -> ALeffectslot*
|
auto LookupEffectSlot(ALCcontext *context, ALuint id) noexcept -> ALeffectslot*
|
||||||
{
|
{
|
||||||
const size_t lidx{(id-1) >> 6};
|
const size_t lidx{(id-1) >> 6};
|
||||||
|
|
@ -111,7 +111,8 @@ auto LookupEffectSlot(ALCcontext *context, ALuint id) noexcept -> ALeffectslot*
|
||||||
return al::to_address(sublist.EffectSlots->begin() + slidx);
|
return al::to_address(sublist.EffectSlots->begin() + 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};
|
||||||
|
|
@ -124,7 +125,8 @@ inline auto LookupEffect(ALCdevice *device, ALuint id) noexcept -> ALeffect*
|
||||||
return al::to_address(sublist.Effects->begin() + slidx);
|
return al::to_address(sublist.Effects->begin() + slidx);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto LookupBuffer(ALCdevice *device, ALuint id) noexcept -> ALbuffer*
|
[[nodiscard]]
|
||||||
|
inline auto LookupBuffer(al::Device *device, ALuint id) noexcept -> ALbuffer*
|
||||||
{
|
{
|
||||||
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};
|
||||||
|
|
@ -217,6 +219,7 @@ void RemoveActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
constexpr auto EffectSlotTypeFromEnum(ALenum type) noexcept -> EffectSlotType
|
constexpr auto EffectSlotTypeFromEnum(ALenum type) noexcept -> EffectSlotType
|
||||||
{
|
{
|
||||||
switch(type)
|
switch(type)
|
||||||
|
|
@ -239,10 +242,11 @@ constexpr auto EffectSlotTypeFromEnum(ALenum type) noexcept -> EffectSlotType
|
||||||
case AL_EFFECT_DEDICATED_DIALOGUE: return EffectSlotType::Dedicated;
|
case AL_EFFECT_DEDICATED_DIALOGUE: return EffectSlotType::Dedicated;
|
||||||
case AL_EFFECT_CONVOLUTION_SOFT: return EffectSlotType::Convolution;
|
case AL_EFFECT_CONVOLUTION_SOFT: return EffectSlotType::Convolution;
|
||||||
}
|
}
|
||||||
ERR("Unhandled effect enum: 0x%04x\n", type);
|
ERR("Unhandled effect enum: {:#04x}", as_unsigned(type));
|
||||||
return EffectSlotType::None;
|
return EffectSlotType::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
auto EnsureEffectSlots(ALCcontext *context, size_t needed) noexcept -> bool
|
auto EnsureEffectSlots(ALCcontext *context, size_t needed) noexcept -> bool
|
||||||
try {
|
try {
|
||||||
size_t count{std::accumulate(context->mEffectSlotList.cbegin(),
|
size_t count{std::accumulate(context->mEffectSlotList.cbegin(),
|
||||||
|
|
@ -267,7 +271,8 @@ catch(...) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ALeffectslot *AllocEffectSlot(ALCcontext *context)
|
[[nodiscard]]
|
||||||
|
auto AllocEffectSlot(ALCcontext *context) -> ALeffectslot*
|
||||||
{
|
{
|
||||||
auto sublist = std::find_if(context->mEffectSlotList.begin(), context->mEffectSlotList.end(),
|
auto sublist = std::find_if(context->mEffectSlotList.begin(), context->mEffectSlotList.end(),
|
||||||
[](const EffectSlotSubList &entry) noexcept -> bool
|
[](const EffectSlotSubList &entry) noexcept -> bool
|
||||||
|
|
@ -322,20 +327,21 @@ FORCE_ALIGN void AL_APIENTRY alGenAuxiliaryEffectSlotsDirect(ALCcontext *context
|
||||||
ALuint *effectslots) noexcept
|
ALuint *effectslots) noexcept
|
||||||
try {
|
try {
|
||||||
if(n < 0)
|
if(n < 0)
|
||||||
throw al::context_error{AL_INVALID_VALUE, "Generating %d effect slots", n};
|
context->throw_error(AL_INVALID_VALUE, "Generating {} effect slots", n);
|
||||||
if(n <= 0) UNLIKELY return;
|
if(n <= 0) UNLIKELY return;
|
||||||
|
|
||||||
std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
|
auto slotlock = std::lock_guard{context->mEffectSlotLock};
|
||||||
ALCdevice *device{context->mALDevice.get()};
|
auto *device = context->mALDevice.get();
|
||||||
|
|
||||||
const al::span eids{effectslots, static_cast<ALuint>(n)};
|
const al::span eids{effectslots, static_cast<ALuint>(n)};
|
||||||
if(eids.size() > device->AuxiliaryEffectSlotMax-context->mNumEffectSlots)
|
if(context->mNumEffectSlots > device->AuxiliaryEffectSlotMax
|
||||||
throw al::context_error{AL_OUT_OF_MEMORY, "Exceeding %u effect slot limit (%u + %d)",
|
|| eids.size() > device->AuxiliaryEffectSlotMax-context->mNumEffectSlots)
|
||||||
device->AuxiliaryEffectSlotMax, context->mNumEffectSlots, n};
|
context->throw_error(AL_OUT_OF_MEMORY, "Exceeding {} effect slot limit ({} + {})",
|
||||||
|
device->AuxiliaryEffectSlotMax, context->mNumEffectSlots, n);
|
||||||
|
|
||||||
if(!EnsureEffectSlots(context, eids.size()))
|
if(!EnsureEffectSlots(context, eids.size()))
|
||||||
throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d effectslot%s", n,
|
context->throw_error(AL_OUT_OF_MEMORY, "Failed to allocate {} effectslot{}", n,
|
||||||
(n == 1) ? "" : "s"};
|
(n==1) ? "" : "s");
|
||||||
|
|
||||||
std::vector<ALeffectslot*> slots;
|
std::vector<ALeffectslot*> slots;
|
||||||
try {
|
try {
|
||||||
|
|
@ -355,16 +361,18 @@ try {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(std::exception& e) {
|
catch(std::exception& e) {
|
||||||
ERR("Exception allocating effectslot %zu of %d: %s\n", slots.size()+1, n, e.what());
|
ERR("Exception allocating effectslot {} of {}: {}", slots.size()+1, n, e.what());
|
||||||
auto delete_effectslot = [context](ALeffectslot *slot) -> void
|
auto delete_effectslot = [context](ALeffectslot *slot) -> void
|
||||||
{ FreeEffectSlot(context, slot); };
|
{ FreeEffectSlot(context, slot); };
|
||||||
std::for_each(slots.begin(), slots.end(), delete_effectslot);
|
std::for_each(slots.begin(), slots.end(), delete_effectslot);
|
||||||
throw al::context_error{AL_INVALID_OPERATION, "Exception allocating %d effectslots: %s", n,
|
context->throw_error(AL_INVALID_OPERATION, "Exception allocating {} effectslots: {}", n,
|
||||||
e.what()};
|
e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
catch(std::exception &e) {
|
||||||
|
ERR("Caught exception: {}", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
AL_API DECL_FUNC2(void, alDeleteAuxiliaryEffectSlots, ALsizei,n, const ALuint*,effectslots)
|
AL_API DECL_FUNC2(void, alDeleteAuxiliaryEffectSlots, ALsizei,n, const ALuint*,effectslots)
|
||||||
|
|
@ -372,7 +380,7 @@ FORCE_ALIGN void AL_APIENTRY alDeleteAuxiliaryEffectSlotsDirect(ALCcontext *cont
|
||||||
const ALuint *effectslots) noexcept
|
const ALuint *effectslots) noexcept
|
||||||
try {
|
try {
|
||||||
if(n < 0) UNLIKELY
|
if(n < 0) UNLIKELY
|
||||||
throw al::context_error{AL_INVALID_VALUE, "Deleting %d effect slots", n};
|
context->throw_error(AL_INVALID_VALUE, "Deleting {} effect slots", n);
|
||||||
if(n <= 0) UNLIKELY return;
|
if(n <= 0) UNLIKELY return;
|
||||||
|
|
||||||
std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
|
std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
|
||||||
|
|
@ -380,10 +388,10 @@ try {
|
||||||
{
|
{
|
||||||
ALeffectslot *slot{LookupEffectSlot(context, *effectslots)};
|
ALeffectslot *slot{LookupEffectSlot(context, *effectslots)};
|
||||||
if(!slot)
|
if(!slot)
|
||||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", *effectslots};
|
context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", *effectslots);
|
||||||
if(slot->ref.load(std::memory_order_relaxed) != 0)
|
if(slot->ref.load(std::memory_order_relaxed) != 0)
|
||||||
throw al::context_error{AL_INVALID_OPERATION, "Deleting in-use effect slot %u",
|
context->throw_error(AL_INVALID_OPERATION, "Deleting in-use effect slot {}",
|
||||||
*effectslots};
|
*effectslots);
|
||||||
|
|
||||||
RemoveActiveEffectSlots({&slot, 1u}, context);
|
RemoveActiveEffectSlots({&slot, 1u}, context);
|
||||||
FreeEffectSlot(context, slot);
|
FreeEffectSlot(context, slot);
|
||||||
|
|
@ -398,10 +406,9 @@ try {
|
||||||
{
|
{
|
||||||
ALeffectslot *slot{LookupEffectSlot(context, eid)};
|
ALeffectslot *slot{LookupEffectSlot(context, eid)};
|
||||||
if(!slot)
|
if(!slot)
|
||||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", eid};
|
context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", eid);
|
||||||
if(slot->ref.load(std::memory_order_relaxed) != 0)
|
if(slot->ref.load(std::memory_order_relaxed) != 0)
|
||||||
throw al::context_error{AL_INVALID_OPERATION, "Deleting in-use effect slot %u",
|
context->throw_error(AL_INVALID_OPERATION, "Deleting in-use effect slot {}", eid);
|
||||||
eid};
|
|
||||||
return slot;
|
return slot;
|
||||||
};
|
};
|
||||||
std::transform(eids.cbegin(), eids.cend(), std::back_inserter(slots), lookupslot);
|
std::transform(eids.cbegin(), eids.cend(), std::back_inserter(slots), lookupslot);
|
||||||
|
|
@ -417,8 +424,10 @@ try {
|
||||||
std::for_each(eids.begin(), eids.end(), delete_effectslot);
|
std::for_each(eids.begin(), eids.end(), delete_effectslot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
catch(std::exception &e) {
|
||||||
|
ERR("Caught exception: {}", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
AL_API DECL_FUNC1(ALboolean, alIsAuxiliaryEffectSlot, ALuint,effectslot)
|
AL_API DECL_FUNC1(ALboolean, alIsAuxiliaryEffectSlot, ALuint,effectslot)
|
||||||
|
|
@ -436,7 +445,6 @@ AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint) noexcept
|
||||||
{
|
{
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if(!context) UNLIKELY return;
|
if(!context) UNLIKELY return;
|
||||||
|
|
||||||
context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotPlaySOFT not supported");
|
context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotPlaySOFT not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -444,7 +452,6 @@ AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei, const ALuint*) n
|
||||||
{
|
{
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if(!context) UNLIKELY return;
|
if(!context) UNLIKELY return;
|
||||||
|
|
||||||
context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotPlayvSOFT not supported");
|
context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotPlayvSOFT not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -452,7 +459,6 @@ AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint) noexcept
|
||||||
{
|
{
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if(!context) UNLIKELY return;
|
if(!context) UNLIKELY return;
|
||||||
|
|
||||||
context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotStopSOFT not supported");
|
context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotStopSOFT not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -460,7 +466,6 @@ AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei, const ALuint*) n
|
||||||
{
|
{
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if(!context) UNLIKELY return;
|
if(!context) UNLIKELY return;
|
||||||
|
|
||||||
context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotStopvSOFT not supported");
|
context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotStopvSOFT not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -474,7 +479,7 @@ try {
|
||||||
|
|
||||||
ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
|
ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
|
||||||
if(!slot) UNLIKELY
|
if(!slot) UNLIKELY
|
||||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
|
context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot);
|
||||||
|
|
||||||
ALeffectslot *target{};
|
ALeffectslot *target{};
|
||||||
ALenum err{};
|
ALenum err{};
|
||||||
|
|
@ -482,20 +487,20 @@ try {
|
||||||
{
|
{
|
||||||
case AL_EFFECTSLOT_EFFECT:
|
case AL_EFFECTSLOT_EFFECT:
|
||||||
{
|
{
|
||||||
ALCdevice *device{context->mALDevice.get()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
auto effectlock = std::lock_guard{device->EffectLock};
|
||||||
ALeffect *effect{value ? LookupEffect(device, static_cast<ALuint>(value)) : nullptr};
|
auto *effect = value ? LookupEffect(device, static_cast<ALuint>(value)) : nullptr;
|
||||||
if(effect)
|
if(effect)
|
||||||
err = slot->initEffect(effect->id, effect->type, effect->Props, context);
|
err = slot->initEffect(effect->id, effect->type, effect->Props, context);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(value != 0)
|
if(value != 0)
|
||||||
throw al::context_error{AL_INVALID_VALUE, "Invalid effect ID %u", value};
|
context->throw_error(AL_INVALID_VALUE, "Invalid effect ID {}", value);
|
||||||
err = slot->initEffect(0, AL_EFFECT_NULL, EffectProps{}, context);
|
err = slot->initEffect(0, AL_EFFECT_NULL, EffectProps{}, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(err != AL_NO_ERROR)
|
if(err != AL_NO_ERROR)
|
||||||
throw al::context_error{err, "Effect initialization failed"};
|
context->throw_error(err, "Effect initialization failed");
|
||||||
|
|
||||||
if(slot->mState == SlotState::Initial) UNLIKELY
|
if(slot->mState == SlotState::Initial) UNLIKELY
|
||||||
{
|
{
|
||||||
|
|
@ -511,8 +516,7 @@ try {
|
||||||
|
|
||||||
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
|
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
|
||||||
if(!(value == AL_TRUE || value == AL_FALSE))
|
if(!(value == AL_TRUE || value == AL_FALSE))
|
||||||
throw al::context_error{AL_INVALID_VALUE,
|
context->throw_error(AL_INVALID_VALUE, "Effect slot auxiliary send auto out of range");
|
||||||
"Effect slot auxiliary send auto out of range"};
|
|
||||||
if(!(slot->AuxSendAuto == !!value)) LIKELY
|
if(!(slot->AuxSendAuto == !!value)) LIKELY
|
||||||
{
|
{
|
||||||
slot->AuxSendAuto = !!value;
|
slot->AuxSendAuto = !!value;
|
||||||
|
|
@ -523,7 +527,7 @@ try {
|
||||||
case AL_EFFECTSLOT_TARGET_SOFT:
|
case AL_EFFECTSLOT_TARGET_SOFT:
|
||||||
target = LookupEffectSlot(context, static_cast<ALuint>(value));
|
target = LookupEffectSlot(context, static_cast<ALuint>(value));
|
||||||
if(value && !target)
|
if(value && !target)
|
||||||
throw al::context_error{AL_INVALID_VALUE, "Invalid effect slot target ID"};
|
context->throw_error(AL_INVALID_VALUE, "Invalid effect slot target ID {}", value);
|
||||||
if(slot->Target == target) UNLIKELY
|
if(slot->Target == target) UNLIKELY
|
||||||
return;
|
return;
|
||||||
if(target)
|
if(target)
|
||||||
|
|
@ -532,9 +536,9 @@ try {
|
||||||
while(checker && checker != slot)
|
while(checker && checker != slot)
|
||||||
checker = checker->Target;
|
checker = checker->Target;
|
||||||
if(checker)
|
if(checker)
|
||||||
throw al::context_error{AL_INVALID_OPERATION,
|
context->throw_error(AL_INVALID_OPERATION,
|
||||||
"Setting target of effect slot ID %u to %u creates circular chain", slot->id,
|
"Setting target of effect slot ID {} to {} creates circular chain", slot->id,
|
||||||
target->id};
|
target->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ALeffectslot *oldtarget{slot->Target})
|
if(ALeffectslot *oldtarget{slot->Target})
|
||||||
|
|
@ -569,17 +573,17 @@ try {
|
||||||
assert(factory);
|
assert(factory);
|
||||||
al::intrusive_ptr<EffectState> state{factory->create()};
|
al::intrusive_ptr<EffectState> state{factory->create()};
|
||||||
|
|
||||||
ALCdevice *device{context->mALDevice.get()};
|
auto *device = context->mALDevice.get();
|
||||||
auto bufferlock = std::unique_lock{device->BufferLock};
|
auto bufferlock = std::unique_lock{device->BufferLock};
|
||||||
ALbuffer *buffer{};
|
ALbuffer *buffer{};
|
||||||
if(value)
|
if(value)
|
||||||
{
|
{
|
||||||
buffer = LookupBuffer(device, static_cast<ALuint>(value));
|
buffer = LookupBuffer(device, static_cast<ALuint>(value));
|
||||||
if(!buffer)
|
if(!buffer)
|
||||||
throw al::context_error{AL_INVALID_VALUE, "Invalid buffer ID %u", value};
|
context->throw_error(AL_INVALID_VALUE, "Invalid buffer ID {}", value);
|
||||||
if(buffer->mCallback)
|
if(buffer->mCallback)
|
||||||
throw al::context_error{AL_INVALID_OPERATION,
|
context->throw_error(AL_INVALID_OPERATION,
|
||||||
"Callback buffer not valid for effects"};
|
"Callback buffer not valid for effects");
|
||||||
|
|
||||||
IncrementRef(buffer->ref);
|
IncrementRef(buffer->ref);
|
||||||
}
|
}
|
||||||
|
|
@ -605,17 +609,17 @@ try {
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ALCdevice *device{context->mALDevice.get()};
|
auto *device = context->mALDevice.get();
|
||||||
auto bufferlock = std::unique_lock{device->BufferLock};
|
auto bufferlock = std::unique_lock{device->BufferLock};
|
||||||
ALbuffer *buffer{};
|
ALbuffer *buffer{};
|
||||||
if(value)
|
if(value)
|
||||||
{
|
{
|
||||||
buffer = LookupBuffer(device, static_cast<ALuint>(value));
|
buffer = LookupBuffer(device, static_cast<ALuint>(value));
|
||||||
if(!buffer)
|
if(!buffer)
|
||||||
throw al::context_error{AL_INVALID_VALUE, "Invalid buffer ID %u", value};
|
context->throw_error(AL_INVALID_VALUE, "Invalid buffer ID {}", value);
|
||||||
if(buffer->mCallback)
|
if(buffer->mCallback)
|
||||||
throw al::context_error{AL_INVALID_OPERATION,
|
context->throw_error(AL_INVALID_OPERATION,
|
||||||
"Callback buffer not valid for effects"};
|
"Callback buffer not valid for effects");
|
||||||
|
|
||||||
IncrementRef(buffer->ref);
|
IncrementRef(buffer->ref);
|
||||||
}
|
}
|
||||||
|
|
@ -633,13 +637,16 @@ try {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case AL_EFFECTSLOT_STATE_SOFT:
|
case AL_EFFECTSLOT_STATE_SOFT:
|
||||||
throw al::context_error{AL_INVALID_OPERATION, "AL_EFFECTSLOT_STATE_SOFT is read-only"};
|
context->throw_error(AL_INVALID_OPERATION, "AL_EFFECTSLOT_STATE_SOFT is read-only");
|
||||||
}
|
}
|
||||||
|
|
||||||
throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param};
|
context->throw_error(AL_INVALID_ENUM, "Invalid effect slot integer property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
catch(std::exception &e) {
|
||||||
|
ERR("Caught exception: {}", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotiv, ALuint,effectslot, ALenum,param, const ALint*,values)
|
AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotiv, ALuint,effectslot, ALenum,param, const ALint*,values)
|
||||||
|
|
@ -660,16 +667,15 @@ try {
|
||||||
std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
|
std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
|
||||||
ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
|
ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
|
||||||
if(!slot)
|
if(!slot)
|
||||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
|
context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot);
|
||||||
|
|
||||||
switch(param)
|
context->throw_error(AL_INVALID_ENUM, "Invalid effect slot integer-vector property {:#04x}",
|
||||||
{
|
as_unsigned(param));
|
||||||
}
|
|
||||||
throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot integer-vector property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
catch(std::exception &e) {
|
||||||
|
ERR("Caught exception: {}", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotf, ALuint,effectslot, ALenum,param, ALfloat,value)
|
AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotf, ALuint,effectslot, ALenum,param, ALfloat,value)
|
||||||
|
|
@ -681,13 +687,13 @@ try {
|
||||||
|
|
||||||
ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
|
ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
|
||||||
if(!slot)
|
if(!slot)
|
||||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
|
context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot);
|
||||||
|
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_EFFECTSLOT_GAIN:
|
case AL_EFFECTSLOT_GAIN:
|
||||||
if(!(value >= 0.0f && value <= 1.0f))
|
if(!(value >= 0.0f && value <= 1.0f))
|
||||||
throw al::context_error{AL_INVALID_VALUE, "Effect slot gain out of range"};
|
context->throw_error(AL_INVALID_VALUE, "Effect slot gain {} out of range", value);
|
||||||
if(!(slot->Gain == value)) LIKELY
|
if(!(slot->Gain == value)) LIKELY
|
||||||
{
|
{
|
||||||
slot->Gain = value;
|
slot->Gain = value;
|
||||||
|
|
@ -696,10 +702,13 @@ try {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param};
|
context->throw_error(AL_INVALID_ENUM, "Invalid effect slot float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
catch(std::exception &e) {
|
||||||
|
ERR("Caught exception: {}", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotfv, ALuint,effectslot, ALenum,param, const ALfloat*,values)
|
AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotfv, ALuint,effectslot, ALenum,param, const ALfloat*,values)
|
||||||
|
|
@ -716,16 +725,15 @@ try {
|
||||||
std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
|
std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
|
||||||
ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
|
ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
|
||||||
if(!slot)
|
if(!slot)
|
||||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
|
context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot);
|
||||||
|
|
||||||
switch(param)
|
context->throw_error(AL_INVALID_ENUM, "Invalid effect slot float-vector property {:#04x}",
|
||||||
{
|
as_unsigned(param));
|
||||||
}
|
|
||||||
throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot float-vector property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
catch(std::exception &e) {
|
||||||
|
ERR("Caught exception: {}", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -736,7 +744,7 @@ try {
|
||||||
std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
|
std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
|
||||||
ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
|
ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
|
||||||
if(!slot)
|
if(!slot)
|
||||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
|
context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot);
|
||||||
|
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
|
|
@ -767,10 +775,13 @@ try {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param};
|
context->throw_error(AL_INVALID_ENUM, "Invalid effect slot integer property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
catch(std::exception &e) {
|
||||||
|
ERR("Caught exception: {}", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotiv, ALuint,effectslot, ALenum,param, ALint*,values)
|
AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotiv, ALuint,effectslot, ALenum,param, ALint*,values)
|
||||||
|
|
@ -791,16 +802,15 @@ try {
|
||||||
std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
|
std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
|
||||||
ALeffectslot *slot = LookupEffectSlot(context, effectslot);
|
ALeffectslot *slot = LookupEffectSlot(context, effectslot);
|
||||||
if(!slot)
|
if(!slot)
|
||||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
|
context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot);
|
||||||
|
|
||||||
switch(param)
|
context->throw_error(AL_INVALID_ENUM, "Invalid effect slot integer-vector property {:#04x}",
|
||||||
{
|
as_unsigned(param));
|
||||||
}
|
|
||||||
throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot integer-vector property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
catch(std::exception &e) {
|
||||||
|
ERR("Caught exception: {}", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotf, ALuint,effectslot, ALenum,param, ALfloat*,value)
|
AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotf, ALuint,effectslot, ALenum,param, ALfloat*,value)
|
||||||
|
|
@ -810,19 +820,20 @@ try {
|
||||||
std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
|
std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
|
||||||
ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
|
ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
|
||||||
if(!slot)
|
if(!slot)
|
||||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
|
context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot);
|
||||||
|
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_EFFECTSLOT_GAIN:
|
case AL_EFFECTSLOT_GAIN: *value = slot->Gain; return;
|
||||||
*value = slot->Gain;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param};
|
context->throw_error(AL_INVALID_ENUM, "Invalid effect slot float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
catch(std::exception &e) {
|
||||||
|
ERR("Caught exception: {}", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotfv, ALuint,effectslot, ALenum,param, ALfloat*,values)
|
AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotfv, ALuint,effectslot, ALenum,param, ALfloat*,values)
|
||||||
|
|
@ -839,16 +850,15 @@ try {
|
||||||
std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
|
std::lock_guard<std::mutex> slotlock{context->mEffectSlotLock};
|
||||||
ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
|
ALeffectslot *slot{LookupEffectSlot(context, effectslot)};
|
||||||
if(!slot)
|
if(!slot)
|
||||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot};
|
context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot);
|
||||||
|
|
||||||
switch(param)
|
context->throw_error(AL_INVALID_ENUM, "Invalid effect slot float-vector property {:#04x}",
|
||||||
{
|
as_unsigned(param));
|
||||||
}
|
|
||||||
throw al::context_error{AL_INVALID_ENUM, "Invalid effect slot float-vector property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
catch(std::exception &e) {
|
||||||
|
ERR("Caught exception: {}", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -890,12 +900,13 @@ ALenum ALeffectslot::initEffect(ALuint effectId, ALenum effectType, const Effect
|
||||||
EffectStateFactory *factory{getFactoryByType(newtype)};
|
EffectStateFactory *factory{getFactoryByType(newtype)};
|
||||||
if(!factory)
|
if(!factory)
|
||||||
{
|
{
|
||||||
ERR("Failed to find factory for effect slot type %d\n", static_cast<int>(newtype));
|
ERR("Failed to find factory for effect slot type {}",
|
||||||
|
int{al::to_underlying(newtype)});
|
||||||
return AL_INVALID_ENUM;
|
return AL_INVALID_ENUM;
|
||||||
}
|
}
|
||||||
al::intrusive_ptr<EffectState> state{factory->create()};
|
al::intrusive_ptr<EffectState> state{factory->create()};
|
||||||
|
|
||||||
ALCdevice *device{context->mALDevice.get()};
|
auto *device = context->mALDevice.get();
|
||||||
state->mOutTarget = device->Dry.Buffer;
|
state->mOutTarget = device->Dry.Buffer;
|
||||||
{
|
{
|
||||||
FPUCtl mixer_mode{};
|
FPUCtl mixer_mode{};
|
||||||
|
|
@ -964,7 +975,7 @@ void ALeffectslot::SetName(ALCcontext* context, ALuint id, std::string_view name
|
||||||
|
|
||||||
auto slot = LookupEffectSlot(context, id);
|
auto slot = LookupEffectSlot(context, id);
|
||||||
if(!slot)
|
if(!slot)
|
||||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect slot ID %u", id};
|
context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", id);
|
||||||
|
|
||||||
context->mEffectSlotNames.insert_or_assign(id, name);
|
context->mEffectSlotNames.insert_or_assign(id, name);
|
||||||
}
|
}
|
||||||
|
|
@ -1004,51 +1015,51 @@ EffectSlotSubList::~EffectSlotSubList()
|
||||||
EffectSlots = nullptr;
|
EffectSlots = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
void ALeffectslot::eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index)
|
void ALeffectslot::eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index)
|
||||||
{
|
{
|
||||||
if(index >= EAX_MAX_FXSLOTS)
|
if(index >= EAX_MAX_FXSLOTS)
|
||||||
eax_fail("Index out of range.");
|
eax_fail("Index out of range.");
|
||||||
|
|
||||||
eax_al_context_ = &al_context;
|
mEaxALContext = &al_context;
|
||||||
eax_fx_slot_index_ = index;
|
mEaxFXSlotIndex = index;
|
||||||
eax_fx_slot_set_defaults();
|
eax_fx_slot_set_defaults();
|
||||||
|
|
||||||
eax_effect_ = std::make_unique<EaxEffect>();
|
mEaxEffect = std::make_unique<EaxEffect>();
|
||||||
if(index == 0) eax_effect_->init<EaxReverbCommitter>();
|
if(index == 0) mEaxEffect->init<EaxReverbCommitter>();
|
||||||
else if(index == 1) eax_effect_->init<EaxChorusCommitter>();
|
else if(index == 1) mEaxEffect->init<EaxChorusCommitter>();
|
||||||
else eax_effect_->init<EaxNullCommitter>();
|
else mEaxEffect->init<EaxNullCommitter>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALeffectslot::eax_commit()
|
void ALeffectslot::eax_commit()
|
||||||
{
|
{
|
||||||
if(eax_df_ != EaxDirtyFlags{})
|
if(mEaxDf.any())
|
||||||
{
|
{
|
||||||
auto df = EaxDirtyFlags{};
|
auto df = std::bitset<eax_dirty_bit_count>{};
|
||||||
switch(eax_version_)
|
switch(mEaxVersion)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
case 2:
|
case 2:
|
||||||
case 3:
|
case 3:
|
||||||
eax5_fx_slot_commit(eax123_, df);
|
eax5_fx_slot_commit(mEax123, df);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
eax4_fx_slot_commit(df);
|
eax4_fx_slot_commit(df);
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
eax5_fx_slot_commit(eax5_, df);
|
eax5_fx_slot_commit(mEax5, df);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
eax_df_ = EaxDirtyFlags{};
|
mEaxDf.reset();
|
||||||
|
|
||||||
if((df & eax_volume_dirty_bit) != EaxDirtyFlags{})
|
if(df.test(eax_volume_dirty_bit))
|
||||||
eax_fx_slot_set_volume();
|
eax_fx_slot_set_volume();
|
||||||
if((df & eax_flags_dirty_bit) != EaxDirtyFlags{})
|
if(df.test(eax_flags_dirty_bit))
|
||||||
eax_fx_slot_set_flags();
|
eax_fx_slot_set_flags();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(eax_effect_->commit(eax_version_))
|
if(mEaxEffect->commit(mEaxVersion))
|
||||||
eax_set_efx_slot_effect(*eax_effect_);
|
eax_set_efx_slot_effect(*mEaxEffect);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] void ALeffectslot::eax_fail(const char* message)
|
[[noreturn]] void ALeffectslot::eax_fail(const char* message)
|
||||||
|
|
@ -1111,7 +1122,7 @@ ALenum ALeffectslot::eax_get_efx_effect_type(const GUID& guid)
|
||||||
|
|
||||||
const GUID& ALeffectslot::eax_get_eax_default_effect_guid() const noexcept
|
const GUID& ALeffectslot::eax_get_eax_default_effect_guid() const noexcept
|
||||||
{
|
{
|
||||||
switch(eax_fx_slot_index_)
|
switch(mEaxFXSlotIndex)
|
||||||
{
|
{
|
||||||
case 0: return EAX_REVERB_EFFECT;
|
case 0: return EAX_REVERB_EFFECT;
|
||||||
case 1: return EAX_CHORUS_EFFECT;
|
case 1: return EAX_CHORUS_EFFECT;
|
||||||
|
|
@ -1124,7 +1135,7 @@ long ALeffectslot::eax_get_eax_default_lock() const noexcept
|
||||||
return eax4_fx_slot_is_legacy() ? EAXFXSLOT_LOCKED : EAXFXSLOT_UNLOCKED;
|
return eax4_fx_slot_is_legacy() ? EAXFXSLOT_LOCKED : EAXFXSLOT_UNLOCKED;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALeffectslot::eax4_fx_slot_set_defaults(Eax4Props& props) noexcept
|
void ALeffectslot::eax4_fx_slot_set_defaults(EAX40FXSLOTPROPERTIES& props) noexcept
|
||||||
{
|
{
|
||||||
props.guidLoadEffect = eax_get_eax_default_effect_guid();
|
props.guidLoadEffect = eax_get_eax_default_effect_guid();
|
||||||
props.lVolume = EAXFXSLOT_DEFAULTVOLUME;
|
props.lVolume = EAXFXSLOT_DEFAULTVOLUME;
|
||||||
|
|
@ -1132,7 +1143,7 @@ void ALeffectslot::eax4_fx_slot_set_defaults(Eax4Props& props) noexcept
|
||||||
props.ulFlags = EAX40FXSLOT_DEFAULTFLAGS;
|
props.ulFlags = EAX40FXSLOT_DEFAULTFLAGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALeffectslot::eax5_fx_slot_set_defaults(Eax5Props& props) noexcept
|
void ALeffectslot::eax5_fx_slot_set_defaults(EAX50FXSLOTPROPERTIES& props) noexcept
|
||||||
{
|
{
|
||||||
props.guidLoadEffect = eax_get_eax_default_effect_guid();
|
props.guidLoadEffect = eax_get_eax_default_effect_guid();
|
||||||
props.lVolume = EAXFXSLOT_DEFAULTVOLUME;
|
props.lVolume = EAXFXSLOT_DEFAULTVOLUME;
|
||||||
|
|
@ -1144,14 +1155,14 @@ void ALeffectslot::eax5_fx_slot_set_defaults(Eax5Props& props) noexcept
|
||||||
|
|
||||||
void ALeffectslot::eax_fx_slot_set_defaults()
|
void ALeffectslot::eax_fx_slot_set_defaults()
|
||||||
{
|
{
|
||||||
eax5_fx_slot_set_defaults(eax123_.i);
|
eax5_fx_slot_set_defaults(mEax123.i);
|
||||||
eax4_fx_slot_set_defaults(eax4_.i);
|
eax4_fx_slot_set_defaults(mEax4.i);
|
||||||
eax5_fx_slot_set_defaults(eax5_.i);
|
eax5_fx_slot_set_defaults(mEax5.i);
|
||||||
eax_ = eax5_.i;
|
mEax = mEax5.i;
|
||||||
eax_df_ = EaxDirtyFlags{};
|
mEaxDf.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALeffectslot::eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props)
|
void ALeffectslot::eax4_fx_slot_get(const EaxCall& call, const EAX40FXSLOTPROPERTIES& props)
|
||||||
{
|
{
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
|
|
@ -1175,7 +1186,7 @@ void ALeffectslot::eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALeffectslot::eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props)
|
void ALeffectslot::eax5_fx_slot_get(const EaxCall& call, const EAX50FXSLOTPROPERTIES& props)
|
||||||
{
|
{
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
|
|
@ -1209,8 +1220,8 @@ void ALeffectslot::eax_fx_slot_get(const EaxCall& call) const
|
||||||
{
|
{
|
||||||
switch(call.get_version())
|
switch(call.get_version())
|
||||||
{
|
{
|
||||||
case 4: eax4_fx_slot_get(call, eax4_.i); break;
|
case 4: eax4_fx_slot_get(call, mEax4.i); break;
|
||||||
case 5: eax5_fx_slot_get(call, eax5_.i); break;
|
case 5: eax5_fx_slot_get(call, mEax5.i); break;
|
||||||
default: eax_fail_unknown_version();
|
default: eax_fail_unknown_version();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1223,7 +1234,7 @@ bool ALeffectslot::eax_get(const EaxCall& call)
|
||||||
eax_fx_slot_get(call);
|
eax_fx_slot_get(call);
|
||||||
break;
|
break;
|
||||||
case EaxCallPropertySetId::fx_slot_effect:
|
case EaxCallPropertySetId::fx_slot_effect:
|
||||||
eax_effect_->get(call);
|
mEaxEffect->get(call);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
eax_fail_unknown_property_id();
|
eax_fail_unknown_property_id();
|
||||||
|
|
@ -1236,19 +1247,19 @@ void ALeffectslot::eax_fx_slot_load_effect(int version, ALenum altype)
|
||||||
{
|
{
|
||||||
if(!IsValidEffectType(altype))
|
if(!IsValidEffectType(altype))
|
||||||
altype = AL_EFFECT_NULL;
|
altype = AL_EFFECT_NULL;
|
||||||
eax_effect_->set_defaults(version, altype);
|
mEaxEffect->set_defaults(version, altype);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALeffectslot::eax_fx_slot_set_volume()
|
void ALeffectslot::eax_fx_slot_set_volume()
|
||||||
{
|
{
|
||||||
const auto volume = std::clamp(eax_.lVolume, EAXFXSLOT_MINVOLUME, EAXFXSLOT_MAXVOLUME);
|
const auto volume = std::clamp(mEax.lVolume, EAXFXSLOT_MINVOLUME, EAXFXSLOT_MAXVOLUME);
|
||||||
const auto gain = level_mb_to_gain(static_cast<float>(volume));
|
const auto gain = level_mb_to_gain(static_cast<float>(volume));
|
||||||
eax_set_efx_slot_gain(gain);
|
eax_set_efx_slot_gain(gain);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALeffectslot::eax_fx_slot_set_environment_flag()
|
void ALeffectslot::eax_fx_slot_set_environment_flag()
|
||||||
{
|
{
|
||||||
eax_set_efx_slot_send_auto((eax_.ulFlags & EAXFXSLOTFLAGS_ENVIRONMENT) != 0u);
|
eax_set_efx_slot_send_auto((mEax.ulFlags & EAXFXSLOTFLAGS_ENVIRONMENT) != 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALeffectslot::eax_fx_slot_set_flags()
|
void ALeffectslot::eax_fx_slot_set_flags()
|
||||||
|
|
@ -1261,11 +1272,11 @@ void ALeffectslot::eax4_fx_slot_set_all(const EaxCall& call)
|
||||||
eax4_fx_slot_ensure_unlocked();
|
eax4_fx_slot_ensure_unlocked();
|
||||||
const auto& src = call.get_value<Exception, const EAX40FXSLOTPROPERTIES>();
|
const auto& src = call.get_value<Exception, const EAX40FXSLOTPROPERTIES>();
|
||||||
Eax4AllValidator{}(src);
|
Eax4AllValidator{}(src);
|
||||||
auto& dst = eax4_.i;
|
auto& dst = mEax4.i;
|
||||||
eax_df_ |= eax_load_effect_dirty_bit; // Always reset the effect.
|
mEaxDf.set(eax_load_effect_dirty_bit); // Always reset the effect.
|
||||||
eax_df_ |= (dst.lVolume != src.lVolume ? eax_volume_dirty_bit : EaxDirtyFlags{});
|
if(dst.lVolume != src.lVolume) mEaxDf.set(eax_volume_dirty_bit);
|
||||||
eax_df_ |= (dst.lLock != src.lLock ? eax_lock_dirty_bit : EaxDirtyFlags{});
|
if(dst.lLock != src.lLock) mEaxDf.set(eax_lock_dirty_bit);
|
||||||
eax_df_ |= (dst.ulFlags != src.ulFlags ? eax_flags_dirty_bit : EaxDirtyFlags{});
|
if(dst.ulFlags != src.ulFlags) mEaxDf.set(eax_flags_dirty_bit);
|
||||||
dst = src;
|
dst = src;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1273,30 +1284,30 @@ void ALeffectslot::eax5_fx_slot_set_all(const EaxCall& call)
|
||||||
{
|
{
|
||||||
const auto& src = call.get_value<Exception, const EAX50FXSLOTPROPERTIES>();
|
const auto& src = call.get_value<Exception, const EAX50FXSLOTPROPERTIES>();
|
||||||
Eax5AllValidator{}(src);
|
Eax5AllValidator{}(src);
|
||||||
auto& dst = eax5_.i;
|
auto& dst = mEax5.i;
|
||||||
eax_df_ |= eax_load_effect_dirty_bit; // Always reset the effect.
|
mEaxDf.set(eax_load_effect_dirty_bit); // Always reset the effect.
|
||||||
eax_df_ |= (dst.lVolume != src.lVolume ? eax_volume_dirty_bit : EaxDirtyFlags{});
|
if(dst.lVolume != src.lVolume) mEaxDf.set(eax_volume_dirty_bit);
|
||||||
eax_df_ |= (dst.lLock != src.lLock ? eax_lock_dirty_bit : EaxDirtyFlags{});
|
if(dst.lLock != src.lLock) mEaxDf.set(eax_lock_dirty_bit);
|
||||||
eax_df_ |= (dst.ulFlags != src.ulFlags ? eax_flags_dirty_bit : EaxDirtyFlags{});
|
if(dst.ulFlags != src.ulFlags) mEaxDf.set(eax_flags_dirty_bit);
|
||||||
eax_df_ |= (dst.lOcclusion != src.lOcclusion ? eax_flags_dirty_bit : EaxDirtyFlags{});
|
if(dst.lOcclusion != src.lOcclusion) mEaxDf.set(eax_flags_dirty_bit);
|
||||||
eax_df_ |= (dst.flOcclusionLFRatio != src.flOcclusionLFRatio ? eax_flags_dirty_bit : EaxDirtyFlags{});
|
if(dst.flOcclusionLFRatio != src.flOcclusionLFRatio) mEaxDf.set(eax_flags_dirty_bit);
|
||||||
dst = src;
|
dst = src;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ALeffectslot::eax_fx_slot_should_update_sources() const noexcept
|
bool ALeffectslot::eax_fx_slot_should_update_sources() const noexcept
|
||||||
{
|
{
|
||||||
static constexpr auto dirty_bits =
|
static constexpr auto dirty_bits = std::bitset<eax_dirty_bit_count>{
|
||||||
eax_occlusion_dirty_bit |
|
(1u << eax_occlusion_dirty_bit)
|
||||||
eax_occlusion_lf_ratio_dirty_bit |
|
| (1u << eax_occlusion_lf_ratio_dirty_bit)
|
||||||
eax_flags_dirty_bit;
|
| (1u << eax_flags_dirty_bit)
|
||||||
|
};
|
||||||
return (eax_df_ & dirty_bits) != EaxDirtyFlags{};
|
return (mEaxDf & dirty_bits).any();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns `true` if all sources should be updated, or `false` otherwise.
|
// Returns `true` if all sources should be updated, or `false` otherwise.
|
||||||
bool ALeffectslot::eax4_fx_slot_set(const EaxCall& call)
|
bool ALeffectslot::eax4_fx_slot_set(const EaxCall& call)
|
||||||
{
|
{
|
||||||
auto& dst = eax4_.i;
|
auto& dst = mEax4.i;
|
||||||
|
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
|
|
@ -1304,24 +1315,25 @@ bool ALeffectslot::eax4_fx_slot_set(const EaxCall& call)
|
||||||
break;
|
break;
|
||||||
case EAXFXSLOT_ALLPARAMETERS:
|
case EAXFXSLOT_ALLPARAMETERS:
|
||||||
eax4_fx_slot_set_all(call);
|
eax4_fx_slot_set_all(call);
|
||||||
if((eax_df_ & eax_load_effect_dirty_bit))
|
if(mEaxDf.test(eax_load_effect_dirty_bit))
|
||||||
eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst.guidLoadEffect));
|
eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst.guidLoadEffect));
|
||||||
break;
|
break;
|
||||||
case EAXFXSLOT_LOADEFFECT:
|
case EAXFXSLOT_LOADEFFECT:
|
||||||
eax4_fx_slot_ensure_unlocked();
|
eax4_fx_slot_ensure_unlocked();
|
||||||
eax_fx_slot_set_dirty<Eax4GuidLoadEffectValidator, eax_load_effect_dirty_bit>(call, dst.guidLoadEffect, eax_df_);
|
eax_fx_slot_set_dirty<Eax4GuidLoadEffectValidator, eax_load_effect_dirty_bit>(call,
|
||||||
if((eax_df_ & eax_load_effect_dirty_bit))
|
dst.guidLoadEffect, mEaxDf);
|
||||||
|
if(mEaxDf.test(eax_load_effect_dirty_bit))
|
||||||
eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst.guidLoadEffect));
|
eax_fx_slot_load_effect(4, eax_get_efx_effect_type(dst.guidLoadEffect));
|
||||||
break;
|
break;
|
||||||
case EAXFXSLOT_VOLUME:
|
case EAXFXSLOT_VOLUME:
|
||||||
eax_fx_slot_set<Eax4VolumeValidator, eax_volume_dirty_bit>(call, dst.lVolume, eax_df_);
|
eax_fx_slot_set<Eax4VolumeValidator, eax_volume_dirty_bit>(call, dst.lVolume, mEaxDf);
|
||||||
break;
|
break;
|
||||||
case EAXFXSLOT_LOCK:
|
case EAXFXSLOT_LOCK:
|
||||||
eax4_fx_slot_ensure_unlocked();
|
eax4_fx_slot_ensure_unlocked();
|
||||||
eax_fx_slot_set<Eax4LockValidator, eax_lock_dirty_bit>(call, dst.lLock, eax_df_);
|
eax_fx_slot_set<Eax4LockValidator, eax_lock_dirty_bit>(call, dst.lLock, mEaxDf);
|
||||||
break;
|
break;
|
||||||
case EAXFXSLOT_FLAGS:
|
case EAXFXSLOT_FLAGS:
|
||||||
eax_fx_slot_set<Eax4FlagsValidator, eax_flags_dirty_bit>(call, dst.ulFlags, eax_df_);
|
eax_fx_slot_set<Eax4FlagsValidator, eax_flags_dirty_bit>(call, dst.ulFlags, mEaxDf);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
eax_fail_unknown_property_id();
|
eax_fail_unknown_property_id();
|
||||||
|
|
@ -1333,7 +1345,7 @@ bool ALeffectslot::eax4_fx_slot_set(const EaxCall& call)
|
||||||
// Returns `true` if all sources should be updated, or `false` otherwise.
|
// Returns `true` if all sources should be updated, or `false` otherwise.
|
||||||
bool ALeffectslot::eax5_fx_slot_set(const EaxCall& call)
|
bool ALeffectslot::eax5_fx_slot_set(const EaxCall& call)
|
||||||
{
|
{
|
||||||
auto& dst = eax5_.i;
|
auto& dst = mEax5.i;
|
||||||
|
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
|
|
@ -1341,28 +1353,31 @@ bool ALeffectslot::eax5_fx_slot_set(const EaxCall& call)
|
||||||
break;
|
break;
|
||||||
case EAXFXSLOT_ALLPARAMETERS:
|
case EAXFXSLOT_ALLPARAMETERS:
|
||||||
eax5_fx_slot_set_all(call);
|
eax5_fx_slot_set_all(call);
|
||||||
if((eax_df_ & eax_load_effect_dirty_bit))
|
if(mEaxDf.test(eax_load_effect_dirty_bit))
|
||||||
eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst.guidLoadEffect));
|
eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst.guidLoadEffect));
|
||||||
break;
|
break;
|
||||||
case EAXFXSLOT_LOADEFFECT:
|
case EAXFXSLOT_LOADEFFECT:
|
||||||
eax_fx_slot_set_dirty<Eax4GuidLoadEffectValidator, eax_load_effect_dirty_bit>(call, dst.guidLoadEffect, eax_df_);
|
eax_fx_slot_set_dirty<Eax4GuidLoadEffectValidator, eax_load_effect_dirty_bit>(call,
|
||||||
if((eax_df_ & eax_load_effect_dirty_bit))
|
dst.guidLoadEffect, mEaxDf);
|
||||||
|
if(mEaxDf.test(eax_load_effect_dirty_bit))
|
||||||
eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst.guidLoadEffect));
|
eax_fx_slot_load_effect(5, eax_get_efx_effect_type(dst.guidLoadEffect));
|
||||||
break;
|
break;
|
||||||
case EAXFXSLOT_VOLUME:
|
case EAXFXSLOT_VOLUME:
|
||||||
eax_fx_slot_set<Eax4VolumeValidator, eax_volume_dirty_bit>(call, dst.lVolume, eax_df_);
|
eax_fx_slot_set<Eax4VolumeValidator, eax_volume_dirty_bit>(call, dst.lVolume, mEaxDf);
|
||||||
break;
|
break;
|
||||||
case EAXFXSLOT_LOCK:
|
case EAXFXSLOT_LOCK:
|
||||||
eax_fx_slot_set<Eax4LockValidator, eax_lock_dirty_bit>(call, dst.lLock, eax_df_);
|
eax_fx_slot_set<Eax4LockValidator, eax_lock_dirty_bit>(call, dst.lLock, mEaxDf);
|
||||||
break;
|
break;
|
||||||
case EAXFXSLOT_FLAGS:
|
case EAXFXSLOT_FLAGS:
|
||||||
eax_fx_slot_set<Eax5FlagsValidator, eax_flags_dirty_bit>(call, dst.ulFlags, eax_df_);
|
eax_fx_slot_set<Eax5FlagsValidator, eax_flags_dirty_bit>(call, dst.ulFlags, mEaxDf);
|
||||||
break;
|
break;
|
||||||
case EAXFXSLOT_OCCLUSION:
|
case EAXFXSLOT_OCCLUSION:
|
||||||
eax_fx_slot_set<Eax5OcclusionValidator, eax_occlusion_dirty_bit>(call, dst.lOcclusion, eax_df_);
|
eax_fx_slot_set<Eax5OcclusionValidator, eax_occlusion_dirty_bit>(call, dst.lOcclusion,
|
||||||
|
mEaxDf);
|
||||||
break;
|
break;
|
||||||
case EAXFXSLOT_OCCLUSIONLFRATIO:
|
case EAXFXSLOT_OCCLUSIONLFRATIO:
|
||||||
eax_fx_slot_set<Eax5OcclusionLfRatioValidator, eax_occlusion_lf_ratio_dirty_bit>(call, dst.flOcclusionLFRatio, eax_df_);
|
eax_fx_slot_set<Eax5OcclusionLfRatioValidator, eax_occlusion_lf_ratio_dirty_bit>(call,
|
||||||
|
dst.flOcclusionLFRatio, mEaxDf);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
eax_fail_unknown_property_id();
|
eax_fail_unknown_property_id();
|
||||||
|
|
@ -1374,7 +1389,7 @@ bool ALeffectslot::eax5_fx_slot_set(const EaxCall& call)
|
||||||
// Returns `true` if all sources should be updated, or `false` otherwise.
|
// Returns `true` if all sources should be updated, or `false` otherwise.
|
||||||
bool ALeffectslot::eax_fx_slot_set(const EaxCall& call)
|
bool ALeffectslot::eax_fx_slot_set(const EaxCall& call)
|
||||||
{
|
{
|
||||||
switch (call.get_version())
|
switch(call.get_version())
|
||||||
{
|
{
|
||||||
case 4: return eax4_fx_slot_set(call);
|
case 4: return eax4_fx_slot_set(call);
|
||||||
case 5: return eax5_fx_slot_set(call);
|
case 5: return eax5_fx_slot_set(call);
|
||||||
|
|
@ -1390,39 +1405,41 @@ bool ALeffectslot::eax_set(const EaxCall& call)
|
||||||
switch(call.get_property_set_id())
|
switch(call.get_property_set_id())
|
||||||
{
|
{
|
||||||
case EaxCallPropertySetId::fx_slot: ret = eax_fx_slot_set(call); break;
|
case EaxCallPropertySetId::fx_slot: ret = eax_fx_slot_set(call); break;
|
||||||
case EaxCallPropertySetId::fx_slot_effect: eax_effect_->set(call); break;
|
case EaxCallPropertySetId::fx_slot_effect: mEaxEffect->set(call); break;
|
||||||
default: eax_fail_unknown_property_id();
|
default: eax_fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto version = call.get_version();
|
const auto version = call.get_version();
|
||||||
if(eax_version_ != version)
|
if(mEaxVersion != version)
|
||||||
eax_df_ = ~EaxDirtyFlags{};
|
mEaxDf.set();
|
||||||
eax_version_ = version;
|
mEaxVersion = version;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALeffectslot::eax4_fx_slot_commit(EaxDirtyFlags& dst_df)
|
void ALeffectslot::eax4_fx_slot_commit(std::bitset<eax_dirty_bit_count>& dst_df)
|
||||||
{
|
{
|
||||||
eax_fx_slot_commit_property<eax_load_effect_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::guidLoadEffect);
|
eax_fx_slot_commit_property<eax_load_effect_dirty_bit>(mEax4, dst_df, &EAX40FXSLOTPROPERTIES::guidLoadEffect);
|
||||||
eax_fx_slot_commit_property<eax_volume_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::lVolume);
|
eax_fx_slot_commit_property<eax_volume_dirty_bit>(mEax4, dst_df, &EAX40FXSLOTPROPERTIES::lVolume);
|
||||||
eax_fx_slot_commit_property<eax_lock_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::lLock);
|
eax_fx_slot_commit_property<eax_lock_dirty_bit>(mEax4, dst_df, &EAX40FXSLOTPROPERTIES::lLock);
|
||||||
eax_fx_slot_commit_property<eax_flags_dirty_bit>(eax4_, dst_df, &EAX40FXSLOTPROPERTIES::ulFlags);
|
eax_fx_slot_commit_property<eax_flags_dirty_bit>(mEax4, dst_df, &EAX40FXSLOTPROPERTIES::ulFlags);
|
||||||
|
|
||||||
auto& dst_i = eax_;
|
auto& dst_i = mEax;
|
||||||
|
|
||||||
if(dst_i.lOcclusion != EAXFXSLOT_DEFAULTOCCLUSION) {
|
if(dst_i.lOcclusion != EAXFXSLOT_DEFAULTOCCLUSION)
|
||||||
dst_df |= eax_occlusion_dirty_bit;
|
{
|
||||||
|
dst_df.set(eax_occlusion_dirty_bit);
|
||||||
dst_i.lOcclusion = EAXFXSLOT_DEFAULTOCCLUSION;
|
dst_i.lOcclusion = EAXFXSLOT_DEFAULTOCCLUSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(dst_i.flOcclusionLFRatio != EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO) {
|
if(dst_i.flOcclusionLFRatio != EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO)
|
||||||
dst_df |= eax_occlusion_lf_ratio_dirty_bit;
|
{
|
||||||
|
dst_df.set(eax_occlusion_lf_ratio_dirty_bit);
|
||||||
dst_i.flOcclusionLFRatio = EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO;
|
dst_i.flOcclusionLFRatio = EAXFXSLOT_DEFAULTOCCLUSIONLFRATIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALeffectslot::eax5_fx_slot_commit(Eax5State& state, EaxDirtyFlags& dst_df)
|
void ALeffectslot::eax5_fx_slot_commit(Eax5State& state, std::bitset<eax_dirty_bit_count>& dst_df)
|
||||||
{
|
{
|
||||||
eax_fx_slot_commit_property<eax_load_effect_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::guidLoadEffect);
|
eax_fx_slot_commit_property<eax_load_effect_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::guidLoadEffect);
|
||||||
eax_fx_slot_commit_property<eax_volume_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::lVolume);
|
eax_fx_slot_commit_property<eax_volume_dirty_bit>(state, dst_df, &EAX50FXSLOTPROPERTIES::lVolume);
|
||||||
|
|
@ -1437,18 +1454,19 @@ void ALeffectslot::eax_set_efx_slot_effect(EaxEffect &effect)
|
||||||
#define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] "
|
#define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] "
|
||||||
|
|
||||||
const auto error = initEffect(0, effect.al_effect_type_, effect.al_effect_props_,
|
const auto error = initEffect(0, effect.al_effect_type_, effect.al_effect_props_,
|
||||||
eax_al_context_);
|
mEaxALContext);
|
||||||
|
if(error != AL_NO_ERROR)
|
||||||
if(error != AL_NO_ERROR) {
|
{
|
||||||
ERR(EAX_PREFIX "%s\n", "Failed to initialize an effect.");
|
ERR(EAX_PREFIX "Failed to initialize an effect.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mState == SlotState::Initial) {
|
if(mState == SlotState::Initial)
|
||||||
|
{
|
||||||
mPropsDirty = false;
|
mPropsDirty = false;
|
||||||
updateProps(eax_al_context_);
|
updateProps(mEaxALContext);
|
||||||
auto effect_slot_ptr = this;
|
auto effect_slot_ptr = this;
|
||||||
AddActiveEffectSlots({&effect_slot_ptr, 1}, eax_al_context_);
|
AddActiveEffectSlots({&effect_slot_ptr, 1}, mEaxALContext);
|
||||||
mState = SlotState::Playing;
|
mState = SlotState::Playing;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1474,7 +1492,7 @@ void ALeffectslot::eax_set_efx_slot_gain(ALfloat gain)
|
||||||
if(gain == Gain)
|
if(gain == Gain)
|
||||||
return;
|
return;
|
||||||
if(gain < 0.0f || gain > 1.0f)
|
if(gain < 0.0f || gain > 1.0f)
|
||||||
ERR(EAX_PREFIX "Gain out of range (%f)\n", gain);
|
ERR(EAX_PREFIX "Slot gain out of range ({:f})", gain);
|
||||||
|
|
||||||
Gain = std::clamp(gain, 0.0f, 1.0f);
|
Gain = std::clamp(gain, 0.0f, 1.0f);
|
||||||
mPropsDirty = true;
|
mPropsDirty = true;
|
||||||
|
|
@ -1484,7 +1502,7 @@ void ALeffectslot::eax_set_efx_slot_gain(ALfloat gain)
|
||||||
|
|
||||||
void ALeffectslot::EaxDeleter::operator()(ALeffectslot* effect_slot)
|
void ALeffectslot::EaxDeleter::operator()(ALeffectslot* effect_slot)
|
||||||
{
|
{
|
||||||
eax_delete_al_effect_slot(*effect_slot->eax_al_context_, *effect_slot);
|
eax_delete_al_effect_slot(*effect_slot->mEaxALContext, *effect_slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context)
|
EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context)
|
||||||
|
|
@ -1494,13 +1512,15 @@ EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context)
|
||||||
std::lock_guard<std::mutex> slotlock{context.mEffectSlotLock};
|
std::lock_guard<std::mutex> slotlock{context.mEffectSlotLock};
|
||||||
auto& device = *context.mALDevice;
|
auto& device = *context.mALDevice;
|
||||||
|
|
||||||
if(context.mNumEffectSlots == device.AuxiliaryEffectSlotMax) {
|
if(context.mNumEffectSlots == device.AuxiliaryEffectSlotMax)
|
||||||
ERR(EAX_PREFIX "%s\n", "Out of memory.");
|
{
|
||||||
|
ERR(EAX_PREFIX "Out of memory.");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!EnsureEffectSlots(&context, 1)) {
|
if(!EnsureEffectSlots(&context, 1))
|
||||||
ERR(EAX_PREFIX "%s\n", "Failed to ensure.");
|
{
|
||||||
|
ERR(EAX_PREFIX "Failed to ensure.");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1517,7 +1537,7 @@ void eax_delete_al_effect_slot(ALCcontext& context, ALeffectslot& effect_slot)
|
||||||
|
|
||||||
if(effect_slot.ref.load(std::memory_order_relaxed) != 0)
|
if(effect_slot.ref.load(std::memory_order_relaxed) != 0)
|
||||||
{
|
{
|
||||||
ERR(EAX_PREFIX "Deleting in-use effect slot %u.\n", effect_slot.id);
|
ERR(EAX_PREFIX "Deleting in-use effect slot {}.", effect_slot.id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -50,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;
|
||||||
};
|
};
|
||||||
|
|
@ -67,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();
|
||||||
|
|
@ -79,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]]
|
||||||
[[nodiscard]] auto eax_get_eax_fx_slot() const noexcept -> const EAX50FXSLOTPROPERTIES&
|
auto eax_get_index() const noexcept -> EaxFxSlotIndexValue { return mEaxFXSlotIndex; }
|
||||||
{ return eax_; }
|
[[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
|
||||||
|
|
@ -94,25 +98,24 @@ public:
|
||||||
void eax_commit();
|
void eax_commit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr auto eax_load_effect_dirty_bit = EaxDirtyFlags{1} << 0;
|
enum {
|
||||||
static constexpr auto eax_volume_dirty_bit = EaxDirtyFlags{1} << 1;
|
eax_load_effect_dirty_bit,
|
||||||
static constexpr auto eax_lock_dirty_bit = EaxDirtyFlags{1} << 2;
|
eax_volume_dirty_bit,
|
||||||
static constexpr auto eax_flags_dirty_bit = EaxDirtyFlags{1} << 3;
|
eax_lock_dirty_bit,
|
||||||
static constexpr auto eax_occlusion_dirty_bit = EaxDirtyFlags{1} << 4;
|
eax_flags_dirty_bit,
|
||||||
static constexpr auto eax_occlusion_lf_ratio_dirty_bit = EaxDirtyFlags{1} << 5;
|
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 {
|
||||||
|
|
@ -237,15 +240,15 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ALCcontext* eax_al_context_{};
|
ALCcontext* mEaxALContext{};
|
||||||
EaxFxSlotIndexValue eax_fx_slot_index_{};
|
EaxFxSlotIndexValue mEaxFXSlotIndex{};
|
||||||
int eax_version_{}; // Current EAX version.
|
int mEaxVersion{}; // Current EAX version.
|
||||||
EaxDirtyFlags eax_df_{}; // Dirty flags for the current EAX version.
|
std::bitset<eax_dirty_bit_count> mEaxDf; // Dirty flags for the current EAX version.
|
||||||
EaxEffectUPtr eax_effect_{};
|
EaxEffectUPtr mEaxEffect;
|
||||||
Eax5State eax123_{}; // EAX1/EAX2/EAX3 state.
|
Eax5State mEax123{}; // EAX1/EAX2/EAX3 state.
|
||||||
Eax4State eax4_{}; // EAX4 state.
|
Eax4State mEax4{}; // EAX4 state.
|
||||||
Eax5State eax5_{}; // EAX5 state.
|
Eax5State mEax5{}; // EAX5 state.
|
||||||
Eax5Props eax_{}; // Current EAX 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();
|
||||||
|
|
@ -256,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>
|
template<typename TValidator, size_t DirtyBit, typename TProperties>
|
||||||
static void eax_fx_slot_set(const EaxCall& call, TProperties& dst, EaxDirtyFlags& dirty_flags)
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -269,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;
|
||||||
|
|
||||||
|
|
@ -288,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 eax4_fx_slot_set_defaults(EAX40FXSLOTPROPERTIES& props) noexcept;
|
||||||
void eax5_fx_slot_set_defaults(Eax5Props& props) noexcept;
|
void eax5_fx_slot_set_defaults(EAX50FXSLOTPROPERTIES& props) noexcept;
|
||||||
void eax4_fx_slot_set_current_defaults(const Eax4Props& props) noexcept;
|
void eax4_fx_slot_set_current_defaults(const EAX40FXSLOTPROPERTIES& props) noexcept;
|
||||||
void eax5_fx_slot_set_current_defaults(const Eax5Props& 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 eax4_fx_slot_get(const EaxCall& call, const EAX40FXSLOTPROPERTIES& props);
|
||||||
static void eax5_fx_slot_get(const EaxCall& call, const Eax5Props& 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);
|
||||||
|
|
@ -321,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 eax4_fx_slot_commit(std::bitset<eax_dirty_bit_count>& dst_df);
|
||||||
void eax5_fx_slot_commit(Eax5State& state, EaxDirtyFlags& 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);
|
||||||
|
|
@ -360,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);
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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::API: return "API"sv;
|
||||||
case DebugSource::System: return "Audio System";
|
case DebugSource::System: return "Audio System"sv;
|
||||||
case DebugSource::ThirdParty: return "Third Party";
|
case DebugSource::ThirdParty: return "Third Party"sv;
|
||||||
case DebugSource::Application: return "Application";
|
case DebugSource::Application: return "Application"sv;
|
||||||
case DebugSource::Other: return "Other";
|
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::Error: return "Error"sv;
|
||||||
case DebugType::DeprecatedBehavior: return "Deprecated Behavior";
|
case DebugType::DeprecatedBehavior: return "Deprecated Behavior"sv;
|
||||||
case DebugType::UndefinedBehavior: return "Undefined Behavior";
|
case DebugType::UndefinedBehavior: return "Undefined Behavior"sv;
|
||||||
case DebugType::Portability: return "Portability";
|
case DebugType::Portability: return "Portability"sv;
|
||||||
case DebugType::Performance: return "Performance";
|
case DebugType::Performance: return "Performance"sv;
|
||||||
case DebugType::Marker: return "Marker";
|
case DebugType::Marker: return "Marker"sv;
|
||||||
case DebugType::PushGroup: return "Push Group";
|
case DebugType::PushGroup: return "Push Group"sv;
|
||||||
case DebugType::PopGroup: return "Pop Group";
|
case DebugType::PopGroup: return "Pop Group"sv;
|
||||||
case DebugType::Other: return "Other";
|
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::High: return "High"sv;
|
||||||
case DebugSeverity::Medium: return "Medium";
|
case DebugSeverity::Medium: return "Medium"sv;
|
||||||
case DebugSeverity::Low: return "Low";
|
case DebugSeverity::Low: return "Low"sv;
|
||||||
case DebugSeverity::Notification: return "Notification";
|
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(),
|
ERR("Debug message too long ({} >= {}):\n-> {}", message.length(),
|
||||||
MaxDebugMessageLength, al::sizei(message), message.data());
|
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"
|
" Source: {}\n"
|
||||||
" Type: %s\n"
|
" Type: {}\n"
|
||||||
" ID: %u\n"
|
" ID: {}\n"
|
||||||
" Severity: %s\n"
|
" Severity: {}\n"
|
||||||
" Message: \"%.*s\"\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)",
|
context->throw_error(AL_INVALID_VALUE, "Debug message too long ({} >= {})", msgview.size(),
|
||||||
msgview.size(), MaxDebugMessageLength};
|
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) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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,
|
context->throw_error(AL_INVALID_OPERATION,
|
||||||
"Debug source cannot be AL_DONT_CARE_EXT with IDs"};
|
"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,
|
context->throw_error(AL_INVALID_OPERATION,
|
||||||
"Debug type cannot be AL_DONT_CARE_EXT with IDs"};
|
"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,
|
context->throw_error(AL_INVALID_OPERATION,
|
||||||
"Debug severity must be AL_DONT_CARE_EXT with IDs"};
|
"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) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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,
|
context->throw_error(AL_INVALID_VALUE, "Debug message too long ({} >= {})", newlen,
|
||||||
MaxDebugMessageLength};
|
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,
|
context->throw_error(AL_INVALID_VALUE, "Debug message too long ({} >= {})", length,
|
||||||
MaxDebugMessageLength};
|
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) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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,
|
context->throw_error(AL_STACK_UNDERFLOW_EXT, "Attempting to pop the default debug group");
|
||||||
"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) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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)
|
if(logBuf && logBufSize < 0)
|
||||||
throw al::context_error{AL_INVALID_VALUE, "Negative debug log buffer size"};
|
context->throw_error(AL_INVALID_VALUE, "Negative debug log buffer size");
|
||||||
|
|
||||||
auto sourcesOut = al::span{sources, sources ? count : 0u};
|
const auto sourcesSpan = al::span{sources, sources ? count : 0u};
|
||||||
auto typesOut = al::span{types, types ? count : 0u};
|
const auto typesSpan = al::span{types, types ? count : 0u};
|
||||||
auto idsOut = al::span{ids, ids ? count : 0u};
|
const auto idsSpan = al::span{ids, ids ? count : 0u};
|
||||||
auto severitiesOut = al::span{severities, severities ? count : 0u};
|
const auto severitiesSpan = al::span{severities, severities ? count : 0u};
|
||||||
auto lengthsOut = al::span{lengths, lengths ? count : 0u};
|
const auto lengthsSpan = al::span{lengths, lengths ? count : 0u};
|
||||||
auto logOut = al::span{logBuf, logBuf ? static_cast<ALuint>(logBufSize) : 0u};
|
const auto logSpan = al::span{logBuf, logBuf ? static_cast<ALuint>(logBufSize) : 0u};
|
||||||
|
|
||||||
std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
|
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};
|
const auto tocopy = size_t{entry.mMessage.size() + 1};
|
||||||
if(logOut.data() != nullptr)
|
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());
|
logiter = std::copy(entry.mMessage.cbegin(), entry.mMessage.cend(), logiter);
|
||||||
*oiter = '\0';
|
*(logiter++) = '\0';
|
||||||
logOut = {oiter+1, logOut.end()};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!sourcesOut.empty())
|
if(al::to_address(sourceiter) != nullptr)
|
||||||
{
|
*(sourceiter++) = GetDebugSourceEnum(entry.mSource);
|
||||||
sourcesOut.front() = GetDebugSourceEnum(entry.mSource);
|
if(al::to_address(typeiter) != nullptr)
|
||||||
sourcesOut = sourcesOut.subspan<1>();
|
*(typeiter++) = GetDebugTypeEnum(entry.mType);
|
||||||
}
|
if(al::to_address(iditer) != nullptr)
|
||||||
if(!typesOut.empty())
|
*(iditer++) = entry.mId;
|
||||||
{
|
if(al::to_address(severityiter) != nullptr)
|
||||||
typesOut.front() = GetDebugTypeEnum(entry.mType);
|
*(severityiter++) = GetDebugSeverityEnum(entry.mSeverity);
|
||||||
typesOut = typesOut.subspan<1>();
|
if(al::to_address(lengthiter) != nullptr)
|
||||||
}
|
*(lengthiter++) = static_cast<ALsizei>(tocopy);
|
||||||
if(!idsOut.empty())
|
|
||||||
{
|
|
||||||
idsOut.front() = entry.mId;
|
|
||||||
idsOut = idsOut.subspan<1>();
|
|
||||||
}
|
|
||||||
if(!severitiesOut.empty())
|
|
||||||
{
|
|
||||||
severitiesOut.front() = GetDebugSeverityEnum(entry.mSeverity);
|
|
||||||
severitiesOut = severitiesOut.subspan<1>();
|
|
||||||
}
|
|
||||||
if(!lengthsOut.empty())
|
|
||||||
{
|
|
||||||
lengthsOut.front() = static_cast<ALsizei>(tocopy);
|
|
||||||
lengthsOut = lengthsOut.subspan<1>();
|
|
||||||
}
|
|
||||||
|
|
||||||
context->mDebugLog.pop_front();
|
context->mDebugLog.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
return 0;
|
||||||
|
}
|
||||||
|
catch(std::exception &e) {
|
||||||
|
ERR("Caught exception: {}", e.what());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -526,13 +536,13 @@ 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)",
|
context->throw_error(AL_INVALID_VALUE, "Object label length too long ({} >= {})",
|
||||||
objname.size(), MaxObjectLabelLength};
|
objname.size(), MaxObjectLabelLength);
|
||||||
|
|
||||||
switch(identifier)
|
switch(identifier)
|
||||||
{
|
{
|
||||||
|
|
@ -543,10 +553,13 @@ try {
|
||||||
case AL_AUXILIARY_EFFECT_SLOT_EXT: ALeffectslot::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::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
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)
|
||||||
|
|
@ -554,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)
|
||||||
|
|
@ -589,20 +602,20 @@ try {
|
||||||
}
|
}
|
||||||
else if(identifier == AL_BUFFER)
|
else if(identifier == AL_BUFFER)
|
||||||
{
|
{
|
||||||
ALCdevice *device{context->mALDevice.get()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard buflock{device->BufferLock};
|
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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard filterlock{device->FilterLock};
|
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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard effectlock{device->EffectLock};
|
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)
|
||||||
|
|
@ -611,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::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
catch(std::exception &e) {
|
||||||
|
ERR("Caught exception: {}", e.what());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(); } \
|
{ return lhs.get_members() == rhs.get_members(); } \
|
||||||
[[nodiscard]] friend bool operator!=(const T &lhs, const T &rhs) noexcept \
|
[[nodiscard]] friend bool operator!=(const T &lhs, const T &rhs) noexcept { return !(lhs == rhs); }
|
||||||
{ 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;
|
DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX40_FXSlot0;
|
||||||
extern const GUID EAXPROPERTYID_EAX50_FXSlot0;
|
DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX50_FXSlot0;
|
||||||
extern const GUID EAXPROPERTYID_EAX40_FXSlot1;
|
DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX40_FXSlot1;
|
||||||
extern const GUID EAXPROPERTYID_EAX50_FXSlot1;
|
DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX50_FXSlot1;
|
||||||
extern const GUID EAXPROPERTYID_EAX40_FXSlot2;
|
DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX40_FXSlot2;
|
||||||
extern const GUID EAXPROPERTYID_EAX50_FXSlot2;
|
DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX50_FXSlot2;
|
||||||
extern const GUID EAXPROPERTYID_EAX40_FXSlot3;
|
DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX40_FXSlot3;
|
||||||
extern const GUID EAXPROPERTYID_EAX50_FXSlot3;
|
DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX50_FXSlot3;
|
||||||
|
|
||||||
extern const GUID EAX40CONTEXT_DEFAULTPRIMARYFXSLOTID;
|
DECL_HIDDEN extern const GUID EAX40CONTEXT_DEFAULTPRIMARYFXSLOTID;
|
||||||
extern const GUID EAX50CONTEXT_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;
|
DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX40_Source;
|
||||||
extern const GUID EAXPROPERTYID_EAX50_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,
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,8 @@ public:
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
EaxCall::EaxCall(
|
EaxCall::EaxCall(EaxCallType type, const GUID &property_set_guid, ALuint property_id,
|
||||||
EaxCallType type,
|
ALuint property_source_id, ALvoid *property_buffer, ALuint property_size)
|
||||||
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)
|
if(mPropertySetId == EaxCallPropertySetId::context)
|
||||||
{
|
{
|
||||||
case EAXCONTEXT_LASTERROR:
|
switch(mPropertyId)
|
||||||
case EAXCONTEXT_SPEAKERCONFIG:
|
{
|
||||||
case EAXCONTEXT_EAXSESSION:
|
case EAXCONTEXT_LASTERROR:
|
||||||
case EAXFXSLOT_NONE:
|
case EAXCONTEXT_SPEAKERCONFIG:
|
||||||
case EAXFXSLOT_ALLPARAMETERS:
|
case EAXCONTEXT_EAXSESSION:
|
||||||
case EAXFXSLOT_LOADEFFECT:
|
// EAX allow to set "defer" flag on immediate-only properties.
|
||||||
case EAXFXSLOT_VOLUME:
|
// If we don't clear our flag then "applyAllUpdates" in EAX context won't be called.
|
||||||
case EAXFXSLOT_LOCK:
|
mIsDeferred = false;
|
||||||
case EAXFXSLOT_FLAGS:
|
break;
|
||||||
case EAXFXSLOT_OCCLUSION:
|
}
|
||||||
case EAXFXSLOT_OCCLUSIONLFRATIO:
|
}
|
||||||
// EAX allow to set "defer" flag on immediate-only properties.
|
else if(mPropertySetId == EaxCallPropertySetId::fx_slot)
|
||||||
// If we don't clear our flag then "applyAllUpdates" in EAX context won't be called.
|
{
|
||||||
mIsDeferred = false;
|
switch(mPropertyId)
|
||||||
break;
|
{
|
||||||
|
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)
|
||||||
|
|
|
||||||
|
|
@ -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_{};
|
||||||
|
|
|
||||||
|
|
@ -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.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
const auto message = fmt::format("{} out of range (value: {}; min: {}; max: {}).", value_name,
|
||||||
std::string{value_name} +
|
value, min_value, max_value);
|
||||||
" out of range (value: " +
|
|
||||||
std::to_string(value) + "; min: " +
|
|
||||||
std::to_string(min_value) + "; max: " +
|
|
||||||
std::to_string(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
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
||||||
|
|
@ -139,7 +139,8 @@ void InitEffectParams(ALeffect *effect, ALenum type) noexcept
|
||||||
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
|
||||||
|
|
@ -162,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
|
||||||
|
|
@ -182,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);
|
||||||
|
|
||||||
|
|
@ -195,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};
|
||||||
|
|
@ -214,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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
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,
|
context->throw_error(AL_OUT_OF_MEMORY, "Failed to allocate {} effect{}", n,
|
||||||
(n == 1) ? "" : "s"};
|
(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) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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)
|
||||||
|
|
@ -236,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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
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
|
||||||
|
|
@ -249,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
|
||||||
|
|
@ -259,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) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
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;
|
||||||
|
|
@ -277,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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
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)
|
||||||
{
|
{
|
||||||
|
|
@ -292,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",
|
context->throw_error(AL_INVALID_VALUE, "Effect type {:#04x} not supported",
|
||||||
value};
|
as_unsigned(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
InitEffectParams(aleffect, value);
|
InitEffectParams(aleffect, value);
|
||||||
|
|
@ -301,15 +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)>>;
|
||||||
using PropType = typename Type::prop_type;
|
using PropType = typename Type::prop_type;
|
||||||
return arg.SetParami(std::get<PropType>(aleffect->Props), param, value);
|
return arg.SetParami(context, std::get<PropType>(aleffect->Props), param, value);
|
||||||
}, aleffect->PropsVariant);
|
}, aleffect->PropsVariant);
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
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)
|
||||||
|
|
@ -323,81 +332,87 @@ try {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ALCdevice *device{context->mALDevice.get()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
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)>>;
|
||||||
using PropType = typename Type::prop_type;
|
using PropType = typename Type::prop_type;
|
||||||
return arg.SetParamiv(std::get<PropType>(aleffect->Props), param, values);
|
return arg.SetParamiv(context, std::get<PropType>(aleffect->Props), param, values);
|
||||||
}, aleffect->PropsVariant);
|
}, aleffect->PropsVariant);
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
auto effectlock = std::lock_guard{device->EffectLock};
|
||||||
|
|
||||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||||
if(!aleffect) UNLIKELY
|
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)>>;
|
||||||
using PropType = typename Type::prop_type;
|
using PropType = typename Type::prop_type;
|
||||||
return arg.SetParamf(std::get<PropType>(aleffect->Props), param, value);
|
return arg.SetParamf(context, std::get<PropType>(aleffect->Props), param, value);
|
||||||
}, aleffect->PropsVariant);
|
}, aleffect->PropsVariant);
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
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)>>;
|
||||||
using PropType = typename Type::prop_type;
|
using PropType = typename Type::prop_type;
|
||||||
return arg.SetParamfv(std::get<PropType>(aleffect->Props), param, values);
|
return arg.SetParamfv(context, std::get<PropType>(aleffect->Props), param, values);
|
||||||
}, aleffect->PropsVariant);
|
}, aleffect->PropsVariant);
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
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)
|
||||||
{
|
{
|
||||||
|
|
@ -407,15 +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)>>;
|
||||||
using PropType = typename Type::prop_type;
|
using PropType = typename Type::prop_type;
|
||||||
return arg.GetParami(std::get<PropType>(aleffect->Props), param, value);
|
return arg.GetParami(context, std::get<PropType>(aleffect->Props), param, value);
|
||||||
}, aleffect->PropsVariant);
|
}, aleffect->PropsVariant);
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
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)
|
||||||
|
|
@ -429,69 +446,75 @@ try {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ALCdevice *device{context->mALDevice.get()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
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)>>;
|
||||||
using PropType = typename Type::prop_type;
|
using PropType = typename Type::prop_type;
|
||||||
return arg.GetParamiv(std::get<PropType>(aleffect->Props), param, values);
|
return arg.GetParamiv(context, std::get<PropType>(aleffect->Props), param, values);
|
||||||
}, aleffect->PropsVariant);
|
}, aleffect->PropsVariant);
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
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)>>;
|
||||||
using PropType = typename Type::prop_type;
|
using PropType = typename Type::prop_type;
|
||||||
return arg.GetParamf(std::get<PropType>(aleffect->Props), param, value);
|
return arg.GetParamf(context, std::get<PropType>(aleffect->Props), param, value);
|
||||||
}, aleffect->PropsVariant);
|
}, aleffect->PropsVariant);
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
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)>>;
|
||||||
using PropType = typename Type::prop_type;
|
using PropType = typename Type::prop_type;
|
||||||
return arg.GetParamfv(std::get<PropType>(aleffect->Props), param, values);
|
return arg.GetParamfv(context, std::get<PropType>(aleffect->Props), param, values);
|
||||||
}, aleffect->PropsVariant);
|
}, aleffect->PropsVariant);
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
catch(std::exception &e) {
|
||||||
|
ERR("Caught exception: {}", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -502,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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
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);
|
||||||
}
|
}
|
||||||
|
|
@ -673,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -688,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;
|
||||||
|
|
@ -721,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
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
@ -43,7 +44,7 @@ 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,
|
using EffectHandlerVariant = std::variant<NullEffectHandler,ReverbEffectHandler,
|
||||||
StdReverbEffectHandler,AutowahEffectHandler,ChorusEffectHandler,CompressorEffectHandler,
|
StdReverbEffectHandler,AutowahEffectHandler,ChorusEffectHandler,CompressorEffectHandler,
|
||||||
|
|
@ -56,7 +57,7 @@ struct ALeffect {
|
||||||
ALenum type{AL_EFFECT_NULL};
|
ALenum type{AL_EFFECT_NULL};
|
||||||
|
|
||||||
EffectHandlerVariant PropsVariant;
|
EffectHandlerVariant PropsVariant;
|
||||||
EffectProps Props{};
|
EffectProps Props;
|
||||||
|
|
||||||
/* Self ID */
|
/* Self ID */
|
||||||
ALuint id{0u};
|
ALuint id{0u};
|
||||||
|
|
|
||||||
|
|
@ -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
|
#if ALSOFT_EAX
|
||||||
#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"
|
||||||
|
|
@ -35,75 +33,68 @@ constexpr EffectProps genDefaultProps() noexcept
|
||||||
|
|
||||||
const EffectProps AutowahEffectProps{genDefaultProps()};
|
const EffectProps AutowahEffectProps{genDefaultProps()};
|
||||||
|
|
||||||
void AutowahEffectHandler::SetParami(AutowahProps&, ALenum param, int)
|
void AutowahEffectHandler::SetParami(ALCcontext *context, AutowahProps&, ALenum param, int)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
|
{ context->throw_error(AL_INVALID_ENUM, "Invalid autowah integer property {:#04x}", as_unsigned(param)); }
|
||||||
void AutowahEffectHandler::SetParamiv(AutowahProps&, ALenum param, const int*)
|
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)); }
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
|
|
||||||
void AutowahEffectHandler::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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void AutowahEffectHandler::SetParamfv(AutowahProps &props, ALenum param, const float *vals)
|
|
||||||
{ SetParamf(props, param, *vals); }
|
|
||||||
|
|
||||||
void AutowahEffectHandler::GetParami(const AutowahProps&, ALenum param, int*)
|
context->throw_error(AL_INVALID_ENUM, "Invalid autowah float property {:#04x}",
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
|
as_unsigned(param));
|
||||||
void AutowahEffectHandler::GetParamiv(const AutowahProps&, ALenum param, int*)
|
|
||||||
{
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
|
void AutowahEffectHandler::SetParamfv(ALCcontext *context, AutowahProps &props, ALenum param, const float *vals)
|
||||||
|
{ SetParamf(context, props, param, *vals); }
|
||||||
|
|
||||||
void AutowahEffectHandler::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_ATTACK_TIME: *val = props.AttackTime; return;
|
||||||
case AL_AUTOWAH_RELEASE_TIME: *val = props.ReleaseTime; break;
|
case AL_AUTOWAH_RELEASE_TIME: *val = props.ReleaseTime; return;
|
||||||
case AL_AUTOWAH_RESONANCE: *val = props.Resonance; break;
|
case AL_AUTOWAH_RESONANCE: *val = props.Resonance; return;
|
||||||
case AL_AUTOWAH_PEAK_GAIN: *val = props.PeakGain; break;
|
case AL_AUTOWAH_PEAK_GAIN: *val = props.PeakGain; return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid autowah float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void AutowahEffectHandler::GetParamfv(const AutowahProps &props, ALenum param, float *vals)
|
void AutowahEffectHandler::GetParamfv(ALCcontext *context, const AutowahProps &props, ALenum param, float *vals)
|
||||||
{ GetParamf(props, param, vals); }
|
{ GetParamf(context, props, param, vals); }
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using AutowahCommitter = EaxCommitter<EaxAutowahCommitter>;
|
using AutowahCommitter = EaxCommitter<EaxAutowahCommitter>;
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,12 @@
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
|
#include "alc/context.h"
|
||||||
|
#include "alnumeric.h"
|
||||||
|
#include "core/logging.h"
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include "al/eax/effect.h"
|
#include "al/eax/effect.h"
|
||||||
#include "al/eax/exception.h"
|
#include "al/eax/exception.h"
|
||||||
|
|
@ -41,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
|
||||||
|
|
@ -72,7 +76,7 @@ constexpr EffectProps genDefaultFlangerProps() noexcept
|
||||||
|
|
||||||
const EffectProps ChorusEffectProps{genDefaultChorusProps()};
|
const EffectProps ChorusEffectProps{genDefaultChorusProps()};
|
||||||
|
|
||||||
void ChorusEffectHandler::SetParami(ChorusProps &props, ALenum param, int val)
|
void ChorusEffectHandler::SetParami(ALCcontext *context, ChorusProps &props, ALenum param, int val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
|
|
@ -80,89 +84,90 @@ void ChorusEffectHandler::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};
|
context->throw_error(AL_INVALID_VALUE, "Invalid chorus waveform: {:#04x}",
|
||||||
break;
|
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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid chorus integer property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void ChorusEffectHandler::SetParamiv(ChorusProps &props, ALenum param, const int *vals)
|
void ChorusEffectHandler::SetParamiv(ALCcontext *context, ChorusProps &props, ALenum param, const int *vals)
|
||||||
{ SetParami(props, param, *vals); }
|
{ SetParami(context, props, param, *vals); }
|
||||||
void ChorusEffectHandler::SetParamf(ChorusProps &props, ALenum param, float val)
|
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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void ChorusEffectHandler::SetParamfv(ChorusProps &props, ALenum param, const float *vals)
|
|
||||||
{ SetParamf(props, param, *vals); }
|
|
||||||
|
|
||||||
void ChorusEffectHandler::GetParami(const ChorusProps &props, ALenum param, int *val)
|
context->throw_error(AL_INVALID_ENUM, "Invalid chorus float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
|
}
|
||||||
|
void ChorusEffectHandler::SetParamfv(ALCcontext *context, ChorusProps &props, ALenum param, const float *vals)
|
||||||
|
{ SetParamf(context, props, param, *vals); }
|
||||||
|
|
||||||
|
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_WAVEFORM: *val = EnumFromWaveform(props.Waveform); return;
|
||||||
case AL_CHORUS_PHASE: *val = props.Phase; break;
|
case AL_CHORUS_PHASE: *val = props.Phase; return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid chorus integer property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void ChorusEffectHandler::GetParamiv(const ChorusProps &props, ALenum param, int *vals)
|
void ChorusEffectHandler::GetParamiv(ALCcontext *context, const ChorusProps &props, ALenum param, int *vals)
|
||||||
{ GetParami(props, param, vals); }
|
{ GetParami(context, props, param, vals); }
|
||||||
void ChorusEffectHandler::GetParamf(const ChorusProps &props, ALenum param, float *val)
|
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_RATE: *val = props.Rate; return;
|
||||||
case AL_CHORUS_DEPTH: *val = props.Depth; break;
|
case AL_CHORUS_DEPTH: *val = props.Depth; return;
|
||||||
case AL_CHORUS_FEEDBACK: *val = props.Feedback; break;
|
case AL_CHORUS_FEEDBACK: *val = props.Feedback; return;
|
||||||
case AL_CHORUS_DELAY: *val = props.Delay; break;
|
case AL_CHORUS_DELAY: *val = props.Delay; return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid chorus float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void ChorusEffectHandler::GetParamfv(const ChorusProps &props, ALenum param, float *vals)
|
void ChorusEffectHandler::GetParamfv(ALCcontext *context, const ChorusProps &props, ALenum param, float *vals)
|
||||||
{ GetParamf(props, param, vals); }
|
{ GetParamf(context, props, param, vals); }
|
||||||
|
|
||||||
|
|
||||||
const EffectProps FlangerEffectProps{genDefaultFlangerProps()};
|
const EffectProps FlangerEffectProps{genDefaultFlangerProps()};
|
||||||
|
|
||||||
void FlangerEffectHandler::SetParami(ChorusProps &props, ALenum param, int val)
|
void FlangerEffectHandler::SetParami(ALCcontext *context, ChorusProps &props, ALenum param, int val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
|
|
@ -170,87 +175,88 @@ void FlangerEffectHandler::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 flanger waveform: 0x%04x", val};
|
context->throw_error(AL_INVALID_VALUE, "Invalid flanger waveform: {:#04x}",
|
||||||
break;
|
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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid flanger integer property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void FlangerEffectHandler::SetParamiv(ChorusProps &props, ALenum param, const int *vals)
|
void FlangerEffectHandler::SetParamiv(ALCcontext *context, ChorusProps &props, ALenum param, const int *vals)
|
||||||
{ SetParami(props, param, *vals); }
|
{ SetParami(context, props, param, *vals); }
|
||||||
void FlangerEffectHandler::SetParamf(ChorusProps &props, ALenum param, float val)
|
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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void FlangerEffectHandler::SetParamfv(ChorusProps &props, ALenum param, const float *vals)
|
|
||||||
{ SetParamf(props, param, *vals); }
|
|
||||||
|
|
||||||
void FlangerEffectHandler::GetParami(const ChorusProps &props, ALenum param, int *val)
|
context->throw_error(AL_INVALID_ENUM, "Invalid flanger float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
|
}
|
||||||
|
void FlangerEffectHandler::SetParamfv(ALCcontext *context, ChorusProps &props, ALenum param, const float *vals)
|
||||||
|
{ SetParamf(context, props, param, *vals); }
|
||||||
|
|
||||||
|
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_WAVEFORM: *val = EnumFromWaveform(props.Waveform); return;
|
||||||
case AL_FLANGER_PHASE: *val = props.Phase; break;
|
case AL_FLANGER_PHASE: *val = props.Phase; return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid flanger integer property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void FlangerEffectHandler::GetParamiv(const ChorusProps &props, ALenum param, int *vals)
|
void FlangerEffectHandler::GetParamiv(ALCcontext *context, const ChorusProps &props, ALenum param, int *vals)
|
||||||
{ GetParami(props, param, vals); }
|
{ GetParami(context, props, param, vals); }
|
||||||
void FlangerEffectHandler::GetParamf(const ChorusProps &props, ALenum param, float *val)
|
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_RATE: *val = props.Rate; return;
|
||||||
case AL_FLANGER_DEPTH: *val = props.Depth; break;
|
case AL_FLANGER_DEPTH: *val = props.Depth; return;
|
||||||
case AL_FLANGER_FEEDBACK: *val = props.Feedback; break;
|
case AL_FLANGER_FEEDBACK: *val = props.Feedback; return;
|
||||||
case AL_FLANGER_DELAY: *val = props.Delay; break;
|
case AL_FLANGER_DELAY: *val = props.Delay; return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid flanger float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void FlangerEffectHandler::GetParamfv(const ChorusProps &props, ALenum param, float *vals)
|
void FlangerEffectHandler::GetParamfv(ALCcontext *context, const ChorusProps &props, ALenum param, float *vals)
|
||||||
{ GetParamf(props, param, vals); }
|
{ GetParamf(context, props, param, vals); }
|
||||||
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct EaxChorusTraits {
|
struct EaxChorusTraits {
|
||||||
|
|
@ -558,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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
#if ALSOFT_EAX
|
||||||
#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"
|
||||||
|
|
@ -28,53 +28,46 @@ constexpr EffectProps genDefaultProps() noexcept
|
||||||
|
|
||||||
const EffectProps CompressorEffectProps{genDefaultProps()};
|
const EffectProps CompressorEffectProps{genDefaultProps()};
|
||||||
|
|
||||||
void CompressorEffectHandler::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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void CompressorEffectHandler::SetParamiv(CompressorProps &props, ALenum param, const int *vals)
|
|
||||||
{ SetParami(props, param, *vals); }
|
|
||||||
void CompressorEffectHandler::SetParamf(CompressorProps&, ALenum param, float)
|
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
|
|
||||||
void CompressorEffectHandler::SetParamfv(CompressorProps&, ALenum param, const float*)
|
|
||||||
{
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompressorEffectHandler::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;
|
case AL_COMPRESSOR_ONOFF: *val = props.OnOff; return;
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid compressor integer property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void CompressorEffectHandler::GetParamiv(const CompressorProps &props, ALenum param, int *vals)
|
void CompressorEffectHandler::GetParamiv(ALCcontext *context, const CompressorProps &props, ALenum param, int *vals)
|
||||||
{ GetParami(props, param, vals); }
|
{ GetParami(context, props, param, vals); }
|
||||||
void CompressorEffectHandler::GetParamf(const CompressorProps&, ALenum param, float*)
|
void CompressorEffectHandler::GetParamf(ALCcontext *context, const CompressorProps&, ALenum param, float*)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
|
{ context->throw_error(AL_INVALID_ENUM, "Invalid compressor float property {:#04x}", as_unsigned(param)); }
|
||||||
void CompressorEffectHandler::GetParamfv(const CompressorProps&, ALenum param, float*)
|
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)); }
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using CompressorCommitter = EaxCommitter<EaxCompressorCommitter>;
|
using CompressorCommitter = EaxCommitter<EaxCompressorCommitter>;
|
||||||
|
|
|
||||||
|
|
@ -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 ConvolutionEffectHandler::SetParami(ConvolutionProps& /*props*/, ALenum param, int /*val*/)
|
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)); }
|
||||||
switch(param)
|
void ConvolutionEffectHandler::SetParamiv(ALCcontext *context, ConvolutionProps &props, ALenum param, const int *vals)
|
||||||
{
|
{ SetParami(context, props, param, *vals); }
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect integer property 0x%04x",
|
void ConvolutionEffectHandler::SetParamf(ALCcontext *context, ConvolutionProps& /*props*/, ALenum param, float /*val*/)
|
||||||
param};
|
{ 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)
|
||||||
}
|
|
||||||
void ConvolutionEffectHandler::SetParamiv(ConvolutionProps &props, ALenum param, const int *vals)
|
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
SetParami(props, param, *vals);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void ConvolutionEffectHandler::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 ConvolutionEffectHandler::SetParamfv(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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
SetParamf(props, param, *values);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetParamf(context, props, param, *values);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConvolutionEffectHandler::GetParami(const ConvolutionProps& /*props*/, ALenum param, int* /*val*/)
|
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)
|
||||||
{
|
{
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void ConvolutionEffectHandler::GetParamiv(const ConvolutionProps &props, ALenum param, int *vals)
|
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
GetParami(props, param, vals);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void ConvolutionEffectHandler::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 ConvolutionEffectHandler::GetParamfv(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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
GetParamf(props, param, values);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GetParamf(context, props, param, values);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -32,91 +33,81 @@ constexpr EffectProps genDefaultLfeProps() noexcept
|
||||||
|
|
||||||
const EffectProps DedicatedDialogEffectProps{genDefaultDialogProps()};
|
const EffectProps DedicatedDialogEffectProps{genDefaultDialogProps()};
|
||||||
|
|
||||||
void DedicatedDialogEffectHandler::SetParami(DedicatedProps&, ALenum param, int)
|
void DedicatedDialogEffectHandler::SetParami(ALCcontext *context, DedicatedProps&, ALenum param, int)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
{ context->throw_error(AL_INVALID_ENUM, "Invalid dedicated integer property {:#04x}", as_unsigned(param)); }
|
||||||
void DedicatedDialogEffectHandler::SetParamiv(DedicatedProps&, ALenum param, const int*)
|
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)); }
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
void DedicatedDialogEffectHandler::SetParamf(ALCcontext *context, DedicatedProps &props, ALenum param, float val)
|
||||||
param};
|
|
||||||
}
|
|
||||||
void DedicatedDialogEffectHandler::SetParamf(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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void DedicatedDialogEffectHandler::SetParamfv(DedicatedProps &props, ALenum param, const float *vals)
|
|
||||||
{ SetParamf(props, param, *vals); }
|
|
||||||
|
|
||||||
void DedicatedDialogEffectHandler::GetParami(const DedicatedProps&, ALenum param, int*)
|
context->throw_error(AL_INVALID_ENUM, "Invalid dedicated float property {:#04x}",
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
as_unsigned(param));
|
||||||
void DedicatedDialogEffectHandler::GetParamiv(const DedicatedProps&, ALenum param, int*)
|
|
||||||
{
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
void DedicatedDialogEffectHandler::GetParamf(const DedicatedProps &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;
|
case AL_DEDICATED_GAIN: *val = props.Gain; return;
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid dedicated float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void DedicatedDialogEffectHandler::GetParamfv(const DedicatedProps &props, ALenum param, float *vals)
|
void DedicatedDialogEffectHandler::GetParamfv(ALCcontext *context, const DedicatedProps &props, ALenum param, float *vals)
|
||||||
{ GetParamf(props, param, vals); }
|
{ GetParamf(context, props, param, vals); }
|
||||||
|
|
||||||
|
|
||||||
const EffectProps DedicatedLfeEffectProps{genDefaultLfeProps()};
|
const EffectProps DedicatedLfeEffectProps{genDefaultLfeProps()};
|
||||||
|
|
||||||
void DedicatedLfeEffectHandler::SetParami(DedicatedProps&, ALenum param, int)
|
void DedicatedLfeEffectHandler::SetParami(ALCcontext *context, DedicatedProps&, ALenum param, int)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
{ context->throw_error(AL_INVALID_ENUM, "Invalid dedicated integer property {:#04x}", as_unsigned(param)); }
|
||||||
void DedicatedLfeEffectHandler::SetParamiv(DedicatedProps&, ALenum param, const int*)
|
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)); }
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
void DedicatedLfeEffectHandler::SetParamf(ALCcontext *context, DedicatedProps &props, ALenum param, float val)
|
||||||
param};
|
|
||||||
}
|
|
||||||
void DedicatedLfeEffectHandler::SetParamf(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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void DedicatedLfeEffectHandler::SetParamfv(DedicatedProps &props, ALenum param, const float *vals)
|
|
||||||
{ SetParamf(props, param, *vals); }
|
|
||||||
|
|
||||||
void DedicatedLfeEffectHandler::GetParami(const DedicatedProps&, ALenum param, int*)
|
context->throw_error(AL_INVALID_ENUM, "Invalid dedicated float property {:#04x}",
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
as_unsigned(param));
|
||||||
void DedicatedLfeEffectHandler::GetParamiv(const DedicatedProps&, ALenum param, int*)
|
|
||||||
{
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
void DedicatedLfeEffectHandler::GetParamf(const DedicatedProps &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;
|
case AL_DEDICATED_GAIN: *val = props.Gain; return;
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid dedicated float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void DedicatedLfeEffectHandler::GetParamfv(const DedicatedProps &props, ALenum param, float *vals)
|
void DedicatedLfeEffectHandler::GetParamfv(ALCcontext *context, const DedicatedProps &props, ALenum param, float *vals)
|
||||||
{ GetParamf(props, param, vals); }
|
{ GetParamf(context, props, param, vals); }
|
||||||
|
|
|
||||||
|
|
@ -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
|
#if ALSOFT_EAX
|
||||||
#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"
|
||||||
|
|
@ -32,80 +32,76 @@ constexpr EffectProps genDefaultProps() noexcept
|
||||||
|
|
||||||
const EffectProps DistortionEffectProps{genDefaultProps()};
|
const EffectProps DistortionEffectProps{genDefaultProps()};
|
||||||
|
|
||||||
void DistortionEffectHandler::SetParami(DistortionProps&, ALenum param, int)
|
void DistortionEffectHandler::SetParami(ALCcontext *context, DistortionProps&, ALenum param, int)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
|
{ context->throw_error(AL_INVALID_ENUM, "Invalid distortion integer property {:#04x}", as_unsigned(param)); }
|
||||||
void DistortionEffectHandler::SetParamiv(DistortionProps&, ALenum param, const int*)
|
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)); }
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
|
|
||||||
param};
|
void DistortionEffectHandler::SetParamf(ALCcontext *context, DistortionProps &props, ALenum param, float val)
|
||||||
}
|
|
||||||
void DistortionEffectHandler::SetParamf(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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void DistortionEffectHandler::SetParamfv(DistortionProps &props, ALenum param, const float *vals)
|
|
||||||
{ SetParamf(props, param, *vals); }
|
|
||||||
|
|
||||||
void DistortionEffectHandler::GetParami(const DistortionProps&, ALenum param, int*)
|
context->throw_error(AL_INVALID_ENUM, "Invalid distortion float property {:#04x}",
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
|
as_unsigned(param));
|
||||||
void DistortionEffectHandler::GetParamiv(const DistortionProps&, ALenum param, int*)
|
|
||||||
{
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
void DistortionEffectHandler::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_EDGE: *val = props.Edge; return;
|
||||||
case AL_DISTORTION_GAIN: *val = props.Gain; break;
|
case AL_DISTORTION_GAIN: *val = props.Gain; return;
|
||||||
case AL_DISTORTION_LOWPASS_CUTOFF: *val = props.LowpassCutoff; break;
|
case AL_DISTORTION_LOWPASS_CUTOFF: *val = props.LowpassCutoff; return;
|
||||||
case AL_DISTORTION_EQCENTER: *val = props.EQCenter; break;
|
case AL_DISTORTION_EQCENTER: *val = props.EQCenter; return;
|
||||||
case AL_DISTORTION_EQBANDWIDTH: *val = props.EQBandwidth; break;
|
case AL_DISTORTION_EQBANDWIDTH: *val = props.EQBandwidth; return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid distortion float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void DistortionEffectHandler::GetParamfv(const DistortionProps &props, ALenum param, float *vals)
|
void DistortionEffectHandler::GetParamfv(ALCcontext *context, const DistortionProps &props, ALenum param, float *vals)
|
||||||
{ GetParamf(props, param, vals); }
|
{ GetParamf(context, props, param, vals); }
|
||||||
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using DistortionCommitter = EaxCommitter<EaxDistortionCommitter>;
|
using DistortionCommitter = EaxCommitter<EaxDistortionCommitter>;
|
||||||
|
|
|
||||||
|
|
@ -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
|
#if ALSOFT_EAX
|
||||||
#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"
|
||||||
|
|
@ -35,74 +35,74 @@ constexpr EffectProps genDefaultProps() noexcept
|
||||||
|
|
||||||
const EffectProps EchoEffectProps{genDefaultProps()};
|
const EffectProps EchoEffectProps{genDefaultProps()};
|
||||||
|
|
||||||
void EchoEffectHandler::SetParami(EchoProps&, ALenum param, int)
|
void EchoEffectHandler::SetParami(ALCcontext *context, EchoProps&, ALenum param, int)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
|
{ context->throw_error(AL_INVALID_ENUM, "Invalid echo integer property {:#04x}", as_unsigned(param)); }
|
||||||
void EchoEffectHandler::SetParamiv(EchoProps&, ALenum param, const int*)
|
void EchoEffectHandler::SetParamiv(ALCcontext *context, EchoProps&, ALenum param, const int*)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
|
{ context->throw_error(AL_INVALID_ENUM, "Invalid echo integer-vector property {:#04x}", as_unsigned(param)); }
|
||||||
void EchoEffectHandler::SetParamf(EchoProps &props, ALenum param, float val)
|
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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void EchoEffectHandler::SetParamfv(EchoProps &props, ALenum param, const float *vals)
|
|
||||||
{ SetParamf(props, param, *vals); }
|
|
||||||
|
|
||||||
void EchoEffectHandler::GetParami(const EchoProps&, ALenum param, int*)
|
context->throw_error(AL_INVALID_ENUM, "Invalid echo float property {:#04x}",
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
|
as_unsigned(param));
|
||||||
void EchoEffectHandler::GetParamiv(const EchoProps&, ALenum param, int*)
|
}
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
|
void EchoEffectHandler::SetParamfv(ALCcontext *context, EchoProps &props, ALenum param, const float *vals)
|
||||||
void EchoEffectHandler::GetParamf(const EchoProps &props, ALenum param, float *val)
|
{ 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_DELAY: *val = props.Delay; return;
|
||||||
case AL_ECHO_LRDELAY: *val = props.LRDelay; break;
|
case AL_ECHO_LRDELAY: *val = props.LRDelay; return;
|
||||||
case AL_ECHO_DAMPING: *val = props.Damping; break;
|
case AL_ECHO_DAMPING: *val = props.Damping; return;
|
||||||
case AL_ECHO_FEEDBACK: *val = props.Feedback; break;
|
case AL_ECHO_FEEDBACK: *val = props.Feedback; return;
|
||||||
case AL_ECHO_SPREAD: *val = props.Spread; break;
|
case AL_ECHO_SPREAD: *val = props.Spread; return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid echo float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void EchoEffectHandler::GetParamfv(const EchoProps &props, ALenum param, float *vals)
|
void EchoEffectHandler::GetParamfv(ALCcontext *context, const EchoProps &props, ALenum param, float *vals)
|
||||||
{ GetParamf(props, param, vals); }
|
{ GetParamf(context, props, param, vals); }
|
||||||
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using EchoCommitter = EaxCommitter<EaxEchoCommitter>;
|
using EchoCommitter = EaxCommitter<EaxEchoCommitter>;
|
||||||
|
|
|
||||||
|
|
@ -3,23 +3,24 @@
|
||||||
|
|
||||||
#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"
|
||||||
|
|
||||||
#define DECL_HANDLER(N, T) \
|
#define DECL_HANDLER(N, T) \
|
||||||
struct N { \
|
struct N { \
|
||||||
using prop_type = T; \
|
using prop_type = T; \
|
||||||
\
|
\
|
||||||
static void SetParami(prop_type &props, ALenum param, int val); \
|
static void SetParami(ALCcontext *context, prop_type &props, ALenum param, int val); \
|
||||||
static void SetParamiv(prop_type &props, ALenum param, const int *vals); \
|
static void SetParamiv(ALCcontext *context, prop_type &props, ALenum param, const int *vals); \
|
||||||
static void SetParamf(prop_type &props, ALenum param, float val); \
|
static void SetParamf(ALCcontext *context, prop_type &props, ALenum param, float val); \
|
||||||
static void SetParamfv(prop_type &props, ALenum param, const float *vals);\
|
static void SetParamfv(ALCcontext *context, prop_type &props, ALenum param, const float *vals);\
|
||||||
static void GetParami(const prop_type &props, ALenum param, int *val); \
|
static void GetParami(ALCcontext *context, const prop_type &props, ALenum param, int *val); \
|
||||||
static void GetParamiv(const prop_type &props, ALenum param, int *vals); \
|
static void GetParamiv(ALCcontext *context, const prop_type &props, ALenum param, int *vals); \
|
||||||
static void GetParamf(const prop_type &props, ALenum param, float *val); \
|
static void GetParamf(ALCcontext *context, const prop_type &props, ALenum param, float *val); \
|
||||||
static void GetParamfv(const prop_type &props, ALenum param, float *vals);\
|
static void GetParamfv(ALCcontext *context, const prop_type &props, ALenum param, float *vals);\
|
||||||
};
|
};
|
||||||
DECL_HANDLER(NullEffectHandler, std::monostate)
|
DECL_HANDLER(NullEffectHandler, std::monostate)
|
||||||
DECL_HANDLER(ReverbEffectHandler, ReverbProps)
|
DECL_HANDLER(ReverbEffectHandler, ReverbProps)
|
||||||
|
|
@ -41,26 +42,23 @@ DECL_HANDLER(ConvolutionEffectHandler, ConvolutionProps)
|
||||||
#undef DECL_HANDLER
|
#undef DECL_HANDLER
|
||||||
|
|
||||||
|
|
||||||
using effect_exception = al::context_error;
|
|
||||||
|
|
||||||
|
|
||||||
/* Default properties for the given effect types. */
|
/* Default properties for the given effect types. */
|
||||||
extern const EffectProps NullEffectProps;
|
DECL_HIDDEN extern const EffectProps NullEffectProps;
|
||||||
extern const EffectProps ReverbEffectProps;
|
DECL_HIDDEN extern const EffectProps ReverbEffectProps;
|
||||||
extern const EffectProps StdReverbEffectProps;
|
DECL_HIDDEN extern const EffectProps StdReverbEffectProps;
|
||||||
extern const EffectProps AutowahEffectProps;
|
DECL_HIDDEN extern const EffectProps AutowahEffectProps;
|
||||||
extern const EffectProps ChorusEffectProps;
|
DECL_HIDDEN extern const EffectProps ChorusEffectProps;
|
||||||
extern const EffectProps CompressorEffectProps;
|
DECL_HIDDEN extern const EffectProps CompressorEffectProps;
|
||||||
extern const EffectProps DistortionEffectProps;
|
DECL_HIDDEN extern const EffectProps DistortionEffectProps;
|
||||||
extern const EffectProps EchoEffectProps;
|
DECL_HIDDEN extern const EffectProps EchoEffectProps;
|
||||||
extern const EffectProps EqualizerEffectProps;
|
DECL_HIDDEN extern const EffectProps EqualizerEffectProps;
|
||||||
extern const EffectProps FlangerEffectProps;
|
DECL_HIDDEN extern const EffectProps FlangerEffectProps;
|
||||||
extern const EffectProps FshifterEffectProps;
|
DECL_HIDDEN extern const EffectProps FshifterEffectProps;
|
||||||
extern const EffectProps ModulatorEffectProps;
|
DECL_HIDDEN extern const EffectProps ModulatorEffectProps;
|
||||||
extern const EffectProps PshifterEffectProps;
|
DECL_HIDDEN extern const EffectProps PshifterEffectProps;
|
||||||
extern const EffectProps VmorpherEffectProps;
|
DECL_HIDDEN extern const EffectProps VmorpherEffectProps;
|
||||||
extern const EffectProps DedicatedDialogEffectProps;
|
DECL_HIDDEN extern const EffectProps DedicatedDialogEffectProps;
|
||||||
extern const EffectProps DedicatedLfeEffectProps;
|
DECL_HIDDEN extern const EffectProps DedicatedLfeEffectProps;
|
||||||
extern const EffectProps ConvolutionEffectProps;
|
DECL_HIDDEN extern const EffectProps ConvolutionEffectProps;
|
||||||
|
|
||||||
#endif /* AL_EFFECTS_EFFECTS_H */
|
#endif /* AL_EFFECTS_EFFECTS_H */
|
||||||
|
|
|
||||||
|
|
@ -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
|
#if ALSOFT_EAX
|
||||||
#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"
|
||||||
|
|
@ -37,115 +37,109 @@ constexpr EffectProps genDefaultProps() noexcept
|
||||||
|
|
||||||
const EffectProps EqualizerEffectProps{genDefaultProps()};
|
const EffectProps EqualizerEffectProps{genDefaultProps()};
|
||||||
|
|
||||||
void EqualizerEffectHandler::SetParami(EqualizerProps&, ALenum param, int)
|
void EqualizerEffectHandler::SetParami(ALCcontext *context, EqualizerProps&, ALenum param, int)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; }
|
{ context->throw_error(AL_INVALID_ENUM, "Invalid equalizer integer property {:#04x}", as_unsigned(param)); }
|
||||||
void EqualizerEffectHandler::SetParamiv(EqualizerProps&, ALenum param, const int*)
|
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)); }
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x",
|
void EqualizerEffectHandler::SetParamf(ALCcontext *context, EqualizerProps &props, ALenum param, float val)
|
||||||
param};
|
|
||||||
}
|
|
||||||
void EqualizerEffectHandler::SetParamf(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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void EqualizerEffectHandler::SetParamfv(EqualizerProps &props, ALenum param, const float *vals)
|
|
||||||
{ SetParamf(props, param, *vals); }
|
|
||||||
|
|
||||||
void EqualizerEffectHandler::GetParami(const EqualizerProps&, ALenum param, int*)
|
context->throw_error(AL_INVALID_ENUM, "Invalid equalizer float property {:#04x}",
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; }
|
as_unsigned(param));
|
||||||
void EqualizerEffectHandler::GetParamiv(const EqualizerProps&, ALenum param, int*)
|
|
||||||
{
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
void EqualizerEffectHandler::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_GAIN: *val = props.LowGain; return;
|
||||||
case AL_EQUALIZER_LOW_CUTOFF: *val = props.LowCutoff; break;
|
case AL_EQUALIZER_LOW_CUTOFF: *val = props.LowCutoff; return;
|
||||||
case AL_EQUALIZER_MID1_GAIN: *val = props.Mid1Gain; break;
|
case AL_EQUALIZER_MID1_GAIN: *val = props.Mid1Gain; return;
|
||||||
case AL_EQUALIZER_MID1_CENTER: *val = props.Mid1Center; break;
|
case AL_EQUALIZER_MID1_CENTER: *val = props.Mid1Center; return;
|
||||||
case AL_EQUALIZER_MID1_WIDTH: *val = props.Mid1Width; break;
|
case AL_EQUALIZER_MID1_WIDTH: *val = props.Mid1Width; return;
|
||||||
case AL_EQUALIZER_MID2_GAIN: *val = props.Mid2Gain; break;
|
case AL_EQUALIZER_MID2_GAIN: *val = props.Mid2Gain; return;
|
||||||
case AL_EQUALIZER_MID2_CENTER: *val = props.Mid2Center; break;
|
case AL_EQUALIZER_MID2_CENTER: *val = props.Mid2Center; return;
|
||||||
case AL_EQUALIZER_MID2_WIDTH: *val = props.Mid2Width; break;
|
case AL_EQUALIZER_MID2_WIDTH: *val = props.Mid2Width; return;
|
||||||
case AL_EQUALIZER_HIGH_GAIN: *val = props.HighGain; break;
|
case AL_EQUALIZER_HIGH_GAIN: *val = props.HighGain; return;
|
||||||
case AL_EQUALIZER_HIGH_CUTOFF: *val = props.HighCutoff; break;
|
case AL_EQUALIZER_HIGH_CUTOFF: *val = props.HighCutoff; return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid equalizer float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void EqualizerEffectHandler::GetParamfv(const EqualizerProps &props, ALenum param, float *vals)
|
void EqualizerEffectHandler::GetParamfv(ALCcontext *context, const EqualizerProps &props, ALenum param, float *vals)
|
||||||
{ GetParamf(props, param, vals); }
|
{ GetParamf(context, props, param, vals); }
|
||||||
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using EqualizerCommitter = EaxCommitter<EaxEqualizerCommitter>;
|
using EqualizerCommitter = EaxCommitter<EaxEqualizerCommitter>;
|
||||||
|
|
|
||||||
|
|
@ -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 FshifterEffectHandler::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 FshifterEffectHandler::SetParami(FshifterProps &props, ALenum param, int va
|
||||||
if(auto diropt = DirectionFromEmum(val))
|
if(auto diropt = DirectionFromEmum(val))
|
||||||
props.LeftDirection = *diropt;
|
props.LeftDirection = *diropt;
|
||||||
else
|
else
|
||||||
throw effect_exception{AL_INVALID_VALUE,
|
context->throw_error(AL_INVALID_VALUE,
|
||||||
"Unsupported frequency shifter left direction: 0x%04x", val};
|
"Unsupported frequency shifter left direction: {:#04x}", as_unsigned(val));
|
||||||
break;
|
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,
|
context->throw_error(AL_INVALID_VALUE,
|
||||||
"Unsupported frequency shifter right direction: 0x%04x", val};
|
"Unsupported frequency shifter right direction: {:#04x}", as_unsigned(val));
|
||||||
break;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM,
|
|
||||||
"Invalid frequency shifter integer property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void FshifterEffectHandler::SetParamiv(FshifterProps &props, ALenum param, const int *vals)
|
|
||||||
{ SetParami(props, param, *vals); }
|
|
||||||
|
|
||||||
void FshifterEffectHandler::SetParamf(FshifterProps &props, ALenum param, float val)
|
context->throw_error(AL_INVALID_ENUM, "Invalid frequency shifter integer property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
|
}
|
||||||
|
void FshifterEffectHandler::SetParamiv(ALCcontext *context, FshifterProps &props, ALenum param, const int *vals)
|
||||||
|
{ SetParami(context, props, param, *vals); }
|
||||||
|
|
||||||
|
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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void FshifterEffectHandler::SetParamfv(FshifterProps &props, ALenum param, const float *vals)
|
|
||||||
{ SetParamf(props, param, *vals); }
|
|
||||||
|
|
||||||
void FshifterEffectHandler::GetParami(const FshifterProps &props, ALenum param, int *val)
|
context->throw_error(AL_INVALID_ENUM, "Invalid frequency shifter float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
|
}
|
||||||
|
void FshifterEffectHandler::SetParamfv(ALCcontext *context, FshifterProps &props, ALenum param, const float *vals)
|
||||||
|
{ SetParamf(context, props, param, *vals); }
|
||||||
|
|
||||||
|
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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM,
|
|
||||||
"Invalid frequency shifter integer property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void FshifterEffectHandler::GetParamiv(const FshifterProps &props, ALenum param, int *vals)
|
|
||||||
{ GetParami(props, param, vals); }
|
|
||||||
|
|
||||||
void FshifterEffectHandler::GetParamf(const FshifterProps &props, ALenum param, float *val)
|
context->throw_error(AL_INVALID_ENUM, "Invalid frequency shifter integer property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
|
}
|
||||||
|
void FshifterEffectHandler::GetParamiv(ALCcontext *context, const FshifterProps &props, ALenum param, int *vals)
|
||||||
|
{ GetParami(context, props, param, vals); }
|
||||||
|
|
||||||
|
void FshifterEffectHandler::GetParamf(ALCcontext *context, const FshifterProps &props, ALenum param, float *val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_FREQUENCY_SHIFTER_FREQUENCY:
|
case AL_FREQUENCY_SHIFTER_FREQUENCY: *val = props.Frequency; return;
|
||||||
*val = props.Frequency;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid frequency shifter float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void FshifterEffectHandler::GetParamfv(const FshifterProps &props, ALenum param, float *vals)
|
void FshifterEffectHandler::GetParamfv(ALCcontext *context, const FshifterProps &props, ALenum param, float *vals)
|
||||||
{ GetParamf(props, param, vals); }
|
{ GetParamf(context, props, param, vals); }
|
||||||
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using FrequencyShifterCommitter = EaxCommitter<EaxFrequencyShifterCommitter>;
|
using FrequencyShifterCommitter = EaxCommitter<EaxFrequencyShifterCommitter>;
|
||||||
|
|
|
||||||
|
|
@ -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: " +
|
throw std::runtime_error{fmt::format("Invalid modulator waveform: {}",
|
||||||
std::to_string(static_cast<int>(type))};
|
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 ModulatorEffectHandler::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));
|
SetParamf(context, props, param, static_cast<float>(val));
|
||||||
break;
|
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};
|
context->throw_error(AL_INVALID_VALUE, "Invalid modulator waveform: {:#04x}",
|
||||||
break;
|
as_unsigned(val));
|
||||||
|
return;
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void ModulatorEffectHandler::SetParamiv(ModulatorProps &props, ALenum param, const int *vals)
|
|
||||||
{ SetParami(props, param, *vals); }
|
|
||||||
|
|
||||||
void ModulatorEffectHandler::SetParamf(ModulatorProps &props, ALenum param, float val)
|
context->throw_error(AL_INVALID_ENUM, "Invalid modulator integer property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
|
}
|
||||||
|
void ModulatorEffectHandler::SetParamiv(ALCcontext *context, ModulatorProps &props, ALenum param, const int *vals)
|
||||||
|
{ SetParami(context, props, param, *vals); }
|
||||||
|
|
||||||
|
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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void ModulatorEffectHandler::SetParamfv(ModulatorProps &props, ALenum param, const float *vals)
|
|
||||||
{ SetParamf(props, param, *vals); }
|
|
||||||
|
|
||||||
void ModulatorEffectHandler::GetParami(const ModulatorProps &props, ALenum param, int *val)
|
context->throw_error(AL_INVALID_ENUM, "Invalid modulator float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
|
}
|
||||||
|
void ModulatorEffectHandler::SetParamfv(ALCcontext *context, ModulatorProps &props, ALenum param, const float *vals)
|
||||||
|
{ SetParamf(context, props, param, *vals); }
|
||||||
|
|
||||||
|
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_FREQUENCY: *val = static_cast<int>(props.Frequency); return;
|
||||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = static_cast<int>(props.HighPassCutoff); break;
|
case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = static_cast<int>(props.HighPassCutoff); return;
|
||||||
case AL_RING_MODULATOR_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
|
case AL_RING_MODULATOR_WAVEFORM: *val = EnumFromWaveform(props.Waveform); return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid modulator integer property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void ModulatorEffectHandler::GetParamiv(const ModulatorProps &props, ALenum param, int *vals)
|
void ModulatorEffectHandler::GetParamiv(ALCcontext *context, const ModulatorProps &props, ALenum param, int *vals)
|
||||||
{ GetParami(props, param, vals); }
|
{ GetParami(context, props, param, vals); }
|
||||||
void ModulatorEffectHandler::GetParamf(const ModulatorProps &props, ALenum param, float *val)
|
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_FREQUENCY: *val = props.Frequency; return;
|
||||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = props.HighPassCutoff; break;
|
case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = props.HighPassCutoff; return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid modulator float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void ModulatorEffectHandler::GetParamfv(const ModulatorProps &props, ALenum param, float *vals)
|
void ModulatorEffectHandler::GetParamfv(ALCcontext *context, const ModulatorProps &props, ALenum param, float *vals)
|
||||||
{ GetParamf(props, param, vals); }
|
{ GetParamf(context, props, param, vals); }
|
||||||
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using ModulatorCommitter = EaxCommitter<EaxModulatorCommitter>;
|
using ModulatorCommitter = EaxCommitter<EaxModulatorCommitter>;
|
||||||
|
|
|
||||||
|
|
@ -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 NullEffectHandler::SetParami(std::monostate& /*props*/, ALenum param, int /*val*/)
|
void NullEffectHandler::SetParami(ALCcontext *context, std::monostate& /*props*/, ALenum param, int /*val*/)
|
||||||
{
|
{
|
||||||
switch(param)
|
context->throw_error(AL_INVALID_ENUM, "Invalid null effect integer property {:#04x}",
|
||||||
{
|
as_unsigned(param));
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
void NullEffectHandler::SetParamiv(std::monostate &props, ALenum param, const int *vals)
|
void NullEffectHandler::SetParamiv(ALCcontext *context, std::monostate &props, ALenum param, const int *vals)
|
||||||
{
|
{
|
||||||
switch(param)
|
SetParami(context, props, param, *vals);
|
||||||
{
|
|
||||||
default:
|
|
||||||
SetParami(props, param, *vals);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
void NullEffectHandler::SetParamf(std::monostate& /*props*/, ALenum param, float /*val*/)
|
void NullEffectHandler::SetParamf(ALCcontext *context, std::monostate& /*props*/, ALenum param, float /*val*/)
|
||||||
{
|
{
|
||||||
switch(param)
|
context->throw_error(AL_INVALID_ENUM, "Invalid null effect float property {:#04x}",
|
||||||
{
|
as_unsigned(param));
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
void NullEffectHandler::SetParamfv(std::monostate &props, ALenum param, const float *vals)
|
void NullEffectHandler::SetParamfv(ALCcontext *context, std::monostate &props, ALenum param, const float *vals)
|
||||||
{
|
{
|
||||||
switch(param)
|
SetParamf(context, props, param, *vals);
|
||||||
{
|
|
||||||
default:
|
|
||||||
SetParamf(props, param, *vals);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullEffectHandler::GetParami(const std::monostate& /*props*/, ALenum param, int* /*val*/)
|
void NullEffectHandler::GetParami(ALCcontext *context, const std::monostate& /*props*/, ALenum param, int* /*val*/)
|
||||||
{
|
{
|
||||||
switch(param)
|
context->throw_error(AL_INVALID_ENUM, "Invalid null effect integer property {:#04x}",
|
||||||
{
|
as_unsigned(param));
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
void NullEffectHandler::GetParamiv(const std::monostate &props, ALenum param, int *vals)
|
void NullEffectHandler::GetParamiv(ALCcontext *context, const std::monostate &props, ALenum param, int *vals)
|
||||||
{
|
{
|
||||||
switch(param)
|
GetParami(context, props, param, vals);
|
||||||
{
|
|
||||||
default:
|
|
||||||
GetParami(props, param, vals);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
void NullEffectHandler::GetParamf(const std::monostate& /*props*/, ALenum param, float* /*val*/)
|
void NullEffectHandler::GetParamf(ALCcontext *context, const std::monostate& /*props*/, ALenum param, float* /*val*/)
|
||||||
{
|
{
|
||||||
switch(param)
|
context->throw_error(AL_INVALID_ENUM, "Invalid null effect float property {:#04x}",
|
||||||
{
|
as_unsigned(param));
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
void NullEffectHandler::GetParamfv(const std::monostate &props, ALenum param, float *vals)
|
void NullEffectHandler::GetParamfv(ALCcontext *context, const std::monostate &props, ALenum param, float *vals)
|
||||||
{
|
{
|
||||||
switch(param)
|
GetParamf(context, props, param, vals);
|
||||||
{
|
|
||||||
default:
|
|
||||||
GetParamf(props, param, vals);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using NullCommitter = EaxCommitter<EaxNullCommitter>;
|
using NullCommitter = EaxCommitter<EaxNullCommitter>;
|
||||||
|
|
|
||||||
|
|
@ -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
|
#if ALSOFT_EAX
|
||||||
#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"
|
||||||
|
|
@ -29,63 +29,55 @@ constexpr EffectProps genDefaultProps() noexcept
|
||||||
|
|
||||||
const EffectProps PshifterEffectProps{genDefaultProps()};
|
const EffectProps PshifterEffectProps{genDefaultProps()};
|
||||||
|
|
||||||
void PshifterEffectHandler::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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void PshifterEffectHandler::SetParamiv(PshifterProps &props, ALenum param, const int *vals)
|
|
||||||
{ SetParami(props, param, *vals); }
|
|
||||||
|
|
||||||
void PshifterEffectHandler::SetParamf(PshifterProps&, ALenum param, float)
|
context->throw_error(AL_INVALID_ENUM, "Invalid pitch shifter integer property {:#04x}",
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
|
as_unsigned(param));
|
||||||
void PshifterEffectHandler::SetParamfv(PshifterProps&, ALenum param, const float*)
|
|
||||||
{
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
|
void PshifterEffectHandler::SetParamiv(ALCcontext *context, PshifterProps &props, ALenum param, const int *vals)
|
||||||
|
{ SetParami(context, props, param, *vals); }
|
||||||
|
|
||||||
void PshifterEffectHandler::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_COARSE_TUNE: *val = props.CoarseTune; return;
|
||||||
case AL_PITCH_SHIFTER_FINE_TUNE: *val = props.FineTune; break;
|
case AL_PITCH_SHIFTER_FINE_TUNE: *val = props.FineTune; return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid pitch shifter integer property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void PshifterEffectHandler::GetParamiv(const PshifterProps &props, ALenum param, int *vals)
|
void PshifterEffectHandler::GetParamiv(ALCcontext *context, const PshifterProps &props, ALenum param, int *vals)
|
||||||
{ GetParami(props, param, vals); }
|
{ GetParami(context, props, param, vals); }
|
||||||
|
|
||||||
void PshifterEffectHandler::GetParamf(const PshifterProps&, ALenum param, float*)
|
void PshifterEffectHandler::GetParamf(ALCcontext *context, const PshifterProps&, ALenum param, float*)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
|
{ context->throw_error(AL_INVALID_ENUM, "Invalid pitch shifter float property {:#04x}", as_unsigned(param)); }
|
||||||
void PshifterEffectHandler::GetParamfv(const PshifterProps&, ALenum param, float*)
|
void PshifterEffectHandler::GetParamfv(ALCcontext *context, const PshifterProps &props, ALenum param, float *vals)
|
||||||
{
|
{ GetParamf(context, props, param, vals); }
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using PitchShifterCommitter = EaxCommitter<EaxPitchShifterCommitter>;
|
using PitchShifterCommitter = EaxCommitter<EaxPitchShifterCommitter>;
|
||||||
|
|
|
||||||
|
|
@ -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 ReverbEffectHandler::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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb integer property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void ReverbEffectHandler::SetParamiv(ReverbProps &props, ALenum param, const int *vals)
|
void ReverbEffectHandler::SetParamiv(ALCcontext *context, ReverbProps &props, ALenum param, const int *vals)
|
||||||
{ SetParami(props, param, *vals); }
|
{ SetParami(context, props, param, *vals); }
|
||||||
void ReverbEffectHandler::SetParamf(ReverbProps &props, ALenum param, float val)
|
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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void ReverbEffectHandler::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 ReverbEffectHandler::SetParamfv(ReverbProps &props, ALenum param, const flo
|
||||||
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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
SetParamf(props, param, *vals);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
SetParamf(context, props, param, *vals);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReverbEffectHandler::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;
|
case AL_EAXREVERB_DECAY_HFLIMIT: *val = props.DecayHFLimit; return;
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb integer property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void ReverbEffectHandler::GetParamiv(const ReverbProps &props, ALenum param, int *vals)
|
void ReverbEffectHandler::GetParamiv(ALCcontext *context, const ReverbProps &props, ALenum param, int *vals)
|
||||||
{ GetParami(props, param, vals); }
|
{ GetParami(context, props, param, vals); }
|
||||||
void ReverbEffectHandler::GetParamf(const ReverbProps &props, ALenum param, float *val)
|
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_DENSITY: *val = props.Density; return;
|
||||||
case AL_EAXREVERB_DIFFUSION: *val = props.Diffusion; break;
|
case AL_EAXREVERB_DIFFUSION: *val = props.Diffusion; return;
|
||||||
case AL_EAXREVERB_GAIN: *val = props.Gain; break;
|
case AL_EAXREVERB_GAIN: *val = props.Gain; return;
|
||||||
case AL_EAXREVERB_GAINHF: *val = props.GainHF; break;
|
case AL_EAXREVERB_GAINHF: *val = props.GainHF; return;
|
||||||
case AL_EAXREVERB_GAINLF: *val = props.GainLF; break;
|
case AL_EAXREVERB_GAINLF: *val = props.GainLF; return;
|
||||||
case AL_EAXREVERB_DECAY_TIME: *val = props.DecayTime; break;
|
case AL_EAXREVERB_DECAY_TIME: *val = props.DecayTime; return;
|
||||||
case AL_EAXREVERB_DECAY_HFRATIO: *val = props.DecayHFRatio; break;
|
case AL_EAXREVERB_DECAY_HFRATIO: *val = props.DecayHFRatio; return;
|
||||||
case AL_EAXREVERB_DECAY_LFRATIO: *val = props.DecayLFRatio; break;
|
case AL_EAXREVERB_DECAY_LFRATIO: *val = props.DecayLFRatio; return;
|
||||||
case AL_EAXREVERB_REFLECTIONS_GAIN: *val = props.ReflectionsGain; break;
|
case AL_EAXREVERB_REFLECTIONS_GAIN: *val = props.ReflectionsGain; return;
|
||||||
case AL_EAXREVERB_REFLECTIONS_DELAY: *val = props.ReflectionsDelay; break;
|
case AL_EAXREVERB_REFLECTIONS_DELAY: *val = props.ReflectionsDelay; return;
|
||||||
case AL_EAXREVERB_LATE_REVERB_GAIN: *val = props.LateReverbGain; break;
|
case AL_EAXREVERB_LATE_REVERB_GAIN: *val = props.LateReverbGain; return;
|
||||||
case AL_EAXREVERB_LATE_REVERB_DELAY: *val = props.LateReverbDelay; break;
|
case AL_EAXREVERB_LATE_REVERB_DELAY: *val = props.LateReverbDelay; return;
|
||||||
case AL_EAXREVERB_ECHO_TIME: *val = props.EchoTime; break;
|
case AL_EAXREVERB_ECHO_TIME: *val = props.EchoTime; return;
|
||||||
case AL_EAXREVERB_ECHO_DEPTH: *val = props.EchoDepth; break;
|
case AL_EAXREVERB_ECHO_DEPTH: *val = props.EchoDepth; return;
|
||||||
case AL_EAXREVERB_MODULATION_TIME: *val = props.ModulationTime; break;
|
case AL_EAXREVERB_MODULATION_TIME: *val = props.ModulationTime; return;
|
||||||
case AL_EAXREVERB_MODULATION_DEPTH: *val = props.ModulationDepth; break;
|
case AL_EAXREVERB_MODULATION_DEPTH: *val = props.ModulationDepth; return;
|
||||||
case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: *val = props.AirAbsorptionGainHF; break;
|
case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: *val = props.AirAbsorptionGainHF; return;
|
||||||
case AL_EAXREVERB_HFREFERENCE: *val = props.HFReference; break;
|
case AL_EAXREVERB_HFREFERENCE: *val = props.HFReference; return;
|
||||||
case AL_EAXREVERB_LFREFERENCE: *val = props.LFReference; break;
|
case AL_EAXREVERB_LFREFERENCE: *val = props.LFReference; return;
|
||||||
case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: *val = props.RoomRolloffFactor; break;
|
case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: *val = props.RoomRolloffFactor; return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void ReverbEffectHandler::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,159 +308,155 @@ void ReverbEffectHandler::GetParamfv(const ReverbProps &props, ALenum param, flo
|
||||||
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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
GetParamf(props, param, vals);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GetParamf(context, props, param, vals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const EffectProps StdReverbEffectProps{genDefaultStdProps()};
|
const EffectProps StdReverbEffectProps{genDefaultStdProps()};
|
||||||
|
|
||||||
void StdReverbEffectHandler::SetParami(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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb integer property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void StdReverbEffectHandler::SetParamiv(ReverbProps &props, ALenum param, const int *vals)
|
void StdReverbEffectHandler::SetParamiv(ALCcontext *context, ReverbProps &props, ALenum param, const int *vals)
|
||||||
{ SetParami(props, param, *vals); }
|
{ SetParami(context, props, param, *vals); }
|
||||||
void StdReverbEffectHandler::SetParamf(ReverbProps &props, ALenum param, float val)
|
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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void StdReverbEffectHandler::SetParamfv(ReverbProps &props, ALenum param, const float *vals)
|
|
||||||
{ SetParamf(props, param, *vals); }
|
|
||||||
|
|
||||||
void StdReverbEffectHandler::GetParami(const ReverbProps &props, ALenum param, int *val)
|
context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
|
}
|
||||||
|
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_DECAY_HFLIMIT: *val = props.DecayHFLimit; break;
|
case AL_REVERB_DECAY_HFLIMIT: *val = props.DecayHFLimit; return;
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb integer property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void StdReverbEffectHandler::GetParamiv(const ReverbProps &props, ALenum param, int *vals)
|
void StdReverbEffectHandler::GetParamiv(ALCcontext *context, const ReverbProps &props, ALenum param, int *vals)
|
||||||
{ GetParami(props, param, vals); }
|
{ GetParami(context, props, param, vals); }
|
||||||
void StdReverbEffectHandler::GetParamf(const ReverbProps &props, ALenum param, float *val)
|
void StdReverbEffectHandler::GetParamf(ALCcontext *context, const ReverbProps &props, ALenum param, float *val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_REVERB_DENSITY: *val = props.Density; break;
|
case AL_REVERB_DENSITY: *val = props.Density; return;
|
||||||
case AL_REVERB_DIFFUSION: *val = props.Diffusion; break;
|
case AL_REVERB_DIFFUSION: *val = props.Diffusion; return;
|
||||||
case AL_REVERB_GAIN: *val = props.Gain; break;
|
case AL_REVERB_GAIN: *val = props.Gain; return;
|
||||||
case AL_REVERB_GAINHF: *val = props.GainHF; break;
|
case AL_REVERB_GAINHF: *val = props.GainHF; return;
|
||||||
case AL_REVERB_DECAY_TIME: *val = props.DecayTime; break;
|
case AL_REVERB_DECAY_TIME: *val = props.DecayTime; return;
|
||||||
case AL_REVERB_DECAY_HFRATIO: *val = props.DecayHFRatio; break;
|
case AL_REVERB_DECAY_HFRATIO: *val = props.DecayHFRatio; return;
|
||||||
case AL_REVERB_REFLECTIONS_GAIN: *val = props.ReflectionsGain; break;
|
case AL_REVERB_REFLECTIONS_GAIN: *val = props.ReflectionsGain; return;
|
||||||
case AL_REVERB_REFLECTIONS_DELAY: *val = props.ReflectionsDelay; break;
|
case AL_REVERB_REFLECTIONS_DELAY: *val = props.ReflectionsDelay; return;
|
||||||
case AL_REVERB_LATE_REVERB_GAIN: *val = props.LateReverbGain; break;
|
case AL_REVERB_LATE_REVERB_GAIN: *val = props.LateReverbGain; return;
|
||||||
case AL_REVERB_LATE_REVERB_DELAY: *val = props.LateReverbDelay; break;
|
case AL_REVERB_LATE_REVERB_DELAY: *val = props.LateReverbDelay; return;
|
||||||
case AL_REVERB_AIR_ABSORPTION_GAINHF: *val = props.AirAbsorptionGainHF; break;
|
case AL_REVERB_AIR_ABSORPTION_GAINHF: *val = props.AirAbsorptionGainHF; return;
|
||||||
case AL_REVERB_ROOM_ROLLOFF_FACTOR: *val = props.RoomRolloffFactor; break;
|
case AL_REVERB_ROOM_ROLLOFF_FACTOR: *val = props.RoomRolloffFactor; return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void StdReverbEffectHandler::GetParamfv(const ReverbProps &props, ALenum param, float *vals)
|
void StdReverbEffectHandler::GetParamfv(ALCcontext *context, const ReverbProps &props, ALenum param, float *vals)
|
||||||
{ GetParamf(props, param, vals); }
|
{ GetParamf(context, props, param, vals); }
|
||||||
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class EaxReverbEffectException : public EaxException
|
class EaxReverbEffectException : public EaxException
|
||||||
|
|
@ -1064,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;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,11 @@
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
#include "core/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 "al/eax/effect.h"
|
#include "al/eax/effect.h"
|
||||||
#include "al/eax/exception.h"
|
#include "al/eax/exception.h"
|
||||||
|
|
@ -96,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,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: " +
|
throw std::runtime_error{fmt::format("Invalid vocal morpher waveform: {}",
|
||||||
std::to_string(static_cast<int>(type))};
|
int{al::to_underlying(type)})};
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr EffectProps genDefaultProps() noexcept
|
constexpr EffectProps genDefaultProps() noexcept
|
||||||
|
|
@ -138,7 +139,7 @@ constexpr EffectProps genDefaultProps() noexcept
|
||||||
|
|
||||||
const EffectProps VmorpherEffectProps{genDefaultProps()};
|
const EffectProps VmorpherEffectProps{genDefaultProps()};
|
||||||
|
|
||||||
void VmorpherEffectHandler::SetParami(VmorpherProps &props, ALenum param, int val)
|
void VmorpherEffectHandler::SetParami(ALCcontext *context, VmorpherProps &props, ALenum param, int val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
|
|
@ -146,101 +147,94 @@ void VmorpherEffectHandler::SetParami(VmorpherProps &props, ALenum param, int va
|
||||||
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};
|
context->throw_error(AL_INVALID_VALUE,
|
||||||
break;
|
"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};
|
context->throw_error(AL_INVALID_VALUE,
|
||||||
break;
|
"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};
|
context->throw_error(AL_INVALID_VALUE, "Vocal morpher waveform out of range: {:#04x}",
|
||||||
break;
|
as_unsigned(val));
|
||||||
|
return;
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid vocal morpher integer property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void VmorpherEffectHandler::SetParamiv(VmorpherProps&, ALenum param, const int*)
|
void VmorpherEffectHandler::SetParamiv(ALCcontext *context, VmorpherProps &props, ALenum param, const int *vals)
|
||||||
{
|
{ SetParami(context, props, param, *vals); }
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x",
|
void VmorpherEffectHandler::SetParamf(ALCcontext *context, VmorpherProps &props, ALenum param, float val)
|
||||||
param};
|
|
||||||
}
|
|
||||||
void VmorpherEffectHandler::SetParamf(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;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher float property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void VmorpherEffectHandler::SetParamfv(VmorpherProps &props, ALenum param, const float *vals)
|
|
||||||
{ SetParamf(props, param, *vals); }
|
|
||||||
|
|
||||||
void VmorpherEffectHandler::GetParami(const VmorpherProps &props, ALenum param, int* val)
|
context->throw_error(AL_INVALID_ENUM, "Invalid vocal morpher float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
|
}
|
||||||
|
void VmorpherEffectHandler::SetParamfv(ALCcontext *context, VmorpherProps &props, ALenum param, const float *vals)
|
||||||
|
{ SetParamf(context, props, param, *vals); }
|
||||||
|
|
||||||
|
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: *val = EnumFromPhenome(props.PhonemeA); return;
|
||||||
case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: *val = props.PhonemeACoarseTuning; break;
|
case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: *val = props.PhonemeACoarseTuning; return;
|
||||||
case AL_VOCAL_MORPHER_PHONEMEB: *val = EnumFromPhenome(props.PhonemeB); break;
|
case AL_VOCAL_MORPHER_PHONEMEB: *val = EnumFromPhenome(props.PhonemeB); return;
|
||||||
case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: *val = props.PhonemeBCoarseTuning; break;
|
case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: *val = props.PhonemeBCoarseTuning; return;
|
||||||
case AL_VOCAL_MORPHER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
|
case AL_VOCAL_MORPHER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); return;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid vocal morpher integer property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void VmorpherEffectHandler::GetParamiv(const VmorpherProps&, ALenum param, int*)
|
void VmorpherEffectHandler::GetParamiv(ALCcontext *context, const VmorpherProps &props, ALenum param, int *vals)
|
||||||
{
|
{ GetParami(context, props, param, vals); }
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x",
|
void VmorpherEffectHandler::GetParamf(ALCcontext *context, const VmorpherProps &props, ALenum param, float *val)
|
||||||
param};
|
|
||||||
}
|
|
||||||
void VmorpherEffectHandler::GetParamf(const VmorpherProps &props, ALenum param, float *val)
|
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_VOCAL_MORPHER_RATE:
|
case AL_VOCAL_MORPHER_RATE: *val = props.Rate; return;
|
||||||
*val = props.Rate;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher float property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->throw_error(AL_INVALID_ENUM, "Invalid vocal morpher float property {:#04x}",
|
||||||
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
void VmorpherEffectHandler::GetParamfv(const VmorpherProps &props, ALenum param, float *vals)
|
void VmorpherEffectHandler::GetParamfv(ALCcontext *context, const VmorpherProps &props, ALenum param, float *vals)
|
||||||
{ GetParamf(props, param, vals); }
|
{ GetParamf(context, props, param, vals); }
|
||||||
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using VocalMorpherCommitter = EaxCommitter<EaxVocalMorpherCommitter>;
|
using VocalMorpherCommitter = EaxCommitter<EaxVocalMorpherCommitter>;
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
void ALCcontext::setErrorImpl(ALenum errorCode, const fmt::string_view fmt, fmt::format_args args)
|
||||||
context_error::context_error(ALenum code, const char *msg, ...) : mErrorCode{code}
|
|
||||||
{
|
{
|
||||||
/* NOLINTBEGIN(*-array-to-pointer-decay) */
|
const auto msg = fmt::vformat(fmt, std::move(args));
|
||||||
std::va_list args;
|
|
||||||
va_start(args, msg);
|
|
||||||
setMessage(msg, args);
|
|
||||||
va_end(args);
|
|
||||||
/* NOLINTEND(*-array-to-pointer-decay) */
|
|
||||||
}
|
|
||||||
context_error::~context_error() = default;
|
|
||||||
} /* namespace al */
|
|
||||||
|
|
||||||
void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
|
WARN("Error generated on context {}, code {:#04x}, \"{}\"",
|
||||||
{
|
decltype(std::declval<void*>()){this}, as_unsigned(errorCode), 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);
|
|
||||||
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,
|
debugMessage(DebugSource::API, DebugType::Error, static_cast<ALuint>(errorCode),
|
||||||
{msg, static_cast<uint>(msglen)});
|
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{};
|
try {
|
||||||
auto value = std::strtoul(optstr->c_str(), &end, 0);
|
auto idx = 0_uz;
|
||||||
if(end && *end == '\0' && value <= std::numeric_limits<ALenum>::max())
|
auto value = std::stoi(*optstr, &idx, 0);
|
||||||
return static_cast<ALenum>(value);
|
if(idx >= optstr->size() || std::isspace(optstr->at(idx)))
|
||||||
ERR("Invalid default error value: \"%s\"", optstr->c_str());
|
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
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
|
||||||
|
|
@ -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,15 +20,17 @@
|
||||||
#include "AL/alext.h"
|
#include "AL/alext.h"
|
||||||
|
|
||||||
#include "alc/context.h"
|
#include "alc/context.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"
|
||||||
|
|
@ -51,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)
|
||||||
|
|
@ -66,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)
|
||||||
{
|
{
|
||||||
|
|
@ -101,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)
|
||||||
{
|
{
|
||||||
|
|
@ -113,18 +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)
|
||||||
{
|
{
|
||||||
context->debugMessage(DebugSource::System, DebugType::Error, 0,
|
if(!context->mEventCb
|
||||||
DebugSeverity::High, evt.msg);
|
|| !enabledevts.test(al::to_underlying(AsyncEnableBits::Disconnected)))
|
||||||
|
return;
|
||||||
|
|
||||||
if(context->mEventCb
|
context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0, al::sizei(evt.msg),
|
||||||
&& enabledevts.test(al::to_underlying(AsyncEnableBits::Disconnected)))
|
evt.msg.c_str(), context->mEventParam);
|
||||||
context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0,
|
|
||||||
static_cast<ALsizei>(evt.msg.length()), evt.msg.c_str(),
|
|
||||||
context->mEventParam);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::visit(overloaded{proc_srcstate, proc_buffercomp, proc_release, proc_disconnect,
|
std::visit(overloaded{proc_srcstate, proc_buffercomp, proc_release, proc_disconnect,
|
||||||
|
|
@ -156,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);
|
||||||
|
|
@ -187,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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -226,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) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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 @@ 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)
|
void FilterTable<NullFilterTable>::setParami(ALCcontext *context, ALfilter*, ALenum param, int)
|
||||||
{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
|
{ 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*)
|
void FilterTable<NullFilterTable>::setParamiv(ALCcontext *context, ALfilter*, ALenum param, const int*)
|
||||||
{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
|
{ context->throw_error(AL_INVALID_ENUM, "Invalid null filter property {:#04x}", as_unsigned(param)); }
|
||||||
template<>
|
template<>
|
||||||
void FilterTable<NullFilterTable>::setParamf(ALfilter*, ALenum param, float)
|
void FilterTable<NullFilterTable>::setParamf(ALCcontext *context, ALfilter*, ALenum param, float)
|
||||||
{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
|
{ 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*)
|
void FilterTable<NullFilterTable>::setParamfv(ALCcontext *context, ALfilter*, ALenum param, const float*)
|
||||||
{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
|
{ 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*)
|
void FilterTable<NullFilterTable>::getParami(ALCcontext *context, const ALfilter*, ALenum param, int*)
|
||||||
{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
|
{ 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*)
|
void FilterTable<NullFilterTable>::getParamiv(ALCcontext *context, const ALfilter*, ALenum param, int*)
|
||||||
{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
|
{ 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*)
|
void FilterTable<NullFilterTable>::getParamf(ALCcontext *context, const ALfilter*, ALenum param, float*)
|
||||||
{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
|
{ 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*)
|
void FilterTable<NullFilterTable>::getParamfv(ALCcontext *context, const ALfilter*, ALenum param, float*)
|
||||||
{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
|
{ 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)
|
void FilterTable<LowpassFilterTable>::setParami(ALCcontext *context, ALfilter*, ALenum param, int)
|
||||||
{ throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
|
{ 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)
|
void FilterTable<LowpassFilterTable>::setParamiv(ALCcontext *context, ALfilter *filter, ALenum param, const int *values)
|
||||||
{ setParami(filter, param, *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)
|
void FilterTable<LowpassFilterTable>::setParamfv(ALCcontext *context, ALfilter *filter, ALenum param, const float *vals)
|
||||||
{ setParamf(filter, param, *vals); }
|
{ setParamf(context, filter, param, *vals); }
|
||||||
template<>
|
template<>
|
||||||
void FilterTable<LowpassFilterTable>::getParami(const ALfilter*, ALenum param, int*)
|
void FilterTable<LowpassFilterTable>::getParami(ALCcontext *context, const ALfilter*, ALenum param, int*)
|
||||||
{ throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
|
{ 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)
|
void FilterTable<LowpassFilterTable>::getParamiv(ALCcontext *context, const ALfilter *filter, ALenum param, int *values)
|
||||||
{ getParami(filter, param, 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)
|
void FilterTable<LowpassFilterTable>::getParamfv(ALCcontext *context, const ALfilter *filter, ALenum param, float *vals)
|
||||||
{ getParamf(filter, param, vals); }
|
{ getParamf(context, filter, param, vals); }
|
||||||
|
|
||||||
/* Highpass parameter handlers */
|
/* Highpass parameter handlers */
|
||||||
template<>
|
template<>
|
||||||
void FilterTable<HighpassFilterTable>::setParami(ALfilter*, ALenum param, int)
|
void FilterTable<HighpassFilterTable>::setParami(ALCcontext *context, ALfilter*, ALenum param, int)
|
||||||
{ throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
|
{ 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)
|
void FilterTable<HighpassFilterTable>::setParamiv(ALCcontext *context, ALfilter *filter, ALenum param, const int *values)
|
||||||
{ setParami(filter, param, *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)
|
void FilterTable<HighpassFilterTable>::setParamfv(ALCcontext *context, ALfilter *filter, ALenum param, const float *vals)
|
||||||
{ setParamf(filter, param, *vals); }
|
{ setParamf(context, filter, param, *vals); }
|
||||||
template<>
|
template<>
|
||||||
void FilterTable<HighpassFilterTable>::getParami(const ALfilter*, ALenum param, int*)
|
void FilterTable<HighpassFilterTable>::getParami(ALCcontext *context, const ALfilter*, ALenum param, int*)
|
||||||
{ throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
|
{ 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)
|
void FilterTable<HighpassFilterTable>::getParamiv(ALCcontext *context, const ALfilter *filter, ALenum param, int *values)
|
||||||
{ getParami(filter, param, 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)
|
void FilterTable<HighpassFilterTable>::getParamfv(ALCcontext *context, const ALfilter *filter, ALenum param, float *vals)
|
||||||
{ getParamf(filter, param, vals); }
|
{ getParamf(context, filter, param, vals); }
|
||||||
|
|
||||||
/* Bandpass parameter handlers */
|
/* Bandpass parameter handlers */
|
||||||
template<>
|
template<>
|
||||||
void FilterTable<BandpassFilterTable>::setParami(ALfilter*, ALenum param, int)
|
void FilterTable<BandpassFilterTable>::setParami(ALCcontext *context, ALfilter*, ALenum param, int)
|
||||||
{ throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
|
{ 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)
|
void FilterTable<BandpassFilterTable>::setParamiv(ALCcontext *context, ALfilter *filter, ALenum param, const int *values)
|
||||||
{ setParami(filter, param, *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)
|
void FilterTable<BandpassFilterTable>::setParamfv(ALCcontext *context, ALfilter *filter, ALenum param, const float *vals)
|
||||||
{ setParamf(filter, param, *vals); }
|
{ setParamf(context, filter, param, *vals); }
|
||||||
template<>
|
template<>
|
||||||
void FilterTable<BandpassFilterTable>::getParami(const ALfilter*, ALenum param, int*)
|
void FilterTable<BandpassFilterTable>::getParami(ALCcontext *context, const ALfilter*, ALenum param, int*)
|
||||||
{ throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
|
{ 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)
|
void FilterTable<BandpassFilterTable>::getParamiv(ALCcontext *context, const ALfilter *filter, ALenum param, int *values)
|
||||||
{ getParami(filter, param, 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)
|
void FilterTable<BandpassFilterTable>::getParamfv(ALCcontext *context, const ALfilter *filter, ALenum param, float *vals)
|
||||||
{ getParamf(filter, param, 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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> filterlock{device->FilterLock};
|
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,
|
context->throw_error(AL_OUT_OF_MEMORY, "Failed to allocate {} filter{}", n,
|
||||||
(n == 1) ? "" : "s"};
|
(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) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> filterlock{device->FilterLock};
|
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) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> filterlock{device->FilterLock};
|
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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> filterlock{device->FilterLock};
|
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);},
|
std::visit([context,alfilt,param,value](auto&& thunk)
|
||||||
alfilt->mTypeVariant);
|
{ thunk.setParami(context, alfilt, param, value); }, alfilt->mTypeVariant);
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> filterlock{device->FilterLock};
|
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);},
|
std::visit([context,alfilt,param,values](auto&& thunk)
|
||||||
alfilt->mTypeVariant);
|
{ thunk.setParamiv(context, alfilt, param, values); }, alfilt->mTypeVariant);
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> filterlock{device->FilterLock};
|
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);},
|
std::visit([context,alfilt,param,value](auto&& thunk)
|
||||||
alfilt->mTypeVariant);
|
{ thunk.setParamf(context, alfilt, param, value); }, alfilt->mTypeVariant);
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> filterlock{device->FilterLock};
|
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);},
|
std::visit([context,alfilt,param,values](auto&& thunk)
|
||||||
alfilt->mTypeVariant);
|
{ thunk.setParamfv(context, alfilt, param, values); }, alfilt->mTypeVariant);
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> filterlock{device->FilterLock};
|
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:
|
case AL_FILTER_TYPE: *value = alfilt->type; return;
|
||||||
*value = alfilt->type;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Call the appropriate handler */
|
/* Call the appropriate handler */
|
||||||
std::visit([alfilt,param,value](auto&& thunk){thunk.getParami(alfilt, param, value);},
|
std::visit([context,alfilt,param,value](auto&& thunk)
|
||||||
alfilt->mTypeVariant);
|
{ thunk.getParami(context, alfilt, param, value); }, alfilt->mTypeVariant);
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> filterlock{device->FilterLock};
|
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);},
|
std::visit([context,alfilt,param,values](auto&& thunk)
|
||||||
alfilt->mTypeVariant);
|
{ thunk.getParamiv(context, alfilt, param, values); }, alfilt->mTypeVariant);
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> filterlock{device->FilterLock};
|
auto filterlock = std::lock_guard{device->FilterLock};
|
||||||
|
|
||||||
const ALfilter *alfilt{LookupFilter(device, filter)};
|
const ALfilter *alfilt{LookupFilter(device, filter)};
|
||||||
if(!alfilt) UNLIKELY
|
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.getParamf(alfilt, param, value);},
|
std::visit([context,alfilt,param,value](auto&& thunk)
|
||||||
alfilt->mTypeVariant);
|
{ thunk.getParamf(context, alfilt, param, value); }, alfilt->mTypeVariant);
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> filterlock{device->FilterLock};
|
auto filterlock = std::lock_guard{device->FilterLock};
|
||||||
|
|
||||||
const ALfilter *alfilt{LookupFilter(device, filter)};
|
const ALfilter *alfilt{LookupFilter(device, filter)};
|
||||||
if(!alfilt) UNLIKELY
|
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.getParamfv(alfilt, param, values);},
|
std::visit([context,alfilt,param,values](auto&& thunk)
|
||||||
alfilt->mTypeVariant);
|
{ thunk.getParamfv(context, alfilt, param, values); }, alfilt->mTypeVariant);
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
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()};
|
auto *device = context->mALDevice.get();
|
||||||
std::lock_guard<std::mutex> filterlock{device->FilterLock};
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 setParami(ALCcontext*, ALfilter*, ALenum, int);
|
||||||
static void setParamiv(struct ALfilter*, ALenum, const int*);
|
static void setParamiv(ALCcontext*, ALfilter*, ALenum, const int*);
|
||||||
static void setParamf(struct ALfilter*, ALenum, float);
|
static void setParamf(ALCcontext*, ALfilter*, ALenum, float);
|
||||||
static void setParamfv(struct ALfilter*, ALenum, const float*);
|
static void setParamfv(ALCcontext*, ALfilter*, ALenum, const float*);
|
||||||
|
|
||||||
static void getParami(const struct ALfilter*, ALenum, int*);
|
static void getParami(ALCcontext*, const ALfilter*, ALenum, int*);
|
||||||
static void getParamiv(const struct ALfilter*, ALenum, int*);
|
static void getParamiv(ALCcontext*, const ALfilter*, ALenum, int*);
|
||||||
static void getParamf(const struct ALfilter*, ALenum, float*);
|
static void getParamf(ALCcontext*, const ALfilter*, ALenum, float*);
|
||||||
static void getParamfv(const struct 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> { };
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
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::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
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) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
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",
|
context->throw_error(AL_INVALID_ENUM, "Invalid listener integer-vector property {:#04x}",
|
||||||
param};
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
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) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
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::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
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) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
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::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
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",
|
context->throw_error(AL_INVALID_ENUM, "Invalid listener integer-vector property {:#04x}",
|
||||||
param};
|
as_unsigned(param));
|
||||||
}
|
}
|
||||||
catch(al::context_error& e) {
|
catch(al::base_exception&) {
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
|
catch(std::exception &e) {
|
||||||
|
ERR("Caught exception: {}", e.what());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -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.
|
EAXBUFFER_REVERBPROPERTIES i; // Immediate.
|
||||||
Eax1Props d; // Deferred.
|
EAXBUFFER_REVERBPROPERTIES d; // Deferred.
|
||||||
};
|
};
|
||||||
|
|
||||||
using Eax2Props = EAX20BUFFERPROPERTIES;
|
|
||||||
struct Eax2State {
|
struct Eax2State {
|
||||||
Eax2Props i; // Immediate.
|
EAX20BUFFERPROPERTIES i; // Immediate.
|
||||||
Eax2Props d; // Deferred.
|
EAX20BUFFERPROPERTIES d; // Deferred.
|
||||||
};
|
};
|
||||||
|
|
||||||
using Eax3Props = EAX30SOURCEPROPERTIES;
|
|
||||||
struct Eax3State {
|
struct Eax3State {
|
||||||
Eax3Props i; // Immediate.
|
EAX30SOURCEPROPERTIES i; // Immediate.
|
||||||
Eax3Props d; // Deferred.
|
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);
|
||||||
|
|
@ -823,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;
|
||||||
|
|
@ -840,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 eax1_translate(const EAXBUFFER_REVERBPROPERTIES& src, Eax5Props& dst) noexcept;
|
||||||
static void eax2_translate(const Eax2Props& src, Eax5Props& dst) noexcept;
|
static void eax2_translate(const EAX20BUFFERPROPERTIES& src, Eax5Props& dst) noexcept;
|
||||||
static void eax3_translate(const Eax3Props& 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(
|
||||||
|
|
@ -907,12 +904,12 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
static void eax_get_active_fx_slot_id(const EaxCall& call, const al::span<const GUID> src_ids);
|
static void eax_get_active_fx_slot_id(const EaxCall& call, const al::span<const GUID> src_ids);
|
||||||
static void eax1_get(const EaxCall& call, const Eax1Props& props);
|
static void eax1_get(const EaxCall& call, const EAXBUFFER_REVERBPROPERTIES& props);
|
||||||
static void eax2_get(const EaxCall& call, const Eax2Props& props);
|
static void eax2_get(const EaxCall& call, const EAX20BUFFERPROPERTIES& props);
|
||||||
static void eax3_get_obstruction(const EaxCall& call, const Eax3Props& props);
|
static void eax3_get_obstruction(const EaxCall& call, const EAX30SOURCEPROPERTIES& props);
|
||||||
static void eax3_get_occlusion(const EaxCall& call, const Eax3Props& props);
|
static void eax3_get_occlusion(const EaxCall& call, const EAX30SOURCEPROPERTIES& props);
|
||||||
static void eax3_get_exclusion(const EaxCall& call, const Eax3Props& props);
|
static void eax3_get_exclusion(const EaxCall& call, const EAX30SOURCEPROPERTIES& props);
|
||||||
static void eax3_get(const EaxCall& call, const Eax3Props& 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);
|
||||||
|
|
@ -1034,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 eax1_set(const EaxCall& call, EAXBUFFER_REVERBPROPERTIES& props);
|
||||||
static void eax2_set(const EaxCall& call, Eax2Props& props);
|
static void eax2_set(const EaxCall& call, EAX20BUFFERPROPERTIES& props);
|
||||||
void eax3_set(const EaxCall& call, Eax3Props& 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);
|
||||||
|
|
|
||||||
|
|
@ -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
|
#if ALSOFT_EAX
|
||||||
#include "alc/device.h"
|
|
||||||
|
|
||||||
#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"; }
|
||||||
|
|
@ -94,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)
|
||||||
{
|
{
|
||||||
|
|
@ -108,6 +113,8 @@ const ALchar *GetResamplerName(const Resampler rtype)
|
||||||
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. */
|
||||||
|
|
@ -144,24 +151,24 @@ constexpr auto ALenumFromDistanceModel(DistanceModel model) -> ALenum
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PropertyValue : ALenum {
|
enum PropertyValue : ALenum {
|
||||||
DopplerFactor = AL_DOPPLER_FACTOR,
|
DopplerFactorProp = AL_DOPPLER_FACTOR,
|
||||||
DopplerVelocity = AL_DOPPLER_VELOCITY,
|
DopplerVelocityProp = AL_DOPPLER_VELOCITY,
|
||||||
DistanceModel = AL_DISTANCE_MODEL,
|
DistanceModelProp = AL_DISTANCE_MODEL,
|
||||||
SpeedOfSound = AL_SPEED_OF_SOUND,
|
SpeedOfSoundProp = AL_SPEED_OF_SOUND,
|
||||||
DeferredUpdates = AL_DEFERRED_UPDATES_SOFT,
|
DeferredUpdatesProp = AL_DEFERRED_UPDATES_SOFT,
|
||||||
GainLimit = AL_GAIN_LIMIT_SOFT,
|
GainLimitProp = AL_GAIN_LIMIT_SOFT,
|
||||||
NumResamplers = AL_NUM_RESAMPLERS_SOFT,
|
NumResamplersProp = AL_NUM_RESAMPLERS_SOFT,
|
||||||
DefaultResampler = AL_DEFAULT_RESAMPLER_SOFT,
|
DefaultResamplerProp = AL_DEFAULT_RESAMPLER_SOFT,
|
||||||
DebugLoggedMessages = AL_DEBUG_LOGGED_MESSAGES_EXT,
|
DebugLoggedMessagesProp = AL_DEBUG_LOGGED_MESSAGES_EXT,
|
||||||
DebugNextLoggedMessageLength = AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT,
|
DebugNextLoggedMessageLengthProp = AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT,
|
||||||
MaxDebugMessageLength = AL_MAX_DEBUG_MESSAGE_LENGTH_EXT,
|
MaxDebugMessageLengthProp = AL_MAX_DEBUG_MESSAGE_LENGTH_EXT,
|
||||||
MaxDebugLoggedMessages = AL_MAX_DEBUG_LOGGED_MESSAGES_EXT,
|
MaxDebugLoggedMessagesProp = AL_MAX_DEBUG_LOGGED_MESSAGES_EXT,
|
||||||
MaxDebugGroupDepth = AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT,
|
MaxDebugGroupDepthProp = AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT,
|
||||||
MaxLabelLength = AL_MAX_LABEL_LENGTH_EXT,
|
MaxLabelLengthProp = AL_MAX_LABEL_LENGTH_EXT,
|
||||||
ContextFlags = AL_CONTEXT_FLAGS_EXT,
|
ContextFlagsProp = AL_CONTEXT_FLAGS_EXT,
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
EaxRamSize = AL_EAX_RAM_SIZE,
|
EaxRamSizeProp = AL_EAX_RAM_SIZE,
|
||||||
EaxRamFree = AL_EAX_RAM_FREE,
|
EaxRamFreeProp = AL_EAX_RAM_FREE,
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -259,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:
|
||||||
|
|
@ -268,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:
|
||||||
|
|
@ -279,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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -331,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)
|
||||||
|
|
@ -355,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)
|
||||||
|
|
@ -369,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); \
|
||||||
|
|
@ -384,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,)
|
AL_API DECL_GETFUNC(ALboolean, alGetBoolean,)
|
||||||
DECL_GETFUNC(ALdouble, alGetDouble,)
|
AL_API DECL_GETFUNC(ALdouble, alGetDouble,)
|
||||||
DECL_GETFUNC(ALfloat, alGetFloat,)
|
AL_API DECL_GETFUNC(ALfloat, alGetFloat,)
|
||||||
DECL_GETFUNC(ALint, alGetInteger,)
|
AL_API DECL_GETFUNC(ALint, alGetInteger,)
|
||||||
|
|
||||||
DECL_GETFUNC(ALint64SOFT, alGetInteger64,SOFT)
|
DECL_GETFUNC(ALvoidptr, alGetPointer,EXT)
|
||||||
DECL_GETFUNC(ALvoid*, alGetPointer,SOFT)
|
AL_API DECL_GETFUNC(ALint64SOFT, alGetInteger64,SOFT)
|
||||||
|
AL_API DECL_GETFUNC(ALvoidptr, alGetPointer,SOFT)
|
||||||
|
|
||||||
#undef DECL_GETFUNC
|
#undef DECL_GETFUNC
|
||||||
|
|
||||||
|
|
@ -442,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");
|
||||||
|
|
@ -464,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)
|
||||||
|
|
@ -472,9 +488,18 @@ FORCE_ALIGN const ALchar* AL_APIENTRY alGetStringDirect(ALCcontext *context, ALe
|
||||||
{
|
{
|
||||||
switch(pname)
|
switch(pname)
|
||||||
{
|
{
|
||||||
case AL_VENDOR: return GetVendorString();
|
case AL_VENDOR:
|
||||||
case AL_VERSION: return GetVersionString();
|
if(auto device = context->mALDevice.get(); !device->mVendorOverride.empty())
|
||||||
case AL_RENDERER: return GetRendererString();
|
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();
|
||||||
|
|
@ -485,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -493,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};
|
||||||
|
|
@ -506,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};
|
||||||
|
|
@ -526,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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -551,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -567,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};
|
||||||
|
|
@ -611,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;
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -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);
|
||||||
|
|
@ -259,7 +259,7 @@ void LoadConfigFromFile(std::istream &f)
|
||||||
|
|
||||||
if(valpart.size() > size_t{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,
|
auto GetConfigValue(const std::string_view devName, const std::string_view blockName,
|
||||||
const std::string_view keyName)
|
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 iter->value;
|
||||||
return nullptr;
|
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());
|
TRACE("Loading config {}...", al::u8_as_char(path.u8string()));
|
||||||
if(std::ifstream f{path}; f.is_open())
|
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";
|
path /= L"alsoft.ini";
|
||||||
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(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());
|
TRACE("Loading config {}...", al::u8_as_char(path.u8string()));
|
||||||
if(std::ifstream f{path}; f.is_open())
|
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());
|
TRACE("Loading config {}...", al::u8_as_char(path.u8string()));
|
||||||
if(std::ifstream f{path}; f.is_open())
|
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());
|
TRACE("Loading config {}...", al::u8_as_char(path.u8string()));
|
||||||
if(std::ifstream f{path}; f.is_open())
|
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,
|
auto ConfigValueStr(const std::string_view devName, const std::string_view blockName,
|
||||||
const std::string_view blockName, const std::string_view keyName)
|
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,
|
auto ConfigValueInt(const std::string_view devName, const std::string_view blockName,
|
||||||
const std::string_view keyName)
|
const std::string_view keyName) -> std::optional<int>
|
||||||
{
|
{
|
||||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try {
|
||||||
return static_cast<int>(std::strtol(val, nullptr, 0));
|
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,
|
auto ConfigValueUInt(const std::string_view devName, const std::string_view blockName,
|
||||||
const std::string_view blockName, const std::string_view keyName)
|
const std::string_view keyName) -> std::optional<unsigned int>
|
||||||
{
|
{
|
||||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try {
|
||||||
return static_cast<unsigned int>(std::strtoul(val, nullptr, 0));
|
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,
|
auto ConfigValueFloat(const std::string_view devName, const std::string_view blockName,
|
||||||
const std::string_view blockName, const std::string_view keyName)
|
const std::string_view keyName) -> std::optional<float>
|
||||||
{
|
{
|
||||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try {
|
||||||
return std::strtof(val, nullptr);
|
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,
|
auto ConfigValueBool(const std::string_view devName, const std::string_view blockName,
|
||||||
const std::string_view blockName, const std::string_view keyName)
|
const std::string_view keyName) -> std::optional<bool>
|
||||||
{
|
{
|
||||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try {
|
||||||
return al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
|
return al::case_compare(val, "on"sv) == 0 || al::case_compare(val, "yes"sv) == 0
|
||||||
|| al::strcasecmp(val, "true") == 0 || atoi(val) != 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,
|
auto GetConfigValueBool(const std::string_view devName, const std::string_view blockName,
|
||||||
const std::string_view keyName, bool def)
|
const std::string_view keyName, bool def) -> bool
|
||||||
{
|
{
|
||||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try {
|
||||||
return al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
|
return al::case_compare(val, "on"sv) == 0 || al::case_compare(val, "yes"sv) == 0
|
||||||
|| al::strcasecmp(val, "true") == 0 || atoi(val) != 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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 <cmath>
|
||||||
#include <climits>
|
|
||||||
#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;
|
||||||
|
|
@ -143,11 +146,11 @@ 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
|
||||||
|
|
@ -186,60 +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::Spline:
|
case Resampler::Spline:
|
||||||
case Resampler::Gaussian:
|
case Resampler::Gaussian:
|
||||||
#ifdef HAVE_NEON
|
#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
|
||||||
|
|
@ -283,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);
|
||||||
}
|
}
|
||||||
|
|
@ -334,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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -434,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;
|
||||||
|
|
@ -462,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;
|
||||||
}
|
/* If this effect slot's Auxiliary Send Auto is off, don't apply the
|
||||||
else
|
* automatic send adjustments based on source distance.
|
||||||
{
|
*/
|
||||||
slot->RoomRolloff = 0.0f;
|
if(slot->AuxSendAuto)
|
||||||
slot->DecayTime = 0.0f;
|
{
|
||||||
slot->DecayLFRatio = 0.0f;
|
slot->DecayTime = reverbprops->DecayTime;
|
||||||
slot->DecayHFRatio = 0.0f;
|
slot->DecayLFRatio = reverbprops->DecayLFRatio;
|
||||||
slot->DecayHFLimit = false;
|
slot->DecayHFRatio = reverbprops->DecayHFRatio;
|
||||||
slot->AirAbsorptionGainHF = 1.0f;
|
slot->DecayHFLimit = reverbprops->DecayHFLimit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EffectState *state{props->State.release()};
|
EffectState *state{props->State.release()};
|
||||||
|
|
@ -493,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);
|
||||||
}
|
}
|
||||||
|
|
@ -686,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,
|
static constexpr auto P = [](const int i, const int l, const int a, const int n,
|
||||||
const AmbiRotateMatrix &R)
|
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)]};
|
||||||
|
|
@ -701,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;
|
||||||
|
|
@ -722,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);
|
||||||
|
|
@ -836,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()};
|
||||||
|
|
@ -1183,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)};
|
||||||
|
|
@ -1194,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),
|
||||||
|
|
@ -1310,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)};
|
if(Device->Dry.Buffer.data() == Device->RealOut.Buffer.data())
|
||||||
|
|
||||||
/* Special-case LFE */
|
|
||||||
if(chans[c].channel == LFE)
|
|
||||||
{
|
{
|
||||||
if(Device->Dry.Buffer.data() == Device->RealOut.Buffer.data())
|
const auto idx = uint{Device->channelIdxByName(chans[c].channel)};
|
||||||
{
|
if(idx != InvalidChannelIndex)
|
||||||
const uint idx{Device->channelIdxByName(chans[c].channel)};
|
voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base * pangain;
|
||||||
if(idx != InvalidChannelIndex)
|
|
||||||
voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base
|
|
||||||
* pangain;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* Warp the channel position toward the source position as
|
/* Warp the channel position toward the source position as the
|
||||||
* the spread decreases. With no spread, all channels are
|
* spread decreases. With no spread, all channels are at the
|
||||||
* at the source position, at full spread (pi*2), each
|
* source position, at full spread (pi*2), each channel
|
||||||
* channel position is left unchanged.
|
* position is left unchanged.
|
||||||
*/
|
*/
|
||||||
const float a{1.0f - (inv_pi_v<float>/2.0f)*Spread};
|
const auto a = 1.0f - (al::numbers::inv_pi_v<float>/2.0f)*Spread;
|
||||||
std::array pos{
|
auto pos = std::array{
|
||||||
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),
|
||||||
lerpf(chans[c].pos[2], zpos, 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])};
|
const auto len = std::sqrt(pos[0]*pos[0] + pos[1]*pos[1] + pos[2]*pos[2]);
|
||||||
if(len < 1.0f)
|
if(len < 1.0f)
|
||||||
{
|
{
|
||||||
pos[0] /= len;
|
pos[0] /= len;
|
||||||
pos[1] /= len;
|
pos[1] /= len;
|
||||||
pos[2] /= len;
|
pos[2] /= len;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Device->mRenderMode == RenderMode::Pairwise)
|
if(Device->mRenderMode == RenderMode::Pairwise)
|
||||||
pos = ScaleAzimuthFront3(pos);
|
pos = ScaleAzimuthFront3(pos);
|
||||||
const auto coeffs = CalcDirectionCoeffs(pos, 0.0f);
|
const auto coeffs = CalcDirectionCoeffs(pos, 0.0f);
|
||||||
|
|
||||||
ComputePanGains(&Device->Dry, coeffs, DryGain.Base * pangain,
|
ComputePanGains(&Device->Dry, coeffs, DryGain.Base * pangain,
|
||||||
voice->mChans[c].mDryParams.Gains.Target);
|
voice->mChans[c].mDryParams.Gains.Target);
|
||||||
for(uint i{0};i < NumSends;i++)
|
for(uint i{0};i < NumSends;i++)
|
||||||
{
|
{
|
||||||
if(const EffectSlot *Slot{SendSlots[i]})
|
if(const EffectSlot *Slot{SendSlots[i]})
|
||||||
ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base * pangain,
|
ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base * pangain,
|
||||||
voice->mChans[c].mWetParams[i].Gains.Target);
|
voice->mChans[c].mWetParams[i].Gains.Target);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1473,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
|
||||||
|
|
@ -1509,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) */
|
||||||
|
|
@ -1670,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};
|
/* FIXME: In keeping with EAX, the base air absorption gain should be
|
||||||
const float distance_meters{distance_base * context->mParams.MetersPerUnit};
|
* taken from the reverb property in the "primary fx slot" when it has
|
||||||
const float dryabsorb{distance_meters * props->AirAbsorptionFactor};
|
* a reverb effect and the environment flag set, and be applied to the
|
||||||
if(dryabsorb > std::numeric_limits<float>::epsilon())
|
* direct path and all environment sends, rather than each path using
|
||||||
DryGain.HF *= std::pow(context->mParams.AirAbsorptionGainHF, dryabsorb);
|
* 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.
|
||||||
|
|
@ -1701,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())
|
if(SendSlots[i]->AirAbsorptionGainHF < 1.0f
|
||||||
WetGain[i].HF *= std::pow(SendSlots[i]->AirAbsorptionGainHF, distance_meters);
|
&& absorb > std::numeric_limits<float>::epsilon())
|
||||||
|
WetGain[i].HF *= std::pow(SendSlots[i]->AirAbsorptionGainHF, absorb);
|
||||||
/* 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;
|
|
||||||
|
|
||||||
const float DecayDistance{SendSlots[i]->DecayTime * SpeedOfSoundMetersPerSec};
|
const float DecayDistance{SendSlots[i]->DecayTime * SpeedOfSoundMetersPerSec};
|
||||||
|
|
||||||
|
|
@ -1726,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;
|
||||||
}
|
}
|
||||||
|
|
@ -1771,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
|
||||||
|
|
@ -1814,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)
|
||||||
{
|
{
|
||||||
|
|
@ -1962,34 +1964,35 @@ void ProcessContexts(DeviceBase *device, const uint SamplesToDo)
|
||||||
{
|
{
|
||||||
ASSUME(SamplesToDo > 0);
|
ASSUME(SamplesToDo > 0);
|
||||||
|
|
||||||
const nanoseconds curtime{device->mClockBase.load(std::memory_order_relaxed) +
|
const auto curtime = device->getClockTime();
|
||||||
nanoseconds{seconds{device->mSamplesDone.load(std::memory_order_relaxed)}}/
|
|
||||||
device->Frequency};
|
|
||||||
|
|
||||||
for(ContextBase *ctx : *device->mContexts.load(std::memory_order_acquire))
|
auto proc_context = [SamplesToDo,curtime](ContextBase *ctx)
|
||||||
{
|
{
|
||||||
const auto auxslotspan = al::span{*ctx->mActiveAuxSlots.load(std::memory_order_acquire)};
|
const auto auxslotspan = al::span{*ctx->mActiveAuxSlots.load(std::memory_order_acquire)};
|
||||||
const auto auxslots = auxslotspan.first(auxslotspan.size()>>1);
|
const auto auxslots = auxslotspan.first(auxslotspan.size()>>1);
|
||||||
const auto sorted_slots = auxslotspan.last(auxslotspan.size()>>1);
|
const auto sorted_slots = auxslotspan.last(auxslotspan.size()>>1);
|
||||||
const al::span<Voice*> voices{ctx->getVoicesSpanAcquired()};
|
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, sorted_slots, 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)
|
auto clear_buffer = [](const FloatBufferSpan buffer)
|
||||||
buffer.fill(0.0f);
|
{ 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())
|
||||||
|
|
@ -2000,60 +2003,54 @@ void ProcessContexts(DeviceBase *device, const uint SamplesToDo)
|
||||||
*/
|
*/
|
||||||
if(!sorted_slots[0])
|
if(!sorted_slots[0])
|
||||||
{
|
{
|
||||||
/* First, copy the slots to the sorted list, then partition the
|
/* First, copy the slots to the sorted list and partition them,
|
||||||
* sorted list so that all slots without a target slot go to
|
* so that all slots without a target slot go to the end.
|
||||||
* the end.
|
|
||||||
*/
|
*/
|
||||||
std::copy(auxslots.begin(), auxslots.end(), sorted_slots.begin());
|
auto has_target = [](const EffectSlot *slot) noexcept -> bool
|
||||||
auto split_point = std::partition(sorted_slots.begin(), sorted_slots.end(),
|
{ return slot->Target != nullptr; };
|
||||||
[](const EffectSlot *slot) noexcept -> bool
|
auto split_point = std::partition_copy(auxslots.rbegin(), auxslots.rend(),
|
||||||
{ return slot->Target != nullptr; });
|
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
|
/* Starting from the back of the sorted list, continue
|
||||||
* all slots go right to the output, or the remaining one must
|
* partitioning the front of the list given each target until
|
||||||
* target an already-partitioned slot.
|
* 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
|
/* This shouldn't happen, but if there's unsorted slots
|
||||||
* back of the sorted list, continue partitioning the front
|
* left that don't target any sorted slots, they can't
|
||||||
* of the list given each target until all targets are
|
* contribute to the output, so leave them.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
auto next_target = sorted_slots.end();
|
if(next_target == split_point) UNLIKELY
|
||||||
do {
|
break;
|
||||||
/* 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;
|
--next_target;
|
||||||
split_point = std::partition(sorted_slots.begin(), split_point,
|
auto not_next = [next_target](const EffectSlot *slot) noexcept -> bool
|
||||||
[next_target](const EffectSlot *slot) noexcept -> bool
|
{ return slot->Target != *next_target; };
|
||||||
{ return slot->Target != *next_target; });
|
split_point = std::partition(sorted_slots.begin(), split_point, not_next);
|
||||||
} while(split_point - sorted_slots.begin() > 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(RingBuffer *ring{ctx->mAsyncEvents.get()}; ring->readSpace() > 0)
|
||||||
if(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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2152,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}
|
/* Some Clang versions don't like calling subspan on an rvalue here. */
|
||||||
.subspan(Offset*FrameStep);
|
const auto output_ = al::span{static_cast<T*>(OutBuffer), (Offset+SamplesToDo)*FrameStep};
|
||||||
size_t c{0};
|
const auto output = output_.subspan(Offset*FrameStep);
|
||||||
for(const FloatBufferLine &inbuf : InBuffer)
|
|
||||||
|
/* 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();
|
const auto src = al::span{srcbuf}.first(SamplesToDo);
|
||||||
auto conv_sample = [FrameStep,c,&out](const float s) noexcept
|
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);
|
||||||
};
|
*out = SampleConv<T>(s);
|
||||||
std::for_each_n(inbuf.cbegin(), SamplesToDo, conv_sample);
|
});
|
||||||
++c;
|
|
||||||
}
|
}
|
||||||
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);
|
const auto src = al::span{*srcbuf}.first(SamplesToDo);
|
||||||
for(size_t i{0};i < SamplesToDo;++i)
|
/* Some Clang versions don't like calling subspan on an rvalue here. */
|
||||||
std::fill_n(&output[i*FrameStep + c], extra, silence);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2195,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) +
|
auto clockBaseSec = mClockBaseSec.load(std::memory_order_relaxed) +
|
||||||
std::chrono::seconds{samplesDone/Frequency};
|
seconds32{samplesDone/mSampleRate};
|
||||||
mSamplesDone.store(samplesDone%Frequency, std::memory_order_relaxed);
|
mSamplesDone.store(samplesDone%mSampleRate, std::memory_order_relaxed);
|
||||||
mClockBase.store(clockBase, 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
|
||||||
|
|
@ -2207,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)
|
||||||
|
|
@ -2222,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};
|
||||||
|
|
@ -2230,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();
|
switch(FmtType)
|
||||||
for(auto *dstbuf : outBuffers)
|
|
||||||
{
|
{
|
||||||
const auto dst = al::span{dstbuf, numSamples}.subspan(total);
|
#define HANDLE_WRITE(T) case T: \
|
||||||
std::copy_n(srcbuf->cbegin(), samplesToDo, dst.begin());
|
Write<DevFmtType_t<T>>(RealOut.Buffer, outBuffers, total, samplesToDo); break;
|
||||||
++srcbuf;
|
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;
|
||||||
}
|
}
|
||||||
|
|
@ -2274,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();
|
||||||
|
|
||||||
|
|
@ -2282,29 +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);
|
||||||
|
disconnect.msg = std::move(msg);
|
||||||
/* NOLINTBEGIN(*-array-to-pointer-decay) */
|
|
||||||
va_list args, args2;
|
|
||||||
va_start(args, msg);
|
|
||||||
va_copy(args2, args);
|
|
||||||
if(int msglen{vsnprintf(nullptr, 0, msg, args)}; msglen > 0)
|
|
||||||
{
|
|
||||||
disconnect.msg.resize(static_cast<uint>(msglen)+1_uz);
|
|
||||||
vsnprintf(disconnect.msg.data(), disconnect.msg.size(), msg, args2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
disconnect.msg = "<failed constructing message>";
|
|
||||||
va_end(args2);
|
|
||||||
va_end(args);
|
|
||||||
/* NOLINTEND(*-array-to-pointer-decay) */
|
|
||||||
|
|
||||||
while(!disconnect.msg.empty() && disconnect.msg.back() == '\0')
|
|
||||||
disconnect.msg.pop_back();
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@ struct EffectSlot;
|
||||||
|
|
||||||
enum class StereoEncoding : std::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 */
|
||||||
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)};
|
const auto spec = std::string_view{*customdevs}.substr(curpos, nextpos-curpos);
|
||||||
ERR("Invalid ALSA device specification \"%s\"\n", spec.c_str());
|
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 = fmt::format("{}-{}-{}", prefix_name(stream), cardid, dev);
|
||||||
name += '-';
|
const auto device_prefix = std::string{ConfigValueStr({}, "alsa"sv, name)
|
||||||
name += cardid;
|
|
||||||
name += '-';
|
|
||||||
name += std::to_string(dev);
|
|
||||||
const std::string device_prefix{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 = fmt::format("{}, {} (CARD={},DEV={})", cardname, snd_pcm_info_get_name(pcminfo),
|
||||||
name += ", ";
|
cardid, dev);
|
||||||
name += snd_pcm_info_get_name(pcminfo);
|
|
||||||
name += " (CARD=";
|
|
||||||
name += cardid;
|
|
||||||
name += ",DEV=";
|
|
||||||
name += std::to_string(dev);
|
|
||||||
name += ')';
|
|
||||||
|
|
||||||
/* "devprefixCARD=cardid,DEV=dev" */
|
/* "devprefixCARD=cardid,DEV=dev" */
|
||||||
std::string device{device_prefix};
|
auto device = fmt::format("{}CARD={},DEV={}", device_prefix, cardid, dev);
|
||||||
device += "CARD=";
|
|
||||||
device += cardid;
|
|
||||||
device += ",DEV=";
|
|
||||||
device += std::to_string(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;
|
||||||
}
|
}
|
||||||
|
|
@ -470,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();
|
||||||
|
|
@ -507,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 update_size{mDevice->mUpdateSize};
|
||||||
const snd_pcm_uframes_t buffer_size{mDevice->BufferSize};
|
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));
|
ERR("Invalid state detected: {}", snd_strerror(state));
|
||||||
mDevice->handleDisconnect("Bad state: %s", 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;
|
||||||
}
|
}
|
||||||
|
|
@ -542,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;
|
||||||
|
|
@ -563,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -574,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;
|
||||||
}
|
}
|
||||||
|
|
@ -591,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 update_size{mDevice->mUpdateSize};
|
||||||
const snd_pcm_uframes_t buffer_size{mDevice->BufferSize};
|
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));
|
ERR("Invalid state detected: {}", snd_strerror(state));
|
||||||
mDevice->handleDisconnect("Bad state: %s", 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;
|
||||||
}
|
}
|
||||||
|
|
@ -624,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -686,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
|
||||||
|
|
@ -695,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;
|
||||||
|
|
@ -709,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()
|
||||||
|
|
@ -740,15 +724,15 @@ bool AlsaPlayback::reset()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool allowmmap{GetConfigValueBool(mDevice->DeviceName, "alsa"sv, "mmap"sv, true)};
|
bool allowmmap{GetConfigValueBool(mDevice->mDeviceName, "alsa"sv, "mmap"sv, true)};
|
||||||
uint periodLen{static_cast<uint>(mDevice->UpdateSize * 1000000_u64 / mDevice->Frequency)};
|
uint periodLen{static_cast<uint>(mDevice->mUpdateSize * 1000000_u64 / mDevice->mSampleRate)};
|
||||||
uint bufferLen{static_cast<uint>(mDevice->BufferSize * 1000000_u64 / mDevice->Frequency)};
|
uint bufferLen{static_cast<uint>(mDevice->mBufferSize * 1000000_u64 / mDevice->mSampleRate)};
|
||||||
uint rate{mDevice->Frequency};
|
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()));
|
||||||
|
|
@ -798,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()));
|
||||||
|
|
||||||
|
|
@ -835,9 +819,9 @@ bool AlsaPlayback::reset()
|
||||||
#undef CHECK
|
#undef CHECK
|
||||||
sp = nullptr;
|
sp = nullptr;
|
||||||
|
|
||||||
mDevice->BufferSize = static_cast<uint>(bufferSizeInFrames);
|
mDevice->mBufferSize = static_cast<uint>(bufferSizeInFrames);
|
||||||
mDevice->UpdateSize = static_cast<uint>(periodSizeInFrames);
|
mDevice->mUpdateSize = static_cast<uint>(periodSizeInFrames);
|
||||||
mDevice->Frequency = rate;
|
mDevice->mSampleRate = rate;
|
||||||
|
|
||||||
setDefaultChannelOrder();
|
setDefaultChannelOrder();
|
||||||
|
|
||||||
|
|
@ -850,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()));
|
||||||
|
|
@ -861,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;
|
||||||
}
|
}
|
||||||
|
|
@ -874,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()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -891,7 +875,7 @@ 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()
|
||||||
|
|
@ -903,18 +887,18 @@ ClockLatency AlsaPlayback::getClockLatency()
|
||||||
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;
|
||||||
|
|
@ -954,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
|
||||||
|
|
@ -964,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();
|
||||||
|
|
@ -998,16 +982,16 @@ void AlsaCapture::open(std::string_view name)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
snd_pcm_uframes_t bufferSizeInFrames{std::max(mDevice->BufferSize,
|
snd_pcm_uframes_t bufferSizeInFrames{std::max(mDevice->mBufferSize,
|
||||||
100u*mDevice->Frequency/1000u)};
|
100u*mDevice->mSampleRate/1000u)};
|
||||||
snd_pcm_uframes_t periodSizeInFrames{std::min(mDevice->BufferSize,
|
snd_pcm_uframes_t periodSizeInFrames{std::min(mDevice->mBufferSize,
|
||||||
25u*mDevice->Frequency/1000u)};
|
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()));
|
||||||
|
|
@ -1018,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));
|
||||||
}
|
}
|
||||||
|
|
@ -1036,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;
|
||||||
|
|
@ -1073,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1109,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;
|
||||||
|
|
@ -1123,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);
|
ERR("restore error: {}", err);
|
||||||
mDevice->handleDisconnect("Capture recovery failure: %s", 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
|
||||||
|
|
@ -1149,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)
|
||||||
|
|
@ -1162,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);
|
ERR("restore error: {}", err);
|
||||||
mDevice->handleDisconnect("Capture recovery failure: %s", 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)};
|
snd_pcm_sframes_t amt{std::min(static_cast<snd_pcm_sframes_t>(vec[0].len), avail)};
|
||||||
amt = snd_pcm_readi(mPcmHandle, vec.first.buf, static_cast<snd_pcm_uframes_t>(amt));
|
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;
|
||||||
|
|
@ -1199,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);
|
ERR("restore error: {}", err);
|
||||||
mDevice->handleDisconnect("Capture recovery failure: %s", err);
|
mDevice->handleDisconnect("Capture recovery failure: {}", err);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
avail = amt;
|
avail = amt;
|
||||||
|
|
@ -1222,11 +1206,11 @@ ClockLatency AlsaCapture::getClockLatency()
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
@ -1236,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1256,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;
|
||||||
|
|
|
||||||
|
|
@ -3,26 +3,18 @@
|
||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#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
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -50,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 = std::chrono::seconds{mDevice->mBufferSize - mDevice->mUpdateSize};
|
||||||
ret.Latency /= mDevice->Frequency;
|
ret.Latency /= mDevice->mSampleRate;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,14 +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 <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;
|
||||||
|
|
@ -35,11 +35,12 @@ struct BackendBase {
|
||||||
virtual ClockLatency getClockLatency();
|
virtual ClockLatency getClockLatency();
|
||||||
|
|
||||||
DeviceBase *const mDevice;
|
DeviceBase *const mDevice;
|
||||||
|
std::string mDeviceName;
|
||||||
|
|
||||||
BackendBase() = delete;
|
BackendBase() = delete;
|
||||||
BackendBase(const BackendBase&) = delete;
|
BackendBase(const BackendBase&) = delete;
|
||||||
BackendBase(BackendBase&&) = delete;
|
BackendBase(BackendBase&&) = delete;
|
||||||
BackendBase(DeviceBase *device) noexcept : mDevice{device} { }
|
explicit BackendBase(DeviceBase *device) noexcept : mDevice{device} { }
|
||||||
virtual ~BackendBase() = default;
|
virtual ~BackendBase() = default;
|
||||||
|
|
||||||
void operator=(const BackendBase&) = delete;
|
void operator=(const BackendBase&) = delete;
|
||||||
|
|
@ -102,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__
|
template<typename ...Args>
|
||||||
[[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]]
|
backend_exception(backend_error code, fmt::format_string<Args...> fmt, Args&& ...args)
|
||||||
#else
|
: base_exception{make_string(fmt, fmt::make_format_args(args...))}, mErrorCode{code}
|
||||||
[[gnu::format(printf, 3, 4)]]
|
{ }
|
||||||
#endif
|
backend_exception(const backend_exception&) = default;
|
||||||
backend_exception(backend_error code, const char *msg, ...);
|
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; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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};
|
auto name = std::string{curitem->mName};
|
||||||
size_t count{1};
|
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 = fmt::format("{} #{}", curitem->mName, ++count);
|
||||||
name += " #";
|
|
||||||
name += std::to_string(++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",
|
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
|
||||||
al::sizei(name), name.data()};
|
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);
|
if(!devname.empty()) mDeviceName = std::move(devname);
|
||||||
else mDevice->DeviceName = "Unknown Device Name";
|
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->BufferSize = static_cast<uint>(mDevice->BufferSize*streamFormat.mSampleRate/
|
mDevice->mBufferSize = static_cast<uint>(mDevice->mBufferSize*streamFormat.mSampleRate/
|
||||||
mDevice->Frequency + 0.5);
|
mDevice->mSampleRate + 0.5);
|
||||||
mDevice->Frequency = static_cast<uint>(streamFormat.mSampleRate);
|
mDevice->mSampleRate = static_cast<uint>(streamFormat.mSampleRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: How to tell what channels are what in the output device, and how
|
struct ChannelMap {
|
||||||
* to specify what we're giving? e.g. 6.0 vs 5.1
|
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))
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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",
|
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
|
||||||
al::sizei(name), name.data()};
|
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{};
|
||||||
|
|
@ -825,13 +896,13 @@ void CoreAudioCapture::open(std::string_view name)
|
||||||
case DevFmtX7144:
|
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;
|
||||||
|
|
@ -851,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};
|
double srateScale{outputFormat.mSampleRate / mDevice->mSampleRate};
|
||||||
auto FrameCount64 = std::max(static_cast<uint64_t>(std::ceil(mDevice->BufferSize*srateScale)),
|
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);
|
||||||
|
|
@ -870,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);
|
||||||
|
|
||||||
|
|
@ -878,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)};
|
||||||
|
|
@ -894,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);
|
if(!devname.empty()) mDeviceName = std::move(devname);
|
||||||
else mDevice->DeviceName = "Unknown Device Name";
|
else mDeviceName = "Unknown Device Name";
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
mDevice->DeviceName = name;
|
mDeviceName = name;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -908,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)
|
||||||
|
|
@ -927,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};
|
const void *src0{rec_vec[0].buf};
|
||||||
auto src0len = static_cast<uint>(rec_vec.first.len);
|
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};
|
size_t total_read{rec_vec[0].len - src0len};
|
||||||
if(got < samples && !src0len && rec_vec.second.len > 0)
|
if(got < samples && !src0len && rec_vec[1].len > 0)
|
||||||
{
|
{
|
||||||
const void *src1{rec_vec.second.buf};
|
const void *src1{rec_vec[1].buf};
|
||||||
auto src1len = static_cast<uint>(rec_vec.second.len);
|
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);
|
||||||
|
|
|
||||||
|
|
@ -37,21 +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 "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"
|
||||||
|
|
||||||
|
|
@ -90,10 +89,7 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
#define DEVNAME_HEAD "OpenAL Soft on "
|
#if HAVE_DYNLOAD
|
||||||
|
|
||||||
|
|
||||||
#ifdef 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);
|
||||||
|
|
@ -146,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};
|
auto count = 1;
|
||||||
std::string newname{basename};
|
auto newname = basename;
|
||||||
while(checkName(devices, newname))
|
while(checkName(devices, newname))
|
||||||
{
|
newname = fmt::format("{} #{}", basename, ++count);
|
||||||
newname = basename;
|
const DevMap &newentry = devices.emplace_back(std::move(newname), *guid);
|
||||||
newname += " #";
|
|
||||||
newname += std::to_string(++count);
|
|
||||||
}
|
|
||||||
devices.emplace_back(std::move(newname), *guid);
|
|
||||||
const DevMap &newentry = devices.back();
|
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -172,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();
|
||||||
|
|
@ -215,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);
|
ERR("Failed to get buffer caps: {:#x}", as_unsigned(err));
|
||||||
mDevice->handleDisconnect("Failure retrieving playback buffer info: 0x%lx", 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};
|
||||||
|
|
@ -242,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);
|
ERR("Failed to play buffer: {:#x}", as_unsigned(err));
|
||||||
mDevice->handleDisconnect("Failure starting playback: 0x%lx", err);
|
mDevice->handleDisconnect("Failure starting playback: {:#x}",
|
||||||
|
as_unsigned(err));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
Playing = true;
|
Playing = true;
|
||||||
|
|
@ -251,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;
|
||||||
|
|
@ -264,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))
|
||||||
{
|
{
|
||||||
|
|
@ -274,22 +267,19 @@ FORCE_ALIGN int DSoundPlayback::mixerProc()
|
||||||
&WritePtr2, &WriteCnt2, 0);
|
&WritePtr2, &WriteCnt2, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(FAILED(err))
|
||||||
if(SUCCEEDED(err))
|
|
||||||
{
|
{
|
||||||
mDevice->renderSamples(WritePtr1, WriteCnt1/FrameSize, FrameStep);
|
ERR("Buffer lock error: {:#x}", as_unsigned(err));
|
||||||
if(WriteCnt2 > 0)
|
mDevice->handleDisconnect("Failed to lock output buffer: {:#x}", as_unsigned(err));
|
||||||
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);
|
|
||||||
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;
|
||||||
|
|
@ -307,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};
|
||||||
|
|
@ -329,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;
|
||||||
}
|
}
|
||||||
|
|
@ -348,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",
|
throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: {:#x}",
|
||||||
hr};
|
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()
|
||||||
|
|
@ -391,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))
|
||||||
|
|
@ -407,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};
|
||||||
|
|
@ -435,7 +425,7 @@ bool DSoundPlayback::reset()
|
||||||
OutputType.Format.wBitsPerSample = static_cast<WORD>(mDevice->bytesFromFmt() * 8);
|
OutputType.Format.wBitsPerSample = static_cast<WORD>(mDevice->bytesFromFmt() * 8);
|
||||||
OutputType.Format.nBlockAlign = static_cast<WORD>(OutputType.Format.nChannels *
|
OutputType.Format.nBlockAlign = static_cast<WORD>(OutputType.Format.nChannels *
|
||||||
OutputType.Format.wBitsPerSample / 8);
|
OutputType.Format.wBitsPerSample / 8);
|
||||||
OutputType.Format.nSamplesPerSec = mDevice->Frequency;
|
OutputType.Format.nSamplesPerSec = mDevice->mSampleRate;
|
||||||
OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
|
OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
|
||||||
OutputType.Format.nBlockAlign;
|
OutputType.Format.nBlockAlign;
|
||||||
OutputType.Format.cbSize = 0;
|
OutputType.Format.cbSize = 0;
|
||||||
|
|
@ -469,16 +459,16 @@ bool DSoundPlayback::reset()
|
||||||
if(FAILED(hr))
|
if(FAILED(hr))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
uint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
|
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);
|
||||||
|
|
@ -492,13 +482,13 @@ bool DSoundPlayback::reset()
|
||||||
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:
|
||||||
|
|
@ -636,8 +626,8 @@ void DSoundCapture::open(std::string_view name)
|
||||||
case DevFmtX7144:
|
case DevFmtX7144:
|
||||||
case DevFmtX3D71:
|
case DevFmtX3D71:
|
||||||
case DevFmtAmbi3D:
|
case DevFmtAmbi3D:
|
||||||
WARN("%s capture not supported\n", DevFmtChannelsString(mDevice->FmtChans));
|
WARN("{} capture not supported", DevFmtChannelsString(mDevice->FmtChans));
|
||||||
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)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -646,7 +636,7 @@ 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;
|
||||||
|
|
@ -663,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);
|
||||||
|
|
@ -676,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))
|
||||||
{
|
{
|
||||||
|
|
@ -684,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",
|
throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: {:#x}",
|
||||||
hr};
|
as_unsigned(hr)};
|
||||||
}
|
}
|
||||||
|
|
||||||
mBufferBytes = DSCBDescription.dwBufferBytes;
|
mBufferBytes = DSCBDescription.dwBufferBytes;
|
||||||
setDefaultWFXChannelOrder();
|
setDefaultWFXChannelOrder();
|
||||||
|
|
||||||
mDevice->DeviceName = name;
|
mDeviceName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSoundCapture::start()
|
void DSoundCapture::start()
|
||||||
|
|
@ -699,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()
|
||||||
|
|
@ -707,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);
|
ERR("stop failed: {:#x}", as_unsigned(hr));
|
||||||
mDevice->handleDisconnect("Failure stopping capture: 0x%lx", hr);
|
mDevice->handleDisconnect("Failure stopping capture: {:#x}", as_unsigned(hr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -745,8 +735,8 @@ uint DSoundCapture::availableSamples()
|
||||||
|
|
||||||
if(FAILED(hr))
|
if(FAILED(hr))
|
||||||
{
|
{
|
||||||
ERR("update failed: 0x%08lx\n", hr);
|
ERR("update failed: {:#x}", as_unsigned(hr));
|
||||||
mDevice->handleDisconnect("Failure retrieving capture data: 0x%lx", hr);
|
mDevice->handleDisconnect("Failure retrieving capture data: {:#x}", as_unsigned(hr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return static_cast<uint>(mRing->readSpace());
|
return static_cast<uint>(mRing->readSpace());
|
||||||
|
|
@ -763,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -808,7 +798,7 @@ auto DSoundBackendFactory::enumerate(BackendType type) -> std::vector<std::strin
|
||||||
case BackendType::Playback:
|
case BackendType::Playback:
|
||||||
PlaybackDevices.clear();
|
PlaybackDevices.clear();
|
||||||
if(HRESULT hr{DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices)}; FAILED(hr))
|
if(HRESULT hr{DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices)}; FAILED(hr))
|
||||||
ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr);
|
ERR("Error enumerating DirectSound playback devices: {:#x}", as_unsigned(hr));
|
||||||
outnames.reserve(PlaybackDevices.size());
|
outnames.reserve(PlaybackDevices.size());
|
||||||
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
|
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
|
||||||
break;
|
break;
|
||||||
|
|
@ -816,7 +806,7 @@ auto DSoundBackendFactory::enumerate(BackendType type) -> std::vector<std::strin
|
||||||
case BackendType::Capture:
|
case BackendType::Capture:
|
||||||
CaptureDevices.clear();
|
CaptureDevices.clear();
|
||||||
if(HRESULT hr{DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices)};FAILED(hr))
|
if(HRESULT hr{DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices)};FAILED(hr))
|
||||||
ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr);
|
ERR("Error enumerating DirectSound capture devices: {:#x}", as_unsigned(hr));
|
||||||
outnames.reserve(CaptureDevices.size());
|
outnames.reserve(CaptureDevices.size());
|
||||||
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
|
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -29,18 +29,17 @@
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <functional>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#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>
|
||||||
|
|
@ -51,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); \
|
||||||
|
|
@ -108,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"
|
||||||
|
|
@ -119,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,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;
|
||||||
|
|
@ -194,15 +195,15 @@ void EnumerateDevices(jack_client_t *client, std::vector<DeviceEntry> &list)
|
||||||
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}+":");
|
const auto &entry = list.emplace_back(portdev, fmt::format("{}:", portdev));
|
||||||
TRACE("Got device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str());
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -216,7 +217,7 @@ void EnumerateDevices(jack_client_t *client, std::vector<DeviceEntry> &list)
|
||||||
if(seppos >= nextpos || seppos == strpos)
|
if(seppos >= nextpos || seppos == strpos)
|
||||||
{
|
{
|
||||||
const auto entry = std::string_view{*listopt}.substr(strpos, nextpos-strpos);
|
const auto entry = std::string_view{*listopt}.substr(strpos, nextpos-strpos);
|
||||||
ERR("Invalid device entry: \"%.*s\"\n", al::sizei(entry), entry.data());
|
ERR("Invalid device entry: \"{}\"", entry);
|
||||||
if(nextpos != std::string::npos) ++nextpos;
|
if(nextpos != std::string::npos) ++nextpos;
|
||||||
strpos = nextpos;
|
strpos = nextpos;
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -234,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(),
|
TRACE("Customized device name: {} = {}", itemmatch->mName, itemmatch->mPattern);
|
||||||
itemmatch->mPattern.c_str());
|
|
||||||
}
|
}
|
||||||
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;
|
||||||
|
|
@ -277,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;
|
||||||
|
|
@ -329,13 +329,13 @@ JackPlayback::~JackPlayback()
|
||||||
|
|
||||||
int JackPlayback::processRt(jack_nframes_t numframes) noexcept
|
int JackPlayback::processRt(jack_nframes_t numframes) noexcept
|
||||||
{
|
{
|
||||||
auto outptrs = std::array<jack_default_audio_sample_t*,MaxOutputChannels>{};
|
auto outptrs = std::array<void*,MaxOutputChannels>{};
|
||||||
auto numchans = size_t{0};
|
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;
|
||||||
outptrs[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);
|
const auto dst = al::span{outptrs}.first(numchans);
|
||||||
|
|
@ -343,8 +343,8 @@ int JackPlayback::processRt(jack_nframes_t numframes) noexcept
|
||||||
mDevice->renderSamples(dst, static_cast<uint>(numframes));
|
mDevice->renderSamples(dst, static_cast<uint>(numframes));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::for_each(dst.begin(), dst.end(), [numframes](float *outbuf) -> void
|
std::for_each(dst.begin(), dst.end(), [numframes](void *outbuf) -> void
|
||||||
{ std::fill_n(outbuf, numframes, 0.0f); });
|
{ std::fill_n(static_cast<float*>(outbuf), numframes, 0.0f); });
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -365,13 +365,13 @@ int JackPlayback::process(jack_nframes_t numframes) noexcept
|
||||||
if(mPlaying.load(std::memory_order_acquire)) LIKELY
|
if(mPlaying.load(std::memory_order_acquire)) LIKELY
|
||||||
{
|
{
|
||||||
auto data = mRing->getReadVector();
|
auto data = mRing->getReadVector();
|
||||||
const auto update_size = size_t{mDevice->UpdateSize};
|
const auto update_size = size_t{mDevice->mUpdateSize};
|
||||||
|
|
||||||
const auto outlen = size_t{numframes / update_size};
|
const auto outlen = size_t{numframes / update_size};
|
||||||
const auto len1 = size_t{std::min(data.first.len/update_size, outlen)};
|
const auto len1 = size_t{std::min(data[0].len/update_size, outlen)};
|
||||||
const auto len2 = size_t{std::min(data.second.len/update_size, outlen-len1)};
|
const auto len2 = size_t{std::min(data[1].len/update_size, outlen-len1)};
|
||||||
|
|
||||||
auto src = al::span{reinterpret_cast<float*>(data.first.buf), update_size*len1*numchans};
|
auto src = al::span{reinterpret_cast<float*>(data[0].buf), update_size*len1*numchans};
|
||||||
for(size_t i{0};i < len1;++i)
|
for(size_t i{0};i < len1;++i)
|
||||||
{
|
{
|
||||||
for(size_t c{0};c < numchans;++c)
|
for(size_t c{0};c < numchans;++c)
|
||||||
|
|
@ -383,7 +383,7 @@ int JackPlayback::process(jack_nframes_t numframes) noexcept
|
||||||
total += update_size;
|
total += update_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
src = al::span{reinterpret_cast<float*>(data.second.buf), update_size*len2*numchans};
|
src = al::span{reinterpret_cast<float*>(data[1].buf), update_size*len2*numchans};
|
||||||
for(size_t i{0};i < len2;++i)
|
for(size_t i{0};i < len2;++i)
|
||||||
{
|
{
|
||||||
for(size_t c{0};c < numchans;++c)
|
for(size_t c{0};c < numchans;++c)
|
||||||
|
|
@ -414,9 +414,9 @@ int JackPlayback::mixerProc()
|
||||||
SetRTPriority();
|
SetRTPriority();
|
||||||
althrd_setname(GetMixerThreadName());
|
althrd_setname(GetMixerThreadName());
|
||||||
|
|
||||||
const auto update_size = uint{mDevice->UpdateSize};
|
const auto update_size = uint{mDevice->mUpdateSize};
|
||||||
const auto num_channels = size_t{mDevice->channelsFromFmt()};
|
const auto num_channels = size_t{mDevice->channelsFromFmt()};
|
||||||
auto outptrs = std::vector<float*>(num_channels);
|
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))
|
||||||
|
|
@ -428,12 +428,11 @@ int JackPlayback::mixerProc()
|
||||||
}
|
}
|
||||||
|
|
||||||
auto data = mRing->getWriteVector();
|
auto data = mRing->getWriteVector();
|
||||||
const auto len1 = size_t{data.first.len / update_size};
|
const auto len1 = size_t{data[0].len / update_size};
|
||||||
const auto len2 = size_t{data.second.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};
|
||||||
auto buffer = al::span{reinterpret_cast<float*>(data.first.buf),
|
auto buffer = al::span{reinterpret_cast<float*>(data[0].buf), data[0].len*num_channels};
|
||||||
data.first.len*num_channels};
|
|
||||||
auto bufiter = buffer.begin();
|
auto bufiter = buffer.begin();
|
||||||
for(size_t i{0};i < len1;++i)
|
for(size_t i{0};i < len1;++i)
|
||||||
{
|
{
|
||||||
|
|
@ -447,8 +446,7 @@ int JackPlayback::mixerProc()
|
||||||
}
|
}
|
||||||
if(len2 > 0)
|
if(len2 > 0)
|
||||||
{
|
{
|
||||||
buffer = al::span{reinterpret_cast<float*>(data.second.buf),
|
buffer = al::span{reinterpret_cast<float*>(data[1].buf), data[1].len*num_channels};
|
||||||
data.second.len*num_channels};
|
|
||||||
bufiter = buffer.begin();
|
bufiter = buffer.begin();
|
||||||
for(size_t i{0};i < len2;++i)
|
for(size_t i{0};i < len2;++i)
|
||||||
{
|
{
|
||||||
|
|
@ -479,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -504,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()
|
||||||
|
|
@ -518,28 +517,29 @@ 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->mSampleRate = jack_get_sample_rate(mClient);
|
||||||
mDevice->UpdateSize = jack_get_buffer_size(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};
|
const auto devname = std::string_view{mDevice->mDeviceName};
|
||||||
uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)};
|
auto bufsize = ConfigValueUInt(devname, "jack", "buffer-size")
|
||||||
bufsize = std::max(NextPowerOf2(bufsize), mDevice->UpdateSize);
|
.value_or(mDevice->mUpdateSize);
|
||||||
mDevice->BufferSize = bufsize + mDevice->UpdateSize;
|
bufsize = std::max(NextPowerOf2(bufsize), mDevice->mUpdateSize);
|
||||||
|
mDevice->mBufferSize = bufsize + mDevice->mUpdateSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Force 32-bit float output. */
|
/* Force 32-bit float output. */
|
||||||
|
|
@ -558,7 +558,7 @@ bool JackPlayback::reset()
|
||||||
}
|
}
|
||||||
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 == ports.begin()) return false;
|
if(bad_port == ports.begin()) return false;
|
||||||
|
|
||||||
|
|
@ -586,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,
|
||||||
|
|
@ -601,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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -614,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->mSampleRate = jack_get_sample_rate(mClient);
|
||||||
mDevice->UpdateSize = jack_get_buffer_size(mClient);
|
mDevice->mUpdateSize = jack_get_buffer_size(mClient);
|
||||||
mDevice->BufferSize = mDevice->UpdateSize * 2;
|
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)};
|
uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size")
|
||||||
bufsize = std::max(NextPowerOf2(bufsize), mDevice->UpdateSize);
|
.value_or(mDevice->mUpdateSize)};
|
||||||
mDevice->BufferSize = bufsize + mDevice->UpdateSize;
|
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()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -665,8 +666,8 @@ ClockLatency JackPlayback::getClockLatency()
|
||||||
std::lock_guard<std::mutex> dlock{mMutex};
|
std::lock_guard<std::mutex> dlock{mMutex};
|
||||||
ClockLatency ret{};
|
ClockLatency ret{};
|
||||||
ret.ClockTime = mDevice->getClockTime();
|
ret.ClockTime = mDevice->getClockTime();
|
||||||
ret.Latency = std::chrono::seconds{mRing ? mRing->readSpace() : mDevice->UpdateSize};
|
ret.Latency = std::chrono::seconds{mRing ? mRing->readSpace() : mDevice->mUpdateSize};
|
||||||
ret.Latency /= mDevice->Frequency;
|
ret.Latency /= mDevice->mSampleRate;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
@ -674,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
|
||||||
|
|
@ -697,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -728,7 +729,7 @@ auto JackBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||||
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());
|
outnames.reserve(PlaybackList.size());
|
||||||
std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
|
std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()};
|
const auto avail = int64_t{std::chrono::duration_cast<seconds>((now-start)
|
||||||
if(avail-done < mDevice->UpdateSize)
|
* 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);
|
mDevice->renderSamples(nullptr, mDevice->mUpdateSize, 0u);
|
||||||
done += mDevice->UpdateSize;
|
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",
|
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
|
||||||
al::sizei(name), name.data()};
|
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()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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));
|
mDevice->handleDisconnect("Oboe AudioStream was disconnected: {}",
|
||||||
TRACE("Error was %s", oboe::convertToText(error));
|
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",
|
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
|
||||||
al::sizei(name), name.data()};
|
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",
|
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
|
||||||
al::sizei(name), name.data()};
|
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.
|
||||||
|
|
@ -276,7 +277,7 @@ void OboeCapture::open(std::string_view name)
|
||||||
case DevFmtX7144:
|
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)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,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)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -330,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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -156,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;
|
||||||
|
|
@ -247,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))
|
||||||
|
|
@ -265,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -278,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,
|
mDevice->renderSamples(data[0].buf,
|
||||||
static_cast<uint>(data.first.len)*mDevice->UpdateSize, frame_step);
|
static_cast<uint>(data[0].len)*mDevice->mUpdateSize, frame_step);
|
||||||
if(data.second.len > 0)
|
if(data[1].len > 0)
|
||||||
mDevice->renderSamples(data.second.buf,
|
mDevice->renderSamples(data[1].buf,
|
||||||
static_cast<uint>(data.second.len)*mDevice->UpdateSize, frame_step);
|
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[0] = data[1];
|
||||||
data.second.buf = nullptr;
|
data[1].buf = nullptr;
|
||||||
data.second.len = 0;
|
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[0].len--;
|
||||||
data.first.buf += mDevice->UpdateSize*mFrameSize;
|
data[0].buf += mDevice->mUpdateSize*mFrameSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -319,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",
|
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
|
||||||
al::sizei(name), name.data()};
|
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;
|
||||||
|
|
@ -361,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()
|
||||||
|
|
@ -397,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);
|
||||||
|
|
@ -435,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);
|
||||||
|
|
@ -472,8 +499,8 @@ bool OpenSLPlayback::reset()
|
||||||
}
|
}
|
||||||
if(SL_RESULT_SUCCESS == result)
|
if(SL_RESULT_SUCCESS == result)
|
||||||
{
|
{
|
||||||
const uint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
|
const uint num_updates{mDevice->mBufferSize / mDevice->mUpdateSize};
|
||||||
mRing = RingBuffer::Create(num_updates, mFrameSize*mDevice->UpdateSize, true);
|
mRing = RingBuffer::Create(num_updates, mFrameSize*mDevice->mUpdateSize, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(SL_RESULT_SUCCESS != result)
|
if(SL_RESULT_SUCCESS != result)
|
||||||
|
|
@ -505,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()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -566,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 = std::chrono::seconds{mRing->readSpace() * mDevice->mUpdateSize};
|
||||||
ret.Latency /= mDevice->Frequency;
|
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;
|
||||||
|
|
@ -623,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",
|
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
|
||||||
al::sizei(name), name.data()};
|
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");
|
||||||
|
|
@ -642,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,
|
uint update_len{std::clamp(mDevice->mBufferSize/3u, mDevice->mSampleRate/100u,
|
||||||
mDevice->Frequency/100u*5u)};
|
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->mUpdateSize = update_len;
|
||||||
mDevice->BufferSize = static_cast<uint>(mRing->writeSpace() * update_len);
|
mDevice->mBufferSize = static_cast<uint>(mRing->writeSpace() * update_len);
|
||||||
}
|
}
|
||||||
if(SL_RESULT_SUCCESS == result)
|
if(SL_RESULT_SUCCESS == result)
|
||||||
{
|
{
|
||||||
|
|
@ -670,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);
|
||||||
|
|
@ -700,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);
|
||||||
|
|
@ -752,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[0].buf, data[0].len*chunk_size, silence);
|
||||||
std::fill_n(data.second.buf, data.second.len*chunk_size, silence);
|
std::fill_n(data[1].buf, data[1].len*chunk_size, silence);
|
||||||
for(size_t i{0u};i < data.first.len && SL_RESULT_SUCCESS == result;i++)
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -782,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()
|
||||||
|
|
@ -801,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()
|
||||||
|
|
@ -819,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
|
||||||
|
|
@ -830,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;
|
||||||
|
|
@ -840,11 +867,11 @@ void OpenSLCapture::captureSamples(std::byte *buffer, uint samples)
|
||||||
mSplOffset = 0;
|
mSplOffset = 0;
|
||||||
|
|
||||||
++adv_count;
|
++adv_count;
|
||||||
rdata.first.len -= 1;
|
rdata[0].len -= 1;
|
||||||
if(!rdata.first.len)
|
if(!rdata[0].len)
|
||||||
rdata.first = rdata.second;
|
rdata[0] = rdata[1];
|
||||||
else
|
else
|
||||||
rdata.first.buf += chunk_size;
|
rdata[0].buf += chunk_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
i += rem;
|
i += rem;
|
||||||
|
|
@ -858,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -877,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 len1 = std::min(wdata[0].len, adv_count-wdata[1].len);
|
||||||
auto buf1 = wdata.first.buf + chunk_size*(wdata.first.len-len1);
|
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 len2 = std::min(wdata[1].len, adv_count);
|
||||||
auto buf2 = wdata.second.buf + chunk_size*(wdata.second.len-len2);
|
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);
|
||||||
|
|
@ -900,11 +927,43 @@ 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); }
|
||||||
|
|
|
||||||
|
|
@ -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};
|
auto count = 1;
|
||||||
std::string newname{handle};
|
auto newname = std::string{handle};
|
||||||
while(checkName(newname))
|
while(checkName(newname))
|
||||||
{
|
newname = fmt::format("{} #{}", handle, ++count);
|
||||||
newname = handle;
|
|
||||||
newname += " #";
|
|
||||||
newname += std::to_string(++count);
|
|
||||||
}
|
|
||||||
|
|
||||||
const DevMap &entry = list.emplace_back(std::move(newname), path);
|
const auto &entry = list.emplace_back(std::move(newname), path);
|
||||||
|
TRACE("Got device \"{}\", \"{}\"", entry.name, entry.device_name);
|
||||||
TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
ERR("SNDCTL_AUDIOINFO ({}) failed: {}", i, std::generic_category().message(errno));
|
||||||
std::generic_category().message(errno).c_str());
|
|
||||||
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());
|
ERR("poll failed: {}", errstr);
|
||||||
mDevice->handleDisconnect("Failed waiting for playback buffer: %s", errstr.c_str());
|
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());
|
ERR("write failed: {}", errstr);
|
||||||
mDevice->handleDisconnect("Failed writing playback samples: %s", errstr.c_str());
|
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,
|
throw al::backend_exception{al::backend_error::NoDevice, "Could not open {}: {}", devname,
|
||||||
std::generic_category().message(errno).c_str()};
|
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, \
|
throw al::backend_exception{al::backend_error::DeviceError, #func " failed: {}", \
|
||||||
std::generic_category().message(errno).c_str()};
|
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),
|
ERR("Failed to set {} samples, got OSS format {:#x}", DevFmtTypeString(mDevice->FmtType),
|
||||||
ossFormat);
|
as_unsigned(ossFormat));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mDevice->Frequency = ossSpeed;
|
mDevice->mSampleRate = ossSpeed;
|
||||||
mDevice->UpdateSize = static_cast<uint>(info.fragsize) / frameSize;
|
mDevice->mUpdateSize = static_cast<uint>(info.fragsize) / frameSize;
|
||||||
mDevice->BufferSize = static_cast<uint>(info.fragments) * mDevice->UpdateSize;
|
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)
|
if(ioctl(mFd, SNDCTL_DSP_RESET) != 0) /* NOLINT(cppcoreguidelines-pro-type-vararg) */
|
||||||
ERR("Error resetting device: %s\n", std::generic_category().message(errno).c_str());
|
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());
|
ERR("poll failed: {}", errstr);
|
||||||
mDevice->handleDisconnect("Failed to check capture samples: %s", errstr.c_str());
|
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());
|
ERR("read failed: {}", errstr);
|
||||||
mDevice->handleDisconnect("Failed reading capture samples: %s", errstr.c_str());
|
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,
|
throw al::backend_exception{al::backend_error::NoDevice, "Could not open {}: {}", devname,
|
||||||
std::generic_category().message(errno).c_str()};
|
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", \
|
throw al::backend_exception{al::backend_error::DeviceError, #func " failed: {}", \
|
||||||
std::generic_category().message(errno).c_str()}; \
|
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),
|
"Failed to set {} samples, got OSS format {:#x}", DevFmtTypeString(mDevice->FmtType),
|
||||||
ossFormat};
|
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)
|
if(ioctl(mFd, SNDCTL_DSP_RESET) != 0) /* NOLINT(cppcoreguidelines-pro-type-vararg) */
|
||||||
ERR("Error resetting device: %s\n", std::generic_category().message(errno).c_str());
|
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)
|
||||||
|
|
|
||||||
700
Engine/lib/openal-soft/alc/backends/otherio.cpp
Normal file
700
Engine/lib/openal-soft/alc/backends/otherio.cpp
Normal file
|
|
@ -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
Engine/lib/openal-soft/alc/backends/otherio.h
Normal file
21
Engine/lib/openal-soft/alc/backends/otherio.h
Normal file
|
|
@ -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 */
|
||||||
|
|
@ -35,6 +35,7 @@
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <functional>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
@ -54,6 +55,8 @@
|
||||||
#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 "fmt/ranges.h"
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
|
|
||||||
|
|
@ -132,6 +135,9 @@ _Pragma("GCC diagnostic pop")
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
template<typename T> [[nodiscard]] constexpr
|
||||||
|
auto as_const_ptr(T *ptr) noexcept -> std::add_const_t<T>* { return ptr; }
|
||||||
|
|
||||||
struct PodDynamicBuilder {
|
struct PodDynamicBuilder {
|
||||||
private:
|
private:
|
||||||
std::vector<std::byte> mStorage;
|
std::vector<std::byte> mStorage;
|
||||||
|
|
@ -143,7 +149,7 @@ private:
|
||||||
mStorage.resize(size);
|
mStorage.resize(size);
|
||||||
}
|
}
|
||||||
catch(...) {
|
catch(...) {
|
||||||
ERR("Failed to resize POD storage\n");
|
ERR("Failed to resize POD storage");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
mPod.data = mStorage.data();
|
mPod.data = mStorage.data();
|
||||||
|
|
@ -152,7 +158,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PodDynamicBuilder(uint32_t initSize=0) : mStorage(initSize)
|
explicit PodDynamicBuilder(uint32_t initSize=1024) : mStorage(initSize)
|
||||||
, mPod{make_pod_builder(mStorage.data(), initSize)}
|
, mPod{make_pod_builder(mStorage.data(), initSize)}
|
||||||
{
|
{
|
||||||
static constexpr auto callbacks{[]
|
static constexpr auto callbacks{[]
|
||||||
|
|
@ -189,12 +195,13 @@ bool check_version(const char *version)
|
||||||
* future.
|
* future.
|
||||||
*/
|
*/
|
||||||
int major{0}, minor{0}, revision{0};
|
int major{0}, minor{0}, revision{0};
|
||||||
|
/* NOLINTNEXTLINE(cert-err34-c,cppcoreguidelines-pro-type-vararg) */
|
||||||
int ret{sscanf(version, "%d.%d.%d", &major, &minor, &revision)};
|
int ret{sscanf(version, "%d.%d.%d", &major, &minor, &revision)};
|
||||||
return ret == 3 && (major > PW_MAJOR || (major == PW_MAJOR && minor > PW_MINOR)
|
return ret == 3 && (major > PW_MAJOR || (major == PW_MAJOR && minor > PW_MINOR)
|
||||||
|| (major == PW_MAJOR && minor == PW_MINOR && revision >= PW_MICRO));
|
|| (major == PW_MAJOR && minor == PW_MINOR && revision >= PW_MICRO));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_DYNLOAD
|
#if HAVE_DYNLOAD
|
||||||
#define PWIRE_FUNCS(MAGIC) \
|
#define PWIRE_FUNCS(MAGIC) \
|
||||||
MAGIC(pw_context_connect) \
|
MAGIC(pw_context_connect) \
|
||||||
MAGIC(pw_context_destroy) \
|
MAGIC(pw_context_destroy) \
|
||||||
|
|
@ -251,7 +258,7 @@ bool pwire_load()
|
||||||
pwire_handle = LoadLib(pwire_library);
|
pwire_handle = LoadLib(pwire_library);
|
||||||
if(!pwire_handle)
|
if(!pwire_handle)
|
||||||
{
|
{
|
||||||
WARN("Failed to load %s\n", pwire_library);
|
WARN("Failed to load {}", pwire_library);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -265,7 +272,7 @@ bool pwire_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(pwire_handle);
|
CloseLib(pwire_handle);
|
||||||
pwire_handle = nullptr;
|
pwire_handle = nullptr;
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -411,9 +418,10 @@ struct PwStreamDeleter {
|
||||||
};
|
};
|
||||||
using PwStreamPtr = std::unique_ptr<pw_stream,PwStreamDeleter>;
|
using PwStreamPtr = std::unique_ptr<pw_stream,PwStreamDeleter>;
|
||||||
|
|
||||||
/* Enums for bitflags... again... *sigh* */
|
/* NOLINTBEGIN(*EnumCastOutOfRange) Enums for bitflags... again... *sigh* */
|
||||||
constexpr pw_stream_flags operator|(pw_stream_flags lhs, pw_stream_flags rhs) noexcept
|
constexpr pw_stream_flags operator|(pw_stream_flags lhs, pw_stream_flags rhs) noexcept
|
||||||
{ return static_cast<pw_stream_flags>(lhs | al::to_underlying(rhs)); }
|
{ return static_cast<pw_stream_flags>(lhs | al::to_underlying(rhs)); }
|
||||||
|
/* NOLINTEND(*EnumCastOutOfRange) */
|
||||||
|
|
||||||
constexpr pw_stream_flags& operator|=(pw_stream_flags &lhs, pw_stream_flags rhs) noexcept
|
constexpr pw_stream_flags& operator|=(pw_stream_flags &lhs, pw_stream_flags rhs) noexcept
|
||||||
{ lhs = lhs | rhs; return lhs; }
|
{ lhs = lhs | rhs; return lhs; }
|
||||||
|
|
@ -497,7 +505,7 @@ struct NodeProxy {
|
||||||
|
|
||||||
uint32_t mId{};
|
uint32_t mId{};
|
||||||
|
|
||||||
PwNodePtr mNode{};
|
PwNodePtr mNode;
|
||||||
spa_hook mListener{};
|
spa_hook mListener{};
|
||||||
|
|
||||||
NodeProxy(uint32_t id, PwNodePtr node)
|
NodeProxy(uint32_t id, PwNodePtr node)
|
||||||
|
|
@ -532,7 +540,7 @@ struct MetadataProxy {
|
||||||
|
|
||||||
uint32_t mId{};
|
uint32_t mId{};
|
||||||
|
|
||||||
PwMetadataPtr mMetadata{};
|
PwMetadataPtr mMetadata;
|
||||||
spa_hook mListener{};
|
spa_hook mListener{};
|
||||||
|
|
||||||
MetadataProxy(uint32_t id, PwMetadataPtr mdata)
|
MetadataProxy(uint32_t id, PwMetadataPtr mdata)
|
||||||
|
|
@ -553,10 +561,10 @@ struct MetadataProxy {
|
||||||
* to objects being added to or removed from the registry.
|
* to objects being added to or removed from the registry.
|
||||||
*/
|
*/
|
||||||
struct EventManager {
|
struct EventManager {
|
||||||
ThreadMainloop mLoop{};
|
ThreadMainloop mLoop;
|
||||||
PwContextPtr mContext{};
|
PwContextPtr mContext;
|
||||||
PwCorePtr mCore{};
|
PwCorePtr mCore;
|
||||||
PwRegistryPtr mRegistry{};
|
PwRegistryPtr mRegistry;
|
||||||
spa_hook mRegistryListener{};
|
spa_hook mRegistryListener{};
|
||||||
spa_hook mCoreListener{};
|
spa_hook mCoreListener{};
|
||||||
|
|
||||||
|
|
@ -585,8 +593,8 @@ struct EventManager {
|
||||||
auto lock() const { return mLoop.lock(); }
|
auto lock() const { return mLoop.lock(); }
|
||||||
auto unlock() const { return mLoop.unlock(); }
|
auto unlock() const { return mLoop.unlock(); }
|
||||||
|
|
||||||
[[nodiscard]] inline
|
[[nodiscard]]
|
||||||
bool initIsDone(std::memory_order m=std::memory_order_seq_cst) const noexcept
|
auto initIsDone(std::memory_order m=std::memory_order_seq_cst) const noexcept -> bool
|
||||||
{ return mInitDone.load(m); }
|
{ return mInitDone.load(m); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -759,7 +767,7 @@ void DeviceNode::Remove(uint32_t id)
|
||||||
{
|
{
|
||||||
if(n.mId != id)
|
if(n.mId != id)
|
||||||
return false;
|
return false;
|
||||||
TRACE("Removing device \"%s\"\n", n.mDevName.c_str());
|
TRACE("Removing device \"{}\"", n.mDevName);
|
||||||
if(gEventHandler.initIsDone(std::memory_order_relaxed))
|
if(gEventHandler.initIsDone(std::memory_order_relaxed))
|
||||||
{
|
{
|
||||||
const std::string msg{"Device removed: "+n.mName};
|
const std::string msg{"Device removed: "+n.mName};
|
||||||
|
|
@ -828,7 +836,7 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce
|
||||||
const uint podType{get_pod_type(value)};
|
const uint podType{get_pod_type(value)};
|
||||||
if(podType != SPA_TYPE_Int)
|
if(podType != SPA_TYPE_Int)
|
||||||
{
|
{
|
||||||
WARN(" Unhandled sample rate POD type: %u\n", podType);
|
WARN(" Unhandled sample rate POD type: {}", podType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -836,13 +844,13 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce
|
||||||
{
|
{
|
||||||
if(nvals != 3)
|
if(nvals != 3)
|
||||||
{
|
{
|
||||||
WARN(" Unexpected SPA_CHOICE_Range count: %u\n", nvals);
|
WARN(" Unexpected SPA_CHOICE_Range count: {}", nvals);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto srates = get_pod_body<int32_t,3>(value);
|
auto srates = get_pod_body<int32_t,3>(value);
|
||||||
|
|
||||||
/* [0] is the default, [1] is the min, and [2] is the max. */
|
/* [0] is the default, [1] is the min, and [2] is the max. */
|
||||||
TRACE(" sample rate: %d (range: %d -> %d)\n", srates[0], srates[1], srates[2]);
|
TRACE(" sample rate: {}, range: {}", srates[0], srates.subspan<1>());
|
||||||
if(!mSampleRate || force_update)
|
if(!mSampleRate || force_update)
|
||||||
mSampleRate = static_cast<uint>(std::clamp<int>(srates[0], MinOutputRate,
|
mSampleRate = static_cast<uint>(std::clamp<int>(srates[0], MinOutputRate,
|
||||||
MaxOutputRate));
|
MaxOutputRate));
|
||||||
|
|
@ -853,19 +861,13 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce
|
||||||
{
|
{
|
||||||
if(nvals == 0)
|
if(nvals == 0)
|
||||||
{
|
{
|
||||||
WARN(" Unexpected SPA_CHOICE_Enum count: %u\n", nvals);
|
WARN(" Unexpected SPA_CHOICE_Enum count: {}", nvals);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto srates = get_pod_body<int32_t>(value, nvals);
|
auto srates = get_pod_body<int32_t>(value, nvals);
|
||||||
|
|
||||||
/* [0] is the default, [1...size()-1] are available selections. */
|
/* [0] is the default, [1...size()-1] are available selections. */
|
||||||
std::string others{(srates.size() > 1) ? std::to_string(srates[1]) : std::string{}};
|
TRACE(" sample rate: {}, list: {}", srates[0], srates.subspan(1));
|
||||||
for(size_t i{2};i < srates.size();++i)
|
|
||||||
{
|
|
||||||
others += ", ";
|
|
||||||
others += std::to_string(srates[i]);
|
|
||||||
}
|
|
||||||
TRACE(" sample rate: %d (%s)\n", srates[0], others.c_str());
|
|
||||||
/* Pick the first rate listed that's within the allowed range (default
|
/* Pick the first rate listed that's within the allowed range (default
|
||||||
* rate if possible).
|
* rate if possible).
|
||||||
*/
|
*/
|
||||||
|
|
@ -885,19 +887,19 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce
|
||||||
{
|
{
|
||||||
if(nvals != 1)
|
if(nvals != 1)
|
||||||
{
|
{
|
||||||
WARN(" Unexpected SPA_CHOICE_None count: %u\n", nvals);
|
WARN(" Unexpected SPA_CHOICE_None count: {}", nvals);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto srates = get_pod_body<int32_t,1>(value);
|
auto srates = get_pod_body<int32_t,1>(value);
|
||||||
|
|
||||||
TRACE(" sample rate: %d\n", srates[0]);
|
TRACE(" sample rate: {}", srates[0]);
|
||||||
if(!mSampleRate || force_update)
|
if(!mSampleRate || force_update)
|
||||||
mSampleRate = static_cast<uint>(std::clamp<int>(srates[0], MinOutputRate,
|
mSampleRate = static_cast<uint>(std::clamp<int>(srates[0], MinOutputRate,
|
||||||
MaxOutputRate));
|
MaxOutputRate));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WARN(" Unhandled sample rate choice type: %u\n", choiceType);
|
WARN(" Unhandled sample rate choice type: {}", choiceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeviceNode::parsePositions(const spa_pod *value, bool force_update) noexcept
|
void DeviceNode::parsePositions(const spa_pod *value, bool force_update) noexcept
|
||||||
|
|
@ -907,7 +909,7 @@ void DeviceNode::parsePositions(const spa_pod *value, bool force_update) noexcep
|
||||||
|
|
||||||
if(choiceType != SPA_CHOICE_None || choiceCount != 1)
|
if(choiceType != SPA_CHOICE_None || choiceCount != 1)
|
||||||
{
|
{
|
||||||
ERR(" Unexpected positions choice: type=%u, count=%u\n", choiceType, choiceCount);
|
ERR(" Unexpected positions choice: type={}, count={}", choiceType, choiceCount);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -938,7 +940,7 @@ void DeviceNode::parsePositions(const spa_pod *value, bool force_update) noexcep
|
||||||
else
|
else
|
||||||
mChannels = DevFmtMono;
|
mChannels = DevFmtMono;
|
||||||
}
|
}
|
||||||
TRACE(" %zu position%s for %s%s\n", chanmap.size(), (chanmap.size()==1)?"":"s",
|
TRACE(" {} position{} for {}{}", chanmap.size(), (chanmap.size()==1)?"":"s",
|
||||||
DevFmtChannelsString(mChannels), mIs51Rear?"(rear)":"");
|
DevFmtChannelsString(mChannels), mIs51Rear?"(rear)":"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -950,7 +952,7 @@ void DeviceNode::parseChannelCount(const spa_pod *value, bool force_update) noex
|
||||||
|
|
||||||
if(choiceType != SPA_CHOICE_None || choiceCount != 1)
|
if(choiceType != SPA_CHOICE_None || choiceCount != 1)
|
||||||
{
|
{
|
||||||
ERR(" Unexpected positions choice: type=%u, count=%u\n", choiceType, choiceCount);
|
ERR(" Unexpected positions choice: type={}, count={}", choiceType, choiceCount);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -966,7 +968,7 @@ void DeviceNode::parseChannelCount(const spa_pod *value, bool force_update) noex
|
||||||
else if(*chancount >= 1)
|
else if(*chancount >= 1)
|
||||||
mChannels = DevFmtMono;
|
mChannels = DevFmtMono;
|
||||||
}
|
}
|
||||||
TRACE(" %d channel%s for %s\n", *chancount, (*chancount==1)?"":"s",
|
TRACE(" {} channel{} for {}", *chancount, (*chancount==1)?"":"s",
|
||||||
DevFmtChannelsString(mChannels));
|
DevFmtChannelsString(mChannels));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1005,7 +1007,7 @@ void NodeProxy::infoCallback(void*, const pw_node_info *info) noexcept
|
||||||
ntype = NodeType::Duplex;
|
ntype = NodeType::Duplex;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TRACE("Dropping device node %u which became type \"%s\"\n", info->id, media_class);
|
TRACE("Dropping device node {} which became type \"{}\"", info->id, media_class);
|
||||||
DeviceNode::Remove(info->id);
|
DeviceNode::Remove(info->id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1024,20 +1026,23 @@ void NodeProxy::infoCallback(void*, const pw_node_info *info) noexcept
|
||||||
serial_id = std::strtoull(serial_str, &serial_end, 0);
|
serial_id = std::strtoull(serial_str, &serial_end, 0);
|
||||||
if(*serial_end != '\0' || errno == ERANGE)
|
if(*serial_end != '\0' || errno == ERANGE)
|
||||||
{
|
{
|
||||||
ERR("Unexpected object serial: %s\n", serial_str);
|
ERR("Unexpected object serial: {}", serial_str);
|
||||||
serial_id = info->id;
|
serial_id = info->id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::string name;
|
auto name = std::invoke([nodeName,info]() -> std::string
|
||||||
if(nodeName && *nodeName) name = nodeName;
|
{
|
||||||
else name = "PipeWire node #"+std::to_string(info->id);
|
if(nodeName && *nodeName)
|
||||||
|
return std::string{nodeName};
|
||||||
|
return fmt::format("PipeWire node #{}", info->id);
|
||||||
|
});
|
||||||
|
|
||||||
const char *form_factor{spa_dict_lookup(info->props, PW_KEY_DEVICE_FORM_FACTOR)};
|
const char *form_factor{spa_dict_lookup(info->props, PW_KEY_DEVICE_FORM_FACTOR)};
|
||||||
TRACE("Got %s device \"%s\"%s%s%s\n", AsString(ntype), devName ? devName : "(nil)",
|
TRACE("Got {} device \"{}\"{}{}{}", AsString(ntype), devName ? devName : "(nil)",
|
||||||
form_factor?" (":"", form_factor?form_factor:"", form_factor?")":"");
|
form_factor?" (":"", form_factor?form_factor:"", form_factor?")":"");
|
||||||
TRACE(" \"%s\" = ID %" PRIu64 "\n", name.c_str(), serial_id);
|
TRACE(" \"{}\" = ID {}", name, serial_id);
|
||||||
|
|
||||||
DeviceNode &node = DeviceNode::Add(info->id);
|
DeviceNode &node = DeviceNode::Add(info->id);
|
||||||
node.mSerial = serial_id;
|
node.mSerial = serial_id;
|
||||||
|
|
@ -1087,7 +1092,7 @@ void NodeProxy::paramCallback(int, uint32_t id, uint32_t, uint32_t, const spa_po
|
||||||
DeviceNode *node{DeviceNode::Find(mId)};
|
DeviceNode *node{DeviceNode::Find(mId)};
|
||||||
if(!node) UNLIKELY return;
|
if(!node) UNLIKELY return;
|
||||||
|
|
||||||
TRACE("Device ID %" PRIu64 " %s format%s:\n", node->mSerial,
|
TRACE("Device ID {} {} format{}:", node->mSerial,
|
||||||
(id == SPA_PARAM_EnumFormat) ? "available" : "current",
|
(id == SPA_PARAM_EnumFormat) ? "available" : "current",
|
||||||
(id == SPA_PARAM_EnumFormat) ? "s" : "");
|
(id == SPA_PARAM_EnumFormat) ? "s" : "");
|
||||||
|
|
||||||
|
|
@ -1122,14 +1127,14 @@ auto MetadataProxy::propertyCallback(void*, uint32_t id, const char *key, const
|
||||||
|
|
||||||
if(!type)
|
if(!type)
|
||||||
{
|
{
|
||||||
TRACE("Default %s device cleared\n", isCapture ? "capture" : "playback");
|
TRACE("Default {} device cleared", isCapture ? "capture" : "playback");
|
||||||
if(!isCapture) DefaultSinkDevice.clear();
|
if(!isCapture) DefaultSinkDevice.clear();
|
||||||
else DefaultSourceDevice.clear();
|
else DefaultSourceDevice.clear();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if("Spa:String:JSON"sv != type)
|
if("Spa:String:JSON"sv != type)
|
||||||
{
|
{
|
||||||
ERR("Unexpected %s property type: %s\n", key, type);
|
ERR("Unexpected {} property type: {}", key, type);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1160,8 +1165,8 @@ auto MetadataProxy::propertyCallback(void*, uint32_t id, const char *key, const
|
||||||
auto propValue = get_json_string(&std::get<1>(it));
|
auto propValue = get_json_string(&std::get<1>(it));
|
||||||
if(!propValue) break;
|
if(!propValue) break;
|
||||||
|
|
||||||
TRACE("Got default %s device \"%s\"\n", isCapture ? "capture" : "playback",
|
TRACE("Got default {} device \"{}\"", isCapture ? "capture" : "playback",
|
||||||
propValue->c_str());
|
*propValue);
|
||||||
if(!isCapture && DefaultSinkDevice != *propValue)
|
if(!isCapture && DefaultSinkDevice != *propValue)
|
||||||
{
|
{
|
||||||
if(gEventHandler.mInitDone.load(std::memory_order_relaxed))
|
if(gEventHandler.mInitDone.load(std::memory_order_relaxed))
|
||||||
|
|
@ -1203,28 +1208,28 @@ bool EventManager::init()
|
||||||
mLoop = ThreadMainloop::Create("PWEventThread");
|
mLoop = ThreadMainloop::Create("PWEventThread");
|
||||||
if(!mLoop)
|
if(!mLoop)
|
||||||
{
|
{
|
||||||
ERR("Failed to create PipeWire event thread loop (errno: %d)\n", errno);
|
ERR("Failed to create PipeWire event thread loop (errno: {})", errno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mContext = mLoop.newContext();
|
mContext = mLoop.newContext();
|
||||||
if(!mContext)
|
if(!mContext)
|
||||||
{
|
{
|
||||||
ERR("Failed to create PipeWire event context (errno: %d)\n", errno);
|
ERR("Failed to create PipeWire event context (errno: {})", errno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mCore = PwCorePtr{pw_context_connect(mContext.get(), nullptr, 0)};
|
mCore = PwCorePtr{pw_context_connect(mContext.get(), nullptr, 0)};
|
||||||
if(!mCore)
|
if(!mCore)
|
||||||
{
|
{
|
||||||
ERR("Failed to connect PipeWire event context (errno: %d)\n", errno);
|
ERR("Failed to connect PipeWire event context (errno: {})", errno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mRegistry = PwRegistryPtr{pw_core_get_registry(mCore.get(), PW_VERSION_REGISTRY, 0)};
|
mRegistry = PwRegistryPtr{pw_core_get_registry(mCore.get(), PW_VERSION_REGISTRY, 0)};
|
||||||
if(!mRegistry)
|
if(!mRegistry)
|
||||||
{
|
{
|
||||||
ERR("Failed to get PipeWire event registry (errno: %d)\n", errno);
|
ERR("Failed to get PipeWire event registry (errno: {})", errno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1241,7 +1246,7 @@ bool EventManager::init()
|
||||||
|
|
||||||
if(int res{mLoop.start()})
|
if(int res{mLoop.start()})
|
||||||
{
|
{
|
||||||
ERR("Failed to start PipeWire event thread loop (res: %d)\n", res);
|
ERR("Failed to start PipeWire event thread loop (res: {})", res);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1279,7 +1284,7 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t
|
||||||
if(!isGood)
|
if(!isGood)
|
||||||
{
|
{
|
||||||
if(!al::contains(className, "/Video"sv) && !al::starts_with(className, "Stream/"sv))
|
if(!al::contains(className, "/Video"sv) && !al::starts_with(className, "Stream/"sv))
|
||||||
TRACE("Ignoring node class %s\n", media_class);
|
TRACE("Ignoring node class {}", media_class);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1288,7 +1293,7 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t
|
||||||
version, 0))};
|
version, 0))};
|
||||||
if(!node)
|
if(!node)
|
||||||
{
|
{
|
||||||
ERR("Failed to create node proxy object (errno: %d)\n", errno);
|
ERR("Failed to create node proxy object (errno: {})", errno);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1311,13 +1316,13 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t
|
||||||
|
|
||||||
if("default"sv != data_class)
|
if("default"sv != data_class)
|
||||||
{
|
{
|
||||||
TRACE("Ignoring metadata \"%s\"\n", data_class);
|
TRACE("Ignoring metadata \"{}\"", data_class);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mDefaultMetadata)
|
if(mDefaultMetadata)
|
||||||
{
|
{
|
||||||
ERR("Duplicate default metadata\n");
|
ERR("Duplicate default metadata");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1325,7 +1330,7 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t
|
||||||
type, version, 0))};
|
type, version, 0))};
|
||||||
if(!mdata)
|
if(!mdata)
|
||||||
{
|
{
|
||||||
ERR("Failed to create metadata proxy object (errno: %d)\n", errno);
|
ERR("Failed to create metadata proxy object (errno: {})", errno);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1382,7 +1387,7 @@ spa_audio_info_raw make_spa_info(DeviceBase *device, bool is51rear, use_f32p_e u
|
||||||
case DevFmtFloat: info.format = SPA_AUDIO_FORMAT_F32; break;
|
case DevFmtFloat: info.format = SPA_AUDIO_FORMAT_F32; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
info.rate = device->Frequency;
|
info.rate = device->mSampleRate;
|
||||||
|
|
||||||
al::span<const spa_audio_channel> map{};
|
al::span<const spa_audio_channel> map{};
|
||||||
switch(device->FmtChans)
|
switch(device->FmtChans)
|
||||||
|
|
@ -1432,7 +1437,7 @@ class PipeWirePlayback final : public BackendBase {
|
||||||
PwStreamPtr mStream;
|
PwStreamPtr mStream;
|
||||||
spa_hook mStreamListener{};
|
spa_hook mStreamListener{};
|
||||||
spa_io_rate_match *mRateMatch{};
|
spa_io_rate_match *mRateMatch{};
|
||||||
std::vector<float*> mChannelPtrs;
|
std::vector<void*> mChannelPtrs;
|
||||||
|
|
||||||
static constexpr pw_stream_events CreateEvents()
|
static constexpr pw_stream_events CreateEvents()
|
||||||
{
|
{
|
||||||
|
|
@ -1448,7 +1453,7 @@ class PipeWirePlayback final : public BackendBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PipeWirePlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
explicit PipeWirePlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~PipeWirePlayback() final
|
~PipeWirePlayback() final
|
||||||
{
|
{
|
||||||
/* Stop the mainloop so the stream can be properly destroyed. */
|
/* Stop the mainloop so the stream can be properly destroyed. */
|
||||||
|
|
@ -1492,7 +1497,7 @@ void PipeWirePlayback::outputCallback() noexcept
|
||||||
uint length{mRateMatch ? mRateMatch->size : 0u};
|
uint length{mRateMatch ? mRateMatch->size : 0u};
|
||||||
#endif
|
#endif
|
||||||
/* If no length is specified, use the device's update size as a fallback. */
|
/* If no length is specified, use the device's update size as a fallback. */
|
||||||
if(!length) UNLIKELY length = mDevice->UpdateSize;
|
if(!length) UNLIKELY length = mDevice->mUpdateSize;
|
||||||
|
|
||||||
/* For planar formats, each datas[] seems to contain one channel, so store
|
/* For planar formats, each datas[] seems to contain one channel, so store
|
||||||
* the pointers in an array. Limit the render length in case the available
|
* the pointers in an array. Limit the render length in case the available
|
||||||
|
|
@ -1503,7 +1508,7 @@ void PipeWirePlayback::outputCallback() noexcept
|
||||||
for(const auto &data : datas)
|
for(const auto &data : datas)
|
||||||
{
|
{
|
||||||
length = std::min(length, data.maxsize/uint{sizeof(float)});
|
length = std::min(length, data.maxsize/uint{sizeof(float)});
|
||||||
*chanptr_end = static_cast<float*>(data.data);
|
*chanptr_end = data.data;
|
||||||
++chanptr_end;
|
++chanptr_end;
|
||||||
|
|
||||||
data.chunk->offset = 0;
|
data.chunk->offset = 0;
|
||||||
|
|
@ -1560,7 +1565,7 @@ void PipeWirePlayback::open(std::string_view name)
|
||||||
auto match = std::find_if(devlist.cbegin(), devlist.cend(), match_name);
|
auto match = std::find_if(devlist.cbegin(), devlist.cend(), match_name);
|
||||||
if(match == devlist.cend())
|
if(match == devlist.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};
|
||||||
|
|
||||||
targetid = match->mSerial;
|
targetid = match->mSerial;
|
||||||
devname = match->mName;
|
devname = match->mName;
|
||||||
|
|
@ -1568,15 +1573,15 @@ void PipeWirePlayback::open(std::string_view name)
|
||||||
|
|
||||||
if(!mLoop)
|
if(!mLoop)
|
||||||
{
|
{
|
||||||
const uint count{OpenCount.fetch_add(1, std::memory_order_relaxed)};
|
const auto count = OpenCount.fetch_add(1u, std::memory_order_relaxed);
|
||||||
const std::string thread_name{"ALSoftP" + std::to_string(count)};
|
const auto thread_name = fmt::format("ALSoftP{}", count);
|
||||||
mLoop = ThreadMainloop::Create(thread_name.c_str());
|
mLoop = ThreadMainloop::Create(thread_name.c_str());
|
||||||
if(!mLoop)
|
if(!mLoop)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Failed to create PipeWire mainloop (errno: %d)", errno};
|
"Failed to create PipeWire mainloop (errno: {})", errno};
|
||||||
if(int res{mLoop.start()})
|
if(int res{mLoop.start()})
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Failed to start PipeWire mainloop (res: %d)", res};
|
"Failed to start PipeWire mainloop (res: {})", res};
|
||||||
}
|
}
|
||||||
MainloopUniqueLock mlock{mLoop};
|
MainloopUniqueLock mlock{mLoop};
|
||||||
if(!mContext)
|
if(!mContext)
|
||||||
|
|
@ -1585,14 +1590,14 @@ void PipeWirePlayback::open(std::string_view name)
|
||||||
mContext = mLoop.newContext(cprops);
|
mContext = mLoop.newContext(cprops);
|
||||||
if(!mContext)
|
if(!mContext)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Failed to create PipeWire event context (errno: %d)\n", errno};
|
"Failed to create PipeWire event context (errno: {})\n", errno};
|
||||||
}
|
}
|
||||||
if(!mCore)
|
if(!mCore)
|
||||||
{
|
{
|
||||||
mCore = PwCorePtr{pw_context_connect(mContext.get(), nullptr, 0)};
|
mCore = PwCorePtr{pw_context_connect(mContext.get(), nullptr, 0)};
|
||||||
if(!mCore)
|
if(!mCore)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Failed to connect PipeWire event context (errno: %d)\n", errno};
|
"Failed to connect PipeWire event context (errno: {})\n", errno};
|
||||||
}
|
}
|
||||||
mlock.unlock();
|
mlock.unlock();
|
||||||
|
|
||||||
|
|
@ -1600,9 +1605,9 @@ void PipeWirePlayback::open(std::string_view name)
|
||||||
|
|
||||||
mTargetId = targetid;
|
mTargetId = targetid;
|
||||||
if(!devname.empty())
|
if(!devname.empty())
|
||||||
mDevice->DeviceName = std::move(devname);
|
mDeviceName = std::move(devname);
|
||||||
else
|
else
|
||||||
mDevice->DeviceName = "PipeWire Output"sv;
|
mDeviceName = "PipeWire Output"sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PipeWirePlayback::reset()
|
bool PipeWirePlayback::reset()
|
||||||
|
|
@ -1634,13 +1639,13 @@ bool PipeWirePlayback::reset()
|
||||||
if(!mDevice->Flags.test(FrequencyRequest) && match->mSampleRate > 0)
|
if(!mDevice->Flags.test(FrequencyRequest) && match->mSampleRate > 0)
|
||||||
{
|
{
|
||||||
/* Scale the update size if the sample rate changes. */
|
/* Scale the update size if the sample rate changes. */
|
||||||
const double scale{static_cast<double>(match->mSampleRate) / mDevice->Frequency};
|
const double scale{static_cast<double>(match->mSampleRate) / mDevice->mSampleRate};
|
||||||
const double updatesize{std::round(mDevice->UpdateSize * scale)};
|
const double updatesize{std::round(mDevice->mUpdateSize * scale)};
|
||||||
const double buffersize{std::round(mDevice->BufferSize * scale)};
|
const double buffersize{std::round(mDevice->mBufferSize * scale)};
|
||||||
|
|
||||||
mDevice->Frequency = match->mSampleRate;
|
mDevice->mSampleRate = match->mSampleRate;
|
||||||
mDevice->UpdateSize = static_cast<uint>(std::clamp(updatesize, 64.0, 8192.0));
|
mDevice->mUpdateSize = static_cast<uint>(std::clamp(updatesize, 64.0, 8192.0));
|
||||||
mDevice->BufferSize = static_cast<uint>(std::max(buffersize, 128.0));
|
mDevice->mBufferSize = static_cast<uint>(std::max(buffersize, 128.0));
|
||||||
}
|
}
|
||||||
if(!mDevice->Flags.test(ChannelsRequest) && match->mChannels != InvalidChannelConfig)
|
if(!mDevice->Flags.test(ChannelsRequest) && match->mChannels != InvalidChannelConfig)
|
||||||
mDevice->FmtChans = match->mChannels;
|
mDevice->FmtChans = match->mChannels;
|
||||||
|
|
@ -1652,12 +1657,10 @@ bool PipeWirePlayback::reset()
|
||||||
/* Force planar 32-bit float output for playback. This is what PipeWire
|
/* Force planar 32-bit float output for playback. This is what PipeWire
|
||||||
* handles internally, and it's easier for us too.
|
* handles internally, and it's easier for us too.
|
||||||
*/
|
*/
|
||||||
spa_audio_info_raw info{make_spa_info(mDevice, is51rear, ForceF32Planar)};
|
auto info = spa_audio_info_raw{make_spa_info(mDevice, is51rear, ForceF32Planar)};
|
||||||
|
|
||||||
static constexpr uint32_t pod_buffer_size{1024};
|
auto b = PodDynamicBuilder{};
|
||||||
PodDynamicBuilder b(pod_buffer_size);
|
auto params = as_const_ptr(spa_format_audio_raw_build(b.get(), SPA_PARAM_EnumFormat, &info));
|
||||||
|
|
||||||
const spa_pod *params{spa_format_audio_raw_build(b.get(), SPA_PARAM_EnumFormat, &info)};
|
|
||||||
if(!params)
|
if(!params)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Failed to set PipeWire audio format parameters"};
|
"Failed to set PipeWire audio format parameters"};
|
||||||
|
|
@ -1666,7 +1669,7 @@ bool PipeWirePlayback::reset()
|
||||||
* be useful?
|
* be useful?
|
||||||
*/
|
*/
|
||||||
auto&& binary = GetProcBinary();
|
auto&& binary = GetProcBinary();
|
||||||
const char *appname{binary.fname.length() ? binary.fname.c_str() : "OpenAL Soft"};
|
const char *appname{!binary.fname.empty() ? binary.fname.c_str() : "OpenAL Soft"};
|
||||||
pw_properties *props{pw_properties_new(PW_KEY_NODE_NAME, appname,
|
pw_properties *props{pw_properties_new(PW_KEY_NODE_NAME, appname,
|
||||||
PW_KEY_NODE_DESCRIPTION, appname,
|
PW_KEY_NODE_DESCRIPTION, appname,
|
||||||
PW_KEY_MEDIA_TYPE, "Audio",
|
PW_KEY_MEDIA_TYPE, "Audio",
|
||||||
|
|
@ -1676,11 +1679,11 @@ bool PipeWirePlayback::reset()
|
||||||
nullptr)};
|
nullptr)};
|
||||||
if(!props)
|
if(!props)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Failed to create PipeWire stream properties (errno: %d)", errno};
|
"Failed to create PipeWire stream properties (errno: {})", errno};
|
||||||
|
|
||||||
pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", mDevice->UpdateSize,
|
pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", mDevice->mUpdateSize,
|
||||||
mDevice->Frequency);
|
mDevice->mSampleRate);
|
||||||
pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", mDevice->Frequency);
|
pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", mDevice->mSampleRate);
|
||||||
#ifdef PW_KEY_TARGET_OBJECT
|
#ifdef PW_KEY_TARGET_OBJECT
|
||||||
pw_properties_setf(props, PW_KEY_TARGET_OBJECT, "%" PRIu64, mTargetId);
|
pw_properties_setf(props, PW_KEY_TARGET_OBJECT, "%" PRIu64, mTargetId);
|
||||||
#else
|
#else
|
||||||
|
|
@ -1692,17 +1695,17 @@ bool PipeWirePlayback::reset()
|
||||||
mStream = PwStreamPtr{pw_stream_new(mCore.get(), "Playback Stream", props)};
|
mStream = PwStreamPtr{pw_stream_new(mCore.get(), "Playback Stream", props)};
|
||||||
if(!mStream)
|
if(!mStream)
|
||||||
throw al::backend_exception{al::backend_error::NoDevice,
|
throw al::backend_exception{al::backend_error::NoDevice,
|
||||||
"Failed to create PipeWire stream (errno: %d)", errno};
|
"Failed to create PipeWire stream (errno: {})", errno};
|
||||||
static constexpr pw_stream_events streamEvents{CreateEvents()};
|
static constexpr pw_stream_events streamEvents{CreateEvents()};
|
||||||
pw_stream_add_listener(mStream.get(), &mStreamListener, &streamEvents, this);
|
pw_stream_add_listener(mStream.get(), &mStreamListener, &streamEvents, this);
|
||||||
|
|
||||||
pw_stream_flags flags{PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE
|
pw_stream_flags flags{PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE
|
||||||
| PW_STREAM_FLAG_MAP_BUFFERS};
|
| PW_STREAM_FLAG_MAP_BUFFERS};
|
||||||
if(GetConfigValueBool(mDevice->DeviceName, "pipewire", "rt-mix", false))
|
if(GetConfigValueBool(mDevice->mDeviceName, "pipewire", "rt-mix", false))
|
||||||
flags |= PW_STREAM_FLAG_RT_PROCESS;
|
flags |= PW_STREAM_FLAG_RT_PROCESS;
|
||||||
if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_OUTPUT, PwIdAny, flags, ¶ms, 1)})
|
if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_OUTPUT, PwIdAny, flags, ¶ms, 1)})
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Error connecting PipeWire stream (res: %d)", res};
|
"Error connecting PipeWire stream (res: {})", res};
|
||||||
|
|
||||||
/* Wait for the stream to become paused (ready to start streaming). */
|
/* Wait for the stream to become paused (ready to start streaming). */
|
||||||
plock.wait([stream=mStream.get()]()
|
plock.wait([stream=mStream.get()]()
|
||||||
|
|
@ -1711,12 +1714,12 @@ bool PipeWirePlayback::reset()
|
||||||
pw_stream_state state{pw_stream_get_state(stream, &error)};
|
pw_stream_state state{pw_stream_get_state(stream, &error)};
|
||||||
if(state == PW_STREAM_STATE_ERROR)
|
if(state == PW_STREAM_STATE_ERROR)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Error connecting PipeWire stream: \"%s\"", error};
|
"Error connecting PipeWire stream: \"{}\"", error};
|
||||||
return state == PW_STREAM_STATE_PAUSED;
|
return state == PW_STREAM_STATE_PAUSED;
|
||||||
});
|
});
|
||||||
|
|
||||||
/* TODO: Update mDevice->UpdateSize with the stream's quantum, and
|
/* TODO: Update mDevice->mUpdateSize with the stream's quantum, and
|
||||||
* mDevice->BufferSize with the total known buffering delay from the head
|
* mDevice->mBufferSize with the total known buffering delay from the head
|
||||||
* of this playback stream to the tail of the device output.
|
* of this playback stream to the tail of the device output.
|
||||||
*
|
*
|
||||||
* This info is apparently not available until after the stream starts.
|
* This info is apparently not available until after the stream starts.
|
||||||
|
|
@ -1735,7 +1738,7 @@ void PipeWirePlayback::start()
|
||||||
MainloopUniqueLock plock{mLoop};
|
MainloopUniqueLock plock{mLoop};
|
||||||
if(int res{pw_stream_set_active(mStream.get(), true)})
|
if(int res{pw_stream_set_active(mStream.get(), true)})
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Failed to start PipeWire stream (res: %d)", res};
|
"Failed to start PipeWire stream (res: {})", res};
|
||||||
|
|
||||||
/* Wait for the stream to start playing (would be nice to not, but we need
|
/* Wait for the stream to start playing (would be nice to not, but we need
|
||||||
* the actual update size which is only available after starting).
|
* the actual update size which is only available after starting).
|
||||||
|
|
@ -1746,7 +1749,7 @@ void PipeWirePlayback::start()
|
||||||
pw_stream_state state{pw_stream_get_state(stream, &error)};
|
pw_stream_state state{pw_stream_get_state(stream, &error)};
|
||||||
if(state == PW_STREAM_STATE_ERROR)
|
if(state == PW_STREAM_STATE_ERROR)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"PipeWire stream error: %s", error ? error : "(unknown)"};
|
"PipeWire stream error: {}", error ? error : "(unknown)"};
|
||||||
return state == PW_STREAM_STATE_STREAMING;
|
return state == PW_STREAM_STATE_STREAMING;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1761,7 +1764,7 @@ void PipeWirePlayback::start()
|
||||||
pw_time ptime{};
|
pw_time ptime{};
|
||||||
if(int res{pw_stream_get_time_n(mStream.get(), &ptime, sizeof(ptime))})
|
if(int res{pw_stream_get_time_n(mStream.get(), &ptime, sizeof(ptime))})
|
||||||
{
|
{
|
||||||
ERR("Failed to get PipeWire stream time (res: %d)\n", res);
|
ERR("Failed to get PipeWire stream time (res: {})", res);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1776,11 +1779,11 @@ void PipeWirePlayback::start()
|
||||||
const uint totalbuffers{ptime.avail_buffers + ptime.queued_buffers};
|
const uint totalbuffers{ptime.avail_buffers + ptime.queued_buffers};
|
||||||
|
|
||||||
/* Ensure the delay is in sample frames. */
|
/* Ensure the delay is in sample frames. */
|
||||||
const uint64_t delay{static_cast<uint64_t>(ptime.delay) * mDevice->Frequency *
|
const uint64_t delay{static_cast<uint64_t>(ptime.delay) * mDevice->mSampleRate *
|
||||||
ptime.rate.num / ptime.rate.denom};
|
ptime.rate.num / ptime.rate.denom};
|
||||||
|
|
||||||
mDevice->UpdateSize = updatesize;
|
mDevice->mUpdateSize = updatesize;
|
||||||
mDevice->BufferSize = static_cast<uint>(ptime.buffered + delay +
|
mDevice->mBufferSize = static_cast<uint>(ptime.buffered + delay +
|
||||||
uint64_t{totalbuffers}*updatesize);
|
uint64_t{totalbuffers}*updatesize);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1791,17 +1794,17 @@ void PipeWirePlayback::start()
|
||||||
if(ptime.rate.denom > 0 && updatesize > 0)
|
if(ptime.rate.denom > 0 && updatesize > 0)
|
||||||
{
|
{
|
||||||
/* Ensure the delay is in sample frames. */
|
/* Ensure the delay is in sample frames. */
|
||||||
const uint64_t delay{static_cast<uint64_t>(ptime.delay) * mDevice->Frequency *
|
const uint64_t delay{static_cast<uint64_t>(ptime.delay) * mDevice->mSampleRate *
|
||||||
ptime.rate.num / ptime.rate.denom};
|
ptime.rate.num / ptime.rate.denom};
|
||||||
|
|
||||||
mDevice->UpdateSize = updatesize;
|
mDevice->mUpdateSize = updatesize;
|
||||||
mDevice->BufferSize = static_cast<uint>(delay + updatesize);
|
mDevice->mBufferSize = static_cast<uint>(delay + updatesize);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if(!--wait_count)
|
if(!--wait_count)
|
||||||
{
|
{
|
||||||
ERR("Timeout getting PipeWire stream buffering info\n");
|
ERR("Timeout getting PipeWire stream buffering info");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1815,7 +1818,7 @@ void PipeWirePlayback::stop()
|
||||||
{
|
{
|
||||||
MainloopUniqueLock plock{mLoop};
|
MainloopUniqueLock plock{mLoop};
|
||||||
if(int res{pw_stream_set_active(mStream.get(), false)})
|
if(int res{pw_stream_set_active(mStream.get(), false)})
|
||||||
ERR("Failed to stop PipeWire stream (res: %d)\n", res);
|
ERR("Failed to stop PipeWire stream (res: {})", res);
|
||||||
|
|
||||||
/* Wait for the stream to stop playing. */
|
/* Wait for the stream to stop playing. */
|
||||||
plock.wait([stream=mStream.get()]()
|
plock.wait([stream=mStream.get()]()
|
||||||
|
|
@ -1836,7 +1839,7 @@ ClockLatency PipeWirePlayback::getClockLatency()
|
||||||
{
|
{
|
||||||
MainloopLockGuard looplock{mLoop};
|
MainloopLockGuard looplock{mLoop};
|
||||||
if(int res{pw_stream_get_time_n(mStream.get(), &ptime, sizeof(ptime))})
|
if(int res{pw_stream_get_time_n(mStream.get(), &ptime, sizeof(ptime))})
|
||||||
ERR("Failed to get PipeWire stream time (res: %d)\n", res);
|
ERR("Failed to get PipeWire stream time (res: {})", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now get the mixer time and the CLOCK_MONOTONIC time atomically (i.e. the
|
/* Now get the mixer time and the CLOCK_MONOTONIC time atomically (i.e. the
|
||||||
|
|
@ -1864,7 +1867,7 @@ ClockLatency PipeWirePlayback::getClockLatency()
|
||||||
*/
|
*/
|
||||||
ptime.now = monoclock.count();
|
ptime.now = monoclock.count();
|
||||||
curtic = mixtime;
|
curtic = mixtime;
|
||||||
delay = nanoseconds{seconds{mDevice->BufferSize}} / mDevice->Frequency;
|
delay = nanoseconds{seconds{mDevice->mBufferSize}} / mDevice->mSampleRate;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -1924,7 +1927,7 @@ class PipeWireCapture final : public BackendBase {
|
||||||
PwStreamPtr mStream;
|
PwStreamPtr mStream;
|
||||||
spa_hook mStreamListener{};
|
spa_hook mStreamListener{};
|
||||||
|
|
||||||
RingBufferPtr mRing{};
|
RingBufferPtr mRing;
|
||||||
|
|
||||||
static constexpr pw_stream_events CreateEvents()
|
static constexpr pw_stream_events CreateEvents()
|
||||||
{
|
{
|
||||||
|
|
@ -1938,7 +1941,7 @@ class PipeWireCapture final : public BackendBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PipeWireCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
explicit PipeWireCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~PipeWireCapture() final { if(mLoop) mLoop.stop(); }
|
~PipeWireCapture() final { if(mLoop) mLoop.stop(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -2025,7 +2028,7 @@ void PipeWireCapture::open(std::string_view name)
|
||||||
}
|
}
|
||||||
if(match == devlist.cend())
|
if(match == devlist.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};
|
||||||
|
|
||||||
targetid = match->mSerial;
|
targetid = match->mSerial;
|
||||||
if(match->mType != NodeType::Sink) devname = match->mName;
|
if(match->mType != NodeType::Sink) devname = match->mName;
|
||||||
|
|
@ -2034,15 +2037,15 @@ void PipeWireCapture::open(std::string_view name)
|
||||||
|
|
||||||
if(!mLoop)
|
if(!mLoop)
|
||||||
{
|
{
|
||||||
const uint count{OpenCount.fetch_add(1, std::memory_order_relaxed)};
|
const auto count = OpenCount.fetch_add(1u, std::memory_order_relaxed);
|
||||||
const std::string thread_name{"ALSoftC" + std::to_string(count)};
|
const auto thread_name = fmt::format("ALSoftC{}", count);
|
||||||
mLoop = ThreadMainloop::Create(thread_name.c_str());
|
mLoop = ThreadMainloop::Create(thread_name.c_str());
|
||||||
if(!mLoop)
|
if(!mLoop)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Failed to create PipeWire mainloop (errno: %d)", errno};
|
"Failed to create PipeWire mainloop (errno: {})", errno};
|
||||||
if(int res{mLoop.start()})
|
if(int res{mLoop.start()})
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Failed to start PipeWire mainloop (res: %d)", res};
|
"Failed to start PipeWire mainloop (res: {})", res};
|
||||||
}
|
}
|
||||||
MainloopUniqueLock mlock{mLoop};
|
MainloopUniqueLock mlock{mLoop};
|
||||||
if(!mContext)
|
if(!mContext)
|
||||||
|
|
@ -2051,14 +2054,14 @@ void PipeWireCapture::open(std::string_view name)
|
||||||
mContext = mLoop.newContext(cprops);
|
mContext = mLoop.newContext(cprops);
|
||||||
if(!mContext)
|
if(!mContext)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Failed to create PipeWire event context (errno: %d)\n", errno};
|
"Failed to create PipeWire event context (errno: {})\n", errno};
|
||||||
}
|
}
|
||||||
if(!mCore)
|
if(!mCore)
|
||||||
{
|
{
|
||||||
mCore = PwCorePtr{pw_context_connect(mContext.get(), nullptr, 0)};
|
mCore = PwCorePtr{pw_context_connect(mContext.get(), nullptr, 0)};
|
||||||
if(!mCore)
|
if(!mCore)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Failed to connect PipeWire event context (errno: %d)\n", errno};
|
"Failed to connect PipeWire event context (errno: {})\n", errno};
|
||||||
}
|
}
|
||||||
mlock.unlock();
|
mlock.unlock();
|
||||||
|
|
||||||
|
|
@ -2066,9 +2069,9 @@ void PipeWireCapture::open(std::string_view name)
|
||||||
|
|
||||||
mTargetId = targetid;
|
mTargetId = targetid;
|
||||||
if(!devname.empty())
|
if(!devname.empty())
|
||||||
mDevice->DeviceName = std::move(devname);
|
mDeviceName = std::move(devname);
|
||||||
else
|
else
|
||||||
mDevice->DeviceName = "PipeWire Input"sv;
|
mDeviceName = "PipeWire Input"sv;
|
||||||
|
|
||||||
|
|
||||||
bool is51rear{false};
|
bool is51rear{false};
|
||||||
|
|
@ -2083,19 +2086,16 @@ void PipeWireCapture::open(std::string_view name)
|
||||||
if(match != devlist.cend())
|
if(match != devlist.cend())
|
||||||
is51rear = match->mIs51Rear;
|
is51rear = match->mIs51Rear;
|
||||||
}
|
}
|
||||||
spa_audio_info_raw info{make_spa_info(mDevice, is51rear, UseDevType)};
|
auto info = spa_audio_info_raw{make_spa_info(mDevice, is51rear, UseDevType)};
|
||||||
|
|
||||||
static constexpr uint32_t pod_buffer_size{1024};
|
auto b = PodDynamicBuilder{};
|
||||||
PodDynamicBuilder b(pod_buffer_size);
|
auto params = as_const_ptr(spa_format_audio_raw_build(b.get(), SPA_PARAM_EnumFormat, &info));
|
||||||
|
if(!params)
|
||||||
std::array params{static_cast<const spa_pod*>(spa_format_audio_raw_build(b.get(),
|
|
||||||
SPA_PARAM_EnumFormat, &info))};
|
|
||||||
if(!params[0])
|
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Failed to set PipeWire audio format parameters"};
|
"Failed to set PipeWire audio format parameters"};
|
||||||
|
|
||||||
auto&& binary = GetProcBinary();
|
auto&& binary = GetProcBinary();
|
||||||
const char *appname{binary.fname.length() ? binary.fname.c_str() : "OpenAL Soft"};
|
const char *appname{!binary.fname.empty() ? binary.fname.c_str() : "OpenAL Soft"};
|
||||||
pw_properties *props{pw_properties_new(
|
pw_properties *props{pw_properties_new(
|
||||||
PW_KEY_NODE_NAME, appname,
|
PW_KEY_NODE_NAME, appname,
|
||||||
PW_KEY_NODE_DESCRIPTION, appname,
|
PW_KEY_NODE_DESCRIPTION, appname,
|
||||||
|
|
@ -2106,15 +2106,15 @@ void PipeWireCapture::open(std::string_view name)
|
||||||
nullptr)};
|
nullptr)};
|
||||||
if(!props)
|
if(!props)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Failed to create PipeWire stream properties (errno: %d)", errno};
|
"Failed to create PipeWire stream properties (errno: {})", errno};
|
||||||
|
|
||||||
/* We don't actually care what the latency/update size is, as long as it's
|
/* We don't actually care what the latency/update size is, as long as it's
|
||||||
* reasonable. Unfortunately, when unspecified PipeWire seems to default to
|
* reasonable. Unfortunately, when unspecified PipeWire seems to default to
|
||||||
* around 40ms, which isn't great. So request 20ms instead.
|
* around 40ms, which isn't great. So request 20ms instead.
|
||||||
*/
|
*/
|
||||||
pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", (mDevice->Frequency+25) / 50,
|
pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", (mDevice->mSampleRate+25) / 50,
|
||||||
mDevice->Frequency);
|
mDevice->mSampleRate);
|
||||||
pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", mDevice->Frequency);
|
pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", mDevice->mSampleRate);
|
||||||
#ifdef PW_KEY_TARGET_OBJECT
|
#ifdef PW_KEY_TARGET_OBJECT
|
||||||
pw_properties_setf(props, PW_KEY_TARGET_OBJECT, "%" PRIu64, mTargetId);
|
pw_properties_setf(props, PW_KEY_TARGET_OBJECT, "%" PRIu64, mTargetId);
|
||||||
#else
|
#else
|
||||||
|
|
@ -2125,15 +2125,15 @@ void PipeWireCapture::open(std::string_view name)
|
||||||
mStream = PwStreamPtr{pw_stream_new(mCore.get(), "Capture Stream", props)};
|
mStream = PwStreamPtr{pw_stream_new(mCore.get(), "Capture Stream", props)};
|
||||||
if(!mStream)
|
if(!mStream)
|
||||||
throw al::backend_exception{al::backend_error::NoDevice,
|
throw al::backend_exception{al::backend_error::NoDevice,
|
||||||
"Failed to create PipeWire stream (errno: %d)", errno};
|
"Failed to create PipeWire stream (errno: {})", errno};
|
||||||
static constexpr pw_stream_events streamEvents{CreateEvents()};
|
static constexpr pw_stream_events streamEvents{CreateEvents()};
|
||||||
pw_stream_add_listener(mStream.get(), &mStreamListener, &streamEvents, this);
|
pw_stream_add_listener(mStream.get(), &mStreamListener, &streamEvents, this);
|
||||||
|
|
||||||
constexpr pw_stream_flags Flags{PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE
|
constexpr pw_stream_flags Flags{PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE
|
||||||
| PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS};
|
| PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS};
|
||||||
if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_INPUT, PwIdAny, Flags, params.data(), 1)})
|
if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_INPUT, PwIdAny, Flags, ¶ms, 1)})
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Error connecting PipeWire stream (res: %d)", res};
|
"Error connecting PipeWire stream (res: {})", res};
|
||||||
|
|
||||||
/* Wait for the stream to become paused (ready to start streaming). */
|
/* Wait for the stream to become paused (ready to start streaming). */
|
||||||
plock.wait([stream=mStream.get()]()
|
plock.wait([stream=mStream.get()]()
|
||||||
|
|
@ -2142,7 +2142,7 @@ void PipeWireCapture::open(std::string_view name)
|
||||||
pw_stream_state state{pw_stream_get_state(stream, &error)};
|
pw_stream_state state{pw_stream_get_state(stream, &error)};
|
||||||
if(state == PW_STREAM_STATE_ERROR)
|
if(state == PW_STREAM_STATE_ERROR)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Error connecting PipeWire stream: \"%s\"", error};
|
"Error connecting PipeWire stream: \"{}\"", error};
|
||||||
return state == PW_STREAM_STATE_PAUSED;
|
return state == PW_STREAM_STATE_PAUSED;
|
||||||
});
|
});
|
||||||
plock.unlock();
|
plock.unlock();
|
||||||
|
|
@ -2150,7 +2150,7 @@ void PipeWireCapture::open(std::string_view name)
|
||||||
setDefaultWFXChannelOrder();
|
setDefaultWFXChannelOrder();
|
||||||
|
|
||||||
/* Ensure at least a 100ms capture buffer. */
|
/* Ensure at least a 100ms capture buffer. */
|
||||||
mRing = RingBuffer::Create(std::max(mDevice->Frequency/10u, mDevice->BufferSize),
|
mRing = RingBuffer::Create(std::max(mDevice->mSampleRate/10u, mDevice->mBufferSize),
|
||||||
mDevice->frameSizeFromFmt(), false);
|
mDevice->frameSizeFromFmt(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2160,7 +2160,7 @@ void PipeWireCapture::start()
|
||||||
MainloopUniqueLock plock{mLoop};
|
MainloopUniqueLock plock{mLoop};
|
||||||
if(int res{pw_stream_set_active(mStream.get(), true)})
|
if(int res{pw_stream_set_active(mStream.get(), true)})
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Failed to start PipeWire stream (res: %d)", res};
|
"Failed to start PipeWire stream (res: {})", res};
|
||||||
|
|
||||||
plock.wait([stream=mStream.get()]()
|
plock.wait([stream=mStream.get()]()
|
||||||
{
|
{
|
||||||
|
|
@ -2168,7 +2168,7 @@ void PipeWireCapture::start()
|
||||||
pw_stream_state state{pw_stream_get_state(stream, &error)};
|
pw_stream_state state{pw_stream_get_state(stream, &error)};
|
||||||
if(state == PW_STREAM_STATE_ERROR)
|
if(state == PW_STREAM_STATE_ERROR)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"PipeWire stream error: %s", error ? error : "(unknown)"};
|
"PipeWire stream error: {}", error ? error : "(unknown)"};
|
||||||
return state == PW_STREAM_STATE_STREAMING;
|
return state == PW_STREAM_STATE_STREAMING;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -2177,7 +2177,7 @@ void PipeWireCapture::stop()
|
||||||
{
|
{
|
||||||
MainloopUniqueLock plock{mLoop};
|
MainloopUniqueLock plock{mLoop};
|
||||||
if(int res{pw_stream_set_active(mStream.get(), false)})
|
if(int res{pw_stream_set_active(mStream.get(), false)})
|
||||||
ERR("Failed to stop PipeWire stream (res: %d)\n", res);
|
ERR("Failed to stop PipeWire stream (res: {})", res);
|
||||||
|
|
||||||
plock.wait([stream=mStream.get()]()
|
plock.wait([stream=mStream.get()]()
|
||||||
{ return pw_stream_get_state(stream, nullptr) != PW_STREAM_STATE_STREAMING; });
|
{ return pw_stream_get_state(stream, nullptr) != PW_STREAM_STATE_STREAMING; });
|
||||||
|
|
@ -2200,11 +2200,11 @@ bool PipeWireBackendFactory::init()
|
||||||
const char *version{pw_get_library_version()};
|
const char *version{pw_get_library_version()};
|
||||||
if(!check_version(version))
|
if(!check_version(version))
|
||||||
{
|
{
|
||||||
WARN("PipeWire version \"%s\" too old (%s or newer required)\n", version,
|
WARN("PipeWire version \"{}\" too old ({} or newer required)", version,
|
||||||
pw_get_headers_version());
|
pw_get_headers_version());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
TRACE("Found PipeWire version \"%s\" (%s or newer)\n", version, pw_get_headers_version());
|
TRACE("Found PipeWire version \"{}\" ({} or newer)", version, pw_get_headers_version());
|
||||||
|
|
||||||
pw_init(nullptr, nullptr);
|
pw_init(nullptr, nullptr);
|
||||||
if(!gEventHandler.init())
|
if(!gEventHandler.init())
|
||||||
|
|
@ -2217,7 +2217,7 @@ bool PipeWireBackendFactory::init()
|
||||||
/* TODO: Temporary warning, until PipeWire gets a proper way to report
|
/* TODO: Temporary warning, until PipeWire gets a proper way to report
|
||||||
* audio support.
|
* audio support.
|
||||||
*/
|
*/
|
||||||
WARN("No audio support detected in PipeWire. See the PipeWire options in alsoftrc.sample if this is wrong.\n");
|
WARN("No audio support detected in PipeWire. See the PipeWire options in alsoftrc.sample if this is wrong.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -20,27 +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 {
|
||||||
|
|
||||||
#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);
|
||||||
|
|
@ -75,8 +73,8 @@ MAKE_FUNC(Pa_GetStreamInfo);
|
||||||
|
|
||||||
struct DeviceEntry {
|
struct DeviceEntry {
|
||||||
std::string mName;
|
std::string mName;
|
||||||
bool mIsPlayback{};
|
uint mPlaybackChannels{};
|
||||||
bool mIsCapture{};
|
uint mCaptureChannels{};
|
||||||
};
|
};
|
||||||
std::vector<DeviceEntry> DeviceNames;
|
std::vector<DeviceEntry> DeviceNames;
|
||||||
|
|
||||||
|
|
@ -85,7 +83,7 @@ void EnumerateDevices()
|
||||||
const auto devcount = Pa_GetDeviceCount();
|
const auto devcount = Pa_GetDeviceCount();
|
||||||
if(devcount < 0)
|
if(devcount < 0)
|
||||||
{
|
{
|
||||||
ERR("Error getting device count: %s\n", Pa_GetErrorText(devcount));
|
ERR("Error getting device count: {}", Pa_GetErrorText(devcount));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,44 +93,42 @@ void EnumerateDevices()
|
||||||
{
|
{
|
||||||
if(auto info = Pa_GetDeviceInfo(idx); info && info->name)
|
if(auto info = Pa_GetDeviceInfo(idx); info && info->name)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
|
||||||
entry.mName = "OpenAL Soft on "+std::string{info->name};
|
|
||||||
#else
|
|
||||||
entry.mName = info->name;
|
entry.mName = info->name;
|
||||||
#endif
|
entry.mPlaybackChannels = static_cast<uint>(std::max(info->maxOutputChannels, 0));
|
||||||
entry.mIsPlayback = (info->maxOutputChannels > 0);
|
entry.mCaptureChannels = static_cast<uint>(std::max(info->maxInputChannels, 0));
|
||||||
entry.mIsCapture = (info->maxInputChannels > 0);
|
TRACE("Device {} \"{}\": {} playback, {} capture channels", idx, entry.mName,
|
||||||
TRACE("Device %d \"%s\": %d playback, %d capture channels\n", idx, entry.mName.c_str(),
|
|
||||||
info->maxOutputChannels, info->maxInputChannels);
|
info->maxOutputChannels, info->maxInputChannels);
|
||||||
}
|
}
|
||||||
++idx;
|
++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{};
|
StreamParamsExt mParams{};
|
||||||
DevFmtChannels mChannelConfig{};
|
PaDeviceIndex mDeviceIdx{-1};
|
||||||
uint mAmbiOrder{};
|
|
||||||
uint mUpdateSize{0u};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,12 +142,62 @@ int PortPlayback::writeCallback(const void*, void *outputBuffer, unsigned long f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PortPlayback::createStream(PaDeviceIndex deviceid)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
|
||||||
|
const PaStreamCallbackFlags statusFlags, void *userData) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<PortPlayback*>(userData)->writeCallback(inputBuffer, outputBuffer,
|
||||||
|
framesPerBuffer, timeInfo, statusFlags);
|
||||||
|
};
|
||||||
|
while(PaError err{Pa_OpenStream(&mStream, nullptr, ¶ms, srate, params.updateSize, paNoFlag,
|
||||||
|
writeCallback, this)})
|
||||||
|
{
|
||||||
|
if(params.updateSize != DefaultUpdateSize)
|
||||||
|
params.updateSize = DefaultUpdateSize;
|
||||||
|
else if(srate != 48000u)
|
||||||
|
srate = (srate != 44100u) ? 44100u : 48000u;
|
||||||
|
else if(params.sampleFormat != paInt16)
|
||||||
|
params.sampleFormat = paInt16;
|
||||||
|
else if(params.channelCount != 2)
|
||||||
|
params.channelCount = 2;
|
||||||
|
else
|
||||||
|
throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: {}",
|
||||||
|
Pa_GetErrorText(err)};
|
||||||
|
}
|
||||||
|
|
||||||
|
mParams = params;
|
||||||
|
}
|
||||||
|
|
||||||
void PortPlayback::open(std::string_view name)
|
void PortPlayback::open(std::string_view name)
|
||||||
{
|
{
|
||||||
if(DeviceNames.empty())
|
if(DeviceNames.empty())
|
||||||
EnumerateDevices();
|
EnumerateDevices();
|
||||||
|
|
||||||
int deviceid{-1};
|
PaDeviceIndex deviceid{-1};
|
||||||
if(name.empty())
|
if(name.empty())
|
||||||
{
|
{
|
||||||
if(auto devidopt = ConfigValueInt({}, "port", "device"))
|
if(auto devidopt = ConfigValueInt({}, "port", "device"))
|
||||||
|
|
@ -163,101 +209,65 @@ void PortPlayback::open(std::string_view name)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto iter = std::find_if(DeviceNames.cbegin(), DeviceNames.cend(),
|
auto iter = std::find_if(DeviceNames.cbegin(), DeviceNames.cend(),
|
||||||
[name](const DeviceEntry &entry) { return entry.mIsPlayback && name == entry.mName; });
|
[name](const DeviceEntry &entry)
|
||||||
|
{ return entry.mPlaybackChannels > 0 && name == entry.mName; });
|
||||||
if(iter == DeviceNames.cend())
|
if(iter == DeviceNames.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};
|
||||||
deviceid = static_cast<int>(std::distance(DeviceNames.cbegin(), iter));
|
deviceid = static_cast<int>(std::distance(DeviceNames.cbegin(), iter));
|
||||||
}
|
}
|
||||||
|
|
||||||
PaStreamParameters params{};
|
createStream(deviceid);
|
||||||
params.device = deviceid;
|
mDeviceIdx = deviceid;
|
||||||
params.suggestedLatency = mDevice->BufferSize / static_cast<double>(mDevice->Frequency);
|
|
||||||
params.hostApiSpecificStreamInfo = nullptr;
|
|
||||||
|
|
||||||
mChannelConfig = mDevice->FmtChans;
|
mDeviceName = name;
|
||||||
mAmbiOrder = mDevice->mAmbiOrder;
|
|
||||||
params.channelCount = static_cast<int>(mDevice->channelsFromFmt());
|
|
||||||
|
|
||||||
switch(mDevice->FmtType)
|
|
||||||
{
|
|
||||||
case DevFmtByte:
|
|
||||||
params.sampleFormat = paInt8;
|
|
||||||
break;
|
|
||||||
case DevFmtUByte:
|
|
||||||
params.sampleFormat = paUInt8;
|
|
||||||
break;
|
|
||||||
case DevFmtUShort:
|
|
||||||
/* fall-through */
|
|
||||||
case DevFmtShort:
|
|
||||||
params.sampleFormat = paInt16;
|
|
||||||
break;
|
|
||||||
case DevFmtUInt:
|
|
||||||
/* fall-through */
|
|
||||||
case DevFmtInt:
|
|
||||||
params.sampleFormat = paInt32;
|
|
||||||
break;
|
|
||||||
case DevFmtFloat:
|
|
||||||
params.sampleFormat = paFloat32;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto writeCallback = [](const void *inputBuffer, void *outputBuffer,
|
|
||||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
|
|
||||||
const PaStreamCallbackFlags statusFlags, void *userData) noexcept
|
|
||||||
{
|
|
||||||
return static_cast<PortPlayback*>(userData)->writeCallback(inputBuffer, outputBuffer,
|
|
||||||
framesPerBuffer, timeInfo, statusFlags);
|
|
||||||
};
|
|
||||||
PaStream *stream{};
|
|
||||||
while(PaError err{Pa_OpenStream(&stream, nullptr, ¶ms, mDevice->Frequency,
|
|
||||||
mDevice->UpdateSize, paNoFlag, writeCallback, this)})
|
|
||||||
{
|
|
||||||
if(params.sampleFormat != paFloat32)
|
|
||||||
throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s",
|
|
||||||
Pa_GetErrorText(err)};
|
|
||||||
params.sampleFormat = paInt16;
|
|
||||||
}
|
|
||||||
|
|
||||||
Pa_CloseStream(mStream);
|
|
||||||
mStream = stream;
|
|
||||||
mParams = params;
|
|
||||||
mUpdateSize = mDevice->UpdateSize;
|
|
||||||
|
|
||||||
mDevice->DeviceName = name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PortPlayback::reset()
|
bool PortPlayback::reset()
|
||||||
{
|
{
|
||||||
|
if(mStream)
|
||||||
|
{
|
||||||
|
auto err = Pa_CloseStream(mStream);
|
||||||
|
if(err != paNoError)
|
||||||
|
ERR("Error closing stream: {}", Pa_GetErrorText(err));
|
||||||
|
mStream = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
createStream(mDeviceIdx);
|
||||||
|
|
||||||
|
switch(mParams.sampleFormat)
|
||||||
|
{
|
||||||
|
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)};
|
const PaStreamInfo *streamInfo{Pa_GetStreamInfo(mStream)};
|
||||||
mDevice->Frequency = static_cast<uint>(streamInfo->sampleRate);
|
mDevice->mSampleRate = static_cast<uint>(std::lround(streamInfo->sampleRate));
|
||||||
mDevice->FmtChans = mChannelConfig;
|
mDevice->mUpdateSize = mParams.updateSize;
|
||||||
mDevice->mAmbiOrder = mAmbiOrder;
|
mDevice->mBufferSize = mDevice->mUpdateSize * 2;
|
||||||
mDevice->UpdateSize = mUpdateSize;
|
|
||||||
mDevice->BufferSize = mUpdateSize * 2;
|
|
||||||
if(streamInfo->outputLatency > 0.0f)
|
if(streamInfo->outputLatency > 0.0f)
|
||||||
{
|
{
|
||||||
const double sampleLatency{streamInfo->outputLatency * streamInfo->sampleRate};
|
const double sampleLatency{streamInfo->outputLatency * streamInfo->sampleRate};
|
||||||
TRACE("Reported stream latency: %f sec (%f samples)\n", streamInfo->outputLatency,
|
TRACE("Reported stream latency: {:f} sec ({:f} samples)", streamInfo->outputLatency,
|
||||||
sampleLatency);
|
sampleLatency);
|
||||||
mDevice->BufferSize = static_cast<uint>(std::clamp(sampleLatency,
|
mDevice->mBufferSize = static_cast<uint>(std::clamp(sampleLatency,
|
||||||
double(mDevice->BufferSize), double{std::numeric_limits<int>::max()}));
|
double(mDevice->mBufferSize), double{std::numeric_limits<int>::max()}));
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
{
|
|
||||||
ERR("Unexpected sample format: 0x%lx\n", mParams.sampleFormat);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setDefaultChannelOrder();
|
setDefaultChannelOrder();
|
||||||
|
|
@ -268,19 +278,19 @@ bool PortPlayback::reset()
|
||||||
void PortPlayback::start()
|
void PortPlayback::start()
|
||||||
{
|
{
|
||||||
if(const PaError err{Pa_StartStream(mStream)}; err != paNoError)
|
if(const PaError err{Pa_StartStream(mStream)}; err != paNoError)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to start playback: %s",
|
throw al::backend_exception{al::backend_error::DeviceError, "Failed to start playback: {}",
|
||||||
Pa_GetErrorText(err)};
|
Pa_GetErrorText(err)};
|
||||||
}
|
}
|
||||||
|
|
||||||
void PortPlayback::stop()
|
void PortPlayback::stop()
|
||||||
{
|
{
|
||||||
if(PaError err{Pa_StopStream(mStream)}; err != paNoError)
|
if(PaError err{Pa_StopStream(mStream)}; err != paNoError)
|
||||||
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
|
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,
|
||||||
|
|
@ -302,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -332,14 +342,15 @@ void PortCapture::open(std::string_view name)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto iter = std::find_if(DeviceNames.cbegin(), DeviceNames.cend(),
|
auto iter = std::find_if(DeviceNames.cbegin(), DeviceNames.cend(),
|
||||||
[name](const DeviceEntry &entry) { return entry.mIsCapture && name == entry.mName; });
|
[name](const DeviceEntry &entry)
|
||||||
|
{ return entry.mCaptureChannels > 0 && name == entry.mName; });
|
||||||
if(iter == DeviceNames.cend())
|
if(iter == DeviceNames.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};
|
||||||
deviceid = static_cast<int>(std::distance(DeviceNames.cbegin(), iter));
|
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);
|
||||||
|
|
@ -367,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());
|
||||||
|
|
@ -379,13 +390,13 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -393,13 +404,13 @@ void PortCapture::start()
|
||||||
{
|
{
|
||||||
if(const PaError err{Pa_StartStream(mStream)}; 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()
|
||||||
{
|
{
|
||||||
if(PaError err{Pa_StopStream(mStream)}; err != paNoError)
|
if(PaError err{Pa_StopStream(mStream)}; err != paNoError)
|
||||||
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
|
ERR("Error stopping stream: {}", Pa_GetErrorText(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -414,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
|
||||||
|
|
@ -457,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;
|
||||||
|
|
@ -467,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
|
||||||
|
|
@ -493,7 +504,7 @@ auto PortBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||||
|
|
||||||
for(size_t i{0};i < DeviceNames.size();++i)
|
for(size_t i{0};i < DeviceNames.size();++i)
|
||||||
{
|
{
|
||||||
if(DeviceNames[i].mIsPlayback)
|
if(DeviceNames[i].mPlaybackChannels > 0)
|
||||||
{
|
{
|
||||||
if(defaultid >= 0 && static_cast<uint>(defaultid) == i)
|
if(defaultid >= 0 && static_cast<uint>(defaultid) == i)
|
||||||
devices.emplace(devices.cbegin(), DeviceNames[i].mName);
|
devices.emplace(devices.cbegin(), DeviceNames[i].mName);
|
||||||
|
|
@ -511,7 +522,7 @@ auto PortBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||||
|
|
||||||
for(size_t i{0};i < DeviceNames.size();++i)
|
for(size_t i{0};i < DeviceNames.size();++i)
|
||||||
{
|
{
|
||||||
if(DeviceNames[i].mIsCapture)
|
if(DeviceNames[i].mCaptureChannels > 0)
|
||||||
{
|
{
|
||||||
if(defaultid >= 0 && static_cast<uint>(defaultid) == i)
|
if(defaultid >= 0 && static_cast<uint>(defaultid) == i)
|
||||||
devices.emplace(devices.cbegin(), DeviceNames[i].mName);
|
devices.emplace(devices.cbegin(), DeviceNames[i].mName);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef BACKENDS_PORTAUDIO_H
|
#ifndef BACKENDS_PORTAUDIO_HPP
|
||||||
#define BACKENDS_PORTAUDIO_H
|
#define BACKENDS_PORTAUDIO_HPP
|
||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
|
|
||||||
|
|
@ -16,4 +16,4 @@ public:
|
||||||
static auto getFactory() -> BackendFactory&;
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_PORTAUDIO_H */
|
#endif /* BACKENDS_PORTAUDIO_HPP */
|
||||||
|
|
@ -32,7 +32,6 @@
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <exception>
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
@ -43,13 +42,14 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "alc/alconfig.h"
|
#include "alc/alconfig.h"
|
||||||
|
#include "alnumeric.h"
|
||||||
#include "alspan.h"
|
#include "alspan.h"
|
||||||
#include "alstring.h"
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "core/devformat.h"
|
#include "core/devformat.h"
|
||||||
#include "core/device.h"
|
#include "core/device.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "dynload.h"
|
#include "dynload.h"
|
||||||
|
#include "fmt/core.h"
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
#include "strutils.h"
|
#include "strutils.h"
|
||||||
|
|
||||||
|
|
@ -58,9 +58,10 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using namespace std::string_view_literals;
|
||||||
using uint = unsigned int;
|
using uint = unsigned int;
|
||||||
|
|
||||||
#ifdef HAVE_DYNLOAD
|
#if HAVE_DYNLOAD
|
||||||
#define PULSE_FUNCS(MAGIC) \
|
#define PULSE_FUNCS(MAGIC) \
|
||||||
MAGIC(pa_context_new); \
|
MAGIC(pa_context_new); \
|
||||||
MAGIC(pa_context_unref); \
|
MAGIC(pa_context_unref); \
|
||||||
|
|
@ -72,8 +73,10 @@ using uint = unsigned int;
|
||||||
MAGIC(pa_context_errno); \
|
MAGIC(pa_context_errno); \
|
||||||
MAGIC(pa_context_connect); \
|
MAGIC(pa_context_connect); \
|
||||||
MAGIC(pa_context_get_server_info); \
|
MAGIC(pa_context_get_server_info); \
|
||||||
|
MAGIC(pa_context_get_sink_info_by_index); \
|
||||||
MAGIC(pa_context_get_sink_info_by_name); \
|
MAGIC(pa_context_get_sink_info_by_name); \
|
||||||
MAGIC(pa_context_get_sink_info_list); \
|
MAGIC(pa_context_get_sink_info_list); \
|
||||||
|
MAGIC(pa_context_get_source_info_by_index); \
|
||||||
MAGIC(pa_context_get_source_info_by_name); \
|
MAGIC(pa_context_get_source_info_by_name); \
|
||||||
MAGIC(pa_context_get_source_info_list); \
|
MAGIC(pa_context_get_source_info_list); \
|
||||||
MAGIC(pa_stream_new); \
|
MAGIC(pa_stream_new); \
|
||||||
|
|
@ -145,8 +148,10 @@ PULSE_FUNCS(MAKE_FUNC)
|
||||||
#define pa_context_errno ppa_context_errno
|
#define pa_context_errno ppa_context_errno
|
||||||
#define pa_context_connect ppa_context_connect
|
#define pa_context_connect ppa_context_connect
|
||||||
#define pa_context_get_server_info ppa_context_get_server_info
|
#define pa_context_get_server_info ppa_context_get_server_info
|
||||||
|
#define pa_context_get_sink_info_by_index ppa_context_get_sink_info_by_index
|
||||||
#define pa_context_get_sink_info_by_name ppa_context_get_sink_info_by_name
|
#define pa_context_get_sink_info_by_name ppa_context_get_sink_info_by_name
|
||||||
#define pa_context_get_sink_info_list ppa_context_get_sink_info_list
|
#define pa_context_get_sink_info_list ppa_context_get_sink_info_list
|
||||||
|
#define pa_context_get_source_info_by_index ppa_context_get_source_info_by_index
|
||||||
#define pa_context_get_source_info_by_name ppa_context_get_source_info_by_name
|
#define pa_context_get_source_info_by_name ppa_context_get_source_info_by_name
|
||||||
#define pa_context_get_source_info_list ppa_context_get_source_info_list
|
#define pa_context_get_source_info_list ppa_context_get_source_info_list
|
||||||
#define pa_stream_new ppa_stream_new
|
#define pa_stream_new ppa_stream_new
|
||||||
|
|
@ -252,7 +257,7 @@ constexpr pa_channel_map MonoChanMap{
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* *grumble* Don't use enums for bitflags. */
|
/* NOLINTBEGIN(*EnumCastOutOfRange) *grumble* Don't use enums for bitflags. */
|
||||||
constexpr pa_stream_flags_t operator|(pa_stream_flags_t lhs, pa_stream_flags_t rhs)
|
constexpr pa_stream_flags_t operator|(pa_stream_flags_t lhs, pa_stream_flags_t rhs)
|
||||||
{ return pa_stream_flags_t(lhs | al::to_underlying(rhs)); }
|
{ return pa_stream_flags_t(lhs | al::to_underlying(rhs)); }
|
||||||
constexpr pa_stream_flags_t& operator|=(pa_stream_flags_t &lhs, pa_stream_flags_t rhs)
|
constexpr pa_stream_flags_t& operator|=(pa_stream_flags_t &lhs, pa_stream_flags_t rhs)
|
||||||
|
|
@ -278,11 +283,13 @@ constexpr pa_context_flags_t& operator|=(pa_context_flags_t &lhs, pa_context_fla
|
||||||
|
|
||||||
constexpr pa_subscription_mask_t operator|(pa_subscription_mask_t lhs, pa_subscription_mask_t rhs)
|
constexpr pa_subscription_mask_t operator|(pa_subscription_mask_t lhs, pa_subscription_mask_t rhs)
|
||||||
{ return pa_subscription_mask_t(lhs | al::to_underlying(rhs)); }
|
{ return pa_subscription_mask_t(lhs | al::to_underlying(rhs)); }
|
||||||
|
/* NOLINTEND(*EnumCastOutOfRange) */
|
||||||
|
|
||||||
|
|
||||||
struct DevMap {
|
struct DevMap {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string device_name;
|
std::string device_name;
|
||||||
|
uint32_t index{};
|
||||||
};
|
};
|
||||||
|
|
||||||
bool checkName(const al::span<const DevMap> list, const std::string &name)
|
bool checkName(const al::span<const DevMap> list, const std::string &name)
|
||||||
|
|
@ -294,6 +301,9 @@ bool checkName(const al::span<const DevMap> list, const std::string &name)
|
||||||
std::vector<DevMap> PlaybackDevices;
|
std::vector<DevMap> PlaybackDevices;
|
||||||
std::vector<DevMap> CaptureDevices;
|
std::vector<DevMap> CaptureDevices;
|
||||||
|
|
||||||
|
std::string DefaultPlaybackDevName;
|
||||||
|
std::string DefaultCaptureDevName;
|
||||||
|
|
||||||
|
|
||||||
/* Global flags and properties */
|
/* Global flags and properties */
|
||||||
pa_context_flags_t pulse_ctx_flags;
|
pa_context_flags_t pulse_ctx_flags;
|
||||||
|
|
@ -344,6 +354,32 @@ public:
|
||||||
void close(pa_stream *stream=nullptr);
|
void close(pa_stream *stream=nullptr);
|
||||||
|
|
||||||
|
|
||||||
|
void updateDefaultDevice(pa_context*, const pa_server_info *info) const
|
||||||
|
{
|
||||||
|
auto default_sink = info->default_sink_name ? std::string_view{info->default_sink_name}
|
||||||
|
: std::string_view{};
|
||||||
|
auto default_src = info->default_source_name ? std::string_view{info->default_source_name}
|
||||||
|
: std::string_view{};
|
||||||
|
|
||||||
|
if(default_sink != DefaultPlaybackDevName)
|
||||||
|
{
|
||||||
|
TRACE("Default playback device: {}", default_sink);
|
||||||
|
DefaultPlaybackDevName = default_sink;
|
||||||
|
|
||||||
|
const auto msg = fmt::format("Default playback device changed: {}", default_sink);
|
||||||
|
alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback, msg);
|
||||||
|
}
|
||||||
|
if(default_src != DefaultCaptureDevName)
|
||||||
|
{
|
||||||
|
TRACE("Default capture device: {}", default_src);
|
||||||
|
DefaultCaptureDevName = default_src;
|
||||||
|
|
||||||
|
const auto msg = fmt::format("Default capture device changed: {}", default_src);
|
||||||
|
alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture, msg);
|
||||||
|
}
|
||||||
|
signal();
|
||||||
|
}
|
||||||
|
|
||||||
void deviceSinkCallback(pa_context*, const pa_sink_info *info, int eol) const noexcept
|
void deviceSinkCallback(pa_context*, const pa_sink_info *info, int eol) const noexcept
|
||||||
{
|
{
|
||||||
if(eol)
|
if(eol)
|
||||||
|
|
@ -361,18 +397,18 @@ public:
|
||||||
/* Make sure the display name (description) is unique. Append a number
|
/* Make sure the display name (description) is unique. Append a number
|
||||||
* counter as needed.
|
* counter as needed.
|
||||||
*/
|
*/
|
||||||
int count{1};
|
auto count = 1;
|
||||||
std::string newname{info->description};
|
auto newname = std::string{info->description};
|
||||||
while(checkName(PlaybackDevices, newname))
|
while(checkName(PlaybackDevices, newname))
|
||||||
{
|
newname = fmt::format("{} #{}", info->description, ++count);
|
||||||
newname = info->description;
|
|
||||||
newname += " #";
|
|
||||||
newname += std::to_string(++count);
|
|
||||||
}
|
|
||||||
PlaybackDevices.emplace_back(DevMap{std::move(newname), info->name});
|
|
||||||
DevMap &newentry = PlaybackDevices.back();
|
|
||||||
|
|
||||||
TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str());
|
const auto &newentry = PlaybackDevices.emplace_back(DevMap{std::move(newname),
|
||||||
|
info->name, info->index});
|
||||||
|
TRACE("Got device \"{}\", \"{}\" ({})", newentry.name, newentry.device_name,
|
||||||
|
newentry.index);
|
||||||
|
|
||||||
|
const auto msg = fmt::format("Device added: {}", newentry.device_name);
|
||||||
|
alc::Event(alc::EventType::DeviceAdded, alc::DeviceType::Playback, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void deviceSourceCallback(pa_context*, const pa_source_info *info, int eol) const noexcept
|
void deviceSourceCallback(pa_context*, const pa_source_info *info, int eol) const noexcept
|
||||||
|
|
@ -392,22 +428,77 @@ public:
|
||||||
/* Make sure the display name (description) is unique. Append a number
|
/* Make sure the display name (description) is unique. Append a number
|
||||||
* counter as needed.
|
* counter as needed.
|
||||||
*/
|
*/
|
||||||
int count{1};
|
auto count = 1;
|
||||||
std::string newname{info->description};
|
auto newname = std::string{info->description};
|
||||||
while(checkName(CaptureDevices, newname))
|
while(checkName(CaptureDevices, newname))
|
||||||
{
|
newname = fmt::format("{} #{}", info->description, ++count);
|
||||||
newname = info->description;
|
|
||||||
newname += " #";
|
|
||||||
newname += std::to_string(++count);
|
|
||||||
}
|
|
||||||
CaptureDevices.emplace_back(DevMap{std::move(newname), info->name});
|
|
||||||
DevMap &newentry = CaptureDevices.back();
|
|
||||||
|
|
||||||
TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str());
|
const auto &newentry = CaptureDevices.emplace_back(DevMap{std::move(newname), info->name,
|
||||||
|
info->index});
|
||||||
|
TRACE("Got device \"{}\", \"{}\" ({})", newentry.name, newentry.device_name,
|
||||||
|
newentry.index);
|
||||||
|
|
||||||
|
const auto msg = fmt::format("Device added: {}", newentry.device_name);
|
||||||
|
alc::Event(alc::EventType::DeviceAdded, alc::DeviceType::Capture, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void probePlaybackDevices();
|
void eventCallback(pa_context *context, pa_subscription_event_type_t t, uint32_t idx) noexcept
|
||||||
void probeCaptureDevices();
|
{
|
||||||
|
const auto eventFacility = (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK);
|
||||||
|
const auto eventType = (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK);
|
||||||
|
|
||||||
|
if(eventFacility == PA_SUBSCRIPTION_EVENT_SERVER
|
||||||
|
&& eventType == PA_SUBSCRIPTION_EVENT_CHANGE)
|
||||||
|
{
|
||||||
|
static constexpr auto server_cb = [](pa_context *ctx, const pa_server_info *info,
|
||||||
|
void *pdata) noexcept
|
||||||
|
{ return static_cast<PulseMainloop*>(pdata)->updateDefaultDevice(ctx, info); };
|
||||||
|
auto *op = pa_context_get_server_info(context, server_cb, this);
|
||||||
|
if(op) pa_operation_unref(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(eventFacility != PA_SUBSCRIPTION_EVENT_SINK
|
||||||
|
&& eventFacility != PA_SUBSCRIPTION_EVENT_SOURCE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto devtype = (eventFacility == PA_SUBSCRIPTION_EVENT_SINK)
|
||||||
|
? alc::DeviceType::Playback : alc::DeviceType::Capture;
|
||||||
|
|
||||||
|
if(eventType == PA_SUBSCRIPTION_EVENT_NEW)
|
||||||
|
{
|
||||||
|
if(eventFacility == PA_SUBSCRIPTION_EVENT_SINK)
|
||||||
|
{
|
||||||
|
static constexpr auto devcallback = [](pa_context *ctx, const pa_sink_info *info,
|
||||||
|
int eol, void *pdata) noexcept
|
||||||
|
{ return static_cast<PulseMainloop*>(pdata)->deviceSinkCallback(ctx, info, eol); };
|
||||||
|
auto *op = pa_context_get_sink_info_by_index(context, idx, devcallback, this);
|
||||||
|
if(op) pa_operation_unref(op);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static constexpr auto devcallback = [](pa_context *ctx, const pa_source_info *info,
|
||||||
|
int eol, void *pdata) noexcept
|
||||||
|
{ return static_cast<PulseMainloop*>(pdata)->deviceSourceCallback(ctx,info,eol); };
|
||||||
|
auto *op = pa_context_get_source_info_by_index(context, idx, devcallback, this);
|
||||||
|
if(op) pa_operation_unref(op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(eventType == PA_SUBSCRIPTION_EVENT_REMOVE)
|
||||||
|
{
|
||||||
|
auto find_index = [idx](const DevMap &entry) noexcept { return entry.index == idx; };
|
||||||
|
|
||||||
|
auto &devlist = (eventFacility == PA_SUBSCRIPTION_EVENT_SINK)
|
||||||
|
? PlaybackDevices : CaptureDevices;
|
||||||
|
auto iter = std::find_if(devlist.cbegin(), devlist.cend(), find_index);
|
||||||
|
if(iter != devlist.cend())
|
||||||
|
{
|
||||||
|
devlist.erase(iter);
|
||||||
|
|
||||||
|
const auto msg = fmt::format("Device removed: {}", idx);
|
||||||
|
alc::Event(alc::EventType::DeviceRemoved, devtype, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
friend struct MainloopUniqueLock;
|
friend struct MainloopUniqueLock;
|
||||||
};
|
};
|
||||||
|
|
@ -434,36 +525,38 @@ struct MainloopUniqueLock : public std::unique_lock<PulseMainloop> {
|
||||||
|
|
||||||
void setEventHandler()
|
void setEventHandler()
|
||||||
{
|
{
|
||||||
pa_operation *op{pa_context_subscribe(mutex()->mContext,
|
auto *context = mutex()->mContext;
|
||||||
PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE,
|
|
||||||
[](pa_context*, int, void *pdata) noexcept
|
/* Watch for device added/removed and server changed events. */
|
||||||
{ static_cast<PulseMainloop*>(pdata)->signal(); },
|
static constexpr auto submask = PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE
|
||||||
mutex())};
|
| PA_SUBSCRIPTION_MASK_SERVER;
|
||||||
|
static constexpr auto do_signal = [](pa_context*, int, void *pdata) noexcept
|
||||||
|
{ static_cast<PulseMainloop*>(pdata)->signal(); };
|
||||||
|
auto *op = pa_context_subscribe(context, submask, do_signal, mutex());
|
||||||
waitForOperation(op);
|
waitForOperation(op);
|
||||||
|
|
||||||
/* Watch for device added/removed events.
|
static constexpr auto handler = [](pa_context *ctx, pa_subscription_event_type_t t,
|
||||||
*
|
uint32_t index, void *pdata) noexcept
|
||||||
* TODO: Also track the "default" device, in as much as PulseAudio has
|
{ return static_cast<PulseMainloop*>(pdata)->eventCallback(ctx, t, index); };
|
||||||
* the concept of a default device (whatever device is opened when not
|
pa_context_set_subscribe_callback(context, handler, mutex());
|
||||||
* specifying a specific sink or source name). There doesn't seem to be
|
|
||||||
* an event for this.
|
/* Fill in the initial device lists, and get the defaults. */
|
||||||
*/
|
auto sink_callback = [](pa_context *ctx, const pa_sink_info *info, int eol, void *pdata) noexcept
|
||||||
auto handler = [](pa_context*, pa_subscription_event_type_t t, uint32_t, void*) noexcept
|
{ return static_cast<PulseMainloop*>(pdata)->deviceSinkCallback(ctx, info, eol); };
|
||||||
{
|
|
||||||
const auto eventFacility = (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK);
|
auto src_callback = [](pa_context *ctx, const pa_source_info *info, int eol, void *pdata) noexcept
|
||||||
if(eventFacility == PA_SUBSCRIPTION_EVENT_SINK
|
{ return static_cast<PulseMainloop*>(pdata)->deviceSourceCallback(ctx, info, eol); };
|
||||||
|| eventFacility == PA_SUBSCRIPTION_EVENT_SOURCE)
|
|
||||||
{
|
auto server_callback = [](pa_context *ctx, const pa_server_info *info, void *pdata) noexcept
|
||||||
const auto deviceType = (eventFacility == PA_SUBSCRIPTION_EVENT_SINK)
|
{ return static_cast<PulseMainloop*>(pdata)->updateDefaultDevice(ctx, info); };
|
||||||
? alc::DeviceType::Playback : alc::DeviceType::Capture;
|
|
||||||
const auto eventType = (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK);
|
auto *sinkop = pa_context_get_sink_info_list(context, sink_callback, mutex());
|
||||||
if(eventType == PA_SUBSCRIPTION_EVENT_NEW)
|
auto *srcop = pa_context_get_source_info_list(context, src_callback, mutex());
|
||||||
alc::Event(alc::EventType::DeviceAdded, deviceType, "Device added");
|
auto *serverop = pa_context_get_server_info(context, server_callback, mutex());
|
||||||
else if(eventType == PA_SUBSCRIPTION_EVENT_REMOVE)
|
|
||||||
alc::Event(alc::EventType::DeviceRemoved, deviceType, "Device removed");
|
waitForOperation(sinkop);
|
||||||
}
|
waitForOperation(srcop);
|
||||||
};
|
waitForOperation(serverop);
|
||||||
pa_context_set_subscribe_callback(mutex()->mContext, handler, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -484,6 +577,13 @@ struct MainloopUniqueLock : public std::unique_lock<PulseMainloop> {
|
||||||
void connectContext();
|
void connectContext();
|
||||||
pa_stream *connectStream(const char *device_name, pa_stream_flags_t flags,
|
pa_stream *connectStream(const char *device_name, pa_stream_flags_t flags,
|
||||||
pa_buffer_attr *attr, pa_sample_spec *spec, pa_channel_map *chanmap, BackendType type);
|
pa_buffer_attr *attr, pa_sample_spec *spec, pa_channel_map *chanmap, BackendType type);
|
||||||
|
|
||||||
|
pa_stream *connectStream(const std::string &device_name, pa_stream_flags_t flags,
|
||||||
|
pa_buffer_attr *attr, pa_sample_spec *spec, pa_channel_map *chanmap, BackendType type)
|
||||||
|
{
|
||||||
|
return connectStream(device_name.empty() ? nullptr : device_name.c_str(), flags, attr,
|
||||||
|
spec, chanmap, type);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
using MainloopLockGuard = std::lock_guard<PulseMainloop>;
|
using MainloopLockGuard = std::lock_guard<PulseMainloop>;
|
||||||
|
|
||||||
|
|
@ -533,7 +633,7 @@ void MainloopUniqueLock::connectContext()
|
||||||
{
|
{
|
||||||
pa_context_unref(mutex()->mContext);
|
pa_context_unref(mutex()->mContext);
|
||||||
mutex()->mContext = nullptr;
|
mutex()->mContext = nullptr;
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "Context did not connect (%s)",
|
throw al::backend_exception{al::backend_error::DeviceError, "Context did not connect ({})",
|
||||||
pa_strerror(err)};
|
pa_strerror(err)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -544,7 +644,7 @@ pa_stream *MainloopUniqueLock::connectStream(const char *device_name, pa_stream_
|
||||||
const char *stream_id{(type==BackendType::Playback) ? "Playback Stream" : "Capture Stream"};
|
const char *stream_id{(type==BackendType::Playback) ? "Playback Stream" : "Capture Stream"};
|
||||||
pa_stream *stream{pa_stream_new(mutex()->mContext, stream_id, spec, chanmap)};
|
pa_stream *stream{pa_stream_new(mutex()->mContext, stream_id, spec, chanmap)};
|
||||||
if(!stream)
|
if(!stream)
|
||||||
throw al::backend_exception{al::backend_error::OutOfMemory, "pa_stream_new() failed (%s)",
|
throw al::backend_exception{al::backend_error::OutOfMemory, "pa_stream_new() failed ({})",
|
||||||
pa_strerror(pa_context_errno(mutex()->mContext))};
|
pa_strerror(pa_context_errno(mutex()->mContext))};
|
||||||
|
|
||||||
pa_stream_set_state_callback(stream, [](pa_stream *strm, void *pdata) noexcept
|
pa_stream_set_state_callback(stream, [](pa_stream *strm, void *pdata) noexcept
|
||||||
|
|
@ -556,7 +656,7 @@ pa_stream *MainloopUniqueLock::connectStream(const char *device_name, pa_stream_
|
||||||
if(err < 0)
|
if(err < 0)
|
||||||
{
|
{
|
||||||
pa_stream_unref(stream);
|
pa_stream_unref(stream);
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "%s did not connect (%s)",
|
throw al::backend_exception{al::backend_error::DeviceError, "%s did not connect ({})",
|
||||||
stream_id, pa_strerror(err)};
|
stream_id, pa_strerror(err)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -568,7 +668,7 @@ pa_stream *MainloopUniqueLock::connectStream(const char *device_name, pa_stream_
|
||||||
err = pa_context_errno(mutex()->mContext);
|
err = pa_context_errno(mutex()->mContext);
|
||||||
pa_stream_unref(stream);
|
pa_stream_unref(stream);
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"%s did not get ready (%s)", stream_id, pa_strerror(err)};
|
"{} did not get ready ({})", stream_id, pa_strerror(err)};
|
||||||
}
|
}
|
||||||
return state == PA_STREAM_READY;
|
return state == PA_STREAM_READY;
|
||||||
});
|
});
|
||||||
|
|
@ -593,56 +693,12 @@ void PulseMainloop::close(pa_stream *stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PulseMainloop::probePlaybackDevices()
|
|
||||||
{
|
|
||||||
PlaybackDevices.clear();
|
|
||||||
try {
|
|
||||||
MainloopUniqueLock plock{*this};
|
|
||||||
plock.connectContext();
|
|
||||||
|
|
||||||
auto sink_callback = [](pa_context *ctx, const pa_sink_info *info, int eol, void *pdata) noexcept
|
|
||||||
{ return static_cast<PulseMainloop*>(pdata)->deviceSinkCallback(ctx, info, eol); };
|
|
||||||
|
|
||||||
pa_operation *op{pa_context_get_sink_info_by_name(mContext, nullptr, sink_callback, this)};
|
|
||||||
plock.waitForOperation(op);
|
|
||||||
|
|
||||||
op = pa_context_get_sink_info_list(mContext, sink_callback, this);
|
|
||||||
plock.waitForOperation(op);
|
|
||||||
}
|
|
||||||
catch(std::exception &e) {
|
|
||||||
ERR("Error enumerating devices: %s\n", e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PulseMainloop::probeCaptureDevices()
|
|
||||||
{
|
|
||||||
CaptureDevices.clear();
|
|
||||||
try {
|
|
||||||
MainloopUniqueLock plock{*this};
|
|
||||||
plock.connectContext();
|
|
||||||
|
|
||||||
auto src_callback = [](pa_context *ctx, const pa_source_info *info, int eol, void *pdata) noexcept
|
|
||||||
{ return static_cast<PulseMainloop*>(pdata)->deviceSourceCallback(ctx, info, eol); };
|
|
||||||
|
|
||||||
pa_operation *op{pa_context_get_source_info_by_name(mContext, nullptr, src_callback,
|
|
||||||
this)};
|
|
||||||
plock.waitForOperation(op);
|
|
||||||
|
|
||||||
op = pa_context_get_source_info_list(mContext, src_callback, this);
|
|
||||||
plock.waitForOperation(op);
|
|
||||||
}
|
|
||||||
catch(std::exception &e) {
|
|
||||||
ERR("Error enumerating devices: %s\n", e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Used for initial connection test and enumeration. */
|
/* Used for initial connection test and enumeration. */
|
||||||
PulseMainloop gGlobalMainloop;
|
PulseMainloop gGlobalMainloop;
|
||||||
|
|
||||||
|
|
||||||
struct PulsePlayback final : public BackendBase {
|
struct PulsePlayback final : public BackendBase {
|
||||||
PulsePlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
explicit PulsePlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~PulsePlayback() override;
|
~PulsePlayback() override;
|
||||||
|
|
||||||
void bufferAttrCallback(pa_stream *stream) noexcept;
|
void bufferAttrCallback(pa_stream *stream) noexcept;
|
||||||
|
|
@ -660,7 +716,7 @@ struct PulsePlayback final : public BackendBase {
|
||||||
|
|
||||||
PulseMainloop mMainloop;
|
PulseMainloop mMainloop;
|
||||||
|
|
||||||
std::optional<std::string> mDeviceName{std::nullopt};
|
std::optional<std::string> mDeviceId{std::nullopt};
|
||||||
|
|
||||||
bool mIs51Rear{false};
|
bool mIs51Rear{false};
|
||||||
pa_buffer_attr mAttr{};
|
pa_buffer_attr mAttr{};
|
||||||
|
|
@ -683,14 +739,14 @@ void PulsePlayback::bufferAttrCallback(pa_stream *stream) noexcept
|
||||||
* leaving it alone means ALC_REFRESH will be off.
|
* leaving it alone means ALC_REFRESH will be off.
|
||||||
*/
|
*/
|
||||||
mAttr = *(pa_stream_get_buffer_attr(stream));
|
mAttr = *(pa_stream_get_buffer_attr(stream));
|
||||||
TRACE("minreq=%d, tlength=%d, prebuf=%d\n", mAttr.minreq, mAttr.tlength, mAttr.prebuf);
|
TRACE("minreq={}, tlength={}, prebuf={}", mAttr.minreq, mAttr.tlength, mAttr.prebuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PulsePlayback::streamStateCallback(pa_stream *stream) noexcept
|
void PulsePlayback::streamStateCallback(pa_stream *stream) noexcept
|
||||||
{
|
{
|
||||||
if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
|
if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
|
||||||
{
|
{
|
||||||
ERR("Received stream failure!\n");
|
ERR("Received stream failure!");
|
||||||
mDevice->handleDisconnect("Playback stream failure");
|
mDevice->handleDisconnect("Playback stream failure");
|
||||||
}
|
}
|
||||||
mMainloop.signal();
|
mMainloop.signal();
|
||||||
|
|
@ -716,7 +772,7 @@ void PulsePlayback::streamWriteCallback(pa_stream *stream, size_t nbytes) noexce
|
||||||
|
|
||||||
int ret{pa_stream_write(stream, buf, buflen, free_func, 0, PA_SEEK_RELATIVE)};
|
int ret{pa_stream_write(stream, buf, buflen, free_func, 0, PA_SEEK_RELATIVE)};
|
||||||
if(ret != PA_OK) UNLIKELY
|
if(ret != PA_OK) UNLIKELY
|
||||||
ERR("Failed to write to stream: %d, %s\n", ret, pa_strerror(ret));
|
ERR("Failed to write to stream: {}, {}", ret, pa_strerror(ret));
|
||||||
} while(nbytes > 0);
|
} while(nbytes > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -759,11 +815,11 @@ void PulsePlayback::sinkInfoCallback(pa_context*, const pa_sink_info *info, int
|
||||||
mIs51Rear = false;
|
mIs51Rear = false;
|
||||||
std::array<char,PA_CHANNEL_MAP_SNPRINT_MAX> chanmap_str{};
|
std::array<char,PA_CHANNEL_MAP_SNPRINT_MAX> chanmap_str{};
|
||||||
pa_channel_map_snprint(chanmap_str.data(), chanmap_str.size(), &info->channel_map);
|
pa_channel_map_snprint(chanmap_str.data(), chanmap_str.size(), &info->channel_map);
|
||||||
WARN("Failed to find format for channel map:\n %s\n", chanmap_str.data());
|
WARN("Failed to find format for channel map:\n {}", chanmap_str.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(info->active_port)
|
if(info->active_port)
|
||||||
TRACE("Active port: %s (%s)\n", info->active_port->name, info->active_port->description);
|
TRACE("Active port: {} ({})", info->active_port->name, info->active_port->description);
|
||||||
mDevice->Flags.set(DirectEar, (info->active_port
|
mDevice->Flags.set(DirectEar, (info->active_port
|
||||||
&& strcmp(info->active_port->name, "analog-output-headphones") == 0));
|
&& strcmp(info->active_port->name, "analog-output-headphones") == 0));
|
||||||
}
|
}
|
||||||
|
|
@ -775,13 +831,13 @@ void PulsePlayback::sinkNameCallback(pa_context*, const pa_sink_info *info, int
|
||||||
mMainloop.signal();
|
mMainloop.signal();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mDevice->DeviceName = info->description;
|
mDeviceName = info->description;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PulsePlayback::streamMovedCallback(pa_stream *stream) noexcept
|
void PulsePlayback::streamMovedCallback(pa_stream *stream) noexcept
|
||||||
{
|
{
|
||||||
mDeviceName = pa_stream_get_device_name(stream);
|
mDeviceId = pa_stream_get_device_name(stream);
|
||||||
TRACE("Stream moved to %s\n", mDeviceName->c_str());
|
TRACE("Stream moved to {}", *mDeviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -792,22 +848,20 @@ void PulsePlayback::open(std::string_view name)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Failed to start device mainloop"};
|
"Failed to start device mainloop"};
|
||||||
|
|
||||||
const char *pulse_name{nullptr};
|
auto pulse_name = std::string{};
|
||||||
std::string_view display_name;
|
|
||||||
if(!name.empty())
|
if(!name.empty())
|
||||||
{
|
{
|
||||||
if(PlaybackDevices.empty())
|
|
||||||
mMainloop.probePlaybackDevices();
|
|
||||||
|
|
||||||
auto match_name = [name](const DevMap &entry) -> bool
|
auto match_name = [name](const DevMap &entry) -> bool
|
||||||
{ return entry.name == name || entry.device_name == name; };
|
{ return entry.name == name || entry.device_name == name; };
|
||||||
|
|
||||||
|
auto plock = MainloopUniqueLock{gGlobalMainloop};
|
||||||
auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(), match_name);
|
auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(), match_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};
|
||||||
|
|
||||||
pulse_name = iter->device_name.c_str();
|
pulse_name = iter->device_name;
|
||||||
display_name = iter->name;
|
mDeviceName = iter->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
MainloopUniqueLock plock{mMainloop};
|
MainloopUniqueLock plock{mMainloop};
|
||||||
|
|
@ -823,38 +877,38 @@ void PulsePlayback::open(std::string_view name)
|
||||||
spec.rate = 44100;
|
spec.rate = 44100;
|
||||||
spec.channels = 2;
|
spec.channels = 2;
|
||||||
|
|
||||||
if(!pulse_name)
|
if(pulse_name.empty())
|
||||||
{
|
{
|
||||||
static const auto defname = al::getenv("ALSOFT_PULSE_DEFAULT");
|
static const auto defname = al::getenv("ALSOFT_PULSE_DEFAULT");
|
||||||
if(defname) pulse_name = defname->c_str();
|
if(defname) pulse_name = *defname;
|
||||||
}
|
}
|
||||||
TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
|
TRACE("Connecting to \"{}\"", pulse_name.empty() ? "(default)"sv:std::string_view{pulse_name});
|
||||||
mStream = plock.connectStream(pulse_name, flags, nullptr, &spec, nullptr,
|
mStream = plock.connectStream(pulse_name, flags, nullptr, &spec, nullptr,
|
||||||
BackendType::Playback);
|
BackendType::Playback);
|
||||||
|
|
||||||
constexpr auto move_callback = [](pa_stream *stream, void *pdata) noexcept
|
static constexpr auto move_callback = [](pa_stream *stream, void *pdata) noexcept
|
||||||
{ return static_cast<PulsePlayback*>(pdata)->streamMovedCallback(stream); };
|
{ return static_cast<PulsePlayback*>(pdata)->streamMovedCallback(stream); };
|
||||||
pa_stream_set_moved_callback(mStream, move_callback, this);
|
pa_stream_set_moved_callback(mStream, move_callback, this);
|
||||||
mFrameSize = static_cast<uint>(pa_frame_size(pa_stream_get_sample_spec(mStream)));
|
mFrameSize = static_cast<uint>(pa_frame_size(pa_stream_get_sample_spec(mStream)));
|
||||||
|
|
||||||
if(pulse_name) mDeviceName.emplace(pulse_name);
|
if(!pulse_name.empty())
|
||||||
else mDeviceName.reset();
|
mDeviceId.emplace(std::move(pulse_name));
|
||||||
if(display_name.empty())
|
|
||||||
|
if(mDeviceName.empty())
|
||||||
{
|
{
|
||||||
auto name_callback = [](pa_context *context, const pa_sink_info *info, int eol, void *pdata) noexcept
|
static constexpr auto name_callback = [](pa_context *context, const pa_sink_info *info,
|
||||||
|
int eol, void *pdata) noexcept
|
||||||
{ return static_cast<PulsePlayback*>(pdata)->sinkNameCallback(context, info, eol); };
|
{ return static_cast<PulsePlayback*>(pdata)->sinkNameCallback(context, info, eol); };
|
||||||
pa_operation *op{pa_context_get_sink_info_by_name(mMainloop.getContext(),
|
pa_operation *op{pa_context_get_sink_info_by_name(mMainloop.getContext(),
|
||||||
pa_stream_get_device_name(mStream), name_callback, this)};
|
pa_stream_get_device_name(mStream), name_callback, this)};
|
||||||
plock.waitForOperation(op);
|
plock.waitForOperation(op);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
mDevice->DeviceName = display_name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PulsePlayback::reset()
|
bool PulsePlayback::reset()
|
||||||
{
|
{
|
||||||
MainloopUniqueLock plock{mMainloop};
|
MainloopUniqueLock plock{mMainloop};
|
||||||
const auto deviceName = mDeviceName ? mDeviceName->c_str() : nullptr;
|
const auto deviceName = mDeviceId ? mDeviceId->c_str() : nullptr;
|
||||||
|
|
||||||
if(mStream)
|
if(mStream)
|
||||||
{
|
{
|
||||||
|
|
@ -877,7 +931,7 @@ bool PulsePlayback::reset()
|
||||||
PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_EARLY_REQUESTS};
|
PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_EARLY_REQUESTS};
|
||||||
if(!GetConfigValueBool({}, "pulse", "allow-moves", true))
|
if(!GetConfigValueBool({}, "pulse", "allow-moves", true))
|
||||||
flags |= PA_STREAM_DONT_MOVE;
|
flags |= PA_STREAM_DONT_MOVE;
|
||||||
if(GetConfigValueBool(mDevice->DeviceName, "pulse", "adjust-latency", false))
|
if(GetConfigValueBool(mDevice->mDeviceName, "pulse", "adjust-latency", false))
|
||||||
{
|
{
|
||||||
/* ADJUST_LATENCY can't be specified with EARLY_REQUESTS, for some
|
/* ADJUST_LATENCY can't be specified with EARLY_REQUESTS, for some
|
||||||
* reason. So if the user wants to adjust the overall device latency,
|
* reason. So if the user wants to adjust the overall device latency,
|
||||||
|
|
@ -886,7 +940,7 @@ bool PulsePlayback::reset()
|
||||||
flags &= ~PA_STREAM_EARLY_REQUESTS;
|
flags &= ~PA_STREAM_EARLY_REQUESTS;
|
||||||
flags |= PA_STREAM_ADJUST_LATENCY;
|
flags |= PA_STREAM_ADJUST_LATENCY;
|
||||||
}
|
}
|
||||||
if(GetConfigValueBool(mDevice->DeviceName, "pulse", "fix-rate", false)
|
if(GetConfigValueBool(mDevice->mDeviceName, "pulse", "fix-rate", false)
|
||||||
|| !mDevice->Flags.test(FrequencyRequest))
|
|| !mDevice->Flags.test(FrequencyRequest))
|
||||||
flags |= PA_STREAM_FIX_RATE;
|
flags |= PA_STREAM_FIX_RATE;
|
||||||
|
|
||||||
|
|
@ -948,16 +1002,16 @@ bool PulsePlayback::reset()
|
||||||
mSpec.format = PA_SAMPLE_FLOAT32NE;
|
mSpec.format = PA_SAMPLE_FLOAT32NE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mSpec.rate = mDevice->Frequency;
|
mSpec.rate = mDevice->mSampleRate;
|
||||||
mSpec.channels = static_cast<uint8_t>(mDevice->channelsFromFmt());
|
mSpec.channels = static_cast<uint8_t>(mDevice->channelsFromFmt());
|
||||||
if(pa_sample_spec_valid(&mSpec) == 0)
|
if(pa_sample_spec_valid(&mSpec) == 0)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "Invalid sample spec"};
|
throw al::backend_exception{al::backend_error::DeviceError, "Invalid sample spec"};
|
||||||
|
|
||||||
const auto frame_size = static_cast<uint>(pa_frame_size(&mSpec));
|
const auto frame_size = static_cast<uint>(pa_frame_size(&mSpec));
|
||||||
mAttr.maxlength = ~0u;
|
mAttr.maxlength = ~0u;
|
||||||
mAttr.tlength = mDevice->BufferSize * frame_size;
|
mAttr.tlength = mDevice->mBufferSize * frame_size;
|
||||||
mAttr.prebuf = 0u;
|
mAttr.prebuf = 0u;
|
||||||
mAttr.minreq = mDevice->UpdateSize * frame_size;
|
mAttr.minreq = mDevice->mUpdateSize * frame_size;
|
||||||
mAttr.fragsize = ~0u;
|
mAttr.fragsize = ~0u;
|
||||||
|
|
||||||
mStream = plock.connectStream(deviceName, flags, &mAttr, &mSpec, &chanmap,
|
mStream = plock.connectStream(deviceName, flags, &mAttr, &mSpec, &chanmap,
|
||||||
|
|
@ -974,15 +1028,15 @@ bool PulsePlayback::reset()
|
||||||
mSpec = *(pa_stream_get_sample_spec(mStream));
|
mSpec = *(pa_stream_get_sample_spec(mStream));
|
||||||
mFrameSize = static_cast<uint>(pa_frame_size(&mSpec));
|
mFrameSize = static_cast<uint>(pa_frame_size(&mSpec));
|
||||||
|
|
||||||
if(mDevice->Frequency != mSpec.rate)
|
if(mDevice->mSampleRate != mSpec.rate)
|
||||||
{
|
{
|
||||||
/* Server updated our playback rate, so modify the buffer attribs
|
/* Server updated our playback rate, so modify the buffer attribs
|
||||||
* accordingly.
|
* accordingly.
|
||||||
*/
|
*/
|
||||||
const auto scale = static_cast<double>(mSpec.rate) / mDevice->Frequency;
|
const auto scale = static_cast<double>(mSpec.rate) / mDevice->mSampleRate;
|
||||||
const auto perlen = std::clamp(std::round(scale*mDevice->UpdateSize), 64.0, 8192.0);
|
const auto perlen = std::clamp(std::round(scale*mDevice->mUpdateSize), 64.0, 8192.0);
|
||||||
const auto bufmax = uint{std::numeric_limits<int>::max()} / mFrameSize;
|
const auto bufmax = uint{std::numeric_limits<int>::max()} / mFrameSize;
|
||||||
const auto buflen = std::clamp(std::round(scale*mDevice->BufferSize), perlen*2.0,
|
const auto buflen = std::clamp(std::round(scale*mDevice->mBufferSize), perlen*2.0,
|
||||||
static_cast<double>(bufmax));
|
static_cast<double>(bufmax));
|
||||||
|
|
||||||
mAttr.maxlength = ~0u;
|
mAttr.maxlength = ~0u;
|
||||||
|
|
@ -994,7 +1048,7 @@ bool PulsePlayback::reset()
|
||||||
&mMainloop);
|
&mMainloop);
|
||||||
plock.waitForOperation(op);
|
plock.waitForOperation(op);
|
||||||
|
|
||||||
mDevice->Frequency = mSpec.rate;
|
mDevice->mSampleRate = mSpec.rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr auto attr_callback = [](pa_stream *stream, void *pdata) noexcept
|
constexpr auto attr_callback = [](pa_stream *stream, void *pdata) noexcept
|
||||||
|
|
@ -1002,8 +1056,8 @@ bool PulsePlayback::reset()
|
||||||
pa_stream_set_buffer_attr_callback(mStream, attr_callback, this);
|
pa_stream_set_buffer_attr_callback(mStream, attr_callback, this);
|
||||||
bufferAttrCallback(mStream);
|
bufferAttrCallback(mStream);
|
||||||
|
|
||||||
mDevice->BufferSize = mAttr.tlength / mFrameSize;
|
mDevice->mBufferSize = mAttr.tlength / mFrameSize;
|
||||||
mDevice->UpdateSize = mAttr.minreq / mFrameSize;
|
mDevice->mUpdateSize = mAttr.minreq / mFrameSize;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -1061,8 +1115,8 @@ ClockLatency PulsePlayback::getClockLatency()
|
||||||
* server yet. Give a generic value since nothing better is available.
|
* server yet. Give a generic value since nothing better is available.
|
||||||
*/
|
*/
|
||||||
if(err != -PA_ERR_NODATA)
|
if(err != -PA_ERR_NODATA)
|
||||||
ERR("Failed to get stream latency: 0x%x\n", err);
|
ERR("Failed to get stream latency: {:#x}", as_unsigned(err));
|
||||||
latency = mDevice->BufferSize - mDevice->UpdateSize;
|
latency = mDevice->mBufferSize - mDevice->mUpdateSize;
|
||||||
neg = 0;
|
neg = 0;
|
||||||
}
|
}
|
||||||
else if(neg) UNLIKELY
|
else if(neg) UNLIKELY
|
||||||
|
|
@ -1074,7 +1128,7 @@ ClockLatency PulsePlayback::getClockLatency()
|
||||||
|
|
||||||
|
|
||||||
struct PulseCapture final : public BackendBase {
|
struct PulseCapture final : public BackendBase {
|
||||||
PulseCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
explicit PulseCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~PulseCapture() override;
|
~PulseCapture() override;
|
||||||
|
|
||||||
void streamStateCallback(pa_stream *stream) noexcept;
|
void streamStateCallback(pa_stream *stream) noexcept;
|
||||||
|
|
@ -1090,7 +1144,7 @@ struct PulseCapture final : public BackendBase {
|
||||||
|
|
||||||
PulseMainloop mMainloop;
|
PulseMainloop mMainloop;
|
||||||
|
|
||||||
std::optional<std::string> mDeviceName{std::nullopt};
|
std::optional<std::string> mDeviceId{std::nullopt};
|
||||||
|
|
||||||
al::span<const std::byte> mCapBuffer;
|
al::span<const std::byte> mCapBuffer;
|
||||||
size_t mHoleLength{0};
|
size_t mHoleLength{0};
|
||||||
|
|
@ -1113,7 +1167,7 @@ void PulseCapture::streamStateCallback(pa_stream *stream) noexcept
|
||||||
{
|
{
|
||||||
if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
|
if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
|
||||||
{
|
{
|
||||||
ERR("Received stream failure!\n");
|
ERR("Received stream failure!");
|
||||||
mDevice->handleDisconnect("Capture stream failure");
|
mDevice->handleDisconnect("Capture stream failure");
|
||||||
}
|
}
|
||||||
mMainloop.signal();
|
mMainloop.signal();
|
||||||
|
|
@ -1126,13 +1180,13 @@ void PulseCapture::sourceNameCallback(pa_context*, const pa_source_info *info, i
|
||||||
mMainloop.signal();
|
mMainloop.signal();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mDevice->DeviceName = info->description;
|
mDeviceName = info->description;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PulseCapture::streamMovedCallback(pa_stream *stream) noexcept
|
void PulseCapture::streamMovedCallback(pa_stream *stream) noexcept
|
||||||
{
|
{
|
||||||
mDeviceName = pa_stream_get_device_name(stream);
|
mDeviceId = pa_stream_get_device_name(stream);
|
||||||
TRACE("Stream moved to %s\n", mDeviceName->c_str());
|
TRACE("Stream moved to {}", *mDeviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1146,21 +1200,20 @@ void PulseCapture::open(std::string_view name)
|
||||||
"Failed to start device mainloop"};
|
"Failed to start device mainloop"};
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *pulse_name{nullptr};
|
auto pulse_name = std::string{};
|
||||||
if(!name.empty())
|
if(!name.empty())
|
||||||
{
|
{
|
||||||
if(CaptureDevices.empty())
|
|
||||||
mMainloop.probeCaptureDevices();
|
|
||||||
|
|
||||||
auto match_name = [name](const DevMap &entry) -> bool
|
auto match_name = [name](const DevMap &entry) -> bool
|
||||||
{ return entry.name == name || entry.device_name == name; };
|
{ return entry.name == name || entry.device_name == name; };
|
||||||
|
|
||||||
|
auto plock = MainloopUniqueLock{gGlobalMainloop};
|
||||||
auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(), match_name);
|
auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(), match_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};
|
||||||
|
|
||||||
pulse_name = iter->device_name.c_str();
|
pulse_name = iter->device_name;
|
||||||
mDevice->DeviceName = iter->name;
|
mDeviceName = iter->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
MainloopUniqueLock plock{mMainloop};
|
MainloopUniqueLock plock{mMainloop};
|
||||||
|
|
@ -1179,7 +1232,7 @@ void PulseCapture::open(std::string_view name)
|
||||||
case DevFmtX7144:
|
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)};
|
||||||
}
|
}
|
||||||
setDefaultWFXChannelOrder();
|
setDefaultWFXChannelOrder();
|
||||||
|
|
@ -1203,26 +1256,26 @@ void PulseCapture::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)};
|
||||||
}
|
}
|
||||||
mSpec.rate = mDevice->Frequency;
|
mSpec.rate = mDevice->mSampleRate;
|
||||||
mSpec.channels = static_cast<uint8_t>(mDevice->channelsFromFmt());
|
mSpec.channels = static_cast<uint8_t>(mDevice->channelsFromFmt());
|
||||||
if(pa_sample_spec_valid(&mSpec) == 0)
|
if(pa_sample_spec_valid(&mSpec) == 0)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "Invalid sample format"};
|
throw al::backend_exception{al::backend_error::DeviceError, "Invalid sample format"};
|
||||||
|
|
||||||
const auto frame_size = static_cast<uint>(pa_frame_size(&mSpec));
|
const auto frame_size = static_cast<uint>(pa_frame_size(&mSpec));
|
||||||
const uint samples{std::max(mDevice->BufferSize, mDevice->Frequency*100u/1000u)};
|
const uint samples{std::max(mDevice->mBufferSize, mDevice->mSampleRate*100u/1000u)};
|
||||||
mAttr.minreq = ~0u;
|
mAttr.minreq = ~0u;
|
||||||
mAttr.prebuf = ~0u;
|
mAttr.prebuf = ~0u;
|
||||||
mAttr.maxlength = samples * frame_size;
|
mAttr.maxlength = samples * frame_size;
|
||||||
mAttr.tlength = ~0u;
|
mAttr.tlength = ~0u;
|
||||||
mAttr.fragsize = std::min(samples, mDevice->Frequency*50u/1000u) * frame_size;
|
mAttr.fragsize = std::min(samples, mDevice->mSampleRate*50u/1000u) * frame_size;
|
||||||
|
|
||||||
pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY};
|
pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY};
|
||||||
if(!GetConfigValueBool({}, "pulse", "allow-moves", true))
|
if(!GetConfigValueBool({}, "pulse", "allow-moves", true))
|
||||||
flags |= PA_STREAM_DONT_MOVE;
|
flags |= PA_STREAM_DONT_MOVE;
|
||||||
|
|
||||||
TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
|
TRACE("Connecting to \"{}\"", pulse_name.empty() ? "(default)"sv:std::string_view{pulse_name});
|
||||||
mStream = plock.connectStream(pulse_name, flags, &mAttr, &mSpec, &chanmap,
|
mStream = plock.connectStream(pulse_name, flags, &mAttr, &mSpec, &chanmap,
|
||||||
BackendType::Capture);
|
BackendType::Capture);
|
||||||
|
|
||||||
|
|
@ -1234,9 +1287,10 @@ void PulseCapture::open(std::string_view name)
|
||||||
{ return static_cast<PulseCapture*>(pdata)->streamStateCallback(stream); };
|
{ return static_cast<PulseCapture*>(pdata)->streamStateCallback(stream); };
|
||||||
pa_stream_set_state_callback(mStream, state_callback, this);
|
pa_stream_set_state_callback(mStream, state_callback, this);
|
||||||
|
|
||||||
if(pulse_name) mDeviceName.emplace(pulse_name);
|
if(!pulse_name.empty())
|
||||||
else mDeviceName.reset();
|
mDeviceId.emplace(std::move(pulse_name));
|
||||||
if(mDevice->DeviceName.empty())
|
|
||||||
|
if(mDeviceName.empty())
|
||||||
{
|
{
|
||||||
constexpr auto name_callback = [](pa_context *context, const pa_source_info *info, int eol,
|
constexpr auto name_callback = [](pa_context *context, const pa_source_info *info, int eol,
|
||||||
void *pdata) noexcept
|
void *pdata) noexcept
|
||||||
|
|
@ -1305,7 +1359,7 @@ void PulseCapture::captureSamples(std::byte *buffer, uint samples)
|
||||||
const pa_stream_state_t state{pa_stream_get_state(mStream)};
|
const pa_stream_state_t state{pa_stream_get_state(mStream)};
|
||||||
if(!PA_STREAM_IS_GOOD(state)) UNLIKELY
|
if(!PA_STREAM_IS_GOOD(state)) UNLIKELY
|
||||||
{
|
{
|
||||||
mDevice->handleDisconnect("Bad capture state: %u", state);
|
mDevice->handleDisconnect("Bad capture state: {}", al::to_underlying(state));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1313,7 +1367,7 @@ void PulseCapture::captureSamples(std::byte *buffer, uint samples)
|
||||||
size_t caplen{};
|
size_t caplen{};
|
||||||
if(pa_stream_peek(mStream, &capbuf, &caplen) < 0) UNLIKELY
|
if(pa_stream_peek(mStream, &capbuf, &caplen) < 0) UNLIKELY
|
||||||
{
|
{
|
||||||
mDevice->handleDisconnect("Failed retrieving capture samples: %s",
|
mDevice->handleDisconnect("Failed retrieving capture samples: {}",
|
||||||
pa_strerror(pa_context_errno(mMainloop.getContext())));
|
pa_strerror(pa_context_errno(mMainloop.getContext())));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1341,8 +1395,8 @@ uint PulseCapture::availableSamples()
|
||||||
if(static_cast<ssize_t>(got) < 0) UNLIKELY
|
if(static_cast<ssize_t>(got) < 0) UNLIKELY
|
||||||
{
|
{
|
||||||
const char *err{pa_strerror(static_cast<int>(got))};
|
const char *err{pa_strerror(static_cast<int>(got))};
|
||||||
ERR("pa_stream_readable_size() failed: %s\n", err);
|
ERR("pa_stream_readable_size() failed: {}", err);
|
||||||
mDevice->handleDisconnect("Failed getting readable size: %s", err);
|
mDevice->handleDisconnect("Failed getting readable size: {}", err);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -1377,7 +1431,7 @@ ClockLatency PulseCapture::getClockLatency()
|
||||||
|
|
||||||
if(err != 0) UNLIKELY
|
if(err != 0) UNLIKELY
|
||||||
{
|
{
|
||||||
ERR("Failed to get stream latency: 0x%x\n", err);
|
ERR("Failed to get stream latency: {:#x}", as_unsigned(err));
|
||||||
latency = 0;
|
latency = 0;
|
||||||
neg = 0;
|
neg = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1393,7 +1447,7 @@ ClockLatency PulseCapture::getClockLatency()
|
||||||
|
|
||||||
bool PulseBackendFactory::init()
|
bool PulseBackendFactory::init()
|
||||||
{
|
{
|
||||||
#ifdef HAVE_DYNLOAD
|
#if HAVE_DYNLOAD
|
||||||
if(!pulse_handle)
|
if(!pulse_handle)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
@ -1406,7 +1460,7 @@ bool PulseBackendFactory::init()
|
||||||
pulse_handle = LoadLib(PALIB);
|
pulse_handle = LoadLib(PALIB);
|
||||||
if(!pulse_handle)
|
if(!pulse_handle)
|
||||||
{
|
{
|
||||||
WARN("Failed to load %s\n", PALIB);
|
WARN("Failed to load {}", PALIB);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1420,13 +1474,13 @@ bool PulseBackendFactory::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(pulse_handle);
|
CloseLib(pulse_handle);
|
||||||
pulse_handle = nullptr;
|
pulse_handle = nullptr;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* HAVE_DYNLOAD */
|
#endif
|
||||||
|
|
||||||
pulse_ctx_flags = PA_CONTEXT_NOFLAGS;
|
pulse_ctx_flags = PA_CONTEXT_NOFLAGS;
|
||||||
if(!GetConfigValueBool({}, "pulse", "spawn-server", false))
|
if(!GetConfigValueBool({}, "pulse", "spawn-server", false))
|
||||||
|
|
@ -1460,21 +1514,32 @@ auto PulseBackendFactory::enumerate(BackendType type) -> std::vector<std::string
|
||||||
{
|
{
|
||||||
std::vector<std::string> outnames;
|
std::vector<std::string> outnames;
|
||||||
|
|
||||||
auto add_device = [&outnames](const DevMap &entry) -> void
|
auto add_playback_device = [&outnames](const DevMap &entry) -> void
|
||||||
{ outnames.push_back(entry.name); };
|
{
|
||||||
|
if(entry.device_name == DefaultPlaybackDevName)
|
||||||
|
outnames.emplace(outnames.cbegin(), entry.name);
|
||||||
|
else
|
||||||
|
outnames.push_back(entry.name);
|
||||||
|
};
|
||||||
|
auto add_capture_device = [&outnames](const DevMap &entry) -> void
|
||||||
|
{
|
||||||
|
if(entry.device_name == DefaultCaptureDevName)
|
||||||
|
outnames.emplace(outnames.cbegin(), entry.name);
|
||||||
|
else
|
||||||
|
outnames.push_back(entry.name);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto plock = MainloopUniqueLock{gGlobalMainloop};
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case BackendType::Playback:
|
case BackendType::Playback:
|
||||||
gGlobalMainloop.probePlaybackDevices();
|
|
||||||
outnames.reserve(PlaybackDevices.size());
|
outnames.reserve(PlaybackDevices.size());
|
||||||
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
|
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_playback_device);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BackendType::Capture:
|
case BackendType::Capture:
|
||||||
gGlobalMainloop.probeCaptureDevices();
|
|
||||||
outnames.reserve(CaptureDevices.size());
|
outnames.reserve(CaptureDevices.size());
|
||||||
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
|
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_capture_device);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1502,9 +1567,9 @@ alc::EventSupport PulseBackendFactory::queryEventSupport(alc::EventType eventTyp
|
||||||
{
|
{
|
||||||
case alc::EventType::DeviceAdded:
|
case alc::EventType::DeviceAdded:
|
||||||
case alc::EventType::DeviceRemoved:
|
case alc::EventType::DeviceRemoved:
|
||||||
|
case alc::EventType::DefaultDeviceChanged:
|
||||||
return alc::EventSupport::FullSupport;
|
return alc::EventSupport::FullSupport;
|
||||||
|
|
||||||
case alc::EventType::DefaultDeviceChanged:
|
|
||||||
case alc::EventType::Count:
|
case alc::EventType::Count:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
using namespace std::string_view_literals;
|
||||||
#define DEVNAME_PREFIX "OpenAL Soft on "
|
|
||||||
#else
|
|
||||||
#define DEVNAME_PREFIX ""
|
|
||||||
#endif
|
|
||||||
|
|
||||||
constexpr auto getDevicePrefix() noexcept -> std::string_view { return DEVNAME_PREFIX; }
|
[[nodiscard]] constexpr auto getDefaultDeviceName() noexcept -> std::string_view
|
||||||
constexpr auto getDefaultDeviceName() noexcept -> std::string_view
|
{ return "Default Device"sv; }
|
||||||
{ return DEVNAME_PREFIX "Default Device"; }
|
|
||||||
|
|
||||||
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.channels = static_cast<Uint8>(std::min<uint>(mDevice->channelsFromFmt(),
|
||||||
want.samples = static_cast<Uint16>(std::min(mDevice->UpdateSize, 8192u));
|
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();
|
mSDLName = name;
|
||||||
if(name.size() >= namePrefix.size() && name.substr(0, namePrefix.size()) == namePrefix)
|
mDeviceID = SDL_OpenAudioDevice(mSDLName.c_str(), SDL_FALSE, &want, &have,
|
||||||
{
|
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||||
/* 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}};
|
|
||||||
}
|
}
|
||||||
|
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,
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "Unhandled SDL format: 0x%04x",
|
"Unhandled SDL format: {:#04x}", have.format};
|
||||||
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;
|
if(mDeviceID)
|
||||||
mDevice->FmtChans = mFmtChans;
|
SDL_CloseAudioDevice(mDeviceID);
|
||||||
mDevice->FmtType = mFmtType;
|
mDeviceID = 0;
|
||||||
mDevice->UpdateSize = mUpdateSize;
|
|
||||||
mDevice->BufferSize = mUpdateSize * 2; /* SDL always (tries to) use two periods. */
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -220,16 +254,14 @@ auto SDL2BackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||||
if(num_devices <= 0)
|
if(num_devices <= 0)
|
||||||
return outnames;
|
return outnames;
|
||||||
|
|
||||||
outnames.reserve(static_cast<unsigned int>(num_devices));
|
outnames.reserve(static_cast<unsigned int>(num_devices)+1_uz);
|
||||||
outnames.emplace_back(getDefaultDeviceName());
|
outnames.emplace_back(getDefaultDeviceName());
|
||||||
for(int i{0};i < num_devices;++i)
|
for(int i{0};i < num_devices;++i)
|
||||||
{
|
{
|
||||||
std::string outname{getDevicePrefix()};
|
|
||||||
if(const char *name = SDL_GetAudioDeviceName(i, SDL_FALSE))
|
if(const char *name = SDL_GetAudioDeviceName(i, SDL_FALSE))
|
||||||
outname += name;
|
outnames.emplace_back(name);
|
||||||
else
|
else
|
||||||
outname += "Unknown Device Name #"+std::to_string(i);
|
outnames.emplace_back("Unknown Device Name #"+std::to_string(i));
|
||||||
outnames.emplace_back(std::move(outname));
|
|
||||||
}
|
}
|
||||||
return outnames;
|
return outnames;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
393
Engine/lib/openal-soft/alc/backends/sdl3.cpp
Normal file
393
Engine/lib/openal-soft/alc/backends/sdl3.cpp
Normal file
|
|
@ -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
Engine/lib/openal-soft/alc/backends/sdl3.h
Normal file
19
Engine/lib/openal-soft/alc/backends/sdl3.h
Normal file
|
|
@ -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 */
|
||||||
|
|
@ -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",
|
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
|
||||||
al::sizei(name), name.data()};
|
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.round = mDevice->mUpdateSize;
|
||||||
par.appbufsz = mDevice->BufferSize - mDevice->UpdateSize;
|
par.appbufsz = mDevice->mBufferSize - mDevice->mUpdateSize;
|
||||||
if(!par.appbufsz) par.appbufsz = mDevice->UpdateSize;
|
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->mUpdateSize = par.round;
|
||||||
mDevice->BufferSize = par.bufsz + 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",
|
mDevice->handleDisconnect("Poll error: {}", std::generic_category().message(errno));
|
||||||
std::generic_category().message(errno).c_str());
|
|
||||||
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);
|
ERR("sio_read failed: {:#x}", got);
|
||||||
mDevice->handleDisconnect("sio_read failed: 0x%" PRIx64, 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",
|
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
|
||||||
al::sizei(name), name.data()};
|
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.appbufsz = std::max(mDevice->mBufferSize, mDevice->mSampleRate/10u);
|
||||||
par.round = std::min(par.appbufsz/2u, mDevice->Frequency/40u);
|
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);
|
mRing = RingBuffer::Create(mDevice->mBufferSize, size_t{par.bps}*par.rchan, false);
|
||||||
mDevice->BufferSize = static_cast<uint>(mRing->writeSpace());
|
mDevice->mBufferSize = static_cast<uint>(mRing->writeSpace());
|
||||||
mDevice->UpdateSize = par.round;
|
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)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef BACKENDS_SNDIO_H
|
#ifndef BACKENDS_SNDIO_HPP
|
||||||
#define BACKENDS_SNDIO_H
|
#define BACKENDS_SNDIO_HPP
|
||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
|
|
||||||
|
|
@ -16,4 +16,4 @@ public:
|
||||||
static auto getFactory() -> BackendFactory&;
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_SNDIO_H */
|
#endif /* BACKENDS_SNDIO_HPP */
|
||||||
|
|
@ -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));
|
ERR("poll failed: {}", strerror(errno));
|
||||||
mDevice->handleDisconnect("Failed to wait for playback buffer: %s", 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));
|
ERR("write failed: {}", strerror(errno));
|
||||||
mDevice->handleDisconnect("Failed to write playback samples: %s", 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",
|
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
|
||||||
al::sizei(name), name.data()};
|
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",
|
throw al::backend_exception{al::backend_error::NoDevice, "Could not open {}: {}",
|
||||||
solaris_driver.c_str(), strerror(errno)};
|
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->mSampleRate = info.play.sample_rate;
|
||||||
mDevice->BufferSize = info.play.buffer_size / frame_size;
|
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
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -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) *
|
const auto avail = int64_t{std::chrono::duration_cast<seconds>((now-start) *
|
||||||
mDevice->Frequency).count()};
|
mDevice->mSampleRate).count()};
|
||||||
if(avail-done < mDevice->UpdateSize)
|
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);
|
mDevice->renderSamples(mBuffer.data(), mDevice->mUpdateSize, frameStep);
|
||||||
done += mDevice->UpdateSize;
|
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())};
|
const size_t fs{fwrite(mBuffer.data(), frameSize, mDevice->mUpdateSize, mFile.get())};
|
||||||
if(fs < mDevice->UpdateSize || ferror(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};
|
seconds s{done/mDevice->mSampleRate};
|
||||||
done %= mDevice->Frequency;
|
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",
|
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
|
||||||
al::sizei(name), name.data()};
|
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",
|
throw al::backend_exception{al::backend_error::DeviceError, "Could not open file '{}': {}",
|
||||||
fname->c_str(), std::generic_category().message(errno).c_str()};
|
*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;
|
||||||
|
|
@ -282,13 +274,24 @@ bool WaveBackend::reset()
|
||||||
chanmask = 0;
|
chanmask = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
bytes = mDevice->bytesFromFmt();
|
const auto bytes = mDevice->bytesFromFmt();
|
||||||
channels = mDevice->channelsFromFmt();
|
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());
|
||||||
|
|
||||||
|
|
@ -300,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
|
||||||
|
|
@ -319,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;
|
||||||
|
|
@ -339,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()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,14 +36,14 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
|
#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"
|
||||||
|
|
@ -54,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;
|
||||||
|
|
||||||
|
|
@ -76,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(std::data(WaveCaps.szPname))};
|
const auto basename = wstr_to_utf8(std::data(WaveCaps.szPname));
|
||||||
|
|
||||||
int count{1};
|
auto count = 1;
|
||||||
std::string newname{basename};
|
auto newname = basename;
|
||||||
while(checkName(PlaybackDevices, newname))
|
while(checkName(PlaybackDevices, newname))
|
||||||
{
|
newname = fmt::format("{} #{}", basename, ++count);
|
||||||
newname = basename;
|
|
||||||
newname += " #";
|
|
||||||
newname += std::to_string(++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));
|
||||||
}
|
}
|
||||||
|
|
@ -107,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(std::data(WaveCaps.szPname))};
|
const auto basename = wstr_to_utf8(std::data(WaveCaps.szPname));
|
||||||
|
|
||||||
int count{1};
|
auto count = 1;
|
||||||
std::string newname{basename};
|
auto newname = basename;
|
||||||
while(checkName(CaptureDevices, newname))
|
while(checkName(CaptureDevices, newname))
|
||||||
{
|
newname = fmt::format("{} #{}", basename, ++count);
|
||||||
newname = basename;
|
|
||||||
newname += " #";
|
|
||||||
newname += std::to_string(++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));
|
||||||
}
|
}
|
||||||
|
|
@ -127,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;
|
||||||
|
|
@ -194,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);
|
||||||
|
|
@ -215,8 +204,8 @@ 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",
|
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
|
||||||
al::sizei(name), name.data()};
|
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};
|
||||||
|
|
@ -238,7 +227,7 @@ void WinMMPlayback::open(std::string_view name)
|
||||||
}
|
}
|
||||||
format.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
|
format.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
|
||||||
format.nBlockAlign = static_cast<WORD>(format.wBitsPerSample * format.nChannels / 8);
|
format.nBlockAlign = static_cast<WORD>(format.wBitsPerSample * format.nChannels / 8);
|
||||||
format.nSamplesPerSec = mDevice->Frequency;
|
format.nSamplesPerSec = mDevice->mSampleRate;
|
||||||
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
|
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
|
||||||
format.cbSize = 0;
|
format.cbSize = 0;
|
||||||
|
|
||||||
|
|
@ -248,7 +237,7 @@ void WinMMPlayback::open(std::string_view name)
|
||||||
if(res == MMSYSERR_NOERROR) break;
|
if(res == MMSYSERR_NOERROR) break;
|
||||||
|
|
||||||
if(fmttype != DevFmtFloat)
|
if(fmttype != DevFmtFloat)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "waveOutOpen failed: %u",
|
throw al::backend_exception{al::backend_error::DeviceError, "waveOutOpen failed: {}",
|
||||||
res};
|
res};
|
||||||
|
|
||||||
fmttype = DevFmtShort;
|
fmttype = DevFmtShort;
|
||||||
|
|
@ -256,16 +245,16 @@ void WinMMPlayback::open(std::string_view name)
|
||||||
|
|
||||||
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} *
|
mDevice->mBufferSize = static_cast<uint>(uint64_t{mDevice->mBufferSize} *
|
||||||
mFormat.nSamplesPerSec / mDevice->Frequency);
|
mFormat.nSamplesPerSec / mDevice->mSampleRate);
|
||||||
mDevice->BufferSize = (mDevice->BufferSize+3) & ~0x3u;
|
mDevice->mBufferSize = (mDevice->mBufferSize+3) & ~0x3u;
|
||||||
mDevice->UpdateSize = mDevice->BufferSize / 4;
|
mDevice->mUpdateSize = mDevice->mBufferSize / 4;
|
||||||
mDevice->Frequency = mFormat.nSamplesPerSec;
|
mDevice->mSampleRate = mFormat.nSamplesPerSec;
|
||||||
|
|
||||||
if(mFormat.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
|
if(mFormat.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
|
||||||
{
|
{
|
||||||
|
|
@ -273,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -285,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,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{};
|
||||||
|
|
@ -331,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()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -354,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;
|
||||||
|
|
@ -446,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",
|
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
|
||||||
al::sizei(name), name.data()};
|
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)
|
||||||
|
|
@ -464,7 +453,7 @@ void WinMMCapture::open(std::string_view name)
|
||||||
case DevFmtX7144:
|
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)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -479,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)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -489,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;
|
||||||
|
|
||||||
|
|
@ -497,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};
|
||||||
|
|
@ -505,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);
|
||||||
|
|
@ -521,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()
|
||||||
|
|
@ -534,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()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,19 +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
|
#if ALSOFT_EAX
|
||||||
|
#include "al/eax/call.h"
|
||||||
#include "al/eax/globals.h"
|
#include "al/eax/globals.h"
|
||||||
#endif // ALSOFT_EAX
|
#endif // ALSOFT_EAX
|
||||||
|
|
||||||
|
|
@ -70,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,
|
||||||
|
|
@ -107,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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -116,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
|
||||||
|
|
||||||
|
|
@ -144,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;
|
||||||
}
|
}
|
||||||
|
|
@ -183,13 +190,15 @@ void ALCcontext::init()
|
||||||
{
|
{
|
||||||
auto iter = std::find(mExtensions.begin(), mExtensions.end(), "AL_EXT_SOURCE_RADIUS"sv);
|
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"sv);
|
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
|
||||||
|
|
||||||
|
|
@ -212,14 +221,26 @@ 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;
|
||||||
|
|
||||||
|
|
@ -236,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(ALCcontext *origctx{this}; sGlobalContext.compare_exchange_strong(origctx, nullptr))
|
||||||
if(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);
|
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)))
|
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
|
||||||
|
|
@ -276,7 +296,7 @@ void ALCcontext::deinit()
|
||||||
stopPlayback = (newsize == 0);
|
stopPlayback = (newsize == 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
stopPlayback = oldarray->empty();
|
stopPlayback = oldarray.empty();
|
||||||
|
|
||||||
StopEventThrd(this);
|
StopEventThrd(this);
|
||||||
|
|
||||||
|
|
@ -297,7 +317,7 @@ void ALCcontext::applyAllUpdates()
|
||||||
/* busy-wait */
|
/* busy-wait */
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
if(mEaxNeedsCommit)
|
if(mEaxNeedsCommit)
|
||||||
eaxCommit();
|
eaxCommit();
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -314,7 +334,7 @@ void ALCcontext::applyAllUpdates()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
template<typename F>
|
template<typename F>
|
||||||
|
|
@ -492,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();
|
||||||
|
|
||||||
|
|
@ -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)
|
/* mEax.flDistanceFactor was changed, so the context props are dirty. */
|
||||||
return;
|
|
||||||
|
|
||||||
mListener.mMetersPerUnit = mEax.flDistanceFactor;
|
|
||||||
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>(
|
eax_defer<Eax4PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(call, state,
|
||||||
call, state, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
|
&EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
|
||||||
break;
|
break;
|
||||||
case EAXCONTEXT_DISTANCEFACTOR:
|
case EAXCONTEXT_DISTANCEFACTOR:
|
||||||
eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
|
eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(call, state,
|
||||||
call, state, &EAX40CONTEXTPROPERTIES::flDistanceFactor);
|
&EAX40CONTEXTPROPERTIES::flDistanceFactor);
|
||||||
break;
|
break;
|
||||||
case EAXCONTEXT_AIRABSORPTIONHF:
|
case EAXCONTEXT_AIRABSORPTIONHF:
|
||||||
eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
|
eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(call, state,
|
||||||
call, state, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
|
&EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
|
||||||
break;
|
break;
|
||||||
case EAXCONTEXT_HFREFERENCE:
|
case EAXCONTEXT_HFREFERENCE:
|
||||||
eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
|
eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(call, state,
|
||||||
call, state, &EAX40CONTEXTPROPERTIES::flHFReference);
|
&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>(
|
eax_defer<Eax5PrimaryFxSlotIdValidator, eax_primary_fx_slot_id_dirty_bit>(call, state,
|
||||||
call, state, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
|
&EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
|
||||||
break;
|
break;
|
||||||
case EAXCONTEXT_DISTANCEFACTOR:
|
case EAXCONTEXT_DISTANCEFACTOR:
|
||||||
eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(
|
eax_defer<Eax4DistanceFactorValidator, eax_distance_factor_dirty_bit>(call, state,
|
||||||
call, state, &EAX50CONTEXTPROPERTIES::flDistanceFactor);
|
&EAX50CONTEXTPROPERTIES::flDistanceFactor);
|
||||||
break;
|
break;
|
||||||
case EAXCONTEXT_AIRABSORPTIONHF:
|
case EAXCONTEXT_AIRABSORPTIONHF:
|
||||||
eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(
|
eax_defer<Eax4AirAbsorptionHfValidator, eax_air_absorption_hf_dirty_bit>(call, state,
|
||||||
call, state, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
|
&EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
|
||||||
break;
|
break;
|
||||||
case EAXCONTEXT_HFREFERENCE:
|
case EAXCONTEXT_HFREFERENCE:
|
||||||
eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(
|
eax_defer<Eax4HfReferenceValidator, eax_hf_reference_dirty_bit>(call, state,
|
||||||
call, state, &EAX50CONTEXTPROPERTIES::flHFReference);
|
&EAX50CONTEXTPROPERTIES::flHFReference);
|
||||||
break;
|
break;
|
||||||
case EAXCONTEXT_MACROFXFACTOR:
|
case EAXCONTEXT_MACROFXFACTOR:
|
||||||
eax_defer<Eax5MacroFxFactorValidator, eax_macro_fx_factor_dirty_bit>(
|
eax_defer<Eax5MacroFxFactorValidator, eax_macro_fx_factor_dirty_bit>(call, state,
|
||||||
call, state, &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
|
&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>(
|
eax_context_commit_property<eax_primary_fx_slot_id_dirty_bit>(state, dst_df,
|
||||||
state, dst_df, &EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
|
&EAX40CONTEXTPROPERTIES::guidPrimaryFXSlotID);
|
||||||
eax_context_commit_property<eax_distance_factor_dirty_bit>(
|
eax_context_commit_property<eax_distance_factor_dirty_bit>(state, dst_df,
|
||||||
state, dst_df, &EAX40CONTEXTPROPERTIES::flDistanceFactor);
|
&EAX40CONTEXTPROPERTIES::flDistanceFactor);
|
||||||
eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(
|
eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(state, dst_df,
|
||||||
state, dst_df, &EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
|
&EAX40CONTEXTPROPERTIES::flAirAbsorptionHF);
|
||||||
eax_context_commit_property<eax_hf_reference_dirty_bit>(
|
eax_context_commit_property<eax_hf_reference_dirty_bit>(state, dst_df,
|
||||||
state, dst_df, &EAX40CONTEXTPROPERTIES::flHFReference);
|
&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>(
|
eax_context_commit_property<eax_primary_fx_slot_id_dirty_bit>(state, dst_df,
|
||||||
state, dst_df, &EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
|
&EAX50CONTEXTPROPERTIES::guidPrimaryFXSlotID);
|
||||||
eax_context_commit_property<eax_distance_factor_dirty_bit>(
|
eax_context_commit_property<eax_distance_factor_dirty_bit>(state, dst_df,
|
||||||
state, dst_df, &EAX50CONTEXTPROPERTIES::flDistanceFactor);
|
&EAX50CONTEXTPROPERTIES::flDistanceFactor);
|
||||||
eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(
|
eax_context_commit_property<eax_air_absorption_hf_dirty_bit>(state, dst_df,
|
||||||
state, dst_df, &EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
|
&EAX50CONTEXTPROPERTIES::flAirAbsorptionHF);
|
||||||
eax_context_commit_property<eax_hf_reference_dirty_bit>(
|
eax_context_commit_property<eax_hf_reference_dirty_bit>(state, dst_df,
|
||||||
state, dst_df, &EAX50CONTEXTPROPERTIES::flHFReference);
|
&EAX50CONTEXTPROPERTIES::flHFReference);
|
||||||
eax_context_commit_property<eax_macro_fx_factor_dirty_bit>(
|
eax_context_commit_property<eax_macro_fx_factor_dirty_bit>(state, dst_df,
|
||||||
state, dst_df, &EAX50CONTEXTPROPERTIES::flMacroFXFactor);
|
&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{})
|
if(dst_df.test(eax_air_absorption_hf_dirty_bit))
|
||||||
eax_context_commit_air_absorbtion_hf();
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
#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 <cstdint>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
@ -18,25 +20,24 @@
|
||||||
#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
|
#if ALSOFT_EAX
|
||||||
#include "al/eax/call.h"
|
#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;
|
||||||
|
|
@ -72,9 +73,12 @@ struct DebugLogEntry {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase {
|
namespace al {
|
||||||
const al::intrusive_ptr<ALCdevice> mALDevice;
|
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__
|
void setErrorImpl(ALenum errorCode, const fmt::string_view fmt, fmt::format_args args);
|
||||||
[[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]]
|
|
||||||
#else
|
template<typename ...Args>
|
||||||
[[gnu::format(printf, 3, 4)]]
|
void setError(ALenum errorCode, fmt::format_string<Args...> msg, Args&& ...args)
|
||||||
#endif
|
{ setErrorImpl(errorCode, msg, fmt::make_format_args(args...)); }
|
||||||
void setError(ALenum errorCode, const char *msg, ...);
|
|
||||||
|
[[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;
|
enum {
|
||||||
static constexpr auto eax_distance_factor_dirty_bit = EaxDirtyFlags{1} << 1;
|
eax_primary_fx_slot_id_dirty_bit,
|
||||||
static constexpr auto eax_air_absorption_hf_dirty_bit = EaxDirtyFlags{1} << 2;
|
eax_distance_factor_dirty_bit,
|
||||||
static constexpr auto eax_hf_reference_dirty_bit = EaxDirtyFlags{1} << 3;
|
eax_air_absorption_hf_dirty_bit,
|
||||||
static constexpr auto eax_macro_fx_factor_dirty_bit = EaxDirtyFlags{1} << 4;
|
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 eax4_context_commit(Eax4State& state, std::bitset<eax_dirty_bit_count>& dst_df);
|
||||||
void eax5_context_commit(Eax5State& state, EaxDirtyFlags& 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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -95,3 +96,5 @@ auto ALCdevice::getOutputMode1() const noexcept -> OutputMode1
|
||||||
}
|
}
|
||||||
return OutputMode1::Any;
|
return OutputMode1::Any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace al
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef ALC_DEVICE_H
|
#ifndef ALC_DEVICE_H
|
||||||
#define ALC_DEVICE_H
|
#define ALC_DEVICE_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
@ -18,7 +20,7 @@
|
||||||
#include "core/device.h"
|
#include "core/device.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
|
||||||
|
|
||||||
|
|
@ -30,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.
|
||||||
|
|
@ -80,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
|
||||||
|
|
||||||
|
|
@ -89,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);
|
std::string mVendorOverride;
|
||||||
~ALCdevice();
|
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<>
|
template<> inline
|
||||||
inline std::optional<std::string> ALCdevice::configValue(const std::string_view block, const std::string_view key)
|
auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional<std::string>
|
||||||
{ return ConfigValueStr(DeviceName, block, key); }
|
{ return ConfigValueStr(mDeviceName, block, key); }
|
||||||
template<>
|
template<> inline
|
||||||
inline std::optional<int> ALCdevice::configValue(const std::string_view block, const std::string_view key)
|
auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional<int>
|
||||||
{ return ConfigValueInt(DeviceName, block, key); }
|
{ return ConfigValueInt(mDeviceName, block, key); }
|
||||||
template<>
|
template<> inline
|
||||||
inline std::optional<uint> ALCdevice::configValue(const std::string_view block, const std::string_view key)
|
auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional<uint>
|
||||||
{ return ConfigValueUInt(DeviceName, block, key); }
|
{ return ConfigValueUInt(mDeviceName, block, key); }
|
||||||
template<>
|
template<> inline
|
||||||
inline std::optional<float> ALCdevice::configValue(const std::string_view block, const std::string_view key)
|
auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional<float>
|
||||||
{ return ConfigValueFloat(DeviceName, block, key); }
|
{ return ConfigValueFloat(mDeviceName, block, key); }
|
||||||
template<>
|
template<> inline
|
||||||
inline std::optional<bool> ALCdevice::configValue(const std::string_view block, const std::string_view key)
|
auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional<bool>
|
||||||
{ return ConfigValueBool(DeviceName, block, key); }
|
{ 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
|
||||||
|
|
|
||||||
|
|
@ -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)};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ struct ChorusState final : public EffectState {
|
||||||
void ChorusState::deviceUpdate(const DeviceBase *Device, const BufferStorage*)
|
void ChorusState::deviceUpdate(const DeviceBase *Device, const BufferStorage*)
|
||||||
{
|
{
|
||||||
constexpr auto MaxDelay = std::max(ChorusMaxDelay, FlangerMaxDelay);
|
constexpr auto MaxDelay = std::max(ChorusMaxDelay, FlangerMaxDelay);
|
||||||
const auto frequency = static_cast<float>(Device->Frequency);
|
const auto frequency = static_cast<float>(Device->mSampleRate);
|
||||||
const size_t maxlen{NextPowerOf2(float2uint(MaxDelay*2.0f*frequency) + 1u)};
|
const size_t maxlen{NextPowerOf2(float2uint(MaxDelay*2.0f*frequency) + 1u)};
|
||||||
if(maxlen != mDelayBuffer.size())
|
if(maxlen != mDelayBuffer.size())
|
||||||
decltype(mDelayBuffer)(maxlen).swap(mDelayBuffer);
|
decltype(mDelayBuffer)(maxlen).swap(mDelayBuffer);
|
||||||
|
|
@ -128,7 +128,7 @@ void ChorusState::update(const ContextBase *context, const EffectSlot *slot,
|
||||||
* delay and depth to allow enough padding for resampling.
|
* delay and depth to allow enough padding for resampling.
|
||||||
*/
|
*/
|
||||||
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);
|
||||||
|
|
||||||
mWaveform = props.Waveform;
|
mWaveform = props.Waveform;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,8 +89,8 @@ void CompressorState::deviceUpdate(const DeviceBase *device, const BufferStorage
|
||||||
/* Number of samples to do a full attack and release (non-integer sample
|
/* Number of samples to do a full attack and release (non-integer sample
|
||||||
* counts are okay).
|
* counts are okay).
|
||||||
*/
|
*/
|
||||||
const float attackCount{static_cast<float>(device->Frequency) * AttackTime};
|
const float attackCount{static_cast<float>(device->mSampleRate) * AttackTime};
|
||||||
const float releaseCount{static_cast<float>(device->Frequency) * ReleaseTime};
|
const float releaseCount{static_cast<float>(device->mSampleRate) * ReleaseTime};
|
||||||
|
|
||||||
/* Calculate per-sample multipliers to attack and release at the desired
|
/* Calculate per-sample multipliers to attack and release at the desired
|
||||||
* rates.
|
* rates.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "config_simd.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
@ -11,11 +12,10 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
#ifdef HAVE_SSE_INTRINSICS
|
#if HAVE_SSE_INTRINSICS
|
||||||
#include <xmmintrin.h>
|
#include <xmmintrin.h>
|
||||||
#elif defined(HAVE_NEON)
|
#elif HAVE_NEON
|
||||||
#include <arm_neon.h>
|
#include <arm_neon.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -171,7 +171,7 @@ constexpr size_t ConvolveUpdateSamples{ConvolveUpdateSize / 2};
|
||||||
void apply_fir(al::span<float> dst, const al::span<const float> input, const al::span<const float,ConvolveUpdateSamples> filter)
|
void apply_fir(al::span<float> dst, const al::span<const float> input, const al::span<const float,ConvolveUpdateSamples> filter)
|
||||||
{
|
{
|
||||||
auto src = input.begin();
|
auto src = input.begin();
|
||||||
#ifdef HAVE_SSE_INTRINSICS
|
#if HAVE_SSE_INTRINSICS
|
||||||
std::generate(dst.begin(), dst.end(), [&src,filter]
|
std::generate(dst.begin(), dst.end(), [&src,filter]
|
||||||
{
|
{
|
||||||
__m128 r4{_mm_setzero_ps()};
|
__m128 r4{_mm_setzero_ps()};
|
||||||
|
|
@ -189,7 +189,7 @@ void apply_fir(al::span<float> dst, const al::span<const float> input, const al:
|
||||||
return _mm_cvtss_f32(r4);
|
return _mm_cvtss_f32(r4);
|
||||||
});
|
});
|
||||||
|
|
||||||
#elif defined(HAVE_NEON)
|
#elif HAVE_NEON
|
||||||
|
|
||||||
std::generate(dst.begin(), dst.end(), [&src,filter]
|
std::generate(dst.begin(), dst.end(), [&src,filter]
|
||||||
{
|
{
|
||||||
|
|
@ -227,7 +227,7 @@ struct ConvolutionState final : public EffectState {
|
||||||
al::vector<std::array<float,ConvolveUpdateSamples>,16> mFilter;
|
al::vector<std::array<float,ConvolveUpdateSamples>,16> mFilter;
|
||||||
al::vector<std::array<float,ConvolveUpdateSamples*2>,16> mOutput;
|
al::vector<std::array<float,ConvolveUpdateSamples*2>,16> mOutput;
|
||||||
|
|
||||||
PFFFTSetup mFft{};
|
PFFFTSetup mFft;
|
||||||
alignas(16) std::array<float,ConvolveUpdateSize> mFftBuffer{};
|
alignas(16) std::array<float,ConvolveUpdateSize> mFftBuffer{};
|
||||||
alignas(16) std::array<float,ConvolveUpdateSize> mFftWorkBuffer{};
|
alignas(16) std::array<float,ConvolveUpdateSize> mFftWorkBuffer{};
|
||||||
|
|
||||||
|
|
@ -237,7 +237,7 @@ struct ConvolutionState final : public EffectState {
|
||||||
struct ChannelData {
|
struct ChannelData {
|
||||||
alignas(16) FloatBufferLine mBuffer{};
|
alignas(16) FloatBufferLine mBuffer{};
|
||||||
float mHfScale{}, mLfScale{};
|
float mHfScale{}, mLfScale{};
|
||||||
BandSplitter mFilter{};
|
BandSplitter mFilter;
|
||||||
std::array<float,MaxOutputChannels> Current{};
|
std::array<float,MaxOutputChannels> Current{};
|
||||||
std::array<float,MaxOutputChannels> Target{};
|
std::array<float,MaxOutputChannels> Target{};
|
||||||
};
|
};
|
||||||
|
|
@ -322,13 +322,13 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag
|
||||||
* called very infrequently, go ahead and use the polyphase resampler.
|
* called very infrequently, go ahead and use the polyphase resampler.
|
||||||
*/
|
*/
|
||||||
PPhaseResampler resampler;
|
PPhaseResampler resampler;
|
||||||
if(device->Frequency != buffer->mSampleRate)
|
if(device->mSampleRate != buffer->mSampleRate)
|
||||||
resampler.init(buffer->mSampleRate, device->Frequency);
|
resampler.init(buffer->mSampleRate, device->mSampleRate);
|
||||||
const auto resampledCount = static_cast<uint>(
|
const auto resampledCount = static_cast<uint>(
|
||||||
(uint64_t{buffer->mSampleLen}*device->Frequency+(buffer->mSampleRate-1)) /
|
(uint64_t{buffer->mSampleLen}*device->mSampleRate+(buffer->mSampleRate-1)) /
|
||||||
buffer->mSampleRate);
|
buffer->mSampleRate);
|
||||||
|
|
||||||
const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->Frequency)};
|
const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->mSampleRate)};
|
||||||
for(auto &e : mChans)
|
for(auto &e : mChans)
|
||||||
e.mFilter = splitter;
|
e.mFilter = splitter;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ void DistortionState::update(const ContextBase *context, const EffectSlot *slot,
|
||||||
/* Divide normalized frequency by the amount of oversampling done during
|
/* Divide normalized frequency by the amount of oversampling done during
|
||||||
* processing.
|
* processing.
|
||||||
*/
|
*/
|
||||||
auto frequency = static_cast<float>(device->Frequency);
|
auto frequency = static_cast<float>(device->mSampleRate);
|
||||||
mLowpass.setParamsFromBandwidth(BiquadType::LowPass, cutoff/frequency/4.0f, 1.0f, bandwidth);
|
mLowpass.setParamsFromBandwidth(BiquadType::LowPass, cutoff/frequency/4.0f, 1.0f, bandwidth);
|
||||||
|
|
||||||
cutoff = props.EQCenter;
|
cutoff = props.EQCenter;
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ struct EchoState final : public EffectState {
|
||||||
|
|
||||||
void EchoState::deviceUpdate(const DeviceBase *Device, const BufferStorage*)
|
void EchoState::deviceUpdate(const DeviceBase *Device, const BufferStorage*)
|
||||||
{
|
{
|
||||||
const auto frequency = static_cast<float>(Device->Frequency);
|
const auto frequency = static_cast<float>(Device->mSampleRate);
|
||||||
|
|
||||||
// Use the next power of 2 for the buffer length, so the tap offsets can be
|
// Use the next power of 2 for the buffer length, so the tap offsets can be
|
||||||
// wrapped using a mask instead of a modulo
|
// wrapped using a mask instead of a modulo
|
||||||
|
|
@ -100,7 +100,7 @@ void EchoState::update(const ContextBase *context, const EffectSlot *slot,
|
||||||
{
|
{
|
||||||
auto &props = std::get<EchoProps>(*props_);
|
auto &props = std::get<EchoProps>(*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);
|
||||||
|
|
||||||
mDelayTap[0] = std::max(float2uint(std::round(props.Delay*frequency)), 1u);
|
mDelayTap[0] = std::max(float2uint(std::round(props.Delay*frequency)), 1u);
|
||||||
mDelayTap[1] = float2uint(std::round(props.LRDelay*frequency)) + mDelayTap[0];
|
mDelayTap[1] = float2uint(std::round(props.LRDelay*frequency)) + mDelayTap[0];
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ void EqualizerState::update(const ContextBase *context, const EffectSlot *slot,
|
||||||
{
|
{
|
||||||
auto &props = std::get<EqualizerProps>(*props_);
|
auto &props = std::get<EqualizerProps>(*props_);
|
||||||
const DeviceBase *device{context->mDevice};
|
const DeviceBase *device{context->mDevice};
|
||||||
auto frequency = static_cast<float>(device->Frequency);
|
auto frequency = static_cast<float>(device->mSampleRate);
|
||||||
|
|
||||||
/* Calculate coefficients for the each type of filter. Note that the shelf
|
/* Calculate coefficients for the each type of filter. Note that the shelf
|
||||||
* and peaking filters' gain is for the centerpoint of the transition band,
|
* and peaking filters' gain is for the centerpoint of the transition band,
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ void FshifterState::update(const ContextBase *context, const EffectSlot *slot,
|
||||||
auto &props = std::get<FshifterProps>(*props_);
|
auto &props = std::get<FshifterProps>(*props_);
|
||||||
const DeviceBase *device{context->mDevice};
|
const DeviceBase *device{context->mDevice};
|
||||||
|
|
||||||
const float step{props.Frequency / static_cast<float>(device->Frequency)};
|
const float step{props.Frequency / static_cast<float>(device->mSampleRate)};
|
||||||
mPhaseStep[0] = mPhaseStep[1] = fastf2u(std::min(step, 1.0f) * MixerFracOne);
|
mPhaseStep[0] = mPhaseStep[1] = fastf2u(std::min(step, 1.0f) * MixerFracOne);
|
||||||
|
|
||||||
switch(props.LeftDirection)
|
switch(props.LeftDirection)
|
||||||
|
|
|
||||||
|
|
@ -122,10 +122,10 @@ void ModulatorState::update(const ContextBase *context, const EffectSlot *slot,
|
||||||
* many iterations per sample.
|
* many iterations per sample.
|
||||||
*/
|
*/
|
||||||
const float samplesPerCycle{props.Frequency > 0.0f
|
const float samplesPerCycle{props.Frequency > 0.0f
|
||||||
? static_cast<float>(device->Frequency)/props.Frequency + 0.5f
|
? static_cast<float>(device->mSampleRate)/props.Frequency + 0.5f
|
||||||
: 1.0f};
|
: 1.0f};
|
||||||
const uint range{static_cast<uint>(std::clamp(samplesPerCycle, 1.0f,
|
const uint range{static_cast<uint>(std::clamp(samplesPerCycle, 1.0f,
|
||||||
static_cast<float>(device->Frequency)))};
|
static_cast<float>(device->mSampleRate)))};
|
||||||
mIndex = static_cast<uint>(uint64_t{mIndex} * range / mRange);
|
mIndex = static_cast<uint>(uint64_t{mIndex} * range / mRange);
|
||||||
mRange = range;
|
mRange = range;
|
||||||
|
|
||||||
|
|
@ -155,7 +155,7 @@ void ModulatorState::update(const ContextBase *context, const EffectSlot *slot,
|
||||||
mSampleGen.emplace<SquareFunc>();
|
mSampleGen.emplace<SquareFunc>();
|
||||||
}
|
}
|
||||||
|
|
||||||
float f0norm{props.HighPassCutoff / static_cast<float>(device->Frequency)};
|
float f0norm{props.HighPassCutoff / static_cast<float>(device->mSampleRate)};
|
||||||
f0norm = std::clamp(f0norm, 1.0f/512.0f, 0.49f);
|
f0norm = std::clamp(f0norm, 1.0f/512.0f, 0.49f);
|
||||||
/* Bandwidth value is constant in octaves. */
|
/* Bandwidth value is constant in octaves. */
|
||||||
mChans[0].mFilter.setParamsFromBandwidth(BiquadType::HighPass, f0norm, 1.0f, 0.75f);
|
mChans[0].mFilter.setParamsFromBandwidth(BiquadType::HighPass, f0norm, 1.0f, 0.75f);
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,6 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <utility>
|
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
#include "alc/effects/base.h"
|
#include "alc/effects/base.h"
|
||||||
#include "alnumbers.h"
|
#include "alnumbers.h"
|
||||||
|
|
@ -403,7 +401,7 @@ struct EarlyReflections {
|
||||||
*/
|
*/
|
||||||
DelayLineU Delay;
|
DelayLineU Delay;
|
||||||
std::array<size_t,NUM_LINES> Offset{};
|
std::array<size_t,NUM_LINES> Offset{};
|
||||||
std::array<float,NUM_LINES> Coeff{};
|
float Coeff{};
|
||||||
|
|
||||||
/* The gain for each output channel based on 3D panning. */
|
/* The gain for each output channel based on 3D panning. */
|
||||||
struct OutGains {
|
struct OutGains {
|
||||||
|
|
@ -502,7 +500,7 @@ struct ReverbPipeline {
|
||||||
|
|
||||||
/* Tap points for early reflection input delay. */
|
/* Tap points for early reflection input delay. */
|
||||||
std::array<std::array<size_t,2>,NUM_LINES> mEarlyDelayTap{};
|
std::array<std::array<size_t,2>,NUM_LINES> mEarlyDelayTap{};
|
||||||
std::array<std::array<float,2>,NUM_LINES> mEarlyDelayCoeff{};
|
std::array<float,2> mEarlyDelayCoeff{};
|
||||||
|
|
||||||
/* Tap points for late reverb feed and delay. */
|
/* Tap points for late reverb feed and delay. */
|
||||||
std::array<std::array<size_t,2>,NUM_LINES> mLateDelayTap{};
|
std::array<std::array<size_t,2>,NUM_LINES> mLateDelayTap{};
|
||||||
|
|
@ -520,7 +518,7 @@ struct ReverbPipeline {
|
||||||
size_t mFadeSampleCount{1};
|
size_t mFadeSampleCount{1};
|
||||||
|
|
||||||
void updateDelayLine(const float gain, const float earlyDelay, const float lateDelay,
|
void updateDelayLine(const float gain, const float earlyDelay, const float lateDelay,
|
||||||
const float density_mult, const float decayTime, const float frequency);
|
const float density_mult, const float frequency);
|
||||||
void update3DPanning(const al::span<const float,3> ReflectionsPan,
|
void update3DPanning(const al::span<const float,3> ReflectionsPan,
|
||||||
const al::span<const float,3> LateReverbPan, const float earlyGain, const float lateGain,
|
const al::span<const float,3> LateReverbPan, const float earlyGain, const float lateGain,
|
||||||
const bool doUpmix, const MixParams *mainMix);
|
const bool doUpmix, const MixParams *mainMix);
|
||||||
|
|
@ -792,7 +790,7 @@ void ReverbState::allocLines(const float frequency)
|
||||||
|
|
||||||
void ReverbState::deviceUpdate(const DeviceBase *device, const BufferStorage*)
|
void ReverbState::deviceUpdate(const DeviceBase *device, const BufferStorage*)
|
||||||
{
|
{
|
||||||
const auto frequency = static_cast<float>(device->Frequency);
|
const auto frequency = static_cast<float>(device->mSampleRate);
|
||||||
|
|
||||||
/* Allocate the delay lines. */
|
/* Allocate the delay lines. */
|
||||||
allocLines(frequency);
|
allocLines(frequency);
|
||||||
|
|
@ -928,10 +926,15 @@ void EarlyReflections::updateLines(const float density_mult, const float diffusi
|
||||||
/* Calculate the delay length of each delay line. */
|
/* Calculate the delay length of each delay line. */
|
||||||
length = EARLY_LINE_LENGTHS[i] * density_mult;
|
length = EARLY_LINE_LENGTHS[i] * density_mult;
|
||||||
Offset[i] = float2uint(length * frequency);
|
Offset[i] = float2uint(length * frequency);
|
||||||
|
|
||||||
/* Calculate the gain (coefficient) for each line. */
|
|
||||||
Coeff[i] = CalcDecayCoeff(length, decayTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Calculate the gain (coefficient) for the secondary reflections based on
|
||||||
|
* the average delay and decay time.
|
||||||
|
*/
|
||||||
|
const auto length = std::reduce(EARLY_LINE_LENGTHS.begin(), EARLY_LINE_LENGTHS.end(), 0.0f)
|
||||||
|
/ float{EARLY_LINE_LENGTHS.size()} * density_mult;
|
||||||
|
Coeff = CalcDecayCoeff(length, decayTime);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update the EAX modulation step and depth. Keep in mind that this kind of
|
/* Update the EAX modulation step and depth. Keep in mind that this kind of
|
||||||
|
|
@ -1038,7 +1041,7 @@ void LateReverb::updateLines(const float density_mult, const float diffusion,
|
||||||
|
|
||||||
/* Update the offsets for the main effect delay line. */
|
/* Update the offsets for the main effect delay line. */
|
||||||
void ReverbPipeline::updateDelayLine(const float gain, const float earlyDelay,
|
void ReverbPipeline::updateDelayLine(const float gain, const float earlyDelay,
|
||||||
const float lateDelay, const float density_mult, const float decayTime, const float frequency)
|
const float lateDelay, const float density_mult, const float frequency)
|
||||||
{
|
{
|
||||||
/* Early reflection taps are decorrelated by means of an average room
|
/* Early reflection taps are decorrelated by means of an average room
|
||||||
* reflection approximation described above the definition of the taps.
|
* reflection approximation described above the definition of the taps.
|
||||||
|
|
@ -1050,11 +1053,11 @@ void ReverbPipeline::updateDelayLine(const float gain, const float earlyDelay,
|
||||||
* delay path and offsets that would continue the propagation naturally
|
* delay path and offsets that would continue the propagation naturally
|
||||||
* into the late lines.
|
* into the late lines.
|
||||||
*/
|
*/
|
||||||
|
mEarlyDelayCoeff[1] = gain;
|
||||||
for(size_t i{0u};i < NUM_LINES;i++)
|
for(size_t i{0u};i < NUM_LINES;i++)
|
||||||
{
|
{
|
||||||
float length{EARLY_TAP_LENGTHS[i]*density_mult};
|
float length{EARLY_TAP_LENGTHS[i]*density_mult};
|
||||||
mEarlyDelayTap[i][1] = float2uint((earlyDelay+length) * frequency);
|
mEarlyDelayTap[i][1] = float2uint((earlyDelay+length) * frequency);
|
||||||
mEarlyDelayCoeff[i][1] = CalcDecayCoeff(length, decayTime) * gain;
|
|
||||||
|
|
||||||
/* Reduce the late delay tap by the shortest early delay line length to
|
/* Reduce the late delay tap by the shortest early delay line length to
|
||||||
* compensate for the late line input being fed by the delayed early
|
* compensate for the late line input being fed by the delayed early
|
||||||
|
|
@ -1120,7 +1123,8 @@ void ReverbPipeline::update3DPanning(const al::span<const float,3> ReflectionsPa
|
||||||
const auto earlymat = GetTransformFromVector(ReflectionsPan);
|
const auto earlymat = GetTransformFromVector(ReflectionsPan);
|
||||||
const auto latemat = GetTransformFromVector(LateReverbPan);
|
const auto latemat = GetTransformFromVector(LateReverbPan);
|
||||||
|
|
||||||
const auto [earlycoeffs, latecoeffs] = [&]{
|
const auto get_coeffs = [&]
|
||||||
|
{
|
||||||
if(doUpmix)
|
if(doUpmix)
|
||||||
{
|
{
|
||||||
/* When upsampling, combine the early and late transforms with the
|
/* When upsampling, combine the early and late transforms with the
|
||||||
|
|
@ -1148,7 +1152,7 @@ void ReverbPipeline::update3DPanning(const al::span<const float,3> ReflectionsPa
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
return std::make_pair(mult_matrix(earlymat), mult_matrix(latemat));
|
return std::array{mult_matrix(earlymat), mult_matrix(latemat)};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* When not upsampling, combine the early and late A-to-B-Format
|
/* When not upsampling, combine the early and late A-to-B-Format
|
||||||
|
|
@ -1175,8 +1179,9 @@ void ReverbPipeline::update3DPanning(const al::span<const float,3> ReflectionsPa
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
return std::make_pair(mult_matrix(EarlyA2B, earlymat), mult_matrix(LateA2B, latemat));
|
return std::array{mult_matrix(EarlyA2B, earlymat), mult_matrix(LateA2B, latemat)};
|
||||||
}();
|
};
|
||||||
|
const auto [earlycoeffs, latecoeffs] = get_coeffs();
|
||||||
|
|
||||||
auto earlygains = mEarly.Gains.begin();
|
auto earlygains = mEarly.Gains.begin();
|
||||||
for(auto &coeffs : earlycoeffs)
|
for(auto &coeffs : earlycoeffs)
|
||||||
|
|
@ -1191,7 +1196,7 @@ void ReverbState::update(const ContextBase *Context, const EffectSlot *Slot,
|
||||||
{
|
{
|
||||||
auto &props = std::get<ReverbProps>(*props_);
|
auto &props = std::get<ReverbProps>(*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);
|
||||||
|
|
||||||
/* If the HF limit parameter is flagged, calculate an appropriate limit
|
/* If the HF limit parameter is flagged, calculate an appropriate limit
|
||||||
* based on the air absorption parameter.
|
* based on the air absorption parameter.
|
||||||
|
|
@ -1243,8 +1248,7 @@ void ReverbState::update(const ContextBase *Context, const EffectSlot *Slot,
|
||||||
mCurrentPipeline = !mCurrentPipeline;
|
mCurrentPipeline = !mCurrentPipeline;
|
||||||
|
|
||||||
auto &oldpipeline = mPipelines[!mCurrentPipeline];
|
auto &oldpipeline = mPipelines[!mCurrentPipeline];
|
||||||
for(size_t j{0};j < NUM_LINES;++j)
|
oldpipeline.mEarlyDelayCoeff[1] = 0.0f;
|
||||||
oldpipeline.mEarlyDelayCoeff[j][1] = 0.0f;
|
|
||||||
}
|
}
|
||||||
auto &pipeline = mPipelines[mCurrentPipeline];
|
auto &pipeline = mPipelines[mCurrentPipeline];
|
||||||
|
|
||||||
|
|
@ -1253,7 +1257,7 @@ void ReverbState::update(const ContextBase *Context, const EffectSlot *Slot,
|
||||||
|
|
||||||
/* Update the main effect delay and associated taps. */
|
/* Update the main effect delay and associated taps. */
|
||||||
pipeline.updateDelayLine(props.Gain, props.ReflectionsDelay, props.LateReverbDelay,
|
pipeline.updateDelayLine(props.Gain, props.ReflectionsDelay, props.LateReverbDelay,
|
||||||
density_mult, props.DecayTime, frequency);
|
density_mult, frequency);
|
||||||
|
|
||||||
/* Update early and late 3D panning. */
|
/* Update early and late 3D panning. */
|
||||||
mOutTarget = target.Main->Buffer;
|
mOutTarget = target.Main->Buffer;
|
||||||
|
|
@ -1507,17 +1511,17 @@ void ReverbPipeline::processEarly(const DelayLineU &main_delay, size_t offset,
|
||||||
/* First, load decorrelated samples from the main delay line as the
|
/* First, load decorrelated samples from the main delay line as the
|
||||||
* primary reflections.
|
* primary reflections.
|
||||||
*/
|
*/
|
||||||
const auto fadeStep = float{1.0f / static_cast<float>(todo)};
|
const auto fadeStep = 1.0f / static_cast<float>(todo);
|
||||||
|
const auto earlycoeff0 = float{mEarlyDelayCoeff[0]};
|
||||||
|
const auto earlycoeff1 = float{mEarlyDelayCoeff[1]};
|
||||||
|
mEarlyDelayCoeff[0] = mEarlyDelayCoeff[1];
|
||||||
for(size_t j{0_uz};j < NUM_LINES;j++)
|
for(size_t j{0_uz};j < NUM_LINES;j++)
|
||||||
{
|
{
|
||||||
const auto input = in_delay.get(j);
|
const auto input = in_delay.get(j);
|
||||||
auto early_delay_tap0 = size_t{offset - mEarlyDelayTap[j][0]};
|
auto early_delay_tap0 = size_t{offset - mEarlyDelayTap[j][0]};
|
||||||
auto early_delay_tap1 = size_t{offset - mEarlyDelayTap[j][1]};
|
auto early_delay_tap1 = size_t{offset - mEarlyDelayTap[j][1]};
|
||||||
mEarlyDelayTap[j][0] = mEarlyDelayTap[j][1];
|
mEarlyDelayTap[j][0] = mEarlyDelayTap[j][1];
|
||||||
const auto coeff0 = float{mEarlyDelayCoeff[j][0]};
|
auto fadeCount = 0.0f;
|
||||||
const auto coeff1 = float{mEarlyDelayCoeff[j][1]};
|
|
||||||
mEarlyDelayCoeff[j][0] = mEarlyDelayCoeff[j][1];
|
|
||||||
auto fadeCount = float{0.0f};
|
|
||||||
|
|
||||||
auto tmp = tempSamples[j].begin();
|
auto tmp = tempSamples[j].begin();
|
||||||
for(size_t i{0_uz};i < todo;)
|
for(size_t i{0_uz};i < todo;)
|
||||||
|
|
@ -1529,10 +1533,10 @@ void ReverbPipeline::processEarly(const DelayLineU &main_delay, size_t offset,
|
||||||
const auto intap0 = input.subspan(early_delay_tap0, td);
|
const auto intap0 = input.subspan(early_delay_tap0, td);
|
||||||
const auto intap1 = input.subspan(early_delay_tap1, td);
|
const auto intap1 = input.subspan(early_delay_tap1, td);
|
||||||
|
|
||||||
auto do_blend = [coeff0,coeff1,fadeStep,&fadeCount](const float in0,
|
auto do_blend = [earlycoeff0,earlycoeff1,fadeStep,&fadeCount](const float in0,
|
||||||
const float in1) noexcept -> float
|
const float in1) noexcept -> float
|
||||||
{
|
{
|
||||||
const auto ret = lerpf(in0*coeff0, in1*coeff1, fadeStep*fadeCount);
|
const auto ret = lerpf(in0*earlycoeff0, in1*earlycoeff1, fadeStep*fadeCount);
|
||||||
fadeCount += 1.0f;
|
fadeCount += 1.0f;
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
@ -1552,11 +1556,11 @@ void ReverbPipeline::processEarly(const DelayLineU &main_delay, size_t offset,
|
||||||
|
|
||||||
/* Apply a delay and bounce to generate secondary reflections. */
|
/* Apply a delay and bounce to generate secondary reflections. */
|
||||||
early_delay.writeReflected(offset, tempSamples, todo);
|
early_delay.writeReflected(offset, tempSamples, todo);
|
||||||
|
const auto feedb_coeff = mEarly.Coeff;
|
||||||
for(size_t j{0_uz};j < NUM_LINES;j++)
|
for(size_t j{0_uz};j < NUM_LINES;j++)
|
||||||
{
|
{
|
||||||
const auto input = early_delay.get(j);
|
const auto input = early_delay.get(j);
|
||||||
auto feedb_tap = size_t{offset - mEarly.Offset[j]};
|
auto feedb_tap = size_t{offset - mEarly.Offset[j]};
|
||||||
const auto feedb_coeff = float{mEarly.Coeff[j]};
|
|
||||||
auto out = outSamples[j].begin() + base;
|
auto out = outSamples[j].begin() + base;
|
||||||
auto tmp = tempSamples[j].begin();
|
auto tmp = tempSamples[j].begin();
|
||||||
|
|
||||||
|
|
@ -1597,20 +1601,20 @@ void ReverbPipeline::processEarly(const DelayLineU &main_delay, size_t offset,
|
||||||
|
|
||||||
auto Modulation::calcDelays(size_t todo) -> al::span<const uint>
|
auto Modulation::calcDelays(size_t todo) -> al::span<const uint>
|
||||||
{
|
{
|
||||||
auto idx = uint{Index};
|
auto idx = Index;
|
||||||
const auto step = uint{Step};
|
const auto step = Step;
|
||||||
const auto depth = float{Depth * float{gCubicTable.sTableSteps}};
|
const auto depth = Depth * float{gCubicTable.sTableSteps};
|
||||||
const auto delays = al::span{ModDelays}.first(todo);
|
const auto delays = al::span{ModDelays}.first(todo);
|
||||||
std::generate(delays.begin(), delays.end(), [step,depth,&idx]
|
std::generate(delays.begin(), delays.end(), [step,depth,&idx]
|
||||||
{
|
{
|
||||||
idx += step;
|
idx += step;
|
||||||
const auto x = float{static_cast<float>(idx&MOD_FRACMASK) * (1.0f/MOD_FRACONE)};
|
const auto x = static_cast<float>(idx&MOD_FRACMASK) * (1.0f/MOD_FRACONE);
|
||||||
/* Approximate sin(x*2pi). As long as it roughly fits a sinusoid shape
|
/* Approximate sin(x*2pi). As long as it roughly fits a sinusoid shape
|
||||||
* and stays within [-1...+1], it needn't be perfect.
|
* and stays within [-1...+1], it needn't be perfect.
|
||||||
*/
|
*/
|
||||||
const auto lfo = float{!(idx&(MOD_FRACONE>>1))
|
const auto lfo = !(idx&(MOD_FRACONE>>1))
|
||||||
? ((-16.0f * x * x) + (8.0f * x))
|
? ((-16.0f * x * x) + (8.0f * x))
|
||||||
: ((16.0f * x * x) + (-8.0f * x) + (-16.0f * x) + 8.0f)};
|
: ((16.0f * x * x) + (-8.0f * x) + (-16.0f * x) + 8.0f);
|
||||||
return float2uint((lfo+1.0f) * depth);
|
return float2uint((lfo+1.0f) * depth);
|
||||||
});
|
});
|
||||||
Index = idx;
|
Index = idx;
|
||||||
|
|
@ -1655,7 +1659,7 @@ void ReverbPipeline::processLate(size_t offset, const size_t samplesToDo,
|
||||||
for(size_t j{0_uz};j < NUM_LINES;++j)
|
for(size_t j{0_uz};j < NUM_LINES;++j)
|
||||||
{
|
{
|
||||||
const auto input = late_delay.get(j);
|
const auto input = late_delay.get(j);
|
||||||
const auto midGain = float{mLate.T60[j].MidGain};
|
const auto midGain = mLate.T60[j].MidGain;
|
||||||
auto late_feedb_tap = size_t{offset - mLate.Offset[j]};
|
auto late_feedb_tap = size_t{offset - mLate.Offset[j]};
|
||||||
|
|
||||||
auto proc_sample = [input,midGain,&late_feedb_tap](const size_t idelay) -> float
|
auto proc_sample = [input,midGain,&late_feedb_tap](const size_t idelay) -> float
|
||||||
|
|
@ -1663,7 +1667,7 @@ void ReverbPipeline::processLate(size_t offset, const size_t samplesToDo,
|
||||||
/* Calculate the read sample offset and sub-sample offset
|
/* Calculate the read sample offset and sub-sample offset
|
||||||
* between it and the next sample.
|
* between it and the next sample.
|
||||||
*/
|
*/
|
||||||
const auto delay = size_t{late_feedb_tap - (idelay>>gCubicTable.sTableBits)};
|
const auto delay = late_feedb_tap - (idelay>>gCubicTable.sTableBits);
|
||||||
const auto delayoffset = size_t{idelay & gCubicTable.sTableMask};
|
const auto delayoffset = size_t{idelay & gCubicTable.sTableMask};
|
||||||
++late_feedb_tap;
|
++late_feedb_tap;
|
||||||
|
|
||||||
|
|
@ -1675,10 +1679,10 @@ void ReverbPipeline::processLate(size_t offset, const size_t samplesToDo,
|
||||||
const auto out2 = float{input[(delay-2) & (input.size()-1)]};
|
const auto out2 = float{input[(delay-2) & (input.size()-1)]};
|
||||||
const auto out3 = float{input[(delay-3) & (input.size()-1)]};
|
const auto out3 = float{input[(delay-3) & (input.size()-1)]};
|
||||||
|
|
||||||
const auto out = float{out0*gCubicTable.getCoeff0(delayoffset)
|
const auto out = out0*gCubicTable.getCoeff0(delayoffset)
|
||||||
+ out1*gCubicTable.getCoeff1(delayoffset)
|
+ out1*gCubicTable.getCoeff1(delayoffset)
|
||||||
+ out2*gCubicTable.getCoeff2(delayoffset)
|
+ out2*gCubicTable.getCoeff2(delayoffset)
|
||||||
+ out3*gCubicTable.getCoeff3(delayoffset)};
|
+ out3*gCubicTable.getCoeff3(delayoffset);
|
||||||
return out * midGain;
|
return out * midGain;
|
||||||
};
|
};
|
||||||
std::transform(delays.begin(), delays.end(), tempSamples[j].begin(), proc_sample);
|
std::transform(delays.begin(), delays.end(), tempSamples[j].begin(), proc_sample);
|
||||||
|
|
@ -1694,10 +1698,10 @@ void ReverbPipeline::processLate(size_t offset, const size_t samplesToDo,
|
||||||
auto late_delay_tap0 = size_t{offset - mLateDelayTap[j][0]};
|
auto late_delay_tap0 = size_t{offset - mLateDelayTap[j][0]};
|
||||||
auto late_delay_tap1 = size_t{offset - mLateDelayTap[j][1]};
|
auto late_delay_tap1 = size_t{offset - mLateDelayTap[j][1]};
|
||||||
mLateDelayTap[j][0] = mLateDelayTap[j][1];
|
mLateDelayTap[j][0] = mLateDelayTap[j][1];
|
||||||
const auto densityGain = float{mLate.DensityGain};
|
const auto densityGain = mLate.DensityGain;
|
||||||
const auto densityStep = float{late_delay_tap0 != late_delay_tap1
|
const auto densityStep = late_delay_tap0 != late_delay_tap1
|
||||||
? densityGain*fadeStep : 0.0f};
|
? densityGain*fadeStep : 0.0f;
|
||||||
auto fadeCount = float{0.0f};
|
auto fadeCount = 0.0f;
|
||||||
|
|
||||||
auto samples = tempSamples[j].begin();
|
auto samples = tempSamples[j].begin();
|
||||||
for(size_t i{0u};i < todo;)
|
for(size_t i{0u};i < todo;)
|
||||||
|
|
@ -1766,8 +1770,7 @@ void ReverbState::process(const size_t samplesToDo, const al::span<const FloatBu
|
||||||
mMainDelay.write(offset, c, tmpspan);
|
mMainDelay.write(offset, c, tmpspan);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mPipelineState < Fading)
|
mPipelineState = std::max(Fading, mPipelineState);
|
||||||
mPipelineState = Fading;
|
|
||||||
|
|
||||||
/* Process reverb for these samples. and mix them to the output. */
|
/* Process reverb for these samples. and mix them to the output. */
|
||||||
pipeline.processEarly(mMainDelay, offset, samplesToDo, mTempSamples, mEarlySamples);
|
pipeline.processEarly(mMainDelay, offset, samplesToDo, mTempSamples, mEarlySamples);
|
||||||
|
|
|
||||||
|
|
@ -249,7 +249,7 @@ void VmorpherState::update(const ContextBase *context, const EffectSlot *slot,
|
||||||
{
|
{
|
||||||
auto &props = std::get<VmorpherProps>(*props_);
|
auto &props = std::get<VmorpherProps>(*props_);
|
||||||
const DeviceBase *device{context->mDevice};
|
const DeviceBase *device{context->mDevice};
|
||||||
const float frequency{static_cast<float>(device->Frequency)};
|
const float frequency{static_cast<float>(device->mSampleRate)};
|
||||||
const float step{props.Rate / frequency};
|
const float step{props.Rate / frequency};
|
||||||
mStep = fastf2u(std::clamp(step*WaveformFracOne, 0.0f, WaveformFracOne-1.0f));
|
mStep = fastf2u(std::clamp(step*WaveformFracOne, 0.0f, WaveformFracOne-1.0f));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,11 @@
|
||||||
|
|
||||||
#include "events.h"
|
#include "events.h"
|
||||||
|
|
||||||
|
#include "alnumeric.h"
|
||||||
#include "alspan.h"
|
#include "alspan.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
|
#include "fmt/core.h"
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -19,7 +21,7 @@ ALCenum EnumFromEventType(const alc::EventType type)
|
||||||
case alc::EventType::DeviceRemoved: return ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT;
|
case alc::EventType::DeviceRemoved: return ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT;
|
||||||
case alc::EventType::Count: break;
|
case alc::EventType::Count: break;
|
||||||
}
|
}
|
||||||
throw std::runtime_error{"Invalid EventType: "+std::to_string(al::to_underlying(type))};
|
throw std::runtime_error{fmt::format("Invalid EventType: {}", int{al::to_underlying(type)})};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
@ -74,7 +76,7 @@ FORCE_ALIGN ALCboolean ALC_APIENTRY alcEventControlSOFT(ALCsizei count, const AL
|
||||||
auto etype = alc::GetEventType(type);
|
auto etype = alc::GetEventType(type);
|
||||||
if(!etype)
|
if(!etype)
|
||||||
{
|
{
|
||||||
WARN("Invalid event type: 0x%04x\n", type);
|
WARN("Invalid event type: {:#04x}", as_unsigned(type));
|
||||||
alcSetError(nullptr, ALC_INVALID_ENUM);
|
alcSetError(nullptr, ALC_INVALID_ENUM);
|
||||||
return ALC_FALSE;
|
return ALC_FALSE;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
#ifndef ALC_EXPORT_LIST_H
|
#ifndef ALC_EXPORT_LIST_H
|
||||||
#define ALC_EXPORT_LIST_H
|
#define ALC_EXPORT_LIST_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#include "AL/alc.h"
|
#include "AL/alc.h"
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/alext.h"
|
#include "AL/alext.h"
|
||||||
|
|
||||||
#include "inprogext.h"
|
#include "inprogext.h"
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
#include "context.h"
|
#include "context.h"
|
||||||
#include "al/eax/x_ram.h"
|
#include "al/eax/x_ram.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -215,6 +217,10 @@ inline const FuncExport alcFunctions[]{
|
||||||
DECL(alPushDebugGroupEXT),
|
DECL(alPushDebugGroupEXT),
|
||||||
DECL(alPopDebugGroupEXT),
|
DECL(alPopDebugGroupEXT),
|
||||||
DECL(alGetDebugMessageLogEXT),
|
DECL(alGetDebugMessageLogEXT),
|
||||||
|
DECL(alObjectLabelEXT),
|
||||||
|
DECL(alGetObjectLabelEXT),
|
||||||
|
DECL(alGetPointerEXT),
|
||||||
|
DECL(alGetPointervEXT),
|
||||||
|
|
||||||
/* Direct Context functions */
|
/* Direct Context functions */
|
||||||
DECL(alcGetProcAddress2),
|
DECL(alcGetProcAddress2),
|
||||||
|
|
@ -365,15 +371,15 @@ inline const FuncExport alcFunctions[]{
|
||||||
DECL(alPushDebugGroupDirectEXT),
|
DECL(alPushDebugGroupDirectEXT),
|
||||||
DECL(alPopDebugGroupDirectEXT),
|
DECL(alPopDebugGroupDirectEXT),
|
||||||
DECL(alGetDebugMessageLogDirectEXT),
|
DECL(alGetDebugMessageLogDirectEXT),
|
||||||
DECL(alObjectLabelEXT),
|
|
||||||
DECL(alObjectLabelDirectEXT),
|
DECL(alObjectLabelDirectEXT),
|
||||||
DECL(alGetObjectLabelEXT),
|
|
||||||
DECL(alGetObjectLabelDirectEXT),
|
DECL(alGetObjectLabelDirectEXT),
|
||||||
|
DECL(alGetPointerDirectEXT),
|
||||||
|
DECL(alGetPointervDirectEXT),
|
||||||
|
|
||||||
/* Extra functions */
|
/* Extra functions */
|
||||||
DECL(alsoft_set_log_callback),
|
DECL(alsoft_set_log_callback),
|
||||||
};
|
};
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
inline const std::array eaxFunctions{
|
inline const std::array eaxFunctions{
|
||||||
DECL(EAXGet),
|
DECL(EAXGet),
|
||||||
DECL(EAXSet),
|
DECL(EAXSet),
|
||||||
|
|
@ -627,6 +633,8 @@ inline const EnumExport alcEnumerations[]{
|
||||||
DECL(AL_FORMAT_51CHN_I32),
|
DECL(AL_FORMAT_51CHN_I32),
|
||||||
DECL(AL_FORMAT_61CHN_I32),
|
DECL(AL_FORMAT_61CHN_I32),
|
||||||
DECL(AL_FORMAT_71CHN_I32),
|
DECL(AL_FORMAT_71CHN_I32),
|
||||||
|
DECL(AL_FORMAT_BFORMAT2D_I32),
|
||||||
|
DECL(AL_FORMAT_BFORMAT3D_I32),
|
||||||
DECL(AL_FORMAT_UHJ2CHN_I32_SOFT),
|
DECL(AL_FORMAT_UHJ2CHN_I32_SOFT),
|
||||||
DECL(AL_FORMAT_UHJ3CHN_I32_SOFT),
|
DECL(AL_FORMAT_UHJ3CHN_I32_SOFT),
|
||||||
DECL(AL_FORMAT_UHJ4CHN_I32_SOFT),
|
DECL(AL_FORMAT_UHJ4CHN_I32_SOFT),
|
||||||
|
|
@ -903,7 +911,7 @@ inline const EnumExport alcEnumerations[]{
|
||||||
|
|
||||||
DECL(AL_STOP_SOURCES_ON_DISCONNECT_SOFT),
|
DECL(AL_STOP_SOURCES_ON_DISCONNECT_SOFT),
|
||||||
};
|
};
|
||||||
#ifdef ALSOFT_EAX
|
#if ALSOFT_EAX
|
||||||
inline const std::array eaxEnumerations{
|
inline const std::array eaxEnumerations{
|
||||||
DECL(AL_EAX_RAM_SIZE),
|
DECL(AL_EAX_RAM_SIZE),
|
||||||
DECL(AL_EAX_RAM_FREE),
|
DECL(AL_EAX_RAM_FREE),
|
||||||
|
|
@ -911,7 +919,7 @@ inline const std::array eaxEnumerations{
|
||||||
DECL(AL_STORAGE_HARDWARE),
|
DECL(AL_STORAGE_HARDWARE),
|
||||||
DECL(AL_STORAGE_ACCESSIBLE),
|
DECL(AL_STORAGE_ACCESSIBLE),
|
||||||
};
|
};
|
||||||
#endif // ALSOFT_EAX
|
#endif
|
||||||
#undef DECL
|
#undef DECL
|
||||||
|
|
||||||
#endif /* ALC_EXPORT_LIST_H */
|
#endif /* ALC_EXPORT_LIST_H */
|
||||||
|
|
|
||||||
|
|
@ -37,11 +37,6 @@ void AL_APIENTRY alFlushMappedBufferDirectSOFT(ALCcontext *context, ALuint buffe
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef AL_SOFT_bformat_hoa
|
|
||||||
#define AL_SOFT_bformat_hoa
|
|
||||||
#define AL_UNPACK_AMBISONIC_ORDER_SOFT 0x199D
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef AL_SOFT_convolution_effect
|
#ifndef AL_SOFT_convolution_effect
|
||||||
#define AL_SOFT_convolution_effect
|
#define AL_SOFT_convolution_effect
|
||||||
#define AL_EFFECT_CONVOLUTION_SOFT 0xA000
|
#define AL_EFFECT_CONVOLUTION_SOFT 0xA000
|
||||||
|
|
@ -70,15 +65,18 @@ void AL_APIENTRY alFlushMappedBufferDirectSOFT(ALCcontext *context, ALuint buffe
|
||||||
#define AL_FORMAT_71CHN_I32 0x19E5
|
#define AL_FORMAT_71CHN_I32 0x19E5
|
||||||
#define AL_FORMAT_71CHN_FLOAT32 0x19E6
|
#define AL_FORMAT_71CHN_FLOAT32 0x19E6
|
||||||
|
|
||||||
#define AL_FORMAT_UHJ2CHN_I32_SOFT 0x19E7
|
#define AL_FORMAT_BFORMAT2D_I32 0x19E7
|
||||||
#define AL_FORMAT_UHJ3CHN_I32_SOFT 0x19E8
|
#define AL_FORMAT_BFORMAT3D_I32 0x19E8
|
||||||
#define AL_FORMAT_UHJ4CHN_I32_SOFT 0x19E9
|
|
||||||
|
#define AL_FORMAT_UHJ2CHN_I32_SOFT 0x19E9
|
||||||
|
#define AL_FORMAT_UHJ3CHN_I32_SOFT 0x19EA
|
||||||
|
#define AL_FORMAT_UHJ4CHN_I32_SOFT 0x19EB
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef AL_SOFT_source_panning
|
#ifndef AL_SOFT_source_panning
|
||||||
#define AL_SOFT_source_panning
|
#define AL_SOFT_source_panning
|
||||||
#define AL_PANNING_ENABLED_SOFT 0x19EA
|
#define AL_PANNING_ENABLED_SOFT 0x19EC
|
||||||
#define AL_PAN_SOFT 0x19EB
|
#define AL_PAN_SOFT 0x19ED
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Non-standard exports. Not part of any extension. */
|
/* Non-standard exports. Not part of any extension. */
|
||||||
|
|
@ -91,6 +89,11 @@ void ALC_APIENTRY alsoft_set_log_callback(LPALSOFTLOGCALLBACK callback, void *us
|
||||||
AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint src, ALsizei nb,
|
AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint src, ALsizei nb,
|
||||||
const ALuint *buffers) noexcept;
|
const ALuint *buffers) noexcept;
|
||||||
|
|
||||||
|
AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid) noexcept;
|
||||||
|
AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids) noexcept;
|
||||||
|
AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid) noexcept;
|
||||||
|
AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids) noexcept;
|
||||||
|
|
||||||
AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname) AL_API_NOEXCEPT;
|
AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname) AL_API_NOEXCEPT;
|
||||||
AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values) AL_API_NOEXCEPT;
|
AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values) AL_API_NOEXCEPT;
|
||||||
ALint64SOFT AL_APIENTRY alGetInteger64DirectSOFT(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT;
|
ALint64SOFT AL_APIENTRY alGetInteger64DirectSOFT(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT;
|
||||||
|
|
|
||||||
|
|
@ -76,53 +76,77 @@ using namespace std::string_view_literals;
|
||||||
using std::chrono::seconds;
|
using std::chrono::seconds;
|
||||||
using std::chrono::nanoseconds;
|
using std::chrono::nanoseconds;
|
||||||
|
|
||||||
const char *GetLabelFromChannel(Channel channel)
|
[[nodiscard]]
|
||||||
|
auto GetLabelFromChannel(Channel channel) -> std::string_view
|
||||||
{
|
{
|
||||||
switch(channel)
|
switch(channel)
|
||||||
{
|
{
|
||||||
case FrontLeft: return "front-left";
|
case FrontLeft: return "front-left"sv;
|
||||||
case FrontRight: return "front-right";
|
case FrontRight: return "front-right"sv;
|
||||||
case FrontCenter: return "front-center";
|
case FrontCenter: return "front-center"sv;
|
||||||
case LFE: return "lfe";
|
case LFE: return "lfe"sv;
|
||||||
case BackLeft: return "back-left";
|
case BackLeft: return "back-left"sv;
|
||||||
case BackRight: return "back-right";
|
case BackRight: return "back-right"sv;
|
||||||
case BackCenter: return "back-center";
|
case BackCenter: return "back-center"sv;
|
||||||
case SideLeft: return "side-left";
|
case SideLeft: return "side-left"sv;
|
||||||
case SideRight: return "side-right";
|
case SideRight: return "side-right"sv;
|
||||||
|
|
||||||
case TopFrontLeft: return "top-front-left";
|
case TopFrontLeft: return "top-front-left"sv;
|
||||||
case TopFrontCenter: return "top-front-center";
|
case TopFrontCenter: return "top-front-center"sv;
|
||||||
case TopFrontRight: return "top-front-right";
|
case TopFrontRight: return "top-front-right"sv;
|
||||||
case TopCenter: return "top-center";
|
case TopCenter: return "top-center"sv;
|
||||||
case TopBackLeft: return "top-back-left";
|
case TopBackLeft: return "top-back-left"sv;
|
||||||
case TopBackCenter: return "top-back-center";
|
case TopBackCenter: return "top-back-center"sv;
|
||||||
case TopBackRight: return "top-back-right";
|
case TopBackRight: return "top-back-right"sv;
|
||||||
|
|
||||||
case BottomFrontLeft: return "bottom-front-left";
|
case BottomFrontLeft: return "bottom-front-left"sv;
|
||||||
case BottomFrontRight: return "bottom-front-right";
|
case BottomFrontRight: return "bottom-front-right"sv;
|
||||||
case BottomBackLeft: return "bottom-back-left";
|
case BottomBackLeft: return "bottom-back-left"sv;
|
||||||
case BottomBackRight: return "bottom-back-right";
|
case BottomBackRight: return "bottom-back-right"sv;
|
||||||
|
|
||||||
case Aux0: return "Aux0";
|
case Aux0: return "Aux0"sv;
|
||||||
case Aux1: return "Aux1";
|
case Aux1: return "Aux1"sv;
|
||||||
case Aux2: return "Aux2";
|
case Aux2: return "Aux2"sv;
|
||||||
case Aux3: return "Aux3";
|
case Aux3: return "Aux3"sv;
|
||||||
case Aux4: return "Aux4";
|
case Aux4: return "Aux4"sv;
|
||||||
case Aux5: return "Aux5";
|
case Aux5: return "Aux5"sv;
|
||||||
case Aux6: return "Aux6";
|
case Aux6: return "Aux6"sv;
|
||||||
case Aux7: return "Aux7";
|
case Aux7: return "Aux7"sv;
|
||||||
case Aux8: return "Aux8";
|
case Aux8: return "Aux8"sv;
|
||||||
case Aux9: return "Aux9";
|
case Aux9: return "Aux9"sv;
|
||||||
case Aux10: return "Aux10";
|
case Aux10: return "Aux10"sv;
|
||||||
case Aux11: return "Aux11";
|
case Aux11: return "Aux11"sv;
|
||||||
case Aux12: return "Aux12";
|
case Aux12: return "Aux12"sv;
|
||||||
case Aux13: return "Aux13";
|
case Aux13: return "Aux13"sv;
|
||||||
case Aux14: return "Aux14";
|
case Aux14: return "Aux14"sv;
|
||||||
case Aux15: return "Aux15";
|
case Aux15: return "Aux15"sv;
|
||||||
|
|
||||||
case MaxChannels: break;
|
case MaxChannels: break;
|
||||||
}
|
}
|
||||||
return "(unknown)";
|
return "(unknown)"sv;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
auto GetLayoutName(DevAmbiLayout layout) noexcept -> std::string_view
|
||||||
|
{
|
||||||
|
switch(layout)
|
||||||
|
{
|
||||||
|
case DevAmbiLayout::FuMa: return "FuMa"sv;
|
||||||
|
case DevAmbiLayout::ACN: return "ACN"sv;
|
||||||
|
}
|
||||||
|
return "<unknown layout enum>"sv;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
auto GetScalingName(DevAmbiScaling scaling) noexcept -> std::string_view
|
||||||
|
{
|
||||||
|
switch(scaling)
|
||||||
|
{
|
||||||
|
case DevAmbiScaling::FuMa: return "FuMa"sv;
|
||||||
|
case DevAmbiScaling::SN3D: return "SN3D"sv;
|
||||||
|
case DevAmbiScaling::N3D: return "N3D"sv;
|
||||||
|
}
|
||||||
|
return "<unknown scaling enum>"sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -140,14 +164,14 @@ std::unique_ptr<FrontStablizer> CreateStablizer(const size_t outchans, const uin
|
||||||
return stablizer;
|
return stablizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AllocChannels(ALCdevice *device, const size_t main_chans, const size_t real_chans)
|
void AllocChannels(al::Device *device, const size_t main_chans, const size_t real_chans)
|
||||||
{
|
{
|
||||||
TRACE("Channel config, Main: %zu, Real: %zu\n", main_chans, real_chans);
|
TRACE("Channel config, Main: {}, Real: {}", main_chans, real_chans);
|
||||||
|
|
||||||
/* Allocate extra channels for any post-filter output. */
|
/* Allocate extra channels for any post-filter output. */
|
||||||
const size_t num_chans{main_chans + real_chans};
|
const size_t num_chans{main_chans + real_chans};
|
||||||
|
|
||||||
TRACE("Allocating %zu channels, %zu bytes\n", num_chans,
|
TRACE("Allocating {} channels, {} bytes", num_chans,
|
||||||
num_chans*sizeof(device->MixBuffer[0]));
|
num_chans*sizeof(device->MixBuffer[0]));
|
||||||
device->MixBuffer.resize(num_chans);
|
device->MixBuffer.resize(num_chans);
|
||||||
al::span<FloatBufferLine> buffer{device->MixBuffer};
|
al::span<FloatBufferLine> buffer{device->MixBuffer};
|
||||||
|
|
@ -239,7 +263,8 @@ struct DecoderConfig<DualBand, 0> {
|
||||||
using DecoderView = DecoderConfig<DualBand, 0>;
|
using DecoderView = DecoderConfig<DualBand, 0>;
|
||||||
|
|
||||||
|
|
||||||
void InitNearFieldCtrl(ALCdevice *device, const float ctrl_dist, const uint order, const bool is3d)
|
void InitNearFieldCtrl(al::Device *device, const float ctrl_dist, const uint order,
|
||||||
|
const bool is3d)
|
||||||
{
|
{
|
||||||
static const std::array<uint,MaxAmbiOrder+1> chans_per_order2d{{1, 2, 2, 2}};
|
static const std::array<uint,MaxAmbiOrder+1> chans_per_order2d{{1, 2, 2, 2}};
|
||||||
static const std::array<uint,MaxAmbiOrder+1> chans_per_order3d{{1, 3, 5, 7}};
|
static const std::array<uint,MaxAmbiOrder+1> chans_per_order3d{{1, 3, 5, 7}};
|
||||||
|
|
@ -249,10 +274,10 @@ void InitNearFieldCtrl(ALCdevice *device, const float ctrl_dist, const uint orde
|
||||||
return;
|
return;
|
||||||
|
|
||||||
device->AvgSpeakerDist = std::clamp(ctrl_dist, 0.1f, 10.0f);
|
device->AvgSpeakerDist = std::clamp(ctrl_dist, 0.1f, 10.0f);
|
||||||
TRACE("Using near-field reference distance: %.2f meters\n", device->AvgSpeakerDist);
|
TRACE("Using near-field reference distance: {:.2f} meters", device->AvgSpeakerDist);
|
||||||
|
|
||||||
const float w1{SpeedOfSoundMetersPerSec /
|
const float w1{SpeedOfSoundMetersPerSec /
|
||||||
(device->AvgSpeakerDist * static_cast<float>(device->Frequency))};
|
(device->AvgSpeakerDist * static_cast<float>(device->mSampleRate))};
|
||||||
device->mNFCtrlFilter.init(w1);
|
device->mNFCtrlFilter.init(w1);
|
||||||
|
|
||||||
auto iter = std::copy_n(is3d ? chans_per_order3d.begin() : chans_per_order2d.begin(), order+1u,
|
auto iter = std::copy_n(is3d ? chans_per_order3d.begin() : chans_per_order2d.begin(), order+1u,
|
||||||
|
|
@ -260,7 +285,7 @@ void InitNearFieldCtrl(ALCdevice *device, const float ctrl_dist, const uint orde
|
||||||
std::fill(iter, device->NumChannelsPerOrder.end(), 0u);
|
std::fill(iter, device->NumChannelsPerOrder.end(), 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitDistanceComp(ALCdevice *device, const al::span<const Channel> channels,
|
void InitDistanceComp(al::Device *device, const al::span<const Channel> channels,
|
||||||
const al::span<const float,MaxOutputChannels> dists)
|
const al::span<const float,MaxOutputChannels> dists)
|
||||||
{
|
{
|
||||||
const float maxdist{std::accumulate(dists.begin(), dists.end(), 0.0f,
|
const float maxdist{std::accumulate(dists.begin(), dists.end(), 0.0f,
|
||||||
|
|
@ -269,7 +294,7 @@ void InitDistanceComp(ALCdevice *device, const al::span<const Channel> channels,
|
||||||
if(!device->getConfigValueBool("decoder", "distance-comp", true) || !(maxdist > 0.0f))
|
if(!device->getConfigValueBool("decoder", "distance-comp", true) || !(maxdist > 0.0f))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto distSampleScale = static_cast<float>(device->Frequency) / SpeedOfSoundMetersPerSec;
|
const auto distSampleScale = static_cast<float>(device->mSampleRate)/SpeedOfSoundMetersPerSec;
|
||||||
|
|
||||||
struct DistCoeffs { uint Length{}; float Gain{}; };
|
struct DistCoeffs { uint Length{}; float Gain{}; };
|
||||||
std::vector<DistCoeffs> ChanDelay;
|
std::vector<DistCoeffs> ChanDelay;
|
||||||
|
|
@ -294,7 +319,7 @@ void InitDistanceComp(ALCdevice *device, const al::span<const Channel> channels,
|
||||||
float delay{std::floor((maxdist - distance)*distSampleScale + 0.5f)};
|
float delay{std::floor((maxdist - distance)*distSampleScale + 0.5f)};
|
||||||
if(delay > float{DistanceComp::MaxDelay-1})
|
if(delay > float{DistanceComp::MaxDelay-1})
|
||||||
{
|
{
|
||||||
ERR("Delay for channel %zu (%s) exceeds buffer length (%f > %d)\n", idx,
|
ERR("Delay for channel {} ({}) exceeds buffer length ({:f} > {})", idx,
|
||||||
GetLabelFromChannel(ch), delay, DistanceComp::MaxDelay-1);
|
GetLabelFromChannel(ch), delay, DistanceComp::MaxDelay-1);
|
||||||
delay = float{DistanceComp::MaxDelay-1};
|
delay = float{DistanceComp::MaxDelay-1};
|
||||||
}
|
}
|
||||||
|
|
@ -302,7 +327,7 @@ void InitDistanceComp(ALCdevice *device, const al::span<const Channel> channels,
|
||||||
ChanDelay.resize(std::max(ChanDelay.size(), idx+1_uz));
|
ChanDelay.resize(std::max(ChanDelay.size(), idx+1_uz));
|
||||||
ChanDelay[idx].Length = static_cast<uint>(delay);
|
ChanDelay[idx].Length = static_cast<uint>(delay);
|
||||||
ChanDelay[idx].Gain = distance / maxdist;
|
ChanDelay[idx].Gain = distance / maxdist;
|
||||||
TRACE("Channel %s distance comp: %u samples, %f gain\n", GetLabelFromChannel(ch),
|
TRACE("Channel {} distance comp: {} samples, {:f} gain", GetLabelFromChannel(ch),
|
||||||
ChanDelay[idx].Length, ChanDelay[idx].Gain);
|
ChanDelay[idx].Length, ChanDelay[idx].Gain);
|
||||||
|
|
||||||
/* Round up to the next 4th sample, so each channel buffer starts
|
/* Round up to the next 4th sample, so each channel buffer starts
|
||||||
|
|
@ -345,8 +370,8 @@ constexpr auto GetAmbiLayout(DevAmbiLayout layouttype) noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DecoderView MakeDecoderView(ALCdevice *device, const AmbDecConf *conf,
|
auto MakeDecoderView(al::Device *device, const AmbDecConf *conf,
|
||||||
DecoderConfig<DualBand,MaxOutputChannels> &decoder)
|
DecoderConfig<DualBand,MaxOutputChannels> &decoder) -> DecoderView
|
||||||
{
|
{
|
||||||
DecoderView ret{};
|
DecoderView ret{};
|
||||||
|
|
||||||
|
|
@ -441,10 +466,11 @@ DecoderView MakeDecoderView(ALCdevice *device, const AmbDecConf *conf,
|
||||||
{
|
{
|
||||||
int idx{};
|
int idx{};
|
||||||
char c{};
|
char c{};
|
||||||
|
/* NOLINTNEXTLINE(cert-err34-c,cppcoreguidelines-pro-type-vararg) */
|
||||||
if(sscanf(speaker.Name.c_str(), "AUX%d%c", &idx, &c) != 1 || idx < 0
|
if(sscanf(speaker.Name.c_str(), "AUX%d%c", &idx, &c) != 1 || idx < 0
|
||||||
|| idx >= MaxChannels-Aux0)
|
|| idx >= MaxChannels-Aux0)
|
||||||
{
|
{
|
||||||
ERR("AmbDec speaker label \"%s\" not recognized\n", speaker.Name.c_str());
|
ERR("AmbDec speaker label \"{}\" not recognized", speaker.Name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ch = static_cast<Channel>(Aux0+idx);
|
ch = static_cast<Channel>(Aux0+idx);
|
||||||
|
|
@ -649,7 +675,7 @@ constexpr DecoderConfig<DualBand, 14> X7144Config{
|
||||||
}}
|
}}
|
||||||
};
|
};
|
||||||
|
|
||||||
void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=false,
|
void InitPanning(al::Device *device, const bool hqdec=false, const bool stablize=false,
|
||||||
DecoderView decoder={})
|
DecoderView decoder={})
|
||||||
{
|
{
|
||||||
if(!decoder)
|
if(!decoder)
|
||||||
|
|
@ -682,10 +708,13 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=
|
||||||
avg_dist = *distopt;
|
avg_dist = *distopt;
|
||||||
else if(auto delayopt = device->configValue<float>("decoder", "nfc-ref-delay"))
|
else if(auto delayopt = device->configValue<float>("decoder", "nfc-ref-delay"))
|
||||||
{
|
{
|
||||||
WARN("nfc-ref-delay is deprecated, use speaker-dist instead\n");
|
WARN("nfc-ref-delay is deprecated, use speaker-dist instead");
|
||||||
avg_dist = *delayopt * SpeedOfSoundMetersPerSec;
|
avg_dist = *delayopt * SpeedOfSoundMetersPerSec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TRACE("{}{} order ambisonic output ({} layout, {} scaling)", device->mAmbiOrder,
|
||||||
|
GetCounterSuffix(device->mAmbiOrder), GetLayoutName(device->mAmbiLayout),
|
||||||
|
GetScalingName(device->mAmbiScale));
|
||||||
InitNearFieldCtrl(device, avg_dist, device->mAmbiOrder, true);
|
InitNearFieldCtrl(device, avg_dist, device->mAmbiOrder, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -700,7 +729,7 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=
|
||||||
const size_t idx{device->channelIdxByName(decoder.mChannels[i])};
|
const size_t idx{device->channelIdxByName(decoder.mChannels[i])};
|
||||||
if(idx == InvalidChannelIndex)
|
if(idx == InvalidChannelIndex)
|
||||||
{
|
{
|
||||||
ERR("Failed to find %s channel in device\n",
|
ERR("Failed to find {} channel in device",
|
||||||
GetLabelFromChannel(decoder.mChannels[i]));
|
GetLabelFromChannel(decoder.mChannels[i]));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -756,22 +785,21 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=
|
||||||
}
|
}
|
||||||
if(!hasfc)
|
if(!hasfc)
|
||||||
{
|
{
|
||||||
stablizer = CreateStablizer(device->channelsFromFmt(), device->Frequency);
|
stablizer = CreateStablizer(device->channelsFromFmt(), device->mSampleRate);
|
||||||
TRACE("Front stablizer enabled\n");
|
TRACE("Front stablizer enabled");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("Enabling %s-band %s-order%s ambisonic decoder\n",
|
TRACE("Enabling {}-band {}-order{} ambisonic decoder", !dual_band ? "single" : "dual",
|
||||||
!dual_band ? "single" : "dual",
|
|
||||||
(decoder.mOrder > 3) ? "fourth" :
|
(decoder.mOrder > 3) ? "fourth" :
|
||||||
(decoder.mOrder > 2) ? "third" :
|
(decoder.mOrder > 2) ? "third" :
|
||||||
(decoder.mOrder > 1) ? "second" : "first",
|
(decoder.mOrder > 1) ? "second" : "first",
|
||||||
decoder.mIs3D ? " periphonic" : "");
|
decoder.mIs3D ? " periphonic" : "");
|
||||||
device->AmbiDecoder = BFormatDec::Create(ambicount, chancoeffs, chancoeffslf,
|
device->AmbiDecoder = BFormatDec::Create(ambicount, chancoeffs, chancoeffslf,
|
||||||
device->mXOverFreq/static_cast<float>(device->Frequency), std::move(stablizer));
|
device->mXOverFreq/static_cast<float>(device->mSampleRate), std::move(stablizer));
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitHrtfPanning(ALCdevice *device)
|
void InitHrtfPanning(al::Device *device)
|
||||||
{
|
{
|
||||||
static constexpr float Deg180{al::numbers::pi_v<float>};
|
static constexpr float Deg180{al::numbers::pi_v<float>};
|
||||||
static constexpr float Deg_90{Deg180 / 2.0f /* 90 degrees*/};
|
static constexpr float Deg_90{Deg180 / 2.0f /* 90 degrees*/};
|
||||||
|
|
@ -929,7 +957,7 @@ void InitHrtfPanning(ALCdevice *device)
|
||||||
std::string_view mode{*modeopt};
|
std::string_view mode{*modeopt};
|
||||||
if(al::case_compare(mode, "basic"sv) == 0)
|
if(al::case_compare(mode, "basic"sv) == 0)
|
||||||
{
|
{
|
||||||
ERR("HRTF mode \"%s\" deprecated, substituting \"%s\"\n", modeopt->c_str(), "ambi2");
|
ERR("HRTF mode \"{}\" deprecated, substituting \"{}\"", *modeopt, "ambi2");
|
||||||
mode = "ambi2";
|
mode = "ambi2";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -937,16 +965,16 @@ void InitHrtfPanning(ALCdevice *device)
|
||||||
{ return al::case_compare(mode, entry.name) == 0; };
|
{ return al::case_compare(mode, entry.name) == 0; };
|
||||||
auto iter = std::find_if(hrtf_modes.begin(), hrtf_modes.end(), match_entry);
|
auto iter = std::find_if(hrtf_modes.begin(), hrtf_modes.end(), match_entry);
|
||||||
if(iter == hrtf_modes.end())
|
if(iter == hrtf_modes.end())
|
||||||
ERR("Unexpected hrtf-mode: %s\n", modeopt->c_str());
|
ERR("Unexpected hrtf-mode: {}", *modeopt);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
device->mRenderMode = iter->mode;
|
device->mRenderMode = iter->mode;
|
||||||
ambi_order = iter->order;
|
ambi_order = iter->order;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TRACE("%u%s order %sHRTF rendering enabled, using \"%s\"\n", ambi_order,
|
TRACE("{}{} order {}HRTF rendering enabled, using \"{}\"", ambi_order,
|
||||||
GetCounterSuffix(ambi_order), (device->mRenderMode == RenderMode::Hrtf) ? "+ Full " : "",
|
GetCounterSuffix(ambi_order), (device->mRenderMode == RenderMode::Hrtf) ? "+ Full " : "",
|
||||||
device->mHrtfName.c_str());
|
device->mHrtfName);
|
||||||
|
|
||||||
bool perHrirMin{false};
|
bool perHrirMin{false};
|
||||||
auto AmbiPoints = al::span{AmbiPoints1O}.subspan(0);
|
auto AmbiPoints = al::span{AmbiPoints1O}.subspan(0);
|
||||||
|
|
@ -983,7 +1011,7 @@ void InitHrtfPanning(ALCdevice *device)
|
||||||
InitNearFieldCtrl(device, Hrtf->mFields[0].distance, ambi_order, true);
|
InitNearFieldCtrl(device, Hrtf->mFields[0].distance, ambi_order, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitUhjPanning(ALCdevice *device)
|
void InitUhjPanning(al::Device *device)
|
||||||
{
|
{
|
||||||
/* UHJ is always 2D first-order. */
|
/* UHJ is always 2D first-order. */
|
||||||
static constexpr size_t count{Ambi2DChannelsFromOrder(1)};
|
static constexpr size_t count{Ambi2DChannelsFromOrder(1)};
|
||||||
|
|
@ -996,11 +1024,21 @@ void InitUhjPanning(ALCdevice *device)
|
||||||
[](const uint8_t &acn) noexcept -> BFChannelConfig
|
[](const uint8_t &acn) noexcept -> BFChannelConfig
|
||||||
{ return BFChannelConfig{1.0f/AmbiScale::FromUHJ[acn], acn}; });
|
{ return BFChannelConfig{1.0f/AmbiScale::FromUHJ[acn], acn}; });
|
||||||
AllocChannels(device, count, device->channelsFromFmt());
|
AllocChannels(device, count, device->channelsFromFmt());
|
||||||
|
|
||||||
|
/* TODO: Should this default to something else? This is simply a regular
|
||||||
|
* (first-order) B-Format mixing which just happens to be UHJ-encoded. As I
|
||||||
|
* understand it, a proper first-order B-Format signal essentially has an
|
||||||
|
* infinite control distance, which we can't really do. However, from what
|
||||||
|
* I've read, 2 meters or so should be sufficient as the near-field
|
||||||
|
* reference becomes inconsequential beyond that.
|
||||||
|
*/
|
||||||
|
const auto spkr_dist = ConfigValueFloat({}, "uhj"sv, "distance-ref"sv).value_or(2.0f);
|
||||||
|
InitNearFieldCtrl(device, spkr_dist, device->mAmbiOrder, !device->m2DMixing);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncoding> stereomode)
|
void aluInitRenderer(al::Device *device, int hrtf_id, std::optional<StereoEncoding> stereomode)
|
||||||
{
|
{
|
||||||
/* Hold the HRTF the device last used, in case it's used again. */
|
/* Hold the HRTF the device last used, in case it's used again. */
|
||||||
HrtfStorePtr old_hrtf{std::move(device->mHrtf)};
|
HrtfStorePtr old_hrtf{std::move(device->mHrtf)};
|
||||||
|
|
@ -1044,25 +1082,25 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncodin
|
||||||
AmbDecConf conf{};
|
AmbDecConf conf{};
|
||||||
if(auto err = conf.load(config))
|
if(auto err = conf.load(config))
|
||||||
{
|
{
|
||||||
ERR("Failed to load layout file %s\n", config);
|
ERR("Failed to load layout file {}", config);
|
||||||
ERR(" %s\n", err->c_str());
|
ERR(" {}", *err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(conf.Speakers.size() > MaxOutputChannels)
|
if(conf.Speakers.size() > MaxOutputChannels)
|
||||||
{
|
{
|
||||||
ERR("Unsupported decoder speaker count %zu (max %zu)\n", conf.Speakers.size(),
|
ERR("Unsupported decoder speaker count {} (max {})", conf.Speakers.size(),
|
||||||
MaxOutputChannels);
|
MaxOutputChannels);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(conf.ChanMask > Ambi3OrderMask)
|
if(conf.ChanMask > Ambi3OrderMask)
|
||||||
{
|
{
|
||||||
ERR("Unsupported decoder channel mask 0x%04x (max 0x%x)\n", conf.ChanMask,
|
ERR("Unsupported decoder channel mask {:#x} (max {:#x})", conf.ChanMask,
|
||||||
Ambi3OrderMask);
|
Ambi3OrderMask);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("Using %s decoder: \"%s\"\n", DevFmtChannelsString(device->FmtChans),
|
TRACE("Using {} decoder: \"{}\"", DevFmtChannelsString(device->FmtChans),
|
||||||
conf.Description.c_str());
|
conf.Description);
|
||||||
device->mXOverFreq = std::clamp(conf.XOverFreq, 100.0f, 1000.0f);
|
device->mXOverFreq = std::clamp(conf.XOverFreq, 100.0f, 1000.0f);
|
||||||
|
|
||||||
decoder_store = std::make_unique<DecoderConfig<DualBand,MaxOutputChannels>>();
|
decoder_store = std::make_unique<DecoderConfig<DualBand,MaxOutputChannels>>();
|
||||||
|
|
@ -1081,7 +1119,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncodin
|
||||||
usingCustom = load_config(decopt->c_str());
|
usingCustom = load_config(decopt->c_str());
|
||||||
}
|
}
|
||||||
if(!usingCustom && device->FmtChans != DevFmtAmbi3D)
|
if(!usingCustom && device->FmtChans != DevFmtAmbi3D)
|
||||||
TRACE("Using built-in %s decoder\n", DevFmtChannelsString(device->FmtChans));
|
TRACE("Using built-in {} decoder", DevFmtChannelsString(device->FmtChans));
|
||||||
|
|
||||||
/* Enable the stablizer only for formats that have front-left, front-
|
/* Enable the stablizer only for formats that have front-left, front-
|
||||||
* right, and front-center outputs.
|
* right, and front-center outputs.
|
||||||
|
|
@ -1113,8 +1151,8 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncodin
|
||||||
}
|
}
|
||||||
if(auto *ambidec{device->AmbiDecoder.get()})
|
if(auto *ambidec{device->AmbiDecoder.get()})
|
||||||
{
|
{
|
||||||
device->PostProcess = ambidec->hasStablizer() ? &ALCdevice::ProcessAmbiDecStablized
|
device->PostProcess = ambidec->hasStablizer() ? &al::Device::ProcessAmbiDecStablized
|
||||||
: &ALCdevice::ProcessAmbiDec;
|
: &al::Device::ProcessAmbiDec;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1132,7 +1170,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncodin
|
||||||
if(hrtf_id >= 0 && static_cast<uint>(hrtf_id) < device->mHrtfList.size())
|
if(hrtf_id >= 0 && static_cast<uint>(hrtf_id) < device->mHrtfList.size())
|
||||||
{
|
{
|
||||||
const std::string_view hrtfname{device->mHrtfList[static_cast<uint>(hrtf_id)]};
|
const std::string_view hrtfname{device->mHrtfList[static_cast<uint>(hrtf_id)]};
|
||||||
if(HrtfStorePtr hrtf{GetLoadedHrtf(hrtfname, device->Frequency)})
|
if(HrtfStorePtr hrtf{GetLoadedHrtf(hrtfname, device->mSampleRate)})
|
||||||
{
|
{
|
||||||
device->mHrtf = std::move(hrtf);
|
device->mHrtf = std::move(hrtf);
|
||||||
device->mHrtfName = hrtfname;
|
device->mHrtfName = hrtfname;
|
||||||
|
|
@ -1143,7 +1181,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncodin
|
||||||
{
|
{
|
||||||
for(const std::string_view hrtfname : device->mHrtfList)
|
for(const std::string_view hrtfname : device->mHrtfList)
|
||||||
{
|
{
|
||||||
if(HrtfStorePtr hrtf{GetLoadedHrtf(hrtfname, device->Frequency)})
|
if(HrtfStorePtr hrtf{GetLoadedHrtf(hrtfname, device->mSampleRate)})
|
||||||
{
|
{
|
||||||
device->mHrtf = std::move(hrtf);
|
device->mHrtf = std::move(hrtf);
|
||||||
device->mHrtfName = hrtfname;
|
device->mHrtfName = hrtfname;
|
||||||
|
|
@ -1165,7 +1203,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncodin
|
||||||
}
|
}
|
||||||
|
|
||||||
InitHrtfPanning(device);
|
InitHrtfPanning(device);
|
||||||
device->PostProcess = &ALCdevice::ProcessHrtf;
|
device->PostProcess = &al::Device::ProcessHrtf;
|
||||||
device->mHrtfStatus = ALC_HRTF_ENABLED_SOFT;
|
device->mHrtfStatus = ALC_HRTF_ENABLED_SOFT;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1174,23 +1212,27 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncodin
|
||||||
|
|
||||||
if(stereomode.value_or(StereoEncoding::Default) == StereoEncoding::Uhj)
|
if(stereomode.value_or(StereoEncoding::Default) == StereoEncoding::Uhj)
|
||||||
{
|
{
|
||||||
|
auto ftype = std::string_view{};
|
||||||
switch(UhjEncodeQuality)
|
switch(UhjEncodeQuality)
|
||||||
{
|
{
|
||||||
case UhjQualityType::IIR:
|
case UhjQualityType::IIR:
|
||||||
device->mUhjEncoder = std::make_unique<UhjEncoderIIR>();
|
device->mUhjEncoder = std::make_unique<UhjEncoderIIR>();
|
||||||
|
ftype = "IIR"sv;
|
||||||
break;
|
break;
|
||||||
case UhjQualityType::FIR256:
|
case UhjQualityType::FIR256:
|
||||||
device->mUhjEncoder = std::make_unique<UhjEncoder<UhjLength256>>();
|
device->mUhjEncoder = std::make_unique<UhjEncoder<UhjLength256>>();
|
||||||
|
ftype = "FIR-256"sv;
|
||||||
break;
|
break;
|
||||||
case UhjQualityType::FIR512:
|
case UhjQualityType::FIR512:
|
||||||
device->mUhjEncoder = std::make_unique<UhjEncoder<UhjLength512>>();
|
device->mUhjEncoder = std::make_unique<UhjEncoder<UhjLength512>>();
|
||||||
|
ftype = "FIR-512"sv;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
assert(device->mUhjEncoder != nullptr);
|
assert(device->mUhjEncoder != nullptr);
|
||||||
|
|
||||||
TRACE("UHJ enabled\n");
|
TRACE("UHJ enabled ({} encoder)", ftype);
|
||||||
InitUhjPanning(device);
|
InitUhjPanning(device);
|
||||||
device->PostProcess = &ALCdevice::ProcessUhj;
|
device->PostProcess = &al::Device::ProcessUhj;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1202,19 +1244,19 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncodin
|
||||||
if(*cflevopt > 0 && *cflevopt <= 6)
|
if(*cflevopt > 0 && *cflevopt <= 6)
|
||||||
{
|
{
|
||||||
auto bs2b = std::make_unique<Bs2b::bs2b>();
|
auto bs2b = std::make_unique<Bs2b::bs2b>();
|
||||||
bs2b->set_params(*cflevopt, static_cast<int>(device->Frequency));
|
bs2b->set_params(*cflevopt, static_cast<int>(device->mSampleRate));
|
||||||
device->Bs2b = std::move(bs2b);
|
device->Bs2b = std::move(bs2b);
|
||||||
TRACE("BS2B enabled\n");
|
TRACE("BS2B enabled");
|
||||||
InitPanning(device);
|
InitPanning(device);
|
||||||
device->PostProcess = &ALCdevice::ProcessBs2b;
|
device->PostProcess = &al::Device::ProcessBs2b;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("Stereo rendering\n");
|
TRACE("Stereo rendering");
|
||||||
InitPanning(device);
|
InitPanning(device);
|
||||||
device->PostProcess = &ALCdevice::ProcessAmbiDec;
|
device->PostProcess = &al::Device::ProcessAmbiDec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,11 @@
|
||||||
# between 24 and 48 points, with anti-aliasing)
|
# between 24 and 48 points, with anti-aliasing)
|
||||||
# fast_bsinc24 - same as bsinc24, except without interpolation between down-
|
# fast_bsinc24 - same as bsinc24, except without interpolation between down-
|
||||||
# sampling scales
|
# sampling scales
|
||||||
#resampler = gaussian
|
# bsinc48 - extrapolates samples using a band-limited Sinc filter (48 points,
|
||||||
|
# with anti-aliasing)
|
||||||
|
# fast_bsinc48 - same as bsinc48, except without interpolation between down-
|
||||||
|
# sampling scales
|
||||||
|
#resampler = spline
|
||||||
|
|
||||||
## rt-prio: (global)
|
## rt-prio: (global)
|
||||||
# Sets the real-time priority value for the mixing thread. Not all drivers may
|
# Sets the real-time priority value for the mixing thread. Not all drivers may
|
||||||
|
|
@ -587,6 +591,13 @@
|
||||||
# configurations. Very experimental.
|
# configurations. Very experimental.
|
||||||
#spatial-api = false
|
#spatial-api = false
|
||||||
|
|
||||||
|
## exclusive-mode:
|
||||||
|
# Enables Exlusive mode for playback devices. This uses the device directly,
|
||||||
|
# allowing lower latencies but prevents the device from being used multiple
|
||||||
|
# times simultaneously. Ignores the periods setting when enabled, as WASAPI
|
||||||
|
# automatically sets a buffer size based on the period size.
|
||||||
|
#exclusive-mode = false
|
||||||
|
|
||||||
## allow-resampler:
|
## allow-resampler:
|
||||||
# Specifies whether to allow an extra resampler pass on the output. Enabling
|
# Specifies whether to allow an extra resampler pass on the output. Enabling
|
||||||
# this will allow the playback device to be set to a different sample rate
|
# this will allow the playback device to be set to a different sample rate
|
||||||
|
|
@ -645,6 +656,11 @@
|
||||||
# Sets whether to enable EAX extensions or not.
|
# Sets whether to enable EAX extensions or not.
|
||||||
#enable = true
|
#enable = true
|
||||||
|
|
||||||
|
## trace-commits: (global)
|
||||||
|
# Sets whether log EAX property commits with trace messages. This can
|
||||||
|
# significantly increase the amount of log messages for apps that use EAX.
|
||||||
|
#trace-commits = false
|
||||||
|
|
||||||
##
|
##
|
||||||
## Per-game compatibility options (these should only be set in per-game config
|
## Per-game compatibility options (these should only be set in per-game config
|
||||||
## files, *NOT* system- or user-level!)
|
## files, *NOT* system- or user-level!)
|
||||||
|
|
@ -686,3 +702,15 @@
|
||||||
## reverse-z: (global)
|
## reverse-z: (global)
|
||||||
# Reverses the local Z (front-back) position of 3D sound sources.
|
# Reverses the local Z (front-back) position of 3D sound sources.
|
||||||
#reverse-z = false
|
#reverse-z = false
|
||||||
|
|
||||||
|
## vendor-override:
|
||||||
|
# Overrides the string returned by alGetString(AL_VENDOR).
|
||||||
|
#vendor-override =
|
||||||
|
|
||||||
|
## version-override:
|
||||||
|
# Overrides the string returned by alGetString(AL_VERSION).
|
||||||
|
#version-override =
|
||||||
|
|
||||||
|
## renderer-override:
|
||||||
|
# Overrides the string returned by alGetString(AL_RENDERER).
|
||||||
|
#renderer-override =
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
version: 1.23.1.{build}
|
version: 1.24.3.{build}
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
||||||
|
|
|
||||||
|
|
@ -80,8 +80,37 @@ macro(find_component _component _pkgconfig _library _header)
|
||||||
${PC_LIB${_component}_LIBRARY_DIRS}
|
${PC_LIB${_component}_LIBRARY_DIRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
set(${_component}_VERSION ${PC_${_component}_VERSION} CACHE STRING "The ${_component} version number." FORCE)
|
if(DEFINED ${PC_${_component}_VERSION})
|
||||||
set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING "The ${_component} CFLAGS." FORCE)
|
set(${_component}_VERSION ${PC_${_component}_VERSION} CACHE STRING
|
||||||
|
"The ${_component} version number." FORCE)
|
||||||
|
elseif(EXISTS "${${_component}_INCLUDE_DIRS}/${_pkgconfig}/version.h")
|
||||||
|
if(EXISTS "${${_component}_INCLUDE_DIRS}/${_pkgconfig}/version_major.h")
|
||||||
|
file(STRINGS "${${_component}_INCLUDE_DIRS}/${_pkgconfig}/version_major.h" majorver
|
||||||
|
REGEX "^#define[ \t]+LIB${_component}_VERSION_MAJOR[ \t]+[0-9]+$")
|
||||||
|
else()
|
||||||
|
file(STRINGS "${${_component}_INCLUDE_DIRS}/${_pkgconfig}/version.h" majorver
|
||||||
|
REGEX "^#define[ \t]+LIB${_component}_VERSION_MAJOR[ \t]+[0-9]+$")
|
||||||
|
endif()
|
||||||
|
file(STRINGS "${${_component}_INCLUDE_DIRS}/${_pkgconfig}/version.h" minorver
|
||||||
|
REGEX "^#define[ \t]+LIB${_component}_VERSION_MINOR[ \t]+[0-9]+$")
|
||||||
|
file(STRINGS "${${_component}_INCLUDE_DIRS}/${_pkgconfig}/version.h" microver
|
||||||
|
REGEX "^#define[ \t]+LIB${_component}_VERSION_MICRO[ \t]+[0-9]+$")
|
||||||
|
|
||||||
|
string(REGEX REPLACE "^#define[ \t]+LIB${_component}_VERSION_MAJOR[ \t]+([0-9]+)$" "\\1"
|
||||||
|
majorver "${majorver}")
|
||||||
|
string(REGEX REPLACE "^#define[ \t]+LIB${_component}_VERSION_MINOR[ \t]+([0-9]+)$" "\\1"
|
||||||
|
minorver "${minorver}")
|
||||||
|
string(REGEX REPLACE "^#define[ \t]+LIB${_component}_VERSION_MICRO[ \t]+([0-9]+)$" "\\1"
|
||||||
|
microver "${microver}")
|
||||||
|
|
||||||
|
set(${_component}_VERSION "${majorver}.${minorver}.${microver}" CACHE STRING
|
||||||
|
"The ${_component} version number." FORCE)
|
||||||
|
unset(microver)
|
||||||
|
unset(minorver)
|
||||||
|
unset(majorver)
|
||||||
|
endif()
|
||||||
|
set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING
|
||||||
|
"The ${_component} CFLAGS." FORCE)
|
||||||
|
|
||||||
set_component_found(${_component})
|
set_component_found(${_component})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ find_path(JACK_INCLUDE_DIR NAMES jack/jack.h
|
||||||
DOC "The JACK include directory"
|
DOC "The JACK include directory"
|
||||||
)
|
)
|
||||||
|
|
||||||
find_library(JACK_LIBRARY NAMES jack
|
find_library(JACK_LIBRARY NAMES jack jack64
|
||||||
DOC "The JACK library"
|
DOC "The JACK library"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,44 @@
|
||||||
|
|
||||||
#include "alassert.h"
|
#include "alassert.h"
|
||||||
|
|
||||||
#include <exception>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
[[noreturn]]
|
||||||
|
void throw_error(const std::string &message)
|
||||||
|
{
|
||||||
|
throw std::runtime_error{message};
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* namespace */
|
||||||
|
|
||||||
namespace al {
|
namespace al {
|
||||||
|
|
||||||
[[noreturn]]
|
[[noreturn]]
|
||||||
void do_assert(const char *message, int linenum, const char *filename, const char *funcname) noexcept
|
void do_assert(const char *message, int linenum, const char *filename, const char *funcname) noexcept
|
||||||
{
|
{
|
||||||
std::string errstr{filename};
|
/* Throwing an exception that tries to leave a noexcept function will
|
||||||
|
* hopefully cause the system to provide info about the caught exception in
|
||||||
|
* an error dialog. At least on Linux, this results in the process printing
|
||||||
|
*
|
||||||
|
* terminate called after throwing an instance of 'std::runtime_error'
|
||||||
|
* what(): <message here>
|
||||||
|
*
|
||||||
|
* before terminating from a SIGABRT. Hopefully Windows and Mac will do the
|
||||||
|
* appropriate things with the message to alert the user about an abnormal
|
||||||
|
* termination.
|
||||||
|
*/
|
||||||
|
auto errstr = std::string{filename};
|
||||||
errstr += ':';
|
errstr += ':';
|
||||||
errstr += std::to_string(linenum);
|
errstr += std::to_string(linenum);
|
||||||
errstr += ": ";
|
errstr += ": ";
|
||||||
errstr += funcname;
|
errstr += funcname;
|
||||||
errstr += ": ";
|
errstr += ": ";
|
||||||
errstr += message;
|
errstr += message;
|
||||||
/* Calling std::terminate in a catch block hopefully causes the system to
|
|
||||||
* provide info about the caught exception in the error dialog. At least on
|
throw_error(errstr);
|
||||||
* Linux, this results in the process printing
|
|
||||||
*
|
|
||||||
* terminate called after throwing an instance of 'std::runtime_error'
|
|
||||||
* what(): <message here>
|
|
||||||
*
|
|
||||||
* before terminating from a SIGABRT. Hopefully Windows and Mac will do the
|
|
||||||
* appropriate things with the message for an abnormal termination.
|
|
||||||
*/
|
|
||||||
try {
|
|
||||||
throw std::runtime_error{errstr};
|
|
||||||
}
|
|
||||||
catch(...) {
|
|
||||||
std::terminate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* namespace al */
|
} /* namespace al */
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef AL_BIT_H
|
#ifndef AL_BIT_H
|
||||||
#define AL_BIT_H
|
#define AL_BIT_H
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#ifndef __GNUC__
|
#ifndef __GNUC__
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
@ -25,6 +26,16 @@ To> bit_cast(const From &src) noexcept
|
||||||
return *std::launder(reinterpret_cast<To*>(dst.data()));
|
return *std::launder(reinterpret_cast<To*>(dst.data()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::enable_if_t<std::is_integral_v<T>,
|
||||||
|
T> byteswap(T value) noexcept
|
||||||
|
{
|
||||||
|
static_assert(std::has_unique_object_representations_v<T>);
|
||||||
|
auto bytes = al::bit_cast<std::array<std::byte,sizeof(T)>>(value);
|
||||||
|
std::reverse(bytes.begin(), bytes.end());
|
||||||
|
return al::bit_cast<T>(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __BYTE_ORDER__
|
#ifdef __BYTE_ORDER__
|
||||||
enum class endian {
|
enum class endian {
|
||||||
little = __ORDER_LITTLE_ENDIAN__,
|
little = __ORDER_LITTLE_ENDIAN__,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using ushort = unsigned short;
|
using ushort = unsigned short;
|
||||||
using ushort2 = std::pair<ushort,ushort>;
|
using ushort2 = std::array<ushort,2>;
|
||||||
using complex_d = std::complex<double>;
|
using complex_d = std::complex<double>;
|
||||||
|
|
||||||
constexpr std::size_t BitReverseCounter(std::size_t log2_size) noexcept
|
constexpr std::size_t BitReverseCounter(std::size_t log2_size) noexcept
|
||||||
|
|
@ -56,8 +56,8 @@ struct BitReverser {
|
||||||
|
|
||||||
if(idx < revidx)
|
if(idx < revidx)
|
||||||
{
|
{
|
||||||
mData[ret_i].first = static_cast<ushort>(idx);
|
mData[ret_i][0] = static_cast<ushort>(idx);
|
||||||
mData[ret_i].second = static_cast<ushort>(revidx);
|
mData[ret_i][1] = static_cast<ushort>(revidx);
|
||||||
++ret_i;
|
++ret_i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -122,7 +122,7 @@ void complex_fft(const al::span<std::complex<double>> buffer, const double sign)
|
||||||
if(log2_size < gBitReverses.size()) LIKELY
|
if(log2_size < gBitReverses.size()) LIKELY
|
||||||
{
|
{
|
||||||
for(auto &rev : gBitReverses[log2_size])
|
for(auto &rev : gBitReverses[log2_size])
|
||||||
std::swap(buffer[rev.first], buffer[rev.second]);
|
std::swap(buffer[rev[0]], buffer[rev[1]]);
|
||||||
|
|
||||||
/* Iterative form of Danielson-Lanczos lemma */
|
/* Iterative form of Danielson-Lanczos lemma */
|
||||||
for(std::size_t i{0};i < log2_size;++i)
|
for(std::size_t i{0};i < log2_size;++i)
|
||||||
|
|
|
||||||
|
|
@ -123,31 +123,74 @@ class out_ptr_t {
|
||||||
static_assert(!std::is_same_v<PT,void*>);
|
static_assert(!std::is_same_v<PT,void*>);
|
||||||
|
|
||||||
SP &mRes;
|
SP &mRes;
|
||||||
std::variant<PT,void*> mPtr{};
|
std::variant<PT,void*> mPtr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
out_ptr_t(SP &res) : mRes{res} { }
|
explicit out_ptr_t(SP &res) : mRes{res} { }
|
||||||
~out_ptr_t()
|
~out_ptr_t() { std::visit([this](auto &ptr) { mRes.reset(static_cast<PT>(ptr)); }, mPtr); }
|
||||||
{
|
|
||||||
auto set_res = [this](auto &ptr)
|
out_ptr_t() = delete;
|
||||||
{ mRes.reset(static_cast<PT>(ptr)); };
|
|
||||||
std::visit(set_res, mPtr);
|
|
||||||
}
|
|
||||||
out_ptr_t(const out_ptr_t&) = delete;
|
out_ptr_t(const out_ptr_t&) = delete;
|
||||||
out_ptr_t& operator=(const out_ptr_t&) = delete;
|
out_ptr_t& operator=(const out_ptr_t&) = delete;
|
||||||
|
|
||||||
operator PT*() noexcept
|
operator PT*() noexcept /* NOLINT(google-explicit-constructor) */
|
||||||
{ return &std::get<PT>(mPtr); }
|
{ return &std::get<PT>(mPtr); }
|
||||||
|
|
||||||
operator void**() noexcept
|
operator void**() noexcept /* NOLINT(google-explicit-constructor) */
|
||||||
{ return &mPtr.template emplace<void*>(); }
|
{ return &mPtr.template emplace<void*>(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T=void, typename SP, typename ...Args>
|
template<typename T=void, typename SP, typename ...Args>
|
||||||
auto out_ptr(SP &res)
|
auto out_ptr(SP &res, Args&& ...args)
|
||||||
{
|
{
|
||||||
using ptype = typename SP::element_type*;
|
static_assert(sizeof...(args) == 0);
|
||||||
return out_ptr_t<SP,ptype>{res};
|
if constexpr(std::is_same_v<T,void>)
|
||||||
|
{
|
||||||
|
using ptype = typename SP::element_type*;
|
||||||
|
return out_ptr_t<SP,ptype,Args...>{res};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return out_ptr_t<SP,T,Args...>{res};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename SP, typename PT, typename ...Args>
|
||||||
|
class inout_ptr_t {
|
||||||
|
static_assert(!std::is_same_v<PT,void*>);
|
||||||
|
|
||||||
|
SP &mRes;
|
||||||
|
std::variant<PT,void*> mPtr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit inout_ptr_t(SP &res) : mRes{res}, mPtr{res.get()} { }
|
||||||
|
~inout_ptr_t()
|
||||||
|
{
|
||||||
|
mRes.release();
|
||||||
|
std::visit([this](auto &ptr) { mRes.reset(static_cast<PT>(ptr)); }, mPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
inout_ptr_t() = delete;
|
||||||
|
inout_ptr_t(const inout_ptr_t&) = delete;
|
||||||
|
inout_ptr_t& operator=(const inout_ptr_t&) = delete;
|
||||||
|
|
||||||
|
operator PT*() noexcept /* NOLINT(google-explicit-constructor) */
|
||||||
|
{ return &std::get<PT>(mPtr); }
|
||||||
|
|
||||||
|
operator void**() noexcept /* NOLINT(google-explicit-constructor) */
|
||||||
|
{ return &mPtr.template emplace<void*>(mRes.get()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T=void, typename SP, typename ...Args>
|
||||||
|
auto inout_ptr(SP &res, Args&& ...args)
|
||||||
|
{
|
||||||
|
static_assert(sizeof...(args) == 0);
|
||||||
|
if constexpr(std::is_same_v<T,void>)
|
||||||
|
{
|
||||||
|
using ptype = typename SP::element_type*;
|
||||||
|
return inout_ptr_t<SP,ptype,Args...>{res};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return inout_ptr_t<SP,T,Args...>{res};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace al
|
} // namespace al
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue