mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-12 23:24:41 +00:00
Merge pull request #1292 from Azaezel/alpha41/openALUpgrade
open al upgrade
This commit is contained in:
commit
7db28feb67
289 changed files with 33931 additions and 27466 deletions
|
|
@ -108,7 +108,7 @@ mark_as_advanced(SDL_WAYLAND)
|
||||||
mark_as_advanced(SDL_WERROR)
|
mark_as_advanced(SDL_WERROR)
|
||||||
mark_as_advanced(SDL_X11)
|
mark_as_advanced(SDL_X11)
|
||||||
mark_as_advanced(SDL_XINPUT)
|
mark_as_advanced(SDL_XINPUT)
|
||||||
|
mark_as_advanced(SDL2_DIR)
|
||||||
add_subdirectory(sdl ${TORQUE_LIB_TARG_DIRECTORY}/sdl2 EXCLUDE_FROM_ALL)
|
add_subdirectory(sdl ${TORQUE_LIB_TARG_DIRECTORY}/sdl2 EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
add_subdirectory(nativeFileDialogs ${TORQUE_LIB_TARG_DIRECTORY}/nfd EXCLUDE_FROM_ALL)
|
add_subdirectory(nativeFileDialogs ${TORQUE_LIB_TARG_DIRECTORY}/nfd EXCLUDE_FROM_ALL)
|
||||||
|
|
@ -199,64 +199,59 @@ add_subdirectory(glad ${TORQUE_LIB_TARG_DIRECTORY}/glad EXCLUDE_FROM_ALL)
|
||||||
if(TORQUE_SFX_OPENAL)
|
if(TORQUE_SFX_OPENAL)
|
||||||
advanced_option(TORQUE_OGGVORBIS "Enable OGG Vorbis" ON)
|
advanced_option(TORQUE_OGGVORBIS "Enable OGG Vorbis" ON)
|
||||||
advanced_option(ALSOFT_EAX "Enable legacy EAX extensions" ${WIN32})
|
advanced_option(ALSOFT_EAX "Enable legacy EAX extensions" ${WIN32})
|
||||||
advanced_option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" ON)
|
advanced_option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" OFF)
|
||||||
advanced_option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" ON)
|
advanced_option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" OFF)
|
||||||
advanced_option(ALSOFT_UPDATE_BUILD_VERSION "Update git build version info" ON)
|
advanced_option(ALSOFT_UPDATE_BUILD_VERSION "Update git build version info" ON)
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_OBOE)
|
mark_as_advanced(ALSOFT_BACKEND_COREAUDIO)
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_PIPEWIRE)
|
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_RTKIT)
|
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_SSE3)
|
|
||||||
#Hide some unnecessary fields as advanced
|
|
||||||
mark_as_advanced(ALSOFT_INSTALL_AMBDEC_PRESETS)
|
|
||||||
mark_as_advanced(ALSOFT_BACKEND_DSOUND)
|
mark_as_advanced(ALSOFT_BACKEND_DSOUND)
|
||||||
mark_as_advanced(ALSOFT_BACKEND_MMDEVAPI)
|
mark_as_advanced(ALSOFT_BACKEND_JACK)
|
||||||
|
mark_as_advanced(ALSOFT_BACKEND_OBOE)
|
||||||
|
mark_as_advanced(ALSOFT_BACKEND_OPENSL)
|
||||||
|
mark_as_advanced(ALSOFT_BACKEND_PIPEWIRE)
|
||||||
|
mark_as_advanced(ALSOFT_BACKEND_PORTAUDIO)
|
||||||
|
mark_as_advanced(ALSOFT_BACKEND_PULSEAUDIO)
|
||||||
|
mark_as_advanced(ALSOFT_BACKEND_SDL2)
|
||||||
|
mark_as_advanced(ALSOFT_BACKEND_WASAPI)
|
||||||
mark_as_advanced(ALSOFT_BACKEND_WAVE)
|
mark_as_advanced(ALSOFT_BACKEND_WAVE)
|
||||||
mark_as_advanced(ALSOFT_BACKEND_WINMM)
|
mark_as_advanced(ALSOFT_BACKEND_WINMM)
|
||||||
mark_as_advanced(ALSOFT_INSTALL_CONFIG)
|
mark_as_advanced(ALSOFT_BUILD_ROUTER)
|
||||||
|
mark_as_advanced(ALSOFT_CPUEXT_NEON)
|
||||||
mark_as_advanced(ALSOFT_CPUEXT_SSE)
|
mark_as_advanced(ALSOFT_CPUEXT_SSE)
|
||||||
mark_as_advanced(ALSOFT_CPUEXT_SSE2)
|
mark_as_advanced(ALSOFT_CPUEXT_SSE2)
|
||||||
mark_as_advanced(ALSOFT_CPUEXT_SSE3)
|
mark_as_advanced(ALSOFT_CPUEXT_SSE3)
|
||||||
mark_as_advanced(ALSOFT_CPUEXT_SSE4_1)
|
mark_as_advanced(ALSOFT_CPUEXT_SSE4_1)
|
||||||
mark_as_advanced(ALSOFT_DLOPEN)
|
mark_as_advanced(ALSOFT_DLOPEN)
|
||||||
mark_as_advanced(ALSOFT_EMBED_HRTF_DATA)
|
mark_as_advanced(ALSOFT_EAX)
|
||||||
mark_as_advanced(ALSOFT_EXAMPLES)
|
advanced_option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library footprint)" OFF)
|
||||||
mark_as_advanced(ALSOFT_INSTALL_HRTF_DATA)
|
advanced_option(ALSOFT_EXAMPLES "Build example programs" OFF)
|
||||||
mark_as_advanced(ALSOFT_INSTALL)
|
mark_as_advanced(ALSOFT_INSTALL)
|
||||||
|
advanced_option(ALSOFT_INSTALL_CONFIG "Install alsoft.conf sample configuration file" OFF)
|
||||||
|
advanced_option(ALSOFT_INSTALL_HRTF_DATA "Install HRTF data files" OFF)
|
||||||
|
advanced_option(ALSOFT_INSTALL_AMBDEC_PRESETS "Install AmbDec preset files" OFF)
|
||||||
|
advanced_option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" OFF)
|
||||||
|
advanced_option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" OFF)
|
||||||
|
mark_as_advanced(ALSOFT_UPDATE_BUILD_VERSION)
|
||||||
mark_as_advanced(ALSOFT_NO_CONFIG_UTIL)
|
mark_as_advanced(ALSOFT_NO_CONFIG_UTIL)
|
||||||
mark_as_advanced(ALSOFT_NO_UID_DEFS)
|
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_ALSA)
|
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_COREAUDIO)
|
mark_as_advanced(ALSOFT_REQUIRE_COREAUDIO)
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_DSOUND)
|
mark_as_advanced(ALSOFT_REQUIRE_DSOUND)
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_JACK)
|
mark_as_advanced(ALSOFT_REQUIRE_JACK)
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_MMDEVAPI)
|
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_NEON)
|
mark_as_advanced(ALSOFT_REQUIRE_NEON)
|
||||||
|
mark_as_advanced(ALSOFT_REQUIRE_OBOE)
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_OPENSL)
|
mark_as_advanced(ALSOFT_REQUIRE_OPENSL)
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_OSS)
|
mark_as_advanced(ALSOFT_REQUIRE_PIPEWIRE)
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_PORTAUDIO)
|
mark_as_advanced(ALSOFT_REQUIRE_PORTAUDIO)
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_PULSEAUDIO)
|
mark_as_advanced(ALSOFT_REQUIRE_PULSEAUDIO)
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_QSA)
|
mark_as_advanced(ALSOFT_REQUIRE_SDL2)
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_SNDIO)
|
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_SOLARIS)
|
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_SSE)
|
mark_as_advanced(ALSOFT_REQUIRE_SSE)
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_SSE2)
|
mark_as_advanced(ALSOFT_REQUIRE_SSE2)
|
||||||
|
mark_as_advanced(ALSOFT_REQUIRE_SSE3)
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_SSE4_1)
|
mark_as_advanced(ALSOFT_REQUIRE_SSE4_1)
|
||||||
|
mark_as_advanced(ALSOFT_REQUIRE_WASAPI)
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_WINMM)
|
mark_as_advanced(ALSOFT_REQUIRE_WINMM)
|
||||||
|
mark_as_advanced(ALSOFT_SEARCH_INSTALL_DATADIR)
|
||||||
mark_as_advanced(ALSOFT_TESTS)
|
mark_as_advanced(ALSOFT_TESTS)
|
||||||
mark_as_advanced(ALSOFT_UTILS)
|
mark_as_advanced(ALSOFT_UTILS)
|
||||||
mark_as_advanced(ALSOFT_WERROR)
|
mark_as_advanced(ALSOFT_WERROR)
|
||||||
mark_as_advanced(COREAUDIO_FRAMEWORK)
|
|
||||||
mark_as_advanced(CMAKE_DEBUG_POSTFIX)
|
|
||||||
mark_as_advanced(FORCE_STATIC_VCRT)
|
|
||||||
mark_as_advanced(ALSOFT_BACKEND_WASAPI)
|
|
||||||
mark_as_advanced(ALSOFT_BUILD_ROUTER)
|
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_SDL2)
|
|
||||||
mark_as_advanced(ALSOFT_REQUIRE_WASAPI)
|
|
||||||
mark_as_advanced(ALSOFT_BACKEND_COREAUDIO)
|
|
||||||
mark_as_advanced(ALSOFT_OSX_FRAMEWORK)
|
|
||||||
mark_as_advanced(ALSOFT_STATIC_LIBGCC)
|
|
||||||
mark_as_advanced(ALSOFT_STATIC_STDCXX)
|
|
||||||
mark_as_advanced(AUDIOTOOLBOX_LIBRARY)
|
|
||||||
mark_as_advanced(AUDIOUNIT_INCLUDE_DIR)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
advanced_option(INSTALL_DOCS "" OFF)
|
advanced_option(INSTALL_DOCS "" OFF)
|
||||||
|
|
@ -273,9 +268,33 @@ add_subdirectory(flac ${TORQUE_LIB_TARG_DIRECTORY}/flac EXCLUDE_FROM_ALL)
|
||||||
advanced_option(INSTALL_DOCS "" OFF)
|
advanced_option(INSTALL_DOCS "" OFF)
|
||||||
advanced_option(OPUS_INSTALL_PKG_CONFIG_MODULE "" OFF)
|
advanced_option(OPUS_INSTALL_PKG_CONFIG_MODULE "" OFF)
|
||||||
advanced_option(OPUS_INSTALL_CMAKE_CONFIG_MODULE "" OFF)
|
advanced_option(OPUS_INSTALL_CMAKE_CONFIG_MODULE "" OFF)
|
||||||
|
mark_as_advanced(OPUS_ASSERTIONS)
|
||||||
|
mark_as_advanced(OPUS_BUILD_PROGRAMS)
|
||||||
|
mark_as_advanced(OPUS_BUILD_SHARED_LIBRARY)
|
||||||
|
mark_as_advanced(OPUS_BUILD_TESTING)
|
||||||
|
mark_as_advanced(OPUS_CHECK_ASM)
|
||||||
|
mark_as_advanced(OPUS_CUSTOM_MODES)
|
||||||
|
mark_as_advanced(OPUS_DISABLE_INTRINSICS)
|
||||||
|
mark_as_advanced(OPUS_ENABLE_FLOAT_API)
|
||||||
|
mark_as_advanced(OPUS_FIXED_POINT)
|
||||||
|
mark_as_advanced(OPUS_FLOAT_APPROX)
|
||||||
|
mark_as_advanced(OPUS_FUZZING)
|
||||||
|
mark_as_advanced(OPUS_HARDENING)
|
||||||
|
mark_as_advanced(OPUS_STACK_PROTECTOR)
|
||||||
|
mark_as_advanced(OPUS_USE_ALLOCA)
|
||||||
|
mark_as_advanced(OPUS_X86_MAY_HAVE_AVX)
|
||||||
|
mark_as_advanced(OPUS_X86_MAY_HAVE_SSE)
|
||||||
|
mark_as_advanced(OPUS_X86_MAY_HAVE_SSE2)
|
||||||
|
mark_as_advanced(OPUS_X86_MAY_HAVE_SSE4_1)
|
||||||
|
mark_as_advanced(OPUS_X86_PRESUME_AVX)
|
||||||
|
mark_as_advanced(OPUS_X86_PRESUME_SSE)
|
||||||
|
mark_as_advanced(OPUS_X86_PRESUME_SSE2)
|
||||||
|
mark_as_advanced(OPUS_X86_PRESUME_SSE4_1)
|
||||||
add_subdirectory(opus ${TORQUE_LIB_TARG_DIRECTORY}/opus EXCLUDE_FROM_ALL)
|
add_subdirectory(opus ${TORQUE_LIB_TARG_DIRECTORY}/opus EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(libtheora ${TORQUE_LIB_TARG_DIRECTORY}/libtheora EXCLUDE_FROM_ALL)
|
add_subdirectory(libtheora ${TORQUE_LIB_TARG_DIRECTORY}/libtheora EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
|
mark_as_advanced(PULSEAUDIO_INCLUDE_DIR)
|
||||||
|
mark_as_advanced(PULSEAUDIO_LIBRARY)
|
||||||
advanced_option(BUILD_PROGRAMS "" OFF)
|
advanced_option(BUILD_PROGRAMS "" OFF)
|
||||||
advanced_option(BUILD_EXAMPLES "" OFF)
|
advanced_option(BUILD_EXAMPLES "" OFF)
|
||||||
advanced_option(ENABLE_CPACK "" OFF)
|
advanced_option(ENABLE_CPACK "" OFF)
|
||||||
|
|
@ -289,3 +308,28 @@ if(TORQUE_TESTING)
|
||||||
add_subdirectory(gtest ${TORQUE_LIB_TARG_DIRECTORY}/gtest EXCLUDE_FROM_ALL)
|
add_subdirectory(gtest ${TORQUE_LIB_TARG_DIRECTORY}/gtest EXCLUDE_FROM_ALL)
|
||||||
endif()
|
endif()
|
||||||
#endif()
|
#endif()
|
||||||
|
|
||||||
|
#misc randoms
|
||||||
|
mark_as_advanced(WINDRES)
|
||||||
|
mark_as_advanced(AUDIOUNIT_INCLUDE_DIR)
|
||||||
|
mark_as_advanced(CCACHE_BINARY)
|
||||||
|
mark_as_advanced(COREAUDIO_FRAMEWORK)
|
||||||
|
mark_as_advanced(FORCE_STATIC_VCRT)
|
||||||
|
mark_as_advanced(HAVE_GIT)
|
||||||
|
mark_as_advanced(LIBRARY_SUFFIX)
|
||||||
|
mark_as_advanced(USE_STATIC_CRT)
|
||||||
|
|
||||||
|
mark_as_advanced(BUILD_CXXLIBS)
|
||||||
|
mark_as_advanced(BUILD_DOCS)
|
||||||
|
mark_as_advanced(BUILD_REGTEST)
|
||||||
|
mark_as_advanced(BUILD_UTILS)
|
||||||
|
|
||||||
|
mark_as_advanced(ENABLE_64_BIT_WORDS)
|
||||||
|
mark_as_advanced(ENABLE_BOW_DOCS)
|
||||||
|
mark_as_advanced(ENABLE_EXTERNAL_LIBS)
|
||||||
|
|
||||||
|
mark_as_advanced(WITH_ASM)
|
||||||
|
mark_as_advanced(WITH_AVX)
|
||||||
|
mark_as_advanced(WITH_FORTIFY_SOURCE)
|
||||||
|
mark_as_advanced(WITH_OGG)
|
||||||
|
mark_as_advanced(WITH_STACK_PROTECTOR)
|
||||||
|
|
|
||||||
46
Engine/lib/openal-soft/.github/workflows/ci.yml
vendored
46
Engine/lib/openal-soft/.github/workflows/ci.yml
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
name: CI
|
name: CI
|
||||||
|
|
||||||
on: [push]
|
on: [push, pull_request]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
@ -14,6 +14,7 @@ jobs:
|
||||||
name: "Win32-Release",
|
name: "Win32-Release",
|
||||||
os: windows-latest,
|
os: windows-latest,
|
||||||
cmake_opts: "-A Win32 \
|
cmake_opts: "-A Win32 \
|
||||||
|
-DALSOFT_TESTS=ON \
|
||||||
-DALSOFT_BUILD_ROUTER=ON \
|
-DALSOFT_BUILD_ROUTER=ON \
|
||||||
-DALSOFT_REQUIRE_WINMM=ON \
|
-DALSOFT_REQUIRE_WINMM=ON \
|
||||||
-DALSOFT_REQUIRE_DSOUND=ON \
|
-DALSOFT_REQUIRE_DSOUND=ON \
|
||||||
|
|
@ -24,6 +25,7 @@ jobs:
|
||||||
name: "Win32-Debug",
|
name: "Win32-Debug",
|
||||||
os: windows-latest,
|
os: windows-latest,
|
||||||
cmake_opts: "-A Win32 \
|
cmake_opts: "-A Win32 \
|
||||||
|
-DALSOFT_TESTS=ON \
|
||||||
-DALSOFT_BUILD_ROUTER=ON \
|
-DALSOFT_BUILD_ROUTER=ON \
|
||||||
-DALSOFT_REQUIRE_WINMM=ON \
|
-DALSOFT_REQUIRE_WINMM=ON \
|
||||||
-DALSOFT_REQUIRE_DSOUND=ON \
|
-DALSOFT_REQUIRE_DSOUND=ON \
|
||||||
|
|
@ -34,6 +36,7 @@ jobs:
|
||||||
name: "Win64-Release",
|
name: "Win64-Release",
|
||||||
os: windows-latest,
|
os: windows-latest,
|
||||||
cmake_opts: "-A x64 \
|
cmake_opts: "-A x64 \
|
||||||
|
-DALSOFT_TESTS=ON \
|
||||||
-DALSOFT_BUILD_ROUTER=ON \
|
-DALSOFT_BUILD_ROUTER=ON \
|
||||||
-DALSOFT_REQUIRE_WINMM=ON \
|
-DALSOFT_REQUIRE_WINMM=ON \
|
||||||
-DALSOFT_REQUIRE_DSOUND=ON \
|
-DALSOFT_REQUIRE_DSOUND=ON \
|
||||||
|
|
@ -44,16 +47,42 @@ jobs:
|
||||||
name: "Win64-Debug",
|
name: "Win64-Debug",
|
||||||
os: windows-latest,
|
os: windows-latest,
|
||||||
cmake_opts: "-A x64 \
|
cmake_opts: "-A x64 \
|
||||||
|
-DALSOFT_TESTS=ON \
|
||||||
-DALSOFT_BUILD_ROUTER=ON \
|
-DALSOFT_BUILD_ROUTER=ON \
|
||||||
-DALSOFT_REQUIRE_WINMM=ON \
|
-DALSOFT_REQUIRE_WINMM=ON \
|
||||||
-DALSOFT_REQUIRE_DSOUND=ON \
|
-DALSOFT_REQUIRE_DSOUND=ON \
|
||||||
-DALSOFT_REQUIRE_WASAPI=ON",
|
-DALSOFT_REQUIRE_WASAPI=ON",
|
||||||
build_type: "Debug"
|
build_type: "Debug"
|
||||||
}
|
}
|
||||||
|
- {
|
||||||
|
name: "Win64-UWP",
|
||||||
|
os: windows-latest,
|
||||||
|
cmake_opts: "-A x64 \
|
||||||
|
-DALSOFT_TESTS=OFF \
|
||||||
|
-DCMAKE_SYSTEM_NAME=WindowsStore \
|
||||||
|
\"-DCMAKE_SYSTEM_VERSION=10.0\" \
|
||||||
|
-DALSOFT_BUILD_ROUTER=ON \
|
||||||
|
-DALSOFT_REQUIRE_WASAPI=ON",
|
||||||
|
build_type: "Release"
|
||||||
|
}
|
||||||
- {
|
- {
|
||||||
name: "macOS-Release",
|
name: "macOS-Release",
|
||||||
os: macos-latest,
|
os: macos-latest,
|
||||||
cmake_opts: "-DALSOFT_REQUIRE_COREAUDIO=ON",
|
cmake_opts: "-DALSOFT_REQUIRE_COREAUDIO=ON \
|
||||||
|
-DALSOFT_TESTS=ON",
|
||||||
|
build_type: "Release"
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
name: "iOS-Release",
|
||||||
|
os: macos-latest,
|
||||||
|
cmake_opts: "-GXcode \
|
||||||
|
-DCMAKE_SYSTEM_NAME=iOS \
|
||||||
|
-DALSOFT_REQUIRE_COREAUDIO=ON \
|
||||||
|
-DALSOFT_UTILS=OFF \
|
||||||
|
-DALSOFT_EXAMPLES=OFF \
|
||||||
|
-DALSOFT_TESTS=OFF \
|
||||||
|
-DALSOFT_INSTALL=OFF \
|
||||||
|
\"-DCMAKE_OSX_ARCHITECTURES=arm64\"",
|
||||||
build_type: "Release"
|
build_type: "Release"
|
||||||
}
|
}
|
||||||
- {
|
- {
|
||||||
|
|
@ -65,7 +94,8 @@ jobs:
|
||||||
-DALSOFT_REQUIRE_PORTAUDIO=ON \
|
-DALSOFT_REQUIRE_PORTAUDIO=ON \
|
||||||
-DALSOFT_REQUIRE_PULSEAUDIO=ON \
|
-DALSOFT_REQUIRE_PULSEAUDIO=ON \
|
||||||
-DALSOFT_REQUIRE_JACK=ON \
|
-DALSOFT_REQUIRE_JACK=ON \
|
||||||
-DALSOFT_REQUIRE_PIPEWIRE=ON",
|
-DALSOFT_REQUIRE_PIPEWIRE=ON \
|
||||||
|
-DALSOFT_TESTS=ON",
|
||||||
deps_cmdline: "sudo apt update && sudo apt-get install -qq \
|
deps_cmdline: "sudo apt update && sudo apt-get install -qq \
|
||||||
libpulse-dev \
|
libpulse-dev \
|
||||||
portaudio19-dev \
|
portaudio19-dev \
|
||||||
|
|
@ -78,7 +108,7 @@ jobs:
|
||||||
}
|
}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
@ -97,6 +127,12 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
cmake --build build --config ${{matrix.config.build_type}}
|
cmake --build build --config ${{matrix.config.build_type}}
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
ctest
|
||||||
|
|
||||||
- name: Create Archive
|
- name: Create Archive
|
||||||
if: ${{ matrix.config.os == 'windows-latest' }}
|
if: ${{ matrix.config.os == 'windows-latest' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
@ -109,7 +145,7 @@ jobs:
|
||||||
|
|
||||||
- name: Upload Archive
|
- name: Upload Archive
|
||||||
# Upload package as an artifact of this workflow.
|
# Upload package as an artifact of this workflow.
|
||||||
uses: actions/upload-artifact@v3.1.1
|
uses: actions/upload-artifact@v3.1.2
|
||||||
if: ${{ matrix.config.os == 'windows-latest' }}
|
if: ${{ matrix.config.os == 'windows-latest' }}
|
||||||
with:
|
with:
|
||||||
name: soft_oal-${{matrix.config.name}}
|
name: soft_oal-${{matrix.config.name}}
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ jobs:
|
||||||
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 makemhr artifact
|
||||||
uses: actions/upload-artifact@v3.1.1
|
uses: actions/upload-artifact@v3.1.2
|
||||||
with:
|
with:
|
||||||
name: makemhr
|
name: makemhr
|
||||||
path: "Artifacts/"
|
path: "Artifacts/"
|
||||||
|
|
|
||||||
1
Engine/lib/openal-soft/.gitignore
vendored
1
Engine/lib/openal-soft/.gitignore
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
build*/
|
build*/
|
||||||
winbuild
|
winbuild
|
||||||
win64build
|
win64build
|
||||||
|
.vs/
|
||||||
|
|
||||||
## kdevelop
|
## kdevelop
|
||||||
*.kdev4
|
*.kdev4
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# CMake build file list for OpenAL
|
# CMake build file list for OpenAL
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.0.2)
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
# The workaround for try_compile failing with code signing
|
# The workaround for try_compile failing with code signing
|
||||||
|
|
@ -28,11 +29,10 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
||||||
FORCE)
|
FORCE)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
elseif(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
|
||||||
|
set(ALSOFT_UWP TRUE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CMAKE_C_VISIBILITY_PRESET hidden)
|
|
||||||
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
|
||||||
|
|
||||||
if(COMMAND CMAKE_POLICY)
|
if(COMMAND CMAKE_POLICY)
|
||||||
cmake_policy(SET CMP0003 NEW)
|
cmake_policy(SET CMP0003 NEW)
|
||||||
cmake_policy(SET CMP0005 NEW)
|
cmake_policy(SET CMP0005 NEW)
|
||||||
|
|
@ -76,8 +76,8 @@ if(NOT CMAKE_DEBUG_POSTFIX)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(DEFAULT_TARGET_PROPS
|
set(DEFAULT_TARGET_PROPS
|
||||||
# Require C++14.
|
# Require C++17.
|
||||||
CXX_STANDARD 14
|
CXX_STANDARD 17
|
||||||
CXX_STANDARD_REQUIRED TRUE
|
CXX_STANDARD_REQUIRED TRUE
|
||||||
# Prefer C11, but support C99 and earlier when possible.
|
# Prefer C11, but support C99 and earlier when possible.
|
||||||
C_STANDARD 11)
|
C_STANDARD 11)
|
||||||
|
|
@ -109,6 +109,7 @@ option(ALSOFT_UTILS "Build utility programs" ON)
|
||||||
option(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" OFF)
|
option(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" OFF)
|
||||||
|
|
||||||
option(ALSOFT_EXAMPLES "Build example programs" ON)
|
option(ALSOFT_EXAMPLES "Build example programs" ON)
|
||||||
|
option(ALSOFT_TESTS "Build test programs" OFF)
|
||||||
|
|
||||||
option(ALSOFT_INSTALL "Install main library" ON)
|
option(ALSOFT_INSTALL "Install main library" ON)
|
||||||
option(ALSOFT_INSTALL_CONFIG "Install alsoft.conf sample configuration file" ON)
|
option(ALSOFT_INSTALL_CONFIG "Install alsoft.conf sample configuration file" ON)
|
||||||
|
|
@ -148,6 +149,8 @@ set(CPP_DEFS ) # C pre-processor, not C++
|
||||||
set(INC_PATHS )
|
set(INC_PATHS )
|
||||||
set(C_FLAGS )
|
set(C_FLAGS )
|
||||||
set(LINKER_FLAGS )
|
set(LINKER_FLAGS )
|
||||||
|
set(LINKER_FLAGS_DEBUG )
|
||||||
|
set(LINKER_FLAGS_RELEASE )
|
||||||
set(EXTRA_LIBS )
|
set(EXTRA_LIBS )
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
|
|
@ -165,7 +168,7 @@ elseif(APPLE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
# QNX's gcc do not uses /usr/include and /usr/lib pathes by default
|
# QNX's gcc do not uses /usr/include and /usr/lib paths by default
|
||||||
if("${CMAKE_C_PLATFORM_ID}" STREQUAL "QNX")
|
if("${CMAKE_C_PLATFORM_ID}" STREQUAL "QNX")
|
||||||
set(INC_PATHS ${INC_PATHS} /usr/include)
|
set(INC_PATHS ${INC_PATHS} /usr/include)
|
||||||
set(LINKER_FLAGS ${LINKER_FLAGS} -L/usr/lib)
|
set(LINKER_FLAGS ${LINKER_FLAGS} -L/usr/lib)
|
||||||
|
|
@ -186,29 +189,18 @@ set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0)
|
||||||
set(EXPORT_DECL "")
|
set(EXPORT_DECL "")
|
||||||
|
|
||||||
|
|
||||||
if(NOT WIN32)
|
# Some systems erroneously require the __STDC_FORMAT_MACROS macro to be defined
|
||||||
# Check if _POSIX_C_SOURCE and _XOPEN_SOURCE needs to be set for POSIX functions
|
# to get the fixed-width integer type formatter macros.
|
||||||
check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_DEFAULT)
|
check_cxx_source_compiles("#include <cinttypes>
|
||||||
if(NOT HAVE_POSIX_MEMALIGN_DEFAULT)
|
#include <cstdio>
|
||||||
set(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
int main()
|
||||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600")
|
{
|
||||||
check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_POSIX)
|
int64_t i64{};
|
||||||
if(NOT HAVE_POSIX_MEMALIGN_POSIX)
|
std::printf(\"%\" PRId64, i64);
|
||||||
set(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS})
|
}"
|
||||||
else()
|
HAVE_STDC_FORMAT_MACROS)
|
||||||
set(CPP_DEFS ${CPP_DEFS} _POSIX_C_SOURCE=200112L _XOPEN_SOURCE=600)
|
if(NOT HAVE_STDC_FORMAT_MACROS)
|
||||||
endif()
|
set(CPP_DEFS ${CPP_DEFS} __STDC_FORMAT_MACROS)
|
||||||
endif()
|
|
||||||
unset(OLD_REQUIRED_FLAGS)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# C99 has restrict, but C++ does not, so we can only utilize __restrict.
|
|
||||||
check_cxx_source_compiles("int *__restrict foo;
|
|
||||||
int main() { return 0; }" HAVE___RESTRICT)
|
|
||||||
if(HAVE___RESTRICT)
|
|
||||||
set(CPP_DEFS ${CPP_DEFS} RESTRICT=__restrict)
|
|
||||||
else()
|
|
||||||
set(CPP_DEFS ${CPP_DEFS} "RESTRICT=")
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Some systems may need libatomic for atomic functions to work
|
# Some systems may need libatomic for atomic functions to work
|
||||||
|
|
@ -278,6 +270,11 @@ else()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
check_cxx_compiler_flag(-Wno-interference-size HAVE_WNO_INTERFERENCE_SIZE)
|
||||||
|
if(HAVE_WNO_INTERFERENCE_SIZE)
|
||||||
|
set(C_FLAGS ${C_FLAGS} $<$<COMPILE_LANGUAGE:CXX>:-Wno-interference-size>)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(ALSOFT_WERROR)
|
if(ALSOFT_WERROR)
|
||||||
set(C_FLAGS ${C_FLAGS} -Werror)
|
set(C_FLAGS ${C_FLAGS} -Werror)
|
||||||
endif()
|
endif()
|
||||||
|
|
@ -312,7 +309,7 @@ else()
|
||||||
option(ALSOFT_STATIC_STDCXX "Static link libstdc++" OFF)
|
option(ALSOFT_STATIC_STDCXX "Static link libstdc++" OFF)
|
||||||
if(ALSOFT_STATIC_STDCXX)
|
if(ALSOFT_STATIC_STDCXX)
|
||||||
set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
|
set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
|
||||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lstdc++,--pop-state")
|
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-static-libstdc++")
|
||||||
check_cxx_source_compiles("int main() { }" HAVE_STATIC_LIBSTDCXX_SWITCH)
|
check_cxx_source_compiles("int main() { }" HAVE_STATIC_LIBSTDCXX_SWITCH)
|
||||||
set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES})
|
set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES})
|
||||||
unset(OLD_REQUIRED_LIBRARIES)
|
unset(OLD_REQUIRED_LIBRARIES)
|
||||||
|
|
@ -320,14 +317,14 @@ else()
|
||||||
if(NOT HAVE_STATIC_LIBSTDCXX_SWITCH)
|
if(NOT HAVE_STATIC_LIBSTDCXX_SWITCH)
|
||||||
message(FATAL_ERROR "Cannot static link libstdc++")
|
message(FATAL_ERROR "Cannot static link libstdc++")
|
||||||
endif()
|
endif()
|
||||||
set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lstdc++,--pop-state")
|
set(LINKER_FLAGS ${LINKER_FLAGS} "-static-libstdc++")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
option(ALSOFT_STATIC_WINPTHREAD "Static link libwinpthread" OFF)
|
option(ALSOFT_STATIC_WINPTHREAD "Static link libwinpthread" OFF)
|
||||||
if(ALSOFT_STATIC_WINPTHREAD)
|
if(ALSOFT_STATIC_WINPTHREAD)
|
||||||
set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
|
set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
|
||||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lwinpthread,--pop-state")
|
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lstdc++,-lwinpthread,--pop-state")
|
||||||
check_cxx_source_compiles("int main() { }" HAVE_STATIC_LIBWINPTHREAD_SWITCH)
|
check_cxx_source_compiles("int main() { }" HAVE_STATIC_LIBWINPTHREAD_SWITCH)
|
||||||
set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES})
|
set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES})
|
||||||
unset(OLD_REQUIRED_LIBRARIES)
|
unset(OLD_REQUIRED_LIBRARIES)
|
||||||
|
|
@ -335,38 +332,38 @@ else()
|
||||||
if(NOT HAVE_STATIC_LIBWINPTHREAD_SWITCH)
|
if(NOT HAVE_STATIC_LIBWINPTHREAD_SWITCH)
|
||||||
message(FATAL_ERROR "Cannot static link libwinpthread")
|
message(FATAL_ERROR "Cannot static link libwinpthread")
|
||||||
endif()
|
endif()
|
||||||
set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lwinpthread,--pop-state")
|
set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lstdc++,-lwinpthread,--pop-state")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Set visibility/export options if available
|
# Set visibility/export options if available
|
||||||
if(WIN32)
|
if(NOT LIBTYPE STREQUAL "STATIC")
|
||||||
if(NOT LIBTYPE STREQUAL "STATIC")
|
if(WIN32)
|
||||||
set(EXPORT_DECL "__declspec(dllexport)")
|
set(EXPORT_DECL "__declspec(dllexport)")
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
set(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
|
|
||||||
# Yes GCC, really don't accept visibility modes you don't support
|
|
||||||
set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Wattributes -Werror")
|
|
||||||
|
|
||||||
check_c_source_compiles("int foo() __attribute__((visibility(\"protected\")));
|
|
||||||
int main() {return 0;}" HAVE_GCC_PROTECTED_VISIBILITY)
|
|
||||||
if(HAVE_GCC_PROTECTED_VISIBILITY)
|
|
||||||
if(NOT LIBTYPE STREQUAL "STATIC")
|
|
||||||
set(EXPORT_DECL "__attribute__((visibility(\"protected\")))")
|
|
||||||
endif()
|
|
||||||
else()
|
else()
|
||||||
check_c_source_compiles("int foo() __attribute__((visibility(\"default\")));
|
set(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
|
||||||
int main() {return 0;}" HAVE_GCC_DEFAULT_VISIBILITY)
|
# Yes GCC, really don't accept visibility modes you don't support
|
||||||
if(HAVE_GCC_DEFAULT_VISIBILITY)
|
set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Wattributes -Werror")
|
||||||
if(NOT LIBTYPE STREQUAL "STATIC")
|
|
||||||
|
check_c_source_compiles("int foo() __attribute__((visibility(\"protected\")));
|
||||||
|
int main() {return 0;}" HAVE_GCC_PROTECTED_VISIBILITY)
|
||||||
|
if(HAVE_GCC_PROTECTED_VISIBILITY)
|
||||||
|
set(EXPORT_DECL "__attribute__((visibility(\"protected\")))")
|
||||||
|
else()
|
||||||
|
check_c_source_compiles("int foo() __attribute__((visibility(\"default\")));
|
||||||
|
int main() {return 0;}" HAVE_GCC_DEFAULT_VISIBILITY)
|
||||||
|
if(HAVE_GCC_DEFAULT_VISIBILITY)
|
||||||
set(EXPORT_DECL "__attribute__((visibility(\"default\")))")
|
set(EXPORT_DECL "__attribute__((visibility(\"default\")))")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
if(HAVE_GCC_PROTECTED_VISIBILITY OR HAVE_GCC_DEFAULT_VISIBILITY)
|
||||||
|
set(CMAKE_C_VISIBILITY_PRESET hidden)
|
||||||
|
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}")
|
set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -403,7 +400,7 @@ if(ALSOFT_CPUEXT_SSE AND HAVE_XMMINTRIN_H)
|
||||||
set(HAVE_SSE 1)
|
set(HAVE_SSE 1)
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE)
|
if(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE)
|
||||||
message(FATAL_ERROR "Failed to enabled required SSE CPU extensions")
|
message(FATAL_ERROR "Failed to enable required SSE CPU extensions")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON)
|
option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON)
|
||||||
|
|
@ -448,10 +445,11 @@ if(ALSOFT_CPUEXT_NEON AND HAVE_ARM_NEON_H)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_REQUIRE_NEON AND NOT HAVE_NEON)
|
if(ALSOFT_REQUIRE_NEON AND NOT HAVE_NEON)
|
||||||
message(FATAL_ERROR "Failed to enabled required ARM NEON CPU extensions")
|
message(FATAL_ERROR "Failed to enable required ARM NEON CPU extensions")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
set(ALSOFT_FORCE_ALIGN )
|
||||||
set(SSE_FLAGS )
|
set(SSE_FLAGS )
|
||||||
set(FPMATH_SET "0")
|
set(FPMATH_SET "0")
|
||||||
if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2)
|
if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2)
|
||||||
|
|
@ -476,6 +474,7 @@ if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2)
|
||||||
# OSs don't guarantee this on 32-bit, so externally-callable
|
# OSs don't guarantee this on 32-bit, so externally-callable
|
||||||
# functions need to ensure an aligned stack.
|
# functions need to ensure an aligned stack.
|
||||||
set(EXPORT_DECL "${EXPORT_DECL}__attribute__((force_align_arg_pointer))")
|
set(EXPORT_DECL "${EXPORT_DECL}__attribute__((force_align_arg_pointer))")
|
||||||
|
set(ALSOFT_FORCE_ALIGN "__attribute__((force_align_arg_pointer))")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
@ -493,13 +492,9 @@ if(HAVE_SSE2)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
check_include_file(malloc.h HAVE_MALLOC_H)
|
|
||||||
check_include_file(cpuid.h HAVE_CPUID_H)
|
check_include_file(cpuid.h HAVE_CPUID_H)
|
||||||
check_include_file(intrin.h HAVE_INTRIN_H)
|
check_include_file(intrin.h HAVE_INTRIN_H)
|
||||||
check_include_file(guiddef.h HAVE_GUIDDEF_H)
|
check_include_file(guiddef.h HAVE_GUIDDEF_H)
|
||||||
if(NOT HAVE_GUIDDEF_H)
|
|
||||||
check_include_file(initguid.h HAVE_INITGUID_H)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Some systems need libm for some math functions to work
|
# Some systems need libm for some math functions to work
|
||||||
set(MATH_LIB )
|
set(MATH_LIB )
|
||||||
|
|
@ -517,7 +512,7 @@ if(HAVE_LIBRT)
|
||||||
set(RT_LIB rt)
|
set(RT_LIB rt)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Check for the dlopen API (for dynamicly loading backend libs)
|
# Check for the dlopen API (for dynamically loading backend libs)
|
||||||
if(ALSOFT_DLOPEN)
|
if(ALSOFT_DLOPEN)
|
||||||
check_include_file(dlfcn.h HAVE_DLFCN_H)
|
check_include_file(dlfcn.h HAVE_DLFCN_H)
|
||||||
check_library_exists(dl dlopen "" HAVE_LIBDL)
|
check_library_exists(dl dlopen "" HAVE_LIBDL)
|
||||||
|
|
@ -546,8 +541,6 @@ if(HAVE_INTRIN_H)
|
||||||
}" HAVE_CPUID_INTRINSIC)
|
}" HAVE_CPUID_INTRINSIC)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN)
|
|
||||||
check_symbol_exists(_aligned_malloc malloc.h HAVE__ALIGNED_MALLOC)
|
|
||||||
check_symbol_exists(proc_pidpath libproc.h HAVE_PROC_PIDPATH)
|
check_symbol_exists(proc_pidpath libproc.h HAVE_PROC_PIDPATH)
|
||||||
|
|
||||||
if(NOT WIN32)
|
if(NOT WIN32)
|
||||||
|
|
@ -582,34 +575,36 @@ if(NOT WIN32)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
check_symbol_exists(getopt unistd.h HAVE_GETOPT)
|
|
||||||
|
|
||||||
|
|
||||||
# Common sources used by both the OpenAL implementation library, the OpenAL
|
# Common sources used by both the OpenAL implementation library, the OpenAL
|
||||||
# router, and certain tools and examples.
|
# router, and certain tools and examples.
|
||||||
set(COMMON_OBJS
|
set(COMMON_OBJS
|
||||||
|
common/alassert.cpp
|
||||||
|
common/alassert.h
|
||||||
common/albit.h
|
common/albit.h
|
||||||
common/albyte.h
|
|
||||||
common/alcomplex.cpp
|
common/alcomplex.cpp
|
||||||
common/alcomplex.h
|
common/alcomplex.h
|
||||||
common/aldeque.h
|
|
||||||
common/alfstream.cpp
|
|
||||||
common/alfstream.h
|
|
||||||
common/almalloc.cpp
|
|
||||||
common/almalloc.h
|
common/almalloc.h
|
||||||
common/alnumbers.h
|
common/alnumbers.h
|
||||||
common/alnumeric.h
|
common/alnumeric.h
|
||||||
common/aloptional.h
|
common/alsem.cpp
|
||||||
|
common/alsem.h
|
||||||
common/alspan.h
|
common/alspan.h
|
||||||
common/alstring.cpp
|
common/alstring.cpp
|
||||||
common/alstring.h
|
common/alstring.h
|
||||||
|
common/althrd_setname.cpp
|
||||||
|
common/althrd_setname.h
|
||||||
|
common/althreads.h
|
||||||
common/altraits.h
|
common/altraits.h
|
||||||
common/atomic.h
|
common/atomic.h
|
||||||
common/comptr.h
|
common/comptr.h
|
||||||
common/dynload.cpp
|
common/dynload.cpp
|
||||||
common/dynload.h
|
common/dynload.h
|
||||||
|
common/flexarray.h
|
||||||
common/intrusive_ptr.h
|
common/intrusive_ptr.h
|
||||||
common/opthelpers.h
|
common/opthelpers.h
|
||||||
|
common/pffft.cpp
|
||||||
|
common/pffft.h
|
||||||
common/phase_shifter.h
|
common/phase_shifter.h
|
||||||
common/polyphase_resampler.cpp
|
common/polyphase_resampler.cpp
|
||||||
common/polyphase_resampler.h
|
common/polyphase_resampler.h
|
||||||
|
|
@ -618,8 +613,6 @@ set(COMMON_OBJS
|
||||||
common/ringbuffer.h
|
common/ringbuffer.h
|
||||||
common/strutils.cpp
|
common/strutils.cpp
|
||||||
common/strutils.h
|
common/strutils.h
|
||||||
common/threads.cpp
|
|
||||||
common/threads.h
|
|
||||||
common/vecmat.h
|
common/vecmat.h
|
||||||
common/vector.h)
|
common/vector.h)
|
||||||
|
|
||||||
|
|
@ -680,6 +673,8 @@ set(CORE_OBJS
|
||||||
core/mixer.cpp
|
core/mixer.cpp
|
||||||
core/mixer.h
|
core/mixer.h
|
||||||
core/resampler_limits.h
|
core/resampler_limits.h
|
||||||
|
core/storage_formats.cpp
|
||||||
|
core/storage_formats.h
|
||||||
core/uhjfilter.cpp
|
core/uhjfilter.cpp
|
||||||
core/uhjfilter.h
|
core/uhjfilter.h
|
||||||
core/uiddefs.cpp
|
core/uiddefs.cpp
|
||||||
|
|
@ -726,7 +721,7 @@ if(NOT WIN32)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_REQUIRE_RTKIT AND NOT HAVE_RTKIT)
|
if(ALSOFT_REQUIRE_RTKIT AND NOT HAVE_RTKIT)
|
||||||
message(FATAL_ERROR "Failed to enabled required RTKit support")
|
message(FATAL_ERROR "Failed to enable required RTKit support")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Default mixers, always available
|
# Default mixers, always available
|
||||||
|
|
@ -742,6 +737,9 @@ set(OPENAL_OBJS
|
||||||
al/auxeffectslot.h
|
al/auxeffectslot.h
|
||||||
al/buffer.cpp
|
al/buffer.cpp
|
||||||
al/buffer.h
|
al/buffer.h
|
||||||
|
al/debug.cpp
|
||||||
|
al/debug.h
|
||||||
|
al/direct_defs.h
|
||||||
al/effect.cpp
|
al/effect.cpp
|
||||||
al/effect.h
|
al/effect.h
|
||||||
al/effects/autowah.cpp
|
al/effects/autowah.cpp
|
||||||
|
|
@ -761,6 +759,7 @@ 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
|
||||||
|
|
@ -798,6 +797,9 @@ set(ALC_OBJS
|
||||||
alc/effects/pshifter.cpp
|
alc/effects/pshifter.cpp
|
||||||
alc/effects/reverb.cpp
|
alc/effects/reverb.cpp
|
||||||
alc/effects/vmorpher.cpp
|
alc/effects/vmorpher.cpp
|
||||||
|
alc/events.cpp
|
||||||
|
alc/events.h
|
||||||
|
alc/export_list.h
|
||||||
alc/inprogext.h
|
alc/inprogext.h
|
||||||
alc/panning.cpp)
|
alc/panning.cpp)
|
||||||
|
|
||||||
|
|
@ -815,7 +817,6 @@ if(ALSOFT_EAX)
|
||||||
al/eax/fx_slot_index.h
|
al/eax/fx_slot_index.h
|
||||||
al/eax/fx_slots.cpp
|
al/eax/fx_slots.cpp
|
||||||
al/eax/fx_slots.h
|
al/eax/fx_slots.h
|
||||||
al/eax/globals.cpp
|
|
||||||
al/eax/globals.h
|
al/eax/globals.h
|
||||||
al/eax/utils.cpp
|
al/eax/utils.cpp
|
||||||
al/eax/utils.h
|
al/eax/utils.h
|
||||||
|
|
@ -899,7 +900,7 @@ if(ALSOFT_BACKEND_PIPEWIRE AND PkgConfig_FOUND)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_REQUIRE_PIPEWIRE AND NOT HAVE_PIPEWIRE)
|
if(ALSOFT_REQUIRE_PIPEWIRE AND NOT HAVE_PIPEWIRE)
|
||||||
message(FATAL_ERROR "Failed to enabled required PipeWire backend")
|
message(FATAL_ERROR "Failed to enable required PipeWire backend")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Check PulseAudio backend
|
# Check PulseAudio backend
|
||||||
|
|
@ -916,7 +917,7 @@ if(ALSOFT_BACKEND_PULSEAUDIO)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO)
|
if(ALSOFT_REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO)
|
||||||
message(FATAL_ERROR "Failed to enabled required PulseAudio backend")
|
message(FATAL_ERROR "Failed to enable required PulseAudio backend")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT WIN32)
|
if(NOT WIN32)
|
||||||
|
|
@ -963,66 +964,72 @@ if(NOT WIN32)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Check SndIO backend
|
# Check SndIO backend (disabled by default on non-BSDs)
|
||||||
option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" ON)
|
if(BSD)
|
||||||
|
option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" ON)
|
||||||
|
else()
|
||||||
|
option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" OFF)
|
||||||
|
endif()
|
||||||
option(ALSOFT_REQUIRE_SNDIO "Require SndIO backend" OFF)
|
option(ALSOFT_REQUIRE_SNDIO "Require SndIO backend" OFF)
|
||||||
if(ALSOFT_BACKEND_SNDIO)
|
if(ALSOFT_BACKEND_SNDIO)
|
||||||
find_package(SoundIO)
|
find_package(SndIO)
|
||||||
if(SOUNDIO_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.h)
|
||||||
set(EXTRA_LIBS ${SOUNDIO_LIBRARIES} ${EXTRA_LIBS})
|
set(EXTRA_LIBS ${SNDIO_LIBRARIES} ${EXTRA_LIBS})
|
||||||
set(INC_PATHS ${INC_PATHS} ${SOUNDIO_INCLUDE_DIRS})
|
set(INC_PATHS ${INC_PATHS} ${SNDIO_INCLUDE_DIRS})
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_REQUIRE_ALSA AND NOT HAVE_ALSA)
|
if(ALSOFT_REQUIRE_ALSA AND NOT HAVE_ALSA)
|
||||||
message(FATAL_ERROR "Failed to enabled required ALSA backend")
|
message(FATAL_ERROR "Failed to enable required ALSA backend")
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS)
|
if(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS)
|
||||||
message(FATAL_ERROR "Failed to enabled required OSS backend")
|
message(FATAL_ERROR "Failed to enable required OSS backend")
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_REQUIRE_SOLARIS AND NOT HAVE_SOLARIS)
|
if(ALSOFT_REQUIRE_SOLARIS AND NOT HAVE_SOLARIS)
|
||||||
message(FATAL_ERROR "Failed to enabled required Solaris backend")
|
message(FATAL_ERROR "Failed to enable required Solaris backend")
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_REQUIRE_SNDIO AND NOT HAVE_SNDIO)
|
if(ALSOFT_REQUIRE_SNDIO AND NOT HAVE_SNDIO)
|
||||||
message(FATAL_ERROR "Failed to enabled required SndIO backend")
|
message(FATAL_ERROR "Failed to enable required SndIO backend")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Check Windows-only backends
|
# Check Windows-only backends
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
# Check MMSystem backend
|
if (NOT ALSOFT_UWP)
|
||||||
option(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON)
|
# Check MMSystem backend
|
||||||
option(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF)
|
option(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON)
|
||||||
if(ALSOFT_BACKEND_WINMM)
|
option(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF)
|
||||||
set(HAVE_WINMM 1)
|
if(ALSOFT_BACKEND_WINMM)
|
||||||
set(BACKENDS "${BACKENDS} WinMM,")
|
set(HAVE_WINMM 1)
|
||||||
set(ALC_OBJS ${ALC_OBJS} alc/backends/winmm.cpp alc/backends/winmm.h)
|
set(BACKENDS "${BACKENDS} WinMM,")
|
||||||
# There doesn't seem to be good way to search for winmm.lib for MSVC.
|
set(ALC_OBJS ${ALC_OBJS} alc/backends/winmm.cpp alc/backends/winmm.h)
|
||||||
# find_library doesn't find it without being told to look in a specific
|
# There doesn't seem to be good way to search for winmm.lib for MSVC.
|
||||||
# place in the WindowsSDK, but it links anyway. If there ends up being
|
# find_library doesn't find it without being told to look in a specific
|
||||||
# Windows targets without this, another means to detect it is needed.
|
# place in the WindowsSDK, but it links anyway. If there ends up being
|
||||||
set(EXTRA_LIBS winmm ${EXTRA_LIBS})
|
# Windows targets without this, another means to detect it is needed.
|
||||||
endif()
|
set(EXTRA_LIBS winmm ${EXTRA_LIBS})
|
||||||
|
|
||||||
# Check DSound backend
|
|
||||||
option(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON)
|
|
||||||
option(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF)
|
|
||||||
if(ALSOFT_BACKEND_DSOUND)
|
|
||||||
check_include_file(dsound.h HAVE_DSOUND_H)
|
|
||||||
if(DXSDK_DIR)
|
|
||||||
find_path(DSOUND_INCLUDE_DIR NAMES "dsound.h"
|
|
||||||
PATHS "${DXSDK_DIR}" PATH_SUFFIXES include
|
|
||||||
DOC "The DirectSound include directory")
|
|
||||||
endif()
|
endif()
|
||||||
if(HAVE_DSOUND_H OR DSOUND_INCLUDE_DIR)
|
|
||||||
set(HAVE_DSOUND 1)
|
|
||||||
set(BACKENDS "${BACKENDS} DirectSound,")
|
|
||||||
set(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h)
|
|
||||||
|
|
||||||
if(NOT HAVE_DSOUND_H)
|
# Check DSound backend
|
||||||
set(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIR})
|
option(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON)
|
||||||
|
option(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF)
|
||||||
|
if(ALSOFT_BACKEND_DSOUND)
|
||||||
|
check_include_file(dsound.h HAVE_DSOUND_H)
|
||||||
|
if(DXSDK_DIR)
|
||||||
|
find_path(DSOUND_INCLUDE_DIR NAMES "dsound.h"
|
||||||
|
PATHS "${DXSDK_DIR}" PATH_SUFFIXES include
|
||||||
|
DOC "The DirectSound include directory")
|
||||||
|
endif()
|
||||||
|
if(HAVE_DSOUND_H OR DSOUND_INCLUDE_DIR)
|
||||||
|
set(HAVE_DSOUND 1)
|
||||||
|
set(BACKENDS "${BACKENDS} DirectSound,")
|
||||||
|
set(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h)
|
||||||
|
|
||||||
|
if(NOT HAVE_DSOUND_H)
|
||||||
|
set(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIR})
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
@ -1040,13 +1047,13 @@ if(WIN32)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_REQUIRE_WINMM AND NOT HAVE_WINMM)
|
if(ALSOFT_REQUIRE_WINMM AND NOT HAVE_WINMM)
|
||||||
message(FATAL_ERROR "Failed to enabled required WinMM backend")
|
message(FATAL_ERROR "Failed to enable required WinMM backend")
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_REQUIRE_DSOUND AND NOT HAVE_DSOUND)
|
if(ALSOFT_REQUIRE_DSOUND AND NOT HAVE_DSOUND)
|
||||||
message(FATAL_ERROR "Failed to enabled required DSound backend")
|
message(FATAL_ERROR "Failed to enable required DSound backend")
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_REQUIRE_WASAPI AND NOT HAVE_WASAPI)
|
if(ALSOFT_REQUIRE_WASAPI AND NOT HAVE_WASAPI)
|
||||||
message(FATAL_ERROR "Failed to enabled required WASAPI backend")
|
message(FATAL_ERROR "Failed to enable required WASAPI backend")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Check JACK backend
|
# Check JACK backend
|
||||||
|
|
@ -1063,7 +1070,7 @@ if(ALSOFT_BACKEND_JACK)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_REQUIRE_JACK AND NOT HAVE_JACK)
|
if(ALSOFT_REQUIRE_JACK AND NOT HAVE_JACK)
|
||||||
message(FATAL_ERROR "Failed to enabled required JACK backend")
|
message(FATAL_ERROR "Failed to enable required JACK backend")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Check CoreAudio backend
|
# Check CoreAudio backend
|
||||||
|
|
@ -1098,7 +1105,7 @@ if(ALSOFT_BACKEND_COREAUDIO)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_REQUIRE_COREAUDIO AND NOT HAVE_COREAUDIO)
|
if(ALSOFT_REQUIRE_COREAUDIO AND NOT HAVE_COREAUDIO)
|
||||||
message(FATAL_ERROR "Failed to enabled required CoreAudio backend")
|
message(FATAL_ERROR "Failed to enable required CoreAudio backend")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Check for Oboe (Android) backend
|
# Check for Oboe (Android) backend
|
||||||
|
|
@ -1130,7 +1137,7 @@ if(ALSOFT_BACKEND_OBOE)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_REQUIRE_OBOE AND NOT HAVE_OBOE)
|
if(ALSOFT_REQUIRE_OBOE AND NOT HAVE_OBOE)
|
||||||
message(FATAL_ERROR "Failed to enabled required Oboe backend")
|
message(FATAL_ERROR "Failed to enable required Oboe backend")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Check for OpenSL (Android) backend
|
# Check for OpenSL (Android) backend
|
||||||
|
|
@ -1142,11 +1149,12 @@ if(ALSOFT_BACKEND_OPENSL)
|
||||||
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,")
|
||||||
set(EXTRA_LIBS "OpenSL::OpenSLES" ${EXTRA_LIBS})
|
set(EXTRA_LIBS ${OPENSL_LIBRARIES} ${EXTRA_LIBS})
|
||||||
|
set(INC_PATHS ${INC_PATHS} ${OPENSL_INCLUDE_DIRS})
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL)
|
if(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL)
|
||||||
message(FATAL_ERROR "Failed to enabled required OpenSL backend")
|
message(FATAL_ERROR "Failed to enable required OpenSL backend")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Check PortAudio backend
|
# Check PortAudio backend
|
||||||
|
|
@ -1163,7 +1171,7 @@ if(ALSOFT_BACKEND_PORTAUDIO)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO)
|
if(ALSOFT_REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO)
|
||||||
message(FATAL_ERROR "Failed to enabled required PortAudio backend")
|
message(FATAL_ERROR "Failed to enable required PortAudio backend")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Check for SDL2 backend
|
# Check for SDL2 backend
|
||||||
|
|
@ -1181,7 +1189,7 @@ if(ALSOFT_BACKEND_SDL2)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_REQUIRE_SDL2 AND NOT SDL2_FOUND)
|
if(ALSOFT_REQUIRE_SDL2 AND NOT SDL2_FOUND)
|
||||||
message(FATAL_ERROR "Failed to enabled required SDL2 backend")
|
message(FATAL_ERROR "Failed to enable required SDL2 backend")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Optionally enable the Wave Writer backend
|
# Optionally enable the Wave Writer backend
|
||||||
|
|
@ -1309,11 +1317,12 @@ configure_file(
|
||||||
@ONLY)
|
@ONLY)
|
||||||
|
|
||||||
|
|
||||||
add_library(common STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS})
|
add_library(alcommon STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS})
|
||||||
target_include_directories(common PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/include)
|
target_include_directories(alcommon PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/include
|
||||||
target_compile_definitions(common PRIVATE ${CPP_DEFS})
|
PUBLIC ${OpenAL_SOURCE_DIR}/common)
|
||||||
target_compile_options(common PRIVATE ${C_FLAGS})
|
target_compile_definitions(alcommon PRIVATE ${CPP_DEFS})
|
||||||
set_target_properties(common PROPERTIES ${DEFAULT_TARGET_PROPS} POSITION_INDEPENDENT_CODE TRUE)
|
target_compile_options(alcommon PRIVATE ${C_FLAGS})
|
||||||
|
set_target_properties(alcommon PROPERTIES ${DEFAULT_TARGET_PROPS} POSITION_INDEPENDENT_CODE TRUE)
|
||||||
|
|
||||||
|
|
||||||
unset(HAS_ROUTER)
|
unset(HAS_ROUTER)
|
||||||
|
|
@ -1348,7 +1357,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 common ${LINKER_FLAGS})
|
target_link_libraries(OpenAL PRIVATE alcommon ${LINKER_FLAGS})
|
||||||
target_include_directories(OpenAL
|
target_include_directories(OpenAL
|
||||||
PUBLIC
|
PUBLIC
|
||||||
$<BUILD_INTERFACE:${OpenAL_SOURCE_DIR}/include>
|
$<BUILD_INTERFACE:${OpenAL_SOURCE_DIR}/include>
|
||||||
|
|
@ -1379,13 +1388,31 @@ 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 common ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB})
|
target_link_libraries(${IMPL_TARGET} PRIVATE alcommon ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB})
|
||||||
|
|
||||||
|
if(ALSOFT_UWP)
|
||||||
|
set(ALSOFT_CPPWINRT_VERSION "2.0.230706.1" CACHE STRING "The soft-oal default cppwinrt version")
|
||||||
|
|
||||||
|
find_program(NUGET_EXE NAMES nuget)
|
||||||
|
if(NOT NUGET_EXE)
|
||||||
|
message("NUGET.EXE not found.")
|
||||||
|
message(FATAL_ERROR "Please install this executable, and run CMake again.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
exec_program(${NUGET_EXE}
|
||||||
|
ARGS install "Microsoft.Windows.CppWinRT" -Version ${ALSOFT_CPPWINRT_VERSION} -ExcludeVersion -OutputDirectory "\"${CMAKE_BINARY_DIR}/packages\"")
|
||||||
|
|
||||||
|
set_target_properties(${IMPL_TARGET} PROPERTIES
|
||||||
|
VS_PROJECT_IMPORT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.props
|
||||||
|
)
|
||||||
|
target_link_libraries(${IMPL_TARGET} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.targets)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NOT WIN32 AND NOT APPLE)
|
if(NOT WIN32 AND NOT APPLE)
|
||||||
# FIXME: This doesn't put a dependency on the version script. Changing
|
# FIXME: This doesn't put a dependency on the version script. Changing
|
||||||
# the version script will not cause a relink as it should.
|
# the version script will not cause a relink as it should.
|
||||||
set_property(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY
|
target_link_options(${IMPL_TARGET} PRIVATE
|
||||||
LINK_FLAGS " -Wl,--version-script=${OpenAL_SOURCE_DIR}/libopenal.version")
|
"-Wl,--version-script=${OpenAL_SOURCE_DIR}/libopenal.version")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE AND ALSOFT_OSX_FRAMEWORK)
|
if(APPLE AND ALSOFT_OSX_FRAMEWORK)
|
||||||
|
|
@ -1443,8 +1470,8 @@ set_target_properties(${IMPL_TARGET} PROPERTIES ${DEFAULT_TARGET_PROPS}
|
||||||
SOVERSION ${LIB_MAJOR_VERSION}
|
SOVERSION ${LIB_MAJOR_VERSION}
|
||||||
)
|
)
|
||||||
target_compile_definitions(${IMPL_TARGET}
|
target_compile_definitions(${IMPL_TARGET}
|
||||||
PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}"
|
PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES $<$<BOOL:${ALSOFT_EAX}>:ALSOFT_EAX>
|
||||||
${CPP_DEFS})
|
"ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}" ${CPP_DEFS})
|
||||||
target_compile_options(${IMPL_TARGET} PRIVATE ${C_FLAGS})
|
target_compile_options(${IMPL_TARGET} PRIVATE ${C_FLAGS})
|
||||||
|
|
||||||
if(TARGET build_version)
|
if(TARGET build_version)
|
||||||
|
|
@ -1462,8 +1489,7 @@ if(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC"
|
||||||
message(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation")
|
message(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation")
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
set_property(TARGET OpenAL APPEND_STRING PROPERTY
|
target_link_options(OpenAL PRIVATE "-Wl,--output-def,${PROJECT_BINARY_DIR}/OpenAL32.def")
|
||||||
LINK_FLAGS " -Wl,--output-def,OpenAL32.def")
|
|
||||||
add_custom_command(TARGET OpenAL POST_BUILD
|
add_custom_command(TARGET OpenAL POST_BUILD
|
||||||
COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def
|
COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def
|
||||||
COMMAND "${CMAKE_DLLTOOL}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll
|
COMMAND "${CMAKE_DLLTOOL}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll
|
||||||
|
|
@ -1494,7 +1520,10 @@ if(FPMATH_SET)
|
||||||
message(STATUS "Building with SSE${FPMATH_SET} codegen")
|
message(STATUS "Building with SSE${FPMATH_SET} codegen")
|
||||||
message(STATUS "")
|
message(STATUS "")
|
||||||
endif()
|
endif()
|
||||||
|
if(ALSOFT_UWP)
|
||||||
|
message(STATUS "Building with UWP support")
|
||||||
|
message(STATUS "")
|
||||||
|
endif()
|
||||||
if(ALSOFT_EAX)
|
if(ALSOFT_EAX)
|
||||||
message(STATUS "Building with legacy EAX extension support")
|
message(STATUS "Building with legacy EAX extension support")
|
||||||
message(STATUS "")
|
message(STATUS "")
|
||||||
|
|
@ -1580,7 +1609,7 @@ 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 common
|
target_link_libraries(uhjdecoder PUBLIC alcommon
|
||||||
PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG})
|
PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG})
|
||||||
set_target_properties(uhjdecoder PROPERTIES ${DEFAULT_TARGET_PROPS})
|
set_target_properties(uhjdecoder PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||||
|
|
||||||
|
|
@ -1589,7 +1618,7 @@ if(ALSOFT_UTILS)
|
||||||
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 common
|
target_link_libraries(uhjencoder PUBLIC alcommon
|
||||||
PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG})
|
PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG})
|
||||||
set_target_properties(uhjencoder PROPERTIES ${DEFAULT_TARGET_PROPS})
|
set_target_properties(uhjencoder PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||||
endif()
|
endif()
|
||||||
|
|
@ -1602,7 +1631,7 @@ if(ALSOFT_UTILS)
|
||||||
target_compile_definitions(sofa-support PRIVATE ${CPP_DEFS})
|
target_compile_definitions(sofa-support PRIVATE ${CPP_DEFS})
|
||||||
target_include_directories(sofa-support PUBLIC ${OpenAL_SOURCE_DIR}/common)
|
target_include_directories(sofa-support PUBLIC ${OpenAL_SOURCE_DIR}/common)
|
||||||
target_compile_options(sofa-support PRIVATE ${C_FLAGS})
|
target_compile_options(sofa-support PRIVATE ${C_FLAGS})
|
||||||
target_link_libraries(sofa-support PUBLIC common MySOFA::MySOFA PRIVATE ${LINKER_FLAGS})
|
target_link_libraries(sofa-support PUBLIC alcommon MySOFA::MySOFA PRIVATE ${LINKER_FLAGS})
|
||||||
set_target_properties(sofa-support PROPERTIES ${DEFAULT_TARGET_PROPS})
|
set_target_properties(sofa-support PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||||
|
|
||||||
set(MAKEMHR_SRCS
|
set(MAKEMHR_SRCS
|
||||||
|
|
@ -1612,9 +1641,6 @@ if(ALSOFT_UTILS)
|
||||||
utils/makemhr/loadsofa.h
|
utils/makemhr/loadsofa.h
|
||||||
utils/makemhr/makemhr.cpp
|
utils/makemhr/makemhr.cpp
|
||||||
utils/makemhr/makemhr.h)
|
utils/makemhr/makemhr.h)
|
||||||
if(NOT HAVE_GETOPT)
|
|
||||||
set(MAKEMHR_SRCS ${MAKEMHR_SRCS} utils/getopt.c utils/getopt.h)
|
|
||||||
endif()
|
|
||||||
add_executable(makemhr ${MAKEMHR_SRCS})
|
add_executable(makemhr ${MAKEMHR_SRCS})
|
||||||
target_compile_definitions(makemhr PRIVATE ${CPP_DEFS})
|
target_compile_definitions(makemhr PRIVATE ${CPP_DEFS})
|
||||||
target_include_directories(makemhr
|
target_include_directories(makemhr
|
||||||
|
|
@ -1644,22 +1670,22 @@ 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(ex-common STATIC EXCLUDE_FROM_ALL
|
add_library(al-excommon STATIC EXCLUDE_FROM_ALL
|
||||||
examples/common/alhelpers.c
|
examples/common/alhelpers.c
|
||||||
examples/common/alhelpers.h)
|
examples/common/alhelpers.h)
|
||||||
target_compile_definitions(ex-common PUBLIC ${CPP_DEFS})
|
target_compile_definitions(al-excommon PUBLIC ${CPP_DEFS})
|
||||||
target_include_directories(ex-common PUBLIC ${OpenAL_SOURCE_DIR}/common)
|
target_include_directories(al-excommon PUBLIC ${OpenAL_SOURCE_DIR}/common)
|
||||||
target_compile_options(ex-common PUBLIC ${C_FLAGS})
|
target_compile_options(al-excommon PUBLIC ${C_FLAGS})
|
||||||
target_link_libraries(ex-common PUBLIC OpenAL PRIVATE ${RT_LIB})
|
target_link_libraries(al-excommon PUBLIC OpenAL PRIVATE ${RT_LIB})
|
||||||
set_target_properties(ex-common PROPERTIES ${DEFAULT_TARGET_PROPS})
|
set_target_properties(al-excommon PROPERTIES ${DEFAULT_TARGET_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} ex-common ${UNICODE_FLAG})
|
target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} al-excommon ${UNICODE_FLAG})
|
||||||
set_target_properties(altonegen PROPERTIES ${DEFAULT_TARGET_PROPS})
|
set_target_properties(altonegen PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||||
|
|
||||||
add_executable(alrecord examples/alrecord.c)
|
add_executable(alrecord examples/alrecord.c)
|
||||||
target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} ex-common ${UNICODE_FLAG})
|
target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} al-excommon ${UNICODE_FLAG})
|
||||||
set_target_properties(alrecord PROPERTIES ${DEFAULT_TARGET_PROPS})
|
set_target_properties(alrecord PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||||
|
|
||||||
if(ALSOFT_INSTALL_EXAMPLES)
|
if(ALSOFT_INSTALL_EXAMPLES)
|
||||||
|
|
@ -1670,48 +1696,53 @@ if(ALSOFT_EXAMPLES)
|
||||||
|
|
||||||
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 ex-common
|
target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
|
||||||
${UNICODE_FLAG})
|
${UNICODE_FLAG})
|
||||||
set_target_properties(alplay PROPERTIES ${DEFAULT_TARGET_PROPS})
|
set_target_properties(alplay PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||||
|
|
||||||
add_executable(alstream examples/alstream.c)
|
add_executable(alstream examples/alstream.c)
|
||||||
target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
|
target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
|
||||||
${UNICODE_FLAG})
|
${UNICODE_FLAG})
|
||||||
set_target_properties(alstream PROPERTIES ${DEFAULT_TARGET_PROPS})
|
set_target_properties(alstream PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||||
|
|
||||||
add_executable(alreverb examples/alreverb.c)
|
add_executable(alreverb examples/alreverb.c)
|
||||||
target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
|
target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
|
||||||
${UNICODE_FLAG})
|
${UNICODE_FLAG})
|
||||||
set_target_properties(alreverb PROPERTIES ${DEFAULT_TARGET_PROPS})
|
set_target_properties(alreverb PROPERTIES ${DEFAULT_TARGET_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 ex-common ${MATH_LIB} ${UNICODE_FLAG})
|
PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${MATH_LIB} ${UNICODE_FLAG})
|
||||||
set_target_properties(almultireverb PROPERTIES ${DEFAULT_TARGET_PROPS})
|
set_target_properties(almultireverb PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||||
|
|
||||||
add_executable(allatency examples/allatency.c)
|
add_executable(allatency examples/allatency.c)
|
||||||
target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
|
target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
|
||||||
${UNICODE_FLAG})
|
${UNICODE_FLAG})
|
||||||
set_target_properties(allatency PROPERTIES ${DEFAULT_TARGET_PROPS})
|
set_target_properties(allatency PROPERTIES ${DEFAULT_TARGET_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 ex-common ${MATH_LIB} ${UNICODE_FLAG})
|
PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${MATH_LIB} ${UNICODE_FLAG})
|
||||||
set_target_properties(alhrtf PROPERTIES ${DEFAULT_TARGET_PROPS})
|
set_target_properties(alhrtf PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||||
|
|
||||||
add_executable(alstreamcb examples/alstreamcb.cpp)
|
add_executable(alstreamcb examples/alstreamcb.cpp)
|
||||||
target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
|
target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
|
||||||
${UNICODE_FLAG})
|
${UNICODE_FLAG})
|
||||||
set_target_properties(alstreamcb PROPERTIES ${DEFAULT_TARGET_PROPS})
|
set_target_properties(alstreamcb PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||||
|
|
||||||
add_executable(alconvolve examples/alconvolve.c)
|
add_executable(aldirect examples/aldirect.cpp)
|
||||||
target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} common SndFile::SndFile ex-common
|
target_link_libraries(aldirect PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
|
||||||
${UNICODE_FLAG})
|
${UNICODE_FLAG})
|
||||||
|
set_target_properties(aldirect PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||||
|
|
||||||
|
add_executable(alconvolve examples/alconvolve.c)
|
||||||
|
target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} alcommon SndFile::SndFile
|
||||||
|
al-excommon ${UNICODE_FLAG})
|
||||||
set_target_properties(alconvolve PROPERTIES ${DEFAULT_TARGET_PROPS})
|
set_target_properties(alconvolve PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||||
|
|
||||||
if(ALSOFT_INSTALL_EXAMPLES)
|
if(ALSOFT_INSTALL_EXAMPLES)
|
||||||
set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alplay alstream alreverb almultireverb allatency
|
set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alplay alstream alreverb almultireverb allatency
|
||||||
alhrtf)
|
alhrtf aldirect)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
message(STATUS "Building SndFile example programs")
|
message(STATUS "Building SndFile example programs")
|
||||||
|
|
@ -1720,7 +1751,7 @@ if(ALSOFT_EXAMPLES)
|
||||||
if(SDL2_FOUND)
|
if(SDL2_FOUND)
|
||||||
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 ex-common ${MATH_LIB})
|
PRIVATE ${LINKER_FLAGS} SDL2::SDL2 al-excommon ${MATH_LIB})
|
||||||
set_target_properties(alloopback PROPERTIES ${DEFAULT_TARGET_PROPS})
|
set_target_properties(alloopback PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||||
|
|
||||||
if(ALSOFT_INSTALL_EXAMPLES)
|
if(ALSOFT_INSTALL_EXAMPLES)
|
||||||
|
|
@ -1757,7 +1788,7 @@ 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} ex-common)
|
PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ${FFMPEG_LIBRARIES} al-excommon)
|
||||||
set_target_properties(alffplay PROPERTIES ${DEFAULT_TARGET_PROPS})
|
set_target_properties(alffplay PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||||
|
|
||||||
if(ALSOFT_INSTALL_EXAMPLES)
|
if(ALSOFT_INSTALL_EXAMPLES)
|
||||||
|
|
@ -1769,6 +1800,10 @@ if(ALSOFT_EXAMPLES)
|
||||||
message(STATUS "")
|
message(STATUS "")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (ALSOFT_TESTS)
|
||||||
|
add_subdirectory(tests)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(EXTRA_INSTALLS)
|
if(EXTRA_INSTALLS)
|
||||||
install(TARGETS ${EXTRA_INSTALLS}
|
install(TARGETS ${EXTRA_INSTALLS}
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
|
|
|
||||||
38
Engine/lib/openal-soft/LICENSE-pffft
Normal file
38
Engine/lib/openal-soft/LICENSE-pffft
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
A modified PFFFT is included, with the following license.
|
||||||
|
|
||||||
|
Copyright (c) 2023 Christopher Robinson
|
||||||
|
|
||||||
|
Copyright (c) 2013 Julien Pommier ( pommier@modartt.com )
|
||||||
|
|
||||||
|
Copyright (c) 2004 the University Corporation for Atmospheric
|
||||||
|
Research ("UCAR"). All rights reserved. Developed by NCAR's
|
||||||
|
Computational and Information Systems Laboratory, UCAR,
|
||||||
|
www.cisl.ucar.edu.
|
||||||
|
|
||||||
|
Redistribution and use of the Software in source and binary forms,
|
||||||
|
with or without modification, is permitted provided that the
|
||||||
|
following conditions are met:
|
||||||
|
|
||||||
|
- Neither the names of NCAR's Computational and Information Systems
|
||||||
|
Laboratory, the University Corporation for Atmospheric Research,
|
||||||
|
nor the names of its sponsors or contributors may be used to
|
||||||
|
endorse or promote products derived from this Software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright
|
||||||
|
notices, this list of conditions, and the disclaimer below.
|
||||||
|
|
||||||
|
- Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions, and the disclaimer below in the
|
||||||
|
documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
cmake_minimum_required(VERSION 3.1)
|
cmake_minimum_required(VERSION 3.1...3.18)
|
||||||
|
|
||||||
include("${CMAKE_CURRENT_LIST_DIR}/OpenALTargets.cmake")
|
include("${CMAKE_CURRENT_LIST_DIR}/OpenALTargets.cmake")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,33 @@ as application-agnostic behavior of the library. See alsoftrc.sample for
|
||||||
available settings.
|
available settings.
|
||||||
|
|
||||||
|
|
||||||
|
Language Bindings
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
As a C API, OpenAL Soft can be used directly by any language that can use
|
||||||
|
functions with C linkage. For languages that can't directly use C-style
|
||||||
|
headers, bindings may be developed to allow code written in that language to
|
||||||
|
call into the library. Some bindings for some languages are listed here.
|
||||||
|
|
||||||
|
C# Bindings:
|
||||||
|
* [OpenTK](https://opentk.net/) includes low-level C# bindings for the OpenAL
|
||||||
|
API, including some extensions. It also includes utility libraries for math and
|
||||||
|
linear algebra, which can be useful for 3D calculations.
|
||||||
|
|
||||||
|
Java Bindings:
|
||||||
|
* [JOAL](https://jogamp.org/joal/www/), part of the JogAmp project, includes
|
||||||
|
Java bindings for the OpenAL API, usable with OpenAL Soft. It also includes a
|
||||||
|
higher level Sound3D Toolkit API and utility functions to make easier use of
|
||||||
|
OpenAL features and capabilities.
|
||||||
|
|
||||||
|
Python Bindings:
|
||||||
|
* [PyOpenAL](https://pypi.org/project/PyOpenAL/). Also includes methods to play
|
||||||
|
wave files and, with PyOgg, also Vorbis, Opus, and FLAC.
|
||||||
|
|
||||||
|
Other bindings for these and other languages also exist. This list will grow as
|
||||||
|
more bindings are found.
|
||||||
|
|
||||||
|
|
||||||
Acknowledgements
|
Acknowledgements
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,23 +1,24 @@
|
||||||
#ifndef AL_AUXEFFECTSLOT_H
|
#ifndef AL_AUXEFFECTSLOT_H
|
||||||
#define AL_AUXEFFECTSLOT_H
|
#define AL_AUXEFFECTSLOT_H
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstddef>
|
#include <cstdint>
|
||||||
|
#include <string_view>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/alc.h"
|
#include "AL/alc.h"
|
||||||
#include "AL/efx.h"
|
|
||||||
|
|
||||||
#include "alc/device.h"
|
|
||||||
#include "alc/effects/base.h"
|
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "atomic.h"
|
#include "alnumeric.h"
|
||||||
|
#include "core/effects/base.h"
|
||||||
#include "core/effectslot.h"
|
#include "core/effectslot.h"
|
||||||
#include "intrusive_ptr.h"
|
#include "intrusive_ptr.h"
|
||||||
#include "vector.h"
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include "eax/api.h"
|
||||||
#include "eax/call.h"
|
#include "eax/call.h"
|
||||||
#include "eax/effect.h"
|
#include "eax/effect.h"
|
||||||
#include "eax/exception.h"
|
#include "eax/exception.h"
|
||||||
|
|
@ -26,8 +27,6 @@
|
||||||
#endif // ALSOFT_EAX
|
#endif // ALSOFT_EAX
|
||||||
|
|
||||||
struct ALbuffer;
|
struct ALbuffer;
|
||||||
struct ALeffect;
|
|
||||||
struct WetBuffer;
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
class EaxFxSlotException : public EaxException {
|
class EaxFxSlotException : public EaxException {
|
||||||
|
|
@ -38,30 +37,30 @@ public:
|
||||||
};
|
};
|
||||||
#endif // ALSOFT_EAX
|
#endif // ALSOFT_EAX
|
||||||
|
|
||||||
enum class SlotState : ALenum {
|
enum class SlotState : bool {
|
||||||
Initial = AL_INITIAL,
|
Initial, Playing,
|
||||||
Playing = AL_PLAYING,
|
|
||||||
Stopped = AL_STOPPED,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ALeffectslot {
|
struct ALeffectslot {
|
||||||
|
ALuint EffectId{};
|
||||||
float Gain{1.0f};
|
float Gain{1.0f};
|
||||||
bool AuxSendAuto{true};
|
bool AuxSendAuto{true};
|
||||||
ALeffectslot *Target{nullptr};
|
ALeffectslot *Target{nullptr};
|
||||||
ALbuffer *Buffer{nullptr};
|
ALbuffer *Buffer{nullptr};
|
||||||
|
|
||||||
struct {
|
struct EffectData {
|
||||||
EffectSlotType Type{EffectSlotType::None};
|
EffectSlotType Type{EffectSlotType::None};
|
||||||
EffectProps Props{};
|
EffectProps Props{};
|
||||||
|
|
||||||
al::intrusive_ptr<EffectState> State;
|
al::intrusive_ptr<EffectState> State;
|
||||||
} Effect;
|
};
|
||||||
|
EffectData Effect;
|
||||||
|
|
||||||
bool mPropsDirty{true};
|
bool mPropsDirty{true};
|
||||||
|
|
||||||
SlotState mState{SlotState::Initial};
|
SlotState mState{SlotState::Initial};
|
||||||
|
|
||||||
RefCount ref{0u};
|
std::atomic<ALuint> ref{0u};
|
||||||
|
|
||||||
EffectSlot *mSlot{nullptr};
|
EffectSlot *mSlot{nullptr};
|
||||||
|
|
||||||
|
|
@ -73,23 +72,23 @@ struct ALeffectslot {
|
||||||
ALeffectslot& operator=(const ALeffectslot&) = delete;
|
ALeffectslot& operator=(const ALeffectslot&) = delete;
|
||||||
~ALeffectslot();
|
~ALeffectslot();
|
||||||
|
|
||||||
ALenum initEffect(ALenum effectType, const EffectProps &effectProps, ALCcontext *context);
|
ALenum initEffect(ALuint effectId, ALenum effectType, const EffectProps &effectProps,
|
||||||
void updateProps(ALCcontext *context);
|
ALCcontext *context);
|
||||||
|
void updateProps(ALCcontext *context) const;
|
||||||
|
|
||||||
/* This can be new'd for the context's default effect slot. */
|
static void SetName(ALCcontext *context, ALuint id, std::string_view name);
|
||||||
DEF_NEWDEL(ALeffectslot)
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
public:
|
public:
|
||||||
void eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index);
|
void eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index);
|
||||||
|
|
||||||
EaxFxSlotIndexValue eax_get_index() const noexcept { return eax_fx_slot_index_; }
|
[[nodiscard]] auto eax_get_index() const noexcept -> EaxFxSlotIndexValue { return eax_fx_slot_index_; }
|
||||||
const EAX50FXSLOTPROPERTIES& eax_get_eax_fx_slot() const noexcept
|
[[nodiscard]] auto eax_get_eax_fx_slot() const noexcept -> const EAX50FXSLOTPROPERTIES&
|
||||||
{ return eax_; }
|
{ return eax_; }
|
||||||
|
|
||||||
// Returns `true` if all sources should be updated, or `false` otherwise.
|
// Returns `true` if all sources should be updated, or `false` otherwise.
|
||||||
bool eax_dispatch(const EaxCall& call)
|
[[nodiscard]] auto eax_dispatch(const EaxCall& call) -> bool
|
||||||
{ return call.is_get() ? eax_get(call) : eax_set(call); }
|
{ return call.is_get() ? eax_get(call) : eax_set(call); }
|
||||||
|
|
||||||
void eax_commit();
|
void eax_commit();
|
||||||
|
|
@ -193,6 +192,17 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Eax5FlagsValidator {
|
||||||
|
void operator()(unsigned long ulFlags) const
|
||||||
|
{
|
||||||
|
EaxRangeValidator{}(
|
||||||
|
"Flags",
|
||||||
|
ulFlags,
|
||||||
|
0UL,
|
||||||
|
~EAX50FXSLOTFLAGS_RESERVED);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct Eax5OcclusionValidator {
|
struct Eax5OcclusionValidator {
|
||||||
void operator()(long lOcclusion) const
|
void operator()(long lOcclusion) const
|
||||||
{
|
{
|
||||||
|
|
@ -215,21 +225,13 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Eax5FlagsValidator {
|
|
||||||
void operator()(unsigned long ulFlags) const
|
|
||||||
{
|
|
||||||
EaxRangeValidator{}(
|
|
||||||
"Flags",
|
|
||||||
ulFlags,
|
|
||||||
0UL,
|
|
||||||
~EAX50FXSLOTFLAGS_RESERVED);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Eax5AllValidator {
|
struct Eax5AllValidator {
|
||||||
void operator()(const EAX50FXSLOTPROPERTIES& all) const
|
void operator()(const EAX50FXSLOTPROPERTIES& all) const
|
||||||
{
|
{
|
||||||
Eax4AllValidator{}(static_cast<const EAX40FXSLOTPROPERTIES&>(all));
|
Eax4GuidLoadEffectValidator{}(all.guidLoadEffect);
|
||||||
|
Eax4VolumeValidator{}(all.lVolume);
|
||||||
|
Eax4LockValidator{}(all.lLock);
|
||||||
|
Eax5FlagsValidator{}(all.ulFlags);
|
||||||
Eax5OcclusionValidator{}(all.lOcclusion);
|
Eax5OcclusionValidator{}(all.lOcclusion);
|
||||||
Eax5OcclusionLfRatioValidator{}(all.flOcclusionLFRatio);
|
Eax5OcclusionLfRatioValidator{}(all.flOcclusionLFRatio);
|
||||||
}
|
}
|
||||||
|
|
@ -277,14 +279,14 @@ private:
|
||||||
dst = src;
|
dst = src;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool eax4_fx_slot_is_legacy() const noexcept
|
[[nodiscard]] constexpr auto eax4_fx_slot_is_legacy() const noexcept -> bool
|
||||||
{ return eax_fx_slot_index_ < 2; }
|
{ return eax_fx_slot_index_ < 2; }
|
||||||
|
|
||||||
void eax4_fx_slot_ensure_unlocked() const;
|
void eax4_fx_slot_ensure_unlocked() const;
|
||||||
|
|
||||||
static ALenum eax_get_efx_effect_type(const GUID& guid);
|
[[nodiscard]] static auto eax_get_efx_effect_type(const GUID& guid) -> ALenum;
|
||||||
const GUID& eax_get_eax_default_effect_guid() const noexcept;
|
[[nodiscard]] auto eax_get_eax_default_effect_guid() const noexcept -> const GUID&;
|
||||||
long eax_get_eax_default_lock() const noexcept;
|
[[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(Eax4Props& props) noexcept;
|
||||||
void eax5_fx_slot_set_defaults(Eax5Props& props) noexcept;
|
void eax5_fx_slot_set_defaults(Eax5Props& props) noexcept;
|
||||||
|
|
@ -293,8 +295,8 @@ private:
|
||||||
void eax_fx_slot_set_current_defaults();
|
void eax_fx_slot_set_current_defaults();
|
||||||
void eax_fx_slot_set_defaults();
|
void eax_fx_slot_set_defaults();
|
||||||
|
|
||||||
void eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props) const;
|
static void eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props);
|
||||||
void eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props) const;
|
static void eax5_fx_slot_get(const EaxCall& call, const Eax5Props& 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);
|
||||||
|
|
@ -307,7 +309,7 @@ private:
|
||||||
void eax4_fx_slot_set_all(const EaxCall& call);
|
void eax4_fx_slot_set_all(const EaxCall& call);
|
||||||
void eax5_fx_slot_set_all(const EaxCall& call);
|
void eax5_fx_slot_set_all(const EaxCall& call);
|
||||||
|
|
||||||
bool eax_fx_slot_should_update_sources() const noexcept;
|
[[nodiscard]] auto eax_fx_slot_should_update_sources() const noexcept -> bool;
|
||||||
|
|
||||||
// Returns `true` if all sources should be updated, or `false` otherwise.
|
// Returns `true` if all sources should be updated, or `false` otherwise.
|
||||||
bool eax4_fx_slot_set(const EaxCall& call);
|
bool eax4_fx_slot_set(const EaxCall& call);
|
||||||
|
|
@ -365,4 +367,20 @@ EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context);
|
||||||
void eax_delete_al_effect_slot(ALCcontext& context, ALeffectslot& effect_slot);
|
void eax_delete_al_effect_slot(ALCcontext& context, ALeffectslot& effect_slot);
|
||||||
#endif // ALSOFT_EAX
|
#endif // ALSOFT_EAX
|
||||||
|
|
||||||
|
struct EffectSlotSubList {
|
||||||
|
uint64_t FreeMask{~0_u64};
|
||||||
|
gsl::owner<std::array<ALeffectslot,64>*> EffectSlots{nullptr};
|
||||||
|
|
||||||
|
EffectSlotSubList() noexcept = default;
|
||||||
|
EffectSlotSubList(const EffectSlotSubList&) = delete;
|
||||||
|
EffectSlotSubList(EffectSlotSubList&& rhs) noexcept
|
||||||
|
: FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots}
|
||||||
|
{ rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; }
|
||||||
|
~EffectSlotSubList();
|
||||||
|
|
||||||
|
EffectSlotSubList& operator=(const EffectSlotSubList&) = delete;
|
||||||
|
EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept
|
||||||
|
{ std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,20 +1,23 @@
|
||||||
#ifndef AL_BUFFER_H
|
#ifndef AL_BUFFER_H
|
||||||
#define AL_BUFFER_H
|
#define AL_BUFFER_H
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string_view>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
|
#include "AL/alc.h"
|
||||||
|
|
||||||
#include "albyte.h"
|
|
||||||
#include "alc/inprogext.h"
|
#include "alc/inprogext.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "atomic.h"
|
#include "alnumeric.h"
|
||||||
#include "core/buffer_storage.h"
|
#include "core/buffer_storage.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
#include "eax/x_ram.h"
|
|
||||||
|
|
||||||
enum class EaxStorage : uint8_t {
|
enum class EaxStorage : uint8_t {
|
||||||
Automatic,
|
Automatic,
|
||||||
Accessible,
|
Accessible,
|
||||||
|
|
@ -26,7 +29,7 @@ enum class EaxStorage : uint8_t {
|
||||||
struct ALbuffer : public BufferStorage {
|
struct ALbuffer : public BufferStorage {
|
||||||
ALbitfieldSOFT Access{0u};
|
ALbitfieldSOFT Access{0u};
|
||||||
|
|
||||||
al::vector<al::byte,16> mDataStorage;
|
al::vector<std::byte,16> mDataStorage;
|
||||||
|
|
||||||
ALuint OriginalSize{0};
|
ALuint OriginalSize{0};
|
||||||
|
|
||||||
|
|
@ -42,12 +45,14 @@ struct ALbuffer : public BufferStorage {
|
||||||
ALuint mLoopEnd{0u};
|
ALuint mLoopEnd{0u};
|
||||||
|
|
||||||
/* Number of times buffer was attached to a source (deletion can only occur when 0) */
|
/* Number of times buffer was attached to a source (deletion can only occur when 0) */
|
||||||
RefCount ref{0u};
|
std::atomic<ALuint> ref{0u};
|
||||||
|
|
||||||
/* Self ID */
|
/* Self ID */
|
||||||
ALuint id{0};
|
ALuint id{0};
|
||||||
|
|
||||||
DISABLE_ALLOC()
|
static void SetName(ALCcontext *context, ALuint id, std::string_view name);
|
||||||
|
|
||||||
|
DISABLE_ALLOC
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
EaxStorage eax_x_ram_mode{EaxStorage::Automatic};
|
EaxStorage eax_x_ram_mode{EaxStorage::Automatic};
|
||||||
|
|
@ -55,4 +60,19 @@ struct ALbuffer : public BufferStorage {
|
||||||
#endif // ALSOFT_EAX
|
#endif // ALSOFT_EAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BufferSubList {
|
||||||
|
uint64_t FreeMask{~0_u64};
|
||||||
|
gsl::owner<std::array<ALbuffer,64>*> Buffers{nullptr};
|
||||||
|
|
||||||
|
BufferSubList() noexcept = default;
|
||||||
|
BufferSubList(const BufferSubList&) = delete;
|
||||||
|
BufferSubList(BufferSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Buffers{rhs.Buffers}
|
||||||
|
{ rhs.FreeMask = ~0_u64; rhs.Buffers = nullptr; }
|
||||||
|
~BufferSubList();
|
||||||
|
|
||||||
|
BufferSubList& operator=(const BufferSubList&) = delete;
|
||||||
|
BufferSubList& operator=(BufferSubList&& rhs) noexcept
|
||||||
|
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Buffers, rhs.Buffers); return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
618
Engine/lib/openal-soft/al/debug.cpp
Normal file
618
Engine/lib/openal-soft/al/debug.cpp
Normal file
|
|
@ -0,0 +1,618 @@
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstring>
|
||||||
|
#include <deque>
|
||||||
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "AL/al.h"
|
||||||
|
#include "AL/alc.h"
|
||||||
|
#include "AL/alext.h"
|
||||||
|
|
||||||
|
#include "alc/context.h"
|
||||||
|
#include "alc/device.h"
|
||||||
|
#include "alc/inprogext.h"
|
||||||
|
#include "alnumeric.h"
|
||||||
|
#include "alspan.h"
|
||||||
|
#include "alstring.h"
|
||||||
|
#include "auxeffectslot.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "core/logging.h"
|
||||||
|
#include "core/voice.h"
|
||||||
|
#include "direct_defs.h"
|
||||||
|
#include "effect.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "filter.h"
|
||||||
|
#include "intrusive_ptr.h"
|
||||||
|
#include "opthelpers.h"
|
||||||
|
#include "source.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Declared here to prevent compilers from thinking it should be inlined, which
|
||||||
|
* GCC warns about increasing code size.
|
||||||
|
*/
|
||||||
|
DebugGroup::~DebugGroup() = default;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
static_assert(DebugSeverityBase+DebugSeverityCount <= 32, "Too many debug bits");
|
||||||
|
|
||||||
|
template<typename T, T ...Vals>
|
||||||
|
constexpr auto make_array_sequence(std::integer_sequence<T, Vals...>)
|
||||||
|
{ return std::array<T,sizeof...(Vals)>{Vals...}; }
|
||||||
|
|
||||||
|
template<typename T, size_t N>
|
||||||
|
constexpr auto make_array_sequence()
|
||||||
|
{ return make_array_sequence(std::make_integer_sequence<T,N>{}); }
|
||||||
|
|
||||||
|
|
||||||
|
constexpr auto GetDebugSource(ALenum source) noexcept -> std::optional<DebugSource>
|
||||||
|
{
|
||||||
|
switch(source)
|
||||||
|
{
|
||||||
|
case AL_DEBUG_SOURCE_API_EXT: return DebugSource::API;
|
||||||
|
case AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT: return DebugSource::System;
|
||||||
|
case AL_DEBUG_SOURCE_THIRD_PARTY_EXT: return DebugSource::ThirdParty;
|
||||||
|
case AL_DEBUG_SOURCE_APPLICATION_EXT: return DebugSource::Application;
|
||||||
|
case AL_DEBUG_SOURCE_OTHER_EXT: return DebugSource::Other;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto GetDebugType(ALenum type) noexcept -> std::optional<DebugType>
|
||||||
|
{
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case AL_DEBUG_TYPE_ERROR_EXT: return DebugType::Error;
|
||||||
|
case AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT: return DebugType::DeprecatedBehavior;
|
||||||
|
case AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT: return DebugType::UndefinedBehavior;
|
||||||
|
case AL_DEBUG_TYPE_PORTABILITY_EXT: return DebugType::Portability;
|
||||||
|
case AL_DEBUG_TYPE_PERFORMANCE_EXT: return DebugType::Performance;
|
||||||
|
case AL_DEBUG_TYPE_MARKER_EXT: return DebugType::Marker;
|
||||||
|
case AL_DEBUG_TYPE_PUSH_GROUP_EXT: return DebugType::PushGroup;
|
||||||
|
case AL_DEBUG_TYPE_POP_GROUP_EXT: return DebugType::PopGroup;
|
||||||
|
case AL_DEBUG_TYPE_OTHER_EXT: return DebugType::Other;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto GetDebugSeverity(ALenum severity) noexcept -> std::optional<DebugSeverity>
|
||||||
|
{
|
||||||
|
switch(severity)
|
||||||
|
{
|
||||||
|
case AL_DEBUG_SEVERITY_HIGH_EXT: return DebugSeverity::High;
|
||||||
|
case AL_DEBUG_SEVERITY_MEDIUM_EXT: return DebugSeverity::Medium;
|
||||||
|
case AL_DEBUG_SEVERITY_LOW_EXT: return DebugSeverity::Low;
|
||||||
|
case AL_DEBUG_SEVERITY_NOTIFICATION_EXT: return DebugSeverity::Notification;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constexpr auto GetDebugSourceEnum(DebugSource source) -> ALenum
|
||||||
|
{
|
||||||
|
switch(source)
|
||||||
|
{
|
||||||
|
case DebugSource::API: return AL_DEBUG_SOURCE_API_EXT;
|
||||||
|
case DebugSource::System: return AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT;
|
||||||
|
case DebugSource::ThirdParty: return AL_DEBUG_SOURCE_THIRD_PARTY_EXT;
|
||||||
|
case DebugSource::Application: return AL_DEBUG_SOURCE_APPLICATION_EXT;
|
||||||
|
case DebugSource::Other: return AL_DEBUG_SOURCE_OTHER_EXT;
|
||||||
|
}
|
||||||
|
throw std::runtime_error{"Unexpected debug source value "+std::to_string(al::to_underlying(source))};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto GetDebugTypeEnum(DebugType type) -> ALenum
|
||||||
|
{
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case DebugType::Error: return AL_DEBUG_TYPE_ERROR_EXT;
|
||||||
|
case DebugType::DeprecatedBehavior: return AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT;
|
||||||
|
case DebugType::UndefinedBehavior: return AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT;
|
||||||
|
case DebugType::Portability: return AL_DEBUG_TYPE_PORTABILITY_EXT;
|
||||||
|
case DebugType::Performance: return AL_DEBUG_TYPE_PERFORMANCE_EXT;
|
||||||
|
case DebugType::Marker: return AL_DEBUG_TYPE_MARKER_EXT;
|
||||||
|
case DebugType::PushGroup: return AL_DEBUG_TYPE_PUSH_GROUP_EXT;
|
||||||
|
case DebugType::PopGroup: return AL_DEBUG_TYPE_POP_GROUP_EXT;
|
||||||
|
case DebugType::Other: return AL_DEBUG_TYPE_OTHER_EXT;
|
||||||
|
}
|
||||||
|
throw std::runtime_error{"Unexpected debug type value "+std::to_string(al::to_underlying(type))};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto GetDebugSeverityEnum(DebugSeverity severity) -> ALenum
|
||||||
|
{
|
||||||
|
switch(severity)
|
||||||
|
{
|
||||||
|
case DebugSeverity::High: return AL_DEBUG_SEVERITY_HIGH_EXT;
|
||||||
|
case DebugSeverity::Medium: return AL_DEBUG_SEVERITY_MEDIUM_EXT;
|
||||||
|
case DebugSeverity::Low: return AL_DEBUG_SEVERITY_LOW_EXT;
|
||||||
|
case DebugSeverity::Notification: return AL_DEBUG_SEVERITY_NOTIFICATION_EXT;
|
||||||
|
}
|
||||||
|
throw std::runtime_error{"Unexpected debug severity value "+std::to_string(al::to_underlying(severity))};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constexpr auto GetDebugSourceName(DebugSource source) noexcept -> const char*
|
||||||
|
{
|
||||||
|
switch(source)
|
||||||
|
{
|
||||||
|
case DebugSource::API: return "API";
|
||||||
|
case DebugSource::System: return "Audio System";
|
||||||
|
case DebugSource::ThirdParty: return "Third Party";
|
||||||
|
case DebugSource::Application: return "Application";
|
||||||
|
case DebugSource::Other: return "Other";
|
||||||
|
}
|
||||||
|
return "<invalid source>";
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto GetDebugTypeName(DebugType type) noexcept -> const char*
|
||||||
|
{
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case DebugType::Error: return "Error";
|
||||||
|
case DebugType::DeprecatedBehavior: return "Deprecated Behavior";
|
||||||
|
case DebugType::UndefinedBehavior: return "Undefined Behavior";
|
||||||
|
case DebugType::Portability: return "Portability";
|
||||||
|
case DebugType::Performance: return "Performance";
|
||||||
|
case DebugType::Marker: return "Marker";
|
||||||
|
case DebugType::PushGroup: return "Push Group";
|
||||||
|
case DebugType::PopGroup: return "Pop Group";
|
||||||
|
case DebugType::Other: return "Other";
|
||||||
|
}
|
||||||
|
return "<invalid type>";
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto GetDebugSeverityName(DebugSeverity severity) noexcept -> const char*
|
||||||
|
{
|
||||||
|
switch(severity)
|
||||||
|
{
|
||||||
|
case DebugSeverity::High: return "High";
|
||||||
|
case DebugSeverity::Medium: return "Medium";
|
||||||
|
case DebugSeverity::Low: return "Low";
|
||||||
|
case DebugSeverity::Notification: return "Notification";
|
||||||
|
}
|
||||||
|
return "<invalid severity>";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
|
void ALCcontext::sendDebugMessage(std::unique_lock<std::mutex> &debuglock, DebugSource source,
|
||||||
|
DebugType type, ALuint id, DebugSeverity severity, std::string_view message)
|
||||||
|
{
|
||||||
|
if(!mDebugEnabled.load(std::memory_order_relaxed)) UNLIKELY
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(message.length() >= MaxDebugMessageLength) UNLIKELY
|
||||||
|
{
|
||||||
|
ERR("Debug message too long (%zu >= %d):\n-> %.*s\n", message.length(),
|
||||||
|
MaxDebugMessageLength, al::sizei(message), message.data());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugGroup &debug = mDebugGroups.back();
|
||||||
|
|
||||||
|
const uint64_t idfilter{(1_u64 << (DebugSourceBase+al::to_underlying(source)))
|
||||||
|
| (1_u64 << (DebugTypeBase+al::to_underlying(type)))
|
||||||
|
| (uint64_t{id} << 32)};
|
||||||
|
auto iditer = std::lower_bound(debug.mIdFilters.cbegin(), debug.mIdFilters.cend(), idfilter);
|
||||||
|
if(iditer != debug.mIdFilters.cend() && *iditer == idfilter)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const uint filter{(1u << (DebugSourceBase+al::to_underlying(source)))
|
||||||
|
| (1u << (DebugTypeBase+al::to_underlying(type)))
|
||||||
|
| (1u << (DebugSeverityBase+al::to_underlying(severity)))};
|
||||||
|
auto iter = std::lower_bound(debug.mFilters.cbegin(), debug.mFilters.cend(), filter);
|
||||||
|
if(iter != debug.mFilters.cend() && *iter == filter)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(mDebugCb)
|
||||||
|
{
|
||||||
|
auto callback = mDebugCb;
|
||||||
|
auto param = mDebugParam;
|
||||||
|
debuglock.unlock();
|
||||||
|
callback(GetDebugSourceEnum(source), GetDebugTypeEnum(type), id,
|
||||||
|
GetDebugSeverityEnum(severity), static_cast<ALsizei>(message.length()), message.data(),
|
||||||
|
param);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(mDebugLog.size() < MaxDebugLoggedMessages)
|
||||||
|
mDebugLog.emplace_back(source, type, id, severity, message);
|
||||||
|
else UNLIKELY
|
||||||
|
ERR("Debug message log overflow. Lost message:\n"
|
||||||
|
" Source: %s\n"
|
||||||
|
" Type: %s\n"
|
||||||
|
" ID: %u\n"
|
||||||
|
" Severity: %s\n"
|
||||||
|
" Message: \"%.*s\"\n",
|
||||||
|
GetDebugSourceName(source), GetDebugTypeName(type), id,
|
||||||
|
GetDebugSeverityName(severity), al::sizei(message), message.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FORCE_ALIGN DECL_FUNCEXT2(void, alDebugMessageCallback,EXT, ALDEBUGPROCEXT,callback, void*,userParam)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alDebugMessageCallbackDirectEXT(ALCcontext *context,
|
||||||
|
ALDEBUGPROCEXT callback, void *userParam) noexcept
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
|
||||||
|
context->mDebugCb = callback;
|
||||||
|
context->mDebugParam = userParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageInsert,EXT, ALenum,source, ALenum,type, ALuint,id, ALenum,severity, ALsizei,length, const ALchar*,message)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alDebugMessageInsertDirectEXT(ALCcontext *context, ALenum source,
|
||||||
|
ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) noexcept
|
||||||
|
try {
|
||||||
|
if(!context->mContextFlags.test(ContextFlags::DebugBit))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(!message)
|
||||||
|
throw al::context_error{AL_INVALID_VALUE, "Null message pointer"};
|
||||||
|
|
||||||
|
auto msgview = (length < 0) ? std::string_view{message}
|
||||||
|
: std::string_view{message, static_cast<uint>(length)};
|
||||||
|
if(msgview.size() >= MaxDebugMessageLength)
|
||||||
|
throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%zu >= %d)",
|
||||||
|
msgview.size(), MaxDebugMessageLength};
|
||||||
|
|
||||||
|
auto dsource = GetDebugSource(source);
|
||||||
|
if(!dsource)
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
|
||||||
|
if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source};
|
||||||
|
|
||||||
|
auto dtype = GetDebugType(type);
|
||||||
|
if(!dtype)
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid debug type 0x%04x", type};
|
||||||
|
|
||||||
|
auto dseverity = GetDebugSeverity(severity);
|
||||||
|
if(!dseverity)
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity};
|
||||||
|
|
||||||
|
context->debugMessage(*dsource, *dtype, id, *dseverity, msgview);
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageControl,EXT, ALenum,source, ALenum,type, ALenum,severity, ALsizei,count, const ALuint*,ids, ALboolean,enable)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, ALenum source,
|
||||||
|
ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) noexcept
|
||||||
|
try {
|
||||||
|
if(count > 0)
|
||||||
|
{
|
||||||
|
if(!ids)
|
||||||
|
throw al::context_error{AL_INVALID_VALUE, "IDs is null with non-0 count"};
|
||||||
|
if(source == AL_DONT_CARE_EXT)
|
||||||
|
throw al::context_error{AL_INVALID_OPERATION,
|
||||||
|
"Debug source cannot be AL_DONT_CARE_EXT with IDs"};
|
||||||
|
if(type == AL_DONT_CARE_EXT)
|
||||||
|
throw al::context_error{AL_INVALID_OPERATION,
|
||||||
|
"Debug type cannot be AL_DONT_CARE_EXT with IDs"};
|
||||||
|
if(severity != AL_DONT_CARE_EXT)
|
||||||
|
throw al::context_error{AL_INVALID_OPERATION,
|
||||||
|
"Debug severity must be AL_DONT_CARE_EXT with IDs"};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(enable != AL_TRUE && enable != AL_FALSE)
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid debug enable %d", enable};
|
||||||
|
|
||||||
|
static constexpr size_t ElemCount{DebugSourceCount + DebugTypeCount + DebugSeverityCount};
|
||||||
|
static constexpr auto Values = make_array_sequence<uint8_t,ElemCount>();
|
||||||
|
|
||||||
|
auto srcIndices = al::span{Values}.subspan(DebugSourceBase,DebugSourceCount);
|
||||||
|
if(source != AL_DONT_CARE_EXT)
|
||||||
|
{
|
||||||
|
auto dsource = GetDebugSource(source);
|
||||||
|
if(!dsource)
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
|
||||||
|
srcIndices = srcIndices.subspan(al::to_underlying(*dsource), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto typeIndices = al::span{Values}.subspan(DebugTypeBase,DebugTypeCount);
|
||||||
|
if(type != AL_DONT_CARE_EXT)
|
||||||
|
{
|
||||||
|
auto dtype = GetDebugType(type);
|
||||||
|
if(!dtype)
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid debug type 0x%04x", type};
|
||||||
|
typeIndices = typeIndices.subspan(al::to_underlying(*dtype), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto svrIndices = al::span{Values}.subspan(DebugSeverityBase,DebugSeverityCount);
|
||||||
|
if(severity != AL_DONT_CARE_EXT)
|
||||||
|
{
|
||||||
|
auto dseverity = GetDebugSeverity(severity);
|
||||||
|
if(!dseverity)
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity};
|
||||||
|
svrIndices = svrIndices.subspan(al::to_underlying(*dseverity), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
|
||||||
|
DebugGroup &debug = context->mDebugGroups.back();
|
||||||
|
if(count > 0)
|
||||||
|
{
|
||||||
|
const uint filterbase{(1u<<srcIndices[0]) | (1u<<typeIndices[0])};
|
||||||
|
|
||||||
|
for(const uint id : al::span{ids, static_cast<uint>(count)})
|
||||||
|
{
|
||||||
|
const uint64_t filter{filterbase | (uint64_t{id} << 32)};
|
||||||
|
|
||||||
|
auto iter = std::lower_bound(debug.mIdFilters.cbegin(), debug.mIdFilters.cend(),
|
||||||
|
filter);
|
||||||
|
if(!enable && (iter == debug.mIdFilters.cend() || *iter != filter))
|
||||||
|
debug.mIdFilters.insert(iter, filter);
|
||||||
|
else if(enable && iter != debug.mIdFilters.cend() && *iter == filter)
|
||||||
|
debug.mIdFilters.erase(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto apply_filter = [enable,&debug](const uint filter)
|
||||||
|
{
|
||||||
|
auto iter = std::lower_bound(debug.mFilters.cbegin(), debug.mFilters.cend(), filter);
|
||||||
|
if(!enable && (iter == debug.mFilters.cend() || *iter != filter))
|
||||||
|
debug.mFilters.insert(iter, filter);
|
||||||
|
else if(enable && iter != debug.mFilters.cend() && *iter == filter)
|
||||||
|
debug.mFilters.erase(iter);
|
||||||
|
};
|
||||||
|
auto apply_severity = [apply_filter,svrIndices](const uint filter)
|
||||||
|
{
|
||||||
|
std::for_each(svrIndices.cbegin(), svrIndices.cend(),
|
||||||
|
[apply_filter,filter](const uint idx){ apply_filter(filter | (1<<idx)); });
|
||||||
|
};
|
||||||
|
auto apply_type = [apply_severity,typeIndices](const uint filter)
|
||||||
|
{
|
||||||
|
std::for_each(typeIndices.cbegin(), typeIndices.cend(),
|
||||||
|
[apply_severity,filter](const uint idx){ apply_severity(filter | (1<<idx)); });
|
||||||
|
};
|
||||||
|
std::for_each(srcIndices.cbegin(), srcIndices.cend(),
|
||||||
|
[apply_type](const uint idx){ apply_type(1<<idx); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FORCE_ALIGN DECL_FUNCEXT4(void, alPushDebugGroup,EXT, ALenum,source, ALuint,id, ALsizei,length, const ALchar*,message)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alPushDebugGroupDirectEXT(ALCcontext *context, ALenum source,
|
||||||
|
ALuint id, ALsizei length, const ALchar *message) noexcept
|
||||||
|
try {
|
||||||
|
if(length < 0)
|
||||||
|
{
|
||||||
|
size_t newlen{std::strlen(message)};
|
||||||
|
if(newlen >= MaxDebugMessageLength)
|
||||||
|
throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%zu >= %d)", newlen,
|
||||||
|
MaxDebugMessageLength};
|
||||||
|
length = static_cast<ALsizei>(newlen);
|
||||||
|
}
|
||||||
|
else if(length >= MaxDebugMessageLength)
|
||||||
|
throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%d >= %d)", length,
|
||||||
|
MaxDebugMessageLength};
|
||||||
|
|
||||||
|
auto dsource = GetDebugSource(source);
|
||||||
|
if(!dsource)
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
|
||||||
|
if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source};
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> debuglock{context->mDebugCbLock};
|
||||||
|
if(context->mDebugGroups.size() >= MaxDebugGroupDepth)
|
||||||
|
throw al::context_error{AL_STACK_OVERFLOW_EXT, "Pushing too many debug groups"};
|
||||||
|
|
||||||
|
context->mDebugGroups.emplace_back(*dsource, id,
|
||||||
|
std::string_view{message, static_cast<uint>(length)});
|
||||||
|
auto &oldback = *(context->mDebugGroups.end()-2);
|
||||||
|
auto &newback = context->mDebugGroups.back();
|
||||||
|
|
||||||
|
newback.mFilters = oldback.mFilters;
|
||||||
|
newback.mIdFilters = oldback.mIdFilters;
|
||||||
|
|
||||||
|
if(context->mContextFlags.test(ContextFlags::DebugBit))
|
||||||
|
context->sendDebugMessage(debuglock, newback.mSource, DebugType::PushGroup, newback.mId,
|
||||||
|
DebugSeverity::Notification, newback.mMessage);
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCE_ALIGN DECL_FUNCEXT(void, alPopDebugGroup,EXT)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alPopDebugGroupDirectEXT(ALCcontext *context) noexcept
|
||||||
|
try {
|
||||||
|
std::unique_lock<std::mutex> debuglock{context->mDebugCbLock};
|
||||||
|
if(context->mDebugGroups.size() <= 1)
|
||||||
|
throw al::context_error{AL_STACK_UNDERFLOW_EXT,
|
||||||
|
"Attempting to pop the default debug group"};
|
||||||
|
|
||||||
|
DebugGroup &debug = context->mDebugGroups.back();
|
||||||
|
const auto source = debug.mSource;
|
||||||
|
const auto id = debug.mId;
|
||||||
|
std::string message{std::move(debug.mMessage)};
|
||||||
|
|
||||||
|
context->mDebugGroups.pop_back();
|
||||||
|
if(context->mContextFlags.test(ContextFlags::DebugBit))
|
||||||
|
context->sendDebugMessage(debuglock, source, DebugType::PopGroup, id,
|
||||||
|
DebugSeverity::Notification, message);
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FORCE_ALIGN DECL_FUNCEXT8(ALuint, alGetDebugMessageLog,EXT, ALuint,count, ALsizei,logBufSize, ALenum*,sources, ALenum*,types, ALuint*,ids, ALenum*,severities, ALsizei*,lengths, ALchar*,logBuf)
|
||||||
|
FORCE_ALIGN ALuint AL_APIENTRY alGetDebugMessageLogDirectEXT(ALCcontext *context, ALuint count,
|
||||||
|
ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities,
|
||||||
|
ALsizei *lengths, ALchar *logBuf) noexcept
|
||||||
|
try {
|
||||||
|
if(logBufSize < 0)
|
||||||
|
throw al::context_error{AL_INVALID_VALUE, "Negative debug log buffer size"};
|
||||||
|
|
||||||
|
auto sourcesOut = al::span{sources, sources ? count : 0u};
|
||||||
|
auto typesOut = al::span{types, types ? count : 0u};
|
||||||
|
auto idsOut = al::span{ids, ids ? count : 0u};
|
||||||
|
auto severitiesOut = al::span{severities, severities ? count : 0u};
|
||||||
|
auto lengthsOut = al::span{lengths, lengths ? count : 0u};
|
||||||
|
auto logOut = al::span{logBuf, logBuf ? static_cast<ALuint>(logBufSize) : 0u};
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
|
||||||
|
for(ALuint i{0};i < count;++i)
|
||||||
|
{
|
||||||
|
if(context->mDebugLog.empty())
|
||||||
|
return i;
|
||||||
|
|
||||||
|
auto &entry = context->mDebugLog.front();
|
||||||
|
const size_t tocopy{entry.mMessage.size() + 1};
|
||||||
|
if(logOut.data() != nullptr)
|
||||||
|
{
|
||||||
|
if(logOut.size() < tocopy)
|
||||||
|
return i;
|
||||||
|
auto oiter = std::copy(entry.mMessage.cbegin(), entry.mMessage.cend(), logOut.begin());
|
||||||
|
*oiter = '\0';
|
||||||
|
logOut = {oiter+1, logOut.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!sourcesOut.empty())
|
||||||
|
{
|
||||||
|
sourcesOut.front() = GetDebugSourceEnum(entry.mSource);
|
||||||
|
sourcesOut = sourcesOut.subspan<1>();
|
||||||
|
}
|
||||||
|
if(!typesOut.empty())
|
||||||
|
{
|
||||||
|
typesOut.front() = GetDebugTypeEnum(entry.mType);
|
||||||
|
typesOut = typesOut.subspan<1>();
|
||||||
|
}
|
||||||
|
if(!idsOut.empty())
|
||||||
|
{
|
||||||
|
idsOut.front() = entry.mId;
|
||||||
|
idsOut = idsOut.subspan<1>();
|
||||||
|
}
|
||||||
|
if(!severitiesOut.empty())
|
||||||
|
{
|
||||||
|
severitiesOut.front() = GetDebugSeverityEnum(entry.mSeverity);
|
||||||
|
severitiesOut = severitiesOut.subspan<1>();
|
||||||
|
}
|
||||||
|
if(!lengthsOut.empty())
|
||||||
|
{
|
||||||
|
lengthsOut.front() = static_cast<ALsizei>(tocopy);
|
||||||
|
lengthsOut = lengthsOut.subspan<1>();
|
||||||
|
}
|
||||||
|
|
||||||
|
context->mDebugLog.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCE_ALIGN DECL_FUNCEXT4(void, alObjectLabel,EXT, ALenum,identifier, ALuint,name, ALsizei,length, const ALchar*,label)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alObjectLabelDirectEXT(ALCcontext *context, ALenum identifier,
|
||||||
|
ALuint name, ALsizei length, const ALchar *label) noexcept
|
||||||
|
try {
|
||||||
|
if(!label && length != 0)
|
||||||
|
throw al::context_error{AL_INVALID_VALUE, "Null label pointer"};
|
||||||
|
|
||||||
|
auto objname = (length < 0) ? std::string_view{label}
|
||||||
|
: std::string_view{label, static_cast<uint>(length)};
|
||||||
|
if(objname.size() >= MaxObjectLabelLength)
|
||||||
|
throw al::context_error{AL_INVALID_VALUE, "Object label length too long (%zu >= %d)",
|
||||||
|
objname.size(), MaxObjectLabelLength};
|
||||||
|
|
||||||
|
switch(identifier)
|
||||||
|
{
|
||||||
|
case AL_SOURCE_EXT: ALsource::SetName(context, name, objname); return;
|
||||||
|
case AL_BUFFER: ALbuffer::SetName(context, name, objname); return;
|
||||||
|
case AL_FILTER_EXT: ALfilter::SetName(context, name, objname); return;
|
||||||
|
case AL_EFFECT_EXT: ALeffect::SetName(context, name, objname); return;
|
||||||
|
case AL_AUXILIARY_EFFECT_SLOT_EXT: ALeffectslot::SetName(context, name, objname); return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier};
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCE_ALIGN DECL_FUNCEXT5(void, alGetObjectLabel,EXT, ALenum,identifier, ALuint,name, ALsizei,bufSize, ALsizei*,length, ALchar*,label)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALenum identifier,
|
||||||
|
ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) noexcept
|
||||||
|
try {
|
||||||
|
if(bufSize < 0)
|
||||||
|
throw al::context_error{AL_INVALID_VALUE, "Negative label bufSize"};
|
||||||
|
|
||||||
|
if(!label && !length)
|
||||||
|
throw al::context_error{AL_INVALID_VALUE, "Null length and label"};
|
||||||
|
if(label && bufSize == 0)
|
||||||
|
throw al::context_error{AL_INVALID_VALUE, "Zero label bufSize"};
|
||||||
|
|
||||||
|
const auto labelOut = al::span{label, label ? static_cast<ALuint>(bufSize) : 0u};
|
||||||
|
auto copy_name = [name,length,labelOut](std::unordered_map<ALuint,std::string> &names)
|
||||||
|
{
|
||||||
|
std::string_view objname;
|
||||||
|
|
||||||
|
auto iter = names.find(name);
|
||||||
|
if(iter != names.end())
|
||||||
|
objname = iter->second;
|
||||||
|
|
||||||
|
if(labelOut.empty())
|
||||||
|
*length = static_cast<ALsizei>(objname.size());
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const size_t tocopy{std::min(objname.size(), labelOut.size()-1)};
|
||||||
|
auto oiter = std::copy_n(objname.cbegin(), tocopy, labelOut.begin());
|
||||||
|
*oiter = '\0';
|
||||||
|
if(length)
|
||||||
|
*length = static_cast<ALsizei>(tocopy);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if(identifier == AL_SOURCE_EXT)
|
||||||
|
{
|
||||||
|
std::lock_guard srclock{context->mSourceLock};
|
||||||
|
copy_name(context->mSourceNames);
|
||||||
|
}
|
||||||
|
else if(identifier == AL_BUFFER)
|
||||||
|
{
|
||||||
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
|
std::lock_guard buflock{device->BufferLock};
|
||||||
|
copy_name(device->mBufferNames);
|
||||||
|
}
|
||||||
|
else if(identifier == AL_FILTER_EXT)
|
||||||
|
{
|
||||||
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
|
std::lock_guard filterlock{device->FilterLock};
|
||||||
|
copy_name(device->mFilterNames);
|
||||||
|
}
|
||||||
|
else if(identifier == AL_EFFECT_EXT)
|
||||||
|
{
|
||||||
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
|
std::lock_guard effectlock{device->EffectLock};
|
||||||
|
copy_name(device->mEffectNames);
|
||||||
|
}
|
||||||
|
else if(identifier == AL_AUXILIARY_EFFECT_SLOT_EXT)
|
||||||
|
{
|
||||||
|
std::lock_guard slotlock{context->mEffectSlotLock};
|
||||||
|
copy_name(context->mEffectSlotNames);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier};
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
|
}
|
||||||
70
Engine/lib/openal-soft/al/debug.h
Normal file
70
Engine/lib/openal-soft/al/debug.h
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
#ifndef AL_DEBUG_H
|
||||||
|
#define AL_DEBUG_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using uint = unsigned int;
|
||||||
|
|
||||||
|
|
||||||
|
/* Somewhat arbitrary. Avoid letting it get out of control if the app enables
|
||||||
|
* logging but never reads it.
|
||||||
|
*/
|
||||||
|
inline constexpr std::uint8_t MaxDebugLoggedMessages{64};
|
||||||
|
inline constexpr std::uint16_t MaxDebugMessageLength{1024};
|
||||||
|
inline constexpr std::uint8_t MaxDebugGroupDepth{64};
|
||||||
|
inline constexpr std::uint16_t MaxObjectLabelLength{1024};
|
||||||
|
|
||||||
|
|
||||||
|
inline constexpr uint DebugSourceBase{0};
|
||||||
|
enum class DebugSource : std::uint8_t {
|
||||||
|
API = 0,
|
||||||
|
System,
|
||||||
|
ThirdParty,
|
||||||
|
Application,
|
||||||
|
Other,
|
||||||
|
};
|
||||||
|
inline constexpr uint DebugSourceCount{5};
|
||||||
|
|
||||||
|
inline constexpr uint DebugTypeBase{DebugSourceBase + DebugSourceCount};
|
||||||
|
enum class DebugType : std::uint8_t {
|
||||||
|
Error = 0,
|
||||||
|
DeprecatedBehavior,
|
||||||
|
UndefinedBehavior,
|
||||||
|
Portability,
|
||||||
|
Performance,
|
||||||
|
Marker,
|
||||||
|
PushGroup,
|
||||||
|
PopGroup,
|
||||||
|
Other,
|
||||||
|
};
|
||||||
|
inline constexpr uint DebugTypeCount{9};
|
||||||
|
|
||||||
|
inline constexpr uint DebugSeverityBase{DebugTypeBase + DebugTypeCount};
|
||||||
|
enum class DebugSeverity : std::uint8_t {
|
||||||
|
High = 0,
|
||||||
|
Medium,
|
||||||
|
Low,
|
||||||
|
Notification,
|
||||||
|
};
|
||||||
|
inline constexpr uint DebugSeverityCount{4};
|
||||||
|
|
||||||
|
struct DebugGroup {
|
||||||
|
const uint mId;
|
||||||
|
const DebugSource mSource;
|
||||||
|
std::string mMessage;
|
||||||
|
std::vector<uint> mFilters;
|
||||||
|
std::vector<std::uint64_t> mIdFilters;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
DebugGroup(DebugSource source, uint id, T&& message)
|
||||||
|
: mId{id}, mSource{source}, mMessage{std::forward<T>(message)}
|
||||||
|
{ }
|
||||||
|
DebugGroup(const DebugGroup&) = default;
|
||||||
|
DebugGroup(DebugGroup&&) = default;
|
||||||
|
~DebugGroup();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* AL_DEBUG_H */
|
||||||
127
Engine/lib/openal-soft/al/direct_defs.h
Normal file
127
Engine/lib/openal-soft/al/direct_defs.h
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
#ifndef AL_DIRECT_DEFS_H
|
||||||
|
#define AL_DIRECT_DEFS_H
|
||||||
|
|
||||||
|
namespace detail_ {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr T DefaultVal() noexcept { return T{}; }
|
||||||
|
|
||||||
|
template<>
|
||||||
|
constexpr void DefaultVal() noexcept { }
|
||||||
|
|
||||||
|
} // namespace detail_
|
||||||
|
|
||||||
|
#define DECL_FUNC(R, Name) \
|
||||||
|
auto AL_APIENTRY Name() noexcept -> R \
|
||||||
|
{ \
|
||||||
|
auto context = GetContextRef(); \
|
||||||
|
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||||
|
return Name##Direct(context.get()); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DECL_FUNC1(R, Name, T1,n1) \
|
||||||
|
auto AL_APIENTRY Name(T1 n1) noexcept -> R \
|
||||||
|
{ \
|
||||||
|
auto context = GetContextRef(); \
|
||||||
|
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||||
|
return Name##Direct(context.get(), n1); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DECL_FUNC2(R, Name, T1,n1, T2,n2) \
|
||||||
|
auto AL_APIENTRY Name(T1 n1, T2 n2) noexcept -> R \
|
||||||
|
{ \
|
||||||
|
auto context = GetContextRef(); \
|
||||||
|
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||||
|
return Name##Direct(context.get(), n1, n2); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DECL_FUNC3(R, Name, T1,n1, T2,n2, T3,n3) \
|
||||||
|
auto AL_APIENTRY Name(T1 n1, T2 n2, T3 n3) noexcept -> R \
|
||||||
|
{ \
|
||||||
|
auto context = GetContextRef(); \
|
||||||
|
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||||
|
return Name##Direct(context.get(), n1, n2, n3); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DECL_FUNC4(R, Name, T1,n1, T2,n2, T3,n3, T4,n4) \
|
||||||
|
auto AL_APIENTRY Name(T1 n1, T2 n2, T3 n3, T4 n4) noexcept -> R \
|
||||||
|
{ \
|
||||||
|
auto context = GetContextRef(); \
|
||||||
|
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||||
|
return Name##Direct(context.get(), n1, n2, n3, n4); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DECL_FUNC5(R, Name, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5) \
|
||||||
|
auto AL_APIENTRY Name(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5) noexcept -> R \
|
||||||
|
{ \
|
||||||
|
auto context = GetContextRef(); \
|
||||||
|
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||||
|
return Name##Direct(context.get(), n1, n2, n3, n4, n5); \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define DECL_FUNCEXT(R, Name,Ext) \
|
||||||
|
auto AL_APIENTRY Name##Ext() noexcept -> R \
|
||||||
|
{ \
|
||||||
|
auto context = GetContextRef(); \
|
||||||
|
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||||
|
return Name##Direct##Ext(context.get()); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DECL_FUNCEXT1(R, Name,Ext, T1,n1) \
|
||||||
|
auto AL_APIENTRY Name##Ext(T1 n1) noexcept -> R \
|
||||||
|
{ \
|
||||||
|
auto context = GetContextRef(); \
|
||||||
|
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||||
|
return Name##Direct##Ext(context.get(), n1); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DECL_FUNCEXT2(R, Name,Ext, T1,n1, T2,n2) \
|
||||||
|
auto AL_APIENTRY Name##Ext(T1 n1, T2 n2) noexcept -> R \
|
||||||
|
{ \
|
||||||
|
auto context = GetContextRef(); \
|
||||||
|
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||||
|
return Name##Direct##Ext(context.get(), n1, n2); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DECL_FUNCEXT3(R, Name,Ext, T1,n1, T2,n2, T3,n3) \
|
||||||
|
auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3) noexcept -> R \
|
||||||
|
{ \
|
||||||
|
auto context = GetContextRef(); \
|
||||||
|
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||||
|
return Name##Direct##Ext(context.get(), n1, n2, n3); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DECL_FUNCEXT4(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4) \
|
||||||
|
auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4) noexcept -> R \
|
||||||
|
{ \
|
||||||
|
auto context = GetContextRef(); \
|
||||||
|
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||||
|
return Name##Direct##Ext(context.get(), n1, n2, n3, n4); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DECL_FUNCEXT5(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5) \
|
||||||
|
auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5) noexcept -> R \
|
||||||
|
{ \
|
||||||
|
auto context = GetContextRef(); \
|
||||||
|
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||||
|
return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DECL_FUNCEXT6(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5, T6,n6) \
|
||||||
|
auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5, T6 n6) noexcept -> R \
|
||||||
|
{ \
|
||||||
|
auto context = GetContextRef(); \
|
||||||
|
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||||
|
return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5, n6); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DECL_FUNCEXT8(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5, T6,n6, T7,n7, T8,n8) \
|
||||||
|
auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5, T6 n6, T7 n7, T8 n8) noexcept -> R \
|
||||||
|
{ \
|
||||||
|
auto context = GetContextRef(); \
|
||||||
|
if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
|
||||||
|
return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5, n6, n7, n8); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* AL_DIRECT_DEFS_H */
|
||||||
|
|
@ -10,34 +10,39 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cfloat>
|
#include <cfloat>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <tuple>
|
||||||
#include <array>
|
#ifdef _WIN32
|
||||||
|
#include <guiddef.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
|
|
||||||
|
|
||||||
#ifndef GUID_DEFINED
|
#ifndef _WIN32
|
||||||
#define GUID_DEFINED
|
using GUID = struct _GUID { /* NOLINT(*-reserved-identifier) */
|
||||||
typedef struct _GUID {
|
|
||||||
std::uint32_t Data1;
|
std::uint32_t Data1;
|
||||||
std::uint16_t Data2;
|
std::uint16_t Data2;
|
||||||
std::uint16_t Data3;
|
std::uint16_t Data3;
|
||||||
std::uint8_t Data4[8];
|
std::array<std::uint8_t,8> Data4;
|
||||||
} GUID;
|
};
|
||||||
|
|
||||||
#ifndef _SYS_GUID_OPERATOR_EQ_
|
|
||||||
#define _SYS_GUID_OPERATOR_EQ_
|
|
||||||
inline bool operator==(const GUID& lhs, const GUID& rhs) noexcept
|
inline bool operator==(const GUID& lhs, const GUID& rhs) noexcept
|
||||||
{ return std::memcmp(&lhs, &rhs, sizeof(GUID)) == 0; }
|
{ return std::memcmp(&lhs, &rhs, sizeof(GUID)) == 0; }
|
||||||
|
|
||||||
inline bool operator!=(const GUID& lhs, const GUID& rhs) noexcept
|
inline bool operator!=(const GUID& lhs, const GUID& rhs) noexcept
|
||||||
{ return !(lhs == rhs); }
|
{ return !(lhs == rhs); }
|
||||||
#endif // _SYS_GUID_OPERATOR_EQ_
|
#endif // _WIN32
|
||||||
#endif // GUID_DEFINED
|
|
||||||
|
|
||||||
|
#define DECL_EQOP(T, ...) \
|
||||||
|
[[nodiscard]] auto get_members() const noexcept { return std::forward_as_tuple(__VA_ARGS__); } \
|
||||||
|
[[nodiscard]] friend bool operator==(const T &lhs, const T &rhs) noexcept \
|
||||||
|
{ return lhs.get_members() == rhs.get_members(); } \
|
||||||
|
[[nodiscard]] friend bool operator!=(const T &lhs, const T &rhs) noexcept \
|
||||||
|
{ return !(lhs == rhs); }
|
||||||
|
|
||||||
extern const GUID DSPROPSETID_EAX_ReverbProperties;
|
extern const GUID DSPROPSETID_EAX_ReverbProperties;
|
||||||
|
|
||||||
|
|
@ -276,11 +281,15 @@ 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
|
}; // EAXVECTOR
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
inline bool operator==(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept
|
inline bool operator==(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept
|
||||||
{ return std::memcmp(&lhs, &rhs, sizeof(EAXVECTOR)) == 0; }
|
{ return lhs.get_members() == rhs.get_members(); }
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
inline bool operator!=(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept
|
inline bool operator!=(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept
|
||||||
{ return !(lhs == rhs); }
|
{ return !(lhs == rhs); }
|
||||||
|
|
||||||
|
|
@ -361,6 +370,7 @@ constexpr auto EAXCONTEXT_MINMACROFXFACTOR = 0.0F;
|
||||||
constexpr auto EAXCONTEXT_MAXMACROFXFACTOR = 1.0F;
|
constexpr auto EAXCONTEXT_MAXMACROFXFACTOR = 1.0F;
|
||||||
constexpr auto EAXCONTEXT_DEFAULTMACROFXFACTOR = 0.0F;
|
constexpr auto EAXCONTEXT_DEFAULTMACROFXFACTOR = 0.0F;
|
||||||
|
|
||||||
|
constexpr auto EAXCONTEXT_DEFAULTLASTERROR = EAX_OK;
|
||||||
|
|
||||||
extern const GUID EAXPROPERTYID_EAX40_FXSlot0;
|
extern const GUID EAXPROPERTYID_EAX40_FXSlot0;
|
||||||
extern const GUID EAXPROPERTYID_EAX50_FXSlot0;
|
extern const GUID EAXPROPERTYID_EAX50_FXSlot0;
|
||||||
|
|
@ -613,7 +623,7 @@ struct EAX30SOURCEPROPERTIES {
|
||||||
float flOcclusionLFRatio; // occlusion low-frequency level re. main control
|
float flOcclusionLFRatio; // occlusion low-frequency level re. main control
|
||||||
float flOcclusionRoomRatio; // relative occlusion control for room effect
|
float flOcclusionRoomRatio; // relative occlusion control for room effect
|
||||||
float flOcclusionDirectRatio; // relative occlusion control for direct path
|
float flOcclusionDirectRatio; // relative occlusion control for direct path
|
||||||
long lExclusion; // main exlusion control (attenuation at high frequencies)
|
long lExclusion; // main exclusion control (attenuation at high frequencies)
|
||||||
float flExclusionLFRatio; // exclusion low-frequency level re. main control
|
float flExclusionLFRatio; // exclusion low-frequency level re. main control
|
||||||
long lOutsideVolumeHF; // outside sound cone level at high frequencies
|
long lOutsideVolumeHF; // outside sound cone level at high frequencies
|
||||||
float flDopplerFactor; // like DS3D flDopplerFactor but per source
|
float flDopplerFactor; // like DS3D flDopplerFactor but per source
|
||||||
|
|
@ -653,11 +663,11 @@ struct EAXSPEAKERLEVELPROPERTIES {
|
||||||
}; // EAXSPEAKERLEVELPROPERTIES
|
}; // EAXSPEAKERLEVELPROPERTIES
|
||||||
|
|
||||||
struct EAX40ACTIVEFXSLOTS {
|
struct EAX40ACTIVEFXSLOTS {
|
||||||
GUID guidActiveFXSlots[EAX40_MAX_ACTIVE_FXSLOTS];
|
std::array<GUID,EAX40_MAX_ACTIVE_FXSLOTS> guidActiveFXSlots;
|
||||||
}; // EAX40ACTIVEFXSLOTS
|
}; // EAX40ACTIVEFXSLOTS
|
||||||
|
|
||||||
struct EAX50ACTIVEFXSLOTS {
|
struct EAX50ACTIVEFXSLOTS {
|
||||||
GUID guidActiveFXSlots[EAX50_MAX_ACTIVE_FXSLOTS];
|
std::array<GUID,EAX50_MAX_ACTIVE_FXSLOTS> guidActiveFXSlots;
|
||||||
}; // EAX50ACTIVEFXSLOTS
|
}; // EAX50ACTIVEFXSLOTS
|
||||||
|
|
||||||
// Use this structure for EAXSOURCE_OBSTRUCTIONPARAMETERS property.
|
// Use this structure for EAXSOURCE_OBSTRUCTIONPARAMETERS property.
|
||||||
|
|
@ -836,6 +846,11 @@ struct EAXREVERBPROPERTIES {
|
||||||
float flLFReference; // reference low frequency
|
float flLFReference; // reference low frequency
|
||||||
float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect
|
float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect
|
||||||
unsigned long ulFlags; // modifies the behavior of properties
|
unsigned long ulFlags; // modifies the behavior of properties
|
||||||
|
DECL_EQOP(EAXREVERBPROPERTIES, ulEnvironment, flEnvironmentSize, flEnvironmentDiffusion, lRoom,
|
||||||
|
lRoomHF, lRoomLF, flDecayTime, flDecayHFRatio, flDecayLFRatio, lReflections,
|
||||||
|
flReflectionsDelay, vReflectionsPan, lReverb, flReverbDelay, vReverbPan, flEchoTime,
|
||||||
|
flEchoDepth, flModulationTime, flModulationDepth, flAirAbsorptionHF, flHFReference,
|
||||||
|
flLFReference, flRoomRolloffFactor, ulFlags)
|
||||||
}; // EAXREVERBPROPERTIES
|
}; // EAXREVERBPROPERTIES
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -965,6 +980,7 @@ enum EAXAGCCOMPRESSOR_PROPERTY : unsigned int {
|
||||||
|
|
||||||
struct EAXAGCCOMPRESSORPROPERTIES {
|
struct EAXAGCCOMPRESSORPROPERTIES {
|
||||||
unsigned long ulOnOff; // Switch Compressor on or off
|
unsigned long ulOnOff; // Switch Compressor on or off
|
||||||
|
DECL_EQOP(EAXAGCCOMPRESSORPROPERTIES, ulOnOff)
|
||||||
}; // EAXAGCCOMPRESSORPROPERTIES
|
}; // EAXAGCCOMPRESSORPROPERTIES
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -991,6 +1007,7 @@ struct EAXAUTOWAHPROPERTIES {
|
||||||
float flReleaseTime; // Release time (seconds)
|
float flReleaseTime; // Release time (seconds)
|
||||||
long lResonance; // Resonance (mB)
|
long lResonance; // Resonance (mB)
|
||||||
long lPeakLevel; // Peak level (mB)
|
long lPeakLevel; // Peak level (mB)
|
||||||
|
DECL_EQOP(EAXAUTOWAHPROPERTIES, flAttackTime, flReleaseTime, lResonance, lPeakLevel)
|
||||||
}; // EAXAUTOWAHPROPERTIES
|
}; // EAXAUTOWAHPROPERTIES
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1038,6 +1055,7 @@ struct EAXCHORUSPROPERTIES {
|
||||||
float flDepth; // Depth (0 to 1)
|
float flDepth; // Depth (0 to 1)
|
||||||
float flFeedback; // Feedback (-1 to 1)
|
float flFeedback; // Feedback (-1 to 1)
|
||||||
float flDelay; // Delay (seconds)
|
float flDelay; // Delay (seconds)
|
||||||
|
DECL_EQOP(EAXCHORUSPROPERTIES, ulWaveform, lPhase, flRate, flDepth, flFeedback, flDelay)
|
||||||
}; // EAXCHORUSPROPERTIES
|
}; // EAXCHORUSPROPERTIES
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1086,6 +1104,7 @@ struct EAXDISTORTIONPROPERTIES {
|
||||||
float flLowPassCutOff; // Controls the cut-off of the filter pre-distortion (Hz)
|
float flLowPassCutOff; // Controls the cut-off of the filter pre-distortion (Hz)
|
||||||
float flEQCenter; // Controls the center frequency of the EQ post-distortion (Hz)
|
float flEQCenter; // Controls the center frequency of the EQ post-distortion (Hz)
|
||||||
float flEQBandwidth; // Controls the bandwidth of the EQ post-distortion (Hz)
|
float flEQBandwidth; // Controls the bandwidth of the EQ post-distortion (Hz)
|
||||||
|
DECL_EQOP(EAXDISTORTIONPROPERTIES, flEdge, lGain, flLowPassCutOff, flEQCenter, flEQBandwidth)
|
||||||
}; // EAXDISTORTIONPROPERTIES
|
}; // EAXDISTORTIONPROPERTIES
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1130,6 +1149,7 @@ struct EAXECHOPROPERTIES {
|
||||||
float flDamping; // Controls a low-pass filter that dampens the echoes (0 to 1)
|
float flDamping; // Controls a low-pass filter that dampens the echoes (0 to 1)
|
||||||
float flFeedback; // Controls the duration of echo repetition (0 to 1)
|
float flFeedback; // Controls the duration of echo repetition (0 to 1)
|
||||||
float flSpread; // Controls the left-right spread of the echoes
|
float flSpread; // Controls the left-right spread of the echoes
|
||||||
|
DECL_EQOP(EAXECHOPROPERTIES, flDelay, flLRDelay, flDamping, flFeedback, flSpread)
|
||||||
}; // EAXECHOPROPERTIES
|
}; // EAXECHOPROPERTIES
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1184,6 +1204,8 @@ struct EAXEQUALIZERPROPERTIES {
|
||||||
float flMid2Width; // (octaves)
|
float flMid2Width; // (octaves)
|
||||||
long lHighGain; // (mB)
|
long lHighGain; // (mB)
|
||||||
float flHighCutOff; // (Hz)
|
float flHighCutOff; // (Hz)
|
||||||
|
DECL_EQOP(EAXEQUALIZERPROPERTIES, lLowGain, flLowCutOff, lMid1Gain, flMid1Center, flMid1Width,
|
||||||
|
lMid2Gain, flMid2Center, flMid2Width, lHighGain, flHighCutOff)
|
||||||
}; // EAXEQUALIZERPROPERTIES
|
}; // EAXEQUALIZERPROPERTIES
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1255,6 +1277,7 @@ struct EAXFLANGERPROPERTIES {
|
||||||
float flDepth; // Depth (0 to 1)
|
float flDepth; // Depth (0 to 1)
|
||||||
float flFeedback; // Feedback (0 to 1)
|
float flFeedback; // Feedback (0 to 1)
|
||||||
float flDelay; // Delay (seconds)
|
float flDelay; // Delay (seconds)
|
||||||
|
DECL_EQOP(EAXFLANGERPROPERTIES, ulWaveform, lPhase, flRate, flDepth, flFeedback, flDelay)
|
||||||
}; // EAXFLANGERPROPERTIES
|
}; // EAXFLANGERPROPERTIES
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1305,6 +1328,7 @@ struct EAXFREQUENCYSHIFTERPROPERTIES {
|
||||||
float flFrequency; // (Hz)
|
float flFrequency; // (Hz)
|
||||||
unsigned long ulLeftDirection; // see enum above
|
unsigned long ulLeftDirection; // see enum above
|
||||||
unsigned long ulRightDirection; // see enum above
|
unsigned long ulRightDirection; // see enum above
|
||||||
|
DECL_EQOP(EAXFREQUENCYSHIFTERPROPERTIES, flFrequency, ulLeftDirection, ulRightDirection)
|
||||||
}; // EAXFREQUENCYSHIFTERPROPERTIES
|
}; // EAXFREQUENCYSHIFTERPROPERTIES
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1383,6 +1407,8 @@ struct EAXVOCALMORPHERPROPERTIES {
|
||||||
long lPhonemeBCoarseTuning; // (semitones)
|
long lPhonemeBCoarseTuning; // (semitones)
|
||||||
unsigned long ulWaveform; // Waveform selector - see enum above
|
unsigned long ulWaveform; // Waveform selector - see enum above
|
||||||
float flRate; // (Hz)
|
float flRate; // (Hz)
|
||||||
|
DECL_EQOP(EAXVOCALMORPHERPROPERTIES, ulPhonemeA, lPhonemeACoarseTuning, ulPhonemeB,
|
||||||
|
lPhonemeBCoarseTuning, ulWaveform, flRate)
|
||||||
}; // EAXVOCALMORPHERPROPERTIES
|
}; // EAXVOCALMORPHERPROPERTIES
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1425,6 +1451,7 @@ enum EAXPITCHSHIFTER_PROPERTY : unsigned int {
|
||||||
struct EAXPITCHSHIFTERPROPERTIES {
|
struct EAXPITCHSHIFTERPROPERTIES {
|
||||||
long lCoarseTune; // Amount of pitch shift (semitones)
|
long lCoarseTune; // Amount of pitch shift (semitones)
|
||||||
long lFineTune; // Amount of pitch shift (cents)
|
long lFineTune; // Amount of pitch shift (cents)
|
||||||
|
DECL_EQOP(EAXPITCHSHIFTERPROPERTIES, lCoarseTune, lFineTune)
|
||||||
}; // EAXPITCHSHIFTERPROPERTIES
|
}; // EAXPITCHSHIFTERPROPERTIES
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1460,6 +1487,7 @@ struct EAXRINGMODULATORPROPERTIES {
|
||||||
float flFrequency; // Frequency of modulation (Hz)
|
float flFrequency; // Frequency of modulation (Hz)
|
||||||
float flHighPassCutOff; // Cut-off frequency of high-pass filter (Hz)
|
float flHighPassCutOff; // Cut-off frequency of high-pass filter (Hz)
|
||||||
unsigned long ulWaveform; // Waveform selector - see enum above
|
unsigned long ulWaveform; // Waveform selector - see enum above
|
||||||
|
DECL_EQOP(EAXRINGMODULATORPROPERTIES, flFrequency, flHighPassCutOff, ulWaveform)
|
||||||
}; // EAXRINGMODULATORPROPERTIES
|
}; // EAXRINGMODULATORPROPERTIES
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1490,4 +1518,5 @@ using LPEAXGET = ALenum(AL_APIENTRY*)(
|
||||||
ALvoid* property_buffer,
|
ALvoid* property_buffer,
|
||||||
ALuint property_size);
|
ALuint property_size);
|
||||||
|
|
||||||
|
#undef DECL_EQOP
|
||||||
#endif // !EAX_API_INCLUDED
|
#endif // !EAX_API_INCLUDED
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,7 @@ EaxCall::EaxCall(
|
||||||
ALuint property_source_id,
|
ALuint property_source_id,
|
||||||
ALvoid* property_buffer,
|
ALvoid* property_buffer,
|
||||||
ALuint property_size)
|
ALuint property_size)
|
||||||
: mCallType{type}, mVersion{0}, mPropertySetId{EaxCallPropertySetId::none}
|
: mCallType{type}, mIsDeferred{(property_id & deferred_flag) != 0}
|
||||||
, 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}
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -31,16 +31,16 @@ public:
|
||||||
ALvoid* property_buffer,
|
ALvoid* property_buffer,
|
||||||
ALuint property_size);
|
ALuint property_size);
|
||||||
|
|
||||||
bool is_get() const noexcept { return mCallType == EaxCallType::get; }
|
[[nodiscard]] auto is_get() const noexcept -> bool { return mCallType == EaxCallType::get; }
|
||||||
bool is_deferred() const noexcept { return mIsDeferred; }
|
[[nodiscard]] auto is_deferred() const noexcept -> bool { return mIsDeferred; }
|
||||||
int get_version() const noexcept { return mVersion; }
|
[[nodiscard]] auto get_version() const noexcept -> int { return mVersion; }
|
||||||
EaxCallPropertySetId get_property_set_id() const noexcept { return mPropertySetId; }
|
[[nodiscard]] auto get_property_set_id() const noexcept -> EaxCallPropertySetId { return mPropertySetId; }
|
||||||
ALuint get_property_id() const noexcept { return mPropertyId; }
|
[[nodiscard]] auto get_property_id() const noexcept -> ALuint { return mPropertyId; }
|
||||||
ALuint get_property_al_name() const noexcept { return mPropertySourceId; }
|
[[nodiscard]] auto get_property_al_name() const noexcept -> ALuint { return mPropertySourceId; }
|
||||||
EaxFxSlotIndex get_fx_slot_index() const noexcept { return mFxSlotIndex; }
|
[[nodiscard]] auto get_fx_slot_index() const noexcept -> EaxFxSlotIndex { return mFxSlotIndex; }
|
||||||
|
|
||||||
template<typename TException, typename TValue>
|
template<typename TException, typename TValue>
|
||||||
TValue& get_value() const
|
[[nodiscard]] auto get_value() const -> TValue&
|
||||||
{
|
{
|
||||||
if(mPropertyBufferSize < sizeof(TValue))
|
if(mPropertyBufferSize < sizeof(TValue))
|
||||||
fail_too_small();
|
fail_too_small();
|
||||||
|
|
@ -49,32 +49,32 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TValue>
|
template<typename TValue>
|
||||||
al::span<TValue> get_values(size_t max_count) const
|
[[nodiscard]] auto get_values(size_t max_count) const -> al::span<TValue>
|
||||||
{
|
{
|
||||||
if(max_count == 0 || mPropertyBufferSize < sizeof(TValue))
|
if(max_count == 0 || mPropertyBufferSize < sizeof(TValue))
|
||||||
fail_too_small();
|
fail_too_small();
|
||||||
|
|
||||||
const auto count = minz(mPropertyBufferSize / sizeof(TValue), max_count);
|
const auto count = std::min(mPropertyBufferSize/sizeof(TValue), max_count);
|
||||||
return al::as_span(static_cast<TValue*>(mPropertyBuffer), count);
|
return {static_cast<TValue*>(mPropertyBuffer), count};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TValue>
|
template<typename TValue>
|
||||||
al::span<TValue> get_values() const
|
[[nodiscard]] auto get_values() const -> al::span<TValue>
|
||||||
{
|
{
|
||||||
return get_values<TValue>(~size_t{});
|
return get_values<TValue>(~0_uz);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TException, typename TValue>
|
template<typename TException, typename TValue>
|
||||||
void set_value(const TValue& value) const
|
auto set_value(const TValue& value) const -> void
|
||||||
{
|
{
|
||||||
get_value<TException, TValue>() = value;
|
get_value<TException, TValue>() = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const EaxCallType mCallType;
|
const EaxCallType mCallType;
|
||||||
int mVersion;
|
int mVersion{};
|
||||||
EaxFxSlotIndex mFxSlotIndex;
|
EaxFxSlotIndex mFxSlotIndex{};
|
||||||
EaxCallPropertySetId mPropertySetId;
|
EaxCallPropertySetId mPropertySetId{EaxCallPropertySetId::none};
|
||||||
bool mIsDeferred;
|
bool mIsDeferred;
|
||||||
|
|
||||||
const ALuint mPropertyId;
|
const ALuint mPropertyId;
|
||||||
|
|
|
||||||
|
|
@ -4,60 +4,56 @@
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
|
#include "AL/alext.h"
|
||||||
#include "core/effects/base.h"
|
#include "core/effects/base.h"
|
||||||
#include "call.h"
|
#include "call.h"
|
||||||
|
|
||||||
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."; }
|
||||||
}; // EaxEffectErrorMessages
|
}; // EaxEffectErrorMessages
|
||||||
|
|
||||||
/* TODO: Use std::variant (C++17). */
|
using EaxEffectProps = std::variant<std::monostate,
|
||||||
enum class EaxEffectType {
|
EAXREVERBPROPERTIES,
|
||||||
None, Reverb, Chorus, Autowah, Compressor, Distortion, Echo, Equalizer, Flanger,
|
EAXCHORUSPROPERTIES,
|
||||||
FrequencyShifter, Modulator, PitchShifter, VocalMorpher
|
EAXAUTOWAHPROPERTIES,
|
||||||
};
|
EAXAGCCOMPRESSORPROPERTIES,
|
||||||
struct EaxEffectProps {
|
EAXDISTORTIONPROPERTIES,
|
||||||
EaxEffectType mType;
|
EAXECHOPROPERTIES,
|
||||||
union {
|
EAXEQUALIZERPROPERTIES,
|
||||||
EAXREVERBPROPERTIES mReverb;
|
EAXFLANGERPROPERTIES,
|
||||||
EAXCHORUSPROPERTIES mChorus;
|
EAXFREQUENCYSHIFTERPROPERTIES,
|
||||||
EAXAUTOWAHPROPERTIES mAutowah;
|
EAXRINGMODULATORPROPERTIES,
|
||||||
EAXAGCCOMPRESSORPROPERTIES mCompressor;
|
EAXPITCHSHIFTERPROPERTIES,
|
||||||
EAXDISTORTIONPROPERTIES mDistortion;
|
EAXVOCALMORPHERPROPERTIES>;
|
||||||
EAXECHOPROPERTIES mEcho;
|
|
||||||
EAXEQUALIZERPROPERTIES mEqualizer;
|
template<typename... Ts>
|
||||||
EAXFLANGERPROPERTIES mFlanger;
|
struct overloaded : Ts... { using Ts::operator()...; };
|
||||||
EAXFREQUENCYSHIFTERPROPERTIES mFrequencyShifter;
|
|
||||||
EAXRINGMODULATORPROPERTIES mModulator;
|
template<typename... Ts>
|
||||||
EAXPITCHSHIFTERPROPERTIES mPitchShifter;
|
overloaded(Ts...) -> overloaded<Ts...>;
|
||||||
EAXVOCALMORPHERPROPERTIES mVocalMorpher;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr ALenum EnumFromEaxEffectType(const EaxEffectProps &props)
|
constexpr ALenum EnumFromEaxEffectType(const EaxEffectProps &props)
|
||||||
{
|
{
|
||||||
switch(props.mType)
|
return std::visit(overloaded{
|
||||||
{
|
[](const std::monostate&) noexcept { return AL_EFFECT_NULL; },
|
||||||
case EaxEffectType::None: break;
|
[](const EAXREVERBPROPERTIES&) noexcept { return AL_EFFECT_EAXREVERB; },
|
||||||
case EaxEffectType::Reverb: return AL_EFFECT_EAXREVERB;
|
[](const EAXCHORUSPROPERTIES&) noexcept { return AL_EFFECT_CHORUS; },
|
||||||
case EaxEffectType::Chorus: return AL_EFFECT_CHORUS;
|
[](const EAXAUTOWAHPROPERTIES&) noexcept { return AL_EFFECT_AUTOWAH; },
|
||||||
case EaxEffectType::Autowah: return AL_EFFECT_AUTOWAH;
|
[](const EAXAGCCOMPRESSORPROPERTIES&) noexcept { return AL_EFFECT_COMPRESSOR; },
|
||||||
case EaxEffectType::Compressor: return AL_EFFECT_COMPRESSOR;
|
[](const EAXDISTORTIONPROPERTIES&) noexcept { return AL_EFFECT_DISTORTION; },
|
||||||
case EaxEffectType::Distortion: return AL_EFFECT_DISTORTION;
|
[](const EAXECHOPROPERTIES&) noexcept { return AL_EFFECT_ECHO; },
|
||||||
case EaxEffectType::Echo: return AL_EFFECT_ECHO;
|
[](const EAXEQUALIZERPROPERTIES&) noexcept { return AL_EFFECT_EQUALIZER; },
|
||||||
case EaxEffectType::Equalizer: return AL_EFFECT_EQUALIZER;
|
[](const EAXFLANGERPROPERTIES&) noexcept { return AL_EFFECT_FLANGER; },
|
||||||
case EaxEffectType::Flanger: return AL_EFFECT_FLANGER;
|
[](const EAXFREQUENCYSHIFTERPROPERTIES&) noexcept { return AL_EFFECT_FREQUENCY_SHIFTER; },
|
||||||
case EaxEffectType::FrequencyShifter: return AL_EFFECT_FREQUENCY_SHIFTER;
|
[](const EAXRINGMODULATORPROPERTIES&) noexcept { return AL_EFFECT_RING_MODULATOR; },
|
||||||
case EaxEffectType::Modulator: return AL_EFFECT_RING_MODULATOR;
|
[](const EAXPITCHSHIFTERPROPERTIES&) noexcept { return AL_EFFECT_PITCH_SHIFTER; },
|
||||||
case EaxEffectType::PitchShifter: return AL_EFFECT_PITCH_SHIFTER;
|
[](const EAXVOCALMORPHERPROPERTIES&) noexcept { return AL_EFFECT_VOCAL_MORPHER; }
|
||||||
case EaxEffectType::VocalMorpher: return AL_EFFECT_VOCAL_MORPHER;
|
}, props);
|
||||||
}
|
|
||||||
return AL_EFFECT_NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EaxReverbCommitter {
|
struct EaxReverbCommitter {
|
||||||
|
|
@ -105,7 +101,6 @@ struct EaxReverbCommitter {
|
||||||
bool commit(const EAX_REVERBPROPERTIES &props);
|
bool commit(const EAX_REVERBPROPERTIES &props);
|
||||||
bool commit(const EAX20LISTENERPROPERTIES &props);
|
bool commit(const EAX20LISTENERPROPERTIES &props);
|
||||||
bool commit(const EAXREVERBPROPERTIES &props);
|
bool commit(const EAXREVERBPROPERTIES &props);
|
||||||
bool commit(const EaxEffectProps &props);
|
|
||||||
|
|
||||||
static void SetDefaults(EAX_REVERBPROPERTIES &props);
|
static void SetDefaults(EAX_REVERBPROPERTIES &props);
|
||||||
static void SetDefaults(EAX20LISTENERPROPERTIES &props);
|
static void SetDefaults(EAX20LISTENERPROPERTIES &props);
|
||||||
|
|
@ -115,16 +110,13 @@ struct EaxReverbCommitter {
|
||||||
static void Get(const EaxCall &call, const EAX_REVERBPROPERTIES &props);
|
static void Get(const EaxCall &call, const EAX_REVERBPROPERTIES &props);
|
||||||
static void Get(const EaxCall &call, const EAX20LISTENERPROPERTIES &props);
|
static void Get(const EaxCall &call, const EAX20LISTENERPROPERTIES &props);
|
||||||
static void Get(const EaxCall &call, const EAXREVERBPROPERTIES &props);
|
static void Get(const EaxCall &call, const EAXREVERBPROPERTIES &props);
|
||||||
static void Get(const EaxCall &call, const EaxEffectProps &props);
|
|
||||||
|
|
||||||
static void Set(const EaxCall &call, EAX_REVERBPROPERTIES &props);
|
static void Set(const EaxCall &call, EAX_REVERBPROPERTIES &props);
|
||||||
static void Set(const EaxCall &call, EAX20LISTENERPROPERTIES &props);
|
static void Set(const EaxCall &call, EAX20LISTENERPROPERTIES &props);
|
||||||
static void Set(const EaxCall &call, EAXREVERBPROPERTIES &props);
|
static void Set(const EaxCall &call, EAXREVERBPROPERTIES &props);
|
||||||
static void Set(const EaxCall &call, EaxEffectProps &props);
|
|
||||||
|
|
||||||
static void translate(const EAX_REVERBPROPERTIES& src, EaxEffectProps& dst) noexcept;
|
static void translate(const EAX_REVERBPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept;
|
||||||
static void translate(const EAX20LISTENERPROPERTIES& src, EaxEffectProps& dst) noexcept;
|
static void translate(const EAX20LISTENERPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept;
|
||||||
static void translate(const EAXREVERBPROPERTIES& src, EaxEffectProps& dst) noexcept;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|
@ -149,51 +141,137 @@ 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()); }
|
||||||
|
|
||||||
bool commit(const EaxEffectProps &props);
|
|
||||||
|
|
||||||
static void SetDefaults(EaxEffectProps &props);
|
|
||||||
static void Get(const EaxCall &call, const EaxEffectProps &props);
|
|
||||||
static void Set(const EaxCall &call, EaxEffectProps &props);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EaxAutowahCommitter : public EaxCommitter<EaxAutowahCommitter> {
|
struct EaxAutowahCommitter : public EaxCommitter<EaxAutowahCommitter> {
|
||||||
using EaxCommitter<EaxAutowahCommitter>::EaxCommitter;
|
using EaxCommitter<EaxAutowahCommitter>::EaxCommitter;
|
||||||
|
|
||||||
|
bool commit(const EAXAUTOWAHPROPERTIES &props);
|
||||||
|
|
||||||
|
static void SetDefaults(EaxEffectProps &props);
|
||||||
|
static void Get(const EaxCall &call, const EAXAUTOWAHPROPERTIES &props);
|
||||||
|
static void Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props);
|
||||||
};
|
};
|
||||||
struct EaxChorusCommitter : public EaxCommitter<EaxChorusCommitter> {
|
struct EaxChorusCommitter : public EaxCommitter<EaxChorusCommitter> {
|
||||||
using EaxCommitter<EaxChorusCommitter>::EaxCommitter;
|
using EaxCommitter<EaxChorusCommitter>::EaxCommitter;
|
||||||
|
|
||||||
|
bool commit(const EAXCHORUSPROPERTIES &props);
|
||||||
|
|
||||||
|
static void SetDefaults(EaxEffectProps &props);
|
||||||
|
static void Get(const EaxCall &call, const EAXCHORUSPROPERTIES &props);
|
||||||
|
static void Set(const EaxCall &call, EAXCHORUSPROPERTIES &props);
|
||||||
};
|
};
|
||||||
struct EaxCompressorCommitter : public EaxCommitter<EaxCompressorCommitter> {
|
struct EaxCompressorCommitter : public EaxCommitter<EaxCompressorCommitter> {
|
||||||
using EaxCommitter<EaxCompressorCommitter>::EaxCommitter;
|
using EaxCommitter<EaxCompressorCommitter>::EaxCommitter;
|
||||||
|
|
||||||
|
bool commit(const EAXAGCCOMPRESSORPROPERTIES &props);
|
||||||
|
|
||||||
|
static void SetDefaults(EaxEffectProps &props);
|
||||||
|
static void Get(const EaxCall &call, const EAXAGCCOMPRESSORPROPERTIES &props);
|
||||||
|
static void Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props);
|
||||||
};
|
};
|
||||||
struct EaxDistortionCommitter : public EaxCommitter<EaxDistortionCommitter> {
|
struct EaxDistortionCommitter : public EaxCommitter<EaxDistortionCommitter> {
|
||||||
using EaxCommitter<EaxDistortionCommitter>::EaxCommitter;
|
using EaxCommitter<EaxDistortionCommitter>::EaxCommitter;
|
||||||
|
|
||||||
|
bool commit(const EAXDISTORTIONPROPERTIES &props);
|
||||||
|
|
||||||
|
static void SetDefaults(EaxEffectProps &props);
|
||||||
|
static void Get(const EaxCall &call, const EAXDISTORTIONPROPERTIES &props);
|
||||||
|
static void Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props);
|
||||||
};
|
};
|
||||||
struct EaxEchoCommitter : public EaxCommitter<EaxEchoCommitter> {
|
struct EaxEchoCommitter : public EaxCommitter<EaxEchoCommitter> {
|
||||||
using EaxCommitter<EaxEchoCommitter>::EaxCommitter;
|
using EaxCommitter<EaxEchoCommitter>::EaxCommitter;
|
||||||
|
|
||||||
|
bool commit(const EAXECHOPROPERTIES &props);
|
||||||
|
|
||||||
|
static void SetDefaults(EaxEffectProps &props);
|
||||||
|
static void Get(const EaxCall &call, const EAXECHOPROPERTIES &props);
|
||||||
|
static void Set(const EaxCall &call, EAXECHOPROPERTIES &props);
|
||||||
};
|
};
|
||||||
struct EaxEqualizerCommitter : public EaxCommitter<EaxEqualizerCommitter> {
|
struct EaxEqualizerCommitter : public EaxCommitter<EaxEqualizerCommitter> {
|
||||||
using EaxCommitter<EaxEqualizerCommitter>::EaxCommitter;
|
using EaxCommitter<EaxEqualizerCommitter>::EaxCommitter;
|
||||||
|
|
||||||
|
bool commit(const EAXEQUALIZERPROPERTIES &props);
|
||||||
|
|
||||||
|
static void SetDefaults(EaxEffectProps &props);
|
||||||
|
static void Get(const EaxCall &call, const EAXEQUALIZERPROPERTIES &props);
|
||||||
|
static void Set(const EaxCall &call, EAXEQUALIZERPROPERTIES &props);
|
||||||
};
|
};
|
||||||
struct EaxFlangerCommitter : public EaxCommitter<EaxFlangerCommitter> {
|
struct EaxFlangerCommitter : public EaxCommitter<EaxFlangerCommitter> {
|
||||||
using EaxCommitter<EaxFlangerCommitter>::EaxCommitter;
|
using EaxCommitter<EaxFlangerCommitter>::EaxCommitter;
|
||||||
|
|
||||||
|
bool commit(const EAXFLANGERPROPERTIES &props);
|
||||||
|
|
||||||
|
static void SetDefaults(EaxEffectProps &props);
|
||||||
|
static void Get(const EaxCall &call, const EAXFLANGERPROPERTIES &props);
|
||||||
|
static void Set(const EaxCall &call, EAXFLANGERPROPERTIES &props);
|
||||||
};
|
};
|
||||||
struct EaxFrequencyShifterCommitter : public EaxCommitter<EaxFrequencyShifterCommitter> {
|
struct EaxFrequencyShifterCommitter : public EaxCommitter<EaxFrequencyShifterCommitter> {
|
||||||
using EaxCommitter<EaxFrequencyShifterCommitter>::EaxCommitter;
|
using EaxCommitter<EaxFrequencyShifterCommitter>::EaxCommitter;
|
||||||
|
|
||||||
|
bool commit(const EAXFREQUENCYSHIFTERPROPERTIES &props);
|
||||||
|
|
||||||
|
static void SetDefaults(EaxEffectProps &props);
|
||||||
|
static void Get(const EaxCall &call, const EAXFREQUENCYSHIFTERPROPERTIES &props);
|
||||||
|
static void Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props);
|
||||||
};
|
};
|
||||||
struct EaxModulatorCommitter : public EaxCommitter<EaxModulatorCommitter> {
|
struct EaxModulatorCommitter : public EaxCommitter<EaxModulatorCommitter> {
|
||||||
using EaxCommitter<EaxModulatorCommitter>::EaxCommitter;
|
using EaxCommitter<EaxModulatorCommitter>::EaxCommitter;
|
||||||
|
|
||||||
|
bool commit(const EAXRINGMODULATORPROPERTIES &props);
|
||||||
|
|
||||||
|
static void SetDefaults(EaxEffectProps &props);
|
||||||
|
static void Get(const EaxCall &call, const EAXRINGMODULATORPROPERTIES &props);
|
||||||
|
static void Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props);
|
||||||
};
|
};
|
||||||
struct EaxPitchShifterCommitter : public EaxCommitter<EaxPitchShifterCommitter> {
|
struct EaxPitchShifterCommitter : public EaxCommitter<EaxPitchShifterCommitter> {
|
||||||
using EaxCommitter<EaxPitchShifterCommitter>::EaxCommitter;
|
using EaxCommitter<EaxPitchShifterCommitter>::EaxCommitter;
|
||||||
|
|
||||||
|
bool commit(const EAXPITCHSHIFTERPROPERTIES &props);
|
||||||
|
|
||||||
|
static void SetDefaults(EaxEffectProps &props);
|
||||||
|
static void Get(const EaxCall &call, const EAXPITCHSHIFTERPROPERTIES &props);
|
||||||
|
static void Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props);
|
||||||
};
|
};
|
||||||
struct EaxVocalMorpherCommitter : public EaxCommitter<EaxVocalMorpherCommitter> {
|
struct EaxVocalMorpherCommitter : public EaxCommitter<EaxVocalMorpherCommitter> {
|
||||||
using EaxCommitter<EaxVocalMorpherCommitter>::EaxCommitter;
|
using EaxCommitter<EaxVocalMorpherCommitter>::EaxCommitter;
|
||||||
|
|
||||||
|
bool commit(const EAXVOCALMORPHERPROPERTIES &props);
|
||||||
|
|
||||||
|
static void SetDefaults(EaxEffectProps &props);
|
||||||
|
static void Get(const EaxCall &call, const EAXVOCALMORPHERPROPERTIES &props);
|
||||||
|
static void Set(const EaxCall &call, EAXVOCALMORPHERPROPERTIES &props);
|
||||||
};
|
};
|
||||||
struct EaxNullCommitter : public EaxCommitter<EaxNullCommitter> {
|
struct EaxNullCommitter : public EaxCommitter<EaxNullCommitter> {
|
||||||
using EaxCommitter<EaxNullCommitter>::EaxCommitter;
|
using EaxCommitter<EaxNullCommitter>::EaxCommitter;
|
||||||
|
|
||||||
|
bool commit(const std::monostate &props);
|
||||||
|
|
||||||
|
static void SetDefaults(EaxEffectProps &props);
|
||||||
|
static void Get(const EaxCall &call, const std::monostate &props);
|
||||||
|
static void Set(const EaxCall &call, std::monostate &props);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct CommitterFromProps { };
|
||||||
|
|
||||||
|
template<> struct CommitterFromProps<std::monostate> { using type = EaxNullCommitter; };
|
||||||
|
template<> struct CommitterFromProps<EAXREVERBPROPERTIES> { using type = EaxReverbCommitter; };
|
||||||
|
template<> struct CommitterFromProps<EAXCHORUSPROPERTIES> { using type = EaxChorusCommitter; };
|
||||||
|
template<> struct CommitterFromProps<EAXAGCCOMPRESSORPROPERTIES> { using type = EaxCompressorCommitter; };
|
||||||
|
template<> struct CommitterFromProps<EAXAUTOWAHPROPERTIES> { using type = EaxAutowahCommitter; };
|
||||||
|
template<> struct CommitterFromProps<EAXDISTORTIONPROPERTIES> { using type = EaxDistortionCommitter; };
|
||||||
|
template<> struct CommitterFromProps<EAXECHOPROPERTIES> { using type = EaxEchoCommitter; };
|
||||||
|
template<> struct CommitterFromProps<EAXEQUALIZERPROPERTIES> { using type = EaxEqualizerCommitter; };
|
||||||
|
template<> struct CommitterFromProps<EAXFLANGERPROPERTIES> { using type = EaxFlangerCommitter; };
|
||||||
|
template<> struct CommitterFromProps<EAXFREQUENCYSHIFTERPROPERTIES> { using type = EaxFrequencyShifterCommitter; };
|
||||||
|
template<> struct CommitterFromProps<EAXRINGMODULATORPROPERTIES> { using type = EaxModulatorCommitter; };
|
||||||
|
template<> struct CommitterFromProps<EAXPITCHSHIFTERPROPERTIES> { using type = EaxPitchShifterCommitter; };
|
||||||
|
template<> struct CommitterFromProps<EAXVOCALMORPHERPROPERTIES> { using type = EaxVocalMorpherCommitter; };
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using CommitterFor = typename CommitterFromProps<std::remove_cv_t<std::remove_reference_t<T>>>::type;
|
||||||
|
|
||||||
|
|
||||||
class EaxEffect {
|
class EaxEffect {
|
||||||
public:
|
public:
|
||||||
|
|
@ -238,51 +316,39 @@ public:
|
||||||
State4 state5_{};
|
State4 state5_{};
|
||||||
|
|
||||||
|
|
||||||
template<typename T, typename ...Args>
|
static void call_set_defaults(const ALenum altype, EaxEffectProps &props)
|
||||||
void call_set_defaults(Args&& ...args)
|
|
||||||
{ return T::SetDefaults(std::forward<Args>(args)...); }
|
|
||||||
|
|
||||||
void call_set_defaults(const ALenum altype, EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
if(altype == AL_EFFECT_EAXREVERB)
|
switch(altype)
|
||||||
return call_set_defaults<EaxReverbCommitter>(props);
|
{
|
||||||
if(altype == AL_EFFECT_CHORUS)
|
case AL_EFFECT_EAXREVERB: return EaxReverbCommitter::SetDefaults(props);
|
||||||
return call_set_defaults<EaxChorusCommitter>(props);
|
case AL_EFFECT_CHORUS: return EaxChorusCommitter::SetDefaults(props);
|
||||||
if(altype == AL_EFFECT_AUTOWAH)
|
case AL_EFFECT_AUTOWAH: return EaxAutowahCommitter::SetDefaults(props);
|
||||||
return call_set_defaults<EaxAutowahCommitter>(props);
|
case AL_EFFECT_COMPRESSOR: return EaxCompressorCommitter::SetDefaults(props);
|
||||||
if(altype == AL_EFFECT_COMPRESSOR)
|
case AL_EFFECT_DISTORTION: return EaxDistortionCommitter::SetDefaults(props);
|
||||||
return call_set_defaults<EaxCompressorCommitter>(props);
|
case AL_EFFECT_ECHO: return EaxEchoCommitter::SetDefaults(props);
|
||||||
if(altype == AL_EFFECT_DISTORTION)
|
case AL_EFFECT_EQUALIZER: return EaxEqualizerCommitter::SetDefaults(props);
|
||||||
return call_set_defaults<EaxDistortionCommitter>(props);
|
case AL_EFFECT_FLANGER: return EaxFlangerCommitter::SetDefaults(props);
|
||||||
if(altype == AL_EFFECT_ECHO)
|
case AL_EFFECT_FREQUENCY_SHIFTER: return EaxFrequencyShifterCommitter::SetDefaults(props);
|
||||||
return call_set_defaults<EaxEchoCommitter>(props);
|
case AL_EFFECT_RING_MODULATOR: return EaxModulatorCommitter::SetDefaults(props);
|
||||||
if(altype == AL_EFFECT_EQUALIZER)
|
case AL_EFFECT_PITCH_SHIFTER: return EaxPitchShifterCommitter::SetDefaults(props);
|
||||||
return call_set_defaults<EaxEqualizerCommitter>(props);
|
case AL_EFFECT_VOCAL_MORPHER: return EaxVocalMorpherCommitter::SetDefaults(props);
|
||||||
if(altype == AL_EFFECT_FLANGER)
|
case AL_EFFECT_NULL: break;
|
||||||
return call_set_defaults<EaxFlangerCommitter>(props);
|
}
|
||||||
if(altype == AL_EFFECT_FREQUENCY_SHIFTER)
|
return EaxNullCommitter::SetDefaults(props);
|
||||||
return call_set_defaults<EaxFrequencyShifterCommitter>(props);
|
|
||||||
if(altype == AL_EFFECT_RING_MODULATOR)
|
|
||||||
return call_set_defaults<EaxModulatorCommitter>(props);
|
|
||||||
if(altype == AL_EFFECT_PITCH_SHIFTER)
|
|
||||||
return call_set_defaults<EaxPitchShifterCommitter>(props);
|
|
||||||
if(altype == AL_EFFECT_VOCAL_MORPHER)
|
|
||||||
return call_set_defaults<EaxVocalMorpherCommitter>(props);
|
|
||||||
return call_set_defaults<EaxNullCommitter>(props);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void init()
|
void init()
|
||||||
{
|
{
|
||||||
call_set_defaults<EaxReverbCommitter>(state1_.d);
|
EaxReverbCommitter::SetDefaults(state1_.d);
|
||||||
state1_.i = state1_.d;
|
state1_.i = state1_.d;
|
||||||
call_set_defaults<EaxReverbCommitter>(state2_.d);
|
EaxReverbCommitter::SetDefaults(state2_.d);
|
||||||
state2_.i = state2_.d;
|
state2_.i = state2_.d;
|
||||||
call_set_defaults<EaxReverbCommitter>(state3_.d);
|
EaxReverbCommitter::SetDefaults(state3_.d);
|
||||||
state3_.i = state3_.d;
|
state3_.i = state3_.d;
|
||||||
call_set_defaults<T>(state4_.d);
|
T::SetDefaults(state4_.d);
|
||||||
state4_.i = state4_.d;
|
state4_.i = state4_.d;
|
||||||
call_set_defaults<T>(state5_.d);
|
T::SetDefaults(state5_.d);
|
||||||
state5_.i = state5_.d;
|
state5_.i = state5_.d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,9 +356,9 @@ public:
|
||||||
{
|
{
|
||||||
switch(eax_version)
|
switch(eax_version)
|
||||||
{
|
{
|
||||||
case 1: call_set_defaults<EaxReverbCommitter>(state1_.d); break;
|
case 1: EaxReverbCommitter::SetDefaults(state1_.d); break;
|
||||||
case 2: call_set_defaults<EaxReverbCommitter>(state2_.d); break;
|
case 2: EaxReverbCommitter::SetDefaults(state2_.d); break;
|
||||||
case 3: call_set_defaults<EaxReverbCommitter>(state3_.d); break;
|
case 3: EaxReverbCommitter::SetDefaults(state3_.d); break;
|
||||||
case 4: call_set_defaults(altype, state4_.d); break;
|
case 4: call_set_defaults(altype, state4_.d); break;
|
||||||
case 5: call_set_defaults(altype, state5_.d); break;
|
case 5: call_set_defaults(altype, state5_.d); break;
|
||||||
}
|
}
|
||||||
|
|
@ -300,47 +366,20 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define EAXCALL(T, Callable, ...) \
|
|
||||||
if(T == EaxEffectType::Reverb) \
|
|
||||||
return Callable<EaxReverbCommitter>(__VA_ARGS__); \
|
|
||||||
if(T == EaxEffectType::Chorus) \
|
|
||||||
return Callable<EaxChorusCommitter>(__VA_ARGS__); \
|
|
||||||
if(T == EaxEffectType::Autowah) \
|
|
||||||
return Callable<EaxAutowahCommitter>(__VA_ARGS__); \
|
|
||||||
if(T == EaxEffectType::Compressor) \
|
|
||||||
return Callable<EaxCompressorCommitter>(__VA_ARGS__); \
|
|
||||||
if(T == EaxEffectType::Distortion) \
|
|
||||||
return Callable<EaxDistortionCommitter>(__VA_ARGS__); \
|
|
||||||
if(T == EaxEffectType::Echo) \
|
|
||||||
return Callable<EaxEchoCommitter>(__VA_ARGS__); \
|
|
||||||
if(T == EaxEffectType::Equalizer) \
|
|
||||||
return Callable<EaxEqualizerCommitter>(__VA_ARGS__); \
|
|
||||||
if(T == EaxEffectType::Flanger) \
|
|
||||||
return Callable<EaxFlangerCommitter>(__VA_ARGS__); \
|
|
||||||
if(T == EaxEffectType::FrequencyShifter) \
|
|
||||||
return Callable<EaxFrequencyShifterCommitter>(__VA_ARGS__); \
|
|
||||||
if(T == EaxEffectType::Modulator) \
|
|
||||||
return Callable<EaxModulatorCommitter>(__VA_ARGS__); \
|
|
||||||
if(T == EaxEffectType::PitchShifter) \
|
|
||||||
return Callable<EaxPitchShifterCommitter>(__VA_ARGS__); \
|
|
||||||
if(T == EaxEffectType::VocalMorpher) \
|
|
||||||
return Callable<EaxVocalMorpherCommitter>(__VA_ARGS__); \
|
|
||||||
return Callable<EaxNullCommitter>(__VA_ARGS__)
|
|
||||||
|
|
||||||
template<typename T, typename ...Args>
|
|
||||||
static void call_set(Args&& ...args)
|
|
||||||
{ return T::Set(std::forward<Args>(args)...); }
|
|
||||||
|
|
||||||
static void call_set(const EaxCall &call, EaxEffectProps &props)
|
static void call_set(const EaxCall &call, EaxEffectProps &props)
|
||||||
{ EAXCALL(props.mType, call_set, call, props); }
|
{
|
||||||
|
return std::visit([&](auto &arg)
|
||||||
|
{ return CommitterFor<decltype(arg)>::Set(call, arg); },
|
||||||
|
props);
|
||||||
|
}
|
||||||
|
|
||||||
void set(const EaxCall &call)
|
void set(const EaxCall &call)
|
||||||
{
|
{
|
||||||
switch(call.get_version())
|
switch(call.get_version())
|
||||||
{
|
{
|
||||||
case 1: call_set<EaxReverbCommitter>(call, state1_.d); break;
|
case 1: EaxReverbCommitter::Set(call, state1_.d); break;
|
||||||
case 2: call_set<EaxReverbCommitter>(call, state2_.d); break;
|
case 2: EaxReverbCommitter::Set(call, state2_.d); break;
|
||||||
case 3: call_set<EaxReverbCommitter>(call, state3_.d); break;
|
case 3: EaxReverbCommitter::Set(call, state3_.d); break;
|
||||||
case 4: call_set(call, state4_.d); break;
|
case 4: call_set(call, state4_.d); break;
|
||||||
case 5: call_set(call, state5_.d); break;
|
case 5: call_set(call, state5_.d); break;
|
||||||
}
|
}
|
||||||
|
|
@ -348,32 +387,32 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<typename T, typename ...Args>
|
|
||||||
static void call_get(Args&& ...args)
|
|
||||||
{ return T::Get(std::forward<Args>(args)...); }
|
|
||||||
|
|
||||||
static void call_get(const EaxCall &call, const EaxEffectProps &props)
|
static void call_get(const EaxCall &call, const EaxEffectProps &props)
|
||||||
{ EAXCALL(props.mType, call_get, call, props); }
|
{
|
||||||
|
return std::visit([&](auto &arg)
|
||||||
|
{ return CommitterFor<decltype(arg)>::Get(call, arg); },
|
||||||
|
props);
|
||||||
|
}
|
||||||
|
|
||||||
void get(const EaxCall &call)
|
void get(const EaxCall &call) const
|
||||||
{
|
{
|
||||||
switch(call.get_version())
|
switch(call.get_version())
|
||||||
{
|
{
|
||||||
case 1: call_get<EaxReverbCommitter>(call, state1_.d); break;
|
case 1: EaxReverbCommitter::Get(call, state1_.d); break;
|
||||||
case 2: call_get<EaxReverbCommitter>(call, state2_.d); break;
|
case 2: EaxReverbCommitter::Get(call, state2_.d); break;
|
||||||
case 3: call_get<EaxReverbCommitter>(call, state3_.d); break;
|
case 3: EaxReverbCommitter::Get(call, state3_.d); break;
|
||||||
case 4: call_get(call, state4_.d); break;
|
case 4: call_get(call, state4_.d); break;
|
||||||
case 5: call_get(call, state5_.d); break;
|
case 5: call_get(call, state5_.d); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<typename T, typename ...Args>
|
|
||||||
bool call_commit(Args&& ...args)
|
|
||||||
{ return T{props_, al_effect_props_}.commit(std::forward<Args>(args)...); }
|
|
||||||
|
|
||||||
bool call_commit(const EaxEffectProps &props)
|
bool call_commit(const EaxEffectProps &props)
|
||||||
{ EAXCALL(props.mType, call_commit, props); }
|
{
|
||||||
|
return std::visit([&](auto &arg)
|
||||||
|
{ return CommitterFor<decltype(arg)>{props_, al_effect_props_}.commit(arg); },
|
||||||
|
props);
|
||||||
|
}
|
||||||
|
|
||||||
bool commit(int eax_version)
|
bool commit(int eax_version)
|
||||||
{
|
{
|
||||||
|
|
@ -388,15 +427,15 @@ public:
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
state1_.i = state1_.d;
|
state1_.i = state1_.d;
|
||||||
ret |= call_commit<EaxReverbCommitter>(state1_.d);
|
ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state1_.d);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
state2_.i = state2_.d;
|
state2_.i = state2_.d;
|
||||||
ret |= call_commit<EaxReverbCommitter>(state2_.d);
|
ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state2_.d);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
state3_.i = state3_.d;
|
state3_.i = state3_.d;
|
||||||
ret |= call_commit<EaxReverbCommitter>(state3_.d);
|
ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state3_.d);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
state4_.i = state4_.d;
|
state4_.i = state4_.d;
|
||||||
|
|
|
||||||
|
|
@ -6,54 +6,27 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
EaxException::EaxException(const char *context, const char *message)
|
EaxException::EaxException(std::string_view context, std::string_view message)
|
||||||
: std::runtime_error{make_message(context, message)}
|
: std::runtime_error{make_message(context, message)}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
EaxException::~EaxException() = default;
|
EaxException::~EaxException() = default;
|
||||||
|
|
||||||
|
|
||||||
std::string EaxException::make_message(const char *context, const char *message)
|
std::string EaxException::make_message(std::string_view context, std::string_view message)
|
||||||
{
|
{
|
||||||
const auto context_size = (context ? std::string::traits_type::length(context) : 0);
|
|
||||||
const auto has_contex = (context_size > 0);
|
|
||||||
|
|
||||||
const auto message_size = (message ? std::string::traits_type::length(message) : 0);
|
|
||||||
const auto has_message = (message_size > 0);
|
|
||||||
|
|
||||||
if (!has_contex && !has_message)
|
|
||||||
{
|
|
||||||
return std::string{};
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr char left_prefix[] = "[";
|
|
||||||
const auto left_prefix_size = std::string::traits_type::length(left_prefix);
|
|
||||||
|
|
||||||
static constexpr char right_prefix[] = "] ";
|
|
||||||
const auto right_prefix_size = std::string::traits_type::length(right_prefix);
|
|
||||||
|
|
||||||
const auto what_size =
|
|
||||||
(
|
|
||||||
has_contex ?
|
|
||||||
left_prefix_size + context_size + right_prefix_size :
|
|
||||||
0) +
|
|
||||||
message_size +
|
|
||||||
1;
|
|
||||||
|
|
||||||
auto what = std::string{};
|
auto what = std::string{};
|
||||||
what.reserve(what_size);
|
if(context.empty() && message.empty())
|
||||||
|
return what;
|
||||||
|
|
||||||
if (has_contex)
|
what.reserve((!context.empty() ? context.size() + 3 : 0) + message.length() + 1);
|
||||||
|
if(!context.empty())
|
||||||
{
|
{
|
||||||
what.append(left_prefix, left_prefix_size);
|
what += "[";
|
||||||
what.append(context, context_size);
|
what += context;
|
||||||
what.append(right_prefix, right_prefix_size);
|
what += "] ";
|
||||||
}
|
|
||||||
|
|
||||||
if (has_message)
|
|
||||||
{
|
|
||||||
what.append(message, message_size);
|
|
||||||
}
|
}
|
||||||
|
what += message;
|
||||||
|
|
||||||
return what;
|
return what;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,23 @@
|
||||||
#ifndef EAX_EXCEPTION_INCLUDED
|
#ifndef EAX_EXCEPTION_INCLUDED
|
||||||
#define EAX_EXCEPTION_INCLUDED
|
#define EAX_EXCEPTION_INCLUDED
|
||||||
|
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
|
||||||
class EaxException : public std::runtime_error {
|
class EaxException : public std::runtime_error {
|
||||||
static std::string make_message(const char *context, const char *message);
|
static std::string make_message(std::string_view context, std::string_view message);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
EaxException(const char *context, const char *message);
|
EaxException() = delete;
|
||||||
|
EaxException(const EaxException&) = default;
|
||||||
|
EaxException(EaxException&&) = default;
|
||||||
|
EaxException(std::string_view context, std::string_view message);
|
||||||
~EaxException() override;
|
~EaxException() override;
|
||||||
}; // EaxException
|
|
||||||
|
|
||||||
|
auto operator=(const EaxException&) -> EaxException& = default;
|
||||||
|
auto operator=(EaxException&&) -> EaxException& = default;
|
||||||
|
};
|
||||||
|
|
||||||
#endif // !EAX_EXCEPTION_INCLUDED
|
#endif /* EAX_EXCEPTION_INCLUDED */
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,16 @@
|
||||||
|
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "aloptional.h"
|
|
||||||
#include "api.h"
|
#include "api.h"
|
||||||
|
|
||||||
|
|
||||||
using EaxFxSlotIndexValue = std::size_t;
|
using EaxFxSlotIndexValue = std::size_t;
|
||||||
|
|
||||||
class EaxFxSlotIndex : public al::optional<EaxFxSlotIndexValue>
|
class EaxFxSlotIndex : public std::optional<EaxFxSlotIndexValue> {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
using al::optional<EaxFxSlotIndexValue>::optional;
|
using std::optional<EaxFxSlotIndexValue>::optional;
|
||||||
|
|
||||||
EaxFxSlotIndex& operator=(const EaxFxSlotIndexValue &value) { set(value); return *this; }
|
EaxFxSlotIndex& operator=(const EaxFxSlotIndexValue &value) { set(value); return *this; }
|
||||||
EaxFxSlotIndex& operator=(const GUID &guid) { set(guid); return *this; }
|
EaxFxSlotIndex& operator=(const GUID &guid) { set(guid); return *this; }
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,10 @@
|
||||||
|
|
||||||
#include "al/auxeffectslot.h"
|
#include "al/auxeffectslot.h"
|
||||||
|
|
||||||
#include "api.h"
|
|
||||||
#include "call.h"
|
|
||||||
#include "fx_slot_index.h"
|
#include "fx_slot_index.h"
|
||||||
|
|
||||||
|
|
||||||
class EaxFxSlots
|
class EaxFxSlots {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
void initialize(ALCcontext& al_context);
|
void initialize(ALCcontext& al_context);
|
||||||
|
|
||||||
|
|
@ -25,11 +22,9 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const ALeffectslot& get(
|
[[nodiscard]] auto get(EaxFxSlotIndex index) const -> const ALeffectslot&;
|
||||||
EaxFxSlotIndex index) const;
|
|
||||||
|
|
||||||
ALeffectslot& get(
|
[[nodiscard]] auto get(EaxFxSlotIndex index) -> ALeffectslot&;
|
||||||
EaxFxSlotIndex index);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using Items = std::array<EaxAlEffectSlotUPtr, EAX_MAX_FXSLOTS>;
|
using Items = std::array<EaxAlEffectSlotUPtr, EAX_MAX_FXSLOTS>;
|
||||||
|
|
@ -39,8 +34,7 @@ private:
|
||||||
|
|
||||||
|
|
||||||
[[noreturn]]
|
[[noreturn]]
|
||||||
static void fail(
|
static void fail(const char* message);
|
||||||
const char* message);
|
|
||||||
|
|
||||||
void initialize_fx_slots(ALCcontext& al_context);
|
void initialize_fx_slots(ALCcontext& al_context);
|
||||||
}; // EaxFxSlots
|
}; // EaxFxSlots
|
||||||
|
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "globals.h"
|
|
||||||
|
|
||||||
|
|
||||||
bool eax_g_is_enabled = true;
|
|
||||||
|
|
||||||
|
|
||||||
const char eax1_ext_name[] = "EAX";
|
|
||||||
const char eax2_ext_name[] = "EAX2.0";
|
|
||||||
const char eax3_ext_name[] = "EAX3.0";
|
|
||||||
const char eax4_ext_name[] = "EAX4.0";
|
|
||||||
const char eax5_ext_name[] = "EAX5.0";
|
|
||||||
|
|
||||||
const char eax_x_ram_ext_name[] = "EAX-RAM";
|
|
||||||
|
|
||||||
const char eax_eax_set_func_name[] = "EAXSet";
|
|
||||||
const char eax_eax_get_func_name[] = "EAXGet";
|
|
||||||
|
|
||||||
const char eax_eax_set_buffer_mode_func_name[] = "EAXSetBufferMode";
|
|
||||||
const char eax_eax_get_buffer_mode_func_name[] = "EAXGetBufferMode";
|
|
||||||
|
|
@ -1,22 +1,6 @@
|
||||||
#ifndef EAX_GLOBALS_INCLUDED
|
#ifndef EAX_GLOBALS_INCLUDED
|
||||||
#define EAX_GLOBALS_INCLUDED
|
#define EAX_GLOBALS_INCLUDED
|
||||||
|
|
||||||
|
inline bool eax_g_is_enabled{true};
|
||||||
|
|
||||||
extern bool eax_g_is_enabled;
|
#endif /* EAX_GLOBALS_INCLUDED */
|
||||||
|
|
||||||
|
|
||||||
extern const char eax1_ext_name[];
|
|
||||||
extern const char eax2_ext_name[];
|
|
||||||
extern const char eax3_ext_name[];
|
|
||||||
extern const char eax4_ext_name[];
|
|
||||||
extern const char eax5_ext_name[];
|
|
||||||
|
|
||||||
extern const char eax_x_ram_ext_name[];
|
|
||||||
|
|
||||||
extern const char eax_eax_set_func_name[];
|
|
||||||
extern const char eax_eax_get_func_name[];
|
|
||||||
|
|
||||||
extern const char eax_eax_set_buffer_mode_func_name[];
|
|
||||||
extern const char eax_eax_get_buffer_mode_func_name[];
|
|
||||||
|
|
||||||
#endif // !EAX_GLOBALS_INCLUDED
|
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,11 @@
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
|
||||||
|
#include "alstring.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
|
||||||
|
|
||||||
void eax_log_exception(const char *message) noexcept
|
void eax_log_exception(std::string_view message) noexcept
|
||||||
{
|
{
|
||||||
const auto exception_ptr = std::current_exception();
|
const auto exception_ptr = std::current_exception();
|
||||||
assert(exception_ptr);
|
assert(exception_ptr);
|
||||||
|
|
@ -17,10 +18,9 @@ void eax_log_exception(const char *message) noexcept
|
||||||
std::rethrow_exception(exception_ptr);
|
std::rethrow_exception(exception_ptr);
|
||||||
}
|
}
|
||||||
catch(const std::exception& ex) {
|
catch(const std::exception& ex) {
|
||||||
const auto ex_message = ex.what();
|
ERR("%.*s %s\n", al::sizei(message), message.data(), ex.what());
|
||||||
ERR("%s %s\n", message ? message : "", ex_message);
|
|
||||||
}
|
}
|
||||||
catch(...) {
|
catch(...) {
|
||||||
ERR("%s %s\n", message ? message : "", "Generic exception.");
|
ERR("%.*s %s\n", al::sizei(message), message.data(), "Generic exception.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,11 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "opthelpers.h"
|
||||||
|
|
||||||
using EaxDirtyFlags = unsigned int;
|
using EaxDirtyFlags = unsigned int;
|
||||||
|
|
||||||
struct EaxAlLowPassParam {
|
struct EaxAlLowPassParam {
|
||||||
|
|
@ -13,16 +16,13 @@ struct EaxAlLowPassParam {
|
||||||
float gain_hf;
|
float gain_hf;
|
||||||
};
|
};
|
||||||
|
|
||||||
void eax_log_exception(const char *message) noexcept;
|
void eax_log_exception(std::string_view message) noexcept;
|
||||||
|
|
||||||
template<typename TException, typename TValue>
|
template<typename TException, typename TValue>
|
||||||
void eax_validate_range(
|
void eax_validate_range(std::string_view value_name, const TValue& value, const TValue& min_value,
|
||||||
const char* value_name,
|
|
||||||
const TValue& value,
|
|
||||||
const TValue& min_value,
|
|
||||||
const TValue& max_value)
|
const TValue& max_value)
|
||||||
{
|
{
|
||||||
if (value >= min_value && value <= max_value)
|
if(value >= min_value && value <= max_value) LIKELY
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto message =
|
const auto message =
|
||||||
|
|
|
||||||
|
|
@ -24,15 +24,7 @@ constexpr auto AL_STORAGE_AUTOMATIC_NAME = "AL_STORAGE_AUTOMATIC";
|
||||||
constexpr auto AL_STORAGE_HARDWARE_NAME = "AL_STORAGE_HARDWARE";
|
constexpr auto AL_STORAGE_HARDWARE_NAME = "AL_STORAGE_HARDWARE";
|
||||||
constexpr auto AL_STORAGE_ACCESSIBLE_NAME = "AL_STORAGE_ACCESSIBLE";
|
constexpr auto AL_STORAGE_ACCESSIBLE_NAME = "AL_STORAGE_ACCESSIBLE";
|
||||||
|
|
||||||
|
ALboolean AL_APIENTRY EAXSetBufferMode(ALsizei n, const ALuint *buffers, ALint value) noexcept;
|
||||||
ALboolean AL_APIENTRY EAXSetBufferMode(
|
ALenum AL_APIENTRY EAXGetBufferMode(ALuint buffer, ALint *pReserved) noexcept;
|
||||||
ALsizei n,
|
|
||||||
const ALuint* buffers,
|
|
||||||
ALint value);
|
|
||||||
|
|
||||||
ALenum AL_APIENTRY EAXGetBufferMode(
|
|
||||||
ALuint buffer,
|
|
||||||
ALint* pReserved);
|
|
||||||
|
|
||||||
|
|
||||||
#endif // !EAX_X_RAM_INCLUDED
|
#endif // !EAX_X_RAM_INCLUDED
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,13 @@
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <new>
|
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/alc.h"
|
#include "AL/alc.h"
|
||||||
|
|
@ -38,26 +42,23 @@
|
||||||
#include "AL/efx-presets.h"
|
#include "AL/efx-presets.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
|
#include "al/effects/effects.h"
|
||||||
#include "albit.h"
|
#include "albit.h"
|
||||||
#include "alc/context.h"
|
#include "alc/context.h"
|
||||||
#include "alc/device.h"
|
#include "alc/device.h"
|
||||||
#include "alc/effects/base.h"
|
|
||||||
#include "alc/inprogext.h"
|
#include "alc/inprogext.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.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 "error.h"
|
||||||
|
#include "intrusive_ptr.h"
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
#include "vector.h"
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include "eax/exception.h"
|
const std::array<EffectList,16> gEffectList{{
|
||||||
#endif // ALSOFT_EAX
|
|
||||||
|
|
||||||
const EffectList gEffectList[16]{
|
|
||||||
{ "eaxreverb", EAXREVERB_EFFECT, AL_EFFECT_EAXREVERB },
|
{ "eaxreverb", EAXREVERB_EFFECT, AL_EFFECT_EAXREVERB },
|
||||||
{ "reverb", REVERB_EFFECT, AL_EFFECT_REVERB },
|
{ "reverb", REVERB_EFFECT, AL_EFFECT_REVERB },
|
||||||
{ "autowah", AUTOWAH_EFFECT, AL_EFFECT_AUTOWAH },
|
{ "autowah", AUTOWAH_EFFECT, AL_EFFECT_AUTOWAH },
|
||||||
|
|
@ -73,95 +74,74 @@ const EffectList gEffectList[16]{
|
||||||
{ "vmorpher", VMORPHER_EFFECT, AL_EFFECT_VOCAL_MORPHER },
|
{ "vmorpher", VMORPHER_EFFECT, AL_EFFECT_VOCAL_MORPHER },
|
||||||
{ "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT },
|
{ "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT },
|
||||||
{ "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_DIALOGUE },
|
{ "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_DIALOGUE },
|
||||||
{ "convolution", CONVOLUTION_EFFECT, AL_EFFECT_CONVOLUTION_REVERB_SOFT },
|
{ "convolution", CONVOLUTION_EFFECT, AL_EFFECT_CONVOLUTION_SOFT },
|
||||||
};
|
}};
|
||||||
|
|
||||||
bool DisabledEffects[MAX_EFFECTS];
|
|
||||||
|
|
||||||
|
|
||||||
effect_exception::effect_exception(ALenum code, const char *msg, ...) : mErrorCode{code}
|
|
||||||
{
|
|
||||||
std::va_list args;
|
|
||||||
va_start(args, msg);
|
|
||||||
setMessage(msg, args);
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
effect_exception::~effect_exception() = default;
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct EffectPropsItem {
|
using SubListAllocator = al::allocator<std::array<ALeffect,64>>;
|
||||||
ALenum Type;
|
|
||||||
const EffectProps &DefaultProps;
|
|
||||||
const EffectVtable &Vtable;
|
|
||||||
};
|
|
||||||
constexpr EffectPropsItem EffectPropsList[] = {
|
|
||||||
{ AL_EFFECT_NULL, NullEffectProps, NullEffectVtable },
|
|
||||||
{ AL_EFFECT_EAXREVERB, ReverbEffectProps, ReverbEffectVtable },
|
|
||||||
{ AL_EFFECT_REVERB, StdReverbEffectProps, StdReverbEffectVtable },
|
|
||||||
{ AL_EFFECT_AUTOWAH, AutowahEffectProps, AutowahEffectVtable },
|
|
||||||
{ AL_EFFECT_CHORUS, ChorusEffectProps, ChorusEffectVtable },
|
|
||||||
{ AL_EFFECT_COMPRESSOR, CompressorEffectProps, CompressorEffectVtable },
|
|
||||||
{ AL_EFFECT_DISTORTION, DistortionEffectProps, DistortionEffectVtable },
|
|
||||||
{ AL_EFFECT_ECHO, EchoEffectProps, EchoEffectVtable },
|
|
||||||
{ AL_EFFECT_EQUALIZER, EqualizerEffectProps, EqualizerEffectVtable },
|
|
||||||
{ AL_EFFECT_FLANGER, FlangerEffectProps, FlangerEffectVtable },
|
|
||||||
{ AL_EFFECT_FREQUENCY_SHIFTER, FshifterEffectProps, FshifterEffectVtable },
|
|
||||||
{ AL_EFFECT_RING_MODULATOR, ModulatorEffectProps, ModulatorEffectVtable },
|
|
||||||
{ AL_EFFECT_PITCH_SHIFTER, PshifterEffectProps, PshifterEffectVtable },
|
|
||||||
{ AL_EFFECT_VOCAL_MORPHER, VmorpherEffectProps, VmorpherEffectVtable },
|
|
||||||
{ AL_EFFECT_DEDICATED_DIALOGUE, DedicatedEffectProps, DedicatedEffectVtable },
|
|
||||||
{ AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, DedicatedEffectProps, DedicatedEffectVtable },
|
|
||||||
{ AL_EFFECT_CONVOLUTION_REVERB_SOFT, ConvolutionEffectProps, ConvolutionEffectVtable },
|
|
||||||
};
|
|
||||||
|
|
||||||
|
constexpr auto GetDefaultProps(ALenum type) noexcept -> const EffectProps&
|
||||||
void ALeffect_setParami(ALeffect *effect, ALenum param, int value)
|
|
||||||
{ effect->vtab->setParami(&effect->Props, param, value); }
|
|
||||||
void ALeffect_setParamiv(ALeffect *effect, ALenum param, const int *values)
|
|
||||||
{ effect->vtab->setParamiv(&effect->Props, param, values); }
|
|
||||||
void ALeffect_setParamf(ALeffect *effect, ALenum param, float value)
|
|
||||||
{ effect->vtab->setParamf(&effect->Props, param, value); }
|
|
||||||
void ALeffect_setParamfv(ALeffect *effect, ALenum param, const float *values)
|
|
||||||
{ effect->vtab->setParamfv(&effect->Props, param, values); }
|
|
||||||
|
|
||||||
void ALeffect_getParami(const ALeffect *effect, ALenum param, int *value)
|
|
||||||
{ effect->vtab->getParami(&effect->Props, param, value); }
|
|
||||||
void ALeffect_getParamiv(const ALeffect *effect, ALenum param, int *values)
|
|
||||||
{ effect->vtab->getParamiv(&effect->Props, param, values); }
|
|
||||||
void ALeffect_getParamf(const ALeffect *effect, ALenum param, float *value)
|
|
||||||
{ effect->vtab->getParamf(&effect->Props, param, value); }
|
|
||||||
void ALeffect_getParamfv(const ALeffect *effect, ALenum param, float *values)
|
|
||||||
{ effect->vtab->getParamfv(&effect->Props, param, values); }
|
|
||||||
|
|
||||||
|
|
||||||
const EffectPropsItem *getEffectPropsItemByType(ALenum type)
|
|
||||||
{
|
{
|
||||||
auto iter = std::find_if(std::begin(EffectPropsList), std::end(EffectPropsList),
|
switch(type)
|
||||||
[type](const EffectPropsItem &item) noexcept -> bool
|
{
|
||||||
{ return item.Type == type; });
|
case AL_EFFECT_NULL: return NullEffectProps;
|
||||||
return (iter != std::end(EffectPropsList)) ? al::to_address(iter) : nullptr;
|
case AL_EFFECT_EAXREVERB: return ReverbEffectProps;
|
||||||
|
case AL_EFFECT_REVERB: return StdReverbEffectProps;
|
||||||
|
case AL_EFFECT_AUTOWAH: return AutowahEffectProps;
|
||||||
|
case AL_EFFECT_CHORUS: return ChorusEffectProps;
|
||||||
|
case AL_EFFECT_COMPRESSOR: return CompressorEffectProps;
|
||||||
|
case AL_EFFECT_DISTORTION: return DistortionEffectProps;
|
||||||
|
case AL_EFFECT_ECHO: return EchoEffectProps;
|
||||||
|
case AL_EFFECT_EQUALIZER: return EqualizerEffectProps;
|
||||||
|
case AL_EFFECT_FLANGER: return FlangerEffectProps;
|
||||||
|
case AL_EFFECT_FREQUENCY_SHIFTER: return FshifterEffectProps;
|
||||||
|
case AL_EFFECT_RING_MODULATOR: return ModulatorEffectProps;
|
||||||
|
case AL_EFFECT_PITCH_SHIFTER: return PshifterEffectProps;
|
||||||
|
case AL_EFFECT_VOCAL_MORPHER: return VmorpherEffectProps;
|
||||||
|
case AL_EFFECT_DEDICATED_DIALOGUE: return DedicatedDialogEffectProps;
|
||||||
|
case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return DedicatedLfeEffectProps;
|
||||||
|
case AL_EFFECT_CONVOLUTION_SOFT: return ConvolutionEffectProps;
|
||||||
|
}
|
||||||
|
return NullEffectProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitEffectParams(ALeffect *effect, ALenum type)
|
void InitEffectParams(ALeffect *effect, ALenum type) noexcept
|
||||||
{
|
{
|
||||||
const EffectPropsItem *item{getEffectPropsItemByType(type)};
|
switch(type)
|
||||||
if(item)
|
|
||||||
{
|
{
|
||||||
effect->Props = item->DefaultProps;
|
case AL_EFFECT_NULL: effect->PropsVariant.emplace<NullEffectHandler>(); break;
|
||||||
effect->vtab = &item->Vtable;
|
case AL_EFFECT_EAXREVERB: effect->PropsVariant.emplace<ReverbEffectHandler>(); break;
|
||||||
}
|
case AL_EFFECT_REVERB: effect->PropsVariant.emplace<StdReverbEffectHandler>(); break;
|
||||||
else
|
case AL_EFFECT_AUTOWAH: effect->PropsVariant.emplace<AutowahEffectHandler>(); break;
|
||||||
{
|
case AL_EFFECT_CHORUS: effect->PropsVariant.emplace<ChorusEffectHandler>(); break;
|
||||||
effect->Props = EffectProps{};
|
case AL_EFFECT_COMPRESSOR: effect->PropsVariant.emplace<CompressorEffectHandler>(); break;
|
||||||
effect->vtab = &NullEffectVtable;
|
case AL_EFFECT_DISTORTION: effect->PropsVariant.emplace<DistortionEffectHandler>(); break;
|
||||||
|
case AL_EFFECT_ECHO: effect->PropsVariant.emplace<EchoEffectHandler>(); break;
|
||||||
|
case AL_EFFECT_EQUALIZER: effect->PropsVariant.emplace<EqualizerEffectHandler>(); break;
|
||||||
|
case AL_EFFECT_FLANGER: effect->PropsVariant.emplace<ChorusEffectHandler>(); break;
|
||||||
|
case AL_EFFECT_FREQUENCY_SHIFTER: effect->PropsVariant.emplace<FshifterEffectHandler>(); break;
|
||||||
|
case AL_EFFECT_RING_MODULATOR: effect->PropsVariant.emplace<ModulatorEffectHandler>(); break;
|
||||||
|
case AL_EFFECT_PITCH_SHIFTER: effect->PropsVariant.emplace<PshifterEffectHandler>(); break;
|
||||||
|
case AL_EFFECT_VOCAL_MORPHER: effect->PropsVariant.emplace<VmorpherEffectHandler>(); break;
|
||||||
|
case AL_EFFECT_DEDICATED_DIALOGUE:
|
||||||
|
effect->PropsVariant.emplace<DedicatedDialogEffectHandler>();
|
||||||
|
break;
|
||||||
|
case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT:
|
||||||
|
effect->PropsVariant.emplace<DedicatedLfeEffectHandler>();
|
||||||
|
break;
|
||||||
|
case AL_EFFECT_CONVOLUTION_SOFT:
|
||||||
|
effect->PropsVariant.emplace<ConvolutionEffectHandler>();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
effect->Props = GetDefaultProps(type);
|
||||||
effect->type = type;
|
effect->type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EnsureEffects(ALCdevice *device, size_t needed)
|
auto EnsureEffects(ALCdevice *device, size_t needed) noexcept -> bool
|
||||||
{
|
try {
|
||||||
size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), size_t{0},
|
size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), 0_uz,
|
||||||
[](size_t cur, const EffectSubList &sublist) noexcept -> size_t
|
[](size_t cur, const EffectSubList &sublist) noexcept -> size_t
|
||||||
{ return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
|
{ return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
|
||||||
|
|
||||||
|
|
@ -170,21 +150,19 @@ bool EnsureEffects(ALCdevice *device, size_t needed)
|
||||||
if(device->EffectList.size() >= 1<<25) UNLIKELY
|
if(device->EffectList.size() >= 1<<25) UNLIKELY
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
device->EffectList.emplace_back();
|
EffectSubList sublist{};
|
||||||
auto sublist = device->EffectList.end() - 1;
|
sublist.FreeMask = ~0_u64;
|
||||||
sublist->FreeMask = ~0_u64;
|
sublist.Effects = SubListAllocator{}.allocate(1);
|
||||||
sublist->Effects = static_cast<ALeffect*>(al_calloc(alignof(ALeffect), sizeof(ALeffect)*64));
|
device->EffectList.emplace_back(std::move(sublist));
|
||||||
if(!sublist->Effects) UNLIKELY
|
count += std::tuple_size_v<SubListAllocator::value_type>;
|
||||||
{
|
|
||||||
device->EffectList.pop_back();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
count += 64;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
catch(...) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ALeffect *AllocEffect(ALCdevice *device)
|
ALeffect *AllocEffect(ALCdevice *device) noexcept
|
||||||
{
|
{
|
||||||
auto sublist = std::find_if(device->EffectList.begin(), device->EffectList.end(),
|
auto sublist = std::find_if(device->EffectList.begin(), device->EffectList.end(),
|
||||||
[](const EffectSubList &entry) noexcept -> bool
|
[](const EffectSubList &entry) noexcept -> bool
|
||||||
|
|
@ -193,7 +171,7 @@ ALeffect *AllocEffect(ALCdevice *device)
|
||||||
auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
|
auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
|
||||||
ASSUME(slidx < 64);
|
ASSUME(slidx < 64);
|
||||||
|
|
||||||
ALeffect *effect{al::construct_at(sublist->Effects + slidx)};
|
ALeffect *effect{al::construct_at(al::to_address(sublist->Effects->begin() + slidx))};
|
||||||
InitEffectParams(effect, AL_EFFECT_NULL);
|
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||||
|
|
||||||
/* Add 1 to avoid effect ID 0. */
|
/* Add 1 to avoid effect ID 0. */
|
||||||
|
|
@ -206,16 +184,18 @@ ALeffect *AllocEffect(ALCdevice *device)
|
||||||
|
|
||||||
void FreeEffect(ALCdevice *device, ALeffect *effect)
|
void FreeEffect(ALCdevice *device, ALeffect *effect)
|
||||||
{
|
{
|
||||||
|
device->mEffectNames.erase(effect->id);
|
||||||
|
|
||||||
const ALuint id{effect->id - 1};
|
const ALuint id{effect->id - 1};
|
||||||
const size_t lidx{id >> 6};
|
const size_t lidx{id >> 6};
|
||||||
const ALuint slidx{id & 0x3f};
|
const ALuint slidx{id & 0x3f};
|
||||||
|
|
||||||
al::destroy_at(effect);
|
std::destroy_at(effect);
|
||||||
|
|
||||||
device->EffectList[lidx].FreeMask |= 1_u64 << slidx;
|
device->EffectList[lidx].FreeMask |= 1_u64 << slidx;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ALeffect *LookupEffect(ALCdevice *device, ALuint id)
|
inline auto LookupEffect(ALCdevice *device, ALuint id) noexcept -> ALeffect*
|
||||||
{
|
{
|
||||||
const size_t lidx{(id-1) >> 6};
|
const size_t lidx{(id-1) >> 6};
|
||||||
const ALuint slidx{(id-1) & 0x3f};
|
const ALuint slidx{(id-1) & 0x3f};
|
||||||
|
|
@ -225,320 +205,294 @@ inline ALeffect *LookupEffect(ALCdevice *device, ALuint id)
|
||||||
EffectSubList &sublist = device->EffectList[lidx];
|
EffectSubList &sublist = device->EffectList[lidx];
|
||||||
if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
|
if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return sublist.Effects + slidx;
|
return al::to_address(sublist.Effects->begin() + slidx);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects)
|
AL_API DECL_FUNC2(void, alGenEffects, ALsizei,n, ALuint*,effects)
|
||||||
START_API_FUNC
|
FORCE_ALIGN void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) noexcept
|
||||||
{
|
try {
|
||||||
ContextRef context{GetContextRef()};
|
if(n < 0)
|
||||||
if(!context) UNLIKELY return;
|
throw al::context_error{AL_INVALID_VALUE, "Generating %d effects", n};
|
||||||
|
|
||||||
if(n < 0) UNLIKELY
|
|
||||||
context->setError(AL_INVALID_VALUE, "Generating %d effects", n);
|
|
||||||
if(n <= 0) UNLIKELY return;
|
if(n <= 0) UNLIKELY return;
|
||||||
|
|
||||||
ALCdevice *device{context->mALDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||||
if(!EnsureEffects(device, static_cast<ALuint>(n)))
|
|
||||||
{
|
|
||||||
context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d effect%s", n, (n==1)?"":"s");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(n == 1) LIKELY
|
const al::span eids{effects, static_cast<ALuint>(n)};
|
||||||
{
|
if(!EnsureEffects(device, eids.size()))
|
||||||
/* Special handling for the easy and normal case. */
|
throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d effect%s", n,
|
||||||
ALeffect *effect{AllocEffect(device)};
|
(n == 1) ? "" : "s"};
|
||||||
effects[0] = effect->id;
|
|
||||||
}
|
std::generate(eids.begin(), eids.end(), [device]{ return AllocEffect(device)->id; });
|
||||||
else
|
}
|
||||||
{
|
catch(al::context_error& e) {
|
||||||
/* Store the allocated buffer IDs in a separate local list, to avoid
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
* modifying the user storage in case of failure.
|
|
||||||
*/
|
|
||||||
al::vector<ALuint> ids;
|
|
||||||
ids.reserve(static_cast<ALuint>(n));
|
|
||||||
do {
|
|
||||||
ALeffect *effect{AllocEffect(device)};
|
|
||||||
ids.emplace_back(effect->id);
|
|
||||||
} while(--n);
|
|
||||||
std::copy(ids.cbegin(), ids.cend(), effects);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects)
|
AL_API DECL_FUNC2(void, alDeleteEffects, ALsizei,n, const ALuint*,effects)
|
||||||
START_API_FUNC
|
FORCE_ALIGN void AL_APIENTRY alDeleteEffectsDirect(ALCcontext *context, ALsizei n,
|
||||||
{
|
const ALuint *effects) noexcept
|
||||||
ContextRef context{GetContextRef()};
|
try {
|
||||||
if(!context) UNLIKELY return;
|
if(n < 0)
|
||||||
|
throw al::context_error{AL_INVALID_VALUE, "Deleting %d effects", n};
|
||||||
if(n < 0) UNLIKELY
|
|
||||||
context->setError(AL_INVALID_VALUE, "Deleting %d effects", n);
|
|
||||||
if(n <= 0) UNLIKELY return;
|
if(n <= 0) UNLIKELY return;
|
||||||
|
|
||||||
ALCdevice *device{context->mALDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||||
|
|
||||||
/* First try to find any effects that are invalid. */
|
/* 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
|
||||||
{ return !eid || LookupEffect(device, eid) != nullptr; };
|
{ return !eid || LookupEffect(device, eid) != nullptr; };
|
||||||
|
|
||||||
const ALuint *effects_end = effects + n;
|
const al::span eids{effects, static_cast<ALuint>(n)};
|
||||||
auto inveffect = std::find_if_not(effects, effects_end, validate_effect);
|
auto inveffect = std::find_if_not(eids.begin(), eids.end(), validate_effect);
|
||||||
if(inveffect != effects_end) UNLIKELY
|
if(inveffect != eids.end())
|
||||||
{
|
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", *inveffect};
|
||||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", *inveffect);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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
|
||||||
{
|
{
|
||||||
ALeffect *effect{eid ? LookupEffect(device, eid) : nullptr};
|
if(ALeffect *effect{eid ? LookupEffect(device, eid) : nullptr})
|
||||||
if(effect) FreeEffect(device, effect);
|
FreeEffect(device, effect);
|
||||||
};
|
};
|
||||||
std::for_each(effects, effects_end, delete_effect);
|
std::for_each(eids.begin(), eids.end(), delete_effect);
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect)
|
AL_API DECL_FUNC1(ALboolean, alIsEffect, ALuint,effect)
|
||||||
START_API_FUNC
|
FORCE_ALIGN ALboolean AL_APIENTRY alIsEffectDirect(ALCcontext *context, ALuint effect) noexcept
|
||||||
{
|
{
|
||||||
ContextRef context{GetContextRef()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
if(context) LIKELY
|
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||||
{
|
if(!effect || LookupEffect(device, effect))
|
||||||
ALCdevice *device{context->mALDevice.get()};
|
return AL_TRUE;
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
|
||||||
if(!effect || LookupEffect(device, effect))
|
|
||||||
return AL_TRUE;
|
|
||||||
}
|
|
||||||
return AL_FALSE;
|
return AL_FALSE;
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value)
|
|
||||||
START_API_FUNC
|
|
||||||
{
|
|
||||||
ContextRef context{GetContextRef()};
|
|
||||||
if(!context) UNLIKELY return;
|
|
||||||
|
|
||||||
|
AL_API DECL_FUNC3(void, alEffecti, ALuint,effect, ALenum,param, ALint,value)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||||
|
ALint value) noexcept
|
||||||
|
try {
|
||||||
ALCdevice *device{context->mALDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||||
|
|
||||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||||
if(!aleffect) UNLIKELY
|
if(!aleffect)
|
||||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||||
else if(param == AL_EFFECT_TYPE)
|
|
||||||
|
switch(param)
|
||||||
{
|
{
|
||||||
bool isOk{value == AL_EFFECT_NULL};
|
case AL_EFFECT_TYPE:
|
||||||
if(!isOk)
|
if(value != AL_EFFECT_NULL)
|
||||||
{
|
{
|
||||||
for(const EffectList &effectitem : gEffectList)
|
auto check_effect = [value](const EffectList &item) -> bool
|
||||||
{
|
{ return value == item.val && !DisabledEffects.test(item.type); };
|
||||||
if(value == effectitem.val && !DisabledEffects[effectitem.type])
|
if(!std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect))
|
||||||
{
|
throw al::context_error{AL_INVALID_VALUE, "Effect type 0x%04x not supported",
|
||||||
isOk = true;
|
value};
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isOk)
|
InitEffectParams(aleffect, value);
|
||||||
InitEffectParams(aleffect, value);
|
|
||||||
else
|
|
||||||
context->setError(AL_INVALID_VALUE, "Effect type 0x%04x not supported", value);
|
|
||||||
}
|
|
||||||
else try
|
|
||||||
{
|
|
||||||
/* Call the appropriate handler */
|
|
||||||
ALeffect_setParami(aleffect, param, value);
|
|
||||||
}
|
|
||||||
catch(effect_exception &e) {
|
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *values)
|
|
||||||
START_API_FUNC
|
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
case AL_EFFECT_TYPE:
|
|
||||||
alEffecti(effect, param, values[0]);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextRef context{GetContextRef()};
|
/* Call the appropriate handler */
|
||||||
if(!context) UNLIKELY return;
|
std::visit([aleffect,param,value](auto &arg)
|
||||||
|
{
|
||||||
|
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||||
|
using PropType = typename Type::prop_type;
|
||||||
|
return arg.SetParami(std::get<PropType>(aleffect->Props), param, value);
|
||||||
|
}, aleffect->PropsVariant);
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
AL_API DECL_FUNC3(void, alEffectiv, ALuint,effect, ALenum,param, const ALint*,values)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alEffectivDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||||
|
const ALint *values) noexcept
|
||||||
|
try {
|
||||||
|
switch(param)
|
||||||
|
{
|
||||||
|
case AL_EFFECT_TYPE:
|
||||||
|
alEffectiDirect(context, effect, param, *values);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ALCdevice *device{context->mALDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||||
|
|
||||||
|
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||||
|
if(!aleffect)
|
||||||
|
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||||
|
|
||||||
|
/* Call the appropriate handler */
|
||||||
|
std::visit([aleffect,param,values](auto &arg)
|
||||||
|
{
|
||||||
|
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||||
|
using PropType = typename Type::prop_type;
|
||||||
|
return arg.SetParamiv(std::get<PropType>(aleffect->Props), param, values);
|
||||||
|
}, aleffect->PropsVariant);
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
AL_API DECL_FUNC3(void, alEffectf, ALuint,effect, ALenum,param, ALfloat,value)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alEffectfDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||||
|
ALfloat value) noexcept
|
||||||
|
try {
|
||||||
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
|
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||||
|
|
||||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||||
if(!aleffect) UNLIKELY
|
if(!aleffect) UNLIKELY
|
||||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||||
else try
|
|
||||||
|
/* Call the appropriate handler */
|
||||||
|
std::visit([aleffect,param,value](auto &arg)
|
||||||
{
|
{
|
||||||
/* Call the appropriate handler */
|
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||||
ALeffect_setParamiv(aleffect, param, values);
|
using PropType = typename Type::prop_type;
|
||||||
}
|
return arg.SetParamf(std::get<PropType>(aleffect->Props), param, value);
|
||||||
catch(effect_exception &e) {
|
}, aleffect->PropsVariant);
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
}
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat value)
|
|
||||||
START_API_FUNC
|
|
||||||
{
|
|
||||||
ContextRef context{GetContextRef()};
|
|
||||||
if(!context) UNLIKELY return;
|
|
||||||
|
|
||||||
|
AL_API DECL_FUNC3(void, alEffectfv, ALuint,effect, ALenum,param, const ALfloat*,values)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||||
|
const ALfloat *values) noexcept
|
||||||
|
try {
|
||||||
ALCdevice *device{context->mALDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||||
|
|
||||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||||
if(!aleffect) UNLIKELY
|
if(!aleffect)
|
||||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||||
else try
|
|
||||||
|
/* Call the appropriate handler */
|
||||||
|
std::visit([aleffect,param,values](auto &arg)
|
||||||
{
|
{
|
||||||
/* Call the appropriate handler */
|
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||||
ALeffect_setParamf(aleffect, param, value);
|
using PropType = typename Type::prop_type;
|
||||||
}
|
return arg.SetParamfv(std::get<PropType>(aleffect->Props), param, values);
|
||||||
catch(effect_exception &e) {
|
}, aleffect->PropsVariant);
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
AL_API void AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *values)
|
|
||||||
START_API_FUNC
|
|
||||||
{
|
|
||||||
ContextRef context{GetContextRef()};
|
|
||||||
if(!context) UNLIKELY return;
|
|
||||||
|
|
||||||
ALCdevice *device{context->mALDevice.get()};
|
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
|
||||||
|
|
||||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
|
||||||
if(!aleffect) UNLIKELY
|
|
||||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
|
||||||
else try
|
|
||||||
{
|
|
||||||
/* Call the appropriate handler */
|
|
||||||
ALeffect_setParamfv(aleffect, param, values);
|
|
||||||
}
|
|
||||||
catch(effect_exception &e) {
|
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value)
|
|
||||||
START_API_FUNC
|
|
||||||
{
|
|
||||||
ContextRef context{GetContextRef()};
|
|
||||||
if(!context) UNLIKELY return;
|
|
||||||
|
|
||||||
|
AL_API DECL_FUNC3(void, alGetEffecti, ALuint,effect, ALenum,param, ALint*,value)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||||
|
ALint *value) noexcept
|
||||||
|
try {
|
||||||
ALCdevice *device{context->mALDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||||
|
|
||||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||||
if(!aleffect) UNLIKELY
|
if(!aleffect)
|
||||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||||
else if(param == AL_EFFECT_TYPE)
|
|
||||||
|
switch(param)
|
||||||
|
{
|
||||||
|
case AL_EFFECT_TYPE:
|
||||||
*value = aleffect->type;
|
*value = aleffect->type;
|
||||||
else try
|
|
||||||
{
|
|
||||||
/* Call the appropriate handler */
|
|
||||||
ALeffect_getParami(aleffect, param, value);
|
|
||||||
}
|
|
||||||
catch(effect_exception &e) {
|
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *values)
|
|
||||||
START_API_FUNC
|
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
case AL_EFFECT_TYPE:
|
|
||||||
alGetEffecti(effect, param, values);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextRef context{GetContextRef()};
|
/* Call the appropriate handler */
|
||||||
if(!context) UNLIKELY return;
|
std::visit([aleffect,param,value](auto &arg)
|
||||||
|
{
|
||||||
|
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||||
|
using PropType = typename Type::prop_type;
|
||||||
|
return arg.GetParami(std::get<PropType>(aleffect->Props), param, value);
|
||||||
|
}, aleffect->PropsVariant);
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
AL_API DECL_FUNC3(void, alGetEffectiv, ALuint,effect, ALenum,param, ALint*,values)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alGetEffectivDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||||
|
ALint *values) noexcept
|
||||||
|
try {
|
||||||
|
switch(param)
|
||||||
|
{
|
||||||
|
case AL_EFFECT_TYPE:
|
||||||
|
alGetEffectiDirect(context, effect, param, values);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ALCdevice *device{context->mALDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||||
|
|
||||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||||
if(!aleffect) UNLIKELY
|
if(!aleffect)
|
||||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||||
else try
|
|
||||||
|
/* Call the appropriate handler */
|
||||||
|
std::visit([aleffect,param,values](auto &arg)
|
||||||
{
|
{
|
||||||
/* Call the appropriate handler */
|
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||||
ALeffect_getParamiv(aleffect, param, values);
|
using PropType = typename Type::prop_type;
|
||||||
}
|
return arg.GetParamiv(std::get<PropType>(aleffect->Props), param, values);
|
||||||
catch(effect_exception &e) {
|
}, aleffect->PropsVariant);
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
}
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *value)
|
|
||||||
START_API_FUNC
|
|
||||||
{
|
|
||||||
ContextRef context{GetContextRef()};
|
|
||||||
if(!context) UNLIKELY return;
|
|
||||||
|
|
||||||
|
AL_API DECL_FUNC3(void, alGetEffectf, ALuint,effect, ALenum,param, ALfloat*,value)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||||
|
ALfloat *value) noexcept
|
||||||
|
try {
|
||||||
ALCdevice *device{context->mALDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||||
|
|
||||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||||
if(!aleffect) UNLIKELY
|
if(!aleffect)
|
||||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||||
else try
|
|
||||||
|
/* Call the appropriate handler */
|
||||||
|
std::visit([aleffect,param,value](auto &arg)
|
||||||
{
|
{
|
||||||
/* Call the appropriate handler */
|
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||||
ALeffect_getParamf(aleffect, param, value);
|
using PropType = typename Type::prop_type;
|
||||||
}
|
return arg.GetParamf(std::get<PropType>(aleffect->Props), param, value);
|
||||||
catch(effect_exception &e) {
|
}, aleffect->PropsVariant);
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
}
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *values)
|
|
||||||
START_API_FUNC
|
|
||||||
{
|
|
||||||
ContextRef context{GetContextRef()};
|
|
||||||
if(!context) UNLIKELY return;
|
|
||||||
|
|
||||||
|
AL_API DECL_FUNC3(void, alGetEffectfv, ALuint,effect, ALenum,param, ALfloat*,values)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||||
|
ALfloat *values) noexcept
|
||||||
|
try {
|
||||||
ALCdevice *device{context->mALDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||||
|
|
||||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||||
if(!aleffect) UNLIKELY
|
if(!aleffect)
|
||||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||||
else try
|
|
||||||
|
/* Call the appropriate handler */
|
||||||
|
std::visit([aleffect,param,values](auto &arg)
|
||||||
{
|
{
|
||||||
/* Call the appropriate handler */
|
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||||
ALeffect_getParamfv(aleffect, param, values);
|
using PropType = typename Type::prop_type;
|
||||||
}
|
return arg.GetParamfv(std::get<PropType>(aleffect->Props), param, values);
|
||||||
catch(effect_exception &e) {
|
}, aleffect->PropsVariant);
|
||||||
context->setError(e.errorCode(), "%s", e.what());
|
}
|
||||||
}
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
|
|
||||||
void InitEffect(ALeffect *effect)
|
void InitEffect(ALeffect *effect)
|
||||||
|
|
@ -546,26 +500,43 @@ void InitEffect(ALeffect *effect)
|
||||||
InitEffectParams(effect, AL_EFFECT_NULL);
|
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ALeffect::SetName(ALCcontext* context, ALuint id, std::string_view name)
|
||||||
|
{
|
||||||
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
|
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||||
|
|
||||||
|
auto effect = LookupEffect(device, id);
|
||||||
|
if(!effect)
|
||||||
|
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", id};
|
||||||
|
|
||||||
|
device->mEffectNames.insert_or_assign(id, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
EffectSubList::~EffectSubList()
|
EffectSubList::~EffectSubList()
|
||||||
{
|
{
|
||||||
|
if(!Effects)
|
||||||
|
return;
|
||||||
|
|
||||||
uint64_t usemask{~FreeMask};
|
uint64_t usemask{~FreeMask};
|
||||||
while(usemask)
|
while(usemask)
|
||||||
{
|
{
|
||||||
const int idx{al::countr_zero(usemask)};
|
const int idx{al::countr_zero(usemask)};
|
||||||
al::destroy_at(Effects+idx);
|
std::destroy_at(al::to_address(Effects->begin()+idx));
|
||||||
usemask &= ~(1_u64 << idx);
|
usemask &= ~(1_u64 << idx);
|
||||||
}
|
}
|
||||||
FreeMask = ~usemask;
|
FreeMask = ~usemask;
|
||||||
al_free(Effects);
|
SubListAllocator{}.deallocate(Effects, 1);
|
||||||
Effects = nullptr;
|
Effects = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define DECL(x) { #x, EFX_REVERB_PRESET_##x }
|
struct EffectPreset {
|
||||||
static const struct {
|
const char name[32]; /* NOLINT(*-avoid-c-arrays) */
|
||||||
const char name[32];
|
|
||||||
EFXEAXREVERBPROPERTIES props;
|
EFXEAXREVERBPROPERTIES props;
|
||||||
} reverblist[] = {
|
};
|
||||||
|
#define DECL(x) EffectPreset{#x, EFX_REVERB_PRESET_##x}
|
||||||
|
static constexpr std::array reverblist{
|
||||||
DECL(GENERIC),
|
DECL(GENERIC),
|
||||||
DECL(PADDEDCELL),
|
DECL(PADDEDCELL),
|
||||||
DECL(ROOM),
|
DECL(ROOM),
|
||||||
|
|
@ -695,61 +666,62 @@ static const struct {
|
||||||
};
|
};
|
||||||
#undef DECL
|
#undef DECL
|
||||||
|
|
||||||
void LoadReverbPreset(const char *name, ALeffect *effect)
|
void LoadReverbPreset(const std::string_view name, ALeffect *effect)
|
||||||
{
|
{
|
||||||
if(al::strcasecmp(name, "NONE") == 0)
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
|
if(al::case_compare(name, "NONE"sv) == 0)
|
||||||
{
|
{
|
||||||
InitEffectParams(effect, AL_EFFECT_NULL);
|
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||||
TRACE("Loading reverb '%s'\n", "NONE");
|
TRACE("Loading reverb '%s'\n", "NONE");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!DisabledEffects[EAXREVERB_EFFECT])
|
if(!DisabledEffects.test(EAXREVERB_EFFECT))
|
||||||
InitEffectParams(effect, AL_EFFECT_EAXREVERB);
|
InitEffectParams(effect, AL_EFFECT_EAXREVERB);
|
||||||
else if(!DisabledEffects[REVERB_EFFECT])
|
else if(!DisabledEffects.test(REVERB_EFFECT))
|
||||||
InitEffectParams(effect, AL_EFFECT_REVERB);
|
InitEffectParams(effect, AL_EFFECT_REVERB);
|
||||||
else
|
else
|
||||||
InitEffectParams(effect, AL_EFFECT_NULL);
|
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||||
for(const auto &reverbitem : reverblist)
|
for(const auto &reverbitem : reverblist)
|
||||||
{
|
{
|
||||||
const EFXEAXREVERBPROPERTIES *props;
|
if(al::case_compare(name, std::data(reverbitem.name)) != 0)
|
||||||
|
|
||||||
if(al::strcasecmp(name, reverbitem.name) != 0)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
TRACE("Loading reverb '%s'\n", reverbitem.name);
|
TRACE("Loading reverb '%s'\n", std::data(reverbitem.name));
|
||||||
props = &reverbitem.props;
|
const auto &props = reverbitem.props;
|
||||||
effect->Props.Reverb.Density = props->flDensity;
|
auto &dst = std::get<ReverbProps>(effect->Props);
|
||||||
effect->Props.Reverb.Diffusion = props->flDiffusion;
|
dst.Density = props.flDensity;
|
||||||
effect->Props.Reverb.Gain = props->flGain;
|
dst.Diffusion = props.flDiffusion;
|
||||||
effect->Props.Reverb.GainHF = props->flGainHF;
|
dst.Gain = props.flGain;
|
||||||
effect->Props.Reverb.GainLF = props->flGainLF;
|
dst.GainHF = props.flGainHF;
|
||||||
effect->Props.Reverb.DecayTime = props->flDecayTime;
|
dst.GainLF = props.flGainLF;
|
||||||
effect->Props.Reverb.DecayHFRatio = props->flDecayHFRatio;
|
dst.DecayTime = props.flDecayTime;
|
||||||
effect->Props.Reverb.DecayLFRatio = props->flDecayLFRatio;
|
dst.DecayHFRatio = props.flDecayHFRatio;
|
||||||
effect->Props.Reverb.ReflectionsGain = props->flReflectionsGain;
|
dst.DecayLFRatio = props.flDecayLFRatio;
|
||||||
effect->Props.Reverb.ReflectionsDelay = props->flReflectionsDelay;
|
dst.ReflectionsGain = props.flReflectionsGain;
|
||||||
effect->Props.Reverb.ReflectionsPan[0] = props->flReflectionsPan[0];
|
dst.ReflectionsDelay = props.flReflectionsDelay;
|
||||||
effect->Props.Reverb.ReflectionsPan[1] = props->flReflectionsPan[1];
|
dst.ReflectionsPan[0] = props.flReflectionsPan[0];
|
||||||
effect->Props.Reverb.ReflectionsPan[2] = props->flReflectionsPan[2];
|
dst.ReflectionsPan[1] = props.flReflectionsPan[1];
|
||||||
effect->Props.Reverb.LateReverbGain = props->flLateReverbGain;
|
dst.ReflectionsPan[2] = props.flReflectionsPan[2];
|
||||||
effect->Props.Reverb.LateReverbDelay = props->flLateReverbDelay;
|
dst.LateReverbGain = props.flLateReverbGain;
|
||||||
effect->Props.Reverb.LateReverbPan[0] = props->flLateReverbPan[0];
|
dst.LateReverbDelay = props.flLateReverbDelay;
|
||||||
effect->Props.Reverb.LateReverbPan[1] = props->flLateReverbPan[1];
|
dst.LateReverbPan[0] = props.flLateReverbPan[0];
|
||||||
effect->Props.Reverb.LateReverbPan[2] = props->flLateReverbPan[2];
|
dst.LateReverbPan[1] = props.flLateReverbPan[1];
|
||||||
effect->Props.Reverb.EchoTime = props->flEchoTime;
|
dst.LateReverbPan[2] = props.flLateReverbPan[2];
|
||||||
effect->Props.Reverb.EchoDepth = props->flEchoDepth;
|
dst.EchoTime = props.flEchoTime;
|
||||||
effect->Props.Reverb.ModulationTime = props->flModulationTime;
|
dst.EchoDepth = props.flEchoDepth;
|
||||||
effect->Props.Reverb.ModulationDepth = props->flModulationDepth;
|
dst.ModulationTime = props.flModulationTime;
|
||||||
effect->Props.Reverb.AirAbsorptionGainHF = props->flAirAbsorptionGainHF;
|
dst.ModulationDepth = props.flModulationDepth;
|
||||||
effect->Props.Reverb.HFReference = props->flHFReference;
|
dst.AirAbsorptionGainHF = props.flAirAbsorptionGainHF;
|
||||||
effect->Props.Reverb.LFReference = props->flLFReference;
|
dst.HFReference = props.flHFReference;
|
||||||
effect->Props.Reverb.RoomRolloffFactor = props->flRoomRolloffFactor;
|
dst.LFReference = props.flLFReference;
|
||||||
effect->Props.Reverb.DecayHFLimit = props->iDecayHFLimit ? AL_TRUE : AL_FALSE;
|
dst.RoomRolloffFactor = props.flRoomRolloffFactor;
|
||||||
|
dst.DecayHFLimit = props.iDecayHFLimit ? AL_TRUE : AL_FALSE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WARN("Reverb preset '%s' not found\n", name);
|
WARN("Reverb preset '%.*s' not found\n", al::sizei(name), name.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValidEffectType(ALenum type) noexcept
|
bool IsValidEffectType(ALenum type) noexcept
|
||||||
|
|
@ -757,10 +729,7 @@ bool IsValidEffectType(ALenum type) noexcept
|
||||||
if(type == AL_EFFECT_NULL)
|
if(type == AL_EFFECT_NULL)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
for(const auto &effect_item : gEffectList)
|
auto check_effect = [type](const EffectList &item) noexcept -> bool
|
||||||
{
|
{ return type == item.val && !DisabledEffects.test(item.type); };
|
||||||
if(type == effect_item.val && !DisabledEffects[effect_item.type])
|
return std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,20 @@
|
||||||
#ifndef AL_EFFECT_H
|
#ifndef AL_EFFECT_H
|
||||||
#define AL_EFFECT_H
|
#define AL_EFFECT_H
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <bitset>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string_view>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
|
#include "AL/alc.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
#include "al/effects/effects.h"
|
#include "almalloc.h"
|
||||||
#include "alc/effects/base.h"
|
#include "alnumeric.h"
|
||||||
|
#include "core/effects/base.h"
|
||||||
|
#include "effects/effects.h"
|
||||||
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
|
@ -27,36 +36,55 @@ enum {
|
||||||
|
|
||||||
MAX_EFFECTS
|
MAX_EFFECTS
|
||||||
};
|
};
|
||||||
extern bool DisabledEffects[MAX_EFFECTS];
|
inline std::bitset<MAX_EFFECTS> DisabledEffects;
|
||||||
|
|
||||||
extern float ReverbBoost;
|
|
||||||
|
|
||||||
struct EffectList {
|
struct EffectList {
|
||||||
const char name[16];
|
const char name[16]; /* NOLINT(*-avoid-c-arrays) */
|
||||||
int type;
|
ALuint type;
|
||||||
ALenum val;
|
ALenum val;
|
||||||
};
|
};
|
||||||
extern const EffectList gEffectList[16];
|
extern const std::array<EffectList,16> gEffectList;
|
||||||
|
|
||||||
|
using EffectHandlerVariant = std::variant<NullEffectHandler,ReverbEffectHandler,
|
||||||
|
StdReverbEffectHandler,AutowahEffectHandler,ChorusEffectHandler,CompressorEffectHandler,
|
||||||
|
DistortionEffectHandler,EchoEffectHandler,EqualizerEffectHandler,FlangerEffectHandler,
|
||||||
|
FshifterEffectHandler,ModulatorEffectHandler,PshifterEffectHandler,VmorpherEffectHandler,
|
||||||
|
DedicatedDialogEffectHandler,DedicatedLfeEffectHandler,ConvolutionEffectHandler>;
|
||||||
|
|
||||||
struct ALeffect {
|
struct ALeffect {
|
||||||
// Effect type (AL_EFFECT_NULL, ...)
|
// Effect type (AL_EFFECT_NULL, ...)
|
||||||
ALenum type{AL_EFFECT_NULL};
|
ALenum type{AL_EFFECT_NULL};
|
||||||
|
|
||||||
|
EffectHandlerVariant PropsVariant;
|
||||||
EffectProps Props{};
|
EffectProps Props{};
|
||||||
|
|
||||||
const EffectVtable *vtab{nullptr};
|
|
||||||
|
|
||||||
/* Self ID */
|
/* Self ID */
|
||||||
ALuint id{0u};
|
ALuint id{0u};
|
||||||
|
|
||||||
DISABLE_ALLOC()
|
static void SetName(ALCcontext *context, ALuint id, std::string_view name);
|
||||||
|
|
||||||
|
DISABLE_ALLOC
|
||||||
};
|
};
|
||||||
|
|
||||||
void InitEffect(ALeffect *effect);
|
void InitEffect(ALeffect *effect);
|
||||||
|
|
||||||
void LoadReverbPreset(const char *name, ALeffect *effect);
|
void LoadReverbPreset(const std::string_view name, ALeffect *effect);
|
||||||
|
|
||||||
bool IsValidEffectType(ALenum type) noexcept;
|
bool IsValidEffectType(ALenum type) noexcept;
|
||||||
|
|
||||||
|
struct EffectSubList {
|
||||||
|
uint64_t FreeMask{~0_u64};
|
||||||
|
gsl::owner<std::array<ALeffect,64>*> Effects{nullptr}; /* 64 */
|
||||||
|
|
||||||
|
EffectSubList() noexcept = default;
|
||||||
|
EffectSubList(const EffectSubList&) = delete;
|
||||||
|
EffectSubList(EffectSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Effects{rhs.Effects}
|
||||||
|
{ rhs.FreeMask = ~0_u64; rhs.Effects = nullptr; }
|
||||||
|
~EffectSubList();
|
||||||
|
|
||||||
|
EffectSubList& operator=(const EffectSubList&) = delete;
|
||||||
|
EffectSubList& operator=(EffectSubList&& rhs) noexcept
|
||||||
|
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Effects, rhs.Effects); return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.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"
|
||||||
#endif // ALSOFT_EAX
|
#endif // ALSOFT_EAX
|
||||||
|
|
@ -20,100 +21,87 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void Autowah_setParamf(EffectProps *props, ALenum param, float val)
|
constexpr EffectProps genDefaultProps() noexcept
|
||||||
|
{
|
||||||
|
AutowahProps props{};
|
||||||
|
props.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
|
||||||
|
props.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
|
||||||
|
props.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
|
||||||
|
props.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
const EffectProps AutowahEffectProps{genDefaultProps()};
|
||||||
|
|
||||||
|
void AutowahEffectHandler::SetParami(AutowahProps&, ALenum param, int)
|
||||||
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
|
||||||
|
void AutowahEffectHandler::SetParamiv(AutowahProps&, ALenum param, const int*)
|
||||||
|
{
|
||||||
|
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
|
||||||
|
param};
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutowahEffectHandler::SetParamf(AutowahProps &props, ALenum param, float val)
|
||||||
{
|
{
|
||||||
switch(param)
|
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Autowah attack time out of range"};
|
||||||
props->Autowah.AttackTime = val;
|
props.AttackTime = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Autowah release time out of range"};
|
||||||
props->Autowah.ReleaseTime = val;
|
props.ReleaseTime = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Autowah resonance out of range"};
|
||||||
props->Autowah.Resonance = val;
|
props.Resonance = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Autowah peak gain out of range"};
|
||||||
props->Autowah.PeakGain = val;
|
props.PeakGain = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Autowah_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
void AutowahEffectHandler::SetParamfv(AutowahProps &props, ALenum param, const float *vals)
|
||||||
{ Autowah_setParamf(props, param, vals[0]); }
|
{ SetParamf(props, param, *vals); }
|
||||||
|
|
||||||
void Autowah_setParami(EffectProps*, ALenum param, int)
|
void AutowahEffectHandler::GetParami(const AutowahProps&, ALenum param, int*)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
|
||||||
void Autowah_setParamiv(EffectProps*, ALenum param, const int*)
|
void AutowahEffectHandler::GetParamiv(const AutowahProps&, ALenum param, int*)
|
||||||
{
|
{
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
|
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Autowah_getParamf(const EffectProps *props, ALenum param, float *val)
|
void AutowahEffectHandler::GetParamf(const AutowahProps &props, ALenum param, float *val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_AUTOWAH_ATTACK_TIME:
|
case AL_AUTOWAH_ATTACK_TIME: *val = props.AttackTime; break;
|
||||||
*val = props->Autowah.AttackTime;
|
case AL_AUTOWAH_RELEASE_TIME: *val = props.ReleaseTime; break;
|
||||||
break;
|
case AL_AUTOWAH_RESONANCE: *val = props.Resonance; break;
|
||||||
|
case AL_AUTOWAH_PEAK_GAIN: *val = props.PeakGain; break;
|
||||||
case AL_AUTOWAH_RELEASE_TIME:
|
|
||||||
*val = props->Autowah.ReleaseTime;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_AUTOWAH_RESONANCE:
|
|
||||||
*val = props->Autowah.Resonance;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_AUTOWAH_PEAK_GAIN:
|
|
||||||
*val = props->Autowah.PeakGain;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
void Autowah_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
void AutowahEffectHandler::GetParamfv(const AutowahProps &props, ALenum param, float *vals)
|
||||||
{ Autowah_getParamf(props, param, vals); }
|
{ GetParamf(props, param, vals); }
|
||||||
|
|
||||||
void Autowah_getParami(const EffectProps*, ALenum param, int*)
|
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
|
|
||||||
void Autowah_getParamiv(const EffectProps*, ALenum param, int*)
|
|
||||||
{
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
|
|
||||||
EffectProps genDefaultProps() noexcept
|
|
||||||
{
|
|
||||||
EffectProps props{};
|
|
||||||
props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
|
|
||||||
props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
|
|
||||||
props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
|
|
||||||
props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
|
|
||||||
return props;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
DEFINE_ALEFFECT_VTABLE(Autowah);
|
|
||||||
|
|
||||||
const EffectProps AutowahEffectProps{genDefaultProps()};
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -189,62 +177,62 @@ template<>
|
||||||
throw Exception{message};
|
throw Exception{message};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
bool EaxAutowahCommitter::commit(const EAXAUTOWAHPROPERTIES &props)
|
||||||
bool AutowahCommitter::commit(const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
if(props.mType == mEaxProps.mType
|
if(auto *cur = std::get_if<EAXAUTOWAHPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||||
&& mEaxProps.mAutowah.flAttackTime == props.mAutowah.flAttackTime
|
|
||||||
&& mEaxProps.mAutowah.flReleaseTime == props.mAutowah.flReleaseTime
|
|
||||||
&& mEaxProps.mAutowah.lResonance == props.mAutowah.lResonance
|
|
||||||
&& mEaxProps.mAutowah.lPeakLevel == props.mAutowah.lPeakLevel)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mEaxProps = props;
|
mEaxProps = props;
|
||||||
|
mAlProps = [&]{
|
||||||
mAlProps.Autowah.AttackTime = props.mAutowah.flAttackTime;
|
AutowahProps ret{};
|
||||||
mAlProps.Autowah.ReleaseTime = props.mAutowah.flReleaseTime;
|
ret.AttackTime = props.flAttackTime;
|
||||||
mAlProps.Autowah.Resonance = level_mb_to_gain(static_cast<float>(props.mAutowah.lResonance));
|
ret.ReleaseTime = props.flReleaseTime;
|
||||||
mAlProps.Autowah.PeakGain = level_mb_to_gain(static_cast<float>(props.mAutowah.lPeakLevel));
|
ret.Resonance = level_mb_to_gain(static_cast<float>(props.lResonance));
|
||||||
|
ret.PeakGain = level_mb_to_gain(static_cast<float>(props.lPeakLevel));
|
||||||
|
return ret;
|
||||||
|
}();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxAutowahCommitter::SetDefaults(EaxEffectProps &props)
|
||||||
void AutowahCommitter::SetDefaults(EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
props.mType = EaxEffectType::Autowah;
|
static constexpr EAXAUTOWAHPROPERTIES defprops{[]
|
||||||
props.mAutowah.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME;
|
{
|
||||||
props.mAutowah.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME;
|
EAXAUTOWAHPROPERTIES ret{};
|
||||||
props.mAutowah.lResonance = EAXAUTOWAH_DEFAULTRESONANCE;
|
ret.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME;
|
||||||
props.mAutowah.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL;
|
ret.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME;
|
||||||
|
ret.lResonance = EAXAUTOWAH_DEFAULTRESONANCE;
|
||||||
|
ret.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL;
|
||||||
|
return ret;
|
||||||
|
}()};
|
||||||
|
props = defprops;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxAutowahCommitter::Get(const EaxCall &call, const EAXAUTOWAHPROPERTIES &props)
|
||||||
void AutowahCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case EAXAUTOWAH_NONE: break;
|
case EAXAUTOWAH_NONE: break;
|
||||||
case EAXAUTOWAH_ALLPARAMETERS: call.set_value<Exception>(props.mAutowah); break;
|
case EAXAUTOWAH_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||||
case EAXAUTOWAH_ATTACKTIME: call.set_value<Exception>(props.mAutowah.flAttackTime); break;
|
case EAXAUTOWAH_ATTACKTIME: call.set_value<Exception>(props.flAttackTime); break;
|
||||||
case EAXAUTOWAH_RELEASETIME: call.set_value<Exception>(props.mAutowah.flReleaseTime); break;
|
case EAXAUTOWAH_RELEASETIME: call.set_value<Exception>(props.flReleaseTime); break;
|
||||||
case EAXAUTOWAH_RESONANCE: call.set_value<Exception>(props.mAutowah.lResonance); break;
|
case EAXAUTOWAH_RESONANCE: call.set_value<Exception>(props.lResonance); break;
|
||||||
case EAXAUTOWAH_PEAKLEVEL: call.set_value<Exception>(props.mAutowah.lPeakLevel); break;
|
case EAXAUTOWAH_PEAKLEVEL: call.set_value<Exception>(props.lPeakLevel); break;
|
||||||
default: fail_unknown_property_id();
|
default: fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxAutowahCommitter::Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props)
|
||||||
void AutowahCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case EAXAUTOWAH_NONE: break;
|
case EAXAUTOWAH_NONE: break;
|
||||||
case EAXAUTOWAH_ALLPARAMETERS: defer<AllValidator>(call, props.mAutowah); break;
|
case EAXAUTOWAH_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||||
case EAXAUTOWAH_ATTACKTIME: defer<AttackTimeValidator>(call, props.mAutowah.flAttackTime); break;
|
case EAXAUTOWAH_ATTACKTIME: defer<AttackTimeValidator>(call, props.flAttackTime); break;
|
||||||
case EAXAUTOWAH_RELEASETIME: defer<ReleaseTimeValidator>(call, props.mAutowah.flReleaseTime); break;
|
case EAXAUTOWAH_RELEASETIME: defer<ReleaseTimeValidator>(call, props.flReleaseTime); break;
|
||||||
case EAXAUTOWAH_RESONANCE: defer<ResonanceValidator>(call, props.mAutowah.lResonance); break;
|
case EAXAUTOWAH_RESONANCE: defer<ResonanceValidator>(call, props.lResonance); break;
|
||||||
case EAXAUTOWAH_PEAKLEVEL: defer<PeakLevelValidator>(call, props.mAutowah.lPeakLevel); break;
|
case EAXAUTOWAH_PEAKLEVEL: defer<PeakLevelValidator>(call, props.lPeakLevel); break;
|
||||||
default: fail_unknown_property_id();
|
default: fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,17 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
#include "alc/effects/base.h"
|
|
||||||
#include "aloptional.h"
|
|
||||||
#include "core/logging.h"
|
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include "alnumeric.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"
|
||||||
#endif // ALSOFT_EAX
|
#endif // ALSOFT_EAX
|
||||||
|
|
@ -27,16 +25,16 @@ static_assert(FlangerMaxDelay >= AL_FLANGER_MAX_DELAY, "Flanger max delay too sm
|
||||||
static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
|
static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
|
||||||
static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
|
static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
|
||||||
|
|
||||||
inline al::optional<ChorusWaveform> WaveformFromEnum(ALenum type)
|
constexpr std::optional<ChorusWaveform> WaveformFromEnum(ALenum type) noexcept
|
||||||
{
|
{
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case AL_CHORUS_WAVEFORM_SINUSOID: return ChorusWaveform::Sinusoid;
|
case AL_CHORUS_WAVEFORM_SINUSOID: return ChorusWaveform::Sinusoid;
|
||||||
case AL_CHORUS_WAVEFORM_TRIANGLE: return ChorusWaveform::Triangle;
|
case AL_CHORUS_WAVEFORM_TRIANGLE: return ChorusWaveform::Triangle;
|
||||||
}
|
}
|
||||||
return al::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
inline ALenum EnumFromWaveform(ChorusWaveform type)
|
constexpr ALenum EnumFromWaveform(ChorusWaveform type)
|
||||||
{
|
{
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
|
|
@ -46,13 +44,41 @@ inline ALenum EnumFromWaveform(ChorusWaveform type)
|
||||||
throw std::runtime_error{"Invalid chorus waveform: "+std::to_string(static_cast<int>(type))};
|
throw std::runtime_error{"Invalid chorus waveform: "+std::to_string(static_cast<int>(type))};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chorus_setParami(EffectProps *props, ALenum param, int val)
|
constexpr EffectProps genDefaultChorusProps() noexcept
|
||||||
|
{
|
||||||
|
ChorusProps props{};
|
||||||
|
props.Waveform = WaveformFromEnum(AL_CHORUS_DEFAULT_WAVEFORM).value();
|
||||||
|
props.Phase = AL_CHORUS_DEFAULT_PHASE;
|
||||||
|
props.Rate = AL_CHORUS_DEFAULT_RATE;
|
||||||
|
props.Depth = AL_CHORUS_DEFAULT_DEPTH;
|
||||||
|
props.Feedback = AL_CHORUS_DEFAULT_FEEDBACK;
|
||||||
|
props.Delay = AL_CHORUS_DEFAULT_DELAY;
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr EffectProps genDefaultFlangerProps() noexcept
|
||||||
|
{
|
||||||
|
ChorusProps props{};
|
||||||
|
props.Waveform = WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM).value();
|
||||||
|
props.Phase = AL_FLANGER_DEFAULT_PHASE;
|
||||||
|
props.Rate = AL_FLANGER_DEFAULT_RATE;
|
||||||
|
props.Depth = AL_FLANGER_DEFAULT_DEPTH;
|
||||||
|
props.Feedback = AL_FLANGER_DEFAULT_FEEDBACK;
|
||||||
|
props.Delay = AL_FLANGER_DEFAULT_DELAY;
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
const EffectProps ChorusEffectProps{genDefaultChorusProps()};
|
||||||
|
|
||||||
|
void ChorusEffectHandler::SetParami(ChorusProps &props, ALenum param, int val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_CHORUS_WAVEFORM:
|
case AL_CHORUS_WAVEFORM:
|
||||||
if(auto formopt = WaveformFromEnum(val))
|
if(auto formopt = WaveformFromEnum(val))
|
||||||
props->Chorus.Waveform = *formopt;
|
props.Waveform = *formopt;
|
||||||
else
|
else
|
||||||
throw effect_exception{AL_INVALID_VALUE, "Invalid chorus waveform: 0x%04x", val};
|
throw effect_exception{AL_INVALID_VALUE, "Invalid chorus waveform: 0x%04x", val};
|
||||||
break;
|
break;
|
||||||
|
|
@ -60,115 +86,89 @@ void Chorus_setParami(EffectProps *props, ALenum param, int val)
|
||||||
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};
|
throw effect_exception{AL_INVALID_VALUE, "Chorus phase out of range: %d", val};
|
||||||
props->Chorus.Phase = val;
|
props.Phase = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Chorus_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
void ChorusEffectHandler::SetParamiv(ChorusProps &props, ALenum param, const int *vals)
|
||||||
{ Chorus_setParami(props, param, vals[0]); }
|
{ SetParami(props, param, *vals); }
|
||||||
void Chorus_setParamf(EffectProps *props, ALenum param, float val)
|
void ChorusEffectHandler::SetParamf(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};
|
throw effect_exception{AL_INVALID_VALUE, "Chorus rate out of range: %f", val};
|
||||||
props->Chorus.Rate = val;
|
props.Rate = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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};
|
throw effect_exception{AL_INVALID_VALUE, "Chorus depth out of range: %f", val};
|
||||||
props->Chorus.Depth = val;
|
props.Depth = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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};
|
throw effect_exception{AL_INVALID_VALUE, "Chorus feedback out of range: %f", val};
|
||||||
props->Chorus.Feedback = val;
|
props.Feedback = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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};
|
throw effect_exception{AL_INVALID_VALUE, "Chorus delay out of range: %f", val};
|
||||||
props->Chorus.Delay = val;
|
props.Delay = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Chorus_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
void ChorusEffectHandler::SetParamfv(ChorusProps &props, ALenum param, const float *vals)
|
||||||
{ Chorus_setParamf(props, param, vals[0]); }
|
{ SetParamf(props, param, *vals); }
|
||||||
|
|
||||||
void Chorus_getParami(const EffectProps *props, ALenum param, int *val)
|
void ChorusEffectHandler::GetParami(const ChorusProps &props, ALenum param, int *val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_CHORUS_WAVEFORM:
|
case AL_CHORUS_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
|
||||||
*val = EnumFromWaveform(props->Chorus.Waveform);
|
case AL_CHORUS_PHASE: *val = props.Phase; break;
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_CHORUS_PHASE:
|
|
||||||
*val = props->Chorus.Phase;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Chorus_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
void ChorusEffectHandler::GetParamiv(const ChorusProps &props, ALenum param, int *vals)
|
||||||
{ Chorus_getParami(props, param, vals); }
|
{ GetParami(props, param, vals); }
|
||||||
void Chorus_getParamf(const EffectProps *props, ALenum param, float *val)
|
void ChorusEffectHandler::GetParamf(const ChorusProps &props, ALenum param, float *val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_CHORUS_RATE:
|
case AL_CHORUS_RATE: *val = props.Rate; break;
|
||||||
*val = props->Chorus.Rate;
|
case AL_CHORUS_DEPTH: *val = props.Depth; break;
|
||||||
break;
|
case AL_CHORUS_FEEDBACK: *val = props.Feedback; break;
|
||||||
|
case AL_CHORUS_DELAY: *val = props.Delay; break;
|
||||||
case AL_CHORUS_DEPTH:
|
|
||||||
*val = props->Chorus.Depth;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_CHORUS_FEEDBACK:
|
|
||||||
*val = props->Chorus.Feedback;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_CHORUS_DELAY:
|
|
||||||
*val = props->Chorus.Delay;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Chorus_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
void ChorusEffectHandler::GetParamfv(const ChorusProps &props, ALenum param, float *vals)
|
||||||
{ Chorus_getParamf(props, param, vals); }
|
{ GetParamf(props, param, vals); }
|
||||||
|
|
||||||
EffectProps genDefaultChorusProps() noexcept
|
|
||||||
{
|
|
||||||
EffectProps props{};
|
|
||||||
props.Chorus.Waveform = *WaveformFromEnum(AL_CHORUS_DEFAULT_WAVEFORM);
|
|
||||||
props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE;
|
|
||||||
props.Chorus.Rate = AL_CHORUS_DEFAULT_RATE;
|
|
||||||
props.Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH;
|
|
||||||
props.Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK;
|
|
||||||
props.Chorus.Delay = AL_CHORUS_DEFAULT_DELAY;
|
|
||||||
return props;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Flanger_setParami(EffectProps *props, ALenum param, int val)
|
const EffectProps FlangerEffectProps{genDefaultFlangerProps()};
|
||||||
|
|
||||||
|
void FlangerEffectHandler::SetParami(ChorusProps &props, ALenum param, int val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_FLANGER_WAVEFORM:
|
case AL_FLANGER_WAVEFORM:
|
||||||
if(auto formopt = WaveformFromEnum(val))
|
if(auto formopt = WaveformFromEnum(val))
|
||||||
props->Chorus.Waveform = *formopt;
|
props.Waveform = *formopt;
|
||||||
else
|
else
|
||||||
throw effect_exception{AL_INVALID_VALUE, "Invalid flanger waveform: 0x%04x", val};
|
throw effect_exception{AL_INVALID_VALUE, "Invalid flanger waveform: 0x%04x", val};
|
||||||
break;
|
break;
|
||||||
|
|
@ -176,127 +176,87 @@ void Flanger_setParami(EffectProps *props, ALenum param, int val)
|
||||||
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};
|
throw effect_exception{AL_INVALID_VALUE, "Flanger phase out of range: %d", val};
|
||||||
props->Chorus.Phase = val;
|
props.Phase = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Flanger_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
void FlangerEffectHandler::SetParamiv(ChorusProps &props, ALenum param, const int *vals)
|
||||||
{ Flanger_setParami(props, param, vals[0]); }
|
{ SetParami(props, param, *vals); }
|
||||||
void Flanger_setParamf(EffectProps *props, ALenum param, float val)
|
void FlangerEffectHandler::SetParamf(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};
|
throw effect_exception{AL_INVALID_VALUE, "Flanger rate out of range: %f", val};
|
||||||
props->Chorus.Rate = val;
|
props.Rate = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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};
|
throw effect_exception{AL_INVALID_VALUE, "Flanger depth out of range: %f", val};
|
||||||
props->Chorus.Depth = val;
|
props.Depth = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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};
|
throw effect_exception{AL_INVALID_VALUE, "Flanger feedback out of range: %f", val};
|
||||||
props->Chorus.Feedback = val;
|
props.Feedback = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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};
|
throw effect_exception{AL_INVALID_VALUE, "Flanger delay out of range: %f", val};
|
||||||
props->Chorus.Delay = val;
|
props.Delay = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Flanger_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
void FlangerEffectHandler::SetParamfv(ChorusProps &props, ALenum param, const float *vals)
|
||||||
{ Flanger_setParamf(props, param, vals[0]); }
|
{ SetParamf(props, param, *vals); }
|
||||||
|
|
||||||
void Flanger_getParami(const EffectProps *props, ALenum param, int *val)
|
void FlangerEffectHandler::GetParami(const ChorusProps &props, ALenum param, int *val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_FLANGER_WAVEFORM:
|
case AL_FLANGER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
|
||||||
*val = EnumFromWaveform(props->Chorus.Waveform);
|
case AL_FLANGER_PHASE: *val = props.Phase; break;
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_FLANGER_PHASE:
|
|
||||||
*val = props->Chorus.Phase;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Flanger_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
void FlangerEffectHandler::GetParamiv(const ChorusProps &props, ALenum param, int *vals)
|
||||||
{ Flanger_getParami(props, param, vals); }
|
{ GetParami(props, param, vals); }
|
||||||
void Flanger_getParamf(const EffectProps *props, ALenum param, float *val)
|
void FlangerEffectHandler::GetParamf(const ChorusProps &props, ALenum param, float *val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_FLANGER_RATE:
|
case AL_FLANGER_RATE: *val = props.Rate; break;
|
||||||
*val = props->Chorus.Rate;
|
case AL_FLANGER_DEPTH: *val = props.Depth; break;
|
||||||
break;
|
case AL_FLANGER_FEEDBACK: *val = props.Feedback; break;
|
||||||
|
case AL_FLANGER_DELAY: *val = props.Delay; break;
|
||||||
case AL_FLANGER_DEPTH:
|
|
||||||
*val = props->Chorus.Depth;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_FLANGER_FEEDBACK:
|
|
||||||
*val = props->Chorus.Feedback;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_FLANGER_DELAY:
|
|
||||||
*val = props->Chorus.Delay;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Flanger_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
void FlangerEffectHandler::GetParamfv(const ChorusProps &props, ALenum param, float *vals)
|
||||||
{ Flanger_getParamf(props, param, vals); }
|
{ GetParamf(props, param, vals); }
|
||||||
|
|
||||||
EffectProps genDefaultFlangerProps() noexcept
|
|
||||||
{
|
|
||||||
EffectProps props{};
|
|
||||||
props.Chorus.Waveform = *WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM);
|
|
||||||
props.Chorus.Phase = AL_FLANGER_DEFAULT_PHASE;
|
|
||||||
props.Chorus.Rate = AL_FLANGER_DEFAULT_RATE;
|
|
||||||
props.Chorus.Depth = AL_FLANGER_DEFAULT_DEPTH;
|
|
||||||
props.Chorus.Feedback = AL_FLANGER_DEFAULT_FEEDBACK;
|
|
||||||
props.Chorus.Delay = AL_FLANGER_DEFAULT_DELAY;
|
|
||||||
return props;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
DEFINE_ALEFFECT_VTABLE(Chorus);
|
|
||||||
|
|
||||||
const EffectProps ChorusEffectProps{genDefaultChorusProps()};
|
|
||||||
|
|
||||||
DEFINE_ALEFFECT_VTABLE(Flanger);
|
|
||||||
|
|
||||||
const EffectProps FlangerEffectProps{genDefaultFlangerProps()};
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct EaxChorusTraits {
|
struct EaxChorusTraits {
|
||||||
using Props = EAXCHORUSPROPERTIES;
|
using EaxProps = EAXCHORUSPROPERTIES;
|
||||||
using Committer = EaxChorusCommitter;
|
using Committer = EaxChorusCommitter;
|
||||||
static constexpr auto Field = &EaxEffectProps::mChorus;
|
|
||||||
|
|
||||||
static constexpr auto eax_effect_type() { return EaxEffectType::Chorus; }
|
|
||||||
static constexpr auto efx_effect() { return AL_EFFECT_CHORUS; }
|
static constexpr auto efx_effect() { return AL_EFFECT_CHORUS; }
|
||||||
|
|
||||||
static constexpr auto eax_none_param_id() { return EAXCHORUS_NONE; }
|
static constexpr auto eax_none_param_id() { return EAXCHORUS_NONE; }
|
||||||
|
|
@ -359,11 +319,9 @@ struct EaxChorusTraits {
|
||||||
}; // EaxChorusTraits
|
}; // EaxChorusTraits
|
||||||
|
|
||||||
struct EaxFlangerTraits {
|
struct EaxFlangerTraits {
|
||||||
using Props = EAXFLANGERPROPERTIES;
|
using EaxProps = EAXFLANGERPROPERTIES;
|
||||||
using Committer = EaxFlangerCommitter;
|
using Committer = EaxFlangerCommitter;
|
||||||
static constexpr auto Field = &EaxEffectProps::mFlanger;
|
|
||||||
|
|
||||||
static constexpr auto eax_effect_type() { return EaxEffectType::Flanger; }
|
|
||||||
static constexpr auto efx_effect() { return AL_EFFECT_FLANGER; }
|
static constexpr auto efx_effect() { return AL_EFFECT_FLANGER; }
|
||||||
|
|
||||||
static constexpr auto eax_none_param_id() { return EAXFLANGER_NONE; }
|
static constexpr auto eax_none_param_id() { return EAXFLANGER_NONE; }
|
||||||
|
|
@ -428,11 +386,10 @@ struct EaxFlangerTraits {
|
||||||
template<typename TTraits>
|
template<typename TTraits>
|
||||||
struct ChorusFlangerEffect {
|
struct ChorusFlangerEffect {
|
||||||
using Traits = TTraits;
|
using Traits = TTraits;
|
||||||
|
using EaxProps = typename Traits::EaxProps;
|
||||||
using Committer = typename Traits::Committer;
|
using Committer = typename Traits::Committer;
|
||||||
using Exception = typename Committer::Exception;
|
using Exception = typename Committer::Exception;
|
||||||
|
|
||||||
static constexpr auto Field = Traits::Field;
|
|
||||||
|
|
||||||
struct WaveformValidator {
|
struct WaveformValidator {
|
||||||
void operator()(unsigned long ulWaveform) const
|
void operator()(unsigned long ulWaveform) const
|
||||||
{
|
{
|
||||||
|
|
@ -500,7 +457,7 @@ struct ChorusFlangerEffect {
|
||||||
}; // DelayValidator
|
}; // DelayValidator
|
||||||
|
|
||||||
struct AllValidator {
|
struct AllValidator {
|
||||||
void operator()(const typename Traits::Props& all) const
|
void operator()(const EaxProps& all) const
|
||||||
{
|
{
|
||||||
WaveformValidator{}(all.ulWaveform);
|
WaveformValidator{}(all.ulWaveform);
|
||||||
PhaseValidator{}(all.lPhase);
|
PhaseValidator{}(all.lPhase);
|
||||||
|
|
@ -514,8 +471,7 @@ struct ChorusFlangerEffect {
|
||||||
public:
|
public:
|
||||||
static void SetDefaults(EaxEffectProps &props)
|
static void SetDefaults(EaxEffectProps &props)
|
||||||
{
|
{
|
||||||
auto&& all = props.*Field;
|
auto&& all = props.emplace<EaxProps>();
|
||||||
props.mType = Traits::eax_effect_type();
|
|
||||||
all.ulWaveform = Traits::eax_default_waveform();
|
all.ulWaveform = Traits::eax_default_waveform();
|
||||||
all.lPhase = Traits::eax_default_phase();
|
all.lPhase = Traits::eax_default_phase();
|
||||||
all.flRate = Traits::eax_default_rate();
|
all.flRate = Traits::eax_default_rate();
|
||||||
|
|
@ -525,109 +481,83 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void Get(const EaxCall &call, const EaxEffectProps &props)
|
static void Get(const EaxCall &call, const EaxProps &all)
|
||||||
{
|
{
|
||||||
auto&& all = props.*Field;
|
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case Traits::eax_none_param_id():
|
case Traits::eax_none_param_id():
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Traits::eax_allparameters_param_id():
|
case Traits::eax_allparameters_param_id():
|
||||||
call.template set_value<Exception>(all);
|
call.template set_value<Exception>(all);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Traits::eax_waveform_param_id():
|
case Traits::eax_waveform_param_id():
|
||||||
call.template set_value<Exception>(all.ulWaveform);
|
call.template set_value<Exception>(all.ulWaveform);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Traits::eax_phase_param_id():
|
case Traits::eax_phase_param_id():
|
||||||
call.template set_value<Exception>(all.lPhase);
|
call.template set_value<Exception>(all.lPhase);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Traits::eax_rate_param_id():
|
case Traits::eax_rate_param_id():
|
||||||
call.template set_value<Exception>(all.flRate);
|
call.template set_value<Exception>(all.flRate);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Traits::eax_depth_param_id():
|
case Traits::eax_depth_param_id():
|
||||||
call.template set_value<Exception>(all.flDepth);
|
call.template set_value<Exception>(all.flDepth);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Traits::eax_feedback_param_id():
|
case Traits::eax_feedback_param_id():
|
||||||
call.template set_value<Exception>(all.flFeedback);
|
call.template set_value<Exception>(all.flFeedback);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Traits::eax_delay_param_id():
|
case Traits::eax_delay_param_id():
|
||||||
call.template set_value<Exception>(all.flDelay);
|
call.template set_value<Exception>(all.flDelay);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Committer::fail_unknown_property_id();
|
Committer::fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Set(const EaxCall &call, EaxEffectProps &props)
|
static void Set(const EaxCall &call, EaxProps &all)
|
||||||
{
|
{
|
||||||
auto&& all = props.*Field;
|
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case Traits::eax_none_param_id():
|
case Traits::eax_none_param_id():
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Traits::eax_allparameters_param_id():
|
case Traits::eax_allparameters_param_id():
|
||||||
Committer::template defer<AllValidator>(call, all);
|
Committer::template defer<AllValidator>(call, all);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Traits::eax_waveform_param_id():
|
case Traits::eax_waveform_param_id():
|
||||||
Committer::template defer<WaveformValidator>(call, all.ulWaveform);
|
Committer::template defer<WaveformValidator>(call, all.ulWaveform);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Traits::eax_phase_param_id():
|
case Traits::eax_phase_param_id():
|
||||||
Committer::template defer<PhaseValidator>(call, all.lPhase);
|
Committer::template defer<PhaseValidator>(call, all.lPhase);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Traits::eax_rate_param_id():
|
case Traits::eax_rate_param_id():
|
||||||
Committer::template defer<RateValidator>(call, all.flRate);
|
Committer::template defer<RateValidator>(call, all.flRate);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Traits::eax_depth_param_id():
|
case Traits::eax_depth_param_id():
|
||||||
Committer::template defer<DepthValidator>(call, all.flDepth);
|
Committer::template defer<DepthValidator>(call, all.flDepth);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Traits::eax_feedback_param_id():
|
case Traits::eax_feedback_param_id():
|
||||||
Committer::template defer<FeedbackValidator>(call, all.flFeedback);
|
Committer::template defer<FeedbackValidator>(call, all.flFeedback);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Traits::eax_delay_param_id():
|
case Traits::eax_delay_param_id():
|
||||||
Committer::template defer<DelayValidator>(call, all.flDelay);
|
Committer::template defer<DelayValidator>(call, all.flDelay);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Committer::fail_unknown_property_id();
|
Committer::fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool Commit(const EaxEffectProps &props, EaxEffectProps &props_, EffectProps &al_props_)
|
static bool Commit(const EaxProps &props, EaxEffectProps &props_, ChorusProps &al_props_)
|
||||||
{
|
{
|
||||||
if(props.mType == props_.mType)
|
if(auto *cur = std::get_if<EaxProps>(&props_); cur && *cur == props)
|
||||||
{
|
return false;
|
||||||
auto&& src = props_.*Field;
|
|
||||||
auto&& dst = props.*Field;
|
|
||||||
if(dst.ulWaveform == src.ulWaveform && dst.lPhase == src.lPhase
|
|
||||||
&& dst.flRate == src.flRate && dst.flDepth == src.flDepth
|
|
||||||
&& dst.flFeedback == src.flFeedback && dst.flDelay == src.flDelay)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
props_ = props;
|
props_ = props;
|
||||||
auto&& dst = props.*Field;
|
|
||||||
|
|
||||||
al_props_.Chorus.Waveform = Traits::eax_waveform(dst.ulWaveform);
|
al_props_.Waveform = Traits::eax_waveform(props.ulWaveform);
|
||||||
al_props_.Chorus.Phase = static_cast<int>(dst.lPhase);
|
al_props_.Phase = static_cast<int>(props.lPhase);
|
||||||
al_props_.Chorus.Rate = dst.flRate;
|
al_props_.Rate = props.flRate;
|
||||||
al_props_.Chorus.Depth = dst.flDepth;
|
al_props_.Depth = props.flDepth;
|
||||||
al_props_.Chorus.Feedback = dst.flFeedback;
|
al_props_.Feedback = props.flFeedback;
|
||||||
al_props_.Chorus.Delay = dst.flDelay;
|
al_props_.Delay = props.flDelay;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -652,29 +582,25 @@ template<>
|
||||||
throw Exception{message};
|
throw Exception{message};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
bool EaxChorusCommitter::commit(const EAXCHORUSPROPERTIES &props)
|
||||||
bool ChorusCommitter::commit(const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
|
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
|
||||||
return Committer::Commit(props, mEaxProps, mAlProps);
|
return Committer::Commit(props, mEaxProps, mAlProps.emplace<ChorusProps>());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxChorusCommitter::SetDefaults(EaxEffectProps &props)
|
||||||
void ChorusCommitter::SetDefaults(EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
|
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
|
||||||
Committer::SetDefaults(props);
|
Committer::SetDefaults(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxChorusCommitter::Get(const EaxCall &call, const EAXCHORUSPROPERTIES &props)
|
||||||
void ChorusCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
|
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
|
||||||
Committer::Get(call, props);
|
Committer::Get(call, props);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxChorusCommitter::Set(const EaxCall &call, EAXCHORUSPROPERTIES &props)
|
||||||
void ChorusCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
|
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
|
||||||
Committer::Set(call, props);
|
Committer::Set(call, props);
|
||||||
|
|
@ -693,29 +619,25 @@ template<>
|
||||||
throw Exception{message};
|
throw Exception{message};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
bool EaxFlangerCommitter::commit(const EAXFLANGERPROPERTIES &props)
|
||||||
bool FlangerCommitter::commit(const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
|
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
|
||||||
return Committer::Commit(props, mEaxProps, mAlProps);
|
return Committer::Commit(props, mEaxProps, mAlProps.emplace<ChorusProps>());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxFlangerCommitter::SetDefaults(EaxEffectProps &props)
|
||||||
void FlangerCommitter::SetDefaults(EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
|
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
|
||||||
Committer::SetDefaults(props);
|
Committer::SetDefaults(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxFlangerCommitter::Get(const EaxCall &call, const EAXFLANGERPROPERTIES &props)
|
||||||
void FlangerCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
|
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
|
||||||
Committer::Get(call, props);
|
Committer::Get(call, props);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxFlangerCommitter::Set(const EaxCall &call, EAXFLANGERPROPERTIES &props)
|
||||||
void FlangerCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
|
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
|
||||||
Committer::Set(call, props);
|
Committer::Set(call, props);
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.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"
|
||||||
#endif // ALSOFT_EAX
|
#endif // ALSOFT_EAX
|
||||||
|
|
@ -16,14 +17,25 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void Compressor_setParami(EffectProps *props, ALenum param, int val)
|
constexpr EffectProps genDefaultProps() noexcept
|
||||||
|
{
|
||||||
|
CompressorProps props{};
|
||||||
|
props.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF;
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
const EffectProps CompressorEffectProps{genDefaultProps()};
|
||||||
|
|
||||||
|
void CompressorEffectHandler::SetParami(CompressorProps &props, ALenum param, int val)
|
||||||
{
|
{
|
||||||
switch(param)
|
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Compressor state out of range"};
|
||||||
props->Compressor.OnOff = (val != AL_FALSE);
|
props.OnOff = (val != AL_FALSE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -31,51 +43,36 @@ void Compressor_setParami(EffectProps *props, ALenum param, int val)
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Compressor_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
void CompressorEffectHandler::SetParamiv(CompressorProps &props, ALenum param, const int *vals)
|
||||||
{ Compressor_setParami(props, param, vals[0]); }
|
{ SetParami(props, param, *vals); }
|
||||||
void Compressor_setParamf(EffectProps*, ALenum param, float)
|
void CompressorEffectHandler::SetParamf(CompressorProps&, ALenum param, float)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
|
||||||
void Compressor_setParamfv(EffectProps*, ALenum param, const float*)
|
void CompressorEffectHandler::SetParamfv(CompressorProps&, ALenum param, const float*)
|
||||||
{
|
{
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x",
|
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x",
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compressor_getParami(const EffectProps *props, ALenum param, int *val)
|
void CompressorEffectHandler::GetParami(const CompressorProps &props, ALenum param, int *val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_COMPRESSOR_ONOFF:
|
case AL_COMPRESSOR_ONOFF: *val = props.OnOff; break;
|
||||||
*val = props->Compressor.OnOff;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
|
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Compressor_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
void CompressorEffectHandler::GetParamiv(const CompressorProps &props, ALenum param, int *vals)
|
||||||
{ Compressor_getParami(props, param, vals); }
|
{ GetParami(props, param, vals); }
|
||||||
void Compressor_getParamf(const EffectProps*, ALenum param, float*)
|
void CompressorEffectHandler::GetParamf(const CompressorProps&, ALenum param, float*)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
|
||||||
void Compressor_getParamfv(const EffectProps*, ALenum param, float*)
|
void CompressorEffectHandler::GetParamfv(const CompressorProps&, ALenum param, float*)
|
||||||
{
|
{
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x",
|
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x",
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
|
|
||||||
EffectProps genDefaultProps() noexcept
|
|
||||||
{
|
|
||||||
EffectProps props{};
|
|
||||||
props.Compressor.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF;
|
|
||||||
return props;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
DEFINE_ALEFFECT_VTABLE(Compressor);
|
|
||||||
|
|
||||||
const EffectProps CompressorEffectProps{genDefaultProps()};
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -115,46 +112,40 @@ template<>
|
||||||
throw Exception{message};
|
throw Exception{message};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
bool EaxCompressorCommitter::commit(const EAXAGCCOMPRESSORPROPERTIES &props)
|
||||||
bool CompressorCommitter::commit(const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
if(props.mType == mEaxProps.mType
|
if(auto *cur = std::get_if<EAXAGCCOMPRESSORPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||||
&& props.mCompressor.ulOnOff == mEaxProps.mCompressor.ulOnOff)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mEaxProps = props;
|
mEaxProps = props;
|
||||||
|
mAlProps = CompressorProps{props.ulOnOff != 0};
|
||||||
|
|
||||||
mAlProps.Compressor.OnOff = (props.mCompressor.ulOnOff != 0);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxCompressorCommitter::SetDefaults(EaxEffectProps &props)
|
||||||
void CompressorCommitter::SetDefaults(EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
props.mType = EaxEffectType::Compressor;
|
props = EAXAGCCOMPRESSORPROPERTIES{EAXAGCCOMPRESSOR_DEFAULTONOFF};
|
||||||
props.mCompressor.ulOnOff = EAXAGCCOMPRESSOR_DEFAULTONOFF;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxCompressorCommitter::Get(const EaxCall &call, const EAXAGCCOMPRESSORPROPERTIES &props)
|
||||||
void CompressorCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case EAXAGCCOMPRESSOR_NONE: break;
|
case EAXAGCCOMPRESSOR_NONE: break;
|
||||||
case EAXAGCCOMPRESSOR_ALLPARAMETERS: call.set_value<Exception>(props.mCompressor); break;
|
case EAXAGCCOMPRESSOR_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||||
case EAXAGCCOMPRESSOR_ONOFF: call.set_value<Exception>(props.mCompressor.ulOnOff); break;
|
case EAXAGCCOMPRESSOR_ONOFF: call.set_value<Exception>(props.ulOnOff); break;
|
||||||
default: fail_unknown_property_id();
|
default: fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxCompressorCommitter::Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props)
|
||||||
void CompressorCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case EAXAGCCOMPRESSOR_NONE: break;
|
case EAXAGCCOMPRESSOR_NONE: break;
|
||||||
case EAXAGCCOMPRESSOR_ALLPARAMETERS: defer<AllValidator>(call, props.mCompressor); break;
|
case EAXAGCCOMPRESSOR_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||||
case EAXAGCCOMPRESSOR_ONOFF: defer<OnOffValidator>(call, props.mCompressor.ulOnOff); break;
|
case EAXAGCCOMPRESSOR_ONOFF: defer<OnOffValidator>(call, props.ulOnOff); break;
|
||||||
default: fail_unknown_property_id();
|
default: fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,93 +1,117 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include <algorithm>
|
||||||
#include "alc/inprogext.h"
|
#include <array>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#include "alc/effects/base.h"
|
#include "AL/al.h"
|
||||||
|
|
||||||
|
#include "alc/inprogext.h"
|
||||||
|
#include "alnumeric.h"
|
||||||
|
#include "alspan.h"
|
||||||
|
#include "core/effects/base.h"
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void Convolution_setParami(EffectProps* /*props*/, ALenum param, int /*val*/)
|
constexpr EffectProps genDefaultProps() noexcept
|
||||||
{
|
{
|
||||||
switch(param)
|
ConvolutionProps props{};
|
||||||
{
|
props.OrientAt = {0.0f, 0.0f, -1.0f};
|
||||||
default:
|
props.OrientUp = {0.0f, 1.0f, 0.0f};
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Convolution_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
Convolution_setParami(props, param, vals[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Convolution_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/)
|
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Convolution_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
Convolution_setParamf(props, param, vals[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Convolution_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/)
|
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Convolution_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
Convolution_getParami(props, param, vals);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Convolution_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/)
|
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Convolution_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
Convolution_getParamf(props, param, vals);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EffectProps genDefaultProps() noexcept
|
|
||||||
{
|
|
||||||
EffectProps props{};
|
|
||||||
return props;
|
return props;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
DEFINE_ALEFFECT_VTABLE(Convolution);
|
|
||||||
|
|
||||||
const EffectProps ConvolutionEffectProps{genDefaultProps()};
|
const EffectProps ConvolutionEffectProps{genDefaultProps()};
|
||||||
|
|
||||||
|
void ConvolutionEffectHandler::SetParami(ConvolutionProps& /*props*/, ALenum param, int /*val*/)
|
||||||
|
{
|
||||||
|
switch(param)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect integer property 0x%04x",
|
||||||
|
param};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void 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); };
|
||||||
|
al::span<const float> vals;
|
||||||
|
switch(param)
|
||||||
|
{
|
||||||
|
case AL_CONVOLUTION_ORIENTATION_SOFT:
|
||||||
|
vals = {values, 6_uz};
|
||||||
|
if(!std::all_of(vals.cbegin(), vals.cend(), finite_checker))
|
||||||
|
throw effect_exception{AL_INVALID_VALUE, "Property 0x%04x value out of range", param};
|
||||||
|
|
||||||
|
std::copy_n(vals.cbegin(), props.OrientAt.size(), props.OrientAt.begin());
|
||||||
|
std::copy_n(vals.cbegin()+3, props.OrientUp.size(), props.OrientUp.begin());
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
SetParamf(props, param, *values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvolutionEffectHandler::GetParami(const ConvolutionProps& /*props*/, ALenum param, int* /*val*/)
|
||||||
|
{
|
||||||
|
switch(param)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect integer property 0x%04x",
|
||||||
|
param};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void 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)
|
||||||
|
{
|
||||||
|
case AL_CONVOLUTION_ORIENTATION_SOFT:
|
||||||
|
vals = {values, 6_uz};
|
||||||
|
std::copy(props.OrientAt.cbegin(), props.OrientAt.cend(), vals.begin());
|
||||||
|
std::copy(props.OrientUp.cbegin(), props.OrientUp.cend(), vals.begin()+3);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
GetParamf(props, param, values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,61 +12,111 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void Dedicated_setParami(EffectProps*, ALenum param, int)
|
constexpr EffectProps genDefaultDialogProps() noexcept
|
||||||
|
{
|
||||||
|
DedicatedProps props{};
|
||||||
|
props.Target = DedicatedProps::Dialog;
|
||||||
|
props.Gain = 1.0f;
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr EffectProps genDefaultLfeProps() noexcept
|
||||||
|
{
|
||||||
|
DedicatedProps props{};
|
||||||
|
props.Target = DedicatedProps::Lfe;
|
||||||
|
props.Gain = 1.0f;
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
const EffectProps DedicatedDialogEffectProps{genDefaultDialogProps()};
|
||||||
|
|
||||||
|
void DedicatedDialogEffectHandler::SetParami(DedicatedProps&, ALenum param, int)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
||||||
void Dedicated_setParamiv(EffectProps*, ALenum param, const int*)
|
void DedicatedDialogEffectHandler::SetParamiv(DedicatedProps&, ALenum param, const int*)
|
||||||
{
|
{
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
void Dedicated_setParamf(EffectProps *props, ALenum param, float val)
|
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"};
|
||||||
props->Dedicated.Gain = val;
|
props.Gain = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Dedicated_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
void DedicatedDialogEffectHandler::SetParamfv(DedicatedProps &props, ALenum param, const float *vals)
|
||||||
{ Dedicated_setParamf(props, param, vals[0]); }
|
{ SetParamf(props, param, *vals); }
|
||||||
|
|
||||||
void Dedicated_getParami(const EffectProps*, ALenum param, int*)
|
void DedicatedDialogEffectHandler::GetParami(const DedicatedProps&, ALenum param, int*)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
||||||
void Dedicated_getParamiv(const EffectProps*, ALenum param, int*)
|
void DedicatedDialogEffectHandler::GetParamiv(const DedicatedProps&, ALenum param, int*)
|
||||||
{
|
{
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
void Dedicated_getParamf(const EffectProps *props, ALenum param, float *val)
|
void DedicatedDialogEffectHandler::GetParamf(const DedicatedProps &props, ALenum param, float *val)
|
||||||
|
{
|
||||||
|
switch(param)
|
||||||
|
{
|
||||||
|
case AL_DEDICATED_GAIN: *val = props.Gain; break;
|
||||||
|
default:
|
||||||
|
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void DedicatedDialogEffectHandler::GetParamfv(const DedicatedProps &props, ALenum param, float *vals)
|
||||||
|
{ GetParamf(props, param, vals); }
|
||||||
|
|
||||||
|
|
||||||
|
const EffectProps DedicatedLfeEffectProps{genDefaultLfeProps()};
|
||||||
|
|
||||||
|
void DedicatedLfeEffectHandler::SetParami(DedicatedProps&, ALenum param, int)
|
||||||
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
||||||
|
void DedicatedLfeEffectHandler::SetParamiv(DedicatedProps&, ALenum param, const int*)
|
||||||
|
{
|
||||||
|
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
||||||
|
param};
|
||||||
|
}
|
||||||
|
void DedicatedLfeEffectHandler::SetParamf(DedicatedProps &props, ALenum param, float val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_DEDICATED_GAIN:
|
case AL_DEDICATED_GAIN:
|
||||||
*val = props->Dedicated.Gain;
|
if(!(val >= 0.0f && std::isfinite(val)))
|
||||||
|
throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"};
|
||||||
|
props.Gain = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Dedicated_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
void DedicatedLfeEffectHandler::SetParamfv(DedicatedProps &props, ALenum param, const float *vals)
|
||||||
{ Dedicated_getParamf(props, param, vals); }
|
{ SetParamf(props, param, *vals); }
|
||||||
|
|
||||||
EffectProps genDefaultProps() noexcept
|
void DedicatedLfeEffectHandler::GetParami(const DedicatedProps&, ALenum param, int*)
|
||||||
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
||||||
|
void DedicatedLfeEffectHandler::GetParamiv(const DedicatedProps&, ALenum param, int*)
|
||||||
{
|
{
|
||||||
EffectProps props{};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
||||||
props.Dedicated.Gain = 1.0f;
|
param};
|
||||||
return props;
|
|
||||||
}
|
}
|
||||||
|
void DedicatedLfeEffectHandler::GetParamf(const DedicatedProps &props, ALenum param, float *val)
|
||||||
} // namespace
|
{
|
||||||
|
switch(param)
|
||||||
DEFINE_ALEFFECT_VTABLE(Dedicated);
|
{
|
||||||
|
case AL_DEDICATED_GAIN: *val = props.Gain; break;
|
||||||
const EffectProps DedicatedEffectProps{genDefaultProps()};
|
default:
|
||||||
|
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void DedicatedLfeEffectHandler::GetParamfv(const DedicatedProps &props, ALenum param, float *vals)
|
||||||
|
{ GetParamf(props, param, vals); }
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.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"
|
||||||
#endif // ALSOFT_EAX
|
#endif // ALSOFT_EAX
|
||||||
|
|
@ -16,108 +17,93 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void Distortion_setParami(EffectProps*, ALenum param, int)
|
constexpr EffectProps genDefaultProps() noexcept
|
||||||
|
{
|
||||||
|
DistortionProps props{};
|
||||||
|
props.Edge = AL_DISTORTION_DEFAULT_EDGE;
|
||||||
|
props.Gain = AL_DISTORTION_DEFAULT_GAIN;
|
||||||
|
props.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF;
|
||||||
|
props.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER;
|
||||||
|
props.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH;
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
const EffectProps DistortionEffectProps{genDefaultProps()};
|
||||||
|
|
||||||
|
void DistortionEffectHandler::SetParami(DistortionProps&, ALenum param, int)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
|
||||||
void Distortion_setParamiv(EffectProps*, ALenum param, const int*)
|
void DistortionEffectHandler::SetParamiv(DistortionProps&, ALenum param, const int*)
|
||||||
{
|
{
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
|
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
void Distortion_setParamf(EffectProps *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"};
|
throw effect_exception{AL_INVALID_VALUE, "Distortion edge out of range"};
|
||||||
props->Distortion.Edge = val;
|
props.Edge = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Distortion gain out of range"};
|
||||||
props->Distortion.Gain = val;
|
props.Gain = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Distortion low-pass cutoff out of range"};
|
||||||
props->Distortion.LowpassCutoff = val;
|
props.LowpassCutoff = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Distortion EQ center out of range"};
|
||||||
props->Distortion.EQCenter = val;
|
props.EQCenter = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Distortion EQ bandwidth out of range"};
|
||||||
props->Distortion.EQBandwidth = val;
|
props.EQBandwidth = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Distortion_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
void DistortionEffectHandler::SetParamfv(DistortionProps &props, ALenum param, const float *vals)
|
||||||
{ Distortion_setParamf(props, param, vals[0]); }
|
{ SetParamf(props, param, *vals); }
|
||||||
|
|
||||||
void Distortion_getParami(const EffectProps*, ALenum param, int*)
|
void DistortionEffectHandler::GetParami(const DistortionProps&, ALenum param, int*)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
|
||||||
void Distortion_getParamiv(const EffectProps*, ALenum param, int*)
|
void DistortionEffectHandler::GetParamiv(const DistortionProps&, ALenum param, int*)
|
||||||
{
|
{
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
|
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
void Distortion_getParamf(const EffectProps *props, ALenum param, float *val)
|
void DistortionEffectHandler::GetParamf(const DistortionProps &props, ALenum param, float *val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_DISTORTION_EDGE:
|
case AL_DISTORTION_EDGE: *val = props.Edge; break;
|
||||||
*val = props->Distortion.Edge;
|
case AL_DISTORTION_GAIN: *val = props.Gain; break;
|
||||||
break;
|
case AL_DISTORTION_LOWPASS_CUTOFF: *val = props.LowpassCutoff; break;
|
||||||
|
case AL_DISTORTION_EQCENTER: *val = props.EQCenter; break;
|
||||||
case AL_DISTORTION_GAIN:
|
case AL_DISTORTION_EQBANDWIDTH: *val = props.EQBandwidth; break;
|
||||||
*val = props->Distortion.Gain;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_DISTORTION_LOWPASS_CUTOFF:
|
|
||||||
*val = props->Distortion.LowpassCutoff;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_DISTORTION_EQCENTER:
|
|
||||||
*val = props->Distortion.EQCenter;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_DISTORTION_EQBANDWIDTH:
|
|
||||||
*val = props->Distortion.EQBandwidth;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Distortion_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
void DistortionEffectHandler::GetParamfv(const DistortionProps &props, ALenum param, float *vals)
|
||||||
{ Distortion_getParamf(props, param, vals); }
|
{ GetParamf(props, param, vals); }
|
||||||
|
|
||||||
EffectProps genDefaultProps() noexcept
|
|
||||||
{
|
|
||||||
EffectProps props{};
|
|
||||||
props.Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE;
|
|
||||||
props.Distortion.Gain = AL_DISTORTION_DEFAULT_GAIN;
|
|
||||||
props.Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF;
|
|
||||||
props.Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER;
|
|
||||||
props.Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH;
|
|
||||||
return props;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
DEFINE_ALEFFECT_VTABLE(Distortion);
|
|
||||||
|
|
||||||
const EffectProps DistortionEffectProps{genDefaultProps()};
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -204,66 +190,66 @@ template<>
|
||||||
throw Exception{message};
|
throw Exception{message};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
bool EaxDistortionCommitter::commit(const EAXDISTORTIONPROPERTIES &props)
|
||||||
bool DistortionCommitter::commit(const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
if(props.mType == mEaxProps.mType && mEaxProps.mDistortion.flEdge == props.mDistortion.flEdge
|
if(auto *cur = std::get_if<EAXDISTORTIONPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||||
&& mEaxProps.mDistortion.lGain == props.mDistortion.lGain
|
|
||||||
&& mEaxProps.mDistortion.flLowPassCutOff == props.mDistortion.flLowPassCutOff
|
|
||||||
&& mEaxProps.mDistortion.flEQCenter == props.mDistortion.flEQCenter
|
|
||||||
&& mEaxProps.mDistortion.flEQBandwidth == props.mDistortion.flEQBandwidth)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mEaxProps = props;
|
mEaxProps = props;
|
||||||
|
mAlProps = [&]{
|
||||||
mAlProps.Distortion.Edge = props.mDistortion.flEdge;
|
DistortionProps ret{};
|
||||||
mAlProps.Distortion.Gain = level_mb_to_gain(static_cast<float>(props.mDistortion.lGain));
|
ret.Edge = props.flEdge;
|
||||||
mAlProps.Distortion.LowpassCutoff = props.mDistortion.flLowPassCutOff;
|
ret.Gain = level_mb_to_gain(static_cast<float>(props.lGain));
|
||||||
mAlProps.Distortion.EQCenter = props.mDistortion.flEQCenter;
|
ret.LowpassCutoff = props.flLowPassCutOff;
|
||||||
mAlProps.Distortion.EQBandwidth = props.mDistortion.flEdge;
|
ret.EQCenter = props.flEQCenter;
|
||||||
|
ret.EQBandwidth = props.flEdge;
|
||||||
|
return ret;
|
||||||
|
}();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxDistortionCommitter::SetDefaults(EaxEffectProps &props)
|
||||||
void DistortionCommitter::SetDefaults(EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
props.mType = EaxEffectType::Distortion;
|
static constexpr EAXDISTORTIONPROPERTIES defprops{[]
|
||||||
props.mDistortion.flEdge = EAXDISTORTION_DEFAULTEDGE;
|
{
|
||||||
props.mDistortion.lGain = EAXDISTORTION_DEFAULTGAIN;
|
EAXDISTORTIONPROPERTIES ret{};
|
||||||
props.mDistortion.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF;
|
ret.flEdge = EAXDISTORTION_DEFAULTEDGE;
|
||||||
props.mDistortion.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER;
|
ret.lGain = EAXDISTORTION_DEFAULTGAIN;
|
||||||
props.mDistortion.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH;
|
ret.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF;
|
||||||
|
ret.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER;
|
||||||
|
ret.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH;
|
||||||
|
return ret;
|
||||||
|
}()};
|
||||||
|
props = defprops;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxDistortionCommitter::Get(const EaxCall &call, const EAXDISTORTIONPROPERTIES &props)
|
||||||
void DistortionCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case EAXDISTORTION_NONE: break;
|
case EAXDISTORTION_NONE: break;
|
||||||
case EAXDISTORTION_ALLPARAMETERS: call.set_value<Exception>(props.mDistortion); break;
|
case EAXDISTORTION_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||||
case EAXDISTORTION_EDGE: call.set_value<Exception>(props.mDistortion.flEdge); break;
|
case EAXDISTORTION_EDGE: call.set_value<Exception>(props.flEdge); break;
|
||||||
case EAXDISTORTION_GAIN: call.set_value<Exception>(props.mDistortion.lGain); break;
|
case EAXDISTORTION_GAIN: call.set_value<Exception>(props.lGain); break;
|
||||||
case EAXDISTORTION_LOWPASSCUTOFF: call.set_value<Exception>(props.mDistortion.flLowPassCutOff); break;
|
case EAXDISTORTION_LOWPASSCUTOFF: call.set_value<Exception>(props.flLowPassCutOff); break;
|
||||||
case EAXDISTORTION_EQCENTER: call.set_value<Exception>(props.mDistortion.flEQCenter); break;
|
case EAXDISTORTION_EQCENTER: call.set_value<Exception>(props.flEQCenter); break;
|
||||||
case EAXDISTORTION_EQBANDWIDTH: call.set_value<Exception>(props.mDistortion.flEQBandwidth); break;
|
case EAXDISTORTION_EQBANDWIDTH: call.set_value<Exception>(props.flEQBandwidth); break;
|
||||||
default: fail_unknown_property_id();
|
default: fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxDistortionCommitter::Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props)
|
||||||
void DistortionCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case EAXDISTORTION_NONE: break;
|
case EAXDISTORTION_NONE: break;
|
||||||
case EAXDISTORTION_ALLPARAMETERS: defer<AllValidator>(call, props.mDistortion); break;
|
case EAXDISTORTION_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||||
case EAXDISTORTION_EDGE: defer<EdgeValidator>(call, props.mDistortion.flEdge); break;
|
case EAXDISTORTION_EDGE: defer<EdgeValidator>(call, props.flEdge); break;
|
||||||
case EAXDISTORTION_GAIN: defer<GainValidator>(call, props.mDistortion.lGain); break;
|
case EAXDISTORTION_GAIN: defer<GainValidator>(call, props.lGain); break;
|
||||||
case EAXDISTORTION_LOWPASSCUTOFF: defer<LowPassCutOffValidator>(call, props.mDistortion.flLowPassCutOff); break;
|
case EAXDISTORTION_LOWPASSCUTOFF: defer<LowPassCutOffValidator>(call, props.flLowPassCutOff); break;
|
||||||
case EAXDISTORTION_EQCENTER: defer<EqCenterValidator>(call, props.mDistortion.flEQCenter); break;
|
case EAXDISTORTION_EQCENTER: defer<EqCenterValidator>(call, props.flEQCenter); break;
|
||||||
case EAXDISTORTION_EQBANDWIDTH: defer<EqBandwidthValidator>(call, props.mDistortion.flEQBandwidth); break;
|
case EAXDISTORTION_EQBANDWIDTH: defer<EqBandwidthValidator>(call, props.flEQBandwidth); break;
|
||||||
default: fail_unknown_property_id();
|
default: fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.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"
|
||||||
#endif // ALSOFT_EAX
|
#endif // ALSOFT_EAX
|
||||||
|
|
@ -19,102 +20,87 @@ namespace {
|
||||||
static_assert(EchoMaxDelay >= AL_ECHO_MAX_DELAY, "Echo max delay too short");
|
static_assert(EchoMaxDelay >= AL_ECHO_MAX_DELAY, "Echo max delay too short");
|
||||||
static_assert(EchoMaxLRDelay >= AL_ECHO_MAX_LRDELAY, "Echo max left-right delay too short");
|
static_assert(EchoMaxLRDelay >= AL_ECHO_MAX_LRDELAY, "Echo max left-right delay too short");
|
||||||
|
|
||||||
void Echo_setParami(EffectProps*, ALenum param, int)
|
constexpr EffectProps genDefaultProps() noexcept
|
||||||
|
{
|
||||||
|
EchoProps props{};
|
||||||
|
props.Delay = AL_ECHO_DEFAULT_DELAY;
|
||||||
|
props.LRDelay = AL_ECHO_DEFAULT_LRDELAY;
|
||||||
|
props.Damping = AL_ECHO_DEFAULT_DAMPING;
|
||||||
|
props.Feedback = AL_ECHO_DEFAULT_FEEDBACK;
|
||||||
|
props.Spread = AL_ECHO_DEFAULT_SPREAD;
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
const EffectProps EchoEffectProps{genDefaultProps()};
|
||||||
|
|
||||||
|
void EchoEffectHandler::SetParami(EchoProps&, ALenum param, int)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
|
||||||
void Echo_setParamiv(EffectProps*, ALenum param, const int*)
|
void EchoEffectHandler::SetParamiv(EchoProps&, ALenum param, const int*)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
|
||||||
void Echo_setParamf(EffectProps *props, ALenum param, float val)
|
void EchoEffectHandler::SetParamf(EchoProps &props, ALenum param, float val)
|
||||||
{
|
{
|
||||||
switch(param)
|
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Echo delay out of range"};
|
||||||
props->Echo.Delay = val;
|
props.Delay = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Echo LR delay out of range"};
|
||||||
props->Echo.LRDelay = val;
|
props.LRDelay = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Echo damping out of range"};
|
||||||
props->Echo.Damping = val;
|
props.Damping = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Echo feedback out of range"};
|
||||||
props->Echo.Feedback = val;
|
props.Feedback = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Echo spread out of range"};
|
||||||
props->Echo.Spread = val;
|
props.Spread = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Echo_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
void EchoEffectHandler::SetParamfv(EchoProps &props, ALenum param, const float *vals)
|
||||||
{ Echo_setParamf(props, param, vals[0]); }
|
{ SetParamf(props, param, *vals); }
|
||||||
|
|
||||||
void Echo_getParami(const EffectProps*, ALenum param, int*)
|
void EchoEffectHandler::GetParami(const EchoProps&, ALenum param, int*)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
|
||||||
void Echo_getParamiv(const EffectProps*, ALenum param, int*)
|
void EchoEffectHandler::GetParamiv(const EchoProps&, ALenum param, int*)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
|
||||||
void Echo_getParamf(const EffectProps *props, ALenum param, float *val)
|
void EchoEffectHandler::GetParamf(const EchoProps &props, ALenum param, float *val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_ECHO_DELAY:
|
case AL_ECHO_DELAY: *val = props.Delay; break;
|
||||||
*val = props->Echo.Delay;
|
case AL_ECHO_LRDELAY: *val = props.LRDelay; break;
|
||||||
break;
|
case AL_ECHO_DAMPING: *val = props.Damping; break;
|
||||||
|
case AL_ECHO_FEEDBACK: *val = props.Feedback; break;
|
||||||
case AL_ECHO_LRDELAY:
|
case AL_ECHO_SPREAD: *val = props.Spread; break;
|
||||||
*val = props->Echo.LRDelay;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_ECHO_DAMPING:
|
|
||||||
*val = props->Echo.Damping;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_ECHO_FEEDBACK:
|
|
||||||
*val = props->Echo.Feedback;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_ECHO_SPREAD:
|
|
||||||
*val = props->Echo.Spread;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Echo_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
void EchoEffectHandler::GetParamfv(const EchoProps &props, ALenum param, float *vals)
|
||||||
{ Echo_getParamf(props, param, vals); }
|
{ GetParamf(props, param, vals); }
|
||||||
|
|
||||||
EffectProps genDefaultProps() noexcept
|
|
||||||
{
|
|
||||||
EffectProps props{};
|
|
||||||
props.Echo.Delay = AL_ECHO_DEFAULT_DELAY;
|
|
||||||
props.Echo.LRDelay = AL_ECHO_DEFAULT_LRDELAY;
|
|
||||||
props.Echo.Damping = AL_ECHO_DEFAULT_DAMPING;
|
|
||||||
props.Echo.Feedback = AL_ECHO_DEFAULT_FEEDBACK;
|
|
||||||
props.Echo.Spread = AL_ECHO_DEFAULT_SPREAD;
|
|
||||||
return props;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
DEFINE_ALEFFECT_VTABLE(Echo);
|
|
||||||
|
|
||||||
const EffectProps EchoEffectProps{genDefaultProps()};
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -201,66 +187,66 @@ template<>
|
||||||
throw Exception{message};
|
throw Exception{message};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
bool EaxEchoCommitter::commit(const EAXECHOPROPERTIES &props)
|
||||||
bool EchoCommitter::commit(const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
if(props.mType == mEaxProps.mType && mEaxProps.mEcho.flDelay == props.mEcho.flDelay
|
if(auto *cur = std::get_if<EAXECHOPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||||
&& mEaxProps.mEcho.flLRDelay == props.mEcho.flLRDelay
|
|
||||||
&& mEaxProps.mEcho.flDamping == props.mEcho.flDamping
|
|
||||||
&& mEaxProps.mEcho.flFeedback == props.mEcho.flFeedback
|
|
||||||
&& mEaxProps.mEcho.flSpread == props.mEcho.flSpread)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mEaxProps = props;
|
mEaxProps = props;
|
||||||
|
mAlProps = [&]{
|
||||||
mAlProps.Echo.Delay = props.mEcho.flDelay;
|
EchoProps ret{};
|
||||||
mAlProps.Echo.LRDelay = props.mEcho.flLRDelay;
|
ret.Delay = props.flDelay;
|
||||||
mAlProps.Echo.Damping = props.mEcho.flDamping;
|
ret.LRDelay = props.flLRDelay;
|
||||||
mAlProps.Echo.Feedback = props.mEcho.flFeedback;
|
ret.Damping = props.flDamping;
|
||||||
mAlProps.Echo.Spread = props.mEcho.flSpread;
|
ret.Feedback = props.flFeedback;
|
||||||
|
ret.Spread = props.flSpread;
|
||||||
|
return ret;
|
||||||
|
}();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxEchoCommitter::SetDefaults(EaxEffectProps &props)
|
||||||
void EchoCommitter::SetDefaults(EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
props.mType = EaxEffectType::Echo;
|
static constexpr EAXECHOPROPERTIES defprops{[]
|
||||||
props.mEcho.flDelay = EAXECHO_DEFAULTDELAY;
|
{
|
||||||
props.mEcho.flLRDelay = EAXECHO_DEFAULTLRDELAY;
|
EAXECHOPROPERTIES ret{};
|
||||||
props.mEcho.flDamping = EAXECHO_DEFAULTDAMPING;
|
ret.flDelay = EAXECHO_DEFAULTDELAY;
|
||||||
props.mEcho.flFeedback = EAXECHO_DEFAULTFEEDBACK;
|
ret.flLRDelay = EAXECHO_DEFAULTLRDELAY;
|
||||||
props.mEcho.flSpread = EAXECHO_DEFAULTSPREAD;
|
ret.flDamping = EAXECHO_DEFAULTDAMPING;
|
||||||
|
ret.flFeedback = EAXECHO_DEFAULTFEEDBACK;
|
||||||
|
ret.flSpread = EAXECHO_DEFAULTSPREAD;
|
||||||
|
return ret;
|
||||||
|
}()};
|
||||||
|
props = defprops;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxEchoCommitter::Get(const EaxCall &call, const EAXECHOPROPERTIES &props)
|
||||||
void EchoCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case EAXECHO_NONE: break;
|
case EAXECHO_NONE: break;
|
||||||
case EAXECHO_ALLPARAMETERS: call.set_value<Exception>(props.mEcho); break;
|
case EAXECHO_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||||
case EAXECHO_DELAY: call.set_value<Exception>(props.mEcho.flDelay); break;
|
case EAXECHO_DELAY: call.set_value<Exception>(props.flDelay); break;
|
||||||
case EAXECHO_LRDELAY: call.set_value<Exception>(props.mEcho.flLRDelay); break;
|
case EAXECHO_LRDELAY: call.set_value<Exception>(props.flLRDelay); break;
|
||||||
case EAXECHO_DAMPING: call.set_value<Exception>(props.mEcho.flDamping); break;
|
case EAXECHO_DAMPING: call.set_value<Exception>(props.flDamping); break;
|
||||||
case EAXECHO_FEEDBACK: call.set_value<Exception>(props.mEcho.flFeedback); break;
|
case EAXECHO_FEEDBACK: call.set_value<Exception>(props.flFeedback); break;
|
||||||
case EAXECHO_SPREAD: call.set_value<Exception>(props.mEcho.flSpread); break;
|
case EAXECHO_SPREAD: call.set_value<Exception>(props.flSpread); break;
|
||||||
default: fail_unknown_property_id();
|
default: fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxEchoCommitter::Set(const EaxCall &call, EAXECHOPROPERTIES &props)
|
||||||
void EchoCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case EAXECHO_NONE: break;
|
case EAXECHO_NONE: break;
|
||||||
case EAXECHO_ALLPARAMETERS: defer<AllValidator>(call, props.mEcho); break;
|
case EAXECHO_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||||
case EAXECHO_DELAY: defer<DelayValidator>(call, props.mEcho.flDelay); break;
|
case EAXECHO_DELAY: defer<DelayValidator>(call, props.flDelay); break;
|
||||||
case EAXECHO_LRDELAY: defer<LrDelayValidator>(call, props.mEcho.flLRDelay); break;
|
case EAXECHO_LRDELAY: defer<LrDelayValidator>(call, props.flLRDelay); break;
|
||||||
case EAXECHO_DAMPING: defer<DampingValidator>(call, props.mEcho.flDamping); break;
|
case EAXECHO_DAMPING: defer<DampingValidator>(call, props.flDamping); break;
|
||||||
case EAXECHO_FEEDBACK: defer<FeedbackValidator>(call, props.mEcho.flFeedback); break;
|
case EAXECHO_FEEDBACK: defer<FeedbackValidator>(call, props.flFeedback); break;
|
||||||
case EAXECHO_SPREAD: defer<SpreadValidator>(call, props.mEcho.flSpread); break;
|
case EAXECHO_SPREAD: defer<SpreadValidator>(call, props.flSpread); break;
|
||||||
default: fail_unknown_property_id();
|
default: fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,3 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include "AL/efx.h"
|
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
|
|
||||||
#endif // ALSOFT_EAX
|
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,47 @@
|
||||||
#ifndef AL_EFFECTS_EFFECTS_H
|
#ifndef AL_EFFECTS_EFFECTS_H
|
||||||
#define AL_EFFECTS_EFFECTS_H
|
#define AL_EFFECTS_EFFECTS_H
|
||||||
|
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
|
|
||||||
#include "core/except.h"
|
#include "al/error.h"
|
||||||
|
#include "core/effects/base.h"
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#define DECL_HANDLER(N, T) \
|
||||||
#include "al/eax/effect.h"
|
struct N { \
|
||||||
#endif // ALSOFT_EAX
|
using prop_type = T; \
|
||||||
|
\
|
||||||
union EffectProps;
|
static void SetParami(prop_type &props, ALenum param, int val); \
|
||||||
|
static void SetParamiv(prop_type &props, ALenum param, const int *vals); \
|
||||||
|
static void SetParamf(prop_type &props, ALenum param, float val); \
|
||||||
class effect_exception final : public al::base_exception {
|
static void SetParamfv(prop_type &props, ALenum param, const float *vals);\
|
||||||
ALenum mErrorCode;
|
static void GetParami(const prop_type &props, ALenum param, int *val); \
|
||||||
|
static void GetParamiv(const prop_type &props, ALenum param, int *vals); \
|
||||||
public:
|
static void GetParamf(const prop_type &props, ALenum param, float *val); \
|
||||||
#ifdef __USE_MINGW_ANSI_STDIO
|
static void GetParamfv(const prop_type &props, ALenum param, float *vals);\
|
||||||
[[gnu::format(gnu_printf, 3, 4)]]
|
|
||||||
#else
|
|
||||||
[[gnu::format(printf, 3, 4)]]
|
|
||||||
#endif
|
|
||||||
effect_exception(ALenum code, const char *msg, ...);
|
|
||||||
~effect_exception() override;
|
|
||||||
|
|
||||||
ALenum errorCode() const noexcept { return mErrorCode; }
|
|
||||||
};
|
};
|
||||||
|
DECL_HANDLER(NullEffectHandler, std::monostate)
|
||||||
|
DECL_HANDLER(ReverbEffectHandler, ReverbProps)
|
||||||
|
DECL_HANDLER(StdReverbEffectHandler, ReverbProps)
|
||||||
|
DECL_HANDLER(AutowahEffectHandler, AutowahProps)
|
||||||
|
DECL_HANDLER(ChorusEffectHandler, ChorusProps)
|
||||||
|
DECL_HANDLER(CompressorEffectHandler, CompressorProps)
|
||||||
|
DECL_HANDLER(DistortionEffectHandler, DistortionProps)
|
||||||
|
DECL_HANDLER(EchoEffectHandler, EchoProps)
|
||||||
|
DECL_HANDLER(EqualizerEffectHandler, EqualizerProps)
|
||||||
|
DECL_HANDLER(FlangerEffectHandler, ChorusProps)
|
||||||
|
DECL_HANDLER(FshifterEffectHandler, FshifterProps)
|
||||||
|
DECL_HANDLER(ModulatorEffectHandler, ModulatorProps)
|
||||||
|
DECL_HANDLER(PshifterEffectHandler, PshifterProps)
|
||||||
|
DECL_HANDLER(VmorpherEffectHandler, VmorpherProps)
|
||||||
|
DECL_HANDLER(DedicatedDialogEffectHandler, DedicatedProps)
|
||||||
|
DECL_HANDLER(DedicatedLfeEffectHandler, DedicatedProps)
|
||||||
|
DECL_HANDLER(ConvolutionEffectHandler, ConvolutionProps)
|
||||||
|
#undef DECL_HANDLER
|
||||||
|
|
||||||
|
|
||||||
struct EffectVtable {
|
using effect_exception = al::context_error;
|
||||||
void (*const setParami)(EffectProps *props, ALenum param, int val);
|
|
||||||
void (*const setParamiv)(EffectProps *props, ALenum param, const int *vals);
|
|
||||||
void (*const setParamf)(EffectProps *props, ALenum param, float val);
|
|
||||||
void (*const setParamfv)(EffectProps *props, ALenum param, const float *vals);
|
|
||||||
|
|
||||||
void (*const getParami)(const EffectProps *props, ALenum param, int *val);
|
|
||||||
void (*const getParamiv)(const EffectProps *props, ALenum param, int *vals);
|
|
||||||
void (*const getParamf)(const EffectProps *props, ALenum param, float *val);
|
|
||||||
void (*const getParamfv)(const EffectProps *props, ALenum param, float *vals);
|
|
||||||
};
|
|
||||||
|
|
||||||
#define DEFINE_ALEFFECT_VTABLE(T) \
|
|
||||||
const EffectVtable T##EffectVtable = { \
|
|
||||||
T##_setParami, T##_setParamiv, \
|
|
||||||
T##_setParamf, T##_setParamfv, \
|
|
||||||
T##_getParami, T##_getParamiv, \
|
|
||||||
T##_getParamf, T##_getParamfv, \
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Default properties for the given effect types. */
|
/* Default properties for the given effect types. */
|
||||||
|
|
@ -64,25 +59,8 @@ extern const EffectProps FshifterEffectProps;
|
||||||
extern const EffectProps ModulatorEffectProps;
|
extern const EffectProps ModulatorEffectProps;
|
||||||
extern const EffectProps PshifterEffectProps;
|
extern const EffectProps PshifterEffectProps;
|
||||||
extern const EffectProps VmorpherEffectProps;
|
extern const EffectProps VmorpherEffectProps;
|
||||||
extern const EffectProps DedicatedEffectProps;
|
extern const EffectProps DedicatedDialogEffectProps;
|
||||||
|
extern const EffectProps DedicatedLfeEffectProps;
|
||||||
extern const EffectProps ConvolutionEffectProps;
|
extern const EffectProps ConvolutionEffectProps;
|
||||||
|
|
||||||
/* Vtables to get/set properties for the given effect types. */
|
|
||||||
extern const EffectVtable NullEffectVtable;
|
|
||||||
extern const EffectVtable ReverbEffectVtable;
|
|
||||||
extern const EffectVtable StdReverbEffectVtable;
|
|
||||||
extern const EffectVtable AutowahEffectVtable;
|
|
||||||
extern const EffectVtable ChorusEffectVtable;
|
|
||||||
extern const EffectVtable CompressorEffectVtable;
|
|
||||||
extern const EffectVtable DistortionEffectVtable;
|
|
||||||
extern const EffectVtable EchoEffectVtable;
|
|
||||||
extern const EffectVtable EqualizerEffectVtable;
|
|
||||||
extern const EffectVtable FlangerEffectVtable;
|
|
||||||
extern const EffectVtable FshifterEffectVtable;
|
|
||||||
extern const EffectVtable ModulatorEffectVtable;
|
|
||||||
extern const EffectVtable PshifterEffectVtable;
|
|
||||||
extern const EffectVtable VmorpherEffectVtable;
|
|
||||||
extern const EffectVtable DedicatedEffectVtable;
|
|
||||||
extern const EffectVtable ConvolutionEffectVtable;
|
|
||||||
|
|
||||||
#endif /* AL_EFFECTS_EFFECTS_H */
|
#endif /* AL_EFFECTS_EFFECTS_H */
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.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"
|
||||||
#endif // ALSOFT_EAX
|
#endif // ALSOFT_EAX
|
||||||
|
|
@ -16,163 +17,133 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void Equalizer_setParami(EffectProps*, ALenum param, int)
|
constexpr EffectProps genDefaultProps() noexcept
|
||||||
|
{
|
||||||
|
EqualizerProps props{};
|
||||||
|
props.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF;
|
||||||
|
props.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN;
|
||||||
|
props.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER;
|
||||||
|
props.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN;
|
||||||
|
props.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH;
|
||||||
|
props.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER;
|
||||||
|
props.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN;
|
||||||
|
props.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH;
|
||||||
|
props.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF;
|
||||||
|
props.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN;
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
const EffectProps EqualizerEffectProps{genDefaultProps()};
|
||||||
|
|
||||||
|
void EqualizerEffectHandler::SetParami(EqualizerProps&, ALenum param, int)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; }
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; }
|
||||||
void Equalizer_setParamiv(EffectProps*, ALenum param, const int*)
|
void EqualizerEffectHandler::SetParamiv(EqualizerProps&, ALenum param, const int*)
|
||||||
{
|
{
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x",
|
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x",
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
void Equalizer_setParamf(EffectProps *props, ALenum param, float val)
|
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Equalizer low-band gain out of range"};
|
||||||
props->Equalizer.LowGain = val;
|
props.LowGain = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Equalizer low-band cutoff out of range"};
|
||||||
props->Equalizer.LowCutoff = val;
|
props.LowCutoff = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band gain out of range"};
|
||||||
props->Equalizer.Mid1Gain = val;
|
props.Mid1Gain = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band center out of range"};
|
||||||
props->Equalizer.Mid1Center = val;
|
props.Mid1Center = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band width out of range"};
|
||||||
props->Equalizer.Mid1Width = val;
|
props.Mid1Width = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band gain out of range"};
|
||||||
props->Equalizer.Mid2Gain = val;
|
props.Mid2Gain = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band center out of range"};
|
||||||
props->Equalizer.Mid2Center = val;
|
props.Mid2Center = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band width out of range"};
|
||||||
props->Equalizer.Mid2Width = val;
|
props.Mid2Width = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band gain out of range"};
|
||||||
props->Equalizer.HighGain = val;
|
props.HighGain = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band cutoff out of range"};
|
||||||
props->Equalizer.HighCutoff = val;
|
props.HighCutoff = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Equalizer_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
void EqualizerEffectHandler::SetParamfv(EqualizerProps &props, ALenum param, const float *vals)
|
||||||
{ Equalizer_setParamf(props, param, vals[0]); }
|
{ SetParamf(props, param, *vals); }
|
||||||
|
|
||||||
void Equalizer_getParami(const EffectProps*, ALenum param, int*)
|
void EqualizerEffectHandler::GetParami(const EqualizerProps&, ALenum param, int*)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; }
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; }
|
||||||
void Equalizer_getParamiv(const EffectProps*, ALenum param, int*)
|
void EqualizerEffectHandler::GetParamiv(const EqualizerProps&, ALenum param, int*)
|
||||||
{
|
{
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x",
|
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x",
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
void Equalizer_getParamf(const EffectProps *props, ALenum param, float *val)
|
void EqualizerEffectHandler::GetParamf(const EqualizerProps &props, ALenum param, float *val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_EQUALIZER_LOW_GAIN:
|
case AL_EQUALIZER_LOW_GAIN: *val = props.LowGain; break;
|
||||||
*val = props->Equalizer.LowGain;
|
case AL_EQUALIZER_LOW_CUTOFF: *val = props.LowCutoff; break;
|
||||||
break;
|
case AL_EQUALIZER_MID1_GAIN: *val = props.Mid1Gain; break;
|
||||||
|
case AL_EQUALIZER_MID1_CENTER: *val = props.Mid1Center; break;
|
||||||
case AL_EQUALIZER_LOW_CUTOFF:
|
case AL_EQUALIZER_MID1_WIDTH: *val = props.Mid1Width; break;
|
||||||
*val = props->Equalizer.LowCutoff;
|
case AL_EQUALIZER_MID2_GAIN: *val = props.Mid2Gain; break;
|
||||||
break;
|
case AL_EQUALIZER_MID2_CENTER: *val = props.Mid2Center; break;
|
||||||
|
case AL_EQUALIZER_MID2_WIDTH: *val = props.Mid2Width; break;
|
||||||
case AL_EQUALIZER_MID1_GAIN:
|
case AL_EQUALIZER_HIGH_GAIN: *val = props.HighGain; break;
|
||||||
*val = props->Equalizer.Mid1Gain;
|
case AL_EQUALIZER_HIGH_CUTOFF: *val = props.HighCutoff; break;
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_EQUALIZER_MID1_CENTER:
|
|
||||||
*val = props->Equalizer.Mid1Center;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_EQUALIZER_MID1_WIDTH:
|
|
||||||
*val = props->Equalizer.Mid1Width;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_EQUALIZER_MID2_GAIN:
|
|
||||||
*val = props->Equalizer.Mid2Gain;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_EQUALIZER_MID2_CENTER:
|
|
||||||
*val = props->Equalizer.Mid2Center;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_EQUALIZER_MID2_WIDTH:
|
|
||||||
*val = props->Equalizer.Mid2Width;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_EQUALIZER_HIGH_GAIN:
|
|
||||||
*val = props->Equalizer.HighGain;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_EQUALIZER_HIGH_CUTOFF:
|
|
||||||
*val = props->Equalizer.HighCutoff;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Equalizer_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
void EqualizerEffectHandler::GetParamfv(const EqualizerProps &props, ALenum param, float *vals)
|
||||||
{ Equalizer_getParamf(props, param, vals); }
|
{ GetParamf(props, param, vals); }
|
||||||
|
|
||||||
EffectProps genDefaultProps() noexcept
|
|
||||||
{
|
|
||||||
EffectProps props{};
|
|
||||||
props.Equalizer.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF;
|
|
||||||
props.Equalizer.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN;
|
|
||||||
props.Equalizer.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER;
|
|
||||||
props.Equalizer.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN;
|
|
||||||
props.Equalizer.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH;
|
|
||||||
props.Equalizer.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER;
|
|
||||||
props.Equalizer.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN;
|
|
||||||
props.Equalizer.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH;
|
|
||||||
props.Equalizer.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF;
|
|
||||||
props.Equalizer.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN;
|
|
||||||
return props;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
DEFINE_ALEFFECT_VTABLE(Equalizer);
|
|
||||||
|
|
||||||
const EffectProps EqualizerEffectProps{genDefaultProps()};
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -319,91 +290,86 @@ template<>
|
||||||
throw Exception{message};
|
throw Exception{message};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
bool EaxEqualizerCommitter::commit(const EAXEQUALIZERPROPERTIES &props)
|
||||||
bool EqualizerCommitter::commit(const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
if(props.mType == mEaxProps.mType && mEaxProps.mEqualizer.lLowGain == props.mEqualizer.lLowGain
|
if(auto *cur = std::get_if<EAXEQUALIZERPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||||
&& mEaxProps.mEqualizer.flLowCutOff == props.mEqualizer.flLowCutOff
|
|
||||||
&& mEaxProps.mEqualizer.lMid1Gain == props.mEqualizer.lMid1Gain
|
|
||||||
&& mEaxProps.mEqualizer.flMid1Center == props.mEqualizer.flMid1Center
|
|
||||||
&& mEaxProps.mEqualizer.flMid1Width == props.mEqualizer.flMid1Width
|
|
||||||
&& mEaxProps.mEqualizer.lMid2Gain == props.mEqualizer.lMid2Gain
|
|
||||||
&& mEaxProps.mEqualizer.flMid2Center == props.mEqualizer.flMid2Center
|
|
||||||
&& mEaxProps.mEqualizer.flMid2Width == props.mEqualizer.flMid2Width
|
|
||||||
&& mEaxProps.mEqualizer.lHighGain == props.mEqualizer.lHighGain
|
|
||||||
&& mEaxProps.mEqualizer.flHighCutOff == props.mEqualizer.flHighCutOff)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mEaxProps = props;
|
mEaxProps = props;
|
||||||
|
mAlProps = [&]{
|
||||||
mAlProps.Equalizer.LowGain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lLowGain));
|
EqualizerProps ret{};
|
||||||
mAlProps.Equalizer.LowCutoff = props.mEqualizer.flLowCutOff;
|
ret.LowGain = level_mb_to_gain(static_cast<float>(props.lLowGain));
|
||||||
mAlProps.Equalizer.Mid1Gain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lMid1Gain));
|
ret.LowCutoff = props.flLowCutOff;
|
||||||
mAlProps.Equalizer.Mid1Center = props.mEqualizer.flMid1Center;
|
ret.Mid1Gain = level_mb_to_gain(static_cast<float>(props.lMid1Gain));
|
||||||
mAlProps.Equalizer.Mid1Width = props.mEqualizer.flMid1Width;
|
ret.Mid1Center = props.flMid1Center;
|
||||||
mAlProps.Equalizer.Mid2Gain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lMid2Gain));
|
ret.Mid1Width = props.flMid1Width;
|
||||||
mAlProps.Equalizer.Mid2Center = props.mEqualizer.flMid2Center;
|
ret.Mid2Gain = level_mb_to_gain(static_cast<float>(props.lMid2Gain));
|
||||||
mAlProps.Equalizer.Mid2Width = props.mEqualizer.flMid2Width;
|
ret.Mid2Center = props.flMid2Center;
|
||||||
mAlProps.Equalizer.HighGain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lHighGain));
|
ret.Mid2Width = props.flMid2Width;
|
||||||
mAlProps.Equalizer.HighCutoff = props.mEqualizer.flHighCutOff;
|
ret.HighGain = level_mb_to_gain(static_cast<float>(props.lHighGain));
|
||||||
|
ret.HighCutoff = props.flHighCutOff;
|
||||||
|
return ret;
|
||||||
|
}();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxEqualizerCommitter::SetDefaults(EaxEffectProps &props)
|
||||||
void EqualizerCommitter::SetDefaults(EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
props.mType = EaxEffectType::Equalizer;
|
static constexpr EAXEQUALIZERPROPERTIES defprops{[]
|
||||||
props.mEqualizer.lLowGain = EAXEQUALIZER_DEFAULTLOWGAIN;
|
{
|
||||||
props.mEqualizer.flLowCutOff = EAXEQUALIZER_DEFAULTLOWCUTOFF;
|
EAXEQUALIZERPROPERTIES ret{};
|
||||||
props.mEqualizer.lMid1Gain = EAXEQUALIZER_DEFAULTMID1GAIN;
|
ret.lLowGain = EAXEQUALIZER_DEFAULTLOWGAIN;
|
||||||
props.mEqualizer.flMid1Center = EAXEQUALIZER_DEFAULTMID1CENTER;
|
ret.flLowCutOff = EAXEQUALIZER_DEFAULTLOWCUTOFF;
|
||||||
props.mEqualizer.flMid1Width = EAXEQUALIZER_DEFAULTMID1WIDTH;
|
ret.lMid1Gain = EAXEQUALIZER_DEFAULTMID1GAIN;
|
||||||
props.mEqualizer.lMid2Gain = EAXEQUALIZER_DEFAULTMID2GAIN;
|
ret.flMid1Center = EAXEQUALIZER_DEFAULTMID1CENTER;
|
||||||
props.mEqualizer.flMid2Center = EAXEQUALIZER_DEFAULTMID2CENTER;
|
ret.flMid1Width = EAXEQUALIZER_DEFAULTMID1WIDTH;
|
||||||
props.mEqualizer.flMid2Width = EAXEQUALIZER_DEFAULTMID2WIDTH;
|
ret.lMid2Gain = EAXEQUALIZER_DEFAULTMID2GAIN;
|
||||||
props.mEqualizer.lHighGain = EAXEQUALIZER_DEFAULTHIGHGAIN;
|
ret.flMid2Center = EAXEQUALIZER_DEFAULTMID2CENTER;
|
||||||
props.mEqualizer.flHighCutOff = EAXEQUALIZER_DEFAULTHIGHCUTOFF;
|
ret.flMid2Width = EAXEQUALIZER_DEFAULTMID2WIDTH;
|
||||||
|
ret.lHighGain = EAXEQUALIZER_DEFAULTHIGHGAIN;
|
||||||
|
ret.flHighCutOff = EAXEQUALIZER_DEFAULTHIGHCUTOFF;
|
||||||
|
return ret;
|
||||||
|
}()};
|
||||||
|
props = defprops;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxEqualizerCommitter::Get(const EaxCall &call, const EAXEQUALIZERPROPERTIES &props)
|
||||||
void EqualizerCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case EAXEQUALIZER_NONE: break;
|
case EAXEQUALIZER_NONE: break;
|
||||||
case EAXEQUALIZER_ALLPARAMETERS: call.set_value<Exception>(props.mEqualizer); break;
|
case EAXEQUALIZER_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||||
case EAXEQUALIZER_LOWGAIN: call.set_value<Exception>(props.mEqualizer.lLowGain); break;
|
case EAXEQUALIZER_LOWGAIN: call.set_value<Exception>(props.lLowGain); break;
|
||||||
case EAXEQUALIZER_LOWCUTOFF: call.set_value<Exception>(props.mEqualizer.flLowCutOff); break;
|
case EAXEQUALIZER_LOWCUTOFF: call.set_value<Exception>(props.flLowCutOff); break;
|
||||||
case EAXEQUALIZER_MID1GAIN: call.set_value<Exception>(props.mEqualizer.lMid1Gain); break;
|
case EAXEQUALIZER_MID1GAIN: call.set_value<Exception>(props.lMid1Gain); break;
|
||||||
case EAXEQUALIZER_MID1CENTER: call.set_value<Exception>(props.mEqualizer.flMid1Center); break;
|
case EAXEQUALIZER_MID1CENTER: call.set_value<Exception>(props.flMid1Center); break;
|
||||||
case EAXEQUALIZER_MID1WIDTH: call.set_value<Exception>(props.mEqualizer.flMid1Width); break;
|
case EAXEQUALIZER_MID1WIDTH: call.set_value<Exception>(props.flMid1Width); break;
|
||||||
case EAXEQUALIZER_MID2GAIN: call.set_value<Exception>(props.mEqualizer.lMid2Gain); break;
|
case EAXEQUALIZER_MID2GAIN: call.set_value<Exception>(props.lMid2Gain); break;
|
||||||
case EAXEQUALIZER_MID2CENTER: call.set_value<Exception>(props.mEqualizer.flMid2Center); break;
|
case EAXEQUALIZER_MID2CENTER: call.set_value<Exception>(props.flMid2Center); break;
|
||||||
case EAXEQUALIZER_MID2WIDTH: call.set_value<Exception>(props.mEqualizer.flMid2Width); break;
|
case EAXEQUALIZER_MID2WIDTH: call.set_value<Exception>(props.flMid2Width); break;
|
||||||
case EAXEQUALIZER_HIGHGAIN: call.set_value<Exception>(props.mEqualizer.lHighGain); break;
|
case EAXEQUALIZER_HIGHGAIN: call.set_value<Exception>(props.lHighGain); break;
|
||||||
case EAXEQUALIZER_HIGHCUTOFF: call.set_value<Exception>(props.mEqualizer.flHighCutOff); break;
|
case EAXEQUALIZER_HIGHCUTOFF: call.set_value<Exception>(props.flHighCutOff); break;
|
||||||
default: fail_unknown_property_id();
|
default: fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxEqualizerCommitter::Set(const EaxCall &call, EAXEQUALIZERPROPERTIES &props)
|
||||||
void EqualizerCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case EAXEQUALIZER_NONE: break;
|
case EAXEQUALIZER_NONE: break;
|
||||||
case EAXEQUALIZER_ALLPARAMETERS: defer<AllValidator>(call, props.mEqualizer); break;
|
case EAXEQUALIZER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||||
case EAXEQUALIZER_LOWGAIN: defer<LowGainValidator>(call, props.mEqualizer.lLowGain); break;
|
case EAXEQUALIZER_LOWGAIN: defer<LowGainValidator>(call, props.lLowGain); break;
|
||||||
case EAXEQUALIZER_LOWCUTOFF: defer<LowCutOffValidator>(call, props.mEqualizer.flLowCutOff); break;
|
case EAXEQUALIZER_LOWCUTOFF: defer<LowCutOffValidator>(call, props.flLowCutOff); break;
|
||||||
case EAXEQUALIZER_MID1GAIN: defer<Mid1GainValidator>(call, props.mEqualizer.lMid1Gain); break;
|
case EAXEQUALIZER_MID1GAIN: defer<Mid1GainValidator>(call, props.lMid1Gain); break;
|
||||||
case EAXEQUALIZER_MID1CENTER: defer<Mid1CenterValidator>(call, props.mEqualizer.flMid1Center); break;
|
case EAXEQUALIZER_MID1CENTER: defer<Mid1CenterValidator>(call, props.flMid1Center); break;
|
||||||
case EAXEQUALIZER_MID1WIDTH: defer<Mid1WidthValidator>(call, props.mEqualizer.flMid1Width); break;
|
case EAXEQUALIZER_MID1WIDTH: defer<Mid1WidthValidator>(call, props.flMid1Width); break;
|
||||||
case EAXEQUALIZER_MID2GAIN: defer<Mid2GainValidator>(call, props.mEqualizer.lMid2Gain); break;
|
case EAXEQUALIZER_MID2GAIN: defer<Mid2GainValidator>(call, props.lMid2Gain); break;
|
||||||
case EAXEQUALIZER_MID2CENTER: defer<Mid2CenterValidator>(call, props.mEqualizer.flMid2Center); break;
|
case EAXEQUALIZER_MID2CENTER: defer<Mid2CenterValidator>(call, props.flMid2Center); break;
|
||||||
case EAXEQUALIZER_MID2WIDTH: defer<Mid2WidthValidator>(call, props.mEqualizer.flMid2Width); break;
|
case EAXEQUALIZER_MID2WIDTH: defer<Mid2WidthValidator>(call, props.flMid2Width); break;
|
||||||
case EAXEQUALIZER_HIGHGAIN: defer<HighGainValidator>(call, props.mEqualizer.lHighGain); break;
|
case EAXEQUALIZER_HIGHGAIN: defer<HighGainValidator>(call, props.lHighGain); break;
|
||||||
case EAXEQUALIZER_HIGHCUTOFF: defer<HighCutOffValidator>(call, props.mEqualizer.flHighCutOff); break;
|
case EAXEQUALIZER_HIGHCUTOFF: defer<HighCutOffValidator>(call, props.flHighCutOff); break;
|
||||||
default: fail_unknown_property_id();
|
default: fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,19 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
#include "alc/effects/base.h"
|
#include "alc/effects/base.h"
|
||||||
#include "aloptional.h"
|
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.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"
|
||||||
#endif // ALSOFT_EAX
|
#endif // ALSOFT_EAX
|
||||||
|
|
@ -20,7 +21,7 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
al::optional<FShifterDirection> DirectionFromEmum(ALenum value)
|
constexpr std::optional<FShifterDirection> DirectionFromEmum(ALenum value) noexcept
|
||||||
{
|
{
|
||||||
switch(value)
|
switch(value)
|
||||||
{
|
{
|
||||||
|
|
@ -28,9 +29,9 @@ al::optional<FShifterDirection> DirectionFromEmum(ALenum value)
|
||||||
case AL_FREQUENCY_SHIFTER_DIRECTION_UP: return FShifterDirection::Up;
|
case AL_FREQUENCY_SHIFTER_DIRECTION_UP: return FShifterDirection::Up;
|
||||||
case AL_FREQUENCY_SHIFTER_DIRECTION_OFF: return FShifterDirection::Off;
|
case AL_FREQUENCY_SHIFTER_DIRECTION_OFF: return FShifterDirection::Off;
|
||||||
}
|
}
|
||||||
return al::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
ALenum EnumFromDirection(FShifterDirection dir)
|
constexpr ALenum EnumFromDirection(FShifterDirection dir)
|
||||||
{
|
{
|
||||||
switch(dir)
|
switch(dir)
|
||||||
{
|
{
|
||||||
|
|
@ -41,31 +42,26 @@ ALenum EnumFromDirection(FShifterDirection dir)
|
||||||
throw std::runtime_error{"Invalid direction: "+std::to_string(static_cast<int>(dir))};
|
throw std::runtime_error{"Invalid direction: "+std::to_string(static_cast<int>(dir))};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fshifter_setParamf(EffectProps *props, ALenum param, float val)
|
constexpr EffectProps genDefaultProps() noexcept
|
||||||
{
|
{
|
||||||
switch(param)
|
FshifterProps props{};
|
||||||
{
|
props.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY;
|
||||||
case AL_FREQUENCY_SHIFTER_FREQUENCY:
|
props.LeftDirection = DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION).value();
|
||||||
if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY))
|
props.RightDirection = DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION).value();
|
||||||
throw effect_exception{AL_INVALID_VALUE, "Frequency shifter frequency out of range"};
|
return props;
|
||||||
props->Fshifter.Frequency = val;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
void Fshifter_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
|
||||||
{ Fshifter_setParamf(props, param, vals[0]); }
|
|
||||||
|
|
||||||
void Fshifter_setParami(EffectProps *props, ALenum param, int val)
|
} // namespace
|
||||||
|
|
||||||
|
const EffectProps FshifterEffectProps{genDefaultProps()};
|
||||||
|
|
||||||
|
void FshifterEffectHandler::SetParami(FshifterProps &props, ALenum param, int val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
|
case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
|
||||||
if(auto diropt = DirectionFromEmum(val))
|
if(auto diropt = DirectionFromEmum(val))
|
||||||
props->Fshifter.LeftDirection = *diropt;
|
props.LeftDirection = *diropt;
|
||||||
else
|
else
|
||||||
throw effect_exception{AL_INVALID_VALUE,
|
throw effect_exception{AL_INVALID_VALUE,
|
||||||
"Unsupported frequency shifter left direction: 0x%04x", val};
|
"Unsupported frequency shifter left direction: 0x%04x", val};
|
||||||
|
|
@ -73,7 +69,7 @@ void Fshifter_setParami(EffectProps *props, ALenum param, int val)
|
||||||
|
|
||||||
case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
|
case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
|
||||||
if(auto diropt = DirectionFromEmum(val))
|
if(auto diropt = DirectionFromEmum(val))
|
||||||
props->Fshifter.RightDirection = *diropt;
|
props.RightDirection = *diropt;
|
||||||
else
|
else
|
||||||
throw effect_exception{AL_INVALID_VALUE,
|
throw effect_exception{AL_INVALID_VALUE,
|
||||||
"Unsupported frequency shifter right direction: 0x%04x", val};
|
"Unsupported frequency shifter right direction: 0x%04x", val};
|
||||||
|
|
@ -84,33 +80,17 @@ void Fshifter_setParami(EffectProps *props, ALenum param, int val)
|
||||||
"Invalid frequency shifter integer property 0x%04x", param};
|
"Invalid frequency shifter integer property 0x%04x", param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Fshifter_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
void FshifterEffectHandler::SetParamiv(FshifterProps &props, ALenum param, const int *vals)
|
||||||
{ Fshifter_setParami(props, param, vals[0]); }
|
{ SetParami(props, param, *vals); }
|
||||||
|
|
||||||
void Fshifter_getParami(const EffectProps *props, ALenum param, int *val)
|
void FshifterEffectHandler::SetParamf(FshifterProps &props, ALenum param, float val)
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
|
|
||||||
*val = EnumFromDirection(props->Fshifter.LeftDirection);
|
|
||||||
break;
|
|
||||||
case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
|
|
||||||
*val = EnumFromDirection(props->Fshifter.RightDirection);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM,
|
|
||||||
"Invalid frequency shifter integer property 0x%04x", param};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Fshifter_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
|
||||||
{ Fshifter_getParami(props, param, vals); }
|
|
||||||
|
|
||||||
void Fshifter_getParamf(const EffectProps *props, ALenum param, float *val)
|
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_FREQUENCY_SHIFTER_FREQUENCY:
|
case AL_FREQUENCY_SHIFTER_FREQUENCY:
|
||||||
*val = props->Fshifter.Frequency;
|
if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY))
|
||||||
|
throw effect_exception{AL_INVALID_VALUE, "Frequency shifter frequency out of range"};
|
||||||
|
props.Frequency = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -118,23 +98,44 @@ void Fshifter_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Fshifter_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
void FshifterEffectHandler::SetParamfv(FshifterProps &props, ALenum param, const float *vals)
|
||||||
{ Fshifter_getParamf(props, param, vals); }
|
{ SetParamf(props, param, *vals); }
|
||||||
|
|
||||||
EffectProps genDefaultProps() noexcept
|
void FshifterEffectHandler::GetParami(const FshifterProps &props, ALenum param, int *val)
|
||||||
{
|
{
|
||||||
EffectProps props{};
|
switch(param)
|
||||||
props.Fshifter.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY;
|
{
|
||||||
props.Fshifter.LeftDirection = *DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION);
|
case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
|
||||||
props.Fshifter.RightDirection = *DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION);
|
*val = EnumFromDirection(props.LeftDirection);
|
||||||
return props;
|
break;
|
||||||
|
case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
|
||||||
|
*val = EnumFromDirection(props.RightDirection);
|
||||||
|
break;
|
||||||
|
|
||||||
|
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); }
|
||||||
|
|
||||||
} // namespace
|
void FshifterEffectHandler::GetParamf(const FshifterProps &props, ALenum param, float *val)
|
||||||
|
{
|
||||||
|
switch(param)
|
||||||
|
{
|
||||||
|
case AL_FREQUENCY_SHIFTER_FREQUENCY:
|
||||||
|
*val = props.Frequency;
|
||||||
|
break;
|
||||||
|
|
||||||
DEFINE_ALEFFECT_VTABLE(Fshifter);
|
default:
|
||||||
|
throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
|
||||||
|
param};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void FshifterEffectHandler::GetParamfv(const FshifterProps &props, ALenum param, float *vals)
|
||||||
|
{ GetParamf(props, param, vals); }
|
||||||
|
|
||||||
const EffectProps FshifterEffectProps{genDefaultProps()};
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -197,13 +198,9 @@ template<>
|
||||||
throw Exception{message};
|
throw Exception{message};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
bool EaxFrequencyShifterCommitter::commit(const EAXFREQUENCYSHIFTERPROPERTIES &props)
|
||||||
bool FrequencyShifterCommitter::commit(const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
if(props.mType == mEaxProps.mType
|
if(auto *cur = std::get_if<EAXFREQUENCYSHIFTERPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||||
&& mEaxProps.mFrequencyShifter.flFrequency == props.mFrequencyShifter.flFrequency
|
|
||||||
&& mEaxProps.mFrequencyShifter.ulLeftDirection == props.mFrequencyShifter.ulLeftDirection
|
|
||||||
&& mEaxProps.mFrequencyShifter.ulRightDirection == props.mFrequencyShifter.ulRightDirection)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mEaxProps = props;
|
mEaxProps = props;
|
||||||
|
|
@ -217,46 +214,52 @@ bool FrequencyShifterCommitter::commit(const EaxEffectProps &props)
|
||||||
return FShifterDirection::Off;
|
return FShifterDirection::Off;
|
||||||
};
|
};
|
||||||
|
|
||||||
mAlProps.Fshifter.Frequency = props.mFrequencyShifter.flFrequency;
|
mAlProps = [&]{
|
||||||
mAlProps.Fshifter.LeftDirection = get_direction(props.mFrequencyShifter.ulLeftDirection);
|
FshifterProps ret{};
|
||||||
mAlProps.Fshifter.RightDirection = get_direction(props.mFrequencyShifter.ulRightDirection);
|
ret.Frequency = props.flFrequency;
|
||||||
|
ret.LeftDirection = get_direction(props.ulLeftDirection);
|
||||||
|
ret.RightDirection = get_direction(props.ulRightDirection);
|
||||||
|
return ret;
|
||||||
|
}();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxFrequencyShifterCommitter::SetDefaults(EaxEffectProps &props)
|
||||||
void FrequencyShifterCommitter::SetDefaults(EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
props.mType = EaxEffectType::FrequencyShifter;
|
static constexpr EAXFREQUENCYSHIFTERPROPERTIES defprops{[]
|
||||||
props.mFrequencyShifter.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY;
|
{
|
||||||
props.mFrequencyShifter.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION;
|
EAXFREQUENCYSHIFTERPROPERTIES ret{};
|
||||||
props.mFrequencyShifter.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION;
|
ret.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY;
|
||||||
|
ret.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION;
|
||||||
|
ret.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION;
|
||||||
|
return ret;
|
||||||
|
}()};
|
||||||
|
props = defprops;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxFrequencyShifterCommitter::Get(const EaxCall &call, const EAXFREQUENCYSHIFTERPROPERTIES &props)
|
||||||
void FrequencyShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case EAXFREQUENCYSHIFTER_NONE: break;
|
case EAXFREQUENCYSHIFTER_NONE: break;
|
||||||
case EAXFREQUENCYSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props.mFrequencyShifter); break;
|
case EAXFREQUENCYSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||||
case EAXFREQUENCYSHIFTER_FREQUENCY: call.set_value<Exception>(props.mFrequencyShifter.flFrequency); break;
|
case EAXFREQUENCYSHIFTER_FREQUENCY: call.set_value<Exception>(props.flFrequency); break;
|
||||||
case EAXFREQUENCYSHIFTER_LEFTDIRECTION: call.set_value<Exception>(props.mFrequencyShifter.ulLeftDirection); break;
|
case EAXFREQUENCYSHIFTER_LEFTDIRECTION: call.set_value<Exception>(props.ulLeftDirection); break;
|
||||||
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: call.set_value<Exception>(props.mFrequencyShifter.ulRightDirection); break;
|
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: call.set_value<Exception>(props.ulRightDirection); break;
|
||||||
default: fail_unknown_property_id();
|
default: fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxFrequencyShifterCommitter::Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props)
|
||||||
void FrequencyShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case EAXFREQUENCYSHIFTER_NONE: break;
|
case EAXFREQUENCYSHIFTER_NONE: break;
|
||||||
case EAXFREQUENCYSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props.mFrequencyShifter); break;
|
case EAXFREQUENCYSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||||
case EAXFREQUENCYSHIFTER_FREQUENCY: defer<FrequencyValidator>(call, props.mFrequencyShifter.flFrequency); break;
|
case EAXFREQUENCYSHIFTER_FREQUENCY: defer<FrequencyValidator>(call, props.flFrequency); break;
|
||||||
case EAXFREQUENCYSHIFTER_LEFTDIRECTION: defer<LeftDirectionValidator>(call, props.mFrequencyShifter.ulLeftDirection); break;
|
case EAXFREQUENCYSHIFTER_LEFTDIRECTION: defer<LeftDirectionValidator>(call, props.ulLeftDirection); break;
|
||||||
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: defer<RightDirectionValidator>(call, props.mFrequencyShifter.ulRightDirection); break;
|
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: defer<RightDirectionValidator>(call, props.ulRightDirection); break;
|
||||||
default: fail_unknown_property_id();
|
default: fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,19 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
#include "alc/effects/base.h"
|
#include "alc/effects/base.h"
|
||||||
#include "aloptional.h"
|
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.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"
|
||||||
#endif // ALSOFT_EAX
|
#endif // ALSOFT_EAX
|
||||||
|
|
@ -20,7 +21,7 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
al::optional<ModulatorWaveform> WaveformFromEmum(ALenum value)
|
constexpr std::optional<ModulatorWaveform> WaveformFromEmum(ALenum value) noexcept
|
||||||
{
|
{
|
||||||
switch(value)
|
switch(value)
|
||||||
{
|
{
|
||||||
|
|
@ -28,9 +29,9 @@ al::optional<ModulatorWaveform> WaveformFromEmum(ALenum value)
|
||||||
case AL_RING_MODULATOR_SAWTOOTH: return ModulatorWaveform::Sawtooth;
|
case AL_RING_MODULATOR_SAWTOOTH: return ModulatorWaveform::Sawtooth;
|
||||||
case AL_RING_MODULATOR_SQUARE: return ModulatorWaveform::Square;
|
case AL_RING_MODULATOR_SQUARE: return ModulatorWaveform::Square;
|
||||||
}
|
}
|
||||||
return al::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
ALenum EnumFromWaveform(ModulatorWaveform type)
|
constexpr ALenum EnumFromWaveform(ModulatorWaveform type)
|
||||||
{
|
{
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
|
|
@ -42,40 +43,31 @@ ALenum EnumFromWaveform(ModulatorWaveform type)
|
||||||
std::to_string(static_cast<int>(type))};
|
std::to_string(static_cast<int>(type))};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Modulator_setParamf(EffectProps *props, ALenum param, float val)
|
constexpr EffectProps genDefaultProps() noexcept
|
||||||
{
|
{
|
||||||
switch(param)
|
ModulatorProps props{};
|
||||||
{
|
props.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
|
||||||
case AL_RING_MODULATOR_FREQUENCY:
|
props.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
|
||||||
if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
|
props.Waveform = WaveformFromEmum(AL_RING_MODULATOR_DEFAULT_WAVEFORM).value();
|
||||||
throw effect_exception{AL_INVALID_VALUE, "Modulator frequency out of range: %f", val};
|
return props;
|
||||||
props->Modulator.Frequency = val;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
|
||||||
if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
|
|
||||||
throw effect_exception{AL_INVALID_VALUE, "Modulator high-pass cutoff out of range: %f", val};
|
|
||||||
props->Modulator.HighPassCutoff = val;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
void Modulator_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
|
||||||
{ Modulator_setParamf(props, param, vals[0]); }
|
} // namespace
|
||||||
void Modulator_setParami(EffectProps *props, ALenum param, int val)
|
|
||||||
|
const EffectProps ModulatorEffectProps{genDefaultProps()};
|
||||||
|
|
||||||
|
void ModulatorEffectHandler::SetParami(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:
|
||||||
Modulator_setParamf(props, param, static_cast<float>(val));
|
SetParamf(props, param, static_cast<float>(val));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AL_RING_MODULATOR_WAVEFORM:
|
case AL_RING_MODULATOR_WAVEFORM:
|
||||||
if(auto formopt = WaveformFromEmum(val))
|
if(auto formopt = WaveformFromEmum(val))
|
||||||
props->Modulator.Waveform = *formopt;
|
props.Waveform = *formopt;
|
||||||
else
|
else
|
||||||
throw effect_exception{AL_INVALID_VALUE, "Invalid modulator waveform: 0x%04x", val};
|
throw effect_exception{AL_INVALID_VALUE, "Invalid modulator waveform: 0x%04x", val};
|
||||||
break;
|
break;
|
||||||
|
|
@ -85,62 +77,61 @@ void Modulator_setParami(EffectProps *props, ALenum param, int val)
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Modulator_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
void ModulatorEffectHandler::SetParamiv(ModulatorProps &props, ALenum param, const int *vals)
|
||||||
{ Modulator_setParami(props, param, vals[0]); }
|
{ SetParami(props, param, *vals); }
|
||||||
|
|
||||||
void Modulator_getParami(const EffectProps *props, ALenum param, int *val)
|
void ModulatorEffectHandler::SetParamf(ModulatorProps &props, ALenum param, float val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_RING_MODULATOR_FREQUENCY:
|
case AL_RING_MODULATOR_FREQUENCY:
|
||||||
*val = static_cast<int>(props->Modulator.Frequency);
|
if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
|
||||||
break;
|
throw effect_exception{AL_INVALID_VALUE, "Modulator frequency out of range: %f", val};
|
||||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
props.Frequency = val;
|
||||||
*val = static_cast<int>(props->Modulator.HighPassCutoff);
|
|
||||||
break;
|
|
||||||
case AL_RING_MODULATOR_WAVEFORM:
|
|
||||||
*val = EnumFromWaveform(props->Modulator.Waveform);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Modulator_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
|
||||||
{ Modulator_getParami(props, param, vals); }
|
|
||||||
void Modulator_getParamf(const EffectProps *props, ALenum param, float *val)
|
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
case AL_RING_MODULATOR_FREQUENCY:
|
|
||||||
*val = props->Modulator.Frequency;
|
|
||||||
break;
|
|
||||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||||
*val = props->Modulator.HighPassCutoff;
|
if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
|
||||||
|
throw effect_exception{AL_INVALID_VALUE, "Modulator high-pass cutoff out of range: %f", val};
|
||||||
|
props.HighPassCutoff = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
|
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Modulator_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
void ModulatorEffectHandler::SetParamfv(ModulatorProps &props, ALenum param, const float *vals)
|
||||||
{ Modulator_getParamf(props, param, vals); }
|
{ SetParamf(props, param, *vals); }
|
||||||
|
|
||||||
EffectProps genDefaultProps() noexcept
|
void ModulatorEffectHandler::GetParami(const ModulatorProps &props, ALenum param, int *val)
|
||||||
{
|
{
|
||||||
EffectProps props{};
|
switch(param)
|
||||||
props.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
|
{
|
||||||
props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
|
case AL_RING_MODULATOR_FREQUENCY: *val = static_cast<int>(props.Frequency); break;
|
||||||
props.Modulator.Waveform = *WaveformFromEmum(AL_RING_MODULATOR_DEFAULT_WAVEFORM);
|
case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = static_cast<int>(props.HighPassCutoff); break;
|
||||||
return props;
|
case AL_RING_MODULATOR_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x",
|
||||||
|
param};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
void ModulatorEffectHandler::GetParamiv(const ModulatorProps &props, ALenum param, int *vals)
|
||||||
|
{ GetParami(props, param, vals); }
|
||||||
|
void ModulatorEffectHandler::GetParamf(const ModulatorProps &props, ALenum param, float *val)
|
||||||
|
{
|
||||||
|
switch(param)
|
||||||
|
{
|
||||||
|
case AL_RING_MODULATOR_FREQUENCY: *val = props.Frequency; break;
|
||||||
|
case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = props.HighPassCutoff; break;
|
||||||
|
|
||||||
} // namespace
|
default:
|
||||||
|
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void ModulatorEffectHandler::GetParamfv(const ModulatorProps &props, ALenum param, float *vals)
|
||||||
|
{ GetParamf(props, param, vals); }
|
||||||
|
|
||||||
DEFINE_ALEFFECT_VTABLE(Modulator);
|
|
||||||
|
|
||||||
const EffectProps ModulatorEffectProps{genDefaultProps()};
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -203,13 +194,9 @@ template<>
|
||||||
throw Exception{message};
|
throw Exception{message};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
bool EaxModulatorCommitter::commit(const EAXRINGMODULATORPROPERTIES &props)
|
||||||
bool ModulatorCommitter::commit(const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
if(props.mType == mEaxProps.mType
|
if(auto *cur = std::get_if<EAXRINGMODULATORPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||||
&& mEaxProps.mModulator.flFrequency == props.mModulator.flFrequency
|
|
||||||
&& mEaxProps.mModulator.flHighPassCutOff == props.mModulator.flHighPassCutOff
|
|
||||||
&& mEaxProps.mModulator.ulWaveform == props.mModulator.ulWaveform)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mEaxProps = props;
|
mEaxProps = props;
|
||||||
|
|
@ -225,46 +212,52 @@ bool ModulatorCommitter::commit(const EaxEffectProps &props)
|
||||||
return ModulatorWaveform::Sinusoid;
|
return ModulatorWaveform::Sinusoid;
|
||||||
};
|
};
|
||||||
|
|
||||||
mAlProps.Modulator.Frequency = props.mModulator.flFrequency;
|
mAlProps = [&]{
|
||||||
mAlProps.Modulator.HighPassCutoff = props.mModulator.flHighPassCutOff;
|
ModulatorProps ret{};
|
||||||
mAlProps.Modulator.Waveform = get_waveform(props.mModulator.ulWaveform);
|
ret.Frequency = props.flFrequency;
|
||||||
|
ret.HighPassCutoff = props.flHighPassCutOff;
|
||||||
|
ret.Waveform = get_waveform(props.ulWaveform);
|
||||||
|
return ret;
|
||||||
|
}();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxModulatorCommitter::SetDefaults(EaxEffectProps &props)
|
||||||
void ModulatorCommitter::SetDefaults(EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
props.mType = EaxEffectType::Modulator;
|
static constexpr EAXRINGMODULATORPROPERTIES defprops{[]
|
||||||
props.mModulator.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY;
|
{
|
||||||
props.mModulator.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF;
|
EAXRINGMODULATORPROPERTIES ret{};
|
||||||
props.mModulator.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM;
|
ret.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY;
|
||||||
|
ret.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF;
|
||||||
|
ret.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM;
|
||||||
|
return ret;
|
||||||
|
}()};
|
||||||
|
props = defprops;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxModulatorCommitter::Get(const EaxCall &call, const EAXRINGMODULATORPROPERTIES &props)
|
||||||
void ModulatorCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case EAXRINGMODULATOR_NONE: break;
|
case EAXRINGMODULATOR_NONE: break;
|
||||||
case EAXRINGMODULATOR_ALLPARAMETERS: call.set_value<Exception>(props.mModulator); break;
|
case EAXRINGMODULATOR_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||||
case EAXRINGMODULATOR_FREQUENCY: call.set_value<Exception>(props.mModulator.flFrequency); break;
|
case EAXRINGMODULATOR_FREQUENCY: call.set_value<Exception>(props.flFrequency); break;
|
||||||
case EAXRINGMODULATOR_HIGHPASSCUTOFF: call.set_value<Exception>(props.mModulator.flHighPassCutOff); break;
|
case EAXRINGMODULATOR_HIGHPASSCUTOFF: call.set_value<Exception>(props.flHighPassCutOff); break;
|
||||||
case EAXRINGMODULATOR_WAVEFORM: call.set_value<Exception>(props.mModulator.ulWaveform); break;
|
case EAXRINGMODULATOR_WAVEFORM: call.set_value<Exception>(props.ulWaveform); break;
|
||||||
default: fail_unknown_property_id();
|
default: fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxModulatorCommitter::Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props)
|
||||||
void ModulatorCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
switch (call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case EAXRINGMODULATOR_NONE: break;
|
case EAXRINGMODULATOR_NONE: break;
|
||||||
case EAXRINGMODULATOR_ALLPARAMETERS: defer<AllValidator>(call, props.mModulator); break;
|
case EAXRINGMODULATOR_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||||
case EAXRINGMODULATOR_FREQUENCY: defer<FrequencyValidator>(call, props.mModulator.flFrequency); break;
|
case EAXRINGMODULATOR_FREQUENCY: defer<FrequencyValidator>(call, props.flFrequency); break;
|
||||||
case EAXRINGMODULATOR_HIGHPASSCUTOFF: defer<HighPassCutOffValidator>(call, props.mModulator.flHighPassCutOff); break;
|
case EAXRINGMODULATOR_HIGHPASSCUTOFF: defer<HighPassCutOffValidator>(call, props.flHighPassCutOff); break;
|
||||||
case EAXRINGMODULATOR_WAVEFORM: defer<WaveformValidator>(call, props.mModulator.ulWaveform); break;
|
case EAXRINGMODULATOR_WAVEFORM: defer<WaveformValidator>(call, props.ulWaveform); break;
|
||||||
default: fail_unknown_property_id();
|
default: fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,94 +8,92 @@
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
|
#include "al/eax/effect.h"
|
||||||
#include "al/eax/exception.h"
|
#include "al/eax/exception.h"
|
||||||
#endif // ALSOFT_EAX
|
#endif // ALSOFT_EAX
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void Null_setParami(EffectProps* /*props*/, ALenum param, int /*val*/)
|
constexpr EffectProps genDefaultProps() noexcept
|
||||||
{
|
{
|
||||||
switch(param)
|
return std::monostate{};
|
||||||
{
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Null_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
Null_setParami(props, param, vals[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Null_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/)
|
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Null_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
Null_setParamf(props, param, vals[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Null_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/)
|
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Null_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
Null_getParami(props, param, vals);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Null_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/)
|
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
|
|
||||||
param};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Null_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
|
||||||
{
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
Null_getParamf(props, param, vals);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EffectProps genDefaultProps() noexcept
|
|
||||||
{
|
|
||||||
EffectProps props{};
|
|
||||||
return props;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
DEFINE_ALEFFECT_VTABLE(Null);
|
|
||||||
|
|
||||||
const EffectProps NullEffectProps{genDefaultProps()};
|
const EffectProps NullEffectProps{genDefaultProps()};
|
||||||
|
|
||||||
|
void NullEffectHandler::SetParami(std::monostate& /*props*/, ALenum param, int /*val*/)
|
||||||
|
{
|
||||||
|
switch(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)
|
||||||
|
{
|
||||||
|
switch(param)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
SetParami(props, param, *vals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void NullEffectHandler::SetParamf(std::monostate& /*props*/, ALenum param, float /*val*/)
|
||||||
|
{
|
||||||
|
switch(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)
|
||||||
|
{
|
||||||
|
switch(param)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
SetParamf(props, param, *vals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NullEffectHandler::GetParami(const std::monostate& /*props*/, ALenum param, int* /*val*/)
|
||||||
|
{
|
||||||
|
switch(param)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
|
||||||
|
param};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void NullEffectHandler::GetParamiv(const std::monostate &props, ALenum param, int *vals)
|
||||||
|
{
|
||||||
|
switch(param)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
GetParami(props, param, vals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void NullEffectHandler::GetParamf(const std::monostate& /*props*/, ALenum param, float* /*val*/)
|
||||||
|
{
|
||||||
|
switch(param)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
|
||||||
|
param};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void NullEffectHandler::GetParamfv(const std::monostate &props, ALenum param, float *vals)
|
||||||
|
{
|
||||||
|
switch(param)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
GetParamf(props, param, vals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -117,30 +115,26 @@ template<>
|
||||||
throw Exception{message};
|
throw Exception{message};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
bool EaxNullCommitter::commit(const std::monostate &props)
|
||||||
bool NullCommitter::commit(const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
const bool ret{props.mType != mEaxProps.mType};
|
const bool ret{std::holds_alternative<std::monostate>(mEaxProps)};
|
||||||
mEaxProps = props;
|
mEaxProps = props;
|
||||||
|
mAlProps = std::monostate{};
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxNullCommitter::SetDefaults(EaxEffectProps &props)
|
||||||
void NullCommitter::SetDefaults(EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
props = EaxEffectProps{};
|
props = std::monostate{};
|
||||||
props.mType = EaxEffectType::None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxNullCommitter::Get(const EaxCall &call, const std::monostate&)
|
||||||
void NullCommitter::Get(const EaxCall &call, const EaxEffectProps&)
|
|
||||||
{
|
{
|
||||||
if(call.get_property_id() != 0)
|
if(call.get_property_id() != 0)
|
||||||
fail_unknown_property_id();
|
fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxNullCommitter::Set(const EaxCall &call, std::monostate&)
|
||||||
void NullCommitter::Set(const EaxCall &call, EaxEffectProps&)
|
|
||||||
{
|
{
|
||||||
if(call.get_property_id() != 0)
|
if(call.get_property_id() != 0)
|
||||||
fail_unknown_property_id();
|
fail_unknown_property_id();
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.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"
|
||||||
#endif // ALSOFT_EAX
|
#endif // ALSOFT_EAX
|
||||||
|
|
@ -16,28 +17,32 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void Pshifter_setParamf(EffectProps*, ALenum param, float)
|
constexpr EffectProps genDefaultProps() noexcept
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
|
|
||||||
void Pshifter_setParamfv(EffectProps*, ALenum param, const float*)
|
|
||||||
{
|
{
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x",
|
PshifterProps props{};
|
||||||
param};
|
props.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE;
|
||||||
|
props.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE;
|
||||||
|
return props;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pshifter_setParami(EffectProps *props, ALenum param, int val)
|
} // namespace
|
||||||
|
|
||||||
|
const EffectProps PshifterEffectProps{genDefaultProps()};
|
||||||
|
|
||||||
|
void PshifterEffectHandler::SetParami(PshifterProps &props, ALenum param, int val)
|
||||||
{
|
{
|
||||||
switch(param)
|
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Pitch shifter coarse tune out of range"};
|
||||||
props->Pshifter.CoarseTune = val;
|
props.CoarseTune = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Pitch shifter fine tune out of range"};
|
||||||
props->Pshifter.FineTune = val;
|
props.FineTune = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -45,49 +50,40 @@ void Pshifter_setParami(EffectProps *props, ALenum param, int val)
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Pshifter_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
void PshifterEffectHandler::SetParamiv(PshifterProps &props, ALenum param, const int *vals)
|
||||||
{ Pshifter_setParami(props, param, vals[0]); }
|
{ SetParami(props, param, *vals); }
|
||||||
|
|
||||||
void Pshifter_getParami(const EffectProps *props, ALenum param, int *val)
|
void PshifterEffectHandler::SetParamf(PshifterProps&, ALenum param, float)
|
||||||
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
|
||||||
|
void PshifterEffectHandler::SetParamfv(PshifterProps&, ALenum param, const float*)
|
||||||
|
{
|
||||||
|
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x",
|
||||||
|
param};
|
||||||
|
}
|
||||||
|
|
||||||
|
void PshifterEffectHandler::GetParami(const PshifterProps &props, ALenum param, int *val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_PITCH_SHIFTER_COARSE_TUNE:
|
case AL_PITCH_SHIFTER_COARSE_TUNE: *val = props.CoarseTune; break;
|
||||||
*val = props->Pshifter.CoarseTune;
|
case AL_PITCH_SHIFTER_FINE_TUNE: *val = props.FineTune; break;
|
||||||
break;
|
|
||||||
case AL_PITCH_SHIFTER_FINE_TUNE:
|
|
||||||
*val = props->Pshifter.FineTune;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x",
|
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x",
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Pshifter_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
void PshifterEffectHandler::GetParamiv(const PshifterProps &props, ALenum param, int *vals)
|
||||||
{ Pshifter_getParami(props, param, vals); }
|
{ GetParami(props, param, vals); }
|
||||||
|
|
||||||
void Pshifter_getParamf(const EffectProps*, ALenum param, float*)
|
void PshifterEffectHandler::GetParamf(const PshifterProps&, ALenum param, float*)
|
||||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
|
{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
|
||||||
void Pshifter_getParamfv(const EffectProps*, ALenum param, float*)
|
void PshifterEffectHandler::GetParamfv(const PshifterProps&, ALenum param, float*)
|
||||||
{
|
{
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x",
|
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x",
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
|
|
||||||
EffectProps genDefaultProps() noexcept
|
|
||||||
{
|
|
||||||
EffectProps props{};
|
|
||||||
props.Pshifter.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE;
|
|
||||||
props.Pshifter.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE;
|
|
||||||
return props;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
DEFINE_ALEFFECT_VTABLE(Pshifter);
|
|
||||||
|
|
||||||
const EffectProps PshifterEffectProps{genDefaultProps()};
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -138,52 +134,48 @@ template<>
|
||||||
throw Exception{message};
|
throw Exception{message};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
bool EaxPitchShifterCommitter::commit(const EAXPITCHSHIFTERPROPERTIES &props)
|
||||||
bool PitchShifterCommitter::commit(const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
if(props.mType == mEaxProps.mType
|
if(auto *cur = std::get_if<EAXPITCHSHIFTERPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||||
&& mEaxProps.mPitchShifter.lCoarseTune == props.mPitchShifter.lCoarseTune
|
|
||||||
&& mEaxProps.mPitchShifter.lFineTune == props.mPitchShifter.lFineTune)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mEaxProps = props;
|
mEaxProps = props;
|
||||||
|
mAlProps = [&]{
|
||||||
mAlProps.Pshifter.CoarseTune = static_cast<int>(mEaxProps.mPitchShifter.lCoarseTune);
|
PshifterProps ret{};
|
||||||
mAlProps.Pshifter.FineTune = static_cast<int>(mEaxProps.mPitchShifter.lFineTune);
|
ret.CoarseTune = static_cast<int>(props.lCoarseTune);
|
||||||
|
ret.FineTune = static_cast<int>(props.lFineTune);
|
||||||
|
return ret;
|
||||||
|
}();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxPitchShifterCommitter::SetDefaults(EaxEffectProps &props)
|
||||||
void PitchShifterCommitter::SetDefaults(EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
props.mType = EaxEffectType::PitchShifter;
|
props = EAXPITCHSHIFTERPROPERTIES{EAXPITCHSHIFTER_DEFAULTCOARSETUNE,
|
||||||
props.mPitchShifter.lCoarseTune = EAXPITCHSHIFTER_DEFAULTCOARSETUNE;
|
EAXPITCHSHIFTER_DEFAULTFINETUNE};
|
||||||
props.mPitchShifter.lFineTune = EAXPITCHSHIFTER_DEFAULTFINETUNE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxPitchShifterCommitter::Get(const EaxCall &call, const EAXPITCHSHIFTERPROPERTIES &props)
|
||||||
void PitchShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case EAXPITCHSHIFTER_NONE: break;
|
case EAXPITCHSHIFTER_NONE: break;
|
||||||
case EAXPITCHSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props.mPitchShifter); break;
|
case EAXPITCHSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||||
case EAXPITCHSHIFTER_COARSETUNE: call.set_value<Exception>(props.mPitchShifter.lCoarseTune); break;
|
case EAXPITCHSHIFTER_COARSETUNE: call.set_value<Exception>(props.lCoarseTune); break;
|
||||||
case EAXPITCHSHIFTER_FINETUNE: call.set_value<Exception>(props.mPitchShifter.lFineTune); break;
|
case EAXPITCHSHIFTER_FINETUNE: call.set_value<Exception>(props.lFineTune); break;
|
||||||
default: fail_unknown_property_id();
|
default: fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxPitchShifterCommitter::Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props)
|
||||||
void PitchShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case EAXPITCHSHIFTER_NONE: break;
|
case EAXPITCHSHIFTER_NONE: break;
|
||||||
case EAXPITCHSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props.mPitchShifter); break;
|
case EAXPITCHSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||||
case EAXPITCHSHIFTER_COARSETUNE: defer<CoarseTuneValidator>(call, props.mPitchShifter.lCoarseTune); break;
|
case EAXPITCHSHIFTER_COARSETUNE: defer<CoarseTuneValidator>(call, props.lCoarseTune); break;
|
||||||
case EAXPITCHSHIFTER_FINETUNE: defer<FineTuneValidator>(call, props.mPitchShifter.lFineTune); break;
|
case EAXPITCHSHIFTER_FINETUNE: defer<FineTuneValidator>(call, props.lFineTune); break;
|
||||||
default: fail_unknown_property_id();
|
default: fail_unknown_property_id();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,18 +1,18 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
#include "alc/effects/base.h"
|
#include "core/effects/base.h"
|
||||||
#include "aloptional.h"
|
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include "alnumeric.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"
|
||||||
#endif // ALSOFT_EAX
|
#endif // ALSOFT_EAX
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
al::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val)
|
constexpr std::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val) noexcept
|
||||||
{
|
{
|
||||||
#define HANDLE_PHENOME(x) case AL_VOCAL_MORPHER_PHONEME_ ## x: \
|
#define HANDLE_PHENOME(x) case AL_VOCAL_MORPHER_PHONEME_ ## x: \
|
||||||
return VMorpherPhenome::x
|
return VMorpherPhenome::x
|
||||||
|
|
@ -57,10 +57,10 @@ al::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val)
|
||||||
HANDLE_PHENOME(V);
|
HANDLE_PHENOME(V);
|
||||||
HANDLE_PHENOME(Z);
|
HANDLE_PHENOME(Z);
|
||||||
}
|
}
|
||||||
return al::nullopt;
|
return std::nullopt;
|
||||||
#undef HANDLE_PHENOME
|
#undef HANDLE_PHENOME
|
||||||
}
|
}
|
||||||
ALenum EnumFromPhenome(VMorpherPhenome phenome)
|
constexpr ALenum EnumFromPhenome(VMorpherPhenome phenome)
|
||||||
{
|
{
|
||||||
#define HANDLE_PHENOME(x) case VMorpherPhenome::x: return AL_VOCAL_MORPHER_PHONEME_ ## x
|
#define HANDLE_PHENOME(x) case VMorpherPhenome::x: return AL_VOCAL_MORPHER_PHONEME_ ## x
|
||||||
switch(phenome)
|
switch(phenome)
|
||||||
|
|
@ -100,7 +100,7 @@ ALenum EnumFromPhenome(VMorpherPhenome phenome)
|
||||||
#undef HANDLE_PHENOME
|
#undef HANDLE_PHENOME
|
||||||
}
|
}
|
||||||
|
|
||||||
al::optional<VMorpherWaveform> WaveformFromEmum(ALenum value)
|
constexpr std::optional<VMorpherWaveform> WaveformFromEmum(ALenum value) noexcept
|
||||||
{
|
{
|
||||||
switch(value)
|
switch(value)
|
||||||
{
|
{
|
||||||
|
|
@ -108,9 +108,9 @@ al::optional<VMorpherWaveform> WaveformFromEmum(ALenum value)
|
||||||
case AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE: return VMorpherWaveform::Triangle;
|
case AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE: return VMorpherWaveform::Triangle;
|
||||||
case AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH: return VMorpherWaveform::Sawtooth;
|
case AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH: return VMorpherWaveform::Sawtooth;
|
||||||
}
|
}
|
||||||
return al::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
ALenum EnumFromWaveform(VMorpherWaveform type)
|
constexpr ALenum EnumFromWaveform(VMorpherWaveform type)
|
||||||
{
|
{
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
|
|
@ -122,13 +122,29 @@ ALenum EnumFromWaveform(VMorpherWaveform type)
|
||||||
std::to_string(static_cast<int>(type))};
|
std::to_string(static_cast<int>(type))};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
|
constexpr EffectProps genDefaultProps() noexcept
|
||||||
|
{
|
||||||
|
VmorpherProps props{};
|
||||||
|
props.Rate = AL_VOCAL_MORPHER_DEFAULT_RATE;
|
||||||
|
props.PhonemeA = PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEA).value();
|
||||||
|
props.PhonemeB = PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEB).value();
|
||||||
|
props.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING;
|
||||||
|
props.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING;
|
||||||
|
props.Waveform = WaveformFromEmum(AL_VOCAL_MORPHER_DEFAULT_WAVEFORM).value();
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
const EffectProps VmorpherEffectProps{genDefaultProps()};
|
||||||
|
|
||||||
|
void VmorpherEffectHandler::SetParami(VmorpherProps &props, ALenum param, int val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_VOCAL_MORPHER_PHONEMEA:
|
case AL_VOCAL_MORPHER_PHONEMEA:
|
||||||
if(auto phenomeopt = PhenomeFromEnum(val))
|
if(auto phenomeopt = PhenomeFromEnum(val))
|
||||||
props->Vmorpher.PhonemeA = *phenomeopt;
|
props.PhonemeA = *phenomeopt;
|
||||||
else
|
else
|
||||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a out of range: 0x%04x", val};
|
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a out of range: 0x%04x", val};
|
||||||
break;
|
break;
|
||||||
|
|
@ -136,12 +152,12 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a coarse tuning out of range"};
|
||||||
props->Vmorpher.PhonemeACoarseTuning = val;
|
props.PhonemeACoarseTuning = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AL_VOCAL_MORPHER_PHONEMEB:
|
case AL_VOCAL_MORPHER_PHONEMEB:
|
||||||
if(auto phenomeopt = PhenomeFromEnum(val))
|
if(auto phenomeopt = PhenomeFromEnum(val))
|
||||||
props->Vmorpher.PhonemeB = *phenomeopt;
|
props.PhonemeB = *phenomeopt;
|
||||||
else
|
else
|
||||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b out of range: 0x%04x", val};
|
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b out of range: 0x%04x", val};
|
||||||
break;
|
break;
|
||||||
|
|
@ -149,12 +165,12 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
|
||||||
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b coarse tuning out of range"};
|
||||||
props->Vmorpher.PhonemeBCoarseTuning = val;
|
props.PhonemeBCoarseTuning = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AL_VOCAL_MORPHER_WAVEFORM:
|
case AL_VOCAL_MORPHER_WAVEFORM:
|
||||||
if(auto formopt = WaveformFromEmum(val))
|
if(auto formopt = WaveformFromEmum(val))
|
||||||
props->Vmorpher.Waveform = *formopt;
|
props.Waveform = *formopt;
|
||||||
else
|
else
|
||||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher waveform out of range: 0x%04x", val};
|
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher waveform out of range: 0x%04x", val};
|
||||||
break;
|
break;
|
||||||
|
|
@ -164,19 +180,19 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Vmorpher_setParamiv(EffectProps*, ALenum param, const int*)
|
void VmorpherEffectHandler::SetParamiv(VmorpherProps&, ALenum param, const int*)
|
||||||
{
|
{
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x",
|
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x",
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
void Vmorpher_setParamf(EffectProps *props, ALenum param, float val)
|
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"};
|
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher rate out of range"};
|
||||||
props->Vmorpher.Rate = val;
|
props.Rate = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -184,49 +200,35 @@ void Vmorpher_setParamf(EffectProps *props, ALenum param, float val)
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Vmorpher_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
void VmorpherEffectHandler::SetParamfv(VmorpherProps &props, ALenum param, const float *vals)
|
||||||
{ Vmorpher_setParamf(props, param, vals[0]); }
|
{ SetParamf(props, param, *vals); }
|
||||||
|
|
||||||
void Vmorpher_getParami(const EffectProps *props, ALenum param, int* val)
|
void VmorpherEffectHandler::GetParami(const VmorpherProps &props, ALenum param, int* val)
|
||||||
{
|
{
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_VOCAL_MORPHER_PHONEMEA:
|
case AL_VOCAL_MORPHER_PHONEMEA: *val = EnumFromPhenome(props.PhonemeA); break;
|
||||||
*val = EnumFromPhenome(props->Vmorpher.PhonemeA);
|
case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: *val = props.PhonemeACoarseTuning; break;
|
||||||
break;
|
case AL_VOCAL_MORPHER_PHONEMEB: *val = EnumFromPhenome(props.PhonemeB); break;
|
||||||
|
case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: *val = props.PhonemeBCoarseTuning; break;
|
||||||
case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING:
|
case AL_VOCAL_MORPHER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
|
||||||
*val = props->Vmorpher.PhonemeACoarseTuning;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_VOCAL_MORPHER_PHONEMEB:
|
|
||||||
*val = EnumFromPhenome(props->Vmorpher.PhonemeB);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING:
|
|
||||||
*val = props->Vmorpher.PhonemeBCoarseTuning;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_VOCAL_MORPHER_WAVEFORM:
|
|
||||||
*val = EnumFromWaveform(props->Vmorpher.Waveform);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x",
|
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x",
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Vmorpher_getParamiv(const EffectProps*, ALenum param, int*)
|
void VmorpherEffectHandler::GetParamiv(const VmorpherProps&, ALenum param, int*)
|
||||||
{
|
{
|
||||||
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x",
|
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x",
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
void Vmorpher_getParamf(const EffectProps *props, ALenum param, float *val)
|
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->Vmorpher.Rate;
|
*val = props.Rate;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -234,26 +236,9 @@ void Vmorpher_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||||
param};
|
param};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Vmorpher_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
void VmorpherEffectHandler::GetParamfv(const VmorpherProps &props, ALenum param, float *vals)
|
||||||
{ Vmorpher_getParamf(props, param, vals); }
|
{ GetParamf(props, param, vals); }
|
||||||
|
|
||||||
EffectProps genDefaultProps() noexcept
|
|
||||||
{
|
|
||||||
EffectProps props{};
|
|
||||||
props.Vmorpher.Rate = AL_VOCAL_MORPHER_DEFAULT_RATE;
|
|
||||||
props.Vmorpher.PhonemeA = *PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEA);
|
|
||||||
props.Vmorpher.PhonemeB = *PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEB);
|
|
||||||
props.Vmorpher.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING;
|
|
||||||
props.Vmorpher.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING;
|
|
||||||
props.Vmorpher.Waveform = *WaveformFromEmum(AL_VOCAL_MORPHER_DEFAULT_WAVEFORM);
|
|
||||||
return props;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
DEFINE_ALEFFECT_VTABLE(Vmorpher);
|
|
||||||
|
|
||||||
const EffectProps VmorpherEffectProps{genDefaultProps()};
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -352,16 +337,9 @@ template<>
|
||||||
throw Exception{message};
|
throw Exception{message};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
bool EaxVocalMorpherCommitter::commit(const EAXVOCALMORPHERPROPERTIES &props)
|
||||||
bool VocalMorpherCommitter::commit(const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
if(props.mType == mEaxProps.mType
|
if(auto *cur = std::get_if<EAXVOCALMORPHERPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||||
&& mEaxProps.mVocalMorpher.ulPhonemeA == props.mVocalMorpher.ulPhonemeA
|
|
||||||
&& mEaxProps.mVocalMorpher.lPhonemeACoarseTuning == props.mVocalMorpher.lPhonemeACoarseTuning
|
|
||||||
&& mEaxProps.mVocalMorpher.ulPhonemeB == props.mVocalMorpher.ulPhonemeB
|
|
||||||
&& mEaxProps.mVocalMorpher.lPhonemeBCoarseTuning == props.mVocalMorpher.lPhonemeBCoarseTuning
|
|
||||||
&& mEaxProps.mVocalMorpher.ulWaveform == props.mVocalMorpher.ulWaveform
|
|
||||||
&& mEaxProps.mVocalMorpher.flRate == props.mVocalMorpher.flRate)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mEaxProps = props;
|
mEaxProps = props;
|
||||||
|
|
@ -413,107 +391,65 @@ bool VocalMorpherCommitter::commit(const EaxEffectProps &props)
|
||||||
return VMorpherWaveform::Sinusoid;
|
return VMorpherWaveform::Sinusoid;
|
||||||
};
|
};
|
||||||
|
|
||||||
mAlProps.Vmorpher.PhonemeA = get_phoneme(props.mVocalMorpher.ulPhonemeA);
|
mAlProps = [&]{
|
||||||
mAlProps.Vmorpher.PhonemeACoarseTuning = static_cast<int>(props.mVocalMorpher.lPhonemeACoarseTuning);
|
VmorpherProps ret{};
|
||||||
mAlProps.Vmorpher.PhonemeB = get_phoneme(props.mVocalMorpher.ulPhonemeB);
|
ret.PhonemeA = get_phoneme(props.ulPhonemeA);
|
||||||
mAlProps.Vmorpher.PhonemeBCoarseTuning = static_cast<int>(props.mVocalMorpher.lPhonemeBCoarseTuning);
|
ret.PhonemeACoarseTuning = static_cast<int>(props.lPhonemeACoarseTuning);
|
||||||
mAlProps.Vmorpher.Waveform = get_waveform(props.mVocalMorpher.ulWaveform);
|
ret.PhonemeB = get_phoneme(props.ulPhonemeB);
|
||||||
mAlProps.Vmorpher.Rate = props.mVocalMorpher.flRate;
|
ret.PhonemeBCoarseTuning = static_cast<int>(props.lPhonemeBCoarseTuning);
|
||||||
|
ret.Waveform = get_waveform(props.ulWaveform);
|
||||||
|
ret.Rate = props.flRate;
|
||||||
|
return ret;
|
||||||
|
}();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxVocalMorpherCommitter::SetDefaults(EaxEffectProps &props)
|
||||||
void VocalMorpherCommitter::SetDefaults(EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
props.mType = EaxEffectType::VocalMorpher;
|
static constexpr EAXVOCALMORPHERPROPERTIES defprops{[]
|
||||||
props.mVocalMorpher.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA;
|
{
|
||||||
props.mVocalMorpher.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING;
|
EAXVOCALMORPHERPROPERTIES ret{};
|
||||||
props.mVocalMorpher.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB;
|
ret.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA;
|
||||||
props.mVocalMorpher.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING;
|
ret.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING;
|
||||||
props.mVocalMorpher.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM;
|
ret.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB;
|
||||||
props.mVocalMorpher.flRate = EAXVOCALMORPHER_DEFAULTRATE;
|
ret.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING;
|
||||||
|
ret.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM;
|
||||||
|
ret.flRate = EAXVOCALMORPHER_DEFAULTRATE;
|
||||||
|
return ret;
|
||||||
|
}()};
|
||||||
|
props = defprops;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxVocalMorpherCommitter::Get(const EaxCall &call, const EAXVOCALMORPHERPROPERTIES &props)
|
||||||
void VocalMorpherCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case EAXVOCALMORPHER_NONE:
|
case EAXVOCALMORPHER_NONE: break;
|
||||||
break;
|
case EAXVOCALMORPHER_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||||
|
case EAXVOCALMORPHER_PHONEMEA: call.set_value<Exception>(props.ulPhonemeA); break;
|
||||||
case EAXVOCALMORPHER_ALLPARAMETERS:
|
case EAXVOCALMORPHER_PHONEMEACOARSETUNING: call.set_value<Exception>(props.lPhonemeACoarseTuning); break;
|
||||||
call.set_value<Exception>(props.mVocalMorpher);
|
case EAXVOCALMORPHER_PHONEMEB: call.set_value<Exception>(props.ulPhonemeB); break;
|
||||||
break;
|
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: call.set_value<Exception>(props.lPhonemeBCoarseTuning); break;
|
||||||
|
case EAXVOCALMORPHER_WAVEFORM: call.set_value<Exception>(props.ulWaveform); break;
|
||||||
case EAXVOCALMORPHER_PHONEMEA:
|
case EAXVOCALMORPHER_RATE: call.set_value<Exception>(props.flRate); break;
|
||||||
call.set_value<Exception>(props.mVocalMorpher.ulPhonemeA);
|
default: fail_unknown_property_id();
|
||||||
break;
|
|
||||||
|
|
||||||
case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
|
|
||||||
call.set_value<Exception>(props.mVocalMorpher.lPhonemeACoarseTuning);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EAXVOCALMORPHER_PHONEMEB:
|
|
||||||
call.set_value<Exception>(props.mVocalMorpher.ulPhonemeB);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
|
|
||||||
call.set_value<Exception>(props.mVocalMorpher.lPhonemeBCoarseTuning);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EAXVOCALMORPHER_WAVEFORM:
|
|
||||||
call.set_value<Exception>(props.mVocalMorpher.ulWaveform);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EAXVOCALMORPHER_RATE:
|
|
||||||
call.set_value<Exception>(props.mVocalMorpher.flRate);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
fail_unknown_property_id();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
void EaxVocalMorpherCommitter::Set(const EaxCall &call, EAXVOCALMORPHERPROPERTIES &props)
|
||||||
void VocalMorpherCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
|
||||||
{
|
{
|
||||||
switch(call.get_property_id())
|
switch(call.get_property_id())
|
||||||
{
|
{
|
||||||
case EAXVOCALMORPHER_NONE:
|
case EAXVOCALMORPHER_NONE: break;
|
||||||
break;
|
case EAXVOCALMORPHER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||||
|
case EAXVOCALMORPHER_PHONEMEA: defer<PhonemeAValidator>(call, props.ulPhonemeA); break;
|
||||||
case EAXVOCALMORPHER_ALLPARAMETERS:
|
case EAXVOCALMORPHER_PHONEMEACOARSETUNING: defer<PhonemeACoarseTuningValidator>(call, props.lPhonemeACoarseTuning); break;
|
||||||
defer<AllValidator>(call, props.mVocalMorpher);
|
case EAXVOCALMORPHER_PHONEMEB: defer<PhonemeBValidator>(call, props.ulPhonemeB); break;
|
||||||
break;
|
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: defer<PhonemeBCoarseTuningValidator>(call, props.lPhonemeBCoarseTuning); break;
|
||||||
|
case EAXVOCALMORPHER_WAVEFORM: defer<WaveformValidator>(call, props.ulWaveform); break;
|
||||||
case EAXVOCALMORPHER_PHONEMEA:
|
case EAXVOCALMORPHER_RATE: defer<RateValidator>(call, props.flRate); break;
|
||||||
defer<PhonemeAValidator>(call, props.mVocalMorpher.ulPhonemeA);
|
default: fail_unknown_property_id();
|
||||||
break;
|
|
||||||
|
|
||||||
case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
|
|
||||||
defer<PhonemeACoarseTuningValidator>(call, props.mVocalMorpher.lPhonemeACoarseTuning);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EAXVOCALMORPHER_PHONEMEB:
|
|
||||||
defer<PhonemeBValidator>(call, props.mVocalMorpher.ulPhonemeB);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
|
|
||||||
defer<PhonemeBCoarseTuningValidator>(call, props.mVocalMorpher.lPhonemeBCoarseTuning);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EAXVOCALMORPHER_WAVEFORM:
|
|
||||||
defer<WaveformValidator>(call, props.mVocalMorpher.ulWaveform);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EAXVOCALMORPHER_RATE:
|
|
||||||
defer<RateValidator>(call, props.mVocalMorpher.flRate);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
fail_unknown_property_id();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@
|
||||||
|
|
||||||
#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>
|
||||||
|
|
@ -29,27 +31,45 @@
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <mutex>
|
#include <limits>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/alc.h"
|
#include "AL/alc.h"
|
||||||
|
|
||||||
|
#include "al/debug.h"
|
||||||
|
#include "alc/alconfig.h"
|
||||||
#include "alc/context.h"
|
#include "alc/context.h"
|
||||||
#include "almalloc.h"
|
#include "alc/inprogext.h"
|
||||||
#include "core/except.h"
|
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
#include "vector.h"
|
#include "strutils.h"
|
||||||
|
|
||||||
|
|
||||||
bool TrapALError{false};
|
namespace al {
|
||||||
|
context_error::context_error(ALenum code, const char *msg, ...) : mErrorCode{code}
|
||||||
|
{
|
||||||
|
/* NOLINTBEGIN(*-array-to-pointer-decay) */
|
||||||
|
std::va_list args;
|
||||||
|
va_start(args, msg);
|
||||||
|
setMessage(msg, args);
|
||||||
|
va_end(args);
|
||||||
|
/* NOLINTEND(*-array-to-pointer-decay) */
|
||||||
|
}
|
||||||
|
context_error::~context_error() = default;
|
||||||
|
} /* namespace al */
|
||||||
|
|
||||||
void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
|
void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
|
||||||
{
|
{
|
||||||
auto message = al::vector<char>(256);
|
auto message = std::vector<char>(256);
|
||||||
|
|
||||||
va_list args, args2;
|
/* NOLINTBEGIN(*-array-to-pointer-decay) */
|
||||||
|
std::va_list args, args2;
|
||||||
va_start(args, msg);
|
va_start(args, msg);
|
||||||
va_copy(args2, args);
|
va_copy(args2, args);
|
||||||
int msglen{std::vsnprintf(message.data(), message.size(), msg, args)};
|
int msglen{std::vsnprintf(message.data(), message.size(), msg, args)};
|
||||||
|
|
@ -60,9 +80,15 @@ void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
|
||||||
}
|
}
|
||||||
va_end(args2);
|
va_end(args2);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
/* NOLINTEND(*-array-to-pointer-decay) */
|
||||||
|
|
||||||
if(msglen >= 0) msg = message.data();
|
if(msglen >= 0)
|
||||||
else msg = "<internal error constructing message>";
|
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",
|
WARN("Error generated on context %p, code 0x%04x, \"%s\"\n",
|
||||||
decltype(std::declval<void*>()){this}, errorCode, msg);
|
decltype(std::declval<void*>()){this}, errorCode, msg);
|
||||||
|
|
@ -77,30 +103,55 @@ void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
ALenum curerr{AL_NO_ERROR};
|
if(mLastThreadError.get() == AL_NO_ERROR)
|
||||||
mLastError.compare_exchange_strong(curerr, errorCode);
|
mLastThreadError.set(errorCode);
|
||||||
|
|
||||||
|
debugMessage(DebugSource::API, DebugType::Error, 0, DebugSeverity::High,
|
||||||
|
{msg, static_cast<uint>(msglen)});
|
||||||
}
|
}
|
||||||
|
|
||||||
AL_API ALenum AL_APIENTRY alGetError(void)
|
/* Special-case alGetError since it (potentially) raises a debug signal and
|
||||||
START_API_FUNC
|
* returns a non-default value for a null context.
|
||||||
|
*/
|
||||||
|
AL_API auto AL_APIENTRY alGetError() noexcept -> ALenum
|
||||||
{
|
{
|
||||||
ContextRef context{GetContextRef()};
|
if(auto context = GetContextRef()) LIKELY
|
||||||
if(!context) UNLIKELY
|
return alGetErrorDirect(context.get());
|
||||||
{
|
|
||||||
static constexpr ALenum deferror{AL_INVALID_OPERATION};
|
|
||||||
WARN("Querying error state on null context (implicitly 0x%04x)\n", deferror);
|
|
||||||
if(TrapALError)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
if(IsDebuggerPresent())
|
|
||||||
DebugBreak();
|
|
||||||
#elif defined(SIGTRAP)
|
|
||||||
raise(SIGTRAP);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return deferror;
|
|
||||||
}
|
|
||||||
|
|
||||||
return context->mLastError.exchange(AL_NO_ERROR);
|
auto get_value = [](const char *envname, const char *optname) -> ALenum
|
||||||
|
{
|
||||||
|
auto optstr = al::getenv(envname);
|
||||||
|
if(!optstr)
|
||||||
|
optstr = ConfigValueStr({}, "game_compat", optname);
|
||||||
|
if(optstr)
|
||||||
|
{
|
||||||
|
char *end{};
|
||||||
|
auto value = std::strtoul(optstr->c_str(), &end, 0);
|
||||||
|
if(end && *end == '\0' && value <= std::numeric_limits<ALenum>::max())
|
||||||
|
return static_cast<ALenum>(value);
|
||||||
|
ERR("Invalid default error value: \"%s\"", optstr->c_str());
|
||||||
|
}
|
||||||
|
return AL_INVALID_OPERATION;
|
||||||
|
};
|
||||||
|
static const ALenum deferror{get_value("__ALSOFT_DEFAULT_ERROR", "default-error")};
|
||||||
|
|
||||||
|
WARN("Querying error state on null context (implicitly 0x%04x)\n", deferror);
|
||||||
|
if(TrapALError)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
if(IsDebuggerPresent())
|
||||||
|
DebugBreak();
|
||||||
|
#elif defined(SIGTRAP)
|
||||||
|
raise(SIGTRAP);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return deferror;
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCE_ALIGN ALenum AL_APIENTRY alGetErrorDirect(ALCcontext *context) noexcept
|
||||||
|
{
|
||||||
|
ALenum ret{context->mLastThreadError.get()};
|
||||||
|
if(ret != AL_NO_ERROR) UNLIKELY
|
||||||
|
context->mLastThreadError.set(AL_NO_ERROR);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
|
||||||
27
Engine/lib/openal-soft/al/error.h
Normal file
27
Engine/lib/openal-soft/al/error.h
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef AL_ERROR_H
|
||||||
|
#define AL_ERROR_H
|
||||||
|
|
||||||
|
#include "AL/al.h"
|
||||||
|
|
||||||
|
#include "core/except.h"
|
||||||
|
|
||||||
|
namespace al {
|
||||||
|
|
||||||
|
class context_error final : public al::base_exception {
|
||||||
|
ALenum mErrorCode{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
[[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]]
|
||||||
|
#else
|
||||||
|
[[gnu::format(printf, 3, 4)]]
|
||||||
|
#endif
|
||||||
|
context_error(ALenum code, const char *msg, ...);
|
||||||
|
~context_error() final;
|
||||||
|
|
||||||
|
[[nodiscard]] auto errorCode() const noexcept -> ALenum { return mErrorCode; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace al */
|
||||||
|
|
||||||
|
#endif /* AL_ERROR_H */
|
||||||
|
|
@ -3,35 +3,49 @@
|
||||||
|
|
||||||
#include "event.h"
|
#include "event.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstring>
|
#include <bitset>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <new>
|
#include <new>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/alc.h"
|
#include "AL/alc.h"
|
||||||
|
#include "AL/alext.h"
|
||||||
|
|
||||||
#include "albyte.h"
|
|
||||||
#include "alc/context.h"
|
#include "alc/context.h"
|
||||||
#include "alc/effects/base.h"
|
#include "alsem.h"
|
||||||
#include "alc/inprogext.h"
|
#include "alspan.h"
|
||||||
#include "almalloc.h"
|
|
||||||
#include "core/async_event.h"
|
#include "core/async_event.h"
|
||||||
#include "core/except.h"
|
#include "core/context.h"
|
||||||
|
#include "core/effects/base.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "core/voice_change.h"
|
#include "debug.h"
|
||||||
|
#include "direct_defs.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "intrusive_ptr.h"
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
#include "threads.h"
|
|
||||||
|
|
||||||
|
|
||||||
static int EventThread(ALCcontext *context)
|
namespace {
|
||||||
|
|
||||||
|
template<typename... Ts>
|
||||||
|
struct overloaded : Ts... { using Ts::operator()...; };
|
||||||
|
|
||||||
|
template<typename... Ts>
|
||||||
|
overloaded(Ts...) -> overloaded<Ts...>;
|
||||||
|
|
||||||
|
int EventThread(ALCcontext *context)
|
||||||
{
|
{
|
||||||
RingBuffer *ring{context->mAsyncEvents.get()};
|
RingBuffer *ring{context->mAsyncEvents.get()};
|
||||||
bool quitnow{false};
|
bool quitnow{false};
|
||||||
|
|
@ -44,76 +58,98 @@ static int EventThread(ALCcontext *context)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> _{context->mEventCbLock};
|
std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
|
||||||
do {
|
auto evt_span = al::span{std::launder(reinterpret_cast<AsyncEvent*>(evt_data.buf)),
|
||||||
auto *evt_ptr = reinterpret_cast<AsyncEvent*>(evt_data.buf);
|
evt_data.len};
|
||||||
evt_data.buf += sizeof(AsyncEvent);
|
for(auto &event : evt_span)
|
||||||
evt_data.len -= 1;
|
{
|
||||||
|
quitnow = std::holds_alternative<AsyncKillThread>(event);
|
||||||
AsyncEvent evt{*evt_ptr};
|
|
||||||
al::destroy_at(evt_ptr);
|
|
||||||
ring->readAdvance(1);
|
|
||||||
|
|
||||||
quitnow = evt.EnumType == AsyncEvent::KillThread;
|
|
||||||
if(quitnow) UNLIKELY break;
|
if(quitnow) UNLIKELY break;
|
||||||
|
|
||||||
if(evt.EnumType == AsyncEvent::ReleaseEffectState)
|
|
||||||
{
|
|
||||||
al::intrusive_ptr<EffectState>{evt.u.mEffectState};
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto enabledevts = context->mEnabledEvts.load(std::memory_order_acquire);
|
auto enabledevts = context->mEnabledEvts.load(std::memory_order_acquire);
|
||||||
if(!context->mEventCb || !enabledevts.test(evt.EnumType))
|
auto proc_killthread = [](AsyncKillThread&) { };
|
||||||
continue;
|
auto proc_release = [](AsyncEffectReleaseEvent &evt)
|
||||||
|
|
||||||
if(evt.EnumType == AsyncEvent::SourceStateChange)
|
|
||||||
{
|
{
|
||||||
|
al::intrusive_ptr<EffectState>{evt.mEffectState};
|
||||||
|
};
|
||||||
|
auto proc_srcstate = [context,enabledevts](AsyncSourceStateEvent &evt)
|
||||||
|
{
|
||||||
|
if(!context->mEventCb
|
||||||
|
|| !enabledevts.test(al::to_underlying(AsyncEnableBits::SourceState)))
|
||||||
|
return;
|
||||||
|
|
||||||
ALuint state{};
|
ALuint state{};
|
||||||
std::string msg{"Source ID " + std::to_string(evt.u.srcstate.id)};
|
std::string msg{"Source ID " + std::to_string(evt.mId)};
|
||||||
msg += " state has changed to ";
|
msg += " state has changed to ";
|
||||||
switch(evt.u.srcstate.state)
|
switch(evt.mState)
|
||||||
{
|
{
|
||||||
case AsyncEvent::SrcState::Reset:
|
case AsyncSrcState::Reset:
|
||||||
msg += "AL_INITIAL";
|
msg += "AL_INITIAL";
|
||||||
state = AL_INITIAL;
|
state = AL_INITIAL;
|
||||||
break;
|
break;
|
||||||
case AsyncEvent::SrcState::Stop:
|
case AsyncSrcState::Stop:
|
||||||
msg += "AL_STOPPED";
|
msg += "AL_STOPPED";
|
||||||
state = AL_STOPPED;
|
state = AL_STOPPED;
|
||||||
break;
|
break;
|
||||||
case AsyncEvent::SrcState::Play:
|
case AsyncSrcState::Play:
|
||||||
msg += "AL_PLAYING";
|
msg += "AL_PLAYING";
|
||||||
state = AL_PLAYING;
|
state = AL_PLAYING;
|
||||||
break;
|
break;
|
||||||
case AsyncEvent::SrcState::Pause:
|
case AsyncSrcState::Pause:
|
||||||
msg += "AL_PAUSED";
|
msg += "AL_PAUSED";
|
||||||
state = AL_PAUSED;
|
state = AL_PAUSED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.u.srcstate.id,
|
context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.mId, state,
|
||||||
state, static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
|
static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
|
||||||
}
|
};
|
||||||
else if(evt.EnumType == AsyncEvent::BufferCompleted)
|
auto proc_buffercomp = [context,enabledevts](AsyncBufferCompleteEvent &evt)
|
||||||
{
|
{
|
||||||
std::string msg{std::to_string(evt.u.bufcomp.count)};
|
if(!context->mEventCb
|
||||||
if(evt.u.bufcomp.count == 1) msg += " buffer completed";
|
|| !enabledevts.test(al::to_underlying(AsyncEnableBits::BufferCompleted)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string msg{std::to_string(evt.mCount)};
|
||||||
|
if(evt.mCount == 1) msg += " buffer completed";
|
||||||
else msg += " buffers completed";
|
else msg += " buffers completed";
|
||||||
context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.u.bufcomp.id,
|
context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.mId, evt.mCount,
|
||||||
evt.u.bufcomp.count, static_cast<ALsizei>(msg.length()), msg.c_str(),
|
static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
|
||||||
context->mEventParam);
|
};
|
||||||
}
|
auto proc_disconnect = [context,enabledevts](AsyncDisconnectEvent &evt)
|
||||||
else if(evt.EnumType == AsyncEvent::Disconnected)
|
|
||||||
{
|
{
|
||||||
context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0,
|
context->debugMessage(DebugSource::System, DebugType::Error, 0,
|
||||||
static_cast<ALsizei>(strlen(evt.u.disconnect.msg)), evt.u.disconnect.msg,
|
DebugSeverity::High, evt.msg);
|
||||||
context->mEventParam);
|
|
||||||
}
|
if(context->mEventCb
|
||||||
} while(evt_data.len != 0);
|
&& enabledevts.test(al::to_underlying(AsyncEnableBits::Disconnected)))
|
||||||
|
context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0,
|
||||||
|
static_cast<ALsizei>(evt.msg.length()), evt.msg.c_str(),
|
||||||
|
context->mEventParam);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::visit(overloaded{proc_srcstate, proc_buffercomp, proc_release, proc_disconnect,
|
||||||
|
proc_killthread}, event);
|
||||||
|
}
|
||||||
|
std::destroy(evt_span.begin(), evt_span.end());
|
||||||
|
ring->readAdvance(evt_span.size());
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr std::optional<AsyncEnableBits> GetEventType(ALenum etype) noexcept
|
||||||
|
{
|
||||||
|
switch(etype)
|
||||||
|
{
|
||||||
|
case AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT: return AsyncEnableBits::BufferCompleted;
|
||||||
|
case AL_EVENT_TYPE_DISCONNECTED_SOFT: return AsyncEnableBits::Disconnected;
|
||||||
|
case AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT: return AsyncEnableBits::SourceState;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
void StartEventThrd(ALCcontext *ctx)
|
void StartEventThrd(ALCcontext *ctx)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|
@ -138,7 +174,7 @@ void StopEventThrd(ALCcontext *ctx)
|
||||||
evt_data = ring->getWriteVector().first;
|
evt_data = ring->getWriteVector().first;
|
||||||
} while(evt_data.len == 0);
|
} while(evt_data.len == 0);
|
||||||
}
|
}
|
||||||
al::construct_at(reinterpret_cast<AsyncEvent*>(evt_data.buf), AsyncEvent::KillThread);
|
std::ignore = InitAsyncEvent<AsyncKillThread>(evt_data.buf);
|
||||||
ring->writeAdvance(1);
|
ring->writeAdvance(1);
|
||||||
|
|
||||||
ctx->mEventSem.post();
|
ctx->mEventSem.post();
|
||||||
|
|
@ -146,34 +182,25 @@ void StopEventThrd(ALCcontext *ctx)
|
||||||
ctx->mEventThread.join();
|
ctx->mEventThread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable)
|
AL_API DECL_FUNCEXT3(void, alEventControl,SOFT, ALsizei,count, const ALenum*,types, ALboolean,enable)
|
||||||
START_API_FUNC
|
FORCE_ALIGN void AL_APIENTRY alEventControlDirectSOFT(ALCcontext *context, ALsizei count,
|
||||||
{
|
const ALenum *types, ALboolean enable) noexcept
|
||||||
ContextRef context{GetContextRef()};
|
try {
|
||||||
if(!context) UNLIKELY return;
|
if(count < 0)
|
||||||
|
throw al::context_error{AL_INVALID_VALUE, "Controlling %d events", count};
|
||||||
|
if(count <= 0) UNLIKELY return;
|
||||||
|
|
||||||
if(count < 0) context->setError(AL_INVALID_VALUE, "Controlling %d events", count);
|
if(!types)
|
||||||
if(count <= 0) return;
|
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||||
if(!types) return context->setError(AL_INVALID_VALUE, "NULL pointer");
|
|
||||||
|
|
||||||
ContextBase::AsyncEventBitset flags{};
|
ContextBase::AsyncEventBitset flags{};
|
||||||
const ALenum *types_end = types+count;
|
for(ALenum evttype : al::span{types, static_cast<uint>(count)})
|
||||||
auto bad_type = std::find_if_not(types, types_end,
|
{
|
||||||
[&flags](ALenum type) noexcept -> bool
|
auto etype = GetEventType(evttype);
|
||||||
{
|
if(!etype)
|
||||||
if(type == AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT)
|
throw al::context_error{AL_INVALID_ENUM, "Invalid event type 0x%04x", evttype};
|
||||||
flags.set(AsyncEvent::BufferCompleted);
|
flags.set(al::to_underlying(*etype));
|
||||||
else if(type == AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT)
|
}
|
||||||
flags.set(AsyncEvent::SourceStateChange);
|
|
||||||
else if(type == AL_EVENT_TYPE_DISCONNECTED_SOFT)
|
|
||||||
flags.set(AsyncEvent::Disconnected);
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if(bad_type != types_end)
|
|
||||||
return context->setError(AL_INVALID_ENUM, "Invalid event type 0x%04x", *bad_type);
|
|
||||||
|
|
||||||
if(enable)
|
if(enable)
|
||||||
{
|
{
|
||||||
|
|
@ -196,20 +223,18 @@ START_API_FUNC
|
||||||
/* Wait to ensure the event handler sees the changed flags before
|
/* Wait to ensure the event handler sees the changed flags before
|
||||||
* returning.
|
* returning.
|
||||||
*/
|
*/
|
||||||
std::lock_guard<std::mutex> _{context->mEventCbLock};
|
std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam)
|
AL_API DECL_FUNCEXT2(void, alEventCallback,SOFT, ALEVENTPROCSOFT,callback, void*,userParam)
|
||||||
START_API_FUNC
|
FORCE_ALIGN void AL_APIENTRY alEventCallbackDirectSOFT(ALCcontext *context,
|
||||||
|
ALEVENTPROCSOFT callback, void *userParam) noexcept
|
||||||
{
|
{
|
||||||
ContextRef context{GetContextRef()};
|
std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
|
||||||
if(!context) UNLIKELY return;
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
|
||||||
std::lock_guard<std::mutex> __{context->mEventCbLock};
|
|
||||||
context->mEventCb = callback;
|
context->mEventCb = callback;
|
||||||
context->mEventParam = userParam;
|
context->mEventParam = userParam;
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
|
||||||
|
|
@ -20,63 +20,59 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <cctype>
|
#include <string_view>
|
||||||
#include <cstdlib>
|
#include <vector>
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#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 "core/except.h"
|
#include "direct_defs.h"
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
|
|
||||||
|
|
||||||
AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extName)
|
AL_API DECL_FUNC1(ALboolean, alIsExtensionPresent, const ALchar*,extName)
|
||||||
START_API_FUNC
|
FORCE_ALIGN ALboolean AL_APIENTRY alIsExtensionPresentDirect(ALCcontext *context, const ALchar *extName) noexcept
|
||||||
{
|
{
|
||||||
ContextRef context{GetContextRef()};
|
|
||||||
if(!context) UNLIKELY return AL_FALSE;
|
|
||||||
|
|
||||||
if(!extName) UNLIKELY
|
if(!extName) UNLIKELY
|
||||||
{
|
{
|
||||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||||
return AL_FALSE;
|
return AL_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t len{strlen(extName)};
|
const std::string_view tofind{extName};
|
||||||
const char *ptr{context->mExtensionList};
|
for(std::string_view ext : context->mExtensions)
|
||||||
while(ptr && *ptr)
|
|
||||||
{
|
{
|
||||||
if(al::strncasecmp(ptr, extName, len) == 0 && (ptr[len] == '\0' || isspace(ptr[len])))
|
if(al::case_compare(ext, tofind) == 0)
|
||||||
return AL_TRUE;
|
return AL_TRUE;
|
||||||
|
|
||||||
if((ptr=strchr(ptr, ' ')) != nullptr)
|
|
||||||
{
|
|
||||||
do {
|
|
||||||
++ptr;
|
|
||||||
} while(isspace(*ptr));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return AL_FALSE;
|
return AL_FALSE;
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
|
|
||||||
AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName)
|
AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName) noexcept
|
||||||
START_API_FUNC
|
|
||||||
{
|
{
|
||||||
if(!funcName) return nullptr;
|
if(!funcName) return nullptr;
|
||||||
return alcGetProcAddress(nullptr, funcName);
|
return alcGetProcAddress(nullptr, funcName);
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName)
|
FORCE_ALIGN ALvoid* AL_APIENTRY alGetProcAddressDirect(ALCcontext*, const ALchar *funcName) noexcept
|
||||||
START_API_FUNC
|
|
||||||
{
|
{
|
||||||
if(!enumName) return static_cast<ALenum>(0);
|
if(!funcName) return nullptr;
|
||||||
|
return alcGetProcAddress(nullptr, funcName);
|
||||||
|
}
|
||||||
|
|
||||||
|
AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName) noexcept
|
||||||
|
{
|
||||||
|
if(!enumName) return ALenum{0};
|
||||||
|
return alcGetEnumValue(nullptr, enumName);
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCE_ALIGN ALenum AL_APIENTRY alGetEnumValueDirect(ALCcontext*, const ALchar *enumName) noexcept
|
||||||
|
{
|
||||||
|
if(!enumName) return ALenum{0};
|
||||||
return alcGetEnumValue(nullptr, enumName);
|
return alcGetEnumValue(nullptr, enumName);
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,15 +1,40 @@
|
||||||
#ifndef AL_FILTER_H
|
#ifndef AL_FILTER_H
|
||||||
#define AL_FILTER_H
|
#define AL_FILTER_H
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string_view>
|
||||||
|
#include <utility>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/alc.h"
|
#include "AL/alc.h"
|
||||||
#include "AL/alext.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
|
#include "alnumeric.h"
|
||||||
|
|
||||||
#define LOWPASSFREQREF 5000.0f
|
|
||||||
#define HIGHPASSFREQREF 250.0f
|
inline constexpr float LowPassFreqRef{5000.0f};
|
||||||
|
inline constexpr float HighPassFreqRef{250.0f};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct FilterTable {
|
||||||
|
static void setParami(struct ALfilter*, ALenum, int);
|
||||||
|
static void setParamiv(struct ALfilter*, ALenum, const int*);
|
||||||
|
static void setParamf(struct ALfilter*, ALenum, float);
|
||||||
|
static void setParamfv(struct ALfilter*, ALenum, const float*);
|
||||||
|
|
||||||
|
static void getParami(const struct ALfilter*, ALenum, int*);
|
||||||
|
static void getParamiv(const struct ALfilter*, ALenum, int*);
|
||||||
|
static void getParamf(const struct ALfilter*, ALenum, float*);
|
||||||
|
static void getParamfv(const struct ALfilter*, ALenum, float*);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NullFilterTable : public FilterTable<NullFilterTable> { };
|
||||||
|
struct LowpassFilterTable : public FilterTable<LowpassFilterTable> { };
|
||||||
|
struct HighpassFilterTable : public FilterTable<HighpassFilterTable> { };
|
||||||
|
struct BandpassFilterTable : public FilterTable<BandpassFilterTable> { };
|
||||||
|
|
||||||
|
|
||||||
struct ALfilter {
|
struct ALfilter {
|
||||||
|
|
@ -17,36 +42,35 @@ struct ALfilter {
|
||||||
|
|
||||||
float Gain{1.0f};
|
float Gain{1.0f};
|
||||||
float GainHF{1.0f};
|
float GainHF{1.0f};
|
||||||
float HFReference{LOWPASSFREQREF};
|
float HFReference{LowPassFreqRef};
|
||||||
float GainLF{1.0f};
|
float GainLF{1.0f};
|
||||||
float LFReference{HIGHPASSFREQREF};
|
float LFReference{HighPassFreqRef};
|
||||||
|
|
||||||
struct Vtable {
|
using TableTypes = std::variant<NullFilterTable,LowpassFilterTable,HighpassFilterTable,
|
||||||
void (*const setParami )(ALfilter *filter, ALenum param, int val);
|
BandpassFilterTable>;
|
||||||
void (*const setParamiv)(ALfilter *filter, ALenum param, const int *vals);
|
TableTypes mTypeVariant;
|
||||||
void (*const setParamf )(ALfilter *filter, ALenum param, float val);
|
|
||||||
void (*const setParamfv)(ALfilter *filter, ALenum param, const float *vals);
|
|
||||||
|
|
||||||
void (*const getParami )(const ALfilter *filter, ALenum param, int *val);
|
|
||||||
void (*const getParamiv)(const ALfilter *filter, ALenum param, int *vals);
|
|
||||||
void (*const getParamf )(const ALfilter *filter, ALenum param, float *val);
|
|
||||||
void (*const getParamfv)(const ALfilter *filter, ALenum param, float *vals);
|
|
||||||
};
|
|
||||||
const Vtable *vtab{nullptr};
|
|
||||||
|
|
||||||
/* Self ID */
|
/* Self ID */
|
||||||
ALuint id{0};
|
ALuint id{0};
|
||||||
|
|
||||||
void setParami(ALenum param, int value) { vtab->setParami(this, param, value); }
|
static void SetName(ALCcontext *context, ALuint id, std::string_view name);
|
||||||
void setParamiv(ALenum param, const int *values) { vtab->setParamiv(this, param, values); }
|
|
||||||
void setParamf(ALenum param, float value) { vtab->setParamf(this, param, value); }
|
|
||||||
void setParamfv(ALenum param, const float *values) { vtab->setParamfv(this, param, values); }
|
|
||||||
void getParami(ALenum param, int *value) const { vtab->getParami(this, param, value); }
|
|
||||||
void getParamiv(ALenum param, int *values) const { vtab->getParamiv(this, param, values); }
|
|
||||||
void getParamf(ALenum param, float *value) const { vtab->getParamf(this, param, value); }
|
|
||||||
void getParamfv(ALenum param, float *values) const { vtab->getParamfv(this, param, values); }
|
|
||||||
|
|
||||||
DISABLE_ALLOC()
|
DISABLE_ALLOC
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FilterSubList {
|
||||||
|
uint64_t FreeMask{~0_u64};
|
||||||
|
gsl::owner<std::array<ALfilter,64>*> Filters{nullptr};
|
||||||
|
|
||||||
|
FilterSubList() noexcept = default;
|
||||||
|
FilterSubList(const FilterSubList&) = delete;
|
||||||
|
FilterSubList(FilterSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Filters{rhs.Filters}
|
||||||
|
{ rhs.FreeMask = ~0_u64; rhs.Filters = nullptr; }
|
||||||
|
~FilterSubList();
|
||||||
|
|
||||||
|
FilterSubList& operator=(const FilterSubList&) = delete;
|
||||||
|
FilterSubList& operator=(FilterSubList&& rhs) noexcept
|
||||||
|
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Filters, rhs.Filters); return *this; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
#include "listener.h"
|
#include "listener.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
|
@ -30,9 +31,10 @@
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
#include "alc/context.h"
|
#include "alc/context.h"
|
||||||
#include "almalloc.h"
|
#include "alc/inprogext.h"
|
||||||
#include "atomic.h"
|
#include "alspan.h"
|
||||||
#include "core/except.h"
|
#include "direct_defs.h"
|
||||||
|
#include "error.h"
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -68,377 +70,333 @@ inline void CommitAndUpdateProps(ALCcontext *context)
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value)
|
AL_API DECL_FUNC2(void, alListenerf, ALenum,param, ALfloat,value)
|
||||||
START_API_FUNC
|
FORCE_ALIGN void AL_APIENTRY alListenerfDirect(ALCcontext *context, ALenum param, ALfloat value) noexcept
|
||||||
{
|
try {
|
||||||
ContextRef context{GetContextRef()};
|
|
||||||
if(!context) UNLIKELY return;
|
|
||||||
|
|
||||||
ALlistener &listener = context->mListener;
|
ALlistener &listener = context->mListener;
|
||||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_GAIN:
|
case AL_GAIN:
|
||||||
if(!(value >= 0.0f && std::isfinite(value)))
|
if(!(value >= 0.0f && std::isfinite(value)))
|
||||||
return context->setError(AL_INVALID_VALUE, "Listener gain out of range");
|
throw al::context_error{AL_INVALID_VALUE, "Listener gain out of range"};
|
||||||
listener.Gain = value;
|
listener.Gain = value;
|
||||||
UpdateProps(context.get());
|
UpdateProps(context);
|
||||||
break;
|
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))
|
||||||
return context->setError(AL_INVALID_VALUE, "Listener meters per unit out of range");
|
throw al::context_error{AL_INVALID_VALUE, "Listener meters per unit out of range"};
|
||||||
listener.mMetersPerUnit = value;
|
listener.mMetersPerUnit = value;
|
||||||
UpdateProps(context.get());
|
UpdateProps(context);
|
||||||
break;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
context->setError(AL_INVALID_ENUM, "Invalid listener float property");
|
|
||||||
}
|
}
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid listener float property 0x%x", param};
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
|
|
||||||
START_API_FUNC
|
|
||||||
{
|
|
||||||
ContextRef context{GetContextRef()};
|
|
||||||
if(!context) UNLIKELY return;
|
|
||||||
|
|
||||||
|
AL_API DECL_FUNC4(void, alListener3f, ALenum,param, ALfloat,value1, ALfloat,value2, ALfloat,value3)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alListener3fDirect(ALCcontext *context, ALenum param, ALfloat value1,
|
||||||
|
ALfloat value2, ALfloat value3) noexcept
|
||||||
|
try {
|
||||||
ALlistener &listener = context->mListener;
|
ALlistener &listener = context->mListener;
|
||||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
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)))
|
||||||
return context->setError(AL_INVALID_VALUE, "Listener position out of range");
|
throw al::context_error{AL_INVALID_VALUE, "Listener position out of range"};
|
||||||
listener.Position[0] = value1;
|
listener.Position[0] = value1;
|
||||||
listener.Position[1] = value2;
|
listener.Position[1] = value2;
|
||||||
listener.Position[2] = value3;
|
listener.Position[2] = value3;
|
||||||
CommitAndUpdateProps(context.get());
|
CommitAndUpdateProps(context);
|
||||||
break;
|
return;
|
||||||
|
|
||||||
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)))
|
||||||
return context->setError(AL_INVALID_VALUE, "Listener velocity out of range");
|
throw al::context_error{AL_INVALID_VALUE, "Listener velocity out of range"};
|
||||||
listener.Velocity[0] = value1;
|
listener.Velocity[0] = value1;
|
||||||
listener.Velocity[1] = value2;
|
listener.Velocity[1] = value2;
|
||||||
listener.Velocity[2] = value3;
|
listener.Velocity[2] = value3;
|
||||||
CommitAndUpdateProps(context.get());
|
CommitAndUpdateProps(context);
|
||||||
break;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
context->setError(AL_INVALID_ENUM, "Invalid listener 3-float property");
|
|
||||||
}
|
}
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-float property 0x%x", param};
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
|
AL_API DECL_FUNC2(void, alListenerfv, ALenum,param, const ALfloat*,values)
|
||||||
START_API_FUNC
|
FORCE_ALIGN void AL_APIENTRY alListenerfvDirect(ALCcontext *context, ALenum param,
|
||||||
{
|
const ALfloat *values) noexcept
|
||||||
if(values)
|
try {
|
||||||
|
if(!values)
|
||||||
|
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||||
|
|
||||||
|
switch(param)
|
||||||
{
|
{
|
||||||
switch(param)
|
case AL_GAIN:
|
||||||
{
|
case AL_METERS_PER_UNIT:
|
||||||
case AL_GAIN:
|
alListenerfDirect(context, param, *values);
|
||||||
case AL_METERS_PER_UNIT:
|
return;
|
||||||
alListenerf(param, values[0]);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case AL_POSITION:
|
case AL_POSITION:
|
||||||
case AL_VELOCITY:
|
case AL_VELOCITY:
|
||||||
alListener3f(param, values[0], values[1], values[2]);
|
auto vals = al::span<const float,3>{values, 3_uz};
|
||||||
return;
|
alListener3fDirect(context, param, vals[0], vals[1], vals[2]);
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextRef context{GetContextRef()};
|
|
||||||
if(!context) UNLIKELY return;
|
|
||||||
|
|
||||||
if(!values) UNLIKELY
|
|
||||||
return context->setError(AL_INVALID_VALUE, "NULL pointer");
|
|
||||||
|
|
||||||
ALlistener &listener = context->mListener;
|
ALlistener &listener = context->mListener;
|
||||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_ORIENTATION:
|
case AL_ORIENTATION:
|
||||||
if(!(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]) &&
|
auto vals = al::span<const float,6>{values, 6_uz};
|
||||||
std::isfinite(values[3]) && std::isfinite(values[4]) && std::isfinite(values[5])))
|
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");
|
return context->setError(AL_INVALID_VALUE, "Listener orientation out of range");
|
||||||
/* AT then UP */
|
/* AT then UP */
|
||||||
listener.OrientAt[0] = values[0];
|
std::copy_n(vals.cbegin(), 3, listener.OrientAt.begin());
|
||||||
listener.OrientAt[1] = values[1];
|
std::copy_n(vals.cbegin()+3, 3, listener.OrientUp.begin());
|
||||||
listener.OrientAt[2] = values[2];
|
CommitAndUpdateProps(context);
|
||||||
listener.OrientUp[0] = values[3];
|
return;
|
||||||
listener.OrientUp[1] = values[4];
|
|
||||||
listener.OrientUp[2] = values[5];
|
|
||||||
CommitAndUpdateProps(context.get());
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
context->setError(AL_INVALID_ENUM, "Invalid listener float-vector property");
|
|
||||||
}
|
}
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid listener float-vector property 0x%x", param};
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alListeneri(ALenum param, ALint /*value*/)
|
|
||||||
START_API_FUNC
|
|
||||||
{
|
|
||||||
ContextRef context{GetContextRef()};
|
|
||||||
if(!context) UNLIKELY return;
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
context->setError(AL_INVALID_ENUM, "Invalid listener integer property");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3)
|
|
||||||
START_API_FUNC
|
AL_API DECL_FUNC2(void, alListeneri, ALenum,param, ALint,value)
|
||||||
{
|
FORCE_ALIGN void AL_APIENTRY alListeneriDirect(ALCcontext *context, ALenum param, ALint /*value*/) noexcept
|
||||||
|
try {
|
||||||
|
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer property 0x%x", param};
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
AL_API DECL_FUNC4(void, alListener3i, ALenum,param, ALint,value1, ALint,value2, ALint,value3)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alListener3iDirect(ALCcontext *context, ALenum param, ALint value1,
|
||||||
|
ALint value2, ALint value3) noexcept
|
||||||
|
try {
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_POSITION:
|
case AL_POSITION:
|
||||||
case AL_VELOCITY:
|
case AL_VELOCITY:
|
||||||
alListener3f(param, static_cast<ALfloat>(value1), static_cast<ALfloat>(value2),
|
alListener3fDirect(context, param, static_cast<ALfloat>(value1),
|
||||||
static_cast<ALfloat>(value3));
|
static_cast<ALfloat>(value2), static_cast<ALfloat>(value3));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextRef context{GetContextRef()};
|
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||||
if(!context) UNLIKELY return;
|
throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-integer property 0x%x", param};
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
AL_API DECL_FUNC2(void, alListeneriv, ALenum,param, const ALint*,values)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alListenerivDirect(ALCcontext *context, ALenum param,
|
||||||
|
const ALint *values) noexcept
|
||||||
|
try {
|
||||||
|
if(!values)
|
||||||
|
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||||
|
|
||||||
|
al::span<const ALint> vals;
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
default:
|
case AL_POSITION:
|
||||||
context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property");
|
case AL_VELOCITY:
|
||||||
|
vals = {values, 3_uz};
|
||||||
|
alListener3fDirect(context, param, static_cast<ALfloat>(vals[0]),
|
||||||
|
static_cast<ALfloat>(vals[1]), static_cast<ALfloat>(vals[2]));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case AL_ORIENTATION:
|
||||||
|
vals = {values, 6_uz};
|
||||||
|
const std::array fvals{static_cast<ALfloat>(vals[0]), static_cast<ALfloat>(vals[1]),
|
||||||
|
static_cast<ALfloat>(vals[2]), static_cast<ALfloat>(vals[3]),
|
||||||
|
static_cast<ALfloat>(vals[4]), static_cast<ALfloat>(vals[5]),
|
||||||
|
};
|
||||||
|
alListenerfvDirect(context, param, fvals.data());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer-vector property 0x%x",
|
||||||
|
param};
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values)
|
|
||||||
START_API_FUNC
|
|
||||||
{
|
|
||||||
if(values)
|
|
||||||
{
|
|
||||||
ALfloat fvals[6];
|
|
||||||
switch(param)
|
|
||||||
{
|
|
||||||
case AL_POSITION:
|
|
||||||
case AL_VELOCITY:
|
|
||||||
alListener3f(param, static_cast<ALfloat>(values[0]), static_cast<ALfloat>(values[1]),
|
|
||||||
static_cast<ALfloat>(values[2]));
|
|
||||||
return;
|
|
||||||
|
|
||||||
case AL_ORIENTATION:
|
|
||||||
fvals[0] = static_cast<ALfloat>(values[0]);
|
|
||||||
fvals[1] = static_cast<ALfloat>(values[1]);
|
|
||||||
fvals[2] = static_cast<ALfloat>(values[2]);
|
|
||||||
fvals[3] = static_cast<ALfloat>(values[3]);
|
|
||||||
fvals[4] = static_cast<ALfloat>(values[4]);
|
|
||||||
fvals[5] = static_cast<ALfloat>(values[5]);
|
|
||||||
alListenerfv(param, fvals);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContextRef context{GetContextRef()};
|
|
||||||
if(!context) UNLIKELY return;
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
|
||||||
if(!values) UNLIKELY
|
|
||||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
|
||||||
else switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
context->setError(AL_INVALID_ENUM, "Invalid listener integer-vector property");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value)
|
AL_API DECL_FUNC2(void, alGetListenerf, ALenum,param, ALfloat*,value)
|
||||||
START_API_FUNC
|
FORCE_ALIGN void AL_APIENTRY alGetListenerfDirect(ALCcontext *context, ALenum param,
|
||||||
{
|
ALfloat *value) noexcept
|
||||||
ContextRef context{GetContextRef()};
|
try {
|
||||||
if(!context) UNLIKELY return;
|
|
||||||
|
|
||||||
ALlistener &listener = context->mListener;
|
|
||||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
|
||||||
if(!value)
|
if(!value)
|
||||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||||
else switch(param)
|
|
||||||
{
|
|
||||||
case AL_GAIN:
|
|
||||||
*value = listener.Gain;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AL_METERS_PER_UNIT:
|
|
||||||
*value = listener.mMetersPerUnit;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
context->setError(AL_INVALID_ENUM, "Invalid listener float property");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
|
|
||||||
START_API_FUNC
|
|
||||||
{
|
|
||||||
ContextRef context{GetContextRef()};
|
|
||||||
if(!context) UNLIKELY return;
|
|
||||||
|
|
||||||
ALlistener &listener = context->mListener;
|
ALlistener &listener = context->mListener;
|
||||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||||
|
switch(param)
|
||||||
|
{
|
||||||
|
case AL_GAIN: *value = listener.Gain; return;
|
||||||
|
case AL_METERS_PER_UNIT: *value = listener.mMetersPerUnit; return;
|
||||||
|
}
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid listener float property 0x%x", param};
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
AL_API DECL_FUNC4(void, alGetListener3f, ALenum,param, ALfloat*,value1, ALfloat*,value2, ALfloat*,value3)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alGetListener3fDirect(ALCcontext *context, ALenum param,
|
||||||
|
ALfloat *value1, ALfloat *value2, ALfloat *value3) noexcept
|
||||||
|
try {
|
||||||
if(!value1 || !value2 || !value3)
|
if(!value1 || !value2 || !value3)
|
||||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||||
else switch(param)
|
|
||||||
|
ALlistener &listener = context->mListener;
|
||||||
|
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||||
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_POSITION:
|
case AL_POSITION:
|
||||||
*value1 = listener.Position[0];
|
*value1 = listener.Position[0];
|
||||||
*value2 = listener.Position[1];
|
*value2 = listener.Position[1];
|
||||||
*value3 = listener.Position[2];
|
*value3 = listener.Position[2];
|
||||||
break;
|
return;
|
||||||
|
|
||||||
case AL_VELOCITY:
|
case AL_VELOCITY:
|
||||||
*value1 = listener.Velocity[0];
|
*value1 = listener.Velocity[0];
|
||||||
*value2 = listener.Velocity[1];
|
*value2 = listener.Velocity[1];
|
||||||
*value3 = listener.Velocity[2];
|
*value3 = listener.Velocity[2];
|
||||||
break;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
context->setError(AL_INVALID_ENUM, "Invalid listener 3-float property");
|
|
||||||
}
|
}
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-float property 0x%x", param};
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
|
AL_API DECL_FUNC2(void, alGetListenerfv, ALenum,param, ALfloat*,values)
|
||||||
START_API_FUNC
|
FORCE_ALIGN void AL_APIENTRY alGetListenerfvDirect(ALCcontext *context, ALenum param,
|
||||||
{
|
ALfloat *values) noexcept
|
||||||
|
try {
|
||||||
|
if(!values)
|
||||||
|
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||||
|
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_GAIN:
|
case AL_GAIN:
|
||||||
case AL_METERS_PER_UNIT:
|
case AL_METERS_PER_UNIT:
|
||||||
alGetListenerf(param, values);
|
alGetListenerfDirect(context, param, values);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case AL_POSITION:
|
case AL_POSITION:
|
||||||
case AL_VELOCITY:
|
case AL_VELOCITY:
|
||||||
alGetListener3f(param, values+0, values+1, values+2);
|
auto vals = al::span<ALfloat,3>{values, 3_uz};
|
||||||
|
alGetListener3fDirect(context, param, &vals[0], &vals[1], &vals[2]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextRef context{GetContextRef()};
|
|
||||||
if(!context) UNLIKELY return;
|
|
||||||
|
|
||||||
ALlistener &listener = context->mListener;
|
ALlistener &listener = context->mListener;
|
||||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||||
if(!values)
|
switch(param)
|
||||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
|
||||||
else switch(param)
|
|
||||||
{
|
{
|
||||||
case AL_ORIENTATION:
|
case AL_ORIENTATION:
|
||||||
|
al::span<ALfloat,6> vals{values, 6_uz};
|
||||||
// AT then UP
|
// AT then UP
|
||||||
values[0] = listener.OrientAt[0];
|
std::copy_n(listener.OrientAt.cbegin(), 3, vals.begin());
|
||||||
values[1] = listener.OrientAt[1];
|
std::copy_n(listener.OrientUp.cbegin(), 3, vals.begin()+3);
|
||||||
values[2] = listener.OrientAt[2];
|
return;
|
||||||
values[3] = listener.OrientUp[0];
|
|
||||||
values[4] = listener.OrientUp[1];
|
|
||||||
values[5] = listener.OrientUp[2];
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
context->setError(AL_INVALID_ENUM, "Invalid listener float-vector property");
|
|
||||||
}
|
}
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid listener float-vector property 0x%x", param};
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value)
|
|
||||||
START_API_FUNC
|
|
||||||
{
|
|
||||||
ContextRef context{GetContextRef()};
|
|
||||||
if(!context) UNLIKELY return;
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
|
||||||
if(!value)
|
|
||||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
|
||||||
else switch(param)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
context->setError(AL_INVALID_ENUM, "Invalid listener integer property");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3)
|
|
||||||
START_API_FUNC
|
AL_API DECL_FUNC2(void, alGetListeneri, ALenum,param, ALint*,value)
|
||||||
{
|
FORCE_ALIGN void AL_APIENTRY alGetListeneriDirect(ALCcontext *context, ALenum param, ALint *value) noexcept
|
||||||
ContextRef context{GetContextRef()};
|
try {
|
||||||
if(!context) UNLIKELY return;
|
if(!value) throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||||
|
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer property 0x%x", param};
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
AL_API DECL_FUNC4(void, alGetListener3i, ALenum,param, ALint*,value1, ALint*,value2, ALint*,value3)
|
||||||
|
FORCE_ALIGN void AL_APIENTRY alGetListener3iDirect(ALCcontext *context, ALenum param,
|
||||||
|
ALint *value1, ALint *value2, ALint *value3) noexcept
|
||||||
|
try {
|
||||||
|
if(!value1 || !value2 || !value3)
|
||||||
|
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||||
|
|
||||||
ALlistener &listener = context->mListener;
|
ALlistener &listener = context->mListener;
|
||||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||||
if(!value1 || !value2 || !value3)
|
switch(param)
|
||||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
|
||||||
else switch(param)
|
|
||||||
{
|
{
|
||||||
case AL_POSITION:
|
case AL_POSITION:
|
||||||
*value1 = static_cast<ALint>(listener.Position[0]);
|
*value1 = static_cast<ALint>(listener.Position[0]);
|
||||||
*value2 = static_cast<ALint>(listener.Position[1]);
|
*value2 = static_cast<ALint>(listener.Position[1]);
|
||||||
*value3 = static_cast<ALint>(listener.Position[2]);
|
*value3 = static_cast<ALint>(listener.Position[2]);
|
||||||
break;
|
return;
|
||||||
|
|
||||||
case AL_VELOCITY:
|
case AL_VELOCITY:
|
||||||
*value1 = static_cast<ALint>(listener.Velocity[0]);
|
*value1 = static_cast<ALint>(listener.Velocity[0]);
|
||||||
*value2 = static_cast<ALint>(listener.Velocity[1]);
|
*value2 = static_cast<ALint>(listener.Velocity[1]);
|
||||||
*value3 = static_cast<ALint>(listener.Velocity[2]);
|
*value3 = static_cast<ALint>(listener.Velocity[2]);
|
||||||
break;
|
return;
|
||||||
|
|
||||||
default:
|
|
||||||
context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property");
|
|
||||||
}
|
}
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-integer property 0x%x", param};
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
||||||
AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
|
AL_API DECL_FUNC2(void, alGetListeneriv, ALenum,param, ALint*,values)
|
||||||
START_API_FUNC
|
FORCE_ALIGN void AL_APIENTRY alGetListenerivDirect(ALCcontext *context, ALenum param,
|
||||||
{
|
ALint *values) noexcept
|
||||||
|
try {
|
||||||
|
if(!values)
|
||||||
|
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||||
|
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_POSITION:
|
case AL_POSITION:
|
||||||
case AL_VELOCITY:
|
case AL_VELOCITY:
|
||||||
alGetListener3i(param, values+0, values+1, values+2);
|
auto vals = al::span<ALint,3>{values, 3_uz};
|
||||||
|
alGetListener3iDirect(context, param, &vals[0], &vals[1], &vals[2]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextRef context{GetContextRef()};
|
|
||||||
if(!context) UNLIKELY return;
|
|
||||||
|
|
||||||
ALlistener &listener = context->mListener;
|
ALlistener &listener = context->mListener;
|
||||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||||
if(!values)
|
|
||||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
static constexpr auto f2i = [](const float val) noexcept { return static_cast<ALint>(val); };
|
||||||
else switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_ORIENTATION:
|
case AL_ORIENTATION:
|
||||||
|
auto vals = al::span<ALint,6>{values, 6_uz};
|
||||||
// AT then UP
|
// AT then UP
|
||||||
values[0] = static_cast<ALint>(listener.OrientAt[0]);
|
std::transform(listener.OrientAt.cbegin(), listener.OrientAt.cend(), vals.begin(), f2i);
|
||||||
values[1] = static_cast<ALint>(listener.OrientAt[1]);
|
std::transform(listener.OrientUp.cbegin(), listener.OrientUp.cend(), vals.begin()+3, f2i);
|
||||||
values[2] = static_cast<ALint>(listener.OrientAt[2]);
|
return;
|
||||||
values[3] = static_cast<ALint>(listener.OrientUp[0]);
|
|
||||||
values[4] = static_cast<ALint>(listener.OrientUp[1]);
|
|
||||||
values[5] = static_cast<ALint>(listener.OrientUp[2]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
context->setError(AL_INVALID_ENUM, "Invalid listener integer-vector property");
|
|
||||||
}
|
}
|
||||||
|
throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer-vector property 0x%x",
|
||||||
|
param};
|
||||||
|
}
|
||||||
|
catch(al::context_error& e) {
|
||||||
|
context->setError(e.errorCode(), "%s", e.what());
|
||||||
}
|
}
|
||||||
END_API_FUNC
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
#include "AL/al.h"
|
|
||||||
#include "AL/alc.h"
|
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
|
|
@ -18,7 +16,7 @@ struct ALlistener {
|
||||||
float Gain{1.0f};
|
float Gain{1.0f};
|
||||||
float mMetersPerUnit{AL_DEFAULT_METERS_PER_UNIT};
|
float mMetersPerUnit{AL_DEFAULT_METERS_PER_UNIT};
|
||||||
|
|
||||||
DISABLE_ALLOC()
|
DISABLE_ALLOC
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -2,26 +2,26 @@
|
||||||
#define AL_SOURCE_H
|
#define AL_SOURCE_H
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <iterator>
|
#include <cstdint>
|
||||||
#include <limits>
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include <limits>
|
||||||
|
#include <string_view>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/alc.h"
|
#include "AL/alc.h"
|
||||||
|
#include "AL/alext.h"
|
||||||
|
|
||||||
#include "alc/alu.h"
|
|
||||||
#include "alc/context.h"
|
|
||||||
#include "alc/inprogext.h"
|
|
||||||
#include "aldeque.h"
|
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
|
#include "alnumbers.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "atomic.h"
|
#include "alspan.h"
|
||||||
|
#include "core/context.h"
|
||||||
#include "core/voice.h"
|
#include "core/voice.h"
|
||||||
#include "vector.h"
|
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
|
#include "eax/api.h"
|
||||||
#include "eax/call.h"
|
#include "eax/call.h"
|
||||||
#include "eax/exception.h"
|
#include "eax/exception.h"
|
||||||
#include "eax/fx_slot_index.h"
|
#include "eax/fx_slot_index.h"
|
||||||
|
|
@ -30,23 +30,23 @@
|
||||||
|
|
||||||
struct ALbuffer;
|
struct ALbuffer;
|
||||||
struct ALeffectslot;
|
struct ALeffectslot;
|
||||||
|
enum class Resampler : uint8_t;
|
||||||
|
|
||||||
enum class SourceStereo : bool {
|
enum class SourceStereo : bool {
|
||||||
Normal = AL_NORMAL_SOFT,
|
Normal = AL_NORMAL_SOFT,
|
||||||
Enhanced = AL_SUPER_STEREO_SOFT
|
Enhanced = AL_SUPER_STEREO_SOFT
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DEFAULT_SENDS 2
|
inline constexpr size_t DefaultSendCount{2};
|
||||||
|
|
||||||
#define INVALID_VOICE_IDX static_cast<ALuint>(-1)
|
inline constexpr ALuint InvalidVoiceIndex{std::numeric_limits<ALuint>::max()};
|
||||||
|
|
||||||
extern bool sBufferSubDataCompat;
|
inline bool sBufferSubDataCompat{false};
|
||||||
|
|
||||||
struct ALbufferQueueItem : public VoiceBufferItem {
|
struct ALbufferQueueItem : public VoiceBufferItem {
|
||||||
ALbuffer *mBuffer{nullptr};
|
ALbuffer *mBuffer{nullptr};
|
||||||
|
|
||||||
DISABLE_ALLOC()
|
DISABLE_ALLOC
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -88,6 +88,7 @@ struct ALsource {
|
||||||
DirectMode DirectChannels{DirectMode::Off};
|
DirectMode DirectChannels{DirectMode::Off};
|
||||||
SpatializeMode mSpatialize{SpatializeMode::Auto};
|
SpatializeMode mSpatialize{SpatializeMode::Auto};
|
||||||
SourceStereo mStereoMode{SourceStereo::Normal};
|
SourceStereo mStereoMode{SourceStereo::Normal};
|
||||||
|
bool mPanningEnabled{false};
|
||||||
|
|
||||||
bool DryGainHFAuto{true};
|
bool DryGainHFAuto{true};
|
||||||
bool WetGainAuto{true};
|
bool WetGainAuto{true};
|
||||||
|
|
@ -105,24 +106,27 @@ struct ALsource {
|
||||||
|
|
||||||
float Radius{0.0f};
|
float Radius{0.0f};
|
||||||
float EnhWidth{0.593f};
|
float EnhWidth{0.593f};
|
||||||
|
float mPan{0.0f};
|
||||||
|
|
||||||
/** Direct filter and auxiliary send info. */
|
/** Direct filter and auxiliary send info. */
|
||||||
struct {
|
struct DirectData {
|
||||||
float Gain;
|
float Gain{};
|
||||||
float GainHF;
|
float GainHF{};
|
||||||
float HFReference;
|
float HFReference{};
|
||||||
float GainLF;
|
float GainLF{};
|
||||||
float LFReference;
|
float LFReference{};
|
||||||
} Direct;
|
|
||||||
struct SendData {
|
|
||||||
ALeffectslot *Slot;
|
|
||||||
float Gain;
|
|
||||||
float GainHF;
|
|
||||||
float HFReference;
|
|
||||||
float GainLF;
|
|
||||||
float LFReference;
|
|
||||||
};
|
};
|
||||||
std::array<SendData,MAX_SENDS> Send;
|
DirectData Direct;
|
||||||
|
|
||||||
|
struct SendData {
|
||||||
|
ALeffectslot *Slot{};
|
||||||
|
float Gain{};
|
||||||
|
float GainHF{};
|
||||||
|
float HFReference{};
|
||||||
|
float GainLF{};
|
||||||
|
float LFReference{};
|
||||||
|
};
|
||||||
|
std::array<SendData,MaxSendCount> Send;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Last user-specified offset, and the offset type (bytes, samples, or
|
* Last user-specified offset, and the offset type (bytes, samples, or
|
||||||
|
|
@ -138,26 +142,28 @@ struct ALsource {
|
||||||
ALenum state{AL_INITIAL};
|
ALenum state{AL_INITIAL};
|
||||||
|
|
||||||
/** Source Buffer Queue head. */
|
/** Source Buffer Queue head. */
|
||||||
al::deque<ALbufferQueueItem> mQueue;
|
std::deque<ALbufferQueueItem> mQueue;
|
||||||
|
|
||||||
bool mPropsDirty{true};
|
bool mPropsDirty{true};
|
||||||
|
|
||||||
/* Index into the context's Voices array. Lazily updated, only checked and
|
/* Index into the context's Voices array. Lazily updated, only checked and
|
||||||
* reset when looking up the voice.
|
* reset when looking up the voice.
|
||||||
*/
|
*/
|
||||||
ALuint VoiceIdx{INVALID_VOICE_IDX};
|
ALuint VoiceIdx{InvalidVoiceIndex};
|
||||||
|
|
||||||
/** Self ID */
|
/** Self ID */
|
||||||
ALuint id{0};
|
ALuint id{0};
|
||||||
|
|
||||||
|
|
||||||
ALsource();
|
ALsource() noexcept;
|
||||||
~ALsource();
|
~ALsource();
|
||||||
|
|
||||||
ALsource(const ALsource&) = delete;
|
ALsource(const ALsource&) = delete;
|
||||||
ALsource& operator=(const ALsource&) = delete;
|
ALsource& operator=(const ALsource&) = delete;
|
||||||
|
|
||||||
DISABLE_ALLOC()
|
static void SetName(ALCcontext *context, ALuint id, std::string_view name);
|
||||||
|
|
||||||
|
DISABLE_ALLOC
|
||||||
|
|
||||||
#ifdef ALSOFT_EAX
|
#ifdef ALSOFT_EAX
|
||||||
public:
|
public:
|
||||||
|
|
@ -171,18 +177,18 @@ public:
|
||||||
private:
|
private:
|
||||||
using Exception = EaxSourceException;
|
using Exception = EaxSourceException;
|
||||||
|
|
||||||
static constexpr auto eax_max_speakers = 9;
|
static constexpr auto eax_max_speakers{9u};
|
||||||
|
|
||||||
using EaxFxSlotIds = const GUID* [EAX_MAX_FXSLOTS];
|
using EaxFxSlotIds = std::array<const GUID*,EAX_MAX_FXSLOTS>;
|
||||||
|
|
||||||
static constexpr const EaxFxSlotIds eax4_fx_slot_ids = {
|
static constexpr const EaxFxSlotIds eax4_fx_slot_ids{
|
||||||
&EAXPROPERTYID_EAX40_FXSlot0,
|
&EAXPROPERTYID_EAX40_FXSlot0,
|
||||||
&EAXPROPERTYID_EAX40_FXSlot1,
|
&EAXPROPERTYID_EAX40_FXSlot1,
|
||||||
&EAXPROPERTYID_EAX40_FXSlot2,
|
&EAXPROPERTYID_EAX40_FXSlot2,
|
||||||
&EAXPROPERTYID_EAX40_FXSlot3,
|
&EAXPROPERTYID_EAX40_FXSlot3,
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr const EaxFxSlotIds eax5_fx_slot_ids = {
|
static constexpr const EaxFxSlotIds eax5_fx_slot_ids{
|
||||||
&EAXPROPERTYID_EAX50_FXSlot0,
|
&EAXPROPERTYID_EAX50_FXSlot0,
|
||||||
&EAXPROPERTYID_EAX50_FXSlot1,
|
&EAXPROPERTYID_EAX50_FXSlot1,
|
||||||
&EAXPROPERTYID_EAX50_FXSlot2,
|
&EAXPROPERTYID_EAX50_FXSlot2,
|
||||||
|
|
@ -215,11 +221,6 @@ private:
|
||||||
Eax3Props source;
|
Eax3Props source;
|
||||||
EaxSends sends;
|
EaxSends sends;
|
||||||
EAX40ACTIVEFXSLOTS active_fx_slots;
|
EAX40ACTIVEFXSLOTS active_fx_slots;
|
||||||
|
|
||||||
bool operator==(const Eax4Props& rhs) noexcept
|
|
||||||
{
|
|
||||||
return std::memcmp(this, &rhs, sizeof(Eax4Props)) == 0;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Eax4State {
|
struct Eax4State {
|
||||||
|
|
@ -232,11 +233,6 @@ private:
|
||||||
EaxSends sends;
|
EaxSends sends;
|
||||||
EAX50ACTIVEFXSLOTS active_fx_slots;
|
EAX50ACTIVEFXSLOTS active_fx_slots;
|
||||||
EaxSpeakerLevels speaker_levels;
|
EaxSpeakerLevels speaker_levels;
|
||||||
|
|
||||||
bool operator==(const Eax5Props& rhs) noexcept
|
|
||||||
{
|
|
||||||
return std::memcmp(this, &rhs, sizeof(Eax5Props)) == 0;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Eax5State {
|
struct Eax5State {
|
||||||
|
|
@ -546,7 +542,24 @@ private:
|
||||||
struct Eax5SourceAllValidator {
|
struct Eax5SourceAllValidator {
|
||||||
void operator()(const EAX50SOURCEPROPERTIES& props) const
|
void operator()(const EAX50SOURCEPROPERTIES& props) const
|
||||||
{
|
{
|
||||||
Eax3SourceAllValidator{}(static_cast<const Eax3Props&>(props));
|
Eax2SourceDirectValidator{}(props.lDirect);
|
||||||
|
Eax2SourceDirectHfValidator{}(props.lDirectHF);
|
||||||
|
Eax2SourceRoomValidator{}(props.lRoom);
|
||||||
|
Eax2SourceRoomHfValidator{}(props.lRoomHF);
|
||||||
|
Eax2SourceObstructionValidator{}(props.lObstruction);
|
||||||
|
Eax2SourceObstructionLfRatioValidator{}(props.flObstructionLFRatio);
|
||||||
|
Eax2SourceOcclusionValidator{}(props.lOcclusion);
|
||||||
|
Eax2SourceOcclusionLfRatioValidator{}(props.flOcclusionLFRatio);
|
||||||
|
Eax2SourceOcclusionRoomRatioValidator{}(props.flOcclusionRoomRatio);
|
||||||
|
Eax3SourceOcclusionDirectRatioValidator{}(props.flOcclusionDirectRatio);
|
||||||
|
Eax3SourceExclusionValidator{}(props.lExclusion);
|
||||||
|
Eax3SourceExclusionLfRatioValidator{}(props.flExclusionLFRatio);
|
||||||
|
Eax2SourceOutsideVolumeHfValidator{}(props.lOutsideVolumeHF);
|
||||||
|
Eax3SourceDopplerFactorValidator{}(props.flDopplerFactor);
|
||||||
|
Eax3SourceRolloffFactorValidator{}(props.flRolloffFactor);
|
||||||
|
Eax2SourceRoomRolloffFactorValidator{}(props.flRoomRolloffFactor);
|
||||||
|
Eax2SourceAirAbsorptionFactorValidator{}(props.flAirAbsorptionFactor);
|
||||||
|
Eax5SourceFlagsValidator{}(props.ulFlags);
|
||||||
Eax5SourceMacroFXFactorValidator{}(props.flMacroFXFactor);
|
Eax5SourceMacroFXFactorValidator{}(props.flMacroFXFactor);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -809,39 +822,38 @@ private:
|
||||||
[[noreturn]] static void eax_fail_unknown_active_fx_slot_id();
|
[[noreturn]] static void eax_fail_unknown_active_fx_slot_id();
|
||||||
[[noreturn]] static void eax_fail_unknown_receiving_fx_slot_id();
|
[[noreturn]] static void eax_fail_unknown_receiving_fx_slot_id();
|
||||||
|
|
||||||
void eax_set_sends_defaults(EaxSends& sends, const EaxFxSlotIds& ids) noexcept;
|
static void eax_set_sends_defaults(EaxSends& sends, const EaxFxSlotIds& ids) noexcept;
|
||||||
void eax1_set_defaults(Eax1Props& props) noexcept;
|
static void eax1_set_defaults(Eax1Props& props) noexcept;
|
||||||
void eax1_set_defaults() noexcept;
|
void eax1_set_defaults() noexcept;
|
||||||
void eax2_set_defaults(Eax2Props& props) noexcept;
|
static void eax2_set_defaults(Eax2Props& props) noexcept;
|
||||||
void eax2_set_defaults() noexcept;
|
void eax2_set_defaults() noexcept;
|
||||||
void eax3_set_defaults(Eax3Props& props) noexcept;
|
static void eax3_set_defaults(Eax3Props& props) noexcept;
|
||||||
void eax3_set_defaults() noexcept;
|
void eax3_set_defaults() noexcept;
|
||||||
void eax4_set_sends_defaults(EaxSends& sends) noexcept;
|
static void eax4_set_sends_defaults(EaxSends& sends) noexcept;
|
||||||
void eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS& slots) noexcept;
|
static void eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS& slots) noexcept;
|
||||||
void eax4_set_defaults() noexcept;
|
void eax4_set_defaults() noexcept;
|
||||||
void eax5_set_source_defaults(EAX50SOURCEPROPERTIES& props) noexcept;
|
static void eax5_set_source_defaults(EAX50SOURCEPROPERTIES& props) noexcept;
|
||||||
void eax5_set_sends_defaults(EaxSends& sends) noexcept;
|
static void eax5_set_sends_defaults(EaxSends& sends) noexcept;
|
||||||
void eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS& slots) noexcept;
|
static void eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS& slots) noexcept;
|
||||||
void eax5_set_speaker_levels_defaults(EaxSpeakerLevels& speaker_levels) noexcept;
|
static void eax5_set_speaker_levels_defaults(EaxSpeakerLevels& speaker_levels) noexcept;
|
||||||
void eax5_set_defaults(Eax5Props& props) noexcept;
|
static void eax5_set_defaults(Eax5Props& props) noexcept;
|
||||||
void eax5_set_defaults() noexcept;
|
void eax5_set_defaults() noexcept;
|
||||||
void eax_set_defaults() noexcept;
|
void eax_set_defaults() noexcept;
|
||||||
|
|
||||||
void eax1_translate(const Eax1Props& src, Eax5Props& dst) noexcept;
|
static void eax1_translate(const Eax1Props& src, Eax5Props& dst) noexcept;
|
||||||
void eax2_translate(const Eax2Props& src, Eax5Props& dst) noexcept;
|
static void eax2_translate(const Eax2Props& src, Eax5Props& dst) noexcept;
|
||||||
void eax3_translate(const Eax3Props& src, Eax5Props& dst) noexcept;
|
static void eax3_translate(const Eax3Props& src, Eax5Props& dst) noexcept;
|
||||||
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(
|
||||||
long src_occlusion_mb,
|
long src_occlusion_mb,
|
||||||
float path_ratio,
|
float path_ratio,
|
||||||
float lf_ratio) noexcept;
|
float lf_ratio) noexcept;
|
||||||
|
|
||||||
EaxAlLowPassParam eax_create_direct_filter_param() const noexcept;
|
[[nodiscard]] auto eax_create_direct_filter_param() const noexcept -> EaxAlLowPassParam;
|
||||||
|
|
||||||
EaxAlLowPassParam eax_create_room_filter_param(
|
[[nodiscard]] auto eax_create_room_filter_param(const ALeffectslot& fx_slot,
|
||||||
const ALeffectslot& fx_slot,
|
const EAXSOURCEALLSENDPROPERTIES& send) const noexcept -> EaxAlLowPassParam;
|
||||||
const EAXSOURCEALLSENDPROPERTIES& send) const noexcept;
|
|
||||||
|
|
||||||
void eax_update_direct_filter();
|
void eax_update_direct_filter();
|
||||||
void eax_update_room_filters();
|
void eax_update_room_filters();
|
||||||
|
|
@ -894,16 +906,16 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void eax_get_active_fx_slot_id(const EaxCall& call, const GUID* ids, size_t max_count);
|
static void eax_get_active_fx_slot_id(const EaxCall& call, const al::span<const GUID> src_ids);
|
||||||
void eax1_get(const EaxCall& call, const Eax1Props& props);
|
static void eax1_get(const EaxCall& call, const Eax1Props& props);
|
||||||
void eax2_get(const EaxCall& call, const Eax2Props& props);
|
static void eax2_get(const EaxCall& call, const Eax2Props& props);
|
||||||
void eax3_get_obstruction(const EaxCall& call, const Eax3Props& props);
|
static void eax3_get_obstruction(const EaxCall& call, const Eax3Props& props);
|
||||||
void eax3_get_occlusion(const EaxCall& call, const Eax3Props& props);
|
static void eax3_get_occlusion(const EaxCall& call, const Eax3Props& props);
|
||||||
void eax3_get_exclusion(const EaxCall& call, const Eax3Props& props);
|
static void eax3_get_exclusion(const EaxCall& call, const Eax3Props& props);
|
||||||
void eax3_get(const EaxCall& call, const Eax3Props& props);
|
static void eax3_get(const EaxCall& call, const Eax3Props& props);
|
||||||
void eax4_get(const EaxCall& call, const Eax4Props& props);
|
void eax4_get(const EaxCall& call, const Eax4Props& props);
|
||||||
void eax5_get_all_2d(const EaxCall& call, const EAX50SOURCEPROPERTIES& props);
|
static void eax5_get_all_2d(const EaxCall& call, const EAX50SOURCEPROPERTIES& props);
|
||||||
void eax5_get_speaker_levels(const EaxCall& call, const EaxSpeakerLevels& props);
|
static void eax5_get_speaker_levels(const EaxCall& call, const EaxSpeakerLevels& props);
|
||||||
void eax5_get(const EaxCall& call, const Eax5Props& props);
|
void eax5_get(const EaxCall& call, const Eax5Props& props);
|
||||||
void eax_get(const EaxCall& call);
|
void eax_get(const EaxCall& call);
|
||||||
|
|
||||||
|
|
@ -976,21 +988,21 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TValidator, size_t TIdCount>
|
template<typename TValidator, size_t TIdCount>
|
||||||
void eax_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount])
|
void eax_defer_active_fx_slot_id(const EaxCall& call, const al::span<GUID,TIdCount> dst_ids)
|
||||||
{
|
{
|
||||||
const auto src_ids = call.get_values<const GUID>(TIdCount);
|
const auto src_ids = call.get_values<const GUID>(TIdCount);
|
||||||
std::for_each(src_ids.cbegin(), src_ids.cend(), TValidator{});
|
std::for_each(src_ids.cbegin(), src_ids.cend(), TValidator{});
|
||||||
std::uninitialized_copy(src_ids.cbegin(), src_ids.cend(), dst_ids);
|
std::uninitialized_copy(src_ids.cbegin(), src_ids.cend(), dst_ids.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t TIdCount>
|
template<size_t TIdCount>
|
||||||
void eax4_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount])
|
void eax4_defer_active_fx_slot_id(const EaxCall& call, const al::span<GUID,TIdCount> dst_ids)
|
||||||
{
|
{
|
||||||
eax_defer_active_fx_slot_id<Eax4ActiveFxSlotIdValidator>(call, dst_ids);
|
eax_defer_active_fx_slot_id<Eax4ActiveFxSlotIdValidator>(call, dst_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t TIdCount>
|
template<size_t TIdCount>
|
||||||
void eax5_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount])
|
void eax5_defer_active_fx_slot_id(const EaxCall& call, const al::span<GUID,TIdCount> dst_ids)
|
||||||
{
|
{
|
||||||
eax_defer_active_fx_slot_id<Eax5ActiveFxSlotIdValidator>(call, dst_ids);
|
eax_defer_active_fx_slot_id<Eax5ActiveFxSlotIdValidator>(call, dst_ids);
|
||||||
}
|
}
|
||||||
|
|
@ -1022,12 +1034,12 @@ private:
|
||||||
void eax_set_efx_wet_gain_auto();
|
void eax_set_efx_wet_gain_auto();
|
||||||
void eax_set_efx_wet_gain_hf_auto();
|
void eax_set_efx_wet_gain_hf_auto();
|
||||||
|
|
||||||
void eax1_set(const EaxCall& call, Eax1Props& props);
|
static void eax1_set(const EaxCall& call, Eax1Props& props);
|
||||||
void eax2_set(const EaxCall& call, Eax2Props& props);
|
static void eax2_set(const EaxCall& call, Eax2Props& props);
|
||||||
void eax3_set(const EaxCall& call, Eax3Props& props);
|
void eax3_set(const EaxCall& call, Eax3Props& props);
|
||||||
void eax4_set(const EaxCall& call, Eax4Props& props);
|
void eax4_set(const EaxCall& call, Eax4Props& props);
|
||||||
void eax5_defer_all_2d(const EaxCall& call, EAX50SOURCEPROPERTIES& props);
|
static void eax5_defer_all_2d(const EaxCall& call, EAX50SOURCEPROPERTIES& props);
|
||||||
void eax5_defer_speaker_levels(const EaxCall& call, EaxSpeakerLevels& props);
|
static void eax5_defer_speaker_levels(const EaxCall& call, EaxSpeakerLevels& props);
|
||||||
void eax5_set(const EaxCall& call, Eax5Props& props);
|
void eax5_set(const EaxCall& call, Eax5Props& props);
|
||||||
void eax_set(const EaxCall& call);
|
void eax_set(const EaxCall& call);
|
||||||
|
|
||||||
|
|
@ -1041,4 +1053,19 @@ private:
|
||||||
|
|
||||||
void UpdateAllSourceProps(ALCcontext *context);
|
void UpdateAllSourceProps(ALCcontext *context);
|
||||||
|
|
||||||
|
struct SourceSubList {
|
||||||
|
uint64_t FreeMask{~0_u64};
|
||||||
|
gsl::owner<std::array<ALsource,64>*> Sources{nullptr};
|
||||||
|
|
||||||
|
SourceSubList() noexcept = default;
|
||||||
|
SourceSubList(const SourceSubList&) = delete;
|
||||||
|
SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources}
|
||||||
|
{ rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; }
|
||||||
|
~SourceSubList();
|
||||||
|
|
||||||
|
SourceSubList& operator=(const SourceSubList&) = delete;
|
||||||
|
SourceSubList& operator=(SourceSubList&& rhs) noexcept
|
||||||
|
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -22,9 +22,6 @@
|
||||||
|
|
||||||
#include "alconfig.h"
|
#include "alconfig.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cctype>
|
|
||||||
#include <cstring>
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
|
|
@ -34,25 +31,47 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdio>
|
#include <array>
|
||||||
|
#include <cctype>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <istream>
|
||||||
|
#include <limits>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "alfstream.h"
|
#include "almalloc.h"
|
||||||
#include "alstring.h"
|
#include "alstring.h"
|
||||||
#include "core/helpers.h"
|
#include "core/helpers.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "strutils.h"
|
#include "strutils.h"
|
||||||
#include "vector.h"
|
|
||||||
|
|
||||||
|
#if defined(ALSOFT_UWP)
|
||||||
|
#include <winrt/Windows.Media.Core.h> // !!This is important!!
|
||||||
|
#include <winrt/Windows.Storage.h>
|
||||||
|
#include <winrt/Windows.Foundation.h>
|
||||||
|
#include <winrt/Windows.Foundation.Collections.h>
|
||||||
|
using namespace winrt;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
|
#if defined(_WIN32) && !defined(_GAMING_XBOX) && !defined(ALSOFT_UWP)
|
||||||
|
struct CoTaskMemDeleter {
|
||||||
|
void operator()(void *mem) const { CoTaskMemFree(mem); }
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
struct ConfigEntry {
|
struct ConfigEntry {
|
||||||
std::string key;
|
std::string key;
|
||||||
std::string value;
|
std::string value;
|
||||||
};
|
};
|
||||||
al::vector<ConfigEntry> ConfOpts;
|
std::vector<ConfigEntry> ConfOpts;
|
||||||
|
|
||||||
|
|
||||||
std::string &lstrip(std::string &line)
|
std::string &lstrip(std::string &line)
|
||||||
|
|
@ -72,57 +91,48 @@ bool readline(std::istream &f, std::string &output)
|
||||||
return std::getline(f, output) && !output.empty();
|
return std::getline(f, output) && !output.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string expdup(const char *str)
|
std::string expdup(std::string_view str)
|
||||||
{
|
{
|
||||||
std::string output;
|
std::string output;
|
||||||
|
|
||||||
std::string envval;
|
while(!str.empty())
|
||||||
while(*str != '\0')
|
|
||||||
{
|
{
|
||||||
const char *addstr;
|
if(auto nextpos = str.find('$'))
|
||||||
size_t addstrlen;
|
|
||||||
|
|
||||||
if(str[0] != '$')
|
|
||||||
{
|
{
|
||||||
const char *next = std::strchr(str, '$');
|
output += str.substr(0, nextpos);
|
||||||
addstr = str;
|
if(nextpos == std::string_view::npos)
|
||||||
addstrlen = next ? static_cast<size_t>(next-str) : std::strlen(str);
|
break;
|
||||||
|
|
||||||
str += addstrlen;
|
str.remove_prefix(nextpos);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
str.remove_prefix(1);
|
||||||
|
if(str.empty())
|
||||||
{
|
{
|
||||||
str++;
|
output += '$';
|
||||||
if(*str == '$')
|
break;
|
||||||
{
|
|
||||||
const char *next = std::strchr(str+1, '$');
|
|
||||||
addstr = str;
|
|
||||||
addstrlen = next ? static_cast<size_t>(next-str) : std::strlen(str);
|
|
||||||
|
|
||||||
str += addstrlen;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const bool hasbraces{(*str == '{')};
|
|
||||||
|
|
||||||
if(hasbraces) str++;
|
|
||||||
const char *envstart = str;
|
|
||||||
while(std::isalnum(*str) || *str == '_')
|
|
||||||
++str;
|
|
||||||
if(hasbraces && *str != '}')
|
|
||||||
continue;
|
|
||||||
const std::string envname{envstart, str};
|
|
||||||
if(hasbraces) str++;
|
|
||||||
|
|
||||||
envval = al::getenv(envname.c_str()).value_or(std::string{});
|
|
||||||
addstr = envval.data();
|
|
||||||
addstrlen = envval.length();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if(addstrlen == 0)
|
if(str.front() == '$')
|
||||||
|
{
|
||||||
|
output += '$';
|
||||||
|
str.remove_prefix(1);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
output.append(addstr, addstrlen);
|
const bool hasbraces{str.front() == '{'};
|
||||||
|
if(hasbraces) str.remove_prefix(1);
|
||||||
|
|
||||||
|
size_t envend{0};
|
||||||
|
while(envend < str.size() && (std::isalnum(str[envend]) || str[envend] == '_'))
|
||||||
|
++envend;
|
||||||
|
if(hasbraces && (envend == str.size() || str[envend] != '}'))
|
||||||
|
continue;
|
||||||
|
const std::string envname{str.substr(0, envend)};
|
||||||
|
if(hasbraces) ++envend;
|
||||||
|
str.remove_prefix(envend);
|
||||||
|
|
||||||
|
if(auto envval = al::getenv(envname.c_str()))
|
||||||
|
output += *envval;
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
|
|
@ -140,44 +150,43 @@ void LoadConfigFromFile(std::istream &f)
|
||||||
|
|
||||||
if(buffer[0] == '[')
|
if(buffer[0] == '[')
|
||||||
{
|
{
|
||||||
auto line = const_cast<char*>(buffer.data());
|
auto endpos = buffer.find(']', 1);
|
||||||
char *section = line+1;
|
if(endpos == 1 || endpos == std::string::npos)
|
||||||
char *endsection;
|
|
||||||
|
|
||||||
endsection = std::strchr(section, ']');
|
|
||||||
if(!endsection || section == endsection)
|
|
||||||
{
|
{
|
||||||
ERR(" config parse error: bad line \"%s\"\n", line);
|
ERR(" config parse error: bad line \"%s\"\n", buffer.c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if(endsection[1] != 0)
|
if(buffer[endpos+1] != '\0')
|
||||||
{
|
{
|
||||||
char *end = endsection+1;
|
size_t last{endpos+1};
|
||||||
while(std::isspace(*end))
|
while(last < buffer.size() && std::isspace(buffer[last]))
|
||||||
++end;
|
++last;
|
||||||
if(*end != 0 && *end != '#')
|
|
||||||
|
if(last < buffer.size() && buffer[last] != '#')
|
||||||
{
|
{
|
||||||
ERR(" config parse error: bad line \"%s\"\n", line);
|
ERR(" config parse error: bad line \"%s\"\n", buffer.c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*endsection = 0;
|
|
||||||
|
auto section = std::string_view{buffer}.substr(1, endpos-1);
|
||||||
|
|
||||||
curSection.clear();
|
curSection.clear();
|
||||||
if(al::strcasecmp(section, "general") != 0)
|
if(al::case_compare(section, "general"sv) != 0)
|
||||||
{
|
{
|
||||||
do {
|
do {
|
||||||
char *nextp = std::strchr(section, '%');
|
auto nextp = section.find('%');
|
||||||
if(!nextp)
|
if(nextp == std::string_view::npos)
|
||||||
{
|
{
|
||||||
curSection += section;
|
curSection += section;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
curSection.append(section, nextp);
|
curSection += section.substr(0, nextp);
|
||||||
section = nextp;
|
section.remove_prefix(nextp);
|
||||||
|
|
||||||
if(((section[1] >= '0' && section[1] <= '9') ||
|
if(section.size() > 2 &&
|
||||||
|
((section[1] >= '0' && section[1] <= '9') ||
|
||||||
(section[1] >= 'a' && section[1] <= 'f') ||
|
(section[1] >= 'a' && section[1] <= 'f') ||
|
||||||
(section[1] >= 'A' && section[1] <= 'F')) &&
|
(section[1] >= 'A' && section[1] <= 'F')) &&
|
||||||
((section[2] >= '0' && section[2] <= '9') ||
|
((section[2] >= '0' && section[2] <= '9') ||
|
||||||
|
|
@ -198,19 +207,19 @@ void LoadConfigFromFile(std::istream &f)
|
||||||
else if(section[2] >= 'A' && section[2] <= 'F')
|
else if(section[2] >= 'A' && section[2] <= 'F')
|
||||||
b |= (section[2]-'A'+0x0a);
|
b |= (section[2]-'A'+0x0a);
|
||||||
curSection += static_cast<char>(b);
|
curSection += static_cast<char>(b);
|
||||||
section += 3;
|
section.remove_prefix(3);
|
||||||
}
|
}
|
||||||
else if(section[1] == '%')
|
else if(section.size() > 1 && section[1] == '%')
|
||||||
{
|
{
|
||||||
curSection += '%';
|
curSection += '%';
|
||||||
section += 2;
|
section.remove_prefix(2);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
curSection += '%';
|
curSection += '%';
|
||||||
section += 1;
|
section.remove_prefix(1);
|
||||||
}
|
}
|
||||||
} while(*section != 0);
|
} while(!section.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -228,16 +237,17 @@ void LoadConfigFromFile(std::istream &f)
|
||||||
ERR(" config parse error: malformed option line: \"%s\"\n", buffer.c_str());
|
ERR(" config parse error: malformed option line: \"%s\"\n", buffer.c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto keyend = sep++;
|
auto keypart = std::string_view{buffer}.substr(0, sep++);
|
||||||
while(keyend > 0 && std::isspace(buffer[keyend-1]))
|
while(!keypart.empty() && std::isspace(keypart.back()))
|
||||||
--keyend;
|
keypart.remove_suffix(1);
|
||||||
if(!keyend)
|
if(keypart.empty())
|
||||||
{
|
{
|
||||||
ERR(" config parse error: malformed option line: \"%s\"\n", buffer.c_str());
|
ERR(" config parse error: malformed option line: \"%s\"\n", buffer.c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
while(sep < buffer.size() && std::isspace(buffer[sep]))
|
auto valpart = std::string_view{buffer}.substr(sep);
|
||||||
sep++;
|
while(!valpart.empty() && std::isspace(valpart.front()))
|
||||||
|
valpart.remove_prefix(1);
|
||||||
|
|
||||||
std::string fullKey;
|
std::string fullKey;
|
||||||
if(!curSection.empty())
|
if(!curSection.empty())
|
||||||
|
|
@ -245,20 +255,24 @@ void LoadConfigFromFile(std::istream &f)
|
||||||
fullKey += curSection;
|
fullKey += curSection;
|
||||||
fullKey += '/';
|
fullKey += '/';
|
||||||
}
|
}
|
||||||
fullKey += buffer.substr(0u, keyend);
|
fullKey += keypart;
|
||||||
|
|
||||||
std::string value{(sep < buffer.size()) ? buffer.substr(sep) : std::string{}};
|
if(valpart.size() > size_t{std::numeric_limits<int>::max()})
|
||||||
if(value.size() > 1)
|
|
||||||
{
|
{
|
||||||
if((value.front() == '"' && value.back() == '"')
|
ERR(" config parse error: value too long in line \"%s\"\n", buffer.c_str());
|
||||||
|| (value.front() == '\'' && value.back() == '\''))
|
continue;
|
||||||
|
}
|
||||||
|
if(valpart.size() > 1)
|
||||||
|
{
|
||||||
|
if((valpart.front() == '"' && valpart.back() == '"')
|
||||||
|
|| (valpart.front() == '\'' && valpart.back() == '\''))
|
||||||
{
|
{
|
||||||
value.pop_back();
|
valpart.remove_prefix(1);
|
||||||
value.erase(value.begin());
|
valpart.remove_suffix(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE(" found '%s' = '%s'\n", fullKey.c_str(), value.c_str());
|
TRACE(" setting '%s' = '%.*s'\n", fullKey.c_str(), al::sizei(valpart), valpart.data());
|
||||||
|
|
||||||
/* Check if we already have this option set */
|
/* Check if we already have this option set */
|
||||||
auto find_key = [&fullKey](const ConfigEntry &entry) -> bool
|
auto find_key = [&fullKey](const ConfigEntry &entry) -> bool
|
||||||
|
|
@ -266,61 +280,49 @@ void LoadConfigFromFile(std::istream &f)
|
||||||
auto ent = std::find_if(ConfOpts.begin(), ConfOpts.end(), find_key);
|
auto ent = std::find_if(ConfOpts.begin(), ConfOpts.end(), find_key);
|
||||||
if(ent != ConfOpts.end())
|
if(ent != ConfOpts.end())
|
||||||
{
|
{
|
||||||
if(!value.empty())
|
if(!valpart.empty())
|
||||||
ent->value = expdup(value.c_str());
|
ent->value = expdup(valpart);
|
||||||
else
|
else
|
||||||
ConfOpts.erase(ent);
|
ConfOpts.erase(ent);
|
||||||
}
|
}
|
||||||
else if(!value.empty())
|
else if(!valpart.empty())
|
||||||
ConfOpts.emplace_back(ConfigEntry{std::move(fullKey), expdup(value.c_str())});
|
ConfOpts.emplace_back(ConfigEntry{std::move(fullKey), expdup(valpart)});
|
||||||
}
|
}
|
||||||
ConfOpts.shrink_to_fit();
|
ConfOpts.shrink_to_fit();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName)
|
const char *GetConfigValue(const std::string_view devName, const std::string_view blockName,
|
||||||
|
const std::string_view keyName)
|
||||||
{
|
{
|
||||||
if(!keyName)
|
if(keyName.empty())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
std::string key;
|
std::string key;
|
||||||
if(blockName && al::strcasecmp(blockName, "general") != 0)
|
if(!blockName.empty() && al::case_compare(blockName, "general"sv) != 0)
|
||||||
{
|
{
|
||||||
key = blockName;
|
key = blockName;
|
||||||
if(devName)
|
|
||||||
{
|
|
||||||
key += '/';
|
|
||||||
key += devName;
|
|
||||||
}
|
|
||||||
key += '/';
|
key += '/';
|
||||||
key += keyName;
|
|
||||||
}
|
}
|
||||||
else
|
if(!devName.empty())
|
||||||
{
|
{
|
||||||
if(devName)
|
key += devName;
|
||||||
{
|
key += '/';
|
||||||
key = devName;
|
|
||||||
key += '/';
|
|
||||||
}
|
|
||||||
key += keyName;
|
|
||||||
}
|
}
|
||||||
|
key += keyName;
|
||||||
|
|
||||||
auto iter = std::find_if(ConfOpts.cbegin(), ConfOpts.cend(),
|
auto iter = std::find_if(ConfOpts.cbegin(), ConfOpts.cend(),
|
||||||
[&key](const ConfigEntry &entry) -> bool
|
[&key](const ConfigEntry &entry) -> bool { return entry.key == key; });
|
||||||
{ return entry.key == key; });
|
|
||||||
if(iter != ConfOpts.cend())
|
if(iter != ConfOpts.cend())
|
||||||
{
|
{
|
||||||
TRACE("Found %s = \"%s\"\n", key.c_str(), iter->value.c_str());
|
TRACE("Found option %s = \"%s\"\n", key.c_str(), iter->value.c_str());
|
||||||
if(!iter->value.empty())
|
if(!iter->value.empty())
|
||||||
return iter->value.c_str();
|
return iter->value.c_str();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!devName)
|
if(devName.empty())
|
||||||
{
|
|
||||||
TRACE("Key %s not found\n", key.c_str());
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
return GetConfigValue({}, blockName, keyName);
|
||||||
return GetConfigValue(nullptr, blockName, keyName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
@ -329,33 +331,48 @@ const char *GetConfigValue(const char *devName, const char *blockName, const cha
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
void ReadALConfig()
|
void ReadALConfig()
|
||||||
{
|
{
|
||||||
WCHAR buffer[MAX_PATH];
|
namespace fs = std::filesystem;
|
||||||
if(SHGetSpecialFolderPathW(nullptr, buffer, CSIDL_APPDATA, FALSE) != FALSE)
|
fs::path path;
|
||||||
{
|
|
||||||
std::string filepath{wstr_to_utf8(buffer)};
|
|
||||||
filepath += "\\alsoft.ini";
|
|
||||||
|
|
||||||
TRACE("Loading config %s...\n", filepath.c_str());
|
#if !defined(_GAMING_XBOX)
|
||||||
al::ifstream f{filepath};
|
{
|
||||||
if(f.is_open())
|
#if !defined(ALSOFT_UWP)
|
||||||
LoadConfigFromFile(f);
|
std::unique_ptr<WCHAR,CoTaskMemDeleter> bufstore;
|
||||||
|
const HRESULT hr{SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DONT_UNEXPAND,
|
||||||
|
nullptr, al::out_ptr(bufstore))};
|
||||||
|
if(SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
const std::wstring_view buffer{bufstore.get()};
|
||||||
|
#else
|
||||||
|
winrt::Windows::Storage::ApplicationDataContainer localSettings = winrt::Windows::Storage::ApplicationData::Current().LocalSettings();
|
||||||
|
auto bufstore = Windows::Storage::ApplicationData::Current().RoamingFolder().Path();
|
||||||
|
std::wstring_view buffer{bufstore};
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
path = fs::path{buffer};
|
||||||
|
path /= L"alsoft.ini";
|
||||||
|
|
||||||
|
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||||
|
if(std::ifstream f{path}; f.is_open())
|
||||||
|
LoadConfigFromFile(f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
std::string ppath{GetProcBinary().path};
|
path = fs::u8path(GetProcBinary().path);
|
||||||
if(!ppath.empty())
|
if(!path.empty())
|
||||||
{
|
{
|
||||||
ppath += "\\alsoft.ini";
|
path /= "alsoft.ini";
|
||||||
TRACE("Loading config %s...\n", ppath.c_str());
|
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||||
al::ifstream f{ppath};
|
if(std::ifstream f{path}; f.is_open())
|
||||||
if(f.is_open())
|
|
||||||
LoadConfigFromFile(f);
|
LoadConfigFromFile(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(auto confpath = al::getenv(L"ALSOFT_CONF"))
|
if(auto confpath = al::getenv(L"ALSOFT_CONF"))
|
||||||
{
|
{
|
||||||
TRACE("Loading config %s...\n", wstr_to_utf8(confpath->c_str()).c_str());
|
path = *confpath;
|
||||||
al::ifstream f{*confpath};
|
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||||
if(f.is_open())
|
if(std::ifstream f{path}; f.is_open())
|
||||||
LoadConfigFromFile(f);
|
LoadConfigFromFile(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -364,13 +381,12 @@ void ReadALConfig()
|
||||||
|
|
||||||
void ReadALConfig()
|
void ReadALConfig()
|
||||||
{
|
{
|
||||||
const char *str{"/etc/openal/alsoft.conf"};
|
namespace fs = std::filesystem;
|
||||||
|
fs::path path{"/etc/openal/alsoft.conf"};
|
||||||
|
|
||||||
TRACE("Loading config %s...\n", str);
|
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||||
al::ifstream f{str};
|
if(std::ifstream f{path}; f.is_open())
|
||||||
if(f.is_open())
|
|
||||||
LoadConfigFromFile(f);
|
LoadConfigFromFile(f);
|
||||||
f.close();
|
|
||||||
|
|
||||||
std::string confpaths{al::getenv("XDG_CONFIG_DIRS").value_or("/etc/xdg")};
|
std::string confpaths{al::getenv("XDG_CONFIG_DIRS").value_or("/etc/xdg")};
|
||||||
/* Go through the list in reverse, since "the order of base directories
|
/* Go through the list in reverse, since "the order of base directories
|
||||||
|
|
@ -378,48 +394,43 @@ void ReadALConfig()
|
||||||
* important". Ergo, we need to load the settings from the later dirs
|
* important". Ergo, we need to load the settings from the later dirs
|
||||||
* first so that the settings in the earlier dirs override them.
|
* first so that the settings in the earlier dirs override them.
|
||||||
*/
|
*/
|
||||||
std::string fname;
|
|
||||||
while(!confpaths.empty())
|
while(!confpaths.empty())
|
||||||
{
|
{
|
||||||
auto next = confpaths.find_last_of(':');
|
auto next = confpaths.rfind(':');
|
||||||
if(next < confpaths.length())
|
if(next < confpaths.length())
|
||||||
{
|
{
|
||||||
fname = confpaths.substr(next+1);
|
path = fs::path{std::string_view{confpaths}.substr(next+1)}.lexically_normal();
|
||||||
confpaths.erase(next);
|
confpaths.erase(next);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fname = confpaths;
|
path = fs::path{confpaths}.lexically_normal();
|
||||||
confpaths.clear();
|
confpaths.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(fname.empty() || fname.front() != '/')
|
if(!path.is_absolute())
|
||||||
WARN("Ignoring XDG config dir: %s\n", fname.c_str());
|
WARN("Ignoring XDG config dir: %s\n", path.u8string().c_str());
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(fname.back() != '/') fname += "/alsoft.conf";
|
path /= "alsoft.conf";
|
||||||
else fname += "alsoft.conf";
|
|
||||||
|
|
||||||
TRACE("Loading config %s...\n", fname.c_str());
|
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||||
f = al::ifstream{fname};
|
if(std::ifstream f{path}; f.is_open())
|
||||||
if(f.is_open())
|
|
||||||
LoadConfigFromFile(f);
|
LoadConfigFromFile(f);
|
||||||
}
|
}
|
||||||
fname.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
CFBundleRef mainBundle = CFBundleGetMainBundle();
|
CFBundleRef mainBundle = CFBundleGetMainBundle();
|
||||||
if(mainBundle)
|
if(mainBundle)
|
||||||
{
|
{
|
||||||
unsigned char fileName[PATH_MAX];
|
CFURLRef configURL{CFBundleCopyResourceURL(mainBundle, CFSTR(".alsoftrc"), CFSTR(""),
|
||||||
CFURLRef configURL;
|
nullptr)};
|
||||||
|
|
||||||
if((configURL=CFBundleCopyResourceURL(mainBundle, CFSTR(".alsoftrc"), CFSTR(""), nullptr)) &&
|
std::array<unsigned char,PATH_MAX> fileName{};
|
||||||
CFURLGetFileSystemRepresentation(configURL, true, fileName, sizeof(fileName)))
|
if(configURL && CFURLGetFileSystemRepresentation(configURL, true, fileName.data(), fileName.size()))
|
||||||
{
|
{
|
||||||
f = al::ifstream{reinterpret_cast<char*>(fileName)};
|
if(std::ifstream f{reinterpret_cast<char*>(fileName.data())}; f.is_open())
|
||||||
if(f.is_open())
|
|
||||||
LoadConfigFromFile(f);
|
LoadConfigFromFile(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -427,102 +438,100 @@ void ReadALConfig()
|
||||||
|
|
||||||
if(auto homedir = al::getenv("HOME"))
|
if(auto homedir = al::getenv("HOME"))
|
||||||
{
|
{
|
||||||
fname = *homedir;
|
path = *homedir;
|
||||||
if(fname.back() != '/') fname += "/.alsoftrc";
|
path /= ".alsoftrc";
|
||||||
else fname += ".alsoftrc";
|
|
||||||
|
|
||||||
TRACE("Loading config %s...\n", fname.c_str());
|
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||||
f = al::ifstream{fname};
|
if(std::ifstream f{path}; f.is_open())
|
||||||
if(f.is_open())
|
|
||||||
LoadConfigFromFile(f);
|
LoadConfigFromFile(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(auto configdir = al::getenv("XDG_CONFIG_HOME"))
|
if(auto configdir = al::getenv("XDG_CONFIG_HOME"))
|
||||||
{
|
{
|
||||||
fname = *configdir;
|
path = *configdir;
|
||||||
if(fname.back() != '/') fname += "/alsoft.conf";
|
path /= "alsoft.conf";
|
||||||
else fname += "alsoft.conf";
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fname.clear();
|
path.clear();
|
||||||
if(auto homedir = al::getenv("HOME"))
|
if(auto homedir = al::getenv("HOME"))
|
||||||
{
|
{
|
||||||
fname = *homedir;
|
path = *homedir;
|
||||||
if(fname.back() != '/') fname += "/.config/alsoft.conf";
|
path /= ".config/alsoft.conf";
|
||||||
else fname += ".config/alsoft.conf";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!fname.empty())
|
if(!path.empty())
|
||||||
{
|
{
|
||||||
TRACE("Loading config %s...\n", fname.c_str());
|
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||||
f = al::ifstream{fname};
|
if(std::ifstream f{path}; f.is_open())
|
||||||
if(f.is_open())
|
|
||||||
LoadConfigFromFile(f);
|
LoadConfigFromFile(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ppath{GetProcBinary().path};
|
path = GetProcBinary().path;
|
||||||
if(!ppath.empty())
|
if(!path.empty())
|
||||||
{
|
{
|
||||||
if(ppath.back() != '/') ppath += "/alsoft.conf";
|
path /= "alsoft.conf";
|
||||||
else ppath += "alsoft.conf";
|
|
||||||
|
|
||||||
TRACE("Loading config %s...\n", ppath.c_str());
|
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||||
f = al::ifstream{ppath};
|
if(std::ifstream f{path}; f.is_open())
|
||||||
if(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 %s...\n", confname->c_str());
|
||||||
f = al::ifstream{*confname};
|
if(std::ifstream f{*confname}; f.is_open())
|
||||||
if(f.is_open())
|
|
||||||
LoadConfigFromFile(f);
|
LoadConfigFromFile(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
al::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName)
|
std::optional<std::string> ConfigValueStr(const std::string_view devName,
|
||||||
|
const std::string_view blockName, const std::string_view keyName)
|
||||||
{
|
{
|
||||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||||
return val;
|
return val;
|
||||||
return al::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
al::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName)
|
std::optional<int> ConfigValueInt(const std::string_view devName, const std::string_view blockName,
|
||||||
|
const std::string_view keyName)
|
||||||
{
|
{
|
||||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||||
return static_cast<int>(std::strtol(val, nullptr, 0));
|
return static_cast<int>(std::strtol(val, nullptr, 0));
|
||||||
return al::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
al::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName)
|
std::optional<unsigned int> ConfigValueUInt(const std::string_view devName,
|
||||||
|
const std::string_view blockName, const std::string_view keyName)
|
||||||
{
|
{
|
||||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||||
return static_cast<unsigned int>(std::strtoul(val, nullptr, 0));
|
return static_cast<unsigned int>(std::strtoul(val, nullptr, 0));
|
||||||
return al::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
al::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName)
|
std::optional<float> ConfigValueFloat(const std::string_view devName,
|
||||||
|
const std::string_view blockName, const std::string_view keyName)
|
||||||
{
|
{
|
||||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||||
return std::strtof(val, nullptr);
|
return std::strtof(val, nullptr);
|
||||||
return al::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
al::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName)
|
std::optional<bool> ConfigValueBool(const std::string_view devName,
|
||||||
|
const std::string_view blockName, const std::string_view keyName)
|
||||||
{
|
{
|
||||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||||
return al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
|
return al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
|
||||||
|| al::strcasecmp(val, "true")==0 || atoi(val) != 0;
|
|| al::strcasecmp(val, "true") == 0 || atoi(val) != 0;
|
||||||
return al::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, bool def)
|
bool GetConfigValueBool(const std::string_view devName, const std::string_view blockName,
|
||||||
|
const std::string_view keyName, bool def)
|
||||||
{
|
{
|
||||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||||
return (al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
|
return al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
|
||||||
|| al::strcasecmp(val, "true") == 0 || atoi(val) != 0);
|
|| al::strcasecmp(val, "true") == 0 || atoi(val) != 0;
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,25 @@
|
||||||
#ifndef ALCONFIG_H
|
#ifndef ALCONFIG_H
|
||||||
#define ALCONFIG_H
|
#define ALCONFIG_H
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
#include "aloptional.h"
|
|
||||||
|
|
||||||
void ReadALConfig();
|
void ReadALConfig();
|
||||||
|
|
||||||
bool GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, bool def);
|
bool GetConfigValueBool(const std::string_view devName, const std::string_view blockName,
|
||||||
|
const std::string_view keyName, bool def);
|
||||||
|
|
||||||
al::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName);
|
std::optional<std::string> ConfigValueStr(const std::string_view devName,
|
||||||
al::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName);
|
const std::string_view blockName, const std::string_view keyName);
|
||||||
al::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName);
|
std::optional<int> ConfigValueInt(const std::string_view devName, const std::string_view blockName,
|
||||||
al::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName);
|
const std::string_view keyName);
|
||||||
al::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName);
|
std::optional<unsigned int> ConfigValueUInt(const std::string_view devName,
|
||||||
|
const std::string_view blockName, const std::string_view keyName);
|
||||||
|
std::optional<float> ConfigValueFloat(const std::string_view devName,
|
||||||
|
const std::string_view blockName, const std::string_view keyName);
|
||||||
|
std::optional<bool> ConfigValueBool(const std::string_view devName,
|
||||||
|
const std::string_view blockName, const std::string_view keyName);
|
||||||
|
|
||||||
#endif /* ALCONFIG_H */
|
#endif /* ALCONFIG_H */
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -2,20 +2,20 @@
|
||||||
#define ALU_H
|
#define ALU_H
|
||||||
|
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
|
#include <cstdint>
|
||||||
#include "aloptional.h"
|
#include <optional>
|
||||||
|
|
||||||
struct ALCcontext;
|
struct ALCcontext;
|
||||||
struct ALCdevice;
|
struct ALCdevice;
|
||||||
struct EffectSlot;
|
struct EffectSlot;
|
||||||
|
|
||||||
enum class StereoEncoding : unsigned char;
|
enum class StereoEncoding : std::uint8_t;
|
||||||
|
|
||||||
|
|
||||||
constexpr float GainMixMax{1000.0f}; /* +60dB */
|
constexpr float GainMixMax{1000.0f}; /* +60dB */
|
||||||
|
|
||||||
|
|
||||||
enum CompatFlags : uint8_t {
|
enum CompatFlags : std::uint8_t {
|
||||||
ReverseX,
|
ReverseX,
|
||||||
ReverseY,
|
ReverseY,
|
||||||
ReverseZ,
|
ReverseZ,
|
||||||
|
|
@ -31,7 +31,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, al::optional<StereoEncoding> stereomode);
|
void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncoding> stereomode);
|
||||||
|
|
||||||
void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context);
|
void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,29 +31,33 @@
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "albyte.h"
|
#include "albit.h"
|
||||||
#include "alc/alconfig.h"
|
#include "alc/alconfig.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "aloptional.h"
|
#include "alstring.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 "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
#include "threads.h"
|
|
||||||
#include "vector.h"
|
|
||||||
|
|
||||||
#include <alsa/asoundlib.h>
|
#include <alsa/asoundlib.h>
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr char alsaDevice[] = "ALSA Default";
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "ALSA Default"sv; }
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAVE_DYNLOAD
|
#ifdef HAVE_DYNLOAD
|
||||||
|
|
@ -248,59 +252,97 @@ struct DevMap {
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
al::vector<DevMap> PlaybackDevices;
|
std::vector<DevMap> PlaybackDevices;
|
||||||
al::vector<DevMap> CaptureDevices;
|
std::vector<DevMap> CaptureDevices;
|
||||||
|
|
||||||
|
|
||||||
const char *prefix_name(snd_pcm_stream_t stream)
|
std::string_view prefix_name(snd_pcm_stream_t stream) noexcept
|
||||||
{
|
{
|
||||||
assert(stream == SND_PCM_STREAM_PLAYBACK || stream == SND_PCM_STREAM_CAPTURE);
|
if(stream == SND_PCM_STREAM_PLAYBACK)
|
||||||
return (stream==SND_PCM_STREAM_PLAYBACK) ? "device-prefix" : "capture-prefix";
|
return "device-prefix"sv;
|
||||||
|
return "capture-prefix"sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
struct SndCtlCardInfo {
|
||||||
|
snd_ctl_card_info_t *mInfo{};
|
||||||
|
|
||||||
|
SndCtlCardInfo() { snd_ctl_card_info_malloc(&mInfo); }
|
||||||
|
~SndCtlCardInfo() { if(mInfo) snd_ctl_card_info_free(mInfo); }
|
||||||
|
SndCtlCardInfo(const SndCtlCardInfo&) = delete;
|
||||||
|
SndCtlCardInfo& operator=(const SndCtlCardInfo&) = delete;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
operator snd_ctl_card_info_t*() const noexcept { return mInfo; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SndPcmInfo {
|
||||||
|
snd_pcm_info_t *mInfo{};
|
||||||
|
|
||||||
|
SndPcmInfo() { snd_pcm_info_malloc(&mInfo); }
|
||||||
|
~SndPcmInfo() { if(mInfo) snd_pcm_info_free(mInfo); }
|
||||||
|
SndPcmInfo(const SndPcmInfo&) = delete;
|
||||||
|
SndPcmInfo& operator=(const SndPcmInfo&) = delete;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
operator snd_pcm_info_t*() const noexcept { return mInfo; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SndCtl {
|
||||||
|
snd_ctl_t *mHandle{};
|
||||||
|
|
||||||
|
SndCtl() = default;
|
||||||
|
~SndCtl() { if(mHandle) snd_ctl_close(mHandle); }
|
||||||
|
SndCtl(const SndCtl&) = delete;
|
||||||
|
SndCtl& operator=(const SndCtl&) = delete;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
auto open(const char *name, int mode) { return snd_ctl_open(&mHandle, name, mode); }
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
operator snd_ctl_t*() const noexcept { return mHandle; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
||||||
{
|
{
|
||||||
al::vector<DevMap> devlist;
|
std::vector<DevMap> devlist;
|
||||||
|
|
||||||
snd_ctl_card_info_t *info;
|
SndCtlCardInfo info;
|
||||||
snd_ctl_card_info_malloc(&info);
|
SndPcmInfo pcminfo;
|
||||||
snd_pcm_info_t *pcminfo;
|
|
||||||
snd_pcm_info_malloc(&pcminfo);
|
|
||||||
|
|
||||||
auto defname = ConfigValueStr(nullptr, "alsa",
|
auto defname = ConfigValueStr({}, "alsa"sv,
|
||||||
(stream == SND_PCM_STREAM_PLAYBACK) ? "device" : "capture");
|
(stream == SND_PCM_STREAM_PLAYBACK) ? "device"sv : "capture"sv);
|
||||||
devlist.emplace_back(alsaDevice, defname ? defname->c_str() : "default");
|
devlist.emplace_back(GetDefaultName(), defname ? std::string_view{*defname} : "default"sv);
|
||||||
|
|
||||||
if(auto customdevs = ConfigValueStr(nullptr, "alsa",
|
if(auto customdevs = ConfigValueStr({}, "alsa"sv,
|
||||||
(stream == SND_PCM_STREAM_PLAYBACK) ? "custom-devices" : "custom-captures"))
|
(stream == SND_PCM_STREAM_PLAYBACK) ? "custom-devices"sv : "custom-captures"sv))
|
||||||
{
|
{
|
||||||
size_t nextpos{customdevs->find_first_not_of(';')};
|
size_t curpos{customdevs->find_first_not_of(';')};
|
||||||
size_t curpos;
|
while(curpos < customdevs->length())
|
||||||
while((curpos=nextpos) < customdevs->length())
|
|
||||||
{
|
{
|
||||||
nextpos = customdevs->find_first_of(';', curpos+1);
|
size_t nextpos{customdevs->find(';', curpos+1)};
|
||||||
|
const size_t seppos{customdevs->find('=', curpos)};
|
||||||
size_t seppos{customdevs->find_first_of('=', curpos)};
|
|
||||||
if(seppos == curpos || seppos >= nextpos)
|
if(seppos == curpos || seppos >= nextpos)
|
||||||
{
|
{
|
||||||
std::string spec{customdevs->substr(curpos, nextpos-curpos)};
|
const std::string spec{customdevs->substr(curpos, nextpos-curpos)};
|
||||||
ERR("Invalid ALSA device specification \"%s\"\n", spec.c_str());
|
ERR("Invalid ALSA device specification \"%s\"\n", spec.c_str());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
devlist.emplace_back(customdevs->substr(curpos, seppos-curpos),
|
const std::string_view strview{*customdevs};
|
||||||
customdevs->substr(seppos+1, nextpos-seppos-1));
|
const auto &entry = devlist.emplace_back(strview.substr(curpos, seppos-curpos),
|
||||||
const auto &entry = devlist.back();
|
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 \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(nextpos < customdevs->length())
|
if(nextpos < customdevs->length())
|
||||||
nextpos = customdevs->find_first_not_of(';', nextpos+1);
|
nextpos = customdevs->find_first_not_of(';', nextpos+1);
|
||||||
|
curpos = nextpos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string main_prefix{
|
const std::string main_prefix{ConfigValueStr({}, "alsa"sv, prefix_name(stream))
|
||||||
ConfigValueStr(nullptr, "alsa", prefix_name(stream)).value_or("plughw:")};
|
.value_or("plughw:")};
|
||||||
|
|
||||||
int card{-1};
|
int card{-1};
|
||||||
int err{snd_card_next(&card)};
|
int err{snd_card_next(&card)};
|
||||||
|
|
@ -308,16 +350,17 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
||||||
{
|
{
|
||||||
std::string name{"hw:" + std::to_string(card)};
|
std::string name{"hw:" + std::to_string(card)};
|
||||||
|
|
||||||
snd_ctl_t *handle;
|
SndCtl handle;
|
||||||
if((err=snd_ctl_open(&handle, name.c_str(), 0)) < 0)
|
err = handle.open(name.c_str(), 0);
|
||||||
|
if(err < 0)
|
||||||
{
|
{
|
||||||
ERR("control open (hw:%d): %s\n", card, snd_strerror(err));
|
ERR("control open (hw:%d): %s\n", card, snd_strerror(err));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if((err=snd_ctl_card_info(handle, info)) < 0)
|
err = snd_ctl_card_info(handle, info);
|
||||||
|
if(err < 0)
|
||||||
{
|
{
|
||||||
ERR("control hardware info (hw:%d): %s\n", card, snd_strerror(err));
|
ERR("control hardware info (hw:%d): %s\n", card, snd_strerror(err));
|
||||||
snd_ctl_close(handle);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -326,8 +369,7 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
||||||
name = prefix_name(stream);
|
name = prefix_name(stream);
|
||||||
name += '-';
|
name += '-';
|
||||||
name += cardid;
|
name += cardid;
|
||||||
const std::string card_prefix{
|
const std::string card_prefix{ConfigValueStr({}, "alsa"sv, name).value_or(main_prefix)};
|
||||||
ConfigValueStr(nullptr, "alsa", name.c_str()).value_or(main_prefix)};
|
|
||||||
|
|
||||||
int dev{-1};
|
int dev{-1};
|
||||||
while(true)
|
while(true)
|
||||||
|
|
@ -339,7 +381,8 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
||||||
snd_pcm_info_set_device(pcminfo, static_cast<uint>(dev));
|
snd_pcm_info_set_device(pcminfo, static_cast<uint>(dev));
|
||||||
snd_pcm_info_set_subdevice(pcminfo, 0);
|
snd_pcm_info_set_subdevice(pcminfo, 0);
|
||||||
snd_pcm_info_set_stream(pcminfo, stream);
|
snd_pcm_info_set_stream(pcminfo, stream);
|
||||||
if((err=snd_ctl_pcm_info(handle, pcminfo)) < 0)
|
err = snd_ctl_pcm_info(handle, pcminfo);
|
||||||
|
if(err < 0)
|
||||||
{
|
{
|
||||||
if(err != -ENOENT)
|
if(err != -ENOENT)
|
||||||
ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err));
|
ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err));
|
||||||
|
|
@ -352,8 +395,8 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
||||||
name += cardid;
|
name += cardid;
|
||||||
name += '-';
|
name += '-';
|
||||||
name += std::to_string(dev);
|
name += std::to_string(dev);
|
||||||
const std::string device_prefix{
|
const std::string device_prefix{ConfigValueStr({}, "alsa"sv, name)
|
||||||
ConfigValueStr(nullptr, "alsa", name.c_str()).value_or(card_prefix)};
|
.value_or(card_prefix)};
|
||||||
|
|
||||||
/* "CardName, PcmName (CARD=cardid,DEV=dev)" */
|
/* "CardName, PcmName (CARD=cardid,DEV=dev)" */
|
||||||
name = cardname;
|
name = cardname;
|
||||||
|
|
@ -372,18 +415,13 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
||||||
device += ",DEV=";
|
device += ",DEV=";
|
||||||
device += std::to_string(dev);
|
device += std::to_string(dev);
|
||||||
|
|
||||||
devlist.emplace_back(std::move(name), std::move(device));
|
const auto &entry = devlist.emplace_back(std::move(name), std::move(device));
|
||||||
const auto &entry = devlist.back();
|
|
||||||
TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
|
TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
|
||||||
}
|
}
|
||||||
snd_ctl_close(handle);
|
|
||||||
}
|
}
|
||||||
if(err < 0)
|
if(err < 0)
|
||||||
ERR("snd_card_next failed: %s\n", snd_strerror(err));
|
ERR("snd_card_next failed: %s\n", snd_strerror(err));
|
||||||
|
|
||||||
snd_pcm_info_free(pcminfo);
|
|
||||||
snd_ctl_card_info_free(info);
|
|
||||||
|
|
||||||
return devlist;
|
return devlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -392,7 +430,6 @@ int verify_state(snd_pcm_t *handle)
|
||||||
{
|
{
|
||||||
snd_pcm_state_t state{snd_pcm_state(handle)};
|
snd_pcm_state_t state{snd_pcm_state(handle)};
|
||||||
|
|
||||||
int err;
|
|
||||||
switch(state)
|
switch(state)
|
||||||
{
|
{
|
||||||
case SND_PCM_STATE_OPEN:
|
case SND_PCM_STATE_OPEN:
|
||||||
|
|
@ -405,15 +442,27 @@ int verify_state(snd_pcm_t *handle)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_STATE_XRUN:
|
case SND_PCM_STATE_XRUN:
|
||||||
if((err=snd_pcm_recover(handle, -EPIPE, 1)) < 0)
|
if(int err{snd_pcm_recover(handle, -EPIPE, 1)}; err < 0)
|
||||||
return err;
|
return err;
|
||||||
break;
|
break;
|
||||||
case SND_PCM_STATE_SUSPENDED:
|
case SND_PCM_STATE_SUSPENDED:
|
||||||
if((err=snd_pcm_recover(handle, -ESTRPIPE, 1)) < 0)
|
if(int err{snd_pcm_recover(handle, -ESTRPIPE, 1)}; err < 0)
|
||||||
return err;
|
return err;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_STATE_DISCONNECTED:
|
case SND_PCM_STATE_DISCONNECTED:
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
/* ALSA headers have made this enum public, leaving us in a bind: use
|
||||||
|
* the enum despite being private and internal to the libasound, or
|
||||||
|
* ignore when an enum value isn't handled. We can't rely on it being
|
||||||
|
* declared either, since older headers don't have it and it could be
|
||||||
|
* removed in the future. We can't even really rely on its value, since
|
||||||
|
* being private/internal means it's subject to change, but this is the
|
||||||
|
* best we can do.
|
||||||
|
*/
|
||||||
|
case 1024 /*SND_PCM_STATE_PRIVATE1*/:
|
||||||
|
assert(state != 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
|
|
@ -427,7 +476,7 @@ struct AlsaPlayback final : public BackendBase {
|
||||||
int mixerProc();
|
int mixerProc();
|
||||||
int mixerNoMMapProc();
|
int mixerNoMMapProc();
|
||||||
|
|
||||||
void open(const char *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;
|
||||||
|
|
@ -439,12 +488,10 @@ struct AlsaPlayback final : public BackendBase {
|
||||||
std::mutex mMutex;
|
std::mutex mMutex;
|
||||||
|
|
||||||
uint mFrameStep{};
|
uint mFrameStep{};
|
||||||
al::vector<al::byte> mBuffer;
|
std::vector<std::byte> mBuffer;
|
||||||
|
|
||||||
std::atomic<bool> mKillNow{true};
|
std::atomic<bool> mKillNow{true};
|
||||||
std::thread mThread;
|
std::thread mThread;
|
||||||
|
|
||||||
DEF_NEWDEL(AlsaPlayback)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
AlsaPlayback::~AlsaPlayback()
|
AlsaPlayback::~AlsaPlayback()
|
||||||
|
|
@ -458,7 +505,7 @@ AlsaPlayback::~AlsaPlayback()
|
||||||
int AlsaPlayback::mixerProc()
|
int AlsaPlayback::mixerProc()
|
||||||
{
|
{
|
||||||
SetRTPriority();
|
SetRTPriority();
|
||||||
althrd_setname(MIXER_THREAD_NAME);
|
althrd_setname(GetMixerThreadName());
|
||||||
|
|
||||||
const snd_pcm_uframes_t update_size{mDevice->UpdateSize};
|
const snd_pcm_uframes_t update_size{mDevice->UpdateSize};
|
||||||
const snd_pcm_uframes_t buffer_size{mDevice->BufferSize};
|
const snd_pcm_uframes_t buffer_size{mDevice->BufferSize};
|
||||||
|
|
@ -506,7 +553,7 @@ int AlsaPlayback::mixerProc()
|
||||||
avail -= avail%update_size;
|
avail -= avail%update_size;
|
||||||
|
|
||||||
// it is possible that contiguous areas are smaller, thus we use a loop
|
// it is possible that contiguous areas are smaller, thus we use a loop
|
||||||
std::lock_guard<std::mutex> _{mMutex};
|
std::lock_guard<std::mutex> dlock{mMutex};
|
||||||
while(avail > 0)
|
while(avail > 0)
|
||||||
{
|
{
|
||||||
snd_pcm_uframes_t frames{avail};
|
snd_pcm_uframes_t frames{avail};
|
||||||
|
|
@ -520,6 +567,7 @@ int AlsaPlayback::mixerProc()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) */
|
||||||
char *WritePtr{static_cast<char*>(areas->addr) + (offset * areas->step / 8)};
|
char *WritePtr{static_cast<char*>(areas->addr) + (offset * areas->step / 8)};
|
||||||
mDevice->renderSamples(WritePtr, static_cast<uint>(frames), mFrameStep);
|
mDevice->renderSamples(WritePtr, static_cast<uint>(frames), mFrameStep);
|
||||||
|
|
||||||
|
|
@ -541,7 +589,7 @@ int AlsaPlayback::mixerProc()
|
||||||
int AlsaPlayback::mixerNoMMapProc()
|
int AlsaPlayback::mixerNoMMapProc()
|
||||||
{
|
{
|
||||||
SetRTPriority();
|
SetRTPriority();
|
||||||
althrd_setname(MIXER_THREAD_NAME);
|
althrd_setname(GetMixerThreadName());
|
||||||
|
|
||||||
const snd_pcm_uframes_t update_size{mDevice->UpdateSize};
|
const snd_pcm_uframes_t update_size{mDevice->UpdateSize};
|
||||||
const snd_pcm_uframes_t buffer_size{mDevice->BufferSize};
|
const snd_pcm_uframes_t buffer_size{mDevice->BufferSize};
|
||||||
|
|
@ -585,13 +633,13 @@ int AlsaPlayback::mixerNoMMapProc()
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
al::byte *WritePtr{mBuffer.data()};
|
auto WritePtr = mBuffer.begin();
|
||||||
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()));
|
||||||
std::lock_guard<std::mutex> _{mMutex};
|
std::lock_guard<std::mutex> dlock{mMutex};
|
||||||
mDevice->renderSamples(WritePtr, static_cast<uint>(avail), mFrameStep);
|
mDevice->renderSamples(al::to_address(WritePtr), static_cast<uint>(avail), mFrameStep);
|
||||||
while(avail > 0)
|
while(avail > 0)
|
||||||
{
|
{
|
||||||
snd_pcm_sframes_t ret{snd_pcm_writei(mPcmHandle, WritePtr,
|
snd_pcm_sframes_t ret{snd_pcm_writei(mPcmHandle, al::to_address(WritePtr),
|
||||||
static_cast<snd_pcm_uframes_t>(avail))};
|
static_cast<snd_pcm_uframes_t>(avail))};
|
||||||
switch(ret)
|
switch(ret)
|
||||||
{
|
{
|
||||||
|
|
@ -626,10 +674,10 @@ int AlsaPlayback::mixerNoMMapProc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AlsaPlayback::open(const char *name)
|
void AlsaPlayback::open(std::string_view name)
|
||||||
{
|
{
|
||||||
std::string driver{"default"};
|
std::string driver{"default"};
|
||||||
if(name)
|
if(!name.empty())
|
||||||
{
|
{
|
||||||
if(PlaybackDevices.empty())
|
if(PlaybackDevices.empty())
|
||||||
PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
|
PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
|
||||||
|
|
@ -638,13 +686,13 @@ void AlsaPlayback::open(const char *name)
|
||||||
[name](const DevMap &entry) -> bool { return entry.name == name; });
|
[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", name};
|
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||||
driver = iter->device_name;
|
driver = iter->device_name;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
name = alsaDevice;
|
name = GetDefaultName();
|
||||||
if(auto driveropt = ConfigValueStr(nullptr, "alsa", "device"))
|
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 \"%s\"\n", driver.c_str());
|
||||||
|
|
@ -692,15 +740,14 @@ bool AlsaPlayback::reset()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool allowmmap{!!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "mmap", true)};
|
bool allowmmap{GetConfigValueBool(mDevice->DeviceName, "alsa"sv, "mmap"sv, true)};
|
||||||
uint periodLen{static_cast<uint>(mDevice->UpdateSize * 1000000_u64 / mDevice->Frequency)};
|
uint periodLen{static_cast<uint>(mDevice->UpdateSize * 1000000_u64 / mDevice->Frequency)};
|
||||||
uint bufferLen{static_cast<uint>(mDevice->BufferSize * 1000000_u64 / mDevice->Frequency)};
|
uint bufferLen{static_cast<uint>(mDevice->BufferSize * 1000000_u64 / mDevice->Frequency)};
|
||||||
uint rate{mDevice->Frequency};
|
uint rate{mDevice->Frequency};
|
||||||
|
|
||||||
int err{};
|
|
||||||
HwParamsPtr hp{CreateHwParams()};
|
HwParamsPtr hp{CreateHwParams()};
|
||||||
#define CHECK(x) do { \
|
#define CHECK(x) do { \
|
||||||
if((err=(x)) < 0) \
|
if(int err{x}; err < 0) \
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \
|
throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \
|
||||||
snd_strerror(err)}; \
|
snd_strerror(err)}; \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
@ -715,17 +762,18 @@ bool AlsaPlayback::reset()
|
||||||
/* test and set format (implicitly sets sample bits) */
|
/* test and set format (implicitly sets sample bits) */
|
||||||
if(snd_pcm_hw_params_test_format(mPcmHandle, hp.get(), format) < 0)
|
if(snd_pcm_hw_params_test_format(mPcmHandle, hp.get(), format) < 0)
|
||||||
{
|
{
|
||||||
static const struct {
|
struct FormatMap {
|
||||||
snd_pcm_format_t format;
|
snd_pcm_format_t format;
|
||||||
DevFmtType fmttype;
|
DevFmtType fmttype;
|
||||||
} formatlist[] = {
|
};
|
||||||
{ SND_PCM_FORMAT_FLOAT, DevFmtFloat },
|
static constexpr std::array formatlist{
|
||||||
{ SND_PCM_FORMAT_S32, DevFmtInt },
|
FormatMap{SND_PCM_FORMAT_FLOAT, DevFmtFloat },
|
||||||
{ SND_PCM_FORMAT_U32, DevFmtUInt },
|
FormatMap{SND_PCM_FORMAT_S32, DevFmtInt },
|
||||||
{ SND_PCM_FORMAT_S16, DevFmtShort },
|
FormatMap{SND_PCM_FORMAT_U32, DevFmtUInt },
|
||||||
{ SND_PCM_FORMAT_U16, DevFmtUShort },
|
FormatMap{SND_PCM_FORMAT_S16, DevFmtShort },
|
||||||
{ SND_PCM_FORMAT_S8, DevFmtByte },
|
FormatMap{SND_PCM_FORMAT_U16, DevFmtUShort},
|
||||||
{ SND_PCM_FORMAT_U8, DevFmtUByte },
|
FormatMap{SND_PCM_FORMAT_S8, DevFmtByte },
|
||||||
|
FormatMap{SND_PCM_FORMAT_U8, DevFmtUByte },
|
||||||
};
|
};
|
||||||
|
|
||||||
for(const auto &fmt : formatlist)
|
for(const auto &fmt : formatlist)
|
||||||
|
|
@ -750,7 +798,7 @@ 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.c_str(), "alsa", "allow-resampler", false)
|
if(!GetConfigValueBool(mDevice->DeviceName, "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)
|
||||||
|
|
@ -760,10 +808,10 @@ bool AlsaPlayback::reset()
|
||||||
WARN("Failed to enable ALSA resampler\n");
|
WARN("Failed to enable ALSA resampler\n");
|
||||||
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((err=snd_pcm_hw_params_set_period_time_near(mPcmHandle, hp.get(), &periodLen, nullptr)) < 0)
|
if(int err{snd_pcm_hw_params_set_period_time_near(mPcmHandle, hp.get(), &periodLen, nullptr)}; err < 0)
|
||||||
ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err));
|
ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err));
|
||||||
/* set buffer time (implicitly sets buffer size/bytes/time and period size/bytes) */
|
/* set buffer time (implicitly sets buffer size/bytes/time and period size/bytes) */
|
||||||
if((err=snd_pcm_hw_params_set_buffer_time_near(mPcmHandle, hp.get(), &bufferLen, nullptr)) < 0)
|
if(int err{snd_pcm_hw_params_set_buffer_time_near(mPcmHandle, hp.get(), &bufferLen, nullptr)}; err < 0)
|
||||||
ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err));
|
ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", 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()));
|
||||||
|
|
@ -798,11 +846,10 @@ bool AlsaPlayback::reset()
|
||||||
|
|
||||||
void AlsaPlayback::start()
|
void AlsaPlayback::start()
|
||||||
{
|
{
|
||||||
int err{};
|
|
||||||
snd_pcm_access_t access{};
|
snd_pcm_access_t access{};
|
||||||
HwParamsPtr hp{CreateHwParams()};
|
HwParamsPtr hp{CreateHwParams()};
|
||||||
#define CHECK(x) do { \
|
#define CHECK(x) do { \
|
||||||
if((err=(x)) < 0) \
|
if(int err{x}; err < 0) \
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \
|
throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \
|
||||||
snd_strerror(err)}; \
|
snd_strerror(err)}; \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
@ -849,10 +896,9 @@ void AlsaPlayback::stop()
|
||||||
|
|
||||||
ClockLatency AlsaPlayback::getClockLatency()
|
ClockLatency AlsaPlayback::getClockLatency()
|
||||||
{
|
{
|
||||||
ClockLatency ret;
|
std::lock_guard<std::mutex> dlock{mMutex};
|
||||||
|
ClockLatency ret{};
|
||||||
std::lock_guard<std::mutex> _{mMutex};
|
ret.ClockTime = mDevice->getClockTime();
|
||||||
ret.ClockTime = GetDeviceClockTime(mDevice);
|
|
||||||
snd_pcm_sframes_t delay{};
|
snd_pcm_sframes_t delay{};
|
||||||
int err{snd_pcm_delay(mPcmHandle, &delay)};
|
int err{snd_pcm_delay(mPcmHandle, &delay)};
|
||||||
if(err < 0)
|
if(err < 0)
|
||||||
|
|
@ -871,23 +917,21 @@ struct AlsaCapture final : public BackendBase {
|
||||||
AlsaCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
AlsaCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~AlsaCapture() override;
|
~AlsaCapture() override;
|
||||||
|
|
||||||
void open(const char *name) override;
|
void open(std::string_view name) override;
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
void captureSamples(al::byte *buffer, uint samples) override;
|
void captureSamples(std::byte *buffer, uint samples) override;
|
||||||
uint availableSamples() override;
|
uint availableSamples() override;
|
||||||
ClockLatency getClockLatency() override;
|
ClockLatency getClockLatency() override;
|
||||||
|
|
||||||
snd_pcm_t *mPcmHandle{nullptr};
|
snd_pcm_t *mPcmHandle{nullptr};
|
||||||
|
|
||||||
al::vector<al::byte> mBuffer;
|
std::vector<std::byte> mBuffer;
|
||||||
|
|
||||||
bool mDoCapture{false};
|
bool mDoCapture{false};
|
||||||
RingBufferPtr mRing{nullptr};
|
RingBufferPtr mRing{nullptr};
|
||||||
|
|
||||||
snd_pcm_sframes_t mLastAvail{0};
|
snd_pcm_sframes_t mLastAvail{0};
|
||||||
|
|
||||||
DEF_NEWDEL(AlsaCapture)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
AlsaCapture::~AlsaCapture()
|
AlsaCapture::~AlsaCapture()
|
||||||
|
|
@ -898,10 +942,10 @@ AlsaCapture::~AlsaCapture()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AlsaCapture::open(const char *name)
|
void AlsaCapture::open(std::string_view name)
|
||||||
{
|
{
|
||||||
std::string driver{"default"};
|
std::string driver{"default"};
|
||||||
if(name)
|
if(!name.empty())
|
||||||
{
|
{
|
||||||
if(CaptureDevices.empty())
|
if(CaptureDevices.empty())
|
||||||
CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
|
CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
|
||||||
|
|
@ -910,19 +954,18 @@ void AlsaCapture::open(const char *name)
|
||||||
[name](const DevMap &entry) -> bool { return entry.name == name; });
|
[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", name};
|
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||||
driver = iter->device_name;
|
driver = iter->device_name;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
name = alsaDevice;
|
name = GetDefaultName();
|
||||||
if(auto driveropt = ConfigValueStr(nullptr, "alsa", "capture"))
|
if(auto driveropt = ConfigValueStr({}, "alsa"sv, "capture"sv))
|
||||||
driver = std::move(driveropt).value();
|
driver = std::move(driveropt).value();
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("Opening device \"%s\"\n", driver.c_str());
|
TRACE("Opening device \"%s\"\n", driver.c_str());
|
||||||
int err{snd_pcm_open(&mPcmHandle, driver.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)};
|
if(int err{snd_pcm_open(&mPcmHandle, driver.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)}; 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 \"%s\"", driver.c_str()};
|
||||||
|
|
||||||
|
|
@ -955,13 +998,15 @@ void AlsaCapture::open(const char *name)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
snd_pcm_uframes_t bufferSizeInFrames{maxu(mDevice->BufferSize, 100*mDevice->Frequency/1000)};
|
snd_pcm_uframes_t bufferSizeInFrames{std::max(mDevice->BufferSize,
|
||||||
snd_pcm_uframes_t periodSizeInFrames{minu(mDevice->BufferSize, 25*mDevice->Frequency/1000)};
|
100u*mDevice->Frequency/1000u)};
|
||||||
|
snd_pcm_uframes_t periodSizeInFrames{std::min(mDevice->BufferSize,
|
||||||
|
25u*mDevice->Frequency/1000u)};
|
||||||
|
|
||||||
bool needring{false};
|
bool needring{false};
|
||||||
HwParamsPtr hp{CreateHwParams()};
|
HwParamsPtr hp{CreateHwParams()};
|
||||||
#define CHECK(x) do { \
|
#define CHECK(x) do { \
|
||||||
if((err=(x)) < 0) \
|
if(int err{x}; err < 0) \
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \
|
throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \
|
||||||
snd_strerror(err)}; \
|
snd_strerror(err)}; \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
@ -999,13 +1044,11 @@ void AlsaCapture::open(const char *name)
|
||||||
|
|
||||||
void AlsaCapture::start()
|
void AlsaCapture::start()
|
||||||
{
|
{
|
||||||
int err{snd_pcm_prepare(mPcmHandle)};
|
if(int err{snd_pcm_prepare(mPcmHandle)}; err < 0)
|
||||||
if(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: %s",
|
||||||
snd_strerror(err)};
|
snd_strerror(err)};
|
||||||
|
|
||||||
err = snd_pcm_start(mPcmHandle);
|
if(int err{snd_pcm_start(mPcmHandle)}; err < 0)
|
||||||
if(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: %s",
|
||||||
snd_strerror(err)};
|
snd_strerror(err)};
|
||||||
|
|
||||||
|
|
@ -1024,25 +1067,27 @@ void AlsaCapture::stop()
|
||||||
/* The ring buffer implicitly captures when checking availability.
|
/* The ring buffer implicitly captures when checking availability.
|
||||||
* Direct access needs to explicitly capture it into temp storage.
|
* Direct access needs to explicitly capture it into temp storage.
|
||||||
*/
|
*/
|
||||||
auto temp = al::vector<al::byte>(
|
auto temp = std::vector<std::byte>(
|
||||||
static_cast<size_t>(snd_pcm_frames_to_bytes(mPcmHandle, avail)));
|
static_cast<size_t>(snd_pcm_frames_to_bytes(mPcmHandle, avail)));
|
||||||
captureSamples(temp.data(), avail);
|
captureSamples(temp.data(), avail);
|
||||||
mBuffer = std::move(temp);
|
mBuffer = std::move(temp);
|
||||||
}
|
}
|
||||||
int err{snd_pcm_drop(mPcmHandle)};
|
if(int err{snd_pcm_drop(mPcmHandle)}; err < 0)
|
||||||
if(err < 0)
|
|
||||||
ERR("drop failed: %s\n", snd_strerror(err));
|
ERR("drop failed: %s\n", snd_strerror(err));
|
||||||
mDoCapture = false;
|
mDoCapture = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlsaCapture::captureSamples(al::byte *buffer, uint samples)
|
void AlsaCapture::captureSamples(std::byte *buffer, uint samples)
|
||||||
{
|
{
|
||||||
if(mRing)
|
if(mRing)
|
||||||
{
|
{
|
||||||
mRing->read(buffer, samples);
|
std::ignore = mRing->read(buffer, samples);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto outspan = al::span{buffer,
|
||||||
|
static_cast<size_t>(snd_pcm_frames_to_bytes(mPcmHandle, samples))};
|
||||||
|
auto outiter = outspan.begin();
|
||||||
mLastAvail -= samples;
|
mLastAvail -= samples;
|
||||||
while(mDevice->Connected.load(std::memory_order_acquire) && samples > 0)
|
while(mDevice->Connected.load(std::memory_order_acquire) && samples > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -1055,20 +1100,21 @@ void AlsaCapture::captureSamples(al::byte *buffer, uint samples)
|
||||||
if(static_cast<snd_pcm_uframes_t>(amt) > samples) amt = samples;
|
if(static_cast<snd_pcm_uframes_t>(amt) > samples) amt = samples;
|
||||||
|
|
||||||
amt = snd_pcm_frames_to_bytes(mPcmHandle, amt);
|
amt = snd_pcm_frames_to_bytes(mPcmHandle, amt);
|
||||||
std::copy_n(mBuffer.begin(), amt, buffer);
|
std::copy_n(mBuffer.begin(), amt, outiter);
|
||||||
|
|
||||||
mBuffer.erase(mBuffer.begin(), mBuffer.begin()+amt);
|
mBuffer.erase(mBuffer.begin(), mBuffer.begin()+amt);
|
||||||
amt = snd_pcm_bytes_to_frames(mPcmHandle, amt);
|
amt = snd_pcm_bytes_to_frames(mPcmHandle, amt);
|
||||||
}
|
}
|
||||||
else if(mDoCapture)
|
else if(mDoCapture)
|
||||||
amt = snd_pcm_readi(mPcmHandle, buffer, 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: %s\n", snd_strerror(static_cast<int>(amt)));
|
||||||
|
|
||||||
if(amt == -EAGAIN)
|
if(amt == -EAGAIN)
|
||||||
continue;
|
continue;
|
||||||
if((amt=snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1)) >= 0)
|
amt = snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1);
|
||||||
|
if(amt >= 0)
|
||||||
{
|
{
|
||||||
amt = snd_pcm_start(mPcmHandle);
|
amt = snd_pcm_start(mPcmHandle);
|
||||||
if(amt >= 0)
|
if(amt >= 0)
|
||||||
|
|
@ -1088,12 +1134,12 @@ void AlsaCapture::captureSamples(al::byte *buffer, uint samples)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer = buffer + amt;
|
outiter += amt;
|
||||||
samples -= static_cast<uint>(amt);
|
samples -= static_cast<uint>(amt);
|
||||||
}
|
}
|
||||||
if(samples > 0)
|
if(samples > 0)
|
||||||
std::fill_n(buffer, snd_pcm_frames_to_bytes(mPcmHandle, samples),
|
std::fill_n(outiter, snd_pcm_frames_to_bytes(mPcmHandle, samples),
|
||||||
al::byte((mDevice->FmtType == DevFmtUByte) ? 0x80 : 0));
|
std::byte((mDevice->FmtType == DevFmtUByte) ? 0x80 : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint AlsaCapture::availableSamples()
|
uint AlsaCapture::availableSamples()
|
||||||
|
|
@ -1105,7 +1151,8 @@ uint AlsaCapture::availableSamples()
|
||||||
{
|
{
|
||||||
ERR("avail update failed: %s\n", snd_strerror(static_cast<int>(avail)));
|
ERR("avail update failed: %s\n", snd_strerror(static_cast<int>(avail)));
|
||||||
|
|
||||||
if((avail=snd_pcm_recover(mPcmHandle, static_cast<int>(avail), 1)) >= 0)
|
avail = snd_pcm_recover(mPcmHandle, static_cast<int>(avail), 1);
|
||||||
|
if(avail >= 0)
|
||||||
{
|
{
|
||||||
if(mDoCapture)
|
if(mDoCapture)
|
||||||
avail = snd_pcm_start(mPcmHandle);
|
avail = snd_pcm_start(mPcmHandle);
|
||||||
|
|
@ -1141,7 +1188,8 @@ uint AlsaCapture::availableSamples()
|
||||||
|
|
||||||
if(amt == -EAGAIN)
|
if(amt == -EAGAIN)
|
||||||
continue;
|
continue;
|
||||||
if((amt=snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1)) >= 0)
|
amt = snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1);
|
||||||
|
if(amt >= 0)
|
||||||
{
|
{
|
||||||
if(mDoCapture)
|
if(mDoCapture)
|
||||||
amt = snd_pcm_start(mPcmHandle);
|
amt = snd_pcm_start(mPcmHandle);
|
||||||
|
|
@ -1168,9 +1216,8 @@ uint AlsaCapture::availableSamples()
|
||||||
|
|
||||||
ClockLatency AlsaCapture::getClockLatency()
|
ClockLatency AlsaCapture::getClockLatency()
|
||||||
{
|
{
|
||||||
ClockLatency ret;
|
ClockLatency ret{};
|
||||||
|
ret.ClockTime = mDevice->getClockTime();
|
||||||
ret.ClockTime = GetDeviceClockTime(mDevice);
|
|
||||||
snd_pcm_sframes_t delay{};
|
snd_pcm_sframes_t delay{};
|
||||||
int err{snd_pcm_delay(mPcmHandle, &delay)};
|
int err{snd_pcm_delay(mPcmHandle, &delay)};
|
||||||
if(err < 0)
|
if(err < 0)
|
||||||
|
|
@ -1189,13 +1236,9 @@ ClockLatency AlsaCapture::getClockLatency()
|
||||||
|
|
||||||
bool AlsaBackendFactory::init()
|
bool AlsaBackendFactory::init()
|
||||||
{
|
{
|
||||||
bool error{false};
|
|
||||||
|
|
||||||
#ifdef HAVE_DYNLOAD
|
#ifdef HAVE_DYNLOAD
|
||||||
if(!alsa_handle)
|
if(!alsa_handle)
|
||||||
{
|
{
|
||||||
std::string missing_funcs;
|
|
||||||
|
|
||||||
alsa_handle = LoadLib("libasound.so.2");
|
alsa_handle = LoadLib("libasound.so.2");
|
||||||
if(!alsa_handle)
|
if(!alsa_handle)
|
||||||
{
|
{
|
||||||
|
|
@ -1203,52 +1246,47 @@ bool AlsaBackendFactory::init()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = false;
|
std::string missing_funcs;
|
||||||
#define LOAD_FUNC(f) do { \
|
#define LOAD_FUNC(f) do { \
|
||||||
p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(alsa_handle, #f)); \
|
p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(alsa_handle, #f)); \
|
||||||
if(p##f == nullptr) { \
|
if(p##f == nullptr) missing_funcs += "\n" #f; \
|
||||||
error = true; \
|
|
||||||
missing_funcs += "\n" #f; \
|
|
||||||
} \
|
|
||||||
} while(0)
|
} while(0)
|
||||||
ALSA_FUNCS(LOAD_FUNC);
|
ALSA_FUNCS(LOAD_FUNC);
|
||||||
#undef LOAD_FUNC
|
#undef LOAD_FUNC
|
||||||
|
|
||||||
if(error)
|
if(!missing_funcs.empty())
|
||||||
{
|
{
|
||||||
WARN("Missing expected functions:%s\n", missing_funcs.c_str());
|
WARN("Missing expected functions:%s\n", missing_funcs.c_str());
|
||||||
CloseLib(alsa_handle);
|
CloseLib(alsa_handle);
|
||||||
alsa_handle = nullptr;
|
alsa_handle = nullptr;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return !error;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AlsaBackendFactory::querySupport(BackendType type)
|
bool AlsaBackendFactory::querySupport(BackendType type)
|
||||||
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
||||||
|
|
||||||
std::string AlsaBackendFactory::probe(BackendType type)
|
auto AlsaBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||||
{
|
{
|
||||||
std::string outnames;
|
std::vector<std::string> outnames;
|
||||||
|
|
||||||
auto add_device = [&outnames](const DevMap &entry) -> void
|
auto add_device = [&outnames](const DevMap &entry) -> void
|
||||||
{
|
{ outnames.emplace_back(entry.name); };
|
||||||
/* +1 to also append the null char (to ensure a null-separated list and
|
|
||||||
* double-null terminated list).
|
|
||||||
*/
|
|
||||||
outnames.append(entry.name.c_str(), entry.name.length()+1);
|
|
||||||
};
|
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case BackendType::Playback:
|
case BackendType::Playback:
|
||||||
PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
|
PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
|
||||||
|
outnames.reserve(PlaybackDevices.size());
|
||||||
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
|
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BackendType::Capture:
|
case BackendType::Capture:
|
||||||
CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
|
CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
|
||||||
|
outnames.reserve(CaptureDevices.size());
|
||||||
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
|
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,15 @@
|
||||||
|
|
||||||
struct AlsaBackendFactory final : public BackendFactory {
|
struct AlsaBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
bool init() override;
|
auto init() -> bool final;
|
||||||
|
|
||||||
bool querySupport(BackendType type) override;
|
auto querySupport(BackendType type) -> bool final;
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||||
|
|
||||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_ALSA_H */
|
#endif /* BACKENDS_ALSA_H */
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,6 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#include <windows.h>
|
|
||||||
#include <mmreg.h>
|
|
||||||
|
|
||||||
#include "albit.h"
|
|
||||||
#include "core/logging.h"
|
|
||||||
#include "aloptional.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "atomic.h"
|
|
||||||
#include "core/devformat.h"
|
#include "core/devformat.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -25,10 +14,12 @@ namespace al {
|
||||||
|
|
||||||
backend_exception::backend_exception(backend_error code, const char *msg, ...) : mErrorCode{code}
|
backend_exception::backend_exception(backend_error code, const char *msg, ...) : mErrorCode{code}
|
||||||
{
|
{
|
||||||
|
/* NOLINTBEGIN(*-array-to-pointer-decay) */
|
||||||
std::va_list args;
|
std::va_list args;
|
||||||
va_start(args, msg);
|
va_start(args, msg);
|
||||||
setMessage(msg, args);
|
setMessage(msg, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
/* NOLINTEND(*-array-to-pointer-decay) */
|
||||||
}
|
}
|
||||||
backend_exception::~backend_exception() = default;
|
backend_exception::~backend_exception() = default;
|
||||||
|
|
||||||
|
|
@ -38,7 +29,7 @@ backend_exception::~backend_exception() = default;
|
||||||
bool BackendBase::reset()
|
bool BackendBase::reset()
|
||||||
{ throw al::backend_exception{al::backend_error::DeviceError, "Invalid BackendBase call"}; }
|
{ throw al::backend_exception{al::backend_error::DeviceError, "Invalid BackendBase call"}; }
|
||||||
|
|
||||||
void BackendBase::captureSamples(al::byte*, uint)
|
void BackendBase::captureSamples(std::byte*, uint)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
uint BackendBase::availableSamples()
|
uint BackendBase::availableSamples()
|
||||||
|
|
@ -46,27 +37,26 @@ uint BackendBase::availableSamples()
|
||||||
|
|
||||||
ClockLatency BackendBase::getClockLatency()
|
ClockLatency BackendBase::getClockLatency()
|
||||||
{
|
{
|
||||||
ClockLatency ret;
|
ClockLatency ret{};
|
||||||
|
|
||||||
uint refcount;
|
uint refcount;
|
||||||
do {
|
do {
|
||||||
refcount = mDevice->waitForMix();
|
refcount = mDevice->waitForMix();
|
||||||
ret.ClockTime = GetDeviceClockTime(mDevice);
|
ret.ClockTime = mDevice->getClockTime();
|
||||||
std::atomic_thread_fence(std::memory_order_acquire);
|
std::atomic_thread_fence(std::memory_order_acquire);
|
||||||
} while(refcount != ReadRef(mDevice->MixCount));
|
} while(refcount != mDevice->mMixCount.load(std::memory_order_relaxed));
|
||||||
|
|
||||||
/* NOTE: The device will generally have about all but one periods filled at
|
/* NOTE: The device will generally have about all but one periods filled at
|
||||||
* 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::max(std::chrono::seconds{mDevice->BufferSize-mDevice->UpdateSize},
|
ret.Latency = std::chrono::seconds{mDevice->BufferSize - mDevice->UpdateSize};
|
||||||
std::chrono::seconds::zero());
|
|
||||||
ret.Latency /= mDevice->Frequency;
|
ret.Latency /= mDevice->Frequency;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendBase::setDefaultWFXChannelOrder()
|
void BackendBase::setDefaultWFXChannelOrder() const
|
||||||
{
|
{
|
||||||
mDevice->RealOut.ChannelIndex.fill(InvalidChannelIndex);
|
mDevice->RealOut.ChannelIndex.fill(InvalidChannelIndex);
|
||||||
|
|
||||||
|
|
@ -126,6 +116,24 @@ void BackendBase::setDefaultWFXChannelOrder()
|
||||||
mDevice->RealOut.ChannelIndex[TopBackLeft] = 10;
|
mDevice->RealOut.ChannelIndex[TopBackLeft] = 10;
|
||||||
mDevice->RealOut.ChannelIndex[TopBackRight] = 11;
|
mDevice->RealOut.ChannelIndex[TopBackRight] = 11;
|
||||||
break;
|
break;
|
||||||
|
case DevFmtX7144:
|
||||||
|
mDevice->RealOut.ChannelIndex[FrontLeft] = 0;
|
||||||
|
mDevice->RealOut.ChannelIndex[FrontRight] = 1;
|
||||||
|
mDevice->RealOut.ChannelIndex[FrontCenter] = 2;
|
||||||
|
mDevice->RealOut.ChannelIndex[LFE] = 3;
|
||||||
|
mDevice->RealOut.ChannelIndex[BackLeft] = 4;
|
||||||
|
mDevice->RealOut.ChannelIndex[BackRight] = 5;
|
||||||
|
mDevice->RealOut.ChannelIndex[SideLeft] = 6;
|
||||||
|
mDevice->RealOut.ChannelIndex[SideRight] = 7;
|
||||||
|
mDevice->RealOut.ChannelIndex[TopFrontLeft] = 8;
|
||||||
|
mDevice->RealOut.ChannelIndex[TopFrontRight] = 9;
|
||||||
|
mDevice->RealOut.ChannelIndex[TopBackLeft] = 10;
|
||||||
|
mDevice->RealOut.ChannelIndex[TopBackRight] = 11;
|
||||||
|
mDevice->RealOut.ChannelIndex[BottomFrontLeft] = 12;
|
||||||
|
mDevice->RealOut.ChannelIndex[BottomFrontRight] = 13;
|
||||||
|
mDevice->RealOut.ChannelIndex[BottomBackLeft] = 14;
|
||||||
|
mDevice->RealOut.ChannelIndex[BottomBackRight] = 15;
|
||||||
|
break;
|
||||||
case DevFmtX3D71:
|
case DevFmtX3D71:
|
||||||
mDevice->RealOut.ChannelIndex[FrontLeft] = 0;
|
mDevice->RealOut.ChannelIndex[FrontLeft] = 0;
|
||||||
mDevice->RealOut.ChannelIndex[FrontRight] = 1;
|
mDevice->RealOut.ChannelIndex[FrontRight] = 1;
|
||||||
|
|
@ -141,7 +149,7 @@ void BackendBase::setDefaultWFXChannelOrder()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendBase::setDefaultChannelOrder()
|
void BackendBase::setDefaultChannelOrder() const
|
||||||
{
|
{
|
||||||
mDevice->RealOut.ChannelIndex.fill(InvalidChannelIndex);
|
mDevice->RealOut.ChannelIndex.fill(InvalidChannelIndex);
|
||||||
|
|
||||||
|
|
@ -179,6 +187,24 @@ void BackendBase::setDefaultChannelOrder()
|
||||||
mDevice->RealOut.ChannelIndex[TopBackLeft] = 10;
|
mDevice->RealOut.ChannelIndex[TopBackLeft] = 10;
|
||||||
mDevice->RealOut.ChannelIndex[TopBackRight] = 11;
|
mDevice->RealOut.ChannelIndex[TopBackRight] = 11;
|
||||||
break;
|
break;
|
||||||
|
case DevFmtX7144:
|
||||||
|
mDevice->RealOut.ChannelIndex[FrontLeft] = 0;
|
||||||
|
mDevice->RealOut.ChannelIndex[FrontRight] = 1;
|
||||||
|
mDevice->RealOut.ChannelIndex[BackLeft] = 2;
|
||||||
|
mDevice->RealOut.ChannelIndex[BackRight] = 3;
|
||||||
|
mDevice->RealOut.ChannelIndex[FrontCenter] = 4;
|
||||||
|
mDevice->RealOut.ChannelIndex[LFE] = 5;
|
||||||
|
mDevice->RealOut.ChannelIndex[SideLeft] = 6;
|
||||||
|
mDevice->RealOut.ChannelIndex[SideRight] = 7;
|
||||||
|
mDevice->RealOut.ChannelIndex[TopFrontLeft] = 8;
|
||||||
|
mDevice->RealOut.ChannelIndex[TopFrontRight] = 9;
|
||||||
|
mDevice->RealOut.ChannelIndex[TopBackLeft] = 10;
|
||||||
|
mDevice->RealOut.ChannelIndex[TopBackRight] = 11;
|
||||||
|
mDevice->RealOut.ChannelIndex[BottomFrontLeft] = 12;
|
||||||
|
mDevice->RealOut.ChannelIndex[BottomFrontRight] = 13;
|
||||||
|
mDevice->RealOut.ChannelIndex[BottomBackLeft] = 14;
|
||||||
|
mDevice->RealOut.ChannelIndex[BottomBackRight] = 15;
|
||||||
|
break;
|
||||||
case DevFmtX3D71:
|
case DevFmtX3D71:
|
||||||
mDevice->RealOut.ChannelIndex[FrontLeft] = 0;
|
mDevice->RealOut.ChannelIndex[FrontLeft] = 0;
|
||||||
mDevice->RealOut.ChannelIndex[FrontRight] = 1;
|
mDevice->RealOut.ChannelIndex[FrontRight] = 1;
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,16 @@
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
|
#include <cstddef>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <ratio>
|
#include <ratio>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "albyte.h"
|
|
||||||
#include "core/device.h"
|
#include "core/device.h"
|
||||||
#include "core/except.h"
|
#include "core/except.h"
|
||||||
|
#include "alc/events.h"
|
||||||
|
|
||||||
|
|
||||||
using uint = unsigned int;
|
using uint = unsigned int;
|
||||||
|
|
@ -20,27 +23,33 @@ struct ClockLatency {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BackendBase {
|
struct BackendBase {
|
||||||
virtual void open(const char *name) = 0;
|
virtual void open(std::string_view name) = 0;
|
||||||
|
|
||||||
virtual bool reset();
|
virtual bool reset();
|
||||||
virtual void start() = 0;
|
virtual void start() = 0;
|
||||||
virtual void stop() = 0;
|
virtual void stop() = 0;
|
||||||
|
|
||||||
virtual void captureSamples(al::byte *buffer, uint samples);
|
virtual void captureSamples(std::byte *buffer, uint samples);
|
||||||
virtual uint availableSamples();
|
virtual uint availableSamples();
|
||||||
|
|
||||||
virtual ClockLatency getClockLatency();
|
virtual ClockLatency getClockLatency();
|
||||||
|
|
||||||
DeviceBase *const mDevice;
|
DeviceBase *const mDevice;
|
||||||
|
|
||||||
|
BackendBase() = delete;
|
||||||
|
BackendBase(const BackendBase&) = delete;
|
||||||
|
BackendBase(BackendBase&&) = delete;
|
||||||
BackendBase(DeviceBase *device) noexcept : mDevice{device} { }
|
BackendBase(DeviceBase *device) noexcept : mDevice{device} { }
|
||||||
virtual ~BackendBase() = default;
|
virtual ~BackendBase() = default;
|
||||||
|
|
||||||
|
void operator=(const BackendBase&) = delete;
|
||||||
|
void operator=(BackendBase&&) = delete;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/** Sets the default channel order used by most non-WaveFormatEx-based APIs. */
|
/** Sets the default channel order used by most non-WaveFormatEx-based APIs. */
|
||||||
void setDefaultChannelOrder();
|
void setDefaultChannelOrder() const;
|
||||||
/** Sets the default channel order used by WaveFormatEx. */
|
/** Sets the default channel order used by WaveFormatEx. */
|
||||||
void setDefaultWFXChannelOrder();
|
void setDefaultWFXChannelOrder() const;
|
||||||
};
|
};
|
||||||
using BackendPtr = std::unique_ptr<BackendBase>;
|
using BackendPtr = std::unique_ptr<BackendBase>;
|
||||||
|
|
||||||
|
|
@ -50,18 +59,6 @@ enum class BackendType {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Helper to get the current clock time from the device's ClockBase, and
|
|
||||||
* SamplesDone converted from the sample rate.
|
|
||||||
*/
|
|
||||||
inline std::chrono::nanoseconds GetDeviceClockTime(DeviceBase *device)
|
|
||||||
{
|
|
||||||
using std::chrono::seconds;
|
|
||||||
using std::chrono::nanoseconds;
|
|
||||||
|
|
||||||
auto ns = nanoseconds{seconds{device->SamplesDone}} / device->Frequency;
|
|
||||||
return device->ClockBase + ns;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper to get the device latency from the backend, including any fixed
|
/* Helper to get the device latency from the backend, including any fixed
|
||||||
* latency from post-processing.
|
* latency from post-processing.
|
||||||
*/
|
*/
|
||||||
|
|
@ -74,16 +71,24 @@ inline ClockLatency GetClockLatency(DeviceBase *device, BackendBase *backend)
|
||||||
|
|
||||||
|
|
||||||
struct BackendFactory {
|
struct BackendFactory {
|
||||||
virtual bool init() = 0;
|
BackendFactory() = default;
|
||||||
|
BackendFactory(const BackendFactory&) = delete;
|
||||||
virtual bool querySupport(BackendType type) = 0;
|
BackendFactory(BackendFactory&&) = delete;
|
||||||
|
|
||||||
virtual std::string probe(BackendType type) = 0;
|
|
||||||
|
|
||||||
virtual BackendPtr createBackend(DeviceBase *device, BackendType type) = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual ~BackendFactory() = default;
|
virtual ~BackendFactory() = default;
|
||||||
|
|
||||||
|
void operator=(const BackendFactory&) = delete;
|
||||||
|
void operator=(BackendFactory&&) = delete;
|
||||||
|
|
||||||
|
virtual auto init() -> bool = 0;
|
||||||
|
|
||||||
|
virtual auto querySupport(BackendType type) -> bool = 0;
|
||||||
|
|
||||||
|
virtual auto queryEventSupport(alc::EventType, BackendType) -> alc::EventSupport
|
||||||
|
{ return alc::EventSupport::NoSupport; }
|
||||||
|
|
||||||
|
virtual auto enumerate(BackendType type) -> std::vector<std::string> = 0;
|
||||||
|
|
||||||
|
virtual auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace al {
|
namespace al {
|
||||||
|
|
@ -98,15 +103,15 @@ class backend_exception final : public base_exception {
|
||||||
backend_error mErrorCode;
|
backend_error mErrorCode;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
#ifdef __USE_MINGW_ANSI_STDIO
|
#ifdef __MINGW32__
|
||||||
[[gnu::format(gnu_printf, 3, 4)]]
|
[[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]]
|
||||||
#else
|
#else
|
||||||
[[gnu::format(printf, 3, 4)]]
|
[[gnu::format(printf, 3, 4)]]
|
||||||
#endif
|
#endif
|
||||||
backend_exception(backend_error code, const char *msg, ...);
|
backend_exception(backend_error code, const char *msg, ...);
|
||||||
~backend_exception() override;
|
~backend_exception() override;
|
||||||
|
|
||||||
backend_error errorCode() const noexcept { return mErrorCode; }
|
[[nodiscard]] auto errorCode() const noexcept -> backend_error { return mErrorCode; }
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace al
|
} // namespace al
|
||||||
|
|
|
||||||
|
|
@ -22,18 +22,20 @@
|
||||||
|
|
||||||
#include "coreaudio.h"
|
#include "coreaudio.h"
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <cinttypes>
|
||||||
|
#include <cmath>
|
||||||
|
#include <memory>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <vector>
|
||||||
#include <cmath>
|
#include <optional>
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.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"
|
||||||
|
|
@ -42,18 +44,40 @@
|
||||||
#include <AudioUnit/AudioUnit.h>
|
#include <AudioUnit/AudioUnit.h>
|
||||||
#include <AudioToolbox/AudioToolbox.h>
|
#include <AudioToolbox/AudioToolbox.h>
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
#if TARGET_OS_IOS || TARGET_OS_TV
|
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||||
#define CAN_ENUMERATE 0
|
#define CAN_ENUMERATE 0
|
||||||
#else
|
#else
|
||||||
|
#include <IOKit/audio/IOAudioTypes.h>
|
||||||
#define CAN_ENUMERATE 1
|
#define CAN_ENUMERATE 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
constexpr auto OutputElement = 0;
|
constexpr auto OutputElement = 0;
|
||||||
constexpr auto InputElement = 1;
|
constexpr auto InputElement = 1;
|
||||||
|
|
||||||
|
struct FourCCPrinter {
|
||||||
|
char mString[sizeof(UInt32) + 1]{};
|
||||||
|
|
||||||
|
constexpr FourCCPrinter(UInt32 code) noexcept
|
||||||
|
{
|
||||||
|
for(size_t i{0};i < sizeof(UInt32);++i)
|
||||||
|
{
|
||||||
|
const auto ch = static_cast<char>(code & 0xff);
|
||||||
|
/* If this breaks early it'll leave the first byte null, to get
|
||||||
|
* read as a 0-length string.
|
||||||
|
*/
|
||||||
|
if(ch <= 0x1f || ch >= 0x7f)
|
||||||
|
break;
|
||||||
|
mString[sizeof(UInt32)-1-i] = ch;
|
||||||
|
code >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
constexpr FourCCPrinter(int code) noexcept : FourCCPrinter{static_cast<UInt32>(code)} { }
|
||||||
|
|
||||||
|
constexpr const char *c_str() const noexcept { return mString; }
|
||||||
|
};
|
||||||
|
|
||||||
#if CAN_ENUMERATE
|
#if CAN_ENUMERATE
|
||||||
struct DeviceEntry {
|
struct DeviceEntry {
|
||||||
AudioDeviceID mId;
|
AudioDeviceID mId;
|
||||||
|
|
@ -147,7 +171,8 @@ UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture)
|
||||||
&propSize);
|
&propSize);
|
||||||
if(err)
|
if(err)
|
||||||
{
|
{
|
||||||
ERR("kAudioDevicePropertyStreamConfiguration size query failed: %u\n", err);
|
ERR("kAudioDevicePropertyStreamConfiguration size query failed: '%s' (%u)\n",
|
||||||
|
FourCCPrinter{err}.c_str(), err);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -158,7 +183,8 @@ UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture)
|
||||||
buflist);
|
buflist);
|
||||||
if(err)
|
if(err)
|
||||||
{
|
{
|
||||||
ERR("kAudioDevicePropertyStreamConfiguration query failed: %u\n", err);
|
ERR("kAudioDevicePropertyStreamConfiguration query failed: '%s' (%u)\n",
|
||||||
|
FourCCPrinter{err}.c_str(), err);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,7 +208,7 @@ void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture)
|
||||||
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: %u\n", err);
|
ERR("Failed to get device list: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,6 +273,48 @@ void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture)
|
||||||
newdevs.swap(list);
|
newdevs.swap(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DeviceHelper {
|
||||||
|
DeviceHelper()
|
||||||
|
{
|
||||||
|
AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice,
|
||||||
|
kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain};
|
||||||
|
OSStatus status = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil);
|
||||||
|
if (status != noErr)
|
||||||
|
ERR("AudioObjectAddPropertyListener fail: %d", status);
|
||||||
|
}
|
||||||
|
~DeviceHelper()
|
||||||
|
{
|
||||||
|
AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice,
|
||||||
|
kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain};
|
||||||
|
OSStatus status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil);
|
||||||
|
if (status != noErr)
|
||||||
|
ERR("AudioObjectRemovePropertyListener fail: %d", status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static OSStatus DeviceListenerProc(AudioObjectID /*inObjectID*/, UInt32 inNumberAddresses,
|
||||||
|
const AudioObjectPropertyAddress *inAddresses, void* /*inClientData*/)
|
||||||
|
{
|
||||||
|
for(UInt32 i = 0; i < inNumberAddresses; ++i)
|
||||||
|
{
|
||||||
|
switch(inAddresses[i].mSelector)
|
||||||
|
{
|
||||||
|
case kAudioHardwarePropertyDefaultOutputDevice:
|
||||||
|
case kAudioHardwarePropertyDefaultSystemOutputDevice:
|
||||||
|
alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback,
|
||||||
|
"Default playback device changed: "+std::to_string(inAddresses[i].mSelector));
|
||||||
|
break;
|
||||||
|
case kAudioHardwarePropertyDefaultInputDevice:
|
||||||
|
alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture,
|
||||||
|
"Default capture device changed: "+std::to_string(inAddresses[i].mSelector));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noErr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::optional<DeviceHelper> sDeviceHelper;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static constexpr char ca_device[] = "CoreAudio Default";
|
static constexpr char ca_device[] = "CoreAudio Default";
|
||||||
|
|
@ -260,15 +328,8 @@ struct CoreAudioPlayback final : public BackendBase {
|
||||||
OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags,
|
OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags,
|
||||||
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
|
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
|
||||||
AudioBufferList *ioData) noexcept;
|
AudioBufferList *ioData) noexcept;
|
||||||
static OSStatus MixerProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
|
|
||||||
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
|
|
||||||
AudioBufferList *ioData) noexcept
|
|
||||||
{
|
|
||||||
return static_cast<CoreAudioPlayback*>(inRefCon)->MixerProc(ioActionFlags, inTimeStamp,
|
|
||||||
inBusNumber, inNumberFrames, ioData);
|
|
||||||
}
|
|
||||||
|
|
||||||
void open(const char *name) override;
|
void open(std::string_view name) override;
|
||||||
bool reset() override;
|
bool reset() override;
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
|
|
@ -277,8 +338,6 @@ struct CoreAudioPlayback final : public BackendBase {
|
||||||
|
|
||||||
uint mFrameSize{0u};
|
uint mFrameSize{0u};
|
||||||
AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD
|
AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD
|
||||||
|
|
||||||
DEF_NEWDEL(CoreAudioPlayback)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CoreAudioPlayback::~CoreAudioPlayback()
|
CoreAudioPlayback::~CoreAudioPlayback()
|
||||||
|
|
@ -301,11 +360,11 @@ OSStatus CoreAudioPlayback::MixerProc(AudioUnitRenderActionFlags*, const AudioTi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CoreAudioPlayback::open(const char *name)
|
void CoreAudioPlayback::open(std::string_view name)
|
||||||
{
|
{
|
||||||
#if CAN_ENUMERATE
|
#if CAN_ENUMERATE
|
||||||
AudioDeviceID audioDevice{kAudioDeviceUnknown};
|
AudioDeviceID audioDevice{kAudioDeviceUnknown};
|
||||||
if(!name)
|
if(name.empty())
|
||||||
GetHwProperty(kAudioHardwarePropertyDefaultOutputDevice, sizeof(audioDevice),
|
GetHwProperty(kAudioHardwarePropertyDefaultOutputDevice, sizeof(audioDevice),
|
||||||
&audioDevice);
|
&audioDevice);
|
||||||
else
|
else
|
||||||
|
|
@ -318,16 +377,16 @@ void CoreAudioPlayback::open(const char *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", name};
|
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||||
|
|
||||||
audioDevice = devmatch->mId;
|
audioDevice = devmatch->mId;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if(!name)
|
if(name.empty())
|
||||||
name = ca_device;
|
name = ca_device;
|
||||||
else if(strcmp(name, ca_device) != 0)
|
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 \"%.*s\" not found",
|
||||||
name};
|
al::sizei(name), name.data()};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* open the default output unit */
|
/* open the default output unit */
|
||||||
|
|
@ -351,7 +410,7 @@ void CoreAudioPlayback::open(const char *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: %u", err};
|
"Could not create component instance: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||||
|
|
||||||
#if CAN_ENUMERATE
|
#if CAN_ENUMERATE
|
||||||
if(audioDevice != kAudioDeviceUnknown)
|
if(audioDevice != kAudioDeviceUnknown)
|
||||||
|
|
@ -362,7 +421,7 @@ void CoreAudioPlayback::open(const char *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: %u", err};
|
"Could not initialize audio unit: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||||
|
|
||||||
/* WARNING: I don't know if "valid" audio unit values are guaranteed to be
|
/* 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.
|
||||||
|
|
@ -375,7 +434,7 @@ void CoreAudioPlayback::open(const char *name)
|
||||||
mAudioUnit = audioUnit;
|
mAudioUnit = audioUnit;
|
||||||
|
|
||||||
#if CAN_ENUMERATE
|
#if CAN_ENUMERATE
|
||||||
if(name)
|
if(!name.empty())
|
||||||
mDevice->DeviceName = name;
|
mDevice->DeviceName = name;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -388,6 +447,21 @@ void CoreAudioPlayback::open(const char *name)
|
||||||
if(!devname.empty()) mDevice->DeviceName = std::move(devname);
|
if(!devname.empty()) mDevice->DeviceName = std::move(devname);
|
||||||
else mDevice->DeviceName = "Unknown Device Name";
|
else mDevice->DeviceName = "Unknown Device Name";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(audioDevice != kAudioDeviceUnknown)
|
||||||
|
{
|
||||||
|
UInt32 type{};
|
||||||
|
err = GetDevProperty(audioDevice, kAudioDevicePropertyDataSource, false,
|
||||||
|
kAudioObjectPropertyElementMaster, sizeof(type), &type);
|
||||||
|
if(err != noErr)
|
||||||
|
ERR("Failed to get audio device type: %u\n", err);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TRACE("Got device type '%s'\n", FourCCPrinter{type}.c_str());
|
||||||
|
mDevice->Flags.set(DirectEar, (type == kIOAudioOutputPortSubTypeHeadphones));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
mDevice->DeviceName = name;
|
mDevice->DeviceName = name;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -397,7 +471,7 @@ bool CoreAudioPlayback::reset()
|
||||||
{
|
{
|
||||||
OSStatus err{AudioUnitUninitialize(mAudioUnit)};
|
OSStatus err{AudioUnitUninitialize(mAudioUnit)};
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
ERR("-- AudioUnitUninitialize failed.\n");
|
ERR("AudioUnitUninitialize failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||||
|
|
||||||
/* retrieve default output unit's properties (output side) */
|
/* retrieve default output unit's properties (output side) */
|
||||||
AudioStreamBasicDescription streamFormat{};
|
AudioStreamBasicDescription streamFormat{};
|
||||||
|
|
@ -406,7 +480,8 @@ bool CoreAudioPlayback::reset()
|
||||||
OutputElement, &streamFormat, &size);
|
OutputElement, &streamFormat, &size);
|
||||||
if(err != noErr || size != sizeof(streamFormat))
|
if(err != noErr || size != sizeof(streamFormat))
|
||||||
{
|
{
|
||||||
ERR("AudioUnitGetProperty failed\n");
|
ERR("AudioUnitGetProperty(StreamFormat) failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(),
|
||||||
|
err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -473,7 +548,8 @@ bool CoreAudioPlayback::reset()
|
||||||
OutputElement, &streamFormat, sizeof(streamFormat));
|
OutputElement, &streamFormat, sizeof(streamFormat));
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioUnitSetProperty failed\n");
|
ERR("AudioUnitSetProperty(StreamFormat) failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(),
|
||||||
|
err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -482,14 +558,16 @@ bool CoreAudioPlayback::reset()
|
||||||
/* setup callback */
|
/* setup callback */
|
||||||
mFrameSize = mDevice->frameSizeFromFmt();
|
mFrameSize = mDevice->frameSizeFromFmt();
|
||||||
AURenderCallbackStruct input{};
|
AURenderCallbackStruct input{};
|
||||||
input.inputProc = CoreAudioPlayback::MixerProcC;
|
input.inputProc = [](void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) noexcept
|
||||||
|
{ return static_cast<CoreAudioPlayback*>(inRefCon)->MixerProc(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); };
|
||||||
input.inputProcRefCon = this;
|
input.inputProcRefCon = this;
|
||||||
|
|
||||||
err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_SetRenderCallback,
|
err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_SetRenderCallback,
|
||||||
kAudioUnitScope_Input, OutputElement, &input, sizeof(AURenderCallbackStruct));
|
kAudioUnitScope_Input, OutputElement, &input, sizeof(AURenderCallbackStruct));
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioUnitSetProperty failed\n");
|
ERR("AudioUnitSetProperty(SetRenderCallback) failed: '%s' (%u)\n",
|
||||||
|
FourCCPrinter{err}.c_str(), err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -497,7 +575,7 @@ bool CoreAudioPlayback::reset()
|
||||||
err = AudioUnitInitialize(mAudioUnit);
|
err = AudioUnitInitialize(mAudioUnit);
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioUnitInitialize failed\n");
|
ERR("AudioUnitInitialize failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -509,14 +587,14 @@ 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: %d", err};
|
"AudioOutputUnitStart failed: '%s' (%u)", 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\n");
|
ERR("AudioOutputUnitStop failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -527,18 +605,11 @@ struct CoreAudioCapture final : public BackendBase {
|
||||||
OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
|
OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
|
||||||
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
|
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
|
||||||
UInt32 inNumberFrames, AudioBufferList *ioData) noexcept;
|
UInt32 inNumberFrames, AudioBufferList *ioData) noexcept;
|
||||||
static OSStatus RecordProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
|
|
||||||
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
|
|
||||||
AudioBufferList *ioData) noexcept
|
|
||||||
{
|
|
||||||
return static_cast<CoreAudioCapture*>(inRefCon)->RecordProc(ioActionFlags, inTimeStamp,
|
|
||||||
inBusNumber, inNumberFrames, ioData);
|
|
||||||
}
|
|
||||||
|
|
||||||
void open(const char *name) override;
|
void open(std::string_view name) override;
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
void captureSamples(al::byte *buffer, uint samples) override;
|
void captureSamples(std::byte *buffer, uint samples) override;
|
||||||
uint availableSamples() override;
|
uint availableSamples() override;
|
||||||
|
|
||||||
AudioUnit mAudioUnit{0};
|
AudioUnit mAudioUnit{0};
|
||||||
|
|
@ -548,11 +619,9 @@ struct CoreAudioCapture final : public BackendBase {
|
||||||
|
|
||||||
SampleConverterPtr mConverter;
|
SampleConverterPtr mConverter;
|
||||||
|
|
||||||
al::vector<char> mCaptureData;
|
std::vector<char> mCaptureData;
|
||||||
|
|
||||||
RingBufferPtr mRing{nullptr};
|
RingBufferPtr mRing{nullptr};
|
||||||
|
|
||||||
DEF_NEWDEL(CoreAudioCapture)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CoreAudioCapture::~CoreAudioCapture()
|
CoreAudioCapture::~CoreAudioCapture()
|
||||||
|
|
@ -568,7 +637,7 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
|
||||||
AudioBufferList*) noexcept
|
AudioBufferList*) noexcept
|
||||||
{
|
{
|
||||||
union {
|
union {
|
||||||
al::byte _[maxz(sizeof(AudioBufferList), offsetof(AudioBufferList, mBuffers[1]))];
|
std::byte buf[std::max(sizeof(AudioBufferList), offsetof(AudioBufferList, mBuffers[1]))];
|
||||||
AudioBufferList list;
|
AudioBufferList list;
|
||||||
} audiobuf{};
|
} audiobuf{};
|
||||||
|
|
||||||
|
|
@ -581,20 +650,20 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
|
||||||
inNumberFrames, &audiobuf.list)};
|
inNumberFrames, &audiobuf.list)};
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioUnitRender capture error: %d\n", err);
|
ERR("AudioUnitRender capture error: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
mRing->write(mCaptureData.data(), inNumberFrames);
|
std::ignore = mRing->write(mCaptureData.data(), inNumberFrames);
|
||||||
return noErr;
|
return noErr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CoreAudioCapture::open(const char *name)
|
void CoreAudioCapture::open(std::string_view name)
|
||||||
{
|
{
|
||||||
#if CAN_ENUMERATE
|
#if CAN_ENUMERATE
|
||||||
AudioDeviceID audioDevice{kAudioDeviceUnknown};
|
AudioDeviceID audioDevice{kAudioDeviceUnknown};
|
||||||
if(!name)
|
if(name.empty())
|
||||||
GetHwProperty(kAudioHardwarePropertyDefaultInputDevice, sizeof(audioDevice),
|
GetHwProperty(kAudioHardwarePropertyDefaultInputDevice, sizeof(audioDevice),
|
||||||
&audioDevice);
|
&audioDevice);
|
||||||
else
|
else
|
||||||
|
|
@ -607,16 +676,16 @@ void CoreAudioCapture::open(const char *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", name};
|
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||||
|
|
||||||
audioDevice = devmatch->mId;
|
audioDevice = devmatch->mId;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if(!name)
|
if(name.empty())
|
||||||
name = ca_device;
|
name = ca_device;
|
||||||
else if(strcmp(name, ca_device) != 0)
|
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 \"%.*s\" not found",
|
||||||
name};
|
al::sizei(name), name.data()};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
AudioComponentDescription desc{};
|
AudioComponentDescription desc{};
|
||||||
|
|
@ -640,7 +709,7 @@ void CoreAudioCapture::open(const char *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: %u", err};
|
"Could not create component instance: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||||
|
|
||||||
// Turn off AudioUnit output
|
// Turn off AudioUnit output
|
||||||
UInt32 enableIO{0};
|
UInt32 enableIO{0};
|
||||||
|
|
@ -648,7 +717,8 @@ void CoreAudioCapture::open(const char *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: %u", err};
|
"Could not disable audio unit output property: '%s' (%u)", FourCCPrinter{err}.c_str(),
|
||||||
|
err};
|
||||||
|
|
||||||
// Turn on AudioUnit input
|
// Turn on AudioUnit input
|
||||||
enableIO = 1;
|
enableIO = 1;
|
||||||
|
|
@ -656,7 +726,8 @@ void CoreAudioCapture::open(const char *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: %u", err};
|
"Could not enable audio unit input property: '%s' (%u)", FourCCPrinter{err}.c_str(),
|
||||||
|
err};
|
||||||
|
|
||||||
#if CAN_ENUMERATE
|
#if CAN_ENUMERATE
|
||||||
if(audioDevice != kAudioDeviceUnknown)
|
if(audioDevice != kAudioDeviceUnknown)
|
||||||
|
|
@ -666,14 +737,15 @@ void CoreAudioCapture::open(const char *name)
|
||||||
|
|
||||||
// set capture callback
|
// set capture callback
|
||||||
AURenderCallbackStruct input{};
|
AURenderCallbackStruct input{};
|
||||||
input.inputProc = CoreAudioCapture::RecordProcC;
|
input.inputProc = [](void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) noexcept
|
||||||
|
{ return static_cast<CoreAudioCapture*>(inRefCon)->RecordProc(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); };
|
||||||
input.inputProcRefCon = this;
|
input.inputProcRefCon = this;
|
||||||
|
|
||||||
err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_SetInputCallback,
|
err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_SetInputCallback,
|
||||||
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: %u", err};
|
"Could not set capture callback: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||||
|
|
||||||
// Disable buffer allocation for capture
|
// Disable buffer allocation for capture
|
||||||
UInt32 flag{0};
|
UInt32 flag{0};
|
||||||
|
|
@ -681,13 +753,14 @@ void CoreAudioCapture::open(const char *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: %u", err};
|
"Could not disable buffer allocation property: '%s' (%u)", FourCCPrinter{err}.c_str(),
|
||||||
|
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: %u", err};
|
"Could not initialize audio unit: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||||
|
|
||||||
// Get the hardware format
|
// Get the hardware format
|
||||||
AudioStreamBasicDescription hardwareFormat{};
|
AudioStreamBasicDescription hardwareFormat{};
|
||||||
|
|
@ -696,7 +769,7 @@ void CoreAudioCapture::open(const char *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: %u", err};
|
"Could not get input format: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||||
|
|
||||||
// Set up the requested format description
|
// Set up the requested format description
|
||||||
AudioStreamBasicDescription requestedFormat{};
|
AudioStreamBasicDescription requestedFormat{};
|
||||||
|
|
@ -749,6 +822,7 @@ void CoreAudioCapture::open(const char *name)
|
||||||
case DevFmtX61:
|
case DevFmtX61:
|
||||||
case DevFmtX71:
|
case DevFmtX71:
|
||||||
case DevFmtX714:
|
case DevFmtX714:
|
||||||
|
case DevFmtX7144:
|
||||||
case DevFmtX3D71:
|
case DevFmtX3D71:
|
||||||
case DevFmtAmbi3D:
|
case DevFmtAmbi3D:
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "%s not supported",
|
throw al::backend_exception{al::backend_error::DeviceError, "%s not supported",
|
||||||
|
|
@ -777,14 +851,14 @@ void CoreAudioCapture::open(const char *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: %u", err};
|
"Could not set input format: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||||
|
|
||||||
/* Calculate the minimum AudioUnit output format frame count for the pre-
|
/* 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->Frequency};
|
||||||
auto FrameCount64 = maxu64(static_cast<uint64_t>(std::ceil(mDevice->BufferSize*srateScale)),
|
auto FrameCount64 = std::max(static_cast<uint64_t>(std::ceil(mDevice->BufferSize*srateScale)),
|
||||||
static_cast<UInt32>(outputFormat.mSampleRate)/10);
|
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,
|
||||||
|
|
@ -796,11 +870,11 @@ void CoreAudioCapture::open(const char *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: %u", err};
|
"Could not get input frame count: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||||
|
|
||||||
mCaptureData.resize(outputFrameCount * mFrameSize);
|
mCaptureData.resize(outputFrameCount * mFrameSize);
|
||||||
|
|
||||||
outputFrameCount = static_cast<UInt32>(maxu64(outputFrameCount, FrameCount64));
|
outputFrameCount = static_cast<UInt32>(std::max(uint64_t{outputFrameCount}, FrameCount64));
|
||||||
mRing = RingBuffer::Create(outputFrameCount, mFrameSize, false);
|
mRing = RingBuffer::Create(outputFrameCount, mFrameSize, false);
|
||||||
|
|
||||||
/* Set up sample converter if needed */
|
/* Set up sample converter if needed */
|
||||||
|
|
@ -810,7 +884,7 @@ void CoreAudioCapture::open(const char *name)
|
||||||
mDevice->Frequency, Resampler::FastBSinc24);
|
mDevice->Frequency, Resampler::FastBSinc24);
|
||||||
|
|
||||||
#if CAN_ENUMERATE
|
#if CAN_ENUMERATE
|
||||||
if(name)
|
if(!name.empty())
|
||||||
mDevice->DeviceName = name;
|
mDevice->DeviceName = name;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -834,21 +908,21 @@ 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: %d", err};
|
"AudioOutputUnitStart failed: '%s' (%u)", 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\n");
|
ERR("AudioOutputUnitStop failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreAudioCapture::captureSamples(al::byte *buffer, uint samples)
|
void CoreAudioCapture::captureSamples(std::byte *buffer, uint samples)
|
||||||
{
|
{
|
||||||
if(!mConverter)
|
if(!mConverter)
|
||||||
{
|
{
|
||||||
mRing->read(buffer, samples);
|
std::ignore = mRing->read(buffer, samples);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -882,28 +956,34 @@ BackendFactory &CoreAudioBackendFactory::getFactory()
|
||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CoreAudioBackendFactory::init() { return true; }
|
bool CoreAudioBackendFactory::init()
|
||||||
|
{
|
||||||
|
#if CAN_ENUMERATE
|
||||||
|
sDeviceHelper.emplace();
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool CoreAudioBackendFactory::querySupport(BackendType type)
|
bool CoreAudioBackendFactory::querySupport(BackendType type)
|
||||||
{ return type == BackendType::Playback || type == BackendType::Capture; }
|
{ return type == BackendType::Playback || type == BackendType::Capture; }
|
||||||
|
|
||||||
std::string CoreAudioBackendFactory::probe(BackendType type)
|
auto CoreAudioBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||||
{
|
{
|
||||||
std::string outnames;
|
std::vector<std::string> outnames;
|
||||||
#if CAN_ENUMERATE
|
#if CAN_ENUMERATE
|
||||||
auto append_name = [&outnames](const DeviceEntry &entry) -> void
|
auto append_name = [&outnames](const DeviceEntry &entry) -> void
|
||||||
{
|
{ outnames.emplace_back(entry.mName); };
|
||||||
/* Includes null char. */
|
|
||||||
outnames.append(entry.mName.c_str(), entry.mName.length()+1);
|
|
||||||
};
|
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case BackendType::Playback:
|
case BackendType::Playback:
|
||||||
EnumerateDevices(PlaybackList, false);
|
EnumerateDevices(PlaybackList, false);
|
||||||
|
outnames.reserve(PlaybackList.size());
|
||||||
std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
|
std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
|
||||||
break;
|
break;
|
||||||
case BackendType::Capture:
|
case BackendType::Capture:
|
||||||
EnumerateDevices(CaptureList, true);
|
EnumerateDevices(CaptureList, true);
|
||||||
|
outnames.reserve(CaptureList.size());
|
||||||
std::for_each(CaptureList.cbegin(), CaptureList.cend(), append_name);
|
std::for_each(CaptureList.cbegin(), CaptureList.cend(), append_name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -914,8 +994,7 @@ std::string CoreAudioBackendFactory::probe(BackendType type)
|
||||||
{
|
{
|
||||||
case BackendType::Playback:
|
case BackendType::Playback:
|
||||||
case BackendType::Capture:
|
case BackendType::Capture:
|
||||||
/* Includes null char. */
|
outnames.emplace_back(ca_device);
|
||||||
outnames.append(ca_device, sizeof(ca_device));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -930,3 +1009,18 @@ BackendPtr CoreAudioBackendFactory::createBackend(DeviceBase *device, BackendTyp
|
||||||
return BackendPtr{new CoreAudioCapture{device}};
|
return BackendPtr{new CoreAudioCapture{device}};
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alc::EventSupport CoreAudioBackendFactory::queryEventSupport(alc::EventType eventType, BackendType)
|
||||||
|
{
|
||||||
|
switch(eventType)
|
||||||
|
{
|
||||||
|
case alc::EventType::DefaultDeviceChanged:
|
||||||
|
return alc::EventSupport::FullSupport;
|
||||||
|
|
||||||
|
case alc::EventType::DeviceAdded:
|
||||||
|
case alc::EventType::DeviceRemoved:
|
||||||
|
case alc::EventType::Count:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return alc::EventSupport::NoSupport;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,17 @@
|
||||||
|
|
||||||
struct CoreAudioBackendFactory final : public BackendFactory {
|
struct CoreAudioBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
bool init() override;
|
auto init() -> bool final;
|
||||||
|
|
||||||
bool querySupport(BackendType type) override;
|
auto querySupport(BackendType type) -> bool final;
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
auto queryEventSupport(alc::EventType eventType, BackendType type) -> alc::EventSupport final;
|
||||||
|
|
||||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||||
|
|
||||||
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_COREAUDIO_H */
|
#endif /* BACKENDS_COREAUDIO_H */
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,6 @@
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <memory.h>
|
|
||||||
|
|
||||||
#include <cguid.h>
|
#include <cguid.h>
|
||||||
#include <mmreg.h>
|
#include <mmreg.h>
|
||||||
#ifndef _WAVEFORMATEXTENSIBLE_
|
#ifndef _WAVEFORMATEXTENSIBLE_
|
||||||
|
|
@ -36,15 +32,21 @@
|
||||||
#include <ksmedia.h>
|
#include <ksmedia.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <thread>
|
#include <cstdio>
|
||||||
#include <string>
|
#include <cstdlib>
|
||||||
#include <vector>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <memory.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "alnumeric.h"
|
#include "alspan.h"
|
||||||
|
#include "alstring.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"
|
||||||
|
|
@ -52,7 +54,6 @@
|
||||||
#include "dynload.h"
|
#include "dynload.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
#include "strutils.h"
|
#include "strutils.h"
|
||||||
#include "threads.h"
|
|
||||||
|
|
||||||
/* MinGW-w64 needs this for some unknown reason now. */
|
/* MinGW-w64 needs this for some unknown reason now. */
|
||||||
using LPCWAVEFORMATEX = const WAVEFORMATEX*;
|
using LPCWAVEFORMATEX = const WAVEFORMATEX*;
|
||||||
|
|
@ -129,10 +130,10 @@ struct DevMap {
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
al::vector<DevMap> PlaybackDevices;
|
std::vector<DevMap> PlaybackDevices;
|
||||||
al::vector<DevMap> CaptureDevices;
|
std::vector<DevMap> CaptureDevices;
|
||||||
|
|
||||||
bool checkName(const al::vector<DevMap> &list, const std::string &name)
|
bool checkName(const al::span<DevMap> list, const std::string &name)
|
||||||
{
|
{
|
||||||
auto match_name = [&name](const DevMap &entry) -> bool
|
auto match_name = [&name](const DevMap &entry) -> bool
|
||||||
{ return entry.name == name; };
|
{ return entry.name == name; };
|
||||||
|
|
@ -144,7 +145,7 @@ BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR*, voi
|
||||||
if(!guid)
|
if(!guid)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
auto& devices = *static_cast<al::vector<DevMap>*>(data);
|
auto& devices = *static_cast<std::vector<DevMap>*>(data);
|
||||||
const std::string basename{DEVNAME_HEAD + wstr_to_utf8(desc)};
|
const std::string basename{DEVNAME_HEAD + wstr_to_utf8(desc)};
|
||||||
|
|
||||||
int count{1};
|
int count{1};
|
||||||
|
|
@ -176,7 +177,7 @@ struct DSoundPlayback final : public BackendBase {
|
||||||
|
|
||||||
int mixerProc();
|
int mixerProc();
|
||||||
|
|
||||||
void open(const char *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;
|
||||||
|
|
@ -189,8 +190,6 @@ struct DSoundPlayback final : public BackendBase {
|
||||||
|
|
||||||
std::atomic<bool> mKillNow{true};
|
std::atomic<bool> mKillNow{true};
|
||||||
std::thread mThread;
|
std::thread mThread;
|
||||||
|
|
||||||
DEF_NEWDEL(DSoundPlayback)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DSoundPlayback::~DSoundPlayback()
|
DSoundPlayback::~DSoundPlayback()
|
||||||
|
|
@ -209,7 +208,7 @@ DSoundPlayback::~DSoundPlayback()
|
||||||
FORCE_ALIGN int DSoundPlayback::mixerProc()
|
FORCE_ALIGN int DSoundPlayback::mixerProc()
|
||||||
{
|
{
|
||||||
SetRTPriority();
|
SetRTPriority();
|
||||||
althrd_setname(MIXER_THREAD_NAME);
|
althrd_setname(GetMixerThreadName());
|
||||||
|
|
||||||
DSBCAPS DSBCaps{};
|
DSBCAPS DSBCaps{};
|
||||||
DSBCaps.dwSize = sizeof(DSBCaps);
|
DSBCaps.dwSize = sizeof(DSBCaps);
|
||||||
|
|
@ -299,24 +298,22 @@ FORCE_ALIGN int DSoundPlayback::mixerProc()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSoundPlayback::open(const char *name)
|
void DSoundPlayback::open(std::string_view name)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
if(PlaybackDevices.empty())
|
if(PlaybackDevices.empty())
|
||||||
{
|
{
|
||||||
/* Initialize COM to prevent name truncation */
|
/* Initialize COM to prevent name truncation */
|
||||||
HRESULT hrcom{CoInitialize(nullptr)};
|
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 (0x%lx)!\n", hr);
|
||||||
if(SUCCEEDED(hrcom))
|
|
||||||
CoUninitialize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const GUID *guid{nullptr};
|
const GUID *guid{nullptr};
|
||||||
if(!name && !PlaybackDevices.empty())
|
if(name.empty() && !PlaybackDevices.empty())
|
||||||
{
|
{
|
||||||
name = PlaybackDevices[0].name.c_str();
|
name = PlaybackDevices[0].name;
|
||||||
guid = &PlaybackDevices[0].guid;
|
guid = &PlaybackDevices[0].guid;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -332,7 +329,7 @@ void DSoundPlayback::open(const char *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", name};
|
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||||
}
|
}
|
||||||
guid = &iter->guid;
|
guid = &iter->guid;
|
||||||
}
|
}
|
||||||
|
|
@ -347,7 +344,7 @@ void DSoundPlayback::open(const char *name)
|
||||||
//DirectSound Init code
|
//DirectSound Init code
|
||||||
ComPtr<IDirectSound> ds;
|
ComPtr<IDirectSound> ds;
|
||||||
if(SUCCEEDED(hr))
|
if(SUCCEEDED(hr))
|
||||||
hr = DirectSoundCreate(guid, ds.getPtr(), nullptr);
|
hr = DirectSoundCreate(guid, al::out_ptr(ds), nullptr);
|
||||||
if(SUCCEEDED(hr))
|
if(SUCCEEDED(hr))
|
||||||
hr = ds->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
|
hr = ds->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
|
||||||
if(FAILED(hr))
|
if(FAILED(hr))
|
||||||
|
|
@ -425,49 +422,53 @@ bool DSoundPlayback::reset()
|
||||||
case DevFmtX51: OutputType.dwChannelMask = isRear51 ? X5DOT1REAR : X5DOT1; break;
|
case DevFmtX51: OutputType.dwChannelMask = isRear51 ? X5DOT1REAR : X5DOT1; break;
|
||||||
case DevFmtX61: OutputType.dwChannelMask = X6DOT1; break;
|
case DevFmtX61: OutputType.dwChannelMask = X6DOT1; break;
|
||||||
case DevFmtX71: OutputType.dwChannelMask = X7DOT1; break;
|
case DevFmtX71: OutputType.dwChannelMask = X7DOT1; break;
|
||||||
|
case DevFmtX7144: mDevice->FmtChans = DevFmtX714;
|
||||||
|
/* fall-through */
|
||||||
case DevFmtX714: OutputType.dwChannelMask = X7DOT1DOT4; break;
|
case DevFmtX714: OutputType.dwChannelMask = X7DOT1DOT4; break;
|
||||||
case DevFmtX3D71: OutputType.dwChannelMask = X7DOT1; break;
|
case DevFmtX3D71: OutputType.dwChannelMask = X7DOT1; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
retry_open:
|
do {
|
||||||
hr = S_OK;
|
hr = S_OK;
|
||||||
OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
|
OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||||
OutputType.Format.nChannels = static_cast<WORD>(mDevice->channelsFromFmt());
|
OutputType.Format.nChannels = static_cast<WORD>(mDevice->channelsFromFmt());
|
||||||
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->Frequency;
|
||||||
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;
|
||||||
|
|
||||||
if(OutputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat)
|
if(OutputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat)
|
||||||
{
|
|
||||||
OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
|
||||||
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
|
|
||||||
OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
|
||||||
if(mDevice->FmtType == DevFmtFloat)
|
|
||||||
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
|
||||||
else
|
|
||||||
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
|
||||||
|
|
||||||
mPrimaryBuffer = nullptr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(SUCCEEDED(hr) && !mPrimaryBuffer)
|
|
||||||
{
|
{
|
||||||
DSBUFFERDESC DSBDescription{};
|
OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||||
DSBDescription.dwSize = sizeof(DSBDescription);
|
/* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */
|
||||||
DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
|
||||||
hr = mDS->CreateSoundBuffer(&DSBDescription, mPrimaryBuffer.getPtr(), nullptr);
|
OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||||
}
|
if(mDevice->FmtType == DevFmtFloat)
|
||||||
if(SUCCEEDED(hr))
|
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||||
hr = mPrimaryBuffer->SetFormat(&OutputType.Format);
|
else
|
||||||
}
|
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||||
|
|
||||||
|
mPrimaryBuffer = nullptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(SUCCEEDED(hr) && !mPrimaryBuffer)
|
||||||
|
{
|
||||||
|
DSBUFFERDESC DSBDescription{};
|
||||||
|
DSBDescription.dwSize = sizeof(DSBDescription);
|
||||||
|
DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
||||||
|
hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mPrimaryBuffer), nullptr);
|
||||||
|
}
|
||||||
|
if(SUCCEEDED(hr))
|
||||||
|
hr = mPrimaryBuffer->SetFormat(&OutputType.Format);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(FAILED(hr))
|
||||||
|
break;
|
||||||
|
|
||||||
if(SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
uint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
|
uint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
|
||||||
if(num_updates > MAX_UPDATES)
|
if(num_updates > MAX_UPDATES)
|
||||||
num_updates = MAX_UPDATES;
|
num_updates = MAX_UPDATES;
|
||||||
|
|
@ -480,26 +481,21 @@ retry_open:
|
||||||
DSBDescription.dwBufferBytes = mDevice->BufferSize * OutputType.Format.nBlockAlign;
|
DSBDescription.dwBufferBytes = mDevice->BufferSize * OutputType.Format.nBlockAlign;
|
||||||
DSBDescription.lpwfxFormat = &OutputType.Format;
|
DSBDescription.lpwfxFormat = &OutputType.Format;
|
||||||
|
|
||||||
hr = mDS->CreateSoundBuffer(&DSBDescription, mBuffer.getPtr(), nullptr);
|
hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mBuffer), nullptr);
|
||||||
if(FAILED(hr) && mDevice->FmtType == DevFmtFloat)
|
if(SUCCEEDED(hr) || mDevice->FmtType != DevFmtFloat)
|
||||||
{
|
break;
|
||||||
mDevice->FmtType = DevFmtShort;
|
mDevice->FmtType = DevFmtShort;
|
||||||
goto retry_open;
|
} while(FAILED(hr));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(SUCCEEDED(hr))
|
if(SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
void *ptr;
|
hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, al::out_ptr(mNotifies));
|
||||||
hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, &ptr);
|
|
||||||
if(SUCCEEDED(hr))
|
if(SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
mNotifies = ComPtr<IDirectSoundNotify>{static_cast<IDirectSoundNotify*>(ptr)};
|
|
||||||
|
|
||||||
uint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
|
uint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
|
||||||
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->UpdateSize * OutputType.Format.nBlockAlign;
|
||||||
|
|
@ -550,10 +546,10 @@ struct DSoundCapture final : public BackendBase {
|
||||||
DSoundCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
DSoundCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~DSoundCapture() override;
|
~DSoundCapture() override;
|
||||||
|
|
||||||
void open(const char *name) override;
|
void open(std::string_view name) override;
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
void captureSamples(al::byte *buffer, uint samples) override;
|
void captureSamples(std::byte *buffer, uint samples) override;
|
||||||
uint availableSamples() override;
|
uint availableSamples() override;
|
||||||
|
|
||||||
ComPtr<IDirectSoundCapture> mDSC;
|
ComPtr<IDirectSoundCapture> mDSC;
|
||||||
|
|
@ -562,8 +558,6 @@ struct DSoundCapture final : public BackendBase {
|
||||||
DWORD mCursor{0u};
|
DWORD mCursor{0u};
|
||||||
|
|
||||||
RingBufferPtr mRing;
|
RingBufferPtr mRing;
|
||||||
|
|
||||||
DEF_NEWDEL(DSoundCapture)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DSoundCapture::~DSoundCapture()
|
DSoundCapture::~DSoundCapture()
|
||||||
|
|
@ -577,24 +571,22 @@ DSoundCapture::~DSoundCapture()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DSoundCapture::open(const char *name)
|
void DSoundCapture::open(std::string_view name)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
if(CaptureDevices.empty())
|
if(CaptureDevices.empty())
|
||||||
{
|
{
|
||||||
/* Initialize COM to prevent name truncation */
|
/* Initialize COM to prevent name truncation */
|
||||||
HRESULT hrcom{CoInitialize(nullptr)};
|
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 (0x%lx)!\n", hr);
|
||||||
if(SUCCEEDED(hrcom))
|
|
||||||
CoUninitialize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const GUID *guid{nullptr};
|
const GUID *guid{nullptr};
|
||||||
if(!name && !CaptureDevices.empty())
|
if(name.empty() && !CaptureDevices.empty())
|
||||||
{
|
{
|
||||||
name = CaptureDevices[0].name.c_str();
|
name = CaptureDevices[0].name;
|
||||||
guid = &CaptureDevices[0].guid;
|
guid = &CaptureDevices[0].guid;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -610,7 +602,7 @@ void DSoundCapture::open(const char *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", name};
|
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||||
}
|
}
|
||||||
guid = &iter->guid;
|
guid = &iter->guid;
|
||||||
}
|
}
|
||||||
|
|
@ -641,6 +633,7 @@ void DSoundCapture::open(const char *name)
|
||||||
case DevFmtX61: InputType.dwChannelMask = X6DOT1; break;
|
case DevFmtX61: InputType.dwChannelMask = X6DOT1; break;
|
||||||
case DevFmtX71: InputType.dwChannelMask = X7DOT1; break;
|
case DevFmtX71: InputType.dwChannelMask = X7DOT1; break;
|
||||||
case DevFmtX714: InputType.dwChannelMask = X7DOT1DOT4; break;
|
case DevFmtX714: InputType.dwChannelMask = X7DOT1DOT4; break;
|
||||||
|
case DevFmtX7144:
|
||||||
case DevFmtX3D71:
|
case DevFmtX3D71:
|
||||||
case DevFmtAmbi3D:
|
case DevFmtAmbi3D:
|
||||||
WARN("%s capture not supported\n", DevFmtChannelsString(mDevice->FmtChans));
|
WARN("%s capture not supported\n", DevFmtChannelsString(mDevice->FmtChans));
|
||||||
|
|
@ -657,6 +650,7 @@ void DSoundCapture::open(const char *name)
|
||||||
InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec *
|
InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec *
|
||||||
InputType.Format.nBlockAlign;
|
InputType.Format.nBlockAlign;
|
||||||
InputType.Format.cbSize = 0;
|
InputType.Format.cbSize = 0;
|
||||||
|
/* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */
|
||||||
InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
|
InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
|
||||||
if(mDevice->FmtType == DevFmtFloat)
|
if(mDevice->FmtType == DevFmtFloat)
|
||||||
InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||||
|
|
@ -669,8 +663,7 @@ void DSoundCapture::open(const char *name)
|
||||||
InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint samples{mDevice->BufferSize};
|
const uint samples{std::max(mDevice->BufferSize, mDevice->Frequency/10u)};
|
||||||
samples = maxu(samples, 100 * mDevice->Frequency / 1000);
|
|
||||||
|
|
||||||
DSCBUFFERDESC DSCBDescription{};
|
DSCBUFFERDESC DSCBDescription{};
|
||||||
DSCBDescription.dwSize = sizeof(DSCBDescription);
|
DSCBDescription.dwSize = sizeof(DSCBDescription);
|
||||||
|
|
@ -679,9 +672,9 @@ void DSoundCapture::open(const char *name)
|
||||||
DSCBDescription.lpwfxFormat = &InputType.Format;
|
DSCBDescription.lpwfxFormat = &InputType.Format;
|
||||||
|
|
||||||
//DirectSoundCapture Init code
|
//DirectSoundCapture Init code
|
||||||
hr = DirectSoundCaptureCreate(guid, mDSC.getPtr(), nullptr);
|
hr = DirectSoundCaptureCreate(guid, al::out_ptr(mDSC), nullptr);
|
||||||
if(SUCCEEDED(hr))
|
if(SUCCEEDED(hr))
|
||||||
mDSC->CreateCaptureBuffer(&DSCBDescription, mDSCbuffer.getPtr(), 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->BufferSize, InputType.Format.nBlockAlign, false);
|
||||||
|
|
||||||
|
|
@ -719,8 +712,8 @@ void DSoundCapture::stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSoundCapture::captureSamples(al::byte *buffer, uint samples)
|
void DSoundCapture::captureSamples(std::byte *buffer, uint samples)
|
||||||
{ mRing->read(buffer, samples); }
|
{ std::ignore = mRing->read(buffer, samples); }
|
||||||
|
|
||||||
uint DSoundCapture::availableSamples()
|
uint DSoundCapture::availableSamples()
|
||||||
{
|
{
|
||||||
|
|
@ -743,9 +736,9 @@ uint DSoundCapture::availableSamples()
|
||||||
}
|
}
|
||||||
if(SUCCEEDED(hr))
|
if(SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
mRing->write(ReadPtr1, ReadCnt1/FrameSize);
|
std::ignore = mRing->write(ReadPtr1, ReadCnt1/FrameSize);
|
||||||
if(ReadPtr2 != nullptr && ReadCnt2 > 0)
|
if(ReadPtr2 != nullptr && ReadCnt2 > 0)
|
||||||
mRing->write(ReadPtr2, ReadCnt2/FrameSize);
|
std::ignore = mRing->write(ReadPtr2, ReadCnt2/FrameSize);
|
||||||
hr = mDSCbuffer->Unlock(ReadPtr1, ReadCnt1, ReadPtr2, ReadCnt2);
|
hr = mDSCbuffer->Unlock(ReadPtr1, ReadCnt1, ReadPtr2, ReadCnt2);
|
||||||
mCursor = ReadCursor;
|
mCursor = ReadCursor;
|
||||||
}
|
}
|
||||||
|
|
@ -802,40 +795,32 @@ bool DSoundBackendFactory::init()
|
||||||
bool DSoundBackendFactory::querySupport(BackendType type)
|
bool DSoundBackendFactory::querySupport(BackendType type)
|
||||||
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
||||||
|
|
||||||
std::string DSoundBackendFactory::probe(BackendType type)
|
auto DSoundBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||||
{
|
{
|
||||||
std::string outnames;
|
std::vector<std::string> outnames;
|
||||||
auto add_device = [&outnames](const DevMap &entry) -> void
|
auto add_device = [&outnames](const DevMap &entry) -> void
|
||||||
{
|
{ outnames.emplace_back(entry.name); };
|
||||||
/* +1 to also append the null char (to ensure a null-separated list and
|
|
||||||
* double-null terminated list).
|
|
||||||
*/
|
|
||||||
outnames.append(entry.name.c_str(), entry.name.length()+1);
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Initialize COM to prevent name truncation */
|
/* Initialize COM to prevent name truncation */
|
||||||
HRESULT hr;
|
ComWrapper com{};
|
||||||
HRESULT hrcom{CoInitialize(nullptr)};
|
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case BackendType::Playback:
|
case BackendType::Playback:
|
||||||
PlaybackDevices.clear();
|
PlaybackDevices.clear();
|
||||||
hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
|
if(HRESULT hr{DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices)}; FAILED(hr))
|
||||||
if(FAILED(hr))
|
|
||||||
ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr);
|
ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr);
|
||||||
|
outnames.reserve(PlaybackDevices.size());
|
||||||
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
|
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BackendType::Capture:
|
case BackendType::Capture:
|
||||||
CaptureDevices.clear();
|
CaptureDevices.clear();
|
||||||
hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
|
if(HRESULT hr{DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices)};FAILED(hr))
|
||||||
if(FAILED(hr))
|
|
||||||
ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr);
|
ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr);
|
||||||
|
outnames.reserve(CaptureDevices.size());
|
||||||
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
|
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(SUCCEEDED(hrcom))
|
|
||||||
CoUninitialize();
|
|
||||||
|
|
||||||
return outnames;
|
return outnames;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,15 @@
|
||||||
|
|
||||||
struct DSoundBackendFactory final : public BackendFactory {
|
struct DSoundBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
bool init() override;
|
auto init() -> bool final;
|
||||||
|
|
||||||
bool querySupport(BackendType type) override;
|
auto querySupport(BackendType type) -> bool final;
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||||
|
|
||||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_DSOUND_H */
|
#endif /* BACKENDS_DSOUND_H */
|
||||||
|
|
|
||||||
|
|
@ -22,23 +22,26 @@
|
||||||
|
|
||||||
#include "jack.h"
|
#include "jack.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
|
#include <mutex>
|
||||||
#include <array>
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "alc/alconfig.h"
|
#include "alc/alconfig.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
|
#include "alsem.h"
|
||||||
|
#include "alstring.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 "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
#include "threads.h"
|
|
||||||
|
|
||||||
#include <jack/jack.h>
|
#include <jack/jack.h>
|
||||||
#include <jack/ringbuffer.h>
|
#include <jack/ringbuffer.h>
|
||||||
|
|
@ -46,6 +49,8 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
#ifdef HAVE_DYNLOAD
|
#ifdef HAVE_DYNLOAD
|
||||||
#define JACK_FUNCS(MAGIC) \
|
#define JACK_FUNCS(MAGIC) \
|
||||||
MAGIC(jack_client_open); \
|
MAGIC(jack_client_open); \
|
||||||
|
|
@ -99,19 +104,13 @@ decltype(jack_error_callback) * pjack_error_callback;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
constexpr char JackDefaultAudioType[] = JACK_DEFAULT_AUDIO_TYPE;
|
|
||||||
|
|
||||||
jack_options_t ClientOptions = JackNullOption;
|
jack_options_t ClientOptions = JackNullOption;
|
||||||
|
|
||||||
bool jack_load()
|
bool jack_load()
|
||||||
{
|
{
|
||||||
bool error{false};
|
|
||||||
|
|
||||||
#ifdef HAVE_DYNLOAD
|
#ifdef HAVE_DYNLOAD
|
||||||
if(!jack_handle)
|
if(!jack_handle)
|
||||||
{
|
{
|
||||||
std::string missing_funcs;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define JACKLIB "libjack.dll"
|
#define JACKLIB "libjack.dll"
|
||||||
#else
|
#else
|
||||||
|
|
@ -124,13 +123,10 @@ bool jack_load()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = false;
|
std::string missing_funcs;
|
||||||
#define LOAD_FUNC(f) do { \
|
#define LOAD_FUNC(f) do { \
|
||||||
p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f)); \
|
p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f)); \
|
||||||
if(p##f == nullptr) { \
|
if(p##f == nullptr) missing_funcs += "\n" #f; \
|
||||||
error = true; \
|
|
||||||
missing_funcs += "\n" #f; \
|
|
||||||
} \
|
|
||||||
} while(0)
|
} while(0)
|
||||||
JACK_FUNCS(LOAD_FUNC);
|
JACK_FUNCS(LOAD_FUNC);
|
||||||
#undef LOAD_FUNC
|
#undef LOAD_FUNC
|
||||||
|
|
@ -139,61 +135,66 @@ bool jack_load()
|
||||||
LOAD_SYM(jack_error_callback);
|
LOAD_SYM(jack_error_callback);
|
||||||
#undef LOAD_SYM
|
#undef LOAD_SYM
|
||||||
|
|
||||||
if(error)
|
if(!missing_funcs.empty())
|
||||||
{
|
{
|
||||||
WARN("Missing expected functions:%s\n", missing_funcs.c_str());
|
WARN("Missing expected functions:%s\n", missing_funcs.c_str());
|
||||||
CloseLib(jack_handle);
|
CloseLib(jack_handle);
|
||||||
jack_handle = nullptr;
|
jack_handle = nullptr;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return !error;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct JackDeleter {
|
struct JackDeleter {
|
||||||
void operator()(void *ptr) { jack_free(ptr); }
|
void operator()(void *ptr) { jack_free(ptr); }
|
||||||
};
|
};
|
||||||
using JackPortsPtr = std::unique_ptr<const char*[],JackDeleter>;
|
using JackPortsPtr = std::unique_ptr<const char*[],JackDeleter>; /* NOLINT(*-avoid-c-arrays) */
|
||||||
|
|
||||||
struct DeviceEntry {
|
struct DeviceEntry {
|
||||||
std::string mName;
|
std::string mName;
|
||||||
std::string mPattern;
|
std::string mPattern;
|
||||||
|
|
||||||
|
DeviceEntry() = default;
|
||||||
|
DeviceEntry(const DeviceEntry&) = default;
|
||||||
|
DeviceEntry(DeviceEntry&&) = default;
|
||||||
template<typename T, typename U>
|
template<typename T, typename U>
|
||||||
DeviceEntry(T&& name, U&& pattern)
|
DeviceEntry(T&& name, U&& pattern)
|
||||||
: mName{std::forward<T>(name)}, mPattern{std::forward<U>(pattern)}
|
: mName{std::forward<T>(name)}, mPattern{std::forward<U>(pattern)}
|
||||||
{ }
|
{ }
|
||||||
|
~DeviceEntry();
|
||||||
|
|
||||||
|
DeviceEntry& operator=(const DeviceEntry&) = default;
|
||||||
|
DeviceEntry& operator=(DeviceEntry&&) = default;
|
||||||
};
|
};
|
||||||
|
DeviceEntry::~DeviceEntry() = default;
|
||||||
|
|
||||||
al::vector<DeviceEntry> PlaybackList;
|
std::vector<DeviceEntry> PlaybackList;
|
||||||
|
|
||||||
|
|
||||||
void EnumerateDevices(jack_client_t *client, al::vector<DeviceEntry> &list)
|
void EnumerateDevices(jack_client_t *client, std::vector<DeviceEntry> &list)
|
||||||
{
|
{
|
||||||
std::remove_reference_t<decltype(list)>{}.swap(list);
|
std::remove_reference_t<decltype(list)>{}.swap(list);
|
||||||
|
|
||||||
if(JackPortsPtr ports{jack_get_ports(client, nullptr, JackDefaultAudioType, JackPortIsInput)})
|
if(JackPortsPtr ports{jack_get_ports(client, nullptr, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput)})
|
||||||
{
|
{
|
||||||
for(size_t i{0};ports[i];++i)
|
for(size_t i{0};ports[i];++i)
|
||||||
{
|
{
|
||||||
const char *sep{std::strchr(ports[i], ':')};
|
const std::string_view portname{ports[i]};
|
||||||
if(!sep || ports[i] == sep) continue;
|
const size_t seppos{portname.find(':')};
|
||||||
|
if(seppos == 0 || seppos >= portname.size())
|
||||||
|
continue;
|
||||||
|
|
||||||
const al::span<const char> portdev{ports[i], sep};
|
const auto portdev = portname.substr(0, seppos);
|
||||||
auto check_name = [portdev](const DeviceEntry &entry) -> bool
|
auto check_name = [portdev](const DeviceEntry &entry) -> bool
|
||||||
{
|
{ return entry.mName == portdev; };
|
||||||
const size_t len{portdev.size()};
|
|
||||||
return entry.mName.length() == len
|
|
||||||
&& entry.mName.compare(0, len, portdev.data(), len) == 0;
|
|
||||||
};
|
|
||||||
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;
|
||||||
|
|
||||||
std::string name{portdev.data(), portdev.size()};
|
const auto &entry = list.emplace_back(portdev, std::string{portdev}+":");
|
||||||
list.emplace_back(name, name+":");
|
|
||||||
const auto &entry = list.back();
|
|
||||||
TRACE("Got device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str());
|
TRACE("Got device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str());
|
||||||
}
|
}
|
||||||
/* There are ports but couldn't get device names from them. Add a
|
/* There are ports but couldn't get device names from them. Add a
|
||||||
|
|
@ -202,11 +203,11 @@ void EnumerateDevices(jack_client_t *client, al::vector<DeviceEntry> &list)
|
||||||
if(ports[0] && list.empty())
|
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.\n");
|
||||||
list.emplace_back("JACK", "");
|
list.emplace_back("JACK"sv, ""sv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(auto listopt = ConfigValueStr(nullptr, "jack", "custom-devices"))
|
if(auto listopt = ConfigValueStr({}, "jack", "custom-devices"))
|
||||||
{
|
{
|
||||||
for(size_t strpos{0};strpos < listopt->size();)
|
for(size_t strpos{0};strpos < listopt->size();)
|
||||||
{
|
{
|
||||||
|
|
@ -214,38 +215,32 @@ void EnumerateDevices(jack_client_t *client, al::vector<DeviceEntry> &list)
|
||||||
size_t seppos{listopt->find('=', strpos)};
|
size_t seppos{listopt->find('=', strpos)};
|
||||||
if(seppos >= nextpos || seppos == strpos)
|
if(seppos >= nextpos || seppos == strpos)
|
||||||
{
|
{
|
||||||
const std::string entry{listopt->substr(strpos, nextpos-strpos)};
|
const auto entry = std::string_view{*listopt}.substr(strpos, nextpos-strpos);
|
||||||
ERR("Invalid device entry: \"%s\"\n", entry.c_str());
|
ERR("Invalid device entry: \"%.*s\"\n", al::sizei(entry), entry.data());
|
||||||
if(nextpos != std::string::npos) ++nextpos;
|
if(nextpos != std::string::npos) ++nextpos;
|
||||||
strpos = nextpos;
|
strpos = nextpos;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const al::span<const char> name{listopt->data()+strpos, seppos-strpos};
|
const auto name = std::string_view{*listopt}.substr(strpos, seppos-strpos);
|
||||||
const al::span<const char> pattern{listopt->data()+(seppos+1),
|
const auto pattern = std::string_view{*listopt}.substr(seppos+1,
|
||||||
std::min(nextpos, listopt->size())-(seppos+1)};
|
std::min(nextpos, listopt->size())-(seppos+1));
|
||||||
|
|
||||||
/* Check if this custom pattern already exists in the list. */
|
/* Check if this custom pattern already exists in the list. */
|
||||||
auto check_pattern = [pattern](const DeviceEntry &entry) -> bool
|
auto check_pattern = [pattern](const DeviceEntry &entry) -> bool
|
||||||
{
|
{ return entry.mPattern == pattern; };
|
||||||
const size_t len{pattern.size()};
|
|
||||||
return entry.mPattern.length() == len
|
|
||||||
&& entry.mPattern.compare(0, len, pattern.data(), len) == 0;
|
|
||||||
};
|
|
||||||
auto itemmatch = std::find_if(list.begin(), list.end(), check_pattern);
|
auto itemmatch = std::find_if(list.begin(), list.end(), check_pattern);
|
||||||
if(itemmatch != list.end())
|
if(itemmatch != list.end())
|
||||||
{
|
{
|
||||||
/* If so, replace the name with this custom one. */
|
/* If so, replace the name with this custom one. */
|
||||||
itemmatch->mName.assign(name.data(), name.size());
|
itemmatch->mName = name;
|
||||||
TRACE("Customized device name: %s = %s\n", itemmatch->mName.c_str(),
|
TRACE("Customized device name: %s = %s\n", itemmatch->mName.c_str(),
|
||||||
itemmatch->mPattern.c_str());
|
itemmatch->mPattern.c_str());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Otherwise, add a new device entry. */
|
/* Otherwise, add a new device entry. */
|
||||||
list.emplace_back(std::string{name.data(), name.size()},
|
const auto &entry = list.emplace_back(name, pattern);
|
||||||
std::string{pattern.data(), pattern.size()});
|
|
||||||
const auto &entry = list.back();
|
|
||||||
TRACE("Got custom device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str());
|
TRACE("Got custom device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -295,7 +290,7 @@ struct JackPlayback final : public BackendBase {
|
||||||
|
|
||||||
int mixerProc();
|
int mixerProc();
|
||||||
|
|
||||||
void open(const char *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;
|
||||||
|
|
@ -304,7 +299,7 @@ struct JackPlayback final : public BackendBase {
|
||||||
std::string mPortPattern;
|
std::string mPortPattern;
|
||||||
|
|
||||||
jack_client_t *mClient{nullptr};
|
jack_client_t *mClient{nullptr};
|
||||||
std::array<jack_port_t*,MAX_OUTPUT_CHANNELS> mPort{};
|
std::array<jack_port_t*,MaxOutputChannels> mPort{};
|
||||||
|
|
||||||
std::mutex mMutex;
|
std::mutex mMutex;
|
||||||
|
|
||||||
|
|
@ -315,8 +310,6 @@ struct JackPlayback final : public BackendBase {
|
||||||
|
|
||||||
std::atomic<bool> mKillNow{true};
|
std::atomic<bool> mKillNow{true};
|
||||||
std::thread mThread;
|
std::thread mThread;
|
||||||
|
|
||||||
DEF_NEWDEL(JackPlayback)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
JackPlayback::~JackPlayback()
|
JackPlayback::~JackPlayback()
|
||||||
|
|
@ -336,22 +329,22 @@ JackPlayback::~JackPlayback()
|
||||||
|
|
||||||
int JackPlayback::processRt(jack_nframes_t numframes) noexcept
|
int JackPlayback::processRt(jack_nframes_t numframes) noexcept
|
||||||
{
|
{
|
||||||
std::array<jack_default_audio_sample_t*,MAX_OUTPUT_CHANNELS> out;
|
auto outptrs = std::array<jack_default_audio_sample_t*,MaxOutputChannels>{};
|
||||||
size_t numchans{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;
|
||||||
out[numchans++] = static_cast<float*>(jack_port_get_buffer(port, numframes));
|
outptrs[numchans++] = static_cast<float*>(jack_port_get_buffer(port, numframes));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto dst = al::span{outptrs}.first(numchans);
|
||||||
if(mPlaying.load(std::memory_order_acquire)) LIKELY
|
if(mPlaying.load(std::memory_order_acquire)) LIKELY
|
||||||
mDevice->renderSamples({out.data(), numchans}, static_cast<uint>(numframes));
|
mDevice->renderSamples(dst, static_cast<uint>(numframes));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto clear_buf = [numframes](float *outbuf) -> void
|
std::for_each(dst.begin(), dst.end(), [numframes](float *outbuf) -> void
|
||||||
{ std::fill_n(outbuf, numframes, 0.0f); };
|
{ std::fill_n(outbuf, numframes, 0.0f); });
|
||||||
std::for_each(out.begin(), out.begin()+numchans, clear_buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -360,53 +353,46 @@ int JackPlayback::processRt(jack_nframes_t numframes) noexcept
|
||||||
|
|
||||||
int JackPlayback::process(jack_nframes_t numframes) noexcept
|
int JackPlayback::process(jack_nframes_t numframes) noexcept
|
||||||
{
|
{
|
||||||
std::array<jack_default_audio_sample_t*,MAX_OUTPUT_CHANNELS> out;
|
std::array<al::span<float>,MaxOutputChannels> out;
|
||||||
size_t numchans{0};
|
size_t numchans{0};
|
||||||
for(auto port : mPort)
|
for(auto port : mPort)
|
||||||
{
|
{
|
||||||
if(!port) break;
|
if(!port) break;
|
||||||
out[numchans++] = static_cast<float*>(jack_port_get_buffer(port, numframes));
|
out[numchans++] = {static_cast<float*>(jack_port_get_buffer(port, numframes)), numframes};
|
||||||
}
|
}
|
||||||
|
|
||||||
jack_nframes_t total{0};
|
size_t total{0};
|
||||||
if(mPlaying.load(std::memory_order_acquire)) LIKELY
|
if(mPlaying.load(std::memory_order_acquire)) LIKELY
|
||||||
{
|
{
|
||||||
auto data = mRing->getReadVector();
|
auto data = mRing->getReadVector();
|
||||||
jack_nframes_t todo{minu(numframes, static_cast<uint>(data.first.len))};
|
const auto update_size = size_t{mDevice->UpdateSize};
|
||||||
auto write_first = [&data,numchans,todo](float *outbuf) -> float*
|
|
||||||
{
|
|
||||||
const float *RESTRICT in = reinterpret_cast<float*>(data.first.buf);
|
|
||||||
auto deinterlace_input = [&in,numchans]() noexcept -> float
|
|
||||||
{
|
|
||||||
float ret{*in};
|
|
||||||
in += numchans;
|
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
std::generate_n(outbuf, todo, deinterlace_input);
|
|
||||||
data.first.buf += sizeof(float);
|
|
||||||
return outbuf + todo;
|
|
||||||
};
|
|
||||||
std::transform(out.begin(), out.begin()+numchans, out.begin(), write_first);
|
|
||||||
total += todo;
|
|
||||||
|
|
||||||
todo = minu(numframes-total, static_cast<uint>(data.second.len));
|
const auto outlen = size_t{numframes / update_size};
|
||||||
if(todo > 0)
|
const auto len1 = size_t{std::min(data.first.len/update_size, outlen)};
|
||||||
|
const auto len2 = size_t{std::min(data.second.len/update_size, outlen-len1)};
|
||||||
|
|
||||||
|
auto src = al::span{reinterpret_cast<float*>(data.first.buf), update_size*len1*numchans};
|
||||||
|
for(size_t i{0};i < len1;++i)
|
||||||
{
|
{
|
||||||
auto write_second = [&data,numchans,todo](float *outbuf) -> float*
|
for(size_t c{0};c < numchans;++c)
|
||||||
{
|
{
|
||||||
const float *RESTRICT in = reinterpret_cast<float*>(data.second.buf);
|
const auto iter = std::copy_n(src.begin(), update_size, out[c].begin());
|
||||||
auto deinterlace_input = [&in,numchans]() noexcept -> float
|
out[c] = {iter, out[c].end()};
|
||||||
{
|
src = src.subspan(update_size);
|
||||||
float ret{*in};
|
}
|
||||||
in += numchans;
|
total += update_size;
|
||||||
return ret;
|
}
|
||||||
};
|
|
||||||
std::generate_n(outbuf, todo, deinterlace_input);
|
src = al::span{reinterpret_cast<float*>(data.second.buf), update_size*len2*numchans};
|
||||||
data.second.buf += sizeof(float);
|
for(size_t i{0};i < len2;++i)
|
||||||
return outbuf + todo;
|
{
|
||||||
};
|
for(size_t c{0};c < numchans;++c)
|
||||||
std::transform(out.begin(), out.begin()+numchans, out.begin(), write_second);
|
{
|
||||||
total += todo;
|
const auto iter = std::copy_n(src.begin(), update_size, out[c].begin());
|
||||||
|
out[c] = {iter, out[c].end()};
|
||||||
|
src = src.subspan(update_size);
|
||||||
|
}
|
||||||
|
total += update_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
mRing->readAdvance(total);
|
mRing->readAdvance(total);
|
||||||
|
|
@ -415,8 +401,8 @@ int JackPlayback::process(jack_nframes_t numframes) noexcept
|
||||||
|
|
||||||
if(numframes > total)
|
if(numframes > total)
|
||||||
{
|
{
|
||||||
const jack_nframes_t todo{numframes - total};
|
auto clear_buf = [](const al::span<float> outbuf) -> void
|
||||||
auto clear_buf = [todo](float *outbuf) -> void { std::fill_n(outbuf, todo, 0.0f); };
|
{ std::fill(outbuf.begin(), outbuf.end(), 0.0f); };
|
||||||
std::for_each(out.begin(), out.begin()+numchans, clear_buf);
|
std::for_each(out.begin(), out.begin()+numchans, clear_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -426,45 +412,70 @@ int JackPlayback::process(jack_nframes_t numframes) noexcept
|
||||||
int JackPlayback::mixerProc()
|
int JackPlayback::mixerProc()
|
||||||
{
|
{
|
||||||
SetRTPriority();
|
SetRTPriority();
|
||||||
althrd_setname(MIXER_THREAD_NAME);
|
althrd_setname(GetMixerThreadName());
|
||||||
|
|
||||||
const size_t frame_step{mDevice->channelsFromFmt()};
|
const auto update_size = uint{mDevice->UpdateSize};
|
||||||
|
const auto num_channels = size_t{mDevice->channelsFromFmt()};
|
||||||
|
auto outptrs = std::vector<float*>(num_channels);
|
||||||
|
|
||||||
while(!mKillNow.load(std::memory_order_acquire)
|
while(!mKillNow.load(std::memory_order_acquire)
|
||||||
&& mDevice->Connected.load(std::memory_order_acquire))
|
&& mDevice->Connected.load(std::memory_order_acquire))
|
||||||
{
|
{
|
||||||
if(mRing->writeSpace() < mDevice->UpdateSize)
|
if(mRing->writeSpace() < update_size)
|
||||||
{
|
{
|
||||||
mSem.wait();
|
mSem.wait();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto data = mRing->getWriteVector();
|
auto data = mRing->getWriteVector();
|
||||||
size_t todo{data.first.len + data.second.len};
|
const auto len1 = size_t{data.first.len / update_size};
|
||||||
todo -= todo%mDevice->UpdateSize;
|
const auto len2 = size_t{data.second.len / update_size};
|
||||||
|
|
||||||
const auto len1 = static_cast<uint>(minz(data.first.len, todo));
|
std::lock_guard<std::mutex> dlock{mMutex};
|
||||||
const auto len2 = static_cast<uint>(minz(data.second.len, todo-len1));
|
auto buffer = al::span{reinterpret_cast<float*>(data.first.buf),
|
||||||
|
data.first.len*num_channels};
|
||||||
std::lock_guard<std::mutex> _{mMutex};
|
auto bufiter = buffer.begin();
|
||||||
mDevice->renderSamples(data.first.buf, len1, frame_step);
|
for(size_t i{0};i < len1;++i)
|
||||||
|
{
|
||||||
|
std::generate_n(outptrs.begin(), outptrs.size(), [&bufiter,update_size]
|
||||||
|
{
|
||||||
|
auto ret = al::to_address(bufiter);
|
||||||
|
bufiter += ptrdiff_t(update_size);
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
mDevice->renderSamples(outptrs, update_size);
|
||||||
|
}
|
||||||
if(len2 > 0)
|
if(len2 > 0)
|
||||||
mDevice->renderSamples(data.second.buf, len2, frame_step);
|
{
|
||||||
mRing->writeAdvance(todo);
|
buffer = al::span{reinterpret_cast<float*>(data.second.buf),
|
||||||
|
data.second.len*num_channels};
|
||||||
|
bufiter = buffer.begin();
|
||||||
|
for(size_t i{0};i < len2;++i)
|
||||||
|
{
|
||||||
|
std::generate_n(outptrs.begin(), outptrs.size(), [&bufiter,update_size]
|
||||||
|
{
|
||||||
|
auto ret = al::to_address(bufiter);
|
||||||
|
bufiter += ptrdiff_t(update_size);
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
mDevice->renderSamples(outptrs, update_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mRing->writeAdvance((len1+len2) * update_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void JackPlayback::open(const char *name)
|
void JackPlayback::open(std::string_view name)
|
||||||
{
|
{
|
||||||
if(!mClient)
|
if(!mClient)
|
||||||
{
|
{
|
||||||
const PathNamePair &binname = GetProcBinary();
|
const PathNamePair &binname = GetProcBinary();
|
||||||
const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()};
|
const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()};
|
||||||
|
|
||||||
jack_status_t status;
|
jack_status_t status{};
|
||||||
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,
|
||||||
|
|
@ -481,9 +492,9 @@ void JackPlayback::open(const char *name)
|
||||||
if(PlaybackList.empty())
|
if(PlaybackList.empty())
|
||||||
EnumerateDevices(mClient, PlaybackList);
|
EnumerateDevices(mClient, PlaybackList);
|
||||||
|
|
||||||
if(!name && !PlaybackList.empty())
|
if(name.empty() && !PlaybackList.empty())
|
||||||
{
|
{
|
||||||
name = PlaybackList[0].mName.c_str();
|
name = PlaybackList[0].mName;
|
||||||
mPortPattern = PlaybackList[0].mPattern;
|
mPortPattern = PlaybackList[0].mPattern;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -493,14 +504,10 @@ void JackPlayback::open(const char *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", name?name:""};
|
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||||
mPortPattern = iter->mPattern;
|
mPortPattern = iter->mPattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
mRTMixing = GetConfigValueBool(name, "jack", "rt-mix", true);
|
|
||||||
jack_set_process_callback(mClient,
|
|
||||||
mRTMixing ? &JackPlayback::processRtC : &JackPlayback::processC, this);
|
|
||||||
|
|
||||||
mDevice->DeviceName = name;
|
mDevice->DeviceName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -511,6 +518,10 @@ 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);
|
||||||
|
jack_set_process_callback(mClient,
|
||||||
|
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.
|
||||||
*/
|
*/
|
||||||
|
|
@ -525,9 +536,9 @@ bool JackPlayback::reset()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const char *devname{mDevice->DeviceName.c_str()};
|
const std::string_view devname{mDevice->DeviceName};
|
||||||
uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)};
|
uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)};
|
||||||
bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize);
|
bufsize = std::max(NextPowerOf2(bufsize), mDevice->UpdateSize);
|
||||||
mDevice->BufferSize = bufsize + mDevice->UpdateSize;
|
mDevice->BufferSize = bufsize + mDevice->UpdateSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -535,27 +546,27 @@ bool JackPlayback::reset()
|
||||||
mDevice->FmtType = DevFmtFloat;
|
mDevice->FmtType = DevFmtFloat;
|
||||||
|
|
||||||
int port_num{0};
|
int port_num{0};
|
||||||
auto ports_end = mPort.begin() + mDevice->channelsFromFmt();
|
auto ports = al::span{mPort}.first(mDevice->channelsFromFmt());
|
||||||
auto bad_port = mPort.begin();
|
auto bad_port = ports.begin();
|
||||||
while(bad_port != ports_end)
|
while(bad_port != ports.end())
|
||||||
{
|
{
|
||||||
std::string name{"channel_" + std::to_string(++port_num)};
|
std::string name{"channel_" + std::to_string(++port_num)};
|
||||||
*bad_port = jack_port_register(mClient, name.c_str(), JackDefaultAudioType,
|
*bad_port = jack_port_register(mClient, name.c_str(), JACK_DEFAULT_AUDIO_TYPE,
|
||||||
JackPortIsOutput | JackPortIsTerminal, 0);
|
JackPortIsOutput | JackPortIsTerminal, 0);
|
||||||
if(!*bad_port) break;
|
if(!*bad_port) break;
|
||||||
++bad_port;
|
++bad_port;
|
||||||
}
|
}
|
||||||
if(bad_port != ports_end)
|
if(bad_port != ports.end())
|
||||||
{
|
{
|
||||||
ERR("Failed to register enough JACK ports for %s output\n",
|
ERR("Failed to register enough JACK ports for %s output\n",
|
||||||
DevFmtChannelsString(mDevice->FmtChans));
|
DevFmtChannelsString(mDevice->FmtChans));
|
||||||
if(bad_port == mPort.begin()) return false;
|
if(bad_port == ports.begin()) return false;
|
||||||
|
|
||||||
if(bad_port == mPort.begin()+1)
|
if(bad_port == ports.begin()+1)
|
||||||
mDevice->FmtChans = DevFmtMono;
|
mDevice->FmtChans = DevFmtMono;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ports_end = mPort.begin()+2;
|
const auto ports_end = ports.begin()+2;
|
||||||
while(bad_port != ports_end)
|
while(bad_port != ports_end)
|
||||||
{
|
{
|
||||||
jack_port_unregister(mClient, *(--bad_port));
|
jack_port_unregister(mClient, *(--bad_port));
|
||||||
|
|
@ -575,10 +586,10 @@ 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 char *devname{mDevice->DeviceName.c_str()};
|
const std::string_view devname{mDevice->DeviceName};
|
||||||
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(), JackDefaultAudioType,
|
JackPortsPtr pnames{jack_get_ports(mClient, mPortPattern.c_str(), JACK_DEFAULT_AUDIO_TYPE,
|
||||||
JackPortIsInput)};
|
JackPortIsInput)};
|
||||||
if(!pnames)
|
if(!pnames)
|
||||||
{
|
{
|
||||||
|
|
@ -586,7 +597,7 @@ void JackPlayback::start()
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "No playback ports found"};
|
throw al::backend_exception{al::backend_error::DeviceError, "No playback ports found"};
|
||||||
}
|
}
|
||||||
|
|
||||||
for(size_t i{0};i < al::size(mPort) && mPort[i];++i)
|
for(size_t i{0};i < std::size(mPort) && mPort[i];++i)
|
||||||
{
|
{
|
||||||
if(!pnames[i])
|
if(!pnames[i])
|
||||||
{
|
{
|
||||||
|
|
@ -613,7 +624,7 @@ void JackPlayback::start()
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)};
|
uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)};
|
||||||
bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize);
|
bufsize = std::max(NextPowerOf2(bufsize), mDevice->UpdateSize);
|
||||||
mDevice->BufferSize = bufsize + mDevice->UpdateSize;
|
mDevice->BufferSize = bufsize + mDevice->UpdateSize;
|
||||||
|
|
||||||
mRing = RingBuffer::Create(bufsize, mDevice->frameSizeFromFmt(), true);
|
mRing = RingBuffer::Create(bufsize, mDevice->frameSizeFromFmt(), true);
|
||||||
|
|
@ -651,10 +662,9 @@ void JackPlayback::stop()
|
||||||
|
|
||||||
ClockLatency JackPlayback::getClockLatency()
|
ClockLatency JackPlayback::getClockLatency()
|
||||||
{
|
{
|
||||||
ClockLatency ret;
|
std::lock_guard<std::mutex> dlock{mMutex};
|
||||||
|
ClockLatency ret{};
|
||||||
std::lock_guard<std::mutex> _{mMutex};
|
ret.ClockTime = mDevice->getClockTime();
|
||||||
ret.ClockTime = GetDeviceClockTime(mDevice);
|
|
||||||
ret.Latency = std::chrono::seconds{mRing ? mRing->readSpace() : mDevice->UpdateSize};
|
ret.Latency = std::chrono::seconds{mRing ? mRing->readSpace() : mDevice->UpdateSize};
|
||||||
ret.Latency /= mDevice->Frequency;
|
ret.Latency /= mDevice->Frequency;
|
||||||
|
|
||||||
|
|
@ -674,7 +684,7 @@ bool JackBackendFactory::init()
|
||||||
if(!jack_load())
|
if(!jack_load())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(!GetConfigValueBool(nullptr, "jack", "spawn-server", false))
|
if(!GetConfigValueBool({}, "jack", "spawn-server", false))
|
||||||
ClientOptions = static_cast<jack_options_t>(ClientOptions | JackNoStartServer);
|
ClientOptions = static_cast<jack_options_t>(ClientOptions | JackNoStartServer);
|
||||||
|
|
||||||
const PathNamePair &binname = GetProcBinary();
|
const PathNamePair &binname = GetProcBinary();
|
||||||
|
|
@ -682,7 +692,7 @@ bool JackBackendFactory::init()
|
||||||
|
|
||||||
void (*old_error_cb)(const char*){&jack_error_callback ? jack_error_callback : nullptr};
|
void (*old_error_cb)(const char*){&jack_error_callback ? jack_error_callback : nullptr};
|
||||||
jack_set_error_function(jack_msg_handler);
|
jack_set_error_function(jack_msg_handler);
|
||||||
jack_status_t status;
|
jack_status_t status{};
|
||||||
jack_client_t *client{jack_client_open(client_name, ClientOptions, &status, nullptr)};
|
jack_client_t *client{jack_client_open(client_name, ClientOptions, &status, nullptr)};
|
||||||
jack_set_error_function(old_error_cb);
|
jack_set_error_function(old_error_cb);
|
||||||
if(!client)
|
if(!client)
|
||||||
|
|
@ -700,18 +710,15 @@ bool JackBackendFactory::init()
|
||||||
bool JackBackendFactory::querySupport(BackendType type)
|
bool JackBackendFactory::querySupport(BackendType type)
|
||||||
{ return (type == BackendType::Playback); }
|
{ return (type == BackendType::Playback); }
|
||||||
|
|
||||||
std::string JackBackendFactory::probe(BackendType type)
|
auto JackBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||||
{
|
{
|
||||||
std::string outnames;
|
std::vector<std::string> outnames;
|
||||||
auto append_name = [&outnames](const DeviceEntry &entry) -> void
|
auto append_name = [&outnames](const DeviceEntry &entry) -> void
|
||||||
{
|
{ outnames.emplace_back(entry.mName); };
|
||||||
/* Includes null char. */
|
|
||||||
outnames.append(entry.mName.c_str(), entry.mName.length()+1);
|
|
||||||
};
|
|
||||||
|
|
||||||
const PathNamePair &binname = GetProcBinary();
|
const PathNamePair &binname = GetProcBinary();
|
||||||
const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()};
|
const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()};
|
||||||
jack_status_t status;
|
jack_status_t status{};
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case BackendType::Playback:
|
case BackendType::Playback:
|
||||||
|
|
@ -722,6 +729,7 @@ std::string JackBackendFactory::probe(BackendType type)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
WARN("jack_client_open() failed, 0x%02x\n", status);
|
WARN("jack_client_open() failed, 0x%02x\n", status);
|
||||||
|
outnames.reserve(PlaybackList.size());
|
||||||
std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
|
std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
|
||||||
break;
|
break;
|
||||||
case BackendType::Capture:
|
case BackendType::Capture:
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,15 @@
|
||||||
|
|
||||||
struct JackBackendFactory final : public BackendFactory {
|
struct JackBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
bool init() override;
|
auto init() -> bool final;
|
||||||
|
|
||||||
bool querySupport(BackendType type) override;
|
auto querySupport(BackendType type) -> bool final;
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||||
|
|
||||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_JACK_H */
|
#endif /* BACKENDS_JACK_H */
|
||||||
|
|
|
||||||
|
|
@ -30,16 +30,14 @@ namespace {
|
||||||
struct LoopbackBackend final : public BackendBase {
|
struct LoopbackBackend final : public BackendBase {
|
||||||
LoopbackBackend(DeviceBase *device) noexcept : BackendBase{device} { }
|
LoopbackBackend(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
|
|
||||||
void open(const char *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;
|
||||||
|
|
||||||
DEF_NEWDEL(LoopbackBackend)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void LoopbackBackend::open(const char *name)
|
void LoopbackBackend::open(std::string_view name)
|
||||||
{
|
{
|
||||||
mDevice->DeviceName = name;
|
mDevice->DeviceName = name;
|
||||||
}
|
}
|
||||||
|
|
@ -65,8 +63,8 @@ bool LoopbackBackendFactory::init()
|
||||||
bool LoopbackBackendFactory::querySupport(BackendType)
|
bool LoopbackBackendFactory::querySupport(BackendType)
|
||||||
{ return true; }
|
{ return true; }
|
||||||
|
|
||||||
std::string LoopbackBackendFactory::probe(BackendType)
|
auto LoopbackBackendFactory::enumerate(BackendType) -> std::vector<std::string>
|
||||||
{ return std::string{}; }
|
{ return {}; }
|
||||||
|
|
||||||
BackendPtr LoopbackBackendFactory::createBackend(DeviceBase *device, BackendType)
|
BackendPtr LoopbackBackendFactory::createBackend(DeviceBase *device, BackendType)
|
||||||
{ return BackendPtr{new LoopbackBackend{device}}; }
|
{ return BackendPtr{new LoopbackBackend{device}}; }
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,15 @@
|
||||||
|
|
||||||
struct LoopbackBackendFactory final : public BackendFactory {
|
struct LoopbackBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
bool init() override;
|
auto init() -> bool final;
|
||||||
|
|
||||||
bool querySupport(BackendType type) override;
|
auto querySupport(BackendType type) -> bool final;
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||||
|
|
||||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_LOOPBACK_H */
|
#endif /* BACKENDS_LOOPBACK_H */
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,11 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "core/device.h"
|
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
|
#include "alstring.h"
|
||||||
|
#include "althrd_setname.h"
|
||||||
|
#include "core/device.h"
|
||||||
#include "core/helpers.h"
|
#include "core/helpers.h"
|
||||||
#include "threads.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -41,8 +42,9 @@ namespace {
|
||||||
using std::chrono::seconds;
|
using std::chrono::seconds;
|
||||||
using std::chrono::milliseconds;
|
using std::chrono::milliseconds;
|
||||||
using std::chrono::nanoseconds;
|
using std::chrono::nanoseconds;
|
||||||
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
constexpr char nullDevice[] = "No Output";
|
[[nodiscard]] constexpr auto GetDeviceName() noexcept { return "No Output"sv; }
|
||||||
|
|
||||||
|
|
||||||
struct NullBackend final : public BackendBase {
|
struct NullBackend final : public BackendBase {
|
||||||
|
|
@ -50,15 +52,13 @@ struct NullBackend final : public BackendBase {
|
||||||
|
|
||||||
int mixerProc();
|
int mixerProc();
|
||||||
|
|
||||||
void open(const char *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;
|
||||||
|
|
||||||
std::atomic<bool> mKillNow{true};
|
std::atomic<bool> mKillNow{true};
|
||||||
std::thread mThread;
|
std::thread mThread;
|
||||||
|
|
||||||
DEF_NEWDEL(NullBackend)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
int NullBackend::mixerProc()
|
int NullBackend::mixerProc()
|
||||||
|
|
@ -66,7 +66,7 @@ int NullBackend::mixerProc()
|
||||||
const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2};
|
const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2};
|
||||||
|
|
||||||
SetRTPriority();
|
SetRTPriority();
|
||||||
althrd_setname(MIXER_THREAD_NAME);
|
althrd_setname(GetMixerThreadName());
|
||||||
|
|
||||||
int64_t done{0};
|
int64_t done{0};
|
||||||
auto start = std::chrono::steady_clock::now();
|
auto start = std::chrono::steady_clock::now();
|
||||||
|
|
@ -105,13 +105,13 @@ int NullBackend::mixerProc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void NullBackend::open(const char *name)
|
void NullBackend::open(std::string_view name)
|
||||||
{
|
{
|
||||||
if(!name)
|
if(name.empty())
|
||||||
name = nullDevice;
|
name = GetDeviceName();
|
||||||
else if(strcmp(name, nullDevice) != 0)
|
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 \"%.*s\" not found",
|
||||||
name};
|
al::sizei(name), name.data()};
|
||||||
|
|
||||||
mDevice->DeviceName = name;
|
mDevice->DeviceName = name;
|
||||||
}
|
}
|
||||||
|
|
@ -150,19 +150,17 @@ bool NullBackendFactory::init()
|
||||||
bool NullBackendFactory::querySupport(BackendType type)
|
bool NullBackendFactory::querySupport(BackendType type)
|
||||||
{ return (type == BackendType::Playback); }
|
{ return (type == BackendType::Playback); }
|
||||||
|
|
||||||
std::string NullBackendFactory::probe(BackendType type)
|
auto NullBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||||
{
|
{
|
||||||
std::string outnames;
|
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case BackendType::Playback:
|
case BackendType::Playback:
|
||||||
/* Includes null char. */
|
/* Include null char. */
|
||||||
outnames.append(nullDevice, sizeof(nullDevice));
|
return std::vector{std::string{GetDeviceName()}};
|
||||||
break;
|
|
||||||
case BackendType::Capture:
|
case BackendType::Capture:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return outnames;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr NullBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
BackendPtr NullBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,15 @@
|
||||||
|
|
||||||
struct NullBackendFactory final : public BackendFactory {
|
struct NullBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
bool init() override;
|
auto init() -> bool final;
|
||||||
|
|
||||||
bool querySupport(BackendType type) override;
|
auto querySupport(BackendType type) -> bool final;
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||||
|
|
||||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_NULL_H */
|
#endif /* BACKENDS_NULL_H */
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,11 @@
|
||||||
#include "oboe.h"
|
#include "oboe.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
|
#include "alstring.h"
|
||||||
#include "core/device.h"
|
#include "core/device.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
|
|
@ -17,7 +18,9 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr char device_name[] = "Oboe Default";
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto GetDeviceName() noexcept { return "Oboe Default"sv; }
|
||||||
|
|
||||||
|
|
||||||
struct OboePlayback final : public BackendBase, public oboe::AudioStreamCallback {
|
struct OboePlayback final : public BackendBase, public oboe::AudioStreamCallback {
|
||||||
|
|
@ -28,7 +31,9 @@ struct OboePlayback final : public BackendBase, public oboe::AudioStreamCallback
|
||||||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData,
|
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData,
|
||||||
int32_t numFrames) override;
|
int32_t numFrames) override;
|
||||||
|
|
||||||
void open(const char *name) override;
|
void onErrorAfterClose(oboe::AudioStream* /* audioStream */, oboe::Result /* error */) override;
|
||||||
|
|
||||||
|
void open(std::string_view name) override;
|
||||||
bool reset() override;
|
bool reset() override;
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
|
|
@ -46,14 +51,20 @@ oboe::DataCallbackResult OboePlayback::onAudioReady(oboe::AudioStream *oboeStrea
|
||||||
return oboe::DataCallbackResult::Continue;
|
return oboe::DataCallbackResult::Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OboePlayback::onErrorAfterClose(oboe::AudioStream*, oboe::Result error)
|
||||||
void OboePlayback::open(const char *name)
|
|
||||||
{
|
{
|
||||||
if(!name)
|
if(error == oboe::Result::ErrorDisconnected)
|
||||||
name = device_name;
|
mDevice->handleDisconnect("Oboe AudioStream was disconnected: %s", oboe::convertToText(error));
|
||||||
else if(std::strcmp(name, device_name) != 0)
|
TRACE("Error was %s", oboe::convertToText(error));
|
||||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
}
|
||||||
name};
|
|
||||||
|
void OboePlayback::open(std::string_view name)
|
||||||
|
{
|
||||||
|
if(name.empty())
|
||||||
|
name = GetDeviceName();
|
||||||
|
else if(name != GetDeviceName())
|
||||||
|
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
|
||||||
|
al::sizei(name), name.data()};
|
||||||
|
|
||||||
/* Open a basic output stream, just to ensure it can work. */
|
/* Open a basic output stream, just to ensure it can work. */
|
||||||
oboe::ManagedStream stream;
|
oboe::ManagedStream stream;
|
||||||
|
|
@ -72,6 +83,7 @@ bool OboePlayback::reset()
|
||||||
oboe::AudioStreamBuilder builder;
|
oboe::AudioStreamBuilder builder;
|
||||||
builder.setDirection(oboe::Direction::Output);
|
builder.setDirection(oboe::Direction::Output);
|
||||||
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
|
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
|
||||||
|
builder.setUsage(oboe::Usage::Game);
|
||||||
/* Don't let Oboe convert. We should be able to handle anything it gives
|
/* Don't let Oboe convert. We should be able to handle anything it gives
|
||||||
* back.
|
* back.
|
||||||
*/
|
*/
|
||||||
|
|
@ -135,7 +147,7 @@ bool OboePlayback::reset()
|
||||||
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: %s",
|
||||||
oboe::convertToText(result)};
|
oboe::convertToText(result)};
|
||||||
mStream->setBufferSizeInFrames(mini(static_cast<int32_t>(mDevice->BufferSize),
|
mStream->setBufferSizeInFrames(std::min(static_cast<int32_t>(mDevice->BufferSize),
|
||||||
mStream->getBufferCapacityInFrames()));
|
mStream->getBufferCapacityInFrames()));
|
||||||
TRACE("Got stream with properties:\n%s", oboe::convertToText(mStream.get()));
|
TRACE("Got stream with properties:\n%s", oboe::convertToText(mStream.get()));
|
||||||
|
|
||||||
|
|
@ -164,6 +176,9 @@ bool OboePlayback::reset()
|
||||||
mDevice->FmtType = DevFmtInt;
|
mDevice->FmtType = DevFmtInt;
|
||||||
break;
|
break;
|
||||||
case oboe::AudioFormat::I24:
|
case oboe::AudioFormat::I24:
|
||||||
|
#endif
|
||||||
|
#if OBOE_VERSION_MAJOR > 1 || (OBOE_VERSION_MAJOR == 1 && OBOE_VERSION_MINOR >= 8)
|
||||||
|
case oboe::AudioFormat::IEC61937:
|
||||||
#endif
|
#endif
|
||||||
case oboe::AudioFormat::Unspecified:
|
case oboe::AudioFormat::Unspecified:
|
||||||
case oboe::AudioFormat::Invalid:
|
case oboe::AudioFormat::Invalid:
|
||||||
|
|
@ -177,9 +192,9 @@ bool OboePlayback::reset()
|
||||||
* FramesPerBurst may not necessarily be correct, but hopefully it can act as a minimum
|
* FramesPerBurst may not necessarily be correct, but hopefully it can act as a minimum
|
||||||
* update size.
|
* update size.
|
||||||
*/
|
*/
|
||||||
mDevice->UpdateSize = maxu(mDevice->Frequency / 100,
|
mDevice->UpdateSize = std::max(mDevice->Frequency/100u,
|
||||||
static_cast<uint32_t>(mStream->getFramesPerBurst()));
|
static_cast<uint32_t>(mStream->getFramesPerBurst()));
|
||||||
mDevice->BufferSize = maxu(mDevice->UpdateSize * 2,
|
mDevice->BufferSize = std::max(mDevice->UpdateSize*2u,
|
||||||
static_cast<uint32_t>(mStream->getBufferSizeInFrames()));
|
static_cast<uint32_t>(mStream->getBufferSizeInFrames()));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -197,8 +212,7 @@ void OboePlayback::stop()
|
||||||
{
|
{
|
||||||
oboe::Result result{mStream->stop()};
|
oboe::Result result{mStream->stop()};
|
||||||
if(result != oboe::Result::OK)
|
if(result != oboe::Result::OK)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to stop stream: %s",
|
ERR("Failed to stop stream: %s\n", oboe::convertToText(result));
|
||||||
oboe::convertToText(result)};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -212,28 +226,28 @@ struct OboeCapture final : public BackendBase, public oboe::AudioStreamCallback
|
||||||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData,
|
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData,
|
||||||
int32_t numFrames) override;
|
int32_t numFrames) override;
|
||||||
|
|
||||||
void open(const char *name) override;
|
void open(std::string_view name) override;
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
void captureSamples(al::byte *buffer, uint samples) override;
|
void captureSamples(std::byte *buffer, uint samples) override;
|
||||||
uint availableSamples() override;
|
uint availableSamples() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
oboe::DataCallbackResult OboeCapture::onAudioReady(oboe::AudioStream*, void *audioData,
|
oboe::DataCallbackResult OboeCapture::onAudioReady(oboe::AudioStream*, void *audioData,
|
||||||
int32_t numFrames)
|
int32_t numFrames)
|
||||||
{
|
{
|
||||||
mRing->write(audioData, static_cast<uint32_t>(numFrames));
|
std::ignore = mRing->write(audioData, static_cast<uint32_t>(numFrames));
|
||||||
return oboe::DataCallbackResult::Continue;
|
return oboe::DataCallbackResult::Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void OboeCapture::open(const char *name)
|
void OboeCapture::open(std::string_view name)
|
||||||
{
|
{
|
||||||
if(!name)
|
if(name.empty())
|
||||||
name = device_name;
|
name = GetDeviceName();
|
||||||
else if(std::strcmp(name, device_name) != 0)
|
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 \"%.*s\" not found",
|
||||||
name};
|
al::sizei(name), name.data()};
|
||||||
|
|
||||||
oboe::AudioStreamBuilder builder;
|
oboe::AudioStreamBuilder builder;
|
||||||
builder.setDirection(oboe::Direction::Input)
|
builder.setDirection(oboe::Direction::Input)
|
||||||
|
|
@ -259,6 +273,7 @@ void OboeCapture::open(const char *name)
|
||||||
case DevFmtX61:
|
case DevFmtX61:
|
||||||
case DevFmtX71:
|
case DevFmtX71:
|
||||||
case DevFmtX714:
|
case DevFmtX714:
|
||||||
|
case DevFmtX7144:
|
||||||
case DevFmtX3D71:
|
case DevFmtX3D71:
|
||||||
case DevFmtAmbi3D:
|
case DevFmtAmbi3D:
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported",
|
throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported",
|
||||||
|
|
@ -297,7 +312,7 @@ void OboeCapture::open(const char *name)
|
||||||
TRACE("Got stream with properties:\n%s", oboe::convertToText(mStream.get()));
|
TRACE("Got stream with properties:\n%s", oboe::convertToText(mStream.get()));
|
||||||
|
|
||||||
/* Ensure a minimum ringbuffer size of 100ms. */
|
/* Ensure a minimum ringbuffer size of 100ms. */
|
||||||
mRing = RingBuffer::Create(maxu(mDevice->BufferSize, mDevice->Frequency/10),
|
mRing = RingBuffer::Create(std::max(mDevice->BufferSize, mDevice->Frequency/10u),
|
||||||
static_cast<uint32_t>(mStream->getBytesPerFrame()), false);
|
static_cast<uint32_t>(mStream->getBytesPerFrame()), false);
|
||||||
|
|
||||||
mDevice->DeviceName = name;
|
mDevice->DeviceName = name;
|
||||||
|
|
@ -315,15 +330,14 @@ 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)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to stop stream: %s",
|
ERR("Failed to stop stream: %s\n", oboe::convertToText(result));
|
||||||
oboe::convertToText(result)};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint OboeCapture::availableSamples()
|
uint OboeCapture::availableSamples()
|
||||||
{ return static_cast<uint>(mRing->readSpace()); }
|
{ return static_cast<uint>(mRing->readSpace()); }
|
||||||
|
|
||||||
void OboeCapture::captureSamples(al::byte *buffer, uint samples)
|
void OboeCapture::captureSamples(std::byte *buffer, uint samples)
|
||||||
{ mRing->read(buffer, samples); }
|
{ std::ignore = mRing->read(buffer, samples); }
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
@ -332,16 +346,15 @@ bool OboeBackendFactory::init() { return true; }
|
||||||
bool OboeBackendFactory::querySupport(BackendType type)
|
bool OboeBackendFactory::querySupport(BackendType type)
|
||||||
{ return type == BackendType::Playback || type == BackendType::Capture; }
|
{ return type == BackendType::Playback || type == BackendType::Capture; }
|
||||||
|
|
||||||
std::string OboeBackendFactory::probe(BackendType type)
|
auto OboeBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||||
{
|
{
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case BackendType::Playback:
|
case BackendType::Playback:
|
||||||
case BackendType::Capture:
|
case BackendType::Capture:
|
||||||
/* Includes null char. */
|
return std::vector{std::string{GetDeviceName()}};
|
||||||
return std::string{device_name, sizeof(device_name)};
|
|
||||||
}
|
}
|
||||||
return std::string{};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr OboeBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
BackendPtr OboeBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,15 @@
|
||||||
|
|
||||||
struct OboeBackendFactory final : public BackendFactory {
|
struct OboeBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
bool init() override;
|
auto init() -> bool final;
|
||||||
|
|
||||||
bool querySupport(BackendType type) override;
|
auto querySupport(BackendType type) -> bool final;
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||||
|
|
||||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_OBOE_H */
|
#endif /* BACKENDS_OBOE_H */
|
||||||
|
|
|
||||||
|
|
@ -23,23 +23,26 @@
|
||||||
|
|
||||||
#include "opensl.h"
|
#include "opensl.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
#include <new>
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <mutex>
|
||||||
|
#include <new>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include "albit.h"
|
#include "albit.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
|
#include "alsem.h"
|
||||||
|
#include "alstring.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 "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
#include "threads.h"
|
|
||||||
|
|
||||||
#include <SLES/OpenSLES.h>
|
#include <SLES/OpenSLES.h>
|
||||||
#include <SLES/OpenSLES_Android.h>
|
#include <SLES/OpenSLES_Android.h>
|
||||||
|
|
@ -48,15 +51,17 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
/* 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
|
||||||
#define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
|
#define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
|
||||||
|
|
||||||
|
|
||||||
constexpr char opensl_device[] = "OpenSL";
|
[[nodiscard]] constexpr auto GetDeviceName() noexcept { return "OpenSL"sv; }
|
||||||
|
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
constexpr SLuint32 GetChannelMask(DevFmtChannels chans) noexcept
|
constexpr SLuint32 GetChannelMask(DevFmtChannels chans) noexcept
|
||||||
{
|
{
|
||||||
switch(chans)
|
switch(chans)
|
||||||
|
|
@ -80,6 +85,7 @@ constexpr SLuint32 GetChannelMask(DevFmtChannels chans) noexcept
|
||||||
SL_SPEAKER_BACK_RIGHT | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT |
|
SL_SPEAKER_BACK_RIGHT | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT |
|
||||||
SL_SPEAKER_TOP_FRONT_LEFT | SL_SPEAKER_TOP_FRONT_RIGHT | SL_SPEAKER_TOP_BACK_LEFT |
|
SL_SPEAKER_TOP_FRONT_LEFT | SL_SPEAKER_TOP_FRONT_RIGHT | SL_SPEAKER_TOP_BACK_LEFT |
|
||||||
SL_SPEAKER_TOP_BACK_RIGHT;
|
SL_SPEAKER_TOP_BACK_RIGHT;
|
||||||
|
case DevFmtX7144:
|
||||||
case DevFmtAmbi3D:
|
case DevFmtAmbi3D:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -159,12 +165,10 @@ struct OpenSLPlayback final : public BackendBase {
|
||||||
~OpenSLPlayback() override;
|
~OpenSLPlayback() override;
|
||||||
|
|
||||||
void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
|
void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
|
||||||
static void processC(SLAndroidSimpleBufferQueueItf bq, void *context) noexcept
|
|
||||||
{ static_cast<OpenSLPlayback*>(context)->process(bq); }
|
|
||||||
|
|
||||||
int mixerProc();
|
int mixerProc();
|
||||||
|
|
||||||
void open(const char *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;
|
||||||
|
|
@ -189,8 +193,6 @@ struct OpenSLPlayback final : public BackendBase {
|
||||||
|
|
||||||
std::atomic<bool> mKillNow{true};
|
std::atomic<bool> mKillNow{true};
|
||||||
std::thread mThread;
|
std::thread mThread;
|
||||||
|
|
||||||
DEF_NEWDEL(OpenSLPlayback)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
OpenSLPlayback::~OpenSLPlayback()
|
OpenSLPlayback::~OpenSLPlayback()
|
||||||
|
|
@ -229,7 +231,7 @@ void OpenSLPlayback::process(SLAndroidSimpleBufferQueueItf) noexcept
|
||||||
int OpenSLPlayback::mixerProc()
|
int OpenSLPlayback::mixerProc()
|
||||||
{
|
{
|
||||||
SetRTPriority();
|
SetRTPriority();
|
||||||
althrd_setname(MIXER_THREAD_NAME);
|
althrd_setname(GetMixerThreadName());
|
||||||
|
|
||||||
SLPlayItf player;
|
SLPlayItf player;
|
||||||
SLAndroidSimpleBufferQueueItf bufferQueue;
|
SLAndroidSimpleBufferQueueItf bufferQueue;
|
||||||
|
|
@ -312,13 +314,13 @@ int OpenSLPlayback::mixerProc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void OpenSLPlayback::open(const char *name)
|
void OpenSLPlayback::open(std::string_view name)
|
||||||
{
|
{
|
||||||
if(!name)
|
if(name.empty())
|
||||||
name = opensl_device;
|
name = GetDeviceName();
|
||||||
else if(strcmp(name, opensl_device) != 0)
|
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 \"%.*s\" not found",
|
||||||
name};
|
al::sizei(name), name.data()};
|
||||||
|
|
||||||
/* 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;
|
||||||
|
|
@ -375,74 +377,6 @@ bool OpenSLPlayback::reset()
|
||||||
|
|
||||||
mRing = nullptr;
|
mRing = nullptr;
|
||||||
|
|
||||||
#if 0
|
|
||||||
if(!mDevice->Flags.get<FrequencyRequest>())
|
|
||||||
{
|
|
||||||
/* FIXME: Disabled until I figure out how to get the Context needed for
|
|
||||||
* the getSystemService call.
|
|
||||||
*/
|
|
||||||
JNIEnv *env = Android_GetJNIEnv();
|
|
||||||
jobject jctx = Android_GetContext();
|
|
||||||
|
|
||||||
/* Get necessary stuff for using java.lang.Integer,
|
|
||||||
* android.content.Context, and android.media.AudioManager.
|
|
||||||
*/
|
|
||||||
jclass int_cls = JCALL(env,FindClass)("java/lang/Integer");
|
|
||||||
jmethodID int_parseint = JCALL(env,GetStaticMethodID)(int_cls,
|
|
||||||
"parseInt", "(Ljava/lang/String;)I"
|
|
||||||
);
|
|
||||||
TRACE("Integer: %p, parseInt: %p\n", int_cls, int_parseint);
|
|
||||||
|
|
||||||
jclass ctx_cls = JCALL(env,FindClass)("android/content/Context");
|
|
||||||
jfieldID ctx_audsvc = JCALL(env,GetStaticFieldID)(ctx_cls,
|
|
||||||
"AUDIO_SERVICE", "Ljava/lang/String;"
|
|
||||||
);
|
|
||||||
jmethodID ctx_getSysSvc = JCALL(env,GetMethodID)(ctx_cls,
|
|
||||||
"getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"
|
|
||||||
);
|
|
||||||
TRACE("Context: %p, AUDIO_SERVICE: %p, getSystemService: %p\n",
|
|
||||||
ctx_cls, ctx_audsvc, ctx_getSysSvc);
|
|
||||||
|
|
||||||
jclass audmgr_cls = JCALL(env,FindClass)("android/media/AudioManager");
|
|
||||||
jfieldID audmgr_prop_out_srate = JCALL(env,GetStaticFieldID)(audmgr_cls,
|
|
||||||
"PROPERTY_OUTPUT_SAMPLE_RATE", "Ljava/lang/String;"
|
|
||||||
);
|
|
||||||
jmethodID audmgr_getproperty = JCALL(env,GetMethodID)(audmgr_cls,
|
|
||||||
"getProperty", "(Ljava/lang/String;)Ljava/lang/String;"
|
|
||||||
);
|
|
||||||
TRACE("AudioManager: %p, PROPERTY_OUTPUT_SAMPLE_RATE: %p, getProperty: %p\n",
|
|
||||||
audmgr_cls, audmgr_prop_out_srate, audmgr_getproperty);
|
|
||||||
|
|
||||||
const char *strchars;
|
|
||||||
jstring strobj;
|
|
||||||
|
|
||||||
/* Now make the calls. */
|
|
||||||
//AudioManager audMgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
|
|
||||||
strobj = JCALL(env,GetStaticObjectField)(ctx_cls, ctx_audsvc);
|
|
||||||
jobject audMgr = JCALL(env,CallObjectMethod)(jctx, ctx_getSysSvc, strobj);
|
|
||||||
strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr);
|
|
||||||
TRACE("Context.getSystemService(%s) = %p\n", strchars, audMgr);
|
|
||||||
JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
|
|
||||||
|
|
||||||
//String srateStr = audMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
|
|
||||||
strobj = JCALL(env,GetStaticObjectField)(audmgr_cls, audmgr_prop_out_srate);
|
|
||||||
jstring srateStr = JCALL(env,CallObjectMethod)(audMgr, audmgr_getproperty, strobj);
|
|
||||||
strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr);
|
|
||||||
TRACE("audMgr.getProperty(%s) = %p\n", strchars, srateStr);
|
|
||||||
JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
|
|
||||||
|
|
||||||
//int sampleRate = Integer.parseInt(srateStr);
|
|
||||||
sampleRate = JCALL(env,CallStaticIntMethod)(int_cls, int_parseint, srateStr);
|
|
||||||
|
|
||||||
strchars = JCALL(env,GetStringUTFChars)(srateStr, nullptr);
|
|
||||||
TRACE("Got system sample rate %uhz (%s)\n", sampleRate, strchars);
|
|
||||||
JCALL(env,ReleaseStringUTFChars)(srateStr, strchars);
|
|
||||||
|
|
||||||
if(!sampleRate) sampleRate = device->Frequency;
|
|
||||||
else sampleRate = maxu(sampleRate, MIN_OUTPUT_RATE);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
mDevice->FmtChans = DevFmtStereo;
|
mDevice->FmtChans = DevFmtStereo;
|
||||||
mDevice->FmtType = DevFmtShort;
|
mDevice->FmtType = DevFmtShort;
|
||||||
|
|
||||||
|
|
@ -564,7 +498,9 @@ void OpenSLPlayback::start()
|
||||||
PrintErr(result, "bufferQueue->GetInterface");
|
PrintErr(result, "bufferQueue->GetInterface");
|
||||||
if(SL_RESULT_SUCCESS == result)
|
if(SL_RESULT_SUCCESS == result)
|
||||||
{
|
{
|
||||||
result = VCALL(bufferQueue,RegisterCallback)(&OpenSLPlayback::processC, this);
|
result = VCALL(bufferQueue,RegisterCallback)(
|
||||||
|
[](SLAndroidSimpleBufferQueueItf bq, void *context) noexcept
|
||||||
|
{ static_cast<OpenSLPlayback*>(context)->process(bq); }, this);
|
||||||
PrintErr(result, "bufferQueue->RegisterCallback");
|
PrintErr(result, "bufferQueue->RegisterCallback");
|
||||||
}
|
}
|
||||||
if(SL_RESULT_SUCCESS != result)
|
if(SL_RESULT_SUCCESS != result)
|
||||||
|
|
@ -628,8 +564,8 @@ ClockLatency OpenSLPlayback::getClockLatency()
|
||||||
{
|
{
|
||||||
ClockLatency ret;
|
ClockLatency ret;
|
||||||
|
|
||||||
std::lock_guard<std::mutex> _{mMutex};
|
std::lock_guard<std::mutex> dlock{mMutex};
|
||||||
ret.ClockTime = GetDeviceClockTime(mDevice);
|
ret.ClockTime = mDevice->getClockTime();
|
||||||
ret.Latency = std::chrono::seconds{mRing->readSpace() * mDevice->UpdateSize};
|
ret.Latency = std::chrono::seconds{mRing->readSpace() * mDevice->UpdateSize};
|
||||||
ret.Latency /= mDevice->Frequency;
|
ret.Latency /= mDevice->Frequency;
|
||||||
|
|
||||||
|
|
@ -642,13 +578,11 @@ struct OpenSLCapture final : public BackendBase {
|
||||||
~OpenSLCapture() override;
|
~OpenSLCapture() override;
|
||||||
|
|
||||||
void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
|
void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
|
||||||
static void processC(SLAndroidSimpleBufferQueueItf bq, void *context) noexcept
|
|
||||||
{ static_cast<OpenSLCapture*>(context)->process(bq); }
|
|
||||||
|
|
||||||
void open(const char *name) override;
|
void open(std::string_view name) override;
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
void captureSamples(al::byte *buffer, uint samples) override;
|
void captureSamples(std::byte *buffer, uint samples) override;
|
||||||
uint availableSamples() override;
|
uint availableSamples() override;
|
||||||
|
|
||||||
/* engine interfaces */
|
/* engine interfaces */
|
||||||
|
|
@ -662,8 +596,6 @@ struct OpenSLCapture final : public BackendBase {
|
||||||
uint mSplOffset{0u};
|
uint mSplOffset{0u};
|
||||||
|
|
||||||
uint mFrameSize{0};
|
uint mFrameSize{0};
|
||||||
|
|
||||||
DEF_NEWDEL(OpenSLCapture)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
OpenSLCapture::~OpenSLCapture()
|
OpenSLCapture::~OpenSLCapture()
|
||||||
|
|
@ -686,13 +618,13 @@ void OpenSLCapture::process(SLAndroidSimpleBufferQueueItf) noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void OpenSLCapture::open(const char* name)
|
void OpenSLCapture::open(std::string_view name)
|
||||||
{
|
{
|
||||||
if(!name)
|
if(name.empty())
|
||||||
name = opensl_device;
|
name = GetDeviceName();
|
||||||
else if(strcmp(name, opensl_device) != 0)
|
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 \"%.*s\" not found",
|
||||||
name};
|
al::sizei(name), name.data()};
|
||||||
|
|
||||||
SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)};
|
SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)};
|
||||||
PrintErr(result, "slCreateEngine");
|
PrintErr(result, "slCreateEngine");
|
||||||
|
|
@ -710,10 +642,10 @@ void OpenSLCapture::open(const char* 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{maxu(mDevice->BufferSize, mDevice->Frequency/10)};
|
uint length{std::max(mDevice->BufferSize, mDevice->Frequency/10u)};
|
||||||
/* Ensure the per-chunk length is at least 10ms, and no more than 50ms. */
|
/* Ensure the per-chunk length is at least 10ms, and no more than 50ms. */
|
||||||
uint update_len{clampu(mDevice->BufferSize/3, mDevice->Frequency/100,
|
uint update_len{std::clamp(mDevice->BufferSize/3u, mDevice->Frequency/100u,
|
||||||
mDevice->Frequency/100*5)};
|
mDevice->Frequency/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);
|
||||||
|
|
@ -813,13 +745,15 @@ void OpenSLCapture::open(const char* name)
|
||||||
}
|
}
|
||||||
if(SL_RESULT_SUCCESS == result)
|
if(SL_RESULT_SUCCESS == result)
|
||||||
{
|
{
|
||||||
result = VCALL(bufferQueue,RegisterCallback)(&OpenSLCapture::processC, this);
|
result = VCALL(bufferQueue,RegisterCallback)(
|
||||||
|
[](SLAndroidSimpleBufferQueueItf bq, void *context) noexcept
|
||||||
|
{ static_cast<OpenSLCapture*>(context)->process(bq); }, this);
|
||||||
PrintErr(result, "bufferQueue->RegisterCallback");
|
PrintErr(result, "bufferQueue->RegisterCallback");
|
||||||
}
|
}
|
||||||
if(SL_RESULT_SUCCESS == result)
|
if(SL_RESULT_SUCCESS == result)
|
||||||
{
|
{
|
||||||
const uint chunk_size{mDevice->UpdateSize * mFrameSize};
|
const uint chunk_size{mDevice->UpdateSize * mFrameSize};
|
||||||
const auto silence = (mDevice->FmtType == DevFmtUByte) ? al::byte{0x80} : al::byte{0};
|
const auto silence = (mDevice->FmtType == DevFmtUByte) ? std::byte{0x80} : std::byte{0};
|
||||||
|
|
||||||
auto data = mRing->getWriteVector();
|
auto data = mRing->getWriteVector();
|
||||||
std::fill_n(data.first.buf, data.first.len*chunk_size, silence);
|
std::fill_n(data.first.buf, data.first.len*chunk_size, silence);
|
||||||
|
|
@ -883,7 +817,7 @@ void OpenSLCapture::stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenSLCapture::captureSamples(al::byte *buffer, uint samples)
|
void OpenSLCapture::captureSamples(std::byte *buffer, uint samples)
|
||||||
{
|
{
|
||||||
const uint update_size{mDevice->UpdateSize};
|
const uint update_size{mDevice->UpdateSize};
|
||||||
const uint chunk_size{update_size * mFrameSize};
|
const uint chunk_size{update_size * mFrameSize};
|
||||||
|
|
@ -895,7 +829,7 @@ void OpenSLCapture::captureSamples(al::byte *buffer, uint samples)
|
||||||
auto rdata = mRing->getReadVector();
|
auto rdata = mRing->getReadVector();
|
||||||
for(uint i{0};i < samples;)
|
for(uint i{0};i < samples;)
|
||||||
{
|
{
|
||||||
const uint rem{minu(samples - i, update_size - mSplOffset)};
|
const uint rem{std::min(samples - i, update_size - mSplOffset)};
|
||||||
std::copy_n(rdata.first.buf + mSplOffset*size_t{mFrameSize}, rem*size_t{mFrameSize},
|
std::copy_n(rdata.first.buf + mSplOffset*size_t{mFrameSize}, rem*size_t{mFrameSize},
|
||||||
buffer + i*size_t{mFrameSize});
|
buffer + i*size_t{mFrameSize});
|
||||||
|
|
||||||
|
|
@ -932,7 +866,7 @@ void OpenSLCapture::captureSamples(al::byte *buffer, uint samples)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* For each buffer chunk that was fully read, queue another writable buffer
|
/* For each buffer chunk that was fully read, queue another writable buffer
|
||||||
* chunk to keep the OpenSL queue full. This is rather convulated, as a
|
* chunk to keep the OpenSL queue full. This is rather convoluted, as a
|
||||||
* result of the ring buffer holding more elements than are writable at a
|
* result of the ring buffer holding more elements than are writable at a
|
||||||
* given time. The end of the write vector increments when the read pointer
|
* given time. The end of the write vector increments when the read pointer
|
||||||
* advances, which will "expose" a previously unwritable element. So for
|
* advances, which will "expose" a previously unwritable element. So for
|
||||||
|
|
@ -975,18 +909,15 @@ bool OSLBackendFactory::init() { return true; }
|
||||||
bool OSLBackendFactory::querySupport(BackendType type)
|
bool OSLBackendFactory::querySupport(BackendType type)
|
||||||
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
||||||
|
|
||||||
std::string OSLBackendFactory::probe(BackendType type)
|
auto OSLBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||||
{
|
{
|
||||||
std::string outnames;
|
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case BackendType::Playback:
|
case BackendType::Playback:
|
||||||
case BackendType::Capture:
|
case BackendType::Capture:
|
||||||
/* Includes null char. */
|
return std::vector{std::string{GetDeviceName()}};
|
||||||
outnames.append(opensl_device, sizeof(opensl_device));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return outnames;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr OSLBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
BackendPtr OSLBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,15 @@
|
||||||
|
|
||||||
struct OSLBackendFactory final : public BackendFactory {
|
struct OSLBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
bool init() override;
|
auto init() -> bool final;
|
||||||
|
|
||||||
bool querySupport(BackendType type) override;
|
auto querySupport(BackendType type) -> bool final;
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||||
|
|
||||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_OSL_H */
|
#endif /* BACKENDS_OSL_H */
|
||||||
|
|
|
||||||
|
|
@ -31,27 +31,26 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <new>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <system_error>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "albyte.h"
|
|
||||||
#include "alc/alconfig.h"
|
#include "alc/alconfig.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "aloptional.h"
|
#include "alstring.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 "threads.h"
|
|
||||||
#include "vector.h"
|
|
||||||
|
|
||||||
#include <sys/soundcard.h>
|
#include <sys/soundcard.h>
|
||||||
|
|
||||||
|
|
@ -83,117 +82,148 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr char DefaultName[] = "OSS Default";
|
using namespace std::string_literals;
|
||||||
std::string DefaultPlayback{"/dev/dsp"};
|
using namespace std::string_view_literals;
|
||||||
std::string DefaultCapture{"/dev/dsp"};
|
|
||||||
|
[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "OSS Default"sv; }
|
||||||
|
|
||||||
|
std::string DefaultPlayback{"/dev/dsp"s};
|
||||||
|
std::string DefaultCapture{"/dev/dsp"s};
|
||||||
|
|
||||||
struct DevMap {
|
struct DevMap {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string device_name;
|
std::string device_name;
|
||||||
|
|
||||||
|
template<typename T, typename U>
|
||||||
|
DevMap(T&& name_, U&& devname_)
|
||||||
|
: name{std::forward<T>(name_)}, device_name{std::forward<U>(devname_)}
|
||||||
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
al::vector<DevMap> PlaybackDevices;
|
std::vector<DevMap> PlaybackDevices;
|
||||||
al::vector<DevMap> CaptureDevices;
|
std::vector<DevMap> CaptureDevices;
|
||||||
|
|
||||||
|
|
||||||
#ifdef ALC_OSS_COMPAT
|
#ifdef ALC_OSS_COMPAT
|
||||||
|
|
||||||
#define DSP_CAP_OUTPUT 0x00020000
|
#define DSP_CAP_OUTPUT 0x00020000
|
||||||
#define DSP_CAP_INPUT 0x00010000
|
#define DSP_CAP_INPUT 0x00010000
|
||||||
void ALCossListPopulate(al::vector<DevMap> &devlist, int type)
|
void ALCossListPopulate(std::vector<DevMap> &devlist, int type)
|
||||||
{
|
{
|
||||||
devlist.emplace_back(DevMap{DefaultName, (type==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback});
|
devlist.emplace_back(GetDefaultName(), (type==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
void ALCossListAppend(al::vector<DevMap> &list, al::span<const char> handle, al::span<const char> path)
|
class FileHandle {
|
||||||
|
int mFd{-1};
|
||||||
|
|
||||||
|
public:
|
||||||
|
FileHandle() = default;
|
||||||
|
FileHandle(const FileHandle&) = delete;
|
||||||
|
FileHandle& operator=(const FileHandle&) = delete;
|
||||||
|
~FileHandle() { if(mFd != -1) ::close(mFd); }
|
||||||
|
|
||||||
|
template<typename ...Args>
|
||||||
|
[[nodiscard]] auto open(const char *fname, Args&& ...args) -> bool
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
mFd = ::open(fname, std::forward<Args>(args)...);
|
||||||
|
return mFd != -1;
|
||||||
|
}
|
||||||
|
void close()
|
||||||
|
{
|
||||||
|
if(mFd != -1)
|
||||||
|
::close(mFd);
|
||||||
|
mFd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
auto get() const noexcept -> int { return mFd; }
|
||||||
|
};
|
||||||
|
|
||||||
|
void ALCossListAppend(std::vector<DevMap> &list, std::string_view handle, std::string_view path)
|
||||||
{
|
{
|
||||||
#ifdef ALC_OSS_DEVNODE_TRUC
|
#ifdef ALC_OSS_DEVNODE_TRUC
|
||||||
for(size_t i{0};i < path.size();++i)
|
for(size_t i{0};i < path.size();++i)
|
||||||
{
|
{
|
||||||
if(path[i] == '.' && handle.size() + i >= path.size())
|
if(path[i] == '.' && handle.size() >= path.size() - i)
|
||||||
{
|
{
|
||||||
const size_t hoffset{handle.size() + i - path.size()};
|
const size_t hoffset{handle.size() + i - path.size()};
|
||||||
if(strncmp(path.data() + i, handle.data() + hoffset, path.size() - i) == 0)
|
if(strncmp(path.data() + i, handle.data() + hoffset, path.size() - i) == 0)
|
||||||
handle = handle.first(hoffset);
|
handle = handle.substr(0, hoffset);
|
||||||
path = path.first(i);
|
path = path.substr(0, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if(handle.empty())
|
if(handle.empty())
|
||||||
handle = path;
|
handle = path;
|
||||||
|
|
||||||
std::string basename{handle.data(), handle.size()};
|
auto match_devname = [path](const DevMap &entry) -> bool
|
||||||
std::string devname{path.data(), path.size()};
|
{ return entry.device_name == path; };
|
||||||
|
|
||||||
auto match_devname = [&devname](const DevMap &entry) -> bool
|
|
||||||
{ return entry.device_name == devname; };
|
|
||||||
if(std::find_if(list.cbegin(), list.cend(), match_devname) != list.cend())
|
if(std::find_if(list.cbegin(), list.cend(), match_devname) != list.cend())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto checkName = [&list](const std::string &name) -> bool
|
auto checkName = [&list](const std::string_view name) -> bool
|
||||||
{
|
{
|
||||||
auto match_name = [&name](const DevMap &entry) -> bool { return entry.name == name; };
|
auto match_name = [name](const DevMap &entry) -> bool { return entry.name == name; };
|
||||||
return std::find_if(list.cbegin(), list.cend(), match_name) != list.cend();
|
return std::find_if(list.cbegin(), list.cend(), match_name) != list.cend();
|
||||||
};
|
};
|
||||||
int count{1};
|
int count{1};
|
||||||
std::string newname{basename};
|
std::string newname{handle};
|
||||||
while(checkName(newname))
|
while(checkName(newname))
|
||||||
{
|
{
|
||||||
newname = basename;
|
newname = handle;
|
||||||
newname += " #";
|
newname += " #";
|
||||||
newname += std::to_string(++count);
|
newname += std::to_string(++count);
|
||||||
}
|
}
|
||||||
|
|
||||||
list.emplace_back(DevMap{std::move(newname), std::move(devname)});
|
const DevMap &entry = list.emplace_back(std::move(newname), path);
|
||||||
const DevMap &entry = list.back();
|
|
||||||
|
|
||||||
TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
|
TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALCossListPopulate(al::vector<DevMap> &devlist, int type_flag)
|
void ALCossListPopulate(std::vector<DevMap> &devlist, int type_flag)
|
||||||
{
|
{
|
||||||
int fd{open("/dev/mixer", O_RDONLY)};
|
oss_sysinfo si{};
|
||||||
if(fd < 0)
|
FileHandle file;
|
||||||
|
if(!file.open("/dev/mixer", O_RDONLY))
|
||||||
{
|
{
|
||||||
TRACE("Could not open /dev/mixer: %s\n", strerror(errno));
|
TRACE("Could not open /dev/mixer: %s\n", std::generic_category().message(errno).c_str());
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
oss_sysinfo si;
|
if(ioctl(file.get(), SNDCTL_SYSINFO, &si) == -1)
|
||||||
if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1)
|
|
||||||
{
|
{
|
||||||
TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
|
TRACE("SNDCTL_SYSINFO failed: %s\n", std::generic_category().message(errno).c_str());
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int i{0};i < si.numaudios;i++)
|
for(int i{0};i < si.numaudios;i++)
|
||||||
{
|
{
|
||||||
oss_audioinfo ai;
|
oss_audioinfo ai{};
|
||||||
ai.dev = i;
|
ai.dev = i;
|
||||||
if(ioctl(fd, SNDCTL_AUDIOINFO, &ai) == -1)
|
if(ioctl(file.get(), SNDCTL_AUDIOINFO, &ai) == -1)
|
||||||
{
|
{
|
||||||
ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i, strerror(errno));
|
ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i,
|
||||||
|
std::generic_category().message(errno).c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if(!(ai.caps&type_flag) || ai.devnode[0] == '\0')
|
if(!(ai.caps&type_flag) || ai.devnode[0] == '\0')
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
al::span<const char> handle;
|
std::string_view handle;
|
||||||
if(ai.handle[0] != '\0')
|
if(ai.handle[0] != '\0')
|
||||||
handle = {ai.handle, strnlen(ai.handle, sizeof(ai.handle))};
|
handle = {ai.handle, strnlen(ai.handle, sizeof(ai.handle))};
|
||||||
else
|
else
|
||||||
handle = {ai.name, strnlen(ai.name, sizeof(ai.name))};
|
handle = {ai.name, strnlen(ai.name, sizeof(ai.name))};
|
||||||
al::span<const char> devnode{ai.devnode, strnlen(ai.devnode, sizeof(ai.devnode))};
|
const std::string_view devnode{ai.devnode, strnlen(ai.devnode, sizeof(ai.devnode))};
|
||||||
|
|
||||||
ALCossListAppend(devlist, handle, devnode);
|
ALCossListAppend(devlist, handle, devnode);
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if(fd >= 0)
|
file.close();
|
||||||
close(fd);
|
|
||||||
fd = -1;
|
|
||||||
|
|
||||||
const char *defdev{((type_flag==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback).c_str()};
|
const char *defdev{((type_flag==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback).c_str()};
|
||||||
auto iter = std::find_if(devlist.cbegin(), devlist.cend(),
|
auto iter = std::find_if(devlist.cbegin(), devlist.cend(),
|
||||||
|
|
@ -201,7 +231,7 @@ done:
|
||||||
{ return entry.device_name == defdev; }
|
{ return entry.device_name == defdev; }
|
||||||
);
|
);
|
||||||
if(iter == devlist.cend())
|
if(iter == devlist.cend())
|
||||||
devlist.insert(devlist.begin(), DevMap{DefaultName, defdev});
|
devlist.insert(devlist.begin(), DevMap{GetDefaultName(), defdev});
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DevMap entry{std::move(*iter)};
|
DevMap entry{std::move(*iter)};
|
||||||
|
|
@ -231,19 +261,17 @@ struct OSSPlayback final : public BackendBase {
|
||||||
|
|
||||||
int mixerProc();
|
int mixerProc();
|
||||||
|
|
||||||
void open(const char *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;
|
||||||
|
|
||||||
int mFd{-1};
|
int mFd{-1};
|
||||||
|
|
||||||
al::vector<al::byte> mMixData;
|
std::vector<std::byte> mMixData;
|
||||||
|
|
||||||
std::atomic<bool> mKillNow{true};
|
std::atomic<bool> mKillNow{true};
|
||||||
std::thread mThread;
|
std::thread mThread;
|
||||||
|
|
||||||
DEF_NEWDEL(OSSPlayback)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
OSSPlayback::~OSSPlayback()
|
OSSPlayback::~OSSPlayback()
|
||||||
|
|
@ -257,7 +285,7 @@ OSSPlayback::~OSSPlayback()
|
||||||
int OSSPlayback::mixerProc()
|
int OSSPlayback::mixerProc()
|
||||||
{
|
{
|
||||||
SetRTPriority();
|
SetRTPriority();
|
||||||
althrd_setname(MIXER_THREAD_NAME);
|
althrd_setname(GetMixerThreadName());
|
||||||
|
|
||||||
const size_t frame_step{mDevice->channelsFromFmt()};
|
const size_t frame_step{mDevice->channelsFromFmt()};
|
||||||
const size_t frame_size{mDevice->frameSizeFromFmt()};
|
const size_t frame_size{mDevice->frameSizeFromFmt()};
|
||||||
|
|
@ -269,38 +297,38 @@ int OSSPlayback::mixerProc()
|
||||||
pollitem.fd = mFd;
|
pollitem.fd = mFd;
|
||||||
pollitem.events = POLLOUT;
|
pollitem.events = POLLOUT;
|
||||||
|
|
||||||
int pret{poll(&pollitem, 1, 1000)};
|
if(int pret{poll(&pollitem, 1, 1000)}; pret < 0)
|
||||||
if(pret < 0)
|
|
||||||
{
|
{
|
||||||
if(errno == EINTR || errno == EAGAIN)
|
if(errno == EINTR || errno == EAGAIN)
|
||||||
continue;
|
continue;
|
||||||
ERR("poll failed: %s\n", strerror(errno));
|
const auto errstr = std::generic_category().message(errno);
|
||||||
mDevice->handleDisconnect("Failed waiting for playback buffer: %s", strerror(errno));
|
ERR("poll failed: %s\n", errstr.c_str());
|
||||||
|
mDevice->handleDisconnect("Failed waiting for playback buffer: %s", errstr.c_str());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if(pret == 0)
|
else if(pret == 0) /* NOLINT(*-else-after-return) 'pret' is local to the if/else blocks */
|
||||||
{
|
{
|
||||||
WARN("poll timeout\n");
|
WARN("poll timeout\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
al::byte *write_ptr{mMixData.data()};
|
al::span write_buf{mMixData};
|
||||||
size_t to_write{mMixData.size()};
|
mDevice->renderSamples(write_buf.data(), static_cast<uint>(write_buf.size()/frame_size),
|
||||||
mDevice->renderSamples(write_ptr, static_cast<uint>(to_write/frame_size), frame_step);
|
frame_step);
|
||||||
while(to_write > 0 && !mKillNow.load(std::memory_order_acquire))
|
while(!write_buf.empty() && !mKillNow.load(std::memory_order_acquire))
|
||||||
{
|
{
|
||||||
ssize_t wrote{write(mFd, write_ptr, to_write)};
|
ssize_t wrote{write(mFd, write_buf.data(), write_buf.size())};
|
||||||
if(wrote < 0)
|
if(wrote < 0)
|
||||||
{
|
{
|
||||||
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
|
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
|
||||||
continue;
|
continue;
|
||||||
ERR("write failed: %s\n", strerror(errno));
|
const auto errstr = std::generic_category().message(errno);
|
||||||
mDevice->handleDisconnect("Failed writing playback samples: %s", strerror(errno));
|
ERR("write failed: %s\n", errstr.c_str());
|
||||||
|
mDevice->handleDisconnect("Failed writing playback samples: %s", errstr.c_str());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
to_write -= static_cast<size_t>(wrote);
|
write_buf = write_buf.subspan(static_cast<size_t>(wrote));
|
||||||
write_ptr += wrote;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -308,11 +336,11 @@ int OSSPlayback::mixerProc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void OSSPlayback::open(const char *name)
|
void OSSPlayback::open(std::string_view name)
|
||||||
{
|
{
|
||||||
const char *devname{DefaultPlayback.c_str()};
|
const char *devname{DefaultPlayback.c_str()};
|
||||||
if(!name)
|
if(name.empty())
|
||||||
name = DefaultName;
|
name = GetDefaultName();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(PlaybackDevices.empty())
|
if(PlaybackDevices.empty())
|
||||||
|
|
@ -324,14 +352,14 @@ void OSSPlayback::open(const char *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", name};
|
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||||
devname = iter->device_name.c_str();
|
devname = iter->device_name.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
int fd{::open(devname, O_WRONLY)};
|
int fd{::open(devname, O_WRONLY)};
|
||||||
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 %s: %s", devname,
|
||||||
strerror(errno)};
|
std::generic_category().message(errno).c_str()};
|
||||||
|
|
||||||
if(mFd != -1)
|
if(mFd != -1)
|
||||||
::close(mFd);
|
::close(mFd);
|
||||||
|
|
@ -367,15 +395,14 @@ bool OSSPlayback::reset()
|
||||||
uint ossSpeed{mDevice->Frequency};
|
uint ossSpeed{mDevice->Frequency};
|
||||||
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{maxu(log2i(mDevice->UpdateSize*frameSize), 4)};
|
uint log2FragmentSize{std::max(log2i(mDevice->UpdateSize*frameSize), 4u)};
|
||||||
uint numFragmentsLogSize{(periods << 16) | log2FragmentSize};
|
uint numFragmentsLogSize{(periods << 16) | log2FragmentSize};
|
||||||
|
|
||||||
audio_buf_info info{};
|
audio_buf_info info{};
|
||||||
const char *err;
|
#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, \
|
||||||
err = #func; \
|
std::generic_category().message(errno).c_str()};
|
||||||
goto err; \
|
|
||||||
}
|
|
||||||
/* 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 */
|
||||||
ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
|
ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
|
||||||
|
|
@ -383,12 +410,6 @@ bool OSSPlayback::reset()
|
||||||
CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels));
|
CHECKERR(ioctl(mFd, SNDCTL_DSP_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));
|
||||||
if(0)
|
|
||||||
{
|
|
||||||
err:
|
|
||||||
ERR("%s failed: %s\n", err, strerror(errno));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#undef CHECKERR
|
#undef CHECKERR
|
||||||
|
|
||||||
if(mDevice->channelsFromFmt() != numChannels)
|
if(mDevice->channelsFromFmt() != numChannels)
|
||||||
|
|
@ -413,7 +434,7 @@ bool OSSPlayback::reset()
|
||||||
|
|
||||||
setDefaultChannelOrder();
|
setDefaultChannelOrder();
|
||||||
|
|
||||||
mMixData.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
|
mMixData.resize(size_t{mDevice->UpdateSize} * mDevice->frameSizeFromFmt());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -437,7 +458,7 @@ void OSSPlayback::stop()
|
||||||
mThread.join();
|
mThread.join();
|
||||||
|
|
||||||
if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
|
if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
|
||||||
ERR("Error resetting device: %s\n", strerror(errno));
|
ERR("Error resetting device: %s\n", std::generic_category().message(errno).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -447,10 +468,10 @@ struct OSScapture final : public BackendBase {
|
||||||
|
|
||||||
int recordProc();
|
int recordProc();
|
||||||
|
|
||||||
void open(const char *name) override;
|
void open(std::string_view name) override;
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
void captureSamples(al::byte *buffer, uint samples) override;
|
void captureSamples(std::byte *buffer, uint samples) override;
|
||||||
uint availableSamples() override;
|
uint availableSamples() override;
|
||||||
|
|
||||||
int mFd{-1};
|
int mFd{-1};
|
||||||
|
|
@ -459,8 +480,6 @@ struct OSScapture final : public BackendBase {
|
||||||
|
|
||||||
std::atomic<bool> mKillNow{true};
|
std::atomic<bool> mKillNow{true};
|
||||||
std::thread mThread;
|
std::thread mThread;
|
||||||
|
|
||||||
DEF_NEWDEL(OSScapture)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
OSScapture::~OSScapture()
|
OSScapture::~OSScapture()
|
||||||
|
|
@ -474,7 +493,7 @@ OSScapture::~OSScapture()
|
||||||
int OSScapture::recordProc()
|
int OSScapture::recordProc()
|
||||||
{
|
{
|
||||||
SetRTPriority();
|
SetRTPriority();
|
||||||
althrd_setname(RECORD_THREAD_NAME);
|
althrd_setname(GetRecordThreadName());
|
||||||
|
|
||||||
const size_t frame_size{mDevice->frameSizeFromFmt()};
|
const size_t frame_size{mDevice->frameSizeFromFmt()};
|
||||||
while(!mKillNow.load(std::memory_order_acquire))
|
while(!mKillNow.load(std::memory_order_acquire))
|
||||||
|
|
@ -483,16 +502,16 @@ int OSScapture::recordProc()
|
||||||
pollitem.fd = mFd;
|
pollitem.fd = mFd;
|
||||||
pollitem.events = POLLIN;
|
pollitem.events = POLLIN;
|
||||||
|
|
||||||
int sret{poll(&pollitem, 1, 1000)};
|
if(int pret{poll(&pollitem, 1, 1000)}; pret < 0)
|
||||||
if(sret < 0)
|
|
||||||
{
|
{
|
||||||
if(errno == EINTR || errno == EAGAIN)
|
if(errno == EINTR || errno == EAGAIN)
|
||||||
continue;
|
continue;
|
||||||
ERR("poll failed: %s\n", strerror(errno));
|
const auto errstr = std::generic_category().message(errno);
|
||||||
mDevice->handleDisconnect("Failed to check capture samples: %s", strerror(errno));
|
ERR("poll failed: %s\n", errstr.c_str());
|
||||||
|
mDevice->handleDisconnect("Failed to check capture samples: %s", errstr.c_str());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if(sret == 0)
|
else if(pret == 0) /* NOLINT(*-else-after-return) 'pret' is local to the if/else blocks */
|
||||||
{
|
{
|
||||||
WARN("poll timeout\n");
|
WARN("poll timeout\n");
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -504,8 +523,9 @@ int OSScapture::recordProc()
|
||||||
ssize_t amt{read(mFd, vec.first.buf, vec.first.len*frame_size)};
|
ssize_t amt{read(mFd, vec.first.buf, vec.first.len*frame_size)};
|
||||||
if(amt < 0)
|
if(amt < 0)
|
||||||
{
|
{
|
||||||
ERR("read failed: %s\n", strerror(errno));
|
const auto errstr = std::generic_category().message(errno);
|
||||||
mDevice->handleDisconnect("Failed reading capture samples: %s", strerror(errno));
|
ERR("read failed: %s\n", errstr.c_str());
|
||||||
|
mDevice->handleDisconnect("Failed reading capture samples: %s", errstr.c_str());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mRing->writeAdvance(static_cast<size_t>(amt)/frame_size);
|
mRing->writeAdvance(static_cast<size_t>(amt)/frame_size);
|
||||||
|
|
@ -516,11 +536,11 @@ int OSScapture::recordProc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void OSScapture::open(const char *name)
|
void OSScapture::open(std::string_view name)
|
||||||
{
|
{
|
||||||
const char *devname{DefaultCapture.c_str()};
|
const char *devname{DefaultCapture.c_str()};
|
||||||
if(!name)
|
if(name.empty())
|
||||||
name = DefaultName;
|
name = GetDefaultName();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(CaptureDevices.empty())
|
if(CaptureDevices.empty())
|
||||||
|
|
@ -532,14 +552,14 @@ void OSScapture::open(const char *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", name};
|
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||||
devname = iter->device_name.c_str();
|
devname = iter->device_name.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
mFd = ::open(devname, O_RDONLY);
|
mFd = ::open(devname, O_RDONLY);
|
||||||
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 %s: %s", devname,
|
||||||
strerror(errno)};
|
std::generic_category().message(errno).c_str()};
|
||||||
|
|
||||||
int ossFormat{};
|
int ossFormat{};
|
||||||
switch(mDevice->FmtType)
|
switch(mDevice->FmtType)
|
||||||
|
|
@ -566,13 +586,13 @@ void OSScapture::open(const char *name)
|
||||||
uint frameSize{numChannels * mDevice->bytesFromFmt()};
|
uint frameSize{numChannels * mDevice->bytesFromFmt()};
|
||||||
uint ossSpeed{mDevice->Frequency};
|
uint ossSpeed{mDevice->Frequency};
|
||||||
/* according to the OSS spec, 16 bytes are the minimum */
|
/* according to the OSS spec, 16 bytes are the minimum */
|
||||||
uint log2FragmentSize{maxu(log2i(mDevice->BufferSize * frameSize / periods), 4)};
|
uint log2FragmentSize{std::max(log2i(mDevice->BufferSize * frameSize / periods), 4u)};
|
||||||
uint numFragmentsLogSize{(periods << 16) | log2FragmentSize};
|
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: %s", \
|
||||||
strerror(errno)}; \
|
std::generic_category().message(errno).c_str()}; \
|
||||||
}
|
}
|
||||||
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));
|
||||||
|
|
@ -617,11 +637,11 @@ void OSScapture::stop()
|
||||||
mThread.join();
|
mThread.join();
|
||||||
|
|
||||||
if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
|
if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
|
||||||
ERR("Error resetting device: %s\n", strerror(errno));
|
ERR("Error resetting device: %s\n", std::generic_category().message(errno).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void OSScapture::captureSamples(al::byte *buffer, uint samples)
|
void OSScapture::captureSamples(std::byte *buffer, uint samples)
|
||||||
{ mRing->read(buffer, samples); }
|
{ std::ignore = mRing->read(buffer, samples); }
|
||||||
|
|
||||||
uint OSScapture::availableSamples()
|
uint OSScapture::availableSamples()
|
||||||
{ return static_cast<uint>(mRing->readSpace()); }
|
{ return static_cast<uint>(mRing->readSpace()); }
|
||||||
|
|
@ -637,9 +657,9 @@ BackendFactory &OSSBackendFactory::getFactory()
|
||||||
|
|
||||||
bool OSSBackendFactory::init()
|
bool OSSBackendFactory::init()
|
||||||
{
|
{
|
||||||
if(auto devopt = ConfigValueStr(nullptr, "oss", "device"))
|
if(auto devopt = ConfigValueStr({}, "oss", "device"))
|
||||||
DefaultPlayback = std::move(*devopt);
|
DefaultPlayback = std::move(*devopt);
|
||||||
if(auto capopt = ConfigValueStr(nullptr, "oss", "capture"))
|
if(auto capopt = ConfigValueStr({}, "oss", "capture"))
|
||||||
DefaultCapture = std::move(*capopt);
|
DefaultCapture = std::move(*capopt);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -648,18 +668,13 @@ bool OSSBackendFactory::init()
|
||||||
bool OSSBackendFactory::querySupport(BackendType type)
|
bool OSSBackendFactory::querySupport(BackendType type)
|
||||||
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
||||||
|
|
||||||
std::string OSSBackendFactory::probe(BackendType type)
|
auto OSSBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||||
{
|
{
|
||||||
std::string outnames;
|
std::vector<std::string> outnames;
|
||||||
|
|
||||||
auto add_device = [&outnames](const DevMap &entry) -> void
|
auto add_device = [&outnames](const DevMap &entry) -> void
|
||||||
{
|
{
|
||||||
struct stat buf;
|
if(struct stat buf{}; stat(entry.device_name.c_str(), &buf) == 0)
|
||||||
if(stat(entry.device_name.c_str(), &buf) == 0)
|
outnames.emplace_back(entry.name);
|
||||||
{
|
|
||||||
/* Includes null char. */
|
|
||||||
outnames.append(entry.name.c_str(), entry.name.length()+1);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
switch(type)
|
switch(type)
|
||||||
|
|
@ -667,12 +682,14 @@ std::string OSSBackendFactory::probe(BackendType type)
|
||||||
case BackendType::Playback:
|
case BackendType::Playback:
|
||||||
PlaybackDevices.clear();
|
PlaybackDevices.clear();
|
||||||
ALCossListPopulate(PlaybackDevices, DSP_CAP_OUTPUT);
|
ALCossListPopulate(PlaybackDevices, DSP_CAP_OUTPUT);
|
||||||
|
outnames.reserve(PlaybackDevices.size());
|
||||||
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
|
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BackendType::Capture:
|
case BackendType::Capture:
|
||||||
CaptureDevices.clear();
|
CaptureDevices.clear();
|
||||||
ALCossListPopulate(CaptureDevices, DSP_CAP_INPUT);
|
ALCossListPopulate(CaptureDevices, DSP_CAP_INPUT);
|
||||||
|
outnames.reserve(CaptureDevices.size());
|
||||||
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
|
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,15 @@
|
||||||
|
|
||||||
struct OSSBackendFactory final : public BackendFactory {
|
struct OSSBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
bool init() override;
|
auto init() -> bool final;
|
||||||
|
|
||||||
bool querySupport(BackendType type) override;
|
auto querySupport(BackendType type) -> bool final;
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||||
|
|
||||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_OSS_H */
|
#endif /* BACKENDS_OSS_H */
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -2,22 +2,26 @@
|
||||||
#define BACKENDS_PIPEWIRE_H
|
#define BACKENDS_PIPEWIRE_H
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "alc/events.h"
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
|
|
||||||
struct DeviceBase;
|
struct DeviceBase;
|
||||||
|
|
||||||
struct PipeWireBackendFactory final : public BackendFactory {
|
struct PipeWireBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
bool init() override;
|
auto init() -> bool final;
|
||||||
|
|
||||||
bool querySupport(BackendType type) override;
|
auto querySupport(BackendType type) -> bool final;
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
auto queryEventSupport(alc::EventType eventType, BackendType type) -> alc::EventSupport final;
|
||||||
|
|
||||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||||
|
|
||||||
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_PIPEWIRE_H */
|
#endif /* BACKENDS_PIPEWIRE_H */
|
||||||
|
|
|
||||||
|
|
@ -26,21 +26,20 @@
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "albit.h"
|
||||||
#include "alc/alconfig.h"
|
#include "alc/alconfig.h"
|
||||||
#include "alnumeric.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>
|
#include <portaudio.h> /* NOLINT(*-duplicate-include) Not the same header. */
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr char pa_device[] = "PortAudio Default";
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAVE_DYNLOAD
|
#ifdef HAVE_DYNLOAD
|
||||||
void *pa_handle;
|
void *pa_handle;
|
||||||
#define MAKE_FUNC(x) decltype(x) * p##x
|
#define MAKE_FUNC(x) decltype(x) * p##x
|
||||||
|
|
@ -51,6 +50,8 @@ MAKE_FUNC(Pa_StartStream);
|
||||||
MAKE_FUNC(Pa_StopStream);
|
MAKE_FUNC(Pa_StopStream);
|
||||||
MAKE_FUNC(Pa_OpenStream);
|
MAKE_FUNC(Pa_OpenStream);
|
||||||
MAKE_FUNC(Pa_CloseStream);
|
MAKE_FUNC(Pa_CloseStream);
|
||||||
|
MAKE_FUNC(Pa_GetDeviceCount);
|
||||||
|
MAKE_FUNC(Pa_GetDeviceInfo);
|
||||||
MAKE_FUNC(Pa_GetDefaultOutputDevice);
|
MAKE_FUNC(Pa_GetDefaultOutputDevice);
|
||||||
MAKE_FUNC(Pa_GetDefaultInputDevice);
|
MAKE_FUNC(Pa_GetDefaultInputDevice);
|
||||||
MAKE_FUNC(Pa_GetStreamInfo);
|
MAKE_FUNC(Pa_GetStreamInfo);
|
||||||
|
|
@ -64,12 +65,49 @@ MAKE_FUNC(Pa_GetStreamInfo);
|
||||||
#define Pa_StopStream pPa_StopStream
|
#define Pa_StopStream pPa_StopStream
|
||||||
#define Pa_OpenStream pPa_OpenStream
|
#define Pa_OpenStream pPa_OpenStream
|
||||||
#define Pa_CloseStream pPa_CloseStream
|
#define Pa_CloseStream pPa_CloseStream
|
||||||
|
#define Pa_GetDeviceCount pPa_GetDeviceCount
|
||||||
|
#define Pa_GetDeviceInfo pPa_GetDeviceInfo
|
||||||
#define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice
|
#define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice
|
||||||
#define Pa_GetDefaultInputDevice pPa_GetDefaultInputDevice
|
#define Pa_GetDefaultInputDevice pPa_GetDefaultInputDevice
|
||||||
#define Pa_GetStreamInfo pPa_GetStreamInfo
|
#define Pa_GetStreamInfo pPa_GetStreamInfo
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct DeviceEntry {
|
||||||
|
std::string mName;
|
||||||
|
bool mIsPlayback{};
|
||||||
|
bool mIsCapture{};
|
||||||
|
};
|
||||||
|
std::vector<DeviceEntry> DeviceNames;
|
||||||
|
|
||||||
|
void EnumerateDevices()
|
||||||
|
{
|
||||||
|
const auto devcount = Pa_GetDeviceCount();
|
||||||
|
if(devcount < 0)
|
||||||
|
{
|
||||||
|
ERR("Error getting device count: %s\n", Pa_GetErrorText(devcount));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DeviceEntry>(static_cast<uint>(devcount)).swap(DeviceNames);
|
||||||
|
PaDeviceIndex idx{0};
|
||||||
|
for(auto &entry : DeviceNames)
|
||||||
|
{
|
||||||
|
if(auto info = Pa_GetDeviceInfo(idx); info && info->name)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
entry.mName = "OpenAL Soft on "+std::string{info->name};
|
||||||
|
#else
|
||||||
|
entry.mName = info->name;
|
||||||
|
#endif
|
||||||
|
entry.mIsPlayback = (info->maxOutputChannels > 0);
|
||||||
|
entry.mIsCapture = (info->maxInputChannels > 0);
|
||||||
|
TRACE("Device %d \"%s\": %d playback, %d capture channels\n", idx, entry.mName.c_str(),
|
||||||
|
info->maxOutputChannels, info->maxInputChannels);
|
||||||
|
}
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct PortPlayback final : public BackendBase {
|
struct PortPlayback final : public BackendBase {
|
||||||
PortPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
PortPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
|
|
@ -77,24 +115,17 @@ struct PortPlayback final : public BackendBase {
|
||||||
|
|
||||||
int writeCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
|
int writeCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
|
||||||
const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) noexcept;
|
const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) noexcept;
|
||||||
static int writeCallbackC(const void *inputBuffer, void *outputBuffer,
|
|
||||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
|
|
||||||
const PaStreamCallbackFlags statusFlags, void *userData) noexcept
|
|
||||||
{
|
|
||||||
return static_cast<PortPlayback*>(userData)->writeCallback(inputBuffer, outputBuffer,
|
|
||||||
framesPerBuffer, timeInfo, statusFlags);
|
|
||||||
}
|
|
||||||
|
|
||||||
void open(const char *name) override;
|
void open(std::string_view name) override;
|
||||||
bool reset() override;
|
bool reset() override;
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
|
|
||||||
PaStream *mStream{nullptr};
|
PaStream *mStream{nullptr};
|
||||||
PaStreamParameters mParams{};
|
PaStreamParameters mParams{};
|
||||||
|
DevFmtChannels mChannelConfig{};
|
||||||
|
uint mAmbiOrder{};
|
||||||
uint mUpdateSize{0u};
|
uint mUpdateSize{0u};
|
||||||
|
|
||||||
DEF_NEWDEL(PortPlayback)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
PortPlayback::~PortPlayback()
|
PortPlayback::~PortPlayback()
|
||||||
|
|
@ -115,22 +146,38 @@ int PortPlayback::writeCallback(const void*, void *outputBuffer, unsigned long f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PortPlayback::open(const char *name)
|
void PortPlayback::open(std::string_view name)
|
||||||
{
|
{
|
||||||
if(!name)
|
if(DeviceNames.empty())
|
||||||
name = pa_device;
|
EnumerateDevices();
|
||||||
else if(strcmp(name, pa_device) != 0)
|
|
||||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
int deviceid{-1};
|
||||||
name};
|
if(name.empty())
|
||||||
|
{
|
||||||
|
if(auto devidopt = ConfigValueInt({}, "port", "device"))
|
||||||
|
deviceid = *devidopt;
|
||||||
|
if(deviceid < 0 || static_cast<uint>(deviceid) >= DeviceNames.size())
|
||||||
|
deviceid = Pa_GetDefaultOutputDevice();
|
||||||
|
name = DeviceNames.at(static_cast<uint>(deviceid)).mName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto iter = std::find_if(DeviceNames.cbegin(), DeviceNames.cend(),
|
||||||
|
[name](const DeviceEntry &entry) { return entry.mIsPlayback && name == entry.mName; });
|
||||||
|
if(iter == DeviceNames.cend())
|
||||||
|
throw al::backend_exception{al::backend_error::NoDevice,
|
||||||
|
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||||
|
deviceid = static_cast<int>(std::distance(DeviceNames.cbegin(), iter));
|
||||||
|
}
|
||||||
|
|
||||||
PaStreamParameters params{};
|
PaStreamParameters params{};
|
||||||
auto devidopt = ConfigValueInt(nullptr, "port", "device");
|
params.device = deviceid;
|
||||||
if(devidopt && *devidopt >= 0) params.device = *devidopt;
|
|
||||||
else params.device = Pa_GetDefaultOutputDevice();
|
|
||||||
params.suggestedLatency = mDevice->BufferSize / static_cast<double>(mDevice->Frequency);
|
params.suggestedLatency = mDevice->BufferSize / static_cast<double>(mDevice->Frequency);
|
||||||
params.hostApiSpecificStreamInfo = nullptr;
|
params.hostApiSpecificStreamInfo = nullptr;
|
||||||
|
|
||||||
params.channelCount = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
|
mChannelConfig = mDevice->FmtChans;
|
||||||
|
mAmbiOrder = mDevice->mAmbiOrder;
|
||||||
|
params.channelCount = static_cast<int>(mDevice->channelsFromFmt());
|
||||||
|
|
||||||
switch(mDevice->FmtType)
|
switch(mDevice->FmtType)
|
||||||
{
|
{
|
||||||
|
|
@ -155,19 +202,21 @@ void PortPlayback::open(const char *name)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
retry_open:
|
static constexpr auto writeCallback = [](const void *inputBuffer, void *outputBuffer,
|
||||||
PaStream *stream{};
|
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
|
||||||
PaError err{Pa_OpenStream(&stream, nullptr, ¶ms, mDevice->Frequency, mDevice->UpdateSize,
|
const PaStreamCallbackFlags statusFlags, void *userData) noexcept
|
||||||
paNoFlag, &PortPlayback::writeCallbackC, this)};
|
|
||||||
if(err != paNoError)
|
|
||||||
{
|
{
|
||||||
if(params.sampleFormat == paFloat32)
|
return static_cast<PortPlayback*>(userData)->writeCallback(inputBuffer, outputBuffer,
|
||||||
{
|
framesPerBuffer, timeInfo, statusFlags);
|
||||||
params.sampleFormat = paInt16;
|
};
|
||||||
goto retry_open;
|
PaStream *stream{};
|
||||||
}
|
while(PaError err{Pa_OpenStream(&stream, nullptr, ¶ms, mDevice->Frequency,
|
||||||
throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s",
|
mDevice->UpdateSize, paNoFlag, writeCallback, this)})
|
||||||
Pa_GetErrorText(err)};
|
{
|
||||||
|
if(params.sampleFormat != paFloat32)
|
||||||
|
throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s",
|
||||||
|
Pa_GetErrorText(err)};
|
||||||
|
params.sampleFormat = paInt16;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pa_CloseStream(mStream);
|
Pa_CloseStream(mStream);
|
||||||
|
|
@ -182,7 +231,18 @@ bool PortPlayback::reset()
|
||||||
{
|
{
|
||||||
const PaStreamInfo *streamInfo{Pa_GetStreamInfo(mStream)};
|
const PaStreamInfo *streamInfo{Pa_GetStreamInfo(mStream)};
|
||||||
mDevice->Frequency = static_cast<uint>(streamInfo->sampleRate);
|
mDevice->Frequency = static_cast<uint>(streamInfo->sampleRate);
|
||||||
|
mDevice->FmtChans = mChannelConfig;
|
||||||
|
mDevice->mAmbiOrder = mAmbiOrder;
|
||||||
mDevice->UpdateSize = mUpdateSize;
|
mDevice->UpdateSize = mUpdateSize;
|
||||||
|
mDevice->BufferSize = mUpdateSize * 2;
|
||||||
|
if(streamInfo->outputLatency > 0.0f)
|
||||||
|
{
|
||||||
|
const double sampleLatency{streamInfo->outputLatency * streamInfo->sampleRate};
|
||||||
|
TRACE("Reported stream latency: %f sec (%f samples)\n", streamInfo->outputLatency,
|
||||||
|
sampleLatency);
|
||||||
|
mDevice->BufferSize = static_cast<uint>(std::clamp(sampleLatency,
|
||||||
|
double(mDevice->BufferSize), double{std::numeric_limits<int>::max()}));
|
||||||
|
}
|
||||||
|
|
||||||
if(mParams.sampleFormat == paInt8)
|
if(mParams.sampleFormat == paInt8)
|
||||||
mDevice->FmtType = DevFmtByte;
|
mDevice->FmtType = DevFmtByte;
|
||||||
|
|
@ -200,15 +260,6 @@ bool PortPlayback::reset()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mParams.channelCount >= 2)
|
|
||||||
mDevice->FmtChans = DevFmtStereo;
|
|
||||||
else if(mParams.channelCount == 1)
|
|
||||||
mDevice->FmtChans = DevFmtMono;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ERR("Unexpected channel count: %u\n", mParams.channelCount);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
setDefaultChannelOrder();
|
setDefaultChannelOrder();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -216,16 +267,14 @@ bool PortPlayback::reset()
|
||||||
|
|
||||||
void PortPlayback::start()
|
void PortPlayback::start()
|
||||||
{
|
{
|
||||||
const PaError err{Pa_StartStream(mStream)};
|
if(const PaError err{Pa_StartStream(mStream)}; err != paNoError)
|
||||||
if(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: %s",
|
||||||
Pa_GetErrorText(err)};
|
Pa_GetErrorText(err)};
|
||||||
}
|
}
|
||||||
|
|
||||||
void PortPlayback::stop()
|
void PortPlayback::stop()
|
||||||
{
|
{
|
||||||
PaError err{Pa_StopStream(mStream)};
|
if(PaError err{Pa_StopStream(mStream)}; err != paNoError)
|
||||||
if(err != paNoError)
|
|
||||||
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
|
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -235,27 +284,18 @@ struct PortCapture final : public BackendBase {
|
||||||
~PortCapture() override;
|
~PortCapture() override;
|
||||||
|
|
||||||
int readCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
|
int readCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
|
||||||
const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) noexcept;
|
const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) const noexcept;
|
||||||
static int readCallbackC(const void *inputBuffer, void *outputBuffer,
|
|
||||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
|
|
||||||
const PaStreamCallbackFlags statusFlags, void *userData) noexcept
|
|
||||||
{
|
|
||||||
return static_cast<PortCapture*>(userData)->readCallback(inputBuffer, outputBuffer,
|
|
||||||
framesPerBuffer, timeInfo, statusFlags);
|
|
||||||
}
|
|
||||||
|
|
||||||
void open(const char *name) override;
|
void open(std::string_view name) override;
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
void captureSamples(al::byte *buffer, uint samples) override;
|
void captureSamples(std::byte *buffer, uint samples) override;
|
||||||
uint availableSamples() override;
|
uint availableSamples() override;
|
||||||
|
|
||||||
PaStream *mStream{nullptr};
|
PaStream *mStream{nullptr};
|
||||||
PaStreamParameters mParams;
|
PaStreamParameters mParams{};
|
||||||
|
|
||||||
RingBufferPtr mRing{nullptr};
|
RingBufferPtr mRing{nullptr};
|
||||||
|
|
||||||
DEF_NEWDEL(PortCapture)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
PortCapture::~PortCapture()
|
PortCapture::~PortCapture()
|
||||||
|
|
@ -268,30 +308,43 @@ PortCapture::~PortCapture()
|
||||||
|
|
||||||
|
|
||||||
int PortCapture::readCallback(const void *inputBuffer, void*, unsigned long framesPerBuffer,
|
int PortCapture::readCallback(const void *inputBuffer, void*, unsigned long framesPerBuffer,
|
||||||
const PaStreamCallbackTimeInfo*, const PaStreamCallbackFlags) noexcept
|
const PaStreamCallbackTimeInfo*, const PaStreamCallbackFlags) const noexcept
|
||||||
{
|
{
|
||||||
mRing->write(inputBuffer, framesPerBuffer);
|
std::ignore = mRing->write(inputBuffer, framesPerBuffer);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PortCapture::open(const char *name)
|
void PortCapture::open(std::string_view name)
|
||||||
{
|
{
|
||||||
if(!name)
|
if(DeviceNames.empty())
|
||||||
name = pa_device;
|
EnumerateDevices();
|
||||||
else if(strcmp(name, pa_device) != 0)
|
|
||||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
|
||||||
name};
|
|
||||||
|
|
||||||
uint samples{mDevice->BufferSize};
|
int deviceid{};
|
||||||
samples = maxu(samples, 100 * mDevice->Frequency / 1000);
|
if(name.empty())
|
||||||
uint frame_size{mDevice->frameSizeFromFmt()};
|
{
|
||||||
|
if(auto devidopt = ConfigValueInt({}, "port", "capture"))
|
||||||
|
deviceid = *devidopt;
|
||||||
|
if(deviceid < 0 || static_cast<uint>(deviceid) >= DeviceNames.size())
|
||||||
|
deviceid = Pa_GetDefaultInputDevice();
|
||||||
|
name = DeviceNames.at(static_cast<uint>(deviceid)).mName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto iter = std::find_if(DeviceNames.cbegin(), DeviceNames.cend(),
|
||||||
|
[name](const DeviceEntry &entry) { return entry.mIsCapture && name == entry.mName; });
|
||||||
|
if(iter == DeviceNames.cend())
|
||||||
|
throw al::backend_exception{al::backend_error::NoDevice,
|
||||||
|
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||||
|
deviceid = static_cast<int>(std::distance(DeviceNames.cbegin(), iter));
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint samples{std::max(mDevice->BufferSize, mDevice->Frequency/10u)};
|
||||||
|
const uint frame_size{mDevice->frameSizeFromFmt()};
|
||||||
|
|
||||||
mRing = RingBuffer::Create(samples, frame_size, false);
|
mRing = RingBuffer::Create(samples, frame_size, false);
|
||||||
|
|
||||||
auto devidopt = ConfigValueInt(nullptr, "port", "capture");
|
mParams.device = deviceid;
|
||||||
if(devidopt && *devidopt >= 0) mParams.device = *devidopt;
|
|
||||||
else mParams.device = Pa_GetDefaultOutputDevice();
|
|
||||||
mParams.suggestedLatency = 0.0f;
|
mParams.suggestedLatency = 0.0f;
|
||||||
mParams.hostApiSpecificStreamInfo = nullptr;
|
mParams.hostApiSpecificStreamInfo = nullptr;
|
||||||
|
|
||||||
|
|
@ -319,8 +372,15 @@ void PortCapture::open(const char *name)
|
||||||
}
|
}
|
||||||
mParams.channelCount = static_cast<int>(mDevice->channelsFromFmt());
|
mParams.channelCount = static_cast<int>(mDevice->channelsFromFmt());
|
||||||
|
|
||||||
|
static constexpr auto readCallback = [](const void *inputBuffer, void *outputBuffer,
|
||||||
|
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
|
||||||
|
const PaStreamCallbackFlags statusFlags, void *userData) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<PortCapture*>(userData)->readCallback(inputBuffer, outputBuffer,
|
||||||
|
framesPerBuffer, timeInfo, statusFlags);
|
||||||
|
};
|
||||||
PaError err{Pa_OpenStream(&mStream, &mParams, nullptr, mDevice->Frequency,
|
PaError err{Pa_OpenStream(&mStream, &mParams, nullptr, mDevice->Frequency,
|
||||||
paFramesPerBufferUnspecified, paNoFlag, &PortCapture::readCallbackC, 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: %s",
|
||||||
Pa_GetErrorText(err)};
|
Pa_GetErrorText(err)};
|
||||||
|
|
@ -331,16 +391,14 @@ void PortCapture::open(const char *name)
|
||||||
|
|
||||||
void PortCapture::start()
|
void PortCapture::start()
|
||||||
{
|
{
|
||||||
const PaError err{Pa_StartStream(mStream)};
|
if(const PaError err{Pa_StartStream(mStream)}; err != paNoError)
|
||||||
if(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: %s", Pa_GetErrorText(err)};
|
||||||
}
|
}
|
||||||
|
|
||||||
void PortCapture::stop()
|
void PortCapture::stop()
|
||||||
{
|
{
|
||||||
PaError err{Pa_StopStream(mStream)};
|
if(PaError err{Pa_StopStream(mStream)}; err != paNoError)
|
||||||
if(err != paNoError)
|
|
||||||
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
|
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -348,16 +406,14 @@ void PortCapture::stop()
|
||||||
uint PortCapture::availableSamples()
|
uint PortCapture::availableSamples()
|
||||||
{ return static_cast<uint>(mRing->readSpace()); }
|
{ return static_cast<uint>(mRing->readSpace()); }
|
||||||
|
|
||||||
void PortCapture::captureSamples(al::byte *buffer, uint samples)
|
void PortCapture::captureSamples(std::byte *buffer, uint samples)
|
||||||
{ mRing->read(buffer, samples); }
|
{ std::ignore = mRing->read(buffer, samples); }
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
bool PortBackendFactory::init()
|
bool PortBackendFactory::init()
|
||||||
{
|
{
|
||||||
PaError err;
|
|
||||||
|
|
||||||
#ifdef HAVE_DYNLOAD
|
#ifdef HAVE_DYNLOAD
|
||||||
if(!pa_handle)
|
if(!pa_handle)
|
||||||
{
|
{
|
||||||
|
|
@ -391,12 +447,15 @@ bool PortBackendFactory::init()
|
||||||
LOAD_FUNC(Pa_StopStream);
|
LOAD_FUNC(Pa_StopStream);
|
||||||
LOAD_FUNC(Pa_OpenStream);
|
LOAD_FUNC(Pa_OpenStream);
|
||||||
LOAD_FUNC(Pa_CloseStream);
|
LOAD_FUNC(Pa_CloseStream);
|
||||||
|
LOAD_FUNC(Pa_GetDeviceCount);
|
||||||
|
LOAD_FUNC(Pa_GetDeviceInfo);
|
||||||
LOAD_FUNC(Pa_GetDefaultOutputDevice);
|
LOAD_FUNC(Pa_GetDefaultOutputDevice);
|
||||||
LOAD_FUNC(Pa_GetDefaultInputDevice);
|
LOAD_FUNC(Pa_GetDefaultInputDevice);
|
||||||
LOAD_FUNC(Pa_GetStreamInfo);
|
LOAD_FUNC(Pa_GetStreamInfo);
|
||||||
#undef LOAD_FUNC
|
#undef LOAD_FUNC
|
||||||
|
|
||||||
if((err=Pa_Initialize()) != paNoError)
|
const PaError err{Pa_Initialize()};
|
||||||
|
if(err != paNoError)
|
||||||
{
|
{
|
||||||
ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
|
ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
|
||||||
CloseLib(pa_handle);
|
CloseLib(pa_handle);
|
||||||
|
|
@ -405,7 +464,8 @@ bool PortBackendFactory::init()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if((err=Pa_Initialize()) != paNoError)
|
const PaError err{Pa_Initialize()};
|
||||||
|
if(err != paNoError)
|
||||||
{
|
{
|
||||||
ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
|
ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -417,18 +477,52 @@ bool PortBackendFactory::init()
|
||||||
bool PortBackendFactory::querySupport(BackendType type)
|
bool PortBackendFactory::querySupport(BackendType type)
|
||||||
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
||||||
|
|
||||||
std::string PortBackendFactory::probe(BackendType type)
|
auto PortBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||||
{
|
{
|
||||||
std::string outnames;
|
std::vector<std::string> devices;
|
||||||
|
|
||||||
|
EnumerateDevices();
|
||||||
|
int defaultid{-1};
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case BackendType::Playback:
|
case BackendType::Playback:
|
||||||
|
defaultid = Pa_GetDefaultOutputDevice();
|
||||||
|
if(auto devidopt = ConfigValueInt({}, "port", "device"); devidopt && *devidopt >= 0
|
||||||
|
&& static_cast<uint>(*devidopt) < DeviceNames.size())
|
||||||
|
defaultid = *devidopt;
|
||||||
|
|
||||||
|
for(size_t i{0};i < DeviceNames.size();++i)
|
||||||
|
{
|
||||||
|
if(DeviceNames[i].mIsPlayback)
|
||||||
|
{
|
||||||
|
if(defaultid >= 0 && static_cast<uint>(defaultid) == i)
|
||||||
|
devices.emplace(devices.cbegin(), DeviceNames[i].mName);
|
||||||
|
else
|
||||||
|
devices.emplace_back(DeviceNames[i].mName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case BackendType::Capture:
|
case BackendType::Capture:
|
||||||
/* Includes null char. */
|
defaultid = Pa_GetDefaultInputDevice();
|
||||||
outnames.append(pa_device, sizeof(pa_device));
|
if(auto devidopt = ConfigValueInt({}, "port", "capture"); devidopt && *devidopt >= 0
|
||||||
|
&& static_cast<uint>(*devidopt) < DeviceNames.size())
|
||||||
|
defaultid = *devidopt;
|
||||||
|
|
||||||
|
for(size_t i{0};i < DeviceNames.size();++i)
|
||||||
|
{
|
||||||
|
if(DeviceNames[i].mIsCapture)
|
||||||
|
{
|
||||||
|
if(defaultid >= 0 && static_cast<uint>(defaultid) == i)
|
||||||
|
devices.emplace(devices.cbegin(), DeviceNames[i].mName);
|
||||||
|
else
|
||||||
|
devices.emplace_back(DeviceNames[i].mName);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return outnames;
|
|
||||||
|
return devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr PortBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
BackendPtr PortBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,15 @@
|
||||||
|
|
||||||
struct PortBackendFactory final : public BackendFactory {
|
struct PortBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
bool init() override;
|
auto init() -> bool final;
|
||||||
|
|
||||||
bool querySupport(BackendType type) override;
|
auto querySupport(BackendType type) -> bool final;
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||||
|
|
||||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_PORTAUDIO_H */
|
#endif /* BACKENDS_PORTAUDIO_H */
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,19 +1,27 @@
|
||||||
#ifndef BACKENDS_PULSEAUDIO_H
|
#ifndef BACKENDS_PULSEAUDIO_H
|
||||||
#define BACKENDS_PULSEAUDIO_H
|
#define BACKENDS_PULSEAUDIO_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "alc/events.h"
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
|
|
||||||
|
struct DeviceBase;
|
||||||
|
|
||||||
class PulseBackendFactory final : public BackendFactory {
|
class PulseBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
bool init() override;
|
auto init() -> bool final;
|
||||||
|
|
||||||
bool querySupport(BackendType type) override;
|
auto querySupport(BackendType type) -> bool final;
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
auto queryEventSupport(alc::EventType eventType, BackendType type) -> alc::EventSupport final;
|
||||||
|
|
||||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||||
|
|
||||||
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_PULSEAUDIO_H */
|
#endif /* BACKENDS_PULSEAUDIO_H */
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
|
|
@ -46,17 +47,17 @@ namespace {
|
||||||
#define DEVNAME_PREFIX ""
|
#define DEVNAME_PREFIX ""
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
constexpr char defaultDeviceName[] = DEVNAME_PREFIX "Default Device";
|
constexpr auto getDevicePrefix() noexcept -> std::string_view { return DEVNAME_PREFIX; }
|
||||||
|
constexpr auto getDefaultDeviceName() noexcept -> std::string_view
|
||||||
|
{ return DEVNAME_PREFIX "Default Device"; }
|
||||||
|
|
||||||
struct Sdl2Backend final : public BackendBase {
|
struct Sdl2Backend final : public BackendBase {
|
||||||
Sdl2Backend(DeviceBase *device) noexcept : BackendBase{device} { }
|
Sdl2Backend(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~Sdl2Backend() override;
|
~Sdl2Backend() override;
|
||||||
|
|
||||||
void audioCallback(Uint8 *stream, int len) noexcept;
|
void audioCallback(Uint8 *stream, int len) noexcept;
|
||||||
static void audioCallbackC(void *ptr, Uint8 *stream, int len) noexcept
|
|
||||||
{ static_cast<Sdl2Backend*>(ptr)->audioCallback(stream, len); }
|
|
||||||
|
|
||||||
void open(const char *name) override;
|
void open(std::string_view name) override;
|
||||||
bool reset() override;
|
bool reset() override;
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
|
|
@ -68,8 +69,6 @@ struct Sdl2Backend final : public BackendBase {
|
||||||
DevFmtChannels mFmtChans{};
|
DevFmtChannels mFmtChans{};
|
||||||
DevFmtType mFmtType{};
|
DevFmtType mFmtType{};
|
||||||
uint mUpdateSize{0u};
|
uint mUpdateSize{0u};
|
||||||
|
|
||||||
DEF_NEWDEL(Sdl2Backend)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Sdl2Backend::~Sdl2Backend()
|
Sdl2Backend::~Sdl2Backend()
|
||||||
|
|
@ -86,7 +85,7 @@ void Sdl2Backend::audioCallback(Uint8 *stream, int len) noexcept
|
||||||
mDevice->renderSamples(stream, ulen / mFrameSize, mDevice->channelsFromFmt());
|
mDevice->renderSamples(stream, ulen / mFrameSize, mDevice->channelsFromFmt());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sdl2Backend::open(const char *name)
|
void Sdl2Backend::open(std::string_view name)
|
||||||
{
|
{
|
||||||
SDL_AudioSpec want{}, have{};
|
SDL_AudioSpec want{}, have{};
|
||||||
|
|
||||||
|
|
@ -102,24 +101,39 @@ void Sdl2Backend::open(const char *name)
|
||||||
case DevFmtFloat: want.format = AUDIO_F32; break;
|
case DevFmtFloat: want.format = AUDIO_F32; break;
|
||||||
}
|
}
|
||||||
want.channels = (mDevice->FmtChans == DevFmtMono) ? 1 : 2;
|
want.channels = (mDevice->FmtChans == DevFmtMono) ? 1 : 2;
|
||||||
want.samples = static_cast<Uint16>(minu(mDevice->UpdateSize, 8192));
|
want.samples = static_cast<Uint16>(std::min(mDevice->UpdateSize, 8192u));
|
||||||
want.callback = &Sdl2Backend::audioCallbackC;
|
want.callback = [](void *ptr, Uint8 *stream, int len) noexcept
|
||||||
|
{ return static_cast<Sdl2Backend*>(ptr)->audioCallback(stream, len); };
|
||||||
want.userdata = this;
|
want.userdata = this;
|
||||||
|
|
||||||
/* Passing nullptr to SDL_OpenAudioDevice opens a default, which isn't
|
/* Passing nullptr to SDL_OpenAudioDevice opens a default, which isn't
|
||||||
* necessarily the first in the list.
|
* necessarily the first in the list.
|
||||||
*/
|
*/
|
||||||
|
const auto defaultDeviceName = getDefaultDeviceName();
|
||||||
SDL_AudioDeviceID devid;
|
SDL_AudioDeviceID devid;
|
||||||
if(!name || strcmp(name, defaultDeviceName) == 0)
|
if(name.empty() || name == defaultDeviceName)
|
||||||
|
{
|
||||||
|
name = defaultDeviceName;
|
||||||
devid = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE);
|
devid = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const size_t prefix_len = strlen(DEVNAME_PREFIX);
|
const auto namePrefix = getDevicePrefix();
|
||||||
if(strncmp(name, DEVNAME_PREFIX, prefix_len) == 0)
|
if(name.size() >= namePrefix.size() && name.substr(0, namePrefix.size()) == namePrefix)
|
||||||
devid = SDL_OpenAudioDevice(name+prefix_len, SDL_FALSE, &want, &have,
|
{
|
||||||
|
/* 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);
|
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
devid = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE);
|
{
|
||||||
|
const std::string devname{name};
|
||||||
|
devid = SDL_OpenAudioDevice(devname.c_str(), SDL_FALSE, &want, &have,
|
||||||
|
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(!devid)
|
if(!devid)
|
||||||
throw al::backend_exception{al::backend_error::NoDevice, "%s", SDL_GetError()};
|
throw al::backend_exception{al::backend_error::NoDevice, "%s", SDL_GetError()};
|
||||||
|
|
@ -161,7 +175,7 @@ void Sdl2Backend::open(const char *name)
|
||||||
mFmtType = devtype;
|
mFmtType = devtype;
|
||||||
mUpdateSize = have.samples;
|
mUpdateSize = have.samples;
|
||||||
|
|
||||||
mDevice->DeviceName = name ? name : defaultDeviceName;
|
mDevice->DeviceName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Sdl2Backend::reset()
|
bool Sdl2Backend::reset()
|
||||||
|
|
@ -195,23 +209,27 @@ bool SDL2BackendFactory::init()
|
||||||
bool SDL2BackendFactory::querySupport(BackendType type)
|
bool SDL2BackendFactory::querySupport(BackendType type)
|
||||||
{ return type == BackendType::Playback; }
|
{ return type == BackendType::Playback; }
|
||||||
|
|
||||||
std::string SDL2BackendFactory::probe(BackendType type)
|
auto SDL2BackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||||
{
|
{
|
||||||
std::string outnames;
|
std::vector<std::string> outnames;
|
||||||
|
|
||||||
if(type != BackendType::Playback)
|
if(type != BackendType::Playback)
|
||||||
return outnames;
|
return outnames;
|
||||||
|
|
||||||
int num_devices{SDL_GetNumAudioDevices(SDL_FALSE)};
|
int num_devices{SDL_GetNumAudioDevices(SDL_FALSE)};
|
||||||
|
if(num_devices <= 0)
|
||||||
|
return outnames;
|
||||||
|
|
||||||
/* Includes null char. */
|
outnames.reserve(static_cast<unsigned int>(num_devices));
|
||||||
outnames.append(defaultDeviceName, sizeof(defaultDeviceName));
|
outnames.emplace_back(getDefaultDeviceName());
|
||||||
for(int i{0};i < num_devices;++i)
|
for(int i{0};i < num_devices;++i)
|
||||||
{
|
{
|
||||||
std::string name{DEVNAME_PREFIX};
|
std::string outname{getDevicePrefix()};
|
||||||
name += SDL_GetAudioDeviceName(i, SDL_FALSE);
|
if(const char *name = SDL_GetAudioDeviceName(i, SDL_FALSE))
|
||||||
if(!name.empty())
|
outname += name;
|
||||||
outnames.append(name.c_str(), name.length()+1);
|
else
|
||||||
|
outname += "Unknown Device Name #"+std::to_string(i);
|
||||||
|
outnames.emplace_back(std::move(outname));
|
||||||
}
|
}
|
||||||
return outnames;
|
return outnames;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,15 @@
|
||||||
|
|
||||||
struct SDL2BackendFactory final : public BackendFactory {
|
struct SDL2BackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
bool init() override;
|
auto init() -> bool final;
|
||||||
|
|
||||||
bool querySupport(BackendType type) override;
|
auto querySupport(BackendType type) -> bool final;
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||||
|
|
||||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_SDL2_H */
|
#endif /* BACKENDS_SDL2_H */
|
||||||
|
|
|
||||||
|
|
@ -22,31 +22,35 @@
|
||||||
|
|
||||||
#include "sndio.h"
|
#include "sndio.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <inttypes.h>
|
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <stdio.h>
|
#include <system_error>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
|
#include "alstring.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 "threads.h"
|
|
||||||
#include "vector.h"
|
|
||||||
|
|
||||||
#include <sndio.h>
|
#include <sndio.h> /* NOLINT(*-duplicate-include) Not the same header. */
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
static const char sndio_device[] = "SndIO Default";
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "SndIO Default"sv; }
|
||||||
|
|
||||||
struct SioPar : public sio_par {
|
struct SioPar : public sio_par {
|
||||||
SioPar() { sio_initpar(this); }
|
SioPar() : sio_par{} { sio_initpar(this); }
|
||||||
|
|
||||||
void clear() { sio_initpar(this); }
|
void clear() { sio_initpar(this); }
|
||||||
};
|
};
|
||||||
|
|
@ -57,7 +61,7 @@ struct SndioPlayback final : public BackendBase {
|
||||||
|
|
||||||
int mixerProc();
|
int mixerProc();
|
||||||
|
|
||||||
void open(const char *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;
|
||||||
|
|
@ -65,12 +69,10 @@ struct SndioPlayback final : public BackendBase {
|
||||||
sio_hdl *mSndHandle{nullptr};
|
sio_hdl *mSndHandle{nullptr};
|
||||||
uint mFrameStep{};
|
uint mFrameStep{};
|
||||||
|
|
||||||
al::vector<al::byte> mBuffer;
|
std::vector<std::byte> mBuffer;
|
||||||
|
|
||||||
std::atomic<bool> mKillNow{true};
|
std::atomic<bool> mKillNow{true};
|
||||||
std::thread mThread;
|
std::thread mThread;
|
||||||
|
|
||||||
DEF_NEWDEL(SndioPlayback)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
SndioPlayback::~SndioPlayback()
|
SndioPlayback::~SndioPlayback()
|
||||||
|
|
@ -86,12 +88,12 @@ int SndioPlayback::mixerProc()
|
||||||
const size_t frameSize{frameStep * mDevice->bytesFromFmt()};
|
const size_t frameSize{frameStep * mDevice->bytesFromFmt()};
|
||||||
|
|
||||||
SetRTPriority();
|
SetRTPriority();
|
||||||
althrd_setname(MIXER_THREAD_NAME);
|
althrd_setname(GetMixerThreadName());
|
||||||
|
|
||||||
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))
|
||||||
{
|
{
|
||||||
al::span<al::byte> buffer{mBuffer};
|
al::span<std::byte> buffer{mBuffer};
|
||||||
|
|
||||||
mDevice->renderSamples(buffer.data(), static_cast<uint>(buffer.size() / frameSize),
|
mDevice->renderSamples(buffer.data(), static_cast<uint>(buffer.size() / frameSize),
|
||||||
frameStep);
|
frameStep);
|
||||||
|
|
@ -112,13 +114,13 @@ int SndioPlayback::mixerProc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SndioPlayback::open(const char *name)
|
void SndioPlayback::open(std::string_view name)
|
||||||
{
|
{
|
||||||
if(!name)
|
if(name.empty())
|
||||||
name = sndio_device;
|
name = GetDefaultName();
|
||||||
else if(strcmp(name, sndio_device) != 0)
|
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 \"%.*s\" not found",
|
||||||
name};
|
al::sizei(name), name.data()};
|
||||||
|
|
||||||
sio_hdl *sndHandle{sio_open(nullptr, SIO_PLAY, 0)};
|
sio_hdl *sndHandle{sio_open(nullptr, SIO_PLAY, 0)};
|
||||||
if(!sndHandle)
|
if(!sndHandle)
|
||||||
|
|
@ -136,72 +138,75 @@ bool SndioPlayback::reset()
|
||||||
SioPar par;
|
SioPar par;
|
||||||
|
|
||||||
auto tryfmt = mDevice->FmtType;
|
auto tryfmt = mDevice->FmtType;
|
||||||
retry_params:
|
while(true)
|
||||||
switch(tryfmt)
|
|
||||||
{
|
{
|
||||||
case DevFmtByte:
|
switch(tryfmt)
|
||||||
par.bits = 8;
|
{
|
||||||
par.sig = 1;
|
case DevFmtByte:
|
||||||
break;
|
par.bits = 8;
|
||||||
case DevFmtUByte:
|
par.sig = 1;
|
||||||
par.bits = 8;
|
break;
|
||||||
par.sig = 0;
|
case DevFmtUByte:
|
||||||
break;
|
par.bits = 8;
|
||||||
case DevFmtShort:
|
par.sig = 0;
|
||||||
par.bits = 16;
|
break;
|
||||||
par.sig = 1;
|
case DevFmtShort:
|
||||||
break;
|
par.bits = 16;
|
||||||
case DevFmtUShort:
|
par.sig = 1;
|
||||||
par.bits = 16;
|
break;
|
||||||
par.sig = 0;
|
case DevFmtUShort:
|
||||||
break;
|
par.bits = 16;
|
||||||
case DevFmtFloat:
|
par.sig = 0;
|
||||||
case DevFmtInt:
|
break;
|
||||||
par.bits = 32;
|
case DevFmtFloat:
|
||||||
par.sig = 1;
|
case DevFmtInt:
|
||||||
break;
|
par.bits = 32;
|
||||||
case DevFmtUInt:
|
par.sig = 1;
|
||||||
par.bits = 32;
|
break;
|
||||||
par.sig = 0;
|
case DevFmtUInt:
|
||||||
break;
|
par.bits = 32;
|
||||||
}
|
par.sig = 0;
|
||||||
par.bps = SIO_BPS(par.bits);
|
break;
|
||||||
par.le = SIO_LE_NATIVE;
|
}
|
||||||
par.msb = 1;
|
par.bps = SIO_BPS(par.bits);
|
||||||
|
par.le = SIO_LE_NATIVE;
|
||||||
|
par.msb = 1;
|
||||||
|
|
||||||
par.rate = mDevice->Frequency;
|
par.rate = mDevice->Frequency;
|
||||||
par.pchan = mDevice->channelsFromFmt();
|
par.pchan = mDevice->channelsFromFmt();
|
||||||
|
|
||||||
par.round = mDevice->UpdateSize;
|
par.round = mDevice->UpdateSize;
|
||||||
par.appbufsz = mDevice->BufferSize - mDevice->UpdateSize;
|
par.appbufsz = mDevice->BufferSize - mDevice->UpdateSize;
|
||||||
if(!par.appbufsz) par.appbufsz = mDevice->UpdateSize;
|
if(!par.appbufsz) par.appbufsz = mDevice->UpdateSize;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(!sio_setpar(mSndHandle, &par))
|
if(!sio_setpar(mSndHandle, &par))
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Failed to set device parameters"};
|
"Failed to set device parameters"};
|
||||||
|
|
||||||
par.clear();
|
par.clear();
|
||||||
if(!sio_getpar(mSndHandle, &par))
|
if(!sio_getpar(mSndHandle, &par))
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Failed to get device parameters"};
|
"Failed to get device parameters"};
|
||||||
|
|
||||||
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"};
|
"%s-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 (%u of %u 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"};
|
||||||
}
|
|
||||||
catch(al::backend_exception &e) {
|
break;
|
||||||
if(tryfmt == DevFmtShort)
|
}
|
||||||
throw;
|
catch(al::backend_exception &e) {
|
||||||
par.clear();
|
if(tryfmt == DevFmtShort)
|
||||||
tryfmt = DevFmtShort;
|
throw;
|
||||||
goto retry_params;
|
par.clear();
|
||||||
|
tryfmt = DevFmtShort;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(par.bps == 1)
|
if(par.bps == 1)
|
||||||
|
|
@ -229,11 +234,11 @@ retry_params:
|
||||||
mDevice->UpdateSize = par.round;
|
mDevice->UpdateSize = par.round;
|
||||||
mDevice->BufferSize = par.bufsz + par.round;
|
mDevice->BufferSize = par.bufsz + par.round;
|
||||||
|
|
||||||
mBuffer.resize(mDevice->UpdateSize * par.pchan*par.bps);
|
mBuffer.resize(size_t{mDevice->UpdateSize} * par.pchan*par.bps);
|
||||||
if(par.sig == 1)
|
if(par.sig == 1)
|
||||||
std::fill(mBuffer.begin(), mBuffer.end(), al::byte{});
|
std::fill(mBuffer.begin(), mBuffer.end(), std::byte{});
|
||||||
else if(par.bits == 8)
|
else if(par.bits == 8)
|
||||||
std::fill_n(mBuffer.data(), mBuffer.size(), al::byte(0x80));
|
std::fill_n(mBuffer.data(), mBuffer.size(), std::byte(0x80));
|
||||||
else if(par.bits == 16)
|
else if(par.bits == 16)
|
||||||
std::fill_n(reinterpret_cast<uint16_t*>(mBuffer.data()), mBuffer.size()/2, 0x8000);
|
std::fill_n(reinterpret_cast<uint16_t*>(mBuffer.data()), mBuffer.size()/2, 0x8000);
|
||||||
else if(par.bits == 32)
|
else if(par.bits == 32)
|
||||||
|
|
@ -280,10 +285,10 @@ struct SndioCapture final : public BackendBase {
|
||||||
|
|
||||||
int recordProc();
|
int recordProc();
|
||||||
|
|
||||||
void open(const char *name) override;
|
void open(std::string_view name) override;
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
void captureSamples(al::byte *buffer, uint samples) override;
|
void captureSamples(std::byte *buffer, uint samples) override;
|
||||||
uint availableSamples() override;
|
uint availableSamples() override;
|
||||||
|
|
||||||
sio_hdl *mSndHandle{nullptr};
|
sio_hdl *mSndHandle{nullptr};
|
||||||
|
|
@ -292,8 +297,6 @@ struct SndioCapture final : public BackendBase {
|
||||||
|
|
||||||
std::atomic<bool> mKillNow{true};
|
std::atomic<bool> mKillNow{true};
|
||||||
std::thread mThread;
|
std::thread mThread;
|
||||||
|
|
||||||
DEF_NEWDEL(SndioCapture)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
SndioCapture::~SndioCapture()
|
SndioCapture::~SndioCapture()
|
||||||
|
|
@ -306,7 +309,7 @@ SndioCapture::~SndioCapture()
|
||||||
int SndioCapture::recordProc()
|
int SndioCapture::recordProc()
|
||||||
{
|
{
|
||||||
SetRTPriority();
|
SetRTPriority();
|
||||||
althrd_setname(RECORD_THREAD_NAME);
|
althrd_setname(GetRecordThreadName());
|
||||||
|
|
||||||
const uint frameSize{mDevice->frameSizeFromFmt()};
|
const uint frameSize{mDevice->frameSizeFromFmt()};
|
||||||
|
|
||||||
|
|
@ -317,29 +320,30 @@ int SndioCapture::recordProc()
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto fds = std::make_unique<pollfd[]>(static_cast<uint>(nfds_pre));
|
auto fds = std::vector<pollfd>(static_cast<uint>(nfds_pre));
|
||||||
|
|
||||||
while(!mKillNow.load(std::memory_order_acquire)
|
while(!mKillNow.load(std::memory_order_acquire)
|
||||||
&& mDevice->Connected.load(std::memory_order_acquire))
|
&& mDevice->Connected.load(std::memory_order_acquire))
|
||||||
{
|
{
|
||||||
/* Wait until there's some samples to read. */
|
/* Wait until there's some samples to read. */
|
||||||
const int nfds{sio_pollfd(mSndHandle, fds.get(), POLLIN)};
|
const int nfds{sio_pollfd(mSndHandle, fds.data(), POLLIN)};
|
||||||
if(nfds <= 0)
|
if(nfds <= 0)
|
||||||
{
|
{
|
||||||
mDevice->handleDisconnect("Failed to get polling fds: %d", nfds);
|
mDevice->handleDisconnect("Failed to get polling fds: %d", nfds);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int pollres{::poll(fds.get(), static_cast<uint>(nfds), 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", strerror(errno));
|
mDevice->handleDisconnect("Poll error: %s",
|
||||||
|
std::generic_category().message(errno).c_str());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(pollres == 0)
|
if(pollres == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const int revents{sio_revents(mSndHandle, fds.get())};
|
const int revents{sio_revents(mSndHandle, fds.data())};
|
||||||
if((revents&POLLHUP))
|
if((revents&POLLHUP))
|
||||||
{
|
{
|
||||||
mDevice->handleDisconnect("Got POLLHUP from poll events");
|
mDevice->handleDisconnect("Got POLLHUP from poll events");
|
||||||
|
|
@ -349,7 +353,7 @@ int SndioCapture::recordProc()
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto data = mRing->getWriteVector();
|
auto data = mRing->getWriteVector();
|
||||||
al::span<al::byte> buffer{data.first.buf, data.first.len*frameSize};
|
al::span<std::byte> buffer{data.first.buf, data.first.len*frameSize};
|
||||||
while(!buffer.empty())
|
while(!buffer.empty())
|
||||||
{
|
{
|
||||||
size_t got{sio_read(mSndHandle, buffer.data(), buffer.size())};
|
size_t got{sio_read(mSndHandle, buffer.data(), buffer.size())};
|
||||||
|
|
@ -373,8 +377,8 @@ int SndioCapture::recordProc()
|
||||||
if(buffer.empty())
|
if(buffer.empty())
|
||||||
{
|
{
|
||||||
/* Got samples to read, but no place to store it. Drop it. */
|
/* Got samples to read, but no place to store it. Drop it. */
|
||||||
static char junk[4096];
|
static std::array<char,4096> junk;
|
||||||
sio_read(mSndHandle, junk, sizeof(junk) - (sizeof(junk)%frameSize));
|
sio_read(mSndHandle, junk.data(), junk.size() - (junk.size()%frameSize));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -382,13 +386,13 @@ int SndioCapture::recordProc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SndioCapture::open(const char *name)
|
void SndioCapture::open(std::string_view name)
|
||||||
{
|
{
|
||||||
if(!name)
|
if(name.empty())
|
||||||
name = sndio_device;
|
name = GetDefaultName();
|
||||||
else if(strcmp(name, sndio_device) != 0)
|
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 \"%.*s\" not found",
|
||||||
name};
|
al::sizei(name), name.data()};
|
||||||
|
|
||||||
mSndHandle = sio_open(nullptr, SIO_REC, true);
|
mSndHandle = sio_open(nullptr, SIO_REC, true);
|
||||||
if(mSndHandle == nullptr)
|
if(mSndHandle == nullptr)
|
||||||
|
|
@ -431,12 +435,12 @@ void SndioCapture::open(const char *name)
|
||||||
par.rchan = mDevice->channelsFromFmt();
|
par.rchan = mDevice->channelsFromFmt();
|
||||||
par.rate = mDevice->Frequency;
|
par.rate = mDevice->Frequency;
|
||||||
|
|
||||||
par.appbufsz = maxu(mDevice->BufferSize, mDevice->Frequency/10);
|
par.appbufsz = std::max(mDevice->BufferSize, mDevice->Frequency/10u);
|
||||||
par.round = minu(par.appbufsz/2, mDevice->Frequency/40);
|
par.round = std::min(par.appbufsz/2u, mDevice->Frequency/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,
|
||||||
"Failed to set device praameters"};
|
"Failed to set device parameters"};
|
||||||
|
|
||||||
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,
|
||||||
|
|
@ -461,7 +465,7 @@ void SndioCapture::open(const char *name)
|
||||||
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->Frequency, par.sig?'s':'u', par.bps*8, par.rchan, par.rate};
|
||||||
|
|
||||||
mRing = RingBuffer::Create(mDevice->BufferSize, par.bps*par.rchan, false);
|
mRing = RingBuffer::Create(mDevice->BufferSize, size_t{par.bps}*par.rchan, false);
|
||||||
mDevice->BufferSize = static_cast<uint>(mRing->writeSpace());
|
mDevice->BufferSize = static_cast<uint>(mRing->writeSpace());
|
||||||
mDevice->UpdateSize = par.round;
|
mDevice->UpdateSize = par.round;
|
||||||
|
|
||||||
|
|
@ -496,8 +500,8 @@ void SndioCapture::stop()
|
||||||
ERR("Error stopping device\n");
|
ERR("Error stopping device\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void SndioCapture::captureSamples(al::byte *buffer, uint samples)
|
void SndioCapture::captureSamples(std::byte *buffer, uint samples)
|
||||||
{ mRing->read(buffer, samples); }
|
{ std::ignore = mRing->read(buffer, samples); }
|
||||||
|
|
||||||
uint SndioCapture::availableSamples()
|
uint SndioCapture::availableSamples()
|
||||||
{ return static_cast<uint>(mRing->readSpace()); }
|
{ return static_cast<uint>(mRing->readSpace()); }
|
||||||
|
|
@ -516,18 +520,15 @@ bool SndIOBackendFactory::init()
|
||||||
bool SndIOBackendFactory::querySupport(BackendType type)
|
bool SndIOBackendFactory::querySupport(BackendType type)
|
||||||
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
||||||
|
|
||||||
std::string SndIOBackendFactory::probe(BackendType type)
|
auto SndIOBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||||
{
|
{
|
||||||
std::string outnames;
|
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case BackendType::Playback:
|
case BackendType::Playback:
|
||||||
case BackendType::Capture:
|
case BackendType::Capture:
|
||||||
/* Includes null char. */
|
return std::vector{std::string{GetDefaultName()}};
|
||||||
outnames.append(sndio_device, sizeof(sndio_device));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return outnames;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr SndIOBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
BackendPtr SndIOBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,15 @@
|
||||||
|
|
||||||
struct SndIOBackendFactory final : public BackendFactory {
|
struct SndIOBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
bool init() override;
|
auto init() -> bool final;
|
||||||
|
|
||||||
bool querySupport(BackendType type) override;
|
auto querySupport(BackendType type) -> bool final;
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||||
|
|
||||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_SNDIO_H */
|
#endif /* BACKENDS_SNDIO_H */
|
||||||
|
|
|
||||||
|
|
@ -35,24 +35,26 @@
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include "albyte.h"
|
|
||||||
#include "alc/alconfig.h"
|
#include "alc/alconfig.h"
|
||||||
|
#include "alstring.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 "threads.h"
|
|
||||||
#include "vector.h"
|
|
||||||
|
|
||||||
#include <sys/audioio.h>
|
#include <sys/audioio.h>
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr char solaris_device[] = "Solaris Default";
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "Solaris Default"sv; }
|
||||||
|
|
||||||
std::string solaris_driver{"/dev/audio"};
|
std::string solaris_driver{"/dev/audio"};
|
||||||
|
|
||||||
|
|
@ -63,7 +65,7 @@ struct SolarisBackend final : public BackendBase {
|
||||||
|
|
||||||
int mixerProc();
|
int mixerProc();
|
||||||
|
|
||||||
void open(const char *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;
|
||||||
|
|
@ -71,12 +73,10 @@ struct SolarisBackend final : public BackendBase {
|
||||||
int mFd{-1};
|
int mFd{-1};
|
||||||
|
|
||||||
uint mFrameStep{};
|
uint mFrameStep{};
|
||||||
al::vector<al::byte> mBuffer;
|
std::vector<std::byte> mBuffer;
|
||||||
|
|
||||||
std::atomic<bool> mKillNow{true};
|
std::atomic<bool> mKillNow{true};
|
||||||
std::thread mThread;
|
std::thread mThread;
|
||||||
|
|
||||||
DEF_NEWDEL(SolarisBackend)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
SolarisBackend::~SolarisBackend()
|
SolarisBackend::~SolarisBackend()
|
||||||
|
|
@ -89,10 +89,10 @@ SolarisBackend::~SolarisBackend()
|
||||||
int SolarisBackend::mixerProc()
|
int SolarisBackend::mixerProc()
|
||||||
{
|
{
|
||||||
SetRTPriority();
|
SetRTPriority();
|
||||||
althrd_setname(MIXER_THREAD_NAME);
|
althrd_setname(GetMixerThreadName());
|
||||||
|
|
||||||
const size_t frame_step{mDevice->channelsFromFmt()};
|
const size_t frame_step{mDevice->channelsFromFmt()};
|
||||||
const uint frame_size{mDevice->frameSizeFromFmt()};
|
const size_t frame_size{mDevice->frameSizeFromFmt()};
|
||||||
|
|
||||||
while(!mKillNow.load(std::memory_order_acquire)
|
while(!mKillNow.load(std::memory_order_acquire)
|
||||||
&& mDevice->Connected.load(std::memory_order_acquire))
|
&& mDevice->Connected.load(std::memory_order_acquire))
|
||||||
|
|
@ -116,12 +116,12 @@ int SolarisBackend::mixerProc()
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
al::byte *write_ptr{mBuffer.data()};
|
al::span<std::byte> buffer{mBuffer};
|
||||||
size_t to_write{mBuffer.size()};
|
mDevice->renderSamples(buffer.data(), static_cast<uint>(buffer.size()/frame_size),
|
||||||
mDevice->renderSamples(write_ptr, static_cast<uint>(to_write/frame_size), frame_step);
|
frame_step);
|
||||||
while(to_write > 0 && !mKillNow.load(std::memory_order_acquire))
|
while(!buffer.empty() && !mKillNow.load(std::memory_order_acquire))
|
||||||
{
|
{
|
||||||
ssize_t wrote{write(mFd, write_ptr, to_write)};
|
ssize_t wrote{write(mFd, buffer.data(), buffer.size())};
|
||||||
if(wrote < 0)
|
if(wrote < 0)
|
||||||
{
|
{
|
||||||
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
|
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
|
||||||
|
|
@ -131,8 +131,7 @@ int SolarisBackend::mixerProc()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
to_write -= static_cast<size_t>(wrote);
|
buffer = buffer.subspan(static_cast<size_t>(wrote));
|
||||||
write_ptr += wrote;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,13 +139,13 @@ int SolarisBackend::mixerProc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SolarisBackend::open(const char *name)
|
void SolarisBackend::open(std::string_view name)
|
||||||
{
|
{
|
||||||
if(!name)
|
if(name.empty())
|
||||||
name = solaris_device;
|
name = GetDefaultName();
|
||||||
else if(strcmp(name, solaris_device) != 0)
|
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 \"%.*s\" not found",
|
||||||
name};
|
al::sizei(name), name.data()};
|
||||||
|
|
||||||
int fd{::open(solaris_driver.c_str(), O_WRONLY)};
|
int fd{::open(solaris_driver.c_str(), O_WRONLY)};
|
||||||
if(fd == -1)
|
if(fd == -1)
|
||||||
|
|
@ -231,7 +230,7 @@ bool SolarisBackend::reset()
|
||||||
setDefaultChannelOrder();
|
setDefaultChannelOrder();
|
||||||
|
|
||||||
mBuffer.resize(mDevice->UpdateSize * size_t{frame_size});
|
mBuffer.resize(mDevice->UpdateSize * size_t{frame_size});
|
||||||
std::fill(mBuffer.begin(), mBuffer.end(), al::byte{});
|
std::fill(mBuffer.begin(), mBuffer.end(), std::byte{});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -268,7 +267,7 @@ BackendFactory &SolarisBackendFactory::getFactory()
|
||||||
|
|
||||||
bool SolarisBackendFactory::init()
|
bool SolarisBackendFactory::init()
|
||||||
{
|
{
|
||||||
if(auto devopt = ConfigValueStr(nullptr, "solaris", "device"))
|
if(auto devopt = ConfigValueStr({}, "solaris", "device"))
|
||||||
solaris_driver = std::move(*devopt);
|
solaris_driver = std::move(*devopt);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -276,23 +275,19 @@ bool SolarisBackendFactory::init()
|
||||||
bool SolarisBackendFactory::querySupport(BackendType type)
|
bool SolarisBackendFactory::querySupport(BackendType type)
|
||||||
{ return type == BackendType::Playback; }
|
{ return type == BackendType::Playback; }
|
||||||
|
|
||||||
std::string SolarisBackendFactory::probe(BackendType type)
|
auto SolarisBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||||
{
|
{
|
||||||
std::string outnames;
|
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case BackendType::Playback:
|
case BackendType::Playback:
|
||||||
{
|
if(struct stat buf{}; stat(solaris_driver.c_str(), &buf) == 0)
|
||||||
struct stat buf;
|
return std::vector{std::string{GetDefaultName()}};
|
||||||
if(stat(solaris_driver.c_str(), &buf) == 0)
|
break;
|
||||||
outnames.append(solaris_device, sizeof(solaris_device));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BackendType::Capture:
|
case BackendType::Capture:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return outnames;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr SolarisBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
BackendPtr SolarisBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,15 @@
|
||||||
|
|
||||||
struct SolarisBackendFactory final : public BackendFactory {
|
struct SolarisBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
bool init() override;
|
auto init() -> bool final;
|
||||||
|
|
||||||
bool querySupport(BackendType type) override;
|
auto querySupport(BackendType type) -> bool final;
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||||
|
|
||||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_SOLARIS_H */
|
#endif /* BACKENDS_SOLARIS_H */
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -5,15 +5,17 @@
|
||||||
|
|
||||||
struct WasapiBackendFactory final : public BackendFactory {
|
struct WasapiBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
bool init() override;
|
auto init() -> bool final;
|
||||||
|
|
||||||
bool querySupport(BackendType type) override;
|
auto querySupport(BackendType type) -> bool final;
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
auto queryEventSupport(alc::EventType eventType, BackendType type) -> alc::EventSupport final;
|
||||||
|
|
||||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||||
|
|
||||||
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_WASAPI_H */
|
#endif /* BACKENDS_WASAPI_H */
|
||||||
|
|
|
||||||
|
|
@ -31,24 +31,26 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <system_error>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "albit.h"
|
#include "albit.h"
|
||||||
#include "albyte.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 "core/device.h"
|
#include "core/device.h"
|
||||||
#include "core/helpers.h"
|
#include "core/helpers.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
#include "strutils.h"
|
#include "strutils.h"
|
||||||
#include "threads.h"
|
|
||||||
#include "vector.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using namespace std::string_view_literals;
|
||||||
using std::chrono::seconds;
|
using std::chrono::seconds;
|
||||||
using std::chrono::milliseconds;
|
using std::chrono::milliseconds;
|
||||||
using std::chrono::nanoseconds;
|
using std::chrono::nanoseconds;
|
||||||
|
|
@ -56,38 +58,43 @@ using std::chrono::nanoseconds;
|
||||||
using ubyte = unsigned char;
|
using ubyte = unsigned char;
|
||||||
using ushort = unsigned short;
|
using ushort = unsigned short;
|
||||||
|
|
||||||
constexpr char waveDevice[] = "Wave File Writer";
|
struct FileDeleter {
|
||||||
|
void operator()(gsl::owner<FILE*> f) { fclose(f); }
|
||||||
|
};
|
||||||
|
using FilePtr = std::unique_ptr<FILE,FileDeleter>;
|
||||||
|
|
||||||
constexpr ubyte SUBTYPE_PCM[]{
|
[[nodiscard]] constexpr auto GetDeviceName() noexcept { return "Wave File Writer"sv; }
|
||||||
|
|
||||||
|
constexpr std::array<ubyte,16> SUBTYPE_PCM{{
|
||||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
|
||||||
0x00, 0x38, 0x9b, 0x71
|
0x00, 0x38, 0x9b, 0x71
|
||||||
};
|
}};
|
||||||
constexpr ubyte SUBTYPE_FLOAT[]{
|
constexpr std::array<ubyte,16> SUBTYPE_FLOAT{{
|
||||||
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
|
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
|
||||||
0x00, 0x38, 0x9b, 0x71
|
0x00, 0x38, 0x9b, 0x71
|
||||||
};
|
}};
|
||||||
|
|
||||||
constexpr ubyte SUBTYPE_BFORMAT_PCM[]{
|
constexpr std::array<ubyte,16> SUBTYPE_BFORMAT_PCM{{
|
||||||
0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
|
0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
|
||||||
0xca, 0x00, 0x00, 0x00
|
0xca, 0x00, 0x00, 0x00
|
||||||
};
|
}};
|
||||||
|
|
||||||
constexpr ubyte SUBTYPE_BFORMAT_FLOAT[]{
|
constexpr std::array<ubyte,16> SUBTYPE_BFORMAT_FLOAT{{
|
||||||
0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
|
0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
|
||||||
0xca, 0x00, 0x00, 0x00
|
0xca, 0x00, 0x00, 0x00
|
||||||
};
|
}};
|
||||||
|
|
||||||
void fwrite16le(ushort val, FILE *f)
|
void fwrite16le(ushort val, FILE *f)
|
||||||
{
|
{
|
||||||
ubyte data[2]{ static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff) };
|
std::array data{static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff)};
|
||||||
fwrite(data, 1, 2, f);
|
fwrite(data.data(), 1, data.size(), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fwrite32le(uint val, FILE *f)
|
void fwrite32le(uint val, FILE *f)
|
||||||
{
|
{
|
||||||
ubyte data[4]{ static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff),
|
std::array data{static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff),
|
||||||
static_cast<ubyte>((val>>16)&0xff), static_cast<ubyte>((val>>24)&0xff) };
|
static_cast<ubyte>((val>>16)&0xff), static_cast<ubyte>((val>>24)&0xff)};
|
||||||
fwrite(data, 1, 4, f);
|
fwrite(data.data(), 1, data.size(), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -97,34 +104,27 @@ struct WaveBackend final : public BackendBase {
|
||||||
|
|
||||||
int mixerProc();
|
int mixerProc();
|
||||||
|
|
||||||
void open(const char *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;
|
||||||
|
|
||||||
FILE *mFile{nullptr};
|
FilePtr mFile{nullptr};
|
||||||
long mDataStart{-1};
|
long mDataStart{-1};
|
||||||
|
|
||||||
al::vector<al::byte> mBuffer;
|
std::vector<std::byte> mBuffer;
|
||||||
|
|
||||||
std::atomic<bool> mKillNow{true};
|
std::atomic<bool> mKillNow{true};
|
||||||
std::thread mThread;
|
std::thread mThread;
|
||||||
|
|
||||||
DEF_NEWDEL(WaveBackend)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
WaveBackend::~WaveBackend()
|
WaveBackend::~WaveBackend() = default;
|
||||||
{
|
|
||||||
if(mFile)
|
|
||||||
fclose(mFile);
|
|
||||||
mFile = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int WaveBackend::mixerProc()
|
int WaveBackend::mixerProc()
|
||||||
{
|
{
|
||||||
const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2};
|
const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2};
|
||||||
|
|
||||||
althrd_setname(MIXER_THREAD_NAME);
|
althrd_setname(GetMixerThreadName());
|
||||||
|
|
||||||
const size_t frameStep{mDevice->channelsFromFmt()};
|
const size_t frameStep{mDevice->channelsFromFmt()};
|
||||||
const size_t frameSize{mDevice->frameSizeFromFmt()};
|
const size_t frameSize{mDevice->frameSizeFromFmt()};
|
||||||
|
|
@ -155,13 +155,13 @@ int WaveBackend::mixerProc()
|
||||||
|
|
||||||
if(bytesize == 2)
|
if(bytesize == 2)
|
||||||
{
|
{
|
||||||
const size_t len{mBuffer.size() & ~size_t{1}};
|
const size_t len{mBuffer.size() & ~1_uz};
|
||||||
for(size_t i{0};i < len;i+=2)
|
for(size_t i{0};i < len;i+=2)
|
||||||
std::swap(mBuffer[i], mBuffer[i+1]);
|
std::swap(mBuffer[i], mBuffer[i+1]);
|
||||||
}
|
}
|
||||||
else if(bytesize == 4)
|
else if(bytesize == 4)
|
||||||
{
|
{
|
||||||
const size_t len{mBuffer.size() & ~size_t{3}};
|
const size_t len{mBuffer.size() & ~3_uz};
|
||||||
for(size_t i{0};i < len;i+=4)
|
for(size_t i{0};i < len;i+=4)
|
||||||
{
|
{
|
||||||
std::swap(mBuffer[i ], mBuffer[i+3]);
|
std::swap(mBuffer[i ], mBuffer[i+3]);
|
||||||
|
|
@ -170,8 +170,8 @@ int WaveBackend::mixerProc()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t fs{fwrite(mBuffer.data(), frameSize, mDevice->UpdateSize, mFile)};
|
const size_t fs{fwrite(mBuffer.data(), frameSize, mDevice->UpdateSize, mFile.get())};
|
||||||
if(fs < mDevice->UpdateSize || ferror(mFile))
|
if(fs < mDevice->UpdateSize || ferror(mFile.get()))
|
||||||
{
|
{
|
||||||
ERR("Error writing to file\n");
|
ERR("Error writing to file\n");
|
||||||
mDevice->handleDisconnect("Failed to write playback samples");
|
mDevice->handleDisconnect("Failed to write playback samples");
|
||||||
|
|
@ -195,32 +195,32 @@ int WaveBackend::mixerProc()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaveBackend::open(const char *name)
|
void WaveBackend::open(std::string_view name)
|
||||||
{
|
{
|
||||||
auto fname = ConfigValueStr(nullptr, "wave", "file");
|
auto fname = ConfigValueStr({}, "wave", "file");
|
||||||
if(!fname) throw al::backend_exception{al::backend_error::NoDevice,
|
if(!fname) throw al::backend_exception{al::backend_error::NoDevice,
|
||||||
"No wave output filename"};
|
"No wave output filename"};
|
||||||
|
|
||||||
if(!name)
|
if(name.empty())
|
||||||
name = waveDevice;
|
name = GetDeviceName();
|
||||||
else if(strcmp(name, waveDevice) != 0)
|
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 \"%.*s\" not found",
|
||||||
name};
|
al::sizei(name), name.data()};
|
||||||
|
|
||||||
/* 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;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
{
|
{
|
||||||
std::wstring wname{utf8_to_wstr(fname->c_str())};
|
std::wstring wname{utf8_to_wstr(fname.value())};
|
||||||
mFile = _wfopen(wname.c_str(), L"wb");
|
mFile = FilePtr{_wfopen(wname.c_str(), L"wb")};
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
mFile = 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 '%s': %s",
|
||||||
fname->c_str(), strerror(errno)};
|
fname->c_str(), std::generic_category().message(errno).c_str()};
|
||||||
|
|
||||||
mDevice->DeviceName = name;
|
mDevice->DeviceName = name;
|
||||||
}
|
}
|
||||||
|
|
@ -229,12 +229,11 @@ bool WaveBackend::reset()
|
||||||
{
|
{
|
||||||
uint channels{0}, bytes{0}, chanmask{0};
|
uint channels{0}, bytes{0}, chanmask{0};
|
||||||
bool isbformat{false};
|
bool isbformat{false};
|
||||||
size_t val;
|
|
||||||
|
|
||||||
fseek(mFile, 0, SEEK_SET);
|
fseek(mFile.get(), 0, SEEK_SET);
|
||||||
clearerr(mFile);
|
clearerr(mFile.get());
|
||||||
|
|
||||||
if(GetConfigValueBool(nullptr, "wave", "bformat", false))
|
if(GetConfigValueBool({}, "wave", "bformat", false))
|
||||||
{
|
{
|
||||||
mDevice->FmtChans = DevFmtAmbi3D;
|
mDevice->FmtChans = DevFmtAmbi3D;
|
||||||
mDevice->mAmbiOrder = 1;
|
mDevice->mAmbiOrder = 1;
|
||||||
|
|
@ -265,6 +264,9 @@ bool WaveBackend::reset()
|
||||||
case DevFmtX51: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400; break;
|
case DevFmtX51: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400; break;
|
||||||
case DevFmtX61: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400; break;
|
case DevFmtX61: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400; break;
|
||||||
case DevFmtX71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
|
case DevFmtX71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
|
||||||
|
case DevFmtX7144:
|
||||||
|
mDevice->FmtChans = DevFmtX714;
|
||||||
|
[[fallthrough]];
|
||||||
case DevFmtX714:
|
case DevFmtX714:
|
||||||
chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400 | 0x1000 | 0x4000
|
chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400 | 0x1000 | 0x4000
|
||||||
| 0x8000 | 0x20000;
|
| 0x8000 | 0x20000;
|
||||||
|
|
@ -273,7 +275,7 @@ bool WaveBackend::reset()
|
||||||
case DevFmtX3D71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
|
case DevFmtX3D71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
|
||||||
case DevFmtAmbi3D:
|
case DevFmtAmbi3D:
|
||||||
/* .amb output requires FuMa */
|
/* .amb output requires FuMa */
|
||||||
mDevice->mAmbiOrder = minu(mDevice->mAmbiOrder, 3);
|
mDevice->mAmbiOrder = std::min(mDevice->mAmbiOrder, 3u);
|
||||||
mDevice->mAmbiLayout = DevAmbiLayout::FuMa;
|
mDevice->mAmbiLayout = DevAmbiLayout::FuMa;
|
||||||
mDevice->mAmbiScale = DevAmbiScaling::FuMa;
|
mDevice->mAmbiScale = DevAmbiScaling::FuMa;
|
||||||
isbformat = true;
|
isbformat = true;
|
||||||
|
|
@ -283,49 +285,48 @@ bool WaveBackend::reset()
|
||||||
bytes = mDevice->bytesFromFmt();
|
bytes = mDevice->bytesFromFmt();
|
||||||
channels = mDevice->channelsFromFmt();
|
channels = mDevice->channelsFromFmt();
|
||||||
|
|
||||||
rewind(mFile);
|
rewind(mFile.get());
|
||||||
|
|
||||||
fputs("RIFF", mFile);
|
fputs("RIFF", mFile.get());
|
||||||
fwrite32le(0xFFFFFFFF, mFile); // 'RIFF' header len; filled in at close
|
fwrite32le(0xFFFFFFFF, mFile.get()); // 'RIFF' header len; filled in at close
|
||||||
|
|
||||||
fputs("WAVE", mFile);
|
fputs("WAVE", mFile.get());
|
||||||
|
|
||||||
fputs("fmt ", mFile);
|
fputs("fmt ", mFile.get());
|
||||||
fwrite32le(40, mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE
|
fwrite32le(40, mFile.get()); // 'fmt ' header len; 40 bytes for EXTENSIBLE
|
||||||
|
|
||||||
// 16-bit val, format type id (extensible: 0xFFFE)
|
// 16-bit val, format type id (extensible: 0xFFFE)
|
||||||
fwrite16le(0xFFFE, mFile);
|
fwrite16le(0xFFFE, mFile.get());
|
||||||
// 16-bit val, channel count
|
// 16-bit val, channel count
|
||||||
fwrite16le(static_cast<ushort>(channels), mFile);
|
fwrite16le(static_cast<ushort>(channels), mFile.get());
|
||||||
// 32-bit val, frequency
|
// 32-bit val, frequency
|
||||||
fwrite32le(mDevice->Frequency, mFile);
|
fwrite32le(mDevice->Frequency, mFile.get());
|
||||||
// 32-bit val, bytes per second
|
// 32-bit val, bytes per second
|
||||||
fwrite32le(mDevice->Frequency * channels * bytes, mFile);
|
fwrite32le(mDevice->Frequency * channels * bytes, mFile.get());
|
||||||
// 16-bit val, frame size
|
// 16-bit val, frame size
|
||||||
fwrite16le(static_cast<ushort>(channels * bytes), mFile);
|
fwrite16le(static_cast<ushort>(channels * bytes), mFile.get());
|
||||||
// 16-bit val, bits per sample
|
// 16-bit val, bits per sample
|
||||||
fwrite16le(static_cast<ushort>(bytes * 8), mFile);
|
fwrite16le(static_cast<ushort>(bytes * 8), mFile.get());
|
||||||
// 16-bit val, extra byte count
|
// 16-bit val, extra byte count
|
||||||
fwrite16le(22, mFile);
|
fwrite16le(22, mFile.get());
|
||||||
// 16-bit val, valid bits per sample
|
// 16-bit val, valid bits per sample
|
||||||
fwrite16le(static_cast<ushort>(bytes * 8), mFile);
|
fwrite16le(static_cast<ushort>(bytes * 8), mFile.get());
|
||||||
// 32-bit val, channel mask
|
// 32-bit val, channel mask
|
||||||
fwrite32le(chanmask, mFile);
|
fwrite32le(chanmask, mFile.get());
|
||||||
// 16 byte GUID, sub-type format
|
// 16 byte GUID, sub-type format
|
||||||
val = fwrite((mDevice->FmtType == DevFmtFloat) ?
|
std::ignore = fwrite((mDevice->FmtType == DevFmtFloat) ?
|
||||||
(isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
|
(isbformat ? SUBTYPE_BFORMAT_FLOAT.data() : SUBTYPE_FLOAT.data()) :
|
||||||
(isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, mFile);
|
(isbformat ? SUBTYPE_BFORMAT_PCM.data() : SUBTYPE_PCM.data()), 1, 16, mFile.get());
|
||||||
(void)val;
|
|
||||||
|
|
||||||
fputs("data", mFile);
|
fputs("data", mFile.get());
|
||||||
fwrite32le(0xFFFFFFFF, mFile); // 'data' header len; filled in at close
|
fwrite32le(0xFFFFFFFF, mFile.get()); // 'data' header len; filled in at close
|
||||||
|
|
||||||
if(ferror(mFile))
|
if(ferror(mFile.get()))
|
||||||
{
|
{
|
||||||
ERR("Error writing header: %s\n", strerror(errno));
|
ERR("Error writing header: %s\n", std::generic_category().message(errno).c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
mDataStart = ftell(mFile);
|
mDataStart = ftell(mFile.get());
|
||||||
|
|
||||||
setDefaultWFXChannelOrder();
|
setDefaultWFXChannelOrder();
|
||||||
|
|
||||||
|
|
@ -337,7 +338,7 @@ bool WaveBackend::reset()
|
||||||
|
|
||||||
void WaveBackend::start()
|
void WaveBackend::start()
|
||||||
{
|
{
|
||||||
if(mDataStart > 0 && fseek(mFile, 0, SEEK_END) != 0)
|
if(mDataStart > 0 && fseek(mFile.get(), 0, SEEK_END) != 0)
|
||||||
WARN("Failed to seek on output file\n");
|
WARN("Failed to seek on output file\n");
|
||||||
try {
|
try {
|
||||||
mKillNow.store(false, std::memory_order_release);
|
mKillNow.store(false, std::memory_order_release);
|
||||||
|
|
@ -357,14 +358,14 @@ void WaveBackend::stop()
|
||||||
|
|
||||||
if(mDataStart > 0)
|
if(mDataStart > 0)
|
||||||
{
|
{
|
||||||
long size{ftell(mFile)};
|
long size{ftell(mFile.get())};
|
||||||
if(size > 0)
|
if(size > 0)
|
||||||
{
|
{
|
||||||
long dataLen{size - mDataStart};
|
long dataLen{size - mDataStart};
|
||||||
if(fseek(mFile, 4, SEEK_SET) == 0)
|
if(fseek(mFile.get(), 4, SEEK_SET) == 0)
|
||||||
fwrite32le(static_cast<uint>(size-8), mFile); // 'WAVE' header len
|
fwrite32le(static_cast<uint>(size-8), mFile.get()); // 'WAVE' header len
|
||||||
if(fseek(mFile, mDataStart-4, SEEK_SET) == 0)
|
if(fseek(mFile.get(), mDataStart-4, SEEK_SET) == 0)
|
||||||
fwrite32le(static_cast<uint>(dataLen), mFile); // 'data' header len
|
fwrite32le(static_cast<uint>(dataLen), mFile.get()); // 'data' header len
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -378,19 +379,16 @@ bool WaveBackendFactory::init()
|
||||||
bool WaveBackendFactory::querySupport(BackendType type)
|
bool WaveBackendFactory::querySupport(BackendType type)
|
||||||
{ return type == BackendType::Playback; }
|
{ return type == BackendType::Playback; }
|
||||||
|
|
||||||
std::string WaveBackendFactory::probe(BackendType type)
|
auto WaveBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||||
{
|
{
|
||||||
std::string outnames;
|
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case BackendType::Playback:
|
case BackendType::Playback:
|
||||||
/* Includes null char. */
|
return std::vector{std::string{GetDeviceName()}};
|
||||||
outnames.append(waveDevice, sizeof(waveDevice));
|
|
||||||
break;
|
|
||||||
case BackendType::Capture:
|
case BackendType::Capture:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return outnames;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr WaveBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
BackendPtr WaveBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,15 @@
|
||||||
|
|
||||||
struct WaveBackendFactory final : public BackendFactory {
|
struct WaveBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
bool init() override;
|
auto init() -> bool final;
|
||||||
|
|
||||||
bool querySupport(BackendType type) override;
|
auto querySupport(BackendType type) -> bool final;
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||||
|
|
||||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_WAVE_H */
|
#endif /* BACKENDS_WAVE_H */
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@
|
||||||
|
|
||||||
#include "winmm.h"
|
#include "winmm.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <cstdlib>
|
||||||
#include <stdio.h>
|
#include <cstdio>
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
@ -38,13 +38,15 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include "alnumeric.h"
|
#include "alsem.h"
|
||||||
|
#include "alstring.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 "strutils.h"
|
#include "strutils.h"
|
||||||
#include "threads.h"
|
#include "vector.h"
|
||||||
|
|
||||||
#ifndef WAVE_FORMAT_IEEE_FLOAT
|
#ifndef WAVE_FORMAT_IEEE_FLOAT
|
||||||
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
|
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
|
||||||
|
|
@ -55,13 +57,13 @@ namespace {
|
||||||
#define DEVNAME_HEAD "OpenAL Soft on "
|
#define DEVNAME_HEAD "OpenAL Soft on "
|
||||||
|
|
||||||
|
|
||||||
al::vector<std::string> PlaybackDevices;
|
std::vector<std::string> PlaybackDevices;
|
||||||
al::vector<std::string> CaptureDevices;
|
std::vector<std::string> CaptureDevices;
|
||||||
|
|
||||||
bool checkName(const al::vector<std::string> &list, const std::string &name)
|
bool checkName(const std::vector<std::string> &list, const std::string &name)
|
||||||
{ return std::find(list.cbegin(), list.cend(), name) != list.cend(); }
|
{ return std::find(list.cbegin(), list.cend(), name) != list.cend(); }
|
||||||
|
|
||||||
void ProbePlaybackDevices(void)
|
void ProbePlaybackDevices()
|
||||||
{
|
{
|
||||||
PlaybackDevices.clear();
|
PlaybackDevices.clear();
|
||||||
|
|
||||||
|
|
@ -74,7 +76,7 @@ void ProbePlaybackDevices(void)
|
||||||
WAVEOUTCAPSW WaveCaps{};
|
WAVEOUTCAPSW WaveCaps{};
|
||||||
if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
|
if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
|
||||||
{
|
{
|
||||||
const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)};
|
const std::string basename{DEVNAME_HEAD + wstr_to_utf8(std::data(WaveCaps.szPname))};
|
||||||
|
|
||||||
int count{1};
|
int count{1};
|
||||||
std::string newname{basename};
|
std::string newname{basename};
|
||||||
|
|
@ -92,7 +94,7 @@ void ProbePlaybackDevices(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProbeCaptureDevices(void)
|
void ProbeCaptureDevices()
|
||||||
{
|
{
|
||||||
CaptureDevices.clear();
|
CaptureDevices.clear();
|
||||||
|
|
||||||
|
|
@ -105,7 +107,7 @@ void ProbeCaptureDevices(void)
|
||||||
WAVEINCAPSW WaveCaps{};
|
WAVEINCAPSW WaveCaps{};
|
||||||
if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
|
if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
|
||||||
{
|
{
|
||||||
const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)};
|
const std::string basename{DEVNAME_HEAD + wstr_to_utf8(std::data(WaveCaps.szPname))};
|
||||||
|
|
||||||
int count{1};
|
int count{1};
|
||||||
std::string newname{basename};
|
std::string newname{basename};
|
||||||
|
|
@ -134,7 +136,7 @@ struct WinMMPlayback final : public BackendBase {
|
||||||
|
|
||||||
int mixerProc();
|
int mixerProc();
|
||||||
|
|
||||||
void open(const char *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;
|
||||||
|
|
@ -143,6 +145,7 @@ struct WinMMPlayback final : public BackendBase {
|
||||||
al::semaphore mSem;
|
al::semaphore mSem;
|
||||||
uint mIdx{0u};
|
uint mIdx{0u};
|
||||||
std::array<WAVEHDR,4> mWaveBuffer{};
|
std::array<WAVEHDR,4> mWaveBuffer{};
|
||||||
|
al::vector<char,16> mBuffer;
|
||||||
|
|
||||||
HWAVEOUT mOutHdl{nullptr};
|
HWAVEOUT mOutHdl{nullptr};
|
||||||
|
|
||||||
|
|
@ -150,8 +153,6 @@ struct WinMMPlayback final : public BackendBase {
|
||||||
|
|
||||||
std::atomic<bool> mKillNow{true};
|
std::atomic<bool> mKillNow{true};
|
||||||
std::thread mThread;
|
std::thread mThread;
|
||||||
|
|
||||||
DEF_NEWDEL(WinMMPlayback)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
WinMMPlayback::~WinMMPlayback()
|
WinMMPlayback::~WinMMPlayback()
|
||||||
|
|
@ -159,14 +160,11 @@ WinMMPlayback::~WinMMPlayback()
|
||||||
if(mOutHdl)
|
if(mOutHdl)
|
||||||
waveOutClose(mOutHdl);
|
waveOutClose(mOutHdl);
|
||||||
mOutHdl = nullptr;
|
mOutHdl = nullptr;
|
||||||
|
|
||||||
al_free(mWaveBuffer[0].lpData);
|
|
||||||
std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* WinMMPlayback::waveOutProc
|
/* WinMMPlayback::waveOutProc
|
||||||
*
|
*
|
||||||
* Posts a message to 'WinMMPlayback::mixerProc' everytime a WaveOut Buffer is
|
* Posts a message to 'WinMMPlayback::mixerProc' every time a WaveOut Buffer is
|
||||||
* completed and returns to the application (for more data)
|
* completed and returns to the application (for more data)
|
||||||
*/
|
*/
|
||||||
void CALLBACK WinMMPlayback::waveOutProc(HWAVEOUT, UINT msg, DWORD_PTR, DWORD_PTR) noexcept
|
void CALLBACK WinMMPlayback::waveOutProc(HWAVEOUT, UINT msg, DWORD_PTR, DWORD_PTR) noexcept
|
||||||
|
|
@ -179,7 +177,7 @@ void CALLBACK WinMMPlayback::waveOutProc(HWAVEOUT, UINT msg, DWORD_PTR, DWORD_PT
|
||||||
FORCE_ALIGN int WinMMPlayback::mixerProc()
|
FORCE_ALIGN int WinMMPlayback::mixerProc()
|
||||||
{
|
{
|
||||||
SetRTPriority();
|
SetRTPriority();
|
||||||
althrd_setname(MIXER_THREAD_NAME);
|
althrd_setname(GetMixerThreadName());
|
||||||
|
|
||||||
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))
|
||||||
|
|
@ -207,59 +205,55 @@ FORCE_ALIGN int WinMMPlayback::mixerProc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WinMMPlayback::open(const char *name)
|
void WinMMPlayback::open(std::string_view name)
|
||||||
{
|
{
|
||||||
if(PlaybackDevices.empty())
|
if(PlaybackDevices.empty())
|
||||||
ProbePlaybackDevices();
|
ProbePlaybackDevices();
|
||||||
|
|
||||||
// Find the Device ID matching the deviceName if valid
|
// Find the Device ID matching the deviceName if valid
|
||||||
auto iter = name ?
|
auto iter = !name.empty() ?
|
||||||
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 \"%.*s\" not found",
|
||||||
name};
|
al::sizei(name), name.data()};
|
||||||
auto DeviceID = static_cast<UINT>(std::distance(PlaybackDevices.cbegin(), iter));
|
auto DeviceID = static_cast<UINT>(std::distance(PlaybackDevices.cbegin(), iter));
|
||||||
|
|
||||||
DevFmtType fmttype{mDevice->FmtType};
|
DevFmtType fmttype{mDevice->FmtType};
|
||||||
retry_open:
|
|
||||||
WAVEFORMATEX format{};
|
WAVEFORMATEX format{};
|
||||||
if(fmttype == DevFmtFloat)
|
do {
|
||||||
{
|
format = WAVEFORMATEX{};
|
||||||
format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
|
||||||
format.wBitsPerSample = 32;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
format.wFormatTag = WAVE_FORMAT_PCM;
|
|
||||||
if(fmttype == DevFmtUByte || fmttype == DevFmtByte)
|
|
||||||
format.wBitsPerSample = 8;
|
|
||||||
else
|
|
||||||
format.wBitsPerSample = 16;
|
|
||||||
}
|
|
||||||
format.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
|
|
||||||
format.nBlockAlign = static_cast<WORD>(format.wBitsPerSample * format.nChannels / 8);
|
|
||||||
format.nSamplesPerSec = mDevice->Frequency;
|
|
||||||
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
|
|
||||||
format.cbSize = 0;
|
|
||||||
|
|
||||||
HWAVEOUT outHandle{};
|
|
||||||
MMRESULT res{waveOutOpen(&outHandle, DeviceID, &format,
|
|
||||||
reinterpret_cast<DWORD_PTR>(&WinMMPlayback::waveOutProcC),
|
|
||||||
reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)};
|
|
||||||
if(res != MMSYSERR_NOERROR)
|
|
||||||
{
|
|
||||||
if(fmttype == DevFmtFloat)
|
if(fmttype == DevFmtFloat)
|
||||||
{
|
{
|
||||||
fmttype = DevFmtShort;
|
format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||||
goto retry_open;
|
format.wBitsPerSample = 32;
|
||||||
}
|
}
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "waveOutOpen failed: %u", res};
|
else
|
||||||
}
|
{
|
||||||
|
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||||
|
if(fmttype == DevFmtUByte || fmttype == DevFmtByte)
|
||||||
|
format.wBitsPerSample = 8;
|
||||||
|
else
|
||||||
|
format.wBitsPerSample = 16;
|
||||||
|
}
|
||||||
|
format.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
|
||||||
|
format.nBlockAlign = static_cast<WORD>(format.wBitsPerSample * format.nChannels / 8);
|
||||||
|
format.nSamplesPerSec = mDevice->Frequency;
|
||||||
|
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
|
||||||
|
format.cbSize = 0;
|
||||||
|
|
||||||
|
MMRESULT res{waveOutOpen(&mOutHdl, DeviceID, &format,
|
||||||
|
reinterpret_cast<DWORD_PTR>(&WinMMPlayback::waveOutProcC),
|
||||||
|
reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)};
|
||||||
|
if(res == MMSYSERR_NOERROR) break;
|
||||||
|
|
||||||
|
if(fmttype != DevFmtFloat)
|
||||||
|
throw al::backend_exception{al::backend_error::DeviceError, "waveOutOpen failed: %u",
|
||||||
|
res};
|
||||||
|
|
||||||
|
fmttype = DevFmtShort;
|
||||||
|
} while(true);
|
||||||
|
|
||||||
if(mOutHdl)
|
|
||||||
waveOutClose(mOutHdl);
|
|
||||||
mOutHdl = outHandle;
|
|
||||||
mFormat = format;
|
mFormat = format;
|
||||||
|
|
||||||
mDevice->DeviceName = PlaybackDevices[DeviceID];
|
mDevice->DeviceName = PlaybackDevices[DeviceID];
|
||||||
|
|
@ -312,11 +306,11 @@ bool WinMMPlayback::reset()
|
||||||
}
|
}
|
||||||
setDefaultWFXChannelOrder();
|
setDefaultWFXChannelOrder();
|
||||||
|
|
||||||
uint BufferSize{mDevice->UpdateSize * mFormat.nChannels * mDevice->bytesFromFmt()};
|
const uint BufferSize{mDevice->UpdateSize * mFormat.nChannels * mDevice->bytesFromFmt()};
|
||||||
|
|
||||||
al_free(mWaveBuffer[0].lpData);
|
decltype(mBuffer)(BufferSize*mWaveBuffer.size()).swap(mBuffer);
|
||||||
mWaveBuffer[0] = WAVEHDR{};
|
mWaveBuffer[0] = WAVEHDR{};
|
||||||
mWaveBuffer[0].lpData = static_cast<char*>(al_calloc(16, BufferSize * mWaveBuffer.size()));
|
mWaveBuffer[0].lpData = mBuffer.data();
|
||||||
mWaveBuffer[0].dwBufferLength = BufferSize;
|
mWaveBuffer[0].dwBufferLength = BufferSize;
|
||||||
for(size_t i{1};i < mWaveBuffer.size();i++)
|
for(size_t i{1};i < mWaveBuffer.size();i++)
|
||||||
{
|
{
|
||||||
|
|
@ -369,16 +363,17 @@ struct WinMMCapture final : public BackendBase {
|
||||||
|
|
||||||
int captureProc();
|
int captureProc();
|
||||||
|
|
||||||
void open(const char *name) override;
|
void open(std::string_view name) override;
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
void captureSamples(al::byte *buffer, uint samples) override;
|
void captureSamples(std::byte *buffer, uint samples) override;
|
||||||
uint availableSamples() override;
|
uint availableSamples() override;
|
||||||
|
|
||||||
std::atomic<uint> mReadable{0u};
|
std::atomic<uint> mReadable{0u};
|
||||||
al::semaphore mSem;
|
al::semaphore mSem;
|
||||||
uint mIdx{0};
|
uint mIdx{0};
|
||||||
std::array<WAVEHDR,4> mWaveBuffer{};
|
std::array<WAVEHDR,4> mWaveBuffer{};
|
||||||
|
al::vector<char,16> mBuffer;
|
||||||
|
|
||||||
HWAVEIN mInHdl{nullptr};
|
HWAVEIN mInHdl{nullptr};
|
||||||
|
|
||||||
|
|
@ -388,8 +383,6 @@ struct WinMMCapture final : public BackendBase {
|
||||||
|
|
||||||
std::atomic<bool> mKillNow{true};
|
std::atomic<bool> mKillNow{true};
|
||||||
std::thread mThread;
|
std::thread mThread;
|
||||||
|
|
||||||
DEF_NEWDEL(WinMMCapture)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
WinMMCapture::~WinMMCapture()
|
WinMMCapture::~WinMMCapture()
|
||||||
|
|
@ -398,14 +391,11 @@ WinMMCapture::~WinMMCapture()
|
||||||
if(mInHdl)
|
if(mInHdl)
|
||||||
waveInClose(mInHdl);
|
waveInClose(mInHdl);
|
||||||
mInHdl = nullptr;
|
mInHdl = nullptr;
|
||||||
|
|
||||||
al_free(mWaveBuffer[0].lpData);
|
|
||||||
std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* WinMMCapture::waveInProc
|
/* WinMMCapture::waveInProc
|
||||||
*
|
*
|
||||||
* Posts a message to 'WinMMCapture::captureProc' everytime a WaveIn Buffer is
|
* Posts a message to 'WinMMCapture::captureProc' every time a WaveIn Buffer is
|
||||||
* completed and returns to the application (with more data).
|
* completed and returns to the application (with more data).
|
||||||
*/
|
*/
|
||||||
void CALLBACK WinMMCapture::waveInProc(HWAVEIN, UINT msg, DWORD_PTR, DWORD_PTR) noexcept
|
void CALLBACK WinMMCapture::waveInProc(HWAVEIN, UINT msg, DWORD_PTR, DWORD_PTR) noexcept
|
||||||
|
|
@ -417,7 +407,7 @@ void CALLBACK WinMMCapture::waveInProc(HWAVEIN, UINT msg, DWORD_PTR, DWORD_PTR)
|
||||||
|
|
||||||
int WinMMCapture::captureProc()
|
int WinMMCapture::captureProc()
|
||||||
{
|
{
|
||||||
althrd_setname(RECORD_THREAD_NAME);
|
althrd_setname(GetRecordThreadName());
|
||||||
|
|
||||||
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))
|
||||||
|
|
@ -434,7 +424,8 @@ int WinMMCapture::captureProc()
|
||||||
WAVEHDR &waveHdr = mWaveBuffer[widx];
|
WAVEHDR &waveHdr = mWaveBuffer[widx];
|
||||||
widx = (widx+1) % mWaveBuffer.size();
|
widx = (widx+1) % mWaveBuffer.size();
|
||||||
|
|
||||||
mRing->write(waveHdr.lpData, waveHdr.dwBytesRecorded / mFormat.nBlockAlign);
|
std::ignore = mRing->write(waveHdr.lpData,
|
||||||
|
waveHdr.dwBytesRecorded / mFormat.nBlockAlign);
|
||||||
mReadable.fetch_sub(1, std::memory_order_acq_rel);
|
mReadable.fetch_sub(1, std::memory_order_acq_rel);
|
||||||
waveInAddBuffer(mInHdl, &waveHdr, sizeof(WAVEHDR));
|
waveInAddBuffer(mInHdl, &waveHdr, sizeof(WAVEHDR));
|
||||||
} while(--todo);
|
} while(--todo);
|
||||||
|
|
@ -445,18 +436,18 @@ int WinMMCapture::captureProc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WinMMCapture::open(const char *name)
|
void WinMMCapture::open(std::string_view name)
|
||||||
{
|
{
|
||||||
if(CaptureDevices.empty())
|
if(CaptureDevices.empty())
|
||||||
ProbeCaptureDevices();
|
ProbeCaptureDevices();
|
||||||
|
|
||||||
// Find the Device ID matching the deviceName if valid
|
// Find the Device ID matching the deviceName if valid
|
||||||
auto iter = name ?
|
auto iter = !name.empty() ?
|
||||||
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 \"%.*s\" not found",
|
||||||
name};
|
al::sizei(name), name.data()};
|
||||||
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)
|
||||||
|
|
@ -470,6 +461,7 @@ void WinMMCapture::open(const char *name)
|
||||||
case DevFmtX61:
|
case DevFmtX61:
|
||||||
case DevFmtX71:
|
case DevFmtX71:
|
||||||
case DevFmtX714:
|
case DevFmtX714:
|
||||||
|
case DevFmtX7144:
|
||||||
case DevFmtX3D71:
|
case DevFmtX3D71:
|
||||||
case DevFmtAmbi3D:
|
case DevFmtAmbi3D:
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported",
|
throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported",
|
||||||
|
|
@ -513,14 +505,14 @@ void WinMMCapture::open(const char *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
|
||||||
uint CapturedDataSize{mDevice->BufferSize};
|
const auto CapturedDataSize = std::max<size_t>(mDevice->BufferSize,
|
||||||
CapturedDataSize = static_cast<uint>(maxz(CapturedDataSize, BufferSize*mWaveBuffer.size()));
|
BufferSize*mWaveBuffer.size());
|
||||||
|
|
||||||
mRing = RingBuffer::Create(CapturedDataSize, mFormat.nBlockAlign, false);
|
mRing = RingBuffer::Create(CapturedDataSize, mFormat.nBlockAlign, false);
|
||||||
|
|
||||||
al_free(mWaveBuffer[0].lpData);
|
decltype(mBuffer)(BufferSize*mWaveBuffer.size()).swap(mBuffer);
|
||||||
mWaveBuffer[0] = WAVEHDR{};
|
mWaveBuffer[0] = WAVEHDR{};
|
||||||
mWaveBuffer[0].lpData = static_cast<char*>(al_calloc(16, BufferSize * mWaveBuffer.size()));
|
mWaveBuffer[0].lpData = mBuffer.data();
|
||||||
mWaveBuffer[0].dwBufferLength = BufferSize;
|
mWaveBuffer[0].dwBufferLength = BufferSize;
|
||||||
for(size_t i{1};i < mWaveBuffer.size();++i)
|
for(size_t i{1};i < mWaveBuffer.size();++i)
|
||||||
{
|
{
|
||||||
|
|
@ -571,8 +563,8 @@ void WinMMCapture::stop()
|
||||||
mIdx = 0;
|
mIdx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WinMMCapture::captureSamples(al::byte *buffer, uint samples)
|
void WinMMCapture::captureSamples(std::byte *buffer, uint samples)
|
||||||
{ mRing->read(buffer, samples); }
|
{ std::ignore = mRing->read(buffer, samples); }
|
||||||
|
|
||||||
uint WinMMCapture::availableSamples()
|
uint WinMMCapture::availableSamples()
|
||||||
{ return static_cast<uint>(mRing->readSpace()); }
|
{ return static_cast<uint>(mRing->readSpace()); }
|
||||||
|
|
@ -586,26 +578,23 @@ bool WinMMBackendFactory::init()
|
||||||
bool WinMMBackendFactory::querySupport(BackendType type)
|
bool WinMMBackendFactory::querySupport(BackendType type)
|
||||||
{ return type == BackendType::Playback || type == BackendType::Capture; }
|
{ return type == BackendType::Playback || type == BackendType::Capture; }
|
||||||
|
|
||||||
std::string WinMMBackendFactory::probe(BackendType type)
|
auto WinMMBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||||
{
|
{
|
||||||
std::string outnames;
|
std::vector<std::string> outnames;
|
||||||
auto add_device = [&outnames](const std::string &dname) -> void
|
auto add_device = [&outnames](const std::string &dname) -> void
|
||||||
{
|
{ if(!dname.empty()) outnames.emplace_back(dname); };
|
||||||
/* +1 to also append the null char (to ensure a null-separated list and
|
|
||||||
* double-null terminated list).
|
|
||||||
*/
|
|
||||||
if(!dname.empty())
|
|
||||||
outnames.append(dname.c_str(), dname.length()+1);
|
|
||||||
};
|
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case BackendType::Playback:
|
case BackendType::Playback:
|
||||||
ProbePlaybackDevices();
|
ProbePlaybackDevices();
|
||||||
|
outnames.reserve(PlaybackDevices.size());
|
||||||
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
|
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BackendType::Capture:
|
case BackendType::Capture:
|
||||||
ProbeCaptureDevices();
|
ProbeCaptureDevices();
|
||||||
|
outnames.reserve(CaptureDevices.size());
|
||||||
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
|
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,15 @@
|
||||||
|
|
||||||
struct WinMMBackendFactory final : public BackendFactory {
|
struct WinMMBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
bool init() override;
|
auto init() -> bool final;
|
||||||
|
|
||||||
bool querySupport(BackendType type) override;
|
auto querySupport(BackendType type) -> bool final;
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||||
|
|
||||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static auto getFactory() -> BackendFactory&;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* BACKENDS_WINMM_H */
|
#endif /* BACKENDS_WINMM_H */
|
||||||
|
|
|
||||||
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